@babylonjs/core 9.9.1 → 9.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/AudioV2/abstractAudio/audioEngineV2.d.ts +34 -1
  2. package/AudioV2/abstractAudio/audioEngineV2.js +54 -0
  3. package/AudioV2/abstractAudio/audioEngineV2.js.map +1 -1
  4. package/AudioV2/abstractAudio/components/spatialAudioAttacherComponent.d.ts +12 -0
  5. package/AudioV2/abstractAudio/components/spatialAudioAttacherComponent.js +18 -0
  6. package/AudioV2/abstractAudio/components/spatialAudioAttacherComponent.js.map +1 -1
  7. package/AudioV2/abstractAudio/index.d.ts +1 -0
  8. package/AudioV2/abstractAudio/index.js +1 -0
  9. package/AudioV2/abstractAudio/index.js.map +1 -1
  10. package/AudioV2/abstractAudio/pure.d.ts +1 -0
  11. package/AudioV2/abstractAudio/pure.js +1 -0
  12. package/AudioV2/abstractAudio/pure.js.map +1 -1
  13. package/AudioV2/abstractAudio/subNodes/spatialAudioSubNode.d.ts +7 -1
  14. package/AudioV2/abstractAudio/subNodes/spatialAudioSubNode.js +12 -0
  15. package/AudioV2/abstractAudio/subNodes/spatialAudioSubNode.js.map +1 -1
  16. package/AudioV2/abstractAudio/subProperties/abstractSpatialAudio.d.ts +14 -0
  17. package/AudioV2/abstractAudio/subProperties/abstractSpatialAudio.js.map +1 -1
  18. package/AudioV2/abstractAudio/subProperties/abstractSpatialAudioListener.d.ts +4 -0
  19. package/AudioV2/abstractAudio/subProperties/abstractSpatialAudioListener.js.map +1 -1
  20. package/AudioV2/abstractAudio/subProperties/spatialAudio.d.ts +6 -0
  21. package/AudioV2/abstractAudio/subProperties/spatialAudio.js +12 -0
  22. package/AudioV2/abstractAudio/subProperties/spatialAudio.js.map +1 -1
  23. package/AudioV2/abstractAudio/subProperties/spatialAudioListener.d.ts +2 -0
  24. package/AudioV2/abstractAudio/subProperties/spatialAudioListener.js +4 -0
  25. package/AudioV2/abstractAudio/subProperties/spatialAudioListener.js.map +1 -1
  26. package/AudioV2/webAudio/webAudioEngine.js +2 -1
  27. package/AudioV2/webAudio/webAudioEngine.js.map +1 -1
  28. package/Engines/abstractEngine.pure.js +2 -2
  29. package/Engines/abstractEngine.pure.js.map +1 -1
  30. package/FrameGraph/Node/Blocks/Layers/selectionOutlineLayerBlock.pure.d.ts +3 -0
  31. package/FrameGraph/Node/Blocks/Layers/selectionOutlineLayerBlock.pure.js +16 -1
  32. package/FrameGraph/Node/Blocks/Layers/selectionOutlineLayerBlock.pure.js.map +1 -1
  33. package/FrameGraph/Tasks/Layers/selectionOutlineTask.d.ts +2 -1
  34. package/FrameGraph/Tasks/Layers/selectionOutlineTask.js +5 -2
  35. package/FrameGraph/Tasks/Layers/selectionOutlineTask.js.map +1 -1
  36. package/Gizmos/index.d.ts +1 -0
  37. package/Gizmos/index.js +1 -0
  38. package/Gizmos/index.js.map +1 -1
  39. package/Gizmos/pure.d.ts +1 -0
  40. package/Gizmos/pure.js +1 -0
  41. package/Gizmos/pure.js.map +1 -1
  42. package/Gizmos/spatialAudioGizmo.d.ts +55 -0
  43. package/Gizmos/spatialAudioGizmo.js +151 -0
  44. package/Gizmos/spatialAudioGizmo.js.map +1 -0
  45. package/Layers/selectionOutlineLayer.pure.d.ts +9 -2
  46. package/Layers/selectionOutlineLayer.pure.js +29 -6
  47. package/Layers/selectionOutlineLayer.pure.js.map +1 -1
  48. package/Layers/thinEffectLayer.d.ts +1 -0
  49. package/Layers/thinEffectLayer.js +7 -4
  50. package/Layers/thinEffectLayer.js.map +1 -1
  51. package/Layers/thinSelectionOutlineLayer.d.ts +17 -3
  52. package/Layers/thinSelectionOutlineLayer.js +82 -17
  53. package/Layers/thinSelectionOutlineLayer.js.map +1 -1
  54. package/Materials/GaussianSplatting/gaussianSplattingMaterial.pure.d.ts +5 -0
  55. package/Materials/GaussianSplatting/gaussianSplattingMaterial.pure.js +54 -1
  56. package/Materials/GaussianSplatting/gaussianSplattingMaterial.pure.js.map +1 -1
  57. package/Materials/PBR/openpbrMaterial.pure.d.ts +13 -0
  58. package/Materials/PBR/openpbrMaterial.pure.js +17 -0
  59. package/Materials/PBR/openpbrMaterial.pure.js.map +1 -1
  60. package/Meshes/GaussianSplatting/gaussianSplattingCompoundMesh.pure.d.ts +2 -1
  61. package/Meshes/GaussianSplatting/gaussianSplattingCompoundMesh.pure.js +3 -2
  62. package/Meshes/GaussianSplatting/gaussianSplattingCompoundMesh.pure.js.map +1 -1
  63. package/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.d.ts +32 -2
  64. package/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.js +180 -22
  65. package/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.js.map +1 -1
  66. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.d.ts +54 -10
  67. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.js +130 -13
  68. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.js.map +1 -1
  69. package/Meshes/abstractMesh.pure.d.ts +5 -1
  70. package/Meshes/abstractMesh.pure.js +92 -2
  71. package/Meshes/abstractMesh.pure.js.map +1 -1
  72. package/Meshes/thinInstanceMesh.pure.js +143 -2
  73. package/Meshes/thinInstanceMesh.pure.js.map +1 -1
  74. package/Meshes/thinInstanceMesh.types.d.ts +2 -1
  75. package/Meshes/thinInstanceMesh.types.js.map +1 -1
  76. package/Misc/tools.pure.js +1 -1
  77. package/Misc/tools.pure.js.map +1 -1
  78. package/Rendering/IBLShadows/iblShadowsRenderPipeline.pure.js +12 -1
  79. package/Rendering/IBLShadows/iblShadowsRenderPipeline.pure.js.map +1 -1
  80. package/Rendering/geometryBufferRenderer.pure.js +8 -0
  81. package/Rendering/geometryBufferRenderer.pure.js.map +1 -1
  82. package/Rendering/objectRenderer.js +4 -2
  83. package/Rendering/objectRenderer.js.map +1 -1
  84. package/Shaders/ShadersInclude/gaussianSplatting.js +54 -5
  85. package/Shaders/ShadersInclude/gaussianSplatting.js.map +1 -1
  86. package/Shaders/ShadersInclude/pbrBlockFinalLitComponents.js +1 -1
  87. package/Shaders/ShadersInclude/pbrBlockFinalLitComponents.js.map +1 -1
  88. package/Shaders/gaussianSplatting.vertex.js +14 -3
  89. package/Shaders/gaussianSplatting.vertex.js.map +1 -1
  90. package/Shaders/gaussianSplattingVoxel.vertex.js +1 -0
  91. package/Shaders/gaussianSplattingVoxel.vertex.js.map +1 -1
  92. package/Shaders/selectionOutline.fragment.js +16 -4
  93. package/Shaders/selectionOutline.fragment.js.map +1 -1
  94. package/ShadersWGSL/ShadersInclude/gaussianSplatting.js +56 -5
  95. package/ShadersWGSL/ShadersInclude/gaussianSplatting.js.map +1 -1
  96. package/ShadersWGSL/ShadersInclude/openpbrBaseLayerData.js +2 -3
  97. package/ShadersWGSL/ShadersInclude/openpbrBaseLayerData.js.map +1 -1
  98. package/ShadersWGSL/ShadersInclude/pbrBlockFinalLitComponents.js +1 -1
  99. package/ShadersWGSL/ShadersInclude/pbrBlockFinalLitComponents.js.map +1 -1
  100. package/ShadersWGSL/gaussianSplatting.vertex.js +14 -3
  101. package/ShadersWGSL/gaussianSplatting.vertex.js.map +1 -1
  102. package/ShadersWGSL/gaussianSplattingVoxel.vertex.js +1 -0
  103. package/ShadersWGSL/gaussianSplattingVoxel.vertex.js.map +1 -1
  104. package/ShadersWGSL/selectionOutline.fragment.js +14 -2
  105. package/ShadersWGSL/selectionOutline.fragment.js.map +1 -1
  106. package/XR/features/WebXRBodyTracking.pure.d.ts +16 -0
  107. package/XR/features/WebXRBodyTracking.pure.js +167 -30
  108. package/XR/features/WebXRBodyTracking.pure.js.map +1 -1
  109. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"selectionOutline.fragment.js","sourceRoot":"","sources":["../../../../dev/core/src/ShadersWGSL/selectionOutline.fragment.ts"],"names":[],"mappings":"AAAA,eAAe;AACf,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAErD,MAAM,IAAI,GAAG,6BAA6B,CAAC;AAC3C,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+Cd,CAAC;AACF,aAAa;AACb,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;IACtC,WAAW,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC;AAChD,CAAC;AAED,gBAAgB;AAChB,MAAM,CAAC,MAAM,+BAA+B,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC","sourcesContent":["// Do not edit.\nimport { ShaderStore } from \"../Engines/shaderStore\";\n\nconst name = \"selectionOutlinePixelShader\";\nconst shader = `var maskSamplerSampler: sampler;var maskSampler: texture_2d<f32>;var depthSamplerSampler: sampler;var depthSampler: texture_2d<f32>;varying vUV: vec2f;uniform screenSize: vec2f;uniform outlineColor: vec3f;uniform outlineThickness: f32;uniform occlusionStrength: f32;uniform occlusionThreshold: f32;\n#define CUSTOM_FRAGMENT_DEFINITIONS\n@fragment\nfn main(input: FragmentInputs)->FragmentOutputs {\n#define CUSTOM_FRAGMENT_MAIN_BEGIN\nlet texelSize: vec2f=1.0/uniforms.screenSize;let sampleOffset: vec2f=texelSize*uniforms.outlineThickness;\n#if defined(OUTLINELAYER_SAMPLING_TRIDIRECTIONAL) || defined(OUTLINELAYER_SAMPLING_OCTADIRECTIONAL)\nlet maskTopCenter: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV+vec2f(0.0,sampleOffset.y),0.0).rg;let maskTopRight: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV+sampleOffset,0.0).rg;let maskMiddleCenter: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV,0.0).rg;let maskMiddleRight: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV+vec2f(sampleOffset.x,0.0),0.0).rg;\n#endif\n#if defined(OUTLINELAYER_SAMPLING_OCTADIRECTIONAL)\nlet maskTopLeft: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV+vec2f(-sampleOffset.x,sampleOffset.y),0.0).rg;let maskMiddleLeft: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV+vec2f(-sampleOffset.x,0.0),0.0).rg;let maskBottomRight: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV+vec2f(sampleOffset.x,-sampleOffset.y),0.0).rg;let maskBottomCenter: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV+vec2f(0.0,-sampleOffset.y),0.0).rg;let maskBottomLeft: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV+vec2f(-sampleOffset.x,-sampleOffset.y),0.0).rg;\n#endif\n#ifdef OUTLINELAYER_SAMPLING_TRIDIRECTIONAL\nlet gradient: vec3f=vec3f(\nmaskMiddleCenter.r-maskMiddleRight.r,\nmaskMiddleCenter.r-maskTopCenter.r,\nmaskMiddleCenter.r-maskTopRight.r\n);let edgeStrength: f32=length(gradient);\n#elif defined(OUTLINELAYER_SAMPLING_OCTADIRECTIONAL)\nlet gradientX: f32 =\n(maskTopLeft.r+2.0*maskMiddleLeft.r+maskBottomLeft.r) -\n(maskTopRight.r+2.0*maskMiddleRight.r+maskBottomRight.r);let gradientY: f32 =\n(maskBottomLeft.r+2.0*maskBottomCenter.r+maskBottomRight.r) -\n(maskTopLeft.r+2.0*maskTopCenter.r+maskTopRight.r);let edgeStrength: f32=length(vec2f(gradientX,gradientY));\n#endif\nlet outlineMask: f32=step(0.5,edgeStrength); \n#if defined(OUTLINELAYER_SAMPLING_TRIDIRECTIONAL) || defined(OUTLINELAYER_SAMPLING_OCTADIRECTIONAL)\nlet depthTopCenter: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV+vec2f(0.0,sampleOffset.y),0.0).r;let depthTopRight: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV+sampleOffset,0.0).r;let depthMiddleCenter: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV,0.0).r;let depthMiddleRight: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV+vec2f(sampleOffset.x,0.0),0.0).r;\n#endif\n#if defined(OUTLINELAYER_SAMPLING_OCTADIRECTIONAL)\nlet depthTopLeft: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV+vec2f(-sampleOffset.x,sampleOffset.y),0.0).r;let depthMiddleLeft: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV+vec2f(-sampleOffset.x,0.0),0.0).r;let depthBottomRight: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV+vec2f(sampleOffset.x,-sampleOffset.y),0.0).r;let depthBottomCenter: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV+vec2f(0.0,-sampleOffset.y),0.0).r;let depthBottomLeft: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV+vec2f(-sampleOffset.x,-sampleOffset.y),0.0).r;\n#endif\n#if defined(OUTLINELAYER_SAMPLING_TRIDIRECTIONAL) || defined(OUTLINELAYER_SAMPLING_OCTADIRECTIONAL)\nlet occlusionTopCenter: f32=step(uniforms.occlusionThreshold,abs(maskTopCenter.g-depthTopCenter));let occlusionTopRight: f32=step(uniforms.occlusionThreshold,abs(maskTopRight.g-depthTopRight));let occlusionMiddleCenter: f32=step(uniforms.occlusionThreshold,abs(maskMiddleCenter.g-depthMiddleCenter));let occlusionMiddleRight: f32=step(uniforms.occlusionThreshold,abs(maskMiddleRight.g-depthMiddleRight));\n#endif\n#if defined(OUTLINELAYER_SAMPLING_OCTADIRECTIONAL)\nlet occlusionTopLeft: f32=step(uniforms.occlusionThreshold,abs(maskTopLeft.g-depthTopLeft));let occlusionMiddleLeft: f32=step(uniforms.occlusionThreshold,abs(maskMiddleLeft.g-depthMiddleLeft));let occlusionBottomRight: f32=step(uniforms.occlusionThreshold,abs(maskBottomRight.g-depthBottomRight));let occlusionBottomCenter: f32=step(uniforms.occlusionThreshold,abs(maskBottomCenter.g-depthBottomCenter));let occlusionBottomLeft: f32=step(uniforms.occlusionThreshold,abs(maskBottomLeft.g-depthBottomLeft));\n#endif\nvar occlusionFactor: f32=occlusionMiddleCenter;\n#ifdef OUTLINELAYER_SAMPLING_TRIDIRECTIONAL\nocclusionFactor=min(occlusionFactor,occlusionTopCenter);occlusionFactor=min(occlusionFactor,occlusionTopRight);occlusionFactor=min(occlusionFactor,occlusionMiddleRight);\n#elif defined(OUTLINELAYER_SAMPLING_OCTADIRECTIONAL)\nocclusionFactor=min(occlusionFactor,occlusionTopCenter);occlusionFactor=min(occlusionFactor,occlusionTopRight);occlusionFactor=min(occlusionFactor,occlusionTopLeft);occlusionFactor=min(occlusionFactor,occlusionMiddleRight);occlusionFactor=min(occlusionFactor,occlusionMiddleLeft);occlusionFactor=min(occlusionFactor,occlusionBottomRight);occlusionFactor=min(occlusionFactor,occlusionBottomCenter);occlusionFactor=min(occlusionFactor,occlusionBottomLeft);\n#endif\nlet finalOutlineMask: f32=outlineMask*(1.0-uniforms.occlusionStrength*occlusionFactor);fragmentOutputs.color=vec4f(uniforms.outlineColor,finalOutlineMask);\n#define CUSTOM_FRAGMENT_MAIN_END\n}\n`;\n// Sideeffect\nif (!ShaderStore.ShadersStoreWGSL[name]) {\n ShaderStore.ShadersStoreWGSL[name] = shader;\n}\n\n/** @internal */\nexport const selectionOutlinePixelShaderWGSL = { name, shader };\n"]}
