@zephyr3d/scene 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/animation/animation.js +4 -33
- package/dist/animation/animation.js.map +1 -1
- package/dist/animation/animationset.js +24 -11
- package/dist/animation/animationset.js.map +1 -1
- package/dist/animation/animationtrack.js +1 -0
- package/dist/animation/animationtrack.js.map +1 -1
- package/dist/animation/eulerrotationtrack.js +0 -1
- package/dist/animation/eulerrotationtrack.js.map +1 -1
- package/dist/animation/ik/ccd_solver.js +372 -0
- package/dist/animation/ik/ccd_solver.js.map +1 -0
- package/dist/animation/ik/fabrik_solver.js +399 -0
- package/dist/animation/ik/fabrik_solver.js.map +1 -0
- package/dist/animation/ik/ik_angle_constraint.js +181 -0
- package/dist/animation/ik/ik_angle_constraint.js.map +1 -0
- package/dist/animation/ik/ik_chain.js +163 -0
- package/dist/animation/ik/ik_chain.js.map +1 -0
- package/dist/animation/ik/ik_chain_builder.js +68 -0
- package/dist/animation/ik/ik_chain_builder.js.map +1 -0
- package/dist/animation/ik/ik_constraint.js +25 -0
- package/dist/animation/ik/ik_constraint.js.map +1 -0
- package/dist/animation/ik/ik_pole_constraint.js +123 -0
- package/dist/animation/ik/ik_pole_constraint.js.map +1 -0
- package/dist/animation/ik/ik_solver.js +48 -0
- package/dist/animation/ik/ik_solver.js.map +1 -0
- package/dist/animation/ik/ik_track.js +96 -0
- package/dist/animation/ik/ik_track.js.map +1 -0
- package/dist/animation/ik/ik_utils.js +270 -0
- package/dist/animation/ik/ik_utils.js.map +1 -0
- package/dist/animation/ik/two_bone_ik_solver.js +332 -0
- package/dist/animation/ik/two_bone_ik_solver.js.map +1 -0
- package/dist/animation/ik_modifier.js +61 -0
- package/dist/animation/ik_modifier.js.map +1 -0
- package/dist/animation/ik_postprocessor.js +71 -0
- package/dist/animation/ik_postprocessor.js.map +1 -0
- package/dist/animation/manual_transform_processor.js +156 -0
- package/dist/animation/manual_transform_processor.js.map +1 -0
- package/dist/animation/morphtarget.js +7 -1
- package/dist/animation/morphtarget.js.map +1 -1
- package/dist/animation/morphtrack.js +3 -3
- package/dist/animation/morphtrack.js.map +1 -1
- package/dist/animation/proptrack.js +37 -41
- package/dist/animation/proptrack.js.map +1 -1
- package/dist/animation/rotationtrack.js +0 -1
- package/dist/animation/rotationtrack.js.map +1 -1
- package/dist/animation/scaletrack.js +0 -1
- package/dist/animation/scaletrack.js.map +1 -1
- package/dist/animation/skeleton.js +57 -60
- package/dist/animation/skeleton.js.map +1 -1
- package/dist/animation/skeleton_modifier.js +38 -0
- package/dist/animation/skeleton_modifier.js.map +1 -0
- package/dist/animation/skeleton_postprocessor.js +50 -0
- package/dist/animation/skeleton_postprocessor.js.map +1 -0
- package/dist/animation/spring/multi_chain_spring_system.js +503 -0
- package/dist/animation/spring/multi_chain_spring_system.js.map +1 -0
- package/dist/animation/spring/spring_chain.js +103 -0
- package/dist/animation/spring/spring_chain.js.map +1 -0
- package/dist/animation/spring/spring_collider.js +247 -0
- package/dist/animation/spring/spring_collider.js.map +1 -0
- package/dist/animation/spring/spring_constraint.js +21 -0
- package/dist/animation/spring/spring_constraint.js.map +1 -0
- package/dist/animation/spring/spring_particle.js +20 -0
- package/dist/animation/spring/spring_particle.js.map +1 -0
- package/dist/animation/spring/spring_system.js +526 -0
- package/dist/animation/spring/spring_system.js.map +1 -0
- package/dist/animation/spring_modifier.js +45 -0
- package/dist/animation/spring_modifier.js.map +1 -0
- package/dist/animation/spring_postprocessor.js +54 -0
- package/dist/animation/spring_postprocessor.js.map +1 -0
- package/dist/animation/translationtrack.js +0 -1
- package/dist/animation/translationtrack.js.map +1 -1
- package/dist/app/api.js +13 -4
- package/dist/app/api.js.map +1 -1
- package/dist/app/app.js +13 -1
- package/dist/app/app.js.map +1 -1
- package/dist/app/engine.js +33 -45
- package/dist/app/engine.js.map +1 -1
- package/dist/app/inputmgr.js.map +1 -1
- package/dist/app/runtimescript.js.map +1 -1
- package/dist/app/screen.js +318 -0
- package/dist/app/screen.js.map +1 -0
- package/dist/app/scriptingsystem.js +10 -6
- package/dist/app/scriptingsystem.js.map +1 -1
- package/dist/app/scriptregistry.js +11 -22
- package/dist/app/scriptregistry.js.map +1 -1
- package/dist/asset/assetmanager.js +79 -58
- package/dist/asset/assetmanager.js.map +1 -1
- package/dist/asset/builtin.js +5 -5
- package/dist/asset/builtin.js.map +1 -1
- package/dist/asset/loaders/dds/dds.js +6 -2
- package/dist/asset/loaders/dds/dds.js.map +1 -1
- package/dist/asset/loaders/dds/dds_loader.js +1 -1
- package/dist/asset/loaders/dds/dds_loader.js.map +1 -1
- package/dist/asset/loaders/gltf/gltf_loader.js +14 -6
- package/dist/asset/loaders/gltf/gltf_loader.js.map +1 -1
- package/dist/asset/loaders/gltf/helpers.js +6 -2
- package/dist/asset/loaders/gltf/helpers.js.map +1 -1
- package/dist/asset/loaders/hdr/hdr.js +22 -15
- package/dist/asset/loaders/hdr/hdr.js.map +1 -1
- package/dist/asset/loaders/image/tga_Loader.js +2 -1
- package/dist/asset/loaders/image/tga_Loader.js.map +1 -1
- package/dist/asset/loaders/image/webimage_loader.js.map +1 -1
- package/dist/asset/loaders/loader.js +2 -2
- package/dist/asset/loaders/loader.js.map +1 -1
- package/dist/asset/model.js +9 -5
- package/dist/asset/model.js.map +1 -1
- package/dist/blitter/bilateralblur.js.map +1 -1
- package/dist/blitter/blitter.js +9 -11
- package/dist/blitter/blitter.js.map +1 -1
- package/dist/blitter/box.js +0 -1
- package/dist/blitter/box.js.map +1 -1
- package/dist/blitter/copy.js.map +1 -1
- package/dist/blitter/gaussianblur.js.map +1 -1
- package/dist/camera/base.js +1 -3
- package/dist/camera/base.js.map +1 -1
- package/dist/camera/camera.js +121 -37
- package/dist/camera/camera.js.map +1 -1
- package/dist/camera/fps.js +19 -17
- package/dist/camera/fps.js.map +1 -1
- package/dist/camera/orbit.js +45 -43
- package/dist/camera/orbit.js.map +1 -1
- package/dist/camera/orthocamera.js +27 -21
- package/dist/camera/orthocamera.js.map +1 -1
- package/dist/camera/perspectivecamera.js +28 -19
- package/dist/camera/perspectivecamera.js.map +1 -1
- package/dist/index.d.ts +15802 -15577
- package/dist/index.js +33 -11
- package/dist/index.js.map +1 -1
- package/dist/material/blinn.js.map +1 -1
- package/dist/material/grassmaterial.js.map +1 -1
- package/dist/material/lambert.js.map +1 -1
- package/dist/material/material.js +11 -5
- package/dist/material/material.js.map +1 -1
- package/dist/material/meshmaterial.js +10 -9
- package/dist/material/meshmaterial.js.map +1 -1
- package/dist/material/mixins/albedocolor.js.map +1 -1
- package/dist/material/mixins/foliage.js.map +1 -1
- package/dist/material/mixins/lightmodel/blinnphong.js +66 -4
- package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
- package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +7 -0
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
- package/dist/material/mixins/lit.js +3 -3
- package/dist/material/mixins/lit.js.map +1 -1
- package/dist/material/mixins/pbr/brdf.js.map +1 -1
- package/dist/material/mixins/pbr/common.js.map +1 -1
- package/dist/material/mixins/texture.js +3 -0
- package/dist/material/mixins/texture.js.map +1 -1
- package/dist/material/mixins/vertexcolor.js.map +1 -1
- package/dist/material/particle.js.map +1 -1
- package/dist/material/pbrblueprint.js +180 -22
- package/dist/material/pbrblueprint.js.map +1 -1
- package/dist/material/pbrmr.js.map +1 -1
- package/dist/material/pbrsg.js.map +1 -1
- package/dist/material/shader/helper.js +41 -56
- package/dist/material/shader/helper.js.map +1 -1
- package/dist/material/sprite.js +301 -0
- package/dist/material/sprite.js.map +1 -0
- package/dist/material/sprite3d.js +301 -0
- package/dist/material/sprite3d.js.map +1 -0
- package/dist/material/sprite3d_std.js +116 -0
- package/dist/material/sprite3d_std.js.map +1 -0
- package/dist/material/sprite3dblueprint.js +235 -0
- package/dist/material/sprite3dblueprint.js.map +1 -0
- package/dist/material/sprite_std.js +116 -0
- package/dist/material/sprite_std.js.map +1 -0
- package/dist/material/spriteblueprint.js +235 -0
- package/dist/material/spriteblueprint.js.map +1 -0
- package/dist/material/terrain-cm.js +0 -1
- package/dist/material/terrain-cm.js.map +1 -1
- package/dist/material/unlit.js.map +1 -1
- package/dist/material/water.js.map +1 -1
- package/dist/posteffect/bloom.js.map +1 -1
- package/dist/posteffect/compositor.js.map +1 -1
- package/dist/posteffect/fxaa.js.map +1 -1
- package/dist/posteffect/grayscale.js.map +1 -1
- package/dist/posteffect/motionblur.js.map +1 -1
- package/dist/posteffect/posteffect.js.map +1 -1
- package/dist/posteffect/sao.js.map +1 -1
- package/dist/posteffect/ssr.js +7 -5
- package/dist/posteffect/ssr.js.map +1 -1
- package/dist/posteffect/taa.js.map +1 -1
- package/dist/posteffect/tonemap.js.map +1 -1
- package/dist/render/abuffer_oit.js +8 -6
- package/dist/render/abuffer_oit.js.map +1 -1
- package/dist/render/clipmap.js +0 -10
- package/dist/render/clipmap.js.map +1 -1
- package/dist/render/cluster_light.js +4 -4
- package/dist/render/cluster_light.js.map +1 -1
- package/dist/render/cull_visitor.js +15 -17
- package/dist/render/cull_visitor.js.map +1 -1
- package/dist/render/depthpass.js +3 -4
- package/dist/render/depthpass.js.map +1 -1
- package/dist/render/drawable.js +15 -0
- package/dist/render/drawable.js.map +1 -0
- package/dist/render/drawable_mixin.js +8 -4
- package/dist/render/drawable_mixin.js.map +1 -1
- package/dist/render/envlight.js.map +1 -1
- package/dist/render/fbm_wavegenerator.js +3 -1
- package/dist/render/fbm_wavegenerator.js.map +1 -1
- package/dist/render/fft_wavegenerator.js +67 -64
- package/dist/render/fft_wavegenerator.js.map +1 -1
- package/dist/render/fullscreenquad.js.map +1 -1
- package/dist/render/gerstner_wavegenerator.js +3 -1
- package/dist/render/gerstner_wavegenerator.js.map +1 -1
- package/dist/render/globalbindgroup_allocator.js +3 -1
- package/dist/render/globalbindgroup_allocator.js.map +1 -1
- package/dist/render/hzb.js +1 -2
- package/dist/render/hzb.js.map +1 -1
- package/dist/render/lightpass.js +13 -13
- package/dist/render/lightpass.js.map +1 -1
- package/dist/render/objectcolorpass.js +4 -7
- package/dist/render/objectcolorpass.js.map +1 -1
- package/dist/render/primitive.js +4 -5
- package/dist/render/primitive.js.map +1 -1
- package/dist/render/render_queue.js +10 -5
- package/dist/render/render_queue.js.map +1 -1
- package/dist/render/renderer.js +16 -22
- package/dist/render/renderer.js.map +1 -1
- package/dist/render/renderpass.js +27 -52
- package/dist/render/renderpass.js.map +1 -1
- package/dist/render/rendertarget.js +5 -0
- package/dist/render/rendertarget.js.map +1 -0
- package/dist/render/screenrendertarget.js +56 -0
- package/dist/render/screenrendertarget.js.map +1 -0
- package/dist/render/shadowmap_pass.js +4 -5
- package/dist/render/shadowmap_pass.js.map +1 -1
- package/dist/render/sky.js +16 -14
- package/dist/render/sky.js.map +1 -1
- package/dist/render/weightedblended_oit.js.map +1 -1
- package/dist/scene/basesprite.js +296 -0
- package/dist/scene/basesprite.js.map +1 -0
- package/dist/scene/batchgroup.js +4 -2
- package/dist/scene/batchgroup.js.map +1 -1
- package/dist/scene/environment.js +9 -10
- package/dist/scene/environment.js.map +1 -1
- package/dist/scene/graph_node.js.map +1 -1
- package/dist/scene/light.js +22 -4
- package/dist/scene/light.js.map +1 -1
- package/dist/scene/mesh.js +96 -26
- package/dist/scene/mesh.js.map +1 -1
- package/dist/scene/octree.js.map +1 -1
- package/dist/scene/particlesys.js +25 -22
- package/dist/scene/particlesys.js.map +1 -1
- package/dist/scene/raycast_visitor.js +0 -16
- package/dist/scene/raycast_visitor.js.map +1 -1
- package/dist/scene/scene.js +7 -4
- package/dist/scene/scene.js.map +1 -1
- package/dist/scene/scene_node.js +20 -35
- package/dist/scene/scene_node.js.map +1 -1
- package/dist/scene/sprite.js +18 -0
- package/dist/scene/sprite.js.map +1 -0
- package/dist/scene/sprite3d.js +18 -0
- package/dist/scene/sprite3d.js.map +1 -0
- package/dist/scene/terrain-cm/grass.js +5 -4
- package/dist/scene/terrain-cm/grass.js.map +1 -1
- package/dist/scene/terrain-cm/grassmaterial.js +3 -1
- package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
- package/dist/scene/terrain-cm/terrain-cm.js +65 -48
- package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
- package/dist/scene/water.js +35 -30
- package/dist/scene/water.js.map +1 -1
- package/dist/shaders/atmosphere.js +17 -29
- package/dist/shaders/atmosphere.js.map +1 -1
- package/dist/shaders/fog.js.map +1 -1
- package/dist/shaders/misc.js.map +1 -1
- package/dist/shaders/noise.js.map +1 -1
- package/dist/shaders/pbr.js.map +1 -1
- package/dist/shaders/shadow.js.map +1 -1
- package/dist/shaders/ssr.js.map +1 -1
- package/dist/shaders/temporal.js.map +1 -1
- package/dist/shaders/water.js.map +1 -1
- package/dist/shadow/esm.js +1 -1
- package/dist/shadow/esm.js.map +1 -1
- package/dist/shadow/pcf_opt.js.map +1 -1
- package/dist/shadow/pcf_pd.js.map +1 -1
- package/dist/shadow/shader.js +3 -1
- package/dist/shadow/shader.js.map +1 -1
- package/dist/shadow/shadow_impl.js.map +1 -1
- package/dist/shadow/shadowmapper.js +6 -18
- package/dist/shadow/shadowmapper.js.map +1 -1
- package/dist/shadow/ssm.js.map +1 -1
- package/dist/shadow/vsm.js.map +1 -1
- package/dist/shapes/box.js +9 -3
- package/dist/shapes/box.js.map +1 -1
- package/dist/shapes/cylinder.js +2 -2
- package/dist/shapes/cylinder.js.map +1 -1
- package/dist/shapes/plane.js +2 -2
- package/dist/shapes/plane.js.map +1 -1
- package/dist/shapes/shape.js +2 -1
- package/dist/shapes/shape.js.map +1 -1
- package/dist/shapes/sphere.js +2 -2
- package/dist/shapes/sphere.js.map +1 -1
- package/dist/shapes/tetrahedron.js +5 -6
- package/dist/shapes/tetrahedron.js.map +1 -1
- package/dist/shapes/torus.js +2 -2
- package/dist/shapes/torus.js.map +1 -1
- package/dist/utility/aabbtree.js +1 -1
- package/dist/utility/aabbtree.js.map +1 -1
- package/dist/utility/blueprint/common/constants.js +578 -104
- package/dist/utility/blueprint/common/constants.js.map +1 -1
- package/dist/utility/blueprint/common/math.js +471 -9
- package/dist/utility/blueprint/common/math.js.map +1 -1
- package/dist/utility/blueprint/common.js +1 -1
- package/dist/utility/blueprint/common.js.map +1 -1
- package/dist/utility/blueprint/material/func.js +9 -5
- package/dist/utility/blueprint/material/func.js.map +1 -1
- package/dist/utility/blueprint/material/inputs.js.map +1 -1
- package/dist/utility/blueprint/material/ir.js +288 -80
- package/dist/utility/blueprint/material/ir.js.map +1 -1
- package/dist/utility/blueprint/material/pbr.js +92 -4
- package/dist/utility/blueprint/material/pbr.js.map +1 -1
- package/dist/utility/blueprint/material/texture.js +62 -45
- package/dist/utility/blueprint/material/texture.js.map +1 -1
- package/dist/utility/blueprint/node.js +28 -1
- package/dist/utility/blueprint/node.js.map +1 -1
- package/dist/utility/bounding_volume.js.map +1 -1
- package/dist/utility/debug.js.map +1 -1
- package/dist/utility/draco/decoder.js.map +1 -1
- package/dist/utility/misc.js +1 -1
- package/dist/utility/misc.js.map +1 -1
- package/dist/utility/panorama.js.map +1 -1
- package/dist/utility/pmrem.js.map +1 -1
- package/dist/utility/rendermipmap.js.map +1 -1
- package/dist/utility/serialization/json.js +19 -15
- package/dist/utility/serialization/json.js.map +1 -1
- package/dist/utility/serialization/manager.js +89 -22
- package/dist/utility/serialization/manager.js.map +1 -1
- package/dist/utility/serialization/scene/animation.js +44 -41
- package/dist/utility/serialization/scene/animation.js.map +1 -1
- package/dist/utility/serialization/scene/batch.js +4 -3
- package/dist/utility/serialization/scene/batch.js.map +1 -1
- package/dist/utility/serialization/scene/camera.js +97 -9
- package/dist/utility/serialization/scene/camera.js.map +1 -1
- package/dist/utility/serialization/scene/common.js +102 -28
- package/dist/utility/serialization/scene/common.js.map +1 -1
- package/dist/utility/serialization/scene/light.js +14 -12
- package/dist/utility/serialization/scene/light.js.map +1 -1
- package/dist/utility/serialization/scene/material.js +216 -26
- package/dist/utility/serialization/scene/material.js.map +1 -1
- package/dist/utility/serialization/scene/mesh.js +71 -100
- package/dist/utility/serialization/scene/mesh.js.map +1 -1
- package/dist/utility/serialization/scene/misc.js +3 -2
- package/dist/utility/serialization/scene/misc.js.map +1 -1
- package/dist/utility/serialization/scene/node.js +42 -9
- package/dist/utility/serialization/scene/node.js.map +1 -1
- package/dist/utility/serialization/scene/particle.js +7 -5
- package/dist/utility/serialization/scene/particle.js.map +1 -1
- package/dist/utility/serialization/scene/primitive.js +18 -15
- package/dist/utility/serialization/scene/primitive.js.map +1 -1
- package/dist/utility/serialization/scene/scene.js +3 -2
- package/dist/utility/serialization/scene/scene.js.map +1 -1
- package/dist/utility/serialization/scene/sprite.js +164 -0
- package/dist/utility/serialization/scene/sprite.js.map +1 -0
- package/dist/utility/serialization/scene/terrain.js +8 -6
- package/dist/utility/serialization/scene/terrain.js.map +1 -1
- package/dist/utility/serialization/scene/water.js +11 -9
- package/dist/utility/serialization/scene/water.js.map +1 -1
- package/dist/utility/serialization/types.js +6 -0
- package/dist/utility/serialization/types.js.map +1 -0
- package/dist/utility/shprojector.js.map +1 -1
- package/dist/utility/textures/ggxlut.js.map +1 -1
- package/dist/utility/textures/gradientnoise.js.map +1 -1
- package/dist/utility/textures/randomnoise.js.map +1 -1
- package/dist/values.js +14 -14
- package/dist/values.js.map +1 -1
- package/package.json +80 -80
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ik_angle_constraint.js","sources":["../../../src/animation/ik/ik_angle_constraint.ts"],"sourcesContent":["import { Vector3 } from '@zephyr3d/base';\r\nimport { IKConstraint } from './ik_constraint';\r\nimport type { IKJoint } from './ik_joint';\r\n\r\n/**\r\n * Angle constraint for IK joints.\r\n *\r\n * Limits the angle between the bone from parent to this joint\r\n * and the bone from this joint to child.\r\n *\r\n * @remarks\r\n * This is useful for joints like elbows and knees that have\r\n * limited range of motion.\r\n *\r\n * @public\r\n */\r\nexport class IKAngleConstraint extends IKConstraint {\r\n /** Minimum angle in degrees */\r\n private _minAngle: number;\r\n /** Maximum angle in degrees */\r\n private _maxAngle: number;\r\n\r\n /**\r\n * Create an angle constraint.\r\n *\r\n * @param jointIndex - Index of the joint in the chain\r\n * @param minAngle - Minimum angle in degrees (0 = straight)\r\n * @param maxAngle - Maximum angle in degrees (180 = fully bent)\r\n */\r\n constructor(jointIndex: number, minAngle: number, maxAngle: number) {\r\n super(jointIndex);\r\n this._minAngle = Math.max(0, minAngle);\r\n this._maxAngle = Math.min(180, maxAngle);\r\n\r\n if (this._minAngle > this._maxAngle) {\r\n throw new Error('IKAngleConstraint: minAngle must be <= maxAngle');\r\n }\r\n }\r\n\r\n /**\r\n * Get the minimum angle in degrees.\r\n */\r\n get minAngle(): number {\r\n return this._minAngle;\r\n }\r\n\r\n /**\r\n * Set the minimum angle in degrees.\r\n */\r\n set minAngle(value: number) {\r\n this._minAngle = Math.max(0, Math.min(value, this._maxAngle));\r\n }\r\n\r\n /**\r\n * Get the maximum angle in degrees.\r\n */\r\n get maxAngle(): number {\r\n return this._maxAngle;\r\n }\r\n\r\n /**\r\n * Set the maximum angle in degrees.\r\n */\r\n set maxAngle(value: number) {\r\n this._maxAngle = Math.min(180, Math.max(value, this._minAngle));\r\n }\r\n\r\n /**\r\n * Apply the angle constraint to limit joint bending.\r\n */\r\n apply(joints: IKJoint[]): void {\r\n const index = this._jointIndex;\r\n\r\n // For end effector (last joint), we constrain the angle between the last two bones\r\n // For other joints, we constrain the angle at the joint itself\r\n if (index === joints.length - 1) {\r\n // End effector: constrain angle between (parent-1 -> parent) and (parent -> this)\r\n if (index < 2) {\r\n return; // Need at least 3 joints total\r\n }\r\n this._applyEndEffectorConstraint(joints, index);\r\n } else {\r\n // Regular joint: constrain angle between (parent -> this) and (this -> child)\r\n if (index < 1) {\r\n return; // Root joint cannot be constrained this way\r\n }\r\n this._applyRegularConstraint(joints, index);\r\n }\r\n }\r\n\r\n /**\r\n * Apply constraint to end effector joint.\r\n */\r\n private _applyEndEffectorConstraint(joints: IKJoint[], index: number): void {\r\n const grandParentJoint = joints[index - 2];\r\n const parentJoint = joints[index - 1];\r\n const currentJoint = joints[index];\r\n\r\n // Calculate bone vectors\r\n const incomingBone = Vector3.sub(parentJoint.position, grandParentJoint.position, new Vector3());\r\n const outgoingBone = Vector3.sub(currentJoint.position, parentJoint.position, new Vector3());\r\n\r\n const incomingLen = incomingBone.magnitude;\r\n const outgoingLen = outgoingBone.magnitude;\r\n\r\n if (incomingLen < 0.000001 || outgoingLen < 0.000001) {\r\n return;\r\n }\r\n\r\n // Normalize\r\n const incomingNorm = Vector3.scale(incomingBone, 1 / incomingLen, new Vector3());\r\n const outgoingNorm = Vector3.scale(outgoingBone, 1 / outgoingLen, new Vector3());\r\n\r\n // Calculate current angle\r\n const dot = Vector3.dot(incomingNorm, outgoingNorm);\r\n const currentAngleDeg = Math.acos(Math.max(-1, Math.min(1, dot))) * (180 / Math.PI);\r\n\r\n // Check if constraint is violated\r\n let targetAngleDeg = currentAngleDeg;\r\n if (currentAngleDeg < this._minAngle) {\r\n targetAngleDeg = this._minAngle;\r\n } else if (currentAngleDeg > this._maxAngle) {\r\n targetAngleDeg = this._maxAngle;\r\n } else {\r\n return;\r\n }\r\n\r\n // Apply the same constraint logic as regular joints\r\n this._adjustJointAngle(\r\n incomingNorm,\r\n outgoingNorm,\r\n currentAngleDeg,\r\n targetAngleDeg,\r\n parentJoint,\r\n currentJoint\r\n );\r\n }\r\n\r\n /**\r\n * Apply constraint to regular joint.\r\n */\r\n private _applyRegularConstraint(joints: IKJoint[], index: number): void {\r\n const parentJoint = joints[index - 1];\r\n const currentJoint = joints[index];\r\n const childJoint = joints[index + 1];\r\n\r\n // Calculate bone vectors: incoming bone (parent->current) and outgoing bone (current->child)\r\n const incomingBone = Vector3.sub(currentJoint.position, parentJoint.position, new Vector3());\r\n const outgoingBone = Vector3.sub(childJoint.position, currentJoint.position, new Vector3());\r\n\r\n const incomingLen = incomingBone.magnitude;\r\n const outgoingLen = outgoingBone.magnitude;\r\n\r\n if (incomingLen < 0.000001 || outgoingLen < 0.000001) {\r\n return;\r\n }\r\n\r\n // Normalize\r\n const incomingNorm = Vector3.scale(incomingBone, 1 / incomingLen, new Vector3());\r\n const outgoingNorm = Vector3.scale(outgoingBone, 1 / outgoingLen, new Vector3());\r\n\r\n // Calculate current angle between incoming and outgoing bones (interior angle)\r\n const dot = Vector3.dot(incomingNorm, outgoingNorm);\r\n const currentAngleDeg = Math.acos(Math.max(-1, Math.min(1, dot))) * (180 / Math.PI);\r\n\r\n // Check if constraint is violated\r\n let targetAngleDeg = currentAngleDeg;\r\n if (currentAngleDeg < this._minAngle) {\r\n targetAngleDeg = this._minAngle;\r\n } else if (currentAngleDeg > this._maxAngle) {\r\n targetAngleDeg = this._maxAngle;\r\n } else {\r\n return;\r\n }\r\n\r\n // Apply the constraint\r\n this._adjustJointAngle(\r\n incomingNorm,\r\n outgoingNorm,\r\n currentAngleDeg,\r\n targetAngleDeg,\r\n currentJoint,\r\n childJoint\r\n );\r\n }\r\n\r\n /**\r\n * Adjust joint angle by rotating the outgoing bone.\r\n */\r\n private _adjustJointAngle(\r\n incomingNorm: Vector3,\r\n outgoingNorm: Vector3,\r\n currentAngleDeg: number,\r\n targetAngleDeg: number,\r\n pivotJoint: IKJoint,\r\n targetJoint: IKJoint\r\n ): void {\r\n // Add a dead zone to prevent oscillation\r\n const angleDifference = Math.abs(targetAngleDeg - currentAngleDeg);\r\n const isHardConstraint = Math.abs(this._maxAngle - this._minAngle) < 0.01;\r\n const deadZone = isHardConstraint ? 0.2 : 0.1;\r\n\r\n if (angleDifference < deadZone) {\r\n return; // Too close to target, avoid over-correction\r\n }\r\n\r\n // Calculate rotation axis (perpendicular to both bone vectors)\r\n const axis = Vector3.cross(incomingNorm, outgoingNorm, new Vector3());\r\n const axisLen = axis.magnitude;\r\n\r\n // Use a smaller threshold to allow constraint to work at small angles\r\n if (axisLen < 0.0001) {\r\n return; // Vectors are too close to parallel or anti-parallel\r\n }\r\n\r\n axis.scaleBy(1 / axisLen);\r\n\r\n // Calculate the angle we need to rotate\r\n const targetAngleRad = targetAngleDeg * (Math.PI / 180);\r\n const currentAngleRad = currentAngleDeg * (Math.PI / 180);\r\n let deltaAngle = targetAngleRad - currentAngleRad;\r\n\r\n // Apply full correction without damping to ensure constraints are enforced\r\n // This is necessary because FABRIK will try to undo the constraint in the next iteration\r\n // So we need strong constraint enforcement\r\n\r\n // Rotate the outgoing bone vector around the axis by deltaAngle\r\n const cos = Math.cos(deltaAngle);\r\n const sin = Math.sin(deltaAngle);\r\n const oneMinusCos = 1 - cos;\r\n\r\n // Rodrigues' rotation formula\r\n const rotated = new Vector3();\r\n rotated.x =\r\n outgoingNorm.x * (cos + axis.x * axis.x * oneMinusCos) +\r\n outgoingNorm.y * (axis.x * axis.y * oneMinusCos - axis.z * sin) +\r\n outgoingNorm.z * (axis.x * axis.z * oneMinusCos + axis.y * sin);\r\n\r\n rotated.y =\r\n outgoingNorm.x * (axis.y * axis.x * oneMinusCos + axis.z * sin) +\r\n outgoingNorm.y * (cos + axis.y * axis.y * oneMinusCos) +\r\n outgoingNorm.z * (axis.y * axis.z * oneMinusCos - axis.x * sin);\r\n\r\n rotated.z =\r\n outgoingNorm.x * (axis.z * axis.x * oneMinusCos - axis.y * sin) +\r\n outgoingNorm.y * (axis.z * axis.y * oneMinusCos + axis.x * sin) +\r\n outgoingNorm.z * (cos + axis.z * axis.z * oneMinusCos);\r\n\r\n // Update target joint position using the rotated bone vector\r\n const boneLength = pivotJoint.boneLength;\r\n rotated.scaleBy(boneLength);\r\n Vector3.add(pivotJoint.position, rotated, targetJoint.position);\r\n\r\n // Note: We do NOT recursively update subsequent joints here.\r\n // Instead, we let FABRIK algorithm handle the propagation in subsequent iterations.\r\n // This ensures that each joint's constraint is properly applied.\r\n }\r\n}\r\n"],"names":["IKAngleConstraint","IKConstraint","jointIndex","minAngle","maxAngle","_minAngle","Math","max","_maxAngle","min","Error","value","apply","joints","index","_jointIndex","length","_applyEndEffectorConstraint","_applyRegularConstraint","grandParentJoint","parentJoint","currentJoint","incomingBone","Vector3","sub","position","outgoingBone","incomingLen","magnitude","outgoingLen","incomingNorm","scale","outgoingNorm","dot","currentAngleDeg","acos","PI","targetAngleDeg","_adjustJointAngle","childJoint","pivotJoint","targetJoint","angleDifference","abs","isHardConstraint","deadZone","axis","cross","axisLen","scaleBy","targetAngleRad","currentAngleRad","deltaAngle","cos","sin","oneMinusCos","rotated","x","y","z","boneLength","add"],"mappings":"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"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { Vector3, Quaternion } from '@zephyr3d/base';
|
|
2
|
+
import { IKUtils } from './ik_utils.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Represents a chain of joints for IK solving.
|
|
6
|
+
*
|
|
7
|
+
* @public
|
|
8
|
+
*/ class IKChain {
|
|
9
|
+
/** Array of joints in the chain (root to end effector) */ _joints;
|
|
10
|
+
/** Total length of the chain */ _totalLength;
|
|
11
|
+
/** Constraints applied to joints */ _constraints;
|
|
12
|
+
/**
|
|
13
|
+
* Create an IK chain from an array of scene nodes.
|
|
14
|
+
*
|
|
15
|
+
* @param nodes - Array of scene nodes from root to end effector
|
|
16
|
+
*/ constructor(nodes){
|
|
17
|
+
if (nodes.length < 2) {
|
|
18
|
+
throw new Error('IK chain must have at least 2 joints');
|
|
19
|
+
}
|
|
20
|
+
this._joints = [];
|
|
21
|
+
this._totalLength = 0;
|
|
22
|
+
this._constraints = [];
|
|
23
|
+
// Create joints from nodes
|
|
24
|
+
for(let i = 0; i < nodes.length; i++){
|
|
25
|
+
const node = nodes[i];
|
|
26
|
+
const position = new Vector3();
|
|
27
|
+
const rotation = new Quaternion();
|
|
28
|
+
node.worldMatrix.decompose(null, rotation, position);
|
|
29
|
+
let boneLength = 0;
|
|
30
|
+
if (i < nodes.length - 1) {
|
|
31
|
+
const nextPosition = new Vector3();
|
|
32
|
+
nodes[i + 1].worldMatrix.decompose(null, null, nextPosition);
|
|
33
|
+
boneLength = IKUtils.calculateBoneLength(position, nextPosition);
|
|
34
|
+
this._totalLength += boneLength;
|
|
35
|
+
}
|
|
36
|
+
this._joints.push({
|
|
37
|
+
node,
|
|
38
|
+
position: position.clone(),
|
|
39
|
+
originalPosition: position.clone(),
|
|
40
|
+
rotation: rotation.clone(),
|
|
41
|
+
originalRotation: rotation.clone(),
|
|
42
|
+
boneLength
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Create an IK chain by traversing from a start node to an end node.
|
|
48
|
+
*
|
|
49
|
+
* @remarks
|
|
50
|
+
* This method walks up the parent chain from the end node until it
|
|
51
|
+
* reaches the start node, building the joint chain in the process.
|
|
52
|
+
*
|
|
53
|
+
* @param startNode - The root joint of the chain
|
|
54
|
+
* @param endNode - The end effector joint
|
|
55
|
+
* @returns The created IK chain
|
|
56
|
+
*/ static fromNodeHierarchy(startNode, endNode) {
|
|
57
|
+
const nodes = [];
|
|
58
|
+
let current = endNode;
|
|
59
|
+
// Walk up the hierarchy from end to start
|
|
60
|
+
while(current){
|
|
61
|
+
nodes.unshift(current); // Add to front
|
|
62
|
+
if (current === startNode) {
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
current = current.parent;
|
|
66
|
+
}
|
|
67
|
+
if (nodes[0] !== startNode) {
|
|
68
|
+
throw new Error('End node is not a descendant of start node');
|
|
69
|
+
}
|
|
70
|
+
if (nodes.length < 2) {
|
|
71
|
+
throw new Error('IK chain must have at least 2 joints');
|
|
72
|
+
}
|
|
73
|
+
return new IKChain(nodes);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get all joints in the chain.
|
|
77
|
+
*/ get joints() {
|
|
78
|
+
return this._joints;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get the root joint (first joint in the chain).
|
|
82
|
+
*/ get root() {
|
|
83
|
+
return this._joints[0];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get the end effector (last joint in the chain).
|
|
87
|
+
*/ get endEffector() {
|
|
88
|
+
return this._joints[this._joints.length - 1];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get the total length of the chain.
|
|
92
|
+
*/ get totalLength() {
|
|
93
|
+
return this._totalLength;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get the number of joints in the chain.
|
|
97
|
+
*/ get length() {
|
|
98
|
+
return this._joints.length;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Update joint positions from their scene nodes.
|
|
102
|
+
*/ updateFromNodes() {
|
|
103
|
+
for (const joint of this._joints){
|
|
104
|
+
joint.node.worldMatrix.decompose(null, joint.rotation, joint.position);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Store current positions as original positions.
|
|
109
|
+
*/ storeOriginalPositions() {
|
|
110
|
+
for (const joint of this._joints){
|
|
111
|
+
joint.originalPosition.set(joint.position);
|
|
112
|
+
joint.originalRotation.set(joint.rotation);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Restore joints to their original positions.
|
|
117
|
+
*/ restoreOriginalPositions() {
|
|
118
|
+
for (const joint of this._joints){
|
|
119
|
+
joint.position.set(joint.originalPosition);
|
|
120
|
+
joint.rotation.set(joint.originalRotation);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Add a constraint to a specific joint.
|
|
125
|
+
*
|
|
126
|
+
* @param constraint - The constraint to add
|
|
127
|
+
*/ addConstraint(constraint) {
|
|
128
|
+
this._constraints.push(constraint);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Remove a constraint from the chain.
|
|
132
|
+
*
|
|
133
|
+
* @param constraint - The constraint to remove
|
|
134
|
+
* @returns True if the constraint was found and removed
|
|
135
|
+
*/ removeConstraint(constraint) {
|
|
136
|
+
const index = this._constraints.indexOf(constraint);
|
|
137
|
+
if (index >= 0) {
|
|
138
|
+
this._constraints.splice(index, 1);
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Clear all constraints from the chain.
|
|
145
|
+
*/ clearConstraints() {
|
|
146
|
+
this._constraints = [];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get all constraints in the chain.
|
|
150
|
+
*/ get constraints() {
|
|
151
|
+
return this._constraints;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Apply all constraints to the joint chain.
|
|
155
|
+
*/ applyConstraints() {
|
|
156
|
+
for (const constraint of this._constraints){
|
|
157
|
+
constraint.apply(this._joints);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export { IKChain };
|
|
163
|
+
//# sourceMappingURL=ik_chain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ik_chain.js","sources":["../../../src/animation/ik/ik_chain.ts"],"sourcesContent":["import { Vector3, Quaternion } from '@zephyr3d/base';\r\nimport type { IKJoint } from './ik_joint';\r\nimport type { SceneNode } from '../../scene/scene_node';\r\nimport type { IKConstraint } from './ik_constraint';\r\nimport { IKUtils } from './ik_utils';\r\n\r\n/**\r\n * Represents a chain of joints for IK solving.\r\n *\r\n * @public\r\n */\r\nexport class IKChain {\r\n /** Array of joints in the chain (root to end effector) */\r\n private _joints: IKJoint[];\r\n /** Total length of the chain */\r\n private _totalLength: number;\r\n /** Constraints applied to joints */\r\n private _constraints: IKConstraint[];\r\n\r\n /**\r\n * Create an IK chain from an array of scene nodes.\r\n *\r\n * @param nodes - Array of scene nodes from root to end effector\r\n */\r\n constructor(nodes: SceneNode[]) {\r\n if (nodes.length < 2) {\r\n throw new Error('IK chain must have at least 2 joints');\r\n }\r\n\r\n this._joints = [];\r\n this._totalLength = 0;\r\n this._constraints = [];\r\n\r\n // Create joints from nodes\r\n for (let i = 0; i < nodes.length; i++) {\r\n const node = nodes[i];\r\n const position = new Vector3();\r\n const rotation = new Quaternion();\r\n node.worldMatrix.decompose(null, rotation, position);\r\n\r\n let boneLength = 0;\r\n if (i < nodes.length - 1) {\r\n const nextPosition = new Vector3();\r\n nodes[i + 1].worldMatrix.decompose(null, null, nextPosition);\r\n boneLength = IKUtils.calculateBoneLength(position, nextPosition);\r\n this._totalLength += boneLength;\r\n }\r\n\r\n this._joints.push({\r\n node,\r\n position: position.clone(),\r\n originalPosition: position.clone(),\r\n rotation: rotation.clone(),\r\n originalRotation: rotation.clone(),\r\n boneLength\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Create an IK chain by traversing from a start node to an end node.\r\n *\r\n * @remarks\r\n * This method walks up the parent chain from the end node until it\r\n * reaches the start node, building the joint chain in the process.\r\n *\r\n * @param startNode - The root joint of the chain\r\n * @param endNode - The end effector joint\r\n * @returns The created IK chain\r\n */\r\n static fromNodeHierarchy(startNode: SceneNode, endNode: SceneNode): IKChain {\r\n const nodes: SceneNode[] = [];\r\n let current: SceneNode | null = endNode;\r\n\r\n // Walk up the hierarchy from end to start\r\n while (current) {\r\n nodes.unshift(current); // Add to front\r\n\r\n if (current === startNode) {\r\n break;\r\n }\r\n\r\n current = current.parent as SceneNode | null;\r\n }\r\n\r\n if (nodes[0] !== startNode) {\r\n throw new Error('End node is not a descendant of start node');\r\n }\r\n\r\n if (nodes.length < 2) {\r\n throw new Error('IK chain must have at least 2 joints');\r\n }\r\n\r\n return new IKChain(nodes);\r\n }\r\n\r\n /**\r\n * Get all joints in the chain.\r\n */\r\n get joints(): IKJoint[] {\r\n return this._joints;\r\n }\r\n\r\n /**\r\n * Get the root joint (first joint in the chain).\r\n */\r\n get root(): IKJoint {\r\n return this._joints[0];\r\n }\r\n\r\n /**\r\n * Get the end effector (last joint in the chain).\r\n */\r\n get endEffector(): IKJoint {\r\n return this._joints[this._joints.length - 1];\r\n }\r\n\r\n /**\r\n * Get the total length of the chain.\r\n */\r\n get totalLength(): number {\r\n return this._totalLength;\r\n }\r\n\r\n /**\r\n * Get the number of joints in the chain.\r\n */\r\n get length(): number {\r\n return this._joints.length;\r\n }\r\n\r\n /**\r\n * Update joint positions from their scene nodes.\r\n */\r\n updateFromNodes(): void {\r\n for (const joint of this._joints) {\r\n joint.node.worldMatrix.decompose(null, joint.rotation, joint.position);\r\n }\r\n }\r\n\r\n /**\r\n * Store current positions as original positions.\r\n */\r\n storeOriginalPositions(): void {\r\n for (const joint of this._joints) {\r\n joint.originalPosition.set(joint.position);\r\n joint.originalRotation.set(joint.rotation);\r\n }\r\n }\r\n\r\n /**\r\n * Restore joints to their original positions.\r\n */\r\n restoreOriginalPositions(): void {\r\n for (const joint of this._joints) {\r\n joint.position.set(joint.originalPosition);\r\n joint.rotation.set(joint.originalRotation);\r\n }\r\n }\r\n\r\n /**\r\n * Add a constraint to a specific joint.\r\n *\r\n * @param constraint - The constraint to add\r\n */\r\n addConstraint(constraint: IKConstraint): void {\r\n this._constraints.push(constraint);\r\n }\r\n\r\n /**\r\n * Remove a constraint from the chain.\r\n *\r\n * @param constraint - The constraint to remove\r\n * @returns True if the constraint was found and removed\r\n */\r\n removeConstraint(constraint: IKConstraint): boolean {\r\n const index = this._constraints.indexOf(constraint);\r\n if (index >= 0) {\r\n this._constraints.splice(index, 1);\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Clear all constraints from the chain.\r\n */\r\n clearConstraints(): void {\r\n this._constraints = [];\r\n }\r\n\r\n /**\r\n * Get all constraints in the chain.\r\n */\r\n get constraints(): readonly IKConstraint[] {\r\n return this._constraints;\r\n }\r\n\r\n /**\r\n * Apply all constraints to the joint chain.\r\n */\r\n applyConstraints(): void {\r\n for (const constraint of this._constraints) {\r\n constraint.apply(this._joints);\r\n }\r\n }\r\n}\r\n"],"names":["IKChain","nodes","length","Error","_joints","_totalLength","_constraints","i","node","position","Vector3","rotation","Quaternion","worldMatrix","decompose","boneLength","nextPosition","IKUtils","calculateBoneLength","push","clone","originalPosition","originalRotation","fromNodeHierarchy","startNode","endNode","current","unshift","parent","joints","root","endEffector","totalLength","updateFromNodes","joint","storeOriginalPositions","set","restoreOriginalPositions","addConstraint","constraint","removeConstraint","index","indexOf","splice","clearConstraints","constraints","applyConstraints","apply"],"mappings":";;;AAMA;;;;AAIC,IACM,MAAMA,OAAAA,CAAAA;+DAEX,OAA2B;qCAE3B,YAA6B;yCAE7B,YAAqC;AAErC;;;;MAKA,WAAA,CAAYC,KAAkB,CAAE;QAC9B,IAAIA,KAAAA,CAAMC,MAAM,GAAG,CAAG,EAAA;AACpB,YAAA,MAAM,IAAIC,KAAM,CAAA,sCAAA,CAAA;AAClB;QAEA,IAAI,CAACC,OAAO,GAAG,EAAE;QACjB,IAAI,CAACC,YAAY,GAAG,CAAA;QACpB,IAAI,CAACC,YAAY,GAAG,EAAE;;AAGtB,QAAA,IAAK,IAAIC,CAAI,GAAA,CAAA,EAAGA,IAAIN,KAAMC,CAAAA,MAAM,EAAEK,CAAK,EAAA,CAAA;YACrC,MAAMC,IAAAA,GAAOP,KAAK,CAACM,CAAE,CAAA;AACrB,YAAA,MAAME,WAAW,IAAIC,OAAAA,EAAAA;AACrB,YAAA,MAAMC,WAAW,IAAIC,UAAAA,EAAAA;AACrBJ,YAAAA,IAAAA,CAAKK,WAAW,CAACC,SAAS,CAAC,MAAMH,QAAUF,EAAAA,QAAAA,CAAAA;AAE3C,YAAA,IAAIM,UAAa,GAAA,CAAA;AACjB,YAAA,IAAIR,CAAIN,GAAAA,KAAAA,CAAMC,MAAM,GAAG,CAAG,EAAA;AACxB,gBAAA,MAAMc,eAAe,IAAIN,OAAAA,EAAAA;gBACzBT,KAAK,CAACM,IAAI,CAAE,CAAA,CAACM,WAAW,CAACC,SAAS,CAAC,IAAA,EAAM,IAAME,EAAAA,YAAAA,CAAAA;gBAC/CD,UAAaE,GAAAA,OAAAA,CAAQC,mBAAmB,CAACT,QAAUO,EAAAA,YAAAA,CAAAA;gBACnD,IAAI,CAACX,YAAY,IAAIU,UAAAA;AACvB;AAEA,YAAA,IAAI,CAACX,OAAO,CAACe,IAAI,CAAC;AAChBX,gBAAAA,IAAAA;AACAC,gBAAAA,QAAAA,EAAUA,SAASW,KAAK,EAAA;AACxBC,gBAAAA,gBAAAA,EAAkBZ,SAASW,KAAK,EAAA;AAChCT,gBAAAA,QAAAA,EAAUA,SAASS,KAAK,EAAA;AACxBE,gBAAAA,gBAAAA,EAAkBX,SAASS,KAAK,EAAA;AAChCL,gBAAAA;AACF,aAAA,CAAA;AACF;AACF;AAEA;;;;;;;;;;AAUC,MACD,OAAOQ,iBAAAA,CAAkBC,SAAoB,EAAEC,OAAkB,EAAW;AAC1E,QAAA,MAAMxB,QAAqB,EAAE;AAC7B,QAAA,IAAIyB,OAA4BD,GAAAA,OAAAA;;AAGhC,QAAA,MAAOC,OAAS,CAAA;YACdzB,KAAM0B,CAAAA,OAAO,CAACD,OAAAA,CAAAA,CAAAA;AAEd,YAAA,IAAIA,YAAYF,SAAW,EAAA;AACzB,gBAAA;AACF;AAEAE,YAAAA,OAAAA,GAAUA,QAAQE,MAAM;AAC1B;AAEA,QAAA,IAAI3B,KAAK,CAAC,CAAE,CAAA,KAAKuB,SAAW,EAAA;AAC1B,YAAA,MAAM,IAAIrB,KAAM,CAAA,4CAAA,CAAA;AAClB;QAEA,IAAIF,KAAAA,CAAMC,MAAM,GAAG,CAAG,EAAA;AACpB,YAAA,MAAM,IAAIC,KAAM,CAAA,sCAAA,CAAA;AAClB;AAEA,QAAA,OAAO,IAAIH,OAAQC,CAAAA,KAAAA,CAAAA;AACrB;AAEA;;AAEC,MACD,IAAI4B,MAAoB,GAAA;QACtB,OAAO,IAAI,CAACzB,OAAO;AACrB;AAEA;;AAEC,MACD,IAAI0B,IAAgB,GAAA;AAClB,QAAA,OAAO,IAAI,CAAC1B,OAAO,CAAC,CAAE,CAAA;AACxB;AAEA;;AAEC,MACD,IAAI2B,WAAuB,GAAA;QACzB,OAAO,IAAI,CAAC3B,OAAO,CAAC,IAAI,CAACA,OAAO,CAACF,MAAM,GAAG,CAAE,CAAA;AAC9C;AAEA;;AAEC,MACD,IAAI8B,WAAsB,GAAA;QACxB,OAAO,IAAI,CAAC3B,YAAY;AAC1B;AAEA;;AAEC,MACD,IAAIH,MAAiB,GAAA;AACnB,QAAA,OAAO,IAAI,CAACE,OAAO,CAACF,MAAM;AAC5B;AAEA;;AAEC,MACD+B,eAAwB,GAAA;AACtB,QAAA,KAAK,MAAMC,KAAAA,IAAS,IAAI,CAAC9B,OAAO,CAAE;YAChC8B,KAAM1B,CAAAA,IAAI,CAACK,WAAW,CAACC,SAAS,CAAC,IAAA,EAAMoB,KAAMvB,CAAAA,QAAQ,EAAEuB,KAAAA,CAAMzB,QAAQ,CAAA;AACvE;AACF;AAEA;;AAEC,MACD0B,sBAA+B,GAAA;AAC7B,QAAA,KAAK,MAAMD,KAAAA,IAAS,IAAI,CAAC9B,OAAO,CAAE;AAChC8B,YAAAA,KAAAA,CAAMb,gBAAgB,CAACe,GAAG,CAACF,MAAMzB,QAAQ,CAAA;AACzCyB,YAAAA,KAAAA,CAAMZ,gBAAgB,CAACc,GAAG,CAACF,MAAMvB,QAAQ,CAAA;AAC3C;AACF;AAEA;;AAEC,MACD0B,wBAAiC,GAAA;AAC/B,QAAA,KAAK,MAAMH,KAAAA,IAAS,IAAI,CAAC9B,OAAO,CAAE;AAChC8B,YAAAA,KAAAA,CAAMzB,QAAQ,CAAC2B,GAAG,CAACF,MAAMb,gBAAgB,CAAA;AACzCa,YAAAA,KAAAA,CAAMvB,QAAQ,CAACyB,GAAG,CAACF,MAAMZ,gBAAgB,CAAA;AAC3C;AACF;AAEA;;;;MAKAgB,aAAAA,CAAcC,UAAwB,EAAQ;AAC5C,QAAA,IAAI,CAACjC,YAAY,CAACa,IAAI,CAACoB,UAAAA,CAAAA;AACzB;AAEA;;;;;MAMAC,gBAAAA,CAAiBD,UAAwB,EAAW;AAClD,QAAA,MAAME,QAAQ,IAAI,CAACnC,YAAY,CAACoC,OAAO,CAACH,UAAAA,CAAAA;AACxC,QAAA,IAAIE,SAAS,CAAG,EAAA;AACd,YAAA,IAAI,CAACnC,YAAY,CAACqC,MAAM,CAACF,KAAO,EAAA,CAAA,CAAA;YAChC,OAAO,IAAA;AACT;QACA,OAAO,KAAA;AACT;AAEA;;AAEC,MACDG,gBAAyB,GAAA;QACvB,IAAI,CAACtC,YAAY,GAAG,EAAE;AACxB;AAEA;;AAEC,MACD,IAAIuC,WAAuC,GAAA;QACzC,OAAO,IAAI,CAACvC,YAAY;AAC1B;AAEA;;AAEC,MACDwC,gBAAyB,GAAA;AACvB,QAAA,KAAK,MAAMP,UAAAA,IAAc,IAAI,CAACjC,YAAY,CAAE;AAC1CiC,YAAAA,UAAAA,CAAWQ,KAAK,CAAC,IAAI,CAAC3C,OAAO,CAAA;AAC/B;AACF;AACF;;;;"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { IKChain } from './ik_chain.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Helper class for building IK chains from various sources.
|
|
5
|
+
*
|
|
6
|
+
* @public
|
|
7
|
+
*/ class IKChainBuilder {
|
|
8
|
+
/**
|
|
9
|
+
* Create an IK chain from an array of scene nodes.
|
|
10
|
+
*
|
|
11
|
+
* @param nodes - Array of scene nodes from root to end effector
|
|
12
|
+
* @returns The created IK chain
|
|
13
|
+
*/ static fromNodes(nodes) {
|
|
14
|
+
return new IKChain(nodes);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Create an IK chain from a skeleton using joint indices.
|
|
18
|
+
*
|
|
19
|
+
* @param skeleton - The skeleton containing the joints
|
|
20
|
+
* @param jointIndices - Array of joint indices from root to end effector
|
|
21
|
+
* @returns The created IK chain
|
|
22
|
+
*/ static fromSkeleton(skeleton, jointIndices) {
|
|
23
|
+
if (jointIndices.length < 2) {
|
|
24
|
+
throw new Error('IK chain must have at least 2 joints');
|
|
25
|
+
}
|
|
26
|
+
const joints = skeleton.joints;
|
|
27
|
+
const nodes = [];
|
|
28
|
+
for (const index of jointIndices){
|
|
29
|
+
if (index < 0 || index >= joints.length) {
|
|
30
|
+
throw new Error(`Invalid joint index: ${index}`);
|
|
31
|
+
}
|
|
32
|
+
nodes.push(joints[index]);
|
|
33
|
+
}
|
|
34
|
+
return new IKChain(nodes);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Create an IK chain by traversing from a start node to an end node.
|
|
38
|
+
*
|
|
39
|
+
* @remarks
|
|
40
|
+
* This method walks up the parent chain from the end node until it
|
|
41
|
+
* reaches the start node, building the joint chain in the process.
|
|
42
|
+
*
|
|
43
|
+
* @param startNode - The root joint of the chain
|
|
44
|
+
* @param endNode - The end effector joint
|
|
45
|
+
* @returns The created IK chain
|
|
46
|
+
*/ static fromNodeHierarchy(startNode, endNode) {
|
|
47
|
+
const nodes = [];
|
|
48
|
+
let current = endNode;
|
|
49
|
+
// Walk up the hierarchy from end to start
|
|
50
|
+
while(current){
|
|
51
|
+
nodes.unshift(current); // Add to front
|
|
52
|
+
if (current === startNode) {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
current = current.parent;
|
|
56
|
+
}
|
|
57
|
+
if (nodes[0] !== startNode) {
|
|
58
|
+
throw new Error('End node is not a descendant of start node');
|
|
59
|
+
}
|
|
60
|
+
if (nodes.length < 2) {
|
|
61
|
+
throw new Error('IK chain must have at least 2 joints');
|
|
62
|
+
}
|
|
63
|
+
return new IKChain(nodes);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { IKChainBuilder };
|
|
68
|
+
//# sourceMappingURL=ik_chain_builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ik_chain_builder.js","sources":["../../../src/animation/ik/ik_chain_builder.ts"],"sourcesContent":["import { IKChain } from './ik_chain';\r\nimport type { SceneNode } from '../../scene/scene_node';\r\nimport type { Skeleton } from '../skeleton';\r\n\r\n/**\r\n * Helper class for building IK chains from various sources.\r\n *\r\n * @public\r\n */\r\nexport class IKChainBuilder {\r\n /**\r\n * Create an IK chain from an array of scene nodes.\r\n *\r\n * @param nodes - Array of scene nodes from root to end effector\r\n * @returns The created IK chain\r\n */\r\n static fromNodes(nodes: SceneNode[]): IKChain {\r\n return new IKChain(nodes);\r\n }\r\n\r\n /**\r\n * Create an IK chain from a skeleton using joint indices.\r\n *\r\n * @param skeleton - The skeleton containing the joints\r\n * @param jointIndices - Array of joint indices from root to end effector\r\n * @returns The created IK chain\r\n */\r\n static fromSkeleton(skeleton: Skeleton, jointIndices: number[]): IKChain {\r\n if (jointIndices.length < 2) {\r\n throw new Error('IK chain must have at least 2 joints');\r\n }\r\n\r\n const joints = skeleton.joints;\r\n const nodes: SceneNode[] = [];\r\n\r\n for (const index of jointIndices) {\r\n if (index < 0 || index >= joints.length) {\r\n throw new Error(`Invalid joint index: ${index}`);\r\n }\r\n nodes.push(joints[index]);\r\n }\r\n\r\n return new IKChain(nodes);\r\n }\r\n\r\n /**\r\n * Create an IK chain by traversing from a start node to an end node.\r\n *\r\n * @remarks\r\n * This method walks up the parent chain from the end node until it\r\n * reaches the start node, building the joint chain in the process.\r\n *\r\n * @param startNode - The root joint of the chain\r\n * @param endNode - The end effector joint\r\n * @returns The created IK chain\r\n */\r\n static fromNodeHierarchy(startNode: SceneNode, endNode: SceneNode): IKChain {\r\n const nodes: SceneNode[] = [];\r\n let current: SceneNode | null = endNode;\r\n\r\n // Walk up the hierarchy from end to start\r\n while (current) {\r\n nodes.unshift(current); // Add to front\r\n\r\n if (current === startNode) {\r\n break;\r\n }\r\n\r\n current = current.parent as SceneNode | null;\r\n }\r\n\r\n if (nodes[0] !== startNode) {\r\n throw new Error('End node is not a descendant of start node');\r\n }\r\n\r\n if (nodes.length < 2) {\r\n throw new Error('IK chain must have at least 2 joints');\r\n }\r\n\r\n return new IKChain(nodes);\r\n }\r\n}\r\n"],"names":["IKChainBuilder","fromNodes","nodes","IKChain","fromSkeleton","skeleton","jointIndices","length","Error","joints","index","push","fromNodeHierarchy","startNode","endNode","current","unshift","parent"],"mappings":";;AAIA;;;;AAIC,IACM,MAAMA,cAAAA,CAAAA;AACX;;;;;MAMA,OAAOC,SAAUC,CAAAA,KAAkB,EAAW;AAC5C,QAAA,OAAO,IAAIC,OAAQD,CAAAA,KAAAA,CAAAA;AACrB;AAEA;;;;;;AAMC,MACD,OAAOE,YAAAA,CAAaC,QAAkB,EAAEC,YAAsB,EAAW;QACvE,IAAIA,YAAAA,CAAaC,MAAM,GAAG,CAAG,EAAA;AAC3B,YAAA,MAAM,IAAIC,KAAM,CAAA,sCAAA,CAAA;AAClB;QAEA,MAAMC,MAAAA,GAASJ,SAASI,MAAM;AAC9B,QAAA,MAAMP,QAAqB,EAAE;QAE7B,KAAK,MAAMQ,SAASJ,YAAc,CAAA;AAChC,YAAA,IAAII,KAAQ,GAAA,CAAA,IAAKA,KAASD,IAAAA,MAAAA,CAAOF,MAAM,EAAE;AACvC,gBAAA,MAAM,IAAIC,KAAAA,CAAM,CAAC,qBAAqB,EAAEE,KAAO,CAAA,CAAA,CAAA;AACjD;AACAR,YAAAA,KAAAA,CAAMS,IAAI,CAACF,MAAM,CAACC,KAAM,CAAA,CAAA;AAC1B;AAEA,QAAA,OAAO,IAAIP,OAAQD,CAAAA,KAAAA,CAAAA;AACrB;AAEA;;;;;;;;;;AAUC,MACD,OAAOU,iBAAAA,CAAkBC,SAAoB,EAAEC,OAAkB,EAAW;AAC1E,QAAA,MAAMZ,QAAqB,EAAE;AAC7B,QAAA,IAAIa,OAA4BD,GAAAA,OAAAA;;AAGhC,QAAA,MAAOC,OAAS,CAAA;YACdb,KAAMc,CAAAA,OAAO,CAACD,OAAAA,CAAAA,CAAAA;AAEd,YAAA,IAAIA,YAAYF,SAAW,EAAA;AACzB,gBAAA;AACF;AAEAE,YAAAA,OAAAA,GAAUA,QAAQE,MAAM;AAC1B;AAEA,QAAA,IAAIf,KAAK,CAAC,CAAE,CAAA,KAAKW,SAAW,EAAA;AAC1B,YAAA,MAAM,IAAIL,KAAM,CAAA,4CAAA,CAAA;AAClB;QAEA,IAAIN,KAAAA,CAAMK,MAAM,GAAG,CAAG,EAAA;AACpB,YAAA,MAAM,IAAIC,KAAM,CAAA,sCAAA,CAAA;AAClB;AAEA,QAAA,OAAO,IAAIL,OAAQD,CAAAA,KAAAA,CAAAA;AACrB;AACF;;;;"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class for IK constraints.
|
|
3
|
+
*
|
|
4
|
+
* Constraints are applied after each FABRIK iteration to enforce
|
|
5
|
+
* joint limitations (angle limits, pole vectors, etc.).
|
|
6
|
+
*
|
|
7
|
+
* @public
|
|
8
|
+
*/ class IKConstraint {
|
|
9
|
+
/** The joint index this constraint applies to */ _jointIndex;
|
|
10
|
+
/**
|
|
11
|
+
* Create an IK constraint.
|
|
12
|
+
*
|
|
13
|
+
* @param jointIndex - Index of the joint in the chain (0 = root)
|
|
14
|
+
*/ constructor(jointIndex){
|
|
15
|
+
this._jointIndex = jointIndex;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Get the joint index this constraint applies to.
|
|
19
|
+
*/ get jointIndex() {
|
|
20
|
+
return this._jointIndex;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export { IKConstraint };
|
|
25
|
+
//# sourceMappingURL=ik_constraint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ik_constraint.js","sources":["../../../src/animation/ik/ik_constraint.ts"],"sourcesContent":["import type { IKJoint } from './ik_joint';\r\n\r\n/**\r\n * Base class for IK constraints.\r\n *\r\n * Constraints are applied after each FABRIK iteration to enforce\r\n * joint limitations (angle limits, pole vectors, etc.).\r\n *\r\n * @public\r\n */\r\nexport abstract class IKConstraint {\r\n /** The joint index this constraint applies to */\r\n protected _jointIndex: number;\r\n\r\n /**\r\n * Create an IK constraint.\r\n *\r\n * @param jointIndex - Index of the joint in the chain (0 = root)\r\n */\r\n constructor(jointIndex: number) {\r\n this._jointIndex = jointIndex;\r\n }\r\n\r\n /**\r\n * Get the joint index this constraint applies to.\r\n */\r\n get jointIndex(): number {\r\n return this._jointIndex;\r\n }\r\n\r\n /**\r\n * Apply the constraint to the joint chain.\r\n *\r\n * This method modifies joint positions to satisfy the constraint.\r\n *\r\n * @param joints - Array of all joints in the chain\r\n */\r\n abstract apply(joints: IKJoint[]): void;\r\n}\r\n\r\n/**\r\n * Twist constraint configuration for a joint.\r\n *\r\n * @public\r\n */\r\nexport interface TwistConstraint {\r\n /** Minimum twist angle in radians */\r\n minTwist: number;\r\n /** Maximum twist angle in radians */\r\n maxTwist: number;\r\n /** Smoothing factor [0-1], 0 = no smoothing, 1 = full smoothing */\r\n smoothFactor: number;\r\n}\r\n"],"names":["IKConstraint","jointIndex","_jointIndex"],"mappings":"AAEA;;;;;;;AAOC,IACM,MAAeA,YAAAA,CAAAA;sDAEpB,WAA8B;AAE9B;;;;MAKA,WAAA,CAAYC,UAAkB,CAAE;QAC9B,IAAI,CAACC,WAAW,GAAGD,UAAAA;AACrB;AAEA;;AAEC,MACD,IAAIA,UAAqB,GAAA;QACvB,OAAO,IAAI,CAACC,WAAW;AACzB;AAUF;;;;"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Vector3 } from '@zephyr3d/base';
|
|
2
|
+
import { IKConstraint } from './ik_constraint.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Pole vector constraint for IK chains.
|
|
6
|
+
*
|
|
7
|
+
* Controls the twist/rotation of a joint chain by ensuring the middle joint
|
|
8
|
+
* bends toward a specified pole vector position.
|
|
9
|
+
*
|
|
10
|
+
* @remarks
|
|
11
|
+
* This is commonly used for arms and legs to control which direction
|
|
12
|
+
* the elbow or knee points. For example, ensuring an elbow points
|
|
13
|
+
* backward or a knee points forward.
|
|
14
|
+
*
|
|
15
|
+
* The pole vector defines a plane, and the middle joint is constrained
|
|
16
|
+
* to lie on this plane.
|
|
17
|
+
*
|
|
18
|
+
* @public
|
|
19
|
+
*/ class IKPoleVectorConstraint extends IKConstraint {
|
|
20
|
+
/** The pole vector position in world space */ _poleVector;
|
|
21
|
+
/** Weight of the constraint (0 = no effect, 1 = full effect) */ _weight;
|
|
22
|
+
/**
|
|
23
|
+
* Create a pole vector constraint.
|
|
24
|
+
*
|
|
25
|
+
* @param jointIndex - Index of the middle joint to constrain (typically elbow or knee)
|
|
26
|
+
* @param poleVector - Position in world space that the joint should point toward
|
|
27
|
+
* @param weight - Constraint weight (0-1, default: 1)
|
|
28
|
+
*/ constructor(jointIndex, poleVector, weight = 1){
|
|
29
|
+
super(jointIndex);
|
|
30
|
+
this._poleVector = poleVector.clone();
|
|
31
|
+
this._weight = Math.max(0, Math.min(1, weight));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get the pole vector position.
|
|
35
|
+
*/ get poleVector() {
|
|
36
|
+
return this._poleVector;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Set the pole vector position.
|
|
40
|
+
*/ set poleVector(value) {
|
|
41
|
+
this._poleVector.set(value);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get the constraint weight.
|
|
45
|
+
*/ get weight() {
|
|
46
|
+
return this._weight;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Set the constraint weight (0-1).
|
|
50
|
+
*/ set weight(value) {
|
|
51
|
+
this._weight = Math.max(0, Math.min(1, value));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Apply the pole vector constraint.
|
|
55
|
+
*
|
|
56
|
+
* This adjusts the middle joint position to align with the pole vector
|
|
57
|
+
* while maintaining bone lengths.
|
|
58
|
+
*/ apply(joints) {
|
|
59
|
+
const index = this._jointIndex;
|
|
60
|
+
// Need at least 3 joints (parent, this, child)
|
|
61
|
+
if (index < 1 || index >= joints.length - 1) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (this._weight < 0.001) {
|
|
65
|
+
return; // No effect
|
|
66
|
+
}
|
|
67
|
+
const parentJoint = joints[index - 1];
|
|
68
|
+
const currentJoint = joints[index];
|
|
69
|
+
const childJoint = joints[index + 1];
|
|
70
|
+
// Calculate the plane defined by parent, child, and pole vector
|
|
71
|
+
const parentPos = parentJoint.position;
|
|
72
|
+
const childPos = childJoint.position;
|
|
73
|
+
const currentPos = currentJoint.position;
|
|
74
|
+
// Vector from parent to child (the "line" we're bending around)
|
|
75
|
+
const parentToChild = Vector3.sub(childPos, parentPos, new Vector3());
|
|
76
|
+
const lineLength = parentToChild.magnitude;
|
|
77
|
+
if (lineLength < 0.000001) {
|
|
78
|
+
return; // Parent and child are at same position
|
|
79
|
+
}
|
|
80
|
+
const lineDir = Vector3.scale(parentToChild, 1 / lineLength, new Vector3());
|
|
81
|
+
// Project current joint onto the parent-child line
|
|
82
|
+
const parentToCurrent = Vector3.sub(currentPos, parentPos, new Vector3());
|
|
83
|
+
const projectionLength = Vector3.dot(parentToCurrent, lineDir);
|
|
84
|
+
const projectionPoint = Vector3.scale(lineDir, projectionLength, new Vector3());
|
|
85
|
+
projectionPoint.addBy(parentPos);
|
|
86
|
+
// Vector from projection point to current joint (perpendicular to line)
|
|
87
|
+
const perpendicular = Vector3.sub(currentPos, projectionPoint, new Vector3());
|
|
88
|
+
const perpLength = perpendicular.magnitude;
|
|
89
|
+
if (perpLength < 0.000001) {
|
|
90
|
+
return; // Current joint is on the line
|
|
91
|
+
}
|
|
92
|
+
// Calculate desired direction toward pole vector
|
|
93
|
+
const parentToPole = Vector3.sub(this._poleVector, parentPos, new Vector3());
|
|
94
|
+
const poleProjectionLength = Vector3.dot(parentToPole, lineDir);
|
|
95
|
+
const poleProjectionPoint = Vector3.scale(lineDir, poleProjectionLength, new Vector3());
|
|
96
|
+
poleProjectionPoint.addBy(parentPos);
|
|
97
|
+
// Direction from projection to pole (perpendicular to line)
|
|
98
|
+
const poleDirection = Vector3.sub(this._poleVector, poleProjectionPoint, new Vector3());
|
|
99
|
+
const poleDirLength = poleDirection.magnitude;
|
|
100
|
+
if (poleDirLength < 0.000001) {
|
|
101
|
+
return; // Pole is on the line
|
|
102
|
+
}
|
|
103
|
+
poleDirection.scaleBy(1 / poleDirLength);
|
|
104
|
+
// Calculate new position for current joint
|
|
105
|
+
// Keep the same distance from the line, but rotate toward pole direction
|
|
106
|
+
const desiredPerpendicular = Vector3.scale(poleDirection, perpLength, new Vector3());
|
|
107
|
+
const newPosition = Vector3.add(projectionPoint, desiredPerpendicular, new Vector3());
|
|
108
|
+
// Apply weight (blend between original and constrained position)
|
|
109
|
+
if (this._weight < 0.999) {
|
|
110
|
+
// Manual lerp: result = a + (b - a) * t
|
|
111
|
+
const blended = new Vector3();
|
|
112
|
+
blended.x = currentPos.x + (newPosition.x - currentPos.x) * this._weight;
|
|
113
|
+
blended.y = currentPos.y + (newPosition.y - currentPos.y) * this._weight;
|
|
114
|
+
blended.z = currentPos.z + (newPosition.z - currentPos.z) * this._weight;
|
|
115
|
+
currentJoint.position.set(blended);
|
|
116
|
+
} else {
|
|
117
|
+
currentJoint.position.set(newPosition);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export { IKPoleVectorConstraint };
|
|
123
|
+
//# sourceMappingURL=ik_pole_constraint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ik_pole_constraint.js","sources":["../../../src/animation/ik/ik_pole_constraint.ts"],"sourcesContent":["import { Vector3 } from '@zephyr3d/base';\r\nimport { IKConstraint } from './ik_constraint';\r\nimport type { IKJoint } from './ik_joint';\r\n\r\n/**\r\n * Pole vector constraint for IK chains.\r\n *\r\n * Controls the twist/rotation of a joint chain by ensuring the middle joint\r\n * bends toward a specified pole vector position.\r\n *\r\n * @remarks\r\n * This is commonly used for arms and legs to control which direction\r\n * the elbow or knee points. For example, ensuring an elbow points\r\n * backward or a knee points forward.\r\n *\r\n * The pole vector defines a plane, and the middle joint is constrained\r\n * to lie on this plane.\r\n *\r\n * @public\r\n */\r\nexport class IKPoleVectorConstraint extends IKConstraint {\r\n /** The pole vector position in world space */\r\n private _poleVector: Vector3;\r\n /** Weight of the constraint (0 = no effect, 1 = full effect) */\r\n private _weight: number;\r\n\r\n /**\r\n * Create a pole vector constraint.\r\n *\r\n * @param jointIndex - Index of the middle joint to constrain (typically elbow or knee)\r\n * @param poleVector - Position in world space that the joint should point toward\r\n * @param weight - Constraint weight (0-1, default: 1)\r\n */\r\n constructor(jointIndex: number, poleVector: Vector3, weight = 1) {\r\n super(jointIndex);\r\n this._poleVector = poleVector.clone();\r\n this._weight = Math.max(0, Math.min(1, weight));\r\n }\r\n\r\n /**\r\n * Get the pole vector position.\r\n */\r\n get poleVector(): Vector3 {\r\n return this._poleVector;\r\n }\r\n\r\n /**\r\n * Set the pole vector position.\r\n */\r\n set poleVector(value: Vector3) {\r\n this._poleVector.set(value);\r\n }\r\n\r\n /**\r\n * Get the constraint weight.\r\n */\r\n get weight(): number {\r\n return this._weight;\r\n }\r\n\r\n /**\r\n * Set the constraint weight (0-1).\r\n */\r\n set weight(value: number) {\r\n this._weight = Math.max(0, Math.min(1, value));\r\n }\r\n\r\n /**\r\n * Apply the pole vector constraint.\r\n *\r\n * This adjusts the middle joint position to align with the pole vector\r\n * while maintaining bone lengths.\r\n */\r\n apply(joints: IKJoint[]): void {\r\n const index = this._jointIndex;\r\n\r\n // Need at least 3 joints (parent, this, child)\r\n if (index < 1 || index >= joints.length - 1) {\r\n return;\r\n }\r\n\r\n if (this._weight < 0.001) {\r\n return; // No effect\r\n }\r\n\r\n const parentJoint = joints[index - 1];\r\n const currentJoint = joints[index];\r\n const childJoint = joints[index + 1];\r\n\r\n // Calculate the plane defined by parent, child, and pole vector\r\n const parentPos = parentJoint.position;\r\n const childPos = childJoint.position;\r\n const currentPos = currentJoint.position;\r\n\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 projectionPoint.addBy(parentPos);\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(this._poleVector, parentPos, new Vector3());\r\n const poleProjectionLength = Vector3.dot(parentToPole, lineDir);\r\n const poleProjectionPoint = Vector3.scale(lineDir, poleProjectionLength, new Vector3());\r\n poleProjectionPoint.addBy(parentPos);\r\n\r\n // Direction from projection to pole (perpendicular to line)\r\n const poleDirection = Vector3.sub(this._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 (this._weight < 0.999) {\r\n // Manual lerp: result = a + (b - a) * t\r\n const blended = new Vector3();\r\n blended.x = currentPos.x + (newPosition.x - currentPos.x) * this._weight;\r\n blended.y = currentPos.y + (newPosition.y - currentPos.y) * this._weight;\r\n blended.z = currentPos.z + (newPosition.z - currentPos.z) * this._weight;\r\n currentJoint.position.set(blended);\r\n } else {\r\n currentJoint.position.set(newPosition);\r\n }\r\n }\r\n}\r\n"],"names":["IKPoleVectorConstraint","IKConstraint","jointIndex","poleVector","weight","_poleVector","clone","_weight","Math","max","min","value","set","apply","joints","index","_jointIndex","length","parentJoint","currentJoint","childJoint","parentPos","position","childPos","currentPos","parentToChild","Vector3","sub","lineLength","magnitude","lineDir","scale","parentToCurrent","projectionLength","dot","projectionPoint","addBy","perpendicular","perpLength","parentToPole","poleProjectionLength","poleProjectionPoint","poleDirection","poleDirLength","scaleBy","desiredPerpendicular","newPosition","add","blended","x","y","z"],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class for IK solvers.
|
|
3
|
+
*
|
|
4
|
+
* @public
|
|
5
|
+
*/ class IKSolver {
|
|
6
|
+
/** The IK chain to solve */ _chain;
|
|
7
|
+
/** Maximum number of iterations */ _maxIterations;
|
|
8
|
+
/** Convergence tolerance (distance threshold) */ _tolerance;
|
|
9
|
+
/**
|
|
10
|
+
* Create an IK solver.
|
|
11
|
+
*
|
|
12
|
+
* @param chain - The IK chain to solve
|
|
13
|
+
* @param maxIterations - Maximum number of iterations (default: 10)
|
|
14
|
+
* @param tolerance - Convergence tolerance in world units (default: 0.001)
|
|
15
|
+
*/ constructor(chain, maxIterations = 10, tolerance = 0.001){
|
|
16
|
+
this._chain = chain;
|
|
17
|
+
this._maxIterations = maxIterations;
|
|
18
|
+
this._tolerance = tolerance;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Get the IK chain.
|
|
22
|
+
*/ get chain() {
|
|
23
|
+
return this._chain;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Set maximum number of iterations.
|
|
27
|
+
*/ setMaxIterations(value) {
|
|
28
|
+
this._maxIterations = Math.max(1, value);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get maximum number of iterations.
|
|
32
|
+
*/ getMaxIterations() {
|
|
33
|
+
return this._maxIterations;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Set convergence tolerance.
|
|
37
|
+
*/ setTolerance(value) {
|
|
38
|
+
this._tolerance = Math.max(0, value);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get convergence tolerance.
|
|
42
|
+
*/ getTolerance() {
|
|
43
|
+
return this._tolerance;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { IKSolver };
|
|
48
|
+
//# sourceMappingURL=ik_solver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ik_solver.js","sources":["../../../src/animation/ik/ik_solver.ts"],"sourcesContent":["import type { Vector3 } from '@zephyr3d/base';\r\nimport type { IKChain } from './ik_chain';\r\n\r\n/**\r\n * Base class for IK solvers.\r\n *\r\n * @public\r\n */\r\nexport abstract class IKSolver {\r\n /** The IK chain to solve */\r\n protected _chain: IKChain;\r\n /** Maximum number of iterations */\r\n protected _maxIterations: number;\r\n /** Convergence tolerance (distance threshold) */\r\n protected _tolerance: number;\r\n\r\n /**\r\n * Create an IK 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 this._chain = chain;\r\n this._maxIterations = maxIterations;\r\n this._tolerance = tolerance;\r\n }\r\n\r\n /**\r\n * Get the IK chain.\r\n */\r\n get chain(): IKChain {\r\n return this._chain;\r\n }\r\n\r\n /**\r\n * Set maximum number of iterations.\r\n */\r\n setMaxIterations(value: number): void {\r\n this._maxIterations = Math.max(1, value);\r\n }\r\n\r\n /**\r\n * Get maximum number of iterations.\r\n */\r\n getMaxIterations(): number {\r\n return this._maxIterations;\r\n }\r\n\r\n /**\r\n * Set convergence tolerance.\r\n */\r\n setTolerance(value: number): void {\r\n this._tolerance = Math.max(0, value);\r\n }\r\n\r\n /**\r\n * Get convergence tolerance.\r\n */\r\n getTolerance(): number {\r\n return this._tolerance;\r\n }\r\n\r\n /**\r\n * Solve the IK chain to reach the target position.\r\n *\r\n * @param target - Target position for the end effector\r\n * @returns True if converged, false otherwise\r\n */\r\n abstract solve(target: Vector3): boolean;\r\n\r\n /**\r\n * Apply the solved positions to the scene nodes as rotations.\r\n *\r\n * @param weight - Blend weight (0 = original, 1 = full IK)\r\n */\r\n abstract applyToNodes(weight?: number): void;\r\n}\r\n"],"names":["IKSolver","chain","maxIterations","tolerance","_chain","_maxIterations","_tolerance","setMaxIterations","value","Math","max","getMaxIterations","setTolerance","getTolerance"],"mappings":"AAGA;;;;AAIC,IACM,MAAeA,QAAAA,CAAAA;iCAEpB,MAA0B;wCAE1B,cAAiC;sDAEjC,UAA6B;AAE7B;;;;;;MAOA,WAAA,CAAYC,KAAc,EAAEC,aAAAA,GAAgB,EAAE,EAAEC,SAAAA,GAAY,KAAK,CAAE;QACjE,IAAI,CAACC,MAAM,GAAGH,KAAAA;QACd,IAAI,CAACI,cAAc,GAAGH,aAAAA;QACtB,IAAI,CAACI,UAAU,GAAGH,SAAAA;AACpB;AAEA;;AAEC,MACD,IAAIF,KAAiB,GAAA;QACnB,OAAO,IAAI,CAACG,MAAM;AACpB;AAEA;;MAGAG,gBAAAA,CAAiBC,KAAa,EAAQ;AACpC,QAAA,IAAI,CAACH,cAAc,GAAGI,IAAKC,CAAAA,GAAG,CAAC,CAAGF,EAAAA,KAAAA,CAAAA;AACpC;AAEA;;AAEC,MACDG,gBAA2B,GAAA;QACzB,OAAO,IAAI,CAACN,cAAc;AAC5B;AAEA;;MAGAO,YAAAA,CAAaJ,KAAa,EAAQ;AAChC,QAAA,IAAI,CAACF,UAAU,GAAGG,IAAKC,CAAAA,GAAG,CAAC,CAAGF,EAAAA,KAAAA,CAAAA;AAChC;AAEA;;AAEC,MACDK,YAAuB,GAAA;QACrB,OAAO,IAAI,CAACP,UAAU;AACxB;AAgBF;;;;"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Quaternion } from '@zephyr3d/base';
|
|
2
|
+
import { AnimationTrack } from '../animationtrack.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Animation track that applies IK solving.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* This track integrates IK solving into the animation system, allowing
|
|
9
|
+
* IK to be blended with other animations. The track solves the IK chain
|
|
10
|
+
* each frame and produces joint rotations that can be mixed with other
|
|
11
|
+
* animation tracks.
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
14
|
+
*/ class IKTrack extends AnimationTrack {
|
|
15
|
+
/** The IK solver */ _solver;
|
|
16
|
+
/** Cached state for reuse */ _state;
|
|
17
|
+
/** Duration of the track (can be infinite for procedural IK) */ _duration;
|
|
18
|
+
/**
|
|
19
|
+
* Create an IK animation track.
|
|
20
|
+
*
|
|
21
|
+
* @param solver - The FABRIK solver to use
|
|
22
|
+
* @param duration - Duration of the track in seconds (default: Infinity for procedural)
|
|
23
|
+
*/ constructor(solver, duration = Infinity){
|
|
24
|
+
super(false);
|
|
25
|
+
this._solver = solver;
|
|
26
|
+
this._duration = duration;
|
|
27
|
+
// Initialize state with identity rotations
|
|
28
|
+
const numJoints = solver.chain.joints.length - 1; // Exclude end effector
|
|
29
|
+
this._state = {
|
|
30
|
+
rotations: Array.from({
|
|
31
|
+
length: numJoints
|
|
32
|
+
}, ()=>new Quaternion())
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get the IK solver.
|
|
37
|
+
*/ get solver() {
|
|
38
|
+
return this._solver;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Set the duration of the track.
|
|
42
|
+
*/ setDuration(duration) {
|
|
43
|
+
this._duration = Math.max(0, duration);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Calculate IK state at the given time.
|
|
47
|
+
*
|
|
48
|
+
* @remarks
|
|
49
|
+
* For procedural IK, the time parameter is typically ignored.
|
|
50
|
+
* The solver uses the current target position set externally.
|
|
51
|
+
*/ calculateState(_target, _currentTime) {
|
|
52
|
+
const joints = this._solver.chain.joints;
|
|
53
|
+
// Store current rotations as the calculated state
|
|
54
|
+
for(let i = 0; i < joints.length - 1; i++){
|
|
55
|
+
this._state.rotations[i].set(joints[i].rotation);
|
|
56
|
+
}
|
|
57
|
+
return this._state;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Apply IK state to the target scene nodes.
|
|
61
|
+
*/ applyState(_target, state) {
|
|
62
|
+
const joints = this._solver.chain.joints;
|
|
63
|
+
// Apply rotations to scene nodes
|
|
64
|
+
for(let i = 0; i < state.rotations.length && i < joints.length - 1; i++){
|
|
65
|
+
joints[i].node.rotation = state.rotations[i];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Mix two IK states using spherical interpolation.
|
|
70
|
+
*/ mixState(a, b, t) {
|
|
71
|
+
const result = {
|
|
72
|
+
rotations: []
|
|
73
|
+
};
|
|
74
|
+
const count = Math.min(a.rotations.length, b.rotations.length);
|
|
75
|
+
for(let i = 0; i < count; i++){
|
|
76
|
+
result.rotations.push(Quaternion.slerp(a.rotations[i], b.rotations[i], t));
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get the blend identifier for this track.
|
|
82
|
+
*
|
|
83
|
+
* IK tracks with the same root joint can be blended together.
|
|
84
|
+
*/ getBlendId() {
|
|
85
|
+
const rootNode = this._solver.chain.root.node;
|
|
86
|
+
return `ik-chain-${rootNode.runtimeId}`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the duration of this track.
|
|
90
|
+
*/ getDuration() {
|
|
91
|
+
return this._duration;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export { IKTrack };
|
|
96
|
+
//# sourceMappingURL=ik_track.js.map
|