26 Aug 2010

This commit is contained in:
g-cont 2010-08-26 00:00:00 +04:00 committed by Alibek Omarov
parent 9e797d351d
commit 08114a7f78
23 changed files with 1925 additions and 420 deletions

View File

@ -1055,6 +1055,13 @@ int AddToFullPack( struct entity_state_s *state, int e, edict_t *ent, edict_t *h
// Copy state data
//
#ifdef DEBUG
// NOTE: edit delta.lst if you want to get it work: add new field
// DEFINE_DELTA( classname, DT_STRING, 1, 1.0 ),
// into Entity_Encode, Player_Encode and Custom_Encode structures
strncpy( state->classname, STRING( ent->v.classname ), sizeof( state->classname ));
#endif
// Round animtime to nearest millisecond
state->animtime = (int)(1000.0 * ent->v.animtime ) / 1000.0;

View File

@ -114,6 +114,7 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t
cl_entity_t *ent;
entity_state_t *state;
bool newent = (old) ? false : true;
int result = 1;
ent = EDICT_NUM( newnum );
state = &cl.entity_curstates[cl.parse_entities & (MAX_PARSE_ENTITIES-1)];
@ -121,13 +122,25 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t
if( newent ) old = &ent->baseline;
if( unchanged ) *state = *old;
else MSG_ReadDeltaEntity( msg, old, state, newnum, cl.frame.servertime );
else result = MSG_ReadDeltaEntity( msg, old, state, newnum, cl.frame.servertime );
if( state->number == -1 )
if( !result )
{
if( newent ) Host_Error( "Cl_DeltaEntity: tried to release new entity\n" );
CL_FreeEntity( ent );
return; // entity was delta removed
if( state->number == -1 )
{
// Msg( "Entity %s was removed from server\n", ent->curstate.classname );
CL_FreeEntity( ent );
}
else
{
// Msg( "Entity %s was removed from delta-message\n", ent->curstate.classname );
ent->curstate.effects |= EF_NODRAW; // don't rendering
}
// entity was delta removed
return;
}
cl.parse_entities++;
@ -442,6 +455,10 @@ void CL_AddPacketEntities( frame_t *frame )
ent = CL_GetEntityByIndex( e );
if( !ent ) continue;
// entity not visible for this client
if( ent->curstate.effects & EF_NODRAW )
continue;
entityType = ent->curstate.entityType;
CL_UpdateEntityFields( ent );

View File

@ -90,7 +90,6 @@ byte *CM_LeafPVS( int leafnum );
byte *CM_LeafPHS( int leafnum );
int CM_PointLeafnum( const vec3_t p );
mleaf_t *CM_PointInLeaf( const vec3_t p, mnode_t *node );
bool CM_HeadnodeVisible( int nodenum, byte *visbits );
int CM_BoxLeafnums( const vec3_t mins, const vec3_t maxs, short *list, int listsize, int *lastleaf );
int CM_TempBoxModel( const vec3_t mins, const vec3_t maxs, bool capsule );
bool CM_BoxVisible( const vec3_t mins, const vec3_t maxs, byte *visbits );
@ -126,8 +125,6 @@ void CM_EndRegistration( void );
//
// cm_studio.c
//
void CM_SpriteModel( model_t *mod, byte *buffer );
void CM_StudioModel( model_t *mod, byte *buffer );
void CM_StudioInitBoxHull( void );
int CM_StudioBodyVariations( int handle );
void CM_StudioGetAttachment( edict_t *e, int iAttachment, float *org, float *ang );

View File

@ -855,6 +855,55 @@ static void CM_BrushModel( model_t *mod, byte *buffer )
}
}
static void CM_StudioModel( model_t *mod, byte *buffer )
{
studiohdr_t *phdr;
mstudioseqdesc_t *pseqdesc;
phdr = (studiohdr_t *)buffer;
if( phdr->version != STUDIO_VERSION )
{
MsgDev( D_ERROR, "CM_StudioModel: %s has wrong version number (%i should be %i)\n", loadmodel->name, phdr->version, STUDIO_VERSION );
return;
}
loadmodel->type = mod_studio;
pseqdesc = (mstudioseqdesc_t *)((byte *)phdr + phdr->seqindex);
loadmodel->numframes = pseqdesc[0].numframes;
loadmodel->registration_sequence = cm.registration_sequence;
loadmodel->mempool = Mem_AllocPool( va("^2%s^7", loadmodel->name ));
loadmodel->extradata = Mem_Alloc( loadmodel->mempool, LittleLong( phdr->length ));
Mem_Copy( loadmodel->extradata, buffer, LittleLong( phdr->length ));
// setup bounding box
VectorCopy( phdr->bbmin, loadmodel->mins );
VectorCopy( phdr->bbmax, loadmodel->maxs );
}
static void CM_SpriteModel( model_t *mod, byte *buffer )
{
dsprite_t *phdr;
phdr = (dsprite_t *)buffer;
if( phdr->version != SPRITE_VERSION )
{
MsgDev( D_ERROR, "CM_SpriteModel: %s has wrong version number (%i should be %i)\n", loadmodel->name, phdr->version, SPRITE_VERSION );
return;
}
loadmodel->type = mod_sprite;
loadmodel->numframes = phdr->numframes;
loadmodel->registration_sequence = cm.registration_sequence;
// setup bounding box
loadmodel->mins[0] = loadmodel->mins[1] = -phdr->bounds[0] / 2;
loadmodel->maxs[0] = loadmodel->maxs[1] = phdr->bounds[0] / 2;
loadmodel->mins[2] = -phdr->bounds[1] / 2;
loadmodel->maxs[2] = phdr->bounds[1] / 2;
}
void CM_FreeWorld( void )
{
if( worldmodel )

View File

@ -800,52 +800,4 @@ void CM_GetBonePosition( edict_t* e, int iBone, float *org, float *ang )
if( org ) Matrix4x4_OriginFromMatrix( studio.bones[iBone], org );
if( ang ) Matrix3x3_ToAngles( axis, ang, true );
}
void CM_StudioModel( model_t *mod, byte *buffer )
{
studiohdr_t *phdr;
mstudioseqdesc_t *pseqdesc;
phdr = (studiohdr_t *)buffer;
if( phdr->version != STUDIO_VERSION )
{
MsgDev( D_ERROR, "CM_StudioModel: %s has wrong version number (%i should be %i)\n", loadmodel->name, phdr->version, STUDIO_VERSION );
return;
}
loadmodel->type = mod_studio;
pseqdesc = (mstudioseqdesc_t *)((byte *)phdr + phdr->seqindex);
loadmodel->numframes = pseqdesc[0].numframes;
loadmodel->registration_sequence = cm.registration_sequence;
loadmodel->mempool = Mem_AllocPool( va("^2%s^7", loadmodel->name ));
loadmodel->extradata = Mem_Alloc( loadmodel->mempool, LittleLong( phdr->length ));
Mem_Copy( loadmodel->extradata, buffer, LittleLong( phdr->length ));
// setup bounding box
CM_StudioExtractBbox( phdr, 0, loadmodel->mins, loadmodel->maxs );
}
void CM_SpriteModel( model_t *mod, byte *buffer )
{
dsprite_t *phdr;
phdr = (dsprite_t *)buffer;
if( phdr->version != SPRITE_VERSION )
{
MsgDev( D_ERROR, "CM_SpriteModel: %s has wrong version number (%i should be %i)\n", loadmodel->name, phdr->version, SPRITE_VERSION );
return;
}
loadmodel->type = mod_sprite;
loadmodel->numframes = phdr->numframes;
loadmodel->registration_sequence = cm.registration_sequence;
// setup bounding box
loadmodel->mins[0] = loadmodel->mins[1] = -phdr->bounds[0] / 2;
loadmodel->maxs[0] = loadmodel->maxs[1] = phdr->bounds[0] / 2;
loadmodel->mins[2] = -phdr->bounds[1] / 2;
loadmodel->maxs[2] = phdr->bounds[1] / 2;
}

View File

@ -5,30 +5,7 @@
#include "cm_local.h"
#include "mathlib.h"
/*
==============
BoxOnPlaneSide (engine fast version)
Returns SIDE_FRONT, SIDE_BACK, or SIDE_ON
==============
*/
int CM_BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const mplane_t *p )
{
if( p->type < 3 ) return ((emaxs[p->type] >= p->dist) | ((emins[p->type] < p->dist) << 1));
switch( p->signbits )
{
default:
case 0: return (((p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]) >= p->dist) | (((p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]) < p->dist) << 1));
case 1: return (((p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]) >= p->dist) | (((p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]) < p->dist) << 1));
case 2: return (((p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]) >= p->dist) | (((p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]) < p->dist) << 1));
case 3: return (((p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]) >= p->dist) | (((p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]) < p->dist) << 1));
case 4: return (((p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]) >= p->dist) | (((p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]) < p->dist) << 1));
case 5: return (((p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]) >= p->dist) | (((p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]) < p->dist) << 1));
case 6: return (((p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]) >= p->dist) | (((p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]) < p->dist) << 1));
case 7: return (((p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]) >= p->dist) | (((p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]) < p->dist) << 1));
}
}
#include "world.h"
/*
==================
@ -89,7 +66,7 @@ void CM_BoxLeafnums_r( leaflist_t *ll, mnode_t *node )
}
plane = node->plane;
s = CM_BoxOnPlaneSide( ll->mins, ll->maxs, plane );
s = BOX_ON_PLANE_SIDE( ll->mins, ll->maxs, plane );
if( s == 1 )
{
@ -136,54 +113,6 @@ int CM_BoxLeafnums( const vec3_t mins, const vec3_t maxs, short *list, int lists
return ll.count;
}
/*
=============
CM_HeadnodeVisible_r
=============
*/
bool CM_HeadnodeVisible_r( mnode_t *node, byte *visbits )
{
mleaf_t *leaf;
int leafnum;
if( node->contents < 0 )
{
if( node->contents != CONTENTS_SOLID )
{
leaf = (mleaf_t *)node;
leafnum = (leaf - worldmodel->leafs - 1);
if( visbits[leafnum>>3] & (1<<( leafnum & 7 )))
return true;
}
return false;
}
if( CM_HeadnodeVisible_r( node->children[0], visbits ))
return true;
return CM_HeadnodeVisible_r( node->children[1], visbits );
}
/*
=============
CM_HeadnodeVisible
returns true if any leaf under headnode
is potentially visible
=============
*/
bool CM_HeadnodeVisible( int nodenum, byte *visbits )
{
mnode_t *node;
if( !worldmodel ) return false;
if( nodenum == -1 ) return false;
node = (mnode_t *)worldmodel->nodes + nodenum;
return CM_HeadnodeVisible_r( node, visbits );
}
/*
=============
CM_BoxVisible

View File

@ -112,7 +112,7 @@ void BF_WriteOneBit( sizebuf_t *bf, int nValue )
void BF_WriteUBitLongExt( sizebuf_t *bf, uint curData, int numbits, bool bCheckRange )
{
#ifdef _DEBUG
#ifdef PARANOID
// make sure it doesn't overflow.
if( bCheckRange && numbits < 32 )
{

View File

@ -1382,11 +1382,21 @@ void MSG_WriteDeltaEntity( entity_state_t *from, entity_state_t *to, sizebuf_t *
if( to == NULL )
{
int fRemoveType;
if( from == NULL ) return;
// a NULL to is a delta remove message
BF_WriteWord( msg, from->number );
BF_WriteOneBit( msg, 1 ); // entity killed
// fRemoveType:
// 0 - keep alive, has delta-update
// 1 - remove from delta message (but keep states)
// 2 - completely remove from server
if( force ) fRemoveType = 2;
else fRemoveType = 1;
BF_WriteUBitLong( msg, fRemoveType, 2 );
return;
}
@ -1396,7 +1406,7 @@ void MSG_WriteDeltaEntity( entity_state_t *from, entity_state_t *to, sizebuf_t *
Host_Error( "MSG_WriteDeltaEntity: Bad entity number: %i\n", to->number );
BF_WriteWord( msg, to->number );
BF_WriteOneBit( msg, 0 ); // alive
BF_WriteUBitLong( msg, 0, 2 ); // alive
if( to->entityType != from->entityType )
{
@ -1448,25 +1458,39 @@ If the delta removes the entity, entity_state_t->number will be set to MAX_EDICT
Can go from either a baseline or a previous packet_entity
==================
*/
void MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state_t *to, int number, int timebase )
bool MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state_t *to, int number, int timebase )
{
delta_info_t *dt;
delta_t *pField;
float flTime = timebase * 0.001f;
int i;
int i, fRemoveType;
if( number < 0 || number >= GI->max_edicts )
Host_Error( "MSG_ReadDeltaEntity: bad delta entity number: %i\n", number );
*to = *from;
to->number = number;
fRemoveType = BF_ReadUBitLong( msg, 2 );
if( BF_ReadOneBit( msg ))
if( fRemoveType )
{
// check for a remove
Mem_Set( to, 0, sizeof( *to ));
to->number = -1; // entity was removed
return;
Mem_Set( to, 0, sizeof( *to ));
if( fRemoveType & 1 )
{
// removed from delta-message
return false;
}
if( fRemoveType & 2 )
{
// entity was removed from server
to->number = -1;
return false;
}
Host_Error( "MSG_ReadDeltaEntity: unknown update type %i\n", fRemoveType );
}
if( BF_ReadOneBit( msg ))
@ -1495,6 +1519,9 @@ void MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state_t *
{
Delta_ReadField( msg, pField, from, to, flTime );
}
// message parsed
return true;
}
void Delta_AddEncoder( char *name, pfnDeltaEncode encodeFunc )

