2010-05-22 22:00:00 +02:00
|
|
|
|
//=======================================================================
|
|
|
|
|
// Copyright XashXT Group 2007 <20>
|
|
|
|
|
// cm_portals.c - server visibility
|
|
|
|
|
//=======================================================================
|
|
|
|
|
|
|
|
|
|
#include "cm_local.h"
|
2010-05-25 22:00:00 +02:00
|
|
|
|
#include "mathlib.h"
|
2010-05-22 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
static byte fatpvs[MAX_MAP_LEAFS/8];
|
|
|
|
|
static byte fatphs[MAX_MAP_LEAFS/8];
|
2010-05-25 22:00:00 +02:00
|
|
|
|
static byte *bitvector;
|
|
|
|
|
static int fatbytes;
|
2010-05-22 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
===================
|
|
|
|
|
CM_DecompressVis
|
|
|
|
|
===================
|
|
|
|
|
*/
|
|
|
|
|
static byte *CM_DecompressVis( const byte *in )
|
|
|
|
|
{
|
2010-05-24 22:00:00 +02:00
|
|
|
|
static byte decompressed[MAX_MAP_LEAFS/8];
|
|
|
|
|
int c, row;
|
|
|
|
|
byte *out;
|
2010-05-22 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
if( !worldmodel )
|
|
|
|
|
{
|
|
|
|
|
Host_Error( "CM_DecompressVis: no worldmodel\n" );
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
row = (worldmodel->numleafs + 7)>>3;
|
2010-05-24 22:00:00 +02:00
|
|
|
|
out = decompressed;
|
2010-05-22 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
if( !in )
|
|
|
|
|
{
|
|
|
|
|
// no vis info, so make all visible
|
|
|
|
|
while( row )
|
|
|
|
|
{
|
|
|
|
|
*out++ = 0xff;
|
|
|
|
|
row--;
|
|
|
|
|
}
|
2010-05-24 22:00:00 +02:00
|
|
|
|
return decompressed;
|
2010-05-22 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if( *in )
|
|
|
|
|
{
|
|
|
|
|
*out++ = *in++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c = in[1];
|
|
|
|
|
in += 2;
|
|
|
|
|
|
|
|
|
|
while( c )
|
|
|
|
|
{
|
|
|
|
|
*out++ = 0;
|
|
|
|
|
c--;
|
|
|
|
|
}
|
2010-05-24 22:00:00 +02:00
|
|
|
|
} while( out - decompressed < row );
|
2010-05-22 22:00:00 +02:00
|
|
|
|
|
2010-05-24 22:00:00 +02:00
|
|
|
|
return decompressed;
|
2010-05-22 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
===============================================================================
|
|
|
|
|
|
|
|
|
|
PVS / PHS
|
|
|
|
|
|
|
|
|
|
===============================================================================
|
|
|
|
|
*/
|
|
|
|
|
/*
|
|
|
|
|
=================
|
|
|
|
|
CM_CalcPHS
|
|
|
|
|
=================
|
|
|
|
|
*/
|
|
|
|
|
void CM_CalcPHS( void )
|
|
|
|
|
{
|
|
|
|
|
int i, j, k, l, index, num;
|
|
|
|
|
int rowbytes, rowwords;
|
2010-05-27 22:00:00 +02:00
|
|
|
|
byte *scan, *visdata;
|
2010-05-22 22:00:00 +02:00
|
|
|
|
uint *dest, *src;
|
|
|
|
|
int hcount, vcount;
|
|
|
|
|
uint timestart;
|
2010-05-27 22:00:00 +02:00
|
|
|
|
int bitbyte;
|
2010-05-22 22:00:00 +02:00
|
|
|
|
|
2010-05-27 22:00:00 +02:00
|
|
|
|
if( !worldmodel || !cm.pvs )
|
2010-05-24 22:00:00 +02:00
|
|
|
|
return;
|
|
|
|
|
|
2010-05-27 22:00:00 +02:00
|
|
|
|
MsgDev( D_INFO, "Building PAS...\n" );
|
2010-05-22 22:00:00 +02:00
|
|
|
|
timestart = Sys_Milliseconds();
|
|
|
|
|
|
|
|
|
|
num = worldmodel->numleafs;
|
|
|
|
|
rowwords = (num + 31)>>5;
|
|
|
|
|
rowbytes = rowwords * 4;
|
|
|
|
|
|
2010-05-27 22:00:00 +02:00
|
|
|
|
// store off pointer to compressed visdata
|
|
|
|
|
// for right freeing after building pas
|
|
|
|
|
visdata = cm.pvs;
|
|
|
|
|
|
|
|
|
|
// allocate pvs and phs data single array
|
|
|
|
|
cm.pvs = Mem_Alloc( worldmodel->mempool, rowbytes * num * 2 );
|
|
|
|
|
cm.phs = cm.pvs + rowbytes * num;
|
|
|
|
|
|
2010-05-22 22:00:00 +02:00
|
|
|
|
scan = cm.pvs;
|
|
|
|
|
vcount = 0;
|
|
|
|
|
|
|
|
|
|
// uncompress pvs first
|
|
|
|
|
for( i = 0; i < num; i++, scan += rowbytes )
|
|
|
|
|
{
|
2010-05-27 22:00:00 +02:00
|
|
|
|
byte *visblock;
|
2010-05-22 22:00:00 +02:00
|
|
|
|
|
2010-05-27 22:00:00 +02:00
|
|
|
|
visblock = CM_DecompressVis( worldmodel->leafs[i].visdata );
|
|
|
|
|
Mem_Copy( scan, visblock, rowbytes );
|
2010-05-22 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
if( i == 0 ) continue;
|
|
|
|
|
|
|
|
|
|
for( j = 0; j < num; j++ )
|
|
|
|
|
{
|
|
|
|
|
if( scan[j>>3] & (1<<( j & 7 )))
|
|
|
|
|
vcount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-27 22:00:00 +02:00
|
|
|
|
// free compressed pvsdata
|
|
|
|
|
Mem_Free( visdata );
|
2010-05-22 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
scan = cm.pvs;
|
|
|
|
|
hcount = 0;
|
|
|
|
|
|
|
|
|
|
dest = (uint *)cm.phs;
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < num; i++, dest += rowwords, scan += rowbytes )
|
|
|
|
|
{
|
|
|
|
|
Mem_Copy( dest, scan, rowbytes );
|
|
|
|
|
|
|
|
|
|
for( j = 0; j < rowbytes; j++ )
|
|
|
|
|
{
|
|
|
|
|
bitbyte = scan[j];
|
|
|
|
|
if( !bitbyte ) continue;
|
|
|
|
|
|
|
|
|
|
for( k = 0; k < 8; k++ )
|
|
|
|
|
{
|
|
|
|
|
if(!( bitbyte & ( 1<<k )))
|
|
|
|
|
continue;
|
|
|
|
|
// or this pvs row into the phs
|
|
|
|
|
// +1 because pvs is 1 based
|
|
|
|
|
index = ((j<<3) + k + 1);
|
|
|
|
|
if( index >= num ) continue;
|
|
|
|
|
|
|
|
|
|
src = (uint *)cm.pvs + index * rowwords;
|
|
|
|
|
for( l = 0; l < rowwords; l++ )
|
|
|
|
|
dest[l] |= src[l];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-27 22:00:00 +02:00
|
|
|
|
// store new leaf pointers for pvs and pas data
|
|
|
|
|
worldmodel->leafs[i].visdata = scan;
|
|
|
|
|
worldmodel->leafs[i].pasdata = (byte *)dest;
|
|
|
|
|
|
2010-05-22 22:00:00 +02:00
|
|
|
|
if( i == 0 ) continue;
|
|
|
|
|
|
|
|
|
|
for( j = 0; j < num; j++ )
|
|
|
|
|
{
|
|
|
|
|
if(((byte *)dest)[j>>3] & (1<<( j & 7 )))
|
|
|
|
|
hcount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-27 22:00:00 +02:00
|
|
|
|
MsgDev( D_INFO, "Average leaves visible / audible / total: %i / %i / %i\n", vcount / num, hcount / num, num );
|
|
|
|
|
MsgDev( D_INFO, "PAS building time: %g secs\n", (Sys_Milliseconds() - timestart) * 0.001f );
|
2010-05-22 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
=================
|
|
|
|
|
CM_LeafPVS
|
|
|
|
|
=================
|
|
|
|
|
*/
|
|
|
|
|
byte *CM_LeafPVS( int leafnum )
|
|
|
|
|
{
|
|
|
|
|
if( !worldmodel || leafnum < 0 || leafnum >= worldmodel->numleafs || !cm.pvs )
|
|
|
|
|
return cm.nullrow;
|
2010-05-27 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
return worldmodel->leafs[leafnum].visdata;
|
|
|
|
|
// return cm.pvs + leafnum * sizeof( int ) * ((worldmodel->numleafs + 31)>>5);
|
2010-05-22 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
=================
|
|
|
|
|
CM_LeafPHS
|
|
|
|
|
=================
|
|
|
|
|
*/
|
|
|
|
|
byte *CM_LeafPHS( int leafnum )
|
|
|
|
|
{
|
|
|
|
|
if( !worldmodel || leafnum < 0 || leafnum >= worldmodel->numleafs || !cm.phs )
|
|
|
|
|
return cm.nullrow;
|
2010-05-27 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
return worldmodel->leafs[leafnum].pasdata;
|
|
|
|
|
// return cm.phs + leafnum * sizeof( int ) * ((worldmodel->numleafs + 31)>>5);
|
2010-05-22 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2010-05-25 22:00:00 +02:00
|
|
|
|
=============================================================================
|
2010-05-22 22:00:00 +02:00
|
|
|
|
|
2010-05-25 22:00:00 +02:00
|
|
|
|
The PVS must include a small area around the client to allow head bobbing
|
|
|
|
|
or other small motion on the client side. Otherwise, a bob might cause an
|
|
|
|
|
entity that should be visible to not show up, especially when the bob
|
|
|
|
|
crosses a waterline.
|
|
|
|
|
|
|
|
|
|
=============================================================================
|
2010-05-22 22:00:00 +02:00
|
|
|
|
*/
|
2010-05-27 22:00:00 +02:00
|
|
|
|
static void CM_AddToFatPVS( const vec3_t org, int type, cnode_t *node )
|
2010-05-22 22:00:00 +02:00
|
|
|
|
{
|
2010-05-27 22:00:00 +02:00
|
|
|
|
byte *vis;
|
2010-05-25 22:00:00 +02:00
|
|
|
|
cplane_t *plane;
|
|
|
|
|
float d;
|
2010-05-22 22:00:00 +02:00
|
|
|
|
|
2010-05-25 22:00:00 +02:00
|
|
|
|
while( 1 )
|
|
|
|
|
{
|
|
|
|
|
// if this is a leaf, accumulate the pvs bits
|
|
|
|
|
if( node->contents < 0 )
|
|
|
|
|
{
|
|
|
|
|
if( node->contents != CONTENTS_SOLID )
|
|
|
|
|
{
|
|
|
|
|
cleaf_t *leaf;
|
|
|
|
|
int i;
|
2010-05-22 22:00:00 +02:00
|
|
|
|
|
2010-05-25 22:00:00 +02:00
|
|
|
|
leaf = (cleaf_t *)node;
|
2010-05-27 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
if( type == DVIS_PVS )
|
|
|
|
|
vis = leaf->visdata;
|
|
|
|
|
else if( type == DVIS_PHS )
|
|
|
|
|
vis = leaf->pasdata;
|
|
|
|
|
else vis = cm.nullrow;
|
2010-05-22 22:00:00 +02:00
|
|
|
|
|
2010-05-25 22:00:00 +02:00
|
|
|
|
for( i = 0; i < fatbytes; i++ )
|
2010-05-27 22:00:00 +02:00
|
|
|
|
bitvector[i] |= vis[i];
|
2010-05-25 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
plane = node->plane;
|
|
|
|
|
d = DotProduct( org, plane->normal ) - plane->dist;
|
2010-05-27 22:00:00 +02:00
|
|
|
|
if( d > 8 ) node = node->children[0];
|
|
|
|
|
else if( d < -8 ) node = node->children[1];
|
2010-05-25 22:00:00 +02:00
|
|
|
|
else
|
2010-05-22 22:00:00 +02:00
|
|
|
|
{
|
2010-05-25 22:00:00 +02:00
|
|
|
|
// go down both
|
2010-05-27 22:00:00 +02:00
|
|
|
|
CM_AddToFatPVS( org, type, node->children[0] );
|
2010-05-25 22:00:00 +02:00
|
|
|
|
node = node->children[1];
|
2010-05-22 22:00:00 +02:00
|
|
|
|
}
|
2010-05-25 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2010-05-22 22:00:00 +02:00
|
|
|
|
|
2010-05-25 22:00:00 +02:00
|
|
|
|
/*
|
|
|
|
|
============
|
|
|
|
|
CM_FatPVS
|
2010-05-22 22:00:00 +02:00
|
|
|
|
|
2010-05-25 22:00:00 +02:00
|
|
|
|
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 )
|
|
|
|
|
{
|
|
|
|
|
bitvector = fatpvs;
|
|
|
|
|
fatbytes = (worldmodel->numleafs+31)>>3;
|
|
|
|
|
if( !portal ) Mem_Set( bitvector, 0, fatbytes );
|
2010-05-27 22:00:00 +02:00
|
|
|
|
CM_AddToFatPVS( org, DVIS_PVS, worldmodel->nodes );
|
2010-05-25 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
return bitvector;
|
2010-05-22 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
============
|
|
|
|
|
CM_FatPHS
|
|
|
|
|
|
|
|
|
|
The client will interpolate the hear position,
|
|
|
|
|
so we can't use a single PHS point
|
|
|
|
|
===========
|
|
|
|
|
*/
|
|
|
|
|
byte *CM_FatPHS( const vec3_t org, bool portal )
|
|
|
|
|
{
|
2010-05-27 22:00:00 +02:00
|
|
|
|
#if 1
|
2010-05-25 22:00:00 +02:00
|
|
|
|
bitvector = fatphs;
|
|
|
|
|
fatbytes = (worldmodel->numleafs+31)>>3;
|
|
|
|
|
if( !portal ) Mem_Set( bitvector, 0, fatbytes );
|
2010-05-27 22:00:00 +02:00
|
|
|
|
CM_AddToFatPVS( org, DVIS_PHS, worldmodel->nodes );
|
2010-05-25 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
return bitvector;
|
|
|
|
|
#else
|
2010-05-22 22:00:00 +02:00
|
|
|
|
int longs, leafnum;
|
|
|
|
|
|
|
|
|
|
longs = (worldmodel->numleafs + 31)>>5;
|
|
|
|
|
leafnum = CM_PointLeafnum( org );
|
|
|
|
|
|
|
|
|
|
if( !portal )
|
|
|
|
|
{
|
|
|
|
|
Mem_Copy( fatphs, CM_LeafPHS( leafnum ), longs << 2 );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
byte *src;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
// or in all the other leaf bits
|
|
|
|
|
src = CM_LeafPHS( leafnum );
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < longs; i++ )
|
|
|
|
|
((int *)fatphs)[i] |= ((int *)src)[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fatphs;
|
2010-05-25 22:00:00 +02:00
|
|
|
|
#endif
|
2010-05-22 22:00:00 +02:00
|
|
|
|
}
|