]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake2/extra/qe4/brush.c
Merge remote-tracking branch 'ttimo/master'
[xonotic/netradiant.git] / tools / quake2 / extra / qe4 / brush.c
diff --git a/tools/quake2/extra/qe4/brush.c b/tools/quake2/extra/qe4/brush.c
new file mode 100644 (file)
index 0000000..e8a2b53
--- /dev/null
@@ -0,0 +1,1568 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include <assert.h>
+#include "qe3.h"
+
+#define MAX_POINTS_ON_WINDING  64
+
+face_t *Face_Alloc( void );
+void    Face_Free( face_t *f );
+
+winding_t      *NewWinding (int points);
+void           FreeWinding (winding_t *w);
+winding_t      *Winding_Clone( winding_t *w );
+winding_t      *ClipWinding (winding_t *in, plane_t *split, qboolean keepon);
+
+void PrintWinding (winding_t *w)
+{
+       int             i;
+
+       printf ("-------------\n");
+       for (i=0 ; i<w->numpoints ; i++)
+               printf ("(%5.2f, %5.2f, %5.2f)\n", w->points[i][0]
+               , w->points[i][1], w->points[i][2]);
+}
+
+void PrintPlane (plane_t *p)
+{
+    printf ("(%5.2f, %5.2f, %5.2f) : %5.2f\n",  p->normal[0],  p->normal[1],
+    p->normal[2],  p->dist);
+}
+
+void PrintVector (vec3_t v)
+{
+     printf ("(%5.2f, %5.2f, %5.2f)\n",  v[0],  v[1], v[2]);
+}
+
+
+face_t *Face_Clone (face_t *f)
+{
+       face_t  *n;
+
+       n = Face_Alloc();
+       n->texdef = f->texdef;
+       memcpy (n->planepts, f->planepts, sizeof(n->planepts));
+
+       // all other fields are derived, and will be set by Brush_Build
+       return n;
+}
+
+//============================================================================
+
+#define        BOGUS_RANGE     18000
+
+
+/*
+==================
+NewWinding
+==================
+*/
+winding_t *NewWinding (int points)
+{
+       winding_t       *w;
+       int                     size;
+
+       if (points > MAX_POINTS_ON_WINDING)
+               Error ("NewWinding: %i points", points);
+
+       size = (int)((winding_t *)0)->points[points];
+       w = malloc (size);
+       memset (w, 0, size);
+       w->maxpoints = points;
+
+       return w;
+}
+
+
+void FreeWinding (winding_t *w)
+{
+       free (w);
+}
+
+
+/*
+==================
+Winding_Clone
+==================
+*/
+winding_t *Winding_Clone(winding_t *w)
+{
+       int                     size;
+       winding_t       *c;
+
+       size = (int)((winding_t *)0)->points[w->numpoints];
+       c = qmalloc (size);
+       memcpy (c, w, size);
+       return c;
+}
+
+
+/*
+==================
+ClipWinding
+
+Clips the winding to the plane, returning the new winding on the positive side
+Frees the input winding.
+If keepon is true, an exactly on-plane winding will be saved, otherwise
+it will be clipped away.
+==================
+*/
+winding_t *ClipWinding (winding_t *in, plane_t *split, qboolean keepon)
+{
+       vec_t   dists[MAX_POINTS_ON_WINDING];
+       int             sides[MAX_POINTS_ON_WINDING];
+       int             counts[3];
+       vec_t   dot;
+       int             i, j;
+       vec_t   *p1, *p2;
+       vec3_t  mid;
+       winding_t       *neww;
+       int             maxpts;
+
+       counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+       for (i=0 ; i<in->numpoints ; i++)
+       {
+               dot = DotProduct (in->points[i], split->normal);
+               dot -= split->dist;
+               dists[i] = dot;
+               if (dot > ON_EPSILON)
+                       sides[i] = SIDE_FRONT;
+               else if (dot < -ON_EPSILON)
+                       sides[i] = SIDE_BACK;
+               else
+               {
+                       sides[i] = SIDE_ON;
+               }
+               counts[sides[i]]++;
+       }
+       sides[i] = sides[0];
+       dists[i] = dists[0];
+
+       if (keepon && !counts[0] && !counts[1])
+               return in;
+
+       if (!counts[0])
+       {
+               FreeWinding (in);
+               return NULL;
+       }
+       if (!counts[1])
+               return in;
+
+       maxpts = in->numpoints+4;       // can't use counts[0]+2 because
+                                                               // of fp grouping errors
+       neww = NewWinding (maxpts);
+
+       for (i=0 ; i<in->numpoints ; i++)
+       {
+               p1 = in->points[i];
+
+               if (sides[i] == SIDE_ON)
+               {
+                       VectorCopy (p1, neww->points[neww->numpoints]);
+                       neww->numpoints++;
+                       continue;
+               }
+
+               if (sides[i] == SIDE_FRONT)
+               {
+                       VectorCopy (p1, neww->points[neww->numpoints]);
+                       neww->numpoints++;
+               }
+
+               if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+                       continue;
+
+       // generate a split point
+               p2 = in->points[(i+1)%in->numpoints];
+
+               dot = dists[i] / (dists[i]-dists[i+1]);
+               for (j=0 ; j<3 ; j++)
+               {       // avoid round off error when possible
+                       if (split->normal[j] == 1)
+                               mid[j] = split->dist;
+                       else if (split->normal[j] == -1)
+                               mid[j] = -split->dist;
+                       else
+                               mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+               }
+
+               VectorCopy (mid, neww->points[neww->numpoints]);
+               neww->numpoints++;
+       }
+
+       if (neww->numpoints > maxpts)
+               Error ("ClipWinding: points exceeded estimate");
+
+// free the original winding
+       FreeWinding (in);
+
+       return neww;
+}
+
+
+
+/*
+=============================================================================
+
+                       TEXTURE COORDINATES
+
+=============================================================================
+*/
+
+
+/*
+==================
+textureAxisFromPlane
+==================
+*/
+vec3_t baseaxis[18] =
+{
+{0,0,1}, {1,0,0}, {0,-1,0},                    // floor
+{0,0,-1}, {1,0,0}, {0,-1,0},           // ceiling
+{1,0,0}, {0,1,0}, {0,0,-1},                    // west wall
+{-1,0,0}, {0,1,0}, {0,0,-1},           // east wall
+{0,1,0}, {1,0,0}, {0,0,-1},                    // south wall
+{0,-1,0}, {1,0,0}, {0,0,-1}                    // north wall
+};
+
+void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv)
+{
+       int             bestaxis;
+       float   dot,best;
+       int             i;
+
+       best = 0;
+       bestaxis = 0;
+
+       for (i=0 ; i<6 ; i++)
+       {
+               dot = DotProduct (pln->normal, baseaxis[i*3]);
+               if (dot > best)
+               {
+                       best = dot;
+                       bestaxis = i;
+               }
+       }
+
+       VectorCopy (baseaxis[bestaxis*3+1], xv);
+       VectorCopy (baseaxis[bestaxis*3+2], yv);
+}
+
+
+float  lightaxis[3] = {0.6, 0.8, 1.0};
+/*
+================
+SetShadeForPlane
+
+Light different planes differently to
+improve recognition
+================
+*/
+float SetShadeForPlane (plane_t *p)
+{
+       int             i;
+       float   f;
+
+       // axial plane
+       for (i=0 ; i<3 ; i++)
+               if (fabs(p->normal[i]) > 0.9)
+               {
+                       f = lightaxis[i];
+                       return f;
+               }
+
+       // between two axial planes
+       for (i=0 ; i<3 ; i++)
+               if (fabs(p->normal[i]) < 0.1)
+               {
+                       f = (lightaxis[(i+1)%3] + lightaxis[(i+2)%3])/2;
+                       return f;
+               }
+
+       // other
+       f= (lightaxis[0] + lightaxis[1] + lightaxis[2]) / 3;
+       return f;
+}
+
+vec3_t  vecs[2];
+float  shift[2];
+
+/*
+================
+BeginTexturingFace
+================
+*/
+void BeginTexturingFace (brush_t *b, face_t *f, qtexture_t *q)
+{
+       vec3_t  pvecs[2];
+       int             sv, tv;
+       float   ang, sinv, cosv;
+       float   ns, nt;
+       int             i,j;
+       float   shade;
+
+       // get natural texture axis
+       TextureAxisFromPlane(&f->plane, pvecs[0], pvecs[1]);
+
+       // set shading for face
+       shade = SetShadeForPlane (&f->plane);
+       if (camera.draw_mode == cd_texture && !b->owner->eclass->fixedsize)
+       {
+               f->d_color[0] =
+               f->d_color[1] =
+               f->d_color[2] = shade;
+       }
+       else
+       {
+               f->d_color[0] = shade*q->color[0];
+               f->d_color[1] = shade*q->color[1];
+               f->d_color[2] = shade*q->color[2];
+       }
+
+       if (camera.draw_mode != cd_texture)
+               return;
+
+       if (!f->texdef.scale[0])
+               f->texdef.scale[0] = 1;
+       if (!f->texdef.scale[1])
+               f->texdef.scale[1] = 1;
+
+
+// rotate axis
+       if (f->texdef.rotate == 0)
+               { sinv = 0 ; cosv = 1; }
+       else if (f->texdef.rotate == 90)
+               { sinv = 1 ; cosv = 0; }
+       else if (f->texdef.rotate == 180)
+               { sinv = 0 ; cosv = -1; }
+       else if (f->texdef.rotate == 270)
+               { sinv = -1 ; cosv = 0; }
+       else
+       {
+               ang = f->texdef.rotate / 180 * Q_PI;
+               sinv = sin(ang);
+               cosv = cos(ang);
+       }
+
+       if (pvecs[0][0])
+               sv = 0;
+       else if (pvecs[0][1])
+               sv = 1;
+       else
+               sv = 2;
+
+       if (pvecs[1][0])
+               tv = 0;
+       else if (pvecs[1][1])
+               tv = 1;
+       else
+               tv = 2;
+
+       for (i=0 ; i<2 ; i++)
+       {
+               ns = cosv * pvecs[i][sv] - sinv * pvecs[i][tv];
+               nt = sinv * pvecs[i][sv] +  cosv * pvecs[i][tv];
+               vecs[i][sv] = ns;
+               vecs[i][tv] = nt;
+       }
+
+       for (i=0 ; i<2 ; i++)
+               for (j=0 ; j<3 ; j++)
+                       vecs[i][j] = vecs[i][j] / f->texdef.scale[i];
+}
+
+
+void _EmitTextureCoordinates (vec3_t v, qtexture_t *q)
+{
+       float   s, t;
+
+       s = DotProduct (v, vecs[0]);
+       t = DotProduct (v, vecs[1]);
+
+       s += shift[0];
+       t += shift[1];
+
+       s /= q->width;
+       t /= q->height;
+
+       glTexCoord2f (s, t);
+}
+
+void EmitTextureCoordinates ( float *xyzst, qtexture_t *q, face_t *f)
+{
+       float   s, t, ns, nt;
+       float   ang, sinv, cosv;
+       vec3_t  vecs[2];
+       texdef_t        *td;
+
+       // get natural texture axis
+       TextureAxisFromPlane(&f->plane, vecs[0], vecs[1]);
+
+       td = &f->texdef;
+
+       ang = td->rotate / 180 * Q_PI;
+       sinv = sin(ang);
+       cosv = cos(ang);
+
+       if (!td->scale[0])
+               td->scale[0] = 1;
+       if (!td->scale[1])
+               td->scale[1] = 1;
+
+       s = DotProduct(xyzst, vecs[0]);
+       t = DotProduct(xyzst, vecs[1]);
+
+       ns = cosv * s - sinv * t;
+       nt = sinv * s +  cosv * t;
+
+       s = ns/td->scale[0] + td->shift[0];
+       t = nt/td->scale[1] + td->shift[1];
+
+       // gl scales everything from 0 to 1
+       s /= q->width;
+       t /= q->height;
+
+       xyzst[3] = s;
+       xyzst[4] = t;
+}
+
+//==========================================================================
+
+
+/*
+=================
+BasePolyForPlane
+=================
+*/
+winding_t *BasePolyForPlane (plane_t *p)
+{
+       int             i, x;
+       vec_t   max, v;
+       vec3_t  org, vright, vup;
+       winding_t       *w;
+
+// find the major axis
+
+       max = -BOGUS_RANGE;
+       x = -1;
+       for (i=0 ; i<3; i++)
+       {
+               v = fabs(p->normal[i]);
+               if (v > max)
+               {
+                       x = i;
+                       max = v;
+               }
+       }
+       if (x==-1)
+               Error ("BasePolyForPlane: no axis found");
+
+       VectorCopy (vec3_origin, vup);
+       switch (x)
+       {
+       case 0:
+       case 1:
+               vup[2] = 1;
+               break;
+       case 2:
+               vup[0] = 1;
+               break;
+       }
+
+
+       v = DotProduct (vup, p->normal);
+       VectorMA (vup, -v, p->normal, vup);
+       VectorNormalize (vup);
+
+       VectorScale (p->normal, p->dist, org);
+
+       CrossProduct (vup, p->normal, vright);
+
+       VectorScale (vup, 8192, vup);
+       VectorScale (vright, 8192, vright);
+
+// project a really big        axis aligned box onto the plane
+       w = NewWinding (4);
+
+       VectorSubtract (org, vright, w->points[0]);
+       VectorAdd (w->points[0], vup, w->points[0]);
+
+       VectorAdd (org, vright, w->points[1]);
+       VectorAdd (w->points[1], vup, w->points[1]);
+
+       VectorAdd (org, vright, w->points[2]);
+       VectorSubtract (w->points[2], vup, w->points[2]);
+
+       VectorSubtract (org, vright, w->points[3]);
+       VectorSubtract (w->points[3], vup, w->points[3]);
+
+       w->numpoints = 4;
+
+       return w;
+}
+
+void Brush_MakeFacePlanes (brush_t *b)
+{
+       face_t  *f;
+       int             j;
+       vec3_t  t1, t2, t3;
+
+       for (f=b->brush_faces ; f ; f=f->next)
+       {
+       // convert to a vector / dist plane
+               for (j=0 ; j<3 ; j++)
+               {
+                       t1[j] = f->planepts[0][j] - f->planepts[1][j];
+                       t2[j] = f->planepts[2][j] - f->planepts[1][j];
+                       t3[j] = f->planepts[1][j];
+               }
+
+               CrossProduct(t1,t2, f->plane.normal);
+               if (VectorCompare (f->plane.normal, vec3_origin))
+                       printf ("WARNING: brush plane with no normal\n");
+               VectorNormalize (f->plane.normal);
+               f->plane.dist = DotProduct (t3, f->plane.normal);
+       }
+}
+
+void DrawBrushEntityName (brush_t *b)
+{
+       char    *name;
+       float   a, s, c;
+       vec3_t  mid;
+       int             i;
+
+       if (!b->owner)
+               return;         // during contruction
+
+       if (b->owner == world_entity)
+               return;
+
+       if (b != b->owner->brushes.onext)
+               return; // not key brush
+
+       // draw the angle pointer
+       a = FloatForKey (b->owner, "angle");
+       if (a)
+       {
+               s = sin (a/180*Q_PI);
+               c = cos (a/180*Q_PI);
+               for (i=0 ; i<3 ; i++)
+                       mid[i] = (b->mins[i] + b->maxs[i])*0.5;
+
+               glBegin (GL_LINE_STRIP);
+               glVertex3fv (mid);
+               mid[0] += c*8;
+               mid[1] += s*8;
+               glVertex3fv (mid);
+               mid[0] -= c*4;
+               mid[1] -= s*4;
+               mid[0] -= s*4;
+               mid[1] += c*4;
+               glVertex3fv (mid);
+               mid[0] += c*4;
+               mid[1] += s*4;
+               mid[0] += s*4;
+               mid[1] -= c*4;
+               glVertex3fv (mid);
+               mid[0] -= c*4;
+               mid[1] -= s*4;
+               mid[0] += s*4;
+               mid[1] -= c*4;
+               glVertex3fv (mid);
+               glEnd ();
+       }
+
+       if (!g_qeglobals.d_savedinfo.show_names)
+               return;
+
+       name = ValueForKey (b->owner, "classname");
+       glRasterPos2f (b->mins[0]+4, b->mins[1]+4);
+       glCallLists (strlen(name), GL_UNSIGNED_BYTE, name);
+}
+
+/*
+=================
+MakeFaceWinding
+
+returns the visible polygon on a face
+=================
+*/
+winding_t      *MakeFaceWinding (brush_t *b, face_t *face)
+{
+       winding_t       *w;
+       face_t          *clip;
+       plane_t                 plane;
+       qboolean                past;
+
+       // get a poly that covers an effectively infinite area
+       w = BasePolyForPlane (&face->plane);
+
+       // chop the poly by all of the other faces
+       past = false;
+       for (clip = b->brush_faces ; clip && w ; clip=clip->next)
+       {
+               if (clip == face)
+               {
+                       past = true;
+                       continue;
+               }
+               if (DotProduct (face->plane.normal, clip->plane.normal) > 0.999
+                       && fabs(face->plane.dist - clip->plane.dist) < 0.01 )
+               {       // identical plane, use the later one
+                       if (past)
+                       {
+                               free (w);
+                               return NULL;
+                       }
+                       continue;
+               }
+
+               // flip the plane, because we want to keep the back side
+               VectorSubtract (vec3_origin,clip->plane.normal, plane.normal);
+               plane.dist = -clip->plane.dist;
+
+               w = ClipWinding (w, &plane, false);
+               if (!w)
+                       return w;
+       }
+
+       if (w->numpoints < 3)
+       {
+               free(w);
+               w = NULL;
+       }
+
+       if (!w)
+               printf ("unused plane\n");
+
+       return w;
+}
+
+
+void Brush_SnapPlanepts (brush_t *b)
+{
+       int             i, j;
+       face_t  *f;
+
+       for (f=b->brush_faces ; f; f=f->next)
+               for (i=0 ; i<3 ; i++)
+                       for (j=0 ; j<3 ; j++)
+                               f->planepts[i][j] = floor (f->planepts[i][j] + 0.5);
+}
+
+/*
+** Brush_Build
+**
+** Builds a brush rendering data and also sets the min/max bounds
+*/
+#define        ZERO_EPSILON    0.001
+void Brush_Build( brush_t *b )
+{
+//     int                             order;
+//     face_t                  *face;
+//     winding_t               *w;
+       char                    title[1024];
+
+       if (modified != 1)
+       {
+               modified = true;        // mark the map as changed
+               sprintf (title, "%s *", currentmap);
+
+               QE_ConvertDOSToUnixName( title, title );
+               Sys_SetTitle (title);
+       }
+
+       /*
+       ** build the windings and generate the bounding box
+       */
+       Brush_BuildWindings( b );
+
+       /*
+       ** move the points and edges if in select mode
+       */
+       if (g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_edge)
+               SetupVertexSelection ();
+}
+
+/*
+=================
+Brush_Parse
+
+The brush is NOT linked to any list
+=================
+*/
+brush_t *Brush_Parse (void)
+{
+       brush_t         *b;
+       face_t          *f;
+       int                     i,j;
+
+       g_qeglobals.d_parsed_brushes++;
+       b = qmalloc(sizeof(brush_t));
+
+       do
+       {
+               if (!GetToken (true))
+                       break;
+               if (!strcmp (token, "}") )
+                       break;
+
+               f = Face_Alloc();
+
+               // add the brush to the end of the chain, so
+               // loading and saving a map doesn't reverse the order
+
+               f->next = NULL;
+               if (!b->brush_faces)
+               {
+                       b->brush_faces = f;
+               }
+               else
+               {
+                       face_t *scan;
+
+                       for (scan=b->brush_faces ; scan->next ; scan=scan->next)
+                               ;
+                       scan->next = f;
+               }
+
+               // read the three point plane definition
+               for (i=0 ; i<3 ; i++)
+               {
+                       if (i != 0)
+                               GetToken (true);
+                       if (strcmp (token, "(") )
+                               Error ("parsing brush");
+
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               GetToken (false);
+                               f->planepts[i][j] = atoi(token);
+                       }
+
+                       GetToken (false);
+                       if (strcmp (token, ")") )
+                               Error ("parsing brush");
+
+               }
+
+       // read the texturedef
+               GetToken (false);
+               strcpy(f->texdef.name, token);
+               GetToken (false);
+               f->texdef.shift[0] = atoi(token);
+               GetToken (false);
+               f->texdef.shift[1] = atoi(token);
+               GetToken (false);
+               f->texdef.rotate = atoi(token);
+               GetToken (false);
+               f->texdef.scale[0] = atof(token);
+               GetToken (false);
+               f->texdef.scale[1] = atof(token);
+
+               // the flags and value field aren't necessarily present
+               f->d_texture = Texture_ForName( f->texdef.name );
+               f->texdef.flags = f->d_texture->flags;
+               f->texdef.value = f->d_texture->value;
+               f->texdef.contents = f->d_texture->contents;
+
+               if (TokenAvailable ())
+               {
+                       GetToken (false);
+                       f->texdef.contents = atoi(token);
+                       GetToken (false);
+                       f->texdef.flags = atoi(token);
+                       GetToken (false);
+                       f->texdef.value = atoi(token);
+               }
+       } while (1);
+
+       return b;
+}
+
+/*
+=================
+Brush_Write
+=================
+*/
+void Brush_Write (brush_t *b, FILE *f)
+{
+       face_t  *fa;
+       char *pname;
+       int             i;
+
+       fprintf (f, "{\n");
+       for (fa=b->brush_faces ; fa ; fa=fa->next)
+       {
+               for (i=0 ; i<3 ; i++)
+                       fprintf (f, "( %i %i %i ) ", (int)fa->planepts[i][0]
+                       , (int)fa->planepts[i][1], (int)fa->planepts[i][2]);
+
+               pname = fa->texdef.name;
+               if (pname[0] == 0)
+                       pname = "unnamed";
+
+               fprintf (f, "%s %i %i %i ", pname,
+                       (int)fa->texdef.shift[0], (int)fa->texdef.shift[1],
+                       (int)fa->texdef.rotate);
+
+               if (fa->texdef.scale[0] == (int)fa->texdef.scale[0])
+                       fprintf (f, "%i ", (int)fa->texdef.scale[0]);
+               else
+                       fprintf (f, "%f ", (float)fa->texdef.scale[0]);
+               if (fa->texdef.scale[1] == (int)fa->texdef.scale[1])
+                       fprintf (f, "%i", (int)fa->texdef.scale[1]);
+               else
+                       fprintf (f, "%f", (float)fa->texdef.scale[1]);
+
+               // only output flags and value if not default
+               if (fa->texdef.value != fa->d_texture->value
+                       || fa->texdef.flags != fa->d_texture->flags
+                       || fa->texdef.contents != fa->d_texture->contents)
+               {
+                       fprintf (f, " %i %i %i", fa->texdef.contents, fa->texdef.flags, fa->texdef.value);
+               }
+
+               fprintf (f, "\n");
+       }
+       fprintf (f, "}\n");
+}
+
+
+/*
+=============
+Brush_Create
+
+Create non-textured blocks for entities
+The brush is NOT linked to any list
+=============
+*/
+brush_t        *Brush_Create (vec3_t mins, vec3_t maxs, texdef_t *texdef)
+{
+       int             i, j;
+       vec3_t  pts[4][2];
+       face_t  *f;
+       brush_t *b;
+
+       for (i=0 ; i<3 ; i++)
+               if (maxs[i] < mins[i])
+                       Error ("Brush_InitSolid: backwards");
+
+       b = qmalloc (sizeof(brush_t));
+
+       pts[0][0][0] = mins[0];
+       pts[0][0][1] = mins[1];
+
+       pts[1][0][0] = mins[0];
+       pts[1][0][1] = maxs[1];
+
+       pts[2][0][0] = maxs[0];
+       pts[2][0][1] = maxs[1];
+
+       pts[3][0][0] = maxs[0];
+       pts[3][0][1] = mins[1];
+
+       for (i=0 ; i<4 ; i++)
+       {
+               pts[i][0][2] = mins[2];
+               pts[i][1][0] = pts[i][0][0];
+               pts[i][1][1] = pts[i][0][1];
+               pts[i][1][2] = maxs[2];
+       }
+
+       for (i=0 ; i<4 ; i++)
+       {
+               f = Face_Alloc();
+               f->texdef = *texdef;
+               f->next = b->brush_faces;
+               b->brush_faces = f;
+               j = (i+1)%4;
+
+               VectorCopy (pts[j][1], f->planepts[0]);
+               VectorCopy (pts[i][1], f->planepts[1]);
+               VectorCopy (pts[i][0], f->planepts[2]);
+       }
+
+       f = Face_Alloc();
+       f->texdef = *texdef;
+       f->next = b->brush_faces;
+       b->brush_faces = f;
+
+       VectorCopy (pts[0][1], f->planepts[0]);
+       VectorCopy (pts[1][1], f->planepts[1]);
+       VectorCopy (pts[2][1], f->planepts[2]);
+
+       f = Face_Alloc();
+       f->texdef = *texdef;
+       f->next = b->brush_faces;
+       b->brush_faces = f;
+
+       VectorCopy (pts[2][0], f->planepts[0]);
+       VectorCopy (pts[1][0], f->planepts[1]);
+       VectorCopy (pts[0][0], f->planepts[2]);
+
+       return b;
+}
+
+
+/*
+=============
+Brush_MakeSided
+
+Makes the current brushhave the given number of 2d sides
+=============
+*/
+void Brush_MakeSided (int sides)
+{
+       int             i;
+       vec3_t  mins, maxs;
+       brush_t *b;
+       texdef_t        *texdef;
+       face_t  *f;
+       vec3_t  mid;
+       float   width;
+       float   sv, cv;
+
+       if (sides < 3)
+       {
+               Sys_Status ("Bad sides number", 0);
+               return;
+       }
+
+       if (!QE_SingleBrush ())
+       {
+               Sys_Status ("Must have a single brush selected", 0 );
+               return;
+       }
+
+       b = selected_brushes.next;
+       VectorCopy (b->mins, mins);
+       VectorCopy (b->maxs, maxs);
+       texdef = &g_qeglobals.d_texturewin.texdef;
+
+       Brush_Free (b);
+
+       // find center of brush
+       width = 8;
+       for (i=0 ; i<2 ; i++)
+       {
+               mid[i] = (maxs[i] + mins[i])*0.5;
+               if (maxs[i] - mins[i] > width)
+                       width = maxs[i] - mins[i];
+       }
+       width /= 2;
+
+       b = qmalloc (sizeof(brush_t));
+
+       // create top face
+       f = Face_Alloc();
+       f->texdef = *texdef;
+       f->next = b->brush_faces;
+       b->brush_faces = f;
+
+f->planepts[2][0] = mins[0];f->planepts[2][1] = mins[1];f->planepts[2][2] = maxs[2];
+f->planepts[1][0] = maxs[0];f->planepts[1][1] = mins[1];f->planepts[1][2] = maxs[2];
+f->planepts[0][0] = maxs[0];f->planepts[0][1] = maxs[1];f->planepts[0][2] = maxs[2];
+
+       // create bottom face
+       f = Face_Alloc();
+       f->texdef = *texdef;
+       f->next = b->brush_faces;
+       b->brush_faces = f;
+
+f->planepts[0][0] = mins[0];f->planepts[0][1] = mins[1];f->planepts[0][2] = mins[2];
+f->planepts[1][0] = maxs[0];f->planepts[1][1] = mins[1];f->planepts[1][2] = mins[2];
+f->planepts[2][0] = maxs[0];f->planepts[2][1] = maxs[1];f->planepts[2][2] = mins[2];
+
+       for (i=0 ; i<sides ; i++)
+       {
+               f = Face_Alloc();
+               f->texdef = *texdef;
+               f->next = b->brush_faces;
+               b->brush_faces = f;
+
+               sv = sin (i*3.14159265*2/sides);
+               cv = cos (i*3.14159265*2/sides);
+
+               f->planepts[0][0] = floor(mid[0]+width*cv+0.5);
+               f->planepts[0][1] = floor(mid[1]+width*sv+0.5);
+               f->planepts[0][2] = mins[2];
+
+               f->planepts[1][0] = f->planepts[0][0];
+               f->planepts[1][1] = f->planepts[0][1];
+               f->planepts[1][2] = maxs[2];
+
+               f->planepts[2][0] = floor(f->planepts[0][0] - width*sv + 0.5);
+               f->planepts[2][1] = floor(f->planepts[0][1] + width*cv + 0.5);
+               f->planepts[2][2] = maxs[2];
+
+       }
+
+       Brush_AddToList (b, &selected_brushes);
+
+       Entity_LinkBrush (world_entity, b);
+
+       Brush_Build( b );
+
+       Sys_UpdateWindows (W_ALL);
+}
+
+
+/*
+=============
+Brush_Free
+
+Frees the brush with all of its faces and display list.
+Unlinks the brush from whichever chain it is in.
+Decrements the owner entity's brushcount.
+Removes owner entity if this was the last brush
+unless owner is the world.
+=============
+*/
+void Brush_Free (brush_t *b)
+{
+       face_t  *f, *next;
+
+       // free faces
+       for (f=b->brush_faces ; f ; f=next)
+       {
+               next = f->next;
+               Face_Free( f );
+       }
+
+       /*
+       for ( i = 0; i < b->d_numwindings; i++ )
+       {
+               if ( b->d_windings[i] )
+               {
+                       FreeWinding( b->d_windings[i] );
+                       b->d_windings[i] = 0;
+               }
+       }
+       */
+
+       // unlink from active/selected list
+       if (b->next)
+               Brush_RemoveFromList (b);
+
+       // unlink from entity list
+       if (b->onext)
+               Entity_UnlinkBrush (b);
+
+       free (b);
+}
+
+/*
+============
+Brush_Move
+============
+*/
+void Brush_Move (brush_t *b, vec3_t move)
+{
+       int             i;
+       face_t  *f;
+
+       for (f=b->brush_faces ; f ; f=f->next)
+               for (i=0 ; i<3 ; i++)
+                       VectorAdd (f->planepts[i], move, f->planepts[i]);
+       Brush_Build( b );
+}
+
+/*
+============
+Brush_Clone
+
+Does NOT add the new brush to any lists
+============
+*/
+brush_t *Brush_Clone (brush_t *b)
+{
+       brush_t *n;
+       face_t  *f, *nf;
+
+       n = qmalloc(sizeof(brush_t));
+       n->owner = b->owner;
+       for (f=b->brush_faces ; f ; f=f->next)
+       {
+               nf = Face_Clone( f );
+               nf->next = n->brush_faces;
+               n->brush_faces = nf;
+       }
+       return n;
+}
+
+/*
+==============
+Brush_Ray
+
+Itersects a ray with a brush
+Returns the face hit and the distance along the ray the intersection occured at
+Returns NULL and 0 if not hit at all
+==============
+*/
+face_t *Brush_Ray (vec3_t origin, vec3_t dir, brush_t *b, float *dist)
+{
+       face_t  *f, *firstface;
+       vec3_t  p1, p2;
+       float   frac, d1, d2;
+       int             i;
+
+       VectorCopy (origin, p1);
+       for (i=0 ; i<3 ; i++)
+               p2[i] = p1[i] + dir[i]*16384;
+
+       for (f=b->brush_faces ; f ; f=f->next)
+       {
+               d1 = DotProduct (p1, f->plane.normal) - f->plane.dist;
+               d2 = DotProduct (p2, f->plane.normal) - f->plane.dist;
+               if (d1 >= 0 && d2 >= 0)
+               {
+                       *dist = 0;
+                       return NULL;    // ray is on front side of face
+               }
+               if (d1 <=0 && d2 <= 0)
+                       continue;
+       // clip the ray to the plane
+               frac = d1 / (d1 - d2);
+               if (d1 > 0)
+               {
+                       firstface = f;
+                       for (i=0 ; i<3 ; i++)
+                               p1[i] = p1[i] + frac *(p2[i] - p1[i]);
+               }
+               else
+               {
+                       for (i=0 ; i<3 ; i++)
+                               p2[i] = p1[i] + frac *(p2[i] - p1[i]);
+               }
+       }
+
+       // find distance p1 is along dir
+       VectorSubtract (p1, origin, p1);
+       d1 = DotProduct (p1, dir);
+
+       *dist = d1;
+
+       return firstface;
+}
+
+void   Brush_AddToList (brush_t *b, brush_t *list)
+{
+       if (b->next || b->prev)
+               Error ("Brush_RemoveFromList: allready linked");
+       b->next = list->next;
+       list->next->prev = b;
+       list->next = b;
+       b->prev = list;
+}
+
+void   Brush_RemoveFromList (brush_t *b)
+{
+       if (!b->next || !b->prev)
+               Error ("Brush_RemoveFromList: not linked");
+       b->next->prev = b->prev;
+       b->prev->next = b->next;
+       b->next = b->prev = NULL;
+}
+
+void   Brush_SetTexture (brush_t *b, texdef_t *texdef)
+{
+       face_t  *f;
+
+       for (f=b->brush_faces ; f ; f=f->next)
+               f->texdef = *texdef;
+       Brush_Build( b );
+}
+
+
+qboolean ClipLineToFace (vec3_t p1, vec3_t p2, face_t *f)
+{
+       float   d1, d2, fr;
+       int             i;
+       float   *v;
+
+       d1 = DotProduct (p1, f->plane.normal) - f->plane.dist;
+       d2 = DotProduct (p2, f->plane.normal) - f->plane.dist;
+
+       if (d1 >= 0 && d2 >= 0)
+               return false;           // totally outside
+       if (d1 <= 0 && d2 <= 0)
+               return true;            // totally inside
+
+       fr = d1 / (d1 - d2);
+
+       if (d1 > 0)
+               v = p1;
+       else
+               v = p2;
+
+       for (i=0 ; i<3 ; i++)
+               v[i] = p1[i] + fr*(p2[i] - p1[i]);
+
+       return true;
+}
+
+
+int AddPlanept (float *f)
+{
+       int             i;
+
+       for (i=0 ; i<g_qeglobals.d_num_move_points ; i++)
+               if (g_qeglobals.d_move_points[i] == f)
+                       return 0;
+       g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = f;
+       return 1;
+}
+
+/*
+==============
+Brush_SelectFaceForDragging
+
+Adds the faces planepts to move_points, and
+rotates and adds the planepts of adjacent face if shear is set
+==============
+*/
+void Brush_SelectFaceForDragging (brush_t *b, face_t *f, qboolean shear)
+{
+       int             i;
+       face_t  *f2;
+       winding_t       *w;
+       float   d;
+       brush_t *b2;
+       int             c;
+
+       if (b->owner->eclass->fixedsize)
+               return;
+
+       c = 0;
+       for (i=0 ; i<3 ; i++)
+               c += AddPlanept (f->planepts[i]);
+       if (c == 0)
+               return;         // allready completely added
+
+       // select all points on this plane in all brushes the selection
+       for (b2=selected_brushes.next ; b2 != &selected_brushes ; b2 = b2->next)
+       {
+               if (b2 == b)
+                       continue;
+               for (f2=b2->brush_faces ; f2 ; f2=f2->next)
+               {
+                       for (i=0 ; i<3 ; i++)
+                               if (fabs(DotProduct(f2->planepts[i], f->plane.normal)
+                               -f->plane.dist) > ON_EPSILON)
+                                       break;
+                       if (i==3)
+                       {       // move this face as well
+                               Brush_SelectFaceForDragging (b2, f2, shear);
+                               break;
+                       }
+               }
+       }
+
+
+       // if shearing, take all the planes adjacent to
+       // selected faces and rotate their points so the
+       // edge clipped by a selcted face has two of the points
+       if (!shear)
+               return;
+
+       for (f2=b->brush_faces ; f2 ; f2=f2->next)
+       {
+               if (f2 == f)
+                       continue;
+               w = MakeFaceWinding (b, f2);
+               if (!w)
+                       continue;
+
+               // any points on f will become new control points
+               for (i=0 ; i<w->numpoints ; i++)
+               {
+                       d = DotProduct (w->points[i], f->plane.normal)
+                               - f->plane.dist;
+                       if (d > -ON_EPSILON && d < ON_EPSILON)
+                               break;
+               }
+
+               //
+               // if none of the points were on the plane,
+               // leave it alone
+               //
+               if (i != w->numpoints)
+               {
+                       if (i == 0)
+                       {       // see if the first clockwise point was the
+                               // last point on the winding
+                               d = DotProduct (w->points[w->numpoints-1]
+                                       , f->plane.normal) - f->plane.dist;
+                               if (d > -ON_EPSILON && d < ON_EPSILON)
+                                       i = w->numpoints - 1;
+                       }
+
+                       AddPlanept (f2->planepts[0]);
+
+                       VectorCopy (w->points[i], f2->planepts[0]);
+                       if (++i == w->numpoints)
+                               i = 0;
+
+                       // see if the next point is also on the plane
+                       d = DotProduct (w->points[i]
+                               , f->plane.normal) - f->plane.dist;
+                       if (d > -ON_EPSILON && d < ON_EPSILON)
+                               AddPlanept (f2->planepts[1]);
+
+                       VectorCopy (w->points[i], f2->planepts[1]);
+                       if (++i == w->numpoints)
+                               i = 0;
+
+                       // the third point is never on the plane
+
+                       VectorCopy (w->points[i], f2->planepts[2]);
+               }
+
+               free(w);
+       }
+}
+
+/*
+==============
+Brush_SideSelect
+
+The mouse click did not hit the brush, so grab one or more side
+planes for dragging
+==============
+*/
+void Brush_SideSelect (brush_t *b, vec3_t origin, vec3_t dir
+                                          , qboolean shear)
+{
+       face_t  *f, *f2;
+       vec3_t  p1, p2;
+
+       for (f=b->brush_faces ; f ; f=f->next)
+       {
+               VectorCopy (origin, p1);
+               VectorMA (origin, 16384, dir, p2);
+
+               for (f2=b->brush_faces ; f2 ; f2=f2->next)
+               {
+                       if (f2 == f)
+                               continue;
+                       ClipLineToFace (p1, p2, f2);
+               }
+
+               if (f2)
+                       continue;
+
+               if (VectorCompare (p1, origin))
+                       continue;
+               if (ClipLineToFace (p1, p2, f))
+                       continue;
+
+               Brush_SelectFaceForDragging (b, f, shear);
+       }
+
+
+}
+
+void Brush_BuildWindings( brush_t *b )
+{
+       winding_t *w;
+       face_t    *face;
+       vec_t      v;
+
+       Brush_SnapPlanepts( b );
+
+       // clear the mins/maxs bounds
+       b->mins[0] = b->mins[1] = b->mins[2] = 99999;
+       b->maxs[0] = b->maxs[1] = b->maxs[2] = -99999;
+
+       Brush_MakeFacePlanes (b);
+
+       face = b->brush_faces;
+
+       for ( ; face ; face=face->next)
+       {
+               int i, j;
+
+               w = face->face_winding = MakeFaceWinding (b, face);
+               face->d_texture = Texture_ForName( face->texdef.name );
+
+               if (!w)
+               {
+                       continue;
+               }
+
+           for (i=0 ; i<w->numpoints ; i++)
+           {
+                       // add to bounding box
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               v = w->points[i][j];
+                               if (v > b->maxs[j])
+                                       b->maxs[j] = v;
+                               if (v < b->mins[j])
+                                       b->mins[j] = v;
+                       }
+           }
+               // setup s and t vectors, and set color
+               BeginTexturingFace( b, face, face->d_texture);
+
+
+           for (i=0 ; i<w->numpoints ; i++)
+           {
+                       EmitTextureCoordinates( w->points[i], face->d_texture, face);
+           }
+       }
+}
+
+/*
+==================
+Brush_RemoveEmptyFaces
+
+Frees any overconstraining faces
+==================
+*/
+void Brush_RemoveEmptyFaces ( brush_t *b )
+{
+       face_t  *f, *next;
+
+       f = b->brush_faces;
+       b->brush_faces = NULL;
+
+       for ( ; f ; f=next)
+       {
+               next = f->next;
+               if (!f->face_winding)
+                       Face_Free (f);
+               else
+               {
+                       f->next = b->brush_faces;
+                       b->brush_faces = f;
+               }
+
+       }
+}
+
+void Brush_Draw( brush_t *b )
+{
+       face_t                  *face;
+       int                             i, order;
+    qtexture_t         *prev = 0;
+       winding_t *w;
+
+       if (b->owner->eclass->fixedsize && camera.draw_mode == cd_texture)
+               glDisable (GL_TEXTURE_2D);
+
+       // guarantee the texture will be set first
+       prev = NULL;
+       for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++)
+       {
+               w = face->face_winding;
+               if (!w)
+                       continue;               // freed face
+
+               if ( face->d_texture != prev && camera.draw_mode == cd_texture)
+               {
+                       // set the texture for this face
+                       prev = face->d_texture;
+                       glBindTexture( GL_TEXTURE_2D, face->d_texture->texture_number );
+               }
+
+               glColor3fv( face->d_color );
+
+               // draw the polygon
+               glBegin(GL_POLYGON);
+           for (i=0 ; i<w->numpoints ; i++)
+               {
+                       if (camera.draw_mode == cd_texture)
+                               glTexCoord2fv( &w->points[i][3] );
+                       glVertex3fv(w->points[i]);
+               }
+               glEnd();
+       }
+
+       if (b->owner->eclass->fixedsize && camera.draw_mode == cd_texture)
+               glEnable (GL_TEXTURE_2D);
+
+       glBindTexture( GL_TEXTURE_2D, 0 );
+}
+
+void Face_Draw( face_t *f )
+{
+       int i;
+
+       if ( f->face_winding == 0 )
+               return;
+       glBegin( GL_POLYGON );
+       for ( i = 0 ; i < f->face_winding->numpoints; i++)
+               glVertex3fv( f->face_winding->points[i] );
+       glEnd();
+}
+
+void Brush_DrawXY( brush_t *b )
+{
+       face_t *face;
+       int     order;
+       winding_t *w;
+       int        i;
+
+       for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++)
+       {
+               // only draw up facing polygons
+               if (face->plane.normal[2] <= 0)
+                       continue;
+
+               w = face->face_winding;
+               if (!w)
+                       continue;
+
+               // draw the polygon
+               glBegin(GL_LINE_LOOP);
+           for (i=0 ; i<w->numpoints ; i++)
+                       glVertex3fv(w->points[i]);
+               glEnd();
+       }
+
+       // optionally add a text label
+       if ( g_qeglobals.d_savedinfo.show_names )
+               DrawBrushEntityName (b);
+}
+
+face_t *Face_Alloc( void )
+{
+       face_t *f = qmalloc( sizeof( *f ) );
+
+       return f;
+}
+
+void Face_Free( face_t *f )
+{
+       assert( f != 0 );
+
+       if ( f->face_winding )
+               free( f->face_winding ), f->face_winding = 0;
+       free( f );
+}