1use rg3d_core::{
2 define_is_as,
3 math::{
4 vec3::Vec3,
5 self,
6 aabb::AxisAlignedBoundingBox
7 },
8 visitor::{Visit, VisitResult, Visitor, VisitError},
9};
10
11#[derive(Clone, Debug)]
12pub struct SphereShape {
13 pub radius: f32
14}
15
16pub trait CircumRadius {
17 fn circumradius(&self) -> f32;
18}
19
20impl Visit for SphereShape {
21 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
22 visitor.enter_region(name)?;
23
24 self.radius.visit("Radius", visitor)?;
25
26 visitor.leave_region()
27 }
28}
29
30impl CircumRadius for SphereShape {
31 fn circumradius(&self) -> f32 {
32 self.radius
33 }
34}
35
36impl Default for SphereShape {
37 fn default() -> Self {
38 Self {
39 radius: 0.5,
40 }
41 }
42}
43
44impl SphereShape {
45 pub fn new(radius: f32) -> Self {
46 Self {
47 radius
48 }
49 }
50
51 pub fn set_radius(&mut self, radius: f32) {
52 self.radius = radius;
53 }
54
55 pub fn get_radius(&self) -> f32 {
56 self.radius
57 }
58
59 pub fn get_farthest_point(&self, direction: Vec3) -> Vec3 {
60 let norm_dir = direction.normalized().unwrap_or_else(|| Vec3::new(1.0, 0.0, 0.0));
61 norm_dir.scale(self.radius)
62 }
63}
64
65#[derive(Clone, Debug)]
66pub struct TriangleShape {
67 pub vertices: [Vec3; 3]
68}
69
70impl CircumRadius for TriangleShape {
71 fn circumradius(&self) -> f32 {
72 AxisAlignedBoundingBox::from_points(&self.vertices).half_extents().max_value()
73 }
74}
75
76impl Visit for TriangleShape {
77 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
78 visitor.enter_region(name)?;
79
80 self.vertices[0].visit("A", visitor)?;
81 self.vertices[1].visit("B", visitor)?;
82 self.vertices[2].visit("C", visitor)?;
83
84 visitor.leave_region()
85 }
86}
87
88impl Default for TriangleShape {
89 fn default() -> Self {
90 Self {
91 vertices: [
92 Vec3::new(0.0, 0.0, 0.0),
93 Vec3::new(1.0, 0.0, 0.0),
94 Vec3::new(0.5, 1.0, 0.0)]
95 }
96 }
97}
98
99impl TriangleShape {
100 pub fn new(vertices: [Vec3; 3]) -> Self {
101 Self {
102 vertices
103 }
104 }
105
106 pub fn get_normal(&self) -> Option<Vec3> {
107 (self.vertices[2] - self.vertices[0]).cross(&(self.vertices[1] - self.vertices[0])).normalized()
108 }
109
110 pub fn get_farthest_point(&self, direction: Vec3) -> Vec3 {
111 math::get_farthest_point(&self.vertices, direction)
112 }
113}
114
115#[derive(Clone, Debug)]
116pub struct BoxShape {
117 half_extents: Vec3,
118}
119
120impl CircumRadius for BoxShape {
121 fn circumradius(&self) -> f32 {
122 self.half_extents.max_value()
123 }
124}
125
126impl Visit for BoxShape {
127 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
128 visitor.enter_region(name)?;
129
130 self.half_extents.visit("HalfExtents", visitor)?;
131
132 visitor.leave_region()
133 }
134}
135
136impl Default for BoxShape {
137 fn default() -> Self {
138 Self {
139 half_extents: Vec3::new(0.5, 0.5, 0.5)
140 }
141 }
142}
143
144impl BoxShape {
145 pub fn new(half_extents: Vec3) -> Self {
146 Self {
147 half_extents
148 }
149 }
150
151 pub fn get_farthest_point(&self, direction: Vec3) -> Vec3 {
152 Vec3 {
153 x: if direction.x >= 0.0 { self.half_extents.x } else { -self.half_extents.x },
154 y: if direction.y >= 0.0 { self.half_extents.y } else { -self.half_extents.y },
155 z: if direction.z >= 0.0 { self.half_extents.z } else { -self.half_extents.z },
156 }
157 }
158
159 pub fn get_min(&self) -> Vec3 {
160 Vec3 {
161 x: -self.half_extents.x,
162 y: -self.half_extents.y,
163 z: -self.half_extents.z,
164 }
165 }
166
167 pub fn get_max(&self) -> Vec3 {
168 Vec3 {
169 x: self.half_extents.x,
170 y: self.half_extents.y,
171 z: self.half_extents.z,
172 }
173 }
174}
175
176#[derive(Clone, Debug)]
177pub struct PointCloudShape {
178 points: Vec<Vec3>
179}
180
181impl CircumRadius for PointCloudShape {
182 fn circumradius(&self) -> f32 {
183 AxisAlignedBoundingBox::from_points(&self.points).half_extents().max_value()
185 }
186}
187
188impl Visit for PointCloudShape {
189 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
190 visitor.enter_region(name)?;
191
192 self.points.visit("Points", visitor)?;
193
194 visitor.leave_region()
195 }
196}
197
198impl Default for PointCloudShape {
199 fn default() -> Self {
200 Self {
201 points: Vec::new(),
202 }
203 }
204}
205
206impl PointCloudShape {
207 pub fn new(points: Vec<Vec3>) -> Self {
208 Self {
209 points
210 }
211 }
212
213 pub fn get_farthest_point(&self, direction: Vec3) -> Vec3 {
214 math::get_farthest_point(&self.points, direction)
215 }
216}
217
218#[derive(Copy, Clone, Debug)]
219pub enum Axis {
220 X = 0,
221 Y = 1,
222 Z = 2,
223}
224
225impl Visit for Axis {
226 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
227 let mut id = *self as u8;
228 id.visit(name, visitor)?;
229 if visitor.is_reading() {
230 *self = match id {
231 0 => Self::X,
232 1 => Self::Y,
233 2 => Self::Z,
234 _ => return Err(VisitError::User("Invalid axis".to_owned()))
235 }
236 }
237 Ok(())
238 }
239}
240
241#[derive(Clone, Debug)]
242pub struct CapsuleShape {
243 axis: Axis,
244 radius: f32,
245 height: f32,
246}
247
248impl CircumRadius for CapsuleShape {
249 fn circumradius(&self) -> f32 {
250 self.radius.max(self.height)
251 }
252}
253
254impl Default for CapsuleShape {
255 fn default() -> Self {
256 Self {
257 axis: Axis::X,
258 radius: 0.0,
259 height: 0.0,
260 }
261 }
262}
263
264impl Visit for CapsuleShape {
265 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
266 visitor.enter_region(name)?;
267
268 self.radius.visit("Radius", visitor)?;
269 self.axis.visit("Axis", visitor)?;
270 self.height.visit("Height", visitor)?;
271
272 visitor.leave_region()
273 }
274}
275
276impl CapsuleShape {
277 pub fn new(radius: f32, height: f32, axis: Axis) -> Self {
278 Self {
279 axis,
280 radius,
281 height
282 }
283 }
284
285 pub fn set_radius(&mut self, radius: f32) {
286 self.radius = radius.abs()
287 }
288
289 pub fn get_radius(&self) -> f32 {
290 self.radius
291 }
292
293 pub fn set_height(&mut self, height: f32) {
294 self.height = height.abs()
295 }
296
297 pub fn get_height(&self) -> f32 {
298 self.height
299 }
300
301 pub fn set_axis(&mut self, axis: Axis) {
302 self.axis = axis;
303 }
304
305 pub fn get_axis(&self) -> Axis {
306 self.axis
307 }
308
309 pub fn get_cap_centers(&self) -> (Vec3, Vec3) {
310 let half_height = self.height * 0.5;
311
312 match self.axis {
313 Axis::X => {
314 (Vec3::new(half_height, 0.0, 0.0),
315 Vec3::new(-half_height, 0.0, 0.0))
316 }
317 Axis::Y => {
318 (Vec3::new(0.0, half_height, 0.0),
319 Vec3::new(0.0, -half_height, 0.0))
320 }
321 Axis::Z => {
322 (Vec3::new(0.0, 0.0, half_height),
323 Vec3::new(0.0, 0.0, -half_height))
324 }
325 }
326 }
327
328 pub fn get_farthest_point(&self, direction: Vec3) -> Vec3 {
329 let norm_dir = direction.normalized().unwrap_or_else(|| Vec3::new(1.0, 0.0, 0.0));
330 let half_height = self.height * 0.5;
331
332 let positive_cap_position = match self.axis {
333 Axis::X => Vec3::new(half_height, 0.0, 0.0),
334 Axis::Y => Vec3::new(0.0, half_height, 0.0),
335 Axis::Z => Vec3::new(0.0, 0.0, half_height),
336 };
337
338 let mut max = -std::f32::MAX;
339 let mut farthest = Vec3::ZERO;
340 for cap_center in [positive_cap_position, -positive_cap_position].iter() {
341 let vertex = *cap_center + norm_dir.scale(self.radius);
342 let dot = norm_dir.dot(&vertex);
343 if dot > max {
344 max = dot;
345 farthest = vertex;
346 }
347 }
348
349 farthest
350 }
351}
352
353#[derive(Clone, Debug)]
354pub enum ConvexShape {
355 Dummy,
356 Box(BoxShape),
357 Sphere(SphereShape),
358 Capsule(CapsuleShape),
359 Triangle(TriangleShape),
360 PointCloud(PointCloudShape),
361}
362
363impl CircumRadius for ConvexShape {
364 fn circumradius(&self) -> f32 {
365 match self {
366 Self::Dummy => 0.0,
367 Self::Box(box_shape) => box_shape.circumradius(),
368 Self::Sphere(sphere) => sphere.circumradius(),
369 Self::Capsule(capsule) => capsule.circumradius(),
370 Self::Triangle(triangle) => triangle.circumradius(),
371 Self::PointCloud(point_cloud) => point_cloud.circumradius(),
372 }
373 }
374}
375
376impl ConvexShape {
377 pub fn get_farthest_point(&self, position: Vec3, direction: Vec3) -> Vec3 {
378 position + match self {
379 Self::Dummy => Vec3::ZERO,
380 Self::Box(box_shape) => box_shape.get_farthest_point(direction),
381 Self::Sphere(sphere) => sphere.get_farthest_point(direction),
382 Self::Capsule(capsule) => capsule.get_farthest_point(direction),
383 Self::Triangle(triangle) => triangle.get_farthest_point(direction),
384 Self::PointCloud(point_cloud) => point_cloud.get_farthest_point(direction),
385 }
386 }
387
388 pub fn id(&self) -> i32 {
389 match self {
390 Self::Dummy => 0,
391 Self::Box(_) => 1,
392 Self::Sphere(_) => 2,
393 Self::Capsule(_) => 3,
394 Self::Triangle(_) => 4,
395 Self::PointCloud(_) => 5,
396 }
397 }
398
399 pub fn new(id: i32) -> Result<Self, String> {
400 match id {
401 0 => Ok(Self::Dummy),
402 1 => Ok(Self::Box(Default::default())),
403 2 => Ok(Self::Sphere(Default::default())),
404 3 => Ok(Self::Capsule(Default::default())),
405 4 => Ok(Self::Triangle(Default::default())),
406 5 => Ok(Self::PointCloud(Default::default())),
407 _ => Err("Invalid shape id!".to_owned())
408 }
409 }
410
411 define_is_as!(ConvexShape : Box -> ref BoxShape => fn is_box, fn as_box, fn as_box_mut);
412 define_is_as!(ConvexShape : Capsule -> ref CapsuleShape => fn is_capsule, fn as_capsule, fn as_capsule_mut);
413 define_is_as!(ConvexShape : Sphere -> ref SphereShape => fn is_sphere, fn as_sphere, fn as_sphere_mut);
414 define_is_as!(ConvexShape : Triangle -> ref TriangleShape => fn is_triangle, fn as_triangle, fn as_triangle_mut);
415 define_is_as!(ConvexShape : PointCloud -> ref PointCloudShape => fn is_point_cloud, fn as_point_cloud, fn as_point_cloud_mut);
416}
417
418impl Visit for ConvexShape {
419 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
420 match self {
421 Self::Dummy => Ok(()),
422 Self::Box(box_shape) => box_shape.visit(name, visitor),
423 Self::Sphere(sphere) => sphere.visit(name, visitor),
424 Self::Capsule(capsule) => capsule.visit(name, visitor),
425 Self::Triangle(triangle) => triangle.visit(name, visitor),
426 Self::PointCloud(point_cloud) => point_cloud.visit(name, visitor),
427 }
428 }
429}
430
431