chore: checkpoint web Blender migration source, tools and docs
Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer, web workflow engine and protocol, tooling, docs and task records. Large generated evidence, user-testing archives, build snapshots and imported test assets remain untracked. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
91
my-tools/diagnose_local_object_editor.mjs
Normal file
91
my-tools/diagnose_local_object_editor.mjs
Normal file
@@ -0,0 +1,91 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import createEngine from '../build_web_blender6/bin/web_engine.js';
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const stages = [];
|
||||
const begin = performance.now();
|
||||
const engine = await createEngine({ wasmBinary: fs.readFileSync(path.join(root, 'build_web_blender6/bin/web_engine.wasm')) });
|
||||
stages.push({ stage: 'instantiate', seconds: (performance.now() - begin) / 1000 });
|
||||
const handle = engine._web_engine_create();
|
||||
const pointers = engine._malloc(8);
|
||||
const call = (stage, data) => {
|
||||
const started = performance.now();
|
||||
const pointer = data ? engine._malloc(data.length) : 0;
|
||||
try {
|
||||
if (data) engine.HEAPU8.set(data, pointer);
|
||||
const code = stage === 'open'
|
||||
? engine._web_engine_open_blend(handle, pointer, data.length)
|
||||
: stage === 'snapshot'
|
||||
? engine._web_engine_get_scene_snapshot(handle, pointers, pointers + 4)
|
||||
: engine._web_engine_call_json(handle, pointer, data.length, pointers, pointers + 4);
|
||||
if (code !== 0) throw new Error(engine.UTF8ToString(engine._web_engine_last_error_message(handle)));
|
||||
if (stage === 'open') return null;
|
||||
const output = engine.HEAPU32[pointers / 4];
|
||||
const length = engine.HEAPU32[pointers / 4 + 1];
|
||||
return JSON.parse(new TextDecoder().decode(engine.HEAPU8.slice(output, output + length)));
|
||||
} finally {
|
||||
if (pointer) engine._free(pointer);
|
||||
stages.push({ stage, seconds: (performance.now() - started) / 1000 });
|
||||
}
|
||||
};
|
||||
const edit = (command, expected = true) => {
|
||||
const result = call(command.type, Buffer.from(JSON.stringify({ cmd: 'applyCommand', params: { command } })));
|
||||
assert.equal(result.ok, expected, result.error ?? command.type);
|
||||
};
|
||||
try {
|
||||
const hierarchy = process.argv.includes('--hierarchy');
|
||||
call('open', fs.readFileSync(path.join(root, 'tests/files/web',
|
||||
hierarchy ? 'object_editor_visibility_scene.blend' : 'object_editor_scene.blend')));
|
||||
const initial = call('snapshot');
|
||||
const cube = initial.nodes.find(node => node.name === 'BasicCube');
|
||||
const collection = initial.collections.find(collection => collection.name === 'Collection');
|
||||
assert(cube && collection);
|
||||
edit({ type: 'clearObjectTransform', objectId: cube.id, channels: ['LOCATION', 'ROTATION', 'SCALE'] });
|
||||
const cleared = call('snapshot').nodes.find(node => node.id === cube.id);
|
||||
if (process.argv.includes('--inspect')) console.log(JSON.stringify({ transform: cleared.transform }));
|
||||
assert.deepEqual(cleared.transform.translation, [0, 0, 0]);
|
||||
assert.deepEqual(cleared.transform.rotationEuler, [0, 0, 0]);
|
||||
assert.deepEqual(cleared.transform.scale, [1, 1, 1]);
|
||||
for (const objectType of ['CAMERA', 'LIGHT', 'EMPTY', 'ARMATURE']) {
|
||||
const name = `Object Editor ${objectType}`;
|
||||
edit({ type: 'createObjectData', objectType, name });
|
||||
assert.equal(call('snapshot').nodes.find(node => node.name === name)?.type, objectType);
|
||||
}
|
||||
edit({ type: 'setObjectSelection', objectId: cube.id, selected: true, active: true });
|
||||
assert.equal(call('snapshot').activeObjectId, cube.id);
|
||||
edit({ type: 'setCollectionVisibility', collectionId: collection.id, viewportVisible: false, renderVisible: false });
|
||||
const hidden = call('snapshot');
|
||||
assert.equal(hidden.nodes.find(node => node.id === cube.id)?.visible, false,
|
||||
'hidden collection must hide its objects in the Web snapshot');
|
||||
if (hierarchy) {
|
||||
assert.equal(hidden.nodes.find(node => node.name === 'BasicCamera')?.visible, false);
|
||||
assert.equal(hidden.nodes.find(node => node.name === 'BasicArea')?.visible, true);
|
||||
assert.equal(hidden.nodes.find(node => node.name === 'ObjectEditor Hidden')?.visible, false);
|
||||
}
|
||||
edit({ type: 'setCollectionVisibility', collectionId: collection.id, viewportVisible: true, renderVisible: true });
|
||||
const shown = call('snapshot');
|
||||
assert.equal(shown.nodes.find(node => node.id === cube.id)?.visible, true);
|
||||
if (hierarchy) {
|
||||
assert.equal(shown.nodes.find(node => node.name === 'BasicCamera')?.visible, true);
|
||||
assert.equal(shown.nodes.find(node => node.name === 'BasicArea')?.visible, true);
|
||||
assert.equal(shown.nodes.find(node => node.name === 'ObjectEditor Hidden')?.visible, false);
|
||||
}
|
||||
for (const command of [
|
||||
{ type: 'clearObjectTransform', objectId: 'object:Missing', channels: ['LOCATION'] },
|
||||
{ type: 'createObjectData', objectType: 'UNSUPPORTED', name: 'Invalid' },
|
||||
{ type: 'setObjectSelection', objectId: 'object:Missing', selected: true, active: true },
|
||||
{ type: 'setCollectionVisibility', collectionId: 'collection:Missing', viewportVisible: false },
|
||||
]) edit(command, false);
|
||||
assert.equal(call('snapshot').nodes.length, initial.nodes.length + 4);
|
||||
console.log(JSON.stringify({ status: 'PASS', stages }));
|
||||
} catch (error) {
|
||||
console.log(JSON.stringify({ status: 'FAIL', error: error.message,
|
||||
frames: String(error.stack).split('\n').filter(line => /wasm-function|wasm:/.test(line)).slice(0, 10), stages }));
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
engine._free(pointers);
|
||||
engine._web_engine_destroy(handle);
|
||||
}
|
||||
Reference in New Issue
Block a user