576 lines
11 KiB
C
576 lines
11 KiB
C
|
|
#include "bsp5.h"
|
|
|
|
|
|
node_t outside_node; // portals outside the world face this
|
|
|
|
//=============================================================================
|
|
|
|
/*
|
|
=============
|
|
addportaltonodes
|
|
=============
|
|
*/
|
|
void addportaltonodes (portal_t *p, node_t *front, node_t *back)
|
|
{
|
|
if (p->nodes[0] || p->nodes[1])
|
|
error ("addportaltonode: allready included");
|
|
|
|
p->nodes[0] = front;
|
|
p->next[0] = front->portals;
|
|
front->portals = p;
|
|
|
|
p->nodes[1] = back;
|
|
p->next[1] = back->portals;
|
|
back->portals = p;
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
removeportalfromnode
|
|
=============
|
|
*/
|
|
void removeportalfromnode (portal_t *portal, node_t *l)
|
|
{
|
|
portal_t **pp, *t;
|
|
|
|
// remove reference to the current portal
|
|
pp = &l->portals;
|
|
while (1)
|
|
{
|
|
t = *pp;
|
|
if (!t)
|
|
error ("removeportalfromnode: portal not in leaf");
|
|
|
|
if ( t == portal )
|
|
break;
|
|
|
|
if (t->nodes[0] == l)
|
|
pp = &t->next[0];
|
|
else if (t->nodes[1] == l)
|
|
pp = &t->next[1];
|
|
else
|
|
error ("removeportalfromnode: portal not bounding leaf");
|
|
}
|
|
|
|
if (portal->nodes[0] == l)
|
|
{
|
|
*pp = portal->next[0];
|
|
portal->nodes[0] = null;
|
|
}
|
|
else if (portal->nodes[1] == l)
|
|
{
|
|
*pp = portal->next[1];
|
|
portal->nodes[1] = null;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
void printportal (portal_t *p)
|
|
{
|
|
int i;
|
|
winding_t *w;
|
|
|
|
w = p->winding;
|
|
for (i=0 ; i<w->numpoints ; i++)
|
|
printf ("(%5.0f,%5.0f,%5.0f)\n",w->points[i][0]
|
|
, w->points[i][1], w->points[i][2]);
|
|
}
|
|
|
|
/*
|
|
================
|
|
makeheadnodeportals
|
|
|
|
the created portals will face the global outside_node
|
|
================
|
|
*/
|
|
void makeheadnodeportals (node_t *node)
|
|
{
|
|
vec3_t bounds[2];
|
|
int i, j, n;
|
|
portal_t *p, *portals[6];
|
|
plane_t bplanes[6], *pl;
|
|
int side;
|
|
|
|
draw_clearwindow ();
|
|
|
|
// pad with some space so there will never be null volume leafs
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
bounds[0][i] = brushset->mins[i] - sidespace;
|
|
bounds[1][i] = brushset->maxs[i] + sidespace;
|
|
}
|
|
|
|
outside_node.contents = contents_solid;
|
|
outside_node.portals = null;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
for (j=0 ; j<2 ; j++)
|
|
{
|
|
n = j*3 + i;
|
|
|
|
p = allocportal ();
|
|
portals[n] = p;
|
|
|
|
pl = &bplanes[n];
|
|
memset (pl, 0, sizeof(*pl));
|
|
if (j)
|
|
{
|
|
pl->normal[i] = -1;
|
|
pl->dist = -bounds[j][i];
|
|
}
|
|
else
|
|
{
|
|
pl->normal[i] = 1;
|
|
pl->dist = bounds[j][i];
|
|
}
|
|
p->planenum = findplane (pl, &side);
|
|
|
|
p->winding = basewindingforplane (pl);
|
|
if (side)
|
|
addportaltonodes (p, &outside_node, node);
|
|
else
|
|
addportaltonodes (p, node, &outside_node);
|
|
}
|
|
|
|
// clip the basewindings by all the other planes
|
|
for (i=0 ; i<6 ; i++)
|
|
{
|
|
for (j=0 ; j<6 ; j++)
|
|
{
|
|
if (j == i)
|
|
continue;
|
|
portals[i]->winding = clipwinding (portals[i]->winding, &bplanes[j], true);
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
void checkwindinginnode (winding_t *w, node_t *node)
|
|
{
|
|
int i, j;
|
|
|
|
for (i=0 ; i<w->numpoints ; i++)
|
|
{
|
|
for (j=0 ; j<3 ; j++)
|
|
if (w->points[i][j] < node->mins[j] - 1
|
|
|| w->points[i][j] > node->maxs[j] + 1)
|
|
{
|
|
printf ("warning: checkwindinginnode: outside\n");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void checkwindingarea (winding_t *w)
|
|
{
|
|
int i;
|
|
float total, add;
|
|
vec3_t v1, v2, cross;
|
|
|
|
total = 0;
|
|
for (i=1 ; i<w->numpoints ; i++)
|
|
{
|
|
vectorsubtract (w->points[i], w->points[0], v1);
|
|
vectorsubtract (w->points[i+1], w->points[0], v2);
|
|
crossproduct (v1, v2, cross);
|
|
add = vectorlength (cross);
|
|
total += add*0.5;
|
|
}
|
|
if (total < 16)
|
|
printf ("warning: winding area %f\n", total);
|
|
}
|
|
|
|
|
|
void planefromwinding (winding_t *w, plane_t *plane)
|
|
{
|
|
vec3_t v1, v2;
|
|
|
|
// calc plane
|
|
vectorsubtract (w->points[2], w->points[1], v1);
|
|
vectorsubtract (w->points[0], w->points[1], v2);
|
|
crossproduct (v2, v1, plane->normal);
|
|
vectornormalize (plane->normal);
|
|
plane->dist = dotproduct (w->points[0], plane->normal);
|
|
}
|
|
|
|
void checkleafportalconsistancy (node_t *node)
|
|
{
|
|
int side, side2;
|
|
portal_t *p, *p2;
|
|
plane_t plane, plane2;
|
|
int i;
|
|
winding_t *w;
|
|
float dist;
|
|
|
|
side = side2 = 0; // quiet compiler warning
|
|
|
|
for (p = node->portals ; p ; p = p->next[side])
|
|
{
|
|
if (p->nodes[0] == node)
|
|
side = 0;
|
|
else if (p->nodes[1] == node)
|
|
side = 1;
|
|
else
|
|
error ("cutnodeportals_r: mislinked portal");
|
|
checkwindinginnode (p->winding, node);
|
|
checkwindingarea (p->winding);
|
|
|
|
// check that the side orders are correct
|
|
plane = planes[p->planenum];
|
|
planefromwinding (p->winding, &plane2);
|
|
|
|
for (p2 = node->portals ; p2 ; p2 = p2->next[side2])
|
|
{
|
|
if (p2->nodes[0] == node)
|
|
side2 = 0;
|
|
else if (p2->nodes[1] == node)
|
|
side2 = 1;
|
|
else
|
|
error ("cutnodeportals_r: mislinked portal");
|
|
w = p2->winding;
|
|
for (i=0 ; i<w->numpoints ; i++)
|
|
{
|
|
dist = dotproduct (w->points[i], plane.normal) - plane.dist;
|
|
if ( (side == 0 && dist < -1) || (side == 1 && dist > 1) )
|
|
{
|
|
printf ("warning: portal siding direction is wrong\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
cutnodeportals_r
|
|
|
|
================
|
|
*/
|
|
void cutnodeportals_r (node_t *node)
|
|
{
|
|
plane_t *plane, clipplane;
|
|
node_t *f, *b, *other_node;
|
|
portal_t *p, *new_portal, *next_portal;
|
|
winding_t *w, *frontwinding, *backwinding;
|
|
int side;
|
|
|
|
// checkleafportalconsistancy (node);
|
|
|
|
//
|
|
// seperate the portals on node into it's children
|
|
//
|
|
if (node->contents)
|
|
{
|
|
return; // at a leaf, no more dividing
|
|
}
|
|
|
|
plane = &planes[node->planenum];
|
|
|
|
f = node->children[0];
|
|
b = node->children[1];
|
|
|
|
//
|
|
// create the new portal by taking the full plane winding for the cutting plane
|
|
// and clipping it by all of the planes from the other portals
|
|
//
|
|
new_portal = allocportal ();
|
|
new_portal->planenum = node->planenum;
|
|
|
|
w = basewindingforplane (&planes[node->planenum]);
|
|
side = 0; // shut up compiler warning
|
|
for (p = node->portals ; p ; p = p->next[side])
|
|
{
|
|
clipplane = planes[p->planenum];
|
|
if (p->nodes[0] == node)
|
|
side = 0;
|
|
else if (p->nodes[1] == node)
|
|
{
|
|
clipplane.dist = -clipplane.dist;
|
|
vectorsubtract (vec3_origin, clipplane.normal, clipplane.normal);
|
|
side = 1;
|
|
}
|
|
else
|
|
error ("cutnodeportals_r: mislinked portal");
|
|
|
|
w = clipwinding (w, &clipplane, true);
|
|
if (!w)
|
|
{
|
|
printf ("warning: cutnodeportals_r:new portal was clipped away\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (w)
|
|
{
|
|
// if the plane was not clipped on all sides, there was an error
|
|
new_portal->winding = w;
|
|
addportaltonodes (new_portal, f, b);
|
|
}
|
|
|
|
//
|
|
// partition the portals
|
|
//
|
|
for (p = node->portals ; p ; p = next_portal)
|
|
{
|
|
if (p->nodes[0] == node)
|
|
side = 0;
|
|
else if (p->nodes[1] == node)
|
|
side = 1;
|
|
else
|
|
error ("cutnodeportals_r: mislinked portal");
|
|
next_portal = p->next[side];
|
|
|
|
other_node = p->nodes[!side];
|
|
removeportalfromnode (p, p->nodes[0]);
|
|
removeportalfromnode (p, p->nodes[1]);
|
|
|
|
//
|
|
// cut the portal into two portals, one on each side of the cut plane
|
|
//
|
|
dividewinding (p->winding, plane, &frontwinding, &backwinding);
|
|
|
|
if (!frontwinding)
|
|
{
|
|
if (side == 0)
|
|
addportaltonodes (p, b, other_node);
|
|
else
|
|
addportaltonodes (p, other_node, b);
|
|
continue;
|
|
}
|
|
if (!backwinding)
|
|
{
|
|
if (side == 0)
|
|
addportaltonodes (p, f, other_node);
|
|
else
|
|
addportaltonodes (p, other_node, f);
|
|
continue;
|
|
}
|
|
|
|
// the winding is split
|
|
new_portal = allocportal ();
|
|
*new_portal = *p;
|
|
new_portal->winding = backwinding;
|
|
freewinding (p->winding);
|
|
p->winding = frontwinding;
|
|
|
|
if (side == 0)
|
|
{
|
|
addportaltonodes (p, f, other_node);
|
|
addportaltonodes (new_portal, b, other_node);
|
|
}
|
|
else
|
|
{
|
|
addportaltonodes (p, other_node, f);
|
|
addportaltonodes (new_portal, other_node, b);
|
|
}
|
|
}
|
|
|
|
drawleaf (f,1);
|
|
drawleaf (b,2);
|
|
|
|
cutnodeportals_r (f);
|
|
cutnodeportals_r (b);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
portalizeworld
|
|
|
|
builds the exact polyhedrons for the nodes and leafs
|
|
==================
|
|
*/
|
|
void portalizeworld (node_t *headnode)
|
|
{
|
|
qprintf ("----- portalize ----\n");
|
|
|
|
makeheadnodeportals (headnode);
|
|
cutnodeportals_r (headnode);
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
freeallportals
|
|
|
|
==================
|
|
*/
|
|
void freeallportals (node_t *node)
|
|
{
|
|
portal_t *p, *nextp;
|
|
|
|
if (!node->contents)
|
|
{
|
|
freeallportals (node->children[0]);
|
|
freeallportals (node->children[1]);
|
|
}
|
|
|
|
for (p=node->portals ; p ; p=nextp)
|
|
{
|
|
if (p->nodes[0] == node)
|
|
nextp = p->next[0];
|
|
else
|
|
nextp = p->next[1];
|
|
removeportalfromnode (p, p->nodes[0]);
|
|
removeportalfromnode (p, p->nodes[1]);
|
|
freewinding (p->winding);
|
|
freeportal (p);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
portal file generation
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
#define portalfile "prt1"
|
|
|
|
file *pf;
|
|
int num_visleafs; // leafs the player can be in
|
|
int num_visportals;
|
|
|
|
void writefloat (file *f, vec_t v)
|
|
{
|
|
if ( fabs(v - q_rint(v)) < 0.001 )
|
|
fprintf (f,"%i ",(int)q_rint(v));
|
|
else
|
|
fprintf (f,"%f ",v);
|
|
}
|
|
|
|
void writeportalfile_r (node_t *node)
|
|
{
|
|
int i;
|
|
portal_t *p;
|
|
winding_t *w;
|
|
plane_t *pl, plane2;
|
|
|
|
if (!node->contents)
|
|
{
|
|
writeportalfile_r (node->children[0]);
|
|
writeportalfile_r (node->children[1]);
|
|
return;
|
|
}
|
|
|
|
if (node->contents == contents_solid)
|
|
return;
|
|
|
|
for (p = node->portals ; p ; )
|
|
{
|
|
w = p->winding;
|
|
if (w && p->nodes[0] == node
|
|
&& p->nodes[0]->contents == p->nodes[1]->contents)
|
|
{
|
|
// write out to the file
|
|
|
|
// sometimes planes get turned around when they are very near
|
|
// the changeover point between different axis. interpret the
|
|
// plane the same way vis will, and flip the side orders if needed
|
|
pl = &planes[p->planenum];
|
|
planefromwinding (w, &plane2);
|
|
if ( dotproduct (pl->normal, plane2.normal) < 0.99 )
|
|
{ // backwards...
|
|
fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[1]->visleafnum, p->nodes[0]->visleafnum);
|
|
}
|
|
else
|
|
fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[0]->visleafnum, p->nodes[1]->visleafnum);
|
|
for (i=0 ; i<w->numpoints ; i++)
|
|
{
|
|
fprintf (pf,"(");
|
|
writefloat (pf, w->points[i][0]);
|
|
writefloat (pf, w->points[i][1]);
|
|
writefloat (pf, w->points[i][2]);
|
|
fprintf (pf,") ");
|
|
}
|
|
fprintf (pf,"\n");
|
|
}
|
|
|
|
if (p->nodes[0] == node)
|
|
p = p->next[0];
|
|
else
|
|
p = p->next[1];
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
================
|
|
numberleafs_r
|
|
================
|
|
*/
|
|
void numberleafs_r (node_t *node)
|
|
{
|
|
portal_t *p;
|
|
|
|
if (!node->contents)
|
|
{ // decision node
|
|
node->visleafnum = -99;
|
|
numberleafs_r (node->children[0]);
|
|
numberleafs_r (node->children[1]);
|
|
return;
|
|
}
|
|
|
|
draw_clearwindow ();
|
|
drawleaf (node, 1);
|
|
|
|
if (node->contents == contents_solid)
|
|
{ // solid block, viewpoint never inside
|
|
node->visleafnum = -1;
|
|
return;
|
|
}
|
|
|
|
node->visleafnum = num_visleafs++;
|
|
|
|
for (p = node->portals ; p ; )
|
|
{
|
|
if (p->nodes[0] == node) // only write out from first leaf
|
|
{
|
|
if (p->nodes[0]->contents == p->nodes[1]->contents)
|
|
num_visportals++;
|
|
p = p->next[0];
|
|
}
|
|
else
|
|
p = p->next[1];
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
writeportalfile
|
|
================
|
|
*/
|
|
void writeportalfile (node_t *headnode)
|
|
{
|
|
// set the visleafnum field in every leaf and count the total number of portals
|
|
num_visleafs = 0;
|
|
num_visportals = 0;
|
|
numberleafs_r (headnode);
|
|
|
|
// write the file
|
|
printf ("writing %s\n", portfilename);
|
|
pf = fopen (portfilename, "w");
|
|
if (!pf)
|
|
error ("error opening %s", portfilename);
|
|
|
|
fprintf (pf, "%s\n", portalfile);
|
|
fprintf (pf, "%i\n", num_visleafs);
|
|
fprintf (pf, "%i\n", num_visportals);
|
|
|
|
writeportalfile_r (headnode);
|
|
|
|
fclose (pf);
|
|
}
|
|
|
|
|