Files
workinf_Blender_Wasm/tools/web/run-blender-golden.mjs
2026-08-12 04:47:48 -04:00

75 lines
3.7 KiB
JavaScript

import fs from "node:fs";
import factory from "../../web/app/src/vendor/blender/web_engine.js";
const wasmBinary = fs.readFileSync(new URL("../../web/app/src/vendor/blender/web_engine.wasm", import.meta.url));
const blend = fs.readFileSync(new URL("../../tests/files/web/basic_scene.blend", import.meta.url));
const expected = JSON.parse(fs.readFileSync(new URL("../../tests/golden/W-079/basic-scene-decimate.json", import.meta.url), "utf8"));
function readSnapshot(engine, handle) {
const dataOut = engine._malloc(4);
const lengthOut = engine._malloc(4);
try {
if (engine._web_engine_get_scene_snapshot(handle, dataOut, lengthOut) !== 0) throw new Error("golden snapshot request failed");
const pointer = engine.HEAPU32[dataOut >>> 2];
const length = engine.HEAPU32[lengthOut >>> 2];
return JSON.parse(new TextDecoder().decode(engine.HEAPU8.slice(pointer, pointer + length)));
}
finally {
engine._free(dataOut);
engine._free(lengthOut);
}
}
function open(engine, handle) {
const pointer = engine._malloc(blend.byteLength);
engine.HEAPU8.set(blend, pointer);
const result = engine._web_engine_open_blend(handle, pointer, blend.byteLength);
engine._free(pointer);
if (result !== 0) throw new Error(`golden fixture open failed: ${engine.UTF8ToString(engine._web_engine_last_error_message())}`);
}
function apply(engine, handle, command) {
const bytes = new TextEncoder().encode(JSON.stringify(command));
const pointer = engine._malloc(bytes.byteLength);
engine.HEAPU8.set(bytes, pointer);
const result = engine._web_engine_apply_command(handle, pointer, bytes.byteLength);
engine._free(pointer);
if (result !== 0) throw new Error(`golden command failed: ${engine.UTF8ToString(engine._web_engine_last_error_message())}`);
}
function sourceSummary(snapshot) {
const meshById = new Map(snapshot.meshes.map((mesh) => [mesh.id, mesh]));
return {
frame: [snapshot.frame.start, snapshot.frame.end],
objects: snapshot.nodes.map((node) => {
const mesh = node.dataId ? meshById.get(node.dataId) : undefined;
return { name: node.name, type: node.type, vertices: mesh?.vertexCount ?? 0, polygons: mesh?.faceCount ?? 0, materials: mesh?.materialSlotIds?.length ?? 0 };
}).sort((left, right) => left.name.localeCompare(right.name)),
};
}
const sourceEngine = await factory({ wasmBinary });
const sourceHandle = sourceEngine._web_engine_create();
open(sourceEngine, sourceHandle);
const source = sourceSummary(readSnapshot(sourceEngine, sourceHandle));
sourceEngine._web_engine_destroy(sourceHandle);
const collapse = {};
for (const ratio of [1, 0.9, 0.8, 0.75, 0.7, 0.65, 0.5, 0.25]) {
const engine = await factory({ wasmBinary });
const handle = engine._web_engine_create();
open(engine, handle);
const snapshot = readSnapshot(engine, handle);
const mesh = snapshot.meshes.find((candidate) => candidate.id === "mesh:Cube.001");
apply(engine, handle, { type: "decimateMesh", meshId: mesh.id, profile: { schemaVersion: 1, sourceMeshRevision: snapshot.revision, attributePolicy: "PRESERVE", mode: "COLLAPSE", ratio, triangulate: false, useSymmetry: false } });
const output = readSnapshot(engine, handle).meshes.find((candidate) => candidate.id === mesh.id);
collapse[String(ratio)] = { vertices: output.vertexCount, triangles: output.triangleCount ?? Math.floor((output.indices?.length ?? 0) / 3) };
engine._web_engine_destroy(handle);
}
const actual = { fixture: expected.fixture, source, collapse };
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
throw new Error(`Blender golden mismatch\nexpected=${JSON.stringify(expected)}\nactual=${JSON.stringify(actual)}`);
}
console.log(`blender-golden-ok fixture=${expected.fixture} ratios=${Object.keys(collapse).join(",")}`);