@vctrl/hooks 0.25.1 → 1.0.0
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/CHANGELOG.md +43 -0
- package/README.md +110 -67
- package/index.cjs +1 -0
- package/index.d.ts +2 -1
- package/index.es.js +4 -4
- package/package.json +18 -10
- package/{chunk-CMqjfN_6.cjs → rolldown-runtime-BocRIvOZ.cjs} +1 -1
- package/use-export-model/use-export-model.d.ts +1 -1
- package/use-export-model-CORoAKbf.cjs +1 -0
- package/{use-export-model-efFsSWe-.js → use-export-model-DXesLypI.js} +1 -1
- package/use-export-model.cjs +1 -0
- package/use-export-model.es.js +1 -1
- package/use-load-model/error-helpers.d.ts +5 -0
- package/use-load-model/file-loaders.d.ts +11 -12
- package/use-load-model/index.d.ts +1 -1
- package/use-load-model/load-context.d.ts +17 -0
- package/use-load-model/optimizer-ingest.d.ts +8 -0
- package/use-load-model/optimizer-integration.d.ts +3 -4
- package/use-load-model/scene-loaders.d.ts +15 -0
- package/use-load-model/state.d.ts +14 -14
- package/use-load-model/types.d.ts +154 -131
- package/use-load-model/use-load-model.d.ts +12 -8
- package/use-load-model/utils/index.d.ts +1 -1
- package/use-load-model/utils/reconstruct-files.d.ts +27 -5
- package/use-load-model/utils/resolve-scene-payload.d.ts +9 -0
- package/use-load-model-CRDVzrPC.js +635 -0
- package/use-load-model-CZCi6bPM.cjs +1 -0
- package/use-load-model.cjs +1 -0
- package/use-load-model.es.js +1 -1
- package/use-optimize-model/index.d.ts +1 -0
- package/use-optimize-model/types.d.ts +3 -1
- package/use-optimize-model/use-optimize-model.d.ts +8 -5
- package/use-optimize-model-Fwew6qD8.cjs +1 -0
- package/{use-optimize-model-BCyM6An0.js → use-optimize-model-sNXkORNg.js} +93 -76
- package/use-optimize-model.cjs +1 -0
- package/use-optimize-model.es.js +2 -2
- package/utils/server-communication.d.ts +0 -5
- package/index.cjs.js +0 -1
- package/use-export-model-yQWV3O3H.cjs +0 -1
- package/use-export-model.cjs.js +0 -1
- package/use-load-model/event-system.d.ts +0 -7
- package/use-load-model-B8oqou4I.cjs +0 -1
- package/use-load-model-C88r8-HE.js +0 -585
- package/use-load-model.cjs.js +0 -1
- package/use-optimize-model-YAGNlhUN.cjs +0 -1
- package/use-optimize-model.cjs.js +0 -1
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { ModelFileTypes } from '../../../core/src/model-loader/index.ts';
|
|
2
|
-
import { Object3D } from 'three';
|
|
2
|
+
import { AnimationClip, Object3D } from 'three';
|
|
3
3
|
import { useOptimizeModel } from '../use-optimize-model';
|
|
4
|
-
import {
|
|
5
|
-
import { initialState } from './state';
|
|
6
|
-
import { ServerOptions, ServerSceneData } from '../../../core/src/index.ts';
|
|
4
|
+
import { ServerOptions, ServerSceneData, ServerScenePayload } from '../../../core/src/index.ts';
|
|
7
5
|
export type { ExtendedGLTFDocument, SceneAssetDataEntry, SerializedSceneAssetDataMap as SceneAssetDataMap, ServerSceneData, ServerScenePayload } from '../../../core/src/index.ts';
|
|
8
6
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* What the loader accepts as a user's selection.
|
|
8
|
+
*
|
|
9
|
+
* NOT the drag-and-drop path, which this was called for years. A folder dropped
|
|
10
|
+
* on a page arrives as plain `File` objects - react-dropzone has already walked
|
|
11
|
+
* it - and `expandDirectories` claims the path each one carries. The
|
|
12
|
+
* `FileSystemDirectoryHandle` branch is for a caller that opened
|
|
13
|
+
* `showDirectoryPicker()` itself and has a handle rather than files, which is a
|
|
14
|
+
* real capability and the reason the union is wider than `File[]`.
|
|
11
15
|
*/
|
|
12
16
|
export type InputFileOrDirectory = (File | FileSystemDirectoryHandle)[];
|
|
13
17
|
/**
|
|
@@ -16,7 +20,9 @@ export type InputFileOrDirectory = (File | FileSystemDirectoryHandle)[];
|
|
|
16
20
|
export interface ModelFile {
|
|
17
21
|
/** The Three.js Object3D scene containing the loaded model */
|
|
18
22
|
model: Object3D;
|
|
19
|
-
/**
|
|
23
|
+
/** Animation clips carried by the model. Absent or empty when it has none. */
|
|
24
|
+
animations?: AnimationClip[];
|
|
25
|
+
/** The file type/format of the model (glTF, GLB, STL, FBX, OBJ). */
|
|
20
26
|
type: ModelFileTypes;
|
|
21
27
|
/** The original filename of the model */
|
|
22
28
|
name: string;
|
|
@@ -26,77 +32,144 @@ export interface ModelFile {
|
|
|
26
32
|
sourceTextureBytes?: number;
|
|
27
33
|
}
|
|
28
34
|
/**
|
|
29
|
-
*
|
|
35
|
+
* Where a model comes from. Every load in the platform is one of these three,
|
|
36
|
+
* and they all go through the single `load(source)` entry point.
|
|
30
37
|
*/
|
|
31
|
-
export
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
/** Whether to automatically apply scene settings (default: true) */
|
|
37
|
-
applySettings?: boolean;
|
|
38
|
-
/** 'direct' parses glTF JSON straight with GLTFLoader (view-only fast path). */
|
|
39
|
-
parseMode?: 'document' | 'direct';
|
|
38
|
+
export type ModelSource =
|
|
39
|
+
/** Files or folders picked by the user (glTF + assets, GLB, STL, FBX, OBJ). */
|
|
40
|
+
{
|
|
41
|
+
kind: 'files';
|
|
42
|
+
files: InputFileOrDirectory;
|
|
40
43
|
}
|
|
41
44
|
/**
|
|
42
|
-
*
|
|
45
|
+
* A scene payload the caller already has in memory (route aggregate, IDB
|
|
46
|
+
* draft). Binary assets may be referenced rather than inlined; the loader
|
|
47
|
+
* fetches them.
|
|
43
48
|
*/
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
| {
|
|
50
|
+
kind: 'scene-data';
|
|
46
51
|
sceneId?: string;
|
|
47
|
-
|
|
48
|
-
sceneData: ServerSceneData;
|
|
49
|
-
/** Whether to automatically apply scene settings (default: true) */
|
|
50
|
-
applySettings?: boolean;
|
|
52
|
+
sceneData: ServerScenePayload;
|
|
51
53
|
/** 'direct' parses glTF JSON straight with GLTFLoader (view-only fast path). */
|
|
52
54
|
parseMode?: 'document' | 'direct';
|
|
53
55
|
}
|
|
56
|
+
/** A scene fetched from the API by id. */
|
|
57
|
+
| {
|
|
58
|
+
kind: 'server';
|
|
59
|
+
sceneId: string;
|
|
60
|
+
serverOptions?: ServerOptions;
|
|
61
|
+
parseMode?: 'document' | 'direct';
|
|
62
|
+
};
|
|
63
|
+
export type ModelSourceKind = ModelSource['kind'];
|
|
54
64
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
65
|
+
* The loader's state, as one value.
|
|
66
|
+
*
|
|
67
|
+
* `status === 'ready'` and `file !== null` are the same fact by construction, so
|
|
68
|
+
* a consumer cannot end up rendering "no model" and "finished loading" at once.
|
|
69
|
+
* `file` and `error` are present on every variant so callers can read them
|
|
70
|
+
* without narrowing; only their types change.
|
|
57
71
|
*/
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
72
|
+
interface ModelStateShape {
|
|
73
|
+
progress: number;
|
|
74
|
+
/** The scene payload the model came from, for sources that have one. */
|
|
75
|
+
sceneData?: ServerSceneData;
|
|
62
76
|
sceneId?: string;
|
|
63
77
|
}
|
|
78
|
+
export type ModelState = (ModelStateShape & {
|
|
79
|
+
status: 'empty';
|
|
80
|
+
file: null;
|
|
81
|
+
error: null;
|
|
82
|
+
source: null;
|
|
83
|
+
}) | (ModelStateShape & {
|
|
84
|
+
status: 'loading';
|
|
85
|
+
file: null;
|
|
86
|
+
error: null;
|
|
87
|
+
source: ModelSourceKind;
|
|
88
|
+
}) | (ModelStateShape & {
|
|
89
|
+
status: 'ready';
|
|
90
|
+
file: ModelFile;
|
|
91
|
+
error: null;
|
|
92
|
+
source: ModelSourceKind;
|
|
93
|
+
}) | (ModelStateShape & {
|
|
94
|
+
status: 'error';
|
|
95
|
+
file: null;
|
|
96
|
+
error: StructuredLoadError;
|
|
97
|
+
source: ModelSourceKind;
|
|
98
|
+
});
|
|
64
99
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
100
|
+
* What `load` resolves to: the state it produced, and whether it still counts.
|
|
101
|
+
*
|
|
102
|
+
* A LOAD THAT LOST IS INDISTINGUISHABLE FROM ONE THAT WON, without this. The
|
|
103
|
+
* hook retires a superseded load's *state* by token, so it can never overwrite
|
|
104
|
+
* the model that replaced it - but the promise still resolves, with a `ready`
|
|
105
|
+
* state carrying a real `ModelFile`, byte-identical to the winner's. A caller
|
|
106
|
+
* acting on the resolved value rather than on the rendered state therefore had
|
|
107
|
+
* no way to tell the two apart, and every one of them had to keep a second
|
|
108
|
+
* clock of its own to compensate. Two already did, differently: a monotonic
|
|
109
|
+
* counter on the converter page and an effect-cleanup flag in `useSceneModel`.
|
|
110
|
+
*
|
|
111
|
+
* `stillCurrent` is that clock, published once. It reads the same token
|
|
112
|
+
* `setState` is gated on, so it stays truthful at any later point - including
|
|
113
|
+
* after awaits the loader knows nothing about, which is what a caller that
|
|
114
|
+
* exports or encodes the result actually needs. `reset()` claims a token too,
|
|
115
|
+
* so a load in flight when the user clears the model also reports false.
|
|
116
|
+
*
|
|
117
|
+
* It answers about *this* load, so hold the resolved value and ask it again
|
|
118
|
+
* rather than caching the boolean.
|
|
67
119
|
*/
|
|
68
|
-
export
|
|
69
|
-
/**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
120
|
+
export type LoadOutcome = ModelState & {
|
|
121
|
+
/** Whether this load is still the newest one, asked now. */
|
|
122
|
+
stillCurrent: () => boolean;
|
|
123
|
+
};
|
|
124
|
+
/** Optional hooks into a load, for callers that need more than its result. */
|
|
125
|
+
export interface LoadOptions {
|
|
126
|
+
/**
|
|
127
|
+
* Called the moment the parsed model reaches the screen.
|
|
128
|
+
*
|
|
129
|
+
* WHY THIS EXISTS, AND WHY `await load(...)` IS NOT THE SAME MOMENT. The
|
|
130
|
+
* loaders publish as soon as the model is parsed and then await the
|
|
131
|
+
* optimizer ingest, so `load` resolves one ingest *after* the viewer changed.
|
|
132
|
+
* A caller that adopts on the resolved value therefore shows model B on the
|
|
133
|
+
* stage while everything it prints around it - the file name, the byte
|
|
134
|
+
* count, a size comparison, a live download button - still describes model A,
|
|
135
|
+
* for as long as ingesting B takes.
|
|
136
|
+
*
|
|
137
|
+
* `stillCurrent` cannot close that window: during it, B genuinely is the
|
|
138
|
+
* current load. The gap is between published and adopted, so the fix is to
|
|
139
|
+
* make those one event.
|
|
140
|
+
*
|
|
141
|
+
* **Called only for a load that is still the current one**, so there is
|
|
142
|
+
* nothing to re-check on entry. What can still go stale is whatever the
|
|
143
|
+
* callback goes on to do, so hold the outcome and ask its `stillCurrent()`
|
|
144
|
+
* again after an await of its own.
|
|
145
|
+
*
|
|
146
|
+
* Optional, and additive: a caller that does not pass it behaves exactly as
|
|
147
|
+
* before. Delaying the publish instead would have closed the same gap by
|
|
148
|
+
* making the viewer wait on work it does not need, which is the trade this
|
|
149
|
+
* deliberately does not make.
|
|
150
|
+
*/
|
|
151
|
+
onPublish?: (outcome: LoadOutcome) => void;
|
|
77
152
|
}
|
|
78
153
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
154
|
+
* A model parsed into Three.js, plus whatever scene payload produced it.
|
|
155
|
+
* Internal hand-off between the per-source loaders and the hook.
|
|
81
156
|
*/
|
|
82
|
-
export
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
payload: boolean;
|
|
88
|
-
} | {
|
|
89
|
-
type: 'set-progress';
|
|
90
|
-
payload: number;
|
|
91
|
-
} | {
|
|
92
|
-
type: 'reset-state';
|
|
93
|
-
};
|
|
157
|
+
export interface LoadedModel {
|
|
158
|
+
file: ModelFile;
|
|
159
|
+
sceneId?: string;
|
|
160
|
+
sceneData?: ServerSceneData;
|
|
161
|
+
}
|
|
94
162
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
163
|
+
* Result of a scene load operation based on server data.
|
|
164
|
+
* Combines the loaded model with its settings.
|
|
97
165
|
*/
|
|
98
|
-
export
|
|
99
|
-
|
|
166
|
+
export interface SceneLoadResult extends ServerSceneData {
|
|
167
|
+
/** The loaded model file */
|
|
168
|
+
file: ModelFile;
|
|
169
|
+
/** Scene ID that was loaded */
|
|
170
|
+
sceneId?: string;
|
|
171
|
+
}
|
|
172
|
+
export type ViewerLoadErrorCode = 'unsupported_format' | 'multiple_models' | 'binary_load_failed' | 'gltf_load_failed' | 'missing_assets' | 'server_load_failed' | 'not_found' | 'quota_exceeded' | 'unknown';
|
|
100
173
|
export interface StructuredLoadError {
|
|
101
174
|
code: ViewerLoadErrorCode;
|
|
102
175
|
message: string;
|
|
@@ -105,88 +178,43 @@ export interface StructuredLoadError {
|
|
|
105
178
|
cause?: unknown;
|
|
106
179
|
context?: Record<string, unknown>;
|
|
107
180
|
}
|
|
108
|
-
/**
|
|
109
|
-
* Maps event types to their corresponding data payloads.
|
|
110
|
-
* Ensures type safety when handling events.
|
|
111
|
-
*/
|
|
112
|
-
export type EventData = {
|
|
113
|
-
/** Array of model files when multiple are detected */
|
|
114
|
-
'multiple-models': File[];
|
|
115
|
-
/** Array of unsupported files */
|
|
116
|
-
'not-loaded-files': File[];
|
|
117
|
-
/** No data for load start event */
|
|
118
|
-
'load-start': null;
|
|
119
|
-
/** Progress value (0-100) */
|
|
120
|
-
'load-progress': number;
|
|
121
|
-
/** The loaded model file data */
|
|
122
|
-
'load-complete': LoadData['file'];
|
|
123
|
-
/** No data for reset event */
|
|
124
|
-
'load-reset': null;
|
|
125
|
-
/** Normalized error payload when loading fails */
|
|
126
|
-
'load-error': StructuredLoadError | Error | unknown;
|
|
127
|
-
/** Scene ID being loaded from server */
|
|
128
|
-
'server-load-start': string;
|
|
129
|
-
/** Complete scene load result */
|
|
130
|
-
'server-load-complete': SceneLoadResult;
|
|
131
|
-
/** Normalized error payload during server scene loading */
|
|
132
|
-
'server-load-error': StructuredLoadError | Error | unknown;
|
|
133
|
-
};
|
|
134
|
-
/**
|
|
135
|
-
* Type-safe event handler function.
|
|
136
|
-
* @template T - The event type being handled
|
|
137
|
-
*/
|
|
138
|
-
export type EventHandler<T extends EventTypes> = (data?: EventData[T]) => void;
|
|
139
181
|
/**
|
|
140
182
|
* Return type for the useLoadModel hook.
|
|
141
183
|
* Conditionally includes optimizer integration based on whether an optimizer was provided.
|
|
142
184
|
*
|
|
143
185
|
* @template HasOptimizer - Boolean indicating if optimizer integration is included
|
|
144
186
|
*/
|
|
145
|
-
export type UseLoadModelReturn<HasOptimizer extends boolean> =
|
|
146
|
-
/**
|
|
147
|
-
|
|
148
|
-
* @param event - The event type to listen for
|
|
149
|
-
* @param handler - Callback function to handle the event
|
|
150
|
-
*/
|
|
151
|
-
on: typeof eventSystem.on;
|
|
152
|
-
/**
|
|
153
|
-
* Unsubscribe from model loading events.
|
|
154
|
-
* @param event - The event type to stop listening for
|
|
155
|
-
* @param handler - The callback function to remove
|
|
156
|
-
*/
|
|
157
|
-
off: typeof eventSystem.off;
|
|
158
|
-
/**
|
|
159
|
-
* Load 3D model files from File objects or directory handles.
|
|
160
|
-
* Supports GLTF, GLB, and USDZ formats with associated assets.
|
|
161
|
-
*/
|
|
162
|
-
load: (filesOrDirectories: InputFileOrDirectory) => Promise<void>;
|
|
163
|
-
/**
|
|
164
|
-
* Load a scene from already-resolved scene data.
|
|
165
|
-
* Preserves original asset payload semantics and bypasses upload-style file processing.
|
|
166
|
-
*/
|
|
167
|
-
loadFromData: (options: SceneDataLoadOptions) => Promise<SceneLoadResult>;
|
|
187
|
+
export type UseLoadModelReturn<HasOptimizer extends boolean> = ModelState & {
|
|
188
|
+
/** List of supported model file types */
|
|
189
|
+
supportedFileTypes: ModelFileTypes[];
|
|
168
190
|
/**
|
|
169
|
-
* Load a
|
|
170
|
-
*
|
|
191
|
+
* Load a model from any source.
|
|
192
|
+
*
|
|
193
|
+
* Never rejects: it resolves to the terminal state, and for every source but
|
|
194
|
+
* `files` that is also the state consumers render from.
|
|
171
195
|
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
196
|
+
* A rejected `files` load is the exception. When a model was already on
|
|
197
|
+
* screen it stays exactly as it was, so dropping the wrong file does not
|
|
198
|
+
* cost the user their scene, and the failure is reported only through the
|
|
199
|
+
* resolved value. With nothing on screen the state is the error as usual.
|
|
200
|
+
* `reset()` is how you clear a model deliberately.
|
|
201
|
+
*
|
|
202
|
+
* A newer `load` supersedes an older one, so an in-flight load can never
|
|
203
|
+
* overwrite the state of the load that replaced it. The superseded call
|
|
204
|
+
* still resolves, with the state it would have produced, so a caller that
|
|
205
|
+
* acts on the result rather than on the state must ask `stillCurrent()`
|
|
206
|
+
* before writing anything down - and ask it again after any further await,
|
|
207
|
+
* because a drop can land during an encode as easily as during a parse.
|
|
174
208
|
*
|
|
175
209
|
* @example
|
|
176
210
|
* ```tsx
|
|
177
211
|
* const model = useLoadModel()
|
|
178
212
|
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* sceneId: 'abc-123',
|
|
182
|
-
* serverOptions: {
|
|
183
|
-
* endpoint: '/api/load-scene',
|
|
184
|
-
* apiKey: 'optional-auth-token'
|
|
185
|
-
* }
|
|
186
|
-
* })
|
|
213
|
+
* await model.load({ kind: 'files', files })
|
|
214
|
+
* await model.load({ kind: 'server', sceneId: 'abc-123' })
|
|
187
215
|
* ```
|
|
188
216
|
*/
|
|
189
|
-
|
|
217
|
+
load: (source: ModelSource, options?: LoadOptions) => Promise<LoadOutcome>;
|
|
190
218
|
/**
|
|
191
219
|
* Reset the model loading state and clear any loaded models.
|
|
192
220
|
*/
|
|
@@ -234,11 +262,6 @@ export type OptimizerIntegrationReturn<HasOptimizer extends boolean = false> = H
|
|
|
234
262
|
* optimizer.simplifyOptimization,
|
|
235
263
|
* { ratio: 0.5 }
|
|
236
264
|
* )
|
|
237
|
-
*
|
|
238
|
-
* @example
|
|
239
|
-
* // Apply multiple optimizations in sequence
|
|
240
|
-
* await optimizer.applyOptimization(optimizer.dedupOptimization)
|
|
241
|
-
* await optimizer.applyOptimization(optimizer.quantizeOptimization, { bits: 12 })
|
|
242
265
|
*/
|
|
243
266
|
applyOptimization: <TOptions>(optimizationFunction?: ((options?: TOptions) => Promise<void>) | undefined, options?: TOptions) => Promise<void>;
|
|
244
267
|
} : null;
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { UseLoadModelReturn } from './types';
|
|
2
2
|
import { useOptimizeModel } from '../use-optimize-model';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Loads and holds one 3D model, from files, from a scene payload, or from the API.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* There is a single entry point, `load(source)`, and a single state value. The
|
|
7
|
+
* state is a discriminated union, so "a model is on screen" and "loading
|
|
8
|
+
* finished" are the same fact and cannot disagree. `load` never rejects: it
|
|
9
|
+
* resolves to the terminal state.
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* One case separates the resolved value from the state. A rejected upload
|
|
12
|
+
* leaves a model that was already on screen exactly as it was, because dropping
|
|
13
|
+
* the wrong file should not cost the user their scene, so that failure is
|
|
14
|
+
* reported only through the value `load` resolves to. With nothing on screen,
|
|
15
|
+
* and for every other source, which replaces what is there, the failure is the
|
|
16
|
+
* state.
|
|
13
17
|
*
|
|
14
18
|
* @template T - The type of the optimizer parameter (inferred automatically)
|
|
15
19
|
* @param optimizer - Optional optimizer hook returned from useOptimizeModel
|
|
16
|
-
* @returns Model
|
|
20
|
+
* @returns Model state and methods, with a conditionally typed optimizer property
|
|
17
21
|
*/
|
|
18
22
|
declare function useLoadModel<T extends ReturnType<typeof useOptimizeModel> | undefined>(optimizer?: T): UseLoadModelReturn<T extends undefined ? false : true>;
|
|
19
23
|
export default useLoadModel;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { default as readDirectory } from './read-directory';
|
|
2
2
|
export { reconstructGltfFiles } from './reconstruct-files';
|
|
3
3
|
export { calculateReferencedBytesFromFiles, calculateReferencedBytesFromServerScene } from './calculate-referenced-bytes';
|
|
4
|
-
export { resolveServerSceneDataContract } from './resolve-scene-payload';
|
|
4
|
+
export { resolvePublishedSceneDataContract, resolveServerSceneDataContract } from './resolve-scene-payload';
|
|
5
5
|
export { fetchManifestAssetData, type FetchManifestAssetsOptions } from './fetch-manifest-assets';
|
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
import { InputFileOrDirectory, ServerSceneData } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* The file name a saved scene's glTF arrives as.
|
|
4
|
+
*
|
|
5
|
+
* A SCENE TITLE IS NOT A FILE NAME, and making it into one takes more than
|
|
6
|
+
* removing separators. Nothing validates `meta.name`, so a title reaches here
|
|
7
|
+
* however the visitor typed it, and two shapes of it break the load outright:
|
|
8
|
+
*
|
|
9
|
+
* `Chair / v2` produced the key `chair / v2.gltf`, and the loader reads a key
|
|
10
|
+
* containing a separator as a path - the model appeared to sit in a folder
|
|
11
|
+
* called `chair `, and every asset carrying its own folder fell outside that
|
|
12
|
+
* scope. Harmless while stored assets were flat, because a folderless key is in
|
|
13
|
+
* every scope; giving them their folders is what made it bite.
|
|
14
|
+
*
|
|
15
|
+
* Replacing the separator with `_` then manufactured the second shape: `./v2`
|
|
16
|
+
* became `._v2.gltf`, and `modelFormatForFileName` refuses any basename
|
|
17
|
+
* starting with `._` as a macOS AppleDouble sidecar. So the scene failed to
|
|
18
|
+
* load from its title alone again, one route over, through the fix for the
|
|
19
|
+
* first. A leading dot has no other use here either - it is a hidden file on
|
|
20
|
+
* every platform this ships to, and the title is also spent as a download name.
|
|
21
|
+
*
|
|
22
|
+
* Exported because the byte gate has to name the model the same way the loader
|
|
23
|
+
* will; two spellings of this rule is how the first defect got in.
|
|
24
|
+
*/
|
|
25
|
+
export declare function sceneGltfFileName(title: string | undefined): string;
|
|
2
26
|
/**
|
|
3
27
|
* Reconstructs GLTF and asset files from scene data received from the server.
|
|
4
28
|
*
|
|
@@ -16,14 +40,12 @@ import { InputFileOrDirectory, ServerSceneData } from '../types';
|
|
|
16
40
|
*
|
|
17
41
|
* @example
|
|
18
42
|
* ```typescript
|
|
19
|
-
*
|
|
20
|
-
* const sceneData = await ServerCommunicationService.loadScene('scene-123')
|
|
43
|
+
* const { load } = useLoadModel()
|
|
21
44
|
*
|
|
22
|
-
* //
|
|
45
|
+
* // `sceneData` is a ServerSceneData payload, e.g. from a route loader
|
|
23
46
|
* const files = reconstructGltfFiles(sceneData)
|
|
24
47
|
*
|
|
25
|
-
*
|
|
26
|
-
* await modelLoader.load(files)
|
|
48
|
+
* await load({ kind: 'files', files })
|
|
27
49
|
* ```
|
|
28
50
|
*/
|
|
29
51
|
export declare function reconstructGltfFiles(data: ServerSceneData): InputFileOrDirectory;
|
|
@@ -4,3 +4,12 @@ import { ServerSceneData, ServerScenePayload } from '../../../../core/src/index.
|
|
|
4
4
|
* This keeps payload-shape adaptation in one explicit contract boundary.
|
|
5
5
|
*/
|
|
6
6
|
export declare function resolveServerSceneDataContract(payload: ServerScenePayload): ServerSceneData;
|
|
7
|
+
/**
|
|
8
|
+
* Resolves an embed manifest, which carries a published GLB instead of a glTF
|
|
9
|
+
* document.
|
|
10
|
+
*
|
|
11
|
+
* Same settings normalization as {@link resolveServerSceneDataContract}, minus
|
|
12
|
+
* the referenced-asset cross-check: a GLB is self-contained, so there are no
|
|
13
|
+
* external URIs to reconcile against the asset map.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolvePublishedSceneDataContract(payload: ServerScenePayload): ServerSceneData;
|