diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..4eab217 --- /dev/null +++ b/biome.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.6/schema.json", + "root": true, + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "formatter": { + "indentStyle": "space", + "indentWidth": 4 + }, + "linter": { + "enabled": true + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + } +} diff --git a/bun.lock b/bun.lock index f115bb9..4adc2b6 100644 --- a/bun.lock +++ b/bun.lock @@ -9,13 +9,33 @@ "three": "^0.173.0", }, "devDependencies": { + "@biomejs/biome": "^2.4.6", + "@types/bun": "^1.3.10", "@types/three": "^0.173.0", - "typescript": "^5.8.2", - "vite": "^6.2.1", + "typescript": "^5.9.3", + "vite": "^6.4.1", }, }, }, "packages": { + "@biomejs/biome": ["@biomejs/biome@2.4.6", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.6", "@biomejs/cli-darwin-x64": "2.4.6", "@biomejs/cli-linux-arm64": "2.4.6", "@biomejs/cli-linux-arm64-musl": "2.4.6", "@biomejs/cli-linux-x64": "2.4.6", "@biomejs/cli-linux-x64-musl": "2.4.6", "@biomejs/cli-win32-arm64": "2.4.6", "@biomejs/cli-win32-x64": "2.4.6" }, "bin": { "biome": "bin/biome" } }, "sha512-QnHe81PMslpy3mnpL8DnO2M4S4ZnYPkjlGCLWBZT/3R9M6b5daArWMMtEfP52/n174RKnwRIf3oT8+wc9ihSfQ=="], + + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-NW18GSyxr+8sJIqgoGwVp5Zqm4SALH4b4gftIA0n62PTuBs6G2tHlwNAOj0Vq0KKSs7Sf88VjjmHh0O36EnzrQ=="], + + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-4uiE/9tuI7cnjtY9b07RgS7gGyYOAfIAGeVJWEfeCnAarOAS7qVmuRyX6d7JTKw28/mt+rUzMasYeZ+0R/U1Mw=="], + + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-kMLaI7OF5GN1Q8Doymjro1P8rVEoy7BKQALNz6fiR8IC1WKduoNyteBtJlHT7ASIL0Cx2jR6VUOBIbcB1B8pew=="], + + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-F/JdB7eN22txiTqHM5KhIVt0jVkzZwVYrdTR1O3Y4auBOQcXxHK4dxULf4z43QyZI5tsnQJrRBHZy7wwtL+B3A=="], + + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.6", "", { "os": "linux", "cpu": "x64" }, "sha512-oHXmUFEoH8Lql1xfc3QkFLiC1hGR7qedv5eKNlC185or+o4/4HiaU7vYODAH3peRCfsuLr1g6v2fK9dFFOYdyw=="], + + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.6", "", { "os": "linux", "cpu": "x64" }, "sha512-C9s98IPDu7DYarjlZNuzJKTjVHN03RUnmHV5htvqsx6vEUXCDSJ59DNwjKVD5XYoSS4N+BYhq3RTBAL8X6svEg=="], + + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-xzThn87Pf3YrOGTEODFGONmqXpTwUNxovQb72iaUOdcw8sBSY3+3WD8Hm9IhMYLnPi0n32s3L3NWU6+eSjfqFg=="], + + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.6", "", { "os": "win32", "cpu": "x64" }, "sha512-7++XhnsPlr1HDbor5amovPjOH6vsrFOCdp93iKXhFn6bcMUI6soodj3WWKfgEO6JosKU1W5n3uky3WW9RlRjTg=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], @@ -120,8 +140,12 @@ "@tweenjs/tween.js": ["@tweenjs/tween.js@23.1.3", "", {}, "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA=="], + "@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="], + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "@types/node": ["@types/node@25.3.5", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA=="], + "@types/stats.js": ["@types/stats.js@0.17.4", "", {}, "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA=="], "@types/three": ["@types/three@0.173.0", "", { "dependencies": { "@tweenjs/tween.js": "~23.1.3", "@types/stats.js": "*", "@types/webxr": "*", "@webgpu/types": "*", "fflate": "~0.8.2", "meshoptimizer": "~0.18.1" } }, "sha512-KtNjfI/CRB6JVKIVeZM1R3GYDX2wkoV2itNcQu2j4d7qkhjGOuB+s2oF6jl9mztycDLGMtrAnJQYxInC8Bb20A=="], @@ -130,6 +154,8 @@ "@webgpu/types": ["@webgpu/types@0.1.69", "", {}, "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ=="], + "bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="], + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], @@ -160,6 +186,8 @@ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], + "vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], } } diff --git a/justfile b/justfile new file mode 100644 index 0000000..8e6c110 --- /dev/null +++ b/justfile @@ -0,0 +1,13 @@ +default: + @just --list + +lint: + bun run lint + +typecheck: + bun run typecheck + +test: + bun run test + +check: lint typecheck test diff --git a/package.json b/package.json index ac9d34c..5019800 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,8 @@ "three": "^0.173.0" }, "devDependencies": { + "@biomejs/biome": "^2.4.6", + "@types/bun": "^1.3.10", "@types/three": "^0.173.0", "typescript": "^5.9.3", "vite": "^6.4.1" @@ -15,7 +17,11 @@ "scripts": { "dev": "vite", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "lint": "bunx biome check --write .", + "typecheck": "bunx tsc --noEmit", + "test": "bun test", + "check": "bun run lint && bun run typecheck && bun run test" }, "license": "MIT" } diff --git a/src/Config.ts b/src/Config.ts index 2db78c6..25f13da 100644 --- a/src/Config.ts +++ b/src/Config.ts @@ -1,14 +1,14 @@ export default { - worldSize: 1024, - antsCount: 12, - simulationStepsPerSecond: 60, - scentThreshold: 0.01, - scentFadeOutFactor: 0.001, - scentBlurRadius: 0.1, - scentMaxStorage: 1e6, - scentPerMarker: 200, - scentMaxPerCell: 10, - antSpeed: 1, - antRotationAngle: Math.PI / 30, - brushRadius: 20, -}; \ No newline at end of file + worldSize: 1024, + antsCount: 12, + simulationStepsPerSecond: 60, + scentThreshold: 0.01, + scentFadeOutFactor: 0.001, + scentBlurRadius: 0.1, + scentMaxStorage: 1e6, + scentPerMarker: 200, + scentMaxPerCell: 10, + antSpeed: 1, + antRotationAngle: Math.PI / 30, + brushRadius: 20, +}; diff --git a/src/GUI.ts b/src/GUI.ts index 7f74fa8..23a5c95 100644 --- a/src/GUI.ts +++ b/src/GUI.ts @@ -4,62 +4,62 @@ import Config from "./Config"; type EventHandler = () => void; class GUIController { - private gui: GUI = new GUI({ - width: 400, - }); - private listeners: Map = new Map(); + private gui: GUI = new GUI({ + width: 400, + }); + private listeners: Map = new Map(); - constructor() { - const simFolder = this.gui.addFolder("Simulation"); + constructor() { + const simFolder = this.gui.addFolder("Simulation"); - simFolder - .add(Config, "worldSize", 256, 4096) - .name("World size") - .step(1) - .onChange(() => this.emit("reset")); - simFolder - .add(Config, "antsCount", 0, 22) - .name("Ants count 2^") - .step(1) - .onChange(() => this.emit("reset")); - simFolder - .add(Config, "scentFadeOutFactor", 0, 0.01) - .name("Pheromone evaporation factor") - .step(0.0001) - .onChange(() => this.emit("reset")); - simFolder - .add(Config, "scentBlurRadius", 0, 0.5) - .name("Pheromone diffusion factor") - .step(0.01) - .onChange(() => this.emit("reset")); - simFolder - .add(Config, "simulationStepsPerSecond", 1, 500) - .name("Simulation steps per second") - .step(1); + simFolder + .add(Config, "worldSize", 256, 4096) + .name("World size") + .step(1) + .onChange(() => this.emit("reset")); + simFolder + .add(Config, "antsCount", 0, 22) + .name("Ants count 2^") + .step(1) + .onChange(() => this.emit("reset")); + simFolder + .add(Config, "scentFadeOutFactor", 0, 0.01) + .name("Pheromone evaporation factor") + .step(0.0001) + .onChange(() => this.emit("reset")); + simFolder + .add(Config, "scentBlurRadius", 0, 0.5) + .name("Pheromone diffusion factor") + .step(0.01) + .onChange(() => this.emit("reset")); + simFolder + .add(Config, "simulationStepsPerSecond", 1, 500) + .name("Simulation steps per second") + .step(1); - const controlsFolder = this.gui.addFolder("Controls"); + const controlsFolder = this.gui.addFolder("Controls"); - controlsFolder.add(Config, "brushRadius", 1, 100).name("Brush radius"); + controlsFolder.add(Config, "brushRadius", 1, 100).name("Brush radius"); - simFolder.open(); - controlsFolder.open(); - } + simFolder.open(); + controlsFolder.open(); + } - on(event: string, handler: EventHandler): void { - if (!this.listeners.has(event)) { - this.listeners.set(event, []); - } - this.listeners.get(event)!.push(handler); - } + on(event: string, handler: EventHandler): void { + if (!this.listeners.has(event)) { + this.listeners.set(event, []); + } + this.listeners.get(event)!.push(handler); + } - private emit(event: string): void { - const handlers = this.listeners.get(event); - if (handlers) { - for (const handler of handlers) { - handler(); - } - } - } + private emit(event: string): void { + const handlers = this.listeners.get(event); + if (handlers) { + for (const handler of handlers) { + handler(); + } + } + } } export default GUIController; diff --git a/src/Renderer.ts b/src/Renderer.ts index 6e7fee7..a91fc6a 100644 --- a/src/Renderer.ts +++ b/src/Renderer.ts @@ -1,177 +1,252 @@ -import * as THREE from 'three'; -import {SceneCollection} from "./App"; +import type { WebGLRenderTarget } from "three"; +import * as THREE from "three"; +import type { SceneCollection } from "./App"; import Config from "./Config"; -import {WebGLRenderTarget} from "three"; interface Resources { - worldRenderTarget: THREE.WebGLRenderTarget; - worldRenderTargetCopy: THREE.WebGLRenderTarget; - worldBlurredRenderTarget: THREE.WebGLRenderTarget; - antsDataRenderTarget0: THREE.WebGLRenderTarget; - antsDataRenderTarget1: THREE.WebGLRenderTarget; - antsDiscreteRenderTarget: THREE.WebGLRenderTarget; + worldRenderTarget: THREE.WebGLRenderTarget; + worldRenderTargetCopy: THREE.WebGLRenderTarget; + worldBlurredRenderTarget: THREE.WebGLRenderTarget; + antsDataRenderTarget0: THREE.WebGLRenderTarget; + antsDataRenderTarget1: THREE.WebGLRenderTarget; + antsDiscreteRenderTarget: THREE.WebGLRenderTarget; } export default class Renderer { - private renderer: THREE.WebGLRenderer; - public resources!: Resources; + private renderer: THREE.WebGLRenderer; + public resources!: Resources; - constructor(public canvas: HTMLCanvasElement) { - this.renderer = new THREE.WebGLRenderer({canvas}) + constructor(public canvas: HTMLCanvasElement) { + this.renderer = new THREE.WebGLRenderer({ canvas }); - this.initResources(); - } + this.initResources(); + } - private initResources() { - const antTextureSize = Math.round(Math.sqrt(2 ** Config.antsCount)); + private initResources() { + const antTextureSize = Math.round(Math.sqrt(2 ** Config.antsCount)); - this.resources = { - worldRenderTarget: new THREE.WebGLRenderTarget(Config.worldSize, Config.worldSize, { - format: THREE.RGBAFormat, - type: THREE.FloatType, - depthBuffer: false, - magFilter: THREE.LinearFilter, - minFilter: THREE.LinearFilter, - }), - worldRenderTargetCopy: new THREE.WebGLRenderTarget(Config.worldSize, Config.worldSize, { - format: THREE.RGBAFormat, - type: THREE.FloatType, - depthBuffer: false, - magFilter: THREE.NearestFilter, - minFilter: THREE.NearestFilter, - }), - worldBlurredRenderTarget: new THREE.WebGLRenderTarget(Config.worldSize, Config.worldSize, { - format: THREE.RGBAFormat, - type: THREE.FloatType, - depthBuffer: false, - magFilter: THREE.NearestFilter, - minFilter: THREE.NearestFilter, - }), - antsDataRenderTarget0: new THREE.WebGLRenderTarget(antTextureSize, antTextureSize, { - format: THREE.RGBAFormat, - type: THREE.FloatType, - depthBuffer: false, - magFilter: THREE.NearestFilter, - minFilter: THREE.NearestFilter, - }), - antsDataRenderTarget1: new THREE.WebGLRenderTarget(antTextureSize, antTextureSize, { - format: THREE.RGBAFormat, - type: THREE.FloatType, - depthBuffer: false, - magFilter: THREE.NearestFilter, - minFilter: THREE.NearestFilter, - }), - antsDiscreteRenderTarget: new THREE.WebGLRenderTarget(Config.worldSize, Config.worldSize, { - format: THREE.RGBAFormat, - type: THREE.UnsignedByteType, - depthBuffer: false, - magFilter: THREE.NearestFilter, - minFilter: THREE.NearestFilter, - }) - }; - } + this.resources = { + worldRenderTarget: new THREE.WebGLRenderTarget( + Config.worldSize, + Config.worldSize, + { + format: THREE.RGBAFormat, + type: THREE.FloatType, + depthBuffer: false, + magFilter: THREE.LinearFilter, + minFilter: THREE.LinearFilter, + }, + ), + worldRenderTargetCopy: new THREE.WebGLRenderTarget( + Config.worldSize, + Config.worldSize, + { + format: THREE.RGBAFormat, + type: THREE.FloatType, + depthBuffer: false, + magFilter: THREE.NearestFilter, + minFilter: THREE.NearestFilter, + }, + ), + worldBlurredRenderTarget: new THREE.WebGLRenderTarget( + Config.worldSize, + Config.worldSize, + { + format: THREE.RGBAFormat, + type: THREE.FloatType, + depthBuffer: false, + magFilter: THREE.NearestFilter, + minFilter: THREE.NearestFilter, + }, + ), + antsDataRenderTarget0: new THREE.WebGLRenderTarget( + antTextureSize, + antTextureSize, + { + format: THREE.RGBAFormat, + type: THREE.FloatType, + depthBuffer: false, + magFilter: THREE.NearestFilter, + minFilter: THREE.NearestFilter, + }, + ), + antsDataRenderTarget1: new THREE.WebGLRenderTarget( + antTextureSize, + antTextureSize, + { + format: THREE.RGBAFormat, + type: THREE.FloatType, + depthBuffer: false, + magFilter: THREE.NearestFilter, + minFilter: THREE.NearestFilter, + }, + ), + antsDiscreteRenderTarget: new THREE.WebGLRenderTarget( + Config.worldSize, + Config.worldSize, + { + format: THREE.RGBAFormat, + type: THREE.UnsignedByteType, + depthBuffer: false, + magFilter: THREE.NearestFilter, + minFilter: THREE.NearestFilter, + }, + ), + }; + } - public renderSimulation(scenes: SceneCollection) { - const [antsComputeSource, antsComputeTarget] = scenes.ants.getRenderTargets(); + public renderSimulation(scenes: SceneCollection) { + const [antsComputeSource, antsComputeTarget] = + scenes.ants.getRenderTargets(); - this.setViewportFromRT(this.resources.worldBlurredRenderTarget); - this.renderer.setRenderTarget(this.resources.worldBlurredRenderTarget); - scenes.worldBlur.material.uniforms.tWorld.value = this.resources.worldRenderTarget.texture; - this.renderer.render(scenes.worldBlur, scenes.worldBlur.camera); + this.setViewportFromRT(this.resources.worldBlurredRenderTarget); + this.renderer.setRenderTarget(this.resources.worldBlurredRenderTarget); + scenes.worldBlur.material.uniforms.tWorld.value = + this.resources.worldRenderTarget.texture; + this.renderer.render(scenes.worldBlur, scenes.worldBlur.camera); - this.setViewportFromRT(antsComputeTarget); - this.renderer.setRenderTarget(antsComputeTarget); - scenes.ants.material.uniforms.tLastState.value = antsComputeSource.texture; - scenes.ants.material.uniforms.tWorld.value = this.resources.worldBlurredRenderTarget.texture; - this.renderer.render(scenes.ants, scenes.ants.camera); + this.setViewportFromRT(antsComputeTarget); + this.renderer.setRenderTarget(antsComputeTarget); + scenes.ants.material.uniforms.tLastState.value = + antsComputeSource.texture; + scenes.ants.material.uniforms.tWorld.value = + this.resources.worldBlurredRenderTarget.texture; + this.renderer.render(scenes.ants, scenes.ants.camera); - this.setViewportFromRT(this.resources.antsDiscreteRenderTarget); - this.renderer.setRenderTarget(this.resources.antsDiscreteRenderTarget); - scenes.discretize.material.uniforms.tDataCurrent.value = antsComputeTarget.texture; - scenes.discretize.material.uniforms.tDataLast.value = antsComputeSource.texture; - this.renderer.render(scenes.discretize, scenes.discretize.camera); + this.setViewportFromRT(this.resources.antsDiscreteRenderTarget); + this.renderer.setRenderTarget(this.resources.antsDiscreteRenderTarget); + scenes.discretize.material.uniforms.tDataCurrent.value = + antsComputeTarget.texture; + scenes.discretize.material.uniforms.tDataLast.value = + antsComputeSource.texture; + this.renderer.render(scenes.discretize, scenes.discretize.camera); - this.setViewportFromRT(this.resources.worldRenderTarget); - this.renderer.setRenderTarget(this.resources.worldRenderTarget); - scenes.world.material.uniforms.tLastState.value = this.resources.worldBlurredRenderTarget.texture; - scenes.world.material.uniforms.tDiscreteAnts.value = this.resources.antsDiscreteRenderTarget.texture; - this.renderer.render(scenes.world, scenes.world.camera); + this.setViewportFromRT(this.resources.worldRenderTarget); + this.renderer.setRenderTarget(this.resources.worldRenderTarget); + scenes.world.material.uniforms.tLastState.value = + this.resources.worldBlurredRenderTarget.texture; + scenes.world.material.uniforms.tDiscreteAnts.value = + this.resources.antsDiscreteRenderTarget.texture; + this.renderer.render(scenes.world, scenes.world.camera); - scenes.screen.material.uniforms.tData.value = antsComputeTarget.texture; - scenes.screen.groundMaterial.uniforms.map.value = this.resources.worldRenderTargetCopy.texture; - } + scenes.screen.material.uniforms.tData.value = antsComputeTarget.texture; + scenes.screen.groundMaterial.uniforms.map.value = + this.resources.worldRenderTargetCopy.texture; + } - private setViewportFromRT(rt: WebGLRenderTarget) { - this.renderer.setViewport(0, 0, rt.width, rt.height); - } + private setViewportFromRT(rt: WebGLRenderTarget) { + this.renderer.setViewport(0, 0, rt.width, rt.height); + } - public renderToScreen(scenes: SceneCollection) { - this.setViewportFromRT(this.resources.worldRenderTargetCopy); - this.renderer.setRenderTarget(this.resources.worldRenderTargetCopy); - scenes.draw.material.uniforms.tWorld.value = this.resources.worldRenderTarget.texture; - scenes.draw.material.uniforms.pointerPosition.value = scenes.screen.pointerPosition; - scenes.draw.material.uniforms.drawMode.value = scenes.screen.drawMode; - scenes.draw.material.uniforms.brushRadius.value = Config.brushRadius; - this.renderer.render(scenes.draw, scenes.draw.camera); - this.renderer.copyFramebufferToTexture(this.resources.worldRenderTarget.texture, new THREE.Vector2()); + public renderToScreen(scenes: SceneCollection) { + this.setViewportFromRT(this.resources.worldRenderTargetCopy); + this.renderer.setRenderTarget(this.resources.worldRenderTargetCopy); + scenes.draw.material.uniforms.tWorld.value = + this.resources.worldRenderTarget.texture; + scenes.draw.material.uniforms.pointerPosition.value = + scenes.screen.pointerPosition; + scenes.draw.material.uniforms.drawMode.value = scenes.screen.drawMode; + scenes.draw.material.uniforms.brushRadius.value = Config.brushRadius; + this.renderer.render(scenes.draw, scenes.draw.camera); + this.renderer.copyFramebufferToTexture( + this.resources.worldRenderTarget.texture, + new THREE.Vector2(), + ); - this.renderer.setViewport(0, 0, scenes.screen.renderWidth, scenes.screen.renderHeight); - this.renderer.setRenderTarget(null); - this.renderer.render(scenes.screen, scenes.screen.camera); - } + this.renderer.setViewport( + 0, + 0, + scenes.screen.renderWidth, + scenes.screen.renderHeight, + ); + this.renderer.setRenderTarget(null); + this.renderer.render(scenes.screen, scenes.screen.camera); + } - public resizeCanvas(width: number, height: number) { - this.canvas.width = width; - this.canvas.height = height; - } + public resizeCanvas(width: number, height: number) { + this.canvas.width = width; + this.canvas.height = height; + } - public getCommonMaterialDefines(): Record { - return { - WORLD_SIZE: Renderer.convertNumberToFloatString(Config.worldSize), - SCENT_THRESHOLD: Renderer.convertNumberToFloatString(Config.scentThreshold), - SCENT_FADE_OUT_FACTOR: Renderer.convertNumberToFloatString(Config.scentFadeOutFactor), - SCENT_BLUR_RADIUS: Renderer.convertNumberToFloatString(Config.scentBlurRadius), - SCENT_MAX_STORAGE: Renderer.convertNumberToFloatString(Config.scentMaxStorage), - SCENT_PER_MARKER: Renderer.convertNumberToFloatString(Config.scentPerMarker), - SCENT_MAX_PER_CELL: Renderer.convertNumberToFloatString(Config.scentMaxPerCell), - ANT_SPEED: Renderer.convertNumberToFloatString(Config.antSpeed), - ANT_ROTATION_ANGLE: Renderer.convertNumberToFloatString(Config.antRotationAngle) - }; - } + public getCommonMaterialDefines(): Record { + return { + WORLD_SIZE: Renderer.convertNumberToFloatString(Config.worldSize), + SCENT_THRESHOLD: Renderer.convertNumberToFloatString( + Config.scentThreshold, + ), + SCENT_FADE_OUT_FACTOR: Renderer.convertNumberToFloatString( + Config.scentFadeOutFactor, + ), + SCENT_BLUR_RADIUS: Renderer.convertNumberToFloatString( + Config.scentBlurRadius, + ), + SCENT_MAX_STORAGE: Renderer.convertNumberToFloatString( + Config.scentMaxStorage, + ), + SCENT_PER_MARKER: Renderer.convertNumberToFloatString( + Config.scentPerMarker, + ), + SCENT_MAX_PER_CELL: Renderer.convertNumberToFloatString( + Config.scentMaxPerCell, + ), + ANT_SPEED: Renderer.convertNumberToFloatString(Config.antSpeed), + ANT_ROTATION_ANGLE: Renderer.convertNumberToFloatString( + Config.antRotationAngle, + ), + }; + } - public reset(scenes: SceneCollection) { - const antTextureSize = Math.round(Math.sqrt(2 ** Config.antsCount)); + public reset(scenes: SceneCollection) { + const antTextureSize = Math.round(Math.sqrt(2 ** Config.antsCount)); - this.resources.worldRenderTarget.setSize(Config.worldSize, Config.worldSize) - this.renderer.setRenderTarget(this.resources.worldRenderTarget); - this.renderer.clear(); + this.resources.worldRenderTarget.setSize( + Config.worldSize, + Config.worldSize, + ); + this.renderer.setRenderTarget(this.resources.worldRenderTarget); + this.renderer.clear(); - this.resources.worldRenderTargetCopy.setSize(Config.worldSize, Config.worldSize) - this.renderer.setRenderTarget(this.resources.worldRenderTargetCopy); - this.renderer.clear(); + this.resources.worldRenderTargetCopy.setSize( + Config.worldSize, + Config.worldSize, + ); + this.renderer.setRenderTarget(this.resources.worldRenderTargetCopy); + this.renderer.clear(); - this.resources.worldBlurredRenderTarget.setSize(Config.worldSize, Config.worldSize) - this.renderer.setRenderTarget(this.resources.worldBlurredRenderTarget); - this.renderer.clear(); + this.resources.worldBlurredRenderTarget.setSize( + Config.worldSize, + Config.worldSize, + ); + this.renderer.setRenderTarget(this.resources.worldBlurredRenderTarget); + this.renderer.clear(); - this.resources.antsDataRenderTarget0.setSize(antTextureSize, antTextureSize) - this.renderer.setRenderTarget(this.resources.antsDataRenderTarget0); - this.renderer.clear(); + this.resources.antsDataRenderTarget0.setSize( + antTextureSize, + antTextureSize, + ); + this.renderer.setRenderTarget(this.resources.antsDataRenderTarget0); + this.renderer.clear(); - this.resources.antsDataRenderTarget1.setSize(antTextureSize, antTextureSize) - this.renderer.setRenderTarget(this.resources.antsDataRenderTarget1); - this.renderer.clear(); + this.resources.antsDataRenderTarget1.setSize( + antTextureSize, + antTextureSize, + ); + this.renderer.setRenderTarget(this.resources.antsDataRenderTarget1); + this.renderer.clear(); - this.resources.antsDiscreteRenderTarget.setSize(Config.worldSize, Config.worldSize) - this.renderer.setRenderTarget(this.resources.antsDiscreteRenderTarget); - this.renderer.clear(); + this.resources.antsDiscreteRenderTarget.setSize( + Config.worldSize, + Config.worldSize, + ); + this.renderer.setRenderTarget(this.resources.antsDiscreteRenderTarget); + this.renderer.clear(); - for (const scene of Object.values(scenes)) { - scene.recompileMaterials(); - } - } + for (const scene of Object.values(scenes)) { + scene.recompileMaterials(); + } + } - static convertNumberToFloatString(n: number): string { - return n.toFixed(8); - } -} \ No newline at end of file + static convertNumberToFloatString(n: number): string { + return n.toFixed(8); + } +} diff --git a/src/__tests__/placeholder.test.ts b/src/__tests__/placeholder.test.ts new file mode 100644 index 0000000..1a97f5c --- /dev/null +++ b/src/__tests__/placeholder.test.ts @@ -0,0 +1,7 @@ +import { describe, expect, test } from "bun:test"; + +describe("placeholder", () => { + test("passes", () => { + expect(true).toBe(true); + }); +}); diff --git a/src/global.d.ts b/src/global.d.ts index bbb2963..0aed40e 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -1,8 +1,8 @@ -declare module '*.vert' { - const content: string; - export default content; +declare module "*.vert" { + const content: string; + export default content; +} +declare module "*.frag" { + const content: string; + export default content; } -declare module '*.frag' { - const content: string; - export default content; -} \ No newline at end of file diff --git a/src/scenes/AntsComputeScene.ts b/src/scenes/AntsComputeScene.ts index bd4b1e9..d6d82b0 100644 --- a/src/scenes/AntsComputeScene.ts +++ b/src/scenes/AntsComputeScene.ts @@ -1,58 +1,56 @@ -import * as THREE from 'three'; -import {WebGLRenderTarget} from 'three'; -import Renderer from "../Renderer"; -import AbstractScene from "./AbstractScene"; +import type { WebGLRenderTarget } from "three"; +import * as THREE from "three"; +import type Renderer from "../Renderer"; +import fragmentShader from "../shaders/antsCompute.frag"; +import vertexShader from "../shaders/antsCompute.vert"; import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry"; -import fragmentShader from '../shaders/antsCompute.frag'; -import vertexShader from '../shaders/antsCompute.vert'; +import AbstractScene from "./AbstractScene"; export default class AntsComputeScene extends AbstractScene { - public camera: THREE.OrthographicCamera = new THREE.OrthographicCamera(); - public material: THREE.RawShaderMaterial; - private renderTargets: [WebGLRenderTarget, WebGLRenderTarget]; + public camera: THREE.OrthographicCamera = new THREE.OrthographicCamera(); + public material: THREE.RawShaderMaterial; + private renderTargets: [WebGLRenderTarget, WebGLRenderTarget]; - constructor(renderer: Renderer) { - super(renderer); + constructor(renderer: Renderer) { + super(renderer); - const geometry = new FullScreenTriangleGeometry(); - const material = new THREE.RawShaderMaterial({ - uniforms: { - uTime: {value: 0}, - tLastState: {value: null}, - tWorld: {value: null}, - }, - vertexShader, - fragmentShader, - defines: this.renderer.getCommonMaterialDefines(), - glslVersion: THREE.GLSL3 - }); - const mesh = new THREE.Mesh(geometry, material); - this.add(mesh); + const geometry = new FullScreenTriangleGeometry(); + const material = new THREE.RawShaderMaterial({ + uniforms: { + uTime: { value: 0 }, + tLastState: { value: null }, + tWorld: { value: null }, + }, + vertexShader, + fragmentShader, + defines: this.renderer.getCommonMaterialDefines(), + glslVersion: THREE.GLSL3, + }); + const mesh = new THREE.Mesh(geometry, material); + this.add(mesh); - this.material = material; + this.material = material; - this.renderTargets = [ - this.renderer.resources.antsDataRenderTarget0, - this.renderer.resources.antsDataRenderTarget1 - ]; - } + this.renderTargets = [ + this.renderer.resources.antsDataRenderTarget0, + this.renderer.resources.antsDataRenderTarget1, + ]; + } - public getRenderTargets(): [WebGLRenderTarget, WebGLRenderTarget] { - this.renderTargets.reverse(); + public getRenderTargets(): [WebGLRenderTarget, WebGLRenderTarget] { + this.renderTargets.reverse(); - return this.renderTargets; - } + return this.renderTargets; + } - public recompileMaterials() { - this.material.defines = this.renderer.getCommonMaterialDefines(); - this.material.needsUpdate = true; - } + public recompileMaterials() { + this.material.defines = this.renderer.getCommonMaterialDefines(); + this.material.needsUpdate = true; + } - public resize(width: number, height: number) { + public resize(width: number, height: number) {} - } - - public update() { - this.material.uniforms.uTime.value = performance.now(); - } -} \ No newline at end of file + public update() { + this.material.uniforms.uTime.value = performance.now(); + } +} diff --git a/src/scenes/DrawScene.ts b/src/scenes/DrawScene.ts index 12c3268..92a5ec2 100644 --- a/src/scenes/DrawScene.ts +++ b/src/scenes/DrawScene.ts @@ -1,46 +1,43 @@ -import * as THREE from 'three'; -import Renderer from "../Renderer"; -import AbstractScene from "./AbstractScene"; +import * as THREE from "three"; +import type Renderer from "../Renderer"; +import fragmentShader from "../shaders/draw.frag"; +import vertexShader from "../shaders/draw.vert"; import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry"; -import fragmentShader from '../shaders/draw.frag'; -import vertexShader from '../shaders/draw.vert'; +import AbstractScene from "./AbstractScene"; export default class DrawScene extends AbstractScene { - public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera(); - public readonly material: THREE.RawShaderMaterial; + public readonly camera: THREE.OrthographicCamera = + new THREE.OrthographicCamera(); + public readonly material: THREE.RawShaderMaterial; - constructor(renderer: Renderer) { - super(renderer); + constructor(renderer: Renderer) { + super(renderer); - const geometry = new FullScreenTriangleGeometry(); - const material = new THREE.RawShaderMaterial({ - uniforms: { - tWorld: {value: null}, - pointerPosition: {value: new THREE.Vector2()}, - drawMode: {value: 0}, - brushRadius: {value: 0}, - }, - vertexShader, - fragmentShader, - defines: this.renderer.getCommonMaterialDefines(), - glslVersion: THREE.GLSL3 - }); - const mesh = new THREE.Mesh(geometry, material); - this.add(mesh); + const geometry = new FullScreenTriangleGeometry(); + const material = new THREE.RawShaderMaterial({ + uniforms: { + tWorld: { value: null }, + pointerPosition: { value: new THREE.Vector2() }, + drawMode: { value: 0 }, + brushRadius: { value: 0 }, + }, + vertexShader, + fragmentShader, + defines: this.renderer.getCommonMaterialDefines(), + glslVersion: THREE.GLSL3, + }); + const mesh = new THREE.Mesh(geometry, material); + this.add(mesh); - this.material = material; - } + this.material = material; + } - public recompileMaterials() { - this.material.defines = this.renderer.getCommonMaterialDefines(); - this.material.needsUpdate = true; - } + public recompileMaterials() { + this.material.defines = this.renderer.getCommonMaterialDefines(); + this.material.needsUpdate = true; + } - public resize(width: number, height: number) { + public resize(width: number, height: number) {} - } - - public update() { - - } -} \ No newline at end of file + public update() {} +} diff --git a/src/scenes/WorldBlurScene.ts b/src/scenes/WorldBlurScene.ts index 9f8a298..068a026 100644 --- a/src/scenes/WorldBlurScene.ts +++ b/src/scenes/WorldBlurScene.ts @@ -1,43 +1,40 @@ -import * as THREE from 'three'; -import Renderer from "../Renderer"; -import AbstractScene from "./AbstractScene"; +import * as THREE from "three"; +import type Renderer from "../Renderer"; +import fragmentShader from "../shaders/worldBlur.frag"; +import vertexShader from "../shaders/worldBlur.vert"; import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry"; -import fragmentShader from '../shaders/worldBlur.frag'; -import vertexShader from '../shaders/worldBlur.vert'; +import AbstractScene from "./AbstractScene"; export default class WorldBlurScene extends AbstractScene { - public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera(); - public readonly material: THREE.RawShaderMaterial; + public readonly camera: THREE.OrthographicCamera = + new THREE.OrthographicCamera(); + public readonly material: THREE.RawShaderMaterial; - constructor(renderer: Renderer) { - super(renderer); + constructor(renderer: Renderer) { + super(renderer); - const geometry = new FullScreenTriangleGeometry(); - const material = new THREE.RawShaderMaterial({ - uniforms: { - tWorld: {value: null}, - }, - vertexShader, - fragmentShader, - defines: this.renderer.getCommonMaterialDefines(), - glslVersion: THREE.GLSL3 - }); - const mesh = new THREE.Mesh(geometry, material); - this.add(mesh); + const geometry = new FullScreenTriangleGeometry(); + const material = new THREE.RawShaderMaterial({ + uniforms: { + tWorld: { value: null }, + }, + vertexShader, + fragmentShader, + defines: this.renderer.getCommonMaterialDefines(), + glslVersion: THREE.GLSL3, + }); + const mesh = new THREE.Mesh(geometry, material); + this.add(mesh); - this.material = material; - } + this.material = material; + } - public recompileMaterials() { - this.material.defines = this.renderer.getCommonMaterialDefines(); - this.material.needsUpdate = true; - } + public recompileMaterials() { + this.material.defines = this.renderer.getCommonMaterialDefines(); + this.material.needsUpdate = true; + } - public resize(width: number, height: number) { + public resize(width: number, height: number) {} - } - - public update() { - - } -} \ No newline at end of file + public update() {} +} diff --git a/src/scenes/WorldComputeScene.ts b/src/scenes/WorldComputeScene.ts index 0d47c71..b390cbc 100644 --- a/src/scenes/WorldComputeScene.ts +++ b/src/scenes/WorldComputeScene.ts @@ -1,44 +1,41 @@ -import * as THREE from 'three'; -import Renderer from "../Renderer"; -import AbstractScene from "./AbstractScene"; +import * as THREE from "three"; +import type Renderer from "../Renderer"; +import fragmentShader from "../shaders/world.frag"; +import vertexShader from "../shaders/world.vert"; import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry"; -import fragmentShader from '../shaders/world.frag'; -import vertexShader from '../shaders/world.vert'; +import AbstractScene from "./AbstractScene"; export default class WorldComputeScene extends AbstractScene { - public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera(); - public readonly material: THREE.RawShaderMaterial; + public readonly camera: THREE.OrthographicCamera = + new THREE.OrthographicCamera(); + public readonly material: THREE.RawShaderMaterial; - constructor(renderer: Renderer) { - super(renderer); + constructor(renderer: Renderer) { + super(renderer); - const geometry = new FullScreenTriangleGeometry(); - const material = new THREE.RawShaderMaterial({ - uniforms: { - tLastState: {value: null}, - tDiscreteAnts: {value: null} - }, - vertexShader, - fragmentShader, - defines: this.renderer.getCommonMaterialDefines(), - glslVersion: THREE.GLSL3 - }); - const mesh = new THREE.Mesh(geometry, material); - this.add(mesh); + const geometry = new FullScreenTriangleGeometry(); + const material = new THREE.RawShaderMaterial({ + uniforms: { + tLastState: { value: null }, + tDiscreteAnts: { value: null }, + }, + vertexShader, + fragmentShader, + defines: this.renderer.getCommonMaterialDefines(), + glslVersion: THREE.GLSL3, + }); + const mesh = new THREE.Mesh(geometry, material); + this.add(mesh); - this.material = material; - } + this.material = material; + } - public recompileMaterials() { - this.material.defines = this.renderer.getCommonMaterialDefines(); - this.material.needsUpdate = true; - } + public recompileMaterials() { + this.material.defines = this.renderer.getCommonMaterialDefines(); + this.material.needsUpdate = true; + } - public resize(width: number, height: number) { + public resize(width: number, height: number) {} - } - - public update() { - - } -} \ No newline at end of file + public update() {} +} diff --git a/src/utils/FullScreenTriangleGeometry.ts b/src/utils/FullScreenTriangleGeometry.ts index db1435e..c30b37d 100644 --- a/src/utils/FullScreenTriangleGeometry.ts +++ b/src/utils/FullScreenTriangleGeometry.ts @@ -1,28 +1,17 @@ import * as THREE from "three"; -const positionBuffer = new Float32Array([ - -1, 3, 0, - -1, -1, 0, - 3, -1, 0, -]); +const positionBuffer = new Float32Array([-1, 3, 0, -1, -1, 0, 3, -1, 0]); -const uvBuffer = new Float32Array([ - 0, 2, - 0, 0, - 2, 0 -]); +const uvBuffer = new Float32Array([0, 2, 0, 0, 2, 0]); export default class FullScreenTriangleGeometry extends THREE.BufferGeometry { - constructor() { - super(); + constructor() { + super(); - this.setAttribute( - 'position', - new THREE.BufferAttribute(positionBuffer, 3) - ); - this.setAttribute( - 'uv', - new THREE.BufferAttribute(uvBuffer, 2) - ); - } -} \ No newline at end of file + this.setAttribute( + "position", + new THREE.BufferAttribute(positionBuffer, 3), + ); + this.setAttribute("uv", new THREE.BufferAttribute(uvBuffer, 2)); + } +}