import assert from "node:assert/strict"; import test from "node:test"; import { importGltf } from "../src/importers/gltf"; import { createScene, latestGraph, testCore } from "./scene-helpers"; function close(actual: ArrayLike, expected: ArrayLike) { assert.equal(actual.length, expected.length); for (let index = 0; index < actual.length; index++) assert.ok(Math.abs(actual[index] - expected[index]) < 1e-5); } test("importGltf hydrates worker output into one batched scene update", async () => { const previousWorker = globalThis.Worker; const previousImageBitmap = globalThis.ImageBitmap; const previousCreateImageBitmap = globalThis.createImageBitmap; class MockImageBitmap { readonly width = 1; readonly height = 1; } const workerResult = { camera: { direction: [0, 0, 1], fov: 1.1 }, textures: [ { image: new MockImageBitmap(), sampler: { magFilter: "linear", minFilter: "linear", mipmapFilter: "linear", addressModeU: "repeat", addressModeV: "repeat", }, }, ], materials: [ { baseColor: [0.2, 0.4, 0.6, 1], metallic: 0.3, roughness: 0.7, flags: 0, baseColorTexture: 0, metallicRoughnessTexture: -1, normalTexture: -1, emissiveTexture: -1, occlusionTexture: -1, transmissionTexture: -1, thicknessTexture: -1, clearcoatTexture: -1, iridescenceTexture: -1, iridescenceThicknessTexture: -1, anisotropyTexture: -1, specularTexture: -1, specularColorTexture: -1, }, ], primitives: [ { positions: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]), normals: new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1]), uvs: new Float32Array([0, 0, 1, 0, 0, 1]), indices: new Uint32Array([0, 1, 2]), material: 0, position: [2, 3, 4], rotor: [0, 0, 0, 1], scale: [1, 1, 1], }, ], lights: [ { type: "point", color: [1, 0.5, 0.25], intensity: 6, range: 12, position: [4, 5, 6], }, ], }; class MockWorker { #listeners = new Map void>(); addEventListener(type: string, listener: (event: any) => void) { this.#listeners.set(type, listener); } postMessage(message: { request: number }) { queueMicrotask(() => this.#listeners.get("message")?.({ data: { request: message.request, result: workerResult }, }), ); } } Object.defineProperties(globalThis, { Worker: { configurable: true, value: MockWorker }, ImageBitmap: { configurable: true, value: MockImageBitmap }, createImageBitmap: { configurable: true, value: async () => new MockImageBitmap(), }, }); try { const scene = await createScene(); const core = testCore(scene); const compiled = core.graphs.length; const meshes = await importGltf(scene, "https://example.test/model.glb"); assert.equal(meshes.length, 1); assert.deepEqual(meshes.camera, workerResult.camera); assert.deepEqual(meshes[0].position.toArray(), [2, 3, 4]); assert.deepEqual( Array.from(scene.geometryData(meshes[0].geometryId, "indices")!), [0, 1, 2], ); close( scene.array("materials").row(meshes[0].materialId).slice(0, 6), [0.2, 0.4, 0.6, 1, 0.3, 0.7], ); assert.equal( scene.array("materialTextures").row(meshes[0].materialId)[0], 1, ); assert.deepEqual( Array.from(scene.array("pointLights").row(1)), [1, 0.5, 0.25, 6, 12, 1, 0, 0], ); const texture = latestGraph(scene).resources.textures.find( (resource: any) => resource.id.startsWith("texture-"), ); assert.equal(texture.format, "rgba8unorm-srgb"); assert.equal(core.uploadedTextures.length, 1); assert.equal(core.graphs.length - compiled, 1); } finally { for (const [name, previous] of [ ["Worker", previousWorker], ["ImageBitmap", previousImageBitmap], ["createImageBitmap", previousCreateImageBitmap], ] as const) if (previous === undefined) delete (globalThis as any)[name]; else Object.defineProperty(globalThis, name, { configurable: true, value: previous, }); } });