Update GUI

This commit is contained in:
vHawk 2022-07-11 03:05:09 +03:00
parent a3c03cafef
commit c84a3b46b7
15 changed files with 60 additions and 52 deletions

View file

@ -16,10 +16,6 @@ It is important to prevent ants from following pheromone trails left by those an
Pheromone trails left by ants evaporate and diffuse over time. Pheromone trails left by ants evaporate and diffuse over time.
## Compute pipeline overview
TODO
## References ## References
- https://softologyblog.wordpress.com/2020/03/21/ant-colony-simulations/ - https://softologyblog.wordpress.com/2020/03/21/ant-colony-simulations/

View file

@ -21,10 +21,9 @@ export default new class App {
private renderer: Renderer = new Renderer(<HTMLCanvasElement>document.getElementById('canvas')); private renderer: Renderer = new Renderer(<HTMLCanvasElement>document.getElementById('canvas'));
private scenes: SceneCollection; private scenes: SceneCollection;
private gui: GUI = new GUI(); private gui: GUI = new GUI();
private renderLoop = (deltaTime: number): void => this.render(deltaTime); private renderLoop = (time: number): void => this.render(time);
private simInterval: NodeJS.Timer; private lastTime: number = 0;
private simulationStepsPerSecond: number = 0; private queuedSimSteps: number = 0;
private simStarted: boolean = false;
constructor() { constructor() {
this.initScenes(); this.initScenes();
@ -35,9 +34,6 @@ export default new class App {
this.renderLoop(0); this.renderLoop(0);
this.simulationStepsPerSecond = Config.simulationStepsPerSecond;
this.updateSimulationInterval();
this.gui.on('antsCount', () => { this.gui.on('antsCount', () => {
this.resetRenderer(); this.resetRenderer();
}); });
@ -45,27 +41,12 @@ export default new class App {
this.gui.on('worldSize', () => { this.gui.on('worldSize', () => {
this.resetRenderer(); this.resetRenderer();
}); });
this.gui.on('simulationStepsPerSecond', () => {
this.simulationStepsPerSecond = Config.simulationStepsPerSecond;
this.updateSimulationInterval();
});
} }
private resetRenderer() { private resetRenderer() {
this.renderer.reset(this.scenes); this.renderer.reset(this.scenes);
} }
private updateSimulationInterval() {
clearInterval(this.simInterval);
this.simInterval = setInterval(() => {
this.simulationStep();
this.simStarted = true;
}, 1000 / this.simulationStepsPerSecond);
}
private initScenes() { private initScenes() {
this.scenes = { this.scenes = {
ants: new AntsComputeScene(this.renderer), ants: new AntsComputeScene(this.renderer),
@ -90,19 +71,31 @@ export default new class App {
private simulationStep() { private simulationStep() {
for (const scene of Object.values(this.scenes)) { for (const scene of Object.values(this.scenes)) {
scene.update(0); scene.update();
} }
this.renderer.renderSimulation(this.scenes); this.renderer.renderSimulation(this.scenes);
} }
private render(deltaTime: number) { private render(time: number) {
requestAnimationFrame(this.renderLoop); requestAnimationFrame(this.renderLoop);
if (!this.simStarted) { const deltaTime = time - this.lastTime;
const simStepsToDo = deltaTime / 1000 * Config.simulationStepsPerSecond;
this.queuedSimSteps += simStepsToDo;
while (this.queuedSimSteps >= 1) {
this.simulationStep();
--this.queuedSimSteps;
}
if (time === 0) {
return; return;
} }
this.renderer.renderToScreen(this.scenes); this.renderer.renderToScreen(this.scenes);
this.lastTime = time;
} }
} }

View file

@ -1,6 +1,6 @@
export default { export default {
worldSize: 1024, worldSize: 1024,
antsCount: 64 ** 2, antsCount: 12,
simulationStepsPerSecond: 60, simulationStepsPerSecond: 60,
scentThreshold: 0.01, scentThreshold: 0.01,
scentFadeOutFactor: 0.998, scentFadeOutFactor: 0.998,

View file

@ -12,19 +12,28 @@ export default class GUI extends EventEmitter {
const simFolder = this.gui.addFolder('Simulation'); const simFolder = this.gui.addFolder('Simulation');
simFolder.add(Config, 'worldSize', 256, 4096).onChange(() => { simFolder.add(Config, 'worldSize', 256, 4096)
this.emit('worldSize'); .name('World size')
}); .step(1)
simFolder.add(Config, 'antsCount', 1, 1e6).onChange(() => { .onChange(() => {
this.emit('antsCount'); this.emit('worldSize');
}); });
simFolder.add(Config, 'simulationStepsPerSecond', 1, 500).onChange(() => { simFolder.add(Config, 'antsCount', 0, 22)
this.emit('simulationStepsPerSecond'); .name('Ants count 2^')
}); .step(1)
.onChange(() => {
this.emit('antsCount');
});
simFolder.add(Config, 'simulationStepsPerSecond', 1, 500)
.name('Simulation steps per second')
.step(1)
.onChange(() => {
this.emit('simulationStepsPerSecond');
});
const controlsFolder = this.gui.addFolder('Controls'); const controlsFolder = this.gui.addFolder('Controls');
controlsFolder.add(Config, 'brushRadius', 1, 100); controlsFolder.add(Config, 'brushRadius', 1, 100).name('Brush radius');
simFolder.open(); simFolder.open();
controlsFolder.open(); controlsFolder.open();

View file

@ -23,7 +23,7 @@ export default class Renderer {
} }
private initResources() { private initResources() {
const antTextureSize = Math.round(Math.sqrt(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(Config.worldSize, Config.worldSize, {
@ -139,7 +139,7 @@ export default class Renderer {
} }
public reset(scenes: SceneCollection) { public reset(scenes: SceneCollection) {
const antTextureSize = Math.ceil(Math.sqrt(Config.antsCount)); const antTextureSize = Math.round(Math.sqrt(2 ** Config.antsCount));
this.resources.worldRenderTarget.setSize(Config.worldSize, Config.worldSize) this.resources.worldRenderTarget.setSize(Config.worldSize, Config.worldSize)
this.renderer.setRenderTarget(this.resources.worldRenderTarget); this.renderer.setRenderTarget(this.resources.worldRenderTarget);

View file

@ -25,7 +25,7 @@
</style> </style>
</head> </head>
<body> <body>
<div id="info">Controls:<br/>Q - draw home cells<br/>W - draw food cells<br/>E - draw obstacle<br/>Drag and scroll to move the camera</div> <div id="info">Controls:<br/>Q - draw home cells<br/>W - draw food cells<br/>E - draw obstacle<br/>R - erase<br/>Drag and scroll to move the camera</div>
<canvas id="canvas"></canvas> <canvas id="canvas"></canvas>
</body> </body>
</html> </html>

View file

@ -15,5 +15,5 @@ export default abstract class AbstractScene extends THREE.Scene {
public abstract resize(width: number, height: number): void; public abstract resize(width: number, height: number): void;
public abstract update(deltaTime: number): void; public abstract update(): void;
} }

View file

@ -52,7 +52,7 @@ export default class AntsComputeScene extends AbstractScene {
} }
public update(deltaTime: number) { public update() {
this.material.uniforms.uTime.value = performance.now(); this.material.uniforms.uTime.value = performance.now();
} }
} }

View file

@ -50,7 +50,7 @@ export default class AntsDiscretizeScene extends AbstractScene {
} }
public update(deltaTime: number) { public update() {
} }
} }

View file

@ -40,7 +40,7 @@ export default class DrawScene extends AbstractScene {
} }
public update(deltaTime: number) { public update() {
} }
} }

View file

@ -11,7 +11,8 @@ enum PointerState {
None, None,
Food, Food,
Home, Home,
Obstacle Obstacle,
Erase
} }
export default class ScreenScene extends AbstractScene { export default class ScreenScene extends AbstractScene {
@ -144,6 +145,10 @@ export default class ScreenScene extends AbstractScene {
this.drawMode = PointerState.Obstacle; this.drawMode = PointerState.Obstacle;
break; break;
} }
case 'KeyR': {
this.drawMode = PointerState.Erase;
break;
}
} }
}); });
@ -198,7 +203,7 @@ export default class ScreenScene extends AbstractScene {
this.renderHeight = height; this.renderHeight = height;
} }
public update(deltaTime: number) { public update() {
} }
} }

