@volter/editor-threejs 0.5.66 → 0.5.68

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.
Files changed (99) hide show
  1. package/contributions/animation-timeline.utility.tsx +44 -0
  2. package/contributions/three-integration.service.ts +13 -0
  3. package/package.json +96 -6
  4. package/src/adapter/renderer-config.ts +3 -4
  5. package/src/adapter/three-contract.ts +72 -0
  6. package/src/ecs/object-marks.ts +1 -1
  7. package/src/ecs/user-data.ts +0 -9
  8. package/src/host-hierarchy-objects.ts +31 -0
  9. package/src/kit/animation/three-clips-subject.ts +190 -0
  10. package/src/kit/asset-compare.ts +294 -0
  11. package/src/kit/asset-preview-command.ts +265 -0
  12. package/src/kit/asset-preview-framing.ts +357 -0
  13. package/src/kit/asset-preview.ts +2802 -0
  14. package/src/kit/asset-workflow/model-inspection.ts +830 -0
  15. package/src/kit/authoring/component-instance-root.ts +171 -0
  16. package/src/kit/authoring/design-time-settle.ts +343 -0
  17. package/src/kit/authoring/live-object-transform.ts +62 -0
  18. package/src/kit/authoring/object3d-document-session-registry.ts +154 -0
  19. package/src/kit/authoring/object3d-document-session.ts +1965 -0
  20. package/src/kit/authoring/object3d-gesture-controller.ts +113 -0
  21. package/src/kit/authoring/quarks-particle-systems.ts +19 -0
  22. package/src/kit/authoring/shell-viewport-policy.ts +48 -0
  23. package/src/kit/authoring/source-object3d-authoring-adapter.ts +526 -0
  24. package/src/kit/authoring/three-projection-core.ts +226 -0
  25. package/src/kit/authoring/viewport-pick-context.ts +39 -0
  26. package/src/kit/authoring/viewport-raycast.ts +240 -0
  27. package/src/kit/authoring/world-hidden-viewport.ts +95 -0
  28. package/src/kit/camera-authoring.ts +175 -0
  29. package/src/kit/components/CameraInfo.tsx +56 -0
  30. package/src/kit/components/InspectorObjectPreview.tsx +57 -0
  31. package/src/kit/components/Object3DDocumentToolbar.tsx +549 -0
  32. package/src/kit/components/Object3DDocumentViewport.tsx +58 -0
  33. package/src/kit/components/StageHost.tsx +2547 -0
  34. package/src/kit/components/StageOverlays.tsx +21 -0
  35. package/src/kit/components/StatsOverlay.tsx +78 -0
  36. package/src/kit/components/ToolObject3DPreview.tsx +39 -0
  37. package/src/kit/components/ViewportFurniture.tsx +655 -0
  38. package/src/kit/components/ViewportOverlay.tsx +215 -0
  39. package/src/kit/components/ViewportShadingMenu.tsx +340 -0
  40. package/src/kit/components/ViewportViewMenu.tsx +155 -0
  41. package/src/kit/components/asset-viewers/EntityModelDocument.tsx +121 -0
  42. package/src/kit/components/asset-viewers/EnvironmentAssetDocument.tsx +440 -0
  43. package/src/kit/components/asset-viewers/LiveModuleDocument.tsx +395 -0
  44. package/src/kit/components/asset-viewers/LutAssetDocument.tsx +444 -0
  45. package/src/kit/components/asset-viewers/ModelAssetDocument.tsx +105 -0
  46. package/src/kit/components/asset-viewers/Object3DPreview.tsx +356 -0
  47. package/src/kit/components/asset-viewers/QuarksAssetDocument.tsx +527 -0
  48. package/src/kit/components/asset-viewers/ShaderAssetDocument.tsx +743 -0
  49. package/src/kit/components/asset-viewers/three-asset-viewers.tsx +132 -0
  50. package/src/kit/components/object3d-contribution-surfaces.tsx +33 -0
  51. package/src/kit/components/stage-keyboard.tsx +40 -0
  52. package/src/kit/components/stage-overlay-set.tsx +105 -0
  53. package/src/kit/components/stage-presence-markers.ts +482 -0
  54. package/src/kit/components/stage-transform-chrome.ts +30 -0
  55. package/src/kit/components/stage-transform-tools.tsx +73 -0
  56. package/src/kit/components/stage-view-name.ts +30 -0
  57. package/src/kit/components/standard-viewport-dressing.ts +1042 -0
  58. package/src/kit/components/world-root-binding.ts +64 -0
  59. package/src/kit/constraint-helper.ts +338 -0
  60. package/src/kit/editor-shell-store.ts +814 -0
  61. package/src/kit/editor-viewport.ts +6621 -0
  62. package/src/kit/entity-lod.ts +31 -0
  63. package/src/kit/entity-object.ts +92 -0
  64. package/src/kit/hierarchy-mark-reader.ts +74 -0
  65. package/src/kit/instanced-presentation.ts +164 -0
  66. package/src/kit/live-module-source.ts +230 -0
  67. package/src/kit/model-thumbnail.ts +539 -0
  68. package/src/kit/play-camera-flight.ts +300 -0
  69. package/src/kit/projection/three.ts +898 -0
  70. package/src/kit/reflection-probe-helper.ts +142 -0
  71. package/src/kit/scene-document-viewport.ts +51 -0
  72. package/src/kit/scene-framing.ts +315 -0
  73. package/src/kit/scene-view-fog.ts +89 -0
  74. package/src/kit/spatial-handle-visuals.ts +332 -0
  75. package/src/kit/stories/three-story-model.ts +66 -0
  76. package/src/kit/three-canvas-render.ts +44 -0
  77. package/src/kit/three-hierarchy-row-media.ts +26 -0
  78. package/src/kit/three-inspection-media.ts +73 -0
  79. package/src/kit/three-integration.ts +86 -0
  80. package/src/kit/three-state.ts +33 -0
  81. package/src/kit/three-viewport/bone-selection-highlight.ts +119 -0
  82. package/src/kit/three-viewport/camera-fit.ts +41 -0
  83. package/src/kit/three-viewport/interactive-renderer.ts +132 -0
  84. package/src/kit/three-viewport/selection-brackets.ts +355 -0
  85. package/src/kit/three-viewport/selection-outline.ts +333 -0
  86. package/src/kit/three-viewport/skeleton-helper.ts +61 -0
  87. package/src/kit/three-viewport/source-color.ts +197 -0
  88. package/src/kit/three-viewport/studio-environment.ts +96 -0
  89. package/src/kit/trigger-volume-helper.ts +116 -0
  90. package/src/kit/viewport-actions.ts +128 -0
  91. package/src/kit/viewport-authoring-policy.ts +154 -0
  92. package/src/kit/viewport-commands.ts +318 -0
  93. package/src/kit/viewport-hotkeys.ts +119 -0
  94. package/src/kit/viewport-shading-boundary.ts +12 -0
  95. package/src/kit/viewport-status-facet.ts +53 -0
  96. package/src/object3d-contributions.ts +494 -0
  97. package/src/render/viewport-shading.ts +6 -2
  98. package/src/viewport-api.ts +92 -0
  99. package/src/viewport-door.ts +237 -0
