@woosh/meep-engine 3.40.0 → 3.42.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/build/bundle-worker-image-decoder.js +1 -1
- package/build/bundle-worker-terrain.js +1 -1
- package/editor/selection/editor_pick.d.ts.map +1 -1
- package/editor/selection/editor_pick.js +2 -1
- package/editor/tools/TransformToolV2.d.ts.map +1 -1
- package/editor/tools/TransformToolV2.js +7 -1
- package/editor/tools/engine/ToolEngine.d.ts.map +1 -1
- package/editor/tools/engine/ToolEngine.js +10 -0
- package/editor/tools/v2/TransformControls.d.ts.map +1 -1
- package/editor/tools/v2/TransformControls.js +7 -20
- package/editor/tools/v2/handle/gizmo_drag_plane.d.ts.map +1 -1
- package/editor/tools/v2/handle/gizmo_drag_plane.js +4 -0
- package/editor/view/ecs/EntityEditor.d.ts.map +1 -1
- package/editor/view/ecs/EntityEditor.js +3 -1
- package/editor/view/node-graph/NodeGraphView.d.ts.map +1 -1
- package/editor/view/node-graph/NodeGraphView.js +28 -12
- package/package.json +1 -1
- package/samples/engine/first-scene/index.html +4 -1
- package/samples/engine/first-scene/main.js +14 -9
- package/src/core/binary/utf8/utf8_decode.js +3 -3
- package/src/core/bvh2/binary/2/bvh32_query_user_data_ray.d.ts.map +1 -1
- package/src/core/bvh2/binary/2/bvh32_query_user_data_ray.js +112 -113
- package/src/core/bvh2/bvh3/ebvh_nodes_sort_sah_local4.js +2 -2
- package/src/core/cache/wtinylfu/benchmark/CacheWTinylfu.bench.md +14 -14
- package/src/core/collection/RingBuffer.d.ts.map +1 -1
- package/src/core/collection/RingBuffer.js +4 -0
- package/src/core/collection/array/array_contains_duplicates.d.ts.map +1 -1
- package/src/core/collection/array/array_contains_duplicates.js +2 -1
- package/src/core/collection/array/array_copy.d.ts.map +1 -1
- package/src/core/collection/array/array_copy.js +23 -0
- package/src/core/collection/array/typed/sparse_typed_array_hash.d.ts +2 -2
- package/src/core/collection/array/typed/sparse_typed_array_hash.d.ts.map +1 -1
- package/src/core/collection/array/typed/sparse_typed_array_hash.js +9 -8
- package/src/core/collection/array/typed/uint16_array_hash.d.ts +2 -2
- package/src/core/collection/array/typed/uint16_array_hash.js +4 -4
- package/src/core/collection/array/typed/uint8_array_hash.d.ts +2 -2
- package/src/core/collection/array/typed/uint8_array_hash.js +4 -4
- package/src/core/collection/array/weightedRandomFromArray.d.ts +1 -0
- package/src/core/collection/array/weightedRandomFromArray.d.ts.map +1 -1
- package/src/core/collection/array/weightedRandomFromArray.js +9 -1
- package/src/core/collection/heap/BinaryHeap.d.ts.map +1 -1
- package/src/core/collection/heap/BinaryHeap.js +4 -0
- package/src/core/collection/list/List.d.ts.map +1 -1
- package/src/core/collection/list/List.js +4 -2
- package/src/core/collection/set/ArraySet.js +1 -1
- package/src/core/collection/set/HashSet.d.ts +1 -1
- package/src/core/collection/set/HashSet.d.ts.map +1 -1
- package/src/core/collection/set/HashSet.js +4 -2
- package/src/core/debug/matchers/IsEqual.js +1 -1
- package/src/core/debug/matchers/IsIterableContaining.d.ts.map +1 -1
- package/src/core/debug/matchers/IsIterableContaining.js +61 -74
- package/src/core/events/signal/SignalBinding.js +101 -101
- package/src/core/function/extractFunctionBody.js +13 -13
- package/src/core/function/objectsEqual.d.ts.map +1 -1
- package/src/core/function/objectsEqual.js +15 -14
- package/src/core/geom/3d/frustum/frustum3_compute_closest_point.d.ts.map +1 -1
- package/src/core/geom/3d/frustum/frustum3_compute_closest_point.js +207 -172
- package/src/core/geom/3d/gjk/gjk_epa_penetration.d.ts.map +1 -1
- package/src/core/geom/3d/gjk/gjk_epa_penetration.js +5 -4
- package/src/core/geom/3d/line/line3_compute_segment_point_distance_eikonal.d.ts.map +1 -1
- package/src/core/geom/3d/line/line3_compute_segment_point_distance_eikonal.js +8 -2
- package/src/core/geom/3d/quaternion/swing_twist_clamp.d.ts +24 -0
- package/src/core/geom/3d/quaternion/swing_twist_clamp.d.ts.map +1 -0
- package/src/core/geom/3d/quaternion/swing_twist_clamp.js +62 -0
- package/src/core/geom/3d/quaternion/swing_twist_compose.d.ts +15 -0
- package/src/core/geom/3d/quaternion/swing_twist_compose.d.ts.map +1 -0
- package/src/core/geom/3d/quaternion/swing_twist_compose.js +36 -0
- package/src/core/geom/3d/quaternion/swing_twist_error.d.ts +39 -0
- package/src/core/geom/3d/quaternion/swing_twist_error.d.ts.map +1 -0
- package/src/core/geom/3d/quaternion/swing_twist_error.js +85 -0
- package/src/core/geom/3d/triangle/tri3_ray_intersection.d.ts +20 -0
- package/src/core/geom/3d/triangle/tri3_ray_intersection.d.ts.map +1 -1
- package/src/core/geom/3d/triangle/tri3_ray_intersection.js +182 -0
- package/src/core/geom/vec3/v3_rigid_align_paired_unit_vectors.d.ts +11 -6
- package/src/core/geom/vec3/v3_rigid_align_paired_unit_vectors.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_rigid_align_paired_unit_vectors.js +27 -13
- package/src/core/math/linalg/eigen/matrix_symmetric_4x4_eigen.d.ts +25 -0
- package/src/core/math/linalg/eigen/matrix_symmetric_4x4_eigen.d.ts.map +1 -0
- package/src/core/math/linalg/eigen/matrix_symmetric_4x4_eigen.js +166 -0
- package/src/core/math/linalg/eigen/matrix_top_eigenvector_power_iteration.d.ts +5 -3
- package/src/core/math/linalg/eigen/matrix_top_eigenvector_power_iteration.d.ts.map +1 -1
- package/src/core/math/linalg/eigen/matrix_top_eigenvector_power_iteration.js +5 -3
- package/src/core/math/noise/curl_noise_3dt.d.ts.map +1 -1
- package/src/core/math/noise/curl_noise_3dt.js +20 -10
- package/src/core/math/noise/sdnoise.d.ts +16 -5
- package/src/core/math/noise/sdnoise.d.ts.map +1 -1
- package/src/core/math/noise/sdnoise.js +43 -9
- package/src/core/math/spline/spline3_hermite_nearest_point.d.ts +16 -1
- package/src/core/math/spline/spline3_hermite_nearest_point.d.ts.map +1 -1
- package/src/core/math/spline/spline3_hermite_nearest_point.js +44 -3
- package/src/engine/Engine.d.ts.map +1 -1
- package/src/engine/Engine.js +6 -2
- package/src/engine/animation/curve/AnimationCurve.d.ts +3 -0
- package/src/engine/animation/curve/AnimationCurve.d.ts.map +1 -1
- package/src/engine/animation/curve/AnimationCurve.js +6 -1
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts +9 -0
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts +26 -0
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.js +2 -0
- package/src/engine/control/first-person/abilities/AbilitySet.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/AbilitySet.js +9 -0
- package/src/engine/control/first-person/abilities/LedgeGrab.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/LedgeGrab.js +6 -0
- package/src/engine/control/first-person/abilities/Slide.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/Slide.js +1 -0
- package/src/engine/control/first-person/abilities/WallRun.d.ts +4 -1
- package/src/engine/control/first-person/abilities/WallRun.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/WallRun.js +13 -1
- package/src/engine/control/first-person/mastery/SlideInitiationTimingEvaluator.d.ts +1 -1
- package/src/engine/control/first-person/mastery/SlideInitiationTimingEvaluator.d.ts.map +1 -1
- package/src/engine/control/first-person/mastery/SlideInitiationTimingEvaluator.js +6 -5
- package/src/engine/control/first-person/mastery/StrideTimingJumpEvaluator.d.ts +3 -3
- package/src/engine/control/first-person/mastery/StrideTimingJumpEvaluator.d.ts.map +1 -1
- package/src/engine/control/first-person/mastery/StrideTimingJumpEvaluator.js +9 -8
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts.map +1 -1
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.js +13 -4
- package/src/engine/ecs/EntityManager.d.ts +9 -1
- package/src/engine/ecs/EntityManager.d.ts.map +1 -1
- package/src/engine/ecs/EntityManager.js +44 -42
- package/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescription.d.ts.map +1 -1
- package/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescription.js +2 -1
- package/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescriptionTable.d.ts.map +1 -1
- package/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescriptionTable.js +1 -0
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts.map +1 -1
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.js +3 -0
- package/src/engine/ecs/gpu/GPUStateAuthority.d.ts +20 -0
- package/src/engine/ecs/gpu/GPUStateAuthority.d.ts.map +1 -1
- package/src/engine/ecs/gpu/GPUStateAuthority.js +21 -0
- package/src/engine/ecs/gpu/GPU_AUTHORITY_EVENT_HOLD_CHANGE.d.ts +13 -0
- package/src/engine/ecs/gpu/GPU_AUTHORITY_EVENT_HOLD_CHANGE.d.ts.map +1 -0
- package/src/engine/ecs/gpu/GPU_AUTHORITY_EVENT_HOLD_CHANGE.js +12 -0
- package/src/engine/ecs/gpu/gpu_authority_grant.d.ts +6 -0
- package/src/engine/ecs/gpu/gpu_authority_grant.d.ts.map +1 -1
- package/src/engine/ecs/gpu/gpu_authority_grant.js +7 -1
- package/src/engine/ecs/gpu/gpu_authority_held.d.ts +14 -0
- package/src/engine/ecs/gpu/gpu_authority_held.d.ts.map +1 -0
- package/src/engine/ecs/gpu/gpu_authority_held.js +23 -0
- package/src/engine/ecs/gpu/gpu_authority_hold.d.ts +23 -0
- package/src/engine/ecs/gpu/gpu_authority_hold.d.ts.map +1 -0
- package/src/engine/ecs/gpu/gpu_authority_hold.js +41 -0
- package/src/engine/ecs/gpu/gpu_authority_unhold.d.ts +14 -0
- package/src/engine/ecs/gpu/gpu_authority_unhold.d.ts.map +1 -0
- package/src/engine/ecs/gpu/gpu_authority_unhold.js +32 -0
- package/src/engine/ecs/ik/IKGoal.d.ts +59 -0
- package/src/engine/ecs/ik/IKGoal.d.ts.map +1 -0
- package/src/engine/ecs/ik/IKGoal.js +83 -0
- package/src/engine/ecs/ik/IKGoalSystem.d.ts +75 -0
- package/src/engine/ecs/ik/IKGoalSystem.d.ts.map +1 -0
- package/src/engine/ecs/ik/IKGoalSystem.js +1456 -0
- package/src/engine/ecs/ik/PLAN.md +179 -0
- package/src/engine/ecs/parent/EntityNode.js +3 -3
- package/src/engine/ecs/speaker/VoiceSystem.d.ts +2 -2
- package/src/engine/ecs/speaker/VoiceSystem.d.ts.map +1 -1
- package/src/engine/ecs/speaker/VoiceSystem.js +4 -0
- package/src/engine/ecs/storage/BinaryBufferDeSerializer.js +5 -5
- package/src/engine/ecs/storage/BinaryBufferSerializer.d.ts.map +1 -1
- package/src/engine/ecs/storage/BinaryBufferSerializer.js +4 -5
- package/src/engine/ecs/storage/binary/BinarySerializationRegistry.d.ts.map +1 -1
- package/src/engine/ecs/storage/binary/BinarySerializationRegistry.js +3 -2
- package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +7 -3
- package/src/engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter2.js +1 -1
- package/src/engine/ecs/storage/binary/object-v2/BinaryObjectDeSerializer.d.ts.map +1 -1
- package/src/engine/ecs/storage/binary/object-v2/BinaryObjectDeSerializer.js +8 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +9 -2
- package/src/engine/ecs/system/orderSystemsByDeclaredAccess.d.ts +33 -0
- package/src/engine/ecs/system/orderSystemsByDeclaredAccess.d.ts.map +1 -0
- package/src/engine/ecs/system/orderSystemsByDeclaredAccess.js +282 -0
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +5 -2
- package/src/engine/ecs/terrain/ecs/layers/TerrainLayers.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/layers/TerrainLayers.js +3 -0
- package/src/engine/ecs/terrain/ecs/splat/SplatMapping.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/splat/SplatMapping.js +1 -0
- package/src/engine/ecs/terrain/overlay/TerrainOverlay.js +1 -1
- package/src/engine/ecs/terrain/tiles/TerrainTile.d.ts.map +1 -1
- package/src/engine/ecs/terrain/tiles/TerrainTile.js +13 -1
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.d.ts.map +1 -1
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.js +1 -0
- package/src/engine/ecs/transform/T64WorldCache.js +139 -139
- package/src/engine/ecs/transform/t64_evaluate_local.d.ts +25 -0
- package/src/engine/ecs/transform/t64_evaluate_local.d.ts.map +1 -0
- package/src/engine/ecs/transform/t64_evaluate_local.js +35 -0
- package/src/engine/ecs/transform/t64_evaluate_world.d.ts +5 -3
- package/src/engine/ecs/transform/t64_evaluate_world.d.ts.map +1 -1
- package/src/engine/ecs/transform/t64_evaluate_world.js +128 -126
- package/src/engine/graphics/ecs/light/LightSerializationAdapter.d.ts.map +1 -1
- package/src/engine/graphics/ecs/light/LightSerializationAdapter.js +13 -1
- package/src/engine/graphics/ecs/light/LightSerializationUpgrader_0_1.d.ts +5 -0
- package/src/engine/graphics/ecs/light/LightSerializationUpgrader_0_1.d.ts.map +1 -0
- package/src/engine/graphics/ecs/light/LightSerializationUpgrader_0_1.js +27 -0
- package/src/engine/graphics/ecs/water/Water.d.ts +11 -0
- package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
- package/src/engine/graphics/ecs/water/Water.js +17 -0
- package/src/engine/graphics/ecs/water/WaterSerializationAdapter.d.ts.map +1 -1
- package/src/engine/graphics/ecs/water/WaterSerializationAdapter.js +3 -1
- package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_4_5.d.ts +9 -0
- package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_4_5.d.ts.map +1 -0
- package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_4_5.js +24 -0
- package/src/engine/graphics/ecs/water/WaterWaves.d.ts +30 -9
- package/src/engine/graphics/ecs/water/WaterWaves.d.ts.map +1 -1
- package/src/engine/graphics/ecs/water/WaterWaves.js +41 -9
- package/src/engine/graphics/ecs/water/WaterWavesSerializationAdapter.d.ts.map +1 -1
- package/src/engine/graphics/ecs/water/WaterWavesSerializationAdapter.js +30 -26
- package/src/engine/graphics/ecs/water/WaterWavesSerializationUpgrader_0_1.d.ts +6 -0
- package/src/engine/graphics/ecs/water/WaterWavesSerializationUpgrader_0_1.d.ts.map +1 -0
- package/src/engine/graphics/ecs/water/WaterWavesSerializationUpgrader_0_1.js +22 -0
- package/src/engine/graphics/geometry/buffered/query/bvh32_geometry_raycast.d.ts.map +1 -1
- package/src/engine/graphics/geometry/buffered/query/bvh32_geometry_raycast.js +3 -5
- package/src/engine/graphics/texture/3d/scs3d_sample_linear3.d.ts +20 -0
- package/src/engine/graphics/texture/3d/scs3d_sample_linear3.d.ts.map +1 -1
- package/src/engine/graphics/texture/3d/scs3d_sample_linear3.js +36 -0
- package/src/engine/graphics3/AnimationGraphSystem.d.ts +2 -0
- package/src/engine/graphics3/AnimationGraphSystem.d.ts.map +1 -1
- package/src/engine/graphics3/AnimationGraphSystem.js +337 -329
- package/src/engine/graphics3/AnimationSystem.d.ts +2 -0
- package/src/engine/graphics3/AnimationSystem.d.ts.map +1 -1
- package/src/engine/graphics3/AnimationSystem.js +360 -352
- package/src/engine/graphics3/WaterSystem.d.ts +12 -3
- package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
- package/src/engine/graphics3/WaterSystem.js +78 -46
- package/src/engine/input/devices/PointerDevice.d.ts.map +1 -1
- package/src/engine/input/devices/PointerDevice.js +29 -4
- package/src/engine/intelligence/behavior/selector/WeightedRandomBehavior.d.ts +1 -0
- package/src/engine/intelligence/behavior/selector/WeightedRandomBehavior.d.ts.map +1 -1
- package/src/engine/intelligence/behavior/selector/WeightedRandomBehavior.js +14 -3
- package/src/engine/physics/cloth/MEASUREMENTS.md +130 -0
- package/src/engine/physics/cloth/PLAN.md +16 -1
- package/src/engine/physics/cloth/ecs/ClothColliderSystem.d.ts +5 -6
- package/src/engine/physics/cloth/ecs/ClothColliderSystem.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothColliderSystem.js +5 -6
- package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +4 -7
- package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothInstance.js +4 -7
- package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +4 -3
- package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothSystem.js +4 -3
- package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothWorld.js +10 -0
- package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts +24 -24
- package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_seed_rig.js +46 -43
- package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts +1 -1
- package/src/engine/physics/cloth/ecs/cloth_write_back.js +1 -1
- package/src/engine/physics/constraint/solve_constraints.d.ts +0 -35
- package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
- package/src/engine/physics/constraint/solve_constraints.js +1 -83
- package/src/engine/physics/ecs/ContactProbe.d.ts +73 -0
- package/src/engine/physics/ecs/ContactProbe.d.ts.map +1 -0
- package/src/engine/physics/ecs/ContactProbe.js +103 -0
- package/src/engine/physics/ecs/ContactProbeSystem.d.ts +44 -0
- package/src/engine/physics/ecs/ContactProbeSystem.d.ts.map +1 -0
- package/src/engine/physics/ecs/ContactProbeSystem.js +397 -0
- package/src/engine/physics/fluid/ecs/FluidComponent.d.ts +1 -1
- package/src/engine/physics/fluid/ecs/FluidComponent.js +1 -1
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_velocity.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_velocity.js +12 -3
- package/src/engine/physics/fluid/solver/v3_grid_advect_sl_velocity.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_advect_sl_velocity.js +8 -2
- package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.d.ts +19 -0
- package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.js +41 -2
- package/src/engine/physics/fluid/solver/v3_mac_advect_scalar.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_mac_advect_scalar.js +9 -3
- package/src/engine/physics/fluid/solver/v3_mac_advect_sl_velocity.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_mac_advect_sl_velocity.js +26 -10
- package/src/engine/physics/inverse_kinematics/frame_fabrik/FrameFabrikProblem.d.ts +205 -0
- package/src/engine/physics/inverse_kinematics/frame_fabrik/FrameFabrikProblem.d.ts.map +1 -0
- package/src/engine/physics/inverse_kinematics/frame_fabrik/FrameFabrikProblem.js +245 -0
- package/src/engine/physics/inverse_kinematics/frame_fabrik/frame_fabrik_limit_from_joint.d.ts +21 -0
- package/src/engine/physics/inverse_kinematics/frame_fabrik/frame_fabrik_limit_from_joint.d.ts.map +1 -0
- package/src/engine/physics/inverse_kinematics/frame_fabrik/frame_fabrik_limit_from_joint.js +63 -0
- package/src/engine/physics/inverse_kinematics/frame_fabrik/frame_fabrik_solve.d.ts +33 -0
- package/src/engine/physics/inverse_kinematics/frame_fabrik/frame_fabrik_solve.d.ts.map +1 -0
- package/src/engine/physics/inverse_kinematics/frame_fabrik/frame_fabrik_solve.js +874 -0
- package/src/engine/physics/inverse_kinematics/joint_hinge_mid_axis.d.ts +27 -0
- package/src/engine/physics/inverse_kinematics/joint_hinge_mid_axis.d.ts.map +1 -0
- package/src/engine/physics/inverse_kinematics/joint_hinge_mid_axis.js +77 -0
- package/src/engine/physics/inverse_kinematics/two_bone_path_solve.d.ts +36 -0
- package/src/engine/physics/inverse_kinematics/two_bone_path_solve.d.ts.map +1 -0
- package/src/engine/physics/inverse_kinematics/two_bone_path_solve.js +97 -0
- package/src/engine/physics/inverse_kinematics/two_joint_ik.d.ts +33 -11
- package/src/engine/physics/inverse_kinematics/two_joint_ik.d.ts.map +1 -1
- package/src/engine/physics/inverse_kinematics/two_joint_ik.js +158 -67
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts +3 -6
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.js +22 -9
- package/src/engine/ui/DraggableAspect.d.ts +5 -1
- package/src/engine/ui/DraggableAspect.d.ts.map +1 -1
- package/src/engine/ui/DraggableAspect.js +61 -4
- package/src/format/image/avif/heif/ItemProperty.d.ts +1 -1
- package/src/format/image/avif/heif/ItemProperty.js +1 -1
- package/src/format/image/png/chunk/png_chunk_decode_iTXt.d.ts.map +1 -1
- package/src/format/image/png/chunk/png_chunk_decode_iTXt.js +17 -6
- package/src/shade/device/mock/SoftwareGPUDevice.d.ts +2 -4
- package/src/shade/device/mock/SoftwareGPUDevice.d.ts.map +1 -1
- package/src/shade/device/mock/SoftwareGPUDevice.js +20 -13
- package/src/shade/playground/skinned_blas_refit/sweep_views.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/sweep_views.js +2 -1
- package/src/shade/playground/water_refraction/README.md +10 -11
- package/src/shade/playground/water_refraction/main.js +9 -8
- package/src/shade/playground/water_refraction/world.js +1 -1
- package/src/shade/playground/water_surface/README.md +43 -0
- package/src/shade/playground/water_surface/index.html +30 -0
- package/src/shade/playground/water_surface/main.d.ts +2 -0
- package/src/shade/playground/water_surface/main.d.ts.map +1 -0
- package/src/shade/playground/water_surface/main.js +154 -0
- package/src/shade/playground/water_surface/world.d.ts +44 -0
- package/src/shade/playground/water_surface/world.d.ts.map +1 -0
- package/src/shade/playground/water_surface/world.js +160 -0
- package/src/shade/renderer/animation/ANIMATION_TRACK_BINDING_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/animation/ANIMATION_TRACK_BINDING_STRUCT.js +10 -1
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts +10 -4
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
- package/src/shade/renderer/animation/GPUAnimationManager.js +251 -23
- package/src/shade/renderer/animation/POSE_ACCUMULATOR_LAYOUT.d.ts +12 -4
- package/src/shade/renderer/animation/POSE_ACCUMULATOR_LAYOUT.d.ts.map +1 -1
- package/src/shade/renderer/animation/POSE_ACCUMULATOR_LAYOUT.js +12 -4
- package/src/shade/renderer/animation/chunk_pose_accumulator.d.ts.map +1 -1
- package/src/shade/renderer/animation/chunk_pose_accumulator.js +14 -1
- package/src/shade/renderer/animation/pose/PoseChannelIndex.d.ts +78 -0
- package/src/shade/renderer/animation/pose/PoseChannelIndex.d.ts.map +1 -0
- package/src/shade/renderer/animation/pose/PoseChannelIndex.js +274 -0
- package/src/shade/renderer/animation/pose/PosePlayback.d.ts +40 -3
- package/src/shade/renderer/animation/pose/PosePlayback.d.ts.map +1 -1
- package/src/shade/renderer/animation/pose/PosePlayback.js +77 -3
- package/src/shade/renderer/animation/pose/pose_evaluate_local.d.ts +21 -3
- package/src/shade/renderer/animation/pose/pose_evaluate_local.d.ts.map +1 -1
- package/src/shade/renderer/animation/pose/pose_evaluate_local.js +79 -11
- package/src/shade/renderer/animation/shader_animation_apply.js +1 -1
- package/src/shade/renderer/animation/shader_animation_pose_resolve.d.ts +3 -2
- package/src/shade/renderer/animation/shader_animation_pose_resolve.d.ts.map +1 -1
- package/src/shade/renderer/animation/shader_animation_pose_resolve.js +187 -176
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts.map +1 -1
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +5 -0
- package/src/shade/renderer/binding/BufferBinding.js +2 -2
- package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/GPUTypedTable.js +5 -3
- package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.d.ts +10 -3
- package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.js +17 -9
- package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts +3 -0
- package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts.map +1 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraInput.js +16 -0
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts +2 -0
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts.map +1 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.js +11 -0
- package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.d.ts +1 -1
- package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.d.ts.map +1 -1
- package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.js +9 -3
- package/src/shade/renderer/geometry/util/geometry_simplify_redundant_topology.d.ts.map +1 -1
- package/src/shade/renderer/geometry/util/geometry_simplify_redundant_topology.js +2 -2
- package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +85 -0
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +12 -6
- package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.js +7 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.js +3 -1
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/object_property/chunk_write_object_property.d.ts +3 -1
- package/src/shade/renderer/object_property/chunk_write_object_property.d.ts.map +1 -1
- package/src/shade/renderer/object_property/chunk_write_object_property.js +5 -2
- package/src/shade/renderer/object_property/chunk_write_object_property_node3d.d.ts +4 -0
- package/src/shade/renderer/object_property/chunk_write_object_property_node3d.d.ts.map +1 -1
- package/src/shade/renderer/object_property/chunk_write_object_property_node3d.js +6 -1
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
- package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.js +2 -1
- package/src/shade/renderer/scene/optimization/geometry/geometry_deduplicate.d.ts +1 -1
- package/src/shade/renderer/scene/optimization/geometry/geometry_deduplicate.d.ts.map +1 -1
- package/src/shade/renderer/scene/optimization/geometry/geometry_deduplicate.js +135 -73
- package/src/shade/renderer/scene/optimization/geometry/internal/cover_greedy.d.ts +7 -0
- package/src/shade/renderer/scene/optimization/geometry/internal/cover_greedy.d.ts.map +1 -1
- package/src/shade/renderer/scene/optimization/geometry/internal/cover_greedy.js +29 -1
- package/src/shade/renderer/scene/optimization/geometry/internal/face_neighbourhood_hash.d.ts +19 -0
- package/src/shade/renderer/scene/optimization/geometry/internal/face_neighbourhood_hash.d.ts.map +1 -0
- package/src/shade/renderer/scene/optimization/geometry/internal/face_neighbourhood_hash.js +167 -0
- package/src/shade/renderer/scene/optimization/geometry/internal/neighbourhood_anchor_index.d.ts +25 -0
- package/src/shade/renderer/scene/optimization/geometry/internal/neighbourhood_anchor_index.d.ts.map +1 -0
- package/src/shade/renderer/scene/optimization/geometry/internal/neighbourhood_anchor_index.js +124 -0
- package/src/shade/renderer/scene/optimization/geometry/internal/seed_correspondence_from_face_pair.d.ts +19 -0
- package/src/shade/renderer/scene/optimization/geometry/internal/seed_correspondence_from_face_pair.d.ts.map +1 -0
- package/src/shade/renderer/scene/optimization/geometry/internal/seed_correspondence_from_face_pair.js +48 -0
- package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts +17 -1
- package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts.map +1 -1
- package/src/shade/renderer/scene/rows/GPUSceneRows.js +29 -3
- package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128_inverse.d.ts +13 -0
- package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128_inverse.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128_inverse.js +42 -0
- package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128x4_inverse_workgroup.d.ts +15 -0
- package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128x4_inverse_workgroup.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128x4_inverse_workgroup.js +39 -0
- package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts.map +1 -1
- package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.js +2 -0
- package/src/shade/renderer/water/GPUWaterRenderer.d.ts +15 -0
- package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
- package/src/shade/renderer/water/GPUWaterRenderer.js +110 -30
- package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +16 -8
- package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +1 -1
- package/src/shade/renderer/water/medium/GPUWaterBodies.js +31 -26
- package/src/shade/renderer/water/medium/shader_water_meshlets.d.ts +0 -1
- package/src/shade/renderer/water/medium/shader_water_meshlets.d.ts.map +1 -1
- package/src/shade/renderer/water/medium/shader_water_meshlets.js +1 -39
- package/src/shade/renderer/water/shader_water.d.ts +0 -1
- package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
- package/src/shade/renderer/water/shader_water.js +201 -72
- package/src/shade/renderer/water/surface/GPUWaterSurface.d.ts +70 -0
- package/src/shade/renderer/water/surface/GPUWaterSurface.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/GPUWaterSurface.js +199 -0
- package/src/shade/renderer/water/surface/WATER_WAVE_RECORD_STRUCT.d.ts +5 -2
- package/src/shade/renderer/water/surface/WATER_WAVE_RECORD_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/water/surface/WATER_WAVE_RECORD_STRUCT.js +32 -12
- package/src/shade/renderer/water/surface/chunk_water_surface_sample.d.ts +22 -0
- package/src/shade/renderer/water/surface/chunk_water_surface_sample.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/chunk_water_surface_sample.js +217 -0
- package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts +65 -11
- package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts.map +1 -1
- package/src/shade/renderer/water/surface/graph_water_tessellation.js +163 -28
- package/src/shade/renderer/water/surface/lod/WATER_LOD.d.ts +51 -0
- package/src/shade/renderer/water/surface/lod/WATER_LOD.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/lod/WATER_LOD.js +95 -0
- package/src/shade/renderer/water/surface/lod/chunk_water_lod.d.ts +10 -0
- package/src/shade/renderer/water/surface/lod/chunk_water_lod.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/lod/chunk_water_lod.js +127 -0
- package/src/shade/renderer/water/surface/lod/chunk_water_lod_cell.d.ts +17 -0
- package/src/shade/renderer/water/surface/lod/chunk_water_lod_cell.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/lod/chunk_water_lod_cell.js +246 -0
- package/src/shade/renderer/water/surface/lod/shader_water_lod.d.ts +118 -0
- package/src/shade/renderer/water/surface/lod/shader_water_lod.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/lod/shader_water_lod.js +727 -0
- package/src/shade/renderer/water/surface/particles/GPUWaterParticles.d.ts +60 -0
- package/src/shade/renderer/water/surface/particles/GPUWaterParticles.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/particles/GPUWaterParticles.js +228 -0
- package/src/shade/renderer/water/surface/particles/WATER_PARTICLE_STRUCT.d.ts +17 -0
- package/src/shade/renderer/water/surface/particles/WATER_PARTICLE_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/particles/WATER_PARTICLE_STRUCT.js +49 -0
- package/src/shade/renderer/water/surface/particles/chunk_water_particle.d.ts +22 -0
- package/src/shade/renderer/water/surface/particles/chunk_water_particle.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/particles/chunk_water_particle.js +143 -0
- package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.d.ts +15 -0
- package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.js +121 -0
- package/src/shade/renderer/water/surface/particles/shader_water_particles.d.ts +83 -0
- package/src/shade/renderer/water/surface/particles/shader_water_particles.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/particles/shader_water_particles.js +502 -0
- package/src/shade/renderer/water/surface/shader_water_cull.d.ts +42 -0
- package/src/shade/renderer/water/surface/shader_water_cull.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/shader_water_cull.js +269 -0
- package/src/shade/renderer/water/surface/shader_water_waves_combine.d.ts +15 -0
- package/src/shade/renderer/water/surface/shader_water_waves_combine.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/shader_water_waves_combine.js +73 -0
- package/src/shade/renderer/water/surface/shader_water_waves_derive.d.ts +11 -0
- package/src/shade/renderer/water/surface/shader_water_waves_derive.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/shader_water_waves_derive.js +78 -0
- package/src/shade/renderer/water/surface/spectrum/GPUWaterSpectrum.d.ts +35 -0
- package/src/shade/renderer/water/surface/spectrum/GPUWaterSpectrum.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/spectrum/GPUWaterSpectrum.js +103 -0
- package/src/shade/renderer/water/surface/spectrum/WATER_BAND_STATE.d.ts +22 -0
- package/src/shade/renderer/water/surface/spectrum/WATER_BAND_STATE.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/spectrum/WATER_BAND_STATE.js +57 -0
- package/src/shade/renderer/water/surface/spectrum/WATER_SPECTRUM.d.ts +22 -0
- package/src/shade/renderer/water/surface/spectrum/WATER_SPECTRUM.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/spectrum/WATER_SPECTRUM.js +25 -0
- package/src/shade/renderer/water/surface/spectrum/chunk_water_jonswap.d.ts +14 -0
- package/src/shade/renderer/water/surface/spectrum/chunk_water_jonswap.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/spectrum/chunk_water_jonswap.js +48 -0
- package/src/shade/renderer/water/surface/spectrum/chunk_water_spectrum.d.ts +23 -0
- package/src/shade/renderer/water/surface/spectrum/chunk_water_spectrum.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/spectrum/chunk_water_spectrum.js +169 -0
- package/src/shade/renderer/water/surface/spectrum/shader_water_spectrum.d.ts +26 -0
- package/src/shade/renderer/water/surface/spectrum/shader_water_spectrum.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/spectrum/shader_water_spectrum.js +161 -0
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +18 -6
- package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLLanguageCore.js +11 -3
- package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.d.ts +0 -28
- package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.d.ts.map +0 -1
- package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.js +0 -86
- package/src/engine/ecs/ik/IKConstraint.d.ts +0 -52
- package/src/engine/ecs/ik/IKConstraint.d.ts.map +0 -1
- package/src/engine/ecs/ik/IKConstraint.js +0 -95
- package/src/engine/ecs/ik/IKProblem.d.ts +0 -45
- package/src/engine/ecs/ik/IKProblem.d.ts.map +0 -1
- package/src/engine/ecs/ik/IKProblem.js +0 -58
- package/src/engine/ecs/ik/IKSolver.d.ts +0 -8
- package/src/engine/ecs/ik/IKSolver.d.ts.map +0 -1
- package/src/engine/ecs/ik/IKSolver.js +0 -9
- package/src/engine/ecs/ik/InverseKinematics.d.ts +0 -48
- package/src/engine/ecs/ik/InverseKinematics.d.ts.map +0 -1
- package/src/engine/ecs/ik/InverseKinematics.js +0 -119
- package/src/engine/ecs/ik/InverseKinematicsSerializationAdapter.d.ts +0 -19
- package/src/engine/ecs/ik/InverseKinematicsSerializationAdapter.d.ts.map +0 -1
- package/src/engine/ecs/ik/InverseKinematicsSerializationAdapter.js +0 -72
- package/src/engine/ecs/ik/InverseKinematicsSystem.d.ts +0 -54
- package/src/engine/ecs/ik/InverseKinematicsSystem.d.ts.map +0 -1
- package/src/engine/ecs/ik/InverseKinematicsSystem.js +0 -212
- package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.d.ts +0 -8
- package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.d.ts.map +0 -1
- package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js +0 -153
- package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.d.ts +0 -5
- package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.d.ts.map +0 -1
- package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.js +0 -120
- package/src/engine/ecs/ik/ik_bone_chain.d.ts +0 -64
- package/src/engine/ecs/ik/ik_bone_chain.d.ts.map +0 -1
- package/src/engine/ecs/ik/ik_bone_chain.js +0 -142
- package/src/engine/ecs/ik/ik_probe_terrain_contact.d.ts +0 -42
- package/src/engine/ecs/ik/ik_probe_terrain_contact.d.ts.map +0 -1
- package/src/engine/ecs/ik/ik_probe_terrain_contact.js +0 -123
- package/src/engine/ecs/ik/ik_write_local_rotation.d.ts +0 -18
- package/src/engine/ecs/ik/ik_write_local_rotation.d.ts.map +0 -1
- package/src/engine/ecs/ik/ik_write_local_rotation.js +0 -32
- package/src/engine/ecs/storage/binary/object-v2/serializerTestHarness.d.ts +0 -29
- package/src/engine/ecs/storage/binary/object-v2/serializerTestHarness.d.ts.map +0 -1
- package/src/engine/ecs/storage/binary/object-v2/serializerTestHarness.js +0 -44
- package/src/shade/renderer/water/medium/shader_water_bodies_derive.d.ts +0 -4
- package/src/shade/renderer/water/medium/shader_water_bodies_derive.d.ts.map +0 -1
- package/src/shade/renderer/water/medium/shader_water_bodies_derive.js +0 -32
- package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.d.ts +0 -25
- package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.d.ts.map +0 -1
- package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.js +0 -128
- package/src/shade/renderer/water/surface/chunk_water_tessellation.d.ts +0 -9
- package/src/shade/renderer/water/surface/chunk_water_tessellation.d.ts.map +0 -1
- package/src/shade/renderer/water/surface/chunk_water_tessellation.js +0 -144
- package/src/shade/renderer/water/surface/chunk_water_wave.d.ts +0 -13
- package/src/shade/renderer/water/surface/chunk_water_wave.d.ts.map +0 -1
- package/src/shade/renderer/water/surface/chunk_water_wave.js +0 -40
- package/src/shade/renderer/water/surface/shader_water_tiles.d.ts +0 -4
- package/src/shade/renderer/water/surface/shader_water_tiles.d.ts.map +0 -1
- package/src/shade/renderer/water/surface/shader_water_tiles.js +0 -123
- package/src/shade/renderer/water/surface/shader_water_wave_batch.d.ts +0 -6
- package/src/shade/renderer/water/surface/shader_water_wave_batch.d.ts.map +0 -1
- package/src/shade/renderer/water/surface/shader_water_wave_batch.js +0 -85
- package/src/shade/renderer/water/surface/shader_water_wave_domain.d.ts +0 -6
- package/src/shade/renderer/water/surface/shader_water_wave_domain.d.ts.map +0 -1
- package/src/shade/renderer/water/surface/shader_water_wave_domain.js +0 -83
|
@@ -0,0 +1,1456 @@
|
|
|
1
|
+
import { assert } from "../../../core/assert.js";
|
|
2
|
+
import Quaternion from "../../../core/geom/Quaternion.js";
|
|
3
|
+
import Vector3 from "../../../core/geom/Vector3.js";
|
|
4
|
+
import { ResourceAccessKind } from "../../../core/model/ResourceAccessKind.js";
|
|
5
|
+
import { ResourceAccessSpecification } from "../../../core/model/ResourceAccessSpecification.js";
|
|
6
|
+
import { pose_evaluate_local } from "../../../shade/renderer/animation/pose/pose_evaluate_local.js";
|
|
7
|
+
import { AnimationGraphController } from "../../graphics3/animation/AnimationGraphController.js";
|
|
8
|
+
import { collect_entity_playbacks } from "../../graphics3/pose/collect_entity_playbacks.js";
|
|
9
|
+
import { FrameFabrikProblem } from "../../physics/inverse_kinematics/frame_fabrik/FrameFabrikProblem.js";
|
|
10
|
+
import { frame_fabrik_limit_from_joint } from "../../physics/inverse_kinematics/frame_fabrik/frame_fabrik_limit_from_joint.js";
|
|
11
|
+
import { frame_fabrik_solve } from "../../physics/inverse_kinematics/frame_fabrik/frame_fabrik_solve.js";
|
|
12
|
+
import { joint_hinge_mid_axis } from "../../physics/inverse_kinematics/joint_hinge_mid_axis.js";
|
|
13
|
+
import { two_bone_path_solve } from "../../physics/inverse_kinematics/two_bone_path_solve.js";
|
|
14
|
+
import { Joint } from "../../physics/ecs/Joint.js";
|
|
15
|
+
import { Animation } from "../animation/Animation.js";
|
|
16
|
+
import { EventType } from "../EventType.js";
|
|
17
|
+
import { GPUStateAuthority } from "../gpu/GPUStateAuthority.js";
|
|
18
|
+
import { GPUStateAuthorityFlags } from "../gpu/GPUStateAuthorityFlags.js";
|
|
19
|
+
import { gpu_authority_hold } from "../gpu/gpu_authority_hold.js";
|
|
20
|
+
import { gpu_authority_unhold } from "../gpu/gpu_authority_unhold.js";
|
|
21
|
+
import { ParentEntity } from "../parent/ParentEntity.js";
|
|
22
|
+
import { System } from "../System.js";
|
|
23
|
+
import { TRANSFORM_ATTACHMENT_EVENT_CHANGE } from "../transform-attachment/TRANSFORM_ATTACHMENT_EVENT_CHANGE.js";
|
|
24
|
+
import { TransformAttachment } from "../transform-attachment/TransformAttachment.js";
|
|
25
|
+
import { T64WorldCache } from "../transform/T64WorldCache.js";
|
|
26
|
+
import {
|
|
27
|
+
Transform64,
|
|
28
|
+
TRANSFORM64_ROTATION_OFFSET,
|
|
29
|
+
TRANSFORM64_SCALE_OFFSET,
|
|
30
|
+
TRANSFORM64_TRANSLATION_OFFSET
|
|
31
|
+
} from "../transform/Transform64.js";
|
|
32
|
+
import { IKGoal } from "./IKGoal.js";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A bone IK holds: what it was before the hold, which goals need it.
|
|
36
|
+
*/
|
|
37
|
+
class IKHeldBone {
|
|
38
|
+
/**
|
|
39
|
+
* @type {number}
|
|
40
|
+
*/
|
|
41
|
+
entity = -1;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The bone's offset when IK took it: its authored local pose, the rest every clip evaluates
|
|
45
|
+
* from, and what IK writes back when it lets go.
|
|
46
|
+
* @type {Transform64}
|
|
47
|
+
*/
|
|
48
|
+
captured = new Transform64();
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @type {TransformAttachment|null}
|
|
52
|
+
*/
|
|
53
|
+
attachment = null;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* How many goals' paths hold it.
|
|
57
|
+
* @type {number}
|
|
58
|
+
*/
|
|
59
|
+
count = 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* One linked goal.
|
|
64
|
+
*/
|
|
65
|
+
class IKGoalRecord {
|
|
66
|
+
/**
|
|
67
|
+
* The effector.
|
|
68
|
+
* @type {number}
|
|
69
|
+
*/
|
|
70
|
+
entity = -1;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @type {IKGoal|null}
|
|
74
|
+
*/
|
|
75
|
+
goal = null;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Effector first, up to and including the chain root.
|
|
79
|
+
* @type {number[]}
|
|
80
|
+
*/
|
|
81
|
+
path = [];
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Whose clips drive the path: the model the effector belongs to.
|
|
85
|
+
* @type {number}
|
|
86
|
+
*/
|
|
87
|
+
model = -1;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Its target's model, likewise.
|
|
91
|
+
* @type {number}
|
|
92
|
+
*/
|
|
93
|
+
target_model = -1;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The knee's hinge, for a two-bone path.
|
|
97
|
+
* @type {Vector3}
|
|
98
|
+
*/
|
|
99
|
+
mid_axis = new Vector3();
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* A rejected goal holds nothing and pulls on nothing.
|
|
103
|
+
* @type {boolean}
|
|
104
|
+
*/
|
|
105
|
+
rejected = false;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Its removal listeners are on.
|
|
109
|
+
* @type {boolean}
|
|
110
|
+
*/
|
|
111
|
+
listening = false;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @type {IKGoalSystem|null}
|
|
115
|
+
*/
|
|
116
|
+
system = null;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The target or a bone of the path is gone, so the goal goes too.
|
|
120
|
+
*/
|
|
121
|
+
handle_removal() {
|
|
122
|
+
if (!this.listening) {
|
|
123
|
+
// a stale dispatch: the listener list is copied before it is walked
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const dataset = this.system.entityManager.dataset;
|
|
128
|
+
|
|
129
|
+
if (dataset.entityExists(this.entity)) {
|
|
130
|
+
dataset.removeComponentFromEntity(this.entity, IKGoal);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Connected paths, solved together: goals whose paths share a bone.
|
|
137
|
+
*/
|
|
138
|
+
class IKGroup {
|
|
139
|
+
/**
|
|
140
|
+
* @type {IKGoalRecord[]}
|
|
141
|
+
*/
|
|
142
|
+
goals = [];
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Every bone of the paths, parents before children; the first is the group's top.
|
|
146
|
+
* @type {number[]}
|
|
147
|
+
*/
|
|
148
|
+
joints = [];
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @type {Map<number, number>}
|
|
152
|
+
*/
|
|
153
|
+
index = new Map();
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Each bone's parent within the group, or -1 for the top.
|
|
157
|
+
* @type {Int32Array|null}
|
|
158
|
+
*/
|
|
159
|
+
parent = null;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The top's parent, which the group does not pose.
|
|
163
|
+
* @type {number}
|
|
164
|
+
*/
|
|
165
|
+
top_parent = -1;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Whose clips drive the bones.
|
|
169
|
+
* @type {number}
|
|
170
|
+
*/
|
|
171
|
+
model = -1;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* One goal on a path of three bones: solved analytically.
|
|
175
|
+
* @type {boolean}
|
|
176
|
+
*/
|
|
177
|
+
two_bone = false;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @type {FrameFabrikProblem|null}
|
|
181
|
+
*/
|
|
182
|
+
problem = null;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* The animated pose of each bone, local and composed.
|
|
186
|
+
* @type {Transform64[]}
|
|
187
|
+
*/
|
|
188
|
+
base_local = [];
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @type {Transform64[]}
|
|
192
|
+
*/
|
|
193
|
+
base_world = [];
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* The world pose of {@link top_parent}.
|
|
197
|
+
* @type {Transform64}
|
|
198
|
+
*/
|
|
199
|
+
above = new Transform64();
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Groups that solve first: this one's bones, or its targets, hang below theirs.
|
|
203
|
+
* @type {IKGroup[]}
|
|
204
|
+
*/
|
|
205
|
+
after = [];
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* @type {boolean}
|
|
209
|
+
*/
|
|
210
|
+
rejected = false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const scratch_world = new Transform64();
|
|
214
|
+
const scratch_target = new Transform64();
|
|
215
|
+
|
|
216
|
+
const v_relative = new Vector3();
|
|
217
|
+
|
|
218
|
+
const v_a = new Vector3();
|
|
219
|
+
const v_b = new Vector3();
|
|
220
|
+
const v_c = new Vector3();
|
|
221
|
+
const v_t = new Vector3();
|
|
222
|
+
|
|
223
|
+
const q_a_world = new Quaternion();
|
|
224
|
+
const q_b_world = new Quaternion();
|
|
225
|
+
const q_a_local = new Quaternion();
|
|
226
|
+
const q_b_local = new Quaternion();
|
|
227
|
+
const q_c_local = new Quaternion();
|
|
228
|
+
const q_target = new Quaternion();
|
|
229
|
+
|
|
230
|
+
const q_0 = new Quaternion();
|
|
231
|
+
const q_1 = new Quaternion();
|
|
232
|
+
const q_2 = new Quaternion();
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Brings bones to their goals ({@link IKGoal}): frame FABRIK over the connected paths of a model, or,
|
|
236
|
+
* for a lone path of three bones, the analytic two-bone solve.
|
|
237
|
+
*
|
|
238
|
+
* **IK holds the bones it poses** (`gpu_authority_hold` on their {@link TransformAttachment}), as
|
|
239
|
+
* cloth holds a cape's. The animation manager mutes every clip binding that names a held bone, and
|
|
240
|
+
* everything downstream — the GPU hierarchy, `t64_evaluate_world`, sockets, cloth anchors — reads the
|
|
241
|
+
* held offset, which IK writes every tick. Nothing downstream knows IK exists.
|
|
242
|
+
*
|
|
243
|
+
* - **Link:** a goal's path is held bone by bone, each offset captured, and the animated pose
|
|
244
|
+
* written at once, so no frame shows the bind pose.
|
|
245
|
+
* - **Every tick:** each bone's animated local pose is evaluated from its clips with the captured
|
|
246
|
+
* offset as its rest (`pose_evaluate_local`, never a held offset, which is IK's own answer), the
|
|
247
|
+
* paths are solved, and each held bone's whole local pose is written: translation, rotation and
|
|
248
|
+
* scale, since a hold mutes every channel.
|
|
249
|
+
* - **Unlink:** the captured offset is written back and the hold ended, for every bone no other goal
|
|
250
|
+
* still needs.
|
|
251
|
+
*
|
|
252
|
+
* A goal is rejected, holding nothing, for a chain root that is not the effector or an ancestor of
|
|
253
|
+
* it, a chain root that is no bone, a target that is gone or hangs below the goal's own path, a
|
|
254
|
+
* two-bone path whose knee has no bend side, and a cycle of groups each waiting on another. Each is a
|
|
255
|
+
* real branch and an assert, because asserts are stripped in production.
|
|
256
|
+
*
|
|
257
|
+
* A target hanging below another goal's bones in the same group is read from the solver's frames on
|
|
258
|
+
* every iteration. One hanging below another group's bones waits for that group to solve and write.
|
|
259
|
+
*
|
|
260
|
+
* Declared access: IK reads {@link Animation} and {@link AnimationGraphController}, which their
|
|
261
|
+
* drivers write, so it runs after the drivers have set this tick's clip times. A system that places a
|
|
262
|
+
* target through its own `TransformAttachment` (a physics contact probe) reads and writes that
|
|
263
|
+
* component as IK does: declared access leaves the two a cycle, and the scheduler keeps their
|
|
264
|
+
* registration order, so register it first.
|
|
265
|
+
*
|
|
266
|
+
* @author Alex Goldring
|
|
267
|
+
* @copyright Company Named Limited (c) 2026
|
|
268
|
+
*/
|
|
269
|
+
export class IKGoalSystem extends System {
|
|
270
|
+
|
|
271
|
+
dependencies = [IKGoal, TransformAttachment];
|
|
272
|
+
|
|
273
|
+
components_used = [
|
|
274
|
+
ResourceAccessSpecification.from(IKGoal, ResourceAccessKind.Read),
|
|
275
|
+
ResourceAccessSpecification.from(TransformAttachment, ResourceAccessKind.Read | ResourceAccessKind.Write),
|
|
276
|
+
ResourceAccessSpecification.from(Transform64, ResourceAccessKind.Read),
|
|
277
|
+
ResourceAccessSpecification.from(GPUStateAuthority, ResourceAccessKind.Read | ResourceAccessKind.Write),
|
|
278
|
+
ResourceAccessSpecification.from(ParentEntity, ResourceAccessKind.Read),
|
|
279
|
+
ResourceAccessSpecification.from(Joint, ResourceAccessKind.Read),
|
|
280
|
+
ResourceAccessSpecification.from(Animation, ResourceAccessKind.Read),
|
|
281
|
+
ResourceAccessSpecification.from(AnimationGraphController, ResourceAccessKind.Read),
|
|
282
|
+
];
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* The most iterations a frame FABRIK solve runs.
|
|
286
|
+
* @type {number}
|
|
287
|
+
*/
|
|
288
|
+
max_iterations = 16;
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* A frame FABRIK solve stops once an iteration moves no bone this far, in world units.
|
|
292
|
+
* @type {number}
|
|
293
|
+
*/
|
|
294
|
+
tolerance = 1e-6;
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* By effector.
|
|
298
|
+
* @type {Map<number, IKGoalRecord>}
|
|
299
|
+
*/
|
|
300
|
+
#records = new Map();
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* By bone.
|
|
304
|
+
* @type {Map<number, IKHeldBone>}
|
|
305
|
+
*/
|
|
306
|
+
#held = new Map();
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Groups in the order they solve.
|
|
310
|
+
* @type {IKGroup[]}
|
|
311
|
+
*/
|
|
312
|
+
#groups = [];
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Goals came or went since the groups were built.
|
|
316
|
+
* @type {boolean}
|
|
317
|
+
*/
|
|
318
|
+
#dirty = false;
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* What each model plays this tick.
|
|
322
|
+
* @type {Map<number, PosePlayback[]>}
|
|
323
|
+
*/
|
|
324
|
+
#playbacks = new Map();
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Exact worlds of this tick per model, forgotten whenever a group writes.
|
|
328
|
+
* @type {Map<number, T64WorldCache>}
|
|
329
|
+
*/
|
|
330
|
+
#caches = new Map();
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* The same caches, to walk without an iterator.
|
|
334
|
+
* @type {T64WorldCache[]}
|
|
335
|
+
*/
|
|
336
|
+
#cache_list = [];
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Which models' playbacks were collected this tick.
|
|
340
|
+
* @type {Set<number>}
|
|
341
|
+
*/
|
|
342
|
+
#collected = new Set();
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* @param {IKGoal} goal
|
|
346
|
+
* @param {TransformAttachment} attachment
|
|
347
|
+
* @param {number} entity
|
|
348
|
+
*/
|
|
349
|
+
link(goal, attachment, entity) {
|
|
350
|
+
const dataset = this.entityManager.dataset;
|
|
351
|
+
|
|
352
|
+
const record = new IKGoalRecord();
|
|
353
|
+
|
|
354
|
+
record.entity = entity;
|
|
355
|
+
record.goal = goal;
|
|
356
|
+
record.system = this;
|
|
357
|
+
record.model = model_of(dataset, entity);
|
|
358
|
+
record.target_model = dataset.entityExists(goal.target) ? model_of(dataset, goal.target) : -1;
|
|
359
|
+
|
|
360
|
+
this.#records.set(entity, record);
|
|
361
|
+
|
|
362
|
+
const problem = this.#validate(record);
|
|
363
|
+
|
|
364
|
+
if (problem === "") {
|
|
365
|
+
for (let i = 0; i < record.path.length; i++) {
|
|
366
|
+
this.#hold(record.path[i], record.model);
|
|
367
|
+
}
|
|
368
|
+
} else {
|
|
369
|
+
record.rejected = true;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
this.#listen(record);
|
|
373
|
+
|
|
374
|
+
this.#dirty = true;
|
|
375
|
+
|
|
376
|
+
assert.equal(problem, "", `IKGoal on entity ${entity} is rejected`);
|
|
377
|
+
|
|
378
|
+
this.#assert_limits(record);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* @param {IKGoal} goal
|
|
383
|
+
* @param {TransformAttachment} attachment
|
|
384
|
+
* @param {number} entity
|
|
385
|
+
*/
|
|
386
|
+
unlink(goal, attachment, entity) {
|
|
387
|
+
const record = this.#records.get(entity);
|
|
388
|
+
|
|
389
|
+
if (record === undefined) {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
this.#records.delete(entity);
|
|
394
|
+
|
|
395
|
+
this.#unlisten(record);
|
|
396
|
+
|
|
397
|
+
if (!record.rejected) {
|
|
398
|
+
this.#release_path(record);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
this.#dirty = true;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
update(time_delta_seconds) {
|
|
405
|
+
const dataset = this.entityManager.dataset;
|
|
406
|
+
|
|
407
|
+
if (dataset === null || dataset === undefined) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (this.#dirty) {
|
|
412
|
+
this.#regroup();
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
this.#collected.clear();
|
|
416
|
+
|
|
417
|
+
const groups = this.#groups;
|
|
418
|
+
|
|
419
|
+
for (let i = 0; i < groups.length; i++) {
|
|
420
|
+
const group = groups[i];
|
|
421
|
+
|
|
422
|
+
if (group.rejected) {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// everything evaluated so far may sit below what the last group wrote
|
|
427
|
+
this.#forget_worlds();
|
|
428
|
+
|
|
429
|
+
if (group.two_bone) {
|
|
430
|
+
this.#solve_two_bone(group);
|
|
431
|
+
} else {
|
|
432
|
+
this.#solve_fabrik(group);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// --- link ---
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Walk the goal's path and check it, filling `record.path`.
|
|
441
|
+
*
|
|
442
|
+
* @param {IKGoalRecord} record
|
|
443
|
+
* @returns {string} why the goal is rejected, or empty
|
|
444
|
+
*/
|
|
445
|
+
#validate(record) {
|
|
446
|
+
const dataset = this.entityManager.dataset;
|
|
447
|
+
const goal = record.goal;
|
|
448
|
+
|
|
449
|
+
const path = record.path;
|
|
450
|
+
|
|
451
|
+
path.length = 0;
|
|
452
|
+
|
|
453
|
+
let bone = record.entity;
|
|
454
|
+
|
|
455
|
+
while (true) {
|
|
456
|
+
const attachment = dataset.getComponent(bone, TransformAttachment);
|
|
457
|
+
|
|
458
|
+
if (attachment === undefined || attachment === null) {
|
|
459
|
+
return `bone ${bone} on the way up to chain_root ${goal.chain_root} has no TransformAttachment: the chain root is no bone, or no ancestor of the effector`;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
path.push(bone);
|
|
463
|
+
|
|
464
|
+
if (bone === goal.chain_root) {
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
bone = attachment.parent;
|
|
469
|
+
|
|
470
|
+
if (bone < 0 || !dataset.entityExists(bone)) {
|
|
471
|
+
return `chain_root ${goal.chain_root} is not the effector or one of its ancestors`;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (!dataset.entityExists(goal.target)) {
|
|
476
|
+
return `target ${goal.target} does not exist`;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// a target the effector carries moves with every answer; one below another bone of the path
|
|
480
|
+
// is found when the groups are known, since another goal's bones may carry it in between
|
|
481
|
+
for (let e = goal.target; e >= 0 && dataset.entityExists(e);) {
|
|
482
|
+
if (e === record.entity) {
|
|
483
|
+
return `target ${goal.target} hangs below its own effector: it would chase the answer`;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const attachment = dataset.getComponent(e, TransformAttachment);
|
|
487
|
+
|
|
488
|
+
if (attachment === undefined || attachment === null) {
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
e = attachment.parent;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (path.length === 3) {
|
|
496
|
+
const knee = path[1];
|
|
497
|
+
|
|
498
|
+
const joint = dataset.getComponent(knee, Joint);
|
|
499
|
+
|
|
500
|
+
if (joint === undefined || joint === null || !joint_hinge_mid_axis(record.mid_axis, joint)) {
|
|
501
|
+
return `the knee ${knee} of a two-bone path has no bend side: it needs a physics Joint hinged with limits that reach further one way`;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return "";
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* The physics Joint on each bone of the path is that bone's limit, read as physics reads it:
|
|
510
|
+
* `entityA` the parent bone, frames that agree at the bone's rest, angles by swing and twist.
|
|
511
|
+
*
|
|
512
|
+
* @param {IKGoalRecord} record
|
|
513
|
+
*/
|
|
514
|
+
#assert_limits(record) {
|
|
515
|
+
const dataset = this.entityManager.dataset;
|
|
516
|
+
const path = record.path;
|
|
517
|
+
|
|
518
|
+
for (let i = 0; i < path.length; i++) {
|
|
519
|
+
const joint = dataset.getComponent(path[i], Joint);
|
|
520
|
+
|
|
521
|
+
if (joint === undefined || joint === null) {
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const held = this.#held.get(path[i]);
|
|
526
|
+
|
|
527
|
+
const attachment = dataset.getComponent(path[i], TransformAttachment);
|
|
528
|
+
|
|
529
|
+
assert.equal(joint.entityA, attachment.parent, `the Joint on bone ${path[i]} has entityA ${joint.entityA}, not its parent bone ${attachment.parent}`);
|
|
530
|
+
assert.ok(joint.swingTwist === true, `the Joint on bone ${path[i]} measures its angles by swing and twist (swingTwist), as IK clamps them`);
|
|
531
|
+
assert.ok(basis_a_is_rest_times_b(joint, held === undefined ? attachment.transform : held.captured), `the Joint on bone ${path[i]} has localBasisA = rest_local * localBasisB`);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Remove the goal when its target or a bone of its path is destroyed, as `TransformAttachment`
|
|
537
|
+
* goes when its parent does.
|
|
538
|
+
*
|
|
539
|
+
* @param {IKGoalRecord} record
|
|
540
|
+
*/
|
|
541
|
+
#listen(record) {
|
|
542
|
+
const dataset = this.entityManager.dataset;
|
|
543
|
+
|
|
544
|
+
if (dataset.entityExists(record.goal.target)) {
|
|
545
|
+
dataset.addEntityEventListener(record.goal.target, EventType.EntityRemoved, record.handle_removal, record);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// the effector's own removal takes the component with it
|
|
549
|
+
for (let i = 1; i < record.path.length; i++) {
|
|
550
|
+
dataset.addEntityEventListener(record.path[i], EventType.EntityRemoved, record.handle_removal, record);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
record.listening = true;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* @param {IKGoalRecord} record
|
|
558
|
+
*/
|
|
559
|
+
#unlisten(record) {
|
|
560
|
+
if (!record.listening) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
record.listening = false;
|
|
565
|
+
|
|
566
|
+
const dataset = this.entityManager.dataset;
|
|
567
|
+
|
|
568
|
+
if (dataset.entityExists(record.goal.target)) {
|
|
569
|
+
dataset.removeEntityEventListener(record.goal.target, EventType.EntityRemoved, record.handle_removal, record);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
for (let i = 1; i < record.path.length; i++) {
|
|
573
|
+
if (dataset.entityExists(record.path[i])) {
|
|
574
|
+
dataset.removeEntityEventListener(record.path[i], EventType.EntityRemoved, record.handle_removal, record);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Hold a bone for one more goal; the first takes it, captures its offset and writes its animated
|
|
581
|
+
* pose at once.
|
|
582
|
+
*
|
|
583
|
+
* @param {number} entity
|
|
584
|
+
* @param {number} model
|
|
585
|
+
*/
|
|
586
|
+
#hold(entity, model) {
|
|
587
|
+
const existing = this.#held.get(entity);
|
|
588
|
+
|
|
589
|
+
if (existing !== undefined) {
|
|
590
|
+
existing.count++;
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const dataset = this.entityManager.dataset;
|
|
595
|
+
|
|
596
|
+
const attachment = dataset.getComponent(entity, TransformAttachment);
|
|
597
|
+
|
|
598
|
+
const held = new IKHeldBone();
|
|
599
|
+
|
|
600
|
+
held.entity = entity;
|
|
601
|
+
held.attachment = attachment;
|
|
602
|
+
held.captured.copy(attachment.transform);
|
|
603
|
+
held.count = 1;
|
|
604
|
+
|
|
605
|
+
this.#held.set(entity, held);
|
|
606
|
+
|
|
607
|
+
// trips the existing assert if another CPU writer (cloth) holds it already
|
|
608
|
+
gpu_authority_hold(dataset, entity, GPUStateAuthorityFlags.TransformAttachment);
|
|
609
|
+
|
|
610
|
+
// the clip's pose, not the bind pose the offset holds, from the first frame of the hold
|
|
611
|
+
pose_evaluate_local(attachment.transform, entity, held.captured, this.#playbacks_of(model));
|
|
612
|
+
|
|
613
|
+
attachment.transform.updateMatrix();
|
|
614
|
+
|
|
615
|
+
dataset.sendEvent(entity, TRANSFORM_ATTACHMENT_EVENT_CHANGE);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Release one goal's hold on each bone of its path; a bone no goal holds any more gets its captured
|
|
620
|
+
* offset back and its hold ended.
|
|
621
|
+
*
|
|
622
|
+
* @param {IKGoalRecord} record
|
|
623
|
+
*/
|
|
624
|
+
#release_path(record) {
|
|
625
|
+
const dataset = this.entityManager.dataset;
|
|
626
|
+
|
|
627
|
+
for (let i = 0; i < record.path.length; i++) {
|
|
628
|
+
const entity = record.path[i];
|
|
629
|
+
|
|
630
|
+
const held = this.#held.get(entity);
|
|
631
|
+
|
|
632
|
+
if (held === undefined) {
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
held.count--;
|
|
637
|
+
|
|
638
|
+
if (held.count > 0) {
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
this.#held.delete(entity);
|
|
643
|
+
|
|
644
|
+
if (!dataset.entityExists(entity)) {
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
held.attachment.transform.copy(held.captured);
|
|
649
|
+
|
|
650
|
+
dataset.sendEvent(entity, TRANSFORM_ATTACHMENT_EVENT_CHANGE);
|
|
651
|
+
|
|
652
|
+
const authority = dataset.getComponent(entity, GPUStateAuthority);
|
|
653
|
+
|
|
654
|
+
if (authority !== undefined && authority !== null && (authority.held & GPUStateAuthorityFlags.TransformAttachment) !== 0) {
|
|
655
|
+
gpu_authority_unhold(dataset, entity, GPUStateAuthorityFlags.TransformAttachment);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// --- groups ---
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Group the goals by connected paths, order the groups by what hangs below what, and reject
|
|
664
|
+
* the groups of a cycle.
|
|
665
|
+
*/
|
|
666
|
+
#regroup() {
|
|
667
|
+
this.#dirty = false;
|
|
668
|
+
|
|
669
|
+
const dataset = this.entityManager.dataset;
|
|
670
|
+
|
|
671
|
+
let groups;
|
|
672
|
+
let group_of_bone;
|
|
673
|
+
let rejected_by_carrier = false;
|
|
674
|
+
|
|
675
|
+
// a goal rejected for its target leaves the groups, which are then formed again without it
|
|
676
|
+
while (true) {
|
|
677
|
+
group_of_bone = new Map();
|
|
678
|
+
groups = form_groups(this.#records, group_of_bone);
|
|
679
|
+
|
|
680
|
+
let rejected = false;
|
|
681
|
+
|
|
682
|
+
for (let i = 0; i < groups.length; i++) {
|
|
683
|
+
const group = groups[i];
|
|
684
|
+
|
|
685
|
+
for (let g = 0; g < group.goals.length; g++) {
|
|
686
|
+
const record = group.goals[g];
|
|
687
|
+
|
|
688
|
+
if (carrier_on_own_path(record, group_of_bone, dataset)) {
|
|
689
|
+
record.rejected = true;
|
|
690
|
+
this.#release_path(record);
|
|
691
|
+
rejected = true;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if (!rejected) {
|
|
697
|
+
break;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
rejected_by_carrier = true;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
for (let i = 0; i < groups.length; i++) {
|
|
704
|
+
build_group(groups[i], dataset);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// what each group waits on: groups whose bones its top or its targets hang below
|
|
708
|
+
for (let i = 0; i < groups.length; i++) {
|
|
709
|
+
const group = groups[i];
|
|
710
|
+
|
|
711
|
+
add_waits(group, group.top_parent, group_of_bone, dataset);
|
|
712
|
+
|
|
713
|
+
for (let g = 0; g < group.goals.length; g++) {
|
|
714
|
+
add_waits(group, group.goals[g].goal.target, group_of_bone, dataset);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// order: Tarjan's components come out with every component a group waits on before it; a
|
|
719
|
+
// component of more than one group is a cycle, and every group on it is rejected
|
|
720
|
+
const ordered = order_groups(groups);
|
|
721
|
+
|
|
722
|
+
for (let i = 0; i < groups.length; i++) {
|
|
723
|
+
const group = groups[i];
|
|
724
|
+
|
|
725
|
+
if (!group.rejected) {
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
for (let g = 0; g < group.goals.length; g++) {
|
|
730
|
+
const record = group.goals[g];
|
|
731
|
+
|
|
732
|
+
if (record.rejected) {
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
record.rejected = true;
|
|
737
|
+
|
|
738
|
+
this.#release_path(record);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
this.#groups = ordered;
|
|
743
|
+
|
|
744
|
+
let cycle = false;
|
|
745
|
+
|
|
746
|
+
for (let i = 0; i < groups.length; i++) {
|
|
747
|
+
cycle = cycle || groups[i].rejected;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
assert.notOk(rejected_by_carrier, 'an IK goal\'s target hangs below a bone of its own path, with no other goal\'s bone in between: it would chase the answer');
|
|
751
|
+
assert.notOk(cycle, 'IK groups wait on each other in a cycle: each one\'s bones or targets hang below the other\'s');
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// --- solve ---
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* @param {number} model
|
|
758
|
+
* @returns {PosePlayback[]}
|
|
759
|
+
*/
|
|
760
|
+
#playbacks_of(model) {
|
|
761
|
+
let list = this.#playbacks.get(model);
|
|
762
|
+
|
|
763
|
+
if (list === undefined) {
|
|
764
|
+
list = [];
|
|
765
|
+
this.#playbacks.set(model, list);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
if (!this.#collected.has(model)) {
|
|
769
|
+
this.#collected.add(model);
|
|
770
|
+
|
|
771
|
+
list.length = 0;
|
|
772
|
+
|
|
773
|
+
if (model >= 0) {
|
|
774
|
+
collect_entity_playbacks(list, this.entityManager, model);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return list;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* @param {number} model
|
|
783
|
+
* @returns {T64WorldCache}
|
|
784
|
+
*/
|
|
785
|
+
#cache_of(model) {
|
|
786
|
+
let cache = this.#caches.get(model);
|
|
787
|
+
|
|
788
|
+
if (cache === undefined) {
|
|
789
|
+
cache = new T64WorldCache();
|
|
790
|
+
this.#caches.set(model, cache);
|
|
791
|
+
this.#cache_list.push(cache);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
return cache;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
#forget_worlds() {
|
|
798
|
+
const caches = this.#cache_list;
|
|
799
|
+
|
|
800
|
+
for (let i = 0; i < caches.length; i++) {
|
|
801
|
+
caches[i].invalidate();
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* The world pose of `entity` this tick, exactly.
|
|
807
|
+
*
|
|
808
|
+
* @param {Transform64} result
|
|
809
|
+
* @param {number} entity
|
|
810
|
+
* @param {number} model
|
|
811
|
+
*/
|
|
812
|
+
#world_of(result, entity, model) {
|
|
813
|
+
this.#cache_of(model).evaluate(result, this.entityManager.dataset, entity, this.#playbacks_of(model));
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* The group's bones as animated, local and composed under the frame above the group.
|
|
818
|
+
*
|
|
819
|
+
* @param {IKGroup} group
|
|
820
|
+
*/
|
|
821
|
+
#evaluate_base(group) {
|
|
822
|
+
const playbacks = this.#playbacks_of(group.model);
|
|
823
|
+
|
|
824
|
+
if (group.top_parent >= 0) {
|
|
825
|
+
this.#world_of(group.above, group.top_parent, group.model);
|
|
826
|
+
} else {
|
|
827
|
+
group.above.makeIdentity();
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
const joints = group.joints;
|
|
831
|
+
|
|
832
|
+
for (let j = 0; j < joints.length; j++) {
|
|
833
|
+
const held = this.#held.get(joints[j]);
|
|
834
|
+
|
|
835
|
+
const local = group.base_local[j];
|
|
836
|
+
|
|
837
|
+
pose_evaluate_local(local, joints[j], held.captured, playbacks);
|
|
838
|
+
|
|
839
|
+
local.updateMatrix();
|
|
840
|
+
|
|
841
|
+
const p = group.parent[j];
|
|
842
|
+
|
|
843
|
+
group.base_world[j].multiplyTransforms(p < 0 ? group.above : group.base_world[p], local);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
/**
|
|
848
|
+
* Where a goal's target is, as animated, into `result`, and which bone of the group carries it:
|
|
849
|
+
* the deepest one it hangs below, or -1 for a target the group does not move.
|
|
850
|
+
*
|
|
851
|
+
* @param {Transform64} result
|
|
852
|
+
* @param {IKGroup} group
|
|
853
|
+
* @param {IKGoalRecord} record
|
|
854
|
+
* @returns {number} index of the carrying bone in the group, or -1
|
|
855
|
+
*/
|
|
856
|
+
#target_of(result, group, record) {
|
|
857
|
+
const dataset = this.entityManager.dataset;
|
|
858
|
+
const target = record.goal.target;
|
|
859
|
+
|
|
860
|
+
this.#world_of(result, target, record.target_model);
|
|
861
|
+
|
|
862
|
+
let carrier = -1;
|
|
863
|
+
|
|
864
|
+
for (let e = target; e >= 0;) {
|
|
865
|
+
const index = group.index.get(e);
|
|
866
|
+
|
|
867
|
+
if (index !== undefined) {
|
|
868
|
+
carrier = index;
|
|
869
|
+
break;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
const attachment = dataset.getComponent(e, TransformAttachment);
|
|
873
|
+
|
|
874
|
+
if (attachment === undefined || attachment === null) {
|
|
875
|
+
break;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
e = attachment.parent;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
if (carrier < 0) {
|
|
882
|
+
return -1;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// the target relative to its carrier is the same whichever pose the carrier is in: put it
|
|
886
|
+
// under the carrier as animated
|
|
887
|
+
const carrier_world = scratch_world;
|
|
888
|
+
const carrier_base = group.base_world[carrier];
|
|
889
|
+
|
|
890
|
+
this.#world_of(carrier_world, group.joints[carrier], record.target_model);
|
|
891
|
+
|
|
892
|
+
// relative = conj(R_c) (P_t - P_c) / s_c, and conj(R_c) R_t
|
|
893
|
+
read_rotation(q_2, carrier_world);
|
|
894
|
+
q_2.conjugate();
|
|
895
|
+
|
|
896
|
+
v_relative.set(
|
|
897
|
+
result[TRANSFORM64_TRANSLATION_OFFSET] - carrier_world[TRANSFORM64_TRANSLATION_OFFSET],
|
|
898
|
+
result[TRANSFORM64_TRANSLATION_OFFSET + 1] - carrier_world[TRANSFORM64_TRANSLATION_OFFSET + 1],
|
|
899
|
+
result[TRANSFORM64_TRANSLATION_OFFSET + 2] - carrier_world[TRANSFORM64_TRANSLATION_OFFSET + 2]
|
|
900
|
+
);
|
|
901
|
+
|
|
902
|
+
v_relative.applyQuaternion(q_2);
|
|
903
|
+
|
|
904
|
+
// uniform scale: the same bone, animated and held, differs by nothing else
|
|
905
|
+
v_relative.multiplyScalar(carrier_base[TRANSFORM64_SCALE_OFFSET] / carrier_world[TRANSFORM64_SCALE_OFFSET]);
|
|
906
|
+
|
|
907
|
+
read_rotation(q_1, result);
|
|
908
|
+
q_2.multiply(q_1);
|
|
909
|
+
|
|
910
|
+
// under the carrier as animated
|
|
911
|
+
read_rotation(q_1, carrier_base);
|
|
912
|
+
|
|
913
|
+
v_relative.applyQuaternion(q_1);
|
|
914
|
+
|
|
915
|
+
q_1.multiply(q_2);
|
|
916
|
+
|
|
917
|
+
result.setTranslation(
|
|
918
|
+
carrier_base[TRANSFORM64_TRANSLATION_OFFSET] + v_relative.x,
|
|
919
|
+
carrier_base[TRANSFORM64_TRANSLATION_OFFSET + 1] + v_relative.y,
|
|
920
|
+
carrier_base[TRANSFORM64_TRANSLATION_OFFSET + 2] + v_relative.z
|
|
921
|
+
);
|
|
922
|
+
|
|
923
|
+
result.setRotation(q_1.x, q_1.y, q_1.z, q_1.w);
|
|
924
|
+
|
|
925
|
+
return carrier;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* @param {IKGroup} group
|
|
930
|
+
*/
|
|
931
|
+
#solve_fabrik(group) {
|
|
932
|
+
this.#evaluate_base(group);
|
|
933
|
+
|
|
934
|
+
const problem = group.problem;
|
|
935
|
+
const joints = group.joints;
|
|
936
|
+
const n = joints.length;
|
|
937
|
+
|
|
938
|
+
problem.joint_count = n;
|
|
939
|
+
problem.goal_count = group.goals.length;
|
|
940
|
+
|
|
941
|
+
for (let j = 0; j < n; j++) {
|
|
942
|
+
const world = group.base_world[j];
|
|
943
|
+
const local = group.base_local[j];
|
|
944
|
+
|
|
945
|
+
problem.joint_parent[j] = group.parent[j];
|
|
946
|
+
|
|
947
|
+
copy3(problem.base_position, j * 3, world, TRANSFORM64_TRANSLATION_OFFSET);
|
|
948
|
+
copy4(problem.base_rotation, j * 4, world, TRANSFORM64_ROTATION_OFFSET);
|
|
949
|
+
copy4(problem.base_local_rotation, j * 4, local, TRANSFORM64_ROTATION_OFFSET);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
for (let g = 0; g < group.goals.length; g++) {
|
|
953
|
+
const record = group.goals[g];
|
|
954
|
+
const goal = record.goal;
|
|
955
|
+
|
|
956
|
+
problem.goal_effector[g] = group.index.get(record.entity);
|
|
957
|
+
problem.goal_root[g] = group.index.get(goal.chain_root);
|
|
958
|
+
problem.goal_weight[g] = goal.position_weight;
|
|
959
|
+
|
|
960
|
+
problem.goal_target_joint[g] = this.#target_of(scratch_target, group, record);
|
|
961
|
+
|
|
962
|
+
copy3(problem.goal_target, g * 3, scratch_target, TRANSFORM64_TRANSLATION_OFFSET);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
frame_fabrik_solve(problem, this.max_iterations, this.tolerance);
|
|
966
|
+
|
|
967
|
+
const rotation = problem.solved_rotation;
|
|
968
|
+
|
|
969
|
+
// each effector turned toward its target's rotation
|
|
970
|
+
for (let g = 0; g < group.goals.length; g++) {
|
|
971
|
+
const record = group.goals[g];
|
|
972
|
+
const weight = record.goal.rotation_weight;
|
|
973
|
+
|
|
974
|
+
if (weight === 0) {
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
const carrier = this.#target_of(scratch_target, group, record);
|
|
979
|
+
|
|
980
|
+
read_rotation(q_target, scratch_target);
|
|
981
|
+
|
|
982
|
+
if (carrier >= 0) {
|
|
983
|
+
// carried from the carrier's animated frame into its solved one
|
|
984
|
+
read_array_rotation(q_0, rotation, carrier * 4);
|
|
985
|
+
read_rotation(q_1, group.base_world[carrier]);
|
|
986
|
+
q_1.conjugate();
|
|
987
|
+
q_0.multiply(q_1);
|
|
988
|
+
q_target.multiplyQuaternions(q_0, q_target);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
const e4 = group.index.get(record.entity) * 4;
|
|
992
|
+
|
|
993
|
+
read_array_rotation(q_0, rotation, e4);
|
|
994
|
+
|
|
995
|
+
if (weight === 1) {
|
|
996
|
+
q_0.copy(q_target);
|
|
997
|
+
} else {
|
|
998
|
+
q_0.slerpQuaternions(q_0, q_target, weight);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
write_array_rotation(rotation, e4, q_0);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// and written, whole, as each bone's local pose
|
|
1005
|
+
const dataset = this.entityManager.dataset;
|
|
1006
|
+
|
|
1007
|
+
for (let j = 0; j < n; j++) {
|
|
1008
|
+
const p = group.parent[j];
|
|
1009
|
+
|
|
1010
|
+
if (p < 0) {
|
|
1011
|
+
read_rotation(q_0, group.above);
|
|
1012
|
+
} else {
|
|
1013
|
+
read_array_rotation(q_0, rotation, p * 4);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
q_0.conjugate();
|
|
1017
|
+
|
|
1018
|
+
read_array_rotation(q_1, rotation, j * 4);
|
|
1019
|
+
|
|
1020
|
+
q_0.multiply(q_1);
|
|
1021
|
+
|
|
1022
|
+
write_local(dataset, this.#held.get(joints[j]), group.base_local[j], q_0);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* @param {IKGroup} group
|
|
1028
|
+
*/
|
|
1029
|
+
#solve_two_bone(group) {
|
|
1030
|
+
this.#evaluate_base(group);
|
|
1031
|
+
|
|
1032
|
+
const record = group.goals[0];
|
|
1033
|
+
const goal = record.goal;
|
|
1034
|
+
|
|
1035
|
+
// joints are [hip, knee, ankle], parents first
|
|
1036
|
+
read_translation(v_a, group.base_world[0]);
|
|
1037
|
+
read_translation(v_b, group.base_world[1]);
|
|
1038
|
+
read_translation(v_c, group.base_world[2]);
|
|
1039
|
+
|
|
1040
|
+
read_rotation(q_a_world, group.base_world[0]);
|
|
1041
|
+
read_rotation(q_b_world, group.base_world[1]);
|
|
1042
|
+
|
|
1043
|
+
read_rotation(q_a_local, group.base_local[0]);
|
|
1044
|
+
read_rotation(q_b_local, group.base_local[1]);
|
|
1045
|
+
read_rotation(q_c_local, group.base_local[2]);
|
|
1046
|
+
|
|
1047
|
+
this.#target_of(scratch_target, group, record);
|
|
1048
|
+
|
|
1049
|
+
read_translation(v_t, scratch_target);
|
|
1050
|
+
read_rotation(q_target, scratch_target);
|
|
1051
|
+
|
|
1052
|
+
// the hip's world is the frame above times its local: keep the pair consistent
|
|
1053
|
+
q_2.multiplyQuaternions(read_rotation(q_0, group.above), q_a_local);
|
|
1054
|
+
q_a_world.copy(q_2);
|
|
1055
|
+
|
|
1056
|
+
two_bone_path_solve(
|
|
1057
|
+
v_a, v_b, v_c, v_t,
|
|
1058
|
+
record.mid_axis,
|
|
1059
|
+
q_a_world, q_b_world,
|
|
1060
|
+
q_a_local, q_b_local, q_c_local,
|
|
1061
|
+
goal.position_weight,
|
|
1062
|
+
q_target, goal.rotation_weight
|
|
1063
|
+
);
|
|
1064
|
+
|
|
1065
|
+
const dataset = this.entityManager.dataset;
|
|
1066
|
+
const joints = group.joints;
|
|
1067
|
+
|
|
1068
|
+
write_local(dataset, this.#held.get(joints[0]), group.base_local[0], q_a_local);
|
|
1069
|
+
write_local(dataset, this.#held.get(joints[1]), group.base_local[1], q_b_local);
|
|
1070
|
+
write_local(dataset, this.#held.get(joints[2]), group.base_local[2], q_c_local);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
/**
|
|
1075
|
+
* The entity whose clips drive `entity`: the model its lifetime belongs to, or itself.
|
|
1076
|
+
*
|
|
1077
|
+
* @param {EntityComponentDataset} dataset
|
|
1078
|
+
* @param {number} entity
|
|
1079
|
+
* @returns {number}
|
|
1080
|
+
*/
|
|
1081
|
+
function model_of(dataset, entity) {
|
|
1082
|
+
const owner = dataset.getComponent(entity, ParentEntity);
|
|
1083
|
+
|
|
1084
|
+
if (owner === undefined || owner === null) {
|
|
1085
|
+
return entity;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
return owner.entity;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
/**
|
|
1092
|
+
* Goals grouped by connected paths: two goals whose paths share a bone are solved together.
|
|
1093
|
+
*
|
|
1094
|
+
* @param {Map<number, IKGoalRecord>} records
|
|
1095
|
+
* @param {Map<number, IKGroup>} group_of_bone filled
|
|
1096
|
+
* @returns {IKGroup[]}
|
|
1097
|
+
*/
|
|
1098
|
+
function form_groups(records, group_of_bone) {
|
|
1099
|
+
const groups = [];
|
|
1100
|
+
|
|
1101
|
+
for (const record of records.values()) {
|
|
1102
|
+
if (record.rejected) {
|
|
1103
|
+
continue;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// every group this goal's path touches merges into one
|
|
1107
|
+
let group = null;
|
|
1108
|
+
|
|
1109
|
+
for (let i = 0; i < record.path.length; i++) {
|
|
1110
|
+
const other = group_of_bone.get(record.path[i]);
|
|
1111
|
+
|
|
1112
|
+
if (other === undefined || other === group) {
|
|
1113
|
+
continue;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
if (group === null) {
|
|
1117
|
+
group = other;
|
|
1118
|
+
continue;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
for (let g = 0; g < other.goals.length; g++) {
|
|
1122
|
+
group.goals.push(other.goals[g]);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
for (const [bone, owner] of group_of_bone) {
|
|
1126
|
+
if (owner === other) {
|
|
1127
|
+
group_of_bone.set(bone, group);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
groups.splice(groups.indexOf(other), 1);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
if (group === null) {
|
|
1135
|
+
group = new IKGroup();
|
|
1136
|
+
groups.push(group);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
group.goals.push(record);
|
|
1140
|
+
|
|
1141
|
+
for (let i = 0; i < record.path.length; i++) {
|
|
1142
|
+
group_of_bone.set(record.path[i], group);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
return groups;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
/**
|
|
1150
|
+
* Whether the deepest posed bone a goal's target hangs below is a bone of the goal's own path: the
|
|
1151
|
+
* target then moves rigidly with the bones the goal turns to reach it. A grip on a weapon the other
|
|
1152
|
+
* hand holds is not that, though both hands share a chest: the other arm's bones carry it.
|
|
1153
|
+
*
|
|
1154
|
+
* @param {IKGoalRecord} record
|
|
1155
|
+
* @param {Map<number, IKGroup>} group_of_bone
|
|
1156
|
+
* @param {EntityComponentDataset} dataset
|
|
1157
|
+
* @returns {boolean}
|
|
1158
|
+
*/
|
|
1159
|
+
function carrier_on_own_path(record, group_of_bone, dataset) {
|
|
1160
|
+
for (let e = record.goal.target; e >= 0 && dataset.entityExists(e);) {
|
|
1161
|
+
if (group_of_bone.has(e)) {
|
|
1162
|
+
return record.path.indexOf(e) !== -1;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
const attachment = dataset.getComponent(e, TransformAttachment);
|
|
1166
|
+
|
|
1167
|
+
if (attachment === undefined || attachment === null) {
|
|
1168
|
+
return false;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
e = attachment.parent;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
return false;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/**
|
|
1178
|
+
* Whether `localBasisA = rest_local * localBasisB`, the Joint's frames agreeing at the bone's rest.
|
|
1179
|
+
*
|
|
1180
|
+
* @param {Joint} joint
|
|
1181
|
+
* @param {Transform64} rest
|
|
1182
|
+
* @returns {boolean}
|
|
1183
|
+
*/
|
|
1184
|
+
function basis_a_is_rest_times_b(joint, rest) {
|
|
1185
|
+
read_rotation(q_0, rest);
|
|
1186
|
+
q_1.set(joint.localBasisB[0], joint.localBasisB[1], joint.localBasisB[2], joint.localBasisB[3]);
|
|
1187
|
+
q_0.multiply(q_1);
|
|
1188
|
+
|
|
1189
|
+
const a = joint.localBasisA;
|
|
1190
|
+
|
|
1191
|
+
const dot = q_0.x * a[0] + q_0.y * a[1] + q_0.z * a[2] + q_0.w * a[3];
|
|
1192
|
+
|
|
1193
|
+
return Math.abs(dot) > 1 - 1e-6;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
/**
|
|
1197
|
+
* Lay out a group: its bones parents first, its top's parent, its model, its solver.
|
|
1198
|
+
*
|
|
1199
|
+
* @param {IKGroup} group
|
|
1200
|
+
* @param {EntityComponentDataset} dataset
|
|
1201
|
+
*/
|
|
1202
|
+
function build_group(group, dataset) {
|
|
1203
|
+
const bones = new Set();
|
|
1204
|
+
|
|
1205
|
+
for (let g = 0; g < group.goals.length; g++) {
|
|
1206
|
+
const path = group.goals[g].path;
|
|
1207
|
+
|
|
1208
|
+
for (let i = 0; i < path.length; i++) {
|
|
1209
|
+
bones.add(path[i]);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
// depth below the group's top, by walking up within the group
|
|
1214
|
+
const depth = new Map();
|
|
1215
|
+
|
|
1216
|
+
for (const bone of bones) {
|
|
1217
|
+
let d = 0;
|
|
1218
|
+
|
|
1219
|
+
for (let e = dataset.getComponent(bone, TransformAttachment).parent; bones.has(e); e = dataset.getComponent(e, TransformAttachment).parent) {
|
|
1220
|
+
d++;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
depth.set(bone, d);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
const joints = Array.from(bones);
|
|
1227
|
+
|
|
1228
|
+
joints.sort((a, b) => depth.get(a) - depth.get(b) || a - b);
|
|
1229
|
+
|
|
1230
|
+
group.joints = joints;
|
|
1231
|
+
|
|
1232
|
+
group.index = new Map();
|
|
1233
|
+
|
|
1234
|
+
for (let j = 0; j < joints.length; j++) {
|
|
1235
|
+
group.index.set(joints[j], j);
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
group.parent = new Int32Array(joints.length);
|
|
1239
|
+
|
|
1240
|
+
for (let j = 0; j < joints.length; j++) {
|
|
1241
|
+
const parent = dataset.getComponent(joints[j], TransformAttachment).parent;
|
|
1242
|
+
|
|
1243
|
+
const index = group.index.get(parent);
|
|
1244
|
+
|
|
1245
|
+
group.parent[j] = index === undefined ? -1 : index;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
assert.equal(group.parent[0], -1, 'a group has one top');
|
|
1249
|
+
|
|
1250
|
+
group.top_parent = dataset.getComponent(joints[0], TransformAttachment).parent;
|
|
1251
|
+
|
|
1252
|
+
group.model = group.goals[0].model;
|
|
1253
|
+
|
|
1254
|
+
group.two_bone = group.goals.length === 1 && group.goals[0].path.length === 3;
|
|
1255
|
+
|
|
1256
|
+
group.base_local = joints.map(() => new Transform64());
|
|
1257
|
+
group.base_world = joints.map(() => new Transform64());
|
|
1258
|
+
|
|
1259
|
+
if (group.two_bone) {
|
|
1260
|
+
group.problem = null;
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
const problem = new FrameFabrikProblem(joints.length, group.goals.length);
|
|
1265
|
+
|
|
1266
|
+
for (let j = 0; j < joints.length; j++) {
|
|
1267
|
+
const joint = dataset.getComponent(joints[j], Joint);
|
|
1268
|
+
|
|
1269
|
+
if (joint !== undefined && joint !== null) {
|
|
1270
|
+
frame_fabrik_limit_from_joint(problem, j, joint);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
group.problem = problem;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
/**
|
|
1278
|
+
* Note every group that `entity`, or anything it hangs below, is a bone of, as one `group` waits
|
|
1279
|
+
* on. Its own bones are no wait: a target among them is read from the solver's frames.
|
|
1280
|
+
*
|
|
1281
|
+
* @param {IKGroup} group
|
|
1282
|
+
* @param {number} entity
|
|
1283
|
+
* @param {Map<number, IKGroup>} group_of_bone
|
|
1284
|
+
* @param {EntityComponentDataset} dataset
|
|
1285
|
+
*/
|
|
1286
|
+
function add_waits(group, entity, group_of_bone, dataset) {
|
|
1287
|
+
for (let e = entity; e >= 0 && dataset.entityExists(e);) {
|
|
1288
|
+
const owner = group_of_bone.get(e);
|
|
1289
|
+
|
|
1290
|
+
if (owner !== undefined && owner !== group && group.after.indexOf(owner) === -1) {
|
|
1291
|
+
group.after.push(owner);
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
const attachment = dataset.getComponent(e, TransformAttachment);
|
|
1295
|
+
|
|
1296
|
+
if (attachment === undefined || attachment === null) {
|
|
1297
|
+
break;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
e = attachment.parent;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
/**
|
|
1305
|
+
* Order groups so each comes after every group it waits on, by Tarjan's strongly connected
|
|
1306
|
+
* components: they are emitted with everything a component waits on already out. A component of
|
|
1307
|
+
* more than one group is a cycle, and each of its groups is rejected; a group that only waits on a
|
|
1308
|
+
* cycle keeps its place, after it.
|
|
1309
|
+
*
|
|
1310
|
+
* @param {IKGroup[]} groups
|
|
1311
|
+
* @returns {IKGroup[]}
|
|
1312
|
+
*/
|
|
1313
|
+
function order_groups(groups) {
|
|
1314
|
+
const ordered = [];
|
|
1315
|
+
const stack = [];
|
|
1316
|
+
|
|
1317
|
+
let counter = 0;
|
|
1318
|
+
|
|
1319
|
+
const index = new Map();
|
|
1320
|
+
const low = new Map();
|
|
1321
|
+
const on_stack = new Set();
|
|
1322
|
+
|
|
1323
|
+
function connect(group) {
|
|
1324
|
+
index.set(group, counter);
|
|
1325
|
+
low.set(group, counter);
|
|
1326
|
+
counter++;
|
|
1327
|
+
|
|
1328
|
+
stack.push(group);
|
|
1329
|
+
on_stack.add(group);
|
|
1330
|
+
|
|
1331
|
+
for (let i = 0; i < group.after.length; i++) {
|
|
1332
|
+
const other = group.after[i];
|
|
1333
|
+
|
|
1334
|
+
if (!index.has(other)) {
|
|
1335
|
+
connect(other);
|
|
1336
|
+
low.set(group, Math.min(low.get(group), low.get(other)));
|
|
1337
|
+
} else if (on_stack.has(other)) {
|
|
1338
|
+
low.set(group, Math.min(low.get(group), index.get(other)));
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
if (low.get(group) !== index.get(group)) {
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
const component = [];
|
|
1347
|
+
|
|
1348
|
+
let member;
|
|
1349
|
+
|
|
1350
|
+
do {
|
|
1351
|
+
member = stack.pop();
|
|
1352
|
+
on_stack.delete(member);
|
|
1353
|
+
component.push(member);
|
|
1354
|
+
} while (member !== group);
|
|
1355
|
+
|
|
1356
|
+
for (let i = 0; i < component.length; i++) {
|
|
1357
|
+
component[i].rejected = component.length > 1;
|
|
1358
|
+
ordered.push(component[i]);
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
for (let i = 0; i < groups.length; i++) {
|
|
1363
|
+
if (!index.has(groups[i])) {
|
|
1364
|
+
connect(groups[i]);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
return ordered;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
function copy3(out, offset, transform, at) {
|
|
1372
|
+
out[offset] = transform[at];
|
|
1373
|
+
out[offset + 1] = transform[at + 1];
|
|
1374
|
+
out[offset + 2] = transform[at + 2];
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
function copy4(out, offset, transform, at) {
|
|
1378
|
+
out[offset] = transform[at];
|
|
1379
|
+
out[offset + 1] = transform[at + 1];
|
|
1380
|
+
out[offset + 2] = transform[at + 2];
|
|
1381
|
+
out[offset + 3] = transform[at + 3];
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
/**
|
|
1385
|
+
* @param {Vector3} out
|
|
1386
|
+
* @param {Transform64} transform
|
|
1387
|
+
* @returns {Vector3}
|
|
1388
|
+
*/
|
|
1389
|
+
function read_translation(out, transform) {
|
|
1390
|
+
out.set(
|
|
1391
|
+
transform[TRANSFORM64_TRANSLATION_OFFSET],
|
|
1392
|
+
transform[TRANSFORM64_TRANSLATION_OFFSET + 1],
|
|
1393
|
+
transform[TRANSFORM64_TRANSLATION_OFFSET + 2]
|
|
1394
|
+
);
|
|
1395
|
+
|
|
1396
|
+
return out;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
/**
|
|
1400
|
+
* @param {Quaternion} out
|
|
1401
|
+
* @param {Transform64} transform
|
|
1402
|
+
* @returns {Quaternion}
|
|
1403
|
+
*/
|
|
1404
|
+
function read_rotation(out, transform) {
|
|
1405
|
+
out.set(
|
|
1406
|
+
transform[TRANSFORM64_ROTATION_OFFSET],
|
|
1407
|
+
transform[TRANSFORM64_ROTATION_OFFSET + 1],
|
|
1408
|
+
transform[TRANSFORM64_ROTATION_OFFSET + 2],
|
|
1409
|
+
transform[TRANSFORM64_ROTATION_OFFSET + 3]
|
|
1410
|
+
);
|
|
1411
|
+
|
|
1412
|
+
return out;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
function read_array_rotation(out, array, offset) {
|
|
1416
|
+
out.set(array[offset], array[offset + 1], array[offset + 2], array[offset + 3]);
|
|
1417
|
+
|
|
1418
|
+
return out;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
function write_array_rotation(array, offset, q) {
|
|
1422
|
+
array[offset] = q.x;
|
|
1423
|
+
array[offset + 1] = q.y;
|
|
1424
|
+
array[offset + 2] = q.z;
|
|
1425
|
+
array[offset + 3] = q.w;
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
/**
|
|
1429
|
+
* A held bone's whole local pose: the animated translation and scale, the solved rotation.
|
|
1430
|
+
*
|
|
1431
|
+
* @param {EntityComponentDataset} dataset
|
|
1432
|
+
* @param {IKHeldBone} held
|
|
1433
|
+
* @param {Transform64} base
|
|
1434
|
+
* @param {Quaternion} rotation
|
|
1435
|
+
*/
|
|
1436
|
+
function write_local(dataset, held, base, rotation) {
|
|
1437
|
+
const local = held.attachment.transform;
|
|
1438
|
+
|
|
1439
|
+
local.setTranslation(
|
|
1440
|
+
base[TRANSFORM64_TRANSLATION_OFFSET],
|
|
1441
|
+
base[TRANSFORM64_TRANSLATION_OFFSET + 1],
|
|
1442
|
+
base[TRANSFORM64_TRANSLATION_OFFSET + 2]
|
|
1443
|
+
);
|
|
1444
|
+
|
|
1445
|
+
local.setRotation(rotation.x, rotation.y, rotation.z, rotation.w);
|
|
1446
|
+
|
|
1447
|
+
local.setScale(
|
|
1448
|
+
base[TRANSFORM64_SCALE_OFFSET],
|
|
1449
|
+
base[TRANSFORM64_SCALE_OFFSET + 1],
|
|
1450
|
+
base[TRANSFORM64_SCALE_OFFSET + 2]
|
|
1451
|
+
);
|
|
1452
|
+
|
|
1453
|
+
local.updateMatrix();
|
|
1454
|
+
|
|
1455
|
+
dataset.sendEvent(held.entity, TRANSFORM_ATTACHMENT_EVENT_CHANGE);
|
|
1456
|
+
}
|