436 lines
11 KiB
436 lines
11 KiB
/* |
|
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( 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", refState.currententity->curstate.number, name + 1 ); |
|
|
|
// search for pixels |
|
for( i = 0; i < refState.currentmodel->numtextures; i++ ) |
|
{ |
|
tx = refState.currentmodel->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( 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, refState.currentmodel->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 < refState.currentmodel->numtextures; i++ ) |
|
{ |
|
tx = refState.currentmodel->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( unsigned short *texture, int skinnum, int topcolor, int bottomcolor ) |
|
{ |
|
char texname[MAX_QPATH]; |
|
rgbdata_t skin, *pic; |
|
texture_t *tx; |
|
|
|
if( !texture || !refState.currentmodel->textures ) |
|
return; // no remapinfo in model |
|
|
|
tx = refState.currentmodel->textures[skinnum]; |
|
if( !tx ) return; // missing texture ? |
|
|
|
if( *texture == 0 ) |
|
{ |
|
Q_snprintf( texname, sizeof( texname ), "%s:remap%i_%i", refState.currentmodel->name, skinnum, refState.currententity->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( int topcolor, int bottomcolor ) |
|
{ |
|
remap_info_t *info; |
|
studiohdr_t *phdr; |
|
aliashdr_t *ahdr; |
|
mstudiotexture_t *src, *dst; |
|
int i, size; |
|
|
|
if( !refState.currententity ) return; |
|
i = ( refState.currententity == &clgame.viewent ) ? clgame.maxEntities : refState.currententity->curstate.number; |
|
|
|
if( !refState.currentmodel || ( refState.currentmodel->type != mod_alias && refState.currentmodel->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( refState.currentmodel->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( refState.currentmodel->type == mod_studio ) |
|
{ |
|
phdr = (studiohdr_t *)Mod_StudioExtradata( refState.currentmodel ); |
|
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 != refState.currentmodel ) |
|
{ |
|
// 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( &dst[i], topcolor, bottomcolor ); |
|
} |
|
} |
|
else if( refState.currentmodel->type == mod_alias ) |
|
{ |
|
ahdr = (aliashdr_t *)Mod_AliasExtradata( refState.currentmodel ); |
|
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 != refState.currentmodel ) |
|
{ |
|
// 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 = refState.currentmodel->numtextures; |
|
|
|
// alias remapping is easy |
|
CL_UpdateRemapInfo( topcolor, bottomcolor ); |
|
} |
|
else |
|
{ |
|
// only alias & studio models are supposed for remapping |
|
return; |
|
} |
|
|
|
info->model = refState.currentmodel; |
|
} |
|
|
|
/* |
|
==================== |
|
CL_UpdateRemapInfo |
|
|
|
Update all remaps per entity |
|
==================== |
|
*/ |
|
void CL_UpdateRemapInfo( int topcolor, int bottomcolor ) |
|
{ |
|
remap_info_t *info; |
|
int i; |
|
|
|
i = ( refState.currententity == &clgame.viewent ) ? clgame.maxEntities : refState.currententity->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( &info->ptexture[i], topcolor, bottomcolor ); |
|
} |
|
else CL_UpdateAliasTexture( &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; |
|
}
|
|
|