@gnsx/three 0.184.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1182) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +85 -0
  3. package/build/three.cjs +79695 -0
  4. package/build/three.core.js +59732 -0
  5. package/build/three.core.min.js +6 -0
  6. package/build/three.module.js +19552 -0
  7. package/build/three.module.min.js +6 -0
  8. package/build/three.tsl.js +654 -0
  9. package/build/three.tsl.min.js +6 -0
  10. package/build/three.webgpu.js +83969 -0
  11. package/build/three.webgpu.min.js +6 -0
  12. package/build/three.webgpu.nodes.js +83702 -0
  13. package/build/three.webgpu.nodes.min.js +6 -0
  14. package/examples/fonts/LICENSE +13 -0
  15. package/examples/fonts/MPLUSRounded1c/MPLUSRounded1c-Regular.typeface.json.zip +0 -0
  16. package/examples/fonts/MPLUSRounded1c/OFL.txt +91 -0
  17. package/examples/fonts/README.md +11 -0
  18. package/examples/fonts/droid/NOTICE +190 -0
  19. package/examples/fonts/droid/README.txt +18 -0
  20. package/examples/fonts/droid/droid_sans_bold.typeface.json +1 -0
  21. package/examples/fonts/droid/droid_sans_mono_regular.typeface.json +1 -0
  22. package/examples/fonts/droid/droid_sans_regular.typeface.json +1 -0
  23. package/examples/fonts/droid/droid_serif_bold.typeface.json +1 -0
  24. package/examples/fonts/droid/droid_serif_regular.typeface.json +1 -0
  25. package/examples/fonts/gentilis_bold.typeface.json +1 -0
  26. package/examples/fonts/gentilis_regular.typeface.json +1 -0
  27. package/examples/fonts/helvetiker_bold.typeface.json +1 -0
  28. package/examples/fonts/helvetiker_regular.typeface.json +1 -0
  29. package/examples/fonts/optimer_bold.typeface.json +1 -0
  30. package/examples/fonts/optimer_regular.typeface.json +1 -0
  31. package/examples/fonts/ttf/README.md +9 -0
  32. package/examples/fonts/ttf/kenpixel.ttf +0 -0
  33. package/examples/jsm/Addons.js +293 -0
  34. package/examples/jsm/animation/AnimationClipCreator.js +168 -0
  35. package/examples/jsm/animation/CCDIKSolver.js +595 -0
  36. package/examples/jsm/capabilities/WebGL.js +113 -0
  37. package/examples/jsm/capabilities/WebGPU.js +59 -0
  38. package/examples/jsm/controls/ArcballControls.js +3539 -0
  39. package/examples/jsm/controls/DragControls.js +452 -0
  40. package/examples/jsm/controls/FirstPersonControls.js +451 -0
  41. package/examples/jsm/controls/FlyControls.js +384 -0
  42. package/examples/jsm/controls/MapControls.js +116 -0
  43. package/examples/jsm/controls/OrbitControls.js +1963 -0
  44. package/examples/jsm/controls/PointerLockControls.js +264 -0
  45. package/examples/jsm/controls/TrackballControls.js +1001 -0
  46. package/examples/jsm/controls/TransformControls.js +1950 -0
  47. package/examples/jsm/csm/CSM.js +598 -0
  48. package/examples/jsm/csm/CSMFrustum.js +209 -0
  49. package/examples/jsm/csm/CSMHelper.js +243 -0
  50. package/examples/jsm/csm/CSMShader.js +307 -0
  51. package/examples/jsm/csm/CSMShadowNode.js +603 -0
  52. package/examples/jsm/curves/CurveExtras.js +694 -0
  53. package/examples/jsm/curves/NURBSCurve.js +155 -0
  54. package/examples/jsm/curves/NURBSSurface.js +98 -0
  55. package/examples/jsm/curves/NURBSUtils.js +532 -0
  56. package/examples/jsm/curves/NURBSVolume.js +82 -0
  57. package/examples/jsm/effects/AnaglyphEffect.js +274 -0
  58. package/examples/jsm/effects/AsciiEffect.js +310 -0
  59. package/examples/jsm/effects/OutlineEffect.js +489 -0
  60. package/examples/jsm/effects/ParallaxBarrierEffect.js +155 -0
  61. package/examples/jsm/effects/StereoEffect.js +91 -0
  62. package/examples/jsm/environments/ColorEnvironment.js +59 -0
  63. package/examples/jsm/environments/DebugEnvironment.js +102 -0
  64. package/examples/jsm/environments/RoomEnvironment.js +185 -0
  65. package/examples/jsm/exporters/DRACOExporter.js +311 -0
  66. package/examples/jsm/exporters/EXRExporter.js +618 -0
  67. package/examples/jsm/exporters/GLTFExporter.js +3738 -0
  68. package/examples/jsm/exporters/KTX2Exporter.js +347 -0
  69. package/examples/jsm/exporters/OBJExporter.js +308 -0
  70. package/examples/jsm/exporters/PLYExporter.js +562 -0
  71. package/examples/jsm/exporters/STLExporter.js +221 -0
  72. package/examples/jsm/exporters/USDZExporter.js +1254 -0
  73. package/examples/jsm/geometries/BoxLineGeometry.js +92 -0
  74. package/examples/jsm/geometries/ConvexGeometry.js +72 -0
  75. package/examples/jsm/geometries/DecalGeometry.js +420 -0
  76. package/examples/jsm/geometries/ParametricFunctions.js +100 -0
  77. package/examples/jsm/geometries/ParametricGeometry.js +172 -0
  78. package/examples/jsm/geometries/RoundedBoxGeometry.js +216 -0
  79. package/examples/jsm/geometries/TeapotGeometry.js +689 -0
  80. package/examples/jsm/geometries/TextGeometry.js +102 -0
  81. package/examples/jsm/gpgpu/BitonicSort.js +715 -0
  82. package/examples/jsm/helpers/AnimationPathHelper.js +302 -0
  83. package/examples/jsm/helpers/LightProbeGridHelper.js +221 -0
  84. package/examples/jsm/helpers/LightProbeHelper.js +165 -0
  85. package/examples/jsm/helpers/LightProbeHelperGPU.js +102 -0
  86. package/examples/jsm/helpers/OctreeHelper.js +109 -0
  87. package/examples/jsm/helpers/PositionalAudioHelper.js +169 -0
  88. package/examples/jsm/helpers/RapierHelper.js +59 -0
  89. package/examples/jsm/helpers/RectAreaLightHelper.js +118 -0
  90. package/examples/jsm/helpers/TextureHelper.js +265 -0
  91. package/examples/jsm/helpers/TextureHelperGPU.js +214 -0
  92. package/examples/jsm/helpers/VertexNormalsHelper.js +155 -0
  93. package/examples/jsm/helpers/VertexTangentsHelper.js +133 -0
  94. package/examples/jsm/helpers/ViewHelper.js +519 -0
  95. package/examples/jsm/inspector/Extension.js +13 -0
  96. package/examples/jsm/inspector/Inspector.js +542 -0
  97. package/examples/jsm/inspector/RendererInspector.js +425 -0
  98. package/examples/jsm/inspector/extensions/extensions.json +6 -0
  99. package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphEditor.js +916 -0
  100. package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphLoader.js +281 -0
  101. package/examples/jsm/inspector/tabs/Console.js +238 -0
  102. package/examples/jsm/inspector/tabs/Memory.js +128 -0
  103. package/examples/jsm/inspector/tabs/Parameters.js +380 -0
  104. package/examples/jsm/inspector/tabs/Performance.js +268 -0
  105. package/examples/jsm/inspector/tabs/Settings.js +264 -0
  106. package/examples/jsm/inspector/tabs/Timeline.js +1611 -0
  107. package/examples/jsm/inspector/tabs/Viewer.js +268 -0
  108. package/examples/jsm/inspector/ui/Graph.js +95 -0
  109. package/examples/jsm/inspector/ui/Item.js +170 -0
  110. package/examples/jsm/inspector/ui/List.js +75 -0
  111. package/examples/jsm/inspector/ui/Profiler.js +2072 -0
  112. package/examples/jsm/inspector/ui/Style.js +1667 -0
  113. package/examples/jsm/inspector/ui/Tab.js +265 -0
  114. package/examples/jsm/inspector/ui/Values.js +476 -0
  115. package/examples/jsm/inspector/ui/utils.js +69 -0
  116. package/examples/jsm/interaction/InteractionManager.js +226 -0
  117. package/examples/jsm/interactive/HTMLMesh.js +601 -0
  118. package/examples/jsm/interactive/InteractiveGroup.js +224 -0
  119. package/examples/jsm/interactive/SelectionBox.js +324 -0
  120. package/examples/jsm/interactive/SelectionHelper.js +150 -0
  121. package/examples/jsm/libs/ammo.wasm.js +822 -0
  122. package/examples/jsm/libs/ammo.wasm.wasm +0 -0
  123. package/examples/jsm/libs/basis/README.md +46 -0
  124. package/examples/jsm/libs/basis/basis_transcoder.js +19 -0
  125. package/examples/jsm/libs/basis/basis_transcoder.wasm +0 -0
  126. package/examples/jsm/libs/chevrotain.module.min.js +141 -0
  127. package/examples/jsm/libs/demuxer_mp4.js +109 -0
  128. package/examples/jsm/libs/draco/README.md +32 -0
  129. package/examples/jsm/libs/draco/draco_decoder.js +34 -0
  130. package/examples/jsm/libs/draco/draco_decoder.wasm +0 -0
  131. package/examples/jsm/libs/draco/draco_encoder.js +33 -0
  132. package/examples/jsm/libs/draco/draco_wasm_wrapper.js +117 -0
  133. package/examples/jsm/libs/draco/gltf/draco_decoder.js +33 -0
  134. package/examples/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
  135. package/examples/jsm/libs/draco/gltf/draco_encoder.js +33 -0
  136. package/examples/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
  137. package/examples/jsm/libs/ecsy.module.js +1792 -0
  138. package/examples/jsm/libs/fflate.module.js +2672 -0
  139. package/examples/jsm/libs/ktx-parse.module.js +1 -0
  140. package/examples/jsm/libs/lil-gui.module.min.js +8 -0
  141. package/examples/jsm/libs/lottie_canvas.module.js +14849 -0
  142. package/examples/jsm/libs/meshopt_decoder.module.js +196 -0
  143. package/examples/jsm/libs/mikktspace.module.js +128 -0
  144. package/examples/jsm/libs/motion-controllers.module.js +397 -0
  145. package/examples/jsm/libs/opentype.module.js +14506 -0
  146. package/examples/jsm/libs/potpack.module.js +125 -0
  147. package/examples/jsm/libs/rhino3dm/rhino3dm.js +21 -0
  148. package/examples/jsm/libs/rhino3dm/rhino3dm.module.js +16 -0
  149. package/examples/jsm/libs/rhino3dm/rhino3dm.wasm +0 -0
  150. package/examples/jsm/libs/stats.module.js +167 -0
  151. package/examples/jsm/libs/surfaceNet.js +201 -0
  152. package/examples/jsm/libs/tween.module.js +876 -0
  153. package/examples/jsm/libs/utif.module.js +1665 -0
  154. package/examples/jsm/libs/zstddec.module.js +1 -0
  155. package/examples/jsm/lighting/DynamicLighting.js +82 -0
  156. package/examples/jsm/lighting/LightProbeGrid.js +651 -0
  157. package/examples/jsm/lighting/TiledLighting.js +42 -0
  158. package/examples/jsm/lights/LightProbeGenerator.js +337 -0
  159. package/examples/jsm/lights/RectAreaLightTexturesLib.js +127 -0
  160. package/examples/jsm/lights/RectAreaLightUniformsLib.js +40 -0
  161. package/examples/jsm/lines/Line2.js +56 -0
  162. package/examples/jsm/lines/LineGeometry.js +157 -0
  163. package/examples/jsm/lines/LineMaterial.js +703 -0
  164. package/examples/jsm/lines/LineSegments2.js +426 -0
  165. package/examples/jsm/lines/LineSegmentsGeometry.js +298 -0
  166. package/examples/jsm/lines/Wireframe.js +108 -0
  167. package/examples/jsm/lines/WireframeGeometry2.js +49 -0
  168. package/examples/jsm/lines/webgpu/Line2.js +46 -0
  169. package/examples/jsm/lines/webgpu/LineSegments2.js +411 -0
  170. package/examples/jsm/lines/webgpu/Wireframe.js +86 -0
  171. package/examples/jsm/loaders/3DMLoader.js +1836 -0
  172. package/examples/jsm/loaders/3MFLoader.js +1621 -0
  173. package/examples/jsm/loaders/AMFLoader.js +541 -0
  174. package/examples/jsm/loaders/BVHLoader.js +484 -0
  175. package/examples/jsm/loaders/ColladaLoader.js +153 -0
  176. package/examples/jsm/loaders/DDSLoader.js +385 -0
  177. package/examples/jsm/loaders/DRACOLoader.js +739 -0
  178. package/examples/jsm/loaders/EXRLoader.js +3423 -0
  179. package/examples/jsm/loaders/FBXLoader.js +4580 -0
  180. package/examples/jsm/loaders/FontLoader.js +260 -0
  181. package/examples/jsm/loaders/GCodeLoader.js +318 -0
  182. package/examples/jsm/loaders/GLTFLoader.js +4860 -0
  183. package/examples/jsm/loaders/HDRCubeTextureLoader.js +164 -0
  184. package/examples/jsm/loaders/HDRLoader.js +485 -0
  185. package/examples/jsm/loaders/IESLoader.js +379 -0
  186. package/examples/jsm/loaders/KMZLoader.js +163 -0
  187. package/examples/jsm/loaders/KTX2Loader.js +1257 -0
  188. package/examples/jsm/loaders/KTXLoader.js +197 -0
  189. package/examples/jsm/loaders/LDrawLoader.js +2510 -0
  190. package/examples/jsm/loaders/LUT3dlLoader.js +205 -0
  191. package/examples/jsm/loaders/LUTCubeLoader.js +190 -0
  192. package/examples/jsm/loaders/LUTImageLoader.js +190 -0
  193. package/examples/jsm/loaders/LWOLoader.js +1047 -0
  194. package/examples/jsm/loaders/LottieLoader.js +130 -0
  195. package/examples/jsm/loaders/MD2Loader.js +435 -0
  196. package/examples/jsm/loaders/MDDLoader.js +147 -0
  197. package/examples/jsm/loaders/MTLLoader.js +593 -0
  198. package/examples/jsm/loaders/MaterialXLoader.js +1104 -0
  199. package/examples/jsm/loaders/NRRDLoader.js +718 -0
  200. package/examples/jsm/loaders/OBJLoader.js +955 -0
  201. package/examples/jsm/loaders/PCDLoader.js +620 -0
  202. package/examples/jsm/loaders/PDBLoader.js +272 -0
  203. package/examples/jsm/loaders/PLYLoader.js +805 -0
  204. package/examples/jsm/loaders/PVRLoader.js +270 -0
  205. package/examples/jsm/loaders/RGBELoader.js +18 -0
  206. package/examples/jsm/loaders/STLLoader.js +421 -0
  207. package/examples/jsm/loaders/SVGLoader.js +3267 -0
  208. package/examples/jsm/loaders/TDSLoader.js +1142 -0
  209. package/examples/jsm/loaders/TGALoader.js +538 -0
  210. package/examples/jsm/loaders/TIFFLoader.js +59 -0
  211. package/examples/jsm/loaders/TTFLoader.js +261 -0
  212. package/examples/jsm/loaders/USDLoader.js +279 -0
  213. package/examples/jsm/loaders/USDZLoader.js +16 -0
  214. package/examples/jsm/loaders/UltraHDRLoader.js +755 -0
  215. package/examples/jsm/loaders/VOXLoader.js +919 -0
  216. package/examples/jsm/loaders/VRMLLoader.js +3646 -0
  217. package/examples/jsm/loaders/VTKLoader.js +1293 -0
  218. package/examples/jsm/loaders/XYZLoader.js +143 -0
  219. package/examples/jsm/loaders/collada/ColladaComposer.js +3044 -0
  220. package/examples/jsm/loaders/collada/ColladaParser.js +1977 -0
  221. package/examples/jsm/loaders/lwo/IFFParser.js +1217 -0
  222. package/examples/jsm/loaders/lwo/LWO2Parser.js +414 -0
  223. package/examples/jsm/loaders/lwo/LWO3Parser.js +373 -0
  224. package/examples/jsm/loaders/usd/USDAParser.js +828 -0
  225. package/examples/jsm/loaders/usd/USDCParser.js +1878 -0
  226. package/examples/jsm/loaders/usd/USDComposer.js +4594 -0
  227. package/examples/jsm/materials/LDrawConditionalLineMaterial.js +183 -0
  228. package/examples/jsm/materials/LDrawConditionalLineNodeMaterial.js +154 -0
  229. package/examples/jsm/materials/WoodNodeMaterial.js +533 -0
  230. package/examples/jsm/math/Capsule.js +159 -0
  231. package/examples/jsm/math/ColorConverter.js +58 -0
  232. package/examples/jsm/math/ColorSpaces.js +147 -0
  233. package/examples/jsm/math/ConvexHull.js +1695 -0
  234. package/examples/jsm/math/ImprovedNoise.js +88 -0
  235. package/examples/jsm/math/Lut.js +317 -0
  236. package/examples/jsm/math/MeshSurfaceSampler.js +315 -0
  237. package/examples/jsm/math/OBB.js +535 -0
  238. package/examples/jsm/math/Octree.js +822 -0
  239. package/examples/jsm/math/SimplexNoise.js +470 -0
  240. package/examples/jsm/misc/ConvexObjectBreaker.js +539 -0
  241. package/examples/jsm/misc/GPUComputationRenderer.js +508 -0
  242. package/examples/jsm/misc/Gyroscope.js +78 -0
  243. package/examples/jsm/misc/MD2Character.js +391 -0
  244. package/examples/jsm/misc/MD2CharacterComplex.js +740 -0
  245. package/examples/jsm/misc/MorphAnimMesh.js +119 -0
  246. package/examples/jsm/misc/MorphBlendMesh.js +425 -0
  247. package/examples/jsm/misc/ProgressiveLightMap.js +370 -0
  248. package/examples/jsm/misc/ProgressiveLightMapGPU.js +322 -0
  249. package/examples/jsm/misc/RollerCoaster.js +661 -0
  250. package/examples/jsm/misc/TubePainter.js +599 -0
  251. package/examples/jsm/misc/Volume.js +520 -0
  252. package/examples/jsm/misc/VolumeSlice.js +275 -0
  253. package/examples/jsm/modifiers/CurveModifier.js +374 -0
  254. package/examples/jsm/modifiers/CurveModifierGPU.js +256 -0
  255. package/examples/jsm/modifiers/EdgeSplitModifier.js +299 -0
  256. package/examples/jsm/modifiers/SimplifyModifier.js +632 -0
  257. package/examples/jsm/modifiers/TessellateModifier.js +340 -0
  258. package/examples/jsm/objects/GroundedSkybox.js +69 -0
  259. package/examples/jsm/objects/Lensflare.js +489 -0
  260. package/examples/jsm/objects/LensflareMesh.js +376 -0
  261. package/examples/jsm/objects/MarchingCubes.js +1261 -0
  262. package/examples/jsm/objects/Reflector.js +386 -0
  263. package/examples/jsm/objects/ReflectorForSSRPass.js +393 -0
  264. package/examples/jsm/objects/Refractor.js +389 -0
  265. package/examples/jsm/objects/ShadowMesh.js +130 -0
  266. package/examples/jsm/objects/Sky.js +321 -0
  267. package/examples/jsm/objects/SkyMesh.js +377 -0
  268. package/examples/jsm/objects/Water.js +374 -0
  269. package/examples/jsm/objects/Water2.js +403 -0
  270. package/examples/jsm/objects/Water2Mesh.js +199 -0
  271. package/examples/jsm/objects/WaterMesh.js +194 -0
  272. package/examples/jsm/offscreen/jank.js +45 -0
  273. package/examples/jsm/offscreen/offscreen.js +8 -0
  274. package/examples/jsm/offscreen/scene.js +86 -0
  275. package/examples/jsm/physics/AmmoPhysics.js +359 -0
  276. package/examples/jsm/physics/JoltPhysics.js +334 -0
  277. package/examples/jsm/physics/RapierPhysics.js +436 -0
  278. package/examples/jsm/postprocessing/AfterimagePass.js +185 -0
  279. package/examples/jsm/postprocessing/BloomPass.js +274 -0
  280. package/examples/jsm/postprocessing/BokehPass.js +218 -0
  281. package/examples/jsm/postprocessing/ClearPass.js +97 -0
  282. package/examples/jsm/postprocessing/CubeTexturePass.js +146 -0
  283. package/examples/jsm/postprocessing/DotScreenPass.js +114 -0
  284. package/examples/jsm/postprocessing/EffectComposer.js +365 -0
  285. package/examples/jsm/postprocessing/FXAAPass.js +40 -0
  286. package/examples/jsm/postprocessing/FilmPass.js +113 -0
  287. package/examples/jsm/postprocessing/GTAOPass.js +727 -0
  288. package/examples/jsm/postprocessing/GlitchPass.js +177 -0
  289. package/examples/jsm/postprocessing/HalftonePass.js +134 -0
  290. package/examples/jsm/postprocessing/LUTPass.js +138 -0
  291. package/examples/jsm/postprocessing/MaskPass.js +195 -0
  292. package/examples/jsm/postprocessing/OutlinePass.js +776 -0
  293. package/examples/jsm/postprocessing/OutputPass.js +148 -0
  294. package/examples/jsm/postprocessing/Pass.js +191 -0
  295. package/examples/jsm/postprocessing/RenderPass.js +193 -0
  296. package/examples/jsm/postprocessing/RenderPixelatedPass.js +314 -0
  297. package/examples/jsm/postprocessing/RenderTransitionPass.js +267 -0
  298. package/examples/jsm/postprocessing/SAOPass.js +407 -0
  299. package/examples/jsm/postprocessing/SMAAPass.js +230 -0
  300. package/examples/jsm/postprocessing/SSAARenderPass.js +313 -0
  301. package/examples/jsm/postprocessing/SSAOPass.js +527 -0
  302. package/examples/jsm/postprocessing/SSRPass.js +856 -0
  303. package/examples/jsm/postprocessing/SavePass.js +132 -0
  304. package/examples/jsm/postprocessing/ShaderPass.js +135 -0
  305. package/examples/jsm/postprocessing/TAARenderPass.js +243 -0
  306. package/examples/jsm/postprocessing/TexturePass.js +131 -0
  307. package/examples/jsm/postprocessing/UnrealBloomPass.js +524 -0
  308. package/examples/jsm/renderers/CSS2DRenderer.js +328 -0
  309. package/examples/jsm/renderers/CSS3DRenderer.js +454 -0
  310. package/examples/jsm/renderers/Projector.js +1154 -0
  311. package/examples/jsm/renderers/SVGRenderer.js +799 -0
  312. package/examples/jsm/shaders/ACESFilmicToneMappingShader.js +96 -0
  313. package/examples/jsm/shaders/AfterimageShader.js +63 -0
  314. package/examples/jsm/shaders/BasicShader.js +36 -0
  315. package/examples/jsm/shaders/BleachBypassShader.js +68 -0
  316. package/examples/jsm/shaders/BlendShader.js +56 -0
  317. package/examples/jsm/shaders/BokehShader.js +151 -0
  318. package/examples/jsm/shaders/BokehShader2.js +404 -0
  319. package/examples/jsm/shaders/BrightnessContrastShader.js +62 -0
  320. package/examples/jsm/shaders/ColorCorrectionShader.js +59 -0
  321. package/examples/jsm/shaders/ColorifyShader.js +57 -0
  322. package/examples/jsm/shaders/ConvolutionShader.js +74 -0
  323. package/examples/jsm/shaders/CopyShader.js +52 -0
  324. package/examples/jsm/shaders/DOFMipMapShader.js +63 -0
  325. package/examples/jsm/shaders/DepthLimitedBlurShader.js +180 -0
  326. package/examples/jsm/shaders/DigitalGlitch.js +104 -0
  327. package/examples/jsm/shaders/DotScreenShader.js +75 -0
  328. package/examples/jsm/shaders/ExposureShader.js +51 -0
  329. package/examples/jsm/shaders/FXAAShader.js +298 -0
  330. package/examples/jsm/shaders/FilmShader.js +72 -0
  331. package/examples/jsm/shaders/FocusShader.js +94 -0
  332. package/examples/jsm/shaders/FreiChenShader.js +103 -0
  333. package/examples/jsm/shaders/GTAOShader.js +434 -0
  334. package/examples/jsm/shaders/GammaCorrectionShader.js +52 -0
  335. package/examples/jsm/shaders/HalftoneShader.js +332 -0
  336. package/examples/jsm/shaders/HorizontalBlurShader.js +68 -0
  337. package/examples/jsm/shaders/HorizontalTiltShiftShader.js +70 -0
  338. package/examples/jsm/shaders/HueSaturationShader.js +74 -0
  339. package/examples/jsm/shaders/KaleidoShader.js +65 -0
  340. package/examples/jsm/shaders/LuminosityHighPassShader.js +68 -0
  341. package/examples/jsm/shaders/LuminosityShader.js +54 -0
  342. package/examples/jsm/shaders/MirrorShader.js +62 -0
  343. package/examples/jsm/shaders/NormalMapShader.js +60 -0
  344. package/examples/jsm/shaders/OutputShader.js +103 -0
  345. package/examples/jsm/shaders/PoissonDenoiseShader.js +239 -0
  346. package/examples/jsm/shaders/RGBShiftShader.js +61 -0
  347. package/examples/jsm/shaders/SAOShader.js +201 -0
  348. package/examples/jsm/shaders/SMAAShader.js +489 -0
  349. package/examples/jsm/shaders/SSAOShader.js +331 -0
  350. package/examples/jsm/shaders/SSRShader.js +396 -0
  351. package/examples/jsm/shaders/SepiaShader.js +57 -0
  352. package/examples/jsm/shaders/SobelOperatorShader.js +98 -0
  353. package/examples/jsm/shaders/SubsurfaceScatteringShader.js +95 -0
  354. package/examples/jsm/shaders/TechnicolorShader.js +51 -0
  355. package/examples/jsm/shaders/ToonShader.js +349 -0
  356. package/examples/jsm/shaders/TriangleBlurShader.js +79 -0
  357. package/examples/jsm/shaders/UnpackDepthRGBAShader.js +60 -0
  358. package/examples/jsm/shaders/VelocityShader.js +137 -0
  359. package/examples/jsm/shaders/VerticalBlurShader.js +66 -0
  360. package/examples/jsm/shaders/VerticalTiltShiftShader.js +70 -0
  361. package/examples/jsm/shaders/VignetteShader.js +56 -0
  362. package/examples/jsm/shaders/VolumeShader.js +298 -0
  363. package/examples/jsm/shaders/WaterRefractionShader.js +106 -0
  364. package/examples/jsm/textures/FlakesTexture.js +54 -0
  365. package/examples/jsm/transpiler/AST.js +675 -0
  366. package/examples/jsm/transpiler/GLSLDecoder.js +1228 -0
  367. package/examples/jsm/transpiler/Linker.js +327 -0
  368. package/examples/jsm/transpiler/ShaderToyDecoder.js +51 -0
  369. package/examples/jsm/transpiler/TSLEncoder.js +983 -0
  370. package/examples/jsm/transpiler/Transpiler.js +67 -0
  371. package/examples/jsm/transpiler/TranspilerUtils.js +29 -0
  372. package/examples/jsm/transpiler/WGSLEncoder.js +839 -0
  373. package/examples/jsm/tsl/WebGLNodesHandler.js +605 -0
  374. package/examples/jsm/tsl/display/AfterImageNode.js +254 -0
  375. package/examples/jsm/tsl/display/AnaglyphPassNode.js +549 -0
  376. package/examples/jsm/tsl/display/AnamorphicNode.js +293 -0
  377. package/examples/jsm/tsl/display/BilateralBlurNode.js +374 -0
  378. package/examples/jsm/tsl/display/BleachBypass.js +33 -0
  379. package/examples/jsm/tsl/display/BloomNode.js +534 -0
  380. package/examples/jsm/tsl/display/CRT.js +150 -0
  381. package/examples/jsm/tsl/display/ChromaticAberrationNode.js +174 -0
  382. package/examples/jsm/tsl/display/DenoiseNode.js +334 -0
  383. package/examples/jsm/tsl/display/DepthOfFieldNode.js +554 -0
  384. package/examples/jsm/tsl/display/DotScreenNode.js +104 -0
  385. package/examples/jsm/tsl/display/FSR1Node.js +477 -0
  386. package/examples/jsm/tsl/display/FXAANode.js +365 -0
  387. package/examples/jsm/tsl/display/FilmNode.js +101 -0
  388. package/examples/jsm/tsl/display/GTAONode.js +572 -0
  389. package/examples/jsm/tsl/display/GaussianBlurNode.js +399 -0
  390. package/examples/jsm/tsl/display/GodraysNode.js +615 -0
  391. package/examples/jsm/tsl/display/LensflareNode.js +279 -0
  392. package/examples/jsm/tsl/display/Lut3DNode.js +109 -0
  393. package/examples/jsm/tsl/display/MotionBlur.js +33 -0
  394. package/examples/jsm/tsl/display/OutlineNode.js +812 -0
  395. package/examples/jsm/tsl/display/ParallaxBarrierPassNode.js +89 -0
  396. package/examples/jsm/tsl/display/PixelationPassNode.js +335 -0
  397. package/examples/jsm/tsl/display/RGBShiftNode.js +96 -0
  398. package/examples/jsm/tsl/display/RetroPassNode.js +263 -0
  399. package/examples/jsm/tsl/display/SMAANode.js +768 -0
  400. package/examples/jsm/tsl/display/SSAAPassNode.js +358 -0
  401. package/examples/jsm/tsl/display/SSGINode.js +638 -0
  402. package/examples/jsm/tsl/display/SSRNode.js +656 -0
  403. package/examples/jsm/tsl/display/SSSNode.js +490 -0
  404. package/examples/jsm/tsl/display/Sepia.js +24 -0
  405. package/examples/jsm/tsl/display/Shape.js +29 -0
  406. package/examples/jsm/tsl/display/SharpenNode.js +283 -0
  407. package/examples/jsm/tsl/display/SobelOperatorNode.js +168 -0
  408. package/examples/jsm/tsl/display/StereoCompositePassNode.js +192 -0
  409. package/examples/jsm/tsl/display/StereoPassNode.js +119 -0
  410. package/examples/jsm/tsl/display/TAAUNode.js +835 -0
  411. package/examples/jsm/tsl/display/TRAANode.js +767 -0
  412. package/examples/jsm/tsl/display/TransitionNode.js +141 -0
  413. package/examples/jsm/tsl/display/boxBlur.js +65 -0
  414. package/examples/jsm/tsl/display/depthAwareBlend.js +80 -0
  415. package/examples/jsm/tsl/display/hashBlur.js +54 -0
  416. package/examples/jsm/tsl/display/radialBlur.js +68 -0
  417. package/examples/jsm/tsl/lighting/DynamicLightsNode.js +300 -0
  418. package/examples/jsm/tsl/lighting/TiledLightsNode.js +442 -0
  419. package/examples/jsm/tsl/lighting/data/AmbientLightDataNode.js +61 -0
  420. package/examples/jsm/tsl/lighting/data/DirectionalLightDataNode.js +111 -0
  421. package/examples/jsm/tsl/lighting/data/HemisphereLightDataNode.js +99 -0
  422. package/examples/jsm/tsl/lighting/data/PointLightDataNode.js +134 -0
  423. package/examples/jsm/tsl/lighting/data/SpotLightDataNode.js +161 -0
  424. package/examples/jsm/tsl/math/Bayer.js +84 -0
  425. package/examples/jsm/tsl/shadows/TileShadowNode.js +456 -0
  426. package/examples/jsm/tsl/shadows/TileShadowNodeHelper.js +212 -0
  427. package/examples/jsm/tsl/utils/Raymarching.js +70 -0
  428. package/examples/jsm/utils/BufferGeometryUtils.js +1434 -0
  429. package/examples/jsm/utils/CameraUtils.js +82 -0
  430. package/examples/jsm/utils/ColorUtils.js +76 -0
  431. package/examples/jsm/utils/GeometryCompressionUtils.js +547 -0
  432. package/examples/jsm/utils/GeometryUtils.js +226 -0
  433. package/examples/jsm/utils/LDrawUtils.js +211 -0
  434. package/examples/jsm/utils/SceneOptimizer.js +458 -0
  435. package/examples/jsm/utils/SceneUtils.js +363 -0
  436. package/examples/jsm/utils/ShadowMapViewer.js +244 -0
  437. package/examples/jsm/utils/ShadowMapViewerGPU.js +233 -0
  438. package/examples/jsm/utils/SkeletonUtils.js +496 -0
  439. package/examples/jsm/utils/SortUtils.js +175 -0
  440. package/examples/jsm/utils/UVsDebug.js +173 -0
  441. package/examples/jsm/utils/WebGLTextureUtils.js +115 -0
  442. package/examples/jsm/utils/WebGPUTextureUtils.js +81 -0
  443. package/examples/jsm/utils/WorkerPool.js +167 -0
  444. package/examples/jsm/webxr/ARButton.js +251 -0
  445. package/examples/jsm/webxr/OculusHandModel.js +194 -0
  446. package/examples/jsm/webxr/OculusHandPointerModel.js +539 -0
  447. package/examples/jsm/webxr/Text2D.js +52 -0
  448. package/examples/jsm/webxr/VRButton.js +264 -0
  449. package/examples/jsm/webxr/XRButton.js +246 -0
  450. package/examples/jsm/webxr/XRControllerModelFactory.js +403 -0
  451. package/examples/jsm/webxr/XREstimatedLight.js +254 -0
  452. package/examples/jsm/webxr/XRHandMeshModel.js +177 -0
  453. package/examples/jsm/webxr/XRHandModelFactory.js +198 -0
  454. package/examples/jsm/webxr/XRHandPrimitiveModel.js +147 -0
  455. package/examples/jsm/webxr/XRPlanes.js +118 -0
  456. package/package.json +123 -0
  457. package/src/Three.Core.js +187 -0
  458. package/src/Three.Legacy.js +0 -0
  459. package/src/Three.TSL.js +647 -0
  460. package/src/Three.WebGPU.Nodes.js +31 -0
  461. package/src/Three.WebGPU.js +37 -0
  462. package/src/Three.js +10 -0
  463. package/src/animation/AnimationAction.js +942 -0
  464. package/src/animation/AnimationClip.js +628 -0
  465. package/src/animation/AnimationMixer.js +860 -0
  466. package/src/animation/AnimationObjectGroup.js +411 -0
  467. package/src/animation/AnimationUtils.js +495 -0
  468. package/src/animation/KeyframeTrack.js +636 -0
  469. package/src/animation/PropertyBinding.js +794 -0
  470. package/src/animation/PropertyMixer.js +385 -0
  471. package/src/animation/tracks/BooleanKeyframeTrack.js +55 -0
  472. package/src/animation/tracks/ColorKeyframeTrack.js +36 -0
  473. package/src/animation/tracks/NumberKeyframeTrack.js +36 -0
  474. package/src/animation/tracks/QuaternionKeyframeTrack.js +51 -0
  475. package/src/animation/tracks/StringKeyframeTrack.js +55 -0
  476. package/src/animation/tracks/VectorKeyframeTrack.js +36 -0
  477. package/src/audio/Audio.js +778 -0
  478. package/src/audio/AudioAnalyser.js +97 -0
  479. package/src/audio/AudioContext.js +40 -0
  480. package/src/audio/AudioListener.js +218 -0
  481. package/src/audio/PositionalAudio.js +253 -0
  482. package/src/cameras/ArrayCamera.js +54 -0
  483. package/src/cameras/Camera.js +160 -0
  484. package/src/cameras/CubeCamera.js +259 -0
  485. package/src/cameras/OrthographicCamera.js +245 -0
  486. package/src/cameras/PerspectiveCamera.js +407 -0
  487. package/src/cameras/StereoCamera.js +146 -0
  488. package/src/constants.js +1760 -0
  489. package/src/core/BufferAttribute.js +1056 -0
  490. package/src/core/BufferGeometry.js +1458 -0
  491. package/src/core/Clock.js +135 -0
  492. package/src/core/EventDispatcher.js +131 -0
  493. package/src/core/GLBufferAttribute.js +171 -0
  494. package/src/core/InstancedBufferAttribute.js +68 -0
  495. package/src/core/InstancedBufferGeometry.js +60 -0
  496. package/src/core/InstancedInterleavedBuffer.js +74 -0
  497. package/src/core/InterleavedBuffer.js +291 -0
  498. package/src/core/InterleavedBufferAttribute.js +549 -0
  499. package/src/core/Layers.js +121 -0
  500. package/src/core/Object3D.js +1667 -0
  501. package/src/core/Raycaster.js +262 -0
  502. package/src/core/RenderTarget.js +392 -0
  503. package/src/core/RenderTarget3D.js +48 -0
  504. package/src/core/Timer.js +184 -0
  505. package/src/core/Uniform.js +46 -0
  506. package/src/core/UniformsGroup.js +180 -0
  507. package/src/extras/Controls.js +120 -0
  508. package/src/extras/DataUtils.js +217 -0
  509. package/src/extras/Earcut.js +28 -0
  510. package/src/extras/ImageUtils.js +137 -0
  511. package/src/extras/PMREMGenerator.js +1167 -0
  512. package/src/extras/ShapeUtils.js +114 -0
  513. package/src/extras/TextureUtils.js +297 -0
  514. package/src/extras/core/Curve.js +517 -0
  515. package/src/extras/core/CurvePath.js +296 -0
  516. package/src/extras/core/Interpolations.js +111 -0
  517. package/src/extras/core/Path.js +329 -0
  518. package/src/extras/core/Shape.js +165 -0
  519. package/src/extras/core/ShapePath.js +367 -0
  520. package/src/extras/curves/ArcCurve.js +39 -0
  521. package/src/extras/curves/CatmullRomCurve3.js +327 -0
  522. package/src/extras/curves/CubicBezierCurve.js +145 -0
  523. package/src/extras/curves/CubicBezierCurve3.js +129 -0
  524. package/src/extras/curves/Curves.js +10 -0
  525. package/src/extras/curves/EllipseCurve.js +258 -0
  526. package/src/extras/curves/LineCurve.js +128 -0
  527. package/src/extras/curves/LineCurve3.js +128 -0
  528. package/src/extras/curves/QuadraticBezierCurve.js +133 -0
  529. package/src/extras/curves/QuadraticBezierCurve3.js +118 -0
  530. package/src/extras/curves/SplineCurve.js +145 -0
  531. package/src/extras/lib/earcut.js +685 -0
  532. package/src/geometries/BoxGeometry.js +219 -0
  533. package/src/geometries/CapsuleGeometry.js +218 -0
  534. package/src/geometries/CircleGeometry.js +142 -0
  535. package/src/geometries/ConeGeometry.js +70 -0
  536. package/src/geometries/CylinderGeometry.js +333 -0
  537. package/src/geometries/DodecahedronGeometry.js +99 -0
  538. package/src/geometries/EdgesGeometry.js +180 -0
  539. package/src/geometries/ExtrudeGeometry.js +910 -0
  540. package/src/geometries/Geometries.js +21 -0
  541. package/src/geometries/IcosahedronGeometry.js +75 -0
  542. package/src/geometries/LatheGeometry.js +230 -0
  543. package/src/geometries/OctahedronGeometry.js +70 -0
  544. package/src/geometries/PlaneGeometry.js +133 -0
  545. package/src/geometries/PolyhedronGeometry.js +348 -0
  546. package/src/geometries/RingGeometry.js +165 -0
  547. package/src/geometries/ShapeGeometry.js +233 -0
  548. package/src/geometries/SphereGeometry.js +175 -0
  549. package/src/geometries/TetrahedronGeometry.js +67 -0
  550. package/src/geometries/TorusGeometry.js +161 -0
  551. package/src/geometries/TorusKnotGeometry.js +206 -0
  552. package/src/geometries/TubeGeometry.js +253 -0
  553. package/src/geometries/WireframeGeometry.js +179 -0
  554. package/src/helpers/ArrowHelper.js +171 -0
  555. package/src/helpers/AxesHelper.js +96 -0
  556. package/src/helpers/Box3Helper.js +83 -0
  557. package/src/helpers/BoxHelper.js +149 -0
  558. package/src/helpers/CameraHelper.js +345 -0
  559. package/src/helpers/DirectionalLightHelper.js +148 -0
  560. package/src/helpers/GridHelper.js +82 -0
  561. package/src/helpers/HemisphereLightHelper.js +130 -0
  562. package/src/helpers/PlaneHelper.js +96 -0
  563. package/src/helpers/PointLightHelper.js +111 -0
  564. package/src/helpers/PolarGridHelper.js +126 -0
  565. package/src/helpers/SkeletonHelper.js +194 -0
  566. package/src/helpers/SpotLightHelper.js +154 -0
  567. package/src/lights/AmbientLight.js +42 -0
  568. package/src/lights/DirectionalLight.js +113 -0
  569. package/src/lights/DirectionalLightShadow.js +31 -0
  570. package/src/lights/HemisphereLight.js +76 -0
  571. package/src/lights/Light.js +85 -0
  572. package/src/lights/LightProbe.js +75 -0
  573. package/src/lights/LightShadow.js +347 -0
  574. package/src/lights/PointLight.js +131 -0
  575. package/src/lights/PointLightShadow.js +31 -0
  576. package/src/lights/RectAreaLight.js +115 -0
  577. package/src/lights/SpotLight.js +194 -0
  578. package/src/lights/SpotLightShadow.js +80 -0
  579. package/src/lights/webgpu/IESSpotLight.js +47 -0
  580. package/src/lights/webgpu/ProjectorLight.js +46 -0
  581. package/src/loaders/AnimationLoader.js +98 -0
  582. package/src/loaders/AudioLoader.js +108 -0
  583. package/src/loaders/BufferGeometryLoader.js +242 -0
  584. package/src/loaders/Cache.js +115 -0
  585. package/src/loaders/CompressedTextureLoader.js +167 -0
  586. package/src/loaders/CubeTextureLoader.js +103 -0
  587. package/src/loaders/DataTextureLoader.js +174 -0
  588. package/src/loaders/FileLoader.js +367 -0
  589. package/src/loaders/ImageBitmapLoader.js +223 -0
  590. package/src/loaders/ImageLoader.js +168 -0
  591. package/src/loaders/Loader.js +222 -0
  592. package/src/loaders/LoaderUtils.js +59 -0
  593. package/src/loaders/LoadingManager.js +329 -0
  594. package/src/loaders/MaterialLoader.js +439 -0
  595. package/src/loaders/ObjectLoader.js +1304 -0
  596. package/src/loaders/TextureLoader.js +74 -0
  597. package/src/loaders/nodes/NodeLoader.js +194 -0
  598. package/src/loaders/nodes/NodeMaterialLoader.js +108 -0
  599. package/src/loaders/nodes/NodeObjectLoader.js +169 -0
  600. package/src/materials/LineBasicMaterial.js +122 -0
  601. package/src/materials/LineDashedMaterial.js +86 -0
  602. package/src/materials/Material.js +1017 -0
  603. package/src/materials/Materials.js +39 -0
  604. package/src/materials/MeshBasicMaterial.js +250 -0
  605. package/src/materials/MeshDepthMaterial.js +148 -0
  606. package/src/materials/MeshDistanceMaterial.js +119 -0
  607. package/src/materials/MeshLambertMaterial.js +402 -0
  608. package/src/materials/MeshMatcapMaterial.js +245 -0
  609. package/src/materials/MeshNormalMaterial.js +175 -0
  610. package/src/materials/MeshPhongMaterial.js +421 -0
  611. package/src/materials/MeshPhysicalMaterial.js +536 -0
  612. package/src/materials/MeshStandardMaterial.js +425 -0
  613. package/src/materials/MeshToonMaterial.js +321 -0
  614. package/src/materials/PointsMaterial.js +139 -0
  615. package/src/materials/RawShaderMaterial.js +42 -0
  616. package/src/materials/ShaderMaterial.js +413 -0
  617. package/src/materials/ShadowMaterial.js +91 -0
  618. package/src/materials/SpriteMaterial.js +136 -0
  619. package/src/materials/nodes/Line2NodeMaterial.js +569 -0
  620. package/src/materials/nodes/LineBasicNodeMaterial.js +46 -0
  621. package/src/materials/nodes/LineDashedNodeMaterial.js +132 -0
  622. package/src/materials/nodes/MeshBasicNodeMaterial.js +134 -0
  623. package/src/materials/nodes/MeshLambertNodeMaterial.js +82 -0
  624. package/src/materials/nodes/MeshMatcapNodeMaterial.js +77 -0
  625. package/src/materials/nodes/MeshNormalNodeMaterial.js +67 -0
  626. package/src/materials/nodes/MeshPhongNodeMaterial.js +141 -0
  627. package/src/materials/nodes/MeshPhysicalNodeMaterial.js +521 -0
  628. package/src/materials/nodes/MeshSSSNodeMaterial.js +175 -0
  629. package/src/materials/nodes/MeshStandardNodeMaterial.js +187 -0
  630. package/src/materials/nodes/MeshToonNodeMaterial.js +66 -0
  631. package/src/materials/nodes/NodeMaterial.js +1330 -0
  632. package/src/materials/nodes/NodeMaterials.js +21 -0
  633. package/src/materials/nodes/PointsNodeMaterial.js +211 -0
  634. package/src/materials/nodes/ShadowNodeMaterial.js +76 -0
  635. package/src/materials/nodes/SpriteNodeMaterial.js +186 -0
  636. package/src/materials/nodes/VolumeNodeMaterial.js +81 -0
  637. package/src/materials/nodes/manager/NodeMaterialObserver.js +718 -0
  638. package/src/math/Box2.js +381 -0
  639. package/src/math/Box3.js +805 -0
  640. package/src/math/Color.js +967 -0
  641. package/src/math/ColorManagement.js +215 -0
  642. package/src/math/Cylindrical.js +120 -0
  643. package/src/math/Euler.js +449 -0
  644. package/src/math/Frustum.js +285 -0
  645. package/src/math/FrustumArray.js +258 -0
  646. package/src/math/Interpolant.js +320 -0
  647. package/src/math/Line3.js +337 -0
  648. package/src/math/MathUtils.js +751 -0
  649. package/src/math/Matrix2.js +128 -0
  650. package/src/math/Matrix3.js +617 -0
  651. package/src/math/Matrix4.js +1314 -0
  652. package/src/math/Plane.js +368 -0
  653. package/src/math/Quaternion.js +918 -0
  654. package/src/math/Ray.js +655 -0
  655. package/src/math/Sphere.js +420 -0
  656. package/src/math/Spherical.js +147 -0
  657. package/src/math/SphericalHarmonics3.js +341 -0
  658. package/src/math/Triangle.js +539 -0
  659. package/src/math/Vector2.js +870 -0
  660. package/src/math/Vector3.js +1263 -0
  661. package/src/math/Vector4.js +1067 -0
  662. package/src/math/interpolants/BezierInterpolant.js +108 -0
  663. package/src/math/interpolants/CubicInterpolant.js +159 -0
  664. package/src/math/interpolants/DiscreteInterpolant.js +34 -0
  665. package/src/math/interpolants/LinearInterpolant.js +51 -0
  666. package/src/math/interpolants/QuaternionLinearInterpolant.js +48 -0
  667. package/src/nodes/Nodes.js +166 -0
  668. package/src/nodes/TSL.js +173 -0
  669. package/src/nodes/accessors/AccessorsUtils.js +53 -0
  670. package/src/nodes/accessors/Arrays.js +68 -0
  671. package/src/nodes/accessors/BatchNode.js +163 -0
  672. package/src/nodes/accessors/Bitangent.js +82 -0
  673. package/src/nodes/accessors/BufferAttributeNode.js +432 -0
  674. package/src/nodes/accessors/BufferNode.js +128 -0
  675. package/src/nodes/accessors/BuiltinNode.js +63 -0
  676. package/src/nodes/accessors/Camera.js +403 -0
  677. package/src/nodes/accessors/ClippingNode.js +255 -0
  678. package/src/nodes/accessors/CubeTextureNode.js +215 -0
  679. package/src/nodes/accessors/InstanceNode.js +349 -0
  680. package/src/nodes/accessors/InstancedMeshNode.js +50 -0
  681. package/src/nodes/accessors/Lights.js +139 -0
  682. package/src/nodes/accessors/MaterialNode.js +783 -0
  683. package/src/nodes/accessors/MaterialProperties.js +56 -0
  684. package/src/nodes/accessors/MaterialReferenceNode.js +84 -0
  685. package/src/nodes/accessors/ModelNode.js +184 -0
  686. package/src/nodes/accessors/ModelViewProjectionNode.js +13 -0
  687. package/src/nodes/accessors/MorphNode.js +310 -0
  688. package/src/nodes/accessors/Normal.js +243 -0
  689. package/src/nodes/accessors/Object3DNode.js +268 -0
  690. package/src/nodes/accessors/PointUVNode.js +55 -0
  691. package/src/nodes/accessors/Position.js +122 -0
  692. package/src/nodes/accessors/ReferenceBaseNode.js +357 -0
  693. package/src/nodes/accessors/ReferenceNode.js +424 -0
  694. package/src/nodes/accessors/ReflectVector.js +36 -0
  695. package/src/nodes/accessors/RendererReferenceNode.js +78 -0
  696. package/src/nodes/accessors/SceneProperties.js +47 -0
  697. package/src/nodes/accessors/SkinningNode.js +328 -0
  698. package/src/nodes/accessors/StorageBufferNode.js +405 -0
  699. package/src/nodes/accessors/StorageTextureNode.js +295 -0
  700. package/src/nodes/accessors/Tangent.js +60 -0
  701. package/src/nodes/accessors/TangentUtils.js +46 -0
  702. package/src/nodes/accessors/Texture3DNode.js +196 -0
  703. package/src/nodes/accessors/TextureBicubic.js +92 -0
  704. package/src/nodes/accessors/TextureNode.js +971 -0
  705. package/src/nodes/accessors/TextureSizeNode.js +77 -0
  706. package/src/nodes/accessors/UV.js +11 -0
  707. package/src/nodes/accessors/UniformArrayNode.js +350 -0
  708. package/src/nodes/accessors/UserDataNode.js +76 -0
  709. package/src/nodes/accessors/VelocityNode.js +224 -0
  710. package/src/nodes/accessors/VertexColorNode.js +109 -0
  711. package/src/nodes/code/CodeNode.js +181 -0
  712. package/src/nodes/code/ExpressionNode.js +68 -0
  713. package/src/nodes/code/FunctionCallNode.js +187 -0
  714. package/src/nodes/code/FunctionNode.js +182 -0
  715. package/src/nodes/core/ArrayNode.js +174 -0
  716. package/src/nodes/core/AssignNode.js +202 -0
  717. package/src/nodes/core/AttributeNode.js +168 -0
  718. package/src/nodes/core/BypassNode.js +93 -0
  719. package/src/nodes/core/ConstNode.js +67 -0
  720. package/src/nodes/core/ContextNode.js +283 -0
  721. package/src/nodes/core/IndexNode.js +165 -0
  722. package/src/nodes/core/InputNode.js +136 -0
  723. package/src/nodes/core/InspectorNode.js +128 -0
  724. package/src/nodes/core/IsolateNode.js +133 -0
  725. package/src/nodes/core/LightingModel.js +77 -0
  726. package/src/nodes/core/MRTNode.js +196 -0
  727. package/src/nodes/core/Node.js +1180 -0
  728. package/src/nodes/core/NodeAttribute.js +53 -0
  729. package/src/nodes/core/NodeBuilder.js +3299 -0
  730. package/src/nodes/core/NodeCache.js +75 -0
  731. package/src/nodes/core/NodeCode.js +46 -0
  732. package/src/nodes/core/NodeError.js +28 -0
  733. package/src/nodes/core/NodeFrame.js +314 -0
  734. package/src/nodes/core/NodeFunction.js +69 -0
  735. package/src/nodes/core/NodeFunctionInput.js +61 -0
  736. package/src/nodes/core/NodeParser.js +23 -0
  737. package/src/nodes/core/NodeUniform.js +91 -0
  738. package/src/nodes/core/NodeUtils.js +408 -0
  739. package/src/nodes/core/NodeVar.js +60 -0
  740. package/src/nodes/core/NodeVarying.js +63 -0
  741. package/src/nodes/core/OutputStructNode.js +105 -0
  742. package/src/nodes/core/ParameterNode.js +94 -0
  743. package/src/nodes/core/PropertyNode.js +367 -0
  744. package/src/nodes/core/StackNode.js +426 -0
  745. package/src/nodes/core/StackTrace.js +139 -0
  746. package/src/nodes/core/StructNode.js +134 -0
  747. package/src/nodes/core/StructType.js +13 -0
  748. package/src/nodes/core/StructTypeNode.js +148 -0
  749. package/src/nodes/core/SubBuildNode.js +89 -0
  750. package/src/nodes/core/TempNode.js +88 -0
  751. package/src/nodes/core/UniformGroupNode.js +167 -0
  752. package/src/nodes/core/UniformNode.js +259 -0
  753. package/src/nodes/core/VarNode.js +367 -0
  754. package/src/nodes/core/VaryingNode.js +210 -0
  755. package/src/nodes/core/constants.js +68 -0
  756. package/src/nodes/display/BlendModes.js +171 -0
  757. package/src/nodes/display/BumpMapNode.js +117 -0
  758. package/src/nodes/display/ColorAdjustment.js +158 -0
  759. package/src/nodes/display/ColorSpaceFunctions.js +54 -0
  760. package/src/nodes/display/ColorSpaceNode.js +164 -0
  761. package/src/nodes/display/FrontFacingNode.js +102 -0
  762. package/src/nodes/display/NormalMapNode.js +153 -0
  763. package/src/nodes/display/PassNode.js +1055 -0
  764. package/src/nodes/display/RenderOutputNode.js +150 -0
  765. package/src/nodes/display/ScreenNode.js +292 -0
  766. package/src/nodes/display/ToneMappingFunctions.js +242 -0
  767. package/src/nodes/display/ToneMappingNode.js +147 -0
  768. package/src/nodes/display/ToonOutlinePassNode.js +191 -0
  769. package/src/nodes/display/ViewportDepthNode.js +342 -0
  770. package/src/nodes/display/ViewportDepthTextureNode.js +63 -0
  771. package/src/nodes/display/ViewportSharedTextureNode.js +73 -0
  772. package/src/nodes/display/ViewportTextureNode.js +256 -0
  773. package/src/nodes/fog/Fog.js +97 -0
  774. package/src/nodes/functions/BSDF/BRDF_GGX.js +55 -0
  775. package/src/nodes/functions/BSDF/BRDF_GGX_Multiscatter.js +52 -0
  776. package/src/nodes/functions/BSDF/BRDF_Lambert.js +9 -0
  777. package/src/nodes/functions/BSDF/BRDF_Sheen.js +57 -0
  778. package/src/nodes/functions/BSDF/DFGLUT.js +56 -0
  779. package/src/nodes/functions/BSDF/D_GGX.js +23 -0
  780. package/src/nodes/functions/BSDF/D_GGX_Anisotropic.js +28 -0
  781. package/src/nodes/functions/BSDF/EnvironmentBRDF.js +13 -0
  782. package/src/nodes/functions/BSDF/F_Schlick.js +16 -0
  783. package/src/nodes/functions/BSDF/LTC.js +175 -0
  784. package/src/nodes/functions/BSDF/Schlick_to_F0.js +21 -0
  785. package/src/nodes/functions/BSDF/V_GGX_SmithCorrelated.js +26 -0
  786. package/src/nodes/functions/BSDF/V_GGX_SmithCorrelated_Anisotropic.js +29 -0
  787. package/src/nodes/functions/BasicLightingModel.js +100 -0
  788. package/src/nodes/functions/PhongLightingModel.js +99 -0
  789. package/src/nodes/functions/PhysicalLightingModel.js +878 -0
  790. package/src/nodes/functions/ShadowMaskModel.js +58 -0
  791. package/src/nodes/functions/ToonLightingModel.js +70 -0
  792. package/src/nodes/functions/VolumetricLightingModel.js +183 -0
  793. package/src/nodes/functions/material/getAlphaHashThreshold.js +68 -0
  794. package/src/nodes/functions/material/getGeometryRoughness.js +19 -0
  795. package/src/nodes/functions/material/getParallaxCorrectNormal.js +37 -0
  796. package/src/nodes/functions/material/getRoughness.js +18 -0
  797. package/src/nodes/functions/material/getShIrradianceAt.js +28 -0
  798. package/src/nodes/geometry/RangeNode.js +210 -0
  799. package/src/nodes/gpgpu/AtomicFunctionNode.js +274 -0
  800. package/src/nodes/gpgpu/BarrierNode.js +98 -0
  801. package/src/nodes/gpgpu/ComputeBuiltinNode.js +227 -0
  802. package/src/nodes/gpgpu/ComputeNode.js +310 -0
  803. package/src/nodes/gpgpu/SubgroupFunctionNode.js +455 -0
  804. package/src/nodes/gpgpu/WorkgroupInfoNode.js +234 -0
  805. package/src/nodes/lighting/AONode.js +45 -0
  806. package/src/nodes/lighting/AmbientLightNode.js +35 -0
  807. package/src/nodes/lighting/AnalyticLightNode.js +309 -0
  808. package/src/nodes/lighting/BasicEnvironmentNode.js +49 -0
  809. package/src/nodes/lighting/BasicLightMapNode.js +49 -0
  810. package/src/nodes/lighting/DirectionalLightNode.js +39 -0
  811. package/src/nodes/lighting/EnvironmentNode.js +178 -0
  812. package/src/nodes/lighting/HemisphereLightNode.js +87 -0
  813. package/src/nodes/lighting/IESSpotLightNode.js +49 -0
  814. package/src/nodes/lighting/IrradianceNode.js +44 -0
  815. package/src/nodes/lighting/LightProbeNode.js +73 -0
  816. package/src/nodes/lighting/LightUtils.js +25 -0
  817. package/src/nodes/lighting/LightingContextNode.js +115 -0
  818. package/src/nodes/lighting/LightingNode.js +36 -0
  819. package/src/nodes/lighting/LightsNode.js +432 -0
  820. package/src/nodes/lighting/PointLightNode.js +102 -0
  821. package/src/nodes/lighting/PointShadowNode.js +325 -0
  822. package/src/nodes/lighting/ProjectorLightNode.js +91 -0
  823. package/src/nodes/lighting/RectAreaLightNode.js +133 -0
  824. package/src/nodes/lighting/ShadowBaseNode.js +81 -0
  825. package/src/nodes/lighting/ShadowFilterNode.js +264 -0
  826. package/src/nodes/lighting/ShadowNode.js +889 -0
  827. package/src/nodes/lighting/SpotLightNode.js +168 -0
  828. package/src/nodes/materialx/DISCLAIMER.md +199 -0
  829. package/src/nodes/materialx/MaterialXNodes.js +197 -0
  830. package/src/nodes/materialx/lib/mx_hsv.js +127 -0
  831. package/src/nodes/materialx/lib/mx_noise.js +1491 -0
  832. package/src/nodes/materialx/lib/mx_transform_color.js +23 -0
  833. package/src/nodes/math/BitcastNode.js +156 -0
  834. package/src/nodes/math/BitcountNode.js +433 -0
  835. package/src/nodes/math/ConditionalNode.js +245 -0
  836. package/src/nodes/math/Hash.js +21 -0
  837. package/src/nodes/math/MathNode.js +1202 -0
  838. package/src/nodes/math/MathUtils.js +54 -0
  839. package/src/nodes/math/OperatorNode.js +752 -0
  840. package/src/nodes/math/PackFloatNode.js +98 -0
  841. package/src/nodes/math/TriNoise3D.js +71 -0
  842. package/src/nodes/math/UnpackFloatNode.js +96 -0
  843. package/src/nodes/parsers/GLSLNodeFunction.js +168 -0
  844. package/src/nodes/parsers/GLSLNodeParser.js +25 -0
  845. package/src/nodes/pmrem/PMREMNode.js +397 -0
  846. package/src/nodes/pmrem/PMREMUtils.js +397 -0
  847. package/src/nodes/procedural/Checker.js +22 -0
  848. package/src/nodes/shapes/Shapes.js +33 -0
  849. package/src/nodes/tsl/TSLBase.js +36 -0
  850. package/src/nodes/tsl/TSLCore.js +1250 -0
  851. package/src/nodes/utils/ArrayElementNode.js +90 -0
  852. package/src/nodes/utils/ConvertNode.js +100 -0
  853. package/src/nodes/utils/CubeMapNode.js +237 -0
  854. package/src/nodes/utils/DebugNode.js +83 -0
  855. package/src/nodes/utils/Discard.js +24 -0
  856. package/src/nodes/utils/EquirectUV.js +27 -0
  857. package/src/nodes/utils/EventNode.js +148 -0
  858. package/src/nodes/utils/FlipNode.js +106 -0
  859. package/src/nodes/utils/FunctionOverloadingNode.js +170 -0
  860. package/src/nodes/utils/JoinNode.js +117 -0
  861. package/src/nodes/utils/LoopNode.js +349 -0
  862. package/src/nodes/utils/MatcapUV.js +22 -0
  863. package/src/nodes/utils/MaxMipLevelNode.js +103 -0
  864. package/src/nodes/utils/MemberNode.js +120 -0
  865. package/src/nodes/utils/Oscillators.js +41 -0
  866. package/src/nodes/utils/Packing.js +33 -0
  867. package/src/nodes/utils/PostProcessingUtils.js +154 -0
  868. package/src/nodes/utils/RTTNode.js +289 -0
  869. package/src/nodes/utils/ReflectorNode.js +629 -0
  870. package/src/nodes/utils/Remap.js +48 -0
  871. package/src/nodes/utils/RotateNode.js +103 -0
  872. package/src/nodes/utils/SampleNode.js +91 -0
  873. package/src/nodes/utils/SetNode.js +108 -0
  874. package/src/nodes/utils/SplitNode.js +179 -0
  875. package/src/nodes/utils/SpriteSheetUV.js +35 -0
  876. package/src/nodes/utils/SpriteUtils.js +63 -0
  877. package/src/nodes/utils/StorageArrayElementNode.js +143 -0
  878. package/src/nodes/utils/Timer.js +26 -0
  879. package/src/nodes/utils/TriplanarTextures.js +65 -0
  880. package/src/nodes/utils/UVUtils.js +67 -0
  881. package/src/nodes/utils/ViewportUtils.js +26 -0
  882. package/src/objects/BatchedMesh.js +1682 -0
  883. package/src/objects/Bone.js +41 -0
  884. package/src/objects/ClippingGroup.js +68 -0
  885. package/src/objects/Group.js +41 -0
  886. package/src/objects/InstancedMesh.js +422 -0
  887. package/src/objects/LOD.js +329 -0
  888. package/src/objects/Line.js +329 -0
  889. package/src/objects/LineLoop.js +37 -0
  890. package/src/objects/LineSegments.js +74 -0
  891. package/src/objects/Mesh.js +496 -0
  892. package/src/objects/Points.js +228 -0
  893. package/src/objects/Skeleton.js +392 -0
  894. package/src/objects/SkinnedMesh.js +370 -0
  895. package/src/objects/Sprite.js +245 -0
  896. package/src/renderers/WebGL3DRenderTarget.js +48 -0
  897. package/src/renderers/WebGLArrayRenderTarget.js +48 -0
  898. package/src/renderers/WebGLCubeRenderTarget.js +182 -0
  899. package/src/renderers/WebGLRenderTarget.js +34 -0
  900. package/src/renderers/WebGLRenderer.js +3699 -0
  901. package/src/renderers/common/Animation.js +159 -0
  902. package/src/renderers/common/Attributes.js +132 -0
  903. package/src/renderers/common/Backend.js +769 -0
  904. package/src/renderers/common/Background.js +225 -0
  905. package/src/renderers/common/BindGroup.js +46 -0
  906. package/src/renderers/common/Binding.js +71 -0
  907. package/src/renderers/common/Bindings.js +394 -0
  908. package/src/renderers/common/BlendMode.js +143 -0
  909. package/src/renderers/common/Buffer.js +127 -0
  910. package/src/renderers/common/BufferUtils.js +58 -0
  911. package/src/renderers/common/BundleGroup.js +83 -0
  912. package/src/renderers/common/CanvasTarget.js +341 -0
  913. package/src/renderers/common/ChainMap.js +122 -0
  914. package/src/renderers/common/ClippingContext.js +262 -0
  915. package/src/renderers/common/Color4.js +77 -0
  916. package/src/renderers/common/ComputePipeline.js +41 -0
  917. package/src/renderers/common/Constants.js +15 -0
  918. package/src/renderers/common/CubeRenderTarget.js +147 -0
  919. package/src/renderers/common/DataMap.js +90 -0
  920. package/src/renderers/common/Geometries.js +396 -0
  921. package/src/renderers/common/IndirectStorageBufferAttribute.js +38 -0
  922. package/src/renderers/common/Info.js +501 -0
  923. package/src/renderers/common/InspectorBase.js +151 -0
  924. package/src/renderers/common/Lighting.js +57 -0
  925. package/src/renderers/common/Pipeline.js +35 -0
  926. package/src/renderers/common/Pipelines.js +498 -0
  927. package/src/renderers/common/PostProcessing.js +28 -0
  928. package/src/renderers/common/ProgrammableStage.js +78 -0
  929. package/src/renderers/common/QuadMesh.js +112 -0
  930. package/src/renderers/common/ReadbackBuffer.js +78 -0
  931. package/src/renderers/common/RenderBundle.js +26 -0
  932. package/src/renderers/common/RenderBundles.js +71 -0
  933. package/src/renderers/common/RenderContext.js +281 -0
  934. package/src/renderers/common/RenderContexts.js +100 -0
  935. package/src/renderers/common/RenderList.js +404 -0
  936. package/src/renderers/common/RenderLists.js +78 -0
  937. package/src/renderers/common/RenderObject.js +915 -0
  938. package/src/renderers/common/RenderObjectPipeline.js +40 -0
  939. package/src/renderers/common/RenderObjects.js +218 -0
  940. package/src/renderers/common/RenderPipeline.js +255 -0
  941. package/src/renderers/common/Renderer.js +3654 -0
  942. package/src/renderers/common/RendererUtils.js +200 -0
  943. package/src/renderers/common/SampledTexture.js +152 -0
  944. package/src/renderers/common/Sampler.js +165 -0
  945. package/src/renderers/common/Storage3DTexture.js +100 -0
  946. package/src/renderers/common/StorageArrayTexture.js +84 -0
  947. package/src/renderers/common/StorageBuffer.js +53 -0
  948. package/src/renderers/common/StorageBufferAttribute.js +46 -0
  949. package/src/renderers/common/StorageInstancedBufferAttribute.js +46 -0
  950. package/src/renderers/common/StorageTexture.js +86 -0
  951. package/src/renderers/common/Textures.js +629 -0
  952. package/src/renderers/common/TimestampQueryPool.js +163 -0
  953. package/src/renderers/common/Uniform.js +375 -0
  954. package/src/renderers/common/UniformBuffer.js +34 -0
  955. package/src/renderers/common/UniformsGroup.js +548 -0
  956. package/src/renderers/common/XRManager.js +1684 -0
  957. package/src/renderers/common/XRRenderTarget.js +91 -0
  958. package/src/renderers/common/extras/PMREMGenerator.js +1042 -0
  959. package/src/renderers/common/nodes/NodeBuilderState.js +152 -0
  960. package/src/renderers/common/nodes/NodeLibrary.js +196 -0
  961. package/src/renderers/common/nodes/NodeManager.js +983 -0
  962. package/src/renderers/common/nodes/NodeSampledTexture.js +140 -0
  963. package/src/renderers/common/nodes/NodeSampler.js +62 -0
  964. package/src/renderers/common/nodes/NodeStorageBuffer.js +72 -0
  965. package/src/renderers/common/nodes/NodeUniform.js +418 -0
  966. package/src/renderers/common/nodes/NodeUniformBuffer.js +105 -0
  967. package/src/renderers/common/nodes/NodeUniformsGroup.js +51 -0
  968. package/src/renderers/shaders/DFGLUTData.js +49 -0
  969. package/src/renderers/shaders/ShaderChunk/alphahash_fragment.glsl.js +7 -0
  970. package/src/renderers/shaders/ShaderChunk/alphahash_pars_fragment.glsl.js +68 -0
  971. package/src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl.js +7 -0
  972. package/src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl.js +7 -0
  973. package/src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl.js +16 -0
  974. package/src/renderers/shaders/ShaderChunk/alphatest_pars_fragment.glsl.js +5 -0
  975. package/src/renderers/shaders/ShaderChunk/aomap_fragment.glsl.js +26 -0
  976. package/src/renderers/shaders/ShaderChunk/aomap_pars_fragment.glsl.js +8 -0
  977. package/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js +49 -0
  978. package/src/renderers/shaders/ShaderChunk/batching_vertex.glsl.js +5 -0
  979. package/src/renderers/shaders/ShaderChunk/begin_vertex.glsl.js +9 -0
  980. package/src/renderers/shaders/ShaderChunk/beginnormal_vertex.glsl.js +9 -0
  981. package/src/renderers/shaders/ShaderChunk/bsdfs.glsl.js +33 -0
  982. package/src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl.js +43 -0
  983. package/src/renderers/shaders/ShaderChunk/clearcoat_normal_fragment_begin.glsl.js +7 -0
  984. package/src/renderers/shaders/ShaderChunk/clearcoat_normal_fragment_maps.glsl.js +10 -0
  985. package/src/renderers/shaders/ShaderChunk/clearcoat_pars_fragment.glsl.js +21 -0
  986. package/src/renderers/shaders/ShaderChunk/clipping_planes_fragment.glsl.js +78 -0
  987. package/src/renderers/shaders/ShaderChunk/clipping_planes_pars_fragment.glsl.js +9 -0
  988. package/src/renderers/shaders/ShaderChunk/clipping_planes_pars_vertex.glsl.js +7 -0
  989. package/src/renderers/shaders/ShaderChunk/clipping_planes_vertex.glsl.js +7 -0
  990. package/src/renderers/shaders/ShaderChunk/color_fragment.glsl.js +7 -0
  991. package/src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl.js +7 -0
  992. package/src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl.js +7 -0
  993. package/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js +29 -0
  994. package/src/renderers/shaders/ShaderChunk/colorspace_fragment.glsl.js +3 -0
  995. package/src/renderers/shaders/ShaderChunk/colorspace_pars_fragment.glsl.js +15 -0
  996. package/src/renderers/shaders/ShaderChunk/common.glsl.js +125 -0
  997. package/src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl.js +186 -0
  998. package/src/renderers/shaders/ShaderChunk/default_fragment.glsl.js +5 -0
  999. package/src/renderers/shaders/ShaderChunk/default_vertex.glsl.js +5 -0
  1000. package/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js +63 -0
  1001. package/src/renderers/shaders/ShaderChunk/displacementmap_pars_vertex.glsl.js +9 -0
  1002. package/src/renderers/shaders/ShaderChunk/displacementmap_vertex.glsl.js +7 -0
  1003. package/src/renderers/shaders/ShaderChunk/dithering_fragment.glsl.js +7 -0
  1004. package/src/renderers/shaders/ShaderChunk/dithering_pars_fragment.glsl.js +20 -0
  1005. package/src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl.js +17 -0
  1006. package/src/renderers/shaders/ShaderChunk/emissivemap_pars_fragment.glsl.js +7 -0
  1007. package/src/renderers/shaders/ShaderChunk/envmap_common_pars_fragment.glsl.js +14 -0
  1008. package/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js +58 -0
  1009. package/src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl.js +21 -0
  1010. package/src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl.js +22 -0
  1011. package/src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js +69 -0
  1012. package/src/renderers/shaders/ShaderChunk/envmap_vertex.glsl.js +37 -0
  1013. package/src/renderers/shaders/ShaderChunk/fog_fragment.glsl.js +17 -0
  1014. package/src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl.js +19 -0
  1015. package/src/renderers/shaders/ShaderChunk/fog_pars_vertex.glsl.js +7 -0
  1016. package/src/renderers/shaders/ShaderChunk/fog_vertex.glsl.js +7 -0
  1017. package/src/renderers/shaders/ShaderChunk/gradientmap_pars_fragment.glsl.js +27 -0
  1018. package/src/renderers/shaders/ShaderChunk/iridescence_fragment.glsl.js +120 -0
  1019. package/src/renderers/shaders/ShaderChunk/iridescence_pars_fragment.glsl.js +14 -0
  1020. package/src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl.js +8 -0
  1021. package/src/renderers/shaders/ShaderChunk/lightprobes_pars_fragment.glsl.js +80 -0
  1022. package/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js +213 -0
  1023. package/src/renderers/shaders/ShaderChunk/lights_fragment_end.glsl.js +19 -0
  1024. package/src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js +44 -0
  1025. package/src/renderers/shaders/ShaderChunk/lights_lambert_fragment.glsl.js +5 -0
  1026. package/src/renderers/shaders/ShaderChunk/lights_lambert_pars_fragment.glsl.js +28 -0
  1027. package/src/renderers/shaders/ShaderChunk/lights_pars_begin.glsl.js +216 -0
  1028. package/src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl.js +7 -0
  1029. package/src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl.js +32 -0
  1030. package/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js +161 -0
  1031. package/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +656 -0
  1032. package/src/renderers/shaders/ShaderChunk/lights_toon_fragment.glsl.js +4 -0
  1033. package/src/renderers/shaders/ShaderChunk/lights_toon_pars_fragment.glsl.js +26 -0
  1034. package/src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl.js +9 -0
  1035. package/src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl.js +9 -0
  1036. package/src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl.js +8 -0
  1037. package/src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl.js +8 -0
  1038. package/src/renderers/shaders/ShaderChunk/map_fragment.glsl.js +17 -0
  1039. package/src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl.js +7 -0
  1040. package/src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl.js +27 -0
  1041. package/src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl.js +27 -0
  1042. package/src/renderers/shaders/ShaderChunk/metalnessmap_fragment.glsl.js +12 -0
  1043. package/src/renderers/shaders/ShaderChunk/metalnessmap_pars_fragment.glsl.js +7 -0
  1044. package/src/renderers/shaders/ShaderChunk/morphcolor_vertex.glsl.js +24 -0
  1045. package/src/renderers/shaders/ShaderChunk/morphinstance_vertex.glsl.js +14 -0
  1046. package/src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl.js +16 -0
  1047. package/src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl.js +26 -0
  1048. package/src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl.js +16 -0
  1049. package/src/renderers/shaders/ShaderChunk/normal_fragment_begin.glsl.js +76 -0
  1050. package/src/renderers/shaders/ShaderChunk/normal_fragment_maps.glsl.js +40 -0
  1051. package/src/renderers/shaders/ShaderChunk/normal_pars_fragment.glsl.js +14 -0
  1052. package/src/renderers/shaders/ShaderChunk/normal_pars_vertex.glsl.js +14 -0
  1053. package/src/renderers/shaders/ShaderChunk/normal_vertex.glsl.js +14 -0
  1054. package/src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js +43 -0
  1055. package/src/renderers/shaders/ShaderChunk/opaque_fragment.glsl.js +11 -0
  1056. package/src/renderers/shaders/ShaderChunk/packing.glsl.js +116 -0
  1057. package/src/renderers/shaders/ShaderChunk/premultiplied_alpha_fragment.glsl.js +7 -0
  1058. package/src/renderers/shaders/ShaderChunk/project_vertex.glsl.js +19 -0
  1059. package/src/renderers/shaders/ShaderChunk/roughnessmap_fragment.glsl.js +12 -0
  1060. package/src/renderers/shaders/ShaderChunk/roughnessmap_pars_fragment.glsl.js +7 -0
  1061. package/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js +376 -0
  1062. package/src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl.js +71 -0
  1063. package/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl.js +78 -0
  1064. package/src/renderers/shaders/ShaderChunk/shadowmask_pars_fragment.glsl.js +66 -0
  1065. package/src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl.js +10 -0
  1066. package/src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl.js +25 -0
  1067. package/src/renderers/shaders/ShaderChunk/skinning_vertex.glsl.js +15 -0
  1068. package/src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl.js +20 -0
  1069. package/src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl.js +14 -0
  1070. package/src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl.js +7 -0
  1071. package/src/renderers/shaders/ShaderChunk/tonemapping_fragment.glsl.js +7 -0
  1072. package/src/renderers/shaders/ShaderChunk/tonemapping_pars_fragment.glsl.js +200 -0
  1073. package/src/renderers/shaders/ShaderChunk/transmission_fragment.glsl.js +36 -0
  1074. package/src/renderers/shaders/ShaderChunk/transmission_pars_fragment.glsl.js +235 -0
  1075. package/src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl.js +119 -0
  1076. package/src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl.js +145 -0
  1077. package/src/renderers/shaders/ShaderChunk/uv_vertex.glsl.js +122 -0
  1078. package/src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl.js +21 -0
  1079. package/src/renderers/shaders/ShaderChunk.js +272 -0
  1080. package/src/renderers/shaders/ShaderLib/background.glsl.js +40 -0
  1081. package/src/renderers/shaders/ShaderLib/backgroundCube.glsl.js +62 -0
  1082. package/src/renderers/shaders/ShaderLib/cube.glsl.js +36 -0
  1083. package/src/renderers/shaders/ShaderLib/depth.glsl.js +117 -0
  1084. package/src/renderers/shaders/ShaderLib/distance.glsl.js +77 -0
  1085. package/src/renderers/shaders/ShaderLib/equirect.glsl.js +35 -0
  1086. package/src/renderers/shaders/ShaderLib/linedashed.glsl.js +76 -0
  1087. package/src/renderers/shaders/ShaderLib/meshbasic.glsl.js +116 -0
  1088. package/src/renderers/shaders/ShaderLib/meshlambert.glsl.js +126 -0
  1089. package/src/renderers/shaders/ShaderLib/meshmatcap.glsl.js +112 -0
  1090. package/src/renderers/shaders/ShaderLib/meshnormal.glsl.js +86 -0
  1091. package/src/renderers/shaders/ShaderLib/meshphong.glsl.js +128 -0
  1092. package/src/renderers/shaders/ShaderLib/meshphysical.glsl.js +224 -0
  1093. package/src/renderers/shaders/ShaderLib/meshtoon.glsl.js +118 -0
  1094. package/src/renderers/shaders/ShaderLib/points.glsl.js +87 -0
  1095. package/src/renderers/shaders/ShaderLib/shadow.glsl.js +58 -0
  1096. package/src/renderers/shaders/ShaderLib/sprite.glsl.js +79 -0
  1097. package/src/renderers/shaders/ShaderLib/vsm.glsl.js +51 -0
  1098. package/src/renderers/shaders/ShaderLib.js +362 -0
  1099. package/src/renderers/shaders/UniformsLib.js +234 -0
  1100. package/src/renderers/shaders/UniformsUtils.js +154 -0
  1101. package/src/renderers/webgl/WebGLAnimation.js +54 -0
  1102. package/src/renderers/webgl/WebGLAttributes.js +237 -0
  1103. package/src/renderers/webgl/WebGLBackground.js +303 -0
  1104. package/src/renderers/webgl/WebGLBindingStates.js +666 -0
  1105. package/src/renderers/webgl/WebGLBufferRenderer.js +57 -0
  1106. package/src/renderers/webgl/WebGLCapabilities.js +150 -0
  1107. package/src/renderers/webgl/WebGLClipping.js +171 -0
  1108. package/src/renderers/webgl/WebGLEnvironments.js +228 -0
  1109. package/src/renderers/webgl/WebGLExtensions.js +61 -0
  1110. package/src/renderers/webgl/WebGLGeometries.js +186 -0
  1111. package/src/renderers/webgl/WebGLIndexedBufferRenderer.js +68 -0
  1112. package/src/renderers/webgl/WebGLInfo.js +73 -0
  1113. package/src/renderers/webgl/WebGLLights.js +584 -0
  1114. package/src/renderers/webgl/WebGLMaterials.js +602 -0
  1115. package/src/renderers/webgl/WebGLMorphtargets.js +168 -0
  1116. package/src/renderers/webgl/WebGLObjects.js +94 -0
  1117. package/src/renderers/webgl/WebGLOutput.js +270 -0
  1118. package/src/renderers/webgl/WebGLProgram.js +1031 -0
  1119. package/src/renderers/webgl/WebGLPrograms.js +679 -0
  1120. package/src/renderers/webgl/WebGLProperties.js +55 -0
  1121. package/src/renderers/webgl/WebGLRenderLists.js +253 -0
  1122. package/src/renderers/webgl/WebGLRenderStates.js +124 -0
  1123. package/src/renderers/webgl/WebGLShader.js +12 -0
  1124. package/src/renderers/webgl/WebGLShaderCache.js +124 -0
  1125. package/src/renderers/webgl/WebGLShadowMap.js +600 -0
  1126. package/src/renderers/webgl/WebGLState.js +1370 -0
  1127. package/src/renderers/webgl/WebGLTextures.js +2506 -0
  1128. package/src/renderers/webgl/WebGLUniforms.js +1203 -0
  1129. package/src/renderers/webgl/WebGLUniformsGroups.js +413 -0
  1130. package/src/renderers/webgl/WebGLUtils.js +217 -0
  1131. package/src/renderers/webgl-fallback/WebGLBackend.js +2783 -0
  1132. package/src/renderers/webgl-fallback/WebGLBufferRenderer.js +99 -0
  1133. package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +1676 -0
  1134. package/src/renderers/webgl-fallback/utils/WebGLAttributeUtils.js +359 -0
  1135. package/src/renderers/webgl-fallback/utils/WebGLCapabilities.js +89 -0
  1136. package/src/renderers/webgl-fallback/utils/WebGLConstants.js +13 -0
  1137. package/src/renderers/webgl-fallback/utils/WebGLExtensions.js +83 -0
  1138. package/src/renderers/webgl-fallback/utils/WebGLState.js +1361 -0
  1139. package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +1324 -0
  1140. package/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js +396 -0
  1141. package/src/renderers/webgl-fallback/utils/WebGLUtils.js +317 -0
  1142. package/src/renderers/webgpu/WebGPUBackend.js +2602 -0
  1143. package/src/renderers/webgpu/WebGPURenderer.Nodes.js +69 -0
  1144. package/src/renderers/webgpu/WebGPURenderer.js +107 -0
  1145. package/src/renderers/webgpu/nodes/BasicNodeLibrary.js +66 -0
  1146. package/src/renderers/webgpu/nodes/StandardNodeLibrary.js +100 -0
  1147. package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +2523 -0
  1148. package/src/renderers/webgpu/nodes/WGSLNodeFunction.js +187 -0
  1149. package/src/renderers/webgpu/nodes/WGSLNodeParser.js +25 -0
  1150. package/src/renderers/webgpu/utils/WebGPUAttributeUtils.js +522 -0
  1151. package/src/renderers/webgpu/utils/WebGPUBindingUtils.js +642 -0
  1152. package/src/renderers/webgpu/utils/WebGPUCapabilities.js +48 -0
  1153. package/src/renderers/webgpu/utils/WebGPUConstants.js +354 -0
  1154. package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +846 -0
  1155. package/src/renderers/webgpu/utils/WebGPUTexturePassUtils.js +389 -0
  1156. package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +1673 -0
  1157. package/src/renderers/webgpu/utils/WebGPUTimestampQueryPool.js +310 -0
  1158. package/src/renderers/webgpu/utils/WebGPUUtils.js +269 -0
  1159. package/src/renderers/webxr/WebXRController.js +440 -0
  1160. package/src/renderers/webxr/WebXRDepthSensing.js +155 -0
  1161. package/src/renderers/webxr/WebXRManager.js +1103 -0
  1162. package/src/scenes/Fog.js +98 -0
  1163. package/src/scenes/FogExp2.js +86 -0
  1164. package/src/scenes/Scene.js +165 -0
  1165. package/src/textures/CanvasTexture.js +45 -0
  1166. package/src/textures/CompressedArrayTexture.js +89 -0
  1167. package/src/textures/CompressedCubeTexture.js +48 -0
  1168. package/src/textures/CompressedTexture.js +86 -0
  1169. package/src/textures/CubeDepthTexture.js +76 -0
  1170. package/src/textures/CubeTexture.js +81 -0
  1171. package/src/textures/Data3DTexture.js +112 -0
  1172. package/src/textures/DataArrayTexture.js +134 -0
  1173. package/src/textures/DataTexture.js +87 -0
  1174. package/src/textures/DepthTexture.js +104 -0
  1175. package/src/textures/ExternalTexture.js +56 -0
  1176. package/src/textures/FramebufferTexture.js +85 -0
  1177. package/src/textures/HTMLTexture.js +74 -0
  1178. package/src/textures/Source.js +230 -0
  1179. package/src/textures/Texture.js +812 -0
  1180. package/src/textures/VideoFrameTexture.js +72 -0
  1181. package/src/textures/VideoTexture.js +127 -0
  1182. package/src/utils.js +493 -0
