@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.
- package/contributions/animation-timeline.utility.tsx +44 -0
- package/contributions/three-integration.service.ts +13 -0
- package/package.json +96 -6
- package/src/adapter/renderer-config.ts +3 -4
- package/src/adapter/three-contract.ts +72 -0
- package/src/ecs/object-marks.ts +1 -1
- package/src/ecs/user-data.ts +0 -9
- package/src/host-hierarchy-objects.ts +31 -0
- package/src/kit/animation/three-clips-subject.ts +190 -0
- package/src/kit/asset-compare.ts +294 -0
- package/src/kit/asset-preview-command.ts +265 -0
- package/src/kit/asset-preview-framing.ts +357 -0
- package/src/kit/asset-preview.ts +2802 -0
- package/src/kit/asset-workflow/model-inspection.ts +830 -0
- package/src/kit/authoring/component-instance-root.ts +171 -0
- package/src/kit/authoring/design-time-settle.ts +343 -0
- package/src/kit/authoring/live-object-transform.ts +62 -0
- package/src/kit/authoring/object3d-document-session-registry.ts +154 -0
- package/src/kit/authoring/object3d-document-session.ts +1965 -0
- package/src/kit/authoring/object3d-gesture-controller.ts +113 -0
- package/src/kit/authoring/quarks-particle-systems.ts +19 -0
- package/src/kit/authoring/shell-viewport-policy.ts +48 -0
- package/src/kit/authoring/source-object3d-authoring-adapter.ts +526 -0
- package/src/kit/authoring/three-projection-core.ts +226 -0
- package/src/kit/authoring/viewport-pick-context.ts +39 -0
- package/src/kit/authoring/viewport-raycast.ts +240 -0
- package/src/kit/authoring/world-hidden-viewport.ts +95 -0
- package/src/kit/camera-authoring.ts +175 -0
- package/src/kit/components/CameraInfo.tsx +56 -0
- package/src/kit/components/InspectorObjectPreview.tsx +57 -0
- package/src/kit/components/Object3DDocumentToolbar.tsx +549 -0
- package/src/kit/components/Object3DDocumentViewport.tsx +58 -0
- package/src/kit/components/StageHost.tsx +2547 -0
- package/src/kit/components/StageOverlays.tsx +21 -0
- package/src/kit/components/StatsOverlay.tsx +78 -0
- package/src/kit/components/ToolObject3DPreview.tsx +39 -0
- package/src/kit/components/ViewportFurniture.tsx +655 -0
- package/src/kit/components/ViewportOverlay.tsx +215 -0
- package/src/kit/components/ViewportShadingMenu.tsx +340 -0
- package/src/kit/components/ViewportViewMenu.tsx +155 -0
- package/src/kit/components/asset-viewers/EntityModelDocument.tsx +121 -0
- package/src/kit/components/asset-viewers/EnvironmentAssetDocument.tsx +440 -0
- package/src/kit/components/asset-viewers/LiveModuleDocument.tsx +395 -0
- package/src/kit/components/asset-viewers/LutAssetDocument.tsx +444 -0
- package/src/kit/components/asset-viewers/ModelAssetDocument.tsx +105 -0
- package/src/kit/components/asset-viewers/Object3DPreview.tsx +356 -0
- package/src/kit/components/asset-viewers/QuarksAssetDocument.tsx +527 -0
- package/src/kit/components/asset-viewers/ShaderAssetDocument.tsx +743 -0
- package/src/kit/components/asset-viewers/three-asset-viewers.tsx +132 -0
- package/src/kit/components/object3d-contribution-surfaces.tsx +33 -0
- package/src/kit/components/stage-keyboard.tsx +40 -0
- package/src/kit/components/stage-overlay-set.tsx +105 -0
- package/src/kit/components/stage-presence-markers.ts +482 -0
- package/src/kit/components/stage-transform-chrome.ts +30 -0
- package/src/kit/components/stage-transform-tools.tsx +73 -0
- package/src/kit/components/stage-view-name.ts +30 -0
- package/src/kit/components/standard-viewport-dressing.ts +1042 -0
- package/src/kit/components/world-root-binding.ts +64 -0
- package/src/kit/constraint-helper.ts +338 -0
- package/src/kit/editor-shell-store.ts +814 -0
- package/src/kit/editor-viewport.ts +6621 -0
- package/src/kit/entity-lod.ts +31 -0
- package/src/kit/entity-object.ts +92 -0
- package/src/kit/hierarchy-mark-reader.ts +74 -0
- package/src/kit/instanced-presentation.ts +164 -0
- package/src/kit/live-module-source.ts +230 -0
- package/src/kit/model-thumbnail.ts +539 -0
- package/src/kit/play-camera-flight.ts +300 -0
- package/src/kit/projection/three.ts +898 -0
- package/src/kit/reflection-probe-helper.ts +142 -0
- package/src/kit/scene-document-viewport.ts +51 -0
- package/src/kit/scene-framing.ts +315 -0
- package/src/kit/scene-view-fog.ts +89 -0
- package/src/kit/spatial-handle-visuals.ts +332 -0
- package/src/kit/stories/three-story-model.ts +66 -0
- package/src/kit/three-canvas-render.ts +44 -0
- package/src/kit/three-hierarchy-row-media.ts +26 -0
- package/src/kit/three-inspection-media.ts +73 -0
- package/src/kit/three-integration.ts +86 -0
- package/src/kit/three-state.ts +33 -0
- package/src/kit/three-viewport/bone-selection-highlight.ts +119 -0
- package/src/kit/three-viewport/camera-fit.ts +41 -0
- package/src/kit/three-viewport/interactive-renderer.ts +132 -0
- package/src/kit/three-viewport/selection-brackets.ts +355 -0
- package/src/kit/three-viewport/selection-outline.ts +333 -0
- package/src/kit/three-viewport/skeleton-helper.ts +61 -0
- package/src/kit/three-viewport/source-color.ts +197 -0
- package/src/kit/three-viewport/studio-environment.ts +96 -0
- package/src/kit/trigger-volume-helper.ts +116 -0
- package/src/kit/viewport-actions.ts +128 -0
- package/src/kit/viewport-authoring-policy.ts +154 -0
- package/src/kit/viewport-commands.ts +318 -0
- package/src/kit/viewport-hotkeys.ts +119 -0
- package/src/kit/viewport-shading-boundary.ts +12 -0
- package/src/kit/viewport-status-facet.ts +53 -0
- package/src/object3d-contributions.ts +494 -0
- package/src/render/viewport-shading.ts +6 -2
- package/src/viewport-api.ts +92 -0
- package/src/viewport-door.ts +237 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE OBJECT3D CONTRIBUTION CONTRACT — Three's own public API for project and package
|
|
3
|
+
* contributions that hand the editor a native three.js graph: the preview and authoring surfaces'
|
|
4
|
+
* props, the document state serializers receive, and the gestures a document owns. A contribution
|
|
5
|
+
* mounts the surfaces from its `surfaces` prop; this module adds them to
|
|
6
|
+
* `ToolContributionSurfaces`, so a contribution that imports these types sees them there, and
|
|
7
|
+
* the Three integration registers what renders them (`kit/three-integration.ts`).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { AuthoringAdapter } from '@volter/editor-project/adapter';
|
|
11
|
+
import type {
|
|
12
|
+
DocumentPersistenceBinding,
|
|
13
|
+
DocumentPersistenceResource,
|
|
14
|
+
} from '@volter/editor-sdk/kit/authoring/object3d-document-persistence';
|
|
15
|
+
import type { PresentationLayer } from '@volter/editor-sdk/kit/viewport-presentation';
|
|
16
|
+
import type { ComponentType } from 'react';
|
|
17
|
+
import type {
|
|
18
|
+
AnimationClip,
|
|
19
|
+
Camera,
|
|
20
|
+
ColorRepresentation,
|
|
21
|
+
Group,
|
|
22
|
+
Intersection,
|
|
23
|
+
Object3D,
|
|
24
|
+
Ray,
|
|
25
|
+
Scene,
|
|
26
|
+
ToneMapping,
|
|
27
|
+
WebGLRenderer,
|
|
28
|
+
} from 'three';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* One disposable native Three.js build supplied to the editor's generic
|
|
32
|
+
* preview surface. This is a host-lifetime boundary, not a model format or
|
|
33
|
+
* procedural-asset base class: project code still constructs ordinary
|
|
34
|
+
* Object3D and AnimationClip instances directly.
|
|
35
|
+
*/
|
|
36
|
+
export interface ToolObject3DPreviewSource {
|
|
37
|
+
readonly root: Object3D;
|
|
38
|
+
/**
|
|
39
|
+
* Optional authored roots for the document hierarchy when `root` is a
|
|
40
|
+
* presentation/lifetime container rather than authored content itself.
|
|
41
|
+
* Framing, ticking and disposal still own `root`; this list only scopes the
|
|
42
|
+
* shared Hierarchy. Omitted means `root`, preserving the native tree.
|
|
43
|
+
*/
|
|
44
|
+
readonly hierarchyRoots?: readonly Object3D[];
|
|
45
|
+
readonly animations?: readonly AnimationClip[];
|
|
46
|
+
/**
|
|
47
|
+
* The source's OWN tick, and the host drives it EXACTLY ONCE per mount — for
|
|
48
|
+
* the bounded design-time settle that gives a physics-owned body the pose it
|
|
49
|
+
* actually rests in (`authoring/design-time-settle.ts`). It is never a frame
|
|
50
|
+
* loop: content time does not advance on a design-time surface, so a source
|
|
51
|
+
* that animates from here shows its settled pose and then holds it. Play is
|
|
52
|
+
* where a world runs.
|
|
53
|
+
*/
|
|
54
|
+
update?(deltaSeconds: number): void;
|
|
55
|
+
/**
|
|
56
|
+
* EVERY CHANGE TO WHAT THE SOURCE DRAWS, announced: the host calls
|
|
57
|
+
* `listener` whenever the source's graph, materials, textures or poses may
|
|
58
|
+
* look different, and returns nothing from it. A source that offers this is
|
|
59
|
+
* drawn only when something changed -- this, the camera, input on the
|
|
60
|
+
* stage, the editor's own state, playback -- so an idle document costs no
|
|
61
|
+
* frames. A source without it is drawn every frame.
|
|
62
|
+
*/
|
|
63
|
+
onChange?(listener: () => void): () => void;
|
|
64
|
+
dispose(): void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Direct native handles for optional project-owned preview presentation. */
|
|
68
|
+
export interface ToolObject3DPreviewContext {
|
|
69
|
+
readonly scene: Scene;
|
|
70
|
+
readonly camera: Camera;
|
|
71
|
+
readonly renderer: WebGLRenderer;
|
|
72
|
+
/**
|
|
73
|
+
* A cloned Object3D graph, never the source root itself. Geometry,
|
|
74
|
+
* materials, and textures are shared read-only with the disposable source;
|
|
75
|
+
* clone a resource before applying preview-only mutations to it.
|
|
76
|
+
*/
|
|
77
|
+
readonly model: Object3D;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface ToolObject3DPreviewExtension {
|
|
81
|
+
/** When present, replaces the viewer's default render call for this frame. */
|
|
82
|
+
render?(deltaSeconds: number): void;
|
|
83
|
+
/** Resize project-owned composers and render targets with the host viewport. */
|
|
84
|
+
resize?(width: number, height: number, pixelRatio: number): void;
|
|
85
|
+
/** Release every light, ground mesh, pass, target, and listener added here. */
|
|
86
|
+
dispose(): void;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface ToolObject3DPreviewProps {
|
|
90
|
+
/** Build a fresh source. The host disposes it after its preview snapshot. */
|
|
91
|
+
readonly build: () => ToolObject3DPreviewSource;
|
|
92
|
+
readonly displayName?: string;
|
|
93
|
+
readonly height?: number | string;
|
|
94
|
+
readonly background?: ColorRepresentation;
|
|
95
|
+
readonly showSkeleton?: boolean;
|
|
96
|
+
readonly cameraDirection?: readonly [number, number, number];
|
|
97
|
+
/** Host diagnostic grid. Defaults off with setupPreview, on otherwise. */
|
|
98
|
+
readonly showGrid?: boolean;
|
|
99
|
+
/** Host studio lights. Defaults off with setupPreview, on otherwise. */
|
|
100
|
+
readonly useDefaultLighting?: boolean;
|
|
101
|
+
/** Explicit renderer exposure. Custom presentation otherwise owns it. */
|
|
102
|
+
readonly exposure?: number;
|
|
103
|
+
/**
|
|
104
|
+
* Optional raw Three.js presentation hook for project-specific lights,
|
|
105
|
+
* ground, or postprocessing. Host grid/studio lights default off when this
|
|
106
|
+
* is present. It is preview chrome and is never exported.
|
|
107
|
+
*/
|
|
108
|
+
readonly setupPreview?: (
|
|
109
|
+
context: ToolObject3DPreviewContext,
|
|
110
|
+
) => ToolObject3DPreviewExtension | undefined;
|
|
111
|
+
readonly active?: boolean;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Per-document overrides of the editor's standard viewport dressing — explicit
|
|
116
|
+
* opt-outs and opt-ins, never re-implementations. Omitted means the standard
|
|
117
|
+
* look (`standard-viewport-dressing.ts`).
|
|
118
|
+
*/
|
|
119
|
+
export interface ToolViewportDressing {
|
|
120
|
+
/** `false` skips the RoomEnvironment IBL — and, with it, the stage's
|
|
121
|
+
* environment INTENSITY: a document that opts out of image-based lighting
|
|
122
|
+
* is lit by its own lights alone. Default on. */
|
|
123
|
+
readonly environment?: boolean;
|
|
124
|
+
/** `false` skips the gradient backdrop. Default on (an explicit flat
|
|
125
|
+
* `background` color or the asset studio stage also skips it). */
|
|
126
|
+
readonly background?: boolean;
|
|
127
|
+
/** `false` skips the key light — and the editor's own design-time light
|
|
128
|
+
* rig with it, because both answer the same question and a document that
|
|
129
|
+
* says "I light myself" is answering it. Default on; a source that
|
|
130
|
+
* authors its own lights skips the key regardless — authored lighting
|
|
131
|
+
* always wins. */
|
|
132
|
+
readonly keyLight?: boolean;
|
|
133
|
+
/** `true` adds the ground grid (content standing on y=0). Default off. */
|
|
134
|
+
readonly grid?: boolean;
|
|
135
|
+
/**
|
|
136
|
+
* An object the STAGE parents to its own camera, so everything under it
|
|
137
|
+
* turns with the view instead of standing still in the world.
|
|
138
|
+
*
|
|
139
|
+
* It exists for VIEW-SPACE LIGHTING — Blender's Solid mode has no world
|
|
140
|
+
* light at all, only four studio lights stated in view space, which is why
|
|
141
|
+
* its shading reads the same however the model is orbited. A directional
|
|
142
|
+
* light here states its direction in the camera's own space (three's camera
|
|
143
|
+
* looks down −Z) and keeps its `target` under this object at the origin.
|
|
144
|
+
*
|
|
145
|
+
* The stage owns the parenting AND the teardown: a document hands the
|
|
146
|
+
* object over and its own `dispose` never touches the camera.
|
|
147
|
+
*/
|
|
148
|
+
readonly viewLocked?: Object3D;
|
|
149
|
+
/**
|
|
150
|
+
* THE STAGE'S VIEW TRANSFORM — three's `WebGLRenderer.toneMapping`. Omitted
|
|
151
|
+
* means the editor's own, ACES.
|
|
152
|
+
*
|
|
153
|
+
* A document whose source system states its own transform passes that one
|
|
154
|
+
* instead: Blender's factory scene is AgX (`view_settings.view_transform`),
|
|
155
|
+
* and this product's Blender RENDER path already photographs through
|
|
156
|
+
* `AgXToneMapping` — so before this existed the viewport was the one
|
|
157
|
+
* surface in the chain running a different curve from the thing it frames.
|
|
158
|
+
* It is not a look preference: the curve decides how a shading range lands
|
|
159
|
+
* on screen, and two curves over one radiance are two different pictures.
|
|
160
|
+
*/
|
|
161
|
+
readonly toneMapping?: ToneMapping;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* A native source graph hosted by the editor's normal Three.js authoring
|
|
166
|
+
* viewport, hierarchy, selection, and Inspector. Unlike Object3DPreview this
|
|
167
|
+
* surface exposes the live graph as the active workspace document context.
|
|
168
|
+
*/
|
|
169
|
+
export interface ToolObject3DAuthoringProps {
|
|
170
|
+
readonly documentId: string;
|
|
171
|
+
readonly sourcePath: string;
|
|
172
|
+
/**
|
|
173
|
+
* Produce the graph to author. Called ONCE PER ACTIVATION, not once per
|
|
174
|
+
* document: the host tears its scene down whenever the document goes inactive
|
|
175
|
+
* (an ordinary tab switch) and calls this again on the way back, disposing
|
|
176
|
+
* whatever the previous call returned. So a caller must pick one of two
|
|
177
|
+
* shapes, and there is no third:
|
|
178
|
+
*
|
|
179
|
+
* - a FACTORY — build a fresh graph every call, and let the returned
|
|
180
|
+
* `dispose` free it (what the model/entity asset documents do); or
|
|
181
|
+
* - an OWNED graph — return the same root every call with an EMPTY `dispose`,
|
|
182
|
+
* and free it from the caller's own lifetime instead (what the 3D
|
|
183
|
+
* components board and the story turntable do, because their graph is
|
|
184
|
+
* async to create).
|
|
185
|
+
*
|
|
186
|
+
* Returning a graph you cannot rebuild synchronously AND a `dispose` that
|
|
187
|
+
* really frees it is the third shape, and it renders a black panel on the
|
|
188
|
+
* second activation.
|
|
189
|
+
*/
|
|
190
|
+
readonly build: () => ToolObject3DPreviewSource;
|
|
191
|
+
readonly displayName?: string;
|
|
192
|
+
/** Explicit flat scene background. Omit it to inherit the editor's standard
|
|
193
|
+
* viewport dressing (environment, gradient backdrop, key light). */
|
|
194
|
+
readonly background?: ColorRepresentation;
|
|
195
|
+
/**
|
|
196
|
+
* The Asset Lab's IMPORT AUDIT on this document's inspector — the Geometry
|
|
197
|
+
* block (triangle counts, GPU estimate, LODs, collision, invalid values)
|
|
198
|
+
* and the Source row. Default on: it is what an imported model's inspector
|
|
199
|
+
* is for. A modeling document that carries its own data panel (the mesh
|
|
200
|
+
* document's Data / Modifiers sections) passes `false`, the way Blender's
|
|
201
|
+
* Object Data tab is the mesh's own, not an importer's report.
|
|
202
|
+
*/
|
|
203
|
+
readonly audit?: boolean;
|
|
204
|
+
readonly cameraDirection?: readonly [number, number, number];
|
|
205
|
+
/** Per-document overrides of the standard viewport dressing. */
|
|
206
|
+
readonly dressing?: ToolViewportDressing;
|
|
207
|
+
/** The kind of stage this view is, for its starting presentation
|
|
208
|
+
* (`@volter/editor-sdk/kit/viewport-presentation`): the document's own kind (`'model'`).
|
|
209
|
+
* Without it the kind is read off the document id's prefix. */
|
|
210
|
+
readonly stageKind?: string;
|
|
211
|
+
/** How far the OPENING view stands back from a fit of the content: `1` fills the view (the
|
|
212
|
+
* default). Frame (numpad .) still fits exactly. */
|
|
213
|
+
readonly openingFit?: number;
|
|
214
|
+
/**
|
|
215
|
+
* THE OPENING VIEW STATED OUTRIGHT, for a document whose file saved one (a .blend's 3D View,
|
|
216
|
+
* where Blender opens it): the pivot, the direction from the pivot to the eye, the view's up,
|
|
217
|
+
* the distance and the projection, in the stage's frame. In place of `cameraDirection` and
|
|
218
|
+
* `openingFit` when given; Frame still fits. The saved field of view is the document's
|
|
219
|
+
* `presentation` (`camera.fov`).
|
|
220
|
+
*/
|
|
221
|
+
readonly openingView?: {
|
|
222
|
+
readonly target: readonly [number, number, number];
|
|
223
|
+
readonly direction: readonly [number, number, number];
|
|
224
|
+
/** The view's own up, which carries its roll. */
|
|
225
|
+
readonly up?: readonly [number, number, number];
|
|
226
|
+
readonly distance: number;
|
|
227
|
+
readonly projection?: 'perspective' | 'orthographic';
|
|
228
|
+
} | null;
|
|
229
|
+
/**
|
|
230
|
+
* THE DOCUMENT'S OWN PRESENTATION LAYER, over the stage's starting values and under a person's
|
|
231
|
+
* choices (`kit/viewport-presentation`): what the file itself says about how it is seen — a
|
|
232
|
+
* `.blend`'s saved lens as `camera.fov`. Read when the document binds.
|
|
233
|
+
*/
|
|
234
|
+
readonly presentation?: PresentationLayer | null;
|
|
235
|
+
/**
|
|
236
|
+
* Optional binding from live native clips back to ordinary project source.
|
|
237
|
+
* Project code only serializes its own TypeScript shape; the editor owns the
|
|
238
|
+
* checksum-guarded write and records it in the canonical project history.
|
|
239
|
+
* Without this binding the animation workspace remains honestly read-only.
|
|
240
|
+
*/
|
|
241
|
+
/**
|
|
242
|
+
* Project-owned serialization for this authored document. Each serializer
|
|
243
|
+
* returns the exact bytes of one ordinary project file; the host writes all
|
|
244
|
+
* resources as one checksum-guarded, failure-atomic history transaction.
|
|
245
|
+
* The project owns the format and the host never interprets its contents.
|
|
246
|
+
*/
|
|
247
|
+
readonly persistence?: ToolObject3DDocumentPersistence;
|
|
248
|
+
/** Project-owned serialization of this document into ONE source module,
|
|
249
|
+
* written through the editor's source seam (see the binding's docs). */
|
|
250
|
+
readonly documentSource?: ToolDocumentSourceBinding;
|
|
251
|
+
/** Replace the default native-tree projection with a semantic adapter. The
|
|
252
|
+
* default adapter is provided for delegation, so a project can add terrain
|
|
253
|
+
* layers, bones, or mesh elements without rebuilding Object3D projection. */
|
|
254
|
+
readonly authoring?: ToolObject3DDocumentAuthoringFactory;
|
|
255
|
+
/** Project-owned direct manipulation hosted by the editor's input and
|
|
256
|
+
* transient-overlay lifecycle. Completed gestures commit `persistence`; an
|
|
257
|
+
* Escape, unmount, thrown callback, or failed write calls `cancel`. */
|
|
258
|
+
readonly interaction?: ToolObject3DDocumentInteraction;
|
|
259
|
+
/**
|
|
260
|
+
* The editor's orange SELECTION SILHOUETTE around whatever the hierarchy
|
|
261
|
+
* has selected. Default on. A document that owns a sub-object editing mode
|
|
262
|
+
* passes `false` while that mode is live: the silhouette is an OBJECT-level
|
|
263
|
+
* affordance, and Blender draws none in Edit Mode. It is live — a document
|
|
264
|
+
* may flip it as its own mode changes, and the outline appears or goes with
|
|
265
|
+
* the next frame.
|
|
266
|
+
*/
|
|
267
|
+
readonly selectionOutline?: boolean;
|
|
268
|
+
readonly active?: boolean;
|
|
269
|
+
/**
|
|
270
|
+
* This document's own counts, drawn in the VIEWPORT OVERLAY under the view
|
|
271
|
+
* and subject lines — Blender's home for them (`sculpting.png`: `Vertices
|
|
272
|
+
* 8` / `Faces 6` below `User Perspective` / `(1) Cube | Cube`, never in the
|
|
273
|
+
* header band). The host owns the block's geometry and ink; the document
|
|
274
|
+
* owns which counts exist and what they are called, because only it knows
|
|
275
|
+
* what it is counting. Omit it and no block is drawn.
|
|
276
|
+
*/
|
|
277
|
+
readonly statistics?: readonly ToolViewportStatistic[];
|
|
278
|
+
/**
|
|
279
|
+
* This document's own SUBJECT LINE, the overlay's second line, drawn as given in place of the
|
|
280
|
+
* host's `<document> | <active object>`. For a document whose source system composes that line
|
|
281
|
+
* itself (Blender's `(1) Collection | Cube`, `draw_selected_name`). Omit it and the host's is
|
|
282
|
+
* drawn.
|
|
283
|
+
*/
|
|
284
|
+
readonly subject?: string;
|
|
285
|
+
/**
|
|
286
|
+
* The name of the grid's step under the subject line, for the world units per DEVICE pixel the
|
|
287
|
+
* view is drawn at, or null for none (Blender's `10 Centimeters`, `draw_grid_unit_name`). The
|
|
288
|
+
* host asks only in an axis-aligned orthographic view, where Blender draws it. Omit it and no
|
|
289
|
+
* line is drawn.
|
|
290
|
+
*/
|
|
291
|
+
readonly gridScale?: (worldPerDevicePixel: number) => string | null;
|
|
292
|
+
/**
|
|
293
|
+
* A CAMERA VIEW: looking through one of the document's own cameras, the way Blender's
|
|
294
|
+
* `view3d.view_camera` (numpad 0, the navigation cluster's camera button) does. The document
|
|
295
|
+
* says which camera and how its view fits a region; the stage owns the toggle, the frame's
|
|
296
|
+
* zoom and offset, and leaving the view. Omit it and the stage has no camera view.
|
|
297
|
+
*/
|
|
298
|
+
readonly cameraView?: ToolCameraViewSource;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** See {@link ToolObject3DAuthoringProps.cameraView}. */
|
|
302
|
+
export interface ToolCameraViewSource {
|
|
303
|
+
/** The camera a camera view entered now would look through, or null when there is none. */
|
|
304
|
+
readonly camera: () => string | null;
|
|
305
|
+
/** The frame zoom a camera view opens at, and the range it keeps to. */
|
|
306
|
+
readonly zoom: { readonly opening: number; readonly min: number; readonly max: number };
|
|
307
|
+
/**
|
|
308
|
+
* The pan after the pointer moves `dx`, `dy` (fractions of the region's width and height,
|
|
309
|
+
* down and right positive) at zoom `zoom`, from `offset`: the source's own pan state, which a
|
|
310
|
+
* camera view opens at `[0, 0]` and hands back to `view` unread.
|
|
311
|
+
*/
|
|
312
|
+
readonly pan: (
|
|
313
|
+
offset: readonly [number, number],
|
|
314
|
+
zoom: number,
|
|
315
|
+
dx: number,
|
|
316
|
+
dy: number,
|
|
317
|
+
) => readonly [number, number];
|
|
318
|
+
/** Told the camera a camera view is looking through (null when none), so the document can
|
|
319
|
+
* stand down its own drawing of it: the view's frame is its border. */
|
|
320
|
+
readonly showing?: (camera: string | null) => void;
|
|
321
|
+
/**
|
|
322
|
+
* Move `camera` to a pose in the stage's frame (the eye, and its rotation looking down -Z),
|
|
323
|
+
* keeping its scale: what a LOCKED camera view does as it is navigated (Blender's
|
|
324
|
+
* `View3D.lock_camera`, `ED_view3d_camera_lock_sync`). `final` is the navigation's end, the
|
|
325
|
+
* one write a history records. Omit it and the view has no lock.
|
|
326
|
+
*/
|
|
327
|
+
readonly setPose?: (
|
|
328
|
+
camera: string,
|
|
329
|
+
position: readonly [number, number, number],
|
|
330
|
+
quaternion: readonly [number, number, number, number],
|
|
331
|
+
final: boolean,
|
|
332
|
+
) => void | Promise<void>;
|
|
333
|
+
/**
|
|
334
|
+
* `camera`'s view on a region `width` × `height` pixels, at frame zoom `zoom` and pan
|
|
335
|
+
* `offset`; null when that camera is gone.
|
|
336
|
+
*/
|
|
337
|
+
readonly view: (
|
|
338
|
+
camera: string,
|
|
339
|
+
region: { readonly width: number; readonly height: number },
|
|
340
|
+
zoom: number,
|
|
341
|
+
offset: readonly [number, number],
|
|
342
|
+
) => ToolCameraView | null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** One camera view, in the stage's frame. */
|
|
346
|
+
export interface ToolCameraView {
|
|
347
|
+
readonly name: string;
|
|
348
|
+
/** The eye, looking down its own -Z with +Y up. */
|
|
349
|
+
readonly position: readonly [number, number, number];
|
|
350
|
+
readonly quaternion: readonly [number, number, number, number];
|
|
351
|
+
readonly projection: 'perspective' | 'orthographic';
|
|
352
|
+
/** The window: at unit distance for a perspective view, in world units for an orthographic
|
|
353
|
+
* one; up positive. */
|
|
354
|
+
readonly window: { readonly left: number; readonly right: number; readonly top: number; readonly bottom: number };
|
|
355
|
+
readonly near: number;
|
|
356
|
+
readonly far: number;
|
|
357
|
+
/** The camera's frame on the region, as fractions of its width and height from the top left. */
|
|
358
|
+
readonly frame: { readonly left: number; readonly top: number; readonly width: number; readonly height: number };
|
|
359
|
+
/** Outside the frame: its colour and opacity (0 draws none). */
|
|
360
|
+
readonly passepartout: { readonly color: string; readonly opacity: number };
|
|
361
|
+
/** The frame's edge: a solid line under a dashed one, and the colour of the dashed box one
|
|
362
|
+
* pixel outside them while the view is locked to the camera; each a CSS colour. */
|
|
363
|
+
readonly border: { readonly solid: string; readonly dashed: string; readonly locked: string };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** One row of the viewport overlay's statistics block: Blender's label column
|
|
367
|
+
* and its value column. */
|
|
368
|
+
export interface ToolViewportStatistic {
|
|
369
|
+
/** Stable row identity — the React key, never drawn. */
|
|
370
|
+
readonly id: string;
|
|
371
|
+
/** Blender's own noun where one exists (`Vertices`, `Faces`). */
|
|
372
|
+
readonly label: string;
|
|
373
|
+
readonly value: string;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** The native document state handed back to project-owned serializers. */
|
|
377
|
+
export interface ToolObject3DDocumentState {
|
|
378
|
+
readonly root: Object3D;
|
|
379
|
+
readonly animations: readonly AnimationClip[];
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** One project-owned artifact participating in an Asset Lab commit. */
|
|
383
|
+
export type ToolObject3DDocumentResource = DocumentPersistenceResource<ToolObject3DDocumentState>;
|
|
384
|
+
|
|
385
|
+
export type ToolObject3DDocumentPersistence = DocumentPersistenceBinding<ToolObject3DDocumentState>;
|
|
386
|
+
|
|
387
|
+
export interface ToolObject3DDocumentAuthoringContext extends ToolObject3DDocumentState {
|
|
388
|
+
readonly documentId: string;
|
|
389
|
+
readonly sourcePath: string;
|
|
390
|
+
readonly scene: Scene;
|
|
391
|
+
readonly defaultAdapter: AuthoringAdapter;
|
|
392
|
+
/**
|
|
393
|
+
* Serialize this project-owned document through its declared persistence
|
|
394
|
+
* resources and record the change in canonical history. Rejects when the
|
|
395
|
+
* document is read-only or the atomic write fails.
|
|
396
|
+
*/
|
|
397
|
+
readonly commit: (label?: string) => Promise<boolean>;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export interface ToolObject3DDocumentAuthoring {
|
|
401
|
+
readonly adapter: AuthoringAdapter;
|
|
402
|
+
dispose?(): void;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export type ToolObject3DDocumentAuthoringFactory = (
|
|
406
|
+
context: ToolObject3DDocumentAuthoringContext,
|
|
407
|
+
) => ToolObject3DDocumentAuthoring;
|
|
408
|
+
|
|
409
|
+
/** Adapter-native hit data for a project-owned Asset Lab gesture. */
|
|
410
|
+
export interface ToolObject3DPointerEvent {
|
|
411
|
+
readonly pointerId: number;
|
|
412
|
+
readonly button: number;
|
|
413
|
+
readonly buttons: number;
|
|
414
|
+
readonly clientX: number;
|
|
415
|
+
readonly clientY: number;
|
|
416
|
+
readonly altKey: boolean;
|
|
417
|
+
readonly ctrlKey: boolean;
|
|
418
|
+
readonly metaKey: boolean;
|
|
419
|
+
readonly shiftKey: boolean;
|
|
420
|
+
readonly ray: Ray;
|
|
421
|
+
readonly hits: readonly Intersection<Object3D>[];
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/** One gesture owns exact rollback to its pre-begin document state. */
|
|
425
|
+
export interface ToolObject3DGesture {
|
|
426
|
+
readonly label?: string;
|
|
427
|
+
update(event: ToolObject3DPointerEvent): void;
|
|
428
|
+
/** Finalize the project-owned native state before the host serializes it. */
|
|
429
|
+
commit(event: ToolObject3DPointerEvent): void | Promise<void>;
|
|
430
|
+
/** Restore exact pre-begin native state. Must be safe after partial commit. */
|
|
431
|
+
cancel(): void | Promise<void>;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export interface ToolObject3DInteractionContext {
|
|
435
|
+
readonly root: Object3D;
|
|
436
|
+
readonly scene: Scene;
|
|
437
|
+
readonly renderer: WebGLRenderer;
|
|
438
|
+
/** Host-owned group removed on document teardown. Project code owns GPU
|
|
439
|
+
* resources it adds and releases them from the extension's `dispose`. */
|
|
440
|
+
readonly overlay: Group;
|
|
441
|
+
/** Live camera getter; projection can change while the document is open. */
|
|
442
|
+
readonly camera: () => Camera;
|
|
443
|
+
/** False means gestures will not begin because no persistence binding exists. */
|
|
444
|
+
readonly writable: boolean;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
export interface ToolObject3DInteractionExtension {
|
|
448
|
+
/** Return null to yield this pointer to the ordinary viewport controls. */
|
|
449
|
+
begin(event: ToolObject3DPointerEvent): ToolObject3DGesture | null;
|
|
450
|
+
/**
|
|
451
|
+
* Optional pre-sample phase. Restore any transforms changed by the previous
|
|
452
|
+
* frame's refinement here; the host advances content time after this call
|
|
453
|
+
* and invokes {@link update} with the resulting native pose.
|
|
454
|
+
*/
|
|
455
|
+
prepareFrame?(deltaSeconds: number): void;
|
|
456
|
+
/**
|
|
457
|
+
* Optional per-frame refinement after the host has advanced content time.
|
|
458
|
+
* Use this for project-owned constraints and overlay presentation.
|
|
459
|
+
*/
|
|
460
|
+
update?(deltaSeconds: number): void;
|
|
461
|
+
dispose(): void;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
export interface ToolObject3DDocumentInteraction {
|
|
465
|
+
setup(context: ToolObject3DInteractionContext): ToolObject3DInteractionExtension;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Project-owned serialization of a WHOLE Asset Lab document into one ordinary
|
|
470
|
+
* source file, for a
|
|
471
|
+
* document whose truth is a TypeScript module (a modeling session's mesh
|
|
472
|
+
* module). The editor owns the checksum-guarded whole-file write through the
|
|
473
|
+
* source seam and records it in canonical history; executable source never
|
|
474
|
+
* travels through {@link ToolObject3DAuthoringProps.persistence}'s resource
|
|
475
|
+
* route, which the server refuses by rule. A document declares either this or
|
|
476
|
+
* `persistence`, never both. MUST be referentially stable across renders.
|
|
477
|
+
*/
|
|
478
|
+
export interface ToolDocumentSourceBinding {
|
|
479
|
+
/** Project-relative source file below src/. */
|
|
480
|
+
readonly path: string;
|
|
481
|
+
/** Undo/redo label shown by the editor history. */
|
|
482
|
+
readonly label?: string;
|
|
483
|
+
/** The exact bytes of the module for this document state — or `null` for
|
|
484
|
+
* "nothing changed, write nothing", so a gesture that ended without an
|
|
485
|
+
* edit leaves the file byte-for-byte alone. */
|
|
486
|
+
readonly serialize: (document: ToolObject3DDocumentState) => string | null;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
declare module '@volter/editor-sdk/contributions' {
|
|
490
|
+
interface ToolContributionSurfaces {
|
|
491
|
+
readonly Object3DPreview: ComponentType<ToolObject3DPreviewProps>;
|
|
492
|
+
readonly Object3DAuthoring: ComponentType<ToolObject3DAuthoringProps>;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
@@ -4,6 +4,10 @@ import { createNeutralMatcapTexture } from './matcap-texture';
|
|
|
4
4
|
/** Temporary developer-facing shading modes. These never become scene data. */
|
|
5
5
|
export type ViewportShadingMode =
|
|
6
6
|
| 'solid'
|
|
7
|
+
/** Authored materials, as `solid`; a stage lights it by a preview (Blender's Material Preview). */
|
|
8
|
+
| 'preview'
|
|
9
|
+
/** Authored materials, as `solid`; a stage lights it by the scene (Blender's Rendered). */
|
|
10
|
+
| 'rendered'
|
|
7
11
|
| 'clay'
|
|
8
12
|
| 'unlit'
|
|
9
13
|
| 'wireframe'
|
|
@@ -65,7 +69,7 @@ export class ViewportShadingRenderer {
|
|
|
65
69
|
draw: () => void,
|
|
66
70
|
include: (mesh: THREE.Mesh) => boolean = () => true,
|
|
67
71
|
): void {
|
|
68
|
-
if (mode === 'solid') {
|
|
72
|
+
if (mode === 'solid' || mode === 'preview' || mode === 'rendered') {
|
|
69
73
|
draw();
|
|
70
74
|
return;
|
|
71
75
|
}
|
|
@@ -107,7 +111,7 @@ export class ViewportShadingRenderer {
|
|
|
107
111
|
|
|
108
112
|
private _materialFor(
|
|
109
113
|
material: THREE.Material,
|
|
110
|
-
mode: Exclude<ViewportShadingMode, 'solid'>,
|
|
114
|
+
mode: Exclude<ViewportShadingMode, 'solid' | 'preview' | 'rendered'>,
|
|
111
115
|
): THREE.Material {
|
|
112
116
|
if (mode === 'normals') return this._normals;
|
|
113
117
|
if (mode === 'overdraw') return this._overdraw;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE AUTHORED THREE VIEWPORT'S PUBLIC SHAPE — what a lane that mounts something reaches through
|
|
3
|
+
* `viewport-door.ts`: the rig a camera flight drives, the frame loop it rides, the helpers it
|
|
4
|
+
* shows, every mounted stage, and the one verb that swaps the viewport's subject for a live
|
|
5
|
+
* instance's roots (Play's adoption of the running scene).
|
|
6
|
+
*/
|
|
7
|
+
import type { MountedRoot } from '@volter/editor-project/adapter/root-adapter';
|
|
8
|
+
import type * as THREE from 'three';
|
|
9
|
+
|
|
10
|
+
/** A live instance's roots, presented as the authored viewport's subject. */
|
|
11
|
+
export interface ViewportPresentation {
|
|
12
|
+
/** The root whose native subject the viewport shows. */
|
|
13
|
+
readonly worldId: string;
|
|
14
|
+
/** Restores the viewport's prior subject; idempotent. */
|
|
15
|
+
dispose(): void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** The authored viewport's camera rig, while a viewport is mounted. */
|
|
19
|
+
export interface ViewportRig {
|
|
20
|
+
readonly camera: THREE.PerspectiveCamera;
|
|
21
|
+
/** The camera the stage draws with NOW: {@link camera}, or the orthographic one in an
|
|
22
|
+
* orthographic view. What a document lighting or picking in step with the picture needs. */
|
|
23
|
+
drawCamera(): THREE.Camera;
|
|
24
|
+
readonly orbit: { readonly target: THREE.Vector3; enabled: boolean; update(): void };
|
|
25
|
+
/** The editor's own scene (helpers live on its editor layer). */
|
|
26
|
+
readonly scene: THREE.Scene;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* ONE MOUNTED 3D STAGE, named by the document it draws. Every 3D document
|
|
31
|
+
* mounts a stage of its own (ARCHITECTURE-CORE §One stage); this is the door
|
|
32
|
+
* to a particular one, where the viewport door's singular members
|
|
33
|
+
* are the door to whichever is primary.
|
|
34
|
+
*/
|
|
35
|
+
export interface ViewportStage {
|
|
36
|
+
/** The workspace document this stage draws. */
|
|
37
|
+
readonly documentId: string;
|
|
38
|
+
rig(): ViewportRig;
|
|
39
|
+
/** As the viewport door's `setHelper`, on this stage alone. */
|
|
40
|
+
setHelper(kind: string, object: THREE.Object3D | null): void;
|
|
41
|
+
/** Runs after THIS stage's own per-frame update; the return unsubscribes. */
|
|
42
|
+
onFrame(fn: (dtSeconds: number) => void): () => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* THE AUTHORED VIEWPORT, as a lane that mounts something reaches it: the rig
|
|
47
|
+
* a camera flight drives, the frame loop it rides, and the one verb that
|
|
48
|
+
* swaps the viewport's subject for a live instance's roots (Play's adoption
|
|
49
|
+
* of the running scene). Which native surface the viewport renders is the
|
|
50
|
+
* host's; a lane that finds no rig (a headless tab, a project with no Three
|
|
51
|
+
* root) mounts without one.
|
|
52
|
+
*
|
|
53
|
+
* THE SINGULAR MEMBERS ARE THE PRIMARY STAGE — the stage presenting live
|
|
54
|
+
* roots if one is, else the FOCUSED stage, else the first bound. A camera
|
|
55
|
+
* flight and Play's adoption want exactly that one, which is why they read
|
|
56
|
+
* here and never enumerate.
|
|
57
|
+
*
|
|
58
|
+
* {@link stages} is EVERY mounted 3D stage. A helper is an `Object3D` and an
|
|
59
|
+
* `Object3D` has one parent, so there is no "set it once for all stages": a
|
|
60
|
+
* helper wanted on every stage is set on each stage through `stages()`, and
|
|
61
|
+
* `onStages` is how a lane keeps up with stages that mount and unmount after
|
|
62
|
+
* it made that pass.
|
|
63
|
+
*/
|
|
64
|
+
/** A mounted adapter surface offered to the viewport. Execution, input and
|
|
65
|
+
* ticking stay with the caller; presentation requires only identity and mount.
|
|
66
|
+
* A game's richer root instance can satisfy this interface directly.
|
|
67
|
+
*/
|
|
68
|
+
export interface ViewportRoot {
|
|
69
|
+
readonly id: string;
|
|
70
|
+
readonly mounted: MountedRoot;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface ViewportDoor {
|
|
74
|
+
rig(): ViewportRig | null;
|
|
75
|
+
/** Null when the viewport has no subject among `roots` (no Three root). */
|
|
76
|
+
presentRoots(roots: readonly ViewportRoot[]): ViewportPresentation | null;
|
|
77
|
+
/** Runs after the viewport's own per-frame update; the return unsubscribes. */
|
|
78
|
+
onFrame(fn: (dtSeconds: number) => void): () => void;
|
|
79
|
+
/**
|
|
80
|
+
* Show an editor-only helper object of `kind` in the authored viewport (a
|
|
81
|
+
* baked navmesh's debug mesh), replacing the previous one of that kind;
|
|
82
|
+
* null clears it. Visibility follows the host's Helpers menu: a kind the
|
|
83
|
+
* menu lists (`navmesh`) toggles on its own, any other follows the master
|
|
84
|
+
* toggle. Kept across viewport remounts.
|
|
85
|
+
*/
|
|
86
|
+
setHelper(kind: string, object: THREE.Object3D | null): void;
|
|
87
|
+
/** Every mounted 3D stage, in bind order. */
|
|
88
|
+
stages(): readonly ViewportStage[];
|
|
89
|
+
/** Fires whenever a stage mounts or unmounts; the return unsubscribes. */
|
|
90
|
+
onStages(fn: (stages: readonly ViewportStage[]) => void): () => void;
|
|
91
|
+
}
|
|
92
|
+
|