@babylonjs/loaders 9.26.0 → 9.26.2

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 (129) hide show
  1. package/FBX/fbxConstraintBehavior.d.ts +188 -0
  2. package/FBX/fbxConstraintBehavior.js +532 -0
  3. package/FBX/fbxConstraintBehavior.js.map +1 -0
  4. package/FBX/fbxFileLoader.pure.d.ts +175 -6
  5. package/FBX/fbxFileLoader.pure.js +1521 -286
  6. package/FBX/fbxFileLoader.pure.js.map +1 -1
  7. package/FBX/index.d.ts +4 -1
  8. package/FBX/index.js +1 -0
  9. package/FBX/index.js.map +1 -1
  10. package/FBX/interpreter/animation.d.ts +73 -33
  11. package/FBX/interpreter/animation.js +620 -223
  12. package/FBX/interpreter/animation.js.map +1 -1
  13. package/FBX/interpreter/animationCurve.d.ts +91 -0
  14. package/FBX/interpreter/animationCurve.js +623 -0
  15. package/FBX/interpreter/animationCurve.js.map +1 -0
  16. package/FBX/interpreter/blendShapes.js +9 -18
  17. package/FBX/interpreter/blendShapes.js.map +1 -1
  18. package/FBX/interpreter/connections.d.ts +1 -1
  19. package/FBX/interpreter/connections.js +173 -34
  20. package/FBX/interpreter/connections.js.map +1 -1
  21. package/FBX/interpreter/constraints.d.ts +68 -0
  22. package/FBX/interpreter/constraints.js +143 -0
  23. package/FBX/interpreter/constraints.js.map +1 -0
  24. package/FBX/interpreter/fbxInterpreter.d.ts +89 -4
  25. package/FBX/interpreter/fbxInterpreter.js +474 -122
  26. package/FBX/interpreter/fbxInterpreter.js.map +1 -1
  27. package/FBX/interpreter/geometry.d.ts +1 -1
  28. package/FBX/interpreter/geometry.js +150 -92
  29. package/FBX/interpreter/geometry.js.map +1 -1
  30. package/FBX/interpreter/legacyDocument.d.ts +11 -0
  31. package/FBX/interpreter/legacyDocument.js +425 -0
  32. package/FBX/interpreter/legacyDocument.js.map +1 -0
  33. package/FBX/interpreter/materialModel.d.ts +71 -0
  34. package/FBX/interpreter/materialModel.js +622 -0
  35. package/FBX/interpreter/materialModel.js.map +1 -0
  36. package/FBX/interpreter/materials.d.ts +13 -1
  37. package/FBX/interpreter/materials.js +88 -5
  38. package/FBX/interpreter/materials.js.map +1 -1
  39. package/FBX/interpreter/nodeTransform.d.ts +32 -0
  40. package/FBX/interpreter/nodeTransform.js +54 -0
  41. package/FBX/interpreter/nodeTransform.js.map +1 -0
  42. package/FBX/interpreter/nurbs.d.ts +99 -0
  43. package/FBX/interpreter/nurbs.js +627 -0
  44. package/FBX/interpreter/nurbs.js.map +1 -0
  45. package/FBX/interpreter/propertyTemplates.d.ts +23 -0
  46. package/FBX/interpreter/propertyTemplates.js +109 -2
  47. package/FBX/interpreter/propertyTemplates.js.map +1 -1
  48. package/FBX/interpreter/rig.js +16 -0
  49. package/FBX/interpreter/rig.js.map +1 -1
  50. package/FBX/interpreter/sceneDiagnostics.js +28 -3
  51. package/FBX/interpreter/sceneDiagnostics.js.map +1 -1
  52. package/FBX/interpreter/skeleton.d.ts +4 -18
  53. package/FBX/interpreter/skeleton.js +16 -94
  54. package/FBX/interpreter/skeleton.js.map +1 -1
  55. package/FBX/parsers/fbxAsciiParser.js +119 -9
  56. package/FBX/parsers/fbxAsciiParser.js.map +1 -1
  57. package/FBX/parsers/fbxBinaryParser.js +74 -22
  58. package/FBX/parsers/fbxBinaryParser.js.map +1 -1
  59. package/FBX/pure.d.ts +1 -0
  60. package/FBX/pure.js +1 -0
  61. package/FBX/pure.js.map +1 -1
  62. package/FBX/types/fbxTypes.d.ts +18 -2
  63. package/FBX/types/fbxTypes.js +40 -12
  64. package/FBX/types/fbxTypes.js.map +1 -1
  65. package/SPLAT/gaussianSplattingStream.d.ts +79 -25
  66. package/SPLAT/gaussianSplattingStream.js +320 -95
  67. package/SPLAT/gaussianSplattingStream.js.map +1 -1
  68. package/USD/usdCommandProtocol.d.ts +20 -4
  69. package/USD/usdCommandProtocol.js +25 -3
  70. package/USD/usdCommandProtocol.js.map +1 -1
  71. package/USD/usdFileLoader.pure.js +2 -0
  72. package/USD/usdFileLoader.pure.js.map +1 -1
  73. package/USD/usdSceneMaterializer.js +430 -92
  74. package/USD/usdSceneMaterializer.js.map +1 -1
  75. package/glTF/2.0/Extensions/KHR_interactivity/declarationMapper.d.ts +110 -3
  76. package/glTF/2.0/Extensions/KHR_interactivity/declarationMapper.js +468 -74
  77. package/glTF/2.0/Extensions/KHR_interactivity/declarationMapper.js.map +1 -1
  78. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphEventReferenceBlock.d.ts +47 -0
  79. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphEventReferenceBlock.js +68 -0
  80. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphEventReferenceBlock.js.map +1 -0
  81. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphGLTFDataProvider.d.ts +2 -2
  82. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphGLTFDataProvider.js +1 -1
  83. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphGLTFDataProvider.js.map +1 -1
  84. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphObjectReferenceBlock.d.ts +24 -0
  85. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphObjectReferenceBlock.js +38 -0
  86. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphObjectReferenceBlock.js.map +1 -0
  87. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphUnsupportedInteractivityBlock.d.ts +41 -0
  88. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphUnsupportedInteractivityBlock.js +63 -0
  89. package/glTF/2.0/Extensions/KHR_interactivity/flowGraphUnsupportedInteractivityBlock.js.map +1 -0
  90. package/glTF/2.0/Extensions/KHR_interactivity/index.d.ts +5 -0
  91. package/glTF/2.0/Extensions/KHR_interactivity/index.js +5 -0
  92. package/glTF/2.0/Extensions/KHR_interactivity/index.js.map +1 -1
  93. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphModel.d.ts +112 -0
  94. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphModel.js +773 -0
  95. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphModel.js.map +1 -0
  96. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphParser.d.ts +18 -10
  97. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphParser.js +299 -54
  98. package/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphParser.js.map +1 -1
  99. package/glTF/2.0/Extensions/KHR_interactivity/interactivityHostResolver.d.ts +2 -1
  100. package/glTF/2.0/Extensions/KHR_interactivity/interactivityHostResolver.js +7 -2
  101. package/glTF/2.0/Extensions/KHR_interactivity/interactivityHostResolver.js.map +1 -1
  102. package/glTF/2.0/Extensions/KHR_interactivity/interactivityNodeState.d.ts +24 -0
  103. package/glTF/2.0/Extensions/KHR_interactivity/interactivityNodeState.js +89 -0
  104. package/glTF/2.0/Extensions/KHR_interactivity/interactivityNodeState.js.map +1 -0
  105. package/glTF/2.0/Extensions/KHR_interactivity/pure.d.ts +5 -0
  106. package/glTF/2.0/Extensions/KHR_interactivity/pure.js +5 -0
  107. package/glTF/2.0/Extensions/KHR_interactivity/pure.js.map +1 -1
  108. package/glTF/2.0/Extensions/KHR_interactivity.pure.d.ts +55 -1
  109. package/glTF/2.0/Extensions/KHR_interactivity.pure.js +124 -76
  110. package/glTF/2.0/Extensions/KHR_interactivity.pure.js.map +1 -1
  111. package/glTF/2.0/Extensions/KHR_interactivity.types.d.ts +18 -1
  112. package/glTF/2.0/Extensions/KHR_interactivity.types.js.map +1 -1
  113. package/glTF/2.0/Extensions/KHR_node_hoverability.pure.d.ts +2 -0
  114. package/glTF/2.0/Extensions/KHR_node_hoverability.pure.js +114 -56
  115. package/glTF/2.0/Extensions/KHR_node_hoverability.pure.js.map +1 -1
  116. package/glTF/2.0/Extensions/KHR_node_selectability.pure.d.ts +2 -0
  117. package/glTF/2.0/Extensions/KHR_node_selectability.pure.js +80 -35
  118. package/glTF/2.0/Extensions/KHR_node_selectability.pure.js.map +1 -1
  119. package/glTF/2.0/Extensions/interactivityRefPathToObjectConverter.js +3 -2
  120. package/glTF/2.0/Extensions/interactivityRefPathToObjectConverter.js.map +1 -1
  121. package/glTF/2.0/Extensions/objectModelMapping.d.ts +4 -2
  122. package/glTF/2.0/Extensions/objectModelMapping.js +33 -4
  123. package/glTF/2.0/Extensions/objectModelMapping.js.map +1 -1
  124. package/glTF/2.0/glTFLoader.pure.d.ts +1 -1
  125. package/glTF/2.0/glTFLoader.pure.js +9 -4
  126. package/glTF/2.0/glTFLoader.pure.js.map +1 -1
  127. package/glTF/2.0/glTFLoaderExtension.d.ts +2 -1
  128. package/glTF/2.0/glTFLoaderExtension.js.map +1 -1
  129. package/package.json +3 -3
