jedi-academy/code/renderer/tr_animation.cpp
2013-04-23 15:21:39 +10:00

478 lines
No EOL
13 KiB
C++

// 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;
}