diff --git a/examples3d/voxels3.rs b/examples3d/voxels3.rs index a91aaf5..bc0bf6e 100644 --- a/examples3d/voxels3.rs +++ b/examples3d/voxels3.rs @@ -14,11 +14,6 @@ pub fn init_world(testbed: &mut Testbed) { let settings = testbed.example_settings_mut(); - let geometry_mode = settings.get_or_set_string( - "Voxels mode", - 0, - vec!["PseudoCube".to_string(), "PseudoBall".to_string()], - ); let falling_objects = settings.get_or_set_string( "Falling objects", 5, // Defaults to Mixed. @@ -40,12 +35,6 @@ pub fn init_world(testbed: &mut Testbed) { // settings.get_or_set_bool("Load .obj", false); let load_obj = false; - let primitive_geometry = if geometry_mode == 0 { - VoxelPrimitiveGeometry::PseudoCube - } else { - VoxelPrimitiveGeometry::PseudoBall - }; - /* * World */ @@ -112,13 +101,8 @@ pub fn init_world(testbed: &mut Testbed) { .map(|idx| [idx[0] as u32, idx[1] as u32, idx[2] as u32]) .collect(); - let decomposed_shape = SharedShape::voxelized_mesh( - primitive_geometry, - &vertices, - &indices, - 0.1, - FillMode::default(), - ); + let decomposed_shape = + SharedShape::voxelized_mesh(&vertices, &indices, 0.1, FillMode::default()); shapes.push(decomposed_shape); @@ -160,8 +144,7 @@ pub fn init_world(testbed: &mut Testbed) { } } } - let collider = - ColliderBuilder::voxels_from_points(primitive_geometry, voxel_size, &samples).build(); + let collider = ColliderBuilder::voxels_from_points(voxel_size, &samples).build(); let floor_aabb = collider.compute_aabb(); colliders.insert(collider); diff --git a/src/dynamics/rigid_body_components.rs b/src/dynamics/rigid_body_components.rs index 36bf799..74958d8 100644 --- a/src/dynamics/rigid_body_components.rs +++ b/src/dynamics/rigid_body_components.rs @@ -738,7 +738,6 @@ impl RigidBodyVelocity { impl std::ops::Mul for RigidBodyVelocity { type Output = Self; - #[must_use] fn mul(self, rhs: Real) -> Self { RigidBodyVelocity { linvel: self.linvel * rhs, @@ -750,7 +749,6 @@ impl std::ops::Mul for RigidBodyVelocity { impl std::ops::Add for RigidBodyVelocity { type Output = Self; - #[must_use] fn add(self, rhs: Self) -> Self { RigidBodyVelocity { linvel: self.linvel + rhs.linvel, @@ -769,7 +767,6 @@ impl std::ops::AddAssign for RigidBodyVelocity { impl std::ops::Sub for RigidBodyVelocity { type Output = Self; - #[must_use] fn sub(self, rhs: Self) -> Self { RigidBodyVelocity { linvel: self.linvel - rhs.linvel, diff --git a/src/geometry/broad_phase_qbvh.rs b/src/geometry/broad_phase_qbvh.rs index 5fd6bcf..d32ea3d 100644 --- a/src/geometry/broad_phase_qbvh.rs +++ b/src/geometry/broad_phase_qbvh.rs @@ -1,4 +1,5 @@ -use crate::geometry::{BroadPhasePairEvent, ColliderHandle, ColliderPair, ColliderSet}; +use crate::dynamics::RigidBodySet; +use crate::geometry::{BroadPhase, BroadPhasePairEvent, ColliderHandle, ColliderPair, ColliderSet}; use parry::math::Real; use parry::partitioning::Qbvh; use parry::partitioning::QbvhUpdateWorkspace; @@ -27,12 +28,15 @@ impl BroadPhaseQbvh { workspace: QbvhUpdateWorkspace::default(), } } +} - #[allow(dead_code)] // This broad-phase is just experimental right now. - pub fn update( +impl BroadPhase for BroadPhaseQbvh { + fn update( &mut self, + dt: Real, prediction_distance: Real, - colliders: &ColliderSet, + colliders: &mut ColliderSet, + bodies: &RigidBodySet, modified_colliders: &[ColliderHandle], removed_colliders: &[ColliderHandle], events: &mut Vec, @@ -46,7 +50,9 @@ impl BroadPhaseQbvh { // Visitor to find collision pairs. let mut visitor = BoundingVolumeIntersectionsSimultaneousVisitor::new( |co1: &ColliderHandle, co2: &ColliderHandle| { - events.push(BroadPhasePairEvent::AddPair(ColliderPair::new(*co1, *co2))); + if *co1 != *co2 { + events.push(BroadPhasePairEvent::AddPair(ColliderPair::new(*co1, *co2))); + } true }, ); @@ -77,6 +83,8 @@ impl BroadPhaseQbvh { let _ = self.qbvh.refit(margin, &mut self.workspace, |handle| { colliders[*handle].compute_collision_aabb(prediction_distance / 2.0) }); + // self.qbvh + // .traverse_bvtt_with_stack(&self.qbvh, &mut visitor, &mut self.stack); self.qbvh .traverse_modified_bvtt_with_stack(&self.qbvh, &mut visitor, &mut self.stack); self.qbvh.rebalance(margin, &mut self.workspace); diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index 87a9602..8165cee 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -12,7 +12,7 @@ use crate::pipeline::{ActiveEvents, ActiveHooks}; use crate::prelude::ColliderEnabled; use na::Unit; use parry::bounding_volume::{Aabb, BoundingVolume}; -use parry::shape::{Shape, TriMeshBuilderError, TriMeshFlags, VoxelPrimitiveGeometry}; +use parry::shape::{Shape, TriMeshBuilderError, TriMeshFlags}; use parry::transformation::voxelization::FillMode; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] @@ -580,45 +580,28 @@ impl ColliderBuilder { /// /// For initializing a voxels shape from points in space, see [`Self::voxels_from_points`]. /// For initializing a voxels shape from a mesh to voxelize, see [`Self::voxelized_mesh`]. - pub fn voxels( - primitive_geometry: VoxelPrimitiveGeometry, - voxel_size: Vector, - voxels: &[Point], - ) -> Self { - Self::new(SharedShape::voxels(primitive_geometry, voxel_size, voxels)) + pub fn voxels(voxel_size: Vector, voxels: &[Point]) -> Self { + Self::new(SharedShape::voxels(voxel_size, voxels)) } /// Initializes a collider made of voxels. /// /// Each voxel has the size `voxel_size` and contains at least one point from `centers`. /// The `primitive_geometry` controls the behavior of collision detection at voxels boundaries. - pub fn voxels_from_points( - primitive_geometry: VoxelPrimitiveGeometry, - voxel_size: Vector, - points: &[Point], - ) -> Self { - Self::new(SharedShape::voxels_from_points( - primitive_geometry, - voxel_size, - points, - )) + pub fn voxels_from_points(voxel_size: Vector, points: &[Point]) -> Self { + Self::new(SharedShape::voxels_from_points(voxel_size, points)) } /// Initializes a voxels obtained from the decomposition of the given trimesh (in 3D) /// or polyline (in 2D) into voxelized convex parts. pub fn voxelized_mesh( - primitive_geometry: VoxelPrimitiveGeometry, vertices: &[Point], indices: &[[u32; DIM]], voxel_size: Real, fill_mode: FillMode, ) -> Self { Self::new(SharedShape::voxelized_mesh( - primitive_geometry, - vertices, - indices, - voxel_size, - fill_mode, + vertices, indices, voxel_size, fill_mode, )) } diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index 4fb9584..c1bd197 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -17,7 +17,7 @@ pub use self::narrow_phase::NarrowPhase; pub use parry::bounding_volume::BoundingVolume; pub use parry::query::{PointQuery, PointQueryWithLocation, RayCast, TrackedContact}; -pub use parry::shape::{SharedShape, VoxelPrimitiveGeometry, VoxelState, VoxelType, Voxels}; +pub use parry::shape::{SharedShape, VoxelState, VoxelType, Voxels}; use crate::math::{Real, Vector}; diff --git a/src_testbed/graphics.rs b/src_testbed/graphics.rs index 4e27b2b..52f1f37 100644 --- a/src_testbed/graphics.rs +++ b/src_testbed/graphics.rs @@ -1,6 +1,6 @@ use bevy::prelude::*; -use na::{point, Point3}; +use na::{point, Point3, Point4}; use crate::objects::node::EntityWithGraphics; use rapier::dynamics::{RigidBodyHandle, RigidBodySet}; @@ -27,8 +27,60 @@ pub type BevyMaterialComponent = MeshMaterial2d; #[cfg(feature = "dim3")] pub type BevyMaterialComponent = MeshMaterial3d; -pub type InstancedMaterials = HashMap, Handle>; -pub const SELECTED_OBJECT_MATERIAL_KEY: Point3 = point![42, 42, 42]; +#[derive(Clone, Default)] +pub struct InstancedMaterials { + cache: HashMap, Handle>, +} + +impl InstancedMaterials { + pub fn insert( + &mut self, + materials: &mut Assets, + color: Point3, + opacity: f32, + ) -> Handle { + let key = color + .coords + .push(opacity) + .map(|c| (c * 255.0) as usize) + .into(); + let bevy_color = Color::from(Srgba::new(color.x, color.y, color.z, opacity)); + + #[cfg(feature = "dim2")] + let material = bevy_sprite::ColorMaterial { + color: bevy_color, + texture: None, + ..default() + }; + #[cfg(feature = "dim3")] + let material = StandardMaterial { + metallic: 0.5, + perceptual_roughness: 0.5, + double_sided: true, // TODO: this doesn't do anything? + ..StandardMaterial::from(bevy_color) + }; + + self.cache + .entry(key) + .or_insert_with(|| materials.add(material)) + .clone_weak() + } + + pub fn get(&self, color: &Point3, opacity: f32) -> Option> { + let key = color + .coords + .push(opacity) + .map(|c| (c * 255.0) as usize) + .into(); + self.cache.get(&key).map(|h| h.clone_weak()) + } + + pub fn clear(&mut self) { + self.cache.clear(); + } +} + +pub const SELECTED_OBJECT_COLOR: Point3 = point![1.0, 0.0, 0.0]; pub struct GraphicsManager { rand: Pcg32, @@ -52,13 +104,16 @@ impl GraphicsManager { ground_color: point![0.5, 0.5, 0.5], b2wireframe: HashMap::new(), prefab_meshes: HashMap::new(), - instanced_materials: HashMap::new(), + instanced_materials: Default::default(), gfx_shift: Vector::zeros(), } } pub fn selection_material(&self) -> Handle { - self.instanced_materials[&SELECTED_OBJECT_MATERIAL_KEY].clone_weak() + self.instanced_materials + .get(&SELECTED_OBJECT_COLOR, 1.0) + .unwrap() + .clone_weak() } pub fn clear(&mut self, commands: &mut Commands) { @@ -110,6 +165,7 @@ impl GraphicsManager { pub fn set_body_color( &mut self, materials: &mut Assets, + material_handles: &mut Query<&mut BevyMaterialComponent>, b: RigidBodyHandle, color: [f32; 3], ) { @@ -117,7 +173,11 @@ impl GraphicsManager { if let Some(ns) = self.b2sn.get_mut(&b) { for n in ns.iter_mut() { - n.set_color(materials, color.into()) + n.set_color(materials, &mut self.instanced_materials, color.into()); + + if let Ok(mut mat) = material_handles.get_mut(n.entity) { + mat.0 = n.material.clone_weak(); + } } } } @@ -185,12 +245,7 @@ impl GraphicsManager { color } - fn alloc_color( - &mut self, - materials: &mut Assets, - handle: RigidBodyHandle, - is_fixed: bool, - ) -> Point3 { + fn alloc_color(&mut self, handle: RigidBodyHandle, is_fixed: bool) -> Point3 { let mut color = self.ground_color; if !is_fixed { @@ -200,7 +255,7 @@ impl GraphicsManager { } } - self.set_body_color(materials, handle, color.into()); + self.b2color.insert(handle, color.into()); color } @@ -221,7 +276,7 @@ impl GraphicsManager { .b2color .get(&handle) .cloned() - .unwrap_or_else(|| self.alloc_color(materials, handle, !body.is_dynamic())); + .unwrap_or_else(|| self.alloc_color(handle, !body.is_dynamic())); let _ = self.add_body_colliders_with_color( commands, meshes, materials, components, handle, bodies, colliders, color, diff --git a/src_testbed/harness/mod.rs b/src_testbed/harness/mod.rs index 734625b..fb7e32c 100644 --- a/src_testbed/harness/mod.rs +++ b/src_testbed/harness/mod.rs @@ -179,7 +179,7 @@ impl Harness { self.physics.hooks = Box::new(hooks); self.physics.islands = IslandManager::new(); - self.physics.broad_phase = DefaultBroadPhase::new(); + self.physics.broad_phase = DefaultBroadPhase::default(); self.physics.narrow_phase = NarrowPhase::new(); self.state.timestep_id = 0; self.state.time = 0.0; diff --git a/src_testbed/objects/node.rs b/src_testbed/objects/node.rs index 28c959b..1d7f6c5 100644 --- a/src_testbed/objects/node.rs +++ b/src_testbed/objects/node.rs @@ -14,7 +14,7 @@ use rapier::geometry::{ColliderHandle, ColliderSet, Shape, ShapeType}; use rapier::geometry::{Cone, Cylinder}; use rapier::math::{Isometry, Real, Vector}; -use crate::graphics::{BevyMaterial, InstancedMaterials, SELECTED_OBJECT_MATERIAL_KEY}; +use crate::graphics::{BevyMaterial, InstancedMaterials, SELECTED_OBJECT_COLOR}; #[cfg(feature = "dim2")] use { na::{vector, Point2, Vector2}, @@ -37,28 +37,7 @@ impl EntityWithGraphics { materials: &mut Assets, instanced_materials: &mut InstancedMaterials, ) { - if instanced_materials.contains_key(&SELECTED_OBJECT_MATERIAL_KEY) { - return; // Already added. - } - - #[cfg(feature = "dim2")] - let selection_material = bevy_sprite::ColorMaterial { - color: Color::from(Srgba::rgb(1.0, 0.0, 0.0)), - texture: None, - ..default() - }; - #[cfg(feature = "dim3")] - let selection_material = StandardMaterial { - metallic: 0.5, - perceptual_roughness: 0.5, - double_sided: true, // TODO: this doesn't do anything? - ..StandardMaterial::from(Color::from(Srgba::rgb(1.0, 0.0, 0.0))) - }; - - instanced_materials.insert( - SELECTED_OBJECT_MATERIAL_KEY, - materials.add(selection_material), - ); + instanced_materials.insert(materials, SELECTED_OBJECT_COLOR, 1.0); } pub fn spawn( @@ -84,8 +63,7 @@ impl EntityWithGraphics { .cloned() .or_else(|| generate_collider_mesh(shape).map(|m| meshes.add(m))); - let opacity = 1.0; - let bevy_color = Color::from(Srgba::new(color.x, color.y, color.z, opacity)); + let opacity = if sensor { 0.25 } else { 1.0 }; let shape_pos = collider_pos * delta; let mut transform = Transform::from_scale(scale); transform.translation.x = shape_pos.translation.vector.x as f32; @@ -108,23 +86,7 @@ impl EntityWithGraphics { transform.rotation = Quat::from_rotation_z(shape_pos.rotation.angle() as f32); } - #[cfg(feature = "dim2")] - let material = bevy_sprite::ColorMaterial { - color: bevy_color, - texture: None, - ..default() - }; - #[cfg(feature = "dim3")] - let material = StandardMaterial { - metallic: 0.5, - perceptual_roughness: 0.5, - double_sided: true, // TODO: this doesn't do anything? - ..StandardMaterial::from(bevy_color) - }; - let material_handle = instanced_materials - .entry(color.coords.map(|c| (c * 255.0) as usize).into()) - .or_insert_with(|| materials.add(material)); - let material_weak_handle = material_handle.clone_weak(); + let material_handle = instanced_materials.insert(materials, color, opacity); if let Some(mesh) = mesh { #[cfg(feature = "dim2")] @@ -154,7 +116,7 @@ impl EntityWithGraphics { base_color: color, collider, delta, - material: material_weak_handle, + material: material_handle, opacity, } } @@ -164,18 +126,13 @@ impl EntityWithGraphics { commands.entity(self.entity).despawn(); } - pub fn set_color(&mut self, materials: &mut Assets, color: Point3) { - if let Some(material) = materials.get_mut(&self.material) { - #[cfg(feature = "dim2")] - { - material.color = Color::from(Srgba::new(color.x, color.y, color.z, self.opacity)); - } - #[cfg(feature = "dim3")] - { - material.base_color = - Color::from(Srgba::new(color.x, color.y, color.z, self.opacity)); - } - } + pub fn set_color( + &mut self, + materials: &mut Assets, + instanced_materials: &mut InstancedMaterials, + color: Point3, + ) { + self.material = instanced_materials.insert(materials, color, self.opacity); self.color = color; self.base_color = color; } diff --git a/src_testbed/physics/mod.rs b/src_testbed/physics/mod.rs index b08d7f5..907a7c9 100644 --- a/src_testbed/physics/mod.rs +++ b/src_testbed/physics/mod.rs @@ -113,7 +113,7 @@ impl PhysicsState { pub fn new() -> Self { Self { islands: IslandManager::new(), - broad_phase: DefaultBroadPhase::new(), + broad_phase: DefaultBroadPhase::default(), narrow_phase: NarrowPhase::new(), bodies: RigidBodySet::new(), colliders: ColliderSet::new(), diff --git a/src_testbed/testbed.rs b/src_testbed/testbed.rs index a321d6a..321510a 100644 --- a/src_testbed/testbed.rs +++ b/src_testbed/testbed.rs @@ -178,11 +178,12 @@ struct OtherBackends { } struct Plugins(Vec>); -pub struct TestbedGraphics<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h> { +pub struct TestbedGraphics<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k> { graphics: &'a mut GraphicsManager, commands: &'a mut Commands<'d, 'e>, meshes: &'a mut Assets, materials: &'a mut Assets, + material_handles: &'a mut Query<'i, 'j, &'k mut BevyMaterialComponent>, components: &'a mut Query<'b, 'f, &'c mut Transform>, #[allow(dead_code)] // Dead in 2D but not in 3D. camera_transform: GlobalTransform, @@ -192,8 +193,8 @@ pub struct TestbedGraphics<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h> { mouse: &'a SceneMouse, } -pub struct Testbed<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h> { - graphics: Option>, +pub struct Testbed<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k> { + graphics: Option>, harness: &'a mut Harness, state: &'a mut TestbedState, #[cfg(feature = "other-backends")] @@ -508,9 +509,10 @@ impl TestbedApp { } } -impl<'g, 'h> TestbedGraphics<'_, '_, '_, '_, '_, '_, 'g, 'h> { +impl<'g, 'h> TestbedGraphics<'_, '_, '_, '_, '_, '_, 'g, 'h, '_, '_, '_> { pub fn set_body_color(&mut self, body: RigidBodyHandle, color: [f32; 3]) { - self.graphics.set_body_color(self.materials, body, color); + self.graphics + .set_body_color(self.materials, self.material_handles, body, color); } pub fn ui_context_mut(&mut self) -> &mut EguiContexts<'g, 'h> { @@ -585,7 +587,7 @@ impl<'g, 'h> TestbedGraphics<'_, '_, '_, '_, '_, '_, 'g, 'h> { } } -impl Testbed<'_, '_, '_, '_, '_, '_, '_, '_> { +impl Testbed<'_, '_, '_, '_, '_, '_, '_, '_, '_, '_, '_> { pub fn set_number_of_steps_per_frame(&mut self, nsteps: usize) { self.state.nsteps = nsteps } @@ -1131,6 +1133,7 @@ fn update_testbed( commands: &mut commands, meshes: &mut *meshes, materials: &mut *materials, + material_handles: &mut material_handles, components: &mut gfx_components, camera_transform: *cameras.single().1, camera: &mut cameras.single_mut().2, @@ -1246,6 +1249,7 @@ fn update_testbed( commands: &mut commands, meshes: &mut *meshes, materials: &mut *materials, + material_handles: &mut material_handles, components: &mut gfx_components, camera_transform: *cameras.single().1, camera: &mut cameras.single_mut().2, @@ -1421,6 +1425,7 @@ fn update_testbed( commands: &mut commands, meshes: &mut *meshes, materials: &mut *materials, + material_handles: &mut material_handles, components: &mut gfx_components, camera_transform: *cameras.single().1, camera: &mut cameras.single_mut().2,