View File

@ -101,6 +101,6 @@ void MSG_ReadDeltaMovevars( sizebuf_t *msg, movevars_t *from, movevars_t *to );
void MSG_WriteClientData( sizebuf_t *msg, clientdata_t *from, clientdata_t *to, int timebase );
void MSG_ReadClientData( sizebuf_t *msg, clientdata_t *from, clientdata_t *to, int timebase );
void MSG_WriteDeltaEntity( entity_state_t *from, entity_state_t *to, sizebuf_t *msg, bool force, int timebase );
void MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state_t *to, int number, int timebase );
bool MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state_t *to, int number, int timebase );
#endif//NET_ENCODE_H

View File

@ -110,4 +110,93 @@ trace_t World_CombineTraces( trace_t *cliptrace, trace_t *trace, edict_t *touch
cliptrace->fStartSolid = true;
return *cliptrace;
}
/*
==================
RankForContents
Used for determine contents priority
==================
*/
int RankForContents( int contents )
{
switch( contents )
{
case CONTENTS_EMPTY: return 0;
case CONTENTS_WATER: return 1;
case CONTENTS_TRANSLUCENT: return 2;
case CONTENTS_CURRENT_0: return 3;
case CONTENTS_CURRENT_90: return 4;
case CONTENTS_CURRENT_180: return 5;
case CONTENTS_CURRENT_270: return 6;
case CONTENTS_CURRENT_UP: return 7;
case CONTENTS_CURRENT_DOWN: return 8;
case CONTENTS_SLIME: return 9;
case CONTENTS_LAVA: return 10;
case CONTENTS_SKY: return 11;
case CONTENTS_SOLID: return 12;
}
return -1;
}
/*
==================
BoxOnPlaneSide
Returns 1, 2, or 1 + 2
==================
*/
int BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const mplane_t *p )
{
float dist1, dist2;
int sides = 0;
// general case
switch( p->signbits )
{
case 0:
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
break;
case 1:
dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
break;
case 2:
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
break;
case 3:
dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
break;
case 4:
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
break;
case 5:
dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
break;
case 6:
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
break;
case 7:
dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
break;
default:
// shut up compiler
dist1 = dist2 = 0;
break;
}
if( dist1 >= p->dist )
sides = 1;
if( dist2 < p->dist )
sides |= 2;
return sides;
}

View File

@ -21,8 +21,8 @@ ENTITY AREA CHECKING
*/
#define EDICT_FROM_AREA( l ) EDICT_NUM( l->entnum )
#define MAX_TOTAL_ENT_LEAFS 128
#define AREA_NODES 64
#define AREA_DEPTH 5
#define AREA_NODES 32
#define AREA_DEPTH 4
typedef struct areanode_s
{
@ -44,30 +44,6 @@ typedef struct area_s
int type;
} area_t;
typedef struct moveclip_s
{
vec3_t boxmins; // enclose the test object along entire move
vec3_t boxmaxs;
float *mins;
float *maxs; // size of the moving object
vec3_t mins2;
vec3_t maxs2;
const float *start;
const float *end;
trace_t trace;
union
{
struct edict_s *passedict;
struct cl_entity_s *passentity;
};
int type; // move type
int flags; // trace flags
} moveclip_t;
extern const char *et_name[];
// linked list
@ -78,6 +54,25 @@ void ClearLink( link_t *l );
// trace common
void World_MoveBounds( const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, vec3_t boxmins, vec3_t boxmaxs );
trace_t World_CombineTraces( trace_t *cliptrace, trace_t *trace, edict_t *touch );
int BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const mplane_t *p );
int RankForContents( int contents );
#define BOX_ON_PLANE_SIDE( emins, emaxs, p ) \
((( p )->type < 3 ) ? \
( \
((p)->dist <= (emins)[(p)->type]) ? \
1 \
: \
( \
((p)->dist >= (emaxs)[(p)->type]) ? \
2 \
: \
3 \
) \
) \
: \
BoxOnPlaneSide(( emins ), ( emaxs ), ( p )))
#include "bspfile.h"
#include "pm_shared.h"

View File

@ -314,6 +314,10 @@ SOURCE=.\server\sv_save.c
# End Source File
# Begin Source File
SOURCE=.\server\sv_studio.c
# End Source File
# Begin Source File
SOURCE=.\server\sv_world.c
# End Source File
# Begin Source File

View File

