import assert from "node:assert/strict"; import test from "node:test"; import { buildNativeWaterGeometry } from "./native-water-geometry.mjs"; const primaryPlacement = { translation: [2, 3, 4], rotation: [0, 0, 0, 1], scale: [1, 1, 1], }; function fixtureLayer({ flags = [0xff, 0xdf, 0x9f, 0xbf, 0x80, 0x00, 0xff, 0x9f, 0xbf] } = {}) { const colors = [ 0xff112233, 0xff445566, 0xff778899, 0xffaabbcc, 0xffddeeff, 0xff010203, 0xff102030, 0xff405060, 0xff708090, ]; return { classGuid: "test-water", version: 3, waterBodyGuid: "exact-body-guid", transform: { scale: [2, 1, 1], rotation: [0, 0, 0, 1], translation: [100, 5, 200] }, height: { origin: [0, 0, 0], spacing: 10, overlap: 0, width: 3, height: 3, values: Float32Array.from([1, 2, 3, 4, 5, 6, 7, 8, 9]), }, color: { present: true, origin: [0, 0, 0], spacing: 10, overlap: 0, width: 3, height: 3, values: Uint32Array.from(colors), }, flags: { origin: [0, 0, 0], spacing: 10, overlap: 0, width: 3, height: 3, values: Uint8Array.from(flags), }, descriptorJoin: { strategy: "exact serialized body GUID bytes", status: "matched", descriptorKeys: ["body-row"], descriptors: [{ key: "body-row", guid: "exact-body-guid", effectType: 4 }], }, unbuildableReason: null, }; } function assertClose(actual, expected, epsilon = 1e-6) { assert.equal(actual.length, expected.length); for (let index = 0; index < expected.length; index += 1) { assert.ok(Math.abs(actual[index] - expected[index]) <= epsilon, `value[${index}] ${actual[index]} != ${expected[index]}`); } } test("native water geometry expands even and odd pair patterns with one reflected-Z winding reversal", () => { const built = buildNativeWaterGeometry([fixtureLayer()], primaryPlacement, 0.1); const [water] = built.layers; assert.equal(water.summary.consideredTwoCellQuadPairs, 2); assert.equal(water.summary.emittedTwoCellQuadPairs, 2); assert.equal(water.summary.emittedTriangles, 8); assert.deepEqual(Array.from(water.indices.slice(0, 12)), [0, 4, 3, 0, 1, 4, 1, 2, 4, 2, 5, 4]); assert.deepEqual(Array.from(water.indices.slice(12, 24)), [3, 4, 6, 4, 7, 6, 4, 5, 8, 4, 8, 7]); assertClose(Array.from(water.positions.slice(0, 3)), [9.8, 0.3, -19.6]); assert.equal(water.summary.sourceSplitPlaneHeight, 6); assertClose(Array.from(water.positions.slice(3, 6)), [11.8, 0.4, -19.6]); assertClose(Array.from(water.sourceWorldXZ.slice(0, 6)), [100, 200, 120, 200, 140, 200]); assertClose(Array.from(water.sourceWorldXZ.slice(6, 12)), [100, 210, 120, 210, 140, 210]); assert.deepEqual(Array.from(water.normals.slice(0, 3)), [0, 1, 0]); assertClose(Array.from(water.colors.slice(0, 4)), [17 / 255, 34 / 255, 51 / 255, 1]); assertClose([water.colors[1 * 4 + 3]], [125 / 255]); assert.equal(water.layer.descriptorJoin.descriptors[0].effectType, 4); }); test("water visibility bit masks pair geometry while preserving per-vertex diffuse alpha", () => { const hidden = buildNativeWaterGeometry([fixtureLayer({ flags: new Array(9).fill(0x3f) })], primaryPlacement, 0.1); assert.equal(hidden.layers[0].indices.length, 0); assert.equal(hidden.layers[0].colors[3], 1); const visibleFromOneVertex = buildNativeWaterGeometry([fixtureLayer({ flags: [0x80, 0, 0, 0, 0, 0, 0, 0, 0] })], primaryPlacement, 0.1); assert.equal(visibleFromOneVertex.layers[0].summary.emittedTriangles, 4); assert.equal(visibleFromOneVertex.layers[0].colors[3], 0); }); test("native water builder refuses non-exact material joins and missing source color bytes", () => { const unmatched = fixtureLayer(); unmatched.descriptorJoin.status = "missing"; assert.throws(() => buildNativeWaterGeometry([unmatched], primaryPlacement, 0.1), /descriptor GUID join is missing/); const v1 = fixtureLayer(); v1.color = { present: false }; v1.unbuildableReason = "native v1 color absent"; const result = buildNativeWaterGeometry([v1], primaryPlacement, 0.1); assert.equal(result.layers.length, 0); assert.equal(result.skipped[0].reason, "native v1 color absent"); });