Generate agents

This commit is contained in:
Andras Schmelczer 2023-05-21 11:03:37 +01:00
parent 5ca7514891
commit 7e8ca4b16f
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
10 changed files with 370 additions and 91 deletions

View file

@ -1,4 +1,5 @@
import { AgentGenerationPipeline } from '../pipelines/agents/agent-generation/agent-generation-pipeline';
import { GenerationCounts } from '../pipelines/agents/agent-generation/generation-counts';
import { AgentPipeline } from '../pipelines/agents/agent-pipeline';
import { BrushPipeline } from '../pipelines/brush/brush-pipeline';
import { CommonState } from '../pipelines/common-state/common-state';
@ -7,16 +8,17 @@ import { DiffusionPipeline } from '../pipelines/diffusion/diffusion-pipeline';
import { RenderPipeline } from '../pipelines/render/render-pipeline';
import { settings } from '../settings';
import { DeltaTimeCalculator } from '../utils/delta-time-calculator';
import { initializeContext } from '../utils/graphics/initialize-context';
import { ResizableTexture } from '../utils/graphics/resizable-texture';
import { sleep } from '../utils/sleep';
import { GameRules } from './game-rules';
import { vec2 } from 'gl-matrix';
export default class GameLoop {
private readonly deltaTimeCalculator = new DeltaTimeCalculator();
private readonly trailMapA: ResizableTexture;
private readonly trailMapB: ResizableTexture;
private readonly commonState: CommonState;
private readonly copyPipeline: CopyPipeline;
private readonly agentGenerationPipeline: AgentGenerationPipeline;
@ -25,6 +27,8 @@ export default class GameLoop {
private readonly brushPipeline: BrushPipeline;
private readonly diffusionPipeline: DiffusionPipeline;
private readonly gameRules = new GameRules(performance.now() / 1000);
private hasFinished = false;
private readonly hasFinishedPromise: Promise<void> = new Promise(
(resolve) => (this.resolveHasFinished = resolve)
@ -35,33 +39,34 @@ export default class GameLoop {
public constructor(
private readonly canvas: HTMLCanvasElement,
private readonly device: GPUDevice
private readonly device: GPUDevice,
private readonly deltaTimeCalculator: DeltaTimeCalculator
) {
const context = this.canvas.getContext('webgpu') as any as GPUCanvasContext;
context.configure({
device: this.device,
format: navigator.gpu.getPreferredCanvasFormat(),
alphaMode: 'premultiplied',
});
const context = initializeContext({ device, canvas });
this.trailMapA = new ResizableTexture(this.device, this.canvasSize);
this.trailMapB = new ResizableTexture(this.device, this.canvasSize);
this.resize();
this.commonState = new CommonState(this.device);
this.commonState.setParameters(this.canvasSize, 0, 0);
this.commonState.setParameters({
canvasSize: this.canvasSize,
time: 0,
deltaTime: 0,
});
this.copyPipeline = new CopyPipeline(this.device);
this.agentGenerationPipeline = new AgentGenerationPipeline(
this.device,
this.commonState
this.commonState,
settings.agentCount
);
this.agentPipeline = new AgentPipeline(
this.device,
this.commonState,
this.agentGenerationPipeline.generateAgents(settings.agentCount)
this.agentGenerationPipeline.agentsBuffer
);
this.brushPipeline = new BrushPipeline(this.device, this.commonState);
this.diffusionPipeline = new DiffusionPipeline(this.device, this.commonState);
@ -85,6 +90,10 @@ export default class GameLoop {
return this.hasFinishedPromise;
}
public get aliveAgentCounts(): GenerationCounts {
return this.gameRules.generationCounts;
}
private onSwipe(event: MouseEvent) {
if (!this.isSwipeActive) {
return;
@ -104,23 +113,56 @@ export default class GameLoop {
private async render(time: DOMHighResTimeStamp) {
if (this.hasFinished) {
this.resolveHasFinished();
return;
}
const deltaTime = this.deltaTimeCalculator.calculateDeltaTimeInSeconds(time);
this.commonState.setParameters(this.canvasSize, deltaTime, time);
time *= settings.renderSpeed;
const timeInSeconds = time / 1000;
[
this.commonState,
this.agentPipeline,
this.brushPipeline,
this.diffusionPipeline,
this.renderPipeline,
].forEach((pipeline) => pipeline.setParameters(settings));
const commandEncoder = this.device.createCommandEncoder();
].forEach((pipeline) =>
pipeline.setParameters({
time,
nextGenerationAggression: this.gameRules.nextGenerationAgression,
deltaTime,
canvasSize: this.canvasSize,
...settings,
})
);
for (let i = 0; i < settings.renderSpeed; i++) {
const commandEncoder = this.device.createCommandEncoder();
if (
this.gameRules.generationCounts.currentGenerationCount == 0 &&
this.gameRules.generationCounts.nextGenerationCount == 0
) {
this.gameRules.updateGenerationCounts(
await this.agentGenerationPipeline.spawnNextGenerationCover(
0,
settings.agentCount * (1 - settings.initialDeadRatio)
)
);
}
const spawnAction = this.gameRules.getSpawnAction(timeInSeconds, this.canvasSize);
this.gameRules.updateGenerationCounts(
await this.agentGenerationPipeline.spawnNextGenerationCircle(
spawnAction.generation,
spawnAction.count,
spawnAction.position,
settings.nextGenerationSpawnRadius
)
);
this.copyPipeline.execute(
commandEncoder,
this.trailMapA.getTextureView(),
@ -138,9 +180,9 @@ export default class GameLoop {
this.trailMapA.getTextureView()
);
this.renderPipeline.execute(commandEncoder, this.trailMapA.getTextureView());
}
this.device.queue.submit([commandEncoder.finish()]);
this.device.queue.submit([commandEncoder.finish()]);
}
if (!this.isSwipeActive) {
this.brushPipeline.clearSwipes();
@ -157,20 +199,19 @@ export default class GameLoop {
requestAnimationFrame(this.render.bind(this));
}
public destroy() {
public async destroy() {
this.hasFinished = true;
await this.hasFinishedPromise;
this.copyPipeline?.destroy();
this.agentGenerationPipeline?.destroy();
this.agentPipeline?.destroy();
this.brushPipeline?.destroy();
this.diffusionPipeline?.destroy();
this.renderPipeline?.destroy();
this.commonState?.destroy();
this.trailMapA?.destroy();
this.trailMapB?.destroy();
this.resolveHasFinished();
}
private get canvasSize(): vec2 {