Refactor scene
This commit is contained in:
parent
77d5938b4b
commit
7e8ad0a125
10 changed files with 232 additions and 159 deletions
1
.gitattributes
vendored
1
.gitattributes
vendored
|
|
@ -1 +0,0 @@
|
|||
*.psd filter=lfs diff=lfs merge=lfs -text
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,10 +23,6 @@ body {
|
|||
|
||||
color: white;
|
||||
white-space: pre;
|
||||
|
||||
@media (max-width: 800px) {
|
||||
font-size: 0.55em;
|
||||
}
|
||||
}
|
||||
|
||||
canvas#main {
|
||||
|
|
|
|||
64
src/index.ts
64
src/index.ts
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
38
src/scenes/rain/droplet.ts
Normal file
38
src/scenes/rain/droplet.ts
Normal 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));
|
||||
}
|
||||
}
|
||||
91
src/scenes/rain/rain-scene.ts
Normal file
91
src/scenes/rain/rain-scene.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
69
src/scenes/tunnel-scene.ts
Normal file
69
src/scenes/tunnel-scene.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue