Paranoia2/cl_dll/util.cpp

1421 lines
32 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.
*
* Use, distribution, and modification of this source code and/or resulting
* object code is restricted to non-commercial enhancements to products from
* Valve LLC. All other use, distribution, or modification is prohibited
* without written permission from Valve LLC.
*
****/
//
// util.cpp
//
// implementation of class-less helper functions
//
#include "STDIO.H"
#include "STDLIB.H"
#include "MATH.H"
#include "hud.h"
#include "cl_util.h"
#include <stringlib.h>
#include "com_model.h"
#include "triangleapi.h"
#include <mathlib.h>
#include "gl_local.h"
#include "event_api.h"
#include "r_efx.h"
#include "pmtrace.h"
#include "gl_studio.h"
#include "gl_world.h"
static byte decompressed[MAX_MAP_LEAFS/8];
/*
=============
pfnAlertMessage
=============
*/
void ALERT( ALERT_TYPE level, char *szFmt, ... )
{
char buffer[2048]; // must support > 1k messages
va_list args;
if( developer_level <= DEV_NONE )
return;
if( level == at_aiconsole && developer_level < DEV_EXTENDED )
return;
va_start( args, szFmt );
Q_vsnprintf( buffer, 2048, szFmt, args );
va_end( args );
if( level == at_warning )
{
gEngfuncs.Con_Printf( va( "^3Warning:^7 %s", buffer ));
}
else if( level == at_error )
{
gEngfuncs.Con_Printf( va( "^1Error:^7 %s", buffer ));
}
else
{
gEngfuncs.Con_Printf( buffer );
}
}
/*
====================
Sys LoadGameDLL
====================
*/
bool Sys_LoadLibrary( const char* dllname, dllhandle_t* handle, const dllfunc_t *fcts )
{
if( !handle ) return false;
const dllfunc_t *gamefunc;
// Initializations
for( gamefunc = fcts; gamefunc && gamefunc->name != NULL; gamefunc++ )
*gamefunc->func = NULL;
char dllpath[128];
// is direct path used ?
if( dllname[0] == '*' ) Q_strncpy( dllpath, dllname + 1, sizeof( dllpath ));
else Q_snprintf( dllpath, sizeof( dllpath ), "%s/cl_dlls/%s", gEngfuncs.pfnGetGameDirectory(), dllname );
dllhandle_t dllhandle = LoadLibrary( dllpath );
// No DLL found
if( !dllhandle ) return false;
// Get the function adresses
for( gamefunc = fcts; gamefunc && gamefunc->name != NULL; gamefunc++ )
{
if( !( *gamefunc->func = (void *)Sys_GetProcAddress( dllhandle, gamefunc->name )))
{
Sys_FreeLibrary( &dllhandle );
return false;
}
}
ALERT( at_aiconsole, "%s loaded succesfully!\n", (dllname[0] == '*') ? (dllname+1) : (dllname));
*handle = dllhandle;
return true;
}
void *Sys_GetProcAddress( dllhandle_t handle, const char *name )
{
return (void *)GetProcAddress( handle, name );
}
void Sys_FreeLibrary( dllhandle_t *handle )
{
if( !handle || !*handle )
return;
FreeLibrary( *handle );
*handle = NULL;
}
bool Sys_RemoveFile( const char *path )
{
char real_path[1024];
int iRet = false;
if( !path || !*path )
return false;
Q_snprintf( real_path, sizeof( real_path ), "%s/%s", gEngfuncs.pfnGetGameDirectory(), path );
COM_FixSlashes( real_path );
iRet = remove( real_path );
return (iRet == 0) ? true : false;
}
/*
=============
WorldToScreen
convert world coordinates (x,y,z) into screen (x, y)
=============
*/
int WorldToScreen( const Vector &world, Vector &screen )
{
int retval = R_WorldToScreen( world, screen );
screen[0] = 0.5f * screen[0] * (float)RI->view.port[2];
screen[1] = -0.5f * screen[1] * (float)RI->view.port[3];
screen[0] += 0.5f * (float)RI->view.port[2];
screen[1] += 0.5f * (float)RI->view.port[3];
return retval;
}
//=================
// UTIL_IsPlayer
//=================
bool UTIL_IsPlayer( int idx )
{
if ( idx >= 1 && idx <= gEngfuncs.GetMaxClients() )
return true;
return false;
}
//=================
// UTIL_IsLocal
//=================
bool UTIL_IsLocal( int idx )
{
return gEngfuncs.pEventAPI->EV_IsLocal( idx - 1 ) ? true : false;
}
//=================
// UTIL_WeaponAnimation
//=================
void UTIL_WeaponAnimation( int iAnim, float framerate )
{
cl_entity_t *view = GET_VIEWMODEL();
gEngfuncs.pEventAPI->EV_WeaponAnimation( iAnim, view->curstate.body );
view->curstate.framerate = framerate;
view->curstate.scale = 1.0f;
#if 0
// just for test Glow Shell effect
view->curstate.renderfx = kRenderFxGlowShell;
view->curstate.rendercolor.r = 255;
view->curstate.rendercolor.g = 255;
view->curstate.rendercolor.b = 255;
view->curstate.renderamt = 100;
#endif
}
void UTIL_StudioDecal( const char *pDecalName, pmtrace_t *pTrace, const Vector &vecSrc )
{
g_StudioRenderer.StudioDecalShoot( pTrace, pDecalName, false );
}
/*
====================
SPRITE_GetList
====================
*/
char *ParseHudSprite( char *pfile, char *psz, client_sprite_t *result )
{
client_sprite_t tempSprite;
int x = 0, y = 0, width = 0, height = 0;
int section = 0;
char token[256];
memset( &tempSprite, 0, sizeof( tempSprite ));
memset( result, 0, sizeof( *result ));
while( pfile )
{
pfile = COM_ParseFile( pfile, token );
if( !Q_stricmp( token, psz ))
{
pfile = COM_ParseFile( pfile, token );
if( !Q_stricmp( token, "{" )) section = 1;
}
if( section )
{
if( !Q_stricmp( token, "}" ))
break; // end section
if( !Q_stricmp( token, "file" ))
{
pfile = COM_ParseFile( pfile, token );
Q_strcpy( tempSprite.szSprite, token );
void *testSprite;
if(( testSprite = gEngfuncs.COM_LoadFile( tempSprite.szSprite, 5, NULL )) != NULL )
{
gEngfuncs.COM_FreeFile( testSprite );
// fill structure at default
HSPRITE m_hSprite = SPR_Load( tempSprite.szSprite );
width = SPR_Width( m_hSprite, 0 );
height = SPR_Height( m_hSprite, 0 );
x = y = 0;
}
else
{
return pfile;
}
}
else if( !Q_stricmp( token, "name" ))
{
pfile = COM_ParseFile( pfile, token );
Q_strcpy( tempSprite.szName, token );
}
else if( !Q_stricmp( token, "x" ))
{
pfile = COM_ParseFile( pfile, token );
x = Q_atoi( token );
}
else if( !Q_stricmp( token, "y" ))
{
pfile = COM_ParseFile( pfile, token );
y = Q_atoi( token );
}
else if( !Q_stricmp( token, "width" ))
{
pfile = COM_ParseFile( pfile, token );
width = Q_atoi( token );
}
else if( !Q_stricmp( token, "height" ))
{
pfile = COM_ParseFile( pfile, token );
height = Q_atoi( token );
}
}
}
if( !section )
return pfile; // data not found
// calculate sprite position
tempSprite.rc.left = x;
tempSprite.rc.right = x + width;
tempSprite.rc.top = y;
tempSprite.rc.bottom = y + height;
// write resolution for backward compatibility
if( ScreenWidth < 640 )
tempSprite.iRes = 320;
else tempSprite.iRes = 640;
*result = tempSprite;
return pfile;
}
client_sprite_t *SPR2_GetList( char *psz, int *piCount )
{
int iSprCount = 0;
char *afile = (char *)gEngfuncs.COM_LoadFile( psz, 5, NULL );
if( !afile )
{
*piCount = iSprCount;
return NULL;
}
char token[256];
char *pfile = afile;
while( pfile )
{
// calculate count of sprites
pfile = COM_ParseFile( pfile, token );
if( !Q_stricmp( token, "hudsprite" ))
iSprCount++;
}
client_sprite_t *phud = new client_sprite_t[iSprCount];
pfile = afile;
for( int i = 0; i < iSprCount; i++ )
{
pfile = ParseHudSprite( pfile, "hudsprite", &phud[i] );
}
gEngfuncs.COM_FreeFile( afile );
*piCount = iSprCount;
return phud;
}
/*
======================================================================
LEAF LISTING
NOTE: this code ripped out from Xash3D
======================================================================
*/
static void Mod_BoxLeafnums_r( leaflist_t *ll, mnode_t *node, model_t *worldmodel )
{
int s;
while( 1 )
{
if( node->contents == CONTENTS_SOLID )
return;
if( node->contents < 0 )
{
mleaf_t *leaf = (mleaf_t *)node;
// it's a leaf!
if( ll->count >= ll->maxcount )
{
ll->overflowed = true;
return;
}
ll->list[ll->count++] = leaf->cluster;
return;
}
s = BOX_ON_PLANE_SIDE( ll->mins, ll->maxs, node->plane );
if( s == 1 )
{
node = node->children[0];
}
else if( s == 2 )
{
node = node->children[1];
}
else
{
// go down both
if( ll->headnode == NULL )
ll->headnode = node;
Mod_BoxLeafnums_r( ll, node->children[0], worldmodel );
node = node->children[1];
}
}
}
/*
==================
Mod_BoxLeafnums
==================
*/
int Mod_BoxLeafnums( const Vector &mins, const Vector &maxs, short *list, int listsize, mnode_t **headnode )
{
leaflist_t ll;
model_t *worldmodel;
worldmodel = gEngfuncs.GetEntityByIndex( 0 )->model;
if( headnode ) *headnode = NULL;
if( !worldmodel )
return 0;
ll.mins = mins;
ll.maxs = maxs;
ll.count = 0;
ll.maxcount = listsize;
ll.list = list;
ll.headnode = NULL;
ll.overflowed = false;
Mod_BoxLeafnums_r( &ll, worldmodel->nodes, worldmodel );
if( headnode )
*headnode = ll.headnode;
return ll.count;
}
/*
=============
Mod_HeadnodeVisible
=============
*/
bool Mod_HeadnodeVisible( mnode_t *node, const byte *visbits )
{
if( !node || node->contents == CONTENTS_SOLID )
return false;
if( node->contents < 0 )
{
if( CHECKVISBIT( visbits, ((mleaf_t *)node)->cluster ))
return true;
return false;
}
if( Mod_HeadnodeVisible( node->children[0], visbits ))
return true;
return Mod_HeadnodeVisible( node->children[1], visbits );
}
/*
=============
Mod_BoxVisible
Returns true if any leaf in boxspace
is potentially visible
=============
*/
bool Mod_BoxVisible( const Vector &mins, const Vector &maxs, const byte *visbits )
{
short leafList[48];
leaflist_t ll;
if( !visbits ) return true;
ll.maxcount = ARRAYSIZE( leafList );
ll.list = leafList;
ll.headnode = NULL;
ll.overflowed = false;
ll.mins = mins;
ll.maxs = maxs;
ll.count = 0;
Mod_BoxLeafnums_r( &ll, worldmodel->nodes, worldmodel );
for( int i = 0; i < ll.count; i++ )
{
if( CHECKVISBIT( visbits, leafList[i] ))
return true;
}
if( !ll.overflowed )
return false; // invisible
// overflowed? go by headnode
if( Mod_HeadnodeVisible( ll.headnode, visbits ))
return true;
return false;
}
/*
===================
Mod_DecompressVis
===================
*/
void Mod_DecompressVis( const byte *in, byte *pvs )
{
int c;
byte *out;
int row;
row = (worldmodel->numleafs+7)>>3;
out = pvs;
if( !in )
{ // no vis info, so make all visible
while( row )
{
*out++ = 0xff;
row--;
}
return;
}
do
{
if( *in )
{
*out++ = *in++;
continue;
}
c = in[1];
in += 2;
while( c )
{
*out++ = 0;
c--;
}
} while( out - pvs < row );
}
byte *Mod_LeafPVS( mleaf_t *leaf, model_t *model )
{
if( !model || !leaf || leaf == model->leafs || !model->visdata )
Mod_DecompressVis( NULL, decompressed );
else Mod_DecompressVis( leaf->compressed_vis, decompressed );
return decompressed;
}
/*
==================
Mod_PointInLeaf
==================
*/
mleaf_t *Mod_PointInLeaf( const Vector &p, mnode_t *node )
{
while( 1 )
{
if( node->contents < 0 )
return (mleaf_t *)node;
node = node->children[PlaneDiff( p, node->plane ) < 0];
}
// never reached
return NULL;
}
bool Mod_PointInSolid( const Vector &p )
{
return (Mod_PointInLeaf( p, worldmodel->nodes )->contents == CONTENTS_SOLID) ? true : false;
}
byte *Mod_GetCurrentVis( void )
{
return RI->view.pvsarray;
}
bool Mod_CheckBoxVisible( const Vector &absmin, const Vector &absmax )
{
return Mod_BoxVisible( absmin, absmax, Mod_GetCurrentVis( ));
}
bool Mod_CheckEntityPVS( cl_entity_t *ent )
{
if( !ent || !ent->index )
return false; // not exist on the client
if( ent->curstate.messagenum != r_currentMessageNum )
return false; // already culled by server
Vector mins = ent->curstate.origin + ent->curstate.mins;
Vector maxs = ent->curstate.origin + ent->curstate.maxs;
return Mod_BoxVisible( mins, maxs, Mod_GetCurrentVis( ));
}
bool Mod_CheckEntityLeafPVS( const Vector &absmin, const Vector &absmax, mleaf_t *leaf )
{
return Mod_BoxVisible( absmin, absmax, Mod_GetCurrentVis( ));
}
bool Mod_CheckTempEntityPVS( TEMPENTITY *pTemp )
{
if( !pTemp ) return false; // not exist on the client
Vector mins = pTemp->entity.curstate.origin + pTemp->entity.curstate.mins;
Vector maxs = pTemp->entity.curstate.origin + pTemp->entity.curstate.maxs;
return Mod_BoxVisible( mins, maxs, Mod_GetCurrentVis( ));
}
model_t *Mod_Handle( int modelIndex )
{
return IEngineStudio.GetModelByIndex( modelIndex );
}
int Mod_GetType( int modelIndex )
{
model_t *m_pModel;
m_pModel = IEngineStudio.GetModelByIndex( modelIndex );
if( m_pModel == NULL )
return mod_bad;
return m_pModel->type;
}
/*
===================
Mod_GetFrames
===================
*/
void Mod_GetFrames( int modelIndex, int &numFrames )
{
model_t *m_pModel;
m_pModel = IEngineStudio.GetModelByIndex( modelIndex );
if( !m_pModel )
{
numFrames = 1;
return;
}
numFrames = m_pModel->numframes;
if( numFrames < 1 ) numFrames = 1;
}
/*
==============
GetVisCache
decompress visibility string
==============
*/
mleaf_t *GetVisCache( mleaf_t *lastleaf, mleaf_t *leaf, byte *pvs )
{
// get the PVS for the pos to limit the number of checks
if( !worldmodel->visdata )
{
if( leaf != lastleaf )
{
memset( pvs, 255, (worldmodel->numleafs + 7) / 8 );
leaf = worldmodel->leafs;
}
}
else if( leaf != lastleaf )
{
if( leaf == worldmodel->leafs )
memset( pvs, 0, (worldmodel->numleafs + 7) / 8 );
else Mod_DecompressVis( leaf->compressed_vis, pvs );
lastleaf = leaf;
}
return lastleaf;
}
/*
==============
SetDLightVis
Init dlight visibility
==============
*/
void SetDLightVis( mworldlight_t *wl, int leafnum )
{
if( !wl->pvs )
wl->pvs = (byte *)Mem_Alloc( (worldmodel->numleafs + 7) / 8 );
GetVisCache( NULL, worldmodel->leafs + leafnum, wl->pvs );
}
/*
==============
MergeDLightVis
Merge dlight vis with another leaf
==============
*/
void MergeDLightVis( mworldlight_t *wl, int leafnum )
{
if( !wl->pvs )
{
SetDLightVis( wl, leafnum );
}
else
{
byte pvs[(MAX_MAP_LEAFS + 7) / 8];
GetVisCache( NULL, worldmodel->leafs + leafnum, pvs );
// merge both vis graphs
for( int i = 0; i < (worldmodel->numleafs + 7) / 8; i++ )
wl->pvs[i] |= pvs[i];
}
}
#define MAX_POLYGON_POINTS 64
#define PLANESIDE_FRONT 1
#define PLANESIDE_BACK 2
#define PLANESIDE_ON 3
/*
==================
R_ClipPolygon
==================
*/
bool R_ClipPolygon( int numPoints, Vector *points, const mplane_t *plane, int *numClipped, Vector *clipped )
{
float dists[MAX_POLYGON_POINTS];
int sides[MAX_POLYGON_POINTS];
bool frontSide, backSide;
float frac;
int i;
if( numPoints >= MAX_POLYGON_POINTS - 2 )
HOST_ERROR( "R_ClipPolygon: MAX_POLYGON_POINTS hit\n" );
*numClipped = 0;
// Determine sides for each point
frontSide = false;
backSide = false;
for( i = 0; i < numPoints; i++ )
{
dists[i] = PlaneDiff( points[i], plane );
if( dists[i] > ON_EPSILON )
{
sides[i] = PLANESIDE_FRONT;
frontSide = true;
continue;
}
if( dists[i] < -ON_EPSILON )
{
sides[i] = PLANESIDE_BACK;
backSide = true;
continue;
}
sides[i] = PLANESIDE_ON;
}
if( !frontSide )
return false; // Not clipped
if( !backSide )
{
*numClipped = numPoints;
memcpy( clipped, points, numPoints * sizeof( Vector ));
return true;
}
// xlip it
points[i] = points[0];
dists[i] = dists[0];
sides[i] = sides[0];
for( i = 0; i < numPoints; i++ )
{
if( sides[i] == PLANESIDE_ON )
{
clipped[(*numClipped)++] = points[i];
continue;
}
if( sides[i] == PLANESIDE_FRONT )
clipped[(*numClipped)++] = points[i];
if( sides[i+1] == PLANESIDE_ON || sides[i+1] == sides[i] )
continue;
if( dists[i] == dists[i+1] )
{
clipped[(*numClipped)++] = points[i];
}
else
{
frac = dists[i] / (dists[i] - dists[i+1]);
clipped[(*numClipped)++] = points[i] + (points[i+1] - points[i]) * frac;
}
}
return true;
}
/*
==================
R_SplitPolygon
==================
*/
void R_SplitPolygon( int numPoints, Vector *points, const mplane_t *plane, int *numFront, Vector *front, int *numBack, Vector *back )
{
float dists[MAX_POLYGON_POINTS];
int sides[MAX_POLYGON_POINTS];
bool frontSide, backSide;
Vector mid;
float frac;
int i;
if( numPoints >= MAX_POLYGON_POINTS - 2 )
HOST_ERROR( "R_SplitPolygon: MAX_POLYGON_POINTS hit\n" );
*numFront = 0;
*numBack = 0;
// Determine sides for each point
frontSide = false;
backSide = false;
for( i = 0; i < numPoints; i++ )
{
dists[i] = PlaneDiff( points[i], plane );
if( dists[i] > ON_EPSILON )
{
sides[i] = PLANESIDE_FRONT;
frontSide = true;
continue;
}
if( dists[i] < -ON_EPSILON )
{
sides[i] = PLANESIDE_BACK;
backSide = true;
continue;
}
sides[i] = PLANESIDE_ON;
}
if( !frontSide )
{
*numBack = numPoints;
memcpy( back, points, numPoints * sizeof( Vector ));
return;
}
if( !backSide )
{
*numFront = numPoints;
memcpy( front, points, numPoints * sizeof( Vector ));
return;
}
// split it
points[i] = points[0];
dists[i] = dists[0];
sides[i] = sides[0];
for( i = 0; i < numPoints; i++ )
{
if( sides[i] == PLANESIDE_ON )
{
front[(*numFront)++] = points[i];
back[(*numBack)++] = points[i];
continue;
}
if( sides[i] == PLANESIDE_FRONT )
front[(*numFront)++] = points[i];
if( sides[i] == PLANESIDE_BACK )
back[(*numBack)++] = points[i];
if( sides[i+1] == PLANESIDE_ON || sides[i+1] == sides[i] )
continue;
if( dists[i] == dists[i+1] )
{
front[(*numFront)++] = points[i];
back[(*numBack)++] = points[i];
}
else
{
frac = dists[i] / (dists[i] - dists[i+1]);
mid = points[i] + (points[i+1] - points[i]) * frac;
front[(*numFront)++] = mid;
back[(*numBack)++] = mid;
}
}
}
/*
==================
R_TransformWorldToDevice
==================
*/
void R_TransformWorldToDevice( const Vector &world, Vector &ndc )
{
Vector4D clip;
float scale;
clip[0] = world[0] * RI->view.worldProjectionMatrix[0][0] + world[1] * RI->view.worldProjectionMatrix[1][0] + world[2] * RI->view.worldProjectionMatrix[2][0] + RI->view.worldProjectionMatrix[3][0];
clip[1] = world[0] * RI->view.worldProjectionMatrix[0][1] + world[1] * RI->view.worldProjectionMatrix[1][1] + world[2] * RI->view.worldProjectionMatrix[2][1] + RI->view.worldProjectionMatrix[3][1];
clip[2] = world[0] * RI->view.worldProjectionMatrix[0][2] + world[1] * RI->view.worldProjectionMatrix[1][2] + world[2] * RI->view.worldProjectionMatrix[2][2] + RI->view.worldProjectionMatrix[3][2];
clip[3] = world[0] * RI->view.worldProjectionMatrix[0][3] + world[1] * RI->view.worldProjectionMatrix[1][3] + world[2] * RI->view.worldProjectionMatrix[2][3] + RI->view.worldProjectionMatrix[3][3];
if( clip[3] == 0.0f )
{
ndc[0] = clip[0];
ndc[1] = clip[1];
ndc[2] = -1.0f;
}
else
{
scale = 1.0f / clip[3];
ndc[0] = clip[0] * scale;
ndc[1] = clip[1] * scale;
ndc[2] = clip[2] * scale;
}
}
/*
==================
R_TransformDeviceToScreen
==================
*/
void R_TransformDeviceToScreen( const Vector &ndc, Vector &screen )
{
screen[0] = (ndc[0] * 0.5f + 0.5f) * (RI->view.port[2] - RI->view.port[0]) + RI->view.port[0];
screen[1] = (ndc[1] * 0.5f + 0.5f) * (RI->view.port[3] - RI->view.port[1]) + RI->view.port[1];
screen[2] = (ndc[2] * 0.5f + 0.5f);
}
/*
==================
R_ScissorForBounds
==================
*/
static bool R_ScissorForBounds( const Vector bbox[8], float *x, float *y, float *w, float *h )
{
static int cornerIndices[6][4] = {{3, 2, 6, 7}, {0, 1, 5, 4}, {2, 3, 1, 0}, {4, 5, 7, 6}, {1, 3, 7, 5}, {2, 0, 4, 6}};
Vector points[2][MAX_POLYGON_POINTS];
float ix1, iy1, ix2, iy2;
float x1, y1, x2, y2;
int numPoints;
Vector bounds[2];
Vector ndc;
int pingPong = 0;
int i, j;
// Clip the light volume to the view frustum
ClearBounds( bounds[0], bounds[1] );
for( i = 0; i < 6; i++ )
{
numPoints = 4;
points[pingPong][0] = bbox[cornerIndices[i][0]];
points[pingPong][1] = bbox[cornerIndices[i][1]];
points[pingPong][2] = bbox[cornerIndices[i][2]];
points[pingPong][3] = bbox[cornerIndices[i][3]];
for( j = 0; j < FRUSTUM_PLANES; j++ )
{
if( !FBitSet( RI->view.frustum.GetClipFlags(), BIT( j )))
continue;
if( !R_ClipPolygon( numPoints, points[pingPong], RI->view.frustum.GetPlane( j ), &numPoints, points[!pingPong] ))
break;
pingPong ^= 1;
}
if( j != FRUSTUM_PLANES )
continue;
// Add the clipped points
for( j = 0; j < numPoints; j++ )
{
// Transform into normalized device coordinates
R_TransformWorldToDevice( points[pingPong][j], ndc );
// Add it
AddPointToBounds( ndc, bounds[0], bounds[1] );
}
}
// If completely clipped away, clear the scissor
if( BoundsIsCleared( bounds[0], bounds[1] ))
return false;
// Transform into screen space
R_TransformDeviceToScreen( bounds[0], bounds[0] );
R_TransformDeviceToScreen( bounds[1], bounds[1] );
x1 = bounds[0].x - 1.0f;
y1 = bounds[0].y - 1.0f;
x2 = bounds[1].x + 1.0f;
y2 = bounds[1].y + 1.0f;
ix1 = Q_max( x1, RI->view.port[0] );
ix2 = Q_min( x2, RI->view.port[2] );
iy1 = Q_max( y1, RI->view.port[1] );
iy2 = Q_min( y2, RI->view.port[3] );
*x = ix1 + 1.0f;
*y = RI->view.port[3] - iy2; // stupid OpenGL bug
*w = ix2 - ix1 - 1.0f;
*h = iy2 - iy1 - 1.0f;
// headshield issues
if( ix1 >= ix2 || iy1 >= iy2 )
return false;
return true;
}
void R_BoundsForAABB( const Vector &absmin, const Vector &absmax, Vector bbox[8] )
{
ASSERT( bbox != NULL );
// compute a full bounding box
for( int i = 0; i < 8; i++ )
{
bbox[i][0] = ( i & 1 ) ? absmin[0] : absmax[0];
bbox[i][1] = ( i & 2 ) ? absmin[1] : absmax[1];
bbox[i][2] = ( i & 4 ) ? absmin[2] : absmax[2];
}
}
bool R_ScissorForAABB( const Vector &absmin, const Vector &absmax, float *x, float *y, float *w, float *h )
{
Vector bbox[8];
R_BoundsForAABB( absmin, absmax, bbox );
return R_ScissorForBounds( bbox, x, y, w, h );
}
bool R_ScissorForFrustum( CFrustum *frustum, float *x, float *y, float *w, float *h )
{
Vector bbox[8];
frustum->ComputeFrustumCorners( bbox );
// NOTE: disable farplane because dynamic zFar
RI->view.frustum.DisablePlane( FRUSTUM_FAR );
bool result = R_ScissorForBounds( bbox, x, y, w, h );
RI->view.frustum.EnablePlane( FRUSTUM_FAR );
return result;
}
bool R_AABBToScreen( const Vector &absmin, const Vector &absmax, Vector2D &scrmin, Vector2D &scrmax, wrect_t *rect )
{
float x, y, w, h;
Vector bbox[8];
R_BoundsForAABB( absmin, absmax, bbox );
ClearBounds( scrmin, scrmax );
if( !R_ScissorForBounds( bbox, &x, &y, &w, &h ))
{
if( rect ) memset( rect, 0, sizeof( *rect ));
return false;
}
// copy rectangle
if( rect )
{
rect->left = x;
rect->right = RI->view.port[3] - h - y; // left bottom corner
rect->top = w;
rect->bottom = h;
}
// compute a bounding box
AddPointToBounds( Vector2D( x, y ), scrmin, scrmax );
AddPointToBounds( Vector2D( x + w, y ), scrmin, scrmax );
AddPointToBounds( Vector2D( x, y + h ), scrmin, scrmax );
AddPointToBounds( Vector2D( x + w, y + h ), scrmin, scrmax );
return true;
}
// debug thing
void R_DrawScissorRectangle( float x, float y, float w, float h )
{
GL_BindTexture( GL_TEXTURE0, tr.whiteTexture );
pglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
GL_Setup2D();
pglColor3f( 1, 0.5, 0 );
pglBegin( GL_LINES );
pglVertex2f( x, y );
pglVertex2f( x + w, y );
pglVertex2f( x, y );
pglVertex2f( x, y + h );
pglVertex2f( x + w, y );
pglVertex2f( x + w, y + h );
pglVertex2f( x, y + h );
pglVertex2f( x + w, y + h );
pglEnd();
GL_Setup3D();
}
//-----------------------------------------------------------------------------
// This returns the diameter of the sphere in pixels based on
// the current model, view, + projection matrices and viewport.
//-----------------------------------------------------------------------------
static float ComputePixelDiameterOfSphere( const Vector& vecAbsOrigin, float flRadius )
{
// This is sort of faked, but it's faster that way
// FIXME: Also, there's a much faster way to do this with similar triangles
// but I want to make sure it exactly matches the current matrices, so
// for now, I do it this conservative way
Vector4D testPoint1 = vecAbsOrigin + GetVUp() * flRadius;
Vector4D testPoint2 = vecAbsOrigin + GetVUp() * -flRadius;
Vector4D clipPos1 = RI->view.worldProjectionMatrix.VectorTransform( testPoint1 );
Vector4D clipPos2 = RI->view.worldProjectionMatrix.VectorTransform( testPoint2 );
if( clipPos1.w >= 0.001f )
{
clipPos1.y /= clipPos1.w;
}
else
{
clipPos1.y *= 1000.0f;
}
if( clipPos2.w >= 0.001f )
{
clipPos2.y /= clipPos2.w;
}
else
{
clipPos2.y *= 1000.0f;
}
// The divide-by-two here is because y goes from -1 to 1 in projection space
return RI->view.port[3] * fabs( clipPos2.y - clipPos1.y ) / 2.0f;
}
float ComputePixelWidthOfSphere( const Vector& vecOrigin, float flRadius )
{
return ComputePixelDiameterOfSphere( vecOrigin, flRadius ) * 2.0f;
}
HSPRITE LoadSprite(const char *pszName)
{
int i;
char sz[256];
if (ScreenWidth < 640)
i = 320;
else
i = 640;
sprintf(sz, pszName, i);
return SPR_Load(sz);
}
void Physic_SweepTest( cl_entity_t *pTouch, const Vector &start, const Vector &mins, const Vector &maxs, const Vector &end, trace_t *tr )
{
if( !pTouch->model || !pTouch->model->cache.data )
{
// bad model?
tr->allsolid = false;
return;
}
Vector trace_mins, trace_maxs, bounds[2];
UTIL_MoveBounds( start, mins, maxs, end, trace_mins, trace_maxs );
if( !R_StudioGetBounds( pTouch, bounds ))
{
tr->allsolid = false;
return;
}
// NOTE: pmove code completely ignore a bounds checking. So we need to do it here
if( !BoundsIntersect( trace_mins, trace_maxs, bounds[0], bounds[1] ))
{
tr->allsolid = false;
return;
}
CMeshDesc *bodyMesh = UTIL_GetCollisionMesh( pTouch->curstate.modelindex );
if( !bodyMesh )
{
tr->allsolid = false;
return;
}
mmesh_t *pMesh = bodyMesh->GetMesh();
areanode_t *pHeadNode = bodyMesh->GetHeadNode();
entity_state_t *pev = &pTouch->curstate;
if( !pMesh )
{
// failed to build mesh for some reasons, so skip them
tr->allsolid = false;
return;
}
TraceMesh trm; // a name like Doom3 :-)
trm.SetTraceMesh( pMesh, pHeadNode, pTouch->curstate.modelindex );
trm.SetMeshOrientation( pev->origin, pev->angles, pev->startpos );
trm.SetupTrace( start, mins, maxs, end, tr );
if( trm.DoTrace( ))
{
if( tr->fraction < 1.0f || tr->startsolid )
tr->ent = (edict_t *)pTouch; // g-cont. no need, really. i'm leave this just for fun
tr->surf = trm.GetLastHitSurface();
}
}
#define GAMMA ( 2.2f ) // Valve Software gamma
#define INVGAMMA ( 1.0f / 2.2f ) // back to 1.0
static float texturetolinear[256]; // texture (0..255) to linear (0..1)
static int lineartotexture[1024]; // linear (0..1) to texture (0..255)
void BuildGammaTable( void )
{
int i;
float g1, g3;
float g = bound( 0.5f, GAMMA, 3.0f );
g = 1.0 / g;
g1 = GAMMA * g;
g3 = 0.125f;
for( i = 0; i < 256; i++ )
{
// convert from nonlinear texture space (0..255) to linear space (0..1)
texturetolinear[i] = pow( i / 255.0f, GAMMA );
}
for( i = 0; i < 1024; i++ )
{
// convert from linear space (0..1) to nonlinear texture space (0..255)
lineartotexture[i] = pow( i / 1023.0, INVGAMMA ) * 255;
}
}
// convert texture to linear 0..1 value
float TextureToLinear( int c ) { return texturetolinear[bound( 0, c, 255 )]; }
// convert texture to linear 0..1 value
int LinearToTexture( float f ) { return lineartotexture[bound( 0, (int)(f * 1023), 1023 )]; }
/*
===============================================================================
LEGACY STUFF
moved here from studio_util.cpp
===============================================================================
*/
/*
====================
AngleMatrix
====================
*/
void AngleMatrix (const float *angles, float (*matrix)[4] )
{
float angle;
float sr, sp, sy, cr, cp, cy;
angle = angles[YAW] * (M_PI*2 / 360);
sy = sin(angle);
cy = cos(angle);
angle = angles[PITCH] * (M_PI*2 / 360);
sp = sin(angle);
cp = cos(angle);
angle = angles[ROLL] * (M_PI*2 / 360);
sr = sin(angle);
cr = cos(angle);
// matrix = (YAW * PITCH) * ROLL
matrix[0][0] = cp*cy;
matrix[1][0] = cp*sy;
matrix[2][0] = -sp;
matrix[0][1] = sr*sp*cy+cr*-sy;
matrix[1][1] = sr*sp*sy+cr*cy;
matrix[2][1] = sr*cp;
matrix[0][2] = (cr*sp*cy+-sr*-sy);
matrix[1][2] = (cr*sp*sy+-sr*cy);
matrix[2][2] = cr*cp;
matrix[0][3] = 0.0;
matrix[1][3] = 0.0;
matrix[2][3] = 0.0;
}
/*
====================
VectorTransform
====================
*/
void VectorTransform (const float *in1, float in2[3][4], float *out)
{
out[0] = DotProduct(in1, in2[0]) + in2[0][3];
out[1] = DotProduct(in1, in2[1]) + in2[1][3];
out[2] = DotProduct(in1, in2[2]) + in2[2][3];
}
vec3_t vec3_origin( 0, 0, 0 );
float Length(const float *v)
{
int i;
float length;
length = 0;
for (i=0 ; i< 3 ; i++)
length += v[i]*v[i];
length = sqrt (length); // FIXME
return length;
}
void VectorAngles( const float *forward, float *angles )
{
float tmp, yaw, pitch;
if (forward[1] == 0 && forward[0] == 0)
{
yaw = 0;
if (forward[2] > 0)
pitch = 90;
else
pitch = 270;
}
else
{
yaw = (atan2(forward[1], forward[0]) * 180 / M_PI);
if (yaw < 0)
yaw += 360;
tmp = sqrt (forward[0]*forward[0] + forward[1]*forward[1]);
pitch = (atan2(forward[2], tmp) * 180 / M_PI);
if (pitch < 0)
pitch += 360;
}
angles[0] = pitch;
angles[1] = yaw;
angles[2] = 0;
}
float VectorNormalize (float *v)
{
float length, ilength;
length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
length = sqrt (length); // FIXME
if (length)
{
ilength = 1/length;
v[0] *= ilength;
v[1] *= ilength;
v[2] *= ilength;
}
return length;
}
void VectorScale (const float *in, float scale, float *out)
{
out[0] = in[0]*scale;
out[1] = in[1]*scale;
out[2] = in[2]*scale;
}
void VectorMA (const float *veca, float scale, const float *vecb, float *vecc)
{
vecc[0] = veca[0] + scale*vecb[0];
vecc[1] = veca[1] + scale*vecb[1];
vecc[2] = veca[2] + scale*vecb[2];
}
float PackColor( const color24 &color )
{
return (float)((double)((color.r << 16) | (color.g << 8) | color.b) / (double)(1 << 24));
}
color24 UnpackColor( float pack )
{
Vector color = Vector( 1.0f, 256.0f, 65536.0f ) * pack;
// same as fract( color )
color.x = color.x - floor( color.x );
color.y = color.y - floor( color.y );
color.z = color.z - floor( color.z );
return color24( color.x * 256, color.y * 256, color.z * 256 );
}