Add colony stats readback for aggregate ant state
This commit is contained in:
parent
862cbfc3b7
commit
451e187032
4 changed files with 76 additions and 0 deletions
67
src/ColonyStats.ts
Normal file
67
src/ColonyStats.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import type * as THREE from "three";
|
||||
|
||||
export interface ColonyStatsData {
|
||||
foragerRatio: number; // fraction of ants currently carrying food
|
||||
totalAnts: number; // total ant count
|
||||
}
|
||||
|
||||
export default class ColonyStats {
|
||||
private buffer: Float32Array;
|
||||
private statsData: ColonyStatsData = {
|
||||
foragerRatio: 0,
|
||||
totalAnts: 0,
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this.buffer = new Float32Array(0);
|
||||
}
|
||||
|
||||
public update(
|
||||
renderer: THREE.WebGLRenderer,
|
||||
antTarget: THREE.WebGLRenderTarget,
|
||||
): ColonyStatsData {
|
||||
const width = antTarget.width;
|
||||
const height = antTarget.height;
|
||||
const pixelCount = width * height;
|
||||
|
||||
// resize buffer if needed
|
||||
if (this.buffer.length !== pixelCount * 4) {
|
||||
this.buffer = new Float32Array(pixelCount * 4);
|
||||
}
|
||||
|
||||
renderer.readRenderTargetPixels(
|
||||
antTarget,
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height,
|
||||
this.buffer,
|
||||
);
|
||||
|
||||
let carryingCount = 0;
|
||||
let activeAnts = 0;
|
||||
|
||||
for (let i = 0; i < pixelCount; i++) {
|
||||
const posX = this.buffer[i * 4];
|
||||
const posY = this.buffer[i * 4 + 1];
|
||||
const w = this.buffer[i * 4 + 3]; // alpha = packed state
|
||||
|
||||
// skip uninitialized ants (both coordinates at origin)
|
||||
if (posX === 0 && posY === 0) continue;
|
||||
|
||||
activeAnts++;
|
||||
const isCarrying = Math.trunc(w) & 1;
|
||||
if (isCarrying) carryingCount++;
|
||||
}
|
||||
|
||||
this.statsData.totalAnts = activeAnts;
|
||||
this.statsData.foragerRatio =
|
||||
activeAnts > 0 ? carryingCount / activeAnts : 0;
|
||||
|
||||
return this.statsData;
|
||||
}
|
||||
|
||||
public get data(): ColonyStatsData {
|
||||
return this.statsData;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import type { WebGLRenderTarget } from "three";
|
||||
import * as THREE from "three";
|
||||
import type { SceneCollection } from "./App";
|
||||
import ColonyStats from "./ColonyStats";
|
||||
import Config from "./Config";
|
||||
import {
|
||||
FOOD_QUALITY_MASK,
|
||||
|
|
@ -22,6 +23,7 @@ interface Resources {
|
|||
export default class Renderer {
|
||||
private renderer: THREE.WebGLRenderer;
|
||||
public resources!: Resources;
|
||||
private colonyStats = new ColonyStats();
|
||||
|
||||
constructor(public canvas: HTMLCanvasElement) {
|
||||
this.renderer = new THREE.WebGLRenderer({ canvas });
|
||||
|
|
@ -157,6 +159,11 @@ export default class Renderer {
|
|||
antsComputeTarget.textures[0];
|
||||
scenes.screen.groundMaterial.uniforms.map.value =
|
||||
this.resources.worldRenderTargetCopy.texture;
|
||||
|
||||
// colony stats readback — read ant state texture, compute aggregate stats on CPU
|
||||
this.colonyStats.update(this.renderer, antsComputeTarget);
|
||||
scenes.ants.material.uniforms.uForagerRatio.value =
|
||||
this.colonyStats.data.foragerRatio;
|
||||
}
|
||||
|
||||
private setViewportFromRT(rt: WebGLRenderTarget) {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export default class AntsComputeScene extends AbstractScene {
|
|||
tLastExtState: { value: null },
|
||||
tWorld: { value: null },
|
||||
tPresence: { value: null },
|
||||
uForagerRatio: { value: 0 },
|
||||
},
|
||||
vertexShader,
|
||||
fragmentShader,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ uniform sampler2D tLastState;
|
|||
uniform sampler2D tLastExtState;
|
||||
uniform sampler2D tWorld;
|
||||
uniform sampler2D tPresence;
|
||||
uniform float uForagerRatio;
|
||||
|
||||
const float sampleDistance = 20.;
|
||||
const float cellSize = 1. / WORLD_SIZE;
|
||||
|
|
|
|||
Loading…
Reference in a new issue