Fix pre-existing typecheck errors

This commit is contained in:
Jared Miller 2026-03-09 10:39:35 -04:00
parent 03679622a3
commit 221606b527
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C
4 changed files with 298 additions and 289 deletions

View file

@ -1,98 +1,101 @@
import AntsComputeScene from "./scenes/AntsComputeScene";
import ScreenScene from "./scenes/ScreenScene";
import Renderer from "./Renderer";
import WorldComputeScene from "./scenes/WorldComputeScene";
import AntsDiscretizeScene from "./scenes/AntsDiscretizeScene";
import WorldBlurScene from "./scenes/WorldBlurScene";
import Config from "./Config"; import Config from "./Config";
import GUI from "./GUI"; import GUI from "./GUI";
import Renderer from "./Renderer";
import AntsComputeScene from "./scenes/AntsComputeScene";
import AntsDiscretizeScene from "./scenes/AntsDiscretizeScene";
import DrawScene from "./scenes/DrawScene"; import DrawScene from "./scenes/DrawScene";
import ScreenScene from "./scenes/ScreenScene";
import WorldBlurScene from "./scenes/WorldBlurScene";
import WorldComputeScene from "./scenes/WorldComputeScene";
export interface SceneCollection { export interface SceneCollection {
ants: AntsComputeScene; ants: AntsComputeScene;
world: WorldComputeScene; world: WorldComputeScene;
worldBlur: WorldBlurScene; worldBlur: WorldBlurScene;
discretize: AntsDiscretizeScene; discretize: AntsDiscretizeScene;
screen: ScreenScene; screen: ScreenScene;
draw: DrawScene; draw: DrawScene;
} }
export default new class App { export default new (class App {
private renderer: Renderer = new Renderer(<HTMLCanvasElement>document.getElementById('canvas')); private renderer: Renderer = new Renderer(
private scenes: SceneCollection; <HTMLCanvasElement>document.getElementById("canvas"),
private gui: GUI = new GUI(); );
private renderLoop = (time: number): void => this.render(time); private scenes!: SceneCollection;
private lastTime: number = 0; private gui: GUI = new GUI();
private queuedSimSteps: number = 0; private renderLoop = (time: number): void => this.render(time);
private lastTime: number = 0;
private queuedSimSteps: number = 0;
constructor() { constructor() {
this.initScenes(); this.initScenes();
window.addEventListener('resize', () => this.resize()); window.addEventListener("resize", () => this.resize());
this.resize(); this.resize();
this.renderLoop(0); this.renderLoop(0);
this.gui.on('reset', () => { this.gui.on("reset", () => {
this.resetRenderer(); this.resetRenderer();
}); });
} }
private resetRenderer() { private resetRenderer() {
this.renderer.reset(this.scenes); this.renderer.reset(this.scenes);
} }
private initScenes() { private initScenes() {
this.scenes = { this.scenes = {
ants: new AntsComputeScene(this.renderer), ants: new AntsComputeScene(this.renderer),
world: new WorldComputeScene(this.renderer), world: new WorldComputeScene(this.renderer),
worldBlur: new WorldBlurScene(this.renderer), worldBlur: new WorldBlurScene(this.renderer),
discretize: new AntsDiscretizeScene(this.renderer), discretize: new AntsDiscretizeScene(this.renderer),
screen: new ScreenScene(this.renderer), screen: new ScreenScene(this.renderer),
draw: new DrawScene(this.renderer) draw: new DrawScene(this.renderer),
}; };
} }
private resize() { private resize() {
const width = window.innerWidth * window.devicePixelRatio; const width = window.innerWidth * window.devicePixelRatio;
const height = window.innerHeight * window.devicePixelRatio; const height = window.innerHeight * window.devicePixelRatio;
this.renderer.resizeCanvas(width, height); this.renderer.resizeCanvas(width, height);
for (const scene of Object.values(this.scenes)) { for (const scene of Object.values(this.scenes)) {
scene.resize(width, height); scene.resize(width, height);
} }
} }
private simulationStep() { private simulationStep() {
for (const scene of Object.values(this.scenes)) { for (const scene of Object.values(this.scenes)) {
scene.update(); scene.update();
} }
this.renderer.renderSimulation(this.scenes); this.renderer.renderSimulation(this.scenes);
} }
private render(time: number) { private render(time: number) {
requestAnimationFrame(this.renderLoop); requestAnimationFrame(this.renderLoop);
const deltaTime = time - this.lastTime; const deltaTime = time - this.lastTime;
const simStepsToDo = deltaTime / 1000 * Config.simulationStepsPerSecond; const simStepsToDo =
(deltaTime / 1000) * Config.simulationStepsPerSecond;
this.queuedSimSteps += simStepsToDo; this.queuedSimSteps += simStepsToDo;
this.queuedSimSteps = Math.min(this.queuedSimSteps, 10); this.queuedSimSteps = Math.min(this.queuedSimSteps, 10);
while (this.queuedSimSteps >= 1) { while (this.queuedSimSteps >= 1) {
this.simulationStep(); this.simulationStep();
--this.queuedSimSteps; --this.queuedSimSteps;
} }
if (time === 0) { if (time === 0) {
return; return;
} }
this.renderer.renderToScreen(this.scenes); this.renderer.renderToScreen(this.scenes);
this.lastTime = time; this.lastTime = time;
} }
} })();

