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

1653 lines
46 KiB
C

//=======================================================================
// Copyright XashXT Group 2007 ©
// r_studio.c - render studio models
//=======================================================================
#include "gl_local.h"
/*
=============================================================
STUDIO MODELS
=============================================================
*/
#define STUDIO_API_VERSION 0.1
matrix3x4 m_pbonestransform [ MAXSTUDIOBONES ];
matrix3x4 m_plighttransform [ MAXSTUDIOBONES ];
matrix3x4 m_protationmatrix;
vec3_t m_plightcolor; //ambient light color
vec3_t m_plightvec; //ambleint light vector
vec3_t m_pshadevector; //shadow vector
//lighting stuff
vec3_t *m_pxformverts;
vec3_t *m_pvlightvalues;
vec3_t m_blightvec [ MAXSTUDIOBONES ];
vec3_t g_xformverts[ MAXSTUDIOVERTS ];
vec3_t g_lightvalues[MAXSTUDIOVERTS];
//chrome stuff
int g_chrome[MAXSTUDIOVERTS][2]; // texture coords for surface normals
int g_chromeage[MAXSTUDIOBONES]; // last time chrome vectors were updated
vec3_t g_chromeup[MAXSTUDIOBONES]; // chrome vector "up" in bone reference frames
vec3_t g_chromeright[MAXSTUDIOBONES]; // chrome vector "right" in bone reference frames
int m_fDoInterp;
int m_pStudioModelCount;
int m_PassNum;
model_t *m_pRenderModel;
entity_t *m_pCurrentEntity;
mstudiomodel_t *m_pSubModel;
studiohdr_t *m_pStudioHeader;
studiohdr_t *m_pTextureHeader;
mstudiobodyparts_t *m_pBodyPart;
int m_nCachedBones; // Number of bones in cache
char m_nCachedBoneNames[ MAXSTUDIOBONES ][ 32 ];
matrix3x4 m_rgCachedBonesTransform [ MAXSTUDIOBONES ];
matrix3x4 m_rgCachedLightTransform [ MAXSTUDIOBONES ];
//studio cvars
cvar_t *r_developer;
/*
====================
R_StudioInit
====================
*/
void R_StudioInit( void )
{
ri.Con_Printf (PRINT_DEVELOPER, "Init Xash Studio. ver: %g\n", STUDIO_API_VERSION);
//get developer value
r_developer = ri.Cvar_Get( "developer", "0", 0 );
m_pBodyPart = NULL;
m_pRenderModel = NULL;
m_pStudioHeader = NULL;
m_pCurrentEntity = NULL;
//TODO:make included interface for studio
}
void R_StudioShutdown( void )
{
}
/*
====================
Studio model loader
====================
*/
image_t *R_StudioLoadTexture( model_t *mod, mstudiotexture_t *ptexture, byte *pin )
{
image_t *image;
rgbdata_t r_skin;
r_skin.width = ptexture->width;
r_skin.height = ptexture->height;
r_skin.flags |= (ptexture->flags & STUDIO_NF_TRANSPARENT) ? IMAGE_HAS_ALPHA : 0;
r_skin.type = PF_INDEXED_24;
r_skin.numMips = 4;
r_skin.palette = pin + ptexture->width * ptexture->height + ptexture->index;
r_skin.buffer = pin + ptexture->index; //texdata
//load studio texture and bind it
image = R_LoadImage(ptexture->name, &r_skin, it_skin );
if(!image)
{
Msg("Warning: %s has null texture %s\n", mod->name, ptexture->name );
image = r_notexture;
}
ptexture->index = image->texnum;
return image;
}
studiohdr_t *R_StudioLoadHeader( model_t *mod, unsigned *buffer )
{
int i;
byte *pin;
studiohdr_t *phdr;
mstudiotexture_t *ptexture;
pin = (byte *)buffer;
phdr = (studiohdr_t *)pin;
if (phdr->version != STUDIO_VERSION)
{
Msg("%s has wrong version number (%i should be %i)\n", phdr->name, phdr->version, STUDIO_VERSION);
return NULL;
}
ptexture = (mstudiotexture_t *)(pin + phdr->textureindex);
if (phdr->textureindex > 0 && phdr->numtextures <= MAXSTUDIOSKINS)
{
mod->numtexinfo = phdr->numtextures; //save info
for (i = 0; i < phdr->numtextures; i++)
mod->skins[i] = R_StudioLoadTexture( mod, &ptexture[i], pin );
}
return (studiohdr_t *)buffer;
}
void R_StudioLoadModel (model_t *mod, void *buffer)
{
studiohdr_t *phdr = R_StudioLoadHeader( mod, buffer );
studiohdr_t *thdr;
if(!phdr) return; //there were problems
mod->phdr = (studiohdr_t *)Mem_Alloc(mod->mempool, LittleLong(phdr->length));
memcpy(mod->phdr, buffer, LittleLong(phdr->length));
if (phdr->numtextures == 0)
{
buffer = FS_LoadFile ( R_ExtName( mod ), NULL );//use buffer again
if(!buffer) ri.Sys_Error (ERR_DROP, "%s not found!\n", R_ExtName( mod ));
thdr = R_StudioLoadHeader( mod, buffer );
if(!thdr) return; //there were problems
mod->thdr = (studiohdr_t *)Mem_Alloc(mod->mempool, LittleLong(thdr->length));
memcpy(mod->thdr, buffer, LittleLong(thdr->length));
}
else mod->thdr = mod->phdr; //just make link
R_StudioExtractBbox( phdr, 0, mod->mins, mod->maxs );
mod->registration_sequence = registration_sequence;
}
/*
====================
MISC STUDIO UTILS
====================
*/
//extract texture filename from modelname
char *R_ExtName( model_t *mod )
{
static char texname[MAX_QPATH];
strcpy( texname, mod->name );
strcpy( &texname[strlen(texname) - 4], "T.mdl" );
return texname;
}
// extract bbox from animation
int R_ExtractBbox( int sequence, float *mins, float *maxs )
{
return R_StudioExtractBbox( m_pStudioHeader, sequence, mins, maxs );
}
int R_StudioExtractBbox( studiohdr_t *phdr, int sequence, float *mins, float *maxs )
{
mstudioseqdesc_t *pseqdesc;
pseqdesc = (mstudioseqdesc_t *)((byte *)phdr + phdr->seqindex);
if(sequence == -1) return 0;
VectorCopy( pseqdesc[ sequence ].bbmin, mins );
VectorCopy( pseqdesc[ sequence ].bbmax, maxs );
return 1;
}
void SetBodygroup( int iGroup, int iValue )
{
int iCurrent;
mstudiobodyparts_t *m_pBodyPart;
if (iGroup > m_pStudioHeader->numbodyparts)
return;
m_pBodyPart = (mstudiobodyparts_t *)((byte *)m_pStudioHeader + m_pStudioHeader->bodypartindex) + iGroup;
if (iValue >= m_pBodyPart->nummodels)
return;
iCurrent = (m_pCurrentEntity->body / m_pBodyPart->base) % m_pBodyPart->nummodels;
m_pCurrentEntity->body = (m_pCurrentEntity->body - (iCurrent * m_pBodyPart->base) + (iValue * m_pBodyPart->base));
}
int R_StudioGetBodygroup( int iGroup )
{
mstudiobodyparts_t *m_pBodyPart;
if (iGroup > m_pStudioHeader->numbodyparts)
return 0;
m_pBodyPart = (mstudiobodyparts_t *)((byte *)m_pStudioHeader + m_pStudioHeader->bodypartindex) + iGroup;
if (m_pBodyPart->nummodels <= 1)
return 0;
return (m_pCurrentEntity->body / m_pBodyPart->base) % m_pBodyPart->nummodels;
}
/*
====================
StudioCalcBoneAdj
====================
*/
void R_StudioCalcBoneAdj( float dadt, float *adj, const byte *pcontroller1, const byte *pcontroller2, byte mouthopen )
{
int i, j;
float value;
mstudiobonecontroller_t *pbonecontroller;
pbonecontroller = (mstudiobonecontroller_t *)((byte *)m_pStudioHeader + m_pStudioHeader->bonecontrollerindex);
for (j = 0; j < m_pStudioHeader->numbonecontrollers; j++)
{
i = pbonecontroller[j].index;
if( i == 4) //mouth hardcoded at controller 4
{
value = mouthopen / 64.0;
if (value > 1.0) value = 1.0;
value = (1.0 - value) * pbonecontroller[j].start + value * pbonecontroller[j].end;
// ri.Con_Printf(PRINT_ALL, "%d %f\n", mouthopen, value );
}
else if (i <= MAXSTUDIOCONTROLLERS )
{
// check for 360% wrapping
if (pbonecontroller[j].type & STUDIO_RLOOP)
{
if (abs(pcontroller1[i] - pcontroller2[i]) > 128)
{
int a, b;
a = (pcontroller1[j] + 128) % 256;
b = (pcontroller2[j] + 128) % 256;
value = ((a * dadt) + (b * (1 - dadt)) - 128) * (360.0/256.0) + pbonecontroller[j].start;
}
else
{
value = ((pcontroller1[i] * dadt + (pcontroller2[i]) * (1.0 - dadt))) * (360.0/256.0) + pbonecontroller[j].start;
}
}
else
{
value = (pcontroller1[i] * dadt + pcontroller2[i] * (1.0 - dadt)) / 255.0;
if (value < 0) value = 0;
if (value > 1.0) value = 1.0;
value = (1.0 - value) * pbonecontroller[j].start + value * pbonecontroller[j].end;
}
// ri.Con_Printf( PRINT_ALL, "%d %d %f : %f\n", m_pCurrentEntity->curstate.controller[j], m_pCurrentEntity->latched.prevcontroller[j], value, dadt );
}
switch(pbonecontroller[j].type & STUDIO_TYPES)
{
case STUDIO_XR:
case STUDIO_YR:
case STUDIO_ZR:
adj[j] = value * (M_PI / 180.0);
break;
case STUDIO_X:
case STUDIO_Y:
case STUDIO_Z:
adj[j] = value;
break;
}
}
}
/*
====================
StudioCalcBoneQuaterion
====================
*/
void R_StudioCalcBoneQuaterion( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, float *q )
{
int j, k;
vec4_t q1, q2;
vec3_t angle1, angle2;
mstudioanimvalue_t *panimvalue;
for (j = 0; j < 3; j++)
{
if (panim->offset[j+3] == 0)
{
angle2[j] = angle1[j] = pbone->value[j+3]; // default;
}
else
{
panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j+3]);
k = frame;
// debug
if (panimvalue->num.total < panimvalue->num.valid) k = 0;
while (panimvalue->num.total <= k)
{
k -= panimvalue->num.total;
panimvalue += panimvalue->num.valid + 1;
// DEBUG
if (panimvalue->num.total < panimvalue->num.valid)
k = 0;
}
// Bah, missing blend!
if (panimvalue->num.valid > k)
{
angle1[j] = panimvalue[k+1].value;
if (panimvalue->num.valid > k + 1)
{
angle2[j] = panimvalue[k+2].value;
}
else
{
if (panimvalue->num.total > k + 1)
angle2[j] = angle1[j];
else
angle2[j] = panimvalue[panimvalue->num.valid+2].value;
}
}
else
{
angle1[j] = panimvalue[panimvalue->num.valid].value;
if (panimvalue->num.total > k + 1)
{
angle2[j] = angle1[j];
}
else
{
angle2[j] = panimvalue[panimvalue->num.valid + 2].value;
}
}
angle1[j] = pbone->value[j+3] + angle1[j] * pbone->scale[j+3];
angle2[j] = pbone->value[j+3] + angle2[j] * pbone->scale[j+3];
}
if (pbone->bonecontroller[j+3] != -1)
{
angle1[j] += adj[pbone->bonecontroller[j+3]];
angle2[j] += adj[pbone->bonecontroller[j+3]];
}
}
if (!VectorCompare( angle1, angle2 ))
{
AngleQuaternion( angle1, q1 );
AngleQuaternion( angle2, q2 );
QuaternionSlerp( q1, q2, s, q );
}
else
{
AngleQuaternion( angle1, q );
}
}
/*
====================
StudioCalcBonePosition
====================
*/
void R_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)
{
panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j]);
//if (j == 0) Msg("%d %d:%d %f\n", frame, panimvalue->num.valid, panimvalue->num.total, s );
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.0 - 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.0 - 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]];
}
}
}
/*
====================
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;
if (s < 0) s = 0;
else if (s > 1.0) s = 1.0;
s1 = 1.0 - s;
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;
}
}
/*
====================
StudioGetAnim
====================
*/
mstudioanim_t *R_StudioGetAnim( model_t *m_pSubModel, mstudioseqdesc_t *pseqdesc )
{
mstudioseqgroup_t *pseqgroup;
cache_user_t *paSequences;
byte *buf;
int filesize;
//WARNING! Not tested!!!
pseqgroup = (mstudioseqgroup_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqgroupindex) + pseqdesc->seqgroup;
if (pseqdesc->seqgroup == 0)
{
return (mstudioanim_t *)((byte *)m_pStudioHeader + pseqgroup->data + pseqdesc->animindex);
}
paSequences = (cache_user_t *)m_pSubModel->submodels;
if (paSequences == NULL)
{
Msg("loading %s\n", pseqgroup->name );
buf = FS_LoadFile (pseqgroup->name, &filesize);
if (!buf)
{
ri.Sys_Error (ERR_DROP, "R_StudioGetAnim: %s not found", pseqgroup->name);
memset (pseqgroup->name, 0, sizeof(pseqgroup->name));
return NULL;
}
if(IDSEQGRPHEADER == LittleLong(*(unsigned *)buf)) //it's sequence group
{
byte *pin = (byte *)buf;
mstudioseqgroup_t *pseqhdr = (mstudioseqgroup_t *)pin;
paSequences = (cache_user_t *)Mem_Alloc(m_pSubModel->mempool, LittleLong(filesize));
m_pSubModel->submodels = (mmodel_t *)paSequences;
memcpy((struct cache_user_s *)&paSequences[pseqdesc->seqgroup], buf, LittleLong(filesize));
}
}
return (mstudioanim_t *)((byte *)paSequences[pseqdesc->seqgroup].data + pseqdesc->animindex);
}
/*
====================
StudioSetUpTransform
====================
*/
void R_StudioSetUpTransform ( void )
{
int i, j;
vec3_t angles, modelpos;
VectorCopy( m_pCurrentEntity->origin, modelpos );
// TODO: should really be stored with the entity instead of being reconstructed
// TODO: should use a look-up table
// TODO: could cache lazily, stored in the entity
angles[PITCH] = m_pCurrentEntity->angles[PITCH];
angles[YAW] = m_pCurrentEntity->angles[YAW];
angles[ROLL] = m_pCurrentEntity->angles[ROLL];
if (m_pCurrentEntity->movetype == MOVETYPE_STEP)
{
float f = 0;
float d;
mstudioseqdesc_t *pseqdesc;//acess to studio flags
// don't do it if the goalstarttime hasn't updated in a while.
pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + m_pCurrentEntity->sequence;
// 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 ( ( r_newrefdef.time < m_pCurrentEntity->animtime + 1.0f ) && ( m_pCurrentEntity->animtime != m_pCurrentEntity->prev.animtime ) )
{
f = (r_newrefdef.time - m_pCurrentEntity->animtime) / (m_pCurrentEntity->animtime - m_pCurrentEntity->prev.animtime);
//Msg("%4.2f %.2f %.2f\n", f, m_pCurrentEntity->animtime, r_newrefdef.time);
}
if (m_fDoInterp)
{
// ugly hack to interpolate angle, position. current is reached 0.1 seconds after being set
f = f - 1.0;
}
else
{
f = 0;
}
if (pseqdesc->motiontype & STUDIO_LX )//enable interpolation only for walk\run
for (i = 0; i < 3; i++) modelpos[i] += (m_pCurrentEntity->origin[i] - m_pCurrentEntity->prev.origin[i]) * f;
for (i = 0; i < 3; i++)
{
float ang1, ang2;
ang1 = m_pCurrentEntity->angles[i];
ang2 = m_pCurrentEntity->prev.angles[i];
d = ang1 - ang2;
if (d > 180) d -= 360;
else if (d < -180) d += 360;
angles[i] += d * f;
}
//Msg("%.3f \n", f );
}
else if ( m_pCurrentEntity->movetype != MOVETYPE_NONE )
{
VectorCopy( m_pCurrentEntity->angles, angles );
}
//Msg("%.0f %0.f %0.f\n", modelpos[0], modelpos[1], modelpos[2] );
//Msg("%.0f %0.f %0.f\n", angles[0], angles[1], angles[2] );
AngleMatrix (angles, m_protationmatrix);
m_protationmatrix[0][3] = modelpos[0];
m_protationmatrix[1][3] = modelpos[1];
m_protationmatrix[2][3] = modelpos[2];
if (m_pCurrentEntity->scale != 0)
{
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
m_protationmatrix[i][j] *= m_pCurrentEntity->scale;
}
}
/*
====================
StudioEstimateInterpolant
====================
*/
float R_StudioEstimateInterpolant( void )
{
float dadt = 1.0;
if ( m_fDoInterp && ( m_pCurrentEntity->animtime >= m_pCurrentEntity->prev.animtime + 0.01 ) )
{
dadt = (r_newrefdef.time - m_pCurrentEntity->animtime) / 0.1;
if (dadt > 2.0) dadt = 2.0;
}
return dadt;
}
/*
====================
StudioCalcRotations
====================
*/
void R_StudioCalcRotations ( float pos[][3], vec4_t *q, mstudioseqdesc_t *pseqdesc, mstudioanim_t *panim, float f )
{
int i;
int frame;
mstudiobone_t *pbone;
float s;
float adj[MAXSTUDIOCONTROLLERS];
float dadt;
if (f > pseqdesc->numframes - 1) f = 0; // bah, fix this bug with changing sequences too fast
else if ( f < -0.01 ) f = -0.01;
// 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
frame = (int)f;
// Msg("%d %.4f %.4f %.4f %.4f %d\n", m_pCurrentEntity->curstate.sequence, m_clTime, m_pCurrentEntity->animtime, m_pCurrentEntity->frame, f, frame );
// Msg( "%f %f %f\n", m_pCurrentEntity->angles[ROLL], m_pCurrentEntity->angles[PITCH], m_pCurrentEntity->angles[YAW] );
// Msg("frame %d %d\n", frame1, frame2 );
dadt = R_StudioEstimateInterpolant();
s = (f - frame);
// add in programtic controllers
pbone = (mstudiobone_t *)((byte *)m_pStudioHeader + m_pStudioHeader->boneindex);
R_StudioCalcBoneAdj( dadt, adj, m_pCurrentEntity->controller, m_pCurrentEntity->prev.controller, m_pCurrentEntity->mouth );
for (i = 0; i < m_pStudioHeader->numbones; i++, pbone++, panim++)
{
R_StudioCalcBoneQuaterion( frame, s, pbone, panim, adj, q[i] );
R_StudioCalcBonePosition( frame, s, pbone, panim, adj, pos[i] );
// if (0 && i == 0) ri.Con_Printf("%d %d %d %d\n", m_pCurrentEntity->sequence, frame, j, k );
}
if (pseqdesc->motiontype & STUDIO_X) pos[pseqdesc->motionbone][0] = 0.0;
if (pseqdesc->motiontype & STUDIO_Y) pos[pseqdesc->motionbone][1] = 0.0;
if (pseqdesc->motiontype & STUDIO_Z) pos[pseqdesc->motionbone][2] = 0.0;
s = 0 * ((1.0 - (f - (int)(f))) / (pseqdesc->numframes)) * m_pCurrentEntity->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];
}
/*
====================
Studio_FxTransform
====================
*/
void R_StudioFxTransform( entity_t *ent, matrix3x4 transform )
{
//TODO: add here some effects :)
}
/*
====================
StudioEstimateFrame
====================
*/
float R_StudioEstimateFrame( mstudioseqdesc_t *pseqdesc )
{
double dfdt, f;
if ( m_fDoInterp )
{
if ( r_newrefdef.time < m_pCurrentEntity->animtime ) dfdt = 0;
else dfdt = (r_newrefdef.time - m_pCurrentEntity->animtime) * m_pCurrentEntity->framerate * pseqdesc->fps;
}
else dfdt = 0;
if (pseqdesc->numframes <= 1) f = 0;
else f = (m_pCurrentEntity->frame * (pseqdesc->numframes - 1)) / 256.0;
f += dfdt;
if (pseqdesc->flags & STUDIO_LOOPING)
{
if (pseqdesc->numframes > 1) f -= (int)(f / (pseqdesc->numframes - 1)) * (pseqdesc->numframes - 1);
if (f < 0) f += (pseqdesc->numframes - 1);
}
else
{
if (f >= pseqdesc->numframes - 1.001) f = pseqdesc->numframes - 1.001;
if (f < 0.0) f = 0.0;
}
return f;
}
/*
====================
StudioSetupBones
====================
*/
void R_StudioSetupBones ( void )
{
int i;
double f;
mstudiobone_t *pbones;
mstudioseqdesc_t *pseqdesc;
mstudioanim_t *panim;
static float pos[MAXSTUDIOBONES][3];
static vec4_t q[MAXSTUDIOBONES];
matrix3x4 bonematrix;
static float pos2[MAXSTUDIOBONES][3];
static vec4_t q2[MAXSTUDIOBONES];
static float pos3[MAXSTUDIOBONES][3];
static vec4_t q3[MAXSTUDIOBONES];
static float pos4[MAXSTUDIOBONES][3];
static vec4_t q4[MAXSTUDIOBONES];
if (m_pCurrentEntity->sequence >= m_pStudioHeader->numseq) m_pCurrentEntity->sequence = 0;
pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + m_pCurrentEntity->sequence;
f = R_StudioEstimateFrame( pseqdesc );
if (m_pCurrentEntity->prev.frame > f)
{
//ri.Con_Printf(PRINT_ALL, "%f %f\n", m_pCurrentEntity->prev.frame, f );
}
panim = R_StudioGetAnim( m_pRenderModel, pseqdesc );
R_StudioCalcRotations( pos, q, pseqdesc, panim, f );
if (pseqdesc->numblends > 1)
{
float s;
float dadt;
panim += m_pStudioHeader->numbones;
R_StudioCalcRotations( pos2, q2, pseqdesc, panim, f );
dadt = R_StudioEstimateInterpolant();
s = (m_pCurrentEntity->blending[0] * dadt + m_pCurrentEntity->prev.blending[0] * (1.0 - dadt)) / 255.0;
R_StudioSlerpBones( q, pos, q2, pos2, s );
if (pseqdesc->numblends == 4)
{
panim += m_pStudioHeader->numbones;
R_StudioCalcRotations( pos3, q3, pseqdesc, panim, f );
panim += m_pStudioHeader->numbones;
R_StudioCalcRotations( pos4, q4, pseqdesc, panim, f );
s = (m_pCurrentEntity->blending[0] * dadt + m_pCurrentEntity->prev.blending[0] * (1.0 - dadt)) / 255.0;
R_StudioSlerpBones( q3, pos3, q4, pos4, s );
s = (m_pCurrentEntity->blending[1] * dadt + m_pCurrentEntity->prev.blending[1] * (1.0 - dadt)) / 255.0;
R_StudioSlerpBones( q, pos, q3, pos3, s );
}
}
if (m_fDoInterp && m_pCurrentEntity->prev.sequencetime && ( m_pCurrentEntity->prev.sequencetime + 0.2 > r_newrefdef.time) && ( m_pCurrentEntity->prev.sequence < m_pStudioHeader->numseq ))
{
// blend from last sequence
static float pos1b[MAXSTUDIOBONES][3];
static vec4_t q1b[MAXSTUDIOBONES];
float s;
pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + m_pCurrentEntity->prev.sequence;
panim = R_StudioGetAnim( m_pRenderModel, pseqdesc );
// clip prevframe
R_StudioCalcRotations( pos1b, q1b, pseqdesc, panim, m_pCurrentEntity->prev.frame );
if (pseqdesc->numblends > 1)
{
panim += m_pStudioHeader->numbones;
R_StudioCalcRotations( pos2, q2, pseqdesc, panim, m_pCurrentEntity->prev.frame );
s = (m_pCurrentEntity->prev.seqblending[0]) / 255.0;
R_StudioSlerpBones( q1b, pos1b, q2, pos2, s );
if (pseqdesc->numblends == 4)
{
panim += m_pStudioHeader->numbones;
R_StudioCalcRotations( pos3, q3, pseqdesc, panim, m_pCurrentEntity->prev.frame );
panim += m_pStudioHeader->numbones;
R_StudioCalcRotations( pos4, q4, pseqdesc, panim, m_pCurrentEntity->prev.frame );
s = (m_pCurrentEntity->prev.seqblending[0]) / 255.0;
R_StudioSlerpBones( q3, pos3, q4, pos4, s );
s = (m_pCurrentEntity->prev.seqblending[1]) / 255.0;
R_StudioSlerpBones( q1b, pos1b, q3, pos3, s );
}
}
s = 1.0 - (r_newrefdef.time - m_pCurrentEntity->prev.sequencetime) / 0.2;
R_StudioSlerpBones( q, pos, q1b, pos1b, s );
}
else
{
//ri.Con_Printf(PRINT_ALL, "prevframe = %4.2f\n", f);
m_pCurrentEntity->prev.frame = f;
}
pbones = (mstudiobone_t *)((byte *)m_pStudioHeader + m_pStudioHeader->boneindex);
for (i = 0; i < m_pStudioHeader->numbones; i++)
{
QuaternionMatrix( q[i], bonematrix );
bonematrix[0][3] = pos[i][0];
bonematrix[1][3] = pos[i][1];
bonematrix[2][3] = pos[i][2];
if (pbones[i].parent == -1)
{
R_ConcatTransforms (m_protationmatrix, bonematrix, m_pbonestransform[i]);
// MatrixCopy should be faster...
//ConcatTransforms (m_protationmatrix, bonematrix, m_plighttransform[i]);
MatrixCopy( m_pbonestransform[i], m_plighttransform[i] );
// Apply client-side effects to the transformation matrix
R_StudioFxTransform( m_pCurrentEntity, m_pbonestransform[i] );
}
else
{
R_ConcatTransforms (m_pbonestransform[pbones[i].parent], bonematrix, m_pbonestransform[i]);
R_ConcatTransforms (m_plighttransform[pbones[i].parent], bonematrix, m_plighttransform[i]);
}
}
}
/*
====================
StudioSaveBones
====================
*/
void R_StudioSaveBones( void )
{
int i;
mstudiobone_t *pbones = (mstudiobone_t *)((byte *)m_pStudioHeader + m_pStudioHeader->boneindex);
m_nCachedBones = m_pStudioHeader->numbones;
for (i = 0; i < m_pStudioHeader->numbones; i++)
{
strcpy( m_nCachedBoneNames[i], pbones[i].name );
MatrixCopy( m_pbonestransform[i], m_rgCachedBonesTransform[i] );
MatrixCopy( m_plighttransform[i], m_rgCachedLightTransform[i] );
}
}
void R_StudioRestoreBones( void )
{
}
/*
====================
StudioMergeBones
====================
*/
void R_StudioMergeBones ( model_t *m_pSubModel )
{
int i, j;
double f;
int do_hunt = true;
mstudiobone_t *pbones;
mstudioseqdesc_t *pseqdesc;
mstudioanim_t *panim;
matrix3x4 bonematrix;
static vec4_t q[MAXSTUDIOBONES];
static float pos[MAXSTUDIOBONES][3];
if (m_pCurrentEntity->sequence >= m_pStudioHeader->numseq) m_pCurrentEntity->sequence = 0;
pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + m_pCurrentEntity->sequence;
f = R_StudioEstimateFrame( pseqdesc );
//if (m_pCurrentEntity->prev.frame > f) ri.Con_Printf(PRINT_ALL, "%f %f\n", m_pCurrentEntity->prev.frame, f );
panim = R_StudioGetAnim( m_pSubModel, pseqdesc );
R_StudioCalcRotations( pos, q, pseqdesc, panim, f );
pbones = (mstudiobone_t *)((byte *)m_pStudioHeader + m_pStudioHeader->boneindex);
for (i = 0; i < m_pStudioHeader->numbones; i++)
{
for (j = 0; j < m_nCachedBones; j++)
{
if (stricmp(pbones[i].name, m_nCachedBoneNames[j]) == 0)
{
MatrixCopy( m_rgCachedBonesTransform[j], m_pbonestransform[i] );
MatrixCopy( m_rgCachedLightTransform[j], m_plighttransform[i] );
break;
}
}
if (j >= m_nCachedBones)
{
QuaternionMatrix( q[i], bonematrix );
bonematrix[0][3] = pos[i][0];
bonematrix[1][3] = pos[i][1];
bonematrix[2][3] = pos[i][2];
if (pbones[i].parent == -1)
{
R_ConcatTransforms (m_protationmatrix, bonematrix, m_pbonestransform[i]);
MatrixCopy( m_pbonestransform[i], m_plighttransform[i] );
// Apply client-side effects to the transformation matrix
R_StudioFxTransform( m_pCurrentEntity, m_pbonestransform[i] );
}
else
{
R_ConcatTransforms (m_pbonestransform[pbones[i].parent], bonematrix, m_pbonestransform[i]);
R_ConcatTransforms (m_plighttransform[pbones[i].parent], bonematrix, m_plighttransform[i]);
}
}
}
}
/*
====================
StudioCalcAttachments
====================
*/
void R_StudioCalcAttachments( void )
{
int i;
mstudioattachment_t *pattachment;
if ( m_pStudioHeader->numattachments > MAXSTUDIOATTACHMENTS )
{
Msg("Warning: Too many attachments on %s\n", m_pCurrentEntity->model->name );
m_pStudioHeader->numattachments = MAXSTUDIOATTACHMENTS;//reduce it
}
// calculate attachment points
pattachment = (mstudioattachment_t *)((byte *)m_pStudioHeader + m_pStudioHeader->attachmentindex);
for (i = 0; i < m_pStudioHeader->numattachments; i++)
{
VectorTransform( pattachment[i].org, m_plighttransform[pattachment[i].bone], m_pCurrentEntity->attachment[i] );
}
// copy attachments into global entity array
memcpy( m_pCurrentEntity->attachment, m_pCurrentEntity->attachment, sizeof( vec3_t ) * MAXSTUDIOATTACHMENTS );
}
static bool R_StudioComputeBBox( vec3_t *bbox )
{
vec3_t vectors[3];
entity_t *e = m_pCurrentEntity;
vec3_t mins, maxs, tmp, angles;
int i, seq = m_pCurrentEntity->sequence;
if(!R_ExtractBbox( seq, mins, maxs ))
return false;
//compute a full bounding box
for ( i = 0; i < 8; i++ )
{
if ( i & 1 ) tmp[0] = mins[0];
else tmp[0] = maxs[0];
if ( i & 2 ) tmp[1] = mins[1];
else tmp[1] = maxs[1];
if ( i & 4 ) tmp[2] = mins[2];
else tmp[2] = maxs[2];
VectorCopy( tmp, bbox[i] );
}
//rotate the bounding box
VectorCopy( e->angles, angles );
angles[PITCH] = -angles[PITCH];
AngleVectors( angles, vectors[0], vectors[1], vectors[2] );
for ( i = 0; i < 8; i++ )
{
VectorCopy( bbox[i], tmp );
bbox[i][0] = DotProduct( vectors[0], tmp );
bbox[i][1] = -DotProduct( vectors[1], tmp );
bbox[i][2] = DotProduct( vectors[2], tmp );
VectorAdd( e->origin, bbox[i], bbox[i] );
}
return true;
}
static bool R_StudioCheckBBox( void )
{
int i, j;
vec3_t bbox[8];
int aggregatemask = ~0;
if(m_pCurrentEntity->flags & RF_WEAPONMODEL)
return true;
if(!R_StudioComputeBBox( bbox ))
return false;
for ( i = 0; i < 8; i++ )
{
int mask = 0;
for ( j = 0; j < 4; j++ )
{
float dp = DotProduct( frustum[j].normal, bbox[i] );
if ( ( dp - frustum[j].dist ) < 0 ) mask |= ( 1 << j );
}
aggregatemask &= mask;
}
//Msg( "aggregatemask %d\n", aggregatemask );
if ( aggregatemask )
return false;
return true;
}
/*
====================
StudioSetupModel
====================
*/
void R_StudioSetupModel ( int bodypart )
{
int index;
if (bodypart > m_pStudioHeader->numbodyparts) bodypart = 0;
m_pBodyPart = (mstudiobodyparts_t *)((byte *)m_pStudioHeader + m_pStudioHeader->bodypartindex) + bodypart;
index = m_pCurrentEntity->body / m_pBodyPart->base;
index = index % m_pBodyPart->nummodels;
m_pSubModel = (mstudiomodel_t *)((byte *)m_pStudioHeader + m_pBodyPart->modelindex) + index;
}
void R_StudioSetupLighting( void )
{
int i;
mstudiobone_t *pbone;
//get light from floor
m_plightvec[0] = 0.0f;
m_plightvec[1] = 0.0f;
m_plightvec[2] = -1.0f;
R_LightPoint (m_pCurrentEntity->origin, m_plightcolor );
if ( m_pCurrentEntity->flags & RF_WEAPONMODEL )
r_lightlevel->value = bound(0, VectorLength(m_plightcolor) * 75.0f, 255);
// TODO: only do it for bones that actually have textures
for (i = 0; i < m_pStudioHeader->numbones; i++)
{
pbone = (mstudiobone_t *)((byte *)m_pStudioHeader + m_pStudioHeader->boneindex + i);
//if(pbone->flags & STUDIO_HAS_CHROME)
{
VectorIRotate( m_plightvec, m_pbonestransform[i], m_blightvec[i] );
}
}
}
void R_StudioLighting (float *lv, int bone, int flags, vec3_t normal)
{
float lightcos;
float shadelight = 192.0f;
float illum = 32.0f;//RF_MINLIGHT & RF_FULLBRIGHT
if (flags & STUDIO_NF_FLATSHADE)
{
illum += shadelight * 0.8f;
}
else
{
lightcos = DotProduct (normal, m_blightvec[bone]);// -1 colinear, 1 opposite
if (lightcos > 1.0) lightcos = 1;
illum += shadelight;
lightcos = (lightcos + 0.5f) / 1.5f;// do modified hemispherical lighting
if (lightcos > 0.0) illum -= shadelight * lightcos;
}
illum = bound( 0, illum, 255);
*lv = illum / 255.0; // Light from 0 to 1.0
}
void R_StudioSetupChrome(int *pchrome, int bone, vec3_t normal)
{
float n;
if (g_chromeage[bone] != m_pStudioModelCount)
{
// 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; // vector pointing at bone in world reference frame
VectorScale( m_pCurrentEntity->origin, -1, tmp );
tmp[0] += m_pbonestransform[bone][0][3];
tmp[1] += m_pbonestransform[bone][1][3];
tmp[2] += m_pbonestransform[bone][2][3];
VectorNormalize( tmp );
CrossProduct( tmp, vright, chromeupvec );
VectorNormalize( chromeupvec );
CrossProduct( tmp, chromeupvec, chromerightvec );
VectorNormalize( chromerightvec );
VectorIRotate( chromeupvec, m_pbonestransform[bone], g_chromeup[bone] );
VectorIRotate( chromerightvec, m_pbonestransform[bone], g_chromeright[bone] );
g_chromeage[bone] = m_pStudioModelCount;
}
// calc s coord
n = DotProduct( normal, g_chromeright[bone] );
pchrome[0] = (n + 1.0) * 32;// FIXME: make this a float
// calc t coord
n = DotProduct( normal, g_chromeup[bone] );
pchrome[1] = (n + 1.0) * 32;// FIXME: make this a float
}
bool R_AcceptPass( int flags, int pass )
{
if(pass == RENDERPASS_SOLID)
{
if(!flags) return true; // draw all
if(flags & STUDIO_NF_CHROME) return true; //chrome drawing once
if(flags & STUDIO_NF_ADDITIVE) return false; //draw it at second pass
if(flags & STUDIO_NF_TRANSPARENT) return true; // must be draw first always
}
if(pass == RENDERPASS_ALPHA)
{
//pass for blended ents
if(m_pCurrentEntity->flags & RF_TRANSLUCENT) return true;
if(!flags) return false; // skip all
if(flags & STUDIO_NF_TRANSPARENT) return false; // must be draw first always
if(flags & STUDIO_NF_ADDITIVE) return true; //draw it at second pass
if(flags & STUDIO_NF_CHROME) return false; //no need draw it again
}
return true;
}
void R_StudioDrawMeshes ( mstudiotexture_t * ptexture, short *pskinref, int pass )
{
int i, j;
float *av, *lv;
float lv_tmp;
vec3_t fbright = {0.95f, 0.95f, 0.95f};
vec3_t irgoggles = {0.95f, 0.0f, 0.0f};//predefined lightcolor
int flags;
mstudiomesh_t *pmesh = (mstudiomesh_t *)((byte *)m_pStudioHeader + m_pSubModel->meshindex);
byte *pnormbone = ((byte *)m_pStudioHeader + m_pSubModel->norminfoindex);
vec3_t *pstudionorms = (vec3_t *)((byte *)m_pStudioHeader + m_pSubModel->normindex);
lv = (float *)m_pvlightvalues;
for (j = 0; j < m_pSubModel->nummesh; j++)
{
flags = ptexture[pskinref[pmesh[j].skinref]].flags;
if(!R_AcceptPass(flags, pass )) continue;
for (i = 0; i < pmesh[j].numnorms; i++, lv += 3, pstudionorms++, pnormbone++)
{
R_StudioLighting (&lv_tmp, *pnormbone, flags, (float *)pstudionorms);
// FIXME: move this check out of the inner loop
if (flags & STUDIO_NF_CHROME)
R_StudioSetupChrome( g_chrome[(float (*)[3])lv - m_pvlightvalues], *pnormbone, (float *)pstudionorms );
VectorScale(m_plightcolor, lv_tmp, lv );
}
}
for (j = 0; j < m_pSubModel->nummesh; j++)
{
float s, t;
short *ptricmds;
pmesh = (mstudiomesh_t *)((byte *)m_pStudioHeader + m_pSubModel->meshindex) + j;
ptricmds = (short *)((byte *)m_pStudioHeader + pmesh->triindex);
flags = ptexture[pskinref[pmesh->skinref]].flags;
if(!R_AcceptPass(flags, pass )) continue;
s = 1.0/(float)ptexture[pskinref[pmesh->skinref]].width;
t = 1.0/(float)ptexture[pskinref[pmesh->skinref]].height;
GL_Bind( ptexture[pskinref[pmesh->skinref]].index );
qglShadeModel (GL_SMOOTH);
GL_TexEnv( GL_MODULATE );
while (i = *(ptricmds++))
{
if (i < 0)
{
qglBegin( GL_TRIANGLE_FAN );
i = -i;
}
else
{
qglBegin( GL_TRIANGLE_STRIP );
}
for(; i > 0; i--, ptricmds += 4)
{
if (flags & STUDIO_NF_CHROME)
qglTexCoord2f(g_chrome[ptricmds[1]][0]*s, g_chrome[ptricmds[1]][1]*t);
else qglTexCoord2f(ptricmds[2]*s, ptricmds[3]*t);
lv = m_pvlightvalues[ptricmds[1]];
if ( m_pCurrentEntity->flags & RF_FULLBRIGHT )
lv = &fbright[0];
if ( m_pCurrentEntity->flags & RF_MINLIGHT )//used for viewmodel only
VectorBound(0.1f, lv, 1.0f );
if ( r_newrefdef.rdflags & RDF_IRGOGGLES && m_pCurrentEntity->flags & RF_IR_VISIBLE)
lv = &irgoggles[0];
if (flags & STUDIO_NF_ADDITIVE)//additive is self-lighting texture
qglColor4f( 1.0f, 1.0f, 1.0f, 0.8f );
else if(m_pCurrentEntity->flags & RF_TRANSLUCENT)
qglColor4f( 1.0f, 1.0f, 1.0f, m_pCurrentEntity->alpha );
else qglColor4f( lv[0], lv[1], lv[2], 1.0f );//get light from floor
av = m_pxformverts[ptricmds[0]];
qglVertex3f(av[0], av[1], av[2]);
}
qglEnd();
}
}
}
void R_StudioDrawPoints ( void )
{
int i;
int m_skinnum = m_pCurrentEntity->skin;
byte *pvertbone;
byte *pnormbone;
vec3_t *pstudioverts;
vec3_t *pstudionorms;
mstudiotexture_t *ptexture;
short *pskinref;
pvertbone = ((byte *)m_pStudioHeader + m_pSubModel->vertinfoindex);
pnormbone = ((byte *)m_pStudioHeader + m_pSubModel->norminfoindex);
ptexture = (mstudiotexture_t *)((byte *)m_pTextureHeader + m_pTextureHeader->textureindex);
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);
for (i = 0; i < m_pSubModel->numverts; i++)
{
VectorTransform (pstudioverts[i], m_pbonestransform[pvertbone[i]], m_pxformverts[i]);
}
if (currententity->flags & RF_DEPTHHACK) // hack the depth range to prevent view model from poking into walls
qglDepthRange (gldepthmin, gldepthmin + 0.3 * (gldepthmax-gldepthmin));
if ( ( m_pCurrentEntity->flags & RF_WEAPONMODEL ) && ( r_lefthand->value == 1.0F ) )
{
qglMatrixMode( GL_PROJECTION );
qglPushMatrix();
qglLoadIdentity();
qglScalef( -1, 1, 1 );
qglPerspective( r_newrefdef.fov_y, ( float ) r_newrefdef.width / r_newrefdef.height, 4, 4096);
qglMatrixMode( GL_MODELVIEW );
qglCullFace( GL_BACK );
}
if(m_PassNum == RENDERPASS_SOLID && !(m_pCurrentEntity->flags & RF_TRANSLUCENT)) //setup for solid format
{
qglEnable (GL_ALPHA_TEST);
qglBlendFunc(GL_ZERO, GL_ZERO);
R_StudioDrawMeshes( ptexture, pskinref, m_PassNum );
qglColor4f( 1, 1, 1, 1 ); //reset color
qglDisable(GL_ALPHA_TEST);
}
if(m_PassNum == RENDERPASS_ALPHA) //setup for alpha format
{
qglEnable(GL_BLEND);
qglBlendFunc(GL_SRC_ALPHA, GL_ONE);
R_StudioDrawMeshes( ptexture, pskinref, m_PassNum );
qglColor4f( 1, 1, 1, 1 ); //reset color
qglDisable(GL_BLEND);
}
if (( m_pCurrentEntity->flags & RF_WEAPONMODEL ) && ( r_lefthand->value == 1.0F ) )
{
qglMatrixMode( GL_PROJECTION );
qglPopMatrix();
qglMatrixMode( GL_MODELVIEW );
qglCullFace( GL_FRONT );
}
}
void R_StudioDrawBones ( void )
{
int i;
mstudiobone_t *pbones = (mstudiobone_t *) ((byte *)m_pStudioHeader + m_pStudioHeader->boneindex);
qglDisable (GL_TEXTURE_2D);
qglDisable (GL_DEPTH_TEST);
for (i = 0; i < m_pStudioHeader->numbones; i++)
{
if (pbones[i].parent >= 0)
{
qglPointSize (3.0f);
qglColor3f (1, 0.7f, 0);
qglBegin (GL_LINES);
qglVertex3f (m_pbonestransform[pbones[i].parent][0][3], m_pbonestransform[pbones[i].parent][1][3], m_pbonestransform[pbones[i].parent][2][3]);
qglVertex3f (m_pbonestransform[i][0][3], m_pbonestransform[i][1][3], m_pbonestransform[i][2][3]);
qglEnd ();
qglColor3f (0, 0, 0.8f);
qglBegin (GL_POINTS);
if (pbones[pbones[i].parent].parent != -1)
qglVertex3f (m_pbonestransform[pbones[i].parent][0][3], m_pbonestransform[pbones[i].parent][1][3], m_pbonestransform[pbones[i].parent][2][3]);
qglVertex3f (m_pbonestransform[i][0][3], m_pbonestransform[i][1][3], m_pbonestransform[i][2][3]);
qglEnd ();
}
else
{
// draw parent bone node
qglPointSize (5.0f);
qglColor3f (0.8f, 0, 0);
qglBegin (GL_POINTS);
qglVertex3f (m_pbonestransform[i][0][3], m_pbonestransform[i][1][3], m_pbonestransform[i][2][3]);
qglEnd ();
}
}
qglPointSize (1.0f);
qglEnable (GL_DEPTH_TEST);
qglEnable (GL_TEXTURE_2D);
}
void R_StudioDrawHitboxes ( void )
{
int i, j;
qglDisable (GL_TEXTURE_2D);
qglDisable (GL_CULL_FACE);
if (m_pCurrentEntity->alpha < 1.0f ) qglDisable (GL_DEPTH_TEST);
else qglEnable (GL_DEPTH_TEST);
qglColor4f (1, 0, 0, 0.5f);
qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
qglEnable (GL_BLEND);
qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
for (i = 0; i < m_pStudioHeader->numhitboxes; i++)
{
mstudiobbox_t *pbboxes = (mstudiobbox_t *) ((byte *) m_pStudioHeader + m_pStudioHeader->hitboxindex);
vec3_t v[8], v2[8], bbmin, bbmax;
VectorCopy (pbboxes[i].bbmin, bbmin);
VectorCopy (pbboxes[i].bbmax, bbmax);
v[0][0] = bbmin[0];
v[0][1] = bbmax[1];
v[0][2] = bbmin[2];
v[1][0] = bbmin[0];
v[1][1] = bbmin[1];
v[1][2] = bbmin[2];
v[2][0] = bbmax[0];
v[2][1] = bbmax[1];
v[2][2] = bbmin[2];
v[3][0] = bbmax[0];
v[3][1] = bbmin[1];
v[3][2] = bbmin[2];
v[4][0] = bbmax[0];
v[4][1] = bbmax[1];
v[4][2] = bbmax[2];
v[5][0] = bbmax[0];
v[5][1] = bbmin[1];
v[5][2] = bbmax[2];
v[6][0] = bbmin[0];
v[6][1] = bbmax[1];
v[6][2] = bbmax[2];
v[7][0] = bbmin[0];
v[7][1] = bbmin[1];
v[7][2] = bbmax[2];
VectorTransform (v[0], m_pbonestransform[pbboxes[i].bone], v2[0]);
VectorTransform (v[1], m_pbonestransform[pbboxes[i].bone], v2[1]);
VectorTransform (v[2], m_pbonestransform[pbboxes[i].bone], v2[2]);
VectorTransform (v[3], m_pbonestransform[pbboxes[i].bone], v2[3]);
VectorTransform (v[4], m_pbonestransform[pbboxes[i].bone], v2[4]);
VectorTransform (v[5], m_pbonestransform[pbboxes[i].bone], v2[5]);
VectorTransform (v[6], m_pbonestransform[pbboxes[i].bone], v2[6]);
VectorTransform (v[7], m_pbonestransform[pbboxes[i].bone], v2[7]);
qglBegin (GL_QUAD_STRIP);
for (j = 0; j < 10; j++) qglVertex3fv (v2[j & 7]);
qglEnd ();
qglBegin (GL_QUAD_STRIP);
qglVertex3fv (v2[6]);
qglVertex3fv (v2[0]);
qglVertex3fv (v2[4]);
qglVertex3fv (v2[2]);
qglEnd ();
qglBegin (GL_QUAD_STRIP);
qglVertex3fv (v2[1]);
qglVertex3fv (v2[7]);
qglVertex3fv (v2[3]);
qglVertex3fv (v2[5]);
qglEnd ();
}
qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
qglEnable(GL_TEXTURE_2D);
qglEnable(GL_CULL_FACE);
qglEnable (GL_DEPTH_TEST);
}
void R_StudioDrawAttachments( void )
{
int i;
qglDisable (GL_TEXTURE_2D);
qglDisable (GL_CULL_FACE);
qglDisable (GL_DEPTH_TEST);
for (i = 0; i < m_pStudioHeader->numattachments; i++)
{
mstudioattachment_t *pattachments = (mstudioattachment_t *) ((byte *)m_pStudioHeader + m_pStudioHeader->attachmentindex);
vec3_t v[4];
VectorTransform (pattachments[i].org, m_pbonestransform[pattachments[i].bone], v[0]);
VectorTransform (pattachments[i].vectors[0], m_pbonestransform[pattachments[i].bone], v[1]);
VectorTransform (pattachments[i].vectors[1], m_pbonestransform[pattachments[i].bone], v[2]);
VectorTransform (pattachments[i].vectors[2], m_pbonestransform[pattachments[i].bone], v[3]);
qglBegin (GL_LINES);
qglColor3f (1, 0, 0);
qglVertex3fv (v[0]);
qglColor3f (1, 1, 1);
qglVertex3fv (v[1]);
qglColor3f (1, 0, 0);
qglVertex3fv (v[0]);
qglColor3f (1, 1, 1);
qglVertex3fv (v[2]);
qglColor3f (1, 0, 0);
qglVertex3fv (v[0]);
qglColor3f (1, 1, 1);
qglVertex3fv (v[3]);
qglEnd ();
qglPointSize (5.0f);
qglColor3f (0, 1, 0);
qglBegin (GL_POINTS);
qglVertex3fv (v[0]);
qglEnd ();
qglPointSize (1.0f);
}
qglEnable(GL_TEXTURE_2D);
qglEnable(GL_CULL_FACE);
qglEnable (GL_DEPTH_TEST);
}
void R_StudioDrawHulls ( void )
{
int i;
vec3_t bbox[8];
if(m_pCurrentEntity->flags & RF_WEAPONMODEL) return;
if(!R_StudioComputeBBox( bbox )) return;
qglDisable( GL_CULL_FACE );
qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
qglDisable( GL_TEXTURE_2D );
qglBegin( GL_TRIANGLE_STRIP );
for ( i = 0; i < 8; i++ )
{
qglVertex3fv( bbox[i] );
}
qglEnd();
qglEnable( GL_TEXTURE_2D );
qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
qglEnable( GL_CULL_FACE );
}
extern vec3_t lightspot;
void R_StudioDrawShadow ( void )
{
float an = m_pCurrentEntity->angles[1] / 180 * M_PI;
m_pshadevector[0] = cos(-an);
m_pshadevector[1] = sin(-an);
m_pshadevector[2] = 1;
VectorNormalize (m_pshadevector);
}
/*
====================
StudioRenderModel
====================
*/
void R_StudioRenderModel( void )
{
int i;
for (i = 0; i < m_pStudioHeader->numbodyparts ; i++)
{
R_StudioSetupModel( i );
R_StudioDrawPoints();
}
switch((int)r_drawentities->value)
{
case 2: R_StudioDrawBones(); break;
case 3: R_StudioDrawHitboxes(); break;
case 4: R_StudioDrawAttachments(); break;
case 5: R_StudioDrawHulls(); break;
}
}
/*
====================
StudioDrawModel
====================
*/
void R_DrawStudioModel( int passnum )
{
m_pCurrentEntity = currententity;
m_pRenderModel = m_pCurrentEntity->model;
m_pStudioHeader = m_pRenderModel->phdr;
m_pTextureHeader = m_pRenderModel->thdr;
m_PassNum = passnum;
R_StudioSetUpTransform();
// see if the bounding box lets us trivially reject, also sets
if (!R_StudioCheckBBox()) return;
m_pStudioModelCount++; // render data cache cookie
m_pxformverts = &g_xformverts[0];
m_pvlightvalues = &g_lightvalues[0];
//nothing to draw
if (m_pStudioHeader->numbodyparts == 0) return;
if ( m_pCurrentEntity->flags & RF_WEAPONMODEL && r_lefthand->value == 2) return;
if (m_pCurrentEntity->movetype == MOVETYPE_FOLLOW)
R_StudioMergeBones( m_pRenderModel );
else R_StudioSetupBones();
R_StudioSetupLighting( );
R_StudioSaveBones( );
//if (flags & STUDIO_EVENTS) R_StudioCalcAttachments();
R_StudioRenderModel();
if (m_pCurrentEntity->weaponmodel)
{
entity_t saveent = *m_pCurrentEntity;
model_t *pweaponmodel = m_pCurrentEntity->weaponmodel;
m_pStudioHeader = pweaponmodel->phdr;
R_StudioMergeBones( pweaponmodel);
R_StudioRenderModel();
R_StudioCalcAttachments();
*m_pCurrentEntity = saveent;
}
GL_TexEnv( GL_REPLACE );
qglShadeModel (GL_FLAT);
if (m_pCurrentEntity->flags & RF_DEPTHHACK)
qglDepthRange (gldepthmin, gldepthmax);
if (gl_shadows->value && !(m_pCurrentEntity->flags & (RF_TRANSLUCENT | RF_WEAPONMODEL)))
{
qglPushMatrix();
R_RotateForEntity(m_pCurrentEntity);
qglDisable (GL_TEXTURE_2D);
qglEnable (GL_BLEND);
qglColor4f (0, 0, 0, 0.5f );
R_StudioDrawShadow();
qglEnable (GL_TEXTURE_2D);
qglDisable (GL_BLEND);
qglPopMatrix ();
}
qglColor4f (1,1,1,1);
}