import assert from 'node:assert/strict'; import test from 'node:test'; import { readFileSync } from 'node:fs'; import { Mesh, MeshStandardMaterial } from 'three'; import { createBuildings } from '../src/scene/buildings.ts'; import { loadBuildingAssets, createBakedBuildings } from '../src/scene/buildingAssets.ts'; test('saved buildings load without GeoJSON and preserve geometry and lighting exactly', async (t) => { const requested: string[] = []; t.mock.method(globalThis, 'fetch', async (input: string) => { requested.push(input); const path = new URL(input, 'http://localhost').pathname; return new Response(readFileSync(new URL(`../public${path}`, import.meta.url))); }); const metadata = JSON.parse(readFileSync(new URL('../public/terrain/gwanaksan.json', import.meta.url), 'utf8')); const bytes = readFileSync(new URL('../public/terrain/gwanaksan.f32', import.meta.url)); const terrain = { metadata, heights: new Float32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4) }; const data = JSON.parse(readFileSync(new URL('../public/buildings/gwacheon.geojson', import.meta.url), 'utf8')); const saved = await loadBuildingAssets(); assert.equal(requested.length, 2); assert.ok(requested.every(path => !path.includes('.geojson'))); const baked = createBakedBuildings(saved, metadata); const original = createBuildings(data, terrain); try { assert.deepEqual(baked.counts, original.counts); assert.deepEqual(baked.target, original.target); assert.equal(baked.group.children.length, original.group.children.length); for (const [i, child] of baked.group.children.entries()) { const actual = child as Mesh, expected = original.group.children[i] as Mesh; assert.equal(actual.name, expected.name); for (const name of ['position', 'normal', 'buildingLightHeight']) { assert.deepEqual(actual.geometry.getAttribute(name).array, expected.geometry.getAttribute(name).array); } assert.deepEqual(actual.geometry.index!.array, expected.geometry.index!.array); expected.geometry.computeBoundingBox(); expected.geometry.computeBoundingSphere(); assert.deepEqual(actual.geometry.boundingBox, expected.geometry.boundingBox); assert.deepEqual(actual.geometry.boundingSphere, expected.geometry.boundingSphere); assert.equal((actual.material as MeshStandardMaterial).color.getHex(), (expected.material as MeshStandardMaterial).color.getHex()); } assert.deepEqual(baked.spill.texture.image.data, original.spill.texture.image.data); assert.deepEqual(baked.spill.bounds, original.spill.bounds); for (const altitude of [-8, 0, 6, -8]) { baked.update(altitude); original.update(altitude); assert.equal(baked.spill.intensity.value, original.spill.intensity.value); } assert.throws(() => createBakedBuildings(saved, { ...metadata, observerSurfaceElevation: metadata.observerSurfaceElevation + 1 }), /지형/); assert.throws(() => createBakedBuildings({ ...saved, buffer: saved.buffer.slice(4) }, metadata), /건물/); } finally { baked.dispose(); original.dispose(); } });