2 Cafu Engine, http://www.cafu.de/
3 Copyright (c) Carsten Fuchs and other contributors.
4 This project is licensed under the terms of the MIT license.
11 #include "BoundingBox.hpp"
14 // This ctor is required for example in order to be able to put BoundingBox3Ts into ArrayTs etc.
15 template<class T
> BoundingBox3T
<T
>::BoundingBox3T()
17 const T MaxVal
=std::numeric_limits
<T
>::max();
19 Min
=Vector3T
<T
>( MaxVal
, MaxVal
, MaxVal
);
20 Max
=Vector3T
<T
>(-MaxVal
, -MaxVal
, -MaxVal
);
26 template<class T
> BoundingBox3T
<T
>::BoundingBox3T(const Vector3T
<T
>& A
)
34 template<class T
> BoundingBox3T
<T
>::BoundingBox3T(const Vector3T
<T
>& A
, const Vector3T
<T
>& B
)
44 template<class T
> BoundingBox3T
<T
>::BoundingBox3T(const ArrayT
< Vector3T
<T
> >& A
)
46 const T MaxVal
=std::numeric_limits
<T
>::max();
48 Min
=Vector3T
<T
>( MaxVal
, MaxVal
, MaxVal
);
49 Max
=Vector3T
<T
>(-MaxVal
, -MaxVal
, -MaxVal
);
53 for (unsigned long VertexNr
=0; VertexNr
<A
.Size(); VertexNr
++)
58 template<class T
> bool BoundingBox3T
<T
>::IsValid() const
60 const T MaxVal
=std::numeric_limits
<T
>::max();
62 // Uninitialized bounding-boxes are valid.
63 if (Min
==Vector3T
<T
>( MaxVal
, MaxVal
, MaxVal
) &&
64 Max
==Vector3T
<T
>(-MaxVal
, -MaxVal
, -MaxVal
)) return true;
66 // Properly initialized bounding-boxes must have a non-negative volume.
67 return Min
.x
<=Max
.x
&& Min
.y
<=Max
.y
&& Min
.z
<=Max
.z
;
71 template<class T
> bool BoundingBox3T
<T
>::IsInited() const
79 template<class T
> void BoundingBox3T
<T
>::Insert(const Vector3T
<T
>& A
)
81 if (A
.x
<Min
.x
) Min
.x
=A
.x
;
82 if (A
.y
<Min
.y
) Min
.y
=A
.y
;
83 if (A
.z
<Min
.z
) Min
.z
=A
.z
;
85 if (A
.x
>Max
.x
) Max
.x
=A
.x
;
86 if (A
.y
>Max
.y
) Max
.y
=A
.y
;
87 if (A
.z
>Max
.z
) Max
.z
=A
.z
;
93 template<class T
> void BoundingBox3T
<T
>::Insert(const ArrayT
< Vector3T
<T
> >& A
)
95 for (unsigned long VertexNr
=0; VertexNr
<A
.Size(); VertexNr
++)
100 template<class T
> void BoundingBox3T
<T
>::Insert(const BoundingBox3T
<T
>& BB
)
102 assert(BB
.IsInited());
111 template<class T
> void BoundingBox3T
<T
>::InsertValid(const BoundingBox3T
<T
>& BB
)
113 if (!BB
.IsInited()) return;
122 template<class T
> BoundingBox3T
<T
> BoundingBox3T
<T
>::GetOverallTranslationBox(const Vector3T
<T
>& Start
, const Vector3T
<T
>& End
) const
126 BoundingBox3T
<T
> ResultBB(*this);
128 // Move the ResultBB to the Start point.
132 // Insert the corresponding points near End.
133 ResultBB
.Insert(Min
+End
);
134 ResultBB
.Insert(Max
+End
);
140 template<class T
> bool BoundingBox3T
<T
>::Contains(const Vector3T
<T
>& A
) const
144 return A
.x
>Min
.x
&& A
.x
<Max
.x
&&
145 A
.y
>Min
.y
&& A
.y
<Max
.y
&&
146 A
.z
>Min
.z
&& A
.z
<Max
.z
;
150 template<class T
> bool BoundingBox3T
<T
>::Intersects(const BoundingBox3T
<T
>& BB
) const
153 assert(BB
.IsInited());
155 // This expression can easily be considered and verified in 2D on a sheet of paper!
156 return Min
.x
<BB
.Max
.x
&& Max
.x
>BB
.Min
.x
&&
157 Min
.y
<BB
.Max
.y
&& Max
.y
>BB
.Min
.y
&&
158 Min
.z
<BB
.Max
.z
&& Max
.z
>BB
.Min
.z
;
162 template<class T
> bool BoundingBox3T
<T
>::IntersectsOrTouches(const BoundingBox3T
<T
>& BB
) const
165 assert(BB
.IsInited());
167 // This expression can easily be considered and verified in 2D on a sheet of paper!
168 return Min
.x
<=BB
.Max
.x
&& Max
.x
>=BB
.Min
.x
&&
169 Min
.y
<=BB
.Max
.y
&& Max
.y
>=BB
.Min
.y
&&
170 Min
.z
<=BB
.Max
.z
&& Max
.z
>=BB
.Min
.z
;
174 template<class T
> typename BoundingBox3T
<T
>::SideT BoundingBox3T
<T
>::WhatSide(const Plane3T
<T
>& P
, const T Epsilon
) const
178 Vector3T
<T
> VertNear
;
181 // We can determine the vertex that is nearest and the vertex that is farthest
182 // from P by evaluating the sign of the components of the normal vector of P.
216 if (P
.GetDistance(VertNear
)>= Epsilon
) return Front
;
217 if (P
.GetDistance(VertFar
)<=-Epsilon
) return Back
;
223 template<class T
> typename BoundingBox3T
<T
>::SideT BoundingBox3T
<T
>::WhatSide_OLD(const Plane3T
<T
>& P
, const T Epsilon
) const
228 const Vector3T
<T
> Center
=(Min
+Max
)*0.5f
;
229 const T DistC
=P
.GetDistance(Center
);
230 const T Dist2
=fabs((Max
.x
-Center
.x
)*P
.Normal
.x
) +
231 fabs((Max
.y
-Center
.y
)*P
.Normal
.y
) +
232 fabs((Max
.z
-Center
.z
)*P
.Normal
.z
);
235 // "Wie weit muß ich eine Ebene, die den Normalenvektor P.Normal hat und Center enthält, parallel verschieben
236 // (entlang ihres Normalvektors), um zu der in dieser Richtung liegenden äußersten Ecke der BB zu gelangen?"
237 if (DistC
-Dist2
>= Epsilon
) return Front
;
238 if (DistC
+Dist2
<=-Epsilon
) return Back
;
242 Vector3T
<T
> VertBB
[8]={ Min
, Vector3T
<T
>(Min
.x
, Min
.y
, Max
.z
),
243 Vector3T
<T
>(Min
.x
, Max
.y
, Min
.z
), Vector3T
<T
>(Min
.x
, Max
.y
, Max
.z
),
244 Vector3T
<T
>(Max
.x
, Min
.y
, Min
.z
), Vector3T
<T
>(Max
.x
, Min
.y
, Max
.z
),
245 Vector3T
<T
>(Max
.x
, Max
.y
, Min
.z
), Max
};
250 for (unsigned long Nr
=0; Nr
<8; Nr
++)
252 if (P
.GetDistance(VertBB
[Nr
])>0.0) VertFront
= 1;
256 return SideT(VertFront
+VertBack
);
261 template<class T
> T BoundingBox3T
<T
>::GetDistance(const Plane3T
<T
>& P
) const
266 Vector3T
<T
> VertNear
;
269 // We can determine the vertex that is nearest and the vertex that is farthest
270 // from P by evaluating the sign of the components of the normal vector of P.
305 Dist
=P
.GetDistance(VertNear
); if (Dist
>0) return Dist
;
306 Dist
=P
.GetDistance(VertFar
); if (Dist
<0) return Dist
;
310 const Vector3T
<T
> Center
=(Min
+Max
)*0.5f
;
311 const T DistC
=P
.GetDistance(Center
);
312 const T Dist2
=fabs((Max
.x
-Center
.x
)*P
.Normal
.x
) +
313 fabs((Max
.y
-Center
.y
)*P
.Normal
.y
) +
314 fabs((Max
.z
-Center
.z
)*P
.Normal
.z
);
317 // "Wie weit muß ich eine Ebene, die den Normalenvektor P.Normal hat und Center enthält, parallel verschieben
318 // (entlang ihres Normalvektors), um zu der in dieser Richtung liegenden äußersten Ecke der BB zu gelangen?"
319 if (DistC
-Dist2
>0) return DistC
-Dist2
;
320 if (DistC
+Dist2
<0) return DistC
+Dist2
;
327 #if defined(_MSC_VER)
328 #pragma warning(push)
329 #pragma warning(disable: 4723) // potential divide by 0
332 // Algorithm derived from Amy Williams et al., "An Efficient and Robust Ray-Box Intersection Algorithm", http://www.cs.utah.edu/~awilliam/box/.
333 template<class T
> bool BoundingBox3T
<T
>::TraceRay(const Vector3T
<T
>& RayOrigin
, const Vector3T
<T
>& RayDir
, T
& Fraction
) const
337 // The RayDir components can be 0, potentially causing a division by 0.
338 // This is perfectly intentional here as described in the above paper.
339 const Vector3T
<T
> OneDivRayDir(1/RayDir
.x
, 1/RayDir
.y
, 1/RayDir
.z
);
340 const bool RayDirSigns
[3]={ RayDir
.x
<0, RayDir
.y
<0, RayDir
.z
<0 };
342 T tmin
=((RayDirSigns
[0] ? Max
: Min
).x
- RayOrigin
.x
) * OneDivRayDir
.x
;
343 T tmax
=((RayDirSigns
[0] ? Min
: Max
).x
- RayOrigin
.x
) * OneDivRayDir
.x
;
344 const T tymin
=((RayDirSigns
[1] ? Max
: Min
).y
- RayOrigin
.y
) * OneDivRayDir
.y
;
345 const T tymax
=((RayDirSigns
[1] ? Min
: Max
).y
- RayOrigin
.y
) * OneDivRayDir
.y
;
347 if ((tmin
>tymax
) || (tymin
>tmax
)) return false;
348 if (tymin
>tmin
) tmin
=tymin
;
349 if (tymax
<tmax
) tmax
=tymax
;
351 const T tzmin
=((RayDirSigns
[2] ? Max
: Min
).z
- RayOrigin
.z
) * OneDivRayDir
.z
;
352 const T tzmax
=((RayDirSigns
[2] ? Min
: Max
).z
- RayOrigin
.z
) * OneDivRayDir
.z
;
354 if ((tmin
>tzmax
) || (tzmin
>tmax
)) return false;
355 if (tzmin
>tmin
) tmin
=tzmin
;
356 if (tzmax
<tmax
) tmax
=tzmax
;
358 if (tmin
<0) return false; // Ray starts in or already "out of" bounding box.
363 #if defined(_MSC_VER)
368 template<class T
> void BoundingBox3T
<T
>::GetCornerVertices(Vector3T
<T
>* Vertices
) const
373 Vertices
[1]=Vector3T
<T
>(Min
.x
, Min
.y
, Max
.z
);
374 Vertices
[2]=Vector3T
<T
>(Min
.x
, Max
.y
, Min
.z
);
375 Vertices
[3]=Vector3T
<T
>(Min
.x
, Max
.y
, Max
.z
);
376 Vertices
[4]=Vector3T
<T
>(Max
.x
, Min
.y
, Min
.z
);
377 Vertices
[5]=Vector3T
<T
>(Max
.x
, Min
.y
, Max
.z
);
378 Vertices
[6]=Vector3T
<T
>(Max
.x
, Max
.y
, Min
.z
);
383 template<class T
> ArrayT
< BoundingBox3T
<T
> > BoundingBox3T
<T
>::GetSplits(const Plane3T
<T
>& SplitPlane
, const T PlaneThickness
) const
387 Vector3T
<T
> VertBB
[8]={ Min
, Vector3T
<T
>(Min
.x
, Min
.y
, Max
.z
),
388 Vector3T
<T
>(Min
.x
, Max
.y
, Min
.z
), Vector3T
<T
>(Min
.x
, Max
.y
, Max
.z
),
389 Vector3T
<T
>(Max
.x
, Min
.y
, Min
.z
), Vector3T
<T
>(Max
.x
, Min
.y
, Max
.z
),
390 Vector3T
<T
>(Max
.x
, Max
.y
, Min
.z
), Max
};
393 ArrayT
< Vector3T
<T
> > VertFront
;
394 ArrayT
< Vector3T
<T
> > VertBack
;
395 ArrayT
< Vector3T
<T
> > VertOn
;
397 const char Edge
[12][2]={ {0,1}, {0,2}, {0,4}, {7,3}, {7,5}, {7,6}, {1,3}, {1,5}, {6,2}, {6,4}, {4,5}, {2,3} };
399 for (unsigned long Nr
=0; Nr
<8; Nr
++)
401 const double Dist
=SplitPlane
.GetDistance(VertBB
[Nr
]);
403 if (Dist
> PlaneThickness
) { VertFront
.PushBack(VertBB
[Nr
]); Side
[Nr
]= 1; }
404 else if (Dist
<-PlaneThickness
) { VertBack
.PushBack(VertBB
[Nr
]); Side
[Nr
]=-1; }
405 else { VertOn
.PushBack(VertBB
[Nr
]); Side
[Nr
]= 0; }
408 for (unsigned long Nr
=0; Nr
<12; Nr
++)
413 if (Side
[v1
] && Side
[v1
]==-Side
[v2
]) VertOn
.PushBack(SplitPlane
.GetIntersection(VertBB
[v1
], VertBB
[v2
], 0));
417 ArrayT
< BoundingBox3T
<T
> > Result
;
419 Result
.PushBackEmpty(2);
421 if (VertFront
.Size())
423 Result
[0]=BoundingBox3T
<T
>(VertFront
);
424 Result
[0].Insert(VertOn
);
429 Result
[1]=BoundingBox3T
<T
>(VertBack
);
430 Result
[1].Insert(VertOn
);
437 template class BoundingBox3T
<float>;
438 template class BoundingBox3T
<double>;