interactivityFunctions.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import { RandomGUID } from "@babylonjs/core/Misc/guid.js";
  2. import { gltfToFlowGraphTypeMap, gltfTypeToBabylonType } from "./interactivityUtils.js";
  3. import { FlowGraphConnectionType } from "@babylonjs/core/FlowGraph/flowGraphConnection.js";
  4. function convertValueWithType(configObject, definition, context) {
  5. if (configObject.type !== undefined) {
  6. // get the type on the gltf definition
  7. const type = definition.types && definition.types[configObject.type];
  8. if (!type) {
  9. throw new Error(`${context}: Unknown type: ${configObject.type}`);
  10. }
  11. const signature = type.signature;
  12. if (!signature) {
  13. throw new Error(`${context}: Type ${configObject.type} has no signature`);
  14. }
  15. const convertedType = gltfTypeToBabylonType[signature];
  16. return {
  17. value: configObject.value,
  18. className: convertedType,
  19. };
  20. }
  21. else {
  22. return configObject.value;
  23. }
  24. }
  25. function convertConfiguration(gltfBlock, definition, id) {
  26. const converted = {};
  27. const configurationList = gltfBlock.configuration ?? [];
  28. for (const configObject of configurationList) {
  29. if (configObject.id === "customEvent") {
  30. const customEvent = definition.customEvents && definition.customEvents[configObject.value];
  31. if (!customEvent) {
  32. throw new Error(`/extensions/KHR_interactivity/nodes/${id}: Unknown custom event: ${configObject.value}`);
  33. }
  34. converted.eventId = customEvent.id;
  35. converted.eventData = customEvent.values.map((v) => v.id);
  36. }
  37. else if (configObject.id === "variable") {
  38. const variable = definition.variables && definition.variables[configObject.value];
  39. if (!variable) {
  40. throw new Error(`/extensions/KHR_interactivity/nodes/${id}: Unknown variable: ${configObject.value}`);
  41. }
  42. converted.variableName = variable.id;
  43. }
  44. else if (configObject.id === "path") {
  45. // Convert from a GLTF path to a reference to the Babylon.js object
  46. const pathValue = configObject.value;
  47. converted.path = pathValue;
  48. }
  49. else {
  50. converted[configObject.id] = convertValueWithType(configObject, definition, `/extensions/KHR_interactivity/nodes/${id}`);
  51. }
  52. }
  53. return converted;
  54. }
  55. function convertBlock(id, gltfBlock, definition) {
  56. const className = gltfToFlowGraphTypeMap[gltfBlock.type];
  57. if (!className) {
  58. throw new Error(`/extensions/KHR_interactivity/nodes/${id}: Unknown block type: ${gltfBlock.type}`);
  59. }
  60. const uniqueId = id.toString();
  61. const config = convertConfiguration(gltfBlock, definition, uniqueId);
  62. const metadata = gltfBlock.metadata;
  63. const dataInputs = [];
  64. const dataOutputs = [];
  65. const signalInputs = [];
  66. const signalOutputs = [];
  67. return {
  68. className,
  69. config,
  70. uniqueId,
  71. metadata,
  72. dataInputs,
  73. dataOutputs,
  74. signalInputs,
  75. signalOutputs,
  76. };
  77. }
  78. /**
  79. * @internal
  80. * Converts a glTF Interactivity Extension to a serialized flow graph.
  81. * @param gltf the interactivity data
  82. * @returns a serialized flow graph
  83. */
  84. export function convertGLTFToSerializedFlowGraph(gltf) {
  85. // create an empty serialized context to store the values of the connections
  86. const context = {
  87. uniqueId: RandomGUID(),
  88. _userVariables: {},
  89. _connectionValues: {},
  90. };
  91. const executionContexts = [context];
  92. // Blocks converted to the flow graph json format
  93. const flowGraphJsonBlocks = [];
  94. for (let i = 0; i < gltf.nodes.length; i++) {
  95. const gltfBlock = gltf.nodes[i];
  96. const flowGraphJsonBlock = convertBlock(i, gltfBlock, gltf);
  97. flowGraphJsonBlocks.push(flowGraphJsonBlock);
  98. }
  99. // Parse the connections
  100. for (let i = 0; i < gltf.nodes.length; i++) {
  101. const gltfBlock = gltf.nodes[i];
  102. // get the block that was created in the previous step
  103. const fgBlock = flowGraphJsonBlocks[i];
  104. const gltfFlows = gltfBlock.flows ?? [];
  105. // for each output flow of the gltf block
  106. for (const flow of gltfFlows) {
  107. const socketOutName = flow.id;
  108. // create an output connection for the flow graph block
  109. const socketOut = {
  110. uniqueId: RandomGUID(),
  111. name: socketOutName,
  112. _connectionType: FlowGraphConnectionType.Output,
  113. connectedPointIds: [],
  114. };
  115. fgBlock.signalOutputs.push(socketOut);
  116. // get the input node of this flow
  117. const nodeInId = flow.node;
  118. const nodeInSocketName = flow.socket;
  119. // find the corresponding flow graph node
  120. const nodeIn = flowGraphJsonBlocks[nodeInId];
  121. if (!nodeIn) {
  122. throw new Error(`/extensions/KHR_interactivity/nodes/${i}: Could not find node with id ${nodeInId} that connects its input with with node ${i}'s output ${socketOutName}`);
  123. }
  124. // in all of the flow graph input connections, find the one with the same name as the socket
  125. let socketIn = nodeIn.signalInputs.find((s) => s.name === nodeInSocketName);
  126. // if the socket doesn't exist, create the input socket for the connection
  127. if (!socketIn) {
  128. socketIn = {
  129. uniqueId: RandomGUID(),
  130. name: nodeInSocketName,
  131. _connectionType: FlowGraphConnectionType.Input,
  132. connectedPointIds: [],
  133. };
  134. nodeIn.signalInputs.push(socketIn);
  135. }
  136. // connect the sockets
  137. socketIn.connectedPointIds.push(socketOut.uniqueId);
  138. socketOut.connectedPointIds.push(socketIn.uniqueId);
  139. }
  140. // for each input value of the gltf block
  141. const gltfValues = gltfBlock.values ?? [];
  142. for (const value of gltfValues) {
  143. const socketInName = value.id;
  144. // create an input data connection for the flow graph block
  145. const socketIn = {
  146. uniqueId: RandomGUID(),
  147. name: socketInName,
  148. _connectionType: FlowGraphConnectionType.Input,
  149. connectedPointIds: [],
  150. };
  151. fgBlock.dataInputs.push(socketIn);
  152. if (value.value !== undefined) {
  153. // if the value is set on the socket itself, store it in the context
  154. const convertedValue = convertValueWithType(value, gltf, `/extensions/KHR_interactivity/nodes/${i}`);
  155. // convertBlockInputType(gltfBlock, value, convertedValue, `/extensions/KHR_interactivity/nodes/${i}`);
  156. context._connectionValues[socketIn.uniqueId] = convertedValue;
  157. }
  158. else if (value.node !== undefined && value.socket !== undefined) {
  159. // if the value is connected with the output data of another socket, connect the two
  160. const nodeOutId = value.node;
  161. const nodeOutSocketName = value.socket;
  162. // find the flow graph node that owns that output socket
  163. const nodeOut = flowGraphJsonBlocks[nodeOutId];
  164. if (!nodeOut) {
  165. throw new Error(`/extensions/KHR_interactivity/nodes/${i}: Could not find node with id ${nodeOutId} that connects its output with node${i}'s input ${socketInName}`);
  166. }
  167. let socketOut = nodeOut.dataOutputs.find((s) => s.name === nodeOutSocketName);
  168. // if the socket doesn't exist, create it
  169. if (!socketOut) {
  170. socketOut = {
  171. uniqueId: RandomGUID(),
  172. name: nodeOutSocketName,
  173. _connectionType: FlowGraphConnectionType.Output,
  174. connectedPointIds: [],
  175. };
  176. nodeOut.dataOutputs.push(socketOut);
  177. }
  178. // connect the sockets
  179. socketIn.connectedPointIds.push(socketOut.uniqueId);
  180. socketOut.connectedPointIds.push(socketIn.uniqueId);
  181. }
  182. else {
  183. throw new Error(`/extensions/KHR_interactivity/nodes/${i}: Invalid socket ${socketInName} in node ${i}`);
  184. }
  185. }
  186. }
  187. const variables = gltf.variables ?? [];
  188. // Parse variables
  189. for (let i = 0; i < variables.length; i++) {
  190. const variable = variables[i];
  191. const variableName = variable.id;
  192. context._userVariables[variableName] = convertValueWithType(variable, gltf, `/extensions/KHR_interactivity/variables/${i}`);
  193. }
  194. return {
  195. allBlocks: flowGraphJsonBlocks,
  196. executionContexts,
  197. };
  198. }
  199. //# sourceMappingURL=interactivityFunctions.js.map