1 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
2 | /** |
---|
3 | * Contains OBB-related code. |
---|
4 | * \file IceOBB.cpp |
---|
5 | * \author Pierre Terdiman |
---|
6 | * \date January, 29, 2000 |
---|
7 | */ |
---|
8 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
9 | |
---|
10 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
11 | /** |
---|
12 | * An Oriented Bounding Box (OBB). |
---|
13 | * \class OBB |
---|
14 | * \author Pierre Terdiman |
---|
15 | * \version 1.0 |
---|
16 | */ |
---|
17 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
18 | |
---|
19 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
20 | // Precompiled Header |
---|
21 | #include "Stdafx.h" |
---|
22 | |
---|
23 | using namespace IceMaths; |
---|
24 | |
---|
25 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
26 | /** |
---|
27 | * Tests if a point is contained within the OBB. |
---|
28 | * \param p [in] the world point to test |
---|
29 | * \return true if inside the OBB |
---|
30 | */ |
---|
31 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
32 | bool OBB::ContainsPoint(const Point& p) const |
---|
33 | { |
---|
34 | // Point in OBB test using lazy evaluation and early exits |
---|
35 | |
---|
36 | // Translate to box space |
---|
37 | Point RelPoint = p - mCenter; |
---|
38 | |
---|
39 | // Point * mRot maps from box space to world space |
---|
40 | // mRot * Point maps from world space to box space (what we need here) |
---|
41 | |
---|
42 | float f = mRot.m[0][0] * RelPoint.x + mRot.m[0][1] * RelPoint.y + mRot.m[0][2] * RelPoint.z; |
---|
43 | if(f >= mExtents.x || f <= -mExtents.x) return false; |
---|
44 | |
---|
45 | f = mRot.m[1][0] * RelPoint.x + mRot.m[1][1] * RelPoint.y + mRot.m[1][2] * RelPoint.z; |
---|
46 | if(f >= mExtents.y || f <= -mExtents.y) return false; |
---|
47 | |
---|
48 | f = mRot.m[2][0] * RelPoint.x + mRot.m[2][1] * RelPoint.y + mRot.m[2][2] * RelPoint.z; |
---|
49 | if(f >= mExtents.z || f <= -mExtents.z) return false; |
---|
50 | return true; |
---|
51 | } |
---|
52 | |
---|
53 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
54 | /** |
---|
55 | * Builds an OBB from an AABB and a world transform. |
---|
56 | * \param aabb [in] the aabb |
---|
57 | * \param mat [in] the world transform |
---|
58 | */ |
---|
59 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
60 | void OBB::Create(const AABB& aabb, const Matrix4x4& mat) |
---|
61 | { |
---|
62 | // Note: must be coherent with Rotate() |
---|
63 | |
---|
64 | aabb.GetCenter(mCenter); |
---|
65 | aabb.GetExtents(mExtents); |
---|
66 | // Here we have the same as OBB::Rotate(mat) where the obb is (mCenter, mExtents, Identity). |
---|
67 | |
---|
68 | // So following what's done in Rotate: |
---|
69 | // - x-form the center |
---|
70 | mCenter *= mat; |
---|
71 | // - combine rotation with identity, i.e. just use given matrix |
---|
72 | mRot = mat; |
---|
73 | } |
---|
74 | |
---|
75 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
76 | /** |
---|
77 | * Computes the obb planes. |
---|
78 | * \param planes [out] 6 box planes |
---|
79 | * \return true if success |
---|
80 | */ |
---|
81 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
82 | bool OBB::ComputePlanes(Plane* planes) const |
---|
83 | { |
---|
84 | // Checkings |
---|
85 | if(!planes) return false; |
---|
86 | |
---|
87 | Point Axis0 = mRot[0]; |
---|
88 | Point Axis1 = mRot[1]; |
---|
89 | Point Axis2 = mRot[2]; |
---|
90 | |
---|
91 | // Writes normals |
---|
92 | planes[0].n = Axis0; |
---|
93 | planes[1].n = -Axis0; |
---|
94 | planes[2].n = Axis1; |
---|
95 | planes[3].n = -Axis1; |
---|
96 | planes[4].n = Axis2; |
---|
97 | planes[5].n = -Axis2; |
---|
98 | |
---|
99 | // Compute a point on each plane |
---|
100 | Point p0 = mCenter + Axis0 * mExtents.x; |
---|
101 | Point p1 = mCenter - Axis0 * mExtents.x; |
---|
102 | Point p2 = mCenter + Axis1 * mExtents.y; |
---|
103 | Point p3 = mCenter - Axis1 * mExtents.y; |
---|
104 | Point p4 = mCenter + Axis2 * mExtents.z; |
---|
105 | Point p5 = mCenter - Axis2 * mExtents.z; |
---|
106 | |
---|
107 | // Compute d |
---|
108 | planes[0].d = -(planes[0].n|p0); |
---|
109 | planes[1].d = -(planes[1].n|p1); |
---|
110 | planes[2].d = -(planes[2].n|p2); |
---|
111 | planes[3].d = -(planes[3].n|p3); |
---|
112 | planes[4].d = -(planes[4].n|p4); |
---|
113 | planes[5].d = -(planes[5].n|p5); |
---|
114 | |
---|
115 | return true; |
---|
116 | } |
---|
117 | |
---|
118 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
119 | /** |
---|
120 | * Computes the obb points. |
---|
121 | * \param pts [out] 8 box points |
---|
122 | * \return true if success |
---|
123 | */ |
---|
124 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
125 | bool OBB::ComputePoints(Point* pts) const |
---|
126 | { |
---|
127 | // Checkings |
---|
128 | if(!pts) return false; |
---|
129 | |
---|
130 | Point Axis0 = mRot[0]; |
---|
131 | Point Axis1 = mRot[1]; |
---|
132 | Point Axis2 = mRot[2]; |
---|
133 | |
---|
134 | Axis0 *= mExtents.x; |
---|
135 | Axis1 *= mExtents.y; |
---|
136 | Axis2 *= mExtents.z; |
---|
137 | |
---|
138 | // 7+------+6 0 = --- |
---|
139 | // /| /| 1 = +-- |
---|
140 | // / | / | 2 = ++- |
---|
141 | // / 4+---/--+5 3 = -+- |
---|
142 | // 3+------+2 / y z 4 = --+ |
---|
143 | // | / | / | / 5 = +-+ |
---|
144 | // |/ |/ |/ 6 = +++ |
---|
145 | // 0+------+1 *---x 7 = -++ |
---|
146 | |
---|
147 | pts[0] = mCenter - Axis0 - Axis1 - Axis2; |
---|
148 | pts[1] = mCenter + Axis0 - Axis1 - Axis2; |
---|
149 | pts[2] = mCenter + Axis0 + Axis1 - Axis2; |
---|
150 | pts[3] = mCenter - Axis0 + Axis1 - Axis2; |
---|
151 | pts[4] = mCenter - Axis0 - Axis1 + Axis2; |
---|
152 | pts[5] = mCenter + Axis0 - Axis1 + Axis2; |
---|
153 | pts[6] = mCenter + Axis0 + Axis1 + Axis2; |
---|
154 | pts[7] = mCenter - Axis0 + Axis1 + Axis2; |
---|
155 | |
---|
156 | return true; |
---|
157 | } |
---|
158 | |
---|
159 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
160 | /** |
---|
161 | * Computes vertex normals. |
---|
162 | * \param pts [out] 8 box points |
---|
163 | * \return true if success |
---|
164 | */ |
---|
165 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
166 | bool OBB::ComputeVertexNormals(Point* pts) const |
---|
167 | { |
---|
168 | static float VertexNormals[] = |
---|
169 | { |
---|
170 | -INVSQRT3, -INVSQRT3, -INVSQRT3, |
---|
171 | INVSQRT3, -INVSQRT3, -INVSQRT3, |
---|
172 | INVSQRT3, INVSQRT3, -INVSQRT3, |
---|
173 | -INVSQRT3, INVSQRT3, -INVSQRT3, |
---|
174 | -INVSQRT3, -INVSQRT3, INVSQRT3, |
---|
175 | INVSQRT3, -INVSQRT3, INVSQRT3, |
---|
176 | INVSQRT3, INVSQRT3, INVSQRT3, |
---|
177 | -INVSQRT3, INVSQRT3, INVSQRT3 |
---|
178 | }; |
---|
179 | |
---|
180 | if(!pts) return false; |
---|
181 | |
---|
182 | const Point* VN = (const Point*)VertexNormals; |
---|
183 | for(udword i=0;i<8;i++) |
---|
184 | { |
---|
185 | pts[i] = VN[i] * mRot; |
---|
186 | } |
---|
187 | |
---|
188 | return true; |
---|
189 | } |
---|
190 | |
---|
191 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
192 | /** |
---|
193 | * Returns edges. |
---|
194 | * \return 24 indices (12 edges) indexing the list returned by ComputePoints() |
---|
195 | */ |
---|
196 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
197 | const udword* OBB::GetEdges() const |
---|
198 | { |
---|
199 | static udword Indices[] = { |
---|
200 | 0, 1, 1, 2, 2, 3, 3, 0, |
---|
201 | 7, 6, 6, 5, 5, 4, 4, 7, |
---|
202 | 1, 5, 6, 2, |
---|
203 | 3, 7, 4, 0 |
---|
204 | }; |
---|
205 | return Indices; |
---|
206 | } |
---|
207 | |
---|
208 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
209 | /** |
---|
210 | * Returns local edge normals. |
---|
211 | * \return edge normals in local space |
---|
212 | */ |
---|
213 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
214 | const Point* OBB::GetLocalEdgeNormals() const |
---|
215 | { |
---|
216 | static float EdgeNormals[] = |
---|
217 | { |
---|
218 | 0, -INVSQRT2, -INVSQRT2, // 0-1 |
---|
219 | INVSQRT2, 0, -INVSQRT2, // 1-2 |
---|
220 | 0, INVSQRT2, -INVSQRT2, // 2-3 |
---|
221 | -INVSQRT2, 0, -INVSQRT2, // 3-0 |
---|
222 | |
---|
223 | 0, INVSQRT2, INVSQRT2, // 7-6 |
---|
224 | INVSQRT2, 0, INVSQRT2, // 6-5 |
---|
225 | 0, -INVSQRT2, INVSQRT2, // 5-4 |
---|
226 | -INVSQRT2, 0, INVSQRT2, // 4-7 |
---|
227 | |
---|
228 | INVSQRT2, -INVSQRT2, 0, // 1-5 |
---|
229 | INVSQRT2, INVSQRT2, 0, // 6-2 |
---|
230 | -INVSQRT2, INVSQRT2, 0, // 3-7 |
---|
231 | -INVSQRT2, -INVSQRT2, 0 // 4-0 |
---|
232 | }; |
---|
233 | return (const Point*)EdgeNormals; |
---|
234 | } |
---|
235 | |
---|
236 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
237 | /** |
---|
238 | * Returns world edge normal |
---|
239 | * \param edge_index [in] 0 <= edge index < 12 |
---|
240 | * \param world_normal [out] edge normal in world space |
---|
241 | */ |
---|
242 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
243 | void OBB::ComputeWorldEdgeNormal(udword edge_index, Point& world_normal) const |
---|
244 | { |
---|
245 | ASSERT(edge_index<12); |
---|
246 | world_normal = GetLocalEdgeNormals()[edge_index] * mRot; |
---|
247 | } |
---|
248 | |
---|
249 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
250 | /** |
---|
251 | * Computes an LSS surrounding the OBB. |
---|
252 | * \param lss [out] the LSS |
---|
253 | */ |
---|
254 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
255 | void OBB::ComputeLSS(LSS& lss) const |
---|
256 | { |
---|
257 | Point Axis0 = mRot[0]; |
---|
258 | Point Axis1 = mRot[1]; |
---|
259 | Point Axis2 = mRot[2]; |
---|
260 | |
---|
261 | switch(mExtents.LargestAxis()) |
---|
262 | { |
---|
263 | case 0: |
---|
264 | lss.mRadius = (mExtents.y + mExtents.z)*0.5f; |
---|
265 | lss.mP0 = mCenter + Axis0 * (mExtents.x - lss.mRadius); |
---|
266 | lss.mP1 = mCenter - Axis0 * (mExtents.x - lss.mRadius); |
---|
267 | break; |
---|
268 | case 1: |
---|
269 | lss.mRadius = (mExtents.x + mExtents.z)*0.5f; |
---|
270 | lss.mP0 = mCenter + Axis1 * (mExtents.y - lss.mRadius); |
---|
271 | lss.mP1 = mCenter - Axis1 * (mExtents.y - lss.mRadius); |
---|
272 | break; |
---|
273 | case 2: |
---|
274 | lss.mRadius = (mExtents.x + mExtents.y)*0.5f; |
---|
275 | lss.mP0 = mCenter + Axis2 * (mExtents.z - lss.mRadius); |
---|
276 | lss.mP1 = mCenter - Axis2 * (mExtents.z - lss.mRadius); |
---|
277 | break; |
---|
278 | } |
---|
279 | } |
---|
280 | |
---|
281 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
282 | /** |
---|
283 | * Checks the OBB is inside another OBB. |
---|
284 | * \param box [in] the other OBB |
---|
285 | * \return TRUE if we're inside the other box |
---|
286 | */ |
---|
287 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
288 | BOOL OBB::IsInside(const OBB& box) const |
---|
289 | { |
---|
290 | // Make a 4x4 from the box & inverse it |
---|
291 | Matrix4x4 M0Inv; |
---|
292 | { |
---|
293 | Matrix4x4 M0 = box.mRot; |
---|
294 | M0.SetTrans(box.mCenter); |
---|
295 | InvertPRMatrix(M0Inv, M0); |
---|
296 | } |
---|
297 | |
---|
298 | // With our inversed 4x4, create box1 in space of box0 |
---|
299 | OBB _1in0; |
---|
300 | Rotate(M0Inv, _1in0); |
---|
301 | |
---|
302 | // This should cancel out box0's rotation, i.e. it's now an AABB. |
---|
303 | // => Center(0,0,0), Rot(identity) |
---|
304 | |
---|
305 | // The two boxes are in the same space so now we can compare them. |
---|
306 | |
---|
307 | // Create the AABB of (box1 in space of box0) |
---|
308 | const Matrix3x3& mtx = _1in0.mRot; |
---|
309 | |
---|
310 | float f = fabsf(mtx.m[0][0] * mExtents.x) + fabsf(mtx.m[1][0] * mExtents.y) + fabsf(mtx.m[2][0] * mExtents.z) - box.mExtents.x; |
---|
311 | if(f > _1in0.mCenter.x) return FALSE; |
---|
312 | if(-f < _1in0.mCenter.x) return FALSE; |
---|
313 | |
---|
314 | f = fabsf(mtx.m[0][1] * mExtents.x) + fabsf(mtx.m[1][1] * mExtents.y) + fabsf(mtx.m[2][1] * mExtents.z) - box.mExtents.y; |
---|
315 | if(f > _1in0.mCenter.y) return FALSE; |
---|
316 | if(-f < _1in0.mCenter.y) return FALSE; |
---|
317 | |
---|
318 | f = fabsf(mtx.m[0][2] * mExtents.x) + fabsf(mtx.m[1][2] * mExtents.y) + fabsf(mtx.m[2][2] * mExtents.z) - box.mExtents.z; |
---|
319 | if(f > _1in0.mCenter.z) return FALSE; |
---|
320 | if(-f < _1in0.mCenter.z) return FALSE; |
---|
321 | |
---|
322 | return TRUE; |
---|
323 | } |
---|