@babylonjs/loaders 9.26.1 → 9.27.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 (69) hide show
  1. package/SPLAT/gaussianSplattingStream.d.ts +79 -25
  2. package/SPLAT/gaussianSplattingStream.js +320 -95
  3. package/SPLAT/gaussianSplattingStream.js.map +1 -1
  4. package/glTF/2.0/Extensions/EXT_texture_avif.pure.js +1 -1
  5. package/glTF/2.0/Extensions/EXT_texture_avif.pure.js.map +1 -1
  6. package/glTF/2.0/Extensions/EXT_texture_webp.pure.js +1 -1
  7. package/glTF/2.0/Extensions/EXT_texture_webp.pure.js.map +1 -1
  8. package/glTF/2.0/Extensions/KHR_interactivity/declarationMapper.d.ts +143 -3
  9. package/glTF/2.0/Extensions/KHR_interactivity/declarationMapper.js +622 -99
  10. package/glTF/2.0/Extensions/KHR_interactivity/declarationMapper.js.map +1 -1
  11. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphEventReferenceBlock.d.ts +47 -0
  12. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphEventReferenceBlock.js +68 -0
  13. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphEventReferenceBlock.js.map +1 -0
  14. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphGLTFDataProvider.d.ts +2 -2
  15. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphGLTFDataProvider.js +1 -1
  16. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphGLTFDataProvider.js.map +1 -1
  17. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphObjectReferenceBlock.d.ts +24 -0
  18. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphObjectReferenceBlock.js +38 -0
  19. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphObjectReferenceBlock.js.map +1 -0
  20. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphUnsupportedInteractivityBlock.d.ts +41 -0
  21. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphUnsupportedInteractivityBlock.js +63 -0
  22. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphUnsupportedInteractivityBlock.js.map +1 -0
  23. package/glTF/2.0/Extensions/KHR_interactivity/index.d.ts +6 -0
  24. package/glTF/2.0/Extensions/KHR_interactivity/index.js +6 -0
  25. package/glTF/2.0/Extensions/KHR_interactivity/index.js.map +1 -1
  26. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphExporter.d.ts +243 -0
  27. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphExporter.js +2335 -0
  28. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphExporter.js.map +1 -0
  29. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphModel.d.ts +193 -0
  30. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphModel.js +919 -0
  31. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphModel.js.map +1 -0
  32. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphParser.d.ts +30 -10
  33. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphParser.js +485 -54
  34. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphParser.js.map +1 -1
  35. package/glTF/2.0/Extensions/KHR_interactivity/interactivityHostResolver.d.ts +2 -1
  36. package/glTF/2.0/Extensions/KHR_interactivity/interactivityHostResolver.js +7 -2
  37. package/glTF/2.0/Extensions/KHR_interactivity/interactivityHostResolver.js.map +1 -1
  38. package/glTF/2.0/Extensions/KHR_interactivity/interactivityNodeState.d.ts +24 -0
  39. package/glTF/2.0/Extensions/KHR_interactivity/interactivityNodeState.js +89 -0
  40. package/glTF/2.0/Extensions/KHR_interactivity/interactivityNodeState.js.map +1 -0
  41. package/glTF/2.0/Extensions/KHR_interactivity/pure.d.ts +6 -0
  42. package/glTF/2.0/Extensions/KHR_interactivity/pure.js +6 -0
  43. package/glTF/2.0/Extensions/KHR_interactivity/pure.js.map +1 -1
  44. package/glTF/2.0/Extensions/KHR_interactivity.pure.d.ts +59 -1
  45. package/glTF/2.0/Extensions/KHR_interactivity.pure.js +129 -77
  46. package/glTF/2.0/Extensions/KHR_interactivity.pure.js.map +1 -1
  47. package/glTF/2.0/Extensions/KHR_interactivity.types.d.ts +18 -1
  48. package/glTF/2.0/Extensions/KHR_interactivity.types.js.map +1 -1
  49. package/glTF/2.0/Extensions/KHR_node_hoverability.pure.d.ts +2 -0
  50. package/glTF/2.0/Extensions/KHR_node_hoverability.pure.js +114 -56
  51. package/glTF/2.0/Extensions/KHR_node_hoverability.pure.js.map +1 -1
  52. package/glTF/2.0/Extensions/KHR_node_selectability.pure.d.ts +2 -0
  53. package/glTF/2.0/Extensions/KHR_node_selectability.pure.js +80 -35
  54. package/glTF/2.0/Extensions/KHR_node_selectability.pure.js.map +1 -1
  55. package/glTF/2.0/Extensions/KHR_texture_basisu.pure.js +1 -1
  56. package/glTF/2.0/Extensions/KHR_texture_basisu.pure.js.map +1 -1
  57. package/glTF/2.0/Extensions/interactivityRefPathToObjectConverter.js +3 -2
  58. package/glTF/2.0/Extensions/interactivityRefPathToObjectConverter.js.map +1 -1
  59. package/glTF/2.0/Extensions/objectModelMapping.d.ts +4 -2
  60. package/glTF/2.0/Extensions/objectModelMapping.js +33 -4
  61. package/glTF/2.0/Extensions/objectModelMapping.js.map +1 -1
  62. package/glTF/2.0/glTFLoader.pure.d.ts +3 -2
  63. package/glTF/2.0/glTFLoader.pure.js +50 -8
  64. package/glTF/2.0/glTFLoader.pure.js.map +1 -1
  65. package/glTF/2.0/glTFLoaderExtension.d.ts +2 -1
  66. package/glTF/2.0/glTFLoaderExtension.js.map +1 -1
  67. package/glTF/2.0/glTFLoaderInterfaces.d.ts +9 -0
  68. package/glTF/2.0/glTFLoaderInterfaces.js.map +1 -1
  69. package/package.json +3 -3
@@ -1,22 +1,81 @@
1
- import { getMappingForDeclaration, getMappingForFullOperationName } from "./declarationMapper.js";
1
+ import { getMappingForDeclaration, getNoOpMappingForDeclaration, HasDefaultInteractivityFlowInput, NormalizeInteractivityEventDataConfiguration, ParseDebugLogTemplate, } from "./declarationMapper.js";
2
2
  import { Logger } from "@babylonjs/core/Misc/logger.js";
3
3
  import { RandomGUID } from "@babylonjs/core/Misc/guid.js";