@ -43,6 +43,7 @@ extern int SV_UPDATE_BACKUP;
// convert msecs to float time properly
#define sv_time() ( sv.time * 0.001f )
#define sv_frametime() ( sv.frametime * 0.001f )
#define SV_IsValidEdict( e ) ( e && !e->free )
typedef enum
{
@ -85,6 +86,8 @@ typedef struct server_s
sizebuf_t signon;
byte signon_buf[MAX_MSGLEN];
model_t *worldmodel; // pointer to world
bool write_bad_message; // just for debug
bool paused;
} server_t;
@ -422,7 +425,6 @@ void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float
edict_t* pfnPEntityOfEntIndex( int iEntIndex );
int pfnIndexOfEdict( const edict_t *pEdict );
void SV_UpdateBaseVelocity( edict_t *ent );
bool SV_IsValidEdict( const edict_t *e );
script_t *CM_GetEntityScript( void );
_inline edict_t *SV_EDICT_NUM( int n, const char * file, const int line )
@ -443,6 +445,16 @@ void SV_ChangeLevel( bool loadfromsavedgame, const char *mapname, const char *st
const char *SV_GetLatestSave( void );
int SV_LoadGameState( char const *level, bool createPlayers );
void SV_LoadAdjacentEnts( const char *pOldLevel, const char *pLandmarkName );
//
// sv_studio.c
//
void SV_InitStudioHull( void );
bool SV_StudioExtractBbox( model_t *mod, int sequence, float *mins, float *maxs );
void SV_StudioGetAttachment( edict_t *e, int iAttachment, float *org, float *ang );
bool SV_StudioTrace( edict_t *ent, const vec3_t p1, vec3_t mins, vec3_t maxs, const vec3_t p2, trace_t *ptr );
void SV_GetBonePosition( edict_t *e, int iBone, float *org, float *ang );
//============================================================
// high level object sorting to reduce interaction tests
@ -480,6 +492,8 @@ bool SV_CopyEdictToPhysEnt( physent_t *pe, edict_t *ed, bool player_trace );
extern areanode_t sv_areanodes[];
void SV_ClearWorld( void );
bool SV_HeadnodeVisible( mnode_t *node, byte *visbits );
int SV_HullPointContents( hull_t *hull, int num, const vec3_t p );
trace_t SV_Move( const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, int type, edict_t *e );
trace_t SV_ClipMove( edict_t *ent, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, int flags );
trace_t SV_MoveToss( edict_t *tossent, edict_t *ignore );

View File

@ -187,7 +187,7 @@ gotnewcl:
newcl->userid = g_userid++; // create unique userid
// get the game a chance to reject this connection or modify the userinfo
if(!(SV_ClientConnect( ent, userinfo )))
if( !( SV_ClientConnect( ent, userinfo )))
{
if(*Info_ValueForKey( userinfo, "rejmsg" ))
Netchan_OutOfBandPrint( NS_SERVER, from, "print\n%s\nConnection refused.\n", Info_ValueForKey( userinfo, "rejmsg" ));

View File

@ -117,8 +117,14 @@ void SV_EmitPacketEntities( client_frame_t *from, client_frame_t *to, sizebuf_t
if( newnum > oldnum )
{
bool force;
if( EDICT_NUM( oldent->number )->free )
force = true; // entity completely removed from server
else force = false; // just removed from delta-message
// remove from message
MSG_WriteDeltaEntity( oldent, NULL, msg, false, sv.time );
MSG_WriteDeltaEntity( oldent, NULL, msg, force, sv.time );
oldindex++;
continue;
}

View File

@ -65,6 +65,7 @@ void SV_SysError( const char *error_string )
void SV_SetMinMaxSize( edict_t *e, const float *min, const float *max )
{
float scale = 1.0f;
int i;
if( !SV_IsValidEdict( e ) || !min || !max )
@ -79,9 +80,21 @@ void SV_SetMinMaxSize( edict_t *e, const float *min, const float *max )
return;
}
}
VectorCopy( min, e->v.mins );
VectorCopy( max, e->v.maxs );
#if 0
// FIXME: enable this when other server parts will be done and tested
if( e->v.scale > 0.0f && e->v.scale != 1.0f )
{
switch( CM_GetModelType( e->v.modelindex ))
{
case mod_sprite:
case mod_studio:
scale = e->v.scale;
break;
}
}
#endif
VectorScale( min, scale, e->v.mins );
VectorScale( max, scale, e->v.maxs );
VectorSubtract( max, min, e->v.size );
SV_LinkEdict( e, false );
@ -106,7 +119,7 @@ void SV_SetModel( edict_t *ent, const char *name )
{
vec3_t mins = { 0.0f, 0.0f, 0.0f };
vec3_t maxs = { 0.0f, 0.0f, 0.0f };
int i;
int i, mod_type;
i = SV_ModelIndex( name );
if( i == 0 ) return;
@ -114,9 +127,16 @@ void SV_SetModel( edict_t *ent, const char *name )
ent->v.model = MAKE_STRING( sv.configstrings[CS_MODELS+i] );
ent->v.modelindex = i;
// set sizes only for brush models
if( CM_GetModelType( ent->v.modelindex ) == mod_brush )
mod_type = CM_GetModelType( ent->v.modelindex );
// studio models set to zero sizes as default
switch( mod_type )
{
case mod_brush:
case mod_sprite:
Mod_GetBounds( ent->v.modelindex, mins, maxs );
break;
}
SV_SetMinMaxSize( ent, mins, maxs );
}
@ -528,16 +548,6 @@ void SV_PlaybackEvent( sizebuf_t *msg, event_info_t *info )
MSG_WriteDeltaEvent( msg, &nullargs, &info->args ); // FIXME: zero-compressing
}
bool SV_IsValidEdict( const edict_t *e )
{
if( !e ) return false;
if( e->free ) return false;
// edict without pvPrivateData is valid edict
// server.dll know how allocate it
return true;
}
const char *SV_ClassName( const edict_t *e )
{
if( !e ) return "(null)";
@ -1346,7 +1356,7 @@ void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float
// can't track this entity on the client.
// write static sound
if( !ent->num_leafs ) flags |= SND_FIXED_ORIGIN;
if( !ent->headnode ) flags |= SND_FIXED_ORIGIN;
// ultimate method for detect bsp models with invalid solidity (e.g. func_pushable)
if( CM_GetModelType( ent->v.modelindex ) == mod_brush )
@ -1389,7 +1399,7 @@ void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float
sound_idx = SV_SoundIndex( sample );
}
if( !ent->num_leafs )
if( !ent->headnode )
entityIndex = 0;
else if( SV_IsValidEdict( ent->v.aiment ))
entityIndex = ent->v.aiment->serialnumber;
@ -2334,6 +2344,9 @@ edict_t* pfnPEntityOfEntIndex( int iEntIndex )
{
if( iEntIndex < 0 || iEntIndex >= svgame.numEntities )
return NULL; // out of range
if( EDICT_NUM( iEntIndex )->free )
return NULL;
return EDICT_NUM( iEntIndex );
}
@ -2467,7 +2480,7 @@ static void pfnGetBonePosition( const edict_t* pEdict, int iBone, float *rgflOri
return;
}
CM_GetBonePosition( (edict_t *)pEdict, iBone, rgflOrigin, rgflAngles );
SV_GetBonePosition( (edict_t *)pEdict, iBone, rgflOrigin, rgflAngles );
}
/*
@ -2564,7 +2577,7 @@ static void pfnGetAttachment( const edict_t *pEdict, int iAttachment, float *rgf
MsgDev( D_WARN, "SV_GetAttachment: invalid entity %s\n", SV_ClassName( pEdict ));
return;
}
CM_StudioGetAttachment(( edict_t *)pEdict, iAttachment, rgflOrigin, rgflAngles );
SV_StudioGetAttachment(( edict_t *)pEdict, iAttachment, rgflOrigin, rgflAngles );
}
/*
@ -3253,50 +3266,51 @@ pfnCheckVisibility
=============
*/
int pfnCheckVisibility( const edict_t *entity, byte *pset )
int pfnCheckVisibility( const edict_t *ent, byte *pset )
{
int i, l;
if( !SV_IsValidEdict( entity ))
if( !SV_IsValidEdict( ent ))
{
MsgDev( D_WARN, "SV_CheckVisibility: invalid entity %s\n", SV_ClassName( entity ));
MsgDev( D_WARN, "SV_CheckVisibility: invalid entity %s\n", SV_ClassName( ent ));
return 0;
}
if( !pset ) return 1; // vis not set - fullvis enabled
// vis not set - fullvis enabled
if( !pset ) return 1;
// check individual leafs
if( !entity->num_leafs )
return 0; // not a linked in
if( !ent->headnode )
return 0; // not a linked in ?
if( entity->num_leafs == -1 )
{
// too many leafs for individual check, go by headnode
if( !CM_HeadnodeVisible( entity->headnode, pset ))
return 0;
}
else
if( ent->headnode == -1 )
{
int i;
// check individual leafs
for( i = 0; i < entity->num_leafs; i++ )
for( i = 0; i < ent->num_leafs; i++ )
{
l = entity->leafnums[i];
if( pset[l>>3] & (1<<( l & 7 )))
if( pset[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i] & 7 )))
break;
}
if( i == entity->num_leafs )
if( i == ent->num_leafs )
return 0; // not visible
}
// run additional check for BoxVisible
if( CM_GetModelType( entity->v.modelindex ) == mod_brush )
else
{
if( !CM_BoxVisible( entity->v.absmin, entity->v.absmax, pset ))
mnode_t *node;
// too many leafs for individual check, go by headnode
node = sv.worldmodel->nodes + ent->headnode;
if( !SV_HeadnodeVisible( node, pset ))
return 0;
}
#if 0
// NOTE: uncommenat this if you want to get more accuracy culling on large brushes
if( CM_GetModelType( ent->v.modelindex ) == mod_brush )
{
if( !CM_BoxVisible( ent->v.absmin, ent->v.absmax, pset ))
return 0;
}
#endif
return 1;
}

View File

