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:
Jared Miller 2026-03-09 10:50:07 -04:00
parent 5899330f5c
commit bdf42a3176
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C
4 changed files with 100 additions and 1 deletions

View file

@ -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),
};
}

View 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
View 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

View file

@ -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);
}