@@ -0,0 +1,4594 @@
1
+ import {
2
+ AnimationClip,
3
+ BoxGeometry,
4
+ BufferAttribute,
5
+ BufferGeometry,
6
+ CapsuleGeometry,
7
+ ClampToEdgeWrapping,
8
+ Color,
9
+ ConeGeometry,
10
+ CylinderGeometry,
11
+ DirectionalLight,
12
+ Euler,
13
+ Group,
14
+ Matrix4,
15
+ Mesh,
16
+ MeshPhysicalMaterial,
17
+ MirroredRepeatWrapping,
18
+ NoColorSpace,
19
+ Object3D,
20
+ OrthographicCamera,
21
+ PerspectiveCamera,
22
+ PointLight,
23
+ Quaternion,
24
+ QuaternionKeyframeTrack,
25
+ RectAreaLight,
26
+ RepeatWrapping,
27
+ ShapeUtils,
28
+ SkinnedMesh,
29
+ Skeleton,
30
+ Bone,
31
+ SphereGeometry,
32
+ SpotLight,
33
+ SRGBColorSpace,
34
+ Texture,
35
+ Vector2,
36
+ Vector3,
37
+ VectorKeyframeTrack
38
+ } from 'three';
39
+
40
+ // Pre-compiled regex patterns for performance
41
+ const VARIANT_PATH_REGEX = /^(.+?)\/\{(\w+)=(\w+)\}\/(.+)$/;
42
+
43
+ // Spec types (must match USDCParser)
44
+ const SpecType = {
45
+ Unknown: 0,
46
+ Attribute: 1,
47
+ Connection: 2,
48
+ Expression: 3,
49
+ Mapper: 4,
50
+ MapperArg: 5,
51
+ Prim: 6,
52
+ PseudoRoot: 7,
53
+ Relationship: 8,
54
+ RelationshipTarget: 9,
55
+ Variant: 10,
56
+ VariantSet: 11
57
+ };
58
+
59
+ // UsdGeomCamera fallback values (OpenUSD schema)
60
+ const USD_CAMERA_DEFAULTS = {
61
+ projection: 'perspective',
62
+ clippingRange: [ 1, 1000000 ],
63
+ horizontalAperture: 20.955,
64
+ verticalAperture: 15.2908,
65
+ horizontalApertureOffset: 0,
66
+ verticalApertureOffset: 0,
67
+ focalLength: 50,
68
+ focusDistance: 0,
69
+ fStop: 0
70
+ };
71
+
72
+ /**
73
+ * USDComposer handles scene composition from parsed USD data.
74
+ * This includes reference resolution, variant selection, transform handling,
75
+ * and building the Three.js scene graph.
76
+ *
77
+ * Works with specsByPath format from USDCParser.
78
+ */
79
+ class USDComposer {
80
+
81
+ constructor( manager = null ) {
82
+
83
+ this.textureCache = {};
84
+ this.skinnedMeshes = [];
85
+ this.manager = manager;
86
+
87
+ }
88
+
89
+ /**
90
+ * Compose a Three.js scene from parsed USD data.
91
+ * @param {Object} parsedData - Data from USDCParser or USDAParser
92
+ * @param {Object} assets - Dictionary of referenced assets (specsByPath or blob URLs)
93
+ * @param {Object} variantSelections - External variant selections
94
+ * @param {string} basePath - Base path for resolving relative references
95
+ * @returns {Group} Three.js scene graph
96
+ */
97
+ compose( parsedData, assets = {}, variantSelections = {}, basePath = '' ) {
98
+
99
+ this.specsByPath = parsedData.specsByPath;
100
+ this.assets = assets;
101
+ this.externalVariantSelections = variantSelections;
102
+ this.basePath = basePath;
103
+ this.skinnedMeshes = [];
104
+ this.skeletons = {};
105
+
106
+ // Build indexes for O(1) lookups
107
+ this._buildIndexes();
108
+
109
+ // Get FPS from root spec
110
+ const rootSpec = this.specsByPath[ '/' ];
111
+ const rootFields = rootSpec ? rootSpec.fields : {};
112
+ this.fps = rootFields.framesPerSecond || rootFields.timeCodesPerSecond || 30;
113
+
114
+ const group = new Group();
115
+ this._buildHierarchy( group, '/' );
116
+
117
+ // Bind skeletons to skinned meshes
118
+ this._bindSkeletons();
119
+
120
+ // Expose skeleton on the root group so that AnimationMixer's
121
+ // PropertyBinding.findNode resolves bone names before scene objects.
122
+ // Without this, Xform prims that share a name with a skeleton joint
123
+ // would be animated instead of the bone.
124
+ const skeletonPaths = Object.keys( this.skeletons );
125
+ if ( skeletonPaths.length === 1 ) {
126
+
127
+ group.skeleton = this.skeletons[ skeletonPaths[ 0 ] ].skeleton;
128
+
129
+ }
130
+
131
+ // Build animations
132
+ group.animations = this._buildAnimations();
133
+
134
+ // Handle metersPerUnit scaling
135
+ const metersPerUnit = rootFields.metersPerUnit;
136
+
137
+ if ( metersPerUnit !== undefined && metersPerUnit !== 1 ) {
138
+
139
+ group.scale.setScalar( metersPerUnit );
140
+
141
+ }
142
+
143
+ // Handle Z-up to Y-up conversion
144
+ if ( rootSpec && rootSpec.fields && rootSpec.fields.upAxis === 'Z' ) {
145
+
146
+ group.rotation.x = - Math.PI / 2;
147
+
148
+ }
149
+
150
+ return group;
151
+
152
+ }
153
+
154
+ /**
155
+ * Apply USD transforms to a Three.js object.
156
+ * Handles xformOpOrder with proper matrix composition.
157
+ * USD uses row-vector convention, Three.js uses column-vector.
158
+ */
159
+ applyTransform( obj, fields, attrs = {} ) {
160
+
161
+ const data = { ...fields, ...attrs };
162
+ const xformOpOrder = data[ 'xformOpOrder' ];
163
+
164
+ // If we have xformOpOrder, apply transforms using matrices
165
+ if ( xformOpOrder && xformOpOrder.length > 0 ) {
166
+
167
+ const matrix = new Matrix4();
168
+ const tempMatrix = new Matrix4();
169
+
170
+ // Track scale for handling negative scale with rotation
171
+ let scaleValues = null;
172
+
173
+ // Iterate FORWARD for Three.js column-vector convention
174
+ for ( let i = 0; i < xformOpOrder.length; i ++ ) {
175
+
176
+ const op = xformOpOrder[ i ];
177
+ const isInverse = op.startsWith( '!invert!' );
178
+ const opName = isInverse ? op.slice( 8 ) : op;
179
+
180
+ if ( opName === 'xformOp:transform' ) {
181
+
182
+ const m = data[ 'xformOp:transform' ];
183
+ if ( m && m.length === 16 ) {
184
+
185
+ tempMatrix.set(
186
+ m[ 0 ], m[ 4 ], m[ 8 ], m[ 12 ],
187
+ m[ 1 ], m[ 5 ], m[ 9 ], m[ 13 ],
188
+ m[ 2 ], m[ 6 ], m[ 10 ], m[ 14 ],
189
+ m[ 3 ], m[ 7 ], m[ 11 ], m[ 15 ]
190
+ );
191
+ if ( isInverse ) tempMatrix.invert();
192
+ matrix.multiply( tempMatrix );
193
+
194
+ }
195
+
196
+ } else if ( opName === 'xformOp:translate' ) {
197
+
198
+ const t = data[ 'xformOp:translate' ];
199
+ if ( t ) {
200
+
201
+ tempMatrix.makeTranslation( t[ 0 ], t[ 1 ], t[ 2 ] );
202
+ if ( isInverse ) tempMatrix.invert();
203
+ matrix.multiply( tempMatrix );
204
+
205
+ }
206
+
207
+ } else if ( opName === 'xformOp:translate:pivot' ) {
208
+
209
+ const t = data[ 'xformOp:translate:pivot' ];
210
+ if ( t ) {
211
+
212
+ tempMatrix.makeTranslation( t[ 0 ], t[ 1 ], t[ 2 ] );
213
+ if ( isInverse ) tempMatrix.invert();
214
+ matrix.multiply( tempMatrix );
215
+
216
+ }
217
+
218
+ } else if ( opName === 'xformOp:scale' ) {
219
+
220
+ const s = data[ 'xformOp:scale' ];
221
+ if ( s ) {
222
+
223
+ if ( Array.isArray( s ) ) {
224
+
225
+ tempMatrix.makeScale( s[ 0 ], s[ 1 ], s[ 2 ] );
226
+ scaleValues = [ s[ 0 ], s[ 1 ], s[ 2 ] ];
227
+
228
+ } else {
229
+
230
+ tempMatrix.makeScale( s, s, s );
231
+ scaleValues = [ s, s, s ];
232
+
233
+ }
234
+
235
+ if ( isInverse ) tempMatrix.invert();
236
+ matrix.multiply( tempMatrix );
237
+
238
+ }
239
+
240
+ } else if ( opName === 'xformOp:rotateXYZ' ) {
241
+
242
+ const r = data[ 'xformOp:rotateXYZ' ];
243
+ if ( r ) {
244
+
245
+ // USD rotateXYZ: matrix = Rx * Ry * Rz
246
+ // Three.js Euler 'ZYX' order produces same result
247
+ const euler = new Euler(
248
+ r[ 0 ] * Math.PI / 180,
249
+ r[ 1 ] * Math.PI / 180,
250
+ r[ 2 ] * Math.PI / 180,
251
+ 'ZYX'
252
+ );
253
+ tempMatrix.makeRotationFromEuler( euler );
254
+ if ( isInverse ) tempMatrix.invert();
255
+ matrix.multiply( tempMatrix );
256
+
257
+ }
258
+
259
+ } else if ( opName === 'xformOp:rotateX' ) {
260
+
261
+ const r = data[ 'xformOp:rotateX' ];
262
+ if ( r !== undefined ) {
263
+
264
+ tempMatrix.makeRotationX( r * Math.PI / 180 );
265
+ if ( isInverse ) tempMatrix.invert();
266
+ matrix.multiply( tempMatrix );
267
+
268
+ }
269
+
270
+ } else if ( opName === 'xformOp:rotateY' ) {
271
+
272
+ const r = data[ 'xformOp:rotateY' ];
273
+ if ( r !== undefined ) {
274
+
275
+ tempMatrix.makeRotationY( r * Math.PI / 180 );
276
+ if ( isInverse ) tempMatrix.invert();
277
+ matrix.multiply( tempMatrix );
278
+
279
+ }
280
+
281
+ } else if ( opName === 'xformOp:rotateZ' ) {
282
+
283
+ const r = data[ 'xformOp:rotateZ' ];
284
+ if ( r !== undefined ) {
285
+
286
+ tempMatrix.makeRotationZ( r * Math.PI / 180 );
287
+ if ( isInverse ) tempMatrix.invert();
288
+ matrix.multiply( tempMatrix );
289
+
290
+ }
291
+
292
+ } else if ( opName === 'xformOp:orient' ) {
293
+
294
+ const q = data[ 'xformOp:orient' ];
295
+ if ( q && q.length === 4 ) {
296
+
297
+ const quat = new Quaternion( q[ 0 ], q[ 1 ], q[ 2 ], q[ 3 ] );
298
+ tempMatrix.makeRotationFromQuaternion( quat );
299
+ if ( isInverse ) tempMatrix.invert();
300
+ matrix.multiply( tempMatrix );
301
+
302
+ }
303
+
304
+ }
305
+
306
+ }
307
+
308
+ obj.matrix.copy( matrix );
309
+ obj.matrix.decompose( obj.position, obj.quaternion, obj.scale );
310
+
311
+ // Fix for negative scale: decompose() may absorb negative scale into quaternion
312
+ // Restore original scale signs to keep animation consistent
313
+ if ( scaleValues ) {
314
+
315
+ const negX = scaleValues[ 0 ] < 0;
316
+ const negY = scaleValues[ 1 ] < 0;
317
+ const negZ = scaleValues[ 2 ] < 0;
318
+ const negCount = ( negX ? 1 : 0 ) + ( negY ? 1 : 0 ) + ( negZ ? 1 : 0 );
319
+
320
+ // decompose() absorbs pairs of negative scales into rotation
321
+ // For [-1,-1,-1] → [-1,1,1], Y and Z were absorbed, flip quat.y and quat.w
322
+ if ( negCount === 3 ) {
323
+
324
+ obj.scale.set( scaleValues[ 0 ], scaleValues[ 1 ], scaleValues[ 2 ] );
325
+ obj.quaternion.set(
326
+ obj.quaternion.x,
327
+ - obj.quaternion.y,
328
+ obj.quaternion.z,
329
+ - obj.quaternion.w
330
+ );
331
+
332
+ }
333
+
334
+ }
335
+
336
+ return;
337
+
338
+ }
339
+
340
+ // Fallback: handle individual transform ops without order
341
+ if ( data[ 'xformOp:translate' ] ) {
342
+
343
+ const t = data[ 'xformOp:translate' ];
344
+ obj.position.set( t[ 0 ], t[ 1 ], t[ 2 ] );
345
+
346
+ }
347
+
348
+ if ( data[ 'xformOp:translate:pivot' ] ) {
349
+
350
+ const p = data[ 'xformOp:translate:pivot' ];
351
+ obj.pivot = new Vector3( p[ 0 ], p[ 1 ], p[ 2 ] );
352
+
353
+ }
354
+
355
+ if ( data[ 'xformOp:scale' ] ) {
356
+
357
+ const s = data[ 'xformOp:scale' ];
358
+
359
+ if ( Array.isArray( s ) ) {
360
+
361
+ obj.scale.set( s[ 0 ], s[ 1 ], s[ 2 ] );
362
+
363
+ } else {
364
+
365
+ obj.scale.set( s, s, s );
366
+
367
+ }
368
+
369
+ }
370
+
371
+ if ( data[ 'xformOp:rotateXYZ' ] ) {
372
+
373
+ const r = data[ 'xformOp:rotateXYZ' ];
374
+ obj.rotation.set(
375
+ r[ 0 ] * Math.PI / 180,
376
+ r[ 1 ] * Math.PI / 180,
377
+ r[ 2 ] * Math.PI / 180
378
+ );
379
+
380
+ }
381
+
382
+ if ( data[ 'xformOp:orient' ] ) {
383
+
384
+ const q = data[ 'xformOp:orient' ];
385
+ if ( q.length === 4 ) {
386
+
387
+ obj.quaternion.set( q[ 0 ], q[ 1 ], q[ 2 ], q[ 3 ] );
388
+
389
+ }
390
+
391
+ }
392
+
393
+ }
394
+
395
+ /**
396
+ * Build indexes for efficient lookups.
397
+ * Called once during compose() to avoid O(n) scans per lookup.
398
+ */
399
+ _buildIndexes() {
400
+
401
+ // childrenByPath: parentPath -> [childName1, childName2, ...]
402
+ this.childrenByPath = new Map();
403
+
404
+ // attributesByPrimPath: primPath -> Map(attrName -> attrSpec)
405
+ this.attributesByPrimPath = new Map();
406
+
407
+ // materialsByRoot: rootPath -> [materialPath1, materialPath2, ...]
408
+ this.materialsByRoot = new Map();
409
+
410
+ // shadersByMaterialPath: materialPath -> [shaderPath1, shaderPath2, ...]
411
+ this.shadersByMaterialPath = new Map();
412
+
413
+ // geomSubsetsByMeshPath: meshPath -> [subsetPath1, subsetPath2, ...]
414
+ this.geomSubsetsByMeshPath = new Map();
415
+
416
+ for ( const path in this.specsByPath ) {
417
+
418
+ const spec = this.specsByPath[ path ];
419
+
420
+ if ( spec.specType === SpecType.Prim ) {
421
+
422
+ // Build parent-child index
423
+ const lastSlash = path.lastIndexOf( '/' );
424
+
425
+ if ( lastSlash > 0 ) {
426
+
427
+ const parentPath = path.slice( 0, lastSlash );
428
+ const childName = path.slice( lastSlash + 1 );
429
+
430
+ if ( ! this.childrenByPath.has( parentPath ) ) {
431
+
432
+ this.childrenByPath.set( parentPath, [] );
433
+
434
+ }
435
+
436
+ this.childrenByPath.get( parentPath ).push( { name: childName, path: path } );
437
+
438
+ } else if ( lastSlash === 0 && path.length > 1 ) {
439
+
440
+ // Direct child of root
441
+ const childName = path.slice( 1 );
442
+
443
+ if ( ! this.childrenByPath.has( '/' ) ) {
444
+
445
+ this.childrenByPath.set( '/', [] );
446
+
447
+ }
448
+
449
+ this.childrenByPath.get( '/' ).push( { name: childName, path: path } );
450
+
451
+ }
452
+
453
+ const typeName = spec.fields.typeName;
454
+
455
+ // Build material index
456
+ if ( typeName === 'Material' ) {
457
+
458
+ const parts = path.split( '/' );
459
+ const rootPath = parts.length > 1 ? '/' + parts[ 1 ] : '/';
460
+
461
+ if ( ! this.materialsByRoot.has( rootPath ) ) {
462
+
463
+ this.materialsByRoot.set( rootPath, [] );
464
+
465
+ }
466
+
467
+ this.materialsByRoot.get( rootPath ).push( path );
468
+
469
+ }
470
+
471
+ // Build shader index (shaders are children or descendants of materials)
472
+ if ( typeName === 'Shader' && lastSlash > 0 ) {
473
+
474
+ // Walk up ancestors to find the nearest Material prim.
475
+ // Shaders may be direct children of a Material, or nested
476
+ // inside a NodeGraph (common with MaterialX materials).
477
+
478
+ let ancestorPath = path.slice( 0, lastSlash );
479
+
480
+ while ( ancestorPath.length > 0 ) {
481
+
482
+ const ancestorSpec = this.specsByPath[ ancestorPath ];
483
+
484
+ if ( ancestorSpec && ancestorSpec.specType === SpecType.Prim && ancestorSpec.fields.typeName === 'Material' ) {
485
+
486
+ if ( ! this.shadersByMaterialPath.has( ancestorPath ) ) {
487
+
488
+ this.shadersByMaterialPath.set( ancestorPath, [] );
489
+
490
+ }
491
+
492
+ this.shadersByMaterialPath.get( ancestorPath ).push( path );
493
+ break;
494
+
495
+ }
496
+
497
+ const slash = ancestorPath.lastIndexOf( '/' );
498
+ if ( slash <= 0 ) break;
499
+ ancestorPath = ancestorPath.slice( 0, slash );
500
+
501
+ }
502
+
503
+ }
504
+
505
+ // Build GeomSubset index (subsets are children of meshes)
506
+ if ( typeName === 'GeomSubset' && lastSlash > 0 ) {
507
+
508
+ const meshPath = path.slice( 0, lastSlash );
509
+
510
+ if ( ! this.geomSubsetsByMeshPath.has( meshPath ) ) {
511
+
512
+ this.geomSubsetsByMeshPath.set( meshPath, [] );
513
+
514
+ }
515
+
516
+ this.geomSubsetsByMeshPath.get( meshPath ).push( path );
517
+
518
+ }
519
+
520
+ } else if ( spec.specType === SpecType.Attribute || spec.specType === SpecType.Relationship ) {
521
+
522
+ // Build attribute index
523
+ const dotIndex = path.lastIndexOf( '.' );
524
+
525
+ if ( dotIndex > 0 ) {
526
+
527
+ const primPath = path.slice( 0, dotIndex );
528
+ const attrName = path.slice( dotIndex + 1 );
529
+
530
+ if ( ! this.attributesByPrimPath.has( primPath ) ) {
531
+
532
+ this.attributesByPrimPath.set( primPath, new Map() );
533
+
534
+ }
535
+
536
+ this.attributesByPrimPath.get( primPath ).set( attrName, spec );
537
+
538
+ }
539
+
540
+ }
541
+
542
+ }
543
+
544
+ }
545
+
546
+ /**
547
+ * Check if a path is a direct child of parentPath.
548
+ */
549
+ _isDirectChild( parentPath, path, prefix ) {
550
+
551
+ if ( ! path.startsWith( prefix ) ) return false;
552
+
553
+ const remainder = path.slice( prefix.length );
554
+ if ( remainder.length === 0 ) return false;
555
+
556
+ // Check for variant paths or simple names
557
+ if ( remainder.startsWith( '{' ) ) {
558
+
559
+ return false; // Variant paths are not direct children
560
+
561
+ }
562
+
563
+ return ! remainder.includes( '/' );
564
+
565
+ }
566
+
567
+ /**
568
+ * Build the scene hierarchy recursively.
569
+ * Uses childrenByPath index for O(1) child lookup instead of O(n) iteration.
570
+ */
571
+ _buildHierarchy( parent, parentPath ) {
572
+
573
+ // Collect children from parentPath and any active variant paths
574
+ const childEntries = [];
575
+ const seenPaths = new Set();
576
+
577
+ // Get direct children using the index
578
+ const directChildren = this.childrenByPath.get( parentPath );
579
+
580
+ if ( directChildren ) {
581
+
582
+ for ( const child of directChildren ) {
583
+
584
+ if ( ! seenPaths.has( child.path ) ) {
585
+
586
+ seenPaths.add( child.path );
587
+ childEntries.push( child );
588
+
589
+ }
590
+
591
+ }
592
+
593
+ }
594
+
595
+ // Also get children from active variant paths
596
+ const variantPaths = this._getVariantPaths( parentPath );
597
+
598
+ for ( const vp of variantPaths ) {
599
+
600
+ const variantChildren = this.childrenByPath.get( vp );
601
+
602
+ if ( variantChildren ) {
603
+
604
+ for ( const child of variantChildren ) {
605
+
606
+ if ( ! seenPaths.has( child.path ) ) {
607
+
608
+ seenPaths.add( child.path );
609
+ childEntries.push( child );
610
+
611
+ }
612
+
613
+ }
614
+
615
+ }
616
+
617
+ }
618
+
619
+ // Process each child
620
+ for ( const { name, path } of childEntries ) {
621
+
622
+ const spec = this.specsByPath[ path ];
623
+ if ( ! spec || spec.specType !== SpecType.Prim ) continue;
624
+
625
+ const typeName = spec.fields.typeName;
626
+
627
+ // Check for references/payloads
628
+ const refValues = this._getReferences( spec );
629
+ if ( refValues.length > 0 ) {
630
+
631
+ // Get local variant selections from this prim
632
+ const localVariants = this._getLocalVariantSelections( spec.fields );
633
+
634
+ // Resolve all references
635
+ const resolvedGroups = [];
636
+ for ( const refValue of refValues ) {
637
+
638
+ const referencedGroup = this._resolveReference( refValue, localVariants );
639
+ if ( referencedGroup ) resolvedGroups.push( referencedGroup );
640
+
641
+ }
642
+
643
+ if ( resolvedGroups.length > 0 ) {
644
+
645
+ const attrs = this._getAttributes( path );
646
+
647
+ // Single reference with single mesh: use optimized path
648
+ // This handles the USDZExporter pattern: Xform references geometry file
649
+ if ( resolvedGroups.length === 1 ) {
650
+
651
+ const singleMesh = this._findSingleMesh( resolvedGroups[ 0 ] );
652
+
653
+ if ( singleMesh && ( typeName === 'Xform' || ! typeName ) ) {
654
+
655
+ // Merge the mesh into this prim
656
+ singleMesh.name = name;
657
+ this.applyTransform( singleMesh, spec.fields, attrs );
658
+
659
+ // Apply material binding from the referencing prim if present
660
+ this._applyMaterialBinding( singleMesh, path );
661
+
662
+ parent.add( singleMesh );
663
+
664
+ // Still build local children (overrides)
665
+ this._buildHierarchy( singleMesh, path );
666
+
667
+ continue;
668
+
669
+ }
670
+
671
+ }
672
+
673
+ // Create a container for the referenced content
674
+ const obj = new Object3D();
675
+ obj.name = name;
676
+ this.applyTransform( obj, spec.fields, attrs );
677
+
678
+ // Add all children from all resolved references
679
+ for ( const referencedGroup of resolvedGroups ) {
680
+
681
+ while ( referencedGroup.children.length > 0 ) {
682
+
683
+ obj.add( referencedGroup.children[ 0 ] );
684
+
685
+ }
686
+
687
+ }
688
+
689
+ parent.add( obj );
690
+
691
+ // Still build local children (overrides)
692
+ this._buildHierarchy( obj, path );
693
+
694
+ continue;
695
+
696
+ }
697
+
698
+ }
699
+
700
+ // Build appropriate object based on type
701
+ if ( typeName === 'SkelRoot' ) {
702
+
703
+ // Skeletal root - treat as transform but track for skeleton binding
704
+ const obj = new Object3D();
705
+ obj.name = name;
706
+ obj.userData.isSkelRoot = true;
707
+ const attrs = this._getAttributes( path );
708
+ this.applyTransform( obj, spec.fields, attrs );
709
+ parent.add( obj );
710
+ this._buildHierarchy( obj, path );
711
+
712
+ } else if ( typeName === 'Skeleton' ) {
713
+
714
+ // Build skeleton and store it
715
+ const skeleton = this._buildSkeleton( path );
716
+ if ( skeleton ) {
717
+
718
+ this.skeletons[ path ] = skeleton;
719
+
720
+ }
721
+
722
+ // Recursively build children (may contain SkelAnimation)
723
+ this._buildHierarchy( parent, path );
724
+
725
+ } else if ( typeName === 'SkelAnimation' ) {
726
+
727
+ // Skip - animations are processed separately in _buildAnimations
728
+
729
+ } else if ( typeName === 'Mesh' ) {
730
+
731
+ const obj = this._buildMesh( path, spec );
732
+ if ( obj ) {
733
+
734
+ parent.add( obj );
735
+ this._buildHierarchy( obj, path );
736
+
737
+ }
738
+
739
+ } else if ( typeName === 'Camera' ) {
740
+
741
+ const obj = this._buildCamera( path );
742
+ obj.name = name;
743
+ const attrs = this._getAttributes( path );
744
+ this.applyTransform( obj, spec.fields, attrs );
745
+ parent.add( obj );
746
+ this._buildHierarchy( obj, path );
747
+
748
+ } else if ( typeName === 'DistantLight' || typeName === 'SphereLight' || typeName === 'RectLight' || typeName === 'DiskLight' ) {
749
+
750
+ const obj = this._buildLight( path, typeName );
751
+ obj.name = name;
752
+ const attrs = this._getAttributes( path );
753
+ this.applyTransform( obj, spec.fields, attrs );
754
+ parent.add( obj );
755
+ this._buildHierarchy( obj, path );
756
+
757
+ } else if ( typeName === 'Cube' || typeName === 'Sphere' || typeName === 'Cylinder' || typeName === 'Cone' || typeName === 'Capsule' ) {
758
+
759
+ const obj = this._buildGeomPrimitive( path, spec, typeName );
760
+ if ( obj ) {
761
+
762
+ parent.add( obj );
763
+ this._buildHierarchy( obj, path );
764
+
765
+ }
766
+
767
+ } else if ( typeName === 'Material' || typeName === 'Shader' || typeName === 'GeomSubset' ) {
768
+
769
+ // Skip materials/shaders/subsets, they're referenced by meshes
770
+
771
+ } else {
772
+
773
+ // Transform node, group, or unknown type
774
+ const obj = new Object3D();
775
+ obj.name = name;
776
+ const attrs = this._getAttributes( path );
777
+ this.applyTransform( obj, spec.fields, attrs );
778
+ parent.add( obj );
779
+ this._buildHierarchy( obj, path );
780
+
781
+ }
782
+
783
+ }
784
+
785
+ }
786
+
787
+ /**
788
+ * Get variant paths for a parent path based on variant selections.
789
+ */
790
+ _getVariantPaths( parentPath ) {
791
+
792
+ const parentSpec = this.specsByPath[ parentPath ];
793
+ const variantSetChildren = parentSpec?.fields?.variantSetChildren;
794
+ const variantPaths = [];
795
+
796
+ if ( ! variantSetChildren || variantSetChildren.length === 0 ) {
797
+
798
+ return variantPaths;
799
+
800
+ }
801
+
802
+ for ( const variantSetName of variantSetChildren ) {
803
+
804
+ // External selections take priority
805
+ let selectedVariant = this.externalVariantSelections[ variantSetName ] || null;
806
+
807
+ // Fall back to file's internal selection
808
+ if ( ! selectedVariant ) {
809
+
810
+ const variantSelection = parentSpec.fields.variantSelection;
811
+ selectedVariant = variantSelection ? variantSelection[ variantSetName ] : null;
812
+
813
+ }
814
+
815
+ // Fall back to first variant child
816
+ if ( ! selectedVariant ) {
817
+
818
+ const variantSetPath = parentPath + '/{' + variantSetName + '=}';
819
+ const variantSetSpec = this.specsByPath[ variantSetPath ];
820
+ if ( variantSetSpec?.fields?.variantChildren ) {
821
+
822
+ selectedVariant = variantSetSpec.fields.variantChildren[ 0 ];
823
+
824
+ }
825
+
826
+ }
827
+
828
+ if ( selectedVariant ) {
829
+
830
+ const variantPath = parentPath + '/{' + variantSetName + '=' + selectedVariant + '}';
831
+ variantPaths.push( variantPath );
832
+
833
+ }
834
+
835
+ }
836
+
837
+ return variantPaths;
838
+
839
+ }
840
+
841
+ /**
842
+ * Resolve a file path relative to basePath.
843
+ */
844
+ _resolveFilePath( refPath ) {
845
+
846
+ let cleanPath = refPath;
847
+
848
+ // Remove ./ prefix
849
+ if ( cleanPath.startsWith( './' ) ) {
850
+
851
+ cleanPath = cleanPath.slice( 2 );
852
+
853
+ }
854
+
855
+ // Combine with base path
856
+ if ( this.basePath ) {
857
+
858
+ return this.basePath + '/' + cleanPath;
859
+
860
+ }
861
+
862
+ return cleanPath;
863
+
864
+ }
865
+
866
+ /**
867
+ * Resolve a USD reference and return the composed content.
868
+ * @param {string} refValue - Reference value like "@./path/to/file.usdc@"
869
+ * @param {Object} localVariants - Variant selections to apply
870
+ * @returns {Group|null} Composed content or null
871
+ */
872
+ _resolveReference( refValue, localVariants = {} ) {
873
+
874
+ if ( ! refValue ) return null;
875
+
876
+ const match = refValue.match( /@([^@]+)@(?:<([^>]+)>)?/ );
877
+ if ( ! match ) return null;
878
+
879
+ const filePath = match[ 1 ];
880
+ const primPath = match[ 2 ]; // e.g., "/Geometry"
881
+
882
+ const resolvedPath = this._resolveFilePath( filePath );
883
+
884
+ // Merge variant selections - external takes priority, then local
885
+ const mergedVariants = { ...localVariants, ...this.externalVariantSelections };
886
+
887
+ // Look up pre-parsed data in assets
888
+ const referencedData = this.assets[ resolvedPath ];
889
+ if ( ! referencedData ) return null;
890
+
891
+ // If it's specsByPath data, compose it
892
+ if ( referencedData.specsByPath ) {
893
+
894
+ const composer = new USDComposer( this.manager );
895
+ const newBasePath = this._getBasePath( resolvedPath );
896
+ const composedGroup = composer.compose( referencedData, this.assets, mergedVariants, newBasePath );
897
+
898
+ // If a primPath is specified, find and return just that subtree
899
+ if ( primPath ) {
900
+
901
+ const primName = primPath.split( '/' ).pop();
902
+
903
+ // Find the direct child with this name (not a deep search)
904
+ // This is important because there may be multiple objects with the same name
905
+ let targetObject = null;
906
+ for ( const child of composedGroup.children ) {
907
+
908
+ if ( child.name === primName ) {
909
+
910
+ targetObject = child;
911
+ break;
912
+
913
+ }
914
+
915
+ }
916
+
917
+ if ( targetObject ) {
918
+
919
+ // Detach from parent for re-parenting
920
+ composedGroup.remove( targetObject );
921
+
922
+ // Wrap in a group to maintain consistent return type
923
+ const wrapper = new Group();
924
+ wrapper.add( targetObject );
925
+ return wrapper;
926
+
927
+ }
928
+
929
+ }
930
+
931
+ return composedGroup;
932
+
933
+ }
934
+
935
+ // If it's already a Three.js Group (legacy support), clone it
936
+ if ( referencedData.isGroup || referencedData.isObject3D ) {
937
+
938
+ return referencedData.clone();
939
+
940
+ }
941
+
942
+ return null;
943
+
944
+ }
945
+
946
+ /**
947
+ * Find a single mesh in the group's shallow hierarchy.
948
+ * Only returns a mesh if it's at depth 0 or 1, not deeply nested.
949
+ * This preserves transforms in complex hierarchies like Kitchen Set
950
+ * while supporting USDZExporter round-trip (Xform > Xform > Mesh pattern).
951
+ */
952
+ _findSingleMesh( group ) {
953
+
954
+ // Check direct children first
955
+ for ( const child of group.children ) {
956
+
957
+ if ( child.isMesh ) {
958
+
959
+ group.remove( child );
960
+ return child;
961
+
962
+ }
963
+
964
+ }
965
+
966
+ // Check grandchildren (USDZExporter pattern: Xform > Geometry > Mesh)
967
+ // Only if there's exactly one child with exactly one grandchild
968
+ if ( group.children.length === 1 ) {
969
+
970
+ const child = group.children[ 0 ];
971
+
972
+ if ( child.children && child.children.length === 1 ) {
973
+
974
+ const grandchild = child.children[ 0 ];
975
+
976
+ if ( grandchild.isMesh && ! this._hasNonIdentityTransform( child ) ) {
977
+
978
+ // Safe to merge - intermediate has identity transform
979
+ child.remove( grandchild );
980
+ return grandchild;
981
+
982
+ }
983
+
984
+ }
985
+
986
+ }
987
+
988
+ return null;
989
+
990
+ }
991
+
992
+ /**
993
+ * Check if an object has a non-identity local transform.
994
+ */
995
+ _hasNonIdentityTransform( obj ) {
996
+
997
+ const pos = obj.position;
998
+ const rot = obj.rotation;
999
+ const scale = obj.scale;
1000
+
1001
+ const hasPosition = pos.x !== 0 || pos.y !== 0 || pos.z !== 0;
1002
+ const hasRotation = rot.x !== 0 || rot.y !== 0 || rot.z !== 0;
1003
+ const hasScale = scale.x !== 1 || scale.y !== 1 || scale.z !== 1;
1004
+
1005
+ return hasPosition || hasRotation || hasScale;
1006
+
1007
+ }
1008
+
1009
+ /**
1010
+ * Get the base path (directory) from a file path.
1011
+ */
1012
+ _getBasePath( filePath ) {
1013
+
1014
+ const lastSlash = filePath.lastIndexOf( '/' );
1015
+ return lastSlash >= 0 ? filePath.slice( 0, lastSlash ) : '';
1016
+
1017
+ }
1018
+
1019
+ /**
1020
+ * Extract variant selections from a spec's fields.
1021
+ */
1022
+ _getLocalVariantSelections( fields ) {
1023
+
1024
+ const variants = {};
1025
+
1026
+ if ( fields.variantSelection ) {
1027
+
1028
+ for ( const key in fields.variantSelection ) {
1029
+
1030
+ variants[ key ] = fields.variantSelection[ key ];
1031
+
1032
+ }
1033
+
1034
+ }
1035
+
1036
+ return variants;
1037
+
1038
+ }
1039
+
1040
+ /**
1041
+ * Get all reference values from a prim spec.
1042
+ * @returns {string[]} Array of reference strings like "@path@" or "@path@<prim>"
1043
+ */
1044
+ _getReferences( spec ) {
1045
+
1046
+ const results = [];
1047
+
1048
+ if ( spec.fields.references && spec.fields.references.length > 0 ) {
1049
+
1050
+ const ref = spec.fields.references[ 0 ];
1051
+
1052
+ if ( typeof ref === 'string' ) {
1053
+
1054
+ // Extract all @...@ references (handles both single and array values)
1055
+ const matches = ref.matchAll( /@([^@]+)@(?:<([^>]+)>)?/g );
1056
+ for ( const match of matches ) {
1057
+
1058
+ results.push( match[ 0 ] );
1059
+
1060
+ }
1061
+
1062
+ } else if ( ref.assetPath ) {
1063
+
1064
+ results.push( '@' + ref.assetPath + '@' );
1065
+
1066
+ }
1067
+
1068
+ }
1069
+
1070
+ if ( results.length === 0 && spec.fields.payload ) {
1071
+
1072
+ const payload = spec.fields.payload;
1073
+ if ( typeof payload === 'string' ) results.push( payload );
1074
+ else if ( payload.assetPath ) results.push( '@' + payload.assetPath + '@' );
1075
+
1076
+ }
1077
+
1078
+ return results;
1079
+
1080
+ }
1081
+
1082
+ /**
1083
+ * Get attributes for a path from attribute specs.
1084
+ */
1085
+ _getAttributes( path ) {
1086
+
1087
+ const attrs = {};
1088
+
1089
+ this._collectAttributesFromPath( path, attrs );
1090
+
1091
+ // Collect overrides from sibling variants (when path is inside a variant)
1092
+ const variantMatch = path.match( VARIANT_PATH_REGEX );
1093
+ if ( variantMatch ) {
1094
+
1095
+ const basePath = variantMatch[ 1 ];
1096
+ const relativePath = variantMatch[ 4 ];
1097
+ const variantPaths = this._getVariantPaths( basePath );
1098
+
1099
+ for ( const vp of variantPaths ) {
1100
+
1101
+ if ( path.startsWith( vp ) ) continue;
1102
+
1103
+ const overridePath = vp + '/' + relativePath;
1104
+ this._collectAttributesFromPath( overridePath, attrs );
1105
+
1106
+ }
1107
+
1108
+ } else {
1109
+
1110
+ // Check for variant overrides at ancestor levels
1111
+ const parts = path.split( '/' );
1112
+ for ( let i = 1; i < parts.length - 1; i ++ ) {
1113
+
1114
+ const ancestorPath = parts.slice( 0, i + 1 ).join( '/' );
1115
+ const relativePath = parts.slice( i + 1 ).join( '/' );
1116
+ const variantPaths = this._getVariantPaths( ancestorPath );
1117
+
1118
+ for ( const vp of variantPaths ) {
1119
+
1120
+ const overridePath = vp + '/' + relativePath;
1121
+ this._collectAttributesFromPath( overridePath, attrs );
1122
+
1123
+ }
1124
+
1125
+ }
1126
+
1127
+ }
1128
+
1129
+ return attrs;
1130
+
1131
+ }
1132
+
1133
+ _collectAttributesFromPath( path, attrs ) {
1134
+
1135
+ // Use the attribute index for O(1) lookup instead of O(n) iteration
1136
+ const attrMap = this.attributesByPrimPath.get( path );
1137
+
1138
+ if ( ! attrMap ) return;
1139
+
1140
+ for ( const [ attrName, attrSpec ] of attrMap ) {
1141
+
1142
+ if ( attrSpec.fields?.default !== undefined ) {
1143
+
1144
+ attrs[ attrName ] = attrSpec.fields.default;
1145
+
1146
+ } else if ( attrSpec.fields?.timeSamples ) {
1147
+
1148
+ // For animated attributes without default, use the first time sample (rest pose)
1149
+ const { times, values } = attrSpec.fields.timeSamples;
1150
+ if ( times && values && times.length > 0 ) {
1151
+
1152
+ // Find time 0, or use the first available time
1153
+ const idx = times.indexOf( 0 );
1154
+ attrs[ attrName ] = idx >= 0 ? values[ idx ] : values[ 0 ];
1155
+
1156
+ }
1157
+
1158
+ }
1159
+
1160
+ if ( attrSpec.fields?.elementSize !== undefined ) {
1161
+
1162
+ attrs[ attrName + ':elementSize' ] = attrSpec.fields.elementSize;
1163
+
1164
+ }
1165
+
1166
+ if ( attrName.startsWith( 'primvars:' ) && attrSpec.fields?.typeName !== undefined ) {
1167
+
1168
+ attrs[ attrName + ':typeName' ] = attrSpec.fields.typeName;
1169
+
1170
+ }
1171
+
1172
+ }
1173
+
1174
+ }
1175
+
1176
+ /**
1177
+ * Build a mesh from a USD geometric primitive (Cube, Sphere, Cylinder, Cone, Capsule).
1178
+ */
1179
+ _buildGeomPrimitive( path, spec, typeName ) {
1180
+
1181
+ const attrs = this._getAttributes( path );
1182
+ const name = path.split( '/' ).pop();
1183
+
1184
+ let geometry;
1185
+
1186
+ switch ( typeName ) {
1187
+
1188
+ case 'Cube': {
1189
+
1190
+ const size = attrs[ 'size' ] || 2;
1191
+ geometry = new BoxGeometry( size, size, size );
1192
+ break;
1193
+
1194
+ }
1195
+
1196
+ case 'Sphere': {
1197
+
1198
+ const radius = attrs[ 'radius' ] || 1;
1199
+ geometry = new SphereGeometry( radius, 32, 16 );
1200
+ break;
1201
+
1202
+ }
1203
+
1204
+ case 'Cylinder': {
1205
+
1206
+ const height = attrs[ 'height' ] || 2;
1207
+ const radius = attrs[ 'radius' ] || 1;
1208
+ geometry = new CylinderGeometry( radius, radius, height, 32 );
1209
+ break;
1210
+
1211
+ }
1212
+
1213
+ case 'Cone': {
1214
+
1215
+ const height = attrs[ 'height' ] || 2;
1216
+ const radius = attrs[ 'radius' ] || 1;
1217
+ geometry = new ConeGeometry( radius, height, 32 );
1218
+ break;
1219
+
1220
+ }
1221
+
1222
+ case 'Capsule': {
1223
+
1224
+ const height = attrs[ 'height' ] || 1;
1225
+ const radius = attrs[ 'radius' ] || 0.5;
1226
+ geometry = new CapsuleGeometry( radius, height, 16, 32 );
1227
+ break;
1228
+
1229
+ }
1230
+
1231
+ }
1232
+
1233
+ // USD defaults axis to "Z", Three.js uses Y
1234
+ const axis = attrs[ 'axis' ] || 'Z';
1235
+
1236
+ if ( axis === 'X' ) {
1237
+
1238
+ geometry.rotateZ( - Math.PI / 2 );
1239
+
1240
+ } else if ( axis === 'Z' ) {
1241
+
1242
+ geometry.rotateX( Math.PI / 2 );
1243
+
1244
+ }
1245
+
1246
+ const material = this._buildMaterial( path, spec.fields );
1247
+ const mesh = new Mesh( geometry, material );
1248
+ mesh.name = name;
1249
+
1250
+ this.applyTransform( mesh, spec.fields, attrs );
1251
+
1252
+ return mesh;
1253
+
1254
+ }
1255
+
1256
+ /**
1257
+ * Build a mesh from a Mesh spec.
1258
+ */
1259
+ _buildMesh( path, spec ) {
1260
+
1261
+ const attrs = this._getAttributes( path );
1262
+
1263
+ // Check for skinning data
1264
+ const jointIndices = attrs[ 'primvars:skel:jointIndices' ];
1265
+ const jointWeights = attrs[ 'primvars:skel:jointWeights' ];
1266
+ const hasSkinning = jointIndices && jointWeights &&
1267
+ jointIndices.length > 0 && jointWeights.length > 0;
1268
+
1269
+ // Collect GeomSubsets for multi-material support
1270
+ const geomSubsets = this._getGeomSubsets( path );
1271
+
1272
+ let geometry, material;
1273
+
1274
+ if ( geomSubsets.length > 0 ) {
1275
+
1276
+ geometry = this._buildGeometryWithSubsets( attrs, geomSubsets, hasSkinning );
1277
+
1278
+ const meshMaterialPath = this._getMaterialPath( path, spec.fields );
1279
+
1280
+ material = geomSubsets.map( subset => {
1281
+
1282
+ const matPath = subset.materialPath || meshMaterialPath;
1283
+ return this._buildMaterialForPath( matPath );
1284
+
1285
+ } );
1286
+
1287
+ } else {
1288
+
1289
+ geometry = this._buildGeometry( path, attrs, hasSkinning );
1290
+ material = this._buildMaterial( path, spec.fields );
1291
+
1292
+ }
1293
+
1294
+ const displayColor = attrs[ 'primvars:displayColor' ];
1295
+ if ( displayColor && displayColor.length >= 3 ) {
1296
+
1297
+ const applyDisplayColor = ( mat ) => {
1298
+
1299
+ if ( mat.color && mat.color.r === 1 && mat.color.g === 1 && mat.color.b === 1 && ! mat.map ) {
1300
+
1301
+ mat.color.setRGB( displayColor[ 0 ], displayColor[ 1 ], displayColor[ 2 ], SRGBColorSpace );
1302
+
1303
+ }
1304
+
1305
+ };
1306
+
1307
+ if ( Array.isArray( material ) ) {
1308
+
1309
+ material.forEach( applyDisplayColor );
1310
+
1311
+ } else {
1312
+
1313
+ applyDisplayColor( material );
1314
+
1315
+ }
1316
+
1317
+ }
1318
+
1319
+ const displayOpacity = attrs[ 'primvars:displayOpacity' ];
1320
+ if ( displayOpacity && displayOpacity.length === 1 && geomSubsets.length === 0 ) {
1321
+
1322
+ const opacity = displayOpacity[ 0 ];
1323
+
1324
+ const applyDisplayOpacity = ( mat ) => {
1325
+
1326
+ if ( opacity < 1 && mat.opacity === 1 && mat.transparent === false ) {
1327
+
1328
+ mat.opacity = opacity;
1329
+ mat.transparent = true;
1330
+
1331
+ }
1332
+
1333
+ };
1334
+
1335
+ if ( Array.isArray( material ) ) {
1336
+
1337
+ material.forEach( applyDisplayOpacity );
1338
+
1339
+ } else {
1340
+
1341
+ applyDisplayOpacity( material );
1342
+
1343
+ }
1344
+
1345
+ }
1346
+
1347
+ let mesh;
1348
+
1349
+ if ( hasSkinning ) {
1350
+
1351
+ mesh = new SkinnedMesh( geometry, material );
1352
+
1353
+ // Find skeleton path from skel:skeleton relationship
1354
+ let skelBindingSpec = this.specsByPath[ path + '.skel:skeleton' ];
1355
+ if ( ! skelBindingSpec ) {
1356
+
1357
+ skelBindingSpec = this.specsByPath[ path + '.rel skel:skeleton' ];
1358
+
1359
+ }
1360
+
1361
+ let skeletonPath = null;
1362
+
1363
+ if ( skelBindingSpec ) {
1364
+
1365
+ if ( skelBindingSpec.fields.targetPaths && skelBindingSpec.fields.targetPaths.length > 0 ) {
1366
+
1367
+ skeletonPath = skelBindingSpec.fields.targetPaths[ 0 ];
1368
+
1369
+ } else if ( skelBindingSpec.fields.default ) {
1370
+
1371
+ skeletonPath = skelBindingSpec.fields.default.replace( /<|>/g, '' );
1372
+
1373
+ }
1374
+
1375
+ }
1376
+
1377
+ // Get per-mesh joint mapping
1378
+ const localJoints = attrs[ 'skel:joints' ];
1379
+
1380
+ // Get geomBindTransform if present
1381
+ const geomBindTransform = attrs[ 'primvars:skel:geomBindTransform' ];
1382
+
1383
+ this.skinnedMeshes.push( { mesh, skeletonPath, path, localJoints, geomBindTransform } );
1384
+
1385
+ } else {
1386
+
1387
+ mesh = new Mesh( geometry, material );
1388
+
1389
+ }
1390
+
1391
+ mesh.name = path.split( '/' ).pop();
1392
+ this.applyTransform( mesh, spec.fields, attrs );
1393
+
1394
+ return mesh;
1395
+
1396
+ }
1397
+
1398
+ /**
1399
+ * Build a camera from a Camera spec.
1400
+ */
1401
+ _buildCamera( path ) {
1402
+
1403
+ const attrs = this._getAttributes( path );
1404
+ const projectionToken = attrs[ 'projection' ];
1405
+ const projection = typeof projectionToken === 'string'
1406
+ ? projectionToken.toLowerCase()
1407
+ : USD_CAMERA_DEFAULTS.projection;
1408
+ const clippingRange = attrs[ 'clippingRange' ] || USD_CAMERA_DEFAULTS.clippingRange;
1409
+ const near = Math.max(
1410
+ Number.EPSILON,
1411
+ this._parseNumber( clippingRange[ 0 ], USD_CAMERA_DEFAULTS.clippingRange[ 0 ] )
1412
+ );
1413
+ const far = Math.max(
1414
+ near + Number.EPSILON,
1415
+ this._parseNumber( clippingRange[ 1 ], USD_CAMERA_DEFAULTS.clippingRange[ 1 ] )
1416
+ );
1417
+ const horizontalAperture = this._parseNumber(
1418
+ attrs[ 'horizontalAperture' ],
1419
+ USD_CAMERA_DEFAULTS.horizontalAperture
1420
+ );
1421
+ const verticalAperture = this._parseNumber(
1422
+ attrs[ 'verticalAperture' ],
1423
+ USD_CAMERA_DEFAULTS.verticalAperture
1424
+ );
1425
+ const horizontalApertureOffset = this._parseNumber(
1426
+ attrs[ 'horizontalApertureOffset' ],
1427
+ USD_CAMERA_DEFAULTS.horizontalApertureOffset
1428
+ );
1429
+ const verticalApertureOffset = this._parseNumber(
1430
+ attrs[ 'verticalApertureOffset' ],
1431
+ USD_CAMERA_DEFAULTS.verticalApertureOffset
1432
+ );
1433
+ const focalLength = this._parseNumber( attrs[ 'focalLength' ], USD_CAMERA_DEFAULTS.focalLength );
1434
+ const focusDistance = this._parseNumber( attrs[ 'focusDistance' ], USD_CAMERA_DEFAULTS.focusDistance );
1435
+ const fStop = this._parseNumber( attrs[ 'fStop' ], USD_CAMERA_DEFAULTS.fStop );
1436
+
1437
+ let camera;
1438
+
1439
+ if ( projection === 'orthographic' ) {
1440
+
1441
+ // USD orthographic apertures are in tenths of a world unit.
1442
+ const width = horizontalAperture / 10;
1443
+ const height = verticalAperture / 10;
1444
+ const offsetX = horizontalApertureOffset / 10;
1445
+ const offsetY = verticalApertureOffset / 10;
1446
+
1447
+ camera = new OrthographicCamera(
1448
+ offsetX - width * 0.5,
1449
+ offsetX + width * 0.5,
1450
+ offsetY + height * 0.5,
1451
+ offsetY - height * 0.5,
1452
+ near,
1453
+ far
1454
+ );
1455
+
1456
+ } else {
1457
+
1458
+ const safeVerticalAperture = Math.max( Number.EPSILON, verticalAperture );
1459
+ const safeFocalLength = Math.max( Number.EPSILON, focalLength );
1460
+ const aspect = horizontalAperture / safeVerticalAperture;
1461
+ const fov = 2 * Math.atan( safeVerticalAperture / ( 2 * safeFocalLength ) ) * 180 / Math.PI;
1462
+
1463
+ camera = new PerspectiveCamera( fov, aspect, near, far );
1464
+ camera.filmGauge = Math.max( horizontalAperture, verticalAperture );
1465
+ camera.filmOffset = horizontalApertureOffset;
1466
+ camera.focus = focusDistance;
1467
+ camera.setFocalLength( safeFocalLength );
1468
+
1469
+ if ( verticalApertureOffset !== 0 ) {
1470
+
1471
+ // Three.js supports only horizontal film offset directly.
1472
+ camera.userData.verticalApertureOffset = verticalApertureOffset;
1473
+
1474
+ }
1475
+
1476
+ }
1477
+
1478
+ camera.userData.fStop = fStop;
1479
+ camera.userData.usdProjection = projection;
1480
+ return camera;
1481
+
1482
+ }
1483
+
1484
+ /**
1485
+ * Build a light from a UsdLux light spec.
1486
+ */
1487
+ _buildLight( path, typeName ) {
1488
+
1489
+ const attrs = this._getAttributes( path );
1490
+
1491
+ const intensity = this._parseNumber( attrs[ 'inputs:intensity' ], 1 );
1492
+ const baseColor = attrs[ 'inputs:color' ] || [ 1, 1, 1 ];
1493
+ const enableColorTemperature = attrs[ 'inputs:enableColorTemperature' ] === true;
1494
+ const colorTemperature = this._parseNumber( attrs[ 'inputs:colorTemperature' ], 6500 );
1495
+
1496
+ const color = new Color( baseColor[ 0 ], baseColor[ 1 ], baseColor[ 2 ] );
1497
+
1498
+ if ( enableColorTemperature ) {
1499
+
1500
+ const temp = this._colorTemperature( colorTemperature );
1501
+ color.multiply( temp );
1502
+
1503
+ }
1504
+
1505
+ let light;
1506
+
1507
+ switch ( typeName ) {
1508
+
1509
+ case 'DistantLight':
1510
+ light = new DirectionalLight( color, intensity );
1511
+ break;
1512
+
1513
+ case 'SphereLight': {
1514
+
1515
+ const coneAngle = this._parseNumber( attrs[ 'shaping:cone:angle' ], 0 );
1516
+
1517
+ if ( coneAngle > 0 ) {
1518
+
1519
+ const angle = coneAngle * Math.PI / 180;
1520
+ const softness = this._parseNumber( attrs[ 'shaping:cone:softness' ], 0 );
1521
+ light = new SpotLight( color, intensity, 0, angle, softness );
1522
+
1523
+ } else {
1524
+
1525
+ light = new PointLight( color, intensity );
1526
+
1527
+ }
1528
+
1529
+ break;
1530
+
1531
+ }
1532
+
1533
+ case 'RectLight': {
1534
+
1535
+ const width = this._parseNumber( attrs[ 'inputs:width' ], 1 );
1536
+ const height = this._parseNumber( attrs[ 'inputs:height' ], 1 );
1537
+ light = new RectAreaLight( color, intensity, width, height );
1538
+ break;
1539
+
1540
+ }
1541
+
1542
+ case 'DiskLight': {
1543
+
1544
+ const radius = this._parseNumber( attrs[ 'inputs:radius' ], 0.5 );
1545
+ const side = radius * 2;
1546
+ light = new RectAreaLight( color, intensity, side, side );
1547
+ break;
1548
+
1549
+ }
1550
+
1551
+ }
1552
+
1553
+ return light;
1554
+
1555
+ }
1556
+
1557
+ /**
1558
+ * Convert a color temperature in Kelvin to an RGB Color.
1559
+ * Based on Tanner Helland's algorithm.
1560
+ */
1561
+ _colorTemperature( kelvin ) {
1562
+
1563
+ const temp = kelvin / 100;
1564
+ let r, g, b;
1565
+
1566
+ if ( temp <= 66 ) {
1567
+
1568
+ r = 1;
1569
+ g = 0.39008157876901960784 * Math.log( temp ) - 0.63184144378862745098;
1570
+
1571
+ } else {
1572
+
1573
+ r = 1.29293618606274509804 * Math.pow( temp - 60, - 0.1332047592 );
1574
+ g = 1.12989086089529411765 * Math.pow( temp - 60, - 0.0755148492 );
1575
+
1576
+ }
1577
+
1578
+ if ( temp >= 66 ) {
1579
+
1580
+ b = 1;
1581
+
1582
+ } else if ( temp <= 19 ) {
1583
+
1584
+ b = 0;
1585
+
1586
+ } else {
1587
+
1588
+ b = 0.54320678911019607843 * Math.log( temp - 10 ) - 1.19625408914;
1589
+
1590
+ }
1591
+
1592
+ return new Color(
1593
+ Math.min( Math.max( r, 0 ), 1 ),
1594
+ Math.min( Math.max( g, 0 ), 1 ),
1595
+ Math.min( Math.max( b, 0 ), 1 )
1596
+ );
1597
+
1598
+ }
1599
+
1600
+ _parseNumber( value, fallback ) {
1601
+
1602
+ const n = Number( value );
1603
+ return Number.isFinite( n ) ? n : fallback;
1604
+
1605
+ }
1606
+
1607
+ _getGeomSubsets( meshPath ) {
1608
+
1609
+ const subsets = [];
1610
+ const subsetPaths = this.geomSubsetsByMeshPath.get( meshPath );
1611
+ if ( ! subsetPaths ) return subsets;
1612
+
1613
+ for ( const p of subsetPaths ) {
1614
+
1615
+ const attrs = this._getAttributes( p );
1616
+ const indices = attrs[ 'indices' ];
1617
+ if ( ! indices || indices.length === 0 ) continue;
1618
+
1619
+ // Get material binding - check direct path and variant paths
1620
+ const materialPath = this._getMaterialBindingTarget( p );
1621
+
1622
+ subsets.push( {
1623
+ name: p.split( '/' ).pop(),
1624
+ indices: indices,
1625
+ materialPath: materialPath
1626
+ } );
1627
+
1628
+ }
1629
+
1630
+ return subsets;
1631
+
1632
+ }
1633
+
1634
+ /**
1635
+ * Get material binding target path, checking variant paths if needed.
1636
+ */
1637
+ _getMaterialBindingTarget( primPath ) {
1638
+
1639
+ const attrName = 'material:binding';
1640
+
1641
+ // First check direct path
1642
+ const directPath = primPath + '.' + attrName;
1643
+ const directSpec = this.specsByPath[ directPath ];
1644
+ if ( directSpec?.fields?.targetPaths?.length > 0 ) {
1645
+
1646
+ return directSpec.fields.targetPaths[ 0 ];
1647
+
1648
+ }
1649
+
1650
+ // Check variant paths at ancestor levels
1651
+ const parts = primPath.split( '/' );
1652
+ for ( let i = 1; i < parts.length; i ++ ) {
1653
+
1654
+ const ancestorPath = parts.slice( 0, i + 1 ).join( '/' );
1655
+ const relativePath = parts.slice( i + 1 ).join( '/' );
1656
+ const variantPaths = this._getVariantPaths( ancestorPath );
1657
+
1658
+ for ( const vp of variantPaths ) {
1659
+
1660
+ const overridePath = relativePath ? vp + '/' + relativePath + '.' + attrName : vp + '.' + attrName;
1661
+ const overrideSpec = this.specsByPath[ overridePath ];
1662
+
1663
+ if ( overrideSpec?.fields?.targetPaths?.length > 0 ) {
1664
+
1665
+ return overrideSpec.fields.targetPaths[ 0 ];
1666
+
1667
+ }
1668
+
1669
+ }
1670
+
1671
+ }
1672
+
1673
+ return null;
1674
+
1675
+ }
1676
+
1677
+ _buildGeometry( path, fields, hasSkinning = false ) {
1678
+
1679
+ const geometry = new BufferGeometry();
1680
+
1681
+ const points = fields[ 'points' ];
1682
+ if ( ! points || points.length === 0 ) return geometry;
1683
+
1684
+ const faceVertexIndices = fields[ 'faceVertexIndices' ];
1685
+ const faceVertexCounts = fields[ 'faceVertexCounts' ];
1686
+
1687
+ // Parse polygon holes (Arnold format: [holeFaceIdx, parentFaceIdx, ...])
1688
+ const polygonHoles = fields[ 'primvars:arnold:polygon_holes' ];
1689
+ const holeMap = this._buildHoleMap( polygonHoles );
1690
+
1691
+ // Compute triangulation pattern once using actual vertex positions
1692
+ // This pattern will be reused for normals, UVs, etc.
1693
+ let indices = faceVertexIndices;
1694
+ let triPattern = null;
1695
+
1696
+ if ( faceVertexCounts && faceVertexCounts.length > 0 ) {
1697
+
1698
+ const result = this._triangulateIndicesWithPattern( faceVertexIndices, faceVertexCounts, points, holeMap );
1699
+ indices = result.indices;
1700
+ triPattern = result.pattern;
1701
+
1702
+ }
1703
+
1704
+ let positions = points;
1705
+ if ( indices && indices.length > 0 ) {
1706
+
1707
+ positions = this._expandAttribute( points, indices, 3 );
1708
+
1709
+ }
1710
+
1711
+ geometry.setAttribute( 'position', new BufferAttribute( new Float32Array( positions ), 3 ) );
1712
+
1713
+ const normals = fields[ 'normals' ] || fields[ 'primvars:normals' ];
1714
+ const normalIndicesRaw = fields[ 'normals:indices' ] || fields[ 'primvars:normals:indices' ];
1715
+
1716
+ if ( normals && normals.length > 0 ) {
1717
+
1718
+ let normalData = normals;
1719
+
1720
+ if ( normalIndicesRaw && normalIndicesRaw.length > 0 && triPattern ) {
1721
+
1722
+ // Indexed normals - apply triangulation pattern to indices
1723
+ const triangulatedNormalIndices = this._applyTriangulationPattern( normalIndicesRaw, triPattern );
1724
+ normalData = this._expandAttribute( normals, triangulatedNormalIndices, 3 );
1725
+
1726
+ } else if ( normals.length === points.length ) {
1727
+
1728
+ // Per-vertex normals
1729
+ if ( indices && indices.length > 0 ) {
1730
+
1731
+ normalData = this._expandAttribute( normals, indices, 3 );
1732
+
1733
+ }
1734
+
1735
+ } else if ( triPattern ) {
1736
+
1737
+ // Per-face-vertex normals (no separate indices) - use same triangulation pattern
1738
+ const normalIndices = this._applyTriangulationPattern(
1739
+ Array.from( { length: normals.length / 3 }, ( _, i ) => i ),
1740
+ triPattern
1741
+ );
1742
+ normalData = this._expandAttribute( normals, normalIndices, 3 );
1743
+
1744
+ }
1745
+
1746
+ geometry.setAttribute( 'normal', new BufferAttribute( new Float32Array( normalData ), 3 ) );
1747
+
1748
+ } else {
1749
+
1750
+ // Compute vertex normals from the original indexed topology where
1751
+ // vertices are shared, then expand them like positions.
1752
+ const vertexNormals = this._computeVertexNormals( points, indices );
1753
+ geometry.setAttribute( 'normal', new BufferAttribute( new Float32Array(
1754
+ this._expandAttribute( vertexNormals, indices, 3 )
1755
+ ), 3 ) );
1756
+
1757
+ }
1758
+
1759
+ const { uvs, uvIndices } = this._findUVPrimvar( fields );
1760
+ const numFaceVertices = faceVertexIndices ? faceVertexIndices.length : 0;
1761
+
1762
+ if ( uvs && uvs.length > 0 ) {
1763
+
1764
+ let uvData = uvs;
1765
+
1766
+ if ( uvIndices && uvIndices.length > 0 && triPattern ) {
1767
+
1768
+ const triangulatedUvIndices = this._applyTriangulationPattern( uvIndices, triPattern );
1769
+ uvData = this._expandAttribute( uvs, triangulatedUvIndices, 2 );
1770
+
1771
+ } else if ( indices && uvs.length / 2 === points.length / 3 ) {
1772
+
1773
+ uvData = this._expandAttribute( uvs, indices, 2 );
1774
+
1775
+ } else if ( triPattern && uvs.length / 2 === numFaceVertices ) {
1776
+
1777
+ // Per-face-vertex UVs (faceVarying, no separate indices)
1778
+ const uvIndicesFromPattern = this._applyTriangulationPattern(
1779
+ Array.from( { length: numFaceVertices }, ( _, i ) => i ),
1780
+ triPattern
1781
+ );
1782
+ uvData = this._expandAttribute( uvs, uvIndicesFromPattern, 2 );
1783
+
1784
+ }
1785
+
1786
+ geometry.setAttribute( 'uv', new BufferAttribute( new Float32Array( uvData ), 2 ) );
1787
+
1788
+ }
1789
+
1790
+ // Second UV set (st1) for lightmaps/AO
1791
+ const { uvs2, uv2Indices } = this._findUV2Primvar( fields );
1792
+
1793
+ if ( uvs2 && uvs2.length > 0 ) {
1794
+
1795
+ let uv2Data = uvs2;
1796
+
1797
+ if ( uv2Indices && uv2Indices.length > 0 && triPattern ) {
1798
+
1799
+ const triangulatedUv2Indices = this._applyTriangulationPattern( uv2Indices, triPattern );
1800
+ uv2Data = this._expandAttribute( uvs2, triangulatedUv2Indices, 2 );
1801
+
1802
+ } else if ( indices && uvs2.length / 2 === points.length / 3 ) {
1803
+
1804
+ uv2Data = this._expandAttribute( uvs2, indices, 2 );
1805
+
1806
+ } else if ( triPattern && uvs2.length / 2 === numFaceVertices ) {
1807
+
1808
+ // Per-face-vertex UV2 (faceVarying, no separate indices)
1809
+ const uv2IndicesFromPattern = this._applyTriangulationPattern(
1810
+ Array.from( { length: numFaceVertices }, ( _, i ) => i ),
1811
+ triPattern
1812
+ );
1813
+ uv2Data = this._expandAttribute( uvs2, uv2IndicesFromPattern, 2 );
1814
+
1815
+ }
1816
+
1817
+ geometry.setAttribute( 'uv1', new BufferAttribute( new Float32Array( uv2Data ), 2 ) );
1818
+
1819
+ }
1820
+
1821
+ // Add skinning attributes
1822
+ if ( hasSkinning ) {
1823
+
1824
+ const jointIndices = fields[ 'primvars:skel:jointIndices' ];
1825
+ const jointWeights = fields[ 'primvars:skel:jointWeights' ];
1826
+ const elementSize = fields[ 'primvars:skel:jointIndices:elementSize' ] || 4;
1827
+
1828
+ if ( jointIndices && jointWeights ) {
1829
+
1830
+ const numVertices = positions.length / 3;
1831
+
1832
+ let skinIndexData, skinWeightData;
1833
+
1834
+ if ( indices && indices.length > 0 ) {
1835
+
1836
+ skinIndexData = this._expandAttribute( jointIndices, indices, elementSize );
1837
+ skinWeightData = this._expandAttribute( jointWeights, indices, elementSize );
1838
+
1839
+ } else {
1840
+
1841
+ skinIndexData = jointIndices;
1842
+ skinWeightData = jointWeights;
1843
+
1844
+ }
1845
+
1846
+ const skinIndices = new Uint16Array( numVertices * 4 );
1847
+ const skinWeights = new Float32Array( numVertices * 4 );
1848
+
1849
+ this._selectTopWeights( skinIndexData, skinWeightData, elementSize, numVertices, skinIndices, skinWeights );
1850
+
1851
+ geometry.setAttribute( 'skinIndex', new BufferAttribute( skinIndices, 4 ) );
1852
+ geometry.setAttribute( 'skinWeight', new BufferAttribute( skinWeights, 4 ) );
1853
+
1854
+ }
1855
+
1856
+ }
1857
+
1858
+ return geometry;
1859
+
1860
+ }
1861
+
1862
+ _buildGeometryWithSubsets( fields, geomSubsets, hasSkinning = false ) {
1863
+
1864
+ const geometry = new BufferGeometry();
1865
+
1866
+ const points = fields[ 'points' ];
1867
+ if ( ! points || points.length === 0 ) return geometry;
1868
+
1869
+ const faceVertexIndices = fields[ 'faceVertexIndices' ];
1870
+ const faceVertexCounts = fields[ 'faceVertexCounts' ];
1871
+
1872
+ if ( ! faceVertexCounts || faceVertexCounts.length === 0 ) return geometry;
1873
+
1874
+ const polygonHoles = fields[ 'primvars:arnold:polygon_holes' ];
1875
+ const holeMap = this._buildHoleMap( polygonHoles );
1876
+ const holeFaces = holeMap.holeFaces;
1877
+ const parentToHoles = holeMap.parentToHoles;
1878
+
1879
+ const { uvs, uvIndices } = this._findUVPrimvar( fields );
1880
+ const { uvs2, uv2Indices } = this._findUV2Primvar( fields );
1881
+ const normals = fields[ 'normals' ] || fields[ 'primvars:normals' ];
1882
+ const normalIndicesRaw = fields[ 'normals:indices' ] || fields[ 'primvars:normals:indices' ];
1883
+
1884
+ const jointIndices = hasSkinning ? fields[ 'primvars:skel:jointIndices' ] : null;
1885
+ const jointWeights = hasSkinning ? fields[ 'primvars:skel:jointWeights' ] : null;
1886
+ const elementSize = fields[ 'primvars:skel:jointIndices:elementSize' ] || 4;
1887
+
1888
+ // Build face-to-triangle mapping (accounting for holes)
1889
+ const faceTriangleOffset = [];
1890
+ let triangleCount = 0;
1891
+
1892
+ for ( let i = 0; i < faceVertexCounts.length; i ++ ) {
1893
+
1894
+ faceTriangleOffset.push( triangleCount );
1895
+
1896
+ // Skip hole faces - they're triangulated with their parent
1897
+ if ( holeFaces.has( i ) ) continue;
1898
+
1899
+ const count = faceVertexCounts[ i ];
1900
+ const holes = parentToHoles.get( i );
1901
+
1902
+ if ( holes && holes.length > 0 ) {
1903
+
1904
+ // For faces with holes, count triangles based on total vertices
1905
+ // Earcut produces (total_vertices - 2) triangles for any polygon including holes
1906
+ let totalVerts = count;
1907
+ for ( const holeIdx of holes ) {
1908
+
1909
+ totalVerts += faceVertexCounts[ holeIdx ];
1910
+
1911
+ }
1912
+
1913
+ triangleCount += totalVerts - 2;
1914
+
1915
+ } else if ( count >= 3 ) {
1916
+
1917
+ triangleCount += count - 2;
1918
+
1919
+ }
1920
+
1921
+ }
1922
+
1923
+ const triangleToSubset = new Int32Array( triangleCount ).fill( - 1 );
1924
+
1925
+ for ( let si = 0; si < geomSubsets.length; si ++ ) {
1926
+
1927
+ const subset = geomSubsets[ si ];
1928
+
1929
+ for ( let i = 0; i < subset.indices.length; i ++ ) {
1930
+
1931
+ const faceIdx = subset.indices[ i ];
1932
+ if ( faceIdx >= faceVertexCounts.length ) continue;
1933
+
1934
+ const triStart = faceTriangleOffset[ faceIdx ];
1935
+ const triCount = faceVertexCounts[ faceIdx ] - 2;
1936
+
1937
+ for ( let t = 0; t < triCount; t ++ ) {
1938
+
1939
+ triangleToSubset[ triStart + t ] = si;
1940
+
1941
+ }
1942
+
1943
+ }
1944
+
1945
+ }
1946
+
1947
+ // Sort triangles by subset
1948
+ const sortedTriangles = [];
1949
+
1950
+ for ( let tri = 0; tri < triangleCount; tri ++ ) {
1951
+
1952
+ sortedTriangles.push( { original: tri, subset: triangleToSubset[ tri ] } );
1953
+
1954
+ }
1955
+
1956
+ sortedTriangles.sort( ( a, b ) => a.subset - b.subset );
1957
+
1958
+ const groups = [];
1959
+ let currentSubset = sortedTriangles.length > 0 ? sortedTriangles[ 0 ].subset : - 1;
1960
+ let groupStart = 0;
1961
+
1962
+ for ( let i = 0; i < sortedTriangles.length; i ++ ) {
1963
+
1964
+ if ( sortedTriangles[ i ].subset !== currentSubset ) {
1965
+
1966
+ if ( currentSubset >= 0 ) {
1967
+
1968
+ groups.push( {
1969
+ start: groupStart * 3,
1970
+ count: ( i - groupStart ) * 3,
1971
+ materialIndex: currentSubset
1972
+ } );
1973
+
1974
+ }
1975
+
1976
+ currentSubset = sortedTriangles[ i ].subset;
1977
+ groupStart = i;
1978
+
1979
+ }
1980
+
1981
+ }
1982
+
1983
+ if ( currentSubset >= 0 && sortedTriangles.length > groupStart ) {
1984
+
1985
+ groups.push( {
1986
+ start: groupStart * 3,
1987
+ count: ( sortedTriangles.length - groupStart ) * 3,
1988
+ materialIndex: currentSubset
1989
+ } );
1990
+
1991
+ }
1992
+
1993
+ for ( const group of groups ) {
1994
+
1995
+ geometry.addGroup( group.start, group.count, group.materialIndex );
1996
+
1997
+ }
1998
+
1999
+ // Triangulate original data using consistent pattern
2000
+ const { indices: origIndices, pattern: triPattern } = this._triangulateIndicesWithPattern( faceVertexIndices, faceVertexCounts, points, holeMap );
2001
+ const numFaceVertices = faceVertexCounts.reduce( ( a, b ) => a + b, 0 );
2002
+ const faceVaryingIdentity = ( uvs && ! uvIndices && uvs.length / 2 === numFaceVertices ) ||
2003
+ ( uvs2 && ! uv2Indices && uvs2.length / 2 === numFaceVertices )
2004
+ ? this._applyTriangulationPattern( Array.from( { length: numFaceVertices }, ( _, i ) => i ), triPattern )
2005
+ : null;
2006
+ const origUvIndices = uvIndices
2007
+ ? this._applyTriangulationPattern( uvIndices, triPattern )
2008
+ : ( uvs && uvs.length / 2 === numFaceVertices ? faceVaryingIdentity : null );
2009
+ const origUv2Indices = uv2Indices
2010
+ ? this._applyTriangulationPattern( uv2Indices, triPattern )
2011
+ : ( uvs2 && uvs2.length / 2 === numFaceVertices ? faceVaryingIdentity : null );
2012
+ const hasIndexedNormals = normals && normalIndicesRaw && normalIndicesRaw.length > 0;
2013
+ const hasFaceVaryingNormals = normals && normals.length / 3 === numFaceVertices;
2014
+ const origNormalIndices = hasIndexedNormals
2015
+ ? this._applyTriangulationPattern( normalIndicesRaw, triPattern )
2016
+ : ( hasFaceVaryingNormals
2017
+ ? this._applyTriangulationPattern( Array.from( { length: numFaceVertices }, ( _, i ) => i ), triPattern )
2018
+ : null );
2019
+
2020
+ // When no normals are provided, compute vertex normals from
2021
+ // the indexed topology so that shared vertices produce averaged normals.
2022
+ const vertexNormals = ( ! normals && origIndices.length > 0 )
2023
+ ? this._computeVertexNormals( points, origIndices )
2024
+ : null;
2025
+
2026
+ // Build reordered vertex data
2027
+ const vertexCount = triangleCount * 3;
2028
+ const positions = new Float32Array( vertexCount * 3 );
2029
+ const uvData = uvs ? new Float32Array( vertexCount * 2 ) : null;
2030
+ const uv1Data = uvs2 ? new Float32Array( vertexCount * 2 ) : null;
2031
+ const normalData = ( normals || vertexNormals ) ? new Float32Array( vertexCount * 3 ) : null;
2032
+ const skinSrcIndices = jointIndices ? new Uint16Array( vertexCount * elementSize ) : null;
2033
+ const skinSrcWeights = jointWeights ? new Float32Array( vertexCount * elementSize ) : null;
2034
+
2035
+ for ( let i = 0; i < sortedTriangles.length; i ++ ) {
2036
+
2037
+ const origTri = sortedTriangles[ i ].original;
2038
+
2039
+ for ( let v = 0; v < 3; v ++ ) {
2040
+
2041
+ const origIdx = origTri * 3 + v;
2042
+ const newIdx = i * 3 + v;
2043
+
2044
+ const pointIdx = origIndices[ origIdx ];
2045
+ positions[ newIdx * 3 ] = points[ pointIdx * 3 ];
2046
+ positions[ newIdx * 3 + 1 ] = points[ pointIdx * 3 + 1 ];
2047
+ positions[ newIdx * 3 + 2 ] = points[ pointIdx * 3 + 2 ];
2048
+
2049
+ if ( uvData && uvs ) {
2050
+
2051
+ if ( origUvIndices ) {
2052
+
2053
+ const uvIdx = origUvIndices[ origIdx ];
2054
+ uvData[ newIdx * 2 ] = uvs[ uvIdx * 2 ];
2055
+ uvData[ newIdx * 2 + 1 ] = uvs[ uvIdx * 2 + 1 ];
2056
+
2057
+ } else if ( uvs.length / 2 === points.length / 3 ) {
2058
+
2059
+ uvData[ newIdx * 2 ] = uvs[ pointIdx * 2 ];
2060
+ uvData[ newIdx * 2 + 1 ] = uvs[ pointIdx * 2 + 1 ];
2061
+
2062
+ }
2063
+
2064
+ }
2065
+
2066
+ if ( uv1Data && uvs2 ) {
2067
+
2068
+ if ( origUv2Indices ) {
2069
+
2070
+ const uv2Idx = origUv2Indices[ origIdx ];
2071
+ uv1Data[ newIdx * 2 ] = uvs2[ uv2Idx * 2 ];
2072
+ uv1Data[ newIdx * 2 + 1 ] = uvs2[ uv2Idx * 2 + 1 ];
2073
+
2074
+ } else if ( uvs2.length / 2 === points.length / 3 ) {
2075
+
2076
+ uv1Data[ newIdx * 2 ] = uvs2[ pointIdx * 2 ];
2077
+ uv1Data[ newIdx * 2 + 1 ] = uvs2[ pointIdx * 2 + 1 ];
2078
+
2079
+ }
2080
+
2081
+ }
2082
+
2083
+ if ( normalData ) {
2084
+
2085
+ if ( normals && origNormalIndices ) {
2086
+
2087
+ const normalIdx = origNormalIndices[ origIdx ];
2088
+ normalData[ newIdx * 3 ] = normals[ normalIdx * 3 ];
2089
+ normalData[ newIdx * 3 + 1 ] = normals[ normalIdx * 3 + 1 ];
2090
+ normalData[ newIdx * 3 + 2 ] = normals[ normalIdx * 3 + 2 ];
2091
+
2092
+ } else if ( normals && normals.length === points.length ) {
2093
+
2094
+ normalData[ newIdx * 3 ] = normals[ pointIdx * 3 ];
2095
+ normalData[ newIdx * 3 + 1 ] = normals[ pointIdx * 3 + 1 ];
2096
+ normalData[ newIdx * 3 + 2 ] = normals[ pointIdx * 3 + 2 ];
2097
+
2098
+ } else if ( vertexNormals ) {
2099
+
2100
+ normalData[ newIdx * 3 ] = vertexNormals[ pointIdx * 3 ];
2101
+ normalData[ newIdx * 3 + 1 ] = vertexNormals[ pointIdx * 3 + 1 ];
2102
+ normalData[ newIdx * 3 + 2 ] = vertexNormals[ pointIdx * 3 + 2 ];
2103
+
2104
+ }
2105
+
2106
+ }
2107
+
2108
+ if ( skinSrcIndices && skinSrcWeights && jointIndices && jointWeights ) {
2109
+
2110
+ for ( let j = 0; j < elementSize; j ++ ) {
2111
+
2112
+ skinSrcIndices[ newIdx * elementSize + j ] = jointIndices[ pointIdx * elementSize + j ] || 0;
2113
+ skinSrcWeights[ newIdx * elementSize + j ] = jointWeights[ pointIdx * elementSize + j ] || 0;
2114
+
2115
+ }
2116
+
2117
+ }
2118
+
2119
+ }
2120
+
2121
+ }
2122
+
2123
+ geometry.setAttribute( 'position', new BufferAttribute( positions, 3 ) );
2124
+
2125
+ if ( uvData ) {
2126
+
2127
+ geometry.setAttribute( 'uv', new BufferAttribute( uvData, 2 ) );
2128
+
2129
+ }
2130
+
2131
+ if ( uv1Data ) {
2132
+
2133
+ geometry.setAttribute( 'uv1', new BufferAttribute( uv1Data, 2 ) );
2134
+
2135
+ }
2136
+
2137
+ geometry.setAttribute( 'normal', new BufferAttribute( normalData, 3 ) );
2138
+
2139
+ if ( skinSrcIndices && skinSrcWeights ) {
2140
+
2141
+ const skinIndexData = new Uint16Array( vertexCount * 4 );
2142
+ const skinWeightData = new Float32Array( vertexCount * 4 );
2143
+
2144
+ this._selectTopWeights( skinSrcIndices, skinSrcWeights, elementSize, vertexCount, skinIndexData, skinWeightData );
2145
+
2146
+ geometry.setAttribute( 'skinIndex', new BufferAttribute( skinIndexData, 4 ) );
2147
+ geometry.setAttribute( 'skinWeight', new BufferAttribute( skinWeightData, 4 ) );
2148
+
2149
+ }
2150
+
2151
+ return geometry;
2152
+
2153
+ }
2154
+
2155
+ _selectTopWeights( srcIndices, srcWeights, elementSize, numVertices, dstIndices, dstWeights ) {
2156
+
2157
+ if ( elementSize <= 4 ) {
2158
+
2159
+ for ( let i = 0; i < numVertices; i ++ ) {
2160
+
2161
+ for ( let j = 0; j < 4; j ++ ) {
2162
+
2163
+ if ( j < elementSize ) {
2164
+
2165
+ dstIndices[ i * 4 + j ] = srcIndices[ i * elementSize + j ] || 0;
2166
+ dstWeights[ i * 4 + j ] = srcWeights[ i * elementSize + j ] || 0;
2167
+
2168
+ } else {
2169
+
2170
+ dstIndices[ i * 4 + j ] = 0;
2171
+ dstWeights[ i * 4 + j ] = 0;
2172
+
2173
+ }
2174
+
2175
+ }
2176
+
2177
+ }
2178
+
2179
+ return;
2180
+
2181
+ }
2182
+
2183
+ // When elementSize > 4, find the 4 largest weights per vertex
2184
+ // using a partial selection sort (4 iterations of O(elementSize)).
2185
+ const order = new Uint32Array( elementSize );
2186
+
2187
+ for ( let i = 0; i < numVertices; i ++ ) {
2188
+
2189
+ const base = i * elementSize;
2190
+
2191
+ for ( let j = 0; j < elementSize; j ++ ) order[ j ] = j;
2192
+
2193
+ for ( let k = 0; k < 4; k ++ ) {
2194
+
2195
+ let maxIdx = k;
2196
+ let maxW = srcWeights[ base + order[ k ] ] || 0;
2197
+
2198
+ for ( let j = k + 1; j < elementSize; j ++ ) {
2199
+
2200
+ const w = srcWeights[ base + order[ j ] ] || 0;
2201
+
2202
+ if ( w > maxW ) {
2203
+
2204
+ maxW = w;
2205
+ maxIdx = j;
2206
+
2207
+ }
2208
+
2209
+ }
2210
+
2211
+ if ( maxIdx !== k ) {
2212
+
2213
+ const tmp = order[ k ];
2214
+ order[ k ] = order[ maxIdx ];
2215
+ order[ maxIdx ] = tmp;
2216
+
2217
+ }
2218
+
2219
+ }
2220
+
2221
+ let total = 0;
2222
+
2223
+ for ( let j = 0; j < 4; j ++ ) {
2224
+
2225
+ total += srcWeights[ base + order[ j ] ] || 0;
2226
+
2227
+ }
2228
+
2229
+ for ( let j = 0; j < 4; j ++ ) {
2230
+
2231
+ const s = order[ j ];
2232
+
2233
+ if ( total > 0 ) {
2234
+
2235
+ dstIndices[ i * 4 + j ] = srcIndices[ base + s ] || 0;
2236
+ dstWeights[ i * 4 + j ] = ( srcWeights[ base + s ] || 0 ) / total;
2237
+
2238
+ } else {
2239
+
2240
+ dstIndices[ i * 4 + j ] = 0;
2241
+ dstWeights[ i * 4 + j ] = 0;
2242
+
2243
+ }
2244
+
2245
+ }
2246
+
2247
+ }
2248
+
2249
+ }
2250
+
2251
+ _findUVPrimvar( fields ) {
2252
+
2253
+ for ( const key in fields ) {
2254
+
2255
+ if ( ! key.startsWith( 'primvars:' ) ) continue;
2256
+ if ( key.endsWith( ':typeName' ) || key.endsWith( ':elementSize' ) || key.endsWith( ':indices' ) ) continue;
2257
+ if ( key.includes( 'skel:' ) ) continue;
2258
+
2259
+ const typeName = fields[ key + ':typeName' ];
2260
+ if ( typeName && typeName.includes( 'texCoord' ) ) {
2261
+
2262
+ return {
2263
+ uvs: fields[ key ],
2264
+ uvIndices: fields[ key + ':indices' ]
2265
+ };
2266
+
2267
+ }
2268
+
2269
+ }
2270
+
2271
+ const uvs = fields[ 'primvars:st' ] || fields[ 'primvars:UVMap' ];
2272
+ const uvIndices = fields[ 'primvars:st:indices' ];
2273
+ return { uvs, uvIndices };
2274
+
2275
+ }
2276
+
2277
+ _findUV2Primvar( fields ) {
2278
+
2279
+ const uvs2 = fields[ 'primvars:st1' ];
2280
+ const uv2Indices = fields[ 'primvars:st1:indices' ];
2281
+ return { uvs2, uv2Indices };
2282
+
2283
+ }
2284
+
2285
+ _buildHoleMap( polygonHoles ) {
2286
+
2287
+ // polygonHoles is in Arnold format: [holeFaceIdx, parentFaceIdx, holeFaceIdx, parentFaceIdx, ...]
2288
+ // Returns a map: parentFaceIdx -> [holeFaceIdx1, holeFaceIdx2, ...]
2289
+ // Also returns a set of hole face indices to skip during triangulation
2290
+ if ( ! polygonHoles || polygonHoles.length === 0 ) {
2291
+
2292
+ return { parentToHoles: new Map(), holeFaces: new Set() };
2293
+
2294
+ }
2295
+
2296
+ const parentToHoles = new Map();
2297
+ const holeFaces = new Set();
2298
+
2299
+ for ( let i = 0; i < polygonHoles.length; i += 2 ) {
2300
+
2301
+ const holeFaceIdx = polygonHoles[ i ];
2302
+ const parentFaceIdx = polygonHoles[ i + 1 ];
2303
+
2304
+ holeFaces.add( holeFaceIdx );
2305
+
2306
+ if ( ! parentToHoles.has( parentFaceIdx ) ) {
2307
+
2308
+ parentToHoles.set( parentFaceIdx, [] );
2309
+
2310
+ }
2311
+
2312
+ parentToHoles.get( parentFaceIdx ).push( holeFaceIdx );
2313
+
2314
+ }
2315
+
2316
+ return { parentToHoles, holeFaces };
2317
+
2318
+ }
2319
+
2320
+ _triangulateIndicesWithPattern( indices, counts, points = null, holeMap = null ) {
2321
+
2322
+ const triangulated = [];
2323
+ const pattern = []; // Stores face-local indices for each triangle vertex
2324
+
2325
+ // Build face offset lookup for accessing hole face data
2326
+ const faceOffsets = [];
2327
+ let offsetAccum = 0;
2328
+ for ( let i = 0; i < counts.length; i ++ ) {
2329
+
2330
+ faceOffsets.push( offsetAccum );
2331
+ offsetAccum += counts[ i ];
2332
+
2333
+ }
2334
+
2335
+ const parentToHoles = holeMap?.parentToHoles || new Map();
2336
+ const holeFaces = holeMap?.holeFaces || new Set();
2337
+
2338
+ let offset = 0;
2339
+
2340
+ for ( let i = 0; i < counts.length; i ++ ) {
2341
+
2342
+ const count = counts[ i ];
2343
+
2344
+ // Skip faces that are holes - they will be triangulated with their parent
2345
+ if ( holeFaces.has( i ) ) {
2346
+
2347
+ offset += count;
2348
+ continue;
2349
+
2350
+ }
2351
+
2352
+ // Check if this face has holes
2353
+ const holes = parentToHoles.get( i );
2354
+
2355
+ if ( holes && holes.length > 0 && points && points.length > 0 ) {
2356
+
2357
+ // Triangulate face with holes using vertex -> face-vertex mapping
2358
+ const vertexToFaceVertex = new Map();
2359
+
2360
+ const faceIndices = [];
2361
+ for ( let j = 0; j < count; j ++ ) {
2362
+
2363
+ const vertIdx = indices[ offset + j ];
2364
+ faceIndices.push( vertIdx );
2365
+ vertexToFaceVertex.set( vertIdx, offset + j );
2366
+
2367
+ }
2368
+
2369
+ const holeContours = [];
2370
+ for ( const holeFaceIdx of holes ) {
2371
+
2372
+ const holeOffset = faceOffsets[ holeFaceIdx ];
2373
+ const holeCount = counts[ holeFaceIdx ];
2374
+ const holeIndices = [];
2375
+ for ( let j = 0; j < holeCount; j ++ ) {
2376
+
2377
+ const vertIdx = indices[ holeOffset + j ];
2378
+ holeIndices.push( vertIdx );
2379
+ vertexToFaceVertex.set( vertIdx, holeOffset + j );
2380
+
2381
+ }
2382
+
2383
+ holeContours.push( holeIndices );
2384
+
2385
+ }
2386
+
2387
+ const triangles = this._triangulateNGonWithHoles( faceIndices, holeContours, points );
2388
+
2389
+ for ( const tri of triangles ) {
2390
+
2391
+ triangulated.push( tri[ 0 ], tri[ 1 ], tri[ 2 ] );
2392
+ pattern.push(
2393
+ vertexToFaceVertex.get( tri[ 0 ] ),
2394
+ vertexToFaceVertex.get( tri[ 1 ] ),
2395
+ vertexToFaceVertex.get( tri[ 2 ] )
2396
+ );
2397
+
2398
+ }
2399
+
2400
+ } else if ( count === 3 ) {
2401
+
2402
+ triangulated.push(
2403
+ indices[ offset ],
2404
+ indices[ offset + 1 ],
2405
+ indices[ offset + 2 ]
2406
+ );
2407
+ pattern.push( offset, offset + 1, offset + 2 );
2408
+
2409
+ } else if ( count === 4 ) {
2410
+
2411
+ triangulated.push(
2412
+ indices[ offset ],
2413
+ indices[ offset + 1 ],
2414
+ indices[ offset + 2 ],
2415
+ indices[ offset ],
2416
+ indices[ offset + 2 ],
2417
+ indices[ offset + 3 ]
2418
+ );
2419
+ pattern.push(
2420
+ offset, offset + 1, offset + 2,
2421
+ offset, offset + 2, offset + 3
2422
+ );
2423
+
2424
+ } else if ( count > 4 ) {
2425
+
2426
+ // Use ear-clipping for complex n-gons if we have vertex positions
2427
+ if ( points && points.length > 0 ) {
2428
+
2429
+ const faceIndices = [];
2430
+ for ( let j = 0; j < count; j ++ ) {
2431
+
2432
+ faceIndices.push( indices[ offset + j ] );
2433
+
2434
+ }
2435
+
2436
+ const triangles = this._triangulateNGon( faceIndices, points );
2437
+
2438
+ for ( const tri of triangles ) {
2439
+
2440
+ triangulated.push( tri[ 0 ], tri[ 1 ], tri[ 2 ] );
2441
+ // Find local indices within the face
2442
+ pattern.push(
2443
+ offset + faceIndices.indexOf( tri[ 0 ] ),
2444
+ offset + faceIndices.indexOf( tri[ 1 ] ),
2445
+ offset + faceIndices.indexOf( tri[ 2 ] )
2446
+ );
2447
+
2448
+ }
2449
+
2450
+ } else {
2451
+
2452
+ // Fallback to fan triangulation
2453
+ for ( let j = 1; j < count - 1; j ++ ) {
2454
+
2455
+ triangulated.push(
2456
+ indices[ offset ],
2457
+ indices[ offset + j ],
2458
+ indices[ offset + j + 1 ]
2459
+ );
2460
+ pattern.push( offset, offset + j, offset + j + 1 );
2461
+
2462
+ }
2463
+
2464
+ }
2465
+
2466
+ }
2467
+
2468
+ offset += count;
2469
+
2470
+ }
2471
+
2472
+ return { indices: triangulated, pattern };
2473
+
2474
+ }
2475
+
2476
+ _applyTriangulationPattern( indices, pattern ) {
2477
+
2478
+ const result = [];
2479
+ for ( let i = 0; i < pattern.length; i ++ ) {
2480
+
2481
+ result.push( indices[ pattern[ i ] ] );
2482
+
2483
+ }
2484
+
2485
+ return result;
2486
+
2487
+ }
2488
+
2489
+ _triangulateNGon( faceIndices, points ) {
2490
+
2491
+ // Project 3D polygon to 2D for triangulation using Newell's method for normal
2492
+ const contour2D = [];
2493
+ const contour3D = [];
2494
+
2495
+ for ( const idx of faceIndices ) {
2496
+
2497
+ contour3D.push( new Vector3(
2498
+ points[ idx * 3 ],
2499
+ points[ idx * 3 + 1 ],
2500
+ points[ idx * 3 + 2 ]
2501
+ ) );
2502
+
2503
+ }
2504
+
2505
+ // Calculate polygon normal using Newell's method
2506
+ const normal = new Vector3();
2507
+ for ( let i = 0; i < contour3D.length; i ++ ) {
2508
+
2509
+ const curr = contour3D[ i ];
2510
+ const next = contour3D[ ( i + 1 ) % contour3D.length ];
2511
+ normal.x += ( curr.y - next.y ) * ( curr.z + next.z );
2512
+ normal.y += ( curr.z - next.z ) * ( curr.x + next.x );
2513
+ normal.z += ( curr.x - next.x ) * ( curr.y + next.y );
2514
+
2515
+ }
2516
+
2517
+ normal.normalize();
2518
+
2519
+ // Create tangent basis for projection
2520
+ const tangent = new Vector3();
2521
+ const bitangent = new Vector3();
2522
+
2523
+ if ( Math.abs( normal.y ) > 0.9 ) {
2524
+
2525
+ tangent.set( 1, 0, 0 );
2526
+
2527
+ } else {
2528
+
2529
+ tangent.set( 0, 1, 0 );
2530
+
2531
+ }
2532
+
2533
+ bitangent.crossVectors( normal, tangent ).normalize();
2534
+ tangent.crossVectors( bitangent, normal ).normalize();
2535
+
2536
+ // Project to 2D
2537
+ for ( const p of contour3D ) {
2538
+
2539
+ contour2D.push( new Vector2( p.dot( tangent ), p.dot( bitangent ) ) );
2540
+
2541
+ }
2542
+
2543
+ // Triangulate using ShapeUtils
2544
+ const triangles = ShapeUtils.triangulateShape( contour2D, [] );
2545
+
2546
+ // Map back to original indices
2547
+ const result = [];
2548
+ for ( const tri of triangles ) {
2549
+
2550
+ result.push( [
2551
+ faceIndices[ tri[ 0 ] ],
2552
+ faceIndices[ tri[ 1 ] ],
2553
+ faceIndices[ tri[ 2 ] ]
2554
+ ] );
2555
+
2556
+ }
2557
+
2558
+ return result;
2559
+
2560
+ }
2561
+
2562
+ _triangulateNGonWithHoles( outerIndices, holeContours, points ) {
2563
+
2564
+ // Project 3D polygon with holes to 2D for triangulation
2565
+ const outer3D = [];
2566
+
2567
+ for ( const idx of outerIndices ) {
2568
+
2569
+ outer3D.push( new Vector3(
2570
+ points[ idx * 3 ],
2571
+ points[ idx * 3 + 1 ],
2572
+ points[ idx * 3 + 2 ]
2573
+ ) );
2574
+
2575
+ }
2576
+
2577
+ // Calculate polygon normal using Newell's method
2578
+ const normal = new Vector3();
2579
+ for ( let i = 0; i < outer3D.length; i ++ ) {
2580
+
2581
+ const curr = outer3D[ i ];
2582
+ const next = outer3D[ ( i + 1 ) % outer3D.length ];
2583
+ normal.x += ( curr.y - next.y ) * ( curr.z + next.z );
2584
+ normal.y += ( curr.z - next.z ) * ( curr.x + next.x );
2585
+ normal.z += ( curr.x - next.x ) * ( curr.y + next.y );
2586
+
2587
+ }
2588
+
2589
+ normal.normalize();
2590
+
2591
+ // Create tangent basis for projection
2592
+ const tangent = new Vector3();
2593
+ const bitangent = new Vector3();
2594
+
2595
+ if ( Math.abs( normal.y ) > 0.9 ) {
2596
+
2597
+ tangent.set( 1, 0, 0 );
2598
+
2599
+ } else {
2600
+
2601
+ tangent.set( 0, 1, 0 );
2602
+
2603
+ }
2604
+
2605
+ bitangent.crossVectors( normal, tangent ).normalize();
2606
+ tangent.crossVectors( bitangent, normal ).normalize();
2607
+
2608
+ // Project outer contour to 2D
2609
+ const outer2D = [];
2610
+ for ( const p of outer3D ) {
2611
+
2612
+ outer2D.push( new Vector2( p.dot( tangent ), p.dot( bitangent ) ) );
2613
+
2614
+ }
2615
+
2616
+ // Project hole contours to 2D
2617
+ const holes2D = [];
2618
+
2619
+ for ( const holeIndices of holeContours ) {
2620
+
2621
+ const hole2D = [];
2622
+
2623
+ for ( const idx of holeIndices ) {
2624
+
2625
+ const p = new Vector3(
2626
+ points[ idx * 3 ],
2627
+ points[ idx * 3 + 1 ],
2628
+ points[ idx * 3 + 2 ]
2629
+ );
2630
+ hole2D.push( new Vector2( p.dot( tangent ), p.dot( bitangent ) ) );
2631
+
2632
+ }
2633
+
2634
+ holes2D.push( hole2D );
2635
+
2636
+ }
2637
+
2638
+ // Build combined index array: outer contour followed by all holes
2639
+ const allIndices = [ ...outerIndices ];
2640
+ for ( const holeIndices of holeContours ) {
2641
+
2642
+ allIndices.push( ...holeIndices );
2643
+
2644
+ }
2645
+
2646
+ // Triangulate using ShapeUtils with holes
2647
+ const triangles = ShapeUtils.triangulateShape( outer2D, holes2D );
2648
+
2649
+ // Map back to original vertex indices
2650
+ const result = [];
2651
+ for ( const tri of triangles ) {
2652
+
2653
+ result.push( [
2654
+ allIndices[ tri[ 0 ] ],
2655
+ allIndices[ tri[ 1 ] ],
2656
+ allIndices[ tri[ 2 ] ]
2657
+ ] );
2658
+
2659
+ }
2660
+
2661
+ return result;
2662
+
2663
+ }
2664
+
2665
+ _triangulateIndices( indices, counts ) {
2666
+
2667
+ const triangulated = [];
2668
+ let offset = 0;
2669
+
2670
+ for ( let i = 0; i < counts.length; i ++ ) {
2671
+
2672
+ const count = counts[ i ];
2673
+
2674
+ if ( count === 3 ) {
2675
+
2676
+ triangulated.push(
2677
+ indices[ offset ],
2678
+ indices[ offset + 1 ],
2679
+ indices[ offset + 2 ]
2680
+ );
2681
+
2682
+ } else if ( count === 4 ) {
2683
+
2684
+ triangulated.push(
2685
+ indices[ offset ],
2686
+ indices[ offset + 1 ],
2687
+ indices[ offset + 2 ],
2688
+ indices[ offset ],
2689
+ indices[ offset + 2 ],
2690
+ indices[ offset + 3 ]
2691
+ );
2692
+
2693
+ } else if ( count > 4 ) {
2694
+
2695
+ // Fan triangulation for n-gons
2696
+ for ( let j = 1; j < count - 1; j ++ ) {
2697
+
2698
+ triangulated.push(
2699
+ indices[ offset ],
2700
+ indices[ offset + j ],
2701
+ indices[ offset + j + 1 ]
2702
+ );
2703
+
2704
+ }
2705
+
2706
+ }
2707
+
2708
+ offset += count;
2709
+
2710
+ }
2711
+
2712
+ return triangulated;
2713
+
2714
+ }
2715
+
2716
+ _expandAttribute( data, indices, itemSize ) {
2717
+
2718
+ const expanded = new Array( indices.length * itemSize );
2719
+
2720
+ for ( let i = 0; i < indices.length; i ++ ) {
2721
+
2722
+ const srcIdx = indices[ i ];
2723
+
2724
+ for ( let j = 0; j < itemSize; j ++ ) {
2725
+
2726
+ expanded[ i * itemSize + j ] = data[ srcIdx * itemSize + j ];
2727
+
2728
+ }
2729
+
2730
+ }
2731
+
2732
+ return expanded;
2733
+
2734
+ }
2735
+
2736
+ /**
2737
+ * Compute per-vertex normals from indexed triangle data.
2738
+ * Accumulates area-weighted face normals at each shared vertex and normalizes.
2739
+ */
2740
+ _computeVertexNormals( points, indices ) {
2741
+
2742
+ const numVertices = points.length / 3;
2743
+ const normals = new Float32Array( numVertices * 3 );
2744
+
2745
+ for ( let i = 0; i < indices.length; i += 3 ) {
2746
+
2747
+ const a = indices[ i ];
2748
+ const b = indices[ i + 1 ];
2749
+ const c = indices[ i + 2 ];
2750
+
2751
+ const ax = points[ a * 3 ], ay = points[ a * 3 + 1 ], az = points[ a * 3 + 2 ];
2752
+ const bx = points[ b * 3 ], by = points[ b * 3 + 1 ], bz = points[ b * 3 + 2 ];
2753
+ const cx = points[ c * 3 ], cy = points[ c * 3 + 1 ], cz = points[ c * 3 + 2 ];
2754
+
2755
+ const e1x = bx - ax, e1y = by - ay, e1z = bz - az;
2756
+ const e2x = cx - ax, e2y = cy - ay, e2z = cz - az;
2757
+
2758
+ const nx = e1y * e2z - e1z * e2y;
2759
+ const ny = e1z * e2x - e1x * e2z;
2760
+ const nz = e1x * e2y - e1y * e2x;
2761
+
2762
+ normals[ a * 3 ] += nx; normals[ a * 3 + 1 ] += ny; normals[ a * 3 + 2 ] += nz;
2763
+ normals[ b * 3 ] += nx; normals[ b * 3 + 1 ] += ny; normals[ b * 3 + 2 ] += nz;
2764
+ normals[ c * 3 ] += nx; normals[ c * 3 + 1 ] += ny; normals[ c * 3 + 2 ] += nz;
2765
+
2766
+ }
2767
+
2768
+ for ( let i = 0; i < numVertices; i ++ ) {
2769
+
2770
+ const x = normals[ i * 3 ], y = normals[ i * 3 + 1 ], z = normals[ i * 3 + 2 ];
2771
+ const len = Math.sqrt( x * x + y * y + z * z );
2772
+
2773
+ if ( len > 0 ) {
2774
+
2775
+ normals[ i * 3 ] /= len;
2776
+ normals[ i * 3 + 1 ] /= len;
2777
+ normals[ i * 3 + 2 ] /= len;
2778
+
2779
+ }
2780
+
2781
+ }
2782
+
2783
+ return normals;
2784
+
2785
+ }
2786
+
2787
+ /**
2788
+ * Get the material path for a mesh, checking various binding sources.
2789
+ */
2790
+ _getMaterialPath( meshPath, fields ) {
2791
+
2792
+ let materialPath = null;
2793
+ const materialBinding = fields[ 'material:binding' ];
2794
+
2795
+ if ( materialBinding ) {
2796
+
2797
+ materialPath = Array.isArray( materialBinding ) ? materialBinding[ 0 ] : materialBinding;
2798
+
2799
+ }
2800
+
2801
+ // Use variant-aware lookup if no direct binding in fields
2802
+ if ( ! materialPath ) {
2803
+
2804
+ materialPath = this._getMaterialBindingTarget( meshPath );
2805
+
2806
+ }
2807
+
2808
+ return materialPath;
2809
+
2810
+ }
2811
+
2812
+ _buildMaterial( meshPath, fields ) {
2813
+
2814
+ const material = new MeshPhysicalMaterial();
2815
+
2816
+ let materialPath = null;
2817
+ const materialBinding = fields[ 'material:binding' ];
2818
+
2819
+ if ( materialBinding ) {
2820
+
2821
+ materialPath = Array.isArray( materialBinding ) ? materialBinding[ 0 ] : materialBinding;
2822
+
2823
+ }
2824
+
2825
+ // Use variant-aware lookup if no direct binding in fields
2826
+ if ( ! materialPath ) {
2827
+
2828
+ materialPath = this._getMaterialBindingTarget( meshPath );
2829
+
2830
+ }
2831
+
2832
+ if ( ! materialPath ) {
2833
+
2834
+ const materialPaths = [];
2835
+ const prefix = meshPath + '/';
2836
+
2837
+ for ( const path in this.specsByPath ) {
2838
+
2839
+ if ( ! path.startsWith( prefix ) ) continue;
2840
+ if ( ! path.endsWith( '.material:binding' ) ) continue;
2841
+
2842
+ const bindingSpec = this.specsByPath[ path ];
2843
+ if ( ! bindingSpec ) continue;
2844
+
2845
+ const targetPaths = bindingSpec.fields.targetPaths;
2846
+ if ( targetPaths && targetPaths.length > 0 ) {
2847
+
2848
+ materialPaths.push( targetPaths[ 0 ] );
2849
+
2850
+ }
2851
+
2852
+ }
2853
+
2854
+ if ( materialPaths.length > 0 ) {
2855
+
2856
+ materialPath = this._pickBestMaterial( materialPaths );
2857
+
2858
+ }
2859
+
2860
+ }
2861
+
2862
+ if ( ! materialPath ) {
2863
+
2864
+ // Use material index for O(1) lookup instead of O(n) iteration
2865
+ const meshParts = meshPath.split( '/' );
2866
+ const rootPath = '/' + meshParts[ 1 ];
2867
+
2868
+ const materialsInRoot = this.materialsByRoot.get( rootPath );
2869
+
2870
+ if ( materialsInRoot ) {
2871
+
2872
+ for ( const path of materialsInRoot ) {
2873
+
2874
+ if ( path.startsWith( rootPath + '/Looks/' ) ||
2875
+ path.startsWith( rootPath + '/Materials/' ) ) {
2876
+
2877
+ materialPath = path;
2878
+ break;
2879
+
2880
+ }
2881
+
2882
+ }
2883
+
2884
+ }
2885
+
2886
+ }
2887
+
2888
+ if ( materialPath ) {
2889
+
2890
+ this._applyMaterial( material, materialPath );
2891
+
2892
+ }
2893
+
2894
+ return material;
2895
+
2896
+ }
2897
+
2898
+ _buildMaterialForPath( materialPath ) {
2899
+
2900
+ const material = new MeshPhysicalMaterial();
2901
+
2902
+ if ( materialPath ) {
2903
+
2904
+ this._applyMaterial( material, materialPath );
2905
+
2906
+ }
2907
+
2908
+ return material;
2909
+
2910
+ }
2911
+
2912
+ /**
2913
+ * Apply material binding from a prim path to a mesh.
2914
+ * Used when merging referenced geometry into a prim that has material binding.
2915
+ */
2916
+ _applyMaterialBinding( mesh, primPath ) {
2917
+
2918
+ // Look for material:binding on this prim
2919
+ const bindingPath = primPath + '.material:binding';
2920
+ const bindingSpec = this.specsByPath[ bindingPath ];
2921
+
2922
+ if ( ! bindingSpec ) return;
2923
+
2924
+ let materialPath = null;
2925
+ const targetPaths = bindingSpec.fields?.targetPaths || bindingSpec.fields?.default;
2926
+
2927
+ if ( targetPaths ) {
2928
+
2929
+ materialPath = Array.isArray( targetPaths ) ? targetPaths[ 0 ] : targetPaths;
2930
+
2931
+ }
2932
+
2933
+ if ( ! materialPath ) return;
2934
+
2935
+ // Clean the material path
2936
+ materialPath = String( materialPath ).replace( /^<|>$/g, '' );
2937
+
2938
+ // Build and apply the material
2939
+ const material = new MeshPhysicalMaterial();
2940
+ this._applyMaterial( material, materialPath );
2941
+ mesh.material = material;
2942
+
2943
+ }
2944
+
2945
+ _pickBestMaterial( materialPaths ) {
2946
+
2947
+ for ( const materialPath of materialPaths ) {
2948
+
2949
+ const shaderPaths = this.shadersByMaterialPath.get( materialPath );
2950
+ if ( ! shaderPaths ) continue;
2951
+
2952
+ for ( const path of shaderPaths ) {
2953
+
2954
+ const attrs = this._getAttributes( path );
2955
+ if ( attrs[ 'info:id' ] === 'UsdUVTexture' && attrs[ 'inputs:file' ] ) {
2956
+
2957
+ return materialPath;
2958
+
2959
+ }
2960
+
2961
+ }
2962
+
2963
+ }
2964
+
2965
+ return materialPaths[ 0 ];
2966
+
2967
+ }
2968
+
2969
+ _applyMaterial( material, materialPath ) {
2970
+
2971
+ const materialSpec = this.specsByPath[ materialPath ];
2972
+ if ( ! materialSpec ) return;
2973
+
2974
+ const shaderPaths = this.shadersByMaterialPath.get( materialPath );
2975
+ if ( ! shaderPaths ) return;
2976
+
2977
+ for ( const path of shaderPaths ) {
2978
+
2979
+ const spec = this.specsByPath[ path ];
2980
+ if ( ! spec ) continue;
2981
+
2982
+ const shaderAttrs = this._getAttributes( path );
2983
+ const infoId = shaderAttrs[ 'info:id' ] || spec.fields[ 'info:id' ];
2984
+
2985
+ if ( infoId === 'UsdPreviewSurface' || infoId === 'ND_UsdPreviewSurface_surfaceshader' ) {
2986
+
2987
+ this._applyPreviewSurface( material, path );
2988
+
2989
+ } else if ( infoId === 'arnold:openpbr_surface' ) {
2990
+
2991
+ this._applyOpenPBRSurface( material, path );
2992
+
2993
+ }
2994
+
2995
+ }
2996
+
2997
+ }
2998
+
2999
+ /**
3000
+ * Shared helper for applying texture or value from shader attribute.
3001
+ * Reduces duplication between _applyPreviewSurface and _applyOpenPBRSurface.
3002
+ */
3003
+ _applyTextureOrValue( material, shaderPath, fields, attrName, textureProperty, colorSpace, valueCallback, textureGetter ) {
3004
+
3005
+ const attrPath = shaderPath + '.' + attrName;
3006
+ const spec = this.specsByPath[ attrPath ];
3007
+
3008
+ if ( spec && spec.fields.connectionPaths && spec.fields.connectionPaths.length > 0 ) {
3009
+
3010
+ // For OpenPBR, try all connection paths; for PreviewSurface, just the first
3011
+ const paths = textureGetter === this._getTextureFromOpenPBRConnection
3012
+ ? spec.fields.connectionPaths
3013
+ : [ spec.fields.connectionPaths[ 0 ] ];
3014
+
3015
+ for ( const connPath of paths ) {
3016
+
3017
+ const texture = textureGetter.call( this, connPath );
3018
+
3019
+ if ( texture ) {
3020
+
3021
+ texture.colorSpace = colorSpace;
3022
+ material[ textureProperty ] = texture;
3023
+ return true;
3024
+
3025
+ }
3026
+
3027
+ }
3028
+
3029
+ }
3030
+
3031
+ if ( fields[ attrName ] !== undefined && valueCallback ) {
3032
+
3033
+ valueCallback( fields[ attrName ] );
3034
+
3035
+ }
3036
+
3037
+ return false;
3038
+
3039
+ }
3040
+
3041
+ _applyPreviewSurface( material, shaderPath ) {
3042
+
3043
+ const fields = this._getAttributes( shaderPath );
3044
+
3045
+ const applyTexture = ( attrName, textureProperty, colorSpace, valueCallback ) => {
3046
+
3047
+ return this._applyTextureOrValue(
3048
+ material, shaderPath, fields, attrName, textureProperty, colorSpace, valueCallback,
3049
+ this._getTextureFromConnection
3050
+ );
3051
+
3052
+ };
3053
+
3054
+ const getAttrSpec = ( attrName ) => {
3055
+
3056
+ const attrPath = shaderPath + '.' + attrName;
3057
+ return this.specsByPath[ attrPath ];
3058
+
3059
+ };
3060
+
3061
+ // Diffuse color / base color map
3062
+ applyTexture(
3063
+ 'inputs:diffuseColor',
3064
+ 'map',
3065
+ SRGBColorSpace,
3066
+ ( color ) => {
3067
+
3068
+ if ( Array.isArray( color ) && color.length >= 3 ) {
3069
+
3070
+ material.color.setRGB( color[ 0 ], color[ 1 ], color[ 2 ], SRGBColorSpace );
3071
+
3072
+ }
3073
+
3074
+ }
3075
+ );
3076
+
3077
+ // Apply UsdUVTexture scale to diffuse color (output = texture * scale + bias)
3078
+ if ( material.map && material.map.userData.scale ) {
3079
+
3080
+ const scale = material.map.userData.scale;
3081
+ if ( Array.isArray( scale ) && scale.length >= 3 ) {
3082
+
3083
+ material.color.setRGB( scale[ 0 ], scale[ 1 ], scale[ 2 ], SRGBColorSpace );
3084
+
3085
+ }
3086
+
3087
+ }
3088
+
3089
+ // Emissive
3090
+ applyTexture(
3091
+ 'inputs:emissiveColor',
3092
+ 'emissiveMap',
3093
+ SRGBColorSpace,
3094
+ ( color ) => {
3095
+
3096
+ if ( Array.isArray( color ) && color.length >= 3 ) {
3097
+
3098
+ material.emissive.setRGB( color[ 0 ], color[ 1 ], color[ 2 ], SRGBColorSpace );
3099
+
3100
+ }
3101
+
3102
+ }
3103
+ );
3104
+
3105
+ if ( material.emissiveMap ) {
3106
+
3107
+ if ( material.emissiveMap.userData.scale ) {
3108
+
3109
+ const scale = material.emissiveMap.userData.scale;
3110
+ if ( Array.isArray( scale ) && scale.length >= 3 ) {
3111
+
3112
+ material.emissive.setRGB( scale[ 0 ], scale[ 1 ], scale[ 2 ], SRGBColorSpace );
3113
+
3114
+ }
3115
+
3116
+ } else {
3117
+
3118
+ material.emissive.set( 0xffffff );
3119
+
3120
+ }
3121
+
3122
+ }
3123
+
3124
+ // Normal map
3125
+ applyTexture( 'inputs:normal', 'normalMap', NoColorSpace, null );
3126
+
3127
+ // Apply normal map scale from UsdUVTexture scale input
3128
+ if ( material.normalMap && material.normalMap.userData.scale ) {
3129
+
3130
+ const scale = material.normalMap.userData.scale;
3131
+ // UsdUVTexture scale is float4 (r,g,b,a), use first two components for normalScale
3132
+ material.normalScale = new Vector2( scale[ 0 ], scale[ 1 ] );
3133
+
3134
+ }
3135
+
3136
+ // Roughness
3137
+ const hasRoughnessMap = applyTexture(
3138
+ 'inputs:roughness',
3139
+ 'roughnessMap',
3140
+ NoColorSpace,
3141
+ ( value ) => {
3142
+
3143
+ material.roughness = value;
3144
+
3145
+ }
3146
+ );
3147
+
3148
+ if ( hasRoughnessMap ) {
3149
+
3150
+ material.roughness = 1.0;
3151
+
3152
+ }
3153
+
3154
+ // Metallic
3155
+ const hasMetalnessMap = applyTexture(
3156
+ 'inputs:metallic',
3157
+ 'metalnessMap',
3158
+ NoColorSpace,
3159
+ ( value ) => {
3160
+
3161
+ material.metalness = value;
3162
+
3163
+ }
3164
+ );
3165
+
3166
+ if ( hasMetalnessMap ) {
3167
+
3168
+ material.metalness = 1.0;
3169
+
3170
+ }
3171
+
3172
+ // Occlusion
3173
+ applyTexture( 'inputs:occlusion', 'aoMap', NoColorSpace, null );
3174
+
3175
+ // IOR
3176
+ if ( fields[ 'inputs:ior' ] !== undefined ) {
3177
+
3178
+ material.ior = fields[ 'inputs:ior' ];
3179
+
3180
+ }
3181
+
3182
+ // Specular color
3183
+ applyTexture(
3184
+ 'inputs:specularColor',
3185
+ 'specularColorMap',
3186
+ SRGBColorSpace,
3187
+ ( color ) => {
3188
+
3189
+ if ( Array.isArray( color ) && color.length >= 3 ) {
3190
+
3191
+ material.specularColor.setRGB( color[ 0 ], color[ 1 ], color[ 2 ], SRGBColorSpace );
3192
+
3193
+ }
3194
+
3195
+ }
3196
+ );
3197
+
3198
+ // Apply UsdUVTexture scale to specular color
3199
+ if ( material.specularColorMap && material.specularColorMap.userData.scale ) {
3200
+
3201
+ const scale = material.specularColorMap.userData.scale;
3202
+ if ( Array.isArray( scale ) && scale.length >= 3 ) {
3203
+
3204
+ material.specularColor.setRGB( scale[ 0 ], scale[ 1 ], scale[ 2 ], SRGBColorSpace );
3205
+
3206
+ }
3207
+
3208
+ }
3209
+
3210
+ // Clearcoat
3211
+ if ( fields[ 'inputs:clearcoat' ] !== undefined ) {
3212
+
3213
+ material.clearcoat = fields[ 'inputs:clearcoat' ];
3214
+
3215
+ }
3216
+
3217
+ // Clearcoat roughness
3218
+ if ( fields[ 'inputs:clearcoatRoughness' ] !== undefined ) {
3219
+
3220
+ material.clearcoatRoughness = fields[ 'inputs:clearcoatRoughness' ];
3221
+
3222
+ }
3223
+
3224
+ // Opacity and opacity modes
3225
+ const opacityThreshold = fields[ 'inputs:opacityThreshold' ] !== undefined ? fields[ 'inputs:opacityThreshold' ] : 0.0;
3226
+
3227
+ // Check if opacity is connected to a texture (e.g., diffuse texture's alpha)
3228
+ const opacitySpec = getAttrSpec( 'inputs:opacity' );
3229
+ const hasOpacityConnection = opacitySpec?.fields?.connectionPaths?.length > 0;
3230
+
3231
+ if ( hasOpacityConnection ) {
3232
+
3233
+ // Opacity from texture alpha - use the diffuse map's alpha channel
3234
+ if ( opacityThreshold > 0 ) {
3235
+
3236
+ // Alpha cutoff mode
3237
+ material.alphaTest = opacityThreshold;
3238
+ material.transparent = false;
3239
+
3240
+ } else {
3241
+
3242
+ // Alpha blend mode
3243
+ material.transparent = true;
3244
+
3245
+ }
3246
+
3247
+ } else {
3248
+
3249
+ // Direct opacity value
3250
+ const opacity = fields[ 'inputs:opacity' ] !== undefined ? fields[ 'inputs:opacity' ] : 1.0;
3251
+
3252
+ if ( opacity < 1.0 ) {
3253
+
3254
+ material.transparent = true;
3255
+ material.opacity = opacity;
3256
+
3257
+ }
3258
+
3259
+ }
3260
+
3261
+ }
3262
+
3263
+ _applyOpenPBRSurface( material, shaderPath ) {
3264
+
3265
+ const fields = this._getAttributes( shaderPath );
3266
+
3267
+ const applyTexture = ( attrName, textureProperty, colorSpace, valueCallback ) => {
3268
+
3269
+ return this._applyTextureOrValue(
3270
+ material, shaderPath, fields, attrName, textureProperty, colorSpace, valueCallback,
3271
+ this._getTextureFromOpenPBRConnection
3272
+ );
3273
+
3274
+ };
3275
+
3276
+ // Base color (diffuse)
3277
+ applyTexture(
3278
+ 'inputs:base_color',
3279
+ 'map',
3280
+ SRGBColorSpace,
3281
+ ( color ) => {
3282
+
3283
+ if ( Array.isArray( color ) && color.length >= 3 ) {
3284
+
3285
+ material.color.setRGB( color[ 0 ], color[ 1 ], color[ 2 ], SRGBColorSpace );
3286
+
3287
+ }
3288
+
3289
+ }
3290
+ );
3291
+
3292
+ // Apply UsdUVTexture scale to base color
3293
+ if ( material.map && material.map.userData.scale ) {
3294
+
3295
+ const scale = material.map.userData.scale;
3296
+ if ( Array.isArray( scale ) && scale.length >= 3 ) {
3297
+
3298
+ material.color.setRGB( scale[ 0 ], scale[ 1 ], scale[ 2 ], SRGBColorSpace );
3299
+
3300
+ }
3301
+
3302
+ }
3303
+
3304
+ // Base metalness
3305
+ applyTexture(
3306
+ 'inputs:base_metalness',
3307
+ 'metalnessMap',
3308
+ NoColorSpace,
3309
+ ( value ) => {
3310
+
3311
+ if ( typeof value === 'number' ) {
3312
+
3313
+ material.metalness = value;
3314
+
3315
+ }
3316
+
3317
+ }
3318
+ );
3319
+
3320
+ // Specular roughness
3321
+ applyTexture(
3322
+ 'inputs:specular_roughness',
3323
+ 'roughnessMap',
3324
+ NoColorSpace,
3325
+ ( value ) => {
3326
+
3327
+ if ( typeof value === 'number' ) {
3328
+
3329
+ material.roughness = value;
3330
+
3331
+ }
3332
+
3333
+ }
3334
+ );
3335
+
3336
+ // Emission color
3337
+ const hasEmissionMap = applyTexture(
3338
+ 'inputs:emission_color',
3339
+ 'emissiveMap',
3340
+ SRGBColorSpace,
3341
+ ( color ) => {
3342
+
3343
+ if ( Array.isArray( color ) && color.length >= 3 ) {
3344
+
3345
+ material.emissive.setRGB( color[ 0 ], color[ 1 ], color[ 2 ], SRGBColorSpace );
3346
+
3347
+ }
3348
+
3349
+ }
3350
+ );
3351
+
3352
+ // Emission luminance/weight - multiply emissive by this factor
3353
+ const emissionLuminance = fields[ 'inputs:emission_luminance' ];
3354
+
3355
+ if ( emissionLuminance !== undefined && emissionLuminance > 0 ) {
3356
+
3357
+ if ( hasEmissionMap ) {
3358
+
3359
+ material.emissiveIntensity = emissionLuminance;
3360
+
3361
+ } else {
3362
+
3363
+ // Scale the emissive color by luminance
3364
+ material.emissive.multiplyScalar( emissionLuminance );
3365
+
3366
+ }
3367
+
3368
+ }
3369
+
3370
+ // Transmission (transparency)
3371
+ const transmissionWeight = fields[ 'inputs:transmission_weight' ];
3372
+
3373
+ if ( transmissionWeight !== undefined && transmissionWeight > 0 ) {
3374
+
3375
+ material.transmission = transmissionWeight;
3376
+
3377
+ const transmissionDepth = fields[ 'inputs:transmission_depth' ];
3378
+
3379
+ if ( transmissionDepth !== undefined ) {
3380
+
3381
+ material.thickness = transmissionDepth;
3382
+
3383
+ }
3384
+
3385
+ const transmissionColor = fields[ 'inputs:transmission_color' ];
3386
+
3387
+ if ( transmissionColor !== undefined && Array.isArray( transmissionColor ) ) {
3388
+
3389
+ material.attenuationColor.setRGB( transmissionColor[ 0 ], transmissionColor[ 1 ], transmissionColor[ 2 ] );
3390
+ material.attenuationDistance = transmissionDepth || 1.0;
3391
+
3392
+ }
3393
+
3394
+ }
3395
+
3396
+ // Geometry opacity (overall surface opacity)
3397
+ const geometryOpacity = fields[ 'inputs:geometry_opacity' ];
3398
+
3399
+ if ( geometryOpacity !== undefined && geometryOpacity < 1.0 ) {
3400
+
3401
+ material.opacity = geometryOpacity;
3402
+ material.transparent = true;
3403
+
3404
+ }
3405
+
3406
+ // Specular IOR
3407
+ const specularIOR = fields[ 'inputs:specular_ior' ];
3408
+
3409
+ if ( specularIOR !== undefined ) {
3410
+
3411
+ material.ior = specularIOR;
3412
+
3413
+ }
3414
+
3415
+ // Coat (clearcoat)
3416
+ const coatWeight = fields[ 'inputs:coat_weight' ];
3417
+
3418
+ if ( coatWeight !== undefined && coatWeight > 0 ) {
3419
+
3420
+ material.clearcoat = coatWeight;
3421
+
3422
+ const coatRoughness = fields[ 'inputs:coat_roughness' ];
3423
+
3424
+ if ( coatRoughness !== undefined ) {
3425
+
3426
+ material.clearcoatRoughness = coatRoughness;
3427
+
3428
+ }
3429
+
3430
+ }
3431
+
3432
+ // Thin film (iridescence)
3433
+ const thinFilmWeight = fields[ 'inputs:thin_film_weight' ];
3434
+
3435
+ if ( thinFilmWeight !== undefined && thinFilmWeight > 0 ) {
3436
+
3437
+ material.iridescence = thinFilmWeight;
3438
+
3439
+ const thinFilmIOR = fields[ 'inputs:thin_film_ior' ];
3440
+
3441
+ if ( thinFilmIOR !== undefined ) {
3442
+
3443
+ material.iridescenceIOR = thinFilmIOR;
3444
+
3445
+ }
3446
+
3447
+ const thinFilmThickness = fields[ 'inputs:thin_film_thickness' ];
3448
+
3449
+ if ( thinFilmThickness !== undefined ) {
3450
+
3451
+ // OpenPBR uses micrometers, Three.js uses nanometers
3452
+ const thicknessNm = thinFilmThickness * 1000;
3453
+ material.iridescenceThicknessRange = [ thicknessNm, thicknessNm ];
3454
+
3455
+ }
3456
+
3457
+ }
3458
+
3459
+ // Specular
3460
+ const specularWeight = fields[ 'inputs:specular_weight' ];
3461
+
3462
+ if ( specularWeight !== undefined ) {
3463
+
3464
+ material.specularIntensity = specularWeight;
3465
+
3466
+ }
3467
+
3468
+ const specularColor = fields[ 'inputs:specular_color' ];
3469
+
3470
+ if ( specularColor !== undefined && Array.isArray( specularColor ) ) {
3471
+
3472
+ material.specularColor.setRGB( specularColor[ 0 ], specularColor[ 1 ], specularColor[ 2 ] );
3473
+
3474
+ }
3475
+
3476
+ // Anisotropy
3477
+ const anisotropy = fields[ 'inputs:specular_roughness_anisotropy' ];
3478
+
3479
+ if ( anisotropy !== undefined && anisotropy > 0 ) {
3480
+
3481
+ material.anisotropy = anisotropy;
3482
+
3483
+ }
3484
+
3485
+ // Geometry normal (normal map)
3486
+ applyTexture(
3487
+ 'inputs:geometry_normal',
3488
+ 'normalMap',
3489
+ NoColorSpace,
3490
+ null
3491
+ );
3492
+
3493
+ }
3494
+
3495
+ _getTextureFromOpenPBRConnection( connPath ) {
3496
+
3497
+ // connPath is like /Material/NodeGraph.outputs:baseColor or /Material/Shader.outputs:out
3498
+ const cleanPath = connPath.replace( /<|>/g, '' );
3499
+ const shaderPath = cleanPath.split( '.' )[ 0 ];
3500
+ const shaderSpec = this.specsByPath[ shaderPath ];
3501
+
3502
+ if ( ! shaderSpec ) return null;
3503
+
3504
+ const attrs = this._getAttributes( shaderPath );
3505
+ const infoId = attrs[ 'info:id' ] || shaderSpec.fields[ 'info:id' ];
3506
+ const typeName = shaderSpec.fields.typeName;
3507
+
3508
+ // Handle NodeGraph - follow output connection to internal shader
3509
+ if ( typeName === 'NodeGraph' ) {
3510
+
3511
+ // Get the output attribute that's connected
3512
+ const outputName = cleanPath.split( '.' )[ 1 ]; // e.g., "outputs:baseColor"
3513
+ const outputAttrPath = shaderPath + '.' + outputName;
3514
+ const outputSpec = this.specsByPath[ outputAttrPath ];
3515
+
3516
+ if ( outputSpec?.fields?.connectionPaths?.length > 0 ) {
3517
+
3518
+ // Follow the internal connection
3519
+ return this._getTextureFromOpenPBRConnection( outputSpec.fields.connectionPaths[ 0 ] );
3520
+
3521
+ }
3522
+
3523
+ return null;
3524
+
3525
+ }
3526
+
3527
+ // Handle arnold:image - Arnold's texture node
3528
+ if ( infoId === 'arnold:image' ) {
3529
+
3530
+ const filePath = attrs[ 'inputs:filename' ];
3531
+ if ( ! filePath ) return null;
3532
+
3533
+ return this._loadTextureFromPath( filePath );
3534
+
3535
+ }
3536
+
3537
+ // Handle MaterialX image nodes (ND_image_color4, ND_image_color3, etc.)
3538
+ if ( infoId && infoId.startsWith( 'ND_image_' ) ) {
3539
+
3540
+ const filePath = attrs[ 'inputs:file' ];
3541
+ if ( ! filePath ) return null;
3542
+
3543
+ return this._loadTextureFromPath( filePath );
3544
+
3545
+ }
3546
+
3547
+ // Handle Maya file texture - follow the inColor connection to the actual image
3548
+ if ( infoId === 'MayaND_fileTexture_color4' ) {
3549
+
3550
+ const inColorPath = shaderPath + '.inputs:inColor';
3551
+ const inColorSpec = this.specsByPath[ inColorPath ];
3552
+
3553
+ if ( inColorSpec?.fields?.connectionPaths?.length > 0 ) {
3554
+
3555
+ return this._getTextureFromOpenPBRConnection( inColorSpec.fields.connectionPaths[ 0 ] );
3556
+
3557
+ }
3558
+
3559
+ return null;
3560
+
3561
+ }
3562
+
3563
+ // Handle color conversion nodes - follow the input connection
3564
+ if ( infoId && infoId.startsWith( 'ND_convert_' ) ) {
3565
+
3566
+ const inPath = shaderPath + '.inputs:in';
3567
+ const inSpec = this.specsByPath[ inPath ];
3568
+
3569
+ if ( inSpec?.fields?.connectionPaths?.length > 0 ) {
3570
+
3571
+ return this._getTextureFromOpenPBRConnection( inSpec.fields.connectionPaths[ 0 ] );
3572
+
3573
+ }
3574
+
3575
+ return null;
3576
+
3577
+ }
3578
+
3579
+ // Handle Arnold bump2d - follow the bump_map input
3580
+ if ( infoId === 'arnold:bump2d' ) {
3581
+
3582
+ const bumpMapPath = shaderPath + '.inputs:bump_map';
3583
+ const bumpMapSpec = this.specsByPath[ bumpMapPath ];
3584
+
3585
+ if ( bumpMapSpec?.fields?.connectionPaths?.length > 0 ) {
3586
+
3587
+ return this._getTextureFromOpenPBRConnection( bumpMapSpec.fields.connectionPaths[ 0 ] );
3588
+
3589
+ }
3590
+
3591
+ return null;
3592
+
3593
+ }
3594
+
3595
+ // Handle Arnold color_correct - follow the input connection
3596
+ if ( infoId === 'arnold:color_correct' ) {
3597
+
3598
+ const inputPath = shaderPath + '.inputs:input';
3599
+ const inputSpec = this.specsByPath[ inputPath ];
3600
+
3601
+ if ( inputSpec?.fields?.connectionPaths?.length > 0 ) {
3602
+
3603
+ return this._getTextureFromOpenPBRConnection( inputSpec.fields.connectionPaths[ 0 ] );
3604
+
3605
+ }
3606
+
3607
+ return null;
3608
+
3609
+ }
3610
+
3611
+ // Handle nested shader paths (e.g., /Material/file2/cc.outputs:a)
3612
+ // Check if parent path is an image node
3613
+ const parentPath = shaderPath.substring( 0, shaderPath.lastIndexOf( '/' ) );
3614
+
3615
+ if ( parentPath ) {
3616
+
3617
+ const parentSpec = this.specsByPath[ parentPath ];
3618
+
3619
+ if ( parentSpec ) {
3620
+
3621
+ const parentAttrs = this._getAttributes( parentPath );
3622
+ const parentInfoId = parentAttrs[ 'info:id' ] || parentSpec.fields[ 'info:id' ];
3623
+
3624
+ if ( parentInfoId === 'arnold:image' ) {
3625
+
3626
+ const filePath = parentAttrs[ 'inputs:filename' ];
3627
+ if ( filePath ) return this._loadTextureFromPath( filePath );
3628
+
3629
+ }
3630
+
3631
+ }
3632
+
3633
+ }
3634
+
3635
+ return null;
3636
+
3637
+ }
3638
+
3639
+ _loadTextureFromPath( filePath ) {
3640
+
3641
+ if ( ! filePath ) return null;
3642
+
3643
+ // Check cache first
3644
+ if ( this.textureCache[ filePath ] ) {
3645
+
3646
+ return this.textureCache[ filePath ];
3647
+
3648
+ }
3649
+
3650
+ const texture = this._loadTexture( filePath, null, null );
3651
+
3652
+ if ( texture ) {
3653
+
3654
+ this.textureCache[ filePath ] = texture;
3655
+
3656
+ }
3657
+
3658
+ return texture;
3659
+
3660
+ }
3661
+
3662
+ _getTextureFromConnection( connPath ) {
3663
+
3664
+ // connPath is like /Material/Shader.outputs:rgb
3665
+ const shaderPath = connPath.split( '.' )[ 0 ];
3666
+ const shaderSpec = this.specsByPath[ shaderPath ];
3667
+
3668
+ if ( ! shaderSpec ) return null;
3669
+
3670
+ const attrs = this._getAttributes( shaderPath );
3671
+ const infoId = attrs[ 'info:id' ] || shaderSpec.fields[ 'info:id' ];
3672
+
3673
+ if ( infoId !== 'UsdUVTexture' ) return null;
3674
+
3675
+ const filePath = attrs[ 'inputs:file' ];
3676
+ if ( ! filePath ) return null;
3677
+
3678
+ // Check for UsdTransform2d connection via inputs:st and trace to PrimvarReader
3679
+ let transformAttrs = null;
3680
+ let uvChannel = 0; // Default to first UV set
3681
+ const stAttrPath = shaderPath + '.inputs:st';
3682
+ const stAttrSpec = this.specsByPath[ stAttrPath ];
3683
+
3684
+ if ( stAttrSpec?.fields?.connectionPaths?.length > 0 ) {
3685
+
3686
+ const stConnPath = stAttrSpec.fields.connectionPaths[ 0 ];
3687
+ const stPath = stConnPath.replace( /<|>/g, '' ).split( '.' )[ 0 ];
3688
+ const stSpec = this.specsByPath[ stPath ];
3689
+
3690
+ if ( stSpec ) {
3691
+
3692
+ const stAttrs = this._getAttributes( stPath );
3693
+ const stInfoId = stAttrs[ 'info:id' ] || stSpec.fields[ 'info:id' ];
3694
+
3695
+ if ( stInfoId === 'UsdTransform2d' ) {
3696
+
3697
+ transformAttrs = stAttrs;
3698
+
3699
+ // Trace to PrimvarReader to find UV set
3700
+ const inAttrPath = stPath + '.inputs:in';
3701
+ const inAttrSpec = this.specsByPath[ inAttrPath ];
3702
+
3703
+ if ( inAttrSpec?.fields?.connectionPaths?.length > 0 ) {
3704
+
3705
+ const inConnPath = inAttrSpec.fields.connectionPaths[ 0 ];
3706
+ const primvarPath = inConnPath.replace( /<|>/g, '' ).split( '.' )[ 0 ];
3707
+ const primvarAttrs = this._getAttributes( primvarPath );
3708
+
3709
+ // Check varname to determine UV channel
3710
+ const varname = primvarAttrs[ 'inputs:varname' ];
3711
+ if ( varname === 'st1' ) uvChannel = 1;
3712
+ else if ( varname === 'st2' ) uvChannel = 2;
3713
+
3714
+ }
3715
+
3716
+ } else if ( stInfoId === 'UsdPrimvarReader_float2' ) {
3717
+
3718
+ // Direct connection to PrimvarReader
3719
+ const varname = stAttrs[ 'inputs:varname' ];
3720
+ if ( varname === 'st1' ) uvChannel = 1;
3721
+ else if ( varname === 'st2' ) uvChannel = 2;
3722
+
3723
+ }
3724
+
3725
+ }
3726
+
3727
+ }
3728
+
3729
+ // Extract scale and bias for texture value modification
3730
+ const scale = attrs[ 'inputs:scale' ];
3731
+ const bias = attrs[ 'inputs:bias' ];
3732
+
3733
+ // Create cache key that includes scale/bias if present
3734
+ let cacheKey = filePath;
3735
+ if ( scale ) cacheKey += ':s' + scale.join( ',' );
3736
+ if ( bias ) cacheKey += ':b' + bias.join( ',' );
3737
+
3738
+ if ( this.textureCache[ cacheKey ] ) {
3739
+
3740
+ return this.textureCache[ cacheKey ];
3741
+
3742
+ }
3743
+
3744
+ const texture = this._loadTexture( filePath, attrs, transformAttrs );
3745
+
3746
+ if ( texture ) {
3747
+
3748
+ // Store scale/bias and UV channel in userData
3749
+ if ( scale ) texture.userData.scale = scale;
3750
+ if ( bias ) texture.userData.bias = bias;
3751
+ if ( uvChannel !== 0 ) texture.channel = uvChannel;
3752
+
3753
+ this.textureCache[ cacheKey ] = texture;
3754
+
3755
+ }
3756
+
3757
+ return texture;
3758
+
3759
+ }
3760
+
3761
+ _applyTextureTransforms( texture, attrs ) {
3762
+
3763
+ if ( ! attrs ) return;
3764
+
3765
+ const scale = attrs[ 'inputs:scale' ];
3766
+ if ( scale && Array.isArray( scale ) && scale.length >= 2 ) {
3767
+
3768
+ texture.repeat.set( scale[ 0 ], scale[ 1 ] );
3769
+
3770
+ }
3771
+
3772
+ const translation = attrs[ 'inputs:translation' ];
3773
+ if ( translation && Array.isArray( translation ) && translation.length >= 2 ) {
3774
+
3775
+ texture.offset.set( translation[ 0 ], translation[ 1 ] );
3776
+
3777
+ }
3778
+
3779
+ const rotation = attrs[ 'inputs:rotation' ];
3780
+ if ( typeof rotation === 'number' ) {
3781
+
3782
+ texture.rotation = rotation * Math.PI / 180;
3783
+
3784
+ }
3785
+
3786
+ }
3787
+
3788
+ _loadTexture( filePath, textureAttrs, transformAttrs ) {
3789
+
3790
+ let cleanPath = filePath;
3791
+ if ( cleanPath.startsWith( '@' ) ) cleanPath = cleanPath.slice( 1 );
3792
+ if ( cleanPath.endsWith( '@' ) ) cleanPath = cleanPath.slice( 0, - 1 );
3793
+
3794
+ // Resolve relative to basePath first
3795
+ const resolvedPath = this._resolveFilePath( cleanPath );
3796
+ let assetData = this.assets[ resolvedPath ];
3797
+
3798
+ // Fallback to unresolved path
3799
+ if ( ! assetData ) {
3800
+
3801
+ assetData = this.assets[ cleanPath ];
3802
+
3803
+ }
3804
+
3805
+ // Last resort: search by basename
3806
+ if ( ! assetData ) {
3807
+
3808
+ const baseName = cleanPath.split( '/' ).pop();
3809
+
3810
+ for ( const key in this.assets ) {
3811
+
3812
+ if ( key.endsWith( baseName ) || key.endsWith( '/' + baseName ) ) {
3813
+
3814
+ return this._createTextureFromData( this.assets[ key ], textureAttrs, transformAttrs );
3815
+
3816
+ }
3817
+
3818
+ }
3819
+
3820
+ // Try loading via LoadingManager if available
3821
+ if ( this.manager ) {
3822
+
3823
+ const url = this.manager.resolveURL( baseName );
3824
+ if ( url !== baseName ) {
3825
+
3826
+ // URL modifier found a match - load it
3827
+ return this._createTextureFromData( url, textureAttrs, transformAttrs );
3828
+
3829
+ }
3830
+
3831
+ }
3832
+
3833
+ console.warn( 'USDLoader: Texture not found:', cleanPath );
3834
+ return null;
3835
+
3836
+ }
3837
+
3838
+ return this._createTextureFromData( assetData, textureAttrs, transformAttrs );
3839
+
3840
+ }
3841
+
3842
+ _createTextureFromData( data, textureAttrs, transformAttrs ) {
3843
+
3844
+ if ( ! data ) return null;
3845
+
3846
+ const scope = this;
3847
+ const texture = new Texture();
3848
+
3849
+ let url;
3850
+
3851
+ if ( typeof data === 'string' ) {
3852
+
3853
+ url = data;
3854
+
3855
+ } else if ( data instanceof Uint8Array || data instanceof ArrayBuffer ) {
3856
+
3857
+ const blob = new Blob( [ data ] );
3858
+ url = URL.createObjectURL( blob );
3859
+
3860
+ } else {
3861
+
3862
+ return null;
3863
+
3864
+ }
3865
+
3866
+ const image = new Image();
3867
+ image.onload = function () {
3868
+
3869
+ texture.image = image;
3870
+
3871
+ if ( textureAttrs ) {
3872
+
3873
+ texture.wrapS = scope._getWrapMode( textureAttrs[ 'inputs:wrapS' ] );
3874
+ texture.wrapT = scope._getWrapMode( textureAttrs[ 'inputs:wrapT' ] );
3875
+
3876
+ }
3877
+
3878
+ scope._applyTextureTransforms( texture, transformAttrs );
3879
+ texture.needsUpdate = true;
3880
+
3881
+ if ( typeof data !== 'string' ) {
3882
+
3883
+ URL.revokeObjectURL( url );
3884
+
3885
+ }
3886
+
3887
+ };
3888
+
3889
+ image.src = url;
3890
+
3891
+ return texture;
3892
+
3893
+ }
3894
+
3895
+ _getWrapMode( wrapValue ) {
3896
+
3897
+ if ( wrapValue === 'repeat' ) return RepeatWrapping;
3898
+ if ( wrapValue === 'mirror' ) return MirroredRepeatWrapping;
3899
+ if ( wrapValue === 'clamp' ) return ClampToEdgeWrapping;
3900
+ return RepeatWrapping;
3901
+
3902
+ }
3903
+
3904
+ // ========================================================================
3905
+ // Skeletal Animation
3906
+ // ========================================================================
3907
+
3908
+ _buildSkeleton( path ) {
3909
+
3910
+ const attrs = this._getAttributes( path );
3911
+
3912
+ // Get joint names (paths like "root", "root/body_joint", etc.)
3913
+ const joints = attrs[ 'joints' ];
3914
+ if ( ! joints || joints.length === 0 ) return null;
3915
+
3916
+ // Get bind transforms (world-space bind pose matrices)
3917
+ // These can be nested arrays (USDA) or flat arrays (USDC)
3918
+ const rawBindTransforms = attrs[ 'bindTransforms' ];
3919
+ const rawRestTransforms = attrs[ 'restTransforms' ];
3920
+
3921
+ const bindTransforms = this._flattenMatrixArray( rawBindTransforms, joints.length );
3922
+ const restTransforms = this._flattenMatrixArray( rawRestTransforms, joints.length );
3923
+
3924
+ // Build bones
3925
+ const bones = [];
3926
+ const bonesByPath = {};
3927
+ const boneInverses = [];
3928
+
3929
+ for ( let i = 0; i < joints.length; i ++ ) {
3930
+
3931
+ const jointPath = joints[ i ];
3932
+ const jointName = jointPath.split( '/' ).pop();
3933
+
3934
+ const bone = new Bone();
3935
+ bone.name = jointName;
3936
+ bones.push( bone );
3937
+ bonesByPath[ jointPath ] = { bone, index: i };
3938
+
3939
+ // Compute inverse bind matrix
3940
+ if ( bindTransforms && bindTransforms.length >= ( i + 1 ) * 16 ) {
3941
+
3942
+ const bindMatrix = new Matrix4();
3943
+ // USD matrices are row-major, Three.js is column-major - need to transpose
3944
+ const m = bindTransforms.slice( i * 16, ( i + 1 ) * 16 );
3945
+ bindMatrix.set(
3946
+ m[ 0 ], m[ 4 ], m[ 8 ], m[ 12 ],
3947
+ m[ 1 ], m[ 5 ], m[ 9 ], m[ 13 ],
3948
+ m[ 2 ], m[ 6 ], m[ 10 ], m[ 14 ],
3949
+ m[ 3 ], m[ 7 ], m[ 11 ], m[ 15 ]
3950
+ );
3951
+ const inverseBindMatrix = bindMatrix.clone().invert();
3952
+ boneInverses.push( inverseBindMatrix );
3953
+
3954
+ } else {
3955
+
3956
+ boneInverses.push( new Matrix4() );
3957
+
3958
+ }
3959
+
3960
+ }
3961
+
3962
+ // Build parent-child relationships based on joint paths
3963
+ for ( let i = 0; i < joints.length; i ++ ) {
3964
+
3965
+ const jointPath = joints[ i ];
3966
+ const parts = jointPath.split( '/' );
3967
+
3968
+ if ( parts.length > 1 ) {
3969
+
3970
+ const parentPath = parts.slice( 0, - 1 ).join( '/' );
3971
+ const parentData = bonesByPath[ parentPath ];
3972
+
3973
+ if ( parentData ) {
3974
+
3975
+ parentData.bone.add( bones[ i ] );
3976
+
3977
+ }
3978
+
3979
+ }
3980
+
3981
+ }
3982
+
3983
+ // Apply rest transforms as bone local transforms.
3984
+ // Rest transforms are the skeleton's default local-space pose and match
3985
+ // the reference frame used by SkelAnimation data. Bind transforms are
3986
+ // world-space matrices used only for computing inverse bind matrices.
3987
+ if ( restTransforms && restTransforms.length >= joints.length * 16 ) {
3988
+
3989
+ for ( let i = 0; i < joints.length; i ++ ) {
3990
+
3991
+ const matrix = new Matrix4();
3992
+ const m = restTransforms.slice( i * 16, ( i + 1 ) * 16 );
3993
+ matrix.set(
3994
+ m[ 0 ], m[ 4 ], m[ 8 ], m[ 12 ],
3995
+ m[ 1 ], m[ 5 ], m[ 9 ], m[ 13 ],
3996
+ m[ 2 ], m[ 6 ], m[ 10 ], m[ 14 ],
3997
+ m[ 3 ], m[ 7 ], m[ 11 ], m[ 15 ]
3998
+ );
3999
+ matrix.decompose( bones[ i ].position, bones[ i ].quaternion, bones[ i ].scale );
4000
+
4001
+ }
4002
+
4003
+ }
4004
+
4005
+ // Find root bone(s) - bones without a parent bone
4006
+ const rootBones = bones.filter( bone => ! bone.parent || ! bone.parent.isBone );
4007
+
4008
+ // Get animation source path
4009
+ const animSourceSpec = this.specsByPath[ path + '.skel:animationSource' ];
4010
+ let animationPath = null;
4011
+ if ( animSourceSpec && animSourceSpec.fields.targetPaths && animSourceSpec.fields.targetPaths.length > 0 ) {
4012
+
4013
+ animationPath = animSourceSpec.fields.targetPaths[ 0 ];
4014
+
4015
+ }
4016
+
4017
+ return {
4018
+ skeleton: new Skeleton( bones, boneInverses ),
4019
+ joints: joints,
4020
+ rootBones: rootBones,
4021
+ animationPath: animationPath,
4022
+ path: path
4023
+ };
4024
+
4025
+ }
4026
+
4027
+ _bindSkeletons() {
4028
+
4029
+ for ( const meshData of this.skinnedMeshes ) {
4030
+
4031
+ const { mesh, skeletonPath, localJoints, geomBindTransform } = meshData;
4032
+
4033
+ let skeletonData = null;
4034
+
4035
+ // Try exact match first
4036
+ if ( skeletonPath && this.skeletons[ skeletonPath ] ) {
4037
+
4038
+ skeletonData = this.skeletons[ skeletonPath ];
4039
+
4040
+ }
4041
+
4042
+ // Try includes match as fallback
4043
+ if ( ! skeletonData ) {
4044
+
4045
+ for ( const skelPath in this.skeletons ) {
4046
+
4047
+ if ( skeletonPath && ( skeletonPath.includes( skelPath ) || skelPath.includes( skeletonPath ) ) ) {
4048
+
4049
+ skeletonData = this.skeletons[ skelPath ];
4050
+ break;
4051
+
4052
+ }
4053
+
4054
+ }
4055
+
4056
+ }
4057
+
4058
+ // Fallback to first skeleton for single-skeleton files
4059
+ if ( ! skeletonData ) {
4060
+
4061
+ const skeletonPaths = Object.keys( this.skeletons );
4062
+ if ( skeletonPaths.length > 0 ) {
4063
+
4064
+ skeletonData = this.skeletons[ skeletonPaths[ 0 ] ];
4065
+
4066
+ }
4067
+
4068
+ }
4069
+
4070
+ if ( ! skeletonData ) {
4071
+
4072
+ console.warn( 'USDComposer: No skeleton found for skinned mesh', mesh.name );
4073
+ continue;
4074
+
4075
+ }
4076
+
4077
+ const { skeleton, rootBones, joints } = skeletonData;
4078
+
4079
+ if ( localJoints && localJoints.length > 0 ) {
4080
+
4081
+ const skinIndex = mesh.geometry.attributes.skinIndex;
4082
+ if ( skinIndex ) {
4083
+
4084
+ const localToGlobal = [];
4085
+ for ( let i = 0; i < localJoints.length; i ++ ) {
4086
+
4087
+ const jointName = localJoints[ i ];
4088
+ const globalIdx = joints.indexOf( jointName );
4089
+ localToGlobal[ i ] = globalIdx >= 0 ? globalIdx : 0;
4090
+
4091
+ }
4092
+
4093
+ const arr = skinIndex.array;
4094
+ for ( let i = 0; i < arr.length; i ++ ) {
4095
+
4096
+ const localIdx = arr[ i ];
4097
+ if ( localIdx < localToGlobal.length ) {
4098
+
4099
+ arr[ i ] = localToGlobal[ localIdx ];
4100
+
4101
+ }
4102
+
4103
+ }
4104
+
4105
+ }
4106
+
4107
+ }
4108
+
4109
+ for ( const rootBone of rootBones ) {
4110
+
4111
+ mesh.add( rootBone );
4112
+
4113
+ }
4114
+
4115
+ // Use geomBindTransform if available, otherwise fall back to identity.
4116
+ // Estimating bind transforms from vertex/joint samples is not robust and can
4117
+ // produce severe skinning distortion for valid assets.
4118
+ const bindMatrix = new Matrix4();
4119
+
4120
+ if ( geomBindTransform && geomBindTransform.length === 16 ) {
4121
+
4122
+ // USD matrices are row-major, Three.js is column-major - need to transpose
4123
+ const m = geomBindTransform;
4124
+ bindMatrix.set(
4125
+ m[ 0 ], m[ 4 ], m[ 8 ], m[ 12 ],
4126
+ m[ 1 ], m[ 5 ], m[ 9 ], m[ 13 ],
4127
+ m[ 2 ], m[ 6 ], m[ 10 ], m[ 14 ],
4128
+ m[ 3 ], m[ 7 ], m[ 11 ], m[ 15 ]
4129
+ );
4130
+
4131
+ }
4132
+
4133
+ mesh.bind( skeleton, bindMatrix );
4134
+
4135
+ }
4136
+
4137
+ }
4138
+
4139
+ _buildAnimations() {
4140
+
4141
+ const animations = [];
4142
+
4143
+ // Find all SkelAnimation prims
4144
+ for ( const path in this.specsByPath ) {
4145
+
4146
+ const spec = this.specsByPath[ path ];
4147
+ if ( spec.specType !== SpecType.Prim ) continue;
4148
+ if ( spec.fields.typeName !== 'SkelAnimation' ) continue;
4149
+
4150
+ const clip = this._buildAnimationClip( path );
4151
+ if ( clip ) {
4152
+
4153
+ animations.push( clip );
4154
+
4155
+ }
4156
+
4157
+ }
4158
+
4159
+ // Build transform animations from time-sampled xformOps
4160
+ const transformTracks = this._buildTransformAnimations();
4161
+ if ( transformTracks.length > 0 ) {
4162
+
4163
+ animations.push( new AnimationClip( 'TransformAnimation', - 1, transformTracks ) );
4164
+
4165
+ }
4166
+
4167
+ return animations;
4168
+
4169
+ }
4170
+
4171
+ _buildTransformAnimations() {
4172
+
4173
+ const tracks = [];
4174
+
4175
+ for ( const path in this.specsByPath ) {
4176
+
4177
+ const spec = this.specsByPath[ path ];
4178
+ if ( spec.specType !== SpecType.Prim ) continue;
4179
+
4180
+ const typeName = spec.fields?.typeName;
4181
+ if ( typeName !== 'Xform' && typeName !== 'Scope' && typeName !== 'Mesh' ) continue;
4182
+
4183
+ const objectName = path.split( '/' ).pop();
4184
+
4185
+ // Check for animated xformOp:orient
4186
+ const orientPath = path + '.xformOp:orient';
4187
+ const orientSpec = this.specsByPath[ orientPath ];
4188
+ if ( orientSpec?.fields?.timeSamples ) {
4189
+
4190
+ const { times, values } = orientSpec.fields.timeSamples;
4191
+ const keyframeTimes = [];
4192
+ const keyframeValues = [];
4193
+
4194
+ for ( let i = 0; i < times.length; i ++ ) {
4195
+
4196
+ keyframeTimes.push( times[ i ] / this.fps );
4197
+
4198
+ const q = values[ i ];
4199
+ keyframeValues.push( q[ 0 ], q[ 1 ], q[ 2 ], q[ 3 ] );
4200
+
4201
+ }
4202
+
4203
+ if ( keyframeTimes.length > 0 ) {
4204
+
4205
+ tracks.push( new QuaternionKeyframeTrack(
4206
+ objectName + '.quaternion',
4207
+ new Float32Array( keyframeTimes ),
4208
+ new Float32Array( keyframeValues )
4209
+ ) );
4210
+
4211
+ }
4212
+
4213
+ }
4214
+
4215
+ // Check for animated xformOp:rotateXYZ
4216
+ const rotateXYZPath = path + '.xformOp:rotateXYZ';
4217
+ const rotateXYZSpec = this.specsByPath[ rotateXYZPath ];
4218
+ if ( rotateXYZSpec?.fields?.timeSamples ) {
4219
+
4220
+ const { times, values } = rotateXYZSpec.fields.timeSamples;
4221
+ const keyframeTimes = [];
4222
+ const keyframeValues = [];
4223
+ const tempEuler = new Euler();
4224
+ const tempQuat = new Quaternion();
4225
+
4226
+ for ( let i = 0; i < times.length; i ++ ) {
4227
+
4228
+ keyframeTimes.push( times[ i ] / this.fps );
4229
+
4230
+ const r = values[ i ];
4231
+ // USD rotateXYZ: matrix = Rx * Ry * Rz, use 'ZYX' order in Three.js
4232
+ tempEuler.set(
4233
+ r[ 0 ] * Math.PI / 180,
4234
+ r[ 1 ] * Math.PI / 180,
4235
+ r[ 2 ] * Math.PI / 180,
4236
+ 'ZYX'
4237
+ );
4238
+ tempQuat.setFromEuler( tempEuler );
4239
+ keyframeValues.push( tempQuat.x, tempQuat.y, tempQuat.z, tempQuat.w );
4240
+
4241
+ }
4242
+
4243
+ if ( keyframeTimes.length > 0 ) {
4244
+
4245
+ tracks.push( new QuaternionKeyframeTrack(
4246
+ objectName + '.quaternion',
4247
+ new Float32Array( keyframeTimes ),
4248
+ new Float32Array( keyframeValues )
4249
+ ) );
4250
+
4251
+ }
4252
+
4253
+ }
4254
+
4255
+ // Check for animated xformOp:translate
4256
+ const translatePath = path + '.xformOp:translate';
4257
+ const translateSpec = this.specsByPath[ translatePath ];
4258
+ if ( translateSpec?.fields?.timeSamples ) {
4259
+
4260
+ const { times, values } = translateSpec.fields.timeSamples;
4261
+ const keyframeTimes = [];
4262
+ const keyframeValues = [];
4263
+
4264
+ for ( let i = 0; i < times.length; i ++ ) {
4265
+
4266
+ keyframeTimes.push( times[ i ] / this.fps );
4267
+
4268
+ const t = values[ i ];
4269
+ keyframeValues.push( t[ 0 ], t[ 1 ], t[ 2 ] );
4270
+
4271
+ }
4272
+
4273
+ if ( keyframeTimes.length > 0 ) {
4274
+
4275
+ tracks.push( new VectorKeyframeTrack(
4276
+ objectName + '.position',
4277
+ new Float32Array( keyframeTimes ),
4278
+ new Float32Array( keyframeValues )
4279
+ ) );
4280
+
4281
+ }
4282
+
4283
+ }
4284
+
4285
+ // Check for animated xformOp:scale
4286
+ const scalePath = path + '.xformOp:scale';
4287
+ const scaleSpec = this.specsByPath[ scalePath ];
4288
+ if ( scaleSpec?.fields?.timeSamples ) {
4289
+
4290
+ const { times, values } = scaleSpec.fields.timeSamples;
4291
+ const keyframeTimes = [];
4292
+ const keyframeValues = [];
4293
+
4294
+ for ( let i = 0; i < times.length; i ++ ) {
4295
+
4296
+ keyframeTimes.push( times[ i ] / this.fps );
4297
+
4298
+ const s = values[ i ];
4299
+ keyframeValues.push( s[ 0 ], s[ 1 ], s[ 2 ] );
4300
+
4301
+ }
4302
+
4303
+ if ( keyframeTimes.length > 0 ) {
4304
+
4305
+ tracks.push( new VectorKeyframeTrack(
4306
+ objectName + '.scale',
4307
+ new Float32Array( keyframeTimes ),
4308
+ new Float32Array( keyframeValues )
4309
+ ) );
4310
+
4311
+ }
4312
+
4313
+ }
4314
+
4315
+ // Check for animated xformOp:transform (matrix animations)
4316
+ // These can have suffixes like xformOp:transform:transform
4317
+ const properties = spec.fields?.properties || [];
4318
+ for ( const prop of properties ) {
4319
+
4320
+ if ( ! prop.startsWith( 'xformOp:transform' ) ) continue;
4321
+
4322
+ const transformPath = path + '.' + prop;
4323
+ const transformSpec = this.specsByPath[ transformPath ];
4324
+
4325
+ if ( ! transformSpec?.fields?.timeSamples ) continue;
4326
+
4327
+ const { times, values } = transformSpec.fields.timeSamples;
4328
+ const positionTimes = [];
4329
+ const positionValues = [];
4330
+ const quaternionTimes = [];
4331
+ const quaternionValues = [];
4332
+ const scaleTimes = [];
4333
+ const scaleValues = [];
4334
+
4335
+ const matrix = new Matrix4();
4336
+ const position = new Vector3();
4337
+ const quaternion = new Quaternion();
4338
+ const scale = new Vector3();
4339
+
4340
+ for ( let i = 0; i < times.length; i ++ ) {
4341
+
4342
+ const m = values[ i ];
4343
+ if ( ! m || m.length < 16 ) continue;
4344
+
4345
+ const t = times[ i ] / this.fps;
4346
+
4347
+ // USD matrices are row-major, Three.js is column-major
4348
+ matrix.set(
4349
+ m[ 0 ], m[ 4 ], m[ 8 ], m[ 12 ],
4350
+ m[ 1 ], m[ 5 ], m[ 9 ], m[ 13 ],
4351
+ m[ 2 ], m[ 6 ], m[ 10 ], m[ 14 ],
4352
+ m[ 3 ], m[ 7 ], m[ 11 ], m[ 15 ]
4353
+ );
4354
+
4355
+ matrix.decompose( position, quaternion, scale );
4356
+
4357
+ positionTimes.push( t );
4358
+ positionValues.push( position.x, position.y, position.z );
4359
+
4360
+ quaternionTimes.push( t );
4361
+ quaternionValues.push( quaternion.x, quaternion.y, quaternion.z, quaternion.w );
4362
+
4363
+ scaleTimes.push( t );
4364
+ scaleValues.push( scale.x, scale.y, scale.z );
4365
+
4366
+ }
4367
+
4368
+ if ( positionTimes.length > 0 ) {
4369
+
4370
+ tracks.push( new VectorKeyframeTrack(
4371
+ objectName + '.position',
4372
+ new Float32Array( positionTimes ),
4373
+ new Float32Array( positionValues )
4374
+ ) );
4375
+
4376
+ tracks.push( new QuaternionKeyframeTrack(
4377
+ objectName + '.quaternion',
4378
+ new Float32Array( quaternionTimes ),
4379
+ new Float32Array( quaternionValues )
4380
+ ) );
4381
+
4382
+ tracks.push( new VectorKeyframeTrack(
4383
+ objectName + '.scale',
4384
+ new Float32Array( scaleTimes ),
4385
+ new Float32Array( scaleValues )
4386
+ ) );
4387
+
4388
+ }
4389
+
4390
+ break; // Only process first transform op
4391
+
4392
+ }
4393
+
4394
+ }
4395
+
4396
+ return tracks;
4397
+
4398
+ }
4399
+
4400
+ _buildAnimationClip( path ) {
4401
+
4402
+ const attrs = this._getAttributes( path );
4403
+ const joints = attrs[ 'joints' ];
4404
+
4405
+ if ( ! joints || joints.length === 0 ) return null;
4406
+
4407
+ const tracks = [];
4408
+
4409
+ // Get rotation time samples
4410
+ const rotationsAttr = this._getTimeSampledAttribute( path, 'rotations' );
4411
+ if ( rotationsAttr && rotationsAttr.times && rotationsAttr.values ) {
4412
+
4413
+ const { times, values } = rotationsAttr;
4414
+
4415
+ for ( let jointIdx = 0; jointIdx < joints.length; jointIdx ++ ) {
4416
+
4417
+ const jointName = joints[ jointIdx ].split( '/' ).pop();
4418
+ const keyframeTimes = [];
4419
+ const keyframeValues = [];
4420
+
4421
+ for ( let t = 0; t < times.length; t ++ ) {
4422
+
4423
+ const quatData = values[ t ];
4424
+ if ( ! quatData || quatData.length < ( jointIdx + 1 ) * 4 ) continue;
4425
+
4426
+ keyframeTimes.push( times[ t ] / this.fps );
4427
+
4428
+ // USD GfQuatf stores imaginary (x,y,z) first, then real (w)
4429
+ // This matches Three.js quaternion order (x,y,z,w)
4430
+ const x = quatData[ jointIdx * 4 + 0 ];
4431
+ const y = quatData[ jointIdx * 4 + 1 ];
4432
+ const z = quatData[ jointIdx * 4 + 2 ];
4433
+ const w = quatData[ jointIdx * 4 + 3 ];
4434
+ keyframeValues.push( x, y, z, w );
4435
+
4436
+ }
4437
+
4438
+ if ( keyframeTimes.length > 0 ) {
4439
+
4440
+ tracks.push( new QuaternionKeyframeTrack(
4441
+ jointName + '.quaternion',
4442
+ new Float32Array( keyframeTimes ),
4443
+ new Float32Array( keyframeValues )
4444
+ ) );
4445
+
4446
+ }
4447
+
4448
+ }
4449
+
4450
+ }
4451
+
4452
+ // Get translation time samples
4453
+ const translationsAttr = this._getTimeSampledAttribute( path, 'translations' );
4454
+ if ( translationsAttr && translationsAttr.times && translationsAttr.values ) {
4455
+
4456
+ const { times, values } = translationsAttr;
4457
+
4458
+ for ( let jointIdx = 0; jointIdx < joints.length; jointIdx ++ ) {
4459
+
4460
+ const jointName = joints[ jointIdx ].split( '/' ).pop();
4461
+ const keyframeTimes = [];
4462
+ const keyframeValues = [];
4463
+
4464
+ for ( let t = 0; t < times.length; t ++ ) {
4465
+
4466
+ const transData = values[ t ];
4467
+ if ( ! transData || transData.length < ( jointIdx + 1 ) * 3 ) continue;
4468
+
4469
+ keyframeTimes.push( times[ t ] / this.fps );
4470
+ keyframeValues.push(
4471
+ transData[ jointIdx * 3 + 0 ],
4472
+ transData[ jointIdx * 3 + 1 ],
4473
+ transData[ jointIdx * 3 + 2 ]
4474
+ );
4475
+
4476
+ }
4477
+
4478
+ if ( keyframeTimes.length > 0 ) {
4479
+
4480
+ tracks.push( new VectorKeyframeTrack(
4481
+ jointName + '.position',
4482
+ new Float32Array( keyframeTimes ),
4483
+ new Float32Array( keyframeValues )
4484
+ ) );
4485
+
4486
+ }
4487
+
4488
+ }
4489
+
4490
+ }
4491
+
4492
+ // Get scale time samples
4493
+ const scalesAttr = this._getTimeSampledAttribute( path, 'scales' );
4494
+ if ( scalesAttr && scalesAttr.times && scalesAttr.values ) {
4495
+
4496
+ const { times, values } = scalesAttr;
4497
+
4498
+ for ( let jointIdx = 0; jointIdx < joints.length; jointIdx ++ ) {
4499
+
4500
+ const jointName = joints[ jointIdx ].split( '/' ).pop();
4501
+ const keyframeTimes = [];
4502
+ const keyframeValues = [];
4503
+
4504
+ for ( let t = 0; t < times.length; t ++ ) {
4505
+
4506
+ const scaleData = values[ t ];
4507
+ if ( ! scaleData || scaleData.length < ( jointIdx + 1 ) * 3 ) continue;
4508
+
4509
+ keyframeTimes.push( times[ t ] / this.fps );
4510
+ keyframeValues.push(
4511
+ scaleData[ jointIdx * 3 + 0 ],
4512
+ scaleData[ jointIdx * 3 + 1 ],
4513
+ scaleData[ jointIdx * 3 + 2 ]
4514
+ );
4515
+
4516
+ }
4517
+
4518
+ if ( keyframeTimes.length > 0 ) {
4519
+
4520
+ tracks.push( new VectorKeyframeTrack(
4521
+ jointName + '.scale',
4522
+ new Float32Array( keyframeTimes ),
4523
+ new Float32Array( keyframeValues )
4524
+ ) );
4525
+
4526
+ }
4527
+
4528
+ }
4529
+
4530
+ }
4531
+
4532
+ if ( tracks.length === 0 ) return null;
4533
+
4534
+ const clipName = path.split( '/' ).pop();
4535
+ return new AnimationClip( clipName, - 1, tracks );
4536
+
4537
+ }
4538
+
4539
+ _getTimeSampledAttribute( primPath, attrName ) {
4540
+
4541
+ // Look for the attribute spec with time samples
4542
+ const attrPath = primPath + '.' + attrName;
4543
+ const attrSpec = this.specsByPath[ attrPath ];
4544
+
4545
+ if ( attrSpec && attrSpec.fields.timeSamples ) {
4546
+
4547
+ const timeSamples = attrSpec.fields.timeSamples;
4548
+ if ( timeSamples.times && timeSamples.values ) {
4549
+
4550
+ return timeSamples;
4551
+
4552
+ }
4553
+
4554
+ }
4555
+
4556
+ return null;
4557
+
4558
+ }
4559
+
4560
+ _flattenMatrixArray( matrices, numMatrices ) {
4561
+
4562
+ if ( ! matrices || matrices.length === 0 ) return null;
4563
+
4564
+ if ( typeof matrices[ 0 ] === 'number' ) return matrices;
4565
+
4566
+ const flatArray = [];
4567
+
4568
+ for ( let m = 0; m < numMatrices; m ++ ) {
4569
+
4570
+ for ( let row = 0; row < 4; row ++ ) {
4571
+
4572
+ const rowData = matrices[ m * 4 + row ];
4573
+
4574
+ if ( rowData && rowData.length === 4 ) {
4575
+
4576
+ flatArray.push( rowData[ 0 ], rowData[ 1 ], rowData[ 2 ], rowData[ 3 ] );
4577
+
4578
+ } else {
4579
+
4580
+ flatArray.push( row === 0 ? 1 : 0, row === 1 ? 1 : 0, row === 2 ? 1 : 0, row === 3 ? 1 : 0 );
4581
+
4582
+ }
4583
+
4584
+ }
4585
+
4586
+ }
4587
+
4588
+ return flatArray;
4589
+
4590
+ }
4591
+
4592
+ }
4593
+
4594
+ export { USDComposer, SpecType };