dae_parser/physics/
shape.rs

1use crate::*;
2
3/// Describes components of a [`RigidBody`].
4#[derive(Clone, Debug)]
5pub struct Shape {
6    /// If true, the mass is distributed along the surface of the shape.
7    pub hollow: Option<bool>,
8    /// The mass of the shape.
9    /// If not provided, it is derived from density x shape volume.
10    pub mass: Option<f32>,
11    /// The density of the shape.
12    /// If not provided, it is derived from mass/shape volume.
13    pub density: Option<f32>,
14    /// The [`PhysicsMaterial`] used for this shape.
15    pub physics_material: Option<Box<DefInstance<PhysicsMaterial>>>,
16    /// The geometry of the shape ([`Plane`], [`Sphere`], [`Mesh`], etc.).
17    pub geom: ShapeGeom,
18    /// Transformation for the shape. Any combination of these elements in any order.
19    /// See [`Node`] for additional information.
20    pub transform: Vec<RigidTransform>,
21    /// Provides arbitrary additional information about this element.
22    pub extra: Vec<Extra>,
23}
24
25impl Shape {
26    /// Construct a new `Shape`.
27    pub fn new(geom: impl Into<ShapeGeom>) -> Self {
28        Self {
29            hollow: Default::default(),
30            mass: Default::default(),
31            density: Default::default(),
32            physics_material: Default::default(),
33            geom: geom.into(),
34            transform: Default::default(),
35            extra: Default::default(),
36        }
37    }
38}
39
40impl XNode for Shape {
41    const NAME: &'static str = "shape";
42    fn parse(element: &Element) -> Result<Self> {
43        debug_assert_eq!(element.name(), Self::NAME);
44        let mut it = element.children().peekable();
45        Ok(Shape {
46            hollow: parse_opt("hollow", &mut it, parse_elem)?,
47            mass: parse_opt("mass", &mut it, parse_elem)?,
48            density: parse_opt("density", &mut it, parse_elem)?,
49            physics_material: parse_opt_many(&mut it, DefInstance::parse)?.map(Box::new),
50            transform: parse_list_many(&mut it, RigidTransform::parse)?,
51            geom: parse_one_many(&mut it, ShapeGeom::parse)?,
52            extra: Extra::parse_many(it)?,
53        })
54    }
55}
56
57impl XNodeWrite for Shape {
58    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
59        let e = Self::elem().start(w)?;
60        ElemBuilder::opt_print("hollow", &self.hollow, w)?;
61        ElemBuilder::opt_print("mass", &self.mass, w)?;
62        ElemBuilder::opt_print("density", &self.density, w)?;
63        self.physics_material.write_to(w)?;
64        self.geom.write_to(w)?;
65        self.transform.write_to(w)?;
66        self.extra.write_to(w)?;
67        e.end(w)
68    }
69}
70
71impl CollectLocalMaps for Shape {
72    fn collect_local_maps<'a>(&'a self, maps: &mut LocalMaps<'a>) {
73        self.physics_material.collect_local_maps(maps);
74    }
75}
76
77/// The geometry of a shape. This can be either an inline definition
78/// using [`Plane`], [`Sphere`] etc, or a [`Instance<Geometry>`],
79/// which can reference other geometry types.
80#[derive(Clone, Debug)]
81pub enum ShapeGeom {
82    /// Defines an infinite plane primitive.
83    Plane(Plane),
84    /// Declares an axis-aligned, centered box primitive.
85    Box(BoxShape),
86    /// Describes a centered sphere primitive.
87    Sphere(Sphere),
88    /// Declares a cylinder primitive that is centered on, and aligned with, the local y axis.
89    Cylinder(Cylinder),
90    /// Describes a tapered cylinder primitive that is centered on and aligned with the local y axis.
91    TaperedCylinder(TaperedCylinder),
92    /// Declares a capsule primitive that is centered on and aligned with the local y axis.
93    Capsule(Capsule),
94    /// Describes a tapered capsule primitive that is centered on, and aligned with, the local y axis.
95    TaperedCapsule(TaperedCapsule),
96    /// A geometry instance using the [`Instance<Geometry>`] element, which
97    /// references any [`GeometryElement`] ([`Mesh`] or [`Spline`]).
98    Geom(Instance<Geometry>),
99}
100
101impl From<Plane> for ShapeGeom {
102    fn from(v: Plane) -> Self {
103        Self::Plane(v)
104    }
105}
106
107impl From<BoxShape> for ShapeGeom {
108    fn from(v: BoxShape) -> Self {
109        Self::Box(v)
110    }
111}
112
113impl From<Sphere> for ShapeGeom {
114    fn from(v: Sphere) -> Self {
115        Self::Sphere(v)
116    }
117}
118
119impl From<Cylinder> for ShapeGeom {
120    fn from(v: Cylinder) -> Self {
121        Self::Cylinder(v)
122    }
123}
124
125impl From<TaperedCylinder> for ShapeGeom {
126    fn from(v: TaperedCylinder) -> Self {
127        Self::TaperedCylinder(v)
128    }
129}
130
131impl From<Capsule> for ShapeGeom {
132    fn from(v: Capsule) -> Self {
133        Self::Capsule(v)
134    }
135}
136
137impl From<TaperedCapsule> for ShapeGeom {
138    fn from(v: TaperedCapsule) -> Self {
139        Self::TaperedCapsule(v)
140    }
141}
142
143impl From<Instance<Geometry>> for ShapeGeom {
144    fn from(v: Instance<Geometry>) -> Self {
145        Self::Geom(v)
146    }
147}
148
149impl ShapeGeom {
150    /// Parse a [`ShapeGeom`] from an XML element.
151    pub fn parse(e: &Element) -> Result<Option<Self>> {
152        match e.name() {
153            Plane::NAME => Ok(Some(Self::Plane(Plane::parse(e)?))),
154            BoxShape::NAME => Ok(Some(Self::Box(BoxShape::parse(e)?))),
155            Sphere::NAME => Ok(Some(Self::Sphere(Sphere::parse(e)?))),
156            Cylinder::NAME => Ok(Some(Self::Cylinder(Cylinder::parse(e)?))),
157            TaperedCylinder::NAME => Ok(Some(Self::TaperedCylinder(TaperedCylinder::parse(e)?))),
158            Capsule::NAME => Ok(Some(Self::Capsule(Capsule::parse(e)?))),
159            TaperedCapsule::NAME => Ok(Some(Self::TaperedCapsule(TaperedCapsule::parse(e)?))),
160            Geometry::INSTANCE => Ok(Some(Self::Geom(Instance::parse(e)?))),
161            _ => Ok(None),
162        }
163    }
164}
165
166impl XNodeWrite for ShapeGeom {
167    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
168        match self {
169            Self::Plane(e) => e.write_to(w),
170            Self::Box(e) => e.write_to(w),
171            Self::Sphere(e) => e.write_to(w),
172            Self::Cylinder(e) => e.write_to(w),
173            Self::TaperedCylinder(e) => e.write_to(w),
174            Self::Capsule(e) => e.write_to(w),
175            Self::TaperedCapsule(e) => e.write_to(w),
176            Self::Geom(e) => e.write_to(w),
177        }
178    }
179}
180
181/// Defines an infinite plane primitive.
182#[derive(Clone, Debug)]
183pub struct Plane {
184    /// The coefficients for the plane’s equation: `Ax + By + Cz + D = 0`.
185    pub equation: [f32; 4],
186    /// Provides arbitrary additional information about this element.
187    pub extra: Vec<Extra>,
188}
189
190impl Plane {
191    /// Construct a new `Plane`.
192    pub fn new(equation: [f32; 4]) -> Self {
193        Self {
194            equation,
195            extra: vec![],
196        }
197    }
198}
199
200impl XNode for Plane {
201    const NAME: &'static str = "plane";
202    fn parse(element: &Element) -> Result<Self> {
203        debug_assert_eq!(element.name(), Self::NAME);
204        let mut it = element.children().peekable();
205        Ok(Plane {
206            equation: *parse_one("equation", &mut it, parse_array_n)?,
207            extra: Extra::parse_many(it)?,
208        })
209    }
210}
211
212impl XNodeWrite for Plane {
213    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
214        let e = Self::elem().start(w)?;
215        ElemBuilder::print_arr("equation", &self.equation, w)?;
216        self.extra.write_to(w)?;
217        e.end(w)
218    }
219}
220
221/// Declares an axis-aligned, centered box primitive.
222/// (Note: The type is not called `Box` to avoid the name clash with the Rust builtin type.)
223#[derive(Clone, Debug)]
224pub struct BoxShape {
225    /// The extents of the box.
226    pub half_extents: [f32; 3],
227    /// Provides arbitrary additional information about this element.
228    pub extra: Vec<Extra>,
229}
230
231impl BoxShape {
232    /// Construct a new `BoxShape`.
233    pub fn new(half_extents: [f32; 3]) -> Self {
234        Self {
235            half_extents,
236            extra: vec![],
237        }
238    }
239}
240
241impl XNode for BoxShape {
242    const NAME: &'static str = "box";
243    fn parse(element: &Element) -> Result<Self> {
244        debug_assert_eq!(element.name(), Self::NAME);
245        let mut it = element.children().peekable();
246        Ok(BoxShape {
247            half_extents: *parse_one("half_extents", &mut it, parse_array_n)?,
248            extra: Extra::parse_many(it)?,
249        })
250    }
251}
252
253impl XNodeWrite for BoxShape {
254    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
255        let e = Self::elem().start(w)?;
256        ElemBuilder::print_arr("half_extents", &self.half_extents, w)?;
257        self.extra.write_to(w)?;
258        e.end(w)
259    }
260}
261
262/// Describes a centered sphere primitive.
263#[derive(Clone, Debug)]
264pub struct Sphere {
265    /// The radius of the sphere.
266    pub radius: f32,
267    /// Provides arbitrary additional information about this element.
268    pub extra: Vec<Extra>,
269}
270
271impl Sphere {
272    /// Construct a new `Sphere`.
273    pub fn new(radius: f32) -> Self {
274        Self {
275            radius,
276            extra: vec![],
277        }
278    }
279}
280
281impl XNode for Sphere {
282    const NAME: &'static str = "sphere";
283    fn parse(element: &Element) -> Result<Self> {
284        debug_assert_eq!(element.name(), Self::NAME);
285        let mut it = element.children().peekable();
286        Ok(Sphere {
287            radius: parse_one("radius", &mut it, parse_elem)?,
288            extra: Extra::parse_many(it)?,
289        })
290    }
291}
292
293impl XNodeWrite for Sphere {
294    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
295        let e = Self::elem().start(w)?;
296        ElemBuilder::print("radius", &self.radius, w)?;
297        self.extra.write_to(w)?;
298        e.end(w)
299    }
300}
301
302/// Declares a cylinder primitive that is centered on, and aligned with, the local y axis.
303#[derive(Clone, Debug)]
304pub struct Cylinder {
305    /// The length of the cylinder along the y axis.
306    pub height: f32,
307    /// The radii of the cylinder (it may be elliptical).
308    pub radius: [f32; 2],
309    /// Provides arbitrary additional information about this element.
310    pub extra: Vec<Extra>,
311}
312
313impl Cylinder {
314    /// Construct a new `Cylinder`.
315    pub fn new(height: f32, radius: [f32; 2]) -> Self {
316        Self {
317            height,
318            radius,
319            extra: vec![],
320        }
321    }
322}
323
324impl XNode for Cylinder {
325    const NAME: &'static str = "cylinder";
326    fn parse(element: &Element) -> Result<Self> {
327        debug_assert_eq!(element.name(), Self::NAME);
328        let mut it = element.children().peekable();
329        Ok(Cylinder {
330            height: parse_one("height", &mut it, parse_elem)?,
331            radius: *parse_one("radius", &mut it, parse_array_n)?,
332            extra: Extra::parse_many(it)?,
333        })
334    }
335}
336
337impl XNodeWrite for Cylinder {
338    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
339        let e = Self::elem().start(w)?;
340        ElemBuilder::print("height", &self.height, w)?;
341        ElemBuilder::print_arr("radius", &self.radius, w)?;
342        self.extra.write_to(w)?;
343        e.end(w)
344    }
345}
346
347/// Describes a tapered cylinder primitive that is centered on and aligned with the local y axis.
348#[derive(Clone, Debug)]
349pub struct TaperedCylinder {
350    /// The length of the cylinder along the y axis.
351    pub height: f32,
352    /// The radii of the tapered cylinder at the positive (height/2) Y value.
353    /// Both ends of the tapered cylinder may be elliptical.
354    pub radius1: [f32; 2],
355    /// The radii of the tapered cylinder at the negative (height/2) Y value.
356    /// Both ends of the tapered cylinder may be elliptical.
357    pub radius2: [f32; 2],
358    /// Provides arbitrary additional information about this element.
359    pub extra: Vec<Extra>,
360}
361
362impl TaperedCylinder {
363    /// Construct a new `TaperedCylinder`.
364    pub fn new(height: f32, radius1: [f32; 2], radius2: [f32; 2]) -> Self {
365        Self {
366            height,
367            radius1,
368            radius2,
369            extra: vec![],
370        }
371    }
372}
373
374impl XNode for TaperedCylinder {
375    const NAME: &'static str = "tapered_cylinder";
376    fn parse(element: &Element) -> Result<Self> {
377        debug_assert_eq!(element.name(), Self::NAME);
378        let mut it = element.children().peekable();
379        Ok(TaperedCylinder {
380            height: parse_one("height", &mut it, parse_elem)?,
381            radius1: *parse_one("radius1", &mut it, parse_array_n)?,
382            radius2: *parse_one("radius2", &mut it, parse_array_n)?,
383            extra: Extra::parse_many(it)?,
384        })
385    }
386}
387
388impl XNodeWrite for TaperedCylinder {
389    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
390        let e = Self::elem().start(w)?;
391        ElemBuilder::print("height", &self.height, w)?;
392        ElemBuilder::print_arr("radius1", &self.radius1, w)?;
393        ElemBuilder::print_arr("radius2", &self.radius2, w)?;
394        self.extra.write_to(w)?;
395        e.end(w)
396    }
397}
398
399/// Declares a capsule primitive that is centered on and aligned with the local y axis.
400#[derive(Clone, Debug)]
401pub struct Capsule {
402    /// The length of the line segment connecting the centers of the capping hemispheres.
403    pub height: f32,
404    /// The radii of the capsule (it may be elliptical).
405    pub radius: [f32; 2],
406    /// Provides arbitrary additional information about this element.
407    pub extra: Vec<Extra>,
408}
409
410impl Capsule {
411    /// Construct a new `Capsule`.
412    pub fn new(height: f32, radius: [f32; 2]) -> Self {
413        Self {
414            height,
415            radius,
416            extra: vec![],
417        }
418    }
419}
420
421impl XNode for Capsule {
422    const NAME: &'static str = "capsule";
423    fn parse(element: &Element) -> Result<Self> {
424        debug_assert_eq!(element.name(), Self::NAME);
425        let mut it = element.children().peekable();
426        Ok(Capsule {
427            height: parse_one("height", &mut it, parse_elem)?,
428            radius: *parse_one("radius", &mut it, parse_array_n)?,
429            extra: Extra::parse_many(it)?,
430        })
431    }
432}
433
434impl XNodeWrite for Capsule {
435    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
436        let e = Self::elem().start(w)?;
437        ElemBuilder::print("height", &self.height, w)?;
438        ElemBuilder::print_arr("radius", &self.radius, w)?;
439        self.extra.write_to(w)?;
440        e.end(w)
441    }
442}
443
444/// Describes a tapered capsule primitive that is centered on, and aligned with, the local y axis.
445#[derive(Clone, Debug)]
446pub struct TaperedCapsule {
447    /// The length of the line segment connecting the centers of the capping hemispheres.
448    pub height: f32,
449    /// The radii of the tapered capsule at the positive (height/2) Y value.
450    /// Both ends of the tapered capsule may be elliptical.
451    pub radius1: [f32; 2],
452    /// The radii of the tapered capsule at the negative (height/2) Y value.
453    /// Both ends of the tapered capsule may be elliptical.
454    pub radius2: [f32; 2],
455    /// Provides arbitrary additional information about this element.
456    pub extra: Vec<Extra>,
457}
458
459impl TaperedCapsule {
460    /// Construct a new `TaperedCapsule`.
461    pub fn new(height: f32, radius1: [f32; 2], radius2: [f32; 2]) -> Self {
462        Self {
463            height,
464            radius1,
465            radius2,
466            extra: vec![],
467        }
468    }
469}
470
471impl XNode for TaperedCapsule {
472    const NAME: &'static str = "tapered_capsule";
473    fn parse(element: &Element) -> Result<Self> {
474        debug_assert_eq!(element.name(), Self::NAME);
475        let mut it = element.children().peekable();
476        Ok(TaperedCapsule {
477            height: parse_one("height", &mut it, parse_elem)?,
478            radius1: *parse_one("radius1", &mut it, parse_array_n)?,
479            radius2: *parse_one("radius2", &mut it, parse_array_n)?,
480            extra: Extra::parse_many(it)?,
481        })
482    }
483}
484
485impl XNodeWrite for TaperedCapsule {
486    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
487        let e = Self::elem().start(w)?;
488        ElemBuilder::print("height", &self.height, w)?;
489        ElemBuilder::print_arr("radius1", &self.radius1, w)?;
490        ElemBuilder::print_arr("radius2", &self.radius2, w)?;
491        self.extra.write_to(w)?;
492        e.end(w)
493    }
494}