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