Advance M8-M11 parity workflows
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

This commit is contained in:
mes123456
2026-08-17 04:37:07 -04:00
parent 7c16b279ae
commit 0fe8d2bb56
324 changed files with 31920 additions and 863 deletions

View File

@@ -0,0 +1,119 @@
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/M9-12/weight-paint.json", root), "utf8"));
const fixture = 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));
function call(engine, handle, name, payload) {
const bytes = new TextEncoder().encode(JSON.stringify(payload));
const pointer = engine._malloc(bytes.byteLength);
try {
engine.HEAPU8.set(bytes, pointer);
assert.equal(engine._web_engine_apply_command(handle, pointer, bytes.byteLength), 0,
`${name}: ${engine.UTF8ToString(engine._web_engine_last_error_message())}`);
}
finally {
engine._free(pointer);
}
}
function open(engine, handle, bytes) {
const pointer = engine._malloc(bytes.byteLength);
try {
engine.HEAPU8.set(bytes, pointer);
assert.equal(engine._web_engine_open_blend(handle, pointer, bytes.byteLength), 0,
engine.UTF8ToString(engine._web_engine_last_error_message()));
}
finally {
engine._free(pointer);
}
}
function snapshot(engine, handle) {
const dataOut = engine._malloc(4);
const lengthOut = engine._malloc(4);
try {
assert.equal(engine._web_engine_get_scene_snapshot(handle, dataOut, lengthOut), 0);
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 save(engine, handle) {
const dataOut = engine._malloc(4);
const lengthOut = engine._malloc(4);
try {
assert.equal(engine._web_engine_save_blend(handle, dataOut, lengthOut), 0);
const pointer = engine.HEAPU32[dataOut >>> 2];
const length = engine.HEAPU32[lengthOut >>> 2];
const bytes = engine.HEAPU8.slice(pointer, pointer + length);
engine._web_engine_free_buffer(pointer);
return bytes;
}
finally {
engine._free(dataOut);
engine._free(lengthOut);
}
}
function weightsByVertex(snapshotValue) {
const mesh = snapshotValue.meshes.find((item) => item.id === `mesh:${golden.mesh}`);
assert.ok(mesh?.skinWeights, "weight golden mesh has no skinWeights");
return golden.steps[0].vertices.map((expectedVertex) => {
const result = {};
for (const [groupIndex, groupName] of mesh.skinWeights.boneNames.entries()) {
const offset = expectedVertex.index * 4;
const slot = mesh.skinWeights.indices.slice(offset, offset + 4).findIndex((value) => value === groupIndex);
result[groupName] = slot < 0 ? 0 : mesh.skinWeights.weights[offset + slot];
}
return result;
});
}
function compareStep(actualSnapshot, expected, label) {
const actual = weightsByVertex(actualSnapshot);
assert.deepEqual(actual.map((weights) => Object.keys(weights).sort()), expected.vertices.map(() => expected.groups.slice().sort()), `${label} group schema`);
expected.vertices.forEach((vertex, index) => {
for (const group of expected.groups) {
const error = Math.abs((actual[index][group] ?? 0) - (vertex.weights[group] ?? 0));
assert.ok(error <= golden.tolerance, `${label} vertex=${vertex.index} group=${group} error=${error}`);
}
});
}
const engine = await factory({ wasmBinary: wasmBinary.slice() });
const handle = engine._web_engine_create();
assert.ok(handle > 0);
try {
open(engine, handle, fixture);
call(engine, handle, "initial", { type: "setVertexWeights", objectId: `object:${golden.object}`, vertexGroup: "WebPaintGroup", indices: [0, 1], values: [0.75, 0.25] });
compareStep(snapshot(engine, handle), golden.steps[0], "initial");
call(engine, handle, "normalize", { type: "setVertexWeights", objectId: `object:${golden.object}`, vertexGroup: "WebPaintGroup", indices: [2], values: [0.5], normalize: true });
compareStep(snapshot(engine, handle), golden.steps[1], "normalize");
call(engine, handle, "limit-normalize", { type: "setVertexWeights", objectId: `object:${golden.object}`, vertexGroup: "WebPaintGroup", indices: [3], values: [0.4], limit: 2, normalize: true });
compareStep(snapshot(engine, handle), golden.steps[2], "limit-normalize");
call(engine, handle, "mirror", { type: "setVertexWeights", objectId: `object:${golden.object}`, vertexGroup: "WebPaintGroup", indices: [0], values: [0.9], mirror: true, mirrorAxis: 0, mirrorTolerance: 1e-4 });
compareStep(snapshot(engine, handle), golden.steps[3], "mirror");
const saved = save(engine, handle);
const reopened = engine._web_engine_create();
try {
open(engine, reopened, saved);
compareStep(snapshot(engine, reopened), golden.steps[3], "save-reopen");
}
finally {
engine._web_engine_destroy(reopened);
}
}
finally {
engine._web_engine_destroy(handle);
}
process.stdout.write(`weight-paint-golden-ok fixture=${golden.fixture} steps=${golden.steps.map((step) => step.name).join(",")} normalize=passed limit=passed mirror=passed save-reopen=passed\n`);