@fps-games/editor 0.1.5-beta.4 → 0.1.6-beta.1

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 (218) hide show
  1. package/dist/build-info.json +16 -16
  2. package/dist/local-editor-agent-actions.d.ts +8 -1
  3. package/dist/local-editor-agent-actions.d.ts.map +1 -1
  4. package/dist/local-editor-agent-actions.js +171 -12
  5. package/dist/local-editor-agent-actions.js.map +1 -1
  6. package/dist/local-editor-agent-context.d.ts +29 -1
  7. package/dist/local-editor-agent-context.d.ts.map +1 -1
  8. package/dist/local-editor-agent-context.js +403 -0
  9. package/dist/local-editor-agent-context.js.map +1 -1
  10. package/dist/local-editor-harness.d.ts +103 -5
  11. package/dist/local-editor-harness.d.ts.map +1 -1
  12. package/dist/local-editor-harness.js +1982 -101
  13. package/dist/local-editor-harness.js.map +1 -1
  14. package/dist/local-editor-marker-graph.d.ts +92 -0
  15. package/dist/local-editor-marker-graph.d.ts.map +1 -0
  16. package/dist/local-editor-marker-graph.js +1432 -0
  17. package/dist/local-editor-marker-graph.js.map +1 -0
  18. package/dist/local-editor-performance-stats.js +1 -1
  19. package/dist/local-editor-performance-stats.js.map +1 -1
  20. package/dist/playable-babylon-facade.d.ts +2 -2
  21. package/dist/playable-babylon-facade.d.ts.map +1 -1
  22. package/dist/playable-babylon-facade.js +1 -1
  23. package/dist/playable-babylon-facade.js.map +1 -1
  24. package/dist/playable-local-editor-host.d.ts +52 -7
  25. package/dist/playable-local-editor-host.d.ts.map +1 -1
  26. package/dist/playable-local-editor-host.js +23 -1
  27. package/dist/playable-local-editor-host.js.map +1 -1
  28. package/dist/playable-sdk.d.ts +4 -2
  29. package/dist/playable-sdk.d.ts.map +1 -1
  30. package/dist/playable-sdk.js +2 -1
  31. package/dist/playable-sdk.js.map +1 -1
  32. package/node_modules/@fps-games/babylon-renderer/package.json +1 -1
  33. package/node_modules/@fps-games/editor-babylon/dist/camera-controller.d.ts.map +1 -1
  34. package/node_modules/@fps-games/editor-babylon/dist/camera-controller.js +11 -0
  35. package/node_modules/@fps-games/editor-babylon/dist/camera-controller.js.map +1 -1
  36. package/node_modules/@fps-games/editor-babylon/dist/editor-sky.d.ts +3 -1
  37. package/node_modules/@fps-games/editor-babylon/dist/editor-sky.d.ts.map +1 -1
  38. package/node_modules/@fps-games/editor-babylon/dist/editor-sky.js +114 -18
  39. package/node_modules/@fps-games/editor-babylon/dist/editor-sky.js.map +1 -1
  40. package/node_modules/@fps-games/editor-babylon/dist/editor-world.d.ts +1 -0
  41. package/node_modules/@fps-games/editor-babylon/dist/editor-world.d.ts.map +1 -1
  42. package/node_modules/@fps-games/editor-babylon/dist/editor-world.js +3 -0
  43. package/node_modules/@fps-games/editor-babylon/dist/editor-world.js.map +1 -1
  44. package/node_modules/@fps-games/editor-babylon/dist/index.d.ts +3 -0
  45. package/node_modules/@fps-games/editor-babylon/dist/index.d.ts.map +1 -1
  46. package/node_modules/@fps-games/editor-babylon/dist/index.js +3 -0
  47. package/node_modules/@fps-games/editor-babylon/dist/index.js.map +1 -1
  48. package/node_modules/@fps-games/editor-babylon/dist/marker-overlay.d.ts +52 -0
  49. package/node_modules/@fps-games/editor-babylon/dist/marker-overlay.d.ts.map +1 -0
  50. package/node_modules/@fps-games/editor-babylon/dist/marker-overlay.js +1395 -0
  51. package/node_modules/@fps-games/editor-babylon/dist/marker-overlay.js.map +1 -0
  52. package/node_modules/@fps-games/editor-babylon/dist/material-property-adapter.d.ts.map +1 -1
  53. package/node_modules/@fps-games/editor-babylon/dist/material-property-adapter.js +4 -65
  54. package/node_modules/@fps-games/editor-babylon/dist/material-property-adapter.js.map +1 -1
  55. package/node_modules/@fps-games/editor-babylon/dist/material-slot-source-resolver.d.ts +28 -0
  56. package/node_modules/@fps-games/editor-babylon/dist/material-slot-source-resolver.d.ts.map +1 -0
  57. package/node_modules/@fps-games/editor-babylon/dist/material-slot-source-resolver.js +235 -0
  58. package/node_modules/@fps-games/editor-babylon/dist/material-slot-source-resolver.js.map +1 -0
  59. package/node_modules/@fps-games/editor-babylon/dist/prefab-comparison-preview-controller.d.ts +37 -0
  60. package/node_modules/@fps-games/editor-babylon/dist/prefab-comparison-preview-controller.d.ts.map +1 -0
  61. package/node_modules/@fps-games/editor-babylon/dist/prefab-comparison-preview-controller.js +451 -0
  62. package/node_modules/@fps-games/editor-babylon/dist/prefab-comparison-preview-controller.js.map +1 -0
  63. package/node_modules/@fps-games/editor-babylon/dist/projection-asset-container-importer.d.ts.map +1 -1
  64. package/node_modules/@fps-games/editor-babylon/dist/projection-asset-container-importer.js +24 -10
  65. package/node_modules/@fps-games/editor-babylon/dist/projection-asset-container-importer.js.map +1 -1
  66. package/node_modules/@fps-games/editor-babylon/dist/projection-pick-resolver.d.ts +1 -0
  67. package/node_modules/@fps-games/editor-babylon/dist/projection-pick-resolver.d.ts.map +1 -1
  68. package/node_modules/@fps-games/editor-babylon/dist/projection-pick-resolver.js +20 -4
  69. package/node_modules/@fps-games/editor-babylon/dist/projection-pick-resolver.js.map +1 -1
  70. package/node_modules/@fps-games/editor-babylon/dist/projection-source-owner-path.d.ts +9 -0
  71. package/node_modules/@fps-games/editor-babylon/dist/projection-source-owner-path.d.ts.map +1 -0
  72. package/node_modules/@fps-games/editor-babylon/dist/projection-source-owner-path.js +64 -0
  73. package/node_modules/@fps-games/editor-babylon/dist/projection-source-owner-path.js.map +1 -0
  74. package/node_modules/@fps-games/editor-babylon/dist/projection.d.ts +44 -1
  75. package/node_modules/@fps-games/editor-babylon/dist/projection.d.ts.map +1 -1
  76. package/node_modules/@fps-games/editor-babylon/dist/projection.js +311 -70
  77. package/node_modules/@fps-games/editor-babylon/dist/projection.js.map +1 -1
  78. package/node_modules/@fps-games/editor-babylon/package.json +5 -5
  79. package/node_modules/@fps-games/editor-browser/dist/index.d.ts +5 -2
  80. package/node_modules/@fps-games/editor-browser/dist/index.d.ts.map +1 -1
  81. package/node_modules/@fps-games/editor-browser/dist/index.js +2 -0
  82. package/node_modules/@fps-games/editor-browser/dist/index.js.map +1 -1
  83. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-asset-kind.d.ts +5 -0
  84. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-asset-kind.d.ts.map +1 -0
  85. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-asset-kind.js +23 -0
  86. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-asset-kind.js.map +1 -0
  87. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.d.ts.map +1 -1
  88. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.js +69 -0
  89. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.js.map +1 -1
  90. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-attach-dialog.d.ts.map +1 -1
  91. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-attach-dialog.js +1 -0
  92. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-attach-dialog.js.map +1 -1
  93. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.d.ts +2 -0
  94. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.d.ts.map +1 -1
  95. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.js +38 -1
  96. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.js.map +1 -1
  97. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-tree.d.ts +1 -1
  98. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-tree.d.ts.map +1 -1
  99. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.d.ts.map +1 -1
  100. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.js +8 -2
  101. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.js.map +1 -1
  102. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.d.ts +1 -1
  103. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.d.ts.map +1 -1
  104. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.js +7 -0
  105. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.js.map +1 -1
  106. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panel-registry.js +1 -1
  107. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panel-registry.js.map +1 -1
  108. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.d.ts +4 -0
  109. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.d.ts.map +1 -1
  110. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js +1471 -29
  111. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js.map +1 -1
  112. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-prefab-stage.d.ts +52 -0
  113. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-prefab-stage.d.ts.map +1 -0
  114. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-prefab-stage.js +131 -0
  115. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-prefab-stage.js.map +1 -0
  116. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.d.ts +1 -1
  117. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.d.ts.map +1 -1
  118. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.d.ts.map +1 -1
  119. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.js +9 -2
  120. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.js.map +1 -1
  121. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts +234 -3
  122. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts.map +1 -1
  123. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.d.ts +10 -1
  124. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.d.ts.map +1 -1
  125. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.js +3 -1
  126. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.js.map +1 -1
  127. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts +4 -3
  128. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts.map +1 -1
  129. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js +1274 -55
  130. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js.map +1 -1
  131. package/node_modules/@fps-games/editor-browser/package.json +3 -3
  132. package/node_modules/@fps-games/editor-core/dist/scene-graph.d.ts +1 -1
  133. package/node_modules/@fps-games/editor-core/dist/scene-graph.d.ts.map +1 -1
  134. package/node_modules/@fps-games/editor-core/dist/viewport-tools.d.ts +2 -0
  135. package/node_modules/@fps-games/editor-core/dist/viewport-tools.d.ts.map +1 -1
  136. package/node_modules/@fps-games/editor-core/dist/viewport-tools.js +1 -0
  137. package/node_modules/@fps-games/editor-core/dist/viewport-tools.js.map +1 -1
  138. package/node_modules/@fps-games/editor-core/package.json +2 -2
  139. package/node_modules/@fps-games/editor-forge-play/package.json +2 -2
  140. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/catalog.d.ts.map +1 -1
  141. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/catalog.js +17 -3
  142. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/catalog.js.map +1 -1
  143. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/document.d.ts.map +1 -1
  144. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/document.js +2 -1
  145. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/document.js.map +1 -1
  146. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/types.d.ts +3 -2
  147. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/types.d.ts.map +1 -1
  148. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/document.d.ts +1 -0
  149. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/document.d.ts.map +1 -1
  150. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/document.js +68 -2
  151. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/document.js.map +1 -1
  152. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/index.d.ts +1 -1
  153. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/index.d.ts.map +1 -1
  154. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/index.js +1 -1
  155. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/index.js.map +1 -1
  156. package/node_modules/@fps-games/editor-playable-sdk/dist/document/helpers.d.ts +56 -1
  157. package/node_modules/@fps-games/editor-playable-sdk/dist/document/helpers.d.ts.map +1 -1
  158. package/node_modules/@fps-games/editor-playable-sdk/dist/document/helpers.js +531 -0
  159. package/node_modules/@fps-games/editor-playable-sdk/dist/document/helpers.js.map +1 -1
  160. package/node_modules/@fps-games/editor-playable-sdk/dist/document/types.d.ts +103 -2
  161. package/node_modules/@fps-games/editor-playable-sdk/dist/document/types.d.ts.map +1 -1
  162. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/index.d.ts +1 -1
  163. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/index.d.ts.map +1 -1
  164. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/index.js +1 -1
  165. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/index.js.map +1 -1
  166. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/schema.d.ts.map +1 -1
  167. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/schema.js +9 -1
  168. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/schema.js.map +1 -1
  169. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/validators.d.ts +1 -0
  170. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/validators.d.ts.map +1 -1
  171. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/validators.js +3 -0
  172. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/validators.js.map +1 -1
  173. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/lifecycle.d.ts.map +1 -1
  174. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/lifecycle.js +3 -7
  175. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/lifecycle.js.map +1 -1
  176. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/binding.d.ts.map +1 -1
  177. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/binding.js +6 -0
  178. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/binding.js.map +1 -1
  179. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/types.d.ts +2 -10
  180. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/types.d.ts.map +1 -1
  181. package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/field-patch.d.ts.map +1 -1
  182. package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/field-patch.js +8 -0
  183. package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/field-patch.js.map +1 -1
  184. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/nodes.d.ts.map +1 -1
  185. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/nodes.js +66 -31
  186. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/nodes.js.map +1 -1
  187. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/types.d.ts +2 -1
  188. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/types.d.ts.map +1 -1
  189. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/protocol.d.ts.map +1 -1
  190. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/protocol.js +40 -2
  191. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/protocol.js.map +1 -1
  192. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/descriptors.d.ts +1 -0
  193. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/descriptors.d.ts.map +1 -1
  194. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/descriptors.js +20 -0
  195. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/descriptors.js.map +1 -1
  196. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/inspector.d.ts.map +1 -1
  197. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/inspector.js +68 -27
  198. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/inspector.js.map +1 -1
  199. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/object.d.ts.map +1 -1
  200. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/object.js +7 -4
  201. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/object.js.map +1 -1
  202. package/node_modules/@fps-games/editor-playable-sdk/dist/session/hierarchy.d.ts.map +1 -1
  203. package/node_modules/@fps-games/editor-playable-sdk/dist/session/hierarchy.js +2 -1
  204. package/node_modules/@fps-games/editor-playable-sdk/dist/session/hierarchy.js.map +1 -1
  205. package/node_modules/@fps-games/editor-playable-sdk/package.json +3 -3
  206. package/node_modules/@fps-games/editor-protocol/dist/agent.d.ts +57 -3
  207. package/node_modules/@fps-games/editor-protocol/dist/agent.d.ts.map +1 -1
  208. package/node_modules/@fps-games/editor-protocol/dist/agent.js.map +1 -1
  209. package/node_modules/@fps-games/editor-protocol/dist/index.d.ts +19 -0
  210. package/node_modules/@fps-games/editor-protocol/dist/index.d.ts.map +1 -1
  211. package/node_modules/@fps-games/editor-protocol/dist/index.js +1 -0
  212. package/node_modules/@fps-games/editor-protocol/dist/index.js.map +1 -1
  213. package/node_modules/@fps-games/editor-protocol/dist/marker-graph.d.ts +128 -0
  214. package/node_modules/@fps-games/editor-protocol/dist/marker-graph.d.ts.map +1 -0
  215. package/node_modules/@fps-games/editor-protocol/dist/marker-graph.js +2 -0
  216. package/node_modules/@fps-games/editor-protocol/dist/marker-graph.js.map +1 -0
  217. package/node_modules/@fps-games/editor-protocol/package.json +1 -1
  218. package/package.json +8 -8