@@ -0,0 +1,237 @@
1
+ /**
2
+ * THE AUTHORED VIEWPORT'S DOOR — the host's side of `host.viewport` (WORK.md
3
+ * §Play leaves the host, P2). Every mounted 3D STAGE binds its rig and its
4
+ * presenter here under its own document id (ARCHITECTURE-CORE §One stage:
5
+ * there is one stage host, mounted once per 3D document, and the door is per
6
+ * stage) and drives its frame hook from its own RAF loop; the lanes that
7
+ * mount something (Play's adoption of the live scene, the play-entry camera
8
+ * flight) read here and never hold a panel.
9
+ *
10
+ * The SINGULAR members — `viewportRig`, `presentViewportRoots`,
11
+ * `setViewportHelper`, `onViewportFrame` — resolve THE PRIMARY STAGE at call
12
+ * time: the stage presenting live roots if one is, else the FOCUSED stage
13
+ * (`activeWorkspaceDocumentId()`'s), else the first bound. Focus and
14
+ * presentation are the two facts; nothing here is "the scene viewport" by
15
+ * name. `viewportStages()` is every stage, for the lane that must touch each
16
+ * one (a helper `Object3D` has one parent, so it is set per stage).
17
+ *
18
+ * Until the lanes move, the host's own lanes import this registry directly; a
19
+ * package reads `@volter/editor-sdk/host`'s `viewport` member, which is this.
20
+ */
21
+
22
+ import type { ViewportDoor, ViewportPresentation, ViewportRig, ViewportRoot, ViewportStage } from './viewport-api';
23
+ import { activeWorkspaceDocumentId } from '@volter/editor-sdk/kit/workspace-document-registry';
24
+
25
+ type Presenter = (roots: readonly ViewportRoot[]) => ViewportPresentation | null;
26
+ /** An editor-only helper object, typed by the host door it is shown through. */
27
+ type HelperObject = Parameters<ViewportDoor['setHelper']>[1];
28
+ type HelperSink = (kind: string, object: HelperObject) => void;
29
+
30
+ interface Binding {
31
+ readonly documentId: string;
32
+ readonly rig: ViewportRig;
33
+ readonly present: Presenter;
34
+ readonly setHelper: HelperSink;
35
+ readonly frameListeners: Set<(dtSeconds: number) => void>;
36
+ /** Helpers set on THIS stage by kind, so a re-bind restores them. */
37
+ readonly helpers: Map<string, HelperObject>;
38
+ /** A presentation of live roots is alive on this stage right now. */
39
+ presenting: boolean;
40
+ }
41
+
42
+ /** Every bound stage, keyed by the document it draws; insertion-ordered, so
43
+ * "the first bound" is a real answer. */
44
+ const bound = new Map<string, Binding>();
45
+ /**
46
+ * What belongs to the STAGE rather than to its current canvas, held by
47
+ * document id so it OUTLIVES a binding: its frame subscribers and its
48
+ * helpers. A stage's viewport remounts (a dock re-layout, HMR) without its
49
+ * baked navmesh having to be re-baked — the `setHelper` contract the SDK
50
+ * states — and without a lane's `onFrame` going quiet.
51
+ */
52
+ const stageStateByDocument = new Map<
53
+ string,
54
+ {
55
+ readonly frameListeners: Set<(dtSeconds: number) => void>;
56
+ readonly helpers: Map<string, HelperObject>;
57
+ }
58
+ >();
59
+
60
+ function stageStateFor(documentId: string): {
61
+ readonly frameListeners: Set<(dtSeconds: number) => void>;
62
+ readonly helpers: Map<string, HelperObject>;
63
+ } {
64
+ let state = stageStateByDocument.get(documentId);
65
+ if (!state) {
66
+ state = { frameListeners: new Set(), helpers: new Map() };
67
+ stageStateByDocument.set(documentId, state);
68
+ }
69
+ return state;
70
+ }
71
+ /** Listeners on the singular `onViewportFrame`: they follow the PRIMARY
72
+ * stage, so they are held here and fanned in rather than added to a
73
+ * binding's own set, which would strand them on a stage that stops being
74
+ * primary (or unmounts). */
75
+ const primaryFrameListeners = new Set<(dtSeconds: number) => void>();
76
+ const stageListeners = new Set<(stages: readonly ViewportStage[]) => void>();
77
+
78
+ /** THE PRIMARY STAGE: presenting live roots > focused > first bound. */
79
+ function primary(): Binding | null {
80
+ for (const binding of bound.values()) if (binding.presenting) return binding;
81
+ const focused = activeWorkspaceDocumentId();
82
+ if (focused !== null) {
83
+ const match = bound.get(focused);
84
+ if (match) return match;
85
+ }
86
+ return bound.values().next().value ?? null;
87
+ }
88
+
89
+ let stagesCache: readonly ViewportStage[] | null = null;
90
+
91
+ function notifyStages(): void {
92
+ stagesCache = null;
93
+ const snapshot = viewportStages();
94
+ for (const fn of stageListeners) fn(snapshot);
95
+ }
96
+
97
+ /** A stage door resolves its binding BY ID on every call, so a door held
98
+ * across that stage's remount keeps driving the live canvas rather than the
99
+ * disposed one. */
100
+ function stageDoor(documentId: string, rig: ViewportRig): ViewportStage {
101
+ const state = stageStateFor(documentId);
102
+ return {
103
+ documentId,
104
+ rig: () => bound.get(documentId)?.rig ?? rig,
105
+ setHelper: (kind, object) => {
106
+ state.helpers.set(kind, object);
107
+ bound.get(documentId)?.setHelper(kind, object);
108
+ },
109
+ onFrame: (fn) => {
110
+ state.frameListeners.add(fn);
111
+ return () => {
112
+ state.frameListeners.delete(fn);
113
+ };
114
+ },
115
+ };
116
+ }
117
+
118
+ /** A mounted stage binds its rig, how it presents live roots and how it shows
119
+ * a helper, under the id of the document it draws; the return unbinds (a
120
+ * re-bind for the same id REPLACES, and a stale binding's unbind never
121
+ * removes its successor). Helpers set through the singular door before the
122
+ * bind are applied now, the way they always were — but "before the bind" is
123
+ * now per stage: a helper shown on THIS document's stage returns to it, and
124
+ * a helper set while nothing is bound anywhere is dropped rather than
125
+ * appearing on whichever stage happens to mount next. */
126
+ export function bindViewportRig(
127
+ rig: ViewportRig,
128
+ present: Presenter,
129
+ setHelper: HelperSink,
130
+ options: { readonly documentId: string },
131
+ ): () => void {
132
+ const state = stageStateFor(options.documentId);
133
+ const next: Binding = {
134
+ documentId: options.documentId,
135
+ rig,
136
+ present,
137
+ setHelper,
138
+ frameListeners: state.frameListeners,
139
+ helpers: state.helpers,
140
+ presenting: false,
141
+ };
142
+ bound.set(options.documentId, next);
143
+ for (const [kind, object] of next.helpers) setHelper(kind, object);
144
+ notifyStages();
145
+ return () => {
146
+ if (bound.get(options.documentId) !== next) return;
147
+ bound.delete(options.documentId);
148
+ notifyStages();
149
+ };
150
+ }
151
+
152
+ /** Show (or clear with null) the editor-only helper of `kind` on the PRIMARY
153
+ * stage. A helper wanted on every stage is set per stage through
154
+ * {@link viewportStages} — an `Object3D` has one parent. */
155
+ export function setViewportHelper(kind: string, object: HelperObject): void {
156
+ const stage = primary();
157
+ if (!stage) return;
158
+ stage.helpers.set(kind, object);
159
+ stage.setHelper(kind, object);
160
+ }
161
+
162
+ export function viewportRig(): ViewportRig | null {
163
+ return primary()?.rig ?? null;
164
+ }
165
+
166
+ export function presentViewportRoots(roots: readonly ViewportRoot[]): ViewportPresentation | null {
167
+ const stage = primary();
168
+ if (!stage) return null;
169
+ const presentation = stage.present(roots);
170
+ if (!presentation) return null;
171
+ // The stage stays PRIMARY for as long as it is showing live roots — that is
172
+ // what makes Play's own camera flight and frame hook keep finding it while
173
+ // focus moves to the live document.
174
+ stage.presenting = true;
175
+ let released = false;
176
+ return {
177
+ worldId: presentation.worldId,
178
+ dispose: () => {
179
+ if (!released) {
180
+ released = true;
181
+ stage.presenting = false;
182
+ }
183
+ presentation.dispose();
184
+ },
185
+ };
186
+ }
187
+
188
+ /** Runs after the PRIMARY stage's own per-frame update, and RE-TARGETS: the
189
+ * listener follows whichever stage is primary, not the one that was primary
190
+ * when it subscribed. */
191
+ export function onViewportFrame(fn: (dtSeconds: number) => void): () => void {
192
+ primaryFrameListeners.add(fn);
193
+ return () => {
194
+ primaryFrameListeners.delete(fn);
195
+ };
196
+ }
197
+
198
+ /** Every bound stage, in bind order. */
199
+ export function viewportStages(): readonly ViewportStage[] {
200
+ if (!stagesCache)
201
+ stagesCache = [...bound.values()].map((binding) => stageDoor(binding.documentId, binding.rig));
202
+ return stagesCache;
203
+ }
204
+
205
+ /**
206
+ * WHICH HELPER KINDS A STAGE IS HOLDING — the kinds something has actually
207
+ * shown on it through `setHelper` (`@volter/editor-sdk/host`,
208
+ * `ViewportStage.setHelper`), cleared ones excluded.
209
+ *
210
+ * It exists because a 3D document's own OVERLAYS MENU must follow what the
211
+ * document put there and nothing else (`Object3DDocumentToolbar`): Blender's
212
+ * viewport overlay popover lists Bones, and ours may not list a Bones row over
213
+ * a document that has no bones to show. Same rule as the Outliner's restriction
214
+ * columns — a control follows what the thing behind it ANSWERS, never a knob.
215
+ */
216
+ export function viewportStageHelperKinds(documentId: string): readonly string[] {
217
+ const state = stageStateByDocument.get(documentId);
218
+ if (!state) return [];
219
+ return [...state.helpers].filter(([, object]) => object !== null).map(([kind]) => kind);
220
+ }
221
+
222
+ export function onViewportStages(fn: (stages: readonly ViewportStage[]) => void): () => void {
223
+ stageListeners.add(fn);
224
+ return () => {
225
+ stageListeners.delete(fn);
226
+ };
227
+ }
228
+
229
+ /** A stage's RAF loop calls this after its own update each frame. The stage's
230
+ * own listeners always run; the primary-stage listeners run here only when
231
+ * this stage IS the primary. */
232
+ export function runViewportFrame(documentId: string, dtSeconds: number): void {
233
+ const binding = bound.get(documentId);
234
+ if (!binding) return;
235
+ for (const fn of binding.frameListeners) fn(dtSeconds);
236
+ if (primary() === binding) for (const fn of primaryFrameListeners) fn(dtSeconds);
237
+ }