1 | /** |
---|
2 | @file Box.cpp |
---|
3 | Box class |
---|
4 | |
---|
5 | @maintainer Morgan McGuire, matrix@graphics3d.com |
---|
6 | |
---|
7 | @created 2001-06-02 |
---|
8 | @edited 2006-02-05 |
---|
9 | */ |
---|
10 | |
---|
11 | #include "G3D/Box.h" |
---|
12 | #include "G3D/debug.h" |
---|
13 | #include "G3D/Plane.h" |
---|
14 | #include "G3D/AABox.h" |
---|
15 | #include "G3D/CoordinateFrame.h" |
---|
16 | |
---|
17 | namespace G3D { |
---|
18 | |
---|
19 | /** |
---|
20 | Sets a field on four vertices. Used by the constructor. |
---|
21 | */ |
---|
22 | #define setMany(i0, i1, i2, i3, field, extreme) \ |
---|
23 | _corner[i0].field = _corner[i1].field = \ |
---|
24 | _corner[i2].field = _corner[i3].field = \ |
---|
25 | (extreme).field |
---|
26 | |
---|
27 | Box::Box() { |
---|
28 | } |
---|
29 | |
---|
30 | |
---|
31 | Box::Box(const AABox& b) { |
---|
32 | init(b.low(), b.high()); |
---|
33 | } |
---|
34 | |
---|
35 | |
---|
36 | Box::Box( |
---|
37 | const Vector3& min, |
---|
38 | const Vector3& max) { |
---|
39 | |
---|
40 | init(min.min(max), min.max(max)); |
---|
41 | |
---|
42 | } |
---|
43 | |
---|
44 | void Box::init( |
---|
45 | const Vector3& min, |
---|
46 | const Vector3& max) { |
---|
47 | |
---|
48 | setMany(0, 1, 2, 3, z, max); |
---|
49 | setMany(4, 5, 6, 7, z, min); |
---|
50 | |
---|
51 | setMany(1, 2, 5, 6, x, max); |
---|
52 | setMany(0, 3, 4, 7, x, min); |
---|
53 | |
---|
54 | setMany(3, 2, 6, 7, y, max); |
---|
55 | setMany(0, 1, 5, 4, y, min); |
---|
56 | |
---|
57 | _extent = max - min; |
---|
58 | |
---|
59 | _axis[0] = Vector3::unitX(); |
---|
60 | _axis[1] = Vector3::unitY(); |
---|
61 | _axis[2] = Vector3::unitZ(); |
---|
62 | |
---|
63 | _volume = _extent.x * _extent.y * _extent.z; |
---|
64 | _area = 2 * |
---|
65 | (_extent.x * _extent.y + |
---|
66 | _extent.y * _extent.z + |
---|
67 | _extent.z * _extent.x); |
---|
68 | |
---|
69 | _center = (max + min) / 2; |
---|
70 | } |
---|
71 | |
---|
72 | |
---|
73 | float Box::volume() const { |
---|
74 | return _volume; |
---|
75 | } |
---|
76 | |
---|
77 | |
---|
78 | float Box::surfaceArea() const { |
---|
79 | return _area; |
---|
80 | } |
---|
81 | |
---|
82 | |
---|
83 | void Box::getLocalFrame(CoordinateFrame& frame) const { |
---|
84 | |
---|
85 | frame.rotation = Matrix3( |
---|
86 | _axis[0][0], _axis[1][0], _axis[2][0], |
---|
87 | _axis[0][1], _axis[1][1], _axis[2][1], |
---|
88 | _axis[0][2], _axis[1][2], _axis[2][2]); |
---|
89 | |
---|
90 | frame.translation = _center; |
---|
91 | } |
---|
92 | |
---|
93 | |
---|
94 | CoordinateFrame Box::localFrame() const { |
---|
95 | CoordinateFrame out; |
---|
96 | getLocalFrame(out); |
---|
97 | return out; |
---|
98 | } |
---|
99 | |
---|
100 | |
---|
101 | void Box::getFaceCorners(int f, Vector3& v0, Vector3& v1, Vector3& v2, Vector3& v3) const { |
---|
102 | switch (f) { |
---|
103 | case 0: |
---|
104 | v0 = _corner[0]; v1 = _corner[1]; v2 = _corner[2]; v3 = _corner[3]; |
---|
105 | break; |
---|
106 | |
---|
107 | case 1: |
---|
108 | v0 = _corner[1]; v1 = _corner[5]; v2 = _corner[6]; v3 = _corner[2]; |
---|
109 | break; |
---|
110 | |
---|
111 | case 2: |
---|
112 | v0 = _corner[7]; v1 = _corner[6]; v2 = _corner[5]; v3 = _corner[4]; |
---|
113 | break; |
---|
114 | |
---|
115 | case 3: |
---|
116 | v0 = _corner[2]; v1 = _corner[6]; v2 = _corner[7]; v3 = _corner[3]; |
---|
117 | break; |
---|
118 | |
---|
119 | case 4: |
---|
120 | v0 = _corner[3]; v1 = _corner[7]; v2 = _corner[4]; v3 = _corner[0]; |
---|
121 | break; |
---|
122 | |
---|
123 | case 5: |
---|
124 | v0 = _corner[1]; v1 = _corner[0]; v2 = _corner[4]; v3 = _corner[5]; |
---|
125 | break; |
---|
126 | |
---|
127 | default: |
---|
128 | debugAssert((f >= 0) && (f < 6)); |
---|
129 | } |
---|
130 | } |
---|
131 | |
---|
132 | |
---|
133 | bool Box::culledBy( |
---|
134 | const Array<Plane>& plane, |
---|
135 | int& cullingPlaneIndex, |
---|
136 | const uint32 inMask, |
---|
137 | uint32& outMask) const { |
---|
138 | |
---|
139 | return culledBy(plane.getCArray(), plane.size(), cullingPlaneIndex, inMask, outMask); |
---|
140 | } |
---|
141 | |
---|
142 | |
---|
143 | bool Box::culledBy( |
---|
144 | const Array<Plane>& plane, |
---|
145 | int& cullingPlaneIndex, |
---|
146 | const uint32 inMask) const { |
---|
147 | |
---|
148 | return culledBy(plane.getCArray(), plane.size(), cullingPlaneIndex, inMask); |
---|
149 | } |
---|
150 | |
---|
151 | |
---|
152 | int32 Box::dummy = 0; |
---|
153 | |
---|
154 | bool Box::culledBy( |
---|
155 | const class Plane* plane, |
---|
156 | int numPlanes, |
---|
157 | int& cullingPlane, |
---|
158 | const uint32 _inMask, |
---|
159 | uint32& childMask) const { |
---|
160 | |
---|
161 | uint32 inMask = _inMask; |
---|
162 | assert(numPlanes < 31); |
---|
163 | |
---|
164 | childMask = 0; |
---|
165 | |
---|
166 | // See if there is one plane for which all of the |
---|
167 | // vertices are in the negative half space. |
---|
168 | for (int p = 0; p < numPlanes; p++) { |
---|
169 | |
---|
170 | // Only test planes that are not masked |
---|
171 | if ((inMask & 1) != 0) { |
---|
172 | |
---|
173 | Vector3 corner; |
---|
174 | |
---|
175 | int numContained = 0; |
---|
176 | int v = 0; |
---|
177 | |
---|
178 | // We can early-out only if we have found one point on each |
---|
179 | // side of the plane (i.e. if we are straddling). That |
---|
180 | // occurs when (numContained < v) && (numContained > 0) |
---|
181 | for (v = 0; (v < 8) && ((numContained == v) || (numContained == 0)); ++v) { |
---|
182 | if (plane[p].halfSpaceContains(getCorner(v))) { |
---|
183 | ++numContained; |
---|
184 | } |
---|
185 | } |
---|
186 | |
---|
187 | if (numContained == 0) { |
---|
188 | // Plane p culled the box |
---|
189 | cullingPlane = p; |
---|
190 | |
---|
191 | // The caller should not recurse into the children, |
---|
192 | // since the parent is culled. If they do recurse, |
---|
193 | // make them only test against this one plane, which |
---|
194 | // will immediately cull the volume. |
---|
195 | childMask = 1 << p; |
---|
196 | return true; |
---|
197 | |
---|
198 | } else if (numContained < v) { |
---|
199 | // The bounding volume straddled the plane; we have |
---|
200 | // to keep testing against this plane |
---|
201 | childMask |= (1 << p); |
---|
202 | } |
---|
203 | } |
---|
204 | |
---|
205 | // Move on to the next bit. |
---|
206 | inMask = inMask >> 1; |
---|
207 | } |
---|
208 | |
---|
209 | // None of the planes could cull this box |
---|
210 | cullingPlane = -1; |
---|
211 | return false; |
---|
212 | } |
---|
213 | |
---|
214 | |
---|
215 | bool Box::culledBy( |
---|
216 | const class Plane* plane, |
---|
217 | int numPlanes, |
---|
218 | int& cullingPlane, |
---|
219 | const uint32 _inMask) const { |
---|
220 | |
---|
221 | uint32 inMask = _inMask; |
---|
222 | assert(numPlanes < 31); |
---|
223 | |
---|
224 | // See if there is one plane for which all of the |
---|
225 | // vertices are in the negative half space. |
---|
226 | for (int p = 0; p < numPlanes; p++) { |
---|
227 | |
---|
228 | // Only test planes that are not masked |
---|
229 | if ((inMask & 1) != 0) { |
---|
230 | |
---|
231 | bool culled = true; |
---|
232 | |
---|
233 | int v; |
---|
234 | |
---|
235 | // Assume this plane culls all points. See if there is a point |
---|
236 | // not culled by the plane... early out when at least one point |
---|
237 | // is in the positive half space. |
---|
238 | for (v = 0; (v < 8) && culled; ++v) { |
---|
239 | culled = ! plane[p].halfSpaceContains(getCorner(v)); |
---|
240 | } |
---|
241 | |
---|
242 | if (culled) { |
---|
243 | // Plane p culled the box |
---|
244 | cullingPlane = p; |
---|
245 | |
---|
246 | return true; |
---|
247 | } |
---|
248 | } |
---|
249 | |
---|
250 | // Move on to the next bit. |
---|
251 | inMask = inMask >> 1; |
---|
252 | } |
---|
253 | |
---|
254 | // None of the planes could cull this box |
---|
255 | cullingPlane = -1; |
---|
256 | return false; |
---|
257 | } |
---|
258 | |
---|
259 | |
---|
260 | bool Box::contains( |
---|
261 | const Vector3& point) const { |
---|
262 | |
---|
263 | // Form axes from three edges, transform the point into that |
---|
264 | // space, and perform 3 interval tests |
---|
265 | |
---|
266 | Vector3 u = _corner[4] - _corner[0]; |
---|
267 | Vector3 v = _corner[3] - _corner[0]; |
---|
268 | Vector3 w = _corner[1] - _corner[0]; |
---|
269 | |
---|
270 | Matrix3 M = Matrix3(u.x, v.x, w.x, |
---|
271 | u.y, v.y, w.y, |
---|
272 | u.z, v.z, w.z); |
---|
273 | |
---|
274 | // M^-1 * (point - _corner[0]) = point in unit cube's object space |
---|
275 | // compute the inverse of M |
---|
276 | Vector3 osPoint = M.inverse() * (point - _corner[0]); |
---|
277 | |
---|
278 | return |
---|
279 | (osPoint.x >= 0) && |
---|
280 | (osPoint.y >= 0) && |
---|
281 | (osPoint.z >= 0) && |
---|
282 | (osPoint.x <= 1) && |
---|
283 | (osPoint.y <= 1) && |
---|
284 | (osPoint.z <= 1); |
---|
285 | } |
---|
286 | |
---|
287 | #undef setMany |
---|
288 | |
---|
289 | #if 0 |
---|
290 | void Box::getRandomSurfacePoint(Vector3& P, Vector3& N) const { |
---|
291 | float aXY = _extent.x * _extent.y; |
---|
292 | float aYZ = _extent.y * _extent.z; |
---|
293 | float aZX = _extent.z * _extent.x; |
---|
294 | |
---|
295 | float r = (float)random(0, aXY + aYZ + aZX); |
---|
296 | |
---|
297 | // Choose evenly between positive and negative face planes |
---|
298 | float d = (random(0, 1) < 0.5f) ? -1.0f : 1.0f; |
---|
299 | |
---|
300 | // The probability of choosing a given face is proportional to |
---|
301 | // its area. |
---|
302 | if (r < aXY) { |
---|
303 | P = _axis[0] * (float)random(-0.5, 0.5) * _extent.x + |
---|
304 | _axis[1] * (float)random(-0.5, 0.5) * _extent.y + |
---|
305 | _center + _axis[2] * d * _extent.z * 0.5f; |
---|
306 | N = _axis[2] * d; |
---|
307 | } else if (r < aYZ) { |
---|
308 | P = _axis[1] * (float)random(-0.5, 0.5) * _extent.y + |
---|
309 | _axis[2] * (float)random(-0.5, 0.5) * _extent.z + |
---|
310 | _center + _axis[0] * d * _extent.x * 0.5f; |
---|
311 | N = _axis[0] * d; |
---|
312 | } else { |
---|
313 | P = _axis[2] * (float)random(-0.5, 0.5) * _extent.z + |
---|
314 | _axis[0] *(float) random(-0.5, 0.5) * _extent.x + |
---|
315 | _center + _axis[1] * d * _extent.y * 0.5f; |
---|
316 | N = _axis[1] * d; |
---|
317 | } |
---|
318 | } |
---|
319 | |
---|
320 | |
---|
321 | Vector3 Box::randomInteriorPoint() const { |
---|
322 | Vector3 sum = _center; |
---|
323 | |
---|
324 | for (int a = 0; a < 3; ++a) { |
---|
325 | sum += _axis[a] * (float)random(-0.5, 0.5) * _extent[a]; |
---|
326 | } |
---|
327 | |
---|
328 | return sum; |
---|
329 | } |
---|
330 | #endif |
---|
331 | |
---|
332 | |
---|
333 | void Box::getBounds(class AABox& aabb) const { |
---|
334 | |
---|
335 | Vector3 lo = _corner[0]; |
---|
336 | Vector3 hi = lo; |
---|
337 | |
---|
338 | for (int v = 1; v < 8; ++v) { |
---|
339 | const Vector3& C = _corner[v]; |
---|
340 | lo = lo.min(C); |
---|
341 | hi = hi.max(C); |
---|
342 | } |
---|
343 | |
---|
344 | aabb = AABox(lo, hi); |
---|
345 | } |
---|
346 | |
---|
347 | |
---|
348 | } // namespace |
---|