use rapier2d::prelude::*; use rapier_testbed2d::Testbed; use lyon::math::Point; use lyon::path::PathEvent; use lyon::tessellation::geometry_builder::*; use lyon::tessellation::{self, FillOptions, FillTessellator}; use usvg::prelude::*; pub fn init_world(testbed: &mut Testbed) { /* * World */ let mut bodies = RigidBodySet::new(); let mut colliders = ColliderSet::new(); let impulse_joints = ImpulseJointSet::new(); let multibody_joints = MultibodyJointSet::new(); /* * Ground */ let ground_size = 25.0; let rigid_body = RigidBodyBuilder::fixed(); let handle = bodies.insert(rigid_body); let collider = ColliderBuilder::cuboid(ground_size, 1.2); colliders.insert_with_parent(collider, handle, &mut bodies); let rigid_body = RigidBodyBuilder::fixed() .rotation(std::f32::consts::FRAC_PI_2) .translation(vector![ground_size, ground_size]); let handle = bodies.insert(rigid_body); let collider = ColliderBuilder::cuboid(ground_size, 1.2); colliders.insert_with_parent(collider, handle, &mut bodies); let rigid_body = RigidBodyBuilder::fixed() .rotation(std::f32::consts::FRAC_PI_2) .translation(vector![-ground_size, ground_size]); let handle = bodies.insert(rigid_body); let collider = ColliderBuilder::cuboid(ground_size, 1.2); colliders.insert_with_parent(collider, handle, &mut bodies); /* * Create the trimeshes from a tesselated SVG. */ let mut fill_tess = FillTessellator::new(); let opt = usvg::Options::default(); let rtree = usvg::Tree::from_str(RAPIER_SVG_STR, &opt).unwrap(); let mut ith = 0; for node in rtree.root().descendants() { if let usvg::NodeKind::Path(ref p) = *node.borrow() { let transform = node.transform(); if p.fill.is_some() { let path = PathConvIter { iter: p.data.iter(), first: Point::new(0.0, 0.0), prev: Point::new(0.0, 0.0), deferred: None, needs_end: false, }; let mut mesh: VertexBuffers<_, u32> = VertexBuffers::new(); fill_tess .tessellate( path, &FillOptions::tolerance(0.01), &mut BuffersBuilder::new(&mut mesh, VertexCtor { prim_id: 0 }), ) .expect("Tesselation failed."); let angle = transform.get_rotate() as f32; let (sx, sy) = ( transform.get_scale().0 as f32 * 0.2, transform.get_scale().1 as f32 * 0.2, ); let indices: Vec<_> = mesh.indices.chunks(3).map(|v| [v[0], v[1], v[2]]).collect(); let vertices: Vec<_> = mesh .vertices .iter() .map(|v| point![v.position[0] * sx, v.position[1] * -sy]) .collect(); for k in 0..5 { let collider = ColliderBuilder::trimesh(vertices.clone(), indices.clone()); let rigid_body = RigidBodyBuilder::dynamic() .translation(vector![ith as f32 * 8.0 - 20.0, 20.0 + k as f32 * 11.0]) .rotation(angle); let handle = bodies.insert(rigid_body); colliders.insert_with_parent(collider, handle, &mut bodies); } ith += 1; } } } /* * Set up the testbed. */ testbed.set_world(bodies, colliders, impulse_joints, multibody_joints); testbed.look_at(point![0.0, 20.0], 17.0); } const RAPIER_SVG_STR: &'static str = r#" "#; pub struct PathConvIter<'a> { iter: std::slice::Iter<'a, usvg::PathSegment>, prev: Point, first: Point, needs_end: bool, deferred: Option, } impl<'l> Iterator for PathConvIter<'l> { type Item = PathEvent; fn next(&mut self) -> Option { if self.deferred.is_some() { return self.deferred.take(); } let next = self.iter.next(); match next { Some(usvg::PathSegment::MoveTo { x, y }) => { if self.needs_end { let last = self.prev; let first = self.first; self.needs_end = false; self.prev = Point::new(*x as f32, *y as f32); self.deferred = Some(PathEvent::Begin { at: self.prev }); self.first = self.prev; Some(PathEvent::End { last, first, close: false, }) } else { self.first = Point::new(*x as f32, *y as f32); Some(PathEvent::Begin { at: self.first }) } } Some(usvg::PathSegment::LineTo { x, y }) => { self.needs_end = true; let from = self.prev; self.prev = Point::new(*x as f32, *y as f32); Some(PathEvent::Line { from, to: self.prev, }) } Some(usvg::PathSegment::CurveTo { x1, y1, x2, y2, x, y, }) => { self.needs_end = true; let from = self.prev; self.prev = Point::new(*x as f32, *y as f32); Some(PathEvent::Cubic { from, ctrl1: Point::new(*x1 as f32, *y1 as f32), ctrl2: Point::new(*x2 as f32, *y2 as f32), to: self.prev, }) } Some(usvg::PathSegment::ClosePath) => { self.needs_end = false; self.prev = self.first; Some(PathEvent::End { last: self.prev, first: self.first, close: true, }) } None => { if self.needs_end { self.needs_end = false; let last = self.prev; let first = self.first; Some(PathEvent::End { last, first, close: false, }) } else { None } } } } } pub struct VertexCtor { pub prim_id: u32, } impl FillVertexConstructor for VertexCtor { fn new_vertex(&mut self, vertex: tessellation::FillVertex) -> GpuVertex { GpuVertex { position: vertex.position().to_array(), prim_id: self.prim_id, } } } impl StrokeVertexConstructor for VertexCtor { fn new_vertex(&mut self, vertex: tessellation::StrokeVertex) -> GpuVertex { GpuVertex { position: vertex.position().to_array(), prim_id: self.prim_id, } } } #[repr(C)] #[derive(Copy, Clone)] pub struct GpuVertex { pub position: [f32; 2], pub prim_id: u32, }