diff --git a/src/App.ts b/src/App.ts index 993e1b1..e637b4f 100644 --- a/src/App.ts +++ b/src/App.ts @@ -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 GUI from "./GUI"; +import Renderer from "./Renderer"; +import AntsComputeScene from "./scenes/AntsComputeScene"; +import AntsDiscretizeScene from "./scenes/AntsDiscretizeScene"; import DrawScene from "./scenes/DrawScene"; +import ScreenScene from "./scenes/ScreenScene"; +import WorldBlurScene from "./scenes/WorldBlurScene"; +import WorldComputeScene from "./scenes/WorldComputeScene"; export interface SceneCollection { - ants: AntsComputeScene; - world: WorldComputeScene; - worldBlur: WorldBlurScene; - discretize: AntsDiscretizeScene; - screen: ScreenScene; - draw: DrawScene; + ants: AntsComputeScene; + world: WorldComputeScene; + worldBlur: WorldBlurScene; + discretize: AntsDiscretizeScene; + screen: ScreenScene; + draw: DrawScene; } -export default new class App { - private renderer: Renderer = new Renderer(document.getElementById('canvas')); - private scenes: SceneCollection; - private gui: GUI = new GUI(); - private renderLoop = (time: number): void => this.render(time); - private lastTime: number = 0; - private queuedSimSteps: number = 0; +export default new (class App { + private renderer: Renderer = new Renderer( + document.getElementById("canvas"), + ); + private scenes!: SceneCollection; + private gui: GUI = new GUI(); + private renderLoop = (time: number): void => this.render(time); + private lastTime: number = 0; + private queuedSimSteps: number = 0; - constructor() { - this.initScenes(); + constructor() { + 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.resetRenderer(); - }); - } + this.gui.on("reset", () => { + this.resetRenderer(); + }); + } - private resetRenderer() { - this.renderer.reset(this.scenes); - } + private resetRenderer() { + this.renderer.reset(this.scenes); + } - private initScenes() { - this.scenes = { - ants: new AntsComputeScene(this.renderer), - world: new WorldComputeScene(this.renderer), - worldBlur: new WorldBlurScene(this.renderer), - discretize: new AntsDiscretizeScene(this.renderer), - screen: new ScreenScene(this.renderer), - draw: new DrawScene(this.renderer) - }; - } + private initScenes() { + this.scenes = { + ants: new AntsComputeScene(this.renderer), + world: new WorldComputeScene(this.renderer), + worldBlur: new WorldBlurScene(this.renderer), + discretize: new AntsDiscretizeScene(this.renderer), + screen: new ScreenScene(this.renderer), + draw: new DrawScene(this.renderer), + }; + } - private resize() { - const width = window.innerWidth * window.devicePixelRatio; - const height = window.innerHeight * window.devicePixelRatio; + private resize() { + const width = window.innerWidth * 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)) { - scene.resize(width, height); - } - } + for (const scene of Object.values(this.scenes)) { + scene.resize(width, height); + } + } - private simulationStep() { - for (const scene of Object.values(this.scenes)) { - scene.update(); - } + private simulationStep() { + for (const scene of Object.values(this.scenes)) { + scene.update(); + } - this.renderer.renderSimulation(this.scenes); - } + this.renderer.renderSimulation(this.scenes); + } - private render(time: number) { - requestAnimationFrame(this.renderLoop); + private render(time: number) { + requestAnimationFrame(this.renderLoop); - const deltaTime = time - this.lastTime; - const simStepsToDo = deltaTime / 1000 * Config.simulationStepsPerSecond; + const deltaTime = time - this.lastTime; + const simStepsToDo = + (deltaTime / 1000) * Config.simulationStepsPerSecond; - this.queuedSimSteps += simStepsToDo; - this.queuedSimSteps = Math.min(this.queuedSimSteps, 10); + this.queuedSimSteps += simStepsToDo; + this.queuedSimSteps = Math.min(this.queuedSimSteps, 10); - while (this.queuedSimSteps >= 1) { - this.simulationStep(); - --this.queuedSimSteps; - } + while (this.queuedSimSteps >= 1) { + this.simulationStep(); + --this.queuedSimSteps; + } - if (time === 0) { - return; - } + if (time === 0) { + return; + } - this.renderer.renderToScreen(this.scenes); + this.renderer.renderToScreen(this.scenes); - this.lastTime = time; - } -} \ No newline at end of file + this.lastTime = time; + } +})(); diff --git a/src/scenes/AbstractScene.ts b/src/scenes/AbstractScene.ts index 9f67445..7677361 100644 --- a/src/scenes/AbstractScene.ts +++ b/src/scenes/AbstractScene.ts @@ -1,19 +1,19 @@ -import * as THREE from 'three'; -import Renderer from "../Renderer"; +import * as THREE from "three"; +import type Renderer from "../Renderer"; export default abstract class AbstractScene extends THREE.Scene { - protected readonly renderer: Renderer; - public readonly camera: THREE.Camera; + protected readonly renderer: Renderer; + public readonly camera!: THREE.Camera; - protected constructor(renderer: Renderer) { - super(); + protected constructor(renderer: Renderer) { + 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; -} \ No newline at end of file + public abstract update(): void; +} diff --git a/src/scenes/AntsDiscretizeScene.ts b/src/scenes/AntsDiscretizeScene.ts index 451aff5..f0ac7db 100644 --- a/src/scenes/AntsDiscretizeScene.ts +++ b/src/scenes/AntsDiscretizeScene.ts @@ -1,56 +1,54 @@ -import * as THREE from 'three'; -import Renderer from "../Renderer"; +import * as THREE from "three"; +import type Renderer from "../Renderer"; +import fragmentShader from "../shaders/antsDiscretize.frag"; +import vertexShader from "../shaders/antsDiscretize.vert"; import AbstractScene from "./AbstractScene"; -import fragmentShader from '../shaders/antsDiscretize.frag'; -import vertexShader from '../shaders/antsDiscretize.vert'; export default class AntsDiscretizeScene extends AbstractScene { - public readonly camera: THREE.OrthographicCamera = new THREE.OrthographicCamera(); - public readonly material: THREE.RawShaderMaterial; - public mesh: THREE.InstancedMesh; + public readonly camera: THREE.OrthographicCamera = + new THREE.OrthographicCamera(); + public readonly material: THREE.RawShaderMaterial; + public mesh!: THREE.InstancedMesh; - constructor(renderer: Renderer) { - super(renderer); + constructor(renderer: Renderer) { + super(renderer); - this.material = new THREE.RawShaderMaterial({ - uniforms: { - tDataCurrent: {value: null}, - tDataLast: {value: null}, - }, - vertexShader, - fragmentShader, - defines: this.renderer.getCommonMaterialDefines(), - glslVersion: THREE.GLSL3 - }); + this.material = new THREE.RawShaderMaterial({ + uniforms: { + tDataCurrent: { value: null }, + tDataLast: { value: null }, + }, + vertexShader, + fragmentShader, + defines: this.renderer.getCommonMaterialDefines(), + glslVersion: THREE.GLSL3, + }); - this.createMesh(); - } + this.createMesh(); + } - private createMesh() { - if (this.mesh) { - this.remove(this.mesh); - this.mesh.dispose(); - } + private createMesh() { + if (this.mesh) { + this.remove(this.mesh); + this.mesh.dispose(); + } - this.mesh = new THREE.InstancedMesh( - new THREE.BoxGeometry(1, 1, 1), - this.material, - this.renderer.resources.antsDataRenderTarget0.width * this.renderer.resources.antsDataRenderTarget0.height - ); - this.add(this.mesh); - } + this.mesh = new THREE.InstancedMesh( + new THREE.BoxGeometry(1, 1, 1), + this.material, + this.renderer.resources.antsDataRenderTarget0.width * + this.renderer.resources.antsDataRenderTarget0.height, + ); + this.add(this.mesh); + } - public recompileMaterials() { - this.material.defines = this.renderer.getCommonMaterialDefines(); - this.material.needsUpdate = true; - this.createMesh(); - } + public recompileMaterials() { + this.material.defines = this.renderer.getCommonMaterialDefines(); + this.material.needsUpdate = true; + this.createMesh(); + } - 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/ScreenScene.ts b/src/scenes/ScreenScene.ts index 5cb7b9f..daff0c2 100644 --- a/src/scenes/ScreenScene.ts +++ b/src/scenes/ScreenScene.ts @@ -1,209 +1,217 @@ -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 * as THREE from "three"; 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 { - None, - Food, - Home, - Obstacle, - Erase + None, + Food, + Home, + Obstacle, + Erase, } export default class ScreenScene extends AbstractScene { - public readonly camera: THREE.OrthographicCamera; - public readonly material: THREE.ShaderMaterial; - public ants: THREE.InstancedMesh; - public readonly groundMaterial: THREE.ShaderMaterial; - public readonly pointerPosition: THREE.Vector2 = new THREE.Vector2(); - public drawMode: PointerState = PointerState.None; - private cameraZoomLinear: number = 0; - private isPointerDown: boolean = false; - public renderWidth: number = 1; - public renderHeight: number = 1; + public readonly camera: THREE.OrthographicCamera; + public readonly material: THREE.ShaderMaterial; + public ants!: THREE.InstancedMesh; + public readonly groundMaterial: THREE.ShaderMaterial; + public readonly pointerPosition: THREE.Vector2 = new THREE.Vector2(); + public drawMode: PointerState = PointerState.None; + private cameraZoomLinear: number = 0; + private isPointerDown: boolean = false; + public renderWidth: number = 1; + public renderHeight: number = 1; - constructor(renderer: Renderer) { - super(renderer); + constructor(renderer: Renderer) { + super(renderer); - const ground = new THREE.Mesh( - new THREE.PlaneGeometry(1, 1), - new THREE.ShaderMaterial({ - uniforms: { - map: {value: this.renderer.resources.worldRenderTarget.texture} - }, - vertexShader: vertexShaderGround, - fragmentShader: fragmentShaderGround, - defines: this.renderer.getCommonMaterialDefines(), - glslVersion: THREE.GLSL3 - }) - ); + const ground = new THREE.Mesh( + new THREE.PlaneGeometry(1, 1), + new THREE.ShaderMaterial({ + uniforms: { + map: { + value: this.renderer.resources.worldRenderTarget + .texture, + }, + }, + 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 foodTexture = new THREE.TextureLoader().load('textures/food.png'); + const antTexture = new THREE.TextureLoader().load("textures/ant.png"); + const foodTexture = new THREE.TextureLoader().load("textures/food.png"); - antTexture.magFilter = foodTexture.magFilter = THREE.NearestFilter; - antTexture.minFilter = foodTexture.minFilter = THREE.LinearMipMapLinearFilter; + antTexture.magFilter = foodTexture.magFilter = THREE.NearestFilter; + antTexture.minFilter = foodTexture.minFilter = + THREE.LinearMipMapLinearFilter; - this.material = new THREE.ShaderMaterial({ - uniforms: { - tData: {value: this.renderer.resources.antsDataRenderTarget0.texture}, - tAnt: {value: antTexture}, - tFood: {value: foodTexture} - }, - vertexShader: vertexShaderAnts, - fragmentShader: fragmentShaderAnts, - transparent: true - }); + this.material = new THREE.ShaderMaterial({ + uniforms: { + tData: { + value: this.renderer.resources.antsDataRenderTarget0 + .texture, + }, + tAnt: { value: antTexture }, + tFood: { value: foodTexture }, + }, + 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 raycaster = new THREE.Raycaster(); + const raycastVector = new THREE.Vector2(0, 0); + const raycaster = new THREE.Raycaster(); - this.renderer.canvas.addEventListener('contextmenu', e => { - e.preventDefault(); - }); + this.renderer.canvas.addEventListener("contextmenu", (e) => { + e.preventDefault(); + }); - this.renderer.canvas.addEventListener('pointerdown', e => { - this.isPointerDown = true; + this.renderer.canvas.addEventListener("pointerdown", (e) => { + this.isPointerDown = true; - raycastVector.x = (e.clientX / window.innerWidth) * 2 - 1; - raycastVector.y = -(e.clientY / window.innerHeight) * 2 + 1; + raycastVector.x = (e.clientX / window.innerWidth) * 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) { - const uv = intersects[0].uv; - this.pointerPosition.copy(uv); - } - }); + if (intersects.length > 0) { + const uv = intersects[0].uv; + if (uv) this.pointerPosition.copy(uv); + } + }); - this.renderer.canvas.addEventListener('pointermove', e => { - if (this.isPointerDown) { - const dx = e.movementX; - const dy = e.movementY; + this.renderer.canvas.addEventListener("pointermove", (e) => { + if (this.isPointerDown) { + const dx = e.movementX; + const dy = e.movementY; - this.camera.position.x -= dx / window.innerHeight / this.camera.zoom; - this.camera.position.y += dy / window.innerHeight / this.camera.zoom; - } + this.camera.position.x -= + 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.y = -(e.clientY / window.innerHeight) * 2 + 1; + raycastVector.x = (e.clientX / window.innerWidth) * 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) { - const uv = intersects[0].uv; - this.pointerPosition.copy(uv); - } - }); + if (intersects.length > 0) { + const uv = intersects[0].uv; + if (uv) this.pointerPosition.copy(uv); + } + }); - this.renderer.canvas.addEventListener('pointerup', e => { - this.isPointerDown = false; - }); + this.renderer.canvas.addEventListener("pointerup", (e) => { + this.isPointerDown = false; + }); - this.renderer.canvas.addEventListener('pointerleave', e => { - this.isPointerDown = false; - }); + this.renderer.canvas.addEventListener("pointerleave", (e) => { + this.isPointerDown = false; + }); - this.renderer.canvas.addEventListener('wheel', e => { - this.cameraZoomLinear -= e.deltaY * 0.001; + this.renderer.canvas.addEventListener("wheel", (e) => { + this.cameraZoomLinear -= e.deltaY * 0.001; - this.updateCameraZoom(); - }); + this.updateCameraZoom(); + }); - window.addEventListener('keydown', e => { - switch (e.code) { - case 'KeyQ': { - this.drawMode = PointerState.Home; - break; - } - case 'KeyW': { - this.drawMode = PointerState.Food; - break; - } - case 'KeyE': { - this.drawMode = PointerState.Obstacle; - break; - } - case 'KeyR': { - this.drawMode = PointerState.Erase; - break; - } - } - }); + window.addEventListener("keydown", (e) => { + switch (e.code) { + case "KeyQ": { + this.drawMode = PointerState.Home; + break; + } + case "KeyW": { + this.drawMode = PointerState.Food; + break; + } + case "KeyE": { + this.drawMode = PointerState.Obstacle; + break; + } + case "KeyR": { + this.drawMode = PointerState.Erase; + break; + } + } + }); - window.addEventListener('keyup', e => { - this.drawMode = PointerState.None; - }); - } + window.addEventListener("keyup", (e) => { + this.drawMode = PointerState.None; + }); + } - private updateCameraZoom() { - this.camera.zoom = 2 ** this.cameraZoomLinear; - this.camera.updateProjectionMatrix(); - } + private updateCameraZoom() { + this.camera.zoom = 2 ** this.cameraZoomLinear; + this.camera.updateProjectionMatrix(); + } - private createInstancedAntsMesh() { - if (this.ants) { - this.remove(this.ants); - this.ants.dispose(); - } + private createInstancedAntsMesh() { + if (this.ants) { + this.remove(this.ants); + this.ants.dispose(); + } - const scale = 8 / Config.worldSize; + const scale = 8 / Config.worldSize; - const ants = new THREE.InstancedMesh( - new THREE.PlaneGeometry(scale, scale), - this.material, - this.renderer.resources.antsDataRenderTarget0.width * this.renderer.resources.antsDataRenderTarget0.height - ) + const ants = new THREE.InstancedMesh( + new THREE.PlaneGeometry(scale, scale), + this.material, + 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() { - this.groundMaterial.defines = this.renderer.getCommonMaterialDefines(); - this.groundMaterial.needsUpdate = true; - this.createInstancedAntsMesh(); - } + public recompileMaterials() { + this.groundMaterial.defines = this.renderer.getCommonMaterialDefines(); + this.groundMaterial.needsUpdate = true; + this.createInstancedAntsMesh(); + } - public resize(width: number, height: number) { - const aspect = width / height; + public resize(width: number, height: number) { + const aspect = width / height; - this.camera.left = -0.5 * aspect; - this.camera.right = 0.5 * aspect; - this.camera.top = 0.5; - this.camera.bottom = -0.5; + this.camera.left = -0.5 * aspect; + this.camera.right = 0.5 * aspect; + this.camera.top = 0.5; + this.camera.bottom = -0.5; - this.camera.updateProjectionMatrix(); + this.camera.updateProjectionMatrix(); - this.renderWidth = width; - this.renderHeight = height; - } + this.renderWidth = width; + this.renderHeight = height; + } - public update() { - - } -} \ No newline at end of file + public update() {} +}