openmohaa/code/renderergl1/tr_model.cpp

2037 lines
No EOL
50 KiB
C++

/*
===========================================================================
Copyright (C) 2024 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA 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.
OpenMoHAA 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 OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_models.cpp -- model loading and caching
#include "tr_local.h"
#include "tiki.h"
#include <vector.h>
#define LL(x) x=LittleLong(x)
qboolean g_bInfoworldtris = qfalse;
static int entityNumIndexes[MAX_ENTITIES];
static int staticModelNumIndexes[4095];
static int R_CullSkelModel(dtiki_t* tiki, refEntity_t* e, skelAnimFrame_t* newFrame, float fScale, float* vLocalOrg);
/*
** R_GetModelByHandle
*/
model_t *R_GetModelByHandle( qhandle_t hModel ) {
model_t *mod;
// out of range gets the default model
if( hModel < 1 || hModel >= tr.numModels ) {
return &tr.models[0];
}
mod = &tr.models[hModel];
return mod;
}
/*
** R_Model_GetHandle
*/
dtiki_t *R_Model_GetHandle( qhandle_t handle ) {
model_t *model = R_GetModelByHandle( handle );
if( model->type == MOD_TIKI ) {
return model->d.tiki;
}
return NULL;
}
//===============================================================================
/*
** R_FreeModel
*/
void R_FreeModel( model_t *mod ) {
if( mod->type == MOD_TIKI ) {
ri.CG_EndTiki( mod->d.tiki );
}
memset( mod, 0, sizeof( model_t ) );
}
/*
** R_AllocModel
*/
model_t *R_AllocModel( void ) {
int i;
for( i = 0; i < tr.numModels; i++ )
{
if( !tr.models[ i ].name[ 0 ] ) {
break;
}
}
if( i == tr.numModels ) {
if( i == MAX_MOD_KNOWN ) {
return NULL;
}
tr.numModels++;
}
tr.models[ i ].index = i;
return &tr.models[ i ];
}
/*
** RE_FreeModels
*/
void RE_FreeModels( void ) {
int hModel;
for( hModel = 0; hModel < tr.numModels; hModel++ )
{
if( !tr.models[ hModel ].name[ 0 ] ) {
continue;
}
R_FreeModel( &tr.models[ hModel ] );
}
}
/*
** R_RegisterShaders
*/
void R_RegisterShaders( model_t *mod ) {
dtiki_t *tiki;
int i, j;
dtikisurface_t *psurface;
shader_t *sh;
tiki = mod->d.tiki;
for( i = 0; i < tiki->num_surfaces; i++ )
{
psurface = &tiki->surfaces[ i ];
assert( psurface->numskins <= MAX_TIKI_SHADER );
for( j = 0; j < psurface->numskins; j++ )
{
if( psurface->shader[ j ][ 0 ] ) {
sh = R_FindShader( psurface->shader[ j ], LIGHTMAP_NONE, !( psurface->flags & TIKI_SURF_NOMIPMAPS), r_picmip->integer, qtrue, qtrue );
psurface->hShader[ j ] = sh->index;
} else {
psurface->hShader[ j ] = 0;
}
}
}
}
/*
** RE_UnregisterServerModel
*/
void RE_UnregisterServerModel( qhandle_t hModel ) {
if( hModel < 0 || hModel >= MAX_MOD_KNOWN ) {
return;
}
if( tr.models[ hModel ].serveronly ) {
R_FreeModel( &tr.models[ hModel ] );
}
}
/*
** R_RegisterModelInternal
*/
static qhandle_t R_RegisterModelInternal( const char *name, qboolean bBeginTiki, qboolean use ) {
model_t *mod;
qhandle_t hModel;
const char *ptr;
if( !name || !*name )
{
ri.Printf( PRINT_ALL, "RE_RegisterModel: NULL name\n" );
return 0;
}
if( strlen( name ) >= 128 )
{
Com_Printf( "Model name exceeds MAX_MODEL_NAME\n" );
return 0;
}
//
// search the currently loaded models
//
for( hModel = 1; hModel < tr.numModels; hModel++ ) {
mod = &tr.models[ hModel ];
if( !Q_stricmp( mod->name, name ) ) {
if( mod->type == MOD_BAD ) {
return 0;
}
return hModel;
}
}
// allocate a new model_t
if( ( mod = R_AllocModel() ) == NULL ) {
ri.Printf( PRINT_WARNING, "RE_RegisterModel: R_AllocModel() failed for '%s'\n", name );
return 0;
}
// only set the name after the model has been successfully loaded
Q_strncpyz( mod->name, name, sizeof( mod->name ) );
// make sure the render thread is stopped
R_IssuePendingRenderCommands();
mod->serveronly = qtrue;
//
// load the files
//
ptr = strrchr( name, '.' );
if( ptr )
{
ptr++;
if( !stricmp( ptr, "spr" ) )
{
mod->d.sprite = SPR_RegisterSprite( name );
Q_strncpyz( mod->name, name, sizeof( mod->name ) );
if( mod->d.sprite )
{
mod->type = MOD_SPRITE;
return mod->index;
}
}
else if( !stricmp( ptr, "tik" ) )
{
mod->d.tiki = ri.TIKI_RegisterTikiFlags( name, use );
Q_strncpyz( mod->name, name, sizeof( mod->name ) );
if( mod->d.tiki )
{
mod->type = MOD_TIKI;
R_RegisterShaders( mod );
if( bBeginTiki ) {
ri.CG_ProcessInitCommands( mod->d.tiki, NULL );
}
return mod->index;
}
}
}
ri.Printf( PRINT_ERROR, "RE_RegisterModel: Registration failed for '%s'\n", name );
mod->type = MOD_BAD;
return 0;
}
/*
** RE_RegisterServerModel
*/
qhandle_t RE_RegisterServerModel( const char *name ) {
return R_RegisterModelInternal( name, qtrue, qfalse );
}
/*
** RE_SpawnEffectModel
*/
qhandle_t RE_SpawnEffectModel( const char *szModel, vec3_t vPos, vec3_t *axis ) {
refEntity_t new_entity;
memset( &new_entity, 0, sizeof( refEntity_t ) );
memset( &new_entity.shaderRGBA, 255, sizeof( byte ) * 4 );
VectorCopy( vPos, new_entity.origin );
new_entity.scale = 1.0;
if( axis ) {
AxisCopy( axis, new_entity.axis );
}
new_entity.hModel = R_RegisterModelInternal( szModel, qfalse, qtrue );
if( new_entity.hModel )
{
tr.models[ new_entity.hModel ].serveronly = qfalse;
ri.CG_ProcessInitCommands( tr.models[ new_entity.hModel ].d.tiki, &new_entity );
}
return new_entity.hModel;
}
/*
** RE_RegisterModel
*/
qhandle_t RE_RegisterModel( const char *name ) {
qhandle_t handle;
handle = R_RegisterModelInternal( name, qtrue, qtrue );
if( handle ) {
tr.models[ handle ].serveronly = qfalse;
}
return handle;
}
//=============================================================================
/*
===============
R_ModelInit
===============
*/
void R_ModelInit( void ) {
model_t *mod;
int i;
// leave a space for NULL model
tr.numModels = 0;
mod = R_AllocModel();
Q_strncpyz( mod->name, "** BAD MODEL **", sizeof( mod->name ) );
mod->type = MOD_BAD;
for (i = 0; i < sizeof(tr.skel_index) / sizeof(tr.skel_index[0]); i++) {
tr.skel_index[i] = -1;
}
}
/*
================
R_Modellist_f
================
*/
void R_Modellist_f( void ) {
int i;
for( i = 1; i < tr.numModels; i++ ) {
ri.Printf( PRINT_ALL, "%s\n", tr.models[ i ].name );
}
}
/*
====================
R_ModelRadius
====================
*/
float R_ModelRadius( qhandle_t handle ) {
int j;
vec3_t bounds[ 2 ];
model_t *model;
float radius, maxRadius;
vec3_t tmpVec;
float w;
model = R_GetModelByHandle( handle );
switch( model->type )
{
case MOD_BRUSH:
maxRadius = 0.0;
VectorCopy( model->d.bmodel->bounds[ 0 ], bounds[ 0 ] );
VectorCopy( model->d.bmodel->bounds[ 1 ], bounds[ 1 ] );
for( j = 0; j < 8; j++ ) {
tmpVec[ 0 ] = bounds[ j & 1 ? 1 : 0 ][ 0 ];
tmpVec[ 1 ] = bounds[ j & 2 ? 1 : 0 ][ 1 ];
tmpVec[ 2 ] = bounds[ j & 4 ? 1 : 0 ][ 2 ];
radius = VectorLength( tmpVec );
if( maxRadius < radius ) {
maxRadius = radius;
}
}
break;
case MOD_TIKI:
return ri.TIKI_GlobalRadius( model->d.tiki );
case MOD_SPRITE:
maxRadius = model->d.sprite->width * model->d.sprite->scale * 0.5;
w = model->d.sprite->height * model->d.sprite->scale * 0.5;
if( maxRadius <= w ) {
maxRadius = w;
}
break;
default:
maxRadius = 0.0;
}
return maxRadius;
}
/*
====================
R_ModelBounds
====================
*/
void R_ModelBounds( qhandle_t handle, vec3_t mins, vec3_t maxs ) {
model_t *model;
model = R_GetModelByHandle( handle );
switch( model->type )
{
default:
case MOD_BAD:
VectorClear( mins );
VectorClear( maxs );
break;
case MOD_BRUSH:
VectorCopy( model->d.bmodel->bounds[ 0 ], mins );
VectorCopy( model->d.bmodel->bounds[ 1 ], maxs );
break;
case MOD_TIKI:
ri.TIKI_CalculateBounds( model->d.tiki, 1.0, mins, maxs );
break;
case MOD_SPRITE:
mins[ 0 ] = -model->d.sprite->width * model->d.sprite->scale * 0.5;
mins[ 1 ] = -model->d.sprite->height * model->d.sprite->scale * 0.5;
mins[ 2 ] = -0.0;
maxs[ 0 ] = model->d.sprite->width * model->d.sprite->scale * 0.5;
maxs[ 1 ] = model->d.sprite->height * model->d.sprite->scale * 0.5;
maxs[ 2 ] = 0.0;
break;
}
}
#if 0
// Replaced by TIKI_FindSkelByHeader
/*
====================
GetModelPath
====================
*/
const char *GetModelPath( skelHeaderGame_t *skelmodel ) {
int i;
int num;
skelcache_t *cache;
num = cache_numskel;
for( i = 0; i < TIKI_MAX_SKELCACHE; i++ )
{
cache = &skelcache[ i ];
if( cache->skel )
{
if( cache->skel == skelmodel ) {
return cache->path;
}
num--;
if( num < 0 ) {
break;
}
}
}
return NULL;
}
#endif
/*
====================
GetLodCutoff
====================
*/
int GetLodCutoff( skelHeaderGame_t *skelmodel, float lod_val, int renderfx ) {
lodControl_t *LOD;
float f;
float fLODCap;
LOD = skelmodel->pLOD;
if( renderfx & RF_DEPTHHACK )
{
fLODCap = LOD->maxMetric + ( LOD->minMetric - LOD->maxMetric ) * r_lodviewmodelcap->value;
}
else
{
f = ( LOD->minMetric - LOD->maxMetric ) * r_lodcap->value + LOD->maxMetric;
fLODCap = ( lod_val - LOD->maxMetric ) * r_lodscale->value + LOD->maxMetric;
if( fLODCap > f ) {
fLODCap = f;
}
}
if( fLODCap >= LOD->minMetric || !r_uselod->integer )
{
return LOD->curve[ 0 ].val;
}
else if( fLODCap <= LOD->maxMetric )
{
return LOD->curve[ 4 ].val;
}
else if( fLODCap <= LOD->consts[ 3 ].cutoff )
{
return fLODCap * LOD->consts[ 3 ].scale + LOD->consts[ 3 ].base;
}
else if( fLODCap <= LOD->consts[ 2 ].cutoff )
{
return fLODCap * LOD->consts[ 2 ].scale + LOD->consts[ 2 ].base;
}
else if( fLODCap <= LOD->consts[ 1 ].cutoff )
{
return fLODCap * LOD->consts[ 1 ].scale + LOD->consts[ 1 ].base;
}
else
{
return fLODCap * LOD->consts[ 0 ].scale + LOD->consts[ 0 ].base;
}
}
/*
===============
R_SaveLODFile
===============
*/
static void R_SaveLODFile(const char *path, lodControl_t *LOD)
{
fileHandle_t file = ri.FS_OpenFileWrite(path);
if (!file) {
ri.Printf(PRINT_WARNING, "SaveLODFile: Failed to open file %s\n", path);
return;
}
ri.FS_Write(LOD, sizeof(lodControl_t), file);
}
/*
====================
GetToolLodCutoff
====================
*/
int GetToolLodCutoff( skelHeaderGame_t *skelmodel, float lod_val ) {
lodControl_t *LOD;
float totalRange;
int i;
char lodPath[ 256 ];
char *ext;
LOD = skelmodel->pLOD;
totalRange = 0.0;
for( i = 0; i < 10; i++ )
{
if( skelmodel->lodIndex[ i ] > 0 ) {
totalRange = skelmodel->lodIndex[ i ];
break;
}
}
if( lod_save->integer == 1 )
{
ri.Cvar_Set( "lod_save", "0" );
Q_strncpyz( lodPath, ri.TIKI_FindSkelByHeader( skelmodel )->path, sizeof( lodPath ) );
ext = strstr( lodPath, "skd" );
strcpy( ext, "lod" );
R_SaveLODFile( lodPath, LOD );
}
if( lod_mesh->modified )
{
lod_mesh->modified = qfalse;
ri.Cvar_Set( "lod_minLOD", va( "%f", LOD->minMetric ) );
ri.Cvar_Set( "lod_maxLOD", va( "%f", LOD->maxMetric ) );
ri.Cvar_Set( "lod_LOD_slider", va( "%f", 0.5 ) );
ri.Cvar_Set( "lod_curve_0_slider", va( "%f", LOD->curve[ 0 ].val / totalRange ) );
ri.Cvar_Set( "lod_curve_1_slider", va( "%f", LOD->curve[ 1 ].val / totalRange ) );
ri.Cvar_Set( "lod_curve_2_slider", va( "%f", LOD->curve[ 2 ].val / totalRange ) );
ri.Cvar_Set( "lod_curve_3_slider", va( "%f", LOD->curve[ 3 ].val / totalRange ) );
ri.Cvar_Set( "lod_curve_4_slider", va( "%f", LOD->curve[ 4 ].val / totalRange ) );
}
ri.Cvar_Set( "lod_curve_0_val", va( "%f", lod_curve_0_slider->value * totalRange ) );
ri.Cvar_Set( "lod_curve_1_val", va( "%f", lod_curve_1_slider->value * totalRange ) );
ri.Cvar_Set( "lod_curve_2_val", va( "%f", lod_curve_2_slider->value * totalRange ) );
ri.Cvar_Set( "lod_curve_3_val", va( "%f", lod_curve_3_slider->value * totalRange ) );
ri.Cvar_Set( "lod_curve_4_val", va( "%f", lod_curve_4_slider->value * totalRange ) );
LOD->minMetric = lod_minLOD->value;
LOD->maxMetric = lod_maxLOD->value;
LOD->curve[ 0 ].val = lod_curve_0_val->value;
LOD->curve[ 1 ].val = lod_curve_1_val->value;
LOD->curve[ 2 ].val = lod_curve_2_val->value;
LOD->curve[ 3 ].val = lod_curve_3_val->value;
LOD->curve[ 4 ].val = lod_curve_4_val->value;
ri.TIKI_CalcLodConsts( LOD );
return GetLodCutoff( skelmodel, lod_val, 0 );
}
/*
==============
R_GetTagPositionAndOrientation
==============
*/
orientation_t R_GetTagPositionAndOrientation( refEntity_t *ent, int tagnum ) {
int i;
orientation_t tag_or, new_or;
tag_or = RE_TIKI_Orientation( ent, tagnum );
VectorCopy( ent->origin, new_or.origin );
for( i = 0; i < 3; i++ )
{
VectorMA( new_or.origin, tag_or.origin[ i ], ent->axis[ i ], new_or.origin );
}
MatrixMultiply( tag_or.axis, ent->axis, new_or.axis );
return new_or;
}
/*
==============
RB_DrawSkeletor
==============
*/
void RB_DrawSkeletor(trRefEntity_t* ent) {
int i;
dtiki_t *tiki;
skeletor_c *skeletor;
tiki = R_Model_GetHandle( ent->e.hModel );
skeletor = ( skeletor_c * )ri.TIKI_GetSkeletor( tiki, ENTITYNUM_NONE );
if( r_showSkeleton->integer == 1 ) {
//vec3_t vForward, vRight, vUp;
orientation_t ori;
orientation_t parent_or;
int iParentBone;
qglLineWidth( 3 );
qglBegin( GL_LINES );
for( i = 0; i < ri.TIKI_GetNumChannels(tiki); i++ ) { // draw a skeleton
ori = R_GetTagPositionAndOrientation( &ent->e, i );
iParentBone = ri.SKEL_GetBoneParent(skeletor, i);
if( iParentBone != -1 ) {
parent_or = R_GetTagPositionAndOrientation( &ent->e, iParentBone );
} else {
VectorCopy( ent->e.origin, parent_or.origin );
AxisCopy( ent->e.axis, parent_or.axis );
}
qglColor3f(1, 1, 1);
qglVertex3fv(parent_or.origin);
qglVertex3fv(ori.origin);
// draw bone axis
/*glColor3f( 1, 0, 0 );
glVertex3fv( parent_or.origin );
VectorAdd( parent_or.origin, parent_or.axis[ 0 ], vForward );
glVertex3fv( vForward );
glVertex3fv( vForward );
glVertex3fv( ori.origin );
glColor3f( 0, 1, 0 );
glVertex3fv( parent_or.origin );
VectorAdd( parent_or.origin, parent_or.axis[ 1 ], vRight );
glVertex3fv( vRight );
glVertex3fv( vRight );
glVertex3fv( ori.origin );
glColor3f( 0, 0, 1 );
glVertex3fv( parent_or.origin );
VectorAdd( parent_or.origin, parent_or.axis[ 2 ], vUp );
glVertex3fv( vUp );
glVertex3fv( vUp );
glVertex3fv( ori.origin );*/
}
qglEnd();
qglLineWidth( 1 );
}
else if( r_showSkeleton->integer == 2 ) { // draw skeleton with bones
orientation_t ori;
orientation_t parent_or;
int iParentBone;
qglLineWidth( 3 );
qglBegin( GL_LINES );
for( i = 0; i < ri.TIKI_GetNumChannels(tiki); i++ ) {
iParentBone = ri.SKEL_GetBoneParent(skeletor, i);
if( iParentBone > 0 ) {
ori = R_GetTagPositionAndOrientation( &ent->e, i );
parent_or = R_GetTagPositionAndOrientation( &ent->e, iParentBone );
qglColor3f( 1, 1, 1 );
qglVertex3fv( parent_or.origin );
qglVertex3fv( ori.origin );
}
}
qglEnd();
qglBegin( GL_LINES );
for( i = 0; i < ri.TIKI_GetNumChannels(tiki); i++ ) {
vec3_t up, down, front;
ori = R_GetTagPositionAndOrientation( &ent->e, i );
VectorCopy( ori.origin, up );
up[ 1 ] += 5;
VectorCopy( ori.origin, down );
down[ 1 ] += 5;
VectorCopy( ori.origin, front );
front[ 0 ] += 5;
qglColor3f( 1, 0, 1 );
qglVertex3fv( front );
qglVertex3fv( ori.origin );
qglVertex3fv( down );
qglVertex3fv( ori.origin );
qglVertex3fv( up );
qglVertex3fv( ori.origin );
}
qglEnd();
qglLineWidth( 1 );
}
}
surfaceType_t skelSurface = SF_TIKI_SKEL;
/*
==============
R_AddSkelSurfaces
==============
*/
void R_AddSkelSurfaces( trRefEntity_t *ent ) {
dtiki_t *tiki;
qboolean personalModel;
float tiki_scale;
vec3_t tiki_localorigin;
vec3_t tiki_worldorigin;
skelBoneCache_t *outbones;
//int tikiSurfNumOffset;
static cvar_t *vmEntity = NULL;
skeletor_c *skeletor;
float radius;
SkelVec3 centroid;
skelAnimFrame_t *newFrame;
int added;
shader_t *shader;
dtikisurface_t *dsurf;
byte *bsurf;
//float range;
//int render_count, total_tris;
skelSurfaceGame_t *surface;
//int skinnum;
//float target;
Vector newDistance;
//vec3_t org;
int i;
int mesh;
int iRadiusCull = 0;
int num_tags;
tiki = ent->e.tiki;
if( !vmEntity ) {
vmEntity = ri.Cvar_Get( "viewmodelentity", "", 0 );
}
R_UpdatePoseInternal( &ent->e );
// don't add third_person objects if in a portal
personalModel = ( ent->e.renderfx & RF_THIRD_PERSON ) && !tr.viewParms.isPortal;
outbones = &TIKI_Skel_Bones[ TIKI_Skel_Bones_Index ];
num_tags = ri.TIKI_GetNumChannels(tiki);
if( num_tags + TIKI_Skel_Bones_Index > MAX_SKELBONES )
{
ri.Printf( PRINT_DEVELOPER, "R_AddSkelSurfaces: too many skeleton models visible on '%s'\n", tiki->a->name );
return;
}
tiki_scale = tiki->load_scale * ent->e.scale;
VectorScale( tiki->load_origin, tiki_scale, tiki_localorigin );
R_LocalPointToWorld( tiki_localorigin, tiki_worldorigin );
radius = R_GetRadius( &ent->e );
if( !lod_tool->integer )
{
iRadiusCull = R_CullPointAndRadius( tiki_worldorigin, radius );
if (r_showcull->integer & 2)
{
switch (iRadiusCull)
{
case CULL_IN:
R_DebugCircle(tiki_worldorigin, radius * 1.2f, 0, 1, 0, 0.5f, qfalse);
break;
case CULL_CLIP:
R_DebugCircle(tiki_worldorigin, radius * 1.2f, 0, 1, 0, 0.5f, qfalse);
break;
case CULL_OUT:
R_DebugCircle(tiki_worldorigin, radius * 1.2f + 32.f, 1, 0.2f, 0.2f, 0.5f, qfalse);
break;
}
}
switch (iRadiusCull)
{
case CULL_IN:
tr.pc.c_sphere_cull_md3_in++;
break;
case CULL_CLIP:
tr.pc.c_sphere_cull_md3_clip++;
break;
case CULL_OUT:
tr.pc.c_sphere_cull_md3_out++;
break;
}
}
if (tiki->a->bIsCharacter) {
if (tr.viewParms.isPortal) {
ent->lodpercentage[1] = R_CalcLod(tiki_worldorigin, 92.f / ent->e.scale);
} else {
ent->lodpercentage[0] = R_CalcLod(tiki_worldorigin, 92.f / ent->e.scale);
}
} else {
if (tr.viewParms.isPortal) {
ent->lodpercentage[1] = R_CalcLod(tiki_worldorigin, radius / ent->e.scale);
} else {
ent->lodpercentage[0] = R_CalcLod(tiki_worldorigin, radius / ent->e.scale);
}
}
newFrame = ( skelAnimFrame_t * )ri.Hunk_AllocateTempMemory( sizeof( skelAnimFrame_t ) + ri.TIKI_GetNumChannels(tiki) * sizeof( SkelMat4 ) );
R_GetFrame( &ent->e, newFrame );
if (lod_tool->integer || iRadiusCull != CULL_CLIP || R_CullSkelModel(tiki, &ent->e, newFrame, tiki_scale, tiki_localorigin) != CULL_OUT)
{
//
// copy bones position and axis
//
for (i = 0; i < num_tags; i++)
{
VectorCopy(newFrame->bones[i][3], outbones->offset);
outbones->matrix[0][0] = newFrame->bones[i][0][0];
outbones->matrix[0][1] = newFrame->bones[i][0][1];
outbones->matrix[0][2] = newFrame->bones[i][0][2];
outbones->matrix[0][3] = 0;
outbones->matrix[1][0] = newFrame->bones[i][1][0];
outbones->matrix[1][1] = newFrame->bones[i][1][1];
outbones->matrix[1][2] = newFrame->bones[i][1][2];
outbones->matrix[1][3] = 0;
outbones->matrix[2][0] = newFrame->bones[i][2][0];
outbones->matrix[2][1] = newFrame->bones[i][2][1];
outbones->matrix[2][2] = newFrame->bones[i][2][2];
outbones->matrix[2][3] = 0;
outbones++;
}
}
ri.Hunk_FreeTempMemory( newFrame );
ent->e.bonestart = TIKI_Skel_Bones_Index;
TIKI_Skel_Bones_Index += num_tags;
ent->e.hasMorph = qfalse;
//
// get the skeletor
//
skeletor = ( skeletor_c * )ri.TIKI_GetSkeletor( tiki, ent->e.entityNumber );
//
// add morphs
//
added = ri.SKEL_GetMorphWeightFrame( skeletor, ent->e.frameInfo[ 0 ].index, ent->e.frameInfo[ 0 ].time, &skeletorMorphCache[ skeletorMorphCacheIndex ] );
ent->e.morphstart = skeletorMorphCacheIndex;
if( added )
{
// found morphs
skeletorMorphCacheIndex += added;
ent->e.hasMorph = qtrue;
}
//
// draw all meshes
//
dsurf = tiki->surfaces;
bsurf = &ent->e.surfaces[ 0 ];
for( mesh = 0; mesh < tiki->numMeshes; mesh++ )
{
skelHeaderGame_t *skelmodel = ri.TIKI_GetSkel( tiki->mesh[ mesh ] );
if( !skelmodel )
{
ri.Printf( PRINT_DEVELOPER, "R_AddSkelSurfaces: couldn't get skel model in '%s'\n", tiki->a->name );
return;
}
if( lod_tool->integer && !stricmp( ent->e.tiki->a->name, lod_tikiname->string ) )
{
if( lod_mesh->integer > tiki->numMeshes - 1 ) {
ri.Cvar_Set( "lod_mesh", va( "%d", tiki->numMeshes - 1 ) );
}
if( mesh == lod_mesh->integer ) {
if( skelmodel->pLOD ) {
break;
}
}
}
//
// draw all surfaces
//
surface = skelmodel->pSurfaces;
for( i = 0; i < skelmodel->numSurfaces; i++, dsurf++, bsurf++, surface = surface->pNext ) {
if( *bsurf & 4 ) {
continue;
}
shader = NULL;
surface->ident = SF_TIKI_SKEL;
// use a custom shader if specified
if( !( ent->e.customShader ) || ( ent->e.renderfx & RF_CUSTOMSHADERPASS ) ) {
int iShaderNum = ent->e.skinNum + ( *bsurf & 3 );
if( iShaderNum >= dsurf->numskins ) {
iShaderNum = 0;
}
shader = tr.shaders[ dsurf->hShader[ iShaderNum ] ];
} else {
shader = R_GetShaderByHandle( ent->e.customShader );
}
if( !personalModel ) {
if( ( *bsurf & 0x40 ) && ( dsurf->numskins > 1 ) ) {
int iShaderNum = ent->e.skinNum + ( *bsurf & 2 );
R_AddDrawSurf( ( surfaceType_t * )surface, tr.shaders[ dsurf->hShader[ iShaderNum ] ], 0 );
R_AddDrawSurf( ( surfaceType_t * )surface, tr.shaders[ dsurf->hShader[ iShaderNum + 1 ] ], 0);
} else {
R_AddDrawSurf( ( surfaceType_t * )surface, shader, 0);
}
}
if( ( ent->e.customShader ) && ( ent->e.renderfx & RF_CUSTOMSHADERPASS ) ) {
shader = R_GetShaderByHandle( ent->e.customShader );
R_AddDrawSurf( ( surfaceType_t * )surface, shader, 0);
}
}
}
// FIXME: setup LOD
}
/*
=============
SkelVertGetNormal
=============
*/
inline static void SkelVertGetNormal( skeletorVertex_t *vert, skelBoneCache_t *bone, vec3_t out ) {
out[ 0 ] = vert->normal[ 0 ] * bone->matrix[ 0 ][ 0 ] +
vert->normal[ 1 ] * bone->matrix[ 1 ][ 0 ] +
vert->normal[ 2 ] * bone->matrix[ 2 ][ 0 ];
out[ 1 ] = vert->normal[ 0 ] * bone->matrix[ 0 ][ 1 ] +
vert->normal[ 1 ] * bone->matrix[ 1 ][ 1 ] +
vert->normal[ 2 ] * bone->matrix[ 2 ][ 1 ];
out[ 2 ] = vert->normal[ 0 ] * bone->matrix[ 0 ][ 2 ] +
vert->normal[ 1 ] * bone->matrix[ 1 ][ 2 ] +
vert->normal[ 2 ] * bone->matrix[ 2 ][ 2 ];
}
/*
=============
SkelMorphGetXyz
=============
*/
inline static void SkelMorphGetXyz( skeletorMorph_t *morph, int *morphcache, vec3_t out ) {
VectorMA(out, *morphcache, morph->offset, out);
}
/*
=============
SkelWeightGetXyz
=============
*/
inline static void SkelWeightGetXyz( skelWeight_t *weight, skelBoneCache_t *bone, vec3_t out ) {
out[ 0 ] += ( ( weight->offset[ 0 ] * bone->matrix[ 0 ][ 0 ] +
weight->offset[ 1 ] * bone->matrix[ 1 ][ 0 ] +
weight->offset[ 2 ] * bone->matrix[ 2 ][ 0 ] ) +
bone->offset[ 0 ] ) * weight->boneWeight;
out[ 1 ] += ( ( weight->offset[ 0 ] * bone->matrix[ 0 ][ 1 ] +
weight->offset[ 1 ] * bone->matrix[ 1 ][ 1 ] +
weight->offset[ 2 ] * bone->matrix[ 2 ][ 1 ] ) +
bone->offset[ 1 ] ) * weight->boneWeight;
out[ 2 ] += ( ( weight->offset[ 0 ] * bone->matrix[ 0 ][ 2 ] +
weight->offset[ 1 ] * bone->matrix[ 1 ][ 2 ] +
weight->offset[ 2 ] * bone->matrix[ 2 ][ 2 ] ) +
bone->offset[ 2 ] ) * weight->boneWeight;
}
/*
=============
SkelWeightMorphGetXyz
=============
*/
inline static void SkelWeightMorphGetXyz( skelWeight_t *weight, skelBoneCache_t *bone, vec3_t totalmorph, vec3_t out ) {
vec3_t point;
VectorAdd( totalmorph, weight->offset, point );
out[ 0 ] += ( ( point[ 0 ] * bone->matrix[ 0 ][ 0 ] +
point[ 1 ] * bone->matrix[ 1 ][ 0 ] +
point[ 2 ] * bone->matrix[ 2 ][ 0 ] ) +
bone->offset[ 0 ] ) * weight->boneWeight;
out[ 1 ] += ( ( point[ 0 ] * bone->matrix[ 0 ][ 1 ] +
point[ 1 ] * bone->matrix[ 1 ][ 1 ] +
point[ 2 ] * bone->matrix[ 2 ][ 1 ] ) +
bone->offset[ 1 ] ) * weight->boneWeight;
out[ 2 ] += ( ( point[ 0 ] * bone->matrix[ 0 ][ 2 ] +
point[ 1 ] * bone->matrix[ 1 ][ 2 ] +
point[ 2 ] * bone->matrix[ 2 ][ 2 ] ) +
bone->offset[ 2 ] ) * weight->boneWeight;
}
/*
=============
RB_SkelMesh
=============
*/
void RB_SkelMesh( skelSurfaceGame_t *sf ) {
unsigned int baseIndex, baseVertex;
unsigned int render_count;
unsigned int indexes;
float* outXyz;
vec4_t* outNormal;
skelIndex_t* triangles;
skelIndex_t* collapse_map;
skeletorVertex_t* newVerts;
skeletorMorph_t* morph;
skelWeight_t* weight;
int vertNum;
int morphNum;
int weightNum;
skelBoneCache_t* bones;
skelBoneCache_t* bone;
int* morphs;
int* morphcache;
float scale;
dtiki_t* tiki;
int mesh;
int surf;
int i;
skelHeaderGame_t* skelmodel;
skelSurfaceGame_t* psurface;
qboolean bFound;
short collapse[TIKI_MAX_VERTEXES];
if (!r_drawentitypoly->integer) {
return;
}
tiki = backEnd.currentEntity->e.tiki;
scale = tiki->load_scale * backEnd.currentEntity->e.scale;
//
// get the mesh associated with the surface
//
bFound = qfalse;
for( mesh = 0; mesh < tiki->numMeshes; mesh++ )
{
skelmodel = ri.TIKI_GetSkel( tiki->mesh[ mesh ] );
psurface = skelmodel->pSurfaces;
// find the surface
for( surf = 0; surf < skelmodel->numSurfaces; surf++ )
{
if( psurface == sf ) {
bFound = qtrue;
break;
}
psurface = psurface->pNext;
}
if( bFound ) {
break;
}
}
assert( bFound );
//
// Process LOD
//
if( skelmodel->pLOD )
{
float lod_val;
int renderfx;
lod_val = backEnd.currentEntity->lodpercentage[0];
renderfx = backEnd.currentEntity->e.renderfx;
if (sf->numVerts > 3) {
skelIndex_t* collapseIndex;
int mid, low, high;
int lod_cutoff;
if (lod_tool->integer
&& !strcmp(backEnd.currentEntity->e.tiki->a->name, lod_tikiname->string)
&& mesh == lod_mesh->integer)
{
lod_cutoff = GetToolLodCutoff(skelmodel, backEnd.currentEntity->lodpercentage[0]);
}
else
{
lod_cutoff = GetLodCutoff(skelmodel, backEnd.currentEntity->lodpercentage[0], renderfx);
}
collapseIndex = sf->pCollapseIndex;
if (collapseIndex[2] < lod_cutoff) {
return;
}
low = mid = 3;
high = sf->numVerts;
while (high >= low) {
mid = (low + high) >> 1;
if (collapseIndex[mid] < lod_cutoff) {
high = mid - 1;
if (collapseIndex[mid - 1] >= lod_cutoff) {
break;
}
}
else
{
mid++;
low = mid;
if (high == mid || collapseIndex[mid] < lod_cutoff) {
break;
}
}
}
render_count = mid;
} else {
render_count = sf->numVerts;
}
if (!render_count) {
return;
}
} else {
render_count = sf->numVerts;
}
indexes = sf->numTriangles * 3;
RB_CHECKOVERFLOW(render_count, indexes);
collapse_map = sf->pCollapse;
triangles = sf->pTriangles;
baseIndex = tess.numIndexes;
baseVertex = tess.numVertexes;
tess.numVertexes += render_count;
outXyz = tess.xyz[baseVertex];
outNormal = &tess.normal[baseVertex];
newVerts = sf->pVerts;
if (render_count == sf->numVerts)
{
for (i = 0; i < indexes; i++) {
tess.indexes[baseIndex + i] = baseVertex + triangles[i];
}
entityNumIndexes[backEnd.currentEntity - backEnd.refdef.entities] += indexes;
tess.numIndexes += indexes;
}
else
{
assert(sf->numVerts < TIKI_MAX_VERTEXES);
for (i = 0; i < render_count; i++) {
collapse[i] = i;
}
for (i = render_count; i < sf->numVerts; i++) {
collapse[i] = collapse[collapse_map[i]];
}
for (i = 0; i < indexes; i += 3)
{
assert(collapse[triangles[i]] < sf->numVerts);
assert(collapse[triangles[i + 1]] < sf->numVerts);
assert(collapse[triangles[i + 2]] < sf->numVerts);
if (collapse[triangles[i]] == collapse[triangles[i + 1]] ||
collapse[triangles[i + 1]] == collapse[triangles[i + 2]] ||
collapse[triangles[i + 2]] == collapse[triangles[i]])
{
break;
}
tess.indexes[baseIndex + i] = baseVertex + collapse[triangles[i]];
tess.indexes[baseIndex + i + 1] = baseVertex + collapse[triangles[i + 1]];
tess.indexes[baseIndex + i + 2] = baseVertex + collapse[triangles[i + 2]];
}
entityNumIndexes[backEnd.currentEntity - backEnd.refdef.entities] += indexes;
tess.numIndexes += i;
}
//
// just copy the vertexes
//
bones = &TIKI_Skel_Bones[backEnd.currentEntity->e.bonestart];
morphs = &skeletorMorphCache[backEnd.currentEntity->e.morphstart];
if (backEnd.currentEntity->e.hasMorph)
{
if (mesh > 0)
{
for (vertNum = 0; vertNum < render_count; vertNum++)
{
vec3_t normal;
vec3_t out;
vec3_t totalmorph;
int channelNum;
int boneNum;
VectorClear(out);
VectorClear(outXyz);
VectorClear(totalmorph);
weight = (skelWeight_t*)((byte*)newVerts + sizeof(skeletorVertex_t) + sizeof(skeletorMorph_t) * newVerts->numMorphs);
morph = (skeletorMorph_t*)((byte*)newVerts + sizeof(skeletorVertex_t));
for (morphNum = 0; morphNum < newVerts->numMorphs; morphNum++) {
morphcache = &morphs[morph->morphIndex];
if (*morphcache) {
SkelMorphGetXyz(morph, morphcache, totalmorph);
}
morph++;
}
if (newVerts->numMorphs) {
channelNum = skelmodel->pBones[morph->morphIndex].channel;
} else {
channelNum = skelmodel->pBones[weight->boneIndex].channel;
}
boneNum = ri.TIKI_GetLocalChannel(tiki, channelNum);
bone = &bones[boneNum];
SkelVertGetNormal(newVerts, bone, normal);
for (weightNum = 0; weightNum < newVerts->numWeights; weightNum++) {
channelNum = skelmodel->pBones[weight->boneIndex].channel;
boneNum = ri.TIKI_GetLocalChannel(tiki, channelNum);
bone = &bones[boneNum];
if (!weightNum) {
SkelWeightMorphGetXyz(weight, bone, totalmorph, out);
} else {
SkelWeightGetXyz(weight, bone, out);
}
weight++;
}
VectorCopy(normal, *outNormal);
VectorScale(out, scale, outXyz);
tess.texCoords[baseVertex + vertNum][0][0] = newVerts->texCoords[0];
tess.texCoords[baseVertex + vertNum][0][1] = newVerts->texCoords[1];
// FIXME: fill in lightmapST for completeness?
newVerts = (skeletorVertex_t*)((byte*)newVerts + sizeof(skeletorVertex_t) + sizeof(skeletorMorph_t) * newVerts->numMorphs + sizeof(skelWeight_t) * newVerts->numWeights);
outXyz += 4;
outNormal++;
}
}
else
{
for (vertNum = 0; vertNum < render_count; vertNum++)
{
vec3_t normal;
vec3_t out;
vec3_t totalmorph;
VectorClear(out);
VectorClear(outXyz);
VectorClear(totalmorph);
weight = (skelWeight_t*)((byte*)newVerts + sizeof(skeletorVertex_t) + sizeof(skeletorMorph_t) * newVerts->numMorphs);
morph = (skeletorMorph_t*)((byte*)newVerts + sizeof(skeletorVertex_t));
for (morphNum = 0; morphNum < newVerts->numMorphs; morphNum++) {
morphcache = &morphs[morph->morphIndex];
if (*morphcache) {
SkelMorphGetXyz(morph, morphcache, totalmorph);
}
morph++;
}
if (newVerts->numMorphs) {
bone = &bones[morph->morphIndex];
} else {
bone = &bones[weight->boneIndex];
}
SkelVertGetNormal(newVerts, bone, normal);
for (weightNum = 0; weightNum < newVerts->numWeights; weightNum++) {
bone = &bones[weight->boneIndex];
if (!weightNum) {
SkelWeightMorphGetXyz(weight, bone, totalmorph, out);
} else {
SkelWeightGetXyz(weight, bone, out);
}
weight++;
}
VectorCopy(normal, *outNormal);
VectorScale(out, scale, outXyz);
tess.texCoords[baseVertex + vertNum][0][0] = newVerts->texCoords[0];
tess.texCoords[baseVertex + vertNum][0][1] = newVerts->texCoords[1];
// FIXME: fill in lightmapST for completeness?
newVerts = (skeletorVertex_t*)((byte*)newVerts + sizeof(skeletorVertex_t) + sizeof(skeletorMorph_t) * newVerts->numMorphs + sizeof(skelWeight_t) * newVerts->numWeights);
outXyz += 4;
outNormal++;
}
}
}
else
{
if (mesh > 0)
{
for (vertNum = 0; vertNum < render_count; vertNum++)
{
vec3_t normal;
vec3_t out;
int channelNum;
int boneNum;
VectorClear(out);
VectorClear(outXyz);
weight = (skelWeight_t*)((byte*)newVerts + sizeof(skeletorVertex_t) + sizeof(skeletorMorph_t) * newVerts->numMorphs);
channelNum = skelmodel->pBones[weight->boneIndex].channel;
boneNum = ri.TIKI_GetLocalChannel(tiki, channelNum);
bone = &bones[boneNum];
SkelVertGetNormal(newVerts, bone, normal);
for (weightNum = 0; weightNum < newVerts->numWeights; weightNum++) {
channelNum = skelmodel->pBones[weight->boneIndex].channel;
boneNum = ri.TIKI_GetLocalChannel(tiki, channelNum);
bone = &bones[boneNum];
SkelWeightGetXyz(weight, bone, out);
weight++;
}
VectorCopy(normal, *outNormal);
VectorScale(out, scale, outXyz);
tess.texCoords[baseVertex + vertNum][0][0] = newVerts->texCoords[0];
tess.texCoords[baseVertex + vertNum][0][1] = newVerts->texCoords[1];
// FIXME: fill in lightmapST for completeness?
newVerts = (skeletorVertex_t*)((byte*)newVerts + sizeof(skeletorVertex_t) + sizeof(skeletorMorph_t) * newVerts->numMorphs + sizeof(skelWeight_t) * newVerts->numWeights);
outXyz += 4;
outNormal++;
}
}
else
{
for (vertNum = 0; vertNum < render_count; vertNum++)
{
vec3_t normal;
vec3_t out;
VectorClear(out);
VectorClear(outXyz);
weight = (skelWeight_t*)((byte*)newVerts + sizeof(skeletorVertex_t) + sizeof(skeletorMorph_t) * newVerts->numMorphs);
bone = &bones[weight->boneIndex];
SkelVertGetNormal(newVerts, bone, normal);
for (weightNum = 0; weightNum < newVerts->numWeights; weightNum++) {
bone = &bones[weight->boneIndex];
SkelWeightGetXyz(weight, bone, out);
weight++;
}
VectorCopy(normal, *outNormal);
VectorScale(out, scale, outXyz);
tess.texCoords[baseVertex + vertNum][0][0] = newVerts->texCoords[0];
tess.texCoords[baseVertex + vertNum][0][1] = newVerts->texCoords[1];
// FIXME: fill in lightmapST for completeness?
newVerts = (skeletorVertex_t*)((byte*)newVerts + sizeof(skeletorVertex_t) + sizeof(skeletorMorph_t) * newVerts->numMorphs + sizeof(skelWeight_t) * newVerts->numWeights);
outXyz += 4;
outNormal++;
}
}
}
#if 0
if( backEnd.currentEntity->e.staticModelIndex ) {
mstaticModel_t *sm;
color4ub_t *out;
color3ub_t *in;
int cdofs;
skdSurface_t *sf2;
sm = &tr.world->staticModels[ backEnd.currentEntity->e.staticModelIndex - 1 ];
tess.useStaticModelVertexColors = qtrue;
cdofs = 0;
sf2 = tiki->surfs;
while( sf2 != sf ) {
cdofs += sf2->numVerts;
sf2 = ( skdSurface_t* )( ( ( byte* )sf2 ) + sf2->ofsEnd );
}
in = &tr.world->smColors[ sm->firstVert + cdofs ];
out = tess.vertexColors + baseVertex;
for( i = 0; i < sf->numVerts; i++, in++, out++ ) {
#if 1
( *out )[ 0 ] = ( *in )[ 0 ];
( *out )[ 1 ] = ( *in )[ 1 ];
( *out )[ 2 ] = ( *in )[ 2 ];
( *out )[ 3 ] = 255;
#elif 0
( ( int* )out ) = tr.identityLightByte;
#else
// su44: set it to something special so
// I can debug vertex colors rendering
( *out )[ 0 ] = 255;
( *out )[ 1 ] = 0;
( *out )[ 2 ] = 0;
( *out )[ 3 ] = 255;
#endif
}
} //else
#endif
//{
// // an attemp to fix bizarre vertex colors bug
// color4ub_t *col;
//
// col = &tess.vertexColors[baseVertex];
// for(i = 0; i < sf->numVerts; i++,col++) {
// (*col)[0] = 255;
// (*col)[1] = 255;
// (*col)[2] = 255;
// (*col)[3] = 255;
// }
//}
//tess.numVertexes += sf->numVerts;
}
/*
=============
RB_StaticMesh
=============
*/
void RB_StaticMesh(staticSurface_t *staticSurf)
{
int i, j;
dtiki_t *tiki;
skelSurfaceGame_t *surf;
int meshNum;
skelHeaderGame_t *skelmodel;
int render_count;
skelIndex_t *collapse_map;
skelIndex_t *triangles;
int indexes;
int baseIndex, baseVertex;
short collapse[1000];
if (!r_drawstaticmodelpoly->integer) {
return;
}
assert(backEnd.currentStaticModel);
tiki = backEnd.currentStaticModel->tiki;
surf = staticSurf->surface;
assert(surf->pStaticXyz);
if (!surf->pStaticXyz) {
return;
}
meshNum = staticSurf->meshNum;
skelmodel = ri.TIKI_GetSkel(tiki->mesh[meshNum]);
//
// Process LOD
//
if (skelmodel->pLOD && r_staticlod->integer) {
float lod_val;
lod_val = backEnd.currentStaticModel->lodpercentage[0];
if (surf->numVerts > 3) {
skelIndex_t *collapseIndex;
int mid, low, high;
int lod_cutoff;
if (lod_tool->integer && !strcmp(backEnd.currentStaticModel->tiki->a->name, lod_tikiname->string)
&& meshNum == lod_mesh->integer) {
lod_cutoff = GetToolLodCutoff(skelmodel, backEnd.currentStaticModel->lodpercentage[0]);
} else {
lod_cutoff = GetLodCutoff(skelmodel, backEnd.currentStaticModel->lodpercentage[0], 0);
}
collapseIndex = surf->pCollapseIndex;
if (collapseIndex[2] < lod_cutoff) {
return;
}
low = mid = 3;
high = surf->numVerts;
while (high >= low) {
mid = (low + high) >> 1;
if (collapseIndex[mid] < lod_cutoff) {
high = mid - 1;
if (collapseIndex[mid - 1] >= lod_cutoff) {
break;
}
} else {
mid++;
low = mid;
if (high == mid || collapseIndex[mid] < lod_cutoff) {
break;
}
}
}
render_count = mid;
} else {
render_count = surf->numVerts;
}
if (!render_count) {
return;
}
} else {
render_count = surf->numVerts;
}
indexes = surf->numTriangles * 3;
RB_CHECKOVERFLOW(render_count, surf->numTriangles);
collapse_map = surf->pCollapse;
triangles = surf->pTriangles;
baseIndex = tess.numIndexes;
baseVertex = tess.numVertexes;
tess.numVertexes += render_count;
if (render_count == surf->numVerts) {
for (j = 0; j < indexes; j++) {
tess.indexes[baseIndex + j] = baseVertex + triangles[j];
}
staticModelNumIndexes[backEnd.currentStaticModel - backEnd.refdef.staticModels] += indexes;
tess.numIndexes += indexes;
} else {
for (i = 0; i < render_count; i++) {
collapse[i] = i;
}
for (i = render_count; i < surf->numVerts; i++) {
collapse[i] = collapse[collapse_map[i]];
}
for (j = 0; j < indexes; j += 3) {
if (collapse[triangles[j]] == collapse[triangles[j + 1]]
|| collapse[triangles[j + 1]] == collapse[triangles[j + 2]]
|| collapse[triangles[j + 2]] == collapse[triangles[j]]) {
break;
}
tess.indexes[baseIndex + j] = baseVertex + collapse[triangles[j]];
tess.indexes[baseIndex + j + 1] = baseVertex + collapse[triangles[j + 1]];
tess.indexes[baseIndex + j + 2] = baseVertex + collapse[triangles[j + 2]];
}
staticModelNumIndexes[backEnd.currentStaticModel - backEnd.refdef.staticModels] += j;
tess.numIndexes += j;
}
for (j = 0; j < render_count; j++) {
Vector4Copy(surf->pStaticXyz[j], tess.xyz[baseVertex + j]);
Vector4Copy(surf->pStaticNormal[j], tess.normal[baseVertex + j]);
tess.texCoords[baseVertex + j][0][0] = surf->pStaticTexCoords[j][0][0];
tess.texCoords[baseVertex + j][0][1] = surf->pStaticTexCoords[j][0][1];
tess.texCoords[baseVertex + j][1][0] = surf->pStaticTexCoords[j][1][0];
tess.texCoords[baseVertex + j][1][1] = surf->pStaticTexCoords[j][1][1];
}
if (backEndData->staticModelData) {
const size_t offset =
backEnd.currentStaticModel->firstVertexData + staticSurf->ofsStaticData * sizeof(color4ub_t);
assert(offset < tr.world->numStaticModelData * sizeof(color4ub_t));
assert(offset + render_count * sizeof(color4ub_t) <= tr.world->numStaticModelData * sizeof(color4ub_t));
const color4ub_t *in = (const color4ub_t *)&backEndData->staticModelData[offset];
for (i = 0; i < render_count; i++) {
tess.vertexColors[baseVertex + i][0] = in[i][0];
tess.vertexColors[baseVertex + i][1] = in[i][1];
tess.vertexColors[baseVertex + i][2] = in[i][2];
tess.vertexColors[baseVertex + i][3] = in[i][3];
}
} else {
for (i = 0; i < render_count; i++) {
tess.vertexColors[baseVertex + i][0] = 0xFF;
tess.vertexColors[baseVertex + i][1] = 0xFF;
tess.vertexColors[baseVertex + i][2] = 0xFF;
tess.vertexColors[baseVertex + i][3] = 0xFF;
}
}
tess.vertexColorValid = qtrue;
}
/*
=============
R_InfoWorldTris_f
=============
*/
void R_InfoWorldTris_f( void ) {
int i;
g_bInfoworldtris = qtrue;
for (i = 0; i < ARRAY_LEN(entityNumIndexes); i++) {
entityNumIndexes[i] = 0;
}
for (i = 0; i < ARRAY_LEN(staticModelNumIndexes); i++) {
staticModelNumIndexes[i] = 0;
}
}
/*
=============
R_PrintInfoWorldtris
=============
*/
void R_PrintInfoWorldtris( void ) {
int i;
int numTris;
int totalNumTris;
dtiki_t* tiki;
skelHeaderGame_t* skelmodel;
totalNumTris = 0;
for (i = 0; i < ARRAY_LEN(entityNumIndexes); i++) {
numTris = entityNumIndexes[i] / 3;
if (!numTris) {
continue;
}
totalNumTris += numTris;
tiki = backEnd.refdef.entities[i].e.tiki;
skelmodel = ri.TIKI_GetSkel(tiki->mesh[0]);
Com_Printf("ent: %i, tris: %i, %s, version: %i\n", i, numTris, tiki->a->name, skelmodel->version);
}
Com_Printf("total entity tris: %i\n\n", totalNumTris);
totalNumTris = 0;
for (i = 0; i < ARRAY_LEN(entityNumIndexes); i++) {
numTris = staticModelNumIndexes[i] / 3;
if (!numTris) {
continue;
}
totalNumTris += numTris;
tiki = backEnd.refdef.staticModels[i].tiki;
skelmodel = ri.TIKI_GetSkel(tiki->mesh[0]);
Com_Printf("sm: %i, tris: %i, %s, version: %i\n", i, numTris, tiki->a->name, skelmodel->version);
}
Com_Printf("total static model tris: %i\n\n", totalNumTris);
}
/*
=============
RE_SetFrameNumber
=============
*/
void RE_SetFrameNumber( int frameNumber ) {
tr.frame_skel_index = frameNumber;
}
/*
=============
R_UpdatePoseInternal
=============
*/
void R_UpdatePoseInternal( refEntity_t *model ) {
if( model->entityNumber != ENTITYNUM_NONE ) {
if( tr.skel_index[ model->entityNumber ] == tr.frame_skel_index ) {
return;
}
tr.skel_index[ model->entityNumber ] = tr.frame_skel_index;
}
ri.TIKI_SetPoseInternal( ri.TIKI_GetSkeletor( model->tiki, model->entityNumber ), model->frameInfo, model->bone_tag, model->bone_quat, model->actionWeight );
}
/*
=============
RE_ForceUpdatePose
=============
*/
void RE_ForceUpdatePose( refEntity_t *model ) {
if( model->entityNumber != ENTITYNUM_NONE ) {
tr.skel_index[ model->entityNumber ] = tr.frame_skel_index;
}
ri.TIKI_SetPoseInternal( ri.TIKI_GetSkeletor( model->tiki, model->entityNumber ), model->frameInfo, model->bone_tag, model->bone_quat, model->actionWeight );
}
/*
=============
RE_TIKI_Orientation
=============
*/
orientation_t RE_TIKI_Orientation( refEntity_t *model, int tagnum ) {
R_UpdatePoseInternal( model );
return ri.TIKI_OrientationInternal( model->tiki, model->entityNumber, tagnum, model->scale );
}
/*
=============
RE_TIKI_IsOnGround
=============
*/
qboolean RE_TIKI_IsOnGround( refEntity_t *model, int tagnum, float threshold ) {
R_UpdatePoseInternal( model );
return ri.TIKI_IsOnGroundInternal( model->tiki, model->entityNumber, tagnum, threshold );
}
/*
=============
R_GetRadius
=============
*/
float R_GetRadius( refEntity_t *model ) {
R_UpdatePoseInternal( model );
return ri.GetRadiusInternal( model->tiki, model->entityNumber, model->scale );
}
/*
=============
R_GetFrame
=============
*/
void R_GetFrame( refEntity_t *model, struct skelAnimFrame_s *newFrame ) {
R_UpdatePoseInternal( model );
ri.GetFrameInternal( model->tiki, model->entityNumber, newFrame );
}
/*
=============
R_DebugSkeleton
=============
*/
void R_DebugSkeleton(void) {
int i;
trRefEntity_t *ent;
model_t *model;
if (!r_showSkeleton->integer) {
return;
}
R_IssuePendingRenderCommands();
GL_Bind( tr.whiteImage );
GL_State( GLS_POLYMODE_LINE );
qglDisableClientState( GL_COLOR_ARRAY );
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
for( i = 0; i < tr.refdef.num_entities; i++ ) {
ent = &tr.refdef.entities[ i ];
if( ent->e.reType == RT_MODEL ) {
model = R_GetModelByHandle( ent->e.hModel );
if( model->type == MOD_TIKI ) {
RE_ForceUpdatePose( &ent->e );
RB_DrawSkeletor( ent );
}
}
}
}
/*
=============
ProjectRadius
=============
*/
static float ProjectRadius(float r, const vec3_t location)
{
Vector separation;
float projectedRadius;
separation = Vector(tr.viewParms.ori.origin) - Vector(location);
projectedRadius = separation.length();
return fabs(r) * (100.0 / tr.viewParms.fovX) / projectedRadius;
}
/*
=============
R_CalcLod
=============
*/
float R_CalcLod(const vec3_t origin, float radius)
{
return ProjectRadius(radius, origin);
}
/*
=============
R_CullTIKI
=============
*/
static int R_CullSkelModel(dtiki_t* tiki, refEntity_t* e, skelAnimFrame_t* newFrame, float fScale, float* vLocalOrg) {
vec3_t bounds[2];
vec3_t delta;
int i;
int cull;
// FIXME: not working properly
return CULL_IN;
if (tr.currentEntity->e.renderfx & RF_FRAMELERP) {
VectorSubtract(e->origin, e->oldorigin, delta);
} else {
VectorClear(delta);
}
for (i = 0; i < 3; i++)
{
bounds[0][i] = newFrame->bounds[0][i] * fScale + vLocalOrg[i];
bounds[1][i] = newFrame->bounds[1][i] * fScale + vLocalOrg[i];
if (delta[i] > 0) {
bounds[1][i] += delta[i];
} else {
bounds[0][i] += delta[i];
}
}
cull = R_CullLocalBox(bounds);
if (r_showcull->integer & 1)
{
float fR, fG, fB;
vec3_t vAngles;
switch (cull)
{
case CULL_IN:
fR = 0;
fG = 1;
fB = 0;
break;
case CULL_CLIP:
fR = 1;
fG = 1;
fB = 0;
break;
case CULL_OUT:
fR = 1;
fG = 0.2f;
fB = 0.2f;
for (i = 0; i < 3; i++) {
bounds[0][i] -= 16;
bounds[1][i] += 16;
}
break;
}
MatrixToEulerAngles(tr.ori.axis, vAngles);
R_DebugRotatedBBox(tr.ori.origin, vAngles, bounds[0], bounds[1], fR, fG, fB, 0.5);
}
switch (cull)
{
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_CountTikiLodTris
Computes the number of triangles to be rendered for the given TIKI model
based on the given LoD percentage, and passes it back with render_tris.
The total number of triangles in the TIKI model are passed back with total_tris.
Currently only used for debugging purposes.
FIXME: Shares some code with RB_StaticMesh, common parts could be extracted.
According to debug symbols, this was originally in tiki_mesh.cpp
==================
*/
void R_CountTikiLodTris(dtiki_t* tiki, float lodpercentage, int* render_tris, int* total_tris)
{
*render_tris = 0;
*total_tris = 0;
int numtris = 0, totaltris = 0;
for (int i = 0; i < tiki->numMeshes; i++)
{
skelHeaderGame_t *skelmodel = ri.TIKI_GetSkel(tiki->mesh[i]);
skelSurfaceGame_t *surface = skelmodel->pSurfaces;
for (int j = 0; j < skelmodel->numSurfaces; j++)
{
int render_count = 0;
if (skelmodel->pLOD)
{
int lod_cutoff = GetLodCutoff(skelmodel, lodpercentage, 0);
skelIndex_t *collapseIndex = surface->pCollapseIndex;
render_count = surface->numVerts;
// Determine the number of vertices to be rendered based on the LOD cutoff
while (render_count > 0 && collapseIndex[render_count - 1] < lod_cutoff)
{
render_count--;
}
}
else
{
// The surf mesh doesn't have a LOD model, so all vertices will be rendered
render_count = surface->numVerts;
}
skelIndex_t *triangles = surface->pTriangles;
int indexes = surface->numTriangles * 3; // 3 vertex/index for each tri
skelIndex_t *collapse_map = surface->pCollapse;
totaltris += surface->numTriangles;
skelIndex_t collapse[4096]{};
// Initialize array for collapsed indices
int k;
for (k = 0; k < render_count; ++k)
{
collapse[k] = k;
}
// Map remaining vertices to their collapsed indices using the collapse map
for (k = render_count; k < surface->numVerts; ++k)
{
collapse[k] = collapse[collapse_map[k]];
}
// Check the first two collapsed indices of each triangle
for (k = 0; k < indexes; k += 3)
{
if (collapse[triangles[k]] == collapse[triangles[k + 1]])
{
// The collapsed indices of the two vertices are the same:
// this, and any subsequent tris do not need to be rendered
// because the collapsed vertices (for this surface) coincide from here.
break;
}
}
surface = surface->pNext;
numtris += k / 3;
}
}
*render_tris = numtris;
*total_tris = totaltris;
}
/*
==================
R_LerpTag
==================
*/
int R_LerpTag(orientation_t* tag, qhandle_t handle, int startFrame, int endFrame,
float frac, const char* tagName) {
// stub
return 0;
}