fteqw/engine/common/q1bsp.c
2005-09-07 01:05:27 +00:00

1098 lines
No EOL
25 KiB
C

#include "quakedef.h"
qbyte *Q1BSP_LeafPVS (model_t *model, mleaf_t *leaf, qbyte *buffer);
/*
============================================================================
Physics functions (common)
*/
#if !id386
/*
==================
SV_HullPointContents
==================
*/
static int Q1_HullPointContents (hull_t *hull, int num, vec3_t p)
{
float d;
dclipnode_t *node;
mplane_t *plane;
while (num >= 0)
{
if (num < hull->firstclipnode || num > hull->lastclipnode)
Sys_Error ("SV_HullPointContents: bad node number");
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;
}
#else
int VARGS Q1_HullPointContents (hull_t *hull, int num, vec3_t p);
#endif // !id386
#define DIST_EPSILON (0.03125)
qboolean Q1BSP_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
{
dclipnode_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
}
if (num < hull->firstclipnode || num > hull->lastclipnode)
Sys_Error ("Q1BSP_RecursiveHullCheck: bad node number");
//
// 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
{
VectorSubtract (vec3_origin, 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
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;
}
int Q1BSP_HullPointContents(hull_t *hull, vec3_t p)
{
switch(Q1_HullPointContents(hull, hull->firstclipnode, p))
{
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;
default:
Sys_Error("Q1_PointContents: Unknown contents type");
return FTECONTENTS_SOLID;
}
}
void Q1BSP_SetHullFuncs(hull_t *hull)
{
// hull->funcs.HullPointContents = Q1BSP_HullPointContents;
}
int Q1BSP_PointContents(model_t *model, vec3_t point)
{
return Q1BSP_HullPointContents(&model->hulls[0], point);
}
qboolean Q1BSP_Trace(model_t *model, int forcehullnum, int frame, vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, trace_t *trace)
{
hull_t *hull;
vec3_t size;
vec3_t start_l, end_l;
vec3_t offset;
memset (trace, 0, sizeof(trace_t));
trace->fraction = 1;
trace->allsolid = true;
VectorSubtract (maxs, mins, size);
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] <= 32 && size[2] <= 28) // Half Player
hull = &model->hulls[3];
else if (size[0] <= 32) // 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)
{
if (size[2] < 54 && 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);
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);
}
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;
if (node->contents < 0)
return;
splitplane = node->plane;
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;
}
// mark the polygons
surf = cl.worldmodel->surfaces + node->firstsurface;
for (i=0 ; i<node->numsurfaces ; i++, surf++)
{
if (surf->dlightframe != r_dlightframecount)
{
surf->dlightbits = 0;
surf->dlightframe = r_dlightframecount;
}
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];
vec_t radius;
int numtris;
} fragmentdecal_t;
#define FloatInterpolate(a, bness, b, c) (c) = (a)*(1-bness) + (b)*bness
#define VectorInterpolate(a, bness, b, c) FloatInterpolate((a)[0], bness, (b)[0], (c)[0]),FloatInterpolate((a)[1], bness, (b)[1], (c)[1]),FloatInterpolate((a)[2], bness, (b)[2], (c)[2])
//#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 128
int Fragment_ClipPolyToPlane(float *inverts, float *outverts, int incount, float *plane, float planedist)
{
float dotv[MAXFRAGMENTVERTS];
int i, i2, i3;
int outcount = 0;
int clippedcount = 0;
vec3_t impact;
float *lastvalid = NULL; //the reason these arn't just an index is because it'd need to be a special case for the first vert.
float lastvaliddot = 0;
for (i = 0; i < incount; i++)
{
dotv[i] = DotProduct((inverts+i*3), plane) - planedist;
if (dotv[i]<-DIST_EPSILON)
clippedcount++;
else
{
lastvalid = inverts+i*3;
lastvaliddot = dotv[i];
}
}
if (clippedcount == incount)
return 0; //all were clipped
if (clippedcount == 0)
{
memcpy(outverts, inverts, sizeof(float)*3*incount);
return incount;
}
for (i = 0; i < incount; )
{
if (dotv[i] < -DIST_EPSILON) //clipped
{
//work out where the line impacts the plane
lastvaliddot = (dotv[i]) / (dotv[i]-lastvaliddot);
VectorInterpolate((inverts+i*3), lastvaliddot, lastvalid, impact);
if (outcount+1 >= MAXFRAGMENTVERTS) //bum
break;
outverts[outcount*3 + 0] = impact[0];
outverts[outcount*3 + 1] = impact[1];
outverts[outcount*3 + 2] = impact[2];
outcount++;
i3 = (i+1);
while (dotv[i3%incount] < -DIST_EPSILON) //clipped
i3++;
i = (i3-1)%incount;
i2=i3%incount;
lastvaliddot = (dotv[i]) / (dotv[i]-dotv[i2]);
VectorInterpolate((inverts+i*3), lastvaliddot, (inverts+i2*3), impact);
outverts[outcount*3 + 0] = impact[0];
outverts[outcount*3 + 1] = impact[1];
outverts[outcount*3 + 2] = impact[2];
outcount++;
lastvalid = outverts+outcount*3;
lastvaliddot = 0; // :)
i = i3;
}
else
{ //this vertex wasn't clipped. Just copy to the output.
if (outcount == MAXFRAGMENTVERTS) //bum
break;
outverts[outcount*3 + 0] = inverts[i*3 + 0];
outverts[outcount*3 + 1] = inverts[i*3 + 1];
outverts[outcount*3 + 2] = inverts[i*3 + 2];
lastvalid = inverts+i*3;
lastvaliddot = dotv[i];
outcount++;
i++;
}
}
return outcount;
}
void Fragment_ClipTriangle(fragmentdecal_t *dec, float *a, float *b, float *c)
{
//emit the triangle, and clip it's fragments.
int p;
float verts[MAXFRAGMENTVERTS*3];
float verts2[MAXFRAGMENTVERTS*3];
int numverts;
if (dec->numtris == MAXFRAGMENTTRIS)
return; //don't bother
VectorCopy(a, (verts+0*3));
VectorCopy(b, (verts+1*3));
VectorCopy(c, (verts+2*3));
numverts = 3;
//clip the triangle to the 6 planes.
for (p = 0; p < 6; p+=2)
{
numverts = Fragment_ClipPolyToPlane(verts, verts2, numverts, dec->planenorm[p], dec->planedist[p]);
if (numverts < 3) //totally clipped.
return;
numverts = Fragment_ClipPolyToPlane(verts2, verts, numverts, dec->planenorm[p+1], dec->planedist[p+1]);
if (numverts < 3) //totally clipped.
return;
}
//decompose the resultant polygon into triangles.
while(numverts>2)
{
if (dec->numtris == MAXFRAGMENTTRIS)
return;
numverts--;
VectorCopy((verts+3*0), decalfragmentverts[dec->numtris*3+0]);
VectorCopy((verts+3*(numverts-1)), decalfragmentverts[dec->numtris*3+1]);
VectorCopy((verts+3*numverts), decalfragmentverts[dec->numtris*3+2]);
dec->numtris++;
}
}
#endif
//this could be inlined, but I'm lazy.
void Q1BSP_FragmentToMesh (fragmentdecal_t *dec, mesh_t *mesh)
{
int i;
float *a, *b, *c;
for (i = 0; i < mesh->numindexes; i+=3)
{
if (dec->numtris == MAXFRAGMENTTRIS)
break;
a = mesh->xyz_array[mesh->indexes[i+0]];
b = mesh->xyz_array[mesh->indexes[i+1]];
c = mesh->xyz_array[mesh->indexes[i+2]];
Fragment_ClipTriangle(dec, a, b, c);
}
}
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;
}
Q1BSP_FragmentToMesh(dec, surf->mesh);
}
Q1BSP_ClipDecalToNodes (dec, node->children[0]);
Q1BSP_ClipDecalToNodes (dec, node->children[1]);
}
int Q1BSP_ClipDecal(vec3_t center, vec3_t normal, vec3_t tangent, 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);
VectorCopy(tangent, dec.tangent1);
VectorCopy(tangent2, dec.tangent2);
dec.radius = size/2;
dec.numtris = 0;
VectorCopy(dec.tangent1, dec.planenorm[0]);
VectorNegate(dec.tangent1, dec.planenorm[1]);
VectorCopy(dec.tangent2, dec.planenorm[2]);
VectorNegate(dec.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]));
Q1BSP_ClipDecalToNodes(&dec, cl.worldmodel->nodes);
*out = (float *)decalfragmentverts;
return dec.numtris;
}
//This is spike's testing function, and is only usable by gl. :)
void Q1BSP_TestClipDecal(void)
{
/*
int i;
int numtris;
vec3_t fwd;
vec3_t start;
vec3_t center, normal, tangent;
float *verts;
if (cls.state != ca_active)
return;
VectorCopy(cl.simorg[0], start);
start[2]+=22;
VectorMA(start, 10000, vpn, fwd);
TraceLineN(start, fwd, center, normal);
CrossProduct(fwd, normal, tangent);
VectorNormalize(tangent);
numtris = Q1BSP_ClipDecal(center, normal, tangent, 128, &verts);
qglDisable(GL_TEXTURE_2D);
qglDisable(GL_BLEND);
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);
*/
}
#endif
/*
Rendering functions (Client only)
==============================================================================
Server only functions
*/
#ifndef CLIENTONLY
extern int fatbytes;
extern qbyte fatpvs[(MAX_MAP_LEAFS+1)/4];
//does the recursive work of Q1BSP_FatPVS
void SV_Q1BSP_AddToFatPVS (model_t *mod, vec3_t org, mnode_t *node)
{
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);
for (i=0 ; i<fatbytes ; i++)
fatpvs[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]);
node = node->children[1];
}
}
}
/*
=============
Q1BSP_FatPVS
Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the
given point.
=============
*/
void Q1BSP_FatPVS (model_t *mod, vec3_t org, qboolean add)
{
fatbytes = (mod->numleafs+31)>>3;
if (!add)
Q_memset (fatpvs, 0, fatbytes);
SV_Q1BSP_AddToFatPVS (mod, org, mod->nodes);
}
qboolean Q1BSP_EdictInFatPVS(model_t *mod, edict_t *ent)
{
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 (fatpvs[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 (edict_t *ent, mnode_t *node)
{
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 - sv.worldmodel->leafs - 1;
ent->leafnums[ent->num_leafs] = leafnum;
ent->num_leafs++;
return;
}
// NODE_MIXED
splitplane = node->plane;
sides = BOX_ON_PLANE_SIDE(ent->v->absmin, ent->v->absmax, splitplane);
// recurse down the contacted sides
if (sides & 1)
Q1BSP_RFindTouchedLeafs (ent, node->children[0]);
if (sides & 2)
Q1BSP_RFindTouchedLeafs (ent, node->children[1]);
}
void Q1BSP_FindTouchedLeafs(model_t *mod, edict_t *ent)
{
ent->num_leafs = 0;
if (ent->v->modelindex)
Q1BSP_RFindTouchedLeafs (ent, mod->nodes);
}
#endif
/*
Server only functions
==============================================================================
PVS type stuff
*/
/*
===================
Mod_DecompressVis
===================
*/
qbyte *Q1BSP_DecompressVis (qbyte *in, model_t *model, qbyte *decompressed)
{
int c;
qbyte *out;
int row;
row = (model->numleafs+7)>>3;
out = decompressed;
#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)
{
static qbyte decompressed[MAX_MAP_LEAFS/8];
if (leaf == model->leafs)
return mod_novis;
if (!buffer)
buffer = decompressed;
return Q1BSP_DecompressVis (leaf->compressed_vis, model, buffer);
}
qbyte *Q1BSP_LeafnumPVS (model_t *model, int leafnum, qbyte *buffer)
{
return Q1BSP_LeafPVS(model, model->leafs + leafnum, buffer);
}
//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));
}
//sets up the functions a server needs.
//fills in bspfuncs_t
void Q1BSP_SetModelFuncs(model_t *mod)
{
#ifndef CLIENTONLY
mod->funcs.FatPVS = Q1BSP_FatPVS;
mod->funcs.EdictInFatPVS = Q1BSP_EdictInFatPVS;
mod->funcs.FindTouchedLeafs_Q1 = Q1BSP_FindTouchedLeafs;
#endif
mod->funcs.LightPointValues = NULL;
mod->funcs.StainNode = NULL;
mod->funcs.MarkLights = NULL;
mod->funcs.LeafnumForPoint = Q1BSP_LeafnumForPoint;
mod->funcs.LeafPVS = Q1BSP_LeafnumPVS;
mod->funcs.Trace = Q1BSP_Trace;
mod->funcs.PointContents = Q1BSP_PointContents;
}