Add cell metadata bit layout for terrain type and food quality
Reserve bits 3-5 for terrain type (0-7) and bits 6-13 for food quality (0-255) in the world texture R channel. Adds shared TS constants and matching GLSL defines. world.frag now preserves upper bits when writing cell data. Infrastructure only — no behavior changes.
This commit is contained in:
parent
5899330f5c
commit
bdf42a3176
4 changed files with 100 additions and 1 deletions
|
|
@ -2,6 +2,12 @@ import type { WebGLRenderTarget } from "three";
|
|||
import * as THREE from "three";
|
||||
import type { SceneCollection } from "./App";
|
||||
import Config from "./Config";
|
||||
import {
|
||||
FOOD_QUALITY_MASK,
|
||||
FOOD_QUALITY_SHIFT,
|
||||
TERRAIN_TYPE_MASK,
|
||||
TERRAIN_TYPE_SHIFT,
|
||||
} from "./constants";
|
||||
|
||||
interface Resources {
|
||||
worldRenderTarget: THREE.WebGLRenderTarget;
|
||||
|
|
@ -10,6 +16,7 @@ interface Resources {
|
|||
antsComputeTarget0: THREE.WebGLRenderTarget;
|
||||
antsComputeTarget1: THREE.WebGLRenderTarget;
|
||||
antsDiscreteRenderTarget: THREE.WebGLRenderTarget;
|
||||
antsPresenceRenderTarget: THREE.WebGLRenderTarget;
|
||||
}
|
||||
|
||||
export default class Renderer {
|
||||
|
|
@ -207,6 +214,10 @@ export default class Renderer {
|
|||
REPELLENT_THRESHOLD: Renderer.convertNumberToFloatString(
|
||||
Config.repellentThreshold,
|
||||
),
|
||||
TERRAIN_TYPE_SHIFT: String(TERRAIN_TYPE_SHIFT),
|
||||
TERRAIN_TYPE_MASK: String(TERRAIN_TYPE_MASK),
|
||||
FOOD_QUALITY_SHIFT: String(FOOD_QUALITY_SHIFT),
|
||||
FOOD_QUALITY_MASK: String(FOOD_QUALITY_MASK),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
66
src/__tests__/constants.test.ts
Normal file
66
src/__tests__/constants.test.ts
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import { describe, expect, test } from "bun:test";
|
||||
import {
|
||||
CELL_FOOD_BIT,
|
||||
CELL_HOME_BIT,
|
||||
CELL_OBSTACLE_BIT,
|
||||
FOOD_QUALITY_MASK,
|
||||
FOOD_QUALITY_SHIFT,
|
||||
TERRAIN_TYPE_MASK,
|
||||
TERRAIN_TYPE_SHIFT,
|
||||
} from "../constants";
|
||||
|
||||
describe("cell metadata bit layout", () => {
|
||||
test("bit fields do not overlap", () => {
|
||||
// cell flags occupy bits 0-2
|
||||
const cellFlagsBits = (1 << (CELL_OBSTACLE_BIT + 1)) - 1;
|
||||
// terrain occupies bits 3-5
|
||||
const terrainBits = TERRAIN_TYPE_MASK << TERRAIN_TYPE_SHIFT;
|
||||
// food quality occupies bits 6-13
|
||||
const foodQualityBits = FOOD_QUALITY_MASK << FOOD_QUALITY_SHIFT;
|
||||
|
||||
expect(cellFlagsBits & terrainBits).toBe(0);
|
||||
expect(cellFlagsBits & foodQualityBits).toBe(0);
|
||||
expect(terrainBits & foodQualityBits).toBe(0);
|
||||
});
|
||||
|
||||
test("terrain type can encode 8 values", () => {
|
||||
for (let t = 0; t <= 7; t++) {
|
||||
const packed = t << TERRAIN_TYPE_SHIFT;
|
||||
const unpacked = (packed >> TERRAIN_TYPE_SHIFT) & TERRAIN_TYPE_MASK;
|
||||
expect(unpacked).toBe(t);
|
||||
}
|
||||
});
|
||||
|
||||
test("food quality can encode 0-255", () => {
|
||||
for (const q of [0, 1, 127, 255]) {
|
||||
const packed = q << FOOD_QUALITY_SHIFT;
|
||||
const unpacked = (packed >> FOOD_QUALITY_SHIFT) & FOOD_QUALITY_MASK;
|
||||
expect(unpacked).toBe(q);
|
||||
}
|
||||
});
|
||||
|
||||
test("all fields round-trip together", () => {
|
||||
const food = 1;
|
||||
const home = 1;
|
||||
const obstacle = 0;
|
||||
const terrain = 5;
|
||||
const quality = 200;
|
||||
|
||||
const packed =
|
||||
food +
|
||||
(home << CELL_HOME_BIT) +
|
||||
(obstacle << CELL_OBSTACLE_BIT) +
|
||||
(terrain << TERRAIN_TYPE_SHIFT) +
|
||||
(quality << FOOD_QUALITY_SHIFT);
|
||||
|
||||
expect(packed & 1).toBe(food);
|
||||
expect((packed >> CELL_HOME_BIT) & 1).toBe(home);
|
||||
expect((packed >> CELL_OBSTACLE_BIT) & 1).toBe(obstacle);
|
||||
expect((packed >> TERRAIN_TYPE_SHIFT) & TERRAIN_TYPE_MASK).toBe(
|
||||
terrain,
|
||||
);
|
||||
expect((packed >> FOOD_QUALITY_SHIFT) & FOOD_QUALITY_MASK).toBe(
|
||||
quality,
|
||||
);
|
||||
});
|
||||
});
|
||||
14
src/constants.ts
Normal file
14
src/constants.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// cell metadata bit layout for world texture R channel
|
||||
|
||||
// bits 0-2: cell type flags
|
||||
export const CELL_FOOD_BIT = 0;
|
||||
export const CELL_HOME_BIT = 1;
|
||||
export const CELL_OBSTACLE_BIT = 2;
|
||||
|
||||
// bits 3-5: terrain type (0-7)
|
||||
export const TERRAIN_TYPE_SHIFT = 3;
|
||||
export const TERRAIN_TYPE_MASK = 0b111; // 3 bits
|
||||
|
||||
// bits 6-13: food quality (0-255)
|
||||
export const FOOD_QUALITY_SHIFT = 6;
|
||||
export const FOOD_QUALITY_MASK = 0xff; // 8 bits
|
||||
|
|
@ -24,5 +24,13 @@ void main() {
|
|||
isFood = 0;
|
||||
}
|
||||
|
||||
FragColor = vec4(float(isFood + (isHome << 1) + (isObstacle << 2)), scentToHome, scentToFood, repellent);
|
||||
int terrainType = (cellData >> TERRAIN_TYPE_SHIFT) & TERRAIN_TYPE_MASK;
|
||||
int foodQuality = (cellData >> FOOD_QUALITY_SHIFT) & FOOD_QUALITY_MASK;
|
||||
|
||||
// reconstruct cell data preserving all bit fields
|
||||
int newCellData = isFood + (isHome << 1) + (isObstacle << 2)
|
||||
+ (terrainType << TERRAIN_TYPE_SHIFT)
|
||||
+ (foodQuality << FOOD_QUALITY_SHIFT);
|
||||
|
||||
FragColor = vec4(float(newCellData), scentToHome, scentToFood, repellent);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue