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 type { WebGLRenderTarget } from "three";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import type { SceneCollection } from "./App";
|
import type { SceneCollection } from "./App";
|
||||||
|
import ColonyStats from "./ColonyStats";
|
||||||
import Config from "./Config";
|
import Config from "./Config";
|
||||||
import {
|
import {
|
||||||
FOOD_QUALITY_MASK,
|
FOOD_QUALITY_MASK,
|
||||||
|
|
@ -22,6 +23,7 @@ interface Resources {
|
||||||
export default class Renderer {
|
export default class Renderer {
|
||||||
private renderer: THREE.WebGLRenderer;
|
private renderer: THREE.WebGLRenderer;
|
||||||
public resources!: Resources;
|
public resources!: Resources;
|
||||||
|
private colonyStats = new ColonyStats();
|
||||||
|
|
||||||
constructor(public canvas: HTMLCanvasElement) {
|
constructor(public canvas: HTMLCanvasElement) {
|
||||||
this.renderer = new THREE.WebGLRenderer({ canvas });
|
this.renderer = new THREE.WebGLRenderer({ canvas });
|
||||||
|
|
@ -157,6 +159,11 @@ export default class Renderer {
|
||||||
antsComputeTarget.textures[0];
|
antsComputeTarget.textures[0];
|
||||||
scenes.screen.groundMaterial.uniforms.map.value =
|
scenes.screen.groundMaterial.uniforms.map.value =
|
||||||
this.resources.worldRenderTargetCopy.texture;
|
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) {
|
private setViewportFromRT(rt: WebGLRenderTarget) {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ export default class AntsComputeScene extends AbstractScene {
|
||||||
tLastExtState: { value: null },
|
tLastExtState: { value: null },
|
||||||
tWorld: { value: null },
|
tWorld: { value: null },
|
||||||
tPresence: { value: null },
|
tPresence: { value: null },
|
||||||
|
uForagerRatio: { value: 0 },
|
||||||
},
|
},
|
||||||
vertexShader,
|
vertexShader,
|
||||||
fragmentShader,
|
fragmentShader,
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ uniform sampler2D tLastState;
|
||||||
uniform sampler2D tLastExtState;
|
uniform sampler2D tLastExtState;
|
||||||
uniform sampler2D tWorld;
|
uniform sampler2D tWorld;
|
||||||
uniform sampler2D tPresence;
|
uniform sampler2D tPresence;
|
||||||
|
uniform float uForagerRatio;
|
||||||
|
|
||||||
const float sampleDistance = 20.;
|
const float sampleDistance = 20.;
|
||||||
const float cellSize = 1. / WORLD_SIZE;
|
const float cellSize = 1. / WORLD_SIZE;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue