forked from a1batross/Paranoia2_original
427 lines
9.1 KiB
C++
427 lines
9.1 KiB
C++
/***
|
|
*
|
|
* Copyright (c) 1996-2002, 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.
|
|
*
|
|
****/
|
|
|
|
#include "bsp5.h"
|
|
|
|
|
|
node_t g_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] )
|
|
COM_FatalError( "AddPortalToNode: allready included\n" );
|
|
|
|
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 ) COM_FatalError( "RemovePortalFromNode: portal not in leaf\n" );
|
|
|
|
if( t == portal )
|
|
break;
|
|
|
|
if( t->nodes[0] == l )
|
|
pp = &t->next[0];
|
|
else if( t->nodes[1] == l )
|
|
pp = &t->next[1];
|
|
else COM_FatalError( "RemovePortalFromNode: portal not bounding leaf\n" );
|
|
}
|
|
|
|
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 )
|
|
{
|
|
pw( p->winding );
|
|
}
|
|
|
|
/*
|
|
================
|
|
MakeHeadnodePortals
|
|
|
|
The created portals will face the global outside_node
|
|
================
|
|
*/
|
|
void MakeHeadnodePortals( node_t *node, const vec3_t mins, const vec3_t maxs )
|
|
{
|
|
vec3_t bounds[2];
|
|
portal_t *p, *portals[6];
|
|
plane_t bplanes[6], *pl;
|
|
int i, j, n;
|
|
|
|
// pad with some space so there will never be null volume leafs
|
|
VectorCopy( mins, bounds[0] );
|
|
VectorCopy( maxs, bounds[1] );
|
|
|
|
ExpandBounds( bounds[0], bounds[1], SIDESPACE );
|
|
g_outside_node.contents = CONTENTS_SOLID;
|
|
g_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 = FindFloatPlane( pl->normal, pl->dist );
|
|
p->winding = BaseWindingForPlane( pl->normal, pl->dist );
|
|
AddPortalToNodes( p, node, &g_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;
|
|
ChopWindingInPlace( &portals[i]->winding, bplanes[j].normal, bplanes[j].dist, g_prtepsilon );
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
/*
|
|
================
|
|
PortalSidesVisible
|
|
|
|
translucent water support
|
|
================
|
|
*/
|
|
bool PortalSidesVisible( portal_t *p )
|
|
{
|
|
#if 1
|
|
int contents0 = p->nodes[0]->contents;
|
|
int contents1 = p->nodes[1]->contents;
|
|
|
|
// can't see through solids
|
|
if( contents0 == CONTENTS_SOLID || contents1 == CONTENTS_SOLID )
|
|
return false;
|
|
|
|
// if contents values are the same and not solid, can see through
|
|
if( contents0 == contents1 )
|
|
return true;
|
|
|
|
// can't see through func_illusionary_visblocker
|
|
if( contents0 == CONTENTS_VISBLOCKER || contents1 == CONTENTS_VISBLOCKER )
|
|
return false;
|
|
|
|
if( IsLiquidContents( contents0 ) && contents1 == CONTENTS_EMPTY )
|
|
return true;
|
|
|
|
if( IsLiquidContents( contents1 ) && contents0 == CONTENTS_EMPTY )
|
|
return true;
|
|
|
|
return false;
|
|
#else
|
|
if( p->nodes[0]->contents == p->nodes[1]->contents )
|
|
return true;
|
|
|
|
if( p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
|
|
&& p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY )
|
|
return true;
|
|
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
PORTAL FILE GENERATION
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
FILE *pf;
|
|
int num_visleafs; // leafs the player can be in
|
|
int num_visportals;
|
|
int c_backward_portals;
|
|
int c_colinear_portals;
|
|
|
|
static void WritePortalFile_r( node_t *node )
|
|
{
|
|
plane_t *plane, plane2;
|
|
portal_t *p, *next;
|
|
winding_t *w;
|
|
int i;
|
|
|
|
if( !FBitSet( node->flags, FNODE_LEAFPORTAL ))
|
|
{
|
|
WritePortalFile_r( node->children[0] );
|
|
WritePortalFile_r( node->children[1] );
|
|
return;
|
|
}
|
|
|
|
if( node->contents == CONTENTS_SOLID )
|
|
return;
|
|
|
|
for( p = node->portals; p != NULL; p = next )
|
|
{
|
|
next = (p->nodes[0] == node) ? p->next[0] : p->next[1];
|
|
|
|
if( !p->winding || p->nodes[0] != node )
|
|
continue;
|
|
|
|
if( !PortalSidesVisible( p ))
|
|
continue;
|
|
|
|
w = p->winding;
|
|
|
|
// 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
|
|
WindingPlane( w, plane2.normal, &plane2.dist );
|
|
plane = &g_mapplanes[p->planenum];
|
|
|
|
if( DotProduct( plane->normal, plane2.normal ) < ( 1.0 - g_prtepsilon ))
|
|
{
|
|
if( DotProduct( plane->normal, plane2.normal ) > -1.0 + g_prtepsilon )
|
|
c_colinear_portals++;
|
|
else c_backward_portals++;
|
|
|
|
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, "(%f %f %f) ", w->p[i][0], w->p[i][1], w->p[i][2] );
|
|
}
|
|
|
|
fprintf( pf,"\n" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
NumberLeafs_r
|
|
================
|
|
*/
|
|
void NumberLeafs_r( node_t *node )
|
|
{
|
|
portal_t *p;
|
|
|
|
if( !FBitSet( node->flags, FNODE_LEAFPORTAL ))
|
|
{
|
|
// decision node
|
|
node->visleafnum = -99;
|
|
NumberLeafs_r( node->children[0] );
|
|
NumberLeafs_r( node->children[1] );
|
|
return;
|
|
}
|
|
|
|
if( node->contents == CONTENTS_SOLID )
|
|
{
|
|
// solid block, viewpoint never inside
|
|
node->visleafnum = -1;
|
|
return;
|
|
}
|
|
|
|
node->visleafnum = num_visleafs++;
|
|
|
|
for( p = node->portals; p != NULL; )
|
|
{
|
|
if( p->nodes[0] == node )
|
|
{
|
|
// only write out from first leaf
|
|
if( p->winding && PortalSidesVisible( p ))
|
|
num_visportals++;
|
|
p = p->next[0];
|
|
}
|
|
else p = p->next[1];
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
================
|
|
CountChildLeafs_r
|
|
================
|
|
*/
|
|
static int CountChildLeafs_r( node_t *node )
|
|
{
|
|
if( node->planenum == PLANENUM_LEAF )
|
|
{
|
|
if( FBitSet( node->flags, FNODE_DETAILCONTENTS ))
|
|
{
|
|
// solid
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// node
|
|
int count = 0;
|
|
|
|
count += CountChildLeafs_r( node->children[0] );
|
|
count += CountChildLeafs_r( node->children[1] );
|
|
|
|
return count;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
WriteLeafCount_r
|
|
================
|
|
*/
|
|
static void WriteLeafCount_r( node_t *node )
|
|
{
|
|
if( !FBitSet( node->flags, FNODE_LEAFPORTAL ))
|
|
{
|
|
WriteLeafCount_r( node->children[0] );
|
|
WriteLeafCount_r( node->children[1] );
|
|
}
|
|
else
|
|
{
|
|
if( node->contents == CONTENTS_SOLID )
|
|
return;
|
|
|
|
int count = CountChildLeafs_r( node );
|
|
fprintf( pf, "%i\n", count );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
WritePortalfile
|
|
================
|
|
*/
|
|
void WritePortalfile( tree_t *tree, bool leaked )
|
|
{
|
|
char shortname[1024];
|
|
|
|
num_visleafs = 0;
|
|
num_visportals = 0;
|
|
c_backward_portals = 0;
|
|
c_colinear_portals = 0;
|
|
|
|
COM_FileBase( g_portfilename, shortname );
|
|
|
|
// set the visleafnum field in every leaf
|
|
// and count the total number of portals
|
|
NumberLeafs_r( tree->headnode );
|
|
|
|
// write the file
|
|
if( leaked ) MsgDev( D_INFO, "^3BSP probably has a leak, writing portal file:^7 %s.prt\n", shortname );
|
|
else MsgDev( D_INFO, "^2BSP generation successful, writing portal file:^7 %s.prt\n", shortname );
|
|
pf = fopen( g_portfilename, "w" );
|
|
if( !pf ) COM_FatalError( "error opening %s\n", g_portfilename );
|
|
|
|
fprintf( pf, "%i\n", num_visleafs );
|
|
fprintf( pf, "%i\n", num_visportals );
|
|
|
|
WriteLeafCount_r( tree->headnode );
|
|
WritePortalFile_r( tree->headnode );
|
|
|
|
fclose( pf );
|
|
|
|
MsgDev( D_REPORT, "%i colinear portals\n", c_colinear_portals );
|
|
MsgDev( D_REPORT, "%i backward portals\n", c_backward_portals );
|
|
}
|
|
|
|
|
|
//===================================================
|
|
void FreePortals( node_t *node )
|
|
{
|
|
portal_t *p, *nextp;
|
|
|
|
if( node->children[0] )
|
|
FreePortals( node->children[0] );
|
|
|
|
if( node->children[1] )
|
|
FreePortals( node->children[1] );
|
|
|
|
for( p = node->portals; p != NULL; 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 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
FreeTreePortals
|
|
==================
|
|
*/
|
|
void FreeTreePortals( tree_t *tree )
|
|
{
|
|
FreePortals( tree->headnode );
|
|
} |