1 | /*
|
---|
2 | -----------------------------------------------------------------------------
|
---|
3 | This source file is part of OGRE
|
---|
4 | (Object-oriented Graphics Rendering Engine)
|
---|
5 | For the latest info, see http://www.ogre3d.org/
|
---|
6 |
|
---|
7 | Copyright (c) 2000-2005 The OGRE Team
|
---|
8 | Also see acknowledgements in Readme.html
|
---|
9 |
|
---|
10 | This program is free software; you can redistribute it and/or modify it under
|
---|
11 | the terms of the GNU Lesser General Public License as published by the Free Software
|
---|
12 | Foundation; either version 2 of the License, or (at your option) any later
|
---|
13 | version.
|
---|
14 |
|
---|
15 | This program is distributed in the hope that it will be useful, but WITHOUT
|
---|
16 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
---|
17 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
---|
18 |
|
---|
19 | You should have received a copy of the GNU Lesser General Public License along with
|
---|
20 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
---|
21 | Place - Suite 330, Boston, MA 02111-1307, USA, or go to
|
---|
22 | http://www.gnu.org/copyleft/lesser.txt.
|
---|
23 | -----------------------------------------------------------------------------
|
---|
24 | */
|
---|
25 | #include "OgreStableHeaders.h"
|
---|
26 |
|
---|
27 | #include "OgreMath.h"
|
---|
28 | #include "asm_math.h"
|
---|
29 | #include "OgreVector2.h"
|
---|
30 | #include "OgreVector3.h"
|
---|
31 | #include "OgreVector4.h"
|
---|
32 | #include "OgreRay.h"
|
---|
33 | #include "OgreSphere.h"
|
---|
34 | #include "OgreAxisAlignedBox.h"
|
---|
35 | #include "OgrePlane.h"
|
---|
36 |
|
---|
37 |
|
---|
38 | namespace Ogre
|
---|
39 | {
|
---|
40 |
|
---|
41 | const Real Math::POS_INFINITY = std::numeric_limits<Real>::infinity();
|
---|
42 | const Real Math::NEG_INFINITY = -std::numeric_limits<Real>::infinity();
|
---|
43 | const Real Math::PI = Real( 4.0 * atan( 1.0 ) );
|
---|
44 | const Real Math::TWO_PI = Real( 2.0 * PI );
|
---|
45 | const Real Math::HALF_PI = Real( 0.5 * PI );
|
---|
46 | const Real Math::fDeg2Rad = PI / Real(180.0);
|
---|
47 | const Real Math::fRad2Deg = Real(180.0) / PI;
|
---|
48 |
|
---|
49 | int Math::mTrigTableSize;
|
---|
50 | Math::AngleUnit Math::msAngleUnit;
|
---|
51 |
|
---|
52 | Real Math::mTrigTableFactor;
|
---|
53 | Real *Math::mSinTable = NULL;
|
---|
54 | Real *Math::mTanTable = NULL;
|
---|
55 |
|
---|
56 | //-----------------------------------------------------------------------
|
---|
57 | Math::Math( unsigned int trigTableSize )
|
---|
58 | {
|
---|
59 | msAngleUnit = AU_DEGREE;
|
---|
60 |
|
---|
61 | mTrigTableSize = trigTableSize;
|
---|
62 | mTrigTableFactor = mTrigTableSize / Math::TWO_PI;
|
---|
63 |
|
---|
64 | mSinTable = new Real[mTrigTableSize];
|
---|
65 | mTanTable = new Real[mTrigTableSize];
|
---|
66 |
|
---|
67 | buildTrigTables();
|
---|
68 | }
|
---|
69 |
|
---|
70 | //-----------------------------------------------------------------------
|
---|
71 | Math::~Math()
|
---|
72 | {
|
---|
73 | delete [] mSinTable;
|
---|
74 | delete [] mTanTable;
|
---|
75 | }
|
---|
76 |
|
---|
77 | //-----------------------------------------------------------------------
|
---|
78 | void Math::buildTrigTables(void)
|
---|
79 | {
|
---|
80 | // Build trig lookup tables
|
---|
81 | // Could get away with building only PI sized Sin table but simpler this
|
---|
82 | // way. Who cares, it'll ony use an extra 8k of memory anyway and I like
|
---|
83 | // simplicity.
|
---|
84 | Real angle;
|
---|
85 | for (int i = 0; i < mTrigTableSize; ++i)
|
---|
86 | {
|
---|
87 | angle = Math::TWO_PI * i / mTrigTableSize;
|
---|
88 | mSinTable[i] = sin(angle);
|
---|
89 | mTanTable[i] = tan(angle);
|
---|
90 | }
|
---|
91 | }
|
---|
92 | //-----------------------------------------------------------------------
|
---|
93 | Real Math::SinTable (Real fValue)
|
---|
94 | {
|
---|
95 | // Convert range to index values, wrap if required
|
---|
96 | int idx;
|
---|
97 | if (fValue >= 0)
|
---|
98 | {
|
---|
99 | idx = int(fValue * mTrigTableFactor) % mTrigTableSize;
|
---|
100 | }
|
---|
101 | else
|
---|
102 | {
|
---|
103 | idx = mTrigTableSize - (int(-fValue * mTrigTableFactor) % mTrigTableSize) - 1;
|
---|
104 | }
|
---|
105 |
|
---|
106 | return mSinTable[idx];
|
---|
107 | }
|
---|
108 | //-----------------------------------------------------------------------
|
---|
109 | Real Math::TanTable (Real fValue)
|
---|
110 | {
|
---|
111 | // Convert range to index values, wrap if required
|
---|
112 | int idx = int(fValue *= mTrigTableFactor) % mTrigTableSize;
|
---|
113 | return mTanTable[idx];
|
---|
114 | }
|
---|
115 | //-----------------------------------------------------------------------
|
---|
116 | int Math::ISign (int iValue)
|
---|
117 | {
|
---|
118 | return ( iValue > 0 ? +1 : ( iValue < 0 ? -1 : 0 ) );
|
---|
119 | }
|
---|
120 | //-----------------------------------------------------------------------
|
---|
121 | Radian Math::ACos (Real fValue)
|
---|
122 | {
|
---|
123 | if ( -1.0 < fValue )
|
---|
124 | {
|
---|
125 | if ( fValue < 1.0 )
|
---|
126 | return Radian(acos(fValue));
|
---|
127 | else
|
---|
128 | return Radian(0.0);
|
---|
129 | }
|
---|
130 | else
|
---|
131 | {
|
---|
132 | return Radian(PI);
|
---|
133 | }
|
---|
134 | }
|
---|
135 | //-----------------------------------------------------------------------
|
---|
136 | Radian Math::ASin (Real fValue)
|
---|
137 | {
|
---|
138 | if ( -1.0 < fValue )
|
---|
139 | {
|
---|
140 | if ( fValue < 1.0 )
|
---|
141 | return Radian(asin(fValue));
|
---|
142 | else
|
---|
143 | return Radian(HALF_PI);
|
---|
144 | }
|
---|
145 | else
|
---|
146 | {
|
---|
147 | return Radian(-HALF_PI);
|
---|
148 | }
|
---|
149 | }
|
---|
150 | //-----------------------------------------------------------------------
|
---|
151 | Real Math::Sign (Real fValue)
|
---|
152 | {
|
---|
153 | if ( fValue > 0.0 )
|
---|
154 | return 1.0;
|
---|
155 |
|
---|
156 | if ( fValue < 0.0 )
|
---|
157 | return -1.0;
|
---|
158 |
|
---|
159 | return 0.0;
|
---|
160 | }
|
---|
161 | //-----------------------------------------------------------------------
|
---|
162 | Real Math::InvSqrt(Real fValue)
|
---|
163 | {
|
---|
164 | return Real(asm_rsq(fValue));
|
---|
165 | }
|
---|
166 | //-----------------------------------------------------------------------
|
---|
167 | Real Math::UnitRandom ()
|
---|
168 | {
|
---|
169 | return asm_rand() / asm_rand_max();
|
---|
170 | }
|
---|
171 |
|
---|
172 | //-----------------------------------------------------------------------
|
---|
173 | Real Math::RangeRandom (Real fLow, Real fHigh)
|
---|
174 | {
|
---|
175 | return (fHigh-fLow)*UnitRandom() + fLow;
|
---|
176 | }
|
---|
177 |
|
---|
178 | //-----------------------------------------------------------------------
|
---|
179 | Real Math::SymmetricRandom ()
|
---|
180 | {
|
---|
181 | return 2.0f * UnitRandom() - 1.0f;
|
---|
182 | }
|
---|
183 |
|
---|
184 | //-----------------------------------------------------------------------
|
---|
185 | void Math::setAngleUnit(Math::AngleUnit unit)
|
---|
186 | {
|
---|
187 | msAngleUnit = unit;
|
---|
188 | }
|
---|
189 | //-----------------------------------------------------------------------
|
---|
190 | Math::AngleUnit Math::getAngleUnit(void)
|
---|
191 | {
|
---|
192 | return msAngleUnit;
|
---|
193 | }
|
---|
194 | //-----------------------------------------------------------------------
|
---|
195 | Real Math::AngleUnitsToRadians(Real angleunits)
|
---|
196 | {
|
---|
197 | if (msAngleUnit == AU_DEGREE)
|
---|
198 | return angleunits * fDeg2Rad;
|
---|
199 | else
|
---|
200 | return angleunits;
|
---|
201 | }
|
---|
202 |
|
---|
203 | //-----------------------------------------------------------------------
|
---|
204 | Real Math::RadiansToAngleUnits(Real radians)
|
---|
205 | {
|
---|
206 | if (msAngleUnit == AU_DEGREE)
|
---|
207 | return radians * fRad2Deg;
|
---|
208 | else
|
---|
209 | return radians;
|
---|
210 | }
|
---|
211 |
|
---|
212 | //-----------------------------------------------------------------------
|
---|
213 | Real Math::AngleUnitsToDegrees(Real angleunits)
|
---|
214 | {
|
---|
215 | if (msAngleUnit == AU_RADIAN)
|
---|
216 | return angleunits * fRad2Deg;
|
---|
217 | else
|
---|
218 | return angleunits;
|
---|
219 | }
|
---|
220 |
|
---|
221 | //-----------------------------------------------------------------------
|
---|
222 | Real Math::DegreesToAngleUnits(Real degrees)
|
---|
223 | {
|
---|
224 | if (msAngleUnit == AU_RADIAN)
|
---|
225 | return degrees * fDeg2Rad;
|
---|
226 | else
|
---|
227 | return degrees;
|
---|
228 | }
|
---|
229 |
|
---|
230 | //-----------------------------------------------------------------------
|
---|
231 | bool Math::pointInTri2D(const Vector2& p, const Vector2& a,
|
---|
232 | const Vector2& b, const Vector2& c)
|
---|
233 | {
|
---|
234 | // Winding must be consistent from all edges for point to be inside
|
---|
235 | Vector2 v1, v2;
|
---|
236 | Real dot[3];
|
---|
237 | bool zeroDot[3];
|
---|
238 |
|
---|
239 | v1 = b - a;
|
---|
240 | v2 = p - a;
|
---|
241 |
|
---|
242 | // Note we don't care about normalisation here since sign is all we need
|
---|
243 | // It means we don't have to worry about magnitude of cross products either
|
---|
244 | dot[0] = v1.crossProduct(v2);
|
---|
245 | zeroDot[0] = Math::RealEqual(dot[0], 0.0f, 1e-3);
|
---|
246 |
|
---|
247 |
|
---|
248 | v1 = c - b;
|
---|
249 | v2 = p - b;
|
---|
250 |
|
---|
251 | dot[1] = v1.crossProduct(v2);
|
---|
252 | zeroDot[1] = Math::RealEqual(dot[1], 0.0f, 1e-3);
|
---|
253 |
|
---|
254 | // Compare signs (ignore colinear / coincident points)
|
---|
255 | if(!zeroDot[0] && !zeroDot[1]
|
---|
256 | && Math::Sign(dot[0]) != Math::Sign(dot[1]))
|
---|
257 | {
|
---|
258 | return false;
|
---|
259 | }
|
---|
260 |
|
---|
261 | v1 = a - c;
|
---|
262 | v2 = p - c;
|
---|
263 |
|
---|
264 | dot[2] = v1.crossProduct(v2);
|
---|
265 | zeroDot[2] = Math::RealEqual(dot[2], 0.0f, 1e-3);
|
---|
266 | // Compare signs (ignore colinear / coincident points)
|
---|
267 | if((!zeroDot[0] && !zeroDot[2]
|
---|
268 | && Math::Sign(dot[0]) != Math::Sign(dot[2])) ||
|
---|
269 | (!zeroDot[1] && !zeroDot[2]
|
---|
270 | && Math::Sign(dot[1]) != Math::Sign(dot[2])))
|
---|
271 | {
|
---|
272 | return false;
|
---|
273 | }
|
---|
274 |
|
---|
275 |
|
---|
276 | return true;
|
---|
277 | }
|
---|
278 | //-----------------------------------------------------------------------
|
---|
279 | bool Math::pointInTri3D(const Vector3& p, const Vector3& a,
|
---|
280 | const Vector3& b, const Vector3& c, const Vector3& normal)
|
---|
281 | {
|
---|
282 | // Winding must be consistent from all edges for point to be inside
|
---|
283 | Vector3 v1, v2;
|
---|
284 | Real dot[3];
|
---|
285 | bool zeroDot[3];
|
---|
286 |
|
---|
287 | v1 = b - a;
|
---|
288 | v2 = p - a;
|
---|
289 |
|
---|
290 | // Note we don't care about normalisation here since sign is all we need
|
---|
291 | // It means we don't have to worry about magnitude of cross products either
|
---|
292 | dot[0] = v1.crossProduct(v2).dotProduct(normal);
|
---|
293 | zeroDot[0] = Math::RealEqual(dot[0], 0.0f, 1e-3);
|
---|
294 |
|
---|
295 |
|
---|
296 | v1 = c - b;
|
---|
297 | v2 = p - b;
|
---|
298 |
|
---|
299 | dot[1] = v1.crossProduct(v2).dotProduct(normal);
|
---|
300 | zeroDot[1] = Math::RealEqual(dot[1], 0.0f, 1e-3);
|
---|
301 |
|
---|
302 | // Compare signs (ignore colinear / coincident points)
|
---|
303 | if(!zeroDot[0] && !zeroDot[1]
|
---|
304 | && Math::Sign(dot[0]) != Math::Sign(dot[1]))
|
---|
305 | {
|
---|
306 | return false;
|
---|
307 | }
|
---|
308 |
|
---|
309 | v1 = a - c;
|
---|
310 | v2 = p - c;
|
---|
311 |
|
---|
312 | dot[2] = v1.crossProduct(v2).dotProduct(normal);
|
---|
313 | zeroDot[2] = Math::RealEqual(dot[2], 0.0f, 1e-3);
|
---|
314 | // Compare signs (ignore colinear / coincident points)
|
---|
315 | if((!zeroDot[0] && !zeroDot[2]
|
---|
316 | && Math::Sign(dot[0]) != Math::Sign(dot[2])) ||
|
---|
317 | (!zeroDot[1] && !zeroDot[2]
|
---|
318 | && Math::Sign(dot[1]) != Math::Sign(dot[2])))
|
---|
319 | {
|
---|
320 | return false;
|
---|
321 | }
|
---|
322 |
|
---|
323 |
|
---|
324 | return true;
|
---|
325 | }
|
---|
326 | //-----------------------------------------------------------------------
|
---|
327 | bool Math::RealEqual( Real a, Real b, Real tolerance )
|
---|
328 | {
|
---|
329 | if (fabs(b-a) <= tolerance)
|
---|
330 | return true;
|
---|
331 | else
|
---|
332 | return false;
|
---|
333 | }
|
---|
334 |
|
---|
335 | //-----------------------------------------------------------------------
|
---|
336 | std::pair<bool, Real> Math::intersects(const Ray& ray, const Plane& plane)
|
---|
337 | {
|
---|
338 |
|
---|
339 | Real denom = plane.normal.dotProduct(ray.getDirection());
|
---|
340 | if (Math::Abs(denom) < std::numeric_limits<Real>::epsilon())
|
---|
341 | {
|
---|
342 | // Parallel
|
---|
343 | return std::pair<bool, Real>(false, 0);
|
---|
344 | }
|
---|
345 | else
|
---|
346 | {
|
---|
347 | Real nom = plane.normal.dotProduct(ray.getOrigin()) + plane.d;
|
---|
348 | Real t = -(nom/denom);
|
---|
349 | return std::pair<bool, Real>(t >= 0, t);
|
---|
350 | }
|
---|
351 |
|
---|
352 | }
|
---|
353 | //-----------------------------------------------------------------------
|
---|
354 | std::pair<bool, Real> Math::intersects(const Ray& ray,
|
---|
355 | const std::vector<Plane>& planes, bool normalIsOutside)
|
---|
356 | {
|
---|
357 | std::vector<Plane>::const_iterator planeit, planeitend;
|
---|
358 | planeitend = planes.end();
|
---|
359 | bool allInside = true;
|
---|
360 | std::pair<bool, Real> ret;
|
---|
361 | ret.first = false;
|
---|
362 | ret.second = 0.0f;
|
---|
363 |
|
---|
364 | // derive side
|
---|
365 | // NB we don't pass directly since that would require Plane::Side in
|
---|
366 | // interface, which results in recursive includes since Math is so fundamental
|
---|
367 | Plane::Side outside = normalIsOutside ? Plane::POSITIVE_SIDE : Plane::NEGATIVE_SIDE;
|
---|
368 |
|
---|
369 | for (planeit = planes.begin(); planeit != planeitend; ++planeit)
|
---|
370 | {
|
---|
371 | const Plane& plane = *planeit;
|
---|
372 | // is origin outside?
|
---|
373 | if (plane.getSide(ray.getOrigin()) == outside)
|
---|
374 | {
|
---|
375 | allInside = false;
|
---|
376 | // Test single plane
|
---|
377 | std::pair<bool, Real> planeRes =
|
---|
378 | ray.intersects(plane);
|
---|
379 | if (planeRes.first)
|
---|
380 | {
|
---|
381 | // Ok, we intersected
|
---|
382 | ret.first = true;
|
---|
383 | // Use the most distant result since convex volume
|
---|
384 | ret.second = std::max(ret.second, planeRes.second);
|
---|
385 | }
|
---|
386 | }
|
---|
387 | }
|
---|
388 |
|
---|
389 | if (allInside)
|
---|
390 | {
|
---|
391 | // Intersecting at 0 distance since inside the volume!
|
---|
392 | ret.first = true;
|
---|
393 | ret.second = 0.0f;
|
---|
394 | }
|
---|
395 |
|
---|
396 | return ret;
|
---|
397 | }
|
---|
398 | //-----------------------------------------------------------------------
|
---|
399 | std::pair<bool, Real> Math::intersects(const Ray& ray,
|
---|
400 | const std::list<Plane>& planes, bool normalIsOutside)
|
---|
401 | {
|
---|
402 | std::list<Plane>::const_iterator planeit, planeitend;
|
---|
403 | planeitend = planes.end();
|
---|
404 | bool allInside = true;
|
---|
405 | std::pair<bool, Real> ret;
|
---|
406 | ret.first = false;
|
---|
407 | ret.second = 0.0f;
|
---|
408 |
|
---|
409 | // derive side
|
---|
410 | // NB we don't pass directly since that would require Plane::Side in
|
---|
411 | // interface, which results in recursive includes since Math is so fundamental
|
---|
412 | Plane::Side outside = normalIsOutside ? Plane::POSITIVE_SIDE : Plane::NEGATIVE_SIDE;
|
---|
413 |
|
---|
414 | for (planeit = planes.begin(); planeit != planeitend; ++planeit)
|
---|
415 | {
|
---|
416 | const Plane& plane = *planeit;
|
---|
417 | // is origin outside?
|
---|
418 | if (plane.getSide(ray.getOrigin()) == outside)
|
---|
419 | {
|
---|
420 | allInside = false;
|
---|
421 | // Test single plane
|
---|
422 | std::pair<bool, Real> planeRes =
|
---|
423 | ray.intersects(plane);
|
---|
424 | if (planeRes.first)
|
---|
425 | {
|
---|
426 | // Ok, we intersected
|
---|
427 | ret.first = true;
|
---|
428 | // Use the most distant result since convex volume
|
---|
429 | ret.second = std::max(ret.second, planeRes.second);
|
---|
430 | }
|
---|
431 | }
|
---|
432 | }
|
---|
433 |
|
---|
434 | if (allInside)
|
---|
435 | {
|
---|
436 | // Intersecting at 0 distance since inside the volume!
|
---|
437 | ret.first = true;
|
---|
438 | ret.second = 0.0f;
|
---|
439 | }
|
---|
440 |
|
---|
441 | return ret;
|
---|
442 | }
|
---|
443 | //-----------------------------------------------------------------------
|
---|
444 | std::pair<bool, Real> Math::intersects(const Ray& ray, const Sphere& sphere,
|
---|
445 | bool discardInside)
|
---|
446 | {
|
---|
447 | const Vector3& raydir = ray.getDirection();
|
---|
448 | // Adjust ray origin relative to sphere center
|
---|
449 | const Vector3& rayorig = ray.getOrigin() - sphere.getCenter();
|
---|
450 | Real radius = sphere.getRadius();
|
---|
451 |
|
---|
452 | // Check origin inside first
|
---|
453 | if (rayorig.squaredLength() <= radius*radius && discardInside)
|
---|
454 | {
|
---|
455 | return std::pair<bool, Real>(true, 0);
|
---|
456 | }
|
---|
457 |
|
---|
458 | // Mmm, quadratics
|
---|
459 | // Build coeffs which can be used with std quadratic solver
|
---|
460 | // ie t = (-b +/- sqrt(b*b + 4ac)) / 2a
|
---|
461 | Real a = raydir.dotProduct(raydir);
|
---|
462 | Real b = 2 * rayorig.dotProduct(raydir);
|
---|
463 | Real c = rayorig.dotProduct(rayorig) - radius*radius;
|
---|
464 |
|
---|
465 | // Calc determinant
|
---|
466 | Real d = (b*b) - (4 * a * c);
|
---|
467 | if (d < 0)
|
---|
468 | {
|
---|
469 | // No intersection
|
---|
470 | return std::pair<bool, Real>(false, 0);
|
---|
471 | }
|
---|
472 | else
|
---|
473 | {
|
---|
474 | // BTW, if d=0 there is one intersection, if d > 0 there are 2
|
---|
475 | // But we only want the closest one, so that's ok, just use the
|
---|
476 | // '-' version of the solver
|
---|
477 | Real t = ( -b - Math::Sqrt(d) ) / (2 * a);
|
---|
478 | if (t < 0)
|
---|
479 | t = ( -b + Math::Sqrt(d) ) / (2 * a);
|
---|
480 | return std::pair<bool, Real>(true, t);
|
---|
481 | }
|
---|
482 |
|
---|
483 |
|
---|
484 | }
|
---|
485 | //-----------------------------------------------------------------------
|
---|
486 | std::pair<bool, Real> Math::intersects(const Ray& ray, const AxisAlignedBox& box)
|
---|
487 | {
|
---|
488 | if (box.isNull()) return std::pair<bool, Real>(false, 0);
|
---|
489 |
|
---|
490 | Real lowt = 0.0f;
|
---|
491 | Real t;
|
---|
492 | bool hit = false;
|
---|
493 | Vector3 hitpoint;
|
---|
494 | const Vector3& min = box.getMinimum();
|
---|
495 | const Vector3& max = box.getMaximum();
|
---|
496 | const Vector3& rayorig = ray.getOrigin();
|
---|
497 | const Vector3& raydir = ray.getDirection();
|
---|
498 |
|
---|
499 | // Check origin inside first
|
---|
500 | if ( rayorig > min && rayorig < max )
|
---|
501 | {
|
---|
502 | return std::pair<bool, Real>(true, 0);
|
---|
503 | }
|
---|
504 |
|
---|
505 | // Check each face in turn, only check closest 3
|
---|
506 | // Min x
|
---|
507 | if (rayorig.x < min.x && raydir.x > 0)
|
---|
508 | {
|
---|
509 | t = (min.x - rayorig.x) / raydir.x;
|
---|
510 | if (t > 0)
|
---|
511 | {
|
---|
512 | // Substitute t back into ray and check bounds and dist
|
---|
513 | hitpoint = rayorig + raydir * t;
|
---|
514 | if (hitpoint.y >= min.y && hitpoint.y <= max.y &&
|
---|
515 | hitpoint.z >= min.z && hitpoint.z <= max.z &&
|
---|
516 | (!hit || t < lowt))
|
---|
517 | {
|
---|
518 | hit = true;
|
---|
519 | lowt = t;
|
---|
520 | }
|
---|
521 | }
|
---|
522 | }
|
---|
523 | // Max x
|
---|
524 | if (rayorig.x > max.x && raydir.x < 0)
|
---|
525 | {
|
---|
526 | t = (max.x - rayorig.x) / raydir.x;
|
---|
527 | if (t > 0)
|
---|
528 | {
|
---|
529 | // Substitute t back into ray and check bounds and dist
|
---|
530 | hitpoint = rayorig + raydir * t;
|
---|
531 | if (hitpoint.y >= min.y && hitpoint.y <= max.y &&
|
---|
532 | hitpoint.z >= min.z && hitpoint.z <= max.z &&
|
---|
533 | (!hit || t < lowt))
|
---|
534 | {
|
---|
535 | hit = true;
|
---|
536 | lowt = t;
|
---|
537 | }
|
---|
538 | }
|
---|
539 | }
|
---|
540 | // Min y
|
---|
541 | if (rayorig.y < min.y && raydir.y > 0)
|
---|
542 | {
|
---|
543 | t = (min.y - rayorig.y) / raydir.y;
|
---|
544 | if (t > 0)
|
---|
545 | {
|
---|
546 | // Substitute t back into ray and check bounds and dist
|
---|
547 | hitpoint = rayorig + raydir * t;
|
---|
548 | if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
|
---|
549 | hitpoint.z >= min.z && hitpoint.z <= max.z &&
|
---|
550 | (!hit || t < lowt))
|
---|
551 | {
|
---|
552 | hit = true;
|
---|
553 | lowt = t;
|
---|
554 | }
|
---|
555 | }
|
---|
556 | }
|
---|
557 | // Max y
|
---|
558 | if (rayorig.y > max.y && raydir.y < 0)
|
---|
559 | {
|
---|
560 | t = (max.y - rayorig.y) / raydir.y;
|
---|
561 | if (t > 0)
|
---|
562 | {
|
---|
563 | // Substitute t back into ray and check bounds and dist
|
---|
564 | hitpoint = rayorig + raydir * t;
|
---|
565 | if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
|
---|
566 | hitpoint.z >= min.z && hitpoint.z <= max.z &&
|
---|
567 | (!hit || t < lowt))
|
---|
568 | {
|
---|
569 | hit = true;
|
---|
570 | lowt = t;
|
---|
571 | }
|
---|
572 | }
|
---|
573 | }
|
---|
574 | // Min z
|
---|
575 | if (rayorig.z < min.z && raydir.z > 0)
|
---|
576 | {
|
---|
577 | t = (min.z - rayorig.z) / raydir.z;
|
---|
578 | if (t > 0)
|
---|
579 | {
|
---|
580 | // Substitute t back into ray and check bounds and dist
|
---|
581 | hitpoint = rayorig + raydir * t;
|
---|
582 | if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
|
---|
583 | hitpoint.y >= min.y && hitpoint.y <= max.y &&
|
---|
584 | (!hit || t < lowt))
|
---|
585 | {
|
---|
586 | hit = true;
|
---|
587 | lowt = t;
|
---|
588 | }
|
---|
589 | }
|
---|
590 | }
|
---|
591 | // Max z
|
---|
592 | if (rayorig.z > max.z && raydir.z < 0)
|
---|
593 | {
|
---|
594 | t = (max.z - rayorig.z) / raydir.z;
|
---|
595 | if (t > 0)
|
---|
596 | {
|
---|
597 | // Substitute t back into ray and check bounds and dist
|
---|
598 | hitpoint = rayorig + raydir * t;
|
---|
599 | if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
|
---|
600 | hitpoint.y >= min.y && hitpoint.y <= max.y &&
|
---|
601 | (!hit || t < lowt))
|
---|
602 | {
|
---|
603 | hit = true;
|
---|
604 | lowt = t;
|
---|
605 | }
|
---|
606 | }
|
---|
607 | }
|
---|
608 |
|
---|
609 | return std::pair<bool, Real>(hit, lowt);
|
---|
610 |
|
---|
611 | }
|
---|
612 | //-----------------------------------------------------------------------
|
---|
613 | bool Math::intersects(const Ray& ray, const AxisAlignedBox& box,
|
---|
614 | Real* d1, Real* d2)
|
---|
615 | {
|
---|
616 | if (box.isNull())
|
---|
617 | return false;
|
---|
618 |
|
---|
619 | const Vector3& min = box.getMinimum();
|
---|
620 | const Vector3& max = box.getMaximum();
|
---|
621 | const Vector3& rayorig = ray.getOrigin();
|
---|
622 | const Vector3& raydir = ray.getDirection();
|
---|
623 |
|
---|
624 | Vector3 absDir;
|
---|
625 | absDir[0] = Math::Abs(raydir[0]);
|
---|
626 | absDir[1] = Math::Abs(raydir[1]);
|
---|
627 | absDir[2] = Math::Abs(raydir[2]);
|
---|
628 |
|
---|
629 | // Sort the axis, ensure check minimise floating error axis first
|
---|
630 | int imax = 0, imid = 1, imin = 2;
|
---|
631 | if (absDir[0] < absDir[2])
|
---|
632 | {
|
---|
633 | imax = 2;
|
---|
634 | imin = 0;
|
---|
635 | }
|
---|
636 | if (absDir[1] < absDir[imin])
|
---|
637 | {
|
---|
638 | imid = imin;
|
---|
639 | imin = 1;
|
---|
640 | }
|
---|
641 | else if (absDir[1] > absDir[imax])
|
---|
642 | {
|
---|
643 | imid = imax;
|
---|
644 | imax = 1;
|
---|
645 | }
|
---|
646 |
|
---|
647 | Real start = 0, end = Math::POS_INFINITY;
|
---|
648 |
|
---|
649 | #define _CALC_AXIS(i) \
|
---|
650 | do { \
|
---|
651 | Real denom = 1 / raydir[i]; \
|
---|
652 | Real newstart = (min[i] - rayorig[i]) * denom; \
|
---|
653 | Real newend = (max[i] - rayorig[i]) * denom; \
|
---|
654 | if (newstart > newend) std::swap(newstart, newend); \
|
---|
655 | if (newstart > end || newend < start) return false; \
|
---|
656 | if (newstart > start) start = newstart; \
|
---|
657 | if (newend < end) end = newend; \
|
---|
658 | } while(0)
|
---|
659 |
|
---|
660 | // Check each axis in turn
|
---|
661 |
|
---|
662 | _CALC_AXIS(imax);
|
---|
663 |
|
---|
664 | if (absDir[imid] < std::numeric_limits<Real>::epsilon())
|
---|
665 | {
|
---|
666 | // Parallel with middle and minimise axis, check bounds only
|
---|
667 | if (rayorig[imid] < min[imid] || rayorig[imid] > max[imid] ||
|
---|
668 | rayorig[imin] < min[imin] || rayorig[imin] > max[imin])
|
---|
669 | return false;
|
---|
670 | }
|
---|
671 | else
|
---|
672 | {
|
---|
673 | _CALC_AXIS(imid);
|
---|
674 |
|
---|
675 | if (absDir[imin] < std::numeric_limits<Real>::epsilon())
|
---|
676 | {
|
---|
677 | // Parallel with minimise axis, check bounds only
|
---|
678 | if (rayorig[imin] < min[imin] || rayorig[imin] > max[imin])
|
---|
679 | return false;
|
---|
680 | }
|
---|
681 | else
|
---|
682 | {
|
---|
683 | _CALC_AXIS(imin);
|
---|
684 | }
|
---|
685 | }
|
---|
686 | #undef _CALC_AXIS
|
---|
687 |
|
---|
688 | if (d1) *d1 = start;
|
---|
689 | if (d2) *d2 = end;
|
---|
690 |
|
---|
691 | return true;
|
---|
692 | }
|
---|
693 | //-----------------------------------------------------------------------
|
---|
694 | std::pair<bool, Real> Math::intersects(const Ray& ray, const Vector3& a,
|
---|
695 | const Vector3& b, const Vector3& c, const Vector3& normal,
|
---|
696 | bool positiveSide, bool negativeSide)
|
---|
697 | {
|
---|
698 | //
|
---|
699 | // Calculate intersection with plane.
|
---|
700 | //
|
---|
701 | Real t;
|
---|
702 | {
|
---|
703 | Real denom = normal.dotProduct(ray.getDirection());
|
---|
704 |
|
---|
705 | // Check intersect side
|
---|
706 | if (denom > + std::numeric_limits<Real>::epsilon())
|
---|
707 | {
|
---|
708 | if (!negativeSide)
|
---|
709 | return std::pair<bool, Real>(false, 0);
|
---|
710 | }
|
---|
711 | else if (denom < - std::numeric_limits<Real>::epsilon())
|
---|
712 | {
|
---|
713 | if (!positiveSide)
|
---|
714 | return std::pair<bool, Real>(false, 0);
|
---|
715 | }
|
---|
716 | else
|
---|
717 | {
|
---|
718 | // Parallel or triangle area is close to zero when
|
---|
719 | // the plane normal not normalised.
|
---|
720 | return std::pair<bool, Real>(false, 0);
|
---|
721 | }
|
---|
722 |
|
---|
723 | t = normal.dotProduct(a - ray.getOrigin()) / denom;
|
---|
724 |
|
---|
725 | if (t < 0)
|
---|
726 | {
|
---|
727 | // Intersection is behind origin
|
---|
728 | return std::pair<bool, Real>(false, 0);
|
---|
729 | }
|
---|
730 | }
|
---|
731 |
|
---|
732 | //
|
---|
733 | // Calculate the largest area projection plane in X, Y or Z.
|
---|
734 | //
|
---|
735 | size_t i0, i1;
|
---|
736 | {
|
---|
737 | Real n0 = Math::Abs(normal[0]);
|
---|
738 | Real n1 = Math::Abs(normal[1]);
|
---|
739 | Real n2 = Math::Abs(normal[2]);
|
---|
740 |
|
---|
741 | i0 = 1; i1 = 2;
|
---|
742 | if (n1 > n2)
|
---|
743 | {
|
---|
744 | if (n1 > n0) i0 = 0;
|
---|
745 | }
|
---|
746 | else
|
---|
747 | {
|
---|
748 | if (n2 > n0) i1 = 0;
|
---|
749 | }
|
---|
750 | }
|
---|
751 |
|
---|
752 | //
|
---|
753 | // Check the intersection point is inside the triangle.
|
---|
754 | //
|
---|
755 | {
|
---|
756 | Real u1 = b[i0] - a[i0];
|
---|
757 | Real v1 = b[i1] - a[i1];
|
---|
758 | Real u2 = c[i0] - a[i0];
|
---|
759 | Real v2 = c[i1] - a[i1];
|
---|
760 | Real u0 = t * ray.getDirection()[i0] + ray.getOrigin()[i0] - a[i0];
|
---|
761 | Real v0 = t * ray.getDirection()[i1] + ray.getOrigin()[i1] - a[i1];
|
---|
762 |
|
---|
763 | Real alpha = u0 * v2 - u2 * v0;
|
---|
764 | Real beta = u1 * v0 - u0 * v1;
|
---|
765 | Real area = u1 * v2 - u2 * v1;
|
---|
766 |
|
---|
767 | // epsilon to avoid float precision error
|
---|
768 | const Real EPSILON = 1e-3f;
|
---|
769 |
|
---|
770 | Real tolerance = - EPSILON * area;
|
---|
771 |
|
---|
772 | if (area > 0)
|
---|
773 | {
|
---|
774 | if (alpha < tolerance || beta < tolerance || alpha+beta > area-tolerance)
|
---|
775 | return std::pair<bool, Real>(false, 0);
|
---|
776 | }
|
---|
777 | else
|
---|
778 | {
|
---|
779 | if (alpha > tolerance || beta > tolerance || alpha+beta < area-tolerance)
|
---|
780 | return std::pair<bool, Real>(false, 0);
|
---|
781 | }
|
---|
782 | }
|
---|
783 |
|
---|
784 | return std::pair<bool, Real>(true, t);
|
---|
785 | }
|
---|
786 | //-----------------------------------------------------------------------
|
---|
787 | std::pair<bool, Real> Math::intersects(const Ray& ray, const Vector3& a,
|
---|
788 | const Vector3& b, const Vector3& c,
|
---|
789 | bool positiveSide, bool negativeSide)
|
---|
790 | {
|
---|
791 | Vector3 normal = calculateBasicFaceNormalWithoutNormalize(a, b, c);
|
---|
792 | return intersects(ray, a, b, c, normal, positiveSide, negativeSide);
|
---|
793 | }
|
---|
794 | //-----------------------------------------------------------------------
|
---|
795 | bool Math::intersects(const Sphere& sphere, const AxisAlignedBox& box)
|
---|
796 | {
|
---|
797 | if (box.isNull()) return false;
|
---|
798 |
|
---|
799 | // Use splitting planes
|
---|
800 | const Vector3& center = sphere.getCenter();
|
---|
801 | Real radius = sphere.getRadius();
|
---|
802 | const Vector3& min = box.getMinimum();
|
---|
803 | const Vector3& max = box.getMaximum();
|
---|
804 |
|
---|
805 | // just test facing planes, early fail if sphere is totally outside
|
---|
806 | if (center.x < min.x &&
|
---|
807 | min.x - center.x > radius)
|
---|
808 | {
|
---|
809 | return false;
|
---|
810 | }
|
---|
811 | if (center.x > max.x &&
|
---|
812 | center.x - max.x > radius)
|
---|
813 | {
|
---|
814 | return false;
|
---|
815 | }
|
---|
816 |
|
---|
817 | if (center.y < min.y &&
|
---|
818 | min.y - center.y > radius)
|
---|
819 | {
|
---|
820 | return false;
|
---|
821 | }
|
---|
822 | if (center.y > max.y &&
|
---|
823 | center.y - max.y > radius)
|
---|
824 | {
|
---|
825 | return false;
|
---|
826 | }
|
---|
827 |
|
---|
828 | if (center.z < min.z &&
|
---|
829 | min.z - center.z > radius)
|
---|
830 | {
|
---|
831 | return false;
|
---|
832 | }
|
---|
833 | if (center.z > max.z &&
|
---|
834 | center.z - max.z > radius)
|
---|
835 | {
|
---|
836 | return false;
|
---|
837 | }
|
---|
838 |
|
---|
839 | // Must intersect
|
---|
840 | return true;
|
---|
841 |
|
---|
842 | }
|
---|
843 | //-----------------------------------------------------------------------
|
---|
844 | bool Math::intersects(const Plane& plane, const AxisAlignedBox& box)
|
---|
845 | {
|
---|
846 | if (box.isNull()) return false;
|
---|
847 |
|
---|
848 | // Get corners of the box
|
---|
849 | const Vector3* pCorners = box.getAllCorners();
|
---|
850 |
|
---|
851 |
|
---|
852 | // Test which side of the plane the corners are
|
---|
853 | // Intersection occurs when at least one corner is on the
|
---|
854 | // opposite side to another
|
---|
855 | Plane::Side lastSide = plane.getSide(pCorners[0]);
|
---|
856 | for (int corner = 1; corner < 8; ++corner)
|
---|
857 | {
|
---|
858 | if (plane.getSide(pCorners[corner]) != lastSide)
|
---|
859 | {
|
---|
860 | return true;
|
---|
861 | }
|
---|
862 | }
|
---|
863 |
|
---|
864 | return false;
|
---|
865 | }
|
---|
866 | //-----------------------------------------------------------------------
|
---|
867 | bool Math::intersects(const Sphere& sphere, const Plane& plane)
|
---|
868 | {
|
---|
869 | return (
|
---|
870 | Math::Abs(plane.normal.dotProduct(sphere.getCenter()))
|
---|
871 | <= sphere.getRadius() );
|
---|
872 | }
|
---|
873 | //-----------------------------------------------------------------------
|
---|
874 | Vector3 Math::calculateTangentSpaceVector(
|
---|
875 | const Vector3& position1, const Vector3& position2, const Vector3& position3,
|
---|
876 | Real u1, Real v1, Real u2, Real v2, Real u3, Real v3)
|
---|
877 | {
|
---|
878 | //side0 is the vector along one side of the triangle of vertices passed in,
|
---|
879 | //and side1 is the vector along another side. Taking the cross product of these returns the normal.
|
---|
880 | Vector3 side0 = position1 - position2;
|
---|
881 | Vector3 side1 = position3 - position1;
|
---|
882 | //Calculate face normal
|
---|
883 | Vector3 normal = side1.crossProduct(side0);
|
---|
884 | normal.normalise();
|
---|
885 | //Now we use a formula to calculate the tangent.
|
---|
886 | Real deltaV0 = v1 - v2;
|
---|
887 | Real deltaV1 = v3 - v1;
|
---|
888 | Vector3 tangent = deltaV1 * side0 - deltaV0 * side1;
|
---|
889 | tangent.normalise();
|
---|
890 | //Calculate binormal
|
---|
891 | Real deltaU0 = u1 - u2;
|
---|
892 | Real deltaU1 = u3 - u1;
|
---|
893 | Vector3 binormal = deltaU1 * side0 - deltaU0 * side1;
|
---|
894 | binormal.normalise();
|
---|
895 | //Now, we take the cross product of the tangents to get a vector which
|
---|
896 | //should point in the same direction as our normal calculated above.
|
---|
897 | //If it points in the opposite direction (the dot product between the normals is less than zero),
|
---|
898 | //then we need to reverse the s and t tangents.
|
---|
899 | //This is because the triangle has been mirrored when going from tangent space to object space.
|
---|
900 | //reverse tangents if necessary
|
---|
901 | Vector3 tangentCross = tangent.crossProduct(binormal);
|
---|
902 | if (tangentCross.dotProduct(normal) < 0.0f)
|
---|
903 | {
|
---|
904 | tangent = -tangent;
|
---|
905 | binormal = -binormal;
|
---|
906 | }
|
---|
907 |
|
---|
908 | return tangent;
|
---|
909 |
|
---|
910 | }
|
---|
911 | //-----------------------------------------------------------------------
|
---|
912 | Matrix4 Math::buildReflectionMatrix(const Plane& p)
|
---|
913 | {
|
---|
914 | return Matrix4(
|
---|
915 | -2 * p.normal.x * p.normal.x + 1, -2 * p.normal.x * p.normal.y, -2 * p.normal.x * p.normal.z, -2 * p.normal.x * p.d,
|
---|
916 | -2 * p.normal.y * p.normal.x, -2 * p.normal.y * p.normal.y + 1, -2 * p.normal.y * p.normal.z, -2 * p.normal.y * p.d,
|
---|
917 | -2 * p.normal.z * p.normal.x, -2 * p.normal.z * p.normal.y, -2 * p.normal.z * p.normal.z + 1, -2 * p.normal.z * p.d,
|
---|
918 | 0, 0, 0, 1);
|
---|
919 | }
|
---|
920 | //-----------------------------------------------------------------------
|
---|
921 | Vector4 Math::calculateFaceNormal(const Vector3& v1, const Vector3& v2, const Vector3& v3)
|
---|
922 | {
|
---|
923 | Vector3 normal = calculateBasicFaceNormal(v1, v2, v3);
|
---|
924 | // Now set up the w (distance of tri from origin
|
---|
925 | return Vector4(normal.x, normal.y, normal.z, -(normal.dotProduct(v1)));
|
---|
926 | }
|
---|
927 | //-----------------------------------------------------------------------
|
---|
928 | Vector3 Math::calculateBasicFaceNormal(const Vector3& v1, const Vector3& v2, const Vector3& v3)
|
---|
929 | {
|
---|
930 | Vector3 normal = (v2 - v1).crossProduct(v3 - v1);
|
---|
931 | normal.normalise();
|
---|
932 | return normal;
|
---|
933 | }
|
---|
934 | //-----------------------------------------------------------------------
|
---|
935 | Vector4 Math::calculateFaceNormalWithoutNormalize(const Vector3& v1, const Vector3& v2, const Vector3& v3)
|
---|
936 | {
|
---|
937 | Vector3 normal = calculateBasicFaceNormalWithoutNormalize(v1, v2, v3);
|
---|
938 | // Now set up the w (distance of tri from origin)
|
---|
939 | return Vector4(normal.x, normal.y, normal.z, -(normal.dotProduct(v1)));
|
---|
940 | }
|
---|
941 | //-----------------------------------------------------------------------
|
---|
942 | Vector3 Math::calculateBasicFaceNormalWithoutNormalize(const Vector3& v1, const Vector3& v2, const Vector3& v3)
|
---|
943 | {
|
---|
944 | Vector3 normal = (v2 - v1).crossProduct(v3 - v1);
|
---|
945 | return normal;
|
---|
946 | }
|
---|
947 | }
|
---|