View file

@ -1,19 +1,19 @@
import * as THREE from 'three'; import * as THREE from "three";
import Renderer from "../Renderer"; import type Renderer from "../Renderer";
export default abstract class AbstractScene extends THREE.Scene { export default abstract class AbstractScene extends THREE.Scene {
protected readonly renderer: Renderer; protected readonly renderer: Renderer;
public readonly camera: THREE.Camera; public readonly camera!: THREE.Camera;
protected constructor(renderer: Renderer) { protected constructor(renderer: Renderer) {
super(); super();
this.renderer = renderer; this.renderer = renderer;
} }
public abstract recompileMaterials(): void; public abstract recompileMaterials(): void;
public abstract resize(width: number, height: number): void; public abstract resize(width: number, height: number): void;
public abstract update(): void; public abstract update(): void;
} }

View file

@ -1,56 +1,54 @@
import * as THREE from 'three'; import * as THREE from "three";
import Renderer from "../Renderer"; import type Renderer from "../Renderer";
import fragmentShader from "../shaders/antsDiscretize.frag";
import vertexShader from "../shaders/antsDiscretize.vert";
import AbstractScene from "./AbstractScene"; import AbstractScene from "./AbstractScene";
import fragmentShader from '../shaders/antsDiscretize.frag';
import vertexShader from '../shaders/antsDiscretize.vert';
export default class AntsDiscretizeScene extends AbstractScene { export default class AntsDiscretizeScene extends AbstractScene {
public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera(); public readonly camera: THREE.OrthographicCamera =
public readonly material: THREE.RawShaderMaterial; new THREE.OrthographicCamera();
public mesh: THREE.InstancedMesh; public readonly material: THREE.RawShaderMaterial;
public mesh!: THREE.InstancedMesh;
constructor(renderer: Renderer) { constructor(renderer: Renderer) {
super(renderer); super(renderer);
this.material = new THREE.RawShaderMaterial({ this.material = new THREE.RawShaderMaterial({
uniforms: { uniforms: {
tDataCurrent: {value: null}, tDataCurrent: { value: null },
tDataLast: {value: null}, tDataLast: { value: null },
}, },
vertexShader, vertexShader,
fragmentShader, fragmentShader,
defines: this.renderer.getCommonMaterialDefines(), defines: this.renderer.getCommonMaterialDefines(),
glslVersion: THREE.GLSL3 glslVersion: THREE.GLSL3,
}); });
this.createMesh(); this.createMesh();
} }
private createMesh() { private createMesh() {
if (this.mesh) { if (this.mesh) {
this.remove(this.mesh); this.remove(this.mesh);
this.mesh.dispose(); this.mesh.dispose();
} }
this.mesh = new THREE.InstancedMesh( this.mesh = new THREE.InstancedMesh(
new THREE.BoxGeometry(1, 1, 1), new THREE.BoxGeometry(1, 1, 1),
this.material, this.material,
this.renderer.resources.antsDataRenderTarget0.width * this.renderer.resources.antsDataRenderTarget0.height this.renderer.resources.antsDataRenderTarget0.width *
); this.renderer.resources.antsDataRenderTarget0.height,
this.add(this.mesh); );
} this.add(this.mesh);
}
public recompileMaterials() { public recompileMaterials() {
this.material.defines = this.renderer.getCommonMaterialDefines(); this.material.defines = this.renderer.getCommonMaterialDefines();
this.material.needsUpdate = true; this.material.needsUpdate = true;
this.createMesh(); this.createMesh();
} }
public resize(width: number, height: number) { public resize(width: number, height: number) {}
} public update() {}
public update() {
}
} }

