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/engine/common/mod_studio.c

1008 lines
27 KiB
C
Raw Normal View History

2011-05-09 22:00:00 +02:00
/*
sv_studio.c - server studio utilities
Copyright (C) 2010 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
2010-08-25 22:00:00 +02:00
#include "common.h"
#include "server.h"
2010-09-10 22:00:00 +02:00
#include "studio.h"
2010-11-15 22:00:00 +01:00
#include "r_studioint.h"
2011-03-08 22:00:00 +01:00
#include "library.h"
2010-08-25 22:00:00 +02:00
2012-02-08 21:00:00 +01:00
typedef int (*STUDIOAPI)( int, sv_blending_interface_t**, server_studio_api_t*, float (*transform)[3][4], float (*bones)[MAXSTUDIOBONES][3][4] );
2010-11-15 22:00:00 +01:00
2012-02-11 21:00:00 +01:00
typedef struct mstudiocache_s
{
float frame;
int sequence;
vec3_t angles;
vec3_t origin;
vec3_t size;
byte controler[4];
byte blending[2];
model_t *model;
uint current_hull;
uint current_plane;
uint numhitboxes;
} mstudiocache_t;
#define STUDIO_CACHESIZE 16
#define STUDIO_CACHEMASK (STUDIO_CACHESIZE - 1)
// trace global variables
2012-12-22 21:00:00 +01:00
static sv_blending_interface_t *pBlendAPI = NULL;
2012-02-11 21:00:00 +01:00
static studiohdr_t *mod_studiohdr;
static matrix3x4 studio_transform;
static hull_t cache_hull[MAXSTUDIOBONES];
static hull_t studio_hull[MAXSTUDIOBONES];
static matrix3x4 studio_bones[MAXSTUDIOBONES];
static uint studio_hull_hitgroup[MAXSTUDIOBONES];
static uint cache_hull_hitgroup[MAXSTUDIOBONES];
static mstudiocache_t cache_studio[STUDIO_CACHESIZE];
static dclipnode_t studio_clipnodes[6];
static mplane_t studio_planes[768];
static mplane_t cache_planes[768];
// current cache state
static int cache_current;
static int cache_current_hull;
static int cache_current_plane;
2010-08-25 22:00:00 +02:00
/*
====================
2012-02-11 21:00:00 +01:00
Mod_InitStudioHull
2010-08-25 22:00:00 +02:00
====================
*/
2012-02-11 21:00:00 +01:00
void Mod_InitStudioHull( void )
2010-08-25 22:00:00 +02:00
{
int i, side;
2012-02-11 21:00:00 +01:00
if( studio_hull[0].planes != NULL )
return; // already initailized
2010-08-25 22:00:00 +02:00
for( i = 0; i < 6; i++ )
{
2012-02-11 21:00:00 +01:00
studio_clipnodes[i].planenum = i;
2010-08-25 22:00:00 +02:00
side = i & 1;
2012-02-11 21:00:00 +01:00
studio_clipnodes[i].children[side] = CONTENTS_EMPTY;
if( i != 5 ) studio_clipnodes[i].children[side^1] = i + 1;
else studio_clipnodes[i].children[side^1] = CONTENTS_SOLID;
}
2010-08-25 22:00:00 +02:00
2012-02-11 21:00:00 +01:00
for( i = 0; i < MAXSTUDIOBONES; i++ )
{
studio_hull[i].clipnodes = studio_clipnodes;
studio_hull[i].planes = &studio_planes[i*6];
studio_hull[i].firstclipnode = 0;
studio_hull[i].lastclipnode = 5;
}
}
/*
===============================================================================
STUDIO MODELS CACHE
===============================================================================
*/
/*
====================
ClearStudioCache
====================
*/
void Mod_ClearStudioCache( void )
{
Q_memset( cache_studio, 0, sizeof( cache_studio ));
cache_current_hull = cache_current_plane = 0;
cache_current = 0;
}
/*
====================
AddToStudioCache
====================
*/
void Mod_AddToStudioCache( float frame, int sequence, vec3_t angles, vec3_t origin, vec3_t size, byte *pcontroller, byte *pblending, model_t *model, hull_t *hull, int numhitboxes )
{
mstudiocache_t *pCache;
if( numhitboxes + cache_current_hull >= MAXSTUDIOBONES )
Mod_ClearStudioCache();
cache_current++;
pCache = &cache_studio[cache_current & STUDIO_CACHEMASK];
pCache->frame = frame;
pCache->sequence = sequence;
VectorCopy( angles, pCache->angles );
VectorCopy( origin, pCache->origin );
VectorCopy( size, pCache->size );
Q_memcpy( pCache->controler, pcontroller, 4 );
Q_memcpy( pCache->blending, pblending, 2 );
pCache->model = model;
pCache->current_hull = cache_current_hull;
pCache->current_plane = cache_current_plane;
Q_memcpy( &cache_hull[cache_current_hull], hull, numhitboxes * sizeof( hull_t ));
Q_memcpy( &cache_planes[cache_current_plane], studio_planes, numhitboxes * sizeof( mplane_t ) * 6 );
Q_memcpy( &cache_hull_hitgroup[cache_current_hull], studio_hull_hitgroup, numhitboxes * sizeof( uint ));
cache_current_hull += numhitboxes;
cache_current_plane += numhitboxes * 6;
pCache->numhitboxes = numhitboxes;
}
/*
====================
CheckStudioCache
====================
*/
mstudiocache_t *Mod_CheckStudioCache( model_t *model, float frame, int sequence, vec3_t angles, vec3_t origin, vec3_t size, byte *pcontroller, byte *pblending )
{
mstudiocache_t *pCache;
int i;
for( i = 0; i < STUDIO_CACHESIZE; i++ )
{
pCache = &cache_studio[(cache_current - i) & STUDIO_CACHEMASK];
if( pCache->model == model && pCache->frame == frame && pCache->sequence == sequence &&
VectorCompare( angles, pCache->angles ) && VectorCompare( origin, pCache->origin ) && VectorCompare( size, pCache->size ) &&
2012-12-21 21:00:00 +01:00
!Q_memcmp( pCache->controler, pcontroller, 4 ) && !Q_memcmp( pCache->blending, pblending, 2 ))
2010-08-25 22:00:00 +02:00
{
2012-02-11 21:00:00 +01:00
return pCache;
2010-08-25 22:00:00 +02:00
}
2012-02-11 21:00:00 +01:00
}
return NULL;
2010-08-25 22:00:00 +02:00
}
2012-02-11 21:00:00 +01:00
/*
===============================================================================
STUDIO MODELS TRACING
===============================================================================
*/
2010-08-25 22:00:00 +02:00
/*
====================
2012-02-11 21:00:00 +01:00
SetStudioHullPlane
2010-08-25 22:00:00 +02:00
====================
*/
2012-02-11 21:00:00 +01:00
void Mod_SetStudioHullPlane( mplane_t *pl, int bone, int axis, float offset )
2010-08-25 22:00:00 +02:00
{
2012-02-11 21:00:00 +01:00
pl->type = 5;
pl->normal[0] = studio_bones[bone][0][axis];
pl->normal[1] = studio_bones[bone][1][axis];
pl->normal[2] = studio_bones[bone][2][axis];
pl->dist = (pl->normal[0] * studio_bones[bone][0][3]) + (pl->normal[1] * studio_bones[bone][1][3]) + (pl->normal[2] * studio_bones[bone][2][3]) + offset;
}
/*
====================
HullForStudio
2012-12-22 21:00:00 +01:00
NOTE: pEdict may be NULL
2012-02-11 21:00:00 +01:00
====================
*/
hull_t *Mod_HullForStudio( model_t *model, float frame, int sequence, vec3_t angles, vec3_t origin, vec3_t size, byte *pcontroller, byte *pblending, int *numhitboxes, edict_t *pEdict )
{
vec3_t angles2;
mstudiocache_t *bonecache;
mstudiobbox_t *phitbox;
int i, j;
2014-05-17 22:00:00 +02:00
ASSERT( numhitboxes );
*numhitboxes = 0; // assume error
2012-02-11 21:00:00 +01:00
if( mod_studiocache->integer )
{
bonecache = Mod_CheckStudioCache( model, frame, sequence, angles, origin, size, pcontroller, pblending );
if( bonecache != NULL )
{
Q_memcpy( studio_planes, &cache_planes[bonecache->current_plane], bonecache->numhitboxes * sizeof( mplane_t ) * 6 );
Q_memcpy( studio_hull_hitgroup, &cache_hull_hitgroup[bonecache->current_hull], bonecache->numhitboxes * sizeof( uint ));
Q_memcpy( studio_hull, &cache_hull[bonecache->current_hull], bonecache->numhitboxes * sizeof( hull_t ));
*numhitboxes = bonecache->numhitboxes;
return studio_hull;
}
}
mod_studiohdr = Mod_Extradata( model );
if( !mod_studiohdr ) return NULL; // probably not a studiomodel
2012-12-22 21:00:00 +01:00
ASSERT( pBlendAPI != NULL );
2012-02-11 21:00:00 +01:00
VectorCopy( angles, angles2 );
2013-09-03 22:00:00 +02:00
if( !( host.features & ENGINE_COMPENSATE_QUAKE_BUG ))
angles2[PITCH] = -angles2[PITCH]; // stupid quake bug
2012-02-11 21:00:00 +01:00
2014-05-08 22:00:00 +02:00
pBlendAPI->SV_StudioSetupBones( model, frame, sequence, angles2, origin, pcontroller, pblending, -1, pEdict );
2012-02-11 21:00:00 +01:00
phitbox = (mstudiobbox_t *)((byte *)mod_studiohdr + mod_studiohdr->hitboxindex);
for( i = j = 0; i < mod_studiohdr->numhitboxes; i++, j += 6 )
{
studio_hull_hitgroup[i] = phitbox[i].group;
Mod_SetStudioHullPlane( &studio_planes[j+0], phitbox[i].bone, 0, phitbox[i].bbmax[0] );
Mod_SetStudioHullPlane( &studio_planes[j+1], phitbox[i].bone, 0, phitbox[i].bbmin[0] );
Mod_SetStudioHullPlane( &studio_planes[j+2], phitbox[i].bone, 1, phitbox[i].bbmax[1] );
Mod_SetStudioHullPlane( &studio_planes[j+3], phitbox[i].bone, 1, phitbox[i].bbmin[1] );
Mod_SetStudioHullPlane( &studio_planes[j+4], phitbox[i].bone, 2, phitbox[i].bbmax[2] );
Mod_SetStudioHullPlane( &studio_planes[j+5], phitbox[i].bone, 2, phitbox[i].bbmin[2] );
studio_planes[j+0].dist += DotProductAbs( studio_planes[j+0].normal, size );
studio_planes[j+1].dist -= DotProductAbs( studio_planes[j+1].normal, size );
studio_planes[j+2].dist += DotProductAbs( studio_planes[j+2].normal, size );
studio_planes[j+3].dist -= DotProductAbs( studio_planes[j+3].normal, size );
studio_planes[j+4].dist += DotProductAbs( studio_planes[j+4].normal, size );
studio_planes[j+5].dist -= DotProductAbs( studio_planes[j+5].normal, size );
}
2012-12-21 21:00:00 +01:00
// tell trace code about hitbox count
2012-02-11 21:00:00 +01:00
*numhitboxes = mod_studiohdr->numhitboxes;
if( mod_studiocache->integer )
{
Mod_AddToStudioCache( frame, sequence, angles, origin, size, pcontroller, pblending, model, studio_hull, *numhitboxes );
}
return studio_hull;
2010-08-25 22:00:00 +02:00
}
/*
===============================================================================
2012-02-11 21:00:00 +01:00
STUDIO MODELS SETUP BONES
2010-08-25 22:00:00 +02:00
===============================================================================
*/
/*
====================
StudioCalcBoneAdj
====================
*/
2012-02-11 21:00:00 +01:00
static void Mod_StudioCalcBoneAdj( float *adj, const byte *pcontroller )
2010-08-25 22:00:00 +02:00
{
int i, j;
float value;
mstudiobonecontroller_t *pbonecontroller;
2012-02-11 21:00:00 +01:00
pbonecontroller = (mstudiobonecontroller_t *)((byte *)mod_studiohdr + mod_studiohdr->bonecontrollerindex);
2010-08-25 22:00:00 +02:00
2012-02-11 21:00:00 +01:00
for( j = 0; j < mod_studiohdr->numbonecontrollers; j++ )
2010-08-25 22:00:00 +02:00
{
i = pbonecontroller[j].index;
2012-12-21 21:00:00 +01:00
if( i == STUDIO_MOUTH )
continue; // ignore mouth
2010-08-25 22:00:00 +02:00
if( i <= MAXSTUDIOCONTROLLERS )
{
// check for 360% wrapping
if( pbonecontroller[j].type & STUDIO_RLOOP )
{
2011-03-01 22:00:00 +01:00
value = pcontroller[i] * (360.0f / 256.0f) + pbonecontroller[j].start;
2010-08-25 22:00:00 +02:00
}
else
{
2011-02-20 22:00:00 +01:00
value = pcontroller[i] / 255.0f;
2012-02-11 21:00:00 +01:00
value = bound( 0.0f, value, 1.0f );
2010-08-25 22:00:00 +02:00
value = (1.0f - value) * pbonecontroller[j].start + value * pbonecontroller[j].end;
}
}
switch( pbonecontroller[j].type & STUDIO_TYPES )
{
case STUDIO_XR:
case STUDIO_YR:
case STUDIO_ZR:
adj[j] = value * (M_PI / 180.0f);
break;
case STUDIO_X:
case STUDIO_Y:
case STUDIO_Z:
adj[j] = value;
break;
}
}
}
/*
====================
StudioCalcBoneQuaterion
====================
*/
2012-02-11 21:00:00 +01:00
static void Mod_StudioCalcBoneQuaterion( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, float *q )
2010-08-25 22:00:00 +02:00
{
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
====================
*/
2012-02-11 21:00:00 +01:00
static void Mod_StudioCalcBonePosition( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, float *pos )
2010-08-25 22:00:00 +02:00
{
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
====================
*/
2012-02-11 21:00:00 +01:00
static void Mod_StudioCalcRotations( int boneused[], int numbones, const byte *pcontroller, float pos[][3], vec4_t *q, mstudioseqdesc_t *pseqdesc, mstudioanim_t *panim, float f )
2010-08-25 22:00:00 +02:00
{
2010-11-15 22:00:00 +01:00
int i, j, frame;
2010-08-25 22:00:00 +02:00
mstudiobone_t *pbone;
float adj[MAXSTUDIOCONTROLLERS];
2010-11-15 22:00:00 +01:00
float s;
2010-08-25 22:00:00 +02:00
if( f > pseqdesc->numframes - 1 )
2012-12-21 21:00:00 +01:00
f = 0.0f;
2010-08-25 22:00:00 +02:00
else if( f < -0.01f )
f = -0.01f;
frame = (int)f;
s = (f - frame);
// add in programtic controllers
2012-02-11 21:00:00 +01:00
pbone = (mstudiobone_t *)((byte *)mod_studiohdr + mod_studiohdr->boneindex);
2010-08-25 22:00:00 +02:00
2012-02-11 21:00:00 +01:00
Mod_StudioCalcBoneAdj( adj, pcontroller );
2010-08-25 22:00:00 +02:00
2010-11-15 22:00:00 +01:00
for( j = numbones - 1; j >= 0; j-- )
2010-08-25 22:00:00 +02:00
{
2010-11-15 22:00:00 +01:00
i = boneused[j];
2012-02-11 21:00:00 +01:00
Mod_StudioCalcBoneQuaterion( frame, s, &pbone[i], &panim[i], adj, q[i] );
Mod_StudioCalcBonePosition( frame, s, &pbone[i], &panim[i], adj, pos[i] );
2010-08-25 22:00:00 +02:00
}
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;
}
/*
====================
StudioEstimateFrame
====================
*/
2012-02-11 21:00:00 +01:00
static float Mod_StudioEstimateFrame( float frame, mstudioseqdesc_t *pseqdesc )
2010-08-25 22:00:00 +02:00
{
double f;
if( pseqdesc->numframes <= 1 )
2012-12-21 21:00:00 +01:00
f = 0.0f;
else f = ( frame * ( pseqdesc->numframes - 1 )) / 256.0f;
2010-08-25 22:00:00 +02:00
if( pseqdesc->flags & STUDIO_LOOPING )
{
if( pseqdesc->numframes > 1 )
f -= (int)(f / (pseqdesc->numframes - 1)) * (pseqdesc->numframes - 1);
2012-12-21 21:00:00 +01:00
if( f < 0.0f ) f += (pseqdesc->numframes - 1);
2010-08-25 22:00:00 +02:00
}
else
{
2012-12-21 21:00:00 +01:00
if( f >= pseqdesc->numframes - 1.001f )
f = pseqdesc->numframes - 1.001f;
if( f < 0.0f ) f = 0.0f;
2010-08-25 22:00:00 +02:00
}
2012-12-21 21:00:00 +01:00
2010-08-25 22:00:00 +02:00
return f;
}
/*
====================
StudioSlerpBones
====================
*/
2012-02-11 21:00:00 +01:00
static void Mod_StudioSlerpBones( vec4_t q1[], float pos1[][3], vec4_t q2[], float pos2[][3], float s )
2010-08-25 22:00:00 +02:00
{
int i;
vec4_t q3;
float s1;
s = bound( 0.0f, s, 1.0f );
s1 = 1.0f - s;
2012-02-11 21:00:00 +01:00
for( i = 0; i < mod_studiohdr->numbones; i++ )
2010-08-25 22:00:00 +02:00
{
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
====================
*/
2012-02-11 21:00:00 +01:00
static mstudioanim_t *Mod_StudioGetAnim( model_t *m_pSubModel, mstudioseqdesc_t *pseqdesc )
2010-08-25 22:00:00 +02:00
{
mstudioseqgroup_t *pseqgroup;
cache_user_t *paSequences;
size_t filesize;
byte *buf;
2012-02-11 21:00:00 +01:00
pseqgroup = (mstudioseqgroup_t *)((byte *)mod_studiohdr + mod_studiohdr->seqgroupindex) + pseqdesc->seqgroup;
2010-08-25 22:00:00 +02:00
if( pseqdesc->seqgroup == 0 )
2012-02-11 21:00:00 +01:00
return (mstudioanim_t *)((byte *)mod_studiohdr + pseqgroup->data + pseqdesc->animindex);
2010-08-25 22:00:00 +02:00
paSequences = (cache_user_t *)m_pSubModel->submodels;
if( paSequences == NULL )
{
2010-12-19 22:00:00 +01:00
paSequences = (cache_user_t *)Mem_Alloc( com_studiocache, MAXSTUDIOGROUPS * sizeof( cache_user_t ));
2010-08-25 22:00:00 +02:00
m_pSubModel->submodels = (void *)paSequences;
}
// check for already loaded
2010-12-17 22:00:00 +01:00
if( !Mod_CacheCheck(( cache_user_t *)&( paSequences[pseqdesc->seqgroup] )))
2010-08-25 22:00:00 +02:00
{
string filepath, modelname, modelpath;
FS_FileBase( m_pSubModel->name, modelname );
FS_ExtractFilePath( m_pSubModel->name, modelpath );
2011-03-09 22:00:00 +01:00
Q_snprintf( filepath, sizeof( filepath ), "%s/%s%i%i.mdl", modelpath, modelname, pseqdesc->seqgroup / 10, pseqdesc->seqgroup % 10 );
2010-08-25 22:00:00 +02:00
2011-03-08 22:00:00 +01:00
buf = FS_LoadFile( filepath, &filesize, false );
2010-12-17 22:00:00 +01:00
if( !buf || !filesize ) Host_Error( "StudioGetAnim: can't load %s\n", filepath );
2010-08-25 22:00:00 +02:00
if( IDSEQGRPHEADER != *(uint *)buf )
2010-12-17 22:00:00 +01:00
Host_Error( "StudioGetAnim: %s is corrupted\n", filepath );
2010-08-25 22:00:00 +02:00
2010-12-17 22:00:00 +01:00
MsgDev( D_INFO, "loading: %s\n", filepath );
paSequences[pseqdesc->seqgroup].data = Mem_Alloc( com_studiocache, filesize );
2011-03-10 22:00:00 +01:00
Q_memcpy( paSequences[pseqdesc->seqgroup].data, buf, filesize );
2010-08-25 22:00:00 +02:00
Mem_Free( buf );
}
return (mstudioanim_t *)((byte *)paSequences[pseqdesc->seqgroup].data + pseqdesc->animindex);
}
/*
====================
StudioSetupBones
2010-11-15 22:00:00 +01:00
NOTE: pEdict is unused
2010-08-25 22:00:00 +02:00
====================
*/
2010-11-15 22:00:00 +01:00
static void SV_StudioSetupBones( model_t *pModel, float frame, int sequence, const vec3_t angles, const vec3_t origin,
2014-05-08 22:00:00 +02:00
const byte *pcontroller, const byte *pblending, int iBone, const edict_t *pEdict )
2010-08-25 22:00:00 +02:00
{
2011-10-01 22:00:00 +02:00
int i, j, numbones = 0;
2010-11-15 22:00:00 +01:00
int boneused[MAXSTUDIOBONES];
2010-08-25 22:00:00 +02:00
double f;
mstudiobone_t *pbones;
mstudioseqdesc_t *pseqdesc;
mstudioanim_t *panim;
static float pos[MAXSTUDIOBONES][3];
static vec4_t q[MAXSTUDIOBONES];
2010-12-22 22:00:00 +01:00
matrix3x4 bonematrix;
2010-08-25 22:00:00 +02:00
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];
2012-02-11 21:00:00 +01:00
if( sequence < 0 || sequence >= mod_studiohdr->numseq )
2011-10-01 22:00:00 +02:00
{
2012-02-11 21:00:00 +01:00
MsgDev( D_WARN, "SV_StudioSetupBones: sequence %i/%i out of range for model %s\n", sequence, mod_studiohdr->numseq, mod_studiohdr->name );
2011-10-01 22:00:00 +02:00
sequence = 0;
}
2012-02-11 21:00:00 +01:00
pseqdesc = (mstudioseqdesc_t *)((byte *)mod_studiohdr + mod_studiohdr->seqindex) + sequence;
pbones = (mstudiobone_t *)((byte *)mod_studiohdr + mod_studiohdr->boneindex);
panim = Mod_StudioGetAnim( pModel, pseqdesc );
2010-08-25 22:00:00 +02:00
2012-02-11 21:00:00 +01:00
if( iBone < -1 || iBone >= mod_studiohdr->numbones )
2010-11-15 22:00:00 +01:00
iBone = 0;
2010-08-25 22:00:00 +02:00
2010-11-15 22:00:00 +01:00
if( iBone == -1 )
{
2012-02-11 21:00:00 +01:00
numbones = mod_studiohdr->numbones;
for( i = 0; i < mod_studiohdr->numbones; i++ )
2010-11-15 22:00:00 +01:00
boneused[(numbones - i) - 1] = i;
}
else
{
2011-10-01 22:00:00 +02:00
// only the parent bones
2010-11-15 22:00:00 +01:00
for( i = iBone; i != -1; i = pbones[i].parent )
2011-10-01 22:00:00 +02:00
boneused[numbones++] = i;
2010-11-15 22:00:00 +01:00
}
2012-02-11 21:00:00 +01:00
f = Mod_StudioEstimateFrame( frame, pseqdesc );
Mod_StudioCalcRotations( boneused, numbones, pcontroller, pos, q, pseqdesc, panim, f );
2010-08-25 22:00:00 +02:00
if( pseqdesc->numblends > 1 )
{
float s;
2012-02-11 21:00:00 +01:00
panim += mod_studiohdr->numbones;
Mod_StudioCalcRotations( boneused, numbones, pcontroller, pos2, q2, pseqdesc, panim, f );
2010-08-25 22:00:00 +02:00
2010-11-15 22:00:00 +01:00
s = (float)pblending[0] / 255.0f;
2010-08-25 22:00:00 +02:00
2012-02-11 21:00:00 +01:00
Mod_StudioSlerpBones( q, pos, q2, pos2, s );
2010-08-25 22:00:00 +02:00
if( pseqdesc->numblends == 4 )
{
2012-02-11 21:00:00 +01:00
panim += mod_studiohdr->numbones;
Mod_StudioCalcRotations( boneused, numbones, pcontroller, pos3, q3, pseqdesc, panim, f );
2010-08-25 22:00:00 +02:00
2012-02-11 21:00:00 +01:00
panim += mod_studiohdr->numbones;
Mod_StudioCalcRotations( boneused, numbones, pcontroller, pos4, q4, pseqdesc, panim, f );
2010-08-25 22:00:00 +02:00
2010-11-15 22:00:00 +01:00
s = (float)pblending[0] / 255.0f;
2012-02-11 21:00:00 +01:00
Mod_StudioSlerpBones( q3, pos3, q4, pos4, s );
2010-08-25 22:00:00 +02:00
2010-11-15 22:00:00 +01:00
s = (float)pblending[1] / 255.0f;
2012-02-11 21:00:00 +01:00
Mod_StudioSlerpBones( q, pos, q3, pos3, s );
2010-08-25 22:00:00 +02:00
}
}
2012-02-11 21:00:00 +01:00
Matrix3x4_CreateFromEntity( studio_transform, angles, origin, 1.0f );
2011-04-05 22:00:00 +02:00
2010-11-15 22:00:00 +01:00
for( j = numbones - 1; j >= 0; j-- )
2010-08-25 22:00:00 +02:00
{
2010-11-15 22:00:00 +01:00
i = boneused[j];
2011-04-05 22:00:00 +02:00
2010-12-22 22:00:00 +01:00
Matrix3x4_FromOriginQuat( bonematrix, q[i], pos[i] );
2010-08-25 22:00:00 +02:00
if( pbones[i].parent == -1 )
2012-02-11 21:00:00 +01:00
Matrix3x4_ConcatTransforms( studio_bones[i], studio_transform, bonematrix );
else Matrix3x4_ConcatTransforms( studio_bones[i], studio_bones[pbones[i].parent], bonematrix );
2010-08-25 22:00:00 +02:00
}
}
2012-02-11 21:00:00 +01:00
/*
====================
StudioGetAttachment
====================
*/
2012-02-12 21:00:00 +01:00
void Mod_StudioGetAttachment( const edict_t *e, int iAttachment, float *origin, float *angles )
2010-08-25 22:00:00 +02:00
{
2010-11-15 22:00:00 +01:00
mstudioattachment_t *pAtt;
2012-02-12 21:00:00 +01:00
vec3_t angles2;
model_t *mod;
2010-11-15 22:00:00 +01:00
2012-02-12 21:00:00 +01:00
mod = Mod_Handle( e->v.modelindex );
mod_studiohdr = (studiohdr_t *)Mod_Extradata( mod );
if( !mod_studiohdr ) return;
2010-11-15 22:00:00 +01:00
2012-02-11 21:00:00 +01:00
if( mod_studiohdr->numattachments <= 0 )
2010-08-25 22:00:00 +02:00
return;
2010-11-15 22:00:00 +01:00
2012-12-22 21:00:00 +01:00
ASSERT( pBlendAPI != NULL );
2012-02-11 21:00:00 +01:00
if( mod_studiohdr->numattachments > MAXSTUDIOATTACHMENTS )
2010-11-15 22:00:00 +01:00
{
2012-02-11 21:00:00 +01:00
mod_studiohdr->numattachments = MAXSTUDIOATTACHMENTS; // reduce it
MsgDev( D_WARN, "SV_StudioGetAttahment: too many attachments on %s\n", mod_studiohdr->name );
2010-08-25 22:00:00 +02:00
}
2010-11-15 22:00:00 +01:00
2012-02-11 21:00:00 +01:00
iAttachment = bound( 0, iAttachment, mod_studiohdr->numattachments );
2010-11-15 22:00:00 +01:00
// calculate attachment origin and angles
2012-02-11 21:00:00 +01:00
pAtt = (mstudioattachment_t *)((byte *)mod_studiohdr + mod_studiohdr->attachmentindex);
2010-11-15 22:00:00 +01:00
2012-02-12 21:00:00 +01:00
VectorCopy( e->v.angles, angles2 );
2013-09-03 22:00:00 +02:00
if( !( host.features & ENGINE_COMPENSATE_QUAKE_BUG ))
angles2[PITCH] = -angles2[PITCH];
2012-02-12 21:00:00 +01:00
pBlendAPI->SV_StudioSetupBones( mod, e->v.frame, e->v.sequence, angles2, e->v.origin,
2014-05-08 22:00:00 +02:00
e->v.controller, e->v.blending, pAtt[iAttachment].bone, e );
2010-11-15 22:00:00 +01:00
// compute pos and angles
2012-02-12 21:00:00 +01:00
if( origin != NULL )
Matrix3x4_VectorTransform( studio_bones[pAtt[iAttachment].bone], pAtt[iAttachment].org, origin );
2011-02-05 22:00:00 +01:00
2012-02-12 21:00:00 +01:00
if( sv_allow_studio_attachment_angles->integer && origin != NULL && angles != NULL )
2011-02-05 22:00:00 +01:00
{
2012-02-12 21:00:00 +01:00
vec3_t forward, bonepos;
2012-02-11 21:00:00 +01:00
Matrix3x4_OriginFromMatrix( studio_bones[pAtt[iAttachment].bone], bonepos );
2012-02-12 21:00:00 +01:00
VectorSubtract( origin, bonepos, forward ); // make forward
2011-02-05 22:00:00 +01:00
VectorNormalizeFast( forward );
2012-02-12 21:00:00 +01:00
VectorAngles( forward, angles );
2011-02-05 22:00:00 +01:00
}
2010-08-25 22:00:00 +02:00
}
2012-02-11 21:00:00 +01:00
/*
====================
GetBonePosition
====================
*/
2012-02-12 21:00:00 +01:00
void Mod_GetBonePosition( const edict_t *e, int iBone, float *origin, float *angles )
2012-02-11 21:00:00 +01:00
{
2012-02-12 21:00:00 +01:00
model_t *mod;
mod = Mod_Handle( e->v.modelindex );
mod_studiohdr = (studiohdr_t *)Mod_Extradata( mod );
if( !mod_studiohdr ) return;
2012-02-11 21:00:00 +01:00
2012-12-22 21:00:00 +01:00
ASSERT( pBlendAPI != NULL );
2012-02-12 21:00:00 +01:00
pBlendAPI->SV_StudioSetupBones( mod, e->v.frame, e->v.sequence, e->v.angles, e->v.origin,
2014-05-08 22:00:00 +02:00
e->v.controller, e->v.blending, iBone, e );
2012-02-11 21:00:00 +01:00
2012-02-12 21:00:00 +01:00
if( origin ) Matrix3x4_OriginFromMatrix( studio_bones[iBone], origin );
if( angles ) VectorAngles( studio_bones[iBone][0], angles ); // bone forward to angles
2012-02-11 21:00:00 +01:00
}
/*
====================
HitgroupForStudioHull
====================
*/
int Mod_HitgroupForStudioHull( int index )
{
return studio_hull_hitgroup[index];
}
/*
====================
StudioBoundVertex
====================
*/
void Mod_StudioBoundVertex( vec3_t out_mins, vec3_t out_maxs, int *counter, const vec3_t vertex )
{
if( *counter == 0 )
{
// init bounds
VectorCopy( vertex, out_mins );
VectorCopy( vertex, out_maxs );
}
else
{
AddPointToBounds( vertex, out_mins, out_maxs );
}
(*counter)++;
}
/*
====================
StudioAccumulateBoneVerts
====================
*/
void Mod_StudioAccumulateBoneVerts( vec3_t mins1, vec3_t maxs1, int *counter1, vec3_t mins2, vec3_t maxs2, int *counter2 )
2010-08-25 22:00:00 +02:00
{
2012-02-11 21:00:00 +01:00
vec3_t midpoint;
if( *counter2 <= 0 )
2010-08-25 22:00:00 +02:00
return;
2012-02-11 21:00:00 +01:00
// calculate the midpoint of the second vertex,
VectorSubtract( maxs2, mins2, midpoint );
VectorScale( midpoint, 0.5f, midpoint );
Mod_StudioBoundVertex( mins1, maxs1, counter1, midpoint );
2011-10-06 22:00:00 +02:00
2012-02-11 21:00:00 +01:00
// negate the midpoint, for whatever reason, and bind it again
VectorNegate( midpoint, midpoint );
Mod_StudioBoundVertex( mins1, maxs1, counter1, midpoint );
VectorClear( mins2 );
VectorClear( maxs2 );
*counter2 = 0;
}
/*
====================
StudioComputeBounds
====================
*/
void Mod_StudioComputeBounds( void *buffer, vec3_t mins, vec3_t maxs )
{
int i, j, k;
studiohdr_t *pstudiohdr;
mstudiobodyparts_t *pbodypart;
mstudiomodel_t *m_pSubModel;
mstudioseqdesc_t *pseqdesc;
mstudiobone_t *pbones;
vec3_t vecmins1, vecmaxs1;
vec3_t vecmins2, vecmaxs2;
int counter1, counter2;
int bodyCount = 0;
vec3_t pos;
counter1 = counter2 = 0;
VectorClear( vecmins1 );
VectorClear( vecmaxs1 );
VectorClear( vecmins2 );
VectorClear( vecmaxs2 );
// Get the body part portion of the model
pstudiohdr = (studiohdr_t *)buffer;
pbodypart = (mstudiobodyparts_t *)((byte *)pstudiohdr + pstudiohdr->bodypartindex);
// each body part has nummodels variations so there are as many total variations as there
// are in a matrix of each part by each other part
for( i = 0; i < pstudiohdr->numbodyparts; i++ )
bodyCount += pbodypart[i].nummodels;
// The studio models we want are rvec3_t mins, vec3_t maxsight after the bodyparts (still need to
// find a detailed breakdown of the mdl format). Move pointer there.
m_pSubModel = (mstudiomodel_t *)(&pbodypart[pstudiohdr->numbodyparts]);
for( i = 0; i < bodyCount; i++ )
{
float *vertIndex = (float *)((byte *)pstudiohdr + m_pSubModel[i].vertindex);
for( j = 0; j < m_pSubModel[i].numverts; j++ )
Mod_StudioBoundVertex( vecmins1, vecmaxs1, &counter1, vertIndex + (3 * j));
}
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);
for( j = 0; j < pstudiohdr->numbones; j++ )
{
for( k = 0; k < pseqdesc[i].numframes; k++ )
{
Mod_StudioCalcBonePosition( k, 0, &pbones[j], panim, NULL, pos );
Mod_StudioBoundVertex( vecmins2, vecmaxs2, &counter2, pos );
}
}
Mod_StudioAccumulateBoneVerts( vecmins1, vecmaxs1, &counter1, vecmins2, vecmaxs2, &counter2 );
}
VectorCopy( vecmins1, mins );
VectorCopy( vecmaxs1, maxs );
}
/*
====================
Mod_GetStudioBounds
====================
*/
qboolean Mod_GetStudioBounds( const char *name, vec3_t mins, vec3_t maxs )
{
int result = false;
byte *f;
if( !Q_strstr( name, "models" ) || !Q_strstr( name, ".mdl" ))
return false;
f = FS_LoadFile( name, NULL, false );
if( !f ) return false;
if( *(uint *)f == IDSTUDIOHEADER )
{
VectorClear( mins );
VectorClear( maxs );
Mod_StudioComputeBounds( f, mins, maxs );
result = true;
}
Mem_Free( f );
return result;
2010-11-15 22:00:00 +01:00
}
static sv_blending_interface_t gBlendAPI =
{
SV_BLENDING_INTERFACE_VERSION,
SV_StudioSetupBones,
};
static server_studio_api_t gStudioAPI =
{
Mod_Calloc,
Mod_CacheCheck,
Mod_LoadCacheFile,
Mod_Extradata,
};
/*
===============
2012-02-11 21:00:00 +01:00
Mod_InitStudioAPI
2010-11-15 22:00:00 +01:00
Initialize server studio (blending interface)
===============
*/
2012-02-11 21:00:00 +01:00
void Mod_InitStudioAPI( void )
2010-11-15 22:00:00 +01:00
{
static STUDIOAPI pBlendIface;
pBlendAPI = &gBlendAPI;
2011-03-08 22:00:00 +01:00
pBlendIface = (STUDIOAPI)Com_GetProcAddress( svgame.hInstance, "Server_GetBlendingInterface" );
2012-02-11 21:00:00 +01:00
if( pBlendIface && pBlendIface( SV_BLENDING_INTERFACE_VERSION, &pBlendAPI, &gStudioAPI, &studio_transform, &studio_bones ))
2012-02-08 21:00:00 +01:00
{
MsgDev( D_AICONSOLE, "SV_LoadProgs: ^2initailized Server Blending interface ^7ver. %i\n", SV_BLENDING_INTERFACE_VERSION );
2012-02-11 21:00:00 +01:00
return;
2012-02-08 21:00:00 +01:00
}
2010-11-15 22:00:00 +01:00
// just restore pointer to builtin function
pBlendAPI = &gBlendAPI;
2012-12-22 21:00:00 +01:00
}
/*
===============
Mod_ResetStudioAPI
Returns to default callbacks
===============
*/
void Mod_ResetStudioAPI( void )
{
pBlendAPI = &gBlendAPI;
2010-08-25 22:00:00 +02:00
}