From 14b954bb6346e5c32056527d873a2b3663d1ca33 Mon Sep 17 00:00:00 2001 From: g-cont Date: Tue, 7 Mar 2017 00:00:00 +0300 Subject: [PATCH] 07 Mar 2017 --- dlls/h_cycler.cpp | 2 +- engine/client/cl_frame.c | 7 + engine/client/cl_game.c | 11 +- engine/client/cl_gameui.c | 2 +- engine/client/cl_parse.c | 4 +- engine/client/cl_pmove.c | 75 +- engine/client/cl_scrn.c | 5 +- engine/client/cl_tent.c | 52 +- engine/client/client.h | 9 +- engine/client/gl_cull.c | 50 +- engine/client/gl_image.c | 84 +- engine/client/gl_local.h | 11 +- engine/client/gl_rlight.c | 177 ++-- engine/client/gl_rmain.c | 7 +- engine/client/gl_rsurf.c | 3 +- engine/client/gl_sprite.c | 57 +- engine/client/gl_studio.c | 1739 ++++++++++++++++++-------------- engine/client/gl_vidnt.c | 6 +- engine/client/vgui/vgui_draw.c | 2 +- engine/common/mathlib.c | 155 ++- engine/common/mathlib.h | 5 +- engine/common/mod_local.h | 3 + engine/common/mod_studio.c | 174 +--- engine/common/pm_debug.c | 10 - engine/common/pm_trace.c | 57 +- engine/server/sv_phys.c | 6 +- engine/server/sv_world.c | 46 + engine/studio.h | 4 +- 28 files changed, 1463 insertions(+), 1300 deletions(-) diff --git a/dlls/h_cycler.cpp b/dlls/h_cycler.cpp index a306359f..917aca90 100644 --- a/dlls/h_cycler.cpp +++ b/dlls/h_cycler.cpp @@ -221,7 +221,7 @@ public: void Spawn( void ); void Think( void ); void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() | FCAP_DONT_SAVE | FCAP_IMPULSE_USE); } + virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() | FCAP_IMPULSE_USE); } virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); void Animate( float frames ); diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index 1ed55707..1f57cdd2 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -1059,6 +1059,7 @@ void CL_LinkPacketEntities( frame_t *frame ) #ifdef STUDIO_INTERPOLATION_FIX if( ent->lastmove >= cl.time ) VectorCopy( ent->curstate.origin, ent->latched.prevorigin ); + ent->curstate.movetype = MOVETYPE_STEP; #else if( ent->lastmove >= cl.time ) { @@ -1108,6 +1109,12 @@ void CL_LinkPacketEntities( frame_t *frame ) VectorCopy( ent->curstate.origin, ent->origin ); VectorCopy( ent->curstate.angles, ent->angles ); } + + if( ent->model->type == mod_studio ) + { + if( ent->curstate.movetype == MOVETYPE_STEP && FBitSet( host.features, ENGINE_COMPUTE_STUDIO_LERP )) + R_StudioLerpStepMovement( ent, cl.time, ent->origin, ent->angles ); + } } if( !FBitSet( state->entityType, ENTITY_NORMAL )) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index b84594fd..68e7bbcd 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -633,7 +633,7 @@ void CL_DrawScreenFade( void ) if( sf->fadeFlags & FFADE_MODULATE ) GL_SetRenderMode( kRenderTransAdd ); else GL_SetRenderMode( kRenderTransTexture ); - R_DrawStretchPic( 0, 0, glState.width, glState.height, 0, 0, 1, 1, cls.fillImage ); + R_DrawStretchPic( 0, 0, glState.width, glState.height, 0, 0, 1, 1, tr.whiteTexture ); pglColor4ub( 255, 255, 255, 255 ); } @@ -1189,7 +1189,7 @@ static model_t *CL_LoadSpriteModel( const char *filename, uint type, uint texFla } // load new map sprite - if( CL_LoadHudSprite( name, &clgame.sprites[i], true, 0 )) + if( CL_LoadHudSprite( name, &clgame.sprites[i], type, 0 )) { if( i < ( MAX_IMAGES - 1 )) clgame.sprites[i].needload = clgame.load_sequence; @@ -1451,7 +1451,7 @@ void CL_FillRGBA( int x, int y, int width, int height, int r, int g, int b, int SPR_AdjustSize( (float *)&x, (float *)&y, (float *)&width, (float *)&height ); GL_SetRenderMode( kRenderTransAdd ); - R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, cls.fillImage ); + R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, tr.whiteTexture ); pglColor4ub( 255, 255, 255, 255 ); } @@ -2835,7 +2835,7 @@ void CL_FillRGBABlend( int x, int y, int width, int height, int r, int g, int b, pglBlendFunc( GL_ONE_MINUS_SRC_ALPHA, GL_ONE ); pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); - R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, cls.fillImage ); + R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, tr.whiteTexture ); pglColor4ub( 255, 255, 255, 255 ); } @@ -4048,5 +4048,8 @@ qboolean CL_LoadProgs( const char *name ) CL_InitStudioAPI( ); + // grab them from client.dll + cl_righthand = Cvar_Get( "cl_righthand", "0", FCVAR_ARCHIVE, "flip viewmodel (left to right)" ); + return true; } \ No newline at end of file diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index 393db283..e0570152 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -522,7 +522,7 @@ static void pfnFillRGBA( int x, int y, int width, int height, int r, int g, int a = bound( 0, a, 255 ); pglColor4ub( r, g, b, a ); GL_SetRenderMode( kRenderTransTexture ); - R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, cls.fillImage ); + R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, tr.whiteTexture ); pglColor4ub( 255, 255, 255, 255 ); } diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 4f269e42..3ceda7b3 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -600,12 +600,12 @@ void CL_WeaponAnim( int iAnim, int body ) view->curstate.rendermode = kRenderNormal; view->curstate.renderamt = 255; -#if 1 // g-cont. for GlowShell testing +#if 0 // g-cont. for GlowShell testing view->curstate.renderfx = kRenderFxGlowShell; view->curstate.rendercolor.r = 255; view->curstate.rendercolor.g = 128; view->curstate.rendercolor.b = 0; - view->curstate.renderamt = 100; + view->curstate.renderamt = 150; #endif } diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index 69038e64..80d50635 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -339,7 +339,7 @@ void CL_ClipPMoveToEntity( physent_t *pe, const vec3_t start, vec3_t mins, vec3_ } } -static void CL_CopyEntityToPhysEnt( physent_t *pe, entity_state_t *state ) +static void CL_CopyEntityToPhysEnt( physent_t *pe, entity_state_t *state, qboolean visent ) { model_t *mod = Mod_Handle( state->modelindex ); @@ -361,31 +361,26 @@ static void CL_CopyEntityToPhysEnt( physent_t *pe, entity_state_t *state ) pe->model = pe->studiomodel = NULL; - switch( state->solid ) + VectorCopy( state->mins, pe->mins ); + VectorCopy( state->maxs, pe->maxs ); + + if( state->solid == SOLID_BBOX ) { - case SOLID_NOT: - case SOLID_BSP: - pe->model = mod; - VectorClear( pe->mins ); - VectorClear( pe->maxs ); - break; - case SOLID_BBOX: - if( mod && mod->type == mod_studio && mod->flags & STUDIO_TRACE_HITBOX ) + if( FBitSet( mod->flags, STUDIO_TRACE_HITBOX )) pe->studiomodel = mod; - VectorCopy( state->mins, pe->mins ); - VectorCopy( state->maxs, pe->maxs ); - break; - case SOLID_CUSTOM: - pe->model = (mod->type == mod_brush) ? mod : NULL; - pe->studiomodel = (mod->type == mod_studio) ? mod : NULL; - VectorCopy( state->mins, pe->mins ); - VectorCopy( state->maxs, pe->maxs ); - break; - default: - pe->studiomodel = (mod->type == mod_studio) ? mod : NULL; - VectorCopy( state->mins, pe->mins ); - VectorCopy( state->maxs, pe->maxs ); - break; + } + else + { + if( pe->solid != SOLID_BSP && mod->type == mod_studio ) + pe->studiomodel = mod; + else pe->model = mod; + } + + // rare case: not solid entities in vistrace + if( visent && VectorIsNull( pe->mins )) + { + VectorCopy( mod->mins, pe->mins ); + VectorCopy( mod->maxs, pe->maxs ); } pe->info = state->number; @@ -458,7 +453,7 @@ void CL_AddLinksToPmove( frame_t *frame ) if(( model->hulls[1].firstclipnode || model->type == mod_studio ) && clgame.pmove->numvisent < MAX_PHYSENTS ) { pe = &clgame.pmove->visents[clgame.pmove->numvisent]; - CL_CopyEntityToPhysEnt( pe, state ); + CL_CopyEntityToPhysEnt( pe, state, true ); clgame.pmove->numvisent++; } @@ -482,7 +477,7 @@ void CL_AddLinksToPmove( frame_t *frame ) continue; pe = &clgame.pmove->moveents[clgame.pmove->nummoveent]; - CL_CopyEntityToPhysEnt( pe, state ); + CL_CopyEntityToPhysEnt( pe, state, false ); clgame.pmove->nummoveent++; } else @@ -492,7 +487,7 @@ void CL_AddLinksToPmove( frame_t *frame ) continue; pe = &clgame.pmove->physents[clgame.pmove->numphysent]; - CL_CopyEntityToPhysEnt( pe, state ); + CL_CopyEntityToPhysEnt( pe, state, false ); clgame.pmove->numphysent++; } } @@ -576,7 +571,7 @@ void CL_SetSolidPlayers( int playernum ) break; pe = &clgame.pmove->physents[clgame.pmove->numphysent]; - CL_CopyEntityToPhysEnt( pe, state ); + CL_CopyEntityToPhysEnt( pe, state, false ); clgame.pmove->numphysent++; // some fields needs to be override from cls.predicted_players @@ -715,15 +710,35 @@ pmtrace_t CL_TraceLine( vec3_t start, vec3_t end, int flags ) { int old_usehull; pmtrace_t tr; - + old_usehull = clgame.pmove->usehull; - clgame.pmove->usehull = 2; + clgame.pmove->usehull = 2; tr = PM_PlayerTraceExt( clgame.pmove, start, end, flags, clgame.pmove->numphysent, clgame.pmove->physents, -1, NULL ); clgame.pmove->usehull = old_usehull; return tr; } +/* +============= +CL_VisTraceLine + +trace by visible objects (thats can be non-solid) +============= +*/ +pmtrace_t CL_VisTraceLine( vec3_t start, vec3_t end, int flags ) +{ + int old_usehull; + pmtrace_t tr; + + old_usehull = clgame.pmove->usehull; + clgame.pmove->usehull = 2; + tr = PM_PlayerTraceExt( clgame.pmove, start, end, flags, clgame.pmove->numvisent, clgame.pmove->visents, -1, NULL ); + clgame.pmove->usehull = old_usehull; + + return tr; +} + /* ============= CL_GetWaterEntity diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index 95717720..c948e750 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -544,15 +544,12 @@ void SCR_InstallParticlePalette( void ) void SCR_RegisterTextures( void ) { - cls.fillImage = GL_LoadTexture( "*white", NULL, 0, TF_IMAGE, NULL ); // used for FillRGBA - cls.particleImage = GL_LoadTexture( "*particle", NULL, 0, TF_IMAGE, NULL ); - // register gfx.wad images cls.pauseIcon = GL_LoadTexture( "gfx.wad/paused.lmp", NULL, 0, TF_IMAGE, NULL ); if( cl_allow_levelshots->value ) cls.loadingBar = GL_LoadTexture( "gfx.wad/lambda.lmp", NULL, 0, TF_IMAGE|TF_LUMINANCE, NULL ); else cls.loadingBar = GL_LoadTexture( "gfx.wad/lambda.lmp", NULL, 0, TF_IMAGE, NULL ); - cls.tileImage = GL_LoadTexture( "gfx.wad/backtile.lmp", NULL, 0, TF_UNCOMPRESSED|TF_NOPICMIP|TF_NOMIPMAP, NULL ); + cls.tileImage = GL_LoadTexture( "gfx.wad/backtile.lmp", NULL, 0, TF_IMAGE, NULL ); } /* diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index db7650f3..f6841257 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -94,27 +94,39 @@ int CL_FxBlend( cl_entity_t *e ) break; // JAY: HACK for now -- not time based case kRenderFxFadeSlow: - if( e->curstate.renderamt > 0 ) - e->curstate.renderamt -= 1; - else e->curstate.renderamt = 0; + if( tr.frametime ) + { + if( e->curstate.renderamt > 0 ) + e->curstate.renderamt -= 1; + else e->curstate.renderamt = 0; + } blend = e->curstate.renderamt; break; case kRenderFxFadeFast: - if( e->curstate.renderamt > 3 ) - e->curstate.renderamt -= 4; - else e->curstate.renderamt = 0; + if( tr.frametime ) + { + if( e->curstate.renderamt > 3 ) + e->curstate.renderamt -= 4; + else e->curstate.renderamt = 0; + } blend = e->curstate.renderamt; break; case kRenderFxSolidSlow: - if( e->curstate.renderamt < 255 ) - e->curstate.renderamt += 1; - else e->curstate.renderamt = 255; + if( tr.frametime ) + { + if( e->curstate.renderamt < 255 ) + e->curstate.renderamt += 1; + else e->curstate.renderamt = 255; + } blend = e->curstate.renderamt; break; case kRenderFxSolidFast: - if( e->curstate.renderamt < 252 ) - e->curstate.renderamt += 4; - else e->curstate.renderamt = 255; + if( tr.frametime ) + { + if( e->curstate.renderamt < 252 ) + e->curstate.renderamt += 4; + else e->curstate.renderamt = 255; + } blend = e->curstate.renderamt; break; case kRenderFxStrobeSlow: @@ -184,6 +196,9 @@ void CL_InitTempEnts( void ) { cl_tempents = Mem_Alloc( cls.mempool, sizeof( TEMPENTITY ) * GI->max_tents ); CL_ClearTempEnts(); + + // load tempent sprites (glowshell, muzzleflashes etc) + CL_LoadClientSprites (); } /* @@ -2636,15 +2651,18 @@ void CL_UpdateFlashlight( cl_entity_t *ent ) VectorAdd( ent->origin, view_ofs, vecSrc ); VectorMA( vecSrc, FLASHLIGHT_DISTANCE, forward, vecEnd ); - trace = CL_TraceLine( vecSrc, vecEnd, PM_STUDIO_BOX ); + trace = CL_VisTraceLine( vecSrc, vecEnd, PM_STUDIO_BOX ); // update flashlight endpos dl = CL_AllocDlight( ent->index ); - - if( trace.ent > 0 && clgame.pmove->physents[trace.ent].studiomodel ) - VectorCopy( clgame.pmove->physents[trace.ent].origin, dl->origin ); +#if 0 + // g-cont. disabled until studio lighting will be finished + if( trace.ent > 0 && clgame.pmove->visents[trace.ent].studiomodel ) + VectorCopy( clgame.pmove->visents[trace.ent].origin, dl->origin ); else VectorCopy( trace.endpos, dl->origin ); - +#else + VectorCopy( trace.endpos, dl->origin ); +#endif // compute falloff falloff = trace.fraction * FLASHLIGHT_DISTANCE; if( falloff < 500.0f ) falloff = 1.0f; diff --git a/engine/client/client.h b/engine/client/client.h index a5b02f97..6672d94a 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -528,13 +528,10 @@ typedef struct int lastoutgoingcommand; // sequence number of last outgoing command int lastupdate_sequence; // prediction stuff - // internal images - int fillImage; // used for emulate FillRGBA to avoid wrong draw-sort - int particleImage; // built-in particle and sparks image + // game images int pauseIcon; // draw 'paused' when game in-pause - int loadingBar; // 'loading' progress bar - int glowShell; // for renderFxGlowShell int tileImage; // for draw any areas not covered by the refresh + int loadingBar; // 'loading' progress bar cl_font_t creditsFont; // shared creditsfont float latency; // rolling average of frame latencey (receivedtime - senttime) values. @@ -620,6 +617,7 @@ extern convar_t *cl_clockreset; extern convar_t *cl_fixtimerate; extern convar_t *gl_showtextures; extern convar_t *cl_bmodelinterp; +extern convar_t *cl_righthand; extern convar_t *cl_lw; // local weapons extern convar_t *cl_showevents; extern convar_t *scr_centertime; @@ -803,6 +801,7 @@ int CL_PointContents( const vec3_t p ); int CL_WaterEntity( const float *rgflPos ); cl_entity_t *CL_GetWaterEntity( const float *rgflPos ); void CL_SetupPMove( playermove_t *pmove, local_state_t *from, usercmd_t *ucmd, qboolean runfuncs, double time ); +pmtrace_t CL_VisTraceLine( vec3_t start, vec3_t end, int flags ); pmtrace_t CL_TraceLine( vec3_t start, vec3_t end, int flags ); void CL_MoveSpectatorCamera( void ); void CL_SetLastUpdate( void ); diff --git a/engine/client/gl_cull.c b/engine/client/gl_cull.c index c19bb9cf..575e92ec 100644 --- a/engine/client/gl_cull.c +++ b/engine/client/gl_cull.c @@ -41,43 +41,43 @@ qboolean R_CullBox( const vec3_t mins, const vec3_t maxs, uint clipflags ) if( r_nocull->value ) return false; - for( i = sizeof( RI.frustum ) / sizeof( RI.frustum[0] ), bit = 1, p = RI.frustum; i > 0; i--, bit<<=1, p++ ) + for( i = sizeof( RI.frustum ) / sizeof( RI.frustum[0] ), bit = 1, p = RI.frustum; i > 0; i--, bit++, p++ ) { - if( !( clipflags & bit )) + if( !FBitSet( clipflags, BIT( bit ))) continue; switch( p->signbits ) { case 0: - if( p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist ) + if( p->normal[0] * maxs[0] + p->normal[1] * maxs[1] + p->normal[2] * maxs[2] < p->dist ) return true; break; case 1: - if( p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist ) + if( p->normal[0] * mins[0] + p->normal[1] * maxs[1] + p->normal[2] * maxs[2] < p->dist ) return true; break; case 2: - if( p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist ) + if( p->normal[0] * maxs[0] + p->normal[1] * mins[1] + p->normal[2] * maxs[2] < p->dist ) return true; break; case 3: - if( p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist ) + if( p->normal[0] * mins[0] + p->normal[1] * mins[1] + p->normal[2] * maxs[2] < p->dist ) return true; break; case 4: - if( p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist ) + if( p->normal[0] * maxs[0] + p->normal[1] * maxs[1] + p->normal[2] * mins[2] < p->dist ) return true; break; case 5: - if( p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist ) + if( p->normal[0] * mins[0] + p->normal[1] * maxs[1] + p->normal[2] * mins[2] < p->dist ) return true; break; case 6: - if( p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist ) + if( p->normal[0] * maxs[0] + p->normal[1] * mins[1] + p->normal[2] * mins[2] < p->dist ) return true; break; case 7: - if( p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist ) + if( p->normal[0] * mins[0] + p->normal[1] * mins[1] + p->normal[2] * mins[2] < p->dist ) return true; break; default: @@ -96,19 +96,22 @@ Returns true if the sphere is completely outside the frustum */ qboolean R_CullSphere( const vec3_t centre, const float radius, const uint clipflags ) { - uint i, bit; - const mplane_t *p; + uint i, bit; + const mplane_t *p; // client.dll may use additional passes for render custom mirrors etc if( r_nocull->value ) return false; - for( i = sizeof( RI.frustum ) / sizeof( RI.frustum[0] ), bit = 1, p = RI.frustum; i > 0; i--, bit<<=1, p++ ) + for( i = sizeof( RI.frustum ) / sizeof( RI.frustum[0] ), bit = 1, p = RI.frustum; i > 0; i--, bit++, p++ ) { - if(!( clipflags & bit )) continue; + if( !FBitSet( clipflags, BIT( bit ))) + continue; + if( DotProduct( centre, p->normal ) - p->dist <= -radius ) return true; } + return false; } @@ -117,30 +120,35 @@ qboolean R_CullSphere( const vec3_t centre, const float radius, const uint clipf R_CullModel ============= */ -int R_CullModel( cl_entity_t *e, vec3_t origin, vec3_t mins, vec3_t maxs, float radius ) +int R_CullModel( cl_entity_t *e, const vec3_t absmin, const vec3_t absmax ) { if( e == &clgame.viewent ) { - if( RI.params & RP_NONVIEWERREF ) + if( CL_IsDevOverviewMode( )) return 1; - return 0; + + if( RP_NORMALPASS() && !RI.thirdPerson && cl.viewentity == ( cl.playernum + 1 )) + return 0; + + return 1; } // don't reflect this entity in mirrors - if( e->curstate.effects & EF_NOREFLECT && RI.params & RP_MIRRORVIEW ) + if( FBitSet( e->curstate.effects, EF_NOREFLECT ) && FBitSet( RI.params, RP_MIRRORVIEW )) return 1; // draw only in mirrors - if( e->curstate.effects & EF_REFLECTONLY && !( RI.params & RP_MIRRORVIEW )) + if( FBitSet( e->curstate.effects, EF_REFLECTONLY ) && !FBitSet( RI.params, RP_MIRRORVIEW )) return 1; + // local client can't view himself if camera or thirdperson is not active if( RP_LOCALCLIENT( e ) && !RI.thirdPerson && cl.viewentity == ( cl.playernum + 1 )) { - if(!( RI.params & RP_MIRRORVIEW )) + if( !FBitSet( RI.params, RP_MIRRORVIEW )) return 1; } - if( R_CullSphere( origin, radius, RI.clipFlags )) + if( R_CullBox( absmin, absmax, RI.clipFlags )) return 1; return 0; diff --git a/engine/client/gl_image.c b/engine/client/gl_image.c index 27a5b0f6..550bbd87 100644 --- a/engine/client/gl_image.c +++ b/engine/client/gl_image.c @@ -18,27 +18,16 @@ GNU General Public License for more details. #include "gl_local.h" #include "studio.h" -#define TEXTURES_HASH_SIZE (MAX_TEXTURES >> 2) +#define TEXTURES_HASH_SIZE (MAX_TEXTURES >> 2) static gltexture_t r_textures[MAX_TEXTURES]; static gltexture_t *r_texturesHashTable[TEXTURES_HASH_SIZE]; -static int r_numTextures; static byte data2D[BLOCK_SIZE_MAX*BLOCK_SIZE_MAX*4]; // intermediate texbuffer +static int r_numTextures; static rgbdata_t r_image; // generic pixelbuffer used for internal textures // internal tables -static vec3_t r_luminanceTable[256]; // RGB to luminance -static byte r_particleTexture[8][8] = -{ -{0,0,0,0,0,0,0,0}, -{0,0,0,1,1,0,0,0}, -{0,0,0,1,1,0,0,0}, -{0,1,1,1,1,1,1,0}, -{0,1,1,1,1,1,1,0}, -{0,0,0,1,1,0,0,0}, -{0,0,0,1,1,0,0,0}, -{0,0,0,0,0,0,0,0}, -}; +static vec3_t r_luminanceTable[256]; // RGB to luminance /* ================= @@ -1896,71 +1885,6 @@ static rgbdata_t *R_InitDefaultTexture( texFlags_t *flags ) return &r_image; } -/* -================== -R_InitParticleTexture -================== -*/ -static rgbdata_t *R_InitParticleTexture( texFlags_t *flags ) -{ - int x, y; - int dx2, dy, d; - - // particle texture - r_image.width = r_image.height = 16; - r_image.buffer = data2D; - r_image.flags = (IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA); - r_image.type = PF_RGBA_32; - r_image.size = r_image.width * r_image.height * 4; - - *flags = TF_NOPICMIP|TF_NOMIPMAP; - - for( x = 0; x < 16; x++ ) - { - dx2 = x - 8; - dx2 = dx2 * dx2; - - for( y = 0; y < 16; y++ ) - { - dy = y - 8; - d = 255 - 35 * sqrt( dx2 + dy * dy ); - data2D[( y*16 + x ) * 4 + 3] = bound( 0, d, 255 ); - } - } - return &r_image; -} - -/* -================== -R_InitParticleTexture2 -================== -*/ -static rgbdata_t *R_InitParticleTexture2( texFlags_t *flags ) -{ - int x, y; - - // particle texture - r_image.width = r_image.height = 8; - r_image.buffer = data2D; - r_image.flags = (IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA); - r_image.type = PF_RGBA_32; - r_image.size = r_image.width * r_image.height * 4; - - *flags = TF_NOPICMIP|TF_NOMIPMAP; - - for( x = 0; x < 8; x++ ) - { - for( y = 0; y < 8; y++ ) - { - data2D[(y * 8 + x) * 4 + 0] = 255; - data2D[(y * 8 + x) * 4 + 1] = 255; - data2D[(y * 8 + x) * 4 + 2] = 255; - data2D[(y * 8 + x) * 4 + 3] = r_particleTexture[x][y] * 255; - } - } - return &r_image; -} - /* ================== R_InitSkyTexture @@ -2477,8 +2401,6 @@ static void R_InitBuiltinTextures( void ) { "*white", &tr.whiteTexture, R_InitWhiteTexture }, { "*gray", &tr.grayTexture, R_InitGrayTexture }, { "*black", &tr.blackTexture, R_InitBlackTexture }, - { "*particle", &tr.particleTexture, R_InitParticleTexture }, - { "*particle2", &tr.particleTexture2, R_InitParticleTexture2 }, { "*cintexture", &tr.cinTexture, R_InitCinematicTexture }, // force linear filter { "*dlight", &tr.dlightTexture, R_InitDlightTexture }, { "*dlight2", &tr.dlightTexture2, R_InitDlightTexture2 }, diff --git a/engine/client/gl_local.h b/engine/client/gl_local.h index 438ce34f..1ef2b392 100644 --- a/engine/client/gl_local.h +++ b/engine/client/gl_local.h @@ -162,8 +162,6 @@ typedef struct int grayTexture; int blackTexture; int defaultTexture; // use for bad textures - int particleTexture; // particle texture - int particleTexture2; // unsmoothed particle texture int solidskyTexture; // quake1 solid-sky layer int alphaskyTexture; // quake1 alpha-sky layer int lightmapTextures[MAX_LIGHTMAPS]; @@ -213,6 +211,7 @@ typedef struct float lightcache[MAX_LIGHTSTYLES]; double frametime; // special frametime for multipass rendering (will set to 0 on a nextview) + float blend; // global blend value // cull info vec3_t modelorg; // relative to viewpoint @@ -279,7 +278,7 @@ void R_ShowTextures( void ); // // gl_cull.c // -int R_CullModel( cl_entity_t *e, vec3_t origin, vec3_t mins, vec3_t maxs, float radius ); +int R_CullModel( cl_entity_t *e, const vec3_t absmin, const vec3_t absmax ); qboolean R_CullBox( const vec3_t mins, const vec3_t maxs, uint clipflags ); qboolean R_CullSphere( const vec3_t centre, const float radius, const uint clipflags ); qboolean R_CullSurface( msurface_t *surf, uint clipflags ); @@ -342,8 +341,8 @@ void R_PushDlights( void ); void R_AnimateLight( void ); void R_GetLightSpot( vec3_t lightspot ); void R_MarkLights( dlight_t *light, int bit, mnode_t *node ); -void R_LightDir( const vec3_t origin, vec3_t lightDir, float radius ); void R_LightForPoint( const vec3_t point, color24 *ambientLight, qboolean invLight, qboolean useAmbient, float radius ); +colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lightspot ); int R_CountSurfaceDlights( msurface_t *surf ); int R_CountDlights( void ); @@ -417,6 +416,7 @@ void R_DrawSpriteModel( cl_entity_t *e ); // void R_StudioInit( void ); void Mod_LoadStudioModel( model_t *mod, const void *buffer, qboolean *loaded ); +void R_StudioLerpStepMovement( cl_entity_t *e, double time, vec3_t origin, vec3_t angles ); struct mstudiotex_s *R_StudioGetTexture( cl_entity_t *e ); float CL_GetStudioEstimatedFrame( cl_entity_t *ent ); void R_DrawStudioModel( cl_entity_t *e ); @@ -666,13 +666,12 @@ extern convar_t *r_detailtextures; extern convar_t *r_faceplanecull; extern convar_t *r_drawentities; extern convar_t *r_adjust_fov; -extern convar_t *r_lefthand; extern convar_t *r_flaresize; extern convar_t *r_decals; extern convar_t *r_novis; extern convar_t *r_nocull; extern convar_t *r_lockpvs; -extern convar_t *r_lockcull; +extern convar_t *r_lockfrustum; extern convar_t *r_dynamic; extern convar_t *r_lightmap; extern convar_t *r_fastsky; diff --git a/engine/client/gl_rlight.c b/engine/client/gl_rlight.c index 286a949e..f9418685 100644 --- a/engine/client/gl_rlight.c +++ b/engine/client/gl_rlight.c @@ -226,17 +226,17 @@ int R_CountSurfaceDlights( msurface_t *surf ) ======================================================================= */ -static uint r_pointColor[3]; -static vec3_t r_lightSpot; +static float g_trace_fraction; +static vec3_t g_trace_lightspot; /* ================= R_RecursiveLightPoint ================= */ -static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, const vec3_t start, const vec3_t end ) +static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f, float p2f, colorVec *cv, const vec3_t start, const vec3_t end ) { - float front, back, frac; + float front, back, frac, midf; int i, map, side, size, s, t; int sample_size; msurface_t *surf; @@ -246,7 +246,10 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, const vec3 // didn't hit anything if( !node || node->contents < 0 ) + { + cv->r = cv->g = cv->b = cv->a = 0; return false; + } // calculate mid point front = PlaneDiff( start, node->plane ); @@ -254,24 +257,27 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, const vec3 side = front < 0; if(( back < 0 ) == side ) - return R_RecursiveLightPoint( model, node->children[side], start, end ); + return R_RecursiveLightPoint( model, node->children[side], p1f, p2f, cv, start, end ); frac = front / ( front - back ); VectorLerp( start, frac, end, mid ); + midf = p1f + ( p2f - p1f ) * frac; // co down front side - if( R_RecursiveLightPoint( model, node->children[side], start, mid )) + if( R_RecursiveLightPoint( model, node->children[side], p1f, midf, cv, start, mid )) return true; // hit something if(( back < 0 ) == side ) - return false;// didn't hit anything - - VectorCopy( mid, r_lightSpot ); + { + cv->r = cv->g = cv->b = cv->a = 0; + return false; // didn't hit anything + } // check for impact on this node surf = model->surfaces + node->firstsurface; sample_size = Mod_SampleSizeForFace( surf ); + VectorCopy( mid, g_trace_lightspot ); for( i = 0; i < node->numsurfaces; i++, surf++ ) { @@ -289,29 +295,31 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, const vec3 s /= sample_size; t /= sample_size; + cv->r = cv->g = cv->b = cv->a = 0; + if( !surf->samples ) return true; - VectorClear( r_pointColor ); - lm = surf->samples + (t * ((surf->extents[0] / sample_size) + 1) + s); - size = ((surf->extents[0] / sample_size) + 1) * ((surf->extents[1] / sample_size) + 1); + size = ((surf->extents[0] / sample_size) + 1) * ((surf->extents[1] / sample_size) + 1); + g_trace_fraction = midf; for( map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++ ) { uint scale = tr.lightstylevalue[surf->styles[map]]; - r_pointColor[0] += TextureToTexGamma( lm->r ) * scale; - r_pointColor[1] += TextureToTexGamma( lm->g ) * scale; - r_pointColor[2] += TextureToTexGamma( lm->b ) * scale; + cv->r += TextureToTexGamma( lm->r ) * scale; + cv->g += TextureToTexGamma( lm->g ) * scale; + cv->b += TextureToTexGamma( lm->b ) * scale; lm += size; // skip to next lightmap } + return true; } // go down back side - return R_RecursiveLightPoint( model, node->children[!side], mid, end ); + return R_RecursiveLightPoint( model, node->children[!side], midf, p2f, cv, mid, end ); } int R_LightTraceFilter( physent_t *pe ) @@ -331,6 +339,7 @@ void R_LightForPoint( const vec3_t point, color24 *ambientLight, qboolean invLig { dlight_t *dl; pmtrace_t trace; + colorVec light; cl_entity_t *m_pGround; vec3_t start, end, dir; qboolean secondpass = false; @@ -405,13 +414,11 @@ get_light: VectorCopy( end_l, end ); } - VectorClear( r_pointColor ); - - if( R_RecursiveLightPoint( pmodel, pnodes, start, end )) + if( R_RecursiveLightPoint( pmodel, pnodes, 0.0f, 1.0f, &light, start, end )) { - ambientLight->r = min((r_pointColor[0] >> 7), 255 ); - ambientLight->g = min((r_pointColor[1] >> 7), 255 ); - ambientLight->b = min((r_pointColor[2] >> 7), 255 ); + ambientLight->r = Q_min(( light.r >> 7 ), 255 ); + ambientLight->g = Q_min(( light.g >> 7 ), 255 ); + ambientLight->b = Q_min(( light.b >> 7 ), 255 ); } else { @@ -439,7 +446,7 @@ get_light: int lnum, total; float f; - VectorClear( r_pointColor ); + light.r = light.g = light.b = light.a = 0; for( total = lnum = 0, dl = cl_dlights; lnum < MAX_DLIGHTS; lnum++, dl++ ) { @@ -453,87 +460,101 @@ get_light: continue; add = 1.0f - (dist / ( dl->radius + radius )); - r_pointColor[0] += TextureToTexGamma( dl->color.r ) * add; - r_pointColor[1] += TextureToTexGamma( dl->color.g ) * add; - r_pointColor[2] += TextureToTexGamma( dl->color.b ) * add; + light.r += TextureToTexGamma( dl->color.r ) * add; + light.g += TextureToTexGamma( dl->color.g ) * add; + light.b += TextureToTexGamma( dl->color.b ) * add; total++; } if( total != 0 ) { - r_pointColor[0] += ambientLight->r; - r_pointColor[1] += ambientLight->g; - r_pointColor[2] += ambientLight->b; + light.r += ambientLight->r; + light.g += ambientLight->g; + light.b += ambientLight->b; - f = max( max( r_pointColor[0], r_pointColor[1] ), r_pointColor[2] ); - if( f > 1.0f ) VectorScale( r_pointColor, ( 255.0f / f ), r_pointColor ); + f = max( max( light.r, light.g ), light.b ); + if( f > 1.0f ) + { + light.r *= (255.0f / f); + light.r *= (255.0f / f); + light.r *= ( 255.0f / f); + } - ambientLight->r = r_pointColor[0]; - ambientLight->g = r_pointColor[1]; - ambientLight->b = r_pointColor[2]; + ambientLight->r = light.r; + ambientLight->g = light.g; + ambientLight->b = light.b; } } } /* ================= -R_GetLightSpot +R_LightVec -NOTE: must call R_LightForPoint first +check bspmodels to get light from ================= */ -void R_GetLightSpot( vec3_t lightspot ) +colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lspot ) { - if( lightspot ) VectorCopy( r_lightSpot, lightspot ); -} + float last_fraction; + int i, maxEnts = 1; + colorVec light, cv; -/* -================= -R_LightDir -================= -*/ -void R_LightDir( const vec3_t origin, vec3_t lightDir, float radius ) -{ - dlight_t *dl; - vec3_t dir, local; - float dist; - int lnum; - - VectorClear( local ); - - // add dynamic lights - if( radius > 0.0f && r_dynamic->value ) + if( cl.worldmodel->lightdata ) { - for( lnum = 0, dl = cl_dlights; lnum < MAX_DLIGHTS; lnum++, dl++ ) + light.r = light.b = light.b = light.a = 0; + last_fraction = 1.0f; + + // get light from bmodels too + if( r_lighting_extended->value ) + maxEnts = clgame.pmove->numphysent; + + // check al the bsp-models + for( i = 0; i < maxEnts; i++ ) { - if( dl->die < cl.time || !dl->radius ) - continue; + physent_t *pe = &clgame.pmove->physents[i]; + vec3_t offset, start_l, end_l; + mnode_t *pnodes; + matrix4x4 matrix; - VectorSubtract( dl->origin, origin, dir ); - dist = VectorLength( dir ); + if( !pe->model || pe->model->type != mod_brush ) + continue; // skip non-bsp models - if( !dist || dist > dl->radius + radius ) - continue; - VectorAdd( local, dir, local ); - } + pnodes = &pe->model->nodes[pe->model->hulls[0].firstclipnode]; + VectorSubtract( pe->model->hulls[0].clip_mins, vec3_origin, offset ); + VectorAdd( offset, pe->origin, offset ); + VectorSubtract( start, offset, start_l ); + VectorSubtract( end, offset, end_l ); - for( lnum = 0, dl = cl_elights; lnum < MAX_ELIGHTS; lnum++, dl++ ) - { - if( dl->die < cl.time || !dl->radius ) - continue; + // rotate start and end into the models frame of reference + if( !VectorIsNull( pe->angles )) + { + Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); + Matrix4x4_VectorITransform( matrix, start, start_l ); + Matrix4x4_VectorITransform( matrix, end, end_l ); + } - VectorSubtract( dl->origin, origin, dir ); - dist = VectorLength( dir ); + VectorClear( g_trace_lightspot ); + g_trace_fraction = 1.0f; - if( !dist || dist > dl->radius + radius ) - continue; - VectorAdd( local, dir, local ); - } + if( !R_RecursiveLightPoint( pe->model, pnodes, 0.0f, 1.0f, &cv, start_l, end_l )) + continue; // didn't hit anything - if( !VectorIsNull( local )) - { - VectorNormalize( local ); - VectorCopy( local, lightDir ); + if( g_trace_fraction < last_fraction ) + { + if( lspot ) VectorCopy( g_trace_lightspot, lspot ); + light.r = Q_min(( cv.r >> 7 ), 255 ); + light.g = Q_min(( cv.g >> 7 ), 255 ); + light.b = Q_min(( cv.b >> 7 ), 255 ); + last_fraction = g_trace_fraction; + } } } + else + { + light.r = light.g = light.b = 255; + light.a = 0; + } + + return light; } \ No newline at end of file diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index 614aebe8..34f4200c 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -283,8 +283,6 @@ qboolean R_AddEntity( struct cl_entity_s *clent, int type ) if( clent->curstate.effects & EF_NODRAW ) return false; // done - clent->curstate.renderamt = CL_FxBlend( clent ); - if( clent->curstate.rendermode != kRenderNormal && clent->curstate.renderamt <= 0.0f ) return true; // invisible @@ -451,7 +449,7 @@ void R_SetupFrustum( void ) // build the transformation matrix for the given view angles AngleVectors( RI.viewangles, RI.vforward, RI.vright, RI.vup ); - if( !r_lockcull->value ) + if( !r_lockfrustum->value ) { VectorCopy( RI.vieworg, RI.cullorigin ); VectorCopy( RI.vforward, RI.cull_vforward ); @@ -914,6 +912,7 @@ void R_DrawEntitiesOnList( void ) int i; glState.drawTrans = false; + tr.blend = 1.0f; // draw the solid submodels fog R_DrawFog (); @@ -966,6 +965,8 @@ void R_DrawEntitiesOnList( void ) { RI.currententity = tr.trans_entities[i]; RI.currentmodel = RI.currententity->model; + tr.blend = CL_FxBlend( RI.currententity ) / 255.0f; + if( tr.blend <= 0.0f ) continue; ASSERT( RI.currententity != NULL ); ASSERT( RI.currententity->model != NULL ); diff --git a/engine/client/gl_rsurf.c b/engine/client/gl_rsurf.c index 5372885a..3462e0dd 100644 --- a/engine/client/gl_rsurf.c +++ b/engine/client/gl_rsurf.c @@ -1485,7 +1485,7 @@ void R_DrawBrushModel( cl_entity_t *e ) case kRenderTransTexture: need_sort = true; case kRenderGlow: - pglColor4ub( 255, 255, 255, e->curstate.renderamt ); + pglColor4f( 1.0f, 1.0f, 1.0f, tr.blend ); break; case kRenderTransColor: pglDisable( GL_TEXTURE_2D ); @@ -1929,6 +1929,7 @@ void R_DrawWorld( void ) GL_SetRenderMode( kRenderNormal ); gl_lms.dynamic_surfaces = NULL; + tr.blend = 1.0f; R_ClearSkyBox (); diff --git a/engine/client/gl_sprite.c b/engine/client/gl_sprite.c index b64256a5..03d86189 100644 --- a/engine/client/gl_sprite.c +++ b/engine/client/gl_sprite.c @@ -29,9 +29,8 @@ GNU General Public License for more details. convar_t *r_sprite_lerping; convar_t *r_sprite_lighting; char group_suffix[8]; -static vec3_t sprite_mins, sprite_maxs; -static float sprite_radius; static uint r_texFlags = 0; +float sprite_radius; /* ==================== @@ -710,41 +709,6 @@ float R_GetSpriteFrameInterpolant( cl_entity_t *ent, mspriteframe_t **oldframe, return lerpFrac; } -/* -================ -R_StudioComputeBBox - -Compute a full bounding box for current sequence -================ -*/ -qboolean R_SpriteComputeBBox( cl_entity_t *e, vec3_t bbox[8] ) -{ - float scale = 1.0f; - vec3_t p1; - int i; - - // copy original bbox (no rotation for sprites) - VectorCopy( e->model->mins, sprite_mins ); - VectorCopy( e->model->maxs, sprite_maxs ); - - // compute a full bounding box - for( i = 0; bbox && i < 8; i++ ) - { - p1[0] = ( i & 1 ) ? sprite_mins[0] : sprite_maxs[0]; - p1[1] = ( i & 2 ) ? sprite_mins[1] : sprite_maxs[1]; - p1[2] = ( i & 4 ) ? sprite_mins[2] : sprite_maxs[2]; - - VectorCopy( p1, bbox[i] ); - } - - if( e->curstate.scale > 0.0f ) - scale = e->curstate.scale; - - sprite_radius = RadiusFromBounds( sprite_mins, sprite_maxs ) * scale; - - return true; -} - /* ================ R_CullSpriteModel @@ -754,16 +718,25 @@ Cull sprite model by bbox */ qboolean R_CullSpriteModel( cl_entity_t *e, vec3_t origin ) { + vec3_t sprite_mins, sprite_maxs; + float scale = 1.0f; + if( !e->model->cache.data ) return true; - if( e == &clgame.viewent && r_lefthand->value >= 2 ) - return true; + if( e->curstate.scale > 0.0f ) + scale = e->curstate.scale; - if( !R_SpriteComputeBBox( e, NULL )) - return true; // invalid frame + // scale original bbox (no rotation for sprites) + VectorScale( e->model->mins, scale, sprite_mins ); + VectorScale( e->model->maxs, scale, sprite_maxs ); - return R_CullModel( e, origin, sprite_mins, sprite_maxs, sprite_radius ); + sprite_radius = RadiusFromBounds( sprite_mins, sprite_maxs ); + + VectorAdd( sprite_mins, origin, sprite_mins ); + VectorAdd( sprite_maxs, origin, sprite_maxs ); + + return R_CullModel( e, sprite_mins, sprite_maxs ); } /* diff --git a/engine/client/gl_studio.c b/engine/client/gl_studio.c index 2b4f5e06..dba9a9e7 100644 --- a/engine/client/gl_studio.c +++ b/engine/client/gl_studio.c @@ -23,13 +23,8 @@ GNU General Public License for more details. #include "gl_local.h" #include "cl_tent.h" -// NOTE: enable this if you want merge both 'model' and 'modelT' files into one model slot. -// otherwise it's uses two slots in models[] array for models with external textures -#define STUDIO_MERGE_TEXTURES - #define EVENT_CLIENT 5000 // less than this value it's a server-side studio events -#define MAXARRAYVERTS 65536 // used for draw shadows -#define LEGS_BONES_COUNT 8 +#define MAX_LOCALLIGHTS 4 static vec3_t hullcolor[8] = { @@ -43,34 +38,6 @@ static vec3_t hullcolor[8] = { 1.0f, 1.0f, 1.0f }, }; -// enumerate all the bones that used for gait animation -const char *legs_bones[] = -{ -{ "Bip01" }, -{ "Bip01 Pelvis" }, -{ "Bip01 L Leg" }, -{ "Bip01 L Leg1" }, -{ "Bip01 L Foot" }, -{ "Bip01 R Leg" }, -{ "Bip01 R Leg1" }, -{ "Bip01 R Foot" }, -}; - -typedef struct studiolight_s -{ - vec3_t lightvec; // light vector - vec3_t lightcolor; // ambient light color - vec3_t lightspot; // potential coords where placed lightsource - - vec3_t blightvec[MAXSTUDIOBONES]; // ambient lightvectors per bone - vec3_t dlightvec[MAX_DLIGHTS][MAXSTUDIOBONES]; - vec3_t dlightcolor[MAX_DLIGHTS]; // ambient dynamic light colors - vec3_t elightvec[MAX_ELIGHTS][MAXSTUDIOBONES]; - vec3_t elightcolor[MAX_ELIGHTS]; // ambient entity light colors - int numdlights; - int numelights; -} studiolight_t; - typedef struct sortedmesh_s { mstudiomesh_t *mesh; @@ -81,22 +48,45 @@ typedef struct { double time; double frametime; + int framecount; // studio framecount + qboolean interpolate; + int rendermode; + float blend; // blend value + + sortedmesh_t meshes[MAXSTUDIOMESHES]; // sorted meshes + vec3_t verts[MAXSTUDIOVERTS]; + vec3_t norms[MAXSTUDIOVERTS]; + + // lighting state + float ambientlight; + float shadelight; + vec3_t lightvec; // averaging light direction + vec3_t lightspot; // shadow spot + vec3_t lightcolor; // averaging lightcolor + vec3_t blightvec[MAXSTUDIOBONES]; // bone light vecs + + // elights cache + int numlocallights; + int lightage[MAXSTUDIOBONES]; + dlight_t *locallight[MAX_LOCALLIGHTS]; + int locallinearlight[MAX_LOCALLIGHTS][3]; + vec4_t lightpos[MAXSTUDIOVERTS][MAX_LOCALLIGHTS]; + vec3_t lightbonepos[MAXSTUDIOBONES][MAX_LOCALLIGHTS]; + float locallightR2[MAX_LOCALLIGHTS]; } studio_draw_state_t; + +CVAR_DEFINE_AUTO( r_shadows, "0", 0, "enable shadows from studiomodels" ); -convar_t *r_studio_lerping; convar_t *r_studio_lambert; convar_t *r_studio_lighting; convar_t *r_studio_sort_textures; convar_t *r_drawviewmodel; -convar_t *r_customdraw_playermodel; +convar_t *cl_righthand = NULL; convar_t *cl_himodels; -cvar_t r_shadows = { "r_shadows", "0", 0, 0 }; // dead cvar. especially disabled -cvar_t r_shadowalpha = { "r_shadowalpha", "0.5", 0, 0.8f }; static r_studio_interface_t *pStudioDraw; static matrix3x4 g_rotationmatrix; static vec3_t g_chrome_origin; static vec2_t g_chrome[MAXSTUDIOVERTS]; // texture coords for surface normals -static sortedmesh_t g_sortedMeshes[MAXSTUDIOMESHES]; static matrix3x4 g_bonestransform[MAXSTUDIOBONES]; static matrix3x4 g_lighttransform[MAXSTUDIOBONES]; static matrix3x4 g_rgCachedBonesTransform[MAXSTUDIOBONES]; @@ -104,34 +94,21 @@ static matrix3x4 g_rgCachedLightTransform[MAXSTUDIOBONES]; static vec3_t g_chromeright[MAXSTUDIOBONES];// chrome vector "right" in bone reference frames static vec3_t g_chromeup[MAXSTUDIOBONES]; // chrome vector "up" in bone reference frames static int g_chromeage[MAXSTUDIOBONES]; // last time chrome vectors were updated -static vec3_t g_xformverts[MAXSTUDIOVERTS]; -static vec3_t g_xformnorms[MAXSTUDIOVERTS]; -static vec3_t g_xarrayverts[MAXARRAYVERTS]; -static uint g_xarrayelems[MAXARRAYVERTS*6]; -static uint g_nNumArrayVerts; -static uint g_nNumArrayElems; +static int g_normaltable[MAXSTUDIOVERTS];// glowshell uses this static vec3_t g_lightvalues[MAXSTUDIOVERTS]; -static studiolight_t g_studiolight; static studio_draw_state_t g_studio; // used for draw studiomodels char g_nCachedBoneNames[MAXSTUDIOBONES][32]; int g_nCachedBones; // number of bones in cache -int g_nStudioCount; // for chrome update -int g_iRenderMode; // currentmodel rendermode int g_iBackFaceCull; -vec3_t studio_mins, studio_maxs; float studio_radius; // global variables -qboolean m_fDoInterp; qboolean m_fDoRemap; mstudiomodel_t *m_pSubModel; mstudiobodyparts_t *m_pBodyPart; player_info_t *m_pPlayerInfo; studiohdr_t *m_pStudioHeader; -studiohdr_t *m_pTextureHeader; float m_flGaitMovement; -pmtrace_t g_shadowTrace; -vec3_t g_mvShadowVec; int g_nTopColor, g_nBottomColor; // remap colors int g_nFaceFlags, g_nForceFaceFlags; @@ -144,18 +121,15 @@ R_StudioInit void R_StudioInit( void ) { r_studio_lambert = Cvar_Get( "r_studio_lambert", "2", FCVAR_ARCHIVE, "bonelighting lambert value" ); - r_studio_lerping = Cvar_Get( "r_studio_lerping", "1", FCVAR_ARCHIVE, "enables studio animation lerping" ); - r_drawviewmodel = Cvar_Get( "r_drawviewmodel", "1", 0, "draw firstperson weapon model" ); cl_himodels = Cvar_Get( "cl_himodels", "1", FCVAR_ARCHIVE, "draw high-resolution player models in multiplayer" ); r_studio_lighting = Cvar_Get( "r_studio_lighting", "1", FCVAR_ARCHIVE, "studio lighting models ( 0 - normal, 1 - extended, 2 - experimental )" ); r_studio_sort_textures = Cvar_Get( "r_studio_sort_textures", "0", FCVAR_ARCHIVE, "sort additive and normal textures for right drawing" ); - - // NOTE: some mods with custom studiomodel renderer may cause error when menu trying draw player model out of the loaded game - r_customdraw_playermodel = Cvar_Get( "r_customdraw_playermodel", "0", FCVAR_ARCHIVE, "allow to drawing playermodel in menu with client renderer" ); + r_drawviewmodel = Cvar_Get( "r_drawviewmodel", "1", 0, "draw firstperson weapon model" ); Matrix3x4_LoadIdentity( g_rotationmatrix ); - g_nStudioCount = 0; + g_studio.interpolate = true; + g_studio.framecount = 0; m_fDoRemap = false; } @@ -229,26 +203,20 @@ static void R_StudioSetupTimings( void ) /* ================ -R_StudioExtractBbox +R_StudioFlipViewModel -Extract bbox from current sequence +should a flip the viewmodel if cl_righthand is set to 1 ================ */ -qboolean R_StudioExtractBbox( studiohdr_t *phdr, int sequence, float *mins, float *maxs ) +static qboolean R_StudioFlipViewModel( cl_entity_t *e ) { - mstudioseqdesc_t *pseqdesc; + if( cl_righthand && cl_righthand->value > 0 ) + { + if( e == &clgame.viewent ) + return true; + } - if( !phdr ) return false; - - // check sequence range - if( sequence < 0 || sequence >= phdr->numseq ) - sequence = 0; - - pseqdesc = (mstudioseqdesc_t *)((byte *)phdr + phdr->seqindex); - VectorCopy( pseqdesc[sequence].bbmin, mins ); - VectorCopy( pseqdesc[sequence].bbmax, maxs ); - - return true; + return false; } /* @@ -260,60 +228,58 @@ Compute a full bounding box for current sequence */ static qboolean R_StudioComputeBBox( cl_entity_t *e, vec3_t bbox[8] ) { - vec3_t tmp_mins, tmp_maxs; - vec3_t vectors[3], angles, p1, p2; - int i, seq = e->curstate.sequence; + studiohdr_t *pstudiohdr = (studiohdr_t *)Mod_Extradata( e->model ); + vec3_t studio_mins, studio_maxs; + vec3_t mins, maxs, size, p1, p2; + mstudioseqdesc_t *pseqdesc; + int i; - if( !R_StudioExtractBbox( m_pStudioHeader, seq, tmp_mins, tmp_maxs )) + if( !pstudiohdr ) return false; - // copy original bbox - VectorCopy( m_pStudioHeader->bbmin, studio_mins ); - VectorCopy( m_pStudioHeader->bbmax, studio_maxs ); + // check if we have valid mins\maxs + if( !VectorCompare( vec3_origin, e->model->mins )) + { + // clipping bounding box + VectorCopy( e->model->mins, mins ); + VectorCopy( e->model->maxs, maxs ); + } + else + { + ClearBounds( mins, maxs ); + } - // rotate the bounding box - VectorCopy( e->angles, angles ); -#if 0 - if( e->player ) angles[PITCH] = 0.0f; // don't rotate player model, only aim - AngleVectors( angles, vectors[0], vectors[1], vectors[2] ); -#else - vectors[0][0] = g_rotationmatrix[0][0]; - vectors[0][1] = g_rotationmatrix[1][0]; - vectors[0][2] = g_rotationmatrix[2][0]; + // check sequence range + if( e->curstate.sequence < 0 || e->curstate.sequence >= pstudiohdr->numseq ) + e->curstate.sequence = 0; - vectors[1][0] = g_rotationmatrix[0][1]; - vectors[1][1] = g_rotationmatrix[1][1]; - vectors[1][2] = g_rotationmatrix[2][1]; + pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + e->curstate.sequence; + + // add sequence box to the model box + AddPointToBounds( pseqdesc->bbmin, mins, maxs ); + AddPointToBounds( pseqdesc->bbmax, mins, maxs ); + ClearBounds( studio_mins, studio_maxs ); - vectors[2][0] = g_rotationmatrix[0][2]; - vectors[2][1] = g_rotationmatrix[1][2]; - vectors[2][2] = g_rotationmatrix[2][2]; -#endif // compute a full bounding box for( i = 0; i < 8; i++ ) { - p1[0] = ( i & 1 ) ? tmp_mins[0] : tmp_maxs[0]; - p1[1] = ( i & 2 ) ? tmp_mins[1] : tmp_maxs[1]; - p1[2] = ( i & 4 ) ? tmp_mins[2] : tmp_maxs[2]; + p1[0] = ( i & 1 ) ? mins[0] : maxs[0]; + p1[1] = ( i & 2 ) ? mins[1] : maxs[1]; + p1[2] = ( i & 4 ) ? mins[2] : maxs[2]; - // rotate by YAW - p2[0] = DotProduct( p1, vectors[0] ); - p2[1] = -DotProduct( p1, vectors[1] ); - p2[2] = DotProduct( p1, vectors[2] ); - - if( bbox ) VectorAdd( p2, e->origin, bbox[i] ); - - if( p2[0] < studio_mins[0] ) studio_mins[0] = p2[0]; - if( p2[0] > studio_maxs[0] ) studio_maxs[0] = p2[0]; - if( p2[1] < studio_mins[1] ) studio_mins[1] = p2[1]; - if( p2[1] > studio_maxs[1] ) studio_maxs[1] = p2[1]; - if( p2[2] < studio_mins[2] ) studio_mins[2] = p2[2]; - if( p2[2] > studio_maxs[2] ) studio_maxs[2] = p2[2]; + Matrix3x4_VectorTransform( g_rotationmatrix, p1, p2 ); + AddPointToBounds( p2, studio_mins, studio_maxs ); + if( bbox ) VectorCopy( p2, bbox[i] ); } - studio_radius = RadiusFromBounds( studio_mins, studio_maxs ); + // calc radius from local bbox + VectorSubtract( studio_maxs, studio_mins, size ); + VectorScale( size, 0.5f, size ); + studio_radius = RadiusFromBounds( size, size ); - return true; + if( !bbox && R_CullModel( e, studio_mins, studio_maxs )) + return false; // model culled + return true; // visible } /* @@ -335,7 +301,7 @@ pfnPlayerInfo */ static player_info_t *pfnPlayerInfo( int index ) { - if( cls.key_dest == key_menu && !index ) + if( !RI.drawWorld ) return &gameui.playerinfo; if( index < 0 || index > cl.maxclients ) @@ -351,6 +317,9 @@ pfnGetPlayerState */ entity_state_t *R_StudioGetPlayerState( int index ) { + if( !RI.drawWorld ) + return &RI.currententity->curstate; + if( index < 0 || index >= cl.maxclients ) return NULL; @@ -414,7 +383,7 @@ pfnGetModelCounters */ static void pfnGetModelCounters( int **s, int **a ) { - *s = &g_nStudioCount; + *s = &g_studio.framecount; *a = &r_stats.c_studio_models_drawn; } @@ -474,31 +443,6 @@ static float ***pfnStudioGetRotationMatrix( void ) return (float ***)g_rotationmatrix; } -/* -==================== -CullStudioModel - -==================== -*/ -qboolean R_CullStudioModel( cl_entity_t *e ) -{ - vec3_t origin; - - if( !e || !e->model || !e->model->cache.data ) - return true; - - if( e == &clgame.viewent && ( r_lefthand->value >= 2 || CL_IsDevOverviewMode( ))) - return true; // hidden - - if( !R_StudioComputeBBox( e, NULL )) - return true; // invalid sequence - - // NOTE: extract real drawing origin from rotation matrix - Matrix3x4_OriginFromMatrix( g_rotationmatrix, origin ); - - return R_CullModel( e, origin, studio_mins, studio_maxs, studio_radius ); -} - /* ==================== StudioPlayerBlend @@ -509,7 +453,7 @@ void R_StudioPlayerBlend( mstudioseqdesc_t *pseqdesc, int *pBlend, float *pPitch { // calc up/down pointing if( RI.params & RP_MIRRORVIEW ) - *pBlend = (*pPitch * -6.0f); + *pBlend = (*pPitch * -3.0f); else *pBlend = (*pPitch * 3.0f); if( *pBlend < pseqdesc->blendstart[0] ) @@ -531,6 +475,37 @@ void R_StudioPlayerBlend( mstudioseqdesc_t *pseqdesc, int *pBlend, float *pPitch } } +/* +==================== +R_StudioLerpStepMovement + +==================== +*/ +void R_StudioLerpStepMovement( cl_entity_t *e, double time, vec3_t origin, vec3_t angles ) +{ + float f = 1.0f; + + // don't do it if the goalstarttime hasn't updated in a while. + // NOTE: Because we need to interpolate multiplayer characters, the interpolation time limit + // was increased to 1.0 s., which is 2x the max lag we are accounting for. + if( g_studio.interpolate && ( time < e->curstate.animtime + 1.0f ) && ( e->curstate.animtime != e->latched.prevanimtime )) + f = ( time - e->curstate.animtime ) / ( e->curstate.animtime - e->latched.prevanimtime ); + + // Msg( "%4.2f %.2f %.2f\n", f, e->curstate.animtime, g_studio.time ); + VectorLerp( e->latched.prevorigin, f, e->curstate.origin, origin ); + + if( !VectorCompare( e->curstate.angles, e->latched.prevangles )) + { + vec4_t q, q1, q2; + + AngleQuaternion( e->curstate.angles, q1, false ); + AngleQuaternion( e->latched.prevangles, q2, false ); + QuaternionSlerp( q2, q1, f, q ); + QuaternionAngle( q, angles ); + } + else VectorCopy( e->curstate.angles, angles ); +} + /* ==================== StudioSetUpTransform @@ -545,48 +520,12 @@ void R_StudioSetUpTransform( cl_entity_t *e ) VectorCopy( e->angles, angles ); // interpolate monsters position (moved into UpdateEntityFields by user request) - if( e->curstate.movetype == MOVETYPE_STEP && !( host.features & ENGINE_COMPUTE_STUDIO_LERP )) + if( e->curstate.movetype == MOVETYPE_STEP && !FBitSet( host.features, ENGINE_COMPUTE_STUDIO_LERP )) { - float d, f = 0.0f; - int i; - - // don't do it if the goalstarttime hasn't updated in a while. - // NOTE: Because we need to interpolate multiplayer characters, the interpolation time limit - // was increased to 1.0 s., which is 2x the max lag we are accounting for. - if( m_fDoInterp && ( g_studio.time < e->curstate.animtime + 1.0f ) && ( e->curstate.animtime != e->latched.prevanimtime )) - { - f = ( g_studio.time - e->curstate.animtime ) / ( e->curstate.animtime - e->latched.prevanimtime ); - // Msg( "%4.2f %.2f %.2f\n", f, e->curstate.animtime, g_studio.time ); - } - - if( m_fDoInterp ) - { - // ugly hack to interpolate angle, position. - // current is reached 0.1 seconds after being set - f = f - 1.0f; - } - - origin[0] += ( e->origin[0] - e->latched.prevorigin[0] ) * f; - origin[1] += ( e->origin[1] - e->latched.prevorigin[1] ) * f; - origin[2] += ( e->origin[2] - e->latched.prevorigin[2] ) * f; - - for( i = 0; i < 3; i++ ) - { - float ang1, ang2; - - ang1 = e->angles[i]; - ang2 = e->latched.prevangles[i]; - - d = ang1 - ang2; - - if( d > 180.0f ) d -= 360.0f; - else if( d < -180.0f ) d += 360.0f; - - angles[i] += d * f; - } + R_StudioLerpStepMovement( e, g_studio.time, origin, angles ); } - if( !( host.features & ENGINE_COMPENSATE_QUAKE_BUG )) + if( !FBitSet( host.features, ENGINE_COMPENSATE_QUAKE_BUG )) angles[PITCH] = -angles[PITCH]; // stupid quake bug // don't rotate clients, only aim @@ -594,9 +533,8 @@ void R_StudioSetUpTransform( cl_entity_t *e ) Matrix3x4_CreateFromEntity( g_rotationmatrix, angles, origin, 1.0f ); - if( e == &clgame.viewent && r_lefthand->value == 1 ) + if( R_StudioFlipViewModel( e )) { - // inverse the right vector (should work in Opposing Force) g_rotationmatrix[0][1] = -g_rotationmatrix[0][1]; g_rotationmatrix[1][1] = -g_rotationmatrix[1][1]; g_rotationmatrix[2][1] = -g_rotationmatrix[2][1]; @@ -613,7 +551,7 @@ float R_StudioEstimateFrame( cl_entity_t *e, mstudioseqdesc_t *pseqdesc ) { double dfdt, f; - if( m_fDoInterp ) + if( g_studio.interpolate ) { if( g_studio.time < e->curstate.animtime ) dfdt = 0.0; else dfdt = (g_studio.time - e->curstate.animtime) * e->curstate.framerate * pseqdesc->fps; @@ -650,11 +588,12 @@ float R_StudioEstimateInterpolant( cl_entity_t *e ) { float dadt = 1.0f; - if( m_fDoInterp && ( e->curstate.animtime >= e->latched.prevanimtime + 0.01f )) + if( g_studio.interpolate && ( e->curstate.animtime >= e->latched.prevanimtime + 0.01f )) { dadt = ( g_studio.time - e->curstate.animtime ) / 0.1f; if( dadt > 2.0f ) dadt = 2.0f; } + return dadt; } @@ -795,7 +734,7 @@ void R_StudioCalcBoneAdj( float dadt, float *adj, const byte *pcontroller1, cons pbonecontroller = (mstudiobonecontroller_t *)((byte *)m_pStudioHeader + m_pStudioHeader->bonecontrollerindex); - for (j = 0; j < m_pStudioHeader->numbonecontrollers; j++) + for( j = 0; j < m_pStudioHeader->numbonecontrollers; j++ ) { i = pbonecontroller[j].index; @@ -803,21 +742,19 @@ void R_StudioCalcBoneAdj( float dadt, float *adj, const byte *pcontroller1, cons { // mouth hardcoded at controller 4 value = (float)mouthopen / 64.0f; - if( value > 1.0f ) value = 1.0f; + value = bound( 0.0f, value, 1.0f ); value = (1.0f - value) * pbonecontroller[j].start + value * pbonecontroller[j].end; } - else if( i <= MAXSTUDIOCONTROLLERS ) + else if( i < 4 ) { // check for 360% wrapping - if( pbonecontroller[j].type & STUDIO_RLOOP ) + if( FBitSet( pbonecontroller[j].type, STUDIO_RLOOP )) { if( abs( pcontroller1[i] - pcontroller2[i] ) > 128 ) { - float a, b; - - a = fmod(( pcontroller1[j] + 128.0f ), 256.0f ); - b = fmod(( pcontroller2[j] + 128.0f ), 256.0f ); - value = ((a * dadt) + (b * (1 - dadt)) - 128.0f) * (360.0f / 256.0f) + pbonecontroller[j].start; + int a = (pcontroller1[j] + 128) % 256; + int b = (pcontroller2[j] + 128) % 256; + value = (( a * dadt ) + ( b * ( 1.0f - dadt )) - 128) * (360.0f / 256.0f) + pbonecontroller[j].start; } else { @@ -837,7 +774,7 @@ void R_StudioCalcBoneAdj( float dadt, float *adj, const byte *pcontroller1, cons case STUDIO_XR: case STUDIO_YR: case STUDIO_ZR: - adj[j] = value * (M_PI / 180.0f); + adj[j] = DEG2RAD( value ); break; case STUDIO_X: case STUDIO_Y: @@ -850,32 +787,33 @@ void R_StudioCalcBoneAdj( float dadt, float *adj, const byte *pcontroller1, cons /* ==================== -StudioCalcBoneQuaterion +StudioCalcBoneQuaternion ==================== */ -void R_StudioCalcBoneQuaterion( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, vec4_t q ) +void R_StudioCalcBoneQuaternion( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, vec4_t q ) { - int j, k; - vec4_t q1, q2; - vec3_t angle1, angle2; - mstudioanimvalue_t *panimvalue; + vec3_t angles1; + vec3_t angles2; + int j, k; for( j = 0; j < 3; j++ ) { - if( panim->offset[j+3] == 0 ) + if( !panim || panim->offset[j+3] == 0 ) { - angle2[j] = angle1[j] = pbone->value[j+3]; // default; + angles2[j] = angles1[j] = pbone->value[j+3]; // default; } else { - panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j+3]); + mstudioanimvalue_t *panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j+3]); + 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; @@ -889,53 +827,49 @@ void R_StudioCalcBoneQuaterion( int frame, float s, mstudiobone_t *pbone, mstudi // bah, missing blend! if( panimvalue->num.valid > k ) { - angle1[j] = panimvalue[k+1].value; + angles1[j] = panimvalue[k+1].value; if( panimvalue->num.valid > k + 1 ) { - angle2[j] = panimvalue[k+2].value; + angles2[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; + angles2[j] = angles1[j]; + else angles2[j] = panimvalue[panimvalue->num.valid+2].value; } } else { - angle1[j] = panimvalue[panimvalue->num.valid].value; - + angles1[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; - } + angles2[j] = angles1[j]; + else angles2[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]; + angles1[j] = pbone->value[j+3] + angles1[j] * pbone->scale[j+3]; + angles2[j] = pbone->value[j+3] + angles2[j] * pbone->scale[j+3]; } - if( pbone->bonecontroller[j+3] != -1 ) + if( pbone->bonecontroller[j+3] != -1 && adj != NULL ) { - angle1[j] += adj[pbone->bonecontroller[j+3]]; - angle2[j] += adj[pbone->bonecontroller[j+3]]; + angles1[j] += adj[pbone->bonecontroller[j+3]]; + angles2[j] += adj[pbone->bonecontroller[j+3]]; } } - if( !VectorCompare( angle1, angle2 )) + if( !VectorCompare( angles1, angles2 )) { - AngleQuaternion( angle1, q1, true ); - AngleQuaternion( angle2, q2, true ); + vec4_t q1, q2; + + AngleQuaternion( angles1, q1, true ); + AngleQuaternion( angles2, q2, true ); QuaternionSlerp( q1, q2, s, q ); } else { - AngleQuaternion( angle1, q, true ); + AngleQuaternion( angles1, q, true ); } } @@ -947,16 +881,20 @@ StudioCalcBonePosition */ void R_StudioCalcBonePosition( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, vec3_t adj, vec3_t pos ) { - mstudioanimvalue_t *panimvalue; - int j, k; + vec3_t origin1; + vec3_t origin2; + int j, k; for( j = 0; j < 3; j++ ) { - pos[j] = pbone->value[j]; // default; - - if( panim->offset[j] != 0 ) + if( !panim || panim->offset[j] == 0 ) { - panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j]); + origin2[j] = origin1[j] = pbone->value[j]; // default; + } + else + { + mstudioanimvalue_t *panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j]); + k = frame; // debug @@ -974,25 +912,48 @@ void R_StudioCalcBonePosition( int frame, float s, mstudiobone_t *pbone, mstudio k = 0; } - // if we're inside the span + // bah, missing blend! if( panimvalue->num.valid > k ) { - // and there's more data in the span + origin1[j] = panimvalue[k+1].value; + 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]; + { + origin2[j] = panimvalue[k+2].value; + } + else + { + if( panimvalue->num.total > k + 1 ) + origin2[j] = origin1[j]; + else origin2[j] = panimvalue[panimvalue->num.valid+2].value; + } } 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]; + origin1[j] = panimvalue[panimvalue->num.valid].value; + if( panimvalue->num.total > k + 1 ) + origin2[j] = origin1[j]; + else origin2[j] = panimvalue[panimvalue->num.valid+2].value; } + + origin1[j] = pbone->value[j] + origin1[j] * pbone->scale[j]; + origin2[j] = pbone->value[j] + origin2[j] * pbone->scale[j]; } - if( pbone->bonecontroller[j] != -1 && adj ) - pos[j] += adj[pbone->bonecontroller[j]]; + if( pbone->bonecontroller[j] != -1 && adj != NULL ) + { + origin1[j] += adj[pbone->bonecontroller[j]]; + origin2[j] += adj[pbone->bonecontroller[j]]; + } + } + + if( !VectorCompare( origin1, origin2 )) + { + VectorLerp( origin1, s, origin2, pos ); + } + else + { + VectorCopy( origin1, pos ); } } @@ -1005,22 +966,13 @@ StudioSlerpBones void R_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; // backlerp for( i = 0; i < m_pStudioHeader->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; + QuaternionSlerp( q1[i], q2[i], s, q1[i] ); + VectorLerp( pos1[i], s, pos2[i], pos1[i] ); } } @@ -1033,19 +985,20 @@ StudioCalcRotations void R_StudioCalcRotations( cl_entity_t *e, 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; + mstudiobone_t *pbone; + // bah, fix this bug with changing sequences too fast if( f > pseqdesc->numframes - 1 ) { - f = 0.0f; // bah, fix this bug with changing sequences too fast + f = 0.0f; } else if( f < -0.01f ) { - // this could cause a crash if the frame # is negative, so we'll go ahead + // BUG ( somewhere else ) but this code should validate this data. + // This could cause a crash if the frame # is negative, so we'll go ahead // and clamp it here - MsgDev( D_ERROR, "StudioCalcRotations: f = %g\n", f ); f = -0.01f; } @@ -1061,20 +1014,13 @@ void R_StudioCalcRotations( cl_entity_t *e, float pos[][3], vec4_t *q, mstudiose for( i = 0; i < m_pStudioHeader->numbones; i++, pbone++, panim++ ) { - R_StudioCalcBoneQuaterion( frame, s, pbone, panim, adj, q[i] ); + R_StudioCalcBoneQuaternion( frame, s, pbone, panim, adj, q[i] ); R_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; -#if 0 - s = 1.0f * ((1.0f - (f - (int)(f))) / (pseqdesc->numframes)) * e->curstate.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]; -#endif } /* @@ -1158,7 +1104,7 @@ void R_StudioSetupBones( cl_entity_t *e ) static vec4_t q3[MAXSTUDIOBONES]; static vec3_t pos4[MAXSTUDIOBONES]; static vec4_t q4[MAXSTUDIOBONES]; - int i, j; + int i; if( e->curstate.sequence >= m_pStudioHeader->numseq ) e->curstate.sequence = 0; @@ -1199,7 +1145,7 @@ void R_StudioSetupBones( cl_entity_t *e ) } } - if( m_fDoInterp && e->latched.sequencetime && ( e->latched.sequencetime + 0.2f > g_studio.time ) && ( e->latched.prevsequence < m_pStudioHeader->numseq )) + if( g_studio.interpolate && e->latched.sequencetime && ( e->latched.sequencetime + 0.2f > g_studio.time ) && ( e->latched.prevsequence < m_pStudioHeader->numseq )) { // blend from last sequence static vec3_t pos1b[MAXSTUDIOBONES]; @@ -1250,6 +1196,8 @@ void R_StudioSetupBones( cl_entity_t *e ) // calc gait animation if( m_pPlayerInfo && m_pPlayerInfo->gaitsequence != 0 ) { + qboolean copy_bones = true; + if( m_pPlayerInfo->gaitsequence >= m_pStudioHeader->numseq ) m_pPlayerInfo->gaitsequence = 0; @@ -1260,14 +1208,12 @@ void R_StudioSetupBones( cl_entity_t *e ) for( i = 0; i < m_pStudioHeader->numbones; i++ ) { - for( j = 0; j < LEGS_BONES_COUNT; j++ ) - { - if( !Q_strcmp( pbones[i].name, legs_bones[j] )) - break; - } + if( !Q_strcmp( pbones[i].name, "Bip01 Spine" )) + copy_bones = false; + else if( !Q_strcmp( pbones[pbones[i].parent].name, "Bip01 Pelvis" )) + copy_bones = true; - if( j == LEGS_BONES_COUNT ) - continue; // not used for legs + if( !copy_bones ) continue; VectorCopy( pos2[i], pos[i] ); Vector4Copy( q2[i], q[i] ); @@ -1317,6 +1263,144 @@ static void R_StudioSaveBones( void ) } } +/* +==================== +StudioBuildNormalTable + +NOTE: m_pSubModel must be set +==================== +*/ +void R_StudioBuildNormalTable( void ) +{ + mstudiomesh_t *pmesh; + int i, j; + + ASSERT( m_pSubModel ); + + // reset chrome cache + for( i = 0; i < m_pStudioHeader->numbones; i++ ) + g_chromeage[i] = 0; + + for( i = 0; i < m_pSubModel->numverts; i++ ) + g_normaltable[i] = -1; + + for( j = 0; j < m_pSubModel->nummesh; j++ ) + { + short *ptricmds; + + pmesh = (mstudiomesh_t *)((byte *)m_pStudioHeader + m_pSubModel->meshindex) + j; + ptricmds = (short *)((byte *)m_pStudioHeader + pmesh->triindex); + + while( i = *( ptricmds++ )) + { + if( i < 0 ) i = -i; + + for( ; i > 0; i--, ptricmds += 4 ) + { + if( g_normaltable[ptricmds[0]] < 0 ) + g_normaltable[ptricmds[0]] = ptricmds[1]; + } + } + } +} + +/* +==================== +StudioGenerateNormals + +NOTE: m_pSubModel must be set +g_studio.verts must be computed +==================== +*/ +void R_StudioGenerateNormals( void ) +{ + int v0, v1, v2; + vec3_t e0, e1, norm; + mstudiomesh_t *pmesh; + int i, j; + + ASSERT( m_pSubModel ); + + for( i = 0; i < m_pSubModel->numverts; i++ ) + VectorClear( g_studio.norms[i] ); + + for( j = 0; j < m_pSubModel->nummesh; j++ ) + { + short *ptricmds; + + pmesh = (mstudiomesh_t *)((byte *)m_pStudioHeader + m_pSubModel->meshindex) + j; + ptricmds = (short *)((byte *)m_pStudioHeader + pmesh->triindex); + + while( i = *( ptricmds++ )) + { + if( i < 0 ) + { + i = -i; + + if( i > 2 ) + { + v0 = ptricmds[0]; ptricmds += 4; + v1 = ptricmds[0]; ptricmds += 4; + + for( i -= 2; i > 0; i--, ptricmds += 4 ) + { + v2 = ptricmds[0]; + + VectorSubtract( g_studio.verts[v1], g_studio.verts[v0], e0 ); + VectorSubtract( g_studio.verts[v2], g_studio.verts[v0], e1 ); + CrossProduct( e1, e0, norm ); + + VectorAdd( g_studio.norms[v0], norm, g_studio.norms[v0] ); + VectorAdd( g_studio.norms[v1], norm, g_studio.norms[v1] ); + VectorAdd( g_studio.norms[v2], norm, g_studio.norms[v2] ); + + v1 = v2; + } + } + else + { + ptricmds += i; + } + } + else + { + if( i > 2 ) + { + qboolean odd = false; + + v0 = ptricmds[0]; ptricmds += 4; + v1 = ptricmds[0]; ptricmds += 4; + + for( i -= 2; i > 0; i--, ptricmds += 4 ) + { + v2 = ptricmds[0]; + + VectorSubtract( g_studio.verts[v1], g_studio.verts[v0], e0 ); + VectorSubtract( g_studio.verts[v2], g_studio.verts[v0], e1 ); + CrossProduct( e1, e0, norm ); + + VectorAdd( g_studio.norms[v0], norm, g_studio.norms[v0] ); + VectorAdd( g_studio.norms[v1], norm, g_studio.norms[v1] ); + VectorAdd( g_studio.norms[v2], norm, g_studio.norms[v2] ); + + if( odd ) v1 = v2; + else v0 = v2; + + odd = !odd; + } + } + else + { + ptricmds += i; + } + } + } + } + + for( i = 0; i < m_pSubModel->numverts; i++ ) + VectorNormalize( g_studio.norms[i] ); +} + /* ==================== StudioSetupChrome @@ -1327,47 +1411,41 @@ void R_StudioSetupChrome( float *pchrome, int bone, vec3_t normal ) { float n; - if( g_chromeage[bone] != g_nStudioCount ) + if( g_chromeage[bone] != g_studio.framecount ) { // calculate vectors from the viewer to the bone. This roughly adjusts for position vec3_t chromeupvec; // g_chrome t vector in world reference frame vec3_t chromerightvec; // g_chrome s vector in world reference frame - vec3_t tmp, v_left; // vector pointing at bone in world reference frame + vec3_t tmp; // vector pointing at bone in world reference frame - VectorCopy( g_chrome_origin, tmp ); + VectorNegate( g_chrome_origin, tmp ); tmp[0] += g_bonestransform[bone][0][3]; tmp[1] += g_bonestransform[bone][1][3]; tmp[2] += g_bonestransform[bone][2][3]; VectorNormalize( tmp ); - VectorNegate( RI.vright, v_left ); if( g_nForceFaceFlags & STUDIO_NF_CHROME ) { - float angle, sr, cr; - int i; + float sr, cr; - angle = anglemod( g_studio.time * 40 ) * (M_PI2 / 360.0f); - SinCos( angle, &sr, &cr ); - - for( i = 0; i < 3; i++ ) - { - chromerightvec[i] = (v_left[i] * cr + RI.vup[i] * sr); - chromeupvec[i] = v_left[i] * -sr + RI.vup[i] * cr; - } + // g-cont. rotate texture for glowshell effect + SinCos( DEG2RAD( g_studio.time * 40 ), &sr, &cr ); + VectorMAMAM( -cr, RI.vright, sr, RI.vup, 0.0f, vec3_origin, chromerightvec ); + VectorMAMAM( sr, RI.vright, cr, RI.vup, 0.0f, vec3_origin, chromeupvec ); } else { - CrossProduct( tmp, v_left, chromeupvec ); + CrossProduct( tmp, RI.vright, chromeupvec ); VectorNormalize( chromeupvec ); CrossProduct( tmp, chromeupvec, chromerightvec ); VectorNormalize( chromerightvec ); - VectorNegate( chromeupvec, chromeupvec ); } Matrix3x4_VectorIRotate( g_bonestransform[bone], chromeupvec, g_chromeup[bone] ); Matrix3x4_VectorIRotate( g_bonestransform[bone], chromerightvec, g_chromeright[bone] ); - g_chromeage[bone] = g_nStudioCount; + + g_chromeage[bone] = g_studio.framecount; } // calc s coord @@ -1451,36 +1529,12 @@ R_StudioCheckBBox */ static int R_StudioCheckBBox( void ) { - if( R_CullStudioModel( RI.currententity )) + cl_entity_t *e = RI.currententity; + + if( !e || !e->model || !e->model->cache.data ) return false; - return true; -} -/* -=============== -R_StudioGetShadowImpactAndDir -=============== -*/ -void R_StudioGetShadowImpactAndDir( void ) -{ - float angle; - vec3_t skyAngles, origin, end; - - // convert skyvec into angles then back into vector to avoid 0 0 0 direction - VectorAngles( (float *)&clgame.movevars.skyvec_x, skyAngles ); - angle = skyAngles[YAW] / 180 * M_PI; - - Matrix3x4_OriginFromMatrix( g_bonestransform[0], origin ); - SinCos( angle, &g_mvShadowVec[1], &g_mvShadowVec[0] ); - - R_LightDir( origin, g_mvShadowVec, 256.0f ); - - VectorSet( g_mvShadowVec, -g_mvShadowVec[0], -g_mvShadowVec[1], -1.0f ); - VectorNormalize( g_mvShadowVec ); - - VectorMA( origin, 256.0f, g_mvShadowVec, end ); - - g_shadowTrace = CL_TraceLine( origin, end, PM_WORLD_ONLY ); + return R_StudioComputeBBox( e, NULL ); } /* @@ -1489,109 +1543,185 @@ R_StudioDynamicLight =============== */ -void R_StudioDynamicLight( cl_entity_t *ent, alight_t *lightinfo ) +void R_StudioDynamicLight( cl_entity_t *ent, alight_t *plight ) { - uint lnum, i; - studiolight_t *plight; - qboolean invLight; - color24 ambient; - float dist, radius2; - vec3_t direction, origin; + movevars_t *mv = &clgame.movevars; + vec3_t lightDir, vecSrc, vecEnd; + vec3_t origin, dist, finalLight; + float add, radius, total; + colorVec light; + uint lnum; dlight_t *dl; - if( !lightinfo ) return; + if( !plight || !ent ) + return; - plight = &g_studiolight; - plight->numdlights = 0; // clear previous dlights + if( !RI.drawWorld || r_fullbright->value || FBitSet( ent->curstate.effects, EF_FULLBRIGHT )) + { + plight->shadelight = 0; + plight->ambientlight = 192; - if( r_studio_lighting->value == 2 ) + VectorSet( plight->plightvec, 0.0f, 0.0f, -1.0f ); + VectorSet( plight->color, 1.0f, 1.0f, 1.0f ); + return; + } + + // determine plane to get lightvalues from: ceil or floor + if( FBitSet( ent->curstate.effects, EF_INVLIGHT )) + VectorSet( lightDir, 0.0f, 0.0f, 1.0f ); + else VectorSet( lightDir, 0.0f, 0.0f, -1.0f ); + + if( ent == RI.currententity ) { Matrix3x4_OriginFromMatrix( g_lighttransform[0], origin ); - // NOTE: in some cases bone origin may be stuck in the geometry + // NOTE: in some cases rootbone origin may be stuck in the geometry // and produced completely black model. Run additional check for this case if( CL_PointContents( origin ) == CONTENTS_SOLID ) Matrix3x4_OriginFromMatrix( g_rotationmatrix, origin ); } - else Matrix3x4_OriginFromMatrix( g_rotationmatrix, origin ); + else VectorCopy( ent->origin, origin ); - // setup light dir - plight->lightvec[0] = clgame.movevars.skyvec_x; - plight->lightvec[1] = clgame.movevars.skyvec_y; - plight->lightvec[2] = clgame.movevars.skyvec_z; + VectorSet( vecSrc, origin[0], origin[1], origin[2] - lightDir[2] * 8.0f ); + light.r = light.g = light.b = light.a = 0; - if( VectorIsNull( plight->lightvec )) - VectorSet( plight->lightvec, 0.0f, 0.0f, -1.0f ); + if(( mv->skycolor_r + mv->skycolor_g + mv->skycolor_b ) != 0 ) + { + msurface_t *psurf; - VectorCopy( plight->lightvec, lightinfo->plightvec ); + if( FBitSet( host.features, ENGINE_WRITE_LARGE_COORD )) + { + vecEnd[0] = origin[0] - mv->skyvec_x * 65536.0f; + vecEnd[1] = origin[1] - mv->skyvec_y * 65536.0f; + vecEnd[2] = origin[2] - mv->skyvec_z * 65536.0f; + } + else + { + vecEnd[0] = origin[0] - mv->skyvec_x * 8192.0f; + vecEnd[1] = origin[1] - mv->skyvec_y * 8192.0f; + vecEnd[2] = origin[2] - mv->skyvec_z * 8192.0f; + } - // setup ambient lighting - invLight = (ent->curstate.effects & EF_INVLIGHT) ? true : false; - R_LightForPoint( origin, &ambient, invLight, true, 0.0f ); // ignore dlights + psurf = PM_TraceSurface( clgame.pmove->physents, vecSrc, vecEnd ); - R_GetLightSpot( plight->lightspot ); // shadow stuff + if( FBitSet( ent->model->flags, STUDIO_FORCE_SKYLIGHT ) || ( psurf && FBitSet( psurf->flags, SURF_DRAWSKY ))) + { + VectorSet( lightDir, mv->skyvec_x, mv->skyvec_y, mv->skyvec_z ); - plight->lightcolor[0] = lightinfo->color[0] = ambient.r * (1.0f / 255.0f); - plight->lightcolor[1] = lightinfo->color[1] = ambient.g * (1.0f / 255.0f); - plight->lightcolor[2] = lightinfo->color[2] = ambient.b * (1.0f / 255.0f); + light.r = mv->skycolor_r; + light.g = mv->skycolor_g; + light.b = mv->skycolor_b; + } + } - VectorCopy( plight->lightcolor, lightinfo->color ); - lightinfo->shadelight = (ambient.r + ambient.g + ambient.b) / 3; - lightinfo->ambientlight = lightinfo->shadelight; + if(( light.r + light.g + light.b ) == 0 ) + { + colorVec gcolor; + float grad[4]; - if( !ent || !ent->model || !r_dynamic->value ) - return; + VectorScale( lightDir, 2048.0f, vecEnd ); + VectorAdd( vecEnd, vecSrc, vecEnd ); + + light = R_LightVec( vecSrc, vecEnd, g_studio.lightspot ); + + // NOTE: we can't compute fake direction with using root bone origin + // because bone may some fluctuating and this looks ugly + Matrix3x4_OriginFromMatrix( g_rotationmatrix, vecSrc ); + VectorScale( lightDir, 2048.0f, vecEnd ); + VectorAdd( vecEnd, vecSrc, vecEnd ); + + vecSrc[0] -= 16.0f; + vecSrc[1] -= 16.0f; + vecEnd[0] -= 16.0f; + vecEnd[1] -= 16.0f; + + gcolor = R_LightVec( vecSrc, vecEnd, NULL ); + grad[0] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + + vecSrc[0] += 32.0f; + vecEnd[0] += 32.0f; + + gcolor = R_LightVec( vecSrc, vecEnd, NULL ); + grad[1] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + + vecSrc[1] += 32.0f; + vecEnd[1] += 32.0f; + + gcolor = R_LightVec( vecSrc, vecEnd, NULL ); + grad[2] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + + vecSrc[0] -= 32.0f; + vecEnd[0] -= 32.0f; + + gcolor = R_LightVec( vecSrc, vecEnd, NULL ); + grad[3] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + + lightDir[0] = grad[0] - grad[1] - grad[2] + grad[3]; + lightDir[1] = grad[1] + grad[0] - grad[2] - grad[3]; + VectorNormalize( lightDir ); + } + + VectorSet( finalLight, light.r, light.g, light.b ); + ent->cvFloorColor = light; + + total = Q_max( Q_max( light.r, light.g ), light.b ); + if( total == 0.0f ) total = 1.0f; + + // scale lightdir by light intentsity + VectorScale( lightDir, total, lightDir ); for( lnum = 0, dl = cl_dlights; lnum < MAX_DLIGHTS; lnum++, dl++ ) { - if( dl->die < g_studio.time || !dl->radius ) + if( dl->die < g_studio.time || !r_dynamic->value ) continue; - VectorSubtract( dl->origin, origin, direction ); - dist = VectorLength( direction ); + VectorSubtract( origin, dl->origin, dist ); - if( !dist || dist > dl->radius + studio_radius ) - continue; + radius = VectorLength( dist ); + add = dl->radius - radius; - radius2 = dl->radius * dl->radius; // squared radius - - for( i = 0; i < m_pStudioHeader->numbones; i++ ) + if( add > 0.0f ) { - vec3_t vec, org; - float dist, atten; - - Matrix3x4_OriginFromMatrix( g_lighttransform[i], org ); - VectorSubtract( org, dl->origin, vec ); - - dist = DotProduct( vec, vec ); - atten = (dist / radius2 - 1) * -1; - if( atten < 0 ) atten = 0; - dist = sqrt( dist ); + total += add; - if( dist ) - { - dist = 1.0f / dist; - VectorScale( vec, dist, vec ); - lightinfo->ambientlight += atten; - lightinfo->shadelight += atten; - } - - Matrix3x4_VectorIRotate( g_lighttransform[i], vec, plight->dlightvec[plight->numdlights][i] ); - VectorScale( plight->dlightvec[plight->numdlights][i], atten, plight->dlightvec[plight->numdlights][i] ); + if( radius > 1.0f ) + VectorScale( dist, ( add / radius ), dist ); + else VectorScale( dist, add, dist ); + + VectorAdd( lightDir, dist, lightDir ); + + finalLight[0] += dl->color.r * ( add / 256.0f ); + finalLight[1] += dl->color.g * ( add / 256.0f ); + finalLight[2] += dl->color.b * ( add / 256.0f ); } - - plight->dlightcolor[plight->numdlights][0] = dl->color.r * (1.0f / 255.0f); - plight->dlightcolor[plight->numdlights][1] = dl->color.g * (1.0f / 255.0f); - plight->dlightcolor[plight->numdlights][2] = dl->color.b * (1.0f / 255.0f); - plight->numdlights++; } - // clamp lighting so it doesn't overbright as much - if( lightinfo->ambientlight > 128 ) - lightinfo->ambientlight = 128; + if( FBitSet( ent->model->flags, STUDIO_AMBIENT_LIGHT )) + add = 0.6f; + else add = 0.9f; - if( lightinfo->ambientlight + lightinfo->shadelight > 192 ) - lightinfo->shadelight = 192 - lightinfo->ambientlight; + VectorScale( lightDir, add, lightDir ); + + plight->shadelight = VectorLength( lightDir ); + plight->ambientlight = total - plight->shadelight; + + total = Q_max( Q_max( finalLight[0], finalLight[1] ), finalLight[2] ); + + if( total > 0.0f ) + { + plight->color[0] = finalLight[0] * ( 1.0f / total ); + plight->color[1] = finalLight[1] * ( 1.0f / total ); + plight->color[2] = finalLight[2] * ( 1.0f / total ); + } + else VectorSet( plight->color, 1.0f, 1.0f, 1.0f ); + + if( plight->ambientlight > 128 ) + plight->ambientlight = 128; + + if( plight->ambientlight + plight->shadelight > 255 ) + plight->shadelight = 255 - plight->ambientlight; + + VectorNormalize2( lightDir, plight->plightvec ); } /* @@ -1602,82 +1732,81 @@ pfnStudioEntityLight */ void R_StudioEntityLight( alight_t *lightinfo ) { - uint lnum, i; - studiolight_t *plight; - float dist, radius2; - vec3_t direction, origin; - cl_entity_t *ent; + int lnum, i, j, k; + float minstrength, dist2, f; + float lstrength[MAX_LOCALLIGHTS]; + cl_entity_t *ent = RI.currententity; + vec3_t mid, origin, pos; dlight_t *el; - ent = RI.currententity; + g_studio.numlocallights = 0; - if( !ent || !ent->model || !r_dynamic->value ) + if( !ent || !r_dynamic->value ) return; - plight = &g_studiolight; - plight->numelights = 0; // clear previous elights + for( i = 0; i < MAX_LOCALLIGHTS; i++ ) + lstrength[i] = 0; - if( r_studio_lighting->value == 2 ) - { - Matrix3x4_OriginFromMatrix( g_lighttransform[0], origin ); - - // NOTE: in some cases bone origin may be stuck in the geometry - // and produced completely black model. Run additional check for this case - if( CL_PointContents( origin ) == CONTENTS_SOLID ) - Matrix3x4_OriginFromMatrix( g_rotationmatrix, origin ); - } - else Matrix3x4_OriginFromMatrix( g_rotationmatrix, origin ); + Matrix3x4_OriginFromMatrix( g_rotationmatrix, origin ); + dist2 = 1000000.0f; + k = 0; for( lnum = 0, el = cl_elights; lnum < MAX_ELIGHTS; lnum++, el++ ) { - if( el->die < g_studio.time || !el->radius ) + if( el->die < g_studio.time || el->radius <= 0.0f ) continue; - VectorSubtract( el->origin, origin, direction ); - dist = VectorLength( direction ); - - if( !dist || dist > el->radius + studio_radius ) - continue; - - radius2 = el->radius * el->radius; // squared radius - - for( i = 0; i < m_pStudioHeader->numbones; i++ ) + if(( el->key & 0xFFF ) == ent->index ) { - vec3_t vec, org; - float dist, atten; - - Matrix3x4_OriginFromMatrix( g_lighttransform[i], org ); - VectorSubtract( org, origin, vec ); - - dist = DotProduct( vec, vec ); - atten = (dist / radius2 - 1) * -1; - if( atten < 0 ) atten = 0; - dist = sqrt( dist ); + int att = (el->key >> 12) & 0xF; - if( dist ) - { - dist = 1.0f / dist; - VectorScale( vec, dist, vec ); - lightinfo->ambientlight += atten; - lightinfo->shadelight += atten; - } - - Matrix3x4_VectorIRotate( g_lighttransform[i], vec, plight->elightvec[plight->numelights][i] ); - VectorScale( plight->elightvec[plight->numelights][i], atten, plight->elightvec[plight->numelights][i] ); + if( att ) VectorCopy( ent->attachment[att], el->origin ); + else VectorCopy( ent->origin, el->origin ); } - plight->elightcolor[plight->numelights][0] = el->color.r * (1.0f / 255.0f); - plight->elightcolor[plight->numelights][1] = el->color.g * (1.0f / 255.0f); - plight->elightcolor[plight->numelights][2] = el->color.b * (1.0f / 255.0f); - plight->numelights++; + VectorCopy( el->origin, pos ); + VectorSubtract( origin, el->origin, mid ); + + f = VectorLength( mid ); + + if( f > ( el->radius * el->radius )) + minstrength = (el->radius * el->radius) / f; + else minstrength = 1.0f; + + if( minstrength > 0.004f ) + { + if( g_studio.numlocallights < 2 ) + { + k = g_studio.numlocallights; + } + else + { + k = -1; + + for( j = 0; j < g_studio.numlocallights; j++ ) + { + if( lstrength[j] < dist2 && lstrength[j] < minstrength ) + { + dist2 = lstrength[j]; + k = j; + } + } + } + + if( k != -1 ) + { + g_studio.locallight[k] = el; + lstrength[k] = minstrength; + g_studio.locallightR2[k] = (el->radius * el->radius); + g_studio.locallinearlight[k][0] = el->color.r; + g_studio.locallinearlight[k][1] = el->color.g; + g_studio.locallinearlight[k][2] = el->color.b; + + if( k + 1 > g_studio.numlocallights ) + g_studio.numlocallights = k + 1; + } + } } - - // clamp lighting so it doesn't overbright as much - if( lightinfo->ambientlight > 128 ) - lightinfo->ambientlight = 128; - - if( lightinfo->ambientlight + lightinfo->shadelight > 192 ) - lightinfo->shadelight = 192 - lightinfo->ambientlight; } /* @@ -1686,25 +1815,21 @@ R_StudioSetupLighting =============== */ -void R_StudioSetupLighting( alight_t *lightinfo ) +void R_StudioSetupLighting( alight_t *plight ) { - studiolight_t *plight; - int i; + int i; - if( !m_pStudioHeader || !lightinfo ) + if( !m_pStudioHeader || !plight ) return; - plight = &g_studiolight; + g_studio.ambientlight = plight->ambientlight; + g_studio.shadelight = plight->shadelight; + VectorCopy( plight->plightvec, g_studio.lightvec ); for( i = 0; i < m_pStudioHeader->numbones; i++ ) - Matrix3x4_VectorIRotate( g_lighttransform[i], lightinfo->plightvec, plight->blightvec[i] ); + Matrix3x4_VectorIRotate( g_lighttransform[i], plight->plightvec, g_studio.blightvec[i] ); - // copy custom alight color in case of mod-maker changed it - plight->lightcolor[0] = lightinfo->color[0]; - plight->lightcolor[1] = lightinfo->color[1]; - plight->lightcolor[2] = lightinfo->color[2]; - - R_StudioGetShadowImpactAndDir(); + VectorCopy( plight->color, g_studio.lightcolor ); } /* @@ -1715,98 +1840,122 @@ R_StudioLighting */ void R_StudioLighting( float *lv, int bone, int flags, vec3_t normal ) { - float max, ambient; - vec3_t illum; - studiolight_t *plight; + float illum; - if( !RI.drawWorld || RI.currententity->curstate.effects & EF_FULLBRIGHT ) + if( FBitSet( flags, STUDIO_NF_FULLBRIGHT )) { - VectorSet( lv, 1.0f, 1.0f, 1.0f ); + *lv = 1.0f; return; } - plight = &g_studiolight; - ambient = max( 0.1f, r_lighting_ambient->value ); // to avoid divison by zero - VectorScale( plight->lightcolor, ambient, illum ); + illum = g_studio.ambientlight; - if( flags & STUDIO_NF_FLATSHADE ) + if( FBitSet( flags, STUDIO_NF_FLATSHADE )) + { + illum += g_studio.shadelight * 0.8f; + } + else { - VectorMA( illum, 0.8f, plight->lightcolor, illum ); - } - else - { float r, lightcos; - int i; - lightcos = DotProduct( normal, plight->blightvec[bone] ); // -1 colinear, 1 opposite + if( bone != -1 ) lightcos = DotProduct( normal, g_studio.blightvec[bone] ); + else lightcos = DotProduct( normal, g_studio.lightvec ); // -1 colinear, 1 opposite + if( lightcos > 1.0f ) lightcos = 1.0f; - if( lightcos > 1.0f ) lightcos = 1; - VectorAdd( illum, plight->lightcolor, illum ); + illum += g_studio.shadelight; r = r_studio_lambert->value; - if( r < 1.0f ) r = 1.0f; - lightcos = (lightcos + ( r - 1.0f )) / r; // do modified hemispherical lighting - if( lightcos > 0.0f ) VectorMA( illum, -lightcos, plight->lightcolor, illum ); - - if( illum[0] <= 0.0f ) illum[0] = 0.0f; - if( illum[1] <= 0.0f ) illum[1] = 0.0f; - if( illum[2] <= 0.0f ) illum[2] = 0.0f; - // now add all dynamic lights - for( i = 0; i < plight->numdlights; i++) + // do modified hemispherical lighting + if( r <= 1.0f ) { - lightcos = -DotProduct( normal, plight->dlightvec[i][bone] ); - if( lightcos > 0.0f ) VectorMA( illum, lightcos, plight->dlightcolor[i], illum ); + r += 1.0f; + lightcos = (( r - 1.0f ) - lightcos) / r; + if( lightcos > 0.0f ) + illum += g_studio.shadelight * lightcos; + } + else + { + lightcos = (lightcos + ( r - 1.0f )) / r; + if( lightcos > 0.0f ) + illum -= g_studio.shadelight * lightcos; } - // now add all entity lights - for( i = 0; i < plight->numelights; i++) - { - lightcos = -DotProduct( normal, plight->elightvec[i][bone] ); - if( lightcos > 0.0f ) VectorMA( illum, lightcos, plight->elightcolor[i], illum ); - } + illum = Q_max( illum, 0.0f ); } - - max = VectorMax( illum ); - - if( max > 1.0f ) - VectorScale( illum, ( 1.0f / max ), lv ); - else VectorCopy( illum, lv ); + illum = Q_min( illum, 255.0f ); + *lv = illum * (1.0f / 255.0f); } /* -=============== -R_StudioSetupTextureHeader +==================== +R_LightLambert -=============== +==================== */ -void R_StudioSetupTextureHeader( void ) +void R_LightLambert( vec4_t light[MAX_LOCALLIGHTS], vec3_t normal, vec3_t color ) { -#ifndef STUDIO_MERGE_TEXTURES - if( !m_pStudioHeader->numtextures || !m_pStudioHeader->textureindex ) + vec3_t finalLight; + int i; + + VectorCopy( color, finalLight ); + + if( g_studio.numlocallights ) { - string texturename; - model_t *textures = NULL; - - Q_strncpy( texturename, R_StudioTexName( RI.currentmodel ), sizeof( texturename )); - COM_FixSlashes( texturename ); - - if( FS_FileExists( texturename, false )) - textures = Mod_ForName( texturename, false ); - - if( !textures ) + for( i = 0; i < g_studio.numlocallights; i++ ) { - m_pTextureHeader = NULL; - return; + float r, r2; + + r = -DotProduct( normal, light[i] ); + + if( r > 0 ) + { + if( light[i][3] == 0.0f ) + { + r2 = DotProduct( light[i], light[i] ); + + if( r2 > 0.0f ) + light[i][3] = g_studio.locallightR2[i] / ( r2 * sqrt( r2 )); + else light[i][3] = 1.0f; + } + finalLight[0] += g_studio.locallinearlight[i][0] * (r * light[i][3]) * 1024.0f; + finalLight[1] += g_studio.locallinearlight[i][1] * (r * light[i][3]) * 1024.0f; + finalLight[2] += g_studio.locallinearlight[i][2] * (r * light[i][3]) * 1024.0f; + } } - m_pTextureHeader = (studiohdr_t *)Mod_Extradata( textures ); + pglColor4f( finalLight[0], finalLight[1], finalLight[2], tr.blend ); } - else -#endif +} + +/* +==================== +R_LightStrength + +==================== +*/ +void R_LightStrength( int bone, float *vert, vec4_t light[MAX_LOCALLIGHTS] ) +{ + vec3_t lpos; + int i; + + if( g_studio.lightage[bone] != g_studio.framecount ) { - m_pTextureHeader = m_pStudioHeader; + for( i = 0; i < g_studio.numlocallights; i++ ) + { + lpos[0] = g_studio.locallight[i]->origin[0] - g_lighttransform[bone][0][3]; + lpos[1] = g_studio.locallight[i]->origin[1] - g_lighttransform[bone][1][3]; + lpos[2] = g_studio.locallight[i]->origin[2] - g_lighttransform[bone][2][3]; + Matrix3x4_VectorIRotate( g_lighttransform[bone], lpos, g_studio.lightbonepos[bone][i] ); + } + g_studio.lightage[bone] = g_studio.framecount; + } + + for( i = 0; i < g_studio.numlocallights; i++ ) + { + VectorSubtract( vert, g_studio.lightbonepos[bone][i], light[i] ); + light[i][3] = 0.0f; } } @@ -1816,25 +1965,23 @@ R_StudioSetupSkin =============== */ -static void R_StudioSetupSkin( mstudiotexture_t *ptexture, int index ) +static void R_StudioSetupSkin( studiohdr_t *ptexturehdr, int index ) { - short *pskinref; - int m_skinnum; + mstudiotexture_t *ptexture = NULL; - R_StudioSetupTextureHeader (); + if( FBitSet( g_nForceFaceFlags, STUDIO_NF_CHROME )) + return; - if( !m_pTextureHeader ) return; + if( ptexturehdr == NULL ) + return; - // NOTE: user can comment call StudioRemapColors and remap_info will be unavailable + // NOTE: user may ignore to call StudioRemapColors and remap_info will be unavailable if( m_fDoRemap ) ptexture = CL_GetRemapInfoForEntity( RI.currententity )->ptexture; + if( !ptexture ) ptexture = (mstudiotexture_t *)((byte *)ptexturehdr + ptexturehdr->textureindex); // fallback - // safety bounding the skinnum - m_skinnum = bound( 0, RI.currententity->curstate.skin, ( m_pTextureHeader->numskinfamilies - 1 )); - pskinref = (short *)((byte *)m_pTextureHeader + m_pTextureHeader->skinindex); - if( m_skinnum != 0 && m_skinnum < m_pTextureHeader->numskinfamilies ) - pskinref += (m_skinnum * m_pTextureHeader->numskinref); - - GL_Bind( GL_TEXTURE0, ptexture[pskinref[index]].index ); + if( r_lightmap->value && !r_fullbright->value ) + GL_Bind( GL_TEXTURE0, tr.whiteTexture ); + else GL_Bind( GL_TEXTURE0, ptexture[index].index ); } /* @@ -1852,29 +1999,7 @@ mstudiotexture_t *R_StudioGetTexture( cl_entity_t *e ) if(( phdr = Mod_Extradata( e->model )) == NULL ) return NULL; -#ifndef STUDIO_MERGE_TEXTURES - if( !m_pStudioHeader->numtextures || !m_pStudioHeader->textureindex ) - { - string texturename; - model_t *textures = NULL; - - Q_strncpy( texturename, R_StudioTexName( RI.currentmodel ), sizeof( texturename )); - COM_FixSlashes( texturename ); - - if( FS_FileExists( texturename, false )) - textures = Mod_ForName( texturename, false ); - - if( !textures ) - return NULL; - - thdr = (studiohdr_t *)Mod_Extradata( textures ); - } - else -#endif - { - thdr = m_pStudioHeader; - } - + thdr = m_pStudioHeader; if( !thdr ) return NULL; if( m_fDoRemap ) ptexture = CL_GetRemapInfoForEntity( e )->ptexture; @@ -1888,7 +2013,7 @@ void R_StudioSetRenderamt( int iRenderamt ) if( !RI.currententity ) return; RI.currententity->curstate.renderamt = iRenderamt; - RI.currententity->curstate.renderamt = CL_FxBlend( RI.currententity ); + tr.blend = CL_FxBlend( RI.currententity ) / 255.0f; } /* @@ -1957,6 +2082,127 @@ static int R_StudioMeshCompare( const sortedmesh_t *a, const sortedmesh_t *b ) return 0; } +/* +=============== +R_StudioDrawNormalMesh + +generic path +=============== +*/ +static _inline void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float s, float t ) +{ + float *lv; + int i; + + while( i = *( ptricmds++ )) + { + if( i < 0 ) + { + pglBegin( GL_TRIANGLE_FAN ); + i = -i; + } + else pglBegin( GL_TRIANGLE_STRIP ); + + for( ; i > 0; i--, ptricmds += 4 ) + { + lv = (float *)g_lightvalues[ptricmds[1]]; + + if( g_studio.numlocallights ) + R_LightLambert( g_studio.lightpos[ptricmds[0]], pstudionorms[ptricmds[1]], lv ); + else pglColor4f( lv[0], lv[1], lv[2], tr.blend ); + pglTexCoord2f( ptricmds[2] * s, ptricmds[3] * t ); + pglVertex3fv( g_studio.verts[ptricmds[0]] ); + } + + pglEnd(); + } +} + +/* +=============== +R_StudioDrawNormalMesh + +generic path +=============== +*/ +static _inline void R_StudioDrawFloatMesh( short *ptricmds, vec3_t *pstudionorms ) +{ + float *lv; + int i; + + while( i = *( ptricmds++ )) + { + if( i < 0 ) + { + pglBegin( GL_TRIANGLE_FAN ); + i = -i; + } + else pglBegin( GL_TRIANGLE_STRIP ); + + for( ; i > 0; i--, ptricmds += 4 ) + { + lv = (float *)g_lightvalues[ptricmds[1]]; + if( g_studio.numlocallights ) + R_LightLambert( g_studio.lightpos[ptricmds[0]], pstudionorms[ptricmds[1]], lv ); + else pglColor4f( lv[0], lv[1], lv[2], tr.blend ); + pglTexCoord2f( HalfToFloat( ptricmds[2] ), HalfToFloat( ptricmds[3] )); + pglVertex3fv( g_studio.verts[ptricmds[0]] ); + } + + pglEnd(); + } +} + +/* +=============== +R_StudioDrawNormalMesh + +generic path +=============== +*/ +static _inline void R_StudioDrawChromeMesh( short *ptricmds, vec3_t *pstudionorms, float s, float t, float scale ) +{ + float *lv, *av; + int i, idx; + qboolean glowShell = (scale > 0.0f) ? true : false; + vec3_t vert; + + while( i = *( ptricmds++ )) + { + if( i < 0 ) + { + pglBegin( GL_TRIANGLE_FAN ); + i = -i; + } + else pglBegin( GL_TRIANGLE_STRIP ); + + for( ; i > 0; i--, ptricmds += 4 ) + { + if( glowShell ) + { + idx = g_normaltable[ptricmds[0]]; + av = g_studio.verts[ptricmds[0]]; + lv = g_studio.norms[ptricmds[0]]; + VectorMA( av, scale, lv, vert ); + pglTexCoord2f( g_chrome[idx][0] * s, g_chrome[idx][1] * t ); + pglVertex3fv( vert ); + } + else + { + idx = ptricmds[1]; + lv = (float *)g_lightvalues[ptricmds[1]]; + if( g_studio.numlocallights ) + R_LightLambert( g_studio.lightpos[ptricmds[0]], pstudionorms[ptricmds[1]], lv ); + else pglColor4f( lv[0], lv[1], lv[2], tr.blend ); + pglTexCoord2f( g_chrome[idx][0] * s, g_chrome[idx][1] * t ); + pglVertex3fv( g_studio.verts[ptricmds[0]] ); + } + } + + pglEnd(); + } +} + /* =============== R_StudioDrawPoints @@ -1966,6 +2212,8 @@ R_StudioDrawPoints static void R_StudioDrawPoints( void ) { int i, j, m_skinnum; + float shellscale = 0.0f; + float *av, *lv, lv_tmp; byte *pvertbone; byte *pnormbone; vec3_t *pstudioverts; @@ -1973,83 +2221,76 @@ static void R_StudioDrawPoints( void ) mstudiotexture_t *ptexture; mstudiomesh_t *pmesh; short *pskinref; - float *av, *lv, *nv, scale; - R_StudioSetupTextureHeader (); - - g_nNumArrayVerts = g_nNumArrayElems = 0; - - if( !m_pTextureHeader ) return; - if( RI.currententity->curstate.renderfx == kRenderFxGlowShell ) - g_nStudioCount++; + if( !m_pStudioHeader ) return; // safety bounding the skinnum - m_skinnum = bound( 0, RI.currententity->curstate.skin, ( m_pTextureHeader->numskinfamilies - 1 )); + m_skinnum = bound( 0, RI.currententity->curstate.skin, ( m_pStudioHeader->numskinfamilies - 1 )); + ptexture = (mstudiotexture_t *)((byte *)m_pStudioHeader + m_pStudioHeader->textureindex); pvertbone = ((byte *)m_pStudioHeader + m_pSubModel->vertinfoindex); pnormbone = ((byte *)m_pStudioHeader + m_pSubModel->norminfoindex); - // NOTE: user can comment call StudioRemapColors and remap_info will be unavailable - if( m_fDoRemap ) ptexture = CL_GetRemapInfoForEntity( RI.currententity )->ptexture; - else ptexture = (mstudiotexture_t *)((byte *)m_pTextureHeader + m_pTextureHeader->textureindex); - - ASSERT( ptexture != NULL ); - pmesh = (mstudiomesh_t *)((byte *)m_pStudioHeader + m_pSubModel->meshindex); pstudioverts = (vec3_t *)((byte *)m_pStudioHeader + m_pSubModel->vertindex); pstudionorms = (vec3_t *)((byte *)m_pStudioHeader + m_pSubModel->normindex); - pskinref = (short *)((byte *)m_pTextureHeader + m_pTextureHeader->skinindex); - if( m_skinnum != 0 && m_skinnum < m_pTextureHeader->numskinfamilies ) - pskinref += (m_skinnum * m_pTextureHeader->numskinref); + pskinref = (short *)((byte *)m_pStudioHeader + m_pStudioHeader->skinindex); + if( m_skinnum != 0 ) pskinref += (m_skinnum * m_pStudioHeader->numskinref); for( i = 0; i < m_pSubModel->numverts; i++ ) - Matrix3x4_VectorTransform( g_bonestransform[pvertbone[i]], pstudioverts[i], g_xformverts[i] ); - - if( g_nForceFaceFlags & STUDIO_NF_CHROME ) { - scale = RI.currententity->curstate.renderamt * (1.0f / 255.0f); + Matrix3x4_VectorTransform( g_bonestransform[pvertbone[i]], pstudioverts[i], g_studio.verts[i] ); + R_LightStrength( pvertbone[i], pstudioverts[i], g_studio.lightpos[i] ); + } - for( i = 0; i < m_pSubModel->numnorms; i++ ) - Matrix3x4_VectorRotate( g_bonestransform[pnormbone[i]], pstudionorms[i], g_xformnorms[i] ); + // generate shared normals for properly scaling glowing shell + if( RI.currententity->curstate.renderfx == kRenderFxGlowShell ) + { + shellscale = RI.currententity->curstate.renderamt * (1.0f / 255.0f); + R_StudioBuildNormalTable(); + R_StudioGenerateNormals(); } lv = (float *)g_lightvalues; for( j = 0; j < m_pSubModel->nummesh; j++ ) { - g_nFaceFlags = ptexture[pskinref[pmesh[j].skinref]].flags; + g_nFaceFlags = ptexture[pskinref[pmesh[j].skinref]].flags | g_nForceFaceFlags; // fill in sortedmesh info - g_sortedMeshes[j].mesh = &pmesh[j]; - g_sortedMeshes[j].flags = g_nFaceFlags; + g_studio.meshes[j].mesh = &pmesh[j]; + g_studio.meshes[j].flags = g_nFaceFlags; for( i = 0; i < pmesh[j].numnorms; i++, lv += 3, pstudionorms++, pnormbone++ ) { - R_StudioLighting( lv, *pnormbone, g_nFaceFlags, (float *)pstudionorms ); + R_StudioLighting( &lv_tmp, *pnormbone, g_nFaceFlags, (float *)pstudionorms ); - if(( g_nFaceFlags & STUDIO_NF_CHROME ) || ( g_nForceFaceFlags & STUDIO_NF_CHROME )) + if( FBitSet( g_nFaceFlags, STUDIO_NF_CHROME )) R_StudioSetupChrome( g_chrome[(float (*)[3])lv - g_lightvalues], *pnormbone, (float *)pstudionorms ); + + VectorScale( g_studio.lightcolor, lv_tmp, lv ); } } if( r_studio_sort_textures->value ) { // sort opaque and translucent for right results - qsort( g_sortedMeshes, m_pSubModel->nummesh, sizeof( sortedmesh_t ), R_StudioMeshCompare ); + qsort( g_studio.meshes, m_pSubModel->nummesh, sizeof( sortedmesh_t ), R_StudioMeshCompare ); } for( j = 0; j < m_pSubModel->nummesh; j++ ) { float s, t, alpha; short *ptricmds; + vec3_t vert; - pmesh = g_sortedMeshes[j].mesh; + pmesh = g_studio.meshes[j].mesh; ptricmds = (short *)((byte *)m_pStudioHeader + pmesh->triindex); - g_nFaceFlags = ptexture[pskinref[pmesh->skinref]].flags; + g_nFaceFlags = ptexture[pskinref[pmesh->skinref]].flags|g_nForceFaceFlags; s = 1.0f / (float)ptexture[pskinref[pmesh->skinref]].width; t = 1.0f / (float)ptexture[pskinref[pmesh->skinref]].height; - if( g_iRenderMode != kRenderTransAdd ) + if( g_studio.rendermode != kRenderTransAdd ) pglDepthMask( GL_TRUE ); else pglDepthMask( GL_FALSE ); @@ -2059,18 +2300,18 @@ static void R_StudioDrawPoints( void ) if( g_nForceFaceFlags & STUDIO_NF_CHROME ) { - color24 *clr; - clr = &RI.currententity->curstate.rendercolor; + color24 *clr = &RI.currententity->curstate.rendercolor; pglColor4ub( clr->r, clr->g, clr->b, 255 ); + s = t = (1.0f / 256.0f); alpha = 1.0f; } - else if( g_nFaceFlags & STUDIO_NF_TRANSPARENT && R_StudioOpaque( g_iRenderMode )) + else if( g_nFaceFlags & STUDIO_NF_TRANSPARENT && R_StudioOpaque( g_studio.rendermode )) { GL_SetRenderMode( kRenderTransAlpha ); - pglAlphaFunc( GL_GEQUAL, 0.5f ); + pglAlphaFunc( GL_GREATER, 0.5f ); alpha = 1.0f; } - else if( g_nFaceFlags & STUDIO_NF_ADDITIVE ) + else if( g_nFaceFlags & STUDIO_NF_ADDITIVE && R_StudioOpaque( g_studio.rendermode )) { GL_SetRenderMode( kRenderTransAdd ); alpha = RI.currententity->curstate.renderamt * (1.0f / 255.0f); @@ -2085,9 +2326,9 @@ static void R_StudioDrawPoints( void ) } else { - GL_SetRenderMode( g_iRenderMode ); + GL_SetRenderMode( g_studio.rendermode ); - if( g_iRenderMode == kRenderNormal ) + if( g_studio.rendermode == kRenderNormal ) { pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); alpha = 1.0f; @@ -2095,64 +2336,36 @@ static void R_StudioDrawPoints( void ) else alpha = RI.currententity->curstate.renderamt * (1.0f / 255.0f); } - if( !( g_nForceFaceFlags & STUDIO_NF_CHROME )) - { - GL_Bind( GL_TEXTURE0, ptexture[pskinref[pmesh->skinref]].index ); - } + R_StudioSetupSkin( m_pStudioHeader, pskinref[pmesh->skinref] ); +#if 1 + if( FBitSet( g_nFaceFlags, STUDIO_NF_CHROME )) + R_StudioDrawChromeMesh( ptricmds, pstudionorms, s, t, shellscale ); + else if( FBitSet( g_nFaceFlags, STUDIO_NF_UV_COORDS )) + R_StudioDrawFloatMesh( ptricmds, pstudionorms ); + else R_StudioDrawNormalMesh( ptricmds, pstudionorms, s, t ); + r_stats.c_studio_polys += pmesh->numtris; +#else while( i = *( ptricmds++ )) { - int vertexState = 0; - qboolean tri_strip; - if( i < 0 ) { pglBegin( GL_TRIANGLE_FAN ); - tri_strip = false; i = -i; } else { pglBegin( GL_TRIANGLE_STRIP ); - tri_strip = true; } - r_stats.c_studio_polys += (i - 2); - for( ; i > 0; i--, ptricmds += 4 ) { - // build in indices - if( vertexState++ < 3 ) + if( g_nForceFaceFlags & STUDIO_NF_CHROME ) { - g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts; + int idx = g_normaltable[ptricmds[0]]; + pglTexCoord2f( g_chrome[idx][0] * s, g_chrome[idx][1] * t ); } - else if( tri_strip ) - { - // flip triangles between clockwise and counter clockwise - if( vertexState & 1 ) - { - // draw triangle [n-2 n-1 n] - g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts - 2; - g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts - 1; - g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts; - } - else - { - // draw triangle [n-1 n-2 n] - g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts - 1; - g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts - 2; - g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts; - } - } - else - { - // draw triangle fan [0 n-1 n] - g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts - ( vertexState - 1 ); - g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts - 1; - g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts; - } - - if( g_nFaceFlags & STUDIO_NF_CHROME || ( g_nForceFaceFlags & STUDIO_NF_CHROME )) + else if( g_nFaceFlags & STUDIO_NF_CHROME ) pglTexCoord2f( g_chrome[ptricmds[1]][0] * s, g_chrome[ptricmds[1]][1] * t ); else if( g_nFaceFlags & STUDIO_NF_UV_COORDS ) pglTexCoord2f( HalfToFloat( ptricmds[2] ), HalfToFloat( ptricmds[3] )); @@ -2160,11 +2373,11 @@ static void R_StudioDrawPoints( void ) if(!( g_nForceFaceFlags & STUDIO_NF_CHROME )) { - if( g_iRenderMode == kRenderTransAdd ) + if( g_studio.rendermode == kRenderTransAdd ) { pglColor4f( 1.0f, 1.0f, 1.0f, alpha ); } - else if( g_iRenderMode == kRenderTransColor ) + else if( g_studio.rendermode == kRenderTransColor ) { color24 *clr; clr = &RI.currententity->curstate.rendercolor; @@ -2181,29 +2394,23 @@ static void R_StudioDrawPoints( void ) } } - av = g_xformverts[ptricmds[0]]; + av = g_studio.verts[ptricmds[0]]; + lv = g_studio.norms[ptricmds[0]]; - if( g_nForceFaceFlags & STUDIO_NF_CHROME ) - { - vec3_t scaled_vertex; - nv = (float *)g_xformnorms[ptricmds[1]]; - VectorMA( av, scale, nv, scaled_vertex ); - pglVertex3fv( scaled_vertex ); - } - else - { - pglVertex3f( av[0], av[1], av[2] ); - ASSERT( g_nNumArrayVerts < MAXARRAYVERTS ); - VectorCopy( av, g_xarrayverts[g_nNumArrayVerts] ); // store off vertex - g_nNumArrayVerts++; - } + VectorMA( av, 0.58, lv, vert ); + + if( gl_test->value && g_nForceFaceFlags & STUDIO_NF_CHROME ) + pglVertex3fv( vert ); + else pglVertex3f( av[0], av[1], av[2] ); } + pglEnd(); } +#endif } // restore depthmask for next call StudioDrawPoints - if( g_iRenderMode != kRenderTransAdd ) + if( g_studio.rendermode != kRenderTransAdd ) pglDepthMask( GL_TRUE ); } @@ -2488,7 +2695,7 @@ static model_t *R_StudioSetupPlayerModel( int index ) player_info_t *info; string modelpath; - if( cls.key_dest == key_menu && !index ) + if( !RI.drawWorld ) { // we are in gameui. info = &gameui.playerinfo; @@ -2612,8 +2819,6 @@ pfnStudioSetHeader void R_StudioSetHeader( studiohdr_t *pheader ) { m_pStudioHeader = pheader; - - VectorClear( g_chrome_origin ); m_fDoRemap = false; } @@ -2637,11 +2842,11 @@ R_StudioSetupRenderer static void R_StudioSetupRenderer( int rendermode ) { if( rendermode > kRenderTransAdd ) rendermode = 0; - g_iRenderMode = bound( 0, rendermode, kRenderTransAdd ); + g_studio.rendermode = bound( 0, rendermode, kRenderTransAdd ); if( clgame.ds.cullMode != GL_NONE ) GL_Cull( GL_FRONT ); // enable depthmask on studiomodels - if( glState.drawTrans && g_iRenderMode != kRenderTransAdd ) + if( glState.drawTrans && g_studio.rendermode != kRenderTransAdd ) pglDepthMask( GL_TRUE ); pglAlphaFunc( GL_GEQUAL, 0.5f ); @@ -2662,7 +2867,7 @@ static void R_StudioRestoreRenderer( void ) pglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); // restore depthmask state for sprites etc - if( glState.drawTrans && g_iRenderMode != kRenderTransAdd ) + if( glState.drawTrans && g_studio.rendermode != kRenderTransAdd ) pglDepthMask( GL_FALSE ); else pglDepthMask( GL_TRUE ); @@ -2681,7 +2886,7 @@ R_StudioSetChromeOrigin */ void R_StudioSetChromeOrigin( void ) { - VectorNegate( RI.vieworg, g_chrome_origin ); + VectorCopy( RI.vieworg, g_chrome_origin ); } /* @@ -2698,51 +2903,66 @@ static int pfnIsHardware( void ) /* =============== -R_StudioDeformShadow +R_StudioDrawPointsShadow -Deform vertices by specified lightdir =============== */ -void R_StudioDeformShadow( void ) +static void R_StudioDrawPointsShadow( void ) { - float *verts, dist, dist2; - int numVerts; + float *av, height; + float vec_x, vec_y; + mstudiomesh_t *pmesh; + vec3_t point; + int i, k; - dist = g_shadowTrace.plane.dist + 1.0f; - dist2 = -1.0f / DotProduct( g_mvShadowVec, g_shadowTrace.plane.normal ); - VectorScale( g_mvShadowVec, dist2, g_mvShadowVec ); - - verts = g_xarrayverts[0]; - numVerts = g_nNumArrayVerts; - - for( numVerts = 0; numVerts < g_nNumArrayVerts; numVerts++, verts += 3 ) - { - dist2 = DotProduct( verts, g_shadowTrace.plane.normal ) - dist; - if( dist2 > 0.0f ) VectorMA( verts, dist2, g_mvShadowVec, verts ); - } -} - -static void R_StudioDrawPlanarShadow( void ) -{ - if( RI.currententity->curstate.effects & EF_NOSHADOW ) + if( FBitSet( RI.currententity->curstate.effects, EF_NOSHADOW )) return; - R_StudioDeformShadow (); - if( glState.stencilEnabled ) pglEnable( GL_STENCIL_TEST ); - pglEnableClientState( GL_VERTEX_ARRAY ); - pglVertexPointer( 3, GL_FLOAT, 12, g_xarrayverts ); + height = g_studio.lightspot[2] + 1.0f; + vec_x = -g_studio.lightvec[0] * 8.0f; + vec_y = -g_studio.lightvec[1] * 8.0f; - if( GL_Support( GL_DRAW_RANGEELEMENTS_EXT )) - pglDrawRangeElementsEXT( GL_TRIANGLES, 0, g_nNumArrayVerts, g_nNumArrayElems, GL_UNSIGNED_INT, g_xarrayelems ); - else pglDrawElements( GL_TRIANGLES, g_nNumArrayElems, GL_UNSIGNED_INT, g_xarrayelems ); + for( k = 0; k < m_pSubModel->nummesh; k++ ) + { + short *ptricmds; + + pmesh = (mstudiomesh_t *)((byte *)m_pStudioHeader + m_pSubModel->meshindex) + k; + ptricmds = (short *)((byte *)m_pStudioHeader + pmesh->triindex); + + r_stats.c_studio_polys += pmesh->numtris; + + while( i = *( ptricmds++ )) + { + if( i < 0 ) + { + pglBegin( GL_TRIANGLE_FAN ); + i = -i; + } + else + { + pglBegin( GL_TRIANGLE_STRIP ); + } + + + for( ; i > 0; i--, ptricmds += 4 ) + { + av = g_studio.verts[ptricmds[0]]; + point[0] = av[0] - (vec_x * ( av[2] - g_studio.lightspot[2] )); + point[1] = av[1] - (vec_y * ( av[2] - g_studio.lightspot[2] )); + point[2] = g_studio.lightspot[2] + 1.0f; + + pglVertex3fv( point ); + } + + pglEnd(); + } + } if( glState.stencilEnabled ) pglDisable( GL_STENCIL_TEST ); - - pglDisableClientState( GL_VERTEX_ARRAY ); } /* @@ -2770,7 +2990,7 @@ static void GL_StudioDrawShadow( void ) if( rendermode == kRenderNormal && RI.currententity != &clgame.viewent ) { - shadow_alpha = 1.0f - r_shadowalpha.value * 0.5f; + shadow_alpha = 1.0f - (tr.blend * 0.5f); pglDisable( GL_TEXTURE_2D ); pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); pglEnable( GL_BLEND ); @@ -2782,7 +3002,7 @@ static void GL_StudioDrawShadow( void ) depthmode = GL_LESS; pglDepthFunc( depthmode ); - R_StudioDrawPlanarShadow(); + R_StudioDrawPointsShadow(); depthmode2 = GL_LEQUAL; pglDepthFunc( depthmode2 ); @@ -2950,6 +3170,9 @@ void R_StudioProcessGait( entity_state_t *pplayer ) if( RI.currententity->curstate.sequence >= m_pStudioHeader->numseq ) RI.currententity->curstate.sequence = 0; + dt = bound( 0.0f, g_studio.frametime, 1.0f ); + if( dt == 0.0f ) return; + pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + RI.currententity->curstate.sequence; R_StudioPlayerBlend( pseqdesc, &iBlend, &RI.currententity->angles[PITCH] ); @@ -2958,8 +3181,6 @@ void R_StudioProcessGait( entity_state_t *pplayer ) RI.currententity->curstate.blending[0] = iBlend; RI.currententity->latched.prevblending[0] = RI.currententity->curstate.blending[0]; RI.currententity->latched.prevseqblending[0] = RI.currententity->curstate.blending[0]; - - dt = bound( 0.0f, g_studio.frametime, 1.0f ); R_StudioEstimateGait( pplayer ); // calc side to side turning @@ -3019,9 +3240,8 @@ R_StudioDrawPlayer static int R_StudioDrawPlayer( int flags, entity_state_t *pplayer ) { int m_nPlayerIndex; - float gaitframe = 0.0f, gaityaw = 0.0f; - vec3_t dir, prevgaitorigin; alight_t lighting; + vec3_t dir; m_nPlayerIndex = pplayer->number - 1; @@ -3034,15 +3254,6 @@ static int R_StudioDrawPlayer( int flags, entity_state_t *pplayer ) R_StudioSetHeader((studiohdr_t *)Mod_Extradata( RI.currentmodel )); - if( !RP_NORMALPASS() ) - { - m_pPlayerInfo = pfnPlayerInfo( m_nPlayerIndex ); - VectorCopy( m_pPlayerInfo->prevgaitorigin, prevgaitorigin ); - gaitframe = m_pPlayerInfo->gaitframe; - gaityaw = m_pPlayerInfo->gaityaw; - m_pPlayerInfo = NULL; - } - if( pplayer->gaitsequence ) { vec3_t orig_angles; @@ -3079,20 +3290,10 @@ static int R_StudioDrawPlayer( int flags, entity_state_t *pplayer ) { // see if the bounding box lets us trivially reject, also sets if( !R_StudioCheckBBox( )) - { - if( !RP_NORMALPASS() ) - { - m_pPlayerInfo = pfnPlayerInfo( m_nPlayerIndex ); - VectorCopy( prevgaitorigin, m_pPlayerInfo->prevgaitorigin ); - m_pPlayerInfo->gaitframe = gaitframe; - m_pPlayerInfo->gaityaw = gaityaw; - m_pPlayerInfo = NULL; - } return 0; - } r_stats.c_studio_models_drawn++; - g_nStudioCount++; // render data cache cookie + g_studio.framecount++; // render data cache cookie if( m_pStudioHeader->numbodyparts == 0 ) return 1; @@ -3171,15 +3372,6 @@ static int R_StudioDrawPlayer( int flags, entity_state_t *pplayer ) } } - if( !RP_NORMALPASS() ) - { - m_pPlayerInfo = pfnPlayerInfo( m_nPlayerIndex ); - VectorCopy( prevgaitorigin, m_pPlayerInfo->prevgaitorigin ); - m_pPlayerInfo->gaitframe = gaitframe; - m_pPlayerInfo->gaityaw = gaityaw; - m_pPlayerInfo = NULL; - } - return 1; } @@ -3197,7 +3389,6 @@ static int R_StudioDrawModel( int flags ) if( RI.currententity->curstate.renderfx == kRenderFxDeadPlayer ) { entity_state_t deadplayer; - int save_interp; int result; if( RI.currententity->curstate.renderamt <= 0 || RI.currententity->curstate.renderamt > cl.maxclients ) @@ -3215,13 +3406,10 @@ static int R_StudioDrawModel( int flags ) VectorCopy( RI.currententity->curstate.angles, deadplayer.angles ); VectorCopy( RI.currententity->curstate.origin, deadplayer.origin ); - save_interp = m_fDoInterp; - m_fDoInterp = 0; - - // draw as though it were a player - result = R_StudioDrawPlayer( flags, &deadplayer ); - - m_fDoInterp = save_interp; + g_studio.interpolate = false; + result = R_StudioDrawPlayer( flags, &deadplayer ); // draw as though it were a player + g_studio.interpolate = true; + return result; } @@ -3236,7 +3424,7 @@ static int R_StudioDrawModel( int flags ) return 0; r_stats.c_studio_models_drawn++; - g_nStudioCount++; // render data cache cookie + g_studio.framecount++; // render data cache cookie if( m_pStudioHeader->numbodyparts == 0 ) return 1; @@ -3291,7 +3479,6 @@ R_DrawStudioModel void R_DrawStudioModelInternal( cl_entity_t *e, qboolean follow_entity ) { int i, flags, result; - float prevFrame; if( RI.params & RP_ENVVIEW ) return; @@ -3305,21 +3492,13 @@ void R_DrawStudioModelInternal( cl_entity_t *e, qboolean follow_entity ) flags = STUDIO_RENDER; else flags = STUDIO_RENDER|STUDIO_EVENTS; - if( e == &clgame.viewent ) - m_fDoInterp = true; // viewmodel can't properly animate without lerping - else if( r_studio_lerping->value ) - m_fDoInterp = (e->curstate.effects & EF_NOINTERP) ? false : true; - else m_fDoInterp = false; - - prevFrame = e->latched.prevframe; - R_StudioSetupTimings(); // prevent to crash some mods like HLFX in menu Customize - if( !RI.drawWorld && !r_customdraw_playermodel->value ) + if( 1 )//!RI.drawWorld ) { if( e->player ) - result = R_StudioDrawPlayer( flags, R_StudioGetPlayerState( e->index - 1 )); + result = R_StudioDrawPlayer( flags, &e->curstate ); else result = R_StudioDrawModel( flags ); } else @@ -3330,9 +3509,6 @@ void R_DrawStudioModelInternal( cl_entity_t *e, qboolean follow_entity ) else result = pStudioDraw->StudioDrawModel( flags ); } - // old frame must be restored - if( !RP_NORMALPASS( )) e->latched.prevframe = prevFrame; - if( !result || follow_entity ) return; // NOTE: we must draw all followed entities @@ -3417,19 +3593,20 @@ void R_DrawViewModel( void ) if( !Mod_Extradata( clgame.viewent.model )) return; + tr.blend = CL_FxBlend( &clgame.viewent ) / 255.0f; + if( tr.blend <= 0.0f ) return; // invisible ? + RI.currententity = &clgame.viewent; RI.currentmodel = RI.currententity->model; if( !RI.currentmodel ) return; R_StudioSetupTimings(); - RI.currententity->curstate.renderamt = CL_FxBlend( RI.currententity ); - // hack the depth range to prevent view model from poking into walls pglDepthRange( gldepthmin, gldepthmin + 0.3f * ( gldepthmax - gldepthmin )); // backface culling for left-handed weapons - if( r_lefthand->value == 1 || g_iBackFaceCull ) + if( R_StudioFlipViewModel( RI.currententity ) || g_iBackFaceCull ) GL_FrontFace( !glState.frontFace ); if( !cl.local.weaponstarttime ) cl.local.weaponstarttime = g_studio.time; @@ -3442,7 +3619,7 @@ void R_DrawViewModel( void ) pglDepthRange( gldepthmin, gldepthmax ); // backface culling for left-handed weapons - if( r_lefthand->value == 1 || g_iBackFaceCull ) + if( R_StudioFlipViewModel( RI.currententity ) || g_iBackFaceCull ) GL_FrontFace( !glState.frontFace ); RI.currententity = NULL; @@ -3621,7 +3798,6 @@ void Mod_LoadStudioModel( model_t *mod, const void *buffer, qboolean *loaded ) phdr = R_StudioLoadHeader( mod, buffer ); if( !phdr ) return; // bad model -#ifdef STUDIO_MERGE_TEXTURES if( phdr->numtextures == 0 ) { studiohdr_t *thdr; @@ -3665,31 +3841,32 @@ void Mod_LoadStudioModel( model_t *mod, const void *buffer, qboolean *loaded ) memcpy( loadmodel->cache.data, buffer, phdr->texturedataindex ); phdr->length = phdr->texturedataindex; // update model size } -#else - // just copy model into memory - loadmodel->cache.data = Mem_Alloc( loadmodel->mempool, phdr->length ); - memcpy( loadmodel->cache.data, buffer, phdr->length ); -#endif + // setup bounding box - VectorCopy( phdr->bbmin, loadmodel->mins ); - VectorCopy( phdr->bbmax, loadmodel->maxs ); + if( !VectorCompare( vec3_origin, phdr->bbmin )) + { + // clipping bounding box + VectorCopy( phdr->bbmin, loadmodel->mins ); + VectorCopy( phdr->bbmax, loadmodel->maxs ); + } + else if( !VectorCompare( vec3_origin, phdr->min )) + { + // movement bounding box + VectorCopy( phdr->min, loadmodel->mins ); + VectorCopy( phdr->max, loadmodel->maxs ); + } + else + { + // well compute bounds from vertices and round to nearest even values + Mod_StudioComputeBounds( phdr, loadmodel->mins, loadmodel->maxs ); + RoundUpHullSize( loadmodel->mins ); + RoundUpHullSize( loadmodel->maxs ); + } loadmodel->numframes = R_StudioBodyVariations( loadmodel ); loadmodel->radius = RadiusFromBounds( loadmodel->mins, loadmodel->maxs ); loadmodel->flags = phdr->flags; // copy header flags - // check for static model - if( phdr->numseqgroups == 1 && phdr->numseq == 1 && phdr->numbones == 1 ) - { - mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)phdr + phdr->seqindex); - - // HACKHACK: MilkShape created a default animations with 30 frames - // TODO: analyze real frames for more predicatable results - // TODO: analyze all the sequences - if( pseqdesc->numframes == 1 || pseqdesc->numframes == 30 ) - pseqdesc->flags |= STUDIO_STATIC; - } - if( loaded ) *loaded = true; } diff --git a/engine/client/gl_vidnt.c b/engine/client/gl_vidnt.c index 7f25cffe..a04014d1 100644 --- a/engine/client/gl_vidnt.c +++ b/engine/client/gl_vidnt.c @@ -66,12 +66,11 @@ convar_t *r_faceplanecull; convar_t *r_drawentities; convar_t *r_adjust_fov; convar_t *r_flaresize; -convar_t *r_lefthand; convar_t *r_decals; convar_t *r_novis; convar_t *r_nocull; convar_t *r_lockpvs; -convar_t *r_lockcull; +convar_t *r_lockfrustum; convar_t *r_dynamic; convar_t *r_lightmap; convar_t *r_fastsky; @@ -1749,13 +1748,12 @@ void GL_InitCommands( void ) r_faceplanecull = Cvar_Get( "r_faceplanecull", "1", 0, "ignore face plane culling (perfomance test)" ); r_detailtextures = Cvar_Get( "r_detailtextures", "1", FCVAR_ARCHIVE, "enable detail textures support, use '2' for autogenerate detail.txt" ); r_lockpvs = Cvar_Get( "r_lockpvs", "0", FCVAR_CHEAT, "lockpvs area at current point (pvs test)" ); - r_lockcull = Cvar_Get( "r_lockcull", "0", FCVAR_CHEAT, "lock frustrum area at current point (cull test)" ); + r_lockfrustum = Cvar_Get( "r_lockfrustum", "0", FCVAR_CHEAT, "lock frustrum area at current point (cull test)" ); r_dynamic = Cvar_Get( "r_dynamic", "1", FCVAR_ARCHIVE, "allow dynamic lighting (dlights, lightstyles)" ); r_lightmap = Cvar_Get( "r_lightmap", "0", FCVAR_CHEAT, "lightmap debugging tool" ); r_fastsky = Cvar_Get( "r_fastsky", "0", FCVAR_ARCHIVE, "enable algorhytm fo fast sky rendering (for old machines)" ); r_drawentities = Cvar_Get( "r_drawentities", "1", FCVAR_CHEAT|FCVAR_ARCHIVE, "render entities" ); r_flaresize = Cvar_Get( "r_flaresize", "200", FCVAR_ARCHIVE, "set flares size" ); - r_lefthand = Cvar_Get( "hand", "0", FCVAR_ARCHIVE, "viewmodel handedness" ); r_decals = Cvar_Get( "r_decals", "4096", FCVAR_ARCHIVE, "sets the maximum number of decals" ); r_xpos = Cvar_Get( "r_xpos", "130", FCVAR_RENDERINFO, "window position by horizontal" ); r_ypos = Cvar_Get( "r_ypos", "48", FCVAR_RENDERINFO, "window position by vertical" ); diff --git a/engine/client/vgui/vgui_draw.c b/engine/client/vgui/vgui_draw.c index 6ee9ed32..a55b6895 100644 --- a/engine/client/vgui/vgui_draw.c +++ b/engine/client/vgui/vgui_draw.c @@ -135,7 +135,7 @@ void VGUI_CreateTexture( int id, int width, int height ) void VGUI_UploadTextureBlock( int id, int drawX, int drawY, const byte *rgba, int blockWidth, int blockHeight ) { - if( id <= 0 || id >= VGUI_MAX_TEXTURES || g_textures[id] == 0 || g_textures[id] == cls.fillImage ) + if( id <= 0 || id >= VGUI_MAX_TEXTURES || g_textures[id] == 0 || g_textures[id] == tr.whiteTexture ) { MsgDev( D_ERROR, "VGUI_UploadTextureBlock: bad texture %i. Ignored\n", id ); return; diff --git a/engine/common/mathlib.c b/engine/common/mathlib.c index 502dd5cf..c60a8cbe 100644 --- a/engine/common/mathlib.c +++ b/engine/common/mathlib.c @@ -15,8 +15,24 @@ GNU General Public License for more details. #include "common.h" #include "mathlib.h" +#include "eiface.h" -vec3_t vec3_origin = { 0, 0, 0 }; +#define NUM_HULL_ROUNDS ARRAYSIZE( hull_table ) +#define HULL_PRECISION 4 + +vec3_t vec3_origin = { 0, 0, 0 }; + +static word hull_table[] = { 2, 4, 6, 8, 12, 16, 18, 24, 28, 32, 36, 40, 48, 54, 56, 60, 64, 72, 80, 112, 120, 128, 140, 176 }; + +int boxpnt[6][4] = +{ +{ 0, 4, 6, 2 }, // +X +{ 0, 1, 5, 4 }, // +Y +{ 0, 2, 3, 1 }, // +Z +{ 7, 5, 1, 3 }, // -X +{ 7, 3, 2, 6 }, // -Y +{ 7, 6, 4, 5 }, // -Z +}; /* ================= @@ -75,6 +91,56 @@ float HalfToFloat( word h ) return *((float *)&f); } +/* +================= +RoundUpHullSize + +round the hullsize to nearest 'right' value +================= +*/ +void RoundUpHullSize( vec3_t size ) +{ + int i, j; + + for( i = 0; i < 3; i++) + { + qboolean negative = false; + float result, value; + + value = size[i]; + if( value < 0.0f ) negative = true; + value = Q_ceil( fabs( value )); + + // lookup hull table to find nearest supposed value + for( j = 0; j < NUM_HULL_ROUNDS; j++ ) + { + if( value > hull_table[j] ) + continue; // ceil only + + if( negative ) + { + result = ( value - hull_table[j] ); + if( result <= HULL_PRECISION ) + { + result = -hull_table[j]; + break; + } + } + else + { + result = ( value - hull_table[j] ); + if( result <= HULL_PRECISION ) + { + result = hull_table[j]; + break; + } + } + } + + size[i] = result; + } +} + /* ================= SignbitsForPlane @@ -370,6 +436,29 @@ qboolean BoundsAndSphereIntersect( const vec3_t mins, const vec3_t maxs, const v return true; } +/* +================= +SphereIntersect +================= +*/ +qboolean SphereIntersect( const vec3_t vSphereCenter, float fSphereRadiusSquared, const vec3_t vLinePt, const vec3_t vLineDir ) +{ + float a, b, c, insideSqr; + vec3_t p; + + // translate sphere to origin. + VectorSubtract( vLinePt, vSphereCenter, p ); + + a = DotProduct( vLineDir, vLineDir ); + b = 2.0f * DotProduct( p, vLineDir ); + c = DotProduct( p, p ) - fSphereRadiusSquared; + + insideSqr = b * b - 4.0f * a * c; + if( insideSqr <= 0.000001f ) + return false; + return true; +} + /* ================= RadiusFromBounds @@ -469,52 +558,70 @@ void QuaternionAngle( const vec4_t q, vec3_t angles ) /* ==================== -QuaternionSlerp +QuaternionAlign +make sure quaternions are within 180 degrees of one another, +if not, reverse q ==================== */ -void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt ) +void QuaternionAlign( const vec4_t p, const vec4_t q, vec4_t qt ) { - float omega, sclp, sclq; - float cosom, sinom; + // decide if one of the quaternions is backwards float a = 0.0f; float b = 0.0f; int i; - // decide if one of the quaternions is backwards - for( i = 0; i < 4; i++ ) + for( i = 0; i < 4; i++ ) { a += (p[i] - q[i]) * (p[i] - q[i]); b += (p[i] + q[i]) * (p[i] + q[i]); } - if( a > b ) + if( a > b ) { - for( i = 0; i < 4; i++ ) - { - q[i] = -q[i]; - } + for( i = 0; i < 4; i++ ) + qt[i] = -q[i]; } + else + { + for( i = 0; i < 4; i++ ) + qt[i] = q[i]; + } +} +/* +==================== +QuaternionSlerpNoAlign +==================== +*/ +void QuaternionSlerpNoAlign( const vec4_t p, const vec4_t q, float t, vec4_t qt ) +{ + float omega, cosom, sinom, sclp, sclq; + int i; + + // 0.0 returns p, 1.0 return q. cosom = p[0] * q[0] + p[1] * q[1] + p[2] * q[2] + p[3] * q[3]; - if(( 1.0 + cosom ) > 0.000001f ) + if(( 1.0f + cosom ) > 0.000001f ) { if(( 1.0f - cosom ) > 0.000001f ) { omega = acos( cosom ); sinom = sin( omega ); - sclp = sin(( 1.0f - t ) * omega ) / sinom; + sclp = sin( (1.0f - t) * omega) / sinom; sclq = sin( t * omega ) / sinom; } else { + // TODO: add short circuit for cosom == 1.0f? sclp = 1.0f - t; sclq = t; } for( i = 0; i < 4; i++ ) + { qt[i] = sclp * p[i] + sclq * q[i]; + } } else { @@ -526,6 +633,26 @@ void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt ) sclq = sin( t * ( 0.5f * M_PI )); for( i = 0; i < 3; i++ ) + { qt[i] = sclp * p[i] + sclq * qt[i]; + } } +} + +/* +==================== +QuaternionSlerp + +Quaternion sphereical linear interpolation +==================== +*/ +void QuaternionSlerp( const vec4_t p, const vec4_t q, float t, vec4_t qt ) +{ + vec4_t q2; + + // 0.0 returns p, 1.0 return q. + // decide if one of the quaternions is backwards + QuaternionAlign( p, q, q2 ); + + QuaternionSlerpNoAlign( p, q2, t, qt ); } \ No newline at end of file diff --git a/engine/common/mathlib.h b/engine/common/mathlib.h index 9faa6150..a1acddf8 100644 --- a/engine/common/mathlib.h +++ b/engine/common/mathlib.h @@ -119,6 +119,7 @@ float rsqrt( float number ); float anglemod( const float a ); word FloatToHalf( float v ); float HalfToFloat( word h ); +void RoundUpHullSize( vec3_t size ); int SignbitsForPlane( const vec3_t normal ); int NearestPOW( int value, qboolean roundDown ); void SinCos( float radians, float *sine, float *cosine ); @@ -133,11 +134,12 @@ void ClearBounds( vec3_t mins, vec3_t maxs ); void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs ); qboolean BoundsIntersect( const vec3_t mins1, const vec3_t maxs1, const vec3_t mins2, const vec3_t maxs2 ); qboolean BoundsAndSphereIntersect( const vec3_t mins, const vec3_t maxs, const vec3_t origin, float radius ); +qboolean SphereIntersect( const vec3_t vSphereCenter, float fSphereRadiusSquared, const vec3_t vLinePt, const vec3_t vLineDir ); float RadiusFromBounds( const vec3_t mins, const vec3_t maxs ); void AngleQuaternion( const vec3_t angles, vec4_t q, qboolean studio ); void QuaternionAngle( const vec4_t q, vec3_t angles ); -void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt ); +void QuaternionSlerp( const vec4_t p, const vec4_t q, float t, vec4_t qt ); float RemapVal( float val, float A, float B, float C, float D ); float ApproachVal( float target, float value, float speed ); @@ -180,6 +182,7 @@ void Matrix4x4_Transpose( matrix4x4 out, const matrix4x4 in1 ); qboolean Matrix4x4_Invert_Full( matrix4x4 out, const matrix4x4 in1 ); extern vec3_t vec3_origin; +extern int boxpnt[6][4]; extern const matrix3x4 matrix3x4_identity; extern const matrix4x4 matrix4x4_identity; diff --git a/engine/common/mod_local.h b/engine/common/mod_local.h index a17a3246..05452b52 100644 --- a/engine/common/mod_local.h +++ b/engine/common/mod_local.h @@ -166,6 +166,9 @@ qboolean Mod_GetStudioBounds( const char *name, vec3_t mins, vec3_t maxs ); void Mod_StudioGetAttachment( const edict_t *e, int iAttachment, float *org, float *ang ); void Mod_GetBonePosition( const edict_t *e, int iBone, float *org, float *ang ); hull_t *Mod_HullForStudio( model_t *m, float frame, int seq, vec3_t ang, vec3_t org, vec3_t size, byte *pcnt, byte *pbl, int *hitboxes, edict_t *ed ); +void R_StudioCalcBoneQuaternion( int frame, float s, void *pbone, void *panim, float *adj, vec4_t q ); +void R_StudioCalcBonePosition( int frame, float s, void *pbone, void *panim, vec3_t adj, vec3_t pos ); +void Mod_StudioComputeBounds( void *buffer, vec3_t mins, vec3_t maxs ); int Mod_HitgroupForStudioHull( int index ); #endif//MOD_LOCAL_H \ No newline at end of file diff --git a/engine/common/mod_studio.c b/engine/common/mod_studio.c index 61789b23..74cfc531 100644 --- a/engine/common/mod_studio.c +++ b/engine/common/mod_studio.c @@ -330,162 +330,6 @@ static void Mod_StudioCalcBoneAdj( float *adj, const byte *pcontroller ) } } -/* -==================== -StudioCalcBoneQuaterion - -==================== -*/ -static void Mod_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, true ); - AngleQuaternion( angle2, q2, true ); - QuaternionSlerp( q1, q2, s, q ); - } - else - { - AngleQuaternion( angle1, q, true ); - } -} - -/* -==================== -StudioCalcBonePosition - -==================== -*/ -static void Mod_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 @@ -515,8 +359,8 @@ static void Mod_StudioCalcRotations( int boneused[], int numbones, const byte *p for( j = numbones - 1; j >= 0; j-- ) { i = boneused[j]; - Mod_StudioCalcBoneQuaterion( frame, s, &pbone[i], &panim[i], adj, q[i] ); - Mod_StudioCalcBonePosition( frame, s, &pbone[i], &panim[i], adj, pos[i] ); + R_StudioCalcBoneQuaternion( frame, s, &pbone[i], &panim[i], adj, q[i] ); + R_StudioCalcBonePosition( frame, s, &pbone[i], &panim[i], adj, pos[i] ); } if( pseqdesc->motiontype & STUDIO_X ) pos[pseqdesc->motionbone][0] = 0.0f; @@ -869,8 +713,10 @@ void Mod_StudioComputeBounds( void *buffer, vec3_t mins, vec3_t maxs ) studiohdr_t *pstudiohdr; mstudiobodyparts_t *pbodypart; mstudiomodel_t *m_pSubModel; + mstudioseqgroup_t *pseqgroup; mstudioseqdesc_t *pseqdesc; mstudiobone_t *pbones; + mstudioanim_t *panim; vec3_t vecmins1, vecmaxs1; vec3_t vecmins2, vecmaxs2; int counter1, counter2; @@ -905,17 +751,21 @@ void Mod_StudioComputeBounds( void *buffer, vec3_t mins, vec3_t maxs ) } pbones = (mstudiobone_t *)((byte *)pstudiohdr + pstudiohdr->boneindex); - pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); for( i = 0; i < pstudiohdr->numseq; i++ ) { - mstudioanim_t *panim = (mstudioanim_t *) (((byte *)buffer) + pseqdesc[i].animindex); + pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + i; + pseqgroup = (mstudioseqgroup_t *)((byte *)pstudiohdr + pstudiohdr->seqgroupindex) + pseqdesc->seqgroup; + + if( pseqdesc->seqgroup == 0 ) + panim = (mstudioanim_t *)((byte *)pstudiohdr + pseqgroup->data + pseqdesc->animindex); + else continue; for( j = 0; j < pstudiohdr->numbones; j++ ) { - for( k = 0; k < pseqdesc[i].numframes; k++ ) + for( k = 0; k < pseqdesc->numframes; k++ ) { - Mod_StudioCalcBonePosition( k, 0, &pbones[j], panim, NULL, pos ); + R_StudioCalcBonePosition( k, 0, &pbones[j], panim, NULL, pos ); Mod_StudioBoundVertex( vecmins2, vecmaxs2, &counter2, pos ); } } diff --git a/engine/common/pm_debug.c b/engine/common/pm_debug.c index f1b24a73..45f1da7d 100644 --- a/engine/common/pm_debug.c +++ b/engine/common/pm_debug.c @@ -20,16 +20,6 @@ GNU General Public License for more details. // expand debugging BBOX particle hulls by this many units. #define BOX_GAP 0.0f -static int boxpnt[6][4] = -{ -{ 0, 4, 6, 2 }, // +X -{ 0, 1, 5, 4 }, // +Y -{ 0, 2, 3, 1 }, // +Z -{ 7, 5, 1, 3 }, // -X -{ 7, 3, 2, 6 }, // -Y -{ 7, 6, 4, 5 }, // -Z -}; - /* =============== PM_ParticleLine diff --git a/engine/common/pm_trace.c b/engine/common/pm_trace.c index b134a8ae..7fe397f5 100644 --- a/engine/common/pm_trace.c +++ b/engine/common/pm_trace.c @@ -22,6 +22,8 @@ GNU General Public License for more details. #include "studio.h" #include "world.h" +#define PM_AllowHitBoxTrace( model, hull ) ( model && model->type == mod_studio && ( FBitSet( model->flags, STUDIO_TRACE_HITBOX ) || hull == 2 )) + static mplane_t pm_boxplanes[6]; static dclipnode_t pm_boxclipnodes[6]; static hull_t pm_boxhull; @@ -341,22 +343,23 @@ pmtrace_t PM_PlayerTraceExt( playermove_t *pmove, vec3_t start, vec3_t end, int VectorCopy( pmove->player_maxs[pmove->usehull], maxs ); VectorClear( offset ); } - else if( !pe->model ) + else if( pe->model ) { - if( !pe->studiomodel ) + hull = PM_HullForBsp( pe, pmove, offset ); + } + else + { + if( pe->studiomodel ) { - VectorSubtract( pe->mins, pmove->player_maxs[pmove->usehull], mins ); - VectorSubtract( pe->maxs, pmove->player_mins[pmove->usehull], maxs ); - - hull = PM_HullForBox( mins, maxs ); - VectorCopy( pe->origin, offset ); - } - else - { - if( flags & PM_STUDIO_IGNORE ) + if( FBitSet( flags, PM_STUDIO_IGNORE )) continue; - if( pe->studiomodel->type != mod_studio || (!( pe->studiomodel->flags & STUDIO_TRACE_HITBOX ) && ( pmove->usehull != 2 || flags & PM_STUDIO_BOX ))) + if( PM_AllowHitBoxTrace( pe->studiomodel, pmove->usehull ) && !FBitSet( flags, PM_STUDIO_BOX )) + { + hull = PM_HullForStudio( pe, pmove, &hullcount ); + VectorClear( offset ); + } + else { VectorSubtract( pe->mins, pmove->player_maxs[pmove->usehull], mins ); VectorSubtract( pe->maxs, pmove->player_mins[pmove->usehull], maxs ); @@ -364,16 +367,16 @@ pmtrace_t PM_PlayerTraceExt( playermove_t *pmove, vec3_t start, vec3_t end, int hull = PM_HullForBox( mins, maxs ); VectorCopy( pe->origin, offset ); } - else - { - hull = PM_HullForStudio( pe, pmove, &hullcount ); - VectorClear( offset ); - } + } + else + { + VectorSubtract( pe->mins, pmove->player_maxs[pmove->usehull], mins ); + VectorSubtract( pe->maxs, pmove->player_mins[pmove->usehull], maxs ); + + hull = PM_HullForBox( mins, maxs ); + VectorCopy( pe->origin, offset ); } - } - else - { - hull = PM_HullForBsp( pe, pmove, offset ); + } if( pe->solid == SOLID_BSP && !VectorIsNull( pe->angles )) @@ -538,7 +541,12 @@ int PM_TestPlayerPosition( playermove_t *pmove, vec3_t pos, pmtrace_t *ptrace, p { hull = PM_HullForBsp( pe, pmove, offset ); } - else if( !pe->studiomodel || pe->studiomodel->type != mod_studio || (!( pe->studiomodel->flags & STUDIO_TRACE_HITBOX ) && pmove->usehull != 2 )) + else if( PM_AllowHitBoxTrace( pe->studiomodel, pmove->usehull )) + { + hull = PM_HullForStudio( pe, pmove, &hullcount ); + VectorClear( offset ); + } + else { VectorSubtract( pe->mins, pmove->player_maxs[pmove->usehull], mins ); VectorSubtract( pe->maxs, pmove->player_mins[pmove->usehull], maxs ); @@ -546,11 +554,6 @@ int PM_TestPlayerPosition( playermove_t *pmove, vec3_t pos, pmtrace_t *ptrace, p hull = PM_HullForBox( mins, maxs ); VectorCopy( pe->origin, offset ); } - else - { - hull = PM_HullForStudio( pe, pmove, &hullcount ); - VectorClear( offset ); - } // CM_TransformedPointContents :-) if( pe->solid == SOLID_BSP && !VectorIsNull( pe->angles )) diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 7748b1e1..cf79663a 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -125,6 +125,7 @@ SV_CheckVelocity void SV_CheckVelocity( edict_t *ent ) { float wishspd; + float maxspd; int i; // bound velocity @@ -144,10 +145,11 @@ void SV_CheckVelocity( edict_t *ent ) } wishspd = DotProduct( ent->v.velocity, ent->v.velocity ); + maxspd = sv_maxvelocity.value * sv_maxvelocity.value * 1.73f; // half-diagonal - if( wishspd > ( sv_maxvelocity.value * sv_maxvelocity.value )) + if( wishspd > maxspd ) { - MsgDev( D_INFO, "Got a velocity too high on %s\n", STRING( ent->v.classname )); + MsgDev( D_INFO, "Got a velocity too high on %s ( %.2f > %.2f )\n", STRING( ent->v.classname ), sqrt( wishspd ), sqrt( maxspd )); wishspd = sv_maxvelocity.value / sqrt( wishspd ); VectorScale( ent->v.velocity, wishspd, ent->v.velocity ); } diff --git a/engine/server/sv_world.c b/engine/server/sv_world.c index 521a9cf5..0e0dd4d4 100644 --- a/engine/server/sv_world.c +++ b/engine/server/sv_world.c @@ -107,6 +107,48 @@ void SV_StudioPlayerBlend( mstudioseqdesc_t *pseqdesc, int *pBlend, float *pPitc } } +/* +==================== +SV_CheckSphereIntersection + +check clients only +==================== +*/ +qboolean SV_CheckSphereIntersection( edict_t *ent, const vec3_t start, const vec3_t end ) +{ + int i, sequence; + float radiusSquared; + vec3_t traceOrg, traceDir; + studiohdr_t *pstudiohdr; + mstudioseqdesc_t *pseqdesc; + model_t *mod; + + if( !FBitSet( ent->v.flags, FL_CLIENT|FL_FAKECLIENT )) + return true; + + if(( mod = Mod_Handle( ent->v.modelindex )) == NULL ) + return true; + + if(( pstudiohdr = (studiohdr_t *)Mod_Extradata( mod )) == NULL ) + return true; + + sequence = ent->v.sequence; + if( sequence < 0 || sequence >= pstudiohdr->numseq ) + sequence = 0; + + pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + sequence; + + VectorCopy( start, traceOrg ); + VectorSubtract( end, start, traceDir ); + radiusSquared = 0.0f; + + for ( i = 0; i < 3; i++ ) + radiusSquared += Q_max( fabs( pseqdesc->bbmin[i] ), fabs( pseqdesc->bbmax[i] )); + + return SphereIntersect( ent->v.origin, radiusSquared, traceOrg, traceDir ); +} + + /* =================== SV_HullForBox @@ -1141,6 +1183,10 @@ static void SV_ClipToLinks( areanode_t *node, moveclip_t *clip ) if( !BoundsIntersect( clip->boxmins, clip->boxmaxs, touch->v.absmin, touch->v.absmax )) continue; + // aditional check to intersects clients with sphere + if( touch->v.solid != SOLID_SLIDEBOX && !SV_CheckSphereIntersection( touch, clip->start, clip->end )) + continue; + // Xash3D extension if( SV_IsValidEdict( clip->passedict ) && clip->passedict->v.solid == SOLID_TRIGGER ) { diff --git a/engine/studio.h b/engine/studio.h index 7ec864de..f44fad38 100644 --- a/engine/studio.h +++ b/engine/studio.h @@ -57,8 +57,9 @@ Studio models are position independent, so the cache manager can move them. #define STUDIO_ZOMGIB 0x0020 // small blood trail #define STUDIO_TRACER2 0x0040 // orange split trail + rotate #define STUDIO_TRACER3 0x0080 // purple trail -#define STUDIO_DYNAMIC_LIGHT 0x0100 // dynamically get lighting from floor or ceil (flying monsters) +#define STUDIO_AMBIENT_LIGHT 0x0100 // force to use ambient shading #define STUDIO_TRACE_HITBOX 0x0200 // always use hitbox trace instead of bbox +#define STUDIO_FORCE_SKYLIGHT 0x0400 // always grab lightvalues from the sky settings (even if sky is invisible) // lighting & rendermode options #define STUDIO_NF_FLATSHADE 0x0001 @@ -97,7 +98,6 @@ Studio models are position independent, so the cache manager can move them. // sequence flags #define STUDIO_LOOPING 0x0001 -#define STUDIO_STATIC 0x8000 // studiomodel is static // bone flags #define STUDIO_HAS_NORMALS 0x0001