View file

@ -1,209 +1,217 @@
import * as THREE from 'three'; import * as THREE from "three";
import Renderer from "../Renderer";
import AbstractScene from "./AbstractScene";
import vertexShaderAnts from "../shaders/ants.vert";
import fragmentShaderAnts from "../shaders/ants.frag";
import vertexShaderGround from "../shaders/screenWorld.vert";
import fragmentShaderGround from "../shaders/screenWorld.frag";
import Config from "../Config"; import Config from "../Config";
import type Renderer from "../Renderer";
import fragmentShaderAnts from "../shaders/ants.frag";
import vertexShaderAnts from "../shaders/ants.vert";
import fragmentShaderGround from "../shaders/screenWorld.frag";
import vertexShaderGround from "../shaders/screenWorld.vert";
import AbstractScene from "./AbstractScene";
enum PointerState { enum PointerState {
None, None,
Food, Food,
Home, Home,
Obstacle, Obstacle,
Erase Erase,
} }
export default class ScreenScene extends AbstractScene { export default class ScreenScene extends AbstractScene {
public readonly camera: THREE.OrthographicCamera; public readonly camera: THREE.OrthographicCamera;
public readonly material: THREE.ShaderMaterial; public readonly material: THREE.ShaderMaterial;
public ants: THREE.InstancedMesh; public ants!: THREE.InstancedMesh;
public readonly groundMaterial: THREE.ShaderMaterial; public readonly groundMaterial: THREE.ShaderMaterial;
public readonly pointerPosition: THREE.Vector2 = new THREE.Vector2(); public readonly pointerPosition: THREE.Vector2 = new THREE.Vector2();
public drawMode: PointerState = PointerState.None; public drawMode: PointerState = PointerState.None;
private cameraZoomLinear: number = 0; private cameraZoomLinear: number = 0;
private isPointerDown: boolean = false; private isPointerDown: boolean = false;
public renderWidth: number = 1; public renderWidth: number = 1;
public renderHeight: number = 1; public renderHeight: number = 1;
constructor(renderer: Renderer) { constructor(renderer: Renderer) {
super(renderer); super(renderer);
const ground = new THREE.Mesh( const ground = new THREE.Mesh(
new THREE.PlaneGeometry(1, 1), new THREE.PlaneGeometry(1, 1),
new THREE.ShaderMaterial({ new THREE.ShaderMaterial({
uniforms: { uniforms: {
map: {value: this.renderer.resources.worldRenderTarget.texture} map: {
}, value: this.renderer.resources.worldRenderTarget
vertexShader: vertexShaderGround, .texture,
fragmentShader: fragmentShaderGround, },
defines: this.renderer.getCommonMaterialDefines(), },
glslVersion: THREE.GLSL3 vertexShader: vertexShaderGround,
}) fragmentShader: fragmentShaderGround,
); defines: this.renderer.getCommonMaterialDefines(),
glslVersion: THREE.GLSL3,
}),
);
this.groundMaterial = ground.material; this.groundMaterial = ground.material;
this.add(ground); this.add(ground);
const antTexture = new THREE.TextureLoader().load('textures/ant.png'); const antTexture = new THREE.TextureLoader().load("textures/ant.png");
const foodTexture = new THREE.TextureLoader().load('textures/food.png'); const foodTexture = new THREE.TextureLoader().load("textures/food.png");
antTexture.magFilter = foodTexture.magFilter = THREE.NearestFilter; antTexture.magFilter = foodTexture.magFilter = THREE.NearestFilter;
antTexture.minFilter = foodTexture.minFilter = THREE.LinearMipMapLinearFilter; antTexture.minFilter = foodTexture.minFilter =
THREE.LinearMipMapLinearFilter;
this.material = new THREE.ShaderMaterial({ this.material = new THREE.ShaderMaterial({
uniforms: { uniforms: {
tData: {value: this.renderer.resources.antsDataRenderTarget0.texture}, tData: {
tAnt: {value: antTexture}, value: this.renderer.resources.antsDataRenderTarget0
tFood: {value: foodTexture} .texture,
}, },
vertexShader: vertexShaderAnts, tAnt: { value: antTexture },
fragmentShader: fragmentShaderAnts, tFood: { value: foodTexture },
transparent: true },
}); vertexShader: vertexShaderAnts,
fragmentShader: fragmentShaderAnts,
transparent: true,
});
this.createInstancedAntsMesh(); this.createInstancedAntsMesh();
this.camera = new THREE.OrthographicCamera(-0.5, 0.5, 0.5, -0.5); this.camera = new THREE.OrthographicCamera(-0.5, 0.5, 0.5, -0.5);
this.add(this.camera); this.add(this.camera);
this.camera.position.z = 12; this.camera.position.z = 12;
const raycastVector = new THREE.Vector2(0, 0); const raycastVector = new THREE.Vector2(0, 0);
const raycaster = new THREE.Raycaster(); const raycaster = new THREE.Raycaster();
this.renderer.canvas.addEventListener('contextmenu', e => { this.renderer.canvas.addEventListener("contextmenu", (e) => {
e.preventDefault(); e.preventDefault();
}); });
this.renderer.canvas.addEventListener('pointerdown', e => { this.renderer.canvas.addEventListener("pointerdown", (e) => {
this.isPointerDown = true; this.isPointerDown = true;
raycastVector.x = (e.clientX / window.innerWidth) * 2 - 1; raycastVector.x = (e.clientX / window.innerWidth) * 2 - 1;
raycastVector.y = -(e.clientY / window.innerHeight) * 2 + 1; raycastVector.y = -(e.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(raycastVector, this.camera); raycaster.setFromCamera(raycastVector, this.camera);
const intersects = raycaster.intersectObjects([ground]); const intersects = raycaster.intersectObjects([ground]);
if (intersects.length > 0) { if (intersects.length > 0) {
const uv = intersects[0].uv; const uv = intersects[0].uv;
this.pointerPosition.copy(uv); if (uv) this.pointerPosition.copy(uv);
} }
}); });
this.renderer.canvas.addEventListener('pointermove', e => { this.renderer.canvas.addEventListener("pointermove", (e) => {
if (this.isPointerDown) { if (this.isPointerDown) {
const dx = e.movementX; const dx = e.movementX;
const dy = e.movementY; const dy = e.movementY;
this.camera.position.x -= dx / window.innerHeight / this.camera.zoom; this.camera.position.x -=
this.camera.position.y += dy / window.innerHeight / this.camera.zoom; dx / window.innerHeight / this.camera.zoom;
} this.camera.position.y +=
dy / window.innerHeight / this.camera.zoom;
}
raycastVector.x = (e.clientX / window.innerWidth) * 2 - 1; raycastVector.x = (e.clientX / window.innerWidth) * 2 - 1;
raycastVector.y = -(e.clientY / window.innerHeight) * 2 + 1; raycastVector.y = -(e.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(raycastVector, this.camera); raycaster.setFromCamera(raycastVector, this.camera);
const intersects = raycaster.intersectObjects([ground]); const intersects = raycaster.intersectObjects([ground]);
if (intersects.length > 0) { if (intersects.length > 0) {
const uv = intersects[0].uv; const uv = intersects[0].uv;
this.pointerPosition.copy(uv); if (uv) this.pointerPosition.copy(uv);
} }
}); });
this.renderer.canvas.addEventListener('pointerup', e => { this.renderer.canvas.addEventListener("pointerup", (e) => {
this.isPointerDown = false; this.isPointerDown = false;
}); });
this.renderer.canvas.addEventListener('pointerleave', e => { this.renderer.canvas.addEventListener("pointerleave", (e) => {
this.isPointerDown = false; this.isPointerDown = false;
}); });
this.renderer.canvas.addEventListener('wheel', e => { this.renderer.canvas.addEventListener("wheel", (e) => {
this.cameraZoomLinear -= e.deltaY * 0.001; this.cameraZoomLinear -= e.deltaY * 0.001;
this.updateCameraZoom(); this.updateCameraZoom();
}); });
window.addEventListener('keydown', e => { window.addEventListener("keydown", (e) => {
switch (e.code) { switch (e.code) {
case 'KeyQ': { case "KeyQ": {
this.drawMode = PointerState.Home; this.drawMode = PointerState.Home;
break; break;
} }
case 'KeyW': { case "KeyW": {
this.drawMode = PointerState.Food; this.drawMode = PointerState.Food;
break; break;
} }
case 'KeyE': { case "KeyE": {
this.drawMode = PointerState.Obstacle; this.drawMode = PointerState.Obstacle;
break; break;
} }
case 'KeyR': { case "KeyR": {
this.drawMode = PointerState.Erase; this.drawMode = PointerState.Erase;
break; break;
} }
} }
}); });
window.addEventListener('keyup', e => { window.addEventListener("keyup", (e) => {
this.drawMode = PointerState.None; this.drawMode = PointerState.None;
}); });
} }
private updateCameraZoom() { private updateCameraZoom() {
this.camera.zoom = 2 ** this.cameraZoomLinear; this.camera.zoom = 2 ** this.cameraZoomLinear;
this.camera.updateProjectionMatrix(); this.camera.updateProjectionMatrix();
} }
private createInstancedAntsMesh() { private createInstancedAntsMesh() {
if (this.ants) { if (this.ants) {
this.remove(this.ants); this.remove(this.ants);
this.ants.dispose(); this.ants.dispose();
} }
const scale = 8 / Config.worldSize; const scale = 8 / Config.worldSize;
const ants = new THREE.InstancedMesh( const ants = new THREE.InstancedMesh(
new THREE.PlaneGeometry(scale, scale), new THREE.PlaneGeometry(scale, scale),
this.material, this.material,
this.renderer.resources.antsDataRenderTarget0.width * this.renderer.resources.antsDataRenderTarget0.height this.renderer.resources.antsDataRenderTarget0.width *
) this.renderer.resources.antsDataRenderTarget0.height,
);
ants.position.x = ants.position.y = -0.5; ants.position.x = ants.position.y = -0.5;
this.add(ants); this.add(ants);
this.ants = ants; this.ants = ants;
} }
public recompileMaterials() { public recompileMaterials() {
this.groundMaterial.defines = this.renderer.getCommonMaterialDefines(); this.groundMaterial.defines = this.renderer.getCommonMaterialDefines();
this.groundMaterial.needsUpdate = true; this.groundMaterial.needsUpdate = true;
this.createInstancedAntsMesh(); this.createInstancedAntsMesh();
} }
public resize(width: number, height: number) { public resize(width: number, height: number) {
const aspect = width / height; const aspect = width / height;
this.camera.left = -0.5 * aspect; this.camera.left = -0.5 * aspect;
this.camera.right = 0.5 * aspect; this.camera.right = 0.5 * aspect;
this.camera.top = 0.5; this.camera.top = 0.5;
this.camera.bottom = -0.5; this.camera.bottom = -0.5;
this.camera.updateProjectionMatrix(); this.camera.updateProjectionMatrix();
this.renderWidth = width; this.renderWidth = width;
this.renderHeight = height; this.renderHeight = height;
} }
public update() { public update() {}
}
} }