import assert from "node:assert/strict"; import crypto from "node:crypto"; import fs from "node:fs"; import factory from "../../web/app/src/vendor/blender/web_engine.js"; const root = new URL("../../", import.meta.url); const wasmBinary = fs.readFileSync(new URL("web/app/src/vendor/blender/web_engine.wasm", root)); const fixture = fs.readFileSync(new URL("tests/files/web/modifier_physics_scene.blend", root)); 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 output(engine, handle, fn, owned = false) { const dataOut = engine._malloc(4); const lengthOut = engine._malloc(4); try { assert.equal(fn(handle, dataOut, lengthOut), 0, engine.UTF8ToString(engine._web_engine_last_error_message())); const pointer = engine.HEAPU32[dataOut >>> 2]; const length = engine.HEAPU32[lengthOut >>> 2]; const bytes = engine.HEAPU8.slice(pointer, pointer + length); if (owned) engine._web_engine_free_buffer(pointer); return bytes; } finally { engine._free(dataOut); engine._free(lengthOut); } } function snapshot(engine, handle) { return JSON.parse(new TextDecoder().decode(output(engine, handle, engine._web_engine_get_scene_snapshot))); } function assertManifest(scene) { const manifest = scene.physicsSimulation; assert.equal(manifest?.schemaVersion, 1); assert.deepEqual(manifest.systems.map((system) => system.family).sort(), ["CLOTH", "SOFT_BODY"]); for (const system of manifest.systems) { assert.match(system.id, /^physics:/); assert.match(system.ownerObjectId, /^object:/); assert.deepEqual(system.settingsHash, crypto.createHash("sha256").update(JSON.stringify(system.settings)).digest("hex")); assert.equal(system.cache, undefined); } const collisionId = "object:PhysicsCollisionDisabled"; assert.ok(manifest.systems.every((system) => system.dependencyIds.includes(collisionId))); const cloth = manifest.systems.find((system) => system.family === "CLOTH"); const soft = manifest.systems.find((system) => system.family === "SOFT_BODY"); assert.equal(cloth.settings.name, "Cloth Disabled"); assert.equal(cloth.settings.enabled, false); assert.equal(soft.ownerObjectId, "object:PhysicsSoftBodyDisabled"); assert.equal(typeof soft.settings.nodeMass, "number"); } const engine = await factory({ wasmBinary: wasmBinary.slice() }); const handle = engine._web_engine_create(); open(engine, handle, fixture); assertManifest(snapshot(engine, handle)); const saved = output(engine, handle, engine._web_engine_save_blend, true); engine._web_engine_destroy(handle); const reopened = engine._web_engine_create(); open(engine, reopened, saved); assertManifest(snapshot(engine, reopened)); engine._web_engine_destroy(reopened); process.stdout.write("physics-main-reader-ok families=CLOTH,SOFT_BODY dependencies=COLLISION settings-sha256=passed save-reopen=passed playback=BLOCKED solver=BLOCKED\n");