-
Notifications
You must be signed in to change notification settings - Fork 0
/
Frustum.cpp
403 lines (321 loc) · 16.8 KB
/
Frustum.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
//***********************************************************************//
// //
// - "Talk to me like I'm a 3 year old!" Programming Lessons - //
// //
// $Author: DigiBen DigiBen@GameTutorials.com //
// //
// $Program: BSP Loader 6 //
// //
// $Description: This adds gravity, jumping and walking up steps //
// //
//***********************************************************************//
#include "Frustum.h"
// This is the index in our selection buffer that has the closet object ID clicked
#define FIRST_OBJECT_ID 3
// We create an enum of the sides so we don't have to call each side 0 or 1.
// This way it makes it more understandable and readable when dealing with frustum sides.
enum FrustumSide
{
RIGHT = 0, // The RIGHT side of the frustum
LEFT = 1, // The LEFT side of the frustum
BOTTOM = 2, // The BOTTOM side of the frustum
TOP = 3, // The TOP side of the frustum
BACK = 4, // The BACK side of the frustum
FRONT = 5 // The FRONT side of the frustum
};
// Like above, instead of saying a number for the ABC and D of the plane, we
// want to be more descriptive.
enum PlaneData
{
A = 0, // The X value of the plane's normal
B = 1, // The Y value of the plane's normal
C = 2, // The Z value of the plane's normal
D = 3 // The distance the plane is from the origin
};
///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This normalizes a plane (A side) from a given frustum.
/////
///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void NormalizePlane(float frustum[6][4], int side)
{
// Here we calculate the magnitude of the normal to the plane (point A B C)
// Remember that (A, B, C) is that same thing as the normal's (X, Y, Z).
// To calculate magnitude you use the equation: magnitude = sqrt( x^2 + y^2 + z^2)
float magnitude = (float)sqrt( frustum[side][A] * frustum[side][A] +
frustum[side][B] * frustum[side][B] +
frustum[side][C] * frustum[side][C] );
// Then we divide the plane's values by it's magnitude.
// This makes it easier to work with.
frustum[side][A] /= magnitude;
frustum[side][B] /= magnitude;
frustum[side][C] /= magnitude;
frustum[side][D] /= magnitude;
}
///////////////////////////////// CALCULATE FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This extracts our frustum from the projection and modelview matrix.
/////
///////////////////////////////// CALCULATE FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CFrustum::CalculateFrustum()
{
float proj[16]; // This will hold our projection matrix
float modl[16]; // This will hold our modelview matrix
float clip[16]; // This will hold the clipping planes
// glGetFloatv() is used to extract information about our OpenGL world.
// Below, we pass in GL_PROJECTION_MATRIX to abstract our projection matrix.
// It then stores the matrix into an array of [16].
glGetFloatv( GL_PROJECTION_MATRIX, proj );
// By passing in GL_MODELVIEW_MATRIX, we can abstract our model view matrix.
// This also stores it in an array of [16].
glGetFloatv( GL_MODELVIEW_MATRIX, modl );
// Now that we have our modelview and projection matrix, if we combine these 2 matrices,
// it will give us our clipping planes. To combine 2 matrices, we multiply them.
clip[ 0] = modl[ 0] * proj[ 0] + modl[ 1] * proj[ 4] + modl[ 2] * proj[ 8] + modl[ 3] * proj[12];
clip[ 1] = modl[ 0] * proj[ 1] + modl[ 1] * proj[ 5] + modl[ 2] * proj[ 9] + modl[ 3] * proj[13];
clip[ 2] = modl[ 0] * proj[ 2] + modl[ 1] * proj[ 6] + modl[ 2] * proj[10] + modl[ 3] * proj[14];
clip[ 3] = modl[ 0] * proj[ 3] + modl[ 1] * proj[ 7] + modl[ 2] * proj[11] + modl[ 3] * proj[15];
clip[ 4] = modl[ 4] * proj[ 0] + modl[ 5] * proj[ 4] + modl[ 6] * proj[ 8] + modl[ 7] * proj[12];
clip[ 5] = modl[ 4] * proj[ 1] + modl[ 5] * proj[ 5] + modl[ 6] * proj[ 9] + modl[ 7] * proj[13];
clip[ 6] = modl[ 4] * proj[ 2] + modl[ 5] * proj[ 6] + modl[ 6] * proj[10] + modl[ 7] * proj[14];
clip[ 7] = modl[ 4] * proj[ 3] + modl[ 5] * proj[ 7] + modl[ 6] * proj[11] + modl[ 7] * proj[15];
clip[ 8] = modl[ 8] * proj[ 0] + modl[ 9] * proj[ 4] + modl[10] * proj[ 8] + modl[11] * proj[12];
clip[ 9] = modl[ 8] * proj[ 1] + modl[ 9] * proj[ 5] + modl[10] * proj[ 9] + modl[11] * proj[13];
clip[10] = modl[ 8] * proj[ 2] + modl[ 9] * proj[ 6] + modl[10] * proj[10] + modl[11] * proj[14];
clip[11] = modl[ 8] * proj[ 3] + modl[ 9] * proj[ 7] + modl[10] * proj[11] + modl[11] * proj[15];
clip[12] = modl[12] * proj[ 0] + modl[13] * proj[ 4] + modl[14] * proj[ 8] + modl[15] * proj[12];
clip[13] = modl[12] * proj[ 1] + modl[13] * proj[ 5] + modl[14] * proj[ 9] + modl[15] * proj[13];
clip[14] = modl[12] * proj[ 2] + modl[13] * proj[ 6] + modl[14] * proj[10] + modl[15] * proj[14];
clip[15] = modl[12] * proj[ 3] + modl[13] * proj[ 7] + modl[14] * proj[11] + modl[15] * proj[15];
// Now we actually want to get the sides of the frustum. To do this we take
// the clipping planes we received above and extract the sides from them.
// This will extract the RIGHT side of the frustum
m_Frustum[RIGHT][A] = clip[ 3] - clip[ 0];
m_Frustum[RIGHT][B] = clip[ 7] - clip[ 4];
m_Frustum[RIGHT][C] = clip[11] - clip[ 8];
m_Frustum[RIGHT][D] = clip[15] - clip[12];
// Now that we have a normal (A,B,C) and a distance (D) to the plane,
// we want to normalize that normal and distance.
// Normalize the RIGHT side
NormalizePlane(m_Frustum, RIGHT);
// This will extract the LEFT side of the frustum
m_Frustum[LEFT][A] = clip[ 3] + clip[ 0];
m_Frustum[LEFT][B] = clip[ 7] + clip[ 4];
m_Frustum[LEFT][C] = clip[11] + clip[ 8];
m_Frustum[LEFT][D] = clip[15] + clip[12];
// Normalize the LEFT side
NormalizePlane(m_Frustum, LEFT);
// This will extract the BOTTOM side of the frustum
m_Frustum[BOTTOM][A] = clip[ 3] + clip[ 1];
m_Frustum[BOTTOM][B] = clip[ 7] + clip[ 5];
m_Frustum[BOTTOM][C] = clip[11] + clip[ 9];
m_Frustum[BOTTOM][D] = clip[15] + clip[13];
// Normalize the BOTTOM side
NormalizePlane(m_Frustum, BOTTOM);
// This will extract the TOP side of the frustum
m_Frustum[TOP][A] = clip[ 3] - clip[ 1];
m_Frustum[TOP][B] = clip[ 7] - clip[ 5];
m_Frustum[TOP][C] = clip[11] - clip[ 9];
m_Frustum[TOP][D] = clip[15] - clip[13];
// Normalize the TOP side
NormalizePlane(m_Frustum, TOP);
// This will extract the BACK side of the frustum
m_Frustum[BACK][A] = clip[ 3] - clip[ 2];
m_Frustum[BACK][B] = clip[ 7] - clip[ 6];
m_Frustum[BACK][C] = clip[11] - clip[10];
m_Frustum[BACK][D] = clip[15] - clip[14];
// Normalize the BACK side
NormalizePlane(m_Frustum, BACK);
// This will extract the FRONT side of the frustum
m_Frustum[FRONT][A] = clip[ 3] + clip[ 2];
m_Frustum[FRONT][B] = clip[ 7] + clip[ 6];
m_Frustum[FRONT][C] = clip[11] + clip[10];
m_Frustum[FRONT][D] = clip[15] + clip[14];
// Normalize the FRONT side
NormalizePlane(m_Frustum, FRONT);
}
// The code below will allow us to make checks within the frustum. For example,
// if we want to see if a point, a sphere, or a cube lies inside of the frustum.
// Because all of our planes point INWARDS (The normals are all pointing inside the frustum)
// we then can assume that if a point is in FRONT of all of the planes, it's inside.
///////////////////////////////// POINT IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This determines if a point is inside of the frustum
/////
///////////////////////////////// POINT IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
bool CFrustum::PointInFrustum( float x, float y, float z )
{
// Go through all the sides of the frustum
for(int i = 0; i < 6; i++ )
{
// Calculate the plane equation and check if the point is behind a side of the frustum
if(m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= 0)
{
// The point was behind a side, so it ISN'T in the frustum
return false;
}
}
// The point was inside of the frustum (In front of ALL the sides of the frustum)
return true;
}
///////////////////////////////// SPHERE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This determines if a sphere is inside of our frustum by it's center and radius.
/////
///////////////////////////////// SPHERE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
bool CFrustum::SphereInFrustum( float x, float y, float z, float radius )
{
// Go through all the sides of the frustum
for(int i = 0; i < 6; i++ )
{
// If the center of the sphere is farther away from the plane than the radius
if( m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= -radius )
{
// The distance was greater than the radius so the sphere is outside of the frustum
return false;
}
}
// The sphere was inside of the frustum!
return true;
}
///////////////////////////////// CUBE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This determines if a cube is in or around our frustum by it's center and 1/2 it's length
/////
///////////////////////////////// CUBE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
bool CFrustum::CubeInFrustum( float x, float y, float z, float size )
{
// Basically, what is going on is, that we are given the center of the cube,
// and half the length. Think of it like a radius. Then we checking each point
// in the cube and seeing if it is inside the frustum. If a point is found in front
// of a side, then we skip to the next side. If we get to a plane that does NOT have
// a point in front of it, then it will return false.
// *Note* - This will sometimes say that a cube is inside the frustum when it isn't.
// This happens when all the corners of the bounding box are not behind any one plane.
// This is rare and shouldn't effect the overall rendering speed.
for(int i = 0; i < 6; i++ )
{
if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
continue;
// If we get here, it isn't in the frustum
return false;
}
return true;
}
///////////////////////////////// BOX IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This determines if a BOX is in or around our frustum by it's min and max points
/////
///////////////////////////////// BOX IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
bool CFrustum::BoxInFrustum( float x, float y, float z, float x2, float y2, float z2)
{
// Go through all of the corners of the box and check then again each plane
// in the frustum. If all of them are behind one of the planes, then it most
// like is not in the frustum.
for(int i = 0; i < 6; i++ )
{
if(m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] > 0) continue;
if(m_Frustum[i][A] * x2 + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] > 0) continue;
if(m_Frustum[i][A] * x + m_Frustum[i][B] * y2 + m_Frustum[i][C] * z + m_Frustum[i][D] > 0) continue;
if(m_Frustum[i][A] * x2 + m_Frustum[i][B] * y2 + m_Frustum[i][C] * z + m_Frustum[i][D] > 0) continue;
if(m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z2 + m_Frustum[i][D] > 0) continue;
if(m_Frustum[i][A] * x2 + m_Frustum[i][B] * y + m_Frustum[i][C] * z2 + m_Frustum[i][D] > 0) continue;
if(m_Frustum[i][A] * x + m_Frustum[i][B] * y2 + m_Frustum[i][C] * z2 + m_Frustum[i][D] > 0) continue;
if(m_Frustum[i][A] * x2 + m_Frustum[i][B] * y2 + m_Frustum[i][C] * z2 + m_Frustum[i][D] > 0) continue;
// If we get here, it isn't in the frustum
return false;
}
// Return a true for the box being inside of the frustum
return true;
}
///////////////////////////////// RENDER DEBUG LINES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This goes through all of the lines that we stored in our list and draws them
/////
///////////////////////////////// RENDER DEBUG LINES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CDebug::RenderDebugLines() // This renders all of the lines
{
glDisable(GL_TEXTURE_2D);
glBegin(GL_LINES); // Start rendering lines
glColor3ub(255, 255, 0); // Turn the lines yellow
// Go through the whole list of lines stored in the vector m_vLines.
for(unsigned int i = 0; i < m_vLines.size(); i++)
{
// Pass in the current point to be rendered as part of a line
glVertex3f(m_vLines[i].x, m_vLines[i].y, m_vLines[i].z);
}
glEnd();
}
///////////////////////////////// ADD DEBUG LINE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This adds a debug LINE to the stack of lines
/////
///////////////////////////////// ADD DEBUG LINE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CDebug::AddDebugLine(glm::vec3 vPoint1, glm::vec3 vPoint2)
{
// Add the 2 points that make up the line into our line list.
m_vLines.push_back(vPoint1);
m_vLines.push_back(vPoint2);
}
///////////////////////////////// ADD DEBUG BOX \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This adds a debug BOX to the stack of lines
/////
///////////////////////////////// ADD DEBUG BOX\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CDebug::AddDebugBox(glm::vec3 vCenter, float width, float height, float depth)
{
// So we can work with the code better, we divide the dimensions in half.
// That way we can create the box from the center outwards.
width /= 2.0f; height /= 2.0f; depth /= 2.0f;
// Below we create all the 8 points so it will be easier to input the lines
// of the box. With the dimensions we calculate the points.
glm::vec3 vTopLeftFront( vCenter.x - width, vCenter.y + height, vCenter.z + depth);
glm::vec3 vTopLeftBack( vCenter.x - width, vCenter.y + height, vCenter.z - depth);
glm::vec3 vTopRightBack( vCenter.x + width, vCenter.y + height, vCenter.z - depth);
glm::vec3 vTopRightFront(vCenter.x + width, vCenter.y + height, vCenter.z + depth);
glm::vec3 vBottom_LeftFront( vCenter.x - width, vCenter.y - height, vCenter.z + depth);
glm::vec3 vBottom_LeftBack( vCenter.x - width, vCenter.y - height, vCenter.z - depth);
glm::vec3 vBottomRightBack( vCenter.x + width, vCenter.y - height, vCenter.z - depth);
glm::vec3 vBottomRightFront(vCenter.x + width, vCenter.y - height, vCenter.z + depth);
////////// TOP LINES //////////
// Store the top front line of the box
m_vLines.push_back(vTopLeftFront); m_vLines.push_back(vTopRightFront);
// Store the top back line of the box
m_vLines.push_back(vTopLeftBack); m_vLines.push_back(vTopRightBack);
// Store the top left line of the box
m_vLines.push_back(vTopLeftFront); m_vLines.push_back(vTopLeftBack);
// Store the top right line of the box
m_vLines.push_back(vTopRightFront); m_vLines.push_back(vTopRightBack);
////////// BOTTOM LINES //////////
// Store the bottom front line of the box
m_vLines.push_back(vBottom_LeftFront); m_vLines.push_back(vBottomRightFront);
// Store the bottom back line of the box
m_vLines.push_back(vBottom_LeftBack); m_vLines.push_back(vBottomRightBack);
// Store the bottom left line of the box
m_vLines.push_back(vBottom_LeftFront); m_vLines.push_back(vBottom_LeftBack);
// Store the bottom right line of the box
m_vLines.push_back(vBottomRightFront); m_vLines.push_back(vBottomRightBack);
////////// SIDE LINES //////////
// Store the bottom front line of the box
m_vLines.push_back(vTopLeftFront); m_vLines.push_back(vBottom_LeftFront);
// Store the back left line of the box
m_vLines.push_back(vTopLeftBack); m_vLines.push_back(vBottom_LeftBack);
// Store the front right line of the box
m_vLines.push_back(vTopRightBack); m_vLines.push_back(vBottomRightBack);
// Store the front left line of the box
m_vLines.push_back(vTopRightFront); m_vLines.push_back(vBottomRightFront);
}