This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/physic/cm_portals.c

284 lines
6.3 KiB
C

//=======================================================================
// Copyright XashXT Group 2007 ©
// cm_portals.c - server visibility
//=======================================================================
#include "cm_local.h"
static byte fatpvs[MAX_MAP_LEAFS/8];
/*
===============================================================================
PVS / PHS
===============================================================================
*/
/*
===================
CM_DecompressVis
===================
*/
void CM_DecompressVis( byte *in, byte *out )
{
byte *out_p;
int c, row;
row = (cm.numclusters + 7)>>3;
out_p = out;
if( !in )
{
// no vis info, so make all visible
while( row )
{
*out_p++ = 0xff;
row--;
}
return;
}
do
{
if(*in)
{
*out_p++ = *in++;
continue;
}
c = in[1];
in += 2;
if((out_p - out) + c > row)
{
c = row - (out_p - out);
MsgDev(D_WARN, "CM_DecompressVis: decompression overrun\n");
}
while( c )
{
*out_p++ = 0;
c--;
}
} while( out_p - out < row );
}
byte *CM_ClusterPVS( int cluster )
{
if( cluster < 0 || cluster >= cm.numclusters || !cm.vis )
Mem_Set( cm.pvsrow, 0xFF, (cm.numclusters + 31) & ~31 );
else CM_DecompressVis( cm.visbase + cm.vis->bitofs[cluster][DVIS_PVS], cm.pvsrow );
return cm.pvsrow;
}
byte *CM_ClusterPHS( int cluster )
{
if( cluster < 0 || cluster >= cm.numclusters || !cm.vis )
Mem_Set( cm.phsrow, 0xFF, (cm.numclusters + 31) & ~31 );
else CM_DecompressVis( cm.visbase + cm.vis->bitofs[cluster][DVIS_PHS], cm.phsrow );
return cm.phsrow;
}
/*
============
CM_FatPVS
The client will interpolate the view position,
so we can't use a single PVS point
===========
*/
byte *CM_FatPVS( const vec3_t org, bool portal )
{
int leafs[128];
int i, j, count;
int longs;
byte *src;
vec3_t mins, maxs;
int snap = portal ? 1 : 8;
for( i = 0; i < 3; i++ )
{
mins[i] = org[i] - snap;
maxs[i] = org[i] + snap;
}
count = CM_BoxLeafnums( mins, maxs, leafs, 128, NULL );
if( count < 1 ) Host_Error( "CM_FatPVS: invalid leafnum count\n" );
longs = (CM_NumClusters() + 31)>>5;
// convert leafs to clusters
for( i = 0; i < count; i++ ) leafs[i] = CM_LeafCluster( leafs[i] );
if( !portal ) Mem_Copy( fatpvs, CM_ClusterPVS( leafs[0] ), longs<<2 );
// or in all the other leaf bits
for( i = portal ? 0 : 1; i < count; i++ )
{
for( j = 0; j < i; j++ )
{
if( leafs[i] == leafs[j] )
break;
}
if( j != i ) continue; // already have the cluster we want
src = CM_ClusterPVS( leafs[i] );
for( j = 0; j < longs; j++ ) ((long *)fatpvs)[j] |= ((long *)src)[j];
}
return fatpvs;
}
/*
===============================================================================
AREAPORTALS
===============================================================================
*/
void CM_FloodArea_r( carea_t *area, int floodnum )
{
int i;
dareaportal_t *p;
if( area->floodvalid == cm.floodvalid )
{
if( area->floodnum == floodnum ) return;
Host_Error( "CM_FloodArea_r: reflooded\n" );
}
area->floodnum = floodnum;
area->floodvalid = cm.floodvalid;
p = &cm.areaportals[area->firstareaportal];
for( i = 0; i < area->numareaportals; i++, p++ )
{
if( cm.portalopen[p->portalnum] )
CM_FloodArea_r( &cm.areas[p->otherarea], floodnum );
}
}
/*
====================
CM_FloodAreaConnections
====================
*/
void CM_FloodAreaConnections( void )
{
carea_t *area;
int i, floodnum = 0;
// all current floods are now invalid
cm.floodvalid++;
// area 0 is not used
for( i = 1; i < cm.numareas; i++ )
{
area = &cm.areas[i];
if( area->floodvalid == cm.floodvalid )
continue; // already flooded into
floodnum++;
CM_FloodArea_r( area, floodnum );
}
}
void CM_SetAreaPortals ( byte *portals, size_t size )
{
if( size == sizeof( cm.portalopen ))
{
Mem_Copy( cm.portalopen, portals, size );
CM_FloodAreaConnections();
return;
}
MsgDev( D_ERROR, "CM_SetAreaPortals: portals mismatch size (%i should be %i)\n", size, cm.numareaportals );
}
void CM_GetAreaPortals ( byte **portals, size_t *size )
{
byte *prt = *portals;
if( prt ) Mem_Copy( prt, cm.portalopen, sizeof( cm.portalopen ));
if( size) *size = sizeof( cm.portalopen );
}
void CM_SetAreaPortalState( int portalnum, bool open )
{
if( portalnum > cm.numareaportals )
Host_Error( "CM_SetAreaPortalState: areaportal > numareaportals\n" );
cm.portalopen[portalnum] = open;
CM_FloodAreaConnections();
}
bool CM_AreasConnected( int area, int otherarea )
{
if( cm_noareas->integer ) return true;
if( area > cm.numareas || otherarea > cm.numareas )
Host_Error("CM_AreasConnected: area >= cm.numareas\n" );
if( !area || !otherarea ) return false;
if( area == otherarea ) return true; // quick test
if( cm.areas[area].floodnum == cm.areas[otherarea].floodnum )
return true;
return false;
}
/*
=================
CM_WriteAreaBits
Writes a length byte followed by a bit vector of all the areas
that area in the same flood as the area parameter
This is used by the client refreshes to cull visibility
=================
*/
int CM_WriteAreaBits( byte *buffer, int area, bool portal )
{
int i, size;
size = (cm.numareas + 7)>>3;
if( cm_noareas->integer )
{
if( !portal ) Mem_Set( buffer, 0xFF, size );
}
else
{
if( !portal ) Mem_Set( buffer, 0x00, size );
for( i = 0; i < cm.numareas; i++ )
{
if(CM_AreasConnected( i, area ) || !area )
{
if( !portal ) buffer[i>>3] |= 1 << (i & 7);
else buffer[i>>3] |= 1 << (i & 7) ^ ~0;
}
}
}
return size;
}
/*
=============
CM_HeadnodeVisible
Returns true if any leaf under headnode has a cluster that
is potentially visible
=============
*/
bool CM_HeadnodeVisible( int nodenum, byte *visbits )
{
int leafnum, cluster;
cnode_t *node;
if( nodenum < 0 )
{
leafnum = -1-nodenum;
cluster = cm.leafs[leafnum].cluster;
if( cluster == -1 ) return false;
if( visbits[cluster>>3] & (1<<(cluster&7)))
return true;
return false;
}
node = &cm.nodes[nodenum];
if( CM_HeadnodeVisible( node->children[0] - cm.nodes, visbits ))
return true;
return CM_HeadnodeVisible( node->children[1] - cm.nodes, visbits );
}