Paranoia2/utils/p2bsp/outside.cpp
2020-08-31 19:50:41 +03:00

532 lines
10 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"
int outleafs;
int valid;
int c_falsenodes;
int c_free_faces;
int c_keep_faces;
portal_t *prevleaknode;
FILE *pointfile = NULL;
FILE *linefile = NULL;
int hit_occupied;
int backdraw;
/*
===========
PointInLeaf
===========
*/
node_t *PointInLeaf( node_t *node, const vec3_t point )
{
while( !FBitSet( node->flags, FNODE_LEAFPORTAL ))
node = node->children[PlaneDiff( point, &g_mapplanes[node->planenum] ) <= 0];
return node;
}
/*
===========
PlaceOccupant
===========
*/
bool PlaceOccupant( int num, vec3_t point, node_t *headnode )
{
node_t *n;
n = PointInLeaf( headnode, point );
if( n->contents == CONTENTS_SOLID )
return false;
n->occupied = num;
return true;
}
/*
=============
Portal_Passable
Returns true if the portal has non-opaque leafs on both sides
from q3map
=============
*/
static bool Portal_Passable( const portal_t *p )
{
if( p->nodes[0] == &g_outside_node || p->nodes[1] == &g_outside_node )
return false;
#if 0 // this break monster navigation
if( p->nodes[0]->opaque() || p->nodes[1]->opaque( ))
return false;
#endif
return true;
}
/*
==============
MarkLeakTrail
==============
*/
void MarkLeakTrail( portal_t *n2 )
{
vec3_t p1, p2, dir;
vec_t len;
portal_t *n1;
n1 = prevleaknode;
prevleaknode = n2;
if( !n1 ) return;
WindingCenter( n1->winding, p1 );
WindingCenter( n2->winding, p2 );
// NOTE: create only if there was a leak.
if( !pointfile ) pointfile = fopen( g_pointfilename, "w" );
if( !pointfile ) COM_FatalError( "couldn't open %s\n", g_pointfilename );
if( !linefile ) linefile = fopen( g_linefilename, "w" );
if( !linefile ) COM_FatalError( "couldn't open %s\n", g_linefilename );
fprintf( linefile, "%f %f %f - %f %f %f\n", p1[0], p1[1], p1[2], p2[0], p2[1], p2[2] );
fprintf( pointfile, "%f %f %f\n", p1[0], p1[1], p1[2] );
VectorSubtract( p2, p1, dir );
len = VectorLength( dir );
VectorNormalize( dir );
while( len > 2.0 )
{
fprintf( pointfile,"%f %f %f\n", p1[0], p1[1], p1[2] );
VectorMA( p1, 2.0, dir, p1 );
len -= 2.0;
}
}
/*
==============
FreeDetailNode_r
==============
*/
static void FreeDetailNode_r( node_t *node )
{
face_t *f, *next;
if( node->planenum == PLANENUM_LEAF )
{
if(!( FBitSet( node->flags, FNODE_LEAFPORTAL ) && node->contents == CONTENTS_SOLID ))
{
Mem_Free( node->markfaces );
node->markfaces = NULL;
}
return;
}
for( int i = 0; i < 2; i++ )
{
FreeDetailNode_r( node->children[i] );
FreeNode( node->children[i] );
node->children[i] = NULL;
}
for( f = node->faces; f != NULL; f = next )
{
next = f->next;
FreeFace( f );
}
node->faces = NULL;
}
/*
==============
FillLeaf
==============
*/
static void FillLeaf( node_t *l )
{
if( !FBitSet( l->flags, FNODE_LEAFPORTAL ))
{
MsgDev( D_WARN, "FillLeaf: not leaf\n" );
return;
}
if( l->contents == CONTENTS_SOLID )
{
MsgDev( D_WARN, "FillLeaf: fill solid\n" );
return;
}
FreeDetailNode_r( l );
l->contents = CONTENTS_SOLID;
l->planenum = PLANENUM_LEAF;
}
/*
==================
RecursiveFillOutside
If fill is false, just check, don't fill
Returns true if an occupied leaf is reached
==================
*/
bool RecursiveFillOutside( node_t *l, bool fill, bool leakfile )
{
portal_t *p;
int s;
if( l->opaque() )
return false;
if( l->valid == valid )
return false;
if( l->occupied )
{
hit_occupied = l->occupied;
backdraw = 1000;
return true;
}
l->valid = valid;
// fill it and it's neighbors
if( fill ) FillLeaf( l );
outleafs++;
for( p = l->portals; p != NULL; )
{
s = (p->nodes[0] == l);
if( Portal_Passable( p ) && RecursiveFillOutside( p->nodes[s], fill, leakfile ))
{
// leaked, so stop filling
if( backdraw-- > 0 && leakfile )
MarkLeakTrail( p );
return true;
}
p = p->next[!s];
}
return false;
}
static void MarkFacesInside_r( node_t *node )
{
if( node->planenum == PLANENUM_LEAF )
{
for( face_t **fp = node->markfaces; *fp != NULL; fp++ )
(*fp)->outputnumber = 0;
}
else
{
MarkFacesInside_r( node->children[0] );
MarkFacesInside_r( node->children[1] );
}
}
/*
==================
ClearOutFaces_r
Removes unused nodes
==================
*/
node_t *ClearOutFaces_r( node_t *node )
{
face_t *f, *fnext;
portal_t *p;
// mark the node and all it's faces, so they
// can be removed if no children use them
node->valid = 0; // will be set if any children touch it
for( f = node->faces; f != NULL; f = f->next )
f->outputnumber = -1;
// go down the children
if( !FBitSet( node->flags, FNODE_LEAFPORTAL ))
{
// decision node
node->children[0] = ClearOutFaces_r( node->children[0] );
node->children[1] = ClearOutFaces_r( node->children[1] );
// free any faces not in open child leafs
f = node->faces;
node->faces = NULL;
for( ; f != NULL; f = fnext )
{
fnext = f->next;
if( f->outputnumber == -1 )
{
// never referenced, so free it
c_free_faces++;
FreeFace( f );
}
else
{
c_keep_faces++;
f->next = node->faces;
node->faces = f;
}
}
// TODO: free memory
if( !node->valid )
{
// this node does not touch any interior leafs
// if both children are solid, just make this node solid
if( node->children[0]->contents == CONTENTS_SOLID && node->children[1]->contents == CONTENTS_SOLID )
{
SetBits( node->flags, FNODE_LEAFPORTAL );
node->contents = CONTENTS_SOLID;
node->planenum = PLANENUM_LEAF;
c_splitnodes--;
return node;
}
// if one child is solid, shortcut down the other side
if( node->children[0]->contents == CONTENTS_SOLID )
{
c_splitnodes--;
return node->children[1];
}
if( node->children[1]->contents == CONTENTS_SOLID )
{
c_splitnodes--;
return node->children[0];
}
c_falsenodes++;
}
return node;
}
// leaf node
if( node->contents != CONTENTS_SOLID )
{
// this node is still inside
// mark all the nodes used as portals
for( p = node->portals; p != NULL; )
{
if( p->onnode )
p->onnode->valid = 1;
// only write out from first leaf
if( p->nodes[0] == node )
p = p->next[0];
else p = p->next[1];
}
MarkFacesInside_r( node );
return node;
}
return node;
}
/*
===========
FillOutside
===========
*/
bool FillOutside( tree_t *tree, int hullnum, bool leakfile )
{
bool inside = false;
node_t *node = tree->headnode;
bool leaked = false;
int s, i, c_nodes;
vec3_t origin;
MsgDev( D_REPORT, "----- FillOutside ----\n" );
c_nodes = c_splitnodes;
// place markers for all entities so
// we know if we leak inside
for( i = 1; i < g_numentities; i++ )
{
entity_t *mapent = &g_entities[i];
GetVectorForKey( mapent, "origin", origin );
// make sure what entity doesn't have brush-model
if( !VectorIsNull( origin ) && ValueForKey( mapent, "model" )[0] != '*' )
{
origin[2] += 1.0;
if( PlaceOccupant( i, origin, node ))
inside = true;
}
}
if( !inside )
{
Msg( "%i nodes (not optimized)\n", c_nodes );
MsgDev( D_ERROR, "No entities in empty space -- no filling performed\n" );
return false;
}
s = !(g_outside_node.portals->nodes[1] == &g_outside_node);
// first check to see if an occupied leaf is hit
prevleaknode = NULL;
outleafs = 0;
valid++;
if( RecursiveFillOutside( g_outside_node.portals->nodes[s], false, leakfile ))
{
if( pointfile ) fclose( pointfile );
if( linefile ) fclose( linefile );
pointfile = linefile = NULL;
leaked = true;
// do animation
MsgAnim( D_INFO, "^3=== LEAK in hull %i ===\r", hullnum );
GetVectorForKey( &g_entities[hit_occupied], "origin", origin );
MsgDev( D_REPORT, "\nEntity %s @ (%4.0f,%4.0f,%4.0f)\n", origin[0], origin[1], origin[2] );
MsgDev( D_REPORT, "no filling performed\n" );
MsgDev( D_REPORT, "point file and line file generated\n" );
// allow to vis maps even with leak
if( g_forcevis == false )
{
Msg( "%i nodes (not optimized)\n", c_nodes );
return false;
}
}
// now go back and fill things in
valid++;
RecursiveFillOutside( g_outside_node.portals->nodes[s], true, false );
// remove faces and nodes from filled in leafs
c_falsenodes = 0;
c_free_faces = 0;
c_keep_faces = 0;
tree->headnode = ClearOutFaces_r( node );
MsgDev( D_REPORT, "%5i outleafs\n", outleafs );
MsgDev( D_REPORT, "%5i freed faces\n", c_free_faces );
MsgDev( D_REPORT, "%5i keep faces\n", c_keep_faces );
MsgDev( D_REPORT, "%5i falsenodes\n", c_falsenodes );
Msg( "%i nodes (%i after merging)\n", c_nodes, c_splitnodes );
// save portal file for vis tracing
if( leakfile ) WritePortalfile( tree, leaked );
return true;
}
//=============================================================================
/*
===========
ResetMark_r
===========
*/
void ResetMark_r( node_t *node )
{
if( FBitSet( node->flags, FNODE_LEAFPORTAL ))
{
if( node->opaque( ))
ClearBits( node->flags, FNODE_EMPTY );
else SetBits( node->flags, FNODE_EMPTY );
}
else
{
ResetMark_r( node->children[0] );
ResetMark_r( node->children[1] );
}
}
/*
===========
MarkOccupied_r
===========
*/
void MarkOccupied_r( node_t *node )
{
portal_t *p;
int s;
if( FBitSet( node->flags, FNODE_EMPTY ))
{
ClearBits( node->flags, FNODE_EMPTY );
for( p = node->portals; p != NULL; p = p->next[!s] )
{
s = (p->nodes[0] == node);
MarkOccupied_r( p->nodes[s] );
}
}
}
/*
===========
RemoveUnused_r
===========
*/
void RemoveUnused_r( node_t *node )
{
if( FBitSet( node->flags, FNODE_LEAFPORTAL ))
{
if( FBitSet( node->flags, FNODE_EMPTY ))
{
FillLeaf( node );
}
}
else
{
RemoveUnused_r( node->children[0] );
RemoveUnused_r( node->children[1] );
}
}
/*
===========
FillInside
===========
*/
void FillInside( node_t *node )
{
vec3_t origin;
node_t *innode;
ClearBits( g_outside_node.flags, FNODE_EMPTY );
ResetMark_r( node );
for( int i = 1; i < g_numentities; i++ )
{
entity_t *mapent = &g_entities[i];
if( CheckKey( mapent, "origin" ))
{
GetVectorForKey( mapent, "origin", origin );
origin[2] += 1.0;
innode = PointInLeaf( node, origin );
MarkOccupied_r( innode );
origin[2] -= 2.0;
innode = PointInLeaf( node, origin );
MarkOccupied_r( innode );
}
}
RemoveUnused_r( node );
}