First public release of Rapier.
This commit is contained in:
BIN
src_testbed/Inconsolata.otf
Normal file
BIN
src_testbed/Inconsolata.otf
Normal file
Binary file not shown.
237
src_testbed/box2d_backend.rs
Normal file
237
src_testbed/box2d_backend.rs
Normal file
@@ -0,0 +1,237 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use na::{Isometry2, Vector2};
|
||||
use rapier::counters::Counters;
|
||||
use rapier::dynamics::{
|
||||
IntegrationParameters, JointParams, JointSet, RigidBodyHandle, RigidBodySet,
|
||||
};
|
||||
use rapier::geometry::{Collider, ColliderSet, Shape};
|
||||
use std::f32;
|
||||
|
||||
use wrapped2d::b2;
|
||||
use wrapped2d::dynamics::joints::{PrismaticJointDef, RevoluteJointDef, WeldJointDef};
|
||||
use wrapped2d::user_data::NoUserData;
|
||||
|
||||
fn na_vec_to_b2_vec(v: Vector2<f32>) -> b2::Vec2 {
|
||||
b2::Vec2 { x: v.x, y: v.y }
|
||||
}
|
||||
|
||||
fn b2_vec_to_na_vec(v: b2::Vec2) -> Vector2<f32> {
|
||||
Vector2::new(v.x, v.y)
|
||||
}
|
||||
|
||||
fn b2_transform_to_na_isometry(v: b2::Transform) -> Isometry2<f32> {
|
||||
Isometry2::new(b2_vec_to_na_vec(v.pos), v.rot.angle())
|
||||
}
|
||||
|
||||
pub struct Box2dWorld {
|
||||
world: b2::World<NoUserData>,
|
||||
rapier2box2d: HashMap<RigidBodyHandle, b2::BodyHandle>,
|
||||
}
|
||||
|
||||
impl Box2dWorld {
|
||||
pub fn from_rapier(
|
||||
gravity: Vector2<f32>,
|
||||
bodies: &RigidBodySet,
|
||||
colliders: &ColliderSet,
|
||||
joints: &JointSet,
|
||||
) -> Self {
|
||||
let mut world = b2::World::new(&na_vec_to_b2_vec(gravity));
|
||||
world.set_continuous_physics(false);
|
||||
|
||||
let mut res = Box2dWorld {
|
||||
world,
|
||||
rapier2box2d: HashMap::new(),
|
||||
};
|
||||
|
||||
res.insert_bodies(bodies);
|
||||
res.insert_colliders(colliders);
|
||||
res.insert_joints(joints);
|
||||
res
|
||||
}
|
||||
|
||||
fn insert_bodies(&mut self, bodies: &RigidBodySet) {
|
||||
for (handle, body) in bodies.iter() {
|
||||
let body_type = if !body.is_dynamic() {
|
||||
b2::BodyType::Static
|
||||
} else {
|
||||
b2::BodyType::Dynamic
|
||||
};
|
||||
|
||||
let linear_damping = 0.0;
|
||||
let angular_damping = 0.0;
|
||||
|
||||
// if let Some(rb) = body.downcast_ref::<RigidBody<f32>>() {
|
||||
// linear_damping = rb.linear_damping();
|
||||
// angular_damping = rb.angular_damping();
|
||||
// } else {
|
||||
// linear_damping = 0.0;
|
||||
// angular_damping = 0.0;
|
||||
// }
|
||||
|
||||
let def = b2::BodyDef {
|
||||
body_type,
|
||||
position: na_vec_to_b2_vec(body.position.translation.vector),
|
||||
angle: body.position.rotation.angle(),
|
||||
linear_velocity: na_vec_to_b2_vec(body.linvel),
|
||||
angular_velocity: body.angvel,
|
||||
linear_damping,
|
||||
angular_damping,
|
||||
..b2::BodyDef::new()
|
||||
};
|
||||
let b2_handle = self.world.create_body(&def);
|
||||
self.rapier2box2d.insert(handle, b2_handle);
|
||||
|
||||
// Collider.
|
||||
let mut b2_body = self.world.body_mut(b2_handle);
|
||||
b2_body.set_bullet(false /* collider.is_ccd_enabled() */);
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_colliders(&mut self, colliders: &ColliderSet) {
|
||||
for (_, collider) in colliders.iter() {
|
||||
let b2_body_handle = self.rapier2box2d[&collider.parent()];
|
||||
let mut b2_body = self.world.body_mut(b2_body_handle);
|
||||
Self::create_fixture(&collider, &mut *b2_body);
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_joints(&mut self, joints: &JointSet) {
|
||||
for joint in joints.iter() {
|
||||
let body_a = self.rapier2box2d[&joint.body1];
|
||||
let body_b = self.rapier2box2d[&joint.body2];
|
||||
|
||||
match &joint.params {
|
||||
JointParams::BallJoint(params) => {
|
||||
let def = RevoluteJointDef {
|
||||
body_a,
|
||||
body_b,
|
||||
collide_connected: true,
|
||||
local_anchor_a: na_vec_to_b2_vec(params.local_anchor1.coords),
|
||||
local_anchor_b: na_vec_to_b2_vec(params.local_anchor2.coords),
|
||||
reference_angle: 0.0,
|
||||
enable_limit: false,
|
||||
lower_angle: 0.0,
|
||||
upper_angle: 0.0,
|
||||
enable_motor: false,
|
||||
motor_speed: 0.0,
|
||||
max_motor_torque: 0.0,
|
||||
};
|
||||
|
||||
self.world.create_joint(&def);
|
||||
}
|
||||
JointParams::FixedJoint(params) => {
|
||||
let def = WeldJointDef {
|
||||
body_a,
|
||||
body_b,
|
||||
collide_connected: true,
|
||||
local_anchor_a: na_vec_to_b2_vec(params.local_anchor1.translation.vector),
|
||||
local_anchor_b: na_vec_to_b2_vec(params.local_anchor2.translation.vector),
|
||||
reference_angle: 0.0,
|
||||
frequency: 0.0,
|
||||
damping_ratio: 0.0,
|
||||
};
|
||||
|
||||
self.world.create_joint(&def);
|
||||
}
|
||||
JointParams::PrismaticJoint(params) => {
|
||||
let def = PrismaticJointDef {
|
||||
body_a,
|
||||
body_b,
|
||||
collide_connected: true,
|
||||
local_anchor_a: na_vec_to_b2_vec(params.local_anchor1.coords),
|
||||
local_anchor_b: na_vec_to_b2_vec(params.local_anchor2.coords),
|
||||
local_axis_a: na_vec_to_b2_vec(params.local_axis1().into_inner()),
|
||||
reference_angle: 0.0,
|
||||
enable_limit: params.limits_enabled,
|
||||
lower_translation: params.limits[0],
|
||||
upper_translation: params.limits[1],
|
||||
enable_motor: false,
|
||||
max_motor_force: 0.0,
|
||||
motor_speed: 0.0,
|
||||
};
|
||||
|
||||
self.world.create_joint(&def);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_fixture(collider: &Collider, body: &mut b2::MetaBody<NoUserData>) {
|
||||
let center = na_vec_to_b2_vec(collider.delta().translation.vector);
|
||||
let mut fixture_def = b2::FixtureDef::new();
|
||||
|
||||
fixture_def.restitution = 0.0;
|
||||
fixture_def.friction = collider.friction;
|
||||
fixture_def.density = collider.density();
|
||||
fixture_def.is_sensor = collider.is_sensor();
|
||||
fixture_def.filter = b2::Filter::new();
|
||||
|
||||
match collider.shape() {
|
||||
Shape::Ball(b) => {
|
||||
let mut b2_shape = b2::CircleShape::new();
|
||||
b2_shape.set_radius(b.radius);
|
||||
b2_shape.set_position(center);
|
||||
body.create_fixture(&b2_shape, &mut fixture_def);
|
||||
}
|
||||
Shape::Cuboid(c) => {
|
||||
let b2_shape = b2::PolygonShape::new_box(c.half_extents.x, c.half_extents.y);
|
||||
body.create_fixture(&b2_shape, &mut fixture_def);
|
||||
}
|
||||
Shape::Polygon(poly) => {
|
||||
let points: Vec<_> = poly
|
||||
.vertices()
|
||||
.iter()
|
||||
.map(|p| collider.delta() * p)
|
||||
.map(|p| na_vec_to_b2_vec(p.coords))
|
||||
.collect();
|
||||
let b2_shape = b2::PolygonShape::new_with(&points);
|
||||
body.create_fixture(&b2_shape, &mut fixture_def);
|
||||
}
|
||||
Shape::HeightField(heightfield) => {
|
||||
let mut segments = heightfield.segments();
|
||||
let seg1 = segments.next().unwrap();
|
||||
let mut vertices = vec![
|
||||
na_vec_to_b2_vec(seg1.a.coords),
|
||||
na_vec_to_b2_vec(seg1.b.coords),
|
||||
];
|
||||
|
||||
// TODO: this will not handle holes properly.
|
||||
segments.for_each(|seg| {
|
||||
vertices.push(na_vec_to_b2_vec(seg.b.coords));
|
||||
});
|
||||
|
||||
let b2_shape = b2::ChainShape::new_chain(&vertices);
|
||||
body.create_fixture(&b2_shape, &mut fixture_def);
|
||||
}
|
||||
_ => eprintln!("Creating a shape unknown to the Box2d backend."),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step(&mut self, counters: &mut Counters, params: &IntegrationParameters) {
|
||||
// self.world.set_continuous_physics(world.integration_parameters.max_ccd_substeps != 0);
|
||||
|
||||
counters.step_started();
|
||||
self.world.step(
|
||||
params.dt(),
|
||||
params.max_velocity_iterations as i32,
|
||||
params.max_position_iterations as i32,
|
||||
);
|
||||
counters.step_completed();
|
||||
}
|
||||
|
||||
pub fn sync(&self, bodies: &mut RigidBodySet, colliders: &mut ColliderSet) {
|
||||
for (handle, mut body) in bodies.iter_mut() {
|
||||
if let Some(pb2_handle) = self.rapier2box2d.get(&handle) {
|
||||
let b2_body = self.world.body(*pb2_handle);
|
||||
let pos = b2_transform_to_na_isometry(b2_body.transform().clone());
|
||||
body.set_position(pos);
|
||||
|
||||
for coll_handle in body.colliders() {
|
||||
let collider = &mut colliders[*coll_handle];
|
||||
collider.set_position_debug(pos * collider.delta());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
694
src_testbed/engine.rs
Normal file
694
src_testbed/engine.rs
Normal file
@@ -0,0 +1,694 @@
|
||||
#[cfg(feature = "dim3")]
|
||||
use kiss3d::camera::ArcBall as Camera;
|
||||
#[cfg(feature = "dim2")]
|
||||
use kiss3d::planar_camera::Sidescroll as Camera;
|
||||
use kiss3d::window::Window;
|
||||
|
||||
use na::Point3;
|
||||
|
||||
use crate::math::Point;
|
||||
use crate::objects::ball::Ball;
|
||||
use crate::objects::box_node::Box as BoxNode;
|
||||
use crate::objects::convex::Convex;
|
||||
use crate::objects::heightfield::HeightField;
|
||||
use crate::objects::node::{GraphicsNode, Node};
|
||||
use rapier::dynamics::{RigidBodyHandle, RigidBodySet};
|
||||
use rapier::geometry::{Collider, ColliderHandle, ColliderSet, Shape};
|
||||
//use crate::objects::capsule::Capsule;
|
||||
//use crate::objects::convex::Convex;
|
||||
//#[cfg(feature = "fluids")]
|
||||
//use crate::objects::fluid::Fluid as FluidNode;
|
||||
//#[cfg(feature = "dim3")]
|
||||
//use crate::objects::mesh::Mesh;
|
||||
//use crate::objects::plane::Plane;
|
||||
//#[cfg(feature = "dim2")]
|
||||
//use crate::objects::polyline::Polyline;
|
||||
//#[cfg(feature = "fluids")]
|
||||
//use crate::objects::FluidRenderingMode;
|
||||
use crate::objects::capsule::Capsule;
|
||||
use crate::objects::mesh::Mesh;
|
||||
use rand::{Rng, SeedableRng};
|
||||
use rand_pcg::Pcg32;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub trait GraphicsWindow {
|
||||
fn remove_graphics_node(&mut self, node: &mut GraphicsNode);
|
||||
fn draw_graphics_line(&mut self, p1: &Point<f32>, p2: &Point<f32>, color: &Point3<f32>);
|
||||
}
|
||||
|
||||
impl GraphicsWindow for Window {
|
||||
fn remove_graphics_node(&mut self, node: &mut GraphicsNode) {
|
||||
#[cfg(feature = "dim2")]
|
||||
self.remove_planar_node(node);
|
||||
#[cfg(feature = "dim3")]
|
||||
self.remove_node(node);
|
||||
}
|
||||
|
||||
fn draw_graphics_line(&mut self, p1: &Point<f32>, p2: &Point<f32>, color: &Point3<f32>) {
|
||||
#[cfg(feature = "dim2")]
|
||||
self.draw_planar_line(p1, p2, color);
|
||||
#[cfg(feature = "dim3")]
|
||||
self.draw_line(p1, p2, color);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GraphicsManager {
|
||||
rand: Pcg32,
|
||||
b2sn: HashMap<RigidBodyHandle, Vec<Node>>,
|
||||
#[cfg(feature = "fluids")]
|
||||
f2sn: HashMap<FluidHandle, FluidNode>,
|
||||
#[cfg(feature = "fluids")]
|
||||
boundary2sn: HashMap<BoundaryHandle, FluidNode>,
|
||||
b2color: HashMap<RigidBodyHandle, Point3<f32>>,
|
||||
b2wireframe: HashMap<RigidBodyHandle, bool>,
|
||||
#[cfg(feature = "fluids")]
|
||||
f2color: HashMap<FluidHandle, Point3<f32>>,
|
||||
ground_color: Point3<f32>,
|
||||
camera: Camera,
|
||||
ground_handle: Option<RigidBodyHandle>,
|
||||
#[cfg(feature = "fluids")]
|
||||
fluid_rendering_mode: FluidRenderingMode,
|
||||
#[cfg(feature = "fluids")]
|
||||
render_boundary_particles: bool,
|
||||
}
|
||||
|
||||
impl GraphicsManager {
|
||||
pub fn new() -> GraphicsManager {
|
||||
let mut camera;
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
{
|
||||
camera = Camera::new(Point3::new(10.0, 10.0, 10.0), Point3::new(0.0, 0.0, 0.0));
|
||||
camera.set_rotate_modifiers(Some(kiss3d::event::Modifiers::Control));
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
{
|
||||
camera = Camera::new();
|
||||
camera.set_zoom(50.0);
|
||||
}
|
||||
|
||||
GraphicsManager {
|
||||
camera,
|
||||
rand: Pcg32::seed_from_u64(0),
|
||||
b2sn: HashMap::new(),
|
||||
#[cfg(feature = "fluids")]
|
||||
f2sn: HashMap::new(),
|
||||
#[cfg(feature = "fluids")]
|
||||
boundary2sn: HashMap::new(),
|
||||
b2color: HashMap::new(),
|
||||
#[cfg(feature = "fluids")]
|
||||
f2color: HashMap::new(),
|
||||
ground_color: Point3::new(0.5, 0.5, 0.5),
|
||||
b2wireframe: HashMap::new(),
|
||||
ground_handle: None,
|
||||
#[cfg(feature = "fluids")]
|
||||
fluid_rendering_mode: FluidRenderingMode::StaticColor,
|
||||
#[cfg(feature = "fluids")]
|
||||
render_boundary_particles: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_ground_handle(&mut self, handle: Option<RigidBodyHandle>) {
|
||||
self.ground_handle = handle
|
||||
}
|
||||
|
||||
#[cfg(feature = "fluids")]
|
||||
pub fn set_fluid_rendering_mode(&mut self, mode: FluidRenderingMode) {
|
||||
self.fluid_rendering_mode = mode;
|
||||
}
|
||||
|
||||
#[cfg(feature = "fluids")]
|
||||
pub fn enable_boundary_particles_rendering(&mut self, enabled: bool) {
|
||||
self.render_boundary_particles = enabled;
|
||||
|
||||
for sn in self.boundary2sn.values_mut() {
|
||||
sn.scene_node_mut().set_visible(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self, window: &mut Window) {
|
||||
for sns in self.b2sn.values_mut() {
|
||||
for sn in sns.iter_mut() {
|
||||
if let Some(node) = sn.scene_node_mut() {
|
||||
window.remove_graphics_node(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "fluids")]
|
||||
for sn in self.f2sn.values_mut().chain(self.boundary2sn.values_mut()) {
|
||||
let node = sn.scene_node_mut();
|
||||
window.remove_graphics_node(node);
|
||||
}
|
||||
|
||||
self.b2sn.clear();
|
||||
#[cfg(feature = "fluids")]
|
||||
self.f2sn.clear();
|
||||
#[cfg(feature = "fluids")]
|
||||
self.boundary2sn.clear();
|
||||
self.b2color.clear();
|
||||
self.b2wireframe.clear();
|
||||
self.rand = Pcg32::seed_from_u64(0);
|
||||
}
|
||||
|
||||
pub fn remove_body_nodes(&mut self, window: &mut Window, body: RigidBodyHandle) {
|
||||
if let Some(sns) = self.b2sn.get_mut(&body) {
|
||||
for sn in sns.iter_mut() {
|
||||
if let Some(node) = sn.scene_node_mut() {
|
||||
window.remove_graphics_node(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.b2sn.remove(&body);
|
||||
}
|
||||
|
||||
#[cfg(feature = "fluids")]
|
||||
pub fn set_fluid_color(&mut self, f: FluidHandle, color: Point3<f32>) {
|
||||
self.f2color.insert(f, color);
|
||||
|
||||
if let Some(n) = self.f2sn.get_mut(&f) {
|
||||
n.set_color(color)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_body_color(&mut self, b: RigidBodyHandle, color: Point3<f32>) {
|
||||
self.b2color.insert(b, color);
|
||||
|
||||
if let Some(ns) = self.b2sn.get_mut(&b) {
|
||||
for n in ns.iter_mut() {
|
||||
n.set_color(color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_body_wireframe(&mut self, b: RigidBodyHandle, enabled: bool) {
|
||||
self.b2wireframe.insert(b, enabled);
|
||||
|
||||
if let Some(ns) = self.b2sn.get_mut(&b) {
|
||||
for n in ns.iter_mut().filter_map(|n| n.scene_node_mut()) {
|
||||
if enabled {
|
||||
n.set_surface_rendering_activation(true);
|
||||
n.set_lines_width(1.0);
|
||||
} else {
|
||||
n.set_surface_rendering_activation(false);
|
||||
n.set_lines_width(1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_wireframe_mode(&mut self, colliders: &ColliderSet, enabled: bool) {
|
||||
for n in self.b2sn.values_mut().flat_map(|val| val.iter_mut()) {
|
||||
let force_wireframe = if let Some(collider) = colliders.get(n.collider()) {
|
||||
collider.is_sensor()
|
||||
|| self
|
||||
.b2wireframe
|
||||
.get(&collider.parent())
|
||||
.cloned()
|
||||
.unwrap_or(false)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if let Some(node) = n.scene_node_mut() {
|
||||
if force_wireframe || enabled {
|
||||
node.set_lines_width(1.0);
|
||||
node.set_surface_rendering_activation(false);
|
||||
} else {
|
||||
node.set_lines_width(0.0);
|
||||
node.set_surface_rendering_activation(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_color(rng: &mut Pcg32) -> Point3<f32> {
|
||||
let mut color: Point3<f32> = rng.gen();
|
||||
color *= 1.5;
|
||||
color.x = color.x.min(1.0);
|
||||
color.y = color.y.min(1.0);
|
||||
color.z = color.z.min(1.0);
|
||||
color
|
||||
}
|
||||
|
||||
fn alloc_color(&mut self, handle: RigidBodyHandle, is_static: bool) -> Point3<f32> {
|
||||
let mut color = self.ground_color;
|
||||
|
||||
if !is_static {
|
||||
match self.b2color.get(&handle).cloned() {
|
||||
Some(c) => color = c,
|
||||
None => color = Self::gen_color(&mut self.rand),
|
||||
}
|
||||
}
|
||||
|
||||
self.set_body_color(handle, color);
|
||||
|
||||
color
|
||||
}
|
||||
|
||||
#[cfg(feature = "fluids")]
|
||||
pub fn add_fluid(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
handle: FluidHandle,
|
||||
fluid: &Fluid<f32>,
|
||||
particle_radius: f32,
|
||||
) {
|
||||
let rand = &mut self.rand;
|
||||
let color = *self
|
||||
.f2color
|
||||
.entry(handle)
|
||||
.or_insert_with(|| Self::gen_color(rand));
|
||||
|
||||
self.add_fluid_with_color(window, handle, fluid, particle_radius, color);
|
||||
}
|
||||
|
||||
#[cfg(feature = "fluids")]
|
||||
pub fn add_boundary(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
handle: BoundaryHandle,
|
||||
boundary: &Boundary<f32>,
|
||||
particle_radius: f32,
|
||||
) {
|
||||
let color = self.ground_color;
|
||||
let node = FluidNode::new(particle_radius, &boundary.positions, color, window);
|
||||
self.boundary2sn.insert(handle, node);
|
||||
}
|
||||
|
||||
#[cfg(feature = "fluids")]
|
||||
pub fn add_fluid_with_color(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
handle: FluidHandle,
|
||||
fluid: &Fluid<f32>,
|
||||
particle_radius: f32,
|
||||
color: Point3<f32>,
|
||||
) {
|
||||
let node = FluidNode::new(particle_radius, &fluid.positions, color, window);
|
||||
self.f2sn.insert(handle, node);
|
||||
}
|
||||
|
||||
pub fn add(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
handle: RigidBodyHandle,
|
||||
bodies: &RigidBodySet,
|
||||
colliders: &ColliderSet,
|
||||
) {
|
||||
let body = bodies.get(handle).unwrap();
|
||||
|
||||
let color = self
|
||||
.b2color
|
||||
.get(&handle)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| self.alloc_color(handle, !body.is_dynamic()));
|
||||
|
||||
self.add_with_color(window, handle, bodies, colliders, color)
|
||||
}
|
||||
|
||||
pub fn add_with_color(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
handle: RigidBodyHandle,
|
||||
bodies: &RigidBodySet,
|
||||
colliders: &ColliderSet,
|
||||
color: Point3<f32>,
|
||||
) {
|
||||
// let body = bodies.get(handle).unwrap();
|
||||
let mut new_nodes = Vec::new();
|
||||
|
||||
for collider_handle in bodies[handle].colliders() {
|
||||
let collider = &colliders[*collider_handle];
|
||||
self.add_collider(window, *collider_handle, collider, color, &mut new_nodes);
|
||||
}
|
||||
|
||||
new_nodes.iter_mut().for_each(|n| n.update(colliders));
|
||||
|
||||
for node in new_nodes.iter_mut().filter_map(|n| n.scene_node_mut()) {
|
||||
if self.b2wireframe.get(&handle).cloned() == Some(true) {
|
||||
node.set_lines_width(1.0);
|
||||
node.set_surface_rendering_activation(false);
|
||||
} else {
|
||||
node.set_lines_width(0.0);
|
||||
node.set_surface_rendering_activation(true);
|
||||
}
|
||||
}
|
||||
|
||||
let nodes = self.b2sn.entry(handle).or_insert_with(Vec::new);
|
||||
nodes.append(&mut new_nodes);
|
||||
}
|
||||
|
||||
fn add_collider(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
handle: ColliderHandle,
|
||||
collider: &Collider,
|
||||
color: Point3<f32>,
|
||||
out: &mut Vec<Node>,
|
||||
) {
|
||||
match collider.shape() {
|
||||
Shape::Ball(ball) => {
|
||||
out.push(Node::Ball(Ball::new(handle, ball.radius, color, window)))
|
||||
}
|
||||
Shape::Polygon(poly) => out.push(Node::Convex(Convex::new(
|
||||
handle,
|
||||
poly.vertices().to_vec(),
|
||||
color,
|
||||
window,
|
||||
))),
|
||||
Shape::Cuboid(cuboid) => out.push(Node::Box(BoxNode::new(
|
||||
handle,
|
||||
cuboid.half_extents,
|
||||
color,
|
||||
window,
|
||||
))),
|
||||
Shape::Capsule(capsule) => {
|
||||
out.push(Node::Capsule(Capsule::new(handle, capsule, color, window)))
|
||||
}
|
||||
Shape::Triangle(triangle) => out.push(Node::Mesh(Mesh::new(
|
||||
handle,
|
||||
vec![triangle.a, triangle.b, triangle.c],
|
||||
vec![Point3::new(0, 1, 2)],
|
||||
color,
|
||||
window,
|
||||
))),
|
||||
Shape::Trimesh(trimesh) => out.push(Node::Mesh(Mesh::new(
|
||||
handle,
|
||||
trimesh.vertices().to_vec(),
|
||||
trimesh
|
||||
.indices()
|
||||
.iter()
|
||||
.map(|idx| na::convert(*idx))
|
||||
.collect(),
|
||||
color,
|
||||
window,
|
||||
))),
|
||||
Shape::HeightField(heightfield) => out.push(Node::HeightField(HeightField::new(
|
||||
handle,
|
||||
heightfield,
|
||||
color,
|
||||
window,
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn add_plane(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
object: DefaultColliderHandle,
|
||||
colliders: &DefaultColliderSet<f32>,
|
||||
shape: &shape::Plane<f32>,
|
||||
color: Point3<f32>,
|
||||
out: &mut Vec<Node>,
|
||||
) {
|
||||
let pos = colliders.get(object).unwrap().position();
|
||||
let position = Point::from(pos.translation.vector);
|
||||
let normal = pos * shape.normal();
|
||||
|
||||
out.push(Node::Plane(Plane::new(
|
||||
object, colliders, &position, &normal, color, window,
|
||||
)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
fn add_polyline(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
object: DefaultColliderHandle,
|
||||
colliders: &DefaultColliderSet<f32>,
|
||||
delta: Isometry<f32>,
|
||||
shape: &shape::Polyline<f32>,
|
||||
color: Point3<f32>,
|
||||
out: &mut Vec<Node>,
|
||||
) {
|
||||
let vertices = shape.points().to_vec();
|
||||
let indices = shape.edges().iter().map(|e| e.indices).collect();
|
||||
|
||||
out.push(Node::Polyline(Polyline::new(
|
||||
object, colliders, delta, vertices, indices, color, window,
|
||||
)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
fn add_mesh(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
object: DefaultColliderHandle,
|
||||
colliders: &DefaultColliderSet<f32>,
|
||||
delta: Isometry<f32>,
|
||||
shape: &TriMesh<f32>,
|
||||
color: Point3<f32>,
|
||||
out: &mut Vec<Node>,
|
||||
) {
|
||||
let points = shape.points();
|
||||
let faces = shape.faces();
|
||||
|
||||
let is = faces
|
||||
.iter()
|
||||
.map(|f| Point3::new(f.indices.x as u32, f.indices.y as u32, f.indices.z as u32))
|
||||
.collect();
|
||||
|
||||
out.push(Node::Mesh(Mesh::new(
|
||||
object,
|
||||
colliders,
|
||||
delta,
|
||||
points.to_vec(),
|
||||
is,
|
||||
color,
|
||||
window,
|
||||
)))
|
||||
}
|
||||
|
||||
fn add_heightfield(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
object: DefaultColliderHandle,
|
||||
colliders: &DefaultColliderSet<f32>,
|
||||
delta: Isometry<f32>,
|
||||
heightfield: &shape::HeightField<f32>,
|
||||
color: Point3<f32>,
|
||||
out: &mut Vec<Node>,
|
||||
) {
|
||||
out.push(Node::HeightField(HeightField::new(
|
||||
object,
|
||||
colliders,
|
||||
delta,
|
||||
heightfield,
|
||||
color,
|
||||
window,
|
||||
)))
|
||||
}
|
||||
|
||||
fn add_capsule(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
object: DefaultColliderHandle,
|
||||
colliders: &DefaultColliderSet<f32>,
|
||||
delta: Isometry<f32>,
|
||||
shape: &shape::Capsule<f32>,
|
||||
color: Point3<f32>,
|
||||
out: &mut Vec<Node>,
|
||||
) {
|
||||
let margin = colliders.get(object).unwrap().margin();
|
||||
out.push(Node::Capsule(Capsule::new(
|
||||
object,
|
||||
colliders,
|
||||
delta,
|
||||
shape.radius() + margin,
|
||||
shape.height(),
|
||||
color,
|
||||
window,
|
||||
)))
|
||||
}
|
||||
|
||||
fn add_ball(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
object: DefaultColliderHandle,
|
||||
colliders: &DefaultColliderSet<f32>,
|
||||
delta: Isometry<f32>,
|
||||
shape: &shape::Ball<f32>,
|
||||
color: Point3<f32>,
|
||||
out: &mut Vec<Node>,
|
||||
) {
|
||||
let margin = colliders.get(object).unwrap().margin();
|
||||
out.push(Node::Ball(Ball::new(
|
||||
object,
|
||||
colliders,
|
||||
delta,
|
||||
shape.radius() + margin,
|
||||
color,
|
||||
window,
|
||||
)))
|
||||
}
|
||||
|
||||
fn add_box(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
object: DefaultColliderHandle,
|
||||
colliders: &DefaultColliderSet<f32>,
|
||||
delta: Isometry<f32>,
|
||||
shape: &Cuboid<f32>,
|
||||
color: Point3<f32>,
|
||||
out: &mut Vec<Node>,
|
||||
) {
|
||||
let margin = colliders.get(object).unwrap().margin();
|
||||
|
||||
out.push(Node::Box(Box::new(
|
||||
object,
|
||||
colliders,
|
||||
delta,
|
||||
shape.half_extents() + Vector::repeat(margin),
|
||||
color,
|
||||
window,
|
||||
)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
fn add_convex(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
object: DefaultColliderHandle,
|
||||
colliders: &DefaultColliderSet<f32>,
|
||||
delta: Isometry<f32>,
|
||||
shape: &ConvexPolygon<f32>,
|
||||
color: Point3<f32>,
|
||||
out: &mut Vec<Node>,
|
||||
) {
|
||||
let points = shape.points();
|
||||
|
||||
out.push(Node::Convex(Convex::new(
|
||||
object,
|
||||
colliders,
|
||||
delta,
|
||||
points.to_vec(),
|
||||
color,
|
||||
window,
|
||||
)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
fn add_convex(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
object: DefaultColliderHandle,
|
||||
colliders: &DefaultColliderSet<f32>,
|
||||
delta: Isometry<f32>,
|
||||
shape: &ConvexHull<f32>,
|
||||
color: Point3<f32>,
|
||||
out: &mut Vec<Node>,
|
||||
) {
|
||||
let mut chull = transformation::convex_hull(shape.points());
|
||||
chull.replicate_vertices();
|
||||
chull.recompute_normals();
|
||||
|
||||
out.push(Node::Convex(Convex::new(
|
||||
object, colliders, delta, &chull, color, window,
|
||||
)))
|
||||
}
|
||||
*/
|
||||
|
||||
#[cfg(feature = "fluids")]
|
||||
pub fn draw_fluids(&mut self, liquid_world: &LiquidWorld<f32>) {
|
||||
for (i, fluid) in liquid_world.fluids().iter() {
|
||||
if let Some(node) = self.f2sn.get_mut(&i) {
|
||||
node.update_with_fluid(fluid, self.fluid_rendering_mode)
|
||||
}
|
||||
}
|
||||
|
||||
if self.render_boundary_particles {
|
||||
for (i, boundary) in liquid_world.boundaries().iter() {
|
||||
if let Some(node) = self.boundary2sn.get_mut(&i) {
|
||||
node.update_with_boundary(boundary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&mut self, colliders: &ColliderSet, window: &mut Window) {
|
||||
// use kiss3d::camera::Camera;
|
||||
// println!(
|
||||
// "camera eye {:?}, at: {:?}",
|
||||
// self.camera.eye(),
|
||||
// self.camera.at()
|
||||
// );
|
||||
for (_, ns) in self.b2sn.iter_mut() {
|
||||
for n in ns.iter_mut() {
|
||||
n.update(colliders);
|
||||
n.draw(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn draw_positions(&mut self, window: &mut Window, rbs: &RigidBodies<f32>) {
|
||||
// for (_, ns) in self.b2sn.iter_mut() {
|
||||
// for n in ns.iter_mut() {
|
||||
// let object = n.object();
|
||||
// let rb = rbs.get(object).expect("Rigid body not found.");
|
||||
|
||||
// // if let WorldObjectBorrowed::RigidBody(rb) = object {
|
||||
// let t = rb.position();
|
||||
// let center = rb.center_of_mass();
|
||||
|
||||
// let rotmat = t.rotation.to_rotation_matrix().unwrap();
|
||||
// let x = rotmat.column(0) * 0.25f32;
|
||||
// let y = rotmat.column(1) * 0.25f32;
|
||||
// let z = rotmat.column(2) * 0.25f32;
|
||||
|
||||
// window.draw_line(center, &(*center + x), &Point3::new(1.0, 0.0, 0.0));
|
||||
// window.draw_line(center, &(*center + y), &Point3::new(0.0, 1.0, 0.0));
|
||||
// window.draw_line(center, &(*center + z), &Point3::new(0.0, 0.0, 1.0));
|
||||
// // }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn camera(&self) -> &Camera {
|
||||
&self.camera
|
||||
}
|
||||
|
||||
pub fn camera_mut(&mut self) -> &mut Camera {
|
||||
&mut self.camera
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
pub fn look_at(&mut self, eye: Point<f32>, at: Point<f32>) {
|
||||
self.camera.look_at(eye, at);
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
pub fn look_at(&mut self, at: Point<f32>, zoom: f32) {
|
||||
self.camera.look_at(at, zoom);
|
||||
}
|
||||
|
||||
pub fn body_nodes(&self, handle: RigidBodyHandle) -> Option<&Vec<Node>> {
|
||||
self.b2sn.get(&handle)
|
||||
}
|
||||
|
||||
pub fn body_nodes_mut(&mut self, handle: RigidBodyHandle) -> Option<&mut Vec<Node>> {
|
||||
self.b2sn.get_mut(&handle)
|
||||
}
|
||||
|
||||
pub fn nodes(&self) -> impl Iterator<Item = &Node> {
|
||||
self.b2sn.values().flat_map(|val| val.iter())
|
||||
}
|
||||
|
||||
pub fn nodes_mut(&mut self) -> impl Iterator<Item = &mut Node> {
|
||||
self.b2sn.values_mut().flat_map(|val| val.iter_mut())
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
pub fn set_up_axis(&mut self, up_axis: na::Vector3<f32>) {
|
||||
self.camera.set_up_axis(up_axis);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GraphicsManager {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
52
src_testbed/lib.rs
Normal file
52
src_testbed/lib.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
#[macro_use]
|
||||
extern crate kiss3d;
|
||||
extern crate nalgebra as na;
|
||||
#[cfg(feature = "dim2")]
|
||||
extern crate ncollide2d as ncollide;
|
||||
#[cfg(feature = "dim3")]
|
||||
extern crate ncollide3d as ncollide;
|
||||
#[cfg(all(feature = "dim2", feature = "other-backends"))]
|
||||
extern crate nphysics2d as nphysics;
|
||||
#[cfg(all(feature = "dim3", feature = "other-backends"))]
|
||||
extern crate nphysics3d as nphysics;
|
||||
#[cfg(feature = "dim2")]
|
||||
extern crate rapier2d as rapier;
|
||||
#[cfg(feature = "dim3")]
|
||||
extern crate rapier3d as rapier;
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
#[cfg(feature = "log")]
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub use crate::engine::GraphicsManager;
|
||||
pub use crate::testbed::Testbed;
|
||||
|
||||
#[cfg(all(feature = "dim2", feature = "other-backends"))]
|
||||
mod box2d_backend;
|
||||
mod engine;
|
||||
#[cfg(feature = "other-backends")]
|
||||
mod nphysics_backend;
|
||||
pub mod objects;
|
||||
#[cfg(all(feature = "dim3", feature = "other-backends"))]
|
||||
mod physx_backend;
|
||||
mod testbed;
|
||||
mod ui;
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
pub mod math {
|
||||
pub type Isometry<N> = na::Isometry2<N>;
|
||||
pub type Vector<N> = na::Vector2<N>;
|
||||
pub type Point<N> = na::Point2<N>;
|
||||
pub type Translation<N> = na::Translation2<N>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
pub mod math {
|
||||
pub type Isometry<N> = na::Isometry3<N>;
|
||||
pub type Vector<N> = na::Vector3<N>;
|
||||
pub type Point<N> = na::Point3<N>;
|
||||
pub type Translation<N> = na::Translation3<N>;
|
||||
}
|
||||
212
src_testbed/nphysics_backend.rs
Normal file
212
src_testbed/nphysics_backend.rs
Normal file
@@ -0,0 +1,212 @@
|
||||
use ncollide::shape::{Ball, Capsule, Cuboid, ShapeHandle};
|
||||
use nphysics::force_generator::DefaultForceGeneratorSet;
|
||||
use nphysics::joint::{
|
||||
DefaultJointConstraintSet, FixedConstraint, PrismaticConstraint, RevoluteConstraint,
|
||||
};
|
||||
use nphysics::object::{
|
||||
BodyPartHandle, ColliderDesc, DefaultBodyHandle, DefaultBodySet, DefaultColliderSet,
|
||||
RigidBodyDesc,
|
||||
};
|
||||
use nphysics::world::{DefaultGeometricalWorld, DefaultMechanicalWorld};
|
||||
use rapier::counters::Counters;
|
||||
use rapier::dynamics::{
|
||||
IntegrationParameters, JointParams, JointSet, RigidBodyHandle, RigidBodySet,
|
||||
};
|
||||
use rapier::geometry::{Collider, ColliderSet, Shape};
|
||||
use rapier::math::{Isometry, Vector};
|
||||
use std::collections::HashMap;
|
||||
#[cfg(feature = "dim3")]
|
||||
use {ncollide::shape::TriMesh, nphysics::joint::BallConstraint};
|
||||
|
||||
pub struct NPhysicsWorld {
|
||||
rapier2nphysics: HashMap<RigidBodyHandle, DefaultBodyHandle>,
|
||||
mechanical_world: DefaultMechanicalWorld<f32>,
|
||||
geometrical_world: DefaultGeometricalWorld<f32>,
|
||||
bodies: DefaultBodySet<f32>,
|
||||
colliders: DefaultColliderSet<f32>,
|
||||
joints: DefaultJointConstraintSet<f32>,
|
||||
force_generators: DefaultForceGeneratorSet<f32>,
|
||||
}
|
||||
|
||||
impl NPhysicsWorld {
|
||||
pub fn from_rapier(
|
||||
gravity: Vector<f32>,
|
||||
bodies: &RigidBodySet,
|
||||
colliders: &ColliderSet,
|
||||
joints: &JointSet,
|
||||
) -> Self {
|
||||
let mut rapier2nphysics = HashMap::new();
|
||||
|
||||
let mechanical_world = DefaultMechanicalWorld::new(gravity);
|
||||
let geometrical_world = DefaultGeometricalWorld::new();
|
||||
let mut nphysics_bodies = DefaultBodySet::new();
|
||||
let mut nphysics_colliders = DefaultColliderSet::new();
|
||||
let mut nphysics_joints = DefaultJointConstraintSet::new();
|
||||
let force_generators = DefaultForceGeneratorSet::new();
|
||||
|
||||
for (rapier_handle, rb) in bodies.iter() {
|
||||
// let material = physics.create_material(rb.collider.friction, rb.collider.friction, 0.0);
|
||||
let nphysics_rb = RigidBodyDesc::new().position(rb.position).build();
|
||||
let nphysics_rb_handle = nphysics_bodies.insert(nphysics_rb);
|
||||
|
||||
rapier2nphysics.insert(rapier_handle, nphysics_rb_handle);
|
||||
}
|
||||
|
||||
for (_, collider) in colliders.iter() {
|
||||
let parent = &bodies[collider.parent()];
|
||||
let nphysics_rb_handle = rapier2nphysics[&collider.parent()];
|
||||
if let Some(collider) =
|
||||
nphysics_collider_from_rapier_collider(&collider, parent.is_dynamic())
|
||||
{
|
||||
let nphysics_collider = collider.build(BodyPartHandle(nphysics_rb_handle, 0));
|
||||
nphysics_colliders.insert(nphysics_collider);
|
||||
} else {
|
||||
eprintln!("Creating shape unknown to the nphysics backend.")
|
||||
}
|
||||
}
|
||||
|
||||
for joint in joints.iter() {
|
||||
let b1 = BodyPartHandle(rapier2nphysics[&joint.body1], 0);
|
||||
let b2 = BodyPartHandle(rapier2nphysics[&joint.body2], 0);
|
||||
|
||||
match &joint.params {
|
||||
JointParams::FixedJoint(params) => {
|
||||
let c = FixedConstraint::new(
|
||||
b1,
|
||||
b2,
|
||||
params.local_anchor1.translation.vector.into(),
|
||||
params.local_anchor1.rotation,
|
||||
params.local_anchor2.translation.vector.into(),
|
||||
params.local_anchor2.rotation,
|
||||
);
|
||||
nphysics_joints.insert(c);
|
||||
}
|
||||
#[cfg(feature = "dim3")]
|
||||
JointParams::BallJoint(params) => {
|
||||
let c = BallConstraint::new(b1, b2, params.local_anchor1, params.local_anchor2);
|
||||
nphysics_joints.insert(c);
|
||||
}
|
||||
#[cfg(feature = "dim2")]
|
||||
JointParams::BallJoint(params) => {
|
||||
let c =
|
||||
RevoluteConstraint::new(b1, b2, params.local_anchor1, params.local_anchor2);
|
||||
nphysics_joints.insert(c);
|
||||
}
|
||||
#[cfg(feature = "dim3")]
|
||||
JointParams::RevoluteJoint(params) => {
|
||||
let c = RevoluteConstraint::new(
|
||||
b1,
|
||||
b2,
|
||||
params.local_anchor1,
|
||||
params.local_axis1,
|
||||
params.local_anchor2,
|
||||
params.local_axis2,
|
||||
);
|
||||
nphysics_joints.insert(c);
|
||||
}
|
||||
JointParams::PrismaticJoint(params) => {
|
||||
let mut c = PrismaticConstraint::new(
|
||||
b1,
|
||||
b2,
|
||||
params.local_anchor1,
|
||||
params.local_axis1(),
|
||||
params.local_anchor2,
|
||||
);
|
||||
|
||||
if params.limits_enabled {
|
||||
c.enable_min_offset(params.limits[0]);
|
||||
c.enable_max_offset(params.limits[1]);
|
||||
}
|
||||
|
||||
nphysics_joints.insert(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
rapier2nphysics,
|
||||
mechanical_world,
|
||||
geometrical_world,
|
||||
bodies: nphysics_bodies,
|
||||
colliders: nphysics_colliders,
|
||||
joints: nphysics_joints,
|
||||
force_generators,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step(&mut self, counters: &mut Counters, params: &IntegrationParameters) {
|
||||
self.mechanical_world
|
||||
.integration_parameters
|
||||
.max_position_iterations = params.max_position_iterations;
|
||||
self.mechanical_world
|
||||
.integration_parameters
|
||||
.max_velocity_iterations = params.max_velocity_iterations;
|
||||
self.mechanical_world
|
||||
.integration_parameters
|
||||
.set_dt(params.dt());
|
||||
|
||||
counters.step_started();
|
||||
self.mechanical_world.step(
|
||||
&mut self.geometrical_world,
|
||||
&mut self.bodies,
|
||||
&mut self.colliders,
|
||||
&mut self.joints,
|
||||
&mut self.force_generators,
|
||||
);
|
||||
counters.step_completed();
|
||||
}
|
||||
|
||||
pub fn sync(&self, bodies: &mut RigidBodySet, colliders: &mut ColliderSet) {
|
||||
for (rapier_handle, nphysics_handle) in self.rapier2nphysics.iter() {
|
||||
let mut rb = bodies.get_mut(*rapier_handle).unwrap();
|
||||
let ra = self.bodies.rigid_body(*nphysics_handle).unwrap();
|
||||
let pos = *ra.position();
|
||||
rb.set_position(pos);
|
||||
|
||||
for coll_handle in rb.colliders() {
|
||||
let collider = &mut colliders[*coll_handle];
|
||||
collider.set_position_debug(pos * collider.delta());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn nphysics_collider_from_rapier_collider(
|
||||
collider: &Collider,
|
||||
is_dynamic: bool,
|
||||
) -> Option<ColliderDesc<f32>> {
|
||||
let margin = ColliderDesc::<f32>::default_margin();
|
||||
let mut pos = Isometry::identity();
|
||||
|
||||
let shape = match collider.shape() {
|
||||
Shape::Cuboid(cuboid) => {
|
||||
ShapeHandle::new(Cuboid::new(cuboid.half_extents.map(|e| e - margin)))
|
||||
}
|
||||
Shape::Ball(ball) => ShapeHandle::new(Ball::new(ball.radius - margin)),
|
||||
Shape::Capsule(capsule) => {
|
||||
pos = capsule.transform_wrt_y();
|
||||
ShapeHandle::new(Capsule::new(capsule.half_height(), capsule.radius))
|
||||
}
|
||||
Shape::HeightField(heightfield) => ShapeHandle::new(heightfield.clone()),
|
||||
#[cfg(feature = "dim3")]
|
||||
Shape::Trimesh(trimesh) => ShapeHandle::new(TriMesh::new(
|
||||
trimesh.vertices().to_vec(),
|
||||
trimesh
|
||||
.indices()
|
||||
.iter()
|
||||
.map(|idx| na::convert(*idx))
|
||||
.collect(),
|
||||
None,
|
||||
)),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let density = if is_dynamic { collider.density() } else { 0.0 };
|
||||
|
||||
Some(
|
||||
ColliderDesc::new(shape)
|
||||
.position(pos)
|
||||
.density(density)
|
||||
.sensor(collider.is_sensor()),
|
||||
)
|
||||
}
|
||||
73
src_testbed/objects/ball.rs
Normal file
73
src_testbed/objects/ball.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use crate::objects::node::{self, GraphicsNode};
|
||||
use kiss3d::window::Window;
|
||||
use na::Point3;
|
||||
use rapier::geometry::{ColliderHandle, ColliderSet};
|
||||
use rapier::math::Isometry;
|
||||
|
||||
pub struct Ball {
|
||||
color: Point3<f32>,
|
||||
base_color: Point3<f32>,
|
||||
gfx: GraphicsNode,
|
||||
collider: ColliderHandle,
|
||||
}
|
||||
|
||||
impl Ball {
|
||||
pub fn new(
|
||||
collider: ColliderHandle,
|
||||
radius: f32,
|
||||
color: Point3<f32>,
|
||||
window: &mut Window,
|
||||
) -> Ball {
|
||||
#[cfg(feature = "dim2")]
|
||||
let node = window.add_circle(radius);
|
||||
#[cfg(feature = "dim3")]
|
||||
let node = window.add_sphere(radius);
|
||||
|
||||
let mut res = Ball {
|
||||
color,
|
||||
base_color: color,
|
||||
gfx: node,
|
||||
collider,
|
||||
};
|
||||
|
||||
// res.gfx.set_texture_from_file(&Path::new("media/kitten.png"), "kitten");
|
||||
res.gfx.set_color(color.x, color.y, color.z);
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select(&mut self) {
|
||||
self.color = Point3::new(1.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
pub fn unselect(&mut self) {
|
||||
self.color = self.base_color;
|
||||
}
|
||||
|
||||
pub fn set_color(&mut self, color: Point3<f32>) {
|
||||
self.gfx.set_color(color.x, color.y, color.z);
|
||||
self.color = color;
|
||||
self.base_color = color;
|
||||
}
|
||||
|
||||
pub fn update(&mut self, colliders: &ColliderSet) {
|
||||
node::update_scene_node(
|
||||
&mut self.gfx,
|
||||
colliders,
|
||||
self.collider,
|
||||
&self.color,
|
||||
&Isometry::identity(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn scene_node(&self) -> &GraphicsNode {
|
||||
&self.gfx
|
||||
}
|
||||
|
||||
pub fn scene_node_mut(&mut self) -> &mut GraphicsNode {
|
||||
&mut self.gfx
|
||||
}
|
||||
|
||||
pub fn object(&self) -> ColliderHandle {
|
||||
self.collider
|
||||
}
|
||||
}
|
||||
73
src_testbed/objects/box_node.rs
Normal file
73
src_testbed/objects/box_node.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use crate::objects::node::{self, GraphicsNode};
|
||||
use kiss3d::window;
|
||||
use na::Point3;
|
||||
use rapier::geometry::{ColliderHandle, ColliderSet};
|
||||
use rapier::math::{Isometry, Vector};
|
||||
|
||||
pub struct Box {
|
||||
color: Point3<f32>,
|
||||
base_color: Point3<f32>,
|
||||
gfx: GraphicsNode,
|
||||
collider: ColliderHandle,
|
||||
}
|
||||
|
||||
impl Box {
|
||||
pub fn new(
|
||||
collider: ColliderHandle,
|
||||
half_extents: Vector<f32>,
|
||||
color: Point3<f32>,
|
||||
window: &mut window::Window,
|
||||
) -> Box {
|
||||
let extents = half_extents * 2.0;
|
||||
#[cfg(feature = "dim2")]
|
||||
let node = window.add_rectangle(extents.x, extents.y);
|
||||
#[cfg(feature = "dim3")]
|
||||
let node = window.add_cube(extents.x, extents.y, extents.z);
|
||||
|
||||
let mut res = Box {
|
||||
color,
|
||||
base_color: color,
|
||||
gfx: node,
|
||||
collider,
|
||||
};
|
||||
|
||||
res.gfx.set_color(color.x, color.y, color.z);
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select(&mut self) {
|
||||
self.color = Point3::new(1.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
pub fn unselect(&mut self) {
|
||||
self.color = self.base_color;
|
||||
}
|
||||
|
||||
pub fn set_color(&mut self, color: Point3<f32>) {
|
||||
self.gfx.set_color(color.x, color.y, color.z);
|
||||
self.color = color;
|
||||
self.base_color = color;
|
||||
}
|
||||
|
||||
pub fn update(&mut self, colliders: &ColliderSet) {
|
||||
node::update_scene_node(
|
||||
&mut self.gfx,
|
||||
colliders,
|
||||
self.collider,
|
||||
&self.color,
|
||||
&Isometry::identity(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn scene_node(&self) -> &GraphicsNode {
|
||||
&self.gfx
|
||||
}
|
||||
|
||||
pub fn scene_node_mut(&mut self) -> &mut GraphicsNode {
|
||||
&mut self.gfx
|
||||
}
|
||||
|
||||
pub fn object(&self) -> ColliderHandle {
|
||||
self.collider
|
||||
}
|
||||
}
|
||||
74
src_testbed/objects/capsule.rs
Normal file
74
src_testbed/objects/capsule.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use crate::objects::node::{self, GraphicsNode};
|
||||
use kiss3d::window;
|
||||
use na::Point3;
|
||||
use rapier::geometry::{self, ColliderHandle, ColliderSet};
|
||||
use rapier::math::Isometry;
|
||||
|
||||
pub struct Capsule {
|
||||
color: Point3<f32>,
|
||||
base_color: Point3<f32>,
|
||||
gfx: GraphicsNode,
|
||||
collider: ColliderHandle,
|
||||
}
|
||||
|
||||
impl Capsule {
|
||||
pub fn new(
|
||||
collider: ColliderHandle,
|
||||
capsule: &geometry::Capsule,
|
||||
color: Point3<f32>,
|
||||
window: &mut window::Window,
|
||||
) -> Capsule {
|
||||
let r = capsule.radius;
|
||||
let h = capsule.half_height() * 2.0;
|
||||
#[cfg(feature = "dim2")]
|
||||
let node = window.add_planar_capsule(r, h);
|
||||
#[cfg(feature = "dim3")]
|
||||
let node = window.add_capsule(r, h);
|
||||
|
||||
let mut res = Capsule {
|
||||
color,
|
||||
base_color: color,
|
||||
gfx: node,
|
||||
collider,
|
||||
};
|
||||
|
||||
res.gfx.set_color(color.x, color.y, color.z);
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select(&mut self) {
|
||||
self.color = Point3::new(1.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
pub fn unselect(&mut self) {
|
||||
self.color = self.base_color;
|
||||
}
|
||||
|
||||
pub fn update(&mut self, colliders: &ColliderSet) {
|
||||
node::update_scene_node(
|
||||
&mut self.gfx,
|
||||
colliders,
|
||||
self.collider,
|
||||
&self.color,
|
||||
&Isometry::identity(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn set_color(&mut self, color: Point3<f32>) {
|
||||
self.gfx.set_color(color.x, color.y, color.z);
|
||||
self.color = color;
|
||||
self.base_color = color;
|
||||
}
|
||||
|
||||
pub fn scene_node(&self) -> &GraphicsNode {
|
||||
&self.gfx
|
||||
}
|
||||
|
||||
pub fn scene_node_mut(&mut self) -> &mut GraphicsNode {
|
||||
&mut self.gfx
|
||||
}
|
||||
|
||||
pub fn object(&self) -> ColliderHandle {
|
||||
self.collider
|
||||
}
|
||||
}
|
||||
77
src_testbed/objects/convex.rs
Normal file
77
src_testbed/objects/convex.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
#![allow(warnings)] // TODO: remove this.
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
use crate::math::Vector;
|
||||
use crate::math::{Isometry, Point};
|
||||
use crate::objects::node::{self, GraphicsNode};
|
||||
use kiss3d::window::Window;
|
||||
use na::Point3;
|
||||
use rapier::geometry::{ColliderHandle, ColliderSet};
|
||||
|
||||
pub struct Convex {
|
||||
color: Point3<f32>,
|
||||
base_color: Point3<f32>,
|
||||
gfx: GraphicsNode,
|
||||
body: ColliderHandle,
|
||||
}
|
||||
|
||||
impl Convex {
|
||||
pub fn new(
|
||||
body: ColliderHandle,
|
||||
vertices: Vec<Point<f32>>,
|
||||
color: Point3<f32>,
|
||||
window: &mut Window,
|
||||
) -> Convex {
|
||||
#[cfg(feature = "dim2")]
|
||||
let node = window.add_convex_polygon(vertices, Vector::from_element(1.0));
|
||||
#[cfg(feature = "dim3")]
|
||||
let node = unimplemented!();
|
||||
|
||||
let mut res = Convex {
|
||||
color,
|
||||
base_color: color,
|
||||
gfx: node,
|
||||
body,
|
||||
};
|
||||
|
||||
// res.gfx.set_texture_from_file(&Path::new("media/kitten.png"), "kitten");
|
||||
res.gfx.set_color(color.x, color.y, color.z);
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select(&mut self) {
|
||||
self.color = Point3::new(1.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
pub fn unselect(&mut self) {
|
||||
self.color = self.base_color;
|
||||
}
|
||||
|
||||
pub fn set_color(&mut self, color: Point3<f32>) {
|
||||
self.gfx.set_color(color.x, color.y, color.z);
|
||||
self.color = color;
|
||||
self.base_color = color;
|
||||
}
|
||||
|
||||
pub fn update(&mut self, colliders: &ColliderSet) {
|
||||
node::update_scene_node(
|
||||
&mut self.gfx,
|
||||
colliders,
|
||||
self.body,
|
||||
&self.color,
|
||||
&Isometry::identity(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn scene_node(&self) -> &GraphicsNode {
|
||||
&self.gfx
|
||||
}
|
||||
|
||||
pub fn scene_node_mut(&mut self) -> &mut GraphicsNode {
|
||||
&mut self.gfx
|
||||
}
|
||||
|
||||
pub fn object(&self) -> ColliderHandle {
|
||||
self.body
|
||||
}
|
||||
}
|
||||
120
src_testbed/objects/heightfield.rs
Normal file
120
src_testbed/objects/heightfield.rs
Normal file
@@ -0,0 +1,120 @@
|
||||
#[cfg(feature = "dim3")]
|
||||
use crate::objects::node::{self, GraphicsNode};
|
||||
use kiss3d::window::Window;
|
||||
use na::{self, Point3};
|
||||
use ncollide::shape;
|
||||
#[cfg(feature = "dim3")]
|
||||
use ncollide::transformation::ToTriMesh;
|
||||
use rapier::geometry::{ColliderHandle, ColliderSet};
|
||||
#[cfg(feature = "dim2")]
|
||||
use rapier::math::Point;
|
||||
#[cfg(feature = "dim3")]
|
||||
use rapier::math::Vector;
|
||||
|
||||
pub struct HeightField {
|
||||
color: Point3<f32>,
|
||||
base_color: Point3<f32>,
|
||||
#[cfg(feature = "dim2")]
|
||||
vertices: Vec<Point<f32>>,
|
||||
#[cfg(feature = "dim3")]
|
||||
gfx: GraphicsNode,
|
||||
collider: ColliderHandle,
|
||||
}
|
||||
|
||||
impl HeightField {
|
||||
#[cfg(feature = "dim2")]
|
||||
pub fn new(
|
||||
collider: ColliderHandle,
|
||||
heightfield: &shape::HeightField<f32>,
|
||||
color: Point3<f32>,
|
||||
_: &mut Window,
|
||||
) -> HeightField {
|
||||
let mut vertices = Vec::new();
|
||||
|
||||
for seg in heightfield.segments() {
|
||||
vertices.push(seg.a);
|
||||
vertices.push(seg.b);
|
||||
}
|
||||
|
||||
HeightField {
|
||||
color,
|
||||
base_color: color,
|
||||
vertices,
|
||||
collider,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
pub fn new(
|
||||
collider: ColliderHandle,
|
||||
heightfield: &shape::HeightField<f32>,
|
||||
color: Point3<f32>,
|
||||
window: &mut Window,
|
||||
) -> HeightField {
|
||||
let mesh = heightfield.to_trimesh(());
|
||||
|
||||
let mut res = HeightField {
|
||||
color,
|
||||
base_color: color,
|
||||
gfx: window.add_trimesh(mesh, Vector::repeat(1.0)),
|
||||
collider: collider,
|
||||
};
|
||||
|
||||
res.gfx.enable_backface_culling(false);
|
||||
res.gfx.set_color(color.x, color.y, color.z);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select(&mut self) {
|
||||
self.color = Point3::new(1.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
pub fn unselect(&mut self) {
|
||||
self.color = self.base_color;
|
||||
}
|
||||
|
||||
pub fn set_color(&mut self, color: Point3<f32>) {
|
||||
#[cfg(feature = "dim3")]
|
||||
{
|
||||
self.gfx.set_color(color.x, color.y, color.z);
|
||||
}
|
||||
self.color = color;
|
||||
self.base_color = color;
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
pub fn update(&mut self, colliders: &ColliderSet) {
|
||||
node::update_scene_node(
|
||||
&mut self.gfx,
|
||||
colliders,
|
||||
self.collider,
|
||||
&self.color,
|
||||
&na::Isometry::identity(),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
pub fn update(&mut self, _colliders: &ColliderSet) {}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
pub fn scene_node(&self) -> &GraphicsNode {
|
||||
&self.gfx
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
pub fn scene_node_mut(&mut self) -> &mut GraphicsNode {
|
||||
&mut self.gfx
|
||||
}
|
||||
|
||||
pub fn object(&self) -> ColliderHandle {
|
||||
self.collider
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
pub fn draw(&mut self, window: &mut Window) {
|
||||
for vtx in self.vertices.chunks(2) {
|
||||
window.draw_planar_line(&vtx[0], &vtx[1], &self.color)
|
||||
}
|
||||
}
|
||||
}
|
||||
108
src_testbed/objects/mesh.rs
Normal file
108
src_testbed/objects/mesh.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
use crate::objects::node::{self, GraphicsNode};
|
||||
use kiss3d::window;
|
||||
use na::Point3;
|
||||
use rapier::geometry::{ColliderHandle, ColliderSet};
|
||||
use rapier::math::{Isometry, Point};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct Mesh {
|
||||
color: Point3<f32>,
|
||||
base_color: Point3<f32>,
|
||||
gfx: GraphicsNode,
|
||||
collider: ColliderHandle,
|
||||
}
|
||||
|
||||
impl Mesh {
|
||||
pub fn new(
|
||||
collider: ColliderHandle,
|
||||
vertices: Vec<Point<f32>>,
|
||||
indices: Vec<Point3<u32>>,
|
||||
color: Point3<f32>,
|
||||
window: &mut window::Window,
|
||||
) -> Mesh {
|
||||
let vs = vertices;
|
||||
let is = indices.into_iter().map(na::convert).collect();
|
||||
|
||||
let mesh;
|
||||
let gfx;
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
{
|
||||
mesh = kiss3d::resource::PlanarMesh::new(vs, is, None, false);
|
||||
gfx = window.add_planar_mesh(
|
||||
Rc::new(RefCell::new(mesh)),
|
||||
crate::math::Vector::from_element(1.0),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
{
|
||||
mesh = kiss3d::resource::Mesh::new(vs, is, None, None, false);
|
||||
gfx = window.add_mesh(Rc::new(RefCell::new(mesh)), na::Vector3::from_element(1.0));
|
||||
}
|
||||
|
||||
let mut res = Mesh {
|
||||
color,
|
||||
base_color: color,
|
||||
gfx,
|
||||
collider,
|
||||
};
|
||||
|
||||
res.gfx.enable_backface_culling(false);
|
||||
res.gfx.set_color(color.x, color.y, color.z);
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select(&mut self) {
|
||||
self.color = Point3::new(1.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
pub fn unselect(&mut self) {
|
||||
self.color = self.base_color;
|
||||
}
|
||||
|
||||
pub fn set_color(&mut self, color: Point3<f32>) {
|
||||
self.gfx.set_color(color.x, color.y, color.z);
|
||||
self.color = color;
|
||||
self.base_color = color;
|
||||
}
|
||||
|
||||
pub fn update(&mut self, colliders: &ColliderSet) {
|
||||
node::update_scene_node(
|
||||
&mut self.gfx,
|
||||
colliders,
|
||||
self.collider,
|
||||
&self.color,
|
||||
&Isometry::identity(),
|
||||
);
|
||||
|
||||
// // Update if some deformation occurred.
|
||||
// // FIXME: don't update if it did not move.
|
||||
// if let Some(c) = colliders.get(self.collider) {
|
||||
// if let ColliderAnchor::OnDeformableBody { .. } = c.anchor() {
|
||||
// let shape = c.shape().as_shape::<TriMesh<f32>>().unwrap();
|
||||
// let vtx = shape.points();
|
||||
//
|
||||
// self.gfx.modify_vertices(&mut |vertices| {
|
||||
// for (v, new_v) in vertices.iter_mut().zip(vtx.iter()) {
|
||||
// *v = *new_v
|
||||
// }
|
||||
// });
|
||||
// self.gfx.recompute_normals();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn scene_node(&self) -> &GraphicsNode {
|
||||
&self.gfx
|
||||
}
|
||||
|
||||
pub fn scene_node_mut(&mut self) -> &mut GraphicsNode {
|
||||
&mut self.gfx
|
||||
}
|
||||
|
||||
pub fn object(&self) -> ColliderHandle {
|
||||
self.collider
|
||||
}
|
||||
}
|
||||
10
src_testbed/objects/mod.rs
Normal file
10
src_testbed/objects/mod.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
pub mod ball;
|
||||
pub mod box_node;
|
||||
pub mod capsule;
|
||||
pub mod convex;
|
||||
pub mod heightfield;
|
||||
pub mod mesh;
|
||||
pub mod node;
|
||||
//pub mod plane;
|
||||
//#[cfg(feature = "dim2")]
|
||||
//pub mod polyline;
|
||||
164
src_testbed/objects/node.rs
Normal file
164
src_testbed/objects/node.rs
Normal file
@@ -0,0 +1,164 @@
|
||||
use crate::objects::ball::Ball;
|
||||
use crate::objects::box_node::Box;
|
||||
use crate::objects::capsule::Capsule;
|
||||
use crate::objects::convex::Convex;
|
||||
use crate::objects::heightfield::HeightField;
|
||||
use crate::objects::mesh::Mesh;
|
||||
//use crate::objects::plane::Plane;
|
||||
//#[cfg(feature = "dim2")]
|
||||
//use crate::objects::polyline::Polyline;
|
||||
use kiss3d::window::Window;
|
||||
use na::Point3;
|
||||
|
||||
use rapier::geometry::{ColliderHandle, ColliderSet};
|
||||
use rapier::math::Isometry;
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
pub type GraphicsNode = kiss3d::scene::PlanarSceneNode;
|
||||
#[cfg(feature = "dim3")]
|
||||
pub type GraphicsNode = kiss3d::scene::SceneNode;
|
||||
|
||||
pub enum Node {
|
||||
// Plane(Plane),
|
||||
Ball(Ball),
|
||||
Box(Box),
|
||||
HeightField(HeightField),
|
||||
Capsule(Capsule),
|
||||
// #[cfg(feature = "dim2")]
|
||||
// Polyline(Polyline),
|
||||
Mesh(Mesh),
|
||||
Convex(Convex),
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn select(&mut self) {
|
||||
match *self {
|
||||
// Node::Plane(ref mut n) => n.select(),
|
||||
Node::Ball(ref mut n) => n.select(),
|
||||
Node::Box(ref mut n) => n.select(),
|
||||
Node::Capsule(ref mut n) => n.select(),
|
||||
Node::HeightField(ref mut n) => n.select(),
|
||||
// #[cfg(feature = "dim2")]
|
||||
// Node::Polyline(ref mut n) => n.select(),
|
||||
Node::Mesh(ref mut n) => n.select(),
|
||||
Node::Convex(ref mut n) => n.select(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unselect(&mut self) {
|
||||
match *self {
|
||||
// Node::Plane(ref mut n) => n.unselect(),
|
||||
Node::Ball(ref mut n) => n.unselect(),
|
||||
Node::Box(ref mut n) => n.unselect(),
|
||||
Node::Capsule(ref mut n) => n.unselect(),
|
||||
Node::HeightField(ref mut n) => n.unselect(),
|
||||
// #[cfg(feature = "dim2")]
|
||||
// Node::Polyline(ref mut n) => n.unselect(),
|
||||
Node::Mesh(ref mut n) => n.unselect(),
|
||||
Node::Convex(ref mut n) => n.unselect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, colliders: &ColliderSet) {
|
||||
match *self {
|
||||
// Node::Plane(ref mut n) => n.update(colliders),
|
||||
Node::Ball(ref mut n) => n.update(colliders),
|
||||
Node::Box(ref mut n) => n.update(colliders),
|
||||
Node::Capsule(ref mut n) => n.update(colliders),
|
||||
Node::HeightField(ref mut n) => n.update(colliders),
|
||||
// #[cfg(feature = "dim2")]
|
||||
// Node::Polyline(ref mut n) => n.update(colliders),
|
||||
Node::Mesh(ref mut n) => n.update(colliders),
|
||||
Node::Convex(ref mut n) => n.update(colliders),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
pub fn draw(&mut self, window: &mut Window) {
|
||||
match *self {
|
||||
// Node::Polyline(ref mut n) => n.draw(_window),
|
||||
Node::HeightField(ref mut n) => n.draw(window),
|
||||
// Node::Plane(ref mut n) => n.draw(_window),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
pub fn draw(&mut self, _: &mut Window) {}
|
||||
|
||||
pub fn scene_node(&self) -> Option<&GraphicsNode> {
|
||||
match *self {
|
||||
// #[cfg(feature = "dim3")]
|
||||
// Node::Plane(ref n) => Some(n.scene_node()),
|
||||
Node::Ball(ref n) => Some(n.scene_node()),
|
||||
Node::Box(ref n) => Some(n.scene_node()),
|
||||
Node::Capsule(ref n) => Some(n.scene_node()),
|
||||
#[cfg(feature = "dim3")]
|
||||
Node::HeightField(ref n) => Some(n.scene_node()),
|
||||
Node::Mesh(ref n) => Some(n.scene_node()),
|
||||
Node::Convex(ref n) => Some(n.scene_node()),
|
||||
#[cfg(feature = "dim2")]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scene_node_mut(&mut self) -> Option<&mut GraphicsNode> {
|
||||
match *self {
|
||||
// #[cfg(feature = "dim3")]
|
||||
// Node::Plane(ref mut n) => Some(n.scene_node_mut()),
|
||||
Node::Ball(ref mut n) => Some(n.scene_node_mut()),
|
||||
Node::Box(ref mut n) => Some(n.scene_node_mut()),
|
||||
Node::Capsule(ref mut n) => Some(n.scene_node_mut()),
|
||||
#[cfg(feature = "dim3")]
|
||||
Node::HeightField(ref mut n) => Some(n.scene_node_mut()),
|
||||
Node::Mesh(ref mut n) => Some(n.scene_node_mut()),
|
||||
Node::Convex(ref mut n) => Some(n.scene_node_mut()),
|
||||
#[cfg(feature = "dim2")]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collider(&self) -> ColliderHandle {
|
||||
match *self {
|
||||
// Node::Plane(ref n) => n.object(),
|
||||
Node::Ball(ref n) => n.object(),
|
||||
Node::Box(ref n) => n.object(),
|
||||
Node::Capsule(ref n) => n.object(),
|
||||
Node::HeightField(ref n) => n.object(),
|
||||
// #[cfg(feature = "dim2")]
|
||||
// Node::Polyline(ref n) => n.object(),
|
||||
Node::Mesh(ref n) => n.object(),
|
||||
Node::Convex(ref n) => n.object(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_color(&mut self, color: Point3<f32>) {
|
||||
match *self {
|
||||
// Node::Plane(ref mut n) => n.set_color(color),
|
||||
Node::Ball(ref mut n) => n.set_color(color),
|
||||
Node::Box(ref mut n) => n.set_color(color),
|
||||
Node::Capsule(ref mut n) => n.set_color(color),
|
||||
Node::HeightField(ref mut n) => n.set_color(color),
|
||||
// #[cfg(feature = "dim2")]
|
||||
// Node::Polyline(ref mut n) => n.set_color(color),
|
||||
Node::Mesh(ref mut n) => n.set_color(color),
|
||||
Node::Convex(ref mut n) => n.set_color(color),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_scene_node(
|
||||
node: &mut GraphicsNode,
|
||||
colliders: &ColliderSet,
|
||||
handle: ColliderHandle,
|
||||
color: &Point3<f32>,
|
||||
delta: &Isometry<f32>,
|
||||
) {
|
||||
if let Some(co) = colliders.get(handle) {
|
||||
node.set_local_transformation(co.position() * delta);
|
||||
node.set_color(color.x, color.y, color.z);
|
||||
} else {
|
||||
node.set_visible(false);
|
||||
node.unlink();
|
||||
}
|
||||
}
|
||||
132
src_testbed/objects/plane.rs
Normal file
132
src_testbed/objects/plane.rs
Normal file
@@ -0,0 +1,132 @@
|
||||
#[cfg(feature = "dim3")]
|
||||
use crate::objects::node::GraphicsNode;
|
||||
use kiss3d::window::Window;
|
||||
use na::Point3;
|
||||
#[cfg(feature = "dim3")]
|
||||
use na::Vector3;
|
||||
#[cfg(feature = "dim2")]
|
||||
use nphysics::math::{Point, Vector};
|
||||
use nphysics::object::{DefaultColliderHandle, DefaultColliderSet};
|
||||
#[cfg(feature = "dim3")]
|
||||
use num::Zero;
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
pub struct Plane {
|
||||
gfx: GraphicsNode,
|
||||
collider: DefaultColliderHandle,
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
pub struct Plane {
|
||||
color: Point3<f32>,
|
||||
base_color: Point3<f32>,
|
||||
position: Point<f32>,
|
||||
normal: na::Unit<Vector<f32>>,
|
||||
collider: DefaultColliderHandle,
|
||||
}
|
||||
|
||||
impl Plane {
|
||||
#[cfg(feature = "dim2")]
|
||||
pub fn new(
|
||||
collider: DefaultColliderHandle,
|
||||
colliders: &DefaultColliderSet<f32>,
|
||||
position: &Point<f32>,
|
||||
normal: &Vector<f32>,
|
||||
color: Point3<f32>,
|
||||
_: &mut Window,
|
||||
) -> Plane {
|
||||
let mut res = Plane {
|
||||
color,
|
||||
base_color: color,
|
||||
position: *position,
|
||||
normal: na::Unit::new_normalize(*normal),
|
||||
collider,
|
||||
};
|
||||
|
||||
res.update(colliders);
|
||||
res
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
pub fn new(
|
||||
collider: DefaultColliderHandle,
|
||||
colliders: &DefaultColliderSet<f32>,
|
||||
world_pos: &Point3<f32>,
|
||||
world_normal: &Vector3<f32>,
|
||||
color: Point3<f32>,
|
||||
window: &mut Window,
|
||||
) -> Plane {
|
||||
let mut res = Plane {
|
||||
gfx: window.add_quad(100.0, 100.0, 10, 10),
|
||||
collider,
|
||||
};
|
||||
|
||||
if colliders
|
||||
.get(collider)
|
||||
.unwrap()
|
||||
.query_type()
|
||||
.is_proximity_query()
|
||||
{
|
||||
res.gfx.set_surface_rendering_activation(false);
|
||||
res.gfx.set_lines_width(1.0);
|
||||
}
|
||||
|
||||
res.gfx.set_color(color.x, color.y, color.z);
|
||||
|
||||
let up = if world_normal.z.is_zero() && world_normal.y.is_zero() {
|
||||
Vector3::z()
|
||||
} else {
|
||||
Vector3::x()
|
||||
};
|
||||
|
||||
res.gfx
|
||||
.reorient(world_pos, &(*world_pos + *world_normal), &up);
|
||||
|
||||
res.update(colliders);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select(&mut self) {}
|
||||
|
||||
pub fn unselect(&mut self) {}
|
||||
|
||||
pub fn update(&mut self, _: &DefaultColliderSet<f32>) {
|
||||
// FIXME: atm we assume the plane does not move
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
pub fn set_color(&mut self, color: Point3<f32>) {
|
||||
self.gfx.set_color(color.x, color.y, color.z);
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
pub fn set_color(&mut self, color: Point3<f32>) {
|
||||
self.color = color;
|
||||
self.base_color = color;
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
pub fn scene_node(&self) -> &GraphicsNode {
|
||||
&self.gfx
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
pub fn scene_node_mut(&mut self) -> &mut GraphicsNode {
|
||||
&mut self.gfx
|
||||
}
|
||||
|
||||
pub fn object(&self) -> DefaultColliderHandle {
|
||||
self.collider
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
pub fn draw(&mut self, window: &mut Window) {
|
||||
let orth = Vector::new(-self.normal.y, self.normal.x);
|
||||
window.draw_planar_line(
|
||||
&(self.position - orth * 50.0),
|
||||
&(self.position + orth * 50.0),
|
||||
&self.color,
|
||||
);
|
||||
}
|
||||
}
|
||||
79
src_testbed/objects/polyline.rs
Normal file
79
src_testbed/objects/polyline.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use kiss3d::window::Window;
|
||||
use na::{Isometry2, Point2, Point3};
|
||||
use ncollide2d::shape;
|
||||
use nphysics2d::object::{ColliderAnchor, DefaultColliderHandle, DefaultColliderSet};
|
||||
|
||||
pub struct Polyline {
|
||||
color: Point3<f32>,
|
||||
base_color: Point3<f32>,
|
||||
vertices: Vec<Point2<f32>>,
|
||||
indices: Vec<Point2<usize>>,
|
||||
collider: DefaultColliderHandle,
|
||||
pos: Isometry2<f32>,
|
||||
}
|
||||
|
||||
impl Polyline {
|
||||
pub fn new(
|
||||
collider: DefaultColliderHandle,
|
||||
colliders: &DefaultColliderSet<f32>,
|
||||
_: Isometry2<f32>,
|
||||
vertices: Vec<Point2<f32>>,
|
||||
indices: Vec<Point2<usize>>,
|
||||
color: Point3<f32>,
|
||||
_: &mut Window,
|
||||
) -> Polyline {
|
||||
let mut res = Polyline {
|
||||
color,
|
||||
pos: Isometry2::identity(),
|
||||
base_color: color,
|
||||
vertices,
|
||||
indices,
|
||||
collider,
|
||||
};
|
||||
|
||||
res.update(colliders);
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select(&mut self) {
|
||||
self.color = Point3::new(1.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
pub fn unselect(&mut self) {
|
||||
self.color = self.base_color;
|
||||
}
|
||||
|
||||
pub fn set_color(&mut self, color: Point3<f32>) {
|
||||
self.color = color;
|
||||
self.base_color = color;
|
||||
}
|
||||
|
||||
pub fn update(&mut self, colliders: &DefaultColliderSet<f32>) {
|
||||
// Update if some deformation occurred.
|
||||
// FIXME: don't update if it did not move.
|
||||
if let Some(c) = colliders.get(self.collider) {
|
||||
self.pos = *c.position();
|
||||
if let ColliderAnchor::OnDeformableBody { .. } = c.anchor() {
|
||||
let shape = c.shape().as_shape::<shape::Polyline<f32>>().unwrap();
|
||||
self.vertices = shape.points().to_vec();
|
||||
self.indices.clear();
|
||||
|
||||
for e in shape.edges() {
|
||||
self.indices.push(e.indices);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn object(&self) -> DefaultColliderHandle {
|
||||
self.collider
|
||||
}
|
||||
|
||||
pub fn draw(&mut self, window: &mut Window) {
|
||||
for idx in &self.indices {
|
||||
let p1 = self.pos * self.vertices[idx.x];
|
||||
let p2 = self.pos * self.vertices[idx.y];
|
||||
window.draw_planar_line(&p1, &p2, &self.color)
|
||||
}
|
||||
}
|
||||
}
|
||||
604
src_testbed/physx_backend.rs
Normal file
604
src_testbed/physx_backend.rs
Normal file
@@ -0,0 +1,604 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use na::{Isometry3, Matrix3, Matrix4, Point3, Rotation3, Translation3, UnitQuaternion, Vector3};
|
||||
use physx::prelude::*;
|
||||
use rapier::counters::Counters;
|
||||
use rapier::dynamics::{
|
||||
IntegrationParameters, JointParams, JointSet, RigidBodyHandle, RigidBodySet,
|
||||
};
|
||||
use rapier::geometry::{Collider, ColliderSet, Shape};
|
||||
use rapier::utils::WBasis;
|
||||
use std::collections::HashMap;
|
||||
|
||||
const PX_PHYSICS_VERSION: u32 = physx::version(4, 1, 1);
|
||||
|
||||
trait IntoNa {
|
||||
type Output;
|
||||
fn into_na(self) -> Self::Output;
|
||||
}
|
||||
|
||||
impl IntoNa for glam::Mat4 {
|
||||
type Output = Matrix4<f32>;
|
||||
fn into_na(self) -> Self::Output {
|
||||
self.to_cols_array_2d().into()
|
||||
}
|
||||
}
|
||||
|
||||
trait IntoPhysx {
|
||||
type Output;
|
||||
fn into_physx(self) -> Self::Output;
|
||||
}
|
||||
|
||||
impl IntoPhysx for Vector3<f32> {
|
||||
type Output = physx_sys::PxVec3;
|
||||
fn into_physx(self) -> Self::Output {
|
||||
physx_sys::PxVec3 {
|
||||
x: self.x,
|
||||
y: self.y,
|
||||
z: self.z,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPhysx for Point3<f32> {
|
||||
type Output = physx_sys::PxVec3;
|
||||
fn into_physx(self) -> Self::Output {
|
||||
physx_sys::PxVec3 {
|
||||
x: self.x,
|
||||
y: self.y,
|
||||
z: self.z,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPhysx for Isometry3<f32> {
|
||||
type Output = physx_sys::PxTransform;
|
||||
fn into_physx(self) -> Self::Output {
|
||||
physx::transform::gl_to_px_tf(self.into_glam())
|
||||
}
|
||||
}
|
||||
|
||||
trait IntoGlam {
|
||||
type Output;
|
||||
fn into_glam(self) -> Self::Output;
|
||||
}
|
||||
|
||||
impl IntoGlam for Vector3<f32> {
|
||||
type Output = glam::Vec3;
|
||||
fn into_glam(self) -> Self::Output {
|
||||
glam::vec3(self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoGlam for Point3<f32> {
|
||||
type Output = glam::Vec3;
|
||||
fn into_glam(self) -> Self::Output {
|
||||
glam::vec3(self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoGlam for Matrix4<f32> {
|
||||
type Output = glam::Mat4;
|
||||
fn into_glam(self) -> Self::Output {
|
||||
glam::Mat4::from_cols_array_2d(&self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoGlam for Isometry3<f32> {
|
||||
type Output = glam::Mat4;
|
||||
fn into_glam(self) -> Self::Output {
|
||||
glam::Mat4::from_cols_array_2d(&self.to_homogeneous().into())
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
pub static FOUNDATION: std::cell::RefCell<Foundation> = std::cell::RefCell::new(Foundation::new(PX_PHYSICS_VERSION));
|
||||
}
|
||||
|
||||
pub struct PhysxWorld {
|
||||
physics: Physics,
|
||||
cooking: Cooking,
|
||||
scene: Scene,
|
||||
rapier2physx: HashMap<RigidBodyHandle, BodyHandle>,
|
||||
}
|
||||
|
||||
impl PhysxWorld {
|
||||
pub fn from_rapier(
|
||||
gravity: Vector3<f32>,
|
||||
integration_parameters: &IntegrationParameters,
|
||||
bodies: &RigidBodySet,
|
||||
colliders: &ColliderSet,
|
||||
joints: &JointSet,
|
||||
use_two_friction_directions: bool,
|
||||
num_threads: usize,
|
||||
) -> Self {
|
||||
let mut rapier2physx = HashMap::new();
|
||||
let mut physics = FOUNDATION.with(|f| {
|
||||
PhysicsBuilder::default()
|
||||
.load_extensions(false)
|
||||
.build(&mut *f.borrow_mut())
|
||||
});
|
||||
let mut cooking = FOUNDATION.with(|f| unsafe {
|
||||
let sc = physx_sys::PxTolerancesScale_new();
|
||||
let params = physx_sys::PxCookingParams_new(&sc);
|
||||
Cooking::new(PX_PHYSICS_VERSION, &mut *f.borrow_mut(), params)
|
||||
});
|
||||
|
||||
let scene_desc = MySceneBuilder::default()
|
||||
.set_gravity(gravity.into_glam())
|
||||
.set_simulation_threading(SimulationThreadType::Dedicated(num_threads as u32))
|
||||
// .set_broad_phase_type(BroadPhaseType::SweepAndPrune)
|
||||
// .set_solver_type(physx_sys::PxSolverType::eTGS)
|
||||
.build_desc(&mut physics);
|
||||
|
||||
let raw_scene =
|
||||
unsafe { physx_sys::PxPhysics_createScene_mut(physics.get_raw_mut(), &scene_desc) };
|
||||
|
||||
// FIXME: we do this because we are also using two
|
||||
// friction directions. We should add to rapier the option to use
|
||||
// one friction direction too, and perhaps an equivalent of physX
|
||||
// ePATCH friction type.
|
||||
if use_two_friction_directions {
|
||||
unsafe {
|
||||
physx_sys::PxScene_setFrictionType_mut(
|
||||
raw_scene,
|
||||
physx_sys::PxFrictionType::eTWO_DIRECTIONAL,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut scene = Scene::new(raw_scene);
|
||||
|
||||
for (rapier_handle, rb) in bodies.iter() {
|
||||
use physx::rigid_dynamic::RigidDynamic;
|
||||
use physx::rigid_static::RigidStatic;
|
||||
use physx::transform;
|
||||
|
||||
let pos = transform::gl_to_px_tf(rb.position.to_homogeneous().into_glam());
|
||||
if rb.is_dynamic() {
|
||||
let actor = unsafe {
|
||||
physx_sys::PxPhysics_createRigidDynamic_mut(physics.get_raw_mut(), &pos)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
physx_sys::PxRigidDynamic_setSolverIterationCounts_mut(
|
||||
actor,
|
||||
integration_parameters.max_position_iterations as u32,
|
||||
integration_parameters.max_velocity_iterations as u32,
|
||||
);
|
||||
}
|
||||
|
||||
let physx_handle = scene.add_dynamic(RigidDynamic::new(actor));
|
||||
rapier2physx.insert(rapier_handle, physx_handle);
|
||||
} else {
|
||||
let actor = unsafe {
|
||||
physx_sys::PxPhysics_createRigidStatic_mut(physics.get_raw_mut(), &pos)
|
||||
};
|
||||
|
||||
let physx_handle = scene.add_actor(RigidStatic::new(actor));
|
||||
rapier2physx.insert(rapier_handle, physx_handle);
|
||||
}
|
||||
}
|
||||
|
||||
for (_, collider) in colliders.iter() {
|
||||
if let Some((px_collider, collider_pos)) =
|
||||
physx_collider_from_rapier_collider(&collider)
|
||||
{
|
||||
let material = physics.create_material(
|
||||
collider.friction,
|
||||
collider.friction,
|
||||
collider.restitution,
|
||||
);
|
||||
let geometry = cooking.make_geometry(px_collider);
|
||||
let flags = if collider.is_sensor() {
|
||||
physx_sys::PxShapeFlags {
|
||||
mBits: physx_sys::PxShapeFlag::eTRIGGER_SHAPE as u8,
|
||||
}
|
||||
} else {
|
||||
physx_sys::PxShapeFlags {
|
||||
mBits: physx_sys::PxShapeFlag::eSIMULATION_SHAPE as u8,
|
||||
}
|
||||
};
|
||||
|
||||
let handle = rapier2physx[&collider.parent()];
|
||||
let parent_body = &bodies[collider.parent()];
|
||||
let parent = if !parent_body.is_dynamic() {
|
||||
scene.get_static_mut(handle).unwrap().as_ptr_mut().ptr
|
||||
as *mut physx_sys::PxRigidActor
|
||||
} else {
|
||||
scene.get_dynamic_mut(handle).unwrap().as_ptr_mut().ptr
|
||||
as *mut physx_sys::PxRigidActor
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let shape = physx_sys::PxPhysics_createShape_mut(
|
||||
physics.get_raw_mut(),
|
||||
geometry.as_raw(),
|
||||
material,
|
||||
true,
|
||||
flags.into(),
|
||||
);
|
||||
let pose = collider_pos.into_physx();
|
||||
physx_sys::PxShape_setLocalPose_mut(shape, &pose);
|
||||
physx_sys::PxRigidActor_attachShape_mut(parent, shape);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let mut res = Self {
|
||||
physics,
|
||||
cooking,
|
||||
scene,
|
||||
rapier2physx,
|
||||
};
|
||||
|
||||
res.setup_joints(joints);
|
||||
res
|
||||
}
|
||||
|
||||
fn setup_joints(&mut self, joints: &JointSet) {
|
||||
unsafe {
|
||||
for joint in joints.iter() {
|
||||
let actor1 = self.rapier2physx[&joint.body1];
|
||||
let actor2 = self.rapier2physx[&joint.body2];
|
||||
|
||||
match &joint.params {
|
||||
JointParams::BallJoint(params) => {
|
||||
let frame1 = physx::transform::gl_to_px_tf(
|
||||
Isometry3::new(params.local_anchor1.coords, na::zero()).into_glam(),
|
||||
);
|
||||
let frame2 = physx::transform::gl_to_px_tf(
|
||||
Isometry3::new(params.local_anchor2.coords, na::zero()).into_glam(),
|
||||
);
|
||||
|
||||
physx_sys::phys_PxSphericalJointCreate(
|
||||
self.physics.get_raw_mut(),
|
||||
actor1.0 as *mut _,
|
||||
&frame1 as *const _,
|
||||
actor2.0 as *mut _,
|
||||
&frame2 as *const _,
|
||||
);
|
||||
}
|
||||
JointParams::RevoluteJoint(params) => {
|
||||
// NOTE: orthonormal_basis() returns the two basis vectors.
|
||||
// However we only use one and recompute the other just to
|
||||
// make sure our basis is right-handed.
|
||||
let basis1a = params.local_axis1.orthonormal_basis()[0];
|
||||
let basis2a = params.local_axis2.orthonormal_basis()[0];
|
||||
let basis1b = params.local_axis1.cross(&basis1a);
|
||||
let basis2b = params.local_axis2.cross(&basis2a);
|
||||
|
||||
let rotmat1 = Rotation3::from_matrix_unchecked(Matrix3::from_columns(&[
|
||||
params.local_axis1.into_inner(),
|
||||
basis1a,
|
||||
basis1b,
|
||||
]));
|
||||
let rotmat2 = Rotation3::from_matrix_unchecked(Matrix3::from_columns(&[
|
||||
params.local_axis2.into_inner(),
|
||||
basis2a,
|
||||
basis2b,
|
||||
]));
|
||||
let axisangle1 = rotmat1.scaled_axis();
|
||||
let axisangle2 = rotmat2.scaled_axis();
|
||||
|
||||
let frame1 = physx::transform::gl_to_px_tf(
|
||||
Isometry3::new(params.local_anchor1.coords, axisangle1).into_glam(),
|
||||
);
|
||||
let frame2 = physx::transform::gl_to_px_tf(
|
||||
Isometry3::new(params.local_anchor2.coords, axisangle2).into_glam(),
|
||||
);
|
||||
|
||||
physx_sys::phys_PxRevoluteJointCreate(
|
||||
self.physics.get_raw_mut(),
|
||||
actor1.0 as *mut _,
|
||||
&frame1 as *const _,
|
||||
actor2.0 as *mut _,
|
||||
&frame2 as *const _,
|
||||
);
|
||||
}
|
||||
|
||||
JointParams::PrismaticJoint(params) => {
|
||||
// NOTE: orthonormal_basis() returns the two basis vectors.
|
||||
// However we only use one and recompute the other just to
|
||||
// make sure our basis is right-handed.
|
||||
let basis1a = params.local_axis1().orthonormal_basis()[0];
|
||||
let basis2a = params.local_axis2().orthonormal_basis()[0];
|
||||
let basis1b = params.local_axis1().cross(&basis1a);
|
||||
let basis2b = params.local_axis2().cross(&basis2a);
|
||||
|
||||
let rotmat1 = Rotation3::from_matrix_unchecked(Matrix3::from_columns(&[
|
||||
params.local_axis1().into_inner(),
|
||||
basis1a,
|
||||
basis1b,
|
||||
]));
|
||||
let rotmat2 = Rotation3::from_matrix_unchecked(Matrix3::from_columns(&[
|
||||
params.local_axis2().into_inner(),
|
||||
basis2a,
|
||||
basis2b,
|
||||
]));
|
||||
|
||||
let axisangle1 = rotmat1.scaled_axis();
|
||||
let axisangle2 = rotmat2.scaled_axis();
|
||||
|
||||
let frame1 = physx::transform::gl_to_px_tf(
|
||||
Isometry3::new(params.local_anchor1.coords, axisangle1).into_glam(),
|
||||
);
|
||||
let frame2 = physx::transform::gl_to_px_tf(
|
||||
Isometry3::new(params.local_anchor2.coords, axisangle2).into_glam(),
|
||||
);
|
||||
|
||||
let joint = physx_sys::phys_PxPrismaticJointCreate(
|
||||
self.physics.get_raw_mut(),
|
||||
actor1.0 as *mut _,
|
||||
&frame1 as *const _,
|
||||
actor2.0 as *mut _,
|
||||
&frame2 as *const _,
|
||||
);
|
||||
|
||||
if params.limits_enabled {
|
||||
let limits = physx_sys::PxJointLinearLimitPair {
|
||||
restitution: 0.0,
|
||||
bounceThreshold: 0.0,
|
||||
stiffness: 0.0,
|
||||
damping: 0.0,
|
||||
contactDistance: 0.01,
|
||||
lower: params.limits[0],
|
||||
upper: params.limits[1],
|
||||
};
|
||||
physx_sys::PxPrismaticJoint_setLimit_mut(joint, &limits);
|
||||
physx_sys::PxPrismaticJoint_setPrismaticJointFlag_mut(
|
||||
joint,
|
||||
physx_sys::PxPrismaticJointFlag::eLIMIT_ENABLED,
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
JointParams::FixedJoint(params) => {
|
||||
let frame1 =
|
||||
physx::transform::gl_to_px_tf(params.local_anchor1.into_glam());
|
||||
let frame2 =
|
||||
physx::transform::gl_to_px_tf(params.local_anchor2.into_glam());
|
||||
|
||||
physx_sys::phys_PxFixedJointCreate(
|
||||
self.physics.get_raw_mut(),
|
||||
actor1.0 as *mut _,
|
||||
&frame1 as *const _,
|
||||
actor2.0 as *mut _,
|
||||
&frame2 as *const _,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step(&mut self, counters: &mut Counters, params: &IntegrationParameters) {
|
||||
counters.step_started();
|
||||
self.scene.step(params.dt(), true);
|
||||
counters.step_completed();
|
||||
}
|
||||
|
||||
pub fn sync(&self, bodies: &mut RigidBodySet, colliders: &mut ColliderSet) {
|
||||
for (rapier_handle, physx_handle) in self.rapier2physx.iter() {
|
||||
let mut rb = bodies.get_mut(*rapier_handle).unwrap();
|
||||
let ra = self.scene.get_rigid_actor(*physx_handle).unwrap();
|
||||
let pos = ra.get_global_pose().into_na();
|
||||
let iso = na::convert_unchecked(pos);
|
||||
rb.set_position(iso);
|
||||
|
||||
if rb.is_kinematic() {}
|
||||
|
||||
for coll_handle in rb.colliders() {
|
||||
let collider = &mut colliders[*coll_handle];
|
||||
collider.set_position_debug(iso * collider.delta());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn physx_collider_from_rapier_collider(
|
||||
collider: &Collider,
|
||||
) -> Option<(ColliderDesc, Isometry3<f32>)> {
|
||||
let mut local_pose = Isometry3::identity();
|
||||
let desc = match collider.shape() {
|
||||
Shape::Cuboid(cuboid) => ColliderDesc::Box(
|
||||
cuboid.half_extents.x,
|
||||
cuboid.half_extents.y,
|
||||
cuboid.half_extents.z,
|
||||
),
|
||||
Shape::Ball(ball) => ColliderDesc::Sphere(ball.radius),
|
||||
Shape::Capsule(capsule) => {
|
||||
let center = capsule.center();
|
||||
let mut dir = capsule.b - capsule.a;
|
||||
|
||||
if dir.x < 0.0 {
|
||||
dir = -dir;
|
||||
}
|
||||
|
||||
let rot = UnitQuaternion::rotation_between(&Vector3::x(), &dir);
|
||||
local_pose =
|
||||
Translation3::from(center.coords) * rot.unwrap_or(UnitQuaternion::identity());
|
||||
ColliderDesc::Capsule(capsule.radius, capsule.height())
|
||||
}
|
||||
Shape::Trimesh(trimesh) => ColliderDesc::TriMesh {
|
||||
vertices: trimesh
|
||||
.vertices()
|
||||
.iter()
|
||||
.map(|pt| pt.into_physx())
|
||||
.collect(),
|
||||
indices: trimesh.flat_indices().to_vec(),
|
||||
mesh_scale: Vector3::repeat(1.0).into_glam(),
|
||||
},
|
||||
_ => {
|
||||
eprintln!("Creating a shape unknown to the PhysX backend.");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some((desc, local_pose))
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* XXX: All the remaining code is a duplicate from physx-rs to allow more customizations.
|
||||
*
|
||||
*/
|
||||
use physx::scene::SimulationThreadType;
|
||||
|
||||
pub struct MySceneBuilder {
|
||||
gravity: glam::Vec3,
|
||||
simulation_filter_shader: Option<physx_sys::SimulationFilterShader>,
|
||||
simulation_threading: Option<SimulationThreadType>,
|
||||
broad_phase_type: BroadPhaseType,
|
||||
use_controller_manager: bool,
|
||||
controller_manager_locking: bool,
|
||||
call_default_filter_shader_first: bool,
|
||||
use_ccd: bool,
|
||||
enable_ccd_resweep: bool,
|
||||
solver_type: u32,
|
||||
}
|
||||
|
||||
impl Default for MySceneBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
gravity: glam::Vec3::new(0.0, -9.80665, 0.0), // standard gravity value
|
||||
call_default_filter_shader_first: true,
|
||||
simulation_filter_shader: None,
|
||||
simulation_threading: None,
|
||||
broad_phase_type: BroadPhaseType::SweepAndPrune,
|
||||
use_controller_manager: false,
|
||||
controller_manager_locking: false,
|
||||
use_ccd: false,
|
||||
enable_ccd_resweep: false,
|
||||
solver_type: physx_sys::PxSolverType::ePGS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MySceneBuilder {
|
||||
/// Set the gravity for the scene.
|
||||
///
|
||||
/// Default: [0.0, -9.80665, 0.0] (standard gravity)
|
||||
pub fn set_gravity(&mut self, gravity: glam::Vec3) -> &mut Self {
|
||||
self.gravity = gravity;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set a callback to be invoked on various simulation events. Note:
|
||||
/// Currently only handles collision events
|
||||
///
|
||||
/// Default: not set
|
||||
pub fn set_simulation_filter_shader(
|
||||
&mut self,
|
||||
simulation_filter_shader: physx_sys::SimulationFilterShader,
|
||||
) -> &mut Self {
|
||||
self.simulation_filter_shader = Some(simulation_filter_shader);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable the controller manager on the scene.
|
||||
///
|
||||
/// Default: false, false
|
||||
pub fn use_controller_manager(
|
||||
&mut self,
|
||||
use_controller_manager: bool,
|
||||
locking_enabled: bool,
|
||||
) -> &mut Self {
|
||||
self.use_controller_manager = use_controller_manager;
|
||||
self.controller_manager_locking = locking_enabled;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_solver_type(&mut self, solver_type: u32) -> &mut Self {
|
||||
self.solver_type = solver_type;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether the filter should begin by calling the default filter shader
|
||||
/// PxDefaultSimulationFilterShader that emulates the PhysX 2.8 rules.
|
||||
///
|
||||
/// Default: true
|
||||
pub fn set_call_default_filter_shader_first(
|
||||
&mut self,
|
||||
call_default_filter_shader_first: bool,
|
||||
) -> &mut Self {
|
||||
self.call_default_filter_shader_first = call_default_filter_shader_first;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the number of threads to use for simulation
|
||||
///
|
||||
/// Default: not set
|
||||
pub fn set_simulation_threading(
|
||||
&mut self,
|
||||
simulation_threading: SimulationThreadType,
|
||||
) -> &mut Self {
|
||||
self.simulation_threading = Some(simulation_threading);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set collision detection type
|
||||
///
|
||||
/// Default: Sweep and prune
|
||||
pub fn set_broad_phase_type(&mut self, broad_phase_type: BroadPhaseType) -> &mut Self {
|
||||
self.broad_phase_type = broad_phase_type;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set if CCD (continuous collision detection) should be available for use in the scene.
|
||||
/// Doesn't automatically enable it for all rigid bodies, they still need to be flagged.
|
||||
///
|
||||
/// If you don't set enable_ccd_resweep to true, eDISABLE_CCD_RESWEEP is set, which improves performance
|
||||
/// at the cost of accuracy right after bounces.
|
||||
///
|
||||
/// Default: false, false
|
||||
pub fn set_use_ccd(&mut self, use_ccd: bool, enable_ccd_resweep: bool) -> &mut Self {
|
||||
self.use_ccd = use_ccd;
|
||||
self.enable_ccd_resweep = enable_ccd_resweep;
|
||||
self
|
||||
}
|
||||
|
||||
pub(super) fn build_desc(&self, physics: &mut Physics) -> physx_sys::PxSceneDesc {
|
||||
unsafe {
|
||||
let tolerances = physics.get_tolerances_scale();
|
||||
let mut scene_desc = physx_sys::PxSceneDesc_new(tolerances);
|
||||
|
||||
let dispatcher = match self.simulation_threading.as_ref().expect("foo") {
|
||||
SimulationThreadType::Default => {
|
||||
physx_sys::phys_PxDefaultCpuDispatcherCreate(1, std::ptr::null_mut()) as *mut _
|
||||
}
|
||||
SimulationThreadType::Dedicated(count) => {
|
||||
physx_sys::phys_PxDefaultCpuDispatcherCreate(*count, std::ptr::null_mut())
|
||||
as *mut _
|
||||
}
|
||||
SimulationThreadType::Shared(dispatcher) => *dispatcher as *mut _,
|
||||
};
|
||||
|
||||
scene_desc.cpuDispatcher = dispatcher;
|
||||
scene_desc.gravity = physx::transform::gl_to_px_v3(self.gravity);
|
||||
if self.use_ccd {
|
||||
scene_desc.flags.mBits |= physx_sys::PxSceneFlag::eENABLE_CCD;
|
||||
if !self.enable_ccd_resweep {
|
||||
scene_desc.flags.mBits |= physx_sys::PxSceneFlag::eDISABLE_CCD_RESWEEP;
|
||||
}
|
||||
}
|
||||
if let Some(filter_shader) = self.simulation_filter_shader {
|
||||
physx_sys::enable_custom_filter_shader(
|
||||
&mut scene_desc as *mut physx_sys::PxSceneDesc,
|
||||
filter_shader,
|
||||
if self.call_default_filter_shader_first {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
},
|
||||
);
|
||||
} else {
|
||||
scene_desc.filterShader = physx_sys::get_default_simulation_filter_shader();
|
||||
}
|
||||
|
||||
scene_desc.broadPhaseType = self.broad_phase_type.into();
|
||||
scene_desc.solverType = self.solver_type;
|
||||
scene_desc
|
||||
}
|
||||
}
|
||||
}
|
||||
1652
src_testbed/testbed.rs
Normal file
1652
src_testbed/testbed.rs
Normal file
File diff suppressed because it is too large
Load Diff
568
src_testbed/ui.rs
Normal file
568
src_testbed/ui.rs
Normal file
@@ -0,0 +1,568 @@
|
||||
use kiss3d::conrod::{self, Borderable, Colorable, Labelable, Positionable, Sizeable, Widget};
|
||||
use kiss3d::window::Window;
|
||||
use rapier::dynamics::IntegrationParameters;
|
||||
|
||||
use crate::testbed::{RunMode, TestbedActionFlags, TestbedState, TestbedStateFlags};
|
||||
|
||||
const SIDEBAR_W: f64 = 200.0;
|
||||
const ELEMENT_W: f64 = SIDEBAR_W - 20.0;
|
||||
const ELEMENT_H: f64 = 20.0;
|
||||
const VSPACE: f64 = 4.0;
|
||||
const TITLE_VSPACE: f64 = 4.0;
|
||||
const LEFT_MARGIN: f64 = 10.0;
|
||||
const ALPHA: f32 = 0.9;
|
||||
|
||||
widget_ids! {
|
||||
pub struct ConrodIds {
|
||||
canvas,
|
||||
title_backends_list,
|
||||
title_demos_list,
|
||||
title_slider_vel_iter,
|
||||
title_slider_pos_iter,
|
||||
title_slider_num_threads,
|
||||
title_slider_ccd_substeps,
|
||||
title_slider_min_island_size,
|
||||
title_warmstart_coeff,
|
||||
title_frequency,
|
||||
backends_list,
|
||||
demos_list,
|
||||
button_pause,
|
||||
button_single_step,
|
||||
button_restart,
|
||||
button_quit,
|
||||
button_prev_example,
|
||||
button_next_example,
|
||||
button_take_snapshot,
|
||||
button_restore_snapshot,
|
||||
slider_vel_iter,
|
||||
slider_pos_iter,
|
||||
slider_num_threads,
|
||||
slider_ccd_substeps,
|
||||
slider_min_island_size,
|
||||
slider_warmstart_coeff,
|
||||
slider_frequency,
|
||||
toggle_sleep,
|
||||
toggle_warm_starting,
|
||||
toggle_sub_stepping,
|
||||
toggle_shapes,
|
||||
toggle_joints,
|
||||
toggle_aabbs,
|
||||
toggle_contact_points,
|
||||
toggle_contact_normals,
|
||||
toggle_center_of_masses,
|
||||
toggle_statistics,
|
||||
toggle_profile,
|
||||
toggle_debug,
|
||||
toggle_wireframe,
|
||||
separator0,
|
||||
separator1,
|
||||
separator2,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TestbedUi {
|
||||
ids: ConrodIds,
|
||||
}
|
||||
|
||||
impl TestbedUi {
|
||||
pub fn new(window: &mut Window) -> Self {
|
||||
use conrod::position::{Align, Direction, Padding, Position, Relative};
|
||||
|
||||
let mut ui = window.conrod_ui_mut();
|
||||
ui.theme = conrod::Theme {
|
||||
name: "Testbed theme".to_string(),
|
||||
padding: Padding::none(),
|
||||
x_position: Position::Relative(Relative::Align(Align::Start), None),
|
||||
y_position: Position::Relative(Relative::Direction(Direction::Backwards, 20.0), None),
|
||||
background_color: conrod::color::DARK_CHARCOAL.alpha(ALPHA),
|
||||
shape_color: conrod::color::LIGHT_CHARCOAL.alpha(ALPHA),
|
||||
border_color: conrod::color::BLACK.alpha(ALPHA),
|
||||
border_width: 0.0,
|
||||
label_color: conrod::color::WHITE.alpha(ALPHA),
|
||||
font_id: None,
|
||||
font_size_large: 15,
|
||||
font_size_medium: 11,
|
||||
font_size_small: 8,
|
||||
widget_styling: conrod::theme::StyleMap::default(),
|
||||
mouse_drag_threshold: 0.0,
|
||||
double_click_threshold: std::time::Duration::from_millis(500),
|
||||
};
|
||||
|
||||
Self {
|
||||
ids: ConrodIds::new(ui.widget_id_generator()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
integration_parameters: &mut IntegrationParameters,
|
||||
state: &mut TestbedState,
|
||||
) {
|
||||
let ui_root = window.conrod_ui().window;
|
||||
let mut ui = window.conrod_ui_mut().set_widgets();
|
||||
conrod::widget::Canvas::new()
|
||||
// .title_bar("Demos")
|
||||
// .title_bar_color(conrod::color::Color::Rgba(1.0, 0.0, 0.0, 1.0))
|
||||
// .pad(100.0)
|
||||
// .pad_left(MARGIN)
|
||||
// .pad_right(MARGIN)
|
||||
.scroll_kids_vertically()
|
||||
.mid_right_with_margin(10.0)
|
||||
.w(SIDEBAR_W)
|
||||
.padded_h_of(ui_root, 10.0)
|
||||
.set(self.ids.canvas, &mut ui);
|
||||
|
||||
// NOTE: If examples_names is empty, we can't change the backend because
|
||||
// we have no way to properly restart the simulation.
|
||||
if state.backend_names.len() > 1 && !state.example_names.is_empty() {
|
||||
/*
|
||||
* Backend drop-down.
|
||||
*/
|
||||
conrod::widget::Text::new("Select backend:")
|
||||
.top_left_with_margins_on(self.ids.canvas, VSPACE, LEFT_MARGIN)
|
||||
.set(self.ids.title_backends_list, &mut ui);
|
||||
|
||||
for selected in conrod::widget::DropDownList::new(
|
||||
&state.backend_names,
|
||||
Some(state.selected_backend),
|
||||
)
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.down_from(self.ids.title_backends_list, TITLE_VSPACE)
|
||||
.left_justify_label()
|
||||
.w_h(ELEMENT_W, ELEMENT_H)
|
||||
.color(conrod::color::LIGHT_CHARCOAL) // No alpha.
|
||||
.set(self.ids.backends_list, &mut ui)
|
||||
{
|
||||
if selected != state.selected_backend {
|
||||
#[cfg(feature = "dim3")]
|
||||
fn is_physx(id: usize) -> bool {
|
||||
id == crate::testbed::PHYSX_BACKEND_PATCH_FRICTION
|
||||
|| id == crate::testbed::PHYSX_BACKEND_TWO_FRICTION_DIR
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "dim3", feature = "other-backends"))]
|
||||
if (is_physx(state.selected_backend) && !is_physx(selected))
|
||||
|| (!is_physx(state.selected_backend) && is_physx(selected))
|
||||
{
|
||||
// PhysX defaults (4 position iterations, 1 velocity) are the
|
||||
// opposite of rapier's (4 velocity iterations, 1 position).
|
||||
std::mem::swap(
|
||||
&mut integration_parameters.max_position_iterations,
|
||||
&mut integration_parameters.max_velocity_iterations,
|
||||
);
|
||||
}
|
||||
|
||||
state.selected_backend = selected;
|
||||
state
|
||||
.action_flags
|
||||
.set(TestbedActionFlags::BACKEND_CHANGED, true)
|
||||
}
|
||||
}
|
||||
|
||||
separator(
|
||||
self.ids.canvas,
|
||||
self.ids.backends_list,
|
||||
self.ids.separator0,
|
||||
&mut ui,
|
||||
);
|
||||
} else {
|
||||
conrod::widget::Text::new("")
|
||||
.top_left_with_margins_on(self.ids.canvas, 0.0, LEFT_MARGIN)
|
||||
.set(self.ids.separator0, &mut ui);
|
||||
}
|
||||
|
||||
let display_ticks = state.example_names.len() > 1;
|
||||
let _select_example_title = if display_ticks {
|
||||
"Select example:"
|
||||
} else {
|
||||
"Current example:"
|
||||
};
|
||||
let tick_width = if display_ticks { 20.0 } else { 0.0 };
|
||||
|
||||
/*
|
||||
* Examples drop-down.
|
||||
*/
|
||||
conrod::widget::Text::new("Select example:")
|
||||
.down_from(self.ids.separator0, VSPACE)
|
||||
// .top_left_with_margins_on(self.ids.canvas, VSPACE, LEFT_MARGIN)
|
||||
// .w_h(ELEMENT_W, ELEMENT_H)
|
||||
.set(self.ids.title_demos_list, &mut ui);
|
||||
|
||||
for selected in
|
||||
conrod::widget::DropDownList::new(&state.example_names, Some(state.selected_example))
|
||||
// .mid_top_with_margin_on(self.ids.canvas, 20.0)
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.down_from(self.ids.title_demos_list, TITLE_VSPACE)
|
||||
// .right_from(self.ids.button_prev_example, 0.0)
|
||||
.left_justify_label()
|
||||
.w_h(ELEMENT_W - tick_width, ELEMENT_H)
|
||||
.color(conrod::color::LIGHT_CHARCOAL) // No alpha.
|
||||
.set(self.ids.demos_list, &mut ui)
|
||||
{
|
||||
if selected != state.selected_example {
|
||||
state.selected_example = selected;
|
||||
state
|
||||
.action_flags
|
||||
.set(TestbedActionFlags::EXAMPLE_CHANGED, true)
|
||||
}
|
||||
}
|
||||
|
||||
if display_ticks {
|
||||
for _click in conrod::widget::Button::new()
|
||||
.label("<")
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.left_from(self.ids.demos_list, 0.0)
|
||||
.w_h(10.0, ELEMENT_H)
|
||||
.enabled(state.selected_example > 0)
|
||||
.color(conrod::color::LIGHT_CHARCOAL) // No alpha.
|
||||
.set(self.ids.button_prev_example, &mut ui)
|
||||
{
|
||||
if state.selected_example > 0 {
|
||||
state.selected_example -= 1;
|
||||
state
|
||||
.action_flags
|
||||
.set(TestbedActionFlags::EXAMPLE_CHANGED, true)
|
||||
}
|
||||
}
|
||||
|
||||
for _click in conrod::widget::Button::new()
|
||||
.label(">")
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.right_from(self.ids.demos_list, 0.0)
|
||||
.w_h(10.0, ELEMENT_H)
|
||||
.enabled(state.selected_example + 1 < state.example_names.len())
|
||||
.color(conrod::color::LIGHT_CHARCOAL) // No alpha.
|
||||
.set(self.ids.button_next_example, &mut ui)
|
||||
{
|
||||
if state.selected_example + 1 < state.example_names.len() {
|
||||
state.selected_example += 1;
|
||||
state
|
||||
.action_flags
|
||||
.set(TestbedActionFlags::EXAMPLE_CHANGED, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
separator(
|
||||
self.ids.canvas,
|
||||
self.ids.demos_list,
|
||||
self.ids.separator1,
|
||||
&mut ui,
|
||||
);
|
||||
|
||||
let curr_vel_iters = integration_parameters.max_velocity_iterations;
|
||||
let curr_pos_iters = integration_parameters.max_position_iterations;
|
||||
#[cfg(feature = "parallel")]
|
||||
let curr_num_threads = state.num_threads;
|
||||
let curr_max_ccd_substeps = integration_parameters.max_ccd_substeps;
|
||||
let curr_min_island_size = integration_parameters.min_island_size;
|
||||
let curr_warmstart_coeff = integration_parameters.warmstart_coeff;
|
||||
let curr_frequency = integration_parameters.inv_dt().round() as usize;
|
||||
|
||||
conrod::widget::Text::new("Vel. Iters.:")
|
||||
.down_from(self.ids.separator1, VSPACE)
|
||||
.set(self.ids.title_slider_vel_iter, &mut ui);
|
||||
|
||||
for val in conrod::widget::Slider::new(curr_vel_iters as f32, 0.0, 200.0)
|
||||
.label(&curr_vel_iters.to_string())
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.down_from(self.ids.title_slider_vel_iter, TITLE_VSPACE)
|
||||
.w_h(ELEMENT_W, ELEMENT_H)
|
||||
.set(self.ids.slider_vel_iter, &mut ui)
|
||||
{
|
||||
integration_parameters.max_velocity_iterations = val as usize;
|
||||
}
|
||||
|
||||
conrod::widget::Text::new("Pos. Iters.:")
|
||||
.down_from(self.ids.slider_vel_iter, VSPACE)
|
||||
.set(self.ids.title_slider_pos_iter, &mut ui);
|
||||
|
||||
for val in conrod::widget::Slider::new(curr_pos_iters as f32, 0.0, 200.0)
|
||||
.label(&curr_pos_iters.to_string())
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.down_from(self.ids.title_slider_pos_iter, TITLE_VSPACE)
|
||||
.w_h(ELEMENT_W, ELEMENT_H)
|
||||
.set(self.ids.slider_pos_iter, &mut ui)
|
||||
{
|
||||
integration_parameters.max_position_iterations = val as usize;
|
||||
}
|
||||
|
||||
#[cfg(feature = "parallel")]
|
||||
{
|
||||
conrod::widget::Text::new("Num. Threads.:")
|
||||
.down_from(self.ids.slider_pos_iter, VSPACE)
|
||||
.set(self.ids.title_slider_num_threads, &mut ui);
|
||||
|
||||
for val in conrod::widget::Slider::new(
|
||||
curr_num_threads as f32,
|
||||
1.0,
|
||||
num_cpus::get_physical() as f32,
|
||||
)
|
||||
.label(&curr_num_threads.to_string())
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.down_from(self.ids.title_slider_num_threads, TITLE_VSPACE)
|
||||
.w_h(ELEMENT_W, ELEMENT_H)
|
||||
.set(self.ids.slider_num_threads, &mut ui)
|
||||
{
|
||||
if state.num_threads != val as usize {
|
||||
state.num_threads = val as usize;
|
||||
state.thread_pool = rapier::rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(state.num_threads)
|
||||
.build()
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conrod::widget::Text::new("CCD substeps:")
|
||||
.down_from(
|
||||
if cfg!(feature = "parallel") {
|
||||
self.ids.slider_num_threads
|
||||
} else {
|
||||
self.ids.slider_pos_iter
|
||||
},
|
||||
VSPACE,
|
||||
)
|
||||
.set(self.ids.title_slider_ccd_substeps, &mut ui);
|
||||
|
||||
for val in conrod::widget::Slider::new(curr_max_ccd_substeps as f32, 0.0, 10.0)
|
||||
.label(&curr_max_ccd_substeps.to_string())
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.down_from(self.ids.title_slider_ccd_substeps, TITLE_VSPACE)
|
||||
.w_h(ELEMENT_W, ELEMENT_H)
|
||||
.set(self.ids.slider_ccd_substeps, &mut ui)
|
||||
{
|
||||
integration_parameters.max_ccd_substeps = val as usize;
|
||||
}
|
||||
|
||||
conrod::widget::Text::new("Min island size:")
|
||||
.down_from(self.ids.slider_ccd_substeps, VSPACE)
|
||||
.set(self.ids.title_slider_min_island_size, &mut ui);
|
||||
|
||||
for val in conrod::widget::Slider::new(curr_min_island_size as f32, 1.0, 10000.0)
|
||||
.label(&curr_min_island_size.to_string())
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.down_from(self.ids.title_slider_min_island_size, TITLE_VSPACE)
|
||||
.w_h(ELEMENT_W, ELEMENT_H)
|
||||
.set(self.ids.slider_min_island_size, &mut ui)
|
||||
{
|
||||
integration_parameters.min_island_size = val as usize;
|
||||
}
|
||||
|
||||
conrod::widget::Text::new("Warm-start coeff.:")
|
||||
.down_from(self.ids.slider_min_island_size, VSPACE)
|
||||
.set(self.ids.title_warmstart_coeff, &mut ui);
|
||||
|
||||
for val in conrod::widget::Slider::new(curr_warmstart_coeff as f32, 0.0, 1.0)
|
||||
.label(&format!("{:.2}", curr_warmstart_coeff))
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.down_from(self.ids.title_warmstart_coeff, TITLE_VSPACE)
|
||||
.w_h(ELEMENT_W, ELEMENT_H)
|
||||
.set(self.ids.slider_warmstart_coeff, &mut ui)
|
||||
{
|
||||
integration_parameters.warmstart_coeff = val;
|
||||
}
|
||||
|
||||
conrod::widget::Text::new("Frequency:")
|
||||
.down_from(self.ids.slider_warmstart_coeff, VSPACE)
|
||||
.set(self.ids.title_frequency, &mut ui);
|
||||
|
||||
for val in conrod::widget::Slider::new(curr_frequency as f32, 0.0, 240.0)
|
||||
.label(&format!("{:.2}Hz", curr_frequency))
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.down_from(self.ids.title_frequency, TITLE_VSPACE)
|
||||
.w_h(ELEMENT_W, ELEMENT_H)
|
||||
.set(self.ids.slider_frequency, &mut ui)
|
||||
{
|
||||
integration_parameters.set_inv_dt(val.round());
|
||||
}
|
||||
|
||||
let toggle_list = [
|
||||
("Sleep", self.ids.toggle_sleep, TestbedStateFlags::SLEEP),
|
||||
// ("Warm Starting", self.ids.toggle_warm_starting, TestbedStateFlags::WARM_STARTING),
|
||||
(
|
||||
"Sub-Stepping",
|
||||
self.ids.toggle_sub_stepping,
|
||||
TestbedStateFlags::SUB_STEPPING,
|
||||
),
|
||||
("", self.ids.separator2, TestbedStateFlags::NONE),
|
||||
// ("Shapes", self.ids.toggle_shapes, TestbedStateFlags::SHAPES),
|
||||
// ("Joints", self.ids.toggle_joints, TestbedStateFlags::JOINTS),
|
||||
("AABBs", self.ids.toggle_aabbs, TestbedStateFlags::AABBS),
|
||||
(
|
||||
"Contacts",
|
||||
self.ids.toggle_contact_points,
|
||||
TestbedStateFlags::CONTACT_POINTS,
|
||||
),
|
||||
// ("ContactManifold Normals", self.ids.toggle_contact_normals, TestbedStateFlags::CONTACT_NORMALS),
|
||||
(
|
||||
"Wireframe",
|
||||
self.ids.toggle_wireframe,
|
||||
TestbedStateFlags::WIREFRAME,
|
||||
),
|
||||
// ("Center of Masses", self.ids.toggle_center_of_masses, TestbedStateFlags::CENTER_OF_MASSES),
|
||||
// ("Statistics", self.ids.toggle_statistics, TestbedStateFlags::STATISTICS),
|
||||
(
|
||||
"Profile",
|
||||
self.ids.toggle_profile,
|
||||
TestbedStateFlags::PROFILE,
|
||||
),
|
||||
(
|
||||
"Debug infos",
|
||||
self.ids.toggle_debug,
|
||||
TestbedStateFlags::DEBUG,
|
||||
),
|
||||
];
|
||||
|
||||
toggles(
|
||||
&toggle_list,
|
||||
self.ids.canvas,
|
||||
self.ids.slider_frequency,
|
||||
&mut ui,
|
||||
&mut state.flags,
|
||||
);
|
||||
|
||||
let label = if state.running == RunMode::Stop {
|
||||
"Start (T)"
|
||||
} else {
|
||||
"Pause (T)"
|
||||
};
|
||||
for _press in conrod::widget::Button::new()
|
||||
.label(label)
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.down_from(self.ids.toggle_debug, VSPACE)
|
||||
.w_h(ELEMENT_W, ELEMENT_H)
|
||||
.set(self.ids.button_pause, &mut ui)
|
||||
{
|
||||
if state.running == RunMode::Stop {
|
||||
state.running = RunMode::Running
|
||||
} else {
|
||||
state.running = RunMode::Stop
|
||||
}
|
||||
}
|
||||
|
||||
for _press in conrod::widget::Button::new()
|
||||
.label("Single Step (S)")
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.down_from(self.ids.button_pause, VSPACE)
|
||||
.set(self.ids.button_single_step, &mut ui)
|
||||
{
|
||||
state.running = RunMode::Step
|
||||
}
|
||||
|
||||
for _press in conrod::widget::Button::new()
|
||||
.label("Take snapshot")
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.down_from(self.ids.button_single_step, VSPACE)
|
||||
.set(self.ids.button_take_snapshot, &mut ui)
|
||||
{
|
||||
state
|
||||
.action_flags
|
||||
.set(TestbedActionFlags::TAKE_SNAPSHOT, true);
|
||||
}
|
||||
|
||||
for _press in conrod::widget::Button::new()
|
||||
.label("Restore snapshot")
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.down_from(self.ids.button_take_snapshot, VSPACE)
|
||||
.set(self.ids.button_restore_snapshot, &mut ui)
|
||||
{
|
||||
state
|
||||
.action_flags
|
||||
.set(TestbedActionFlags::RESTORE_SNAPSHOT, true);
|
||||
}
|
||||
|
||||
let before_quit_button_id = if !state.example_names.is_empty() {
|
||||
for _press in conrod::widget::Button::new()
|
||||
.label("Restart (R)")
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.down_from(self.ids.button_restore_snapshot, VSPACE)
|
||||
.set(self.ids.button_restart, &mut ui)
|
||||
{
|
||||
state.action_flags.set(TestbedActionFlags::RESTART, true);
|
||||
}
|
||||
|
||||
self.ids.button_restart
|
||||
} else {
|
||||
self.ids.button_restore_snapshot
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
for _press in conrod::widget::Button::new()
|
||||
.label("Quit (Esc)")
|
||||
.align_middle_x_of(self.ids.canvas)
|
||||
.down_from(before_quit_button_id, VSPACE)
|
||||
.set(self.ids.button_quit, &mut ui)
|
||||
{
|
||||
state.running = RunMode::Quit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn toggles(
|
||||
toggles: &[(&str, conrod::widget::Id, TestbedStateFlags)],
|
||||
canvas: conrod::widget::Id,
|
||||
prev: conrod::widget::Id,
|
||||
ui: &mut conrod::UiCell,
|
||||
flags: &mut TestbedStateFlags,
|
||||
) {
|
||||
toggle(
|
||||
toggles[0].0,
|
||||
toggles[0].2,
|
||||
canvas,
|
||||
prev,
|
||||
toggles[0].1,
|
||||
ui,
|
||||
flags,
|
||||
);
|
||||
|
||||
for win in toggles.windows(2) {
|
||||
toggle(win[1].0, win[1].2, canvas, win[0].1, win[1].1, ui, flags)
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle(
|
||||
title: &str,
|
||||
flag: TestbedStateFlags,
|
||||
canvas: conrod::widget::Id,
|
||||
prev: conrod::widget::Id,
|
||||
curr: conrod::widget::Id,
|
||||
ui: &mut conrod::UiCell,
|
||||
flags: &mut TestbedStateFlags,
|
||||
) {
|
||||
if title == "" {
|
||||
// This is a separator.
|
||||
separator(canvas, prev, curr, ui)
|
||||
} else {
|
||||
for _pressed in conrod::widget::Toggle::new(flags.contains(flag))
|
||||
.mid_left_with_margin_on(canvas, LEFT_MARGIN)
|
||||
.down_from(prev, VSPACE)
|
||||
.w_h(20.0 /*ELEMENT_W*/, ELEMENT_H)
|
||||
.label(title)
|
||||
.label_color(kiss3d::conrod::color::WHITE)
|
||||
.label_x(conrod::position::Relative::Direction(
|
||||
conrod::position::Direction::Forwards,
|
||||
5.0,
|
||||
))
|
||||
.border(2.0)
|
||||
// .border_color(kiss3d::conrod::color::WHITE)
|
||||
.set(curr, ui)
|
||||
{
|
||||
flags.toggle(flag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn separator(
|
||||
canvas: conrod::widget::Id,
|
||||
prev: conrod::widget::Id,
|
||||
curr: conrod::widget::Id,
|
||||
ui: &mut conrod::UiCell,
|
||||
) {
|
||||
conrod::widget::Line::centred([-ELEMENT_W / 2.0, 0.0], [ELEMENT_W / 2.0, 0.0])
|
||||
.align_middle_x_of(canvas)
|
||||
.down_from(prev, VSPACE)
|
||||
.w(ELEMENT_W)
|
||||
.set(curr, ui);
|
||||
}
|
||||
Reference in New Issue
Block a user