hoomd_bevy/representation/
surface_mesh.rs

1// Copyright (c) 2024-2026 The Regents of the University of Michigan.
2// Part of hoomd-rs, released under the BSD 3-Clause License.
3
4//! A 3D mesh
5//!
6//! The [`SurfaceMesh`] representation places a bevy `Mesh3d` at each site.
7
8use bevy::prelude::*;
9use itertools::{
10    EitherOrBoth::{Both, Left, Right},
11    Itertools,
12};
13use std::marker::PhantomData;
14
15/// Represent each entity with a triangle mesh in 3D.
16///
17/// Each entity is an instanced copy of the given mesh. Provide the position and
18/// orientation of each mesh to [`sync`](Self::sync).
19///
20/// All triangle meshes of the same type must have the same material. To display
21/// meshes with different materials, call `setup` and `sync` for multiple types of
22/// triangle meshes with different marker types.
23///
24/// To use:
25/// * Add [`setup`](Self::setup) to the `Startup` schedule.
26/// * Call [`sync`](Self::sync) in an `Update` schedule that runs after `AdvanceSet`.
27#[derive(Component)]
28pub struct SurfaceMesh<T> {
29    /// Mark the type of the disk.
30    marker: PhantomData<T>,
31}
32
33/// Assets that represent a 3D mesh in the scene.
34#[derive(Resource)]
35pub struct Representation<T> {
36    /// The mesh.
37    mesh: Handle<Mesh>,
38    /// The material.
39    material: Handle<StandardMaterial>,
40    /// Mark the type of the triangle mesh assets.
41    marker: PhantomData<T>,
42}
43
44impl<T> Representation<T> {
45    /// Get the material
46    #[must_use]
47    pub fn material(&self) -> &Handle<StandardMaterial> {
48        &self.material
49    }
50}
51
52impl<T: Send + Sync + 'static> SurfaceMesh<T> {
53    /// Create assets to render instanced triangle meshes.
54    pub fn setup(
55        mesh_material: In<(Mesh, StandardMaterial)>,
56        mut commands: Commands,
57        mut meshes: ResMut<Assets<Mesh>>,
58        mut materials: ResMut<Assets<StandardMaterial>>,
59    ) {
60        let (mesh, material) = mesh_material.0;
61        let mesh = meshes.add(mesh);
62        let material = materials.add(material.clone());
63
64        commands.insert_resource(Representation::<T> {
65            mesh,
66            material,
67            marker: PhantomData,
68        });
69    }
70
71    /// Copy the current positions of simulation particles to bevy entities.
72    pub fn sync<I>(
73        commands: &mut Commands,
74        triangle_mesh_representation: Res<Representation<T>>,
75        query: Query<(Entity, &mut Transform), With<Self>>,
76        triangle_meshes: I,
77    ) where
78        I: IntoIterator<Item = (Vec3, Quat)>,
79    {
80        for item in &mut query.into_iter().zip_longest(triangle_meshes) {
81            match item {
82                Both((_, mut transform), (position, rotation)) => {
83                    transform.translation = position;
84                    transform.rotation = rotation;
85                }
86                Left((entity, _)) => commands.entity(entity).despawn(),
87                Right((position, rotation)) => {
88                    commands.spawn((
89                        Mesh3d(triangle_mesh_representation.mesh.clone()),
90                        MeshMaterial3d(triangle_mesh_representation.material.clone()),
91                        Transform::from_translation(position).with_rotation(rotation),
92                        Self {
93                            marker: PhantomData,
94                        },
95                    ));
96                }
97            }
98        }
99    }
100}