1 /* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
5 * PrBoom: a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11 * Copyright 2005, 2006 by
12 * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 * 02111-1307, USA.
28 *
29 * DESCRIPTION:
30 *
31 *---------------------------------------------------------------------
32 */
33
34 /*
35 *
36 ** gl_clipper.cpp
37 **
38 ** Handles visibility checks.
39 ** Loosely based on the JDoom clipper.
40 **
41 **---------------------------------------------------------------------------
42 ** Copyright 2003 Tim Stump
43 ** All rights reserved.
44 **
45 ** Redistribution and use in source and binary forms, with or without
46 ** modification, are permitted provided that the following conditions
47 ** are met:
48 **
49 ** 1. Redistributions of source code must retain the above copyright
50 ** notice, this list of conditions and the following disclaimer.
51 ** 2. Redistributions in binary form must reproduce the above copyright
52 ** notice, this list of conditions and the following disclaimer in the
53 ** documentation and/or other materials provided with the distribution.
54 ** 3. The name of the author may not be used to endorse or promote products
55 ** derived from this software without specific prior written permission.
56 **
57 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
58 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
59 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
60 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
61 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
62 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
63 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
64 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
65 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
66 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
67 **---------------------------------------------------------------------------
68 **
69 */
70
71 #include <SDL_opengl.h>
72 #include <math.h>
73 #include "v_video.h"
74 #include "gl_intern.h"
75 #include "e6y.h"
76
77 static GLdouble viewMatrix[16];
78 static GLdouble projMatrix[16];
79 float frustum[6][4];
80
81 typedef struct clipnode_s
82 {
83 struct clipnode_s *prev, *next;
84 angle_t start, end;
85 } clipnode_t;
86
87 clipnode_t *freelist;
88 clipnode_t *clipnodes;
89 clipnode_t *cliphead;
90
91 static clipnode_t * gld_clipnode_GetNew(void);
92 static clipnode_t * gld_clipnode_NewRange(angle_t start, angle_t end);
93 static dboolean gld_clipper_IsRangeVisible(angle_t startAngle, angle_t endAngle);
94 static void gld_clipper_AddClipRange(angle_t start, angle_t end);
95 static void gld_clipper_RemoveRange(clipnode_t * range);
96 static void gld_clipnode_Free(clipnode_t *node);
97
gld_clipnode_GetNew(void)98 static clipnode_t * gld_clipnode_GetNew(void)
99 {
100 if (freelist)
101 {
102 clipnode_t * p = freelist;
103 freelist = p->next;
104 return p;
105 }
106 else
107 {
108 return malloc(sizeof(clipnode_t));
109 }
110 }
111
gld_clipnode_NewRange(angle_t start,angle_t end)112 static clipnode_t * gld_clipnode_NewRange(angle_t start, angle_t end)
113 {
114 clipnode_t * c = gld_clipnode_GetNew();
115 c->start = start;
116 c->end = end;
117 c->next = c->prev=NULL;
118 return c;
119 }
120
gld_clipper_SafeCheckRange(angle_t startAngle,angle_t endAngle)121 dboolean gld_clipper_SafeCheckRange(angle_t startAngle, angle_t endAngle)
122 {
123 if(startAngle > endAngle)
124 {
125 return (gld_clipper_IsRangeVisible(startAngle, ANGLE_MAX) || gld_clipper_IsRangeVisible(0, endAngle));
126 }
127
128 return gld_clipper_IsRangeVisible(startAngle, endAngle);
129 }
130
gld_clipper_IsRangeVisible(angle_t startAngle,angle_t endAngle)131 static dboolean gld_clipper_IsRangeVisible(angle_t startAngle, angle_t endAngle)
132 {
133 clipnode_t *ci;
134 ci = cliphead;
135
136 if (endAngle == 0 && ci && ci->start == 0)
137 return false;
138
139 while (ci != NULL && ci->start < endAngle)
140 {
141 if (startAngle >= ci->start && endAngle <= ci->end)
142 {
143 return false;
144 }
145 ci = ci->next;
146 }
147
148 return true;
149 }
150
gld_clipnode_Free(clipnode_t * node)151 static void gld_clipnode_Free(clipnode_t *node)
152 {
153 node->next = freelist;
154 freelist = node;
155 }
156
gld_clipper_RemoveRange(clipnode_t * range)157 static void gld_clipper_RemoveRange(clipnode_t *range)
158 {
159 if (range == cliphead)
160 {
161 cliphead = cliphead->next;
162 }
163 else
164 {
165 if (range->prev)
166 {
167 range->prev->next = range->next;
168 }
169 if (range->next)
170 {
171 range->next->prev = range->prev;
172 }
173 }
174
175 gld_clipnode_Free(range);
176 }
177
gld_clipper_SafeAddClipRange(angle_t startangle,angle_t endangle)178 void gld_clipper_SafeAddClipRange(angle_t startangle, angle_t endangle)
179 {
180 if(startangle > endangle)
181 {
182 // The range has to added in two parts.
183 gld_clipper_AddClipRange(startangle, ANGLE_MAX);
184 gld_clipper_AddClipRange(0, endangle);
185 }
186 else
187 {
188 // Add the range as usual.
189 gld_clipper_AddClipRange(startangle, endangle);
190 }
191 }
192
gld_clipper_AngleToPseudo(angle_t ang)193 angle_t gld_clipper_AngleToPseudo(angle_t ang)
194 {
195 double vecx = cos(ang * M_PI / ANG180);
196 double vecy = sin(ang * M_PI / ANG180);
197
198 double result = vecy / (fabs(vecx) + fabs(vecy));
199 if (vecx < 0)
200 {
201 result = 2.f - result;
202 }
203 return (angle_t)(result * (1<<30));
204 }
205
gld_clipper_SafeAddClipRangeRealAngles(angle_t startangle,angle_t endangle)206 void gld_clipper_SafeAddClipRangeRealAngles(angle_t startangle, angle_t endangle)
207 {
208 gld_clipper_SafeAddClipRange(
209 gld_clipper_AngleToPseudo(startangle),
210 gld_clipper_AngleToPseudo(endangle));
211 }
212
213
gld_clipper_AddClipRange(angle_t start,angle_t end)214 static void gld_clipper_AddClipRange(angle_t start, angle_t end)
215 {
216 clipnode_t *node, *temp, *prevNode, *node2, *delnode;
217
218 if (cliphead)
219 {
220 //check to see if range contains any old ranges
221 node = cliphead;
222 while (node != NULL && node->start < end)
223 {
224 if (node->start >= start && node->end <= end)
225 {
226 temp = node;
227 node = node->next;
228 gld_clipper_RemoveRange(temp);
229 }
230 else
231 {
232 if (node->start <= start && node->end >= end)
233 {
234 return;
235 }
236 else
237 {
238 node = node->next;
239 }
240 }
241 }
242
243 //check to see if range overlaps a range (or possibly 2)
244 node = cliphead;
245 while (node != NULL && node->start <= end)
246 {
247 if (node->end >= start)
248 {
249 // we found the first overlapping node
250 if (node->start > start)
251 {
252 // the new range overlaps with this node's start point
253 node->start = start;
254 }
255 if (node->end < end)
256 {
257 node->end = end;
258 }
259
260 node2 = node->next;
261 while (node2 && node2->start <= node->end)
262 {
263 if (node2->end > node->end)
264 {
265 node->end = node2->end;
266 }
267
268 delnode = node2;
269 node2 = node2->next;
270 gld_clipper_RemoveRange(delnode);
271 }
272 return;
273 }
274 node = node->next;
275 }
276
277 //just add range
278 node = cliphead;
279 prevNode = NULL;
280 temp = gld_clipnode_NewRange(start, end);
281 while (node != NULL && node->start < end)
282 {
283 prevNode = node;
284 node = node->next;
285 }
286 temp->next = node;
287 if (node == NULL)
288 {
289 temp->prev = prevNode;
290 if (prevNode)
291 {
292 prevNode->next = temp;
293 }
294 if (!cliphead)
295 {
296 cliphead = temp;
297 }
298 }
299 else
300 {
301 if (node == cliphead)
302 {
303 cliphead->prev = temp;
304 cliphead = temp;
305 }
306 else
307 {
308 temp->prev = prevNode;
309 prevNode->next = temp;
310 node->prev = temp;
311 }
312 }
313 }
314 else
315 {
316 temp = gld_clipnode_NewRange(start, end);
317 cliphead = temp;
318 return;
319 }
320 }
321
gld_clipper_Clear(void)322 void gld_clipper_Clear(void)
323 {
324 clipnode_t *node = cliphead;
325 clipnode_t *temp;
326
327 while (node != NULL)
328 {
329 temp = node;
330 node = node->next;
331 gld_clipnode_Free(temp);
332 }
333
334 cliphead = NULL;
335 }
336
gld_FrustumAngle(void)337 angle_t gld_FrustumAngle(void)
338 {
339 double floatangle;
340 angle_t a1;
341
342 float tilt = (float)fabs(((double)(int)viewpitch) / ANG1);
343 if (tilt > 90.0f)
344 {
345 tilt = 90.0f;
346 }
347
348 // If the pitch is larger than this you can look all around at a FOV of 90
349 if (D_abs(viewpitch) > 46 * ANG1)
350 return 0xffffffff;
351
352 // ok, this is a gross hack that barely works...
353 // but at least it doesn't overestimate too much...
354 floatangle = 2.0f + (45.0f + (tilt / 1.9f)) * (float)render_fov * 48.0f / render_multiplier / 90.0f;
355 a1 = ANG1 * (int)floatangle;
356 if (a1 >= ANG180)
357 return 0xffffffff;
358 return a1;
359 }
360
361 //
362 // gld_FrustrumSetup
363 //
364
365 #define CALCMATRIX(a, b, c, d, e, f, g, h)\
366 (float)(viewMatrix[a] * projMatrix[b] + \
367 viewMatrix[c] * projMatrix[d] + \
368 viewMatrix[e] * projMatrix[f] + \
369 viewMatrix[g] * projMatrix[h])
370
371 #define NORMALIZE_PLANE(i)\
372 t = (float)sqrt(\
373 frustum[i][0] * frustum[i][0] + \
374 frustum[i][1] * frustum[i][1] + \
375 frustum[i][2] * frustum[i][2]); \
376 frustum[i][0] /= t; \
377 frustum[i][1] /= t; \
378 frustum[i][2] /= t; \
379 frustum[i][3] /= t
380
gld_FrustrumSetup(void)381 void gld_FrustrumSetup(void)
382 {
383 float t;
384 float clip[16];
385
386 glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
387 glGetDoublev(GL_MODELVIEW_MATRIX, viewMatrix);
388
389 clip[0] = CALCMATRIX(0, 0, 1, 4, 2, 8, 3, 12);
390 clip[1] = CALCMATRIX(0, 1, 1, 5, 2, 9, 3, 13);
391 clip[2] = CALCMATRIX(0, 2, 1, 6, 2, 10, 3, 14);
392 clip[3] = CALCMATRIX(0, 3, 1, 7, 2, 11, 3, 15);
393
394 clip[4] = CALCMATRIX(4, 0, 5, 4, 6, 8, 7, 12);
395 clip[5] = CALCMATRIX(4, 1, 5, 5, 6, 9, 7, 13);
396 clip[6] = CALCMATRIX(4, 2, 5, 6, 6, 10, 7, 14);
397 clip[7] = CALCMATRIX(4, 3, 5, 7, 6, 11, 7, 15);
398
399 clip[8] = CALCMATRIX(8, 0, 9, 4, 10, 8, 11, 12);
400 clip[9] = CALCMATRIX(8, 1, 9, 5, 10, 9, 11, 13);
401 clip[10] = CALCMATRIX(8, 2, 9, 6, 10, 10, 11, 14);
402 clip[11] = CALCMATRIX(8, 3, 9, 7, 10, 11, 11, 15);
403
404 clip[12] = CALCMATRIX(12, 0, 13, 4, 14, 8, 15, 12);
405 clip[13] = CALCMATRIX(12, 1, 13, 5, 14, 9, 15, 13);
406 clip[14] = CALCMATRIX(12, 2, 13, 6, 14, 10, 15, 14);
407 clip[15] = CALCMATRIX(12, 3, 13, 7, 14, 11, 15, 15);
408
409 // Right plane
410 frustum[0][0] = clip[ 3] - clip[ 0];
411 frustum[0][1] = clip[ 7] - clip[ 4];
412 frustum[0][2] = clip[11] - clip[ 8];
413 frustum[0][3] = clip[15] - clip[12];
414 NORMALIZE_PLANE(0);
415
416 // Left plane
417 frustum[1][0] = clip[ 3] + clip[ 0];
418 frustum[1][1] = clip[ 7] + clip[ 4];
419 frustum[1][2] = clip[11] + clip[ 8];
420 frustum[1][3] = clip[15] + clip[12];
421 NORMALIZE_PLANE(1);
422
423 // Bottom plane
424 frustum[2][0] = clip[ 3] + clip[ 1];
425 frustum[2][1] = clip[ 7] + clip[ 5];
426 frustum[2][2] = clip[11] + clip[ 9];
427 frustum[2][3] = clip[15] + clip[13];
428 NORMALIZE_PLANE(2);
429
430 // Top plane
431 frustum[3][0] = clip[ 3] - clip[ 1];
432 frustum[3][1] = clip[ 7] - clip[ 5];
433 frustum[3][2] = clip[11] - clip[ 9];
434 frustum[3][3] = clip[15] - clip[13];
435 NORMALIZE_PLANE(3);
436
437 // Far plane
438 frustum[4][0] = clip[ 3] - clip[ 2];
439 frustum[4][1] = clip[ 7] - clip[ 6];
440 frustum[4][2] = clip[11] - clip[10];
441 frustum[4][3] = clip[15] - clip[14];
442 NORMALIZE_PLANE(4);
443
444 // Near plane
445 frustum[5][0] = clip[ 3] + clip[ 2];
446 frustum[5][1] = clip[ 7] + clip[ 6];
447 frustum[5][2] = clip[11] + clip[10];
448 frustum[5][3] = clip[15] + clip[14];
449 NORMALIZE_PLANE(5);
450 }
451
gld_SphereInFrustum(float x,float y,float z,float radius)452 dboolean gld_SphereInFrustum(float x, float y, float z, float radius)
453 {
454 int p;
455
456 for (p = 0; p < 4; p++)
457 {
458 if (frustum[p][0] * x +
459 frustum[p][1] * y +
460 frustum[p][2] * z +
461 frustum[p][3] <= -radius)
462 {
463 return false;
464 }
465 }
466 return true;
467 }
468