jediacademy/code/qcommon/cm_shader.cpp
2013-04-04 17:35:38 -05:00

529 lines
13 KiB
C++

#include "../server/exe_headers.h"
#include "../game/q_shared.h"
#include "cm_local.h"
#include "memory.h"
#include "chash.h"
class CCMShaderText
{
private:
char mName[MAX_QPATH];
class CCMShaderText *mNext;
const char *mData;
public:
// Constructors
CCMShaderText(const char *name, const char *data) { Q_strncpyz(mName, name, MAX_QPATH); mNext = NULL; mData = data; }
~CCMShaderText(void) {}
// Accessors
const char *GetName(void) const { return(mName); }
class CCMShaderText *GetNext(void) const { return(mNext); }
void SetNext(class CCMShaderText *next) { mNext = next; }
void Destroy(void) { delete this; }
const char *GetData(void) const { return(mData); }
};
char *shaderText = NULL;
CHash<CCMShaderText> shaderTextTable;
CHash<CCMShader> cmShaderTable;
const char *SkipWhitespace( const char *data, qboolean *hasNewLines );
//rwwFIXMEFIXME: Called at RE_BeginRegistration because Hunk_Clear
//destroys the memory cmShaderTable is on. This is a temp solution
//I guess.
void ShaderTableCleanup()
{
cmShaderTable.clear();
}
/*
====================
CM_CreateShaderTextHash
=====================
*/
void CM_CreateShaderTextHash(void)
{
const char *p;
qboolean hasNewLines;
char *token;
CCMShaderText *shader;
p = shaderText;
// look for label
while (p)
{
p = SkipWhitespace(p, &hasNewLines);
token = COM_ParseExt( &p, qtrue );
if ( !token[0] )
{
break;
}
shader = new CCMShaderText(token, p);
shaderTextTable.insert(shader);
SkipBracedSection(&p);
}
}
/*
====================
CM_LoadShaderFiles
Finds and loads all .shader files, combining them into
a single large text block that can be scanned for shader names
=====================
*/
#define MAX_SHADER_FILES 1024
void CM_LoadShaderFiles( void )
{
char **shaderFiles1;
int numShaders1;
char *buffers[MAX_SHADER_FILES];
int numShaders;
int i;
int sum = 0;
// scan for shader files
shaderFiles1 = FS_ListFiles( "shaders", ".shader", &numShaders1 );
if ( !shaderFiles1 || !numShaders1 )
{
Com_Printf( S_COLOR_YELLOW "WARNING: no shader files found\n" );
return;
}
numShaders = numShaders1;
if ( numShaders > MAX_SHADER_FILES )
{
numShaders = MAX_SHADER_FILES;
}
// load and parse shader files
for ( i = 0; i < numShaders1; i++ )
{
char filename[MAX_QPATH];
Com_sprintf( filename, sizeof( filename ), "shaders/%s", shaderFiles1[i] );
Com_DPrintf( "...loading '%s'\n", filename );
FS_ReadFile( filename, (void **)&buffers[i] );
if ( !buffers[i] )
{
Com_Error( ERR_DROP, "Couldn't load %s", filename );
}
sum += COM_Compress( buffers[i] );
}
// build single large buffer
shaderText = (char *)Z_Malloc( sum + numShaders * 2, TAG_SHADERTEXT, qtrue);
// free in reverse order, so the temp files are all dumped
for ( i = numShaders - 1; i >= 0 ; i-- )
{
strcat( shaderText, "\n" );
strcat( shaderText, buffers[i] );
FS_FreeFile( buffers[i] );
}
// free up memory
FS_FreeFileList( shaderFiles1 );
}
/*
==================
CM_GetShaderText
==================
*/
const char *CM_GetShaderText(const char *key)
{
CCMShaderText *st;
st = shaderTextTable[key];
if(st)
{
return(st->GetData());
}
return(NULL);
}
/*
==================
CM_FreeShaderText
==================
*/
void CM_FreeShaderText(void)
{
shaderTextTable.clear();
if(shaderText)
{
Z_Free(shaderText);
shaderText = NULL;
}
}
/*
==================
CM_LoadShaderText
Loads in all the .shader files so it can be accessed by the server and the renderer
Creates a hash table to quickly access the shader text
==================
*/
void CM_LoadShaderText(bool forceReload)
{
if(forceReload)
{
CM_FreeShaderText();
}
if(shaderText)
{
return;
}
Com_Printf("Loading shader text .....\n");
CM_LoadShaderFiles();
CM_CreateShaderTextHash();
Com_Printf("..... %d shader definitions loaded\n", shaderTextTable.count());
}
/*
===============
ParseSurfaceParm
surfaceparm <name>
===============
*/
typedef struct
{
char *name;
int clearSolid, surfaceFlags, contents;
} infoParm_t;
infoParm_t svInfoParms[] =
{
// Game content Flags
{"nonsolid", ~CONTENTS_SOLID, 0, 0 }, // special hack to clear solid flag
{"nonopaque", ~CONTENTS_OPAQUE, 0, 0 }, // special hack to clear opaque flag
{"lava", ~CONTENTS_SOLID, 0, CONTENTS_LAVA }, // very damaging
{"slime", ~CONTENTS_SOLID, 0, CONTENTS_SLIME }, // mildly damaging
{"water", ~CONTENTS_SOLID, 0, CONTENTS_WATER },
{"fog", ~CONTENTS_SOLID, 0, CONTENTS_FOG}, // carves surfaces entering
{"shotclip", ~CONTENTS_SOLID, 0, CONTENTS_SHOTCLIP }, /* block shots, but not people */
{"playerclip", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_PLAYERCLIP }, /* block only the player */
{"monsterclip", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_MONSTERCLIP },
{"botclip", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_BOTCLIP }, /* NPC do not enter */
{"trigger", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_TRIGGER },
{"nodrop", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_NODROP }, // don't drop items or leave bodies (death fog, lava, etc)
{"terrain", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_TERRAIN }, /* use special terrain collsion */
{"ladder", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_LADDER }, // climb up in it like water
{"abseil", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_ABSEIL }, // can abseil down this brush
{"outside", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_OUTSIDE }, // volume is considered to be in the outside (i.e. not indoors)
{"inside", ~(CONTENTS_SOLID|CONTENTS_OPAQUE),0,CONTENTS_INSIDE }, // volume is considered to be inside (i.e. indoors)
{"detail", -1, 0, CONTENTS_DETAIL }, // don't include in structural bsp
{"trans", -1, 0, CONTENTS_TRANSLUCENT }, // surface has an alpha component
/* Game surface flags */
{"sky", -1, SURF_SKY, 0 }, /* emit light from an environment map */
{"slick", -1, SURF_SLICK, 0 },
{"nodamage", -1, SURF_NODAMAGE, 0 },
{"noimpact", -1, SURF_NOIMPACT, 0 }, /* don't make impact explosions or marks */
{"nomarks", -1, SURF_NOMARKS, 0 }, /* don't make impact marks, but still explode */
{"nodraw", -1, SURF_NODRAW, 0 }, /* don't generate a drawsurface (or a lightmap) */
{"nosteps", -1, SURF_NOSTEPS, 0 },
{"nodlight", -1, SURF_NODLIGHT, 0 }, /* don't ever add dynamic lights */
{"metalsteps", -1, SURF_METALSTEPS,0 },
{"nomiscents", -1, SURF_NOMISCENTS,0 }, /* No misc ents on this surface */
{"forcefield", -1, SURF_FORCEFIELD,0 },
{"forcesight", -1, SURF_FORCESIGHT,0 }, // only visible with force sight
};
void SV_ParseSurfaceParm( CCMShader * shader, const char **text )
{
char *token;
int numsvInfoParms = sizeof(svInfoParms) / sizeof(svInfoParms[0]);
int i;
token = COM_ParseExt( text, qfalse );
for ( i = 0 ; i < numsvInfoParms ; i++ )
{
if ( !Q_stricmp( token, svInfoParms[i].name ) )
{
shader->surfaceFlags |= svInfoParms[i].surfaceFlags;
shader->contentFlags |= svInfoParms[i].contents;
shader->contentFlags &= svInfoParms[i].clearSolid;
break;
}
}
}
/*
=================
ParseMaterial
=================
*/
const char *svMaterialNames[MATERIAL_LAST] =
{
MATERIALS
};
void SV_ParseMaterial( CCMShader *shader, const char **text )
{
char *token;
int i;
token = COM_ParseExt( text, qfalse );
if ( !token[0] )
{
Com_Printf( S_COLOR_YELLOW "WARNING: missing material in shader '%s'\n", shader->shader );
return;
}
for(i = 0; i < MATERIAL_LAST; i++)
{
if ( !Q_stricmp( token, svMaterialNames[i] ) )
{
shader->surfaceFlags &= ~MATERIAL_MASK;//safety, clear it first
shader->surfaceFlags |= i;
break;
}
}
}
/*
===============
ParseVector
===============
*/
qboolean CM_ParseVector( CCMShader *shader, const char **text, int count, float *v )
{
char *token;
int i;
// FIXME: spaces are currently required after parens, should change parseext...
token = COM_ParseExt( text, qfalse );
if ( strcmp( token, "(" ) )
{
Com_Printf( S_COLOR_YELLOW "WARNING: missing parenthesis in shader '%s'\n", shader->shader );
return qfalse;
}
for ( i = 0 ; i < count ; i++ )
{
token = COM_ParseExt( text, qfalse );
if ( !token[0] )
{
Com_Printf( S_COLOR_YELLOW "WARNING: missing vector element in shader '%s'\n", shader->shader );
return qfalse;
}
v[i] = atof( token );
}
token = COM_ParseExt( text, qfalse );
if ( strcmp( token, ")" ) )
{
Com_Printf( S_COLOR_YELLOW "WARNING: missing parenthesis in shader '%s'\n", shader->shader );
return qfalse;
}
return qtrue;
}
/*
=================
CM_ParseShader
The current text pointer is at the explicit text definition of the
shader. Parse it into the global shader variable.
This extracts all the info from the shader required for physics and collision
It is designed to *NOT* load any image files and not require any of the renderer to
be initialised.
=================
*/
void CM_ParseShader( CCMShader *shader, const char **text )
{
char *token;
token = COM_ParseExt( text, qtrue );
if ( token[0] != '{' )
{
Com_Printf( S_COLOR_YELLOW "WARNING: expecting '{', found '%s' instead in shader '%s'\n", token, shader->shader );
return;
}
while ( true )
{
token = COM_ParseExt( text, qtrue );
if ( !token[0] )
{
Com_Printf( S_COLOR_YELLOW "WARNING: no concluding '}' in shader %s\n", shader->shader );
return;
}
// end of shader definition
if ( token[0] == '}' )
{
break;
}
// stage definition
else if ( token[0] == '{' )
{
SkipBracedSection( text );
continue;
}
// material deprecated as of 11 Jan 01
// material undeprecated as of 7 May 01 - q3map_material deprecated
else if ( !Q_stricmp( token, "material" ) || !Q_stricmp( token, "q3map_material" ) )
{
SV_ParseMaterial( shader, text );
}
// sun parms
// q3map_sun deprecated as of 11 Jan 01
else if ( !Q_stricmp( token, "sun" ) || !Q_stricmp( token, "q3map_sun" ) )
{
// float a, b;
token = COM_ParseExt( text, qfalse );
// shader->sunLight[0] = atof( token );
token = COM_ParseExt( text, qfalse );
// shader->sunLight[1] = atof( token );
token = COM_ParseExt( text, qfalse );
// shader->sunLight[2] = atof( token );
// VectorNormalize( shader->sunLight );
token = COM_ParseExt( text, qfalse );
// a = atof( token );
// VectorScale( shader->sunLight, a, shader->sunLight);
token = COM_ParseExt( text, qfalse );
// a = DEG2RAD(atof( token ));
token = COM_ParseExt( text, qfalse );
// b = DEG2RAD(atof( token ));
// shader->sunDirection[0] = cos( a ) * cos( b );
// shader->sunDirection[1] = sin( a ) * cos( b );
// shader->sunDirection[2] = sin( b );
}
else if ( !Q_stricmp( token, "surfaceParm" ) )
{
SV_ParseSurfaceParm( shader, text );
continue;
}
else if ( !Q_stricmp( token, "fogParms" ) )
{
vec3_t fogColor;
if ( !CM_ParseVector( shader, text, 3, fogColor ) )
{
return;
}
token = COM_ParseExt( text, qfalse );
if ( !token[0] )
{
Com_Printf( S_COLOR_YELLOW "WARNING: missing parm for 'fogParms' keyword in shader '%s'\n", shader->shader );
continue;
}
// shader->depthForOpaque = atof( token );
// skip any old gradient directions
SkipRestOfLine( (const char **)text );
continue;
}
}
return;
}
/*
=================
CM_SetupShaderProperties
Scans thru the shaders loaded for the map, parses the text of that shader and
extracts the interesting info *WITHOUT* loading up any images or requiring
the renderer to be active.
=================
*/
void CM_SetupShaderProperties(void)
{
int i;
const char *def;
CCMShader *shader;
// Add all basic shaders to the cmShaderTable
for(i = 0; i < cmg.numShaders; i++)
{
cmShaderTable.insert(CM_GetShaderInfo(i));
}
// Go through and parse evaluate shader names to shadernums
for(i = 0; i < cmg.numShaders; i++)
{
shader = CM_GetShaderInfo(i);
def = CM_GetShaderText(shader->shader);
if(def)
{
CM_ParseShader(shader, &def);
}
}
}
void CM_ShutdownShaderProperties(void)
{
if(cmShaderTable.count())
{
Com_Printf("Shutting down cmShaderTable .....\n");
cmShaderTable.clear();
}
}
CCMShader *CM_GetShaderInfo( const char *name )
{
CCMShader *out;
const char *def;
out = cmShaderTable[name];
if(out)
{
return(out);
}
// Create a new CCMShader class
//out = (CCMShader *)Hunk_Alloc( sizeof( CCMShader ), h_high );
out = (CCMShader *)Hunk_Alloc( sizeof( CCMShader ), qtrue );
// Set defaults
Q_strncpyz(out->shader, name, MAX_QPATH);
out->contentFlags = CONTENTS_SOLID | CONTENTS_OPAQUE;
// Parse in any text if it exists
def = CM_GetShaderText(name);
if(def)
{
CM_ParseShader(out, &def);
}
cmShaderTable.insert(out);
return(out);
}
CCMShader *CM_GetShaderInfo( int shaderNum )
{
CCMShader *out;
if((shaderNum < 0) || (shaderNum >= cmg.numShaders))
{
return(NULL);
}
out = cmg.shaders + shaderNum;
return(out);
}
// end