Add local glTF viewer and visual coverage
Amp-Thread-ID: https://ampcode.com/threads/T-01a02d1c-27fc-7761-9f51-ed97b2dd4e3e Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
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<number>, expected: ArrayLike<number>) {
|
||||
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<string, (event: any) => 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,
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user