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": {
|
"scripts": {
|
||||||
"start": "webpack-dev-server --mode development",
|
"start": "webpack-dev-server --mode development",
|
||||||
"lint": "npx eslint --fix \"src/**/*.ts\" && npx prettier --write \"src/**/*.ts\"",
|
"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": [],
|
"keywords": [],
|
||||||
"author": "András Schmelczer",
|
"author": "András Schmelczer",
|
||||||
|
|
@ -58,8 +58,12 @@
|
||||||
"terser-webpack-plugin": "^2.3.8",
|
"terser-webpack-plugin": "^2.3.8",
|
||||||
"ts-loader": "^8.0.3",
|
"ts-loader": "^8.0.3",
|
||||||
"typescript": "^3.9.7",
|
"typescript": "^3.9.7",
|
||||||
|
"shx": "^0.3.2",
|
||||||
"webpack": "^4.44.1",
|
"webpack": "^4.44.1",
|
||||||
"webpack-cli": "^3.3.11",
|
"webpack-cli": "^3.3.11",
|
||||||
"webpack-dev-server": "^3.10.3"
|
"webpack-dev-server": "^3.10.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"source-map-loader": "^1.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,6 @@ body {
|
||||||
|
|
||||||
color: white;
|
color: white;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
|
|
||||||
@media (max-width: 800px) {
|
|
||||||
font-size: 0.55em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas#main {
|
canvas#main {
|
||||||
|
|
|
||||||
58
src/index.ts
58
src/index.ts
|
|
@ -1,9 +1,9 @@
|
||||||
import { glMatrix, vec2, vec3 } from 'gl-matrix';
|
import { glMatrix } from 'gl-matrix';
|
||||||
import { compile, Renderer } from 'sdf-2d';
|
|
||||||
import { DeltaTimeCalculator } from './helper/delta-time-calculator';
|
import { DeltaTimeCalculator } from './helper/delta-time-calculator';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
import { RainScene } from './scenes/rain-scene';
|
import { RainScene } from './scenes/rain/rain-scene';
|
||||||
import { Scene } from './scenes/scene';
|
import { Scene } from './scenes/scene';
|
||||||
|
import { TunnelScene } from './scenes/tunnel-scene';
|
||||||
|
|
||||||
glMatrix.setMatrixArrayType(Array);
|
glMatrix.setMatrixArrayType(Array);
|
||||||
|
|
||||||
|
|
@ -11,51 +11,20 @@ const deltaTimeCalculator = new DeltaTimeCalculator();
|
||||||
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
|
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
|
||||||
const overlay = document.querySelector('#overlay') as HTMLDivElement;
|
const overlay = document.querySelector('#overlay') as HTMLDivElement;
|
||||||
|
|
||||||
const drawFrame = (
|
const handleScene = async (SceneConstructor: new () => Scene) => {
|
||||||
renderer: Renderer,
|
const scene = new SceneConstructor();
|
||||||
scene: Scene,
|
await scene.initialize(canvas, overlay);
|
||||||
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' },
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let triggerIsOver: () => void;
|
let triggerIsOver: () => void;
|
||||||
const isOver = new Promise((resolve) => (triggerIsOver = resolve));
|
const isOver = new Promise((resolve) => (triggerIsOver = resolve));
|
||||||
|
|
||||||
let timeSinceStart = 0;
|
let timeSinceStart = 0;
|
||||||
|
|
||||||
const handleFrame = (currentTime: DOMHighResTimeStamp) => {
|
const handleFrame = (currentTime: DOMHighResTimeStamp) => {
|
||||||
const deltaTime = deltaTimeCalculator.getNextDeltaTime(currentTime);
|
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();
|
triggerIsOver();
|
||||||
} else {
|
} else {
|
||||||
requestAnimationFrame(handleFrame);
|
requestAnimationFrame(handleFrame);
|
||||||
|
|
@ -65,16 +34,21 @@ const handleScene = async (scene: Scene) => {
|
||||||
requestAnimationFrame(handleFrame);
|
requestAnimationFrame(handleFrame);
|
||||||
await isOver;
|
await isOver;
|
||||||
|
|
||||||
renderer.destroy();
|
scene.destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
const scenes = [new RainScene()];
|
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
|
try {
|
||||||
|
const scenes = [TunnelScene, RainScene];
|
||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
await handleScene(scenes[i++ % scenes.length]);
|
await handleScene(scenes[i++ % scenes.length]);
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
alert(e);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
export interface Scene {
|
||||||
readonly descriptors: Array<DrawableDescriptor>;
|
initialize(canvas: HTMLCanvasElement, overlay: HTMLDivElement): Promise<void>;
|
||||||
animate(
|
drawNextFrame(currentTime: DOMHighResTimeStamp, deltaTime: DOMHighResTimeStamp): void;
|
||||||
currentTime: DOMHighResTimeStamp,
|
destroy(): void;
|
||||||
viewAreaSize: { width: number; height: number }
|
|
||||||
): void;
|
|
||||||
readonly drawables: Array<Drawable>;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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: {
|
watchOptions: {
|
||||||
ignored: /node_modules/,
|
ignored: /node_modules/,
|
||||||
},
|
},
|
||||||
devtool: 'inline-source-map',
|
devtool: 'source-map',
|
||||||
devServer: {
|
devServer: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
disableHostCheck: true,
|
disableHostCheck: true,
|
||||||
|
|
@ -71,22 +71,6 @@ module.exports = {
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
|
||||||
test: /\.(glsl)$/,
|
|
||||||
use: {
|
|
||||||
loader: 'raw-loader',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.ico$/i,
|
|
||||||
use: {
|
|
||||||
loader: 'file-loader',
|
|
||||||
query: {
|
|
||||||
outputPath: '/',
|
|
||||||
name: '[name].[ext]',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
test: /\.scss$/i,
|
test: /\.scss$/i,
|
||||||
use: [
|
use: [
|
||||||
|
|
@ -108,6 +92,11 @@ module.exports = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
enforce: 'pre',
|
||||||
|
use: ['source-map-loader'],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
test: /\.ts$/,
|
test: /\.ts$/,
|
||||||
use: {
|
use: {
|
||||||
|
|
@ -118,7 +107,7 @@ module.exports = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.ts', '.js', '.glsl'],
|
extensions: ['.ts', '.js'],
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
filename: '[name].[contenthash].js',
|
filename: '[name].[contenthash].js',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue