jedioutcast/utils/Radiant/brush.cpp
2013-04-04 13:07:40 -05:00

3530 lines
78 KiB
C++

#include "stdafx.h"
#include <assert.h>
#include "qe3.h"
#include "oddbits.h"
#include "groupnames.h"
#define MAX_POINTS_ON_WINDING 64
face_t *Face_Alloc( void );
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);
qboolean qbShowFaces = false;
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;
}
//============================================================================
/*
==================
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 = (winding_t*) 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 = (winding_t*)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 + 0.0001))
{
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];
/*
================
SetFaceColor
================
*/
void SetFaceColor (brush_t *b, face_t *f, float fCurveColor)
{
float shade;
qtexture_t *q;
q = f->d_texture;
// set shading for face
shade = SetShadeForPlane (&f->plane);
if (g_pParentWnd->GetCamera()->Camera().draw_mode == cd_texture && !b->owner->eclass->fixedsize)
{
//if (b->curveBrush)
// shade = fCurveColor;
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];
}
}
/*
================
FaceTextureVectors
================
*/
void FaceTextureVectors (face_t *f, float STfromXYZ[2][4])
{
vec3_t pvecs[2];
int sv, tv;
float ang, sinv, cosv;
float ns, nt;
int i,j;
qtexture_t *q;
texdef_t *td;
td = &f->texdef;
q = f->d_texture;
memset (STfromXYZ, 0, 8*sizeof(float));
if (!td->scale[0])
td->scale[0] = (g_PrefsDlg.m_bHiColorTextures) ? fTEXTURE_SCALE : 1;
if (!td->scale[1])
td->scale[1] = (g_PrefsDlg.m_bHiColorTextures) ? fTEXTURE_SCALE : 1;
// get natural texture axis
TextureAxisFromPlane(&f->plane, pvecs[0], pvecs[1]);
// rotate axis
if (td->rotate == 0)
{ sinv = 0 ; cosv = 1; }
else if (td->rotate == 90)
{ sinv = 1 ; cosv = 0; }
else if (td->rotate == 180)
{ sinv = 0 ; cosv = -1; }
else if (td->rotate == 270)
{ sinv = -1 ; cosv = 0; }
else
{
ang = td->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];
STfromXYZ[i][sv] = ns;
STfromXYZ[i][tv] = nt;
}
// scale
for (i=0 ; i<2 ; i++)
for (j=0 ; j<3 ; j++)
STfromXYZ[i][j] = STfromXYZ[i][j] / td->scale[i];
// shift
STfromXYZ[0][3] = td->shift[0];
STfromXYZ[1][3] = td->shift[1];
for (j=0 ; j<4 ; j++) {
STfromXYZ[0][j] /= q->width;
STfromXYZ[1][j] /= q->height;
}
}
void EmitTextureCoordinates ( float *xyzst, qtexture_t *q, face_t *f)
{
float STfromXYZ[2][4];
FaceTextureVectors (f, STfromXYZ);
xyzst[3] = DotProduct (xyzst, STfromXYZ[0]) + STfromXYZ[0][3];
xyzst[4] = DotProduct (xyzst, STfromXYZ[1]) + STfromXYZ[1][3];
}
//==========================================================================
/*
=================
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 = MIN_WORLD_COORD;
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, MAX_WORLD_COORD, vup);
VectorScale (vright, MAX_WORLD_COORD, 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_MakeFacePlane (face_t *f) {
int j;
vec3_t t1, t2, t3;
// 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 Brush_MakeFacePlanes (brush_t *b)
{
face_t *f;
for (f=b->brush_faces ; f ; f=f->next)
{
Brush_MakeFacePlane (f);
}
}
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
// MERGEME
#if 0
if (!(g_qeglobals.d_savedinfo.exclude & EXCLUDE_ANGLES))
{
// 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;
qglBegin (GL_LINE_STRIP);
qglVertex3fv (mid);
mid[0] += c*8;
mid[1] += s*8;
mid[2] += s*8;
qglVertex3fv (mid);
mid[0] -= c*4;
mid[1] -= s*4;
mid[2] -= s*4;
mid[0] -= s*4;
mid[1] += c*4;
mid[2] += c*4;
qglVertex3fv (mid);
mid[0] += c*4;
mid[1] += s*4;
mid[2] += s*4;
mid[0] += s*4;
mid[1] -= c*4;
mid[2] -= c*4;
qglVertex3fv (mid);
mid[0] -= c*4;
mid[1] -= s*4;
mid[2] -= s*4;
mid[0] += s*4;
mid[1] -= c*4;
mid[2] -= c*4;
qglVertex3fv (mid);
qglEnd ();
}
}
#endif
// slightly odd code for SHOW_GROUPNAMES I know...
//
if (g_qeglobals.d_savedinfo.show_names)
{
name = ValueForKey (b->owner, "classname");
if (g_qeglobals.d_savedinfo.exclude & SHOW_GROUPNAMES)
{
char *psGroupName = ValueForKey (b->owner, sKEYFIELD_GROUPNAME);
if (strlen(psGroupName))
{
name = va("%s ( %s )",name,psGroupName);
}
}
qglRasterPos3f (b->mins[0]+4, b->mins[1]+4, b->mins[2]+4);
qglCallLists (strlen(name), GL_UNSIGNED_BYTE, name);
#ifdef QUAKE3
if (strnicmp(name,"waypoint",8)==0)
{
name = ValueForKey (b->owner, "targetname");
if (name && strlen(name)) // just being safe....
{
CString name2;
name2.Format("( %s )",name);
qglRasterPos3f (b->mins[0]+4, b->mins[1]+4-10, b->mins[2]+4);
qglCallLists (strlen(name2), GL_UNSIGNED_BYTE, name2);
}
}
#endif
}
else
{
// (this is deliberately not subservient to the above bool)
//
if (g_qeglobals.d_savedinfo.exclude & SHOW_GROUPNAMES)
{
name = ValueForKey (b->owner, sKEYFIELD_GROUPNAME);
if (strlen(name))
{
name = va("( Group: \"%s\" )",name);
qglRasterPos3f (b->mins[0]+4, b->mins[1]+4, b->mins[2]+4);
qglCallLists (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;
if (g_PrefsDlg.m_bNoClamp)
return;
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, bool bSnap, bool bMarkMap )
{
// int order;
// face_t *face;
// winding_t *w;
/*
** build the windings and generate the bounding box
*/
Brush_BuildWindings(b, bSnap);
Patch_BuildPoints (b);
/*
b->alphaBrush = false;
for (face_t *f=b->brush_faces ; f; f=f->next)
{
if (f->texdef.flags & SURF_ALPHA)
{
b->alphaBrush = true;
break;
}
}
*/
/*
** 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 ();
if (bMarkMap)
{
Sys_MarkMapModified();
}
}
/*
=================
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 = (brush_t*)qmalloc(sizeof(brush_t));
do
{
if (!GetToken (true))
break;
if (!strcmp (token, "}") )
break;
if (strcmpi(token, "patchDef2") == 0 || strcmpi(token, "patchDef3") == 0)
{
free (b);
// double string compare but will go away soon
b = Patch_Parse(strcmpi(token, "patchDef2") == 0);
if (b == NULL)
{
Warning ("parsing patch/brush");
return NULL;
}
else
{
continue;
}
// handle inline patch
}
else
{
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, "(") )
{
Warning ("parsing brush");
return NULL;
}
for (j=0 ; j<3 ; j++)
{
GetToken (false);
f->planepts[i][j] = atof(token);
}
GetToken (false);
if (strcmp (token, ")") )
{
Warning ("parsing brush");
return NULL;
}
}
}
// 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 (but all new maps saved in this version *do* write them)
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);
}
#ifdef SOF
// optional fields here depending on flags...
//
if(f->texdef.flags & 0x400) // tall wall, read lighting values
{
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
}
else
{
Error ("Brush_Parse: Expecting tall-wall lighting values, but none found!");
}
}
#endif
} while (1);
return b;
}
/*
=================
Brush_Write
=================
*/
void Brush_Write (brush_t *b, FILE *f)
{
face_t *fa;
char *pname;
int i;
if (b->patchBrush)
{
Patch_Write(b->nPatchID, f);
return;
}
fprintf (f, "{\n");
for (fa=b->brush_faces ; fa ; fa=fa->next)
{
if (g_PrefsDlg.m_bNoClamp)
{
for (i=0 ; i<3 ; i++)
{
fprintf(f, "( ");
for (int j = 0; j < 3; j++)
{
if (fa->planepts[i][j] == static_cast<int>(fa->planepts[i][j]))
fprintf(f, "%i ", static_cast<int>(fa->planepts[i][j]));
else
fprintf(f, "%f ", fa->planepts[i][j]);
}
fprintf(f, ") ");
}
}
else
{
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 ",
#ifdef QUAKE3
strlwr
#endif
(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
// KPRadiant (and any tga setup always needs to write them unless the build tools read the ini file
#if 1
//if (!(g_pParentWnd->GetPlugInMgr().GetTextureInfo() && g_pParentWnd->GetPlugInMgr().GetTextureInfo()->m_bHalfLife))
//{
fprintf (f, " %i %i %i", fa->texdef.contents, fa->texdef.flags, fa->texdef.value);
//}
#else
bool bFlagsOut = false;
if (!fa->d_texture || 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);
bFlagsOut = true;
}
#endif
#ifdef SOF
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]);
}
#endif
fprintf (f, "\n");
}
fprintf (f, "}\n");
}
/*
=================
Brush_Write to a CMemFile*
=================
*/
void Brush_Write (brush_t *b, CMemFile *pMemFile)
{
face_t *fa;
char *pname;
int i;
if (b->patchBrush)
{
Patch_Write(b->nPatchID, pMemFile);
return;
}
MemFile_fprintf (pMemFile, "{\n");
for (fa=b->brush_faces ; fa ; fa=fa->next)
{
if (g_PrefsDlg.m_bNoClamp)
{
for (i=0 ; i<3 ; i++)
{
MemFile_fprintf (pMemFile, "( ");
for (int j = 0; j < 3; j++)
{
if (fa->planepts[i][j] == static_cast<int>(fa->planepts[i][j]))
MemFile_fprintf (pMemFile, "%i ", static_cast<int>(fa->planepts[i][j]));
else
MemFile_fprintf (pMemFile, "%f ", fa->planepts[i][j]);
}
MemFile_fprintf (pMemFile, ") ");
}
}
else
{
for (i=0 ; i<3 ; i++)
{
MemFile_fprintf (pMemFile, "( %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";
MemFile_fprintf (pMemFile, "%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])
MemFile_fprintf (pMemFile, "%i ", (int)fa->texdef.scale[0]);
else
MemFile_fprintf (pMemFile, "%f ", (float)fa->texdef.scale[0]);
if (fa->texdef.scale[1] == (int)fa->texdef.scale[1])
MemFile_fprintf (pMemFile, "%i", (int)fa->texdef.scale[1]);
else
MemFile_fprintf (pMemFile, "%f", (float)fa->texdef.scale[1]);
// only output flags and value if not default
bool bFlagsOut = false;
if (fa->texdef.value != fa->d_texture->value
|| fa->texdef.flags != fa->d_texture->flags
|| fa->texdef.contents != fa->d_texture->contents)
{
MemFile_fprintf (pMemFile, " %i %i %i", fa->texdef.contents, fa->texdef.flags, fa->texdef.value);
bFlagsOut = true;
}
#ifdef SOF
if(fa->texdef.flags & 0x400) // tall wall, write lighting values
{
MemFile_fprintf (pMemFile, " %f %f %f %f", fa->texdef.lighting[0], fa->texdef.lighting[1], fa->texdef.lighting[2], fa->texdef.lighting[3]);
}
#endif
MemFile_fprintf (pMemFile, "\n");
}
MemFile_fprintf (pMemFile, "}\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 = (brush_t*)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->texdef.flags &= ~SURF_KEEP;
f->texdef.contents &= ~CONTENTS_KEEP;
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->texdef.flags &= ~SURF_KEEP;
f->texdef.contents &= ~CONTENTS_KEEP;
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->texdef.flags &= ~SURF_KEEP;
f->texdef.contents &= ~CONTENTS_KEEP;
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_CreatePyramid
Create non-textured pyramid for light entities
The brush is NOT linked to any list
=============
*/
brush_t *Brush_CreatePyramid (vec3_t mins, vec3_t maxs, texdef_t *texdef)
{
return Brush_Create(mins, maxs, texdef);
for (int i=0 ; i<3 ; i++)
if (maxs[i] < mins[i])
Error ("Brush_InitSolid: backwards");
brush_t* b = (brush_t*)qmalloc (sizeof(brush_t));
vec3_t corners[4];
float fMid = Q_rint(mins[2] + (Q_rint((maxs[2] - mins[2]) / 2)));
corners[0][0] = mins[0];
corners[0][1] = mins[1];
corners[0][2] = fMid;
corners[1][0] = mins[0];
corners[1][1] = maxs[1];
corners[1][2] = fMid;
corners[2][0] = maxs[0];
corners[2][1] = maxs[1];
corners[2][2] = fMid;
corners[3][0] = maxs[0];
corners[3][1] = mins[1];
corners[3][2] = fMid;
vec3_t top, bottom;
top[0] = Q_rint(mins[0] + ((maxs[0] - mins[0]) / 2));
top[1] = Q_rint(mins[1] + ((maxs[1] - mins[1]) / 2));
top[2] = Q_rint(maxs[2]);
VectorCopy(top, bottom);
bottom[2] = mins[2];
// sides
for (i = 0; i < 4; i++)
{
face_t* f = Face_Alloc();
f->texdef = *texdef;
f->texdef.flags &= ~SURF_KEEP;
f->texdef.contents &= ~CONTENTS_KEEP;
f->next = b->brush_faces;
b->brush_faces = f;
int j = (i+1)%4;
VectorCopy (top, f->planepts[0]);
VectorCopy (corners[i], f->planepts[1]);
VectorCopy(corners[j], f->planepts[2]);
f = Face_Alloc();
f->texdef = *texdef;
f->texdef.flags &= ~SURF_KEEP;
f->texdef.contents &= ~CONTENTS_KEEP;
f->next = b->brush_faces;
b->brush_faces = f;
VectorCopy (bottom, f->planepts[2]);
VectorCopy (corners[i], f->planepts[1]);
VectorCopy(corners[j], f->planepts[0]);
}
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 = (brush_t*)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 );
}
// unlink from active/selected list
if (b->next)
Brush_RemoveFromList (b);
// unlink from entity list
if (b->onext)
Entity_UnlinkBrush (b);
free (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 = (brush_t*)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;
double frac, d1, d2;
float scaleValue;
bool scale;
int i;
VectorCopy (origin, p1);
// Need to clamp the p1 - p2 vector to compelety inside the valid range
// This is horrid.. =)
scale = true;
scaleValue = WORLD_SIZE;
while(scale)
{
VectorMA(p1, scaleValue, dir, p2);
scale = false;
for(i = 0; i < 3; i++)
{
if(p2[i] > MAX_WORLD_COORD)
{
scale = true;
}
if(p2[i] < MIN_WORLD_COORD)
{
scale = true;
}
}
scaleValue *= 0.8f;
}
scaleValue /= 0.8f;
VectorMA(p1, scaleValue, dir, p2);
// ..end of nastiness
for (f=b->brush_faces ; f ; f=f->next)
{
d1 = (double) DotProduct (p1, f->plane.normal) - f->plane.dist;
d2 = (double) 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] = (double)p1[i] + frac * ((double)p2[i] - (double)p1[i]);
}
else
{
for (i=0 ; i<3 ; i++)
p2[i] = (double)p1[i] + frac * ((double)p2[i] - (double)p1[i]);
}
}
// find distance p1 is along dir
VectorSubtract (p1, origin, p1);
d1 = DotProduct (p1, dir);
*dist = d1;
return firstface;
}
//PGM
face_t *Brush_Point (vec3_t origin, brush_t *b)
{
face_t *f;
float d1;
for (f=b->brush_faces ; f ; f=f->next)
{
d1 = DotProduct (origin, f->plane.normal) - f->plane.dist;
if (d1 > 0)
{
return NULL; // point is on front side of face
}
}
return b->brush_faces;
}
//PGM
void Brush_AddToList (brush_t *b, brush_t *list, bool bAddToBackOfList /* = false */)
{
if (b->next || b->prev)
Error ("Brush_AddToList: already linked");
if (b->patchBrush && list == &selected_brushes)
{
Patch_Select(b->nPatchID);
}
if (bAddToBackOfList)
{
// lists in radiant are circular, so no need to traverse to list end, just link to list-head previous...
//
b->prev = list->prev;
list->prev->next = b;
list->prev = b;
b->next = list;
}
else
{
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");
if (b->patchBrush)
{
Patch_Deselect(b->nPatchID);
}
b->next->prev = b->prev;
b->prev->next = b->next;
b->next = b->prev = NULL;
}
/*
===============
SetFaceTexdef
Doesn't set the curve flags
===============
*/
void SetFaceTexdef (brush_t *b, face_t *f, texdef_t *texdef, bool bFitScale, bool bNoSystemTextureOverwrite)
{
int oldFlags;
int oldContents;
face_t *tf;
if (bNoSystemTextureOverwrite && !strnicmp(f->texdef.name,"system/",7))
{
Sys_Printf("( System-shader protecion: Refusing to overwrite face using \"%s\" )\n",f->texdef.name);
return;
}
oldFlags = f->texdef.flags;
oldContents = f->texdef.contents;
if (bFitScale)
{
f->texdef = *texdef;
// fit the scaling of the texture on the actual plane
vec3_t p1,p2,p3; // absolute coordinates
// compute absolute coordinates
ComputeAbsolute(f,p1,p2,p3);
// compute the scale
vec3_t vx,vy;
VectorSubtract(p2,p1,vx);
VectorNormalize(vx);
VectorSubtract(p3,p1,vy);
VectorNormalize(vy);
// assign scale
VectorScale(vx,texdef->scale[0],vx);
VectorScale(vy,texdef->scale[1],vy);
VectorAdd(p1,vx,p2);
VectorAdd(p1,vy,p3);
// compute back shift scale rot
AbsoluteToLocal(f->plane,f,p1,p2,p3);
}
else
f->texdef = *texdef;
f->texdef.flags = (f->texdef.flags & ~SURF_KEEP) | (oldFlags & SURF_KEEP);
f->texdef.contents = (f->texdef.contents & ~CONTENTS_KEEP) | (oldContents & CONTENTS_KEEP);
// if this is a curve face, set all other curve faces to the same texdef
if (f->texdef.flags & SURF_CURVE)
{
for (tf = b->brush_faces ; tf ; tf = tf->next)
{
if (tf->texdef.flags & SURF_CURVE)
{
tf->texdef = f->texdef;
}
}
}
}
void Brush_SetTexture (brush_t *b, texdef_t *texdef, bool bFitScale/*=false*/, bool bNoSystemTextureOverwrite/*=false*/)
{
for (face_t* f = b->brush_faces ; f ; f = f->next)
{
SetFaceTexdef (b, f, texdef, bFitScale, bNoSystemTextureOverwrite);
}
Brush_Build( b );
if (b->patchBrush)
{
Patch_SetTexture(b->nPatchID, texdef);
}
}
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;
static overflow = 0;
if (D_MAXPOINTS == g_qeglobals.d_num_move_points) //jfm: stop this overflow
{
overflow++;
Sys_Printf ("Trying to drag too many points!! %d\n",D_MAXPOINTS + overflow);
return 0;
}
overflow = 0;
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;
//if (b->patchBrush)
// return;
//Patch_SideSelect(b->nPatchID, origin, dir);
for (f=b->brush_faces ; f ; f=f->next)
{
VectorCopy (origin, p1);
VectorMA (origin, WORLD_SIZE, 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, bool bSnap )
{
winding_t *w;
face_t *face;
vec_t v;
if (bSnap)
Brush_SnapPlanepts( b );
// clear the mins/maxs bounds
b->mins[0] = b->mins[1] = b->mins[2] = MAX_WORLD_COORD;
b->maxs[0] = b->maxs[1] = b->maxs[2] = MIN_WORLD_COORD;
Brush_MakeFacePlanes (b);
face = b->brush_faces;
float fCurveColor = 1.0;
for ( ; face ; face=face->next)
{
int i, j;
free(face->face_winding);
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
SetFaceColor (b, face, fCurveColor);
fCurveColor -= .10;
if (fCurveColor <= 0)
fCurveColor = 1.0;
//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_SnapToGrid(brush_t *pb)
{
for (face_t *f = pb->brush_faces ; f; f = f->next)
{
for (int i = 0 ;i < 3 ;i++)
{
for (int j = 0 ;j < 3 ; j++)
{
f->planepts[i][j] = floor (f->planepts[i][j] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize;
}
}
}
Brush_Build(pb);
}
void Brush_Rotate(brush_t *b, vec3_t vAngle, vec3_t vOrigin, bool bBuild)
{
for (face_t* f=b->brush_faces ; f ; f=f->next)
{
for (int i=0 ; i<3 ; i++)
{
VectorRotate(f->planepts[i], vAngle, vOrigin, f->planepts[i]);
}
}
if (bBuild)
{
Brush_Build(b, false, false);
}
}
void Brush_Scale2 (eclass_t *e,brush_t *b, float scale, vec3_t vOrigin, bool bBuild)
{
vec3_t pts[4][2];
int i,j;
face_t* f;
pts[0][0][0] = e->mins[0];
pts[0][0][1] = e->mins[1];
pts[1][0][0] = e->mins[0];
pts[1][0][1] = e->maxs[1];
pts[2][0][0] = e->maxs[0];
pts[2][0][1] = e->maxs[1];
pts[3][0][0] = e->maxs[0];
pts[3][0][1] = e->mins[1];
for (i=0 ; i<4 ; i++)
{
pts[i][0][2] = e->mins[2];
pts[i][1][0] = pts[i][0][0];
pts[i][1][1] = pts[i][0][1];
pts[i][1][2] = e->maxs[2];
}
f=b->brush_faces;
for (i=0;i<4; i++,f=f->next)
{
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]);
}
VectorCopy (pts[0][1], f->planepts[0]);
VectorCopy (pts[1][1], f->planepts[1]);
VectorCopy (pts[2][1], f->planepts[2]);
f=f->next;
VectorCopy (pts[2][0], f->planepts[0]);
VectorCopy (pts[1][0], f->planepts[1]);
VectorCopy (pts[0][0], f->planepts[2]);
for (f=b->brush_faces ; f ; f=f->next)
{
for (int i=0 ; i<3 ; i++)
{
VectorScale(f->planepts[i], scale, f->planepts[i]);
VectorAdd(vOrigin,f->planepts[i],f->planepts[i]);
}
}
if (bBuild)
{
Brush_Build(b, false, false);
}
}
// only designed for fixed size entity brushes
void Brush_Resize(brush_t *b, vec3_t vMin, vec3_t vMax)
{
brush_t *b2 = Brush_Create(vMin, vMax, &b->brush_faces->texdef);
face_t *next;
for (face_t *f=b->brush_faces ; f ; f=next)
{
next = f->next;
Face_Free( f );
}
b->brush_faces = b2->brush_faces;
// unlink from active/selected list
if (b2->next)
Brush_RemoveFromList (b2);
free(b2);
Brush_Build(b, true);
}
eclass_t* HasModel(brush_t *b)
{
vec3_t vMin, vMax;
vMin[0] = vMin[1] = vMin[2] = MAX_WORLD_COORD;
vMax[0] = vMax[1] = vMax[2] = MIN_WORLD_COORD;
if (b->owner->md3Class != NULL)
{
return b->owner->md3Class;
}
if (Eclass_hasModel(b->owner->eclass, vMin, vMax))
{
return b->owner->eclass;
}
eclass_t *e = NULL;
// FIXME: entity needs to track whether a cache hit failed and not ask again
if (strnicmp(b->owner->eclass->name, "misc_model",10) == 0)
{
char *pModel = ValueForKey(b->owner, "model");
if (pModel != NULL && strlen(pModel) > 0)
{
e = GetCachedModel(b->owner, pModel, vMin, vMax);
if (e != NULL)
{
// we need to scale the brush to the proper size based on the model load
// recreate brush just like in load/save
VectorAdd( vMin, b->owner->origin, vMin);
VectorAdd( vMax, b->owner->origin, vMax);
Brush_Resize(b, vMin, vMax);
/*
// we need to scale the brush to the proper size based on the model load
vec3_t vTemp, vTemp2;
VectorSubtract(b->maxs, b->mins, vTemp);
VectorSubtract(vMax, vMin, vTemp2);
for (int i = 0; i < 3; i++)
{
if (vTemp[i] != 0)
{
vTemp2[i] /= vTemp[i];
}
}
vec3_t vMid;
vMid[0] = vMid[1] = vMid[2] = 0.0;
for (int j=0 ; j<3 ; j++)
{
vMid[j] = (b->mins[j] + (vTemp[j] / 2));
}
for (face_t* f=b->brush_faces ; f ; f=f->next)
{
for (int i=0 ; i<3 ; i++)
{
//scale
VectorSubtract(f->planepts[i], vMid, f->planepts[i]);
f->planepts[i][0] *= vTemp2[0];
f->planepts[i][1] *= vTemp2[1];
f->planepts[i][2] *= vTemp2[2];
VectorAdd(f->planepts[i], vMid, f->planepts[i]);
}
}
//Brush_SnapToGrid(b);
Brush_Build(b, true);
*/
b->bModelFailed = false;
}
else
{
b->bModelFailed = true;
}
}
}
return e;
}
static bool g_bInPaintedModel = false;
bool PaintedModel(brush_t *b, bool bOkToTexture, bool bIsGhost)
{
if (g_bInPaintedModel)
{
return true;
}
if (g_PrefsDlg.m_nEntityShowState == ENTITY_BOX)
{
return false;
}
else if (!IsBrushSelected(b) && (g_PrefsDlg.m_nEntityShowState & ENTITY_SELECTED_ONLY))
{
return false;
}
g_bInPaintedModel = true;
bool bReturn = false;
eclass_t *pEclass = HasModel(b);
if (pEclass)
{
qglPushAttrib(GL_ALL_ATTRIB_BITS);
entitymodel *model = pEclass->model;
float a = FloatForKey (b->owner, "angle");
//==============
#ifdef QUAKE3
bool bUseAnglesCode = false;
vec3_t v3Angles;
if (bOkToTexture)
{
vec3_t v3;
if (GetVectorForKey (b->owner, "angles", v3))
{
// for some reason this program's own maths routines expect entity axis orders differently, so...
//
v3Angles[0] = v3[2];
v3Angles[1] = v3[0];
v3Angles[2] = v3[1];
bUseAnglesCode = true;
}
}
#endif
//==============
while (model != NULL)
{
if (bOkToTexture == false || g_PrefsDlg.m_nEntityShowState & ENTITY_WIREFRAME || model->nTextureBind == -1) // skinned
{
qglDisable( GL_CULL_FACE );
qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
qglDisable(GL_TEXTURE_2D);
qglColor3fv(pEclass->color);
}
else
{
qglColor3f(1, 1, 1);
qglEnable(GL_TEXTURE_2D);
qglBindTexture( GL_TEXTURE_2D, model->nTextureBind );
}
vec3_t v;
int i,j;
VectorAdd(b->maxs, b->mins, v); //
VectorScale(v, 0.5, v); //
VectorCopy(b->owner->origin, v); // this is dumb, 3rd line just overwrites the other two -slc
if (bIsGhost)
{
qglColor4fv(v4GhostColor);
qglLineStipple( 8, 0xAAAA);
qglEnable(GL_LINE_STIPPLE);
}
//for (i = 0; i < 3; i++)
//{
// v[i] -= (pEclass->mins[i] - b->mins[i]);
//}
//if (model->nModelPosition)
//{
//v[2] = b->mins[2] - (pEclass->mins[2]);
//}
float s, c;
if (a)
{
s = sin (a/180*Q_PI);
c = cos (a/180*Q_PI);
}
qglBegin (GL_TRIANGLES);
for (i = 0; i < model->nTriCount; i++)
{
for (j = 0; j < 3; j++)
{
//==============
#ifdef QUAKE3
if (bUseAnglesCode)
{
vec3_t v3;
VectorSet(v3, model->pTriList[i].v[j][0] + v[0],
model->pTriList[i].v[j][1] + v[1],
model->pTriList[i].v[j][2] + v[2]
);
VectorRotate(v3, v3Angles, b->owner->origin, v3);
qglTexCoord2f (model->pTriList[i].st[j][0], model->pTriList[i].st[j][1]);
qglVertex3f(v3[0], v3[1], v3[2]);
}
else
#endif
//==============
{
float x = model->pTriList[i].v[j][0] + v[0];
float y = model->pTriList[i].v[j][1] + v[1];
if (a)
{
float x2 = (((x - v[0]) * c) - ((y - v[1]) * s)) + v[0];
float y2 = (((x - v[0]) * s) + ((y - v[1]) * c)) + v[1];
x = x2;
y = y2;
}
//qglTexCoord2f (pEclass->pTriList[i].st[j][0] / pEclass->nSkinWidth, pEclass->pTriList[i].st[j][1] / pEclass->nSkinHeight);
qglTexCoord2f (model->pTriList[i].st[j][0], model->pTriList[i].st[j][1]);
qglVertex3f(x, y, model->pTriList[i].v[j][2] + v[2]);
}
}
}
qglEnd();
if (g_PrefsDlg.m_nEntityShowState & ENTITY_WIREFRAME) // skinned
{
qglEnable(GL_CULL_FACE );
qglEnable(GL_TEXTURE_2D);
qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
}
else
{
qglDisable(GL_TEXTURE_2D);
}
if (bIsGhost)
{
qglDisable(GL_LINE_STIPPLE);
}
model = model->pNext;
}
if (g_PrefsDlg.m_nEntityShowState & ENTITY_BOXED)
{
if (bIsGhost)
{
qglColor4fv(v4GhostColor);
qglLineStipple( 8, 0xAAAA);
qglEnable(GL_LINE_STIPPLE);
}
else
{
qglColor3fv(pEclass->color);
}
qglBegin(GL_LINE_LOOP);
qglVertex3f(b->mins[0],b->mins[1],b->mins[2]);
qglVertex3f(b->maxs[0],b->mins[1],b->mins[2]);
qglVertex3f(b->maxs[0],b->maxs[1],b->mins[2]);
qglVertex3f(b->mins[0],b->maxs[1],b->mins[2]);
qglEnd();
qglBegin(GL_LINE_LOOP);
qglVertex3f(b->mins[0],b->mins[1],b->maxs[2]);
qglVertex3f(b->maxs[0],b->mins[1],b->maxs[2]);
qglVertex3f(b->maxs[0],b->maxs[1],b->maxs[2]);
qglVertex3f(b->mins[0],b->maxs[1],b->maxs[2]);
qglEnd();
qglBegin(GL_LINES);
qglVertex3f(b->mins[0],b->mins[1],b->mins[2]);
qglVertex3f(b->mins[0],b->mins[1],b->maxs[2]);
qglVertex3f(b->mins[0],b->maxs[1],b->maxs[2]);
qglVertex3f(b->mins[0],b->maxs[1],b->mins[2]);
qglVertex3f(b->maxs[0],b->mins[1],b->mins[2]);
qglVertex3f(b->maxs[0],b->mins[1],b->maxs[2]);
qglVertex3f(b->maxs[0],b->maxs[1],b->maxs[2]);
qglVertex3f(b->maxs[0],b->maxs[1],b->mins[2]);
qglEnd();
if (bIsGhost)
{
qglDisable(GL_LINE_STIPPLE);
}
}
qglPopAttrib();
bReturn = true;
}
else
{
b->bModelFailed = true;
}
g_bInPaintedModel = false;
return bReturn;
}
void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
{
float angle;
static float sr, sp, sy, cr, cp, cy;
// static to help MS compiler fp bugs
angle = angles[YAW] * Q_PI / 180;
sy = sin(angle);
cy = cos(angle);
angle = angles[PITCH] * Q_PI / 180;
sp = sin(angle);
cp = cos(angle);
angle = angles[ROLL] * Q_PI / 180;
sr = sin(angle);
cr = cos(angle);
if (forward)
{
forward[0] = cp*cy;
forward[1] = cp*sy;
forward[2] = -sp;
}
if (right)
{
right[0] = (-1*sr*sp*cy+-1*cr*-sy);
right[1] = (-1*sr*sp*sy+-1*cr*cy);
right[2] = -1*sr*cp;
}
if (up)
{
up[0] = (cr*sp*cy+-sr*-sy);
up[1] = (cr*sp*sy+-sr*cy);
up[2] = cr*cp;
}
}
void FacingVectors (entity_t *e, vec3_t forward, vec3_t right, vec3_t up)
{
int angleVal;
vec3_t angles;
angleVal = IntForKey(e, "angle");
if (angleVal == -1) // up
{
VectorSet(angles, 270, 0, 0);
}
else if(angleVal == -2) // down
{
VectorSet(angles, 90, 0, 0);
}
else
{
VectorSet(angles, 0, angleVal, 0);
}
AngleVectors(angles, forward, right, up);
}
void Brush_DrawFacingAngle (brush_t *b, entity_t *e)
{
vec3_t forward, right, up;
vec3_t endpoint, tip1, tip2;
vec3_t start;
float dist;
VectorAdd(e->brushes.onext->mins, e->brushes.onext->maxs, start);
VectorScale(start, 0.5, start);
dist = (b->maxs[0] - start[0]) * 2.5;
FacingVectors (e, forward, right, up);
VectorMA (start, dist, forward, endpoint);
dist = (b->maxs[0] - start[0]) * 0.5;
VectorMA (endpoint, -dist, forward, tip1);
VectorMA (tip1, -dist, up, tip1);
VectorMA (tip1, 2*dist, up, tip2);
qglColor4f (1, 1, 1, 1);
qglLineWidth (4);
qglBegin (GL_LINES);
qglVertex3fv (start);
qglVertex3fv (endpoint);
qglVertex3fv (endpoint);
qglVertex3fv (tip1);
qglVertex3fv (endpoint);
qglVertex3fv (tip2);
qglEnd ();
qglLineWidth (1);
}
void DrawLight(brush_t *b)
{
vec3_t vTriColor;
bool bTriPaint = false;
vTriColor[0] = vTriColor[2] = 1.0;
vTriColor[1] = 1.0;
bTriPaint = true;
CString strColor = ValueForKey(b->owner, "_color");
if (strColor.GetLength() > 0)
{
float fR, fG, fB;
int n = sscanf(strColor,"%f %f %f", &fR, &fG, &fB);
if (n == 3)
{
vTriColor[0] = fR;
vTriColor[1] = fG;
vTriColor[2] = fB;
}
}
qglColor3f(vTriColor[0], vTriColor[1], vTriColor[2]);
vec3_t vCorners[4];
float fMid = b->mins[2] + (b->maxs[2] - b->mins[2]) / 2;
vCorners[0][0] = b->mins[0];
vCorners[0][1] = b->mins[1];
vCorners[0][2] = fMid;
vCorners[1][0] = b->mins[0];
vCorners[1][1] = b->maxs[1];
vCorners[1][2] = fMid;
vCorners[2][0] = b->maxs[0];
vCorners[2][1] = b->maxs[1];
vCorners[2][2] = fMid;
vCorners[3][0] = b->maxs[0];
vCorners[3][1] = b->mins[1];
vCorners[3][2] = fMid;
vec3_t vTop, vBottom;
vTop[0] = b->mins[0] + ((b->maxs[0] - b->mins[0]) / 2);
vTop[1] = b->mins[1] + ((b->maxs[1] - b->mins[1]) / 2);
vTop[2] = b->maxs[2];
VectorCopy(vTop, vBottom);
vBottom[2] = b->mins[2];
vec3_t vSave;
VectorCopy(vTriColor, vSave);
qglBegin(GL_TRIANGLE_FAN);
qglVertex3fv(vTop);
for (int i = 0; i <= 3; i++)
{
vTriColor[0] *= 0.95;
vTriColor[1] *= 0.95;
vTriColor[2] *= 0.95;
qglColor3f(vTriColor[0], vTriColor[1], vTriColor[2]);
qglVertex3fv(vCorners[i]);
}
qglVertex3fv(vCorners[0]);
qglEnd();
VectorCopy(vSave, vTriColor);
vTriColor[0] *= 0.95;
vTriColor[1] *= 0.95;
vTriColor[2] *= 0.95;
qglBegin(GL_TRIANGLE_FAN);
qglVertex3fv(vBottom);
qglVertex3fv(vCorners[0]);
for (i = 3; i >= 0; i--)
{
vTriColor[0] *= 0.95;
vTriColor[1] *= 0.95;
vTriColor[2] *= 0.95;
qglColor3f(vTriColor[0], vTriColor[1], vTriColor[2]);
qglVertex3fv(vCorners[i]);
}
qglEnd();
}
extern qboolean qbCheckShowFaces;
void Brush_Draw( brush_t *b, bool bIsGhost )
{
face_t *face;
int i, order;
qtexture_t *prev = 0;
winding_t *w;
if (b->hiddenBrush)
{
return;
}
if (b->patchBrush)
{
Patch_DrawCam(b->nPatchID, bIsGhost);
// if (!g_bPatchShowBounds)
return;
}
int nDrawMode = g_pParentWnd->GetCamera()->Camera().draw_mode;
if (b->owner->eclass->fixedsize)
{
/* MERGEME
if(!(g_qeglobals.d_savedinfo.exclude & EXCLUDE_ANGLES) &&
!strnicmp(b->owner->eclass->name, "info_player", 11))
{
Brush_DrawFacingAngle(b, b->owner);
}
*/
if (g_PrefsDlg.m_bNewLightDraw && strcmpi(b->owner->eclass->name, "light") == 0)
{
DrawLight(b);
return;
}
if (nDrawMode == cd_texture || nDrawMode == cd_light)
qglDisable (GL_TEXTURE_2D);
// if we are wireframing models
bool bp = (b->bModelFailed) ? false : PaintedModel(b, true, bIsGhost);
if (nDrawMode == cd_texture || nDrawMode == cd_light)
qglEnable (GL_TEXTURE_2D);
if (bp)
return;
}
// 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 0
if (b->alphaBrush)
{
if (!(face->texdef.flags & SURF_ALPHA))
continue;
//--qglPushAttrib(GL_ALL_ATTRIB_BITS);
qglDisable(GL_CULL_FACE);
//--qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
//--qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//--qglDisable(GL_DEPTH_TEST);
//--qglBlendFunc (GL_SRC_ALPHA, GL_DST_ALPHA);
//--qglEnable (GL_BLEND);
}
#endif
if ((nDrawMode == cd_texture || nDrawMode == cd_light) && face->d_texture != prev)
{
// set the texture for this face
prev = face->d_texture;
qglBindTexture( GL_TEXTURE_2D, face->d_texture->texture_number );
}
if (!b->patchBrush)
{
if (face->texdef.flags & SURF_TRANS33)
qglColor4f ( face->d_color[0], face->d_color[1], face->d_color[2], 0.33 );
else if ( face->texdef.flags & SURF_TRANS66)
qglColor4f ( face->d_color[0], face->d_color[1], face->d_color[2], 0.66 );
else
qglColor3fv( face->d_color );
}
else
{
qglColor4f ( face->d_color[0], face->d_color[1], face->d_color[2], 0.13 );
}
// shader drawing stuff
if (face->d_texture->bFromShader)
{
// setup shader drawing
qglColor4f ( face->d_color[0], face->d_color[1], face->d_color[2], face->d_texture->fTrans );
}
if (bIsGhost)
{
qglColor4fv( v4GhostColor );
qglDisable (GL_TEXTURE_2D);
}
// draw the polygon
qglBegin(GL_POLYGON);
if (nDrawMode == cd_light)
qglNormal3fv(face->plane.normal);
for (i=0 ; i<w->numpoints ; i++)
{
if (nDrawMode == cd_texture || nDrawMode == cd_light)
qglTexCoord2fv( &w->points[i][3] );
qglVertex3fv(w->points[i]);
}
qglEnd();
// draw face lines over top of faces (artist/designer request)...
// drawing them as tris helps show how much overlap.waste there is because of diagonal lines
//
if (qbCheckShowFaces && qbShowFaces)
{
qglDisable (GL_TEXTURE_2D);
if (bIsGhost)
{
qglColor4fv( v4GhostColor );
}
else
{
qglColor3f(255,255,255);
}
for (i=2 ; i<w->numpoints ; i++)
{
qglBegin(GL_LINE_STRIP);
qglVertex3fv (w->points[0]);
qglVertex3fv (w->points[i-1]);
qglVertex3fv (w->points[i]);
qglVertex3fv (w->points[0]);
qglEnd ();
}
if (nDrawMode == cd_texture || nDrawMode == cd_light)
qglEnable (GL_TEXTURE_2D);
}
if (bIsGhost)
{
qglEnable (GL_TEXTURE_2D);
}
}
#if 0
if (b->alphaBrush)
{
//--qglPopAttrib();
qglEnable(GL_CULL_FACE);
//--qglDisable (GL_BLEND);
//--qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
#endif
if (b->owner->eclass->fixedsize && (nDrawMode == cd_texture || nDrawMode == cd_light))
qglEnable (GL_TEXTURE_2D);
qglBindTexture( GL_TEXTURE_2D, 0 );
}
void Face_Draw( face_t *f )
{
int i;
if ( f->face_winding == 0 )
return;
qglBegin( GL_POLYGON );
for ( i = 0 ; i < f->face_winding->numpoints; i++)
qglVertex3fv( f->face_winding->points[i] );
qglEnd();
}
bool gbSelectedLightBrushesAlwaysDrawRadii = false;
//#define DEGTORAD 0.01745329252
//#define RADTODEG 57.295779513
void Brush_DrawXY(brush_t *b, int nViewType, bool bIsGhost)
{
face_t *face;
int order;
winding_t *w;
int i;
if (b->hiddenBrush)
{
return;
}
if (b->patchBrush)
{
Patch_DrawXY(b->nPatchID, bIsGhost);
if (!g_bPatchShowBounds)
return;
}
if (b->owner->eclass->fixedsize)
{
if (g_PrefsDlg.m_bNewLightDraw && strcmpi(b->owner->eclass->name, "light") == 0)
{
vec3_t vCorners[4];
float fMid = b->mins[2] + (b->maxs[2] - b->mins[2]) / 2;
vCorners[0][0] = b->mins[0];
vCorners[0][1] = b->mins[1];
vCorners[0][2] = fMid;
vCorners[1][0] = b->mins[0];
vCorners[1][1] = b->maxs[1];
vCorners[1][2] = fMid;
vCorners[2][0] = b->maxs[0];
vCorners[2][1] = b->maxs[1];
vCorners[2][2] = fMid;
vCorners[3][0] = b->maxs[0];
vCorners[3][1] = b->mins[1];
vCorners[3][2] = fMid;
vec3_t vTop, vBottom;
vTop[0] = b->mins[0] + ((b->maxs[0] - b->mins[0]) / 2);
vTop[1] = b->mins[1] + ((b->maxs[1] - b->mins[1]) / 2);
vTop[2] = b->maxs[2];
VectorCopy(vTop, vBottom);
vBottom[2] = b->mins[2];
qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
qglBegin(GL_TRIANGLE_FAN);
qglVertex3fv(vTop);
qglVertex3fv(vCorners[0]);
qglVertex3fv(vCorners[1]);
qglVertex3fv(vCorners[2]);
qglVertex3fv(vCorners[3]);
qglVertex3fv(vCorners[0]);
qglEnd();
qglBegin(GL_TRIANGLE_FAN);
qglVertex3fv(vBottom);
qglVertex3fv(vCorners[0]);
qglVertex3fv(vCorners[3]);
qglVertex3fv(vCorners[2]);
qglVertex3fv(vCorners[1]);
qglVertex3fv(vCorners[0]);
qglEnd();
DrawBrushEntityName (b);
// now draw lighting radius stuff...
//
if (!(g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS_RADII) || (gbSelectedLightBrushesAlwaysDrawRadii && IsBrushSelected(b)) )
{
vec3_t v3Origin;
v3Origin[0] = b->mins[0] + (b->maxs[0] - b->mins[0]) / 2;
v3Origin[1] = b->mins[1] + (b->maxs[1] - b->mins[1]) / 2;
v3Origin[2] = b->mins[2] + (b->maxs[2] - b->mins[2]) / 2;
char *p = ValueForKey(b->owner, "light");
if (p[0]==0)
{
p = "300";
}
float f = atof(p);
float fLight = f;
float f45Len = 0.707;
vec3_t v3TargetOrigin;
bool bDrawSpotlightArc = false;
LPCSTR psTargetName = ValueForKey(b->owner, "target");
bool bIsSpotLight = !!psTargetName[0];
if (bIsSpotLight)
{
char *p = ValueForKey(b->owner, "radius");
if (p[0]==0)
{
p="64";
}
f = atof(p);
// find the origin of the target...
//
entity_t *e = FindEntity("targetname", psTargetName);
if (e)
{
v3TargetOrigin[0] = e->brushes.mins[0] + (e->brushes.maxs[0] - e->brushes.mins[0]) / 2;
v3TargetOrigin[1] = e->brushes.mins[1] + (e->brushes.maxs[1] - e->brushes.mins[1]) / 2;
v3TargetOrigin[2] = e->brushes.mins[2] + (e->brushes.maxs[2] - e->brushes.mins[2]) / 2;
bDrawSpotlightArc = true;
}
}
for (int iPass=0; iPass<2; iPass++)
{
if (iPass)
{
float _f = f;
#define iFLAGS_LINEAR 0x01
#define iFLAGS_NOINCIDENCE 0x02
if (bIsSpotLight)
{
if (bDrawSpotlightArc)
{
// I give up on this, it's beyond me
}
}
else
{
int iFlags = atoi( ValueForKey(b->owner,"spawnflags") );
if (iFlags & iFLAGS_NOINCIDENCE)
{
f *= 0.9;
}
else
{
f/=4;
}
p = ValueForKey(b->owner, "scale");
if (p[0]==0)
{
p="1";
}
float fScale = atof(p);
f *= fScale;
// clamp inner dotted circle so it never exceeds outer...
//
if (f>_f)
f=_f;
}
qglLineStipple( 8, 0xAAAA);
qglEnable(GL_LINE_STIPPLE);
}
qglBegin(GL_LINE_LOOP);
{
if (bIsSpotLight)
{
if (bDrawSpotlightArc)
{
// I give up on this, it's beyond me
/*
vec3_t v3Temp;
VectorSubtract(v3TargetOrigin,v3Origin,v3Temp);
float fAdjacent = VectorLength(v3Temp);
float fOpposite = f/2;
float f2 = atan2(fOpposite,fAdjacent);
f2 *= 180*Q_PI; // radtodeg
float x, y, a;
switch (g_pParentWnd->ActiveXY()->GetViewType())
{
case XY:
x = v3Origin[0];
y = v3Origin[1];
a = f2;
break;
case XZ:
x = v3Origin[0];
y = v3Origin[2];
a = f2;
break;
case YZ:
x = v3Origin[1];
y = v3Origin[2];
a = f2;
break;
}
// findmeste
qglVertex3f (x+48*cos(a+Q_PI/(360.0f/a)), y+48*sin(a+Q_PI/(360.0f/a)), 0);
qglVertex3f (x, y, 0);
qglVertex3f (x+48*cos(a-Q_PI/(360.0f/a)), y+48*sin(a-Q_PI/(360.0f/a)), 0);
qglEnd ();
*/
}
}
else
{
switch (g_pParentWnd->ActiveXY()->GetViewType())
{
case XY:
qglVertex3f( v3Origin[0]+(f*0), v3Origin[1]+(f*1), v3Origin[2] );
qglVertex3f( v3Origin[0]+(f*f45Len), v3Origin[1]+(f*f45Len), v3Origin[2] );
qglVertex3f( v3Origin[0]+(f*1), v3Origin[1]+(f*0), v3Origin[2] );
qglVertex3f( v3Origin[0]+(f*f45Len), v3Origin[1]+(-f*f45Len), v3Origin[2] );
qglVertex3f( v3Origin[0]+(f*0), v3Origin[1]+(-f*1), v3Origin[2] );
qglVertex3f( v3Origin[0]+(-f*f45Len), v3Origin[1]+(-f*f45Len), v3Origin[2] );
qglVertex3f( v3Origin[0]+(-f*1), v3Origin[1]+(f*0), v3Origin[2] );
qglVertex3f( v3Origin[0]+(-f*f45Len), v3Origin[1]+(f*f45Len), v3Origin[2] );
break;
case XZ:
qglVertex3f( v3Origin[0]+(f*0), v3Origin[1], v3Origin[2]+(f*1) );
qglVertex3f( v3Origin[0]+(f*f45Len), v3Origin[1], v3Origin[2]+(f*f45Len) );
qglVertex3f( v3Origin[0]+(f*1), v3Origin[1], v3Origin[2]+(f*0) );
qglVertex3f( v3Origin[0]+(f*f45Len), v3Origin[1], v3Origin[2]+(-f*f45Len) );
qglVertex3f( v3Origin[0]+(f*0), v3Origin[1], v3Origin[2]+(-f*1) );
qglVertex3f( v3Origin[0]+(-f*f45Len), v3Origin[1], v3Origin[2]+(-f*f45Len) );
qglVertex3f( v3Origin[0]+(-f*1), v3Origin[1], v3Origin[2]+(f*0) );
qglVertex3f( v3Origin[0]+(-f*f45Len), v3Origin[1], v3Origin[2]+(f*f45Len) );
break;
case YZ:
qglVertex3f( v3Origin[0], v3Origin[1]+(f*0), v3Origin[2]+(f*1) );
qglVertex3f( v3Origin[0], v3Origin[1]+(f*f45Len), v3Origin[2]+(f*f45Len) );
qglVertex3f( v3Origin[0], v3Origin[1]+(f*1), v3Origin[2]+(f*0) );
qglVertex3f( v3Origin[0], v3Origin[1]+(f*f45Len), v3Origin[2]+(-f*f45Len) );
qglVertex3f( v3Origin[0], v3Origin[1]+(f*0), v3Origin[2]+(-f*1) );
qglVertex3f( v3Origin[0], v3Origin[1]+(-f*f45Len), v3Origin[2]+(-f*f45Len) );
qglVertex3f( v3Origin[0], v3Origin[1]+(-f*1), v3Origin[2]+(f*0) );
qglVertex3f( v3Origin[0], v3Origin[1]+(-f*f45Len), v3Origin[2]+(f*f45Len) );
break;
}
}
}
qglEnd();
}
qglDisable(GL_LINE_STIPPLE);
}
return;
}
else if (strnicmp(b->owner->eclass->name, "misc_model",10) == 0)
{
if (PaintedModel(b, false, bIsGhost))
return;
}
}
for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++)
{
// only draw polygons facing in a direction we care about
if (nViewType == XY)
{
if (face->plane.normal[2] <= 0)
continue;
}
else
{
if (nViewType == XZ)
{
if (face->plane.normal[1] <= 0)
continue;
}
else
{
if (face->plane.normal[0] <= 0)
continue;
}
}
w = face->face_winding;
if (!w)
continue;
//if (b->alphaBrush && !(face->texdef.flags & SURF_ALPHA))
// continue;
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// draw the polygon
qglBegin(GL_LINE_LOOP);
for (i=0 ; i<w->numpoints ; i++)
qglVertex3fv(w->points[i]);
qglEnd();
// draw angle arrows, but only if viewing from top down, and it's an ent brush with an "angle" field...
//
if (nViewType == XY && b->owner && (b->owner != world_entity) && strlen(ValueForKey(b->owner,"angle")))
{
// draw directional arrow...
//
// (work out size first)
//
float fMaxX = MIN_WORLD_COORD;
float fMinX = MAX_WORLD_COORD;
float fMaxY = MIN_WORLD_COORD;
float fMinY = MAX_WORLD_COORD;
float fMaxZ = MIN_WORLD_COORD;
float fMinZ = MAX_WORLD_COORD;
for (i=0 ; i<w->numpoints ; i++)
{
fMaxX = max(fMaxX, w->points[i][0]);
fMinX = min(fMinX, w->points[i][0]);
fMaxY = max(fMaxY, w->points[i][1]);
fMinY = min(fMinY, w->points[i][1]);
fMaxZ = max(fMaxZ, w->points[i][2]); // Z stuff not really important, but useful for coords later so WTF?
fMinZ = min(fMinZ, w->points[i][2]);
}
float fSmallest= min( (fMaxX-fMinX) , (fMaxY-fMinY) ); // do NOT use Z for smallest calc!
fSmallest*= 0.8f;
vec3_t v3Centre =
{
fMinX + ((fMaxX-fMinX)/2),
fMinY + ((fMaxY-fMinY)/2),
fMinZ + ((fMaxZ-fMinZ)/2),
};
vec3_t v3Top;
VectorCopy(v3Centre,v3Top);
v3Top[1] += fSmallest/2;
vec3_t v3LTArrowEdge = {v3Centre[0] - fSmallest/4, v3Centre[1] - fSmallest/4, v3Top[2]};
vec3_t v3RTArrowEdge = {v3Centre[0] + fSmallest/4, v3Centre[1] - fSmallest/4, v3Top[2]};
vec3_t v3Angle = {0,0, FloatForKey (b->owner, "angle")-90};
VectorRotate(v3Top, v3Angle, v3Centre, v3Top);
VectorRotate(v3LTArrowEdge, v3Angle, v3Centre, v3LTArrowEdge);
VectorRotate(v3RTArrowEdge, v3Angle, v3Centre, v3RTArrowEdge);
qglBegin(GL_LINE_LOOP);
qglVertex3fv(v3Top);
qglVertex3fv(v3LTArrowEdge);
qglVertex3fv(v3Centre);
qglVertex3fv(v3RTArrowEdge);
qglEnd();
}
}
DrawBrushEntityName (b);
}
face_t *Face_Alloc( void )
{
face_t *f = (face_t*)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 );
}
void Clamp(float& f, int nClamp)
{
float fFrac = f - static_cast<int>(f);
f = static_cast<int>(f) % nClamp;
f += fFrac;
}
void Face_MoveTexture(face_t *f, vec3_t delta)
{
vec3_t vX, vY;
TextureAxisFromPlane(&f->plane, vX, vY);
vec3_t vDP, vShift;
vDP[0] = DotProduct(delta, vX);
vDP[1] = DotProduct(delta, vY);
double fAngle = f->texdef.rotate / 180 * Q_PI;
double c = cos(fAngle);
double s = sin(fAngle);
vShift[0] = vDP[0] * c - vDP[1] * s;
vShift[1] = vDP[0] * s + vDP[1] * c;
if (!f->texdef.scale[0])
f->texdef.scale[0] = 1; // fTEXTURE_SCALE?
if (!f->texdef.scale[1])
f->texdef.scale[1] = 1; // fTEXTURE_SCALE?
f->texdef.shift[0] -= vShift[0] / f->texdef.scale[0];
f->texdef.shift[1] -= vShift[1] / f->texdef.scale[1];
// clamp the shifts
//f->texdef.shift[0] = static_cast<int>(f->texdef.shift[0]) % f->d_texture->width;
//f->texdef.shift[1] = static_cast<int>(f->texdef.shift[1]) % f->d_texture->height;
Clamp(f->texdef.shift[0], f->d_texture->width);
Clamp(f->texdef.shift[1], f->d_texture->height);
}
/*
============
Brush_Move
============
*/
void Brush_Move (brush_t *b, const vec3_t move, bool bSnap)
{
int i;
face_t *f;
for (f=b->brush_faces ; f ; f=f->next)
{
vec3_t vTemp;
VectorCopy(move, vTemp);
if (g_PrefsDlg.m_bTextureLock)
Face_MoveTexture(f, vTemp);
for (i=0 ; i<3 ; i++)
VectorAdd (f->planepts[i], move, f->planepts[i]);
}
Brush_Build( b, bSnap );
if (b->patchBrush)
{
Patch_Move(b->nPatchID, move);
}
// PGM - keep the origin vector up to date on fixed size entities.
if(b->owner->eclass->fixedsize)
{
VectorAdd(b->owner->origin, move, b->owner->origin);
//VectorAdd(b->maxs, b->mins, b->owner->origin);
//VectorScale(b->owner->origin, 0.5, b->owner->origin);
}
}
void Brush_Print(brush_t* b)
{
int nFace = 0;
for (face_t* f = b->brush_faces ; f ; f=f->next)
{
Sys_Printf("Face %i\n", nFace++);
Sys_Printf("%f %f %f\n", f->planepts[0][0], f->planepts[0][1], f->planepts[0][2]);
Sys_Printf("%f %f %f\n", f->planepts[1][0], f->planepts[1][1], f->planepts[1][2]);
Sys_Printf("%f %f %f\n", f->planepts[2][0], f->planepts[2][1], f->planepts[2][2]);
}
}
/*
=============
Brush_MakeSided
Makes the current brushhave the given number of 2d sides and turns it into a cone
=============
*/
void Brush_MakeSidedCone(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 = (brush_t*)qmalloc (sizeof(brush_t));
// 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] = mid[0];
f->planepts[1][1] = mid[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
Makes the current brushhave the given number of 2d sides and turns it into a sphere
=============
*/
void Brush_MakeSidedSphere(int sides)
{
int i,j;
vec3_t mins, maxs;
brush_t *b;
texdef_t *texdef;
face_t *f;
vec3_t mid;
if (sides < 4)
{
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
float radius = 8;
for (i=0 ; i<2 ; i++)
{
mid[i] = (maxs[i] + mins[i])*0.5;
if (maxs[i] - mins[i] > radius)
radius = maxs[i] - mins[i];
}
radius /= 2;
b = (brush_t*)qmalloc (sizeof(brush_t));
float dt = float(2 * Q_PI / sides);
float dp = float(Q_PI / sides);
float t,p;
for(i=0; i <= sides-1; i++)
{
for(j=0;j <= sides-2; j++)
{
t = i * dt;
p = float(j * dp - Q_PI / 2);
f = Face_Alloc();
f->texdef = *texdef;
f->next = b->brush_faces;
b->brush_faces = f;
VectorPolar(f->planepts[0], radius, t, p);
VectorPolar(f->planepts[1], radius, t, p + dp);
VectorPolar(f->planepts[2], radius, t + dt, p + dp);
for (int k = 0; k < 3; k++)
VectorAdd(f->planepts[k], mid, f->planepts[k]);
}
}
p = float((sides - 1) * dp - Q_PI / 2);
for(i = 0; i <= sides-1; i++)
{
t = i * dt;
f = Face_Alloc();
f->texdef = *texdef;
f->next = b->brush_faces;
b->brush_faces = f;
VectorPolar(f->planepts[0], radius, t, p);
VectorPolar(f->planepts[1], radius, t + dt, p + dp);
VectorPolar(f->planepts[2], radius, t + dt, p);
for (int k = 0; k < 3; k++)
VectorAdd(f->planepts[k], mid, f->planepts[k]);
}
Brush_AddToList (b, &selected_brushes);
Entity_LinkBrush (world_entity, b);
Brush_Build( b );
Sys_UpdateWindows (W_ALL);
}
void Brush_FitTexture( brush_t *b, int nHeight, int nWidth )
{
face_t *face;
for (face = b->brush_faces ; face ; face=face->next)
{
Face_FitTexture( face, nHeight, nWidth );
}
}
void ClearBounds (vec3_t mins, vec3_t maxs)
{
mins[0] = mins[1] = mins[2] = MAX_WORLD_COORD;
maxs[0] = maxs[1] = maxs[2] = MIN_WORLD_COORD;
}
void AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs)
{
int i;
vec_t val;
for (i=0 ; i<3 ; i++)
{
val = v[i];
if (val < mins[i])
mins[i] = val;
if (val > maxs[i])
maxs[i] = val;
}
}
void Face_FitTexture( face_t * face, int nHeight, int nWidth )
{
winding_t *w;
vec3_t mins,maxs;
int i;
float width, height, temp;
float rot_width, rot_height;
float cosv,sinv,ang;
float min_t, min_s, max_t, max_s;
float s,t;
vec3_t vecs[2];
vec3_t coords[4];
texdef_t *td;
if (nHeight < 1)
{
nHeight = 1;
}
if (nWidth < 1)
{
nWidth = 1;
}
ClearBounds (mins, maxs);
td = &face->texdef;
w = face->face_winding;
if (!w)
{
return;
}
for (i=0 ; i<w->numpoints ; i++)
{
AddPointToBounds( w->points[i], mins, maxs );
}
//fitcode
//
// get the current angle
//
// hack, swap sides if 90 or 270 degrees, fixes a non-fit bug...
//
if (fabs(td->rotate - 90) < 0.001f || fabs(td->rotate - 270) < 0.001f)
{
int iTemp = nHeight;
nHeight = nWidth;
nWidth = iTemp;
}
ang = td->rotate / 180 * Q_PI;
sinv = sin(ang);
cosv = cos(ang);
// get natural texture axis
TextureAxisFromPlane(&face->plane, vecs[0], vecs[1]);
min_s = DotProduct( mins, vecs[0] );
min_t = DotProduct( mins, vecs[1] );
max_s = DotProduct( maxs, vecs[0] );
max_t = DotProduct( maxs, vecs[1] );
width = max_s - min_s;
height = max_t - min_t;
coords[0][0] = min_s;
coords[0][1] = min_t;
coords[1][0] = max_s;
coords[1][1] = min_t;
coords[2][0] = min_s;
coords[2][1] = max_t;
coords[3][0] = max_s;
coords[3][1] = max_t;
min_s = min_t = MAX_WORLD_COORD;
max_s = max_t = MIN_WORLD_COORD;
for (i=0; i<4; i++)
{
s = cosv * coords[i][0] - sinv * coords[i][1];
t = sinv * coords[i][0] + cosv * coords[i][1];
////////////
if (s > max_s)
{
max_s = s;
}
if (s < min_s)
{
min_s = s;
}
if (t < min_t)
{
min_t = t;
}
if (t > max_t)
{
max_t = t;
}
////////////
if (i&1)
{
if (s > max_s)
{
max_s = s;
}
}
else
{
if (s < min_s)
{
min_s = s;
}
if (i<2)
{
if (t < min_t)
{
min_t = t;
}
}
else
{
if (t > max_t)
{
max_t = t;
}
}
}
}
rot_width = (max_s - min_s);
rot_height = (max_t - min_t);
td->scale[0] = (rot_width/((float)(face->d_texture->width*nWidth)));
td->scale[1] = (rot_height/((float)(face->d_texture->height*nHeight)));
td->shift[0] = min_s/td->scale[0];
temp = (int)(td->shift[0] / (face->d_texture->width*nWidth));
temp = (temp+1)*face->d_texture->width*nWidth;
td->shift[0] = (int)(temp - td->shift[0])%(face->d_texture->width*nWidth);
td->shift[1] = min_t/td->scale[1];
temp = (int)(td->shift[1] / (face->d_texture->height*nHeight));
temp = (temp+1)*(face->d_texture->height*nHeight);
td->shift[1] = (int)(temp - td->shift[1])%(face->d_texture->height*nHeight);
}