Add tooling setup with biome, justfile, and check pipeline

This commit is contained in:
Jared Miller 2026-03-09 10:39:31 -04:00
parent a3bbc258be
commit 03679622a3
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C
14 changed files with 524 additions and 393 deletions

24
biome.json Normal file
View file

@ -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"
}
}
}
}

View file

@ -9,13 +9,33 @@
"three": "^0.173.0", "three": "^0.173.0",
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^2.4.6",
"@types/bun": "^1.3.10",
"@types/three": "^0.173.0", "@types/three": "^0.173.0",
"typescript": "^5.8.2", "typescript": "^5.9.3",
"vite": "^6.2.1", "vite": "^6.4.1",
}, },
}, },
}, },
"packages": { "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/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=="], "@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=="], "@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/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/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=="], "@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=="], "@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=="], "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=="], "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=="], "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=="], "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=="],
} }
} }

13
justfile Normal file
View file

@ -0,0 +1,13 @@
default:
@just --list
lint:
bun run lint
typecheck:
bun run typecheck
test:
bun run test
check: lint typecheck test

View file

@ -8,6 +8,8 @@
"three": "^0.173.0" "three": "^0.173.0"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^2.4.6",
"@types/bun": "^1.3.10",
"@types/three": "^0.173.0", "@types/three": "^0.173.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vite": "^6.4.1" "vite": "^6.4.1"
@ -15,7 +17,11 @@
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "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" "license": "MIT"
} }

View file

@ -1,14 +1,14 @@
export default { export default {
worldSize: 1024, worldSize: 1024,
antsCount: 12, antsCount: 12,
simulationStepsPerSecond: 60, simulationStepsPerSecond: 60,
scentThreshold: 0.01, scentThreshold: 0.01,
scentFadeOutFactor: 0.001, scentFadeOutFactor: 0.001,
scentBlurRadius: 0.1, scentBlurRadius: 0.1,
scentMaxStorage: 1e6, scentMaxStorage: 1e6,
scentPerMarker: 200, scentPerMarker: 200,
scentMaxPerCell: 10, scentMaxPerCell: 10,
antSpeed: 1, antSpeed: 1,
antRotationAngle: Math.PI / 30, antRotationAngle: Math.PI / 30,
brushRadius: 20, brushRadius: 20,
}; };

View file

@ -4,62 +4,62 @@ import Config from "./Config";
type EventHandler = () => void; type EventHandler = () => void;
class GUIController { class GUIController {
private gui: GUI = new GUI({ private gui: GUI = new GUI({
width: 400, width: 400,
}); });
private listeners: Map<string, EventHandler[]> = new Map(); private listeners: Map<string, EventHandler[]> = new Map();
constructor() { constructor() {
const simFolder = this.gui.addFolder("Simulation"); const simFolder = this.gui.addFolder("Simulation");
simFolder simFolder
.add(Config, "worldSize", 256, 4096) .add(Config, "worldSize", 256, 4096)
.name("World size") .name("World size")
.step(1) .step(1)
.onChange(() => this.emit("reset")); .onChange(() => this.emit("reset"));
simFolder simFolder
.add(Config, "antsCount", 0, 22) .add(Config, "antsCount", 0, 22)
.name("Ants count 2^") .name("Ants count 2^")
.step(1) .step(1)
.onChange(() => this.emit("reset")); .onChange(() => this.emit("reset"));
simFolder simFolder
.add(Config, "scentFadeOutFactor", 0, 0.01) .add(Config, "scentFadeOutFactor", 0, 0.01)
.name("Pheromone evaporation factor") .name("Pheromone evaporation factor")
.step(0.0001) .step(0.0001)
.onChange(() => this.emit("reset")); .onChange(() => this.emit("reset"));
simFolder simFolder
.add(Config, "scentBlurRadius", 0, 0.5) .add(Config, "scentBlurRadius", 0, 0.5)
.name("Pheromone diffusion factor") .name("Pheromone diffusion factor")
.step(0.01) .step(0.01)
.onChange(() => this.emit("reset")); .onChange(() => this.emit("reset"));
simFolder simFolder
.add(Config, "simulationStepsPerSecond", 1, 500) .add(Config, "simulationStepsPerSecond", 1, 500)
.name("Simulation steps per second") .name("Simulation steps per second")
.step(1); .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(); simFolder.open();
controlsFolder.open(); controlsFolder.open();
} }
on(event: string, handler: EventHandler): void { on(event: string, handler: EventHandler): void {
if (!this.listeners.has(event)) { if (!this.listeners.has(event)) {
this.listeners.set(event, []); this.listeners.set(event, []);
} }
this.listeners.get(event)!.push(handler); this.listeners.get(event)!.push(handler);
} }
private emit(event: string): void { private emit(event: string): void {
const handlers = this.listeners.get(event); const handlers = this.listeners.get(event);
if (handlers) { if (handlers) {
for (const handler of handlers) { for (const handler of handlers) {
handler(); handler();
} }
} }
} }
} }
export default GUIController; export default GUIController;

