* feat: migrate to glam whenever relevant + migrate testbed to kiss3d instead of bevy * chore: update changelog * Fix warnings and tests * Release v0.32.0
244 lines
7.3 KiB
Rust
244 lines
7.3 KiB
Rust
//! Testbed struct for building examples.
|
|
|
|
use kiss3d::color::Color;
|
|
use rapier::dynamics::{
|
|
ImpulseJointSet, IntegrationParameters, MultibodyJointSet, RigidBodyHandle, RigidBodySet,
|
|
};
|
|
use rapier::geometry::{ColliderHandle, ColliderSet};
|
|
use rapier::pipeline::PhysicsHooks;
|
|
|
|
#[cfg(feature = "dim3")]
|
|
use {glamx::Vec3, rapier::control::DynamicRayCastVehicleController};
|
|
|
|
use crate::harness::Harness;
|
|
use crate::physics::PhysicsState;
|
|
use crate::settings::ExampleSettings;
|
|
|
|
use super::graphics_context::TestbedGraphics;
|
|
use super::state::{TestbedActionFlags, TestbedState};
|
|
|
|
#[cfg(all(feature = "dim3", feature = "other-backends"))]
|
|
use super::OtherBackends;
|
|
|
|
#[cfg(all(feature = "dim3", feature = "other-backends"))]
|
|
use super::state::{PHYSX_BACKEND_PATCH_FRICTION, PHYSX_BACKEND_TWO_FRICTION_DIR};
|
|
|
|
#[cfg(all(feature = "dim3", feature = "other-backends"))]
|
|
use crate::physx_backend::PhysxWorld;
|
|
|
|
use super::Plugins;
|
|
|
|
/// An example/demo that can be run in the testbed
|
|
#[derive(Clone)]
|
|
pub struct Example {
|
|
/// Display name of the example
|
|
pub name: &'static str,
|
|
/// Group/category for organizing in the UI (e.g., "Demos", "Joints", "Debug")
|
|
pub group: &'static str,
|
|
/// The builder function that initializes the example
|
|
pub builder: fn(&mut Testbed),
|
|
}
|
|
|
|
impl Example {
|
|
/// Create a new example with a group
|
|
pub fn new(group: &'static str, name: &'static str, builder: fn(&mut Testbed)) -> Self {
|
|
Self {
|
|
name,
|
|
group,
|
|
builder,
|
|
}
|
|
}
|
|
|
|
/// Create a new example in the default "Demos" group
|
|
pub fn demo(name: &'static str, builder: fn(&mut Testbed)) -> Self {
|
|
Self::new("Demos", name, builder)
|
|
}
|
|
}
|
|
|
|
/// Allow constructing Example from a tuple (group, name, builder) for convenience
|
|
impl From<(&'static str, &'static str, fn(&mut Testbed))> for Example {
|
|
fn from((group, name, builder): (&'static str, &'static str, fn(&mut Testbed))) -> Self {
|
|
Self::new(group, name, builder)
|
|
}
|
|
}
|
|
|
|
/// Type alias for simulation builder functions
|
|
pub type SimulationBuilders = Vec<Example>;
|
|
|
|
/// The main testbed struct passed to example builders
|
|
pub struct Testbed<'a> {
|
|
pub graphics: Option<TestbedGraphics<'a>>,
|
|
pub harness: &'a mut Harness,
|
|
pub state: &'a mut TestbedState,
|
|
#[cfg(all(feature = "dim3", feature = "other-backends"))]
|
|
pub other_backends: &'a mut OtherBackends,
|
|
pub plugins: &'a mut Plugins,
|
|
}
|
|
|
|
impl Testbed<'_> {
|
|
pub fn set_number_of_steps_per_frame(&mut self, nsteps: usize) {
|
|
self.state.nsteps = nsteps;
|
|
}
|
|
|
|
#[cfg(feature = "dim3")]
|
|
pub fn set_vehicle_controller(&mut self, controller: DynamicRayCastVehicleController) {
|
|
self.state.vehicle_controller = Some(controller);
|
|
}
|
|
|
|
pub fn allow_grabbing_behind_ground(&mut self, allow: bool) {
|
|
self.state.can_grab_behind_ground = allow;
|
|
}
|
|
|
|
pub fn integration_parameters_mut(&mut self) -> &mut IntegrationParameters {
|
|
&mut self.harness.physics.integration_parameters
|
|
}
|
|
|
|
pub fn physics_state_mut(&mut self) -> &mut PhysicsState {
|
|
&mut self.harness.physics
|
|
}
|
|
|
|
pub fn harness(&self) -> &Harness {
|
|
self.harness
|
|
}
|
|
|
|
pub fn harness_mut(&mut self) -> &mut Harness {
|
|
self.harness
|
|
}
|
|
|
|
pub fn example_settings_mut(&mut self) -> &mut ExampleSettings {
|
|
&mut self.state.example_settings
|
|
}
|
|
|
|
pub fn set_world(
|
|
&mut self,
|
|
bodies: RigidBodySet,
|
|
colliders: ColliderSet,
|
|
impulse_joints: ImpulseJointSet,
|
|
multibody_joints: MultibodyJointSet,
|
|
) {
|
|
self.set_world_with_params(
|
|
bodies,
|
|
colliders,
|
|
impulse_joints,
|
|
multibody_joints,
|
|
rapier::math::Vector::Y * -9.81,
|
|
(),
|
|
)
|
|
}
|
|
|
|
pub fn set_world_with_params(
|
|
&mut self,
|
|
bodies: RigidBodySet,
|
|
colliders: ColliderSet,
|
|
impulse_joints: ImpulseJointSet,
|
|
multibody_joints: MultibodyJointSet,
|
|
gravity: rapier::math::Vector,
|
|
hooks: impl PhysicsHooks + 'static,
|
|
) {
|
|
self.harness.set_world_with_params(
|
|
bodies,
|
|
colliders,
|
|
impulse_joints,
|
|
multibody_joints,
|
|
self.state.broad_phase_type,
|
|
gravity,
|
|
hooks,
|
|
);
|
|
|
|
self.state
|
|
.action_flags
|
|
.set(TestbedActionFlags::RESET_WORLD_GRAPHICS, true);
|
|
|
|
#[cfg(feature = "dim3")]
|
|
{
|
|
self.state.vehicle_controller = None;
|
|
}
|
|
|
|
#[cfg(all(feature = "dim3", feature = "other-backends"))]
|
|
{
|
|
if self.state.selected_backend == PHYSX_BACKEND_PATCH_FRICTION
|
|
|| self.state.selected_backend == PHYSX_BACKEND_TWO_FRICTION_DIR
|
|
{
|
|
self.other_backends.physx = Some(PhysxWorld::from_rapier(
|
|
self.harness.physics.gravity,
|
|
&self.harness.physics.integration_parameters,
|
|
&self.harness.physics.bodies,
|
|
&self.harness.physics.colliders,
|
|
&self.harness.physics.impulse_joints,
|
|
&self.harness.physics.multibody_joints,
|
|
self.state.selected_backend == PHYSX_BACKEND_TWO_FRICTION_DIR,
|
|
self.harness.state.num_threads(),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn set_graphics_shift(&mut self, shift: rapier::math::Vector) {
|
|
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: glamx::Vec2, zoom: f32) {
|
|
if !self.state.camera_locked
|
|
&& let Some(graphics) = &mut self.graphics
|
|
{
|
|
graphics.camera.set_at(at);
|
|
graphics.camera.set_zoom(zoom);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "dim3")]
|
|
pub fn look_at(&mut self, eye: Vec3, at: Vec3) {
|
|
if !self.state.camera_locked
|
|
&& let Some(graphics) = &mut self.graphics
|
|
{
|
|
graphics.camera.look_at(eye, at);
|
|
}
|
|
}
|
|
|
|
pub fn set_initial_body_color(&mut self, body: RigidBodyHandle, color: Color) {
|
|
if let Some(graphics) = &mut self.graphics {
|
|
graphics.graphics.set_initial_body_color(body, color);
|
|
}
|
|
}
|
|
|
|
pub fn set_initial_collider_color(&mut self, collider: ColliderHandle, color: Color) {
|
|
if let Some(graphics) = &mut self.graphics {
|
|
graphics
|
|
.graphics
|
|
.set_initial_collider_color(collider, color);
|
|
}
|
|
}
|
|
|
|
pub fn set_body_wireframe(&mut self, body: RigidBodyHandle, wireframe_enabled: bool) {
|
|
if let Some(graphics) = &mut self.graphics {
|
|
graphics
|
|
.graphics
|
|
.set_body_wireframe(body, wireframe_enabled);
|
|
}
|
|
}
|
|
|
|
pub fn add_callback<
|
|
F: FnMut(
|
|
Option<&mut TestbedGraphics>,
|
|
&mut PhysicsState,
|
|
&crate::physics::PhysicsEvents,
|
|
&crate::harness::RunState,
|
|
) + 'static,
|
|
>(
|
|
&mut self,
|
|
callback: F,
|
|
) {
|
|
self.harness.add_callback(callback);
|
|
}
|
|
|
|
pub fn add_plugin(&mut self, mut plugin: impl crate::plugin::TestbedPlugin + 'static) {
|
|
plugin.init_plugin();
|
|
self.plugins.0.push(Box::new(plugin));
|
|
}
|
|
}
|