558242992c3bf0038ad40a74e8827bad91c051e8
[xonotic/netradiant.git] / tools / quake3 / common / polylib.c
1 /*
2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22
23 #include "cmdlib.h"
24 #include "mathlib.h"
25 #include "inout.h"
26 #include "polylib.h"
27 #include "qfiles.h"
28
29
30 extern int numthreads;
31
32 // counters are only bumped when running single threaded,
33 // because they are an awefull coherence problem
34 int     c_active_windings;
35 int     c_peak_windings;
36 int     c_winding_allocs;
37 int     c_winding_points;
38
39 #define BOGUS_RANGE     WORLD_SIZE
40
41 void pw(winding_t *w)
42 {
43         int             i;
44         for (i=0 ; i<w->numpoints ; i++)
45                 Sys_Printf ("(%5.1f, %5.1f, %5.1f)\n",w->p[i][0], w->p[i][1],w->p[i][2]);
46 }
47
48
49 /*
50 =============
51 AllocWinding
52 =============
53 */
54 winding_t       *AllocWinding (int points)
55 {
56         winding_t       *w;
57         int                     s;
58
59   if (points >= MAX_POINTS_ON_WINDING)
60     Error ("AllocWinding failed: MAX_POINTS_ON_WINDING exceeded");
61
62         if (numthreads == 1)
63         {
64                 c_winding_allocs++;
65                 c_winding_points += points;
66                 c_active_windings++;
67                 if (c_active_windings > c_peak_windings)
68                         c_peak_windings = c_active_windings;
69         }
70         s = sizeof(vec_t)*3*points + sizeof(int);
71         w = safe_malloc (s);
72         memset (w, 0, s); 
73         return w;
74 }
75
76 void FreeWinding (winding_t *w)
77 {
78         if (*(unsigned *)w == 0xdeaddead)
79                 Error ("FreeWinding: freed a freed winding");
80         *(unsigned *)w = 0xdeaddead;
81
82         if (numthreads == 1)
83                 c_active_windings--;
84         free (w);
85 }
86
87 /*
88 ============
89 RemoveColinearPoints
90 ============
91 */
92 int     c_removed;
93
94 void    RemoveColinearPoints (winding_t *w)
95 {
96         int             i, j, k;
97         vec3_t  v1, v2;
98         int             nump;
99         vec3_t  p[MAX_POINTS_ON_WINDING];
100
101         nump = 0;
102         for (i=0 ; i<w->numpoints ; i++)
103         {
104                 j = (i+1)%w->numpoints;
105                 k = (i+w->numpoints-1)%w->numpoints;
106                 VectorSubtract (w->p[j], w->p[i], v1);
107                 VectorSubtract (w->p[i], w->p[k], v2);
108                 VectorNormalize(v1,v1);
109                 VectorNormalize(v2,v2);
110                 if (DotProduct(v1, v2) < 0.999)
111                 {
112                         VectorCopy (w->p[i], p[nump]);
113                         nump++;
114                 }
115         }
116
117         if (nump == w->numpoints)
118                 return;
119
120         if (numthreads == 1)
121                 c_removed += w->numpoints - nump;
122         w->numpoints = nump;
123         memcpy (w->p, p, nump*sizeof(p[0]));
124 }
125
126 /*
127 ============
128 WindingPlane
129 ============
130 */
131 void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist)
132 {
133         vec3_t  v1, v2;
134
135         VectorSubtract (w->p[1], w->p[0], v1);
136         VectorSubtract (w->p[2], w->p[0], v2);
137         CrossProduct (v2, v1, normal);
138         VectorNormalize (normal, normal);
139         *dist = DotProduct (w->p[0], normal);
140
141 }
142
143 /*
144 =============
145 WindingArea
146 =============
147 */
148 vec_t   WindingArea (winding_t *w)
149 {
150         int             i;
151         vec3_t  d1, d2, cross;
152         vec_t   total;
153
154         total = 0;
155         for (i=2 ; i<w->numpoints ; i++)
156         {
157                 VectorSubtract (w->p[i-1], w->p[0], d1);
158                 VectorSubtract (w->p[i], w->p[0], d2);
159                 CrossProduct (d1, d2, cross);
160                 total += 0.5 * VectorLength ( cross );
161         }
162         return total;
163 }
164
165 void    WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs)
166 {
167         vec_t   v;
168         int             i,j;
169
170         mins[0] = mins[1] = mins[2] = 99999;
171         maxs[0] = maxs[1] = maxs[2] = -99999;
172
173         for (i=0 ; i<w->numpoints ; i++)
174         {
175                 for (j=0 ; j<3 ; j++)
176                 {
177                         v = w->p[i][j];
178                         if (v < mins[j])
179                                 mins[j] = v;
180                         if (v > maxs[j])
181                                 maxs[j] = v;
182                 }
183         }
184 }
185
186 /*
187 =============
188 WindingCenter
189 =============
190 */
191 void    WindingCenter (winding_t *w, vec3_t center)
192 {
193         int             i;
194         float   scale;
195
196         VectorCopy (vec3_origin, center);
197         for (i=0 ; i<w->numpoints ; i++)
198                 VectorAdd (w->p[i], center, center);
199
200         scale = 1.0/w->numpoints;
201         VectorScale (center, scale, center);
202 }
203
204 /*
205 =================
206 BaseWindingForPlane
207 =================
208 */
209 winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist)
210 {
211         int             i, x;
212         vec_t   max, v;
213         vec3_t  org, vright, vup;
214         winding_t       *w;
215         
216 // find the major axis
217
218         max = -BOGUS_RANGE;
219         x = -1;
220         for (i=0 ; i<3; i++)
221         {
222                 v = fabs(normal[i]);
223                 if (v > max)
224                 {
225                         x = i;
226                         max = v;
227                 }
228         }
229         if (x==-1)
230                 Error ("BaseWindingForPlane: no axis found");
231                 
232         VectorCopy (vec3_origin, vup);  
233         switch (x)
234         {
235         case 0:
236         case 1:
237                 vup[2] = 1;
238                 break;          
239         case 2:
240                 vup[0] = 1;
241                 break;          
242         }
243
244         v = DotProduct (vup, normal);
245         VectorMA (vup, -v, normal, vup);
246         VectorNormalize (vup, vup);
247                 
248         VectorScale (normal, dist, org);
249         
250         CrossProduct (vup, normal, vright);
251         
252         VectorScale (vup, MAX_WORLD_COORD, vup);
253         VectorScale (vright, MAX_WORLD_COORD, vright);
254
255   // project a really big       axis aligned box onto the plane
256         w = AllocWinding (4);
257         
258         VectorSubtract (org, vright, w->p[0]);
259         VectorAdd (w->p[0], vup, w->p[0]);
260         
261         VectorAdd (org, vright, w->p[1]);
262         VectorAdd (w->p[1], vup, w->p[1]);
263         
264         VectorAdd (org, vright, w->p[2]);
265         VectorSubtract (w->p[2], vup, w->p[2]);
266         
267         VectorSubtract (org, vright, w->p[3]);
268         VectorSubtract (w->p[3], vup, w->p[3]);
269         
270         w->numpoints = 4;
271         
272         return w;       
273 }
274
275 /*
276 ==================
277 CopyWinding
278 ==================
279 */
280 winding_t       *CopyWinding (winding_t *w)
281 {
282         int                     size;
283         winding_t       *c;
284
285         c = AllocWinding (w->numpoints);
286         size = (int)((winding_t *)0)->p[w->numpoints];
287         memcpy (c, w, size);
288         return c;
289 }
290
291 /*
292 ==================
293 ReverseWinding
294 ==================
295 */
296 winding_t       *ReverseWinding (winding_t *w)
297 {
298         int                     i;
299         winding_t       *c;
300
301         c = AllocWinding (w->numpoints);
302         for (i=0 ; i<w->numpoints ; i++)
303         {
304                 VectorCopy (w->p[w->numpoints-1-i], c->p[i]);
305         }
306         c->numpoints = w->numpoints;
307         return c;
308 }
309
310
311 /*
312 =============
313 ClipWindingEpsilon
314 =============
315 */
316 void    ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist, 
317                                 vec_t epsilon, winding_t **front, winding_t **back)
318 {
319         vec_t   dists[MAX_POINTS_ON_WINDING+4];
320         int             sides[MAX_POINTS_ON_WINDING+4];
321         int             counts[3];
322         static  vec_t   dot;            // VC 4.2 optimizer bug if not static
323         int             i, j;
324         vec_t   *p1, *p2;
325         vec3_t  mid;
326         winding_t       *f, *b;
327         int             maxpts;
328         
329         counts[0] = counts[1] = counts[2] = 0;
330
331 // determine sides for each point
332         for (i=0 ; i<in->numpoints ; i++)
333   {
334
335                 dot = DotProduct (in->p[i], normal);
336                 dot -= dist;
337                 dists[i] = dot;
338                 if (dot > epsilon)
339                         sides[i] = SIDE_FRONT;
340                 else if (dot < -epsilon)
341                         sides[i] = SIDE_BACK;
342                 else
343                 {
344                         sides[i] = SIDE_ON;
345                 }
346                 counts[sides[i]]++;
347         }
348         sides[i] = sides[0];
349         dists[i] = dists[0];
350         
351         *front = *back = NULL;
352
353         if (!counts[0])
354         {
355                 *back = CopyWinding (in);
356                 return;
357         }
358         if (!counts[1])
359         {
360                 *front = CopyWinding (in);
361                 return;
362         }
363
364         maxpts = in->numpoints+4;       // cant use counts[0]+2 because
365                                                                 // of fp grouping errors
366
367         *front = f = AllocWinding (maxpts);
368         *back = b = AllocWinding (maxpts);
369                 
370         for (i=0 ; i<in->numpoints ; i++)
371         {
372                 p1 = in->p[i];
373                 
374                 if (sides[i] == SIDE_ON)
375                 {
376                         VectorCopy (p1, f->p[f->numpoints]);
377                         f->numpoints++;
378                         VectorCopy (p1, b->p[b->numpoints]);
379                         b->numpoints++;
380                         continue;
381                 }
382         
383                 if (sides[i] == SIDE_FRONT)
384                 {
385                         VectorCopy (p1, f->p[f->numpoints]);
386                         f->numpoints++;
387                 }
388                 if (sides[i] == SIDE_BACK)
389                 {
390                         VectorCopy (p1, b->p[b->numpoints]);
391                         b->numpoints++;
392                 }
393
394                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
395                         continue;
396                         
397         // generate a split point
398                 p2 = in->p[(i+1)%in->numpoints];
399                 
400                 dot = dists[i] / (dists[i]-dists[i+1]);
401                 for (j=0 ; j<3 ; j++)
402                 {       // avoid round off error when possible
403                         if (normal[j] == 1)
404                                 mid[j] = dist;
405                         else if (normal[j] == -1)
406                                 mid[j] = -dist;
407                         else
408                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
409                 }
410                         
411                 VectorCopy (mid, f->p[f->numpoints]);
412                 f->numpoints++;
413                 VectorCopy (mid, b->p[b->numpoints]);
414                 b->numpoints++;
415         }
416         
417         if (f->numpoints > maxpts || b->numpoints > maxpts)
418                 Error ("ClipWinding: points exceeded estimate");
419         if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING)
420                 Error ("ClipWinding: MAX_POINTS_ON_WINDING");
421 }
422
423
424 /*
425 =============
426 ChopWindingInPlace
427 =============
428 */
429 void ChopWindingInPlace (winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon)
430 {
431         winding_t       *in;
432         vec_t   dists[MAX_POINTS_ON_WINDING+4];
433         int             sides[MAX_POINTS_ON_WINDING+4];
434         int             counts[3];
435         static  vec_t   dot;            // VC 4.2 optimizer bug if not static
436         int             i, j;
437         vec_t   *p1, *p2;
438         vec3_t  mid;
439         winding_t       *f;
440         int             maxpts;
441
442         in = *inout;
443         counts[0] = counts[1] = counts[2] = 0;
444
445 // determine sides for each point
446         for (i=0 ; i<in->numpoints ; i++)
447         {
448                 dot = DotProduct (in->p[i], normal);
449                 dot -= dist;
450                 dists[i] = dot;
451                 if (dot > epsilon)
452                         sides[i] = SIDE_FRONT;
453                 else if (dot < -epsilon)
454                         sides[i] = SIDE_BACK;
455                 else
456                 {
457                         sides[i] = SIDE_ON;
458                 }
459                 counts[sides[i]]++;
460         }
461         sides[i] = sides[0];
462         dists[i] = dists[0];
463         
464         if (!counts[0])
465         {
466                 FreeWinding (in);
467                 *inout = NULL;
468                 return;
469         }
470         if (!counts[1])
471                 return;         // inout stays the same
472
473         maxpts = in->numpoints+4;       // cant use counts[0]+2 because
474                                                                 // of fp grouping errors
475
476         f = AllocWinding (maxpts);
477                 
478         for (i=0 ; i<in->numpoints ; i++)
479         {
480                 p1 = in->p[i];
481                 
482                 if (sides[i] == SIDE_ON)
483                 {
484                         VectorCopy (p1, f->p[f->numpoints]);
485                         f->numpoints++;
486                         continue;
487                 }
488         
489                 if (sides[i] == SIDE_FRONT)
490                 {
491                         VectorCopy (p1, f->p[f->numpoints]);
492                         f->numpoints++;
493                 }
494
495                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
496                         continue;
497                         
498         // generate a split point
499                 p2 = in->p[(i+1)%in->numpoints];
500                 
501                 dot = dists[i] / (dists[i]-dists[i+1]);
502                 for (j=0 ; j<3 ; j++)
503                 {       // avoid round off error when possible
504                         if (normal[j] == 1)
505                                 mid[j] = dist;
506                         else if (normal[j] == -1)
507                                 mid[j] = -dist;
508                         else
509                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
510                 }
511                         
512                 VectorCopy (mid, f->p[f->numpoints]);
513                 f->numpoints++;
514         }
515         
516         if (f->numpoints > maxpts)
517                 Error ("ClipWinding: points exceeded estimate");
518         if (f->numpoints > MAX_POINTS_ON_WINDING)
519                 Error ("ClipWinding: MAX_POINTS_ON_WINDING");
520
521         FreeWinding (in);
522         *inout = f;
523 }
524
525
526 /*
527 =================
528 ChopWinding
529
530 Returns the fragment of in that is on the front side
531 of the cliping plane.  The original is freed.
532 =================
533 */
534 winding_t       *ChopWinding (winding_t *in, vec3_t normal, vec_t dist)
535 {
536         winding_t       *f, *b;
537
538         ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b);
539         FreeWinding (in);
540         if (b)
541                 FreeWinding (b);
542         return f;
543 }
544
545
546 /*
547 =================
548 CheckWinding
549
550 =================
551 */
552 void CheckWinding (winding_t *w)
553 {
554         int             i, j;
555         vec_t   *p1, *p2;
556         vec_t   d, edgedist;
557         vec3_t  dir, edgenormal, facenormal;
558         vec_t   area;
559         vec_t   facedist;
560
561         if (w->numpoints < 3)
562                 Error ("CheckWinding: %i points",w->numpoints);
563         
564         area = WindingArea(w);
565         if (area < 1)
566                 Error ("CheckWinding: %f area", area);
567
568         WindingPlane (w, facenormal, &facedist);
569         
570         for (i=0 ; i<w->numpoints ; i++)
571         {
572                 p1 = w->p[i];
573
574                 for (j=0 ; j<3 ; j++)
575                         if (p1[j] > MAX_WORLD_COORD || p1[j] < MIN_WORLD_COORD)
576                                 Error ("CheckFace: MAX_WORLD_COORD exceeded: %f",p1[j]);
577
578                 j = i+1 == w->numpoints ? 0 : i+1;
579                 
580         // check the point is on the face plane
581                 d = DotProduct (p1, facenormal) - facedist;
582                 if (d < -ON_EPSILON || d > ON_EPSILON)
583                         Error ("CheckWinding: point off plane");
584         
585         // check the edge isnt degenerate
586                 p2 = w->p[j];
587                 VectorSubtract (p2, p1, dir);
588                 
589                 if (VectorLength (dir) < ON_EPSILON)
590                         Error ("CheckWinding: degenerate edge");
591                         
592                 CrossProduct (facenormal, dir, edgenormal);
593                 VectorNormalize (edgenormal, edgenormal);
594                 edgedist = DotProduct (p1, edgenormal);
595                 edgedist += ON_EPSILON;
596                 
597         // all other points must be on front side
598                 for (j=0 ; j<w->numpoints ; j++)
599                 {
600                         if (j == i)
601                                 continue;
602                         d = DotProduct (w->p[j], edgenormal);
603                         if (d > edgedist)
604                                 Error ("CheckWinding: non-convex");
605                 }
606         }
607 }
608
609
610 /*
611 ============
612 WindingOnPlaneSide
613 ============
614 */
615 int             WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist)
616 {
617         qboolean        front, back;
618         int                     i;
619         vec_t           d;
620
621         front = qfalse;
622         back = qfalse;
623         for (i=0 ; i<w->numpoints ; i++)
624         {
625                 d = DotProduct (w->p[i], normal) - dist;
626                 if (d < -ON_EPSILON)
627                 {
628                         if (front)
629                                 return SIDE_CROSS;
630                         back = qtrue;
631                         continue;
632                 }
633                 if (d > ON_EPSILON)
634                 {
635                         if (back)
636                                 return SIDE_CROSS;
637                         front = qtrue;
638                         continue;
639                 }
640         }
641
642         if (back)
643                 return SIDE_BACK;
644         if (front)
645                 return SIDE_FRONT;
646         return SIDE_ON;
647 }
648
649
650 /*
651 =================
652 AddWindingToConvexHull
653
654 Both w and *hull are on the same plane
655 =================
656 */
657 #define MAX_HULL_POINTS         128
658 void    AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal ) {
659         int                     i, j, k;
660         float           *p, *copy;
661         vec3_t          dir;
662         float           d;
663         int                     numHullPoints, numNew;
664         vec3_t          hullPoints[MAX_HULL_POINTS];
665         vec3_t          newHullPoints[MAX_HULL_POINTS];
666         vec3_t          hullDirs[MAX_HULL_POINTS];
667         qboolean        hullSide[MAX_HULL_POINTS];
668         qboolean        outside;
669
670         if ( !*hull ) {
671                 *hull = CopyWinding( w );
672                 return;
673         }
674
675         numHullPoints = (*hull)->numpoints;
676         memcpy( hullPoints, (*hull)->p, numHullPoints * sizeof(vec3_t) );
677
678         for ( i = 0 ; i < w->numpoints ; i++ ) {
679                 p = w->p[i];
680
681                 // calculate hull side vectors
682                 for ( j = 0 ; j < numHullPoints ; j++ ) {
683                         k = ( j + 1 ) % numHullPoints;
684
685                         VectorSubtract( hullPoints[k], hullPoints[j], dir );
686                         VectorNormalize( dir, dir );
687                         CrossProduct( normal, dir, hullDirs[j] );
688                 }
689
690                 outside = qfalse;
691                 for ( j = 0 ; j < numHullPoints ; j++ ) {
692                         VectorSubtract( p, hullPoints[j], dir );
693                         d = DotProduct( dir, hullDirs[j] );
694                         if ( d >= ON_EPSILON ) {
695                                 outside = qtrue;
696                         }
697                         if ( d >= -ON_EPSILON ) {
698                                 hullSide[j] = qtrue;
699                         } else {
700                                 hullSide[j] = qfalse;
701                         }
702                 }
703
704                 // if the point is effectively inside, do nothing
705                 if ( !outside ) {
706                         continue;
707                 }
708
709                 // find the back side to front side transition
710                 for ( j = 0 ; j < numHullPoints ; j++ ) {
711                         if ( !hullSide[ j % numHullPoints ] && hullSide[ (j + 1) % numHullPoints ] ) {
712                                 break;
713                         }
714                 }
715                 if ( j == numHullPoints ) {
716                         continue;
717                 }
718
719                 // insert the point here
720                 VectorCopy( p, newHullPoints[0] );
721                 numNew = 1;
722
723                 // copy over all points that aren't double fronts
724                 j = (j+1)%numHullPoints;
725                 for ( k = 0 ; k < numHullPoints ; k++ ) {
726                         if ( hullSide[ (j+k) % numHullPoints ] && hullSide[ (j+k+1) % numHullPoints ] ) {
727                                 continue;
728                         }
729                         copy = hullPoints[ (j+k+1) % numHullPoints ];
730                         VectorCopy( copy, newHullPoints[numNew] );
731                         numNew++;
732                 }
733
734                 numHullPoints = numNew;
735                 memcpy( hullPoints, newHullPoints, numHullPoints * sizeof(vec3_t) );
736         }
737
738         FreeWinding( *hull );
739         w = AllocWinding( numHullPoints );
740         w->numpoints = numHullPoints;
741         *hull = w;
742         memcpy( w->p, hullPoints, numHullPoints * sizeof(vec3_t) );
743 }
744
745