Initial commit.
This commit is contained in:
478
code/renderer/tr_animation.cpp
Normal file
478
code/renderer/tr_animation.cpp
Normal file
@@ -0,0 +1,478 @@
|
||||
// leave this as first line for PCH reasons...
|
||||
//
|
||||
#include "../server/exe_headers.h"
|
||||
|
||||
|
||||
#include "tr_local.h"
|
||||
#include "MatComp.h"
|
||||
|
||||
#ifdef VV_LIGHTING
|
||||
#include "tr_lightmanager.h"
|
||||
#endif
|
||||
|
||||
extern int R_ComputeLOD( trRefEntity_t *ent );
|
||||
|
||||
/*
|
||||
|
||||
All bones should be an identity orientation to display the mesh exactly
|
||||
as it is specified.
|
||||
|
||||
For all other frames, the bones represent the transformation from the
|
||||
orientation of the bone in the base frame to the orientation in this
|
||||
frame.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
R_ACullModel
|
||||
=============
|
||||
*/
|
||||
static int R_ACullModel( md4Header_t *header, trRefEntity_t *ent ) {
|
||||
vec3_t bounds[2];
|
||||
md4Frame_t *oldFrame, *newFrame;
|
||||
int i;
|
||||
int frameSize;
|
||||
// compute frame pointers
|
||||
|
||||
if (header->ofsFrames<0) // Compressed
|
||||
{
|
||||
frameSize = (int)( &((md4CompFrame_t *)0)->bones[ tr.currentModel->md4->numBones ] );
|
||||
newFrame = (md4Frame_t *)((byte *)header - header->ofsFrames + ent->e.frame * frameSize );
|
||||
oldFrame = (md4Frame_t *)((byte *)header - header->ofsFrames + ent->e.oldframe * frameSize );
|
||||
// HACK! These frames actually are md4CompFrames, but the first fields are the same,
|
||||
// so this will work for this routine.
|
||||
}
|
||||
else
|
||||
{
|
||||
frameSize = (int)( &((md4Frame_t *)0)->bones[ tr.currentModel->md4->numBones ] );
|
||||
newFrame = (md4Frame_t *)((byte *)header + header->ofsFrames + ent->e.frame * frameSize );
|
||||
oldFrame = (md4Frame_t *)((byte *)header + header->ofsFrames + ent->e.oldframe * frameSize );
|
||||
}
|
||||
|
||||
// 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_AComputeFogNum
|
||||
|
||||
=================
|
||||
*/
|
||||
static int R_AComputeFogNum( md4Header_t *header, trRefEntity_t *ent ) {
|
||||
int i;
|
||||
fog_t *fog;
|
||||
md4Frame_t *frame;
|
||||
vec3_t localOrigin;
|
||||
int frameSize;
|
||||
|
||||
if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (header->ofsFrames<0) // Compressed
|
||||
{
|
||||
frameSize = (int)( &((md4CompFrame_t *)0)->bones[ header->numBones ] );
|
||||
frame = (md4Frame_t *)((byte *)header - header->ofsFrames + ent->e.frame * frameSize );
|
||||
// HACK! These frames actually are md4CompFrames, but the first fields are the same,
|
||||
// so this will work for this routine.
|
||||
}
|
||||
else
|
||||
{
|
||||
frameSize = (int)( &((md4Frame_t *)0)->bones[ header->numBones ] );
|
||||
frame = (md4Frame_t *)((byte *)header + header->ofsFrames + ent->e.frame * frameSize );
|
||||
}
|
||||
|
||||
VectorAdd( ent->e.origin, frame->localOrigin, localOrigin );
|
||||
int partialFog = 0;
|
||||
for ( i = 1 ; i < tr.world->numfogs ; i++ ) {
|
||||
fog = &tr.world->fogs[i];
|
||||
if ( localOrigin[0] - frame->radius >= fog->bounds[0][0]
|
||||
&& localOrigin[0] + frame->radius <= fog->bounds[1][0]
|
||||
&& localOrigin[1] - frame->radius >= fog->bounds[0][1]
|
||||
&& localOrigin[1] + frame->radius <= fog->bounds[1][1]
|
||||
&& localOrigin[2] - frame->radius >= fog->bounds[0][2]
|
||||
&& localOrigin[2] + frame->radius <= fog->bounds[1][2] )
|
||||
{//totally inside it
|
||||
return i;
|
||||
break;
|
||||
}
|
||||
if ( ( localOrigin[0] - frame->radius >= fog->bounds[0][0] && localOrigin[1] - frame->radius >= fog->bounds[0][1] && localOrigin[2] - frame->radius >= fog->bounds[0][2] &&
|
||||
localOrigin[0] - frame->radius <= fog->bounds[1][0] && localOrigin[1] - frame->radius <= fog->bounds[1][1] && localOrigin[2] - frame->radius <= fog->bounds[1][2] ) ||
|
||||
( localOrigin[0] + frame->radius >= fog->bounds[0][0] && localOrigin[1] + frame->radius >= fog->bounds[0][1] && localOrigin[2] + frame->radius >= fog->bounds[0][2] &&
|
||||
localOrigin[0] + frame->radius <= fog->bounds[1][0] && localOrigin[1] + frame->radius <= fog->bounds[1][1] && localOrigin[2] + frame->radius <= fog->bounds[1][2] ) )
|
||||
{//partially inside it
|
||||
if ( tr.refdef.fogIndex == i || R_FogParmsMatch( tr.refdef.fogIndex, i ) )
|
||||
{//take new one only if it's the same one that the viewpoint is in
|
||||
return i;
|
||||
break;
|
||||
}
|
||||
else if ( !partialFog )
|
||||
{//first partialFog
|
||||
partialFog = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
//if all else fails, return the first partialFog
|
||||
return partialFog;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
R_AddAnimSurfaces
|
||||
==============
|
||||
*/
|
||||
void R_AddAnimSurfaces( trRefEntity_t *ent ) {
|
||||
md4Header_t *header;
|
||||
md4Surface_t *surface;
|
||||
md4LOD_t *lod;
|
||||
shader_t *shader = 0;
|
||||
shader_t *cust_shader = 0;
|
||||
int fogNum = 0;
|
||||
qboolean personalModel;
|
||||
int cull;
|
||||
int i, whichLod;
|
||||
|
||||
// don't add third_person objects if not in a portal
|
||||
personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal;
|
||||
|
||||
if ( ent->e.renderfx & RF_CAP_FRAMES) {
|
||||
if (ent->e.frame > tr.currentModel->md4->numFrames-1)
|
||||
ent->e.frame = tr.currentModel->md4->numFrames-1;
|
||||
if (ent->e.oldframe > tr.currentModel->md4->numFrames-1)
|
||||
ent->e.oldframe = tr.currentModel->md4->numFrames-1;
|
||||
}
|
||||
else if ( ent->e.renderfx & RF_WRAP_FRAMES ) {
|
||||
ent->e.frame %= tr.currentModel->md4->numFrames;
|
||||
ent->e.oldframe %= tr.currentModel->md4->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 >= tr.currentModel->md4->numFrames)
|
||||
|| (ent->e.frame < 0)
|
||||
|| (ent->e.oldframe >= tr.currentModel->md4->numFrames)
|
||||
|| (ent->e.oldframe < 0) )
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
VID_Printf (PRINT_ALL, "R_AddAnimSurfaces: no such frame %d to %d for '%s'\n",
|
||||
#else
|
||||
VID_Printf (PRINT_DEVELOPER, "R_AddAnimSurfaces: no such frame %d to %d for '%s'\n",
|
||||
#endif
|
||||
ent->e.oldframe, ent->e.frame,
|
||||
tr.currentModel->name );
|
||||
ent->e.frame = 0;
|
||||
ent->e.oldframe = 0;
|
||||
}
|
||||
|
||||
header = tr.currentModel->md4;
|
||||
|
||||
//
|
||||
// cull the entire model if merged bounding box of both frames
|
||||
// is outside the view frustum.
|
||||
//
|
||||
cull = R_ACullModel ( header, ent );
|
||||
if ( cull == CULL_OUT ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// compute LOD
|
||||
//
|
||||
lod = (md4LOD_t *)( (byte *)header + header->ofsLODs );
|
||||
whichLod = R_ComputeLOD( ent );
|
||||
for ( i = 0; i < whichLod; i++)
|
||||
{
|
||||
lod = (md4LOD_t*)( (byte *)lod + lod->ofsEnd );
|
||||
}
|
||||
|
||||
//
|
||||
// set up lighting now that we know we aren't culled
|
||||
//
|
||||
if ( !personalModel || r_shadows->integer > 1 ) {
|
||||
#ifdef VV_LIGHTING
|
||||
VVLightMan.R_SetupEntityLighting( &tr.refdef, ent );
|
||||
#else
|
||||
R_SetupEntityLighting( &tr.refdef, ent );
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// see if we are in a fog volume
|
||||
//
|
||||
fogNum = R_AComputeFogNum( header, ent );
|
||||
|
||||
|
||||
//
|
||||
// draw all surfaces
|
||||
//
|
||||
cust_shader = R_GetShaderByHandle( ent->e.customShader );
|
||||
|
||||
|
||||
surface = (md4Surface_t *)( (byte *)lod + lod->ofsSurfaces );
|
||||
for ( i = 0 ; i < lod->numSurfaces ; i++ ) {
|
||||
if ( ent->e.customShader ) {
|
||||
shader = cust_shader;
|
||||
} else if ( ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins ) {
|
||||
skin_t *skin;
|
||||
int j;
|
||||
|
||||
skin = R_GetSkinByHandle( ent->e.customSkin );
|
||||
|
||||
// match the surface name to something in the skin file
|
||||
shader = tr.defaultShader;
|
||||
for ( j = 0 ; j < skin->numSurfaces ; j++ ) {
|
||||
// the names have both been lowercased
|
||||
if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) {
|
||||
shader = skin->surfaces[j]->shader;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
shader = R_GetShaderByHandle( surface->shaderIndex );
|
||||
}
|
||||
// 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
|
||||
#ifndef VV_LIGHTING
|
||||
&& fogNum == 0
|
||||
#endif
|
||||
&& (ent->e.renderfx & RF_SHADOW_PLANE )
|
||||
&& !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) )
|
||||
&& shader->sort == SS_OPAQUE ) {
|
||||
R_AddDrawSurf( (surfaceType_t *)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( (surfaceType_t *)surface, tr.projectionShadowShader, 0, qfalse );
|
||||
}
|
||||
|
||||
// don't add third_person objects if not viewing through a portal
|
||||
if ( !personalModel ) {
|
||||
R_AddDrawSurf( (surfaceType_t *)surface, shader, fogNum, qfalse );
|
||||
}
|
||||
|
||||
surface = (md4Surface_t *)( (byte *)surface + surface->ofsEnd );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
RB_SurfaceAnim
|
||||
==============
|
||||
*/
|
||||
void RB_SurfaceAnim( md4Surface_t *surface ) {
|
||||
int i, j, k;
|
||||
float frontlerp, backlerp;
|
||||
int *triangles;
|
||||
int indexes;
|
||||
int baseIndex, baseVertex;
|
||||
int numVerts;
|
||||
md4Vertex_t *v;
|
||||
md4Bone_t bones[MD4_MAX_BONES];
|
||||
md4Bone_t tbone[2];
|
||||
md4Bone_t *bonePtr, *bone;
|
||||
md4Header_t *header;
|
||||
md4Frame_t *frame=0;
|
||||
md4Frame_t *oldFrame=0;
|
||||
md4CompFrame_t *cframe=0;
|
||||
md4CompFrame_t *coldFrame=0;
|
||||
int frameSize;
|
||||
qboolean compressed;
|
||||
|
||||
|
||||
if ( backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame ) {
|
||||
backlerp = 0;
|
||||
frontlerp = 1;
|
||||
} else {
|
||||
backlerp = backEnd.currentEntity->e.backlerp;
|
||||
frontlerp = 1.0 - backlerp;
|
||||
}
|
||||
header = (md4Header_t *)((byte *)surface + surface->ofsHeader);
|
||||
|
||||
if (header->ofsFrames<0) // Compressed
|
||||
{
|
||||
compressed = qtrue;
|
||||
frameSize = (int)( &((md4CompFrame_t *)0)->bones[ header->numBones ] );
|
||||
cframe = (md4CompFrame_t *)((byte *)header - header->ofsFrames + backEnd.currentEntity->e.frame * frameSize );
|
||||
coldFrame = (md4CompFrame_t *)((byte *)header - header->ofsFrames + backEnd.currentEntity->e.oldframe * frameSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
compressed = qfalse;
|
||||
frameSize = (int)( &((md4Frame_t *)0)->bones[ header->numBones ] );
|
||||
frame = (md4Frame_t *)((byte *)header + header->ofsFrames +
|
||||
backEnd.currentEntity->e.frame * frameSize );
|
||||
oldFrame = (md4Frame_t *)((byte *)header + header->ofsFrames +
|
||||
backEnd.currentEntity->e.oldframe * frameSize );
|
||||
}
|
||||
|
||||
|
||||
|
||||
RB_CheckOverflow( surface->numVerts, surface->numTriangles );
|
||||
|
||||
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;
|
||||
|
||||
//
|
||||
// lerp all the needed bones
|
||||
//
|
||||
if ( !backlerp && !compressed)
|
||||
// no lerping needed
|
||||
bonePtr = frame->bones;
|
||||
else
|
||||
{
|
||||
bonePtr = bones;
|
||||
if (compressed)
|
||||
{
|
||||
for ( i = 0 ; i < header->numBones ; i++ )
|
||||
{
|
||||
if ( !backlerp )
|
||||
MC_UnCompress(bonePtr[i].matrix,cframe->bones[i].Comp);
|
||||
else
|
||||
{
|
||||
MC_UnCompress(tbone[0].matrix,cframe->bones[i].Comp);
|
||||
MC_UnCompress(tbone[1].matrix,coldFrame->bones[i].Comp);
|
||||
for ( j = 0 ; j < 12 ; j++ )
|
||||
((float *)&bonePtr[i])[j] = frontlerp * ((float *)&tbone[0])[j]
|
||||
+ backlerp * ((float *)&tbone[1])[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( i = 0 ; i < header->numBones*12 ; i++ )
|
||||
((float *)bonePtr)[i] = frontlerp * ((float *)frame->bones)[i]
|
||||
+ backlerp * ((float *)oldFrame->bones)[i];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// deform the vertexes by the lerped bones
|
||||
//
|
||||
numVerts = surface->numVerts;
|
||||
v = (md4Vertex_t *) ((byte *)surface + surface->ofsVerts);
|
||||
for ( j = 0; j < numVerts; j++ ) {
|
||||
vec3_t tempVert, tempNormal;
|
||||
md4Weight_t *w;
|
||||
|
||||
VectorClear( tempVert );
|
||||
VectorClear( tempNormal );
|
||||
w = v->weights;
|
||||
for ( k = 0 ; k < v->numWeights ; k++, w++ ) {
|
||||
bone = bonePtr + w->boneIndex;
|
||||
|
||||
tempVert[0] += w->boneWeight * ( DotProduct( bone->matrix[0], w->offset ) + bone->matrix[0][3] );
|
||||
tempVert[1] += w->boneWeight * ( DotProduct( bone->matrix[1], w->offset ) + bone->matrix[1][3] );
|
||||
tempVert[2] += w->boneWeight * ( DotProduct( bone->matrix[2], w->offset ) + bone->matrix[2][3] );
|
||||
|
||||
tempNormal[0] += w->boneWeight * DotProduct( bone->matrix[0], v->normal );
|
||||
tempNormal[1] += w->boneWeight * DotProduct( bone->matrix[1], v->normal );
|
||||
tempNormal[2] += w->boneWeight * DotProduct( bone->matrix[2], v->normal );
|
||||
}
|
||||
|
||||
tess.xyz[baseVertex + j][0] = tempVert[0];
|
||||
tess.xyz[baseVertex + j][1] = tempVert[1];
|
||||
tess.xyz[baseVertex + j][2] = tempVert[2];
|
||||
|
||||
tess.normal[baseVertex + j][0] = tempNormal[0];
|
||||
tess.normal[baseVertex + j][1] = tempNormal[1];
|
||||
tess.normal[baseVertex + j][2] = tempNormal[2];
|
||||
|
||||
tess.texCoords[baseVertex + j][0][0] = v->texCoords[0];
|
||||
tess.texCoords[baseVertex + j][0][1] = v->texCoords[1];
|
||||
|
||||
v = (md4Vertex_t *)&v->weights[v->numWeights];
|
||||
}
|
||||
|
||||
tess.numVertexes += surface->numVerts;
|
||||
}
|
||||
Reference in New Issue
Block a user