1
+ {"version":3,"file":"selectionOutline.fragment.js","sourceRoot":"","sources":["../../../../dev/core/src/ShadersWGSL/selectionOutline.fragment.ts"],"names":[],"mappings":"AAAA,eAAe;AACf,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAErD,MAAM,IAAI,GAAG,6BAA6B,CAAC;AAC3C,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2Dd,CAAC;AACF,aAAa;AACb,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;IACtC,WAAW,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC;AAChD,CAAC;AAED,gBAAgB;AAChB,MAAM,CAAC,MAAM,+BAA+B,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC","sourcesContent":["// Do not edit.\nimport { ShaderStore } from \"../Engines/shaderStore\";\n\nconst name = \"selectionOutlinePixelShader\";\nconst shader = `var maskSamplerSampler: sampler;var maskSampler: texture_2d<f32>;\n#ifdef OUTLINELAYER_DEPTH_OCCLUSION\nvar depthSamplerSampler: sampler;var depthSampler: texture_2d<f32>;\n#endif\nvarying vUV: vec2f;uniform screenSize: vec2f;uniform outlineColor: vec3f;uniform outlineThickness: f32;\n#ifdef OUTLINELAYER_DEPTH_OCCLUSION\nuniform occlusionStrength: f32;uniform occlusionThreshold: f32;\n#endif\n#define CUSTOM_FRAGMENT_DEFINITIONS\n@fragment\nfn main(input: FragmentInputs)->FragmentOutputs {\n#define CUSTOM_FRAGMENT_MAIN_BEGIN\nlet texelSize: vec2f=1.0/uniforms.screenSize;let sampleOffset: vec2f=texelSize*uniforms.outlineThickness;\n#if defined(OUTLINELAYER_SAMPLING_TRIDIRECTIONAL) || defined(OUTLINELAYER_SAMPLING_OCTADIRECTIONAL)\nlet maskTopCenter: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV+vec2f(0.0,sampleOffset.y),0.0).rg;let maskTopRight: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV+sampleOffset,0.0).rg;let maskMiddleCenter: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV,0.0).rg;let maskMiddleRight: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV+vec2f(sampleOffset.x,0.0),0.0).rg;\n#endif\n#if defined(OUTLINELAYER_SAMPLING_OCTADIRECTIONAL)\nlet maskTopLeft: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV+vec2f(-sampleOffset.x,sampleOffset.y),0.0).rg;let maskMiddleLeft: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV+vec2f(-sampleOffset.x,0.0),0.0).rg;let maskBottomRight: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV+vec2f(sampleOffset.x,-sampleOffset.y),0.0).rg;let maskBottomCenter: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV+vec2f(0.0,-sampleOffset.y),0.0).rg;let maskBottomLeft: vec2f=textureSampleLevel(maskSampler,maskSamplerSampler,fragmentInputs.vUV+vec2f(-sampleOffset.x,-sampleOffset.y),0.0).rg;\n#endif\n#ifdef OUTLINELAYER_SAMPLING_TRIDIRECTIONAL\nlet gradient: vec3f=vec3f(\nmaskMiddleCenter.r-maskMiddleRight.r,\nmaskMiddleCenter.r-maskTopCenter.r,\nmaskMiddleCenter.r-maskTopRight.r\n);let edgeStrength: f32=length(gradient);\n#elif defined(OUTLINELAYER_SAMPLING_OCTADIRECTIONAL)\nlet gradientX: f32 =\n(maskTopLeft.r+2.0*maskMiddleLeft.r+maskBottomLeft.r) -\n(maskTopRight.r+2.0*maskMiddleRight.r+maskBottomRight.r);let gradientY: f32 =\n(maskBottomLeft.r+2.0*maskBottomCenter.r+maskBottomRight.r) -\n(maskTopLeft.r+2.0*maskTopCenter.r+maskTopRight.r);let edgeStrength: f32=length(vec2f(gradientX,gradientY));\n#endif\nlet outlineMask: f32=step(0.5,edgeStrength); \nvar finalOutlineMask: f32=outlineMask;\n#ifdef OUTLINELAYER_DEPTH_OCCLUSION\nif (outlineMask>0.0) {\n#if defined(OUTLINELAYER_SAMPLING_TRIDIRECTIONAL) || defined(OUTLINELAYER_SAMPLING_OCTADIRECTIONAL)\nlet depthTopCenter: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV+vec2f(0.0,sampleOffset.y),0.0).r;let depthTopRight: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV+sampleOffset,0.0).r;let depthMiddleCenter: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV,0.0).r;let depthMiddleRight: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV+vec2f(sampleOffset.x,0.0),0.0).r;\n#endif\n#if defined(OUTLINELAYER_SAMPLING_OCTADIRECTIONAL)\nlet depthTopLeft: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV+vec2f(-sampleOffset.x,sampleOffset.y),0.0).r;let depthMiddleLeft: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV+vec2f(-sampleOffset.x,0.0),0.0).r;let depthBottomRight: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV+vec2f(sampleOffset.x,-sampleOffset.y),0.0).r;let depthBottomCenter: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV+vec2f(0.0,-sampleOffset.y),0.0).r;let depthBottomLeft: f32=textureSampleLevel(depthSampler,depthSamplerSampler,fragmentInputs.vUV+vec2f(-sampleOffset.x,-sampleOffset.y),0.0).r;\n#endif\n#if defined(OUTLINELAYER_SAMPLING_TRIDIRECTIONAL) || defined(OUTLINELAYER_SAMPLING_OCTADIRECTIONAL)\nlet occlusionTopCenter: f32=step(uniforms.occlusionThreshold,abs(maskTopCenter.g-depthTopCenter));let occlusionTopRight: f32=step(uniforms.occlusionThreshold,abs(maskTopRight.g-depthTopRight));let occlusionMiddleCenter: f32=step(uniforms.occlusionThreshold,abs(maskMiddleCenter.g-depthMiddleCenter));let occlusionMiddleRight: f32=step(uniforms.occlusionThreshold,abs(maskMiddleRight.g-depthMiddleRight));\n#endif\n#if defined(OUTLINELAYER_SAMPLING_OCTADIRECTIONAL)\nlet occlusionTopLeft: f32=step(uniforms.occlusionThreshold,abs(maskTopLeft.g-depthTopLeft));let occlusionMiddleLeft: f32=step(uniforms.occlusionThreshold,abs(maskMiddleLeft.g-depthMiddleLeft));let occlusionBottomRight: f32=step(uniforms.occlusionThreshold,abs(maskBottomRight.g-depthBottomRight));let occlusionBottomCenter: f32=step(uniforms.occlusionThreshold,abs(maskBottomCenter.g-depthBottomCenter));let occlusionBottomLeft: f32=step(uniforms.occlusionThreshold,abs(maskBottomLeft.g-depthBottomLeft));\n#endif\nvar occlusionFactor: f32=occlusionMiddleCenter;\n#ifdef OUTLINELAYER_SAMPLING_TRIDIRECTIONAL\nocclusionFactor=min(occlusionFactor,occlusionTopCenter);occlusionFactor=min(occlusionFactor,occlusionTopRight);occlusionFactor=min(occlusionFactor,occlusionMiddleRight);\n#elif defined(OUTLINELAYER_SAMPLING_OCTADIRECTIONAL)\nocclusionFactor=min(occlusionFactor,occlusionTopCenter);occlusionFactor=min(occlusionFactor,occlusionTopRight);occlusionFactor=min(occlusionFactor,occlusionTopLeft);occlusionFactor=min(occlusionFactor,occlusionMiddleRight);occlusionFactor=min(occlusionFactor,occlusionMiddleLeft);occlusionFactor=min(occlusionFactor,occlusionBottomRight);occlusionFactor=min(occlusionFactor,occlusionBottomCenter);occlusionFactor=min(occlusionFactor,occlusionBottomLeft);\n#endif\nfinalOutlineMask*=1.0-uniforms.occlusionStrength*occlusionFactor;}\n#endif\nfragmentOutputs.color=vec4f(uniforms.outlineColor,finalOutlineMask);\n#define CUSTOM_FRAGMENT_MAIN_END\n}\n`;\n// Sideeffect\nif (!ShaderStore.ShadersStoreWGSL[name]) {\n ShaderStore.ShadersStoreWGSL[name] = shader;\n}\n\n/** @internal */\nexport const selectionOutlinePixelShaderWGSL = { name, shader };\n"]}
@@ -487,6 +487,8 @@ export declare class WebXRTrackedBody implements IDisposable {
487
487
  private _mappedChildBones;
488
488
  /** Bind-space local child direction for each mapped bone. */
489
489
  private _bindLocalAimDirections;
490
+ /** Bind-space local hand-plane normal used to correct wrist/hand twist from tracked finger positions. */
491
+ private _bindLocalTwistNormals;
490
492
  /**
491
493
  * XR joint index to aim each mapped bone at. This can be a mapped joint
492
494
  * (same as `_boneToJointIdx.get(aimChildBone)`) or an **unmapped** XR
@@ -496,6 +498,8 @@ export declare class WebXRTrackedBody implements IDisposable {
496
498
  * "where the hand is pointing".
497
499
  */
498
500
  private _boneAimTargetJointIdx;
501
+ /** Per-bone pair of tracked joints that define the hand plane used for twist correction. */
502
+ private _boneTwistReferenceJointIdx;
499
503
  /**
500
504
  * Per-mapped-bone bind-pose world rotation in mesh-local space
501
505
  * (decomposed from `bone.getFinalMatrix()` at bind time). Used by the
@@ -541,6 +545,15 @@ export declare class WebXRTrackedBody implements IDisposable {
541
545
  private _tempParentAccumRot;
542
546
  /** Scratch quaternion reused for the parent-world intermediate product. */
543
547
  private _tempParentAccumTmp;
548
+ /** Scratch vectors reused by hand twist correction. */
549
+ private _tempTwistFirst;
550
+ private _tempTwistSecond;
551
+ private _tempTwistNormal;
552
+ private _tempCurrentTwistNormal;
553
+ private _tempProjectedTwistNormal;
554
+ private _tempProjectedDesiredTwistNormal;
555
+ private _tempTwistAimAxis;
556
+ private _tempTwistCross;
544
557
  /** The skeleton reference for iterating bones in parent-first order. */
545
558
  private _skeleton;
546
559
  /** Cached inverse of the skeleton mesh's world matrix. */
@@ -718,6 +731,9 @@ export declare class WebXRTrackedBody implements IDisposable {
718
731
  * direct-retarget path (or re-triggering auto-capture on the next frame).
719
732
  */
720
733
  clearTrackedBind(): void;
734
+ private _computeNormalFromJointPositions;
735
+ private _projectOnPlaneToRef;
736
+ private _storeBindLocalTwistNormalFromPositions;
721
737
  /** Internal: copy current desiredFinals into the tracked-bind slots. */
722
738
  private _captureTrackedBindFromDesiredFinals;
723
739
  /**
@@ -584,6 +584,16 @@ export const MixamoAimChildOverrides = {
584
584
  ["left-hand-wrist" /* WebXRBodyJoint.LEFT_HAND_WRIST */]: "left-hand-middle-metacarpal" /* WebXRBodyJoint.LEFT_HAND_MIDDLE_METACARPAL */,
585
585
  ["right-hand-wrist" /* WebXRBodyJoint.RIGHT_HAND_WRIST */]: "right-hand-middle-metacarpal" /* WebXRBodyJoint.RIGHT_HAND_MIDDLE_METACARPAL */,
586
586
  };
