Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

l_poly.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 
00023 #include <malloc.h>
00024 #include "l_cmd.h"
00025 #include "l_math.h"
00026 #include "l_poly.h"
00027 #include "l_log.h"
00028 #include "l_mem.h"
00029 
00030 #define BOGUS_RANGE     65535
00031 
00032 extern int numthreads;
00033 
00034 // counters are only bumped when running single threaded,
00035 // because they are an awefull coherence problem
00036 int c_active_windings;
00037 int c_peak_windings;
00038 int c_winding_allocs;
00039 int c_winding_points;
00040 int c_windingmemory;
00041 int c_peak_windingmemory;
00042 
00043 char windingerror[1024];
00044 
00045 void pw(winding_t *w)
00046 {
00047     int     i;
00048     for (i=0 ; i<w->numpoints ; i++)
00049         printf ("(%5.3f, %5.3f, %5.3f)\n",w->p[i][0], w->p[i][1],w->p[i][2]);
00050 }
00051 
00052 
00053 void ResetWindings(void)
00054 {
00055     c_active_windings = 0;
00056     c_peak_windings = 0;
00057     c_winding_allocs = 0;
00058     c_winding_points = 0;
00059     c_windingmemory = 0;
00060     c_peak_windingmemory = 0;
00061 
00062     strcpy(windingerror, "");
00063 } //end of the function ResetWindings
00064 /*
00065 =============
00066 AllocWinding
00067 =============
00068 */
00069 winding_t *AllocWinding (int points)
00070 {
00071     winding_t   *w;
00072     int         s;
00073 
00074     s = sizeof(vec_t)*3*points + sizeof(int);
00075     w = GetMemory(s);
00076     memset(w, 0, s);
00077 
00078     if (numthreads == 1)
00079     {
00080         c_winding_allocs++;
00081         c_winding_points += points;
00082         c_active_windings++;
00083         if (c_active_windings > c_peak_windings)
00084             c_peak_windings = c_active_windings;
00085         c_windingmemory += MemorySize(w);
00086         if (c_windingmemory > c_peak_windingmemory)
00087             c_peak_windingmemory = c_windingmemory;
00088     } //end if
00089     return w;
00090 } //end of the function AllocWinding
00091 
00092 void FreeWinding (winding_t *w)
00093 {
00094     if (*(unsigned *)w == 0xdeaddead)
00095         Error ("FreeWinding: freed a freed winding");
00096 
00097     if (numthreads == 1)
00098     {
00099         c_active_windings--;
00100         c_windingmemory -= MemorySize(w);
00101     } //end if
00102 
00103     *(unsigned *)w = 0xdeaddead;
00104 
00105     FreeMemory(w);
00106 } //end of the function FreeWinding
00107 
00108 int WindingMemory(void)
00109 {
00110     return c_windingmemory;
00111 } //end of the function WindingMemory
00112 
00113 int WindingPeakMemory(void)
00114 {
00115     return c_peak_windingmemory;
00116 } //end of the function WindingPeakMemory
00117 
00118 int ActiveWindings(void)
00119 {
00120     return c_active_windings;
00121 } //end of the function ActiveWindings
00122 /*
00123 ============
00124 RemoveColinearPoints
00125 ============
00126 */
00127 int c_removed;
00128 
00129 void RemoveColinearPoints (winding_t *w)
00130 {
00131     int     i, j, k;
00132     vec3_t  v1, v2;
00133     int     nump;
00134     vec3_t  p[MAX_POINTS_ON_WINDING];
00135 
00136     nump = 0;
00137     for (i=0 ; i<w->numpoints ; i++)
00138     {
00139         j = (i+1)%w->numpoints;
00140         k = (i+w->numpoints-1)%w->numpoints;
00141         VectorSubtract (w->p[j], w->p[i], v1);
00142         VectorSubtract (w->p[i], w->p[k], v2);
00143         VectorNormalize(v1);
00144         VectorNormalize(v2);
00145         if (DotProduct(v1, v2) < 0.999)
00146         {
00147             if (nump >= MAX_POINTS_ON_WINDING)
00148                 Error("RemoveColinearPoints: MAX_POINTS_ON_WINDING");
00149             VectorCopy (w->p[i], p[nump]);
00150             nump++;
00151         }
00152     }
00153 
00154     if (nump == w->numpoints)
00155         return;
00156 
00157     if (numthreads == 1)
00158         c_removed += w->numpoints - nump;
00159     w->numpoints = nump;
00160     memcpy (w->p, p, nump*sizeof(p[0]));
00161 }
00162 
00163 /*
00164 ============
00165 WindingPlane
00166 ============
00167 */
00168 void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist)
00169 {
00170     vec3_t v1, v2;
00171     int i;
00172 
00173     //find two vectors each longer than 0.5 units
00174     for (i = 0; i < w->numpoints; i++)
00175     {
00176         VectorSubtract(w->p[(i+1) % w->numpoints], w->p[i], v1);
00177         VectorSubtract(w->p[(i+2) % w->numpoints], w->p[i], v2);
00178         if (VectorLength(v1) > 0.5 && VectorLength(v2) > 0.5) break;
00179     } //end for
00180     CrossProduct(v2, v1, normal);
00181     VectorNormalize(normal);
00182     *dist = DotProduct(w->p[0], normal);
00183 } //end of the function WindingPlane
00184 
00185 /*
00186 =============
00187 WindingArea
00188 =============
00189 */
00190 vec_t   WindingArea (winding_t *w)
00191 {
00192     int     i;
00193     vec3_t  d1, d2, cross;
00194     vec_t   total;
00195 
00196     total = 0;
00197     for (i=2 ; i<w->numpoints ; i++)
00198     {
00199         VectorSubtract (w->p[i-1], w->p[0], d1);
00200         VectorSubtract (w->p[i], w->p[0], d2);
00201         CrossProduct (d1, d2, cross);
00202         total += 0.5 * VectorLength ( cross );
00203     }
00204     return total;
00205 }
00206 
00207 void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs)
00208 {
00209     vec_t   v;
00210     int     i,j;
00211 
00212     mins[0] = mins[1] = mins[2] = 99999;
00213     maxs[0] = maxs[1] = maxs[2] = -99999;
00214 
00215     for (i=0 ; i<w->numpoints ; i++)
00216     {
00217         for (j=0 ; j<3 ; j++)
00218         {
00219             v = w->p[i][j];
00220             if (v < mins[j])
00221                 mins[j] = v;
00222             if (v > maxs[j])
00223                 maxs[j] = v;
00224         }
00225     }
00226 }
00227 
00228 /*
00229 =============
00230 WindingCenter
00231 =============
00232 */
00233 void    WindingCenter (winding_t *w, vec3_t center)
00234 {
00235     int     i;
00236     float   scale;
00237 
00238     VectorCopy (vec3_origin, center);
00239     for (i=0 ; i<w->numpoints ; i++)
00240         VectorAdd (w->p[i], center, center);
00241 
00242     scale = 1.0/w->numpoints;
00243     VectorScale (center, scale, center);
00244 }
00245 
00246 /*
00247 =================
00248 BaseWindingForPlane
00249 =================
00250 */
00251 winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist)
00252 {
00253     int     i, x;
00254     vec_t   max, v;
00255     vec3_t  org, vright, vup;
00256     winding_t   *w;
00257     
00258 // find the major axis
00259 
00260     max = -BOGUS_RANGE;
00261     x = -1;
00262     for (i=0 ; i<3; i++)
00263     {
00264         v = fabs(normal[i]);
00265         if (v > max)
00266         {
00267             x = i;
00268             max = v;
00269         }
00270     }
00271     if (x==-1)
00272         Error ("BaseWindingForPlane: no axis found");
00273         
00274     VectorCopy (vec3_origin, vup);  
00275     switch (x)
00276     {
00277     case 0:
00278     case 1:
00279         vup[2] = 1;
00280         break;      
00281     case 2:
00282         vup[0] = 1;
00283         break;      
00284     }
00285 
00286     v = DotProduct (vup, normal);
00287     VectorMA (vup, -v, normal, vup);
00288     VectorNormalize (vup);
00289         
00290     VectorScale (normal, dist, org);
00291     
00292     CrossProduct (vup, normal, vright);
00293     
00294     VectorScale (vup, BOGUS_RANGE, vup);
00295     VectorScale (vright, BOGUS_RANGE, vright);
00296 
00297 // project a really big axis aligned box onto the plane
00298     w = AllocWinding (4);
00299     
00300     VectorSubtract (org, vright, w->p[0]);
00301     VectorAdd (w->p[0], vup, w->p[0]);
00302     
00303     VectorAdd (org, vright, w->p[1]);
00304     VectorAdd (w->p[1], vup, w->p[1]);
00305     
00306     VectorAdd (org, vright, w->p[2]);
00307     VectorSubtract (w->p[2], vup, w->p[2]);
00308     
00309     VectorSubtract (org, vright, w->p[3]);
00310     VectorSubtract (w->p[3], vup, w->p[3]);
00311     
00312     w->numpoints = 4;
00313     
00314     return w;   
00315 }
00316 
00317 /*
00318 ==================
00319 CopyWinding
00320 ==================
00321 */
00322 winding_t *CopyWinding (winding_t *w)
00323 {
00324     int         size;
00325     winding_t   *c;
00326 
00327     c = AllocWinding (w->numpoints);
00328     size = (int)((winding_t *)0)->p[w->numpoints];
00329     memcpy (c, w, size);
00330     return c;
00331 }
00332 
00333 /*
00334 ==================
00335 ReverseWinding
00336 ==================
00337 */
00338 winding_t   *ReverseWinding (winding_t *w)
00339 {
00340     int         i;
00341     winding_t   *c;
00342 
00343     c = AllocWinding (w->numpoints);
00344     for (i=0 ; i<w->numpoints ; i++)
00345     {
00346         VectorCopy (w->p[w->numpoints-1-i], c->p[i]);
00347     }
00348     c->numpoints = w->numpoints;
00349     return c;
00350 }
00351 
00352 
00353 /*
00354 =============
00355 ClipWindingEpsilon
00356 =============
00357 */
00358 void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist, 
00359                 vec_t epsilon, winding_t **front, winding_t **back)
00360 {
00361     vec_t   dists[MAX_POINTS_ON_WINDING+4];
00362     int     sides[MAX_POINTS_ON_WINDING+4];
00363     int     counts[3];
00364     //MrElusive: DOH can't use statics when unsing multithreading!!!
00365     vec_t dot;      // VC 4.2 optimizer bug if not static
00366     int     i, j;
00367     vec_t   *p1, *p2;
00368     vec3_t  mid;
00369     winding_t   *f, *b;
00370     int     maxpts;
00371     
00372     counts[0] = counts[1] = counts[2] = 0;
00373 
00374 // determine sides for each point
00375     for (i=0 ; i<in->numpoints ; i++)
00376     {
00377         dot = DotProduct (in->p[i], normal);
00378         dot -= dist;
00379         dists[i] = dot;
00380         if (dot > epsilon)
00381             sides[i] = SIDE_FRONT;
00382         else if (dot < -epsilon)
00383             sides[i] = SIDE_BACK;
00384         else
00385         {
00386             sides[i] = SIDE_ON;
00387         }
00388         counts[sides[i]]++;
00389     }
00390     sides[i] = sides[0];
00391     dists[i] = dists[0];
00392     
00393     *front = *back = NULL;
00394 
00395     if (!counts[0])
00396     {
00397         *back = CopyWinding (in);
00398         return;
00399     }
00400     if (!counts[1])
00401     {
00402         *front = CopyWinding (in);
00403         return;
00404     }
00405 
00406     maxpts = in->numpoints+4;   // cant use counts[0]+2 because
00407                                 // of fp grouping errors
00408 
00409     *front = f = AllocWinding (maxpts);
00410     *back = b = AllocWinding (maxpts);
00411         
00412     for (i=0 ; i<in->numpoints ; i++)
00413     {
00414         p1 = in->p[i];
00415         
00416         if (sides[i] == SIDE_ON)
00417         {
00418             VectorCopy (p1, f->p[f->numpoints]);
00419             f->numpoints++;
00420             VectorCopy (p1, b->p[b->numpoints]);
00421             b->numpoints++;
00422             continue;
00423         }
00424     
00425         if (sides[i] == SIDE_FRONT)
00426         {
00427             VectorCopy (p1, f->p[f->numpoints]);
00428             f->numpoints++;
00429         }
00430         if (sides[i] == SIDE_BACK)
00431         {
00432             VectorCopy (p1, b->p[b->numpoints]);
00433             b->numpoints++;
00434         }
00435 
00436         if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
00437             continue;
00438             
00439     // generate a split point
00440         p2 = in->p[(i+1)%in->numpoints];
00441         
00442         dot = dists[i] / (dists[i]-dists[i+1]);
00443         for (j=0 ; j<3 ; j++)
00444         {   // avoid round off error when possible
00445             if (normal[j] == 1)
00446                 mid[j] = dist;
00447             else if (normal[j] == -1)
00448                 mid[j] = -dist;
00449             else
00450                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
00451         }
00452             
00453         VectorCopy (mid, f->p[f->numpoints]);
00454         f->numpoints++;
00455         VectorCopy (mid, b->p[b->numpoints]);
00456         b->numpoints++;
00457     }
00458     
00459     if (f->numpoints > maxpts || b->numpoints > maxpts)
00460         Error ("ClipWinding: points exceeded estimate");
00461     if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING)
00462         Error ("ClipWinding: MAX_POINTS_ON_WINDING");
00463 }
00464 
00465 
00466 /*
00467 =============
00468 ChopWindingInPlace
00469 =============
00470 */
00471 void ChopWindingInPlace (winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon)
00472 {
00473     winding_t *in;
00474     vec_t   dists[MAX_POINTS_ON_WINDING+4];
00475     int sides[MAX_POINTS_ON_WINDING+4];
00476     int counts[3];
00477     //MrElusive: DOH can't use statics when unsing multithreading!!!
00478     vec_t dot;      // VC 4.2 optimizer bug if not static
00479     int i, j;
00480     vec_t *p1, *p2;
00481     vec3_t mid;
00482     winding_t *f;
00483     int maxpts;
00484 
00485     in = *inout;
00486     counts[0] = counts[1] = counts[2] = 0;
00487 
00488 // determine sides for each point
00489     for (i=0 ; i<in->numpoints ; i++)
00490     {
00491         dot = DotProduct (in->p[i], normal);
00492         dot -= dist;
00493         dists[i] = dot;
00494         if (dot > epsilon)
00495             sides[i] = SIDE_FRONT;
00496         else if (dot < -epsilon)
00497             sides[i] = SIDE_BACK;
00498         else
00499         {
00500             sides[i] = SIDE_ON;
00501         }
00502         counts[sides[i]]++;
00503     }
00504     sides[i] = sides[0];
00505     dists[i] = dists[0];
00506     
00507     if (!counts[0])
00508     {
00509         FreeWinding (in);
00510         *inout = NULL;
00511         return;
00512     }
00513     if (!counts[1])
00514         return;     // inout stays the same
00515 
00516     maxpts = in->numpoints+4;   // cant use counts[0]+2 because
00517                                 // of fp grouping errors
00518 
00519     f = AllocWinding (maxpts);
00520         
00521     for (i=0 ; i<in->numpoints ; i++)
00522     {
00523         p1 = in->p[i];
00524         
00525         if (sides[i] == SIDE_ON)
00526         {
00527             VectorCopy (p1, f->p[f->numpoints]);
00528             f->numpoints++;
00529             continue;
00530         }
00531     
00532         if (sides[i] == SIDE_FRONT)
00533         {
00534             VectorCopy (p1, f->p[f->numpoints]);
00535             f->numpoints++;
00536         }
00537 
00538         if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
00539             continue;
00540             
00541     // generate a split point
00542         p2 = in->p[(i+1)%in->numpoints];
00543         
00544         dot = dists[i] / (dists[i]-dists[i+1]);
00545         for (j=0 ; j<3 ; j++)
00546         {   // avoid round off error when possible
00547             if (normal[j] == 1)
00548                 mid[j] = dist;
00549             else if (normal[j] == -1)
00550                 mid[j] = -dist;
00551             else
00552                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
00553         }
00554             
00555         VectorCopy (mid, f->p[f->numpoints]);
00556         f->numpoints++;
00557     }
00558     
00559     if (f->numpoints > maxpts)
00560         Error ("ClipWinding: points exceeded estimate");
00561     if (f->numpoints > MAX_POINTS_ON_WINDING)
00562         Error ("ClipWinding: MAX_POINTS_ON_WINDING");
00563 
00564     FreeWinding (in);
00565     *inout = f;
00566 }
00567 
00568 
00569 /*
00570 =================
00571 ChopWinding
00572 
00573 Returns the fragment of in that is on the front side
00574 of the cliping plane.  The original is freed.
00575 =================
00576 */
00577 winding_t   *ChopWinding (winding_t *in, vec3_t normal, vec_t dist)
00578 {
00579     winding_t   *f, *b;
00580 
00581     ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b);
00582     FreeWinding (in);
00583     if (b)
00584         FreeWinding (b);
00585     return f;
00586 }
00587 
00588 
00589 /*
00590 =================
00591 CheckWinding
00592 
00593 =================
00594 */
00595 void CheckWinding (winding_t *w)
00596 {
00597     int     i, j;
00598     vec_t   *p1, *p2;
00599     vec_t   d, edgedist;
00600     vec3_t  dir, edgenormal, facenormal;
00601     vec_t   area;
00602     vec_t   facedist;
00603 
00604     if (w->numpoints < 3)
00605         Error ("CheckWinding: %i points",w->numpoints);
00606     
00607     area = WindingArea(w);
00608     if (area < 1)
00609         Error ("CheckWinding: %f area", area);
00610 
00611     WindingPlane (w, facenormal, &facedist);
00612     
00613     for (i=0 ; i<w->numpoints ; i++)
00614     {
00615         p1 = w->p[i];
00616 
00617         for (j=0 ; j<3 ; j++)
00618             if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE)
00619                 Error ("CheckWinding: BUGUS_RANGE: %f",p1[j]);
00620 
00621         j = i+1 == w->numpoints ? 0 : i+1;
00622         
00623     // check the point is on the face plane
00624         d = DotProduct (p1, facenormal) - facedist;
00625         if (d < -ON_EPSILON || d > ON_EPSILON)
00626             Error ("CheckWinding: point off plane");
00627     
00628     // check the edge isnt degenerate
00629         p2 = w->p[j];
00630         VectorSubtract (p2, p1, dir);
00631         
00632         if (VectorLength (dir) < ON_EPSILON)
00633             Error ("CheckWinding: degenerate edge");
00634             
00635         CrossProduct (facenormal, dir, edgenormal);
00636         VectorNormalize (edgenormal);
00637         edgedist = DotProduct (p1, edgenormal);
00638         edgedist += ON_EPSILON;
00639         
00640     // all other points must be on front side
00641         for (j=0 ; j<w->numpoints ; j++)
00642         {
00643             if (j == i)
00644                 continue;
00645             d = DotProduct (w->p[j], edgenormal);
00646             if (d > edgedist)
00647                 Error ("CheckWinding: non-convex");
00648         }
00649     }
00650 }
00651 
00652 
00653 /*
00654 ============
00655 WindingOnPlaneSide
00656 ============
00657 */
00658 int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist)
00659 {
00660     qboolean    front, back;
00661     int         i;
00662     vec_t       d;
00663 
00664     front = false;
00665     back = false;
00666     for (i=0 ; i<w->numpoints ; i++)
00667     {
00668         d = DotProduct (w->p[i], normal) - dist;
00669         if (d < -ON_EPSILON)
00670         {
00671             if (front)
00672                 return SIDE_CROSS;
00673             back = true;
00674             continue;
00675         }
00676         if (d > ON_EPSILON)
00677         {
00678             if (back)
00679                 return SIDE_CROSS;
00680             front = true;
00681             continue;
00682         }
00683     }
00684 
00685     if (back)
00686         return SIDE_BACK;
00687     if (front)
00688         return SIDE_FRONT;
00689     return SIDE_ON;
00690 }
00691 
00692 //#ifdef ME
00693     #define CONTINUOUS_EPSILON  0.005
00694 //#else
00695 //  #define CONTINUOUS_EPSILON  0.001
00696 //#endif
00697 
00698 /*
00699 =============
00700 TryMergeWinding
00701 
00702 If two polygons share a common edge and the edges that meet at the
00703 common points are both inside the other polygons, merge them
00704 
00705 Returns NULL if the faces couldn't be merged, or the new face.
00706 The originals will NOT be freed.
00707 =============
00708 */
00709 
00710 winding_t *TryMergeWinding (winding_t *f1, winding_t *f2, vec3_t planenormal)
00711 {
00712     vec_t       *p1, *p2, *p3, *p4, *back;
00713     winding_t   *newf;
00714     int         i, j, k, l;
00715     vec3_t      normal, delta;
00716     vec_t       dot;
00717     qboolean    keep1, keep2;
00718     
00719 
00720     //
00721     // find a common edge
00722     //  
00723     p1 = p2 = NULL; // stop compiler warning
00724     j = 0;          // 
00725     
00726     for (i = 0; i < f1->numpoints; i++)
00727     {
00728         p1 = f1->p[i];
00729         p2 = f1->p[(i+1) % f1->numpoints];
00730         for (j = 0; j < f2->numpoints; j++)
00731         {
00732             p3 = f2->p[j];
00733             p4 = f2->p[(j+1) % f2->numpoints];
00734             for (k = 0; k < 3; k++)
00735             {
00736                 if (fabs(p1[k] - p4[k]) > 0.1)//EQUAL_EPSILON) //ME
00737                     break;
00738                 if (fabs(p2[k] - p3[k]) > 0.1)//EQUAL_EPSILON) //ME
00739                     break;
00740             } //end for
00741             if (k==3)
00742                 break;
00743         } //end for
00744         if (j < f2->numpoints)
00745             break;
00746     } //end for
00747     
00748     if (i == f1->numpoints)
00749         return NULL;            // no matching edges
00750 
00751     //
00752     // check slope of connected lines
00753     // if the slopes are colinear, the point can be removed
00754     //
00755     back = f1->p[(i+f1->numpoints-1)%f1->numpoints];
00756     VectorSubtract (p1, back, delta);
00757     CrossProduct (planenormal, delta, normal);
00758     VectorNormalize (normal);
00759     
00760     back = f2->p[(j+2)%f2->numpoints];
00761     VectorSubtract (back, p1, delta);
00762     dot = DotProduct (delta, normal);
00763     if (dot > CONTINUOUS_EPSILON)
00764         return NULL;            // not a convex polygon
00765     keep1 = (qboolean)(dot < -CONTINUOUS_EPSILON);
00766     
00767     back = f1->p[(i+2)%f1->numpoints];
00768     VectorSubtract (back, p2, delta);
00769     CrossProduct (planenormal, delta, normal);
00770     VectorNormalize (normal);
00771 
00772     back = f2->p[(j+f2->numpoints-1)%f2->numpoints];
00773     VectorSubtract (back, p2, delta);
00774     dot = DotProduct (delta, normal);
00775     if (dot > CONTINUOUS_EPSILON)
00776         return NULL;            // not a convex polygon
00777     keep2 = (qboolean)(dot < -CONTINUOUS_EPSILON);
00778 
00779     //
00780     // build the new polygon
00781     //
00782     newf = AllocWinding (f1->numpoints + f2->numpoints);
00783     
00784     // copy first polygon
00785     for (k=(i+1)%f1->numpoints ; k != i ; k=(k+1)%f1->numpoints)
00786     {
00787         if (k==(i+1)%f1->numpoints && !keep2)
00788             continue;
00789         
00790         VectorCopy (f1->p[k], newf->p[newf->numpoints]);
00791         newf->numpoints++;
00792     }
00793     
00794     // copy second polygon
00795     for (l= (j+1)%f2->numpoints ; l != j ; l=(l+1)%f2->numpoints)
00796     {
00797         if (l==(j+1)%f2->numpoints && !keep1)
00798             continue;
00799         VectorCopy (f2->p[l], newf->p[newf->numpoints]);
00800         newf->numpoints++;
00801     }
00802 
00803     return newf;
00804 }
00805 
00806 //#ifdef ME
00807 //===========================================================================
00808 //
00809 // Parameter:               -
00810 // Returns:                 -
00811 // Changes Globals:     -
00812 //===========================================================================
00813 winding_t *MergeWindings(winding_t *w1, winding_t *w2, vec3_t planenormal)
00814 {
00815     winding_t *neww;
00816     float dist;
00817     int i, j, n, found, insertafter;
00818     int sides[MAX_POINTS_ON_WINDING+4];
00819     vec3_t newp[MAX_POINTS_ON_WINDING+4];
00820     int numpoints;
00821     vec3_t edgevec, sepnormal, v;
00822 
00823     RemoveEqualPoints(w1, 0.2);
00824     numpoints = w1->numpoints;
00825     memcpy(newp, w1->p, w1->numpoints * sizeof(vec3_t));
00826     //
00827     for (i = 0; i < w2->numpoints; i++)
00828     {
00829         VectorCopy(w2->p[i], v);
00830         for (j = 0; j < numpoints; j++)
00831         {
00832             VectorSubtract(newp[(j+1)%numpoints],
00833                             newp[(j)%numpoints], edgevec);
00834             CrossProduct(edgevec, planenormal, sepnormal);
00835             VectorNormalize(sepnormal);
00836             if (VectorLength(sepnormal) < 0.9)
00837             {
00838                 //remove the point from the new winding
00839                 for (n = j; n < numpoints-1; n++)
00840                 {
00841                     VectorCopy(newp[n+1], newp[n]);
00842                     sides[n] = sides[n+1];
00843                 } //end for
00844                 numpoints--;
00845                 j--;
00846                 Log_Print("MergeWindings: degenerate edge on winding %f %f %f\n", sepnormal[0],
00847                                                                                 sepnormal[1],
00848                                                                                 sepnormal[2]);
00849                 continue;
00850             } //end if
00851             dist = DotProduct(newp[(j)%numpoints], sepnormal);
00852             if (DotProduct(v, sepnormal) - dist < -0.1) sides[j] = SIDE_BACK;
00853             else sides[j] = SIDE_FRONT;
00854         } //end for
00855         //remove all unnecesary points
00856         for (j = 0; j < numpoints;)
00857         {
00858             if (sides[j] == SIDE_BACK
00859                 && sides[(j+1)%numpoints] == SIDE_BACK)
00860             {
00861                 //remove the point from the new winding
00862                 for (n = (j+1)%numpoints; n < numpoints-1; n++)
00863                 {
00864                     VectorCopy(newp[n+1], newp[n]);
00865                     sides[n] = sides[n+1];
00866                 } //end for
00867                 numpoints--;
00868             } //end if
00869             else
00870             {
00871                 j++;
00872             } //end else
00873         } //end for
00874         //
00875         found = false;
00876         for (j = 0; j < numpoints; j++)
00877         {
00878             if (sides[j] == SIDE_FRONT
00879                 && sides[(j+1)%numpoints] == SIDE_BACK)
00880             {
00881                 if (found) Log_Print("Warning: MergeWindings: front to back found twice\n");
00882                 found = true;
00883             } //end if
00884         } //end for
00885         //
00886         for (j = 0; j < numpoints; j++)
00887         {
00888             if (sides[j] == SIDE_FRONT
00889                 && sides[(j+1)%numpoints] == SIDE_BACK)
00890             {
00891                 insertafter = (j+1)%numpoints;
00892                 //insert the new point after j+1
00893                 for (n = numpoints-1; n > insertafter; n--)
00894                 {
00895                     VectorCopy(newp[n], newp[n+1]);
00896                 } //end for
00897                 numpoints++;
00898                 VectorCopy(v, newp[(insertafter+1)%numpoints]);
00899                 break;
00900             } //end if
00901         } //end for
00902     } //end for
00903     neww = AllocWinding(numpoints);
00904     neww->numpoints = numpoints;
00905     memcpy(neww->p, newp, numpoints * sizeof(vec3_t));
00906     RemoveColinearPoints(neww);
00907     return neww;
00908 } //end of the function MergeWindings
00909 //===========================================================================
00910 //
00911 // Parameter:               -
00912 // Returns:                 -
00913 // Changes Globals:     -
00914 //===========================================================================
00915 char *WindingErrorString(void)
00916 {
00917     return windingerror;
00918 } //end of the function WindingErrorString
00919 //===========================================================================
00920 //
00921 // Parameter:               -
00922 // Returns:                 -
00923 // Changes Globals:     -
00924 //===========================================================================
00925 int WindingError(winding_t *w)
00926 {
00927     int     i, j;
00928     vec_t   *p1, *p2;
00929     vec_t   d, edgedist;
00930     vec3_t  dir, edgenormal, facenormal;
00931     vec_t   area;
00932     vec_t   facedist;
00933 
00934     if (w->numpoints < 3)
00935     {
00936         sprintf(windingerror, "winding %i points", w->numpoints);
00937         return WE_NOTENOUGHPOINTS;
00938     } //end if
00939     
00940     area = WindingArea(w);
00941     if (area < 1)
00942     {
00943         sprintf(windingerror, "winding %f area", area);
00944         return WE_SMALLAREA;
00945     } //end if
00946 
00947     WindingPlane (w, facenormal, &facedist);
00948     
00949     for (i=0 ; i<w->numpoints ; i++)
00950     {
00951         p1 = w->p[i];
00952 
00953         for (j=0 ; j<3 ; j++)
00954         {
00955             if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE)
00956             {
00957                 sprintf(windingerror, "winding point %d BUGUS_RANGE \'%f %f %f\'", j, p1[0], p1[1], p1[2]);
00958                 return WE_POINTBOGUSRANGE;
00959             } //end if
00960         } //end for
00961 
00962         j = i+1 == w->numpoints ? 0 : i+1;
00963         
00964     // check the point is on the face plane
00965         d = DotProduct (p1, facenormal) - facedist;
00966         if (d < -ON_EPSILON || d > ON_EPSILON)
00967         {
00968             sprintf(windingerror, "winding point %d off plane", i);
00969             return WE_POINTOFFPLANE;
00970         } //end if
00971     
00972     // check the edge isnt degenerate
00973         p2 = w->p[j];
00974         VectorSubtract (p2, p1, dir);
00975         
00976         if (VectorLength (dir) < ON_EPSILON)
00977         {
00978             sprintf(windingerror, "winding degenerate edge %d-%d", i, j);
00979             return WE_DEGENERATEEDGE;
00980         } //end if
00981             
00982         CrossProduct (facenormal, dir, edgenormal);
00983         VectorNormalize (edgenormal);
00984         edgedist = DotProduct (p1, edgenormal);
00985         edgedist += ON_EPSILON;
00986         
00987     // all other points must be on front side
00988         for (j=0 ; j<w->numpoints ; j++)
00989         {
00990             if (j == i)
00991                 continue;
00992             d = DotProduct (w->p[j], edgenormal);
00993             if (d > edgedist)
00994             {
00995                 sprintf(windingerror, "winding non-convex");
00996                 return WE_NONCONVEX;
00997             } //end if
00998         } //end for
00999     } //end for
01000     return WE_NONE;
01001 } //end of the function WindingError
01002 //===========================================================================
01003 //
01004 // Parameter:               -
01005 // Returns:                 -
01006 // Changes Globals:     -
01007 //===========================================================================
01008 void RemoveEqualPoints(winding_t *w, float epsilon)
01009 {
01010     int i, nump;
01011     vec3_t v;
01012     vec3_t p[MAX_POINTS_ON_WINDING];
01013 
01014     VectorCopy(w->p[0], p[0]);
01015     nump = 1;
01016     for (i = 1; i < w->numpoints; i++)
01017     {
01018         VectorSubtract(w->p[i], p[nump-1], v);
01019         if (VectorLength(v) > epsilon)
01020         {
01021             if (nump >= MAX_POINTS_ON_WINDING)
01022                 Error("RemoveColinearPoints: MAX_POINTS_ON_WINDING");
01023             VectorCopy (w->p[i], p[nump]);
01024             nump++;
01025         } //end if
01026     } //end for
01027 
01028     if (nump == w->numpoints)
01029         return;
01030 
01031     w->numpoints = nump;
01032     memcpy(w->p, p, nump * sizeof(p[0]));
01033 } //end of the function RemoveEqualPoints
01034 //===========================================================================
01035 // adds the given point to a winding at the given spot
01036 // (for instance when spot is zero then the point is added at position zero)
01037 // the original winding is NOT freed
01038 //
01039 // Parameter:               -
01040 // Returns:                 the new winding with the added point
01041 // Changes Globals:     -
01042 //===========================================================================
01043 winding_t *AddWindingPoint(winding_t *w, vec3_t point, int spot)
01044 {
01045     int i, j;
01046     winding_t *neww;
01047 
01048     if (spot > w->numpoints)
01049     {
01050         Error("AddWindingPoint: num > w->numpoints");
01051     } //end if
01052     if (spot < 0)
01053     {
01054         Error("AddWindingPoint: num < 0");
01055     } //end if
01056     neww = AllocWinding(w->numpoints + 1);
01057     neww->numpoints = w->numpoints + 1;
01058     for (i = 0, j = 0; i < neww->numpoints; i++)
01059     {
01060         if (i == spot)
01061         {
01062             VectorCopy(point, neww->p[i]);
01063         } //end if
01064         else
01065         {
01066             VectorCopy(w->p[j], neww->p[i]);
01067             j++;
01068         } //end else
01069     } //end for
01070     return neww;
01071 } //end of the function AddWindingPoint
01072 //===========================================================================
01073 // the position where the new point should be added in the winding is
01074 // stored in *spot
01075 //
01076 // Parameter:               -
01077 // Returns:                 true if the point is on the winding
01078 // Changes Globals:     -
01079 //===========================================================================
01080 #define MELT_ON_EPSILON     0.2
01081 
01082 int PointOnWinding(winding_t *w, vec3_t normal, float dist, vec3_t point, int *spot)
01083 {
01084     int i, j;
01085     vec3_t v1, v2;
01086     vec3_t edgenormal, edgevec;
01087     float edgedist, dot;
01088 
01089     *spot = 0;
01090     //the point must be on the winding plane
01091     dot = DotProduct(point, normal) - dist;
01092     if (dot < -MELT_ON_EPSILON || dot > MELT_ON_EPSILON) return false;
01093     //
01094     for (i = 0; i < w->numpoints; i++)
01095     {
01096         j = (i+1) % w->numpoints;
01097         //get a plane orthogonal to the winding plane through the edge
01098         VectorSubtract(w->p[j], w->p[i], edgevec);
01099         CrossProduct(normal, edgevec, edgenormal);
01100         VectorNormalize(edgenormal);
01101         edgedist = DotProduct(edgenormal, w->p[i]);
01102         //point must be not too far from the plane
01103         dot = DotProduct(point, edgenormal) - edgedist;
01104         if (dot < -MELT_ON_EPSILON || dot > MELT_ON_EPSILON) continue;
01105         //vector from first point of winding to the point to test
01106         VectorSubtract(point, w->p[i], v1);
01107         //vector from second point of winding to the point to test
01108         VectorSubtract(point, w->p[j], v2);
01109         //if the length of the vector is not larger than 0.5 units then
01110         //the point is assumend to be the same as one of the winding points
01111         if (VectorNormalize(v1) < 0.5) return false;
01112         if (VectorNormalize(v2) < 0.5) return false;
01113         //point must be between the two winding points
01114         //(the two vectors must be directed towards each other, and on the
01115         //same straight line)
01116         if (DotProduct(v1, v2) < -0.99)
01117         {
01118             *spot = i + 1;
01119             return true;
01120         } //end if
01121     } //end for
01122     return false;
01123 } //end of the function PointOnWinding
01124 //===========================================================================
01125 //
01126 // Parameter:               -
01127 // Returns:                 -
01128 // Changes Globals:     -
01129 //===========================================================================
01130 int FindPlaneSeperatingWindings(winding_t *w1, winding_t *w2, vec3_t dir,
01131                                             vec3_t normal, float *dist)
01132 {
01133     int i, i2, j, j2, n;
01134     int sides1[3], sides2[3];
01135     float dist1, dist2, dot, diff;
01136     vec3_t normal1, normal2;
01137     vec3_t v1, v2;
01138 
01139     for (i = 0; i < w1->numpoints; i++)
01140     {
01141         i2 = (i+1) % w1->numpoints;
01142         //
01143         VectorSubtract(w1->p[i2], w1->p[i], v1);
01144         if (VectorLength(v1) < 0.1)
01145         {
01146             //Log_Write("FindPlaneSeperatingWindings: winding1 with degenerate edge\r\n");
01147             continue;
01148         } //end if
01149         CrossProduct(v1, dir, normal1);
01150         VectorNormalize(normal1);
01151         dist1 = DotProduct(normal1, w1->p[i]);
01152         //
01153         for (j = 0; j < w2->numpoints; j++)
01154         {
01155             j2 = (j+1) % w2->numpoints;
01156             //
01157             VectorSubtract(w2->p[j2], w2->p[j], v2);
01158             if (VectorLength(v2) < 0.1)
01159             {
01160                 //Log_Write("FindPlaneSeperatingWindings: winding2 with degenerate edge\r\n");
01161                 continue;
01162             } //end if
01163             CrossProduct(v2, dir, normal2);
01164             VectorNormalize(normal2);
01165             dist2 = DotProduct(normal2, w2->p[j]);
01166             //
01167             diff = dist1 - dist2;
01168             if (diff < -0.1 || diff > 0.1)
01169             {
01170                 dist2 = -dist2;
01171                 VectorNegate(normal2, normal2);
01172                 diff = dist1 - dist2;
01173                 if (diff < -0.1 || diff > 0.1) continue;
01174             } //end if
01175             //check if the normal vectors are equal
01176             for (n = 0; n < 3; n++)
01177             {
01178                 diff = normal1[n] - normal2[n];
01179                 if (diff < -0.0001 || diff > 0.0001) break;
01180             } //end for
01181             if (n != 3) continue;
01182             //check on which side of the seperating plane the points of
01183             //the first winding are
01184             sides1[0] = sides1[1] = sides1[2] = 0;
01185             for (n = 0; n < w1->numpoints; n++)
01186             {
01187                 dot = DotProduct(w1->p[n], normal1) - dist1;
01188                 if (dot > 0.1) sides1[0]++;
01189                 else if (dot < -0.1) sides1[1]++;
01190                 else sides1[2]++;
01191             } //end for
01192             //check on which side of the seperating plane the points of
01193             //the second winding are
01194             sides2[0] = sides2[1] = sides2[2] = 0;
01195             for (n = 0; n < w2->numpoints; n++)
01196             {
01197                 //used normal1 and dist1 (they are equal to normal2 and dist2)
01198                 dot = DotProduct(w2->p[n], normal1) - dist1;
01199                 if (dot > 0.1) sides2[0]++;
01200                 else if (dot < -0.1) sides2[1]++;
01201                 else sides2[2]++;
01202             } //end for
01203             //if the first winding has points at both sides
01204             if (sides1[0] && sides1[1])
01205             {
01206                 Log_Write("FindPlaneSeperatingWindings: winding1 non-convex\r\n");
01207                 continue;
01208             } //end if
01209             //if the second winding has points at both sides
01210             if (sides2[0] && sides2[1])
01211             {
01212                 Log_Write("FindPlaneSeperatingWindings: winding2 non-convex\r\n");
01213                 continue;
01214             } //end if
01215             //
01216             if ((!sides1[0] && !sides1[1]) || (!sides2[0] && !sides2[1]))
01217             {
01218                 //don't use one of the winding planes as the seperating plane
01219                 continue;
01220             } //end if
01221             //the windings must be at different sides of the seperating plane
01222             if ((!sides1[0] && !sides2[1]) || (!sides1[1] && !sides2[0]))
01223             {
01224                 VectorCopy(normal1, normal);
01225                 *dist = dist1;
01226                 return true;
01227             } //end if
01228         } //end for
01229     } //end for
01230     return false;
01231 } //end of the function FindPlaneSeperatingWindings
01232 //===========================================================================
01233 //
01234 // Parameter:               -
01235 // Returns:                 -
01236 // Changes Globals:     -
01237 //===========================================================================
01238 #define WCONVEX_EPSILON     0.2
01239 
01240 int WindingsNonConvex(winding_t *w1, winding_t *w2,
01241                              vec3_t normal1, vec3_t normal2,
01242                              float dist1, float dist2)
01243 {
01244     int i;
01245 
01246     if (!w1 || !w2) return false;
01247 
01248     //check if one of the points of face1 is at the back of the plane of face2
01249     for (i = 0; i < w1->numpoints; i++)
01250     {
01251         if (DotProduct(normal2, w1->p[i]) - dist2 > WCONVEX_EPSILON) return true;
01252     } //end for
01253     //check if one of the points of face2 is at the back of the plane of face1
01254     for (i = 0; i < w2->numpoints; i++)
01255     {
01256         if (DotProduct(normal1, w2->p[i]) - dist1 > WCONVEX_EPSILON) return true;
01257     } //end for
01258 
01259     return false;
01260 } //end of the function WindingsNonConvex
01261 //===========================================================================
01262 //
01263 // Parameter:               -
01264 // Returns:                 -
01265 // Changes Globals:     -
01266 //===========================================================================
01267 /*
01268 #define VERTEX_EPSILON      0.5
01269 
01270 qboolean EqualVertexes(vec3_t v1, vec3_t v2)
01271 {
01272     float diff;
01273 
01274     diff = v1[0] - v2[0];
01275     if (diff > -VERTEX_EPSILON && diff < VERTEX_EPSILON)
01276     {
01277         diff = v1[1] - v2[1];
01278         if (diff > -VERTEX_EPSILON && diff < VERTEX_EPSILON)
01279         {
01280             diff = v1[2] - v2[2];
01281             if (diff > -VERTEX_EPSILON && diff < VERTEX_EPSILON)
01282             {
01283                 return true;
01284             } //end if
01285         } //end if
01286     } //end if
01287     return false;
01288 } //end of the function EqualVertexes
01289 
01290 #define CONTINUOUS_EPSILON  0.001
01291 
01292 winding_t *AAS_MergeWindings(winding_t *w1, winding_t *w2, vec3_t windingnormal)
01293 {
01294     int n, i, k;
01295     vec3_t normal, delta;
01296     winding_t *winding, *neww;
01297     float dist, dot;
01298     int p1, p2;
01299     int points[2][64];
01300     int numpoints[2] = {0, 0};
01301     int newnumpoints;
01302     int keep[2];
01303 
01304     if (!FindPlaneSeperatingWindings(w1, w2, windingnormal, normal, &dist)) return NULL;
01305 
01306     //for both windings
01307     for (n = 0; n < 2; n++)
01308     {
01309         if (n == 0) winding = w1;
01310         else winding = w2;
01311         //get the points of the winding which are on the seperating plane
01312         for (i = 0; i < winding->numpoints; i++)
01313         {
01314             dot = DotProduct(winding->p[i], normal) - dist;
01315             if (dot > -ON_EPSILON && dot < ON_EPSILON)
01316             {
01317                 //don't allow more than 64 points on the seperating plane
01318                 if (numpoints[n] >= 64) Error("AAS_MergeWindings: more than 64 points on seperating plane\n");
01319                 points[n][numpoints[n]++] = i;
01320             } //end if
01321         } //end for
01322         //there must be at least two points of each winding on the seperating plane
01323         if (numpoints[n] < 2) return NULL;
01324     } //end for
01325 
01326     //if the first point of winding1 (which is on the seperating plane) is unequal
01327     //to the last point of winding2 (which is on the seperating plane)
01328     if (!EqualVertexes(w1->p[points[0][0]], w2->p[points[1][numpoints[1]-1]]))
01329     {
01330         return NULL;
01331     } //end if
01332     //if the last point of winding1 (which is on the seperating plane) is unequal
01333     //to the first point of winding2 (which is on the seperating plane)
01334     if (!EqualVertexes(w1->p[points[0][numpoints[0]-1]], w2->p[points[1][0]]))
01335     {
01336         return NULL;
01337     } //end if
01338     //
01339     // check slope of connected lines
01340     // if the slopes are colinear, the point can be removed
01341     //
01342