From 0f9c1b47f2270428f214750a9181291d914b5382 Mon Sep 17 00:00:00 2001 From: Jared Miller Date: Wed, 11 Mar 2026 13:47:56 -0400 Subject: [PATCH] Replace bit-packed cell flags with material ID constants --- src/Renderer.ts | 24 ++++++---- src/__tests__/constants.test.ts | 81 ++++++++++----------------------- src/constants.ts | 21 ++++----- 3 files changed, 46 insertions(+), 80 deletions(-) diff --git a/src/Renderer.ts b/src/Renderer.ts index b3d198b..e1e89e1 100644 --- a/src/Renderer.ts +++ b/src/Renderer.ts @@ -4,10 +4,12 @@ import type { SceneCollection } from "./App"; import ColonyStats from "./ColonyStats"; import Config from "./Config"; import { - FOOD_QUALITY_MASK, - FOOD_QUALITY_SHIFT, - TERRAIN_TYPE_MASK, - TERRAIN_TYPE_SHIFT, + MAT_AIR, + MAT_DIRT, + MAT_FOOD, + MAT_HOME, + MAT_ROCK, + MAT_SAND, } from "./constants"; import { generateColorData, @@ -70,8 +72,8 @@ export default class Renderer { format: THREE.RGBAFormat, type: THREE.FloatType, depthBuffer: false, - magFilter: THREE.LinearFilter, - minFilter: THREE.LinearFilter, + magFilter: THREE.NearestFilter, + minFilter: THREE.NearestFilter, }, ), worldRenderTargetCopy: new THREE.WebGLRenderTarget( @@ -264,10 +266,12 @@ 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), + MAT_AIR: String(MAT_AIR), + MAT_SAND: String(MAT_SAND), + MAT_DIRT: String(MAT_DIRT), + MAT_ROCK: String(MAT_ROCK), + MAT_FOOD: String(MAT_FOOD), + MAT_HOME: String(MAT_HOME), }; } diff --git a/src/__tests__/constants.test.ts b/src/__tests__/constants.test.ts index ba24b87..57ff24c 100644 --- a/src/__tests__/constants.test.ts +++ b/src/__tests__/constants.test.ts @@ -1,66 +1,33 @@ 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, + MAT_AIR, + MAT_DIRT, + MAT_FOOD, + MAT_HOME, + MAT_ROCK, + MAT_SAND, } 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); +describe("material ID constants", () => { + test("IDs match registry order", () => { + expect(MAT_AIR).toBe(0); + expect(MAT_SAND).toBe(1); + expect(MAT_DIRT).toBe(2); + expect(MAT_ROCK).toBe(3); + expect(MAT_FOOD).toBe(4); + expect(MAT_HOME).toBe(5); }); - 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("IDs are unique", () => { + const ids = [MAT_AIR, MAT_SAND, MAT_DIRT, MAT_ROCK, MAT_FOOD, MAT_HOME]; + expect(new Set(ids).size).toBe(ids.length); + }); + + test("IDs fit in a byte", () => { + const ids = [MAT_AIR, MAT_SAND, MAT_DIRT, MAT_ROCK, MAT_FOOD, MAT_HOME]; + for (const id of ids) { + expect(id).toBeGreaterThanOrEqual(0); + expect(id).toBeLessThanOrEqual(255); } }); - - 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 >> CELL_FOOD_BIT) & 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, - ); - }); }); diff --git a/src/constants.ts b/src/constants.ts index f2b3cc9..a5dc3c0 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,14 +1,9 @@ -// cell metadata bit layout for world texture R channel +// material IDs for world texture R channel +// must match registration order in materials/registry.ts -// 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 +export const MAT_AIR = 0; +export const MAT_SAND = 1; +export const MAT_DIRT = 2; +export const MAT_ROCK = 3; +export const MAT_FOOD = 4; +export const MAT_HOME = 5;