|
13 | 13 | evaluate_edge_edge_contact_2_vertices, |
14 | 14 | evaluate_vertex_triangle_collision_force_hessian_4_vertices, |
15 | 15 | ) |
16 | | -from .rigid_vbd_kernels import _eval_body_particle_contact, _eval_soft_ef_contact |
| 16 | +from .rigid_vbd_kernels import _NUM_CONTACT_THREADS_PER_BODY, _eval_body_particle_contact, _eval_soft_ef_contact |
17 | 17 | from .tri_mesh_collision import TriMeshCollisionInfo |
18 | 18 |
|
19 | 19 | wp.set_module_options({"enable_backward": False}) |
@@ -150,58 +150,84 @@ def _harvest_vbd_body_particle_contact_forces_on_proxy_bodies_kernel( |
150 | 150 | body_particle_contact_normal: wp.array[wp.vec3], |
151 | 151 | shape_margin: wp.array[float], |
152 | 152 | shape_body: wp.array[wp.int32], |
| 153 | + body_particle_contact_buffer_pre_alloc: int, |
| 154 | + body_particle_contact_counts: wp.array[wp.int32], |
| 155 | + body_particle_contact_indices: wp.array[wp.int32], |
153 | 156 | out_body_f: wp.array[wp.spatial_vector], |
154 | 157 | ): |
155 | | - contact_idx = wp.tid() |
156 | | - if contact_idx >= body_particle_contact_count[0]: |
157 | | - return |
158 | | - |
159 | | - shape_idx = body_particle_contact_shape[contact_idx] |
160 | | - if shape_idx < 0 or shape_idx >= shape_body.shape[0]: |
161 | | - return |
162 | | - |
163 | | - body_idx = shape_body[shape_idx] |
164 | | - if body_idx < 0 or body_idx >= body_local_to_proxy_global.shape[0]: |
| 158 | + """Sum the body-side soft-contact reaction the destination solve applied to each proxy body. |
| 159 | +
|
| 160 | + Walks the per-body adjacency list, and truncates it at the same per-body capacity as |
| 161 | + ``accumulate_body_particle_contacts_per_body``. A body carrying more soft contacts than the |
| 162 | + list holds is pushed by only the records the list kept, so harvesting the whole contact |
| 163 | + stream would hand the source solver a reaction the destination never applied -- momentum the |
| 164 | + coupled pair would then disagree about, growing with the size of the overflow. |
| 165 | + """ |
| 166 | + tid = wp.tid() |
| 167 | + body_idx = tid // _NUM_CONTACT_THREADS_PER_BODY |
| 168 | + thread_id_within_body = tid % _NUM_CONTACT_THREADS_PER_BODY |
| 169 | + |
| 170 | + if body_idx >= body_local_to_proxy_global.shape[0]: |
165 | 171 | return |
166 | 172 |
|
167 | 173 | proxy_global = body_local_to_proxy_global[body_idx] |
168 | 174 | if proxy_global < 0 or proxy_global >= out_body_f.shape[0]: |
169 | 175 | return |
170 | 176 |
|
171 | | - corners = soft_contact_indices[contact_idx] |
172 | | - if corners[0] < 0 or corners[0] >= particle_q.shape[0]: |
| 177 | + num_contacts = body_particle_contact_counts[body_idx] |
| 178 | + if num_contacts > body_particle_contact_buffer_pre_alloc: |
| 179 | + num_contacts = body_particle_contact_buffer_pre_alloc |
| 180 | + if num_contacts == 0: |
173 | 181 | return |
174 | 182 |
|
175 | | - bary = soft_contact_barycentric[contact_idx] |
176 | | - |
177 | | - force_on_particle, _hess, cp_world = _eval_soft_ef_contact( |
178 | | - contact_idx, |
179 | | - corners, |
180 | | - bary, |
181 | | - particle_q, |
182 | | - particle_q_prev, |
183 | | - particle_radius, |
184 | | - body_particle_contact_penalty_k[contact_idx], |
185 | | - body_particle_contact_material_kd[contact_idx], |
186 | | - body_particle_contact_material_mu[contact_idx], |
187 | | - friction_epsilon, |
188 | | - shape_body, |
189 | | - body_q, |
190 | | - body_q_prev, |
191 | | - body_qd, |
192 | | - body_com, |
193 | | - body_particle_contact_shape, |
194 | | - body_particle_contact_body_pos, |
195 | | - body_particle_contact_body_vel, |
196 | | - body_particle_contact_normal, |
197 | | - shape_margin, |
198 | | - dt, |
199 | | - ) |
200 | | - |
201 | | - force_on_body = -force_on_particle |
| 183 | + max_contacts = body_particle_contact_count[0] # single total soft-contact count |
202 | 184 | com_world = wp.transform_point(body_q[body_idx], body_com[body_idx]) |
203 | | - torque_on_body = wp.cross(cp_world - com_world, force_on_body) |
204 | | - wp.atomic_add(out_body_f, proxy_global, wp.spatial_vector(force_on_body, torque_on_body)) |
| 185 | + |
| 186 | + force_acc = wp.vec3(0.0) |
| 187 | + torque_acc = wp.vec3(0.0) |
| 188 | + |
| 189 | + i = thread_id_within_body |
| 190 | + while i < num_contacts: |
| 191 | + contact_idx = body_particle_contact_indices[body_idx * body_particle_contact_buffer_pre_alloc + i] |
| 192 | + i += _NUM_CONTACT_THREADS_PER_BODY |
| 193 | + if contact_idx >= max_contacts: |
| 194 | + continue |
| 195 | + |
| 196 | + corners = soft_contact_indices[contact_idx] |
| 197 | + if corners[0] < 0 or corners[0] >= particle_q.shape[0]: |
| 198 | + continue |
| 199 | + |
| 200 | + bary = soft_contact_barycentric[contact_idx] |
| 201 | + |
| 202 | + force_on_particle, _hess, cp_world = _eval_soft_ef_contact( |
| 203 | + contact_idx, |
| 204 | + corners, |
| 205 | + bary, |
| 206 | + particle_q, |
| 207 | + particle_q_prev, |
| 208 | + particle_radius, |
| 209 | + body_particle_contact_penalty_k[contact_idx], |
| 210 | + body_particle_contact_material_kd[contact_idx], |
| 211 | + body_particle_contact_material_mu[contact_idx], |
| 212 | + friction_epsilon, |
| 213 | + shape_body, |
| 214 | + body_q, |
| 215 | + body_q_prev, |
| 216 | + body_qd, |
| 217 | + body_com, |
| 218 | + body_particle_contact_shape, |
| 219 | + body_particle_contact_body_pos, |
| 220 | + body_particle_contact_body_vel, |
| 221 | + body_particle_contact_normal, |
| 222 | + shape_margin, |
| 223 | + dt, |
| 224 | + ) |
| 225 | + |
| 226 | + force_on_body = -force_on_particle |
| 227 | + force_acc += force_on_body |
| 228 | + torque_acc += wp.cross(cp_world - com_world, force_on_body) |
| 229 | + |
| 230 | + wp.atomic_add(out_body_f, proxy_global, wp.spatial_vector(force_acc, torque_acc)) |
205 | 231 |
|
206 | 232 |
|
207 | 233 | @wp.func |
|
0 commit comments