diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index ea544b2..0000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -*.psd filter=lfs diff=lfs merge=lfs -text diff --git a/package.json b/package.json index 36a771d..aad8bff 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/src/index.scss b/src/index.scss index 5105afb..bc0da88 100644 --- a/src/index.scss +++ b/src/index.scss @@ -23,10 +23,6 @@ body { color: white; white-space: pre; - - @media (max-width: 800px) { - font-size: 0.55em; - } } canvas#main { diff --git a/src/index.ts b/src/index.ts index 4dc7d66..f5199e6 100644 --- a/src/index.ts +++ b/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); } }; diff --git a/src/scenes/rain-scene.ts b/src/scenes/rain-scene.ts deleted file mode 100644 index d44e7b7..0000000 --- a/src/scenes/rain-scene.ts +++ /dev/null @@ -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 = []; - - 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 { - 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 { - 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)); - } -} diff --git a/src/scenes/rain/droplet.ts b/src/scenes/rain/droplet.ts new file mode 100644 index 0000000..8964a48 --- /dev/null +++ b/src/scenes/rain/droplet.ts @@ -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)); + } +} diff --git a/src/scenes/rain/rain-scene.ts b/src/scenes/rain/rain-scene.ts new file mode 100644 index 0000000..387b205 --- /dev/null +++ b/src/scenes/rain/rain-scene.ts @@ -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 = []; + 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 { + 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(); + } +} diff --git a/src/scenes/scene.ts b/src/scenes/scene.ts index 6c5d2d5..d99e786 100644 --- a/src/scenes/scene.ts +++ b/src/scenes/scene.ts @@ -1,10 +1,5 @@ -import { Drawable, DrawableDescriptor } from 'sdf-2d'; - export interface Scene { - readonly descriptors: Array; - animate( - currentTime: DOMHighResTimeStamp, - viewAreaSize: { width: number; height: number } - ): void; - readonly drawables: Array; + initialize(canvas: HTMLCanvasElement, overlay: HTMLDivElement): Promise; + drawNextFrame(currentTime: DOMHighResTimeStamp, deltaTime: DOMHighResTimeStamp): void; + destroy(): void; } diff --git a/src/scenes/tunnel-scene.ts b/src/scenes/tunnel-scene.ts new file mode 100644 index 0000000..ee2a304 --- /dev/null +++ b/src/scenes/tunnel-scene.ts @@ -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 { + 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(); + } +} diff --git a/webpack.config.js b/webpack.config.js index f91ecb7..f4798e5 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -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',