Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
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.
8 : *
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.
13 : *
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/>.
16 : */
17 :
18 : /*
19 : Usually associated with the camera, there are 6 planes which define the
20 : view pyramid. But we allow more planes per frustum which may be used for
21 : portal rendering, where a portal may have 3 or more edges.
22 : */
23 :
24 : #include "precompiled.h"
25 :
26 : #include "Frustum.h"
27 :
28 : #include "maths/BoundingBoxAligned.h"
29 : #include "maths/MathUtil.h"
30 : #include "maths/Matrix3D.h"
31 :
32 37 : CFrustum::CFrustum()
33 : {
34 37 : m_NumPlanes = 0;
35 37 : }
36 :
37 38 : CFrustum::~CFrustum()
38 : {
39 38 : }
40 :
41 4 : void CFrustum::SetNumPlanes(size_t num)
42 : {
43 4 : m_NumPlanes = num;
44 :
45 4 : if (m_NumPlanes >= MAX_NUM_FRUSTUM_PLANES)
46 : {
47 0 : debug_warn(L"CFrustum::SetNumPlanes: Too many planes");
48 0 : m_NumPlanes = MAX_NUM_FRUSTUM_PLANES - 1;
49 : }
50 4 : }
51 :
52 0 : void CFrustum::AddPlane(const CPlane& plane)
53 : {
54 0 : if (m_NumPlanes >= MAX_NUM_FRUSTUM_PLANES)
55 : {
56 0 : debug_warn(L"CFrustum::AddPlane: Too many planes");
57 0 : return;
58 : }
59 :
60 0 : m_Planes[m_NumPlanes++] = plane;
61 : }
62 :
63 0 : void CFrustum::Transform(const CMatrix3D& m)
64 : {
65 0 : for (size_t i = 0; i < m_NumPlanes; ++i)
66 : {
67 0 : CVector3D n = m.Rotate(m_Planes[i].m_Norm);
68 0 : CVector3D p = m.Transform(m_Planes[i].m_Norm * -m_Planes[i].m_Dist);
69 0 : m_Planes[i].Set(n, p);
70 0 : m_Planes[i].Normalize();
71 : }
72 0 : }
73 :
74 0 : bool CFrustum::IsPointVisible(const CVector3D& point) const
75 : {
76 0 : for (size_t i = 0; i < m_NumPlanes; ++i)
77 : {
78 0 : if (m_Planes[i].IsPointOnBackSide(point))
79 0 : return false;
80 : }
81 :
82 0 : return true;
83 : }
84 :
85 0 : bool CFrustum::DoesSegmentIntersect(const CVector3D& startRef, const CVector3D& endRef) const
86 : {
87 0 : CVector3D start = startRef;
88 0 : CVector3D end = endRef;
89 :
90 0 : if (IsPointVisible(start) || IsPointVisible(end))
91 0 : return true;
92 :
93 0 : CVector3D intersect;
94 0 : for (size_t i = 0; i < m_NumPlanes; ++i)
95 : {
96 0 : if (m_Planes[i].FindLineSegIntersection(start, end, &intersect))
97 : {
98 0 : if (IsPointVisible(intersect))
99 0 : return true;
100 : }
101 : }
102 0 : return false;
103 : }
104 :
105 0 : bool CFrustum::IsSphereVisible(const CVector3D& center, float radius) const
106 : {
107 0 : for (size_t i = 0; i < m_NumPlanes; ++i)
108 : {
109 : // If none of the sphere is in front of the plane, then
110 : // it is outside the frustum.
111 0 : if (-m_Planes[i].DistanceToPlane(center) > radius)
112 0 : return false;
113 : }
114 :
115 0 : return true;
116 : }
117 :
118 0 : bool CFrustum::IsBoxVisible(const CVector3D& position, const CBoundingBoxAligned& bounds) const
119 : {
120 : // Basically for every plane we calculate the furthest point
121 : // in the box to that plane. If that point is beyond the plane
122 : // then the box is not visible.
123 0 : CVector3D farPoint;
124 0 : CVector3D minPoint = position + bounds[0];
125 0 : CVector3D maxPoint = position + bounds[1];
126 :
127 0 : for (size_t i = 0; i < m_NumPlanes; ++i)
128 : {
129 0 : farPoint.X = m_Planes[i].m_Norm.X > 0.0f ? maxPoint.X : minPoint.X;
130 0 : farPoint.Y = m_Planes[i].m_Norm.Y > 0.0f ? maxPoint.Y : minPoint.Y;
131 0 : farPoint.Z = m_Planes[i].m_Norm.Z > 0.0f ? maxPoint.Z : minPoint.Z;
132 :
133 0 : if (m_Planes[i].IsPointOnBackSide(farPoint))
134 0 : return false;
135 : }
136 :
137 0 : return true;
138 : }
139 :
140 0 : bool CFrustum::IsBoxVisible(const CBoundingBoxAligned& bounds) const
141 : {
142 : // Same as the previous one, but with the position = (0, 0, 0).
143 0 : CVector3D farPoint;
144 :
145 0 : for (size_t i = 0; i < m_NumPlanes; ++i)
146 : {
147 0 : farPoint.X = m_Planes[i].m_Norm.X > 0.0f ? bounds[1].X : bounds[0].X;
148 0 : farPoint.Y = m_Planes[i].m_Norm.Y > 0.0f ? bounds[1].Y : bounds[0].Y;
149 0 : farPoint.Z = m_Planes[i].m_Norm.Z > 0.0f ? bounds[1].Z : bounds[0].Z;
150 :
151 0 : if (m_Planes[i].IsPointOnBackSide(farPoint))
152 0 : return false;
153 : }
154 :
155 0 : return true;
156 3 : }
|