From a1e164454d8637c2cfc1743e21624f78260369ae Mon Sep 17 00:00:00 2001 From: Jared Miller Date: Wed, 11 Mar 2026 13:23:28 -0400 Subject: [PATCH] Add GPU lookup texture data generation for materials --- src/__tests__/materials.test.ts | 47 +++++++++++++++++++++++++++++++++ src/materials/lookupTexture.ts | 28 ++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 src/materials/lookupTexture.ts diff --git a/src/__tests__/materials.test.ts b/src/__tests__/materials.test.ts index 503ed36..dbc1304 100644 --- a/src/__tests__/materials.test.ts +++ b/src/__tests__/materials.test.ts @@ -1,4 +1,8 @@ import { describe, expect, test } from "bun:test"; +import { + generateColorData, + generateLookupData, +} from "../materials/lookupTexture"; import { MaterialRegistry } from "../materials/registry"; import { BEHAVIOR_GAS, @@ -72,3 +76,46 @@ describe("MaterialRegistry", () => { expect(() => reg.get(254)).toThrow(); }); }); + +describe("lookup texture generation", () => { + test("generates 256-entry property array", () => { + const reg = new MaterialRegistry(); + const data = generateLookupData(reg); + expect(data.length).toBe(256 * 4); + }); + + test("sand properties at correct offset", () => { + const reg = new MaterialRegistry(); + const data = generateLookupData(reg); + const sand = reg.getByName("sand")!; + const offset = sand.id * 4; + expect(data[offset + 0]).toBe(BEHAVIOR_POWDER); + expect(data[offset + 1]).toBeCloseTo(1.5); + expect(data[offset + 2]).toBeCloseTo(0.1); + }); + + test("generates 256-entry color array", () => { + const reg = new MaterialRegistry(); + const data = generateColorData(reg); + expect(data.length).toBe(256 * 4); + }); + + test("sand color at correct offset", () => { + const reg = new MaterialRegistry(); + const data = generateColorData(reg); + const sand = reg.getByName("sand")!; + const offset = sand.id * 4; + expect(data[offset + 0]).toBeCloseTo(0.76); + expect(data[offset + 1]).toBeCloseTo(0.7); + expect(data[offset + 2]).toBeCloseTo(0.5); + expect(data[offset + 3]).toBeCloseTo(1.0); + }); + + test("unregistered ids default to air properties", () => { + const reg = new MaterialRegistry(); + const data = generateLookupData(reg); + const offset = 200 * 4; + expect(data[offset + 0]).toBe(0); + expect(data[offset + 1]).toBe(0); + }); +}); diff --git a/src/materials/lookupTexture.ts b/src/materials/lookupTexture.ts new file mode 100644 index 0000000..73e8519 --- /dev/null +++ b/src/materials/lookupTexture.ts @@ -0,0 +1,28 @@ +import type { MaterialRegistry } from "./registry"; + +// generates flat Float32 array for material properties texture (256x1 RGBA) +// R: behavior type, G: density, B: hardness, A: angleOfRepose / 90 +export function generateLookupData(registry: MaterialRegistry): Float32Array { + const data = new Float32Array(256 * 4); + for (const m of registry.all()) { + const offset = m.id * 4; + data[offset + 0] = m.behavior; + data[offset + 1] = m.density; + data[offset + 2] = m.hardness; + data[offset + 3] = m.angleOfRepose / 90.0; + } + return data; +} + +// generates flat Float32 array for material color texture (256x1 RGBA) +export function generateColorData(registry: MaterialRegistry): Float32Array { + const data = new Float32Array(256 * 4); + for (const m of registry.all()) { + const offset = m.id * 4; + data[offset + 0] = m.color[0]; + data[offset + 1] = m.color[1]; + data[offset + 2] = m.color[2]; + data[offset + 3] = m.color[3]; + } + return data; +}