729 lines
22 KiB
C++
729 lines
22 KiB
C++
|
/***
|
||
|
*
|
||
|
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||
|
*
|
||
|
* This product contains software technology licensed from Id
|
||
|
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||
|
* All Rights Reserved.
|
||
|
*
|
||
|
****/
|
||
|
|
||
|
// studio.c
|
||
|
|
||
|
#include "qrad.h"
|
||
|
#include "..\..\engine\studio.h"
|
||
|
#include "model_trace.h"
|
||
|
#include "imagelib.h"
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
char name[64];
|
||
|
mstudiomodel_t *pmodel;
|
||
|
bool shadow;
|
||
|
} TmpModel_t;
|
||
|
|
||
|
static void ExtractAnimValue( int frame, mstudioanim_t *panim, int dof, float scale, float &v1 )
|
||
|
{
|
||
|
if( !panim || panim->offset[dof] == 0 )
|
||
|
{
|
||
|
v1 = 0.0f;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const mstudioanimvalue_t *panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[dof]);
|
||
|
int k = frame;
|
||
|
|
||
|
while( panimvalue->num.total <= k )
|
||
|
{
|
||
|
k -= panimvalue->num.total;
|
||
|
panimvalue += panimvalue->num.valid + 1;
|
||
|
|
||
|
if( panimvalue->num.total == 0 )
|
||
|
{
|
||
|
v1 = 0.0f;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Bah, missing blend!
|
||
|
if( panimvalue->num.valid > k )
|
||
|
{
|
||
|
v1 = panimvalue[k+1].value * scale;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// get last valid data block
|
||
|
v1 = panimvalue[panimvalue->num.valid].value * scale;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void StudioCalcBoneTransform( int frame, mstudiobone_t *pbone, mstudioanim_t *panim, vec3_t pos, vec4_t q )
|
||
|
{
|
||
|
vec3_t origin;
|
||
|
vec3_t angles;
|
||
|
|
||
|
ExtractAnimValue( frame, panim, 0, pbone->scale[0], origin[0] );
|
||
|
ExtractAnimValue( frame, panim, 1, pbone->scale[1], origin[1] );
|
||
|
ExtractAnimValue( frame, panim, 2, pbone->scale[2], origin[2] );
|
||
|
ExtractAnimValue( frame, panim, 3, pbone->scale[3], angles[0] );
|
||
|
ExtractAnimValue( frame, panim, 4, pbone->scale[4], angles[1] );
|
||
|
ExtractAnimValue( frame, panim, 5, pbone->scale[5], angles[2] );
|
||
|
|
||
|
for( int j = 0; j < 3; j++ )
|
||
|
{
|
||
|
origin[j] = pbone->value[j+0] + origin[j];
|
||
|
angles[j] = pbone->value[j+3] + angles[j];
|
||
|
}
|
||
|
|
||
|
AngleQuaternion( angles, q );
|
||
|
VectorCopy( origin, pos );
|
||
|
}
|
||
|
|
||
|
static void StudioSetupTriangle( tmesh_t *mesh, int index )
|
||
|
{
|
||
|
tface_t *face = &mesh->faces[index];
|
||
|
|
||
|
// calculate mins & maxs
|
||
|
ClearBounds( face->absmin, face->absmax );
|
||
|
face->contents = CONTENTS_SOLID;
|
||
|
|
||
|
assert( face->a < mesh->numverts );
|
||
|
assert( face->b < mesh->numverts );
|
||
|
assert( face->c < mesh->numverts );
|
||
|
|
||
|
// calc bounds
|
||
|
AddPointToBounds( mesh->verts[face->a].point, face->absmin, face->absmax );
|
||
|
AddPointToBounds( mesh->verts[face->a].point, mesh->absmin, mesh->absmax );
|
||
|
AddPointToBounds( mesh->verts[face->b].point, face->absmin, face->absmax );
|
||
|
AddPointToBounds( mesh->verts[face->b].point, mesh->absmin, mesh->absmax );
|
||
|
AddPointToBounds( mesh->verts[face->c].point, face->absmin, face->absmax );
|
||
|
AddPointToBounds( mesh->verts[face->c].point, mesh->absmin, mesh->absmax );
|
||
|
ExpandBounds( face->absmin, face->absmax, 1.0 );
|
||
|
|
||
|
// convert skinref to texture pointer
|
||
|
face->texture = &mesh->textures[face->skinref];
|
||
|
// setup efficiency intersection data
|
||
|
face->PrepareIntersectionData( mesh->verts );
|
||
|
}
|
||
|
|
||
|
static void StudioRelinkFace( aabb_tree_t *tree, tface_t *face )
|
||
|
{
|
||
|
// only shadow faces must be linked
|
||
|
if( !face->shadow ) return;
|
||
|
|
||
|
// find the first node that the facet box crosses
|
||
|
areanode_t *node = tree->areanodes;
|
||
|
|
||
|
while( 1 )
|
||
|
{
|
||
|
if( node->axis == -1 ) break;
|
||
|
if( face->absmin[node->axis] > node->dist )
|
||
|
node = node->children[0];
|
||
|
else if( face->absmax[node->axis] < node->dist )
|
||
|
node = node->children[1];
|
||
|
else break; // crosses the node
|
||
|
}
|
||
|
|
||
|
// link it in
|
||
|
InsertLinkBefore( &face->area, &node->solid_edicts );
|
||
|
}
|
||
|
|
||
|
static int StudioCreateMeshFromTriangles( entity_t *ent, studiohdr_t *phdr, const char *modname, int body, int skin, int flags, matrix3x4 transform[] )
|
||
|
{
|
||
|
TmpModel_t submodel[MAXSTUDIOMODELS]; // list of unique models
|
||
|
int i, j, k, totalVertSize = 0;
|
||
|
int num_submodels = 0;
|
||
|
mstudiobodyparts_t *pbodypart;
|
||
|
mstudiomodel_t *psubmodel;
|
||
|
|
||
|
memset( submodel, 0, sizeof( submodel ));
|
||
|
|
||
|
// build list of unique submodels (by name)
|
||
|
for( i = 0; i < phdr->numbodyparts; i++ )
|
||
|
{
|
||
|
pbodypart = (mstudiobodyparts_t *)((byte *)phdr + phdr->bodypartindex) + i;
|
||
|
|
||
|
for( j = 0; j < pbodypart->nummodels; j++ )
|
||
|
{
|
||
|
psubmodel = (mstudiomodel_t *)((byte *)phdr + pbodypart->modelindex) + j;
|
||
|
if( !psubmodel->nummesh ) continue; // blank submodel, ignore it
|
||
|
|
||
|
for( k = 0; k < num_submodels; k++ )
|
||
|
{
|
||
|
if( !Q_stricmp( submodel[k].name, psubmodel->name ))
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// add new one
|
||
|
if( k == num_submodels )
|
||
|
{
|
||
|
Q_strncpy( submodel[k].name, psubmodel->name, sizeof( submodel[k].name ));
|
||
|
submodel[k].pmodel = psubmodel;
|
||
|
num_submodels++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// find which parts are used by current body
|
||
|
for( i = 0; i < phdr->numbodyparts; i++ )
|
||
|
{
|
||
|
pbodypart = (mstudiobodyparts_t *)((byte *)phdr + phdr->bodypartindex) + i;
|
||
|
|
||
|
int index = body / pbodypart->base;
|
||
|
index = index % pbodypart->nummodels;
|
||
|
psubmodel = (mstudiomodel_t *)((byte *)phdr + pbodypart->modelindex) + index;
|
||
|
|
||
|
for( j = 0; j < num_submodels; j++ )
|
||
|
{
|
||
|
if( submodel[j].pmodel == psubmodel )
|
||
|
{
|
||
|
submodel[j].shadow = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// count unique model vertices
|
||
|
for( i = 0; i < num_submodels; i++ )
|
||
|
{
|
||
|
psubmodel = submodel[i].pmodel;
|
||
|
totalVertSize += psubmodel->numverts;
|
||
|
}
|
||
|
|
||
|
tface_t *faces = (tface_t *)Mem_Alloc( sizeof( tface_t ) * totalVertSize * 8 ); // allocate face lighting array
|
||
|
tvert_t *verts = (tvert_t *)Mem_Alloc( sizeof( tvert_t ) * totalVertSize * 8 ); // allocate vertex array
|
||
|
mstudiotexture_t *ptexture = (mstudiotexture_t *)((byte *)phdr + phdr->textureindex);
|
||
|
dvlightofs_t vsubmodels[MAXSTUDIOMODELS];
|
||
|
dflightofs_t fsubmodels[MAXSTUDIOMODELS];
|
||
|
int numFaces = 0, numVerts = 0;
|
||
|
|
||
|
memset( vsubmodels, 0, sizeof( vsubmodels ));
|
||
|
memset( fsubmodels, 0, sizeof( fsubmodels ));
|
||
|
|
||
|
for( k = 0; k < num_submodels; k++ )
|
||
|
{
|
||
|
mstudiomodel_t *psubmodel = submodel[k].pmodel;
|
||
|
|
||
|
if( psubmodel->numverts <= 0 )
|
||
|
continue; // a blank submodel
|
||
|
|
||
|
vec3_t *pstudioverts = (vec3_t *)((byte *)phdr + psubmodel->vertindex);
|
||
|
vec3_t *pstudionorms = (vec3_t *)((byte *)phdr + psubmodel->normindex);
|
||
|
vec3_t *m_verts = (vec3_t *)Mem_Alloc( sizeof( vec3_t ) * psubmodel->numverts );
|
||
|
vec3_t *m_norms = (vec3_t *)Mem_Alloc( sizeof( vec3_t ) * psubmodel->numnorms );
|
||
|
byte *pvertbone = ((byte *)phdr + psubmodel->vertinfoindex);
|
||
|
byte *pnormbone = ((byte *)phdr + psubmodel->norminfoindex);
|
||
|
short *pskinref = (short *)((byte *)phdr + phdr->skinindex);
|
||
|
int m_skinnum = bound( 0, skin, MAXSTUDIOSKINS - 1 );
|
||
|
|
||
|
// setup skin
|
||
|
if( m_skinnum != 0 && m_skinnum < phdr->numskinfamilies )
|
||
|
pskinref += (m_skinnum * phdr->numskinref);
|
||
|
|
||
|
// setup all the vertices
|
||
|
for( i = 0; i < psubmodel->numverts; i++ )
|
||
|
Matrix3x4_VectorTransform( transform[pvertbone[i]], pstudioverts[i], m_verts[i] );
|
||
|
|
||
|
// setup all the normals
|
||
|
for( i = 0; i < psubmodel->numnorms; i++ )
|
||
|
Matrix3x4_VectorRotate( transform[pnormbone[i]], pstudionorms[i], m_norms[i] );
|
||
|
|
||
|
vsubmodels[k].submodel_offset = (byte *)psubmodel - (byte *)phdr;
|
||
|
vsubmodels[k].vertex_offset = numVerts;
|
||
|
fsubmodels[k].submodel_offset = (byte *)psubmodel - (byte *)phdr;
|
||
|
fsubmodels[k].surface_offset = numFaces;
|
||
|
|
||
|
// build all the light vertices because we should include all the bodies into the buffer
|
||
|
for( j = 0; j < psubmodel->nummesh; j++ )
|
||
|
{
|
||
|
mstudiomesh_t *pmesh = (mstudiomesh_t *)((byte *)phdr + psubmodel->meshindex) + j;
|
||
|
float s = 1.0f / (float)ptexture[pskinref[pmesh->skinref]].width;
|
||
|
float t = 1.0f / (float)ptexture[pskinref[pmesh->skinref]].height;
|
||
|
short *ptricmds = (short *)((byte *)phdr + pmesh->triindex);
|
||
|
int flags = ptexture[pskinref[pmesh->skinref]].flags;
|
||
|
|
||
|
while( i = *( ptricmds++ ))
|
||
|
{
|
||
|
int vertexState = 0;
|
||
|
bool tri_strip;
|
||
|
|
||
|
if( i < 0 )
|
||
|
{
|
||
|
tri_strip = false;
|
||
|
i = -i;
|
||
|
}
|
||
|
else tri_strip = true;
|
||
|
|
||
|
for( ; i > 0; i--, ptricmds += 4 )
|
||
|
{
|
||
|
tvert_t *tv = &verts[numVerts];
|
||
|
tface_t *tf = &faces[numFaces];
|
||
|
|
||
|
if( submodel[k].shadow )
|
||
|
tf->shadow = true;
|
||
|
tf->skinref = pskinref[pmesh->skinref];
|
||
|
|
||
|
// build in indices
|
||
|
if( vertexState++ < 3 )
|
||
|
{
|
||
|
switch( vertexState )
|
||
|
{
|
||
|
case 1:
|
||
|
tf->a = numVerts;
|
||
|
break;
|
||
|
case 2:
|
||
|
tf->b = numVerts;
|
||
|
break;
|
||
|
case 3:
|
||
|
tf->c = numVerts;
|
||
|
numFaces++;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else if( tri_strip )
|
||
|
{
|
||
|
// flip triangles between clockwise and counter clockwise
|
||
|
if( vertexState & 1 )
|
||
|
{
|
||
|
// draw triangle [n-2 n-1 n]
|
||
|
tf->a = numVerts - 2;
|
||
|
tf->b = numVerts - 1;
|
||
|
tf->c = numVerts;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// draw triangle [n-1 n-2 n]
|
||
|
tf->a = numVerts - 1;
|
||
|
tf->b = numVerts - 2;
|
||
|
tf->c = numVerts;
|
||
|
}
|
||
|
numFaces++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// draw triangle fan [0 n-1 n]
|
||
|
tf->a = numVerts - ( vertexState - 1 );
|
||
|
tf->b = numVerts - 1;
|
||
|
tf->c = numVerts;
|
||
|
numFaces++;
|
||
|
}
|
||
|
|
||
|
if( FBitSet( flags, STUDIO_NF_CHROME ))
|
||
|
{
|
||
|
// probably always equal 64 (see studiomdl.c for details)
|
||
|
tv->st[0] = float( s );
|
||
|
tv->st[1] = float( t );
|
||
|
}
|
||
|
else if( FBitSet( flags, STUDIO_NF_UV_COORDS ))
|
||
|
{
|
||
|
tv->st[0] = HalfToFloat( ptricmds[2] );
|
||
|
tv->st[1] = HalfToFloat( ptricmds[3] );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
tv->st[0] = float( ptricmds[2] * s );
|
||
|
tv->st[1] = float( ptricmds[3] * t );
|
||
|
}
|
||
|
|
||
|
VectorCopy( m_verts[ptricmds[0]], tv->point );
|
||
|
VectorCopy( m_norms[ptricmds[1]], tv->normal );
|
||
|
VectorNormalize2( tv->normal );
|
||
|
numVerts++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// don't keep this because different submodels may have difference count of normals
|
||
|
Mem_Free( m_norms );
|
||
|
Mem_Free( m_verts );
|
||
|
}
|
||
|
|
||
|
// perfomance warning
|
||
|
if( numFaces >= MAX_TRIANGLES )
|
||
|
{
|
||
|
MsgDev( D_ERROR, "%s have too many triangles (%i). Ignored\n", modname, numFaces );
|
||
|
Mem_Free( verts );
|
||
|
Mem_Free( faces );
|
||
|
return 0; // failed to build (too many triangles)
|
||
|
}
|
||
|
else if( numFaces >= (MAX_TRIANGLES>>1))
|
||
|
MsgDev( D_WARN, "%s have too many triangles (%i)\n", modname, numFaces );
|
||
|
|
||
|
size_t texdata_size = sizeof( timage_t ) * phdr->numtextures;
|
||
|
char diffuse[128], texname[128], mdlname[64];
|
||
|
rgbdata_t *external_textures[256];
|
||
|
|
||
|
COM_FileBase( modname, mdlname );
|
||
|
memset( external_textures, 0 , sizeof( external_textures ));
|
||
|
|
||
|
// store only texdata where we has alpha-testing
|
||
|
for( i = 0; i < phdr->numtextures; i++ )
|
||
|
{
|
||
|
mstudiotexture_t *tex = &ptexture[i];
|
||
|
|
||
|
COM_FileBase( tex->name, texname );
|
||
|
Q_snprintf( diffuse, sizeof( diffuse ), "textures/%s/%s", mdlname, texname );
|
||
|
|
||
|
#ifdef HLRAD_EXTERNAL_TEXTURES
|
||
|
if( FBitSet( tex->flags, STUDIO_NF_ADDITIVE|STUDIO_NF_MASKED ) && IMAGE_EXISTS( diffuse ))
|
||
|
{
|
||
|
rgbdata_t *test = COM_LoadImage( diffuse );
|
||
|
|
||
|
// external texture has alpha - use it
|
||
|
if( test )
|
||
|
{
|
||
|
if( FBitSet( test->flags, IMAGE_HAS_ALPHA ))
|
||
|
{
|
||
|
MsgDev( D_REPORT, "load external texture %s\n", diffuse );
|
||
|
texdata_size += test->width * test->height;
|
||
|
external_textures[i] = test;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// has no alpha
|
||
|
Mem_Free( test );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
if( external_textures[i] ) continue; // already loaded
|
||
|
|
||
|
// NOTE: store the only pixels, we doesn't need a palette
|
||
|
if( FBitSet( tex->flags, STUDIO_NF_MASKED ))
|
||
|
texdata_size += tex->width * tex->height;
|
||
|
}
|
||
|
|
||
|
size_t memsize = sizeof( tmesh_t ) + ( sizeof( tface_t ) * numFaces ) + ( sizeof( tvert_t ) * numVerts ) + texdata_size;
|
||
|
|
||
|
// alloc vislight matrix
|
||
|
if( FBitSet( flags, FMESH_MODEL_LIGHTMAPS|FMESH_VERTEX_LIGHTING ))
|
||
|
memsize += (g_numworldlights + 7) / 8;
|
||
|
|
||
|
// alloc lighting faces
|
||
|
if( FBitSet( flags, FMESH_MODEL_LIGHTMAPS ))
|
||
|
memsize += sizeof( lface_t ) * numFaces;
|
||
|
|
||
|
// alloc lighting verts
|
||
|
if( FBitSet( flags, FMESH_VERTEX_LIGHTING ))
|
||
|
memsize += sizeof( lvert_t ) * numVerts;
|
||
|
|
||
|
// Msg( "%s alloc %s\n", modname, Q_memprint( memsize ));
|
||
|
|
||
|
byte *meshdata = (byte *)Mem_Alloc( memsize );
|
||
|
byte *meshend = meshdata + memsize; // bounds checking
|
||
|
tmesh_t *mesh = (tmesh_t *)meshdata;
|
||
|
|
||
|
if( ent->cache ) Mem_Free( ent->cache ); // throw previous instance
|
||
|
ent->cache = meshdata; // FreeEntities will be automatically free that
|
||
|
|
||
|
// setup pointers
|
||
|
meshdata += sizeof( tmesh_t );
|
||
|
mesh->textures = (timage_t *)meshdata;
|
||
|
meshdata += sizeof( timage_t ) * phdr->numtextures;
|
||
|
mesh->faces = (tface_t *)meshdata;
|
||
|
meshdata += sizeof( tface_t ) * numFaces;
|
||
|
mesh->numfaces = numFaces;
|
||
|
|
||
|
// store faces
|
||
|
memcpy( mesh->faces, faces, sizeof( tface_t ) * mesh->numfaces );
|
||
|
Mem_Free( faces );
|
||
|
|
||
|
// setup additional lightdata if present
|
||
|
if( FBitSet( flags, FMESH_MODEL_LIGHTMAPS ))
|
||
|
{
|
||
|
for( int i = 0; i < numFaces; i++ )
|
||
|
{
|
||
|
mesh->faces[i].light = (lface_t *)meshdata;
|
||
|
meshdata += sizeof( lface_t );
|
||
|
|
||
|
// clearing lightdata
|
||
|
for( int j = 0; j < MAXLIGHTMAPS; j++ )
|
||
|
mesh->faces[i].light->styles[j] = 255;
|
||
|
mesh->faces[i].light->lightofs = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mesh->verts = (tvert_t *)meshdata;
|
||
|
meshdata += sizeof( tvert_t ) * numVerts;
|
||
|
mesh->numverts = numVerts;
|
||
|
|
||
|
// store vertexes
|
||
|
memcpy( mesh->verts, verts, sizeof( tvert_t ) * mesh->numverts );
|
||
|
Mem_Free( verts );
|
||
|
|
||
|
// setup additional lightdata if present
|
||
|
if( FBitSet( flags, FMESH_VERTEX_LIGHTING ))
|
||
|
{
|
||
|
for( int i = 0; i < numVerts; i++ )
|
||
|
{
|
||
|
mesh->verts[i].light = (lvert_t *)meshdata;
|
||
|
meshdata += sizeof( lvert_t );
|
||
|
VectorCopy( mesh->verts[i].point, mesh->verts[i].light->pos );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
memcpy( mesh->vsubmodels, vsubmodels, sizeof( mesh->vsubmodels ));
|
||
|
memcpy( mesh->fsubmodels, fsubmodels, sizeof( mesh->fsubmodels ));
|
||
|
|
||
|
if( FBitSet( flags, FMESH_MODEL_LIGHTMAPS|FMESH_VERTEX_LIGHTING ))
|
||
|
{
|
||
|
mesh->vislight = (byte *)meshdata;
|
||
|
meshdata += (g_numworldlights + 7) / 8;
|
||
|
}
|
||
|
|
||
|
for( int l = 0; l < MAXLIGHTMAPS; l++ )
|
||
|
mesh->styles[l] = 255;
|
||
|
|
||
|
// store only texdata where we has alpha-testing
|
||
|
for( i = 0; i < phdr->numtextures; i++ )
|
||
|
{
|
||
|
mstudiotexture_t *src = &ptexture[i];
|
||
|
timage_t *dst = &mesh->textures[i];
|
||
|
|
||
|
dst->width = src->width;
|
||
|
dst->height = src->height;
|
||
|
dst->flags = src->flags;
|
||
|
|
||
|
if( external_textures[i] )
|
||
|
{
|
||
|
rgbdata_t *test = external_textures[i];
|
||
|
dst->data = meshdata;
|
||
|
dst->width = test->width;
|
||
|
dst->height = test->height;
|
||
|
|
||
|
// copy alpha channel from external texture
|
||
|
for( j = 0; j < test->width * test->height; j++ )
|
||
|
dst->data[j] = test->buffer[j*4+3] < 255 ? 255 : 0; // because we used palette index, not alpha value
|
||
|
meshdata += test->width * test->height; // move pointer
|
||
|
external_textures[i] = NULL;
|
||
|
Mem_Free( test );
|
||
|
}
|
||
|
else if( FBitSet( src->flags, STUDIO_NF_MASKED ))
|
||
|
{
|
||
|
// NOTE: store the only pixels, we doesn't need a palette
|
||
|
dst->data = meshdata;
|
||
|
memcpy( dst->data, (byte *)phdr + src->index, src->width * src->height );
|
||
|
meshdata += src->width * src->height; // move pointer
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( meshdata != meshend )
|
||
|
Msg( "%s memory corrupted\n", modname );
|
||
|
|
||
|
ClearBounds( mesh->absmin, mesh->absmax );
|
||
|
|
||
|
// do some post-initialization
|
||
|
for( i = 0; i < numFaces; i++ )
|
||
|
StudioSetupTriangle( mesh, i );
|
||
|
|
||
|
return numFaces;
|
||
|
}
|
||
|
|
||
|
bool StudioConstructMesh( entity_t *ent, void *extradata, const char *modname, uint modelCRC, int flags )
|
||
|
{
|
||
|
studiohdr_t *phdr = (studiohdr_t *)extradata;
|
||
|
double start = I_FloatTime();
|
||
|
vec3_t origin, angles;
|
||
|
int body, skin;
|
||
|
int numFaces;
|
||
|
vec3_t xform;
|
||
|
float scale;
|
||
|
|
||
|
if( phdr->numbones < 1 )
|
||
|
return false;
|
||
|
|
||
|
// get model properties
|
||
|
GetVectorForKey( ent, "origin", origin );
|
||
|
GetVectorForKey( ent, "angles", angles );
|
||
|
|
||
|
angles[0] = -angles[0]; // Stupid quake bug workaround
|
||
|
scale = FloatForKey( ent, "scale" );
|
||
|
body = IntForKey( ent, "body" );
|
||
|
skin = IntForKey( ent, "skin" );
|
||
|
|
||
|
GetVectorForKey( ent, "xform", xform );
|
||
|
|
||
|
if( VectorIsNull( xform ))
|
||
|
VectorFill( xform, scale );
|
||
|
|
||
|
// check xform values
|
||
|
if( xform[0] < 0.01f ) xform[0] = 1.0f;
|
||
|
if( xform[1] < 0.01f ) xform[1] = 1.0f;
|
||
|
if( xform[2] < 0.01f ) xform[2] = 1.0f;
|
||
|
if( xform[0] > 16.0f ) xform[0] = 16.0f;
|
||
|
if( xform[1] > 16.0f ) xform[1] = 16.0f;
|
||
|
if( xform[2] > 16.0f ) xform[2] = 16.0f;
|
||
|
|
||
|
// compute default pose for building mesh from
|
||
|
mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)phdr + phdr->seqindex);
|
||
|
mstudioseqgroup_t *pseqgroup = (mstudioseqgroup_t *)((byte *)phdr + phdr->seqgroupindex);
|
||
|
mstudioanim_t *panim = (mstudioanim_t *)((byte *)phdr + pseqdesc->animindex);
|
||
|
mstudiobone_t *pbone = (mstudiobone_t *)((byte *)phdr + phdr->boneindex);
|
||
|
static vec3_t pos[MAXSTUDIOBONES];
|
||
|
static vec4_t q[MAXSTUDIOBONES];
|
||
|
|
||
|
for( int i = 0; i < phdr->numbones; i++, pbone++, panim++ )
|
||
|
StudioCalcBoneTransform( 0, pbone, panim, pos[i], q[i] );
|
||
|
pbone = (mstudiobone_t *)((byte *)phdr + phdr->boneindex);
|
||
|
matrix3x4 transform, bonematrix, bonetransform[MAXSTUDIOBONES];
|
||
|
Matrix3x4_CreateFromEntityScale3f( transform, angles, origin, xform );
|
||
|
|
||
|
// compute bones for default anim
|
||
|
for( i = 0; i < phdr->numbones; i++ )
|
||
|
{
|
||
|
// initialize bonematrix
|
||
|
Matrix3x4_FromOriginQuat( bonematrix, q[i], pos[i] );
|
||
|
|
||
|
if( pbone[i].parent == -1 )
|
||
|
Matrix3x4_ConcatTransforms( bonetransform[i], transform, bonematrix );
|
||
|
else Matrix3x4_ConcatTransforms( bonetransform[i], bonetransform[pbone[i].parent], bonematrix );
|
||
|
}
|
||
|
|
||
|
if(( numFaces = StudioCreateMeshFromTriangles( ent, phdr, modname, body, skin, flags, bonetransform )) == 0 )
|
||
|
return false;
|
||
|
|
||
|
tmesh_t *mesh = (tmesh_t *)ent->cache;
|
||
|
size_t memsize = Mem_Size( ent->cache );
|
||
|
int maxDepth = AREA_MIN_DEPTH;
|
||
|
|
||
|
// FIXME: check this efficiency
|
||
|
if( numFaces < 1000 )
|
||
|
maxDepth = AREA_MIN_DEPTH;
|
||
|
else if( numFaces >= 1000 && numFaces <= 10000 )
|
||
|
maxDepth = 5;
|
||
|
else if( numFaces >= 10000 && numFaces <= 20000 )
|
||
|
maxDepth = 6;
|
||
|
else if( numFaces >= 20000 && numFaces <= 50000 )
|
||
|
maxDepth = 7;
|
||
|
else if( numFaces >= 50000 && numFaces <= 100000 )
|
||
|
maxDepth = 8;
|
||
|
else if( numFaces >= 100000 && numFaces <= 200000 )
|
||
|
maxDepth = 9;
|
||
|
else maxDepth = AREA_MAX_DEPTH;
|
||
|
|
||
|
// create AABB tree for speedup reasons
|
||
|
CreateAreaNode( &mesh->face_tree, 0, maxDepth, mesh->absmin, mesh->absmax );
|
||
|
|
||
|
for( i = 0; i < numFaces; i++ )
|
||
|
StudioRelinkFace( &mesh->face_tree, &mesh->faces[i] );
|
||
|
|
||
|
ent->modtype = mod_studio; // now our mesh is valid and ready to trace
|
||
|
mesh->modelCRC = modelCRC;
|
||
|
VectorCopy( origin, mesh->origin );
|
||
|
VectorCopy( angles, mesh->angles );
|
||
|
VectorCopy( xform, mesh->scale );
|
||
|
mesh->backfrac = FloatForKey( ent, "zhlt_backfrac" );
|
||
|
mesh->flags = flags; // copy settings
|
||
|
|
||
|
MsgDev( D_REPORT, "%s: build time %g secs, size %s\n", modname, I_FloatTime() - start, Q_memprint( memsize ));
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void LoadStudio( entity_t *ent, void *extradata, long fileLength, int flags )
|
||
|
{
|
||
|
const char *modname = ValueForKey( ent, "model" );
|
||
|
dword modelCRC = 0;
|
||
|
studiohdr_t *phdr;
|
||
|
|
||
|
if( !extradata )
|
||
|
{
|
||
|
MsgDev( D_WARN, "LoadStudio: couldn't load %s\n", modname );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
phdr = (studiohdr_t *)extradata;
|
||
|
|
||
|
if( phdr->ident != IDSTUDIOHEADER || phdr->version != STUDIO_VERSION )
|
||
|
{
|
||
|
if( phdr->ident != IDSTUDIOHEADER )
|
||
|
MsgDev( D_WARN, "LoadStudio: %s not a studio model\n", modname );
|
||
|
else if( phdr->version != STUDIO_VERSION )
|
||
|
MsgDev( D_WARN, "LoadStudio: %s has wrong version number (%i should be %i)\n", modname, phdr->version, STUDIO_VERSION );
|
||
|
Mem_Free( extradata, C_FILESYSTEM );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#ifndef HLRAD_VERTEXLIGHTING
|
||
|
ClearBits( flags, FMESH_VERTEX_LIGHTING );
|
||
|
#endif
|
||
|
|
||
|
#ifndef HLRAD_LIGHTMAPMODELS
|
||
|
ClearBits( flags, FMESH_MODEL_LIGHTMAPS );
|
||
|
#endif
|
||
|
|
||
|
#if defined( HLRAD_VERTEXLIGHTING ) || defined( HLRAD_LIGHTMAPMODELS )
|
||
|
CRC32_Init( &modelCRC );
|
||
|
CRC32_ProcessBuffer( &modelCRC, extradata, phdr->length );
|
||
|
CRC32_Final( &modelCRC );
|
||
|
#endif
|
||
|
// well the textures place in separate file (very stupid case)
|
||
|
if( phdr->numtextures == 0 )
|
||
|
{
|
||
|
char texname[128], texpath[128];
|
||
|
byte *texdata, *moddata;
|
||
|
studiohdr_t *thdr, *newhdr;
|
||
|
|
||
|
Q_strncpy( texname, modname, sizeof( texname ));
|
||
|
COM_StripExtension( texname );
|
||
|
|
||
|
Q_snprintf( texpath, sizeof( texpath ), "%sT.mdl", texname );
|
||
|
MsgDev( D_REPORT, "loading %s\n", texpath );
|
||
|
texdata = FS_LoadFile( texpath, NULL, false );
|
||
|
|
||
|
if( !texdata )
|
||
|
{
|
||
|
MsgDev( D_WARN, "LoadStudioModel: couldn't load %s\n", texpath );
|
||
|
Mem_Free( extradata, C_FILESYSTEM );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
moddata = (byte *)extradata;
|
||
|
phdr = (studiohdr_t *)moddata;
|
||
|
thdr = (studiohdr_t *)texdata;
|
||
|
|
||
|
// merge textures with main model buffer
|
||
|
extradata = Mem_Alloc( phdr->length + thdr->length - sizeof( studiohdr_t ), C_FILESYSTEM ); // we don't need two headers
|
||
|
memcpy( extradata, moddata, phdr->length );
|
||
|
memcpy((byte *)extradata + phdr->length, texdata + sizeof( studiohdr_t ), thdr->length - sizeof( studiohdr_t ));
|
||
|
|
||
|
// merge header
|
||
|
newhdr = (studiohdr_t *)extradata;
|
||
|
|
||
|
newhdr->numskinfamilies = thdr->numskinfamilies;
|
||
|
newhdr->numtextures = thdr->numtextures;
|
||
|
newhdr->numskinref = thdr->numskinref;
|
||
|
newhdr->textureindex = phdr->length;
|
||
|
newhdr->skinindex = newhdr->textureindex + ( newhdr->numtextures * sizeof( mstudiotexture_t ));
|
||
|
newhdr->texturedataindex = newhdr->skinindex + (newhdr->numskinfamilies * newhdr->numskinref * sizeof( short ));
|
||
|
newhdr->length = phdr->length + thdr->length - sizeof( studiohdr_t );
|
||
|
|
||
|
// and finally merge datapointers for textures
|
||
|
for( int i = 0; i < newhdr->numtextures; i++ )
|
||
|
{
|
||
|
mstudiotexture_t *ptexture = (mstudiotexture_t *)(((byte *)newhdr) + newhdr->textureindex);
|
||
|
ptexture[i].index += ( phdr->length - sizeof( studiohdr_t ));
|
||
|
}
|
||
|
|
||
|
Mem_Free( moddata, C_FILESYSTEM );
|
||
|
Mem_Free( texdata, C_FILESYSTEM );
|
||
|
}
|
||
|
|
||
|
StudioConstructMesh( ent, extradata, modname, modelCRC, flags );
|
||
|
Mem_Free( extradata, C_FILESYSTEM );
|
||
|
}
|
||
|
|
||
|
void StudioGetBounds( entity_t *ent, vec3_t mins, vec3_t maxs )
|
||
|
{
|
||
|
tmesh_t *mesh = (tmesh_t *)ent->cache;
|
||
|
|
||
|
// assume point hull
|
||
|
VectorClear( mins );
|
||
|
VectorClear( maxs );
|
||
|
|
||
|
if( !mesh || ent->modtype != mod_studio )
|
||
|
return;
|
||
|
|
||
|
VectorSubtract( mesh->absmin, ent->origin, mins );
|
||
|
VectorSubtract( mesh->absmax, ent->origin, maxs );
|
||
|
}
|