@ -342,6 +342,7 @@ bool SV_SpawnServer( const char *mapname, const char *startspot )
com.sprintf( sv.configstrings[CS_MODELS+1], "maps/%s.bsp", sv.name );
CM_BeginRegistration( sv.configstrings[CS_MODELS+1], false, &checksum );
com.sprintf( sv.configstrings[CS_MAPCHECKSUM], "%i", checksum );
sv.worldmodel = CM_ClipHandleToModel( 1 ); // get world pointer
for( i = 1; i < CM_NumBmodels(); i++ )
{

View File

@ -34,22 +34,6 @@ Utility functions
===============================================================================
*/
/*
============
SV_TestEntityPosition
returns true if the entity is in solid currently
============
*/
bool SV_TestEntityPosition( edict_t *ent )
{
trace_t trace;
trace = SV_Move( ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NORMAL|FMOVE_SIMPLEBOX, ent );
return trace.fStartSolid;
}
/*
================
SV_CheckAllEnts

967
engine/server/sv_studio.c Normal file
View File

@ -0,0 +1,967 @@
//=======================================================================
// Copyright XashXT Group 2010 ©
// sv_studio.c - server studio utilities
//=======================================================================
// FIXME: these code needs be some cleanup from lerping code
#include "common.h"
#include "server.h"
#include "matrix_lib.h"
static studiohdr_t *sv_studiohdr;
static mplane_t sv_hitboxplanes[6]; // there a temp hitbox
static matrix4x4 sv_studiomatrix;
static matrix4x4 sv_studiobones[MAXSTUDIOBONES];
typedef bool (*pfnHitboxTrace)( trace_t *trace );
static float trace_realfraction;
static vec3_t trace_startmins, trace_endmins;
static vec3_t trace_startmaxs, trace_endmaxs;
static vec3_t trace_absmins, trace_absmaxs;
/*
====================
SV_InitStudioHull
====================
*/
void SV_InitStudioHull( void )
{
int i, side;
mplane_t *p;
for( i = 0; i < 6; i++ )
{
side = i & 1;
// planes
p = &sv_hitboxplanes[i];
VectorClear( p->normal );
if( side )
{
p->type = PLANE_NONAXIAL;
p->normal[i>>1] = -1.0f;
p->signbits = (1<<(i>>1));
}
else
{
p->type = i>>1;
p->normal[i>>1] = 1.0f;
p->signbits = 0;
}
}
}
/*
====================
SV_HullForHitbox
====================
*/
static void SV_HullForHitbox( const vec3_t mins, const vec3_t maxs )
{
sv_hitboxplanes[0].dist = maxs[0];
sv_hitboxplanes[1].dist = -mins[0];
sv_hitboxplanes[2].dist = maxs[1];
sv_hitboxplanes[3].dist = -mins[1];
sv_hitboxplanes[4].dist = maxs[2];
sv_hitboxplanes[5].dist = -mins[2];
}
/*
===============================================================================
STUDIO MODELS TRACING
===============================================================================
*/
/*
====================
StudioSetUpTransform
====================
*/
static void SV_StudioSetUpTransform( edict_t *ent )
{
float *ang, *org;
float scale = 1.0f;
org = ent->v.origin;
ang = ent->v.angles;
if( ent->v.scale != 0.0f ) scale = ent->v.scale;
Matrix4x4_CreateFromEntity( sv_studiomatrix, org[0], org[1], org[2], -ang[PITCH], ang[YAW], ang[ROLL], scale );
}
/*
====================
StudioCalcBoneAdj
====================
*/
static void SV_StudioCalcBoneAdj( float dadt, float *adj, const byte *pcontroller1, const byte *pcontroller2 )
{
int i, j;
float value;
mstudiobonecontroller_t *pbonecontroller;
pbonecontroller = (mstudiobonecontroller_t *)((byte *)sv_studiohdr + sv_studiohdr->bonecontrollerindex);
for( j = 0; j < sv_studiohdr->numbonecontrollers; j++ )
{
i = pbonecontroller[j].index;
if( i == 4 ) continue; // ignore mouth
if( i <= MAXSTUDIOCONTROLLERS )
{
// check for 360% wrapping
if( pbonecontroller[j].type & STUDIO_RLOOP )
{
if( abs( pcontroller1[i] - pcontroller2[i] ) > 128 )
{
int a, b;
a = (pcontroller1[j] + 128) % 256;
b = (pcontroller2[j] + 128) % 256;
value = ((a * dadt) + (b * (1.0f - dadt)) - 128) * (360.0f / 256.0f) + pbonecontroller[j].start;
}
else
{
value = ((pcontroller1[i] * dadt + (pcontroller2[i]) * (1.0 - dadt))) * (360.0/256.0) + pbonecontroller[j].start;
}
}
else
{
value = (pcontroller1[i] * dadt + pcontroller2[i] * (1.0f - dadt)) / 255.0f;
if( value < 0.0f ) value = 0.0f;
if( value > 1.0f ) value = 1.0f;
value = (1.0f - value) * pbonecontroller[j].start + value * pbonecontroller[j].end;
}
}
switch( pbonecontroller[j].type & STUDIO_TYPES )
{
case STUDIO_XR:
case STUDIO_YR:
case STUDIO_ZR:
adj[j] = value * (M_PI / 180.0f);
break;
case STUDIO_X:
case STUDIO_Y:
case STUDIO_Z:
adj[j] = value;
break;
}
}
}
/*
====================
StudioCalcBoneQuaterion
====================
*/
static void SV_StudioCalcBoneQuaterion( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, float *q )
{
int j, k;
vec4_t q1, q2;
vec3_t angle1, angle2;
mstudioanimvalue_t *panimvalue;
for( j = 0; j < 3; j++ )
{
if( panim->offset[j+3] == 0 )
{
angle2[j] = angle1[j] = pbone->value[j+3]; // default;
}
else
{
panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j+3]);
k = frame;
// debug
if( panimvalue->num.total < panimvalue->num.valid )
k = 0;
while( panimvalue->num.total <= k )
{
k -= panimvalue->num.total;
panimvalue += panimvalue->num.valid + 1;
// DEBUG
if( panimvalue->num.total < panimvalue->num.valid )
k = 0;
}
// Bah, missing blend!
if( panimvalue->num.valid > k )
{
angle1[j] = panimvalue[k+1].value;
if( panimvalue->num.valid > k + 1 )
{
angle2[j] = panimvalue[k+2].value;
}
else
{
if( panimvalue->num.total > k + 1 )
angle2[j] = angle1[j];
else angle2[j] = panimvalue[panimvalue->num.valid+2].value;
}
}
else
{
angle1[j] = panimvalue[panimvalue->num.valid].value;
if( panimvalue->num.total > k + 1 )
{
angle2[j] = angle1[j];
}
else
{
angle2[j] = panimvalue[panimvalue->num.valid + 2].value;
}
}
angle1[j] = pbone->value[j+3] + angle1[j] * pbone->scale[j+3];
angle2[j] = pbone->value[j+3] + angle2[j] * pbone->scale[j+3];
}
if( pbone->bonecontroller[j+3] != -1 )
{
angle1[j] += adj[pbone->bonecontroller[j+3]];
angle2[j] += adj[pbone->bonecontroller[j+3]];
}
}
if( !VectorCompare( angle1, angle2 ))
{
AngleQuaternion( angle1, q1 );
AngleQuaternion( angle2, q2 );
QuaternionSlerp( q1, q2, s, q );
}
else
{
AngleQuaternion( angle1, q );
}
}
/*
====================
StudioCalcBonePosition
====================
*/
static void SV_StudioCalcBonePosition( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, float *pos )
{
int j, k;
mstudioanimvalue_t *panimvalue;
for( j = 0; j < 3; j++ )
{
pos[j] = pbone->value[j]; // default;
if( panim->offset[j] != 0.0f )
{
panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j]);
k = frame;
// debug
if( panimvalue->num.total < panimvalue->num.valid )
k = 0;
// find span of values that includes the frame we want
while( panimvalue->num.total <= k )
{
k -= panimvalue->num.total;
panimvalue += panimvalue->num.valid + 1;
// DEBUG
if( panimvalue->num.total < panimvalue->num.valid )
k = 0;
}
// if we're inside the span
if( panimvalue->num.valid > k )
{
// and there's more data in the span
if( panimvalue->num.valid > k + 1 )
{
pos[j] += (panimvalue[k+1].value * (1.0f - s) + s * panimvalue[k+2].value) * pbone->scale[j];
}
else
{
pos[j] += panimvalue[k+1].value * pbone->scale[j];
}
}
else
{
// are we at the end of the repeating values section and there's another section with data?
if( panimvalue->num.total <= k + 1 )
{
pos[j] += (panimvalue[panimvalue->num.valid].value * (1.0f - s) + s * panimvalue[panimvalue->num.valid + 2].value) * pbone->scale[j];
}
else
{
pos[j] += panimvalue[panimvalue->num.valid].value * pbone->scale[j];
}
}
}
if( pbone->bonecontroller[j] != -1 && adj )
{
pos[j] += adj[pbone->bonecontroller[j]];
}
}
}
/*
====================
StudioCalcRotations
====================
*/
static void SV_StudioCalcRotations( edict_t *ent, float pos[][3], vec4_t *q, mstudioseqdesc_t *pseqdesc, mstudioanim_t *panim, float f )
{
int i, frame;
mstudiobone_t *pbone;
float adj[MAXSTUDIOCONTROLLERS];
float s, dadt = 1.0f; // noInterp
if( f > pseqdesc->numframes - 1 )
f = 0;
else if( f < -0.01f )
f = -0.01f;
frame = (int)f;
s = (f - frame);
// add in programtic controllers
pbone = (mstudiobone_t *)((byte *)sv_studiohdr + sv_studiohdr->boneindex);
SV_StudioCalcBoneAdj( dadt, adj, ent->v.controller, ent->v.controller );
for( i = 0; i < sv_studiohdr->numbones; i++, pbone++, panim++ )
{
SV_StudioCalcBoneQuaterion( frame, s, pbone, panim, adj, q[i] );
SV_StudioCalcBonePosition( frame, s, pbone, panim, adj, pos[i] );
}
if( pseqdesc->motiontype & STUDIO_X ) pos[pseqdesc->motionbone][0] = 0.0f;
if( pseqdesc->motiontype & STUDIO_Y ) pos[pseqdesc->motionbone][1] = 0.0f;
if( pseqdesc->motiontype & STUDIO_Z ) pos[pseqdesc->motionbone][2] = 0.0f;
s = 0 * ((1.0f - (f - (int)(f))) / (pseqdesc->numframes)) * ent->v.framerate;
if( pseqdesc->motiontype & STUDIO_LX ) pos[pseqdesc->motionbone][0] += s * pseqdesc->linearmovement[0];
if( pseqdesc->motiontype & STUDIO_LY ) pos[pseqdesc->motionbone][1] += s * pseqdesc->linearmovement[1];
if( pseqdesc->motiontype & STUDIO_LZ ) pos[pseqdesc->motionbone][2] += s * pseqdesc->linearmovement[2];
}
/*
====================
StudioEstimateFrame
====================
*/
static float SV_StudioEstimateFrame( edict_t *ent, mstudioseqdesc_t *pseqdesc )
{
double f;
if( pseqdesc->numframes <= 1 )
f = 0;
else f = (ent->v.frame * ( pseqdesc->numframes - 1 )) / 256.0;
if( pseqdesc->flags & STUDIO_LOOPING )
{
if( pseqdesc->numframes > 1 )
f -= (int)(f / (pseqdesc->numframes - 1)) * (pseqdesc->numframes - 1);
if( f < 0 ) f += (pseqdesc->numframes - 1);
}
else
{
if( f >= pseqdesc->numframes - 1.001 )
f = pseqdesc->numframes - 1.001;
if( f < 0.0 ) f = 0.0;
}
return f;
}
/*
====================
StudioSlerpBones
====================
*/
static void SV_StudioSlerpBones( vec4_t q1[], float pos1[][3], vec4_t q2[], float pos2[][3], float s )
{
int i;
vec4_t q3;
float s1;
s = bound( 0.0f, s, 1.0f );
s1 = 1.0f - s;
for( i = 0; i < sv_studiohdr->numbones; i++ )
{
QuaternionSlerp( q1[i], q2[i], s, q3 );
q1[i][0] = q3[0];
q1[i][1] = q3[1];
q1[i][2] = q3[2];
q1[i][3] = q3[3];
pos1[i][0] = pos1[i][0] * s1 + pos2[i][0] * s;
pos1[i][1] = pos1[i][1] * s1 + pos2[i][1] * s;
pos1[i][2] = pos1[i][2] * s1 + pos2[i][2] * s;
}
}
/*
====================
StudioGetAnim
====================
*/
static mstudioanim_t *SV_StudioGetAnim( model_t *m_pSubModel, mstudioseqdesc_t *pseqdesc )
{
mstudioseqgroup_t *pseqgroup;
cache_user_t *paSequences;
size_t filesize;
byte *buf;
pseqgroup = (mstudioseqgroup_t *)((byte *)sv_studiohdr + sv_studiohdr->seqgroupindex) + pseqdesc->seqgroup;
if( pseqdesc->seqgroup == 0 )
return (mstudioanim_t *)((byte *)sv_studiohdr + pseqgroup->data + pseqdesc->animindex);
paSequences = (cache_user_t *)m_pSubModel->submodels;
if( paSequences == NULL )
{
paSequences = (cache_user_t *)Mem_Alloc( m_pSubModel->mempool, MAXSTUDIOGROUPS * sizeof( cache_user_t ));
m_pSubModel->submodels = (void *)paSequences;
}
// check for already loaded
if( !Cache_Check( m_pSubModel->mempool, ( cache_user_t *)&( paSequences[pseqdesc->seqgroup] )))
{
string filepath, modelname, modelpath;
FS_FileBase( m_pSubModel->name, modelname );
FS_ExtractFilePath( m_pSubModel->name, modelpath );
com.snprintf( filepath, sizeof( filepath ), "%s/%s%i%i.mdl", modelpath, modelname, pseqdesc->seqgroup / 10, pseqdesc->seqgroup % 10 );
buf = FS_LoadFile( filepath, &filesize );
if( !buf || !filesize ) Host_Error( "CM_StudioGetAnim: can't load %s\n", modelpath );
if( IDSEQGRPHEADER != *(uint *)buf )
Host_Error( "SV_StudioGetAnim: %s is corrupted\n", modelpath );
paSequences[pseqdesc->seqgroup].data = Mem_Alloc( m_pSubModel->mempool, filesize );
Mem_Copy( paSequences[pseqdesc->seqgroup].data, buf, filesize );
Mem_Free( buf );
}
return (mstudioanim_t *)((byte *)paSequences[pseqdesc->seqgroup].data + pseqdesc->animindex);
}
/*
====================
StudioSetupBones
====================
*/
static void SV_StudioSetupBones( edict_t *ent )
{
int i;
double f;
mstudiobone_t *pbones;
mstudioseqdesc_t *pseqdesc;
mstudioanim_t *panim;
model_t *m_pModel;
static float pos[MAXSTUDIOBONES][3];
static vec4_t q[MAXSTUDIOBONES];
matrix4x4 bonematrix;
static float pos2[MAXSTUDIOBONES][3];
static vec4_t q2[MAXSTUDIOBONES];
static float pos3[MAXSTUDIOBONES][3];
static vec4_t q3[MAXSTUDIOBONES];
static float pos4[MAXSTUDIOBONES][3];
static vec4_t q4[MAXSTUDIOBONES];
m_pModel = CM_ClipHandleToModel( ent->v.modelindex );
if( ent->v.sequence >= sv_studiohdr->numseq ) ent->v.sequence = 0;
pseqdesc = (mstudioseqdesc_t *)((byte *)sv_studiohdr + sv_studiohdr->seqindex) + ent->v.sequence;
f = SV_StudioEstimateFrame( ent, pseqdesc );
panim = SV_StudioGetAnim( m_pModel, pseqdesc );
SV_StudioCalcRotations( ent, pos, q, pseqdesc, panim, f );
if( pseqdesc->numblends > 1 )
{
float s;
float dadt = 1.0f;
panim += sv_studiohdr->numbones;
SV_StudioCalcRotations( ent, pos2, q2, pseqdesc, panim, f );
s = (ent->v.blending[0] * dadt + ent->v.blending[0] * ( 1.0f - dadt )) / 255.0f;
SV_StudioSlerpBones( q, pos, q2, pos2, s );
if( pseqdesc->numblends == 4 )
{
panim += sv_studiohdr->numbones;
SV_StudioCalcRotations( ent, pos3, q3, pseqdesc, panim, f );
panim += sv_studiohdr->numbones;
SV_StudioCalcRotations( ent, pos4, q4, pseqdesc, panim, f );
s = ( ent->v.blending[0] * dadt + ent->v.blending[0] * ( 1.0f - dadt )) / 255.0f;
SV_StudioSlerpBones( q3, pos3, q4, pos4, s );
s = ( ent->v.blending[1] * dadt + ent->v.blending[1] * ( 1.0f - dadt )) / 255.0f;
SV_StudioSlerpBones( q, pos, q3, pos3, s );
}
}
pbones = (mstudiobone_t *)((byte *)sv_studiohdr + sv_studiohdr->boneindex);
for( i = 0; i < sv_studiohdr->numbones; i++ )
{
Matrix4x4_FromOriginQuat( bonematrix, pos[i][0], pos[i][1], pos[i][2], q[i][0], q[i][1], q[i][2], q[i][3] );
if( pbones[i].parent == -1 )
Matrix4x4_ConcatTransforms( sv_studiobones[i], sv_studiomatrix, bonematrix );
else Matrix4x4_ConcatTransforms( sv_studiobones[i], sv_studiobones[pbones[i].parent], bonematrix );
}
}
/*
====================
StudioCalcAttachments
====================
*/
static bool SV_StudioCalcAttachments( edict_t *e, int iAttachment, float *org, float *ang )
{
int i;
mstudioattachment_t *pAtt;
vec3_t axis[3];
vec3_t localOrg, localAng;
void *hdr = Mod_Extradata( e->v.modelindex );
if( !hdr ) return false;
sv_studiohdr = (studiohdr_t *)hdr;
if( sv_studiohdr->numattachments <= 0 )
return false;
SV_StudioSetUpTransform( e );
SV_StudioSetupBones( e );
if( sv_studiohdr->numattachments > MAXSTUDIOATTACHMENTS )
{
sv_studiohdr->numattachments = MAXSTUDIOATTACHMENTS; // reduce it
MsgDev( D_WARN, "SV_StudioCalcAttahments: too many attachments on %s\n", sv_studiohdr->name );
}
iAttachment = bound( 0, iAttachment, sv_studiohdr->numattachments );
// calculate attachment points
pAtt = (mstudioattachment_t *)((byte *)sv_studiohdr + sv_studiohdr->attachmentindex);
for( i = 0; i < sv_studiohdr->numattachments; i++ )
{
if( i == iAttachment )
{
// compute pos and angles
Matrix4x4_VectorTransform( sv_studiobones[pAtt[i].bone], pAtt[i].org, localOrg );
Matrix4x4_VectorTransform( sv_studiobones[pAtt[i].bone], pAtt[i].vectors[0], axis[0] );
Matrix4x4_VectorTransform( sv_studiobones[pAtt[i].bone], pAtt[i].vectors[1], axis[1] );
Matrix4x4_VectorTransform( sv_studiobones[pAtt[i].bone], pAtt[i].vectors[2], axis[2] );
Matrix3x3_ToAngles( axis, localAng, true ); // FIXME: dll's uses FLU ?
if( org ) VectorCopy( localOrg, org );
if( ang ) VectorCopy( localAng, ang );
break; // done
}
}
return true;
}
static bool SV_StudioSetupModel( edict_t *ent )
{
void *hdr = Mod_Extradata( ent->v.modelindex );
if( !hdr ) return false;
sv_studiohdr = (studiohdr_t *)hdr;
SV_StudioSetUpTransform( ent );
SV_StudioSetupBones( ent );
return true;
}
bool SV_StudioExtractBbox( model_t *mod, int sequence, float *mins, float *maxs )
{
mstudioseqdesc_t *pseqdesc;
studiohdr_t *phdr;
ASSERT( mod != NULL );
if( mod->type != mod_studio || !mod->extradata )
return false;
phdr = (studiohdr_t *)mod->extradata;
if( !phdr->numhitboxes ) return false;
pseqdesc = (mstudioseqdesc_t *)((byte *)phdr + phdr->seqindex);
if( sequence < 0 || sequence >= phdr->numseq )
return false;
VectorCopy( pseqdesc[sequence].bbmin, mins );
VectorCopy( pseqdesc[sequence].bbmax, maxs );
return true;
}
/*
================
StudioTestToHitbox
test point trace in hitbox
================
*/
static bool SV_StudioTestToHitbox( trace_t *trace )
{
int i;
mplane_t *p;
for( i = 0; i < 6; i++ )
{
p = &sv_hitboxplanes[i];
// push the plane out apropriately for mins/maxs
// if completely in front of face, no intersection
if( p->type < 3 )
{
if( trace_startmins[p->type] > p->dist )
return false;
}
else
{
switch( p->signbits )
{
case 0:
if( p->normal[0]*trace_startmins[0] + p->normal[1]*trace_startmins[1] + p->normal[2]*trace_startmins[2] > p->dist )
return false;
break;
case 1:
if( p->normal[0]*trace_startmaxs[0] + p->normal[1]*trace_startmins[1] + p->normal[2]*trace_startmins[2] > p->dist )
return false;
break;
case 2:
if( p->normal[0]*trace_startmins[0] + p->normal[1]*trace_startmaxs[1] + p->normal[2]*trace_startmins[2] > p->dist )
return false;
break;
case 3:
if( p->normal[0]*trace_startmaxs[0] + p->normal[1]*trace_startmaxs[1] + p->normal[2]*trace_startmins[2] > p->dist )
return false;
break;
case 4:
if( p->normal[0]*trace_startmins[0] + p->normal[1]*trace_startmins[1] + p->normal[2]*trace_startmaxs[2] > p->dist )
return false;
break;
case 5:
if( p->normal[0]*trace_startmaxs[0] + p->normal[1]*trace_startmins[1] + p->normal[2]*trace_startmaxs[2] > p->dist )
return false;
break;
case 6:
if( p->normal[0]*trace_startmins[0] + p->normal[1]*trace_startmaxs[1] + p->normal[2]*trace_startmaxs[2] > p->dist )
return false;
break;
case 7:
if( p->normal[0]*trace_startmaxs[0] + p->normal[1]*trace_startmaxs[1] + p->normal[2]*trace_startmaxs[2] > p->dist )
return false;
break;
default:
return false;
}
}
}
// inside this hitbox
trace->flFraction = trace_realfraction = 0;
trace->fStartSolid = trace->fAllSolid = true;
return true;
}
/*
================
StudioClipToHitbox
trace hitbox
================
*/
static bool SV_StudioClipToHitbox( trace_t *trace )
{
int i;
mplane_t *p, *clipplane;
float enterfrac, leavefrac, distfrac;
float d, d1, d2;
bool getout, startout;
float f;
enterfrac = -1.0f;
leavefrac = 1.0f;
clipplane = NULL;
getout = false;
startout = false;
for( i = 0; i < 6; i++ )
{
p = &sv_hitboxplanes[i];
// push the plane out apropriately for mins/maxs
if( p->type < 3 )
{
d1 = trace_startmins[p->type] - p->dist;
d2 = trace_endmins[p->type] - p->dist;
}
else
{
switch( p->signbits )
{
case 0:
d1 = p->normal[0]*trace_startmins[0] + p->normal[1]*trace_startmins[1] + p->normal[2]*trace_startmins[2] - p->dist;
d2 = p->normal[0]*trace_endmins[0] + p->normal[1]*trace_endmins[1] + p->normal[2]*trace_endmins[2] - p->dist;
break;
case 1:
d1 = p->normal[0]*trace_startmaxs[0] + p->normal[1]*trace_startmins[1] + p->normal[2]*trace_startmins[2] - p->dist;
d2 = p->normal[0]*trace_endmaxs[0] + p->normal[1]*trace_endmins[1] + p->normal[2]*trace_endmins[2] - p->dist;
break;
case 2:
d1 = p->normal[0]*trace_startmins[0] + p->normal[1]*trace_startmaxs[1] + p->normal[2]*trace_startmins[2] - p->dist;
d2 = p->normal[0]*trace_endmins[0] + p->normal[1]*trace_endmaxs[1] + p->normal[2]*trace_endmins[2] - p->dist;
break;
case 3:
d1 = p->normal[0]*trace_startmaxs[0] + p->normal[1]*trace_startmaxs[1] + p->normal[2]*trace_startmins[2] - p->dist;
d2 = p->normal[0]*trace_endmaxs[0] + p->normal[1]*trace_endmaxs[1] + p->normal[2]*trace_endmins[2] - p->dist;
break;
case 4:
d1 = p->normal[0]*trace_startmins[0] + p->normal[1]*trace_startmins[1] + p->normal[2]*trace_startmaxs[2] - p->dist;
d2 = p->normal[0]*trace_endmins[0] + p->normal[1]*trace_endmins[1] + p->normal[2]*trace_endmaxs[2] - p->dist;
break;
case 5:
d1 = p->normal[0]*trace_startmaxs[0] + p->normal[1]*trace_startmins[1] + p->normal[2]*trace_startmaxs[2] - p->dist;
d2 = p->normal[0]*trace_endmaxs[0] + p->normal[1]*trace_endmins[1] + p->normal[2]*trace_endmaxs[2] - p->dist;
break;
case 6:
d1 = p->normal[0]*trace_startmins[0] + p->normal[1]*trace_startmaxs[1] + p->normal[2]*trace_startmaxs[2] - p->dist;
d2 = p->normal[0]*trace_endmins[0] + p->normal[1]*trace_endmaxs[1] + p->normal[2]*trace_endmaxs[2] - p->dist;
break;
case 7:
d1 = p->normal[0]*trace_startmaxs[0] + p->normal[1]*trace_startmaxs[1] + p->normal[2]*trace_startmaxs[2] - p->dist;
d2 = p->normal[0]*trace_endmaxs[0] + p->normal[1]*trace_endmaxs[1] + p->normal[2]*trace_endmaxs[2] - p->dist;
break;
default:
d1 = d2 = 0; // shut up compiler
break;
}
}
if( d2 > 0 ) getout = true; // endpoint is not in solid
if( d1 > 0 ) startout = true;
// if completely in front of face, no intersection
if( d1 > 0 && d2 >= d1 )
return false;
if( d1 <= 0 && d2 <= 0 )
continue;
// crosses face
d = 1.0f / ( d1 - d2 );
f = d1 * d;
if( d > 0 )
{
// enter
if( f > enterfrac )
{
distfrac = d;
enterfrac = f;
clipplane = p;
}
}
else if( d < 0 )
{
// leave
if( f < leavefrac )
leavefrac = f;
}
}
if( !startout )
{
// original point was inside hitbox
trace->fStartSolid = true;
if( !getout ) trace->fAllSolid = true;
return true;
}
if( enterfrac - FRAC_EPSILON <= leavefrac )
{
if( enterfrac > -1.0f && enterfrac < trace_realfraction )
{
if( enterfrac < 0 )
enterfrac = 0;
trace_realfraction = enterfrac;
trace->flFraction = enterfrac - DIST_EPSILON * distfrac;
VectorCopy( clipplane->normal, trace->vecPlaneNormal );
trace->flPlaneDist = clipplane->dist;
return true;
}
}
return false;
}
/*
================
SV_StudioIntersect
testing for potentially intersection of trace and animation bboxes
================
*/
static bool SV_StudioIntersect( edict_t *ent, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end )
{
vec3_t trace_mins, trace_maxs;
vec3_t anim_mins, anim_maxs;
model_t *mod = CM_ClipHandleToModel( ent->v.modelindex );
// create the bounding box of the entire move
World_MoveBounds( start, mins, maxs, end, trace_mins, trace_maxs );
if( !SV_StudioExtractBbox( mod, ent->v.sequence, anim_mins, anim_maxs ))
return false; // invalid sequence
if( !VectorIsNull( ent->v.angles ))
{
// expand for rotation
float max, v;
int i;
for( i = 0, max = 0.0f; i < 3; i++ )
{
v = fabs( anim_mins[i] );
if( v > max ) max = v;
v = fabs( anim_maxs[i] );
if( v > max ) max = v;
}
for( i = 0; i < 3; i++ )
{
anim_mins[i] = ent->v.origin[i] - max;
anim_maxs[i] = ent->v.origin[i] + max;
}
}
else
{
VectorAdd( anim_mins, ent->v.origin, anim_mins );
VectorAdd( anim_maxs, ent->v.origin, anim_maxs );
}
// check intersection with trace entire move and animation bbox
return BoundsIntersect( trace_mins, trace_maxs, anim_mins, anim_maxs );
}
bool SV_StudioTrace( edict_t *ent, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, trace_t *ptr )
{
vec3_t start_l, end_l;
int i, outBone = -1;
pfnHitboxTrace TraceHitbox = NULL;
// assume we didn't hit anything
Mem_Set( ptr, 0, sizeof( pmtrace_t ));
VectorCopy( end, ptr->vecEndPos );
ptr->flFraction = trace_realfraction = 1.0f;
ptr->iHitgroup = -1;
if( !SV_StudioIntersect( ent, start, mins, maxs, end ))
return false;
if( !SV_StudioSetupModel( ent ))
return false;
if( VectorCompare( start, end ))
TraceHitbox = SV_StudioTestToHitbox; // special case for test position
else TraceHitbox = SV_StudioClipToHitbox;
// go to check individual hitboxes
for( i = 0; i < sv_studiohdr->numhitboxes; i++ )
{
mstudiobbox_t *phitbox = (mstudiobbox_t *)((byte*)sv_studiohdr + sv_studiohdr->hitboxindex) + i;
matrix4x4 bonemat;
// transform traceline into local bone space
Matrix4x4_Invert_Simple( bonemat, sv_studiobones[phitbox->bone] );
Matrix4x4_VectorTransform( bonemat, start, start_l );
Matrix4x4_VectorTransform( bonemat, end, end_l );
SV_HullForHitbox( phitbox->bbmin, phitbox->bbmax );
VectorAdd( start_l, mins, trace_startmins );
VectorAdd( start_l, maxs, trace_startmaxs );
VectorAdd( end_l, mins, trace_endmins );
VectorAdd( end_l, maxs, trace_endmaxs );
if( TraceHitbox( ptr ))
{
outBone = phitbox->bone;
ptr->iHitgroup = phitbox->group;
}
if( ptr->fAllSolid )
break;
}
// all hitboxes were swept, get trace result
if( outBone >= 0 )
{
vec3_t temp;
ptr->pHit = ent;
VectorCopy( ptr->vecPlaneNormal, temp );
ptr->flFraction = bound( 0, ptr->flFraction, 1.0f );
VectorLerp( start, ptr->flFraction, end, ptr->vecEndPos );
Matrix4x4_TransformPositivePlane( sv_studiobones[outBone], temp, ptr->flPlaneDist, ptr->vecPlaneNormal, &ptr->flPlaneDist );
return true;
}
return false;
}
void SV_StudioGetAttachment( edict_t *e, int iAttachment, float *org, float *ang )
{
if( !SV_StudioCalcAttachments( e, iAttachment, org, ang ))
{
// reset attachments
if( org ) VectorCopy( e->v.origin, org );
if( ang ) VectorCopy( e->v.angles, ang );
return;
}
}
void SV_GetBonePosition( edict_t *e, int iBone, float *org, float *ang )
{
matrix3x3 axis;
if( !SV_StudioSetupModel( e ) || sv_studiohdr->numbones <= 0 )
{
// reset bones
if( org ) VectorCopy( e->v.origin, org );
if( ang ) VectorCopy( e->v.angles, ang );
return;
}
iBone = bound( 0, iBone, sv_studiohdr->numbones );
Matrix3x3_FromMatrix4x4( axis, sv_studiobones[iBone] );
if( org ) Matrix4x4_OriginFromMatrix( sv_studiobones[iBone], org );
if( ang ) Matrix3x3_ToAngles( axis, ang, true );
}

