1 /* bzflag
2 * Copyright (c) 1993-2021 Tim Riker
3 *
4 * This package is free software; you can redistribute it and/or
5 * modify it under the terms of the license found in the file
6 * named COPYING that should have accompanied this file.
7 *
8 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11 */
12
13 #include "common.h"
14 #include <math.h>
15 #include <string.h>
16 #include "Frustum.h"
17
18
Frustum()19 Frustum::Frustum()
20 {
21 static float defaultEye[3] = { 0.0, 0.0, 0.0 };
22 static float defaultTarget[3] = { 0.0, 1.0, 0.0 };
23 static float identity[16] = { 1.0, 0.0, 0.0, 0.0,
24 0.0, 1.0, 0.0, 0.0,
25 0.0, 0.0, 1.0, 0.0,
26 0.0, 0.0, 0.0, 1.0
27 };
28
29 // initialize view and projection matrices to identity
30 ::memcpy(viewMatrix, identity, sizeof(viewMatrix));
31 ::memcpy(billboardMatrix, identity, sizeof(billboardMatrix));
32 ::memcpy(projectionMatrix, identity, sizeof(projectionMatrix));
33 ::memcpy(deepProjectionMatrix, identity, sizeof(deepProjectionMatrix));
34
35 setProjection((float)(M_PI/4.0), 1.0f, 100.0f, 1000.0f, 1, 1, 1);
36 setView(defaultEye, defaultTarget);
37 }
38
39
~Frustum()40 Frustum::~Frustum()
41 {
42 // do nothing
43 }
44
45
getEyeDepth(const float * p) const46 float Frustum::getEyeDepth(const float* p) const
47 {
48 return viewMatrix[2] * p[0] + viewMatrix[6] * p[1] +
49 viewMatrix[10] * p[2] + viewMatrix[14];
50 }
51
52
setView(const float * _eye,const float * _target)53 void Frustum::setView(const float* _eye, const float* _target)
54 {
55 // set eye and target points
56 eye[0] = _eye[0];
57 eye[1] = _eye[1];
58 eye[2] = _eye[2];
59 target[0] = _target[0];
60 target[1] = _target[1];
61 target[2] = _target[2];
62
63 // compute forward vector and normalize
64 plane[0][0] = target[0] - eye[0];
65 plane[0][1] = target[1] - eye[1];
66 plane[0][2] = target[2] - eye[2];
67 float d = 1.0f / sqrtf(plane[0][0] * plane[0][0] +
68 plane[0][1] * plane[0][1] +
69 plane[0][2] * plane[0][2]);
70 plane[0][0] *= d;
71 plane[0][1] *= d;
72 plane[0][2] *= d;
73
74 // compute left vector (by crossing forward with
75 // world-up [0 0 1]T and normalizing)
76 right[0] = plane[0][1];
77 right[1] = -plane[0][0];
78 d = 1.0f / hypotf(right[0], right[1]);
79 right[0] *= d;
80 right[1] *= d;
81 right[2] = 0.0f;
82
83 // compute local up vector (by crossing right and forward,
84 // normalization unnecessary)
85 up[0] = right[1] * plane[0][2];
86 up[1] = -right[0] * plane[0][2];
87 up[2] = right[0] * plane[0][1] - right[1] * plane[0][0];
88
89 // build view matrix, including a transformation bringing
90 // world up [0 0 1 0]T to eye up [0 1 0 0]T, world north
91 // [0 1 0 0]T to eye forward [0 0 -1 0]T.
92 viewMatrix[0] = right[0];
93 viewMatrix[4] = right[1];
94 viewMatrix[8] = 0.0f;
95
96 viewMatrix[1] = up[0];
97 viewMatrix[5] = up[1];
98 viewMatrix[9] = up[2];
99
100 viewMatrix[2] = -plane[0][0];
101 viewMatrix[6] = -plane[0][1];
102 viewMatrix[10] = -plane[0][2];
103
104 viewMatrix[12] = -(viewMatrix[0] * eye[0] +
105 viewMatrix[4] * eye[1] +
106 viewMatrix[8] * eye[2]);
107 viewMatrix[13] = -(viewMatrix[1] * eye[0] +
108 viewMatrix[5] * eye[1] +
109 viewMatrix[9] * eye[2]);
110 viewMatrix[14] = -(viewMatrix[2] * eye[0] +
111 viewMatrix[6] * eye[1] +
112 viewMatrix[10] * eye[2]);
113
114 // build billboard matrix. billboard matrix performs rotation
115 // so that polygons drawn in the xy plane face the camera.
116 billboardMatrix[0] = viewMatrix[0];
117 billboardMatrix[1] = viewMatrix[4];
118 billboardMatrix[2] = viewMatrix[8];
119 billboardMatrix[4] = viewMatrix[1];
120 billboardMatrix[5] = viewMatrix[5];
121 billboardMatrix[6] = viewMatrix[9];
122 billboardMatrix[8] = viewMatrix[2];
123 billboardMatrix[9] = viewMatrix[6];
124 billboardMatrix[10] = viewMatrix[10];
125
126 // compute vectors of frustum edges
127 const float xs = fabsf(1.0f / projectionMatrix[0]);
128 const float ys = fabsf(1.0f / projectionMatrix[5]);
129 float edge[4][3];
130 edge[0][0] = plane[0][0] - xs * right[0] - ys * up[0];
131 edge[0][1] = plane[0][1] - xs * right[1] - ys * up[1];
132 edge[0][2] = plane[0][2] - xs * right[2] - ys * up[2];
133 edge[1][0] = plane[0][0] + xs * right[0] - ys * up[0];
134 edge[1][1] = plane[0][1] + xs * right[1] - ys * up[1];
135 edge[1][2] = plane[0][2] + xs * right[2] - ys * up[2];
136 edge[2][0] = plane[0][0] + xs * right[0] + ys * up[0];
137 edge[2][1] = plane[0][1] + xs * right[1] + ys * up[1];
138 edge[2][2] = plane[0][2] + xs * right[2] + ys * up[2];
139 edge[3][0] = plane[0][0] - xs * right[0] + ys * up[0];
140 edge[3][1] = plane[0][1] - xs * right[1] + ys * up[1];
141 edge[3][2] = plane[0][2] - xs * right[2] + ys * up[2];
142
143 // make frustum planes
144 plane[0][3] = -(eye[0] * plane[0][0] + eye[1] * plane[0][1] +
145 eye[2] * plane[0][2] + m_near);
146 makePlane(edge[0], edge[3], 1);
147 makePlane(edge[2], edge[1], 2);
148 makePlane(edge[1], edge[0], 3);
149 makePlane(edge[3], edge[2], 4);
150
151 plane[5][0] = -plane[0][0];
152 plane[5][1] = -plane[0][1];
153 plane[5][2] = -plane[0][2];
154 plane[5][3] = -plane[0][3] + m_far;
155
156 // make far corners
157 for (int i = 0; i < 4; i++)
158 {
159 farCorner[i][0] = eye[0] + m_far * edge[i][0];
160 farCorner[i][1] = eye[1] + m_far * edge[i][1];
161 farCorner[i][2] = eye[2] + m_far * edge[i][2];
162 }
163
164 // setup tilt and angle
165 const float* dir = plane[0];
166 tilt = (float)((180.0 / M_PI) * atan2((double)dir[2], 1.0));
167 rotation = (float)((180.0 / M_PI) * atan2((double)dir[1], (double)dir[2]));
168 }
169
170
setFarPlaneCull(bool useCulling)171 void Frustum::setFarPlaneCull(bool useCulling)
172 {
173 // far clip plane
174 if (useCulling)
175 planeCount = 6;
176 else
177 planeCount = 5;
178 }
179
180
setProjection(float fov,float _m_near,float _m_far,float m_deep_far,int width,int height,int viewHeight)181 void Frustum::setProjection(float fov,
182 float _m_near, float _m_far, float m_deep_far,
183 int width, int height, int viewHeight)
184 {
185 // do easy stuff
186 m_near = _m_near;
187 m_far = _m_far;
188 fovx = fov;
189
190 // clear the far plane culling here
191 planeCount = 5;
192
193 // compute projectionMatrix
194 const float s = 1.0f / tanf(fov / 2.0f);
195 const float fracHeight = 1.0f - float(viewHeight) / float(height);
196 projectionMatrix[0] = s;
197 projectionMatrix[5] = (1.0f - fracHeight) * s * float(width) / float(viewHeight);
198 projectionMatrix[8] = 0.0f;
199 projectionMatrix[9] = -fracHeight;
200 projectionMatrix[10] = -(m_far + m_near) / (m_far - m_near);
201 projectionMatrix[11] = -1.0f;
202 projectionMatrix[12] = 0.0f;
203 projectionMatrix[14] = -2.0f * m_far * m_near / (m_far - m_near);
204 projectionMatrix[15] = 0.0f;
205
206 deepProjectionMatrix[0] = projectionMatrix[0];
207 deepProjectionMatrix[5] = projectionMatrix[5];
208 deepProjectionMatrix[8] = projectionMatrix[8];
209 deepProjectionMatrix[9] = projectionMatrix[9];
210 deepProjectionMatrix[11] = projectionMatrix[11];
211 deepProjectionMatrix[12] = projectionMatrix[12];
212 deepProjectionMatrix[15] = projectionMatrix[15];
213 deepProjectionMatrix[10] = -(m_deep_far + m_near) / (m_deep_far - m_near);
214 deepProjectionMatrix[14] = -2.0f * m_deep_far * m_near / (m_deep_far - m_near);
215
216 // get field of view in y direction
217 fovy = 2.0f * atanf(1.0f / projectionMatrix[5]);
218
219 // compute areaFactor
220 areaFactor = 0.25f * s * float(height);
221 areaFactor = (float)(M_PI * areaFactor * areaFactor);
222 }
223
224
setOffset(float eyeOffset,float focalPlane)225 void Frustum::setOffset(float eyeOffset, float focalPlane)
226 {
227 projectionMatrix[12] = 0.5f * eyeOffset * projectionMatrix[0];
228 projectionMatrix[8] = projectionMatrix[12] / focalPlane;
229 deepProjectionMatrix[8] = projectionMatrix[8];
230 deepProjectionMatrix[12] = projectionMatrix[12];
231 }
232
233
makePlane(const float * v1,const float * v2,int index)234 void Frustum::makePlane(const float* v1, const float* v2, int index)
235 {
236 // get normal by crossing v1 and v2 and normalizing
237 float n[3];
238 n[0] = v1[1] * v2[2] - v1[2] * v2[1];
239 n[1] = v1[2] * v2[0] - v1[0] * v2[2];
240 n[2] = v1[0] * v2[1] - v1[1] * v2[0];
241 float d = 1.0f / sqrtf(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]);
242 plane[index][0] = d * n[0];
243 plane[index][1] = d * n[1];
244 plane[index][2] = d * n[2];
245 plane[index][3] = -(eye[0] * plane[index][0] + eye[1] * plane[index][1] +
246 eye[2] * plane[index][2]);
247 }
248
249
250 // these next two functions should be more generic
251 // flipX, flipY, flipZ, all with and offset along the axis
flipVertical()252 void Frustum::flipVertical()
253 {
254 eye[2] = -eye[2];
255 target[2] = -target[2];
256 setView(eye, target);
257 projectionMatrix[5] = -projectionMatrix[5];
258 deepProjectionMatrix[5] = -deepProjectionMatrix[5];
259
260 return;
261 }
262
263
flipHorizontal()264 void Frustum::flipHorizontal()
265 {
266 eye[0] = -eye[0];
267 target[0] = -target[0];
268 setView(eye, target);
269 projectionMatrix[0] = -projectionMatrix[0];
270 deepProjectionMatrix[0] = -deepProjectionMatrix[0];
271 return;
272 }
273
274
275 // used for radar culling, not really a frustum
setOrthoPlanes(const Frustum & view,float width,float breadth)276 void Frustum::setOrthoPlanes(const Frustum& view, float width, float breadth)
277 {
278 // setup the eye, and the clipping planes
279 memcpy(eye, view.getEye(), sizeof(float[3]));
280
281 float front[2], left[2];
282 const float* dir = view.getDirection();
283 float len = (dir[0] * dir[0]) + (dir[1] * dir[1]);
284 if (len != 0)
285 {
286 len = 1.0f / sqrtf(len);
287 front[0] = dir[0] * len;
288 front[1] = dir[1] * len;
289 }
290 else
291 {
292 front[0] = 1.0f;
293 front[1] = 0.0f;
294 }
295
296 left[0] = -front[1];
297 left[1] = +front[0];
298
299 plane[1][0] = +left[0];
300 plane[1][1] = +left[1];
301 plane[1][3] = -((eye[0] * plane[1][0]) + (eye[1] * plane[1][1])) + width;
302
303 plane[2][0] = -left[0];
304 plane[2][1] = -left[1];
305 plane[2][3] = -((eye[0] * plane[2][0]) + (eye[1] * plane[2][1])) + width;
306
307 plane[3][0] = +front[0];
308 plane[3][1] = +front[1];
309 plane[3][3] = -((eye[0] * plane[3][0]) + (eye[1] * plane[3][1])) + breadth;
310
311 plane[4][0] = -front[0];
312 plane[4][1] = -front[1];
313 plane[4][3] = -((eye[0] * plane[4][0]) + (eye[1] * plane[4][1])) + breadth;
314
315 plane[1][2] = 0.0f;
316 plane[2][2] = 0.0f;
317 plane[3][2] = 0.0f;
318 plane[4][2] = 0.0f;
319
320 // disable the near and far planes
321 plane[0][0] = plane[0][1] = 0.0f;
322 plane[0][2] = 1.0f;
323 plane[0][3] = -1.0e6;
324 plane[5][0] = plane[0][1] = 0.0f;
325 plane[5][2] = 1.0f;
326 plane[5][3] = -1.0e6;
327
328 planeCount = 5;
329
330 return;
331 }
332
333
334 // Local Variables: ***
335 // mode: C++ ***
336 // tab-width: 4 ***
337 // c-basic-offset: 4 ***
338 // indent-tabs-mode: nil ***
339 // End: ***
340 // ex: shiftwidth=4 tabstop=4
341