This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/render/r_alias.c

1111 lines
33 KiB
C

/*
Copyright (C) 2002-2007 Victor Luchits
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// r_alias.c: Quake 2 .md2 and Quake III Arena .md3 model formats support
#include "r_local.h"
#include "mathlib.h"
#include "quatlib.h"
#include "byteorder.h"
static mesh_t alias_mesh;
static vec3_t alias_mins;
static vec3_t alias_maxs;
static float alias_radius;
/*
=================
Mod_AliasBuildMeshesForFrame0
=================
*/
static void Mod_AliasBuildMeshesForFrame0( ref_model_t *mod )
{
int i, j, k;
size_t size;
maliasframe_t *frame;
maliasmodel_t *aliasmodel = ( maliasmodel_t * )mod->extradata;
frame = &aliasmodel->frames[0];
for( k = 0; k < aliasmodel->nummeshes; k++ )
{
maliasmesh_t *mesh = &aliasmodel->meshes[k];
size = sizeof( vec4_t ) + sizeof( vec4_t ); // xyz and normals
if( glConfig.ext.GLSL )
size += sizeof( vec4_t ); // s-vectors
size *= mesh->numverts;
mesh->xyzArray = ( vec4_t * )Mod_Malloc( mod, size );
mesh->normalsArray = ( vec4_t * )( ( byte * )mesh->xyzArray + mesh->numverts * sizeof( vec4_t ) );
if( glConfig.ext.GLSL )
mesh->sVectorsArray = ( vec4_t * )( ( byte * )mesh->normalsArray + mesh->numverts * sizeof( vec4_t ) );
for( i = 0; i < mesh->numverts; i++ )
{
for( j = 0; j < 3; j++ )
mesh->xyzArray[i][j] = frame->translate[j] + frame->scale[j] * mesh->vertexes[i].point[j];
mesh->xyzArray[i][3] = 1;
R_LatLongToNorm( mesh->vertexes[i].latlong, mesh->normalsArray[i] );
mesh->normalsArray[i][3] = 0;
}
if( glConfig.ext.GLSL )
R_BuildTangentVectors( mesh->numverts, mesh->xyzArray, mesh->normalsArray, mesh->stArray, mesh->numtris, mesh->elems, mesh->sVectorsArray );
}
}
#ifdef QUAKE2_JUNK
/*
==============================================================================
MD2 MODELS
==============================================================================
*/
/*
=================
Mod_AliasCalculateVertexNormals
=================
*/
static void Mod_AliasCalculateVertexNormals( int numElems, elem_t *elems, int numVerts, maliasvertex_t *v )
{
int i, j, k, vertRemap[MD2_MAX_VERTS];
vec3_t dir1, dir2, normal, trnormals[MD2_MAX_TRIANGLES];
int numUniqueVerts, uniqueVerts[MD2_MAX_VERTS];
byte latlongs[MD2_MAX_VERTS][2];
// count unique verts
for( i = 0, numUniqueVerts = 0; i < numVerts; i++ )
{
for( j = 0; j < numUniqueVerts; j++ )
{
if( VectorCompare( v[uniqueVerts[j]].point, v[i].point ) )
{
vertRemap[i] = j;
break;
}
}
if( j == numUniqueVerts )
{
vertRemap[i] = numUniqueVerts;
uniqueVerts[numUniqueVerts++] = i;
}
}
for( i = 0, j = 0; i < numElems; i += 3, j++ )
{
// calculate two mostly perpendicular edge directions
VectorSubtract( v[elems[i+0]].point, v[elems[i+1]].point, dir1 );
VectorSubtract( v[elems[i+2]].point, v[elems[i+1]].point, dir2 );
// we have two edge directions, we can calculate a third vector from
// them, which is the direction of the surface normal
CrossProduct( dir1, dir2, trnormals[j] );
VectorNormalize( trnormals[j] );
}
// sum all triangle normals
for( i = 0; i < numUniqueVerts; i++ )
{
VectorClear( normal );
for( j = 0, k = 0; j < numElems; j += 3, k++ )
{
if( vertRemap[elems[j+0]] == i || vertRemap[elems[j+1]] == i || vertRemap[elems[j+2]] == i )
VectorAdd( normal, trnormals[k], normal );
}
VectorNormalize( normal );
NormToLatLong( normal, latlongs[i] );
}
// copy normals back
for( i = 0; i < numVerts; i++ )
*(short *)v[i].latlong = *(short *)latlongs[vertRemap[i]];
}
/*
=================
Mod_LoadAliasMD2Model
=================
*/
void Mod_LoadAliasMD2Model( ref_model_t *mod, ref_model_t *parent, void *buffer )
{
int i, j, k;
int version, framesize;
float skinwidth, skinheight;
int numverts, numelems;
int indremap[MD2_MAX_TRIANGLES*3];
elem_t ptempelem[MD2_MAX_TRIANGLES*3], ptempstelem[MD2_MAX_TRIANGLES*3];
dmd2_t *pinmodel;
dstvert_t *pinst;
dtriangle_t *pintri;
daliasframe_t *pinframe;
elem_t *poutelem;
maliasmodel_t *poutmodel;
maliasmesh_t *poutmesh;
vec2_t *poutcoord;
maliasframe_t *poutframe;
maliasvertex_t *poutvertex;
maliasskin_t *poutskin;
pinmodel = ( dmd2_t * )buffer;
version = LittleLong( pinmodel->version );
framesize = LittleLong( pinmodel->framesize );
if( version != MD2_ALIAS_VERSION )
Host_Error( ERR_DROP, "%s has wrong version number (%i should be %i)",
mod->name, version, MD2_ALIAS_VERSION );
mod->type = mod_alias;
mod->aliasmodel = poutmodel = Mod_Malloc( mod, sizeof( maliasmodel_t ) );
mod->radius = 0;
ClearBounds( mod->mins, mod->maxs );
// byte swap the header fields and sanity check
skinwidth = LittleLong( pinmodel->skinwidth );
skinheight = LittleLong( pinmodel->skinheight );
if( skinwidth <= 0 )
Host_Error( ERR_DROP, "model %s has invalid skin width", mod->name );
if( skinheight <= 0 )
Host_Error( ERR_DROP, "model %s has invalid skin height", mod->name );
poutmodel->numframes = LittleLong( pinmodel->num_frames );
poutmodel->numskins = LittleLong( pinmodel->num_skins );
if( poutmodel->numframes > MD2_MAX_FRAMES )
Host_Error( ERR_DROP, "model %s has too many frames", mod->name );
else if( poutmodel->numframes <= 0 )
Host_Error( ERR_DROP, "model %s has no frames", mod->name );
if( poutmodel->numskins > MD2_MAX_SKINS )
Host_Error( ERR_DROP, "model %s has too many skins", mod->name );
else if( poutmodel->numskins < 0 )
Host_Error( ERR_DROP, "model %s has invalid number of skins", mod->name );
poutmodel->numtags = 0;
poutmodel->tags = NULL;
poutmodel->nummeshes = 1;
poutmesh = poutmodel->meshes = Mod_Malloc( mod, sizeof( maliasmesh_t ) );
Q_strncpyz( poutmesh->name, "default", MD3_MAX_PATH );
poutmesh->numverts = LittleLong( pinmodel->num_xyz );
poutmesh->numtris = LittleLong( pinmodel->num_tris );
if( poutmesh->numverts <= 0 )
Host_Error( "model %s has no vertices\n", mod->name );
else if( poutmesh->numverts > MD2_MAX_VERTS )
Host_Error( "model %s has too many vertices\n", mod->name );
if( poutmesh->numtris > MD2_MAX_TRIANGLES )
Host_Error( "model %s has too many triangles\n", mod->name );
else if( poutmesh->numtris <= 0 )
Host_Error( "model %s has no triangles\n", mod->name );
numelems = poutmesh->numtris * 3;
poutelem = poutmesh->elems = Mod_Malloc( mod, numelems * sizeof( elem_t ) );
//
// load triangle lists
//
pintri = ( dtriangle_t * )( ( byte * )pinmodel + LittleLong( pinmodel->ofs_tris ) );
pinst = ( dstvert_t * ) ( ( byte * )pinmodel + LittleLong( pinmodel->ofs_st ) );
for( i = 0, k = 0; i < poutmesh->numtris; i++, k += 3 )
{
for( j = 0; j < 3; j++ )
{
ptempelem[k+j] = ( elem_t )LittleShort( pintri[i].index_xyz[j] );
ptempstelem[k+j] = ( elem_t )LittleShort( pintri[i].index_st[j] );
}
}
//
// build list of unique vertexes
//
numverts = 0;
memset( indremap, -1, MD2_MAX_TRIANGLES * 3 * sizeof( int ) );
for( i = 0; i < numelems; i++ )
{
if( indremap[i] != -1 )
continue;
// remap duplicates
for( j = i + 1; j < numelems; j++ )
{
if( ( ptempelem[j] == ptempelem[i] )
&& ( pinst[ptempstelem[j]].s == pinst[ptempstelem[i]].s )
&& ( pinst[ptempstelem[j]].t == pinst[ptempstelem[i]].t ) )
{
indremap[j] = i;
poutelem[j] = numverts;
}
}
// add unique vertex
indremap[i] = i;
poutelem[i] = numverts++;
}
MsgDev( "%s: remapped %i verts to %i (%i tris)\n", mod->name, poutmesh->numverts, numverts, poutmesh->numtris );
poutmesh->numverts = numverts;
//
// load base s and t vertices
//
poutcoord = poutmesh->stArray = Mod_Malloc( mod, numverts * sizeof( vec2_t ) );
for( i = 0; i < numelems; i++ )
{
if( indremap[i] == i )
{
poutcoord[poutelem[i]][0] = ( (float)LittleShort( pinst[ptempstelem[i]].s ) + 0.5 ) / skinwidth;
poutcoord[poutelem[i]][1] = ( (float)LittleShort( pinst[ptempstelem[i]].t ) + 0.5 ) / skinheight;
}
}
//
// load the frames
//
poutframe = poutmodel->frames = Mod_Malloc( mod, poutmodel->numframes * ( sizeof( maliasframe_t ) + numverts * sizeof( maliasvertex_t ) ) );
poutvertex = poutmesh->vertexes = ( maliasvertex_t *)( ( byte * )poutframe + poutmodel->numframes * sizeof( maliasframe_t ) );
for( i = 0; i < poutmodel->numframes; i++, poutframe++, poutvertex += numverts )
{
pinframe = ( daliasframe_t * )( ( byte * )pinmodel + LittleLong( pinmodel->ofs_frames ) + i * framesize );
for( j = 0; j < 3; j++ )
{
poutframe->scale[j] = LittleFloat( pinframe->scale[j] );
poutframe->translate[j] = LittleFloat( pinframe->translate[j] );
}
for( j = 0; j < numelems; j++ )
{ // verts are all 8 bit, so no swapping needed
if( indremap[j] == j )
{
poutvertex[poutelem[j]].point[0] = (short)pinframe->verts[ptempelem[j]].v[0];
poutvertex[poutelem[j]].point[1] = (short)pinframe->verts[ptempelem[j]].v[1];
poutvertex[poutelem[j]].point[2] = (short)pinframe->verts[ptempelem[j]].v[2];
}
}
Mod_AliasCalculateVertexNormals( numelems, poutelem, numverts, poutvertex );
VectorCopy( poutframe->translate, poutframe->mins );
VectorMA( poutframe->translate, 255, poutframe->scale, poutframe->maxs );
poutframe->radius = RadiusFromBounds( poutframe->mins, poutframe->maxs );
mod->radius = max( mod->radius, poutframe->radius );
AddPointToBounds( poutframe->mins, mod->mins, mod->maxs );
AddPointToBounds( poutframe->maxs, mod->mins, mod->maxs );
}
//
// build S and T vectors for frame 0
//
Mod_AliasBuildMeshesForFrame0( mod );
// register all skins
poutskin = poutmodel->skins = Mod_Malloc( mod, poutmodel->numskins * sizeof( maliasskin_t ) );
for( i = 0; i < poutmodel->numskins; i++, poutskin++ )
{
if( LittleLong( pinmodel->ofs_skins ) == -1 )
continue;
poutskin->shader = R_RegisterSkin( ( char * )pinmodel + LittleLong( pinmodel->ofs_skins ) + i*MD2_MAX_SKINNAME );
}
}
#endif
/*
==============================================================================
MD3 MODELS
==============================================================================
*/
/*
=================
Mod_LoadAliasMD3Model
=================
*/
void Mod_LoadAliasMD3Model( ref_model_t *mod, ref_model_t *parent, void *buffer )
{
int version, i, j, l;
int bufsize, numverts;
byte *buf;
dmd3header_t *pinmodel;
dmd3frame_t *pinframe;
dmd3tag_t *pintag;
dmd3mesh_t *pinmesh;
dmd3skin_t *pinskin;
dmd3coord_t *pincoord;
dmd3vertex_t *pinvert;
elem_t *pinelem, *poutelem;
maliasvertex_t *poutvert;
vec2_t *poutcoord;
maliasskin_t *poutskin;
maliasmesh_t *poutmesh;
maliastag_t *pouttag;
maliasframe_t *poutframe;
maliasmodel_t *poutmodel;
vec3_t ebbox = { 0, 0, 0 };
pinmodel = ( dmd3header_t * )buffer;
version = LittleLong( pinmodel->version );
if( version != MD3_ALIAS_VERSION )
Host_Error( "%s has wrong version number (%i should be %i)\n",
mod->name, version, MD3_ALIAS_VERSION );
mod->type = mod_alias;
mod->extradata = poutmodel = Mod_Malloc( mod, sizeof( maliasmodel_t ) );
mod->radius = 0;
ClearBounds( mod->mins, mod->maxs );
// byte swap the header fields and sanity check
poutmodel->numframes = LittleLong( pinmodel->num_frames );
poutmodel->numtags = LittleLong( pinmodel->num_tags );
poutmodel->nummeshes = LittleLong( pinmodel->num_meshes );
poutmodel->numskins = 0;
if( poutmodel->numframes <= 0 )
Host_Error( "model %s has no frames\n", mod->name );
// else if( poutmodel->numframes > MD3_MAX_FRAMES )
// Host_Error( "model %s has too many frames\n", mod->name );
if( poutmodel->numtags > MD3_MAX_TAGS )
Host_Error( "model %s has too many tags\n", mod->name );
else if( poutmodel->numtags < 0 )
Host_Error( "model %s has invalid number of tags\n", mod->name );
if( poutmodel->nummeshes < 0 )
Host_Error( "model %s has invalid number of meshes\n", mod->name );
else if( !poutmodel->nummeshes && !poutmodel->numtags )
Host_Error( "model %s has no meshes and no tags\n", mod->name );
// else if( poutmodel->nummeshes > MD3_MAX_MESHES )
// Host_Error( "model %s has too many meshes\n", mod->name );
bufsize = poutmodel->numframes * ( sizeof( maliasframe_t ) + sizeof( maliastag_t ) * poutmodel->numtags ) +
poutmodel->nummeshes * sizeof( maliasmesh_t );
buf = Mod_Malloc( mod, bufsize );
//
// load the frames
//
pinframe = ( dmd3frame_t * )( ( byte * )pinmodel + LittleLong( pinmodel->ofs_frames ) );
poutframe = poutmodel->frames = ( maliasframe_t * )buf; buf += sizeof( maliasframe_t ) * poutmodel->numframes;
for( i = 0; i < poutmodel->numframes; i++, pinframe++, poutframe++ )
{
for( j = 0; j < 3; j++ )
{
poutframe->scale[j] = MD3_XYZ_SCALE;
poutframe->translate[j] = LittleFloat( pinframe->translate[j] );
}
// never trust the modeler utility and recalculate bbox and radius
ClearBounds( poutframe->mins, poutframe->maxs );
}
//
// load the tags
//
pintag = ( dmd3tag_t * )( ( byte * )pinmodel + LittleLong( pinmodel->ofs_tags ) );
pouttag = poutmodel->tags = ( maliastag_t * )buf; buf += sizeof( maliastag_t ) * poutmodel->numframes * poutmodel->numtags;
for( i = 0; i < poutmodel->numframes; i++ )
{
for( l = 0; l < poutmodel->numtags; l++, pintag++, pouttag++ )
{
vec3_t axis[3];
for( j = 0; j < 3; j++ )
{
axis[0][j] = LittleFloat( pintag->axis[0][j] );
axis[1][j] = LittleFloat( pintag->axis[1][j] );
axis[2][j] = LittleFloat( pintag->axis[2][j] );
pouttag->origin[j] = LittleFloat( pintag->origin[j] );
}
Matrix_Quat( axis, pouttag->quat );
Quat_Normalize( pouttag->quat );
com.strncpy( pouttag->name, pintag->name, MD3_MAX_PATH );
}
}
//
// load the meshes
//
pinmesh = ( dmd3mesh_t * )( ( byte * )pinmodel + LittleLong( pinmodel->ofs_meshes ) );
poutmesh = poutmodel->meshes = ( maliasmesh_t * )buf;
for( i = 0; i < poutmodel->nummeshes; i++, poutmesh++ )
{
if( pinmesh->id != IDMD3HEADER )
Host_Error( "mesh %s in model %s has wrong id (%s should be %s)\n",
pinmesh->name, mod->name, pinmesh->id, IDMD3HEADER );
com.strncpy( poutmesh->name, pinmesh->name, MD3_MAX_PATH );
Mod_StripLODSuffix( poutmesh->name );
poutmesh->numtris = LittleLong( pinmesh->num_tris );
poutmesh->numskins = LittleLong( pinmesh->num_skins );
poutmesh->numverts = numverts = LittleLong( pinmesh->num_verts );
/* if( poutmesh->numskins <= 0 )
Host_Error( ERR_DROP, "mesh %i in model %s has no skins", i, mod->name );
else*/ if( poutmesh->numskins > MD3_MAX_SHADERS )
Host_Error( "mesh %i in model %s has too many skins\n", i, mod->name );
if( poutmesh->numtris <= 0 )
Host_Error( "mesh %i in model %s has no elements\n", i, mod->name );
else if( poutmesh->numtris > MD3_MAX_TRIANGLES )
Host_Error( "mesh %i in model %s has too many triangles\n", i, mod->name );
if( poutmesh->numverts <= 0 )
Host_Error( "mesh %i in model %s has no vertices\n", i, mod->name );
else if( poutmesh->numverts > MD3_MAX_VERTS )
Host_Error( "mesh %i in model %s has too many vertices\n", i, mod->name );
bufsize = sizeof( maliasskin_t ) * poutmesh->numskins + poutmesh->numtris * sizeof( elem_t ) * 3 +
numverts * ( sizeof( vec2_t ) + sizeof( maliasvertex_t ) * poutmodel->numframes );
buf = Mod_Malloc( mod, bufsize );
//
// load the skins
//
pinskin = ( dmd3skin_t * )( ( byte * )pinmesh + LittleLong( pinmesh->ofs_skins ) );
poutskin = poutmesh->skins = ( maliasskin_t * )buf; buf += sizeof( maliasskin_t ) * poutmesh->numskins;
for( j = 0; j < poutmesh->numskins; j++, pinskin++, poutskin++ )
{
poutskin->shader = R_RegisterSkin( pinskin->name );
R_DeformvBBoxForShader( poutskin->shader, ebbox );
}
//
// load the elems
//
pinelem = ( elem_t * )( ( byte * )pinmesh + LittleLong( pinmesh->ofs_elems ) );
poutelem = poutmesh->elems = ( elem_t * )buf; buf += poutmesh->numtris * sizeof( elem_t ) * 3;
for( j = 0; j < poutmesh->numtris; j++, pinelem += 3, poutelem += 3 )
{
poutelem[0] = (elem_t)LittleLong( pinelem[0] );
poutelem[1] = (elem_t)LittleLong( pinelem[1] );
poutelem[2] = (elem_t)LittleLong( pinelem[2] );
}
//
// load the texture coordinates
//
pincoord = ( dmd3coord_t * )( ( byte * )pinmesh + LittleLong( pinmesh->ofs_tcs ) );
poutcoord = poutmesh->stArray = ( vec2_t * )buf; buf += poutmesh->numverts * sizeof( vec2_t );
for( j = 0; j < poutmesh->numverts; j++, pincoord++ )
{
poutcoord[j][0] = LittleFloat( pincoord->st[0] );
poutcoord[j][1] = LittleFloat( pincoord->st[1] );
}
//
// load the vertexes and normals
//
pinvert = ( dmd3vertex_t * )( ( byte * )pinmesh + LittleLong( pinmesh->ofs_verts ) );
poutvert = poutmesh->vertexes = ( maliasvertex_t * )buf;
for( l = 0, poutframe = poutmodel->frames; l < poutmodel->numframes; l++, poutframe++, pinvert += poutmesh->numverts, poutvert += poutmesh->numverts )
{
vec3_t v;
for( j = 0; j < poutmesh->numverts; j++ )
{
poutvert[j].point[0] = LittleShort( pinvert[j].point[0] );
poutvert[j].point[1] = LittleShort( pinvert[j].point[1] );
poutvert[j].point[2] = LittleShort( pinvert[j].point[2] );
poutvert[j].latlong[0] = pinvert[j].norm[0];
poutvert[j].latlong[1] = pinvert[j].norm[1];
VectorCopy( poutvert[j].point, v );
AddPointToBounds( v, poutframe->mins, poutframe->maxs );
}
}
pinmesh = ( dmd3mesh_t * )( ( byte * )pinmesh + LittleLong( pinmesh->meshsize ) );
}
//
// build S and T vectors for frame 0
//
Mod_AliasBuildMeshesForFrame0( mod );
//
// calculate model bounds
//
poutframe = poutmodel->frames;
for( i = 0; i < poutmodel->numframes; i++, poutframe++ )
{
VectorMA( poutframe->translate, MD3_XYZ_SCALE, poutframe->mins, poutframe->mins );
VectorMA( poutframe->translate, MD3_XYZ_SCALE, poutframe->maxs, poutframe->maxs );
VectorSubtract( poutframe->mins, ebbox, poutframe->mins );
VectorAdd( poutframe->maxs, ebbox, poutframe->maxs );
poutframe->radius = RadiusFromBounds( poutframe->mins, poutframe->maxs );
AddPointToBounds( poutframe->mins, mod->mins, mod->maxs );
AddPointToBounds( poutframe->maxs, mod->mins, mod->maxs );
mod->radius = max( mod->radius, poutframe->radius );
}
}
/*
=============
R_AliasModelLOD
=============
*/
static ref_model_t *R_AliasModelLOD( ref_entity_t *e )
{
int lod;
float dist;
if( !e->model->numlods || ( e->flags & RF_FORCENOLOD ) )
return e->model;
dist = DistanceFast( e->origin, RI.viewOrigin );
dist *= RI.lod_dist_scale_for_fov;
lod = (int)( dist / e->model->radius );
if( r_lodscale->integer )
lod /= r_lodscale->integer;
lod += r_lodbias->integer;
if( lod < 1 )
return e->model;
return e->model->lods[min( lod, e->model->numlods )-1];
}
/*
=============
R_AliasModelLerpBBox
=============
*/
static void R_AliasModelLerpBBox( ref_entity_t *e, ref_model_t *mod )
{
int i;
maliasmodel_t *aliasmodel = ( maliasmodel_t * )mod->extradata;
maliasframe_t *pframe, *poldframe;
float *thismins, *oldmins, *thismaxs, *oldmaxs;
if( !aliasmodel->nummeshes )
{
alias_radius = 0;
ClearBounds( alias_mins, alias_maxs );
return;
}
if( ( e->frame >= aliasmodel->numframes ) || ( e->frame < 0 ) )
{
#ifndef WSW_RELEASE
MsgDev( D_ERROR, "R_DrawAliasModel %s: no such frame %d\n", mod->name, e->frame );
#endif
e->frame = 0;
}
if( ( e->oldframe >= aliasmodel->numframes ) || ( e->oldframe < 0 ) )
{
#ifndef WSW_RELEASE
MsgDev( D_ERROR, "R_DrawAliasModel %s: no such oldframe %d\n", mod->name, e->oldframe );
#endif
e->oldframe = 0;
}
pframe = aliasmodel->frames + e->frame;
poldframe = aliasmodel->frames + e->oldframe;
// compute axially aligned mins and maxs
if( pframe == poldframe )
{
VectorCopy( pframe->mins, alias_mins );
VectorCopy( pframe->maxs, alias_maxs );
alias_radius = pframe->radius;
}
else
{
thismins = pframe->mins;
thismaxs = pframe->maxs;
oldmins = poldframe->mins;
oldmaxs = poldframe->maxs;
for( i = 0; i < 3; i++ )
{
alias_mins[i] = min( thismins[i], oldmins[i] );
alias_maxs[i] = max( thismaxs[i], oldmaxs[i] );
}
alias_radius = RadiusFromBounds( thismins, thismaxs );
}
if( e->scale != 1.0f )
{
VectorScale( alias_mins, e->scale, alias_mins );
VectorScale( alias_maxs, e->scale, alias_maxs );
alias_radius *= e->scale;
}
}
/*
=============
R_AliasModelLerpTag
=============
*/
bool R_AliasModelLerpTag( orientation_t *orient, maliasmodel_t *aliasmodel, int oldframenum, int framenum, float lerpfrac, const char *name )
{
int i;
quat_t quat;
maliastag_t *tag, *oldtag;
// find the appropriate tag
for( i = 0; i < aliasmodel->numtags; i++ )
{
if( !com.stricmp( aliasmodel->tags[i].name, name ) )
break;
}
if( i == aliasmodel->numtags )
{
// MsgDev ("R_AliasModelLerpTag: no such tag %s\n", name );
return false;
}
// ignore invalid frames
if( ( framenum >= aliasmodel->numframes ) || ( framenum < 0 ) )
{
#ifndef WSW_RELEASE
MsgDev( D_ERROR, "R_AliasModelLerpTag %s: no such oldframe %i\n", name, framenum );
#endif
framenum = 0;
}
if( ( oldframenum >= aliasmodel->numframes ) || ( oldframenum < 0 ) )
{
#ifndef WSW_RELEASE
MsgDev( D_ERROR, "R_AliasModelLerpTag %s: no such oldframe %i\n", name, oldframenum );
#endif
oldframenum = 0;
}
tag = aliasmodel->tags + framenum * aliasmodel->numtags + i;
oldtag = aliasmodel->tags + oldframenum * aliasmodel->numtags + i;
// interpolate axis and origin
Quat_Lerp( oldtag->quat, tag->quat, lerpfrac, quat );
Quat_Matrix( quat, orient->axis );
orient->origin[0] = oldtag->origin[0] + ( tag->origin[0] - oldtag->origin[0] ) * lerpfrac;
orient->origin[1] = oldtag->origin[1] + ( tag->origin[1] - oldtag->origin[1] ) * lerpfrac;
orient->origin[2] = oldtag->origin[2] + ( tag->origin[2] - oldtag->origin[2] ) * lerpfrac;
return true;
}
/*
=============
R_DrawAliasFrameLerp
Interpolates between two frames and origins
=============
*/
static void R_DrawAliasFrameLerp( const meshbuffer_t *mb, float backlerp )
{
int i, meshnum;
int features;
float backv[3], frontv[3];
vec3_t normal, oldnormal;
bool unlockVerts, calcVerts, calcNormals, calcSTVectors;
vec3_t move;
maliasframe_t *frame, *oldframe;
maliasmesh_t *mesh;
maliasvertex_t *v, *ov;
ref_entity_t *e = RI.currententity;
ref_model_t *mod = Mod_ForHandle( mb->LODModelHandle );
maliasmodel_t *model = ( maliasmodel_t * )mod->extradata;
ref_shader_t *shader;
static maliasmesh_t *alias_prevmesh;
static ref_shader_t *alias_prevshader;
static int alias_framecount, alias_riparams;
if( alias_riparams != RI.params || RI.params & RP_SHADOWMAPVIEW )
{
alias_riparams = RI.params; // do not try to lock arrays between RI updates
alias_framecount = !r_framecount;
}
meshnum = -mb->infokey - 1;
if( meshnum < 0 || meshnum >= model->nummeshes )
return;
mesh = model->meshes + meshnum;
frame = model->frames + e->frame;
oldframe = model->frames + e->oldframe;
for( i = 0; i < 3; i++ )
move[i] = frame->translate[i] + ( oldframe->translate[i] - frame->translate[i] ) * backlerp;
MB_NUM2SHADER( mb->shaderkey, shader );
features = MF_NONBATCHED | shader->features;
if( RI.params & RP_SHADOWMAPVIEW )
{
features &= ~( MF_COLORS|MF_SVECTORS|MF_ENABLENORMALS );
if( !( shader->features & MF_DEFORMVS ) )
features &= ~MF_NORMALS;
}
else
{
if( ( features & MF_SVECTORS ) || r_shownormals->integer )
features |= MF_NORMALS;
#ifdef HARDWARE_OUTLINES
if( e->outlineHeight )
features |= MF_NORMALS|(glConfig.ext.GLSL ? MF_ENABLENORMALS : 0);
#endif
}
calcNormals = calcSTVectors = false;
calcNormals = ( ( features & MF_NORMALS ) != 0 ) && ( ( e->frame != 0 ) || ( e->oldframe != 0 ) );
if( alias_framecount == r_framecount && RI.previousentity && RI.previousentity->model == e->model && alias_prevmesh == mesh && alias_prevshader == shader )
{
ref_entity_t *pe = RI.previousentity;
if( pe->frame == e->frame
&& pe->oldframe == e->oldframe
&& ( pe->backlerp == e->backlerp || e->frame == e->oldframe ) )
{
unlockVerts = ( ( ( features & MF_DEFORMVS ) /* && (e->shaderTime != pe->shaderTime)*/ ) );
calcNormals = ( calcNormals && ( shader->features & SHADER_DEFORMV_NORMAL ) );
}
}
unlockVerts = true;
calcSTVectors = ( ( features & MF_SVECTORS ) != 0 ) && calcNormals;
alias_prevmesh = mesh;
alias_prevshader = shader;
alias_framecount = r_framecount;
if( unlockVerts )
{
if( !e->frame && !e->oldframe )
{
calcVerts = false;
if( calcNormals )
{
v = mesh->vertexes;
for( i = 0; i < mesh->numverts; i++, v++ )
R_LatLongToNorm( v->latlong, inNormalsArray[i] );
}
}
else if( e->frame == e->oldframe )
{
calcVerts = true;
for( i = 0; i < 3; i++ )
frontv[i] = frame->scale[i];
v = mesh->vertexes + e->frame * mesh->numverts;
for( i = 0; i < mesh->numverts; i++, v++ )
{
Vector4Set( inVertsArray[i],
move[0] + v->point[0]*frontv[0],
move[1] + v->point[1]*frontv[1],
move[2] + v->point[2]*frontv[2], 1 );
if( calcNormals )
R_LatLongToNorm( v->latlong, inNormalsArray[i] );
}
}
else
{
calcVerts = true;
for( i = 0; i < 3; i++ )
{
backv[i] = backlerp * oldframe->scale[i];
frontv[i] = ( 1.0f - backlerp ) * frame->scale[i];
}
v = mesh->vertexes + e->frame * mesh->numverts;
ov = mesh->vertexes + e->oldframe * mesh->numverts;
for( i = 0; i < mesh->numverts; i++, v++, ov++ )
{
Vector4Set( inVertsArray[i],
move[0] + v->point[0]*frontv[0] + ov->point[0]*backv[0],
move[1] + v->point[1]*frontv[1] + ov->point[1]*backv[1],
move[2] + v->point[2]*frontv[2] + ov->point[2]*backv[2], 1 );
if( calcNormals )
{
R_LatLongToNorm( v->latlong, normal );
R_LatLongToNorm( ov->latlong, oldnormal );
VectorSet( inNormalsArray[i],
normal[0] + ( oldnormal[0] - normal[0] ) * backlerp,
normal[1] + ( oldnormal[1] - normal[1] ) * backlerp,
normal[2] + ( oldnormal[2] - normal[2] ) * backlerp );
}
}
}
if( calcSTVectors )
R_BuildTangentVectors( mesh->numverts, inVertsArray, inNormalsArray, mesh->stArray, mesh->numtris, mesh->elems, inSVectorsArray );
alias_mesh.xyzArray = calcVerts ? inVertsArray : mesh->xyzArray;
}
else
{
features |= MF_KEEPLOCK;
}
alias_mesh.elems = mesh->elems;
alias_mesh.numElems = mesh->numtris * 3;
alias_mesh.numVertexes = mesh->numverts;
alias_mesh.stArray = mesh->stArray;
if( features & MF_NORMALS )
alias_mesh.normalsArray = calcNormals ? inNormalsArray : mesh->normalsArray;
if( features & MF_SVECTORS )
alias_mesh.sVectorsArray = calcSTVectors ? inSVectorsArray : mesh->sVectorsArray;
R_RotateForEntity( e );
R_PushMesh( &alias_mesh, features );
R_RenderMeshBuffer( mb );
}
/*
=================
R_DrawAliasModel
=================
*/
void R_DrawAliasModel( const meshbuffer_t *mb )
{
ref_entity_t *e = RI.currententity;
if( OCCLUSION_QUERIES_ENABLED( RI ) && OCCLUSION_TEST_ENTITY( e ) )
{
ref_shader_t *shader;
MB_NUM2SHADER( mb->shaderkey, shader );
if( !R_GetOcclusionQueryResultBool( shader->type == SHADER_PLANAR_SHADOW ? OQ_PLANARSHADOW : OQ_ENTITY,
e - r_entities, true ) )
return;
}
// hack the depth range to prevent view model from poking into walls
if( e->flags & RF_WEAPONMODEL )
pglDepthRange( gldepthmin, gldepthmin + 0.3 * ( gldepthmax - gldepthmin ) );
// backface culling for left-handed weapons
if( e->flags & RF_CULLHACK )
GL_FrontFace( !glState.frontFace );
if( !r_lerpmodels->integer )
e->backlerp = 0;
R_DrawAliasFrameLerp( mb, e->backlerp );
if( e->flags & RF_WEAPONMODEL )
pglDepthRange( gldepthmin, gldepthmax );
if( e->flags & RF_CULLHACK )
GL_FrontFace( !glState.frontFace );
}
/*
=================
R_AliasModelBBox
=================
*/
float R_AliasModelBBox( ref_entity_t *e, vec3_t mins, vec3_t maxs )
{
ref_model_t *mod;
mod = R_AliasModelLOD( e );
if( !mod )
return 0;
R_AliasModelLerpBBox( e, mod );
VectorCopy( alias_mins, mins );
VectorCopy( alias_maxs, maxs );
return alias_radius;
}
/*
=================
R_CullAliasModel
=================
*/
bool R_CullAliasModel( ref_entity_t *e )
{
int i, j, clipped;
bool frustum, query;
unsigned int modhandle, numtris;
ref_model_t *mod;
ref_shader_t *shader;
meshbuffer_t *mb;
maliasmodel_t *aliasmodel;
maliasmesh_t *mesh;
mod = R_AliasModelLOD( e );
if( !( aliasmodel = ( ( maliasmodel_t * )mod->extradata ) ) || !aliasmodel->nummeshes )
return true;
R_AliasModelLerpBBox( e, mod );
modhandle = Mod_Handle( mod );
clipped = R_CullModel( e, alias_mins, alias_maxs, alias_radius );
frustum = clipped & 1;
if( clipped & 2 )
return true;
query = OCCLUSION_QUERIES_ENABLED( RI ) && OCCLUSION_TEST_ENTITY( e ) ? true : false;
if( !frustum && query )
R_IssueOcclusionQuery( R_GetOcclusionQueryNum( OQ_ENTITY, e - r_entities ), e, alias_mins, alias_maxs );
if( ( RI.refdef.rdflags & RDF_NOWORLDMODEL )
|| ( r_shadows->integer != SHADOW_PLANAR && !( r_shadows->integer == SHADOW_MAPPING && ( e->flags & RF_PLANARSHADOW ) ) )
|| R_CullPlanarShadow( e, alias_mins, alias_maxs, query ) )
return frustum; // entity is not in PVS or shadow is culled away by frustum culling
numtris = 0;
for( i = 0, mesh = aliasmodel->meshes; i < aliasmodel->nummeshes; i++, mesh++ )
{
shader = NULL;
if( e->customSkin )
shader = R_FindShaderForSkinFile( e->customSkin, mesh->name );
else if( e->customShader )
shader = e->customShader;
#ifdef QUAKE2_JUNK
else if( ( e->skinNum >= 0 ) && ( e->skinNum < aliasmodel->numskins ) )
shader = aliasmodel->skins[e->skinNum].shader;
#endif
else if( mesh->numskins )
{
for( j = 0; j < mesh->numskins; j++ )
{
shader = mesh->skins[j].shader;
if( shader && shader->sort <= SHADER_SORT_ALPHATEST )
break;
shader = NULL;
}
}
if( shader && ( shader->sort <= SHADER_SORT_ALPHATEST ) )
{
mb = R_AddMeshToList( MB_MODEL, NULL, R_PlanarShadowShader(), -( i+1 ) );
if( mb )
mb->LODModelHandle = modhandle;
}
}
return frustum;
}
/*
=================
R_AddAliasModelToList
=================
*/
void R_AddAliasModelToList( ref_entity_t *e )
{
int i, j;
unsigned int modhandle, entnum = e - r_entities;
mfog_t *fog = NULL;
ref_model_t *mod;
ref_shader_t *shader;
maliasmodel_t *aliasmodel;
maliasmesh_t *mesh;
mod = R_AliasModelLOD( e );
aliasmodel = ( maliasmodel_t * )mod->extradata;
modhandle = Mod_Handle( mod );
if( RI.params & RP_SHADOWMAPVIEW )
{
if( r_entShadowBits[entnum] & RI.shadowGroup->bit )
{
if( !r_shadows_self_shadow->integer )
r_entShadowBits[entnum] &= ~RI.shadowGroup->bit;
if( e->flags & RF_WEAPONMODEL )
return;
}
else
{
R_AliasModelLerpBBox( e, mod );
if( !R_CullModel( e, alias_mins, alias_maxs, alias_radius ) )
r_entShadowBits[entnum] |= RI.shadowGroup->bit;
return; // mark as shadowed, proceed with caster otherwise
}
}
else
{
fog = R_FogForSphere( e->origin, alias_radius );
#if 0
if( !( e->flags & RF_WEAPONMODEL ) && fog )
{
R_AliasModelLerpBBox( e, mod );
if( R_CompletelyFogged( fog, e->origin, alias_radius ) )
return;
}
#endif
}
for( i = 0, mesh = aliasmodel->meshes; i < aliasmodel->nummeshes; i++, mesh++ )
{
shader = NULL;
if( e->customSkin )
shader = R_FindShaderForSkinFile( e->customSkin, mesh->name );
else if( e->customShader )
shader = e->customShader;
#ifdef QUAKE2_JUNK
else if( ( e->skinNum >= 0 ) && ( e->skinNum < aliasmodel->numskins ) )
shader = aliasmodel->skins[e->skinNum].shader;
#endif
else if( mesh->numskins )
{
for( j = 0; j < mesh->numskins; j++ )
{
shader = mesh->skins[j].shader;
if( shader )
R_AddModelMeshToList( modhandle, fog, shader, i );
}
continue;
}
if( shader )
R_AddModelMeshToList( modhandle, fog, shader, i );
}
}