@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.
- package/LICENSE +21 -0
- package/README.md +85 -0
- package/build/three.cjs +79695 -0
- package/build/three.core.js +59732 -0
- package/build/three.core.min.js +6 -0
- package/build/three.module.js +19552 -0
- package/build/three.module.min.js +6 -0
- package/build/three.tsl.js +654 -0
- package/build/three.tsl.min.js +6 -0
- package/build/three.webgpu.js +83969 -0
- package/build/three.webgpu.min.js +6 -0
- package/build/three.webgpu.nodes.js +83702 -0
- package/build/three.webgpu.nodes.min.js +6 -0
- package/examples/fonts/LICENSE +13 -0
- package/examples/fonts/MPLUSRounded1c/MPLUSRounded1c-Regular.typeface.json.zip +0 -0
- package/examples/fonts/MPLUSRounded1c/OFL.txt +91 -0
- package/examples/fonts/README.md +11 -0
- package/examples/fonts/droid/NOTICE +190 -0
- package/examples/fonts/droid/README.txt +18 -0
- package/examples/fonts/droid/droid_sans_bold.typeface.json +1 -0
- package/examples/fonts/droid/droid_sans_mono_regular.typeface.json +1 -0
- package/examples/fonts/droid/droid_sans_regular.typeface.json +1 -0
- package/examples/fonts/droid/droid_serif_bold.typeface.json +1 -0
- package/examples/fonts/droid/droid_serif_regular.typeface.json +1 -0
- package/examples/fonts/gentilis_bold.typeface.json +1 -0
- package/examples/fonts/gentilis_regular.typeface.json +1 -0
- package/examples/fonts/helvetiker_bold.typeface.json +1 -0
- package/examples/fonts/helvetiker_regular.typeface.json +1 -0
- package/examples/fonts/optimer_bold.typeface.json +1 -0
- package/examples/fonts/optimer_regular.typeface.json +1 -0
- package/examples/fonts/ttf/README.md +9 -0
- package/examples/fonts/ttf/kenpixel.ttf +0 -0
- package/examples/jsm/Addons.js +293 -0
- package/examples/jsm/animation/AnimationClipCreator.js +168 -0
- package/examples/jsm/animation/CCDIKSolver.js +595 -0
- package/examples/jsm/capabilities/WebGL.js +113 -0
- package/examples/jsm/capabilities/WebGPU.js +59 -0
- package/examples/jsm/controls/ArcballControls.js +3539 -0
- package/examples/jsm/controls/DragControls.js +452 -0
- package/examples/jsm/controls/FirstPersonControls.js +451 -0
- package/examples/jsm/controls/FlyControls.js +384 -0
- package/examples/jsm/controls/MapControls.js +116 -0
- package/examples/jsm/controls/OrbitControls.js +1963 -0
- package/examples/jsm/controls/PointerLockControls.js +264 -0
- package/examples/jsm/controls/TrackballControls.js +1001 -0
- package/examples/jsm/controls/TransformControls.js +1950 -0
- package/examples/jsm/csm/CSM.js +598 -0
- package/examples/jsm/csm/CSMFrustum.js +209 -0
- package/examples/jsm/csm/CSMHelper.js +243 -0
- package/examples/jsm/csm/CSMShader.js +307 -0
- package/examples/jsm/csm/CSMShadowNode.js +603 -0
- package/examples/jsm/curves/CurveExtras.js +694 -0
- package/examples/jsm/curves/NURBSCurve.js +155 -0
- package/examples/jsm/curves/NURBSSurface.js +98 -0
- package/examples/jsm/curves/NURBSUtils.js +532 -0
- package/examples/jsm/curves/NURBSVolume.js +82 -0
- package/examples/jsm/effects/AnaglyphEffect.js +274 -0
- package/examples/jsm/effects/AsciiEffect.js +310 -0
- package/examples/jsm/effects/OutlineEffect.js +489 -0
- package/examples/jsm/effects/ParallaxBarrierEffect.js +155 -0
- package/examples/jsm/effects/StereoEffect.js +91 -0
- package/examples/jsm/environments/ColorEnvironment.js +59 -0
- package/examples/jsm/environments/DebugEnvironment.js +102 -0
- package/examples/jsm/environments/RoomEnvironment.js +185 -0
- package/examples/jsm/exporters/DRACOExporter.js +311 -0
- package/examples/jsm/exporters/EXRExporter.js +618 -0
- package/examples/jsm/exporters/GLTFExporter.js +3738 -0
- package/examples/jsm/exporters/KTX2Exporter.js +347 -0
- package/examples/jsm/exporters/OBJExporter.js +308 -0
- package/examples/jsm/exporters/PLYExporter.js +562 -0
- package/examples/jsm/exporters/STLExporter.js +221 -0
- package/examples/jsm/exporters/USDZExporter.js +1254 -0
- package/examples/jsm/geometries/BoxLineGeometry.js +92 -0
- package/examples/jsm/geometries/ConvexGeometry.js +72 -0
- package/examples/jsm/geometries/DecalGeometry.js +420 -0
- package/examples/jsm/geometries/ParametricFunctions.js +100 -0
- package/examples/jsm/geometries/ParametricGeometry.js +172 -0
- package/examples/jsm/geometries/RoundedBoxGeometry.js +216 -0
- package/examples/jsm/geometries/TeapotGeometry.js +689 -0
- package/examples/jsm/geometries/TextGeometry.js +102 -0
- package/examples/jsm/gpgpu/BitonicSort.js +715 -0
- package/examples/jsm/helpers/AnimationPathHelper.js +302 -0
- package/examples/jsm/helpers/LightProbeGridHelper.js +221 -0
- package/examples/jsm/helpers/LightProbeHelper.js +165 -0
- package/examples/jsm/helpers/LightProbeHelperGPU.js +102 -0
- package/examples/jsm/helpers/OctreeHelper.js +109 -0
- package/examples/jsm/helpers/PositionalAudioHelper.js +169 -0
- package/examples/jsm/helpers/RapierHelper.js +59 -0
- package/examples/jsm/helpers/RectAreaLightHelper.js +118 -0
- package/examples/jsm/helpers/TextureHelper.js +265 -0
- package/examples/jsm/helpers/TextureHelperGPU.js +214 -0
- package/examples/jsm/helpers/VertexNormalsHelper.js +155 -0
- package/examples/jsm/helpers/VertexTangentsHelper.js +133 -0
- package/examples/jsm/helpers/ViewHelper.js +519 -0
- package/examples/jsm/inspector/Extension.js +13 -0
- package/examples/jsm/inspector/Inspector.js +542 -0
- package/examples/jsm/inspector/RendererInspector.js +425 -0
- package/examples/jsm/inspector/extensions/extensions.json +6 -0
- package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphEditor.js +916 -0
- package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphLoader.js +281 -0
- package/examples/jsm/inspector/tabs/Console.js +238 -0
- package/examples/jsm/inspector/tabs/Memory.js +128 -0
- package/examples/jsm/inspector/tabs/Parameters.js +380 -0
- package/examples/jsm/inspector/tabs/Performance.js +268 -0
- package/examples/jsm/inspector/tabs/Settings.js +264 -0
- package/examples/jsm/inspector/tabs/Timeline.js +1611 -0
- package/examples/jsm/inspector/tabs/Viewer.js +268 -0
- package/examples/jsm/inspector/ui/Graph.js +95 -0
- package/examples/jsm/inspector/ui/Item.js +170 -0
- package/examples/jsm/inspector/ui/List.js +75 -0
- package/examples/jsm/inspector/ui/Profiler.js +2072 -0
- package/examples/jsm/inspector/ui/Style.js +1667 -0
- package/examples/jsm/inspector/ui/Tab.js +265 -0
- package/examples/jsm/inspector/ui/Values.js +476 -0
- package/examples/jsm/inspector/ui/utils.js +69 -0
- package/examples/jsm/interaction/InteractionManager.js +226 -0
- package/examples/jsm/interactive/HTMLMesh.js +601 -0
- package/examples/jsm/interactive/InteractiveGroup.js +224 -0
- package/examples/jsm/interactive/SelectionBox.js +324 -0
- package/examples/jsm/interactive/SelectionHelper.js +150 -0
- package/examples/jsm/libs/ammo.wasm.js +822 -0
- package/examples/jsm/libs/ammo.wasm.wasm +0 -0
- package/examples/jsm/libs/basis/README.md +46 -0
- package/examples/jsm/libs/basis/basis_transcoder.js +19 -0
- package/examples/jsm/libs/basis/basis_transcoder.wasm +0 -0
- package/examples/jsm/libs/chevrotain.module.min.js +141 -0
- package/examples/jsm/libs/demuxer_mp4.js +109 -0
- package/examples/jsm/libs/draco/README.md +32 -0
- package/examples/jsm/libs/draco/draco_decoder.js +34 -0
- package/examples/jsm/libs/draco/draco_decoder.wasm +0 -0
- package/examples/jsm/libs/draco/draco_encoder.js +33 -0
- package/examples/jsm/libs/draco/draco_wasm_wrapper.js +117 -0
- package/examples/jsm/libs/draco/gltf/draco_decoder.js +33 -0
- package/examples/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
- package/examples/jsm/libs/draco/gltf/draco_encoder.js +33 -0
- package/examples/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
- package/examples/jsm/libs/ecsy.module.js +1792 -0
- package/examples/jsm/libs/fflate.module.js +2672 -0
- package/examples/jsm/libs/ktx-parse.module.js +1 -0
- package/examples/jsm/libs/lil-gui.module.min.js +8 -0
- package/examples/jsm/libs/lottie_canvas.module.js +14849 -0
- package/examples/jsm/libs/meshopt_decoder.module.js +196 -0
- package/examples/jsm/libs/mikktspace.module.js +128 -0
- package/examples/jsm/libs/motion-controllers.module.js +397 -0
- package/examples/jsm/libs/opentype.module.js +14506 -0
- package/examples/jsm/libs/potpack.module.js +125 -0
- package/examples/jsm/libs/rhino3dm/rhino3dm.js +21 -0
- package/examples/jsm/libs/rhino3dm/rhino3dm.module.js +16 -0
- package/examples/jsm/libs/rhino3dm/rhino3dm.wasm +0 -0
- package/examples/jsm/libs/stats.module.js +167 -0
- package/examples/jsm/libs/surfaceNet.js +201 -0
- package/examples/jsm/libs/tween.module.js +876 -0
- package/examples/jsm/libs/utif.module.js +1665 -0
- package/examples/jsm/libs/zstddec.module.js +1 -0
- package/examples/jsm/lighting/DynamicLighting.js +82 -0
- package/examples/jsm/lighting/LightProbeGrid.js +651 -0
- package/examples/jsm/lighting/TiledLighting.js +42 -0
- package/examples/jsm/lights/LightProbeGenerator.js +337 -0
- package/examples/jsm/lights/RectAreaLightTexturesLib.js +127 -0
- package/examples/jsm/lights/RectAreaLightUniformsLib.js +40 -0
- package/examples/jsm/lines/Line2.js +56 -0
- package/examples/jsm/lines/LineGeometry.js +157 -0
- package/examples/jsm/lines/LineMaterial.js +703 -0
- package/examples/jsm/lines/LineSegments2.js +426 -0
- package/examples/jsm/lines/LineSegmentsGeometry.js +298 -0
- package/examples/jsm/lines/Wireframe.js +108 -0
- package/examples/jsm/lines/WireframeGeometry2.js +49 -0
- package/examples/jsm/lines/webgpu/Line2.js +46 -0
- package/examples/jsm/lines/webgpu/LineSegments2.js +411 -0
- package/examples/jsm/lines/webgpu/Wireframe.js +86 -0
- package/examples/jsm/loaders/3DMLoader.js +1836 -0
- package/examples/jsm/loaders/3MFLoader.js +1621 -0
- package/examples/jsm/loaders/AMFLoader.js +541 -0
- package/examples/jsm/loaders/BVHLoader.js +484 -0
- package/examples/jsm/loaders/ColladaLoader.js +153 -0
- package/examples/jsm/loaders/DDSLoader.js +385 -0
- package/examples/jsm/loaders/DRACOLoader.js +739 -0
- package/examples/jsm/loaders/EXRLoader.js +3423 -0
- package/examples/jsm/loaders/FBXLoader.js +4580 -0
- package/examples/jsm/loaders/FontLoader.js +260 -0
- package/examples/jsm/loaders/GCodeLoader.js +318 -0
- package/examples/jsm/loaders/GLTFLoader.js +4860 -0
- package/examples/jsm/loaders/HDRCubeTextureLoader.js +164 -0
- package/examples/jsm/loaders/HDRLoader.js +485 -0
- package/examples/jsm/loaders/IESLoader.js +379 -0
- package/examples/jsm/loaders/KMZLoader.js +163 -0
- package/examples/jsm/loaders/KTX2Loader.js +1257 -0
- package/examples/jsm/loaders/KTXLoader.js +197 -0
- package/examples/jsm/loaders/LDrawLoader.js +2510 -0
- package/examples/jsm/loaders/LUT3dlLoader.js +205 -0
- package/examples/jsm/loaders/LUTCubeLoader.js +190 -0
- package/examples/jsm/loaders/LUTImageLoader.js +190 -0
- package/examples/jsm/loaders/LWOLoader.js +1047 -0
- package/examples/jsm/loaders/LottieLoader.js +130 -0
- package/examples/jsm/loaders/MD2Loader.js +435 -0
- package/examples/jsm/loaders/MDDLoader.js +147 -0
- package/examples/jsm/loaders/MTLLoader.js +593 -0
- package/examples/jsm/loaders/MaterialXLoader.js +1104 -0
- package/examples/jsm/loaders/NRRDLoader.js +718 -0
- package/examples/jsm/loaders/OBJLoader.js +955 -0
- package/examples/jsm/loaders/PCDLoader.js +620 -0
- package/examples/jsm/loaders/PDBLoader.js +272 -0
- package/examples/jsm/loaders/PLYLoader.js +805 -0
- package/examples/jsm/loaders/PVRLoader.js +270 -0
- package/examples/jsm/loaders/RGBELoader.js +18 -0
- package/examples/jsm/loaders/STLLoader.js +421 -0
- package/examples/jsm/loaders/SVGLoader.js +3267 -0
- package/examples/jsm/loaders/TDSLoader.js +1142 -0
- package/examples/jsm/loaders/TGALoader.js +538 -0
- package/examples/jsm/loaders/TIFFLoader.js +59 -0
- package/examples/jsm/loaders/TTFLoader.js +261 -0
- package/examples/jsm/loaders/USDLoader.js +279 -0
- package/examples/jsm/loaders/USDZLoader.js +16 -0
- package/examples/jsm/loaders/UltraHDRLoader.js +755 -0
- package/examples/jsm/loaders/VOXLoader.js +919 -0
- package/examples/jsm/loaders/VRMLLoader.js +3646 -0
- package/examples/jsm/loaders/VTKLoader.js +1293 -0
- package/examples/jsm/loaders/XYZLoader.js +143 -0
- package/examples/jsm/loaders/collada/ColladaComposer.js +3044 -0
- package/examples/jsm/loaders/collada/ColladaParser.js +1977 -0
- package/examples/jsm/loaders/lwo/IFFParser.js +1217 -0
- package/examples/jsm/loaders/lwo/LWO2Parser.js +414 -0
- package/examples/jsm/loaders/lwo/LWO3Parser.js +373 -0
- package/examples/jsm/loaders/usd/USDAParser.js +828 -0
- package/examples/jsm/loaders/usd/USDCParser.js +1878 -0
- package/examples/jsm/loaders/usd/USDComposer.js +4594 -0
- package/examples/jsm/materials/LDrawConditionalLineMaterial.js +183 -0
- package/examples/jsm/materials/LDrawConditionalLineNodeMaterial.js +154 -0
- package/examples/jsm/materials/WoodNodeMaterial.js +533 -0
- package/examples/jsm/math/Capsule.js +159 -0
- package/examples/jsm/math/ColorConverter.js +58 -0
- package/examples/jsm/math/ColorSpaces.js +147 -0
- package/examples/jsm/math/ConvexHull.js +1695 -0
- package/examples/jsm/math/ImprovedNoise.js +88 -0
- package/examples/jsm/math/Lut.js +317 -0
- package/examples/jsm/math/MeshSurfaceSampler.js +315 -0
- package/examples/jsm/math/OBB.js +535 -0
- package/examples/jsm/math/Octree.js +822 -0
- package/examples/jsm/math/SimplexNoise.js +470 -0
- package/examples/jsm/misc/ConvexObjectBreaker.js +539 -0
- package/examples/jsm/misc/GPUComputationRenderer.js +508 -0
- package/examples/jsm/misc/Gyroscope.js +78 -0
- package/examples/jsm/misc/MD2Character.js +391 -0
- package/examples/jsm/misc/MD2CharacterComplex.js +740 -0
- package/examples/jsm/misc/MorphAnimMesh.js +119 -0
- package/examples/jsm/misc/MorphBlendMesh.js +425 -0
- package/examples/jsm/misc/ProgressiveLightMap.js +370 -0
- package/examples/jsm/misc/ProgressiveLightMapGPU.js +322 -0
- package/examples/jsm/misc/RollerCoaster.js +661 -0
- package/examples/jsm/misc/TubePainter.js +599 -0
- package/examples/jsm/misc/Volume.js +520 -0
- package/examples/jsm/misc/VolumeSlice.js +275 -0
- package/examples/jsm/modifiers/CurveModifier.js +374 -0
- package/examples/jsm/modifiers/CurveModifierGPU.js +256 -0
- package/examples/jsm/modifiers/EdgeSplitModifier.js +299 -0
- package/examples/jsm/modifiers/SimplifyModifier.js +632 -0
- package/examples/jsm/modifiers/TessellateModifier.js +340 -0
- package/examples/jsm/objects/GroundedSkybox.js +69 -0
- package/examples/jsm/objects/Lensflare.js +489 -0
- package/examples/jsm/objects/LensflareMesh.js +376 -0
- package/examples/jsm/objects/MarchingCubes.js +1261 -0
- package/examples/jsm/objects/Reflector.js +386 -0
- package/examples/jsm/objects/ReflectorForSSRPass.js +393 -0
- package/examples/jsm/objects/Refractor.js +389 -0
- package/examples/jsm/objects/ShadowMesh.js +130 -0
- package/examples/jsm/objects/Sky.js +321 -0
- package/examples/jsm/objects/SkyMesh.js +377 -0
- package/examples/jsm/objects/Water.js +374 -0
- package/examples/jsm/objects/Water2.js +403 -0
- package/examples/jsm/objects/Water2Mesh.js +199 -0
- package/examples/jsm/objects/WaterMesh.js +194 -0
- package/examples/jsm/offscreen/jank.js +45 -0
- package/examples/jsm/offscreen/offscreen.js +8 -0
- package/examples/jsm/offscreen/scene.js +86 -0
- package/examples/jsm/physics/AmmoPhysics.js +359 -0
- package/examples/jsm/physics/JoltPhysics.js +334 -0
- package/examples/jsm/physics/RapierPhysics.js +436 -0
- package/examples/jsm/postprocessing/AfterimagePass.js +185 -0
- package/examples/jsm/postprocessing/BloomPass.js +274 -0
- package/examples/jsm/postprocessing/BokehPass.js +218 -0
- package/examples/jsm/postprocessing/ClearPass.js +97 -0
- package/examples/jsm/postprocessing/CubeTexturePass.js +146 -0
- package/examples/jsm/postprocessing/DotScreenPass.js +114 -0
- package/examples/jsm/postprocessing/EffectComposer.js +365 -0
- package/examples/jsm/postprocessing/FXAAPass.js +40 -0
- package/examples/jsm/postprocessing/FilmPass.js +113 -0
- package/examples/jsm/postprocessing/GTAOPass.js +727 -0
- package/examples/jsm/postprocessing/GlitchPass.js +177 -0
- package/examples/jsm/postprocessing/HalftonePass.js +134 -0
- package/examples/jsm/postprocessing/LUTPass.js +138 -0
- package/examples/jsm/postprocessing/MaskPass.js +195 -0
- package/examples/jsm/postprocessing/OutlinePass.js +776 -0
- package/examples/jsm/postprocessing/OutputPass.js +148 -0
- package/examples/jsm/postprocessing/Pass.js +191 -0
- package/examples/jsm/postprocessing/RenderPass.js +193 -0
- package/examples/jsm/postprocessing/RenderPixelatedPass.js +314 -0
- package/examples/jsm/postprocessing/RenderTransitionPass.js +267 -0
- package/examples/jsm/postprocessing/SAOPass.js +407 -0
- package/examples/jsm/postprocessing/SMAAPass.js +230 -0
- package/examples/jsm/postprocessing/SSAARenderPass.js +313 -0
- package/examples/jsm/postprocessing/SSAOPass.js +527 -0
- package/examples/jsm/postprocessing/SSRPass.js +856 -0
- package/examples/jsm/postprocessing/SavePass.js +132 -0
- package/examples/jsm/postprocessing/ShaderPass.js +135 -0
- package/examples/jsm/postprocessing/TAARenderPass.js +243 -0
- package/examples/jsm/postprocessing/TexturePass.js +131 -0
- package/examples/jsm/postprocessing/UnrealBloomPass.js +524 -0
- package/examples/jsm/renderers/CSS2DRenderer.js +328 -0
- package/examples/jsm/renderers/CSS3DRenderer.js +454 -0
- package/examples/jsm/renderers/Projector.js +1154 -0
- package/examples/jsm/renderers/SVGRenderer.js +799 -0
- package/examples/jsm/shaders/ACESFilmicToneMappingShader.js +96 -0
- package/examples/jsm/shaders/AfterimageShader.js +63 -0
- package/examples/jsm/shaders/BasicShader.js +36 -0
- package/examples/jsm/shaders/BleachBypassShader.js +68 -0
- package/examples/jsm/shaders/BlendShader.js +56 -0
- package/examples/jsm/shaders/BokehShader.js +151 -0
- package/examples/jsm/shaders/BokehShader2.js +404 -0
- package/examples/jsm/shaders/BrightnessContrastShader.js +62 -0
- package/examples/jsm/shaders/ColorCorrectionShader.js +59 -0
- package/examples/jsm/shaders/ColorifyShader.js +57 -0
- package/examples/jsm/shaders/ConvolutionShader.js +74 -0
- package/examples/jsm/shaders/CopyShader.js +52 -0
- package/examples/jsm/shaders/DOFMipMapShader.js +63 -0
- package/examples/jsm/shaders/DepthLimitedBlurShader.js +180 -0
- package/examples/jsm/shaders/DigitalGlitch.js +104 -0
- package/examples/jsm/shaders/DotScreenShader.js +75 -0
- package/examples/jsm/shaders/ExposureShader.js +51 -0
- package/examples/jsm/shaders/FXAAShader.js +298 -0
- package/examples/jsm/shaders/FilmShader.js +72 -0
- package/examples/jsm/shaders/FocusShader.js +94 -0
- package/examples/jsm/shaders/FreiChenShader.js +103 -0
- package/examples/jsm/shaders/GTAOShader.js +434 -0
- package/examples/jsm/shaders/GammaCorrectionShader.js +52 -0
- package/examples/jsm/shaders/HalftoneShader.js +332 -0
- package/examples/jsm/shaders/HorizontalBlurShader.js +68 -0
- package/examples/jsm/shaders/HorizontalTiltShiftShader.js +70 -0
- package/examples/jsm/shaders/HueSaturationShader.js +74 -0
- package/examples/jsm/shaders/KaleidoShader.js +65 -0
- package/examples/jsm/shaders/LuminosityHighPassShader.js +68 -0
- package/examples/jsm/shaders/LuminosityShader.js +54 -0
- package/examples/jsm/shaders/MirrorShader.js +62 -0
- package/examples/jsm/shaders/NormalMapShader.js +60 -0
- package/examples/jsm/shaders/OutputShader.js +103 -0
- package/examples/jsm/shaders/PoissonDenoiseShader.js +239 -0
- package/examples/jsm/shaders/RGBShiftShader.js +61 -0
- package/examples/jsm/shaders/SAOShader.js +201 -0
- package/examples/jsm/shaders/SMAAShader.js +489 -0
- package/examples/jsm/shaders/SSAOShader.js +331 -0
- package/examples/jsm/shaders/SSRShader.js +396 -0
- package/examples/jsm/shaders/SepiaShader.js +57 -0
- package/examples/jsm/shaders/SobelOperatorShader.js +98 -0
- package/examples/jsm/shaders/SubsurfaceScatteringShader.js +95 -0
- package/examples/jsm/shaders/TechnicolorShader.js +51 -0
- package/examples/jsm/shaders/ToonShader.js +349 -0
- package/examples/jsm/shaders/TriangleBlurShader.js +79 -0
- package/examples/jsm/shaders/UnpackDepthRGBAShader.js +60 -0
- package/examples/jsm/shaders/VelocityShader.js +137 -0
- package/examples/jsm/shaders/VerticalBlurShader.js +66 -0
- package/examples/jsm/shaders/VerticalTiltShiftShader.js +70 -0
- package/examples/jsm/shaders/VignetteShader.js +56 -0
- package/examples/jsm/shaders/VolumeShader.js +298 -0
- package/examples/jsm/shaders/WaterRefractionShader.js +106 -0
- package/examples/jsm/textures/FlakesTexture.js +54 -0
- package/examples/jsm/transpiler/AST.js +675 -0
- package/examples/jsm/transpiler/GLSLDecoder.js +1228 -0
- package/examples/jsm/transpiler/Linker.js +327 -0
- package/examples/jsm/transpiler/ShaderToyDecoder.js +51 -0
- package/examples/jsm/transpiler/TSLEncoder.js +983 -0
- package/examples/jsm/transpiler/Transpiler.js +67 -0
- package/examples/jsm/transpiler/TranspilerUtils.js +29 -0
- package/examples/jsm/transpiler/WGSLEncoder.js +839 -0
- package/examples/jsm/tsl/WebGLNodesHandler.js +605 -0
- package/examples/jsm/tsl/display/AfterImageNode.js +254 -0
- package/examples/jsm/tsl/display/AnaglyphPassNode.js +549 -0
- package/examples/jsm/tsl/display/AnamorphicNode.js +293 -0
- package/examples/jsm/tsl/display/BilateralBlurNode.js +374 -0
- package/examples/jsm/tsl/display/BleachBypass.js +33 -0
- package/examples/jsm/tsl/display/BloomNode.js +534 -0
- package/examples/jsm/tsl/display/CRT.js +150 -0
- package/examples/jsm/tsl/display/ChromaticAberrationNode.js +174 -0
- package/examples/jsm/tsl/display/DenoiseNode.js +334 -0
- package/examples/jsm/tsl/display/DepthOfFieldNode.js +554 -0
- package/examples/jsm/tsl/display/DotScreenNode.js +104 -0
- package/examples/jsm/tsl/display/FSR1Node.js +477 -0
- package/examples/jsm/tsl/display/FXAANode.js +365 -0
- package/examples/jsm/tsl/display/FilmNode.js +101 -0
- package/examples/jsm/tsl/display/GTAONode.js +572 -0
- package/examples/jsm/tsl/display/GaussianBlurNode.js +399 -0
- package/examples/jsm/tsl/display/GodraysNode.js +615 -0
- package/examples/jsm/tsl/display/LensflareNode.js +279 -0
- package/examples/jsm/tsl/display/Lut3DNode.js +109 -0
- package/examples/jsm/tsl/display/MotionBlur.js +33 -0
- package/examples/jsm/tsl/display/OutlineNode.js +812 -0
- package/examples/jsm/tsl/display/ParallaxBarrierPassNode.js +89 -0
- package/examples/jsm/tsl/display/PixelationPassNode.js +335 -0
- package/examples/jsm/tsl/display/RGBShiftNode.js +96 -0
- package/examples/jsm/tsl/display/RetroPassNode.js +263 -0
- package/examples/jsm/tsl/display/SMAANode.js +768 -0
- package/examples/jsm/tsl/display/SSAAPassNode.js +358 -0
- package/examples/jsm/tsl/display/SSGINode.js +638 -0
- package/examples/jsm/tsl/display/SSRNode.js +656 -0
- package/examples/jsm/tsl/display/SSSNode.js +490 -0
- package/examples/jsm/tsl/display/Sepia.js +24 -0
- package/examples/jsm/tsl/display/Shape.js +29 -0
- package/examples/jsm/tsl/display/SharpenNode.js +283 -0
- package/examples/jsm/tsl/display/SobelOperatorNode.js +168 -0
- package/examples/jsm/tsl/display/StereoCompositePassNode.js +192 -0
- package/examples/jsm/tsl/display/StereoPassNode.js +119 -0
- package/examples/jsm/tsl/display/TAAUNode.js +835 -0
- package/examples/jsm/tsl/display/TRAANode.js +767 -0
- package/examples/jsm/tsl/display/TransitionNode.js +141 -0
- package/examples/jsm/tsl/display/boxBlur.js +65 -0
- package/examples/jsm/tsl/display/depthAwareBlend.js +80 -0
- package/examples/jsm/tsl/display/hashBlur.js +54 -0
- package/examples/jsm/tsl/display/radialBlur.js +68 -0
- package/examples/jsm/tsl/lighting/DynamicLightsNode.js +300 -0
- package/examples/jsm/tsl/lighting/TiledLightsNode.js +442 -0
- package/examples/jsm/tsl/lighting/data/AmbientLightDataNode.js +61 -0
- package/examples/jsm/tsl/lighting/data/DirectionalLightDataNode.js +111 -0
- package/examples/jsm/tsl/lighting/data/HemisphereLightDataNode.js +99 -0
- package/examples/jsm/tsl/lighting/data/PointLightDataNode.js +134 -0
- package/examples/jsm/tsl/lighting/data/SpotLightDataNode.js +161 -0
- package/examples/jsm/tsl/math/Bayer.js +84 -0
- package/examples/jsm/tsl/shadows/TileShadowNode.js +456 -0
- package/examples/jsm/tsl/shadows/TileShadowNodeHelper.js +212 -0
- package/examples/jsm/tsl/utils/Raymarching.js +70 -0
- package/examples/jsm/utils/BufferGeometryUtils.js +1434 -0
- package/examples/jsm/utils/CameraUtils.js +82 -0
- package/examples/jsm/utils/ColorUtils.js +76 -0
- package/examples/jsm/utils/GeometryCompressionUtils.js +547 -0
- package/examples/jsm/utils/GeometryUtils.js +226 -0
- package/examples/jsm/utils/LDrawUtils.js +211 -0
- package/examples/jsm/utils/SceneOptimizer.js +458 -0
- package/examples/jsm/utils/SceneUtils.js +363 -0
- package/examples/jsm/utils/ShadowMapViewer.js +244 -0
- package/examples/jsm/utils/ShadowMapViewerGPU.js +233 -0
- package/examples/jsm/utils/SkeletonUtils.js +496 -0
- package/examples/jsm/utils/SortUtils.js +175 -0
- package/examples/jsm/utils/UVsDebug.js +173 -0
- package/examples/jsm/utils/WebGLTextureUtils.js +115 -0
- package/examples/jsm/utils/WebGPUTextureUtils.js +81 -0
- package/examples/jsm/utils/WorkerPool.js +167 -0
- package/examples/jsm/webxr/ARButton.js +251 -0
- package/examples/jsm/webxr/OculusHandModel.js +194 -0
- package/examples/jsm/webxr/OculusHandPointerModel.js +539 -0
- package/examples/jsm/webxr/Text2D.js +52 -0
- package/examples/jsm/webxr/VRButton.js +264 -0
- package/examples/jsm/webxr/XRButton.js +246 -0
- package/examples/jsm/webxr/XRControllerModelFactory.js +403 -0
- package/examples/jsm/webxr/XREstimatedLight.js +254 -0
- package/examples/jsm/webxr/XRHandMeshModel.js +177 -0
- package/examples/jsm/webxr/XRHandModelFactory.js +198 -0
- package/examples/jsm/webxr/XRHandPrimitiveModel.js +147 -0
- package/examples/jsm/webxr/XRPlanes.js +118 -0
- package/package.json +123 -0
- package/src/Three.Core.js +187 -0
- package/src/Three.Legacy.js +0 -0
- package/src/Three.TSL.js +647 -0
- package/src/Three.WebGPU.Nodes.js +31 -0
- package/src/Three.WebGPU.js +37 -0
- package/src/Three.js +10 -0
- package/src/animation/AnimationAction.js +942 -0
- package/src/animation/AnimationClip.js +628 -0
- package/src/animation/AnimationMixer.js +860 -0
- package/src/animation/AnimationObjectGroup.js +411 -0
- package/src/animation/AnimationUtils.js +495 -0
- package/src/animation/KeyframeTrack.js +636 -0
- package/src/animation/PropertyBinding.js +794 -0
- package/src/animation/PropertyMixer.js +385 -0
- package/src/animation/tracks/BooleanKeyframeTrack.js +55 -0
- package/src/animation/tracks/ColorKeyframeTrack.js +36 -0
- package/src/animation/tracks/NumberKeyframeTrack.js +36 -0
- package/src/animation/tracks/QuaternionKeyframeTrack.js +51 -0
- package/src/animation/tracks/StringKeyframeTrack.js +55 -0
- package/src/animation/tracks/VectorKeyframeTrack.js +36 -0
- package/src/audio/Audio.js +778 -0
- package/src/audio/AudioAnalyser.js +97 -0
- package/src/audio/AudioContext.js +40 -0
- package/src/audio/AudioListener.js +218 -0
- package/src/audio/PositionalAudio.js +253 -0
- package/src/cameras/ArrayCamera.js +54 -0
- package/src/cameras/Camera.js +160 -0
- package/src/cameras/CubeCamera.js +259 -0
- package/src/cameras/OrthographicCamera.js +245 -0
- package/src/cameras/PerspectiveCamera.js +407 -0
- package/src/cameras/StereoCamera.js +146 -0
- package/src/constants.js +1760 -0
- package/src/core/BufferAttribute.js +1056 -0
- package/src/core/BufferGeometry.js +1458 -0
- package/src/core/Clock.js +135 -0
- package/src/core/EventDispatcher.js +131 -0
- package/src/core/GLBufferAttribute.js +171 -0
- package/src/core/InstancedBufferAttribute.js +68 -0
- package/src/core/InstancedBufferGeometry.js +60 -0
- package/src/core/InstancedInterleavedBuffer.js +74 -0
- package/src/core/InterleavedBuffer.js +291 -0
- package/src/core/InterleavedBufferAttribute.js +549 -0
- package/src/core/Layers.js +121 -0
- package/src/core/Object3D.js +1667 -0
- package/src/core/Raycaster.js +262 -0
- package/src/core/RenderTarget.js +392 -0
- package/src/core/RenderTarget3D.js +48 -0
- package/src/core/Timer.js +184 -0
- package/src/core/Uniform.js +46 -0
- package/src/core/UniformsGroup.js +180 -0
- package/src/extras/Controls.js +120 -0
- package/src/extras/DataUtils.js +217 -0
- package/src/extras/Earcut.js +28 -0
- package/src/extras/ImageUtils.js +137 -0
- package/src/extras/PMREMGenerator.js +1167 -0
- package/src/extras/ShapeUtils.js +114 -0
- package/src/extras/TextureUtils.js +297 -0
- package/src/extras/core/Curve.js +517 -0
- package/src/extras/core/CurvePath.js +296 -0
- package/src/extras/core/Interpolations.js +111 -0
- package/src/extras/core/Path.js +329 -0
- package/src/extras/core/Shape.js +165 -0
- package/src/extras/core/ShapePath.js +367 -0
- package/src/extras/curves/ArcCurve.js +39 -0
- package/src/extras/curves/CatmullRomCurve3.js +327 -0
- package/src/extras/curves/CubicBezierCurve.js +145 -0
- package/src/extras/curves/CubicBezierCurve3.js +129 -0
- package/src/extras/curves/Curves.js +10 -0
- package/src/extras/curves/EllipseCurve.js +258 -0
- package/src/extras/curves/LineCurve.js +128 -0
- package/src/extras/curves/LineCurve3.js +128 -0
- package/src/extras/curves/QuadraticBezierCurve.js +133 -0
- package/src/extras/curves/QuadraticBezierCurve3.js +118 -0
- package/src/extras/curves/SplineCurve.js +145 -0
- package/src/extras/lib/earcut.js +685 -0
- package/src/geometries/BoxGeometry.js +219 -0
- package/src/geometries/CapsuleGeometry.js +218 -0
- package/src/geometries/CircleGeometry.js +142 -0
- package/src/geometries/ConeGeometry.js +70 -0
- package/src/geometries/CylinderGeometry.js +333 -0
- package/src/geometries/DodecahedronGeometry.js +99 -0
- package/src/geometries/EdgesGeometry.js +180 -0
- package/src/geometries/ExtrudeGeometry.js +910 -0
- package/src/geometries/Geometries.js +21 -0
- package/src/geometries/IcosahedronGeometry.js +75 -0
- package/src/geometries/LatheGeometry.js +230 -0
- package/src/geometries/OctahedronGeometry.js +70 -0
- package/src/geometries/PlaneGeometry.js +133 -0
- package/src/geometries/PolyhedronGeometry.js +348 -0
- package/src/geometries/RingGeometry.js +165 -0
- package/src/geometries/ShapeGeometry.js +233 -0
- package/src/geometries/SphereGeometry.js +175 -0
- package/src/geometries/TetrahedronGeometry.js +67 -0
- package/src/geometries/TorusGeometry.js +161 -0
- package/src/geometries/TorusKnotGeometry.js +206 -0
- package/src/geometries/TubeGeometry.js +253 -0
- package/src/geometries/WireframeGeometry.js +179 -0
- package/src/helpers/ArrowHelper.js +171 -0
- package/src/helpers/AxesHelper.js +96 -0
- package/src/helpers/Box3Helper.js +83 -0
- package/src/helpers/BoxHelper.js +149 -0
- package/src/helpers/CameraHelper.js +345 -0
- package/src/helpers/DirectionalLightHelper.js +148 -0
- package/src/helpers/GridHelper.js +82 -0
- package/src/helpers/HemisphereLightHelper.js +130 -0
- package/src/helpers/PlaneHelper.js +96 -0
- package/src/helpers/PointLightHelper.js +111 -0
- package/src/helpers/PolarGridHelper.js +126 -0
- package/src/helpers/SkeletonHelper.js +194 -0
- package/src/helpers/SpotLightHelper.js +154 -0
- package/src/lights/AmbientLight.js +42 -0
- package/src/lights/DirectionalLight.js +113 -0
- package/src/lights/DirectionalLightShadow.js +31 -0
- package/src/lights/HemisphereLight.js +76 -0
- package/src/lights/Light.js +85 -0
- package/src/lights/LightProbe.js +75 -0
- package/src/lights/LightShadow.js +347 -0
- package/src/lights/PointLight.js +131 -0
- package/src/lights/PointLightShadow.js +31 -0
- package/src/lights/RectAreaLight.js +115 -0
- package/src/lights/SpotLight.js +194 -0
- package/src/lights/SpotLightShadow.js +80 -0
- package/src/lights/webgpu/IESSpotLight.js +47 -0
- package/src/lights/webgpu/ProjectorLight.js +46 -0
- package/src/loaders/AnimationLoader.js +98 -0
- package/src/loaders/AudioLoader.js +108 -0
- package/src/loaders/BufferGeometryLoader.js +242 -0
- package/src/loaders/Cache.js +115 -0
- package/src/loaders/CompressedTextureLoader.js +167 -0
- package/src/loaders/CubeTextureLoader.js +103 -0
- package/src/loaders/DataTextureLoader.js +174 -0
- package/src/loaders/FileLoader.js +367 -0
- package/src/loaders/ImageBitmapLoader.js +223 -0
- package/src/loaders/ImageLoader.js +168 -0
- package/src/loaders/Loader.js +222 -0
- package/src/loaders/LoaderUtils.js +59 -0
- package/src/loaders/LoadingManager.js +329 -0
- package/src/loaders/MaterialLoader.js +439 -0
- package/src/loaders/ObjectLoader.js +1304 -0
- package/src/loaders/TextureLoader.js +74 -0
- package/src/loaders/nodes/NodeLoader.js +194 -0
- package/src/loaders/nodes/NodeMaterialLoader.js +108 -0
- package/src/loaders/nodes/NodeObjectLoader.js +169 -0
- package/src/materials/LineBasicMaterial.js +122 -0
- package/src/materials/LineDashedMaterial.js +86 -0
- package/src/materials/Material.js +1017 -0
- package/src/materials/Materials.js +39 -0
- package/src/materials/MeshBasicMaterial.js +250 -0
- package/src/materials/MeshDepthMaterial.js +148 -0
- package/src/materials/MeshDistanceMaterial.js +119 -0
- package/src/materials/MeshLambertMaterial.js +402 -0
- package/src/materials/MeshMatcapMaterial.js +245 -0
- package/src/materials/MeshNormalMaterial.js +175 -0
- package/src/materials/MeshPhongMaterial.js +421 -0
- package/src/materials/MeshPhysicalMaterial.js +536 -0
- package/src/materials/MeshStandardMaterial.js +425 -0
- package/src/materials/MeshToonMaterial.js +321 -0
- package/src/materials/PointsMaterial.js +139 -0
- package/src/materials/RawShaderMaterial.js +42 -0
- package/src/materials/ShaderMaterial.js +413 -0
- package/src/materials/ShadowMaterial.js +91 -0
- package/src/materials/SpriteMaterial.js +136 -0
- package/src/materials/nodes/Line2NodeMaterial.js +569 -0
- package/src/materials/nodes/LineBasicNodeMaterial.js +46 -0
- package/src/materials/nodes/LineDashedNodeMaterial.js +132 -0
- package/src/materials/nodes/MeshBasicNodeMaterial.js +134 -0
- package/src/materials/nodes/MeshLambertNodeMaterial.js +82 -0
- package/src/materials/nodes/MeshMatcapNodeMaterial.js +77 -0
- package/src/materials/nodes/MeshNormalNodeMaterial.js +67 -0
- package/src/materials/nodes/MeshPhongNodeMaterial.js +141 -0
- package/src/materials/nodes/MeshPhysicalNodeMaterial.js +521 -0
- package/src/materials/nodes/MeshSSSNodeMaterial.js +175 -0
- package/src/materials/nodes/MeshStandardNodeMaterial.js +187 -0
- package/src/materials/nodes/MeshToonNodeMaterial.js +66 -0
- package/src/materials/nodes/NodeMaterial.js +1330 -0
- package/src/materials/nodes/NodeMaterials.js +21 -0
- package/src/materials/nodes/PointsNodeMaterial.js +211 -0
- package/src/materials/nodes/ShadowNodeMaterial.js +76 -0
- package/src/materials/nodes/SpriteNodeMaterial.js +186 -0
- package/src/materials/nodes/VolumeNodeMaterial.js +81 -0
- package/src/materials/nodes/manager/NodeMaterialObserver.js +718 -0
- package/src/math/Box2.js +381 -0
- package/src/math/Box3.js +805 -0
- package/src/math/Color.js +967 -0
- package/src/math/ColorManagement.js +215 -0
- package/src/math/Cylindrical.js +120 -0
- package/src/math/Euler.js +449 -0
- package/src/math/Frustum.js +285 -0
- package/src/math/FrustumArray.js +258 -0
- package/src/math/Interpolant.js +320 -0
- package/src/math/Line3.js +337 -0
- package/src/math/MathUtils.js +751 -0
- package/src/math/Matrix2.js +128 -0
- package/src/math/Matrix3.js +617 -0
- package/src/math/Matrix4.js +1314 -0
- package/src/math/Plane.js +368 -0
- package/src/math/Quaternion.js +918 -0
- package/src/math/Ray.js +655 -0
- package/src/math/Sphere.js +420 -0
- package/src/math/Spherical.js +147 -0
- package/src/math/SphericalHarmonics3.js +341 -0
- package/src/math/Triangle.js +539 -0
- package/src/math/Vector2.js +870 -0
- package/src/math/Vector3.js +1263 -0
- package/src/math/Vector4.js +1067 -0
- package/src/math/interpolants/BezierInterpolant.js +108 -0
- package/src/math/interpolants/CubicInterpolant.js +159 -0
- package/src/math/interpolants/DiscreteInterpolant.js +34 -0
- package/src/math/interpolants/LinearInterpolant.js +51 -0
- package/src/math/interpolants/QuaternionLinearInterpolant.js +48 -0
- package/src/nodes/Nodes.js +166 -0
- package/src/nodes/TSL.js +173 -0
- package/src/nodes/accessors/AccessorsUtils.js +53 -0
- package/src/nodes/accessors/Arrays.js +68 -0
- package/src/nodes/accessors/BatchNode.js +163 -0
- package/src/nodes/accessors/Bitangent.js +82 -0
- package/src/nodes/accessors/BufferAttributeNode.js +432 -0
- package/src/nodes/accessors/BufferNode.js +128 -0
- package/src/nodes/accessors/BuiltinNode.js +63 -0
- package/src/nodes/accessors/Camera.js +403 -0
- package/src/nodes/accessors/ClippingNode.js +255 -0
- package/src/nodes/accessors/CubeTextureNode.js +215 -0
- package/src/nodes/accessors/InstanceNode.js +349 -0
- package/src/nodes/accessors/InstancedMeshNode.js +50 -0
- package/src/nodes/accessors/Lights.js +139 -0
- package/src/nodes/accessors/MaterialNode.js +783 -0
- package/src/nodes/accessors/MaterialProperties.js +56 -0
- package/src/nodes/accessors/MaterialReferenceNode.js +84 -0
- package/src/nodes/accessors/ModelNode.js +184 -0
- package/src/nodes/accessors/ModelViewProjectionNode.js +13 -0
- package/src/nodes/accessors/MorphNode.js +310 -0
- package/src/nodes/accessors/Normal.js +243 -0
- package/src/nodes/accessors/Object3DNode.js +268 -0
- package/src/nodes/accessors/PointUVNode.js +55 -0
- package/src/nodes/accessors/Position.js +122 -0
- package/src/nodes/accessors/ReferenceBaseNode.js +357 -0
- package/src/nodes/accessors/ReferenceNode.js +424 -0
- package/src/nodes/accessors/ReflectVector.js +36 -0
- package/src/nodes/accessors/RendererReferenceNode.js +78 -0
- package/src/nodes/accessors/SceneProperties.js +47 -0
- package/src/nodes/accessors/SkinningNode.js +328 -0
- package/src/nodes/accessors/StorageBufferNode.js +405 -0
- package/src/nodes/accessors/StorageTextureNode.js +295 -0
- package/src/nodes/accessors/Tangent.js +60 -0
- package/src/nodes/accessors/TangentUtils.js +46 -0
- package/src/nodes/accessors/Texture3DNode.js +196 -0
- package/src/nodes/accessors/TextureBicubic.js +92 -0
- package/src/nodes/accessors/TextureNode.js +971 -0
- package/src/nodes/accessors/TextureSizeNode.js +77 -0
- package/src/nodes/accessors/UV.js +11 -0
- package/src/nodes/accessors/UniformArrayNode.js +350 -0
- package/src/nodes/accessors/UserDataNode.js +76 -0
- package/src/nodes/accessors/VelocityNode.js +224 -0
- package/src/nodes/accessors/VertexColorNode.js +109 -0
- package/src/nodes/code/CodeNode.js +181 -0
- package/src/nodes/code/ExpressionNode.js +68 -0
- package/src/nodes/code/FunctionCallNode.js +187 -0
- package/src/nodes/code/FunctionNode.js +182 -0
- package/src/nodes/core/ArrayNode.js +174 -0
- package/src/nodes/core/AssignNode.js +202 -0
- package/src/nodes/core/AttributeNode.js +168 -0
- package/src/nodes/core/BypassNode.js +93 -0
- package/src/nodes/core/ConstNode.js +67 -0
- package/src/nodes/core/ContextNode.js +283 -0
- package/src/nodes/core/IndexNode.js +165 -0
- package/src/nodes/core/InputNode.js +136 -0
- package/src/nodes/core/InspectorNode.js +128 -0
- package/src/nodes/core/IsolateNode.js +133 -0
- package/src/nodes/core/LightingModel.js +77 -0
- package/src/nodes/core/MRTNode.js +196 -0
- package/src/nodes/core/Node.js +1180 -0
- package/src/nodes/core/NodeAttribute.js +53 -0
- package/src/nodes/core/NodeBuilder.js +3299 -0
- package/src/nodes/core/NodeCache.js +75 -0
- package/src/nodes/core/NodeCode.js +46 -0
- package/src/nodes/core/NodeError.js +28 -0
- package/src/nodes/core/NodeFrame.js +314 -0
- package/src/nodes/core/NodeFunction.js +69 -0
- package/src/nodes/core/NodeFunctionInput.js +61 -0
- package/src/nodes/core/NodeParser.js +23 -0
- package/src/nodes/core/NodeUniform.js +91 -0
- package/src/nodes/core/NodeUtils.js +408 -0
- package/src/nodes/core/NodeVar.js +60 -0
- package/src/nodes/core/NodeVarying.js +63 -0
- package/src/nodes/core/OutputStructNode.js +105 -0
- package/src/nodes/core/ParameterNode.js +94 -0
- package/src/nodes/core/PropertyNode.js +367 -0
- package/src/nodes/core/StackNode.js +426 -0
- package/src/nodes/core/StackTrace.js +139 -0
- package/src/nodes/core/StructNode.js +134 -0
- package/src/nodes/core/StructType.js +13 -0
- package/src/nodes/core/StructTypeNode.js +148 -0
- package/src/nodes/core/SubBuildNode.js +89 -0
- package/src/nodes/core/TempNode.js +88 -0
- package/src/nodes/core/UniformGroupNode.js +167 -0
- package/src/nodes/core/UniformNode.js +259 -0
- package/src/nodes/core/VarNode.js +367 -0
- package/src/nodes/core/VaryingNode.js +210 -0
- package/src/nodes/core/constants.js +68 -0
- package/src/nodes/display/BlendModes.js +171 -0
- package/src/nodes/display/BumpMapNode.js +117 -0
- package/src/nodes/display/ColorAdjustment.js +158 -0
- package/src/nodes/display/ColorSpaceFunctions.js +54 -0
- package/src/nodes/display/ColorSpaceNode.js +164 -0
- package/src/nodes/display/FrontFacingNode.js +102 -0
- package/src/nodes/display/NormalMapNode.js +153 -0
- package/src/nodes/display/PassNode.js +1055 -0
- package/src/nodes/display/RenderOutputNode.js +150 -0
- package/src/nodes/display/ScreenNode.js +292 -0
- package/src/nodes/display/ToneMappingFunctions.js +242 -0
- package/src/nodes/display/ToneMappingNode.js +147 -0
- package/src/nodes/display/ToonOutlinePassNode.js +191 -0
- package/src/nodes/display/ViewportDepthNode.js +342 -0
- package/src/nodes/display/ViewportDepthTextureNode.js +63 -0
- package/src/nodes/display/ViewportSharedTextureNode.js +73 -0
- package/src/nodes/display/ViewportTextureNode.js +256 -0
- package/src/nodes/fog/Fog.js +97 -0
- package/src/nodes/functions/BSDF/BRDF_GGX.js +55 -0
- package/src/nodes/functions/BSDF/BRDF_GGX_Multiscatter.js +52 -0
- package/src/nodes/functions/BSDF/BRDF_Lambert.js +9 -0
- package/src/nodes/functions/BSDF/BRDF_Sheen.js +57 -0
- package/src/nodes/functions/BSDF/DFGLUT.js +56 -0
- package/src/nodes/functions/BSDF/D_GGX.js +23 -0
- package/src/nodes/functions/BSDF/D_GGX_Anisotropic.js +28 -0
- package/src/nodes/functions/BSDF/EnvironmentBRDF.js +13 -0
- package/src/nodes/functions/BSDF/F_Schlick.js +16 -0
- package/src/nodes/functions/BSDF/LTC.js +175 -0
- package/src/nodes/functions/BSDF/Schlick_to_F0.js +21 -0
- package/src/nodes/functions/BSDF/V_GGX_SmithCorrelated.js +26 -0
- package/src/nodes/functions/BSDF/V_GGX_SmithCorrelated_Anisotropic.js +29 -0
- package/src/nodes/functions/BasicLightingModel.js +100 -0
- package/src/nodes/functions/PhongLightingModel.js +99 -0
- package/src/nodes/functions/PhysicalLightingModel.js +878 -0
- package/src/nodes/functions/ShadowMaskModel.js +58 -0
- package/src/nodes/functions/ToonLightingModel.js +70 -0
- package/src/nodes/functions/VolumetricLightingModel.js +183 -0
- package/src/nodes/functions/material/getAlphaHashThreshold.js +68 -0
- package/src/nodes/functions/material/getGeometryRoughness.js +19 -0
- package/src/nodes/functions/material/getParallaxCorrectNormal.js +37 -0
- package/src/nodes/functions/material/getRoughness.js +18 -0
- package/src/nodes/functions/material/getShIrradianceAt.js +28 -0
- package/src/nodes/geometry/RangeNode.js +210 -0
- package/src/nodes/gpgpu/AtomicFunctionNode.js +274 -0
- package/src/nodes/gpgpu/BarrierNode.js +98 -0
- package/src/nodes/gpgpu/ComputeBuiltinNode.js +227 -0
- package/src/nodes/gpgpu/ComputeNode.js +310 -0
- package/src/nodes/gpgpu/SubgroupFunctionNode.js +455 -0
- package/src/nodes/gpgpu/WorkgroupInfoNode.js +234 -0
- package/src/nodes/lighting/AONode.js +45 -0
- package/src/nodes/lighting/AmbientLightNode.js +35 -0
- package/src/nodes/lighting/AnalyticLightNode.js +309 -0
- package/src/nodes/lighting/BasicEnvironmentNode.js +49 -0
- package/src/nodes/lighting/BasicLightMapNode.js +49 -0
- package/src/nodes/lighting/DirectionalLightNode.js +39 -0
- package/src/nodes/lighting/EnvironmentNode.js +178 -0
- package/src/nodes/lighting/HemisphereLightNode.js +87 -0
- package/src/nodes/lighting/IESSpotLightNode.js +49 -0
- package/src/nodes/lighting/IrradianceNode.js +44 -0
- package/src/nodes/lighting/LightProbeNode.js +73 -0
- package/src/nodes/lighting/LightUtils.js +25 -0
- package/src/nodes/lighting/LightingContextNode.js +115 -0
- package/src/nodes/lighting/LightingNode.js +36 -0
- package/src/nodes/lighting/LightsNode.js +432 -0
- package/src/nodes/lighting/PointLightNode.js +102 -0
- package/src/nodes/lighting/PointShadowNode.js +325 -0
- package/src/nodes/lighting/ProjectorLightNode.js +91 -0
- package/src/nodes/lighting/RectAreaLightNode.js +133 -0
- package/src/nodes/lighting/ShadowBaseNode.js +81 -0
- package/src/nodes/lighting/ShadowFilterNode.js +264 -0
- package/src/nodes/lighting/ShadowNode.js +889 -0
- package/src/nodes/lighting/SpotLightNode.js +168 -0
- package/src/nodes/materialx/DISCLAIMER.md +199 -0
- package/src/nodes/materialx/MaterialXNodes.js +197 -0
- package/src/nodes/materialx/lib/mx_hsv.js +127 -0
- package/src/nodes/materialx/lib/mx_noise.js +1491 -0
- package/src/nodes/materialx/lib/mx_transform_color.js +23 -0
- package/src/nodes/math/BitcastNode.js +156 -0
- package/src/nodes/math/BitcountNode.js +433 -0
- package/src/nodes/math/ConditionalNode.js +245 -0
- package/src/nodes/math/Hash.js +21 -0
- package/src/nodes/math/MathNode.js +1202 -0
- package/src/nodes/math/MathUtils.js +54 -0
- package/src/nodes/math/OperatorNode.js +752 -0
- package/src/nodes/math/PackFloatNode.js +98 -0
- package/src/nodes/math/TriNoise3D.js +71 -0
- package/src/nodes/math/UnpackFloatNode.js +96 -0
- package/src/nodes/parsers/GLSLNodeFunction.js +168 -0
- package/src/nodes/parsers/GLSLNodeParser.js +25 -0
- package/src/nodes/pmrem/PMREMNode.js +397 -0
- package/src/nodes/pmrem/PMREMUtils.js +397 -0
- package/src/nodes/procedural/Checker.js +22 -0
- package/src/nodes/shapes/Shapes.js +33 -0
- package/src/nodes/tsl/TSLBase.js +36 -0
- package/src/nodes/tsl/TSLCore.js +1250 -0
- package/src/nodes/utils/ArrayElementNode.js +90 -0
- package/src/nodes/utils/ConvertNode.js +100 -0
- package/src/nodes/utils/CubeMapNode.js +237 -0
- package/src/nodes/utils/DebugNode.js +83 -0
- package/src/nodes/utils/Discard.js +24 -0
- package/src/nodes/utils/EquirectUV.js +27 -0
- package/src/nodes/utils/EventNode.js +148 -0
- package/src/nodes/utils/FlipNode.js +106 -0
- package/src/nodes/utils/FunctionOverloadingNode.js +170 -0
- package/src/nodes/utils/JoinNode.js +117 -0
- package/src/nodes/utils/LoopNode.js +349 -0
- package/src/nodes/utils/MatcapUV.js +22 -0
- package/src/nodes/utils/MaxMipLevelNode.js +103 -0
- package/src/nodes/utils/MemberNode.js +120 -0
- package/src/nodes/utils/Oscillators.js +41 -0
- package/src/nodes/utils/Packing.js +33 -0
- package/src/nodes/utils/PostProcessingUtils.js +154 -0
- package/src/nodes/utils/RTTNode.js +289 -0
- package/src/nodes/utils/ReflectorNode.js +629 -0
- package/src/nodes/utils/Remap.js +48 -0
- package/src/nodes/utils/RotateNode.js +103 -0
- package/src/nodes/utils/SampleNode.js +91 -0
- package/src/nodes/utils/SetNode.js +108 -0
- package/src/nodes/utils/SplitNode.js +179 -0
- package/src/nodes/utils/SpriteSheetUV.js +35 -0
- package/src/nodes/utils/SpriteUtils.js +63 -0
- package/src/nodes/utils/StorageArrayElementNode.js +143 -0
- package/src/nodes/utils/Timer.js +26 -0
- package/src/nodes/utils/TriplanarTextures.js +65 -0
- package/src/nodes/utils/UVUtils.js +67 -0
- package/src/nodes/utils/ViewportUtils.js +26 -0
- package/src/objects/BatchedMesh.js +1682 -0
- package/src/objects/Bone.js +41 -0
- package/src/objects/ClippingGroup.js +68 -0
- package/src/objects/Group.js +41 -0
- package/src/objects/InstancedMesh.js +422 -0
- package/src/objects/LOD.js +329 -0
- package/src/objects/Line.js +329 -0
- package/src/objects/LineLoop.js +37 -0
- package/src/objects/LineSegments.js +74 -0
- package/src/objects/Mesh.js +496 -0
- package/src/objects/Points.js +228 -0
- package/src/objects/Skeleton.js +392 -0
- package/src/objects/SkinnedMesh.js +370 -0
- package/src/objects/Sprite.js +245 -0
- package/src/renderers/WebGL3DRenderTarget.js +48 -0
- package/src/renderers/WebGLArrayRenderTarget.js +48 -0
- package/src/renderers/WebGLCubeRenderTarget.js +182 -0
- package/src/renderers/WebGLRenderTarget.js +34 -0
- package/src/renderers/WebGLRenderer.js +3699 -0
- package/src/renderers/common/Animation.js +159 -0
- package/src/renderers/common/Attributes.js +132 -0
- package/src/renderers/common/Backend.js +769 -0
- package/src/renderers/common/Background.js +225 -0
- package/src/renderers/common/BindGroup.js +46 -0
- package/src/renderers/common/Binding.js +71 -0
- package/src/renderers/common/Bindings.js +394 -0
- package/src/renderers/common/BlendMode.js +143 -0
- package/src/renderers/common/Buffer.js +127 -0
- package/src/renderers/common/BufferUtils.js +58 -0
- package/src/renderers/common/BundleGroup.js +83 -0
- package/src/renderers/common/CanvasTarget.js +341 -0
- package/src/renderers/common/ChainMap.js +122 -0
- package/src/renderers/common/ClippingContext.js +262 -0
- package/src/renderers/common/Color4.js +77 -0
- package/src/renderers/common/ComputePipeline.js +41 -0
- package/src/renderers/common/Constants.js +15 -0
- package/src/renderers/common/CubeRenderTarget.js +147 -0
- package/src/renderers/common/DataMap.js +90 -0
- package/src/renderers/common/Geometries.js +396 -0
- package/src/renderers/common/IndirectStorageBufferAttribute.js +38 -0
- package/src/renderers/common/Info.js +501 -0
- package/src/renderers/common/InspectorBase.js +151 -0
- package/src/renderers/common/Lighting.js +57 -0
- package/src/renderers/common/Pipeline.js +35 -0
- package/src/renderers/common/Pipelines.js +498 -0
- package/src/renderers/common/PostProcessing.js +28 -0
- package/src/renderers/common/ProgrammableStage.js +78 -0
- package/src/renderers/common/QuadMesh.js +112 -0
- package/src/renderers/common/ReadbackBuffer.js +78 -0
- package/src/renderers/common/RenderBundle.js +26 -0
- package/src/renderers/common/RenderBundles.js +71 -0
- package/src/renderers/common/RenderContext.js +281 -0
- package/src/renderers/common/RenderContexts.js +100 -0
- package/src/renderers/common/RenderList.js +404 -0
- package/src/renderers/common/RenderLists.js +78 -0
- package/src/renderers/common/RenderObject.js +915 -0
- package/src/renderers/common/RenderObjectPipeline.js +40 -0
- package/src/renderers/common/RenderObjects.js +218 -0
- package/src/renderers/common/RenderPipeline.js +255 -0
- package/src/renderers/common/Renderer.js +3654 -0
- package/src/renderers/common/RendererUtils.js +200 -0
- package/src/renderers/common/SampledTexture.js +152 -0
- package/src/renderers/common/Sampler.js +165 -0
- package/src/renderers/common/Storage3DTexture.js +100 -0
- package/src/renderers/common/StorageArrayTexture.js +84 -0
- package/src/renderers/common/StorageBuffer.js +53 -0
- package/src/renderers/common/StorageBufferAttribute.js +46 -0
- package/src/renderers/common/StorageInstancedBufferAttribute.js +46 -0
- package/src/renderers/common/StorageTexture.js +86 -0
- package/src/renderers/common/Textures.js +629 -0
- package/src/renderers/common/TimestampQueryPool.js +163 -0
- package/src/renderers/common/Uniform.js +375 -0
- package/src/renderers/common/UniformBuffer.js +34 -0
- package/src/renderers/common/UniformsGroup.js +548 -0
- package/src/renderers/common/XRManager.js +1684 -0
- package/src/renderers/common/XRRenderTarget.js +91 -0
- package/src/renderers/common/extras/PMREMGenerator.js +1042 -0
- package/src/renderers/common/nodes/NodeBuilderState.js +152 -0
- package/src/renderers/common/nodes/NodeLibrary.js +196 -0
- package/src/renderers/common/nodes/NodeManager.js +983 -0
- package/src/renderers/common/nodes/NodeSampledTexture.js +140 -0
- package/src/renderers/common/nodes/NodeSampler.js +62 -0
- package/src/renderers/common/nodes/NodeStorageBuffer.js +72 -0
- package/src/renderers/common/nodes/NodeUniform.js +418 -0
- package/src/renderers/common/nodes/NodeUniformBuffer.js +105 -0
- package/src/renderers/common/nodes/NodeUniformsGroup.js +51 -0
- package/src/renderers/shaders/DFGLUTData.js +49 -0
- package/src/renderers/shaders/ShaderChunk/alphahash_fragment.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/alphahash_pars_fragment.glsl.js +68 -0
- package/src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl.js +16 -0
- package/src/renderers/shaders/ShaderChunk/alphatest_pars_fragment.glsl.js +5 -0
- package/src/renderers/shaders/ShaderChunk/aomap_fragment.glsl.js +26 -0
- package/src/renderers/shaders/ShaderChunk/aomap_pars_fragment.glsl.js +8 -0
- package/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js +49 -0
- package/src/renderers/shaders/ShaderChunk/batching_vertex.glsl.js +5 -0
- package/src/renderers/shaders/ShaderChunk/begin_vertex.glsl.js +9 -0
- package/src/renderers/shaders/ShaderChunk/beginnormal_vertex.glsl.js +9 -0
- package/src/renderers/shaders/ShaderChunk/bsdfs.glsl.js +33 -0
- package/src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl.js +43 -0
- package/src/renderers/shaders/ShaderChunk/clearcoat_normal_fragment_begin.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/clearcoat_normal_fragment_maps.glsl.js +10 -0
- package/src/renderers/shaders/ShaderChunk/clearcoat_pars_fragment.glsl.js +21 -0
- package/src/renderers/shaders/ShaderChunk/clipping_planes_fragment.glsl.js +78 -0
- package/src/renderers/shaders/ShaderChunk/clipping_planes_pars_fragment.glsl.js +9 -0
- package/src/renderers/shaders/ShaderChunk/clipping_planes_pars_vertex.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/clipping_planes_vertex.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/color_fragment.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js +29 -0
- package/src/renderers/shaders/ShaderChunk/colorspace_fragment.glsl.js +3 -0
- package/src/renderers/shaders/ShaderChunk/colorspace_pars_fragment.glsl.js +15 -0
- package/src/renderers/shaders/ShaderChunk/common.glsl.js +125 -0
- package/src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl.js +186 -0
- package/src/renderers/shaders/ShaderChunk/default_fragment.glsl.js +5 -0
- package/src/renderers/shaders/ShaderChunk/default_vertex.glsl.js +5 -0
- package/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js +63 -0
- package/src/renderers/shaders/ShaderChunk/displacementmap_pars_vertex.glsl.js +9 -0
- package/src/renderers/shaders/ShaderChunk/displacementmap_vertex.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/dithering_fragment.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/dithering_pars_fragment.glsl.js +20 -0
- package/src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl.js +17 -0
- package/src/renderers/shaders/ShaderChunk/emissivemap_pars_fragment.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/envmap_common_pars_fragment.glsl.js +14 -0
- package/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js +58 -0
- package/src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl.js +21 -0
- package/src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl.js +22 -0
- package/src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js +69 -0
- package/src/renderers/shaders/ShaderChunk/envmap_vertex.glsl.js +37 -0
- package/src/renderers/shaders/ShaderChunk/fog_fragment.glsl.js +17 -0
- package/src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl.js +19 -0
- package/src/renderers/shaders/ShaderChunk/fog_pars_vertex.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/fog_vertex.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/gradientmap_pars_fragment.glsl.js +27 -0
- package/src/renderers/shaders/ShaderChunk/iridescence_fragment.glsl.js +120 -0
- package/src/renderers/shaders/ShaderChunk/iridescence_pars_fragment.glsl.js +14 -0
- package/src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl.js +8 -0
- package/src/renderers/shaders/ShaderChunk/lightprobes_pars_fragment.glsl.js +80 -0
- package/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js +213 -0
- package/src/renderers/shaders/ShaderChunk/lights_fragment_end.glsl.js +19 -0
- package/src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js +44 -0
- package/src/renderers/shaders/ShaderChunk/lights_lambert_fragment.glsl.js +5 -0
- package/src/renderers/shaders/ShaderChunk/lights_lambert_pars_fragment.glsl.js +28 -0
- package/src/renderers/shaders/ShaderChunk/lights_pars_begin.glsl.js +216 -0
- package/src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl.js +32 -0
- package/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js +161 -0
- package/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +656 -0
- package/src/renderers/shaders/ShaderChunk/lights_toon_fragment.glsl.js +4 -0
- package/src/renderers/shaders/ShaderChunk/lights_toon_pars_fragment.glsl.js +26 -0
- package/src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl.js +9 -0
- package/src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl.js +9 -0
- package/src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl.js +8 -0
- package/src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl.js +8 -0
- package/src/renderers/shaders/ShaderChunk/map_fragment.glsl.js +17 -0
- package/src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl.js +27 -0
- package/src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl.js +27 -0
- package/src/renderers/shaders/ShaderChunk/metalnessmap_fragment.glsl.js +12 -0
- package/src/renderers/shaders/ShaderChunk/metalnessmap_pars_fragment.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/morphcolor_vertex.glsl.js +24 -0
- package/src/renderers/shaders/ShaderChunk/morphinstance_vertex.glsl.js +14 -0
- package/src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl.js +16 -0
- package/src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl.js +26 -0
- package/src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl.js +16 -0
- package/src/renderers/shaders/ShaderChunk/normal_fragment_begin.glsl.js +76 -0
- package/src/renderers/shaders/ShaderChunk/normal_fragment_maps.glsl.js +40 -0
- package/src/renderers/shaders/ShaderChunk/normal_pars_fragment.glsl.js +14 -0
- package/src/renderers/shaders/ShaderChunk/normal_pars_vertex.glsl.js +14 -0
- package/src/renderers/shaders/ShaderChunk/normal_vertex.glsl.js +14 -0
- package/src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js +43 -0
- package/src/renderers/shaders/ShaderChunk/opaque_fragment.glsl.js +11 -0
- package/src/renderers/shaders/ShaderChunk/packing.glsl.js +116 -0
- package/src/renderers/shaders/ShaderChunk/premultiplied_alpha_fragment.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/project_vertex.glsl.js +19 -0
- package/src/renderers/shaders/ShaderChunk/roughnessmap_fragment.glsl.js +12 -0
- package/src/renderers/shaders/ShaderChunk/roughnessmap_pars_fragment.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js +376 -0
- package/src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl.js +71 -0
- package/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl.js +78 -0
- package/src/renderers/shaders/ShaderChunk/shadowmask_pars_fragment.glsl.js +66 -0
- package/src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl.js +10 -0
- package/src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl.js +25 -0
- package/src/renderers/shaders/ShaderChunk/skinning_vertex.glsl.js +15 -0
- package/src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl.js +20 -0
- package/src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl.js +14 -0
- package/src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/tonemapping_fragment.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/tonemapping_pars_fragment.glsl.js +200 -0
- package/src/renderers/shaders/ShaderChunk/transmission_fragment.glsl.js +36 -0
- package/src/renderers/shaders/ShaderChunk/transmission_pars_fragment.glsl.js +235 -0
- package/src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl.js +119 -0
- package/src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl.js +145 -0
- package/src/renderers/shaders/ShaderChunk/uv_vertex.glsl.js +122 -0
- package/src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl.js +21 -0
- package/src/renderers/shaders/ShaderChunk.js +272 -0
- package/src/renderers/shaders/ShaderLib/background.glsl.js +40 -0
- package/src/renderers/shaders/ShaderLib/backgroundCube.glsl.js +62 -0
- package/src/renderers/shaders/ShaderLib/cube.glsl.js +36 -0
- package/src/renderers/shaders/ShaderLib/depth.glsl.js +117 -0
- package/src/renderers/shaders/ShaderLib/distance.glsl.js +77 -0
- package/src/renderers/shaders/ShaderLib/equirect.glsl.js +35 -0
- package/src/renderers/shaders/ShaderLib/linedashed.glsl.js +76 -0
- package/src/renderers/shaders/ShaderLib/meshbasic.glsl.js +116 -0
- package/src/renderers/shaders/ShaderLib/meshlambert.glsl.js +126 -0
- package/src/renderers/shaders/ShaderLib/meshmatcap.glsl.js +112 -0
- package/src/renderers/shaders/ShaderLib/meshnormal.glsl.js +86 -0
- package/src/renderers/shaders/ShaderLib/meshphong.glsl.js +128 -0
- package/src/renderers/shaders/ShaderLib/meshphysical.glsl.js +224 -0
- package/src/renderers/shaders/ShaderLib/meshtoon.glsl.js +118 -0
- package/src/renderers/shaders/ShaderLib/points.glsl.js +87 -0
- package/src/renderers/shaders/ShaderLib/shadow.glsl.js +58 -0
- package/src/renderers/shaders/ShaderLib/sprite.glsl.js +79 -0
- package/src/renderers/shaders/ShaderLib/vsm.glsl.js +51 -0
- package/src/renderers/shaders/ShaderLib.js +362 -0
- package/src/renderers/shaders/UniformsLib.js +234 -0
- package/src/renderers/shaders/UniformsUtils.js +154 -0
- package/src/renderers/webgl/WebGLAnimation.js +54 -0
- package/src/renderers/webgl/WebGLAttributes.js +237 -0
- package/src/renderers/webgl/WebGLBackground.js +303 -0
- package/src/renderers/webgl/WebGLBindingStates.js +666 -0
- package/src/renderers/webgl/WebGLBufferRenderer.js +57 -0
- package/src/renderers/webgl/WebGLCapabilities.js +150 -0
- package/src/renderers/webgl/WebGLClipping.js +171 -0
- package/src/renderers/webgl/WebGLEnvironments.js +228 -0
- package/src/renderers/webgl/WebGLExtensions.js +61 -0
- package/src/renderers/webgl/WebGLGeometries.js +186 -0
- package/src/renderers/webgl/WebGLIndexedBufferRenderer.js +68 -0
- package/src/renderers/webgl/WebGLInfo.js +73 -0
- package/src/renderers/webgl/WebGLLights.js +584 -0
- package/src/renderers/webgl/WebGLMaterials.js +602 -0
- package/src/renderers/webgl/WebGLMorphtargets.js +168 -0
- package/src/renderers/webgl/WebGLObjects.js +94 -0
- package/src/renderers/webgl/WebGLOutput.js +270 -0
- package/src/renderers/webgl/WebGLProgram.js +1031 -0
- package/src/renderers/webgl/WebGLPrograms.js +679 -0
- package/src/renderers/webgl/WebGLProperties.js +55 -0
- package/src/renderers/webgl/WebGLRenderLists.js +253 -0
- package/src/renderers/webgl/WebGLRenderStates.js +124 -0
- package/src/renderers/webgl/WebGLShader.js +12 -0
- package/src/renderers/webgl/WebGLShaderCache.js +124 -0
- package/src/renderers/webgl/WebGLShadowMap.js +600 -0
- package/src/renderers/webgl/WebGLState.js +1370 -0
- package/src/renderers/webgl/WebGLTextures.js +2506 -0
- package/src/renderers/webgl/WebGLUniforms.js +1203 -0
- package/src/renderers/webgl/WebGLUniformsGroups.js +413 -0
- package/src/renderers/webgl/WebGLUtils.js +217 -0
- package/src/renderers/webgl-fallback/WebGLBackend.js +2783 -0
- package/src/renderers/webgl-fallback/WebGLBufferRenderer.js +99 -0
- package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +1676 -0
- package/src/renderers/webgl-fallback/utils/WebGLAttributeUtils.js +359 -0
- package/src/renderers/webgl-fallback/utils/WebGLCapabilities.js +89 -0
- package/src/renderers/webgl-fallback/utils/WebGLConstants.js +13 -0
- package/src/renderers/webgl-fallback/utils/WebGLExtensions.js +83 -0
- package/src/renderers/webgl-fallback/utils/WebGLState.js +1361 -0
- package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +1324 -0
- package/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js +396 -0
- package/src/renderers/webgl-fallback/utils/WebGLUtils.js +317 -0
- package/src/renderers/webgpu/WebGPUBackend.js +2602 -0
- package/src/renderers/webgpu/WebGPURenderer.Nodes.js +69 -0
- package/src/renderers/webgpu/WebGPURenderer.js +107 -0
- package/src/renderers/webgpu/nodes/BasicNodeLibrary.js +66 -0
- package/src/renderers/webgpu/nodes/StandardNodeLibrary.js +100 -0
- package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +2523 -0
- package/src/renderers/webgpu/nodes/WGSLNodeFunction.js +187 -0
- package/src/renderers/webgpu/nodes/WGSLNodeParser.js +25 -0
- package/src/renderers/webgpu/utils/WebGPUAttributeUtils.js +522 -0
- package/src/renderers/webgpu/utils/WebGPUBindingUtils.js +642 -0
- package/src/renderers/webgpu/utils/WebGPUCapabilities.js +48 -0
- package/src/renderers/webgpu/utils/WebGPUConstants.js +354 -0
- package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +846 -0
- package/src/renderers/webgpu/utils/WebGPUTexturePassUtils.js +389 -0
- package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +1673 -0
- package/src/renderers/webgpu/utils/WebGPUTimestampQueryPool.js +310 -0
- package/src/renderers/webgpu/utils/WebGPUUtils.js +269 -0
- package/src/renderers/webxr/WebXRController.js +440 -0
- package/src/renderers/webxr/WebXRDepthSensing.js +155 -0
- package/src/renderers/webxr/WebXRManager.js +1103 -0
- package/src/scenes/Fog.js +98 -0
- package/src/scenes/FogExp2.js +86 -0
- package/src/scenes/Scene.js +165 -0
- package/src/textures/CanvasTexture.js +45 -0
- package/src/textures/CompressedArrayTexture.js +89 -0
- package/src/textures/CompressedCubeTexture.js +48 -0
- package/src/textures/CompressedTexture.js +86 -0
- package/src/textures/CubeDepthTexture.js +76 -0
- package/src/textures/CubeTexture.js +81 -0
- package/src/textures/Data3DTexture.js +112 -0
- package/src/textures/DataArrayTexture.js +134 -0
- package/src/textures/DataTexture.js +87 -0
- package/src/textures/DepthTexture.js +104 -0
- package/src/textures/ExternalTexture.js +56 -0
- package/src/textures/FramebufferTexture.js +85 -0
- package/src/textures/HTMLTexture.js +74 -0
- package/src/textures/Source.js +230 -0
- package/src/textures/Texture.js +812 -0
- package/src/textures/VideoFrameTexture.js +72 -0
- package/src/textures/VideoTexture.js +127 -0
- 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 };
|