/* 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; }