feat: switch to the new Bvh from parry for the broad-phase (#853)
* feat: switch to the new Bvh from parry for the broad-phase * chore: cargo fmt + update testbed * chore: remove the multi-grid SAP broad-phase * fix soft-ccd handling in broad-phase * Fix contact cleanup in broad-phase after collider removal * chore: clippy fixes * fix CCD regression * chore: update changelog * fix build with the parallel feature enabled * chore: remove the now useless broad-phase proxy index from colliders * fix tests
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use na::{point, Point3, Point4};
|
||||
use na::{Point3, Point4, point};
|
||||
|
||||
use crate::objects::node::EntityWithGraphics;
|
||||
use rapier::dynamics::{RigidBodyHandle, RigidBodySet};
|
||||
@@ -140,11 +140,11 @@ impl GraphicsManager {
|
||||
let body = body.unwrap_or(RigidBodyHandle::invalid());
|
||||
if let Some(sns) = self.b2sn.get_mut(&body) {
|
||||
sns.retain(|sn| {
|
||||
if let Some(sn_c) = sn.collider {
|
||||
if sn_c == collider {
|
||||
commands.entity(sn.entity).despawn();
|
||||
return false;
|
||||
}
|
||||
if let Some(sn_c) = sn.collider
|
||||
&& sn_c == collider
|
||||
{
|
||||
commands.entity(sn.entity).despawn();
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
@@ -236,7 +236,7 @@ impl GraphicsManager {
|
||||
}
|
||||
|
||||
fn gen_color(rng: &mut Pcg32) -> Point3<f32> {
|
||||
let mut color: Point3<f32> = rng.gen();
|
||||
let mut color: Point3<f32> = rng.r#gen();
|
||||
|
||||
// Quantize the colors a bit to get some amount of auto-instancing from bevy.
|
||||
color.x = (color.x * 5.0).round() / 5.0;
|
||||
|
||||
@@ -1,20 +1,44 @@
|
||||
#![allow(clippy::unnecessary_cast)] // Casts are needed for switching between f32/f64.
|
||||
|
||||
use crate::{
|
||||
physics::{PhysicsEvents, PhysicsState},
|
||||
TestbedGraphics,
|
||||
physics::{PhysicsEvents, PhysicsState},
|
||||
};
|
||||
use plugin::HarnessPlugin;
|
||||
use rapier::dynamics::{
|
||||
CCDSolver, ImpulseJointSet, IntegrationParameters, IslandManager, MultibodyJointSet,
|
||||
RigidBodySet,
|
||||
};
|
||||
use rapier::geometry::{ColliderSet, DefaultBroadPhase, NarrowPhase};
|
||||
use rapier::geometry::{
|
||||
BroadPhase, BroadPhaseBvh, BvhOptimizationStrategy, ColliderSet, NarrowPhase,
|
||||
};
|
||||
use rapier::math::{Real, Vector};
|
||||
use rapier::pipeline::{ChannelEventCollector, PhysicsHooks, PhysicsPipeline, QueryPipeline};
|
||||
use rapier::pipeline::{ChannelEventCollector, PhysicsHooks, PhysicsPipeline};
|
||||
|
||||
pub mod plugin;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub enum RapierBroadPhaseType {
|
||||
#[default]
|
||||
BvhSubtreeOptimizer,
|
||||
BvhWithoutOptimization,
|
||||
}
|
||||
|
||||
impl RapierBroadPhaseType {
|
||||
pub fn init_broad_phase(self) -> Box<dyn BroadPhase> {
|
||||
match self {
|
||||
RapierBroadPhaseType::BvhSubtreeOptimizer => {
|
||||
Box::new(BroadPhaseBvh::with_optimization_strategy(
|
||||
BvhOptimizationStrategy::SubtreeOptimizer,
|
||||
))
|
||||
}
|
||||
RapierBroadPhaseType::BvhWithoutOptimization => Box::new(
|
||||
BroadPhaseBvh::with_optimization_strategy(BvhOptimizationStrategy::None),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RunState {
|
||||
#[cfg(feature = "parallel")]
|
||||
pub thread_pool: rapier::rayon::ThreadPool,
|
||||
@@ -121,9 +145,16 @@ impl Harness {
|
||||
colliders: ColliderSet,
|
||||
impulse_joints: ImpulseJointSet,
|
||||
multibody_joints: MultibodyJointSet,
|
||||
broad_phase_type: RapierBroadPhaseType,
|
||||
) -> Self {
|
||||
let mut res = Self::new_empty();
|
||||
res.set_world(bodies, colliders, impulse_joints, multibody_joints);
|
||||
res.set_world(
|
||||
bodies,
|
||||
colliders,
|
||||
impulse_joints,
|
||||
multibody_joints,
|
||||
broad_phase_type,
|
||||
);
|
||||
res
|
||||
}
|
||||
|
||||
@@ -149,12 +180,14 @@ impl Harness {
|
||||
colliders: ColliderSet,
|
||||
impulse_joints: ImpulseJointSet,
|
||||
multibody_joints: MultibodyJointSet,
|
||||
broad_phase_type: RapierBroadPhaseType,
|
||||
) {
|
||||
self.set_world_with_params(
|
||||
bodies,
|
||||
colliders,
|
||||
impulse_joints,
|
||||
multibody_joints,
|
||||
broad_phase_type,
|
||||
Vector::y() * -9.81,
|
||||
(),
|
||||
)
|
||||
@@ -166,6 +199,7 @@ impl Harness {
|
||||
colliders: ColliderSet,
|
||||
impulse_joints: ImpulseJointSet,
|
||||
multibody_joints: MultibodyJointSet,
|
||||
broad_phase_type: RapierBroadPhaseType,
|
||||
gravity: Vector<Real>,
|
||||
hooks: impl PhysicsHooks + 'static,
|
||||
) {
|
||||
@@ -179,12 +213,11 @@ impl Harness {
|
||||
self.physics.hooks = Box::new(hooks);
|
||||
|
||||
self.physics.islands = IslandManager::new();
|
||||
self.physics.broad_phase = DefaultBroadPhase::default();
|
||||
self.physics.broad_phase = broad_phase_type.init_broad_phase();
|
||||
self.physics.narrow_phase = NarrowPhase::new();
|
||||
self.state.timestep_id = 0;
|
||||
self.state.time = 0.0;
|
||||
self.physics.ccd_solver = CCDSolver::new();
|
||||
self.physics.query_pipeline = QueryPipeline::new();
|
||||
self.physics.pipeline = PhysicsPipeline::new();
|
||||
self.physics.pipeline.counters.enable();
|
||||
}
|
||||
@@ -217,14 +250,13 @@ impl Harness {
|
||||
&physics.gravity,
|
||||
&physics.integration_parameters,
|
||||
&mut physics.islands,
|
||||
&mut physics.broad_phase,
|
||||
&mut *physics.broad_phase,
|
||||
&mut physics.narrow_phase,
|
||||
&mut physics.bodies,
|
||||
&mut physics.colliders,
|
||||
&mut physics.impulse_joints,
|
||||
&mut physics.multibody_joints,
|
||||
&mut physics.ccd_solver,
|
||||
Some(&mut physics.query_pipeline),
|
||||
&*physics.hooks,
|
||||
event_handler,
|
||||
);
|
||||
@@ -236,14 +268,13 @@ impl Harness {
|
||||
&self.physics.gravity,
|
||||
&self.physics.integration_parameters,
|
||||
&mut self.physics.islands,
|
||||
&mut self.physics.broad_phase,
|
||||
&mut *self.physics.broad_phase,
|
||||
&mut self.physics.narrow_phase,
|
||||
&mut self.physics.bodies,
|
||||
&mut self.physics.colliders,
|
||||
&mut self.physics.impulse_joints,
|
||||
&mut self.physics.multibody_joints,
|
||||
&mut self.physics.ccd_solver,
|
||||
Some(&mut self.physics.query_pipeline),
|
||||
&*self.physics.hooks,
|
||||
&self.event_handler,
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::PhysicsState;
|
||||
use crate::harness::RunState;
|
||||
use crate::physics::PhysicsEvents;
|
||||
use crate::PhysicsState;
|
||||
|
||||
pub trait HarnessPlugin {
|
||||
fn run_callbacks(
|
||||
|
||||
@@ -7,7 +7,7 @@ pub use crate::harness::plugin::HarnessPlugin;
|
||||
pub use crate::physics::PhysicsState;
|
||||
pub use crate::plugin::TestbedPlugin;
|
||||
pub use crate::testbed::{Testbed, TestbedApp, TestbedGraphics, TestbedState};
|
||||
pub use bevy::prelude::KeyCode;
|
||||
pub use bevy::prelude::{Color, KeyCode};
|
||||
|
||||
#[cfg(all(feature = "dim2", feature = "other-backends"))]
|
||||
mod box2d_backend;
|
||||
|
||||
@@ -4,7 +4,7 @@ use bevy::prelude::*;
|
||||
use bevy::render::mesh::{Indices, VertexAttributeValues};
|
||||
|
||||
//use crate::objects::plane::Plane;
|
||||
use na::{point, Point3, Vector3};
|
||||
use na::{Point3, Vector3, point};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use bevy::render::render_resource::PrimitiveTopology;
|
||||
@@ -17,7 +17,7 @@ use rapier::math::{Isometry, Real, Vector};
|
||||
use crate::graphics::{BevyMaterial, InstancedMaterials, SELECTED_OBJECT_COLOR};
|
||||
#[cfg(feature = "dim2")]
|
||||
use {
|
||||
na::{vector, Point2, Vector2},
|
||||
na::{Point2, Vector2, vector},
|
||||
rapier::geometry::{Ball, Cuboid},
|
||||
};
|
||||
|
||||
@@ -143,25 +143,25 @@ impl EntityWithGraphics {
|
||||
components: &mut Query<&mut Transform>,
|
||||
gfx_shift: &Vector<Real>,
|
||||
) {
|
||||
if let Some(Some(co)) = self.collider.map(|c| colliders.get(c)) {
|
||||
if let Ok(mut pos) = components.get_mut(self.entity) {
|
||||
let co_pos = co.position() * self.delta;
|
||||
pos.translation.x = (co_pos.translation.vector.x + gfx_shift.x) as f32;
|
||||
pos.translation.y = (co_pos.translation.vector.y + gfx_shift.y) as f32;
|
||||
#[cfg(feature = "dim3")]
|
||||
{
|
||||
pos.translation.z = (co_pos.translation.vector.z + gfx_shift.z) as f32;
|
||||
pos.rotation = Quat::from_xyzw(
|
||||
co_pos.rotation.i as f32,
|
||||
co_pos.rotation.j as f32,
|
||||
co_pos.rotation.k as f32,
|
||||
co_pos.rotation.w as f32,
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "dim2")]
|
||||
{
|
||||
pos.rotation = Quat::from_rotation_z(co_pos.rotation.angle() as f32);
|
||||
}
|
||||
if let Some(Some(co)) = self.collider.map(|c| colliders.get(c))
|
||||
&& let Ok(mut pos) = components.get_mut(self.entity)
|
||||
{
|
||||
let co_pos = co.position() * self.delta;
|
||||
pos.translation.x = (co_pos.translation.vector.x + gfx_shift.x) as f32;
|
||||
pos.translation.y = (co_pos.translation.vector.y + gfx_shift.y) as f32;
|
||||
#[cfg(feature = "dim3")]
|
||||
{
|
||||
pos.translation.z = (co_pos.translation.vector.z + gfx_shift.z) as f32;
|
||||
pos.rotation = Quat::from_xyzw(
|
||||
co_pos.rotation.i as f32,
|
||||
co_pos.rotation.j as f32,
|
||||
co_pos.rotation.k as f32,
|
||||
co_pos.rotation.w as f32,
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "dim2")]
|
||||
{
|
||||
pos.rotation = Quat::from_rotation_z(co_pos.rotation.angle() as f32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ use rapier::dynamics::{
|
||||
RigidBodySet,
|
||||
};
|
||||
use rapier::geometry::{
|
||||
ColliderSet, CollisionEvent, ContactForceEvent, DefaultBroadPhase, NarrowPhase,
|
||||
BroadPhase, ColliderSet, CollisionEvent, ContactForceEvent, DefaultBroadPhase, NarrowPhase,
|
||||
};
|
||||
use rapier::math::{Real, Vector};
|
||||
use rapier::pipeline::{PhysicsHooks, PhysicsPipeline, QueryPipeline};
|
||||
use rapier::pipeline::{PhysicsHooks, PhysicsPipeline};
|
||||
|
||||
pub struct PhysicsSnapshot {
|
||||
timestep_id: usize,
|
||||
@@ -76,7 +76,7 @@ impl PhysicsSnapshot {
|
||||
+ self.colliders.len()
|
||||
+ self.impulse_joints.len()
|
||||
+ self.multibody_joints.len();
|
||||
println!("Snapshot length: {}B", total);
|
||||
println!("Snapshot length: {total}B");
|
||||
println!("|_ broad_phase: {}B", self.broad_phase.len());
|
||||
println!("|_ narrow_phase: {}B", self.narrow_phase.len());
|
||||
println!("|_ island_manager: {}B", self.island_manager.len());
|
||||
@@ -89,7 +89,7 @@ impl PhysicsSnapshot {
|
||||
|
||||
pub struct PhysicsState {
|
||||
pub islands: IslandManager,
|
||||
pub broad_phase: DefaultBroadPhase,
|
||||
pub broad_phase: Box<dyn BroadPhase>,
|
||||
pub narrow_phase: NarrowPhase,
|
||||
pub bodies: RigidBodySet,
|
||||
pub colliders: ColliderSet,
|
||||
@@ -97,7 +97,6 @@ pub struct PhysicsState {
|
||||
pub multibody_joints: MultibodyJointSet,
|
||||
pub ccd_solver: CCDSolver,
|
||||
pub pipeline: PhysicsPipeline,
|
||||
pub query_pipeline: QueryPipeline,
|
||||
pub integration_parameters: IntegrationParameters,
|
||||
pub gravity: Vector<Real>,
|
||||
pub hooks: Box<dyn PhysicsHooks>,
|
||||
@@ -113,7 +112,7 @@ impl PhysicsState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
islands: IslandManager::new(),
|
||||
broad_phase: DefaultBroadPhase::default(),
|
||||
broad_phase: Box::new(DefaultBroadPhase::default()),
|
||||
narrow_phase: NarrowPhase::new(),
|
||||
bodies: RigidBodySet::new(),
|
||||
colliders: ColliderSet::new(),
|
||||
@@ -121,7 +120,6 @@ impl PhysicsState {
|
||||
multibody_joints: MultibodyJointSet::new(),
|
||||
ccd_solver: CCDSolver::new(),
|
||||
pipeline: PhysicsPipeline::new(),
|
||||
query_pipeline: QueryPipeline::new(),
|
||||
integration_parameters: IntegrationParameters::default(),
|
||||
gravity: Vector::y() * -9.81,
|
||||
hooks: Box::new(()),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::GraphicsManager;
|
||||
use crate::graphics::BevyMaterial;
|
||||
use crate::harness::Harness;
|
||||
use crate::physics::PhysicsState;
|
||||
use crate::GraphicsManager;
|
||||
use bevy::prelude::*;
|
||||
// use bevy::render::render_resource::RenderPipelineDescriptor;
|
||||
use bevy_egui::EguiContexts;
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::num::NonZeroUsize;
|
||||
|
||||
use crate::debug_render::{DebugRenderPipelineResource, RapierDebugRenderPlugin};
|
||||
use crate::graphics::BevyMaterialComponent;
|
||||
use crate::mouse::{self, track_mouse_state, MainCamera, SceneMouse};
|
||||
use crate::mouse::{self, MainCamera, SceneMouse, track_mouse_state};
|
||||
use crate::physics::{DeserializedPhysicsSnapshot, PhysicsEvents, PhysicsSnapshot, PhysicsState};
|
||||
use crate::plugin::TestbedPlugin;
|
||||
use crate::save::SerializableTestbedState;
|
||||
@@ -23,22 +23,22 @@ use rapier::dynamics::{
|
||||
RigidBodyHandle, RigidBodySet,
|
||||
};
|
||||
#[cfg(feature = "dim3")]
|
||||
use rapier::geometry::Ray;
|
||||
use rapier::geometry::{BroadPhaseBvh, Ray};
|
||||
use rapier::geometry::{ColliderHandle, ColliderSet, NarrowPhase};
|
||||
use rapier::math::{Real, Vector};
|
||||
use rapier::pipeline::{PhysicsHooks, QueryPipeline};
|
||||
use rapier::pipeline::PhysicsHooks;
|
||||
#[cfg(feature = "dim3")]
|
||||
use rapier::{control::DynamicRayCastVehicleController, prelude::QueryFilter};
|
||||
|
||||
#[cfg(all(feature = "dim2", feature = "other-backends"))]
|
||||
use crate::box2d_backend::Box2dWorld;
|
||||
use crate::harness::Harness;
|
||||
use crate::harness::{Harness, RapierBroadPhaseType};
|
||||
#[cfg(all(feature = "dim3", feature = "other-backends"))]
|
||||
use crate::physx_backend::PhysxWorld;
|
||||
use bevy::render::camera::{Camera, ClearColor};
|
||||
use bevy_egui::EguiContexts;
|
||||
use bevy_pbr::wireframe::WireframePlugin;
|
||||
use bevy_pbr::AmbientLight;
|
||||
use bevy_pbr::wireframe::WireframePlugin;
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
use crate::camera2d::{OrbitCamera, OrbitCameraPlugin};
|
||||
@@ -132,6 +132,7 @@ pub struct TestbedState {
|
||||
pub selected_backend: usize,
|
||||
pub example_settings: ExampleSettings,
|
||||
pub solver_type: RapierSolverType,
|
||||
pub broad_phase_type: RapierBroadPhaseType,
|
||||
pub physx_use_two_friction_directions: bool,
|
||||
pub snapshot: Option<PhysicsSnapshot>,
|
||||
pub nsteps: usize,
|
||||
@@ -178,7 +179,7 @@ struct OtherBackends {
|
||||
}
|
||||
struct Plugins(Vec<Box<dyn TestbedPlugin>>);
|
||||
|
||||
pub struct TestbedGraphics<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k> {
|
||||
pub struct TestbedGraphics<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm> {
|
||||
graphics: &'a mut GraphicsManager,
|
||||
commands: &'a mut Commands<'d, 'e>,
|
||||
meshes: &'a mut Assets<Mesh>,
|
||||
@@ -189,12 +190,14 @@ pub struct TestbedGraphics<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k> {
|
||||
camera_transform: GlobalTransform,
|
||||
camera: &'a mut OrbitCamera,
|
||||
ui_context: &'a mut EguiContexts<'g, 'h>,
|
||||
pub settings: Option<&'a mut ExampleSettings>, // TODO: get rid of the Option
|
||||
keys: &'a ButtonInput<KeyCode>,
|
||||
mouse: &'a SceneMouse,
|
||||
pub gizmos: &'a mut Gizmos<'l, 'm>,
|
||||
}
|
||||
|
||||
pub struct Testbed<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k> {
|
||||
graphics: Option<TestbedGraphics<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k>>,
|
||||
pub struct Testbed<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm> {
|
||||
graphics: Option<TestbedGraphics<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm>>,
|
||||
harness: &'a mut Harness,
|
||||
state: &'a mut TestbedState,
|
||||
#[cfg(feature = "other-backends")]
|
||||
@@ -247,6 +250,7 @@ impl TestbedApp {
|
||||
selected_example: 0,
|
||||
selected_backend: RAPIER_BACKEND,
|
||||
solver_type: RapierSolverType::default(),
|
||||
broad_phase_type: RapierBroadPhaseType::default(),
|
||||
physx_use_two_friction_directions: true,
|
||||
nsteps: 1,
|
||||
camera_locked: false,
|
||||
@@ -306,7 +310,7 @@ impl TestbedApp {
|
||||
),
|
||||
];
|
||||
let usage = |exe_name: &str, err: Option<&str>| {
|
||||
println!("Usage: {} [OPTION] ", exe_name);
|
||||
println!("Usage: {exe_name} [OPTION] ");
|
||||
println!();
|
||||
println!("Options:");
|
||||
for (long, s, desc) in cmds {
|
||||
@@ -374,7 +378,7 @@ impl TestbedApp {
|
||||
println!("Running benchmark for {}", builder.0);
|
||||
|
||||
for (backend_id, backend) in backend_names.iter().enumerate() {
|
||||
println!("|_ using backend {}", backend);
|
||||
println!("|_ using backend {backend}");
|
||||
self.state.selected_backend = backend_id;
|
||||
self.harness
|
||||
.physics
|
||||
@@ -447,7 +451,7 @@ impl TestbedApp {
|
||||
|
||||
write!(file, "{}", backend_names[0]).unwrap();
|
||||
for backend in &backend_names[1..] {
|
||||
write!(file, ",{}", backend).unwrap();
|
||||
write!(file, ",{backend}").unwrap();
|
||||
}
|
||||
writeln!(file).unwrap();
|
||||
|
||||
@@ -509,7 +513,7 @@ 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, self.material_handles, body, color);
|
||||
@@ -587,7 +591,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
|
||||
}
|
||||
@@ -609,6 +613,10 @@ impl Testbed<'_, '_, '_, '_, '_, '_, '_, '_, '_, '_, '_> {
|
||||
&mut self.harness.physics
|
||||
}
|
||||
|
||||
pub fn harness(&self) -> &Harness {
|
||||
&*self.harness
|
||||
}
|
||||
|
||||
pub fn harness_mut(&mut self) -> &mut Harness {
|
||||
self.harness
|
||||
}
|
||||
@@ -648,6 +656,7 @@ impl Testbed<'_, '_, '_, '_, '_, '_, '_, '_, '_, '_, '_> {
|
||||
colliders,
|
||||
impulse_joints,
|
||||
multibody_joints,
|
||||
self.state.broad_phase_type,
|
||||
gravity,
|
||||
hooks,
|
||||
);
|
||||
@@ -694,40 +703,39 @@ impl Testbed<'_, '_, '_, '_, '_, '_, '_, '_, '_, '_, '_> {
|
||||
}
|
||||
|
||||
pub fn set_graphics_shift(&mut self, shift: Vector<Real>) {
|
||||
if !self.state.camera_locked {
|
||||
if let Some(graphics) = &mut self.graphics {
|
||||
graphics.graphics.gfx_shift = shift;
|
||||
}
|
||||
if !self.state.camera_locked
|
||||
&& let Some(graphics) = &mut self.graphics
|
||||
{
|
||||
graphics.graphics.gfx_shift = shift;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
pub fn look_at(&mut self, at: Point2<f32>, zoom: f32) {
|
||||
if !self.state.camera_locked {
|
||||
if let Some(graphics) = &mut self.graphics {
|
||||
graphics.camera.center.x = at.x;
|
||||
graphics.camera.center.y = at.y;
|
||||
graphics.camera.zoom = zoom;
|
||||
}
|
||||
if !self.state.camera_locked
|
||||
&& let Some(graphics) = &mut self.graphics
|
||||
{
|
||||
graphics.camera.center.x = at.x;
|
||||
graphics.camera.center.y = at.y;
|
||||
graphics.camera.zoom = zoom;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
pub fn look_at(&mut self, eye: Point3<f32>, at: Point3<f32>) {
|
||||
if !self.state.camera_locked {
|
||||
if let Some(graphics) = &mut self.graphics {
|
||||
graphics.camera.center.x = at.x;
|
||||
graphics.camera.center.y = at.y;
|
||||
graphics.camera.center.z = at.z;
|
||||
if !self.state.camera_locked
|
||||
&& let Some(graphics) = &mut self.graphics
|
||||
{
|
||||
graphics.camera.center.x = at.x;
|
||||
graphics.camera.center.y = at.y;
|
||||
graphics.camera.center.z = at.z;
|
||||
|
||||
let view_dir = eye - at;
|
||||
graphics.camera.distance = view_dir.norm();
|
||||
let view_dir = eye - at;
|
||||
graphics.camera.distance = view_dir.norm();
|
||||
|
||||
if graphics.camera.distance > 0.0 {
|
||||
graphics.camera.y = (view_dir.y / graphics.camera.distance).acos();
|
||||
graphics.camera.x =
|
||||
(-view_dir.z).atan2(view_dir.x) - std::f32::consts::FRAC_PI_2;
|
||||
}
|
||||
if graphics.camera.distance > 0.0 {
|
||||
graphics.camera.y = (view_dir.y / graphics.camera.distance).acos();
|
||||
graphics.camera.x = (-view_dir.z).atan2(view_dir.x) - std::f32::consts::FRAC_PI_2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -806,12 +814,25 @@ impl Testbed<'_, '_, '_, '_, '_, '_, '_, '_, '_, '_, '_> {
|
||||
wheels[1].engine_force = engine_force;
|
||||
wheels[1].steering = steering_angle;
|
||||
|
||||
let query_pipeline = if let Some(bf) = self
|
||||
.harness
|
||||
.physics
|
||||
.broad_phase
|
||||
.downcast_ref::<BroadPhaseBvh>()
|
||||
{
|
||||
bf.as_query_pipeline_mut(
|
||||
self.harness.physics.narrow_phase.query_dispatcher(),
|
||||
&mut self.harness.physics.bodies,
|
||||
&mut self.harness.physics.colliders,
|
||||
QueryFilter::exclude_dynamic().exclude_rigid_body(vehicle.chassis),
|
||||
)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
vehicle.update_vehicle(
|
||||
self.harness.physics.integration_parameters.dt,
|
||||
&mut self.harness.physics.bodies,
|
||||
&self.harness.physics.colliders,
|
||||
&self.harness.physics.query_pipeline,
|
||||
QueryFilter::exclude_dynamic().exclude_rigid_body(vehicle.chassis),
|
||||
query_pipeline,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1111,11 +1132,12 @@ fn update_testbed(
|
||||
#[cfg(feature = "other-backends")] mut other_backends: NonSendMut<OtherBackends>,
|
||||
mut plugins: NonSendMut<Plugins>,
|
||||
mut ui_context: EguiContexts,
|
||||
(mut gfx_components, mut visibilities, mut cameras, mut material_handles): (
|
||||
(mut gfx_components, mut visibilities, mut cameras, mut material_handles, mut gizmos): (
|
||||
Query<&mut Transform>,
|
||||
Query<&mut Visibility>,
|
||||
Query<(&Camera, &GlobalTransform, &mut OrbitCamera)>,
|
||||
Query<&mut BevyMaterialComponent>,
|
||||
Gizmos,
|
||||
),
|
||||
keys: Res<ButtonInput<KeyCode>>,
|
||||
) {
|
||||
@@ -1138,6 +1160,8 @@ fn update_testbed(
|
||||
camera_transform: *cameras.single().1,
|
||||
camera: &mut cameras.single_mut().2,
|
||||
ui_context: &mut ui_context,
|
||||
gizmos: &mut gizmos,
|
||||
settings: None,
|
||||
keys: &keys,
|
||||
mouse: &mouse,
|
||||
};
|
||||
@@ -1236,6 +1260,7 @@ fn update_testbed(
|
||||
}
|
||||
plugins.0.clear();
|
||||
|
||||
state.selected_example = state.selected_example.min(builders.0.len() - 1);
|
||||
let selected_example = state.selected_example;
|
||||
let graphics = &mut *graphics;
|
||||
let meshes = &mut *meshes;
|
||||
@@ -1254,6 +1279,8 @@ fn update_testbed(
|
||||
camera_transform: *cameras.single().1,
|
||||
camera: &mut cameras.single_mut().2,
|
||||
ui_context: &mut ui_context,
|
||||
gizmos: &mut gizmos,
|
||||
settings: None,
|
||||
keys: &keys,
|
||||
mouse: &mouse,
|
||||
};
|
||||
@@ -1279,17 +1306,21 @@ fn update_testbed(
|
||||
state
|
||||
.action_flags
|
||||
.set(TestbedActionFlags::TAKE_SNAPSHOT, false);
|
||||
state.snapshot = PhysicsSnapshot::new(
|
||||
harness.state.timestep_id,
|
||||
&harness.physics.broad_phase,
|
||||
&harness.physics.narrow_phase,
|
||||
&harness.physics.islands,
|
||||
&harness.physics.bodies,
|
||||
&harness.physics.colliders,
|
||||
&harness.physics.impulse_joints,
|
||||
&harness.physics.multibody_joints,
|
||||
)
|
||||
.ok();
|
||||
// FIXME
|
||||
println!(
|
||||
"!!!!!!!!! Snapshots are not working any more. Requires broad-phase serialization."
|
||||
);
|
||||
// state.snapshot = PhysicsSnapshot::new(
|
||||
// harness.state.timestep_id,
|
||||
// &*harness.physics.broad_phase,
|
||||
// &harness.physics.narrow_phase,
|
||||
// &harness.physics.islands,
|
||||
// &harness.physics.bodies,
|
||||
// &harness.physics.colliders,
|
||||
// &harness.physics.impulse_joints,
|
||||
// &harness.physics.multibody_joints,
|
||||
// )
|
||||
// .ok();
|
||||
|
||||
if let Some(snap) = &state.snapshot {
|
||||
snap.print_snapshot_len();
|
||||
@@ -1303,8 +1334,8 @@ fn update_testbed(
|
||||
state
|
||||
.action_flags
|
||||
.set(TestbedActionFlags::RESTORE_SNAPSHOT, false);
|
||||
if let Some(snapshot) = &state.snapshot {
|
||||
if let Ok(DeserializedPhysicsSnapshot {
|
||||
if let Some(snapshot) = &state.snapshot
|
||||
&& let Ok(DeserializedPhysicsSnapshot {
|
||||
timestep_id,
|
||||
broad_phase,
|
||||
narrow_phase,
|
||||
@@ -1314,27 +1345,25 @@ fn update_testbed(
|
||||
impulse_joints,
|
||||
multibody_joints,
|
||||
}) = snapshot.restore()
|
||||
{
|
||||
clear(&mut commands, &mut state, &mut graphics, &mut plugins);
|
||||
{
|
||||
clear(&mut commands, &mut state, &mut graphics, &mut plugins);
|
||||
|
||||
for plugin in &mut plugins.0 {
|
||||
plugin.clear_graphics(&mut graphics, &mut commands);
|
||||
}
|
||||
|
||||
harness.state.timestep_id = timestep_id;
|
||||
harness.physics.broad_phase = broad_phase;
|
||||
harness.physics.narrow_phase = narrow_phase;
|
||||
harness.physics.islands = island_manager;
|
||||
harness.physics.bodies = bodies;
|
||||
harness.physics.colliders = colliders;
|
||||
harness.physics.impulse_joints = impulse_joints;
|
||||
harness.physics.multibody_joints = multibody_joints;
|
||||
harness.physics.query_pipeline = QueryPipeline::new();
|
||||
|
||||
state
|
||||
.action_flags
|
||||
.set(TestbedActionFlags::RESET_WORLD_GRAPHICS, true);
|
||||
for plugin in &mut plugins.0 {
|
||||
plugin.clear_graphics(&mut graphics, &mut commands);
|
||||
}
|
||||
|
||||
harness.state.timestep_id = timestep_id;
|
||||
harness.physics.broad_phase = Box::new(broad_phase);
|
||||
harness.physics.narrow_phase = narrow_phase;
|
||||
harness.physics.islands = island_manager;
|
||||
harness.physics.bodies = bodies;
|
||||
harness.physics.colliders = colliders;
|
||||
harness.physics.impulse_joints = impulse_joints;
|
||||
harness.physics.multibody_joints = multibody_joints;
|
||||
|
||||
state
|
||||
.action_flags
|
||||
.set(TestbedActionFlags::RESET_WORLD_GRAPHICS, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1430,6 +1459,8 @@ fn update_testbed(
|
||||
camera_transform: *cameras.single().1,
|
||||
camera: &mut cameras.single_mut().2,
|
||||
ui_context: &mut ui_context,
|
||||
gizmos: &mut gizmos,
|
||||
settings: Some(&mut state.example_settings),
|
||||
keys: &keys,
|
||||
mouse: &mouse,
|
||||
};
|
||||
@@ -1576,13 +1607,13 @@ fn highlight_hovered_body(
|
||||
camera: &Camera,
|
||||
camera_transform: &GlobalTransform,
|
||||
) {
|
||||
if let Some(highlighted_body) = testbed_state.highlighted_body {
|
||||
if let Some(nodes) = graphics_manager.body_nodes_mut(highlighted_body) {
|
||||
for node in nodes {
|
||||
if let Ok(mut handle) = material_handles.get_mut(node.entity) {
|
||||
**handle = node.material.clone_weak()
|
||||
};
|
||||
}
|
||||
if let Some(highlighted_body) = testbed_state.highlighted_body
|
||||
&& let Some(nodes) = graphics_manager.body_nodes_mut(highlighted_body)
|
||||
{
|
||||
for node in nodes {
|
||||
if let Ok(mut handle) = material_handles.get_mut(node.entity) {
|
||||
**handle = node.material.clone_weak()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1599,14 +1630,18 @@ fn highlight_hovered_body(
|
||||
let ray_dir = Vector3::new(ray_dir.x as Real, ray_dir.y as Real, ray_dir.z as Real);
|
||||
|
||||
let ray = Ray::new(ray_origin, ray_dir);
|
||||
let hit = physics.query_pipeline.cast_ray(
|
||||
&physics.bodies,
|
||||
&physics.colliders,
|
||||
&ray,
|
||||
Real::MAX,
|
||||
true,
|
||||
QueryFilter::only_dynamic(),
|
||||
);
|
||||
let query_pipeline = if let Some(bf) = physics.broad_phase.downcast_ref::<BroadPhaseBvh>() {
|
||||
bf.as_query_pipeline(
|
||||
physics.narrow_phase.query_dispatcher(),
|
||||
&physics.bodies,
|
||||
&physics.colliders,
|
||||
QueryFilter::only_dynamic(),
|
||||
)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let hit = query_pipeline.cast_ray(&ray, Real::MAX, true);
|
||||
|
||||
if let Some((handle, _)) = hit {
|
||||
let collider = &physics.colliders[handle];
|
||||
|
||||
@@ -3,18 +3,18 @@ use rapier::math::Real;
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
use crate::debug_render::DebugRenderPipelineResource;
|
||||
use crate::harness::Harness;
|
||||
use crate::harness::{Harness, RapierBroadPhaseType};
|
||||
use crate::testbed::{
|
||||
RapierSolverType, RunMode, TestbedActionFlags, TestbedState, TestbedStateFlags,
|
||||
PHYSX_BACKEND_PATCH_FRICTION, PHYSX_BACKEND_TWO_FRICTION_DIR,
|
||||
PHYSX_BACKEND_PATCH_FRICTION, PHYSX_BACKEND_TWO_FRICTION_DIR, RapierSolverType, RunMode,
|
||||
TestbedActionFlags, TestbedState, TestbedStateFlags,
|
||||
};
|
||||
|
||||
pub use bevy_egui::egui;
|
||||
|
||||
use crate::settings::SettingValue;
|
||||
use crate::PhysicsState;
|
||||
use bevy_egui::egui::{ComboBox, Slider, Ui, Window};
|
||||
use crate::settings::SettingValue;
|
||||
use bevy_egui::EguiContexts;
|
||||
use bevy_egui::egui::{ComboBox, Slider, Ui, Window};
|
||||
use rapier::dynamics::IntegrationParameters;
|
||||
use web_time::Instant;
|
||||
|
||||
@@ -118,6 +118,7 @@ pub(crate) fn update_ui(
|
||||
integration_parameters.num_solver_iterations =
|
||||
NonZeroUsize::new(num_iterations).unwrap();
|
||||
} else {
|
||||
// Solver type.
|
||||
let mut changed = false;
|
||||
egui::ComboBox::from_label("solver type")
|
||||
.width(150.0)
|
||||
@@ -151,6 +152,32 @@ pub(crate) fn update_ui(
|
||||
}
|
||||
}
|
||||
|
||||
// Broad-phase.
|
||||
let mut changed = false;
|
||||
egui::ComboBox::from_label("broad-phase")
|
||||
.width(150.0)
|
||||
.selected_text(format!("{:?}", state.broad_phase_type))
|
||||
.show_ui(ui, |ui| {
|
||||
let broad_phase_type = [
|
||||
RapierBroadPhaseType::BvhSubtreeOptimizer,
|
||||
RapierBroadPhaseType::BvhWithoutOptimization,
|
||||
];
|
||||
for sty in broad_phase_type {
|
||||
changed = ui
|
||||
.selectable_value(&mut state.broad_phase_type, sty, format!("{sty:?}"))
|
||||
.changed()
|
||||
|| changed;
|
||||
}
|
||||
});
|
||||
|
||||
if changed {
|
||||
harness.physics.broad_phase = state.broad_phase_type.init_broad_phase();
|
||||
// Restart the simulation after a broad-phase changes since some
|
||||
// broad-phase might not support hot-swapping.
|
||||
state.action_flags.set(TestbedActionFlags::RESTART, true);
|
||||
}
|
||||
|
||||
// Solver iterations.
|
||||
let mut num_iterations = integration_parameters.num_solver_iterations.get();
|
||||
ui.add(Slider::new(&mut num_iterations, 1..=40).text("num solver iters."));
|
||||
integration_parameters.num_solver_iterations =
|
||||
@@ -191,17 +218,14 @@ pub(crate) fn update_ui(
|
||||
&mut integration_parameters.contact_natural_frequency,
|
||||
0.01..=120.0,
|
||||
)
|
||||
.text(format!("contacts Hz (erp = {:.3})", curr_erp)),
|
||||
.text(format!("contacts Hz (erp = {curr_erp:.3})")),
|
||||
);
|
||||
ui.add(
|
||||
Slider::new(
|
||||
&mut integration_parameters.contact_damping_ratio,
|
||||
0.01..=20.0,
|
||||
)
|
||||
.text(format!(
|
||||
"damping ratio (cfm-factor = {:.3})",
|
||||
curr_cfm_factor
|
||||
)),
|
||||
.text(format!("damping ratio (cfm-factor = {curr_cfm_factor:.3})",)),
|
||||
);
|
||||
ui.add(
|
||||
Slider::new(
|
||||
@@ -377,10 +401,6 @@ fn profiling_ui(ui: &mut Ui, counters: &Counters) {
|
||||
"Island computation: {:.2}ms",
|
||||
counters.island_construction_time_ms()
|
||||
));
|
||||
ui.label(format!(
|
||||
"Query pipeline: {:.2}ms",
|
||||
counters.query_pipeline_update_time_ms()
|
||||
));
|
||||
ui.label(format!(
|
||||
"User changes: {:.2}ms",
|
||||
counters.stages.user_changes.time_ms()
|
||||
@@ -391,7 +411,7 @@ fn profiling_ui(ui: &mut Ui, counters: &Counters) {
|
||||
fn serialization_string(timestep_id: usize, physics: &PhysicsState) -> String {
|
||||
let t = Instant::now();
|
||||
// let t = Instant::now();
|
||||
let bf = bincode::serialize(&physics.broad_phase).unwrap();
|
||||
// let bf = bincode::serialize(&physics.broad_phase).unwrap();
|
||||
// println!("bf: {}", Instant::now() - t);
|
||||
// let t = Instant::now();
|
||||
let nf = bincode::serialize(&physics.narrow_phase).unwrap();
|
||||
@@ -406,7 +426,7 @@ fn serialization_string(timestep_id: usize, physics: &PhysicsState) -> String {
|
||||
let js = bincode::serialize(&physics.impulse_joints).unwrap();
|
||||
// println!("js: {}", Instant::now() - t);
|
||||
let serialization_time = Instant::now() - t;
|
||||
let hash_bf = md5::compute(&bf);
|
||||
// let hash_bf = md5::compute(&bf);
|
||||
let hash_nf = md5::compute(&nf);
|
||||
let hash_bodies = md5::compute(&bs);
|
||||
let hash_colliders = md5::compute(&cs);
|
||||
@@ -421,16 +441,16 @@ Hashes at frame: {}
|
||||
|_ Joints [{:.1}KB]: {}"#,
|
||||
serialization_time.as_secs_f64() * 1000.0,
|
||||
timestep_id,
|
||||
bf.len() as f32 / 1000.0,
|
||||
format!("{:?}", hash_bf).split_at(10).0,
|
||||
"<fixme>", // bf.len() as f32 / 1000.0,
|
||||
"<fixme>", // format!("{:?}", hash_bf).split_at(10).0,
|
||||
nf.len() as f32 / 1000.0,
|
||||
format!("{:?}", hash_nf).split_at(10).0,
|
||||
format!("{hash_nf:?}").split_at(10).0,
|
||||
bs.len() as f32 / 1000.0,
|
||||
format!("{:?}", hash_bodies).split_at(10).0,
|
||||
format!("{hash_bodies:?}").split_at(10).0,
|
||||
cs.len() as f32 / 1000.0,
|
||||
format!("{:?}", hash_colliders).split_at(10).0,
|
||||
format!("{hash_colliders:?}").split_at(10).0,
|
||||
js.len() as f32 / 1000.0,
|
||||
format!("{:?}", hash_joints).split_at(10).0,
|
||||
format!("{hash_joints:?}").split_at(10).0,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -447,7 +467,7 @@ fn example_settings_ui(ui_context: &mut EguiContexts, state: &mut TestbedState)
|
||||
let prev_value = value.clone();
|
||||
match value {
|
||||
SettingValue::Label(value) => {
|
||||
ui.label(format!("{}: {}", name, value));
|
||||
ui.label(format!("{name}: {value}"));
|
||||
}
|
||||
SettingValue::F32 { value, range } => {
|
||||
ui.add(Slider::new(value, range.clone()).text(name));
|
||||
|
||||
Reference in New Issue
Block a user