1811 lines
37 KiB
C
1811 lines
37 KiB
C
|
#include <assert.h>
|
||
|
#include "qe3.h"
|
||
|
|
||
|
#define MAX_POINTS_ON_WINDING 64
|
||
|
|
||
|
|
||
|
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] = q->scale_x;
|
||
|
if (!f->texdef.scale[1])
|
||
|
f->texdef.scale[1] = q->scale_y;
|
||
|
|
||
|
|
||
|
// 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, 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.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
|
||
|
static int inabuild = 0;
|
||
|
void Brush_Build( brush_t *b )
|
||
|
{
|
||
|
// int order;
|
||
|
// face_t *face;
|
||
|
// winding_t *w;
|
||
|
char title[1024];
|
||
|
|
||
|
QE_ResetIdle();
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
if (TokenAvailable())
|
||
|
{
|
||
|
GetToken (false);
|
||
|
f->texdef.lighting[0] = atof(token); // red
|
||
|
GetToken (false);
|
||
|
f->texdef.lighting[1] = atof(token); // green
|
||
|
GetToken (false);
|
||
|
f->texdef.lighting[2] = atof(token); // blue
|
||
|
GetToken (false);
|
||
|
f->texdef.lighting[3] = atof(token); // alpha
|
||
|
}
|
||
|
|
||
|
} 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 || (fa->texdef.flags & 0x400))
|
||
|
{
|
||
|
fprintf (f, " %i %i %i", fa->texdef.contents, fa->texdef.flags, fa->texdef.value);
|
||
|
}
|
||
|
|
||
|
|
||
|
if(fa->texdef.flags & 0x400) // tall wall, write lighting values
|
||
|
{
|
||
|
fprintf (f, " %f %f %f %f", fa->texdef.lighting[0], fa->texdef.lighting[1], fa->texdef.lighting[2], fa->texdef.lighting[3]);
|
||
|
}
|
||
|
|
||
|
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));
|
||
|
b->texLocked = false;
|
||
|
|
||
|
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;
|
||
|
qboolean wasLocked;
|
||
|
|
||
|
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;
|
||
|
wasLocked = b->texLocked;
|
||
|
|
||
|
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));
|
||
|
b->texLocked = wasLocked;
|
||
|
|
||
|
// 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_MakeSided_Snap
|
||
|
|
||
|
Makes the current brushhave the given number of 2d sides, and snap vertices to grid
|
||
|
=============
|
||
|
*/
|
||
|
void Brush_MakeSided_Snap (int sides)
|
||
|
{
|
||
|
int i;
|
||
|
vec3_t mins, maxs;
|
||
|
brush_t *b;
|
||
|
texdef_t *texdef;
|
||
|
face_t *f;
|
||
|
vec3_t mid;
|
||
|
float width;
|
||
|
float s1, s2, c1, c2;
|
||
|
float alpha;
|
||
|
float ca;
|
||
|
qboolean wasLocked;
|
||
|
|
||
|
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;
|
||
|
wasLocked = b->texLocked;
|
||
|
|
||
|
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));
|
||
|
b->texLocked = wasLocked;
|
||
|
|
||
|
// 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];
|
||
|
|
||
|
alpha = (3.14159265/sides);
|
||
|
ca = cos (alpha);
|
||
|
for (i=0 ; i<sides ; i++)
|
||
|
{
|
||
|
f = Face_Alloc();
|
||
|
f->texdef = *texdef;
|
||
|
f->next = b->brush_faces;
|
||
|
b->brush_faces = f;
|
||
|
|
||
|
s1 = sin ((i*3.14159265*2/sides) - alpha);
|
||
|
s2 = sin ((i*3.14159265*2/sides) + alpha);
|
||
|
c1 = cos ((i*3.14159265*2/sides) - alpha);
|
||
|
c2 = cos ((i*3.14159265*2/sides) + alpha);
|
||
|
f->planepts[0][0] = floor(mid[0]+(width/ca*c1+0.5));
|
||
|
f->planepts[0][1] = floor(mid[1]+(width/ca*s1+0.5));
|
||
|
|
||
|
f->planepts[1][0] = f->planepts[0][0];
|
||
|
f->planepts[1][1] = f->planepts[0][1];
|
||
|
|
||
|
f->planepts[2][0] = floor(mid[0]+(width/ca*c2+0.5));
|
||
|
f->planepts[2][1] = floor(mid[1]+(width/ca*s2+0.5));
|
||
|
|
||
|
|
||
|
f->planepts[0][0] = floor(f->planepts[0][0]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
|
||
|
f->planepts[0][1] = floor(f->planepts[0][1]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
|
||
|
f->planepts[0][2] = mins[2];
|
||
|
f->planepts[1][0] = floor(f->planepts[1][0]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
|
||
|
f->planepts[1][1] = floor(f->planepts[1][1]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
|
||
|
f->planepts[1][2] = maxs[2];
|
||
|
f->planepts[2][0] = floor(f->planepts[2][0]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
|
||
|
f->planepts[2][1] = floor(f->planepts[2][1]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
|
||
|
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, qboolean moveorigin)
|
||
|
{
|
||
|
int i, j;
|
||
|
face_t *f;
|
||
|
float points[5], s[MAXPOINTS], t[MAXPOINTS];
|
||
|
vec3_t origin;
|
||
|
char text[128];
|
||
|
|
||
|
j=0;
|
||
|
for (f=b->brush_faces ; f ; f=f->next)
|
||
|
{
|
||
|
if(b->texLocked)
|
||
|
{
|
||
|
VectorCopy(f->planepts[0], points);
|
||
|
EmitTextureCoordinates(points, f->d_texture, f);
|
||
|
s[j] = points[3];
|
||
|
t[j++] = points[4];
|
||
|
}
|
||
|
for (i=0 ; i<3 ; i++)
|
||
|
VectorAdd (f->planepts[i], move, f->planepts[i]);
|
||
|
|
||
|
}
|
||
|
|
||
|
if (strcmp(b->owner->eclass->name,"worldspawn") && (b->owner->eclass->fixedsize ||
|
||
|
b->owner->eclass->PhysicsModel) && moveorigin)
|
||
|
{ //need to set this only once for physics models!
|
||
|
GetVectorForKey (b->owner, "origin", origin);
|
||
|
VectorAdd (origin, move, origin);
|
||
|
sprintf (text, "%i %i %i", (int)origin[0],(int)origin[1], (int)origin[2]);
|
||
|
SetKeyValue (b->owner, "origin", text);
|
||
|
}
|
||
|
if(b->texLocked)
|
||
|
{
|
||
|
j=0;
|
||
|
for (f=b->brush_faces ; f ; f=f->next)
|
||
|
{
|
||
|
VectorCopy(f->planepts[0], points);
|
||
|
EmitTextureCoordinates(points, f->d_texture, f);
|
||
|
f->texdef.shift[0] += (s[j] - points[3]) * f->d_texture->width;
|
||
|
f->texdef.shift[1] += (t[j++] - points[4]) * f->d_texture->height;
|
||
|
}
|
||
|
}
|
||
|
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;
|
||
|
n->texLocked = b->texLocked;
|
||
|
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
|
||
|
|
||
|
Intersects 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;
|
||
|
// continue;
|
||
|
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_AddToList: 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;
|
||
|
vec3_t testVect, tempVect;
|
||
|
|
||
|
VectorAdd(b->mins, b->maxs, tempVect);
|
||
|
VectorScale(tempVect, .5, tempVect);
|
||
|
VectorSubtract(tempVect, camera.origin, testVect);
|
||
|
if(VectorLength(testVect) > g_qeglobals.d_maxViewDist)return;
|
||
|
|
||
|
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 );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
Brush_Scale
|
||
|
|
||
|
Scales all selected brushes
|
||
|
==================
|
||
|
*/
|
||
|
|
||
|
void Brush_Scale(float scale, int groupCenter, int nox, int noy, int noz)
|
||
|
{
|
||
|
brush_t *b, *startBrush;
|
||
|
face_t *face;
|
||
|
int i;
|
||
|
int numBrushes = 0;
|
||
|
vec3_t center, dist;
|
||
|
|
||
|
|
||
|
if(groupCenter)
|
||
|
{
|
||
|
b = selected_brushes.next;
|
||
|
|
||
|
startBrush = b;
|
||
|
|
||
|
VectorClear(center);
|
||
|
|
||
|
while(b != &selected_brushes)
|
||
|
{
|
||
|
numBrushes++;
|
||
|
|
||
|
VectorAdd(center, b->maxs, center);
|
||
|
VectorAdd(center, b->mins, center);
|
||
|
|
||
|
b = b->next;
|
||
|
}
|
||
|
VectorScale(center, (1.0 / (numBrushes*2.0)), center);
|
||
|
}
|
||
|
|
||
|
b = selected_brushes.next;
|
||
|
|
||
|
startBrush = b;
|
||
|
|
||
|
while(b != &selected_brushes)
|
||
|
{
|
||
|
if(!groupCenter)
|
||
|
{
|
||
|
VectorAdd(b->mins, b->maxs, center);
|
||
|
VectorScale(center, .5, center);
|
||
|
}
|
||
|
|
||
|
// Stretch the thingy out
|
||
|
for (face = b->brush_faces; face ; face=face->next)
|
||
|
{
|
||
|
// scale the planepts
|
||
|
for (i=0 ; i<3 ; i++)
|
||
|
{
|
||
|
VectorSubtract(face->planepts[i], center, dist);
|
||
|
if(!nox)
|
||
|
{
|
||
|
face->planepts[i][0] = center[0] + dist[0] * scale;
|
||
|
}
|
||
|
if(!noy)
|
||
|
{
|
||
|
face->planepts[i][1] = center[1] + dist[1] * scale;
|
||
|
}
|
||
|
if(!noz)
|
||
|
{
|
||
|
face->planepts[i][2] = center[2] + dist[2] * scale;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Brush_Build(b);
|
||
|
|
||
|
b = b->next;
|
||
|
}
|
||
|
|
||
|
Sys_UpdateWindows (W_ALL);
|
||
|
}
|