@woosh/meep-engine 3.43.0 → 3.44.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/editor/prototypeEditorShell.d.ts.map +1 -1
- package/editor/prototypeEditorShell.js +4 -2
- package/package.json +1 -1
- package/src/engine/Clock.d.ts +15 -0
- package/src/engine/Clock.d.ts.map +1 -1
- package/src/engine/Clock.js +29 -5
- package/src/engine/Engine.d.ts.map +1 -1
- package/src/engine/Engine.js +11 -19
- package/src/engine/graphics/ecs/mesh-v2/MeshSkin.d.ts +57 -0
- package/src/engine/graphics/ecs/mesh-v2/MeshSkin.d.ts.map +1 -0
- package/src/engine/graphics/ecs/mesh-v2/MeshSkin.js +84 -0
- package/src/engine/graphics3/AnimationGraphSystem.d.ts +23 -13
- package/src/engine/graphics3/AnimationGraphSystem.d.ts.map +1 -1
- package/src/engine/graphics3/AnimationGraphSystem.js +61 -64
- package/src/engine/graphics3/AnimationSystem.d.ts +24 -15
- package/src/engine/graphics3/AnimationSystem.d.ts.map +1 -1
- package/src/engine/graphics3/AnimationSystem.js +67 -61
- package/src/engine/graphics3/MeshSkinSystem.d.ts +56 -0
- package/src/engine/graphics3/MeshSkinSystem.d.ts.map +1 -0
- package/src/engine/graphics3/MeshSkinSystem.js +317 -0
- package/src/engine/graphics3/MeshSystem.d.ts +15 -35
- package/src/engine/graphics3/MeshSystem.d.ts.map +1 -1
- package/src/engine/graphics3/MeshSystem.js +21 -298
- package/src/engine/graphics3/RowChains.d.ts +76 -0
- package/src/engine/graphics3/RowChains.d.ts.map +1 -0
- package/src/engine/graphics3/RowChains.js +347 -0
- package/src/engine/graphics3/ShadedGeometrySystem.d.ts +6 -0
- package/src/engine/graphics3/ShadedGeometrySystem.d.ts.map +1 -1
- package/src/engine/graphics3/ShadedGeometrySystem.js +329 -309
- package/src/engine/graphics3/animation/AnimationClips.d.ts +56 -0
- package/src/engine/graphics3/animation/AnimationClips.d.ts.map +1 -0
- package/src/engine/graphics3/animation/AnimationClips.js +79 -0
- package/src/engine/graphics3/animation/ClipListPlayer.d.ts +13 -14
- package/src/engine/graphics3/animation/ClipListPlayer.d.ts.map +1 -1
- package/src/engine/graphics3/animation/ClipListPlayer.js +262 -263
- package/src/engine/graphics3/animation/write_clip_playback.d.ts +3 -3
- package/src/engine/graphics3/animation/write_clip_playback.js +4 -4
- package/src/engine/graphics3/pose/query_entity_node_world_pose.d.ts +5 -0
- package/src/engine/graphics3/pose/query_entity_node_world_pose.d.ts.map +1 -1
- package/src/engine/graphics3/pose/query_entity_node_world_pose.js +80 -75
- package/src/engine/graphics3/prefab/PrefabInstance.d.ts +4 -24
- package/src/engine/graphics3/prefab/PrefabInstance.d.ts.map +1 -1
- package/src/engine/graphics3/prefab/PrefabInstance.js +60 -80
- package/src/engine/graphics3/prefab/prefab_instantiate.d.ts +16 -3
- package/src/engine/graphics3/prefab/prefab_instantiate.d.ts.map +1 -1
- package/src/engine/graphics3/prefab/prefab_instantiate.js +49 -12
- package/src/engine/graphics3/prefab/prefab_place.d.ts +31 -17
- package/src/engine/graphics3/prefab/prefab_place.d.ts.map +1 -1
- package/src/engine/graphics3/prefab/prefab_place.js +175 -130
- package/src/engine/graphics3/register_clips.d.ts +64 -0
- package/src/engine/graphics3/register_clips.d.ts.map +1 -0
- package/src/engine/graphics3/register_clips.js +114 -0
- package/src/engine/physics/cloth/MEASUREMENTS.md +795 -0
- package/src/engine/physics/cloth/PLAN.md +339 -9
- package/src/engine/physics/cloth/build/ClothProxy.d.ts +113 -61
- package/src/engine/physics/cloth/build/ClothProxy.d.ts.map +1 -1
- package/src/engine/physics/cloth/build/ClothProxy.js +142 -77
- package/src/engine/physics/cloth/build/cloth_bind_in_frame.d.ts +22 -0
- package/src/engine/physics/cloth/build/cloth_bind_in_frame.d.ts.map +1 -0
- package/src/engine/physics/cloth/build/cloth_bind_in_frame.js +75 -0
- package/src/engine/physics/cloth/build/cloth_proxy_from_joints.d.ts +43 -30
- package/src/engine/physics/cloth/build/cloth_proxy_from_joints.d.ts.map +1 -1
- package/src/engine/physics/cloth/build/cloth_proxy_from_joints.js +357 -284
- package/src/engine/physics/cloth/build/cloth_proxy_from_mesh.d.ts +62 -0
- package/src/engine/physics/cloth/build/cloth_proxy_from_mesh.d.ts.map +1 -0
- package/src/engine/physics/cloth/build/cloth_proxy_from_mesh.js +280 -0
- package/src/engine/physics/cloth/collider/cloth_collider_sdf.d.ts +1 -1
- package/src/engine/physics/cloth/collider/cloth_collider_sdf.js +1 -1
- package/src/engine/physics/cloth/ecs/Cloth.d.ts +17 -9
- package/src/engine/physics/cloth/ecs/Cloth.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/Cloth.js +220 -212
- package/src/engine/physics/cloth/ecs/ClothDynamicsFlags.js +6 -0
- package/src/engine/physics/cloth/ecs/ClothHold.d.ts +91 -0
- package/src/engine/physics/cloth/ecs/ClothHold.d.ts.map +1 -0
- package/src/engine/physics/cloth/ecs/ClothHold.js +103 -0
- package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +254 -48
- package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothInstance.js +277 -47
- package/src/engine/physics/cloth/ecs/ClothPin.d.ts +73 -0
- package/src/engine/physics/cloth/ecs/ClothPin.d.ts.map +1 -0
- package/src/engine/physics/cloth/ecs/ClothPin.js +92 -0
- package/src/engine/physics/cloth/ecs/ClothPinSerializationAdapter.d.ts +36 -0
- package/src/engine/physics/cloth/ecs/ClothPinSerializationAdapter.d.ts.map +1 -0
- package/src/engine/physics/cloth/ecs/ClothPinSerializationAdapter.js +61 -0
- package/src/engine/physics/cloth/ecs/ClothRig.d.ts +23 -17
- package/src/engine/physics/cloth/ecs/ClothRig.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothRig.js +29 -27
- package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +12 -14
- package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothSystem.js +20 -15
- package/src/engine/physics/cloth/ecs/ClothWorld.d.ts +57 -36
- package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothWorld.js +816 -179
- package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts +12 -13
- package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/WorkerClothSystem.js +20 -14
- package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts +63 -11
- package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_build_rows.js +321 -84
- package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts +9 -7
- package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_dynamics_map.js +9 -7
- package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts +3 -3
- package/src/engine/physics/cloth/ecs/cloth_gather_colliders.js +30 -30
- package/src/engine/physics/cloth/ecs/cloth_geodesic.d.ts +37 -0
- package/src/engine/physics/cloth/ecs/cloth_geodesic.d.ts.map +1 -0
- package/src/engine/physics/cloth/ecs/cloth_geodesic.js +142 -0
- package/src/engine/physics/cloth/ecs/cloth_holds.d.ts +97 -0
- package/src/engine/physics/cloth/ecs/cloth_holds.d.ts.map +1 -0
- package/src/engine/physics/cloth/ecs/cloth_holds.js +608 -0
- package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.d.ts +2 -2
- package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.js +160 -9
- package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts +17 -22
- package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_seed_rig.js +55 -117
- package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts +21 -6
- package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_seed_subtree.js +44 -15
- package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts +5 -0
- package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_write_back.js +60 -16
- package/src/engine/physics/cloth/ecs/cloth_write_back_rig.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_write_back_rig.js +65 -58
- package/src/engine/physics/cloth/playground/collide_main.js +1 -1
- package/src/engine/physics/cloth/playground/garment_build.d.ts.map +1 -1
- package/src/engine/physics/cloth/playground/garment_build.js +17 -5
- package/src/engine/physics/cloth/playground/garment_main.js +1 -1
- package/src/engine/physics/cloth/playground/rig_build.d.ts.map +1 -1
- package/src/engine/physics/cloth/playground/rig_build.js +302 -296
- package/src/engine/physics/cloth/solver/ClothState.d.ts +45 -7
- package/src/engine/physics/cloth/solver/ClothState.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/ClothState.js +63 -7
- package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts +6 -1
- package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/cloth_contact_find.js +7 -2
- package/src/engine/physics/cloth/solver/cloth_hold_line.d.ts +114 -0
- package/src/engine/physics/cloth/solver/cloth_hold_line.d.ts.map +1 -0
- package/src/engine/physics/cloth/solver/cloth_hold_line.js +362 -0
- package/src/engine/physics/cloth/solver/cloth_self_find.d.ts +6 -2
- package/src/engine/physics/cloth/solver/cloth_self_find.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/cloth_self_find.js +13 -13
- package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts +24 -12
- package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/cloth_solver_constants.js +24 -12
- package/src/engine/physics/cloth/solver/cloth_step.d.ts +16 -0
- package/src/engine/physics/cloth/solver/cloth_step.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/cloth_step.js +183 -20
- package/src/engine/physics/cloth/solver/cloth_step_instance.d.ts +1 -0
- package/src/engine/physics/cloth/solver/cloth_step_instance.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/cloth_step_instance.js +10 -3
- package/src/engine/physics/cloth/solver/constraint/cloth_leash_row.d.ts +1 -1
- package/src/engine/physics/cloth/solver/constraint/cloth_leash_row.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/constraint/cloth_leash_row.js +353 -346
- package/src/engine/physics/cloth/solver/constraint/cloth_self_row.d.ts +7 -5
- package/src/engine/physics/cloth/solver/constraint/cloth_self_row.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/constraint/cloth_self_row.js +7 -5
- package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts +9 -9
- package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts.map +1 -1
- package/src/engine/physics/cloth/wind/cloth_sample_air.js +26 -26
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/engine/simulation/Ticker.d.ts +13 -0
- package/src/engine/simulation/Ticker.d.ts.map +1 -1
- package/src/engine/simulation/Ticker.js +43 -8
- package/src/shade/RENDERER_CONTRACT.md +8 -2
- package/src/shade/playground/mesh_row_ceiling/main.js +2 -1
- package/src/shade/playground/skinned_blas_refit/main.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/main.js +11 -19
- package/src/shade/playground/skinned_blas_refit/place_character.d.ts +6 -3
- package/src/shade/playground/skinned_blas_refit/place_character.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/place_character.js +90 -90
- package/src/shade/playground/skinned_mesh_soup/main.d.ts.map +1 -1
- package/src/shade/playground/skinned_mesh_soup/main.js +502 -509
- package/src/shade/playground/texture_encode/README.md +129 -109
- package/src/shade/playground/texture_encode/main.js +1430 -1430
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts +45 -14
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
- package/src/shade/renderer/animation/GPUAnimationManager.js +316 -64
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts +5 -0
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts.map +1 -1
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +11 -1
- package/src/shade/renderer/geometry/BoxGeometry.d.ts +1 -11
- package/src/shade/renderer/geometry/BoxGeometry.d.ts.map +1 -1
- package/src/shade/renderer/geometry/BoxGeometry.js +6 -13
- package/src/shade/renderer/geometry/CapsuleGeometry.d.ts +19 -0
- package/src/shade/renderer/geometry/CapsuleGeometry.d.ts.map +1 -0
- package/src/shade/renderer/geometry/CapsuleGeometry.js +24 -0
- package/src/shade/renderer/geometry/ConeGeometry.d.ts +20 -0
- package/src/shade/renderer/geometry/ConeGeometry.d.ts.map +1 -0
- package/src/shade/renderer/geometry/ConeGeometry.js +33 -0
- package/src/shade/renderer/geometry/CylinderGeometry.d.ts +21 -0
- package/src/shade/renderer/geometry/CylinderGeometry.d.ts.map +1 -0
- package/src/shade/renderer/geometry/CylinderGeometry.js +35 -0
- package/src/shade/renderer/geometry/PlaneGeometry.d.ts +17 -0
- package/src/shade/renderer/geometry/PlaneGeometry.d.ts.map +1 -0
- package/src/shade/renderer/geometry/PlaneGeometry.js +22 -0
- package/src/shade/renderer/geometry/TorusGeometry.d.ts +18 -0
- package/src/shade/renderer/geometry/TorusGeometry.d.ts.map +1 -0
- package/src/shade/renderer/geometry/TorusGeometry.js +23 -0
- package/src/shade/renderer/geometry/UVSphereGeometry.d.ts +21 -0
- package/src/shade/renderer/geometry/UVSphereGeometry.d.ts.map +1 -0
- package/src/shade/renderer/geometry/UVSphereGeometry.js +34 -0
- package/src/shade/renderer/geometry/primitives/make_capsule_geometry.d.ts +19 -0
- package/src/shade/renderer/geometry/primitives/make_capsule_geometry.d.ts.map +1 -0
- package/src/shade/renderer/geometry/primitives/make_capsule_geometry.js +205 -0
- package/src/shade/renderer/geometry/primitives/make_cone_geometry.d.ts +21 -0
- package/src/shade/renderer/geometry/primitives/make_cone_geometry.d.ts.map +1 -0
- package/src/shade/renderer/geometry/primitives/make_cone_geometry.js +36 -0
- package/src/shade/renderer/geometry/primitives/make_cylinder_geometry.d.ts +1 -1
- package/src/shade/renderer/geometry/primitives/make_cylinder_geometry.d.ts.map +1 -1
- package/src/shade/renderer/geometry/primitives/make_cylinder_geometry.js +44 -9
- package/src/shade/renderer/geometry/primitives/make_uv_sphere_geometry.d.ts +20 -0
- package/src/shade/renderer/geometry/primitives/make_uv_sphere_geometry.d.ts.map +1 -0
- package/src/shade/renderer/geometry/primitives/make_uv_sphere_geometry.js +190 -0
- package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.js +7 -2
- package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.d.ts +26 -8
- package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.d.ts.map +1 -1
- package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.js +56 -39
- package/src/shade/renderer/gpu_primitive/prefix_sum/v1/shader_prefix_scan_csdldf.d.ts +7 -0
- package/src/shade/renderer/gpu_primitive/prefix_sum/v1/shader_prefix_scan_csdldf.d.ts.map +1 -1
- package/src/shade/renderer/gpu_primitive/prefix_sum/v1/shader_prefix_scan_csdldf.js +46 -4
- package/src/shade/renderer/gpu_primitive/prefix_sum/v1/test_prefix_sum.d.ts.map +1 -1
- package/src/shade/renderer/gpu_primitive/prefix_sum/v1/test_prefix_sum.js +4 -2
- package/src/shade/renderer/gpu_primitive/sort/defs.d.ts +2 -2
- package/src/shade/renderer/gpu_primitive/sort/defs.js +79 -79
- package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.d.ts +1 -1
- package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.d.ts.map +1 -1
- package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.js +7 -3
- package/src/shade/renderer/particles/GPUParticleSystem.js +1 -1
- package/src/shade/renderer/particles/coherence/graph_particle_bucket.d.ts +4 -3
- package/src/shade/renderer/particles/coherence/graph_particle_bucket.d.ts.map +1 -1
- package/src/shade/renderer/particles/coherence/graph_particle_bucket.js +200 -194
- package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles.js +6 -2
- package/src/shade/renderer/particles/warmup/graph_particle_warmup.d.ts.map +1 -1
- package/src/shade/renderer/particles/warmup/graph_particle_warmup.js +334 -329
- package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +7 -2
- package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.js +16 -4
- package/src/shade/renderer/texture/COMPRESSED_TEXTURE_PLAN_2026_09_01.md +2444 -2345
- package/src/shade/renderer/texture/encode/chunk_astc_encode_block_4x4_rgb.d.ts.map +1 -1
- package/src/shade/renderer/texture/encode/chunk_astc_encode_block_4x4_rgb.js +312 -310
- package/src/shade/renderer/texture/encode/chunk_bc1_encode_block.d.ts +3 -3
- package/src/shade/renderer/texture/encode/chunk_bc1_encode_block.d.ts.map +1 -1
- package/src/shade/renderer/texture/encode/chunk_bc1_encode_block.js +383 -375
- package/src/shade/renderer/texture/encode/chunk_bc6h_encode_block_mode11.d.ts.map +1 -1
- package/src/shade/renderer/texture/encode/chunk_bc6h_encode_block_mode11.js +349 -346
- package/src/shade/renderer/texture/encode/chunk_bc7_encode_block.d.ts +58 -0
- package/src/shade/renderer/texture/encode/chunk_bc7_encode_block.d.ts.map +1 -0
- package/src/shade/renderer/texture/encode/chunk_bc7_encode_block.js +536 -0
- package/src/shade/renderer/texture/encode/chunk_bc7_encode_block_mode6.d.ts +21 -7
- package/src/shade/renderer/texture/encode/chunk_bc7_encode_block_mode6.d.ts.map +1 -1
- package/src/shade/renderer/texture/encode/chunk_bc7_encode_block_mode6.js +291 -327
- package/src/shade/renderer/texture/encode/chunk_bc7_pack_mode1.d.ts +25 -0
- package/src/shade/renderer/texture/encode/chunk_bc7_pack_mode1.d.ts.map +1 -0
- package/src/shade/renderer/texture/encode/chunk_bc7_pack_mode1.js +99 -0
- package/src/shade/renderer/texture/encode/chunk_bc7_pack_mode6.d.ts +5 -3
- package/src/shade/renderer/texture/encode/chunk_bc7_pack_mode6.d.ts.map +1 -1
- package/src/shade/renderer/texture/encode/chunk_bc7_pack_mode6.js +47 -71
- package/src/shade/renderer/texture/encode/chunk_bc7_partition_2.d.ts +10 -0
- package/src/shade/renderer/texture/encode/chunk_bc7_partition_2.d.ts.map +1 -0
- package/src/shade/renderer/texture/encode/chunk_bc7_partition_2.js +50 -0
- package/src/shade/renderer/texture/encode/shader_texture_encode_bc7.d.ts +4 -5
- package/src/shade/renderer/texture/encode/shader_texture_encode_bc7.d.ts.map +1 -1
- package/src/shade/renderer/texture/encode/shader_texture_encode_bc7.js +7 -8
- package/src/shade/renderer/water/chunk_water_transport.d.ts.map +1 -1
- package/src/shade/renderer/water/chunk_water_transport.js +7 -5
- package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +1 -1
- package/src/shade/renderer/water/medium/GPUWaterBodies.js +8 -2
- package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.d.ts +3 -2
- package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.d.ts.map +1 -1
- package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.js +3 -2
- package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
- package/src/shade/renderer/water/shader_water.js +3 -1
- package/src/shade/renderer/water/surface/GPUWaterSurface.d.ts.map +1 -1
- package/src/shade/renderer/water/surface/GPUWaterSurface.js +3 -1
- package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts.map +1 -1
- package/src/shade/renderer/water/surface/graph_water_tessellation.js +11 -4
- package/src/shade/renderer/water/surface/particles/GPUWaterParticles.d.ts.map +1 -1
- package/src/shade/renderer/water/surface/particles/GPUWaterParticles.js +37 -20
- package/src/shade/wgsl/validate_wgsl_source.d.ts.map +1 -1
- package/src/shade/wgsl/validate_wgsl_source.js +74 -3
- package/src/engine/graphics3/register_instance_animation.d.ts +0 -114
- package/src/engine/graphics3/register_instance_animation.d.ts.map +0 -1
- package/src/engine/graphics3/register_instance_animation.js +0 -201
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { assert } from "../../../../core/assert.js";
|
|
2
|
-
import { quat3_multiply_array } from "../../../../core/geom/3d/quaternion/quat3_multiply.js";
|
|
2
|
+
import { quat3_multiply, quat3_multiply_array } from "../../../../core/geom/3d/quaternion/quat3_multiply.js";
|
|
3
3
|
import { quat3_nlerp_array } from "../../../../core/geom/3d/quaternion/quat3_nlerp.js";
|
|
4
4
|
import { v3_quaternion_apply_array } from "../../../../core/geom/vec3/v3_quaternion_apply.js";
|
|
5
5
|
import { v3_quaternion_apply_inverse_array } from "../../../../core/geom/vec3/v3_quaternion_apply_inverse.js";
|
|
6
6
|
import { EntityObserver } from "../../../ecs/EntityObserver.js";
|
|
7
|
+
import { EventType } from "../../../ecs/EventType.js";
|
|
8
|
+
import { GPUStateAuthorityFlags } from "../../../ecs/gpu/GPUStateAuthorityFlags.js";
|
|
9
|
+
import { gpu_authority_has } from "../../../ecs/gpu/gpu_authority_has.js";
|
|
10
|
+
import { ParentEntity } from "../../../ecs/parent/ParentEntity.js";
|
|
7
11
|
import {
|
|
8
12
|
TransformAttachment
|
|
9
13
|
} from "../../../ecs/transform-attachment/TransformAttachment.js";
|
|
@@ -12,13 +16,14 @@ import {
|
|
|
12
16
|
} from "../../../ecs/transform-attachment/TransformAttachmentSystem.js";
|
|
13
17
|
import {
|
|
14
18
|
TRANSFORM64_ROTATION_OFFSET,
|
|
15
|
-
TRANSFORM64_TRANSLATION_OFFSET
|
|
19
|
+
TRANSFORM64_TRANSLATION_OFFSET,
|
|
20
|
+
Transform64
|
|
16
21
|
} from "../../../ecs/transform/Transform64.js";
|
|
17
22
|
import { t64_evaluate_world } from "../../../ecs/transform/t64_evaluate_world.js";
|
|
18
23
|
import {
|
|
19
24
|
collect_entity_playbacks
|
|
20
25
|
} from "../../../graphics3/pose/collect_entity_playbacks.js";
|
|
21
|
-
import {
|
|
26
|
+
import { MeshSkin } from "../../../graphics/ecs/mesh-v2/MeshSkin.js";
|
|
22
27
|
import { CLOTH_SELF_RADIUS_MAX_RATIO, CLOTH_SELF_RADIUS_MIN_RATIO } from "../solver/cloth_solver_constants.js";
|
|
23
28
|
import { cloth_air_changed, cloth_sample_air } from "../wind/cloth_sample_air.js";
|
|
24
29
|
import { ClothStepParam } from "../solver/cloth_step_instance.js";
|
|
@@ -26,11 +31,14 @@ import { ClothStepResult } from "../solver/ClothState.js";
|
|
|
26
31
|
import { ClothColliderSystem } from "./ClothColliderSystem.js";
|
|
27
32
|
import { ClothDynamicsFlags } from "./ClothDynamicsFlags.js";
|
|
28
33
|
import { ClothFlags } from "./ClothFlags.js";
|
|
34
|
+
import { ClothHold } from "./ClothHold.js";
|
|
29
35
|
import { ClothInstance } from "./ClothInstance.js";
|
|
36
|
+
import { ClothPin } from "./ClothPin.js";
|
|
37
|
+
import { cloth_apply_holds, cloth_capture_hold, cloth_hold_step } from "./cloth_holds.js";
|
|
30
38
|
import { ClothRig } from "./ClothRig.js";
|
|
31
39
|
import {
|
|
32
|
-
|
|
33
|
-
|
|
40
|
+
CLOTH_FRAME_ROTATION_LIMIT,
|
|
41
|
+
CLOTH_FRAME_TRAVEL_REACH,
|
|
34
42
|
CLOTH_SLEEP_DISPLACEMENT_RATIO,
|
|
35
43
|
CLOTH_SLEEP_SPEED_RATIO,
|
|
36
44
|
CLOTH_SLEEP_STEPS,
|
|
@@ -61,12 +69,9 @@ import { cloth_write_back_rig } from "./cloth_write_back_rig.js";
|
|
|
61
69
|
* # Every cloth in a scene, and everything a step needs that is not arithmetic
|
|
62
70
|
*
|
|
63
71
|
* One {@link ClothInstance} per {@link Cloth} component, stepped at the fixed
|
|
64
|
-
* rate. Per step, per cloth: move the local frame
|
|
65
|
-
* leash rows at
|
|
66
|
-
* it, step, and put the result back on the subtree.
|
|
67
|
-
* leashes are measured against is not recomposed per step — in the entity path
|
|
68
|
-
* it is constant in the anchor's own frame, and `cloth_seed_subtree.js` bakes
|
|
69
|
-
* it.
|
|
72
|
+
* rate. Per step, per cloth: take where its holders are, move the local frame,
|
|
73
|
+
* point the leash rows at what they measure from, gather the colliders that
|
|
74
|
+
* could touch it, step, and put the result back on the subtree.
|
|
70
75
|
*
|
|
71
76
|
* ## Why this is not a `System`
|
|
72
77
|
*
|
|
@@ -101,11 +106,11 @@ import { cloth_write_back_rig } from "./cloth_write_back_rig.js";
|
|
|
101
106
|
*
|
|
102
107
|
* ## The local frame and the inertia model — §7 P7
|
|
103
108
|
*
|
|
104
|
-
* Particles live in the
|
|
109
|
+
* Particles live in the cloth's frame. When the frame moves, the cloth is
|
|
105
110
|
* asked how much of that motion it would like to feel:
|
|
106
111
|
* `ClothDynamics#inertia = 1` keeps every particle's **world** position and
|
|
107
112
|
* velocity, so a cape trails a sprinting character; `0` keeps their **local**
|
|
108
|
-
* ones, so the cloth rides the
|
|
113
|
+
* ones, so the cloth rides the frame as though it were standing still. The
|
|
109
114
|
* blend between the two is a lerp in the local frame, which is all the frame
|
|
110
115
|
* change is.
|
|
111
116
|
*
|
|
@@ -127,14 +132,32 @@ import { cloth_write_back_rig } from "./cloth_write_back_rig.js";
|
|
|
127
132
|
* step's drag response is zero, `cloth_step` takes the uniform-gravity path,
|
|
128
133
|
* and a scene produces the bytes M5 measured.
|
|
129
134
|
*
|
|
135
|
+
* ## Holds
|
|
136
|
+
*
|
|
137
|
+
* A {@link ClothPin} on any entity holds the particles it names. This world
|
|
138
|
+
* keeps one {@link ClothHold} per pin, by the cloth entity it names, for as long
|
|
139
|
+
* as the pin exists — **outside** the seeded instance, because the offsets a
|
|
140
|
+
* hold takes when it begins are the hold's and must survive a re-seed. A pin is
|
|
141
|
+
* only ever *noticed* by the observer; everything it changes in a cloth's state
|
|
142
|
+
* is written in {@link advance}, which is the one window in which the main
|
|
143
|
+
* thread owns that state under both systems.
|
|
144
|
+
*
|
|
145
|
+
* Each step, before anything else, every holder's pose is taken — read off its
|
|
146
|
+
* `Transform64` when the CPU owns it, evaluated exactly when a clip does — and
|
|
147
|
+
* expressed in the cloth's frame. `cloth_holds.js` turns those poses into
|
|
148
|
+
* where the held particles go, the animated pose hung from them, and the leash
|
|
149
|
+
* centres.
|
|
150
|
+
*
|
|
130
151
|
* ## Teleport — §7 P6
|
|
131
152
|
*
|
|
132
|
-
*
|
|
133
|
-
* amount of it
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
153
|
+
* Frame motion past a threshold is not locomotion and is not treated as any
|
|
154
|
+
* amount of it, and neither is a holder jumping that far in the frame — a ship
|
|
155
|
+
* the rope is tied to, moved across the map. `ClothFlags.TeleportKeep` carries
|
|
156
|
+
* the shape across: for the frame that is literally doing nothing, and for a
|
|
157
|
+
* holder it is that holder's own jump applied to what hangs from it. The default
|
|
158
|
+
* re-seeds at the animated pose with zero velocity and opens a stabilisation
|
|
159
|
+
* window in which the velocity limit ramps back from nothing, so the first steps
|
|
160
|
+
* after a reset cannot produce a whip.
|
|
138
161
|
*
|
|
139
162
|
* @author Alex Goldring
|
|
140
163
|
* @copyright Company Named Limited (c) 2026
|
|
@@ -162,24 +185,6 @@ export class ClothWorld {
|
|
|
162
185
|
*/
|
|
163
186
|
instances = [];
|
|
164
187
|
|
|
165
|
-
/**
|
|
166
|
-
* Where a skinned model instance comes from. Anything that answers
|
|
167
|
-
* `instance_of(entity)` with a `PrefabInstance` — skins whose joints
|
|
168
|
-
* are entities — will do, and {@link MeshSystem} is what does in a
|
|
169
|
-
* running engine.
|
|
170
|
-
*
|
|
171
|
-
* Public and assignable because a garment's joints are a *rendering*
|
|
172
|
-
* structure and `MeshSystem` needs a graphics engine, a scene and a loader
|
|
173
|
-
* to exist — so a spec that wants joints without a renderer sets this
|
|
174
|
-
* instead, and gets exactly the interface the system uses.
|
|
175
|
-
*
|
|
176
|
-
* `null` means no rig can seed, which is the same additive shape the
|
|
177
|
-
* collider index has: a scene without one gets M1's cloth rather than an
|
|
178
|
-
* error.
|
|
179
|
-
* @type {{instance_of: function(number): (PrefabInstance|null)}|null}
|
|
180
|
-
*/
|
|
181
|
-
models = null;
|
|
182
|
-
|
|
183
188
|
/**
|
|
184
189
|
* @type {EntityManager|null}
|
|
185
190
|
*/
|
|
@@ -292,6 +297,44 @@ export class ClothWorld {
|
|
|
292
297
|
this
|
|
293
298
|
);
|
|
294
299
|
|
|
300
|
+
/**
|
|
301
|
+
* Every {@link ClothPin} in the dataset, as the hold it makes. A pin is
|
|
302
|
+
* noticed here and nowhere else; what it does to a cloth is written in
|
|
303
|
+
* {@link advance}.
|
|
304
|
+
* @type {EntityObserver}
|
|
305
|
+
* @private
|
|
306
|
+
*/
|
|
307
|
+
__pin_observer = new EntityObserver(
|
|
308
|
+
[ClothPin],
|
|
309
|
+
(pin, entity) => this.#pin_added(pin, entity),
|
|
310
|
+
(pin, entity) => this.#pin_removed(pin, entity),
|
|
311
|
+
this
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Holds by the cloth entity they name, each list ascending by holder — so a
|
|
316
|
+
* particle two pins name goes to the lower holder, every time.
|
|
317
|
+
* @type {Map<number, ClothHold[]>}
|
|
318
|
+
* @private
|
|
319
|
+
*/
|
|
320
|
+
#holds = new Map();
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Bumped for a cloth entity whenever a hold on it appears or goes;
|
|
324
|
+
* {@link ClothInstance#holds_version} is what an instance last applied.
|
|
325
|
+
* @type {Map<number, number>}
|
|
326
|
+
* @private
|
|
327
|
+
*/
|
|
328
|
+
#hold_versions = new Map();
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* The dataset the observers are attached to, for the one thing a pin needs
|
|
332
|
+
* done outside a step: going when its cloth's entity does.
|
|
333
|
+
* @type {EntityComponentDataset|null}
|
|
334
|
+
* @private
|
|
335
|
+
*/
|
|
336
|
+
#dataset = null;
|
|
337
|
+
|
|
295
338
|
/**
|
|
296
339
|
* Resolve the systems a cloth world reads. Called from the owning system's
|
|
297
340
|
* `startup`.
|
|
@@ -311,10 +354,6 @@ export class ClothWorld {
|
|
|
311
354
|
// Optional by design. A scene with no ClothColliderSystem gets cloth
|
|
312
355
|
// that does not collide, rather than cloth that throws.
|
|
313
356
|
this.colliders = entityManager.getSystem(ClothColliderSystem) ?? null;
|
|
314
|
-
|
|
315
|
-
if (this.models === null) {
|
|
316
|
-
this.models = entityManager.getSystem(MeshSystem) ?? null;
|
|
317
|
-
}
|
|
318
357
|
}
|
|
319
358
|
|
|
320
359
|
/**
|
|
@@ -347,7 +386,18 @@ export class ClothWorld {
|
|
|
347
386
|
* @returns {void}
|
|
348
387
|
*/
|
|
349
388
|
attach_dataset(dataset) {
|
|
389
|
+
this.#dataset = dataset;
|
|
390
|
+
|
|
350
391
|
dataset.addObserver(this.__rig_observer, true);
|
|
392
|
+
|
|
393
|
+
// A cloth simulates fine with nothing holding it, so a scene need not
|
|
394
|
+
// have registered the component to have cloth in it.
|
|
395
|
+
dataset.registerComponentType(ClothPin);
|
|
396
|
+
|
|
397
|
+
// nor a skin, to have cloth that is not a rig
|
|
398
|
+
dataset.registerComponentType(MeshSkin);
|
|
399
|
+
|
|
400
|
+
dataset.addObserver(this.__pin_observer, true);
|
|
351
401
|
}
|
|
352
402
|
|
|
353
403
|
/**
|
|
@@ -356,6 +406,406 @@ export class ClothWorld {
|
|
|
356
406
|
*/
|
|
357
407
|
detach_dataset(dataset) {
|
|
358
408
|
dataset.removeObserver(this.__rig_observer, false);
|
|
409
|
+
dataset.removeObserver(this.__pin_observer, false);
|
|
410
|
+
|
|
411
|
+
this.#dataset = null;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* @param {ClothPin} pin
|
|
416
|
+
* @param {number} holder
|
|
417
|
+
* @private
|
|
418
|
+
*/
|
|
419
|
+
#pin_added(pin, holder) {
|
|
420
|
+
const cloth = pin.cloth;
|
|
421
|
+
|
|
422
|
+
let list = this.#holds.get(cloth);
|
|
423
|
+
|
|
424
|
+
if (list === undefined) {
|
|
425
|
+
list = [];
|
|
426
|
+
|
|
427
|
+
this.#holds.set(cloth, list);
|
|
428
|
+
|
|
429
|
+
// A pin names its cloth's entity, and an entity id is reused: when
|
|
430
|
+
// that entity goes, the pins naming it go with it, rather than
|
|
431
|
+
// waiting to hold whatever is spawned under the same id next. The
|
|
432
|
+
// same contract `IKGoal` has with its target.
|
|
433
|
+
const dataset = this.#dataset;
|
|
434
|
+
|
|
435
|
+
if (dataset !== null && dataset.entityExists(cloth)) {
|
|
436
|
+
dataset.addEntityEventListener(cloth, EventType.EntityRemoved, this.#cloth_entity_removed, this);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const hold = new ClothHold();
|
|
441
|
+
|
|
442
|
+
hold.holder = holder;
|
|
443
|
+
hold.pin = pin;
|
|
444
|
+
|
|
445
|
+
let at = list.length;
|
|
446
|
+
|
|
447
|
+
while (at > 0 && list[at - 1].holder > holder) {
|
|
448
|
+
at--;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
list.splice(at, 0, hold);
|
|
452
|
+
|
|
453
|
+
this.#bump(cloth);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* @param {ClothPin} pin
|
|
458
|
+
* @param {number} holder
|
|
459
|
+
* @private
|
|
460
|
+
*/
|
|
461
|
+
#pin_removed(pin, holder) {
|
|
462
|
+
const cloth = pin.cloth;
|
|
463
|
+
|
|
464
|
+
const list = this.#holds.get(cloth);
|
|
465
|
+
|
|
466
|
+
if (list === undefined) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
for (let i = 0; i < list.length; i++) {
|
|
471
|
+
if (list[i].holder === holder) {
|
|
472
|
+
list.splice(i, 1);
|
|
473
|
+
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (list.length === 0) {
|
|
479
|
+
this.#holds.delete(cloth);
|
|
480
|
+
|
|
481
|
+
const dataset = this.#dataset;
|
|
482
|
+
|
|
483
|
+
if (dataset !== null && dataset.entityExists(cloth)) {
|
|
484
|
+
dataset.removeEntityEventListener(cloth, EventType.EntityRemoved, this.#cloth_entity_removed, this);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
this.#bump(cloth);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* A cloth's entity has gone: take every pin naming it off its holder.
|
|
493
|
+
*
|
|
494
|
+
* @param {*} event
|
|
495
|
+
* @param {number} cloth
|
|
496
|
+
* @private
|
|
497
|
+
*/
|
|
498
|
+
#cloth_entity_removed(event, cloth) {
|
|
499
|
+
const list = this.#holds.get(cloth);
|
|
500
|
+
|
|
501
|
+
const dataset = this.#dataset;
|
|
502
|
+
|
|
503
|
+
if (list === undefined || dataset === null) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Removing a pin edits the list, so walk a copy.
|
|
508
|
+
const holders = list.map(hold => hold.holder);
|
|
509
|
+
|
|
510
|
+
for (let i = 0; i < holders.length; i++) {
|
|
511
|
+
if (dataset.entityExists(holders[i])) {
|
|
512
|
+
dataset.removeComponentFromEntity(holders[i], ClothPin);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* @param {number} cloth
|
|
519
|
+
* @private
|
|
520
|
+
*/
|
|
521
|
+
#bump(cloth) {
|
|
522
|
+
this.#hold_versions.set(cloth, (this.#hold_versions.get(cloth) ?? 0) + 1);
|
|
523
|
+
|
|
524
|
+
const instance = this.__by_entity.get(cloth);
|
|
525
|
+
|
|
526
|
+
if (instance !== undefined) {
|
|
527
|
+
instance.asleep = false;
|
|
528
|
+
instance.still_steps = 0;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* The particles a cloth has and the entities they are, enough to say
|
|
534
|
+
* whether a holder hangs from one of them.
|
|
535
|
+
*
|
|
536
|
+
* A holder that cloth itself moves — a link of the same rope, or anything
|
|
537
|
+
* hanging below one — would chase its own write-back one step behind, so it
|
|
538
|
+
* is refused once, by name, and holds nothing. The cloth's own entity is
|
|
539
|
+
* allowed: for a subtree it is the frame, which cloth reads and never writes.
|
|
540
|
+
*
|
|
541
|
+
* @param {ClothInstance} instance
|
|
542
|
+
* @param {ClothHold} hold
|
|
543
|
+
* @returns {string} empty when the hold may hold, otherwise why not
|
|
544
|
+
* @private
|
|
545
|
+
*/
|
|
546
|
+
#refusal(instance, hold) {
|
|
547
|
+
const ecd = this.entityManager.dataset;
|
|
548
|
+
|
|
549
|
+
for (let entity = hold.holder, depth = 0; entity >= 0 && depth < 256; depth++) {
|
|
550
|
+
if (this.#is_output(instance, entity)) {
|
|
551
|
+
return `entity ${hold.holder} holds cloth ${instance.entity} and hangs from entity ${entity}, which that cloth moves`;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const attachment = ecd.getComponent(entity, TransformAttachment);
|
|
555
|
+
|
|
556
|
+
entity = attachment === undefined ? -1 : attachment.parent;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const particles = hold.pin.particles;
|
|
560
|
+
|
|
561
|
+
for (let k = 0; k < particles.length; k++) {
|
|
562
|
+
if (particles[k] >= instance.node_count) {
|
|
563
|
+
return `entity ${hold.holder} holds particle ${particles[k]} of cloth ${instance.entity}, which has ${instance.node_count}`;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return "";
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* @param {ClothInstance} instance
|
|
572
|
+
* @param {number} entity
|
|
573
|
+
* @returns {boolean} whether cloth writes this entity's pose
|
|
574
|
+
* @private
|
|
575
|
+
*/
|
|
576
|
+
#is_output(instance, entity) {
|
|
577
|
+
const output = instance.node_output;
|
|
578
|
+
const entities = instance.rig === null ? instance.node_entity : instance.node_joint;
|
|
579
|
+
|
|
580
|
+
for (let node = 0; node < instance.node_count; node++) {
|
|
581
|
+
if (output[node] !== 0 && entities[node] === entity) {
|
|
582
|
+
return true;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Bring an instance's holds up to date with the pins that name it: pick up a
|
|
591
|
+
* changed set, take the offsets of any hold whose holder has just been found,
|
|
592
|
+
* and re-derive whatever holding decides. Nothing here runs unless something
|
|
593
|
+
* changed.
|
|
594
|
+
*
|
|
595
|
+
* @param {ClothInstance} instance
|
|
596
|
+
* @param {Float64Array} frame_translation the frame the particle positions are in
|
|
597
|
+
* @param {Float64Array} frame_rotation
|
|
598
|
+
* @returns {boolean} whether anything about what holds the cloth changed
|
|
599
|
+
* @private
|
|
600
|
+
*/
|
|
601
|
+
#refresh_holds(instance, frame_translation, frame_rotation) {
|
|
602
|
+
let changed = false;
|
|
603
|
+
|
|
604
|
+
const holds = instance.holds;
|
|
605
|
+
|
|
606
|
+
for (let h = 0; h < holds.length; h++) {
|
|
607
|
+
const hold = holds[h];
|
|
608
|
+
|
|
609
|
+
if (!hold.captured && hold.valid && !hold.refused) {
|
|
610
|
+
cloth_capture_hold(instance, hold, frame_translation, frame_rotation);
|
|
611
|
+
|
|
612
|
+
changed = true;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (changed || instance.holds_applied !== instance.holds_version) {
|
|
617
|
+
cloth_apply_holds(instance);
|
|
618
|
+
|
|
619
|
+
instance.holds_applied = instance.holds_version;
|
|
620
|
+
|
|
621
|
+
// A different set of holds is a different cloth to rest; the window
|
|
622
|
+
// that was open measured the old one.
|
|
623
|
+
instance.still_steps = 0;
|
|
624
|
+
instance.sleep_reference = null;
|
|
625
|
+
|
|
626
|
+
changed = true;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
return changed;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Pick up a changed set of pins: the instance's holds become the registry's,
|
|
634
|
+
* each one admitted or refused.
|
|
635
|
+
*
|
|
636
|
+
* @param {ClothInstance} instance
|
|
637
|
+
* @private
|
|
638
|
+
*/
|
|
639
|
+
#sync_holds(instance) {
|
|
640
|
+
const version = this.#hold_versions.get(instance.entity) ?? 0;
|
|
641
|
+
|
|
642
|
+
if (instance.holds_version === version) {
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
const list = this.#holds.get(instance.entity);
|
|
647
|
+
|
|
648
|
+
instance.holds = list === undefined ? [] : list.slice();
|
|
649
|
+
instance.holds_version = version;
|
|
650
|
+
|
|
651
|
+
for (let h = 0; h < instance.holds.length; h++) {
|
|
652
|
+
const hold = instance.holds[h];
|
|
653
|
+
|
|
654
|
+
const refusal = this.#refusal(instance, hold);
|
|
655
|
+
|
|
656
|
+
hold.refused = refusal !== "";
|
|
657
|
+
|
|
658
|
+
if (hold.refused && !hold.reported) {
|
|
659
|
+
hold.reported = true;
|
|
660
|
+
|
|
661
|
+
console.warn(`ClothSystem: ${refusal}`);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Where every holder is this step: in the world, and in the cloth's frame.
|
|
668
|
+
*
|
|
669
|
+
* @param {ClothInstance} instance
|
|
670
|
+
* @param {Float64Array} frame_translation this step's frame
|
|
671
|
+
* @param {Float64Array} frame_rotation
|
|
672
|
+
* @returns {boolean} whether any holder moved, appeared or vanished since
|
|
673
|
+
* the last step
|
|
674
|
+
* @private
|
|
675
|
+
*/
|
|
676
|
+
#evaluate_holds(instance, frame_translation, frame_rotation) {
|
|
677
|
+
let moved = false;
|
|
678
|
+
|
|
679
|
+
const holds = instance.holds;
|
|
680
|
+
|
|
681
|
+
for (let h = 0; h < holds.length; h++) {
|
|
682
|
+
const hold = holds[h];
|
|
683
|
+
|
|
684
|
+
const was_valid = hold.valid;
|
|
685
|
+
|
|
686
|
+
hold.valid = !hold.refused && this.#holder_world(hold.holder, hold.world);
|
|
687
|
+
|
|
688
|
+
if (!hold.valid) {
|
|
689
|
+
moved = moved || was_valid;
|
|
690
|
+
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const world = hold.world;
|
|
695
|
+
const pose = hold.pose;
|
|
696
|
+
|
|
697
|
+
// in the frame: q_f⁻¹ · (t − t_f), q_f⁻¹ ⊗ q
|
|
698
|
+
HOLD_SCRATCH[0] = world[0] - frame_translation[0];
|
|
699
|
+
HOLD_SCRATCH[1] = world[1] - frame_translation[1];
|
|
700
|
+
HOLD_SCRATCH[2] = world[2] - frame_translation[2];
|
|
701
|
+
|
|
702
|
+
v3_quaternion_apply_inverse_array(pose, 0, HOLD_SCRATCH, 0, frame_rotation, 0);
|
|
703
|
+
|
|
704
|
+
FRAME_CONJUGATE[0] = -frame_rotation[0];
|
|
705
|
+
FRAME_CONJUGATE[1] = -frame_rotation[1];
|
|
706
|
+
FRAME_CONJUGATE[2] = -frame_rotation[2];
|
|
707
|
+
FRAME_CONJUGATE[3] = frame_rotation[3];
|
|
708
|
+
|
|
709
|
+
quat3_multiply_array(pose, 3, FRAME_CONJUGATE, 0, world, 3);
|
|
710
|
+
|
|
711
|
+
if (!hold.has_previous) {
|
|
712
|
+
moved = true;
|
|
713
|
+
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
const before = hold.world_previous;
|
|
718
|
+
|
|
719
|
+
for (let i = 0; i < 7; i++) {
|
|
720
|
+
if (world[i] !== before[i]) {
|
|
721
|
+
moved = true;
|
|
722
|
+
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
return moved;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* A holder's world pose, `[tx, ty, tz, qx, qy, qz, qw]`.
|
|
733
|
+
*
|
|
734
|
+
* Read off its `Transform64` when the CPU owns that, which is every prop and
|
|
735
|
+
* every hand a script moves. A transform the GPU composes — a bone a clip
|
|
736
|
+
* drives — holds the rest pose under wherever the CPU last composed it, so
|
|
737
|
+
* it is evaluated exactly instead, under the clips of every model on its
|
|
738
|
+
* chain: a pole in a hand belongs to one model and hangs from another's
|
|
739
|
+
* bones, and evaluating it under only its own would put it where the hand
|
|
740
|
+
* was in the bind pose.
|
|
741
|
+
*
|
|
742
|
+
* @param {number} entity
|
|
743
|
+
* @param {Float64Array} out
|
|
744
|
+
* @returns {boolean} false when the entity has no pose to give yet — gone,
|
|
745
|
+
* or hanging from a parent that has no transform
|
|
746
|
+
* @private
|
|
747
|
+
*/
|
|
748
|
+
#holder_world(entity, out) {
|
|
749
|
+
const ecd = this.entityManager.dataset;
|
|
750
|
+
|
|
751
|
+
if (!ecd.entityExists(entity)) {
|
|
752
|
+
return false;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
const transform = ecd.getComponent(entity, Transform64);
|
|
756
|
+
|
|
757
|
+
if (transform === undefined) {
|
|
758
|
+
return false;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
let source = transform;
|
|
762
|
+
|
|
763
|
+
if (gpu_authority_has(ecd, entity, GPUStateAuthorityFlags.Transform64)) {
|
|
764
|
+
const playbacks = PLAYBACKS;
|
|
765
|
+
|
|
766
|
+
playbacks.length = 0;
|
|
767
|
+
|
|
768
|
+
// every model on the chain, each once; and a chain that is not
|
|
769
|
+
// complete yet is not one to evaluate
|
|
770
|
+
MODELS.length = 0;
|
|
771
|
+
|
|
772
|
+
for (let e = entity, depth = 0; e >= 0 && depth < 256; depth++) {
|
|
773
|
+
if (!ecd.entityExists(e) || ecd.getComponent(e, Transform64) === undefined) {
|
|
774
|
+
playbacks.length = 0;
|
|
775
|
+
|
|
776
|
+
return false;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
const owner = ecd.getComponent(e, ParentEntity);
|
|
780
|
+
const model = owner === undefined || owner === null ? e : owner.entity;
|
|
781
|
+
|
|
782
|
+
if (MODELS.indexOf(model) === -1) {
|
|
783
|
+
MODELS.push(model);
|
|
784
|
+
|
|
785
|
+
collect_entity_playbacks(playbacks, this.entityManager, model);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
const attachment = ecd.getComponent(e, TransformAttachment);
|
|
789
|
+
|
|
790
|
+
e = attachment === undefined ? -1 : attachment.parent;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
t64_evaluate_world(EVALUATED, ecd, entity, playbacks);
|
|
794
|
+
|
|
795
|
+
playbacks.length = 0;
|
|
796
|
+
|
|
797
|
+
source = EVALUATED;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
out[0] = source[TRANSFORM64_TRANSLATION_OFFSET];
|
|
801
|
+
out[1] = source[TRANSFORM64_TRANSLATION_OFFSET + 1];
|
|
802
|
+
out[2] = source[TRANSFORM64_TRANSLATION_OFFSET + 2];
|
|
803
|
+
out[3] = source[TRANSFORM64_ROTATION_OFFSET];
|
|
804
|
+
out[4] = source[TRANSFORM64_ROTATION_OFFSET + 1];
|
|
805
|
+
out[5] = source[TRANSFORM64_ROTATION_OFFSET + 2];
|
|
806
|
+
out[6] = source[TRANSFORM64_ROTATION_OFFSET + 3];
|
|
807
|
+
|
|
808
|
+
return true;
|
|
359
809
|
}
|
|
360
810
|
|
|
361
811
|
/**
|
|
@@ -489,13 +939,14 @@ export class ClothWorld {
|
|
|
489
939
|
}
|
|
490
940
|
|
|
491
941
|
instance.seeded = false;
|
|
492
|
-
instance.
|
|
942
|
+
instance.frame_valid = false;
|
|
493
943
|
|
|
494
944
|
return instance;
|
|
495
945
|
}
|
|
496
946
|
|
|
497
947
|
/**
|
|
498
|
-
* Wake a sleeping cloth.
|
|
948
|
+
* Wake a sleeping cloth. Frame motion, a holder moving or a hold changing,
|
|
949
|
+
* and a `ClothDynamics` change do this
|
|
499
950
|
* on their own; a caller that has changed the world in some way the cloth
|
|
500
951
|
* cannot see — M6's wind, for one — says so here.
|
|
501
952
|
*
|
|
@@ -519,6 +970,37 @@ export class ClothWorld {
|
|
|
519
970
|
return this.__by_entity.get(entity);
|
|
520
971
|
}
|
|
521
972
|
|
|
973
|
+
/**
|
|
974
|
+
* Which particle of a cloth an entity is: a link of a subtree cloth, or a
|
|
975
|
+
* joint of a rigged one — the number a {@link ClothPin} names it by.
|
|
976
|
+
*
|
|
977
|
+
* For code that has the entity rather than the number, which is what a hand
|
|
978
|
+
* closing on a rope has. It answers once the cloth has seeded, which is its
|
|
979
|
+
* first step.
|
|
980
|
+
*
|
|
981
|
+
* @param {number} cloth the entity carrying the `Cloth`
|
|
982
|
+
* @param {number} entity
|
|
983
|
+
* @returns {number} the particle, or `−1` when the entity is not one of the
|
|
984
|
+
* cloth's, or the cloth has not seeded yet
|
|
985
|
+
*/
|
|
986
|
+
particle_of(cloth, entity) {
|
|
987
|
+
const instance = this.__by_entity.get(cloth);
|
|
988
|
+
|
|
989
|
+
if (instance === undefined || !instance.seeded) {
|
|
990
|
+
return -1;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
const entities = instance.rig === null ? instance.node_entity : instance.node_joint;
|
|
994
|
+
|
|
995
|
+
for (let node = 0; node < instance.node_count; node++) {
|
|
996
|
+
if (entities[node] === entity) {
|
|
997
|
+
return node;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
return -1;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
522
1004
|
/**
|
|
523
1005
|
* Everything that happens once per tick rather than once per cloth.
|
|
524
1006
|
*
|
|
@@ -640,9 +1122,9 @@ export class ClothWorld {
|
|
|
640
1122
|
* simulates the joints of the skinned model the entity is showing. There is
|
|
641
1123
|
* no mode field anywhere, and the solver does not know which ran.
|
|
642
1124
|
*
|
|
643
|
-
* A rig can fail to seed for reasons that are not errors —
|
|
644
|
-
*
|
|
645
|
-
* that does is left unseeded for the next step to try again. A rig that
|
|
1125
|
+
* A rig can fail to seed for reasons that are not errors — it names no skin
|
|
1126
|
+
* yet, or the entity it names carries none yet, a model still loading — and
|
|
1127
|
+
* one that does is left unseeded for the next step to try again. A rig that
|
|
646
1128
|
* fails for a reason that *is* an error says so once and then stops, because
|
|
647
1129
|
* a proxy that does not fit its skin will not start fitting it.
|
|
648
1130
|
*
|
|
@@ -659,6 +1141,10 @@ export class ClothWorld {
|
|
|
659
1141
|
|
|
660
1142
|
cloth_seed_subtree(instance, ecd, this.attachments, dt);
|
|
661
1143
|
|
|
1144
|
+
// A new state holds nothing yet: the holds — offsets and all — are
|
|
1145
|
+
// applied to it on its first step.
|
|
1146
|
+
instance.holds_applied = -1;
|
|
1147
|
+
|
|
662
1148
|
return;
|
|
663
1149
|
}
|
|
664
1150
|
|
|
@@ -673,21 +1159,16 @@ export class ClothWorld {
|
|
|
673
1159
|
return;
|
|
674
1160
|
}
|
|
675
1161
|
|
|
676
|
-
|
|
677
|
-
|
|
1162
|
+
const skin = rig.skin === -1 || !ecd.entityExists(rig.skin)
|
|
1163
|
+
? undefined
|
|
1164
|
+
: ecd.getComponent(rig.skin, MeshSkin);
|
|
678
1165
|
|
|
1166
|
+
if (skin === undefined) {
|
|
1167
|
+
// Not named yet, or not there yet: still loading. Not an error, and
|
|
1168
|
+
// not worth a message.
|
|
679
1169
|
return;
|
|
680
1170
|
}
|
|
681
1171
|
|
|
682
|
-
const model = this.models.instance_of(instance.entity);
|
|
683
|
-
|
|
684
|
-
if (model === null || model === undefined) {
|
|
685
|
-
// Still loading. Not an error, and not worth a message.
|
|
686
|
-
return;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
const skin = model.skins[rig.skin];
|
|
690
|
-
|
|
691
1172
|
const error = rig.proxy.checkAgainst(skin);
|
|
692
1173
|
|
|
693
1174
|
if (error !== "") {
|
|
@@ -699,6 +1180,8 @@ export class ClothWorld {
|
|
|
699
1180
|
instance.rig = rig;
|
|
700
1181
|
|
|
701
1182
|
cloth_seed_rig(instance, rig.proxy, skin.joints, ecd, dt);
|
|
1183
|
+
|
|
1184
|
+
instance.holds_applied = -1;
|
|
702
1185
|
}
|
|
703
1186
|
|
|
704
1187
|
/**
|
|
@@ -720,8 +1203,9 @@ export class ClothWorld {
|
|
|
720
1203
|
|
|
721
1204
|
/**
|
|
722
1205
|
* Carry one seeded cloth to the edge of its step and write the parameters
|
|
723
|
-
* the step will run on: whatever the
|
|
724
|
-
* the leash rows measure from, and the colliders
|
|
1206
|
+
* the step will run on: whatever the frame and the holders did, where the
|
|
1207
|
+
* held particles go, where the leash rows measure from, and the colliders
|
|
1208
|
+
* within reach.
|
|
725
1209
|
*
|
|
726
1210
|
* @param {ClothInstance} instance
|
|
727
1211
|
* @param {number} dt seconds
|
|
@@ -760,7 +1244,7 @@ export class ClothWorld {
|
|
|
760
1244
|
|
|
761
1245
|
const transform = instance.rig === null
|
|
762
1246
|
? instance.transform
|
|
763
|
-
: this.#
|
|
1247
|
+
: this.#frame_pose(instance);
|
|
764
1248
|
|
|
765
1249
|
const tx = transform[TRANSFORM64_TRANSLATION_OFFSET];
|
|
766
1250
|
const ty = transform[TRANSFORM64_TRANSLATION_OFFSET + 1];
|
|
@@ -771,15 +1255,38 @@ export class ClothWorld {
|
|
|
771
1255
|
const qz = transform[TRANSFORM64_ROTATION_OFFSET + 2];
|
|
772
1256
|
const qw = transform[TRANSFORM64_ROTATION_OFFSET + 3];
|
|
773
1257
|
|
|
774
|
-
const previous_translation = instance.
|
|
775
|
-
const previous_rotation = instance.
|
|
1258
|
+
const previous_translation = instance.frame_translation;
|
|
1259
|
+
const previous_rotation = instance.frame_rotation;
|
|
1260
|
+
|
|
1261
|
+
FRAME_TRANSLATION[0] = tx;
|
|
1262
|
+
FRAME_TRANSLATION[1] = ty;
|
|
1263
|
+
FRAME_TRANSLATION[2] = tz;
|
|
1264
|
+
|
|
1265
|
+
FRAME_ROTATION[0] = qx;
|
|
1266
|
+
FRAME_ROTATION[1] = qy;
|
|
1267
|
+
FRAME_ROTATION[2] = qz;
|
|
1268
|
+
FRAME_ROTATION[3] = qw;
|
|
1269
|
+
|
|
1270
|
+
// What holds the cloth, before anything that might decide it is asleep:
|
|
1271
|
+
// a hold appearing or going is a reason to wake, and so is a holder
|
|
1272
|
+
// moving.
|
|
1273
|
+
this.#sync_holds(instance);
|
|
1274
|
+
|
|
1275
|
+
const holders_moved = this.#evaluate_holds(instance, FRAME_TRANSLATION, FRAME_ROTATION);
|
|
1276
|
+
|
|
1277
|
+
// Positions are still in the frame they were last stepped in, which on
|
|
1278
|
+
// a cloth's first step is this one; a hold begins where its particles
|
|
1279
|
+
// are, so that is the frame its offsets are taken in.
|
|
1280
|
+
const holds_changed = instance.frame_valid
|
|
1281
|
+
? this.#refresh_holds(instance, previous_translation, previous_rotation)
|
|
1282
|
+
: this.#refresh_holds(instance, FRAME_TRANSLATION, FRAME_ROTATION);
|
|
776
1283
|
|
|
777
1284
|
if (instance.cloth.blend !== instance.blend_seen) {
|
|
778
1285
|
instance.asleep = false;
|
|
779
1286
|
instance.still_steps = 0;
|
|
780
1287
|
}
|
|
781
1288
|
|
|
782
|
-
const moved = instance.
|
|
1289
|
+
const moved = instance.frame_valid && (
|
|
783
1290
|
previous_translation[0] !== tx
|
|
784
1291
|
|| previous_translation[1] !== ty
|
|
785
1292
|
|| previous_translation[2] !== tz
|
|
@@ -789,7 +1296,7 @@ export class ClothWorld {
|
|
|
789
1296
|
|| previous_rotation[3] !== qw
|
|
790
1297
|
);
|
|
791
1298
|
|
|
792
|
-
if (moved) {
|
|
1299
|
+
if (moved || holders_moved || holds_changed) {
|
|
793
1300
|
instance.asleep = false;
|
|
794
1301
|
instance.still_steps = 0;
|
|
795
1302
|
}
|
|
@@ -814,7 +1321,7 @@ export class ClothWorld {
|
|
|
814
1321
|
// cheaper than the step it decides whether to skip.
|
|
815
1322
|
if (cloth_air_changed(
|
|
816
1323
|
instance.state, this.wind,
|
|
817
|
-
instance.
|
|
1324
|
+
instance.frame_translation, instance.frame_rotation,
|
|
818
1325
|
instance.air_seen, CLOTH_SLEEP_SPEED_RATIO * instance.reach,
|
|
819
1326
|
instance.age
|
|
820
1327
|
)) {
|
|
@@ -831,33 +1338,68 @@ export class ClothWorld {
|
|
|
831
1338
|
return false;
|
|
832
1339
|
}
|
|
833
1340
|
|
|
834
|
-
|
|
835
|
-
|
|
1341
|
+
// §7 P6. Locomotion moves a frame a fraction of the cloth's own reach a
|
|
1342
|
+
// step; a jump past this is something else, whether it is the frame
|
|
1343
|
+
// that jumped or something holding the cloth, measured in the frame.
|
|
1344
|
+
const threshold = Math.max(CLOTH_TELEPORT_REACH * instance.reach, CLOTH_TELEPORT_FLOOR);
|
|
836
1345
|
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
);
|
|
1346
|
+
const frame_jumped = moved && Math.hypot(
|
|
1347
|
+
tx - previous_translation[0], ty - previous_translation[1], tz - previous_translation[2]
|
|
1348
|
+
) > threshold;
|
|
841
1349
|
|
|
842
|
-
|
|
843
|
-
|
|
1350
|
+
let holder_jumped = false;
|
|
1351
|
+
|
|
1352
|
+
const holds = instance.holds;
|
|
1353
|
+
|
|
1354
|
+
for (let h = 0; h < holds.length; h++) {
|
|
1355
|
+
const hold = holds[h];
|
|
1356
|
+
|
|
1357
|
+
if (!hold.valid || !hold.has_previous || !hold.captured) {
|
|
1358
|
+
continue;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
const pose = hold.pose;
|
|
1362
|
+
const before = hold.pose_previous;
|
|
1363
|
+
|
|
1364
|
+
if (Math.hypot(pose[0] - before[0], pose[1] - before[1], pose[2] - before[2]) > threshold) {
|
|
1365
|
+
holder_jumped = true;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
const keep = (instance.cloth.flags & ClothFlags.TeleportKeep) !== 0;
|
|
1370
|
+
|
|
1371
|
+
let reset = false;
|
|
1372
|
+
|
|
1373
|
+
if (frame_jumped || holder_jumped) {
|
|
1374
|
+
instance.teleport_count++;
|
|
1375
|
+
|
|
1376
|
+
if (keep) {
|
|
1377
|
+
instance.holds_dirty = true;
|
|
1378
|
+
|
|
1379
|
+
// The frame's jump needs nothing: the particles are in its frame.
|
|
1380
|
+
// A holder's is carried onto what hangs from it.
|
|
1381
|
+
for (let h = 0; h < holds.length; h++) {
|
|
1382
|
+
const hold = holds[h];
|
|
1383
|
+
|
|
1384
|
+
if (hold.valid && hold.has_previous && hold.captured) {
|
|
1385
|
+
carry_with_holder(instance, h);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
844
1388
|
} else {
|
|
845
|
-
|
|
846
|
-
// wherever the call was not inlined
|
|
847
|
-
ANCHOR_POSE[0] = tx;
|
|
848
|
-
ANCHOR_POSE[1] = ty;
|
|
849
|
-
ANCHOR_POSE[2] = tz;
|
|
850
|
-
ANCHOR_POSE[3] = qx;
|
|
851
|
-
ANCHOR_POSE[4] = qy;
|
|
852
|
-
ANCHOR_POSE[5] = qz;
|
|
853
|
-
ANCHOR_POSE[6] = qw;
|
|
854
|
-
|
|
855
|
-
move_local_frame(instance, ANCHOR_POSE, previous_translation, previous_rotation);
|
|
1389
|
+
reset = true;
|
|
856
1390
|
}
|
|
857
|
-
} else if (
|
|
858
|
-
//
|
|
859
|
-
//
|
|
860
|
-
|
|
1391
|
+
} else if (moved) {
|
|
1392
|
+
// the new pose goes in as an array: handed over as seven numbers, each was boxed
|
|
1393
|
+
// wherever the call was not inlined
|
|
1394
|
+
FRAME_POSE[0] = tx;
|
|
1395
|
+
FRAME_POSE[1] = ty;
|
|
1396
|
+
FRAME_POSE[2] = tz;
|
|
1397
|
+
FRAME_POSE[3] = qx;
|
|
1398
|
+
FRAME_POSE[4] = qy;
|
|
1399
|
+
FRAME_POSE[5] = qz;
|
|
1400
|
+
FRAME_POSE[6] = qw;
|
|
1401
|
+
|
|
1402
|
+
move_local_frame(instance, FRAME_POSE, previous_translation, previous_rotation);
|
|
861
1403
|
}
|
|
862
1404
|
|
|
863
1405
|
previous_translation[0] = tx;
|
|
@@ -869,13 +1411,30 @@ export class ClothWorld {
|
|
|
869
1411
|
previous_rotation[2] = qz;
|
|
870
1412
|
previous_rotation[3] = qw;
|
|
871
1413
|
|
|
872
|
-
instance.
|
|
1414
|
+
instance.frame_valid = true;
|
|
1415
|
+
|
|
1416
|
+
// Where every held particle goes, the pose hung from them, the leash
|
|
1417
|
+
// centres — from this step's holder poses.
|
|
1418
|
+
cloth_hold_step(instance);
|
|
1419
|
+
|
|
1420
|
+
if (reset) {
|
|
1421
|
+
this.#reset(instance);
|
|
1422
|
+
}
|
|
873
1423
|
|
|
874
|
-
|
|
1424
|
+
for (let h = 0; h < holds.length; h++) {
|
|
1425
|
+
const hold = holds[h];
|
|
1426
|
+
|
|
1427
|
+
if (hold.valid) {
|
|
1428
|
+
hold.pose_previous.set(hold.pose);
|
|
1429
|
+
hold.world_previous.set(hold.world);
|
|
1430
|
+
|
|
1431
|
+
hold.has_previous = true;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
875
1434
|
|
|
876
1435
|
const dynamics = instance.cloth.dynamics;
|
|
877
1436
|
|
|
878
|
-
// Gravity is a world vector and the solve is in the
|
|
1437
|
+
// Gravity is a world vector and the solve is in the cloth's frame.
|
|
879
1438
|
const gravity = this.gravity;
|
|
880
1439
|
|
|
881
1440
|
// `previous_rotation` has just been set to this step's `q`; vector and rotation go in as arrays
|
|
@@ -894,7 +1453,8 @@ export class ClothWorld {
|
|
|
894
1453
|
// nothing beyond it — `cloth_gather_reach` carries the derivation,
|
|
895
1454
|
// and why the velocity clamp that used to stand here was both far
|
|
896
1455
|
// too wide and missing two terms.
|
|
897
|
-
|
|
1456
|
+
// and as far again as a holder drags it, which no velocity says
|
|
1457
|
+
const reach = cloth_gather_reach(state, this.colliders.index, dt) + instance.kinematic_travel;
|
|
898
1458
|
|
|
899
1459
|
cloth_gather_colliders(instance, this.colliders.index, reach);
|
|
900
1460
|
|
|
@@ -921,7 +1481,7 @@ export class ClothWorld {
|
|
|
921
1481
|
|
|
922
1482
|
instance.air_varies = cloth_sample_air(
|
|
923
1483
|
state, this.wind,
|
|
924
|
-
instance.
|
|
1484
|
+
instance.frame_translation, instance.frame_rotation,
|
|
925
1485
|
instance.air_seen, instance.age
|
|
926
1486
|
);
|
|
927
1487
|
} else {
|
|
@@ -929,6 +1489,7 @@ export class ClothWorld {
|
|
|
929
1489
|
}
|
|
930
1490
|
|
|
931
1491
|
params[offset + ClothStepParam.DRAG_RESPONSE] = drag_response;
|
|
1492
|
+
params[offset + ClothStepParam.KINEMATIC_TRAVEL] = instance.kinematic_travel;
|
|
932
1493
|
|
|
933
1494
|
const self_collision = (dynamics.flags & ClothDynamicsFlags.SelfCollision) !== 0;
|
|
934
1495
|
|
|
@@ -980,8 +1541,10 @@ export class ClothWorld {
|
|
|
980
1541
|
}
|
|
981
1542
|
|
|
982
1543
|
/**
|
|
983
|
-
*
|
|
984
|
-
* entity is playing
|
|
1544
|
+
* A rig's frame joint's world pose, computed on the CPU from the clips the
|
|
1545
|
+
* entity is playing — and, while the playbacks are in hand, the pose in that
|
|
1546
|
+
* frame of every bone the garment hangs from, for the write-back to express
|
|
1547
|
+
* the garment's roots against.
|
|
985
1548
|
*
|
|
986
1549
|
* **Computed, never read back**, for two reasons that are both the engine's
|
|
987
1550
|
* own. A joint of an animated skeleton is the GPU's (`GPUStateAuthority`),
|
|
@@ -997,36 +1560,62 @@ export class ClothWorld {
|
|
|
997
1560
|
* @returns {Transform64}
|
|
998
1561
|
* @private
|
|
999
1562
|
*/
|
|
1000
|
-
#
|
|
1563
|
+
#frame_pose(instance) {
|
|
1001
1564
|
const playbacks = PLAYBACKS;
|
|
1002
1565
|
|
|
1003
1566
|
playbacks.length = 0;
|
|
1004
1567
|
|
|
1005
1568
|
collect_entity_playbacks(playbacks, this.entityManager, instance.entity);
|
|
1006
1569
|
|
|
1007
|
-
|
|
1570
|
+
const ecd = this.entityManager.dataset;
|
|
1571
|
+
|
|
1572
|
+
const frame = instance.frame_pose;
|
|
1573
|
+
|
|
1574
|
+
t64_evaluate_world(frame, ecd, instance.frame_entity, playbacks);
|
|
1575
|
+
|
|
1576
|
+
const node_output = instance.node_output;
|
|
1577
|
+
const boundary = instance.boundary_pose;
|
|
1578
|
+
|
|
1579
|
+
for (let node = 0; node < instance.node_count; node++) {
|
|
1580
|
+
if (node_output[node] !== 0) {
|
|
1581
|
+
continue;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
t64_evaluate_world(EVALUATED, ecd, instance.node_joint[node], playbacks);
|
|
1585
|
+
|
|
1586
|
+
const b = node * 7;
|
|
1587
|
+
|
|
1588
|
+
HOLD_SCRATCH[0] = EVALUATED[TRANSFORM64_TRANSLATION_OFFSET] - frame[TRANSFORM64_TRANSLATION_OFFSET];
|
|
1589
|
+
HOLD_SCRATCH[1] = EVALUATED[TRANSFORM64_TRANSLATION_OFFSET + 1] - frame[TRANSFORM64_TRANSLATION_OFFSET + 1];
|
|
1590
|
+
HOLD_SCRATCH[2] = EVALUATED[TRANSFORM64_TRANSLATION_OFFSET + 2] - frame[TRANSFORM64_TRANSLATION_OFFSET + 2];
|
|
1591
|
+
|
|
1592
|
+
v3_quaternion_apply_inverse_array(boundary, b, HOLD_SCRATCH, 0, frame, TRANSFORM64_ROTATION_OFFSET);
|
|
1593
|
+
|
|
1594
|
+
FRAME_CONJUGATE[0] = -frame[TRANSFORM64_ROTATION_OFFSET];
|
|
1595
|
+
FRAME_CONJUGATE[1] = -frame[TRANSFORM64_ROTATION_OFFSET + 1];
|
|
1596
|
+
FRAME_CONJUGATE[2] = -frame[TRANSFORM64_ROTATION_OFFSET + 2];
|
|
1597
|
+
FRAME_CONJUGATE[3] = frame[TRANSFORM64_ROTATION_OFFSET + 3];
|
|
1598
|
+
|
|
1599
|
+
quat3_multiply_array(boundary, b + 3, FRAME_CONJUGATE, 0, EVALUATED, TRANSFORM64_ROTATION_OFFSET);
|
|
1600
|
+
}
|
|
1008
1601
|
|
|
1009
1602
|
playbacks.length = 0;
|
|
1010
1603
|
|
|
1011
|
-
return
|
|
1604
|
+
return frame;
|
|
1012
1605
|
}
|
|
1013
1606
|
|
|
1014
1607
|
/**
|
|
1608
|
+
* §7 P6's `Reset`: every particle onto the animated pose — the rest pose
|
|
1609
|
+
* hung from where its holders now are — at rest, with nothing carried over.
|
|
1610
|
+
*
|
|
1611
|
+
* A held particle lands on its target, and its target is where the predict
|
|
1612
|
+
* carries it to, so it does not move again this step: the jump is taken
|
|
1613
|
+
* between steps rather than dragged through one.
|
|
1614
|
+
*
|
|
1015
1615
|
* @param {ClothInstance} instance
|
|
1016
1616
|
* @private
|
|
1017
1617
|
*/
|
|
1018
|
-
#
|
|
1019
|
-
instance.teleport_count++;
|
|
1020
|
-
|
|
1021
|
-
if ((instance.cloth.flags & ClothFlags.TeleportKeep) !== 0) {
|
|
1022
|
-
// `Keep` carries the cloth's shape across the jump. Particles are
|
|
1023
|
-
// stored relative to the anchor, so that is exactly what happens if
|
|
1024
|
-
// this function does nothing to them — the frame moved and they did
|
|
1025
|
-
// not, which is MagicaCloth2's rigid delta expressed in the frame
|
|
1026
|
-
// that makes it free.
|
|
1027
|
-
return;
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1618
|
+
#reset(instance) {
|
|
1030
1619
|
const state = instance.state;
|
|
1031
1620
|
|
|
1032
1621
|
const node_count = instance.node_count;
|
|
@@ -1043,6 +1632,10 @@ export class ClothWorld {
|
|
|
1043
1632
|
}
|
|
1044
1633
|
|
|
1045
1634
|
state.position_previous.set(state.position);
|
|
1635
|
+
state.kinematic_target.set(state.position);
|
|
1636
|
+
|
|
1637
|
+
instance.kinematic_travel = 0;
|
|
1638
|
+
instance.holds_dirty = true;
|
|
1046
1639
|
state.velocity.fill(0);
|
|
1047
1640
|
state.inertia_blend.fill(0);
|
|
1048
1641
|
|
|
@@ -1078,11 +1671,51 @@ const LOCAL_GRAVITY = new Float64Array(3);
|
|
|
1078
1671
|
const FRAME = new Float64Array(8);
|
|
1079
1672
|
|
|
1080
1673
|
/**
|
|
1081
|
-
* The
|
|
1674
|
+
* The frame's pose this step, `[tx, ty, tz, qx, qy, qz, qw]`, as {@link move_local_frame} takes it.
|
|
1675
|
+
* @type {Float64Array}
|
|
1676
|
+
* @private
|
|
1677
|
+
*/
|
|
1678
|
+
const FRAME_POSE = new Float64Array(7);
|
|
1679
|
+
|
|
1680
|
+
/**
|
|
1681
|
+
* The frame's translation and rotation this step, apart, as the holds take them.
|
|
1082
1682
|
* @type {Float64Array}
|
|
1083
1683
|
* @private
|
|
1084
1684
|
*/
|
|
1085
|
-
const
|
|
1685
|
+
const FRAME_TRANSLATION = new Float64Array(3);
|
|
1686
|
+
|
|
1687
|
+
/**
|
|
1688
|
+
* @type {Float64Array}
|
|
1689
|
+
* @private
|
|
1690
|
+
*/
|
|
1691
|
+
const FRAME_ROTATION = new Float64Array(4);
|
|
1692
|
+
|
|
1693
|
+
/**
|
|
1694
|
+
* @type {Float64Array}
|
|
1695
|
+
* @private
|
|
1696
|
+
*/
|
|
1697
|
+
const FRAME_CONJUGATE = new Float64Array(4);
|
|
1698
|
+
|
|
1699
|
+
/**
|
|
1700
|
+
* @type {Float64Array}
|
|
1701
|
+
* @private
|
|
1702
|
+
*/
|
|
1703
|
+
const HOLD_SCRATCH = new Float64Array(4);
|
|
1704
|
+
|
|
1705
|
+
/**
|
|
1706
|
+
* An exactly evaluated world pose: a holder a clip drives, a bone a garment
|
|
1707
|
+
* hangs from.
|
|
1708
|
+
* @type {Transform64}
|
|
1709
|
+
* @private
|
|
1710
|
+
*/
|
|
1711
|
+
const EVALUATED = new Transform64();
|
|
1712
|
+
|
|
1713
|
+
/**
|
|
1714
|
+
* The models a holder's chain passes through, each once.
|
|
1715
|
+
* @type {number[]}
|
|
1716
|
+
* @private
|
|
1717
|
+
*/
|
|
1718
|
+
const MODELS = [];
|
|
1086
1719
|
|
|
1087
1720
|
/**
|
|
1088
1721
|
* {@link move_local_frame}'s `q_now⁻¹`, `[-qx, -qy, -qz, qw]`.
|
|
@@ -1100,27 +1733,27 @@ const POSE_CONJUGATE = new Float64Array(4);
|
|
|
1100
1733
|
const IDENTITY_ROTATION = new Float64Array([0, 0, 0, 1]);
|
|
1101
1734
|
|
|
1102
1735
|
/**
|
|
1103
|
-
* Carry the particles across a change of
|
|
1736
|
+
* Carry the free particles across a change of frame pose, by however much of it the
|
|
1104
1737
|
* fabric is willing to feel. §7 P7.
|
|
1105
1738
|
*
|
|
1106
1739
|
* A particle at local `p` was at world `t_prev + q_prev·p`. Holding that world
|
|
1107
1740
|
* position fixed, its local coordinates become `D + Q·p` with
|
|
1108
1741
|
* `D = q_now⁻¹·(t_prev − t_now)` and `Q = q_now⁻¹ ⊗ q_prev`. Feeling *all* of
|
|
1109
|
-
* the
|
|
1742
|
+
* the frame's motion is taking that; feeling *none* is leaving `p` alone; and
|
|
1110
1743
|
* `inertia` is the lerp between them. Velocity is carried the same way, minus
|
|
1111
1744
|
* the translation, since a translation of the frame does not rotate a vector.
|
|
1112
1745
|
*
|
|
1113
1746
|
* `D` and `Q` are limited before the lerp. The limits are the cloth's own reach
|
|
1114
|
-
* per step and a fixed angle per step, which is what makes
|
|
1747
|
+
* per step and a fixed angle per step, which is what makes a frame that
|
|
1115
1748
|
* teleports a metre — or spins — something a cape *lags behind* rather than
|
|
1116
1749
|
* something it is fired out of.
|
|
1117
1750
|
*
|
|
1118
1751
|
* Every vector and quaternion goes to its function as an array and an offset: handed over as numbers,
|
|
1119
1752
|
* each was boxed into a heap object wherever the call was not inlined, which until V8 optimises this
|
|
1120
|
-
* is every call, twice per particle per step of a moving
|
|
1753
|
+
* is every call, twice per particle per step of a moving frame.
|
|
1121
1754
|
*
|
|
1122
1755
|
* @param {ClothInstance} instance
|
|
1123
|
-
* @param {Float64Array} pose the
|
|
1756
|
+
* @param {Float64Array} pose the frame's pose now, `[tx, ty, tz, qx, qy, qz, qw]`
|
|
1124
1757
|
* @param {Float64Array} previous_translation
|
|
1125
1758
|
* @param {Float64Array} previous_rotation
|
|
1126
1759
|
* @private
|
|
@@ -1142,7 +1775,7 @@ function move_local_frame(instance, pose, previous_translation, previous_rotatio
|
|
|
1142
1775
|
|
|
1143
1776
|
v3_quaternion_apply_inverse_array(FRAME, 0, FRAME, 0, pose, 3);
|
|
1144
1777
|
|
|
1145
|
-
const travel_limit =
|
|
1778
|
+
const travel_limit = CLOTH_FRAME_TRAVEL_REACH * instance.reach;
|
|
1146
1779
|
|
|
1147
1780
|
const travel = Math.hypot(FRAME[0], FRAME[1], FRAME[2]);
|
|
1148
1781
|
|
|
@@ -1164,10 +1797,10 @@ function move_local_frame(instance, pose, previous_translation, previous_rotatio
|
|
|
1164
1797
|
// `|w|` is `cos(θ/2)`, so this is the angle test without an `acos`.
|
|
1165
1798
|
const half_angle_cos = FRAME[7] < 0 ? -FRAME[7] : FRAME[7];
|
|
1166
1799
|
|
|
1167
|
-
if (half_angle_cos < Math.cos(
|
|
1800
|
+
if (half_angle_cos < Math.cos(CLOTH_FRAME_ROTATION_LIMIT * 0.5)) {
|
|
1168
1801
|
const angle = 2 * Math.acos(half_angle_cos > 1 ? 1 : half_angle_cos);
|
|
1169
1802
|
|
|
1170
|
-
quat3_nlerp_array(FRAME, 4, IDENTITY_ROTATION, 0, FRAME, 4,
|
|
1803
|
+
quat3_nlerp_array(FRAME, 4, IDENTITY_ROTATION, 0, FRAME, 4, CLOTH_FRAME_ROTATION_LIMIT / angle);
|
|
1171
1804
|
}
|
|
1172
1805
|
|
|
1173
1806
|
const state = instance.state;
|
|
@@ -1181,7 +1814,16 @@ function move_local_frame(instance, pose, previous_translation, previous_rotatio
|
|
|
1181
1814
|
const dy = FRAME[1];
|
|
1182
1815
|
const dz = FRAME[2];
|
|
1183
1816
|
|
|
1817
|
+
const mass_inverse = state.mass_inverse;
|
|
1818
|
+
|
|
1184
1819
|
for (let i = 0; i < particle_count; i++) {
|
|
1820
|
+
// A held particle belongs to its holder, not to the world: it goes where
|
|
1821
|
+
// its holder takes it, through the step, and not where the frame's
|
|
1822
|
+
// motion would have left it.
|
|
1823
|
+
if (mass_inverse[i] === 0) {
|
|
1824
|
+
continue;
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1185
1827
|
const p = i * 3;
|
|
1186
1828
|
|
|
1187
1829
|
v3_quaternion_apply_array(CARRIED, 0, position, p, FRAME, 4);
|
|
@@ -1196,30 +1838,68 @@ function move_local_frame(instance, pose, previous_translation, previous_rotatio
|
|
|
1196
1838
|
velocity[p + 1] += inertia * (CARRIED[1] - velocity[p + 1]);
|
|
1197
1839
|
velocity[p + 2] += inertia * (CARRIED[2] - velocity[p + 2]);
|
|
1198
1840
|
}
|
|
1199
|
-
|
|
1200
|
-
pin_anchor(instance);
|
|
1201
1841
|
}
|
|
1202
1842
|
|
|
1203
1843
|
/**
|
|
1844
|
+
* `ClothFlags.TeleportKeep` for a holder that jumped: its own jump, in the
|
|
1845
|
+
* frame, applied to everything hanging from it — the particles it holds and
|
|
1846
|
+
* every free one whose nearest held particle is one of them — positions and
|
|
1847
|
+
* velocities alike. What hangs from another holder stays where it is.
|
|
1848
|
+
*
|
|
1204
1849
|
* @param {ClothInstance} instance
|
|
1850
|
+
* @param {number} h index into {@link ClothInstance#holds}
|
|
1205
1851
|
* @private
|
|
1206
1852
|
*/
|
|
1207
|
-
function
|
|
1208
|
-
const
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1853
|
+
function carry_with_holder(instance, h) {
|
|
1854
|
+
const hold = instance.holds[h];
|
|
1855
|
+
|
|
1856
|
+
const now = hold.pose;
|
|
1857
|
+
const before = hold.pose_previous;
|
|
1858
|
+
|
|
1859
|
+
// the jump's rotation, q_now ⊗ q_before⁻¹
|
|
1860
|
+
quat3_multiply(
|
|
1861
|
+
JUMP, 0,
|
|
1862
|
+
now[3], now[4], now[5], now[6],
|
|
1863
|
+
-before[3], -before[4], -before[5], before[6]
|
|
1864
|
+
);
|
|
1865
|
+
|
|
1866
|
+
const state = instance.state;
|
|
1867
|
+
const position = state.position;
|
|
1868
|
+
const velocity = state.velocity;
|
|
1869
|
+
|
|
1870
|
+
const node_hold = instance.node_hold;
|
|
1871
|
+
const source = instance.tether_source;
|
|
1872
|
+
const particle_of_node = instance.particle_of_node;
|
|
1873
|
+
|
|
1874
|
+
for (let node = 0; node < instance.node_count; node++) {
|
|
1875
|
+
const owner = node_hold[node] !== -1 ? node_hold[node] : (source[node] === -1 ? -1 : node_hold[source[node]]);
|
|
1876
|
+
|
|
1877
|
+
if (owner !== h) {
|
|
1878
|
+
continue;
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
const p = particle_of_node[node] * 3;
|
|
1882
|
+
|
|
1883
|
+
CARRIED[0] = position[p] - before[0];
|
|
1884
|
+
CARRIED[1] = position[p + 1] - before[1];
|
|
1885
|
+
CARRIED[2] = position[p + 2] - before[2];
|
|
1886
|
+
|
|
1887
|
+
v3_quaternion_apply_array(position, p, CARRIED, 0, JUMP, 0);
|
|
1888
|
+
|
|
1889
|
+
position[p] += now[0];
|
|
1890
|
+
position[p + 1] += now[1];
|
|
1891
|
+
position[p + 2] += now[2];
|
|
1892
|
+
|
|
1893
|
+
v3_quaternion_apply_array(velocity, p, velocity, p, JUMP, 0);
|
|
1894
|
+
}
|
|
1221
1895
|
}
|
|
1222
1896
|
|
|
1897
|
+
/**
|
|
1898
|
+
* @type {Float64Array}
|
|
1899
|
+
* @private
|
|
1900
|
+
*/
|
|
1901
|
+
const JUMP = new Float64Array(4);
|
|
1902
|
+
|
|
1223
1903
|
/**
|
|
1224
1904
|
* @type {Float64Array}
|
|
1225
1905
|
* @private
|
|
@@ -1285,46 +1965,3 @@ function cloth_inside_sleep_window(instance) {
|
|
|
1285
1965
|
return true;
|
|
1286
1966
|
}
|
|
1287
1967
|
|
|
1288
|
-
/**
|
|
1289
|
-
* Point every leash row at where it measures from: a slack row at the
|
|
1290
|
-
* particle's animated position, a tether at the anchor.
|
|
1291
|
-
*
|
|
1292
|
-
* @param {ClothInstance} instance
|
|
1293
|
-
* @private
|
|
1294
|
-
*/
|
|
1295
|
-
function refresh_leash_targets(instance) {
|
|
1296
|
-
const state = instance.state;
|
|
1297
|
-
|
|
1298
|
-
const target = state.leash_target;
|
|
1299
|
-
|
|
1300
|
-
const animated = instance.animated_position;
|
|
1301
|
-
|
|
1302
|
-
const anchor = instance.particle_of_node[0] * 3;
|
|
1303
|
-
|
|
1304
|
-
const anchor_x = state.position[anchor];
|
|
1305
|
-
const anchor_y = state.position[anchor + 1];
|
|
1306
|
-
const anchor_z = state.position[anchor + 2];
|
|
1307
|
-
|
|
1308
|
-
const node_count = instance.node_count;
|
|
1309
|
-
|
|
1310
|
-
const slack_row = instance.slack_row;
|
|
1311
|
-
const tether_row = instance.tether_row;
|
|
1312
|
-
|
|
1313
|
-
for (let node = 1; node < node_count; node++) {
|
|
1314
|
-
const tether = tether_row[node];
|
|
1315
|
-
|
|
1316
|
-
if (tether >= 0) {
|
|
1317
|
-
target[tether * 3] = anchor_x;
|
|
1318
|
-
target[tether * 3 + 1] = anchor_y;
|
|
1319
|
-
target[tether * 3 + 2] = anchor_z;
|
|
1320
|
-
}
|
|
1321
|
-
|
|
1322
|
-
const slack = slack_row[node];
|
|
1323
|
-
|
|
1324
|
-
if (slack >= 0) {
|
|
1325
|
-
target[slack * 3] = animated[node * 3];
|
|
1326
|
-
target[slack * 3 + 1] = animated[node * 3 + 1];
|
|
1327
|
-
target[slack * 3 + 2] = animated[node * 3 + 2];
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
}
|