@@ -2,6 +2,8 @@ import { cloneEditorTransformMatrix, combineEditorTransforms, createIdentityEdit
2
2
  import { applyBabylonDefaultOutline } from '@fps-games/babylon-renderer';
3
3
  import { applyArtistMaterialProfileToRuntimeMaterial } from './material-property-adapter.js';
4
4
  import { pickBabylonProjectionNodeIdAt, resolveBabylonPickSelectionNodeId, resolveBabylonProjectionNodeId, } from './projection-pick-resolver.js';
5
+ import { normalizeProjectionOwnerNodePath, readProjectionSourceNodeSegment, } from './projection-source-owner-path.js';
6
+ import { createMaterialSlotOwnerPathMatchKey, isMaterialSlotOwnerPathMatch, } from './material-slot-source-resolver.js';
5
7
  const LARGE_SELECTION_EDGE_LIMIT = 200;
6
8
  const PROJECTION_LOCAL_MATRIX_METADATA_KEY = 'editorLocalTransformMatrix';
7
9
  const ASSET_MESH_SELECTION_SEPARATOR = '::assetMesh::';
@@ -123,6 +125,7 @@ export function createBabylonEditorProjection(options) {
123
125
  const disposeProjectedNode = (projection) => {
124
126
  projection.selectionEdgesPrewarmCancel?.();
125
127
  projection.selectionEdgesPrewarmCancel = null;
128
+ projection.importStructure = null;
126
129
  for (const animationGroup of projection.animationGroups) {
127
130
  animationGroup.dispose?.();
128
131
  }
@@ -261,6 +264,9 @@ export function createBabylonEditorProjection(options) {
261
264
  }
262
265
  return transforms;
263
266
  },
267
+ readNodeImportStructure(nodeId) {
268
+ return cloneProjectionImportStructureSnapshot(projections.get(nodeId)?.importStructure ?? null);
269
+ },
264
270
  setNodeTransformPreview(nodeId, transform) {
265
271
  const projection = projections.get(nodeId);
266
272
  if (!projection?.root)
@@ -557,9 +563,9 @@ function attachImportedProjectionResult(options, node, asset, projection, result
557
563
  for (const mesh of result.meshes ?? []) {
558
564
  mesh.isPickable = true;
559
565
  applyProjectionRenderingToMesh(node, mesh);
560
- const ownerNodePath = buildProjectionOwnerNodePath(mesh, modelRoot);
566
+ const ownerNodePath = buildProjectionOwnerNodePath(mesh, modelRoot, node.id);
561
567
  if (ownerNodePath) {
562
- const selectionKey = resolveProjectionAssetMeshSelectionKey(asset, ownerNodePath);
568
+ const selectionKey = resolveProjectionAssetMeshSelectionKey(asset, ownerNodePath, node.id);
563
569
  mesh.metadata = {
564
570
  ...(mesh.metadata ?? {}),
565
571
  editorProjection: {
@@ -580,12 +586,215 @@ function attachImportedProjectionResult(options, node, asset, projection, result
580
586
  }
581
587
  projection.animationGroups = result.animationGroups ?? [];
582
588
  projection.outlineMeshes = (result.meshes ?? []).filter((mesh) => mesh.getTotalVertices?.() > 0);
589
+ projection.importStructure = createProjectionImportStructureSnapshot(node, asset, modelRoot, result);
583
590
  }
584
591
  catch (error) {
585
592
  disposeImportedProjectionResult(result);
586
593
  throw error;
587
594
  }
588
595
  }
596
+ function createProjectionImportStructureSnapshot(node, asset, modelRoot, result) {
597
+ const runtimeNodes = collectProjectionImportRuntimeNodes(modelRoot, result);
598
+ const nodeIdByRuntimeNode = new Map();
599
+ const materialIdByRuntimeMaterial = new Map();
600
+ const textureIdByRuntimeTexture = new Map();
601
+ const materialNodeIds = new Map();
602
+ const materialTextureIds = new Map();
603
+ const rootSnapshotId = 'root';
604
+ const snapshots = [{
605
+ id: rootSnapshotId,
606
+ parentId: null,
607
+ name: readProjectionRuntimeName(modelRoot, node.name ?? node.id),
608
+ kind: 'root',
609
+ }];
610
+ for (const runtimeNode of runtimeNodes) {
611
+ const snapshotId = createProjectionImportRuntimeNodeId(runtimeNode, nodeIdByRuntimeNode.size);
612
+ nodeIdByRuntimeNode.set(runtimeNode, snapshotId);
613
+ }
614
+ for (const runtimeNode of runtimeNodes) {
615
+ const snapshotId = nodeIdByRuntimeNode.get(runtimeNode);
616
+ if (!snapshotId)
617
+ continue;
618
+ const materialIds = readProjectionRuntimeNodeMaterials(runtimeNode)
619
+ .map(runtimeMaterial => getProjectionImportMaterialSnapshotId(runtimeMaterial, materialIdByRuntimeMaterial))
620
+ .filter((materialId) => !!materialId);
621
+ for (const materialId of materialIds) {
622
+ const nodeIds = materialNodeIds.get(materialId) ?? new Set();
623
+ nodeIds.add(snapshotId);
624
+ materialNodeIds.set(materialId, nodeIds);
625
+ const textureIds = readProjectionRuntimeMaterialTextures(readProjectionRuntimeNodeMaterialById(runtimeNode, runtimeNode, materialId, materialIdByRuntimeMaterial))
626
+ .map(runtimeTexture => getProjectionImportTextureSnapshotId(runtimeTexture, textureIdByRuntimeTexture))
627
+ .filter((textureId) => !!textureId);
628
+ const materialTextures = materialTextureIds.get(materialId) ?? new Set();
629
+ for (const textureId of textureIds)
630
+ materialTextures.add(textureId);
631
+ materialTextureIds.set(materialId, materialTextures);
632
+ }
633
+ snapshots.push({
634
+ id: snapshotId,
635
+ parentId: resolveProjectionImportRuntimeParentId(runtimeNode, modelRoot, nodeIdByRuntimeNode, rootSnapshotId),
636
+ name: readProjectionRuntimeName(runtimeNode, snapshotId),
637
+ kind: isProjectionRuntimeMeshLike(runtimeNode) ? 'mesh' : 'transform',
638
+ ...readProjectionRuntimeSourceName(runtimeNode),
639
+ ...readProjectionRuntimeOwnerNodePath(runtimeNode),
640
+ ...(materialIds.length > 0 ? { materialIds } : {}),
641
+ });
642
+ }
643
+ const materials = [...materialIdByRuntimeMaterial.entries()].map(([runtimeMaterial, id]) => ({
644
+ id,
645
+ name: readProjectionRuntimeName(runtimeMaterial, id),
646
+ ...readProjectionRuntimeKind(runtimeMaterial),
647
+ nodeIds: [...(materialNodeIds.get(id) ?? [])],
648
+ textureIds: [...(materialTextureIds.get(id) ?? [])],
649
+ }));
650
+ const textures = [...textureIdByRuntimeTexture.entries()].map(([runtimeTexture, id]) => ({
651
+ id,
652
+ name: readProjectionRuntimeName(runtimeTexture, id),
653
+ ...readProjectionRuntimeKind(runtimeTexture),
654
+ ...readProjectionRuntimeTextureUrl(runtimeTexture),
655
+ }));
656
+ const animations = createProjectionImportAnimationSnapshots(result.animationGroups ?? []);
657
+ return {
658
+ projectionNodeId: node.id,
659
+ ...(asset.id ? { assetId: asset.id } : {}),
660
+ ...(asset.sourceId ? { sourceId: asset.sourceId } : {}),
661
+ nodes: snapshots,
662
+ materials,
663
+ textures,
664
+ animations,
665
+ };
666
+ }
667
+ function collectProjectionImportRuntimeNodes(modelRoot, result) {
668
+ const known = new Set([
669
+ ...(result.transformNodes ?? []),
670
+ ...(result.meshes ?? []),
671
+ ].filter(Boolean));
672
+ const ordered = [];
673
+ const visit = (runtimeNode) => {
674
+ for (const child of readProjectionRuntimeChildren(runtimeNode)) {
675
+ if (!known.has(child) || ordered.includes(child))
676
+ continue;
677
+ ordered.push(child);
678
+ visit(child);
679
+ }
680
+ };
681
+ visit(modelRoot);
682
+ for (const runtimeNode of known) {
683
+ if (!ordered.includes(runtimeNode))
684
+ ordered.push(runtimeNode);
685
+ }
686
+ return ordered;
687
+ }
688
+ function readProjectionRuntimeChildren(runtimeNode) {
689
+ const children = runtimeNode?.getChildren?.();
690
+ return Array.isArray(children) ? children : [];
691
+ }
692
+ function createProjectionImportRuntimeNodeId(runtimeNode, index) {
693
+ const sourceName = readProjectionSourceNodeSegment(runtimeNode);
694
+ const rawName = sourceName || runtimeNode?.name || runtimeNode?.id || `node-${index + 1}`;
695
+ return `${isProjectionRuntimeMeshLike(runtimeNode) ? 'mesh' : 'node'}:${sanitizeProjectionImportSnapshotId(rawName)}:${index + 1}`;
696
+ }
697
+ function resolveProjectionImportRuntimeParentId(runtimeNode, modelRoot, nodeIdByRuntimeNode, rootSnapshotId) {
698
+ const parent = runtimeNode?.parent ?? null;
699
+ if (!parent || parent === modelRoot)
700
+ return rootSnapshotId;
701
+ return nodeIdByRuntimeNode.get(parent) ?? rootSnapshotId;
702
+ }
703
+ function isProjectionRuntimeMeshLike(runtimeNode) {
704
+ return typeof runtimeNode?.getTotalVertices === 'function';
705
+ }
706
+ function readProjectionRuntimeNodeMaterials(runtimeNode) {
707
+ const material = runtimeNode?.material;
708
+ if (!material)
709
+ return [];
710
+ const subMaterials = material.subMaterials;
711
+ if (Array.isArray(subMaterials) && subMaterials.length > 0) {
712
+ return subMaterials.filter(Boolean);
713
+ }
714
+ return [material];
715
+ }
716
+ function readProjectionRuntimeNodeMaterialById(runtimeNode, _fallbackNode, materialId, materialIdByRuntimeMaterial) {
717
+ for (const material of readProjectionRuntimeNodeMaterials(runtimeNode)) {
718
+ if (materialIdByRuntimeMaterial.get(material) === materialId)
719
+ return material;
720
+ }
721
+ return null;
722
+ }
723
+ function getProjectionImportMaterialSnapshotId(runtimeMaterial, materialIdByRuntimeMaterial) {
724
+ if (!runtimeMaterial)
725
+ return null;
726
+ const existing = materialIdByRuntimeMaterial.get(runtimeMaterial);
727
+ if (existing)
728
+ return existing;
729
+ const id = `material:${sanitizeProjectionImportSnapshotId(readProjectionRuntimeName(runtimeMaterial, 'material'))}:${materialIdByRuntimeMaterial.size + 1}`;
730
+ materialIdByRuntimeMaterial.set(runtimeMaterial, id);
731
+ return id;
732
+ }
733
+ function readProjectionRuntimeMaterialTextures(runtimeMaterial) {
734
+ if (!runtimeMaterial)
735
+ return [];
736
+ const textures = runtimeMaterial.getActiveTextures?.();
737
+ return Array.isArray(textures) ? textures.filter(Boolean) : [];
738
+ }
739
+ function getProjectionImportTextureSnapshotId(runtimeTexture, textureIdByRuntimeTexture) {
740
+ if (!runtimeTexture)
741
+ return null;
742
+ const existing = textureIdByRuntimeTexture.get(runtimeTexture);
743
+ if (existing)
744
+ return existing;
745
+ const name = readProjectionRuntimeTextureUrl(runtimeTexture).url
746
+ ?? readProjectionRuntimeName(runtimeTexture, 'texture');
747
+ const id = `texture:${sanitizeProjectionImportSnapshotId(name)}:${textureIdByRuntimeTexture.size + 1}`;
748
+ textureIdByRuntimeTexture.set(runtimeTexture, id);
749
+ return id;
750
+ }
751
+ function createProjectionImportAnimationSnapshots(animationGroups) {
752
+ return animationGroups.filter(Boolean).map((animationGroup, index) => ({
753
+ id: `animation:${sanitizeProjectionImportSnapshotId(readProjectionRuntimeName(animationGroup, 'animation'))}:${index + 1}`,
754
+ name: readProjectionRuntimeName(animationGroup, `Animation ${index + 1}`),
755
+ }));
756
+ }
757
+ function readProjectionRuntimeName(runtimeObject, fallback) {
758
+ const name = runtimeObject?.name ?? runtimeObject?.id;
759
+ return typeof name === 'string' && name.trim() ? name.trim() : fallback;
760
+ }
761
+ function readProjectionRuntimeKind(runtimeObject) {
762
+ const kind = runtimeObject?.getClassName?.();
763
+ return typeof kind === 'string' && kind.trim() ? { kind: kind.trim() } : {};
764
+ }
765
+ function readProjectionRuntimeTextureUrl(runtimeTexture) {
766
+ const url = runtimeTexture?.url ?? runtimeTexture?.name;
767
+ return typeof url === 'string' && url.trim() ? { url: url.trim() } : {};
768
+ }
769
+ function readProjectionRuntimeSourceName(runtimeNode) {
770
+ const sourceName = readProjectionSourceNodeSegment(runtimeNode);
771
+ return sourceName ? { sourceName } : {};
772
+ }
773
+ function readProjectionRuntimeOwnerNodePath(runtimeNode) {
774
+ const ownerNodePath = runtimeNode?.metadata?.editorProjection?.ownerNodePath;
775
+ return typeof ownerNodePath === 'string' && ownerNodePath.trim() ? { ownerNodePath: ownerNodePath.trim() } : {};
776
+ }
777
+ function sanitizeProjectionImportSnapshotId(value) {
778
+ return value.trim().toLowerCase().replace(/[^a-z0-9_.:-]+/gi, '-').replace(/^-+|-+$/g, '') || 'item';
779
+ }
780
+ function cloneProjectionImportStructureSnapshot(snapshot) {
781
+ if (!snapshot)
782
+ return null;
783
+ return {
784
+ ...snapshot,
785
+ nodes: snapshot.nodes.map(item => ({
786
+ ...item,
787
+ ...(item.materialIds ? { materialIds: [...item.materialIds] } : {}),
788
+ })),
789
+ materials: snapshot.materials.map(item => ({
790
+ ...item,
791
+ nodeIds: [...item.nodeIds],
792
+ textureIds: [...item.textureIds],
793
+ })),
794
+ textures: snapshot.textures.map(item => ({ ...item })),
795
+ animations: snapshot.animations.map(item => ({ ...item })),
796
+ };
797
+ }
589
798
  function disposeImportedProjectionResult(result) {
590
799
  for (const animationGroup of result.animationGroups ?? []) {
591
800
  animationGroup.dispose?.();
@@ -654,6 +863,8 @@ function attachDirectionalLightProjection(babylon, scene, node, projection) {
654
863
  light.setEnabled?.(node.active !== false);
655
864
  projection.runtimeObjects.push(light);
656
865
  }
866
+ if (!settings.helperVisible)
867
+ return;
657
868
  const helper = MeshBuilder.CreateBox(`${node.id}.lightHelper`, {
658
869
  size: 0.28,
659
870
  }, scene);
@@ -759,9 +970,9 @@ function createPrimitiveProjectionMesh(babylon, scene, node, options) {
759
970
  return mesh;
760
971
  }
761
972
  function applyProjectionArtistMaterialProfilesToMeshes(options, node, modelRoot, meshes) {
762
- const slotProfiles = node.artistMaterialSlotProfiles ?? {};
973
+ const slotBindings = createProjectionMaterialSlotBindings(node);
763
974
  const hasRootProfile = !!node.artistMaterialProfile;
764
- const hasSlotProfiles = Object.keys(slotProfiles).length > 0;
975
+ const hasSlotProfiles = slotBindings.length > 0;
765
976
  if (!hasRootProfile && !hasSlotProfiles)
766
977
  return [];
767
978
  const clonedMaterials = [];
@@ -783,16 +994,16 @@ function applyProjectionArtistMaterialProfilesToMeshes(options, node, modelRoot,
783
994
  const material = mesh?.material;
784
995
  if (!material)
785
996
  continue;
786
- const ownerNodePath = buildProjectionOwnerNodePath(mesh, modelRoot);
787
- const slotProfile = resolveProjectionSlotProfile(slotProfiles, ownerNodePath);
788
- if (!slotProfile)
997
+ const ownerNodePath = buildProjectionOwnerNodePath(mesh, modelRoot, node.id);
998
+ const slotBinding = resolveProjectionSlotBinding(slotBindings, ownerNodePath, node.id);
999
+ if (!slotBinding)
789
1000
  continue;
790
1001
  const slotMaterial = detachProjectionSlotMaterial(mesh, materialCounts, ownerNodePath);
791
1002
  if (!slotMaterial)
792
1003
  continue;
793
1004
  if (slotMaterial !== material)
794
1005
  clonedMaterials.push(slotMaterial);
795
- applyProjectionArtistMaterialProfile(options, slotMaterial, slotProfile);
1006
+ applyProjectionArtistMaterialProfile(options, slotMaterial, slotBinding.profile);
796
1007
  }
797
1008
  return clonedMaterials;
798
1009
  }
@@ -804,29 +1015,87 @@ function applyProjectionArtistMaterialProfile(options, material, profile) {
804
1015
  resolveTextureUrl: options.resolveTextureUrl,
805
1016
  });
806
1017
  }
807
- function resolveProjectionSlotProfile(slotProfiles, ownerNodePath) {
1018
+ function createProjectionMaterialSlotBindings(node) {
1019
+ const bindings = [];
1020
+ const occupiedOwnerPaths = new Set();
1021
+ for (const binding of node.artistMaterialSlotBindings ?? []) {
1022
+ const profile = binding?.profile;
1023
+ const slot = createProjectionMaterialSlotDescriptor(binding?.slot, node.id);
1024
+ if (!profile || !slot)
1025
+ continue;
1026
+ bindings.push({ slot, profile });
1027
+ occupiedOwnerPaths.add(slot.matchKey);
1028
+ }
1029
+ for (const [ownerNodePath, profile] of Object.entries(node.artistMaterialSlotProfiles ?? {})) {
1030
+ const slot = createProjectionMaterialSlotDescriptor({ ownerNodePath }, node.id);
1031
+ if (!profile || !slot || occupiedOwnerPaths.has(slot.matchKey))
1032
+ continue;
1033
+ bindings.push({ slot, profile });
1034
+ occupiedOwnerPaths.add(slot.matchKey);
1035
+ }
1036
+ return bindings;
1037
+ }
1038
+ function createProjectionMaterialSlotDescriptor(value, projectionNodeId) {
1039
+ if (!value || typeof value !== 'object')
1040
+ return null;
1041
+ const ownerNodePath = typeof value.ownerNodePath === 'string'
1042
+ ? normalizeProjectionOwnerNodePath(value.ownerNodePath, { projectionNodeId })
1043
+ : '';
1044
+ if (!ownerNodePath)
1045
+ return null;
1046
+ const slotId = typeof value.slotId === 'string' && value.slotId.trim() ? value.slotId.trim() : '';
1047
+ const label = typeof value.label === 'string' && value.label.trim() ? value.label.trim() : '';
1048
+ const nodeIndex = Number.isInteger(value.nodeIndex) ? value.nodeIndex : undefined;
1049
+ const nodeIndexPath = Array.isArray(value.nodeIndexPath)
1050
+ ? value.nodeIndexPath.filter((item) => Number.isInteger(item))
1051
+ : undefined;
1052
+ const meshIndex = Number.isInteger(value.meshIndex) ? value.meshIndex : undefined;
1053
+ const primitiveIndex = Number.isInteger(value.primitiveIndex) ? value.primitiveIndex : undefined;
1054
+ const sourceMaterialIndex = Number.isInteger(value.sourceMaterialIndex) ? value.sourceMaterialIndex : undefined;
1055
+ const sourceMaterialIndices = Array.isArray(value.sourceMaterialIndices)
1056
+ ? value.sourceMaterialIndices.filter((item) => Number.isInteger(item))
1057
+ : undefined;
1058
+ const materialName = typeof value.materialName === 'string' && value.materialName.trim() ? value.materialName.trim() : '';
1059
+ const materialNames = Array.isArray(value.materialNames)
1060
+ ? value.materialNames.filter((item) => typeof item === 'string' && !!item.trim()).map(item => item.trim())
1061
+ : undefined;
1062
+ return {
1063
+ ...(slotId ? { slotId } : {}),
1064
+ ownerNodePath,
1065
+ ...(label ? { label } : {}),
1066
+ ...(nodeIndex != null ? { nodeIndex } : {}),
1067
+ ...(nodeIndexPath && nodeIndexPath.length > 0 ? { nodeIndexPath } : {}),
1068
+ ...(meshIndex != null ? { meshIndex } : {}),
1069
+ ...(primitiveIndex != null ? { primitiveIndex } : {}),
1070
+ ...(sourceMaterialIndex != null ? { sourceMaterialIndex } : {}),
1071
+ ...(sourceMaterialIndices && sourceMaterialIndices.length > 0
1072
+ ? { sourceMaterialIndices }
1073
+ : {}),
1074
+ ...(materialName ? { materialName } : {}),
1075
+ ...(materialNames && materialNames.length > 0 ? { materialNames } : {}),
1076
+ key: slotId || ownerNodePath,
1077
+ normalizedOwnerNodePath: ownerNodePath,
1078
+ matchKey: createMaterialSlotOwnerPathMatchKey(ownerNodePath, { projectionNodeId }),
1079
+ };
1080
+ }
1081
+ function resolveProjectionSlotBinding(slotBindings, ownerNodePath, projectionNodeId) {
808
1082
  if (ownerNodePath == null)
809
1083
  return null;
810
- const direct = slotProfiles[ownerNodePath];
811
- if (direct)
812
- return direct;
813
- const normalized = normalizeProjectionOwnerNodePath(ownerNodePath);
814
- if (normalized !== ownerNodePath && slotProfiles[normalized])
815
- return slotProfiles[normalized];
1084
+ const normalized = normalizeProjectionOwnerNodePath(ownerNodePath, { projectionNodeId });
1085
+ const normalizedMatchKey = createMaterialSlotOwnerPathMatchKey(normalized, { projectionNodeId });
816
1086
  let descendantMatch = null;
817
- for (const [candidatePath, profile] of Object.entries(slotProfiles)) {
818
- const normalizedCandidate = normalizeProjectionOwnerNodePath(candidatePath);
819
- if (normalizedCandidate === normalized)
820
- return profile;
821
- if (isProjectionOwnerPathDescendant(normalized, normalizedCandidate)) {
822
- if (!descendantMatch || normalizedCandidate.length > descendantMatch.path.length) {
823
- descendantMatch = { path: normalizedCandidate, profile };
1087
+ for (const binding of slotBindings) {
1088
+ if (binding.slot.matchKey === normalizedMatchKey)
1089
+ return binding;
1090
+ if (isMaterialSlotOwnerPathMatch(normalized, binding.slot.normalizedOwnerNodePath, { projectionNodeId })) {
1091
+ if (!descendantMatch || binding.slot.matchKey.length > descendantMatch.slot.matchKey.length) {
1092
+ descendantMatch = binding;
824
1093
  }
825
1094
  }
826
1095
  }
827
- return descendantMatch?.profile ?? null;
1096
+ return descendantMatch;
828
1097
  }
829
- function buildProjectionOwnerNodePath(node, rootNode) {
1098
+ function buildProjectionOwnerNodePath(node, rootNode, projectionNodeId) {
830
1099
  if (!node || !rootNode || node === rootNode)
831
1100
  return '';
832
1101
  const segments = [];
@@ -840,9 +1109,12 @@ function buildProjectionOwnerNodePath(node, rootNode) {
840
1109
  }
841
1110
  if (current !== rootNode)
842
1111
  return null;
843
- return segments.reverse().join('/');
1112
+ return normalizeProjectionOwnerNodePath(segments.reverse().join('/'), { projectionNodeId });
844
1113
  }
845
1114
  function stableProjectionNodeSegment(node) {
1115
+ const sourceSegment = readProjectionSourceNodeSegment(node);
1116
+ if (sourceSegment)
1117
+ return sourceSegment;
846
1118
  const candidates = [node?.name, node?.id];
847
1119
  for (const candidate of candidates) {
848
1120
  if (typeof candidate !== 'string')
@@ -853,59 +1125,36 @@ function stableProjectionNodeSegment(node) {
853
1125
  }
854
1126
  return null;
855
1127
  }
856
- function normalizeProjectionOwnerNodePath(path) {
857
- const segments = path
858
- .split('/')
859
- .map(normalizeProjectionOwnerPathSegment)
860
- .filter(Boolean);
861
- while (segments.length > 0 && isSyntheticProjectionOwnerRootSegment(segments[0])) {
862
- segments.shift();
863
- }
864
- return segments.join('/');
865
- }
866
- function normalizeProjectionOwnerPathSegment(segment) {
867
- const trimmed = segment.trim();
868
- return trimmed.includes(':') ? trimmed.slice(trimmed.lastIndexOf(':') + 1) : trimmed;
869
- }
870
- function isSyntheticProjectionOwnerRootSegment(segment) {
871
- return segment === '__root__';
872
- }
873
- function isProjectionOwnerPathDescendant(ownerNodePath, candidateAncestorPath) {
874
- if (!ownerNodePath || !candidateAncestorPath)
875
- return false;
876
- return ownerNodePath.startsWith(`${candidateAncestorPath}/`);
877
- }
878
1128
  function createAssetMeshProjectionNodeId(rootNodeId, ownerNodePath) {
879
1129
  return `${rootNodeId}${ASSET_MESH_SELECTION_SEPARATOR}${encodeURIComponent(ownerNodePath)}`;
880
1130
  }
881
- function resolveProjectionAssetMeshSelectionKey(asset, ownerNodePath) {
882
- const normalizedOwnerPath = normalizeProjectionOwnerNodePath(ownerNodePath);
1131
+ function resolveProjectionAssetMeshSelectionKey(asset, ownerNodePath, projectionNodeId) {
1132
+ const normalizedOwnerPath = normalizeProjectionOwnerNodePath(ownerNodePath, { projectionNodeId });
883
1133
  let bestSlot = null;
884
- for (const slot of readProjectionAssetMaterialSlots(asset)) {
885
- if (!isProjectionOwnerPathSlotMatch(normalizedOwnerPath, slot.path))
1134
+ for (const slot of readProjectionAssetMaterialSlots(asset, projectionNodeId)) {
1135
+ if (!isMaterialSlotOwnerPathMatch(normalizedOwnerPath, slot.normalizedOwnerNodePath, { projectionNodeId }))
886
1136
  continue;
887
- if (!bestSlot || slot.path.length > bestSlot.path.length) {
1137
+ if (!bestSlot || slot.matchKey.length > bestSlot.matchKey.length) {
888
1138
  bestSlot = slot;
889
1139
  }
890
1140
  }
891
1141
  return bestSlot?.key ?? ownerNodePath;
892
1142
  }
893
- function readProjectionAssetMaterialSlots(asset) {
1143
+ function readProjectionAssetMaterialSlots(asset, projectionNodeId) {
894
1144
  const rawSlots = asset.metadata?.materialSlots;
895
1145
  if (!Array.isArray(rawSlots))
896
1146
  return [];
897
1147
  const slots = [];
898
1148
  for (const rawSlot of rawSlots) {
899
- const slot = readProjectionAssetMaterialSlot(rawSlot);
1149
+ const slot = readProjectionAssetMaterialSlot(rawSlot, projectionNodeId);
900
1150
  if (slot)
901
1151
  slots.push(slot);
902
1152
  }
903
1153
  return slots;
904
1154
  }
905
- function readProjectionAssetMaterialSlot(value) {
1155
+ function readProjectionAssetMaterialSlot(value, projectionNodeId) {
906
1156
  if (typeof value === 'string') {
907
- const path = normalizeProjectionOwnerNodePath(value);
908
- return path ? { key: path, path } : null;
1157
+ return createProjectionMaterialSlotDescriptor({ ownerNodePath: value }, projectionNodeId);
909
1158
  }
910
1159
  if (!value || typeof value !== 'object' || Array.isArray(value))
911
1160
  return null;
@@ -913,23 +1162,11 @@ function readProjectionAssetMaterialSlot(value) {
913
1162
  const rawOwnerPath = readProjectionAssetMaterialSlotString(record.ownerNodePath)
914
1163
  || readProjectionAssetMaterialSlotString(record.path)
915
1164
  || readProjectionAssetMaterialSlotString(record.name);
916
- const path = normalizeProjectionOwnerNodePath(rawOwnerPath);
917
- if (!path)
918
- return null;
919
- const slotId = readProjectionAssetMaterialSlotString(record.slotId);
920
- return {
921
- key: slotId || path,
922
- path,
923
- };
1165
+ return createProjectionMaterialSlotDescriptor({ ...record, ownerNodePath: rawOwnerPath }, projectionNodeId);
924
1166
  }
925
1167
  function readProjectionAssetMaterialSlotString(value) {
926
1168
  return typeof value === 'string' ? value.trim() : '';
927
1169
  }
928
- function isProjectionOwnerPathSlotMatch(ownerNodePath, slotOwnerPath) {
929
- if (!ownerNodePath || !slotOwnerPath)
930
- return false;
931
- return ownerNodePath === slotOwnerPath || isProjectionOwnerPathDescendant(ownerNodePath, slotOwnerPath);
932
- }
933
1170
  function countProjectionMeshMaterials(meshes) {
934
1171
  const counts = new Map();
935
1172
  for (const mesh of meshes) {
@@ -1125,6 +1362,8 @@ function attachHemisphericLightProjection(babylon, scene, node, projection) {
1125
1362
  light.setEnabled?.(node.active !== false);
1126
1363
  projection.runtimeObjects.push(light);
1127
1364
  }
1365
+ if (!settings.helperVisible)
1366
+ return;
1128
1367
  const helper = MeshBuilder.CreateSphere?.(`${node.id}.hemisphericLightHelper`, {
1129
1368
  diameter: 0.32,
1130
1369
  segments: 18,
@@ -1271,6 +1510,7 @@ function readProjectionDirectionalLightSettings(settings) {
1271
1510
  intensity: Math.max(0, readFiniteNumber(settings?.intensity, 2)),
1272
1511
  direction: readVector3(settings?.direction, { x: -0.3, y: -1, z: -0.2 }),
1273
1512
  diffuseColor: readProjectionColor(settings?.diffuseColor, { r: 1, g: 1, b: 1 }),
1513
+ helperVisible: settings?.helperVisible !== false,
1274
1514
  };
1275
1515
  }
1276
1516
  function readProjectionHemisphericLightSettings(settings) {
@@ -1279,6 +1519,7 @@ function readProjectionHemisphericLightSettings(settings) {
1279
1519
  intensity: Math.max(0, readFiniteNumber(settings?.intensity, 0.8)),
1280
1520
  diffuseColor: readProjectionColor(settings?.diffuseColor, { r: 1, g: 1, b: 1 }),
1281
1521
  groundColor: readProjectionColor(settings?.groundColor, { r: 0.48, g: 0.52, b: 0.62 }),
1522
+ helperVisible: settings?.helperVisible !== false,
1282
1523
  };
1283
1524
  }
1284
1525
  function readProjectionColor(value, fallback) {