478 lines
No EOL
14 KiB
C++
478 lines
No EOL
14 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;
|
|
} |