Skip to main content

rapier3d/dynamics/soft_body/collision_mesh/
collision_mesh_topology.rs

1//! Construction of a collision mesh, its topology tables, and the per-step update of its
2//! vertices, orientation and collider shape.
3
4use crate::alloc_prelude::*;
5use crate::geometry::ColliderHandle;
6use crate::math::{DIM, Pose, Real, Vector};
7use parry::utils::hashmap::HashMap;
8#[cfg(not(feature = "std"))]
9#[allow(unused_imports)]
10use simba::scalar::{ComplexField as _, RealField as _};
11
12use super::collision_mesh_binding::{bind_to_cells, closest_cell_binding};
13use super::soft_body_builder::{element_vertices, element_vertices_mut};
14use super::{
15    SoftBody, SoftBodyCell, SoftCollisionMesh, SoftMeshCellBinding, SoftMeshId, SoftMeshMapping,
16    SoftTopologyRemap, surface_element_cells, surface_is_closed, surface_rings,
17    surface_vertex_elements,
18};
19
20impl SoftCollisionMesh {
21    /// The mesh a body collides through when it collides through its own boundary: one vertex
22    /// per particle (interior particles included, so vertex indices are particle indices), the
23    /// cells' boundary as elements.
24    pub(crate) fn boundary(body: &SoftBody, self_contacts: bool, collision_enabled: bool) -> Self {
25        let num_vertices = body.particles.len();
26        let indices = body.boundary.clone();
27        let (ring_offsets, ring) = surface_rings(num_vertices, &indices);
28        let (vertex_elements_offsets, vertex_elements) =
29            surface_vertex_elements(num_vertices, &indices);
30        #[cfg(feature = "dim3")]
31        let (edges, element_edges, edge_owners) =
32            super::soft_body_builder::surface_edge_table(&indices);
33
34        Self {
35            binding: SoftMeshMapping::Direct {
36                particles: (0..num_vertices as u32).collect(),
37            },
38            id: SoftMeshId {
39                cluster: 0,
40                mesh: 0,
41            },
42            follows_boundary: true,
43            closed: surface_is_closed(&indices),
44            rest_signed_volume: SoftBody::boundary_volume(&indices, |i| {
45                body.particles[i as usize].rest_position
46            }),
47            element_cells: Vec::new(),
48            indices,
49            vertices: Vec::new(),
50            ring_offsets,
51            ring,
52            vertex_elements_offsets,
53            vertex_elements,
54            #[cfg(feature = "dim3")]
55            edges,
56            #[cfg(feature = "dim3")]
57            element_edges,
58            #[cfg(feature = "dim3")]
59            edge_owners,
60            inverted: false,
61            orientation_unreliable: false,
62            oriented: false,
63            collision_enabled,
64            collider: ColliderHandle::invalid(),
65            self_contacts,
66            edge_contacts: Vec::new(),
67            vertex_contacts: Vec::new(),
68            overlap_states: Vec::new(),
69            overlap_warm: Vec::new(),
70            volume_contacts: Vec::new(),
71            crossing_sweep_travel: Real::MAX,
72            crossed_partners: Vec::new(),
73            vertex_cache: Vec::new(),
74            topology_version: 0,
75        }
76    }
77
78    /// A mesh bound to a cluster through `mapping`, whose vertices are at `vertices` when the
79    /// binding is taken.
80    pub(crate) fn new(
81        mapping: SoftMeshMapping,
82        indices: Vec<[u32; DIM]>,
83        vertices: Vec<Vector>,
84        body: &SoftBody,
85        self_contacts: bool,
86    ) -> Self {
87        let num_vertices = vertices.len();
88        let (ring_offsets, ring) = surface_rings(num_vertices, &indices);
89        let (vertex_elements_offsets, vertex_elements) =
90            surface_vertex_elements(num_vertices, &indices);
91        #[cfg(feature = "dim3")]
92        let (edges, element_edges, edge_owners) =
93            super::soft_body_builder::surface_edge_table(&indices);
94        let skinned = matches!(mapping, SoftMeshMapping::Skinned { .. });
95        // A wire has no facet, so no owning cell to read a winding from.
96        let wire = indices
97            .first()
98            .is_some_and(|e| super::soft_body_builder::element_vertices(e).len() < DIM);
99
100        Self {
101            closed: surface_is_closed(&indices),
102            // A wire encloses nothing: its "volume" is not a quantity.
103            rest_signed_volume: if wire {
104                0.0
105            } else {
106                SoftBody::boundary_volume(&indices, |i| vertices[i as usize])
107            },
108            element_cells: if skinned || wire {
109                Vec::new()
110            } else {
111                surface_element_cells(&indices, &body.cells)
112            },
113            binding: mapping,
114            id: SoftMeshId::default(),
115            follows_boundary: false,
116            indices,
117            // A direct mesh reads its particles; only a skinned one caches its vertices.
118            vertices: if skinned { vertices } else { Vec::new() },
119            ring_offsets,
120            ring,
121            vertex_elements_offsets,
122            vertex_elements,
123            #[cfg(feature = "dim3")]
124            edges,
125            #[cfg(feature = "dim3")]
126            element_edges,
127            #[cfg(feature = "dim3")]
128            edge_owners,
129            inverted: false,
130            orientation_unreliable: false,
131            oriented: false,
132            collision_enabled: true,
133            collider: ColliderHandle::invalid(),
134            self_contacts,
135            edge_contacts: Vec::new(),
136            vertex_contacts: Vec::new(),
137            overlap_states: Vec::new(),
138            overlap_warm: Vec::new(),
139            volume_contacts: Vec::new(),
140            crossing_sweep_travel: Real::MAX,
141            crossed_partners: Vec::new(),
142            vertex_cache: Vec::new(),
143            topology_version: 0,
144        }
145    }
146
147    /// A wire a body collides through: its segments over the particles, one vertex per particle
148    /// (vertex indices are particle indices, like the boundary mesh). A wire is a curve that
149    /// encloses nothing, so it is never closed and its contacts are always two-sided.
150    #[cfg(feature = "dim3")]
151    pub(crate) fn wire(
152        body: &SoftBody,
153        segments: &[[u32; 2]],
154        self_contacts: bool,
155        collision_enabled: bool,
156    ) -> Self {
157        let indices: Vec<[u32; DIM]> = segments
158            .iter()
159            .map(|s| core::array::from_fn(|k| if k < 2 { s[k] } else { u32::MAX }))
160            .collect();
161        let mut mesh = Self::new(
162            SoftMeshMapping::Direct {
163                particles: (0..body.particles.len() as u32).collect(),
164            },
165            indices,
166            body.particles.iter().map(|p| p.position).collect(),
167            body,
168            self_contacts,
169        );
170        mesh.collision_enabled = collision_enabled;
171        mesh
172    }
173
174    /// A mesh bound to the body's cells: for every vertex, the closest cell and the vertex's
175    /// barycentric coordinates in it. `positions` are the particle positions the cells are in
176    /// when the binding is taken, which the mesh's vertices are expected to match.
177    pub(crate) fn skinned(
178        vertices: &[Vector],
179        indices: &[[u32; DIM]],
180        cells: &[SoftBodyCell],
181        positions: &[Vector],
182        self_contacts: bool,
183        collision_enabled: bool,
184    ) -> Option<Self> {
185        let bindings = bind_to_cells(vertices, indices, cells, positions)?;
186        let (ring_offsets, ring) = surface_rings(vertices.len(), indices);
187        let (vertex_elements_offsets, vertex_elements) =
188            surface_vertex_elements(vertices.len(), indices);
189        #[cfg(feature = "dim3")]
190        let (edges, element_edges, edge_owners) =
191            super::soft_body_builder::surface_edge_table(indices);
192
193        Some(Self {
194            binding: SoftMeshMapping::Skinned { bindings },
195            id: SoftMeshId {
196                cluster: 0,
197                mesh: 0,
198            },
199            follows_boundary: false,
200            closed: surface_is_closed(indices),
201            rest_signed_volume: SoftBody::boundary_volume(indices, |i| vertices[i as usize]),
202            element_cells: Vec::new(),
203            indices: indices.to_vec(),
204            vertices: vertices.to_vec(),
205            ring_offsets,
206            ring,
207            vertex_elements_offsets,
208            vertex_elements,
209            #[cfg(feature = "dim3")]
210            edges,
211            #[cfg(feature = "dim3")]
212            element_edges,
213            #[cfg(feature = "dim3")]
214            edge_owners,
215            inverted: false,
216            orientation_unreliable: false,
217            oriented: false,
218            collision_enabled,
219            collider: ColliderHandle::invalid(),
220            self_contacts,
221            edge_contacts: Vec::new(),
222            vertex_contacts: Vec::new(),
223            overlap_states: Vec::new(),
224            overlap_warm: Vec::new(),
225            volume_contacts: Vec::new(),
226            crossing_sweep_travel: Real::MAX,
227            crossed_partners: Vec::new(),
228            vertex_cache: Vec::new(),
229            topology_version: 0,
230        })
231    }
232
233    /// The vertices of this mesh's collider shape, expressed in the collider's frame (`pose` is
234    /// its world pose: the cluster proxy's pose composed with the collider's pose relative to
235    /// it). A rigid motion of the body moves the frame, not the vertices.
236    pub(crate) fn local_vertices(&self, body: &SoftBody, pose: &Pose) -> Vec<Vector> {
237        (0..self.vertex_count())
238            .map(|i| pose.inverse_transform_point(self.vertex(body, i)))
239            .collect()
240    }
241
242    /// Moves the vertices of this mesh's collider shape to the current positions, in place:
243    /// parry refits the shape's BVH.
244    pub(crate) fn deform_shape(
245        &self,
246        body: &SoftBody,
247        pose: &Pose,
248        shape: &mut dyn parry::shape::Shape,
249    ) {
250        let write = |vertices: &mut [Vector]| {
251            debug_assert_eq!(vertices.len(), self.vertex_count());
252            for (i, v) in vertices.iter_mut().enumerate() {
253                *v = pose.inverse_transform_point(self.vertex(body, i));
254            }
255        };
256        #[cfg(feature = "dim3")]
257        if let Some(trimesh) = shape.as_trimesh_mut() {
258            trimesh.update_vertices(write);
259            return;
260        }
261        // A polyline in either dimension: a 2D surface, or a wire in 3D.
262        if let Some(polyline) = shape.as_polyline_mut() {
263            polyline.update_vertices(write);
264        }
265    }
266
267    /// Moves a skinned mesh to the current particle positions (a direct mesh reads them
268    /// directly and has nothing to update).
269    pub(crate) fn update(&mut self, cells: &[SoftBodyCell], particles: &[super::SoftBodyParticle]) {
270        let SoftMeshMapping::Skinned { bindings } = &self.binding else {
271            return;
272        };
273        for (vertex, binding) in self.vertices.iter_mut().zip(bindings) {
274            let Some(cell) = cells.get(binding.cell as usize) else {
275                continue;
276            };
277            let mut skinned = Vector::ZERO;
278            for (vid, weight) in cell.vertices.iter().zip(&binding.weights) {
279                skinned += particles[*vid as usize].position * *weight;
280            }
281            *vertex = skinned;
282        }
283    }
284
285    /// Updates the inside-out state of a closed mesh from the current vertex positions.
286    pub(crate) fn update_orientation(&mut self, body: &SoftBody) {
287        if !self.closed || self.rest_signed_volume == 0.0 {
288            return;
289        }
290        // With hysteresis: a crumpled body hovering around a zero volume must not flip its
291        // orientation (contact normals) every step.
292        let volume = SoftBody::boundary_volume(&self.indices, |i| self.vertex(body, i as usize));
293        let ratio = volume / self.rest_signed_volume;
294        if ratio < -0.25 {
295            self.inverted = true;
296        } else if ratio > 0.25 {
297            self.inverted = false;
298        }
299        // Inside the band the loop is likely self-crossed (an O squeezed into an 8): part of the
300        // winding is mirrored, so no global orientation is trustworthy.
301        self.orientation_unreliable = ratio.abs() <= 0.25;
302    }
303
304    /// A counter bumped whenever this mesh's topology changes: a renderer keying its mesh on it
305    /// rebuilds only what changed.
306    pub fn topology_version(&self) -> u32 {
307        self.topology_version
308    }
309
310    /// Rebuilds the tables derived from the elements after a topology change (a tear).
311    pub(crate) fn rebuild_tables(&mut self, body: &SoftBody) {
312        self.topology_version = self.topology_version.wrapping_add(1);
313        let num_vertices = self.vertex_count();
314        let (offsets, ring) = surface_rings(num_vertices, &self.indices);
315        self.ring_offsets = offsets;
316        self.ring = ring;
317        let (offsets, elements) = surface_vertex_elements(num_vertices, &self.indices);
318        self.vertex_elements_offsets = offsets;
319        self.vertex_elements = elements;
320        self.closed = surface_is_closed(&self.indices);
321        if !self.is_skinned() && !self.follows_boundary {
322            self.element_cells = surface_element_cells(&self.indices, &body.cells);
323        }
324        #[cfg(feature = "dim3")]
325        {
326            let (edges, element_edges, owners) =
327                super::soft_body_builder::surface_edge_table(&self.indices);
328            self.edges = edges;
329            self.element_edges = element_edges;
330            self.edge_owners = owners;
331        }
332    }
333
334    /// Follows the body through a topology change (tear, cut, cluster removal): a boundary mesh is
335    /// re-derived, a direct mesh follows its particles ([`follow_insertions`], [`follow_splits`]),
336    /// a skinned mesh follows its cells and comes apart along the cracks ([`follow_pieces`]).
337    pub(crate) fn remap_topology(&mut self, body: &SoftBody, remap: &SoftTopologyRemap) {
338        match &mut self.binding {
339            SoftMeshMapping::Skinned { bindings } => {
340                if !remap.cells.is_empty() {
341                    for binding in bindings.iter_mut() {
342                        binding.cell = remap
343                            .cells
344                            .get(binding.cell as usize)
345                            .copied()
346                            .unwrap_or(u32::MAX);
347                    }
348                }
349                let dangling: Vec<usize> = bindings
350                    .iter()
351                    .enumerate()
352                    .filter(|(_, binding)| body.cells.get(binding.cell as usize).is_none())
353                    .map(|(i, _)| i)
354                    .collect();
355                for vertex in dangling {
356                    let position = self.vertices[vertex];
357                    if let Some(binding) = closest_cell_binding(body, position, |_| true) {
358                        bindings[vertex] = binding;
359                    }
360                }
361                if !remap.split.is_empty() {
362                    follow_pieces(bindings, &mut self.vertices, &mut self.indices, body);
363                }
364            }
365            SoftMeshMapping::Direct { particles } if self.follows_boundary => {
366                self.indices = body.boundary.clone();
367                *particles = (0..body.particles.len() as u32).collect();
368                self.rest_signed_volume = SoftBody::boundary_volume(&self.indices, |i| {
369                    body.particles[i as usize].rest_position
370                });
371            }
372            SoftMeshMapping::Direct { particles } => {
373                if !remap.particles.is_empty() {
374                    // The vertices whose particle died go with it, and so do the elements they
375                    // were in; the survivors are compacted.
376                    let mut vertex_remap = vec![u32::MAX; particles.len()];
377                    let mut kept = Vec::with_capacity(particles.len());
378                    for (vertex, particle) in particles.iter().enumerate() {
379                        let new = remap.particles.get(*particle as usize).copied();
380                        if let Some(new) = new.filter(|p| *p != u32::MAX) {
381                            vertex_remap[vertex] = kept.len() as u32;
382                            kept.push(new);
383                        }
384                    }
385                    *particles = kept;
386                    // A wire element pads its unused slot with `u32::MAX`: only the used
387                    // vertices are looked up.
388                    self.indices.retain(|element| {
389                        element_vertices(element)
390                            .iter()
391                            .all(|v| vertex_remap[*v as usize] != u32::MAX)
392                    });
393                    for element in &mut self.indices {
394                        for v in element_vertices_mut(element) {
395                            *v = vertex_remap[*v as usize];
396                        }
397                    }
398                }
399                if !remap.inserted.is_empty() {
400                    follow_insertions(particles, &mut self.indices, remap.inserted);
401                }
402                if !remap.split.is_empty() {
403                    follow_splits(particles, &mut self.indices, body, remap.split);
404                }
405            }
406        }
407    }
408
409    /// A copy of this mesh over the vertices `keep` accepts: other vertices and their elements
410    /// dropped, tables rebuilt, contact state cleared, no collider or id. `None` when no element
411    /// remains; how a mesh follows a cluster split.
412    pub(crate) fn restricted_to(
413        &self,
414        body: &SoftBody,
415        keep: impl Fn(u32) -> bool,
416    ) -> Option<Self> {
417        let n = self.vertex_count();
418        let mut vertex_remap = vec![u32::MAX; n];
419        let mut kept = 0u32;
420        for v in 0..n {
421            if keep(v as u32) {
422                vertex_remap[v] = kept;
423                kept += 1;
424            }
425        }
426        let indices: Vec<[u32; DIM]> = self
427            .indices
428            .iter()
429            .filter(|element| {
430                element_vertices(element)
431                    .iter()
432                    .all(|v| vertex_remap[*v as usize] != u32::MAX)
433            })
434            .map(|element| {
435                let mut element = *element;
436                for v in element_vertices_mut(&mut element) {
437                    *v = vertex_remap[*v as usize];
438                }
439                element
440            })
441            .collect();
442        if indices.is_empty() {
443            return None;
444        }
445        let mut mesh = self.clone();
446        mesh.indices = indices;
447        let mut keep = (0..n).map(|v| vertex_remap[v] != u32::MAX);
448        match &mut mesh.binding {
449            SoftMeshMapping::Direct { particles } => particles.retain(|_| keep.next().unwrap()),
450            SoftMeshMapping::Skinned { bindings } => {
451                let mut keep_vertices = keep.clone();
452                bindings.retain(|_| keep.next().unwrap());
453                mesh.vertices.retain(|_| keep_vertices.next().unwrap());
454            }
455        }
456        mesh.collider = ColliderHandle::invalid();
457        mesh.id = SoftMeshId::default();
458        mesh.clear_contacts();
459        mesh.overlap_states.clear();
460        mesh.overlap_warm.clear();
461        mesh.volume_contacts.clear();
462        mesh.rebuild_tables(body);
463        mesh.rest_signed_volume = if mesh.is_wire() {
464            0.0
465        } else {
466            SoftBody::boundary_volume(&mesh.indices, |i| mesh.rest_vertex(body, i as usize))
467        };
468        mesh.update_orientation(body);
469        Some(mesh)
470    }
471
472    /// Clears the contact state kept across steps (a topology change invalidated its ids).
473    pub(crate) fn clear_contacts(&mut self) {
474        self.edge_contacts.clear();
475        self.vertex_contacts.clear();
476        self.crossing_sweep_travel = Real::MAX;
477        self.crossed_partners.clear();
478    }
479}
480
481/// Follows the particles a cut inserted into segments (`inserted`: `[a, b, p, q]`, segment `(a, b)`
482/// became `(a, p)` and `(q, b)`) in a direct mesh (`particles`: the particle of each vertex): a
483/// mesh segment over `a` and `b` is split the same way, with a new vertex per inserted particle.
484fn follow_insertions(
485    particles: &mut Vec<u32>,
486    indices: &mut Vec<[u32; DIM]>,
487    inserted: &[[u32; 4]],
488) {
489    for &[a, b, p, q] in inserted {
490        for i in 0..indices.len() {
491            let element = indices[i];
492            if element_vertices(&element).len() != 2 {
493                continue;
494            }
495            let ends = [
496                particles[element[0] as usize],
497                particles[element[1] as usize],
498            ];
499            let (first, second) = if ends == [a, b] {
500                (p, q)
501            } else if ends == [b, a] {
502                (q, p)
503            } else {
504                continue;
505            };
506            particles.push(first);
507            particles.push(second);
508            let n = particles.len() as u32;
509            indices[i][1] = n - 2;
510            let mut rest = element;
511            rest[0] = n - 1;
512            indices.push(rest);
513        }
514    }
515}
516
517/// Separates a skinned mesh along the cracks between the body's pieces: in an element whose
518/// vertices ride cells of different pieces, each vertex outside the majority piece is replaced by
519/// a copy bound to the closest cell of that piece (one shared copy per vertex and piece).
520fn follow_pieces(
521    bindings: &mut Vec<SoftMeshCellBinding>,
522    vertices: &mut Vec<Vector>,
523    indices: &mut [[u32; DIM]],
524    body: &SoftBody,
525) {
526    let pieces = body.connected_pieces();
527    if pieces.len() < 2 {
528        return;
529    }
530    let mut particle_piece = vec![u32::MAX; body.particles.len()];
531    for (k, piece) in pieces.iter().enumerate() {
532        for &p in piece {
533            particle_piece[p as usize] = k as u32;
534        }
535    }
536    let cell_piece = |cell: u32| {
537        body.cells
538            .get(cell as usize)
539            .map_or(u32::MAX, |c| particle_piece[c.vertices[0] as usize])
540    };
541    let mut copies: HashMap<(u32, u32), u32> = HashMap::default();
542    for element in indices.iter_mut() {
543        let used = element_vertices(element).len();
544        let piece_of: Vec<u32> = element[..used]
545            .iter()
546            .map(|&w| {
547                bindings
548                    .get(w as usize)
549                    .map_or(u32::MAX, |b| cell_piece(b.cell))
550            })
551            .collect();
552        // The most frequent piece, ties going to the one met first.
553        let mut majority = (u32::MAX, 0);
554        for &piece in piece_of.iter().filter(|&&piece| piece != u32::MAX) {
555            let count = piece_of.iter().filter(|&&other| other == piece).count();
556            if count > majority.1 {
557                majority = (piece, count);
558            }
559        }
560        let majority = majority.0;
561        if piece_of
562            .iter()
563            .all(|&piece| piece == majority || piece == u32::MAX)
564        {
565            continue;
566        }
567        for k in 0..used {
568            if piece_of[k] == majority || piece_of[k] == u32::MAX {
569                continue;
570            }
571            let w = element[k];
572            let copy = match copies.get(&(w, majority)) {
573                Some(&copy) => copy,
574                None => {
575                    let position = vertices[w as usize];
576                    let Some(binding) =
577                        closest_cell_binding(body, position, |cell| cell_piece(cell) == majority)
578                    else {
579                        continue;
580                    };
581                    bindings.push(binding);
582                    vertices.push(position);
583                    let copy = vertices.len() as u32 - 1;
584                    let _ = copies.insert((w, majority), copy);
585                    copy
586                }
587            };
588            element[k] = copy;
589        }
590    }
591}
592
593/// The largest number of particle combinations [`follow_splits`] tries for one element.
594const MAX_SPLIT_COMBINATIONS: usize = 64;
595
596/// Follows particle splits (`split`: `(copy, source)` in creation order) in a direct mesh
597/// (`particles`: the particle of each vertex): an element whose particles no longer share a
598/// measure element of `body` switches to the combination of copies that does, adding vertices.
599fn follow_splits(
600    particles: &mut Vec<u32>,
601    indices: &mut [[u32; DIM]],
602    body: &SoftBody,
603    split: &[(u32, u32)],
604) {
605    // Every split particle's family: the particle it was first split from, then its copies.
606    let mut root: HashMap<u32, u32> = HashMap::default();
607    let mut families: HashMap<u32, Vec<u32>> = HashMap::default();
608    for &(copy, source) in split {
609        let first = root.get(&source).copied().unwrap_or(source);
610        root.insert(copy, first);
611        families
612            .entry(first)
613            .or_insert_with(|| vec![first])
614            .push(copy);
615    }
616    let mut vertex_of: HashMap<u32, u32> = HashMap::default();
617    for (vertex, &particle) in particles.iter().enumerate() {
618        vertex_of.entry(particle).or_insert(vertex as u32);
619    }
620    for element in indices.iter_mut() {
621        let used = element_vertices(element).len();
622        let current: Vec<u32> = element[..used]
623            .iter()
624            .map(|&w| particles[w as usize])
625            .collect();
626        let candidates: Vec<&[u32]> = current
627            .iter()
628            .map(|p| {
629                let first = root.get(p).copied().unwrap_or(*p);
630                families
631                    .get(&first)
632                    .map_or(core::slice::from_ref(p), Vec::as_slice)
633            })
634            .collect();
635        let combinations: usize = candidates.iter().map(|c| c.len()).product();
636        if combinations == 1
637            || combinations > MAX_SPLIT_COMBINATIONS
638            || body.measure_element_holds(&current)
639        {
640            continue;
641        }
642        for index in 0..combinations {
643            let mut digits = index;
644            let combination: Vec<u32> = candidates
645                .iter()
646                .map(|c| {
647                    let p = c[digits % c.len()];
648                    digits /= c.len();
649                    p
650                })
651                .collect();
652            if body.measure_element_holds(&combination) {
653                for (k, &p) in combination.iter().enumerate() {
654                    if p != current[k] {
655                        element[k] = *vertex_of.entry(p).or_insert_with(|| {
656                            particles.push(p);
657                            particles.len() as u32 - 1
658                        });
659                    }
660                }
661                break;
662            }
663        }
664    }
665}