quake-hipnotic-sdk/utils/qbsp/portals.c
1997-03-11 00:00:00 +00:00

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);
}