View file

@ -1,177 +1,252 @@
import * as THREE from 'three'; import type { WebGLRenderTarget } from "three";
import {SceneCollection} from "./App"; import * as THREE from "three";
import type { SceneCollection } from "./App";
import Config from "./Config"; import Config from "./Config";
import {WebGLRenderTarget} from "three";
interface Resources { interface Resources {
worldRenderTarget: THREE.WebGLRenderTarget; worldRenderTarget: THREE.WebGLRenderTarget;
worldRenderTargetCopy: THREE.WebGLRenderTarget; worldRenderTargetCopy: THREE.WebGLRenderTarget;
worldBlurredRenderTarget: THREE.WebGLRenderTarget; worldBlurredRenderTarget: THREE.WebGLRenderTarget;
antsDataRenderTarget0: THREE.WebGLRenderTarget; antsDataRenderTarget0: THREE.WebGLRenderTarget;
antsDataRenderTarget1: THREE.WebGLRenderTarget; antsDataRenderTarget1: THREE.WebGLRenderTarget;
antsDiscreteRenderTarget: THREE.WebGLRenderTarget; antsDiscreteRenderTarget: THREE.WebGLRenderTarget;
} }
export default class Renderer { export default class Renderer {
private renderer: THREE.WebGLRenderer; private renderer: THREE.WebGLRenderer;
public resources!: Resources; public resources!: Resources;
constructor(public canvas: HTMLCanvasElement) { constructor(public canvas: HTMLCanvasElement) {
this.renderer = new THREE.WebGLRenderer({canvas}) this.renderer = new THREE.WebGLRenderer({ canvas });
this.initResources(); this.initResources();
} }
private initResources() { private initResources() {
const antTextureSize = Math.round(Math.sqrt(2 ** Config.antsCount)); const antTextureSize = Math.round(Math.sqrt(2 ** Config.antsCount));
this.resources = { this.resources = {
worldRenderTarget: new THREE.WebGLRenderTarget(Config.worldSize, Config.worldSize, { worldRenderTarget: new THREE.WebGLRenderTarget(
format: THREE.RGBAFormat, Config.worldSize,
type: THREE.FloatType, Config.worldSize,
depthBuffer: false, {
magFilter: THREE.LinearFilter, format: THREE.RGBAFormat,
minFilter: THREE.LinearFilter, type: THREE.FloatType,
}), depthBuffer: false,
worldRenderTargetCopy: new THREE.WebGLRenderTarget(Config.worldSize, Config.worldSize, { magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat, minFilter: THREE.LinearFilter,
type: THREE.FloatType, },
depthBuffer: false, ),
magFilter: THREE.NearestFilter, worldRenderTargetCopy: new THREE.WebGLRenderTarget(
minFilter: THREE.NearestFilter, Config.worldSize,
}), Config.worldSize,
worldBlurredRenderTarget: new THREE.WebGLRenderTarget(Config.worldSize, Config.worldSize, { {
format: THREE.RGBAFormat, format: THREE.RGBAFormat,
type: THREE.FloatType, type: THREE.FloatType,
depthBuffer: false, depthBuffer: false,
magFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter,
minFilter: THREE.NearestFilter, minFilter: THREE.NearestFilter,
}), },
antsDataRenderTarget0: new THREE.WebGLRenderTarget(antTextureSize, antTextureSize, { ),
format: THREE.RGBAFormat, worldBlurredRenderTarget: new THREE.WebGLRenderTarget(
type: THREE.FloatType, Config.worldSize,
depthBuffer: false, Config.worldSize,
magFilter: THREE.NearestFilter, {
minFilter: THREE.NearestFilter, format: THREE.RGBAFormat,
}), type: THREE.FloatType,
antsDataRenderTarget1: new THREE.WebGLRenderTarget(antTextureSize, antTextureSize, { depthBuffer: false,
format: THREE.RGBAFormat, magFilter: THREE.NearestFilter,
type: THREE.FloatType, minFilter: THREE.NearestFilter,
depthBuffer: false, },
magFilter: THREE.NearestFilter, ),
minFilter: THREE.NearestFilter, antsDataRenderTarget0: new THREE.WebGLRenderTarget(
}), antTextureSize,
antsDiscreteRenderTarget: new THREE.WebGLRenderTarget(Config.worldSize, Config.worldSize, { antTextureSize,
format: THREE.RGBAFormat, {
type: THREE.UnsignedByteType, format: THREE.RGBAFormat,
depthBuffer: false, type: THREE.FloatType,
magFilter: THREE.NearestFilter, depthBuffer: false,
minFilter: THREE.NearestFilter, 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) { public renderSimulation(scenes: SceneCollection) {
const [antsComputeSource, antsComputeTarget] = scenes.ants.getRenderTargets(); const [antsComputeSource, antsComputeTarget] =
scenes.ants.getRenderTargets();
this.setViewportFromRT(this.resources.worldBlurredRenderTarget); this.setViewportFromRT(this.resources.worldBlurredRenderTarget);
this.renderer.setRenderTarget(this.resources.worldBlurredRenderTarget); this.renderer.setRenderTarget(this.resources.worldBlurredRenderTarget);
scenes.worldBlur.material.uniforms.tWorld.value = this.resources.worldRenderTarget.texture; scenes.worldBlur.material.uniforms.tWorld.value =
this.renderer.render(scenes.worldBlur, scenes.worldBlur.camera); this.resources.worldRenderTarget.texture;
this.renderer.render(scenes.worldBlur, scenes.worldBlur.camera);
this.setViewportFromRT(antsComputeTarget); this.setViewportFromRT(antsComputeTarget);
this.renderer.setRenderTarget(antsComputeTarget); this.renderer.setRenderTarget(antsComputeTarget);
scenes.ants.material.uniforms.tLastState.value = antsComputeSource.texture; scenes.ants.material.uniforms.tLastState.value =
scenes.ants.material.uniforms.tWorld.value = this.resources.worldBlurredRenderTarget.texture; antsComputeSource.texture;
this.renderer.render(scenes.ants, scenes.ants.camera); scenes.ants.material.uniforms.tWorld.value =
this.resources.worldBlurredRenderTarget.texture;
this.renderer.render(scenes.ants, scenes.ants.camera);
this.setViewportFromRT(this.resources.antsDiscreteRenderTarget); this.setViewportFromRT(this.resources.antsDiscreteRenderTarget);
this.renderer.setRenderTarget(this.resources.antsDiscreteRenderTarget); this.renderer.setRenderTarget(this.resources.antsDiscreteRenderTarget);
scenes.discretize.material.uniforms.tDataCurrent.value = antsComputeTarget.texture; scenes.discretize.material.uniforms.tDataCurrent.value =
scenes.discretize.material.uniforms.tDataLast.value = antsComputeSource.texture; antsComputeTarget.texture;
this.renderer.render(scenes.discretize, scenes.discretize.camera); scenes.discretize.material.uniforms.tDataLast.value =
antsComputeSource.texture;
this.renderer.render(scenes.discretize, scenes.discretize.camera);
this.setViewportFromRT(this.resources.worldRenderTarget); this.setViewportFromRT(this.resources.worldRenderTarget);
this.renderer.setRenderTarget(this.resources.worldRenderTarget); this.renderer.setRenderTarget(this.resources.worldRenderTarget);
scenes.world.material.uniforms.tLastState.value = this.resources.worldBlurredRenderTarget.texture; scenes.world.material.uniforms.tLastState.value =
scenes.world.material.uniforms.tDiscreteAnts.value = this.resources.antsDiscreteRenderTarget.texture; this.resources.worldBlurredRenderTarget.texture;
this.renderer.render(scenes.world, scenes.world.camera); 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.material.uniforms.tData.value = antsComputeTarget.texture;
scenes.screen.groundMaterial.uniforms.map.value = this.resources.worldRenderTargetCopy.texture; scenes.screen.groundMaterial.uniforms.map.value =
} this.resources.worldRenderTargetCopy.texture;
}
private setViewportFromRT(rt: WebGLRenderTarget) { private setViewportFromRT(rt: WebGLRenderTarget) {
this.renderer.setViewport(0, 0, rt.width, rt.height); this.renderer.setViewport(0, 0, rt.width, rt.height);
} }
public renderToScreen(scenes: SceneCollection) { public renderToScreen(scenes: SceneCollection) {
this.setViewportFromRT(this.resources.worldRenderTargetCopy); this.setViewportFromRT(this.resources.worldRenderTargetCopy);
this.renderer.setRenderTarget(this.resources.worldRenderTargetCopy); this.renderer.setRenderTarget(this.resources.worldRenderTargetCopy);
scenes.draw.material.uniforms.tWorld.value = this.resources.worldRenderTarget.texture; scenes.draw.material.uniforms.tWorld.value =
scenes.draw.material.uniforms.pointerPosition.value = scenes.screen.pointerPosition; this.resources.worldRenderTarget.texture;
scenes.draw.material.uniforms.drawMode.value = scenes.screen.drawMode; scenes.draw.material.uniforms.pointerPosition.value =
scenes.draw.material.uniforms.brushRadius.value = Config.brushRadius; scenes.screen.pointerPosition;
this.renderer.render(scenes.draw, scenes.draw.camera); scenes.draw.material.uniforms.drawMode.value = scenes.screen.drawMode;
this.renderer.copyFramebufferToTexture(this.resources.worldRenderTarget.texture, new THREE.Vector2()); 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.setViewport(
this.renderer.setRenderTarget(null); 0,
this.renderer.render(scenes.screen, scenes.screen.camera); 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) { public resizeCanvas(width: number, height: number) {
this.canvas.width = width; this.canvas.width = width;
this.canvas.height = height; this.canvas.height = height;
} }
public getCommonMaterialDefines(): Record<string, string> { public getCommonMaterialDefines(): Record<string, string> {
return { return {
WORLD_SIZE: Renderer.convertNumberToFloatString(Config.worldSize), WORLD_SIZE: Renderer.convertNumberToFloatString(Config.worldSize),
SCENT_THRESHOLD: Renderer.convertNumberToFloatString(Config.scentThreshold), SCENT_THRESHOLD: Renderer.convertNumberToFloatString(
SCENT_FADE_OUT_FACTOR: Renderer.convertNumberToFloatString(Config.scentFadeOutFactor), Config.scentThreshold,
SCENT_BLUR_RADIUS: Renderer.convertNumberToFloatString(Config.scentBlurRadius), ),
SCENT_MAX_STORAGE: Renderer.convertNumberToFloatString(Config.scentMaxStorage), SCENT_FADE_OUT_FACTOR: Renderer.convertNumberToFloatString(
SCENT_PER_MARKER: Renderer.convertNumberToFloatString(Config.scentPerMarker), Config.scentFadeOutFactor,
SCENT_MAX_PER_CELL: Renderer.convertNumberToFloatString(Config.scentMaxPerCell), ),
ANT_SPEED: Renderer.convertNumberToFloatString(Config.antSpeed), SCENT_BLUR_RADIUS: Renderer.convertNumberToFloatString(
ANT_ROTATION_ANGLE: Renderer.convertNumberToFloatString(Config.antRotationAngle) 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) { public reset(scenes: SceneCollection) {
const antTextureSize = Math.round(Math.sqrt(2 ** Config.antsCount)); const antTextureSize = Math.round(Math.sqrt(2 ** Config.antsCount));
this.resources.worldRenderTarget.setSize(Config.worldSize, Config.worldSize) this.resources.worldRenderTarget.setSize(
this.renderer.setRenderTarget(this.resources.worldRenderTarget); Config.worldSize,
this.renderer.clear(); Config.worldSize,
);
this.renderer.setRenderTarget(this.resources.worldRenderTarget);
this.renderer.clear();
this.resources.worldRenderTargetCopy.setSize(Config.worldSize, Config.worldSize) this.resources.worldRenderTargetCopy.setSize(
this.renderer.setRenderTarget(this.resources.worldRenderTargetCopy); Config.worldSize,
this.renderer.clear(); Config.worldSize,
);
this.renderer.setRenderTarget(this.resources.worldRenderTargetCopy);
this.renderer.clear();
this.resources.worldBlurredRenderTarget.setSize(Config.worldSize, Config.worldSize) this.resources.worldBlurredRenderTarget.setSize(
this.renderer.setRenderTarget(this.resources.worldBlurredRenderTarget); Config.worldSize,
this.renderer.clear(); Config.worldSize,
);
this.renderer.setRenderTarget(this.resources.worldBlurredRenderTarget);
this.renderer.clear();
this.resources.antsDataRenderTarget0.setSize(antTextureSize, antTextureSize) this.resources.antsDataRenderTarget0.setSize(
this.renderer.setRenderTarget(this.resources.antsDataRenderTarget0); antTextureSize,
this.renderer.clear(); antTextureSize,
);
this.renderer.setRenderTarget(this.resources.antsDataRenderTarget0);
this.renderer.clear();
this.resources.antsDataRenderTarget1.setSize(antTextureSize, antTextureSize) this.resources.antsDataRenderTarget1.setSize(
this.renderer.setRenderTarget(this.resources.antsDataRenderTarget1); antTextureSize,
this.renderer.clear(); antTextureSize,
);
this.renderer.setRenderTarget(this.resources.antsDataRenderTarget1);
this.renderer.clear();
this.resources.antsDiscreteRenderTarget.setSize(Config.worldSize, Config.worldSize) this.resources.antsDiscreteRenderTarget.setSize(
this.renderer.setRenderTarget(this.resources.antsDiscreteRenderTarget); Config.worldSize,
this.renderer.clear(); Config.worldSize,
);
this.renderer.setRenderTarget(this.resources.antsDiscreteRenderTarget);
this.renderer.clear();
for (const scene of Object.values(scenes)) { for (const scene of Object.values(scenes)) {
scene.recompileMaterials(); scene.recompileMaterials();
} }
} }
static convertNumberToFloatString(n: number): string { static convertNumberToFloatString(n: number): string {
return n.toFixed(8); return n.toFixed(8);
} }
} }

View file

@ -0,0 +1,7 @@
import { describe, expect, test } from "bun:test";
describe("placeholder", () => {
test("passes", () => {
expect(true).toBe(true);
});
});

12
src/global.d.ts vendored
View file

@ -1,8 +1,8 @@
declare module '*.vert' { declare module "*.vert" {
const content: string; const content: string;
export default content; export default content;
} }
declare module '*.frag' { declare module "*.frag" {
const content: string; const content: string;
export default content; export default content;
} }

View file

@ -1,58 +1,56 @@
import * as THREE from 'three'; import type { WebGLRenderTarget } from "three";
import {WebGLRenderTarget} from 'three'; import * as THREE from "three";
import Renderer from "../Renderer"; import type Renderer from "../Renderer";
import AbstractScene from "./AbstractScene"; import fragmentShader from "../shaders/antsCompute.frag";
import vertexShader from "../shaders/antsCompute.vert";
import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry"; import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry";
import fragmentShader from '../shaders/antsCompute.frag'; import AbstractScene from "./AbstractScene";
import vertexShader from '../shaders/antsCompute.vert';
export default class AntsComputeScene extends AbstractScene { export default class AntsComputeScene extends AbstractScene {
public camera: THREE.OrthographicCamera = new THREE.OrthographicCamera(); public camera: THREE.OrthographicCamera = new THREE.OrthographicCamera();
public material: THREE.RawShaderMaterial; public material: THREE.RawShaderMaterial;
private renderTargets: [WebGLRenderTarget, WebGLRenderTarget]; private renderTargets: [WebGLRenderTarget, WebGLRenderTarget];
constructor(renderer: Renderer) { constructor(renderer: Renderer) {
super(renderer); super(renderer);
const geometry = new FullScreenTriangleGeometry(); const geometry = new FullScreenTriangleGeometry();
const material = new THREE.RawShaderMaterial({ const material = new THREE.RawShaderMaterial({
uniforms: { uniforms: {
uTime: {value: 0}, uTime: { value: 0 },
tLastState: {value: null}, tLastState: { value: null },
tWorld: {value: null}, tWorld: { value: null },
}, },
vertexShader, vertexShader,
fragmentShader, fragmentShader,
defines: this.renderer.getCommonMaterialDefines(), defines: this.renderer.getCommonMaterialDefines(),
glslVersion: THREE.GLSL3 glslVersion: THREE.GLSL3,
}); });
const mesh = new THREE.Mesh(geometry, material); const mesh = new THREE.Mesh(geometry, material);
this.add(mesh); this.add(mesh);
this.material = material; this.material = material;
this.renderTargets = [ this.renderTargets = [
this.renderer.resources.antsDataRenderTarget0, this.renderer.resources.antsDataRenderTarget0,
this.renderer.resources.antsDataRenderTarget1 this.renderer.resources.antsDataRenderTarget1,
]; ];
} }
public getRenderTargets(): [WebGLRenderTarget, WebGLRenderTarget] { public getRenderTargets(): [WebGLRenderTarget, WebGLRenderTarget] {
this.renderTargets.reverse(); this.renderTargets.reverse();
return this.renderTargets; return this.renderTargets;
} }
public recompileMaterials() { public recompileMaterials() {
this.material.defines = this.renderer.getCommonMaterialDefines(); this.material.defines = this.renderer.getCommonMaterialDefines();
this.material.needsUpdate = true; 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();
public update() { }
this.material.uniforms.uTime.value = performance.now();
}
} }

View file

@ -1,46 +1,43 @@
import * as THREE from 'three'; import * as THREE from "three";
import Renderer from "../Renderer"; import type Renderer from "../Renderer";
import AbstractScene from "./AbstractScene"; import fragmentShader from "../shaders/draw.frag";
import vertexShader from "../shaders/draw.vert";
import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry"; import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry";
import fragmentShader from '../shaders/draw.frag'; import AbstractScene from "./AbstractScene";
import vertexShader from '../shaders/draw.vert';
export default class DrawScene extends AbstractScene { export default class DrawScene extends AbstractScene {
public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera(); public readonly camera: THREE.OrthographicCamera =
public readonly material: THREE.RawShaderMaterial; new THREE.OrthographicCamera();
public readonly material: THREE.RawShaderMaterial;
constructor(renderer: Renderer) { constructor(renderer: Renderer) {
super(renderer); super(renderer);
const geometry = new FullScreenTriangleGeometry(); const geometry = new FullScreenTriangleGeometry();
const material = new THREE.RawShaderMaterial({ const material = new THREE.RawShaderMaterial({
uniforms: { uniforms: {
tWorld: {value: null}, tWorld: { value: null },
pointerPosition: {value: new THREE.Vector2()}, pointerPosition: { value: new THREE.Vector2() },
drawMode: {value: 0}, drawMode: { value: 0 },
brushRadius: {value: 0}, brushRadius: { value: 0 },
}, },
vertexShader, vertexShader,
fragmentShader, fragmentShader,
defines: this.renderer.getCommonMaterialDefines(), defines: this.renderer.getCommonMaterialDefines(),
glslVersion: THREE.GLSL3 glslVersion: THREE.GLSL3,
}); });
const mesh = new THREE.Mesh(geometry, material); const mesh = new THREE.Mesh(geometry, material);
this.add(mesh); this.add(mesh);
this.material = material; this.material = material;
} }
public recompileMaterials() { public recompileMaterials() {
this.material.defines = this.renderer.getCommonMaterialDefines(); this.material.defines = this.renderer.getCommonMaterialDefines();
this.material.needsUpdate = true; this.material.needsUpdate = true;
} }
public resize(width: number, height: number) { public resize(width: number, height: number) {}
} public update() {}
public update() {
}
} }

View file

@ -1,43 +1,40 @@
import * as THREE from 'three'; import * as THREE from "three";
import Renderer from "../Renderer"; import type Renderer from "../Renderer";
import AbstractScene from "./AbstractScene"; import fragmentShader from "../shaders/worldBlur.frag";
import vertexShader from "../shaders/worldBlur.vert";
import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry"; import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry";
import fragmentShader from '../shaders/worldBlur.frag'; import AbstractScene from "./AbstractScene";
import vertexShader from '../shaders/worldBlur.vert';
export default class WorldBlurScene extends AbstractScene { export default class WorldBlurScene extends AbstractScene {
public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera(); public readonly camera: THREE.OrthographicCamera =
public readonly material: THREE.RawShaderMaterial; new THREE.OrthographicCamera();
public readonly material: THREE.RawShaderMaterial;
constructor(renderer: Renderer) { constructor(renderer: Renderer) {
super(renderer); super(renderer);
const geometry = new FullScreenTriangleGeometry(); const geometry = new FullScreenTriangleGeometry();
const material = new THREE.RawShaderMaterial({ const material = new THREE.RawShaderMaterial({
uniforms: { uniforms: {
tWorld: {value: null}, tWorld: { value: null },
}, },
vertexShader, vertexShader,
fragmentShader, fragmentShader,
defines: this.renderer.getCommonMaterialDefines(), defines: this.renderer.getCommonMaterialDefines(),
glslVersion: THREE.GLSL3 glslVersion: THREE.GLSL3,
}); });
const mesh = new THREE.Mesh(geometry, material); const mesh = new THREE.Mesh(geometry, material);
this.add(mesh); this.add(mesh);
this.material = material; this.material = material;
} }
public recompileMaterials() { public recompileMaterials() {
this.material.defines = this.renderer.getCommonMaterialDefines(); this.material.defines = this.renderer.getCommonMaterialDefines();
this.material.needsUpdate = true; this.material.needsUpdate = true;
} }
public resize(width: number, height: number) { public resize(width: number, height: number) {}
} public update() {}
public update() {
}
} }

View file

@ -1,44 +1,41 @@
import * as THREE from 'three'; import * as THREE from "three";
import Renderer from "../Renderer"; import type Renderer from "../Renderer";
import AbstractScene from "./AbstractScene"; import fragmentShader from "../shaders/world.frag";
import vertexShader from "../shaders/world.vert";
import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry"; import FullScreenTriangleGeometry from "../utils/FullScreenTriangleGeometry";
import fragmentShader from '../shaders/world.frag'; import AbstractScene from "./AbstractScene";
import vertexShader from '../shaders/world.vert';
export default class WorldComputeScene extends AbstractScene { export default class WorldComputeScene extends AbstractScene {
public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera(); public readonly camera: THREE.OrthographicCamera =
public readonly material: THREE.RawShaderMaterial; new THREE.OrthographicCamera();
public readonly material: THREE.RawShaderMaterial;
constructor(renderer: Renderer) { constructor(renderer: Renderer) {
super(renderer); super(renderer);
const geometry = new FullScreenTriangleGeometry(); const geometry = new FullScreenTriangleGeometry();
const material = new THREE.RawShaderMaterial({ const material = new THREE.RawShaderMaterial({
uniforms: { uniforms: {
tLastState: {value: null}, tLastState: { value: null },
tDiscreteAnts: {value: null} tDiscreteAnts: { value: null },
}, },
vertexShader, vertexShader,
fragmentShader, fragmentShader,
defines: this.renderer.getCommonMaterialDefines(), defines: this.renderer.getCommonMaterialDefines(),
glslVersion: THREE.GLSL3 glslVersion: THREE.GLSL3,
}); });
const mesh = new THREE.Mesh(geometry, material); const mesh = new THREE.Mesh(geometry, material);
this.add(mesh); this.add(mesh);
this.material = material; this.material = material;
} }
public recompileMaterials() { public recompileMaterials() {
this.material.defines = this.renderer.getCommonMaterialDefines(); this.material.defines = this.renderer.getCommonMaterialDefines();
this.material.needsUpdate = true; this.material.needsUpdate = true;
} }
public resize(width: number, height: number) { public resize(width: number, height: number) {}
} public update() {}
public update() {
}
} }

View file

@ -1,28 +1,17 @@
import * as THREE from "three"; import * as THREE from "three";
const positionBuffer = new Float32Array([ const positionBuffer = new Float32Array([-1, 3, 0, -1, -1, 0, 3, -1, 0]);
-1, 3, 0,
-1, -1, 0,
3, -1, 0,
]);
const uvBuffer = new Float32Array([ const uvBuffer = new Float32Array([0, 2, 0, 0, 2, 0]);
0, 2,
0, 0,
2, 0
]);
export default class FullScreenTriangleGeometry extends THREE.BufferGeometry { export default class FullScreenTriangleGeometry extends THREE.BufferGeometry {
constructor() { constructor() {
super(); super();
this.setAttribute( this.setAttribute(
'position', "position",
new THREE.BufferAttribute(positionBuffer, 3) new THREE.BufferAttribute(positionBuffer, 3),
); );
this.setAttribute( this.setAttribute("uv", new THREE.BufferAttribute(uvBuffer, 2));
'uv', }
new THREE.BufferAttribute(uvBuffer, 2)
);
}
} }