From 39ed6265090830192ecd74359ed9be595f35f849 Mon Sep 17 00:00:00 2001 From: schmelczerandras Date: Fri, 2 Oct 2020 17:38:23 +0200 Subject: [PATCH] Add metaball scene --- package.json | 2 +- src/index.ts | 3 +- src/scenes/blob/blob.ts | 2 +- src/scenes/metaball/metaball-scene.ts | 104 ++++++++++++++++++++++++++ src/scenes/metaball/metaball.ts | 21 ++++++ src/scenes/rain/droplet.ts | 8 +- src/scenes/rain/rain-scene.ts | 13 ++-- src/scenes/tunnel-scene.ts | 3 +- 8 files changed, 140 insertions(+), 16 deletions(-) create mode 100644 src/scenes/metaball/metaball-scene.ts create mode 100644 src/scenes/metaball/metaball.ts diff --git a/package.json b/package.json index 7eadacd..ddd615f 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "resolve-url-loader": "^3.1.1", "sass": "^1.26.3", "sass-loader": "^9.0.3", - "sdf-2d": "^0.3.4", + "sdf-2d": "^0.3.6", "source-map-loader": "^1.1.0", "svg-url-loader": "^6.0.0", "terser-webpack-plugin": "^2.3.8", diff --git a/src/index.ts b/src/index.ts index ab9ef54..8a48776 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,12 +10,13 @@ import '../static/og-image.png'; import { DeltaTimeCalculator } from './helper/delta-time-calculator'; import { removeUnnecessaryOutlines } from './helper/remove-unnecessary-outlines'; import { BlobScene } from './scenes/blob/blob-scene'; +import { MetaballScene } from './scenes/metaball/metaball-scene'; import { RainScene } from './scenes/rain/rain-scene'; import { Scene } from './scenes/scene'; import { TunnelScene } from './scenes/tunnel-scene'; import './styles/index.scss'; -const scenes = [TunnelScene, RainScene, BlobScene]; +const scenes = [TunnelScene, MetaballScene, RainScene, BlobScene]; const sceneIntervalInSeconds = 8; glMatrix.setMatrixArrayType(Array); diff --git a/src/scenes/blob/blob.ts b/src/scenes/blob/blob.ts index c7e6b74..8a4f5b7 100644 --- a/src/scenes/blob/blob.ts +++ b/src/scenes/blob/blob.ts @@ -53,7 +53,7 @@ export class Blob extends Drawable { headCenter: 'headCenters', }, uniformCountMacroName: 'BLOB_COUNT', - shaderCombinationSteps: [1], + shaderCombinationSteps: [0, 1], empty: new Blob(vec2.fromValues(0, 0)), }; diff --git a/src/scenes/metaball/metaball-scene.ts b/src/scenes/metaball/metaball-scene.ts new file mode 100644 index 0000000..4211af0 --- /dev/null +++ b/src/scenes/metaball/metaball-scene.ts @@ -0,0 +1,104 @@ +import { vec2 } from 'gl-matrix'; +import { compile, Flashlight, MetaCircle, Renderer } from 'sdf-2d'; +import { prettyPrint } from '../../helper/pretty-print'; +import { Random } from '../../helper/random'; +import { rgb } from '../../helper/rgb'; +import { rgb255 } from '../../helper/rgb255'; +import { Scene } from '../scene'; +import { Metaball } from './metaball'; + +export class MetaballScene implements Scene { + private circles: Array = []; + + private renderer: Renderer; + private canvas: HTMLCanvasElement; + private overlay: HTMLDivElement; + + public async initialize( + canvas: HTMLCanvasElement, + overlay: HTMLDivElement + ): Promise { + this.canvas = canvas; + this.overlay = overlay; + this.renderer = await compile(canvas, [ + { + ...Flashlight.descriptor, + shaderCombinationSteps: [0, 2], + }, + { + ...MetaCircle.descriptor, + shaderCombinationSteps: [0, 1, 2, 4, 8, 12, 16], + }, + ]); + + this.renderer.setRuntimeSettings({ + ambientLight: rgb(0.1, 0.1, 0.3), + colorPalette: [ + rgb(1, 1, 1), + rgb(1, 1, 1), + rgb(1, 1, 1), + rgb(1, 1, 1), + rgb(1, 1, 0), + rgb255(186, 59, 70), + ], + }); + + for (let i = 0; i < 16; i++) { + this.circles.push( + new Metaball( + vec2.fromValues( + Random.getRandomInRange(0.3, 0.6), + Random.getRandomInRange(0.3, 0.6) + ), + vec2.fromValues( + Random.getRandomInRange(0.05, 0.5), + Random.getRandomInRange(0.05, 0.5) + ) + ) + ); + } + } + + public drawNextFrame( + currentTime: DOMHighResTimeStamp, + deltaTime: DOMHighResTimeStamp + ): void { + const { width, height } = this.canvas.getBoundingClientRect(); + const viewAreaWidth = width / Math.max(width, height); + const viewAreaHeight = height / Math.max(width, height); + this.renderer.setViewArea( + vec2.fromValues(0, viewAreaHeight), + vec2.fromValues(viewAreaWidth, viewAreaHeight) + ); + this.renderer.autoscaleQuality(deltaTime); + + this.overlay.innerText = prettyPrint(this.renderer.insights); + + this.circles.forEach((c) => { + c.animate(currentTime / 2000, viewAreaWidth, viewAreaHeight); + this.renderer.addDrawable(c.shape); + }); + + const light1 = new Flashlight( + vec2.fromValues(-0.05, -0.05), + rgb255(104, 171, 212), + 0.02, + vec2.fromValues(1, 1) + ); + const light2 = new Flashlight( + vec2.fromValues(viewAreaWidth + 0.05, -0.05), + rgb255(226, 90, 102), + 0.02, + vec2.fromValues(-1, 1) + ); + + this.renderer.addDrawable(light1); + this.renderer.addDrawable(light2); + + this.renderer.renderDrawables(); + } + + public destroy(): void { + this.renderer.destroy(); + } +} diff --git a/src/scenes/metaball/metaball.ts b/src/scenes/metaball/metaball.ts new file mode 100644 index 0000000..39970cc --- /dev/null +++ b/src/scenes/metaball/metaball.ts @@ -0,0 +1,21 @@ +import { vec2 } from 'gl-matrix'; +import { MetaCircle } from 'sdf-2d'; +import { Random } from '../../helper/random'; + +export class Metaball { + public shape = new MetaCircle(vec2.create(), Random.getRandomInRange(0.025, 0.075)); + + private direction = Random.getRandom() > 0.5 ? 1 : -1; + private speed = Random.getRandomInRange(0.5, 2); + constructor(private readonly center: vec2, private readonly size: vec2) {} + + public animate(currentTime: DOMHighResTimeStamp, width: number, height: number) { + vec2.set( + this.shape.center, + this.size.x * Math.cos(currentTime * this.speed * this.direction) + + this.center.x * width, + this.size.y * Math.sin(currentTime * this.speed * this.direction) + + this.center.y * height + ); + } +} diff --git a/src/scenes/rain/droplet.ts b/src/scenes/rain/droplet.ts index 8964a48..9585e2d 100644 --- a/src/scenes/rain/droplet.ts +++ b/src/scenes/rain/droplet.ts @@ -1,9 +1,9 @@ import { vec2 } from 'gl-matrix'; -import { Tunnel } from 'sdf-2d'; +import { Droplet } from 'sdf-2d'; import { Random } from '../../helper/random'; -export class Droplet { - public readonly drawable: Tunnel; +export class DropletWrapper { + public readonly drawable: Droplet; private speed = Random.getRandom() * 0.2 + 0.2; private position = vec2.fromValues( @@ -15,7 +15,7 @@ export class Droplet { constructor() { const size = Random.getRandom() * 2 + 2; - this.drawable = new Tunnel( + this.drawable = new Droplet( vec2.create(), vec2.create(), size + Random.getRandom() * 2 + 2, diff --git a/src/scenes/rain/rain-scene.ts b/src/scenes/rain/rain-scene.ts index 213cb0c..231041b 100644 --- a/src/scenes/rain/rain-scene.ts +++ b/src/scenes/rain/rain-scene.ts @@ -1,13 +1,13 @@ import { vec2 } from 'gl-matrix'; -import { CircleLight, compile, Renderer, Tunnel } from 'sdf-2d'; +import { CircleLight, compile, Droplet, Renderer } from 'sdf-2d'; import { prettyPrint } from '../../helper/pretty-print'; import { rgb } from '../../helper/rgb'; import { rgb255 } from '../../helper/rgb255'; import { Scene } from '../scene'; -import { Droplet } from './droplet'; +import { DropletWrapper } from './droplet'; export class RainScene implements Scene { - private droplets: Array = []; + private droplets: Array = []; private light1: CircleLight = new CircleLight(vec2.create(), rgb255(184, 41, 255), 2); private light2: CircleLight = new CircleLight(vec2.create(), rgb255(255, 31, 109), 2); @@ -23,12 +23,12 @@ export class RainScene implements Scene { this.overlay = overlay; this.renderer = await compile(canvas, [ { - ...Tunnel.descriptor, + ...Droplet.descriptor, shaderCombinationSteps: [0, 1, 2, 4, 8, 12, 16, 24], }, { ...CircleLight.descriptor, - shaderCombinationSteps: [2], + shaderCombinationSteps: [0, 2], }, ]); @@ -36,11 +36,10 @@ export class RainScene implements Scene { ambientLight: rgb(0.2, 0.2, 0.2), backgroundColor: rgb(1, 1, 1), colorPalette: [rgb(1, 1, 1), rgb(0.3, 1, 1), rgb(1, 1, 0), rgb(0.3, 1, 1)], - enableHighDpiRendering: false, }); for (let i = 0; i < (canvas.getBoundingClientRect().width / 800) * 20; i++) { - this.droplets.push(new Droplet()); + this.droplets.push(new DropletWrapper()); } } diff --git a/src/scenes/tunnel-scene.ts b/src/scenes/tunnel-scene.ts index 98e2709..583a973 100644 --- a/src/scenes/tunnel-scene.ts +++ b/src/scenes/tunnel-scene.ts @@ -72,7 +72,7 @@ export class TunnelScene implements Scene { }, { ...CircleLight.descriptor, - shaderCombinationSteps: [1, 2, 3, 4, 5, 6, 7], + shaderCombinationSteps: [0, 1, 2, 3, 4, 5, 6, 7], }, ]); @@ -81,7 +81,6 @@ export class TunnelScene implements Scene { ambientLight: rgb(0.35, 0.1, 0.45), backgroundColor: rgba(1, 1, 1, 1), colorPalette: [rgb(0.4, 0.5, 0.6), rgb(0, 0, 0), rgb(0, 0, 0), rgb(0, 0, 0)], - enableHighDpiRendering: false, }); for (let i = 0; i < 200; i++) {