@woosh/meep-engine 3.25.0 → 3.26.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/package.json +1 -1
- package/src/core/geom/3d/shape/HeightMapShape3D.d.ts +59 -0
- package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/HeightMapShape3D.js +285 -0
- package/src/core/geom/3d/shape/PosedShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/PosedShape3D.js +57 -0
- package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/TransformedShape3D.js +102 -0
- package/src/core/geom/3d/shape/UnionShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/UnionShape3D.js +64 -0
- package/src/engine/physics/cloth/MEASUREMENTS.md +582 -0
- package/src/engine/physics/cloth/PLAN.md +330 -49
- package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts +84 -17
- package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts.map +1 -1
- package/src/engine/physics/cloth/collider/ClothColliderIndex.js +451 -42
- package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +65 -7
- package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothInstance.js +72 -8
- package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +25 -4
- package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothSystem.js +75 -8
- package/src/engine/physics/cloth/ecs/ClothWorld.d.ts +41 -4
- package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothWorld.js +207 -63
- package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts +10 -0
- package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/WorkerClothSystem.js +17 -1
- package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts +27 -3
- package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_dynamics_map.js +28 -3
- package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts +75 -10
- package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_gather_colliders.js +131 -12
- package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts +19 -0
- package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_seed_rig.js +249 -201
- package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts +21 -0
- package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_seed_subtree.js +62 -1
- package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_write_back.js +18 -10
- package/src/engine/physics/cloth/playground/collide_main.js +1 -1
- package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts +29 -8
- package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/cloth_contact_find.js +37 -9
- package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts +12 -2
- package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/cloth_solver_constants.js +12 -2
- package/src/engine/physics/cloth/solver/cloth_step_instance.js +1 -1
- package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.d.ts +9 -2
- package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.js +9 -2
- package/src/engine/physics/cloth/wind/AbstractClothWind.d.ts +23 -9
- package/src/engine/physics/cloth/wind/AbstractClothWind.d.ts.map +1 -1
- package/src/engine/physics/cloth/wind/AbstractClothWind.js +23 -9
- package/src/engine/physics/cloth/wind/ClothAmbientWind.d.ts +7 -5
- package/src/engine/physics/cloth/wind/ClothAmbientWind.d.ts.map +1 -1
- package/src/engine/physics/cloth/wind/ClothAmbientWind.js +12 -9
- package/src/engine/physics/cloth/wind/ClothFluidWind.d.ts.map +1 -1
- package/src/engine/physics/cloth/wind/ClothFluidWind.js +5 -2
- package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts +7 -2
- package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts.map +1 -1
- package/src/engine/physics/cloth/wind/cloth_sample_air.js +12 -5
|
@@ -1,201 +1,249 @@
|
|
|
1
|
-
import { assert } from "../../../../core/assert.js";
|
|
2
|
-
import { quat3_multiply } from "../../../../core/geom/3d/quaternion/quat3_multiply.js";
|
|
3
|
-
import { v3_quaternion_apply } from "../../../../core/geom/vec3/v3_quaternion_apply.js";
|
|
4
|
-
import { GPUStateAuthorityFlags } from "../../../ecs/gpu/GPUStateAuthorityFlags.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* a
|
|
22
|
-
* `
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* animated
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* anchor
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* @
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* @param {
|
|
58
|
-
* @param {
|
|
59
|
-
* @param {
|
|
60
|
-
* @param {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
assert.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
instance.
|
|
120
|
-
|
|
121
|
-
instance.
|
|
122
|
-
instance.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
instance.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
instance.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
1
|
+
import { assert } from "../../../../core/assert.js";
|
|
2
|
+
import { quat3_multiply } from "../../../../core/geom/3d/quaternion/quat3_multiply.js";
|
|
3
|
+
import { v3_quaternion_apply } from "../../../../core/geom/vec3/v3_quaternion_apply.js";
|
|
4
|
+
import { GPUStateAuthorityFlags } from "../../../ecs/gpu/GPUStateAuthorityFlags.js";
|
|
5
|
+
import { gpu_authority_grant } from "../../../ecs/gpu/gpu_authority_grant.js";
|
|
6
|
+
import { gpu_authority_revoke } from "../../../ecs/gpu/gpu_authority_revoke.js";
|
|
7
|
+
import { TransformAttachment } from "../../../ecs/transform-attachment/TransformAttachment.js";
|
|
8
|
+
import { cloth_build_children, cloth_build_rows } from "./cloth_build_rows.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* # Seeding a cloth from a skinned model's joints — §11.2
|
|
12
|
+
*
|
|
13
|
+
* One particle per joint the proxy names, with the anchor as the fixed set, and
|
|
14
|
+
* everything after that is `cloth_build_rows.js` — the same rows, the same
|
|
15
|
+
* geodesics, the same leashes the entity path gets. §11 says "the solver is
|
|
16
|
+
* identical either way; only seeding and write-back differ", and this file plus
|
|
17
|
+
* `cloth_write_back_rig.js` is the whole of the difference.
|
|
18
|
+
*
|
|
19
|
+
* ## The joints are entities, and cloth takes them back from the GPU
|
|
20
|
+
*
|
|
21
|
+
* A skinned model is a prefab spawned as entities, so a joint is an entity with
|
|
22
|
+
* a `TransformAttachment` — the offset from its parent joint — and a
|
|
23
|
+
* `GPUStateAuthority` saying who writes that offset's row. A joint a clip
|
|
24
|
+
* drives is the GPU's: the clip resolves the row on the GPU and the CPU never
|
|
25
|
+
* writes it, so the attachment's offset stops describing it. Cloth writing that
|
|
26
|
+
* offset on such a joint would be writing to something nothing reads.
|
|
27
|
+
*
|
|
28
|
+
* So the seed **revokes** the GPU's authority over every joint it drives. The
|
|
29
|
+
* consequence is stated rather than hidden: a joint cloth simulates stops being
|
|
30
|
+
* animatable by a clip, because two writers of one row is the race the
|
|
31
|
+
* authority component exists to prevent. That is the right trade for a cape —
|
|
32
|
+
* the whole point of simulating it is that nobody keyframes it — and it is an
|
|
33
|
+
* authoring error for a garment whose joints are also keyed, which is why the
|
|
34
|
+
* seed says so.
|
|
35
|
+
*
|
|
36
|
+
* ## The animated pose is constant, exactly as it is for a rope
|
|
37
|
+
*
|
|
38
|
+
* §11.1 says a subtree's authored pose is rigid relative to its anchor, so in
|
|
39
|
+
* the anchor's own frame it does not move, and then says "a subtree whose links
|
|
40
|
+
* are themselves animated is not an M1 case — that is the rig path, where the
|
|
41
|
+
* animated pose comes from the skeleton and is live".
|
|
42
|
+
*
|
|
43
|
+
* For a **rigged** garment it is not live either, and the reason is the
|
|
44
|
+
* authority claim above: cloth owns those joints, so nothing else poses them,
|
|
45
|
+
* so their authored pose is the proxy's baked bind pose — constant in the
|
|
46
|
+
* anchor's frame, exactly as a rope's is. What moves is the anchor, and the
|
|
47
|
+
* anchor is a joint of the character's own animated skeleton, whose pose the
|
|
48
|
+
* system evaluates exactly (`t64_evaluate_world`).
|
|
49
|
+
*
|
|
50
|
+
* @author Alex Goldring
|
|
51
|
+
* @copyright Company Named Limited (c) 2026
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Seed `instance` from a proxy and the joints of a skin.
|
|
56
|
+
*
|
|
57
|
+
* @param {ClothInstance} instance seeded in place
|
|
58
|
+
* @param {ClothProxy} proxy
|
|
59
|
+
* @param {number[]} joints the skin's joints as entities, in the order the proxy indexes
|
|
60
|
+
* @param {EntityComponentDataset} ecd
|
|
61
|
+
* @param {number} dt
|
|
62
|
+
*/
|
|
63
|
+
export function cloth_seed_rig(instance, proxy, joints, ecd, dt) {
|
|
64
|
+
assert.ok(proxy.isClothProxy === true, 'proxy must be a ClothProxy');
|
|
65
|
+
|
|
66
|
+
const joint_count = proxy.joint_count;
|
|
67
|
+
|
|
68
|
+
// Node 0 is the anchor, exactly as it is for the entity path: the fixed
|
|
69
|
+
// set, the origin of the local frame, and `d_geo = 0`.
|
|
70
|
+
const node_count = joint_count + 1;
|
|
71
|
+
|
|
72
|
+
instance.node_count = node_count;
|
|
73
|
+
|
|
74
|
+
const node_parent = new Int32Array(node_count);
|
|
75
|
+
|
|
76
|
+
node_parent[0] = -1;
|
|
77
|
+
|
|
78
|
+
for (let i = 0; i < joint_count; i++) {
|
|
79
|
+
node_parent[i + 1] = proxy.joint_parent[i] + 1;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
instance.node_parent = node_parent;
|
|
83
|
+
|
|
84
|
+
const node_joint = new Int32Array(node_count);
|
|
85
|
+
|
|
86
|
+
node_joint[0] = joints[proxy.anchor];
|
|
87
|
+
|
|
88
|
+
const node_attachment = new Array(node_count);
|
|
89
|
+
|
|
90
|
+
node_attachment[0] = null;
|
|
91
|
+
|
|
92
|
+
// What this seeding took, so that giving it back is possible. A revoke that
|
|
93
|
+
// nothing can undo is half a transaction, and the half that is missing is
|
|
94
|
+
// the one that runs when the garment is removed.
|
|
95
|
+
const revoked = [];
|
|
96
|
+
|
|
97
|
+
for (let i = 0; i < joint_count; i++) {
|
|
98
|
+
const joint = joints[proxy.joint[i]];
|
|
99
|
+
|
|
100
|
+
assert.defined(joint, `joint ${proxy.joint[i]} of the skin`);
|
|
101
|
+
assert.ok(ecd.entityExists(joint), `joint ${proxy.joint[i]} of the skin, entity ${joint}, does not exist`);
|
|
102
|
+
|
|
103
|
+
node_joint[i + 1] = joint;
|
|
104
|
+
|
|
105
|
+
const attachment = ecd.getComponent(joint, TransformAttachment);
|
|
106
|
+
|
|
107
|
+
assert.defined(attachment, `joint entity ${joint} carries no TransformAttachment to write`);
|
|
108
|
+
|
|
109
|
+
node_attachment[i + 1] = attachment;
|
|
110
|
+
|
|
111
|
+
// See the module docblock. A clip-driven joint's offset is the GPU's, and taking it back
|
|
112
|
+
// is what makes the write-back reach the row at all. An entity that never declared has
|
|
113
|
+
// nothing to take back.
|
|
114
|
+
if (gpu_authority_revoke(ecd, joint, GPUStateAuthorityFlags.TransformAttachment)) {
|
|
115
|
+
revoked.push(joint);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
instance.revoked_joints = revoked;
|
|
120
|
+
|
|
121
|
+
instance.node_joint = node_joint;
|
|
122
|
+
instance.node_attachment = node_attachment;
|
|
123
|
+
|
|
124
|
+
// The entity path's arrays have no meaning here and holding stale ones is
|
|
125
|
+
// how a write-back reaches into a previous seeding's entities.
|
|
126
|
+
instance.node_entity = null;
|
|
127
|
+
instance.node_transform = [];
|
|
128
|
+
instance.pruned_count = 0;
|
|
129
|
+
instance.pruned_entity = null;
|
|
130
|
+
instance.pruned_parent = null;
|
|
131
|
+
instance.pruned_transform = [];
|
|
132
|
+
instance.pruned_attachment = [];
|
|
133
|
+
|
|
134
|
+
cloth_build_children(instance);
|
|
135
|
+
|
|
136
|
+
read_bind_pose(instance, proxy);
|
|
137
|
+
|
|
138
|
+
instance.dynamics_seen.copy(instance.cloth.dynamics);
|
|
139
|
+
|
|
140
|
+
cloth_build_rows(instance, dt);
|
|
141
|
+
|
|
142
|
+
instance.seeded = true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The proxy's bind pose, composed: local offsets per node and the position and
|
|
147
|
+
* rotation of every node in the anchor's frame.
|
|
148
|
+
*
|
|
149
|
+
* @param {ClothInstance} instance
|
|
150
|
+
* @param {ClothProxy} proxy
|
|
151
|
+
*/
|
|
152
|
+
function read_bind_pose(instance, proxy) {
|
|
153
|
+
const node_count = instance.node_count;
|
|
154
|
+
|
|
155
|
+
const local_position = new Float32Array(node_count * 3);
|
|
156
|
+
const local_rotation = new Float32Array(node_count * 4);
|
|
157
|
+
|
|
158
|
+
const position = new Float32Array(node_count * 3);
|
|
159
|
+
const rotation = new Float32Array(node_count * 4);
|
|
160
|
+
|
|
161
|
+
// The anchor is the origin of the frame it anchors.
|
|
162
|
+
local_rotation[3] = 1;
|
|
163
|
+
rotation[3] = 1;
|
|
164
|
+
|
|
165
|
+
const parent = instance.node_parent;
|
|
166
|
+
|
|
167
|
+
for (let node = 1; node < node_count; node++) {
|
|
168
|
+
const joint = node - 1;
|
|
169
|
+
|
|
170
|
+
local_position[node * 3] = proxy.rest_position[joint * 3];
|
|
171
|
+
local_position[node * 3 + 1] = proxy.rest_position[joint * 3 + 1];
|
|
172
|
+
local_position[node * 3 + 2] = proxy.rest_position[joint * 3 + 2];
|
|
173
|
+
|
|
174
|
+
local_rotation[node * 4] = proxy.rest_rotation[joint * 4];
|
|
175
|
+
local_rotation[node * 4 + 1] = proxy.rest_rotation[joint * 4 + 1];
|
|
176
|
+
local_rotation[node * 4 + 2] = proxy.rest_rotation[joint * 4 + 2];
|
|
177
|
+
local_rotation[node * 4 + 3] = proxy.rest_rotation[joint * 4 + 3];
|
|
178
|
+
|
|
179
|
+
const p = parent[node];
|
|
180
|
+
|
|
181
|
+
const pqx = rotation[p * 4];
|
|
182
|
+
const pqy = rotation[p * 4 + 1];
|
|
183
|
+
const pqz = rotation[p * 4 + 2];
|
|
184
|
+
const pqw = rotation[p * 4 + 3];
|
|
185
|
+
|
|
186
|
+
v3_quaternion_apply(
|
|
187
|
+
COMPOSE, 0,
|
|
188
|
+
local_position[node * 3], local_position[node * 3 + 1], local_position[node * 3 + 2],
|
|
189
|
+
pqx, pqy, pqz, pqw
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
position[node * 3] = position[p * 3] + COMPOSE[0];
|
|
193
|
+
position[node * 3 + 1] = position[p * 3 + 1] + COMPOSE[1];
|
|
194
|
+
position[node * 3 + 2] = position[p * 3 + 2] + COMPOSE[2];
|
|
195
|
+
|
|
196
|
+
quat3_multiply(
|
|
197
|
+
rotation, node * 4,
|
|
198
|
+
pqx, pqy, pqz, pqw,
|
|
199
|
+
local_rotation[node * 4], local_rotation[node * 4 + 1],
|
|
200
|
+
local_rotation[node * 4 + 2], local_rotation[node * 4 + 3]
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
instance.rest_local_position = local_position;
|
|
205
|
+
instance.rest_local_rotation = local_rotation;
|
|
206
|
+
|
|
207
|
+
instance.animated_position = position;
|
|
208
|
+
instance.animated_rotation = rotation;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Give back every joint's `TransformAttachment` authority that this cloth's
|
|
213
|
+
* seeding took from the GPU.
|
|
214
|
+
*
|
|
215
|
+
* The seed revokes so that the write-back reaches the row at all, and the
|
|
216
|
+
* consequence the module docblock states — "a joint cloth simulates stops being
|
|
217
|
+
* animatable by a clip" — is only the right trade while the cloth is *there*.
|
|
218
|
+
* A garment removed and nothing re-granting leaves its joints owned by a CPU
|
|
219
|
+
* that is no longer writing them: neither animated nor simulated, and stuck in
|
|
220
|
+
* the pose the solver stopped in.
|
|
221
|
+
*
|
|
222
|
+
* Only the joints the revoke actually changed are given back, so a joint that
|
|
223
|
+
* was already the CPU's before cloth arrived stays the CPU's.
|
|
224
|
+
*
|
|
225
|
+
* @param {ClothInstance} instance
|
|
226
|
+
* @param {EntityComponentDataset} ecd
|
|
227
|
+
* @returns {void}
|
|
228
|
+
*/
|
|
229
|
+
export function cloth_release_rig(instance, ecd) {
|
|
230
|
+
const revoked = instance.revoked_joints;
|
|
231
|
+
|
|
232
|
+
if (revoked === null) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
for (let i = 0; i < revoked.length; i++) {
|
|
237
|
+
const joint = revoked[i];
|
|
238
|
+
|
|
239
|
+
if (!ecd.entityExists(joint)) {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
gpu_authority_grant(ecd, joint, GPUStateAuthorityFlags.TransformAttachment);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
instance.revoked_joints = null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const COMPOSE = new Float64Array(3);
|
|
@@ -55,5 +55,26 @@
|
|
|
55
55
|
* @returns {void}
|
|
56
56
|
*/
|
|
57
57
|
export function cloth_seed_subtree(instance: ClothInstance, ecd: EntityComponentDataset, attachments: TransformAttachmentSystem, dt: number): void;
|
|
58
|
+
/**
|
|
59
|
+
* Put the authored local transforms back on the subtree and announce, so a
|
|
60
|
+
* cloth that is taken away leaves the hierarchy the way it found it.
|
|
61
|
+
*
|
|
62
|
+
* `cloth_write_back` overwrites every simulated node's
|
|
63
|
+
* `TransformAttachment#transform` with the solved local pose, every step. That
|
|
64
|
+
* is what makes a recomposition reproduce the simulation rather than undo it —
|
|
65
|
+
* and it means the authored pose exists nowhere but inside the cloth. Removing
|
|
66
|
+
* the component without this leaves the subtree frozen in whatever shape the
|
|
67
|
+
* solver stopped in, and a `Cloth` added back seeds *that* as the pose it is
|
|
68
|
+
* supposed to return to at `blend = 0`.
|
|
69
|
+
*
|
|
70
|
+
* One announcement, on the anchor, which is the same shape the write-back's own
|
|
71
|
+
* pruned-boundary pass uses: everything below is composed by the attachment
|
|
72
|
+
* cascade rather than by a second walk here.
|
|
73
|
+
*
|
|
74
|
+
* @param {ClothInstance} instance
|
|
75
|
+
* @param {EntityComponentDataset} ecd
|
|
76
|
+
* @returns {void}
|
|
77
|
+
*/
|
|
78
|
+
export function cloth_release_subtree(instance: ClothInstance, ecd: EntityComponentDataset): void;
|
|
58
79
|
import { ClothInstance } from "./ClothInstance.js";
|
|
59
80
|
//# sourceMappingURL=cloth_seed_subtree.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloth_seed_subtree.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/cloth/ecs/cloth_seed_subtree.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cloth_seed_subtree.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/cloth/ecs/cloth_seed_subtree.js"],"names":[],"mappings":"AAiBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH;;;;;;;;;GASG;AACH,6CAPW,aAAa,2EAGb,MAAM,GAEJ,IAAI,CAwGhB;AA6GD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,gDAJW,aAAa,gCAEX,IAAI,CAkChB;8BAnU6B,oBAAoB"}
|
|
@@ -4,6 +4,7 @@ import { v3_quaternion_apply } from "../../../../core/geom/vec3/v3_quaternion_ap
|
|
|
4
4
|
import { GPUStateAuthorityFlags } from "../../../ecs/gpu/GPUStateAuthorityFlags.js";
|
|
5
5
|
import { gpu_authority_assert_cpu } from "../../../ecs/gpu/gpu_authority_assert_cpu.js";
|
|
6
6
|
import { TransformAttachment } from "../../../ecs/transform-attachment/TransformAttachment.js";
|
|
7
|
+
import { t64_announce_change } from "../../../ecs/transform/t64_announce_change.js";
|
|
7
8
|
import {
|
|
8
9
|
TRANSFORM64_ROTATION_OFFSET,
|
|
9
10
|
TRANSFORM64_TRANSLATION_OFFSET,
|
|
@@ -282,6 +283,60 @@ function read_rest_pose(instance, remembered) {
|
|
|
282
283
|
instance.animated_rotation = rotation;
|
|
283
284
|
}
|
|
284
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Put the authored local transforms back on the subtree and announce, so a
|
|
288
|
+
* cloth that is taken away leaves the hierarchy the way it found it.
|
|
289
|
+
*
|
|
290
|
+
* `cloth_write_back` overwrites every simulated node's
|
|
291
|
+
* `TransformAttachment#transform` with the solved local pose, every step. That
|
|
292
|
+
* is what makes a recomposition reproduce the simulation rather than undo it —
|
|
293
|
+
* and it means the authored pose exists nowhere but inside the cloth. Removing
|
|
294
|
+
* the component without this leaves the subtree frozen in whatever shape the
|
|
295
|
+
* solver stopped in, and a `Cloth` added back seeds *that* as the pose it is
|
|
296
|
+
* supposed to return to at `blend = 0`.
|
|
297
|
+
*
|
|
298
|
+
* One announcement, on the anchor, which is the same shape the write-back's own
|
|
299
|
+
* pruned-boundary pass uses: everything below is composed by the attachment
|
|
300
|
+
* cascade rather than by a second walk here.
|
|
301
|
+
*
|
|
302
|
+
* @param {ClothInstance} instance
|
|
303
|
+
* @param {EntityComponentDataset} ecd
|
|
304
|
+
* @returns {void}
|
|
305
|
+
*/
|
|
306
|
+
export function cloth_release_subtree(instance, ecd) {
|
|
307
|
+
const rest_position = instance.rest_local_position;
|
|
308
|
+
const rest_rotation = instance.rest_local_rotation;
|
|
309
|
+
|
|
310
|
+
if (rest_position === null || instance.node_entity === null) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const node_count = instance.node_count;
|
|
315
|
+
|
|
316
|
+
for (let node = 1; node < node_count; node++) {
|
|
317
|
+
const attachment = instance.node_attachment[node];
|
|
318
|
+
|
|
319
|
+
if (attachment === undefined || attachment === null) {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const transform = attachment.transform;
|
|
324
|
+
|
|
325
|
+
transform.setTranslation(
|
|
326
|
+
rest_position[node * 3], rest_position[node * 3 + 1], rest_position[node * 3 + 2]
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
transform.setRotation(
|
|
330
|
+
rest_rotation[node * 4], rest_rotation[node * 4 + 1],
|
|
331
|
+
rest_rotation[node * 4 + 2], rest_rotation[node * 4 + 3]
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
transform.updateMatrix();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
t64_announce_change(ecd, instance.node_entity[0]);
|
|
338
|
+
}
|
|
339
|
+
|
|
285
340
|
/**
|
|
286
341
|
* @type {Float64Array}
|
|
287
342
|
* @private
|
|
@@ -300,7 +355,13 @@ const COMPOSE = new Float64Array(3);
|
|
|
300
355
|
function remember_rest_pose(instance) {
|
|
301
356
|
const remembered = new Map();
|
|
302
357
|
|
|
303
|
-
|
|
358
|
+
// Keyed on there *being* a previous capture rather than on
|
|
359
|
+
// {@link ClothInstance#seeded}, because `ClothWorld#reseed` clears `seeded`
|
|
360
|
+
// before this runs — which made the one path that exists to re-walk a live
|
|
361
|
+
// cloth the one path that could not remember anything. `node_entity` is
|
|
362
|
+
// null for a cloth that was previously seeded as a rig, whose captured pose
|
|
363
|
+
// is a joint's and not this walk's.
|
|
364
|
+
if (instance.rest_local_position === null || instance.node_entity === null) {
|
|
304
365
|
return remembered;
|
|
305
366
|
}
|
|
306
367
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloth_write_back.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/cloth/ecs/cloth_write_back.js"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH;;;;;;GAMG;AACH,wFAFa,IAAI,
|
|
1
|
+
{"version":3,"file":"cloth_write_back.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/cloth/ecs/cloth_write_back.js"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH;;;;;;GAMG;AACH,wFAFa,IAAI,CA2DhB;AAyBD;;;;;;;;;GASG;AACH,0FAJW,MAAM,UACN,MAAM,GACJ,IAAI,CA4BhB"}
|
|
@@ -76,16 +76,24 @@ export function cloth_write_back(instance, ecd) {
|
|
|
76
76
|
const blended = instance.blended_position;
|
|
77
77
|
const reconstructed = instance.blended_rotation;
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
79
|
+
// The anchor pose the particles were carried into **this step**, not the
|
|
80
|
+
// live component. For `ClothSystem` those are the same value, because the
|
|
81
|
+
// step happened between them. For `WorkerClothSystem` they are not: it
|
|
82
|
+
// writes step T−1 back at the top of tick T, by which time the anchor has
|
|
83
|
+
// moved on, and composing last step's shape against this tick's anchor
|
|
84
|
+
// produces a world pose that describes neither. Nothing rendered can tell —
|
|
85
|
+
// the attachment cascade re-derives position from the anchor and the local
|
|
86
|
+
// transforms either way (§4.1) — but a **nested** cloth reads this pose as
|
|
87
|
+
// its own anchor, and then the two systems hand it different numbers.
|
|
88
|
+
// `MEASUREMENTS.md` §56.
|
|
89
|
+
const ax = instance.anchor_translation[0];
|
|
90
|
+
const ay = instance.anchor_translation[1];
|
|
91
|
+
const az = instance.anchor_translation[2];
|
|
92
|
+
|
|
93
|
+
const aqx = instance.anchor_rotation[0];
|
|
94
|
+
const aqy = instance.anchor_rotation[1];
|
|
95
|
+
const aqz = instance.anchor_rotation[2];
|
|
96
|
+
const aqw = instance.anchor_rotation[3];
|
|
89
97
|
|
|
90
98
|
const parent = instance.node_parent;
|
|
91
99
|
|
|
@@ -173,7 +173,7 @@ function step_once() {
|
|
|
173
173
|
|
|
174
174
|
const started = performance.now();
|
|
175
175
|
|
|
176
|
-
contact_rows = cloth_contact_find(state, colliders.table, colliders.keys, colliders.count);
|
|
176
|
+
contact_rows = cloth_contact_find(state, colliders.table, colliders.keys, colliders.count, FIXED_STEP);
|
|
177
177
|
|
|
178
178
|
cloth_step(
|
|
179
179
|
state, FIXED_STEP, 0, -9.81, 0,
|