2023-05-08 14:33:37 +02:00
|
|
|
/*
|
|
|
|
===========================================================================
|
2023-05-09 19:18:16 +02:00
|
|
|
Copyright (C) 2023 the OpenMoHAA team
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2023-05-09 19:18:16 +02:00
|
|
|
This file is part of OpenMoHAA source code.
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2023-05-09 19:18:16 +02:00
|
|
|
OpenMoHAA source code is free software; you can redistribute it
|
2023-05-08 14:33:37 +02:00
|
|
|
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.
|
|
|
|
|
2023-05-09 19:18:16 +02:00
|
|
|
OpenMoHAA source code is distributed in the hope that it will be
|
2023-05-08 14:33:37 +02:00
|
|
|
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
|
2023-05-09 19:18:16 +02:00
|
|
|
along with OpenMoHAA source code; if not, write to the Free Software
|
2023-05-08 14:33:37 +02:00
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
2023-05-09 19:18:16 +02:00
|
|
|
|
|
|
|
// tr_models.cpp -- model loading and caching
|
2023-05-08 14:33:37 +02:00
|
|
|
|
|
|
|
#include "tr_local.h"
|
|
|
|
#include "tiki.h"
|
|
|
|
#include <vector.h>
|
|
|
|
|
|
|
|
#define LL(x) x=LittleLong(x)
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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 ] ) {
|
2023-05-09 19:13:37 +02:00
|
|
|
sh = R_FindShader( psurface->shader[ j ], LIGHTMAP_NONE, !( psurface->flags & TIKI_SURF_NOMIPMAPS), !(psurface->flags & TIKI_SURF_NOPICMIP), qtrue, qtrue );
|
2023-05-08 14:33:37 +02:00
|
|
|
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( !strcmp( 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_SyncRenderThread();
|
|
|
|
|
|
|
|
mod->serveronly = qtrue;
|
|
|
|
|
|
|
|
//
|
|
|
|
// load the files
|
|
|
|
//
|
|
|
|
ptr = strrchr( name, '.' );
|
|
|
|
|
|
|
|
if( ptr )
|
|
|
|
{
|
|
|
|
ptr++;
|
|
|
|
|
|
|
|
if( !stricmp( ptr, "spr" ) )
|
|
|
|
{
|
|
|
|
mod->d.sprite = SPR_RegisterSprite( name );
|
|
|
|
strcpy( mod->name, name );
|
|
|
|
|
|
|
|
if( mod->d.sprite )
|
|
|
|
{
|
|
|
|
mod->type = MOD_SPRITE;
|
|
|
|
return mod->index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( !stricmp( ptr, "tik" ) )
|
|
|
|
{
|
|
|
|
mod->d.tiki = TIKI_RegisterTikiFlags( name, use );
|
|
|
|
strcpy( mod->name, 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;
|
|
|
|
|
|
|
|
// leave a space for NULL model
|
|
|
|
tr.numModels = 0;
|
|
|
|
|
|
|
|
mod = R_AllocModel();
|
|
|
|
strcpy( mod->name, "** BAD MODEL **" );
|
|
|
|
mod->type = MOD_BAD;
|
|
|
|
|
|
|
|
memset( tr.skel_index, 255, sizeof( tr.skel_index ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
================
|
|
|
|
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 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:
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
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 )
|
|
|
|
{
|
|
|
|
Cvar_Set( "lod_save", "0" );
|
|
|
|
strcpy( lodPath, GetModelPath( skelmodel ) );
|
|
|
|
ext = strstr( lodPath, "skd" );
|
|
|
|
strcpy( ext, "lod" );
|
|
|
|
SaveLODFile( lodPath, LOD );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( lod_mesh->modified )
|
|
|
|
{
|
|
|
|
lod_mesh->modified = qfalse;
|
|
|
|
Cvar_Set( "lod_minLOD", va( "%f", LOD->minMetric ) );
|
|
|
|
Cvar_Set( "lod_maxLOD", va( "%f", LOD->maxMetric ) );
|
|
|
|
Cvar_Set( "lod_LOD_slider", va( "%f", 0.5 ) );
|
|
|
|
Cvar_Set( "lod_curve_0_slider", va( "%f", LOD->curve[ 0 ].val / totalRange ) );
|
|
|
|
Cvar_Set( "lod_curve_1_slider", va( "%f", LOD->curve[ 1 ].val / totalRange ) );
|
|
|
|
Cvar_Set( "lod_curve_2_slider", va( "%f", LOD->curve[ 2 ].val / totalRange ) );
|
|
|
|
Cvar_Set( "lod_curve_3_slider", va( "%f", LOD->curve[ 3 ].val / totalRange ) );
|
|
|
|
Cvar_Set( "lod_curve_4_slider", va( "%f", LOD->curve[ 4 ].val / totalRange ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Cvar_Set( "lod_curve_0_val", va( "%f", lod_curve_0_slider->value * totalRange ) );
|
|
|
|
Cvar_Set( "lod_curve_1_val", va( "%f", lod_curve_1_slider->value * totalRange ) );
|
|
|
|
Cvar_Set( "lod_curve_2_val", va( "%f", lod_curve_2_slider->value * totalRange ) );
|
|
|
|
Cvar_Set( "lod_curve_3_val", va( "%f", lod_curve_3_slider->value * totalRange ) );
|
|
|
|
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;
|
|
|
|
|
|
|
|
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 * )TIKI_GetSkeletor( tiki, ENTITYNUM_NONE );
|
|
|
|
|
|
|
|
if( r_showSkeleton->integer == 1 ) {
|
|
|
|
//vec3_t vForward, vRight, vUp;
|
|
|
|
orientation_t or;
|
|
|
|
orientation_t parent_or;
|
|
|
|
int iParentBone;
|
|
|
|
|
|
|
|
glLineWidth( 3 );
|
|
|
|
glBegin( GL_LINES );
|
|
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < tiki->m_boneList.NumChannels(); i++ ) { // draw a skeleton
|
|
|
|
|
|
|
|
or = R_GetTagPositionAndOrientation( &ent->e, i );
|
|
|
|
iParentBone = skeletor->GetBoneParent( 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 );
|
|
|
|
}
|
|
|
|
|
|
|
|
glColor3f( 1, 1, 1 );
|
|
|
|
glVertex3fv( parent_or.origin );
|
|
|
|
glVertex3fv( or.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( or.origin );
|
|
|
|
|
|
|
|
glColor3f( 0, 1, 0 );
|
|
|
|
glVertex3fv( parent_or.origin );
|
|
|
|
VectorAdd( parent_or.origin, parent_or.axis[ 1 ], vRight );
|
|
|
|
glVertex3fv( vRight );
|
|
|
|
glVertex3fv( vRight );
|
|
|
|
glVertex3fv( or.origin );
|
|
|
|
|
|
|
|
glColor3f( 0, 0, 1 );
|
|
|
|
glVertex3fv( parent_or.origin );
|
|
|
|
VectorAdd( parent_or.origin, parent_or.axis[ 2 ], vUp );
|
|
|
|
glVertex3fv( vUp );
|
|
|
|
glVertex3fv( vUp );
|
|
|
|
glVertex3fv( or.origin );*/
|
|
|
|
|
|
|
|
}
|
|
|
|
glEnd();
|
|
|
|
glLineWidth( 1 );
|
|
|
|
}
|
|
|
|
else if( r_showSkeleton->integer == 2 ) { // draw skeleton with bones
|
|
|
|
orientation_t or;
|
|
|
|
orientation_t parent_or;
|
|
|
|
int iParentBone;
|
|
|
|
|
|
|
|
glLineWidth( 3 );
|
|
|
|
glBegin( GL_LINES );
|
|
|
|
for( i = 0; i < tiki->m_boneList.NumChannels(); i++ ) {
|
|
|
|
iParentBone = skeletor->GetBoneParent( i );
|
|
|
|
|
|
|
|
if( iParentBone > 0 ) {
|
|
|
|
or = R_GetTagPositionAndOrientation( &ent->e, i );
|
|
|
|
parent_or = R_GetTagPositionAndOrientation( &ent->e, iParentBone );
|
|
|
|
glColor3f( 1, 1, 1 );
|
|
|
|
glVertex3fv( parent_or.origin );
|
|
|
|
glVertex3fv( or.origin );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
glBegin( GL_LINES );
|
|
|
|
for( i = 0; i < tiki->m_boneList.NumChannels(); i++ ) {
|
|
|
|
vec3_t up, down, front;
|
|
|
|
or = R_GetTagPositionAndOrientation( &ent->e, i );
|
|
|
|
|
|
|
|
VectorCopy( or.origin, up );
|
|
|
|
up[ 1 ] += 5;
|
|
|
|
VectorCopy( or.origin, down );
|
|
|
|
down[ 1 ] += 5;
|
|
|
|
VectorCopy( or.origin, front );
|
|
|
|
front[ 0 ] += 5;
|
|
|
|
|
|
|
|
glColor3f( 1, 0, 1 );
|
|
|
|
glVertex3fv( front );
|
|
|
|
glVertex3fv( or.origin );
|
|
|
|
glVertex3fv( down );
|
|
|
|
glVertex3fv( or.origin );
|
|
|
|
glVertex3fv( up );
|
|
|
|
glVertex3fv( or.origin );
|
|
|
|
}
|
|
|
|
|
|
|
|
glEnd();
|
|
|
|
glLineWidth( 1 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
R_CullTIKI
|
|
|
|
=============
|
|
|
|
*/
|
|
|
|
static int R_CullTIKI( dtiki_t *tiki, trRefEntity_t *ent ) {
|
|
|
|
#if 0
|
|
|
|
//// cull bounding sphere ONLY if this is not an upscaled entity
|
|
|
|
if( !ent->e.nonNormalizedAxes )
|
|
|
|
{
|
|
|
|
switch( R_CullPointAndRadius( ent->e.origin, ent->e.radius ) )
|
|
|
|
{
|
|
|
|
case CULL_OUT:
|
|
|
|
tr.pc.c_sphere_cull_tiki_out++;
|
|
|
|
return CULL_OUT;
|
|
|
|
|
|
|
|
case CULL_IN:
|
|
|
|
tr.pc.c_sphere_cull_tiki_in++;
|
|
|
|
return CULL_IN;
|
|
|
|
|
|
|
|
case CULL_CLIP:
|
|
|
|
tr.pc.c_sphere_cull_tiki_clip++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch( R_CullLocalBox( ent->e.bounds ) )
|
|
|
|
{
|
|
|
|
case CULL_IN:
|
|
|
|
tr.pc.c_box_cull_tiki_in++;
|
|
|
|
return CULL_IN;
|
|
|
|
case CULL_CLIP:
|
|
|
|
tr.pc.c_box_cull_tiki_clip++;
|
|
|
|
return CULL_CLIP;
|
|
|
|
case CULL_OUT:
|
|
|
|
default:
|
|
|
|
tr.pc.c_box_cull_tiki_out++;
|
|
|
|
return CULL_OUT;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// FIXME: unimplemented
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 = tiki->m_boneList.NumChannels();
|
|
|
|
|
|
|
|
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 );
|
|
|
|
// FIXME: Draw a debug circle
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: calculate lod percentage
|
|
|
|
if( !lod_tool->integer && iRadiusCull == -1 ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//if( R_CullTIKI( tiki, ent ) == CULL_OUT ) {
|
|
|
|
// return;
|
|
|
|
//}
|
|
|
|
|
|
|
|
newFrame = ( skelAnimFrame_t * )ri.Hunk_AllocateTempMemory( sizeof( skelAnimFrame_t ) + tiki->m_boneList.NumChannels() * sizeof( SkelMat4 ) );
|
|
|
|
R_GetFrame( &ent->e, newFrame );
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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 * )TIKI_GetSkeletor( tiki, ent->e.entityNumber );
|
|
|
|
|
|
|
|
//
|
|
|
|
// add morphs
|
|
|
|
//
|
|
|
|
added = skeletor->GetMorphWeightFrame( 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// set up lighting now that we know we aren't culled
|
|
|
|
//
|
|
|
|
if( !personalModel || r_shadows->integer > 1 ) {
|
|
|
|
R_SetupEntityLighting( &tr.refdef, ent );
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// draw all meshes
|
|
|
|
//
|
|
|
|
dsurf = tiki->surfaces;
|
|
|
|
bsurf = &ent->e.surfaces[ 0 ];
|
|
|
|
for( mesh = 0; mesh < tiki->numMeshes; mesh++ )
|
|
|
|
{
|
|
|
|
skelHeaderGame_t *skelmodel = 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, 0 );
|
|
|
|
R_AddDrawSurf( ( surfaceType_t * )surface, tr.shaders[ dsurf->hShader[ iShaderNum + 1 ] ], 0, 0);
|
|
|
|
} else {
|
|
|
|
R_AddDrawSurf( ( surfaceType_t * )surface, shader, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ( ent->e.customShader ) && ( ent->e.renderfx & RF_CUSTOMSHADERPASS ) ) {
|
|
|
|
shader = R_GetShaderByHandle( ent->e.customShader );
|
|
|
|
R_AddDrawSurf( ( surfaceType_t * )surface, shader, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: setup LOD
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
SkelVertGetNormal
|
|
|
|
=============
|
|
|
|
*/
|
|
|
|
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
|
|
|
|
=============
|
|
|
|
*/
|
|
|
|
static void SkelMorphGetXyz( skeletorMorph_t *morph, int *morphcache, vec3_t out ) {
|
|
|
|
out[ 0 ] += morph->offset[ 0 ] * *morphcache +
|
|
|
|
morph->offset[ 1 ] * *morphcache +
|
|
|
|
morph->offset[ 2 ] * *morphcache;
|
|
|
|
|
|
|
|
out[ 1 ] += morph->offset[ 0 ] * *morphcache +
|
|
|
|
morph->offset[ 1 ] * *morphcache +
|
|
|
|
morph->offset[ 2 ] * *morphcache;
|
|
|
|
|
|
|
|
out[ 2 ] += morph->offset[ 0 ] * *morphcache +
|
|
|
|
morph->offset[ 1 ] * *morphcache +
|
|
|
|
morph->offset[ 2 ] * *morphcache;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
SkelWeightGetXyz
|
|
|
|
=============
|
|
|
|
*/
|
|
|
|
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
|
|
|
|
=============
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
LerpSkelMesh
|
|
|
|
=============
|
|
|
|
*/
|
|
|
|
static void LerpSkelMesh( skelSurfaceGame_t *sf ) {
|
|
|
|
float *outXyz;
|
|
|
|
vec4_t *outNormal;
|
|
|
|
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;
|
|
|
|
skelHeaderGame_t *skelmodel;
|
|
|
|
skelSurfaceGame_t *psurface;
|
|
|
|
qboolean bFound;
|
|
|
|
|
|
|
|
tiki = backEnd.currentEntity->e.tiki;
|
|
|
|
newVerts = sf->pVerts;
|
|
|
|
|
|
|
|
outXyz = tess.xyz[ tess.numVertexes ];
|
|
|
|
outNormal = &tess.normal[ tess.numVertexes ];
|
|
|
|
|
|
|
|
bones = &TIKI_Skel_Bones[ backEnd.currentEntity->e.bonestart ];
|
|
|
|
morphs = &skeletorMorphCache[ backEnd.currentEntity->e.morphstart ];
|
|
|
|
|
|
|
|
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 = 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 )
|
|
|
|
{
|
|
|
|
// FIXME
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// just copy the vertexes
|
|
|
|
//
|
|
|
|
for( vertNum = 0; vertNum < sf->numVerts; vertNum++ )
|
|
|
|
{
|
|
|
|
vec3_t normal;
|
|
|
|
vec3_t out;
|
|
|
|
|
|
|
|
VectorClear( out );
|
|
|
|
VectorClear( outXyz );
|
|
|
|
|
|
|
|
weight = ( skelWeight_t * )( ( byte * )newVerts + sizeof( skeletorVertex_t ) + sizeof( skeletorMorph_t ) * newVerts->numMorphs );
|
|
|
|
|
|
|
|
if( backEnd.currentEntity->e.hasMorph )
|
|
|
|
{
|
|
|
|
vec3_t totalmorph;
|
|
|
|
|
|
|
|
VectorClear( totalmorph );
|
|
|
|
morph = ( skeletorMorph_t * )( ( byte * )newVerts + sizeof( skeletorVertex_t ) );
|
|
|
|
|
|
|
|
if( mesh > 0 )
|
|
|
|
{
|
|
|
|
int channelNum;
|
|
|
|
int boneNum;
|
|
|
|
|
|
|
|
for( morphNum = 0; morphNum < newVerts->numMorphs; morphNum++ ) {
|
|
|
|
morphcache = &morphs[ morph->morphIndex ];
|
|
|
|
|
|
|
|
if( *morphcache ) {
|
|
|
|
SkelMorphGetXyz( morph, morphcache, totalmorph );
|
|
|
|
}
|
|
|
|
|
|
|
|
morph++;
|
|
|
|
}
|
|
|
|
|
|
|
|
channelNum = skelmodel->pBones[ morph->morphIndex ].channel;
|
|
|
|
boneNum = tiki->m_boneList.GetLocalFromGlobal( channelNum );
|
|
|
|
bone = &bones[ boneNum ];
|
|
|
|
|
|
|
|
SkelVertGetNormal( newVerts, bone, normal );
|
|
|
|
|
|
|
|
for( weightNum = 0; weightNum < newVerts->numWeights; weightNum++ ) {
|
|
|
|
channelNum = skelmodel->pBones[ weight->boneIndex ].channel;
|
|
|
|
boneNum = tiki->m_boneList.GetLocalFromGlobal( channelNum );
|
|
|
|
bone = &bones[ boneNum ];
|
|
|
|
|
|
|
|
if( !weightNum ) {
|
|
|
|
SkelWeightMorphGetXyz( weight, bone, totalmorph, out );
|
|
|
|
} else {
|
|
|
|
SkelWeightGetXyz( weight, bone, out );
|
|
|
|
}
|
|
|
|
|
|
|
|
weight++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for( morphNum = 0; morphNum < newVerts->numMorphs; morphNum++ ) {
|
|
|
|
morphcache = &morphs[ morph->morphIndex ];
|
|
|
|
|
|
|
|
if( *morphcache ) {
|
|
|
|
SkelMorphGetXyz( morph, morphcache, totalmorph );
|
|
|
|
}
|
|
|
|
|
|
|
|
morph++;
|
|
|
|
}
|
|
|
|
|
|
|
|
bone = &bones[ morph->morphIndex ];
|
|
|
|
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++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( mesh > 0 )
|
|
|
|
{
|
|
|
|
int channelNum;
|
|
|
|
int boneNum;
|
|
|
|
|
|
|
|
channelNum = skelmodel->pBones[ weight->boneIndex ].channel;
|
|
|
|
boneNum = tiki->m_boneList.GetLocalFromGlobal( channelNum );
|
|
|
|
bone = &bones[ boneNum ];
|
|
|
|
|
|
|
|
SkelVertGetNormal( newVerts, bone, normal );
|
|
|
|
|
|
|
|
for( weightNum = 0; weightNum < newVerts->numWeights; weightNum++ ) {
|
|
|
|
channelNum = skelmodel->pBones[ weight->boneIndex ].channel;
|
|
|
|
boneNum = tiki->m_boneList.GetLocalFromGlobal( channelNum );
|
|
|
|
bone = &bones[ boneNum ];
|
|
|
|
|
|
|
|
SkelWeightGetXyz( weight, bone, out );
|
|
|
|
|
|
|
|
weight++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for( weightNum = 0; weightNum < newVerts->numWeights; weightNum++ ) {
|
|
|
|
bone = &bones[ weight->boneIndex ];
|
|
|
|
|
|
|
|
SkelWeightGetXyz( weight, bone, out );
|
|
|
|
|
|
|
|
weight++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorCopy(normal, *outNormal);
|
|
|
|
|
|
|
|
VectorScale( out, scale, outXyz );
|
|
|
|
|
|
|
|
newVerts = ( skeletorVertex_t * )( ( byte * )newVerts + sizeof( skeletorVertex_t ) + sizeof( skeletorMorph_t ) * newVerts->numMorphs + sizeof( skelWeight_t ) * newVerts->numWeights );
|
|
|
|
outXyz += 4;
|
|
|
|
outNormal++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
RB_SkelMesh
|
|
|
|
=============
|
|
|
|
*/
|
|
|
|
void RB_SkelMesh( skelSurfaceGame_t *sf ) {
|
|
|
|
int j;
|
|
|
|
skeletorVertex_t *vert;
|
|
|
|
int baseIndex, baseVertex;
|
|
|
|
int numVerts;
|
|
|
|
int numIndexes;
|
|
|
|
|
|
|
|
numIndexes = sf->numTriangles * 3;
|
|
|
|
|
|
|
|
RB_CHECKOVERFLOW( sf->numVerts, numIndexes );
|
|
|
|
|
|
|
|
LerpSkelMesh( sf );
|
|
|
|
|
|
|
|
baseIndex = tess.numIndexes;
|
|
|
|
baseVertex = tess.numVertexes;
|
|
|
|
for( j = 0; j < numIndexes; j++ ) {
|
|
|
|
tess.indexes[ baseIndex + j ] = baseVertex + sf->pTriangles[ j ];
|
|
|
|
}
|
|
|
|
tess.numIndexes += numIndexes;
|
|
|
|
|
|
|
|
numVerts = sf->numVerts;
|
|
|
|
vert = sf->pVerts;
|
|
|
|
for( j = 0; j < numVerts; j++ ) {
|
|
|
|
tess.texCoords[ baseVertex + j ][ 0 ][ 0 ] = vert->texCoords[ 0 ];
|
|
|
|
tess.texCoords[ baseVertex + j ][ 0 ][ 1 ] = vert->texCoords[ 1 ];
|
|
|
|
vert = ( skeletorVertex_t * )( ( byte * )vert + sizeof( skeletorVertex_t ) + sizeof( skeletorMorph_t ) * vert->numMorphs + sizeof( skelWeight_t ) * vert->numWeights );
|
|
|
|
// FIXME: fill in lightmapST for completeness?
|
|
|
|
}
|
|
|
|
|
|
|
|
tess.numVertexes += numVerts;
|
|
|
|
|
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
R_PrintInfoWorldtris
|
|
|
|
=============
|
|
|
|
*/
|
|
|
|
void R_PrintInfoWorldtris( void ) {
|
|
|
|
// FIXME: stub
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
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( 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( 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;
|
|
|
|
|
|
|
|
R_SyncRenderThread();
|
|
|
|
|
|
|
|
GL_Bind( tr.whiteImage );
|
|
|
|
GL_State( 0x200 );
|
|
|
|
|
|
|
|
glDisableClientState( GL_COLOR_ARRAY );
|
|
|
|
glDisableClientState( 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 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|