4
+ import { _CreateKHRInteractivityRuntimeValueSnapshot, CloneKHRInteractivityGraph, CreateEffectiveKHRInteractivityGraph, gltfTypeToBabylonType, KHR_INTERACTIVITY_SPECIFICATION_COMMIT, } from "./interactivityGraphModel.js";
5
+ import { DelayReferencePrefix, EventReferencePrefix } from "./interactivityReferences.js";
6
+ export { gltfTypeToBabylonType } from "./interactivityGraphModel.js";
7
+ function _GetOwnMapping(mapping, key) {
8
+ return mapping && Object.prototype.hasOwnProperty.call(mapping, key) ? mapping[key] : undefined;
9
+ }
10
+ function _CloneMetadataValue(value) {
11
+ if (Array.isArray(value)) {
12
+ return value.map((entry) => _CloneMetadataValue(entry));
13
+ }
14
+ if (value !== null && typeof value === "object") {
15
+ const clone = {};
16
+ for (const [key, entry] of Object.entries(value)) {
17
+ Object.defineProperty(clone, key, {
18
+ configurable: true,
19
+ enumerable: true,
20
+ value: _CloneMetadataValue(entry),
21
+ writable: true,
22
+ });
23
+ }
24
+ return clone;
25
+ }
26
+ return value;
27
+ }
28
+ function _IsSerializableConfigurationValue(value) {
29
+ return (value === null ||
30
+ ["string", "number", "boolean"].includes(typeof value) ||
31
+ (Array.isArray(value) && value.every(_IsSerializableConfigurationValue)) ||
32
+ (typeof value === "object" && Object.values(value).every(_IsSerializableConfigurationValue)));
33
+ }
34
+ /**
35
+ * Captures import-time defaults for unconnected runtime inputs that have no authored KHR socket.
36
+ * @param flowGraph the fully parsed FlowGraph
37
+ * @internal
38
+ */
4
39
  // eslint-disable-next-line @typescript-eslint/naming-convention
5
- export const gltfTypeToBabylonType = {
6
- float: { length: 1, flowGraphType: "number" /* FlowGraphTypes.Number */, elementType: "number" },
7
- bool: { length: 1, flowGraphType: "boolean" /* FlowGraphTypes.Boolean */, elementType: "boolean" },
8
- float2: { length: 2, flowGraphType: "Vector2" /* FlowGraphTypes.Vector2 */, elementType: "number" },
9
- float3: { length: 3, flowGraphType: "Vector3" /* FlowGraphTypes.Vector3 */, elementType: "number" },
10
- float4: { length: 4, flowGraphType: "Vector4" /* FlowGraphTypes.Vector4 */, elementType: "number" },
11
- float4x4: { length: 16, flowGraphType: "Matrix" /* FlowGraphTypes.Matrix */, elementType: "number" },
12
- float2x2: { length: 4, flowGraphType: "Matrix2D" /* FlowGraphTypes.Matrix2D */, elementType: "number" },
13
- float3x3: { length: 9, flowGraphType: "Matrix3D" /* FlowGraphTypes.Matrix3D */, elementType: "number" },
14
- int: { length: 1, flowGraphType: "FlowGraphInteger" /* FlowGraphTypes.Integer */, elementType: "number" },
15
- // KHR_interactivity opaque reference type. Represented as a JSON Pointer string
16
- // (e.g. "/nodes/17/") that addresses a glTF object. The empty string is the
17
- // canonical "null reference" sentinel used by the parser.
18
- ref: { length: 1, flowGraphType: "string" /* FlowGraphTypes.String */, elementType: "string" },
19
- };
40
+ export function _CaptureKHRInteractivityRuntimeInputDefaults(flowGraph) {
41
+ for (const block of flowGraph.getAllBlocks()) {
42
+ const provenance = block.metadata?.khrInteractivity;
43
+ if (!provenance) {
44
+ continue;
45
+ }
46
+ if (Object.prototype.hasOwnProperty.call(provenance.generatedConfiguration ?? {}, "eventData")) {
47
+ provenance.generatedConfigurationRuntime || (provenance.generatedConfigurationRuntime = {});
48
+ if (!Object.prototype.hasOwnProperty.call(provenance.generatedConfigurationRuntime, "eventData")) {
49
+ Object.defineProperty(provenance.generatedConfigurationRuntime, "eventData", {
50
+ configurable: true,
51
+ enumerable: true,
52
+ value: _CreateKHRInteractivityRuntimeValueSnapshot(NormalizeInteractivityEventDataConfiguration(block.config?.eventData)),
53
+ writable: true,
54
+ });
55
+ }
56
+ }
57
+ for (const input of block.dataInputs) {
58
+ if (input.isConnected()) {
59
+ continue;
60
+ }
61
+ const socketProvenance = input.metadata?.khrInteractivity;
62
+ if (socketProvenance) {
63
+ socketProvenance.runtimeValueSnapshot ?? (socketProvenance.runtimeValueSnapshot = _CreateKHRInteractivityRuntimeValueSnapshot(input._defaultValue));
64
+ continue;
65
+ }
66
+ if (provenance.generatedInputDefaults && Object.prototype.hasOwnProperty.call(provenance.generatedInputDefaults, input.name)) {
67
+ continue;
68
+ }
69
+ provenance.generatedInputDefaults || (provenance.generatedInputDefaults = {});
70
+ Object.defineProperty(provenance.generatedInputDefaults, input.name, {
71
+ configurable: true,
72
+ enumerable: true,
73
+ value: _CreateKHRInteractivityRuntimeValueSnapshot(input._defaultValue),
74
+ writable: true,
75
+ });
76
+ }
77
+ }
78
+ }
20
79
  /**
21
80
  * Parses a KHR_interactivity graph definition (the raw glTF JSON object) into
22
81
  * the serialized FlowGraph form consumed by {@link ParseFlowGraphAsync}.
@@ -26,10 +85,12 @@ export const gltfTypeToBabylonType = {
26
85
  * {@link serializeToFlowGraph}.
27
86
  */
