404 lines
11 KiB
C++
404 lines
11 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.
|
|
*
|
|
****/
|
|
|
|
// alias.c
|
|
|
|
#include "qrad.h"
|
|
#include "..\..\engine\alias.h"
|
|
#include "model_trace.h"
|
|
|
|
static trivertex_t *g_poseverts[MAXALIASFRAMES];
|
|
static stvert_t g_stverts[MAXALIASVERTS];
|
|
static dtriangle_t g_triangles[MAXALIASTRIS];
|
|
static int g_posenum;
|
|
|
|
void *LoadSingleSkin( daliasskintype_t *pskintype, int size )
|
|
{
|
|
return ((byte *)(pskintype + 1) + size);
|
|
}
|
|
|
|
void *LoadGroupSkin( daliasskintype_t *pskintype, int size )
|
|
{
|
|
daliasskininterval_t *pinskinintervals;
|
|
daliasskingroup_t *pinskingroup;
|
|
int i;
|
|
|
|
// animating skin group. yuck.
|
|
pskintype++;
|
|
pinskingroup = (daliasskingroup_t *)pskintype;
|
|
pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1);
|
|
pskintype = (daliasskintype_t *)(pinskinintervals + pinskingroup->numskins);
|
|
|
|
for( i = 0; i < pinskingroup->numskins; i++ )
|
|
pskintype = (daliasskintype_t *)((byte *)(pskintype) + size);
|
|
return pskintype;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
LoadAllSkins
|
|
===============
|
|
*/
|
|
void *LoadAllSkins( daliashdr_t *phdr, daliasskintype_t *pskintype )
|
|
{
|
|
int i, size;
|
|
|
|
size = phdr->skinwidth * phdr->skinheight;
|
|
|
|
for( i = 0; i < phdr->numskins; i++ )
|
|
{
|
|
if( pskintype->type == ALIAS_SKIN_SINGLE )
|
|
pskintype = (daliasskintype_t *)LoadSingleSkin( pskintype, size );
|
|
else pskintype = (daliasskintype_t *)LoadGroupSkin( pskintype, size );
|
|
}
|
|
|
|
return (void *)pskintype;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
LoadAliasFrame
|
|
=================
|
|
*/
|
|
void *LoadAliasFrame( daliashdr_t *phdr, void *pin )
|
|
{
|
|
daliasframe_t *pdaliasframe;
|
|
trivertex_t *pinframe;
|
|
|
|
pdaliasframe = (daliasframe_t *)pin;
|
|
pinframe = (trivertex_t *)(pdaliasframe + 1);
|
|
|
|
g_poseverts[g_posenum] = (trivertex_t *)pinframe;
|
|
pinframe += phdr->numverts;
|
|
g_posenum++;
|
|
|
|
return (void *)pinframe;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
LoadAliasGroup
|
|
=================
|
|
*/
|
|
void *LoadAliasGroup( daliashdr_t *phdr, void *pin )
|
|
{
|
|
daliasgroup_t *pingroup;
|
|
int i, numframes;
|
|
daliasinterval_t *pin_intervals;
|
|
void *ptemp;
|
|
|
|
pingroup = (daliasgroup_t *)pin;
|
|
numframes = pingroup->numframes;
|
|
|
|
pin_intervals = (daliasinterval_t *)(pingroup + 1);
|
|
pin_intervals += numframes;
|
|
ptemp = (void *)pin_intervals;
|
|
|
|
for( i = 0; i < numframes; i++ )
|
|
{
|
|
g_poseverts[g_posenum] = (trivertex_t *)((daliasframe_t *)ptemp + 1);
|
|
ptemp = (trivertex_t *)((daliasframe_t *)ptemp + 1) + phdr->numverts;
|
|
g_posenum++;
|
|
}
|
|
|
|
return ptemp;
|
|
}
|
|
|
|
static void AliasSetupTriangle( tmesh_t *mesh, int index )
|
|
{
|
|
tface_t *face = &mesh->faces[index];
|
|
|
|
// calculate mins & maxs
|
|
ClearBounds( face->absmin, face->absmax );
|
|
face->contents = CONTENTS_SOLID;
|
|
|
|
// 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 );
|
|
|
|
// alias always have single texture on a mesh
|
|
face->texture = mesh->textures;
|
|
// setup efficiency intersection data
|
|
face->PrepareIntersectionData( mesh->verts );
|
|
}
|
|
|
|
static void AliasRelinkFace( 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 );
|
|
}
|
|
|
|
void LoadAlias( entity_t *ent, void *extradata, long fileLength, int flags )
|
|
{
|
|
const char *modname = ValueForKey( ent, "model" );
|
|
int keyframe = IntForKey( ent, "frame" );
|
|
double start = I_FloatTime();
|
|
dword modelCRC = 0;
|
|
daliashdr_t *phdr;
|
|
stvert_t *pinstverts;
|
|
dtriangle_t *pintriangles;
|
|
daliasframetype_t *pframetype;
|
|
daliasskintype_t *pskintype;
|
|
trivertex_t *poseverts;
|
|
int i, j;
|
|
|
|
// alias doesn't allow these features
|
|
ClearBits( flags, FMESH_VERTEX_LIGHTING|FMESH_MODEL_LIGHTMAPS );
|
|
|
|
if( !extradata )
|
|
{
|
|
MsgDev( D_WARN, "LoadAlias: couldn't load %s\n", modname );
|
|
return;
|
|
}
|
|
|
|
phdr = (daliashdr_t *)extradata;
|
|
i = phdr->version;
|
|
|
|
if( i != ALIAS_VERSION )
|
|
{
|
|
MsgDev( D_ERROR, "%s has wrong version number (%i should be %i)\n", modname, i, ALIAS_VERSION );
|
|
Mem_Free( extradata, C_FILESYSTEM );
|
|
return;
|
|
}
|
|
|
|
if( phdr->numframes < 1 )
|
|
{
|
|
MsgDev( D_ERROR, "%s has invalid # of frames: %d\n", modname, phdr->numframes );
|
|
Mem_Free( extradata, C_FILESYSTEM );
|
|
return;
|
|
}
|
|
|
|
if( keyframe < 0 || keyframe >= phdr->numframes )
|
|
{
|
|
MsgDev( D_WARN, "%s specified invalid frame: %d\n", modname, keyframe );
|
|
keyframe = 0;
|
|
}
|
|
|
|
if( phdr->numverts <= 0 )
|
|
{
|
|
MsgDev( D_ERROR, "model %s has no vertices\n", modname );
|
|
Mem_Free( extradata, C_FILESYSTEM );
|
|
return;
|
|
}
|
|
|
|
if( phdr->numverts > MAXALIASVERTS )
|
|
{
|
|
MsgDev( D_ERROR, "model %s has too many vertices\n", modname );
|
|
Mem_Free( extradata, C_FILESYSTEM );
|
|
return;
|
|
}
|
|
|
|
if( phdr->numtris <= 0 )
|
|
{
|
|
MsgDev( D_ERROR, "model %s has no triangles\n", modname );
|
|
Mem_Free( extradata, C_FILESYSTEM );
|
|
return;
|
|
}
|
|
|
|
if( phdr->numskins < 1 || phdr->numskins > MAX_SKINS )
|
|
{
|
|
MsgDev( D_ERROR, "model %s has invalid # of skins: %d\n", modname, phdr->numskins );
|
|
Mem_Free( extradata, C_FILESYSTEM );
|
|
return;
|
|
}
|
|
|
|
// load the skins
|
|
pskintype = (daliasskintype_t *)&phdr[1];
|
|
pskintype = (daliasskintype_t *)LoadAllSkins( phdr, pskintype );
|
|
|
|
// load base s and t vertices
|
|
pinstverts = (stvert_t *)pskintype;
|
|
|
|
for( i = 0; i < phdr->numverts; i++ )
|
|
{
|
|
g_stverts[i].onseam = pinstverts[i].onseam;
|
|
g_stverts[i].s = pinstverts[i].s;
|
|
g_stverts[i].t = pinstverts[i].t;
|
|
}
|
|
|
|
// load triangle lists
|
|
pintriangles = (dtriangle_t *)&pinstverts[phdr->numverts];
|
|
|
|
for( i = 0; i < phdr->numtris; i++ )
|
|
{
|
|
g_triangles[i].facesfront = pintriangles[i].facesfront;
|
|
|
|
for( j = 0; j < 3; j++ )
|
|
g_triangles[i].vertindex[j] = pintriangles[i].vertindex[j];
|
|
}
|
|
|
|
// load the frames
|
|
pframetype = (daliasframetype_t *)&pintriangles[phdr->numtris];
|
|
g_posenum = 0;
|
|
|
|
for( i = 0; i < phdr->numframes; i++ )
|
|
{
|
|
aliasframetype_t frametype = pframetype->type;
|
|
|
|
if( frametype == ALIAS_SINGLE )
|
|
pframetype = (daliasframetype_t *)LoadAliasFrame( phdr, pframetype + 1 );
|
|
else pframetype = (daliasframetype_t *)LoadAliasGroup( phdr, pframetype + 1 );
|
|
}
|
|
|
|
size_t memsize = sizeof( tmesh_t ) + ( sizeof( tface_t ) * phdr->numtris ) + sizeof( timage_t ) + sizeof( tvert_t ) * phdr->numverts;
|
|
byte *meshdata = (byte *)Mem_Alloc( memsize );
|
|
byte *meshend = meshdata + memsize;
|
|
tmesh_t *mesh = (tmesh_t *)meshdata;
|
|
vec3_t origin, angles, point, normal;
|
|
matrix3x4 transform;
|
|
vec3_t xform;
|
|
float scale;
|
|
|
|
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->faces = (tface_t *)meshdata;
|
|
meshdata += sizeof( tface_t ) * phdr->numtris;
|
|
mesh->textures = (timage_t *)meshdata;
|
|
meshdata += sizeof( timage_t );
|
|
mesh->verts = (tvert_t *)meshdata;
|
|
meshdata += sizeof( tvert_t ) * phdr->numverts;
|
|
mesh->numverts = phdr->numverts;
|
|
|
|
if( meshdata != meshend )
|
|
Msg( "%s memory corrupted\n", modname );
|
|
|
|
// mesh->submodels aren't used because alias model doesn't have the submodels
|
|
for( int l = 0; l < MAXLIGHTMAPS; l++ )
|
|
mesh->styles[l] = 255;
|
|
|
|
// select the specified frame
|
|
poseverts = g_poseverts[keyframe];
|
|
ASSERT( poseverts != NULL );
|
|
|
|
ClearBounds( mesh->absmin, mesh->absmax );
|
|
|
|
// just in case, not needs
|
|
mesh->textures->width = phdr->skinwidth;
|
|
mesh->textures->height = phdr->skinheight;
|
|
|
|
// get model properties
|
|
GetVectorForKey( ent, "origin", origin );
|
|
GetVectorForKey( ent, "angles", angles );
|
|
|
|
angles[0] = -angles[0]; // Stupid quake bug workaround
|
|
scale = FloatForKey( ent, "scale" );
|
|
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;
|
|
|
|
Matrix3x4_CreateFromEntityScale3f( transform, angles, origin, xform );
|
|
|
|
// fill the verts
|
|
for( i = 0; i < phdr->numverts; i++ )
|
|
{
|
|
stvert_t *st = &g_stverts[i];
|
|
trivertex_t *v = &poseverts[i];
|
|
tvert_t *tv = &mesh->verts[i];
|
|
float s, t;
|
|
|
|
point[0] = (float)v->v[0] * phdr->scale[0] + phdr->scale_origin[0];
|
|
point[1] = (float)v->v[1] * phdr->scale[1] + phdr->scale_origin[1];
|
|
point[2] = (float)v->v[2] * phdr->scale[2] + phdr->scale_origin[2];
|
|
Matrix3x4_VectorTransform( transform, point, tv->point );
|
|
|
|
// compute lightvert normal
|
|
VectorCopy( g_anorms[v->lightnormalindex], point );
|
|
Matrix3x4_VectorRotate( transform, point, normal );
|
|
VectorNormalize2( normal );
|
|
VectorCopy( normal, tv->normal );
|
|
|
|
s = st->s;
|
|
t = st->t;
|
|
|
|
#if 0 // FIXME: unreachable. but who cares?
|
|
if( !tri->facesfront && st->onseam )
|
|
s += phdr->skinwidth / 2; // on back side
|
|
#endif
|
|
tv->st[0] = (s + 0.5f) / phdr->skinwidth;
|
|
tv->st[1] = (t + 0.5f) / phdr->skinheight;
|
|
}
|
|
|
|
// fill the triangles
|
|
for( i = 0; i < phdr->numtris; i++ )
|
|
{
|
|
dtriangle_t *tri = &g_triangles[i];
|
|
tface_t *tf = &mesh->faces[i];
|
|
|
|
tf->a = tri->vertindex[0];
|
|
tf->b = tri->vertindex[1];
|
|
tf->c = tri->vertindex[2];
|
|
tf->shadow = true; // used for trace
|
|
}
|
|
|
|
// do some post-initialization
|
|
for( i = 0; i < phdr->numtris; i++ )
|
|
AliasSetupTriangle( mesh, i );
|
|
|
|
// create AABB tree for speedup reasons
|
|
CreateAreaNode( &mesh->face_tree, 0, AREA_MIN_DEPTH, mesh->absmin, mesh->absmax );
|
|
|
|
for( i = 0; i < phdr->numtris; i++ )
|
|
AliasRelinkFace( &mesh->face_tree, &mesh->faces[i] );
|
|
|
|
Mem_Free( extradata, C_FILESYSTEM );
|
|
ent->modtype = mod_alias; // now our mesh is valid and ready to trace
|
|
mesh->backfrac = FloatForKey( ent, "zhlt_backfrac" );
|
|
mesh->flags = flags; // copy settings
|
|
mesh->modelCRC = modelCRC;
|
|
|
|
MsgDev( D_REPORT, "%s: build time %g secs, size %s\n", modname, I_FloatTime() - start, Q_memprint( memsize ));
|
|
}
|
|
|
|
void AliasGetBounds( 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_alias )
|
|
return;
|
|
|
|
VectorSubtract( mesh->absmin, ent->origin, mins );
|
|
VectorSubtract( mesh->absmax, ent->origin, maxs );
|
|
} |