mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 03:24:56 +08:00
361 lines
10 KiB
C
361 lines
10 KiB
C
/*
|
|
cl_sprite.c - sprite loading
|
|
Copyright (C) 2010 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 "common.h"
|
|
#include "client.h"
|
|
#include "sprite.h"
|
|
#include "ref_common.h"
|
|
|
|
static char sprite_name[MAX_QPATH];
|
|
static char group_suffix[8];
|
|
static uint r_texFlags = 0;
|
|
static int sprite_version;
|
|
|
|
/*
|
|
================
|
|
R_GetSpriteFrame
|
|
|
|
assume pModel is valid
|
|
================
|
|
*/
|
|
mspriteframe_t *R_GetSpriteFrame( const model_t *pModel, int frame, float yaw )
|
|
{
|
|
msprite_t *psprite;
|
|
mspritegroup_t *pspritegroup;
|
|
mspriteframe_t *pspriteframe = NULL;
|
|
float *pintervals, fullinterval;
|
|
int i, numframes;
|
|
float targettime;
|
|
|
|
if( !pModel )
|
|
return NULL;
|
|
|
|
psprite = pModel->cache.data;
|
|
|
|
if( frame < 0 )
|
|
{
|
|
frame = 0;
|
|
}
|
|
else if( frame >= psprite->numframes )
|
|
{
|
|
if( frame > psprite->numframes )
|
|
Con_Printf( S_WARN "%s: no such frame %d (%s)\n", __func__, frame, pModel->name );
|
|
frame = psprite->numframes - 1;
|
|
}
|
|
|
|
if( psprite->frames[frame].type == SPR_SINGLE )
|
|
{
|
|
pspriteframe = psprite->frames[frame].frameptr;
|
|
}
|
|
else if( psprite->frames[frame].type == SPR_GROUP )
|
|
{
|
|
pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
|
|
pintervals = pspritegroup->intervals;
|
|
numframes = pspritegroup->numframes;
|
|
fullinterval = pintervals[numframes - 1];
|
|
|
|
// when loading in Mod_LoadSpriteGroup, we guaranteed all interval values
|
|
// are positive, so we don't have to worry about division by zero
|
|
targettime = cl.time - ((int)( cl.time / fullinterval )) * fullinterval;
|
|
|
|
for( i = 0; i < ( numframes - 1 ); i++ )
|
|
{
|
|
if( pintervals[i] > targettime )
|
|
break;
|
|
}
|
|
pspriteframe = pspritegroup->frames[i];
|
|
}
|
|
else if( psprite->frames[frame].type == FRAME_ANGLED )
|
|
{
|
|
int angleframe = (int)( Q_rint(( refState.viewangles[1] - yaw + 45.0f ) / 360 * 8 ) - 4 ) & 7;
|
|
|
|
// e.g. doom-style sprite monsters
|
|
pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
|
|
pspriteframe = pspritegroup->frames[angleframe];
|
|
}
|
|
|
|
return pspriteframe;
|
|
}
|
|
|
|
void R_GetSpriteParms( int *frameWidth, int *frameHeight, int *numFrames, int currentFrame, const model_t *pSprite )
|
|
{
|
|
mspriteframe_t *pFrame;
|
|
|
|
if( !pSprite || pSprite->type != mod_sprite )
|
|
return;
|
|
|
|
pFrame = R_GetSpriteFrame( pSprite, currentFrame, 0.0f );
|
|
|
|
if( frameWidth )
|
|
*frameWidth = pFrame->width;
|
|
if( frameHeight )
|
|
*frameHeight = pFrame->height;
|
|
if( numFrames )
|
|
*numFrames = pSprite->numframes;
|
|
}
|
|
|
|
int R_GetSpriteTexture( const model_t *m_pSpriteModel, int frame )
|
|
{
|
|
if( !m_pSpriteModel || m_pSpriteModel->type != mod_sprite || !m_pSpriteModel->cache.data )
|
|
return 0;
|
|
|
|
return R_GetSpriteFrame( m_pSpriteModel, frame, 0.0f )->gl_texturenum;
|
|
}
|
|
|
|
static void Mod_SpriteTextureReplacementReport( const char *modelname, int gl_texturenum, const char *foundpath )
|
|
{
|
|
if( host_allow_materials.value != 2.0f )
|
|
return;
|
|
|
|
if( gl_texturenum > 0 )
|
|
Con_Printf( "Looking for %s tex replacement..." S_GREEN "OK (%s)\n", modelname, foundpath );
|
|
else if( gl_texturenum < 0 )
|
|
Con_Printf( "Looking for %s tex replacement..." S_YELLOW "MISS (%s)\n", modelname, foundpath );
|
|
else
|
|
Con_Printf( "Looking for %s tex replacement..." S_RED "FAIL (%s)\n", modelname, foundpath );
|
|
}
|
|
|
|
static qboolean Mod_SpriteSearchForTextureReplacement( char *out, size_t size, const char *modelname, const char *fmt, ... )
|
|
{
|
|
va_list ap;
|
|
int ret;
|
|
|
|
va_start( ap, fmt );
|
|
ret = Q_vsnprintf( out, size, fmt, ap );
|
|
va_end( ap );
|
|
|
|
if( ret < 0 )
|
|
{
|
|
Mod_SpriteTextureReplacementReport( modelname, -1, "overflow" );
|
|
return false;
|
|
}
|
|
|
|
if( FS_FileExists( out, false ))
|
|
return true;
|
|
|
|
Mod_SpriteTextureReplacementReport( modelname, -1, out );
|
|
return false;
|
|
}
|
|
|
|
static const byte *Mod_SpriteLoadFrame( model_t *mod, const void *pin, mspriteframe_t **ppframe, int num )
|
|
{
|
|
dspriteframe_t pinframe;
|
|
mspriteframe_t *pspriteframe;
|
|
int gl_texturenum = 0;
|
|
char texname[128];
|
|
int bytes = 1;
|
|
|
|
memcpy( &pinframe, pin, sizeof( dspriteframe_t ));
|
|
|
|
if( sprite_version == SPRITE_VERSION_32 )
|
|
bytes = 4;
|
|
|
|
// build unique frame name
|
|
if( FBitSet( mod->flags, MODEL_CLIENT )) // it's a HUD sprite
|
|
{
|
|
Q_snprintf( texname, sizeof( texname ), "#HUD/%s(%s:%i%i).spr", sprite_name, group_suffix, num / 10, num % 10 );
|
|
gl_texturenum = ref.dllFuncs.GL_LoadTexture( texname, pin, pinframe.width * pinframe.height * bytes, r_texFlags );
|
|
}
|
|
else
|
|
{
|
|
// partial HD-textures support
|
|
if( Mod_AllowMaterials( ))
|
|
{
|
|
if( Mod_SpriteSearchForTextureReplacement( texname, sizeof( texname ), sprite_name, "materials/%s/%s%i%i.tga", sprite_name, group_suffix, num / 10, num % 10 ))
|
|
{
|
|
gl_texturenum = ref.dllFuncs.GL_LoadTexture( texname, NULL, 0, r_texFlags );
|
|
Mod_SpriteTextureReplacementReport( sprite_name, gl_texturenum, texname );
|
|
}
|
|
}
|
|
|
|
if( gl_texturenum == 0 )
|
|
{
|
|
Q_snprintf( texname, sizeof( texname ), "#%s(%s:%i%i).spr", sprite_name, group_suffix, num / 10, num % 10 );
|
|
gl_texturenum = ref.dllFuncs.GL_LoadTexture( texname, pin, pinframe.width * pinframe.height * bytes, r_texFlags );
|
|
}
|
|
}
|
|
|
|
// setup frame description
|
|
pspriteframe = Mem_Malloc( mod->mempool, sizeof( mspriteframe_t ));
|
|
pspriteframe->width = pinframe.width;
|
|
pspriteframe->height = pinframe.height;
|
|
pspriteframe->up = pinframe.origin[1];
|
|
pspriteframe->left = pinframe.origin[0];
|
|
pspriteframe->down = pinframe.origin[1] - pinframe.height;
|
|
pspriteframe->right = pinframe.width + pinframe.origin[0];
|
|
pspriteframe->gl_texturenum = gl_texturenum;
|
|
*ppframe = pspriteframe;
|
|
|
|
return (const byte *)pin + sizeof( dspriteframe_t ) + pinframe.width * pinframe.height * bytes;
|
|
}
|
|
|
|
static const byte *Mod_SpriteLoadGroup( model_t *mod, const void *pin, mspriteframe_t **ppframe, int framenum )
|
|
{
|
|
const dspritegroup_t *pingroup;
|
|
mspritegroup_t *pspritegroup;
|
|
const dspriteinterval_t *pin_intervals;
|
|
float *poutintervals;
|
|
int i, groupsize, numframes;
|
|
const void *ptemp;
|
|
|
|
pingroup = (const dspritegroup_t *)pin;
|
|
numframes = pingroup->numframes;
|
|
|
|
groupsize = sizeof( mspritegroup_t ) + ( numframes - 1 ) * sizeof( pspritegroup->frames[0] );
|
|
pspritegroup = Mem_Calloc( mod->mempool, groupsize );
|
|
pspritegroup->numframes = numframes;
|
|
|
|
*ppframe = (mspriteframe_t *)pspritegroup;
|
|
pin_intervals = (const dspriteinterval_t *)( pingroup + 1 );
|
|
poutintervals = Mem_Calloc( mod->mempool, numframes * sizeof( float ));
|
|
pspritegroup->intervals = poutintervals;
|
|
|
|
for( i = 0; i < numframes; i++ )
|
|
{
|
|
*poutintervals = pin_intervals->interval;
|
|
if( *poutintervals <= 0.0f )
|
|
*poutintervals = 1.0f; // set error value
|
|
poutintervals++;
|
|
pin_intervals++;
|
|
}
|
|
|
|
ptemp = (const void *)pin_intervals;
|
|
for( i = 0; i < numframes; i++ )
|
|
{
|
|
ptemp = Mod_SpriteLoadFrame( mod, ptemp, &pspritegroup->frames[i], framenum * 10 + i );
|
|
}
|
|
|
|
return ptemp;
|
|
}
|
|
|
|
void Mod_SpriteLoadTextures( model_t *mod, const void *buffer )
|
|
{
|
|
const dsprite_t *pin = buffer;
|
|
const short *numi = NULL;
|
|
const byte *pframetype;
|
|
msprite_t *psprite = mod->cache.data;
|
|
int i;
|
|
|
|
if( pin->version == SPRITE_VERSION_Q1 || pin->version == SPRITE_VERSION_32 )
|
|
numi = NULL;
|
|
else if( pin->version == SPRITE_VERSION_HL )
|
|
numi = (const short *)((const byte *)buffer + sizeof( dsprite_hl_t ));
|
|
|
|
r_texFlags = mod->numtexinfo;
|
|
sprite_version = pin->version;
|
|
Q_strncpy( sprite_name, mod->name, sizeof( sprite_name ));
|
|
COM_StripExtension( sprite_name );
|
|
|
|
if( numi == NULL )
|
|
{
|
|
rgbdata_t *pal;
|
|
|
|
pal = FS_LoadImage( "#id.pal", (byte *)&i, 768 );
|
|
pframetype = (const byte *)buffer + sizeof( dsprite_q1_t );
|
|
FS_FreeImage( pal );
|
|
}
|
|
else if( *numi <= 256 )
|
|
{
|
|
const byte *src = (const byte *)( numi + 1 );
|
|
rgbdata_t *pal;
|
|
size_t pal_bytes = *numi * 3;
|
|
|
|
// install palette
|
|
switch( psprite->texFormat )
|
|
{
|
|
case SPR_INDEXALPHA:
|
|
pal = FS_LoadImage( "#gradient.pal", src, pal_bytes );
|
|
break;
|
|
case SPR_ALPHTEST:
|
|
pal = FS_LoadImage( "#masked.pal", src, pal_bytes );
|
|
break;
|
|
default:
|
|
pal = FS_LoadImage( "#texgamma.pal", src, pal_bytes );
|
|
break;
|
|
}
|
|
|
|
pframetype = (const byte *)( src + pal_bytes );
|
|
FS_FreeImage( pal );
|
|
}
|
|
else
|
|
{
|
|
Con_DPrintf( S_ERROR "%s has wrong number of palette colors %i (should be less or equal than 256)\n", mod->name, *numi );
|
|
return;
|
|
}
|
|
|
|
if( mod->numframes < 1 )
|
|
return;
|
|
|
|
for( i = 0; i < mod->numframes; i++ )
|
|
{
|
|
frametype_t frametype;
|
|
dframetype_t dframetype;
|
|
|
|
memcpy( &dframetype, pframetype, sizeof( dframetype ));
|
|
frametype = dframetype.type;
|
|
psprite->frames[i].type = (spriteframetype_t)frametype;
|
|
|
|
switch( frametype )
|
|
{
|
|
case FRAME_SINGLE:
|
|
Q_strncpy( group_suffix, "frame", sizeof( group_suffix ));
|
|
pframetype = Mod_SpriteLoadFrame( mod, pframetype + sizeof( dframetype_t ), &psprite->frames[i].frameptr, i );
|
|
break;
|
|
case FRAME_GROUP:
|
|
Q_strncpy( group_suffix, "group", sizeof( group_suffix ));
|
|
pframetype = Mod_SpriteLoadGroup( mod, pframetype + sizeof( dframetype_t ), &psprite->frames[i].frameptr, i );
|
|
break;
|
|
case FRAME_ANGLED:
|
|
Q_strncpy( group_suffix, "angle", sizeof( group_suffix ));
|
|
pframetype = Mod_SpriteLoadGroup( mod, pframetype + sizeof( dframetype_t ), &psprite->frames[i].frameptr, i );
|
|
break;
|
|
}
|
|
if( pframetype == NULL )
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Mod_SpriteUnloadTextures( void *data )
|
|
{
|
|
msprite_t *psprite = data;
|
|
int i;
|
|
|
|
if( !data )
|
|
return;
|
|
|
|
// release all textures
|
|
for( i = 0; i < psprite->numframes; i++ )
|
|
{
|
|
if( !psprite->frames[i].frameptr )
|
|
continue;
|
|
|
|
if( psprite->frames[i].type == SPR_SINGLE )
|
|
{
|
|
ref.dllFuncs.GL_FreeTexture( psprite->frames[i].frameptr->gl_texturenum );
|
|
}
|
|
else
|
|
{
|
|
mspritegroup_t *pspritegroup = (mspritegroup_t *)psprite->frames[i].frameptr;
|
|
int j;
|
|
|
|
for( j = 0; j < pspritegroup->numframes; j++ )
|
|
{
|
|
if( pspritegroup->frames[j] )
|
|
ref.dllFuncs.GL_FreeTexture( pspritegroup->frames[j]->gl_texturenum );
|
|
}
|
|
}
|
|
}
|
|
}
|