Diligent Engine API Reference
AdvancedMath.h
1 /* Copyright 2015-2018 Egor Yusov
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
12  *
13  * In no event and under no legal theory, whether in tort (including negligence),
14  * contract, or otherwise, unless required by applicable law (such as deliberate
15  * and grossly negligent acts) or agreed to in writing, shall any Contributor be
16  * liable for any damages, including any direct, indirect, special, incidental,
17  * or consequential damages of any character arising as a result of this License or
18  * out of the use or inability to use the software (including but not limited to damages
19  * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
20  * all other commercial damages or losses), even if such Contributor has been advised
21  * of the possibility of such damages.
22  */
23 
24 #pragma once
25 
26 #include "BasicMath.h"
27 
28 // Structure describing a plane
29 struct Plane3D
30 {
31  float3 Normal;
32  float Distance; //Distance from the coordinate system origin to the plane along the normal direction
33 };
34 
35 struct ViewFrustum
36 {
37  Plane3D LeftPlane, RightPlane, BottomPlane, TopPlane, NearPlane, FarPlane;
38 };
39 
40 struct ViewFrustumExt : public ViewFrustum
41 {
42  float3 FrustumCorners[8];
43 };
44 
45 // For OpenGL, matrix is still considered row-major. The only difference is that
46 // near clip plane is at -1, not 0.
47 inline void ExtractViewFrustumPlanesFromMatrix(const float4x4 &Matrix, ViewFrustum &Frustum, bool bIsDirectX)
48 {
49  // For more details, see Gribb G., Hartmann K., "Fast Extraction of Viewing Frustum Planes from the
50  // World-View-Projection Matrix" (the paper is available at
51  // http://gamedevs.org/uploads/fast-extraction-viewing-frustum-planes-from-world-view-projection-matrix.pdf)
52 
53  // Left clipping plane
54  Frustum.LeftPlane.Normal.x = Matrix._14 + Matrix._11;
55  Frustum.LeftPlane.Normal.y = Matrix._24 + Matrix._21;
56  Frustum.LeftPlane.Normal.z = Matrix._34 + Matrix._31;
57  Frustum.LeftPlane.Distance = Matrix._44 + Matrix._41;
58 
59  // Right clipping plane
60  Frustum.RightPlane.Normal.x = Matrix._14 - Matrix._11;
61  Frustum.RightPlane.Normal.y = Matrix._24 - Matrix._21;
62  Frustum.RightPlane.Normal.z = Matrix._34 - Matrix._31;
63  Frustum.RightPlane.Distance = Matrix._44 - Matrix._41;
64 
65  // Top clipping plane
66  Frustum.TopPlane.Normal.x = Matrix._14 - Matrix._12;
67  Frustum.TopPlane.Normal.y = Matrix._24 - Matrix._22;
68  Frustum.TopPlane.Normal.z = Matrix._34 - Matrix._32;
69  Frustum.TopPlane.Distance = Matrix._44 - Matrix._42;
70 
71  // Bottom clipping plane
72  Frustum.BottomPlane.Normal.x = Matrix._14 + Matrix._12;
73  Frustum.BottomPlane.Normal.y = Matrix._24 + Matrix._22;
74  Frustum.BottomPlane.Normal.z = Matrix._34 + Matrix._32;
75  Frustum.BottomPlane.Distance = Matrix._44 + Matrix._42;
76 
77  // Near clipping plane
78  if( bIsDirectX )
79  {
80  // 0 <= z <= w
81  Frustum.NearPlane.Normal.x = Matrix._13;
82  Frustum.NearPlane.Normal.y = Matrix._23;
83  Frustum.NearPlane.Normal.z = Matrix._33;
84  Frustum.NearPlane.Distance = Matrix._43;
85  }
86  else
87  {
88  // -w <= z <= w
89  Frustum.NearPlane.Normal.x = Matrix._14 + Matrix._13;
90  Frustum.NearPlane.Normal.y = Matrix._24 + Matrix._23;
91  Frustum.NearPlane.Normal.z = Matrix._34 + Matrix._33;
92  Frustum.NearPlane.Distance = Matrix._44 + Matrix._43;
93  }
94 
95  // Far clipping plane
96  Frustum.FarPlane.Normal.x = Matrix._14 - Matrix._13;
97  Frustum.FarPlane.Normal.y = Matrix._24 - Matrix._23;
98  Frustum.FarPlane.Normal.z = Matrix._34 - Matrix._33;
99  Frustum.FarPlane.Distance = Matrix._44 - Matrix._43;
100 }
101 
102 inline void ExtractViewFrustumPlanesFromMatrix(const float4x4 &Matrix, ViewFrustumExt &FrustumExt, bool bIsDirectX)
103 {
104  ExtractViewFrustumPlanesFromMatrix(Matrix, static_cast<ViewFrustum&>(FrustumExt), bIsDirectX);
105 
106  // Compute frustum corners
107  float4x4 InvMatrix = inverseMatrix(Matrix);
108 
109  float nearClipZ = bIsDirectX ? 0.f : -1.f;
110  static const float3 ProjSpaceCorners[] =
111  {
112  float3(-1,-1, nearClipZ),
113  float3( 1,-1, nearClipZ),
114  float3(-1, 1, nearClipZ),
115  float3( 1, 1, nearClipZ),
116 
117  float3(-1,-1, 1),
118  float3( 1,-1, 1),
119  float3(-1, 1, 1),
120  float3( 1, 1, 1),
121  };
122 
123  for(int i = 0; i < 8; ++i)
124  FrustumExt.FrustumCorners[i] = ProjSpaceCorners[i] * InvMatrix;
125 }
126 
127 struct BoundBox
128 {
129  // Order must not be changed!
130  float fMinX, fMaxX, fMinY, fMaxY, fMinZ, fMaxZ;
131 };
132 
133 enum class BoxVisibility
134 {
135  // Bounding box is guaranteed to be outside of the view frustum
136  // .
137  // . ' |
138  // . ' |
139  // | |
140  // . |
141  // ___ ' . |
142  // | | ' .
143  // |___|
144  //
145  Invisible,
146 
147  // Bounding box intersects the frustum
148  // .
149  // . ' |
150  // . ' |
151  // | |
152  // _.__ |
153  // | '|. |
154  // |____| ' .
155  //
156  Intersecting,
157 
158  // Bounding box is fully inside the view frustum
159  // .
160  // . ' |
161  // . '___ |
162  // | | | |
163  // . |___| |
164  // ' . |
165  // ' .
166  //
167  FullyVisible
168 };
169 
170 template<bool TestFullVisibility>
171 inline BoxVisibility GetBoxVisibilityAgainstPlane(const Plane3D& Plane, const BoundBox &Box)
172 {
173  const float3& Normal = Plane.Normal;
174 
175  float3 MaxPoint(
176  (Normal.x > 0) ? Box.fMaxX : Box.fMinX,
177  (Normal.y > 0) ? Box.fMaxY : Box.fMinY,
178  (Normal.z > 0) ? Box.fMaxZ : Box.fMinZ
179  );
180 
181  float DMax = dot( MaxPoint, Normal ) + Plane.Distance;
182 
183  if( DMax < 0 )
184  return BoxVisibility::Invisible;
185 
186  if (TestFullVisibility)
187  {
188  float3 MinPoint(
189  (Normal.x > 0) ? Box.fMinX : Box.fMaxX,
190  (Normal.y > 0) ? Box.fMinY : Box.fMaxY,
191  (Normal.z > 0) ? Box.fMinZ : Box.fMaxZ
192  );
193 
194  float DMin = dot(MinPoint, Normal) + Plane.Distance;
195 
196  if (DMin > 0)
197  return BoxVisibility::FullyVisible;
198  }
199 
200  return BoxVisibility::Intersecting;
201 }
202 
203 // Tests if bounding box is visible by the camera
204 template<bool TestFullVisibility>
205 inline BoxVisibility GetBoxVisibility(const ViewFrustum &ViewFrustum, const BoundBox &Box)
206 {
207  const Plane3D *pPlanes = reinterpret_cast<const Plane3D*>(&ViewFrustum);
208 
209  int NumPlanesInside = 0;
210  for(int iViewFrustumPlane = 0; iViewFrustumPlane < 6; iViewFrustumPlane++)
211  {
212  const Plane3D &CurrPlane = pPlanes[iViewFrustumPlane];
213  auto VisibilityAgainstPlane = GetBoxVisibilityAgainstPlane<TestFullVisibility>(CurrPlane, Box);
214 
215  // If bounding box is "behind" one of the planes, it is definitely invisible
216  if (VisibilityAgainstPlane == BoxVisibility::Invisible)
217  return BoxVisibility::Invisible;
218 
219  // Count total number of planes the bound box is inside
220  if (VisibilityAgainstPlane == BoxVisibility::FullyVisible)
221  ++NumPlanesInside;
222  }
223 
224  return (TestFullVisibility && NumPlanesInside == 6) ? BoxVisibility::FullyVisible : BoxVisibility::Intersecting;
225 }
226 
227 template<bool TestFullVisibility>
228 inline BoxVisibility GetBoxVisibility(const ViewFrustumExt &ViewFrustumExt, const BoundBox &Box)
229 {
230  auto Visibility = GetBoxVisibility<TestFullVisibility>(static_cast<const ViewFrustum&>(ViewFrustumExt), Box);
231  if (Visibility == BoxVisibility::FullyVisible || Visibility == BoxVisibility::Invisible)
232  return Visibility;
233 
234  // Additionally test if the whole frustum is outside one of
235  // the the bounding box planes. This helps in the following situation:
236  //
237  //
238  // .
239  // / ' . .
240  // / AABB / . ' |
241  // / /. ' |
242  // ' . / | |
243  // * . | |
244  // ' . |
245  // ' . |
246  // ' .
247 
248  for(int iBoundBoxPlane = 0; iBoundBoxPlane < 6; ++iBoundBoxPlane)
249  {
250  // struct BoundBox
251  // {
252  // float fMinX, fMaxX, fMinY, fMaxY, fMinZ, fMaxZ;
253  // };
254  float CurrPlaneCoord = reinterpret_cast<const float*>(&Box)[iBoundBoxPlane];
255  int iCoordOrder = iBoundBoxPlane / 2; // 0, 0, 1, 1, 2, 2
256  float fSign = (iBoundBoxPlane & 0x01) ? +1.f : -1.f;
257  bool bAllCornersOutside = true;
258  for(int iCorner=0; iCorner < 8; iCorner++)
259  {
260  float CurrCornerCoord = ViewFrustumExt.FrustumCorners[iCorner][iCoordOrder];
261  if( fSign * (CurrPlaneCoord - CurrCornerCoord) > 0)
262  {
263  bAllCornersOutside = false;
264  break;
265  }
266  }
267  if( bAllCornersOutside )
268  return BoxVisibility::Invisible;
269  }
270 
271  return BoxVisibility::Intersecting;
272 }
273 
274 inline float GetPointToBoxDistance(const BoundBox &BndBox, const float3 &Pos)
275 {
276  VERIFY_EXPR(BndBox.fMaxX >= BndBox.fMinX &&
277  BndBox.fMaxY >= BndBox.fMinY &&
278  BndBox.fMaxZ >= BndBox.fMinZ);
279  float fdX = (Pos.x > BndBox.fMaxX) ? (Pos.x - BndBox.fMaxX) : ( (Pos.x < BndBox.fMinX) ? (BndBox.fMinX - Pos.x) : 0.f );
280  float fdY = (Pos.y > BndBox.fMaxY) ? (Pos.y - BndBox.fMaxY) : ( (Pos.y < BndBox.fMinY) ? (BndBox.fMinY - Pos.y) : 0.f );
281  float fdZ = (Pos.z > BndBox.fMaxZ) ? (Pos.z - BndBox.fMaxZ) : ( (Pos.z < BndBox.fMinZ) ? (BndBox.fMinZ - Pos.z) : 0.f );
282  VERIFY_EXPR(fdX >= 0 && fdY >= 0 && fdZ >= 0);
283 
284  float3 RangeVec(fdX, fdY, fdZ);
285  return length( RangeVec );
286 }
287 
288 inline bool operator == (const Plane3D &p1, const Plane3D &p2)
289 {
290  return p1.Normal == p2.Normal &&
291  p1.Distance == p2.Distance;
292 }
293 
294 inline bool operator == (const ViewFrustum &f1, const ViewFrustum &f2)
295 {
296  return f1.LeftPlane == f2.LeftPlane &&
297  f1.RightPlane == f2.RightPlane &&
298  f1.BottomPlane == f2.BottomPlane &&
299  f1.TopPlane == f2.TopPlane &&
300  f1.NearPlane == f2.NearPlane &&
301  f1.FarPlane == f2.FarPlane;
302 }
303 
304 inline bool operator == (const ViewFrustumExt &f1, const ViewFrustumExt &f2)
305 {
306  if (! (static_cast<const ViewFrustum &>(f1) == static_cast<const ViewFrustum &>(f2)) )
307  return false;
308 
309  for (int c = 0; c < _countof(f1.FrustumCorners); ++c)
310  if (f1.FrustumCorners[c] != f2.FrustumCorners[c])
311  return false;
312 
313  return true;
314 }
315 
316 namespace std
317 {
318  template<>
319  struct hash<Plane3D>
320  {
321  size_t operator()( const Plane3D &Plane ) const
322  {
323  return Diligent::ComputeHash(Plane.Normal, Plane.Distance);
324  }
325  };
326 
327  template<>
328  struct hash<ViewFrustum>
329  {
330  size_t operator()( const ViewFrustum &Frustum ) const
331  {
332  return Diligent::ComputeHash(Frustum.LeftPlane, Frustum.RightPlane, Frustum.BottomPlane, Frustum.TopPlane, Frustum.NearPlane, Frustum.FarPlane);
333  }
334  };
335 
336  template<>
337  struct hash<ViewFrustumExt>
338  {
339  size_t operator()( const ViewFrustumExt &Frustum ) const
340  {
341  auto Seed = Diligent::ComputeHash(static_cast<const ViewFrustum&>(Frustum));
342  for (int Corner = 0; Corner < _countof(Frustum.FrustumCorners); ++Corner)
343  Diligent::HashCombine(Seed, Frustum.FrustumCorners[Corner]);
344  return Seed;
345  }
346  };
347 }
Definition: AdvancedMath.h:316