quakeforge/tools/qfbsp/source/portals.c

577 lines
12 KiB
C

/* Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
See file, 'COPYING', for details.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "QF/sys.h"
#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])
Sys_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)
Sys_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
Sys_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
Sys_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
Sys_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
Sys_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
Sys_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 - (int) (v + 0.5)) < 0.001)
fprintf (f, "%i ", (int) (v + 0.5));
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)
Sys_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);
}