@babylonjs/loaders 9.28.0 → 9.30.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.
Files changed (38) hide show
  1. package/BVH/bvhLoader.js +1 -3
  2. package/BVH/bvhLoader.js.map +1 -1
  3. package/OBJ/mtlFileLoader.js +9 -35
  4. package/OBJ/mtlFileLoader.js.map +1 -1
  5. package/OBJ/objFileLoader.pure.js +18 -23
  6. package/OBJ/objFileLoader.pure.js.map +1 -1
  7. package/OBJ/solidParser.js +11 -25
  8. package/OBJ/solidParser.js.map +1 -1
  9. package/SPLAT/splatFileLoader.pure.js +86 -82
  10. package/SPLAT/splatFileLoader.pure.js.map +1 -1
  11. package/STL/stlFileLoader.pure.d.ts +1 -0
  12. package/STL/stlFileLoader.pure.js +18 -16
  13. package/STL/stlFileLoader.pure.js.map +1 -1
  14. package/glTF/1.0/glTFLoader.pure.js +30 -37
  15. package/glTF/1.0/glTFLoader.pure.js.map +1 -1
  16. package/glTF/2.0/Extensions/EXT_lights_area.pure.js +16 -23
  17. package/glTF/2.0/Extensions/EXT_lights_area.pure.js.map +1 -1
  18. package/glTF/2.0/Extensions/EXT_lights_ies.pure.js +1 -3
  19. package/glTF/2.0/Extensions/EXT_lights_ies.pure.js.map +1 -1
  20. package/glTF/2.0/Extensions/KHR_gaussian_splatting.pure.js +1 -3
  21. package/glTF/2.0/Extensions/KHR_gaussian_splatting.pure.js.map +1 -1
  22. package/glTF/2.0/Extensions/KHR_interactivity/declarationMapper.js +3 -3
  23. package/glTF/2.0/Extensions/KHR_interactivity/declarationMapper.js.map +1 -1
  24. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphExporter.d.ts +15 -1
  25. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphExporter.js +70 -21
  26. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphExporter.js.map +1 -1
  27. package/glTF/2.0/Extensions/KHR_interactivity/interactivityNodeReference.d.ts +13 -0
  28. package/glTF/2.0/Extensions/KHR_interactivity/interactivityNodeReference.js +16 -0
  29. package/glTF/2.0/Extensions/KHR_interactivity/interactivityNodeReference.js.map +1 -0
  30. package/glTF/2.0/Extensions/KHR_lights_punctual.pure.js +18 -25
  31. package/glTF/2.0/Extensions/KHR_lights_punctual.pure.js.map +1 -1
  32. package/glTF/2.0/Extensions/KHR_node_hoverability.pure.js +3 -10
  33. package/glTF/2.0/Extensions/KHR_node_hoverability.pure.js.map +1 -1
  34. package/glTF/2.0/Extensions/KHR_node_selectability.pure.js +2 -5
  35. package/glTF/2.0/Extensions/KHR_node_selectability.pure.js.map +1 -1
  36. package/glTF/2.0/glTFLoader.pure.js +15 -37
  37. package/glTF/2.0/glTFLoader.pure.js.map +1 -1
  38. package/package.json +3 -3
@@ -129,7 +129,7 @@ export interface IKHRInteractivityExportProvider {
129
129
  /**
130
130
  * Builds the canonical extension after glTF entity indices are finalized.
131
131
  * @param context final serializer remapping context
132
- * @returns ratified KHR_interactivity extension payload
132
+ * @returns KHR_interactivity extension payload
133
133
  */
134
134
  build(context: IKHRInteractivitySerializerContext): IKHRInteractivity;
135
135
  }
