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

1
.gitattributes vendored
View file

@ -1 +0,0 @@
*.psd filter=lfs diff=lfs merge=lfs -text

View file

@ -7,7 +7,7 @@
"scripts": {
"start": "webpack-dev-server --mode development",
"lint": "npx eslint --fix \"src/**/*.ts\" && npx prettier --write \"src/**/*.ts\"",
"build": "webpack && find dist -type f -not -name '*.html' | xargs rm"
"build": "webpack && shx find dist -type f -not -name '*.html' | xargs rm"
},
"keywords": [],
"author": "András Schmelczer",
@ -58,8 +58,12 @@
"terser-webpack-plugin": "^2.3.8",
"ts-loader": "^8.0.3",
"typescript": "^3.9.7",
"shx": "^0.3.2",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3"
},
"dependencies": {
"source-map-loader": "^1.1.0"
}
}

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();
}
}

View file

@ -14,7 +14,7 @@ module.exports = {
watchOptions: {
ignored: /node_modules/,
},
devtool: 'inline-source-map',
devtool: 'source-map',
devServer: {
host: '0.0.0.0',
disableHostCheck: true,
@ -71,22 +71,6 @@ module.exports = {
},
module: {
rules: [
{
test: /\.(glsl)$/,
use: {
loader: 'raw-loader',
},
},
{
test: /\.ico$/i,
use: {
loader: 'file-loader',
query: {
outputPath: '/',
name: '[name].[ext]',
},
},
},
{
test: /\.scss$/i,
use: [
@ -108,6 +92,11 @@ module.exports = {
},
],
},
{
test: /\.js$/,
enforce: 'pre',
use: ['source-map-loader'],
},
{
test: /\.ts$/,
use: {
@ -118,7 +107,7 @@ module.exports = {
],
},
resolve: {
extensions: ['.ts', '.js', '.glsl'],
extensions: ['.ts', '.js'],
},
output: {
filename: '[name].[contenthash].js',