587
+ const HandTwistReferenceJoints = {
588
+ ["left-hand-wrist" /* WebXRBodyJoint.LEFT_HAND_WRIST */]: {
589
+ first: "left-hand-index-metacarpal" /* WebXRBodyJoint.LEFT_HAND_INDEX_METACARPAL */,
590
+ second: "left-hand-little-metacarpal" /* WebXRBodyJoint.LEFT_HAND_LITTLE_METACARPAL */,
591
+ },
592
+ ["right-hand-wrist" /* WebXRBodyJoint.RIGHT_HAND_WRIST */]: {
593
+ first: "right-hand-index-metacarpal" /* WebXRBodyJoint.RIGHT_HAND_INDEX_METACARPAL */,
594
+ second: "right-hand-little-metacarpal" /* WebXRBodyJoint.RIGHT_HAND_LITTLE_METACARPAL */,
595
+ },
596
+ };
587
597
  /**
588
598
  * Resolve the Mixamo rig mapping for a given body mesh, auto-detecting the
589
599
  * `mixamorig:` bone-name prefix. Falls back to the unprefixed names.
@@ -784,6 +794,8 @@ export class WebXRTrackedBody {
784
794
  this._mappedChildBones = new Map();
785
795
  /** Bind-space local child direction for each mapped bone. */
