mirror of
https://github.com/YGGverse/xash3d-fwgs.git
synced 2025-01-15 01:20:32 +00:00
5e0a0765ce
The `.editorconfig` file in this repo is configured to trim all trailing whitespace regardless of whether the line is modified. Trims all trailing whitespace in the repository to make the codebase easier to work with in editors that respect `.editorconfig`. `git blame` becomes less useful on these lines but it already isn't very useful. Commands: ``` find . -type f -name '*.h' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ find . -type f -name '*.c' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ ```
1507 lines
38 KiB
C
1507 lines
38 KiB
C
/*
|
|
gl_alias.c - alias model renderer
|
|
Copyright (C) 2017 Uncle Mike
|
|
|
|
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 3 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.
|
|
*/
|
|
#include "gl_local.h"
|
|
#include "xash3d_mathlib.h"
|
|
#include "const.h"
|
|
#include "r_studioint.h"
|
|
#include "triangleapi.h"
|
|
#include "alias.h"
|
|
#include "pm_local.h"
|
|
#include "pmtrace.h"
|
|
|
|
extern cvar_t r_shadows;
|
|
|
|
typedef struct
|
|
{
|
|
double time;
|
|
double frametime;
|
|
int framecount; // alias framecount
|
|
qboolean interpolate;
|
|
|
|
float ambientlight;
|
|
float shadelight;
|
|
vec3_t lightvec; // averaging light direction
|
|
vec3_t lightvec_local; // light direction in local space
|
|
vec3_t lightspot; // shadow spot
|
|
vec3_t lightcolor; // averaging lightcolor
|
|
int oldpose; // shadow used
|
|
int newpose; // shadow used
|
|
float lerpfrac; // lerp frames
|
|
} alias_draw_state_t;
|
|
|
|
static alias_draw_state_t g_alias; // global alias state
|
|
|
|
/*
|
|
=================================================================
|
|
|
|
ALIAS MODEL DISPLAY LIST GENERATION
|
|
|
|
=================================================================
|
|
*/
|
|
static qboolean m_fDoRemap;
|
|
static aliashdr_t *m_pAliasHeader;
|
|
static trivertex_t *g_poseverts[MAXALIASFRAMES];
|
|
static dtriangle_t g_triangles[MAXALIASTRIS];
|
|
static stvert_t g_stverts[MAXALIASVERTS];
|
|
static int g_used[8192];
|
|
|
|
// a pose is a single set of vertexes. a frame may be
|
|
// an animating sequence of poses
|
|
int g_posenum;
|
|
|
|
// the command list holds counts and s/t values that are valid for
|
|
// every frame
|
|
static int g_commands[8192];
|
|
static int g_numcommands;
|
|
|
|
// all frames will have their vertexes rearranged and expanded
|
|
// so they are in the order expected by the command list
|
|
static int g_vertexorder[8192];
|
|
static int g_numorder;
|
|
|
|
static int g_stripverts[128];
|
|
static int g_striptris[128];
|
|
static int g_stripcount;
|
|
|
|
/*
|
|
====================
|
|
R_StudioInit
|
|
|
|
====================
|
|
*/
|
|
void R_AliasInit( void )
|
|
{
|
|
g_alias.interpolate = true;
|
|
m_fDoRemap = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
StripLength
|
|
================
|
|
*/
|
|
static int StripLength( int starttri, int startv )
|
|
{
|
|
int m1, m2, j, k;
|
|
dtriangle_t *last, *check;
|
|
|
|
g_used[starttri] = 2;
|
|
|
|
last = &g_triangles[starttri];
|
|
|
|
g_stripverts[0] = last->vertindex[(startv+0) % 3];
|
|
g_stripverts[1] = last->vertindex[(startv+1) % 3];
|
|
g_stripverts[2] = last->vertindex[(startv+2) % 3];
|
|
|
|
g_striptris[0] = starttri;
|
|
g_stripcount = 1;
|
|
|
|
m1 = last->vertindex[(startv+2)%3];
|
|
m2 = last->vertindex[(startv+1)%3];
|
|
nexttri:
|
|
// look for a matching triangle
|
|
for( j = starttri + 1, check = &g_triangles[starttri + 1]; j < m_pAliasHeader->numtris; j++, check++ )
|
|
{
|
|
if( check->facesfront != last->facesfront )
|
|
continue;
|
|
|
|
for( k = 0; k < 3; k++ )
|
|
{
|
|
if( check->vertindex[k] != m1 )
|
|
continue;
|
|
if( check->vertindex[(k+1) % 3] != m2 )
|
|
continue;
|
|
|
|
// this is the next part of the fan
|
|
|
|
// if we can't use this triangle, this tristrip is done
|
|
if( g_used[j] ) goto done;
|
|
|
|
// the new edge
|
|
if( g_stripcount & 1 )
|
|
m2 = check->vertindex[(k+2) % 3];
|
|
else m1 = check->vertindex[(k+2) % 3];
|
|
|
|
g_stripverts[g_stripcount+2] = check->vertindex[(k+2) % 3];
|
|
g_striptris[g_stripcount] = j;
|
|
g_stripcount++;
|
|
|
|
g_used[j] = 2;
|
|
goto nexttri;
|
|
}
|
|
}
|
|
done:
|
|
// clear the temp used flags
|
|
for( j = starttri + 1; j < m_pAliasHeader->numtris; j++ )
|
|
{
|
|
if( g_used[j] == 2 )
|
|
g_used[j] = 0;
|
|
}
|
|
|
|
return g_stripcount;
|
|
}
|
|
|
|
/*
|
|
===========
|
|
FanLength
|
|
===========
|
|
*/
|
|
static int FanLength( int starttri, int startv )
|
|
{
|
|
int m1, m2, j, k;
|
|
dtriangle_t *last, *check;
|
|
|
|
g_used[starttri] = 2;
|
|
|
|
last = &g_triangles[starttri];
|
|
|
|
g_stripverts[0] = last->vertindex[(startv+0) % 3];
|
|
g_stripverts[1] = last->vertindex[(startv+1) % 3];
|
|
g_stripverts[2] = last->vertindex[(startv+2) % 3];
|
|
|
|
g_striptris[0] = starttri;
|
|
g_stripcount = 1;
|
|
|
|
m1 = last->vertindex[(startv+0) % 3];
|
|
m2 = last->vertindex[(startv+2) % 3];
|
|
|
|
nexttri:
|
|
// look for a matching triangle
|
|
for( j = starttri + 1, check = &g_triangles[starttri + 1]; j < m_pAliasHeader->numtris; j++, check++ )
|
|
{
|
|
if( check->facesfront != last->facesfront )
|
|
continue;
|
|
|
|
for( k = 0; k < 3; k++ )
|
|
{
|
|
if( check->vertindex[k] != m1 )
|
|
continue;
|
|
if( check->vertindex[(k+1) % 3] != m2 )
|
|
continue;
|
|
|
|
// this is the next part of the fan
|
|
// if we can't use this triangle, this tristrip is done
|
|
if( g_used[j] ) goto done;
|
|
|
|
// the new edge
|
|
m2 = check->vertindex[(k+2) % 3];
|
|
|
|
g_stripverts[g_stripcount + 2] = m2;
|
|
g_striptris[g_stripcount] = j;
|
|
g_stripcount++;
|
|
|
|
g_used[j] = 2;
|
|
goto nexttri;
|
|
}
|
|
}
|
|
done:
|
|
// clear the temp used flags
|
|
for( j = starttri + 1; j < m_pAliasHeader->numtris; j++ )
|
|
{
|
|
if( g_used[j] == 2 )
|
|
g_used[j] = 0;
|
|
}
|
|
|
|
return g_stripcount;
|
|
}
|
|
|
|
/*
|
|
================
|
|
BuildTris
|
|
|
|
Generate a list of trifans or strips
|
|
for the model, which holds for all frames
|
|
================
|
|
*/
|
|
void BuildTris( void )
|
|
{
|
|
int len, bestlen, besttype = 0;
|
|
int bestverts[1024];
|
|
int besttris[1024];
|
|
int type, startv;
|
|
int i, j, k;
|
|
float s, t;
|
|
|
|
//
|
|
// build tristrips
|
|
//
|
|
memset( g_used, 0, sizeof( g_used ));
|
|
g_numcommands = 0;
|
|
g_numorder = 0;
|
|
|
|
for( i = 0; i < m_pAliasHeader->numtris; i++ )
|
|
{
|
|
// pick an unused triangle and start the trifan
|
|
if( g_used[i] ) continue;
|
|
|
|
bestlen = 0;
|
|
for( type = 0; type < 2; type++ )
|
|
{
|
|
for( startv = 0; startv < 3; startv++ )
|
|
{
|
|
if( type == 1 ) len = StripLength( i, startv );
|
|
else len = FanLength( i, startv );
|
|
|
|
if( len > bestlen )
|
|
{
|
|
besttype = type;
|
|
bestlen = len;
|
|
|
|
for( j = 0; j < bestlen + 2; j++ )
|
|
bestverts[j] = g_stripverts[j];
|
|
|
|
for( j = 0; j < bestlen; j++ )
|
|
besttris[j] = g_striptris[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
// mark the tris on the best strip as used
|
|
for( j = 0; j < bestlen; j++ )
|
|
g_used[besttris[j]] = 1;
|
|
|
|
if( besttype == 1 )
|
|
g_commands[g_numcommands++] = (bestlen + 2);
|
|
else g_commands[g_numcommands++] = -(bestlen + 2);
|
|
|
|
for( j = 0; j < bestlen + 2; j++ )
|
|
{
|
|
// emit a vertex into the reorder buffer
|
|
k = bestverts[j];
|
|
g_vertexorder[g_numorder++] = k;
|
|
|
|
// emit s/t coords into the commands stream
|
|
s = g_stverts[k].s;
|
|
t = g_stverts[k].t;
|
|
|
|
if( !g_triangles[besttris[0]].facesfront && g_stverts[k].onseam )
|
|
s += m_pAliasHeader->skinwidth / 2; // on back side
|
|
s = (s + 0.5f) / m_pAliasHeader->skinwidth;
|
|
t = (t + 0.5f) / m_pAliasHeader->skinheight;
|
|
|
|
// Carmack use floats and Valve use shorts here...
|
|
*(float *)&g_commands[g_numcommands++] = s;
|
|
*(float *)&g_commands[g_numcommands++] = t;
|
|
}
|
|
}
|
|
|
|
g_commands[g_numcommands++] = 0; // end of list marker
|
|
}
|
|
|
|
/*
|
|
================
|
|
GL_MakeAliasModelDisplayLists
|
|
================
|
|
*/
|
|
void GL_MakeAliasModelDisplayLists( model_t *m )
|
|
{
|
|
trivertex_t *verts;
|
|
int i, j;
|
|
|
|
BuildTris( );
|
|
|
|
// save the data out
|
|
m_pAliasHeader->poseverts = g_numorder;
|
|
|
|
m_pAliasHeader->commands = Mem_Malloc( m->mempool, g_numcommands * 4 );
|
|
memcpy( m_pAliasHeader->commands, g_commands, g_numcommands * 4 );
|
|
|
|
m_pAliasHeader->posedata = Mem_Malloc( m->mempool, m_pAliasHeader->numposes * m_pAliasHeader->poseverts * sizeof( trivertex_t ));
|
|
verts = m_pAliasHeader->posedata;
|
|
|
|
for( i = 0; i < m_pAliasHeader->numposes; i++ )
|
|
{
|
|
for( j = 0; j < g_numorder; j++ )
|
|
*verts++ = g_poseverts[i][g_vertexorder[j]];
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
ALIAS MODELS
|
|
|
|
==============================================================================
|
|
*/
|
|
/*
|
|
=================
|
|
Mod_LoadAliasFrame
|
|
=================
|
|
*/
|
|
void *Mod_LoadAliasFrame( void *pin, maliasframedesc_t *frame )
|
|
{
|
|
daliasframe_t *pdaliasframe;
|
|
trivertex_t *pinframe;
|
|
int i;
|
|
|
|
pdaliasframe = (daliasframe_t *)pin;
|
|
|
|
Q_strncpy( frame->name, pdaliasframe->name, sizeof( frame->name ));
|
|
frame->firstpose = g_posenum;
|
|
frame->numposes = 1;
|
|
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
frame->bboxmin.v[i] = pdaliasframe->bboxmin.v[i];
|
|
frame->bboxmax.v[i] = pdaliasframe->bboxmax.v[i];
|
|
}
|
|
|
|
pinframe = (trivertex_t *)(pdaliasframe + 1);
|
|
|
|
g_poseverts[g_posenum] = (trivertex_t *)pinframe;
|
|
pinframe += m_pAliasHeader->numverts;
|
|
g_posenum++;
|
|
|
|
return (void *)pinframe;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadAliasGroup
|
|
=================
|
|
*/
|
|
void *Mod_LoadAliasGroup( void *pin, maliasframedesc_t *frame )
|
|
{
|
|
daliasgroup_t *pingroup;
|
|
int i, numframes;
|
|
daliasinterval_t *pin_intervals;
|
|
void *ptemp;
|
|
|
|
pingroup = (daliasgroup_t *)pin;
|
|
numframes = pingroup->numframes;
|
|
|
|
frame->firstpose = g_posenum;
|
|
frame->numposes = numframes;
|
|
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
frame->bboxmin.v[i] = pingroup->bboxmin.v[i];
|
|
frame->bboxmax.v[i] = pingroup->bboxmax.v[i];
|
|
}
|
|
|
|
pin_intervals = (daliasinterval_t *)(pingroup + 1);
|
|
|
|
// all the intervals are always equal 0.1 so we don't care about them
|
|
frame->interval = pin_intervals->interval;
|
|
pin_intervals += numframes;
|
|
ptemp = (void *)pin_intervals;
|
|
|
|
for( i = 0; i < numframes; i++ )
|
|
{
|
|
g_poseverts[g_posenum] = (trivertex_t *)((daliasframe_t *)ptemp + 1);
|
|
ptemp = (trivertex_t *)((daliasframe_t *)ptemp + 1) + m_pAliasHeader->numverts;
|
|
g_posenum++;
|
|
}
|
|
|
|
return ptemp;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Mod_CreateSkinData
|
|
===============
|
|
*/
|
|
rgbdata_t *Mod_CreateSkinData( model_t *mod, byte *data, int width, int height )
|
|
{
|
|
static rgbdata_t skin;
|
|
char name[MAX_QPATH];
|
|
int i;
|
|
model_t *loadmodel = gEngfuncs.Mod_GetCurrentLoadingModel();
|
|
|
|
skin.width = width;
|
|
skin.height = height;
|
|
skin.depth = 1;
|
|
skin.type = PF_INDEXED_24;
|
|
skin.flags = IMAGE_HAS_COLOR|IMAGE_QUAKEPAL;
|
|
skin.encode = DXT_ENCODE_DEFAULT;
|
|
skin.numMips = 1;
|
|
skin.buffer = data;
|
|
skin.palette = (byte *)gEngfuncs.CL_GetPaletteColor( 0 );
|
|
skin.size = width * height;
|
|
|
|
if( !gEngfuncs.Image_CustomPalette() )
|
|
{
|
|
for( i = 0; i < skin.width * skin.height; i++ )
|
|
{
|
|
if( data[i] > 224 && data[i] != 255 )
|
|
{
|
|
SetBits( skin.flags, IMAGE_HAS_LUMA );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
COM_FileBase( loadmodel->name, name );
|
|
|
|
// for alias models only player can have remap textures
|
|
if( mod != NULL && !Q_stricmp( name, "player" ))
|
|
{
|
|
texture_t *tx = NULL;
|
|
int i, size;
|
|
|
|
i = mod->numtextures;
|
|
mod->textures = (texture_t **)Mem_Realloc( mod->mempool, mod->textures, ( i + 1 ) * sizeof( texture_t* ));
|
|
size = width * height + 768;
|
|
tx = Mem_Calloc( mod->mempool, sizeof( *tx ) + size );
|
|
mod->textures[i] = tx;
|
|
|
|
Q_strncpy( tx->name, "DM_Skin", sizeof( tx->name ));
|
|
tx->anim_min = SHIRT_HUE_START; // topcolor start
|
|
tx->anim_max = SHIRT_HUE_END; // topcolor end
|
|
// bottomcolor start always equal is (topcolor end + 1)
|
|
tx->anim_total = PANTS_HUE_END;// bottomcolor end
|
|
|
|
tx->width = width;
|
|
tx->height = height;
|
|
|
|
// the pixels immediately follow the structures
|
|
memcpy( (tx+1), data, width * height );
|
|
memcpy( ((byte *)(tx+1)+(width * height)), skin.palette, 768 );
|
|
mod->numtextures++; // done
|
|
}
|
|
|
|
// make an copy
|
|
return gEngfuncs.FS_CopyImage( &skin );
|
|
}
|
|
|
|
void *Mod_LoadSingleSkin( daliasskintype_t *pskintype, int skinnum, int size )
|
|
{
|
|
string name, lumaname;
|
|
string checkname;
|
|
rgbdata_t *pic;
|
|
model_t *loadmodel = gEngfuncs.Mod_GetCurrentLoadingModel();
|
|
|
|
Q_snprintf( name, sizeof( name ), "%s:frame%i", loadmodel->name, skinnum );
|
|
Q_snprintf( lumaname, sizeof( lumaname ), "%s:luma%i", loadmodel->name, skinnum );
|
|
Q_snprintf( checkname, sizeof( checkname ), "%s_%i.tga", loadmodel->name, skinnum );
|
|
if( !gEngfuncs.FS_FileExists( checkname, false ) || ( pic = gEngfuncs.FS_LoadImage( checkname, NULL, 0 )) == NULL )
|
|
pic = Mod_CreateSkinData( loadmodel, (byte *)(pskintype + 1), m_pAliasHeader->skinwidth, m_pAliasHeader->skinheight );
|
|
|
|
m_pAliasHeader->gl_texturenum[skinnum][0] =
|
|
m_pAliasHeader->gl_texturenum[skinnum][1] =
|
|
m_pAliasHeader->gl_texturenum[skinnum][2] =
|
|
m_pAliasHeader->gl_texturenum[skinnum][3] = GL_LoadTextureInternal( name, pic, 0 );
|
|
gEngfuncs.FS_FreeImage( pic );
|
|
|
|
if( R_GetTexture( m_pAliasHeader->gl_texturenum[skinnum][0] )->flags & TF_HAS_LUMA )
|
|
{
|
|
pic = Mod_CreateSkinData( NULL, (byte *)(pskintype + 1), m_pAliasHeader->skinwidth, m_pAliasHeader->skinheight );
|
|
m_pAliasHeader->fb_texturenum[skinnum][0] =
|
|
m_pAliasHeader->fb_texturenum[skinnum][1] =
|
|
m_pAliasHeader->fb_texturenum[skinnum][2] =
|
|
m_pAliasHeader->fb_texturenum[skinnum][3] = GL_LoadTextureInternal( lumaname, pic, TF_MAKELUMA );
|
|
gEngfuncs.FS_FreeImage( pic );
|
|
}
|
|
|
|
return ((byte *)(pskintype + 1) + size);
|
|
}
|
|
|
|
void *Mod_LoadGroupSkin( daliasskintype_t *pskintype, int skinnum, int size )
|
|
{
|
|
daliasskininterval_t *pinskinintervals;
|
|
daliasskingroup_t *pinskingroup;
|
|
string name, lumaname;
|
|
rgbdata_t *pic;
|
|
int i, j;
|
|
model_t *loadmodel = gEngfuncs.Mod_GetCurrentLoadingModel();
|
|
|
|
// animating skin group. yuck.
|
|
pskintype++;
|
|
pinskingroup = (daliasskingroup_t *)pskintype;
|
|
pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1);
|
|
pskintype = (void *)(pinskinintervals + pinskingroup->numskins);
|
|
|
|
for( i = 0; i < pinskingroup->numskins; i++ )
|
|
{
|
|
Q_snprintf( name, sizeof( name ), "%s_%i_%i", loadmodel->name, skinnum, i );
|
|
pic = Mod_CreateSkinData( loadmodel, (byte *)(pskintype), m_pAliasHeader->skinwidth, m_pAliasHeader->skinheight );
|
|
m_pAliasHeader->gl_texturenum[skinnum][i & 3] = GL_LoadTextureInternal( name, pic, 0 );
|
|
gEngfuncs.FS_FreeImage( pic );
|
|
|
|
if( R_GetTexture( m_pAliasHeader->gl_texturenum[skinnum][i & 3] )->flags & TF_HAS_LUMA )
|
|
{
|
|
Q_snprintf( lumaname, sizeof( lumaname ), "%s_%i_%i_luma", loadmodel->name, skinnum, i );
|
|
pic = Mod_CreateSkinData( NULL, (byte *)(pskintype), m_pAliasHeader->skinwidth, m_pAliasHeader->skinheight );
|
|
m_pAliasHeader->fb_texturenum[skinnum][i & 3] = GL_LoadTextureInternal( lumaname, pic, TF_MAKELUMA );
|
|
gEngfuncs.FS_FreeImage( pic );
|
|
}
|
|
|
|
pskintype = (daliasskintype_t *)((byte *)(pskintype) + size);
|
|
}
|
|
|
|
for( j = i; i < 4; i++ )
|
|
{
|
|
m_pAliasHeader->gl_texturenum[skinnum][i & 3] = m_pAliasHeader->gl_texturenum[skinnum][i - j];
|
|
m_pAliasHeader->fb_texturenum[skinnum][i & 3] = m_pAliasHeader->fb_texturenum[skinnum][i - j];
|
|
}
|
|
|
|
return pskintype;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Mod_LoadAllSkins
|
|
===============
|
|
*/
|
|
void *Mod_LoadAllSkins( int numskins, daliasskintype_t *pskintype )
|
|
{
|
|
int i, size;
|
|
|
|
if( numskins < 1 || numskins > MAX_SKINS )
|
|
gEngfuncs.Host_Error( "Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins );
|
|
|
|
size = m_pAliasHeader->skinwidth * m_pAliasHeader->skinheight;
|
|
|
|
for( i = 0; i < numskins; i++ )
|
|
{
|
|
if( pskintype->type == ALIAS_SKIN_SINGLE )
|
|
{
|
|
pskintype = (daliasskintype_t *)Mod_LoadSingleSkin( pskintype, i, size );
|
|
}
|
|
else
|
|
{
|
|
pskintype = (daliasskintype_t *)Mod_LoadGroupSkin( pskintype, i, size );
|
|
}
|
|
}
|
|
|
|
return (void *)pskintype;
|
|
}
|
|
|
|
//=========================================================================
|
|
/*
|
|
=================
|
|
Mod_CalcAliasBounds
|
|
=================
|
|
*/
|
|
void Mod_CalcAliasBounds( model_t *mod )
|
|
{
|
|
int i, j, k;
|
|
float radius;
|
|
float dist;
|
|
vec3_t v;
|
|
|
|
ClearBounds( mod->mins, mod->maxs );
|
|
radius = 0.0f;
|
|
|
|
// process verts
|
|
for( i = 0; i < m_pAliasHeader->numposes; i++ )
|
|
{
|
|
for( j = 0; j < m_pAliasHeader->numverts; j++ )
|
|
{
|
|
for( k = 0; k < 3; k++ )
|
|
v[k] = g_poseverts[i][j].v[k] * m_pAliasHeader->scale[k] + m_pAliasHeader->scale_origin[k];
|
|
|
|
AddPointToBounds( v, mod->mins, mod->maxs );
|
|
dist = DotProduct( v, v );
|
|
|
|
if( radius < dist )
|
|
radius = dist;
|
|
}
|
|
}
|
|
|
|
mod->radius = sqrt( radius );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadAliasModel
|
|
=================
|
|
*/
|
|
void Mod_LoadAliasModel( model_t *mod, const void *buffer, qboolean *loaded )
|
|
{
|
|
daliashdr_t *pinmodel;
|
|
stvert_t *pinstverts;
|
|
dtriangle_t *pintriangles;
|
|
daliasframetype_t *pframetype;
|
|
daliasskintype_t *pskintype;
|
|
int i, j, size;
|
|
|
|
if( loaded ) *loaded = false;
|
|
pinmodel = (daliashdr_t *)buffer;
|
|
i = pinmodel->version;
|
|
|
|
if( i != ALIAS_VERSION )
|
|
{
|
|
gEngfuncs.Con_DPrintf( S_ERROR "%s has wrong version number (%i should be %i)\n", mod->name, i, ALIAS_VERSION );
|
|
return;
|
|
}
|
|
|
|
if( pinmodel->numverts <= 0 || pinmodel->numtris <= 0 || pinmodel->numframes <= 0 )
|
|
return; // how to possible is make that?
|
|
|
|
mod->mempool = Mem_AllocPool( va( "^2%s^7", mod->name ));
|
|
|
|
// allocate space for a working header, plus all the data except the frames,
|
|
// skin and group info
|
|
size = sizeof( aliashdr_t ) + (pinmodel->numframes - 1) * sizeof( maliasframedesc_t );
|
|
|
|
m_pAliasHeader = Mem_Calloc( mod->mempool, size );
|
|
mod->flags = pinmodel->flags; // share effects flags
|
|
|
|
// endian-adjust and copy the data, starting with the alias model header
|
|
m_pAliasHeader->boundingradius = pinmodel->boundingradius;
|
|
m_pAliasHeader->numskins = pinmodel->numskins;
|
|
m_pAliasHeader->skinwidth = pinmodel->skinwidth;
|
|
m_pAliasHeader->skinheight = pinmodel->skinheight;
|
|
m_pAliasHeader->numverts = pinmodel->numverts;
|
|
m_pAliasHeader->numtris = pinmodel->numtris;
|
|
m_pAliasHeader->numframes = pinmodel->numframes;
|
|
|
|
if( m_pAliasHeader->numverts > MAXALIASVERTS )
|
|
{
|
|
gEngfuncs.Con_DPrintf( S_ERROR "model %s has too many vertices\n", mod->name );
|
|
return;
|
|
}
|
|
|
|
m_pAliasHeader->size = pinmodel->size;
|
|
// mod->synctype = pinmodel->synctype;
|
|
mod->numframes = m_pAliasHeader->numframes;
|
|
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
m_pAliasHeader->scale[i] = pinmodel->scale[i];
|
|
m_pAliasHeader->scale_origin[i] = pinmodel->scale_origin[i];
|
|
m_pAliasHeader->eyeposition[i] = pinmodel->eyeposition[i];
|
|
}
|
|
|
|
// load the skins
|
|
pskintype = (daliasskintype_t *)&pinmodel[1];
|
|
pskintype = Mod_LoadAllSkins( m_pAliasHeader->numskins, pskintype );
|
|
|
|
// load base s and t vertices
|
|
pinstverts = (stvert_t *)pskintype;
|
|
|
|
for( i = 0; i < m_pAliasHeader->numverts; i++ )
|
|
{
|
|
g_stverts[i].onseam = pinstverts[i].onseam;
|
|
g_stverts[i].s = pinstverts[i].s;
|
|
g_stverts[i].t = pinstverts[i].t;
|
|
}
|
|
|
|
// load triangle lists
|
|
pintriangles = (dtriangle_t *)&pinstverts[m_pAliasHeader->numverts];
|
|
|
|
for( i = 0; i < m_pAliasHeader->numtris; i++ )
|
|
{
|
|
g_triangles[i].facesfront = pintriangles[i].facesfront;
|
|
|
|
for( j = 0; j < 3; j++ )
|
|
g_triangles[i].vertindex[j] = pintriangles[i].vertindex[j];
|
|
}
|
|
|
|
// load the frames
|
|
pframetype = (daliasframetype_t *)&pintriangles[m_pAliasHeader->numtris];
|
|
g_posenum = 0;
|
|
|
|
for( i = 0; i < m_pAliasHeader->numframes; i++ )
|
|
{
|
|
aliasframetype_t frametype = pframetype->type;
|
|
|
|
if( frametype == ALIAS_SINGLE )
|
|
pframetype = (daliasframetype_t *)Mod_LoadAliasFrame( pframetype + 1, &m_pAliasHeader->frames[i] );
|
|
else pframetype = (daliasframetype_t *)Mod_LoadAliasGroup( pframetype + 1, &m_pAliasHeader->frames[i] );
|
|
}
|
|
|
|
m_pAliasHeader->numposes = g_posenum;
|
|
|
|
Mod_CalcAliasBounds( mod );
|
|
mod->type = mod_alias;
|
|
|
|
// build the draw lists
|
|
GL_MakeAliasModelDisplayLists( mod );
|
|
|
|
// move the complete, relocatable alias model to the cache
|
|
gEngfuncs.Mod_GetCurrentLoadingModel()->cache.data = m_pAliasHeader;
|
|
|
|
if( loaded ) *loaded = true; // done
|
|
}
|
|
|
|
void Mod_AliasUnloadTextures( void *data )
|
|
{
|
|
aliashdr_t *palias;
|
|
int i, j;
|
|
|
|
palias = data;
|
|
if( !palias ) return; // already freed
|
|
|
|
for( i = 0; i < MAX_SKINS; i++ )
|
|
{
|
|
if( !palias->gl_texturenum[i][0] )
|
|
break;
|
|
|
|
for( j = 0; j < 4; j++ )
|
|
{
|
|
GL_FreeTexture( palias->gl_texturenum[i][j] );
|
|
GL_FreeTexture( palias->fb_texturenum[i][j] );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============================================================
|
|
|
|
ALIAS MODELS
|
|
|
|
=============================================================
|
|
*/
|
|
|
|
/*
|
|
===============
|
|
R_AliasDynamicLight
|
|
|
|
similar to R_StudioDynamicLight
|
|
===============
|
|
*/
|
|
void R_AliasDynamicLight( cl_entity_t *ent, alight_t *plight )
|
|
{
|
|
movevars_t *mv = gEngfuncs.pfnGetMoveVars();
|
|
vec3_t lightDir, vecSrc, vecEnd;
|
|
vec3_t origin, dist, finalLight;
|
|
float add, radius, total;
|
|
colorVec light;
|
|
uint lnum;
|
|
dlight_t *dl;
|
|
|
|
if( !plight || !ent )
|
|
return;
|
|
|
|
if( !RI.drawWorld || r_fullbright->value || FBitSet( ent->curstate.effects, EF_FULLBRIGHT ))
|
|
{
|
|
plight->shadelight = 0;
|
|
plight->ambientlight = 192;
|
|
|
|
VectorSet( plight->plightvec, 0.0f, 0.0f, -1.0f );
|
|
VectorSet( plight->color, 1.0f, 1.0f, 1.0f );
|
|
return;
|
|
}
|
|
|
|
// determine plane to get lightvalues from: ceil or floor
|
|
if( FBitSet( ent->curstate.effects, EF_INVLIGHT ))
|
|
VectorSet( lightDir, 0.0f, 0.0f, 1.0f );
|
|
else VectorSet( lightDir, 0.0f, 0.0f, -1.0f );
|
|
|
|
VectorCopy( ent->origin, origin );
|
|
|
|
VectorSet( vecSrc, origin[0], origin[1], origin[2] - lightDir[2] * 8.0f );
|
|
light.r = light.g = light.b = light.a = 0;
|
|
|
|
if(( mv->skycolor_r + mv->skycolor_g + mv->skycolor_b ) != 0 )
|
|
{
|
|
msurface_t *psurf = NULL;
|
|
pmtrace_t trace;
|
|
|
|
if( FBitSet( ENGINE_GET_PARM( PARM_FEATURES ), ENGINE_WRITE_LARGE_COORD ))
|
|
{
|
|
vecEnd[0] = origin[0] - mv->skyvec_x * 65536.0f;
|
|
vecEnd[1] = origin[1] - mv->skyvec_y * 65536.0f;
|
|
vecEnd[2] = origin[2] - mv->skyvec_z * 65536.0f;
|
|
}
|
|
else
|
|
{
|
|
vecEnd[0] = origin[0] - mv->skyvec_x * 8192.0f;
|
|
vecEnd[1] = origin[1] - mv->skyvec_y * 8192.0f;
|
|
vecEnd[2] = origin[2] - mv->skyvec_z * 8192.0f;
|
|
}
|
|
|
|
trace = gEngfuncs.CL_TraceLine( vecSrc, vecEnd, PM_STUDIO_IGNORE );
|
|
if( trace.ent > 0 ) psurf = gEngfuncs.EV_TraceSurface( trace.ent, vecSrc, vecEnd );
|
|
else psurf = gEngfuncs.EV_TraceSurface( 0, vecSrc, vecEnd );
|
|
|
|
if( psurf && FBitSet( psurf->flags, SURF_DRAWSKY ))
|
|
{
|
|
VectorSet( lightDir, mv->skyvec_x, mv->skyvec_y, mv->skyvec_z );
|
|
|
|
light.r = gEngfuncs.LightToTexGamma( bound( 0, mv->skycolor_r, 255 ));
|
|
light.g = gEngfuncs.LightToTexGamma( bound( 0, mv->skycolor_g, 255 ));
|
|
light.b = gEngfuncs.LightToTexGamma( bound( 0, mv->skycolor_b, 255 ));
|
|
}
|
|
}
|
|
|
|
if(( light.r + light.g + light.b ) == 0 )
|
|
{
|
|
colorVec gcolor;
|
|
float grad[4];
|
|
|
|
VectorScale( lightDir, 2048.0f, vecEnd );
|
|
VectorAdd( vecEnd, vecSrc, vecEnd );
|
|
|
|
light = R_LightVec( vecSrc, vecEnd, g_alias.lightspot, g_alias.lightvec );
|
|
|
|
if( VectorIsNull( g_alias.lightvec ))
|
|
{
|
|
vecSrc[0] -= 16.0f;
|
|
vecSrc[1] -= 16.0f;
|
|
vecEnd[0] -= 16.0f;
|
|
vecEnd[1] -= 16.0f;
|
|
|
|
gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL );
|
|
grad[0] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f;
|
|
|
|
vecSrc[0] += 32.0f;
|
|
vecEnd[0] += 32.0f;
|
|
|
|
gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL );
|
|
grad[1] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f;
|
|
|
|
vecSrc[1] += 32.0f;
|
|
vecEnd[1] += 32.0f;
|
|
|
|
gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL );
|
|
grad[2] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f;
|
|
|
|
vecSrc[0] -= 32.0f;
|
|
vecEnd[0] -= 32.0f;
|
|
|
|
gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL );
|
|
grad[3] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f;
|
|
|
|
lightDir[0] = grad[0] - grad[1] - grad[2] + grad[3];
|
|
lightDir[1] = grad[1] + grad[0] - grad[2] - grad[3];
|
|
VectorNormalize( lightDir );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( g_alias.lightvec, lightDir );
|
|
}
|
|
}
|
|
|
|
VectorSet( finalLight, light.r, light.g, light.b );
|
|
ent->cvFloorColor = light;
|
|
|
|
total = Q_max( Q_max( light.r, light.g ), light.b );
|
|
if( total == 0.0f ) total = 1.0f;
|
|
|
|
// scale lightdir by light intentsity
|
|
VectorScale( lightDir, total, lightDir );
|
|
|
|
for( lnum = 0; lnum < MAX_DLIGHTS; lnum++ )
|
|
{
|
|
dl = gEngfuncs.GetDynamicLight( lnum );
|
|
|
|
if( dl->die < g_alias.time || !r_dynamic->value )
|
|
continue;
|
|
|
|
VectorSubtract( origin, dl->origin, dist );
|
|
|
|
radius = VectorLength( dist );
|
|
add = dl->radius - radius;
|
|
|
|
if( add > 0.0f )
|
|
{
|
|
total += add;
|
|
|
|
if( radius > 1.0f )
|
|
VectorScale( dist, ( add / radius ), dist );
|
|
else VectorScale( dist, add, dist );
|
|
|
|
VectorAdd( lightDir, dist, lightDir );
|
|
|
|
finalLight[0] += gEngfuncs.LightToTexGamma( dl->color.r ) * ( add / 256.0f ) * 2.0f;
|
|
finalLight[1] += gEngfuncs.LightToTexGamma( dl->color.g ) * ( add / 256.0f ) * 2.0f;
|
|
finalLight[2] += gEngfuncs.LightToTexGamma( dl->color.b ) * ( add / 256.0f ) * 2.0f;
|
|
}
|
|
}
|
|
|
|
VectorScale( lightDir, 0.9f, lightDir );
|
|
|
|
plight->shadelight = VectorLength( lightDir );
|
|
plight->ambientlight = total - plight->shadelight;
|
|
|
|
total = Q_max( Q_max( finalLight[0], finalLight[1] ), finalLight[2] );
|
|
|
|
if( total > 0.0f )
|
|
{
|
|
plight->color[0] = finalLight[0] * ( 1.0f / total );
|
|
plight->color[1] = finalLight[1] * ( 1.0f / total );
|
|
plight->color[2] = finalLight[2] * ( 1.0f / total );
|
|
}
|
|
else VectorSet( plight->color, 1.0f, 1.0f, 1.0f );
|
|
|
|
if( plight->ambientlight > 128 )
|
|
plight->ambientlight = 128;
|
|
|
|
if( plight->ambientlight + plight->shadelight > 255 )
|
|
plight->shadelight = 255 - plight->ambientlight;
|
|
|
|
VectorNormalize2( lightDir, plight->plightvec );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_AliasSetupLighting
|
|
|
|
===============
|
|
*/
|
|
void R_AliasSetupLighting( alight_t *plight )
|
|
{
|
|
if( !m_pAliasHeader || !plight )
|
|
return;
|
|
|
|
g_alias.ambientlight = plight->ambientlight;
|
|
g_alias.shadelight = plight->shadelight;
|
|
VectorCopy( plight->plightvec, g_alias.lightvec );
|
|
VectorCopy( plight->color, g_alias.lightcolor );
|
|
|
|
// transform back to local space
|
|
Matrix4x4_VectorIRotate( RI.objectMatrix, g_alias.lightvec, g_alias.lightvec_local );
|
|
VectorNormalize( g_alias.lightvec_local );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_AliasLighting
|
|
|
|
===============
|
|
*/
|
|
void R_AliasLighting( float *lv, const vec3_t normal )
|
|
{
|
|
float illum = g_alias.ambientlight;
|
|
float r, lightcos;
|
|
|
|
lightcos = DotProduct( normal, g_alias.lightvec_local ); // -1 colinear, 1 opposite
|
|
if( lightcos > 1.0f ) lightcos = 1.0f;
|
|
|
|
illum += g_alias.shadelight;
|
|
|
|
r = SHADE_LAMBERT;
|
|
|
|
// do modified hemispherical lighting
|
|
if( r <= 1.0f )
|
|
{
|
|
r += 1.0f;
|
|
lightcos = (( r - 1.0f ) - lightcos) / r;
|
|
if( lightcos > 0.0f )
|
|
illum += g_alias.shadelight * lightcos;
|
|
}
|
|
else
|
|
{
|
|
lightcos = (lightcos + ( r - 1.0f )) / r;
|
|
if( lightcos > 0.0f )
|
|
illum -= g_alias.shadelight * lightcos;
|
|
}
|
|
|
|
illum = Q_max( illum, 0.0f );
|
|
illum = Q_min( illum, 255.0f );
|
|
*lv = illum * (1.0f / 255.0f);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_AliasSetRemapColors
|
|
|
|
===============
|
|
*/
|
|
void R_AliasSetRemapColors( int newTop, int newBottom )
|
|
{
|
|
gEngfuncs.CL_AllocRemapInfo( RI.currententity, newTop, newBottom );
|
|
|
|
if( gEngfuncs.CL_GetRemapInfoForEntity( RI.currententity ))
|
|
{
|
|
gEngfuncs.CL_UpdateRemapInfo( RI.currententity, newTop, newBottom );
|
|
m_fDoRemap = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
GL_DrawAliasFrame
|
|
=============
|
|
*/
|
|
void GL_DrawAliasFrame( aliashdr_t *paliashdr )
|
|
{
|
|
float lv_tmp;
|
|
trivertex_t *verts0;
|
|
trivertex_t *verts1;
|
|
vec3_t vert, norm;
|
|
int *order;
|
|
int count;
|
|
|
|
verts0 = verts1 = paliashdr->posedata;
|
|
verts0 += g_alias.oldpose * paliashdr->poseverts;
|
|
verts1 += g_alias.newpose * paliashdr->poseverts;
|
|
order = paliashdr->commands;
|
|
|
|
while( 1 )
|
|
{
|
|
// get the vertex count and primitive type
|
|
count = *order++;
|
|
if( !count ) break; // done
|
|
|
|
if( count < 0 )
|
|
{
|
|
pglBegin( GL_TRIANGLE_FAN );
|
|
count = -count;
|
|
}
|
|
else
|
|
{
|
|
pglBegin( GL_TRIANGLE_STRIP );
|
|
}
|
|
|
|
do
|
|
{
|
|
// texture coordinates come from the draw list
|
|
if( GL_Support( GL_ARB_MULTITEXTURE ) && glState.activeTMU > 0 )
|
|
{
|
|
GL_MultiTexCoord2f( XASH_TEXTURE0, ((float *)order)[0], ((float *)order)[1] );
|
|
GL_MultiTexCoord2f( XASH_TEXTURE1, ((float *)order)[0], ((float *)order)[1] );
|
|
}
|
|
else
|
|
{
|
|
pglTexCoord2f( ((float *)order)[0], ((float *)order)[1] );
|
|
}
|
|
order += 2;
|
|
|
|
VectorLerp( m_bytenormals[verts0->lightnormalindex], g_alias.lerpfrac, m_bytenormals[verts1->lightnormalindex], norm );
|
|
VectorNormalize( norm );
|
|
R_AliasLighting( &lv_tmp, norm );
|
|
pglColor4f( g_alias.lightcolor[0] * lv_tmp, g_alias.lightcolor[1] * lv_tmp, g_alias.lightcolor[2] * lv_tmp, tr.blend );
|
|
VectorLerp( verts0->v, g_alias.lerpfrac, verts1->v, vert );
|
|
pglVertex3fv( vert );
|
|
verts0++, verts1++;
|
|
} while( --count );
|
|
|
|
pglEnd();
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
GL_DrawAliasShadow
|
|
=============
|
|
*/
|
|
void GL_DrawAliasShadow( aliashdr_t *paliashdr )
|
|
{
|
|
trivertex_t *verts0;
|
|
trivertex_t *verts1;
|
|
float vec_x, vec_y;
|
|
vec3_t av, point;
|
|
int *order;
|
|
float height;
|
|
int count;
|
|
|
|
if( FBitSet( RI.currententity->curstate.effects, EF_NOSHADOW ))
|
|
return;
|
|
|
|
if( glState.stencilEnabled )
|
|
pglEnable( GL_STENCIL_TEST );
|
|
|
|
height = g_alias.lightspot[2] + 1.0f;
|
|
vec_x = -g_alias.lightvec[0] * 8.0f;
|
|
vec_y = -g_alias.lightvec[1] * 8.0f;
|
|
|
|
r_stats.c_alias_polys += paliashdr->numtris;
|
|
|
|
verts0 = verts1 = paliashdr->posedata;
|
|
verts0 += g_alias.oldpose * paliashdr->poseverts;
|
|
verts1 += g_alias.newpose * paliashdr->poseverts;
|
|
order = paliashdr->commands;
|
|
|
|
while( 1 )
|
|
{
|
|
// get the vertex count and primitive type
|
|
count = *order++;
|
|
if( !count ) break; // done
|
|
|
|
if( count < 0 )
|
|
{
|
|
pglBegin( GL_TRIANGLE_FAN );
|
|
count = -count;
|
|
}
|
|
else
|
|
{
|
|
pglBegin( GL_TRIANGLE_STRIP );
|
|
}
|
|
|
|
do
|
|
{
|
|
// texture coordinates come from the draw list
|
|
// (skipped for shadows) pglTexCoord2fv ((float *)order);
|
|
order += 2;
|
|
|
|
// normals and vertexes come from the frame list
|
|
VectorLerp( verts0->v, g_alias.lerpfrac, verts1->v, av );
|
|
point[0] = av[0] * paliashdr->scale[0] + paliashdr->scale_origin[0];
|
|
point[1] = av[1] * paliashdr->scale[1] + paliashdr->scale_origin[1];
|
|
point[2] = av[2] * paliashdr->scale[2] + paliashdr->scale_origin[2];
|
|
Matrix3x4_VectorTransform( RI.objectMatrix, point, av );
|
|
|
|
point[0] = av[0] - (vec_x * ( av[2] - g_alias.lightspot[2] ));
|
|
point[1] = av[1] - (vec_y * ( av[2] - g_alias.lightspot[2] ));
|
|
point[2] = g_alias.lightspot[2] + 1.0f;
|
|
|
|
pglVertex3fv( point );
|
|
verts0++, verts1++;
|
|
|
|
} while( --count );
|
|
|
|
pglEnd();
|
|
}
|
|
|
|
if( glState.stencilEnabled )
|
|
pglDisable( GL_STENCIL_TEST );
|
|
}
|
|
|
|
/*
|
|
====================
|
|
R_AliasLerpMovement
|
|
|
|
====================
|
|
*/
|
|
void R_AliasLerpMovement( cl_entity_t *e )
|
|
{
|
|
float f = 1.0f;
|
|
|
|
// don't do it if the goalstarttime hasn't updated in a while.
|
|
// NOTE: Because we need to interpolate multiplayer characters, the interpolation time limit
|
|
// was increased to 1.0 s., which is 2x the max lag we are accounting for.
|
|
if( g_alias.interpolate && ( g_alias.time < e->curstate.animtime + 1.0f ) && ( e->curstate.animtime != e->latched.prevanimtime ))
|
|
f = ( g_alias.time - e->curstate.animtime ) / ( e->curstate.animtime - e->latched.prevanimtime );
|
|
|
|
if( ENGINE_GET_PARM( PARM_PLAYING_DEMO ) == DEMO_QUAKE1 )
|
|
f = f + 1.0f;
|
|
|
|
g_alias.lerpfrac = bound( 0.0f, f, 1.0f );
|
|
|
|
if( e->player || e->curstate.movetype != MOVETYPE_STEP )
|
|
return; // monsters only
|
|
|
|
// Con_Printf( "%4.2f %.2f %.2f\n", f, e->curstate.animtime, g_alias.time );
|
|
VectorLerp( e->latched.prevorigin, f, e->curstate.origin, e->origin );
|
|
|
|
if( !VectorCompareEpsilon( e->curstate.angles, e->latched.prevangles, ON_EPSILON ))
|
|
{
|
|
vec4_t q, q1, q2;
|
|
|
|
AngleQuaternion( e->curstate.angles, q1, false );
|
|
AngleQuaternion( e->latched.prevangles, q2, false );
|
|
QuaternionSlerp( q2, q1, f, q );
|
|
QuaternionAngle( q, e->angles );
|
|
}
|
|
else VectorCopy( e->curstate.angles, e->angles );
|
|
|
|
// NOTE: this completely over control about angles and don't broke interpolation
|
|
if( FBitSet( e->model->flags, ALIAS_ROTATE ))
|
|
e->angles[1] = anglemod( 100.0f * g_alias.time );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_SetupAliasFrame
|
|
|
|
=================
|
|
*/
|
|
void R_SetupAliasFrame( cl_entity_t *e, aliashdr_t *paliashdr )
|
|
{
|
|
int newpose, oldpose;
|
|
int newframe, oldframe;
|
|
int numposes, cycle;
|
|
float interval;
|
|
|
|
oldframe = e->latched.prevframe;
|
|
newframe = e->curstate.frame;
|
|
|
|
if( newframe < 0 )
|
|
{
|
|
newframe = 0;
|
|
}
|
|
else if( newframe >= paliashdr->numframes )
|
|
{
|
|
if( newframe > paliashdr->numframes )
|
|
gEngfuncs.Con_Reportf( S_WARN "R_GetAliasFrame: no such frame %d (%s)\n", newframe, e->model->name );
|
|
newframe = paliashdr->numframes - 1;
|
|
}
|
|
|
|
if(( oldframe >= paliashdr->numframes ) || ( oldframe < 0 ))
|
|
oldframe = newframe;
|
|
|
|
numposes = paliashdr->frames[newframe].numposes;
|
|
|
|
if( numposes > 1 )
|
|
{
|
|
oldpose = newpose = paliashdr->frames[newframe].firstpose;
|
|
interval = 1.0f / paliashdr->frames[newframe].interval;
|
|
cycle = (int)(g_alias.time * interval);
|
|
oldpose += (cycle + 0) % numposes; // lerpframe from
|
|
newpose += (cycle + 1) % numposes; // lerpframe to
|
|
g_alias.lerpfrac = ( g_alias.time * interval );
|
|
g_alias.lerpfrac -= (int)g_alias.lerpfrac;
|
|
}
|
|
else
|
|
{
|
|
oldpose = paliashdr->frames[oldframe].firstpose;
|
|
newpose = paliashdr->frames[newframe].firstpose;
|
|
}
|
|
|
|
g_alias.oldpose = oldpose;
|
|
g_alias.newpose = newpose;
|
|
|
|
GL_DrawAliasFrame( paliashdr );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_StudioDrawAbsBBox
|
|
|
|
===============
|
|
*/
|
|
static void R_AliasDrawAbsBBox( cl_entity_t *e, const vec3_t absmin, const vec3_t absmax )
|
|
{
|
|
vec3_t p[8];
|
|
int i;
|
|
|
|
// looks ugly, skip
|
|
if( r_drawentities->value != 5 || e == gEngfuncs.GetViewModel() )
|
|
return;
|
|
|
|
// compute a full bounding box
|
|
for( i = 0; i < 8; i++ )
|
|
{
|
|
p[i][0] = ( i & 1 ) ? absmin[0] : absmax[0];
|
|
p[i][1] = ( i & 2 ) ? absmin[1] : absmax[1];
|
|
p[i][2] = ( i & 4 ) ? absmin[2] : absmax[2];
|
|
}
|
|
|
|
GL_Bind( XASH_TEXTURE0, tr.whiteTexture );
|
|
TriColor4f( 0.5f, 0.5f, 1.0f, 0.5f );
|
|
TriRenderMode( kRenderTransAdd );
|
|
pglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
|
|
|
|
TriBegin( TRI_QUADS );
|
|
for( i = 0; i < 6; i++ )
|
|
{
|
|
TriBrightness( g_alias.shadelight / 255.0f );
|
|
TriVertex3fv( p[boxpnt[i][0]] );
|
|
TriVertex3fv( p[boxpnt[i][1]] );
|
|
TriVertex3fv( p[boxpnt[i][2]] );
|
|
TriVertex3fv( p[boxpnt[i][3]] );
|
|
}
|
|
TriEnd();
|
|
|
|
TriRenderMode( kRenderNormal );
|
|
}
|
|
|
|
static void R_AliasDrawLightTrace( cl_entity_t *e )
|
|
{
|
|
if( r_drawentities->value == 7 )
|
|
{
|
|
vec3_t origin;
|
|
|
|
pglDisable( GL_TEXTURE_2D );
|
|
pglDisable( GL_DEPTH_TEST );
|
|
|
|
pglBegin( GL_LINES );
|
|
pglColor3f( 1, 0.5, 0 );
|
|
pglVertex3fv( e->origin );
|
|
pglVertex3fv( g_alias.lightspot );
|
|
pglEnd();
|
|
|
|
pglBegin( GL_LINES );
|
|
pglColor3f( 0, 0.5, 1 );
|
|
VectorMA( g_alias.lightspot, -64.0f, g_alias.lightvec, origin );
|
|
pglVertex3fv( g_alias.lightspot );
|
|
pglVertex3fv( origin );
|
|
pglEnd();
|
|
|
|
pglPointSize( 5.0f );
|
|
pglColor3f( 1, 0, 0 );
|
|
pglBegin( GL_POINTS );
|
|
pglVertex3fv( g_alias.lightspot );
|
|
pglEnd();
|
|
pglPointSize( 1.0f );
|
|
|
|
pglEnable( GL_DEPTH_TEST );
|
|
pglEnable( GL_TEXTURE_2D );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_AliasSetupTimings
|
|
|
|
init current time for a given model
|
|
================
|
|
*/
|
|
static void R_AliasSetupTimings( void )
|
|
{
|
|
if( RI.drawWorld )
|
|
{
|
|
// synchronize with server time
|
|
g_alias.time = gpGlobals->time;
|
|
}
|
|
else
|
|
{
|
|
// menu stuff
|
|
g_alias.time = gpGlobals->realtime;
|
|
}
|
|
|
|
m_fDoRemap = false;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_DrawAliasModel
|
|
|
|
=================
|
|
*/
|
|
void R_DrawAliasModel( cl_entity_t *e )
|
|
{
|
|
model_t *clmodel;
|
|
vec3_t absmin, absmax;
|
|
remap_info_t *pinfo = NULL;
|
|
int anim, skin;
|
|
alight_t lighting;
|
|
player_info_t *playerinfo;
|
|
vec3_t dir, angles;
|
|
|
|
clmodel = RI.currententity->model;
|
|
|
|
VectorAdd( e->origin, clmodel->mins, absmin );
|
|
VectorAdd( e->origin, clmodel->maxs, absmax );
|
|
|
|
if( R_CullModel( e, absmin, absmax ))
|
|
return;
|
|
|
|
//
|
|
// locate the proper data
|
|
//
|
|
m_pAliasHeader = (aliashdr_t *)gEngfuncs.Mod_Extradata( mod_alias, RI.currententity->model );
|
|
if( !m_pAliasHeader ) return;
|
|
|
|
// init time
|
|
R_AliasSetupTimings();
|
|
|
|
// angles will be modify below keep original
|
|
VectorCopy( e->angles, angles );
|
|
|
|
R_AliasLerpMovement( e );
|
|
|
|
if( !FBitSet( ENGINE_GET_PARM( PARM_FEATURES ), ENGINE_COMPENSATE_QUAKE_BUG ))
|
|
e->angles[PITCH] = -e->angles[PITCH]; // stupid quake bug
|
|
|
|
// don't rotate clients, only aim
|
|
if( e->player ) e->angles[PITCH] = 0.0f;
|
|
|
|
//
|
|
// get lighting information
|
|
//
|
|
lighting.plightvec = dir;
|
|
R_AliasDynamicLight( e, &lighting );
|
|
|
|
r_stats.c_alias_polys += m_pAliasHeader->numtris;
|
|
r_stats.c_alias_models_drawn++;
|
|
|
|
//
|
|
// draw all the triangles
|
|
//
|
|
|
|
R_RotateForEntity( e );
|
|
|
|
// model and frame independant
|
|
R_AliasSetupLighting( &lighting );
|
|
GL_SetRenderMode( e->curstate.rendermode );
|
|
|
|
// setup remapping only for players
|
|
if( e->player && ( playerinfo = pfnPlayerInfo( e->curstate.number - 1 )) != NULL )
|
|
{
|
|
// get remap colors
|
|
int topcolor = bound( 0, playerinfo->topcolor, 13 );
|
|
int bottomcolor = bound( 0, playerinfo->bottomcolor, 13 );
|
|
R_AliasSetRemapColors( topcolor, bottomcolor );
|
|
}
|
|
|
|
if( tr.fFlipViewModel )
|
|
{
|
|
pglTranslatef( m_pAliasHeader->scale_origin[0], -m_pAliasHeader->scale_origin[1], m_pAliasHeader->scale_origin[2] );
|
|
pglScalef( m_pAliasHeader->scale[0], -m_pAliasHeader->scale[1], m_pAliasHeader->scale[2] );
|
|
}
|
|
else
|
|
{
|
|
pglTranslatef( m_pAliasHeader->scale_origin[0], m_pAliasHeader->scale_origin[1], m_pAliasHeader->scale_origin[2] );
|
|
pglScalef( m_pAliasHeader->scale[0], m_pAliasHeader->scale[1], m_pAliasHeader->scale[2] );
|
|
}
|
|
|
|
anim = (int)(g_alias.time * 10) & 3;
|
|
skin = bound( 0, RI.currententity->curstate.skin, m_pAliasHeader->numskins - 1 );
|
|
if( m_fDoRemap ) pinfo = gEngfuncs.CL_GetRemapInfoForEntity( e );
|
|
|
|
if( r_lightmap->value && !r_fullbright->value )
|
|
GL_Bind( XASH_TEXTURE0, tr.whiteTexture );
|
|
else if( pinfo != NULL && pinfo->textures[skin] != 0 )
|
|
GL_Bind( XASH_TEXTURE0, pinfo->textures[skin] ); // FIXME: allow remapping for skingroups someday
|
|
else
|
|
{
|
|
GL_Bind( XASH_TEXTURE0, m_pAliasHeader->gl_texturenum[skin][anim] );
|
|
|
|
if( FBitSet( R_GetTexture( m_pAliasHeader->gl_texturenum[skin][anim] )->flags, TF_HAS_ALPHA ))
|
|
{
|
|
pglEnable( GL_ALPHA_TEST );
|
|
pglAlphaFunc( GL_GREATER, 0.0f );
|
|
tr.blend = 1.0f;
|
|
}
|
|
}
|
|
|
|
pglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
|
|
|
|
if( GL_Support( GL_ARB_MULTITEXTURE ) && m_pAliasHeader->fb_texturenum[skin][anim] )
|
|
{
|
|
GL_Bind( XASH_TEXTURE1, m_pAliasHeader->fb_texturenum[skin][anim] );
|
|
pglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD );
|
|
}
|
|
|
|
pglShadeModel( GL_SMOOTH );
|
|
R_SetupAliasFrame( e, m_pAliasHeader );
|
|
|
|
if( GL_Support( GL_ARB_MULTITEXTURE ) && m_pAliasHeader->fb_texturenum[skin][anim] )
|
|
GL_CleanUpTextureUnits( 1 );
|
|
|
|
pglShadeModel( GL_FLAT );
|
|
R_LoadIdentity();
|
|
|
|
// get lerped origin
|
|
VectorAdd( e->origin, clmodel->mins, absmin );
|
|
VectorAdd( e->origin, clmodel->maxs, absmax );
|
|
|
|
R_AliasDrawAbsBBox( e, absmin, absmax );
|
|
R_AliasDrawLightTrace( e );
|
|
|
|
pglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
|
|
pglAlphaFunc( GL_GREATER, DEFAULT_ALPHATEST );
|
|
pglDisable( GL_ALPHA_TEST );
|
|
|
|
if( r_shadows.value )
|
|
{
|
|
// need to compute transformation matrix
|
|
Matrix4x4_CreateFromEntity( RI.objectMatrix, e->angles, e->origin, 1.0f );
|
|
pglDisable( GL_TEXTURE_2D );
|
|
pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
|
pglEnable( GL_BLEND );
|
|
pglColor4f( 0.0f, 0.0f, 0.0f, 0.5f );
|
|
pglDepthFunc( GL_LESS );
|
|
|
|
GL_DrawAliasShadow( m_pAliasHeader );
|
|
|
|
pglDepthFunc( GL_LEQUAL );
|
|
pglEnable( GL_TEXTURE_2D );
|
|
pglDisable( GL_BLEND );
|
|
pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
|
|
R_LoadIdentity();
|
|
}
|
|
|
|
// restore original angles
|
|
VectorCopy( angles, e->angles );
|
|
}
|
|
|
|
//==================================================================================
|