mirror of
https://github.com/w23/xash3d-fwgs
synced 2024-12-17 06:30:44 +01: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:]]\+$//' {} \+ ```
437 lines
11 KiB
C
437 lines
11 KiB
C
/*
|
|
gl_remap.c - remap model textures
|
|
Copyright (C) 2011 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 "studio.h"
|
|
|
|
/*
|
|
====================
|
|
CL_GetRemapInfoForEntity
|
|
|
|
Returns remapinfo slot for specified entity
|
|
====================
|
|
*/
|
|
remap_info_t *CL_GetRemapInfoForEntity( cl_entity_t *e )
|
|
{
|
|
if( !e ) return NULL;
|
|
|
|
if( e == &clgame.viewent )
|
|
return clgame.remap_info[clgame.maxEntities];
|
|
|
|
return clgame.remap_info[e->curstate.number];
|
|
}
|
|
|
|
/*
|
|
====================
|
|
CL_CmpStudioTextures
|
|
|
|
return true if equal
|
|
====================
|
|
*/
|
|
qboolean CL_CmpStudioTextures( int numtexs, mstudiotexture_t *p1, mstudiotexture_t *p2 )
|
|
{
|
|
int i;
|
|
|
|
if( !p1 || !p2 ) return false;
|
|
|
|
for( i = 0; i < numtexs; i++, p1++, p2++ )
|
|
{
|
|
if( p1->flags & STUDIO_NF_COLORMAP )
|
|
continue; // colormaps always has different indexes
|
|
|
|
if( p1->index != p2->index )
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
====================
|
|
CL_CreateRawTextureFromPixels
|
|
|
|
Convert texture_t struct into mstudiotexture_t prototype
|
|
====================
|
|
*/
|
|
byte *CL_CreateRawTextureFromPixels( texture_t *tx, size_t *size, int topcolor, int bottomcolor )
|
|
{
|
|
static mstudiotexture_t pin;
|
|
byte *pal;
|
|
|
|
Assert( size != NULL );
|
|
|
|
*size = sizeof( pin ) + (tx->width * tx->height) + 768;
|
|
|
|
// fill header
|
|
if( !pin.name[0] ) Q_strncpy( pin.name, "#raw_remap_image.mdl", sizeof( pin.name ));
|
|
pin.flags = STUDIO_NF_COLORMAP; // just in case :-)
|
|
//pin.index = (int)(tx + 1); // pointer to pixels
|
|
// no more pointer-to-int-to-pointer casts
|
|
Image_SetMDLPointer( (byte*)((texture_t *)tx + 1) );
|
|
pin.width = tx->width;
|
|
pin.height = tx->height;
|
|
|
|
// update palette
|
|
pal = (byte *)(tx + 1) + (tx->width * tx->height);
|
|
Image_PaletteHueReplace( pal, topcolor, tx->anim_min, tx->anim_max, 3 );
|
|
Image_PaletteHueReplace( pal, bottomcolor, tx->anim_max + 1, tx->anim_total, 3 );
|
|
|
|
return (byte *)&pin;
|
|
}
|
|
|
|
/*
|
|
====================
|
|
CL_DuplicateTexture
|
|
|
|
Dupliacte texture with remap pixels
|
|
====================
|
|
*/
|
|
void CL_DuplicateTexture( cl_entity_t *entity, mstudiotexture_t *ptexture, int topcolor, int bottomcolor )
|
|
{
|
|
const char *name;
|
|
texture_t *tx = NULL;
|
|
char texname[128];
|
|
int i, index;
|
|
size_t size;
|
|
byte paletteBackup[768];
|
|
byte *raw, *pal;
|
|
|
|
// save off the real texture index
|
|
index = ptexture->index;
|
|
name = ref.dllFuncs.GL_TextureName( index );
|
|
Q_snprintf( texname, sizeof( texname ), "#%i_%s", entity->curstate.number, name + 1 );
|
|
|
|
// search for pixels
|
|
for( i = 0; i < entity->model->numtextures; i++ )
|
|
{
|
|
tx = entity->model->textures[i];
|
|
if( tx->gl_texturenum == index )
|
|
break; // found
|
|
}
|
|
|
|
Assert( tx != NULL );
|
|
|
|
// backup original palette
|
|
pal = (byte *)(tx + 1) + (tx->width * tx->height);
|
|
memcpy( paletteBackup, pal, 768 );
|
|
|
|
raw = CL_CreateRawTextureFromPixels( tx, &size, topcolor, bottomcolor );
|
|
ptexture->index = ref.dllFuncs.GL_LoadTexture( texname, raw, size, TF_FORCE_COLOR ); // do copy
|
|
|
|
// restore original palette
|
|
memcpy( pal, paletteBackup, 768 );
|
|
}
|
|
|
|
/*
|
|
====================
|
|
CL_UpdateStudioTexture
|
|
|
|
Update texture top and bottom colors
|
|
====================
|
|
*/
|
|
void CL_UpdateStudioTexture( cl_entity_t *entity, mstudiotexture_t *ptexture, int topcolor, int bottomcolor )
|
|
{
|
|
rgbdata_t *pic;
|
|
texture_t *tx = NULL;
|
|
char texname[128], name[128], mdlname[128];
|
|
const char *origtexname;
|
|
int i, index;
|
|
size_t size;
|
|
byte paletteBackup[768];
|
|
byte *raw, *pal;
|
|
|
|
// save off the real texture index
|
|
origtexname = ref.dllFuncs.GL_TextureName( ptexture->index );
|
|
|
|
// build name of original texture
|
|
Q_strncpy( mdlname, entity->model->name, sizeof( mdlname ));
|
|
COM_FileBase( ptexture->name, name );
|
|
COM_StripExtension( mdlname );
|
|
|
|
Q_snprintf( texname, sizeof( texname ), "#%s/%s.mdl", mdlname, name );
|
|
index = ref.dllFuncs.GL_FindTexture( texname );
|
|
if( !index ) return; // couldn't find texture
|
|
|
|
// search for pixels
|
|
for( i = 0; i < entity->model->numtextures; i++ )
|
|
{
|
|
tx = entity->model->textures[i];
|
|
if( tx->gl_texturenum == index )
|
|
break; // found
|
|
}
|
|
|
|
Assert( tx != NULL );
|
|
|
|
// backup original palette
|
|
pal = (byte *)(tx + 1) + (tx->width * tx->height);
|
|
memcpy( paletteBackup, pal, 768 );
|
|
|
|
raw = CL_CreateRawTextureFromPixels( tx, &size, topcolor, bottomcolor );
|
|
pic = FS_LoadImage( origtexname, raw, size );
|
|
if( !pic )
|
|
{
|
|
Con_DPrintf( S_ERROR "Couldn't update texture %s\n", origtexname );
|
|
return;
|
|
}
|
|
|
|
index = GL_UpdateTextureInternal( origtexname, pic, 0 );
|
|
FS_FreeImage( pic );
|
|
|
|
// restore original palette
|
|
memcpy( pal, paletteBackup, 768 );
|
|
|
|
Assert( index == ptexture->index );
|
|
}
|
|
|
|
/*
|
|
====================
|
|
CL_UpdateAliasTexture
|
|
|
|
Update texture top and bottom colors
|
|
====================
|
|
*/
|
|
void CL_UpdateAliasTexture( cl_entity_t *entity, unsigned short *texture, int skinnum, int topcolor, int bottomcolor )
|
|
{
|
|
char texname[MAX_QPATH];
|
|
rgbdata_t skin, *pic;
|
|
texture_t *tx;
|
|
|
|
if( !texture || !entity->model->textures )
|
|
return; // no remapinfo in model
|
|
|
|
tx = entity->model->textures[skinnum];
|
|
if( !tx ) return; // missing texture ?
|
|
|
|
if( *texture == 0 )
|
|
{
|
|
Q_snprintf( texname, sizeof( texname ), "%s:remap%i_%i", entity->model->name, skinnum, entity->index );
|
|
skin.width = tx->width;
|
|
skin.height = tx->height;
|
|
skin.depth = skin.numMips = 1;
|
|
skin.size = tx->width * tx->height;
|
|
skin.type = PF_INDEXED_24;
|
|
skin.flags = IMAGE_HAS_COLOR|IMAGE_QUAKEPAL;
|
|
skin.encode = DXT_ENCODE_DEFAULT;
|
|
skin.buffer = (byte *)(tx + 1);
|
|
skin.palette = skin.buffer + skin.size;
|
|
pic = FS_CopyImage( &skin ); // because GL_LoadTextureInternal will freed a rgbdata_t at end
|
|
*texture = GL_LoadTextureInternal( texname, pic, TF_KEEP_SOURCE );
|
|
}
|
|
|
|
// and now we can remap with internal routines
|
|
ref.dllFuncs.GL_ProcessTexture( *texture, -1.0f, topcolor, bottomcolor );
|
|
}
|
|
|
|
/*
|
|
====================
|
|
CL_AllocRemapInfo
|
|
|
|
Allocate new remap info per entity
|
|
and make copy of remap textures
|
|
====================
|
|
*/
|
|
void CL_AllocRemapInfo( cl_entity_t *entity, int topcolor, int bottomcolor )
|
|
{
|
|
remap_info_t *info;
|
|
studiohdr_t *phdr;
|
|
aliashdr_t *ahdr;
|
|
mstudiotexture_t *src, *dst;
|
|
int i, size;
|
|
|
|
if( !entity ) return;
|
|
i = ( entity == &clgame.viewent ) ? clgame.maxEntities : entity->curstate.number;
|
|
|
|
if( !entity->model || ( entity->model->type != mod_alias && entity->model->type != mod_studio ))
|
|
{
|
|
// entity has changed model by another type, release remap info
|
|
if( clgame.remap_info[i] )
|
|
{
|
|
CL_FreeRemapInfo( clgame.remap_info[i] );
|
|
clgame.remap_info[i] = NULL;
|
|
}
|
|
return; // missed or hide model, ignore it
|
|
}
|
|
|
|
// model doesn't contains remap textures
|
|
if( entity->model->numtextures <= 0 )
|
|
{
|
|
// entity has changed model with no remap textures
|
|
if( clgame.remap_info[i] )
|
|
{
|
|
CL_FreeRemapInfo( clgame.remap_info[i] );
|
|
clgame.remap_info[i] = NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if( entity->model->type == mod_studio )
|
|
{
|
|
phdr = (studiohdr_t *)Mod_StudioExtradata( entity->model );
|
|
if( !phdr ) return; // bad model?
|
|
|
|
src = (mstudiotexture_t *)(((byte *)phdr) + phdr->textureindex);
|
|
dst = (clgame.remap_info[i] ? clgame.remap_info[i]->ptexture : NULL);
|
|
|
|
// NOTE: we must copy all the structures 'mstudiotexture_t' for easy access when model is rendering
|
|
if( !CL_CmpStudioTextures( phdr->numtextures, src, dst ) || clgame.remap_info[i]->model != entity->model )
|
|
{
|
|
// this code catches studiomodel change with another studiomodel with remap textures
|
|
// e.g. playermodel 'barney' with playermodel 'gordon'
|
|
if( clgame.remap_info[i] ) CL_FreeRemapInfo( clgame.remap_info[i] ); // free old info
|
|
size = sizeof( remap_info_t ) + ( sizeof( mstudiotexture_t ) * phdr->numtextures );
|
|
info = clgame.remap_info[i] = Mem_Calloc( clgame.mempool, size );
|
|
info->ptexture = (mstudiotexture_t *)(info + 1); // textures are immediately comes after remap_info
|
|
}
|
|
else
|
|
{
|
|
// studiomodel is valid, nothing to change
|
|
return;
|
|
}
|
|
|
|
info->numtextures = phdr->numtextures;
|
|
info->topcolor = topcolor;
|
|
info->bottomcolor = bottomcolor;
|
|
|
|
src = (mstudiotexture_t *)(((byte *)phdr) + phdr->textureindex);
|
|
dst = info->ptexture;
|
|
|
|
// copy unchanged first
|
|
memcpy( dst, src, sizeof( mstudiotexture_t ) * phdr->numtextures );
|
|
|
|
// make local copies for remap textures
|
|
for( i = 0; i < info->numtextures; i++ )
|
|
{
|
|
if( dst[i].flags & STUDIO_NF_COLORMAP )
|
|
CL_DuplicateTexture( entity, &dst[i], topcolor, bottomcolor );
|
|
}
|
|
}
|
|
else if( entity->model->type == mod_alias )
|
|
{
|
|
ahdr = (aliashdr_t *)Mod_AliasExtradata( entity->model );
|
|
if( !ahdr ) return; // bad model?
|
|
|
|
// NOTE: we must copy all the structures 'mstudiotexture_t' for easy access when model is rendering
|
|
if( !clgame.remap_info[i] || clgame.remap_info[i]->model != entity->model )
|
|
{
|
|
// this code catches studiomodel change with another studiomodel with remap textures
|
|
// e.g. playermodel 'barney' with playermodel 'gordon'
|
|
if( clgame.remap_info[i] ) CL_FreeRemapInfo( clgame.remap_info[i] ); // free old info
|
|
info = clgame.remap_info[i] = Mem_Calloc( clgame.mempool, sizeof( remap_info_t ));
|
|
}
|
|
else
|
|
{
|
|
// aliasmodel is valid, nothing to change
|
|
return;
|
|
}
|
|
|
|
info->numtextures = entity->model->numtextures;
|
|
|
|
// alias remapping is easy
|
|
CL_UpdateRemapInfo( entity, topcolor, bottomcolor );
|
|
}
|
|
else
|
|
{
|
|
// only alias & studio models are supposed for remapping
|
|
return;
|
|
}
|
|
|
|
info->model = entity->model;
|
|
}
|
|
|
|
/*
|
|
====================
|
|
CL_UpdateRemapInfo
|
|
|
|
Update all remaps per entity
|
|
====================
|
|
*/
|
|
void CL_UpdateRemapInfo( cl_entity_t *entity, int topcolor, int bottomcolor )
|
|
{
|
|
remap_info_t *info;
|
|
int i;
|
|
|
|
i = ( entity == &clgame.viewent ) ? clgame.maxEntities : entity->curstate.number;
|
|
info = clgame.remap_info[i];
|
|
if( !info ) return; // no remap info
|
|
|
|
if( info->topcolor == topcolor && info->bottomcolor == bottomcolor )
|
|
return; // values is valid
|
|
|
|
for( i = 0; i < info->numtextures; i++ )
|
|
{
|
|
if( info->ptexture != NULL )
|
|
{
|
|
if( FBitSet( info->ptexture[i].flags, STUDIO_NF_COLORMAP ))
|
|
CL_UpdateStudioTexture( entity, &info->ptexture[i], topcolor, bottomcolor );
|
|
}
|
|
else CL_UpdateAliasTexture( entity, &info->textures[i], i, topcolor, bottomcolor );
|
|
}
|
|
|
|
info->topcolor = topcolor;
|
|
info->bottomcolor = bottomcolor;
|
|
}
|
|
|
|
/*
|
|
====================
|
|
CL_FreeRemapInfo
|
|
|
|
Release remap info per entity
|
|
====================
|
|
*/
|
|
void CL_FreeRemapInfo( remap_info_t *info )
|
|
{
|
|
int i;
|
|
|
|
Assert( info != NULL );
|
|
|
|
// release all colormap texture copies
|
|
for( i = 0; i < info->numtextures; i++ )
|
|
{
|
|
if( info->ptexture != NULL )
|
|
{
|
|
if( FBitSet( info->ptexture[i].flags, STUDIO_NF_COLORMAP ))
|
|
ref.dllFuncs.GL_FreeTexture( info->ptexture[i].index );
|
|
}
|
|
|
|
if( info->textures[i] != 0 )
|
|
ref.dllFuncs.GL_FreeTexture( info->textures[i] );
|
|
}
|
|
|
|
Mem_Free( info ); // release struct
|
|
}
|
|
|
|
/*
|
|
====================
|
|
CL_ClearAllRemaps
|
|
|
|
Release all remap infos
|
|
====================
|
|
*/
|
|
void CL_ClearAllRemaps( void )
|
|
{
|
|
int i;
|
|
|
|
if( clgame.remap_info )
|
|
{
|
|
for( i = 0; i < clgame.maxRemapInfos; i++ )
|
|
{
|
|
if( clgame.remap_info[i] )
|
|
CL_FreeRemapInfo( clgame.remap_info[i] );
|
|
}
|
|
Mem_Free( clgame.remap_info );
|
|
}
|
|
clgame.remap_info = NULL;
|
|
}
|