@@ -0,0 +1,773 @@
1
+ import { getMappingForDeclaration, getNoOpMappingForDeclaration, ParseDebugLogTemplate, } from "./declarationMapper.js";
2
+ /**
3
+ * Ratified KHR_interactivity specification baseline used by this importer.
4
+ */
5
+ export const KHR_INTERACTIVITY_SPECIFICATION_COMMIT = "f798712c5685bc9223a628140fba707db8889300";
6
+ /**
7
+ * Runtime representation of every ratified built-in KHR_interactivity type.
8
+ */
9
+ // eslint-disable-next-line @typescript-eslint/naming-convention
10
+ export const gltfTypeToBabylonType = {
11
+ float: { length: 1, flowGraphType: "number" /* FlowGraphTypes.Number */, elementType: "number" },
12
+ bool: { length: 1, flowGraphType: "boolean" /* FlowGraphTypes.Boolean */, elementType: "boolean" },
13
+ float2: { length: 2, flowGraphType: "Vector2" /* FlowGraphTypes.Vector2 */, elementType: "number" },
14
+ float3: { length: 3, flowGraphType: "Vector3" /* FlowGraphTypes.Vector3 */, elementType: "number" },
15
+ float4: { length: 4, flowGraphType: "Vector4" /* FlowGraphTypes.Vector4 */, elementType: "number" },
16
+ float4x4: { length: 16, flowGraphType: "Matrix" /* FlowGraphTypes.Matrix */, elementType: "number" },
17
+ float2x2: { length: 4, flowGraphType: "Matrix2D" /* FlowGraphTypes.Matrix2D */, elementType: "number" },
18
+ float3x3: { length: 9, flowGraphType: "Matrix3D" /* FlowGraphTypes.Matrix3D */, elementType: "number" },
19
+ int: { length: 1, flowGraphType: "FlowGraphInteger" /* FlowGraphTypes.Integer */, elementType: "number" },
20
+ ref: { length: 1, flowGraphType: "string" /* FlowGraphTypes.String */, elementType: "string" },
21
+ custom: { length: 0, flowGraphType: "any" /* FlowGraphTypes.Any */, elementType: "any" },
22
+ };
23
+ function _cloneJson(value) {
24
+ if (Array.isArray(value)) {
25
+ return value.map((entry) => _cloneJson(entry));
26
+ }
27
+ if (value !== null && typeof value === "object") {
28
+ const clone = {};
29
+ for (const [key, entry] of Object.entries(value)) {
30
+ Object.defineProperty(clone, key, {
31
+ configurable: true,
32
+ enumerable: true,
33
+ value: _cloneJson(entry),
34
+ writable: true,
35
+ });
36
+ }
37
+ return clone;
38
+ }
39
+ return value;
40
+ }
41
+ /**
42
+ * Creates a detached JSON copy of a KHR_interactivity graph.
43
+ * @param graph source graph
44
+ * @returns a lossless JSON copy
45
+ */
46
+ export function CloneKHRInteractivityGraph(graph) {
47
+ return _cloneJson(graph);
48
+ }
49
+ function _isValidIndex(value, length) {
50
+ return typeof value === "number" && Number.isInteger(value) && value >= 0 && value < length;
51
+ }
52
+ function _addError(diagnostics, path, message) {
53
+ diagnostics.push({ path, message, severity: "error" });
54
+ }
55
+ function _addWarning(diagnostics, path, message) {
56
+ diagnostics.push({ path, message, severity: "warning" });
57
+ }
58
+ function _isValidJsonPointer(value) {
59
+ return value === "" || /^(?:\/(?:[^~]|~[01])*)*$/.test(value);
60
+ }
61
+ function _matchesOutputSocket(mapping, socket, declaration, node, graph, defaultSocket) {
62
+ if (mapping && Object.prototype.hasOwnProperty.call(mapping, socket)) {
63
+ return true;
64
+ }
65
+ if (socket === defaultSocket && mapping && Object.prototype.hasOwnProperty.call(mapping, defaultSocket)) {
66
+ return true;
67
+ }
68
+ if (!Object.keys(mapping ?? {}).some((name) => name.startsWith("[") && name.endsWith("]"))) {
69
+ return false;
70
+ }
71
+ if (declaration.operation === "event/receive") {
72
+ const eventIndex = node.configuration?.event?.value?.[0];
73
+ return typeof eventIndex === "number" && Object.prototype.hasOwnProperty.call(graph.events?.[eventIndex]?.values ?? {}, socket);
74
+ }
75
+ return true;
76
+ }
77
+ function _validateTypeIndex(typeIndex, graph, diagnostics, path) {
78
+ if (!_isValidIndex(typeIndex, graph.types?.length ?? 0)) {
79
+ _addError(diagnostics, path, `Type index ${String(typeIndex)} is out of range.`);
80
+ return false;
81
+ }
82
+ return true;
83
+ }
84
+ function _validateValue(value, graph, diagnostics, path) {
85
+ if (!_validateTypeIndex(value.type, graph, diagnostics, `${path}/type`)) {
86
+ return;
87
+ }
88
+ if (value.value === undefined) {
89
+ return;
90
+ }
91
+ const type = graph.types[value.type];
92
+ const runtimeType = gltfTypeToBabylonType[type.signature];
93
+ if (runtimeType && type.signature !== "custom" && value.value.length !== runtimeType.length) {
94
+ _addError(diagnostics, `${path}/value`, `Expected ${runtimeType.length} value component(s) for type "${type.signature}", received ${value.value.length}.`);
95
+ }
96
+ for (let componentIndex = 0; componentIndex < value.value.length; componentIndex++) {
97
+ const component = value.value[componentIndex];
98
+ let valid = true;
99
+ switch (type.signature) {
100
+ case "bool":
101
+ valid = typeof component === "boolean";
102
+ break;
103
+ case "int":
104
+ valid = typeof component === "number" && Number.isInteger(component) && component >= -2147483648 && component <= 2147483647;
105
+ break;
106
+ case "float":
107
+ case "float2":
108
+ case "float3":
109
+ case "float4":
110
+ case "float2x2":
111
+ case "float3x3":
112
+ case "float4x4":
113
+ valid = typeof component === "number" && Number.isFinite(component);
114
+ break;
115
+ case "ref":
116
+ valid = typeof component === "string" && _isValidJsonPointer(component);
117
+ break;
118
+ default:
119
+ break;
120
+ }
121
+ if (!valid) {
122
+ _addError(diagnostics, `${path}/value/${componentIndex}`, `Value component is invalid for type "${type.signature}".`);
123
+ }
124
+ }
125
+ }
126
+ function _getDeclarationMapping(declaration, graph, supportedExtensions) {
127
+ const extensionEnabled = !declaration.extension || !supportedExtensions || supportedExtensions.has(declaration.extension);
128
+ const mapping = extensionEnabled ? getMappingForDeclaration(declaration, false) : undefined;
129
+ if (mapping) {
130
+ if (declaration.extension && mapping.declarationSchema && !_isCompatibleExtensionDeclaration(declaration, graph, mapping.declarationSchema)) {
131
+ return { mapping: undefined, support: "unsupported-extension" };
132
+ }
133
+ return { mapping, support: declaration.extension ? "extension" : "core" };
134
+ }
135
+ return {
136
+ mapping: undefined,
137
+ support: declaration.extension ? "unsupported-extension" : "unknown-core",
138
+ };
139
+ }
140
+ function _isCompatibleExtensionDeclaration(declaration, graph, schema) {
141
+ const matches = (actual, expected) => {
142
+ const actualKeys = Object.keys(actual ?? {}).sort();
143
+ const expectedKeys = Object.keys(expected).sort();
144
+ if (actualKeys.length !== expectedKeys.length || actualKeys.some((key, index) => key !== expectedKeys[index])) {
145
+ return false;
146
+ }
147
+ return actualKeys.every((key) => graph.types?.[actual[key].type]?.signature === expected[key]);
148
+ };
149
+ return matches(declaration.inputValueSockets, schema.inputValueSockets) && matches(declaration.outputValueSockets, schema.outputValueSockets);
150
+ }
151
+ function _validateConfigurationValue(value, type) {
152
+ if (!value) {
153
+ return false;
154
+ }
155
+ const isInt = (entry) => typeof entry === "number" && Number.isInteger(entry) && entry >= -2147483648 && entry <= 2147483647;
156
+ switch (type) {
157
+ case "bool":
158
+ return value.length === 1 && typeof value[0] === "boolean";
159
+ case "int":
160
+ return value.length === 1 && isInt(value[0]);
161
+ case "int[]":
162
+ return value.every(isInt);
163
+ case "string":
164
+ return value.length === 1 && typeof value[0] === "string";
165
+ default:
166
+ return value.length > 0;
167
+ }
168
+ }
169
+ function _getIndexedArrayLength(graph, source, assetNodeCount) {
170
+ switch (source) {
171
+ case "types":
172
+ return graph.types?.length ?? 0;
173
+ case "variables":
174
+ return graph.variables?.length ?? 0;
175
+ case "events":
176
+ return graph.events?.length ?? 0;
177
+ case "nodes":
178
+ return graph.nodes?.length ?? 0;
179
+ case "assetNodes":
180
+ return assetNodeCount ?? 0;
181
+ default:
182
+ return 0;
183
+ }
184
+ }
185
+ function _isEffectiveConfigurationValue(configuration, property, graph, assetNodeCount) {
186
+ if (!configuration || !_validateConfigurationValue(configuration.value, property.configurationType) || !configuration.value) {
187
+ return false;
188
+ }
189
+ if (property.minItems !== undefined && configuration.value.length < property.minItems) {
190
+ return false;
191
+ }
192
+ if (property.minimum !== undefined && configuration.value.some((value) => typeof value !== "number" || value < property.minimum)) {
193
+ return false;
194
+ }
195
+ if (property.maximum !== undefined && configuration.value.some((value) => typeof value !== "number" || value > property.maximum)) {
196
+ return false;
197
+ }
198
+ if (property.indexSource) {
199
+ const length = _getIndexedArrayLength(graph, property.indexSource, assetNodeCount);
200
+ if (configuration.value.some((value) => !_isValidIndex(value, length))) {
201
+ return false;
202
+ }
203
+ }
204
+ if (property.allowedSignatures) {
205
+ const typeIndex = configuration.value[0];
206
+ const signature = typeof typeIndex === "number" ? graph.types?.[typeIndex]?.signature : undefined;
207
+ if (!signature || !property.allowedSignatures.includes(signature)) {
208
+ return false;
209
+ }
210
+ }
211
+ if (property.debugLogTemplate && !ParseDebugLogTemplate(configuration.value[0]).valid) {
212
+ return false;
213
+ }
214
+ return true;
215
+ }
216
+ function _getAllowedDynamicFlowOutputSockets(node, mapping, graph, assetNodeCount) {
217
+ for (const [key, property] of Object.entries(mapping.configuration ?? {})) {
218
+ if (!property.generatesOutputFlowSockets) {
219
+ continue;
220
+ }
221
+ const configured = node.configuration?.[key];
222
+ const values = _isEffectiveConfigurationValue(configured, property, graph, assetNodeCount) ? configured.value : property.defaultValue;
223
+ return new Set(Array.isArray(values) ? values.map(String) : []);
224
+ }
225
+ return undefined;
226
+ }
227
+ function _matchesFlowOutputSocket(mapping, socket, allowedDynamicSockets) {
228
+ const flowMappings = mapping.outputs?.flows;
229
+ if (flowMappings && Object.prototype.hasOwnProperty.call(flowMappings, socket)) {
230
+ return true;
231
+ }
232
+ const hasWildcard = Object.keys(flowMappings ?? {}).some((name) => name.startsWith("[") && name.endsWith("]"));
233
+ return !hasWildcard || !allowedDynamicSockets || allowedDynamicSockets.has(socket);
234
+ }
235
+ /**
236
+ * Creates the executable graph view with all ratified configuration defaults and fallback rules
237
+ * applied without modifying the lossless source graph.
238
+ * @param graph source graph
239
+ * @param declarations canonical declarations
240
+ * @param assetNodeCount number of glTF nodes in the containing asset
241
+ * @returns normalized executable graph
242
+ */
243
+ export function CreateEffectiveKHRInteractivityGraph(graph, declarations, assetNodeCount) {
244
+ const effective = CloneKHRInteractivityGraph(graph);
245
+ for (let nodeIndex = 0; nodeIndex < (effective.nodes?.length ?? 0); nodeIndex++) {
246
+ const node = effective.nodes[nodeIndex];
247
+ const declaration = declarations[node.declaration];
248
+ if (!declaration || declaration.support === "unknown-core" || declaration.support === "unsupported-extension") {
249
+ continue;
250
+ }
251
+ const mapping = getMappingForDeclaration(declaration.source, false);
252
+ if (!mapping?.configuration) {
253
+ continue;
254
+ }
255
+ const groupedValidity = new Map();
256
+ for (const [key, property] of Object.entries(mapping.configuration)) {
257
+ if (property.configurationGroup) {
258
+ const valid = _isEffectiveConfigurationValue(node.configuration?.[key], property, effective, assetNodeCount);
259
+ groupedValidity.set(property.configurationGroup, (groupedValidity.get(property.configurationGroup) ?? true) && valid);
260
+ }
261
+ }
262
+ for (const [key, property] of Object.entries(mapping.configuration)) {
263
+ const valid = (!property.configurationGroup || groupedValidity.get(property.configurationGroup) === true) &&
264
+ _isEffectiveConfigurationValue(node.configuration?.[key], property, effective, assetNodeCount);
265
+ if (!valid && property.defaultValue !== undefined) {
266
+ node.configuration ?? (node.configuration = {});
267
+ node.configuration[key] = {
268
+ value: Array.isArray(property.defaultValue) ? _cloneJson(property.defaultValue) : [property.defaultValue],
269
+ };
270
+ }
271
+ else if (valid && property.uniqueValues && node.configuration?.[key]?.value) {
272
+ node.configuration[key].value = Array.from(new Set(node.configuration[key].value));
273
+ }
274
+ }
275
+ }
276
+ return effective;
277
+ }
278
+ function _getPointerTemplateSockets(pointer) {
279
+ const sockets = [];
280
+ for (const segment of pointer.split("/")) {
281
+ const signature = segment.startsWith("[") && !segment.startsWith("[[") ? "int" : segment.startsWith("{") && !segment.startsWith("{{") ? "ref" : undefined;
282
+ if (signature) {
283
+ const name = segment
284
+ .substring(1, segment.length - 1)
285
+ .replace(/~1/g, "/")
286
+ .replace(/~0/g, "~");
287
+ sockets.push({ name, signature });
288
+ }
289
+ }
290
+ return sockets;
291
+ }
292
+ function _resolveValueTypeIndex(value, graph, declarations, visited = new Set()) {
293
+ if ("node" in value) {
294
+ return _resolveOutputTypeIndex(value.node, value.socket ?? "value", graph, declarations, visited);
295
+ }
296
+ return value.type;
297
+ }
298
+ function _resolveOutputTypeIndex(nodeIndex, socket, graph, declarations, visited = new Set()) {
299
+ const key = `${nodeIndex}:${socket}`;
300
+ if (visited.has(key)) {
301
+ return undefined;
302
+ }
303
+ visited.add(key);
304
+ if (!_isValidIndex(nodeIndex, graph.nodes?.length ?? 0)) {
305
+ return undefined;
306
+ }
307
+ const sourceNode = graph.nodes[nodeIndex];
308
+ if (!_isValidIndex(sourceNode.declaration, declarations.length)) {
309
+ return undefined;
310
+ }
311
+ const declaration = declarations[sourceNode.declaration];
312
+ const declaredType = declaration.source.outputValueSockets?.[socket]?.type;
313
+ if (declaredType !== undefined) {
314
+ return declaredType;
315
+ }
316
+ const booleanType = () => graph.types?.findIndex((type) => type.signature === "bool");
317
+ if (socket === "isValid") {
318
+ return booleanType();
319
+ }
320
+ if (declaration.operation === "pointer/get" && socket === "value") {
321
+ const configuredType = sourceNode.configuration?.type?.value?.[0];
322
+ return typeof configuredType === "number" ? configuredType : undefined;
323
+ }
324
+ if (declaration.operation === "variable/get" && socket === "value") {
325
+ const variableIndex = sourceNode.configuration?.variable?.value?.[0];
326
+ return typeof variableIndex === "number" ? graph.variables?.[variableIndex]?.type : undefined;
327
+ }
328
+ if (declaration.operation === "math/switch" && socket === "value") {
329
+ const defaultInput = sourceNode.values?.default;
330
+ return defaultInput ? _resolveValueTypeIndex(defaultInput, graph, declarations, visited) : undefined;
331
+ }
332
+ if (declaration.operation === "event/receive" && socket !== "event") {
333
+ const eventIndex = sourceNode.configuration?.event?.value?.[0];
334
+ return typeof eventIndex === "number" ? graph.events?.[eventIndex]?.values?.[socket]?.type : undefined;
335
+ }
336
+ const fixedSignature = _getFixedOutputSignature(declaration.operation, socket);
337
+ if (fixedSignature) {
338
+ return graph.types?.findIndex((type) => type.signature === fixedSignature);
339
+ }
340
+ const mapping = getMappingForDeclaration(declaration.source, false);
341
+ const outputMapping = mapping?.outputs?.values?.[socket];
342
+ const mappedType = outputMapping?.gltfType;
343
+ const signature = mappedType === "number" ? "float" : mappedType === "boolean" ? "bool" : mappedType;
344
+ if (signature && signature in gltfTypeToBabylonType) {
345
+ return graph.types?.findIndex((type) => type.signature === signature);
346
+ }
347
+ const typeSource = outputMapping?.typeSourceInput ? sourceNode.values?.[outputMapping.typeSourceInput] : undefined;
348
+ return typeSource ? _resolveValueTypeIndex(typeSource, graph, declarations, visited) : undefined;
349
+ }
350
+ function _getFixedOutputSignature(operation, socket) {
351
+ if (socket !== "value") {
352
+ if ((operation === "math/rgbToOkLCh" && ["l", "c", "h"].includes(socket)) ||
353
+ (operation === "math/rgbFromOkLCh" && ["r", "g", "b"].includes(socket)) ||
354
+ operation.startsWith("math/extract")) {
355
+ return "float";
356
+ }
357
+ if (operation === "math/matDecompose") {
358
+ return socket === "rotation" ? "float4" : socket === "translation" || socket === "scale" ? "float3" : undefined;
359
+ }
360
+ if (operation === "math/quatToAxisAngle") {
361
+ return socket === "axis" ? "float3" : socket === "angle" ? "float" : undefined;
362
+ }
363
+ if ((operation === "flow/doN" && socket === "currentCount") || (operation === "flow/for" && socket === "index")) {
364
+ return "int";
365
+ }
366
+ if (operation === "flow/setDelay" && socket === "lastDelay") {
367
+ return "ref";
368
+ }
369
+ if (operation === "flow/throttle" && socket === "lastRemainingTime") {
370
+ return "float";
371
+ }
372
+ if ((operation === "event/onStart" || operation === "event/onTick" || operation === "event/receive") && socket === "event") {
373
+ return "ref";
374
+ }
375
+ if (operation === "event/onTick" && socket === "timeSinceLastTick") {
376
+ return "float";
377
+ }
378
+ if (operation === "event/onTick" && socket === "timeSinceStart") {
379
+ return "float";
380
+ }
381
+ return undefined;
382
+ }
383
+ if (["math/E", "math/Pi", "math/Tau", "math/Inf", "math/NaN", "math/random", "math/length", "math/dot", "math/determinant", "math/quatAngleBetween"].includes(operation)) {
384
+ return "float";
385
+ }
386
+ if (["math/eq", "ref/eq", "math/lt", "math/le", "math/gt", "math/ge", "math/isNaN", "math/isInf", "type/intToBool", "type/floatToBool"].includes(operation)) {
387
+ return "bool";
388
+ }
389
+ if (["math/clz", "math/ctz", "math/popcnt", "type/boolToInt", "type/floatToInt"].includes(operation)) {
390
+ return "int";
391
+ }
392
+ if (["type/boolToFloat", "type/intToFloat"].includes(operation)) {
393
+ return "float";
394
+ }
395
+ return undefined;
396
+ }
397
+ function _validateNode(node, nodeIndex, graph, declarations, diagnostics, graphPath, assetNodeCount) {
398
+ const path = `${graphPath}/nodes/${nodeIndex}`;
399
+ if (!_isValidIndex(node.declaration, declarations.length)) {
400
+ _addError(diagnostics, `${path}/declaration`, `Declaration index ${String(node.declaration)} is out of range.`);
401
+ return;
402
+ }
403
+ const declarationModel = declarations[node.declaration];
404
+ const mapping = declarationModel.support === "unsupported-extension" ? getNoOpMappingForDeclaration(declarationModel.source) : getMappingForDeclaration(declarationModel.source, false);
405
+ if (declarationModel.support === "unknown-core" || !mapping) {
406
+ return;
407
+ }
408
+ const reportConfigurationIssue = (property, issuePath, message) => {
409
+ if (property.invalidUsesDefault || property.defaultValue !== undefined) {
410
+ _addWarning(diagnostics, issuePath, `${message} The default configuration will be used.`);
411
+ }
412
+ else {
413
+ _addError(diagnostics, issuePath, message);
414
+ }
415
+ };
416
+ for (const [key, configuration] of Object.entries(node.configuration ?? {})) {
417
+ if (configuration.value && configuration.value.length === 0) {
418
+ _addError(diagnostics, `${path}/configuration/${key}/value`, "Configuration value arrays must contain at least one item when present.");
419
+ }
420
+ }
421
+ for (const [key, property] of Object.entries(mapping.configuration ?? {})) {
422
+ const configuration = node.configuration?.[key];
423
+ const isConfigurationValid = !!configuration && _validateConfigurationValue(configuration.value, property.configurationType);
424
+ if (!configuration && property.required) {
425
+ _addError(diagnostics, `${path}/configuration/${key}`, `Required configuration "${key}" is missing.`);
426
+ }
427
+ else if (configuration && !isConfigurationValid) {
428
+ reportConfigurationIssue(property, `${path}/configuration/${key}`, `Configuration "${key}" is invalid.`);
429
+ }
430
+ if (isConfigurationValid && configuration.value && property.minItems !== undefined && configuration.value.length < property.minItems) {
431
+ _addError(diagnostics, `${path}/configuration/${key}/value`, `Configuration "${key}" requires at least ${property.minItems} value(s).`);
432
+ }
433
+ if (isConfigurationValid && configuration.value && property.indexSource) {
434
+ const length = _getIndexedArrayLength(graph, property.indexSource, assetNodeCount);
435
+ for (let index = 0; index < configuration.value.length; index++) {
436
+ if (!_isValidIndex(configuration.value[index], length)) {
437
+ reportConfigurationIssue(property, `${path}/configuration/${key}/value/${index}`, `Index is out of range for graph ${property.indexSource}.`);
438
+ }
439
+ }
440
+ }
441
+ if (isConfigurationValid && configuration.value && property.allowedSignatures) {
442
+ const typeIndex = configuration.value[0];
443
+ const signature = typeof typeIndex === "number" ? graph.types?.[typeIndex]?.signature : undefined;
444
+ if (!signature || !property.allowedSignatures.includes(signature)) {
445
+ reportConfigurationIssue(property, `${path}/configuration/${key}/value/0`, `Type "${String(signature)}" is not supported by this operation.`);
446
+ }
447
+ }
448
+ if (isConfigurationValid && configuration.value && property.debugLogTemplate && !ParseDebugLogTemplate(configuration.value[0]).valid) {
449
+ reportConfigurationIssue(property, `${path}/configuration/${key}/value/0`, `Configuration "${key}" is not a valid debug message template.`);
450
+ }
451
+ if (isConfigurationValid && configuration.value && (property.minimum !== undefined || property.maximum !== undefined)) {
452
+ const numericValue = configuration.value[0];
453
+ if ((property.minimum !== undefined && numericValue < property.minimum) || (property.maximum !== undefined && numericValue > property.maximum)) {
454
+ reportConfigurationIssue(property, `${path}/configuration/${key}/value/0`, `Configuration "${key}" must be between ${String(property.minimum)} and ${String(property.maximum)}.`);
455
+ }
456
+ }
457
+ if (isConfigurationValid && configuration.value && property.generatesInputValueSockets) {
458
+ for (const configuredIndex of configuration.value) {
459
+ if (typeof configuredIndex !== "number" || !_isValidIndex(configuredIndex, graph.variables?.length ?? 0)) {
460
+ continue;
461
+ }
462
+ const socket = node.values?.[String(configuredIndex)];
463
+ if (!socket) {
464
+ _addError(diagnostics, `${path}/values/${configuredIndex}`, `Required configured input value socket "${configuredIndex}" is missing.`);
465
+ }
466
+ else if (_resolveValueTypeIndex(socket, graph, declarations) !== graph.variables[configuredIndex].type) {
467
+ _addError(diagnostics, `${path}/values/${configuredIndex}/type`, `Configured input value socket "${configuredIndex}" has the wrong type.`);
468
+ }
469
+ }
470
+ }
471
+ if (isConfigurationValid && configuration.value && property.generatesCaseInputValueSockets) {
472
+ const defaultType = node.values?.default ? _resolveValueTypeIndex(node.values.default, graph, declarations) : undefined;
473
+ for (const caseValue of configuration.value) {
474
+ const socket = node.values?.[String(caseValue)];
475
+ if (!socket) {
476
+ _addError(diagnostics, `${path}/values/${caseValue}`, `Required case input value socket "${caseValue}" is missing.`);
477
+ }
478
+ else if (defaultType !== undefined && _resolveValueTypeIndex(socket, graph, declarations) !== defaultType) {
479
+ _addError(diagnostics, `${path}/values/${caseValue}/type`, `Case input value socket "${caseValue}" must match the default socket type.`);
480
+ }
481
+ }
482
+ }
483
+ if (isConfigurationValid && configuration.value && property.pointerTemplate && typeof configuration.value[0] === "string") {
484
+ for (const socketDefinition of _getPointerTemplateSockets(configuration.value[0])) {
485
+ const socket = node.values?.[socketDefinition.name];
486
+ const expectedType = graph.types?.findIndex((type) => type.signature === socketDefinition.signature) ?? -1;
487
+ if (!socket) {
488
+ _addError(diagnostics, `${path}/values/${socketDefinition.name}`, `Required pointer template input "${socketDefinition.name}" is missing.`);
489
+ }
490
+ else if (expectedType < 0 || _resolveValueTypeIndex(socket, graph, declarations) !== expectedType) {
491
+ _addError(diagnostics, `${path}/values/${socketDefinition.name}/type`, `Pointer template input "${socketDefinition.name}" must have type "${socketDefinition.signature}".`);
492
+ }
493
+ }
494
+ }
495
+ }
496
+ if (declarationModel.operation === "debug/log") {
497
+ const severityProperty = mapping.configuration?.severity;
498
+ const messageProperty = mapping.configuration?.message;
499
+ const severityValid = !!severityProperty && _isEffectiveConfigurationValue(node.configuration?.severity, severityProperty, graph, assetNodeCount);
500
+ const messageValid = !!messageProperty && _isEffectiveConfigurationValue(node.configuration?.message, messageProperty, graph, assetNodeCount);
501
+ const message = severityValid && messageValid ? node.configuration.message.value[0] : "";
502
+ for (const socket of ParseDebugLogTemplate(message).sockets) {
503
+ if (!node.values?.[socket]) {
504
+ _addError(diagnostics, `${path}/values/${socket}`, `Required debug message template input "${socket}" is missing.`);
505
+ }
506
+ }
507
+ }
508
+ for (const [key, property] of Object.entries(mapping.inputs?.values ?? {})) {
509
+ if (!key.startsWith("[") && !node.values?.[key]) {
510
+ _addError(diagnostics, `${path}/values/${key}`, `Required input value socket "${key}" is missing.`);
511
+ }
512
+ if (declarationModel.operation === "event/send") {
513
+ const eventIndex = node.configuration?.event?.value?.[0];
514
+ const event = typeof eventIndex === "number" ? graph.events?.[eventIndex] : undefined;
515
+ for (const [socket, eventValue] of Object.entries(event?.values ?? {})) {
516
+ const source = node.values?.[socket];
517
+ if (!source) {
518
+ _addError(diagnostics, `${path}/values/${socket}`, `Required custom event input value socket "${socket}" is missing.`);
519
+ }
520
+ else if (_resolveValueTypeIndex(source, graph, declarations) !== eventValue.type) {
521
+ _addError(diagnostics, `${path}/values/${socket}/type`, `Custom event input value socket "${socket}" has the wrong type.`);
522
+ }
523
+ }
524
+ }
525
+ const expectedSignature = property.gltfType === "number" ? "float" : property.gltfType === "boolean" ? "bool" : property.gltfType;
526
+ const source = node.values?.[key];
527
+ if (source && expectedSignature && expectedSignature in gltfTypeToBabylonType) {
528
+ const typeIndex = _resolveValueTypeIndex(source, graph, declarations);
529
+ if (typeIndex !== undefined && graph.types?.[typeIndex]?.signature !== expectedSignature) {
530
+ _addError(diagnostics, `${path}/values/${key}/type`, `Input value socket "${key}" must have type "${expectedSignature}".`);
531
+ }
532
+ }
533
+ }
534
+ if (mapping.validation) {
535
+ try {
536
+ const validationNode = _cloneJson(node);
537
+ for (const value of Object.values(validationNode.values ?? {})) {
538
+ if (value.type === undefined) {
539
+ value.type = _resolveValueTypeIndex(value, graph, declarations);
540
+ }
541
+ }
542
+ const result = mapping.validation(validationNode, graph);
543
+ if (!result.valid) {
544
+ _addError(diagnostics, path, result.error ?? `Operation "${declarationModel.operation}" is invalid.`);
545
+ }
546
+ }
547
+ catch (error) {
548
+ _addError(diagnostics, path, error.message);
549
+ }
550
+ }
551
+ for (const [key, value] of Object.entries(node.values ?? {})) {
552
+ if (declarationModel.support === "unsupported-extension" && !declarationModel.source.inputValueSockets?.[key]) {
553
+ _addError(diagnostics, `${path}/values/${key}`, `Input value socket "${key}" is not defined by operation "${declarationModel.operation}".`);
554
+ }
555
+ if ("node" in value) {
556
+ if ("value" in value) {
557
+ _addError(diagnostics, `${path}/values/${key}`, "An input value socket cannot define both inline and node sources.");
558
+ }
559
+ if (!_isValidIndex(value.node, graph.nodes?.length ?? 0)) {
560
+ _addError(diagnostics, `${path}/values/${key}/node`, `Node index ${String(value.node)} is out of range.`);
561
+ continue;
562
+ }
563
+ if (value.node >= nodeIndex) {
564
+ _addError(diagnostics, `${path}/values/${key}/node`, "Value connections must reference an earlier node.");
565
+ }
566
+ if (value.type !== undefined) {
567
+ _validateTypeIndex(value.type, graph, diagnostics, `${path}/values/${key}/type`);
568
+ }
569
+ const sourceNode = graph.nodes[value.node];
570
+ if (_isValidIndex(sourceNode.declaration, declarations.length)) {
571
+ const sourceDeclaration = declarations[sourceNode.declaration];
572
+ const sourceMapping = sourceDeclaration.support === "unsupported-extension"
573
+ ? getNoOpMappingForDeclaration(sourceDeclaration.source)
574
+ : getMappingForDeclaration(sourceDeclaration.source, false);
575
+ const sourceSocket = value.socket ?? "value";
576
+ const hasMappedSocket = _matchesOutputSocket(sourceMapping?.outputs?.values, sourceSocket, sourceDeclaration, sourceNode, graph, "value");
577
+ if (sourceMapping && !hasMappedSocket) {
578
+ _addError(diagnostics, `${path}/values/${key}/socket`, `Output value socket "${sourceSocket}" does not exist on node ${value.node}.`);
579
+ }
580
+ const declaredOutputType = sourceDeclaration.source.outputValueSockets?.[sourceSocket]?.type;
581
+ const effectiveOutputType = declaredOutputType ?? _resolveOutputTypeIndex(value.node, sourceSocket, graph, declarations);
582
+ if (value.type !== undefined && effectiveOutputType !== undefined && value.type !== effectiveOutputType) {
583
+ _addError(diagnostics, `${path}/values/${key}/type`, `Type assertion does not match output socket "${sourceSocket}".`);
584
+ }
585
+ }
586
+ }
587
+ else {
588
+ _validateValue(value, graph, diagnostics, `${path}/values/${key}`);
589
+ }
590
+ }
591
+ for (const [key, flow] of Object.entries(node.flows ?? {})) {
592
+ const allowedDynamicSockets = _getAllowedDynamicFlowOutputSockets(node, mapping, graph, assetNodeCount);
593
+ if (!_matchesFlowOutputSocket(mapping, key, allowedDynamicSockets)) {
594
+ _addError(diagnostics, `${path}/flows/${key}`, `Output flow socket "${key}" is not defined by operation "${declarationModel.operation}".`);
595
+ }
596
+ if (!_isValidIndex(flow.node, graph.nodes?.length ?? 0)) {
597
+ _addError(diagnostics, `${path}/flows/${key}/node`, `Node index ${String(flow.node)} is out of range.`);
598
+ continue;
599
+ }
600
+ if (flow.node <= nodeIndex) {
601
+ _addError(diagnostics, `${path}/flows/${key}/node`, "Flow connections must reference a later node.");
602
+ }
603
+ }
604
+ }
605
+ /**
606
+ * Creates and validates a canonical copy of one KHR_interactivity graph.
607
+ * @param graph source graph
608
+ * @param index graph index in the root extension
609
+ * @param supportedExtensions enabled extensions that may provide executable operations
610
+ * @param assetNodeCount number of nodes in the containing glTF asset
611
+ * @returns the canonical graph model
612
+ */
613
+ export function CreateKHRInteractivityGraphModel(graph, index = 0, supportedExtensions, assetNodeCount) {
614
+ const diagnostics = [];
615
+ const path = `/extensions/KHR_interactivity/graphs/${index}`;
616
+ if (graph === null || typeof graph !== "object" || Array.isArray(graph)) {
617
+ _addError(diagnostics, path, "Behavior graph must be an object.");
618
+ graph = {};
619
+ }
620
+ else {
621
+ graph = CloneKHRInteractivityGraph(graph);
622
+ }
623
+ const invalidEntryDefaults = {
624
+ types: () => ({ signature: "custom" }),
625
+ variables: () => ({ type: -1 }),
626
+ events: () => ({}),
627
+ declarations: () => ({ op: "" }),
628
+ nodes: () => ({ declaration: -1 }),
629
+ };
630
+ for (const key of ["types", "variables", "events", "declarations", "nodes"]) {
631
+ const collection = graph[key];
632
+ if (collection !== undefined && !Array.isArray(collection)) {
633
+ _addError(diagnostics, `${path}/${key}`, `"${key}" must be an array when present.`);
634
+ graph[key] = [];
635
+ }
636
+ else if (Array.isArray(collection) && collection.length === 0) {
637
+ _addError(diagnostics, `${path}/${key}`, `"${key}" must contain at least one item when present.`);
638
+ }
639
+ else if (Array.isArray(collection)) {
640
+ graph[key] = collection.map((entry, entryIndex) => {
641
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) {
642
+ _addError(diagnostics, `${path}/${key}/${entryIndex}`, `Each "${key}" item must be an object.`);
643
+ return invalidEntryDefaults[key]();
644
+ }
645
+ return entry;
646
+ });
647
+ }
648
+ }
649
+ for (let eventIndex = 0; eventIndex < (graph.events?.length ?? 0); eventIndex++) {
650
+ if (graph.events[eventIndex].values && Object.keys(graph.events[eventIndex].values).length === 0) {
651
+ _addError(diagnostics, `${path}/events/${eventIndex}/values`, '"values" must contain at least one property when present.');
652
+ }
653
+ }
654
+ for (let declarationIndex = 0; declarationIndex < (graph.declarations?.length ?? 0); declarationIndex++) {
655
+ const declaration = graph.declarations[declarationIndex];
656
+ for (const key of ["inputValueSockets", "outputValueSockets"]) {
657
+ if (declaration[key] && Object.keys(declaration[key]).length === 0) {
658
+ _addError(diagnostics, `${path}/declarations/${declarationIndex}/${key}`, `"${key}" must contain at least one property when present.`);
659
+ }
660
+ }
661
+ }
662
+ for (let nodeIndex = 0; nodeIndex < (graph.nodes?.length ?? 0); nodeIndex++) {
663
+ const node = graph.nodes[nodeIndex];
664
+ for (const key of ["configuration", "values", "flows"]) {
665
+ if (node[key] && Object.keys(node[key]).length === 0) {
666
+ _addError(diagnostics, `${path}/nodes/${nodeIndex}/${key}`, `"${key}" must contain at least one property when present.`);
667
+ }
668
+ }
669
+ }
670
+ const declarationKeys = new Set();
671
+ const declarations = (graph.declarations ?? []).map((declaration, declarationIndex) => {
672
+ const inputSockets = Object.keys(declaration.inputValueSockets ?? {})
673
+ .sort()
674
+ .map((key) => `${JSON.stringify(key)}:${declaration.inputValueSockets[key].type}`)
675
+ .join(",");
676
+ const declarationKey = `${JSON.stringify(declaration.op)}:${JSON.stringify(declaration.extension ?? "")}:${inputSockets}`;
677
+ if (declarationKeys.has(declarationKey)) {
678
+ _addError(diagnostics, `${path}/declarations/${declarationIndex}`, "Duplicate equivalent declaration.");
679
+ }
680
+ declarationKeys.add(declarationKey);
681
+ const { support } = _getDeclarationMapping(declaration, graph, supportedExtensions);
682
+ if (support === "unknown-core") {
683
+ _addError(diagnostics, `${path}/declarations/${declarationIndex}/op`, `Unknown core operation "${declaration.op}".`);
684
+ }
685
+ if (!declaration.extension && (declaration.inputValueSockets || declaration.outputValueSockets)) {
686
+ _addError(diagnostics, `${path}/declarations/${declarationIndex}`, "Core operation declarations cannot define dynamic value sockets.");
687
+ }
688
+ for (const [socket, definition] of Object.entries(declaration.inputValueSockets ?? {})) {
689
+ _validateTypeIndex(definition.type, graph, diagnostics, `${path}/declarations/${declarationIndex}/inputValueSockets/${socket}/type`);
690
+ }
691
+ for (const [socket, definition] of Object.entries(declaration.outputValueSockets ?? {})) {
692
+ _validateTypeIndex(definition.type, graph, diagnostics, `${path}/declarations/${declarationIndex}/outputValueSockets/${socket}/type`);
693
+ }
694
+ return {
695
+ index: declarationIndex,
696
+ operation: declaration.extension ? `${declaration.op}:${declaration.extension}` : declaration.op,
697
+ support,
698
+ source: declaration,
699
+ };
700
+ });
701
+ const signatures = new Set();
702
+ for (let typeIndex = 0; typeIndex < (graph.types?.length ?? 0); typeIndex++) {
703
+ const signature = graph.types[typeIndex].signature;
704
+ if (signature !== "custom" && signatures.has(signature)) {
705
+ _addError(diagnostics, `${path}/types/${typeIndex}/signature`, `Duplicate built-in type signature "${signature}".`);
706
+ }
707
+ signatures.add(signature);
708
+ if (signature !== "custom" && !gltfTypeToBabylonType[signature]) {
709
+ _addError(diagnostics, `${path}/types/${typeIndex}/signature`, `Unknown built-in type signature "${signature}".`);
710
+ }
711
+ }
712
+ for (let variableIndex = 0; variableIndex < (graph.variables?.length ?? 0); variableIndex++) {
713
+ _validateValue(graph.variables[variableIndex], graph, diagnostics, `${path}/variables/${variableIndex}`);
714
+ }
715
+ for (let eventIndex = 0; eventIndex < (graph.events?.length ?? 0); eventIndex++) {
716
+ const eventId = graph.events[eventIndex].id;
717
+ if (eventId !== undefined && graph.events.slice(0, eventIndex).some((event) => event.id === eventId)) {
718
+ _addError(diagnostics, `${path}/events/${eventIndex}/id`, `Duplicate event id "${eventId}".`);
719
+ }
720
+ if (graph.events[eventIndex].values && Object.prototype.hasOwnProperty.call(graph.events[eventIndex].values, "event")) {
721
+ _addError(diagnostics, `${path}/events/${eventIndex}/values/event`, 'Custom event value socket id "event" is reserved.');
722
+ }
723
+ for (const [socket, value] of Object.entries(graph.events[eventIndex].values ?? {})) {
724
+ _validateValue(value, graph, diagnostics, `${path}/events/${eventIndex}/values/${socket}`);
725
+ }
726
+ }
727
+ for (let nodeIndex = 0; nodeIndex < (graph.nodes?.length ?? 0); nodeIndex++) {
728
+ _validateNode(graph.nodes[nodeIndex], nodeIndex, graph, declarations, diagnostics, path, assetNodeCount);
729
+ }
730
+ return {
731
+ index,
732
+ path,
733
+ name: graph.name ?? `Graph ${index + 1}`,
734
+ source: graph,
735
+ effectiveSource: CreateEffectiveKHRInteractivityGraph(graph, declarations, assetNodeCount),
736
+ declarations,
737
+ diagnostics,
738
+ valid: !diagnostics.some((diagnostic) => diagnostic.severity === "error"),
739
+ };
740
+ }
741
+ /**
742
+ * Creates an immutable canonical KHR_interactivity document and validates all graph-local references.
743
+ * @param extension source extension object from the glTF document
744
+ * @param supportedExtensions enabled extensions that may provide executable operations
745
+ * @param assetNodeCount number of nodes in the containing glTF asset
746
+ * @returns the canonical document
747
+ */
748
+ export function CreateKHRInteractivityDocument(extension, supportedExtensions, assetNodeCount) {
749
+ const source = _cloneJson(extension);
750
+ const diagnostics = [];
751
+ if (!Array.isArray(source.graphs)) {
752
+ _addError(diagnostics, "/extensions/KHR_interactivity/graphs", '"graphs" must be an array.');
753
+ source.graphs = [];
754
+ }
755
+ if (source.graphs.length === 0) {
756
+ _addError(diagnostics, "/extensions/KHR_interactivity/graphs", "At least one behavior graph is required.");
757
+ }
758
+ const requestedDefaultGraphIndex = source.graph ?? 0;
759
+ const defaultGraphIndex = _isValidIndex(requestedDefaultGraphIndex, source.graphs.length) ? requestedDefaultGraphIndex : -1;
760
+ if (defaultGraphIndex === -1) {
761
+ _addError(diagnostics, "/extensions/KHR_interactivity/graph", `Default graph index ${String(requestedDefaultGraphIndex)} is out of range.`);
762
+ }
763
+ const graphs = source.graphs.map((graph, index) => CreateKHRInteractivityGraphModel(graph, index, supportedExtensions, assetNodeCount));
764
+ source.graphs = graphs.map((graph) => graph.source);
765
+ return {
766
+ specificationCommit: KHR_INTERACTIVITY_SPECIFICATION_COMMIT,
767
+ source,
768
+ defaultGraphIndex,
769
+ graphs,
770
+ diagnostics,
771
+ };
772
+ }
773
+ //# sourceMappingURL=interactivityGraphModel.js.map