81 lines
3.8 KiB
JavaScript
81 lines
3.8 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import factory from "../../web/app/src/vendor/blender/web_engine.js";
|
|
|
|
const root = new URL("../../", import.meta.url);
|
|
const golden = JSON.parse(fs.readFileSync(new URL("tests/golden/W-080/animation-depsgraph.json", root), "utf8"));
|
|
const blend = fs.readFileSync(new URL(`tests/files/web/${golden.fixture}`, root));
|
|
const wasmBinary = fs.readFileSync(new URL("web/app/src/vendor/blender/web_engine.wasm", root));
|
|
const tolerance = golden.tolerance;
|
|
|
|
function readJson(engine, dataOut, lengthOut) {
|
|
const pointer = engine.HEAPU32[dataOut >>> 2];
|
|
const length = engine.HEAPU32[lengthOut >>> 2];
|
|
assert.ok(pointer && length, "native response is empty");
|
|
return JSON.parse(new TextDecoder().decode(engine.HEAPU8.slice(pointer, pointer + length)));
|
|
}
|
|
|
|
function sendCommand(engine, handle, command) {
|
|
const encoded = new TextEncoder().encode(JSON.stringify(command));
|
|
const pointer = engine._malloc(encoded.byteLength);
|
|
try {
|
|
engine.HEAPU8.set(encoded, pointer);
|
|
assert.equal(engine._web_engine_apply_command(handle, pointer, encoded.byteLength), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
}
|
|
finally {
|
|
engine._free(pointer);
|
|
}
|
|
}
|
|
|
|
function evaluate(engine, handle) {
|
|
const dataOut = engine._malloc(4);
|
|
const lengthOut = engine._malloc(4);
|
|
try {
|
|
assert.equal(engine._web_engine_evaluate_depsgraph(handle, dataOut, lengthOut), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
return readJson(engine, dataOut, lengthOut);
|
|
}
|
|
finally {
|
|
engine._free(dataOut);
|
|
engine._free(lengthOut);
|
|
}
|
|
}
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
assert.ok(handle > 0, "WebEngine handle creation failed");
|
|
try {
|
|
const input = engine._malloc(blend.byteLength);
|
|
try {
|
|
engine.HEAPU8.set(blend, input);
|
|
assert.equal(engine._web_engine_open_blend(handle, input, blend.byteLength), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
}
|
|
finally {
|
|
engine._free(input);
|
|
}
|
|
|
|
for (const expected of golden.frames) {
|
|
sendCommand(engine, handle, { type: "setFrame", frame: expected.frame });
|
|
const report = evaluate(engine, handle);
|
|
assert.equal(report.status, "EVALUATED");
|
|
assert.equal(report.frame, expected.frame);
|
|
const mesh = report.meshes.find((candidate) => candidate.sourceMeshId === `mesh:${golden.mesh}`);
|
|
assert.ok(mesh, `mesh ${golden.mesh} is missing at frame ${expected.frame}`);
|
|
assert.equal(mesh.vertexCount, expected.positions.length / 3);
|
|
assert.equal(mesh.positions.length, expected.positions.length);
|
|
const positionErrors = mesh.positions.map((value, index) => value - expected.positions[index]);
|
|
const maxPositionError = Math.max(0, ...positionErrors.map((value) => Math.abs(value)));
|
|
const rmsPositionError = Math.sqrt(positionErrors.reduce((sum, value) => sum + value * value, 0) / positionErrors.length);
|
|
assert.ok(maxPositionError <= tolerance.maxPositionError, `frame ${expected.frame} max position error ${maxPositionError}`);
|
|
assert.ok(rmsPositionError <= tolerance.rmsPositionError, `frame ${expected.frame} RMS position error ${rmsPositionError}`);
|
|
const matrixErrors = mesh.worldMatrix.map((value, index) => value - expected.worldMatrix[index]);
|
|
const maxMatrixError = Math.max(0, ...matrixErrors.map((value) => Math.abs(value)));
|
|
if (maxMatrixError > tolerance.maxMatrixError) console.error("debug-world", expected.frame, mesh.worldMatrix);
|
|
assert.ok(maxMatrixError <= tolerance.maxMatrixError, `frame ${expected.frame} world matrix error ${maxMatrixError}`);
|
|
}
|
|
}
|
|
finally {
|
|
engine._web_engine_destroy(handle);
|
|
}
|
|
|
|
process.stdout.write(`frame-evaluation-ok fixture=${golden.fixture} frames=${golden.frames.map((frame) => frame.frame).join(",")}\n`);
|