halflife-thewastes-sdk/utils/qrad/trace.c
1999-04-07 00:00:00 +00:00

320 lines
6.3 KiB
C

/***
*
* Copyright (c) 1998, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
****/
// trace.c
#include "cmdlib.h"
#include "mathlib.h"
#include "bspfile.h"
#include "polylib.h"
// #define ON_EPSILON 0.001
typedef struct tnode_s
{
int type;
vec3_t normal;
float dist;
int children[2];
int pad;
} tnode_t;
tnode_t *tnodes, *tnode_p;
/*
==============
MakeTnode
Converts the disk node structure into the efficient tracing structure
==============
*/
void MakeTnode (int nodenum)
{
tnode_t *t;
dplane_t *plane;
int i;
dnode_t *node;
t = tnode_p++;
node = dnodes + nodenum;
plane = dplanes + node->planenum;
t->type = plane->type;
VectorCopy (plane->normal, t->normal);
t->dist = plane->dist;
for (i=0 ; i<2 ; i++)
{
if (node->children[i] < 0)
t->children[i] = dleafs[-node->children[i] - 1].contents;
else
{
t->children[i] = tnode_p - tnodes;
MakeTnode (node->children[i]);
}
}
}
/*
=============
MakeTnodes
Loads the node structure out of a .bsp file to be used for light occlusion
=============
*/
void MakeTnodes (dmodel_t *bm)
{
// 32 byte align the structs
tnodes = calloc( (numnodes+1), sizeof(tnode_t));
tnodes = (tnode_t *)(((int)tnodes + 31)&~31);
tnode_p = tnodes;
MakeTnode (0);
}
//==========================================================
byte nodehit[MAX_MAP_NODES];
/*
=============
PartialHead
=============
*/
int PartialHead (void)
{
int nodenum;
dnode_t *node;
tnode_p = tnodes;
// skip single sided nodes from root
nodenum = 0;
while (nodenum >= 0)
{
node = &dnodes[nodenum];
if ( (node->children[0] < 0) || !nodehit[node->children[0]])
nodenum = node->children[1];
else if ((node->children[1] < 0) || !nodehit[node->children[1]])
nodenum = node->children[0];
else
break;
}
return nodenum;
}
//==========================================================
int TestLine_r (int node, vec3_t start, vec3_t stop)
{
tnode_t *tnode;
float front, back;
vec3_t mid;
float frac;
int side;
int r;
if (node == CONTENTS_SOLID)
return CONTENTS_SOLID;
if (node == CONTENTS_SKY)
return CONTENTS_SKY;
if (node < 0)
return CONTENTS_EMPTY;
tnode = &tnodes[node];
switch (tnode->type)
{
case PLANE_X:
front = start[0] - tnode->dist;
back = stop[0] - tnode->dist;
break;
case PLANE_Y:
front = start[1] - tnode->dist;
back = stop[1] - tnode->dist;
break;
case PLANE_Z:
front = start[2] - tnode->dist;
back = stop[2] - tnode->dist;
break;
default:
front = (start[0]*tnode->normal[0] + start[1]*tnode->normal[1] + start[2]*tnode->normal[2]) - tnode->dist;
back = (stop[0]*tnode->normal[0] + stop[1]*tnode->normal[1] + stop[2]*tnode->normal[2]) - tnode->dist;
break;
}
if (front >= -ON_EPSILON && back >= -ON_EPSILON)
return TestLine_r (tnode->children[0], start, stop);
if (front < ON_EPSILON && back < ON_EPSILON)
return TestLine_r (tnode->children[1], start, stop);
side = front < 0;
frac = front / (front-back);
mid[0] = start[0] + (stop[0] - start[0])*frac;
mid[1] = start[1] + (stop[1] - start[1])*frac;
mid[2] = start[2] + (stop[2] - start[2])*frac;
r = TestLine_r (tnode->children[side], start, mid);
if (r != CONTENTS_EMPTY)
return r;
return TestLine_r (tnode->children[!side], mid, stop);
}
int TestLine (vec3_t start, vec3_t stop)
{
return TestLine_r (0, start, stop);
}
/*
==============================================================================
LINE TRACING
The major lighting operation is a point to point visibility test, performed
by recursive subdivision of the line by the BSP tree.
==============================================================================
*/
typedef struct
{
vec3_t backpt;
int side;
int node;
} tracestack_t;
/*
==============
TestLine
==============
*/
qboolean _TestLine (vec3_t start, vec3_t stop)
{
int node;
float front, back;
tracestack_t *tstack_p;
int side;
float frontx,fronty, frontz, backx, backy, backz;
tracestack_t tracestack[64];
tnode_t *tnode;
frontx = start[0];
fronty = start[1];
frontz = start[2];
backx = stop[0];
backy = stop[1];
backz = stop[2];
tstack_p = tracestack;
node = 0;
while (1)
{
if (node == CONTENTS_SOLID)
{
#if 0
float d1, d2, d3;
d1 = backx - frontx;
d2 = backy - fronty;
d3 = backz - frontz;
if (d1*d1 + d2*d2 + d3*d3 > 1)
#endif
return false; // DONE!
}
while (node < 0)
{
// pop up the stack for a back side
tstack_p--;
if (tstack_p < tracestack)
return true;
node = tstack_p->node;
// set the hit point for this plane
frontx = backx;
fronty = backy;
frontz = backz;
// go down the back side
backx = tstack_p->backpt[0];
backy = tstack_p->backpt[1];
backz = tstack_p->backpt[2];
node = tnodes[tstack_p->node].children[!tstack_p->side];
}
tnode = &tnodes[node];
switch (tnode->type)
{
case PLANE_X:
front = frontx - tnode->dist;
back = backx - tnode->dist;
break;
case PLANE_Y:
front = fronty - tnode->dist;
back = backy - tnode->dist;
break;
case PLANE_Z:
front = frontz - tnode->dist;
back = backz - tnode->dist;
break;
default:
front = (frontx*tnode->normal[0] + fronty*tnode->normal[1] + frontz*tnode->normal[2]) - tnode->dist;
back = (backx*tnode->normal[0] + backy*tnode->normal[1] + backz*tnode->normal[2]) - tnode->dist;
break;
}
if (front > -ON_EPSILON && back > -ON_EPSILON)
{
node = tnode->children[0];
continue;
}
if (front < ON_EPSILON && back < ON_EPSILON)
{
node = tnode->children[1];
continue;
}
side = front < 0;
front = front / (front-back);
tstack_p->node = node;
tstack_p->side = side;
tstack_p->backpt[0] = backx;
tstack_p->backpt[1] = backy;
tstack_p->backpt[2] = backz;
tstack_p++;
backx = frontx + front*(backx-frontx);
backy = fronty + front*(backy-fronty);
backz = frontz + front*(backz-frontz);
node = tnode->children[side];
}
}