use rand_xorshift::{self, XorShiftRng};
use vec_map::VecMap;
use gl_utils::*;
use linear_sim::*;
use crate::{component, program};
const INITIAL_HISTORY_LENGTH : usize = 4096;
mod drop_test {
pub(super) const INITIAL_CAMERA3D_POSITION : [f32; 3] =
[-4.317_839_6, 2.071_068_5, 6.0];
pub(super) const INITIAL_CAMERA3D_YAW : f32 = 5.497_787_5;
pub(super) const INITIAL_CAMERA3D_PITCH : f32 = -std::f32::consts::FRAC_PI_6;
pub(super) const GRAVITY_ACCELERATION : [f64; 3] =
[0.0, 0.0, (-9.8)/ (60.0 * 60.0)];
pub(super) const BLOCK_POSITION : [f64; 3] = [0.0, 1.0, 1.6];
pub(super) const BLOCK_EXTENTS : [f64; 3] = [2.0, 3.0, 0.2];
pub(super) const GROUND_POSITION : [f64; 3] = [0.0, 0.0, 0.0];
pub(super) const SPHERE_POSITION : [f64; 3] = [-2.3, 4.3, 6.0]; pub(super) const SPHERE_RADIUS : f64 = 0.5;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum ObjectStatic {
Block = 0,
Ground
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum ObjectDynamic {
BallA = 0,
BallB,
BallC
}
}
mod collide_capsule_capsule {
use gl_utils::render::resource::draw3d;
pub(super) const CAPSULE_COUNT : usize = 5;
pub(super) const GENERATION_START : u64 = 14;
pub(super) const DENSITY_MIN : f64 = 150.0; pub(super) const DENSITY_MAX : f64 = 20000.0;
pub(super) const CAPSULE_RADIUS_MIN : f64 = 0.05; pub(super) const CAPSULE_RADIUS_MAX : f64 = 0.7; pub(super) const CAPSULE_HALF_HEIGHT_MIN : f64 = 0.0; pub(super) const CAPSULE_HALF_HEIGHT_MAX : f64 = 2.0;
pub(super) const SPEED_MIN : f64 = 1.0/120.0; pub(super) const SPEED_MAX : f64 = 2.5;
pub(super) const TIME_TARGET : f64 = 1.0; pub(super) const STEPS_PER_SECOND : f64 = 60.0;
pub(super) const INITIAL_CAMERA3D_POSITION : [f32; 3] =
[0.0, -(1.0 * draw3d::MESH_GRID_DIMS as f32), 2.0];
#[expect(dead_code)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum ObjectDynamic {
CapsuleA = 0,
CapsuleB,
CapsuleC
}
}
mod stack_test {
pub(super) const INITIAL_CAMERA3D_POSITION : [f32; 3] = [-8.0, 1.0, 8.0];
pub(super) const INITIAL_CAMERA3D_YAW : f32 = -1.6;
pub(super) const INITIAL_CAMERA3D_PITCH : f32 = -0.25;
pub(super) const GRAVITY_ACCELERATION : [f64; 3] = [0.0, 0.0, (-9.8)/ (60.0 * 60.0)];
pub(super) const GROUND_POSITION : [f64; 3] = [0.0, 0.0, 0.0];
pub(super) const CUBOID_POSITION : [f64; 3] = [0.0, 1.0, 6.0]; pub(super) const CUBOID_EXTENT : f64 = 0.5;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum ObjectStatic {
Ground = 0
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum ObjectDynamic {
BoxA = 0,
BoxB,
BoxC
}
}
mod ball_pit_test {
pub(super) const GENERATION_START : u64 = 0;
pub(super) const INITIAL_CAMERA3D_POSITION : [f32; 3] = [-12.0, 1.0, 14.0];
pub(super) const INITIAL_CAMERA3D_YAW : f32 = -std::f32::consts::FRAC_PI_2;
pub(super) const INITIAL_CAMERA3D_PITCH : f32 = -0.25;
pub(super) const GRAVITY_ACCELERATION : [f64; 3] =
[0.0, 0.0, (-9.8) / (60.0 * 60.0)];
pub(super) const PIT_DEPTH : f64 = 8.0;
pub(super) const BLOCK_THICKNESS : f64 = 0.4;
pub(super) const BLOCK_BOTTOM_POSITION : [f64; 3] = [ 0.0, 1.0, 1.6];
pub(super) const BLOCK_BOTTOM_EXTENTS : [f64; 3] = [
2.0 + BLOCK_THICKNESS / 2.0,
2.0 + BLOCK_THICKNESS / 2.0,
BLOCK_THICKNESS / 2.0
];
pub(super) const BLOCK_LEFT_POSITION : [f64; 3] = [
BLOCK_BOTTOM_POSITION[0] - BLOCK_BOTTOM_EXTENTS[0] +
BLOCK_THICKNESS / 2.0,
BLOCK_BOTTOM_POSITION[1],
BLOCK_BOTTOM_POSITION[2] + PIT_DEPTH / 2.0 + BLOCK_THICKNESS / 2.0
];
pub(super) const BLOCK_LEFT_EXTENTS : [f64; 3] = [
BLOCK_THICKNESS / 2.0,
BLOCK_BOTTOM_EXTENTS[1] - BLOCK_THICKNESS,
PIT_DEPTH / 2.0
];
pub(super) const BLOCK_RIGHT_POSITION : [f64; 3] = [
BLOCK_BOTTOM_POSITION[0] + BLOCK_BOTTOM_EXTENTS[0] -
BLOCK_THICKNESS / 2.0,
BLOCK_BOTTOM_POSITION[1],
BLOCK_BOTTOM_POSITION[2] + PIT_DEPTH / 2.0 + BLOCK_THICKNESS / 2.0
];
pub(super) const BLOCK_RIGHT_EXTENTS : [f64; 3] = [
BLOCK_THICKNESS / 2.0,
BLOCK_BOTTOM_EXTENTS[1] - BLOCK_THICKNESS,
PIT_DEPTH / 2.0
];
pub(super) const BLOCK_FRONT_POSITION : [f64; 3] = [
BLOCK_BOTTOM_POSITION[0],
BLOCK_BOTTOM_POSITION[1] - BLOCK_BOTTOM_EXTENTS[1] +
BLOCK_THICKNESS / 2.0,
BLOCK_BOTTOM_POSITION[2] + PIT_DEPTH / 2.0 + BLOCK_THICKNESS / 2.0
];
pub(super) const BLOCK_FRONT_EXTENTS : [f64; 3] = [
BLOCK_BOTTOM_EXTENTS[0],
BLOCK_THICKNESS / 2.0,
PIT_DEPTH / 2.0
];
pub(super) const BLOCK_BACK_POSITION : [f64; 3] = [
BLOCK_BOTTOM_POSITION[0],
BLOCK_BOTTOM_POSITION[1] + BLOCK_BOTTOM_EXTENTS[1] -
BLOCK_THICKNESS / 2.0,
BLOCK_BOTTOM_POSITION[2] + PIT_DEPTH / 2.0 + BLOCK_THICKNESS / 2.0
];
pub(super) const BLOCK_BACK_EXTENTS : [f64; 3] = [
BLOCK_BOTTOM_EXTENTS[0],
BLOCK_THICKNESS / 2.0,
PIT_DEPTH / 2.0
];
pub(super) const N_BALLS : u32 = 40;
pub(super) const SPHERE_RADIUS : f64 = 0.5;
pub(super) const SPHERE_POSITION : [f64; 3] = [
BLOCK_BOTTOM_POSITION[0],
BLOCK_BOTTOM_POSITION[1],
BLOCK_BOTTOM_POSITION[2] + 18.0
];
pub(super) const SPHERE_RANGE : [f64; 3] = [
BLOCK_BOTTOM_EXTENTS[0] - BLOCK_THICKNESS - SPHERE_RADIUS - 0.05 + 3.5,
BLOCK_BOTTOM_EXTENTS[1] - BLOCK_THICKNESS - SPHERE_RADIUS - 0.05 + 3.5,
6.0
];
}
mod hull_pit_test {
pub(super) const GENERATION_START : u64 = 0;
pub(super) const INITIAL_CAMERA3D_POSITION : [f32; 3] = [-8.0, 1.0, 8.0];
pub(super) const INITIAL_CAMERA3D_YAW : f32 = -1.6;
pub(super) const INITIAL_CAMERA3D_PITCH : f32 = -0.25;
pub(super) const GRAVITY_ACCELERATION : [f64; 3] =
[0.0, 0.0, (-9.8) / (60.0 * 60.0)];
pub(super) const PIT_DEPTH : f64 = 8.0;
pub(super) const BLOCK_THICKNESS : f64 = 0.4;
pub(super) const BLOCK_BOTTOM_POSITION : [f64; 3] = [ 0.0, 1.0, 1.6];
pub(super) const BLOCK_BOTTOM_EXTENTS : [f64; 3] = [
2.0 + BLOCK_THICKNESS / 2.0,
2.0 + BLOCK_THICKNESS / 2.0,
BLOCK_THICKNESS / 2.0
];
pub(super) const BLOCK_LEFT_POSITION : [f64; 3] = [
BLOCK_BOTTOM_POSITION[0] - BLOCK_BOTTOM_EXTENTS[0] +
BLOCK_THICKNESS / 2.0,
BLOCK_BOTTOM_POSITION[1],
BLOCK_BOTTOM_POSITION[2] + PIT_DEPTH / 2.0 + BLOCK_THICKNESS / 2.0
];
pub(super) const BLOCK_LEFT_EXTENTS : [f64; 3] = [
BLOCK_THICKNESS / 2.0,
BLOCK_BOTTOM_EXTENTS[1] - BLOCK_THICKNESS,
PIT_DEPTH / 2.0
];
pub(super) const BLOCK_RIGHT_POSITION : [f64; 3] = [
BLOCK_BOTTOM_POSITION[0] + BLOCK_BOTTOM_EXTENTS[0] -
BLOCK_THICKNESS / 2.0,
BLOCK_BOTTOM_POSITION[1],
BLOCK_BOTTOM_POSITION[2] + PIT_DEPTH / 2.0 + BLOCK_THICKNESS / 2.0
];
pub(super) const BLOCK_RIGHT_EXTENTS : [f64; 3] = [
BLOCK_THICKNESS / 2.0,
BLOCK_BOTTOM_EXTENTS[1] - BLOCK_THICKNESS,
PIT_DEPTH / 2.0
];
pub(super) const BLOCK_FRONT_POSITION : [f64; 3] = [
BLOCK_BOTTOM_POSITION[0],
BLOCK_BOTTOM_POSITION[1] - BLOCK_BOTTOM_EXTENTS[1] +
BLOCK_THICKNESS / 2.0,
BLOCK_BOTTOM_POSITION[2] + PIT_DEPTH / 2.0 + BLOCK_THICKNESS / 2.0
];
pub(super) const BLOCK_FRONT_EXTENTS : [f64; 3] = [
BLOCK_BOTTOM_EXTENTS[0],
BLOCK_THICKNESS / 2.0,
PIT_DEPTH / 2.0
];
pub(super) const BLOCK_BACK_POSITION : [f64; 3] = [
BLOCK_BOTTOM_POSITION[0],
BLOCK_BOTTOM_POSITION[1] + BLOCK_BOTTOM_EXTENTS[1] -
BLOCK_THICKNESS / 2.0,
BLOCK_BOTTOM_POSITION[2] + PIT_DEPTH / 2.0 + BLOCK_THICKNESS / 2.0
];
pub(super) const BLOCK_BACK_EXTENTS : [f64; 3] = [
BLOCK_BOTTOM_EXTENTS[0],
BLOCK_THICKNESS / 2.0,
PIT_DEPTH / 2.0
];
pub(super) const N_HULLS : u32 = 30;
pub(super) const HULL_MIN_POINTS : u32 = 6;
pub(super) const HULL_MAX_POINTS : u32 = 20;
pub(super) const SPHERE_RADIUS : f64 = 0.5;
pub(super) const SPHERE_POSITION : [f64; 3] = [
BLOCK_BOTTOM_POSITION[0],
BLOCK_BOTTOM_POSITION[1],
BLOCK_BOTTOM_POSITION[2] + 12.0
];
pub(super) const SPHERE_RANGE : [f64; 3] = [
BLOCK_BOTTOM_EXTENTS[0] - BLOCK_THICKNESS - SPHERE_RADIUS - 0.05,
BLOCK_BOTTOM_EXTENTS[1] - BLOCK_THICKNESS - SPHERE_RADIUS - 0.05,
6.0
];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum ObjectStatic {
Bottom = 0,
Left,
Right,
Front,
Back
}
}
pub struct Simulation {
pub system : System <integrator::SemiImplicitEuler>,
pub history : Vec <System <integrator::SemiImplicitEuler>>,
pub hull_meshes : Vec <geometry::mesh::VertexEdgeTriangleMesh>
}
#[derive(Debug,Default)]
pub struct Playback {
pub paused : bool,
pub single_step : bool,
pub single_step_reverse : bool
}
pub fn init_drop_test (render_context : &mut Render <render::resource::Default>)
-> Simulation
{
use self::drop_test::*;
use render::resource::draw3d;
use math::num::Zero;
program::reset_render_context (render_context);
render_context.camera3d_position_set (INITIAL_CAMERA3D_POSITION.into());
render_context.camera3d_rotate (
math::Rad (INITIAL_CAMERA3D_YAW), math::Rad (INITIAL_CAMERA3D_PITCH),
math::Rad::zero());
let simulation = Simulation::new_drop_test();
let grid_vertex_data = render::resource::Default::debug_grid_vertices();
let (capsule_vertex_data, aabb_vertex_data, sphere_vertex_data, hull_vertex_data) =
simulation.object_vertex_data();
debug_assert!(hull_vertex_data.is_empty());
let aabb_lines = Some (&aabb_vertex_data[..]);
let meshes = {
let mut v = VecMap::with_capacity (2);
assert!(v.insert (draw3d::MeshId::Grid as usize, &grid_vertex_data[..])
.is_none());
assert!(v.insert (draw3d::MeshId::Capsule as usize, &capsule_vertex_data[..])
.is_none());
assert!(v.insert (draw3d::MeshId::Sphere as usize, &sphere_vertex_data[..])
.is_none());
v
};
render_context.resource.draw3d.instance_vertices_set (
&render_context.glium_display,
draw3d::InstancesInit {
billboards: VecMap::default(),
aabb_lines,
meshes,
.. Default::default()
}
);
program::print_testbed_modes_prompt();
println!("linear-sim simulation drop test");
println!(" press 'P' to pause/resume");
println!(" press '.' to step forward");
println!(" press ',' to step backward");
println!(" press 'Backspace' to reset");
simulation
}
pub fn init_collide_capsule_capsule (
render_context : &mut Render <render::resource::Default>,
rng : &mut XorShiftRng,
) -> (Simulation, u64) {
use rand::SeedableRng;
use self::collide_capsule_capsule::*;
use render::resource::draw3d;
program::reset_render_context (render_context);
render_context.camera3d_position_set (INITIAL_CAMERA3D_POSITION.into());
*rng = XorShiftRng::seed_from_u64 (0);
let simulation = {
let mut generation = 0;
loop {
let simulation = Simulation::new_collide_capsule_capsule (rng);
if generation == GENERATION_START { break simulation }
generation += 1;
}
};
let grid_vertex_data = render::resource::Default::debug_grid_vertices();
let (capsule_vertex_data, aabb_vertex_data, sphere_vertex_data, hull_vertex_data) =
simulation.object_vertex_data();
debug_assert!(hull_vertex_data.is_empty());
let aabb_lines = Some (&aabb_vertex_data[..]);
let meshes = {
let mut v = VecMap::with_capacity (2);
assert!(v.insert (draw3d::MeshId::Grid as usize, &grid_vertex_data[..])
.is_none());
assert!(v.insert (draw3d::MeshId::Capsule as usize, &capsule_vertex_data[..])
.is_none());
assert!(v.insert (draw3d::MeshId::Sphere as usize, &sphere_vertex_data[..])
.is_none());
v
};
render_context.resource.draw3d.instance_vertices_set (
&render_context.glium_display,
draw3d::InstancesInit {
billboards: VecMap::default(),
aabb_lines,
meshes,
.. Default::default()
}
);
program::print_testbed_modes_prompt();
println!("linear-sim simulation collide capsule capsule");
println!(" press 'P' to pause/resume");
println!(" press '.' to step forward");
println!(" press ',' to step backward");
println!(" press 'Backspace' to reset current pair");
println!(" press 'Tab' to generate a new collision pair");
(simulation, GENERATION_START)
}
pub fn init_stack_test (render_context : &mut Render <render::resource::Default>)
-> Simulation
{
use self::stack_test::*;
use render::resource::draw3d;
use math::num::Zero;
program::reset_render_context (render_context);
render_context.camera3d_position_set (INITIAL_CAMERA3D_POSITION.into());
render_context.camera3d_rotate (
math::Rad (INITIAL_CAMERA3D_YAW), math::Rad (INITIAL_CAMERA3D_PITCH),
math::Rad::zero());
let simulation = Simulation::new_stack_test();
let grid_vertex_data = render::resource::Default::debug_grid_vertices();
let (capsule_vertex_data, aabb_vertex_data, sphere_vertex_data, hull_vertex_data) =
simulation.object_vertex_data();
debug_assert!(hull_vertex_data.is_empty());
let aabb_lines = Some (&aabb_vertex_data[..]);
let meshes = {
let mut v = VecMap::with_capacity (2);
assert!(v.insert (draw3d::MeshId::Grid as usize, &grid_vertex_data[..])
.is_none());
assert!(v.insert (draw3d::MeshId::Capsule as usize, &capsule_vertex_data[..])
.is_none());
assert!(v.insert (draw3d::MeshId::Sphere as usize, &sphere_vertex_data[..])
.is_none());
v
};
render_context.resource.draw3d.instance_vertices_set (
&render_context.glium_display,
draw3d::InstancesInit {
billboards: VecMap::default(),
aabb_lines,
meshes,
.. Default::default()
}
);
program::print_testbed_modes_prompt();
println!("linear-sim simulation stack test");
println!(" press 'P' to pause/resume");
println!(" press '.' to step forward");
println!(" press ',' to step backward");
println!(" press 'Backspace' to reset");
simulation
}
pub fn init_ball_pit_test (
render_context : &mut Render <render::resource::Default>,
rng : &mut XorShiftRng
) -> (Simulation, u64) {
use rand::SeedableRng;
use math::num::Zero;
use render::resource::draw3d;
use self::ball_pit_test::*;
program::reset_render_context (render_context);
render_context.camera3d_position_set (INITIAL_CAMERA3D_POSITION.into());
render_context.camera3d_rotate (
math::Rad (INITIAL_CAMERA3D_YAW), math::Rad (INITIAL_CAMERA3D_PITCH),
math::Rad::zero());
*rng = XorShiftRng::seed_from_u64 (1);
let simulation = {
let mut generation = 0;
loop {
let simulation = Simulation::new_ball_pit_test (rng);
if generation == GENERATION_START { break simulation }
generation += 1;
}
};
let grid_vertex_data = render::resource::Default::debug_grid_vertices();
let (capsule_vertex_data, aabb_vertex_data, sphere_vertex_data, hull_vertex_data) =
simulation.object_vertex_data();
const HULL_LINE_POINTS : usize = 36 * 4 + 24 * 4;
let buffer = render_context.resource.draw3d.user_buffers.entry (0).or_insert_with (||
glium::VertexBuffer::empty_dynamic (&render_context.glium_display, HULL_LINE_POINTS)
.unwrap());
buffer.write (&hull_vertex_data);
assert!(render_context.resource.draw3d.user_draw.insert (0, draw3d::UserDraw {
buffer_key: draw3d::UserBufferKey (0),
range: 0..hull_vertex_data.len() as u32,
primitive_type: glium::index::PrimitiveType::LinesList,
shader_program_id: shader::ProgramId::WorldSpace3dColor,
draw_pass: draw3d::DrawPass::Depth
}).is_none());
let aabb_lines = Some (&aabb_vertex_data[..]);
let meshes = {
let mut v = VecMap::with_capacity (2);
assert!(v.insert (draw3d::MeshId::Grid as usize, &grid_vertex_data[..])
.is_none());
assert!(v.insert (draw3d::MeshId::Capsule as usize, &capsule_vertex_data[..])
.is_none());
assert!(v.insert (draw3d::MeshId::Sphere as usize, &sphere_vertex_data[..])
.is_none());
v
};
render_context.resource.draw3d.instance_vertices_set (
&render_context.glium_display,
draw3d::InstancesInit {
billboards: VecMap::default(),
aabb_lines,
meshes,
.. Default::default()
}
);
program::print_testbed_modes_prompt();
println!("linear-sim simulation ball pit test");
println!(" press 'P' to pause/resume");
println!(" press '.' to step forward");
println!(" press ',' to step backward");
println!(" press 'Backspace' to reset");
(simulation, GENERATION_START)
}
pub fn init_hull_pit_test (
render_context : &mut Render <render::resource::Default>,
rng : &mut XorShiftRng
) -> (Simulation, u64) {
use rand::SeedableRng;
use math::num::Zero;
use render::resource::draw3d;
use self::hull_pit_test::*;
program::reset_render_context (render_context);
render_context.camera3d_position_set (INITIAL_CAMERA3D_POSITION.into());
render_context.camera3d_rotate (
math::Rad (INITIAL_CAMERA3D_YAW), math::Rad (INITIAL_CAMERA3D_PITCH),
math::Rad::zero());
*rng = XorShiftRng::seed_from_u64 (1);
let simulation = {
let mut generation = 0;
loop {
let simulation = Simulation::new_hull_pit_test (rng);
if generation == GENERATION_START { break simulation }
generation += 1;
}
};
let grid_vertex_data = render::resource::Default::debug_grid_vertices();
let (capsule_vertex_data, aabb_vertex_data, sphere_vertex_data, hull_vertex_data) =
simulation.object_vertex_data();
const MAX_POINTS_PER_HULL : usize = 3 * (HULL_MAX_POINTS * 2 - 4) as usize;
const MAX_LINE_POINTS : usize = 2 * MAX_POINTS_PER_HULL * N_HULLS as usize;
let buffer = render_context.resource.draw3d.user_buffers.entry (0).or_insert_with (||
glium::VertexBuffer::empty_dynamic (&render_context.glium_display, MAX_LINE_POINTS)
.unwrap());
buffer.slice(..hull_vertex_data.len()).unwrap().write (&hull_vertex_data);
assert!(render_context.resource.draw3d.user_draw.insert (0, draw3d::UserDraw {
buffer_key: draw3d::UserBufferKey (0),
range: 0..hull_vertex_data.len() as u32,
primitive_type: glium::index::PrimitiveType::LinesList,
shader_program_id: shader::ProgramId::WorldSpace3dColor,
draw_pass: draw3d::DrawPass::Depth
}).is_none());
let aabb_lines = Some (&aabb_vertex_data[..]);
let meshes = {
let mut v = VecMap::with_capacity (2);
assert!(v.insert (draw3d::MeshId::Grid as usize, &grid_vertex_data[..])
.is_none());
assert!(v.insert (draw3d::MeshId::Capsule as usize, &capsule_vertex_data[..])
.is_none());
assert!(v.insert (draw3d::MeshId::Sphere as usize, &sphere_vertex_data[..])
.is_none());
v
};
render_context.resource.draw3d.instance_vertices_set (
&render_context.glium_display,
draw3d::InstancesInit {
billboards: VecMap::default(),
aabb_lines,
meshes,
.. Default::default()
}
);
program::print_testbed_modes_prompt();
println!("linear-sim simulation hull pit test");
println!(" press 'P' to pause/resume");
println!(" press '.' to step forward");
println!(" press ',' to step backward");
println!(" press 'Backspace' to reset");
(simulation, GENERATION_START)
}
pub fn update (
render_context : &mut Render <render::resource::Default>,
playback : &mut Playback,
simulation : &mut Simulation
) {
if !playback.paused {
step (render_context, simulation);
} else if playback.single_step {
step (render_context, simulation);
playback.single_step = false;
} else if playback.single_step_reverse {
step_reverse (render_context, simulation);
playback.single_step_reverse = false;
}
}
pub fn restart_drop_test (
render_context : &mut Render <render::resource::Default>,
simulation : &mut Simulation
) {
*simulation = Simulation::new_drop_test();
update_render_context (render_context, &simulation.system, &simulation.hull_meshes);
}
pub fn restart_collide_capsule_capsule (
render_context : &mut Render <render::resource::Default>,
simulation : &mut Simulation
) {
simulation.system = simulation.history[0].clone();
simulation.history.truncate (1);
update_render_context (render_context, &simulation.system, &simulation.hull_meshes);
}
pub fn restart_stack_test (
render_context : &mut Render <render::resource::Default>,
simulation : &mut Simulation
) {
*simulation = Simulation::new_stack_test();
update_render_context (render_context, &simulation.system, &simulation.hull_meshes);
}
pub fn restart_ball_pit_test (
render_context : &mut Render <render::resource::Default>,
simulation : &mut Simulation
) {
simulation.system = simulation.history[0].clone();
simulation.history.truncate (1);
update_render_context (render_context, &simulation.system, &simulation.hull_meshes);
}
pub fn restart_hull_pit_test (
render_context : &mut Render <render::resource::Default>,
simulation : &mut Simulation
) {
simulation.system = simulation.history[0].clone();
simulation.history.truncate (1);
update_render_context (render_context, &simulation.system, &simulation.hull_meshes);
}
impl Simulation {
pub fn new_drop_test() -> Self {
use geometry::shape;
use self::drop_test::*;
let mut system = System::<integrator::SemiImplicitEuler>::default();
let mut history = Vec::with_capacity (INITIAL_HISTORY_LENGTH);
let myblock = {
let position = component::Position (BLOCK_POSITION.into());
let bound = component::Bound (shape::Bounded::from (
shape::Cuboid::noisy (BLOCK_EXTENTS)).into());
let material = component::MATERIAL_STONE;
let collidable = true;
object::Static { position, bound, material, collidable }.into()
};
let result = system.handle_event (event::Input::CreateObject (
myblock, Some (object::Key::from (ObjectStatic::Block as u32))
));
assert_create_object_success (result);
let myground = {
let position = component::Position (GROUND_POSITION.into());
let bound = component::Bound (shape::Unbounded::from (
shape::Orthant::from (math::SignedAxis3::PosZ)).into());
let material = component::MATERIAL_STONE;
let collidable = true;
object::Static { position, bound, material, collidable }.into()
};
let result = system.handle_event (event::Input::CreateObject (
myground, Some (object::Key::from (ObjectStatic::Ground as u32))
));
assert_create_object_success (result);
let mysphere = {
let position = component::Position (SPHERE_POSITION.into());
let mass = component::Mass::new (20.0);
let derivatives = component::Derivatives::zero();
let drag = component::Drag::zero();
let bound = component::Bound::from (shape::Sphere::noisy (SPHERE_RADIUS));
let material = component::MATERIAL_STONE;
let collidable = true;
object::Dynamic {
position, mass, derivatives, drag, bound, material, collidable
}.into()
};
let result = system.handle_event (event::Input::CreateObject (
mysphere, Some (object::Key::from (ObjectDynamic::BallA as u32))
));
assert_create_object_success (result);
let mysphere = {
let position = component::Position (
math::Point3::from (SPHERE_POSITION) +
math::Vector3::new (-0.01, -0.015, 4.0)
);
let mass = component::Mass::new (20.0);
let derivatives = component::Derivatives::zero();
let drag = component::Drag::zero();
let bound = component::Bound::from (shape::Sphere::noisy (SPHERE_RADIUS));
let material = component::MATERIAL_STONE;
let collidable = true;
object::Dynamic {
position, mass, derivatives, drag, bound, material, collidable
}.into()
};
let result = system.handle_event (event::Input::CreateObject (
mysphere, Some (object::Key::from (ObjectDynamic::BallB as u32))
));
assert_create_object_success (result);
let mysphere = {
let position = component::Position (
math::Point3::from (SPHERE_POSITION) +
math::Vector3::new (0.02, 0.03, 2.0)
);
let mass = component::Mass::new (20.0);
let derivatives = component::Derivatives::zero();
let drag = component::Drag::zero();
let bound = component::Bound::from (shape::Sphere::noisy (SPHERE_RADIUS));
let material = component::MATERIAL_STONE;
let collidable = true;
object::Dynamic {
position, mass, derivatives, drag, bound, material, collidable
}.into()
};
let result = system.handle_event (event::Input::CreateObject (
mysphere, Some (object::Key::from (ObjectDynamic::BallC as u32))
));
assert_create_object_success (result);
let gravity = force::Gravity { acceleration: GRAVITY_ACCELERATION.into() };
assert!(system.handle_event (event::Input::SetGravity (gravity)).is_empty());
history.push (system.clone());
let hull_meshes = vec![];
Simulation { system, history, hull_meshes }
}
pub fn new_collide_capsule_capsule (rng : &mut XorShiftRng) -> Self {
use self::collide_capsule_capsule::*;
fn generate_random_capsule (rng : &mut XorShiftRng) -> object::Dynamic {
use std::f64::consts::*;
use rand::RngExt;
use geometry::shape;
use geometry::shape::Stereometric;
const UNIT_Y : math::Vector3 <f64> =
math::Vector3 { x: 0.0, y: 1.0, z: 0.0};
let radius = rng.random_range (
CAPSULE_RADIUS_MIN..CAPSULE_RADIUS_MAX);
let half_height = rng.random_range (
CAPSULE_HALF_HEIGHT_MIN..CAPSULE_HALF_HEIGHT_MAX);
let density = rng.random_range (DENSITY_MIN..DENSITY_MAX);
let shape = shape::Capsule::noisy (radius, half_height);
let volume = *shape.volume();
let mass = component::Mass::new (density * volume);
let speed = rng.random_range (SPEED_MIN..SPEED_MAX);
let random_yaw = math::Rad (rng.random_range (0.0..2.0 * PI));
let rotation_yaw = math::Rotation3::from_angle_z (random_yaw);
let random_pitch = math::Rad (rng.random_range (-0.5 * PI..0.5 * PI));
let rotation_pitch = math::Rotation3::from_angle_x (random_pitch);
let direction = *(rotation_pitch * rotation_yaw) * UNIT_Y;
let velocity = speed * direction;
let position = component::Position (
math::Point3 (-TIME_TARGET * STEPS_PER_SECOND * velocity));
let derivatives = component::Derivatives {
velocity, .. component::Derivatives::zero()
};
let drag = component::Drag (0.01);
let bound = component::Bound::from (shape);
let material = component::MATERIAL_STONE;
let collidable = true;
object::Dynamic {
position, mass, derivatives, drag, bound, material, collidable
}
}
let mut system = System::<integrator::SemiImplicitEuler>::default();
let mut history = Vec::with_capacity (INITIAL_HISTORY_LENGTH);
for i in 0..CAPSULE_COUNT {
loop {
let capsule = generate_random_capsule (rng);
let mut failed = false;
for output_event in system.handle_event (event::Input::CreateObject (
capsule.clone().into(), Some (object::Key::from (i as u32))
)) {
match output_event {
event::Output::CreateObjectResult (
event::CreateObjectResult::Intersection (..)
) => {
failed = true;
break
}
event::Output::CreateObjectResult (
event::CreateObjectResult::Created (..)
) => {}
_ => unreachable!("only create object result events should be returned")
}
}
if !failed { break }
}
}
history.push (system.clone());
let hull_meshes = vec![];
Simulation { system, history, hull_meshes }
}
pub fn new_stack_test() -> Self {
use geometry::shape;
use self::stack_test::*;
let mut system = System::<integrator::SemiImplicitEuler>::default();
let mut history = Vec::with_capacity (INITIAL_HISTORY_LENGTH);
let myground = {
let position = component::Position (GROUND_POSITION.into());
let bound = component::Bound (shape::Unbounded::from (
shape::Orthant::from (math::SignedAxis3::PosZ)).into());
let material = component::MATERIAL_STONE;
let collidable = true;
object::Static { position, bound, material, collidable }.into()
};
let result = system.handle_event (event::Input::CreateObject (
myground, Some (object::Key::from (ObjectStatic::Ground as u32))
));
assert_create_object_success (result);
let mycuboid = {
let position = component::Position (CUBOID_POSITION.into());
let mass = component::Mass::new (20.0);
let derivatives = component::Derivatives::zero();
let drag = component::Drag::zero();
let bound = shape::Cuboid::noisy ([
CUBOID_EXTENT, CUBOID_EXTENT, CUBOID_EXTENT
]).into();
let material = component::MATERIAL_STONE;
let collidable = true;
object::Dynamic {
position, mass, derivatives, drag, bound, material, collidable
}.into()
};
let result = system.handle_event (event::Input::CreateObject (
mycuboid, Some (object::Key::from (ObjectDynamic::BoxA as u32))
));
assert_create_object_success (result);
let mycuboid = {
let position = component::Position (
math::Point3::from (CUBOID_POSITION) +
math::Vector3::new (-0.01, -0.015, 4.0)
);
let mass = component::Mass::new (20.0);
let derivatives = component::Derivatives::zero();
let drag = component::Drag::zero();
let bound = shape::Cuboid::noisy ([
CUBOID_EXTENT, CUBOID_EXTENT, CUBOID_EXTENT
]).into();
let material = component::MATERIAL_STONE;
let collidable = true;
object::Dynamic {
position, mass, derivatives, drag, bound, material, collidable
}.into()
};
let result = system.handle_event (event::Input::CreateObject (
mycuboid, Some (object::Key::from (ObjectDynamic::BoxB as u32))
));
assert_create_object_success (result);
let mycuboid = {
let position = component::Position (
math::Point3::from (CUBOID_POSITION) +
math::Vector3::new (0.02, 0.03, 2.0)
);
let mass = component::Mass::new (20.0);
let derivatives = component::Derivatives::zero();
let drag = component::Drag::zero();
let bound = shape::Cuboid::noisy ([
CUBOID_EXTENT, CUBOID_EXTENT, CUBOID_EXTENT
]).into();
let material = component::MATERIAL_STONE;
let collidable = true;
object::Dynamic {
position, mass, derivatives, drag, bound, material, collidable
}.into()
};
let result = system.handle_event (event::Input::CreateObject (
mycuboid, Some (object::Key::from (ObjectDynamic::BoxC as u32))
));
assert_create_object_success (result);
let gravity = force::Gravity { acceleration: GRAVITY_ACCELERATION.into() };
assert!(system.handle_event (event::Input::SetGravity (gravity)).is_empty());
history.push (system.clone());
let hull_meshes = vec![];
Simulation { system, history, hull_meshes }
}
pub fn new_ball_pit_test (rng : &mut XorShiftRng) -> Self {
use math::*;
use geometry::shape;
use self::ball_pit_test::*;
let mut system = System::<integrator::SemiImplicitEuler>::default();
let mut history = Vec::with_capacity (INITIAL_HISTORY_LENGTH);
let mut hull_meshes = vec![];
let mut object_index = 0u32;
let mut make_block = |position, extents| {
let object = {
let position = component::Position (position);
let bound = component::Bound::from (shape::Cuboid::noisy (extents));
let material = component::MATERIAL_STONE;
let collidable = true;
object::Static { position, bound, material, collidable }.into()
};
let result = system.handle_event (event::Input::CreateObject (
object, Some (object::Key::from (object_index))
));
assert_create_object_success (result);
object_index += 1;
};
make_block (BLOCK_BOTTOM_POSITION.into(), BLOCK_BOTTOM_EXTENTS);
make_block (BLOCK_LEFT_POSITION.into(), BLOCK_LEFT_EXTENTS);
make_block (BLOCK_RIGHT_POSITION.into(), BLOCK_RIGHT_EXTENTS);
make_block (BLOCK_FRONT_POSITION.into(), BLOCK_FRONT_EXTENTS);
make_block (BLOCK_BACK_POSITION.into(), BLOCK_BACK_EXTENTS);
let mut make_obb = |
position,
extents : [Positive <f64>; 3],
orientation
| {
let mut obb = geometry::Obb3::new (Point3::origin(), extents.into(), orientation);
let object = {
let position = component::Position (position);
let bound = {
let (hull, mesh) = geometry::Hull3::from_points_with_mesh (
&obb.corners()
).unwrap();
hull_meshes.push (mesh);
component::Bound::from (hull)
};
let material = component::MATERIAL_STONE;
let collidable = true;
object::Static { position, bound, material, collidable }.into()
};
let result = system.handle_event (event::Input::CreateObject (
object, Some (object::Key::from (object_index))
));
assert_create_object_success (result);
object_index += 1;
obb.center = position;
obb
};
let obb_left = make_obb (
point3 (0.0, 4.5, 11.2125),
[BLOCK_BOTTOM_EXTENTS[0] - 0.4, BLOCK_BOTTOM_EXTENTS[1], 0.2]
.map (Positive::noisy),
Angles3::wrap (0.0.into(), Turn (0.125).into(), 0.0.into()));
let obb_right = make_obb (
point3 (0.0, -2.5, 11.2125),
[BLOCK_BOTTOM_EXTENTS[0] - 0.4, BLOCK_BOTTOM_EXTENTS[1], 0.2]
.map (Positive::noisy),
Angles3::wrap (0.0.into(), Turn (-0.125).into(), 0.0.into()));
let obb_front = make_obb (
point3 (-3.5, 1.0, 11.2125),
[BLOCK_BOTTOM_EXTENTS[0], BLOCK_BOTTOM_EXTENTS[1] - 0.4, 0.2]
.map (Positive::noisy),
Angles3::wrap (0.0.into(), 0.0.into(), Turn (0.125).into()));
let obb_back = make_obb (
point3 (3.5, 1.0, 11.2125),
[BLOCK_BOTTOM_EXTENTS[0], BLOCK_BOTTOM_EXTENTS[1] - 0.4, 0.2]
.map (Positive::noisy),
Angles3::wrap (0.0.into(), 0.0.into(), Turn (-0.125).into()));
let mut make_hull = |points : &[Point3 <f64>]| {
let object = {
let position = component::Position::origin();
let bound = {
let (hull, mesh) = geometry::Hull3::from_points_with_mesh (points).unwrap();
hull_meshes.push (mesh);
component::Bound::from (hull)
};
let material = component::MATERIAL_STONE;
let collidable = true;
object::Static { position, bound, material, collidable }.into()
};
let result = system.handle_event (event::Input::CreateObject (
object, Some (object::Key::from (object_index))
));
assert_create_object_success (result);
object_index += 1;
};
make_hull (&[
Point3::from (BLOCK_BOTTOM_POSITION) + Vector3::from (BLOCK_BOTTOM_EXTENTS)
- vector3 (BLOCK_THICKNESS, BLOCK_THICKNESS, 0.0)
+ vector3 (0.0, 0.0, PIT_DEPTH),
obb_left.aabb().corner (Octant::PosPosPos),
obb_back.aabb().corner (Octant::PosPosPos),
obb_left.aabb().corner (Octant::PosPosPos) + (obb_back.aabb().corner (Octant::PosPosPos) -
(Point3::from (BLOCK_BOTTOM_POSITION) + Vector3::from (BLOCK_BOTTOM_EXTENTS)
- vector3 (BLOCK_THICKNESS, BLOCK_THICKNESS, 0.0)
+ vector3 (0.0, 0.0, PIT_DEPTH))),
obb_back.aabb().corner (Octant::PosPosPos),
obb_left.aabb().corner (Octant::PosPosNeg),
obb_back.aabb().corner (Octant::PosPosNeg)
]);
make_hull (&[
Point3::from (BLOCK_BOTTOM_POSITION) + Vector3::from (BLOCK_BOTTOM_EXTENTS)
- vector3 (0.0, 2.0 * BLOCK_BOTTOM_EXTENTS[1], 0.0)
- vector3 (BLOCK_THICKNESS, -BLOCK_THICKNESS, 0.0)
+ vector3 (0.0, 0.0, PIT_DEPTH),
obb_right.aabb().corner (Octant::PosNegPos),
obb_back.aabb().corner (Octant::PosNegPos),
obb_right.aabb().corner (Octant::PosNegPos) + (obb_back.aabb().corner (Octant::PosNegPos) -
(Point3::from (BLOCK_BOTTOM_POSITION) + Vector3::from (BLOCK_BOTTOM_EXTENTS)
- vector3 (0.0, 2.0 * BLOCK_BOTTOM_EXTENTS[1], 0.0)
- vector3 (BLOCK_THICKNESS, -BLOCK_THICKNESS, 0.0)
+ vector3 (0.0, 0.0, PIT_DEPTH))),
obb_back.aabb().corner (Octant::PosNegPos),
obb_right.aabb().corner (Octant::PosNegNeg),
obb_back.aabb().corner (Octant::PosNegNeg)
]);
make_hull (&[
Point3::from (BLOCK_BOTTOM_POSITION) - Vector3::from (BLOCK_BOTTOM_EXTENTS)
+ vector3 (BLOCK_THICKNESS, BLOCK_THICKNESS, 0.0)
+ vector3 (0.0, 0.0, PIT_DEPTH),
obb_right.aabb().corner (Octant::NegNegPos),
obb_front.aabb().corner (Octant::NegNegPos),
obb_right.aabb().corner (Octant::NegNegPos) + (obb_front.aabb().corner (Octant::NegNegPos) -
(Point3::from (BLOCK_BOTTOM_POSITION) - Vector3::from (BLOCK_BOTTOM_EXTENTS)
+ vector3 (BLOCK_THICKNESS, BLOCK_THICKNESS, 0.0)
+ vector3 (0.0, 0.0, PIT_DEPTH))),
obb_front.aabb().corner (Octant::NegNegPos),
obb_right.aabb().corner (Octant::NegNegNeg),
obb_front.aabb().corner (Octant::NegNegNeg)
]);
make_hull (&[
Point3::from (BLOCK_BOTTOM_POSITION) - Vector3::from (BLOCK_BOTTOM_EXTENTS)
+ vector3 (0.0, 2.0 * BLOCK_BOTTOM_EXTENTS[1], 0.0)
- vector3 (-BLOCK_THICKNESS, BLOCK_THICKNESS, 0.0)
+ vector3 (0.0, 0.0, PIT_DEPTH),
obb_left.aabb().corner (Octant::NegPosPos),
obb_front.aabb().corner (Octant::NegPosPos),
obb_left.aabb().corner (Octant::NegPosPos) + (obb_front.aabb().corner (Octant::NegPosPos) -
(Point3::from (BLOCK_BOTTOM_POSITION) - Vector3::from (BLOCK_BOTTOM_EXTENTS)
+ vector3 (0.0, 2.0 * BLOCK_BOTTOM_EXTENTS[1], 0.0)
- vector3 (-BLOCK_THICKNESS, BLOCK_THICKNESS, 0.0)
+ vector3 (0.0, 0.0, PIT_DEPTH))),
obb_front.aabb().corner (Octant::NegPosPos),
obb_left.aabb().corner (Octant::NegPosNeg),
obb_front.aabb().corner (Octant::NegPosNeg)
]);
let mut generate_random_ball = |object_index| {
use rand::RngExt;
let mut position = [
SPHERE_POSITION[0] + rng.random_range (-SPHERE_RANGE[0]..SPHERE_RANGE[0]),
SPHERE_POSITION[1] + rng.random_range (-SPHERE_RANGE[1]..SPHERE_RANGE[1]),
SPHERE_POSITION[2] + rng.random_range (-SPHERE_RANGE[2]..SPHERE_RANGE[2])
].into();
loop {
let sphere = {
let position = component::Position (position);
let mass = component::Mass::new (20.0);
let derivatives = component::Derivatives::zero();
let drag = component::Drag::zero();
let bound = shape::Sphere::noisy (SPHERE_RADIUS).into();
let material = component::Material {
friction: NonNegative::noisy (0.05),
.. component::MATERIAL_STONE
};
let collidable = true;
object::Dynamic {
position, mass, derivatives, drag, bound, material, collidable
}.into()
};
let mut result = system.handle_event (event::Input::CreateObject (
sphere, Some (object::Key::from (object_index))
));
if let event::Output::CreateObjectResult (
event::CreateObjectResult::Created (_)
) = result.pop().unwrap() {
debug_assert!(result.is_empty());
break
}
position.0.z += 2.0;
}
};
for i in 0..N_BALLS {
generate_random_ball (i);
}
let gravity = force::Gravity { acceleration: GRAVITY_ACCELERATION.into() };
assert!(system.handle_event (event::Input::SetGravity (gravity)).is_empty());
history.push (system.clone());
Simulation { system, history, hull_meshes }
}
pub fn new_hull_pit_test (rng : &mut XorShiftRng) -> Self {
use geometry::shape;
use self::hull_pit_test::*;
let mut system = System::<integrator::SemiImplicitEuler>::default();
let mut history = Vec::with_capacity (INITIAL_HISTORY_LENGTH);
let mut make_block = |object_index, position, extents| {
let block = {
let position = component::Position (position);
let bound = component::Bound::from (shape::Cuboid::noisy (extents));
let material = component::Material {
restitution: math::Normalized::new (0.4).unwrap(),
friction: math::NonNegative::new (0.01).unwrap()
};
let collidable = true;
object::Static { position, bound, material, collidable }.into()
};
let result = system.handle_event (event::Input::CreateObject (
block, Some (object::Key::from (object_index))
));
assert_create_object_success (result);
};
make_block (ObjectStatic::Bottom as u32, BLOCK_BOTTOM_POSITION.into(),
BLOCK_BOTTOM_EXTENTS);
make_block (ObjectStatic::Left as u32, BLOCK_LEFT_POSITION.into(),
BLOCK_LEFT_EXTENTS);
make_block (ObjectStatic::Right as u32, BLOCK_RIGHT_POSITION.into(),
BLOCK_RIGHT_EXTENTS);
make_block (ObjectStatic::Front as u32, BLOCK_FRONT_POSITION.into(),
BLOCK_FRONT_EXTENTS);
make_block (ObjectStatic::Back as u32, BLOCK_BACK_POSITION.into(),
BLOCK_BACK_EXTENTS);
let mut hull_meshes = vec![];
let mut generate_random_hull = |object_index| {
use rand::RngExt;
let mut position = [
SPHERE_POSITION[0] +
rng.random_range (-SPHERE_RANGE[0]..SPHERE_RANGE[0]),
SPHERE_POSITION[1] +
rng.random_range (-SPHERE_RANGE[1]..SPHERE_RANGE[1]),
SPHERE_POSITION[2] +
rng.random_range (-SPHERE_RANGE[2]..SPHERE_RANGE[2])
].into();
let aabb = geometry::Aabb3::with_minmax (
[-0.5, -0.5, -0.5].into(),
[ 0.5, 0.5, 0.5].into()
).unwrap();
let (hull, mesh) = {
let mut points = vec![];
let mut max_magnitude = 0.0;
for _ in 0..rng.random_range(HULL_MIN_POINTS..HULL_MAX_POINTS) {
let point = aabb.rand_point (rng);
let magnitude = point.0.magnitude();
if magnitude > max_magnitude {
max_magnitude = magnitude;
}
points.push (point);
}
for point in points.iter_mut() {
point.0 /= max_magnitude;
}
geometry::Hull3::from_points_with_mesh (&points).unwrap()
};
hull_meshes.push (mesh);
loop {
let object = {
let position = component::Position (position);
let mass = component::Mass::new (20.0);
let derivatives = component::Derivatives::zero();
let drag = component::Drag::zero();
let bound = component::Bound::from (hull.clone());
let material = component::MATERIAL_STONE;
let collidable = true;
object::Dynamic {
position, mass, derivatives, drag, bound, material, collidable
}.into()
};
let mut result = system.handle_event (event::Input::CreateObject (
object, Some (object::Key::from (object_index))
));
if let event::Output::CreateObjectResult (
event::CreateObjectResult::Created (_)
) = result.pop().unwrap() {
debug_assert!(result.is_empty());
break
}
position.0.z += 2.0;
}
};
for i in 0..N_HULLS {
generate_random_hull (i);
}
let gravity = force::Gravity { acceleration: GRAVITY_ACCELERATION.into() };
assert!(system.handle_event (event::Input::SetGravity (gravity)).is_empty());
history.push (system.clone());
Simulation { system, history, hull_meshes }
}
pub fn object_vertex_data (&self) -> (
Vec <vertex::Vert3dOrientationScaleColor>, // capsule vertex data
Vec <vertex::Vert3dOrientationScaleColor>, // cuboid (aabb) vertex data
Vec <vertex::Vert3dOrientationScaleColor>, // sphere vertex data
Vec <vertex::Vert3dColor> // hull vertex data
) {
use geometry::shape;
let capsule_vertex_data = {
let mut vertices : Vec <vertex::Vert3dOrientationScaleColor> =
self.system.objects_static().iter().filter_map (
|(_, object)| match &object.bound {
component::Bound (shape::Variant::Bounded (shape::Bounded::Capsule (_)))
=> Some (capsule_to_vertex (object, color::DEBUG_YELLOW)),
_ => None }
).collect();
let dynamic_capsules = self.system.objects_dynamic().iter().filter_map (
|(_, object)| match &object.bound {
component::Bound (shape::Variant::Bounded (shape::Bounded::Capsule (_)))
=> Some (capsule_to_vertex (object, color::DEBUG_GOLD)),
_ => None });
vertices.extend (dynamic_capsules);
vertices
};
let aabb_vertex_data = {
let mut vertices : Vec <vertex::Vert3dOrientationScaleColor> =
self.system.objects_static().iter().filter_map (
|(_, object)| match &object.bound {
component::Bound (shape::Variant::Bounded (shape::Bounded::Cuboid (_)))
=> Some (cuboid_to_vertex (object, color::DEBUG_PINK)),
_ => None }
).collect();
let dynamic_aabbs = self.system.objects_dynamic().iter().filter_map (
|(_, object)| match &object.bound {
component::Bound (shape::Variant::Bounded (shape::Bounded::Cuboid (_)))
=> Some (cuboid_to_vertex (object, color::DEBUG_RED)),
_ => None });
vertices.extend (dynamic_aabbs);
vertices
};
let sphere_vertex_data = {
let mut vertices : Vec <vertex::Vert3dOrientationScaleColor> =
self.system.objects_static().iter().filter_map (
|(_, object)| match &object.bound {
component::Bound (shape::Variant::Bounded (shape::Bounded::Sphere (_)))
=> Some (sphere_to_vertex (object, color::DEBUG_YELLOW)),
_ => None }
).collect();
let dynamic_spheres = self.system.objects_dynamic().iter().filter_map (
|(_, object)| match &object.bound {
component::Bound (shape::Variant::Bounded (shape::Bounded::Sphere (_)))
=> Some (sphere_to_vertex (object, color::DEBUG_GOLD)),
_ => None });
vertices.extend (dynamic_spheres);
vertices
};
let hull_vertex_data = hull_vertex_data (&self.system, &self.hull_meshes, &[]);
(capsule_vertex_data, aabb_vertex_data, sphere_vertex_data, hull_vertex_data)
}
}
fn step (
render_context : &mut Render <render::resource::Default>,
simulation : &mut Simulation
) {
let Simulation { system, history, hull_meshes } = simulation;
let output_events = system.handle_event (event::Input::Step);
log::info!("step [{}]", system.step()-1);
log::debug!("output events: {output_events:?}");
history.push (system.clone());
update_render_context (render_context, system, hull_meshes);
}
fn step_reverse (
render_context : &mut Render <render::resource::Default>,
simulation : &mut Simulation
) {
let Simulation { system, history, hull_meshes } = simulation;
if history.len() > 1 {
history.pop();
*system = history.last().unwrap().clone();
}
update_render_context (render_context, system, hull_meshes);
}
fn update_render_context (
render_context : &mut Render <render::resource::Default>,
system : &System <integrator::SemiImplicitEuler>,
hull_meshes : &[geometry::mesh::VertexEdgeTriangleMesh]
) {
use render::resource::draw3d::MeshId;
let contacts = system.contacts();
let mut dynamic_ids = vec![];
for (id_a, id_b, _) in contacts.into_iter().flatten() {
if id_a.kind == object::Kind::Dynamic {
dynamic_ids.push (id_a);
}
debug_assert_eq!(id_b.kind, object::Kind::Dynamic);
dynamic_ids.push (id_b);
}
let dynamic_object_color = |id| if dynamic_ids.contains (&id) {
color::CYAN
} else {
color::DEBUG_GOLD
};
let capsule_vertex_data : Vec <vertex::Vert3dOrientationScaleColor> =
system.objects_dynamic().iter().filter_map (|(i, object)|{
use geometry::shape;
let id = object::Id { kind: object::Kind::Dynamic, key: i.into() };
let color = dynamic_object_color (id);
match &object.bound {
component::Bound (shape::Variant::Bounded (shape::Bounded::Capsule (_)))
=> Some (capsule_to_vertex (object, color)),
_ => None }
}).collect();
let aabb_vertex_data : Vec <vertex::Vert3dOrientationScaleColor> =
system.objects_dynamic().iter().filter_map (|(i, object)|{
use geometry::shape;
let id = object::Id { kind: object::Kind::Dynamic, key: i.into() };
let color = dynamic_object_color (id);
match &object.bound {
component::Bound (shape::Variant::Bounded (shape::Bounded::Cuboid (_)))
=> Some (cuboid_to_vertex (object, color)),
_ => None }
}).collect();
let sphere_vertex_data : Vec <vertex::Vert3dOrientationScaleColor> =
system.objects_dynamic().iter().filter_map (|(i, object)|{
use geometry::shape;
let id = object::Id { kind: object::Kind::Dynamic, key: i.into() };
let color = dynamic_object_color (id);
match &object.bound {
component::Bound (shape::Variant::Bounded (shape::Bounded::Sphere (_)))
=> Some (sphere_to_vertex (object, color)),
_ => None }
}).collect();
if !capsule_vertex_data.is_empty() {
render_context.resource.draw3d.instance_vertices_mesh_write (
MeshId::Capsule as usize, capsule_vertex_data.as_slice());
}
if !aabb_vertex_data.is_empty() {
render_context.resource.draw3d
.instance_vertices_aabb_lines_write (aabb_vertex_data.as_slice());
}
if !sphere_vertex_data.is_empty() {
render_context.resource.draw3d.instance_vertices_mesh_write (
MeshId::Sphere as usize, sphere_vertex_data.as_slice());
}
let hull_vertex_data = hull_vertex_data (system, hull_meshes, &dynamic_ids);
if !hull_vertex_data.is_empty() {
render_context.resource.draw3d.user_buffers[0].slice (..hull_vertex_data.len())
.unwrap().write (&hull_vertex_data);
render_context.resource.draw3d.user_draw[0].range = 0..hull_vertex_data.len() as u32;
}
}
fn capsule_to_vertex <O : object::Bounded> (object : &O, color : [u8; 4])
-> vertex::Vert3dOrientationScaleColor
{
use geometry::shape;
let capsule = {
match object.bound() {
component::Bound (shape::Variant::Bounded (shape::Bounded::Capsule (capsule)))
=> capsule.capsule3 (object.position().0).numcast().unwrap(),
_ => panic!("capsule to vertex called on a non-capsule object: {object:?}")
}
};
vertex::Vert3dOrientationScaleColor {
color: color::rgba_u8_to_rgba_f32 (color),
.. util::capsule_to_vertex (capsule)
}
}
fn cuboid_to_vertex <O : object::Bounded> (object : &O, color : [u8; 4])
-> vertex::Vert3dOrientationScaleColor
{
use geometry::shape;
let aabb = {
match object.bound() {
component::Bound (shape::Variant::Bounded (shape::Bounded::Cuboid (cuboid)))
=> cuboid.aabb3 (object.position().0).numcast().unwrap(),
_ => panic!("cuboid to vertex called on a non-cuboid object: {object:?}")
}
};
vertex::Vert3dOrientationScaleColor {
color: color::rgba_u8_to_rgba_f32 (color),
.. util::aabb_to_vertex (aabb)
}
}
fn sphere_to_vertex <O : object::Bounded> (object : &O, color : [u8; 4])
-> vertex::Vert3dOrientationScaleColor
{
use geometry::shape;
let sphere = {
match object.bound() {
component::Bound (shape::Variant::Bounded (shape::Bounded::Sphere (sphere)))
=> sphere.sphere3 (object.position().0).numcast().unwrap(),
_ => panic!("capsule to vertex called on a non-capsule object: {object:?}")
}
};
vertex::Vert3dOrientationScaleColor {
color: color::rgba_u8_to_rgba_f32 (color),
.. util::sphere_to_vertex (sphere)
}
}
fn hull_vertex_data (
system : &System <integrator::SemiImplicitEuler>,
hull_meshes : &[geometry::mesh::VertexEdgeTriangleMesh],
dynamic_ids : &[object::Id]
) -> Vec <vertex::Vert3dColor> {
use geometry::shape;
let mut vertices : Vec <vertex::Vert3dColor> =
system.objects_static().iter().filter (|(_, object)| match object.bound {
component::Bound (shape::Variant::Bounded (shape::Bounded::Hull (_)))
=> true,
_ => false }).enumerate().flat_map (|(i, (_, object))|{
let mesh = &hull_meshes[i];
hull_to_vertices (object, mesh, color::DEBUG_PINK)
}).collect();
let n_static_vertices = vertices.len() / 2;
let dynamic_object_color = |id| if dynamic_ids.contains (&id) {
color::CYAN
} else {
color::DEBUG_GOLD
};
let dynamic_hulls = system.objects_dynamic().iter().filter (|(_, object)|
match object.bound {
component::Bound (shape::Variant::Bounded (shape::Bounded::Hull (_)))
=> true,
_ => false }
) .enumerate().flat_map (|(j, (i, object))| {
let id = object::Id { kind: object::Kind::Dynamic, key: i.into() };
let color = dynamic_object_color (id);
let mesh = &hull_meshes[n_static_vertices + j];
hull_to_vertices (object, mesh, color)
});
vertices.extend (dynamic_hulls);
vertices
}
fn hull_to_vertices <O : object::Bounded> (
object : &O,
mesh : &geometry::mesh::VertexEdgeTriangleMesh,
color : [u8; 4]
) -> Vec <vertex::Vert3dColor> {
use geometry::{shape, Primitive};
let mut hull : shape::Hull <f64> = object.bound().clone().try_into()
.expect ("capsule to vertex called on a non-capsule object");
hull.translate (object.position().0.0);
let color = color::rgba_u8_to_rgba_f32 (color);
let point_to_vertex = |point : math::Point3 <f64>| vertex::Vert3dColor {
position: point.0.numcast().unwrap().into_array(),
color
};
let mut lines = vec![];
for edge in mesh.edges().values() {
let edge = hull.edge (edge);
for point in edge.points() {
lines.push (point_to_vertex (point));
}
}
lines
}
fn assert_create_object_success (mut result : Vec <event::Output>) {
match result.pop().unwrap() {
event::Output::CreateObjectResult (event::CreateObjectResult::Created (_))
=> {}
_ => unreachable!()
}
assert!(result.pop().is_none());
}