Preliminary MDO model support from revanic
This commit is contained in:
2
Makefile
2
Makefile
@@ -1704,6 +1704,7 @@ Q3ROAOBJ = \
|
||||
$(B)/renderer_oa/tr_mesh.o \
|
||||
$(B)/renderer_oa/tr_model.o \
|
||||
$(B)/renderer_oa/tr_model_iqm.o \
|
||||
$(B)/renderer_oa/tr_model_mdo.o \
|
||||
$(B)/renderer_oa/tr_noise.o \
|
||||
$(B)/renderer_oa/tr_scene.o \
|
||||
$(B)/renderer_oa/tr_shade.o \
|
||||
@@ -1777,6 +1778,7 @@ Q3RSOFTOBJ = \
|
||||
$(B)/renderer_oa/tr_mesh.o \
|
||||
$(B)/renderer_oa/tr_model.o \
|
||||
$(B)/renderer_oa/tr_model_iqm.o \
|
||||
$(B)/renderer_oa/tr_model_mdo.o \
|
||||
$(B)/renderer_oa/tr_noise.o \
|
||||
$(B)/renderer_oa/tr_scene.o \
|
||||
$(B)/renderer_oa/tr_shade.o \
|
||||
|
@@ -35,7 +35,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#endif
|
||||
|
||||
// surface geometry should not exceed these limits
|
||||
#define SHADER_MAX_VERTEXES 1000
|
||||
#define SHADER_MAX_VERTEXES 4000
|
||||
#define SHADER_MAX_INDEXES (6*SHADER_MAX_VERTEXES)
|
||||
|
||||
|
||||
|
@@ -31,6 +31,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#include "../renderercommon/tr_public.h"
|
||||
#include "../renderercommon/tr_common.h"
|
||||
#include "../renderercommon/iqm.h"
|
||||
#include "tr_mdo.h"
|
||||
#include "../renderercommon/qgl.h"
|
||||
|
||||
#define GL_INDEX_TYPE GL_UNSIGNED_INT
|
||||
@@ -526,6 +527,7 @@ typedef enum {
|
||||
SF_POLY,
|
||||
SF_MD3,
|
||||
SF_MDR,
|
||||
SF_MDO,
|
||||
SF_IQM,
|
||||
SF_FLARE,
|
||||
SF_ENTITY, // beams, rails, lightning, etc that can be determined by entity
|
||||
@@ -926,6 +928,7 @@ typedef enum {
|
||||
MOD_BRUSH,
|
||||
MOD_MESH,
|
||||
MOD_MDR,
|
||||
MOD_MDO,
|
||||
MOD_IQM
|
||||
} modtype_t;
|
||||
|
||||
@@ -937,7 +940,7 @@ typedef struct model_s {
|
||||
int dataSize; // just for listing purposes
|
||||
bmodel_t *bmodel; // only if type == MOD_BRUSH
|
||||
md3Header_t *md3[MD3_MAX_LODS]; // only if type == MOD_MESH
|
||||
void *modelData; // only if type == (MOD_MDR | MOD_IQM)
|
||||
void *modelData; // only if type == (MOD_MDR | MOD_IQM | MOD_MDO)
|
||||
|
||||
int numLods;
|
||||
} model_t;
|
||||
|
@@ -23,6 +23,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
|
||||
#include <string.h> // memcpy
|
||||
|
||||
trGlobals_t tr;
|
||||
@@ -43,6 +44,9 @@ refimport_t ri;
|
||||
// point at this for their sorting surface
|
||||
surfaceType_t entitySurface = SF_ENTITY;
|
||||
|
||||
|
||||
void R_AddMDOSurfaces( trRefEntity_t *ent );
|
||||
|
||||
/*
|
||||
=================
|
||||
R_CullLocalBox
|
||||
@@ -1245,6 +1249,9 @@ void R_AddEntitySurfaces (void) {
|
||||
case MOD_MDR:
|
||||
R_MDRAddAnimSurfaces( ent );
|
||||
break;
|
||||
case MOD_MDO:
|
||||
R_AddMDOSurfaces( ent );
|
||||
break;
|
||||
case MOD_IQM:
|
||||
R_AddIQMSurfaces( ent );
|
||||
break;
|
||||
|
116
code/renderer_oa/tr_mdo.h
Normal file
116
code/renderer_oa/tr_mdo.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
.MDO Open Model format - supports simple skeletal animation and static meshes
|
||||
v2 - removed frame name, and converted boneframe to compressed using quats
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#define MDO_IDENT "OPENMDL2"
|
||||
#define MDO_VERSION 2
|
||||
#define MDO_MAX_BONES 128
|
||||
#define MDO_MAX_WEIGHTS 4
|
||||
|
||||
// compression scales
|
||||
#define MDO_TRANSLATION_SCALE (1.0 / 64)
|
||||
#define MDO_QUAT_SCALE (1.0 / 768)
|
||||
#define MDO_BYTE_WEIGHT_SCALE (1.0 / 255)
|
||||
|
||||
// bone flags
|
||||
#define BFLAG_TAG 1
|
||||
|
||||
typedef struct {
|
||||
unsigned char boneIndex;
|
||||
unsigned char boneWeight;
|
||||
}mdoWeight_t;
|
||||
|
||||
typedef struct {
|
||||
float xyz[3];
|
||||
float texCoords[2];
|
||||
unsigned short normal; // compressed normal
|
||||
unsigned char numWeights;
|
||||
mdoWeight_t weights[1];
|
||||
} mdoVertex_t;
|
||||
|
||||
typedef struct {
|
||||
int indexes[3];
|
||||
} mdoTriangle_t;
|
||||
|
||||
typedef struct {
|
||||
int ident; // for renderer use
|
||||
|
||||
char name[MAX_QPATH]; // polyset name
|
||||
char shader[MAX_QPATH];
|
||||
int shaderIndex; // for in-game use
|
||||
|
||||
int ofsHeader; // subtract this to get header
|
||||
|
||||
int numVerts;
|
||||
int numTriangles;
|
||||
int numBoneRefs;
|
||||
int ofsVerts;
|
||||
int ofsTriangles;
|
||||
int ofsBoneRefs;
|
||||
|
||||
int ofsEnd; // next surface follows
|
||||
} mdoSurface_t;
|
||||
|
||||
typedef struct {
|
||||
short rotQuat[4]; // always normalized
|
||||
short translation[3];
|
||||
} mdoBoneComp_t;
|
||||
|
||||
typedef struct {
|
||||
char name[32];
|
||||
int parent;
|
||||
int flags;
|
||||
} mdoBoneInfo_t;
|
||||
|
||||
typedef struct {
|
||||
float bounds[2][3]; // bounds of all surfaces of all LOD's for this frame
|
||||
float localOrigin[3]; // midpoint of bounds, used for sphere cull
|
||||
float radius; // dist from localOrigin to corner
|
||||
mdoBoneComp_t bones[1]; // [numBones], bone mats
|
||||
} mdoFrame_t;
|
||||
|
||||
typedef struct {
|
||||
int numSurfaces;
|
||||
int ofsSurfaces; // first surface, others follow
|
||||
int ofsEnd; // next lod follows
|
||||
} mdoLOD_t;
|
||||
|
||||
typedef struct {
|
||||
char ident[8];
|
||||
int version;
|
||||
|
||||
// frames and bones are shared by all levels of detail
|
||||
int numFrames;
|
||||
int numBones;
|
||||
int numLODs;
|
||||
|
||||
int ofsFrames;
|
||||
int ofsBones; // boneinfos
|
||||
int ofsLODs;
|
||||
|
||||
int ofsEnd;
|
||||
} mdoHeader_t;
|
@@ -27,6 +27,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
static qboolean R_LoadMD3(model_t *mod, int lod, void *buffer, const char *name );
|
||||
static qboolean R_LoadMDR(model_t *mod, void *buffer, int filesize, const char *name );
|
||||
qboolean R_LoadMDO( model_t *mod, void *buffer, int filesize, const char *mod_name );
|
||||
extern int ismaptexture; // leilei - for listing map textures
|
||||
/*
|
||||
====================
|
||||
@@ -182,6 +183,47 @@ qhandle_t R_RegisterIQM(const char *name, model_t *mod)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
R_RegisterMDO
|
||||
====================
|
||||
*/
|
||||
qhandle_t R_RegisterMDO(const char *name, model_t *mod)
|
||||
{
|
||||
union {
|
||||
unsigned *u;
|
||||
void *v;
|
||||
} buf;
|
||||
int ident;
|
||||
qboolean loaded = qfalse;
|
||||
int filesize;
|
||||
|
||||
filesize = ri.FS_ReadFile(name, (void **) &buf.v);
|
||||
if(!buf.u)
|
||||
{
|
||||
mod->type = MOD_BAD;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ident = LittleLong(*(unsigned *)buf.u);
|
||||
if(ident == MDO_IDENT)
|
||||
loaded = R_LoadMDO(mod, buf.u, filesize, name);
|
||||
|
||||
ri.FS_FreeFile (buf.v);
|
||||
|
||||
if(!loaded)
|
||||
{
|
||||
ri.Printf(PRINT_WARNING,"R_RegisterMDO: couldn't load mdo file %s\n", name);
|
||||
mod->type = MOD_BAD;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mod->index;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *ext;
|
||||
@@ -193,10 +235,10 @@ typedef struct
|
||||
static modelExtToLoaderMap_t modelLoaders[ ] =
|
||||
{
|
||||
{ "iqm", R_RegisterIQM },
|
||||
{ "mdo", R_RegisterMDO },
|
||||
{ "mdr", R_RegisterMDR },
|
||||
{ "md3", R_RegisterMD3 }
|
||||
};
|
||||
|
||||
static int numModelLoaders = ARRAY_LEN(modelLoaders);
|
||||
|
||||
//===============================================================================
|
||||
|
768
code/renderer_oa/tr_model_mdo.c
Normal file
768
code/renderer_oa/tr_model_mdo.c
Normal file
@@ -0,0 +1,768 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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, k, 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 = (int)( &((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, k;
|
||||
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 ) )
|
||||
{
|
||||
vec4_t quat;
|
||||
vec3_t trans;
|
||||
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 frontlerp, 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;
|
||||
mdoBoneInfo_t *boneInfo;
|
||||
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
|
||||
frontlerp = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
backlerp = ent->backlerp;
|
||||
frontlerp = 1.0f - 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;
|
||||
|
||||
// boneInfos
|
||||
boneInfo = (mdoBoneInfo_t *)((byte *)header + header->ofsBones);
|
||||
|
||||
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;
|
||||
}
|
||||
|
@@ -854,6 +854,7 @@ static void LerpMeshVertexes(md3Surface_t *surf, float backlerp)
|
||||
LerpMeshVertexes_scalar( surf, backlerp );
|
||||
}
|
||||
|
||||
void RB_SurfaceMDO( mdoSurface_t *surface );
|
||||
|
||||
/*
|
||||
=============
|
||||
@@ -1300,6 +1301,7 @@ void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])( void *) = {
|
||||
(void(*)(void*))RB_SurfacePolychain, // SF_POLY,
|
||||
(void(*)(void*))RB_SurfaceMesh, // SF_MD3,
|
||||
(void(*)(void*))RB_MDRSurfaceAnim, // SF_MDR,
|
||||
(void(*)(void*))RB_SurfaceMDO, // SF_MDO,
|
||||
(void(*)(void*))RB_IQMSurfaceAnim, // SF_IQM,
|
||||
(void(*)(void*))RB_SurfaceFlare, // SF_FLARE,
|
||||
(void(*)(void*))RB_SurfaceEntity, // SF_ENTITY
|
||||
|
Reference in New Issue
Block a user