|
1 |
| -use bevy_math::{Quat, Vec3}; |
| 1 | +use bevy_math::*; |
| 2 | +use bevy_reflect::Reflect; |
| 3 | +//use bevy_transform::components::GlobalTransform; |
| 4 | +use std::error::Error; |
| 5 | +use std::fmt; |
2 | 6 |
|
3 |
| -pub trait Primitive3d {} |
| 7 | +pub trait Primitive3d { |
| 8 | + /* |
| 9 | + /// Returns true if this primitive is on the outside (normal direction) of the supplied |
| 10 | + fn outside_plane( |
| 11 | + &self, |
| 12 | + primitive_transform: GlobalTransform, |
| 13 | + plane: Plane, |
| 14 | + plane_transform: GlobalTransform, |
| 15 | + ) -> bool;*/ |
| 16 | +} |
4 | 17 |
|
5 |
| -#[derive(Copy, Clone, PartialEq, Debug)] |
| 18 | +#[derive(Debug, Clone)] |
| 19 | +pub enum PrimitiveError { |
| 20 | + MinGreaterThanMax, |
| 21 | + NonPositiveExtents, |
| 22 | +} |
| 23 | +impl Error for PrimitiveError {} |
| 24 | +impl fmt::Display for PrimitiveError { |
| 25 | + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 26 | + match self { |
| 27 | + PrimitiveError::MinGreaterThanMax => { |
| 28 | + write!(f, "AxisAlignedBox minimums must be smaller than maximums") |
| 29 | + } |
| 30 | + PrimitiveError::NonPositiveExtents => { |
| 31 | + write!(f, "AxisAlignedBox extents must be greater than zero") |
| 32 | + } |
| 33 | + } |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +#[derive(Copy, Clone, PartialEq, Debug, Reflect)] |
6 | 38 | pub struct Sphere {
|
7 | 39 | pub origin: Vec3,
|
8 | 40 | pub radius: f32,
|
9 | 41 | }
|
| 42 | +impl Primitive3d for Sphere {} |
10 | 43 |
|
11 |
| -#[derive(Copy, Clone, PartialEq, Debug)] |
12 |
| -pub struct Box { |
13 |
| - pub maximums: Vec3, |
14 |
| - pub minimums: Vec3, |
| 44 | +/// An oriented box, unlike an axis aligned box, can be rotated and is not constrained to match the |
| 45 | +/// orientation of the coordinate system it is defined in. Internally, this is represented as an |
| 46 | +/// axis aligned box with some rotation ([Quat]) applied. |
| 47 | +#[derive(Copy, Clone, PartialEq, Debug, Reflect)] |
| 48 | +pub struct OrientedBox { |
| 49 | + pub aab: AxisAlignedBox, |
15 | 50 | pub orientation: Quat,
|
16 | 51 | }
|
| 52 | +impl Primitive3d for OrientedBox {} |
17 | 53 |
|
18 |
| -#[derive(Copy, Clone, PartialEq, Debug)] |
| 54 | +/// An axis aligned box is a box whose axes lie in the x/y/z directions of the coordinate system |
| 55 | +/// the box is defined in. |
| 56 | +#[derive(Copy, Clone, PartialEq, Debug, Reflect)] |
19 | 57 | pub struct AxisAlignedBox {
|
20 |
| - pub maximums: Vec3, |
21 |
| - pub minimums: Vec3, |
| 58 | + minimums: Vec3, |
| 59 | + maximums: Vec3, |
22 | 60 | }
|
| 61 | +impl Primitive3d for AxisAlignedBox {} |
| 62 | +impl AxisAlignedBox { |
| 63 | + pub fn from_min_max(minimums: Vec3, maximums: Vec3) -> Result<AxisAlignedBox, PrimitiveError> { |
| 64 | + if (maximums - minimums).min_element() > 0.0 { |
| 65 | + Ok(AxisAlignedBox { minimums, maximums }) |
| 66 | + } else { |
| 67 | + Err(PrimitiveError::MinGreaterThanMax) |
| 68 | + } |
| 69 | + } |
| 70 | + pub fn from_extents_origin( |
| 71 | + extents: Vec3, |
| 72 | + origin: Vec3, |
| 73 | + ) -> Result<AxisAlignedBox, PrimitiveError> { |
| 74 | + if extents.min_element() > 0.0 { |
| 75 | + Ok(AxisAlignedBox { |
| 76 | + minimums: origin, |
| 77 | + maximums: extents + origin, |
| 78 | + }) |
| 79 | + } else { |
| 80 | + Err(PrimitiveError::NonPositiveExtents) |
| 81 | + } |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +/// A frustum is a truncated pyramid that is used to represent the "volume" of world space that is |
| 86 | +/// visible to the camera. |
| 87 | +#[derive(Copy, Clone, PartialEq, Debug, Reflect)] |
| 88 | +#[reflect_value(PartialEq)] |
| 89 | +pub struct Frustum { |
| 90 | + planes: [Plane; 6], |
| 91 | +} |
| 92 | +impl Primitive3d for Frustum {} |
| 93 | +impl Frustum { |
| 94 | + pub fn from_camera_properties( |
| 95 | + &self, |
| 96 | + camera_position: Mat4, |
| 97 | + projection_matrix: Mat4, |
| 98 | + ) -> Frustum { |
| 99 | + let ndc_to_world: Mat4 = camera_position * projection_matrix.inverse(); |
| 100 | + // Near/Far, Top/Bottom, Left/Right |
| 101 | + let nbl_world = ndc_to_world.project_point3(Vec3::new(-1.0, -1.0, -1.0)); |
| 102 | + let nbr_world = ndc_to_world.project_point3(Vec3::new(1.0, -1.0, -1.0)); |
| 103 | + let ntl_world = ndc_to_world.project_point3(Vec3::new(-1.0, 1.0, -1.0)); |
| 104 | + let fbl_world = ndc_to_world.project_point3(Vec3::new(-1.0, -1.0, 1.0)); |
| 105 | + let ftr_world = ndc_to_world.project_point3(Vec3::new(1.0, 1.0, 1.0)); |
| 106 | + let ftl_world = ndc_to_world.project_point3(Vec3::new(-1.0, 1.0, 1.0)); |
| 107 | + let fbr_world = ndc_to_world.project_point3(Vec3::new(1.0, -1.0, 1.0)); |
| 108 | + let ntr_world = ndc_to_world.project_point3(Vec3::new(1.0, 1.0, -1.0)); |
| 109 | + |
| 110 | + let near_normal = (nbr_world - nbl_world) |
| 111 | + .cross(ntl_world - nbl_world) |
| 112 | + .normalize(); |
| 113 | + let far_normal = (fbr_world - ftr_world) |
| 114 | + .cross(ftl_world - ftr_world) |
| 115 | + .normalize(); |
| 116 | + let top_normal = (ftl_world - ftr_world) |
| 117 | + .cross(ntr_world - ftr_world) |
| 118 | + .normalize(); |
| 119 | + let bottom_normal = (fbl_world - nbl_world) |
| 120 | + .cross(nbr_world - nbl_world) |
| 121 | + .normalize(); |
| 122 | + let right_normal = (ntr_world - ftr_world) |
| 123 | + .cross(fbr_world - ftr_world) |
| 124 | + .normalize(); |
| 125 | + let left_normal = (ntl_world - nbl_world) |
| 126 | + .cross(fbl_world - nbl_world) |
| 127 | + .normalize(); |
23 | 128 |
|
| 129 | + let left = Plane { |
| 130 | + point: nbl_world, |
| 131 | + normal: left_normal, |
| 132 | + }; |
| 133 | + let right = Plane { |
| 134 | + point: ftr_world, |
| 135 | + normal: right_normal, |
| 136 | + }; |
| 137 | + let bottom = Plane { |
| 138 | + point: nbl_world, |
| 139 | + normal: bottom_normal, |
| 140 | + }; |
| 141 | + let top = Plane { |
| 142 | + point: ftr_world, |
| 143 | + normal: top_normal, |
| 144 | + }; |
| 145 | + let near = Plane { |
| 146 | + point: nbl_world, |
| 147 | + normal: near_normal, |
| 148 | + }; |
| 149 | + let far = Plane { |
| 150 | + point: ftr_world, |
| 151 | + normal: far_normal, |
| 152 | + }; |
| 153 | + Frustum { |
| 154 | + planes: [left, right, top, bottom, near, far], |
| 155 | + } |
| 156 | + } |
| 157 | +} |
| 158 | + |
| 159 | +/// A plane is defined by a point in space and a normal vector at that point. |
24 | 160 | #[derive(Copy, Clone, PartialEq, Debug)]
|
25 | 161 | pub struct Plane {
|
26 |
| - pub point: Vec3, |
27 |
| - pub normal: Vec3, |
| 162 | + point: Vec3, |
| 163 | + normal: Vec3, |
| 164 | +} |
| 165 | +impl Primitive3d for Plane {} |
| 166 | +impl Plane { |
| 167 | + /// Generate a plane from three points that lie on the plane. |
| 168 | + pub fn from_points(points: [Vec3; 3]) -> Plane { |
| 169 | + let point = points[1]; |
| 170 | + let arm_1 = points[0] - point; |
| 171 | + let arm_2 = points[2] - point; |
| 172 | + let normal = arm_1.cross(arm_2).normalize(); |
| 173 | + Plane { point, normal } |
| 174 | + } |
| 175 | + /// Generate a plane from a point on that plane and the normal direction of the plane. The |
| 176 | + /// normal vector does not need to be normalized (length can be != 1). |
| 177 | + pub fn from_point_normal(point: Vec3, normal: Vec3) -> Plane { |
| 178 | + Plane { |
| 179 | + point, |
| 180 | + normal: normal.normalize(), |
| 181 | + } |
| 182 | + } |
28 | 183 | }
|
0 commit comments