786
796
  this._bindLocalAimDirections = new Map();
797
+ /** Bind-space local hand-plane normal used to correct wrist/hand twist from tracked finger positions. */
798
+ this._bindLocalTwistNormals = new Map();
787
799
  /**
788
800
  * XR joint index to aim each mapped bone at. This can be a mapped joint
789
801
  * (same as `_boneToJointIdx.get(aimChildBone)`) or an **unmapped** XR
@@ -793,6 +805,8 @@ export class WebXRTrackedBody {
793
805
  * "where the hand is pointing".
794
806
  */
795
807
  this._boneAimTargetJointIdx = new Map();
808
+ /** Per-bone pair of tracked joints that define the hand plane used for twist correction. */
809
+ this._boneTwistReferenceJointIdx = new Map();
796
810
  /**
797
811
  * Per-mapped-bone bind-pose world rotation in mesh-local space
798
812
  * (decomposed from `bone.getFinalMatrix()` at bind time). Used by the
@@ -838,6 +852,15 @@ export class WebXRTrackedBody {
838
852
  this._tempParentAccumRot = new Quaternion();
839
853
  /** Scratch quaternion reused for the parent-world intermediate product. */
840
854
  this._tempParentAccumTmp = new Quaternion();
855
+ /** Scratch vectors reused by hand twist correction. */
856
+ this._tempTwistFirst = new Vector3();
857
+ this._tempTwistSecond = new Vector3();
858
+ this._tempTwistNormal = new Vector3();
859
+ this._tempCurrentTwistNormal = new Vector3();
860
+ this._tempProjectedTwistNormal = new Vector3();
861
+ this._tempProjectedDesiredTwistNormal = new Vector3();
862
+ this._tempTwistAimAxis = new Vector3();
863
+ this._tempTwistCross = new Vector3();
841
864
  /** The skeleton reference for iterating bones in parent-first order. */
