1 /* Copyright (C) 2021 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
19 * Axis-aligned bounding box
22 #include "precompiled.h"
24 #include "BoundingBoxAligned.h"
26 #include "maths/BoundingBoxOriented.h"
27 #include "maths/Brush.h"
28 #include "maths/Frustum.h"
29 #include "maths/Matrix3D.h"
33 const CBoundingBoxAligned
CBoundingBoxAligned::EMPTY
= CBoundingBoxAligned(); // initializes to an empty bound
35 ///////////////////////////////////////////////////////////////////////////////
36 // RayIntersect: intersect ray with this bound; return true
37 // if ray hits (and store entry and exit times), or false
39 // note: incoming ray direction must be normalised
40 bool CBoundingBoxAligned::RayIntersect(
41 const CVector3D
& origin
, const CVector3D
& dir
, float& tmin
, float& tmax
) const
48 if (origin
[0] < m_Data
[0][0] || origin
[0] > m_Data
[1][0])
52 tnear
= -std::numeric_limits
<float>::max();
53 tfar
= std::numeric_limits
<float>::max();
58 t1
= (m_Data
[0][0] - origin
[0]) / dir
[0];
59 t2
= (m_Data
[1][0] - origin
[0]) / dir
[0];
76 if (dir
[1] == 0 && (origin
[1] < m_Data
[0][1] || origin
[1] > m_Data
[1][1]))
80 t1
= (m_Data
[0][1] - origin
[1]) / dir
[1];
81 t2
= (m_Data
[1][1] - origin
[1]) / dir
[1];
98 if (tnear
> tfar
|| tfar
< 0)
102 if (dir
[2] == 0 && (origin
[2] < m_Data
[0][2] || origin
[2] > m_Data
[1][2]))
106 t1
= (m_Data
[0][2] - origin
[2]) / dir
[2];
107 t2
= (m_Data
[1][2] - origin
[2]) / dir
[2];
124 if (tnear
> tfar
|| tfar
< 0)
134 ///////////////////////////////////////////////////////////////////////////////
135 // SetEmpty: initialise this bound as empty
136 void CBoundingBoxAligned::SetEmpty()
138 m_Data
[0] = CVector3D::Max();
139 m_Data
[1] = CVector3D::Min();
142 ///////////////////////////////////////////////////////////////////////////////
143 // IsEmpty: tests whether this bound is empty
144 bool CBoundingBoxAligned::IsEmpty() const
146 return m_Data
[0] == CVector3D::Max() && m_Data
[1] == CVector3D::Min();
149 ///////////////////////////////////////////////////////////////////////////////
150 // Transform: transform this bound by given matrix; return transformed bound
151 // in 'result' parameter - slightly modified version of code in Graphic Gems
152 // (can't remember which one it was, though)
153 void CBoundingBoxAligned::Transform(const CMatrix3D
& m
, CBoundingBoxAligned
& result
) const
155 ENSURE(this != &result
);
157 for (int i
= 0; i
< 3; ++i
)
159 // handle translation
160 result
[0][i
] = result
[1][i
] = m(i
, 3);
162 // Now find the extreme points by considering the product of the
163 // min and max with each component of matrix
164 for (int j
= 0; j
< 3; ++j
)
166 float a
= m(i
, j
) * m_Data
[0][j
];
167 float b
= m(i
, j
) * m_Data
[1][j
];
178 void CBoundingBoxAligned::Transform(const CMatrix3D
& transform
, CBoundingBoxOriented
& result
) const
180 const CVector3D
& pMin
= m_Data
[0];
181 const CVector3D
& pMax
= m_Data
[1];
183 // the basis vectors of the OBB are the normalized versions of the transformed AABB basis vectors, which
184 // are the columns of the identity matrix, so the unnormalized OBB basis vectors are the transformation
186 CVector3D
u(transform
._11
, transform
._21
, transform
._31
);
187 CVector3D
v(transform
._12
, transform
._22
, transform
._32
);
188 CVector3D
w(transform
._13
, transform
._23
, transform
._33
);
190 // the half-sizes are scaled by whatever factor the AABB unit vectors end up scaled by
191 result
.m_HalfSizes
= CVector3D(
192 (pMax
.X
- pMin
.X
) / 2.f
* u
.Length(),
193 (pMax
.Y
- pMin
.Y
) / 2.f
* v
.Length(),
194 (pMax
.Z
- pMin
.Z
) / 2.f
* w
.Length()
201 result
.m_Basis
[0] = u
;
202 result
.m_Basis
[1] = v
;
203 result
.m_Basis
[2] = w
;
205 result
.m_Center
= transform
.Transform((pMax
+ pMin
) * 0.5f
);
208 ///////////////////////////////////////////////////////////////////////////////
209 // Intersect with the given frustum in a conservative manner
210 void CBoundingBoxAligned::IntersectFrustumConservative(const CFrustum
& frustum
)
212 // if this bound is empty, then the result must be empty (we should not attempt to intersect with
213 // a brush, may cause crashes due to the numeric representation of empty bounds -- see
214 // http://trac.wildfiregames.com/ticket/1027)
221 brush
.Intersect(frustum
, buf
);
226 ///////////////////////////////////////////////////////////////////////////////
227 CFrustum
CBoundingBoxAligned::ToFrustum() const
231 frustum
.SetNumPlanes(6);
233 // get the LEFT plane
234 frustum
[0].m_Norm
= CVector3D(1, 0, 0);
235 frustum
[0].m_Dist
= -m_Data
[0].X
;
237 // get the RIGHT plane
238 frustum
[1].m_Norm
= CVector3D(-1, 0, 0);
239 frustum
[1].m_Dist
= m_Data
[1].X
;
241 // get the BOTTOM plane
242 frustum
[2].m_Norm
= CVector3D(0, 1, 0);
243 frustum
[2].m_Dist
= -m_Data
[0].Y
;
246 frustum
[3].m_Norm
= CVector3D(0, -1, 0);
247 frustum
[3].m_Dist
= m_Data
[1].Y
;
249 // get the NEAR plane
250 frustum
[4].m_Norm
= CVector3D(0, 0, 1);
251 frustum
[4].m_Dist
= -m_Data
[0].Z
;
254 frustum
[5].m_Norm
= CVector3D(0, 0, -1);
255 frustum
[5].m_Dist
= m_Data
[1].Z
;
260 ///////////////////////////////////////////////////////////////////////////////
261 void CBoundingBoxAligned::Expand(float amount
)
263 m_Data
[0] -= CVector3D(amount
, amount
, amount
);
264 m_Data
[1] += CVector3D(amount
, amount
, amount
);
267 bool CBoundingBoxAligned::IsPointInside(const CVector3D
& point
) const
270 m_Data
[0].X
<= point
.X
&& point
.X
<= m_Data
[1].X
&&
271 m_Data
[0].Y
<= point
.Y
&& point
.Y
<= m_Data
[1].Y
&&
272 m_Data
[0].Z
<= point
.Z
&& point
.Z
<= m_Data
[1].Z
;