jkxr/Projects/Android/jni/OpenJK/codemp/rd-vanilla/tr_skin.cpp

433 lines
9.5 KiB
C++

/*
===========================================================================
Copyright (C) 2000 - 2013, Raven Software, Inc.
Copyright (C) 2001 - 2013, Activision, Inc.
Copyright (C) 2013 - 2015, OpenJK contributors
This file is part of the OpenJK source code.
OpenJK is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/
#include "tr_local.h"
/*
============================================================================
SKINS
============================================================================
*/
static char *CommaParse( char **data_p );
//can't be dec'd here since we need it for non-dedicated builds now as well.
/*
===============
RE_RegisterSkin
===============
*/
bool gServerSkinHack = false;
shader_t *R_FindServerShader( const char *name, const int *lightmapIndex, const byte *styles, qboolean mipRawImage );
static char *CommaParse( char **data_p );
/*
===============
RE_SplitSkins
input = skinname, possibly being a macro for three skins
return= true if three part skins found
output= qualified names to three skins if return is true, undefined if false
===============
*/
bool RE_SplitSkins(const char *INname, char *skinhead, char *skintorso, char *skinlower)
{ //INname= "models/players/jedi_tf/|head01_skin1|torso01|lower01";
if (strchr(INname, '|'))
{
char name[MAX_QPATH];
strcpy(name, INname);
char *p = strchr(name, '|');
*p=0;
p++;
//fill in the base path
strcpy (skinhead, name);
strcpy (skintorso, name);
strcpy (skinlower, name);
//now get the the individual files
//advance to second
char *p2 = strchr(p, '|');
if (!p2)
{
return false;
}
*p2=0;
p2++;
strcat (skinhead, p);
strcat (skinhead, ".skin");
//advance to third
p = strchr(p2, '|');
if (!p)
{
return false;
}
*p=0;
p++;
strcat (skintorso,p2);
strcat (skintorso, ".skin");
strcat (skinlower,p);
strcat (skinlower, ".skin");
return true;
}
return false;
}
// given a name, go get the skin we want and return
qhandle_t RE_RegisterIndividualSkin( const char *name , qhandle_t hSkin)
{
skin_t *skin;
skinSurface_t *surf;
char *text, *text_p;
char *token;
char surfName[MAX_QPATH];
// load and parse the skin file
ri.FS_ReadFile( name, (void **)&text );
if ( !text ) {
#ifndef FINAL_BUILD
ri.Printf( PRINT_ALL, "WARNING: RE_RegisterSkin( '%s' ) failed to load!\n", name );
#endif
return 0;
}
assert (tr.skins[hSkin]); //should already be setup, but might be an 3part append
skin = tr.skins[hSkin];
text_p = text;
while ( text_p && *text_p ) {
// get surface name
token = CommaParse( &text_p );
Q_strncpyz( surfName, token, sizeof( surfName ) );
if ( !token[0] ) {
break;
}
// lowercase the surface name so skin compares are faster
Q_strlwr( surfName );
if ( *text_p == ',' ) {
text_p++;
}
if ( !strncmp( token, "tag_", 4 ) ) { //these aren't in there, but just in case you load an id style one...
continue;
}
// parse the shader name
token = CommaParse( &text_p );
if ( !strcmp( &surfName[strlen(surfName)-4], "_off") )
{
if ( !strcmp( token ,"*off" ) )
{
continue; //don't need these double offs
}
surfName[strlen(surfName)-4] = 0; //remove the "_off"
}
if ( (unsigned)skin->numSurfaces >= ARRAY_LEN( skin->surfaces ) )
{
assert( ARRAY_LEN( skin->surfaces ) > (unsigned)skin->numSurfaces );
ri.Printf( PRINT_ALL, "WARNING: RE_RegisterSkin( '%s' ) more than %u surfaces!\n", name, (unsigned int )ARRAY_LEN( skin->surfaces ) );
break;
}
surf = (skinSurface_t *) Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low );
skin->surfaces[skin->numSurfaces] = (_skinSurface_t *)surf;
Q_strncpyz( surf->name, surfName, sizeof( surf->name ) );
if (gServerSkinHack) surf->shader = R_FindServerShader( token, lightmapsNone, stylesDefault, qtrue );
else surf->shader = R_FindShader( token, lightmapsNone, stylesDefault, qtrue );
skin->numSurfaces++;
}
ri.FS_FreeFile( text );
// never let a skin have 0 shaders
if ( skin->numSurfaces == 0 ) {
return 0; // use default skin
}
return hSkin;
}
qhandle_t RE_RegisterSkin( const char *name ) {
qhandle_t hSkin;
skin_t *skin;
if ( !name || !name[0] ) {
ri.Printf( PRINT_ALL, "Empty name passed to RE_RegisterSkin\n" );
return 0;
}
if ( strlen( name ) >= MAX_QPATH ) {
ri.Printf( PRINT_ALL, "Skin name exceeds MAX_QPATH\n" );
return 0;
}
// see if the skin is already loaded
for ( hSkin = 1; hSkin < tr.numSkins ; hSkin++ ) {
skin = tr.skins[hSkin];
if ( !Q_stricmp( skin->name, name ) ) {
if( skin->numSurfaces == 0 ) {
return 0; // default skin
}
return hSkin;
}
}
// allocate a new skin
if ( tr.numSkins == MAX_SKINS ) {
ri.Printf( PRINT_ALL, "WARNING: RE_RegisterSkin( '%s' ) MAX_SKINS hit\n", name );
return 0;
}
tr.numSkins++;
skin = (struct skin_s *)Hunk_Alloc( sizeof( skin_t ), h_low );
tr.skins[hSkin] = skin;
Q_strncpyz( skin->name, name, sizeof( skin->name ) );
skin->numSurfaces = 0;
// make sure the render thread is stopped
R_IssuePendingRenderCommands();
// If not a .skin file, load as a single shader
if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) {
/* skin->numSurfaces = 1;
skin->surfaces[0] = (skinSurface_t *)Hunk_Alloc( sizeof(skin->surfaces[0]), h_low );
skin->surfaces[0]->shader = R_FindShader( name, lightmapsNone, stylesDefault, qtrue );
return hSkin;
*/
}
char skinhead[MAX_QPATH]={0};
char skintorso[MAX_QPATH]={0};
char skinlower[MAX_QPATH]={0};
if ( RE_SplitSkins(name, (char*)&skinhead, (char*)&skintorso, (char*)&skinlower ) )
{//three part
hSkin = RE_RegisterIndividualSkin(skinhead, hSkin);
if (hSkin && strcmp(skinhead, skintorso))
{
hSkin = RE_RegisterIndividualSkin(skintorso, hSkin);
}
if (hSkin && strcmp(skinhead, skinlower) && strcmp(skintorso, skinlower))
{
hSkin = RE_RegisterIndividualSkin(skinlower, hSkin);
}
}
else
{//single skin
hSkin = RE_RegisterIndividualSkin(name, hSkin);
}
return(hSkin);
}
/*
==================
CommaParse
This is unfortunate, but the skin files aren't
compatible with our normal parsing rules.
==================
*/
static char *CommaParse( char **data_p ) {
int c = 0, len;
char *data;
static char com_token[MAX_TOKEN_CHARS];
data = *data_p;
len = 0;
com_token[0] = 0;
// make sure incoming data is valid
if ( !data ) {
*data_p = NULL;
return com_token;
}
while ( 1 ) {
// skip whitespace
while( (c = *(const unsigned char* /*eurofix*/)data) <= ' ') {
if( !c ) {
break;
}
data++;
}
c = *data;
// skip double slash comments
if ( c == '/' && data[1] == '/' )
{
while (*data && *data != '\n')
data++;
}
// skip /* */ comments
else if ( c=='/' && data[1] == '*' )
{
while ( *data && ( *data != '*' || data[1] != '/' ) )
{
data++;
}
if ( *data )
{
data += 2;
}
}
else
{
break;
}
}
if ( c == 0 ) {
return "";
}
// handle quoted strings
if (c == '\"')
{
data++;
while (1)
{
c = *data++;
if (c=='\"' || !c)
{
com_token[len] = 0;
*data_p = ( char * ) data;
return com_token;
}
if (len < MAX_TOKEN_CHARS - 1)
{
com_token[len] = c;
len++;
}
}
}
// parse a regular word
do
{
if (len < MAX_TOKEN_CHARS - 1)
{
com_token[len] = c;
len++;
}
data++;
c = *data;
} while (c>32 && c != ',' );
com_token[len] = 0;
*data_p = ( char * ) data;
return com_token;
}
/*
===============
RE_RegisterServerSkin
Mangled version of the above function to load .skin files on the server.
===============
*/
qhandle_t RE_RegisterServerSkin( const char *name ) {
qhandle_t r;
if (ri.Cvar_VariableIntegerValue( "cl_running" ) &&
ri.Com_TheHunkMarkHasBeenMade() &&
ShaderHashTableExists())
{ //If the client is running then we can go straight into the normal registerskin func
return RE_RegisterSkin(name);
}
gServerSkinHack = true;
r = RE_RegisterSkin(name);
gServerSkinHack = false;
return r;
}
/*
===============
R_InitSkins
===============
*/
void R_InitSkins( void ) {
skin_t *skin;
tr.numSkins = 1;
// make the default skin have all default shaders
skin = tr.skins[0] = (struct skin_s *)ri.Hunk_Alloc( sizeof( skin_t ), h_low );
Q_strncpyz( skin->name, "<default skin>", sizeof( skin->name ) );
skin->numSurfaces = 1;
skin->surfaces[0] = (_skinSurface_t *)ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low );
skin->surfaces[0]->shader = tr.defaultShader;
}
/*
===============
R_GetSkinByHandle
===============
*/
skin_t *R_GetSkinByHandle( qhandle_t hSkin ) {
if ( hSkin < 1 || hSkin >= tr.numSkins ) {
return tr.skins[0];
}
return tr.skins[ hSkin ];
}
/*
===============
R_SkinList_f
===============
*/
void R_SkinList_f( void ) {
int i, j;
skin_t *skin;
ri.Printf( PRINT_ALL, "------------------\n");
for ( i = 0 ; i < tr.numSkins ; i++ ) {
skin = tr.skins[i];
ri.Printf( PRINT_ALL, "%3i:%s\n", i, skin->name );
for ( j = 0 ; j < skin->numSurfaces ; j++ ) {
ri.Printf( PRINT_ALL, " %s = %s\n",
skin->surfaces[j]->name, ((shader_t* )skin->surfaces[j]->shader)->name );
}
}
ri.Printf( PRINT_ALL, "------------------\n");
}