View file

@ -37,7 +37,7 @@ export default class WorldBlurScene extends AbstractScene {
} }
public update(deltaTime: number) { public update() {
} }
} }

View file

@ -38,7 +38,7 @@ export default class WorldComputeScene extends AbstractScene {
} }
public update(deltaTime: number) { public update() {
} }
} }

View file

@ -23,8 +23,9 @@ void main() {
float id = float(gl_InstanceID); float id = float(gl_InstanceID);
float sampleY = floor(id / dataTextureSize); float sampleY = floor(id / dataTextureSize);
float sampleX = id - sampleY * dataTextureSize; float sampleX = id - sampleY * dataTextureSize;
vec2 antDataUV = (vec2(sampleX, sampleY) + 0.5) / dataTextureSize;
vec4 dataSample = texture(tData, vec2(sampleX, sampleY) / dataTextureSize); vec4 dataSample = texture(tData, antDataUV);
vec2 offset = dataSample.xy; vec2 offset = dataSample.xy;
vec2 rotatedPosition = rotate(position.xy, -dataSample.z + PI * 0.5); vec2 rotatedPosition = rotate(position.xy, -dataSample.z + PI * 0.5);

View file

@ -25,6 +25,10 @@ void main() {
isHome = 1; isHome = 1;
} else if (drawMode == 3.) { } else if (drawMode == 3.) {
isObstacle = 1; isObstacle = 1;
} else if (drawMode == 4.) {
isFood = 0;
isHome = 0;
isObstacle = 0;
} }
} }