View File

@ -8,6 +8,159 @@
#include "const.h"
#include "pm_local.h"
typedef struct moveclip_s
{
vec3_t boxmins, boxmaxs; // enclose the test object along entire move
float *mins, *maxs; // size of the moving object
vec3_t mins2, maxs2; // size when clipping against mosnters
const float *start, *end;
trace_t trace;
edict_t *passedict;
int type; // move type
int flags; // trace flags
int trace_hull; // -1 to let entity select hull
} moveclip_t;
/*
===============================================================================
HULL BOXES
===============================================================================
*/
static hull_t box_hull;
static dclipnode_t box_clipnodes[6];
static mplane_t box_planes[6];
/*
===================
SV_InitBoxHull
Set up the planes and clipnodes so that the six floats of a bounding box
can just be stored out and get a proper hull_t structure.
===================
*/
void SV_InitBoxHull( void )
{
int i, side;
box_hull.clipnodes = box_clipnodes;
box_hull.planes = box_planes;
box_hull.firstclipnode = 0;
box_hull.lastclipnode = 5;
for( i = 0; i < 6; i++ )
{
box_clipnodes[i].planenum = i;
side = i & 1;
box_clipnodes[i].children[side] = CONTENTS_EMPTY;
if( i != 5 ) box_clipnodes[i].children[side^1] = i + 1;
else box_clipnodes[i].children[side^1] = CONTENTS_SOLID;
box_planes[i].type = i>>1;
box_planes[i].normal[i>>1] = 1;
}
}
/*
===================
SV_HullForBox
To keep everything totally uniform, bounding boxes are turned into small
BSP trees instead of being compared directly.
===================
*/
hull_t *SV_HullForBox( const vec3_t mins, const vec3_t maxs )
{
box_planes[0].dist = maxs[0];
box_planes[1].dist = mins[0];
box_planes[2].dist = maxs[1];
box_planes[3].dist = mins[1];
box_planes[4].dist = maxs[2];
box_planes[5].dist = mins[2];
return &box_hull;
}
/*
================
SV_HullForEntity
Returns a hull that can be used for testing or clipping an object of mins/maxs
size.
Offset is filled in to contain the adjustment that must be added to the
testing object's origin to get a point to use with the returned hull.
================
*/
hull_t *SV_HullForEntity( edict_t *ent, int hullNumber, vec3_t mins, vec3_t maxs, vec3_t offset )
{
hull_t *hull;
model_t *model;
vec3_t hullmins, hullmaxs;
vec3_t size;
// decide which clipping hull to use, based on the size
if( ent->v.solid == SOLID_BSP )
{
// explicit hulls in the BSP model
if( ent->v.movetype != MOVETYPE_PUSH )
Host_Error( "SOLID_BSP without MOVETYPE_PUSH\n" );
model = CM_ClipHandleToModel( ent->v.modelindex );
if( !model || model->type != mod_brush && model->type != mod_world )
Host_Error( "MOVETYPE_PUSH with a non bsp model\n" );
VectorSubtract( maxs, mins, size );
if( hullNumber == -1 )
{
// FIXME: these constanst doesn't works with user-defined hulls
// revisit this
if( size[0] < 3 )
hull = &model->hulls[0]; // point hull
else if( size[0] <= 36 )
{
if( size[2] <= 36 )
hull = &model->hulls[3]; // head hull (ducked)
else hull = &model->hulls[1]; // human hull
}
else hull = &model->hulls[2]; // large hull
}
else
{
// TraceHull stuff
hull = &model->hulls[hullNumber];
}
// calculate an offset value to center the origin
VectorSubtract( hull->clip_mins, mins, offset );
VectorAdd( offset, ent->v.origin, offset );
}
else
{
// create a temp hull from bounding box sizes
VectorSubtract( ent->v.mins, maxs, hullmins );
VectorSubtract( ent->v.maxs, mins, hullmaxs );
hull = SV_HullForBox( hullmins, hullmaxs );
VectorCopy( ent->v.origin, offset );
}
return hull;
}
/*
===============================================================================
ENTITY AREA CHECKING
===============================================================================
*/
areanode_t sv_areanodes[AREA_NODES];
int sv_numareanodes;
@ -43,7 +196,7 @@ areanode_t *SV_CreateAreaNode( int depth, vec3_t mins, vec3_t maxs )
anode->axis = 0;
else anode->axis = 1;
anode->dist = 0.5f * (maxs[anode->axis] + mins[anode->axis]);
anode->dist = 0.5f * ( maxs[anode->axis] + mins[anode->axis] );
VectorCopy( mins, mins1 );
VectorCopy( mins, mins2 );
VectorCopy( maxs, maxs1 );
@ -64,13 +217,28 @@ SV_ClearWorld
*/
void SV_ClearWorld( void )
{
vec3_t mins, maxs;
int worldIndex = 1;
SV_InitBoxHull(); // for box testing
SV_InitStudioHull();// for hitbox testing
sv_numareanodes = 0;
Mod_GetBounds( worldIndex, mins, maxs );
Mem_Set( sv_areanodes, 0, sizeof( sv_areanodes ));
SV_CreateAreaNode( 0, mins, maxs );
sv_numareanodes = 0;
SV_CreateAreaNode( 0, sv.worldmodel->mins, sv.worldmodel->maxs );
}
/*
===============
SV_UnlinkEdict
===============
*/
void SV_UnlinkEdict( edict_t *ent )
{
// not linked in anywhere
if( !ent->area.prev ) return;
RemoveLink( &ent->area );
ent->area.prev = NULL;
ent->area.next = NULL;
}
/*
@ -90,27 +258,27 @@ void SV_TouchLinks( edict_t *ent, areanode_t *node )
{
next = l->next;
touch = EDICT_FROM_AREA( l );
if( touch == ent ) continue;
if( touch->free || touch->v.solid != SOLID_TRIGGER ) // disabled ?
if( touch == ent || touch->v.solid != SOLID_TRIGGER ) // disabled ?
continue;
if( !BoundsIntersect( ent->v.absmin, ent->v.absmax, touch->v.absmin, touch->v.absmax ))
continue;
// check triggers accuracy
if( CM_GetModelType( touch->v.modelindex ) == mod_brush )
{
hull = CM_HullForBsp( touch, ent->v.mins, ent->v.maxs, test );
hull = SV_HullForEntity( touch, -1, ent->v.mins, ent->v.maxs, test );
// offset the test point appropriately for this hull.
VectorSubtract( ent->v.origin, test, test );
// test hull for intersection with this model
if( CM_HullPointContents( hull, hull->firstclipnode, test ) == CONTENTS_EMPTY )
if( SV_HullPointContents( hull, hull->firstclipnode, test ) == CONTENTS_EMPTY )
continue;
}
svgame.dllFuncs.pfnTouch( touch, ent );
if( ent->free ) break; // killtarget issues
}
// recurse down both sides
@ -122,30 +290,17 @@ void SV_TouchLinks( edict_t *ent, areanode_t *node )
SV_TouchLinks( ent, node->children[1] );
}
/*
===============
SV_UnlinkEdict
===============
*/
void SV_UnlinkEdict( edict_t *ent )
{
// not linked in anywhere
if( !ent->area.prev ) return;
RemoveLink( &ent->area );
ent->area.prev = NULL;
ent->area.next = NULL;
}
/*
===============
SV_CheckForOutside
Remove entity out of level
Remove entity out of the level
===============
*/
void SV_CheckForOutside( edict_t *ent )
{
const float *org;
// not solid edicts can be fly through walls
if( ent->v.solid == SOLID_NOT ) return;
@ -159,29 +314,105 @@ void SV_CheckForOutside( edict_t *ent )
if( CM_GetModelType( ent->v.modelindex ) != mod_studio )
return;
if( SV_PointContents( ent->v.origin ) == CONTENTS_SOLID )
{
const float *org = ent->v.origin;
org = ent->v.origin;
MsgDev( D_ERROR, "%s outside of the world at %g %g %g\n", SV_ClassName( ent ), org[0], org[1], org[2] );
ent->v.flags |= FL_KILLME;
}
if( SV_PointContents( org ) != CONTENTS_SOLID )
return;
MsgDev( D_ERROR, "%s outside of the world at %g %g %g\n", SV_ClassName( ent ), org[0], org[1], org[2] );
ent->v.flags |= FL_KILLME;
}
/*
===============
SV_LinkEntity
SV_FindTouchedLeafs
===============
*/
void SV_FindTouchedLeafs( edict_t *ent, mnode_t *node )
{
mplane_t *splitplane;
int sides, leafnum;
mleaf_t *leaf;
if( node->contents == CONTENTS_SOLID )
return;
// add an efrag if the node is a leaf
if( node->contents < 0 )
{
// get headnode
if( ent->headnode == -1 )
ent->headnode = node - worldmodel->nodes;
if( ent->num_leafs == MAX_ENT_LEAFS )
return;
leaf = (mleaf_t *)node;
leafnum = leaf - sv.worldmodel->leafs - 1;
ent->leafnums[ent->num_leafs] = leafnum;
ent->num_leafs++;
return;
}
// NODE_MIXED
splitplane = node->plane;
sides = BOX_ON_PLANE_SIDE( ent->v.absmin, ent->v.absmax, splitplane );
if( sides == 3 )
{
// get headnode
if( ent->headnode == -1 )
ent->headnode = node - worldmodel->nodes;
}
// recurse down the contacted sides
if( sides & 1 ) SV_FindTouchedLeafs( ent, node->children[0] );
if( sides & 2 ) SV_FindTouchedLeafs( ent, node->children[1] );
}
/*
=============
SV_HeadnodeVisible
=============
*/
bool SV_HeadnodeVisible( mnode_t *node, byte *visbits )
{
mleaf_t *leaf;
int leafnum;
if( node->contents < 0 )
{
if( node->contents != CONTENTS_SOLID )
{
leaf = (mleaf_t *)node;
leafnum = (leaf - worldmodel->leafs - 1);
if( visbits[leafnum >> 3] & (1<<( leafnum & 7 )))
return true;
}
return false;
}
if( SV_HeadnodeVisible( node->children[0], visbits ))
return true;
return SV_HeadnodeVisible( node->children[1], visbits );
}
/*
===============
SV_LinkEdict
===============
*/
void SV_LinkEdict( edict_t *ent, bool touch_triggers )
{
areanode_t *node;
short leafs[MAX_TOTAL_ENT_LEAFS];
int i, j, num_leafs, topNode;
if( ent->area.prev ) SV_UnlinkEdict( ent ); // unlink from old position
if( ent == EDICT_NUM( 0 )) return; // don't add the world
if( ent->free ) return;
if( ent->area.prev ) SV_UnlinkEdict( ent ); // unlink from old position
if( ent == svgame.edicts ) return; // don't add the world
if( !SV_IsValidEdict( ent )) return; // never add freed ents
// set the abs box
svgame.dllFuncs.pfnSetAbsBox( ent );
@ -189,50 +420,23 @@ void SV_LinkEdict( edict_t *ent, bool touch_triggers )
// link to PVS leafs
ent->num_leafs = 0;
// get all leafs, including solids
num_leafs = CM_BoxLeafnums( ent->v.absmin, ent->v.absmax, leafs, MAX_TOTAL_ENT_LEAFS, &topNode );
// if none of the leafs were inside the map, the
// entity is outside the world and can be considered unlinked
if( !num_leafs )
if( ent->v.modelindex )
{
SV_CheckForOutside( ent );
return;
}
ent->headnode = -1;
SV_FindTouchedLeafs( ent, sv.worldmodel->nodes );
if( num_leafs >= MAX_ENT_LEAFS )
{
// assume we missed some leafs, and mark by headnode
ent->num_leafs = -1;
ent->headnode = topNode;
}
else
{
ent->num_leafs = 0;
for( i = 0; i < num_leafs; i++ )
// if none of the leafs were inside the map, the
// entity is outside the world and can be considered unlinked
if( !ent->num_leafs )
{
if( leafs[i] == -1 )
continue; // not a visible leaf
for( j = 0; j < i; j++ )
{
if( leafs[j] == leafs[i] )
break;
}
if( j == i )
{
if( ent->num_leafs == MAX_ENT_LEAFS )
{
// assume we missed some leafs, and mark by headNode
ent->num_leafs = -1;
ent->headnode = topNode;
break;
}
ent->leafnums[ent->num_leafs++] = leafs[i];
}
SV_CheckForOutside( ent );
ent->headnode = -1;
return;
}
if( ent->num_leafs == MAX_ENT_LEAFS )
ent->num_leafs = 0; // so we use headnode instead
else ent->headnode = -1; // use normal leafs check
}
// ignore not solid bodies
@ -263,79 +467,256 @@ void SV_LinkEdict( edict_t *ent, bool touch_triggers )
}
/*
============================================================================
===============================================================================
AREA QUERY
POINT TESTING IN HULLS
Fills in a list of all entities who's absmin / absmax intersects the given
bounds. This does NOT mean that they actually touch in the case of bmodels.
============================================================================
===============================================================================
*/
/*
====================
SV_AreaEdicts_r
====================
==================
SV_HullPointContents
==================
*/
void SV_AreaEdicts_r( areanode_t *node, area_t *ap )
int SV_HullPointContents( hull_t *hull, int num, const vec3_t p )
{
link_t *l, *next, *start;
edict_t *check;
int count = 0;
float d;
dclipnode_t *node;
mplane_t *plane;
// touch linked edicts
if( ap->type == AREA_SOLID )
start = &node->solid_edicts;
else if( ap->type == AREA_TRIGGERS )
start = &node->trigger_edicts;
else start = &node->water_edicts;
for( l = start->next; l != start; l = next )
while( num >= 0 )
{
next = l->next;
check = EDICT_FROM_AREA( l );
if( check->v.solid == SOLID_NOT && check->v.skin == CONTENTS_NONE )
continue; // deactivated
if( !BoundsIntersect( check->v.absmin, check->v.absmax, ap->mins, ap->maxs ))
continue; // not touching
if( ap->count == ap->maxcount )
{
MsgDev( D_WARN, "SV_AreaEdicts: maxcount hit\n" );
return;
}
ap->list[ap->count] = check;
ap->count++;
}
if( num < hull->firstclipnode || num > hull->lastclipnode )
Host_Error( "SV_HullPointContents: bad node number %i\n", num );
if( node->axis == -1 ) return; // terminal node
node = hull->clipnodes + num;
plane = hull->planes + node->planenum;
if( plane->type < 3 )
d = p[plane->type] - plane->dist;
else d = DotProduct( plane->normal, p ) - plane->dist;
// recurse down both sides
if( ap->maxs[node->axis] > node->dist ) SV_AreaEdicts_r( node->children[0], ap );
if( ap->mins[node->axis] < node->dist ) SV_AreaEdicts_r( node->children[1], ap );
if( d < 0 ) num = node->children[1];
else num = node->children[0];
}
return num;
}
/*
================
SV_AreaEdicts
================
====================
SV_WaterLinks
====================
*/
int SV_AreaEdicts( const vec3_t mins, const vec3_t maxs, edict_t **list, int maxcount, int areatype )
void SV_WaterLinks( const vec3_t mins, const vec3_t maxs, int *pCont, areanode_t *node )
{
area_t ap;
link_t *l, *next;
edict_t *touch;
ap.mins = mins;
ap.maxs = maxs;
ap.list = list;
ap.count = 0;
ap.maxcount = maxcount;
ap.type = areatype;
// get water edicts
for( l = node->water_edicts.next; l != &node->water_edicts; l = next )
{
next = l->next;
touch = EDICT_FROM_AREA( l );
SV_AreaEdicts_r( sv_areanodes, &ap );
if( touch->v.solid != SOLID_NOT ) // disabled ?
continue;
return ap.count;
// only brushes can have special contents
if( CM_GetModelType( touch->v.modelindex ) != mod_brush )
continue;
if( !BoundsIntersect( mins, maxs, touch->v.absmin, touch->v.absmax ))
continue;
// compare contents ranking
if( RankForContents( touch->v.skin ) > RankForContents( *pCont ))
*pCont = touch->v.skin; // new content has more priority
}
// recurse down both sides
if( node->axis == -1 ) return;
if( maxs[node->axis] > node->dist )
SV_WaterLinks( mins, maxs, pCont, node->children[0] );
if( mins[node->axis] < node->dist )
SV_WaterLinks( mins, maxs, pCont, node->children[1] );
}
/*
=============
SV_TruePointContents
=============
*/
int SV_TruePointContents( const vec3_t p )
{
int cont;
// sanity check
if( !p ) return CONTENTS_NONE;
// get base contents from world
cont = SV_HullPointContents( &sv.worldmodel->hulls[0], 0, p );
// check all water entities
SV_WaterLinks( p, p, &cont, sv_areanodes );
return cont;
}
/*
=============
SV_PointContents
=============
*/
int SV_PointContents( const vec3_t p )
{
int cont = SV_TruePointContents( p );
if( cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN )
cont = CONTENTS_WATER;
return cont;
}
//===========================================================================
/*
============
SV_TestEntityPosition
returns true if the entity is in solid currently
============
*/
bool SV_TestEntityPosition( edict_t *ent )
{
trace_t trace;
trace = SV_Move( ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NORMAL, ent );
return trace.fStartSolid;
}
/*
===============================================================================
LINE TESTING IN HULLS
===============================================================================
*/
/*
==================
SV_RecursiveHullCheck
==================
*/
bool SV_RecursiveHullCheck( hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace )
{
dclipnode_t *node;
mplane_t *plane;
float t1, t2;
float frac, midf;
int side;
vec3_t mid;
// check for empty
if( num < 0 )
{
if( num != CONTENTS_SOLID )
{
trace->fAllSolid = false;
if( num == CONTENTS_EMPTY )
trace->fInOpen = true;
else trace->fInWater = true;
}
else trace->fStartSolid = true;
return true; // empty
}
if( num < hull->firstclipnode || num > hull->lastclipnode )
Host_Error( "SV_RecursiveHullCheck: bad node number %i\n", num );
// find the point distances
node = hull->clipnodes + num;
plane = hull->planes + node->planenum;
if( plane->type < 3 )
{
t1 = p1[plane->type] - plane->dist;
t2 = p2[plane->type] - plane->dist;
}
else
{
t1 = DotProduct( plane->normal, p1 ) - plane->dist;
t2 = DotProduct( plane->normal, p2 ) - plane->dist;
}
if( t1 >= 0 && t2 >= 0 )
return SV_RecursiveHullCheck( hull, node->children[0], p1f, p2f, p1, p2, trace );
if( t1 < 0 && t2 < 0 )
return SV_RecursiveHullCheck( hull, node->children[1], p1f, p2f, p1, p2, trace );
// put the crosspoint DIST_EPSILON pixels on the near side
if( t1 < 0 ) frac = ( t1 + DIST_EPSILON ) / ( t1 - t2 );
else frac = ( t1 - DIST_EPSILON ) / ( t1 - t2 );
if( frac < 0.0f ) frac = 0.0f;
if( frac > 1.0f ) frac = 1.0f;
midf = p1f + ( p2f - p1f ) * frac;
VectorLerp( p1, frac, p2, mid );
side = (t1 < 0);
// move up to the node
if( !SV_RecursiveHullCheck( hull, node->children[side], p1f, midf, p1, mid, trace ))
return false;
if( SV_HullPointContents( hull, node->children[side^1], mid ) != CONTENTS_SOLID )
{
// go past the node
return SV_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace);
}
if( trace->fAllSolid )
return false; // never got out of the solid area
//==================
// the other side of the node is solid, this is the impact point
//==================
if( !side )
{
VectorCopy( plane->normal, trace->vecPlaneNormal );
trace->flPlaneDist = plane->dist;
}
else
{
VectorNegate( plane->normal, trace->vecPlaneNormal );
trace->flPlaneDist = -plane->dist;
}
while( SV_HullPointContents( hull, hull->firstclipnode, mid ) == CONTENTS_SOLID )
{
// shouldn't really happen, but does occasionally
frac -= 0.1f;
if( frac < 0.0f )
{
trace->flFraction = midf;
VectorCopy( mid, trace->vecEndPos );
MsgDev( D_WARN, "trace backed up 0.0\n" );
return false;
}
midf = p1f + (p2f - p1f) * frac;
VectorLerp( p1, frac, p2, mid );
}
trace->flFraction = midf;
VectorCopy( mid, trace->vecEndPos );
return false;
}
/*
@ -590,62 +971,79 @@ trace_t SV_MoveToss( edict_t *tossent, edict_t *ignore )
}
/*
=============
SV_PointContents
============================================================================
=============
AREA QUERY
Fills in a list of all entities who's absmin / absmax intersects the given
bounds. This does NOT mean that they actually touch in the case of bmodels.
============================================================================
*/
int SV_TruePointContents( const vec3_t p )
/*
====================
SV_AreaEdicts_r
====================
*/
void SV_AreaEdicts_r( areanode_t *node, area_t *ap )
{
int i, num, contents;
edict_t *hit, *touch[MAX_EDICTS];
link_t *l, *next, *start;
edict_t *check;
int count = 0;
// sanity check
if( !p ) return CONTENTS_NONE;
// touch linked edicts
if( ap->type == AREA_SOLID )
start = &node->solid_edicts;
else if( ap->type == AREA_TRIGGERS )
start = &node->trigger_edicts;
else start = &node->water_edicts;
// get base contents from world
contents = CM_PointContents( p );
if( contents != CONTENTS_EMPTY )
return contents; // have some world contents
// check contents from all the solid entities
num = SV_AreaEdicts( p, p, touch, MAX_EDICTS, AREA_SOLID );
for( i = 0; i < num; i++ )
for( l = start->next; l != start; l = next )
{
hit = touch[i];
next = l->next;
check = EDICT_FROM_AREA( l );
if( hit->v.solid != SOLID_BSP )
continue; // monsters, players
if( check->v.solid == SOLID_NOT && check->v.skin == CONTENTS_NONE )
continue; // deactivated
// solid entity found
return CONTENTS_SOLID;
if( !BoundsIntersect( check->v.absmin, check->v.absmax, ap->mins, ap->maxs ))
continue; // not touching
if( ap->count == ap->maxcount )
{
MsgDev( D_WARN, "SV_AreaEdicts: maxcount hit\n" );
return;
}
ap->list[ap->count] = check;
ap->count++;
}
if( node->axis == -1 ) return; // terminal node
// check contents from all the custom entities
num = SV_AreaEdicts( p, p, touch, MAX_EDICTS, AREA_CUSTOM );
for( i = 0; i < num; i++ )
{
hit = touch[i];
if( hit->v.solid != SOLID_NOT || hit->v.skin == CONTENTS_NONE )
continue; // invalid water ?
// custom contents found
return hit->v.skin;
}
return contents;
// recurse down both sides
if( ap->maxs[node->axis] > node->dist ) SV_AreaEdicts_r( node->children[0], ap );
if( ap->mins[node->axis] < node->dist ) SV_AreaEdicts_r( node->children[1], ap );
}
int SV_PointContents( const vec3_t p )
/*
================
SV_AreaEdicts
================
*/
int SV_AreaEdicts( const vec3_t mins, const vec3_t maxs, edict_t **list, int maxcount, int areatype )
{
int cont = SV_TruePointContents( p );
area_t ap;
if( cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN )
cont = CONTENTS_WATER;
return cont;
ap.mins = mins;
ap.maxs = maxs;
ap.list = list;
ap.count = 0;
ap.maxcount = maxcount;
ap.type = areatype;
SV_AreaEdicts_r( sv_areanodes, &ap );
return ap.count;
}
/*

View File

@ -151,7 +151,8 @@ bool R_VisCullBox( const vec3_t mins, const vec3_t maxs )
if( !node->plane )
return false;
s = BoxOnPlaneSide( extmins, extmaxs, node->plane ) - 1;
s = BOX_ON_PLANE_SIDE( extmins, extmaxs, node->plane ) - 1;
if( s < 2 )
{
node = node->children[s];

View File

@ -113,27 +113,64 @@ void CategorizePlane( mplane_t *plane )
}
/*
==============
BoxOnPlaneSide (engine fast version)
==================
BoxOnPlaneSide
Returns SIDE_FRONT, SIDE_BACK, or SIDE_ON
==============
Returns 1, 2, or 1 + 2
==================
*/
int BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const mplane_t *p )
{
if( p->type < 3 ) return ((emaxs[p->type] >= p->dist) | ((emins[p->type] < p->dist) << 1));
float dist1, dist2;
int sides = 0;
// general case
switch( p->signbits )
{
case 0:
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
break;
case 1:
dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
break;
case 2:
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
break;
case 3:
dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
break;
case 4:
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
break;
case 5:
dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
break;
case 6:
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
break;
case 7:
dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
break;
default:
case 0: return (((p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]) >= p->dist) | (((p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]) < p->dist) << 1));
case 1: return (((p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]) >= p->dist) | (((p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]) < p->dist) << 1));
case 2: return (((p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]) >= p->dist) | (((p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]) < p->dist) << 1));
case 3: return (((p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]) >= p->dist) | (((p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]) < p->dist) << 1));
case 4: return (((p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]) >= p->dist) | (((p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]) < p->dist) << 1));
case 5: return (((p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]) >= p->dist) | (((p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]) < p->dist) << 1));
case 6: return (((p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]) >= p->dist) | (((p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]) < p->dist) << 1));
case 7: return (((p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]) >= p->dist) | (((p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]) < p->dist) << 1));
// shut up compiler
dist1 = dist2 = 0;
break;
}
if( dist1 >= p->dist )
sides = 1;
if( dist2 < p->dist )
sides |= 2;
return sides;
}
/*

View File

@ -21,12 +21,29 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// r_math.h
float CalcFov( float fov_x, float width, float height );
void AdjustFov( float *fov_x, float *fov_y, float width, float height, bool lock_x );
int BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const mplane_t *p );
void AdjustFov( float *fov_x, float *fov_y, float width, float height, bool lock_x );
void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees );
void PlaneFromPoints( vec3_t verts[3], mplane_t *plane );
void CategorizePlane( mplane_t *plane );
#define BOX_ON_PLANE_SIDE( emins, emaxs, p ) \
((( p )->type < 3 ) ? \
( \
((p)->dist <= (emins)[(p)->type]) ? \
1 \
: \
( \
((p)->dist >= (emaxs)[(p)->type]) ? \
2 \
: \
3 \
) \
) \
: \
BoxOnPlaneSide(( emins ), ( emaxs ), ( p )))
/*
===============
ColorNormalize