fteqw/engine/common/q1bsp.c

2004 lines
48 KiB
C
Raw Normal View History

#include "quakedef.h"
#include "pr_common.h"
qboolean Heightmap_Trace(model_t *model, int forcehullnum, int frame, vec3_t axis[3], vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, unsigned int contentmask, trace_t *trace);
/*
============================================================================
Physics functions (common)
*/
void Q1BSP_CheckHullNodes(hull_t *hull)
{
int num, c;
mclipnode_t *node;
for (num = hull->firstclipnode; num < hull->lastclipnode; num++)
{
node = hull->clipnodes + num;
for (c = 0; c < 2; c++)
if (node->children[c] >= 0)
if (node->children[c] < hull->firstclipnode || node->children[c] > hull->lastclipnode)
Sys_Error ("Q1BSP_CheckHull: bad node number");
}
}
/*
==================
SV_HullPointContents
==================
*/
static int Q1_HullPointContents (hull_t *hull, int num, vec3_t p)
{
float d;
mclipnode_t *node;
mplane_t *plane;
while (num >= 0)
{
node = hull->clipnodes + num;
plane = hull->planes + node->planenum;
if (plane->type < 3)
d = p[plane->type] - plane->dist;
else
d = DotProduct (plane->normal, p) - plane->dist;
if (d < 0)
num = node->children[1];
else
num = node->children[0];
}
return num;
}
static int Q1_ModelPointContents (mnode_t *node, vec3_t p)
{
float d;
mplane_t *plane;
while(node->contents >= 0)
{
plane = node->plane;
if (plane->type < 3)
d = p[plane->type] - plane->dist;
else
d = DotProduct(plane->normal, p) - plane->dist;
node = node->children[d<0];
}
return node->contents;
}
#define DIST_EPSILON (0.03125)
#if 0
enum
{
rht_solid,
rht_empty,
rht_impact
};
vec3_t rht_start, rht_end;
static int Q1BSP_RecursiveHullTrace (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
{
mclipnode_t *node;
mplane_t *plane;
float t1, t2;
vec3_t mid;
int side;
float midf;
int rht;
reenter:
if (num < 0)
{
/*hit a leaf*/
if (num == Q1CONTENTS_SOLID)
{
if (trace->allsolid)
trace->startsolid = true;
return rht_solid;
}
else
{
trace->allsolid = false;
if (num == Q1CONTENTS_EMPTY)
trace->inopen = true;
else
trace->inwater = true;
return rht_empty;
}
}
/*its a node*/
/*get the node info*/
node = hull->clipnodes + num;
plane = hull->planes + node->planenum;
if (plane->type < 3)
{
t1 = p1[plane->type] - plane->dist;
t2 = p2[plane->type] - plane->dist;
}
else
{
t1 = DotProduct (plane->normal, p1) - plane->dist;
t2 = DotProduct (plane->normal, p2) - plane->dist;
}
/*if its completely on one side, resume on that side*/
if (t1 >= 0 && t2 >= 0)
{
//return Q1BSP_RecursiveHullTrace (hull, node->children[0], p1f, p2f, p1, p2, trace);
num = node->children[0];
goto reenter;
}
if (t1 < 0 && t2 < 0)
{
//return Q1BSP_RecursiveHullTrace (hull, node->children[1], p1f, p2f, p1, p2, trace);
num = node->children[1];
goto reenter;
}
if (plane->type < 3)
{
t1 = rht_start[plane->type] - plane->dist;
t2 = rht_end[plane->type] - plane->dist;
}
else
{
t1 = DotProduct (plane->normal, rht_start) - plane->dist;
t2 = DotProduct (plane->normal, rht_end) - plane->dist;
}
side = t1 < 0;
midf = t1 / (t1 - t2);
if (midf < p1f) midf = p1f;
if (midf > p2f) midf = p2f;
VectorInterpolate(rht_start, midf, rht_end, mid);
rht = Q1BSP_RecursiveHullTrace(hull, node->children[side], p1f, midf, p1, mid, trace);
if (rht != rht_empty)
return rht;
rht = Q1BSP_RecursiveHullTrace(hull, node->children[side^1], midf, p2f, mid, p2, trace);
if (rht != rht_solid)
return rht;
if (side)
{
/*we impacted the back of the node, so flip the plane*/
trace->plane.dist = -plane->dist;
VectorNegate(plane->normal, trace->plane.normal);
midf = (t1 + DIST_EPSILON) / (t1 - t2);
}
else
{
/*we impacted the front of the node*/
trace->plane.dist = plane->dist;
VectorCopy(plane->normal, trace->plane.normal);
midf = (t1 - DIST_EPSILON) / (t1 - t2);
}
t1 = DotProduct (trace->plane.normal, rht_start) - trace->plane.dist;
t2 = DotProduct (trace->plane.normal, rht_end) - trace->plane.dist;
midf = (t1 - DIST_EPSILON) / (t1 - t2);
trace->fraction = midf;
VectorCopy (mid, trace->endpos);
VectorInterpolate(rht_start, midf, rht_end, trace->endpos);
return rht_impact;
}
qboolean Q1BSP_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
{
if (VectorEquals(p1, p2))
{
/*points cannot cross planes, so do it faster*/
switch(Q1_HullPointContents(hull, num, p1))
{
case Q1CONTENTS_SOLID:
trace->startsolid = true;
break;
case Q1CONTENTS_EMPTY:
trace->allsolid = false;
trace->inopen = true;
break;
default:
trace->allsolid = false;
trace->inwater = true;
break;
}
return true;
}
else
{
VectorCopy(p1, rht_start);
VectorCopy(p2, rht_end);
return Q1BSP_RecursiveHullTrace(hull, num, p1f, p2f, p1, p2, trace) != rht_impact;
}
}
#else
qboolean Q1BSP_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
{
mclipnode_t *node;
mplane_t *plane;
float t1, t2;
float frac;
int i;
vec3_t mid;
int side;
float midf;
// check for empty
if (num < 0)
{
if (num != Q1CONTENTS_SOLID)
{
trace->allsolid = false;
if (num == Q1CONTENTS_EMPTY)
trace->inopen = true;
else
trace->inwater = true;
}
else
trace->startsolid = true;
return true; // empty
}
//
// find the point distances
//
node = hull->clipnodes + num;
plane = hull->planes + node->planenum;
if (plane->type < 3)
{
t1 = p1[plane->type] - plane->dist;
t2 = p2[plane->type] - plane->dist;
}
else
{
t1 = DotProduct (plane->normal, p1) - plane->dist;
t2 = DotProduct (plane->normal, p2) - plane->dist;
}
#if 1
if (t1 >= 0 && t2 >= 0)
return Q1BSP_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace);
if (t1 < 0 && t2 < 0)
return Q1BSP_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace);
#else
if ( (t1 >= DIST_EPSILON && t2 >= DIST_EPSILON) || (t2 > t1 && t1 >= 0) )
return Q1BSP_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace);
if ( (t1 <= -DIST_EPSILON && t2 <= -DIST_EPSILON) || (t2 < t1 && t1 <= 0) )
return Q1BSP_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace);
#endif
// put the crosspoint DIST_EPSILON pixels on the near side
if (t1 < 0)
frac = (t1 + DIST_EPSILON)/(t1-t2);
else
frac = (t1 - DIST_EPSILON)/(t1-t2);
if (frac < 0)
frac = 0;
if (frac > 1)
frac = 1;
midf = p1f + (p2f - p1f)*frac;
for (i=0 ; i<3 ; i++)
mid[i] = p1[i] + frac*(p2[i] - p1[i]);
side = (t1 < 0);
// move up to the node
if (!Q1BSP_RecursiveHullCheck (hull, node->children[side], p1f, midf, p1, mid, trace) )
return false;
#ifdef PARANOID
if (Q1BSP_RecursiveHullCheck (sv_hullmodel, mid, node->children[side])
== Q1CONTENTS_SOLID)
{
Con_Printf ("mid PointInHullSolid\n");
return false;
}
#endif
if (Q1_HullPointContents (hull, node->children[side^1], mid)
!= Q1CONTENTS_SOLID)
// go past the node
return Q1BSP_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace);
if (trace->allsolid)
return false; // never got out of the solid area
//==================
// the other side of the node is solid, this is the impact point
//==================
if (!side)
{
VectorCopy (plane->normal, trace->plane.normal);
trace->plane.dist = plane->dist;
}
else
{
VectorNegate (plane->normal, trace->plane.normal);
trace->plane.dist = -plane->dist;
}
while (Q1_HullPointContents (hull, hull->firstclipnode, mid)
== Q1CONTENTS_SOLID)
{ // shouldn't really happen, but does occasionally
if (!(frac < 10000000) && !(frac > -10000000))
{
trace->fraction = 0;
VectorClear (trace->endpos);
Con_Printf ("nan in traceline\n");
return false;
}
frac -= 0.1;
if (frac < 0)
{
trace->fraction = midf;
VectorCopy (mid, trace->endpos);
Con_DPrintf ("backup past 0\n");
return false;
}
midf = p1f + (p2f - p1f)*frac;
for (i=0 ; i<3 ; i++)
mid[i] = p1[i] + frac*(p2[i] - p1[i]);
}
trace->fraction = midf;
VectorCopy (mid, trace->endpos);
return false;
}
#endif
/*
the bsp tree we're walking through is the renderable hull
we need to trace a box through the world.
by its very nature, this will reach more nodes than we really want, and as we can follow a node sideways, the underlying bsp structure is no longer 100% reliable (meaning we cross planes that are entirely to one side, and follow its children too)
so all contents and solidity must come from the brushes and ONLY the brushes.
*/
struct traceinfo_s
{
unsigned int solidcontents;
trace_t trace;
qboolean sphere;
float radius;
/*set even for sphere traces (used for bbox tests)*/
vec3_t mins;
vec3_t maxs;
vec3_t start;
vec3_t end;
};
#if 0
#include "shader.h"
void TestDrawPlane(float *normal, float dist, float r, float g, float b)
{
scenetris_t *t;
if (cl_numstris == cl_maxstris)
{
cl_maxstris+=8;
cl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);
}
t = &cl_stris[cl_numstris++];
t->shader = R_RegisterShader("testplane", "{\n{\nmap $whiteimage\nrgbgen vertex\nalphagen vertex\nblendfunc add\nnodepth\n}\n}\n");
t->firstidx = cl_numstrisidx;
t->firstvert = cl_numstrisvert;
t->numvert = 0;
t->numidx = 0;
if (cl_numstrisidx+6 > cl_maxstrisidx)
{
cl_maxstrisidx=cl_numstrisidx+6 + 64;
cl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);
}
if (cl_numstrisvert+4 > cl_maxstrisvert)
{
cl_maxstrisvert+=64;
cl_strisvertv = BZ_Realloc(cl_strisvertv, sizeof(*cl_strisvertv)*cl_maxstrisvert);
cl_strisvertt = BZ_Realloc(cl_strisvertt, sizeof(*cl_strisvertt)*cl_maxstrisvert);
cl_strisvertc = BZ_Realloc(cl_strisvertc, sizeof(*cl_strisvertc)*cl_maxstrisvert);
}
{
vec3_t tmp = {0,0.04,0.96};
vec3_t right, forward;
CrossProduct(normal, tmp, right);
VectorNormalize(right);
CrossProduct(normal, right, forward);
VectorNormalize(forward);
VectorScale( normal, dist, cl_strisvertv[cl_numstrisvert]);
VectorMA(cl_strisvertv[cl_numstrisvert], 8192, right, cl_strisvertv[cl_numstrisvert]);
VectorMA(cl_strisvertv[cl_numstrisvert], 8192, forward, cl_strisvertv[cl_numstrisvert]);
Vector4Set(cl_strisvertc[cl_numstrisvert], r, g, b, 0.2);
cl_numstrisvert++;
VectorScale( normal, dist, cl_strisvertv[cl_numstrisvert]);
VectorMA(cl_strisvertv[cl_numstrisvert], 8192, right, cl_strisvertv[cl_numstrisvert]);
VectorMA(cl_strisvertv[cl_numstrisvert], -8192, forward, cl_strisvertv[cl_numstrisvert]);
Vector4Set(cl_strisvertc[cl_numstrisvert], r, g, b, 0.2);
cl_numstrisvert++;
VectorScale( normal, dist, cl_strisvertv[cl_numstrisvert]);
VectorMA(cl_strisvertv[cl_numstrisvert], -8192, right, cl_strisvertv[cl_numstrisvert]);
VectorMA(cl_strisvertv[cl_numstrisvert], -8192, forward, cl_strisvertv[cl_numstrisvert]);
Vector4Set(cl_strisvertc[cl_numstrisvert], r, g, b, 0.2);
cl_numstrisvert++;
VectorScale( normal, dist, cl_strisvertv[cl_numstrisvert]);
VectorMA(cl_strisvertv[cl_numstrisvert], -8192, right, cl_strisvertv[cl_numstrisvert]);
VectorMA(cl_strisvertv[cl_numstrisvert], 8192, forward, cl_strisvertv[cl_numstrisvert]);
Vector4Set(cl_strisvertc[cl_numstrisvert], r, g, b, 0.2);
cl_numstrisvert++;
}
/*build the triangles*/
cl_strisidx[cl_numstrisidx++] = t->numvert + 0;
cl_strisidx[cl_numstrisidx++] = t->numvert + 1;
cl_strisidx[cl_numstrisidx++] = t->numvert + 2;
cl_strisidx[cl_numstrisidx++] = t->numvert + 0;
cl_strisidx[cl_numstrisidx++] = t->numvert + 2;
cl_strisidx[cl_numstrisidx++] = t->numvert + 3;
t->numidx = cl_numstrisidx - t->firstidx;
t->numvert += 4;
}
#endif
static void Q1BSP_ClipToBrushes(struct traceinfo_s *traceinfo, mbrush_t *brush)
{
struct mbrushplane_s *plane;
struct mbrushplane_s *enterplane;
int i, j;
vec3_t ofs;
qboolean startout, endout;
float d1,d2,dist,enterdist=0;
float f, enterfrac, exitfrac;
for (; brush; brush = brush->next)
{
/*ignore if its not solid to us*/
if (!(traceinfo->solidcontents & brush->contents))
continue;
startout = false;
endout = false;
enterplane= NULL;
enterfrac = -1;
exitfrac = 10;
for (i = brush->numplanes, plane = brush->planes; i; i--, plane++)
{
/*calculate the distance based upon the shape of the object we're tracing for*/
if (traceinfo->sphere)
{
dist = plane->dist + traceinfo->radius;
}
else
{
for (j=0 ; j<3 ; j++)
{
if (plane->normal[j] < 0)
ofs[j] = traceinfo->maxs[j];
else
ofs[j] = traceinfo->mins[j];
}
dist = DotProduct (ofs, plane->normal);
dist = plane->dist - dist;
}
d1 = DotProduct (traceinfo->start, plane->normal) - dist;
d2 = DotProduct (traceinfo->end, plane->normal) - dist;
if (d1 >= 0)
startout = true;
if (d2 > 0)
endout = true;
//if we're fully outside any plane, then we cannot possibly enter the brush, skip to the next one
if (d1 > 0 && d2 >= 0)
goto nextbrush;
//if we're fully inside the plane, then whatever is happening is not relevent for this plane
if (d1 < 0 && d2 <= 0)
continue;
f = d1 / (d1-d2);
if (d1 > d2)
{
//entered the brush. favour the furthest fraction to avoid extended edges (yay for convex shapes)
if (enterfrac < f)
{
enterfrac = f;
enterplane = plane;
enterdist = dist;
}
}
else
{
//left the brush, favour the nearest plane (smallest frac)
if (exitfrac > f)
{
exitfrac = f;
}
}
}
if (!startout)
{
traceinfo->trace.startsolid = true;
if (!endout)
traceinfo->trace.allsolid = true;
traceinfo->trace.contents |= brush->contents;
return;
}
if (enterfrac != -1 && enterfrac < exitfrac)
{
//impact!
if (enterfrac < traceinfo->trace.fraction)
{
traceinfo->trace.fraction = enterfrac;
traceinfo->trace.plane.dist = enterdist;
VectorCopy(enterplane->normal, traceinfo->trace.plane.normal);
traceinfo->trace.contents = brush->contents;
}
}
nextbrush:
;
}
}
static void Q1BSP_InsertBrush(mnode_t *node, mbrush_t *brush, vec3_t bmins, vec3_t bmaxs)
{
vec3_t near, far;
float nd, fd;
int i;
while(1)
{
if (node->contents < 0) /*leaf, so no smaller node to put it in (I'd be surprised if it got this far)*/
{
brush->next = node->brushes;
node->brushes = brush;
return;
}
for (i = 0; i < 3; i++)
{
if (node->plane->normal[i] > 0)
{
near[i] = bmins[i];
far[i] = bmaxs[i];
}
else
{
near[i] = bmaxs[i];
far[i] = bmins[i];
}
}
nd = DotProduct(node->plane->normal, near) - node->plane->dist;
fd = DotProduct(node->plane->normal, far) - node->plane->dist;
/*if its fully on either side, continue walking*/
if (nd < 0 && fd < 0)
node = node->children[1];
else if (nd > 0 && fd > 0)
node = node->children[0];
else
{
/*plane crosses bbox, so insert here*/
brush->next = node->brushes;
node->brushes = brush;
return;
}
}
}
static void Q1BSP_RecursiveBrushCheck (struct traceinfo_s *traceinfo, mnode_t *node, float p1f, float p2f, vec3_t p1, vec3_t p2)
{
mplane_t *plane;
float t1, t2;
float frac;
int i;
vec3_t mid;
int side;
float midf;
float offset;
if (node->brushes)
{
Q1BSP_ClipToBrushes(traceinfo, node->brushes);
}
if (traceinfo->trace.fraction < p1f)
{
//already hit something closer than this node
return;
}
if (node->contents < 0)
{
//we're in a leaf
return;
}
//
// find the point distances
//
plane = node->plane;
if (plane->type < 3)
{
t1 = p1[plane->type] - plane->dist;
t2 = p2[plane->type] - plane->dist;
if (plane->normal[plane->type] < 0)
offset = -traceinfo->mins[plane->type];
else
offset = traceinfo->maxs[plane->type];
}
else
{
t1 = DotProduct (plane->normal, p1) - plane->dist;
t2 = DotProduct (plane->normal, p2) - plane->dist;
offset = 0;
for (i = 0; i < 3; i++)
{
if (plane->normal[i] < 0)
offset += plane->normal[i] * -traceinfo->mins[i];
else
offset += plane->normal[i] * traceinfo->maxs[i];
}
}
/*if we're fully on one side of the trace, go only down that side*/
if (t1 >= offset && t2 >= offset)
{
Q1BSP_RecursiveBrushCheck (traceinfo, node->children[0], p1f, p2f, p1, p2);
return;
}
if (t1 < -offset && t2 < -offset)
{
Q1BSP_RecursiveBrushCheck (traceinfo, node->children[1], p1f, p2f, p1, p2);
return;
}
// put the crosspoint DIST_EPSILON pixels on the near side
if (t1 < 0)
{
frac = (t1 + DIST_EPSILON)/(t1-t2);
side = 1;
}
else
{
frac = (t1 - DIST_EPSILON)/(t1-t2);
side = 0;
}
if (frac < 0)
frac = 0;
if (frac > 1)
frac = 1;
midf = p1f + (p2f - p1f)*frac;
for (i=0 ; i<3 ; i++)
mid[i] = p1[i] + frac*(p2[i] - p1[i]);
// move up to the node
Q1BSP_RecursiveBrushCheck (traceinfo, node->children[side], p1f, midf, p1, mid);
// go past the node
Q1BSP_RecursiveBrushCheck (traceinfo, node->children[side^1], midf, p2f, mid, p2);
}
static unsigned int Q1BSP_TranslateContents(int contents)
{
switch(contents)
{
case Q1CONTENTS_EMPTY:
return FTECONTENTS_EMPTY;
case Q1CONTENTS_SOLID:
return FTECONTENTS_SOLID;
case Q1CONTENTS_WATER:
return FTECONTENTS_WATER;
case Q1CONTENTS_SLIME:
return FTECONTENTS_SLIME;
case Q1CONTENTS_LAVA:
return FTECONTENTS_LAVA;
case Q1CONTENTS_SKY:
return FTECONTENTS_SKY;
case Q1CONTENTS_LADDER:
return FTECONTENTS_LADDER;
default:
Sys_Error("Q1BSP_TranslateContents: Unknown contents type - %i", contents);
return FTECONTENTS_SOLID;
}
}
int Q1BSP_HullPointContents(hull_t *hull, vec3_t p)
{
return Q1BSP_TranslateContents(Q1_HullPointContents(hull, hull->firstclipnode, p));
}
unsigned int Q1BSP_PointContents(model_t *model, vec3_t axis[3], vec3_t point)
{
if (axis)
{
vec3_t transformed;
transformed[0] = DotProduct(point, axis[0]);
transformed[1] = DotProduct(point, axis[1]);
transformed[2] = DotProduct(point, axis[2]);
return Q1BSP_HullPointContents(&model->hulls[0], transformed);
}
if (!model->firstmodelsurface)
{
return Q1BSP_TranslateContents(Q1_ModelPointContents(model->nodes, point));
}
return Q1BSP_HullPointContents(&model->hulls[0], point);
}
void Q1BSP_LoadBrushes(model_t *model)
{
struct {
unsigned int ver;
unsigned int modelnum;
unsigned int numbrushes;
unsigned int numplanes;
} *permodel;
struct {
float mins[3];
float maxs[3];
signed short contents;
unsigned short numplanes;
} *perbrush;
/*
Note to implementors:
a pointy brush with angles pointier than 90 degrees will extend further than any adjacent brush, thus creating invisible walls with larger expansions.
the engine inserts 6 axial planes acording to the bbox, thus the qbsp need not write any axial planes
note that doing it this way probably isn't good if you want to query textures...
*/
struct {
vec3_t normal;
float dist;
} *perplane;
static vec3_t axis[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
int br, pl, remainingplanes;
mbrush_t *brush;
mnode_t *rootnode;
unsigned int lumpsizeremaining;
model->engineflags &= ~MDLF_HASBRUSHES;
permodel = Q1BSPX_FindLump("BRUSHLIST", &lumpsizeremaining);
if (!permodel)
return;
while (lumpsizeremaining)
{
if (lumpsizeremaining < sizeof(*permodel))
return;
permodel->ver = LittleLong(permodel->ver);
permodel->modelnum = LittleLong(permodel->modelnum);
permodel->numbrushes = LittleLong(permodel->numbrushes);
permodel->numplanes = LittleLong(permodel->numplanes);
if (permodel->ver != 1 || lumpsizeremaining < sizeof(*permodel) + permodel->numbrushes*sizeof(*perbrush) + permodel->numplanes*sizeof(*perplane))
return;
//find the correct rootnode for the model
rootnode = model->nodes;
if (permodel->modelnum > model->numsubmodels)
return;
if (permodel->modelnum)
rootnode += model->submodels[permodel->modelnum-1].headnode[0];
brush = Hunk_Alloc((sizeof(*brush) - sizeof(brush->planes[0]))*permodel->numbrushes + sizeof(brush->planes[0])*(permodel->numbrushes*6+permodel->numplanes));
remainingplanes = permodel->numplanes;
perbrush = (void*)(permodel+1);
for (br = 0; br < permodel->numbrushes; br++)
{
/*byteswap it all in place*/
perbrush->mins[0] = LittleFloat(perbrush->mins[0]);
perbrush->mins[1] = LittleFloat(perbrush->mins[1]);
perbrush->mins[2] = LittleFloat(perbrush->mins[2]);
perbrush->maxs[0] = LittleFloat(perbrush->maxs[0]);
perbrush->maxs[1] = LittleFloat(perbrush->maxs[1]);
perbrush->maxs[2] = LittleFloat(perbrush->maxs[2]);
perbrush->contents = LittleShort(perbrush->contents);
perbrush->numplanes = LittleShort(perbrush->numplanes);
/*make sure planes don't overflow*/
if (perbrush->numplanes > remainingplanes)
return;
remainingplanes-=perbrush->numplanes;
/*set up the mbrush from the file*/
brush->contents = Q1BSP_TranslateContents(perbrush->contents);
brush->numplanes = perbrush->numplanes;
for (pl = 0, perplane = (void*)(perbrush+1); pl < perbrush->numplanes; pl++, perplane++)
{
brush->planes[pl].normal[0] = LittleFloat(perplane->normal[0]);
brush->planes[pl].normal[1] = LittleFloat(perplane->normal[1]);
brush->planes[pl].normal[2] = LittleFloat(perplane->normal[2]);
brush->planes[pl].dist = LittleFloat(perplane->dist);
}
/*and add axial planes acording to the brush's bbox*/
for (pl = 0; pl < 3; pl++)
{
VectorCopy(axis[pl], brush->planes[brush->numplanes].normal);
brush->planes[brush->numplanes].dist = perbrush->maxs[pl];
brush->numplanes++;
}
for (pl = 0; pl < 3; pl++)
{
VectorNegate(axis[pl], brush->planes[brush->numplanes].normal);
brush->planes[brush->numplanes].dist = -perbrush->mins[pl];
brush->numplanes++;
}
/*link it in to the bsp tree*/
Q1BSP_InsertBrush(rootnode, brush, perbrush->mins, perbrush->maxs);
/*set up for the next brush*/
brush = (void*)&brush->planes[brush->numplanes];
perbrush = (void*)perplane;
}
/*move on to the next model*/
lumpsizeremaining -= sizeof(*permodel) + permodel->numbrushes*sizeof(*perbrush) + permodel->numplanes*sizeof(*perplane);
permodel = (void*)((char*)permodel + sizeof(*permodel) + permodel->numbrushes*sizeof(*perbrush) + permodel->numplanes*sizeof(*perplane));
}
/*parsing was successful! flag it as okay*/
model->engineflags |= MDLF_HASBRUSHES;
}
qboolean Q1BSP_Trace(model_t *model, int forcehullnum, int frame, vec3_t axis[3], vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, unsigned int hitcontentsmask, trace_t *trace)
{
hull_t *hull;
vec3_t size;
vec3_t start_l, end_l;
vec3_t offset;
VectorSubtract (maxs, mins, size);
if ((model->engineflags & MDLF_HASBRUSHES))// && (size[0] || size[1] || size[2]))
{
struct traceinfo_s traceinfo;
memset (&traceinfo.trace, 0, sizeof(trace_t));
traceinfo.trace.fraction = 1;
traceinfo.trace.allsolid = false;
VectorCopy(mins, traceinfo.mins);
VectorCopy(maxs, traceinfo.maxs);
VectorCopy(start, traceinfo.start);
VectorCopy(end, traceinfo.end);
traceinfo.sphere = false;
/* traceinfo.sphere = true;
traceinfo.radius = 48;
traceinfo.mins[0] = -traceinfo.radius;
traceinfo.mins[1] = -traceinfo.radius;
traceinfo.mins[2] = -traceinfo.radius;
traceinfo.maxs[0] = traceinfo.radius;
traceinfo.maxs[1] = traceinfo.radius;
traceinfo.maxs[2] = traceinfo.radius;
*/
traceinfo.solidcontents = hitcontentsmask;
Q1BSP_RecursiveBrushCheck(&traceinfo, model->nodes, 0, 1, start, end);
memcpy(trace, &traceinfo.trace, sizeof(trace_t));
if (trace->fraction < 1)
{
float d1 = DotProduct(start, trace->plane.normal) - trace->plane.dist;
float d2 = DotProduct(end, trace->plane.normal) - trace->plane.dist;
float f = (d1 - DIST_EPSILON) / (d1 - d2);
if (f < 0)
f = 0;
trace->fraction = f;
}
VectorInterpolate(start, trace->fraction, end, trace->endpos);
return trace->fraction != 1;
}
memset (trace, 0, sizeof(trace_t));
trace->fraction = 1;
trace->allsolid = true;
if (forcehullnum >= 1 && forcehullnum <= MAX_MAP_HULLSM && model->hulls[forcehullnum-1].available)
hull = &model->hulls[forcehullnum-1];
else
{
if (model->hulls[5].available)
{ //choose based on hexen2 sizes.
if (size[0] < 3) // Point
hull = &model->hulls[0];
else if (size[0] <= 8.1 && model->hulls[4].available)
hull = &model->hulls[4]; //Pentacles
else if (size[0] <= 32.1 && size[2] <= 28.1) // Half Player
hull = &model->hulls[3];
else if (size[0] <= 32.1) // Full Player
hull = &model->hulls[1];
else // Golumn
hull = &model->hulls[5];
}
else
{
if (size[0] < 3 || !model->hulls[1].available)
hull = &model->hulls[0];
else if (size[0] <= 32.1)
{
if (size[2] < 54.1 && model->hulls[3].available)
hull = &model->hulls[3]; // 32x32x36 (half-life's crouch)
else
hull = &model->hulls[1];
}
else
hull = &model->hulls[2];
}
}
// calculate an offset value to center the origin
VectorSubtract (hull->clip_mins, mins, offset);
if (axis)
{
vec3_t tmp;
VectorSubtract(start, offset, tmp);
start_l[0] = DotProduct(tmp, axis[0]);
start_l[1] = DotProduct(tmp, axis[1]);
start_l[2] = DotProduct(tmp, axis[2]);
VectorSubtract(end, offset, tmp);
end_l[0] = DotProduct(tmp, axis[0]);
end_l[1] = DotProduct(tmp, axis[1]);
end_l[2] = DotProduct(tmp, axis[2]);
Q1BSP_RecursiveHullCheck(hull, hull->firstclipnode, 0, 1, start_l, end_l, trace);
if (trace->fraction == 1)
{
VectorCopy (end, trace->endpos);
}
else
{
vec3_t iaxis[3];
vec3_t norm;
Matrix3x3_RM_Invert_Simple((void *)axis, iaxis);
VectorCopy(trace->plane.normal, norm);
trace->plane.normal[0] = DotProduct(norm, iaxis[0]);
trace->plane.normal[1] = DotProduct(norm, iaxis[1]);
trace->plane.normal[2] = DotProduct(norm, iaxis[2]);
/*just interpolate it, its easier than inverse matrix rotations*/
VectorInterpolate(start, trace->fraction, end, trace->endpos);
}
}
else
{
VectorSubtract(start, offset, start_l);
VectorSubtract(end, offset, end_l);
Q1BSP_RecursiveHullCheck(hull, hull->firstclipnode, 0, 1, start_l, end_l, trace);
if (trace->fraction == 1)
{
VectorCopy (end, trace->endpos);
}
else
{
VectorAdd (trace->endpos, offset, trace->endpos);
}
}
#ifdef TERRAIN
if (model->terrain && trace->fraction)
{
trace_t hmt;
Heightmap_Trace(model, forcehullnum, frame, axis, start, end, mins, maxs, hitcontentsmask, &hmt);
if (hmt.fraction < trace->fraction)
*trace = hmt;
}
#endif
return trace->fraction != 1;
}
/*
Physics functions (common)
============================================================================
Rendering functions (Client only)
*/
#ifndef SERVERONLY
extern int r_dlightframecount;
//goes through the nodes marking the surfaces near the dynamic light as lit.
void Q1BSP_MarkLights (dlight_t *light, int bit, mnode_t *node)
{
mplane_t *splitplane;
float dist;
msurface_t *surf;
int i;
float l, maxdist;
int j, s, t;
vec3_t impact;
if (node->contents < 0)
return;
splitplane = node->plane;
if (splitplane->type < 3)
dist = light->origin[splitplane->type] - splitplane->dist;
else
dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist;
if (dist > light->radius)
{
Q1BSP_MarkLights (light, bit, node->children[0]);
return;
}
if (dist < -light->radius)
{
Q1BSP_MarkLights (light, bit, node->children[1]);
return;
}
maxdist = light->radius*light->radius;
// mark the polygons
surf = currentmodel->surfaces + node->firstsurface;
for (i=0 ; i<node->numsurfaces ; i++, surf++)
{
//Yeah, you can blame LordHavoc for this alternate code here.
for (j=0 ; j<3 ; j++)
impact[j] = light->origin[j] - surf->plane->normal[j]*dist;
// clamp center of light to corner and check brightness
l = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0];
s = l+0.5;if (s < 0) s = 0;else if (s > surf->extents[0]) s = surf->extents[0];
s = l - s;
l = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1];
t = l+0.5;if (t < 0) t = 0;else if (t > surf->extents[1]) t = surf->extents[1];
t = l - t;
// compare to minimum light
if ((s*s+t*t+dist*dist) < maxdist)
{
if (surf->dlightframe != r_dlightframecount)
{
surf->dlightbits = bit;
surf->dlightframe = r_dlightframecount;
}
else
surf->dlightbits |= bit;
}
}
Q1BSP_MarkLights (light, bit, node->children[0]);
Q1BSP_MarkLights (light, bit, node->children[1]);
}
#define MAXFRAGMENTTRIS 256
vec3_t decalfragmentverts[MAXFRAGMENTTRIS*3];
typedef struct {
vec3_t center;
vec3_t normal;
// vec3_t tangent1;
// vec3_t tangent2;
vec3_t planenorm[6];
float planedist[6];
int numplanes;
vec_t radius;
int numtris;
} fragmentdecal_t;
//#define SHOWCLIPS
//#define FRAGMENTASTRIANGLES //works, but produces more fragments.
#ifdef FRAGMENTASTRIANGLES
//if the triangle is clipped away, go recursive if there are tris left.
void Fragment_ClipTriToPlane(int trinum, float *plane, float planedist, fragmentdecal_t *dec)
{
float *point[3];
float dotv[3];
vec3_t impact1, impact2;
float t;
int i, i2, i3;
int clippedverts = 0;
for (i = 0; i < 3; i++)
{
point[i] = decalfragmentverts[trinum*3+i];
dotv[i] = DotProduct(point[i], plane)-planedist;
clippedverts += dotv[i] < 0;
}
//if they're all clipped away, scrap the tri
switch (clippedverts)
{
case 0:
return; //plane does not clip the triangle.
case 1: //split into 3, disregard the clipped vert
for (i = 0; i < 3; i++)
{
if (dotv[i] < 0)
{ //This is the vertex that's getting clipped.
if (dotv[i] > -DIST_EPSILON)
return; //it's only over the line by a tiny ammount.
i2 = (i+1)%3;
i3 = (i+2)%3;
if (dotv[i2] < DIST_EPSILON)
return;
if (dotv[i3] < DIST_EPSILON)
return;
//work out where the two lines impact the plane
t = (dotv[i]) / (dotv[i]-dotv[i2]);
VectorInterpolate(point[i], t, point[i2], impact1);
t = (dotv[i]) / (dotv[i]-dotv[i3]);
VectorInterpolate(point[i], t, point[i3], impact2);
#ifdef SHOWCLIPS
if (dec->numtris != MAXFRAGMENTTRIS)
{
VectorCopy(impact2, decalfragmentverts[dec->numtris*3+0]);
VectorCopy(decalfragmentverts[trinum*3+i], decalfragmentverts[dec->numtris*3+1]);
VectorCopy(impact1, decalfragmentverts[dec->numtris*3+2]);
dec->numtris++;
}
#endif
//shrink the tri, putting the impact into the killed vertex.
VectorCopy(impact2, point[i]);
if (dec->numtris == MAXFRAGMENTTRIS)
return; //:(
//build the second tri
VectorCopy(impact1, decalfragmentverts[dec->numtris*3+0]);
VectorCopy(decalfragmentverts[trinum*3+i2], decalfragmentverts[dec->numtris*3+1]);
VectorCopy(impact2, decalfragmentverts[dec->numtris*3+2]);
dec->numtris++;
return;
}
}
Sys_Error("Fragment_ClipTriToPlane: Clipped vertex not founc\n");
return; //can't handle it
case 2: //split into 3, disregarding both the clipped.
for (i = 0; i < 3; i++)
{
if (!(dotv[i] < 0))
{ //This is the vertex that's staying.
if (dotv[i] < DIST_EPSILON)
break; //only just inside
i2 = (i+1)%3;
i3 = (i+2)%3;
//work out where the two lines impact the plane
t = (dotv[i]) / (dotv[i]-dotv[i2]);
VectorInterpolate(point[i], t, point[i2], impact1);
t = (dotv[i]) / (dotv[i]-dotv[i3]);
VectorInterpolate(point[i], t, point[i3], impact2);
//shrink the tri, putting the impact into the killed vertex.
#ifdef SHOWCLIPS
if (dec->numtris != MAXFRAGMENTTRIS)
{
VectorCopy(impact1, decalfragmentverts[dec->numtris*3+0]);
VectorCopy(point[i2], decalfragmentverts[dec->numtris*3+1]);
VectorCopy(point[i3], decalfragmentverts[dec->numtris*3+2]);
dec->numtris++;
}
if (dec->numtris != MAXFRAGMENTTRIS)
{
VectorCopy(impact1, decalfragmentverts[dec->numtris*3+0]);
VectorCopy(point[i3], decalfragmentverts[dec->numtris*3+1]);
VectorCopy(impact2, decalfragmentverts[dec->numtris*3+2]);
dec->numtris++;
}
#endif
VectorCopy(impact1, point[i2]);
VectorCopy(impact2, point[i3]);
return;
}
}
case 3://scrap it
//fill the verts with the verts of the last and go recursive (due to the nature of Fragment_ClipTriangle, which doesn't actually know if we clip them away)
#ifndef SHOWCLIPS
dec->numtris--;
VectorCopy(decalfragmentverts[dec->numtris*3+0], decalfragmentverts[trinum*3+0]);
VectorCopy(decalfragmentverts[dec->numtris*3+1], decalfragmentverts[trinum*3+1]);
VectorCopy(decalfragmentverts[dec->numtris*3+2], decalfragmentverts[trinum*3+2]);
if (trinum < dec->numtris)
Fragment_ClipTriToPlane(trinum, plane, planedist, dec);
#endif
return;
}
}
void Fragment_ClipTriangle(fragmentdecal_t *dec, float *a, float *b, float *c)
{
//emit the triangle, and clip it's fragments.
int start, i;
int p;
if (dec->numtris == MAXFRAGMENTTRIS)
return; //:(
start = dec->numtris;
VectorCopy(a, decalfragmentverts[dec->numtris*3+0]);
VectorCopy(b, decalfragmentverts[dec->numtris*3+1]);
VectorCopy(c, decalfragmentverts[dec->numtris*3+2]);
dec->numtris++;
//clip all the fragments to all of the planes.
//This will produce a quad if the source triangle was big enough.
for (p = 0; p < 6; p++)
{
for (i = start; i < dec->numtris; i++)
Fragment_ClipTriToPlane(i, dec->planenorm[p], dec->plantdist[p], dec);
}
}
#else
#define MAXFRAGMENTVERTS 360
int Fragment_ClipPolyToPlane(float *inverts, float *outverts, int incount, float *plane, float planedist)
{
#define C 4
float dotv[MAXFRAGMENTVERTS+1];
char keep[MAXFRAGMENTVERTS+1];
#define KEEP_KILL 0
#define KEEP_KEEP 1
#define KEEP_BORDER 2
int i;
int outcount = 0;
int clippedcount = 0;
float d, *p1, *p2, *out;
#define FRAG_EPSILON 0.5
for (i = 0; i < incount; i++)
{
dotv[i] = DotProduct((inverts+i*C), plane) - planedist;
if (dotv[i]<-FRAG_EPSILON)
{
keep[i] = KEEP_KILL;
clippedcount++;
}
else if (dotv[i] > FRAG_EPSILON)
keep[i] = KEEP_KEEP;
else
keep[i] = KEEP_BORDER;
}
dotv[i] = dotv[0];
keep[i] = keep[0];
if (clippedcount == incount)
return 0; //all were clipped
if (clippedcount == 0)
{ //none were clipped
for (i = 0; i < incount; i++)
VectorCopy((inverts+i*C), (outverts+i*C));
return incount;
}
for (i = 0; i < incount; i++)
{
p1 = inverts+i*C;
if (keep[i] == KEEP_BORDER)
{
out = outverts+outcount++*C;
VectorCopy(p1, out);
continue;
}
if (keep[i] == KEEP_KEEP)
{
out = outverts+outcount++*C;
VectorCopy(p1, out);
}
if (keep[i+1] == KEEP_BORDER || keep[i] == keep[i+1])
continue;
p2 = inverts+((i+1)%incount)*C;
d = dotv[i] - dotv[i+1];
if (d)
d = dotv[i] / d;
out = outverts+outcount++*C;
VectorInterpolate(p1, d, p2, out);
}
return outcount;
}
void Fragment_ClipPoly(fragmentdecal_t *dec, int numverts, float *inverts)
{
//emit the triangle, and clip it's fragments.
int p;
float verts[MAXFRAGMENTVERTS*C];
float verts2[MAXFRAGMENTVERTS*C];
float *cverts;
int flip;
if (numverts > MAXFRAGMENTTRIS)
return;
if (dec->numtris == MAXFRAGMENTTRIS)
return; //don't bother
//clip to the first plane specially, so we don't have extra copys
numverts = Fragment_ClipPolyToPlane(inverts, verts, numverts, dec->planenorm[0], dec->planedist[0]);
//clip the triangle to the 6 planes.
flip = 0;
for (p = 1; p < dec->numplanes; p++)
{
flip^=1;
if (flip)
numverts = Fragment_ClipPolyToPlane(verts, verts2, numverts, dec->planenorm[p], dec->planedist[p]);
else
numverts = Fragment_ClipPolyToPlane(verts2, verts, numverts, dec->planenorm[p], dec->planedist[p]);
if (numverts < 3) //totally clipped.
return;
}
if (flip)
cverts = verts2;
else
cverts = verts;
//decompose the resultant polygon into triangles.
while(numverts>2)
{
if (dec->numtris == MAXFRAGMENTTRIS)
return;
numverts--;
VectorCopy((cverts+C*0), decalfragmentverts[dec->numtris*3+0]);
VectorCopy((cverts+C*(numverts-1)), decalfragmentverts[dec->numtris*3+1]);
VectorCopy((cverts+C*numverts), decalfragmentverts[dec->numtris*3+2]);
dec->numtris++;
}
}
#endif
//this could be inlined, but I'm lazy.
void Fragment_Mesh (fragmentdecal_t *dec, mesh_t *mesh)
{
int i;
vecV_t verts[3];
/*if its a triangle fan/poly/quad then we can just submit the entire thing without generating extra fragments*/
if (mesh->istrifan)
{
Fragment_ClipPoly(dec, mesh->numvertexes, mesh->xyz_array[0]);
return;
}
//Fixme: optimise q3 patches (quad strips with bends between each strip)
/*otherwise it goes in and out in weird places*/
for (i = 0; i < mesh->numindexes; i+=3)
{
if (dec->numtris == MAXFRAGMENTTRIS)
break;
VectorCopy(mesh->xyz_array[mesh->indexes[i+0]], verts[0]);
VectorCopy(mesh->xyz_array[mesh->indexes[i+1]], verts[1]);
VectorCopy(mesh->xyz_array[mesh->indexes[i+2]], verts[2]);
Fragment_ClipPoly(dec, 3, verts[0]);
}
}
void Q1BSP_ClipDecalToNodes (fragmentdecal_t *dec, mnode_t *node)
{
mplane_t *splitplane;
float dist;
msurface_t *surf;
int i;
if (node->contents < 0)
return;
splitplane = node->plane;
dist = DotProduct (dec->center, splitplane->normal) - splitplane->dist;
if (dist > dec->radius)
{
Q1BSP_ClipDecalToNodes (dec, node->children[0]);
return;
}
if (dist < -dec->radius)
{
Q1BSP_ClipDecalToNodes (dec, node->children[1]);
return;
}
// mark the polygons
surf = cl.worldmodel->surfaces + node->firstsurface;
for (i=0 ; i<node->numsurfaces ; i++, surf++)
{
if (surf->flags & SURF_PLANEBACK)
{
if (-DotProduct(surf->plane->normal, dec->normal) > -0.5)
continue;
}
else
{
if (DotProduct(surf->plane->normal, dec->normal) > -0.5)
continue;
}
Fragment_Mesh(dec, surf->mesh);
}
Q1BSP_ClipDecalToNodes (dec, node->children[0]);
Q1BSP_ClipDecalToNodes (dec, node->children[1]);
}
#ifdef RTLIGHTS
extern int sh_shadowframe;
#else
static int sh_shadowframe;
#endif
#ifdef Q3BSPS
void Q3BSP_ClipDecalToNodes (fragmentdecal_t *dec, mnode_t *node)
{
mplane_t *splitplane;
float dist;
msurface_t **msurf;
msurface_t *surf;
mleaf_t *leaf;
int i;
if (node->contents != -1)
{
leaf = (mleaf_t *)node;
// mark the polygons
msurf = leaf->firstmarksurface;
for (i=0 ; i<leaf->nummarksurfaces ; i++, msurf++)
{
surf = *msurf;
//only check each surface once. it can appear in multiple leafs.
if (surf->shadowframe == sh_shadowframe)
continue;
surf->shadowframe = sh_shadowframe;
Fragment_Mesh(dec, surf->mesh);
}
return;
}
splitplane = node->plane;
dist = DotProduct (dec->center, splitplane->normal) - splitplane->dist;
if (dist > dec->radius)
{
Q3BSP_ClipDecalToNodes (dec, node->children[0]);
return;
}
if (dist < -dec->radius)
{
Q3BSP_ClipDecalToNodes (dec, node->children[1]);
return;
}
Q3BSP_ClipDecalToNodes (dec, node->children[0]);
Q3BSP_ClipDecalToNodes (dec, node->children[1]);
}
#endif
int Q1BSP_ClipDecal(vec3_t center, vec3_t normal, vec3_t tangent1, vec3_t tangent2, float size, float **out)
{ //quad marks a full, independant quad
int p;
fragmentdecal_t dec;
VectorCopy(center, dec.center);
VectorCopy(normal, dec.normal);
dec.radius = size/2;
dec.numtris = 0;
VectorCopy(tangent1, dec.planenorm[0]);
VectorNegate(tangent1, dec.planenorm[1]);
VectorCopy(tangent2, dec.planenorm[2]);
VectorNegate(tangent2, dec.planenorm[3]);
VectorCopy(dec.normal, dec.planenorm[4]);
VectorNegate(dec.normal, dec.planenorm[5]);
for (p = 0; p < 6; p++)
dec.planedist[p] = -(dec.radius - DotProduct(dec.center, dec.planenorm[p]));
dec.numplanes = 6;
sh_shadowframe++;
if (!cl.worldmodel)
return 0;
else if (cl.worldmodel->fromgame == fg_quake)
Q1BSP_ClipDecalToNodes(&dec, cl.worldmodel->nodes);
#ifdef Q3BSPS
else if (cl.worldmodel->fromgame == fg_quake3)
Q3BSP_ClipDecalToNodes(&dec, cl.worldmodel->nodes);
#endif
*out = (float *)decalfragmentverts;
return dec.numtris;
}
//This is spike's testing function, and is only usable by gl. :)
/*
#include "glquake.h"
void Q1BSP_TestClipDecal(void)
{
int i;
int numtris;
vec3_t fwd;
vec3_t start;
vec3_t center, normal, tangent, tangent2;
float *verts;
if (cls.state != ca_active)
return;
VectorCopy(r_origin, start);
// start[2]+=22;
VectorMA(start, 10000, vpn, fwd);
if (!TraceLineN(start, fwd, center, normal))
{
VectorCopy(start, center);
normal[0] = 0;
normal[1] = 0;
normal[2] = 1;
}
CrossProduct(fwd, normal, tangent);
VectorNormalize(tangent);
CrossProduct(normal, tangent, tangent2);
numtris = Q1BSP_ClipDecal(center, normal, tangent, tangent2, 128, &verts);
PPL_RevertToKnownState();
qglDisable(GL_TEXTURE_2D);
qglDisable(GL_BLEND);
qglDisable(GL_ALPHA_TEST);
qglDisable(GL_DEPTH_TEST);
qglColor3f(1, 0, 0);
qglShadeModel(GL_SMOOTH);
qglBegin(GL_TRIANGLES);
for (i = 0; i < numtris; i++)
{
qglVertex3fv(verts+i*9+0);
qglVertex3fv(verts+i*9+3);
qglVertex3fv(verts+i*9+6);
}
qglEnd();
qglColor3f(1, 1, 1);
qglBegin(GL_LINES);
for (i = 0; i < numtris; i++)
{
qglVertex3fv(verts+i*9+0);
qglVertex3fv(verts+i*9+3);
qglVertex3fv(verts+i*9+3);
qglVertex3fv(verts+i*9+6);
qglVertex3fv(verts+i*9+6);
qglVertex3fv(verts+i*9+0);
}
qglVertex3fv(center);
VectorMA(center, 10, normal, fwd);
qglVertex3fv(fwd);
qglColor3f(0, 1, 0);
qglVertex3fv(center);
VectorMA(center, 10, tangent, fwd);
qglVertex3fv(fwd);
qglColor3f(0, 0, 1);
qglVertex3fv(center);
CrossProduct(tangent, normal, fwd);
VectorMA(center, 10, fwd, fwd);
qglVertex3fv(fwd);
qglColor3f(1, 1, 1);
qglEnd();
qglEnable(GL_TEXTURE_2D);
qglEnable(GL_DEPTH_TEST);
PPL_RevertToKnownState();
}
*/
#endif
/*
Rendering functions (Client only)
==============================================================================
Server only functions
*/
#ifndef CLIENTONLY
//does the recursive work of Q1BSP_FatPVS
void SV_Q1BSP_AddToFatPVS (model_t *mod, vec3_t org, mnode_t *node, qbyte *buffer, unsigned int buffersize)
{
int i;
qbyte *pvs;
mplane_t *plane;
float d;
while (1)
{
// if this is a leaf, accumulate the pvs bits
if (node->contents < 0)
{
if (node->contents != Q1CONTENTS_SOLID)
{
pvs = Q1BSP_LeafPVS (mod, (mleaf_t *)node, NULL, 0);
for (i=0; i<buffersize; i++)
buffer[i] |= pvs[i];
}
return;
}
plane = node->plane;
d = DotProduct (org, plane->normal) - plane->dist;
if (d > 8)
node = node->children[0];
else if (d < -8)
node = node->children[1];
else
{ // go down both
SV_Q1BSP_AddToFatPVS (mod, org, node->children[0], buffer, buffersize);
node = node->children[1];
}
}
}
/*
=============
Q1BSP_FatPVS
Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the
given point.
=============
*/
unsigned int Q1BSP_FatPVS (model_t *mod, vec3_t org, qbyte *pvsbuffer, unsigned int buffersize, qboolean add)
{
unsigned int fatbytes = (mod->numleafs+31)>>3;
if (fatbytes > buffersize)
Sys_Error("map had too much pvs data (too many leaves)\n");;
if (!add)
Q_memset (pvsbuffer, 0, fatbytes);
SV_Q1BSP_AddToFatPVS (mod, org, mod->nodes, pvsbuffer, fatbytes);
return fatbytes;
}
#endif
qboolean Q1BSP_EdictInFatPVS(model_t *mod, struct pvscache_s *ent, qbyte *pvs)
{
int i;
if (ent->num_leafs == MAX_ENT_LEAFS+1)
return true; //it's in too many leafs for us to cope with. Just trivially accept it.
for (i=0 ; i < ent->num_leafs ; i++)
if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) ))
return true; //we might be able to see this one.
return false; //none of this ents leafs were visible, so neither is the ent.
}
/*
===============
SV_FindTouchedLeafs
Links the edict to the right leafs so we can get it's potential visability.
===============
*/
void Q1BSP_RFindTouchedLeafs (model_t *wm, struct pvscache_s *ent, mnode_t *node, float *mins, float *maxs)
{
mplane_t *splitplane;
mleaf_t *leaf;
int sides;
int leafnum;
if (node->contents == Q1CONTENTS_SOLID)
return;
// add an efrag if the node is a leaf
if ( node->contents < 0)
{
if (ent->num_leafs >= MAX_ENT_LEAFS)
{
ent->num_leafs = MAX_ENT_LEAFS+1; //too many. mark it as such so we can trivially accept huge mega-big brush models.
return;
}
leaf = (mleaf_t *)node;
leafnum = leaf - wm->leafs - 1;
ent->leafnums[ent->num_leafs] = leafnum;
ent->num_leafs++;
return;
}
// NODE_MIXED
splitplane = node->plane;
sides = BOX_ON_PLANE_SIDE(mins, maxs, splitplane);
// recurse down the contacted sides
if (sides & 1)
Q1BSP_RFindTouchedLeafs (wm, ent, node->children[0], mins, maxs);
if (sides & 2)
Q1BSP_RFindTouchedLeafs (wm, ent, node->children[1], mins, maxs);
}
void Q1BSP_FindTouchedLeafs(model_t *mod, struct pvscache_s *ent, float *mins, float *maxs)
{
ent->num_leafs = 0;
if (mins && maxs)
Q1BSP_RFindTouchedLeafs (mod, ent, mod->nodes, mins, maxs);
}
/*
Server only functions
==============================================================================
PVS type stuff
*/
/*
===================
Mod_DecompressVis
===================
*/
qbyte *Q1BSP_DecompressVis (qbyte *in, model_t *model, qbyte *decompressed, unsigned int buffersize)
{
int c;
qbyte *out;
int row;
row = (model->numleafs+7)>>3;
out = decompressed;
if (buffersize < row)
row = buffersize;
#if 0
memcpy (out, in, row);
#else
if (!in)
{ // no vis info, so make all visible
while (row)
{
*out++ = 0xff;
row--;
}
return decompressed;
}
do
{
if (*in)
{
*out++ = *in++;
continue;
}
c = in[1];
in += 2;
while (c)
{
*out++ = 0;
c--;
}
} while (out - decompressed < row);
#endif
return decompressed;
}
static qbyte mod_novis[MAX_MAP_LEAFS/8];
qbyte *Q1BSP_LeafPVS (model_t *model, mleaf_t *leaf, qbyte *buffer, unsigned int buffersize)
{
static qbyte decompressed[MAX_MAP_LEAFS/8];
if (leaf == model->leafs)
return mod_novis;
if (!buffer)
{
buffer = decompressed;
buffersize = sizeof(decompressed);
}
return Q1BSP_DecompressVis (leaf->compressed_vis, model, buffer, buffersize);
}
qbyte *Q1BSP_LeafnumPVS (model_t *model, int leafnum, qbyte *buffer, unsigned int buffersize)
{
return Q1BSP_LeafPVS(model, model->leafs + leafnum, buffer, buffersize);
}
//returns the leaf number, which is used as a bit index into the pvs.
int Q1BSP_LeafnumForPoint (model_t *model, vec3_t p)
{
mnode_t *node;
float d;
mplane_t *plane;
if (!model)
{
Sys_Error ("Mod_PointInLeaf: bad model");
}
if (!model->nodes)
return 0;
node = model->nodes;
while (1)
{
if (node->contents < 0)
return (mleaf_t *)node - model->leafs;
plane = node->plane;
d = DotProduct (p,plane->normal) - plane->dist;
if (d > 0)
node = node->children[0];
else
node = node->children[1];
}
return 0; // never reached
}
mleaf_t *Q1BSP_LeafForPoint (model_t *model, vec3_t p)
{
return model->leafs + Q1BSP_LeafnumForPoint(model, p);
}
/*
PVS type stuff
==============================================================================
Init stuff
*/
void Q1BSP_Init(void)
{
memset (mod_novis, 0xff, sizeof(mod_novis));
}
typedef struct {
char lumpname[24]; // up to 23 chars, zero-padded
int fileofs; // from file start
int filelen;
} bspx_lump_t;
typedef struct {
char id[4]; // 'BSPX'
int numlumps;
bspx_lump_t lumps[1];
} bspx_header_t;
static char *bspxbase;
static bspx_header_t *bspxheader;
//supported lumps:
//RGBLIGHTING (.lit)
//LIGHTINGDIR (.lux)
void *Q1BSPX_FindLump(char *lumpname, int *lumpsize)
{
int i;
*lumpsize = 0;
if (!bspxheader)
return NULL;
for (i = 0; i < bspxheader->numlumps; i++)
{
if (!strncmp(bspxheader->lumps[i].lumpname, lumpname, 24))
{
*lumpsize = bspxheader->lumps[i].filelen;
return bspxbase + bspxheader->lumps[i].fileofs;
}
}
return NULL;
}
void Q1BSPX_Setup(model_t *mod, char *filebase, unsigned int filelen, lump_t *lumps, int numlumps)
{
int i;
int offs = 0;
bspx_header_t *h;
bspxbase = filebase;
bspxheader = NULL;
for (i = 0; i < numlumps; i++, lumps++)
{
if (offs < lumps->fileofs + lumps->filelen)
offs = lumps->fileofs + lumps->filelen;
}
offs = (offs + 3) & ~3;
if (offs + sizeof(*bspxheader) > filelen)
return; /*no space for it*/
h = (bspx_header_t*)(filebase + offs);
i = LittleLong(h->numlumps);
/*verify the header*/
if (*(int*)h->id != *(int*)"BSPX" ||
i < 0 ||
offs + sizeof(*h) + sizeof(h->lumps[0])*(i-1) > filelen)
return;
h->numlumps = i;
while(i-->0)
{
h->lumps[i].fileofs = LittleLong(h->lumps[i].fileofs);
h->lumps[i].filelen = LittleLong(h->lumps[i].filelen);
if (h->lumps[i].fileofs + h->lumps[i].filelen > filelen)
return;
}
bspxheader = h;
}
//sets up the functions a server needs.
//fills in bspfuncs_t
void Q1BSP_SetModelFuncs(model_t *mod)
{
#ifndef CLIENTONLY
mod->funcs.FatPVS = Q1BSP_FatPVS;
#endif
mod->funcs.EdictInFatPVS = Q1BSP_EdictInFatPVS;
mod->funcs.FindTouchedLeafs = Q1BSP_FindTouchedLeafs;
mod->funcs.LightPointValues = NULL;
mod->funcs.StainNode = NULL;
mod->funcs.MarkLights = NULL;
mod->funcs.LeafnumForPoint = Q1BSP_LeafnumForPoint;
mod->funcs.LeafPVS = Q1BSP_LeafnumPVS;
mod->funcs.NativeTrace = Q1BSP_Trace;
mod->funcs.PointContents = Q1BSP_PointContents;
}