@volter/editor-blender 0.1.133 → 0.1.134
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/host/blender-runtime-host.ts +36 -4
- package/package.json +6 -6
|
@@ -43,7 +43,7 @@ import type {
|
|
|
43
43
|
BlenderUvLayout,
|
|
44
44
|
} from '@volter/blender-engine/browser/rna';
|
|
45
45
|
import { createBlenderDisplayTransform, blenderDisplaySettingsForRender } from '@volter/blender-engine/browser/three/blender-display-transform';
|
|
46
|
-
import
|
|
46
|
+
import { BlenderRuntimeView } from '@volter/blender-engine/browser/three/blender-runtime-view';
|
|
47
47
|
import type { EditorCommandResult } from '@volter/editor-sdk/commands';
|
|
48
48
|
import { editorHost } from '@volter/editor-sdk/host';
|
|
49
49
|
import { invokeViewVerb, type ViewVerbContribution } from '@volter/editor-sdk/views';
|
|
@@ -821,6 +821,7 @@ function driveNodeView(cmd: { type: string; [key: string]: unknown }): NodeViewS
|
|
|
821
821
|
}
|
|
822
822
|
|
|
823
823
|
interface RuntimeView {
|
|
824
|
+
readonly root: THREE.Group;
|
|
824
825
|
stageFrame(part: { session: string; revision: number; abort?: boolean; mesh?: string; image?: string; piece?: unknown }): void;
|
|
825
826
|
applyFrame(frame: unknown): unknown;
|
|
826
827
|
snapshot(): ReturnType<BlenderRuntimeView['snapshot']>;
|
|
@@ -903,6 +904,7 @@ const isRuntimeView = (value: unknown): value is RuntimeView =>
|
|
|
903
904
|
typeof (value as RuntimeView).applyFrame === 'function' &&
|
|
904
905
|
typeof (value as RuntimeView).stageFrame === 'function' &&
|
|
905
906
|
typeof (value as RuntimeView).snapshot === 'function' &&
|
|
907
|
+
(value as RuntimeView).root?.isObject3D === true &&
|
|
906
908
|
typeof (value as RuntimeView).captureSnapshot === 'function' &&
|
|
907
909
|
typeof (value as RuntimeView).recordPresentation === 'function' &&
|
|
908
910
|
typeof (value as RuntimeView).recordPhotograph === 'function';
|
|
@@ -951,11 +953,20 @@ export function blenderRuntime(): BlenderRuntime {
|
|
|
951
953
|
captureLifetime = lifetime;
|
|
952
954
|
let photographing = false;
|
|
953
955
|
let streamedFrame: { view: RuntimeView; session: string; revision: number } | null = null;
|
|
956
|
+
let renderFrame: { view: BlenderRuntimeView; source: RuntimeView; sourceFrame: ReturnType<RuntimeView['snapshot']>; documentId: string; session: string; revision: number } | null = null;
|
|
957
|
+
const discardRenderFrame = () => {
|
|
958
|
+
const owned = renderFrame;
|
|
959
|
+
renderFrame = null;
|
|
960
|
+
owned?.view.dispose();
|
|
961
|
+
owned?.view.root.removeFromParent();
|
|
962
|
+
};
|
|
954
963
|
const discardStreamedFrame = () => {
|
|
955
964
|
const staged = streamedFrame;
|
|
956
965
|
streamedFrame = null;
|
|
957
966
|
if (staged) staged.view.stageFrame({ session: staged.session, revision: staged.revision, abort: true });
|
|
967
|
+
discardRenderFrame();
|
|
958
968
|
};
|
|
969
|
+
lifetime.signal.addEventListener('abort', discardStreamedFrame, { once: true });
|
|
959
970
|
runtime = new BlenderRuntime({
|
|
960
971
|
work: beginBlenderWork,
|
|
961
972
|
history: (entries) => {
|
|
@@ -997,6 +1008,20 @@ export function blenderRuntime(): BlenderRuntime {
|
|
|
997
1008
|
const view = await runtimeView();
|
|
998
1009
|
lifetime.signal.throwIfAborted();
|
|
999
1010
|
if (presentationDocumentId() !== documentId) throw new Error('Blender document changed during frame transfer');
|
|
1011
|
+
if (part.evaluation === 'render') {
|
|
1012
|
+
if (!renderFrame) {
|
|
1013
|
+
if (part.mesh !== undefined || part.image !== undefined) throw new Error('Render resource arrived before its frame');
|
|
1014
|
+
const detached = BlenderRuntimeView.forPhotograph();
|
|
1015
|
+
view.root.updateWorldMatrix(true, false);
|
|
1016
|
+
detached.root.matrix.copy(view.root.matrixWorld);
|
|
1017
|
+
renderFrame = { view: detached, source: view, sourceFrame: view.snapshot(), documentId, session: part.session, revision: part.revision };
|
|
1018
|
+
}
|
|
1019
|
+
if (renderFrame.source !== view || renderFrame.sourceFrame !== view.snapshot() || renderFrame.documentId !== documentId || renderFrame.session !== part.session || renderFrame.revision !== part.revision)
|
|
1020
|
+
throw new Error('Blender capture source changed during render transfer');
|
|
1021
|
+
renderFrame.view.stageFrame(part);
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
if (renderFrame) throw new Error('Viewport frame arrived during render transfer');
|
|
1000
1025
|
if (streamedFrame && streamedFrame.view !== view)
|
|
1001
1026
|
throw new Error('Blender presenter changed during frame transfer');
|
|
1002
1027
|
view.stageFrame(part);
|
|
@@ -1027,6 +1052,12 @@ export function blenderRuntime(): BlenderRuntime {
|
|
|
1027
1052
|
throw new Error('Blender document changed before its frame could be presented');
|
|
1028
1053
|
if (streamedFrame && streamedFrame.view !== view)
|
|
1029
1054
|
throw new Error('Blender presenter changed before its frame could be presented');
|
|
1055
|
+
const rendering = capture?.evaluation === 'render' ? renderFrame : null;
|
|
1056
|
+
if (capture?.evaluation === 'render' && !rendering)
|
|
1057
|
+
throw new Error('Render-evaluated frame arrived without its staged resources');
|
|
1058
|
+
if (rendering && (rendering.source !== view || rendering.sourceFrame !== view.snapshot() || rendering.documentId !== documentId))
|
|
1059
|
+
throw new Error('Blender capture source changed before render presentation');
|
|
1060
|
+
const presenter = rendering?.view ?? view;
|
|
1030
1061
|
// WHAT THE PRESENTER HELD BEFORE THIS FRAME, carried back to the session
|
|
1031
1062
|
// beside whatever this present produced. The session only has a RECORD of
|
|
1032
1063
|
// what it sent; this view is the authority on what it actually holds, and
|
|
@@ -1036,7 +1067,7 @@ export function blenderRuntime(): BlenderRuntime {
|
|
|
1036
1067
|
// all, rather than a wrong `null`.
|
|
1037
1068
|
const endApply = beginBlenderWork('applying frame to Model');
|
|
1038
1069
|
const applied = (() => {
|
|
1039
|
-
try { return
|
|
1070
|
+
try { return presenter.applyFrame(frame) as { held?: unknown } | null | undefined; }
|
|
1040
1071
|
finally { endApply(); }
|
|
1041
1072
|
})();
|
|
1042
1073
|
streamedFrame = null;
|
|
@@ -1049,7 +1080,7 @@ export function blenderRuntime(): BlenderRuntime {
|
|
|
1049
1080
|
// THE RECORD OF WHAT WAS SUBMITTED, kept only once the frame was taken:
|
|
1050
1081
|
// a refused frame is not displayed and must not be described as if it
|
|
1051
1082
|
// were.
|
|
1052
|
-
|
|
1083
|
+
presenter.recordPresentation(description);
|
|
1053
1084
|
if (!capture) return answer(undefined);
|
|
1054
1085
|
lastCapture = capture;
|
|
1055
1086
|
if (!capture.render) return answer(undefined);
|
|
@@ -1091,7 +1122,7 @@ export function blenderRuntime(): BlenderRuntime {
|
|
|
1091
1122
|
'A Blender render capture must carry the scene camera position, target and up',
|
|
1092
1123
|
);
|
|
1093
1124
|
if (photographing) throw new Error('A Blender render capture is already in progress');
|
|
1094
|
-
const snapshot = view.captureSnapshot();
|
|
1125
|
+
const snapshot = rendering ? rendering.view.takeCaptureSnapshot() : view.captureSnapshot();
|
|
1095
1126
|
photographing = true;
|
|
1096
1127
|
lifetime.signal.addEventListener('abort', snapshot.dispose, { once: true });
|
|
1097
1128
|
try {
|
|
@@ -1150,6 +1181,7 @@ export function blenderRuntime(): BlenderRuntime {
|
|
|
1150
1181
|
photographing = false;
|
|
1151
1182
|
lifetime.signal.removeEventListener('abort', snapshot.dispose);
|
|
1152
1183
|
snapshot.dispose();
|
|
1184
|
+
if (rendering && renderFrame === rendering) renderFrame = null;
|
|
1153
1185
|
}
|
|
1154
1186
|
},
|
|
1155
1187
|
// The worker's OWN stdout and stderr, forwarded so a developer can read
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@volter/editor-blender",
|
|
3
3
|
"author": "Volter AI, Inc.",
|
|
4
4
|
"license": "AGPL-3.0-only AND GPL-3.0-or-later",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.134",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
8
8
|
},
|
|
@@ -72,15 +72,15 @@
|
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
74
|
"@modelcontextprotocol/sdk": "1.32.1",
|
|
75
|
-
"@volter/blender-engine": "0.1.
|
|
76
|
-
"@volter/editor-threejs": "0.5.
|
|
75
|
+
"@volter/blender-engine": "0.1.132",
|
|
76
|
+
"@volter/editor-threejs": "0.5.195",
|
|
77
77
|
"zod": "^4.3.6",
|
|
78
78
|
"@fortawesome/free-solid-svg-icons": "^7.2.0"
|
|
79
79
|
},
|
|
80
80
|
"peerDependencies": {
|
|
81
|
-
"@volter/editor-live": "0.5.
|
|
82
|
-
"@volter/editor-project": "0.5.
|
|
83
|
-
"@volter/editor-sdk": "0.5.
|
|
81
|
+
"@volter/editor-live": "0.5.195",
|
|
82
|
+
"@volter/editor-project": "0.5.195",
|
|
83
|
+
"@volter/editor-sdk": "0.5.195",
|
|
84
84
|
"react": "^19.0.0",
|
|
85
85
|
"react-dom": "^19.0.0",
|
|
86
86
|
"three": "^0.180.0",
|