@zephyr3d/scene 0.8.0 → 0.8.2
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/dist/animation/animationset.js +220 -1
- package/dist/animation/animationset.js.map +1 -1
- package/dist/animation/ik/ccd_solver.js +3 -4
- package/dist/animation/ik/ccd_solver.js.map +1 -1
- package/dist/animation/ik/fabrik_solver.js +3 -4
- package/dist/animation/ik/fabrik_solver.js.map +1 -1
- package/dist/animation/ik/ik_pole_constraint.js +7 -2
- package/dist/animation/ik/ik_pole_constraint.js.map +1 -1
- package/dist/animation/ik/ik_utils.js +0 -79
- package/dist/animation/ik/ik_utils.js.map +1 -1
- package/dist/animation/ik/two_bone_ik_solver.js +3 -4
- package/dist/animation/ik/two_bone_ik_solver.js.map +1 -1
- package/dist/animation/skeleton.js +157 -125
- package/dist/animation/skeleton.js.map +1 -1
- package/dist/animation/spring/spring_bone.js +240 -0
- package/dist/animation/spring/spring_bone.js.map +1 -0
- package/dist/animation/spring/spring_bone_collider.js +198 -0
- package/dist/animation/spring/spring_bone_collider.js.map +1 -0
- package/dist/animation/spring/spring_bone_constraint.js +170 -0
- package/dist/animation/spring/spring_bone_constraint.js.map +1 -0
- package/dist/animation/spring/spring_bone_system.js +328 -0
- package/dist/animation/spring/spring_bone_system.js.map +1 -0
- package/dist/asset/model.js +2 -2
- package/dist/index.d.ts +17 -0
- package/dist/material/mixins/pbr/common.js +20 -17
- package/dist/material/mixins/pbr/common.js.map +1 -1
- package/dist/render/gpu_picking.js +100 -0
- package/dist/render/gpu_picking.js.map +1 -0
- package/dist/render/rendergraph/device_pool_allocator.js +41 -0
- package/dist/render/rendergraph/device_pool_allocator.js.map +1 -0
- package/dist/render/rendergraph/executor.js +146 -0
- package/dist/render/rendergraph/executor.js.map +1 -0
- package/dist/render/rendergraph/forward_plus_builder.js +414 -0
- package/dist/render/rendergraph/forward_plus_builder.js.map +1 -0
- package/dist/render/rendergraph/rendergraph.js +306 -0
- package/dist/render/rendergraph/rendergraph.js.map +1 -0
- package/dist/render/rendergraph/types.js +57 -0
- package/dist/render/rendergraph/types.js.map +1 -0
- package/dist/render/sky_motion_vector.js +85 -0
- package/dist/render/sky_motion_vector.js.map +1 -0
- package/dist/utility/serialization/scene/animation.js +3 -3
- package/dist/utility/serialization/scene/node.js +1 -1
- package/package.json +2 -2
- package/dist/animation/ik/ik_chain_builder.js +0 -68
- package/dist/animation/ik/ik_chain_builder.js.map +0 -1
- package/dist/node_modules/@zephyr3d/runtime/dist/runtime/runtimemgr.js +0 -38
- package/dist/node_modules/@zephyr3d/runtime/dist/runtime/runtimemgr.js.map +0 -1
- package/dist/node_modules/@zephyr3d/runtime/dist/runtime/runtimescript.js +0 -10
- package/dist/node_modules/@zephyr3d/runtime/dist/runtime/runtimescript.js.map +0 -1
- package/dist/node_modules/@zephyr3d/runtime/dist/runtime/scriptingsystem.js +0 -127
- package/dist/node_modules/@zephyr3d/runtime/dist/runtime/scriptingsystem.js.map +0 -1
- package/dist/node_modules/@zephyr3d/runtime/dist/runtime/scriptregistry.js +0 -263
- package/dist/node_modules/@zephyr3d/runtime/dist/runtime/scriptregistry.js.map +0 -1
- package/dist/src/animation/animation.js +0 -127
- package/dist/src/animation/animation.js.map +0 -1
- package/dist/src/animation/animationset.js +0 -255
- package/dist/src/animation/animationset.js.map +0 -1
- package/dist/src/animation/animationtrack.js +0 -34
- package/dist/src/animation/animationtrack.js.map +0 -1
- package/dist/src/animation/eulerrotationtrack.js +0 -52
- package/dist/src/animation/eulerrotationtrack.js.map +0 -1
- package/dist/src/animation/morphtarget.js +0 -93
- package/dist/src/animation/morphtarget.js.map +0 -1
- package/dist/src/animation/morphtrack.js +0 -70
- package/dist/src/animation/morphtrack.js.map +0 -1
- package/dist/src/animation/proptrack.js +0 -161
- package/dist/src/animation/proptrack.js.map +0 -1
- package/dist/src/animation/rotationtrack.js +0 -51
- package/dist/src/animation/rotationtrack.js.map +0 -1
- package/dist/src/animation/scaletrack.js +0 -50
- package/dist/src/animation/scaletrack.js.map +0 -1
- package/dist/src/animation/skeleton.js +0 -204
- package/dist/src/animation/skeleton.js.map +0 -1
- package/dist/src/animation/translationtrack.js +0 -50
- package/dist/src/animation/translationtrack.js.map +0 -1
- package/dist/src/app/app.js +0 -129
- package/dist/src/app/app.js.map +0 -1
- package/dist/src/app/inputmgr.js +0 -267
- package/dist/src/app/inputmgr.js.map +0 -1
- package/dist/src/asset/assetmanager.js +0 -404
- package/dist/src/asset/assetmanager.js.map +0 -1
- package/dist/src/asset/builtin.js +0 -337
- package/dist/src/asset/builtin.js.map +0 -1
- package/dist/src/asset/loaders/dds/dds.js +0 -470
- package/dist/src/asset/loaders/dds/dds.js.map +0 -1
- package/dist/src/asset/loaders/dds/dds_loader.js +0 -28
- package/dist/src/asset/loaders/dds/dds_loader.js.map +0 -1
- package/dist/src/asset/loaders/gltf/gltf_loader.js +0 -1265
- package/dist/src/asset/loaders/gltf/gltf_loader.js.map +0 -1
- package/dist/src/asset/loaders/gltf/helpers.js +0 -327
- package/dist/src/asset/loaders/gltf/helpers.js.map +0 -1
- package/dist/src/asset/loaders/hdr/hdr.js +0 -180
- package/dist/src/asset/loaders/hdr/hdr.js.map +0 -1
- package/dist/src/asset/loaders/image/tga_Loader.js +0 -116
- package/dist/src/asset/loaders/image/tga_Loader.js.map +0 -1
- package/dist/src/asset/loaders/image/webimage_loader.js +0 -63
- package/dist/src/asset/loaders/image/webimage_loader.js.map +0 -1
- package/dist/src/asset/loaders/loader.js +0 -45
- package/dist/src/asset/loaders/loader.js.map +0 -1
- package/dist/src/asset/model.js +0 -414
- package/dist/src/asset/model.js.map +0 -1
- package/dist/src/blitter/bilateralblur.js +0 -221
- package/dist/src/blitter/bilateralblur.js.map +0 -1
- package/dist/src/blitter/blitter.js +0 -390
- package/dist/src/blitter/blitter.js.map +0 -1
- package/dist/src/blitter/box.js +0 -118
- package/dist/src/blitter/box.js.map +0 -1
- package/dist/src/blitter/copy.js +0 -22
- package/dist/src/blitter/copy.js.map +0 -1
- package/dist/src/blitter/gaussianblur.js +0 -228
- package/dist/src/blitter/gaussianblur.js.map +0 -1
- package/dist/src/camera/base.js +0 -92
- package/dist/src/camera/base.js.map +0 -1
- package/dist/src/camera/camera.js +0 -1005
- package/dist/src/camera/camera.js.map +0 -1
- package/dist/src/camera/fps.js +0 -238
- package/dist/src/camera/fps.js.map +0 -1
- package/dist/src/camera/orbit.js +0 -245
- package/dist/src/camera/orbit.js.map +0 -1
- package/dist/src/camera/orthocamera.js +0 -167
- package/dist/src/camera/orthocamera.js.map +0 -1
- package/dist/src/camera/perspectivecamera.js +0 -141
- package/dist/src/camera/perspectivecamera.js.map +0 -1
- package/dist/src/index.js +0 -120
- package/dist/src/index.js.map +0 -1
- package/dist/src/material/blinn.js +0 -81
- package/dist/src/material/blinn.js.map +0 -1
- package/dist/src/material/grassmaterial.js +0 -113
- package/dist/src/material/grassmaterial.js.map +0 -1
- package/dist/src/material/lambert.js +0 -92
- package/dist/src/material/lambert.js.map +0 -1
- package/dist/src/material/material.js +0 -301
- package/dist/src/material/material.js.map +0 -1
- package/dist/src/material/meshmaterial.js +0 -704
- package/dist/src/material/meshmaterial.js.map +0 -1
- package/dist/src/material/mixins/albedocolor.js +0 -76
- package/dist/src/material/mixins/albedocolor.js.map +0 -1
- package/dist/src/material/mixins/foliage.js +0 -47
- package/dist/src/material/mixins/foliage.js.map +0 -1
- package/dist/src/material/mixins/lightmodel/blinnphong.js +0 -112
- package/dist/src/material/mixins/lightmodel/blinnphong.js.map +0 -1
- package/dist/src/material/mixins/lightmodel/lambert.js +0 -58
- package/dist/src/material/mixins/lightmodel/lambert.js.map +0 -1
- package/dist/src/material/mixins/lightmodel/pbrmetallicroughness.js +0 -178
- package/dist/src/material/mixins/lightmodel/pbrmetallicroughness.js.map +0 -1
- package/dist/src/material/mixins/lightmodel/pbrspecularglossness.js +0 -139
- package/dist/src/material/mixins/lightmodel/pbrspecularglossness.js.map +0 -1
- package/dist/src/material/mixins/lit.js +0 -476
- package/dist/src/material/mixins/lit.js.map +0 -1
- package/dist/src/material/mixins/pbr/common.js +0 -918
- package/dist/src/material/mixins/pbr/common.js.map +0 -1
- package/dist/src/material/mixins/texture.js +0 -172
- package/dist/src/material/mixins/texture.js.map +0 -1
- package/dist/src/material/mixins/vertexcolor.js +0 -56
- package/dist/src/material/mixins/vertexcolor.js.map +0 -1
- package/dist/src/material/particle.js +0 -178
- package/dist/src/material/particle.js.map +0 -1
- package/dist/src/material/pbrmr.js +0 -97
- package/dist/src/material/pbrmr.js.map +0 -1
- package/dist/src/material/pbrsg.js +0 -97
- package/dist/src/material/pbrsg.js.map +0 -1
- package/dist/src/material/shader/helper.js +0 -1209
- package/dist/src/material/shader/helper.js.map +0 -1
- package/dist/src/material/terrain-cm.js +0 -606
- package/dist/src/material/terrain-cm.js.map +0 -1
- package/dist/src/material/terrainmaterial.js +0 -375
- package/dist/src/material/terrainmaterial.js.map +0 -1
- package/dist/src/material/unlit.js +0 -41
- package/dist/src/material/unlit.js.map +0 -1
- package/dist/src/material/water.js +0 -417
- package/dist/src/material/water.js.map +0 -1
- package/dist/src/posteffect/bloom.js +0 -361
- package/dist/src/posteffect/bloom.js.map +0 -1
- package/dist/src/posteffect/compositor.js +0 -226
- package/dist/src/posteffect/compositor.js.map +0 -1
- package/dist/src/posteffect/fxaa.js +0 -273
- package/dist/src/posteffect/fxaa.js.map +0 -1
- package/dist/src/posteffect/grayscale.js +0 -69
- package/dist/src/posteffect/grayscale.js.map +0 -1
- package/dist/src/posteffect/motionblur.js +0 -96
- package/dist/src/posteffect/motionblur.js.map +0 -1
- package/dist/src/posteffect/posteffect.js +0 -126
- package/dist/src/posteffect/posteffect.js.map +0 -1
- package/dist/src/posteffect/sao.js +0 -324
- package/dist/src/posteffect/sao.js.map +0 -1
- package/dist/src/posteffect/ssr.js +0 -489
- package/dist/src/posteffect/ssr.js.map +0 -1
- package/dist/src/posteffect/taa.js +0 -172
- package/dist/src/posteffect/taa.js.map +0 -1
- package/dist/src/posteffect/tonemap.js +0 -94
- package/dist/src/posteffect/tonemap.js.map +0 -1
- package/dist/src/render/abuffer_oit.js +0 -361
- package/dist/src/render/abuffer_oit.js.map +0 -1
- package/dist/src/render/clipmap.js +0 -851
- package/dist/src/render/clipmap.js.map +0 -1
- package/dist/src/render/cluster_light.js +0 -333
- package/dist/src/render/cluster_light.js.map +0 -1
- package/dist/src/render/cull_visitor.js +0 -187
- package/dist/src/render/cull_visitor.js.map +0 -1
- package/dist/src/render/depthpass.js +0 -68
- package/dist/src/render/depthpass.js.map +0 -1
- package/dist/src/render/drawable_mixin.js +0 -227
- package/dist/src/render/drawable_mixin.js.map +0 -1
- package/dist/src/render/envlight.js +0 -463
- package/dist/src/render/envlight.js.map +0 -1
- package/dist/src/render/fbm_wavegenerator.js +0 -251
- package/dist/src/render/fbm_wavegenerator.js.map +0 -1
- package/dist/src/render/fft_wavegenerator.js +0 -1006
- package/dist/src/render/fft_wavegenerator.js.map +0 -1
- package/dist/src/render/fullscreenquad.js +0 -38
- package/dist/src/render/fullscreenquad.js.map +0 -1
- package/dist/src/render/gerstner_wavegenerator.js +0 -314
- package/dist/src/render/gerstner_wavegenerator.js.map +0 -1
- package/dist/src/render/globalbindgroup_allocator.js +0 -60
- package/dist/src/render/globalbindgroup_allocator.js.map +0 -1
- package/dist/src/render/hzb.js +0 -273
- package/dist/src/render/hzb.js.map +0 -1
- package/dist/src/render/lightpass.js +0 -172
- package/dist/src/render/lightpass.js.map +0 -1
- package/dist/src/render/objectcolorpass.js +0 -51
- package/dist/src/render/objectcolorpass.js.map +0 -1
- package/dist/src/render/primitive.js +0 -364
- package/dist/src/render/primitive.js.map +0 -1
- package/dist/src/render/render_queue.js +0 -467
- package/dist/src/render/render_queue.js.map +0 -1
- package/dist/src/render/renderbundle_wrapper.js +0 -152
- package/dist/src/render/renderbundle_wrapper.js.map +0 -1
- package/dist/src/render/renderer.js +0 -455
- package/dist/src/render/renderer.js.map +0 -1
- package/dist/src/render/renderpass.js +0 -200
- package/dist/src/render/renderpass.js.map +0 -1
- package/dist/src/render/shadowmap_pass.js +0 -56
- package/dist/src/render/shadowmap_pass.js.map +0 -1
- package/dist/src/render/sky.js +0 -1103
- package/dist/src/render/sky.js.map +0 -1
- package/dist/src/render/weightedblended_oit.js +0 -168
- package/dist/src/render/weightedblended_oit.js.map +0 -1
- package/dist/src/scene/batchgroup.js +0 -162
- package/dist/src/scene/batchgroup.js.map +0 -1
- package/dist/src/scene/environment.js +0 -209
- package/dist/src/scene/environment.js.map +0 -1
- package/dist/src/scene/graph_node.js +0 -72
- package/dist/src/scene/graph_node.js.map +0 -1
- package/dist/src/scene/light.js +0 -416
- package/dist/src/scene/light.js.map +0 -1
- package/dist/src/scene/mesh.js +0 -341
- package/dist/src/scene/mesh.js.map +0 -1
- package/dist/src/scene/octree.js +0 -649
- package/dist/src/scene/octree.js.map +0 -1
- package/dist/src/scene/particlesys.js +0 -738
- package/dist/src/scene/particlesys.js.map +0 -1
- package/dist/src/scene/raycast_visitor.js +0 -103
- package/dist/src/scene/raycast_visitor.js.map +0 -1
- package/dist/src/scene/scene.js +0 -284
- package/dist/src/scene/scene.js.map +0 -1
- package/dist/src/scene/scene_node.js +0 -732
- package/dist/src/scene/scene_node.js.map +0 -1
- package/dist/src/scene/terrain/grass.js +0 -278
- package/dist/src/scene/terrain/grass.js.map +0 -1
- package/dist/src/scene/terrain/heightfield.js +0 -475
- package/dist/src/scene/terrain/heightfield.js.map +0 -1
- package/dist/src/scene/terrain/patch.js +0 -530
- package/dist/src/scene/terrain/patch.js.map +0 -1
- package/dist/src/scene/terrain/quadtree.js +0 -461
- package/dist/src/scene/terrain/quadtree.js.map +0 -1
- package/dist/src/scene/terrain/terrain.js +0 -246
- package/dist/src/scene/terrain/terrain.js.map +0 -1
- package/dist/src/scene/terrain-cm/grass.js +0 -594
- package/dist/src/scene/terrain-cm/grass.js.map +0 -1
- package/dist/src/scene/terrain-cm/grassmaterial.js +0 -159
- package/dist/src/scene/terrain-cm/grassmaterial.js.map +0 -1
- package/dist/src/scene/terrain-cm/terrain-cm.js +0 -538
- package/dist/src/scene/terrain-cm/terrain-cm.js.map +0 -1
- package/dist/src/scene/water.js +0 -374
- package/dist/src/scene/water.js.map +0 -1
- package/dist/src/shaders/atmosphere.js +0 -957
- package/dist/src/shaders/atmosphere.js.map +0 -1
- package/dist/src/shaders/fog.js +0 -112
- package/dist/src/shaders/fog.js.map +0 -1
- package/dist/src/shaders/misc.js +0 -266
- package/dist/src/shaders/misc.js.map +0 -1
- package/dist/src/shaders/noise.js +0 -222
- package/dist/src/shaders/noise.js.map +0 -1
- package/dist/src/shaders/pbr.js +0 -51
- package/dist/src/shaders/pbr.js.map +0 -1
- package/dist/src/shaders/shadow.js +0 -636
- package/dist/src/shaders/shadow.js.map +0 -1
- package/dist/src/shaders/ssr.js +0 -490
- package/dist/src/shaders/ssr.js.map +0 -1
- package/dist/src/shaders/temporal.js +0 -215
- package/dist/src/shaders/temporal.js.map +0 -1
- package/dist/src/shaders/water.js +0 -756
- package/dist/src/shaders/water.js.map +0 -1
- package/dist/src/shadow/esm.js +0 -237
- package/dist/src/shadow/esm.js.map +0 -1
- package/dist/src/shadow/pcf_opt.js +0 -181
- package/dist/src/shadow/pcf_opt.js.map +0 -1
- package/dist/src/shadow/pcf_pd.js +0 -189
- package/dist/src/shadow/pcf_pd.js.map +0 -1
- package/dist/src/shadow/shader.js +0 -37
- package/dist/src/shadow/shader.js.map +0 -1
- package/dist/src/shadow/shadow_impl.js +0 -15
- package/dist/src/shadow/shadow_impl.js.map +0 -1
- package/dist/src/shadow/shadowmapper.js +0 -790
- package/dist/src/shadow/shadowmapper.js.map +0 -1
- package/dist/src/shadow/ssm.js +0 -159
- package/dist/src/shadow/ssm.js.map +0 -1
- package/dist/src/shadow/vsm.js +0 -297
- package/dist/src/shadow/vsm.js.map +0 -1
- package/dist/src/shapes/box.js +0 -386
- package/dist/src/shapes/box.js.map +0 -1
- package/dist/src/shapes/cylinder.js +0 -125
- package/dist/src/shapes/cylinder.js.map +0 -1
- package/dist/src/shapes/plane.js +0 -88
- package/dist/src/shapes/plane.js.map +0 -1
- package/dist/src/shapes/shape.js +0 -87
- package/dist/src/shapes/shape.js.map +0 -1
- package/dist/src/shapes/sphere.js +0 -114
- package/dist/src/shapes/sphere.js.map +0 -1
- package/dist/src/shapes/tetrahedron.js +0 -188
- package/dist/src/shapes/tetrahedron.js.map +0 -1
- package/dist/src/shapes/torus.js +0 -111
- package/dist/src/shapes/torus.js.map +0 -1
- package/dist/src/utility/aabbtree.js +0 -400
- package/dist/src/utility/aabbtree.js.map +0 -1
- package/dist/src/utility/bounding_volume.js +0 -29
- package/dist/src/utility/bounding_volume.js.map +0 -1
- package/dist/src/utility/debug.js +0 -28
- package/dist/src/utility/debug.js.map +0 -1
- package/dist/src/utility/draco/decoder.js +0 -116
- package/dist/src/utility/draco/decoder.js.map +0 -1
- package/dist/src/utility/misc.js +0 -105
- package/dist/src/utility/misc.js.map +0 -1
- package/dist/src/utility/panorama.js +0 -163
- package/dist/src/utility/panorama.js.map +0 -1
- package/dist/src/utility/pmrem.js +0 -354
- package/dist/src/utility/pmrem.js.map +0 -1
- package/dist/src/utility/rendermipmap.js +0 -115
- package/dist/src/utility/rendermipmap.js.map +0 -1
- package/dist/src/utility/serialization/json.js +0 -402
- package/dist/src/utility/serialization/json.js.map +0 -1
- package/dist/src/utility/serialization/manager.js +0 -623
- package/dist/src/utility/serialization/manager.js.map +0 -1
- package/dist/src/utility/serialization/scene/animation.js +0 -248
- package/dist/src/utility/serialization/scene/animation.js.map +0 -1
- package/dist/src/utility/serialization/scene/batch.js +0 -59
- package/dist/src/utility/serialization/scene/batch.js.map +0 -1
- package/dist/src/utility/serialization/scene/camera.js +0 -790
- package/dist/src/utility/serialization/scene/camera.js.map +0 -1
- package/dist/src/utility/serialization/scene/common.js +0 -222
- package/dist/src/utility/serialization/scene/common.js.map +0 -1
- package/dist/src/utility/serialization/scene/light.js +0 -575
- package/dist/src/utility/serialization/scene/light.js.map +0 -1
- package/dist/src/utility/serialization/scene/material.js +0 -1111
- package/dist/src/utility/serialization/scene/material.js.map +0 -1
- package/dist/src/utility/serialization/scene/mesh.js +0 -148
- package/dist/src/utility/serialization/scene/mesh.js.map +0 -1
- package/dist/src/utility/serialization/scene/misc.js +0 -39
- package/dist/src/utility/serialization/scene/misc.js.map +0 -1
- package/dist/src/utility/serialization/scene/node.js +0 -451
- package/dist/src/utility/serialization/scene/node.js.map +0 -1
- package/dist/src/utility/serialization/scene/particle.js +0 -425
- package/dist/src/utility/serialization/scene/particle.js.map +0 -1
- package/dist/src/utility/serialization/scene/primitive.js +0 -692
- package/dist/src/utility/serialization/scene/primitive.js.map +0 -1
- package/dist/src/utility/serialization/scene/scene.js +0 -704
- package/dist/src/utility/serialization/scene/scene.js.map +0 -1
- package/dist/src/utility/serialization/scene/terrain.js +0 -488
- package/dist/src/utility/serialization/scene/terrain.js.map +0 -1
- package/dist/src/utility/serialization/scene/water.js +0 -465
- package/dist/src/utility/serialization/scene/water.js.map +0 -1
- package/dist/src/utility/shprojector.js +0 -297
- package/dist/src/utility/shprojector.js.map +0 -1
- package/dist/src/utility/textures/ggxlut.js +0 -213
- package/dist/src/utility/textures/ggxlut.js.map +0 -1
- package/dist/src/utility/textures/gradientnoise.js +0 -62
- package/dist/src/utility/textures/gradientnoise.js.map +0 -1
- package/dist/src/utility/textures/randomnoise.js +0 -41
- package/dist/src/utility/textures/randomnoise.js.map +0 -1
- package/dist/src/values.js +0 -162
- package/dist/src/values.js.map +0 -1
- package/dist/utility/blueprint/material/common.js +0 -7
- package/dist/utility/blueprint/material/common.js.map +0 -1
- package/dist/utility/serialization/blueprint/constants.js +0 -255
- package/dist/utility/serialization/blueprint/constants.js.map +0 -1
- package/dist/utility/serialization/blueprint/material/constants.js +0 -203
- package/dist/utility/serialization/blueprint/material/constants.js.map +0 -1
- package/dist/utility/serialization/blueprint/material/texture.js +0 -165
- package/dist/utility/serialization/blueprint/material/texture.js.map +0 -1
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import { Disposable, weightedAverage } from '@zephyr3d/base';
|
|
1
|
+
import { Disposable, weightedAverage, Interpolator } from '@zephyr3d/base';
|
|
2
2
|
import { AnimationClip } from './animation.js';
|
|
3
|
+
import { NodeRotationTrack } from './rotationtrack.js';
|
|
4
|
+
import { NodeEulerRotationTrack } from './eulerrotationtrack.js';
|
|
5
|
+
import { NodeTranslationTrack } from './translationtrack.js';
|
|
6
|
+
import { NodeScaleTrack } from './scaletrack.js';
|
|
7
|
+
import { Skeleton } from './skeleton.js';
|
|
3
8
|
|
|
4
9
|
/**
|
|
5
10
|
* Animation set
|
|
@@ -327,6 +332,220 @@ import { AnimationClip } from './animation.js';
|
|
|
327
332
|
}
|
|
328
333
|
}
|
|
329
334
|
/**
|
|
335
|
+
* Copy an animation clip from another AnimationSet into this one.
|
|
336
|
+
*
|
|
337
|
+
* Prerequisites:
|
|
338
|
+
* - Both sets must reference skeletons with identical joint names and counts.
|
|
339
|
+
* - The source clip must exist in `sourceSet`.
|
|
340
|
+
*
|
|
341
|
+
* @param sourceSet - The AnimationSet to copy from.
|
|
342
|
+
* @param animationName - Name of the clip to copy.
|
|
343
|
+
* @param targetName - Name for the new clip in this set. Defaults to `animationName`.
|
|
344
|
+
* @param excludeJoint - Optional predicate; joints whose name returns true are excluded from
|
|
345
|
+
* skeleton structure matching.
|
|
346
|
+
* @returns The newly created AnimationClip, or null on failure.
|
|
347
|
+
*/ copyAnimationFrom(sourceSet, animationName, targetName, excludeJoint) {
|
|
348
|
+
const destName = targetName ?? animationName;
|
|
349
|
+
const sourceClip = sourceSet.get(animationName);
|
|
350
|
+
if (!sourceClip) {
|
|
351
|
+
console.error(`copyAnimationFrom: animation '${animationName}' not found in source set`);
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
if (this._animations[destName]) {
|
|
355
|
+
console.error(`copyAnimationFrom: animation '${destName}' already exists in target set`);
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
if (sourceClip.skeletons.size !== 1) {
|
|
359
|
+
console.error(`copyAnimationFrom: source animation clip must be affected by exactly one skeleton`);
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
const srcSkeletonId = [
|
|
363
|
+
...sourceClip.skeletons
|
|
364
|
+
][0];
|
|
365
|
+
const srcSkeleton = Skeleton.findSkeletonById(srcSkeletonId);
|
|
366
|
+
if (!srcSkeleton) {
|
|
367
|
+
console.error(`copyAnimationFrom: source skeleton '${srcSkeletonId}' not found`);
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
const nodeMap = new Map();
|
|
371
|
+
const jointRemapBySrcNode = new Map();
|
|
372
|
+
const jointsFiltered = srcSkeleton.joints.filter((j)=>!excludeJoint?.(j.name));
|
|
373
|
+
const srcRootNode = findRootJoint(jointsFiltered);
|
|
374
|
+
if (!srcRootNode) {
|
|
375
|
+
console.error(`copyAnimationFrom: cannot determine the root joint for source skeleton`);
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
// Build filtered joint lists for matching (exclude joints rejected by filterJoint)
|
|
379
|
+
const srcJointsFiltered = sortJoints(srcRootNode, jointsFiltered);
|
|
380
|
+
if (!srcJointsFiltered) {
|
|
381
|
+
console.error(`copyAnimationFrom: invalid source skeleton structure`);
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
let dstJointsFiltered = [];
|
|
385
|
+
const dstSkeleton = this._skeletons.map((ref)=>ref.get()).find((sk)=>{
|
|
386
|
+
if (!sk) {
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
const jointsFiltered = sk.joints.filter((j)=>!excludeJoint?.(j.name));
|
|
390
|
+
if (jointsFiltered.length !== srcJointsFiltered.length) {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
const rootNode = findRootJoint(jointsFiltered);
|
|
394
|
+
if (!rootNode) {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
const sortedJointsFiltered = sortJoints(rootNode, jointsFiltered);
|
|
398
|
+
if (sortedJointsFiltered && srcJointsFiltered.every((j, i)=>j.name === sortedJointsFiltered[i].name)) {
|
|
399
|
+
dstJointsFiltered = sortedJointsFiltered;
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
return false;
|
|
403
|
+
});
|
|
404
|
+
if (!dstJointsFiltered || !dstSkeleton) {
|
|
405
|
+
console.error(`copyAnimationFrom: no matching skeleton in target set for '${srcSkeletonId}'`);
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
// Build remap only for joints that pass the filter
|
|
409
|
+
for(let fi = 0; fi < srcJointsFiltered.length; fi++){
|
|
410
|
+
const srcJoint = srcJointsFiltered[fi];
|
|
411
|
+
const dstJoint = dstJointsFiltered[fi];
|
|
412
|
+
const si = srcSkeleton.joints.indexOf(srcJoint);
|
|
413
|
+
const di = dstSkeleton.joints.indexOf(dstJoint);
|
|
414
|
+
const srcLen = srcSkeleton.bindPose[si].position.magnitude;
|
|
415
|
+
const dstLen = dstSkeleton.bindPose[di].position.magnitude;
|
|
416
|
+
const translationScale = srcLen > 1e-6 ? dstLen / srcLen : 1;
|
|
417
|
+
nodeMap.set(srcJoint, dstJoint);
|
|
418
|
+
jointRemapBySrcNode.set(srcJoint, {
|
|
419
|
+
dstNode: dstJoint,
|
|
420
|
+
srcBindRot: srcSkeleton.bindPose[si].rotation,
|
|
421
|
+
dstBindRot: dstSkeleton.bindPose[di].rotation,
|
|
422
|
+
translationScale
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
const dstClip = this.createAnimation(destName);
|
|
426
|
+
if (!dstClip) {
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
dstClip.timeDuration = sourceClip.timeDuration;
|
|
430
|
+
dstClip.weight = sourceClip.weight;
|
|
431
|
+
dstClip.autoPlay = sourceClip.autoPlay;
|
|
432
|
+
// Register destination skeleton
|
|
433
|
+
dstClip.addSkeleton(dstSkeleton.persistentId);
|
|
434
|
+
for (const srcNode of srcJointsFiltered){
|
|
435
|
+
const srcTracks = sourceClip.tracks.get(srcNode);
|
|
436
|
+
if (!srcTracks) {
|
|
437
|
+
console.error(`copyAnimationFrom: no track for joint: ${srcNode.name}`);
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
const dstNode = nodeMap.get(srcNode);
|
|
441
|
+
const remap = jointRemapBySrcNode.get(srcNode) ?? null;
|
|
442
|
+
for (const srcTrack of srcTracks){
|
|
443
|
+
let dstTrack;
|
|
444
|
+
if (srcTrack instanceof NodeRotationTrack) {
|
|
445
|
+
dstTrack = retargetRotationTrack(srcTrack);
|
|
446
|
+
} else if (srcTrack instanceof NodeEulerRotationTrack) {
|
|
447
|
+
dstTrack = retargetEulerToRotationTrack(srcTrack);
|
|
448
|
+
} else if (srcTrack instanceof NodeTranslationTrack) {
|
|
449
|
+
dstTrack = retargetTranslationTrack(srcTrack, remap);
|
|
450
|
+
} else if (srcTrack instanceof NodeScaleTrack) {
|
|
451
|
+
dstTrack = new NodeScaleTrack(cloneInterpolator(srcTrack.interpolator));
|
|
452
|
+
} else {
|
|
453
|
+
console.warn(`copyAnimationFrom: unsupported track type '${srcTrack.constructor.name}', skipping`);
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
dstTrack.name = srcTrack.name;
|
|
457
|
+
dstTrack.target = srcTrack.target;
|
|
458
|
+
dstTrack.jointIndex = srcTrack.jointIndex;
|
|
459
|
+
dstClip.addTrack(dstNode, dstTrack);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return dstClip;
|
|
463
|
+
function findRootJoint(joints) {
|
|
464
|
+
let root = null;
|
|
465
|
+
for (const joint of joints){
|
|
466
|
+
if (!root) {
|
|
467
|
+
root = joint;
|
|
468
|
+
}
|
|
469
|
+
while(!root.isParentOf(joint)){
|
|
470
|
+
root = root.parent;
|
|
471
|
+
}
|
|
472
|
+
if (!root) {
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
if (!root || !joints.includes(root)) {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
return root;
|
|
480
|
+
}
|
|
481
|
+
function sortJoints(root, joints) {
|
|
482
|
+
const ordered = [];
|
|
483
|
+
const visited = new Set();
|
|
484
|
+
function visit(joint) {
|
|
485
|
+
if (visited.has(joint)) {
|
|
486
|
+
return true;
|
|
487
|
+
}
|
|
488
|
+
if (!joints.includes(joint)) {
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
if (joint !== root) {
|
|
492
|
+
visit(joint.parent);
|
|
493
|
+
}
|
|
494
|
+
visited.add(joint);
|
|
495
|
+
ordered.push(joint);
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
for (const joint of joints){
|
|
499
|
+
if (!visit(joint)) {
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return ordered;
|
|
504
|
+
}
|
|
505
|
+
function retargetTranslationTrack(src, remap) {
|
|
506
|
+
if (!remap || Math.abs(remap.translationScale - 1) < 1e-6) {
|
|
507
|
+
return new NodeTranslationTrack(cloneInterpolator(src.interpolator));
|
|
508
|
+
}
|
|
509
|
+
const scale = remap.translationScale;
|
|
510
|
+
const srcOutputs = src.interpolator.outputs;
|
|
511
|
+
const newOutputs = new Float32Array(srcOutputs.length);
|
|
512
|
+
for(let i = 0; i < newOutputs.length; i++){
|
|
513
|
+
newOutputs[i] = srcOutputs[i] * scale;
|
|
514
|
+
}
|
|
515
|
+
return new NodeTranslationTrack(new Interpolator(src.interpolator.mode, 'vec3', new Float32Array(src.interpolator.inputs), newOutputs));
|
|
516
|
+
}
|
|
517
|
+
function cloneInterpolator(src) {
|
|
518
|
+
return new Interpolator(src.mode, src.target, src.inputs instanceof Float32Array ? new Float32Array(src.inputs) : [
|
|
519
|
+
...src.inputs
|
|
520
|
+
], src.outputs instanceof Float32Array ? new Float32Array(src.outputs) : [
|
|
521
|
+
...src.outputs
|
|
522
|
+
]);
|
|
523
|
+
}
|
|
524
|
+
function retargetRotationTrack(src) {
|
|
525
|
+
const isCubic = src.interpolator.mode === 'cubicspline';
|
|
526
|
+
const frameStride = isCubic ? 12 : 4;
|
|
527
|
+
const numFrames = src.interpolator.inputs.length;
|
|
528
|
+
const srcOutputs = src.interpolator.outputs;
|
|
529
|
+
const newOutputs = new Float32Array(srcOutputs.length);
|
|
530
|
+
for(let f = 0; f < numFrames; f++){
|
|
531
|
+
const base = f * frameStride;
|
|
532
|
+
newOutputs.set(srcOutputs.subarray(base, base + (isCubic ? 12 : 4)), base);
|
|
533
|
+
}
|
|
534
|
+
return new NodeRotationTrack(new Interpolator(src.interpolator.mode, 'quat', new Float32Array(src.interpolator.inputs), newOutputs));
|
|
535
|
+
}
|
|
536
|
+
function retargetEulerToRotationTrack(src) {
|
|
537
|
+
const srcInputs = src.interpolator.inputs;
|
|
538
|
+
const srcOutputs = src.interpolator.outputs;
|
|
539
|
+
const numFrames = srcInputs.length;
|
|
540
|
+
const newOutputs = new Float32Array(numFrames * 3);
|
|
541
|
+
for(let f = 0; f < numFrames; f++){
|
|
542
|
+
const base = f * 3;
|
|
543
|
+
newOutputs.set(srcOutputs.subarray(base, base + 3), base);
|
|
544
|
+
}
|
|
545
|
+
return new NodeRotationTrack(new Interpolator('linear', 'quat', new Float32Array(srcInputs), newOutputs));
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
330
549
|
* Dispose the animation set and release owned resources.
|
|
331
550
|
*
|
|
332
551
|
* - Disposes the weak reference to the model.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"animationset.js","sources":["../../src/animation/animationset.ts"],"sourcesContent":["import { weightedAverage, Disposable } from '@zephyr3d/base';\r\nimport type { DRef, IDisposable } from '@zephyr3d/base';\r\nimport type { SceneNode } from '../scene';\r\nimport { AnimationClip } from './animation';\r\nimport type { AnimationTrack } from './animationtrack';\r\nimport type { Skeleton } from './skeleton';\r\n\r\n/**\r\n * Options for playing an animation.\r\n *\r\n * Controls looping, playback speed (including reverse), and fade-in blending.\r\n * @public\r\n **/\r\nexport type PlayAnimationOptions = {\r\n /**\r\n * Number of loops to play.\r\n *\r\n * - 0: infinite looping (default).\r\n * - n \\> 0: play exactly n loops (each loop is one full duration of the clip).\r\n */\r\n repeat?: number;\r\n /**\r\n * Playback speed multiplier.\r\n *\r\n * - 1: normal speed (default).\r\n * - \\>1: faster; \\<1: slower.\r\n * - Negative values play the clip in reverse. The initial `currentTime` will be set to the end.\r\n */\r\n speedRatio?: number;\r\n /**\r\n * Fade-in duration in seconds.\r\n *\r\n * Interpolates the animation weight from 0 to the clip's configured weight over this time.\r\n * Use together with `stopAnimation(..., { fadeOut })` for smooth cross-fading.\r\n * Default is 0 (no fade-in).\r\n */\r\n fadeIn?: number;\r\n /**\r\n * Weight of the animation clip.\r\n *\r\n * Used during blending when multiple animations affect the same property.\r\n * Default is the clip's configured weight.\r\n */\r\n weight?: number;\r\n};\r\n\r\n/**\r\n * Options for stopping an animation.\r\n *\r\n * Allows a graceful fade-out instead of abrupt stop.\r\n * @public\r\n */\r\nexport type StopAnimationOptions = {\r\n /**\r\n * Fade-out duration in seconds.\r\n *\r\n * Interpolates the current animation weight down to 0 over this time.\r\n * Default is 0 (immediate stop).\r\n */\r\n fadeOut?: number;\r\n};\r\n\r\n/**\r\n * Animation set\r\n *\r\n * Manages a collection of named animation clips for a model and orchestrates:\r\n * - Playback state (time, loops, speed, weights, fade-in/out).\r\n * - Blending across multiple tracks targeting the same property via weighted averages.\r\n * - Skeleton usage and application for clips that drive skeletal animation.\r\n * - Active track registration and cleanup as clips start/stop.\r\n *\r\n * Usage:\r\n * - Create or retrieve `AnimationClip`s by name.\r\n * - Start playback with `playAnimation(name, options)`.\r\n * - Advance animation with `update(deltaSeconds)`.\r\n * - Optionally adjust weight while playing with `setAnimationWeight(name, weight)`.\r\n *\r\n * Lifetime:\r\n * - Disposing the set releases references to the model, clips, and clears active state.\r\n *\r\n * @public\r\n */\r\nexport class AnimationSet extends Disposable implements IDisposable {\r\n /** @internal */\r\n private _model: SceneNode;\r\n /** @internal */\r\n private _animations: Partial<Record<string, AnimationClip>>;\r\n /** @internal */\r\n private _skeletons: DRef<Skeleton>[];\r\n /** @internal */\r\n private readonly _activeTracks: Map<object, Map<unknown, AnimationTrack[]>>;\r\n /** @internal */\r\n private readonly _activeSkeletons: Map<Skeleton, number>;\r\n /** @internal */\r\n private readonly _activeAnimations: Map<\r\n AnimationClip,\r\n {\r\n currentTime: number;\r\n repeat: number;\r\n repeatCounter: number;\r\n weight: number;\r\n speedRatio: number;\r\n firstFrame: boolean;\r\n fadeIn: number;\r\n fadeOut: number;\r\n fadeOutStart: number;\r\n animateTime: number;\r\n }\r\n >;\r\n /**\r\n * Create an AnimationSet controlling the provided model.\r\n *\r\n * @param model - The SceneNode (model root) controlled by this animation set.\r\n */\r\n constructor(model: SceneNode) {\r\n super();\r\n this._model = model;\r\n this._animations = {};\r\n this._activeTracks = new Map();\r\n this._activeSkeletons = new Map();\r\n this._activeAnimations = new Map();\r\n this._skeletons = [];\r\n }\r\n /**\r\n * The model (SceneNode) controlled by this animation set.\r\n */\r\n get model() {\r\n return this._model;\r\n }\r\n /**\r\n * Number of animation clips registered in this set.\r\n */\r\n get numAnimations() {\r\n return Object.getOwnPropertyNames(this._animations).length;\r\n }\r\n /**\r\n * The skeletons used by animations in this set.\r\n */\r\n get skeletons() {\r\n return this._skeletons;\r\n }\r\n /**\r\n * Retrieve an animation clip by name.\r\n *\r\n * @param name - Name of the animation.\r\n * @returns The clip if present; otherwise null.\r\n */\r\n get(name: string) {\r\n return this._animations[name] ?? null;\r\n }\r\n /**\r\n * Create and register a new animation clip.\r\n *\r\n * @param name - Unique name for the animation clip.\r\n * @param embedded - Whether the clip is embedded/owned (implementation-specific). Default false.\r\n * @returns The created clip, or null if the name is empty or not unique.\r\n */\r\n createAnimation(name: string, embedded = false) {\r\n if (!name || this._animations[name]) {\r\n console.error('Animation must have unique name');\r\n return null;\r\n } else {\r\n const animation = new AnimationClip(name, this, embedded);\r\n this._animations[name] = animation;\r\n this._model.scene?.queueUpdateNode(this._model);\r\n return animation;\r\n }\r\n }\r\n /**\r\n * Delete and dispose an animation clip by name.\r\n *\r\n * - If the animation is currently playing, it is first stopped (immediately).\r\n *\r\n * @param name - Name of the animation to remove.\r\n */\r\n deleteAnimation(name: string) {\r\n const animation = this._animations[name];\r\n if (animation) {\r\n this.stopAnimation(name);\r\n delete this._animations[name];\r\n animation.dispose();\r\n }\r\n }\r\n /**\r\n * Get the list of all registered animation names.\r\n *\r\n * @returns An array of clip names.\r\n */\r\n getAnimationNames() {\r\n return Object.keys(this._animations);\r\n }\r\n /**\r\n * Advance and apply active animations.\r\n *\r\n * Responsibilities per call:\r\n * - Update time cursor for each active clip (respecting speedRatio and looping).\r\n * - Enforce repeat limits and apply fade-out termination if configured.\r\n * - For each animated target, blend active tracks (weighted by clip weight × fade-in × fade-out)\r\n * and apply the resulting state to the target.\r\n * - Apply all active skeletons to update skinning transforms.\r\n *\r\n * @param deltaInSeconds - Time step in seconds since last update.\r\n */\r\n update(deltaInSeconds: number) {\r\n this._activeAnimations.forEach((v, k) => {\r\n if (v.fadeOut > 0 && v.fadeOutStart < 0) {\r\n v.fadeOutStart = v.animateTime;\r\n }\r\n // Update animation time\r\n if (v.firstFrame) {\r\n v.firstFrame = false;\r\n } else {\r\n const timeAdvance = deltaInSeconds * v.speedRatio;\r\n v.currentTime += timeAdvance;\r\n v.animateTime += timeAdvance;\r\n if (v.currentTime > k.timeDuration) {\r\n v.repeatCounter++;\r\n v.currentTime = 0;\r\n } else if (v.currentTime < 0) {\r\n v.repeatCounter++;\r\n v.currentTime = k.timeDuration;\r\n }\r\n if (v.repeat !== 0 && v.repeatCounter >= v.repeat) {\r\n this.stopAnimation(k.name);\r\n } else if (v.fadeOut > 0) {\r\n if (v.animateTime - v.fadeOutStart >= v.fadeOut) {\r\n this.stopAnimation(k.name);\r\n }\r\n }\r\n }\r\n });\r\n // Update tracks\r\n this._activeTracks.forEach((v, k) => {\r\n v.forEach((alltracks) => {\r\n // Only deal with tracks which have not been removed\r\n const tracks = alltracks.filter(\r\n (track) => track.animation && this.isPlayingAnimation(track.animation.name)\r\n );\r\n if (tracks.length > 0) {\r\n const weights = tracks.map((track) => {\r\n const info = this._activeAnimations.get(track.animation!)!;\r\n const weight = info.weight;\r\n const fadeIn = info.fadeIn === 0 ? 1 : Math.min(1, info.animateTime / info.fadeIn);\r\n let fadeOut = 1;\r\n if (info.fadeOut !== 0) {\r\n fadeOut = 1 - (info.animateTime - info.fadeOutStart) / info.fadeOut;\r\n }\r\n return weight * fadeIn * fadeOut;\r\n });\r\n const states = tracks.map((track) => {\r\n const info = this._activeAnimations.get(track.animation!)!;\r\n const t = (info.currentTime / track.animation!.timeDuration) * track.getDuration();\r\n return track.calculateState(k, t);\r\n });\r\n const state = weightedAverage(weights, states, (a, b, t) => {\r\n return tracks[0].mixState(a, b, t);\r\n });\r\n tracks[0].applyState(k, state);\r\n }\r\n });\r\n });\r\n // Update skeletons\r\n this._skeletons.forEach((v) => {\r\n v.get()?.apply(deltaInSeconds);\r\n });\r\n }\r\n /**\r\n * Check whether an animation is currently playing.\r\n *\r\n * @param name - Optional animation name. If omitted, returns true if any animation is playing.\r\n * @returns True if playing; otherwise false.\r\n */\r\n isPlayingAnimation(name?: string) {\r\n if (name) {\r\n const animation = this._animations[name];\r\n return !!animation && this._activeAnimations.has(animation);\r\n }\r\n return this._activeAnimations.size > 0;\r\n }\r\n /**\r\n * Get an animation clip by name.\r\n *\r\n * Alias of `get(name)` returning a nullable type.\r\n *\r\n * @param name - Name of the animation.\r\n * @returns The clip if present; otherwise null.\r\n */\r\n getAnimationClip(name: string) {\r\n return this._animations[name] ?? null;\r\n }\r\n /**\r\n * Set the runtime blend weight for a currently playing animation.\r\n *\r\n * Has no effect if the clip is not active.\r\n *\r\n * @param name - Name of the playing animation.\r\n * @param weight - New weight value used during blending.\r\n */\r\n setAnimationWeight(name: string, weight: number) {\r\n const ani = this._animations[name];\r\n if (!ani) {\r\n console.error(`Animation ${name} not exists`);\r\n return;\r\n }\r\n const info = this._activeAnimations.get(ani);\r\n if (info) {\r\n info.weight = weight;\r\n }\r\n }\r\n /**\r\n * Start (or update) playback of an animation clip.\r\n *\r\n * Behavior:\r\n * - If the clip is already playing, updates its fade-in (resets fade-out).\r\n * - Otherwise initializes playback state (repeat counter, speed, weight, initial time).\r\n * - Registers clip tracks and skeletons into the active sets for blending and application.\r\n *\r\n * @param name - Name of the animation to play.\r\n * @param options - Playback options (repeat, speedRatio, fadeIn).\r\n */\r\n playAnimation(name: string, options?: PlayAnimationOptions) {\r\n const ani = this._animations[name];\r\n if (!ani) {\r\n console.error(`Animation ${name} not exists`);\r\n return;\r\n }\r\n const fadeIn = Math.max(options?.fadeIn ?? 0, 0);\r\n const info = this._activeAnimations.get(ani);\r\n if (info) {\r\n info.fadeOut = 0;\r\n info.fadeIn = fadeIn;\r\n } else {\r\n const repeat = options?.repeat ?? 0;\r\n const speedRatio = options?.speedRatio ?? 1;\r\n const weight = options?.weight ?? ani.weight ?? 1;\r\n this._activeAnimations.set(ani, {\r\n repeat,\r\n weight,\r\n speedRatio,\r\n fadeIn,\r\n fadeOut: 0,\r\n repeatCounter: 0,\r\n currentTime: speedRatio < 0 ? ani.timeDuration : 0,\r\n animateTime: 0,\r\n fadeOutStart: 0,\r\n firstFrame: true\r\n });\r\n ani.tracks?.forEach((v, k) => {\r\n let nodeTracks = this._activeTracks.get(k);\r\n if (!nodeTracks) {\r\n nodeTracks = new Map();\r\n this._activeTracks.set(k, nodeTracks);\r\n }\r\n for (const track of v) {\r\n const blendId = track.getBlendId();\r\n let blendedTracks = nodeTracks.get(blendId);\r\n if (!blendedTracks) {\r\n blendedTracks = [];\r\n nodeTracks.set(blendId, blendedTracks);\r\n }\r\n blendedTracks.push(track);\r\n }\r\n });\r\n ani.skeletons?.forEach((v, k) => {\r\n const skeleton = this.model.findSkeletonById(k);\r\n if (skeleton) {\r\n const refcount = this._activeSkeletons.get(skeleton);\r\n if (refcount) {\r\n this._activeSkeletons.set(skeleton, refcount + 1);\r\n } else {\r\n this._activeSkeletons.set(skeleton, 1);\r\n }\r\n skeleton.playing = true;\r\n }\r\n });\r\n }\r\n }\r\n /**\r\n * Stop playback of an animation clip.\r\n *\r\n * Behavior:\r\n * - If `options.fadeOut > 0`, marks the clip for fade-out; actual removal occurs after fade completes.\r\n * - If `fadeOut` is 0 or omitted, immediately:\r\n * - Removes the clip from active animations.\r\n * - Unregisters its tracks from active track maps.\r\n * - Decrements skeleton reference counts; resets and removes skeletons when refcount reaches 0.\r\n *\r\n * @param name - Name of the animation to stop.\r\n * @param options - Optional fade-out configuration.\r\n */\r\n stopAnimation(name: string, options?: StopAnimationOptions) {\r\n const ani = this._animations[name];\r\n if (!ani) {\r\n console.error(`Animation ${name} not exists`);\r\n return;\r\n }\r\n const info = this._activeAnimations.get(ani);\r\n if (info) {\r\n const fadeOut = Math.max(options?.fadeOut ?? 0, 0);\r\n if (fadeOut !== 0) {\r\n info.fadeOut = fadeOut;\r\n info.fadeOutStart = -1;\r\n } else {\r\n this._activeAnimations.delete(ani);\r\n this._activeTracks.forEach((v) => {\r\n v.forEach((tracks, id) => {\r\n v.set(\r\n id,\r\n tracks.filter((track) => track.animation !== ani)\r\n );\r\n });\r\n });\r\n ani.skeletons?.forEach((v, k) => {\r\n const skeleton = this.model.findSkeletonById(k);\r\n if (skeleton) {\r\n const refcount = this._activeSkeletons.get(skeleton)!;\r\n if (refcount === 1) {\r\n skeleton.reset();\r\n this._activeSkeletons.delete(skeleton);\r\n } else {\r\n this._activeSkeletons.set(skeleton, refcount - 1);\r\n }\r\n }\r\n });\r\n }\r\n }\r\n }\r\n /**\r\n * Dispose the animation set and release owned resources.\r\n *\r\n * - Disposes the weak reference to the model.\r\n * - Disposes all registered animation clips.\r\n * - Clears active animations, tracks, and skeleton references.\r\n */\r\n protected onDispose() {\r\n super.onDispose();\r\n for (const k in this._animations) {\r\n this._animations[k]!.dispose();\r\n }\r\n this._animations = {};\r\n this._activeAnimations.clear();\r\n this._activeSkeletons.clear();\r\n this._activeTracks.clear();\r\n }\r\n}\r\n"],"names":["AnimationSet","Disposable","model","_model","_animations","_activeTracks","Map","_activeSkeletons","_activeAnimations","_skeletons","numAnimations","Object","getOwnPropertyNames","length","skeletons","get","name","createAnimation","embedded","console","error","animation","AnimationClip","scene","queueUpdateNode","deleteAnimation","stopAnimation","dispose","getAnimationNames","keys","update","deltaInSeconds","forEach","v","k","fadeOut","fadeOutStart","animateTime","firstFrame","timeAdvance","speedRatio","currentTime","timeDuration","repeatCounter","repeat","alltracks","tracks","filter","track","isPlayingAnimation","weights","map","info","weight","fadeIn","Math","min","states","t","getDuration","calculateState","state","weightedAverage","a","b","mixState","applyState","apply","has","size","getAnimationClip","setAnimationWeight","ani","playAnimation","options","max","set","nodeTracks","blendId","getBlendId","blendedTracks","push","skeleton","findSkeletonById","refcount","playing","delete","id","reset","onDispose","clear"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"animationset.js","sources":["../../src/animation/animationset.ts"],"sourcesContent":["import { weightedAverage, Disposable, Interpolator } from '@zephyr3d/base';\r\nimport type { DRef, IDisposable, Nullable, Quaternion } from '@zephyr3d/base';\r\nimport type { SceneNode } from '../scene';\r\nimport { AnimationClip } from './animation';\r\nimport type { AnimationTrack } from './animationtrack';\r\nimport { NodeRotationTrack } from './rotationtrack';\r\nimport { NodeEulerRotationTrack } from './eulerrotationtrack';\r\nimport { NodeTranslationTrack } from './translationtrack';\r\nimport { NodeScaleTrack } from './scaletrack';\r\nimport { Skeleton } from './skeleton';\r\n\r\n/**\r\n * Options for playing an animation.\r\n *\r\n * Controls looping, playback speed (including reverse), and fade-in blending.\r\n * @public\r\n **/\r\nexport type PlayAnimationOptions = {\r\n /**\r\n * Number of loops to play.\r\n *\r\n * - 0: infinite looping (default).\r\n * - n \\> 0: play exactly n loops (each loop is one full duration of the clip).\r\n */\r\n repeat?: number;\r\n /**\r\n * Playback speed multiplier.\r\n *\r\n * - 1: normal speed (default).\r\n * - \\>1: faster; \\<1: slower.\r\n * - Negative values play the clip in reverse. The initial `currentTime` will be set to the end.\r\n */\r\n speedRatio?: number;\r\n /**\r\n * Fade-in duration in seconds.\r\n *\r\n * Interpolates the animation weight from 0 to the clip's configured weight over this time.\r\n * Use together with `stopAnimation(..., { fadeOut })` for smooth cross-fading.\r\n * Default is 0 (no fade-in).\r\n */\r\n fadeIn?: number;\r\n /**\r\n * Weight of the animation clip.\r\n *\r\n * Used during blending when multiple animations affect the same property.\r\n * Default is the clip's configured weight.\r\n */\r\n weight?: number;\r\n};\r\n\r\n/**\r\n * Options for stopping an animation.\r\n *\r\n * Allows a graceful fade-out instead of abrupt stop.\r\n * @public\r\n */\r\nexport type StopAnimationOptions = {\r\n /**\r\n * Fade-out duration in seconds.\r\n *\r\n * Interpolates the current animation weight down to 0 over this time.\r\n * Default is 0 (immediate stop).\r\n */\r\n fadeOut?: number;\r\n};\r\n\r\n/**\r\n * Animation set\r\n *\r\n * Manages a collection of named animation clips for a model and orchestrates:\r\n * - Playback state (time, loops, speed, weights, fade-in/out).\r\n * - Blending across multiple tracks targeting the same property via weighted averages.\r\n * - Skeleton usage and application for clips that drive skeletal animation.\r\n * - Active track registration and cleanup as clips start/stop.\r\n *\r\n * Usage:\r\n * - Create or retrieve `AnimationClip`s by name.\r\n * - Start playback with `playAnimation(name, options)`.\r\n * - Advance animation with `update(deltaSeconds)`.\r\n * - Optionally adjust weight while playing with `setAnimationWeight(name, weight)`.\r\n *\r\n * Lifetime:\r\n * - Disposing the set releases references to the model, clips, and clears active state.\r\n *\r\n * @public\r\n */\r\nexport class AnimationSet extends Disposable implements IDisposable {\r\n /** @internal */\r\n private _model: SceneNode;\r\n /** @internal */\r\n private _animations: Partial<Record<string, AnimationClip>>;\r\n /** @internal */\r\n private _skeletons: DRef<Skeleton>[];\r\n /** @internal */\r\n private readonly _activeTracks: Map<object, Map<unknown, AnimationTrack[]>>;\r\n /** @internal */\r\n private readonly _activeSkeletons: Map<Skeleton, number>;\r\n /** @internal */\r\n private readonly _activeAnimations: Map<\r\n AnimationClip,\r\n {\r\n currentTime: number;\r\n repeat: number;\r\n repeatCounter: number;\r\n weight: number;\r\n speedRatio: number;\r\n firstFrame: boolean;\r\n fadeIn: number;\r\n fadeOut: number;\r\n fadeOutStart: number;\r\n animateTime: number;\r\n }\r\n >;\r\n /**\r\n * Create an AnimationSet controlling the provided model.\r\n *\r\n * @param model - The SceneNode (model root) controlled by this animation set.\r\n */\r\n constructor(model: SceneNode) {\r\n super();\r\n this._model = model;\r\n this._animations = {};\r\n this._activeTracks = new Map();\r\n this._activeSkeletons = new Map();\r\n this._activeAnimations = new Map();\r\n this._skeletons = [];\r\n }\r\n /**\r\n * The model (SceneNode) controlled by this animation set.\r\n */\r\n get model() {\r\n return this._model;\r\n }\r\n /**\r\n * Number of animation clips registered in this set.\r\n */\r\n get numAnimations() {\r\n return Object.getOwnPropertyNames(this._animations).length;\r\n }\r\n /**\r\n * The skeletons used by animations in this set.\r\n */\r\n get skeletons() {\r\n return this._skeletons;\r\n }\r\n /**\r\n * Retrieve an animation clip by name.\r\n *\r\n * @param name - Name of the animation.\r\n * @returns The clip if present; otherwise null.\r\n */\r\n get(name: string) {\r\n return this._animations[name] ?? null;\r\n }\r\n /**\r\n * Create and register a new animation clip.\r\n *\r\n * @param name - Unique name for the animation clip.\r\n * @param embedded - Whether the clip is embedded/owned (implementation-specific). Default false.\r\n * @returns The created clip, or null if the name is empty or not unique.\r\n */\r\n createAnimation(name: string, embedded = false) {\r\n if (!name || this._animations[name]) {\r\n console.error('Animation must have unique name');\r\n return null;\r\n } else {\r\n const animation = new AnimationClip(name, this, embedded);\r\n this._animations[name] = animation;\r\n this._model.scene?.queueUpdateNode(this._model);\r\n return animation;\r\n }\r\n }\r\n /**\r\n * Delete and dispose an animation clip by name.\r\n *\r\n * - If the animation is currently playing, it is first stopped (immediately).\r\n *\r\n * @param name - Name of the animation to remove.\r\n */\r\n deleteAnimation(name: string) {\r\n const animation = this._animations[name];\r\n if (animation) {\r\n this.stopAnimation(name);\r\n delete this._animations[name];\r\n animation.dispose();\r\n }\r\n }\r\n /**\r\n * Get the list of all registered animation names.\r\n *\r\n * @returns An array of clip names.\r\n */\r\n getAnimationNames() {\r\n return Object.keys(this._animations);\r\n }\r\n /**\r\n * Advance and apply active animations.\r\n *\r\n * Responsibilities per call:\r\n * - Update time cursor for each active clip (respecting speedRatio and looping).\r\n * - Enforce repeat limits and apply fade-out termination if configured.\r\n * - For each animated target, blend active tracks (weighted by clip weight × fade-in × fade-out)\r\n * and apply the resulting state to the target.\r\n * - Apply all active skeletons to update skinning transforms.\r\n *\r\n * @param deltaInSeconds - Time step in seconds since last update.\r\n */\r\n update(deltaInSeconds: number) {\r\n this._activeAnimations.forEach((v, k) => {\r\n if (v.fadeOut > 0 && v.fadeOutStart < 0) {\r\n v.fadeOutStart = v.animateTime;\r\n }\r\n // Update animation time\r\n if (v.firstFrame) {\r\n v.firstFrame = false;\r\n } else {\r\n const timeAdvance = deltaInSeconds * v.speedRatio;\r\n v.currentTime += timeAdvance;\r\n v.animateTime += timeAdvance;\r\n if (v.currentTime > k.timeDuration) {\r\n v.repeatCounter++;\r\n v.currentTime = 0;\r\n } else if (v.currentTime < 0) {\r\n v.repeatCounter++;\r\n v.currentTime = k.timeDuration;\r\n }\r\n if (v.repeat !== 0 && v.repeatCounter >= v.repeat) {\r\n this.stopAnimation(k.name);\r\n } else if (v.fadeOut > 0) {\r\n if (v.animateTime - v.fadeOutStart >= v.fadeOut) {\r\n this.stopAnimation(k.name);\r\n }\r\n }\r\n }\r\n });\r\n // Update tracks\r\n this._activeTracks.forEach((v, k) => {\r\n v.forEach((alltracks) => {\r\n // Only deal with tracks which have not been removed\r\n const tracks = alltracks.filter(\r\n (track) => track.animation && this.isPlayingAnimation(track.animation.name)\r\n );\r\n if (tracks.length > 0) {\r\n const weights = tracks.map((track) => {\r\n const info = this._activeAnimations.get(track.animation!)!;\r\n const weight = info.weight;\r\n const fadeIn = info.fadeIn === 0 ? 1 : Math.min(1, info.animateTime / info.fadeIn);\r\n let fadeOut = 1;\r\n if (info.fadeOut !== 0) {\r\n fadeOut = 1 - (info.animateTime - info.fadeOutStart) / info.fadeOut;\r\n }\r\n return weight * fadeIn * fadeOut;\r\n });\r\n const states = tracks.map((track) => {\r\n const info = this._activeAnimations.get(track.animation!)!;\r\n const t = (info.currentTime / track.animation!.timeDuration) * track.getDuration();\r\n return track.calculateState(k, t);\r\n });\r\n const state = weightedAverage(weights, states, (a, b, t) => {\r\n return tracks[0].mixState(a, b, t);\r\n });\r\n tracks[0].applyState(k, state);\r\n }\r\n });\r\n });\r\n // Update skeletons\r\n this._skeletons.forEach((v) => {\r\n v.get()?.apply(deltaInSeconds);\r\n });\r\n }\r\n /**\r\n * Check whether an animation is currently playing.\r\n *\r\n * @param name - Optional animation name. If omitted, returns true if any animation is playing.\r\n * @returns True if playing; otherwise false.\r\n */\r\n isPlayingAnimation(name?: string) {\r\n if (name) {\r\n const animation = this._animations[name];\r\n return !!animation && this._activeAnimations.has(animation);\r\n }\r\n return this._activeAnimations.size > 0;\r\n }\r\n /**\r\n * Get an animation clip by name.\r\n *\r\n * Alias of `get(name)` returning a nullable type.\r\n *\r\n * @param name - Name of the animation.\r\n * @returns The clip if present; otherwise null.\r\n */\r\n getAnimationClip(name: string) {\r\n return this._animations[name] ?? null;\r\n }\r\n /**\r\n * Set the runtime blend weight for a currently playing animation.\r\n *\r\n * Has no effect if the clip is not active.\r\n *\r\n * @param name - Name of the playing animation.\r\n * @param weight - New weight value used during blending.\r\n */\r\n setAnimationWeight(name: string, weight: number) {\r\n const ani = this._animations[name];\r\n if (!ani) {\r\n console.error(`Animation ${name} not exists`);\r\n return;\r\n }\r\n const info = this._activeAnimations.get(ani);\r\n if (info) {\r\n info.weight = weight;\r\n }\r\n }\r\n /**\r\n * Start (or update) playback of an animation clip.\r\n *\r\n * Behavior:\r\n * - If the clip is already playing, updates its fade-in (resets fade-out).\r\n * - Otherwise initializes playback state (repeat counter, speed, weight, initial time).\r\n * - Registers clip tracks and skeletons into the active sets for blending and application.\r\n *\r\n * @param name - Name of the animation to play.\r\n * @param options - Playback options (repeat, speedRatio, fadeIn).\r\n */\r\n playAnimation(name: string, options?: PlayAnimationOptions) {\r\n const ani = this._animations[name];\r\n if (!ani) {\r\n console.error(`Animation ${name} not exists`);\r\n return;\r\n }\r\n const fadeIn = Math.max(options?.fadeIn ?? 0, 0);\r\n const info = this._activeAnimations.get(ani);\r\n if (info) {\r\n info.fadeOut = 0;\r\n info.fadeIn = fadeIn;\r\n } else {\r\n const repeat = options?.repeat ?? 0;\r\n const speedRatio = options?.speedRatio ?? 1;\r\n const weight = options?.weight ?? ani.weight ?? 1;\r\n this._activeAnimations.set(ani, {\r\n repeat,\r\n weight,\r\n speedRatio,\r\n fadeIn,\r\n fadeOut: 0,\r\n repeatCounter: 0,\r\n currentTime: speedRatio < 0 ? ani.timeDuration : 0,\r\n animateTime: 0,\r\n fadeOutStart: 0,\r\n firstFrame: true\r\n });\r\n ani.tracks?.forEach((v, k) => {\r\n let nodeTracks = this._activeTracks.get(k);\r\n if (!nodeTracks) {\r\n nodeTracks = new Map();\r\n this._activeTracks.set(k, nodeTracks);\r\n }\r\n for (const track of v) {\r\n const blendId = track.getBlendId();\r\n let blendedTracks = nodeTracks.get(blendId);\r\n if (!blendedTracks) {\r\n blendedTracks = [];\r\n nodeTracks.set(blendId, blendedTracks);\r\n }\r\n blendedTracks.push(track);\r\n }\r\n });\r\n ani.skeletons?.forEach((v, k) => {\r\n const skeleton = this.model.findSkeletonById(k);\r\n if (skeleton) {\r\n const refcount = this._activeSkeletons.get(skeleton);\r\n if (refcount) {\r\n this._activeSkeletons.set(skeleton, refcount + 1);\r\n } else {\r\n this._activeSkeletons.set(skeleton, 1);\r\n }\r\n skeleton.playing = true;\r\n }\r\n });\r\n }\r\n }\r\n /**\r\n * Stop playback of an animation clip.\r\n *\r\n * Behavior:\r\n * - If `options.fadeOut > 0`, marks the clip for fade-out; actual removal occurs after fade completes.\r\n * - If `fadeOut` is 0 or omitted, immediately:\r\n * - Removes the clip from active animations.\r\n * - Unregisters its tracks from active track maps.\r\n * - Decrements skeleton reference counts; resets and removes skeletons when refcount reaches 0.\r\n *\r\n * @param name - Name of the animation to stop.\r\n * @param options - Optional fade-out configuration.\r\n */\r\n stopAnimation(name: string, options?: StopAnimationOptions) {\r\n const ani = this._animations[name];\r\n if (!ani) {\r\n console.error(`Animation ${name} not exists`);\r\n return;\r\n }\r\n const info = this._activeAnimations.get(ani);\r\n if (info) {\r\n const fadeOut = Math.max(options?.fadeOut ?? 0, 0);\r\n if (fadeOut !== 0) {\r\n info.fadeOut = fadeOut;\r\n info.fadeOutStart = -1;\r\n } else {\r\n this._activeAnimations.delete(ani);\r\n this._activeTracks.forEach((v) => {\r\n v.forEach((tracks, id) => {\r\n v.set(\r\n id,\r\n tracks.filter((track) => track.animation !== ani)\r\n );\r\n });\r\n });\r\n ani.skeletons?.forEach((v, k) => {\r\n const skeleton = this.model.findSkeletonById(k);\r\n if (skeleton) {\r\n const refcount = this._activeSkeletons.get(skeleton)!;\r\n if (refcount === 1) {\r\n skeleton.reset();\r\n this._activeSkeletons.delete(skeleton);\r\n } else {\r\n this._activeSkeletons.set(skeleton, refcount - 1);\r\n }\r\n }\r\n });\r\n }\r\n }\r\n }\r\n /**\r\n * Copy an animation clip from another AnimationSet into this one.\r\n *\r\n * Prerequisites:\r\n * - Both sets must reference skeletons with identical joint names and counts.\r\n * - The source clip must exist in `sourceSet`.\r\n *\r\n * @param sourceSet - The AnimationSet to copy from.\r\n * @param animationName - Name of the clip to copy.\r\n * @param targetName - Name for the new clip in this set. Defaults to `animationName`.\r\n * @param excludeJoint - Optional predicate; joints whose name returns true are excluded from\r\n * skeleton structure matching.\r\n * @returns The newly created AnimationClip, or null on failure.\r\n */\r\n copyAnimationFrom(\r\n sourceSet: AnimationSet,\r\n animationName: string,\r\n targetName?: string,\r\n excludeJoint?: (jointName: string) => boolean\r\n ): AnimationClip | null {\r\n const destName = targetName ?? animationName;\r\n const sourceClip = sourceSet.get(animationName);\r\n if (!sourceClip) {\r\n console.error(`copyAnimationFrom: animation '${animationName}' not found in source set`);\r\n return null;\r\n }\r\n if (this._animations[destName]) {\r\n console.error(`copyAnimationFrom: animation '${destName}' already exists in target set`);\r\n return null;\r\n }\r\n\r\n // Per-joint retargeting info indexed by source joint node\r\n type JointRemap = {\r\n dstNode: SceneNode;\r\n srcBindRot: Quaternion;\r\n dstBindRot: Quaternion;\r\n translationScale: number;\r\n };\r\n if (sourceClip.skeletons.size !== 1) {\r\n console.error(`copyAnimationFrom: source animation clip must be affected by exactly one skeleton`);\r\n return null;\r\n }\r\n const srcSkeletonId = [...sourceClip.skeletons][0];\r\n const srcSkeleton = Skeleton.findSkeletonById(srcSkeletonId);\r\n if (!srcSkeleton) {\r\n console.error(`copyAnimationFrom: source skeleton '${srcSkeletonId}' not found`);\r\n return null;\r\n }\r\n const nodeMap = new Map<object, SceneNode>();\r\n const jointRemapBySrcNode = new Map<object, JointRemap>();\r\n\r\n const jointsFiltered = srcSkeleton.joints.filter((j) => !excludeJoint?.(j.name));\r\n const srcRootNode = findRootJoint(jointsFiltered);\r\n if (!srcRootNode) {\r\n console.error(`copyAnimationFrom: cannot determine the root joint for source skeleton`);\r\n return null;\r\n }\r\n // Build filtered joint lists for matching (exclude joints rejected by filterJoint)\r\n const srcJointsFiltered = sortJoints(srcRootNode, jointsFiltered);\r\n if (!srcJointsFiltered) {\r\n console.error(`copyAnimationFrom: invalid source skeleton structure`);\r\n return null;\r\n }\r\n let dstJointsFiltered: SceneNode[] = [];\r\n const dstSkeleton = this._skeletons\r\n .map((ref) => ref.get())\r\n .find((sk) => {\r\n if (!sk) {\r\n return false;\r\n }\r\n const jointsFiltered = sk.joints.filter((j) => !excludeJoint?.(j.name));\r\n if (jointsFiltered.length !== srcJointsFiltered.length) {\r\n return false;\r\n }\r\n const rootNode = findRootJoint(jointsFiltered);\r\n if (!rootNode) {\r\n return false;\r\n }\r\n const sortedJointsFiltered = sortJoints(rootNode, jointsFiltered);\r\n if (\r\n sortedJointsFiltered &&\r\n srcJointsFiltered.every((j, i) => j.name === sortedJointsFiltered[i].name)\r\n ) {\r\n dstJointsFiltered = sortedJointsFiltered;\r\n return true;\r\n }\r\n return false;\r\n });\r\n if (!dstJointsFiltered || !dstSkeleton) {\r\n console.error(`copyAnimationFrom: no matching skeleton in target set for '${srcSkeletonId}'`);\r\n return null;\r\n }\r\n // Build remap only for joints that pass the filter\r\n for (let fi = 0; fi < srcJointsFiltered.length; fi++) {\r\n const srcJoint = srcJointsFiltered[fi];\r\n const dstJoint = dstJointsFiltered[fi];\r\n const si = srcSkeleton.joints.indexOf(srcJoint);\r\n const di = dstSkeleton.joints.indexOf(dstJoint);\r\n const srcLen = srcSkeleton.bindPose[si].position.magnitude;\r\n const dstLen = dstSkeleton.bindPose[di].position.magnitude;\r\n const translationScale = srcLen > 1e-6 ? dstLen / srcLen : 1;\r\n nodeMap.set(srcJoint, dstJoint);\r\n jointRemapBySrcNode.set(srcJoint, {\r\n dstNode: dstJoint,\r\n srcBindRot: srcSkeleton.bindPose[si].rotation,\r\n dstBindRot: dstSkeleton.bindPose[di].rotation,\r\n translationScale\r\n });\r\n }\r\n\r\n const dstClip = this.createAnimation(destName);\r\n if (!dstClip) {\r\n return null;\r\n }\r\n dstClip.timeDuration = sourceClip.timeDuration;\r\n dstClip.weight = sourceClip.weight;\r\n dstClip.autoPlay = sourceClip.autoPlay;\r\n\r\n // Register destination skeleton\r\n dstClip.addSkeleton(dstSkeleton.persistentId);\r\n\r\n for (const srcNode of srcJointsFiltered) {\r\n const srcTracks = sourceClip.tracks.get(srcNode);\r\n if (!srcTracks) {\r\n console.error(`copyAnimationFrom: no track for joint: ${srcNode.name}`);\r\n return null;\r\n }\r\n const dstNode = nodeMap.get(srcNode)!;\r\n const remap = jointRemapBySrcNode.get(srcNode) ?? null;\r\n\r\n for (const srcTrack of srcTracks) {\r\n let dstTrack: AnimationTrack;\r\n if (srcTrack instanceof NodeRotationTrack) {\r\n dstTrack = retargetRotationTrack(srcTrack);\r\n } else if (srcTrack instanceof NodeEulerRotationTrack) {\r\n dstTrack = retargetEulerToRotationTrack(srcTrack);\r\n } else if (srcTrack instanceof NodeTranslationTrack) {\r\n dstTrack = retargetTranslationTrack(srcTrack, remap);\r\n } else if (srcTrack instanceof NodeScaleTrack) {\r\n dstTrack = new NodeScaleTrack(cloneInterpolator(srcTrack.interpolator));\r\n } else {\r\n console.warn(`copyAnimationFrom: unsupported track type '${srcTrack.constructor.name}', skipping`);\r\n continue;\r\n }\r\n\r\n dstTrack.name = srcTrack.name;\r\n dstTrack.target = srcTrack.target;\r\n dstTrack.jointIndex = srcTrack.jointIndex;\r\n dstClip.addTrack(dstNode, dstTrack);\r\n }\r\n }\r\n\r\n return dstClip;\r\n\r\n function findRootJoint(joints: SceneNode[]) {\r\n let root: Nullable<SceneNode> = null;\r\n for (const joint of joints) {\r\n if (!root) {\r\n root = joint;\r\n }\r\n while (!root!.isParentOf(joint)) {\r\n root = root!.parent;\r\n }\r\n if (!root) {\r\n break;\r\n }\r\n }\r\n if (!root || !joints.includes(root)) {\r\n return null;\r\n }\r\n return root;\r\n }\r\n\r\n function sortJoints(root: SceneNode, joints: SceneNode[]): Nullable<SceneNode[]> {\r\n const ordered: SceneNode[] = [];\r\n const visited = new Set<SceneNode>();\r\n function visit(joint: SceneNode) {\r\n if (visited.has(joint)) {\r\n return true;\r\n }\r\n if (!joints.includes(joint)) {\r\n return false;\r\n }\r\n if (joint !== root) {\r\n visit(joint.parent!);\r\n }\r\n visited.add(joint);\r\n ordered.push(joint);\r\n return true;\r\n }\r\n for (const joint of joints) {\r\n if (!visit(joint)) {\r\n return null;\r\n }\r\n }\r\n return ordered;\r\n }\r\n function retargetTranslationTrack(\r\n src: NodeTranslationTrack,\r\n remap: JointRemap | null\r\n ): NodeTranslationTrack {\r\n if (!remap || Math.abs(remap.translationScale - 1) < 1e-6) {\r\n return new NodeTranslationTrack(cloneInterpolator(src.interpolator));\r\n }\r\n const scale = remap.translationScale;\r\n const srcOutputs = src.interpolator.outputs as Float32Array;\r\n const newOutputs = new Float32Array(srcOutputs.length);\r\n for (let i = 0; i < newOutputs.length; i++) {\r\n newOutputs[i] = srcOutputs[i] * scale;\r\n }\r\n return new NodeTranslationTrack(\r\n new Interpolator(\r\n src.interpolator.mode,\r\n 'vec3',\r\n new Float32Array(src.interpolator.inputs as Float32Array),\r\n newOutputs\r\n )\r\n );\r\n }\r\n\r\n function cloneInterpolator(src: Interpolator): Interpolator {\r\n return new Interpolator(\r\n src.mode,\r\n src.target,\r\n src.inputs instanceof Float32Array ? new Float32Array(src.inputs) : [...src.inputs],\r\n src.outputs instanceof Float32Array ? new Float32Array(src.outputs) : [...src.outputs]\r\n );\r\n }\r\n\r\n function retargetRotationTrack(src: NodeRotationTrack): NodeRotationTrack {\r\n const isCubic = src.interpolator.mode === 'cubicspline';\r\n const frameStride = isCubic ? 12 : 4;\r\n const numFrames = (src.interpolator.inputs as Float32Array).length;\r\n const srcOutputs = src.interpolator.outputs as Float32Array;\r\n const newOutputs = new Float32Array(srcOutputs.length);\r\n for (let f = 0; f < numFrames; f++) {\r\n const base = f * frameStride;\r\n newOutputs.set(srcOutputs.subarray(base, base + (isCubic ? 12 : 4)), base);\r\n }\r\n return new NodeRotationTrack(\r\n new Interpolator(\r\n src.interpolator.mode,\r\n 'quat',\r\n new Float32Array(src.interpolator.inputs as Float32Array),\r\n newOutputs\r\n )\r\n );\r\n }\r\n\r\n function retargetEulerToRotationTrack(src: NodeEulerRotationTrack): NodeRotationTrack {\r\n const srcInputs = src.interpolator.inputs as Float32Array;\r\n const srcOutputs = src.interpolator.outputs as Float32Array;\r\n const numFrames = srcInputs.length;\r\n const newOutputs = new Float32Array(numFrames * 3);\r\n for (let f = 0; f < numFrames; f++) {\r\n const base = f * 3;\r\n newOutputs.set(srcOutputs.subarray(base, base + 3), base);\r\n }\r\n return new NodeRotationTrack(\r\n new Interpolator('linear', 'quat', new Float32Array(srcInputs), newOutputs)\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Dispose the animation set and release owned resources.\r\n *\r\n * - Disposes the weak reference to the model.\r\n * - Disposes all registered animation clips.\r\n * - Clears active animations, tracks, and skeleton references.\r\n */\r\n protected onDispose() {\r\n super.onDispose();\r\n for (const k in this._animations) {\r\n this._animations[k]!.dispose();\r\n }\r\n this._animations = {};\r\n this._activeAnimations.clear();\r\n this._activeSkeletons.clear();\r\n this._activeTracks.clear();\r\n }\r\n}\r\n"],"names":["AnimationSet","Disposable","model","_model","_animations","_activeTracks","Map","_activeSkeletons","_activeAnimations","_skeletons","numAnimations","Object","getOwnPropertyNames","length","skeletons","get","name","createAnimation","embedded","console","error","animation","AnimationClip","scene","queueUpdateNode","deleteAnimation","stopAnimation","dispose","getAnimationNames","keys","update","deltaInSeconds","forEach","v","k","fadeOut","fadeOutStart","animateTime","firstFrame","timeAdvance","speedRatio","currentTime","timeDuration","repeatCounter","repeat","alltracks","tracks","filter","track","isPlayingAnimation","weights","map","info","weight","fadeIn","Math","min","states","t","getDuration","calculateState","state","weightedAverage","a","b","mixState","applyState","apply","has","size","getAnimationClip","setAnimationWeight","ani","playAnimation","options","max","set","nodeTracks","blendId","getBlendId","blendedTracks","push","skeleton","findSkeletonById","refcount","playing","delete","id","reset","copyAnimationFrom","sourceSet","animationName","targetName","excludeJoint","destName","sourceClip","srcSkeletonId","srcSkeleton","Skeleton","nodeMap","jointRemapBySrcNode","jointsFiltered","joints","j","srcRootNode","findRootJoint","srcJointsFiltered","sortJoints","dstJointsFiltered","dstSkeleton","ref","find","sk","rootNode","sortedJointsFiltered","every","i","fi","srcJoint","dstJoint","si","indexOf","di","srcLen","bindPose","position","magnitude","dstLen","translationScale","dstNode","srcBindRot","rotation","dstBindRot","dstClip","autoPlay","addSkeleton","persistentId","srcNode","srcTracks","remap","srcTrack","dstTrack","NodeRotationTrack","retargetRotationTrack","NodeEulerRotationTrack","retargetEulerToRotationTrack","NodeTranslationTrack","retargetTranslationTrack","NodeScaleTrack","cloneInterpolator","interpolator","warn","target","jointIndex","addTrack","root","joint","isParentOf","parent","includes","ordered","visited","Set","visit","add","src","abs","scale","srcOutputs","outputs","newOutputs","Float32Array","Interpolator","mode","inputs","isCubic","frameStride","numFrames","f","base","subarray","srcInputs","onDispose","clear"],"mappings":"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"}
|
|
@@ -327,9 +327,9 @@ import { IKUtils } from './ik_utils.js';
|
|
|
327
327
|
// Apply swing-twist constraint to prevent over-rotation
|
|
328
328
|
const swing = new Quaternion();
|
|
329
329
|
const twist = new Quaternion();
|
|
330
|
-
|
|
330
|
+
worldRotation.decomposeSwingTwist(newDirNorm, swing, twist);
|
|
331
331
|
// Get twist angle
|
|
332
|
-
let twistAngle =
|
|
332
|
+
let twistAngle = twist.getTwistAngle(newDirNorm);
|
|
333
333
|
// Get twist constraint for this joint
|
|
334
334
|
const constraint = this._twistConstraints.get(i);
|
|
335
335
|
if (constraint) {
|
|
@@ -339,8 +339,7 @@ import { IKUtils } from './ik_utils.js';
|
|
|
339
339
|
// Store for next frame
|
|
340
340
|
joint.previousTwist = twistAngle;
|
|
341
341
|
// Reconstruct rotation from clamped twist: Q = Twist * Swing
|
|
342
|
-
const clampedTwist =
|
|
343
|
-
IKUtils.createTwist(twistAngle, newDirNorm, clampedTwist);
|
|
342
|
+
const clampedTwist = Quaternion.fromAxisAngle(newDirNorm, twistAngle);
|
|
344
343
|
worldRotation = Quaternion.multiply(clampedTwist, swing, worldRotation);
|
|
345
344
|
// Blend with original rotation based on weight
|
|
346
345
|
if (weight < 1) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ccd_solver.js","sources":["../../../src/animation/ik/ccd_solver.ts"],"sourcesContent":["import { Vector3, Quaternion } from '@zephyr3d/base';\r\nimport { IKSolver } from './ik_solver';\r\nimport type { IKChain } from './ik_chain';\r\nimport { IKUtils } from './ik_utils';\r\nimport type { TwistConstraint } from './ik_constraint';\r\n\r\n/**\r\n * Pole vector configuration for a specific joint.\r\n */\r\ninterface PoleVectorConfig {\r\n /** Pole vector position in world space */\r\n position: Vector3;\r\n /** Weight of the pole vector constraint (0-1) */\r\n weight: number;\r\n}\r\n\r\n/**\r\n * CCD (Cyclic Coordinate Descent) IK solver.\r\n *\r\n * @remarks\r\n * CCD is an iterative IK algorithm that works by:\r\n * 1. Starting from the joint closest to the end effector\r\n * 2. Rotating each joint to point the end effector toward the target\r\n * 3. Moving backward through the chain to the root\r\n * 4. Repeating until convergence or max iterations\r\n *\r\n * CCD is generally faster than FABRIK and works well for chains of any length.\r\n * It's particularly good for tentacles, tails, and other flexible chains.\r\n *\r\n * Supports multiple pole vectors to control the bending direction of different joints.\r\n *\r\n * @public\r\n */\r\nexport class CCDSolver extends IKSolver {\r\n /** Map of joint index to pole vector configuration */\r\n private _poleVectors: Map<number, PoleVectorConfig>;\r\n /** Twist constraints for each joint (indexed by joint index) */\r\n private _twistConstraints: Map<number, TwistConstraint>;\r\n\r\n /**\r\n * Create a CCD solver.\r\n *\r\n * @param chain - The IK chain to solve\r\n * @param maxIterations - Maximum number of iterations (default: 10)\r\n * @param tolerance - Convergence tolerance in world units (default: 0.001)\r\n */\r\n constructor(chain: IKChain, maxIterations = 10, tolerance = 0.001) {\r\n super(chain, maxIterations, tolerance);\r\n this._poleVectors = new Map();\r\n this._twistConstraints = new Map();\r\n\r\n // Set default twist constraints for all joints\r\n for (let i = 0; i < chain.joints.length - 1; i++) {\r\n // Middle joints have more restrictive twist limits\r\n const isMiddleJoint = i > 0 && i < chain.joints.length - 2;\r\n this._twistConstraints.set(i, {\r\n minTwist: isMiddleJoint ? -Math.PI * 0.2 : -Math.PI * 0.3,\r\n maxTwist: isMiddleJoint ? Math.PI * 0.2 : Math.PI * 0.3,\r\n smoothFactor: 0.3\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Set twist constraint for a specific joint.\r\n *\r\n * @param jointIndex - Index of the joint\r\n * @param minTwist - Minimum twist angle in radians\r\n * @param maxTwist - Maximum twist angle in radians\r\n * @param smoothFactor - Smoothing factor [0-1] (default: 0.3)\r\n */\r\n setTwistConstraint(\r\n jointIndex: number,\r\n minTwist: number,\r\n maxTwist: number,\r\n smoothFactor: number = 0.3\r\n ): void {\r\n if (jointIndex < 0 || jointIndex >= this._chain.joints.length - 1) {\r\n throw new Error(`Invalid joint index: ${jointIndex}`);\r\n }\r\n\r\n this._twistConstraints.set(jointIndex, {\r\n minTwist,\r\n maxTwist,\r\n smoothFactor: Math.max(0, Math.min(1, smoothFactor))\r\n });\r\n }\r\n\r\n /**\r\n * Get twist constraint for a specific joint.\r\n *\r\n * @param jointIndex - Index of the joint\r\n * @returns Twist constraint or undefined if not set\r\n */\r\n getTwistConstraint(jointIndex: number): TwistConstraint | undefined {\r\n return this._twistConstraints.get(jointIndex);\r\n }\r\n\r\n /**\r\n * Remove twist constraint for a specific joint.\r\n *\r\n * @param jointIndex - Index of the joint\r\n * @returns True if a constraint was removed\r\n */\r\n removeTwistConstraint(jointIndex: number): boolean {\r\n return this._twistConstraints.delete(jointIndex);\r\n }\r\n\r\n /**\r\n * Clear all twist constraints.\r\n */\r\n clearTwistConstraints(): void {\r\n this._twistConstraints.clear();\r\n }\r\n\r\n /**\r\n * Set pole vector for a specific joint.\r\n *\r\n * @param jointIndex - Index of the joint to apply pole vector to\r\n * @param poleVector - Pole vector position in world space\r\n * @param weight - Weight of the pole vector constraint (0-1, default: 1)\r\n */\r\n setPoleVector(jointIndex: number, poleVector: Vector3, weight = 1): void {\r\n if (jointIndex < 0 || jointIndex >= this._chain.joints.length) {\r\n throw new Error(`Invalid joint index: ${jointIndex}`);\r\n }\r\n\r\n this._poleVectors.set(jointIndex, {\r\n position: poleVector.clone(),\r\n weight: Math.max(0, Math.min(1, weight))\r\n });\r\n }\r\n\r\n /**\r\n * Remove pole vector for a specific joint.\r\n *\r\n * @param jointIndex - Index of the joint to remove pole vector from\r\n * @returns True if a pole vector was removed, false if none existed\r\n */\r\n removePoleVector(jointIndex: number): boolean {\r\n return this._poleVectors.delete(jointIndex);\r\n }\r\n\r\n /**\r\n * Clear all pole vectors.\r\n */\r\n clearPoleVectors(): void {\r\n this._poleVectors.clear();\r\n }\r\n\r\n /**\r\n * Get pole vector configuration for a specific joint.\r\n *\r\n * @param jointIndex - Index of the joint\r\n * @returns Pole vector configuration or undefined if not set\r\n */\r\n getPoleVector(jointIndex: number): { position: Vector3; weight: number } | undefined {\r\n const config = this._poleVectors.get(jointIndex);\r\n if (config) {\r\n return {\r\n position: config.position.clone(),\r\n weight: config.weight\r\n };\r\n }\r\n return undefined;\r\n }\r\n\r\n /**\r\n * Check if a joint has a pole vector.\r\n *\r\n * @param jointIndex - Index of the joint\r\n * @returns True if the joint has a pole vector\r\n */\r\n hasPoleVector(jointIndex: number): boolean {\r\n return this._poleVectors.has(jointIndex);\r\n }\r\n\r\n /**\r\n * Solve the IK chain to reach the target position using CCD algorithm.\r\n *\r\n * @param target - Target position for the end effector\r\n * @returns True if converged within tolerance, false otherwise\r\n */\r\n solve(target: Vector3): boolean {\r\n const joints = this._chain.joints;\r\n\r\n // Update joint positions from scene nodes before solving\r\n this._chain.updateFromNodes();\r\n\r\n // Store original positions\r\n this._chain.storeOriginalPositions();\r\n\r\n let converged = false;\r\n\r\n for (let iteration = 0; iteration < this._maxIterations; iteration++) {\r\n // Check convergence\r\n const endEffector = joints[joints.length - 1];\r\n const distToTarget = Vector3.distance(endEffector.position, target);\r\n\r\n if (distToTarget < this._tolerance) {\r\n converged = true;\r\n break;\r\n }\r\n\r\n // Iterate backward through joints (from second-to-last to root)\r\n // We skip the end effector itself since it doesn't have a child to rotate toward\r\n for (let i = joints.length - 2; i >= 0; i--) {\r\n const joint = joints[i];\r\n\r\n // Vector from current joint to end effector\r\n const toEnd = Vector3.sub(endEffector.position, joint.position, new Vector3());\r\n const toEndDist = toEnd.magnitude;\r\n\r\n // Vector from current joint to target\r\n const toTarget = Vector3.sub(target, joint.position, new Vector3());\r\n const toTargetDist = toTarget.magnitude;\r\n\r\n // Skip if either vector is too small\r\n if (toEndDist < 0.000001 || toTargetDist < 0.000001) {\r\n continue;\r\n }\r\n\r\n // Normalize vectors\r\n const toEndDir = Vector3.scale(toEnd, 1 / toEndDist, new Vector3());\r\n const toTargetDir = Vector3.scale(toTarget, 1 / toTargetDist, new Vector3());\r\n\r\n // Calculate rotation needed to align end effector toward target\r\n let rotation = new Quaternion();\r\n IKUtils.fromToRotation(toEndDir, toTargetDir, rotation);\r\n\r\n // Apply rotation to all joints from current to end effector\r\n this._rotateJointsFromIndex(i, rotation, joint.position);\r\n\r\n // Apply constraints after rotating this joint\r\n this._applyConstraintsForJoint(i);\r\n }\r\n\r\n // Apply all pole vector constraints after each full iteration\r\n // This is more stable than applying them during the CCD loop\r\n this._applyAllPoleVectors();\r\n }\r\n\r\n return converged;\r\n }\r\n\r\n /**\r\n * Rotate all joints from the given index to the end effector around a pivot point.\r\n *\r\n * @param startIndex - Index of the joint to start rotating from\r\n * @param rotation - Rotation to apply\r\n * @param pivot - Pivot point for rotation (position of the joint being adjusted)\r\n */\r\n private _rotateJointsFromIndex(startIndex: number, rotation: Quaternion, pivot: Vector3): void {\r\n const joints = this._chain.joints;\r\n\r\n // Rotate all joints from startIndex+1 to end effector\r\n for (let i = startIndex + 1; i < joints.length; i++) {\r\n const joint = joints[i];\r\n\r\n // Translate to pivot\r\n const offset = Vector3.sub(joint.position, pivot, new Vector3());\r\n\r\n // Rotate around pivot\r\n const rotated = rotation.transform(offset, new Vector3());\r\n\r\n // Translate back\r\n joint.position.set(Vector3.add(pivot, rotated, new Vector3()));\r\n }\r\n }\r\n\r\n /**\r\n * Apply all pole vector constraints.\r\n *\r\n * @remarks\r\n * This method applies all configured pole vectors to their respective joints.\r\n * Pole vectors are applied in reverse order (from end to root) to minimize\r\n * interference between multiple pole vectors. This ensures that pole vectors\r\n * closer to the end effector are not affected by pole vectors closer to the root.\r\n */\r\n private _applyAllPoleVectors(): void {\r\n if (this._poleVectors.size === 0) {\r\n return;\r\n }\r\n\r\n // Apply pole vectors in reverse order (from end to root)\r\n // This prevents earlier pole vectors from affecting later ones\r\n const sortedIndices = Array.from(this._poleVectors.keys()).sort((a, b) => b - a);\r\n\r\n for (const jointIndex of sortedIndices) {\r\n const config = this._poleVectors.get(jointIndex);\r\n if (config && config.weight > 0.001) {\r\n this._applyPoleVectorTwist(jointIndex, config.position, config.weight);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Apply pole vector twist to a specific joint.\r\n *\r\n * @remarks\r\n * This method is called after the base CCD rotation has been applied.\r\n * It adds an additional twist rotation around the joint-to-end axis\r\n * to make the next joint (child) bend toward the pole vector.\r\n *\r\n * @param jointIndex - Index of the pole joint\r\n * @param poleVector - Pole vector position in world space\r\n * @param weight - Weight of the pole vector constraint (0-1)\r\n */\r\n private _applyPoleVectorTwist(jointIndex: number, poleVector: Vector3, weight: number): void {\r\n const joints = this._chain.joints;\r\n\r\n // We need at least 3 joints to apply pole vector (parent, pole joint, child)\r\n if (jointIndex < 1 || jointIndex >= joints.length - 1) {\r\n return;\r\n }\r\n\r\n const parentJoint = joints[jointIndex - 1];\r\n const currentJoint = joints[jointIndex];\r\n const childJoint = joints[jointIndex + 1];\r\n\r\n const parentPos = parentJoint.position;\r\n const currentPos = currentJoint.position;\r\n const childPos = childJoint.position;\r\n\r\n // Calculate the plane defined by parent, child, and pole vector\r\n // Vector from parent to child (the \"line\" we're bending around)\r\n const parentToChild = Vector3.sub(childPos, parentPos, new Vector3());\r\n const lineLength = parentToChild.magnitude;\r\n\r\n if (lineLength < 0.000001) {\r\n return; // Parent and child are at same position\r\n }\r\n\r\n const lineDir = Vector3.scale(parentToChild, 1 / lineLength, new Vector3());\r\n\r\n // Project current joint onto the parent-child line\r\n const parentToCurrent = Vector3.sub(currentPos, parentPos, new Vector3());\r\n const projectionLength = Vector3.dot(parentToCurrent, lineDir);\r\n const projectionPoint = Vector3.scale(lineDir, projectionLength, new Vector3());\r\n Vector3.add(parentPos, projectionPoint, projectionPoint);\r\n\r\n // Vector from projection point to current joint (perpendicular to line)\r\n const perpendicular = Vector3.sub(currentPos, projectionPoint, new Vector3());\r\n const perpLength = perpendicular.magnitude;\r\n\r\n if (perpLength < 0.000001) {\r\n return; // Current joint is on the line\r\n }\r\n\r\n // Calculate desired direction toward pole vector\r\n const parentToPole = Vector3.sub(poleVector, parentPos, new Vector3());\r\n const poleProjectionLength = Vector3.dot(parentToPole, lineDir);\r\n const poleProjectionPoint = Vector3.scale(lineDir, poleProjectionLength, new Vector3());\r\n Vector3.add(parentPos, poleProjectionPoint, poleProjectionPoint);\r\n\r\n // Direction from projection to pole (perpendicular to line)\r\n const poleDirection = Vector3.sub(poleVector, poleProjectionPoint, new Vector3());\r\n const poleDirLength = poleDirection.magnitude;\r\n\r\n if (poleDirLength < 0.000001) {\r\n return; // Pole is on the line\r\n }\r\n\r\n poleDirection.scaleBy(1 / poleDirLength);\r\n\r\n // Calculate new position for current joint\r\n // Keep the same distance from the line, but rotate toward pole direction\r\n const desiredPerpendicular = Vector3.scale(poleDirection, perpLength, new Vector3());\r\n const newPosition = Vector3.add(projectionPoint, desiredPerpendicular, new Vector3());\r\n\r\n // Apply weight (blend between original and constrained position)\r\n if (weight < 0.999) {\r\n const blended = new Vector3();\r\n blended.x = currentPos.x + (newPosition.x - currentPos.x) * weight;\r\n blended.y = currentPos.y + (newPosition.y - currentPos.y) * weight;\r\n blended.z = currentPos.z + (newPosition.z - currentPos.z) * weight;\r\n currentJoint.position.set(blended);\r\n } else {\r\n currentJoint.position.set(newPosition);\r\n }\r\n }\r\n\r\n /**\r\n * Apply constraints for a specific joint.\r\n *\r\n * @param jointIndex - Index of the joint to apply constraints to\r\n */\r\n private _applyConstraintsForJoint(jointIndex: number): void {\r\n const constraints = this._chain.constraints;\r\n for (const constraint of constraints) {\r\n if (constraint.jointIndex === jointIndex) {\r\n constraint.apply(this._chain.joints);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Apply the solved joint positions to scene nodes as rotations.\r\n *\r\n * @param weight - Blend weight (0 = original, 1 = full IK, default: 1)\r\n */\r\n applyToNodes(weight = 1): void {\r\n const joints = this._chain.joints;\r\n\r\n // Apply rotation for all joints except the end effector\r\n for (let i = 0; i < joints.length - 1; i++) {\r\n const joint = joints[i];\r\n const nextJoint = joints[i + 1];\r\n\r\n // Calculate direction from current joint to next joint in world space\r\n const originalDir = Vector3.sub(nextJoint.originalPosition, joint.originalPosition, new Vector3());\r\n const newDir = Vector3.sub(nextJoint.position, joint.position, new Vector3());\r\n\r\n const originalDirNorm = Vector3.normalize(originalDir, new Vector3());\r\n const newDirNorm = Vector3.normalize(newDir, new Vector3());\r\n\r\n // Calculate basic rotation\r\n const deltaRotation = new Quaternion();\r\n IKUtils.fromToRotation(originalDirNorm, newDirNorm, deltaRotation);\r\n\r\n // Calculate new world rotation\r\n let worldRotation = Quaternion.multiply(deltaRotation, joint.originalRotation, new Quaternion());\r\n\r\n // Apply swing-twist constraint to prevent over-rotation\r\n const swing = new Quaternion();\r\n const twist = new Quaternion();\r\n IKUtils.decomposeSwingTwist(worldRotation, newDirNorm, swing, twist);\r\n\r\n // Get twist angle\r\n let twistAngle = IKUtils.getTwistAngle(twist, newDirNorm);\r\n\r\n // Get twist constraint for this joint\r\n const constraint = this._twistConstraints.get(i);\r\n if (constraint) {\r\n // Clamp and smooth twist\r\n twistAngle = IKUtils.clampAndSmoothTwist(\r\n twistAngle,\r\n joint.previousTwist,\r\n constraint.minTwist,\r\n constraint.maxTwist,\r\n constraint.smoothFactor\r\n );\r\n }\r\n\r\n // Store for next frame\r\n joint.previousTwist = twistAngle;\r\n\r\n // Reconstruct rotation from clamped twist: Q = Twist * Swing\r\n const clampedTwist = new Quaternion();\r\n IKUtils.createTwist(twistAngle, newDirNorm, clampedTwist);\r\n worldRotation = Quaternion.multiply(clampedTwist, swing, worldRotation);\r\n\r\n // Blend with original rotation based on weight\r\n if (weight < 1) {\r\n Quaternion.slerp(joint.originalRotation, worldRotation, weight, worldRotation);\r\n }\r\n\r\n // Store this frame's world rotation for next frame's twist continuity\r\n if (!joint.previousIKRotation) {\r\n joint.previousIKRotation = worldRotation.clone();\r\n } else {\r\n joint.previousIKRotation.set(worldRotation);\r\n }\r\n\r\n // Convert world rotation to local rotation (relative to parent)\r\n if (joint.node.parent) {\r\n const parentWorldRotation = new Quaternion();\r\n joint.node.parent.worldMatrix.decompose(null, parentWorldRotation, null);\r\n\r\n // localRotation = conjugate(parentWorldRotation) * worldRotation\r\n const parentInvRotation = Quaternion.conjugate(parentWorldRotation, new Quaternion());\r\n const localRotation = Quaternion.multiply(parentInvRotation, worldRotation, new Quaternion());\r\n\r\n joint.node.rotation = localRotation;\r\n } else {\r\n // Root node has no parent, world rotation is local rotation\r\n joint.node.rotation = worldRotation;\r\n }\r\n }\r\n }\r\n}\r\n"],"names":["CCDSolver","IKSolver","chain","maxIterations","tolerance","_poleVectors","Map","_twistConstraints","i","joints","length","isMiddleJoint","set","minTwist","Math","PI","maxTwist","smoothFactor","setTwistConstraint","jointIndex","_chain","Error","max","min","getTwistConstraint","get","removeTwistConstraint","delete","clearTwistConstraints","clear","setPoleVector","poleVector","weight","position","clone","removePoleVector","clearPoleVectors","getPoleVector","config","undefined","hasPoleVector","has","solve","target","updateFromNodes","storeOriginalPositions","converged","iteration","_maxIterations","endEffector","distToTarget","Vector3","distance","_tolerance","joint","toEnd","sub","toEndDist","magnitude","toTarget","toTargetDist","toEndDir","scale","toTargetDir","rotation","Quaternion","IKUtils","fromToRotation","_rotateJointsFromIndex","_applyConstraintsForJoint","_applyAllPoleVectors","startIndex","pivot","offset","rotated","transform","add","size","sortedIndices","Array","from","keys","sort","a","b","_applyPoleVectorTwist","parentJoint","currentJoint","childJoint","parentPos","currentPos","childPos","parentToChild","lineLength","lineDir","parentToCurrent","projectionLength","dot","projectionPoint","perpendicular","perpLength","parentToPole","poleProjectionLength","poleProjectionPoint","poleDirection","poleDirLength","scaleBy","desiredPerpendicular","newPosition","blended","x","y","z","constraints","constraint","apply","applyToNodes","nextJoint","originalDir","originalPosition","newDir","originalDirNorm","normalize","newDirNorm","deltaRotation","worldRotation","multiply","originalRotation","swing","twist","decomposeSwingTwist","twistAngle","getTwistAngle","clampAndSmoothTwist","previousTwist","clampedTwist","createTwist","slerp","previousIKRotation","node","parent","parentWorldRotation","worldMatrix","decompose","parentInvRotation","conjugate","localRotation"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ccd_solver.js","sources":["../../../src/animation/ik/ccd_solver.ts"],"sourcesContent":["import { Vector3, Quaternion } from '@zephyr3d/base';\r\nimport { IKSolver } from './ik_solver';\r\nimport type { IKChain } from './ik_chain';\r\nimport { IKUtils } from './ik_utils';\r\nimport type { TwistConstraint } from './ik_constraint';\r\n\r\n/**\r\n * Pole vector configuration for a specific joint.\r\n */\r\ninterface PoleVectorConfig {\r\n /** Pole vector position in world space */\r\n position: Vector3;\r\n /** Weight of the pole vector constraint (0-1) */\r\n weight: number;\r\n}\r\n\r\n/**\r\n * CCD (Cyclic Coordinate Descent) IK solver.\r\n *\r\n * @remarks\r\n * CCD is an iterative IK algorithm that works by:\r\n * 1. Starting from the joint closest to the end effector\r\n * 2. Rotating each joint to point the end effector toward the target\r\n * 3. Moving backward through the chain to the root\r\n * 4. Repeating until convergence or max iterations\r\n *\r\n * CCD is generally faster than FABRIK and works well for chains of any length.\r\n * It's particularly good for tentacles, tails, and other flexible chains.\r\n *\r\n * Supports multiple pole vectors to control the bending direction of different joints.\r\n *\r\n * @public\r\n */\r\nexport class CCDSolver extends IKSolver {\r\n /** Map of joint index to pole vector configuration */\r\n private _poleVectors: Map<number, PoleVectorConfig>;\r\n /** Twist constraints for each joint (indexed by joint index) */\r\n private _twistConstraints: Map<number, TwistConstraint>;\r\n\r\n /**\r\n * Create a CCD solver.\r\n *\r\n * @param chain - The IK chain to solve\r\n * @param maxIterations - Maximum number of iterations (default: 10)\r\n * @param tolerance - Convergence tolerance in world units (default: 0.001)\r\n */\r\n constructor(chain: IKChain, maxIterations = 10, tolerance = 0.001) {\r\n super(chain, maxIterations, tolerance);\r\n this._poleVectors = new Map();\r\n this._twistConstraints = new Map();\r\n\r\n // Set default twist constraints for all joints\r\n for (let i = 0; i < chain.joints.length - 1; i++) {\r\n // Middle joints have more restrictive twist limits\r\n const isMiddleJoint = i > 0 && i < chain.joints.length - 2;\r\n this._twistConstraints.set(i, {\r\n minTwist: isMiddleJoint ? -Math.PI * 0.2 : -Math.PI * 0.3,\r\n maxTwist: isMiddleJoint ? Math.PI * 0.2 : Math.PI * 0.3,\r\n smoothFactor: 0.3\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Set twist constraint for a specific joint.\r\n *\r\n * @param jointIndex - Index of the joint\r\n * @param minTwist - Minimum twist angle in radians\r\n * @param maxTwist - Maximum twist angle in radians\r\n * @param smoothFactor - Smoothing factor [0-1] (default: 0.3)\r\n */\r\n setTwistConstraint(\r\n jointIndex: number,\r\n minTwist: number,\r\n maxTwist: number,\r\n smoothFactor: number = 0.3\r\n ): void {\r\n if (jointIndex < 0 || jointIndex >= this._chain.joints.length - 1) {\r\n throw new Error(`Invalid joint index: ${jointIndex}`);\r\n }\r\n\r\n this._twistConstraints.set(jointIndex, {\r\n minTwist,\r\n maxTwist,\r\n smoothFactor: Math.max(0, Math.min(1, smoothFactor))\r\n });\r\n }\r\n\r\n /**\r\n * Get twist constraint for a specific joint.\r\n *\r\n * @param jointIndex - Index of the joint\r\n * @returns Twist constraint or undefined if not set\r\n */\r\n getTwistConstraint(jointIndex: number): TwistConstraint | undefined {\r\n return this._twistConstraints.get(jointIndex);\r\n }\r\n\r\n /**\r\n * Remove twist constraint for a specific joint.\r\n *\r\n * @param jointIndex - Index of the joint\r\n * @returns True if a constraint was removed\r\n */\r\n removeTwistConstraint(jointIndex: number): boolean {\r\n return this._twistConstraints.delete(jointIndex);\r\n }\r\n\r\n /**\r\n * Clear all twist constraints.\r\n */\r\n clearTwistConstraints(): void {\r\n this._twistConstraints.clear();\r\n }\r\n\r\n /**\r\n * Set pole vector for a specific joint.\r\n *\r\n * @param jointIndex - Index of the joint to apply pole vector to\r\n * @param poleVector - Pole vector position in world space\r\n * @param weight - Weight of the pole vector constraint (0-1, default: 1)\r\n */\r\n setPoleVector(jointIndex: number, poleVector: Vector3, weight = 1): void {\r\n if (jointIndex < 0 || jointIndex >= this._chain.joints.length) {\r\n throw new Error(`Invalid joint index: ${jointIndex}`);\r\n }\r\n\r\n this._poleVectors.set(jointIndex, {\r\n position: poleVector.clone(),\r\n weight: Math.max(0, Math.min(1, weight))\r\n });\r\n }\r\n\r\n /**\r\n * Remove pole vector for a specific joint.\r\n *\r\n * @param jointIndex - Index of the joint to remove pole vector from\r\n * @returns True if a pole vector was removed, false if none existed\r\n */\r\n removePoleVector(jointIndex: number): boolean {\r\n return this._poleVectors.delete(jointIndex);\r\n }\r\n\r\n /**\r\n * Clear all pole vectors.\r\n */\r\n clearPoleVectors(): void {\r\n this._poleVectors.clear();\r\n }\r\n\r\n /**\r\n * Get pole vector configuration for a specific joint.\r\n *\r\n * @param jointIndex - Index of the joint\r\n * @returns Pole vector configuration or undefined if not set\r\n */\r\n getPoleVector(jointIndex: number): { position: Vector3; weight: number } | undefined {\r\n const config = this._poleVectors.get(jointIndex);\r\n if (config) {\r\n return {\r\n position: config.position.clone(),\r\n weight: config.weight\r\n };\r\n }\r\n return undefined;\r\n }\r\n\r\n /**\r\n * Check if a joint has a pole vector.\r\n *\r\n * @param jointIndex - Index of the joint\r\n * @returns True if the joint has a pole vector\r\n */\r\n hasPoleVector(jointIndex: number): boolean {\r\n return this._poleVectors.has(jointIndex);\r\n }\r\n\r\n /**\r\n * Solve the IK chain to reach the target position using CCD algorithm.\r\n *\r\n * @param target - Target position for the end effector\r\n * @returns True if converged within tolerance, false otherwise\r\n */\r\n solve(target: Vector3): boolean {\r\n const joints = this._chain.joints;\r\n\r\n // Update joint positions from scene nodes before solving\r\n this._chain.updateFromNodes();\r\n\r\n // Store original positions\r\n this._chain.storeOriginalPositions();\r\n\r\n let converged = false;\r\n\r\n for (let iteration = 0; iteration < this._maxIterations; iteration++) {\r\n // Check convergence\r\n const endEffector = joints[joints.length - 1];\r\n const distToTarget = Vector3.distance(endEffector.position, target);\r\n\r\n if (distToTarget < this._tolerance) {\r\n converged = true;\r\n break;\r\n }\r\n\r\n // Iterate backward through joints (from second-to-last to root)\r\n // We skip the end effector itself since it doesn't have a child to rotate toward\r\n for (let i = joints.length - 2; i >= 0; i--) {\r\n const joint = joints[i];\r\n\r\n // Vector from current joint to end effector\r\n const toEnd = Vector3.sub(endEffector.position, joint.position, new Vector3());\r\n const toEndDist = toEnd.magnitude;\r\n\r\n // Vector from current joint to target\r\n const toTarget = Vector3.sub(target, joint.position, new Vector3());\r\n const toTargetDist = toTarget.magnitude;\r\n\r\n // Skip if either vector is too small\r\n if (toEndDist < 0.000001 || toTargetDist < 0.000001) {\r\n continue;\r\n }\r\n\r\n // Normalize vectors\r\n const toEndDir = Vector3.scale(toEnd, 1 / toEndDist, new Vector3());\r\n const toTargetDir = Vector3.scale(toTarget, 1 / toTargetDist, new Vector3());\r\n\r\n // Calculate rotation needed to align end effector toward target\r\n let rotation = new Quaternion();\r\n IKUtils.fromToRotation(toEndDir, toTargetDir, rotation);\r\n\r\n // Apply rotation to all joints from current to end effector\r\n this._rotateJointsFromIndex(i, rotation, joint.position);\r\n\r\n // Apply constraints after rotating this joint\r\n this._applyConstraintsForJoint(i);\r\n }\r\n\r\n // Apply all pole vector constraints after each full iteration\r\n // This is more stable than applying them during the CCD loop\r\n this._applyAllPoleVectors();\r\n }\r\n\r\n return converged;\r\n }\r\n\r\n /**\r\n * Rotate all joints from the given index to the end effector around a pivot point.\r\n *\r\n * @param startIndex - Index of the joint to start rotating from\r\n * @param rotation - Rotation to apply\r\n * @param pivot - Pivot point for rotation (position of the joint being adjusted)\r\n */\r\n private _rotateJointsFromIndex(startIndex: number, rotation: Quaternion, pivot: Vector3): void {\r\n const joints = this._chain.joints;\r\n\r\n // Rotate all joints from startIndex+1 to end effector\r\n for (let i = startIndex + 1; i < joints.length; i++) {\r\n const joint = joints[i];\r\n\r\n // Translate to pivot\r\n const offset = Vector3.sub(joint.position, pivot, new Vector3());\r\n\r\n // Rotate around pivot\r\n const rotated = rotation.transform(offset, new Vector3());\r\n\r\n // Translate back\r\n joint.position.set(Vector3.add(pivot, rotated, new Vector3()));\r\n }\r\n }\r\n\r\n /**\r\n * Apply all pole vector constraints.\r\n *\r\n * @remarks\r\n * This method applies all configured pole vectors to their respective joints.\r\n * Pole vectors are applied in reverse order (from end to root) to minimize\r\n * interference between multiple pole vectors. This ensures that pole vectors\r\n * closer to the end effector are not affected by pole vectors closer to the root.\r\n */\r\n private _applyAllPoleVectors(): void {\r\n if (this._poleVectors.size === 0) {\r\n return;\r\n }\r\n\r\n // Apply pole vectors in reverse order (from end to root)\r\n // This prevents earlier pole vectors from affecting later ones\r\n const sortedIndices = Array.from(this._poleVectors.keys()).sort((a, b) => b - a);\r\n\r\n for (const jointIndex of sortedIndices) {\r\n const config = this._poleVectors.get(jointIndex);\r\n if (config && config.weight > 0.001) {\r\n this._applyPoleVectorTwist(jointIndex, config.position, config.weight);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Apply pole vector twist to a specific joint.\r\n *\r\n * @remarks\r\n * This method is called after the base CCD rotation has been applied.\r\n * It adds an additional twist rotation around the joint-to-end axis\r\n * to make the next joint (child) bend toward the pole vector.\r\n *\r\n * @param jointIndex - Index of the pole joint\r\n * @param poleVector - Pole vector position in world space\r\n * @param weight - Weight of the pole vector constraint (0-1)\r\n */\r\n private _applyPoleVectorTwist(jointIndex: number, poleVector: Vector3, weight: number): void {\r\n const joints = this._chain.joints;\r\n\r\n // We need at least 3 joints to apply pole vector (parent, pole joint, child)\r\n if (jointIndex < 1 || jointIndex >= joints.length - 1) {\r\n return;\r\n }\r\n\r\n const parentJoint = joints[jointIndex - 1];\r\n const currentJoint = joints[jointIndex];\r\n const childJoint = joints[jointIndex + 1];\r\n\r\n const parentPos = parentJoint.position;\r\n const currentPos = currentJoint.position;\r\n const childPos = childJoint.position;\r\n\r\n // Calculate the plane defined by parent, child, and pole vector\r\n // Vector from parent to child (the \"line\" we're bending around)\r\n const parentToChild = Vector3.sub(childPos, parentPos, new Vector3());\r\n const lineLength = parentToChild.magnitude;\r\n\r\n if (lineLength < 0.000001) {\r\n return; // Parent and child are at same position\r\n }\r\n\r\n const lineDir = Vector3.scale(parentToChild, 1 / lineLength, new Vector3());\r\n\r\n // Project current joint onto the parent-child line\r\n const parentToCurrent = Vector3.sub(currentPos, parentPos, new Vector3());\r\n const projectionLength = Vector3.dot(parentToCurrent, lineDir);\r\n const projectionPoint = Vector3.scale(lineDir, projectionLength, new Vector3());\r\n Vector3.add(parentPos, projectionPoint, projectionPoint);\r\n\r\n // Vector from projection point to current joint (perpendicular to line)\r\n const perpendicular = Vector3.sub(currentPos, projectionPoint, new Vector3());\r\n const perpLength = perpendicular.magnitude;\r\n\r\n if (perpLength < 0.000001) {\r\n return; // Current joint is on the line\r\n }\r\n\r\n // Calculate desired direction toward pole vector\r\n const parentToPole = Vector3.sub(poleVector, parentPos, new Vector3());\r\n const poleProjectionLength = Vector3.dot(parentToPole, lineDir);\r\n const poleProjectionPoint = Vector3.scale(lineDir, poleProjectionLength, new Vector3());\r\n Vector3.add(parentPos, poleProjectionPoint, poleProjectionPoint);\r\n\r\n // Direction from projection to pole (perpendicular to line)\r\n const poleDirection = Vector3.sub(poleVector, poleProjectionPoint, new Vector3());\r\n const poleDirLength = poleDirection.magnitude;\r\n\r\n if (poleDirLength < 0.000001) {\r\n return; // Pole is on the line\r\n }\r\n\r\n poleDirection.scaleBy(1 / poleDirLength);\r\n\r\n // Calculate new position for current joint\r\n // Keep the same distance from the line, but rotate toward pole direction\r\n const desiredPerpendicular = Vector3.scale(poleDirection, perpLength, new Vector3());\r\n const newPosition = Vector3.add(projectionPoint, desiredPerpendicular, new Vector3());\r\n\r\n // Apply weight (blend between original and constrained position)\r\n if (weight < 0.999) {\r\n const blended = new Vector3();\r\n blended.x = currentPos.x + (newPosition.x - currentPos.x) * weight;\r\n blended.y = currentPos.y + (newPosition.y - currentPos.y) * weight;\r\n blended.z = currentPos.z + (newPosition.z - currentPos.z) * weight;\r\n currentJoint.position.set(blended);\r\n } else {\r\n currentJoint.position.set(newPosition);\r\n }\r\n }\r\n\r\n /**\r\n * Apply constraints for a specific joint.\r\n *\r\n * @param jointIndex - Index of the joint to apply constraints to\r\n */\r\n private _applyConstraintsForJoint(jointIndex: number): void {\r\n const constraints = this._chain.constraints;\r\n for (const constraint of constraints) {\r\n if (constraint.jointIndex === jointIndex) {\r\n constraint.apply(this._chain.joints);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Apply the solved joint positions to scene nodes as rotations.\r\n *\r\n * @param weight - Blend weight (0 = original, 1 = full IK, default: 1)\r\n */\r\n applyToNodes(weight = 1): void {\r\n const joints = this._chain.joints;\r\n\r\n // Apply rotation for all joints except the end effector\r\n for (let i = 0; i < joints.length - 1; i++) {\r\n const joint = joints[i];\r\n const nextJoint = joints[i + 1];\r\n\r\n // Calculate direction from current joint to next joint in world space\r\n const originalDir = Vector3.sub(nextJoint.originalPosition, joint.originalPosition, new Vector3());\r\n const newDir = Vector3.sub(nextJoint.position, joint.position, new Vector3());\r\n\r\n const originalDirNorm = Vector3.normalize(originalDir, new Vector3());\r\n const newDirNorm = Vector3.normalize(newDir, new Vector3());\r\n\r\n // Calculate basic rotation\r\n const deltaRotation = new Quaternion();\r\n IKUtils.fromToRotation(originalDirNorm, newDirNorm, deltaRotation);\r\n\r\n // Calculate new world rotation\r\n let worldRotation = Quaternion.multiply(deltaRotation, joint.originalRotation, new Quaternion());\r\n\r\n // Apply swing-twist constraint to prevent over-rotation\r\n const swing = new Quaternion();\r\n const twist = new Quaternion();\r\n worldRotation.decomposeSwingTwist(newDirNorm, swing, twist);\r\n\r\n // Get twist angle\r\n let twistAngle = twist.getTwistAngle(newDirNorm);\r\n\r\n // Get twist constraint for this joint\r\n const constraint = this._twistConstraints.get(i);\r\n if (constraint) {\r\n // Clamp and smooth twist\r\n twistAngle = IKUtils.clampAndSmoothTwist(\r\n twistAngle,\r\n joint.previousTwist,\r\n constraint.minTwist,\r\n constraint.maxTwist,\r\n constraint.smoothFactor\r\n );\r\n }\r\n\r\n // Store for next frame\r\n joint.previousTwist = twistAngle;\r\n\r\n // Reconstruct rotation from clamped twist: Q = Twist * Swing\r\n const clampedTwist = Quaternion.fromAxisAngle(newDirNorm, twistAngle);\r\n worldRotation = Quaternion.multiply(clampedTwist, swing, worldRotation);\r\n\r\n // Blend with original rotation based on weight\r\n if (weight < 1) {\r\n Quaternion.slerp(joint.originalRotation, worldRotation, weight, worldRotation);\r\n }\r\n\r\n // Store this frame's world rotation for next frame's twist continuity\r\n if (!joint.previousIKRotation) {\r\n joint.previousIKRotation = worldRotation.clone();\r\n } else {\r\n joint.previousIKRotation.set(worldRotation);\r\n }\r\n\r\n // Convert world rotation to local rotation (relative to parent)\r\n if (joint.node.parent) {\r\n const parentWorldRotation = new Quaternion();\r\n joint.node.parent.worldMatrix.decompose(null, parentWorldRotation, null);\r\n\r\n // localRotation = conjugate(parentWorldRotation) * worldRotation\r\n const parentInvRotation = Quaternion.conjugate(parentWorldRotation, new Quaternion());\r\n const localRotation = Quaternion.multiply(parentInvRotation, worldRotation, new Quaternion());\r\n\r\n joint.node.rotation = localRotation;\r\n } else {\r\n // Root node has no parent, world rotation is local rotation\r\n joint.node.rotation = worldRotation;\r\n }\r\n }\r\n }\r\n}\r\n"],"names":["CCDSolver","IKSolver","chain","maxIterations","tolerance","_poleVectors","Map","_twistConstraints","i","joints","length","isMiddleJoint","set","minTwist","Math","PI","maxTwist","smoothFactor","setTwistConstraint","jointIndex","_chain","Error","max","min","getTwistConstraint","get","removeTwistConstraint","delete","clearTwistConstraints","clear","setPoleVector","poleVector","weight","position","clone","removePoleVector","clearPoleVectors","getPoleVector","config","undefined","hasPoleVector","has","solve","target","updateFromNodes","storeOriginalPositions","converged","iteration","_maxIterations","endEffector","distToTarget","Vector3","distance","_tolerance","joint","toEnd","sub","toEndDist","magnitude","toTarget","toTargetDist","toEndDir","scale","toTargetDir","rotation","Quaternion","IKUtils","fromToRotation","_rotateJointsFromIndex","_applyConstraintsForJoint","_applyAllPoleVectors","startIndex","pivot","offset","rotated","transform","add","size","sortedIndices","Array","from","keys","sort","a","b","_applyPoleVectorTwist","parentJoint","currentJoint","childJoint","parentPos","currentPos","childPos","parentToChild","lineLength","lineDir","parentToCurrent","projectionLength","dot","projectionPoint","perpendicular","perpLength","parentToPole","poleProjectionLength","poleProjectionPoint","poleDirection","poleDirLength","scaleBy","desiredPerpendicular","newPosition","blended","x","y","z","constraints","constraint","apply","applyToNodes","nextJoint","originalDir","originalPosition","newDir","originalDirNorm","normalize","newDirNorm","deltaRotation","worldRotation","multiply","originalRotation","swing","twist","decomposeSwingTwist","twistAngle","getTwistAngle","clampAndSmoothTwist","previousTwist","clampedTwist","fromAxisAngle","slerp","previousIKRotation","node","parent","parentWorldRotation","worldMatrix","decompose","parentInvRotation","conjugate","localRotation"],"mappings":"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"}
|
|
@@ -182,9 +182,9 @@ import { IKUtils } from './ik_utils.js';
|
|
|
182
182
|
// Apply swing-twist constraint to prevent over-rotation
|
|
183
183
|
const swing = new Quaternion();
|
|
184
184
|
const twist = new Quaternion();
|
|
185
|
-
|
|
185
|
+
worldRotation.decomposeSwingTwist(newDirNorm, swing, twist);
|
|
186
186
|
// Get twist angle
|
|
187
|
-
let twistAngle =
|
|
187
|
+
let twistAngle = twist.getTwistAngle(newDirNorm);
|
|
188
188
|
// Get twist constraint for this joint
|
|
189
189
|
const constraint = this._twistConstraints.get(i);
|
|
190
190
|
if (constraint) {
|
|
@@ -194,8 +194,7 @@ import { IKUtils } from './ik_utils.js';
|
|
|
194
194
|
// Store for next frame
|
|
195
195
|
joint.previousTwist = twistAngle;
|
|
196
196
|
// Reconstruct rotation from clamped twist: Q = Twist * Swing
|
|
197
|
-
const clampedTwist =
|
|
198
|
-
IKUtils.createTwist(twistAngle, newDirNorm, clampedTwist);
|
|
197
|
+
const clampedTwist = Quaternion.fromAxisAngle(newDirNorm, twistAngle);
|
|
199
198
|
worldRotation = Quaternion.multiply(clampedTwist, swing, worldRotation);
|
|
200
199
|
// Blend with original rotation based on weight
|
|
201
200
|
if (weight < 1) {
|