1#![allow(clippy::unnecessary_cast, clippy::useless_conversion)] use crate::testbed::TestbedStateFlags;
4use kiss3d::prelude::*;
5use rand_pcg::Pcg32;
6use rapier::dynamics::{RigidBodyHandle, RigidBodySet};
7use rapier::geometry::{ColliderHandle, ColliderSet, Shape, ShapeType};
8use std::collections::HashMap;
9
10#[cfg(feature = "dim2")]
11pub use kiss3d::prelude::SceneNode2d as SceneNode;
12#[cfg(feature = "dim3")]
13pub use kiss3d::prelude::SceneNode3d as SceneNode;
14
15#[derive(Hash, Eq, PartialEq, Clone, Debug)]
17pub enum ShapeTemplateType {
18 Ball,
19 Cuboid,
20 #[cfg(feature = "dim3")]
22 Cylinder,
23 #[cfg(feature = "dim3")]
24 Cone,
25}
26
27#[derive(Clone)]
29pub struct InstancedCollider {
30 pub collider: ColliderHandle,
31 pub body: Option<RigidBodyHandle>,
32 pub delta: rapier::math::Pose,
33 pub half_extents: rapier::math::Vector,
34 pub color: Color,
35 pub tmp_color: Option<Color>,
36}
37
38pub struct ShapeTemplate {
40 pub node: SceneNode,
41 pub colliders: Vec<InstancedCollider>,
42}
43
44pub struct IndividualNode {
46 pub node: SceneNode,
47 pub collider: ColliderHandle,
48 pub delta: rapier::math::Pose,
49 pub color: Color,
50 pub tmp_color: Option<Color>,
51}
52
53#[derive(Clone, Debug)]
55pub enum NodeLocation {
56 Instanced {
57 template: ShapeTemplateType,
58 index: usize,
59 },
60 Individual {
61 index: usize,
62 },
63}
64
65pub struct GraphicsManager {
66 scene: SceneNode,
67 rand: Pcg32,
68 templates: HashMap<ShapeTemplateType, ShapeTemplate>,
70 individual_nodes: Vec<IndividualNode>,
72 c2nodes: HashMap<ColliderHandle, NodeLocation>,
74 b2colliders: HashMap<RigidBodyHandle, Vec<ColliderHandle>>,
77 b2color: HashMap<RigidBodyHandle, Color>,
79 c2color: HashMap<ColliderHandle, Color>,
81 b2wireframe: HashMap<RigidBodyHandle, bool>,
83 ground_color: Color,
85 pub gfx_shift: rapier::math::Vector,
87 wireframe_mode: bool,
89}
90
91const GROUND_COLOR: Color = Color {
92 r: 0.58,
93 g: 0.54,
94 b: 0.50,
95 a: 1.0,
96};
97
98impl GraphicsManager {
99 pub fn new() -> GraphicsManager {
100 GraphicsManager {
101 scene: Default::default(),
102 rand: Pcg32::new(0, 1),
103 templates: HashMap::new(),
104 individual_nodes: Vec::new(),
105 c2nodes: HashMap::new(),
106 b2colliders: HashMap::new(),
107 b2color: HashMap::new(),
108 c2color: HashMap::new(),
109 b2wireframe: HashMap::new(),
110 ground_color: GROUND_COLOR,
111 gfx_shift: rapier::math::Vector::ZERO,
112 wireframe_mode: false,
113 }
114 }
115
116 pub fn clear(&mut self) {
117 self.scene = SceneNode::empty();
118 #[cfg(feature = "dim3")]
119 {
120 let mut light = self
122 .scene
123 .add_light(Light::directional(Vec3::new(-1.0, -1.0, -1.0)));
124 light.set_position(Vec3::new(100.0, 100.0, 100.0));
125
126 let mut light = self
127 .scene
128 .add_light(Light::point(10000.0).with_intensity(1.0));
129 light.set_position(Vec3::new(-100.0, 100.0, -100.0));
130 }
131
132 self.templates.clear();
133 self.individual_nodes.clear();
134 self.c2nodes.clear();
135 self.b2colliders.clear();
136 self.c2color.clear();
137 self.b2color.clear();
138 self.b2wireframe.clear();
139 self.rand = Pcg32::new(0, 1);
140 }
141
142 pub fn scene(&self) -> &SceneNode {
143 &self.scene
144 }
145
146 pub fn scene_mut(&mut self) -> &mut SceneNode {
147 &mut self.scene
148 }
149
150 #[cfg(feature = "dim3")]
152 fn get_or_create_template(&mut self, template_type: ShapeTemplateType) -> &mut ShapeTemplate {
153 self.templates
154 .entry(template_type.clone())
155 .or_insert_with(|| {
156 let node = match template_type {
158 ShapeTemplateType::Ball => self.scene.add_sphere_with_subdiv(1.0, 20, 20),
159 ShapeTemplateType::Cuboid => self.scene.add_cube(2.0, 2.0, 2.0),
160 ShapeTemplateType::Cylinder => self.scene.add_cylinder(1.0, 2.0),
162 ShapeTemplateType::Cone => self.scene.add_cone(1.0, 2.0),
163 };
164 ShapeTemplate {
165 node,
166 colliders: Vec::new(),
167 }
168 })
169 }
170
171 #[cfg(feature = "dim2")]
172 fn get_or_create_template(&mut self, template_type: ShapeTemplateType) -> &mut ShapeTemplate {
173 self.templates
174 .entry(template_type.clone())
175 .or_insert_with(|| {
176 let node = match template_type {
178 ShapeTemplateType::Ball => self.scene.add_circle(1.0),
179 ShapeTemplateType::Cuboid => self.scene.add_rectangle(2.0, 2.0),
180 };
185 ShapeTemplate {
186 node,
187 colliders: Vec::new(),
188 }
189 })
190 }
191
192 fn shape_template_type(shape: &dyn Shape) -> Option<ShapeTemplateType> {
194 match shape.shape_type() {
195 ShapeType::Ball => Some(ShapeTemplateType::Ball),
196 ShapeType::Cuboid | ShapeType::RoundCuboid => Some(ShapeTemplateType::Cuboid),
197 #[cfg(feature = "dim3")]
199 ShapeType::Cylinder | ShapeType::RoundCylinder => Some(ShapeTemplateType::Cylinder),
200 #[cfg(feature = "dim3")]
201 ShapeType::Cone | ShapeType::RoundCone => Some(ShapeTemplateType::Cone),
202 _ => None,
203 }
204 }
205
206 #[cfg(feature = "dim3")]
208 fn shape_half_extents(shape: &dyn Shape) -> rapier::math::Vector {
209 match shape.shape_type() {
210 ShapeType::Ball => {
211 let r = shape.as_ball().unwrap().radius;
212 rapier::math::Vector::new(r, r, r)
213 }
214 ShapeType::Cuboid => shape.as_cuboid().unwrap().half_extents,
215 ShapeType::RoundCuboid => shape.as_round_cuboid().unwrap().inner_shape.half_extents,
216 ShapeType::Capsule => {
217 let c = shape.as_capsule().unwrap();
218 rapier::math::Vector::new(c.radius, c.half_height() + c.radius, c.radius)
219 }
220 ShapeType::Cylinder => {
221 let c = shape.as_cylinder().unwrap();
222 rapier::math::Vector::new(c.radius, c.half_height, c.radius)
223 }
224 ShapeType::RoundCylinder => {
225 let c = &shape.as_round_cylinder().unwrap().inner_shape;
226 rapier::math::Vector::new(c.radius, c.half_height, c.radius)
227 }
228 ShapeType::Cone => {
229 let c = shape.as_cone().unwrap();
230 rapier::math::Vector::new(c.radius, c.half_height, c.radius)
231 }
232 ShapeType::RoundCone => {
233 let c = &shape.as_round_cone().unwrap().inner_shape;
234 rapier::math::Vector::new(c.radius, c.half_height, c.radius)
235 }
236 _ => rapier::math::Vector::new(1.0, 1.0, 1.0),
237 }
238 }
239
240 #[cfg(feature = "dim2")]
241 fn shape_half_extents(shape: &dyn Shape) -> rapier::math::Vector {
242 match shape.shape_type() {
243 ShapeType::Ball => {
244 let r = shape.as_ball().unwrap().radius;
245 rapier::math::Vector::new(r, r)
246 }
247 ShapeType::Cuboid => shape.as_cuboid().unwrap().half_extents,
248 ShapeType::RoundCuboid => shape.as_round_cuboid().unwrap().inner_shape.half_extents,
249 ShapeType::Capsule => {
250 let c = shape.as_capsule().unwrap();
251 rapier::math::Vector::new(c.radius, c.half_height() + c.radius)
252 }
253 _ => rapier::math::Vector::new(1.0, 1.0),
254 }
255 }
256
257 fn remove_node(&mut self, location: NodeLocation) {
258 match location {
259 NodeLocation::Instanced { template, index } => {
260 if let Some(tmpl) = self.templates.get_mut(&template) {
261 if index < tmpl.colliders.len() {
263 tmpl.colliders.swap_remove(index);
264 if index < tmpl.colliders.len() {
266 let swapped_collider = tmpl.colliders[index].collider;
267 self.c2nodes.insert(
268 swapped_collider,
269 NodeLocation::Instanced {
270 template: template.clone(),
271 index,
272 },
273 );
274 }
275 }
276 }
277 }
278 NodeLocation::Individual { index } => {
279 if index < self.individual_nodes.len() {
280 self.individual_nodes[index].node.detach();
281 self.individual_nodes.swap_remove(index);
282 if index < self.individual_nodes.len() {
284 let swapped_collider = self.individual_nodes[index].collider;
285 self.c2nodes
286 .insert(swapped_collider, NodeLocation::Individual { index });
287 }
288 }
289 }
290 }
291 }
292
293 pub fn remove_collider_nodes(&mut self, collider: ColliderHandle) {
294 if let Some(location) = self.c2nodes.remove(&collider) {
295 self.remove_node(location);
296 }
297 }
298
299 pub fn remove_body_nodes(&mut self, body: RigidBodyHandle) {
300 if let Some(colliders) = self.b2colliders.get(&body).cloned() {
301 for collider in colliders {
302 self.remove_collider_nodes(collider);
303 }
304 }
305 }
306
307 pub fn set_body_color(&mut self, b: RigidBodyHandle, color: Color, tmp_color: bool) {
308 if !tmp_color {
309 self.b2color.insert(b, color);
310 }
311
312 if let Some(colls) = self.b2colliders.get(&b) {
313 for co in colls.iter() {
314 match &self.c2nodes[co] {
315 NodeLocation::Individual { index } => {
316 if tmp_color {
317 self.individual_nodes[*index].tmp_color = Some(color);
318 } else {
319 self.individual_nodes[*index].color = color;
320 }
321 }
322 NodeLocation::Instanced { template, index } => {
323 let instances = &mut self.templates.get_mut(template).unwrap();
324 if tmp_color {
325 instances.colliders[*index].tmp_color = Some(color);
326 } else {
327 instances.colliders[*index].color = color;
328 }
329 }
330 }
331 }
332 }
333 }
334
335 pub fn set_initial_body_color(&mut self, b: RigidBodyHandle, color: Color) {
336 self.b2color.insert(b, color);
337 }
338
339 pub fn set_initial_collider_color(&mut self, c: ColliderHandle, color: Color) {
340 self.c2color.insert(c, color);
341 }
342
343 pub fn set_body_wireframe(&mut self, b: RigidBodyHandle, enabled: bool) {
344 self.b2wireframe.insert(b, enabled);
345 }
346
347 pub fn toggle_wireframe_mode(&mut self, _colliders: &ColliderSet, enabled: bool) {
348 self.wireframe_mode = enabled;
349
350 }
372
373 pub fn next_color(&mut self) -> Rgba<f32> {
374 Self::gen_color(&mut self.rand)
375 }
376
377 fn gen_color(rng: &mut Pcg32) -> Rgba<f32> {
378 use rand::Rng;
379 let [r, g, b]: [f32; 3] = rng.random();
380 [r, g, b, 1.0].into()
381 }
382
383 fn alloc_color(&mut self, handle: RigidBodyHandle, is_fixed: bool) -> Color {
384 let mut color = self.ground_color;
385
386 if !is_fixed {
387 match self.b2color.get(&handle).cloned() {
388 Some(c) => color = c,
389 None => color = Self::gen_color(&mut self.rand),
390 }
391 }
392
393 self.b2color.insert(handle, color);
394 color
395 }
396
397 pub fn add_body_colliders(
398 &mut self,
399 window: &mut Window,
400 handle: RigidBodyHandle,
401 bodies: &RigidBodySet,
402 colliders: &ColliderSet,
403 ) {
404 let body = bodies.get(handle).unwrap();
405
406 let color = self
407 .b2color
408 .get(&handle)
409 .cloned()
410 .unwrap_or_else(|| self.alloc_color(handle, !body.is_dynamic()));
411
412 self.add_body_colliders_with_color(window, handle, bodies, colliders, color);
413 }
414
415 pub fn add_body_colliders_with_color(
416 &mut self,
417 window: &mut Window,
418 handle: RigidBodyHandle,
419 bodies: &RigidBodySet,
420 colliders: &ColliderSet,
421 color: Color,
422 ) {
423 for collider_handle in bodies[handle].colliders() {
424 let color = self.c2color.get(collider_handle).copied().unwrap_or(color);
425 let collider = &colliders[*collider_handle];
426 self.add_shape(
427 window,
428 *collider_handle,
429 Some(handle),
430 collider.shape(),
431 collider.is_sensor(),
432 rapier::math::Pose::IDENTITY,
433 color,
434 );
435 }
436 }
437
438 pub fn add_collider(
439 &mut self,
440 window: &mut Window,
441 handle: ColliderHandle,
442 colliders: &ColliderSet,
443 ) {
444 let collider = &colliders[handle];
445 let collider_parent = collider.parent();
446 let color = collider_parent
447 .and_then(|p| self.b2color.get(&p).copied())
448 .unwrap_or(self.ground_color);
449 let color = self.c2color.get(&handle).copied().unwrap_or(color);
450 self.add_shape(
451 window,
452 handle,
453 collider_parent,
454 collider.shape(),
455 collider.is_sensor(),
456 rapier::math::Pose::IDENTITY,
457 color,
458 );
459 }
460
461 pub fn add_shape(
462 &mut self,
463 _window: &mut Window,
464 handle: ColliderHandle,
465 body: Option<RigidBodyHandle>,
466 shape: &dyn Shape,
467 sensor: bool,
468 delta: rapier::math::Pose,
469 color: Color,
470 ) {
471 if let Some(body) = body {
472 let colls = self.b2colliders.entry(body).or_default();
473 if !colls.contains(&handle) {
474 colls.push(handle);
475 }
476 }
477
478 if let Some(compound) = shape.as_compound() {
480 for (shape_pos, sub_shape) in compound.shapes() {
481 self.add_shape(
482 _window,
483 handle,
484 body,
485 &**sub_shape,
486 sensor,
487 *shape_pos * delta,
488 color,
489 );
490 }
491 return;
492 }
493
494 let opacity = if sensor { 0.5 } else { 1.0 };
495
496 if let Some(template_type) = Self::shape_template_type(shape) {
498 let half_extents = Self::shape_half_extents(shape);
499 let template = self.get_or_create_template(template_type.clone());
500 let index = template.colliders.len();
501 template.colliders.push(InstancedCollider {
502 collider: handle,
503 body,
504 delta,
505 half_extents,
506 color: color.with_alpha(opacity),
507 tmp_color: None,
508 });
509 self.c2nodes.insert(
510 handle,
511 NodeLocation::Instanced {
512 template: template_type,
513 index,
514 },
515 );
516 } else {
517 if let Some(node) = Self::create_individual_node(&mut self.scene, shape, color, sensor)
519 {
520 let index = self.individual_nodes.len();
521 self.individual_nodes.push(IndividualNode {
522 node,
523 collider: handle,
524 delta,
525 color: color.with_alpha(opacity),
526 tmp_color: None,
527 });
528 self.c2nodes
529 .insert(handle, NodeLocation::Individual { index });
530 }
531 }
532 }
533
534 #[cfg(feature = "dim3")]
535 fn create_individual_node(
536 scene: &mut SceneNode,
537 shape: &dyn Shape,
538 color: Color,
539 sensor: bool,
540 ) -> Option<SceneNode3d> {
541 use kiss3d::procedural::{IndexBuffer, RenderMesh};
542
543 fn to_render_mesh(trimesh: &rapier::geometry::TriMesh) -> RenderMesh {
544 let vtx = trimesh
545 .vertices()
546 .iter()
547 .map(|pt| Vec3::new(pt.x as f32, pt.y as f32, pt.z as f32))
548 .collect();
549 let idx = trimesh.indices().to_vec();
550 let mut mesh = RenderMesh::new(vtx, None, None, Some(IndexBuffer::Unified(idx)));
551 mesh.replicate_vertices();
552 mesh.recompute_normals();
553 mesh
554 }
555
556 let mut node = match shape.shape_type() {
557 ShapeType::TriMesh => {
558 let trimesh = shape.as_trimesh().unwrap();
559 Some(scene.add_render_mesh(to_render_mesh(trimesh), Vec3::ONE))
560 }
561 ShapeType::HeightField => {
562 let heightfield = shape.as_heightfield().unwrap();
563 let (vertices, indices) = heightfield.to_trimesh();
564 let trimesh = rapier::geometry::TriMesh::new(vertices, indices).unwrap();
565 Some(scene.add_render_mesh(to_render_mesh(&trimesh), Vec3::ONE))
566 }
567 ShapeType::ConvexPolyhedron | ShapeType::RoundConvexPolyhedron => {
568 let poly = shape
569 .as_convex_polyhedron()
570 .or_else(|| shape.as_round_convex_polyhedron().map(|rp| &rp.inner_shape));
571 poly.map(|p| {
572 let (vertices, indices) = p.to_trimesh();
573 let trimesh = rapier::geometry::TriMesh::new(vertices, indices).unwrap();
574 scene.add_render_mesh(to_render_mesh(&trimesh), Vec3::ONE)
575 })
576 }
577 ShapeType::Capsule => {
578 let caps = shape.as_capsule().unwrap();
579 let pose = caps.canonical_transform();
580 let mut parent = scene.add_group();
581 let mut node =
582 parent.add_capsule(caps.radius as f32, caps.half_height() as f32 * 2.0);
583 node.set_pose(pose.into());
584 Some(parent)
585 }
586 ShapeType::Triangle => {
587 let tri = shape.as_triangle().unwrap();
588 let vertices = vec![tri.a, tri.b, tri.c];
589 let indices = vec![[0u32, 1, 2], [0u32, 2, 1]];
590 let trimesh = rapier::geometry::TriMesh::new(vertices, indices).unwrap();
591 Some(scene.add_render_mesh(to_render_mesh(&trimesh), Vec3::ONE))
592 }
593 ShapeType::HalfSpace => {
594 let mut parent = scene.add_group();
595 parent.rotate(Quat::from_axis_angle(Vec3::X, -std::f32::consts::FRAC_PI_2));
596 let node = parent.add_quad(2000.0, 2000.0, 1, 1);
597 Some(node)
598 }
599 ShapeType::Voxels => {
600 let voxels = shape.as_voxels().unwrap();
601 let (vertices, indices) = voxels.to_trimesh();
602 let trimesh = rapier::geometry::TriMesh::new(vertices, indices).unwrap();
603 Some(scene.add_render_mesh(to_render_mesh(&trimesh), Vec3::ONE))
604 }
605 _ => None,
606 };
607
608 if let Some(ref mut n) = node {
609 n.set_color(color);
610 if sensor {
611 n.set_surface_rendering_activation(false);
612 n.set_lines_width(1.0, false);
613 }
614 }
615
616 node
617 }
618
619 #[cfg(feature = "dim2")]
620 fn create_individual_node(
621 scene: &mut SceneNode,
622 shape: &dyn Shape,
623 color: Color,
624 _sensor: bool,
625 ) -> Option<SceneNode2d> {
626 let mut node = match shape.shape_type() {
627 ShapeType::Triangle => {
628 let tri = shape.as_triangle().unwrap();
629 let vertices: Vec<Vec2> = vec![
630 Vec2::new(tri.a.x as f32, tri.a.y as f32),
631 Vec2::new(tri.b.x as f32, tri.b.y as f32),
632 Vec2::new(tri.c.x as f32, tri.c.y as f32),
633 ];
634 Some(scene.add_convex_polygon(vertices, Vec2::ONE))
635 }
636 ShapeType::TriMesh => {
637 let trimesh = shape.as_trimesh().unwrap();
638 let vertices: Vec<Vec2> = trimesh
639 .vertices()
640 .iter()
641 .map(|pt| Vec2::new(pt.x as f32, pt.y as f32))
642 .collect();
643 let render_mesh = GpuMesh2d::new(vertices, trimesh.indices().to_vec(), None, false);
644 Some(scene.add_mesh(Rc::new(RefCell::new(render_mesh)), Vec2::ONE))
645 }
646 ShapeType::ConvexPolygon | ShapeType::RoundConvexPolygon => {
647 let poly = shape
648 .as_convex_polygon()
649 .or_else(|| shape.as_round_convex_polygon().map(|rp| &rp.inner_shape));
650 poly.map(|p| {
651 let vertices: Vec<Vec2> = p
652 .points()
653 .iter()
654 .map(|pt| Vec2::new(pt.x as f32, pt.y as f32))
655 .collect();
656 scene.add_convex_polygon(vertices, Vec2::ONE)
657 })
658 }
659 ShapeType::Capsule => {
660 let caps = shape.as_capsule().unwrap();
661 let pose = caps.canonical_transform();
662 let mut parent = scene.add_group();
663 let mut node =
664 parent.add_capsule(caps.radius as f32, caps.half_height() as f32 * 2.0);
665 node.set_pose(pose.into());
666 Some(parent)
667 }
668 ShapeType::Segment => {
669 let seg = shape.as_segment().unwrap();
671 let vertices = vec![
672 Vec2::new(seg.a.x as f32, seg.a.y as f32),
673 Vec2::new(seg.b.x as f32, seg.b.y as f32),
674 ];
675 Some(scene.add_polyline(vertices, None, 0.1))
676 }
677 ShapeType::Polyline => {
678 let polyline = shape.as_polyline().unwrap();
680 let vertices: Vec<Vec2> = polyline
681 .vertices()
682 .iter()
683 .map(|pt| Vec2::new(pt.x as f32, pt.y as f32))
684 .collect();
685 Some(
686 scene
687 .add_polyline(vertices, Some(polyline.indices().to_vec()), 0.2)
688 .set_lines_color(Some(GROUND_COLOR)),
689 )
690 }
691 ShapeType::HeightField => {
692 let hf = shape.as_heightfield().unwrap();
693 let (polyline_verts, indices) = hf.to_polyline();
694 let vertices: Vec<Vec2> = polyline_verts
695 .iter()
696 .map(|pt| Vec2::new(pt.x as f32, pt.y as f32))
697 .collect();
698 Some(
699 scene
700 .add_polyline(vertices, Some(indices), 0.2)
701 .set_lines_color(Some(GROUND_COLOR)),
702 )
703 }
704 ShapeType::Voxels => {
705 let voxels = shape.as_voxels().unwrap();
706
707 let mut vtx = vec![];
708 let mut idx = vec![];
709 let sz = voxels.voxel_size() / 2.0;
710 for vox in voxels.voxels() {
711 if !vox.state.is_empty() {
712 let bid = vtx.len() as u32;
713 let center = Vec2::new(vox.center.x as f32, vox.center.y as f32);
714 vtx.push(center + Vec2::new(sz.x as f32, sz.y as f32));
715 vtx.push(center + Vec2::new(-sz.x as f32, sz.y as f32));
716 vtx.push(center + Vec2::new(-sz.x as f32, -sz.y as f32));
717 vtx.push(center + Vec2::new(sz.x as f32, -sz.y as f32));
718 idx.push([bid, bid + 1, bid + 2]);
719 idx.push([bid + 2, bid + 3, bid]);
720 }
721 }
722
723 let mesh = GpuMesh2d::new(vtx, idx, None, false);
724 Some(scene.add_mesh(Rc::new(RefCell::new(mesh)), Vec2::ONE))
725 }
726 _ => None,
727 };
728
729 if let Some(ref mut n) = node {
730 n.set_color(color);
731 }
732
733 node
734 }
735
736 #[cfg(feature = "dim3")]
737 pub fn draw(
738 &mut self,
739 flags: TestbedStateFlags,
740 _bodies: &RigidBodySet,
741 colliders: &ColliderSet,
742 ) {
743 let draw_surfaces = flags.contains(TestbedStateFlags::DRAW_SURFACES);
744
745 for (template_type, template) in &mut self.templates {
747 template
748 .node
749 .set_visible(draw_surfaces && !template.colliders.is_empty());
750
751 if template.colliders.is_empty() {
752 continue;
753 }
754
755 let instances: Vec<InstanceData3d> = template
756 .colliders
757 .iter_mut()
758 .filter_map(|ic| {
759 let co = colliders.get(ic.collider)?;
760 let co_pos = *co.position() * ic.delta;
761
762 let bcolor = ic
764 .body
765 .and_then(|b| self.b2color.get(&b).copied())
766 .unwrap_or(ic.color);
767 let ccolor = self
768 .c2color
769 .get(&ic.collider)
770 .copied()
771 .unwrap_or(bcolor)
772 .with_alpha(ic.color.a);
773 let color = ic.tmp_color.take().unwrap_or(ccolor);
774
775 let pos = Vec3::new(
777 (co_pos.translation.x + self.gfx_shift.x) as f32,
778 (co_pos.translation.y + self.gfx_shift.y) as f32,
779 (co_pos.translation.z + self.gfx_shift.z) as f32,
780 );
781
782 let rot = Quat::from_xyzw(
784 co_pos.rotation.x as f32,
785 co_pos.rotation.y as f32,
786 co_pos.rotation.z as f32,
787 co_pos.rotation.w as f32,
788 );
789 let rot_mat = Mat3::from_quat(rot);
790
791 let scale = match template_type {
793 ShapeTemplateType::Ball => {
794 Vec3::splat(ic.half_extents.x as f32)
796 }
797 ShapeTemplateType::Cuboid => {
798 Vec3::new(
800 ic.half_extents.x as f32,
801 ic.half_extents.y as f32,
802 ic.half_extents.z as f32,
803 )
804 }
805 ShapeTemplateType::Cylinder => {
813 Vec3::new(
815 ic.half_extents.x as f32,
816 ic.half_extents.y as f32,
817 ic.half_extents.z as f32,
818 )
819 }
820 ShapeTemplateType::Cone => {
821 Vec3::new(
823 ic.half_extents.x as f32,
824 ic.half_extents.y as f32,
825 ic.half_extents.z as f32,
826 )
827 }
828 };
829
830 let scale_mat = Mat3::from_diagonal(scale);
831 let deformation = rot_mat * scale_mat;
832
833 Some(InstanceData3d {
834 position: pos,
835 deformation,
836 color,
837 lines_color: None,
838 lines_width: None,
839 points_color: None,
840 points_size: None,
841 })
842 })
843 .collect();
844
845 template.node.set_instances(&instances);
846 }
847
848 for node in &mut self.individual_nodes {
850 node.node.set_visible(draw_surfaces);
851
852 if let Some(co) = colliders.get(node.collider) {
853 let co_pos = *co.position() * node.delta;
854 node.node
855 .set_pose(co_pos.append_translation(self.gfx_shift).into());
856 node.node
857 .set_color(node.tmp_color.take().unwrap_or(node.color));
858 }
859 }
860 }
861
862 #[cfg(feature = "dim2")]
863 pub fn draw(
864 &mut self,
865 flags: TestbedStateFlags,
866 _bodies: &RigidBodySet,
867 colliders: &ColliderSet,
868 ) {
869 let draw_surfaces = flags.contains(TestbedStateFlags::DRAW_SURFACES);
870
871 for (template_type, template) in &mut self.templates {
873 template
874 .node
875 .set_visible(draw_surfaces && !template.colliders.is_empty());
876
877 if template.colliders.is_empty() {
878 continue;
879 }
880
881 let instances: Vec<InstanceData2d> = template
882 .colliders
883 .iter_mut()
884 .filter_map(|ic| {
885 let co = colliders.get(ic.collider)?;
886 let co_pos = *co.position() * ic.delta;
887
888 let bcolor = ic
890 .body
891 .and_then(|b| self.b2color.get(&b).copied())
892 .unwrap_or(ic.color);
893 let ccolor = self
894 .c2color
895 .get(&ic.collider)
896 .copied()
897 .unwrap_or(bcolor)
898 .with_alpha(ic.color.a);
899 let color = ic.tmp_color.take().unwrap_or(ccolor);
900
901 let pos = co_pos.translation + self.gfx_shift;
903 let pos = Vec2::new(pos.x as f32, pos.y as f32);
904
905 let rot_mat = co_pos.rotation.to_mat();
907 let rot_mat = Mat2::from_cols_array(&[
908 rot_mat.x_axis.x as f32,
909 rot_mat.x_axis.y as f32,
910 rot_mat.y_axis.x as f32,
911 rot_mat.y_axis.y as f32,
912 ]);
913
914 let scale = match template_type {
916 ShapeTemplateType::Ball => {
917 Vec2::splat(ic.half_extents.x as f32)
919 }
920 ShapeTemplateType::Cuboid => {
921 Vec2::new(ic.half_extents.x as f32, ic.half_extents.y as f32)
923 }
924 };
925
926 let scale_mat = Mat2::from_diagonal(scale);
927 let deformation = rot_mat * scale_mat;
928
929 Some(InstanceData2d {
930 position: pos,
931 deformation,
932 color: color.into(),
933 lines_color: None,
934 lines_width: None,
935 points_color: None,
936 points_size: None,
937 })
938 })
939 .collect();
940
941 template.node.set_instances(&instances);
942 }
943
944 for node in &mut self.individual_nodes {
946 node.node.set_visible(draw_surfaces);
947
948 if let Some(co) = colliders.get(node.collider) {
949 let co_pos = *co.position() * node.delta;
950 node.node.set_position(Vec2::new(
951 (co_pos.translation.x + self.gfx_shift.x) as f32,
952 (co_pos.translation.y + self.gfx_shift.y) as f32,
953 ));
954 node.node.set_rotation(co_pos.rotation.angle() as f32);
955 node.node
956 .set_color(node.tmp_color.take().unwrap_or(node.color));
957 }
958 }
959 }
960}
961
962impl Default for GraphicsManager {
963 fn default() -> Self {
964 Self::new()
965 }
966}