Fix editor regressions and add Blender scene and animated material import

This commit is contained in:
emil28092005
2026-09-12 00:53:20 +03:00
parent 1a0bf725fa
commit 6f497d1977
34 changed files with 1241 additions and 213 deletions
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env node
import { spawn } from 'node:child_process';
import { access } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const args = process.argv.slice(2);
if (!args.length || args.includes('--help')) {
console.log(`Usage: npm run blender:export -- INPUT.blend --output OUTPUT.glb [options]
--blender PATH Blender binary (or FORMA_BLENDER; default: blender)
--scene NAME Scene to export
--objects GLOB Export matching objects; repeat for several patterns
--bake-materials Bake procedural/animated surface inputs to UV atlases
--resolution 128 Pixels per baked frame, 161024
--samples 8 Cycles samples per bake, 1256
--lighting compat Unitless lights for Forma; spec preserves physical units
--start-frame N --end-frame N --frame-step N
--allow-lossy Accept approximations listed in OUTPUT.report.json
See docs/BLENDER.md for supported features and limitations.`);
process.exit(args.length ? 0 : 1);
}
const input = path.resolve(args.shift());
if (!/\.blend$/i.test(input)) throw Error('Input must be a .blend file');
await access(input);
const binaryFlag = args.indexOf('--blender');
let binary = process.env.FORMA_BLENDER || 'blender';
if (binaryFlag >= 0) {
if (!args[binaryFlag + 1]) throw Error('--blender requires a path');
binary = args[binaryFlag + 1]; args.splice(binaryFlag, 2);
}
const outputFlag = args.indexOf('--output');
if (outputFlag < 0 || !args[outputFlag + 1] || !/\.glb$/i.test(args[outputFlag + 1])) throw Error('--output OUTPUT.glb is required');
const output = path.resolve(args[outputFlag + 1]);
if (output === input) throw Error('Output must differ from input');
args[outputFlag + 1] = output;
const script = fileURLToPath(new URL('../blender/export_forma.py', import.meta.url));
const child = spawn(binary, ['--background', '--factory-startup', '--disable-autoexec', input, '--python-exit-code', '1', '--python', script, '--', ...args], { stdio: 'inherit', shell: false });
child.on('error', error => { console.error(`Cannot start Blender: ${error.message}. Set FORMA_BLENDER or --blender.`); process.exitCode = 1; });
child.on('exit', (code, signal) => { process.exitCode = code ?? (signal ? 1 : 0); });
process.once('SIGINT', () => child.kill('SIGINT'));
process.once('SIGTERM', () => child.kill('SIGTERM'));
+1 -1
View File
@@ -22,5 +22,5 @@ for (const [sourceDir, targetDir] of [['hosted-editor', 'studio'], ['engine', 'e
}
await cp(path.join(engine, 'public/favicon.svg'), path.join(out, 'favicon.svg'));
const title = project.name.replace(/[<>&"']/g, '');
await writeFile(path.join(out, 'index.html'), `<!doctype html><html lang="ru"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"><title>Forma — ${title}</title><meta name="description" content="Редактор Forma с открытым проектом. Редактируй сцену, модели и скрипты; запускай игру и сохраняй проект."><link rel="icon" href="/favicon.svg"><link rel="stylesheet" href="/studio/editor.css"></head><body><div id="root"><p style="padding:32px;font:16px system-ui">Открываю проект Forma…</p></div><script type="module" src="/studio/editor.js"></script></body></html>`);
await writeFile(path.join(out, 'index.html'), `<!doctype html><html lang="ru"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"><title>Forma — ${title}</title><meta name="description" content="Редактор Forma с открытым проектом. Редактируй сцену, модели и скрипты; запускай игру и сохраняй проект."><link rel="icon" href="./favicon.svg"><link rel="stylesheet" href="./studio/editor.css"></head><body><div id="root"><p style="padding:32px;font:16px system-ui">Открываю проект Forma…</p></div><script type="module" src="./studio/editor.js"></script></body></html>`);
console.log(`Browser editor exported to ${out}; project: ${project.name}, revision ${project.revision}.`);