Refactor scene

This commit is contained in:
schmelczerandras 2020-09-17 17:51:53 +02:00
parent 77d5938b4b
commit 7e8ad0a125
10 changed files with 232 additions and 159 deletions

View file

@ -23,10 +23,6 @@ body {
color: white;
white-space: pre;
@media (max-width: 800px) {
font-size: 0.55em;
}
}
canvas#main {

View file

@ -1,9 +1,9 @@
import { glMatrix, vec2, vec3 } from 'gl-matrix';
import { compile, Renderer } from 'sdf-2d';
import { glMatrix } from 'gl-matrix';
import { DeltaTimeCalculator } from './helper/delta-time-calculator';
import './index.scss';
import { RainScene } from './scenes/rain-scene';
import { RainScene } from './scenes/rain/rain-scene';
import { Scene } from './scenes/scene';
import { TunnelScene } from './scenes/tunnel-scene';
glMatrix.setMatrixArrayType(Array);
@ -11,51 +11,20 @@ const deltaTimeCalculator = new DeltaTimeCalculator();
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
const overlay = document.querySelector('#overlay') as HTMLDivElement;
const drawFrame = (
renderer: Renderer,
scene: Scene,
deltaTime: DOMHighResTimeStamp,
currentTime: DOMHighResTimeStamp
) => {
const { width, height } = canvas.getBoundingClientRect();
renderer.setViewArea(vec2.fromValues(0, height), vec2.fromValues(width, height));
renderer.autoscaleQuality(deltaTime);
overlay.innerText = JSON.stringify(
renderer.insights,
(_, v) => (v.toFixed ? Number(v.toFixed(2)) : v),
' '
);
scene.animate(currentTime, { width, height });
scene.drawables.forEach((d) => renderer.addDrawable(d));
renderer.renderDrawables();
};
const handleScene = async (scene: Scene) => {
const renderer = compile(
canvas,
scene.descriptors,
[vec3.fromValues(0.4, 1, 0.6), vec3.fromValues(1, 1, 0), vec3.fromValues(0.3, 1, 1)],
{
tileMultiplier: 10,
enableStopwatch: false,
shaderMacros: { softShadowTraceCount: '128', hardShadowTraceCount: '48' },
}
);
const handleScene = async (SceneConstructor: new () => Scene) => {
const scene = new SceneConstructor();
await scene.initialize(canvas, overlay);
let triggerIsOver: () => void;
const isOver = new Promise((resolve) => (triggerIsOver = resolve));
let timeSinceStart = 0;
const handleFrame = (currentTime: DOMHighResTimeStamp) => {
const deltaTime = deltaTimeCalculator.getNextDeltaTime(currentTime);
drawFrame(renderer, scene, deltaTime, currentTime);
scene.drawNextFrame(currentTime, deltaTime);
if ((timeSinceStart += deltaTime) > 200000 * 1000) {
if ((timeSinceStart += deltaTime) > 2 * 1000) {
triggerIsOver();
} else {
requestAnimationFrame(handleFrame);
@ -65,15 +34,20 @@ const handleScene = async (scene: Scene) => {
requestAnimationFrame(handleFrame);
await isOver;
renderer.destroy();
scene.destroy();
};
const scenes = [new RainScene()];
const main = async () => {
let i = 0;
for (;;) {
await handleScene(scenes[i++ % scenes.length]);
try {
const scenes = [TunnelScene, RainScene];
let i = 0;
for (;;) {
await handleScene(scenes[i++ % scenes.length]);
}
} catch (e) {
console.error(e);
alert(e);
}
};

View file

@ -1,82 +0,0 @@
import { vec2, vec3 } from 'gl-matrix';
import { CircleLight, Drawable, DrawableDescriptor, Tunnel } from 'sdf-2d';
import { Random } from '../helper/random';
import { Scene } from './scene';
export class RainScene implements Scene {
public droplets: Array<Droplet> = [];
private light1: CircleLight = new CircleLight(
vec2.create(),
1,
vec3.fromValues(1, 0, 1),
1
);
private light2: CircleLight = new CircleLight(
vec2.create(),
1,
vec3.fromValues(1, 0, 0.5),
1
);
public constructor() {
for (let i = 0; i < 20; i++) {
this.droplets.push(new Droplet());
}
}
public get descriptors(): Array<DrawableDescriptor> {
return [Tunnel.descriptor, CircleLight.descriptor];
}
public animate(
currentTime: number,
viewAreaSize: { width: number; height: number }
): void {
vec2.set(this.light1.center, 0, viewAreaSize.height / 2);
vec2.set(this.light2.center, viewAreaSize.width, viewAreaSize.height / 2);
this.droplets.forEach((d) => d.animate(currentTime, viewAreaSize));
}
public get drawables(): Array<Drawable> {
return [...this.droplets.map((d) => d.drawable), this.light1, this.light2];
}
}
class Droplet {
public readonly drawable: Tunnel;
private speed = Random.getRandom() * 0.2 + 0.2;
private position = vec2.fromValues(
Random.getRandomInRange(0.1, 0.9),
Random.getRandom()
);
private length = Random.getRandom() * 20 + 4;
constructor() {
const size = Random.getRandom() * 2 + 2;
this.drawable = new Tunnel(
vec2.create(),
vec2.create(),
size + Random.getRandom() * 2 + 2,
size
);
}
public animate(
currentTime: number,
{ width, height }: { width: number; height: number }
) {
const heightOffset = 100;
vec2.set(
this.drawable.from,
this.position[0] * width,
height -
((this.position[1] * height + this.speed * currentTime) % (height + heightOffset))
);
vec2.add(this.drawable.to, this.drawable.from, vec2.fromValues(0, this.length));
}
}

View file

@ -0,0 +1,38 @@
import { vec2 } from 'gl-matrix';
import { Tunnel } from 'sdf-2d';
import { Random } from '../../helper/random';
export class Droplet {
public readonly drawable: Tunnel;
private speed = Random.getRandom() * 0.2 + 0.2;
private position = vec2.fromValues(
Random.getRandomInRange(0.1, 0.9),
Random.getRandom()
);
private length = Random.getRandom() * 20 + 4;
constructor() {
const size = Random.getRandom() * 2 + 2;
this.drawable = new Tunnel(
vec2.create(),
vec2.create(),
size + Random.getRandom() * 2 + 2,
size
);
}
public animate(currentTime: number, viewAreaSize: vec2) {
const heightOffset = 100;
vec2.set(
this.drawable.from,
this.position.x * viewAreaSize.x,
viewAreaSize.y -
((this.position.y * viewAreaSize.y + this.speed * currentTime) %
(viewAreaSize.y + heightOffset))
);
vec2.add(this.drawable.to, this.drawable.from, vec2.fromValues(0, this.length));
}
}

View file

@ -0,0 +1,91 @@
import { vec2, vec3 } from 'gl-matrix';
import { CircleLight, compile, Renderer, Tunnel } from 'sdf-2d';
import { Scene } from '../scene';
import { Droplet } from './droplet';
export class RainScene implements Scene {
private droplets: Array<Droplet> = [];
private light1: CircleLight = new CircleLight(
vec2.create(),
vec3.fromValues(1, 0, 1),
1
);
private light2: CircleLight = new CircleLight(
vec2.create(),
vec3.fromValues(1, 0, 0.5),
1
);
private renderer: Renderer;
private canvas: HTMLCanvasElement;
private overlay: HTMLDivElement;
public constructor() {
for (let i = 0; i < 20; i++) {
this.droplets.push(new Droplet());
}
}
public async initialize(
canvas: HTMLCanvasElement,
overlay: HTMLDivElement
): Promise<void> {
this.canvas = canvas;
this.overlay = overlay;
this.renderer = await compile(
canvas,
[
{
...Tunnel.descriptor,
shaderCombinationSteps: [0, 1, 2, 4, 8, 12],
},
{
...CircleLight.descriptor,
shaderCombinationSteps: [2],
},
],
[
vec3.fromValues(0.4, 1, 0.6),
vec3.fromValues(1, 1, 0),
vec3.fromValues(0.3, 1, 1),
],
{
enableStopwatch: false,
softShadowTraceCount: '128',
hardShadowTraceCount: '48',
}
);
}
public drawNextFrame(
currentTime: DOMHighResTimeStamp,
deltaTime: DOMHighResTimeStamp
): void {
const { width, height } = this.canvas.getBoundingClientRect();
this.renderer.setViewArea(vec2.fromValues(0, height), vec2.fromValues(width, height));
this.renderer.autoscaleQuality(deltaTime);
this.overlay.innerText = JSON.stringify(
this.renderer.insights,
(_, v) => (v.toFixed ? Number(v.toFixed(2)) : v),
' '
);
const viewAreaSize = this.renderer.viewAreaSize;
vec2.set(this.light1.center, 0, viewAreaSize.y / 2);
vec2.set(this.light2.center, viewAreaSize.x, viewAreaSize.y / 2);
this.droplets.forEach((d) => d.animate(currentTime, viewAreaSize));
[...this.droplets.map((d) => d.drawable), this.light1, this.light2].forEach((d) =>
this.renderer.addDrawable(d)
);
this.renderer.renderDrawables();
}
public destroy(): void {
this.renderer.destroy();
}
}

View file

@ -1,10 +1,5 @@
import { Drawable, DrawableDescriptor } from 'sdf-2d';
export interface Scene {
readonly descriptors: Array<DrawableDescriptor>;
animate(
currentTime: DOMHighResTimeStamp,
viewAreaSize: { width: number; height: number }
): void;
readonly drawables: Array<Drawable>;
initialize(canvas: HTMLCanvasElement, overlay: HTMLDivElement): Promise<void>;
drawNextFrame(currentTime: DOMHighResTimeStamp, deltaTime: DOMHighResTimeStamp): void;
destroy(): void;
}

View file

@ -0,0 +1,69 @@
import { vec2, vec3 } from 'gl-matrix';
import { CircleLight, compile, Renderer, Tunnel } from 'sdf-2d';
import { Scene } from './scene';
export class TunnelScene implements Scene {
private renderer: Renderer;
private canvas: HTMLCanvasElement;
private overlay: HTMLDivElement;
public constructor() {}
public async initialize(
canvas: HTMLCanvasElement,
overlay: HTMLDivElement
): Promise<void> {
this.canvas = canvas;
this.overlay = overlay;
this.renderer = await compile(
canvas,
[
{
...Tunnel.descriptor,
shaderCombinationSteps: [0, 1, 2, 4, 8, 12],
},
{
...CircleLight.descriptor,
shaderCombinationSteps: [2],
},
],
[
vec3.fromValues(0.4, 1, 0.6),
vec3.fromValues(1, 1, 0),
vec3.fromValues(0.3, 1, 1),
],
{
enableStopwatch: false,
softShadowTraceCount: '128',
hardShadowTraceCount: '48',
}
);
}
public drawNextFrame(
currentTime: DOMHighResTimeStamp,
deltaTime: DOMHighResTimeStamp
): void {
const { width, height } = this.canvas.getBoundingClientRect();
this.renderer.setViewArea(vec2.fromValues(0, height), vec2.fromValues(width, height));
this.renderer.autoscaleQuality(deltaTime);
this.overlay.innerText = JSON.stringify(
this.renderer.insights,
(_, v) => (v.toFixed ? Number(v.toFixed(2)) : v),
' '
);
const viewAreaSize = this.renderer.viewAreaSize;
[
new Tunnel(vec2.fromValues(200, 200), vec2.fromValues(600, 600), 30, 200),
].forEach((d) => this.renderer.addDrawable(d));
this.renderer.renderDrawables();
}
public destroy(): void {
this.renderer.destroy();
}
}