Replace Kiss3d by Bevy for the testbed renderer.
This commit is contained in:
94
src_testbed/camera.rs
Normal file
94
src_testbed/camera.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
// NOTE: this is inspired from the `bevy-orbit-controls` projects but
|
||||
// with some modifications like Panning, and 2D support.
|
||||
use bevy::input::mouse::MouseMotion;
|
||||
use bevy::input::mouse::MouseScrollUnit::{Line, Pixel};
|
||||
use bevy::input::mouse::MouseWheel;
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::camera::Camera;
|
||||
|
||||
const LINE_TO_PIXEL_RATIO: f32 = 0.1;
|
||||
|
||||
pub struct OrbitCamera {
|
||||
pub zoom: f32,
|
||||
pub center: Vec3,
|
||||
pub pan_sensitivity: f32,
|
||||
pub zoom_sensitivity: f32,
|
||||
pub pan_button: MouseButton,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
impl Default for OrbitCamera {
|
||||
fn default() -> Self {
|
||||
OrbitCamera {
|
||||
zoom: 100.0,
|
||||
center: Vec3::ZERO,
|
||||
pan_sensitivity: 1.0,
|
||||
zoom_sensitivity: 0.8,
|
||||
pan_button: MouseButton::Right,
|
||||
enabled: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adapted from the 3D orbit camera from bevy-orbit-controls
|
||||
pub struct OrbitCameraPlugin;
|
||||
impl OrbitCameraPlugin {
|
||||
fn update_transform_system(
|
||||
mut query: Query<(&OrbitCamera, &mut Transform), (Changed<OrbitCamera>, With<Camera>)>,
|
||||
) {
|
||||
for (camera, mut transform) in query.iter_mut() {
|
||||
if camera.enabled {
|
||||
transform.translation = camera.center;
|
||||
transform.scale = Vec3::new(1.0 / camera.zoom, 1.0 / camera.zoom, 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_motion_system(
|
||||
_time: Res<Time>,
|
||||
mut mouse_motion_events: EventReader<MouseMotion>,
|
||||
mouse_button_input: Res<Input<MouseButton>>,
|
||||
mut query: Query<(&mut OrbitCamera, &mut Transform, &mut Camera)>,
|
||||
) {
|
||||
let mut delta = Vec2::ZERO;
|
||||
for event in mouse_motion_events.iter() {
|
||||
delta += event.delta;
|
||||
}
|
||||
for (mut camera, _, _) in query.iter_mut() {
|
||||
if !camera.enabled {
|
||||
continue;
|
||||
}
|
||||
|
||||
if mouse_button_input.pressed(camera.pan_button) {
|
||||
let delta = delta * camera.pan_sensitivity;
|
||||
camera.center += Vec3::new(-delta.x, delta.y, 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn zoom_system(
|
||||
mut mouse_wheel_events: EventReader<MouseWheel>,
|
||||
mut query: Query<&mut OrbitCamera, With<Camera>>,
|
||||
) {
|
||||
let mut total = 0.0;
|
||||
for event in mouse_wheel_events.iter() {
|
||||
total -= event.y
|
||||
* match event.unit {
|
||||
Line => 1.0,
|
||||
Pixel => LINE_TO_PIXEL_RATIO,
|
||||
};
|
||||
}
|
||||
for mut camera in query.iter_mut() {
|
||||
if camera.enabled {
|
||||
camera.zoom *= camera.zoom_sensitivity.powf(total);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Plugin for OrbitCameraPlugin {
|
||||
fn build(&self, app: &mut AppBuilder) {
|
||||
app.add_system(Self::mouse_motion_system.system())
|
||||
.add_system(Self::zoom_system.system())
|
||||
.add_system(Self::update_transform_system.system());
|
||||
}
|
||||
}
|
||||
@@ -1,712 +0,0 @@
|
||||
#[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::{Isometry, Point};
|
||||
use crate::objects::ball::Ball;
|
||||
use crate::objects::box_node::Box as BoxNode;
|
||||
use crate::objects::heightfield::HeightField;
|
||||
use crate::objects::node::{GraphicsNode, Node};
|
||||
use rapier::dynamics::{RigidBodyHandle, RigidBodySet};
|
||||
use rapier::geometry::{ColliderHandle, ColliderSet, Shape};
|
||||
//use crate::objects::capsule::Capsule;
|
||||
use crate::objects::convex::Convex;
|
||||
//#[cfg(feature = "dim3")]
|
||||
//use crate::objects::mesh::Mesh;
|
||||
//use crate::objects::plane::Plane;
|
||||
//#[cfg(feature = "dim2")]
|
||||
//use crate::objects::polyline::Polyline;
|
||||
use crate::objects::capsule::Capsule;
|
||||
#[cfg(feature = "dim3")]
|
||||
use crate::objects::cone::Cone;
|
||||
#[cfg(feature = "dim3")]
|
||||
use crate::objects::cylinder::Cylinder;
|
||||
use crate::objects::mesh::Mesh;
|
||||
use crate::objects::polyline::Polyline;
|
||||
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>>,
|
||||
b2color: HashMap<RigidBodyHandle, Point3<f32>>,
|
||||
c2color: HashMap<ColliderHandle, Point3<f32>>,
|
||||
b2wireframe: HashMap<RigidBodyHandle, bool>,
|
||||
ground_color: Point3<f32>,
|
||||
camera: Camera,
|
||||
}
|
||||
|
||||
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(),
|
||||
b2color: HashMap::new(),
|
||||
c2color: HashMap::new(),
|
||||
ground_color: Point3::new(0.5, 0.5, 0.5),
|
||||
b2wireframe: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.b2sn.clear();
|
||||
self.c2color.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);
|
||||
}
|
||||
|
||||
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_collider_initial_color(&mut self, c: ColliderHandle, color: Point3<f32>) {
|
||||
self.c2color.insert(c, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_color(&mut self) -> Point3<f32> {
|
||||
Self::gen_color(&mut self.rand)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 color = self.c2color.get(collider_handle).copied().unwrap_or(color);
|
||||
let collider = &colliders[*collider_handle];
|
||||
self.do_add_shape(
|
||||
window,
|
||||
*collider_handle,
|
||||
collider.shape(),
|
||||
&Isometry::identity(),
|
||||
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);
|
||||
}
|
||||
|
||||
pub fn add_collider(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
handle: ColliderHandle,
|
||||
colliders: &ColliderSet,
|
||||
) {
|
||||
let collider = &colliders[handle];
|
||||
let color = *self.b2color.get(&collider.parent()).unwrap();
|
||||
let color = self.c2color.get(&handle).copied().unwrap_or(color);
|
||||
let mut nodes =
|
||||
std::mem::replace(self.b2sn.get_mut(&collider.parent()).unwrap(), Vec::new());
|
||||
self.do_add_shape(
|
||||
window,
|
||||
handle,
|
||||
collider.shape(),
|
||||
&Isometry::identity(),
|
||||
color,
|
||||
&mut nodes,
|
||||
);
|
||||
self.b2sn.insert(collider.parent(), nodes);
|
||||
}
|
||||
|
||||
fn do_add_shape(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
handle: ColliderHandle,
|
||||
shape: &dyn Shape,
|
||||
delta: &Isometry<f32>,
|
||||
color: Point3<f32>,
|
||||
out: &mut Vec<Node>,
|
||||
) {
|
||||
if let Some(compound) = shape.as_compound() {
|
||||
for (shape_pos, shape) in compound.shapes() {
|
||||
self.do_add_shape(window, handle, &**shape, shape_pos, color, out)
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ball) = shape.as_ball() {
|
||||
out.push(Node::Ball(Ball::new(
|
||||
handle,
|
||||
*delta,
|
||||
ball.radius,
|
||||
color,
|
||||
window,
|
||||
)))
|
||||
}
|
||||
|
||||
if let Some(cuboid) = shape
|
||||
.as_cuboid()
|
||||
.or(shape.as_round_cuboid().map(|r| &r.base_shape))
|
||||
{
|
||||
out.push(Node::Box(BoxNode::new(
|
||||
handle,
|
||||
*delta,
|
||||
cuboid.half_extents,
|
||||
color,
|
||||
window,
|
||||
)))
|
||||
}
|
||||
|
||||
if let Some(capsule) = shape.as_capsule() {
|
||||
out.push(Node::Capsule(Capsule::new(
|
||||
handle, *delta, capsule, color, window,
|
||||
)))
|
||||
}
|
||||
|
||||
if let Some(triangle) = shape
|
||||
.as_triangle()
|
||||
.or(shape.as_round_triangle().map(|r| &r.base_shape))
|
||||
{
|
||||
out.push(Node::Mesh(Mesh::new(
|
||||
handle,
|
||||
vec![triangle.a, triangle.b, triangle.c],
|
||||
vec![[0, 1, 2]],
|
||||
color,
|
||||
window,
|
||||
)))
|
||||
}
|
||||
|
||||
if let Some(trimesh) = shape.as_trimesh() {
|
||||
out.push(Node::Mesh(Mesh::new(
|
||||
handle,
|
||||
trimesh.vertices().to_vec(),
|
||||
trimesh.indices().to_vec(),
|
||||
color,
|
||||
window,
|
||||
)))
|
||||
}
|
||||
|
||||
if let Some(polyline) = shape.as_polyline() {
|
||||
out.push(Node::Polyline(Polyline::new(
|
||||
handle,
|
||||
polyline.vertices().to_vec(),
|
||||
polyline.indices().to_vec(),
|
||||
color,
|
||||
)))
|
||||
}
|
||||
|
||||
if let Some(heightfield) = shape.as_heightfield() {
|
||||
out.push(Node::HeightField(HeightField::new(
|
||||
handle,
|
||||
heightfield,
|
||||
color,
|
||||
window,
|
||||
)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
if let Some(convex_polygon) = shape
|
||||
.as_convex_polygon()
|
||||
.or(shape.as_round_convex_polygon().map(|r| &r.base_shape))
|
||||
{
|
||||
let vertices = convex_polygon.points().to_vec();
|
||||
out.push(Node::Convex(Convex::new(
|
||||
handle, *delta, vertices, color, window,
|
||||
)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
if let Some(convex_polyhedron) = shape
|
||||
.as_convex_polyhedron()
|
||||
.or(shape.as_round_convex_polyhedron().map(|r| &r.base_shape))
|
||||
{
|
||||
let (vertices, indices) = convex_polyhedron.to_trimesh();
|
||||
out.push(Node::Convex(Convex::new(
|
||||
handle, *delta, vertices, indices, color, window,
|
||||
)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
if let Some(cylinder) = shape
|
||||
.as_cylinder()
|
||||
.or(shape.as_round_cylinder().map(|r| &r.base_shape))
|
||||
{
|
||||
out.push(Node::Cylinder(Cylinder::new(
|
||||
handle,
|
||||
*delta,
|
||||
cylinder.half_height,
|
||||
cylinder.radius,
|
||||
color,
|
||||
window,
|
||||
)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
if let Some(cone) = shape
|
||||
.as_cone()
|
||||
.or(shape.as_round_cone().map(|r| &r.base_shape))
|
||||
{
|
||||
out.push(Node::Cone(Cone::new(
|
||||
handle,
|
||||
*delta,
|
||||
cone.half_height,
|
||||
cone.radius,
|
||||
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,
|
||||
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,
|
||||
)))
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn draw(&mut self, _bodies: &RigidBodySet, 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() {
|
||||
// if let Some(co) = colliders.get(n.collider()) {
|
||||
// let bo = &_bodies[co.parent()];
|
||||
//
|
||||
// if bo.is_dynamic() {
|
||||
// if bo.is_ccd_active() {
|
||||
// n.set_color(Point3::new(1.0, 0.0, 0.0));
|
||||
// } else {
|
||||
// n.set_color(Point3::new(0.0, 1.0, 0.0));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
400
src_testbed/graphics.rs
Normal file
400
src_testbed/graphics.rs
Normal file
@@ -0,0 +1,400 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use na::Point3;
|
||||
|
||||
use crate::math::Isometry;
|
||||
use crate::objects::node::EntityWithGraphics;
|
||||
use rapier::dynamics::{RigidBodyHandle, RigidBodySet};
|
||||
use rapier::geometry::{ColliderHandle, ColliderSet, Shape, ShapeType};
|
||||
//use crate::objects::capsule::Capsule;
|
||||
//#[cfg(feature = "dim3")]
|
||||
//use crate::objects::mesh::Mesh;
|
||||
//use crate::objects::plane::Plane;
|
||||
//#[cfg(feature = "dim2")]
|
||||
//use crate::objects::polyline::Polyline;
|
||||
// use crate::objects::mesh::Mesh;
|
||||
use rand::{Rng, SeedableRng};
|
||||
use rand_pcg::Pcg32;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct GraphicsManager {
|
||||
rand: Pcg32,
|
||||
b2sn: HashMap<RigidBodyHandle, Vec<EntityWithGraphics>>,
|
||||
b2color: HashMap<RigidBodyHandle, Point3<f32>>,
|
||||
c2color: HashMap<ColliderHandle, Point3<f32>>,
|
||||
b2wireframe: HashMap<RigidBodyHandle, bool>,
|
||||
ground_color: Point3<f32>,
|
||||
prefab_meshes: HashMap<ShapeType, Handle<Mesh>>,
|
||||
}
|
||||
|
||||
impl GraphicsManager {
|
||||
pub fn new() -> GraphicsManager {
|
||||
GraphicsManager {
|
||||
rand: Pcg32::seed_from_u64(0),
|
||||
b2sn: HashMap::new(),
|
||||
b2color: HashMap::new(),
|
||||
c2color: HashMap::new(),
|
||||
ground_color: Point3::new(0.5, 0.5, 0.5),
|
||||
b2wireframe: HashMap::new(),
|
||||
prefab_meshes: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self, commands: &mut Commands) {
|
||||
for sns in self.b2sn.values_mut() {
|
||||
for sn in sns.iter_mut() {
|
||||
commands.entity(sn.entity).despawn()
|
||||
}
|
||||
}
|
||||
|
||||
self.b2sn.clear();
|
||||
self.c2color.clear();
|
||||
self.b2color.clear();
|
||||
self.b2wireframe.clear();
|
||||
self.rand = Pcg32::seed_from_u64(0);
|
||||
}
|
||||
|
||||
pub fn remove_collider_nodes(
|
||||
&mut self,
|
||||
commands: &mut Commands,
|
||||
body: RigidBodyHandle,
|
||||
collider: ColliderHandle,
|
||||
) {
|
||||
if let Some(sns) = self.b2sn.get_mut(&body) {
|
||||
for sn in sns.iter_mut() {
|
||||
if sn.collider == collider {
|
||||
commands.entity(sn.entity).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_body_nodes(&mut self, commands: &mut Commands, body: RigidBodyHandle) {
|
||||
if let Some(sns) = self.b2sn.get_mut(&body) {
|
||||
for sn in sns.iter_mut() {
|
||||
commands.entity(sn.entity).despawn();
|
||||
}
|
||||
}
|
||||
|
||||
self.b2sn.remove(&body);
|
||||
}
|
||||
|
||||
pub fn set_body_color(
|
||||
&mut self,
|
||||
materials: &mut Assets<StandardMaterial>,
|
||||
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(materials, color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_initial_body_color(&mut self, b: RigidBodyHandle, color: Point3<f32>) {
|
||||
self.b2color.insert(b, color);
|
||||
}
|
||||
|
||||
pub fn set_initial_collider_color(&mut self, c: ColliderHandle, color: Point3<f32>) {
|
||||
self.c2color.insert(c, 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);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_color(&mut self) -> Point3<f32> {
|
||||
Self::gen_color(&mut self.rand)
|
||||
}
|
||||
|
||||
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,
|
||||
materials: &mut Assets<StandardMaterial>,
|
||||
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(materials, handle, color);
|
||||
|
||||
color
|
||||
}
|
||||
|
||||
pub fn add(
|
||||
&mut self,
|
||||
commands: &mut Commands,
|
||||
meshes: &mut Assets<Mesh>,
|
||||
materials: &mut Assets<StandardMaterial>,
|
||||
components: &mut Query<(&mut Transform,)>,
|
||||
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(materials, handle, !body.is_dynamic()));
|
||||
|
||||
self.add_with_color(
|
||||
commands, meshes, materials, components, handle, bodies, colliders, color,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn add_with_color(
|
||||
&mut self,
|
||||
commands: &mut Commands,
|
||||
meshes: &mut Assets<Mesh>,
|
||||
materials: &mut Assets<StandardMaterial>,
|
||||
components: &mut Query<(&mut Transform,)>,
|
||||
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 color = self.c2color.get(collider_handle).copied().unwrap_or(color);
|
||||
let collider = &colliders[*collider_handle];
|
||||
self.do_add_shape(
|
||||
commands,
|
||||
meshes,
|
||||
materials,
|
||||
*collider_handle,
|
||||
collider.shape(),
|
||||
collider.is_sensor(),
|
||||
collider.position(),
|
||||
&Isometry::identity(),
|
||||
color,
|
||||
&mut new_nodes,
|
||||
);
|
||||
}
|
||||
|
||||
new_nodes
|
||||
.iter_mut()
|
||||
.for_each(|n| n.update(colliders, components));
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
pub fn add_collider(
|
||||
&mut self,
|
||||
commands: &mut Commands,
|
||||
meshes: &mut Assets<Mesh>,
|
||||
materials: &mut Assets<StandardMaterial>,
|
||||
handle: ColliderHandle,
|
||||
colliders: &ColliderSet,
|
||||
) {
|
||||
let collider = &colliders[handle];
|
||||
let color = *self.b2color.get(&collider.parent()).unwrap();
|
||||
let color = self.c2color.get(&handle).copied().unwrap_or(color);
|
||||
let mut nodes =
|
||||
std::mem::replace(self.b2sn.get_mut(&collider.parent()).unwrap(), Vec::new());
|
||||
self.do_add_shape(
|
||||
commands,
|
||||
meshes,
|
||||
materials,
|
||||
handle,
|
||||
collider.shape(),
|
||||
collider.is_sensor(),
|
||||
collider.position(),
|
||||
&Isometry::identity(),
|
||||
color,
|
||||
&mut nodes,
|
||||
);
|
||||
self.b2sn.insert(collider.parent(), nodes);
|
||||
}
|
||||
|
||||
fn do_add_shape(
|
||||
&mut self,
|
||||
commands: &mut Commands,
|
||||
meshes: &mut Assets<Mesh>,
|
||||
materials: &mut Assets<StandardMaterial>,
|
||||
handle: ColliderHandle,
|
||||
shape: &dyn Shape,
|
||||
sensor: bool,
|
||||
pos: &Isometry<f32>,
|
||||
delta: &Isometry<f32>,
|
||||
color: Point3<f32>,
|
||||
out: &mut Vec<EntityWithGraphics>,
|
||||
) {
|
||||
if let Some(compound) = shape.as_compound() {
|
||||
for (shape_pos, shape) in compound.shapes() {
|
||||
self.do_add_shape(
|
||||
commands,
|
||||
meshes,
|
||||
materials,
|
||||
handle,
|
||||
&**shape,
|
||||
sensor,
|
||||
pos,
|
||||
&(shape_pos * delta),
|
||||
color,
|
||||
out,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if self.prefab_meshes.is_empty() {
|
||||
EntityWithGraphics::gen_prefab_meshes(&mut self.prefab_meshes, meshes);
|
||||
}
|
||||
|
||||
let node = EntityWithGraphics::spawn(
|
||||
commands,
|
||||
meshes,
|
||||
materials,
|
||||
&self.prefab_meshes,
|
||||
shape,
|
||||
handle,
|
||||
*pos,
|
||||
*delta,
|
||||
color,
|
||||
sensor,
|
||||
);
|
||||
out.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
&mut self,
|
||||
_bodies: &RigidBodySet,
|
||||
colliders: &ColliderSet,
|
||||
components: &mut Query<(&mut Transform,)>,
|
||||
) {
|
||||
for (_, ns) in self.b2sn.iter_mut() {
|
||||
for n in ns.iter_mut() {
|
||||
// if let Some(co) = colliders.get(n.collider()) {
|
||||
// let bo = &_bodies[co.parent()];
|
||||
//
|
||||
// if bo.is_dynamic() {
|
||||
// if bo.is_ccd_active() {
|
||||
// n.set_color(Point3::new(1.0, 0.0, 0.0));
|
||||
// } else {
|
||||
// n.set_color(Point3::new(0.0, 1.0, 0.0));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
n.update(colliders, components);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 body_nodes(&self, handle: RigidBodyHandle) -> Option<&Vec<EntityWithGraphics>> {
|
||||
self.b2sn.get(&handle)
|
||||
}
|
||||
|
||||
pub fn body_nodes_mut(
|
||||
&mut self,
|
||||
handle: RigidBodyHandle,
|
||||
) -> Option<&mut Vec<EntityWithGraphics>> {
|
||||
self.b2sn.get_mut(&handle)
|
||||
}
|
||||
|
||||
pub fn nodes(&self) -> impl Iterator<Item = &EntityWithGraphics> {
|
||||
self.b2sn.values().flat_map(|val| val.iter())
|
||||
}
|
||||
|
||||
pub fn nodes_mut(&mut self) -> impl Iterator<Item = &mut EntityWithGraphics> {
|
||||
self.b2sn.values_mut().flat_map(|val| val.iter_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GraphicsManager {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
use crate::{
|
||||
physics::{PhysicsEvents, PhysicsState},
|
||||
GraphicsManager,
|
||||
TestbedGraphics,
|
||||
};
|
||||
use kiss3d::window::Window;
|
||||
use plugin::HarnessPlugin;
|
||||
use rapier::dynamics::{CCDSolver, IntegrationParameters, IslandManager, JointSet, RigidBodySet};
|
||||
use rapier::geometry::{BroadPhase, ColliderSet, NarrowPhase};
|
||||
@@ -52,17 +51,8 @@ pub struct Harness {
|
||||
pub state: RunState,
|
||||
}
|
||||
|
||||
type Callbacks = Vec<
|
||||
Box<
|
||||
dyn FnMut(
|
||||
Option<&mut Window>,
|
||||
Option<&mut GraphicsManager>,
|
||||
&mut PhysicsState,
|
||||
&PhysicsEvents,
|
||||
&RunState,
|
||||
),
|
||||
>,
|
||||
>;
|
||||
type Callbacks =
|
||||
Vec<Box<dyn FnMut(Option<&mut TestbedGraphics>, &mut PhysicsState, &PhysicsEvents, &RunState)>>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Harness {
|
||||
@@ -145,13 +135,7 @@ impl Harness {
|
||||
}
|
||||
|
||||
pub fn add_callback<
|
||||
F: FnMut(
|
||||
Option<&mut Window>,
|
||||
Option<&mut GraphicsManager>,
|
||||
&mut PhysicsState,
|
||||
&PhysicsEvents,
|
||||
&RunState,
|
||||
) + 'static,
|
||||
F: FnMut(Option<&mut TestbedGraphics>, &mut PhysicsState, &PhysicsEvents, &RunState) + 'static,
|
||||
>(
|
||||
&mut self,
|
||||
callback: F,
|
||||
@@ -160,14 +144,10 @@ impl Harness {
|
||||
}
|
||||
|
||||
pub fn step(&mut self) {
|
||||
self.step_with_graphics(None, None);
|
||||
self.step_with_graphics(None);
|
||||
}
|
||||
|
||||
pub fn step_with_graphics(
|
||||
&mut self,
|
||||
window: Option<&mut Window>,
|
||||
graphics: Option<&mut GraphicsManager>,
|
||||
) {
|
||||
pub fn step_with_graphics(&mut self, mut graphics: Option<&mut TestbedGraphics>) {
|
||||
#[cfg(feature = "parallel")]
|
||||
{
|
||||
let physics = &mut self.physics;
|
||||
@@ -214,26 +194,13 @@ impl Harness {
|
||||
plugin.step(&mut self.physics, &self.state)
|
||||
}
|
||||
|
||||
// FIXME: This assumes either window & graphics are Some, or they are all None
|
||||
// this is required as we cannot pass Option<&mut Window> & Option<&mut GraphicsManager directly in a loop
|
||||
// there must be a better way of doing this?
|
||||
match (window, graphics) {
|
||||
(Some(window), Some(graphics)) => {
|
||||
for f in &mut self.callbacks {
|
||||
f(
|
||||
Some(window),
|
||||
Some(graphics),
|
||||
&mut self.physics,
|
||||
&self.events,
|
||||
&self.state,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
for f in &mut self.callbacks {
|
||||
f(None, None, &mut self.physics, &self.events, &self.state);
|
||||
}
|
||||
}
|
||||
for f in &mut self.callbacks {
|
||||
f(
|
||||
graphics.as_deref_mut(),
|
||||
&mut self.physics,
|
||||
&self.events,
|
||||
&self.state,
|
||||
);
|
||||
}
|
||||
|
||||
for plugin in &mut self.plugins {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#[macro_use]
|
||||
extern crate kiss3d;
|
||||
extern crate nalgebra as na;
|
||||
#[cfg(feature = "dim2")]
|
||||
extern crate ncollide2d as ncollide;
|
||||
@@ -25,15 +23,16 @@ extern crate bitflags;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub use crate::engine::GraphicsManager;
|
||||
pub use crate::graphics::GraphicsManager;
|
||||
pub use crate::harness::plugin::HarnessPlugin;
|
||||
pub use crate::physics::PhysicsState;
|
||||
pub use crate::plugin::TestbedPlugin;
|
||||
pub use crate::testbed::Testbed;
|
||||
pub use crate::testbed::{Testbed, TestbedApp, TestbedGraphics};
|
||||
|
||||
#[cfg(all(feature = "dim2", feature = "other-backends"))]
|
||||
mod box2d_backend;
|
||||
mod engine;
|
||||
mod camera;
|
||||
mod graphics;
|
||||
pub mod harness;
|
||||
#[cfg(feature = "other-backends")]
|
||||
mod nphysics_backend;
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
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,
|
||||
delta: Isometry<f32>,
|
||||
}
|
||||
|
||||
impl Ball {
|
||||
pub fn new(
|
||||
collider: ColliderHandle,
|
||||
delta: Isometry<f32>,
|
||||
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,
|
||||
delta,
|
||||
};
|
||||
|
||||
// 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,
|
||||
&self.delta,
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
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,
|
||||
delta: Isometry<f32>,
|
||||
}
|
||||
|
||||
impl Box {
|
||||
pub fn new(
|
||||
collider: ColliderHandle,
|
||||
delta: Isometry<f32>,
|
||||
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,
|
||||
delta,
|
||||
};
|
||||
|
||||
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,
|
||||
&self.delta,
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
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,
|
||||
delta: Isometry<f32>,
|
||||
}
|
||||
|
||||
impl Capsule {
|
||||
pub fn new(
|
||||
collider: ColliderHandle,
|
||||
delta: Isometry<f32>,
|
||||
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,
|
||||
delta: delta * capsule.transform_wrt_y(),
|
||||
};
|
||||
|
||||
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,
|
||||
&self.delta,
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
use crate::objects::node::{self, GraphicsNode};
|
||||
use kiss3d::window::Window;
|
||||
use na::Point3;
|
||||
use rapier::geometry::{ColliderHandle, ColliderSet};
|
||||
use rapier::math::Isometry;
|
||||
|
||||
pub struct Cone {
|
||||
color: Point3<f32>,
|
||||
base_color: Point3<f32>,
|
||||
gfx: GraphicsNode,
|
||||
collider: ColliderHandle,
|
||||
delta: Isometry<f32>,
|
||||
}
|
||||
|
||||
impl Cone {
|
||||
pub fn new(
|
||||
collider: ColliderHandle,
|
||||
delta: Isometry<f32>,
|
||||
half_height: f32,
|
||||
radius: f32,
|
||||
color: Point3<f32>,
|
||||
window: &mut Window,
|
||||
) -> Cone {
|
||||
#[cfg(feature = "dim2")]
|
||||
let node = window.add_rectangle(radius, half_height);
|
||||
#[cfg(feature = "dim3")]
|
||||
let node = window.add_cone(radius, half_height * 2.0);
|
||||
|
||||
let mut res = Cone {
|
||||
color,
|
||||
base_color: color,
|
||||
gfx: node,
|
||||
collider,
|
||||
delta,
|
||||
};
|
||||
|
||||
// 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,
|
||||
&self.delta,
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
#![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,
|
||||
delta: Isometry<f32>,
|
||||
}
|
||||
|
||||
impl Convex {
|
||||
pub fn new(
|
||||
body: ColliderHandle,
|
||||
delta: Isometry<f32>,
|
||||
vertices: Vec<Point<f32>>,
|
||||
#[cfg(feature = "dim3")] indices: Vec<[u32; 3]>,
|
||||
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 = {
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
let mut mesh_vertices = Vec::new();
|
||||
let mut mesh_indices = Vec::new();
|
||||
for idx in indices {
|
||||
let i = mesh_vertices.len() as u16;
|
||||
mesh_vertices.push(vertices[idx[0] as usize]);
|
||||
mesh_vertices.push(vertices[idx[1] as usize]);
|
||||
mesh_vertices.push(vertices[idx[2] as usize]);
|
||||
mesh_indices.push(Point3::new(i, i + 1, i + 2));
|
||||
}
|
||||
|
||||
let mesh = kiss3d::resource::Mesh::new(mesh_vertices, mesh_indices, None, None, false);
|
||||
window.add_mesh(Rc::new(RefCell::new(mesh)), na::Vector3::from_element(1.0))
|
||||
};
|
||||
|
||||
let mut res = Convex {
|
||||
color,
|
||||
base_color: color,
|
||||
gfx: node,
|
||||
body,
|
||||
delta,
|
||||
};
|
||||
|
||||
// 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,
|
||||
&self.delta,
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
use crate::objects::node::{self, GraphicsNode};
|
||||
use kiss3d::window::Window;
|
||||
use na::Point3;
|
||||
use rapier::geometry::{ColliderHandle, ColliderSet};
|
||||
use rapier::math::Isometry;
|
||||
|
||||
pub struct Cylinder {
|
||||
color: Point3<f32>,
|
||||
base_color: Point3<f32>,
|
||||
gfx: GraphicsNode,
|
||||
collider: ColliderHandle,
|
||||
delta: Isometry<f32>,
|
||||
}
|
||||
|
||||
impl Cylinder {
|
||||
pub fn new(
|
||||
collider: ColliderHandle,
|
||||
delta: Isometry<f32>,
|
||||
half_height: f32,
|
||||
radius: f32,
|
||||
color: Point3<f32>,
|
||||
window: &mut Window,
|
||||
) -> Cylinder {
|
||||
#[cfg(feature = "dim2")]
|
||||
let node = window.add_rectangle(radius, half_height);
|
||||
#[cfg(feature = "dim3")]
|
||||
let node = window.add_cylinder(radius, half_height * 2.0);
|
||||
|
||||
let mut res = Cylinder {
|
||||
color,
|
||||
base_color: color,
|
||||
gfx: node,
|
||||
collider,
|
||||
delta,
|
||||
};
|
||||
|
||||
// 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,
|
||||
&self.delta,
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
use kiss3d::window::Window;
|
||||
use na::{self, Point3};
|
||||
use parry::shape;
|
||||
use rapier::geometry::{ColliderHandle, ColliderSet};
|
||||
#[cfg(feature = "dim2")]
|
||||
use rapier::math::Point;
|
||||
#[cfg(feature = "dim3")]
|
||||
use {
|
||||
crate::objects::node::{self, GraphicsNode},
|
||||
kiss3d::resource::Mesh,
|
||||
rapier::math::Vector,
|
||||
std::cell::RefCell,
|
||||
};
|
||||
|
||||
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,
|
||||
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,
|
||||
color: Point3<f32>,
|
||||
window: &mut Window,
|
||||
) -> HeightField {
|
||||
use std::rc::Rc;
|
||||
|
||||
let (vertices, indices) = heightfield.to_trimesh();
|
||||
let indices = indices
|
||||
.into_iter()
|
||||
.map(|idx| Point3::new(idx[0] as u16, idx[1] as u16, idx[2] as u16))
|
||||
.collect();
|
||||
let mesh = Mesh::new(vertices, indices, None, None, false);
|
||||
|
||||
let mut res = HeightField {
|
||||
color,
|
||||
base_color: color,
|
||||
gfx: window.add_mesh(Rc::new(RefCell::new(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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
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<[u32; 3]>,
|
||||
color: Point3<f32>,
|
||||
window: &mut window::Window,
|
||||
) -> Mesh {
|
||||
let vs = vertices;
|
||||
let is = indices
|
||||
.into_iter()
|
||||
.map(|idx| Point3::new(idx[0] as u16, idx[1] as u16, idx[2] as u16))
|
||||
.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
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1 @@
|
||||
pub mod ball;
|
||||
pub mod box_node;
|
||||
pub mod capsule;
|
||||
pub mod cone;
|
||||
pub mod convex;
|
||||
pub mod cylinder;
|
||||
pub mod heightfield;
|
||||
pub mod mesh;
|
||||
pub mod node;
|
||||
pub mod polyline;
|
||||
//pub mod plane;
|
||||
//#[cfg(feature = "dim2")]
|
||||
//pub mod polyline;
|
||||
|
||||
@@ -1,177 +1,432 @@
|
||||
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 bevy::prelude::*;
|
||||
use bevy::render::mesh::{Indices, VertexAttributeValues};
|
||||
|
||||
//use crate::objects::plane::Plane;
|
||||
use crate::objects::polyline::Polyline;
|
||||
use kiss3d::window::Window;
|
||||
use na::Point3;
|
||||
use na::{Point3, Vector3};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::objects::cone::Cone;
|
||||
use crate::objects::cylinder::Cylinder;
|
||||
use rapier::geometry::{ColliderHandle, ColliderSet};
|
||||
use rapier::math::Isometry;
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
pub type GraphicsNode = kiss3d::scene::PlanarSceneNode;
|
||||
use bevy::render::pipeline::PrimitiveTopology;
|
||||
use bevy::render::wireframe::Wireframe;
|
||||
use rapier::geometry::{ColliderHandle, ColliderSet, Shape, ShapeType};
|
||||
#[cfg(feature = "dim3")]
|
||||
pub type GraphicsNode = kiss3d::scene::SceneNode;
|
||||
use rapier::geometry::{Cone, Cylinder};
|
||||
use rapier::math::Isometry;
|
||||
#[cfg(feature = "dim2")]
|
||||
use {
|
||||
na::{Point2, Vector2},
|
||||
rapier::geometry::{Ball, Cuboid},
|
||||
};
|
||||
|
||||
pub enum Node {
|
||||
// Plane(Plane),
|
||||
Ball(Ball),
|
||||
Box(Box),
|
||||
HeightField(HeightField),
|
||||
Capsule(Capsule),
|
||||
Polyline(Polyline),
|
||||
Mesh(Mesh),
|
||||
Convex(Convex),
|
||||
Cylinder(Cylinder),
|
||||
Cone(Cone),
|
||||
pub struct EntityWithGraphics {
|
||||
pub entity: Entity,
|
||||
pub color: Point3<f32>,
|
||||
pub base_color: Point3<f32>,
|
||||
pub collider: ColliderHandle,
|
||||
pub delta: Isometry<f32>,
|
||||
material: Handle<StandardMaterial>,
|
||||
}
|
||||
|
||||
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(),
|
||||
Node::Polyline(ref mut n) => n.select(),
|
||||
Node::Mesh(ref mut n) => n.select(),
|
||||
Node::Convex(ref mut n) => n.select(),
|
||||
Node::Cylinder(ref mut n) => n.select(),
|
||||
Node::Cone(ref mut n) => n.select(),
|
||||
impl EntityWithGraphics {
|
||||
pub fn spawn(
|
||||
commands: &mut Commands,
|
||||
meshes: &mut Assets<Mesh>,
|
||||
materials: &mut Assets<StandardMaterial>,
|
||||
prefab_meshs: &HashMap<ShapeType, Handle<Mesh>>,
|
||||
shape: &dyn Shape,
|
||||
collider: ColliderHandle,
|
||||
collider_pos: Isometry<f32>,
|
||||
delta: Isometry<f32>,
|
||||
color: Point3<f32>,
|
||||
sensor: bool,
|
||||
) -> Self {
|
||||
let entity = commands.spawn().id();
|
||||
|
||||
let scale = collider_mesh_scale(shape);
|
||||
let mesh = prefab_meshs
|
||||
.get(&shape.shape_type())
|
||||
.cloned()
|
||||
.or_else(|| generate_collider_mesh(shape).map(|m| meshes.add(m)))
|
||||
.expect("Could not build the collider's render mesh");
|
||||
|
||||
let bevy_color = Color::rgb(color.x, color.y, color.z);
|
||||
let shape_pos = collider_pos * delta;
|
||||
let mut transform = Transform::from_scale(scale);
|
||||
transform.translation.x = shape_pos.translation.vector.x;
|
||||
transform.translation.y = shape_pos.translation.vector.y;
|
||||
#[cfg(feature = "dim3")]
|
||||
{
|
||||
transform.translation.z = shape_pos.translation.vector.z;
|
||||
transform.rotation = Quat::from_xyzw(
|
||||
shape_pos.rotation.i,
|
||||
shape_pos.rotation.j,
|
||||
shape_pos.rotation.k,
|
||||
shape_pos.rotation.w,
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "dim2")]
|
||||
{
|
||||
if sensor {
|
||||
transform.translation.z = -10.0;
|
||||
}
|
||||
transform.rotation = Quat::from_rotation_z(shape_pos.rotation.angle());
|
||||
}
|
||||
|
||||
let material = StandardMaterial {
|
||||
metallic: 0.5,
|
||||
roughness: 0.5,
|
||||
double_sided: true, // TODO: this doesn't do anything?
|
||||
..StandardMaterial::from(bevy_color)
|
||||
};
|
||||
let material_handle = materials.add(material);
|
||||
let material_weak_handle = material_handle.clone_weak();
|
||||
let pbr = PbrBundle {
|
||||
mesh,
|
||||
material: material_handle,
|
||||
transform,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut entity_commands = commands.entity(entity);
|
||||
entity_commands.insert_bundle(pbr);
|
||||
|
||||
if sensor {
|
||||
entity_commands.insert(Wireframe);
|
||||
}
|
||||
|
||||
EntityWithGraphics {
|
||||
entity,
|
||||
color,
|
||||
base_color: color,
|
||||
collider,
|
||||
delta,
|
||||
material: material_weak_handle,
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
Node::Polyline(ref mut n) => n.unselect(),
|
||||
Node::Mesh(ref mut n) => n.unselect(),
|
||||
Node::Convex(ref mut n) => n.unselect(),
|
||||
Node::Cylinder(ref mut n) => n.unselect(),
|
||||
Node::Cone(ref mut n) => n.unselect(),
|
||||
pub fn select(&mut self, materials: &mut Assets<StandardMaterial>) {
|
||||
// NOTE: we don't just call `self.set_color` because that would
|
||||
// overwrite self.base_color too.
|
||||
self.color = Point3::new(1.0, 0.0, 0.0);
|
||||
if let Some(material) = materials.get_mut(&self.material) {
|
||||
material.base_color = Color::rgb(self.color.x, self.color.y, self.color.z);
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
Node::Polyline(ref mut n) => n.update(colliders),
|
||||
Node::Mesh(ref mut n) => n.update(colliders),
|
||||
Node::Convex(ref mut n) => n.update(colliders),
|
||||
Node::Cylinder(ref mut n) => n.update(colliders),
|
||||
Node::Cone(ref mut n) => n.update(colliders),
|
||||
pub fn unselect(&mut self, materials: &mut Assets<StandardMaterial>) {
|
||||
self.set_color(materials, self.base_color);
|
||||
}
|
||||
|
||||
pub fn set_color(&mut self, materials: &mut Assets<StandardMaterial>, color: Point3<f32>) {
|
||||
if let Some(material) = materials.get_mut(&self.material) {
|
||||
material.base_color = Color::rgb(color.x, color.y, color.z);
|
||||
}
|
||||
self.color = color;
|
||||
self.base_color = color;
|
||||
}
|
||||
|
||||
pub fn update(&mut self, colliders: &ColliderSet, components: &mut Query<(&mut Transform,)>) {
|
||||
if let Some(co) = colliders.get(self.collider) {
|
||||
if let Ok(mut pos) = components.get_component_mut::<Transform>(self.entity) {
|
||||
let co_pos = co.position() * self.delta;
|
||||
pos.translation.x = co_pos.translation.vector.x;
|
||||
pos.translation.y = co_pos.translation.vector.y;
|
||||
#[cfg(feature = "dim3")]
|
||||
{
|
||||
pos.translation.z = co_pos.translation.vector.z;
|
||||
pos.rotation = Quat::from_xyzw(
|
||||
co_pos.rotation.i,
|
||||
co_pos.rotation.j,
|
||||
co_pos.rotation.k,
|
||||
co_pos.rotation.w,
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "dim2")]
|
||||
{
|
||||
pos.rotation = Quat::from_rotation_z(co_pos.rotation.angle());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn object(&self) -> ColliderHandle {
|
||||
self.collider
|
||||
}
|
||||
|
||||
#[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),
|
||||
_ => {}
|
||||
}
|
||||
pub fn gen_prefab_meshes(
|
||||
out: &mut HashMap<ShapeType, Handle<Mesh>>,
|
||||
meshes: &mut Assets<Mesh>,
|
||||
) {
|
||||
//
|
||||
// Cuboid mesh
|
||||
//
|
||||
let cuboid = bevy_mesh_from_polyline(Cuboid::new(Vector2::new(1.0, 1.0)).to_polyline());
|
||||
out.insert(ShapeType::Cuboid, meshes.add(cuboid));
|
||||
|
||||
//
|
||||
// Ball mesh
|
||||
//
|
||||
let ball = bevy_mesh_from_polyline(Ball::new(1.0).to_polyline(30));
|
||||
out.insert(ShapeType::Ball, meshes.add(ball));
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
pub fn draw(&mut self, _: &mut Window) {}
|
||||
pub fn gen_prefab_meshes(
|
||||
out: &mut HashMap<ShapeType, Handle<Mesh>>,
|
||||
meshes: &mut Assets<Mesh>,
|
||||
) {
|
||||
//
|
||||
// Cuboid mesh
|
||||
//
|
||||
let cuboid = Mesh::from(shape::Cube { size: 2.0 });
|
||||
out.insert(ShapeType::Cuboid, meshes.add(cuboid));
|
||||
|
||||
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()),
|
||||
Node::Cylinder(ref n) => Some(n.scene_node()),
|
||||
Node::Cone(ref n) => Some(n.scene_node()),
|
||||
Node::Polyline(_) => None,
|
||||
#[cfg(feature = "dim2")]
|
||||
Node::HeightField(_) => None,
|
||||
}
|
||||
}
|
||||
//
|
||||
// Ball mesh
|
||||
//
|
||||
let ball = Mesh::from(shape::Icosphere {
|
||||
subdivisions: 2,
|
||||
radius: 1.0,
|
||||
});
|
||||
out.insert(ShapeType::Ball, meshes.add(ball));
|
||||
|
||||
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()),
|
||||
Node::Cylinder(ref mut n) => Some(n.scene_node_mut()),
|
||||
Node::Cone(ref mut n) => Some(n.scene_node_mut()),
|
||||
Node::Polyline(_) => None,
|
||||
#[cfg(feature = "dim2")]
|
||||
Node::HeightField(_) => None,
|
||||
}
|
||||
}
|
||||
//
|
||||
// Cylinder mesh
|
||||
//
|
||||
let cylinder = Cylinder::new(1.0, 1.0);
|
||||
let mesh = bevy_mesh(cylinder.to_trimesh(20));
|
||||
out.insert(ShapeType::Cylinder, meshes.add(mesh.clone()));
|
||||
out.insert(ShapeType::RoundCylinder, meshes.add(mesh));
|
||||
|
||||
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(),
|
||||
Node::Polyline(ref n) => n.object(),
|
||||
Node::Mesh(ref n) => n.object(),
|
||||
Node::Convex(ref n) => n.object(),
|
||||
Node::Cylinder(ref n) => n.object(),
|
||||
Node::Cone(ref n) => n.object(),
|
||||
}
|
||||
}
|
||||
//
|
||||
// Cone mesh
|
||||
//
|
||||
let cone = Cone::new(1.0, 1.0);
|
||||
let mesh = bevy_mesh(cone.to_trimesh(10));
|
||||
out.insert(ShapeType::Cone, meshes.add(mesh.clone()));
|
||||
out.insert(ShapeType::RoundCone, meshes.add(mesh));
|
||||
|
||||
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),
|
||||
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),
|
||||
Node::Cylinder(ref mut n) => n.set_color(color),
|
||||
Node::Cone(ref mut n) => n.set_color(color),
|
||||
}
|
||||
//
|
||||
// Halfspace
|
||||
//
|
||||
let vertices = vec![
|
||||
Point3::new(-1000.0, 0.0, -1000.0),
|
||||
Point3::new(1000.0, 0.0, -1000.0),
|
||||
Point3::new(1000.0, 0.0, 1000.0),
|
||||
Point3::new(-1000.0, 0.0, 1000.0),
|
||||
];
|
||||
let indices = vec![[0, 1, 2], [0, 2, 3]];
|
||||
let mesh = bevy_mesh((vertices, indices));
|
||||
out.insert(ShapeType::HalfSpace, meshes.add(mesh));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
#[cfg(feature = "dim2")]
|
||||
fn bevy_mesh_from_polyline(vertices: Vec<Point2<f32>>) -> Mesh {
|
||||
let n = vertices.len();
|
||||
let idx = (1..n as u32 - 1).map(|i| [0, i, i + 1]).collect();
|
||||
let vtx = vertices
|
||||
.into_iter()
|
||||
.map(|v| Point3::new(v.x, v.y, 0.0))
|
||||
.collect();
|
||||
bevy_mesh((vtx, idx))
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
fn bevy_polyline(buffers: (Vec<Point2<f32>>, Option<Vec<[u32; 2]>>)) -> Mesh {
|
||||
let (vtx, idx) = buffers;
|
||||
// let mut normals: Vec<[f32; 3]> = vec![];
|
||||
let mut vertices: Vec<[f32; 3]> = vec![];
|
||||
|
||||
if let Some(idx) = idx {
|
||||
for idx in idx {
|
||||
let a = vtx[idx[0] as usize];
|
||||
let b = vtx[idx[1] as usize];
|
||||
|
||||
vertices.push([a.x, a.y, 0.0]);
|
||||
vertices.push([b.x, b.y, 0.0]);
|
||||
}
|
||||
} else {
|
||||
node.set_visible(false);
|
||||
node.unlink();
|
||||
vertices = vtx.iter().map(|v| [v.x, v.y, 0.0]).collect();
|
||||
}
|
||||
|
||||
let indices: Vec<_> = (0..vertices.len() as u32).collect();
|
||||
let uvs: Vec<_> = (0..vertices.len()).map(|_| [0.0, 0.0]).collect();
|
||||
let normals: Vec<_> = (0..vertices.len()).map(|_| [0.0, 0.0, 1.0]).collect();
|
||||
|
||||
// Generate the mesh
|
||||
let mut mesh = Mesh::new(PrimitiveTopology::LineStrip);
|
||||
mesh.set_attribute(
|
||||
Mesh::ATTRIBUTE_POSITION,
|
||||
VertexAttributeValues::from(vertices),
|
||||
);
|
||||
mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, VertexAttributeValues::from(normals));
|
||||
mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, VertexAttributeValues::from(uvs));
|
||||
mesh.set_indices(Some(Indices::U32(indices)));
|
||||
mesh
|
||||
}
|
||||
|
||||
fn bevy_mesh(buffers: (Vec<Point3<f32>>, Vec<[u32; 3]>)) -> Mesh {
|
||||
let (vtx, idx) = buffers;
|
||||
let mut normals: Vec<[f32; 3]> = vec![];
|
||||
let mut vertices: Vec<[f32; 3]> = vec![];
|
||||
|
||||
for idx in idx {
|
||||
let a = vtx[idx[0] as usize];
|
||||
let b = vtx[idx[1] as usize];
|
||||
let c = vtx[idx[2] as usize];
|
||||
|
||||
vertices.push(a.into());
|
||||
vertices.push(b.into());
|
||||
vertices.push(c.into());
|
||||
}
|
||||
|
||||
for vtx in vertices.chunks(3) {
|
||||
let a = Point3::from(vtx[0]);
|
||||
let b = Point3::from(vtx[1]);
|
||||
let c = Point3::from(vtx[2]);
|
||||
let n = (b - a).cross(&(c - a)).normalize();
|
||||
normals.push(n.into());
|
||||
normals.push(n.into());
|
||||
normals.push(n.into());
|
||||
}
|
||||
|
||||
normals
|
||||
.iter_mut()
|
||||
.for_each(|n| *n = Vector3::from(*n).normalize().into());
|
||||
let indices: Vec<_> = (0..vertices.len() as u32).collect();
|
||||
let uvs: Vec<_> = (0..vertices.len()).map(|_| [0.0, 0.0]).collect();
|
||||
|
||||
// Generate the mesh
|
||||
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
|
||||
mesh.set_attribute(
|
||||
Mesh::ATTRIBUTE_POSITION,
|
||||
VertexAttributeValues::from(vertices),
|
||||
);
|
||||
mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, VertexAttributeValues::from(normals));
|
||||
mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, VertexAttributeValues::from(uvs));
|
||||
mesh.set_indices(Some(Indices::U32(indices)));
|
||||
mesh
|
||||
}
|
||||
|
||||
fn collider_mesh_scale(co_shape: &dyn Shape) -> Vec3 {
|
||||
match co_shape.shape_type() {
|
||||
#[cfg(feature = "dim2")]
|
||||
ShapeType::Cuboid => {
|
||||
let c = co_shape.as_cuboid().unwrap();
|
||||
Vec3::new(c.half_extents.x, c.half_extents.y, 1.0)
|
||||
}
|
||||
ShapeType::Ball => {
|
||||
let b = co_shape.as_ball().unwrap();
|
||||
Vec3::new(b.radius, b.radius, b.radius)
|
||||
}
|
||||
#[cfg(feature = "dim3")]
|
||||
ShapeType::Cuboid => {
|
||||
let c = co_shape.as_cuboid().unwrap();
|
||||
Vec3::from_slice_unaligned(c.half_extents.as_slice())
|
||||
}
|
||||
#[cfg(feature = "dim3")]
|
||||
ShapeType::Cylinder => {
|
||||
let c = co_shape.as_cylinder().unwrap();
|
||||
Vec3::new(c.radius, c.half_height, c.radius)
|
||||
}
|
||||
#[cfg(feature = "dim3")]
|
||||
ShapeType::RoundCylinder => {
|
||||
let c = &co_shape.as_round_cylinder().unwrap().base_shape;
|
||||
Vec3::new(c.radius, c.half_height, c.radius)
|
||||
}
|
||||
#[cfg(feature = "dim3")]
|
||||
ShapeType::Cone => {
|
||||
let c = co_shape.as_cone().unwrap();
|
||||
Vec3::new(c.radius, c.half_height, c.radius)
|
||||
}
|
||||
#[cfg(feature = "dim3")]
|
||||
ShapeType::RoundCone => {
|
||||
let c = &co_shape.as_round_cone().unwrap().base_shape;
|
||||
Vec3::new(c.radius, c.half_height, c.radius)
|
||||
}
|
||||
_ => Vec3::ONE,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
fn generate_collider_mesh(co_shape: &dyn Shape) -> Option<Mesh> {
|
||||
let mesh = match co_shape.shape_type() {
|
||||
ShapeType::Capsule => {
|
||||
let capsule = co_shape.as_capsule().unwrap();
|
||||
bevy_mesh_from_polyline(capsule.to_polyline(10))
|
||||
}
|
||||
ShapeType::Triangle => {
|
||||
let tri = co_shape.as_triangle().unwrap();
|
||||
bevy_mesh_from_polyline(vec![tri.a, tri.b, tri.c])
|
||||
}
|
||||
ShapeType::TriMesh => {
|
||||
let trimesh = co_shape.as_trimesh().unwrap();
|
||||
let vertices = trimesh
|
||||
.vertices()
|
||||
.iter()
|
||||
.map(|p| Point3::new(p.x, p.y, 0.0))
|
||||
.collect();
|
||||
bevy_mesh((vertices, trimesh.indices().to_vec()))
|
||||
}
|
||||
ShapeType::Polyline => {
|
||||
let polyline = co_shape.as_polyline().unwrap();
|
||||
bevy_polyline((
|
||||
polyline.vertices().to_vec(),
|
||||
Some(polyline.indices().to_vec()),
|
||||
))
|
||||
}
|
||||
ShapeType::HeightField => {
|
||||
let heightfield = co_shape.as_heightfield().unwrap();
|
||||
let vertices: Vec<_> = heightfield
|
||||
.segments()
|
||||
.flat_map(|s| vec![s.a, s.b])
|
||||
.collect();
|
||||
bevy_polyline((vertices, None))
|
||||
}
|
||||
ShapeType::ConvexPolygon => {
|
||||
let poly = co_shape.as_convex_polygon().unwrap();
|
||||
bevy_mesh_from_polyline(poly.points().to_vec())
|
||||
}
|
||||
ShapeType::RoundConvexPolygon => {
|
||||
let poly = co_shape.as_round_convex_polygon().unwrap();
|
||||
bevy_mesh_from_polyline(poly.base_shape.points().to_vec())
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(mesh)
|
||||
}
|
||||
|
||||
#[cfg(feature = "dim3")]
|
||||
fn generate_collider_mesh(co_shape: &dyn Shape) -> Option<Mesh> {
|
||||
let mesh = match co_shape.shape_type() {
|
||||
ShapeType::Capsule => {
|
||||
let capsule = co_shape.as_capsule().unwrap();
|
||||
bevy_mesh(capsule.to_trimesh(20, 10))
|
||||
}
|
||||
ShapeType::Triangle => {
|
||||
let tri = co_shape.as_triangle().unwrap();
|
||||
bevy_mesh((vec![tri.a, tri.b, tri.c], vec![[0, 1, 2], [0, 2, 1]]))
|
||||
}
|
||||
ShapeType::TriMesh => {
|
||||
let trimesh = co_shape.as_trimesh().unwrap();
|
||||
bevy_mesh((trimesh.vertices().to_vec(), trimesh.indices().to_vec()))
|
||||
}
|
||||
ShapeType::HeightField => {
|
||||
let heightfield = co_shape.as_heightfield().unwrap();
|
||||
bevy_mesh(heightfield.to_trimesh())
|
||||
}
|
||||
ShapeType::ConvexPolyhedron => {
|
||||
let poly = co_shape.as_convex_polyhedron().unwrap();
|
||||
bevy_mesh(poly.to_trimesh())
|
||||
}
|
||||
ShapeType::RoundConvexPolyhedron => {
|
||||
let poly = co_shape.as_round_convex_polyhedron().unwrap();
|
||||
bevy_mesh(poly.base_shape.to_trimesh())
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(mesh)
|
||||
}
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
#[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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
use kiss3d::window::Window;
|
||||
use na::Point3;
|
||||
use rapier::geometry::{ColliderHandle, ColliderSet};
|
||||
use rapier::math::{Isometry, Point};
|
||||
|
||||
pub struct Polyline {
|
||||
color: Point3<f32>,
|
||||
base_color: Point3<f32>,
|
||||
vertices: Vec<Point<f32>>,
|
||||
indices: Vec<[u32; 2]>,
|
||||
collider: ColliderHandle,
|
||||
pos: Isometry<f32>,
|
||||
}
|
||||
|
||||
impl Polyline {
|
||||
pub fn new(
|
||||
collider: ColliderHandle,
|
||||
vertices: Vec<Point<f32>>,
|
||||
indices: Vec<[u32; 2]>,
|
||||
color: Point3<f32>,
|
||||
) -> Polyline {
|
||||
Polyline {
|
||||
color,
|
||||
pos: Isometry::identity(),
|
||||
base_color: color,
|
||||
vertices,
|
||||
indices,
|
||||
collider,
|
||||
}
|
||||
}
|
||||
|
||||
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: &ColliderSet) {
|
||||
self.pos = colliders
|
||||
.get(self.collider)
|
||||
.map(|c| *c.position())
|
||||
.unwrap_or(Isometry::identity());
|
||||
}
|
||||
|
||||
pub fn object(&self) -> ColliderHandle {
|
||||
self.collider
|
||||
}
|
||||
|
||||
pub fn draw(&mut self, window: &mut Window) {
|
||||
for idx in &self.indices {
|
||||
let p1 = self.pos * self.vertices[idx[0] as usize];
|
||||
let p2 = self.pos * self.vertices[idx[1] as usize];
|
||||
|
||||
#[cfg(feature = "dim2")]
|
||||
window.draw_planar_line(&p1, &p2, &self.color);
|
||||
#[cfg(feature = "dim3")]
|
||||
window.draw_line(&p1, &p2, &self.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,11 @@
|
||||
use crate::harness::RunState;
|
||||
use crate::physics::PhysicsState;
|
||||
use kiss3d::window::Window;
|
||||
use na::Point3;
|
||||
|
||||
pub trait TestbedPlugin {
|
||||
fn init_graphics(&mut self, window: &mut Window, gen_color: &mut dyn FnMut() -> Point3<f32>);
|
||||
fn clear_graphics(&mut self, window: &mut Window);
|
||||
fn run_callbacks(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
physics: &mut PhysicsState,
|
||||
run_state: &RunState,
|
||||
);
|
||||
fn init_graphics(&mut self, gen_color: &mut dyn FnMut() -> Point3<f32>);
|
||||
fn clear_graphics(&mut self);
|
||||
fn run_callbacks(&mut self, physics: &mut PhysicsState, run_state: &RunState);
|
||||
fn step(&mut self, physics: &mut PhysicsState);
|
||||
fn draw(&mut self);
|
||||
fn profiling_string(&self) -> String;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,225 +1,65 @@
|
||||
use kiss3d::conrod::{self, Borderable, Colorable, Labelable, Positionable, Sizeable, Widget};
|
||||
use kiss3d::window::Window;
|
||||
use rapier::dynamics::IntegrationParameters;
|
||||
use rapier::counters::Counters;
|
||||
|
||||
use crate::harness::RunState;
|
||||
use crate::harness::Harness;
|
||||
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;
|
||||
use crate::PhysicsState;
|
||||
use bevy_egui::egui::Slider;
|
||||
use bevy_egui::{egui, EguiContext};
|
||||
|
||||
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,
|
||||
_run_state: &mut RunState,
|
||||
) {
|
||||
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.
|
||||
pub fn update_ui(ui_context: &EguiContext, state: &mut TestbedState, harness: &mut Harness) {
|
||||
egui::Window::new("Parameters").show(ui_context.ctx(), |ui| {
|
||||
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(all(feature = "dim3", feature = "other-backends"))]
|
||||
fn is_physx(id: usize) -> bool {
|
||||
id == crate::testbed::PHYSX_BACKEND_PATCH_FRICTION
|
||||
|| id == crate::testbed::PHYSX_BACKEND_TWO_FRICTION_DIR
|
||||
#[cfg(feature = "dim3")]
|
||||
let prev_selected_backend = state.selected_backend;
|
||||
let mut changed = false;
|
||||
egui::ComboBox::from_label("backend")
|
||||
.width(150.0)
|
||||
.selected_text(state.backend_names[state.selected_backend])
|
||||
.show_ui(ui, |ui| {
|
||||
for (id, name) in state.backend_names.iter().enumerate() {
|
||||
changed = ui
|
||||
.selectable_value(&mut state.selected_backend, id, *name)
|
||||
.changed()
|
||||
|| changed;
|
||||
}
|
||||
});
|
||||
|
||||
#[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,
|
||||
);
|
||||
}
|
||||
if changed {
|
||||
state
|
||||
.action_flags
|
||||
.set(TestbedActionFlags::BACKEND_CHANGED, true);
|
||||
|
||||
state.selected_backend = selected;
|
||||
state
|
||||
.action_flags
|
||||
.set(TestbedActionFlags::BACKEND_CHANGED, true)
|
||||
#[cfg(all(feature = "dim3", feature = "other-backends"))]
|
||||
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(prev_selected_backend))
|
||||
|| (!is_physx(state.selected_backend) && is_physx(prev_selected_backend))
|
||||
{
|
||||
// PhysX defaults (4 position iterations, 1 velocity) are the
|
||||
// opposite of rapier's (4 velocity iterations, 1 position).
|
||||
std::mem::swap(
|
||||
&mut harness
|
||||
.physics
|
||||
.integration_parameters
|
||||
.max_position_iterations,
|
||||
&mut harness
|
||||
.physics
|
||||
.integration_parameters
|
||||
.max_velocity_iterations,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
ui.separator();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("<").clicked() {
|
||||
if state.selected_example > 0 {
|
||||
state.selected_example -= 1;
|
||||
state
|
||||
@@ -228,15 +68,7 @@ impl TestbedUi {
|
||||
}
|
||||
}
|
||||
|
||||
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 ui.button(">").clicked() {
|
||||
if state.selected_example + 1 < state.example_names.len() {
|
||||
state.selected_example += 1;
|
||||
state
|
||||
@@ -244,199 +76,94 @@ impl TestbedUi {
|
||||
.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 = _run_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 _run_state.num_threads != val as usize {
|
||||
_run_state.num_threads = val as usize;
|
||||
_run_state.thread_pool = rapier::rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(_run_state.num_threads)
|
||||
.build()
|
||||
.unwrap();
|
||||
}
|
||||
let mut changed = false;
|
||||
egui::ComboBox::from_label("example")
|
||||
.width(150.0)
|
||||
.selected_text(state.example_names[state.selected_example])
|
||||
.show_ui(ui, |ui| {
|
||||
for (id, name) in state.example_names.iter().enumerate() {
|
||||
changed = ui
|
||||
.selectable_value(&mut state.selected_example, id, *name)
|
||||
.changed()
|
||||
|| changed;
|
||||
}
|
||||
});
|
||||
if changed {
|
||||
state
|
||||
.action_flags
|
||||
.set(TestbedActionFlags::EXAMPLE_CHANGED, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
ui.separator();
|
||||
|
||||
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;
|
||||
}
|
||||
ui.collapsing("Profile infos", |ui| {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.label(profiling_string(&harness.physics.pipeline.counters))
|
||||
});
|
||||
});
|
||||
ui.collapsing("Serialization infos", |ui| {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.label(serialization_string(
|
||||
harness.state.timestep_id,
|
||||
&harness.physics,
|
||||
))
|
||||
});
|
||||
});
|
||||
|
||||
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 integration_parameters = &mut harness.physics.integration_parameters;
|
||||
ui.add(
|
||||
Slider::new(&mut integration_parameters.max_velocity_iterations, 0..=200)
|
||||
.text("vels. iters."),
|
||||
);
|
||||
ui.add(
|
||||
Slider::new(&mut integration_parameters.max_position_iterations, 0..=200)
|
||||
.text("pos. iters."),
|
||||
);
|
||||
#[cfg(feature = "parallel")]
|
||||
{
|
||||
ui.add(
|
||||
Slider::new(&mut harness.state.num_threads, 1..num_cpus::get_physical())
|
||||
.text("num. threads"),
|
||||
);
|
||||
}
|
||||
ui.add(
|
||||
Slider::new(&mut integration_parameters.max_ccd_substeps, 0..=10).text("CCD substeps"),
|
||||
);
|
||||
ui.add(
|
||||
Slider::new(&mut integration_parameters.min_island_size, 1..=10_000)
|
||||
.text("min island size"),
|
||||
);
|
||||
ui.add(
|
||||
Slider::new(&mut integration_parameters.warmstart_coeff, 0.0..=1.0)
|
||||
.text("warmstart coeff"),
|
||||
);
|
||||
let mut frequency = integration_parameters.inv_dt().round() as u32;
|
||||
ui.add(Slider::new(&mut frequency, 0..=240).text("frequency (Hz)"));
|
||||
integration_parameters.set_inv_dt(frequency as f32);
|
||||
|
||||
let mut sleep = state.flags.contains(TestbedStateFlags::SLEEP);
|
||||
// let mut contact_points = state.flags.contains(TestbedStateFlags::CONTACT_POINTS);
|
||||
// let mut wireframe = state.flags.contains(TestbedStateFlags::WIREFRAME);
|
||||
ui.checkbox(&mut sleep, "sleep enabled");
|
||||
// ui.checkbox(&mut contact_points, "draw contacts");
|
||||
// ui.checkbox(&mut wireframe, "draw wireframes");
|
||||
|
||||
state.flags.set(TestbedStateFlags::SLEEP, sleep);
|
||||
// state
|
||||
// .flags
|
||||
// .set(TestbedStateFlags::CONTACT_POINTS, contact_points);
|
||||
// state.flags.set(TestbedStateFlags::WIREFRAME, wireframe);
|
||||
ui.separator();
|
||||
|
||||
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 ui.button(label).clicked() {
|
||||
if state.running == RunMode::Stop {
|
||||
state.running = RunMode::Running
|
||||
} else {
|
||||
@@ -444,127 +171,109 @@ impl TestbedUi {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
if ui.button("Single Step (S)").clicked() {
|
||||
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)
|
||||
{
|
||||
if ui.button("Take snapshot").clicked() {
|
||||
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)
|
||||
{
|
||||
if ui.button("Restore snapshot").clicked() {
|
||||
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
|
||||
if ui.button("Restart (R)").clicked() {
|
||||
state.action_flags.set(TestbedActionFlags::RESTART, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 profiling_string(counters: &Counters) -> String {
|
||||
format!(
|
||||
r#"Total: {:.2}ms
|
||||
Collision detection: {:.2}ms
|
||||
|_ Broad-phase: {:.2}ms
|
||||
Narrow-phase: {:.2}ms
|
||||
Island computation: {:.2}ms
|
||||
Solver: {:.2}ms
|
||||
|_ Velocity assembly: {:.2}ms
|
||||
Velocity resolution: {:.2}ms
|
||||
Velocity integration: {:.2}ms
|
||||
Position assembly: {:.2}ms
|
||||
Position resolution: {:.2}ms
|
||||
CCD: {:.2}ms
|
||||
|_ # of substeps: {}
|
||||
TOI computation: {:.2}ms
|
||||
Broad-phase: {:.2}ms
|
||||
Narrow-phase: {:.2}ms
|
||||
Solver: {:.2}ms"#,
|
||||
counters.step_time(),
|
||||
counters.collision_detection_time(),
|
||||
counters.broad_phase_time(),
|
||||
counters.narrow_phase_time(),
|
||||
counters.island_construction_time(),
|
||||
counters.solver_time(),
|
||||
counters.solver.velocity_assembly_time.time(),
|
||||
counters.velocity_resolution_time(),
|
||||
counters.solver.velocity_update_time.time(),
|
||||
counters.solver.position_assembly_time.time(),
|
||||
counters.position_resolution_time(),
|
||||
counters.ccd_time(),
|
||||
counters.ccd.num_substeps,
|
||||
counters.ccd.toi_computation_time.time(),
|
||||
counters.ccd.broad_phase_time.time(),
|
||||
counters.ccd.narrow_phase_time.time(),
|
||||
counters.ccd.solver_time.time(),
|
||||
)
|
||||
}
|
||||
|
||||
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);
|
||||
fn serialization_string(timestep_id: usize, physics: &PhysicsState) -> String {
|
||||
let t = instant::now();
|
||||
// let t = instant::now();
|
||||
let bf = bincode::serialize(&physics.broad_phase).unwrap();
|
||||
// println!("bf: {}", instant::now() - t);
|
||||
// let t = instant::now();
|
||||
let nf = bincode::serialize(&physics.narrow_phase).unwrap();
|
||||
// println!("nf: {}", instant::now() - t);
|
||||
// let t = instant::now();
|
||||
let bs = bincode::serialize(&physics.bodies).unwrap();
|
||||
// println!("bs: {}", instant::now() - t);
|
||||
// let t = instant::now();
|
||||
let cs = bincode::serialize(&physics.colliders).unwrap();
|
||||
// println!("cs: {}", instant::now() - t);
|
||||
// let t = instant::now();
|
||||
let js = bincode::serialize(&physics.joints).unwrap();
|
||||
// println!("js: {}", instant::now() - t);
|
||||
let serialization_time = instant::now() - t;
|
||||
let hash_bf = md5::compute(&bf);
|
||||
let hash_nf = md5::compute(&nf);
|
||||
let hash_bodies = md5::compute(&bs);
|
||||
let hash_colliders = md5::compute(&cs);
|
||||
let hash_joints = md5::compute(&js);
|
||||
format!(
|
||||
r#"Serialization time: {:.2}ms
|
||||
Hashes at frame: {}
|
||||
|_ Broad phase [{:.1}KB]: {}
|
||||
|_ Narrow phase [{:.1}KB]: {}
|
||||
|_ Bodies [{:.1}KB]: {}
|
||||
|_ Colliders [{:.1}KB]: {}
|
||||
|_ Joints [{:.1}KB]: {}"#,
|
||||
serialization_time,
|
||||
timestep_id,
|
||||
bf.len() as f32 / 1000.0,
|
||||
format!("{:?}", hash_bf).split_at(10).0,
|
||||
nf.len() as f32 / 1000.0,
|
||||
format!("{:?}", hash_nf).split_at(10).0,
|
||||
bs.len() as f32 / 1000.0,
|
||||
format!("{:?}", hash_bodies).split_at(10).0,
|
||||
cs.len() as f32 / 1000.0,
|
||||
format!("{:?}", hash_colliders).split_at(10).0,
|
||||
js.len() as f32 / 1000.0,
|
||||
format!("{:?}", hash_joints).split_at(10).0,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user