@vizij/render 2.0.0 → 3.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/README.md +25 -2
- package/dist/index.d.mts +102 -13
- package/dist/index.d.ts +102 -13
- package/dist/index.js +171 -50
- package/dist/index.mjs +175 -50
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -177,11 +177,32 @@ covers the whole page, and every slot already shows the runtime's stages.
|
|
|
177
177
|
their `WithBundle` forms return the world, its animatables, the
|
|
178
178
|
`VIZIJ_bundle` and the GLB's animations. A blob is parsed in memory, with
|
|
179
179
|
nothing fetched.
|
|
180
|
+
- The elements are the nodes that carry `RobotData`. With `aggressiveImport`,
|
|
181
|
+
a GLB in which no node does (a plain Blender export) is imported from its
|
|
182
|
+
meshes instead, framed on `rootBounds` or, absent, on its geometry's
|
|
183
|
+
bounds; one with no mesh, ellipse or rectangle either fails with
|
|
184
|
+
`EmptyModelError`.
|
|
180
185
|
- `buildSceneGraph(world, animatables, rootId)` builds the three.js graph of a
|
|
181
186
|
world element with no renderer; `exportScene(root, { bundle, animations })`
|
|
182
187
|
encodes it as GLB bytes. Every node carries its element's `RobotData`, and
|
|
183
188
|
its transform, morph weights and material factors are the element's
|
|
184
189
|
authored defaults.
|
|
190
|
+
- `createExportAnimation({ name, duration, tracks, root })` turns baked
|
|
191
|
+
tracks — plain data, `ExportAnimationTrack`: a node's name, a
|
|
192
|
+
`position`, `quaternion`, `scale` or `morphTargetInfluences` property, key
|
|
193
|
+
times in seconds and flattened values — into one of the `animations`
|
|
194
|
+
`exportScene` writes. Each track is bound against `root` by node name first,
|
|
195
|
+
and one that would not bind (no such node, no such morph target, a value
|
|
196
|
+
count that is not `times × stride`) is left out and returned in `issues`,
|
|
197
|
+
since the exporter otherwise drops the whole animation or throws.
|
|
198
|
+
`animation` is `null` when no track binds.
|
|
199
|
+
- An element's compound features — translation, rotation, scale and the
|
|
200
|
+
colours — are whole (`translation`, a vector), per axis (`translation.x`,
|
|
201
|
+
`rotation.r`, `color.g`, each a number: the form Semio Studio writes its
|
|
202
|
+
faces in), or both, a component given per axis standing over the whole
|
|
203
|
+
value's. Both forms load, build into the scene graph and export unchanged.
|
|
204
|
+
A rotation is an euler `{ x, y, z }`, or `{ r, p, y }` (roll, pitch, yaw)
|
|
205
|
+
as Studio names the same angles; either applies in ZYX order.
|
|
185
206
|
- An ellipse or a rectangle exports as a node with no mesh, carrying its
|
|
186
207
|
`RobotData` — `type`, `width`, `height`, `fillColor`, `fillOpacity` — from
|
|
187
208
|
which the Bevy view draws it: a unit circle or square scaled by the width
|
|
@@ -218,7 +239,8 @@ pnpm --filter "@vizij/render" size
|
|
|
218
239
|
```
|
|
219
240
|
|
|
220
241
|
The tests run on Node (`node --test`): the loader, the headless export round
|
|
221
|
-
trip of the shipped faces and of an ellipse and a rectangle, the
|
|
242
|
+
trip of the shipped faces and of an ellipse and a rectangle, the binding and
|
|
243
|
+
export of baked animations, the canvas's frame
|
|
222
244
|
loop, and the loading view's stages, labels and overrides, against a stand-in of
|
|
223
245
|
`@vizij/runtime` (`tests/runtime-stand-in.mjs`). `size-limit` guards the bundle
|
|
224
246
|
size.
|
|
@@ -227,7 +249,8 @@ Faces are drawn by `@vizij/runtime`'s Bevy view, a peer dependency. `three` is
|
|
|
227
249
|
imported only by the loader (`src/functions/load-gltf.ts`,
|
|
228
250
|
`src/functions/gltf-loading/`) and the exporter
|
|
229
251
|
(`src/functions/build-scene-graph.ts`, `src/functions/export.ts`,
|
|
230
|
-
`src/functions/vizij-bundle.ts`); the
|
|
252
|
+
`src/functions/export-animation.ts`, `src/functions/vizij-bundle.ts`); the
|
|
253
|
+
rest imports its types at most. An API
|
|
231
254
|
change updates the apps that use the package and `@vizij/runtime-react` in the
|
|
232
255
|
same change.
|
|
233
256
|
|
package/dist/index.d.mts
CHANGED
|
@@ -176,6 +176,24 @@ interface StoredAnimatedFeature {
|
|
|
176
176
|
* @param value - The id of the {@link AnimatableValue} used to populate this value if animated, or the value {@link RawValue} if static.
|
|
177
177
|
*/
|
|
178
178
|
type Feature = AnimatedFeature | StaticFeature;
|
|
179
|
+
/**
|
|
180
|
+
* A compound feature in its per-axis form: one feature per component, named
|
|
181
|
+
* `<feature>.<axis>` (`translation.x`, `rotation.r`, `color.g`), each a
|
|
182
|
+
* number. Semio Studio writes its faces this way. An element may carry a
|
|
183
|
+
* compound feature whole, per axis, or both; a component named per axis
|
|
184
|
+
* stands over that component of the whole value.
|
|
185
|
+
*/
|
|
186
|
+
type PerAxisFeatures<Name extends string, Axis extends string> = {
|
|
187
|
+
[Key in `${Name}.${Axis}`]?: Feature;
|
|
188
|
+
};
|
|
189
|
+
/** The components of a translation or a scale. */
|
|
190
|
+
type VectorAxis = "x" | "y" | "z";
|
|
191
|
+
/** The components of a rotation: roll, pitch and yaw, the euler's x, y and z. */
|
|
192
|
+
type RotationAxis = "r" | "p" | "y";
|
|
193
|
+
/** The components of a colour. */
|
|
194
|
+
type ColorAxis = "r" | "g" | "b";
|
|
195
|
+
/** An element's translation, rotation and scale, per axis. */
|
|
196
|
+
type PerAxisTransformFeatures = PerAxisFeatures<"translation", VectorAxis> & PerAxisFeatures<"rotation", RotationAxis> & PerAxisFeatures<"scale", VectorAxis>;
|
|
179
197
|
|
|
180
198
|
interface RenderableBase {
|
|
181
199
|
id: string;
|
|
@@ -218,9 +236,9 @@ interface Ellipse extends RenderableBase {
|
|
|
218
236
|
strokeColor?: Feature;
|
|
219
237
|
strokeWidth?: Feature;
|
|
220
238
|
strokeOffset?: Feature;
|
|
221
|
-
translation
|
|
222
|
-
rotation
|
|
223
|
-
}
|
|
239
|
+
translation?: Feature;
|
|
240
|
+
rotation?: Feature;
|
|
241
|
+
} & PerAxisFeatures<"translation", VectorAxis> & PerAxisFeatures<"rotation", RotationAxis> & PerAxisFeatures<"fillColor", ColorAxis> & PerAxisFeatures<"strokeColor", ColorAxis>;
|
|
224
242
|
}
|
|
225
243
|
type EllipseFeature = keyof Ellipse["features"];
|
|
226
244
|
type StoredEllipse = Stored<Ellipse>;
|
|
@@ -247,9 +265,9 @@ interface Rectangle extends RenderableBase {
|
|
|
247
265
|
strokeWidth?: Feature;
|
|
248
266
|
strokeRadius?: Feature;
|
|
249
267
|
strokeOffset?: Feature;
|
|
250
|
-
translation
|
|
251
|
-
rotation
|
|
252
|
-
}
|
|
268
|
+
translation?: Feature;
|
|
269
|
+
rotation?: Feature;
|
|
270
|
+
} & PerAxisFeatures<"translation", VectorAxis> & PerAxisFeatures<"rotation", RotationAxis> & PerAxisFeatures<"fillColor", ColorAxis> & PerAxisFeatures<"strokeColor", ColorAxis>;
|
|
253
271
|
}
|
|
254
272
|
type RectangleFeature = keyof Rectangle["features"];
|
|
255
273
|
type StoredRectangle = Stored<Rectangle>;
|
|
@@ -268,11 +286,12 @@ type StoredRectangle = Stored<Rectangle>;
|
|
|
268
286
|
interface Group extends RenderableBase {
|
|
269
287
|
type: "group";
|
|
270
288
|
root: boolean;
|
|
289
|
+
/** Translation and rotation, each whole or per axis. */
|
|
271
290
|
features: {
|
|
272
|
-
translation
|
|
273
|
-
rotation
|
|
291
|
+
translation?: Feature;
|
|
292
|
+
rotation?: Feature;
|
|
274
293
|
scale?: Feature;
|
|
275
|
-
};
|
|
294
|
+
} & PerAxisTransformFeatures;
|
|
276
295
|
rootBounds?: {
|
|
277
296
|
center: RawVector2;
|
|
278
297
|
size: RawVector2;
|
|
@@ -311,10 +330,10 @@ interface Shape extends RenderableBase {
|
|
|
311
330
|
emissive?: Feature;
|
|
312
331
|
emissiveIntensity?: Feature;
|
|
313
332
|
specular?: Feature;
|
|
314
|
-
translation
|
|
315
|
-
rotation
|
|
333
|
+
translation?: Feature;
|
|
334
|
+
rotation?: Feature;
|
|
316
335
|
scale?: Feature;
|
|
317
|
-
}
|
|
336
|
+
} & PerAxisTransformFeatures & PerAxisFeatures<"color", ColorAxis> & PerAxisFeatures<"emissive", ColorAxis> & PerAxisFeatures<"specular", ColorAxis>;
|
|
318
337
|
material: ShapeMaterial;
|
|
319
338
|
geometry: BufferGeometry | ShapeGeometry;
|
|
320
339
|
morphTargets?: string[];
|
|
@@ -737,9 +756,15 @@ declare function useVizijStoreSetter(): {
|
|
|
737
756
|
|
|
738
757
|
declare function useVizijStoreGetter(): () => VizijData & VizijActions;
|
|
739
758
|
|
|
759
|
+
/**
|
|
760
|
+
* The error a load ends with when the GLB holds no face: no element carries
|
|
761
|
+
* `RobotData`, and the aggressive import finds no mesh, ellipse or rectangle
|
|
762
|
+
* to make one from.
|
|
763
|
+
*/
|
|
740
764
|
declare class EmptyModelError extends Error {
|
|
741
765
|
constructor(message: string);
|
|
742
766
|
}
|
|
767
|
+
|
|
743
768
|
type ParserJsonFallbackSource = {
|
|
744
769
|
url?: string;
|
|
745
770
|
arrayBuffer?: ArrayBuffer;
|
|
@@ -821,6 +846,64 @@ declare function patchVizijBundleMetadata(glb: ArrayBuffer, metadata: Record<str
|
|
|
821
846
|
*/
|
|
822
847
|
declare function exportScene(root: THREE.Object3D, options?: ExportSceneOptions): Promise<ArrayBuffer>;
|
|
823
848
|
|
|
849
|
+
/** A glTF-animatable node property, under three.js's property name. */
|
|
850
|
+
type ExportAnimationProperty = "position" | "quaternion" | "scale" | "morphTargetInfluences";
|
|
851
|
+
/** One node property's keys, to export as a glTF animation channel. */
|
|
852
|
+
interface ExportAnimationTrack {
|
|
853
|
+
/**
|
|
854
|
+
* The property-binding path: `<node>.<property>`, or
|
|
855
|
+
* `<node>.morphTargetInfluences[<morph target name>]`.
|
|
856
|
+
*/
|
|
857
|
+
name: string;
|
|
858
|
+
property: ExportAnimationProperty;
|
|
859
|
+
/** The name of the node the track drives. */
|
|
860
|
+
elementName: string;
|
|
861
|
+
/** Key times, in seconds, ascending. */
|
|
862
|
+
times: number[];
|
|
863
|
+
/** Flattened key values, `times.length * stride` of them. */
|
|
864
|
+
values: number[];
|
|
865
|
+
/** Values per key: 3 for a position or scale, 4 for a quaternion, 1 for a morph weight. */
|
|
866
|
+
stride: number;
|
|
867
|
+
}
|
|
868
|
+
interface ExportAnimationIssue {
|
|
869
|
+
trackName: string;
|
|
870
|
+
reason: "node-not-found" | "morph-target-not-found" | "bad-stride";
|
|
871
|
+
detail?: string;
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* What a track binds against: the export root and its descendants, found by
|
|
875
|
+
* name. The graph `buildSceneGraph` returns is one.
|
|
876
|
+
*/
|
|
877
|
+
interface ExportAnimationRoot {
|
|
878
|
+
name: string;
|
|
879
|
+
traverse: (callback: (child: ExportAnimationRoot) => void) => void;
|
|
880
|
+
morphTargetDictionary?: Record<string, number>;
|
|
881
|
+
}
|
|
882
|
+
interface ExportAnimationResult {
|
|
883
|
+
/** For `exportScene({ animations })`; `null` when no track binds. */
|
|
884
|
+
animation: AnimationClip | null;
|
|
885
|
+
/** The tracks left out, each with why. */
|
|
886
|
+
issues: ExportAnimationIssue[];
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* An animation for `exportScene`, from baked tracks, each checked against the
|
|
890
|
+
* tree it will be exported with.
|
|
891
|
+
*
|
|
892
|
+
* The exporter drops a whole animation when one track fails to bind, and
|
|
893
|
+
* throws when a morph target name is missing from the mesh's
|
|
894
|
+
* `morphTargetDictionary`. So each track is bound here first, by node name
|
|
895
|
+
* (`exportScene` clones a root that is not a `Scene`, which keeps names and
|
|
896
|
+
* changes uuids), and a track that would not bind is left out and reported.
|
|
897
|
+
*/
|
|
898
|
+
declare function createExportAnimation(options: {
|
|
899
|
+
name: string;
|
|
900
|
+
/** In seconds. */
|
|
901
|
+
duration: number;
|
|
902
|
+
tracks: ReadonlyArray<ExportAnimationTrack>;
|
|
903
|
+
/** The tree the animation will be exported with. */
|
|
904
|
+
root: ExportAnimationRoot;
|
|
905
|
+
}): ExportAnimationResult;
|
|
906
|
+
|
|
824
907
|
/**
|
|
825
908
|
* Build the three.js graph of the world element `rootId` and its descendants,
|
|
826
909
|
* for `exportScene`.
|
|
@@ -830,6 +913,12 @@ declare function exportScene(root: THREE.Object3D, options?: ExportSceneOptions)
|
|
|
830
913
|
* them, with the element's `RobotData` in `userData`. No renderer is involved,
|
|
831
914
|
* so this runs in Node.
|
|
832
915
|
*
|
|
916
|
+
* A compound feature — translation, rotation, scale, a colour — is read whole,
|
|
917
|
+
* per axis (`translation.x`, `rotation.r`, `color.g`: Semio Studio's form),
|
|
918
|
+
* or both, a component given per axis standing over the whole value's. A
|
|
919
|
+
* rotation is an euler `{ x, y, z }` or, as Studio names it, `{ r, p, y }`
|
|
920
|
+
* (roll, pitch, yaw), applied in ZYX order.
|
|
921
|
+
*
|
|
833
922
|
* Every feature takes its authored value: a static feature its value, an
|
|
834
923
|
* animated one its animatable's `default` in `animatables`. Node translation,
|
|
835
924
|
* rotation and scale, morph weights and material factors in the exported file
|
|
@@ -886,4 +975,4 @@ declare function sanitizeMorphKey(name: string, fallbackIndex: number, used: Set
|
|
|
886
975
|
*/
|
|
887
976
|
declare function deriveMorphFeatureKeys(names: ReadonlyArray<string>): string[];
|
|
888
977
|
|
|
889
|
-
export { type AnimatedFeature, DEFAULT_TONE_MAPPING_MODE, type Ellipse, type EllipseFeature, EmptyModelError, type ExportSceneOptions, type Feature, type Group, type GroupFeature, type LoadedVizijAsset, type LoadingStage, type Rectangle, type RectangleFeature, type RenderableBase, type RenderableFeature, type Selection, type Shape, type ShapeFeature, ShapeMaterial, type StaticFeature, type Stored, type StoredAnimatedFeature, type StoredEllipse, type StoredFeatures, type StoredGroup, type StoredRectangle, type StoredRenderable, type StoredShape, TONE_MAPPING_LABELS, TONE_MAPPING_MODES, type ToneMappingMode, Vizij, type VizijActions, type VizijAnimationClipData, type VizijAnimationId, type VizijAnimationTrackData, type VizijBundleAnimationClip, type VizijBundleAnimationEntry, type VizijBundleAnimationKeyframe, type VizijBundleAnimationTrack, type VizijBundleExtension, type VizijBundleGraphEntry, type VizijBundleGraphKind, type VizijBundleGraphMetadata, type VizijBundlePoseSection, type VizijBundleProfile, type VizijBundleStarredSection, type VizijBundleVersion, VizijCanvas, type VizijCanvasProps, VizijContext, type VizijData, type VizijGraphId, type VizijLoading, type VizijPoseDefinition, type VizijPoseId, type VizijPoseRigConfig, type VizijProfileKey, type VizijProgress, type VizijProps, VizijSlice, type VizijSpeechConfig, type VizijStarredItem, type VizijStarredKind, type VizijStore, type VizijStoreGetter, type VizijStoreSetter, type World, applyVizijBundle, buildSceneGraph, canvasMounted, createVizijStore, deriveMorphFeatureKeys, deriveRootBounds, exportScene, extractVizijBundle, isToneMappingMode, loadGLTF, loadGLTFFromBlob, loadGLTFFromBlobWithBundle, loadGLTFWithBundle, normalizeToneMappingMode, parseGlbJsonChunk, patchGlbJson, patchVizijBundleMetadata, sanitizeMorphKey, setVizijProgress, useDefaultVizijStore, useVizijStore, useVizijStoreGetter, useVizijStoreSetter, useVizijStoreSubscription };
|
|
978
|
+
export { type AnimatedFeature, type ColorAxis, DEFAULT_TONE_MAPPING_MODE, type Ellipse, type EllipseFeature, EmptyModelError, type ExportAnimationIssue, type ExportAnimationProperty, type ExportAnimationResult, type ExportAnimationRoot, type ExportAnimationTrack, type ExportSceneOptions, type Feature, type Group, type GroupFeature, type LoadedVizijAsset, type LoadingStage, type PerAxisFeatures, type PerAxisTransformFeatures, type Rectangle, type RectangleFeature, type RenderableBase, type RenderableFeature, type RotationAxis, type Selection, type Shape, type ShapeFeature, ShapeMaterial, type StaticFeature, type Stored, type StoredAnimatedFeature, type StoredEllipse, type StoredFeatures, type StoredGroup, type StoredRectangle, type StoredRenderable, type StoredShape, TONE_MAPPING_LABELS, TONE_MAPPING_MODES, type ToneMappingMode, type VectorAxis, Vizij, type VizijActions, type VizijAnimationClipData, type VizijAnimationId, type VizijAnimationTrackData, type VizijBundleAnimationClip, type VizijBundleAnimationEntry, type VizijBundleAnimationKeyframe, type VizijBundleAnimationTrack, type VizijBundleExtension, type VizijBundleGraphEntry, type VizijBundleGraphKind, type VizijBundleGraphMetadata, type VizijBundlePoseSection, type VizijBundleProfile, type VizijBundleStarredSection, type VizijBundleVersion, VizijCanvas, type VizijCanvasProps, VizijContext, type VizijData, type VizijGraphId, type VizijLoading, type VizijPoseDefinition, type VizijPoseId, type VizijPoseRigConfig, type VizijProfileKey, type VizijProgress, type VizijProps, VizijSlice, type VizijSpeechConfig, type VizijStarredItem, type VizijStarredKind, type VizijStore, type VizijStoreGetter, type VizijStoreSetter, type World, applyVizijBundle, buildSceneGraph, canvasMounted, createExportAnimation, createVizijStore, deriveMorphFeatureKeys, deriveRootBounds, exportScene, extractVizijBundle, isToneMappingMode, loadGLTF, loadGLTFFromBlob, loadGLTFFromBlobWithBundle, loadGLTFWithBundle, normalizeToneMappingMode, parseGlbJsonChunk, patchGlbJson, patchVizijBundleMetadata, sanitizeMorphKey, setVizijProgress, useDefaultVizijStore, useVizijStore, useVizijStoreGetter, useVizijStoreSetter, useVizijStoreSubscription };
|
package/dist/index.d.ts
CHANGED
|
@@ -176,6 +176,24 @@ interface StoredAnimatedFeature {
|
|
|
176
176
|
* @param value - The id of the {@link AnimatableValue} used to populate this value if animated, or the value {@link RawValue} if static.
|
|
177
177
|
*/
|
|
178
178
|
type Feature = AnimatedFeature | StaticFeature;
|
|
179
|
+
/**
|
|
180
|
+
* A compound feature in its per-axis form: one feature per component, named
|
|
181
|
+
* `<feature>.<axis>` (`translation.x`, `rotation.r`, `color.g`), each a
|
|
182
|
+
* number. Semio Studio writes its faces this way. An element may carry a
|
|
183
|
+
* compound feature whole, per axis, or both; a component named per axis
|
|
184
|
+
* stands over that component of the whole value.
|
|
185
|
+
*/
|
|
186
|
+
type PerAxisFeatures<Name extends string, Axis extends string> = {
|
|
187
|
+
[Key in `${Name}.${Axis}`]?: Feature;
|
|
188
|
+
};
|
|
189
|
+
/** The components of a translation or a scale. */
|
|
190
|
+
type VectorAxis = "x" | "y" | "z";
|
|
191
|
+
/** The components of a rotation: roll, pitch and yaw, the euler's x, y and z. */
|
|
192
|
+
type RotationAxis = "r" | "p" | "y";
|
|
193
|
+
/** The components of a colour. */
|
|
194
|
+
type ColorAxis = "r" | "g" | "b";
|
|
195
|
+
/** An element's translation, rotation and scale, per axis. */
|
|
196
|
+
type PerAxisTransformFeatures = PerAxisFeatures<"translation", VectorAxis> & PerAxisFeatures<"rotation", RotationAxis> & PerAxisFeatures<"scale", VectorAxis>;
|
|
179
197
|
|
|
180
198
|
interface RenderableBase {
|
|
181
199
|
id: string;
|
|
@@ -218,9 +236,9 @@ interface Ellipse extends RenderableBase {
|
|
|
218
236
|
strokeColor?: Feature;
|
|
219
237
|
strokeWidth?: Feature;
|
|
220
238
|
strokeOffset?: Feature;
|
|
221
|
-
translation
|
|
222
|
-
rotation
|
|
223
|
-
}
|
|
239
|
+
translation?: Feature;
|
|
240
|
+
rotation?: Feature;
|
|
241
|
+
} & PerAxisFeatures<"translation", VectorAxis> & PerAxisFeatures<"rotation", RotationAxis> & PerAxisFeatures<"fillColor", ColorAxis> & PerAxisFeatures<"strokeColor", ColorAxis>;
|
|
224
242
|
}
|
|
225
243
|
type EllipseFeature = keyof Ellipse["features"];
|
|
226
244
|
type StoredEllipse = Stored<Ellipse>;
|
|
@@ -247,9 +265,9 @@ interface Rectangle extends RenderableBase {
|
|
|
247
265
|
strokeWidth?: Feature;
|
|
248
266
|
strokeRadius?: Feature;
|
|
249
267
|
strokeOffset?: Feature;
|
|
250
|
-
translation
|
|
251
|
-
rotation
|
|
252
|
-
}
|
|
268
|
+
translation?: Feature;
|
|
269
|
+
rotation?: Feature;
|
|
270
|
+
} & PerAxisFeatures<"translation", VectorAxis> & PerAxisFeatures<"rotation", RotationAxis> & PerAxisFeatures<"fillColor", ColorAxis> & PerAxisFeatures<"strokeColor", ColorAxis>;
|
|
253
271
|
}
|
|
254
272
|
type RectangleFeature = keyof Rectangle["features"];
|
|
255
273
|
type StoredRectangle = Stored<Rectangle>;
|
|
@@ -268,11 +286,12 @@ type StoredRectangle = Stored<Rectangle>;
|
|
|
268
286
|
interface Group extends RenderableBase {
|
|
269
287
|
type: "group";
|
|
270
288
|
root: boolean;
|
|
289
|
+
/** Translation and rotation, each whole or per axis. */
|
|
271
290
|
features: {
|
|
272
|
-
translation
|
|
273
|
-
rotation
|
|
291
|
+
translation?: Feature;
|
|
292
|
+
rotation?: Feature;
|
|
274
293
|
scale?: Feature;
|
|
275
|
-
};
|
|
294
|
+
} & PerAxisTransformFeatures;
|
|
276
295
|
rootBounds?: {
|
|
277
296
|
center: RawVector2;
|
|
278
297
|
size: RawVector2;
|
|
@@ -311,10 +330,10 @@ interface Shape extends RenderableBase {
|
|
|
311
330
|
emissive?: Feature;
|
|
312
331
|
emissiveIntensity?: Feature;
|
|
313
332
|
specular?: Feature;
|
|
314
|
-
translation
|
|
315
|
-
rotation
|
|
333
|
+
translation?: Feature;
|
|
334
|
+
rotation?: Feature;
|
|
316
335
|
scale?: Feature;
|
|
317
|
-
}
|
|
336
|
+
} & PerAxisTransformFeatures & PerAxisFeatures<"color", ColorAxis> & PerAxisFeatures<"emissive", ColorAxis> & PerAxisFeatures<"specular", ColorAxis>;
|
|
318
337
|
material: ShapeMaterial;
|
|
319
338
|
geometry: BufferGeometry | ShapeGeometry;
|
|
320
339
|
morphTargets?: string[];
|
|
@@ -737,9 +756,15 @@ declare function useVizijStoreSetter(): {
|
|
|
737
756
|
|
|
738
757
|
declare function useVizijStoreGetter(): () => VizijData & VizijActions;
|
|
739
758
|
|
|
759
|
+
/**
|
|
760
|
+
* The error a load ends with when the GLB holds no face: no element carries
|
|
761
|
+
* `RobotData`, and the aggressive import finds no mesh, ellipse or rectangle
|
|
762
|
+
* to make one from.
|
|
763
|
+
*/
|
|
740
764
|
declare class EmptyModelError extends Error {
|
|
741
765
|
constructor(message: string);
|
|
742
766
|
}
|
|
767
|
+
|
|
743
768
|
type ParserJsonFallbackSource = {
|
|
744
769
|
url?: string;
|
|
745
770
|
arrayBuffer?: ArrayBuffer;
|
|
@@ -821,6 +846,64 @@ declare function patchVizijBundleMetadata(glb: ArrayBuffer, metadata: Record<str
|
|
|
821
846
|
*/
|
|
822
847
|
declare function exportScene(root: THREE.Object3D, options?: ExportSceneOptions): Promise<ArrayBuffer>;
|
|
823
848
|
|
|
849
|
+
/** A glTF-animatable node property, under three.js's property name. */
|
|
850
|
+
type ExportAnimationProperty = "position" | "quaternion" | "scale" | "morphTargetInfluences";
|
|
851
|
+
/** One node property's keys, to export as a glTF animation channel. */
|
|
852
|
+
interface ExportAnimationTrack {
|
|
853
|
+
/**
|
|
854
|
+
* The property-binding path: `<node>.<property>`, or
|
|
855
|
+
* `<node>.morphTargetInfluences[<morph target name>]`.
|
|
856
|
+
*/
|
|
857
|
+
name: string;
|
|
858
|
+
property: ExportAnimationProperty;
|
|
859
|
+
/** The name of the node the track drives. */
|
|
860
|
+
elementName: string;
|
|
861
|
+
/** Key times, in seconds, ascending. */
|
|
862
|
+
times: number[];
|
|
863
|
+
/** Flattened key values, `times.length * stride` of them. */
|
|
864
|
+
values: number[];
|
|
865
|
+
/** Values per key: 3 for a position or scale, 4 for a quaternion, 1 for a morph weight. */
|
|
866
|
+
stride: number;
|
|
867
|
+
}
|
|
868
|
+
interface ExportAnimationIssue {
|
|
869
|
+
trackName: string;
|
|
870
|
+
reason: "node-not-found" | "morph-target-not-found" | "bad-stride";
|
|
871
|
+
detail?: string;
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* What a track binds against: the export root and its descendants, found by
|
|
875
|
+
* name. The graph `buildSceneGraph` returns is one.
|
|
876
|
+
*/
|
|
877
|
+
interface ExportAnimationRoot {
|
|
878
|
+
name: string;
|
|
879
|
+
traverse: (callback: (child: ExportAnimationRoot) => void) => void;
|
|
880
|
+
morphTargetDictionary?: Record<string, number>;
|
|
881
|
+
}
|
|
882
|
+
interface ExportAnimationResult {
|
|
883
|
+
/** For `exportScene({ animations })`; `null` when no track binds. */
|
|
884
|
+
animation: AnimationClip | null;
|
|
885
|
+
/** The tracks left out, each with why. */
|
|
886
|
+
issues: ExportAnimationIssue[];
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* An animation for `exportScene`, from baked tracks, each checked against the
|
|
890
|
+
* tree it will be exported with.
|
|
891
|
+
*
|
|
892
|
+
* The exporter drops a whole animation when one track fails to bind, and
|
|
893
|
+
* throws when a morph target name is missing from the mesh's
|
|
894
|
+
* `morphTargetDictionary`. So each track is bound here first, by node name
|
|
895
|
+
* (`exportScene` clones a root that is not a `Scene`, which keeps names and
|
|
896
|
+
* changes uuids), and a track that would not bind is left out and reported.
|
|
897
|
+
*/
|
|
898
|
+
declare function createExportAnimation(options: {
|
|
899
|
+
name: string;
|
|
900
|
+
/** In seconds. */
|
|
901
|
+
duration: number;
|
|
902
|
+
tracks: ReadonlyArray<ExportAnimationTrack>;
|
|
903
|
+
/** The tree the animation will be exported with. */
|
|
904
|
+
root: ExportAnimationRoot;
|
|
905
|
+
}): ExportAnimationResult;
|
|
906
|
+
|
|
824
907
|
/**
|
|
825
908
|
* Build the three.js graph of the world element `rootId` and its descendants,
|
|
826
909
|
* for `exportScene`.
|
|
@@ -830,6 +913,12 @@ declare function exportScene(root: THREE.Object3D, options?: ExportSceneOptions)
|
|
|
830
913
|
* them, with the element's `RobotData` in `userData`. No renderer is involved,
|
|
831
914
|
* so this runs in Node.
|
|
832
915
|
*
|
|
916
|
+
* A compound feature — translation, rotation, scale, a colour — is read whole,
|
|
917
|
+
* per axis (`translation.x`, `rotation.r`, `color.g`: Semio Studio's form),
|
|
918
|
+
* or both, a component given per axis standing over the whole value's. A
|
|
919
|
+
* rotation is an euler `{ x, y, z }` or, as Studio names it, `{ r, p, y }`
|
|
920
|
+
* (roll, pitch, yaw), applied in ZYX order.
|
|
921
|
+
*
|
|
833
922
|
* Every feature takes its authored value: a static feature its value, an
|
|
834
923
|
* animated one its animatable's `default` in `animatables`. Node translation,
|
|
835
924
|
* rotation and scale, morph weights and material factors in the exported file
|
|
@@ -886,4 +975,4 @@ declare function sanitizeMorphKey(name: string, fallbackIndex: number, used: Set
|
|
|
886
975
|
*/
|
|
887
976
|
declare function deriveMorphFeatureKeys(names: ReadonlyArray<string>): string[];
|
|
888
977
|
|
|
889
|
-
export { type AnimatedFeature, DEFAULT_TONE_MAPPING_MODE, type Ellipse, type EllipseFeature, EmptyModelError, type ExportSceneOptions, type Feature, type Group, type GroupFeature, type LoadedVizijAsset, type LoadingStage, type Rectangle, type RectangleFeature, type RenderableBase, type RenderableFeature, type Selection, type Shape, type ShapeFeature, ShapeMaterial, type StaticFeature, type Stored, type StoredAnimatedFeature, type StoredEllipse, type StoredFeatures, type StoredGroup, type StoredRectangle, type StoredRenderable, type StoredShape, TONE_MAPPING_LABELS, TONE_MAPPING_MODES, type ToneMappingMode, Vizij, type VizijActions, type VizijAnimationClipData, type VizijAnimationId, type VizijAnimationTrackData, type VizijBundleAnimationClip, type VizijBundleAnimationEntry, type VizijBundleAnimationKeyframe, type VizijBundleAnimationTrack, type VizijBundleExtension, type VizijBundleGraphEntry, type VizijBundleGraphKind, type VizijBundleGraphMetadata, type VizijBundlePoseSection, type VizijBundleProfile, type VizijBundleStarredSection, type VizijBundleVersion, VizijCanvas, type VizijCanvasProps, VizijContext, type VizijData, type VizijGraphId, type VizijLoading, type VizijPoseDefinition, type VizijPoseId, type VizijPoseRigConfig, type VizijProfileKey, type VizijProgress, type VizijProps, VizijSlice, type VizijSpeechConfig, type VizijStarredItem, type VizijStarredKind, type VizijStore, type VizijStoreGetter, type VizijStoreSetter, type World, applyVizijBundle, buildSceneGraph, canvasMounted, createVizijStore, deriveMorphFeatureKeys, deriveRootBounds, exportScene, extractVizijBundle, isToneMappingMode, loadGLTF, loadGLTFFromBlob, loadGLTFFromBlobWithBundle, loadGLTFWithBundle, normalizeToneMappingMode, parseGlbJsonChunk, patchGlbJson, patchVizijBundleMetadata, sanitizeMorphKey, setVizijProgress, useDefaultVizijStore, useVizijStore, useVizijStoreGetter, useVizijStoreSetter, useVizijStoreSubscription };
|
|
978
|
+
export { type AnimatedFeature, type ColorAxis, DEFAULT_TONE_MAPPING_MODE, type Ellipse, type EllipseFeature, EmptyModelError, type ExportAnimationIssue, type ExportAnimationProperty, type ExportAnimationResult, type ExportAnimationRoot, type ExportAnimationTrack, type ExportSceneOptions, type Feature, type Group, type GroupFeature, type LoadedVizijAsset, type LoadingStage, type PerAxisFeatures, type PerAxisTransformFeatures, type Rectangle, type RectangleFeature, type RenderableBase, type RenderableFeature, type RotationAxis, type Selection, type Shape, type ShapeFeature, ShapeMaterial, type StaticFeature, type Stored, type StoredAnimatedFeature, type StoredEllipse, type StoredFeatures, type StoredGroup, type StoredRectangle, type StoredRenderable, type StoredShape, TONE_MAPPING_LABELS, TONE_MAPPING_MODES, type ToneMappingMode, type VectorAxis, Vizij, type VizijActions, type VizijAnimationClipData, type VizijAnimationId, type VizijAnimationTrackData, type VizijBundleAnimationClip, type VizijBundleAnimationEntry, type VizijBundleAnimationKeyframe, type VizijBundleAnimationTrack, type VizijBundleExtension, type VizijBundleGraphEntry, type VizijBundleGraphKind, type VizijBundleGraphMetadata, type VizijBundlePoseSection, type VizijBundleProfile, type VizijBundleStarredSection, type VizijBundleVersion, VizijCanvas, type VizijCanvasProps, VizijContext, type VizijData, type VizijGraphId, type VizijLoading, type VizijPoseDefinition, type VizijPoseId, type VizijPoseRigConfig, type VizijProfileKey, type VizijProgress, type VizijProps, VizijSlice, type VizijSpeechConfig, type VizijStarredItem, type VizijStarredKind, type VizijStore, type VizijStoreGetter, type VizijStoreSetter, type World, applyVizijBundle, buildSceneGraph, canvasMounted, createExportAnimation, createVizijStore, deriveMorphFeatureKeys, deriveRootBounds, exportScene, extractVizijBundle, isToneMappingMode, loadGLTF, loadGLTFFromBlob, loadGLTFFromBlobWithBundle, loadGLTFWithBundle, normalizeToneMappingMode, parseGlbJsonChunk, patchGlbJson, patchVizijBundleMetadata, sanitizeMorphKey, setVizijProgress, useDefaultVizijStore, useVizijStore, useVizijStoreGetter, useVizijStoreSetter, useVizijStoreSubscription };
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,7 @@ __export(index_exports, {
|
|
|
42
42
|
applyVizijBundle: () => applyVizijBundle,
|
|
43
43
|
buildSceneGraph: () => buildSceneGraph,
|
|
44
44
|
canvasMounted: () => canvasMounted,
|
|
45
|
+
createExportAnimation: () => createExportAnimation,
|
|
45
46
|
createVizijStore: () => createVizijStore,
|
|
46
47
|
deriveMorphFeatureKeys: () => deriveMorphFeatureKeys,
|
|
47
48
|
deriveRootBounds: () => deriveRootBounds,
|
|
@@ -814,19 +815,10 @@ var VizijSlice = (set, _get) => ({
|
|
|
814
815
|
(0, import_immer.produce)((state) => {
|
|
815
816
|
const lookupId = (0, import_utils3.getLookup)(namespace, id);
|
|
816
817
|
if (typeof value === "function") {
|
|
817
|
-
const current = state.values.get(lookupId);
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
}
|
|
822
|
-
} else {
|
|
823
|
-
const animatableLookup = state.animatables[id];
|
|
824
|
-
const updatedValue = value(
|
|
825
|
-
animatableLookup !== void 0 ? animatableLookup.default : void 0
|
|
826
|
-
);
|
|
827
|
-
if (updatedValue !== void 0) {
|
|
828
|
-
state.values.set(lookupId, updatedValue);
|
|
829
|
-
}
|
|
818
|
+
const current = state.values.get(lookupId) ?? state.animatables[id]?.default;
|
|
819
|
+
const updatedValue = value(current);
|
|
820
|
+
if (updatedValue !== void 0) {
|
|
821
|
+
state.values.set(lookupId, updatedValue);
|
|
830
822
|
}
|
|
831
823
|
} else {
|
|
832
824
|
state.values.set(lookupId, value);
|
|
@@ -1106,6 +1098,14 @@ function recordRenderCounter(key, delta = 1) {
|
|
|
1106
1098
|
// src/functions/gltf-loading/traverse-three.ts
|
|
1107
1099
|
var THREE = __toESM(require("three"));
|
|
1108
1100
|
|
|
1101
|
+
// src/functions/empty-model-error.ts
|
|
1102
|
+
var EmptyModelError = class extends Error {
|
|
1103
|
+
constructor(message) {
|
|
1104
|
+
super(message);
|
|
1105
|
+
this.name = "EmptyModelError";
|
|
1106
|
+
}
|
|
1107
|
+
};
|
|
1108
|
+
|
|
1109
1109
|
// src/functions/gltf-loading/map-features.ts
|
|
1110
1110
|
function mapFeatures(features) {
|
|
1111
1111
|
const animatableValues = {};
|
|
@@ -1656,7 +1656,9 @@ function traverseThree(group, aggressiveImport = false, rootBounds) {
|
|
|
1656
1656
|
} else {
|
|
1657
1657
|
const derivedRootBounds = rootBounds ?? deriveRootBounds(group);
|
|
1658
1658
|
if (!derivedRootBounds) {
|
|
1659
|
-
throw new
|
|
1659
|
+
throw new EmptyModelError(
|
|
1660
|
+
"The GLB has no RobotData and no mesh, ellipse or rectangle to import"
|
|
1661
|
+
);
|
|
1660
1662
|
}
|
|
1661
1663
|
const [newWorldData, newAnimatableData] = importScene(
|
|
1662
1664
|
group,
|
|
@@ -1761,41 +1763,40 @@ function stripMaterialSuffixes(name) {
|
|
|
1761
1763
|
function isStoredAnimatedFeature(feature) {
|
|
1762
1764
|
return Boolean(feature) && typeof feature === "object" && feature.animated === true && "value" in feature;
|
|
1763
1765
|
}
|
|
1764
|
-
|
|
1766
|
+
var TRANSFORM = [
|
|
1767
|
+
["translation", ["x", "y", "z"]],
|
|
1768
|
+
["rotation", ["r", "p", "y"]]
|
|
1769
|
+
];
|
|
1770
|
+
var REQUIRED_FEATURES = {
|
|
1771
|
+
group: TRANSFORM,
|
|
1772
|
+
shape: TRANSFORM,
|
|
1773
|
+
ellipse: [...TRANSFORM, ["width", []], ["height", []]],
|
|
1774
|
+
rectangle: [...TRANSFORM, ["width", []], ["height", []]]
|
|
1775
|
+
};
|
|
1776
|
+
function assertRequiredFeatures(kind, value) {
|
|
1765
1777
|
if (!value || typeof value !== "object") {
|
|
1766
1778
|
throw new Error("Expected object");
|
|
1767
1779
|
}
|
|
1768
|
-
|
|
1769
|
-
|
|
1780
|
+
const missing = REQUIRED_FEATURES[kind].filter(
|
|
1781
|
+
([name, axes]) => !(name in value) && !axes.some((axis) => `${name}.${axis}` in value)
|
|
1782
|
+
).map(([name]) => name);
|
|
1783
|
+
if (missing.length > 0) {
|
|
1784
|
+
throw new Error(
|
|
1785
|
+
`An element of type ${kind} lacks ${missing.join(", ")} in its features`
|
|
1786
|
+
);
|
|
1770
1787
|
}
|
|
1771
1788
|
}
|
|
1789
|
+
function isGroupFeatures(value) {
|
|
1790
|
+
assertRequiredFeatures("group", value);
|
|
1791
|
+
}
|
|
1772
1792
|
function isShapeFeatures(value) {
|
|
1773
|
-
|
|
1774
|
-
throw new Error("Expected object");
|
|
1775
|
-
}
|
|
1776
|
-
if (!["translation", "rotation"].every((key) => key in value)) {
|
|
1777
|
-
throw new Error("Expected translation and rotation keys in features");
|
|
1778
|
-
}
|
|
1793
|
+
assertRequiredFeatures("shape", value);
|
|
1779
1794
|
}
|
|
1780
1795
|
function isEllipseFeatures(value) {
|
|
1781
|
-
|
|
1782
|
-
throw new Error("Expected object");
|
|
1783
|
-
}
|
|
1784
|
-
if (!["translation", "rotation", "height", "width"].every((key) => key in value)) {
|
|
1785
|
-
throw new Error(
|
|
1786
|
-
"Expected translation, rotation, width, and height keys in features"
|
|
1787
|
-
);
|
|
1788
|
-
}
|
|
1796
|
+
assertRequiredFeatures("ellipse", value);
|
|
1789
1797
|
}
|
|
1790
1798
|
function isRectangleFeatures(value) {
|
|
1791
|
-
|
|
1792
|
-
throw new Error("Expected object");
|
|
1793
|
-
}
|
|
1794
|
-
if (!["translation", "rotation", "height", "width"].every((key) => key in value)) {
|
|
1795
|
-
throw new Error(
|
|
1796
|
-
"Expected translation, rotation, width, and height keys in features"
|
|
1797
|
-
);
|
|
1798
|
-
}
|
|
1799
|
+
assertRequiredFeatures("rectangle", value);
|
|
1799
1800
|
}
|
|
1800
1801
|
var PRIMITIVE_HALF_EXTENT = {
|
|
1801
1802
|
ellipse: 1,
|
|
@@ -2357,12 +2358,6 @@ var GLB_VERSION = 2;
|
|
|
2357
2358
|
var GLB_HEADER_BYTES = 12;
|
|
2358
2359
|
var GLB_CHUNK_HEADER_BYTES = 8;
|
|
2359
2360
|
var GLB_JSON_CHUNK_TYPE = 1313821514;
|
|
2360
|
-
var EmptyModelError = class extends Error {
|
|
2361
|
-
constructor(message) {
|
|
2362
|
-
super(message);
|
|
2363
|
-
this.name = "EmptyModelError";
|
|
2364
|
-
}
|
|
2365
|
-
};
|
|
2366
2361
|
function parseGlbJsonChunk(buffer) {
|
|
2367
2362
|
if (buffer.byteLength < GLB_HEADER_BYTES + GLB_CHUNK_HEADER_BYTES) {
|
|
2368
2363
|
return void 0;
|
|
@@ -2702,6 +2697,71 @@ async function exportScene(root, options = {}) {
|
|
|
2702
2697
|
return sanitizeExportedGlb(gltf, root.name?.trim() || void 0);
|
|
2703
2698
|
}
|
|
2704
2699
|
|
|
2700
|
+
// src/functions/export-animation.ts
|
|
2701
|
+
var import_three4 = require("three");
|
|
2702
|
+
function findByName(root, name) {
|
|
2703
|
+
let found = null;
|
|
2704
|
+
root.traverse((child) => {
|
|
2705
|
+
if (!found && child.name === name) {
|
|
2706
|
+
found = child;
|
|
2707
|
+
}
|
|
2708
|
+
});
|
|
2709
|
+
return found;
|
|
2710
|
+
}
|
|
2711
|
+
function parseMorphTargetName(trackName) {
|
|
2712
|
+
const match = /\.morphTargetInfluences\[(.+)\]$/.exec(trackName);
|
|
2713
|
+
return match ? match[1] : null;
|
|
2714
|
+
}
|
|
2715
|
+
function createExportAnimation(options) {
|
|
2716
|
+
const { name, duration, tracks, root } = options;
|
|
2717
|
+
const issues = [];
|
|
2718
|
+
const built = [];
|
|
2719
|
+
for (const spec of tracks) {
|
|
2720
|
+
const node = findByName(root, spec.elementName);
|
|
2721
|
+
if (!node) {
|
|
2722
|
+
issues.push({
|
|
2723
|
+
trackName: spec.name,
|
|
2724
|
+
reason: "node-not-found",
|
|
2725
|
+
detail: spec.elementName
|
|
2726
|
+
});
|
|
2727
|
+
continue;
|
|
2728
|
+
}
|
|
2729
|
+
if (spec.values.length !== spec.times.length * spec.stride) {
|
|
2730
|
+
issues.push({
|
|
2731
|
+
trackName: spec.name,
|
|
2732
|
+
reason: "bad-stride",
|
|
2733
|
+
detail: `${spec.values.length} values for ${spec.times.length} times at stride ${spec.stride}`
|
|
2734
|
+
});
|
|
2735
|
+
continue;
|
|
2736
|
+
}
|
|
2737
|
+
if (spec.property === "morphTargetInfluences") {
|
|
2738
|
+
const morphName = parseMorphTargetName(spec.name);
|
|
2739
|
+
const dictionary = node.morphTargetDictionary;
|
|
2740
|
+
if (!morphName || !dictionary || !(morphName in dictionary)) {
|
|
2741
|
+
issues.push({
|
|
2742
|
+
trackName: spec.name,
|
|
2743
|
+
reason: "morph-target-not-found",
|
|
2744
|
+
detail: morphName ?? void 0
|
|
2745
|
+
});
|
|
2746
|
+
continue;
|
|
2747
|
+
}
|
|
2748
|
+
built.push(new import_three4.NumberKeyframeTrack(spec.name, spec.times, spec.values));
|
|
2749
|
+
continue;
|
|
2750
|
+
}
|
|
2751
|
+
if (spec.property === "quaternion") {
|
|
2752
|
+
built.push(
|
|
2753
|
+
new import_three4.QuaternionKeyframeTrack(spec.name, spec.times, spec.values)
|
|
2754
|
+
);
|
|
2755
|
+
continue;
|
|
2756
|
+
}
|
|
2757
|
+
built.push(new import_three4.VectorKeyframeTrack(spec.name, spec.times, spec.values));
|
|
2758
|
+
}
|
|
2759
|
+
if (built.length === 0) {
|
|
2760
|
+
return { animation: null, issues };
|
|
2761
|
+
}
|
|
2762
|
+
return { animation: new import_three4.AnimationClip(name, duration, built), issues };
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2705
2765
|
// src/functions/build-scene-graph.ts
|
|
2706
2766
|
var THREE4 = __toESM(require("three"));
|
|
2707
2767
|
var import_utils9 = require("@vizij/utils");
|
|
@@ -2807,7 +2867,10 @@ function buildSceneGraph(world, animatables, rootId) {
|
|
|
2807
2867
|
const object = new THREE4.Group();
|
|
2808
2868
|
object.name = entry.name;
|
|
2809
2869
|
object.userData = { gltfExtensions: { RobotData: robotData } };
|
|
2810
|
-
applyFeatures(entry.features, animatables,
|
|
2870
|
+
applyFeatures(entry.features, animatables, {
|
|
2871
|
+
...groupTransform(object),
|
|
2872
|
+
...perAxisTransform(object, { scale: true })
|
|
2873
|
+
});
|
|
2811
2874
|
for (const child of entry.children) {
|
|
2812
2875
|
object.add(buildSceneGraph(world, animatables, child));
|
|
2813
2876
|
}
|
|
@@ -2825,7 +2888,9 @@ function buildSceneGraph(world, animatables, rootId) {
|
|
|
2825
2888
|
object.userData = { gltfExtensions: { RobotData: robotData } };
|
|
2826
2889
|
applyFeatures(entry.features, animatables, {
|
|
2827
2890
|
...shapeTransform(object),
|
|
2891
|
+
...perAxisTransform(object, { scale: true }),
|
|
2828
2892
|
...materialFactors(material),
|
|
2893
|
+
...perAxisMaterialFactors(material),
|
|
2829
2894
|
...morphWeights(object, entry)
|
|
2830
2895
|
});
|
|
2831
2896
|
for (const child of entry.children ?? []) {
|
|
@@ -2842,6 +2907,7 @@ function buildSceneGraph(world, animatables, rootId) {
|
|
|
2842
2907
|
applyFeatures(entry.features, animatables, {
|
|
2843
2908
|
translation,
|
|
2844
2909
|
rotation,
|
|
2910
|
+
...perAxisTransform(object, { scale: false }),
|
|
2845
2911
|
width: (value) => {
|
|
2846
2912
|
if ((0, import_utils9.instanceOfRawNumber)(value)) object.scale.x = value;
|
|
2847
2913
|
},
|
|
@@ -2882,8 +2948,9 @@ function groupTransform(object) {
|
|
|
2882
2948
|
}
|
|
2883
2949
|
},
|
|
2884
2950
|
rotation: (value) => {
|
|
2885
|
-
|
|
2886
|
-
|
|
2951
|
+
const euler = eulerOf(value);
|
|
2952
|
+
if (euler) {
|
|
2953
|
+
object.rotation.set(...euler, "ZYX");
|
|
2887
2954
|
} else if ((0, import_utils9.instanceOfRawNumber)(value)) {
|
|
2888
2955
|
object.rotation.set(0, 0, 0);
|
|
2889
2956
|
object.rotateZ(value);
|
|
@@ -2912,8 +2979,9 @@ function shapeTransform(object) {
|
|
|
2912
2979
|
}
|
|
2913
2980
|
},
|
|
2914
2981
|
rotation: (value) => {
|
|
2915
|
-
|
|
2916
|
-
|
|
2982
|
+
const euler = eulerOf(value);
|
|
2983
|
+
if (euler) {
|
|
2984
|
+
object.rotation.set(...euler, "ZYX");
|
|
2917
2985
|
}
|
|
2918
2986
|
},
|
|
2919
2987
|
scale: (value) => {
|
|
@@ -2927,6 +2995,58 @@ function shapeTransform(object) {
|
|
|
2927
2995
|
}
|
|
2928
2996
|
};
|
|
2929
2997
|
}
|
|
2998
|
+
function eulerOf(value) {
|
|
2999
|
+
if ((0, import_utils9.instanceOfRawEuler)(value)) {
|
|
3000
|
+
return [value.x, value.y, value.z];
|
|
3001
|
+
}
|
|
3002
|
+
const rpy = value;
|
|
3003
|
+
if (rpy !== null && typeof rpy === "object" && typeof rpy.r === "number" && typeof rpy.p === "number" && typeof rpy.y === "number") {
|
|
3004
|
+
return [rpy.r, rpy.p, rpy.y];
|
|
3005
|
+
}
|
|
3006
|
+
return void 0;
|
|
3007
|
+
}
|
|
3008
|
+
function perAxis(name, axes, set) {
|
|
3009
|
+
return Object.fromEntries(
|
|
3010
|
+
axes.map((axis, index) => [
|
|
3011
|
+
`${name}.${axis}`,
|
|
3012
|
+
(value) => {
|
|
3013
|
+
if ((0, import_utils9.instanceOfRawNumber)(value)) {
|
|
3014
|
+
set(index, value);
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
])
|
|
3018
|
+
);
|
|
3019
|
+
}
|
|
3020
|
+
function perAxisTransform(object, { scale }) {
|
|
3021
|
+
return {
|
|
3022
|
+
...perAxis("translation", ["x", "y", "z"], (index, value) => {
|
|
3023
|
+
object.position.setComponent(index, value);
|
|
3024
|
+
}),
|
|
3025
|
+
...perAxis("rotation", ["r", "p", "y"], (index, value) => {
|
|
3026
|
+
const euler = [object.rotation.x, object.rotation.y, object.rotation.z];
|
|
3027
|
+
euler[index] = value;
|
|
3028
|
+
object.rotation.set(euler[0], euler[1], euler[2], "ZYX");
|
|
3029
|
+
}),
|
|
3030
|
+
...scale ? perAxis("scale", ["x", "y", "z"], (index, value) => {
|
|
3031
|
+
object.scale.setComponent(index, value);
|
|
3032
|
+
}) : {}
|
|
3033
|
+
};
|
|
3034
|
+
}
|
|
3035
|
+
function perAxisMaterialFactors(material) {
|
|
3036
|
+
const standard = material;
|
|
3037
|
+
const phong = material;
|
|
3038
|
+
const channels = ["r", "g", "b"];
|
|
3039
|
+
const component = (color) => (index, value) => {
|
|
3040
|
+
if (color) {
|
|
3041
|
+
color[channels[index]] = value;
|
|
3042
|
+
}
|
|
3043
|
+
};
|
|
3044
|
+
return {
|
|
3045
|
+
...perAxis("color", channels, component(standard.color)),
|
|
3046
|
+
...perAxis("emissive", channels, component(standard.emissive)),
|
|
3047
|
+
...perAxis("specular", channels, component(phong.specular))
|
|
3048
|
+
};
|
|
3049
|
+
}
|
|
2930
3050
|
function materialFactors(material) {
|
|
2931
3051
|
const standard = material;
|
|
2932
3052
|
const phong = material;
|
|
@@ -3073,6 +3193,7 @@ function authoredDefault(animatable) {
|
|
|
3073
3193
|
applyVizijBundle,
|
|
3074
3194
|
buildSceneGraph,
|
|
3075
3195
|
canvasMounted,
|
|
3196
|
+
createExportAnimation,
|
|
3076
3197
|
createVizijStore,
|
|
3077
3198
|
deriveMorphFeatureKeys,
|
|
3078
3199
|
deriveRootBounds,
|
package/dist/index.mjs
CHANGED
|
@@ -757,19 +757,10 @@ var VizijSlice = (set, _get) => ({
|
|
|
757
757
|
produce((state) => {
|
|
758
758
|
const lookupId = getLookup(namespace, id);
|
|
759
759
|
if (typeof value === "function") {
|
|
760
|
-
const current = state.values.get(lookupId);
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
}
|
|
765
|
-
} else {
|
|
766
|
-
const animatableLookup = state.animatables[id];
|
|
767
|
-
const updatedValue = value(
|
|
768
|
-
animatableLookup !== void 0 ? animatableLookup.default : void 0
|
|
769
|
-
);
|
|
770
|
-
if (updatedValue !== void 0) {
|
|
771
|
-
state.values.set(lookupId, updatedValue);
|
|
772
|
-
}
|
|
760
|
+
const current = state.values.get(lookupId) ?? state.animatables[id]?.default;
|
|
761
|
+
const updatedValue = value(current);
|
|
762
|
+
if (updatedValue !== void 0) {
|
|
763
|
+
state.values.set(lookupId, updatedValue);
|
|
773
764
|
}
|
|
774
765
|
} else {
|
|
775
766
|
state.values.set(lookupId, value);
|
|
@@ -1049,6 +1040,14 @@ function recordRenderCounter(key, delta = 1) {
|
|
|
1049
1040
|
// src/functions/gltf-loading/traverse-three.ts
|
|
1050
1041
|
import * as THREE from "three";
|
|
1051
1042
|
|
|
1043
|
+
// src/functions/empty-model-error.ts
|
|
1044
|
+
var EmptyModelError = class extends Error {
|
|
1045
|
+
constructor(message) {
|
|
1046
|
+
super(message);
|
|
1047
|
+
this.name = "EmptyModelError";
|
|
1048
|
+
}
|
|
1049
|
+
};
|
|
1050
|
+
|
|
1052
1051
|
// src/functions/gltf-loading/map-features.ts
|
|
1053
1052
|
function mapFeatures(features) {
|
|
1054
1053
|
const animatableValues = {};
|
|
@@ -1601,7 +1600,9 @@ function traverseThree(group, aggressiveImport = false, rootBounds) {
|
|
|
1601
1600
|
} else {
|
|
1602
1601
|
const derivedRootBounds = rootBounds ?? deriveRootBounds(group);
|
|
1603
1602
|
if (!derivedRootBounds) {
|
|
1604
|
-
throw new
|
|
1603
|
+
throw new EmptyModelError(
|
|
1604
|
+
"The GLB has no RobotData and no mesh, ellipse or rectangle to import"
|
|
1605
|
+
);
|
|
1605
1606
|
}
|
|
1606
1607
|
const [newWorldData, newAnimatableData] = importScene(
|
|
1607
1608
|
group,
|
|
@@ -1706,41 +1707,40 @@ function stripMaterialSuffixes(name) {
|
|
|
1706
1707
|
function isStoredAnimatedFeature(feature) {
|
|
1707
1708
|
return Boolean(feature) && typeof feature === "object" && feature.animated === true && "value" in feature;
|
|
1708
1709
|
}
|
|
1709
|
-
|
|
1710
|
+
var TRANSFORM = [
|
|
1711
|
+
["translation", ["x", "y", "z"]],
|
|
1712
|
+
["rotation", ["r", "p", "y"]]
|
|
1713
|
+
];
|
|
1714
|
+
var REQUIRED_FEATURES = {
|
|
1715
|
+
group: TRANSFORM,
|
|
1716
|
+
shape: TRANSFORM,
|
|
1717
|
+
ellipse: [...TRANSFORM, ["width", []], ["height", []]],
|
|
1718
|
+
rectangle: [...TRANSFORM, ["width", []], ["height", []]]
|
|
1719
|
+
};
|
|
1720
|
+
function assertRequiredFeatures(kind, value) {
|
|
1710
1721
|
if (!value || typeof value !== "object") {
|
|
1711
1722
|
throw new Error("Expected object");
|
|
1712
1723
|
}
|
|
1713
|
-
|
|
1714
|
-
|
|
1724
|
+
const missing = REQUIRED_FEATURES[kind].filter(
|
|
1725
|
+
([name, axes]) => !(name in value) && !axes.some((axis) => `${name}.${axis}` in value)
|
|
1726
|
+
).map(([name]) => name);
|
|
1727
|
+
if (missing.length > 0) {
|
|
1728
|
+
throw new Error(
|
|
1729
|
+
`An element of type ${kind} lacks ${missing.join(", ")} in its features`
|
|
1730
|
+
);
|
|
1715
1731
|
}
|
|
1716
1732
|
}
|
|
1733
|
+
function isGroupFeatures(value) {
|
|
1734
|
+
assertRequiredFeatures("group", value);
|
|
1735
|
+
}
|
|
1717
1736
|
function isShapeFeatures(value) {
|
|
1718
|
-
|
|
1719
|
-
throw new Error("Expected object");
|
|
1720
|
-
}
|
|
1721
|
-
if (!["translation", "rotation"].every((key) => key in value)) {
|
|
1722
|
-
throw new Error("Expected translation and rotation keys in features");
|
|
1723
|
-
}
|
|
1737
|
+
assertRequiredFeatures("shape", value);
|
|
1724
1738
|
}
|
|
1725
1739
|
function isEllipseFeatures(value) {
|
|
1726
|
-
|
|
1727
|
-
throw new Error("Expected object");
|
|
1728
|
-
}
|
|
1729
|
-
if (!["translation", "rotation", "height", "width"].every((key) => key in value)) {
|
|
1730
|
-
throw new Error(
|
|
1731
|
-
"Expected translation, rotation, width, and height keys in features"
|
|
1732
|
-
);
|
|
1733
|
-
}
|
|
1740
|
+
assertRequiredFeatures("ellipse", value);
|
|
1734
1741
|
}
|
|
1735
1742
|
function isRectangleFeatures(value) {
|
|
1736
|
-
|
|
1737
|
-
throw new Error("Expected object");
|
|
1738
|
-
}
|
|
1739
|
-
if (!["translation", "rotation", "height", "width"].every((key) => key in value)) {
|
|
1740
|
-
throw new Error(
|
|
1741
|
-
"Expected translation, rotation, width, and height keys in features"
|
|
1742
|
-
);
|
|
1743
|
-
}
|
|
1743
|
+
assertRequiredFeatures("rectangle", value);
|
|
1744
1744
|
}
|
|
1745
1745
|
var PRIMITIVE_HALF_EXTENT = {
|
|
1746
1746
|
ellipse: 1,
|
|
@@ -2302,12 +2302,6 @@ var GLB_VERSION = 2;
|
|
|
2302
2302
|
var GLB_HEADER_BYTES = 12;
|
|
2303
2303
|
var GLB_CHUNK_HEADER_BYTES = 8;
|
|
2304
2304
|
var GLB_JSON_CHUNK_TYPE = 1313821514;
|
|
2305
|
-
var EmptyModelError = class extends Error {
|
|
2306
|
-
constructor(message) {
|
|
2307
|
-
super(message);
|
|
2308
|
-
this.name = "EmptyModelError";
|
|
2309
|
-
}
|
|
2310
|
-
};
|
|
2311
2305
|
function parseGlbJsonChunk(buffer) {
|
|
2312
2306
|
if (buffer.byteLength < GLB_HEADER_BYTES + GLB_CHUNK_HEADER_BYTES) {
|
|
2313
2307
|
return void 0;
|
|
@@ -2647,6 +2641,76 @@ async function exportScene(root, options = {}) {
|
|
|
2647
2641
|
return sanitizeExportedGlb(gltf, root.name?.trim() || void 0);
|
|
2648
2642
|
}
|
|
2649
2643
|
|
|
2644
|
+
// src/functions/export-animation.ts
|
|
2645
|
+
import {
|
|
2646
|
+
AnimationClip,
|
|
2647
|
+
NumberKeyframeTrack,
|
|
2648
|
+
QuaternionKeyframeTrack,
|
|
2649
|
+
VectorKeyframeTrack
|
|
2650
|
+
} from "three";
|
|
2651
|
+
function findByName(root, name) {
|
|
2652
|
+
let found = null;
|
|
2653
|
+
root.traverse((child) => {
|
|
2654
|
+
if (!found && child.name === name) {
|
|
2655
|
+
found = child;
|
|
2656
|
+
}
|
|
2657
|
+
});
|
|
2658
|
+
return found;
|
|
2659
|
+
}
|
|
2660
|
+
function parseMorphTargetName(trackName) {
|
|
2661
|
+
const match = /\.morphTargetInfluences\[(.+)\]$/.exec(trackName);
|
|
2662
|
+
return match ? match[1] : null;
|
|
2663
|
+
}
|
|
2664
|
+
function createExportAnimation(options) {
|
|
2665
|
+
const { name, duration, tracks, root } = options;
|
|
2666
|
+
const issues = [];
|
|
2667
|
+
const built = [];
|
|
2668
|
+
for (const spec of tracks) {
|
|
2669
|
+
const node = findByName(root, spec.elementName);
|
|
2670
|
+
if (!node) {
|
|
2671
|
+
issues.push({
|
|
2672
|
+
trackName: spec.name,
|
|
2673
|
+
reason: "node-not-found",
|
|
2674
|
+
detail: spec.elementName
|
|
2675
|
+
});
|
|
2676
|
+
continue;
|
|
2677
|
+
}
|
|
2678
|
+
if (spec.values.length !== spec.times.length * spec.stride) {
|
|
2679
|
+
issues.push({
|
|
2680
|
+
trackName: spec.name,
|
|
2681
|
+
reason: "bad-stride",
|
|
2682
|
+
detail: `${spec.values.length} values for ${spec.times.length} times at stride ${spec.stride}`
|
|
2683
|
+
});
|
|
2684
|
+
continue;
|
|
2685
|
+
}
|
|
2686
|
+
if (spec.property === "morphTargetInfluences") {
|
|
2687
|
+
const morphName = parseMorphTargetName(spec.name);
|
|
2688
|
+
const dictionary = node.morphTargetDictionary;
|
|
2689
|
+
if (!morphName || !dictionary || !(morphName in dictionary)) {
|
|
2690
|
+
issues.push({
|
|
2691
|
+
trackName: spec.name,
|
|
2692
|
+
reason: "morph-target-not-found",
|
|
2693
|
+
detail: morphName ?? void 0
|
|
2694
|
+
});
|
|
2695
|
+
continue;
|
|
2696
|
+
}
|
|
2697
|
+
built.push(new NumberKeyframeTrack(spec.name, spec.times, spec.values));
|
|
2698
|
+
continue;
|
|
2699
|
+
}
|
|
2700
|
+
if (spec.property === "quaternion") {
|
|
2701
|
+
built.push(
|
|
2702
|
+
new QuaternionKeyframeTrack(spec.name, spec.times, spec.values)
|
|
2703
|
+
);
|
|
2704
|
+
continue;
|
|
2705
|
+
}
|
|
2706
|
+
built.push(new VectorKeyframeTrack(spec.name, spec.times, spec.values));
|
|
2707
|
+
}
|
|
2708
|
+
if (built.length === 0) {
|
|
2709
|
+
return { animation: null, issues };
|
|
2710
|
+
}
|
|
2711
|
+
return { animation: new AnimationClip(name, duration, built), issues };
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2650
2714
|
// src/functions/build-scene-graph.ts
|
|
2651
2715
|
import * as THREE4 from "three";
|
|
2652
2716
|
import {
|
|
@@ -2759,7 +2823,10 @@ function buildSceneGraph(world, animatables, rootId) {
|
|
|
2759
2823
|
const object = new THREE4.Group();
|
|
2760
2824
|
object.name = entry.name;
|
|
2761
2825
|
object.userData = { gltfExtensions: { RobotData: robotData } };
|
|
2762
|
-
applyFeatures(entry.features, animatables,
|
|
2826
|
+
applyFeatures(entry.features, animatables, {
|
|
2827
|
+
...groupTransform(object),
|
|
2828
|
+
...perAxisTransform(object, { scale: true })
|
|
2829
|
+
});
|
|
2763
2830
|
for (const child of entry.children) {
|
|
2764
2831
|
object.add(buildSceneGraph(world, animatables, child));
|
|
2765
2832
|
}
|
|
@@ -2777,7 +2844,9 @@ function buildSceneGraph(world, animatables, rootId) {
|
|
|
2777
2844
|
object.userData = { gltfExtensions: { RobotData: robotData } };
|
|
2778
2845
|
applyFeatures(entry.features, animatables, {
|
|
2779
2846
|
...shapeTransform(object),
|
|
2847
|
+
...perAxisTransform(object, { scale: true }),
|
|
2780
2848
|
...materialFactors(material),
|
|
2849
|
+
...perAxisMaterialFactors(material),
|
|
2781
2850
|
...morphWeights(object, entry)
|
|
2782
2851
|
});
|
|
2783
2852
|
for (const child of entry.children ?? []) {
|
|
@@ -2794,6 +2863,7 @@ function buildSceneGraph(world, animatables, rootId) {
|
|
|
2794
2863
|
applyFeatures(entry.features, animatables, {
|
|
2795
2864
|
translation,
|
|
2796
2865
|
rotation,
|
|
2866
|
+
...perAxisTransform(object, { scale: false }),
|
|
2797
2867
|
width: (value) => {
|
|
2798
2868
|
if (instanceOfRawNumber(value)) object.scale.x = value;
|
|
2799
2869
|
},
|
|
@@ -2834,8 +2904,9 @@ function groupTransform(object) {
|
|
|
2834
2904
|
}
|
|
2835
2905
|
},
|
|
2836
2906
|
rotation: (value) => {
|
|
2837
|
-
|
|
2838
|
-
|
|
2907
|
+
const euler = eulerOf(value);
|
|
2908
|
+
if (euler) {
|
|
2909
|
+
object.rotation.set(...euler, "ZYX");
|
|
2839
2910
|
} else if (instanceOfRawNumber(value)) {
|
|
2840
2911
|
object.rotation.set(0, 0, 0);
|
|
2841
2912
|
object.rotateZ(value);
|
|
@@ -2864,8 +2935,9 @@ function shapeTransform(object) {
|
|
|
2864
2935
|
}
|
|
2865
2936
|
},
|
|
2866
2937
|
rotation: (value) => {
|
|
2867
|
-
|
|
2868
|
-
|
|
2938
|
+
const euler = eulerOf(value);
|
|
2939
|
+
if (euler) {
|
|
2940
|
+
object.rotation.set(...euler, "ZYX");
|
|
2869
2941
|
}
|
|
2870
2942
|
},
|
|
2871
2943
|
scale: (value) => {
|
|
@@ -2879,6 +2951,58 @@ function shapeTransform(object) {
|
|
|
2879
2951
|
}
|
|
2880
2952
|
};
|
|
2881
2953
|
}
|
|
2954
|
+
function eulerOf(value) {
|
|
2955
|
+
if (instanceOfRawEuler(value)) {
|
|
2956
|
+
return [value.x, value.y, value.z];
|
|
2957
|
+
}
|
|
2958
|
+
const rpy = value;
|
|
2959
|
+
if (rpy !== null && typeof rpy === "object" && typeof rpy.r === "number" && typeof rpy.p === "number" && typeof rpy.y === "number") {
|
|
2960
|
+
return [rpy.r, rpy.p, rpy.y];
|
|
2961
|
+
}
|
|
2962
|
+
return void 0;
|
|
2963
|
+
}
|
|
2964
|
+
function perAxis(name, axes, set) {
|
|
2965
|
+
return Object.fromEntries(
|
|
2966
|
+
axes.map((axis, index) => [
|
|
2967
|
+
`${name}.${axis}`,
|
|
2968
|
+
(value) => {
|
|
2969
|
+
if (instanceOfRawNumber(value)) {
|
|
2970
|
+
set(index, value);
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
])
|
|
2974
|
+
);
|
|
2975
|
+
}
|
|
2976
|
+
function perAxisTransform(object, { scale }) {
|
|
2977
|
+
return {
|
|
2978
|
+
...perAxis("translation", ["x", "y", "z"], (index, value) => {
|
|
2979
|
+
object.position.setComponent(index, value);
|
|
2980
|
+
}),
|
|
2981
|
+
...perAxis("rotation", ["r", "p", "y"], (index, value) => {
|
|
2982
|
+
const euler = [object.rotation.x, object.rotation.y, object.rotation.z];
|
|
2983
|
+
euler[index] = value;
|
|
2984
|
+
object.rotation.set(euler[0], euler[1], euler[2], "ZYX");
|
|
2985
|
+
}),
|
|
2986
|
+
...scale ? perAxis("scale", ["x", "y", "z"], (index, value) => {
|
|
2987
|
+
object.scale.setComponent(index, value);
|
|
2988
|
+
}) : {}
|
|
2989
|
+
};
|
|
2990
|
+
}
|
|
2991
|
+
function perAxisMaterialFactors(material) {
|
|
2992
|
+
const standard = material;
|
|
2993
|
+
const phong = material;
|
|
2994
|
+
const channels = ["r", "g", "b"];
|
|
2995
|
+
const component = (color) => (index, value) => {
|
|
2996
|
+
if (color) {
|
|
2997
|
+
color[channels[index]] = value;
|
|
2998
|
+
}
|
|
2999
|
+
};
|
|
3000
|
+
return {
|
|
3001
|
+
...perAxis("color", channels, component(standard.color)),
|
|
3002
|
+
...perAxis("emissive", channels, component(standard.emissive)),
|
|
3003
|
+
...perAxis("specular", channels, component(phong.specular))
|
|
3004
|
+
};
|
|
3005
|
+
}
|
|
2882
3006
|
function materialFactors(material) {
|
|
2883
3007
|
const standard = material;
|
|
2884
3008
|
const phong = material;
|
|
@@ -3024,6 +3148,7 @@ export {
|
|
|
3024
3148
|
applyVizijBundle,
|
|
3025
3149
|
buildSceneGraph,
|
|
3026
3150
|
canvasMounted,
|
|
3151
|
+
createExportAnimation,
|
|
3027
3152
|
createVizijStore,
|
|
3028
3153
|
deriveMorphFeatureKeys,
|
|
3029
3154
|
deriveRootBounds,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vizij/render",
|
|
3
3
|
"description": "Vizij faces on a page: the shared canvas and face slots over @vizij/runtime's Bevy view, and the GLB loader, exporter and world model.",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "3.0.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|