842
865
  this._skeleton = null;
843
866
  /** Cached inverse of the skeleton mesh's world matrix. */
@@ -940,7 +963,9 @@ export class WebXRTrackedBody {
940
963
  this._mappedBoneBindLocals.clear();
941
964
  this._mappedChildBones.clear();
942
965
  this._bindLocalAimDirections.clear();
966
+ this._bindLocalTwistNormals.clear();
943
967
  this._boneAimTargetJointIdx.clear();
968
+ this._boneTwistReferenceJointIdx.clear();
944
969
  this._bindBoneWorldRotMeshLocal.clear();
945
970
  this._computedBoneNewWorldRot.clear();
946
971
  this._computedBoneNewWorldRotFrameId.clear();
@@ -1023,6 +1048,34 @@ export class WebXRTrackedBody {
1023
1048
  this._bindBoneWorldRotMeshLocal.set(bone, this._tempRotQuat.clone());
1024
1049
  }
1025
1050
  }
1051
+ const findBestUnmappedDescendantForJoint = (bone, targetJointName) => {
1052
+ const tokens = targetJointName
1053
+ .toLowerCase()
1054
+ .split(/[_\-\s]+/)
1055
+ .filter((t) => t.length >= 4 && t !== "left" && t !== "right" && t !== "hand" && t !== "foot" && t !== "joint" && t !== "body");
1056
+ let bestDescendant = null;
1057
+ let bestScore = 0;
1058
+ const walk = (b) => {
1059
+ for (const child of b.children) {
1060
+ if (!this._boneToJointIdx.has(child)) {
1061
+ const lname = child.name.toLowerCase();
1062
+ let score = 0;
1063
+ for (const t of tokens) {
1064
+ if (lname.indexOf(t) !== -1) {
1065
+ score++;
1066
+ }
1067
+ }
1068
+ if (score > bestScore) {
1069
+ bestScore = score;
1070
+ bestDescendant = child;
1071
+ }
1072
+ walk(child);
1073
+ }
1074
+ }
1075
+ };
1076
+ walk(bone);
1077
+ return bestScore > 0 ? bestDescendant : null;
1078
+ };
1026
1079
  for (const bone of Array.from(this._boneToJointIdx.keys())) {
1027
1080
  // Prefer an explicit override (XR-joint → XR-joint) when provided.
1028
1081
  const selfJointIdx = this._boneToJointIdx.get(bone);
@@ -1074,37 +1127,16 @@ export class WebXRTrackedBody {
1074
1127
  // offset).
1075
1128
  if (overrideTargetJointIdx !== -1 && overrideTargetJointName) {
1076
1129
  this._boneAimTargetJointIdx.set(bone, overrideTargetJointIdx);
1077
- // Extract significant tokens from target joint name.
1078
- // "LEFT_HAND_MIDDLE_METACARPAL" → ["middle", "metacarpal"]
1079
- const tokens = overrideTargetJointName
1080
- .toLowerCase()
1081
- .split(/[_\-\s]+/)
1082
- .filter((t) => t.length >= 4 && t !== "left" && t !== "right" && t !== "hand" && t !== "foot" && t !== "joint" && t !== "body");
1083
- // Find best-matching descendant bone: most tokens matched,
1084
- // prefer shorter names (closer to the target).
1085
- let bestDescendant = null;
1086
- let bestScore = 0;
1087
- const walk = (b) => {
1088
- for (const child of b.children) {
1089
- if (!this._boneToJointIdx.has(child)) {
1090
- const lname = child.name.toLowerCase();
1091
- let score = 0;
1092
- for (const t of tokens) {
1093
- if (lname.indexOf(t) !== -1) {
1094
- score++;
1095
- }
1096
- }
1097
- if (score > bestScore) {
1098
- bestScore = score;
1099
- bestDescendant = child;
1100
- }
1101
- walk(child);
1102
- }
1130
+ const twistReferences = HandTwistReferenceJoints[selfJointName];
1131
+ if (twistReferences) {
1132
+ const firstIdx = BodyJointNameToIndex.get(twistReferences.first);
1133
+ const secondIdx = BodyJointNameToIndex.get(twistReferences.second);
1134
+ if (firstIdx !== undefined && secondIdx !== undefined) {
1135
+ this._boneTwistReferenceJointIdx.set(bone, { first: firstIdx, second: secondIdx });
1103
1136
  }
1104
- };
1105
- walk(bone);
1106
- if (bestDescendant && bestScore > 0) {
1107
- const descendant = bestDescendant;
1137
+ }
1138
+ const descendant = findBestUnmappedDescendantForJoint(bone, overrideTargetJointName);
1139
+ if (descendant) {
1108
1140
  const boneBindPos = bindWorldPositions.get(bone);
1109
1141
  // Walk up: descendant's bind world position isn't in
1110
1142
  // bindWorldPositions (only mapped bones were added).
@@ -1124,6 +1156,18 @@ export class WebXRTrackedBody {
1124
1156
  }
1125
1157
  }
1126
1158
  }
1159
+ if (twistReferences && boneBindPos && descBindFinal && bindWorldRotation) {
1160
+ const firstDescendant = findBestUnmappedDescendantForJoint(bone, twistReferences.first);
1161
+ const secondDescendant = findBestUnmappedDescendantForJoint(bone, twistReferences.second);
1162
+ const firstBindFinal = firstDescendant ? bindPoseFinals.get(firstDescendant) : null;
1163
+ const secondBindFinal = secondDescendant ? bindPoseFinals.get(secondDescendant) : null;
1164
+ if (firstBindFinal && secondBindFinal) {
1165
+ descBindFinal.decompose(undefined, undefined, this._tempPosVec);
1166
+ firstBindFinal.decompose(undefined, undefined, this._tempTwistFirst);
1167
+ secondBindFinal.decompose(undefined, undefined, this._tempTwistSecond);
1168
+ this._storeBindLocalTwistNormalFromPositions(bone, boneBindPos, this._tempPosVec, this._tempTwistFirst, this._tempTwistSecond, bindWorldRotation);
1169
+ }
1170
+ }
1127
1171
  }
1128
1172
  // If we couldn't find a descendant, bind aim will be
1129
1173
  // computed from tracked-bind positions later (fallback).
@@ -1432,6 +1476,18 @@ export class WebXRTrackedBody {
1432
1476
  this._tempJointMatrix.multiplyToRef(this._meshWorldMatrixInverse, this._tempLocalMatrix);
1433
1477
  this._tempLocalMatrix.decompose(undefined, undefined, this._desiredFinalPositions[targetIdx]);
1434
1478
  });
1479
+ this._boneTwistReferenceJointIdx.forEach((twistReferences) => {
1480
+ if (!this._jointHasBone[twistReferences.first]) {
1481
+ Matrix.FromArrayToRef(this._jointTransformMatrices, twistReferences.first * 16, this._tempJointMatrix);
1482
+ this._tempJointMatrix.multiplyToRef(this._meshWorldMatrixInverse, this._tempLocalMatrix);
1483
+ this._tempLocalMatrix.decompose(undefined, undefined, this._desiredFinalPositions[twistReferences.first]);
1484
+ }
1485
+ if (!this._jointHasBone[twistReferences.second]) {
1486
+ Matrix.FromArrayToRef(this._jointTransformMatrices, twistReferences.second * 16, this._tempJointMatrix);
1487
+ this._tempJointMatrix.multiplyToRef(this._meshWorldMatrixInverse, this._tempLocalMatrix);
1488
+ this._tempLocalMatrix.decompose(undefined, undefined, this._desiredFinalPositions[twistReferences.second]);
1489
+ }
1490
+ });
1435
1491
  // Auto-capture bind on first frame when enabled.
1436
1492
  if (!this._hasTrackedBind && this.autoCaptureBindOnFirstFrame) {
1437
1493
  this._captureTrackedBindFromDesiredFinals();
@@ -1513,6 +1569,45 @@ export class WebXRTrackedBody {
1513
1569
  clearTrackedBind() {
1514
1570
  this._hasTrackedBind = false;
1515
1571
  }
1572
+ _computeNormalFromJointPositions(origin, aimTarget, first, second, result, aimAxisResult) {
1573
+ aimTarget.subtractToRef(origin, aimAxisResult);
1574
+ if (aimAxisResult.lengthSquared() < 1e-8) {
1575
+ return false;
1576
+ }
1577
+ aimAxisResult.normalize();
1578
+ first.subtractToRef(second, this._tempTwistCross);
1579
+ if (this._tempTwistCross.lengthSquared() < 1e-8) {
1580
+ return false;
1581
+ }
1582
+ this._tempTwistCross.normalize();
1583
+ Vector3.CrossToRef(aimAxisResult, this._tempTwistCross, result);
1584
+ if (result.lengthSquared() < 1e-8) {
1585
+ return false;
1586
+ }
1587
+ result.normalize();
1588
+ return true;
1589
+ }
1590
+ _projectOnPlaneToRef(vector, planeNormal, result) {
1591
+ const dot = Vector3.Dot(vector, planeNormal);
1592
+ result.copyFrom(planeNormal).scaleInPlace(-dot).addInPlace(vector);
1593
+ if (result.lengthSquared() < 1e-8) {
1594
+ return false;
1595
+ }
1596
+ result.normalize();
1597
+ return true;
1598
+ }
1599
+ _storeBindLocalTwistNormalFromPositions(bone, origin, aimTarget, first, second, bindWorldRotation) {
1600
+ if (!this._computeNormalFromJointPositions(origin, aimTarget, first, second, this._tempTwistNormal, this._tempTwistAimAxis)) {
1601
+ return;
1602
+ }
1603
+ Quaternion.InverseToRef(bindWorldRotation, this._tempRotQuat2);
1604
+ this._tempTwistNormal.rotateByQuaternionToRef(this._tempRotQuat2, this._tempLocalDirection);
1605
+ if (this._tempLocalDirection.lengthSquared() < 1e-8) {
1606
+ return;
1607
+ }
1608
+ this._tempLocalDirection.normalize();
1609
+ this._bindLocalTwistNormals.set(bone, this._tempLocalDirection.clone());
1610
+ }
1516
1611
  /** Internal: copy current desiredFinals into the tracked-bind slots. */
1517
1612
  _captureTrackedBindFromDesiredFinals() {
1518
1613
  if (!this._trackedBindDesiredFinalRot) {
@@ -1536,6 +1631,14 @@ export class WebXRTrackedBody {
1536
1631
  }
1537
1632
  this._trackedBindDesiredFinalPos[targetIdx].copyFrom(this._desiredFinalPositions[targetIdx]);
1538
1633
  }
1634
+ for (const twistReferences of Array.from(this._boneTwistReferenceJointIdx.values())) {
1635
+ if (!this._jointHasBone[twistReferences.first]) {
1636
+ this._trackedBindDesiredFinalPos[twistReferences.first].copyFrom(this._desiredFinalPositions[twistReferences.first]);
1637
+ }
1638
+ if (!this._jointHasBone[twistReferences.second]) {
1639
+ this._trackedBindDesiredFinalPos[twistReferences.second].copyFrom(this._desiredFinalPositions[twistReferences.second]);
1640
+ }
1641
+ }
1539
1642
  // For any bone whose aim target is UNMAPPED and therefore wasn't
1540
1643
  // resolved at setBodyMesh time, compute its bind-local aim direction
1541
1644
  // now — using tracked bind positions for both endpoints and the
@@ -1562,6 +1665,18 @@ export class WebXRTrackedBody {
1562
1665
  this._tempLocalDirection.normalize();
1563
1666
  this._bindLocalAimDirections.set(bone, this._tempLocalDirection.clone());
1564
1667
  }
1668
+ for (const [bone, twistReferences] of Array.from(this._boneTwistReferenceJointIdx)) {
1669
+ if (this._bindLocalTwistNormals.has(bone)) {
1670
+ continue;
1671
+ }
1672
+ const selfJointIdx = this._boneToJointIdx.get(bone);
1673
+ const targetIdx = this._boneAimTargetJointIdx.get(bone);
1674
+ const bindWorldRot = this._bindBoneWorldRotMeshLocal.get(bone);
1675
+ if (selfJointIdx === undefined || targetIdx === undefined || !bindWorldRot) {
1676
+ continue;
1677
+ }
1678
+ this._storeBindLocalTwistNormalFromPositions(bone, this._trackedBindDesiredFinalPos[selfJointIdx], this._trackedBindDesiredFinalPos[targetIdx], this._trackedBindDesiredFinalPos[twistReferences.first], this._trackedBindDesiredFinalPos[twistReferences.second], bindWorldRot);
1679
+ }
1565
1680
  this._hasTrackedBind = true;
1566
1681
  }
1567
1682
  /**
@@ -1644,6 +1759,28 @@ export class WebXRTrackedBody {
1644
1759
  }
1645
1760
  }
1646
1761
  }
1762
+ const twistReferences = this._boneTwistReferenceJointIdx.get(bone);
1763
+ const bindLocalTwistNormal = this._bindLocalTwistNormals.get(bone);
1764
+ if (twistReferences && bindLocalTwistNormal && targetIdx !== undefined) {
1765
+ if (this._computeNormalFromJointPositions(this._desiredFinalPositions[jointIdx], this._desiredFinalPositions[targetIdx], this._desiredFinalPositions[twistReferences.first], this._desiredFinalPositions[twistReferences.second], this._tempTwistNormal, this._tempTwistAimAxis)) {
1766
+ // Rotate bind-space twist normal by the current world orientation,
1767
+ // then rotate around the aimed axis to match the tracked hand plane.
1768
+ bindLocalTwistNormal.rotateByQuaternionToRef(this._tempBoneWorldRot, this._tempCurrentTwistNormal);
1769
+ if (this._projectOnPlaneToRef(this._tempCurrentTwistNormal, this._tempTwistAimAxis, this._tempProjectedTwistNormal) &&
1770
+ this._projectOnPlaneToRef(this._tempTwistNormal, this._tempTwistAimAxis, this._tempProjectedDesiredTwistNormal)) {
1771
+ const dot = Vector3.Dot(this._tempProjectedTwistNormal, this._tempProjectedDesiredTwistNormal);
1772
+ if (dot < -0.9999) {
1773
+ Quaternion.RotationAxisToRef(this._tempTwistAimAxis, Math.PI, this._tempRotQuat2);
1774
+ }
1775
+ else {
1776
+ Quaternion.FromUnitVectorsToRef(this._tempProjectedTwistNormal, this._tempProjectedDesiredTwistNormal, this._tempRotQuat2);
1777
+ }
1778
+ this._tempRotQuat2.multiplyToRef(this._tempBoneWorldRot, this._tempDeltaQuat);
1779
+ this._tempBoneWorldRot.copyFrom(this._tempDeltaQuat);
1780
+ this._tempBoneWorldRot.normalize();
1781
+ }
1782
+ }
1783
+ }
1647
1784
  }
1648
1785
  // Store for children's parent lookup (reuse pooled quaternion).
1649
1786
  let pooled = this._computedBoneNewWorldRot.get(bone);