28
87
  export class InteractivityGraphToFlowGraphParser {
29
- constructor(_interactivityGraph, _gltf, _animationTargetFps = 60) {
30
- this._interactivityGraph = _interactivityGraph;
88
+ constructor(interactivityGraph, _gltf, _animationTargetFps = 60, _graphIndex = 0, _supportedExtensions, _declarationModels, canonicalGraph) {
31
89
  this._gltf = _gltf;
32
90
  this._animationTargetFps = _animationTargetFps;
91
+ this._graphIndex = _graphIndex;
92
+ this._supportedExtensions = _supportedExtensions;
93
+ this._declarationModels = _declarationModels;
33
94
  /**
34
95
  * Note - the graph should be rejected if the same type is defined twice.
35
96
  * We currently don't validate that.
@@ -47,6 +108,10 @@ export class InteractivityGraphToFlowGraphParser {
47
108
  * the last block) is not disturbed, then concatenated into the serialized graph.
48
109
  */
49
110
  this._insertedBlocks = [];
111
+ this._canonicalGraph = CloneKHRInteractivityGraph(canonicalGraph ?? interactivityGraph);
112
+ this._interactivityGraph = this._declarationModels
113
+ ? CreateEffectiveKHRInteractivityGraph(interactivityGraph, this._declarationModels, this._gltf.nodes?.length ?? 0)
114
+ : CloneKHRInteractivityGraph(interactivityGraph);
50
115
  // start with types
51
116
  this._parseTypes();
52
117
  // continue with declarations
@@ -55,6 +120,60 @@ export class InteractivityGraphToFlowGraphParser {
55
120
  this._parseEvents();
56
121
  this._parseNodes();
57
122
  }
123
+ get _strictValidation() {
124
+ return !!this._declarationModels;
125
+ }
126
+ _getAllowedDynamicValueSockets(operation, node, direction) {
127
+ if ((operation === "debug/log" || operation === "flow/log:BABYLON") && direction === "input") {
128
+ const message = node.configuration?.[operation === "debug/log" ? "message" : "messageTemplate"]?.value?.[0];
129
+ return new Set(typeof message === "string" ? ParseDebugLogTemplate(message).sockets : []);
130
+ }
131
+ if ((operation === "event/send" && direction === "input") || (operation === "event/receive" && direction === "output")) {
132
+ const eventIndex = node.configuration?.event?.value?.[0];
133
+ if (typeof eventIndex === "number") {
134
+ return new Set(Object.keys(this._interactivityGraph.events?.[eventIndex]?.values ?? {}));
135
+ }
136
+ }
137
+ if (operation === "variable/set" && direction === "input") {
138
+ return new Set((node.configuration?.variables?.value ?? []).map(String));
139
+ }
140
+ if (operation === "math/switch" && direction === "input") {
141
+ return new Set((node.configuration?.cases?.value ?? []).map(String));
142
+ }
143
+ if ((operation === "pointer/get" || operation === "pointer/set" || operation === "pointer/interpolate") && direction === "input") {
144
+ const pointer = node.configuration?.pointer?.value?.[0];
145
+ if (typeof pointer === "string") {
146
+ return new Set(pointer
147
+ .split("/")
148
+ .filter((segment) => (segment.startsWith("[") && !segment.startsWith("[[")) || (segment.startsWith("{") && !segment.startsWith("{{")))
149
+ .map((segment) => segment
150
+ .substring(1, segment.length - 1)
151
+ .replace(/~1/g, "/")
152
+ .replace(/~0/g, "~")));
153
+ }
154
+ }
155
+ return undefined;
156
+ }
157
+ _getAllowedDynamicFlowSockets(node, mapping, direction) {
158
+ for (const [key, property] of Object.entries(mapping.configuration ?? {})) {
159
+ const generatesSockets = direction === "input" ? property.generatesInputFlowSockets : property.generatesOutputFlowSockets;
160
+ if (!generatesSockets) {
161
+ continue;
162
+ }
163
+ const configuredValues = node.configuration?.[key]?.value;
164
+ const validValues = configuredValues &&
165
+ configuredValues.length > 0 &&
166
+ (property.configurationType !== "int" || configuredValues.length === 1) &&
167
+ configuredValues.every((value) => typeof value === "number" && Number.isInteger(value) && value >= (property.minimum ?? -2147483648) && value <= (property.maximum ?? 2147483647));
168
+ const values = validValues ? configuredValues : property.defaultValue;
169
+ if (direction === "input") {
170
+ const count = Array.isArray(values) && typeof values[0] === "number" ? values[0] : 0;
171
+ return new Set(Array.from({ length: count }, (_, index) => String(index)));
172
+ }
173
+ return new Set(Array.isArray(values) ? values.map(String) : []);
174
+ }
175
+ return undefined;
176
+ }
58
177
  get arrays() {
59
178
  return {
60
179
  types: this._types,
@@ -76,10 +195,15 @@ export class InteractivityGraphToFlowGraphParser {
76
195
  if (!this._interactivityGraph.declarations) {
77
196
  return;
78
197
  }
79
- for (const declaration of this._interactivityGraph.declarations) {
80
- // make sure we have the mapping for this operation
81
- const mapping = getMappingForDeclaration(declaration);
82
- // mapping is defined, because we generate an empty mapping if it's not found
198
+ for (let index = 0; index < this._interactivityGraph.declarations.length; index++) {
199
+ const declaration = this._interactivityGraph.declarations[index];
200
+ const declarationModel = this._declarationModels?.[index];
201
+ const extensionEnabled = !declaration.extension || !this._supportedExtensions || this._supportedExtensions.has(declaration.extension);
202
+ const supportedMapping = declarationModel?.support === "unsupported-extension" ? undefined : extensionEnabled ? getMappingForDeclaration(declaration, false) : undefined;
203
+ if (!supportedMapping && !declaration.extension) {
204
+ throw new Error(`Unknown core KHR_interactivity operation "${declaration.op}".`);
205
+ }
206
+ const mapping = supportedMapping ?? (declaration.extension ? getNoOpMappingForDeclaration(declaration) : undefined);
83
207
  if (!mapping) {
84
208
  Logger.Error(["No mapping found for declaration", declaration]);
85
209
  throw new Error("Error parsing declarations");
@@ -87,6 +211,13 @@ export class InteractivityGraphToFlowGraphParser {
87
211
  this._mappings.push({
88
212
  flowGraphMapping: mapping,
89
213
  fullOperationName: declaration.extension ? declaration.op + ":" + declaration.extension : declaration.op,
214
+ declaration: declarationModel ??
215
+ {
216
+ index,
217
+ operation: declaration.extension ? `${declaration.op}:${declaration.extension}` : declaration.op,
218
+ support: supportedMapping ? (declaration.extension ? "extension" : "core") : "unsupported-extension",
219
+ source: declaration,
220
+ },
90
221
  });
91
222
  }
92
223
  }
@@ -107,12 +238,12 @@ export class InteractivityGraphToFlowGraphParser {
107
238
  throw new Error("Error parsing variables");
108
239
  }
109
240
  if (variable.value) {
110
- if (variable.value.length !== type.length) {
241
+ if (this._interactivityGraph.types?.[variable.type]?.signature !== "custom" && variable.value.length !== type.length) {
111
242
  Logger.Error(["Invalid value length for variable", variable, type]);
112
243
  throw new Error("Error parsing variables");
113
244
  }
114
245
  }
115
- const value = variable.value || [];
246
+ const value = variable.value ? variable.value.slice() : [];
116
247
  if (!value.length) {
117
248
  switch (type.flowGraphType) {
118
249
  case "boolean" /* FlowGraphTypes.Boolean */:
@@ -137,13 +268,17 @@ export class InteractivityGraphToFlowGraphParser {
137
268
  case "Vector4" /* FlowGraphTypes.Vector4 */:
138
269
  case "Matrix2D" /* FlowGraphTypes.Matrix2D */:
139
270
  case "Quaternion" /* FlowGraphTypes.Quaternion */:
140
- value.fill(NaN, 0, 4);
271
+ value.push(NaN, NaN, NaN, NaN);
141
272
  break;
142
273
  case "Matrix" /* FlowGraphTypes.Matrix */:
143
- value.fill(NaN, 0, 16);
274
+ for (let i = 0; i < 16; i++) {
275
+ value.push(NaN);
276
+ }
144
277
  break;
145
278
  case "Matrix3D" /* FlowGraphTypes.Matrix3D */:
146
- value.fill(NaN, 0, 9);
279
+ for (let i = 0; i < 9; i++) {
280
+ value.push(NaN);
281
+ }
147
282
  break;
148
283
  default:
149
284
  break;
@@ -153,15 +288,52 @@ export class InteractivityGraphToFlowGraphParser {
153
288
  if (type.elementType === "number" && typeof value[0] === "string") {
154
289
  value[0] = parseFloat(value[0]);
155
290
  }
291
+ else if (type.flowGraphType === "string" /* FlowGraphTypes.String */ && typeof value[0] === "string") {
292
+ value[0] = this._normalizeStaticReference(value[0]);
293
+ }
156
294
  return { type: type.flowGraphType, value: dataTransform ? dataTransform(value, this) : value };
157
295
  }
296
+ _normalizeStaticReference(reference) {
297
+ if (!reference || reference.startsWith(EventReferencePrefix) || reference.startsWith(DelayReferencePrefix)) {
298
+ return "";
299
+ }
300
+ if (!reference.startsWith("/")) {
301
+ return "";
302
+ }
303
+ let current = this._gltf;
304
+ for (const rawSegment of reference.substring(1).split("/")) {
305
+ const segment = rawSegment.replace(/~1/g, "/").replace(/~0/g, "~");
306
+ if (Array.isArray(current)) {
307
+ if (!/^(0|[1-9]\d*)$/.test(segment)) {
308
+ return "";
309
+ }
310
+ current = current[parseInt(segment, 10)];
311
+ }
312
+ else if (current !== null && typeof current === "object" && Object.prototype.hasOwnProperty.call(current, segment)) {
313
+ current = current[segment];
314
+ }
315
+ else {
316
+ return "";
317
+ }
318
+ }
319
+ return current !== null && typeof current === "object" ? reference : "";
320
+ }
158
321
  _parseEvents() {
159
322
  if (!this._interactivityGraph.events) {
160
323
  return;
161
324
  }
325
+ const eventIds = new Set(this._interactivityGraph.events.flatMap((event) => (event.id === undefined ? [] : [event.id])));
326
+ const internalPrefix = `__babylon_khr_internal_${this._graphIndex}_`;
162
327
  for (const event of this._interactivityGraph.events) {
328
+ let generatedEventId;
329
+ if (event.id === undefined) {
330
+ do {
331
+ generatedEventId = internalPrefix + this._internalEventsCounter++;
332
+ } while (eventIds.has(generatedEventId));
333
+ eventIds.add(generatedEventId);
334
+ }
163
335
  const converted = {
164
- eventId: event.id || "internalEvent_" + this._internalEventsCounter++,
336
+ eventId: event.id ?? generatedEventId,
165
337
  };
166
338
  if (event.values) {
167
339
  converted.eventData = Object.keys(event.values).map((key) => {
@@ -191,7 +363,8 @@ export class InteractivityGraphToFlowGraphParser {
191
363
  if (!this._interactivityGraph.nodes) {
192
364
  return;
193
365
  }
194
- for (const node of this._interactivityGraph.nodes) {
366
+ for (let nodeIndex = 0; nodeIndex < this._interactivityGraph.nodes.length; nodeIndex++) {
367
+ const node = this._interactivityGraph.nodes[nodeIndex];
195
368
  // some validation
196
369
  if (typeof node.declaration !== "number") {
197
370
  Logger.Error(["No declaration found for node", node]);
@@ -209,16 +382,30 @@ export class InteractivityGraphToFlowGraphParser {
209
382
  }
210
383
  }
211
384
  const blocks = [];
385
+ if (mapping.declaration.support === "unsupported-extension") {
386
+ blocks.push(this._createUnsupportedExtensionBlock(node, nodeIndex, mapping));
387
+ }
212
388
  // create block(s) for this node using the mapping
213
- for (const blockType of mapping.flowGraphMapping.blocks) {
214
- const block = this._getEmptyBlock(blockType, mapping.fullOperationName);
389
+ for (let role = 0; role < mapping.flowGraphMapping.blocks.length; role++) {
390
+ const blockType = mapping.flowGraphMapping.blocks[role];
391
+ const block = this._getEmptyBlock(blockType, mapping.fullOperationName, nodeIndex, node.declaration, role);
215
392
  this._parseNodeConfiguration(node, block, mapping.flowGraphMapping, blockType);
216
393
  blocks.push(block);
217
394
  }
218
395
  this._nodes.push({ blocks, fullOperationName: mapping.fullOperationName });
219
396
  }
220
397
  }
221
- _getEmptyBlock(className, type) {
398
+ _createBlockProvenance(nodeIndex, declarationIndex, operation, role) {
399
+ return {
400
+ graphIndex: this._graphIndex,
401
+ nodeIndex,
402
+ declarationIndex,
403
+ operation,
404
+ role,
405
+ sourcePath: `/extensions/KHR_interactivity/graphs/${this._graphIndex}/nodes/${nodeIndex}`,
406
+ };
407
+ }
408
+ _getEmptyBlock(className, type, nodeIndex, declarationIndex, role) {
222
409
  return {
223
410
  uniqueId: RandomGUID(),
224
411
  className,
@@ -228,9 +415,71 @@ export class InteractivityGraphToFlowGraphParser {
228
415
  signalOutputs: [],
229
416
  config: {},
230
417
  type,
231
- metadata: {},
418
+ metadata: nodeIndex === undefined
419
+ ? {}
420
+ : {
421
+ khrInteractivity: this._createBlockProvenance(nodeIndex, declarationIndex, type, role ?? 0),
422
+ },
232
423
  };
233
424
  }
425
+ _setSocketProvenance(connection, nodeIndex, declarationIndex, operation, role, kind, direction, socket) {
426
+ connection.metadata = {
427
+ ...(connection.metadata ?? {}),
428
+ khrInteractivity: {
429
+ ...this._createBlockProvenance(nodeIndex, declarationIndex, operation, role),
430
+ kind,
431
+ direction,
432
+ socket,
433
+ },
434
+ };
435
+ }
436
+ _getBlockRole(blocks, block) {
437
+ return block.metadata?.khrInteractivity?.role ?? blocks.indexOf(block);
438
+ }
439
+ _createUnsupportedExtensionBlock(node, nodeIndex, mapping) {
440
+ const block = this._getEmptyBlock("KHR_interactivity/FlowGraphUnsupportedInteractivityBlock", mapping.fullOperationName, nodeIndex, node.declaration, 0);
441
+ const inputFlowSockets = new Set();
442
+ for (const sourceNode of this._interactivityGraph.nodes ?? []) {
443
+ for (const flow of Object.values(sourceNode.flows ?? {})) {
444
+ if (flow.node === nodeIndex) {
445
+ inputFlowSockets.add(flow.socket ?? "in");
446
+ }
447
+ }
448
+ }
449
+ const toSockets = (sockets) => Object.entries(sockets ?? {}).map(([name, definition]) => ({
450
+ name,
451
+ type: this._types[definition.type]?.flowGraphType,
452
+ signature: this._interactivityGraph.types?.[definition.type]?.signature,
453
+ }));
454
+ block.config = {
455
+ operation: mapping.fullOperationName,
456
+ inputValueSockets: toSockets(mapping.declaration.source.inputValueSockets),
457
+ outputValueSockets: toSockets(mapping.declaration.source.outputValueSockets),
458
+ inputFlowSockets: Array.from(inputFlowSockets).sort(),
459
+ outputFlowSockets: Object.keys(node.flows ?? {}).sort(),
460
+ };
461
+ for (const socket of block.config.inputValueSockets) {
462
+ const connection = this._createNewSocketConnection(socket.name);
463
+ this._setSocketProvenance(connection, nodeIndex, node.declaration, mapping.fullOperationName, 0, "value", "input", socket.name);
464
+ block.dataInputs.push(connection);
465
+ }
466
+ for (const socket of block.config.outputValueSockets) {
467
+ const connection = this._createNewSocketConnection(socket.name, true);
468
+ this._setSocketProvenance(connection, nodeIndex, node.declaration, mapping.fullOperationName, 0, "value", "output", socket.name);
469
+ block.dataOutputs.push(connection);
470
+ }
471
+ for (const socket of block.config.inputFlowSockets) {
472
+ const connection = this._createNewSocketConnection(socket);
473
+ this._setSocketProvenance(connection, nodeIndex, node.declaration, mapping.fullOperationName, 0, "flow", "input", socket);
474
+ block.signalInputs.push(connection);
475
+ }
476
+ for (const socket of block.config.outputFlowSockets) {
477
+ const connection = this._createNewSocketConnection(socket, true);
478
+ this._setSocketProvenance(connection, nodeIndex, node.declaration, mapping.fullOperationName, 0, "flow", "output", socket);
479
+ block.signalOutputs.push(connection);
480
+ }
481
+ return block;
482
+ }
234
483
  _parseNodeConfiguration(node, block, nodeMapping, blockType) {
235
484
  const gltfConfiguration = node.configuration;
236
485
  if (gltfConfiguration) {
@@ -240,6 +489,12 @@ export class InteractivityGraphToFlowGraphParser {
240
489
  throw new Error("Error parsing node configuration");
241
490
  }
242
491
  const propertyMapping = nodeMapping.configuration?.[key];
492
+ if (!propertyMapping && this._strictValidation) {
493
+ continue;
494
+ }
495
+ if (propertyMapping?.validationOnly) {
496
+ continue;
497
+ }
243
498
  const belongsToBlock = propertyMapping && propertyMapping.toBlock ? propertyMapping.toBlock === blockType : nodeMapping.blocks.indexOf(blockType) === 0;
244
499
  if (belongsToBlock) {
245
500
  let value = propertyMapping?.defaultValue;
@@ -297,8 +552,31 @@ export class InteractivityGraphToFlowGraphParser {
297
552
  // connect the flows
298
553
  for (const flowKey of flowsKeys) {
299
554
  const flow = flowsFromGLTF[flowKey];
300
- const flowMapping = outputMapper.flowGraphMapping.outputs?.flows?.[flowKey];
301
- const socketOutName = flowMapping?.name || flowKey;
555
+ let flowMapping = _GetOwnMapping(outputMapper.flowGraphMapping.outputs?.flows, flowKey);
556
+ if (this._strictValidation && flowMapping?.compatibilityOnly) {
557
+ flowMapping = undefined;
558
+ }
559
+ let outputArrayMapping = false;
560
+ if (!flowMapping) {
561
+ for (const key in outputMapper.flowGraphMapping.outputs?.flows) {
562
+ if (key.startsWith("[") && key.endsWith("]")) {
563
+ const wildcardMapping = outputMapper.flowGraphMapping.outputs?.flows?.[key];
564
+ if (!this._strictValidation || !wildcardMapping?.compatibilityOnly) {
565
+ outputArrayMapping = true;
566
+ flowMapping = wildcardMapping;
567
+ break;
568
+ }
569
+ }
570
+ }
571
+ }
572
+ const allowedDynamicOutputs = outputArrayMapping ? this._getAllowedDynamicFlowSockets(gltfNode, outputMapper.flowGraphMapping, "output") : undefined;
573
+ if (this._strictValidation && allowedDynamicOutputs && !allowedDynamicOutputs.has(flowKey)) {
574
+ flowMapping = undefined;
575
+ }
576
+ if (!flowMapping && !this._isUnsupportedExtensionBlock(flowGraphBlocks.blocks[0])) {
577
+ continue;
578
+ }
579
+ const socketOutName = flowMapping ? (outputArrayMapping ? flowMapping.name.replace("$1", flowKey) : flowMapping.name) : flowKey;
302
580
  // get the input node of this block
303
581
  const inputNodeId = flow.node;
304
582
  const nodeIn = this._nodes[inputNodeId];
@@ -314,24 +592,43 @@ export class InteractivityGraphToFlowGraphParser {
314
592
  continue;
315
593
  }
316
594
  // create a serialized socket
317
- const socketOut = this._createNewSocketConnection(socketOutName, true);
318
595
  const block = (flowMapping && flowMapping.toBlock && flowGraphBlocks.blocks.find((b) => b.className === flowMapping.toBlock)) || flowGraphBlocks.blocks[0];
319
- block.signalOutputs.push(socketOut);
596
+ const socketOut = block.signalOutputs.find((socket) => socket.name === socketOutName) ?? this._createNewSocketConnection(socketOutName, true);
597
+ if (!block.signalOutputs.includes(socketOut)) {
598
+ block.signalOutputs.push(socketOut);
599
+ }
600
+ this._setSocketProvenance(socketOut, i, gltfNode.declaration, flowGraphBlocks.fullOperationName, this._getBlockRole(flowGraphBlocks.blocks, block), "flow", "output", flowKey);
320
601
  // get the mapper for the input node - in case it mapped to multiple blocks
321
- const inputMapper = getMappingForFullOperationName(nodeIn.fullOperationName);
602
+ const inputMapper = this._mappings[this._interactivityGraph.nodes[inputNodeId].declaration]?.flowGraphMapping;
322
603
  if (!inputMapper) {
323
604
  Logger.Error(["No mapping found for input node", nodeIn]);
324
605
  throw new Error("Error parsing node connections");
325
606
  }
326
- let flowInMapping = inputMapper.inputs?.flows?.[flow.socket || "in"];
607
+ let flowInMapping = _GetOwnMapping(inputMapper.inputs?.flows, flow.socket || "in");
608
+ if (this._strictValidation && flowInMapping?.compatibilityOnly) {
609
+ flowInMapping = undefined;
610
+ }
327
611
  let arrayMapping = false;
328
612
  if (!flowInMapping) {
329
613
  for (const key in inputMapper.inputs?.flows) {
330
614
  if (key.startsWith("[") && key.endsWith("]")) {
331
- arrayMapping = true;
332
- flowInMapping = inputMapper.inputs?.flows?.[key];
615
+ const wildcardMapping = inputMapper.inputs?.flows?.[key];
616
+ if (!this._strictValidation || !wildcardMapping?.compatibilityOnly) {
617
+ arrayMapping = true;
618
+ flowInMapping = wildcardMapping;
619
+ }
333
620
  }
334
621
  }
622
+ const allowedDynamicInputs = arrayMapping ? this._getAllowedDynamicFlowSockets(this._interactivityGraph.nodes[inputNodeId], inputMapper, "input") : undefined;
623
+ if (this._strictValidation && allowedDynamicInputs && !allowedDynamicInputs.has(flow.socket ?? "in")) {
624
+ flowInMapping = undefined;
625
+ }
626
+ if (!flowInMapping && (flow.socket ?? "in") === "in" && HasDefaultInteractivityFlowInput(nodeIn.fullOperationName)) {
627
+ flowInMapping = { name: "in" };
628
+ }
629
+ if (!flowInMapping && !this._isUnsupportedExtensionBlock(nodeIn.blocks[0])) {
630
+ continue;
631
+ }
335
632
  }
336
633
  const nodeInSocketName = flowInMapping ? (arrayMapping ? flowInMapping.name.replace("$1", flow.socket || "") : flowInMapping.name) : flow.socket || "in";
337
634
  const inputBlock = (flowInMapping && flowInMapping.toBlock && nodeIn.blocks.find((b) => b.className === flowInMapping.toBlock)) || nodeIn.blocks[0];
@@ -342,6 +639,7 @@ export class InteractivityGraphToFlowGraphParser {
342
639
  socketIn = this._createNewSocketConnection(nodeInSocketName);
343
640
  inputBlock.signalInputs.push(socketIn);
344
641
  }
642
+ this._setSocketProvenance(socketIn, inputNodeId, this._interactivityGraph.nodes[inputNodeId].declaration, nodeIn.fullOperationName, this._getBlockRole(nodeIn.blocks, inputBlock), "flow", "input", flow.socket ?? "in");
345
643
  // connect the sockets
346
644
  socketIn.connectedPointIds.push(socketOut.uniqueId);
347
645
  socketOut.connectedPointIds.push(socketIn.uniqueId);
@@ -351,21 +649,36 @@ export class InteractivityGraphToFlowGraphParser {
351
649
  const valuesKeys = Object.keys(valuesFromGLTF);
352
650
  for (const valueKey of valuesKeys) {
353
651
  const value = valuesFromGLTF[valueKey];
354
- let valueMapping = outputMapper.flowGraphMapping.inputs?.values?.[valueKey];
652
+ let valueMapping = _GetOwnMapping(outputMapper.flowGraphMapping.inputs?.values, valueKey);
653
+ if (this._strictValidation && valueMapping?.compatibilityOnly) {
654
+ valueMapping = undefined;
655
+ }
355
656
  let arrayMapping = false;
356
657
  if (!valueMapping) {
357
658
  for (const key in outputMapper.flowGraphMapping.inputs?.values) {
358
659
  if (key.startsWith("[") && key.endsWith("]")) {
359
- arrayMapping = true;
360
- valueMapping = outputMapper.flowGraphMapping.inputs?.values?.[key];
660
+ const wildcardMapping = outputMapper.flowGraphMapping.inputs?.values?.[key];
661
+ if (!this._strictValidation || !wildcardMapping?.compatibilityOnly) {
662
+ arrayMapping = true;
663
+ valueMapping = wildcardMapping;
664
+ }
361
665
  }
362
666
  }
363
667
  }
668
+ const allowedDynamicInputs = arrayMapping ? this._getAllowedDynamicValueSockets(outputMapper.fullOperationName, gltfNode, "input") : undefined;
669
+ if (!valueMapping || (allowedDynamicInputs && !allowedDynamicInputs.has(valueKey))) {
670
+ continue;
671
+ }
364
672
  const socketInName = valueMapping ? (arrayMapping ? valueMapping.name.replace("$1", valueKey) : valueMapping.name) : valueKey;
365
673
  // create a serialized socket
366
- const socketIn = this._createNewSocketConnection(socketInName);
367
674
  const block = (valueMapping && valueMapping.toBlock && flowGraphBlocks.blocks.find((b) => b.className === valueMapping.toBlock)) || flowGraphBlocks.blocks[0];
368
- block.dataInputs.push(socketIn);
675
+ const socketIn = block.dataInputs.find((socket) => socket.name === socketInName) ?? this._createNewSocketConnection(socketInName);
676
+ if (!block.dataInputs.includes(socketIn)) {
677
+ block.dataInputs.push(socketIn);
678
+ }
679
+ this._setSocketProvenance(socketIn, i, gltfNode.declaration, flowGraphBlocks.fullOperationName, this._getBlockRole(flowGraphBlocks.blocks, block), "value", "input", valueKey);
680
+ const socketProvenance = socketIn.metadata.khrInteractivity;
681
+ socketProvenance.sourceValue = _CloneMetadataValue(this._canonicalGraph.nodes?.[i]?.values?.[valueKey] ?? value);
369
682
  // Captured before the connected branch below shadows `valueMapping`. When set and the
370
683
  // value is supplied by a connection, the seconds→frames `dataTransformer` cannot run
371
684
  // (it is parse-time only), so the raw connected value is scaled by a runtime multiply.
@@ -373,6 +686,8 @@ export class InteractivityGraphToFlowGraphParser {
373
686
  if (value.value !== undefined) {
374
687
  const convertedValue = this._parseVariable(value, valueMapping && valueMapping.dataTransformer);
375
688
  context._connectionValues[socketIn.uniqueId] = convertedValue;
689
+ socketIn.defaultValue = convertedValue;
690
+ socketProvenance.runtimeValue = _CloneMetadataValue(convertedValue.value);
376
691
  }
377
692
  else if (typeof value.node !== "undefined") {
378
693
  const nodeOutId = value.node;
@@ -390,22 +705,34 @@ export class InteractivityGraphToFlowGraphParser {
390
705
  Logger.Warn(`Dropping value connection from no-op node #${nodeOutId} (unsupported operation: ${nodeOut.fullOperationName}) into node #${i} "${valueKey}"; consumer will use type-default value`);
391
706
  continue;
392
707
  }
393
- const outputMapper = getMappingForFullOperationName(nodeOut.fullOperationName);
708
+ const outputMapper = this._mappings[this._interactivityGraph.nodes[nodeOutId].declaration]?.flowGraphMapping;
394
709
  if (!outputMapper) {
395
710
  Logger.Error(["No mapping found for output socket reference", value]);
396
711
  throw new Error("Error parsing node connections");
397
712
  }
398
- let valueMapping = outputMapper.outputs?.values?.[nodeOutSocketName];
713
+ let valueMapping = _GetOwnMapping(outputMapper.outputs?.values, nodeOutSocketName);
714
+ if (this._strictValidation && valueMapping?.compatibilityOnly) {
715
+ valueMapping = undefined;
716
+ }
399
717
  let arrayMapping = false;
400
718
  // check if there is an array mapping defined
401
719
  if (!valueMapping) {
402
720
  // search for a value mapping that has an array mapping
403
721
  for (const key in outputMapper.outputs?.values) {
404
722
  if (key.startsWith("[") && key.endsWith("]")) {
405
- arrayMapping = true;
406
- valueMapping = outputMapper.outputs?.values?.[key];
723
+ const wildcardMapping = outputMapper.outputs?.values?.[key];
724
+ if (!this._strictValidation || !wildcardMapping?.compatibilityOnly) {
725
+ arrayMapping = true;
726
+ valueMapping = wildcardMapping;
727
+ }
407
728
  }
408
729
  }
730
+ const allowedDynamicOutputs = arrayMapping
731
+ ? this._getAllowedDynamicValueSockets(nodeOut.fullOperationName, this._interactivityGraph.nodes[nodeOutId], "output")
732
+ : undefined;
733
+ if (!valueMapping || (allowedDynamicOutputs && !allowedDynamicOutputs.has(nodeOutSocketName))) {
734
+ continue;
735
+ }
409
736
  }
410
737
  const socketOutName = valueMapping ? (arrayMapping ? valueMapping.name.replace("$1", nodeOutSocketName) : valueMapping?.name) : nodeOutSocketName;
411
738
  const outBlock = (valueMapping && valueMapping.toBlock && nodeOut.blocks.find((b) => b.className === valueMapping.toBlock)) || nodeOut.blocks[0];
@@ -415,9 +742,10 @@ export class InteractivityGraphToFlowGraphParser {
415
742
  socketOut = this._createNewSocketConnection(socketOutName, true);
416
743
  outBlock.dataOutputs.push(socketOut);
417
744
  }
745
+ this._setSocketProvenance(socketOut, nodeOutId, this._interactivityGraph.nodes[nodeOutId].declaration, nodeOut.fullOperationName, this._getBlockRole(nodeOut.blocks, outBlock), "value", "output", nodeOutSocketName);
418
746
  // connect the sockets
419
747
  if (convertConnectedTimeToFrames) {
420
- this._connectWithSecondsToFramesConversion(context, socketOut, socketIn);
748
+ this._connectWithSecondsToFramesConversion(context, socketOut, socketIn, i, gltfNode.declaration, flowGraphBlocks.fullOperationName);
421
749
  }
422
750
  else {
423
751
  socketIn.connectedPointIds.push(socketOut.uniqueId);
@@ -446,6 +774,93 @@ export class InteractivityGraphToFlowGraphParser {
446
774
  }
447
775
  flowGraphBlocks.blocks = outputMapper.flowGraphMapping.extraProcessor(gltfNode, declaration, outputMapper.flowGraphMapping, this, flowGraphBlocks.blocks, context, this._gltf);
448
776
  }
777
+ this._ensureMappedSocketProvenance(i, gltfNode, flowGraphBlocks.blocks, outputMapper);
778
+ }
779
+ }
780
+ _ensureMappedSocketProvenance(nodeIndex, node, blocks, mapping) {
781
+ const ensureConnections = (socketMappings, kind, direction) => {
782
+ for (const [sourceName, property] of Object.entries(socketMappings ?? {})) {
783
+ if ((sourceName.startsWith("[") && sourceName.endsWith("]")) || (this._strictValidation && property.compatibilityOnly)) {
784
+ continue;
785
+ }
786
+ const block = (property.toBlock && blocks.find((candidate) => candidate.className === property.toBlock)) || blocks[0];
787
+ if (!block) {
788
+ continue;
789
+ }
790
+ const connections = kind === "value" ? (direction === "input" ? block.dataInputs : block.dataOutputs) : direction === "input" ? block.signalInputs : block.signalOutputs;
791
+ let connection = connections.find((candidate) => candidate.name === property.name);
792
+ if (!connection) {
793
+ connection = this._createNewSocketConnection(property.name, direction === "output");
794
+ connections.push(connection);
795
+ }
796
+ this._setSocketProvenance(connection, nodeIndex, node.declaration, mapping.fullOperationName, this._getBlockRole(blocks, block), kind, direction, sourceName);
797
+ }
798
+ };
799
+ ensureConnections(mapping.flowGraphMapping.outputs?.values, "value", "output");
800
+ ensureConnections(mapping.flowGraphMapping.inputs?.flows, "flow", "input");
801
+ ensureConnections(mapping.flowGraphMapping.outputs?.flows, "flow", "output");
802
+ if (HasDefaultInteractivityFlowInput(mapping.fullOperationName)) {
803
+ const block = blocks[0];
804
+ if (block) {
805
+ let connection = block.signalInputs.find((candidate) => candidate.name === "in");
806
+ if (!connection) {
807
+ connection = this._createNewSocketConnection("in");
808
+ block.signalInputs.push(connection);
809
+ }
810
+ this._setSocketProvenance(connection, nodeIndex, node.declaration, mapping.fullOperationName, this._getBlockRole(blocks, block), "flow", "input", "in");
811
+ }
812
+ }
813
+ for (const [sourceKey, property] of Object.entries(mapping.flowGraphMapping.configuration ?? {})) {
814
+ const block = (property.toBlock && blocks.find((candidate) => candidate.className === property.toBlock)) || blocks[0];
815
+ const provenance = block?.metadata?.khrInteractivity;
816
+ if (!block || !provenance) {
817
+ continue;
818
+ }
819
+ const serializedRuntimeValue = block.config?.[property.name];
820
+ const runtimeValue = serializedRuntimeValue !== null && typeof serializedRuntimeValue === "object" && Object.prototype.hasOwnProperty.call(serializedRuntimeValue, "value")
821
+ ? serializedRuntimeValue.value
822
+ : serializedRuntimeValue;
823
+ provenance.configuration || (provenance.configuration = {});
824
+ const configurationProvenance = {
825
+ sourceValue: _CloneMetadataValue(this._canonicalGraph.nodes?.[nodeIndex]?.configuration?.[sourceKey]?.value),
826
+ runtimeValue: _CloneMetadataValue(runtimeValue),
827
+ };
828
+ Object.defineProperty(provenance.configuration, sourceKey, {
829
+ configurable: true,
830
+ enumerable: true,
831
+ value: configurationProvenance,
832
+ writable: true,
833
+ });
834
+ }
835
+ for (const block of blocks) {
836
+ const provenance = block.metadata?.khrInteractivity;
837
+ if (!provenance) {
838
+ continue;
839
+ }
840
+ const mappedConfigurationNames = new Set(Object.values(mapping.flowGraphMapping.configuration ?? {})
841
+ .filter((property) => (property.toBlock ? block.className === property.toBlock : provenance.role === 0))
842
+ .map((property) => property.name));
843
+ const ignoredConfigurationNames = new Set(["glTF"]);
844
+ for (const [key, value] of Object.entries(block.config ?? {})) {
845
+ if (mappedConfigurationNames.has(key) || ignoredConfigurationNames.has(key)) {
846
+ continue;
847
+ }
848
+ const normalizedValue = key === "eventData"
849
+ ? NormalizeInteractivityEventDataConfiguration(value)
850
+ : value !== null && typeof value === "object" && Object.prototype.hasOwnProperty.call(value, "value")
851
+ ? value.value
852
+ : value;
853
+ if (!_IsSerializableConfigurationValue(normalizedValue)) {
854
+ continue;
855
+ }
856
+ provenance.generatedConfiguration || (provenance.generatedConfiguration = {});
857
+ Object.defineProperty(provenance.generatedConfiguration, key, {
858
+ configurable: true,
859
+ enumerable: true,
860
+ value: _CloneMetadataValue(normalizedValue),
861
+ writable: true,
862
+ });
863
+ }
449
864
  }
450
865
  }
451
866
  _createNewSocketConnection(name, isOutput) {
@@ -456,6 +871,9 @@ export class InteractivityGraphToFlowGraphParser {
456
871
  connectedPointIds: [],
457
872
  };
458
873
  }
874
+ _isUnsupportedExtensionBlock(block) {
875
+ return block?.className === "KHR_interactivity/FlowGraphUnsupportedInteractivityBlock";
876
+ }
459
877
  /**
460
878
  * Wires an upstream data output into a downstream data input through a runtime multiply block that
461
879
  * scales the value by the animation target fps. This converts a KHR animation time (seconds),
@@ -465,9 +883,12 @@ export class InteractivityGraphToFlowGraphParser {
465
883
  * @param context the serialized flow graph context that stores literal socket values
466
884
  * @param upstreamOutput the data output socket providing the time value (in seconds)
467
885
  * @param downstreamInput the data input socket that expects the time in frames
886
+ * @param nodeIndex source node receiving the converted value
887
+ * @param declarationIndex source declaration used by the receiving node
888
+ * @param operation owning KHR operation
468
889
  */
469
- _connectWithSecondsToFramesConversion(context, upstreamOutput, downstreamInput) {
470
- const multiplyBlock = this._getEmptyBlock("FlowGraphMultiplyBlock" /* FlowGraphBlockNames.Multiply */, "FlowGraphMultiplyBlock" /* FlowGraphBlockNames.Multiply */);
890
+ _connectWithSecondsToFramesConversion(context, upstreamOutput, downstreamInput, nodeIndex, declarationIndex, operation) {
891
+ const multiplyBlock = this._getEmptyBlock("FlowGraphMultiplyBlock" /* FlowGraphBlockNames.Multiply */, operation, nodeIndex, declarationIndex, -1);
471
892
  // Scalar (float) multiply; matches how the `math/mul` mapping configures the block.
472
893
  multiplyBlock.config = { type: "number" /* FlowGraphTypes.Number */ };
473
894
  const inputA = this._createNewSocketConnection("a");
@@ -476,7 +897,9 @@ export class InteractivityGraphToFlowGraphParser {
476
897
  multiplyBlock.dataInputs.push(inputA, inputB);
477
898
  multiplyBlock.dataOutputs.push(output);
478
899
  // The second factor is the constant animation target fps.
479
- context._connectionValues[inputB.uniqueId] = { type: "number" /* FlowGraphTypes.Number */, value: [this._animationTargetFps] };
900
+ const frameRateValue = { type: "number" /* FlowGraphTypes.Number */, value: [this._animationTargetFps] };
901
+ context._connectionValues[inputB.uniqueId] = frameRateValue;
902
+ inputB.defaultValue = frameRateValue;
480
903
  // upstream time output -> multiply.a
481
904
  inputA.connectedPointIds.push(upstreamOutput.uniqueId);
482
905
  upstreamOutput.connectedPointIds.push(inputA.uniqueId);
@@ -531,6 +954,14 @@ export class InteractivityGraphToFlowGraphParser {
531
954
  }
532
955
  const allBlocks = this._nodes.reduce((acc, val) => acc.concat(val.blocks), []).concat(this._insertedBlocks);
533
956
  return {
957
+ name: this._interactivityGraph.name ?? `Graph ${this._graphIndex + 1}`,
958
+ metadata: {
959
+ khrInteractivity: {
960
+ graphIndex: this._graphIndex,
961
+ specificationCommit: KHR_INTERACTIVITY_SPECIFICATION_COMMIT,
962
+ source: CloneKHRInteractivityGraph(this._canonicalGraph),
963
+ },
964
+ },
534
965
  rightHanded: true,
535
966
  allBlocks,
536
967
  executionContexts: [context],