@@ -141,6 +141,12 @@ export interface IKHRInteractivityExportOptions {
141
141
  document?: IKHRInteractivityDocument;
142
142
  /** Loader glTF tree used to resolve original entity references. */
143
143
  sourceGLTF?: IGLTF;
144
+ /**
145
+ * Whether export patches the retained source document instead of reserializing the scene.
146
+ * Preserves source entity indices, extension-backed pointers, and imported compatibility flow sockets.
147
+ * Requires sourceGLTF and buildWithSourceIndices; runtime scene changes are not exported.
148
+ */
149
+ preserveSourceDocument?: boolean;
144
150
  /** Root default graph index. Defaults to the canonical document selection or zero. */
145
151
  defaultGraphIndex?: number;
146
152
  /** Target animation frame rate used by imported animation composites. Defaults to 60. */
@@ -171,6 +177,7 @@ export declare class KHRInteractivityExportPlan implements IKHRInteractivityExpo
171
177
  private _additionalExtensionsUsed;
172
178
  private readonly _rootDiagnostics;
173
179
  private _isPreflight;
180
+ private _sourceIndexContext?;
174
181
  /** Whether KHR_interactivity is required in the exported asset. */
175
182
  readonly required: boolean;
176
183
  /** Additional operation/companion extensions referenced by the exported graphs. */
@@ -195,7 +202,14 @@ export declare class KHRInteractivityExportPlan implements IKHRInteractivityExpo
195
202
  * @returns canonical KHR_interactivity payload
196
203
  */
197
204
  build(context: IKHRInteractivitySerializerContext): IKHRInteractivity;
205
+ /**
206
+ * Builds a graph for patching the original glTF document using its existing entity indices.
207
+ * Companion node extensions are already present in that document and are retained by the patcher.
208
+ * @returns canonical KHR_interactivity payload with source glTF indices
209
+ */
210
+ buildWithSourceIndices(): IKHRInteractivity;
198
211
  private _getSourceGraph;
212
+ private _createSourceIndexContext;
199
213
  private _validateBuildWithSourceIndices;
200
214
  private _analyzeGraphs;
201
215
  private _classifyStandaloneBlock;
@@ -401,6 +401,9 @@ export class KHRInteractivityExportPlan {
401
401
  this._additionalExtensionsUsed = [];
402
402
  this._rootDiagnostics = [];
403
403
  this._isPreflight = false;
404
+ if (_options.preserveSourceDocument && !_options.sourceGLTF) {
405
+ throw new Error("Source-preserving interactivity export requires the source glTF document.");
406
+ }
404
407
  this.required = _options.required ?? true;
405
408
  this.additionalExtensionsRequired = Array.from(new Set(_options.additionalExtensionsRequired ?? [])).sort();
406
409
  this._refreshAnalysis();
@@ -463,6 +466,9 @@ export class KHRInteractivityExportPlan {
463
466
  * @returns canonical KHR_interactivity payload
464
467
  */
465
468
  build(context) {
469
+ if (this._options.preserveSourceDocument && context !== this._sourceIndexContext) {
470
+ throw new Error("Source-preserving interactivity export must use buildWithSourceIndices().");
471
+ }
466
472
  this._refreshAnalysis();
467
473
  if (!this._analysis.representable) {
468
474
  throw new KHRInteractivityExportError(this._analysis.diagnostics);
@@ -489,6 +495,17 @@ export class KHRInteractivityExportPlan {
489
495
  }
490
496
  return source;
491
497
  }
498
+ /**
499
+ * Builds a graph for patching the original glTF document using its existing entity indices.
500
+ * Companion node extensions are already present in that document and are retained by the patcher.
501
+ * @returns canonical KHR_interactivity payload with source glTF indices
502
+ */
503
+ buildWithSourceIndices() {
504
+ if (!this._options.sourceGLTF) {
505
+ throw new Error("Source glTF data is required to retain entity indices.");
506
+ }
507
+ return this.build(this._createSourceIndexContext());
508
+ }
492
509
  _getSourceGraph(graph, graphIndex) {
493
510
  const documentGraph = this._options.document?.graphs[graphIndex]?.source;
494
511
  if (documentGraph) {
@@ -497,11 +514,13 @@ export class KHRInteractivityExportPlan {
497
514
  const provenance = graph.metadata?.khrInteractivity;
498
515
  return provenance?.source ? CloneKHRInteractivityGraph(provenance.source) : undefined;
499
516
  }
500
- _validateBuildWithSourceIndices() {
501
- const diagnostics = [];
517
+ _createSourceIndexContext() {
518
+ if (this._sourceIndexContext) {
519
+ return this._sourceIndexContext;
520
+ }
502
521
  const sourceGLTF = this._options.sourceGLTF;
503
522
  const indexOrUndefined = (index) => (index !== undefined && index >= 0 ? index : undefined);
504
- const context = {
523
+ return (this._sourceIndexContext = {
505
524
  getNodeCount: () => sourceGLTF?.nodes?.length ?? 0,
506
525
  getNodeIndex: (node) => indexOrUndefined(sourceGLTF?.nodes?.findIndex((candidate) => candidate._babylonTransformNode === node)),
507
526
  getAnimationIndex: (animation) => indexOrUndefined(sourceGLTF?.animations?.findIndex((candidate) => candidate._babylonAnimationGroup === animation)),
@@ -509,7 +528,11 @@ export class KHRInteractivityExportPlan {
509
528
  getMaterialIndex: (material) => indexOrUndefined(sourceGLTF?.materials?.findIndex((candidate) => Object.values(candidate._data ?? {}).some((entry) => entry.babylonMaterial === material))),
510
529
  getRootIndex: (collection, entity) => this._getSourceRootIndex(collection, entity),
511
530
  setNodeExtension: () => { },
512
- };
531
+ });
532
+ }
533
+ _validateBuildWithSourceIndices() {
534
+ const diagnostics = [];
535
+ const context = this._createSourceIndexContext();
513
536
  this._isPreflight = true;
514
537
  try {
515
538
  for (const analysis of this._graphAnalyses) {
@@ -639,8 +662,9 @@ export class KHRInteractivityExportPlan {
639
662
  const declaration = source.declarations?.[sourceNode.declaration];
640
663
  const declarationModel = this._options.document?.graphs[graphIndex]?.declarations[sourceNode.declaration];
641
664
  const operation = declaration ? _FullOperationName(declaration.op, declaration.extension) : "";
642
- const mapping = declaration && declarationModel?.support !== "unsupported-extension" ? getMappingForDeclaration(declaration, false) : undefined;
643
665
  const blocks = groups.get(nodeIndex) ?? [];
666
+ const hasCompatibilityRuntime = this._options.preserveSourceDocument && blocks.some((block) => block.getClassName() !== _UnsupportedBlockClassName);
667
+ const mapping = declaration && (declarationModel?.support !== "unsupported-extension" || hasCompatibilityRuntime) ? getMappingForDeclaration(declaration, false) : undefined;
644
668
  const diagnostics = [];
645
669
  let classification = mapping && mapping.blocks.length > 1 ? "inverse-composite" : "exact";
646
670
  if (!declaration) {
@@ -653,7 +677,7 @@ export class KHRInteractivityExportPlan {
653
677
  message: `Declaration index ${sourceNode.declaration} is unavailable.`,
654
678
  });
655
679
  }
656
- else if (declaration.extension === "BABYLON") {
680
+ else if (declaration.extension === "BABYLON" && !this._options.preserveSourceDocument) {
657
681
  classification = "unsupported";
658
682
  _PushDiagnostic(diagnostics, {
659
683
  code: "BLOCK_UNSUPPORTED",
@@ -1007,7 +1031,7 @@ export class KHRInteractivityExportPlan {
1007
1031
  });
1008
1032
  }
1009
1033
  }
1010
- return graph;
1034
+ return this._options.preserveSourceDocument && _JsonEquivalent(graph, analysis.source) ? CloneKHRInteractivityGraph(analysis.source) : graph;
1011
1035
  }
1012
1036
  _rebuildNode(graph, sourceNode, logicalNode, graphAnalysis, context, diagnostics) {
1013
1037
  const node = _CloneJson(sourceNode);
@@ -1178,7 +1202,7 @@ export class KHRInteractivityExportPlan {
1178
1202
  ? _CloneJson(sourceNode.flows[socket])
1179
1203
  : { node: provenance.nodeIndex };
1180
1204
  rebuiltFlow.node = provenance.nodeIndex;
1181
- if (provenance.socket === "in") {
1205
+ if (provenance.socket === "in" && !(this._options.preserveSourceDocument && rebuiltFlow.socket === "in")) {
1182
1206
  delete rebuiltFlow.socket;
1183
1207
  }
1184
1208
  else {
@@ -1187,7 +1211,11 @@ export class KHRInteractivityExportPlan {
1187
1211
  _SetOwnProperty(rebuiltFlows, socket, rebuiltFlow);
1188
1212
  }
1189
1213
  if (Object.keys(rebuiltFlows).length > 0) {
1190
- node.flows = rebuiltFlows;
1214
+ node.flows = this._options.preserveSourceDocument
1215
+ ? Object.fromEntries([...new Set([...Object.keys(sourceNode.flows ?? {}), ...Object.keys(rebuiltFlows)])]
1216
+ .filter((socket) => Object.prototype.hasOwnProperty.call(rebuiltFlows, socket))
1217
+ .map((socket) => [socket, rebuiltFlows[socket]]))
1218
+ : rebuiltFlows;
1191
1219
  }
1192
1220
  else {
1193
1221
  delete node.flows;
@@ -1304,7 +1332,7 @@ export class KHRInteractivityExportPlan {
1304
1332
  return Object.prototype.hasOwnProperty.call(node.flows ?? {}, socket);
1305
1333
  }
1306
1334
  const fixed = _GetOwn(mappings, socket);
1307
- if (fixed && !fixed.compatibilityOnly) {
1335
+ if (fixed && (!fixed.compatibilityOnly || (this._options.preserveSourceDocument && Object.prototype.hasOwnProperty.call(node.flows ?? {}, socket)))) {
1308
1336
  return true;
1309
1337
  }
1310
1338
  if (!Object.keys(mappings ?? {}).some((key) => key.startsWith("[") && key.endsWith("]"))) {
@@ -1451,7 +1479,7 @@ export class KHRInteractivityExportPlan {
1451
1479
  const rebuiltReference = _CloneJson(sourceValue);
1452
1480
  delete rebuiltReference.value;
1453
1481
  rebuiltReference.node = provenance.nodeIndex;
1454
- if (provenance.socket === "value") {
1482
+ if (provenance.socket === "value" && !(this._options.preserveSourceDocument && rebuiltReference.socket === "value")) {
1455
1483
  delete rebuiltReference.socket;
1456
1484
  }
1457
1485
  else {
@@ -1643,8 +1671,7 @@ export class KHRInteractivityExportPlan {
1643
1671
  _JsonEquivalent(_GetConfigurationBlock(logicalNode, property)?.config?.[property.name], provenance.runtimeValue) &&
1644
1672
  !_IsValidConfigurationValue(provenance.sourceValue, property, graph, this._options.sourceGLTF?.nodes?.length);
1645
1673
  if (!preservesFallback && property.indexSource === "assetNodes" && current.length === 1 && typeof current[0] === "number") {
1646
- const sourceNode = this._options.sourceGLTF?.nodes?.[current[0]]?._babylonTransformNode;
1647
- const remapped = sourceNode ? context.getNodeIndex(sourceNode) : undefined;
1674
+ const remapped = this._getRemappedRootIndex("nodes", current[0], context);
1648
1675
  if (remapped === undefined) {
1649
1676
  _PushDiagnostic(diagnostics, {
1650
1677
  code: "REFERENCE_UNRESOLVED",
@@ -1687,6 +1714,9 @@ export class KHRInteractivityExportPlan {
1687
1714
  const rootMatch = typeof rootReference === "string" ? /^\/([^/]+)$/.exec(rootReference) : undefined;
1688
1715
  collection = rootMatch?.[1] ?? "";
1689
1716
  if (!_KnownIndexedRootCollections.has(collection)) {
1717
+ if (this._options.preserveSourceDocument) {
1718
+ continue;
1719
+ }
1690
1720
  _PushDiagnostic(diagnostics, {
1691
1721
  code: "REFERENCE_UNRESOLVED",
1692
1722
  graphIndex,
@@ -1712,6 +1742,9 @@ export class KHRInteractivityExportPlan {
1712
1742
  const socket = node.values?.[indexPlaceholder[1]];
1713
1743
  const path = `/graphs/${graphIndex}/nodes/${logicalNode.sourceIndex}/values/${indexPlaceholder[1]}`;
1714
1744
  if (!socket || "node" in socket) {
1745
+ if (socket && this._options.preserveSourceDocument) {
1746
+ continue;
1747
+ }
1715
1748
  _PushDiagnostic(diagnostics, {
1716
1749
  code: "REFERENCE_UNRESOLVED",
1717
1750
  graphIndex,
@@ -1967,7 +2000,7 @@ export class KHRInteractivityExportPlan {
1967
2000
  for (const [socket, value] of Object.entries(node.values ?? {})) {
1968
2001
  if (!("node" in value) && graph.types?.[value.type]?.signature === "ref" && value.value) {
1969
2002
  const pointerCollection = this._getPointerReferenceCollection(node, mapping, socket);
1970
- if (pointerCollection === null) {
2003
+ if (pointerCollection === null && !(this._options.preserveSourceDocument && typeof value.value[0] === "string")) {
1971
2004
  _PushDiagnostic(diagnostics, {
1972
2005
  code: "REFERENCE_UNRESOLVED",
1973
2006
  graphIndex,
@@ -2060,6 +2093,9 @@ export class KHRInteractivityExportPlan {
2060
2093
  return undefined;
2061
2094
  }
2062
2095
  _getRemappedRootIndex(collection, sourceIndex, context) {
2096
+ if (this._options.preserveSourceDocument) {
2097
+ return this._options.sourceGLTF?.[collection]?.[sourceIndex] ? sourceIndex : undefined;
2098
+ }
2063
2099
  const indices = new Set(this._getSourceRootObjects(collection, sourceIndex)
2064
2100
  .map((target) => this._getExportedObjectIndex(collection, target, context))
2065
2101
  .filter((index) => index !== undefined));
@@ -2140,11 +2176,7 @@ export class KHRInteractivityExportPlan {
2140
2176
  if (!match || !_KnownIndexedRootCollections.has(match[1])) {
2141
2177
  return this._remapReference(reference, context, diagnostics, graphIndex, nodeIndex);
2142
2178
  }
2143
- const targets = this._getSourceRootObjects(targetCollection, parseInt(match[2], 10));
2144
- const targetIndices = new Set(targets
2145
- .map((target) => this._getExportedObjectIndex(targetCollection, target, context))
2146
- .filter((index) => index !== undefined));
2147
- const targetIndex = targetIndices.size === 1 ? targetIndices.values().next().value : undefined;
2179
+ const targetIndex = this._getRemappedRootIndex(targetCollection, parseInt(match[2], 10), context);
2148
2180
  if (targetIndex === undefined) {
2149
2181
  _PushDiagnostic(diagnostics, {
2150
2182
  code: "REFERENCE_UNRESOLVED",
@@ -2161,8 +2193,22 @@ export class KHRInteractivityExportPlan {
2161
2193
  if (!reference || reference.startsWith("/extensions/KHR_interactivity/events/") || reference.startsWith("/extensions/KHR_interactivity/delays/")) {
2162
2194
  return reference;
2163
2195
  }
2164
- const extensionCollection = /^\/extensions\/([^/]+)\/[^/]+\/(0|[1-9]\d*)(?:\/|$)/.exec(reference);
2196
+ const extensionCollection = /^\/extensions\/([^/]+)\/([^/]+)\/(0|[1-9]\d*)(?:\/|$)/.exec(reference);
2165
2197
  if (extensionCollection && extensionCollection[1] !== "KHR_interactivity") {
2198
+ if (this._options.preserveSourceDocument) {
2199
+ const collection = this._options.sourceGLTF?.extensions?.[extensionCollection[1]]?.[extensionCollection[2]];
2200
+ if (!Array.isArray(collection) || collection[Number(extensionCollection[3])] === undefined) {
2201
+ _PushDiagnostic(diagnostics, {
2202
+ code: "REFERENCE_UNRESOLVED",
2203
+ graphIndex,
2204
+ nodeIndex,
2205
+ path: `/graphs/${graphIndex}${nodeIndex === undefined ? "" : `/nodes/${nodeIndex}`}`,
2206
+ message: `Reference "${reference}" does not identify an entry in the retained source document.`,
2207
+ });
2208
+ return reference;
2209
+ }
2210
+ return reference;
2211
+ }
2166
2212
  _PushDiagnostic(diagnostics, {
2167
2213
  code: "REFERENCE_UNRESOLVED",
2168
2214
  graphIndex,
@@ -2196,7 +2242,7 @@ export class KHRInteractivityExportPlan {
2196
2242
  }
2197
2243
  _validateExtensionReferencePreservation(reference, resolvedCollection, diagnostics, graphIndex, nodeIndex, path = `/graphs/${graphIndex}${nodeIndex === undefined ? "" : `/nodes/${nodeIndex}`}`) {
2198
2244
  const nestedExtensions = _GetPointerExtensionNames(reference).filter((extension) => extension !== "KHR_interactivity");
2199
- if (nestedExtensions.length === 0 || _CanPreserveNestedExtensionPointer(reference, resolvedCollection)) {
2245
+ if (this._options.preserveSourceDocument || nestedExtensions.length === 0 || _CanPreserveNestedExtensionPointer(reference, resolvedCollection)) {
2200
2246
  return true;
2201
2247
  }
2202
2248
  _PushDiagnostic(diagnostics, {
@@ -2294,6 +2340,9 @@ export class KHRInteractivityExportPlan {
2294
2340
  return Array.from(extensions).sort();
2295
2341
  }
2296
2342
  _writeCompanionNodeExtensions(context, diagnostics) {
2343
+ if (this._options.preserveSourceDocument) {
2344
+ return;
2345
+ }
2297
2346
  for (let sourceIndex = 0; sourceIndex < (this._options.sourceGLTF?.nodes?.length ?? 0); sourceIndex++) {
2298
2347
  const sourceNode = this._options.sourceGLTF.nodes[sourceIndex];
2299
2348
  for (const extensionName of _CompanionNodeExtensions) {