@babylonjs/loaders 9.20.0 → 9.21.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/SPLAT/gaussianSplattingStream.d.ts +170 -1
- package/SPLAT/gaussianSplattingStream.js +516 -40
- package/SPLAT/gaussianSplattingStream.js.map +1 -1
- package/SPLAT/gaussianSplattingWorkBuffer.d.ts +163 -4
- package/SPLAT/gaussianSplattingWorkBuffer.js +683 -35
- package/SPLAT/gaussianSplattingWorkBuffer.js.map +1 -1
- package/SPLAT/gaussianSplattingWorkBufferShaders.d.ts +70 -2
- package/SPLAT/gaussianSplattingWorkBufferShaders.js +465 -4
- package/SPLAT/gaussianSplattingWorkBufferShaders.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_interactivity/declarationMapper.d.ts +16 -1
- package/glTF/2.0/Extensions/KHR_interactivity/declarationMapper.js +232 -25
- package/glTF/2.0/Extensions/KHR_interactivity/declarationMapper.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphParser.d.ts +52 -2
- package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphParser.js +108 -7
- package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphParser.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_interactivity/interactivityHostResolver.d.ts +37 -0
- package/glTF/2.0/Extensions/KHR_interactivity/interactivityHostResolver.js +69 -0
- package/glTF/2.0/Extensions/KHR_interactivity/interactivityHostResolver.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_interactivity/interactivityReferences.d.ts +44 -0
- package/glTF/2.0/Extensions/KHR_interactivity/interactivityReferences.js +51 -0
- package/glTF/2.0/Extensions/KHR_interactivity/interactivityReferences.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_interactivity.pure.d.ts +1 -0
- package/glTF/2.0/Extensions/KHR_interactivity.pure.js +143 -12
- package/glTF/2.0/Extensions/KHR_interactivity.pure.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_node_hoverability.pure.js +6 -0
- package/glTF/2.0/Extensions/KHR_node_hoverability.pure.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_node_selectability.pure.js +4 -0
- package/glTF/2.0/Extensions/KHR_node_selectability.pure.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_node_visibility.pure.js +11 -0
- package/glTF/2.0/Extensions/KHR_node_visibility.pure.js.map +1 -1
- package/glTF/2.0/Extensions/babylonScenePathToObjectConverter.d.ts +126 -0
- package/glTF/2.0/Extensions/babylonScenePathToObjectConverter.js +251 -0
- package/glTF/2.0/Extensions/babylonScenePathToObjectConverter.js.map +1 -0
- package/glTF/2.0/Extensions/compositePathToObjectConverter.d.ts +51 -0
- package/glTF/2.0/Extensions/compositePathToObjectConverter.js +51 -0
- package/glTF/2.0/Extensions/compositePathToObjectConverter.js.map +1 -0
- package/glTF/2.0/Extensions/gltfPathToObjectConverter.js +69 -7
- package/glTF/2.0/Extensions/gltfPathToObjectConverter.js.map +1 -1
- package/glTF/2.0/Extensions/interactivityAssetPathToObjectConverter.d.ts +41 -0
- package/glTF/2.0/Extensions/interactivityAssetPathToObjectConverter.js +108 -0
- package/glTF/2.0/Extensions/interactivityAssetPathToObjectConverter.js.map +1 -0
- package/glTF/2.0/Extensions/interactivityRefPathToObjectConverter.d.ts +28 -0
- package/glTF/2.0/Extensions/interactivityRefPathToObjectConverter.js +71 -0
- package/glTF/2.0/Extensions/interactivityRefPathToObjectConverter.js.map +1 -0
- package/glTF/2.0/Extensions/objectModelMapping.d.ts +118 -30
- package/glTF/2.0/Extensions/objectModelMapping.js +472 -70
- package/glTF/2.0/Extensions/objectModelMapping.js.map +1 -1
- package/glTF/2.0/glTFLoader.pure.js +2 -1
- package/glTF/2.0/glTFLoader.pure.js.map +1 -1
- package/package.json +3 -3
|
@@ -3,14 +3,32 @@ import { Matrix, Quaternion, Vector2 } from "@babylonjs/core/Maths/math.vector.p
|
|
|
3
3
|
import { Constants } from "@babylonjs/core/Engines/constants.js";
|
|
4
4
|
import { Color4 } from "@babylonjs/core/Maths/math.color.pure.js";
|
|
5
5
|
import { SpotLight } from "@babylonjs/core/Lights/spotLight.pure.js";
|
|
6
|
+
import { FlowGraphInteger } from "@babylonjs/core/FlowGraph/CustomTypes/flowGraphInteger.pure.js";
|
|
6
7
|
import { GLTFPathToObjectConverter } from "./gltfPathToObjectConverter.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Builds an accessor for an array `length` pointer (e.g. `/nodes.length`, `/meshes/{}/primitives.length`).
|
|
10
|
+
*
|
|
11
|
+
* The KHR object model types these `length` values as `int`, so the accessor reports the count as a
|
|
12
|
+
* {@link FlowGraphInteger} (type {@link FlowGraphTypes.Integer}). Keeping the representation an integer
|
|
13
|
+
* at the source lets integer-typed consumers such as `FlowGraphIntToFloat` accept the precise
|
|
14
|
+
* `RichTypeFlowGraphInteger` instead of a widened `RichTypeAny`.
|
|
15
|
+
* @param getLength returns the raw count for the addressed array; may be `undefined` when the array is not addressable
|
|
16
|
+
* @param getTarget returns the Babylon object(s) backing the array
|
|
17
|
+
* @returns an object accessor whose value is the count as a {@link FlowGraphInteger}
|
|
18
|
+
*/
|
|
19
|
+
function _CreateLengthAccessor(getLength, getTarget) {
|
|
20
|
+
return {
|
|
21
|
+
type: "FlowGraphInteger" /* FlowGraphTypes.Integer */,
|
|
22
|
+
get: (target) => {
|
|
23
|
+
const length = getLength(target);
|
|
24
|
+
return length === undefined ? undefined : FlowGraphInteger.FromValue(length);
|
|
25
|
+
},
|
|
26
|
+
getTarget,
|
|
12
27
|
getPropertyName: [() => "length"],
|
|
13
|
-
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const nodesTree = {
|
|
31
|
+
length: _CreateLengthAccessor((nodes) => nodes.length, (nodes) => nodes.map((node) => node._babylonTransformNode)),
|
|
14
32
|
__array__: {
|
|
15
33
|
__target__: true,
|
|
16
34
|
translation: {
|
|
@@ -35,24 +53,60 @@ const nodesTree = {
|
|
|
35
53
|
getPropertyName: [() => "scaling"],
|
|
36
54
|
},
|
|
37
55
|
weights: {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
56
|
+
// Skip glTF objectTree traversal — weights may be undefined on the glTF node
|
|
57
|
+
// but accessible via the Babylon MorphTargetManager on the INode's meshes
|
|
58
|
+
__passThroughTarget__: true,
|
|
59
|
+
length: _CreateLengthAccessor((node) => {
|
|
60
|
+
const found = _findNodeMorphTargets(node);
|
|
61
|
+
return found ? found.mtm.numTargets : node?.mesh !== undefined ? 0 : undefined;
|
|
62
|
+
}, (node) => node?._babylonTransformNode),
|
|
44
63
|
__array__: {
|
|
45
64
|
__target__: true,
|
|
46
65
|
type: "number",
|
|
47
|
-
get: (node, index) =>
|
|
48
|
-
|
|
49
|
-
|
|
66
|
+
get: (node, index) => {
|
|
67
|
+
const found = _findNodeMorphTargets(node);
|
|
68
|
+
if (found && index !== undefined && index >= 0 && index < found.mtm.numTargets) {
|
|
69
|
+
return _roundFloat32Artifact(found.mtm.getTarget(index).influence);
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
},
|
|
73
|
+
set: (value, node, index) => {
|
|
74
|
+
const numValue = typeof value === "number" ? value : typeof value?.value === "number" ? value.value : value;
|
|
75
|
+
const found = _findNodeMorphTargets(node);
|
|
76
|
+
if (!found || index === undefined || index < 0 || index >= found.mtm.numTargets) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// Fan out to every mesh that shares this morph target manager so
|
|
80
|
+
// multi-primitive meshes stay in sync.
|
|
81
|
+
for (const mesh of found.meshes) {
|
|
82
|
+
const target = mesh.morphTargetManager?.getTarget(index);
|
|
83
|
+
if (target) {
|
|
84
|
+
target.influence = numValue;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
getTarget: (node, index) => {
|
|
89
|
+
const found = _findNodeMorphTargets(node);
|
|
90
|
+
if (found && index !== undefined && index >= 0 && index < found.mtm.numTargets) {
|
|
91
|
+
return found.mtm.getTarget(index);
|
|
92
|
+
}
|
|
93
|
+
return node?._babylonTransformNode;
|
|
94
|
+
},
|
|
50
95
|
getPropertyName: [() => "influence"],
|
|
51
96
|
},
|
|
52
97
|
type: "number[]",
|
|
53
|
-
get: (node
|
|
54
|
-
|
|
55
|
-
|
|
98
|
+
get: (node) => {
|
|
99
|
+
const found = _findNodeMorphTargets(node);
|
|
100
|
+
if (!found) {
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
const weights = [];
|
|
104
|
+
for (let i = 0; i < found.mtm.numTargets; i++) {
|
|
105
|
+
weights.push(_roundFloat32Artifact(found.mtm.getTarget(i).influence));
|
|
106
|
+
}
|
|
107
|
+
return weights;
|
|
108
|
+
},
|
|
109
|
+
getTarget: (node) => node?._babylonTransformNode,
|
|
56
110
|
getPropertyName: [() => "influence"],
|
|
57
111
|
},
|
|
58
112
|
// readonly!
|
|
@@ -88,6 +142,45 @@ const nodesTree = {
|
|
|
88
142
|
getTarget: (node) => node._babylonTransformNode,
|
|
89
143
|
isReadOnly: true,
|
|
90
144
|
},
|
|
145
|
+
camera: {
|
|
146
|
+
type: "string",
|
|
147
|
+
// Per KHR_interactivity Object Model: read-only ref pointing to the
|
|
148
|
+
// attached camera, encoded as a JSON Pointer string. Empty string
|
|
149
|
+
// when no camera is attached (the spec's null-ref convention).
|
|
150
|
+
get: (node) => (node.camera !== undefined ? `/cameras/${node.camera}` : ""),
|
|
151
|
+
getTarget: (node) => node,
|
|
152
|
+
isReadOnly: true,
|
|
153
|
+
},
|
|
154
|
+
mesh: {
|
|
155
|
+
type: "string",
|
|
156
|
+
get: (node) => (node.mesh !== undefined ? `/meshes/${node.mesh}` : ""),
|
|
157
|
+
getTarget: (node) => node,
|
|
158
|
+
isReadOnly: true,
|
|
159
|
+
},
|
|
160
|
+
skin: {
|
|
161
|
+
type: "string",
|
|
162
|
+
get: (node) => (node.skin !== undefined ? `/skins/${node.skin}` : ""),
|
|
163
|
+
getTarget: (node) => node,
|
|
164
|
+
isReadOnly: true,
|
|
165
|
+
},
|
|
166
|
+
parent: {
|
|
167
|
+
type: "string",
|
|
168
|
+
get: (node) => (node.parent && node.parent.index !== undefined ? `/nodes/${node.parent.index}` : ""),
|
|
169
|
+
getTarget: (node) => node,
|
|
170
|
+
isReadOnly: true,
|
|
171
|
+
},
|
|
172
|
+
children: {
|
|
173
|
+
length: _CreateLengthAccessor((children) => children?.length ?? 0, (children) => children ?? []),
|
|
174
|
+
__array__: {
|
|
175
|
+
__target__: true,
|
|
176
|
+
type: "string",
|
|
177
|
+
// The wrapping converter passes the indexed child value (an
|
|
178
|
+
// INode index); convert it to a JSON Pointer ref string.
|
|
179
|
+
get: (childIndex) => (typeof childIndex === "number" ? `/nodes/${childIndex}` : ""),
|
|
180
|
+
getTarget: () => ({ __nodeIndex: true }),
|
|
181
|
+
isReadOnly: true,
|
|
182
|
+
},
|
|
183
|
+
},
|
|
91
184
|
extensions: {
|
|
92
185
|
EXT_lights_ies: {
|
|
93
186
|
multiplier: {
|
|
@@ -139,24 +232,50 @@ const nodesTree = {
|
|
|
139
232
|
},
|
|
140
233
|
};
|
|
141
234
|
const animationsTree = {
|
|
142
|
-
length:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
235
|
+
length: _CreateLengthAccessor((animations) => animations.length, (animations) => animations.map((animation) => animation._babylonAnimationGroup)),
|
|
236
|
+
__array__: {
|
|
237
|
+
// Indexed access to the animation, surfaced as a JSON Pointer ref string so blocks like
|
|
238
|
+
// ``animation/start`` can consume it directly. Uses the animation's own ``index`` property
|
|
239
|
+
// (populated by the loader's ArrayItem.Assign step) so the ref is resolved without needing
|
|
240
|
+
// a separate index payload from the path converter.
|
|
241
|
+
__target__: true,
|
|
242
|
+
type: "string",
|
|
243
|
+
get: (animation) => (animation && typeof animation.index === "number" ? `/animations/${animation.index}` : ""),
|
|
244
|
+
getTarget: (animation) => animation._babylonAnimationGroup,
|
|
245
|
+
isReadOnly: true,
|
|
147
246
|
},
|
|
148
|
-
__array__: {},
|
|
149
247
|
};
|
|
150
248
|
const meshesTree = {
|
|
151
|
-
length:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
249
|
+
length: _CreateLengthAccessor((meshes) => meshes.length, (meshes) => meshes.map((mesh) => mesh.primitives[0]._instanceData?.babylonSourceMesh)),
|
|
250
|
+
__array__: {
|
|
251
|
+
__target__: true,
|
|
252
|
+
primitives: {
|
|
253
|
+
length: _CreateLengthAccessor((primitives) => primitives?.length ?? 0, (primitives) => primitives ?? []),
|
|
254
|
+
__array__: {
|
|
255
|
+
__target__: true,
|
|
256
|
+
material: {
|
|
257
|
+
type: "string",
|
|
258
|
+
// Read-only ref to the assigned material, JSON Pointer encoded.
|
|
259
|
+
get: (primitive) => (primitive.material !== undefined ? `/materials/${primitive.material}` : ""),
|
|
260
|
+
getTarget: (primitive) => primitive,
|
|
261
|
+
isReadOnly: true,
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
weights: {
|
|
266
|
+
length: _CreateLengthAccessor((weights) => weights?.length ?? 0, (weights) => weights ?? []),
|
|
267
|
+
__array__: {
|
|
268
|
+
__target__: true,
|
|
269
|
+
type: "number",
|
|
270
|
+
get: (weightValue) => weightValue,
|
|
271
|
+
getTarget: () => ({ __weightValue: true }),
|
|
272
|
+
isReadOnly: true,
|
|
273
|
+
},
|
|
274
|
+
},
|
|
156
275
|
},
|
|
157
|
-
__array__: {},
|
|
158
276
|
};
|
|
159
277
|
const camerasTree = {
|
|
278
|
+
length: _CreateLengthAccessor((cameras) => cameras.length, (cameras) => cameras.map((camera) => camera._babylonCamera)),
|
|
160
279
|
__array__: {
|
|
161
280
|
__target__: true,
|
|
162
281
|
orthographic: {
|
|
@@ -254,8 +373,33 @@ const camerasTree = {
|
|
|
254
373
|
},
|
|
255
374
|
};
|
|
256
375
|
const materialsTree = {
|
|
376
|
+
length: _CreateLengthAccessor((materials) => materials.length, (materials) => materials.map((material) => material._data?.[Constants.MATERIAL_TriangleFillMode]?.babylonMaterial)),
|
|
257
377
|
__array__: {
|
|
258
378
|
__target__: true,
|
|
379
|
+
doubleSided: {
|
|
380
|
+
type: "boolean",
|
|
381
|
+
get: (material, index, payload) => !GetMaterial(material, index, payload)?.backFaceCulling,
|
|
382
|
+
set: (value, material, index, payload) => {
|
|
383
|
+
const mat = GetMaterial(material, index, payload);
|
|
384
|
+
if (mat) {
|
|
385
|
+
mat.backFaceCulling = !value;
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
getTarget: (material, index, payload) => GetMaterial(material, index, payload),
|
|
389
|
+
getPropertyName: [() => "backFaceCulling"],
|
|
390
|
+
},
|
|
391
|
+
alphaCutoff: {
|
|
392
|
+
type: "number",
|
|
393
|
+
get: (material, index, payload) => GetMaterial(material, index, payload)?.alphaCutOff,
|
|
394
|
+
set: (value, material, index, payload) => {
|
|
395
|
+
const mat = GetMaterial(material, index, payload);
|
|
396
|
+
if (mat) {
|
|
397
|
+
mat.alphaCutOff = value;
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
getTarget: (material, index, payload) => GetMaterial(material, index, payload),
|
|
401
|
+
getPropertyName: [() => "alphaCutOff"],
|
|
402
|
+
},
|
|
259
403
|
emissiveFactor: {
|
|
260
404
|
type: "Color3",
|
|
261
405
|
get: (material, index, payload) => GetMaterial(material, index, payload).emissiveColor,
|
|
@@ -357,18 +501,24 @@ const materialsTree = {
|
|
|
357
501
|
KHR_materials_anisotropy: {
|
|
358
502
|
anisotropyStrength: {
|
|
359
503
|
type: "number",
|
|
360
|
-
get: (material, index, payload) => GetMaterial(material, index, payload)
|
|
504
|
+
get: (material, index, payload) => GetMaterial(material, index, payload)?.anisotropy?.intensity,
|
|
361
505
|
set: (value, material, index, payload) => {
|
|
362
|
-
GetMaterial(material, index, payload)
|
|
506
|
+
const mat = GetMaterial(material, index, payload);
|
|
507
|
+
if (mat) {
|
|
508
|
+
mat.anisotropy.intensity = value;
|
|
509
|
+
}
|
|
363
510
|
},
|
|
364
511
|
getTarget: (material, index, payload) => GetMaterial(material, index, payload),
|
|
365
512
|
getPropertyName: [() => "anisotropy.intensity"],
|
|
366
513
|
},
|
|
367
514
|
anisotropyRotation: {
|
|
368
515
|
type: "number",
|
|
369
|
-
get: (material, index, payload) => GetMaterial(material, index, payload)
|
|
516
|
+
get: (material, index, payload) => GetMaterial(material, index, payload)?.anisotropy?.angle,
|
|
370
517
|
set: (value, material, index, payload) => {
|
|
371
|
-
GetMaterial(material, index, payload)
|
|
518
|
+
const mat = GetMaterial(material, index, payload);
|
|
519
|
+
if (mat) {
|
|
520
|
+
mat.anisotropy.angle = value;
|
|
521
|
+
}
|
|
372
522
|
},
|
|
373
523
|
getTarget: (material, index, payload) => GetMaterial(material, index, payload),
|
|
374
524
|
getPropertyName: [() => "anisotropy.angle"],
|
|
@@ -500,7 +650,7 @@ const materialsTree = {
|
|
|
500
650
|
},
|
|
501
651
|
sheenRoughnessTexture: {
|
|
502
652
|
extensions: {
|
|
503
|
-
KHR_texture_transform: GenerateTextureMap("sheen", "
|
|
653
|
+
KHR_texture_transform: GenerateTextureMap("sheen", "textureRoughness"),
|
|
504
654
|
},
|
|
505
655
|
},
|
|
506
656
|
},
|
|
@@ -540,7 +690,10 @@ const materialsTree = {
|
|
|
540
690
|
},
|
|
541
691
|
transmissionTexture: {
|
|
542
692
|
extensions: {
|
|
543
|
-
KHR_texture_transform: GenerateTextureMap("subSurface", "refractionIntensityTexture"
|
|
693
|
+
KHR_texture_transform: GenerateTextureMap("subSurface", "refractionIntensityTexture", {
|
|
694
|
+
extensionKey: "KHR_materials_transmission",
|
|
695
|
+
texturePath: ["transmissionTexture"],
|
|
696
|
+
}),
|
|
544
697
|
},
|
|
545
698
|
},
|
|
546
699
|
},
|
|
@@ -553,7 +706,10 @@ const materialsTree = {
|
|
|
553
706
|
},
|
|
554
707
|
diffuseTransmissionTexture: {
|
|
555
708
|
extensions: {
|
|
556
|
-
KHR_texture_transform: GenerateTextureMap("subSurface", "translucencyIntensityTexture"
|
|
709
|
+
KHR_texture_transform: GenerateTextureMap("subSurface", "translucencyIntensityTexture", {
|
|
710
|
+
extensionKey: "KHR_materials_diffuse_transmission",
|
|
711
|
+
texturePath: ["diffuseTransmissionTexture"],
|
|
712
|
+
}),
|
|
557
713
|
},
|
|
558
714
|
},
|
|
559
715
|
diffuseTransmissionColorFactor: {
|
|
@@ -564,7 +720,10 @@ const materialsTree = {
|
|
|
564
720
|
},
|
|
565
721
|
diffuseTransmissionColorTexture: {
|
|
566
722
|
extensions: {
|
|
567
|
-
KHR_texture_transform: GenerateTextureMap("subSurface", "translucencyColorTexture"
|
|
723
|
+
KHR_texture_transform: GenerateTextureMap("subSurface", "translucencyColorTexture", {
|
|
724
|
+
extensionKey: "KHR_materials_diffuse_transmission",
|
|
725
|
+
texturePath: ["diffuseTransmissionColorTexture"],
|
|
726
|
+
}),
|
|
568
727
|
},
|
|
569
728
|
},
|
|
570
729
|
},
|
|
@@ -589,7 +748,7 @@ const materialsTree = {
|
|
|
589
748
|
},
|
|
590
749
|
thicknessTexture: {
|
|
591
750
|
extensions: {
|
|
592
|
-
KHR_texture_transform: GenerateTextureMap("subSurface", "thicknessTexture"),
|
|
751
|
+
KHR_texture_transform: GenerateTextureMap("subSurface", "thicknessTexture", { extensionKey: "KHR_materials_volume", texturePath: ["thicknessTexture"] }),
|
|
593
752
|
},
|
|
594
753
|
},
|
|
595
754
|
},
|
|
@@ -599,12 +758,7 @@ const materialsTree = {
|
|
|
599
758
|
const extensionsTree = {
|
|
600
759
|
KHR_lights_punctual: {
|
|
601
760
|
lights: {
|
|
602
|
-
length:
|
|
603
|
-
type: "number",
|
|
604
|
-
get: (lights) => lights.length,
|
|
605
|
-
getTarget: (lights) => lights.map((light) => light._babylonLight),
|
|
606
|
-
getPropertyName: [(_lights) => "length"],
|
|
607
|
-
},
|
|
761
|
+
length: _CreateLengthAccessor((lights) => lights.length, (lights) => lights.map((light) => light._babylonLight)),
|
|
608
762
|
__array__: {
|
|
609
763
|
__target__: true,
|
|
610
764
|
color: {
|
|
@@ -649,12 +803,7 @@ const extensionsTree = {
|
|
|
649
803
|
},
|
|
650
804
|
EXT_lights_area: {
|
|
651
805
|
lights: {
|
|
652
|
-
length:
|
|
653
|
-
type: "number",
|
|
654
|
-
get: (lights) => lights.length,
|
|
655
|
-
getTarget: (lights) => lights.map((light) => light._babylonLight),
|
|
656
|
-
getPropertyName: [(_lights) => "length"],
|
|
657
|
-
},
|
|
806
|
+
length: _CreateLengthAccessor((lights) => lights.length, (lights) => lights.map((light) => light._babylonLight)),
|
|
658
807
|
__array__: {
|
|
659
808
|
__target__: true,
|
|
660
809
|
color: {
|
|
@@ -692,22 +841,12 @@ const extensionsTree = {
|
|
|
692
841
|
},
|
|
693
842
|
EXT_lights_ies: {
|
|
694
843
|
lights: {
|
|
695
|
-
length:
|
|
696
|
-
type: "number",
|
|
697
|
-
get: (lights) => lights.length,
|
|
698
|
-
getTarget: (lights) => lights.map((light) => light._babylonLight),
|
|
699
|
-
getPropertyName: [(_lights) => "length"],
|
|
700
|
-
},
|
|
844
|
+
length: _CreateLengthAccessor((lights) => lights.length, (lights) => lights.map((light) => light._babylonLight)),
|
|
701
845
|
},
|
|
702
846
|
},
|
|
703
847
|
EXT_lights_image_based: {
|
|
704
848
|
lights: {
|
|
705
|
-
length:
|
|
706
|
-
type: "number",
|
|
707
|
-
get: (lights) => lights.length,
|
|
708
|
-
getTarget: (lights) => lights.map((light) => light._babylonTexture),
|
|
709
|
-
getPropertyName: [(_lights) => "length"],
|
|
710
|
-
},
|
|
849
|
+
length: _CreateLengthAccessor((lights) => lights.length, (lights) => lights.map((light) => light._babylonTexture)),
|
|
711
850
|
__array__: {
|
|
712
851
|
__target__: true,
|
|
713
852
|
intensity: {
|
|
@@ -746,7 +885,159 @@ function GetTexture(material, payload, textureType, textureInObject) {
|
|
|
746
885
|
function GetMaterial(material, _index, payload) {
|
|
747
886
|
return material._data?.[payload?.fillMode ?? Constants.MATERIAL_TriangleFillMode]?.babylonMaterial;
|
|
748
887
|
}
|
|
749
|
-
function
|
|
888
|
+
function _getNodeMorphTargetManager(node) {
|
|
889
|
+
const tn = node?._babylonTransformNode;
|
|
890
|
+
if (!tn) {
|
|
891
|
+
return undefined;
|
|
892
|
+
}
|
|
893
|
+
// Single primitive: transform node IS the mesh with morphTargetManager
|
|
894
|
+
if (tn.morphTargetManager) {
|
|
895
|
+
return tn.morphTargetManager;
|
|
896
|
+
}
|
|
897
|
+
// Multiple primitives: check each primitive mesh and its source
|
|
898
|
+
const primMeshes = node._primitiveBabylonMeshes;
|
|
899
|
+
if (primMeshes) {
|
|
900
|
+
for (const mesh of primMeshes) {
|
|
901
|
+
if (mesh?.morphTargetManager) {
|
|
902
|
+
return mesh.morphTargetManager;
|
|
903
|
+
}
|
|
904
|
+
// Check source mesh for instanced meshes
|
|
905
|
+
if (mesh?.sourceMesh?.morphTargetManager) {
|
|
906
|
+
return mesh.sourceMesh.morphTargetManager;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return undefined;
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* KHR_interactivity test assets routinely use a glTF hierarchy where the
|
|
914
|
+
* **parent** node has no `mesh` but a descendant does. The Khronos morph-weight
|
|
915
|
+
* tests query `/nodes/<parent>/weights/*` and expect the result to come from
|
|
916
|
+
* the morph targets of the first descendant mesh. To support this we walk the
|
|
917
|
+
* Babylon-side scene graph below the queried INode looking for a Mesh that has
|
|
918
|
+
* a morphTargetManager.
|
|
919
|
+
*
|
|
920
|
+
* For multi-primitive meshes (one INode → several Babylon meshes parented to a
|
|
921
|
+
* wrapper TransformNode) we also collect the sibling primitives so a `set`
|
|
922
|
+
* touches every mesh that shares the manager.
|
|
923
|
+
* @param node the glTF node to start the lookup from
|
|
924
|
+
* @returns the active morph target manager and every Babylon mesh that shares
|
|
925
|
+
* it, or `undefined` when no morph target manager is reachable from the node.
|
|
926
|
+
*/
|
|
927
|
+
function _findNodeMorphTargets(node) {
|
|
928
|
+
const tn = node?._babylonTransformNode;
|
|
929
|
+
if (!tn) {
|
|
930
|
+
return undefined;
|
|
931
|
+
}
|
|
932
|
+
// Direct: this node's own mesh has a morph target manager.
|
|
933
|
+
const directMtm = _getNodeMorphTargetManager(node);
|
|
934
|
+
if (directMtm && node._primitiveBabylonMeshes && node._primitiveBabylonMeshes.length > 0) {
|
|
935
|
+
return { mtm: directMtm, meshes: node._primitiveBabylonMeshes };
|
|
936
|
+
}
|
|
937
|
+
// Fallback: search descendants in the Babylon scene graph for the first
|
|
938
|
+
// mesh that has a morph target manager, then collect every sibling mesh
|
|
939
|
+
// that shares it (covers the multi-primitive case).
|
|
940
|
+
const descendants = tn.getDescendants(false);
|
|
941
|
+
for (const desc of descendants) {
|
|
942
|
+
const candidate = desc;
|
|
943
|
+
const mtm = candidate.morphTargetManager ?? candidate.sourceMesh?.morphTargetManager;
|
|
944
|
+
if (!mtm) {
|
|
945
|
+
continue;
|
|
946
|
+
}
|
|
947
|
+
const meshes = [];
|
|
948
|
+
const parent = candidate.parent;
|
|
949
|
+
if (parent) {
|
|
950
|
+
for (const sib of parent.getChildMeshes(true)) {
|
|
951
|
+
const sibMtm = sib.morphTargetManager ?? sib.sourceMesh?.morphTargetManager;
|
|
952
|
+
if (sibMtm === mtm) {
|
|
953
|
+
meshes.push(sib);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
if (meshes.length === 0) {
|
|
958
|
+
meshes.push(candidate);
|
|
959
|
+
}
|
|
960
|
+
return { mtm, meshes };
|
|
961
|
+
}
|
|
962
|
+
return undefined;
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Collapse float32-precision artifacts back to the closest "clean" double.
|
|
966
|
+
*
|
|
967
|
+
* glTF stores numbers as JSON, but tools usually serialize float32 morph weights
|
|
968
|
+
* with their full double-precision text — `0.1` becomes `0.10000000149011612`.
|
|
969
|
+
* KHR_interactivity tests then compare the read-back weight via strict `math/eq`
|
|
970
|
+
* against literals like `0.1`, which fails because the two doubles aren't bitwise
|
|
971
|
+
* equal. Rounding the value to 7 significant figures (the precision of a float32)
|
|
972
|
+
* recovers the original "clean" double for any value that survived a float32
|
|
973
|
+
* round-trip while leaving genuinely high-precision doubles essentially intact.
|
|
974
|
+
* @param v the value to round
|
|
975
|
+
* @returns the rounded value, or the input unchanged if it is not finite
|
|
976
|
+
*/
|
|
977
|
+
function _roundFloat32Artifact(v) {
|
|
978
|
+
if (!Number.isFinite(v)) {
|
|
979
|
+
return v;
|
|
980
|
+
}
|
|
981
|
+
return parseFloat(v.toPrecision(7));
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Read the KHR_texture_transform object stored in the source glTF JSON for a
|
|
985
|
+
* texture-info that lives under one of the material's extensions, creating
|
|
986
|
+
* empty parent objects on demand so callers can write through it. Returns
|
|
987
|
+
* `undefined` when the input shape is incompatible.
|
|
988
|
+
* @param material the source IMaterial owning the extension
|
|
989
|
+
* @param gltfPath the path describing where the texture-info lives
|
|
990
|
+
* @param createMissing when true, create missing parent objects so writes succeed
|
|
991
|
+
* @returns the glTF-side KHR_texture_transform object, or undefined
|
|
992
|
+
*/
|
|
993
|
+
function _gltfTextureTransform(material, gltfPath, createMissing) {
|
|
994
|
+
if (!material) {
|
|
995
|
+
return undefined;
|
|
996
|
+
}
|
|
997
|
+
let extensions = material.extensions;
|
|
998
|
+
if (!extensions) {
|
|
999
|
+
if (!createMissing) {
|
|
1000
|
+
return undefined;
|
|
1001
|
+
}
|
|
1002
|
+
extensions = {};
|
|
1003
|
+
material.extensions = extensions;
|
|
1004
|
+
}
|
|
1005
|
+
let cursor = extensions[gltfPath.extensionKey];
|
|
1006
|
+
if (!cursor) {
|
|
1007
|
+
if (!createMissing) {
|
|
1008
|
+
return undefined;
|
|
1009
|
+
}
|
|
1010
|
+
cursor = {};
|
|
1011
|
+
extensions[gltfPath.extensionKey] = cursor;
|
|
1012
|
+
}
|
|
1013
|
+
for (const key of gltfPath.texturePath) {
|
|
1014
|
+
let next = cursor[key];
|
|
1015
|
+
if (!next) {
|
|
1016
|
+
if (!createMissing) {
|
|
1017
|
+
return undefined;
|
|
1018
|
+
}
|
|
1019
|
+
next = {};
|
|
1020
|
+
cursor[key] = next;
|
|
1021
|
+
}
|
|
1022
|
+
cursor = next;
|
|
1023
|
+
}
|
|
1024
|
+
if (!cursor.extensions) {
|
|
1025
|
+
if (!createMissing) {
|
|
1026
|
+
return undefined;
|
|
1027
|
+
}
|
|
1028
|
+
cursor.extensions = {};
|
|
1029
|
+
}
|
|
1030
|
+
let xform = cursor.extensions.KHR_texture_transform;
|
|
1031
|
+
if (!xform) {
|
|
1032
|
+
if (!createMissing) {
|
|
1033
|
+
return undefined;
|
|
1034
|
+
}
|
|
1035
|
+
xform = {};
|
|
1036
|
+
cursor.extensions.KHR_texture_transform = xform;
|
|
1037
|
+
}
|
|
1038
|
+
return xform;
|
|
1039
|
+
}
|
|
1040
|
+
function GenerateTextureMap(textureType, textureInObject, gltfPath) {
|
|
750
1041
|
return {
|
|
751
1042
|
offset: {
|
|
752
1043
|
componentsCount: 2,
|
|
@@ -754,12 +1045,29 @@ function GenerateTextureMap(textureType, textureInObject) {
|
|
|
754
1045
|
type: "Vector2",
|
|
755
1046
|
get: (material, _index, payload) => {
|
|
756
1047
|
const texture = GetTexture(material, payload, textureType, textureInObject);
|
|
757
|
-
|
|
1048
|
+
if (texture) {
|
|
1049
|
+
return new Vector2(texture.uOffset, texture.vOffset);
|
|
1050
|
+
}
|
|
1051
|
+
if (gltfPath) {
|
|
1052
|
+
const xform = _gltfTextureTransform(material, gltfPath, false);
|
|
1053
|
+
const o = xform?.offset;
|
|
1054
|
+
return new Vector2(o?.[0] ?? 0, o?.[1] ?? 0);
|
|
1055
|
+
}
|
|
1056
|
+
return new Vector2(0, 0);
|
|
758
1057
|
},
|
|
759
1058
|
getTarget: GetMaterial,
|
|
760
1059
|
set: (value, material, _index, payload) => {
|
|
761
1060
|
const texture = GetTexture(material, payload, textureType, textureInObject);
|
|
762
|
-
(
|
|
1061
|
+
if (texture) {
|
|
1062
|
+
texture.uOffset = value.x;
|
|
1063
|
+
texture.vOffset = value.y;
|
|
1064
|
+
}
|
|
1065
|
+
if (gltfPath) {
|
|
1066
|
+
const xform = _gltfTextureTransform(material, gltfPath, true);
|
|
1067
|
+
if (xform) {
|
|
1068
|
+
xform.offset = [value.x, value.y];
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
763
1071
|
},
|
|
764
1072
|
getPropertyName: [
|
|
765
1073
|
() => `${textureType}${textureInObject ? "." + textureInObject : ""}.uOffset`,
|
|
@@ -768,9 +1076,30 @@ function GenerateTextureMap(textureType, textureInObject) {
|
|
|
768
1076
|
},
|
|
769
1077
|
rotation: {
|
|
770
1078
|
type: "number",
|
|
771
|
-
get: (material, _index, payload) =>
|
|
1079
|
+
get: (material, _index, payload) => {
|
|
1080
|
+
const texture = GetTexture(material, payload, textureType, textureInObject);
|
|
1081
|
+
if (texture) {
|
|
1082
|
+
return texture.wAng;
|
|
1083
|
+
}
|
|
1084
|
+
if (gltfPath) {
|
|
1085
|
+
const xform = _gltfTextureTransform(material, gltfPath, false);
|
|
1086
|
+
return xform?.rotation ?? 0;
|
|
1087
|
+
}
|
|
1088
|
+
return 0;
|
|
1089
|
+
},
|
|
772
1090
|
getTarget: GetMaterial,
|
|
773
|
-
set: (value, material, _index, payload) =>
|
|
1091
|
+
set: (value, material, _index, payload) => {
|
|
1092
|
+
const texture = GetTexture(material, payload, textureType, textureInObject);
|
|
1093
|
+
if (texture) {
|
|
1094
|
+
texture.wAng = value;
|
|
1095
|
+
}
|
|
1096
|
+
if (gltfPath) {
|
|
1097
|
+
const xform = _gltfTextureTransform(material, gltfPath, true);
|
|
1098
|
+
if (xform) {
|
|
1099
|
+
xform.rotation = value;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
},
|
|
774
1103
|
getPropertyName: [() => `${textureType}${textureInObject ? "." + textureInObject : ""}.wAng`],
|
|
775
1104
|
},
|
|
776
1105
|
scale: {
|
|
@@ -778,12 +1107,29 @@ function GenerateTextureMap(textureType, textureInObject) {
|
|
|
778
1107
|
type: "Vector2",
|
|
779
1108
|
get: (material, _index, payload) => {
|
|
780
1109
|
const texture = GetTexture(material, payload, textureType, textureInObject);
|
|
781
|
-
|
|
1110
|
+
if (texture) {
|
|
1111
|
+
return new Vector2(texture.uScale, texture.vScale);
|
|
1112
|
+
}
|
|
1113
|
+
if (gltfPath) {
|
|
1114
|
+
const xform = _gltfTextureTransform(material, gltfPath, false);
|
|
1115
|
+
const s = xform?.scale;
|
|
1116
|
+
return new Vector2(s?.[0] ?? 1, s?.[1] ?? 1);
|
|
1117
|
+
}
|
|
1118
|
+
return new Vector2(1, 1);
|
|
782
1119
|
},
|
|
783
1120
|
getTarget: GetMaterial,
|
|
784
1121
|
set: (value, material, index, payload) => {
|
|
785
1122
|
const texture = GetTexture(material, payload, textureType, textureInObject);
|
|
786
|
-
(
|
|
1123
|
+
if (texture) {
|
|
1124
|
+
texture.uScale = value.x;
|
|
1125
|
+
texture.vScale = value.y;
|
|
1126
|
+
}
|
|
1127
|
+
if (gltfPath) {
|
|
1128
|
+
const xform = _gltfTextureTransform(material, gltfPath, true);
|
|
1129
|
+
if (xform) {
|
|
1130
|
+
xform.scale = [value.x, value.y];
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
787
1133
|
},
|
|
788
1134
|
getPropertyName: [
|
|
789
1135
|
() => `${textureType}${textureInObject ? "." + textureInObject : ""}.uScale`,
|
|
@@ -792,13 +1138,69 @@ function GenerateTextureMap(textureType, textureInObject) {
|
|
|
792
1138
|
},
|
|
793
1139
|
};
|
|
794
1140
|
}
|
|
1141
|
+
const scenesTree = {
|
|
1142
|
+
length: _CreateLengthAccessor((scenes) => scenes.length, (scenes) => scenes),
|
|
1143
|
+
__array__: {
|
|
1144
|
+
__target__: true,
|
|
1145
|
+
nodes: {
|
|
1146
|
+
length: _CreateLengthAccessor((nodes) => nodes?.length ?? 0, (nodes) => nodes ?? []),
|
|
1147
|
+
__array__: {
|
|
1148
|
+
__target__: true,
|
|
1149
|
+
type: "string",
|
|
1150
|
+
// Indexed scene root: the underlying value is the INode index;
|
|
1151
|
+
// KHR_interactivity expects a ref-typed JSON Pointer string.
|
|
1152
|
+
get: (nodeIndex) => (typeof nodeIndex === "number" ? `/nodes/${nodeIndex}` : ""),
|
|
1153
|
+
getTarget: () => ({ __nodeIndex: true }),
|
|
1154
|
+
isReadOnly: true,
|
|
1155
|
+
},
|
|
1156
|
+
},
|
|
1157
|
+
},
|
|
1158
|
+
};
|
|
1159
|
+
const skinsTree = {
|
|
1160
|
+
length: _CreateLengthAccessor((skins) => skins.length, (skins) => skins.map((skin) => skin._data?.babylonSkeleton)),
|
|
1161
|
+
__array__: {
|
|
1162
|
+
__target__: true,
|
|
1163
|
+
joints: {
|
|
1164
|
+
length: _CreateLengthAccessor((joints) => joints?.length ?? 0, (joints) => joints ?? []),
|
|
1165
|
+
__array__: {
|
|
1166
|
+
__target__: true,
|
|
1167
|
+
type: "string",
|
|
1168
|
+
// Indexed skin joint: returns a ref to the joint node.
|
|
1169
|
+
get: (jointIndex) => (typeof jointIndex === "number" ? `/nodes/${jointIndex}` : ""),
|
|
1170
|
+
getTarget: () => ({ __nodeIndex: true }),
|
|
1171
|
+
isReadOnly: true,
|
|
1172
|
+
},
|
|
1173
|
+
},
|
|
1174
|
+
skeleton: {
|
|
1175
|
+
type: "string",
|
|
1176
|
+
// Skin's skeleton root: returns a ref to the root node, or empty
|
|
1177
|
+
// (null ref) when no skeleton root is declared.
|
|
1178
|
+
get: (skin) => {
|
|
1179
|
+
const skeleton = skin.skeleton;
|
|
1180
|
+
return typeof skeleton === "number" ? `/nodes/${skeleton}` : "";
|
|
1181
|
+
},
|
|
1182
|
+
getTarget: (skin) => skin,
|
|
1183
|
+
isReadOnly: true,
|
|
1184
|
+
},
|
|
1185
|
+
},
|
|
1186
|
+
};
|
|
795
1187
|
const objectModelMapping = {
|
|
1188
|
+
scene: {
|
|
1189
|
+
__target__: true,
|
|
1190
|
+
type: "number",
|
|
1191
|
+
get: (sceneIndex) => sceneIndex ?? 0,
|
|
1192
|
+
getTarget: () => ({ __gltfRoot: true }),
|
|
1193
|
+
isReadOnly: true,
|
|
1194
|
+
getPropertyName: [() => "scene"],
|
|
1195
|
+
},
|
|
796
1196
|
cameras: camerasTree,
|
|
797
1197
|
nodes: nodesTree,
|
|
798
1198
|
materials: materialsTree,
|
|
799
1199
|
extensions: extensionsTree,
|
|
800
1200
|
animations: animationsTree,
|
|
801
1201
|
meshes: meshesTree,
|
|
1202
|
+
scenes: scenesTree,
|
|
1203
|
+
skins: skinsTree,
|
|
802
1204
|
};
|
|
803
1205
|
/**
|
|
804
1206
|
* get a path-to-object converter for the given glTF tree
|