From 754a48b7ff6d8c58b1ee08651e60112900b60455 Mon Sep 17 00:00:00 2001 From: Sébastien Crozet Date: Tue, 25 Aug 2020 22:10:25 +0200 Subject: First public release of Rapier. --- src_testbed/Inconsolata.otf | Bin 0 -> 58464 bytes src_testbed/box2d_backend.rs | 237 ++++++ src_testbed/engine.rs | 694 +++++++++++++++ src_testbed/lib.rs | 52 ++ src_testbed/nphysics_backend.rs | 212 +++++ src_testbed/objects/ball.rs | 73 ++ src_testbed/objects/box_node.rs | 73 ++ src_testbed/objects/capsule.rs | 74 ++ src_testbed/objects/convex.rs | 77 ++ src_testbed/objects/heightfield.rs | 120 +++ src_testbed/objects/mesh.rs | 108 +++ src_testbed/objects/mod.rs | 10 + src_testbed/objects/node.rs | 164 ++++ src_testbed/objects/plane.rs | 132 +++ src_testbed/objects/polyline.rs | 79 ++ src_testbed/physx_backend.rs | 604 +++++++++++++ src_testbed/testbed.rs | 1652 ++++++++++++++++++++++++++++++++++++ src_testbed/ui.rs | 568 +++++++++++++ 18 files changed, 4929 insertions(+) create mode 100644 src_testbed/Inconsolata.otf create mode 100644 src_testbed/box2d_backend.rs create mode 100644 src_testbed/engine.rs create mode 100644 src_testbed/lib.rs create mode 100644 src_testbed/nphysics_backend.rs create mode 100644 src_testbed/objects/ball.rs create mode 100644 src_testbed/objects/box_node.rs create mode 100644 src_testbed/objects/capsule.rs create mode 100644 src_testbed/objects/convex.rs create mode 100644 src_testbed/objects/heightfield.rs create mode 100644 src_testbed/objects/mesh.rs create mode 100644 src_testbed/objects/mod.rs create mode 100644 src_testbed/objects/node.rs create mode 100644 src_testbed/objects/plane.rs create mode 100644 src_testbed/objects/polyline.rs create mode 100644 src_testbed/physx_backend.rs create mode 100644 src_testbed/testbed.rs create mode 100644 src_testbed/ui.rs (limited to 'src_testbed') diff --git a/src_testbed/Inconsolata.otf b/src_testbed/Inconsolata.otf new file mode 100644 index 0000000..3488898 Binary files /dev/null and b/src_testbed/Inconsolata.otf differ diff --git a/src_testbed/box2d_backend.rs b/src_testbed/box2d_backend.rs new file mode 100644 index 0000000..07cef1e --- /dev/null +++ b/src_testbed/box2d_backend.rs @@ -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) -> b2::Vec2 { + b2::Vec2 { x: v.x, y: v.y } +} + +fn b2_vec_to_na_vec(v: b2::Vec2) -> Vector2 { + Vector2::new(v.x, v.y) +} + +fn b2_transform_to_na_isometry(v: b2::Transform) -> Isometry2 { + Isometry2::new(b2_vec_to_na_vec(v.pos), v.rot.angle()) +} + +pub struct Box2dWorld { + world: b2::World, + rapier2box2d: HashMap, +} + +impl Box2dWorld { + pub fn from_rapier( + gravity: Vector2, + 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::>() { + // 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) { + 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()); + } + } + } + } +} diff --git a/src_testbed/engine.rs b/src_testbed/engine.rs new file mode 100644 index 0000000..62ab37e --- /dev/null +++ b/src_testbed/engine.rs @@ -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, p2: &Point, color: &Point3); +} + +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, p2: &Point, color: &Point3) { + #[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>, + #[cfg(feature = "fluids")] + f2sn: HashMap, + #[cfg(feature = "fluids")] + boundary2sn: HashMap, + b2color: HashMap>, + b2wireframe: HashMap, + #[cfg(feature = "fluids")] + f2color: HashMap>, + ground_color: Point3, + camera: Camera, + ground_handle: Option, + #[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) { + 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) { + 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) { + 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 { + let mut color: Point3 = 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 { + 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, + 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, + 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, + particle_radius: f32, + color: Point3, + ) { + 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, + ) { + // 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, + out: &mut Vec, + ) { + 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, + shape: &shape::Plane, + color: Point3, + out: &mut Vec, + ) { + 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, + delta: Isometry, + shape: &shape::Polyline, + color: Point3, + out: &mut Vec, + ) { + 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, + delta: Isometry, + shape: &TriMesh, + color: Point3, + out: &mut Vec, + ) { + 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, + delta: Isometry, + heightfield: &shape::HeightField, + color: Point3, + out: &mut Vec, + ) { + out.push(Node::HeightField(HeightField::new( + object, + colliders, + delta, + heightfield, + color, + window, + ))) + } + + fn add_capsule( + &mut self, + window: &mut Window, + object: DefaultColliderHandle, + colliders: &DefaultColliderSet, + delta: Isometry, + shape: &shape::Capsule, + color: Point3, + out: &mut Vec, + ) { + 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, + delta: Isometry, + shape: &shape::Ball, + color: Point3, + out: &mut Vec, + ) { + 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, + delta: Isometry, + shape: &Cuboid, + color: Point3, + out: &mut Vec, + ) { + 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, + delta: Isometry, + shape: &ConvexPolygon, + color: Point3, + out: &mut Vec, + ) { + 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, + delta: Isometry, + shape: &ConvexHull, + color: Point3, + out: &mut Vec, + ) { + 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) { + 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) { + // 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, at: Point) { + self.camera.look_at(eye, at); + } + + #[cfg(feature = "dim2")] + pub fn look_at(&mut self, at: Point, zoom: f32) { + self.camera.look_at(at, zoom); + } + + pub fn body_nodes(&self, handle: RigidBodyHandle) -> Option<&Vec> { + self.b2sn.get(&handle) + } + + pub fn body_nodes_mut(&mut self, handle: RigidBodyHandle) -> Option<&mut Vec> { + self.b2sn.get_mut(&handle) + } + + pub fn nodes(&self) -> impl Iterator { + self.b2sn.values().flat_map(|val| val.iter()) + } + + pub fn nodes_mut(&mut self) -> impl Iterator { + self.b2sn.values_mut().flat_map(|val| val.iter_mut()) + } + + #[cfg(feature = "dim3")] + pub fn set_up_axis(&mut self, up_axis: na::Vector3) { + self.camera.set_up_axis(up_axis); + } +} + +impl Default for GraphicsManager { + fn default() -> Self { + Self::new() + } +} diff --git a/src_testbed/lib.rs b/src_testbed/lib.rs new file mode 100644 index 0000000..e077bb1 --- /dev/null +++ b/src_testbed/lib.rs @@ -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 = na::Isometry2; + pub type Vector = na::Vector2; + pub type Point = na::Point2; + pub type Translation = na::Translation2; +} + +#[cfg(feature = "dim3")] +pub mod math { + pub type Isometry = na::Isometry3; + pub type Vector = na::Vector3; + pub type Point = na::Point3; + pub type Translation = na::Translation3; +} diff --git a/src_testbed/nphysics_backend.rs b/src_testbed/nphysics_backend.rs new file mode 100644 index 0000000..43f3bf9 --- /dev/null +++ b/src_testbed/nphysics_backend.rs @@ -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, + mechanical_world: DefaultMechanicalWorld, + geometrical_world: DefaultGeometricalWorld, + bodies: DefaultBodySet, + colliders: DefaultColliderSet, + joints: DefaultJointConstraintSet, + force_generators: DefaultForceGeneratorSet, +} + +impl NPhysicsWorld { + pub fn from_rapier( + gravity: Vector, + 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> { + let margin = ColliderDesc::::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()), + ) +} diff --git a/src_testbed/objects/ball.rs b/src_testbed/objects/ball.rs new file mode 100644 index 0000000..f72c284 --- /dev/null +++ b/src_testbed/objects/ball.rs @@ -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, + base_color: Point3, + gfx: GraphicsNode, + collider: ColliderHandle, +} + +impl Ball { + pub fn new( + collider: ColliderHandle, + radius: f32, + color: Point3, + 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) { + 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 + } +} diff --git a/src_testbed/objects/box_node.rs b/src_testbed/objects/box_node.rs new file mode 100644 index 0000000..493ffba --- /dev/null +++ b/src_testbed/objects/box_node.rs @@ -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, + base_color: Point3, + gfx: GraphicsNode, + collider: ColliderHandle, +} + +impl Box { + pub fn new( + collider: ColliderHandle, + half_extents: Vector, + color: Point3, + 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) { + 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 + } +} diff --git a/src_testbed/objects/capsule.rs b/src_testbed/objects/capsule.rs new file mode 100644 index 0000000..23160be --- /dev/null +++ b/src_testbed/objects/capsule.rs @@ -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, + base_color: Point3, + gfx: GraphicsNode, + collider: ColliderHandle, +} + +impl Capsule { + pub fn new( + collider: ColliderHandle, + capsule: &geometry::Capsule, + color: Point3, + 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) { + 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 + } +} diff --git a/src_testbed/objects/convex.rs b/src_testbed/objects/convex.rs new file mode 100644 index 0000000..0347144 --- /dev/null +++ b/src_testbed/objects/convex.rs @@ -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, + base_color: Point3, + gfx: GraphicsNode, + body: ColliderHandle, +} + +impl Convex { + pub fn new( + body: ColliderHandle, + vertices: Vec>, + color: Point3, + 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) { + 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 + } +} diff --git a/src_testbed/objects/heightfield.rs b/src_testbed/objects/heightfield.rs new file mode 100644 index 0000000..0815592 --- /dev/null +++ b/src_testbed/objects/heightfield.rs @@ -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, + base_color: Point3, + #[cfg(feature = "dim2")] + vertices: Vec>, + #[cfg(feature = "dim3")] + gfx: GraphicsNode, + collider: ColliderHandle, +} + +impl HeightField { + #[cfg(feature = "dim2")] + pub fn new( + collider: ColliderHandle, + heightfield: &shape::HeightField, + color: Point3, + _: &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, + color: Point3, + 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) { + #[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) + } + } +} diff --git a/src_testbed/objects/mesh.rs b/src_testbed/objects/mesh.rs new file mode 100644 index 0000000..5187a8b --- /dev/null +++ b/src_testbed/objects/mesh.rs @@ -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, + base_color: Point3, + gfx: GraphicsNode, + collider: ColliderHandle, +} + +impl Mesh { + pub fn new( + collider: ColliderHandle, + vertices: Vec>, + indices: Vec>, + color: Point3, + 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) { + 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::>().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 + } +} diff --git a/src_testbed/objects/mod.rs b/src_testbed/objects/mod.rs new file mode 100644 index 0000000..82895b3 --- /dev/null +++ b/src_testbed/objects/mod.rs @@ -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; diff --git a/src_testbed/objects/node.rs b/src_testbed/objects/node.rs new file mode 100644 index 0000000..93b5eac --- /dev/null +++ b/src_testbed/objects/node.rs @@ -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) { + 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, + delta: &Isometry, +) { + 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(); + } +} diff --git a/src_testbed/objects/plane.rs b/src_testbed/objects/plane.rs new file mode 100644 index 0000000..218e97a --- /dev/null +++ b/src_testbed/objects/plane.rs @@ -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}