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