@volter/blender-engine 0.1.147 → 0.1.149
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +16 -0
- package/README.md +4 -11
- package/browser/addons/vrm/LICENSE_(OPTION1)_MIT.txt +22 -0
- package/browser/addons/vrm/LICENSE_(OPTION2)_GPL-3_0.txt +674 -0
- package/browser/addons/vrm/LICENSE_MAIN.txt +1 -0
- package/browser/addons/vrm/README.txt +3 -0
- package/browser/addons/vrm/__init__.py +41 -0
- package/browser/addons/vrm/blender_manifest.toml +16 -0
- package/browser/addons/vrm/common/__init__.py +1 -0
- package/browser/addons/vrm/common/animation.py +58 -0
- package/browser/addons/vrm/common/blender_manifest.py +82 -0
- package/browser/addons/vrm/common/char.py +16 -0
- package/browser/addons/vrm/common/convert.py +287 -0
- package/browser/addons/vrm/common/convert_any.py +48 -0
- package/browser/addons/vrm/common/debug.py +40 -0
- package/browser/addons/vrm/common/deep.py +162 -0
- package/browser/addons/vrm/common/error_dialog.py +274 -0
- package/browser/addons/vrm/common/fs.py +71 -0
- package/browser/addons/vrm/common/gl.py +20 -0
- package/browser/addons/vrm/common/gltf.py +1149 -0
- package/browser/addons/vrm/common/human_bone_mapper/__init__.py +1 -0
- package/browser/addons/vrm/common/human_bone_mapper/biped_mapping.py +84 -0
- package/browser/addons/vrm/common/human_bone_mapper/cats_blender_plugin_fix_model_mapping.py +63 -0
- package/browser/addons/vrm/common/human_bone_mapper/human_bone_mapper.py +203 -0
- package/browser/addons/vrm/common/human_bone_mapper/microsoft_rocketbox_mapping.py +69 -0
- package/browser/addons/vrm/common/human_bone_mapper/mixamo_mapping.py +62 -0
- package/browser/addons/vrm/common/human_bone_mapper/mmd_mapping.py +115 -0
- package/browser/addons/vrm/common/human_bone_mapper/mpfb_mapping.py +120 -0
- package/browser/addons/vrm/common/human_bone_mapper/ready_player_me_mapping.py +65 -0
- package/browser/addons/vrm/common/human_bone_mapper/reallusion_mapping.py +94 -0
- package/browser/addons/vrm/common/human_bone_mapper/rigify_meta_rig_mapping.py +65 -0
- package/browser/addons/vrm/common/human_bone_mapper/structure_based_mapping.py +891 -0
- package/browser/addons/vrm/common/human_bone_mapper/unreal_mapping.py +62 -0
- package/browser/addons/vrm/common/human_bone_mapper/vrm_addon_mapping.py +112 -0
- package/browser/addons/vrm/common/human_bone_mapper/vroid_mapping.py +89 -0
- package/browser/addons/vrm/common/legacy_gltf.py +16 -0
- package/browser/addons/vrm/common/logger.py +84 -0
- package/browser/addons/vrm/common/mtoon0.blend +0 -0
- package/browser/addons/vrm/common/mtoon1.blend +0 -0
- package/browser/addons/vrm/common/mtoon1_outline.blend +0 -0
- package/browser/addons/vrm/common/mtoon_auto_setup.blend +0 -0
- package/browser/addons/vrm/common/mtoon_unversioned.py +62 -0
- package/browser/addons/vrm/common/ops/__init__.py +10 -0
- package/browser/addons/vrm/common/ops/export_scene.py +67 -0
- package/browser/addons/vrm/common/ops/icyp.py +53 -0
- package/browser/addons/vrm/common/ops/import_scene.py +57 -0
- package/browser/addons/vrm/common/ops/vrm.py +2380 -0
- package/browser/addons/vrm/common/ops/wm.py +167 -0
- package/browser/addons/vrm/common/preferences.py +362 -0
- package/browser/addons/vrm/common/progress.py +87 -0
- package/browser/addons/vrm/common/rotation.py +89 -0
- package/browser/addons/vrm/common/safe_removal.py +46 -0
- package/browser/addons/vrm/common/scene_watcher.py +230 -0
- package/browser/addons/vrm/common/shader.py +2243 -0
- package/browser/addons/vrm/common/shape_key_mapper/__init__.py +1 -0
- package/browser/addons/vrm/common/shape_key_mapper/arkit_mapping.py +168 -0
- package/browser/addons/vrm/common/shape_key_mapper/mmd_mapping.py +62 -0
- package/browser/addons/vrm/common/shape_key_mapper/ready_player_me_mapping.py +52 -0
- package/browser/addons/vrm/common/shape_key_mapper/vrchat_mapping.py +17 -0
- package/browser/addons/vrm/common/third_party_user_extension.py +214 -0
- package/browser/addons/vrm/common/transparent_z_write.blend +0 -0
- package/browser/addons/vrm/common/version.py +161 -0
- package/browser/addons/vrm/common/vrm0/__init__.py +1 -0
- package/browser/addons/vrm/common/vrm0/human_bone.py +662 -0
- package/browser/addons/vrm/common/vrm0/material_property.py +346 -0
- package/browser/addons/vrm/common/vrm1/__init__.py +1 -0
- package/browser/addons/vrm/common/vrm1/expression_preset.py +207 -0
- package/browser/addons/vrm/common/vrm1/human_bone.py +822 -0
- package/browser/addons/vrm/common/workspace.py +227 -0
- package/browser/addons/vrm/common/writable_context.py +77 -0
- package/browser/addons/vrm/documentation/en-us/animation/index.md +63 -0
- package/browser/addons/vrm/documentation/en-us/create-humanoid-vrm-from-scratch/index.md +131 -0
- package/browser/addons/vrm/documentation/en-us/create-simple-vrm-from-scratch/index.md +30 -0
- package/browser/addons/vrm/documentation/en-us/development/index.md +136 -0
- package/browser/addons/vrm/documentation/en-us/index.md +41 -0
- package/browser/addons/vrm/documentation/en-us/installation/index.md +152 -0
- package/browser/addons/vrm/documentation/en-us/material-mtoon/index.md +122 -0
- package/browser/addons/vrm/documentation/en-us/material-pbr/index.md +94 -0
- package/browser/addons/vrm/documentation/en-us/scripting-api/index.md +391 -0
- package/browser/addons/vrm/documentation/en-us/ui/export_scene.vrm/index.md +57 -0
- package/browser/addons/vrm/documentation/en-us/ui/export_scene.vrma/index.md +17 -0
- package/browser/addons/vrm/documentation/en-us/ui/import_scene.vrm/index.md +61 -0
- package/browser/addons/vrm/documentation/en-us/ui/import_scene.vrma/index.md +13 -0
- package/browser/addons/vrm/documentation/ja-jp/animation/index.md +57 -0
- package/browser/addons/vrm/documentation/ja-jp/create-humanoid-vrm-from-scratch/index.md +129 -0
- package/browser/addons/vrm/documentation/ja-jp/create-simple-vrm-from-scratch/index.md +29 -0
- package/browser/addons/vrm/documentation/ja-jp/development/index.md +137 -0
- package/browser/addons/vrm/documentation/ja-jp/index.md +37 -0
- package/browser/addons/vrm/documentation/ja-jp/installation/index.md +150 -0
- package/browser/addons/vrm/documentation/ja-jp/material-mtoon/index.md +113 -0
- package/browser/addons/vrm/documentation/ja-jp/material-pbr/index.md +90 -0
- package/browser/addons/vrm/documentation/ja-jp/scripting-api/index.md +389 -0
- package/browser/addons/vrm/documentation/ja-jp/ui/export_scene.vrm/index.md +55 -0
- package/browser/addons/vrm/documentation/ja-jp/ui/export_scene.vrma/index.md +19 -0
- package/browser/addons/vrm/documentation/ja-jp/ui/import_scene.vrm/index.md +56 -0
- package/browser/addons/vrm/documentation/ja-jp/ui/import_scene.vrma/index.md +14 -0
- package/browser/addons/vrm/editor/__init__.py +1 -0
- package/browser/addons/vrm/editor/extension.py +425 -0
- package/browser/addons/vrm/editor/extension_accessor.py +75 -0
- package/browser/addons/vrm/editor/handler.py +84 -0
- package/browser/addons/vrm/editor/khr_character/__init__.py +1 -0
- package/browser/addons/vrm/editor/khr_character/panel.py +69 -0
- package/browser/addons/vrm/editor/khr_character/property_group.py +27 -0
- package/browser/addons/vrm/editor/khr_xmp_json_ld/__init__.py +1 -0
- package/browser/addons/vrm/editor/khr_xmp_json_ld/ops.py +464 -0
- package/browser/addons/vrm/editor/khr_xmp_json_ld/panel.py +136 -0
- package/browser/addons/vrm/editor/khr_xmp_json_ld/property_group.py +83 -0
- package/browser/addons/vrm/editor/khr_xmp_json_ld/ui_list.py +198 -0
- package/browser/addons/vrm/editor/make_armature.py +709 -0
- package/browser/addons/vrm/editor/menu.py +147 -0
- package/browser/addons/vrm/editor/migration.py +297 -0
- package/browser/addons/vrm/editor/mtoon1/__init__.py +1 -0
- package/browser/addons/vrm/editor/mtoon1/handler.py +20 -0
- package/browser/addons/vrm/editor/mtoon1/migration.py +536 -0
- package/browser/addons/vrm/editor/mtoon1/ops.py +304 -0
- package/browser/addons/vrm/editor/mtoon1/panel.py +442 -0
- package/browser/addons/vrm/editor/mtoon1/property_group.py +4977 -0
- package/browser/addons/vrm/editor/mtoon1/scene_watcher.py +381 -0
- package/browser/addons/vrm/editor/node_constraint1/__init__.py +1 -0
- package/browser/addons/vrm/editor/node_constraint1/panel.py +317 -0
- package/browser/addons/vrm/editor/node_constraint1/property_group.py +25 -0
- package/browser/addons/vrm/editor/ops.py +471 -0
- package/browser/addons/vrm/editor/panel.py +315 -0
- package/browser/addons/vrm/editor/property_group.py +888 -0
- package/browser/addons/vrm/editor/search.py +682 -0
- package/browser/addons/vrm/editor/spring_bone1/__init__.py +1 -0
- package/browser/addons/vrm/editor/spring_bone1/handler.py +994 -0
- package/browser/addons/vrm/editor/spring_bone1/menu.py +246 -0
- package/browser/addons/vrm/editor/spring_bone1/migration.py +86 -0
- package/browser/addons/vrm/editor/spring_bone1/ops.py +1679 -0
- package/browser/addons/vrm/editor/spring_bone1/panel.py +605 -0
- package/browser/addons/vrm/editor/spring_bone1/property_group.py +1457 -0
- package/browser/addons/vrm/editor/spring_bone1/ui_list.py +408 -0
- package/browser/addons/vrm/editor/subscription.py +123 -0
- package/browser/addons/vrm/editor/t_pose.py +713 -0
- package/browser/addons/vrm/editor/validation.py +1271 -0
- package/browser/addons/vrm/editor/vrm0/__init__.py +1 -0
- package/browser/addons/vrm/editor/vrm0/gizmo_group.py +63 -0
- package/browser/addons/vrm/editor/vrm0/handler.py +23 -0
- package/browser/addons/vrm/editor/vrm0/menu.py +145 -0
- package/browser/addons/vrm/editor/vrm0/migration.py +761 -0
- package/browser/addons/vrm/editor/vrm0/ops.py +2145 -0
- package/browser/addons/vrm/editor/vrm0/panel.py +1333 -0
- package/browser/addons/vrm/editor/vrm0/property_group.py +1818 -0
- package/browser/addons/vrm/editor/vrm0/ui_list.py +688 -0
- package/browser/addons/vrm/editor/vrm1/__init__.py +1 -0
- package/browser/addons/vrm/editor/vrm1/handler.py +28 -0
- package/browser/addons/vrm/editor/vrm1/menu.py +76 -0
- package/browser/addons/vrm/editor/vrm1/migration.py +321 -0
- package/browser/addons/vrm/editor/vrm1/ops.py +2679 -0
- package/browser/addons/vrm/editor/vrm1/panel.py +1243 -0
- package/browser/addons/vrm/editor/vrm1/property_group.py +2087 -0
- package/browser/addons/vrm/editor/vrm1/scene_watcher.py +90 -0
- package/browser/addons/vrm/editor/vrm1/ui_list.py +533 -0
- package/browser/addons/vrm/exporter/__init__.py +1 -0
- package/browser/addons/vrm/exporter/abstract_base_vrm_exporter.py +628 -0
- package/browser/addons/vrm/exporter/export_scene.py +930 -0
- package/browser/addons/vrm/exporter/gltf2_export_user_extension.py +45 -0
- package/browser/addons/vrm/exporter/khr_character_exporter.py +34 -0
- package/browser/addons/vrm/exporter/vrm0_exporter.py +4020 -0
- package/browser/addons/vrm/exporter/vrm1_exporter.py +3682 -0
- package/browser/addons/vrm/exporter/vrm_animation_exporter.py +1035 -0
- package/browser/addons/vrm/external/__init__.py +1 -0
- package/browser/addons/vrm/external/auto_rig_pro_support.py +3 -0
- package/browser/addons/vrm/external/io_scene_gltf2_support.py +418 -0
- package/browser/addons/vrm/external/rigify_support.py +1 -0
- package/browser/addons/vrm/importer/__init__.py +1 -0
- package/browser/addons/vrm/importer/abstract_base_vrm_importer.py +1548 -0
- package/browser/addons/vrm/importer/file_handler.py +34 -0
- package/browser/addons/vrm/importer/gltf2_import_user_extension.py +268 -0
- package/browser/addons/vrm/importer/import_scene.py +646 -0
- package/browser/addons/vrm/importer/license_validation.py +198 -0
- package/browser/addons/vrm/importer/vrm0_importer.py +1723 -0
- package/browser/addons/vrm/importer/vrm1_importer.py +2014 -0
- package/browser/addons/vrm/importer/vrm_animation_importer.py +763 -0
- package/browser/addons/vrm/importer/vrm_diff.py +316 -0
- package/browser/addons/vrm/locale/__init__.py +1 -0
- package/browser/addons/vrm/locale/ja_jp.py +826 -0
- package/browser/addons/vrm/locale/ruff.toml +8 -0
- package/browser/addons/vrm/locale/translation_dictionary.py +10 -0
- package/browser/addons/vrm/locale/zh_hans.py +928 -0
- package/browser/addons/vrm/registration.py +670 -0
- package/browser/addons/vrm.json +18 -0
- package/browser/blender-emscripten-engine.mts +60 -128
- package/browser/blender-engine.mts +20 -186
- package/browser/blender-wali-engine.mts +35 -81
- package/browser/describe-thrown.mts +3 -6
- package/browser/document-chunks.mts +4 -16
- package/browser/gltf_export.py +3161 -0
- package/browser/heap-growth.mts +23 -0
- package/browser/index.ts +2 -0
- package/browser/protocol.ts +119 -35
- package/browser/pull-job.mts +2 -10
- package/browser/rna.ts +190 -73
- package/browser/runtime.ts +197 -82
- package/browser/session-addons.mts +33 -0
- package/browser/session-frame.mts +5 -24
- package/browser/session.py +1338 -1427
- package/browser/three/attach-presenter.ts +11 -39
- package/browser/three/blender-base64.ts +1 -36
- package/browser/three/blender-corner-normals.ts +1 -23
- package/browser/three/blender-display-shaders.json +1 -1
- package/browser/three/blender-display-shaders.lut +0 -0
- package/browser/three/blender-display-transform.ts +2 -1
- package/browser/three/blender-frame-columns.ts +5 -13
- package/browser/three/blender-graph-material.ts +18 -3
- package/browser/three/blender-node-graph.ts +10 -34
- package/browser/three/blender-physical-material.ts +2 -31
- package/browser/three/blender-runtime-armature.ts +7 -60
- package/browser/three/blender-runtime-frame.ts +9 -23
- package/browser/three/blender-runtime-geometry.ts +22 -26
- package/browser/three/blender-runtime-lighting.ts +48 -178
- package/browser/three/blender-runtime-shadows.ts +187 -1
- package/browser/three/blender-runtime-skeleton.ts +53 -1
- package/browser/three/blender-runtime-view.ts +505 -283
- package/browser/three/blender-runtime-weights.ts +9 -72
- package/browser/three/blender-sky.ts +13 -33
- package/browser/three/blender-triangulate.ts +7 -35
- package/browser/three/blender-viewport-sampling.ts +235 -0
- package/browser/three/blender-workbench-material.ts +25 -29
- package/browser/three/blender-workbench-outline.ts +199 -0
- package/browser/three/blender-world-volume.ts +62 -10
- package/browser/three/presenter.ts +10 -26
- package/browser/three/release.ts +5 -21
- package/browser/three/sky-precompute-worker.ts +1 -18
- package/browser/three/sky-worker.ts +5 -23
- package/browser/three/world-field-sampler.ts +12 -26
- package/browser/volter_three.py +9 -105
- package/browser/worker.ts +156 -151
- package/package.json +5 -2
- package/wasm/BUNDLE.json +17 -17
- package/wasm/DEPENDENCY-LICENSES.txt +14 -0
- package/wasm/blender_browser.data.br +0 -0
- package/wasm/blender_browser.js +1 -1
- package/wasm/blender_browser.wasm.br +0 -0
- package/browser/frame-transfer.mts +0 -19
- package/browser/three/agx-base-srgb.lut +0 -0
- package/browser/three/agx-look-medium-high-contrast.lut +0 -0
- package/browser/three/agx-look-punchy.lut +0 -0
- package/browser/three/blender-agx.ts +0 -235
- package/browser/three/blender-display-lut.ts +0 -145
- package/browser/three/blender-filmic.ts +0 -49
- package/browser/three/blender-standard.ts +0 -68
- package/browser/three/blender-world-visibility.ts +0 -278
- package/browser/three/filmic-srgb.lut +0 -0
|
@@ -56,6 +56,9 @@ import {
|
|
|
56
56
|
sleep,
|
|
57
57
|
} from './blender-engine.mts';
|
|
58
58
|
import sessionPython from './session.py?raw';
|
|
59
|
+
// Imported by session.py’s gltf-export operation from the same directory.
|
|
60
|
+
import gltfExportPython from './gltf_export.py?raw';
|
|
61
|
+
import { sessionAddonFiles } from './session-addons.mts';
|
|
59
62
|
|
|
60
63
|
/** Blender's own resource root inside the program, spelled the same way the
|
|
61
64
|
* standalone skew spells it so `BLENDER_SYSTEM_*` reads identically. */
|
|
@@ -81,17 +84,22 @@ interface BrowserFileSystemLike {
|
|
|
81
84
|
on?(event: 'change' | 'delete', listener: (path: string) => void): unknown;
|
|
82
85
|
}
|
|
83
86
|
|
|
87
|
+
/** A write that replaces whatever is at `path` (`BlenderFiles.writeFile`). */
|
|
88
|
+
function replaceFileSync(filesystem: BrowserFileSystemLike, path: string, data: string | Uint8Array): void {
|
|
89
|
+
if (filesystem.existsSync(path)) filesystem.unlinkSync(path);
|
|
90
|
+
filesystem.writeFileSync(path, data);
|
|
91
|
+
}
|
|
92
|
+
|
|
84
93
|
interface WaliProgram {
|
|
85
94
|
run(
|
|
86
95
|
argv: readonly string[],
|
|
87
96
|
options: {
|
|
88
97
|
onStdout?(chunk: string): void;
|
|
89
98
|
onStderr?(chunk: string): void;
|
|
90
|
-
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
|
|
94
|
-
* that types that call. */
|
|
99
|
+
/**
|
|
100
|
+
* The WALI runtime’s diagnostic callback, separate from the program’s output. Reports an
|
|
101
|
+
* unimplemented syscall answered with -ENOSYS.
|
|
102
|
+
*/
|
|
95
103
|
onDiagnostic?(message: string): void;
|
|
96
104
|
},
|
|
97
105
|
): Promise<{ exitCode: number; stdout: string; stderr: string }>;
|
|
@@ -118,15 +126,8 @@ async function substrate(module: string): Promise<Record<string, unknown>> {
|
|
|
118
126
|
}
|
|
119
127
|
|
|
120
128
|
/**
|
|
121
|
-
* Blender
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* ONE INDEX AND ONE BLOB, not 2,276 requests. The tree is 78 MB in ~2,300
|
|
125
|
-
* files and every one of them has to exist before Blender's first act, so the
|
|
126
|
-
* editor serves a JSON index and the bytes concatenated in its order
|
|
127
|
-
* (`blender-wasm-artifact.ts`); this walks the index and slices. The
|
|
128
|
-
* standalone skew's equivalent is the packed `.data` payload, and this is the
|
|
129
|
-
* same shape for a filesystem that is not a module's.
|
|
129
|
+
* Install Blender’s scripts, datafiles and Python into the program filesystem from one index and
|
|
130
|
+
* concatenated blob. The Emscripten build uses its packed .data payload for the same content.
|
|
130
131
|
*/
|
|
131
132
|
async function stageRuntime(
|
|
132
133
|
filesystem: BrowserFileSystemLike,
|
|
@@ -149,20 +150,14 @@ async function stageRuntime(
|
|
|
149
150
|
`Blender runtime ${entry.path}: bytes ${offset}..${offset + entry.size} lie outside ` +
|
|
150
151
|
`the ${blob.byteLength}-byte runtime blob`,
|
|
151
152
|
);
|
|
152
|
-
//
|
|
153
|
-
// `blender -b` with no session: at startup it asks a presenter at its own
|
|
154
|
-
// directory and waits two seconds for one. This session registers the same
|
|
155
|
-
// override itself (`session.py`, `VolterRenderEngine`) and answers through
|
|
156
|
-
// its own channel, so here the module could only wait out those two
|
|
157
|
-
// seconds (measured 2026-09-27: every boot printed "no presenter answered
|
|
158
|
-
// at /tmp/vgai-presenter within 2 s").
|
|
153
|
+
// Skip the pack’s standalone render override: session.py registers VolterRenderEngine and serves its own presenter channel.
|
|
159
154
|
if (PACK_RENDER_OVERRIDES.has(entry.path)) {
|
|
160
155
|
offset += entry.size;
|
|
161
156
|
continue;
|
|
162
157
|
}
|
|
163
158
|
const path = `${RESOURCES}/${entry.path}`;
|
|
164
159
|
filesystem.mkdirSync(path.slice(0, path.lastIndexOf('/')), { recursive: true });
|
|
165
|
-
filesystem
|
|
160
|
+
replaceFileSync(filesystem, path, blob.subarray(offset, offset + entry.size));
|
|
166
161
|
offset += entry.size;
|
|
167
162
|
}
|
|
168
163
|
if (offset !== blob.byteLength)
|
|
@@ -181,15 +176,7 @@ type LayerFromIndex = (
|
|
|
181
176
|
options: { mtimeMs: number; include?: (path: string) => boolean },
|
|
182
177
|
) => unknown;
|
|
183
178
|
|
|
184
|
-
/**
|
|
185
|
-
* Blender's runtime tree as a SHARED LAYER the program reads, never written
|
|
186
|
-
* into this page's filesystem (browser-substrate ADR-0044 §6): the blob is
|
|
187
|
-
* read once, straight into shared memory, and the index names where each file
|
|
188
|
-
* lies in it. Staged file by file, the tree was held twice -- in this page's
|
|
189
|
-
* filesystem and in the layer the substrate made of it for the program's
|
|
190
|
-
* workers -- and took 0.3-0.5 s of every boot (measured 2026-09-27). A
|
|
191
|
-
* substrate without the door (`sharedFileLayerFromIndex`) stages as before.
|
|
192
|
-
*/
|
|
179
|
+
/** Build a shared runtime file layer from one blob and its index. The program reads the layer without copying it into the page filesystem. */
|
|
193
180
|
async function sharedRuntime(
|
|
194
181
|
fromIndex: LayerFromIndex,
|
|
195
182
|
log: BlenderEngineOptions['log'],
|
|
@@ -220,12 +207,7 @@ async function sharedRuntime(
|
|
|
220
207
|
throw new Error(`Blender runtime index accounts for ${total} bytes; the blob served ${filled}`);
|
|
221
208
|
const layer = fromIndex(RESOURCES, index.files, shared, {
|
|
222
209
|
mtimeMs: Date.now(),
|
|
223
|
-
//
|
|
224
|
-
// `blender -b` with no session, and at startup it asks a presenter at its
|
|
225
|
-
// own directory and waits two seconds for one. This session registers the
|
|
226
|
-
// same override itself (`session.py`, `VolterRenderEngine`) and answers
|
|
227
|
-
// through its own channel, so here the module could only wait (measured
|
|
228
|
-
// 2026-09-27: every boot printed "no presenter answered ... within 2 s").
|
|
210
|
+
// Skip the standalone render override because session.py registers VolterRenderEngine on this session’s channel.
|
|
229
211
|
include: (path) => !PACK_RENDER_OVERRIDES.has(path),
|
|
230
212
|
});
|
|
231
213
|
log('log', `blender: shared Blender's ${index.files.length}-file runtime tree in ${Math.round(performance.now() - began)} ms`);
|
|
@@ -239,16 +221,7 @@ type StoreLayerFromIndex = (
|
|
|
239
221
|
options: { mtimeMs: number; include?: (path: string) => boolean },
|
|
240
222
|
) => unknown;
|
|
241
223
|
|
|
242
|
-
/**
|
|
243
|
-
* Blender's runtime tree as a STORE ON THE ORIGIN'S DISK the program reads
|
|
244
|
-
* (browser-substrate ADR-0048): installed once per version by the
|
|
245
|
-
* substrate's installer worker, and read file by file as Blender opens them,
|
|
246
|
-
* a large file by the ranges it reads. The shared layer before it copied all
|
|
247
|
-
* 96 MB into memory before Blender started (0.9 s of each boot) and held it
|
|
248
|
-
* for the run; a boot reads 268 of the 2,082 files (measured 2026-09-28).
|
|
249
|
-
* Undefined, and said, where it cannot be installed: the shared layer then
|
|
250
|
-
* carries the tree as before.
|
|
251
|
-
*/
|
|
224
|
+
/** Install the versioned runtime file store on the origin and read files by range as Blender requests them. The shared layer carries the tree when installation is unavailable. */
|
|
252
225
|
async function installedRuntime(
|
|
253
226
|
fromIndex: StoreLayerFromIndex,
|
|
254
227
|
hold: ((projectId: string) => Promise<void>) | undefined,
|
|
@@ -354,7 +327,7 @@ function programFiles(filesystem: BrowserFileSystemLike): BlenderFiles {
|
|
|
354
327
|
}) } : {}),
|
|
355
328
|
readFile: (path) => filesystem.readFile(path),
|
|
356
329
|
// Not `async`: a write that throws throws here, so `request` never starts `.done` after a failed `.json`.
|
|
357
|
-
writeFile: (path, data) => { filesystem
|
|
330
|
+
writeFile: (path, data) => { replaceFileSync(filesystem, path, data); return Promise.resolve(); },
|
|
358
331
|
mkdirTree: async (path) => filesystem.mkdirSync(path, { recursive: true }),
|
|
359
332
|
readdir: async (path) => filesystem.readdirSync(path),
|
|
360
333
|
stat: async (path) => {
|
|
@@ -377,10 +350,11 @@ export async function startWaliBlenderEngine(
|
|
|
377
350
|
status: BlenderArtifactStatus,
|
|
378
351
|
): Promise<BlenderEngine> {
|
|
379
352
|
const started = performance.now();
|
|
380
|
-
const [wali, runtime, layers] = await Promise.all([
|
|
353
|
+
const [wali, runtime, layers, addonFiles] = await Promise.all([
|
|
381
354
|
substrate('browser-wali/worker-program.js'),
|
|
382
355
|
substrate('browser-runtime/browser-memory-filesystem.js'),
|
|
383
356
|
substrate('browser-wali/shared-file-layer.js').catch((): Record<string, unknown> => ({})),
|
|
357
|
+
sessionAddonFiles(),
|
|
384
358
|
]);
|
|
385
359
|
const WaliWorkerProgram = wali['BrowserWaliWorkerProgram'] as
|
|
386
360
|
| (new (
|
|
@@ -398,12 +372,7 @@ export async function startWaliBlenderEngine(
|
|
|
398
372
|
'does not know.',
|
|
399
373
|
);
|
|
400
374
|
|
|
401
|
-
//
|
|
402
|
-
// was fetched and compiled by the program's worker only once the program
|
|
403
|
-
// ran, after the layer below: measured 2026-09-28, the layer 270-975 ms and
|
|
404
|
-
// then the module 200-250 ms warm, in series. The substrate's preload fills
|
|
405
|
-
// the cache the program's worker reads; a substrate without it, or a preload
|
|
406
|
-
// that fails, leaves the worker to load the module as before.
|
|
375
|
+
// Preload the program module while constructing the runtime layer so fetch and compilation can overlap.
|
|
407
376
|
const preload = wali['preloadWaliModule'] as ((url: string, integrity: string) => Promise<void>) | undefined;
|
|
408
377
|
if (preload && status.integrity) void preload(artifactUrl('blender.wasm'), status.integrity).catch(() => undefined);
|
|
409
378
|
|
|
@@ -427,7 +396,12 @@ export async function startWaliBlenderEngine(
|
|
|
427
396
|
const runtimeLayer = (fromStore ? await installedRuntime(fromStore, holdTree, options.log) : undefined)
|
|
428
397
|
?? (fromIndex ? await sharedRuntime(fromIndex, options.log) : undefined);
|
|
429
398
|
if (!runtimeLayer) await stageRuntime(filesystem, options.log);
|
|
430
|
-
filesystem
|
|
399
|
+
replaceFileSync(filesystem, SESSION_SCRIPT, sessionPython);
|
|
400
|
+
replaceFileSync(filesystem, `${SESSION_ROOT}/gltf_export.py`, gltfExportPython);
|
|
401
|
+
for (const { path, bytes } of addonFiles) {
|
|
402
|
+
filesystem.mkdirSync(path.slice(0, path.lastIndexOf('/')), { recursive: true });
|
|
403
|
+
replaceFileSync(filesystem, path, bytes);
|
|
404
|
+
}
|
|
431
405
|
|
|
432
406
|
const workers = status.workers ?? { pool: 16, blender: 4 };
|
|
433
407
|
const program = new WaliWorkerProgram(filesystem, {
|
|
@@ -457,22 +431,11 @@ export async function startWaliBlenderEngine(
|
|
|
457
431
|
VOLTER_SESSION_ROOT: SESSION_ROOT,
|
|
458
432
|
// THE ARENA'S DOOR ON THIS SKEW. See `readArena`.
|
|
459
433
|
VOLTER_EXPORT_BUFFER_PATH: ARENA_PATH,
|
|
460
|
-
//
|
|
461
|
-
// BLENDER_WALI_WORKERS). Its default pool is built at the CPU count
|
|
462
|
-
// before `-t` reaches it: measured 2026-09-28, nine of the ten threads
|
|
463
|
-
// this Blender started were OpenImageIO's, idle for the session, each
|
|
464
|
-
// holding a pool worker (browser-substrate `programs/blender/5.2.0`
|
|
465
|
-
// README §(f)).
|
|
434
|
+
// Limit OpenImageIO’s pool before Blender starts; -t applies to Blender’s own worker pool.
|
|
466
435
|
OPENIMAGEIO_THREADS: '1',
|
|
467
436
|
},
|
|
468
437
|
...(runtimeLayer ? { layers: [runtimeLayer] } : {}),
|
|
469
|
-
//
|
|
470
|
-
// what it writes there never crosses to this page, so nothing waits for
|
|
471
|
-
// the page to take it (browser-substrate `privateRoots`, ADR-0046).
|
|
472
|
-
// Measured 2026-09-27: published, a `.blend` saved to /tmp cost 11 ms
|
|
473
|
-
// against the standalone skew's 2, most of it the page taking the file
|
|
474
|
-
// before Blender's next printed line. The session's channel and the
|
|
475
|
-
// project live elsewhere and still cross.
|
|
438
|
+
// Keep Blender’s /tmp private to the program. The session channel and project files remain shared with the page.
|
|
476
439
|
privateRoots: ['/tmp'],
|
|
477
440
|
threadPoolSize: workers.pool,
|
|
478
441
|
// Blender talks to nothing. The editor's own routes are this worker's, not
|
|
@@ -486,10 +449,7 @@ export async function startWaliBlenderEngine(
|
|
|
486
449
|
for (const line of text.split('\n')) {
|
|
487
450
|
if (line === '') continue;
|
|
488
451
|
if (line.startsWith('@@VOLTER-READY ')) readyLine = line.slice('@@VOLTER-READY '.length);
|
|
489
|
-
//
|
|
490
|
-
// -- the same rule as the standalone engine, and for the same measured
|
|
491
|
-
// reason: only the session's own named conditions belong in the set an
|
|
492
|
-
// agent must drive to zero.
|
|
452
|
+
// Route Blender streams to page output; named session conditions use their own channel.
|
|
493
453
|
options.log(sessionLevel(line), line);
|
|
494
454
|
}
|
|
495
455
|
};
|
|
@@ -518,14 +478,8 @@ export async function startWaliBlenderEngine(
|
|
|
518
478
|
{
|
|
519
479
|
onStdout: say,
|
|
520
480
|
onStderr: say,
|
|
521
|
-
//
|
|
522
|
-
//
|
|
523
|
-
// runtime's own word to its host -- today, the one-shot notice that an
|
|
524
|
-
// unimplemented syscall was answered `-ENOSYS` instead of trapping
|
|
525
|
-
// (`@volter/browser-wali`'s `runtime.ts`). Answering like a kernel is
|
|
526
|
-
// only defensible because the CALLER can degrade VISIBLY; without this
|
|
527
|
-
// the degrade is silent, and before it existed the notice reached a
|
|
528
|
-
// nested worker's devtools console and nothing else.
|
|
481
|
+
// Forward the WALI runtime’s unimplemented-syscall diagnostic to page output so a degraded
|
|
482
|
+
// operation remains visible to the caller.
|
|
529
483
|
onDiagnostic: (message: string) => options.log('error', `@@VOLTER-ERROR ${message}`),
|
|
530
484
|
},
|
|
531
485
|
)
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* named messages or JSON fields, with a safe fallback for cyclic values.
|
|
5
|
-
* Startup wrappers omit Error stacks from their user-visible message; worker
|
|
6
|
-
* replies keep them as before. */
|
|
1
|
+
/** Preserve thrown messages across the worker boundary, including named fields on plain
|
|
2
|
+
* objects and a printable representation of cyclic values. Startup wrappers omit Error stacks;
|
|
3
|
+
* worker replies include them. */
|
|
7
4
|
export function describeThrown(error: unknown, includeStack = true): string {
|
|
8
5
|
if (error instanceof Error)
|
|
9
6
|
return (includeStack ? error.stack : undefined) ?? `${error.name}: ${error.message}`;
|
|
@@ -1,19 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A saved `.blend` cut into content-defined chunks, the unit a document save travels in
|
|
3
|
-
* (`editor-blender/serving/blender-routes.ts`, `/__editor/blender-document`).
|
|
4
|
-
*
|
|
5
|
-
* WHY CONTENT-DEFINED. A save rewrites the whole file, but an edit changes little of it:
|
|
6
|
-
* MEASURED 2026-09-29, native Blender 5.2 on the Stoneguard bridge, one object moved between two
|
|
7
|
-
* saves changed 1 of 475 one-MiB chunks of a 497 MB file. Fixed offsets would keep that only while
|
|
8
|
-
* no block changes size; an edit that grows a mesh would shift every chunk after it. Cutting where
|
|
9
|
-
* the bytes themselves say (a gear hash, as FastCDC does) re-aligns right after the change, so only
|
|
10
|
-
* the chunks around it are new.
|
|
11
|
-
*
|
|
12
|
-
* The bounds are the transport's: no chunk above {@link CHUNK_MAX}, which the server refuses.
|
|
13
|
-
*/
|
|
1
|
+
/** Split saved .blend bytes at content-defined gear-hash boundaries so unchanged ranges retain their identities after size-changing edits. CHUNK_MAX is the server’s transport limit. */
|
|
14
2
|
|
|
15
|
-
|
|
16
|
-
|
|
3
|
+
const CHUNK_MIN = 256 * 1024;
|
|
4
|
+
const CHUNK_MAX = 4 * 1024 * 1024;
|
|
17
5
|
/** 20 bits: a cut about every MiB past the minimum. */
|
|
18
6
|
const CUT_MASK = (1 << 20) - 1;
|
|
19
7
|
|
|
@@ -32,7 +20,7 @@ const GEAR = (() => {
|
|
|
32
20
|
})();
|
|
33
21
|
|
|
34
22
|
/** The end offsets of each chunk of `bytes`. */
|
|
35
|
-
|
|
23
|
+
function chunkBoundaries(bytes: Uint8Array): number[] {
|
|
36
24
|
const ends: number[] = [];
|
|
37
25
|
let start = 0;
|
|
38
26
|
while (start < bytes.length) {
|