|
|
|
@@ -1,760 +0,0 @@
|
|
|
|
|
/*
|
|
|
|
|
===========================================================================
|
|
|
|
|
Copyright (C) 2011 Thilo Schulz <thilo@tjps.eu>
|
|
|
|
|
Copyright (C) 2011 Matthias Bentrup <matthias.bentrup@googlemail.com>
|
|
|
|
|
|
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
|
|
|
|
|
|
Quake III Arena source code 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 2 of the License,
|
|
|
|
|
or (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
Quake III Arena source code 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.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with Quake III Arena source code; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
|
===========================================================================
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "tr_local.h"
|
|
|
|
|
|
|
|
|
|
#define LL(x) x=LittleLong(x)
|
|
|
|
|
|
|
|
|
|
typedef struct{
|
|
|
|
|
float matrix[3][4];
|
|
|
|
|
}boneMatrix_t;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
=================
|
|
|
|
|
LOADING CODE
|
|
|
|
|
=================
|
|
|
|
|
*/
|
|
|
|
|
/*
|
|
|
|
|
=================
|
|
|
|
|
R_LoadMDO
|
|
|
|
|
=================
|
|
|
|
|
*/
|
|
|
|
|
qboolean R_LoadMDO( model_t *mod, void *buffer, int filesize, const char *mod_name ) {
|
|
|
|
|
int i, j, lodindex;
|
|
|
|
|
mdoHeader_t *pinmodel, *mdo;
|
|
|
|
|
mdoFrame_t *frame;
|
|
|
|
|
mdoLOD_t *lod;
|
|
|
|
|
mdoSurface_t *surf;
|
|
|
|
|
mdoTriangle_t *tri;
|
|
|
|
|
mdoVertex_t *v;
|
|
|
|
|
mdoBoneInfo_t *bone;
|
|
|
|
|
int *boneRef;
|
|
|
|
|
int version;
|
|
|
|
|
int size;
|
|
|
|
|
shader_t *sh;
|
|
|
|
|
int frameSize;
|
|
|
|
|
|
|
|
|
|
pinmodel = (mdoHeader_t *)buffer;
|
|
|
|
|
|
|
|
|
|
if( Q_strncmp(pinmodel->ident, MDO_IDENT, sizeof(pinmodel->ident) ) ){ // failed
|
|
|
|
|
ri.Printf( PRINT_WARNING, "R_LoadMDO: %s has wrong ident (%s should be %s)\n",
|
|
|
|
|
mod_name, pinmodel->ident, MDO_IDENT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
version = LittleLong (pinmodel->version);
|
|
|
|
|
if (version != MDO_VERSION) {
|
|
|
|
|
ri.Printf( PRINT_WARNING, "R_LoadMDO: %s has wrong version (%i should be %i)\n",
|
|
|
|
|
mod_name, version, MDO_VERSION);
|
|
|
|
|
return qfalse;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mod->type = MOD_MDO;
|
|
|
|
|
|
|
|
|
|
size = LittleLong(pinmodel->ofsEnd);
|
|
|
|
|
if(size > filesize)
|
|
|
|
|
{
|
|
|
|
|
ri.Printf(PRINT_WARNING, "R_LoadMDO: Header of %s is broken. Wrong filesize declared!\n", mod_name);
|
|
|
|
|
return qfalse;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mod->dataSize += size;
|
|
|
|
|
mod->modelData = mdo = ri.Hunk_Alloc( size, h_low );
|
|
|
|
|
|
|
|
|
|
Com_Memcpy( mdo, buffer, LittleLong(pinmodel->ofsEnd) );
|
|
|
|
|
|
|
|
|
|
LL(mdo->version);
|
|
|
|
|
LL(mdo->numFrames);
|
|
|
|
|
LL(mdo->numBones);
|
|
|
|
|
LL(mdo->numLODs);
|
|
|
|
|
LL(mdo->ofsFrames);
|
|
|
|
|
LL(mdo->ofsLODs);
|
|
|
|
|
LL(mdo->ofsEnd);
|
|
|
|
|
|
|
|
|
|
if ( mdo->numFrames < 1 ) {
|
|
|
|
|
ri.Printf( PRINT_WARNING, "R_LoadMDO: %s has no frames\n", mod_name );
|
|
|
|
|
return qfalse;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( mdo->numLODs < 1){
|
|
|
|
|
ri.Printf( PRINT_WARNING, "R_LoadMDO: %s has no LODs\n", mod_name );
|
|
|
|
|
return qfalse;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mod->numLods = mdo->numLODs; // ... don't forget this or LODs won't work
|
|
|
|
|
|
|
|
|
|
// swap all the frames
|
|
|
|
|
frameSize = (size_t)( &((mdoFrame_t *)0)->bones[ mdo->numBones ] );
|
|
|
|
|
for ( i = 0 ; i < mdo->numFrames ; i++, frame++) {
|
|
|
|
|
frame = (mdoFrame_t *) ( (byte *)mdo + mdo->ofsFrames + i * frameSize );
|
|
|
|
|
frame->radius = LittleFloat( frame->radius );
|
|
|
|
|
for ( j = 0 ; j < 3 ; j++ ) {
|
|
|
|
|
frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] );
|
|
|
|
|
frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] );
|
|
|
|
|
frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for ( j = 0 ; j < mdo->numBones * sizeof( mdoBoneComp_t ) / sizeof( short ); j++ ) {
|
|
|
|
|
( (short *)frame->bones )[j] = LittleShort( ( (short *)frame->bones )[j] );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// swap all the bone infos
|
|
|
|
|
bone = (mdoBoneInfo_t *) ( (byte *)mdo + mdo->ofsBones );
|
|
|
|
|
for ( i = 0; i < mdo->numBones; i++, bone++ ){
|
|
|
|
|
// lowercase the bone name so name compares are faster
|
|
|
|
|
Q_strlwr( bone->name );
|
|
|
|
|
bone->flags = LittleLong(bone->flags);
|
|
|
|
|
bone->parent = LittleLong(bone->parent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// swap all the LOD's
|
|
|
|
|
lod = (mdoLOD_t *) ( (byte *)mdo + mdo->ofsLODs );
|
|
|
|
|
for ( lodindex = 0 ; lodindex < mdo->numLODs ; lodindex++ ) {
|
|
|
|
|
LL(lod->numSurfaces);
|
|
|
|
|
LL(lod->ofsSurfaces);
|
|
|
|
|
LL(lod->ofsEnd);
|
|
|
|
|
// swap all the surfaces
|
|
|
|
|
surf = (mdoSurface_t *) ( (byte *)lod + lod->ofsSurfaces );
|
|
|
|
|
for ( i = 0 ; i < lod->numSurfaces ; i++) {
|
|
|
|
|
//LL(surf->ident);
|
|
|
|
|
LL(surf->ofsHeader); // don't swap header offset
|
|
|
|
|
LL(surf->numTriangles);
|
|
|
|
|
LL(surf->ofsTriangles);
|
|
|
|
|
LL(surf->numVerts);
|
|
|
|
|
LL(surf->ofsVerts);
|
|
|
|
|
LL(surf->numBoneRefs);
|
|
|
|
|
LL(surf->ofsBoneRefs);
|
|
|
|
|
LL(surf->ofsEnd);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ( surf->numVerts > SHADER_MAX_VERTEXES ) {
|
|
|
|
|
ri.Error (ERR_DROP, "R_LoadMDO: %s has more than %i verts on a surface (%i)",
|
|
|
|
|
mod_name, SHADER_MAX_VERTEXES, surf->numVerts );
|
|
|
|
|
}
|
|
|
|
|
if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) {
|
|
|
|
|
ri.Error (ERR_DROP, "R_LoadMDO: %s has more than %i triangles on a surface (%i)",
|
|
|
|
|
mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// change to surface identifier
|
|
|
|
|
surf->ident = SF_MDO;
|
|
|
|
|
|
|
|
|
|
// lowercase the surface name so skin compares are faster
|
|
|
|
|
Q_strlwr( surf->name );
|
|
|
|
|
|
|
|
|
|
// register the shaders
|
|
|
|
|
sh = R_FindShader(surf->shader, LIGHTMAP_NONE, qtrue);
|
|
|
|
|
if ( sh->defaultShader ) {
|
|
|
|
|
surf->shaderIndex = 0;
|
|
|
|
|
} else {
|
|
|
|
|
surf->shaderIndex = sh->index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// swap all the vertexes and texcoords
|
|
|
|
|
v = (mdoVertex_t *) ( (byte *)surf + surf->ofsVerts);
|
|
|
|
|
for ( j = 0 ; j < surf->numVerts ; j++ ) {
|
|
|
|
|
v->xyz[0] = LittleFloat( v->xyz[0] );
|
|
|
|
|
v->xyz[1] = LittleFloat( v->xyz[1] );
|
|
|
|
|
v->xyz[2] = LittleFloat( v->xyz[2] );
|
|
|
|
|
|
|
|
|
|
v->texCoords[0] = LittleFloat( v->texCoords[0] );
|
|
|
|
|
v->texCoords[1] = LittleFloat( v->texCoords[1] );
|
|
|
|
|
|
|
|
|
|
v->normal = LittleShort( v->normal );
|
|
|
|
|
// num bone weights + weights[] are bytes
|
|
|
|
|
v = (mdoVertex_t *)&v->weights[v->numWeights];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// swap all the triangles
|
|
|
|
|
tri = (mdoTriangle_t *) ( (byte *)surf + surf->ofsTriangles );
|
|
|
|
|
for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) {
|
|
|
|
|
LL(tri->indexes[0]);
|
|
|
|
|
LL(tri->indexes[1]);
|
|
|
|
|
LL(tri->indexes[2]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// swap all the bone references
|
|
|
|
|
boneRef = ( int * )( ( byte *)surf + surf->ofsBoneRefs );
|
|
|
|
|
for ( j = 0; j < surf->numBoneRefs; j++, boneRef++ ){
|
|
|
|
|
*boneRef = LittleLong( *boneRef );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// find the next surface
|
|
|
|
|
surf = (mdoSurface_t *)( (byte *)surf + surf->ofsEnd );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// find the next LOD
|
|
|
|
|
lod = (mdoLOD_t *)( (byte *)lod + lod->ofsEnd );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return qtrue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
=================
|
|
|
|
|
DRAWING CODE
|
|
|
|
|
=================
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
=============
|
|
|
|
|
R_CullMDO
|
|
|
|
|
=============
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int R_CullMDO( mdoHeader_t *header, trRefEntity_t *ent ) {
|
|
|
|
|
vec3_t bounds[2];
|
|
|
|
|
mdoFrame_t *oldFrame, *newFrame;
|
|
|
|
|
int i, frameSize;
|
|
|
|
|
|
|
|
|
|
frameSize = (size_t)( &((mdoFrame_t *)0)->bones[ header->numBones ] );
|
|
|
|
|
|
|
|
|
|
// compute frame pointers
|
|
|
|
|
newFrame = ( mdoFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame);
|
|
|
|
|
oldFrame = ( mdoFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.oldframe);
|
|
|
|
|
|
|
|
|
|
// cull bounding sphere ONLY if this is not an upscaled entity
|
|
|
|
|
if ( !ent->e.nonNormalizedAxes )
|
|
|
|
|
{
|
|
|
|
|
if ( ent->e.frame == ent->e.oldframe )
|
|
|
|
|
{
|
|
|
|
|
switch ( R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ) )
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
case CULL_OUT:
|
|
|
|
|
tr.pc.c_sphere_cull_md3_out++;
|
|
|
|
|
return CULL_OUT;
|
|
|
|
|
|
|
|
|
|
case CULL_IN:
|
|
|
|
|
tr.pc.c_sphere_cull_md3_in++;
|
|
|
|
|
return CULL_IN;
|
|
|
|
|
|
|
|
|
|
case CULL_CLIP:
|
|
|
|
|
tr.pc.c_sphere_cull_md3_clip++;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int sphereCull, sphereCullB;
|
|
|
|
|
|
|
|
|
|
sphereCull = R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius );
|
|
|
|
|
if ( newFrame == oldFrame ) {
|
|
|
|
|
sphereCullB = sphereCull;
|
|
|
|
|
} else {
|
|
|
|
|
sphereCullB = R_CullLocalPointAndRadius( oldFrame->localOrigin, oldFrame->radius );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( sphereCull == sphereCullB )
|
|
|
|
|
{
|
|
|
|
|
if ( sphereCull == CULL_OUT )
|
|
|
|
|
{
|
|
|
|
|
tr.pc.c_sphere_cull_md3_out++;
|
|
|
|
|
return CULL_OUT;
|
|
|
|
|
}
|
|
|
|
|
else if ( sphereCull == CULL_IN )
|
|
|
|
|
{
|
|
|
|
|
tr.pc.c_sphere_cull_md3_in++;
|
|
|
|
|
return CULL_IN;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
tr.pc.c_sphere_cull_md3_clip++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// calculate a bounding box in the current coordinate system
|
|
|
|
|
for (i = 0 ; i < 3 ; i++) {
|
|
|
|
|
bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i];
|
|
|
|
|
bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch ( R_CullLocalBox( bounds ) )
|
|
|
|
|
{
|
|
|
|
|
case CULL_IN:
|
|
|
|
|
tr.pc.c_box_cull_md3_in++;
|
|
|
|
|
return CULL_IN;
|
|
|
|
|
case CULL_CLIP:
|
|
|
|
|
tr.pc.c_box_cull_md3_clip++;
|
|
|
|
|
return CULL_CLIP;
|
|
|
|
|
case CULL_OUT:
|
|
|
|
|
default:
|
|
|
|
|
tr.pc.c_box_cull_md3_out++;
|
|
|
|
|
return CULL_OUT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
=================
|
|
|
|
|
R_ComputeMDOFogNum
|
|
|
|
|
|
|
|
|
|
=================
|
|
|
|
|
*/
|
|
|
|
|
int R_ComputeMDOFogNum( mdoHeader_t *header, trRefEntity_t *ent ) {
|
|
|
|
|
int i, j;
|
|
|
|
|
fog_t *fog;
|
|
|
|
|
mdoFrame_t *frame;
|
|
|
|
|
vec3_t localOrigin;
|
|
|
|
|
int frameSize;
|
|
|
|
|
|
|
|
|
|
if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
frameSize = (size_t)( &((mdoFrame_t *)0)->bones[ header->numBones ] );
|
|
|
|
|
|
|
|
|
|
// FIXME: non-normalized axis issues
|
|
|
|
|
frame = ( mdoFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame);
|
|
|
|
|
VectorAdd( ent->e.origin, frame->localOrigin, localOrigin );
|
|
|
|
|
for ( i = 1 ; i < tr.world->numfogs ; i++ ) {
|
|
|
|
|
fog = &tr.world->fogs[i];
|
|
|
|
|
for ( j = 0 ; j < 3 ; j++ ) {
|
|
|
|
|
if ( localOrigin[j] - frame->radius >= fog->bounds[1][j] ) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if ( localOrigin[j] + frame->radius <= fog->bounds[0][j] ) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( j == 3 ) {
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
==============
|
|
|
|
|
R_AddMDOSurfaces
|
|
|
|
|
==============
|
|
|
|
|
*/
|
|
|
|
|
void R_AddMDOSurfaces( trRefEntity_t *ent ) {
|
|
|
|
|
mdoHeader_t *header;
|
|
|
|
|
mdoSurface_t *surface;
|
|
|
|
|
mdoLOD_t *lod;
|
|
|
|
|
shader_t *shader;
|
|
|
|
|
skin_t *skin;
|
|
|
|
|
int i, j;
|
|
|
|
|
int lodnum = 0;
|
|
|
|
|
int fogNum = 0;
|
|
|
|
|
int cull;
|
|
|
|
|
qboolean personalModel;
|
|
|
|
|
|
|
|
|
|
header = (mdoHeader_t *) tr.currentModel->modelData;
|
|
|
|
|
|
|
|
|
|
personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal;
|
|
|
|
|
|
|
|
|
|
if ( ent->e.renderfx & RF_WRAP_FRAMES )
|
|
|
|
|
{
|
|
|
|
|
ent->e.frame %= header->numFrames;
|
|
|
|
|
ent->e.oldframe %= header->numFrames;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Validate the frames so there is no chance of a crash.
|
|
|
|
|
// This will write directly into the entity structure, so
|
|
|
|
|
// when the surfaces are rendered, they don't need to be
|
|
|
|
|
// range checked again.
|
|
|
|
|
//
|
|
|
|
|
if ((ent->e.frame >= header->numFrames)
|
|
|
|
|
|| (ent->e.frame < 0)
|
|
|
|
|
|| (ent->e.oldframe >= header->numFrames)
|
|
|
|
|
|| (ent->e.oldframe < 0) )
|
|
|
|
|
{
|
|
|
|
|
ri.Printf( PRINT_DEVELOPER, "R_AddMDOSurfaces: no such frame %d to %d for '%s'\n",
|
|
|
|
|
ent->e.oldframe, ent->e.frame, tr.currentModel->name );
|
|
|
|
|
ent->e.frame = 0;
|
|
|
|
|
ent->e.oldframe = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// cull the entire model if merged bounding box of both frames
|
|
|
|
|
// is outside the view frustum.
|
|
|
|
|
//
|
|
|
|
|
cull = R_CullMDO (header, ent);
|
|
|
|
|
if ( cull == CULL_OUT ) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// figure out the current LOD of the model we're rendering, and set the lod pointer respectively.
|
|
|
|
|
lodnum = R_ComputeLOD(ent);
|
|
|
|
|
// check whether this model has as that many LODs at all. If not, try the closest thing we got.
|
|
|
|
|
if(header->numLODs <= 0)
|
|
|
|
|
return;
|
|
|
|
|
if(header->numLODs <= lodnum)
|
|
|
|
|
lodnum = header->numLODs - 1;
|
|
|
|
|
|
|
|
|
|
lod = (mdoLOD_t *)( (byte *)header + header->ofsLODs);
|
|
|
|
|
for(i = 0; i < lodnum; i++)
|
|
|
|
|
{
|
|
|
|
|
lod = (mdoLOD_t *) ((byte *) lod + lod->ofsEnd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set up lighting
|
|
|
|
|
if ( !personalModel || r_shadows->integer > 1 )
|
|
|
|
|
{
|
|
|
|
|
R_SetupEntityLighting( &tr.refdef, ent );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// fogNum
|
|
|
|
|
fogNum = R_ComputeMDOFogNum( header, ent );
|
|
|
|
|
|
|
|
|
|
surface = (mdoSurface_t *)( (byte *)lod + lod->ofsSurfaces );
|
|
|
|
|
|
|
|
|
|
for ( i = 0 ; i < lod->numSurfaces ; i++ )
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if(ent->e.customShader)
|
|
|
|
|
shader = R_GetShaderByHandle(ent->e.customShader);
|
|
|
|
|
else if(ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins)
|
|
|
|
|
{
|
|
|
|
|
skin = R_GetSkinByHandle(ent->e.customSkin);
|
|
|
|
|
shader = tr.defaultShader;
|
|
|
|
|
|
|
|
|
|
for(j = 0; j < skin->numSurfaces; j++)
|
|
|
|
|
{
|
|
|
|
|
if (!strcmp(skin->surfaces[j]->name, surface->name))
|
|
|
|
|
{
|
|
|
|
|
shader = skin->surfaces[j]->shader;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(surface->shaderIndex > 0)
|
|
|
|
|
shader = R_GetShaderByHandle( surface->shaderIndex );
|
|
|
|
|
else
|
|
|
|
|
shader = tr.defaultShader;
|
|
|
|
|
|
|
|
|
|
// we will add shadows even if the main object isn't visible in the view
|
|
|
|
|
|
|
|
|
|
// stencil shadows can't do personal models unless I polyhedron clip
|
|
|
|
|
if ( !personalModel
|
|
|
|
|
&& r_shadows->integer == 2
|
|
|
|
|
&& fogNum == 0
|
|
|
|
|
&& !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) )
|
|
|
|
|
&& shader->sort == SS_OPAQUE )
|
|
|
|
|
{
|
|
|
|
|
R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// projection shadows work fine with personal models
|
|
|
|
|
if ( r_shadows->integer == 3
|
|
|
|
|
&& fogNum == 0
|
|
|
|
|
&& (ent->e.renderfx & RF_SHADOW_PLANE )
|
|
|
|
|
&& shader->sort == SS_OPAQUE )
|
|
|
|
|
{
|
|
|
|
|
R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!personalModel)
|
|
|
|
|
R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse );
|
|
|
|
|
|
|
|
|
|
surface = (mdoSurface_t *)( (byte *)surface + surface->ofsEnd );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
=================
|
|
|
|
|
ANIMATION
|
|
|
|
|
=================
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void CompBoneToMatrix( const vec4_t quat, vec3_t trans, float matrix[3][4] ) {
|
|
|
|
|
float xx = 2.0f * quat[0] * quat[0];
|
|
|
|
|
float yy = 2.0f * quat[1] * quat[1];
|
|
|
|
|
float zz = 2.0f * quat[2] * quat[2];
|
|
|
|
|
float xy = 2.0f * quat[0] * quat[1];
|
|
|
|
|
float xz = 2.0f * quat[0] * quat[2];
|
|
|
|
|
float yz = 2.0f * quat[1] * quat[2];
|
|
|
|
|
float wx = 2.0f * quat[3] * quat[0];
|
|
|
|
|
float wy = 2.0f * quat[3] * quat[1];
|
|
|
|
|
float wz = 2.0f * quat[3] * quat[2];
|
|
|
|
|
|
|
|
|
|
matrix[0][0] = ( 1.0f - ( yy + zz ) );
|
|
|
|
|
matrix[0][1] = ( xy - wz );
|
|
|
|
|
matrix[0][2] = ( xz + wy );
|
|
|
|
|
matrix[0][3] = trans[0];
|
|
|
|
|
|
|
|
|
|
matrix[1][0] = ( xy + wz);
|
|
|
|
|
matrix[1][1] = ( 1.0f - ( xx + zz ) );
|
|
|
|
|
matrix[1][2] = ( yz - wx);
|
|
|
|
|
matrix[1][3] = trans[1];
|
|
|
|
|
|
|
|
|
|
matrix[2][0] = ( xz - wy );
|
|
|
|
|
matrix[2][1] = ( yz + wx );
|
|
|
|
|
matrix[2][2] = ( 1.0f - ( xx + yy ) );
|
|
|
|
|
matrix[2][3] = trans[2];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void R_UncompressBone( int boneIndex, mdoFrame_t *frame, float matrix[3][4] )
|
|
|
|
|
{
|
|
|
|
|
mdoBoneComp_t *bone = &frame->bones[boneIndex];
|
|
|
|
|
vec4_t quat;
|
|
|
|
|
vec3_t trans;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
quat[0] = bone->rotQuat[0] * MDO_QUAT_SCALE;
|
|
|
|
|
quat[1] = bone->rotQuat[1] * MDO_QUAT_SCALE;
|
|
|
|
|
quat[2] = bone->rotQuat[2] * MDO_QUAT_SCALE;
|
|
|
|
|
quat[3] = bone->rotQuat[3] * MDO_QUAT_SCALE;
|
|
|
|
|
|
|
|
|
|
trans[0] = bone->translation[0] * MDO_TRANSLATION_SCALE;
|
|
|
|
|
trans[1] = bone->translation[1] * MDO_TRANSLATION_SCALE;
|
|
|
|
|
trans[2] = bone->translation[2] * MDO_TRANSLATION_SCALE;
|
|
|
|
|
|
|
|
|
|
CompBoneToMatrix( quat, trans, matrix );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Matrix3x4Lerp( float mat1[3][4], float mat2[3][4], float blerp, float out[3][4], qboolean slerp )
|
|
|
|
|
{
|
|
|
|
|
float flerp = 1.0f - blerp;
|
|
|
|
|
|
|
|
|
|
out[0][0] = flerp * mat1[0][0] + blerp * mat2[0][0];
|
|
|
|
|
out[0][1] = flerp * mat1[0][1] + blerp * mat2[0][1];
|
|
|
|
|
out[0][2] = flerp * mat1[0][2] + blerp * mat2[0][2];
|
|
|
|
|
out[0][3] = flerp * mat1[0][3] + blerp * mat2[0][3];
|
|
|
|
|
|
|
|
|
|
out[1][0] = flerp * mat1[1][0] + blerp * mat2[1][0];
|
|
|
|
|
out[1][1] = flerp * mat1[1][1] + blerp * mat2[1][1];
|
|
|
|
|
out[1][2] = flerp * mat1[1][2] + blerp * mat2[1][2];
|
|
|
|
|
out[1][3] = flerp * mat1[1][3] + blerp * mat2[1][3];
|
|
|
|
|
|
|
|
|
|
out[2][0] = flerp * mat1[2][0] + blerp * mat2[2][0];
|
|
|
|
|
out[2][1] = flerp * mat1[2][1] + blerp * mat2[2][1];
|
|
|
|
|
out[2][2] = flerp * mat1[2][2] + blerp * mat2[2][2];
|
|
|
|
|
out[2][3] = flerp * mat1[2][3] + blerp * mat2[2][3];
|
|
|
|
|
|
|
|
|
|
if( slerp ){ // unsquash the rotations
|
|
|
|
|
VectorNormalize( out[0] );
|
|
|
|
|
VectorNormalize( out[1] );
|
|
|
|
|
VectorNormalize( out[2] );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
================
|
|
|
|
|
R_GetMDOTag
|
|
|
|
|
================
|
|
|
|
|
*/
|
|
|
|
|
md3Tag_t *R_GetMDOTag( mdoHeader_t *mod, int framenum, const char *tagName, md3Tag_t * dest)
|
|
|
|
|
{
|
|
|
|
|
int i, j, k;
|
|
|
|
|
int frameSize;
|
|
|
|
|
mdoFrame_t *frame;
|
|
|
|
|
mdoBoneInfo_t *bone;
|
|
|
|
|
|
|
|
|
|
if ( framenum >= mod->numFrames )
|
|
|
|
|
{
|
|
|
|
|
// it is possible to have a bad frame while changing models, so don't error
|
|
|
|
|
framenum = mod->numFrames - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bone = (mdoBoneInfo_t *)((byte *)mod + mod->ofsBones);
|
|
|
|
|
for ( i = 0; i < mod->numBones; i++, bone++ )
|
|
|
|
|
{
|
|
|
|
|
if( !(bone->flags & BFLAG_TAG) ){
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( !strcmp( bone->name, tagName ) )
|
|
|
|
|
{
|
|
|
|
|
float matrix[3][4];
|
|
|
|
|
|
|
|
|
|
Q_strncpyz(dest->name, bone->name, sizeof(dest->name));
|
|
|
|
|
|
|
|
|
|
frameSize = (size_t)( &((mdoFrame_t *)0)->bones[ mod->numBones ] );
|
|
|
|
|
frame = (mdoFrame_t *)((byte *)mod + mod->ofsFrames + framenum * frameSize );
|
|
|
|
|
|
|
|
|
|
R_UncompressBone( i, frame, matrix);
|
|
|
|
|
|
|
|
|
|
for (j = 0; j < 3; j++)
|
|
|
|
|
{
|
|
|
|
|
for (k = 0; k < 3; k++){
|
|
|
|
|
dest->axis[j][k] = matrix[k][j];
|
|
|
|
|
}
|
|
|
|
|
dest->origin[j] = matrix[j][3];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dest;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
================
|
|
|
|
|
RB_SurfaceMDO
|
|
|
|
|
================
|
|
|
|
|
*/
|
|
|
|
|
#define FTABLESIZE_DIV_256 4 // optimizations, may want to move this right next to the functables
|
|
|
|
|
#define FTABLESIZE_DIV_4 256 // and use it for other functions that use them
|
|
|
|
|
void RB_SurfaceMDO( mdoSurface_t *surface )
|
|
|
|
|
{
|
|
|
|
|
int i, j, k;
|
|
|
|
|
int frameSize;
|
|
|
|
|
float backlerp;
|
|
|
|
|
int *triangles;
|
|
|
|
|
int indexes;
|
|
|
|
|
int baseIndex, baseVertex;
|
|
|
|
|
int numVerts;
|
|
|
|
|
int *boneRefList;
|
|
|
|
|
int *boneRef;
|
|
|
|
|
mdoVertex_t *v;
|
|
|
|
|
mdoHeader_t *header;
|
|
|
|
|
mdoFrame_t *frame;
|
|
|
|
|
mdoFrame_t *oldFrame;
|
|
|
|
|
boneMatrix_t bones[MDO_MAX_BONES], *bonePtr, *bone;
|
|
|
|
|
refEntity_t *ent;
|
|
|
|
|
|
|
|
|
|
ent = &backEnd.currentEntity->e;
|
|
|
|
|
|
|
|
|
|
// don't lerp if lerping off, or this is the only frame, or the last frame...
|
|
|
|
|
//
|
|
|
|
|
if (ent->oldframe == ent->frame)
|
|
|
|
|
{
|
|
|
|
|
backlerp = 0; // if backlerp is 0, lerping is off and frontlerp is never used
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
backlerp = ent->backlerp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
header = (mdoHeader_t *)((byte *)surface - surface->ofsHeader);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Set up all triangles.
|
|
|
|
|
//
|
|
|
|
|
RB_CHECKOVERFLOW( surface->numVerts, surface->numTriangles * 3 );
|
|
|
|
|
triangles = (int *) ((byte *)surface + surface->ofsTriangles);
|
|
|
|
|
indexes = surface->numTriangles * 3;
|
|
|
|
|
baseIndex = tess.numIndexes;
|
|
|
|
|
baseVertex = tess.numVertexes;
|
|
|
|
|
|
|
|
|
|
for (j = 0 ; j < indexes ; j++)
|
|
|
|
|
{
|
|
|
|
|
tess.indexes[baseIndex + j] = baseVertex + triangles[j];
|
|
|
|
|
}
|
|
|
|
|
tess.numIndexes += indexes;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Setup and lerp all the needed bones
|
|
|
|
|
//
|
|
|
|
|
frameSize = (size_t)( &((mdoFrame_t *)0)->bones[ header->numBones ] );
|
|
|
|
|
|
|
|
|
|
frame = (mdoFrame_t *)((byte *)header + header->ofsFrames + ent->frame * frameSize );
|
|
|
|
|
oldFrame = (mdoFrame_t *)((byte *)header + header->ofsFrames + ent->oldframe * frameSize );
|
|
|
|
|
|
|
|
|
|
// boneRefs
|
|
|
|
|
boneRefList = ( int * )( (byte *)surface + surface->ofsBoneRefs );
|
|
|
|
|
boneRef = boneRefList;
|
|
|
|
|
|
|
|
|
|
bonePtr = bones;
|
|
|
|
|
for( i = 0 ; i < surface->numBoneRefs ; i++, boneRef++ )
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if( !backlerp ){ // no need for cache, animation is simple
|
|
|
|
|
R_UncompressBone( *boneRef, frame, bonePtr[*boneRef].matrix );
|
|
|
|
|
}else{
|
|
|
|
|
float mat1[3][4], mat2[3][4];
|
|
|
|
|
R_UncompressBone( *boneRef, frame, mat1 );
|
|
|
|
|
R_UncompressBone( *boneRef, oldFrame, mat2 );
|
|
|
|
|
Matrix3x4Lerp(mat1, mat2, backlerp, bonePtr[*boneRef].matrix, qtrue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// deform the vertexes by the lerped bones
|
|
|
|
|
//
|
|
|
|
|
numVerts = surface->numVerts;
|
|
|
|
|
v = (mdoVertex_t *) ((byte *)surface + surface->ofsVerts);
|
|
|
|
|
for ( j = 0; j < numVerts; j++ )
|
|
|
|
|
{
|
|
|
|
|
vec3_t vertex, normal;
|
|
|
|
|
vec3_t unCompNormal;
|
|
|
|
|
unsigned int lat, lng;
|
|
|
|
|
|
|
|
|
|
VectorClear( vertex );
|
|
|
|
|
VectorClear( normal );
|
|
|
|
|
|
|
|
|
|
// Uncompress the normal
|
|
|
|
|
lat = ( v->normal >> 8 ) & 0xff;
|
|
|
|
|
lng = ( v->normal & 0xff );
|
|
|
|
|
lat *= (FTABLESIZE_DIV_256);
|
|
|
|
|
lng *= (FTABLESIZE_DIV_256);
|
|
|
|
|
|
|
|
|
|
unCompNormal[0] = tr.sinTable[(lat+(FTABLESIZE_DIV_4))&FUNCTABLE_MASK] * tr.sinTable[lng];
|
|
|
|
|
unCompNormal[1] = tr.sinTable[lat] * tr.sinTable[lng];
|
|
|
|
|
unCompNormal[2] = tr.sinTable[(lng+(FTABLESIZE_DIV_4))&FUNCTABLE_MASK];
|
|
|
|
|
|
|
|
|
|
if( !v->numWeights || !header->numBones ){ // static model, or un weighted vertex
|
|
|
|
|
vertex[0] = v->xyz[0];
|
|
|
|
|
vertex[1] = v->xyz[1];
|
|
|
|
|
vertex[2] = v->xyz[2];
|
|
|
|
|
|
|
|
|
|
normal[0] = unCompNormal[0];
|
|
|
|
|
normal[1] = unCompNormal[1];
|
|
|
|
|
normal[2] = unCompNormal[2];
|
|
|
|
|
|
|
|
|
|
VectorNormalizeFast(normal); // ensure a normalized normal
|
|
|
|
|
}else{
|
|
|
|
|
mdoWeight_t *w;
|
|
|
|
|
w = v->weights;
|
|
|
|
|
for( k = 0; k < v->numWeights; k++, w++ ){
|
|
|
|
|
float fWeight = w->boneWeight * MDO_BYTE_WEIGHT_SCALE;
|
|
|
|
|
int boneIndex = w->boneIndex;
|
|
|
|
|
|
|
|
|
|
bone = bonePtr + boneIndex;
|
|
|
|
|
// Transform the vert and normal
|
|
|
|
|
vertex[0] += fWeight * ( DotProduct( bone->matrix[0], v->xyz ) + bone->matrix[0][3] );
|
|
|
|
|
vertex[1] += fWeight * ( DotProduct( bone->matrix[1], v->xyz ) + bone->matrix[1][3] );
|
|
|
|
|
vertex[2] += fWeight * ( DotProduct( bone->matrix[2], v->xyz ) + bone->matrix[2][3] );
|
|
|
|
|
|
|
|
|
|
normal[0] += fWeight * DotProduct( bone->matrix[0], unCompNormal );
|
|
|
|
|
normal[1] += fWeight * DotProduct( bone->matrix[1], unCompNormal );
|
|
|
|
|
normal[2] += fWeight * DotProduct( bone->matrix[2], unCompNormal );
|
|
|
|
|
}
|
|
|
|
|
VectorNormalizeFast(normal); // ensure a normalized normal
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tess.xyz[baseVertex + j][0] = vertex[0];
|
|
|
|
|
tess.xyz[baseVertex + j][1] = vertex[1];
|
|
|
|
|
tess.xyz[baseVertex + j][2] = vertex[2];
|
|
|
|
|
|
|
|
|
|
tess.normal[baseVertex + j][0] = normal[0];
|
|
|
|
|
tess.normal[baseVertex + j][1] = normal[1];
|
|
|
|
|
tess.normal[baseVertex + j][2] = normal[2];
|
|
|
|
|
|
|
|
|
|
tess.texCoords[baseVertex + j][0][0] = v->texCoords[0];
|
|
|
|
|
tess.texCoords[baseVertex + j][0][1] = v->texCoords[1];
|
|
|
|
|
|
|
|
|
|
v = (mdoVertex_t *)&v->weights[v->numWeights];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tess.numVertexes += surface->numVerts;
|
|
|
|
|
}
|
|
|
|
|
|