776 lines
17 KiB
C++
776 lines
17 KiB
C++
// Filename:- shader.cpp
|
|
//
|
|
// guess...
|
|
|
|
|
|
#include "stdafx.h"
|
|
#include "includes.h"
|
|
#include "R_Common.h"
|
|
#include "R_Image.h"
|
|
#include "files.h"
|
|
//
|
|
#include "shader.h"
|
|
|
|
shaderCommands_t tess;
|
|
|
|
// stuff from lower down, may externalise.
|
|
//
|
|
char *COM_ParseExt( char **data_p, qboolean allowLineBreaks );
|
|
|
|
|
|
|
|
static char *s_shaderText = NULL;
|
|
/*
|
|
// the shader is parsed into these global variables, then copied into
|
|
// dynamically allocated memory if it is valid.
|
|
static shaderStage_t stages[MAX_SHADER_STAGES];
|
|
static shader_t shader;
|
|
static texModInfo_t texMods[MAX_SHADER_STAGES][TR_MAX_TEXMODS];
|
|
static qboolean deferLoad;
|
|
|
|
#define FILE_HASH_SIZE 1024
|
|
static shader_t* hashTable[FILE_HASH_SIZE];
|
|
|
|
//#define SHADERTEXTHASH
|
|
|
|
#define MAX_SHADERTEXT_HASH 2048
|
|
static char **shaderTextHashTable[MAX_SHADERTEXT_HASH];
|
|
*/
|
|
|
|
|
|
|
|
|
|
static char com_token[MAX_TOKEN_CHARS];
|
|
static char com_parsename[MAX_TOKEN_CHARS];
|
|
static int com_lines;
|
|
|
|
void COM_BeginParseSession( const char *name )
|
|
{
|
|
com_lines = 0;
|
|
Com_sprintf(com_parsename, sizeof(com_parsename), "%s", name);
|
|
}
|
|
|
|
int COM_GetCurrentParseLine( void )
|
|
{
|
|
return com_lines;
|
|
}
|
|
|
|
char *COM_Parse( char **data_p )
|
|
{
|
|
return COM_ParseExt( data_p, qtrue );
|
|
}
|
|
|
|
void COM_ParseError( char *format, ... )
|
|
{
|
|
va_list argptr;
|
|
static char string[4096];
|
|
|
|
va_start (argptr, format);
|
|
vsprintf (string, format, argptr);
|
|
va_end (argptr);
|
|
|
|
Com_Printf("ERROR: %s, line %d: %s\n", com_parsename, com_lines, string);
|
|
}
|
|
|
|
void COM_ParseWarning( char *format, ... )
|
|
{
|
|
va_list argptr;
|
|
static char string[4096];
|
|
|
|
va_start (argptr, format);
|
|
vsprintf (string, format, argptr);
|
|
va_end (argptr);
|
|
|
|
Com_Printf("WARNING: %s, line %d: %s\n", com_parsename, com_lines, string);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
COM_Parse
|
|
|
|
Parse a token out of a string
|
|
Will never return NULL, just empty strings
|
|
|
|
If "allowLineBreaks" is qtrue then an empty
|
|
string will be returned if the next token is
|
|
a newline.
|
|
==============
|
|
*/
|
|
static char *SkipWhitespace( char *data, qboolean *hasNewLines ) {
|
|
int c;
|
|
|
|
while( (c = *data) <= ' ') {
|
|
if( !c ) {
|
|
return NULL;
|
|
}
|
|
if( c == '\n' ) {
|
|
com_lines++;
|
|
*hasNewLines = qtrue;
|
|
}
|
|
data++;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SkipBracedSection
|
|
|
|
The next token should be an open brace.
|
|
Skips until a matching close brace is found.
|
|
Internal brace depths are properly skipped.
|
|
=================
|
|
*/
|
|
void SkipBracedSection (char **program) {
|
|
char *token;
|
|
int depth;
|
|
|
|
depth = 0;
|
|
do {
|
|
token = COM_ParseExt( program, qtrue );
|
|
if( token[1] == 0 ) {
|
|
if( token[0] == '{' ) {
|
|
depth++;
|
|
}
|
|
else if( token[0] == '}' ) {
|
|
depth--;
|
|
}
|
|
}
|
|
} while( depth && *program );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SkipRestOfLine
|
|
=================
|
|
*/
|
|
void SkipRestOfLine ( char **data ) {
|
|
char *p;
|
|
int c;
|
|
|
|
p = *data;
|
|
while ( (c = *p++) != 0 ) {
|
|
if ( c == '\n' ) {
|
|
com_lines++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*data = p;
|
|
}
|
|
|
|
|
|
char *COM_ParseExt( char **data_p, qboolean allowLineBreaks )
|
|
{
|
|
int c = 0, len;
|
|
qboolean hasNewLines = qfalse;
|
|
char *data;
|
|
|
|
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
|
|
data = SkipWhitespace( data, &hasNewLines );
|
|
if ( !data )
|
|
{
|
|
*data_p = NULL;
|
|
return com_token;
|
|
}
|
|
if ( hasNewLines && !allowLineBreaks )
|
|
{
|
|
*data_p = data;
|
|
return com_token;
|
|
}
|
|
|
|
c = *data;
|
|
|
|
// skip double slash comments
|
|
if ( c == '/' && data[1] == '/' )
|
|
{
|
|
data += 2;
|
|
while (*data && *data != '\n') {
|
|
data++;
|
|
}
|
|
}
|
|
// skip /* */ comments
|
|
else if ( c=='/' && data[1] == '*' )
|
|
{
|
|
data += 2;
|
|
while ( *data && ( *data != '*' || data[1] != '/' ) )
|
|
{
|
|
data++;
|
|
}
|
|
if ( *data )
|
|
{
|
|
data += 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
com_token[len] = c;
|
|
len++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// parse a regular word
|
|
do
|
|
{
|
|
if (len < MAX_TOKEN_CHARS)
|
|
{
|
|
com_token[len] = c;
|
|
len++;
|
|
}
|
|
data++;
|
|
c = *data;
|
|
if ( c == '\n' )
|
|
com_lines++;
|
|
} while (c>32);
|
|
|
|
if (len == MAX_TOKEN_CHARS)
|
|
{
|
|
// Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS);
|
|
len = 0;
|
|
}
|
|
com_token[len] = 0;
|
|
|
|
*data_p = ( char * ) data;
|
|
return com_token;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
====================
|
|
FindShaderInShaderText
|
|
|
|
Scans the combined text description of all the shader files for
|
|
the given shader name.
|
|
|
|
return NULL if not found
|
|
|
|
If found, it will return a valid shader
|
|
=====================
|
|
*/
|
|
static char *FindShaderInShaderText( const char *shadername ) {
|
|
#ifdef SHADERTEXTHASH
|
|
|
|
char *token, *p;
|
|
int i, hash;
|
|
|
|
hash = generateHashValue(shadername, MAX_SHADERTEXT_HASH);
|
|
|
|
for (i = 0; shaderTextHashTable[hash][i]; i++) {
|
|
p = shaderTextHashTable[hash][i];
|
|
token = COM_ParseExt(&p, qtrue);
|
|
if ( !Q_stricmp( token, shadername ) ) {
|
|
return p;
|
|
}
|
|
}
|
|
return NULL;
|
|
#else
|
|
char *p = s_shaderText;
|
|
char *token;
|
|
|
|
if ( !p ) {
|
|
return NULL;
|
|
}
|
|
|
|
// look for label
|
|
while ( 1 ) {
|
|
token = COM_ParseExt( &p, qtrue );
|
|
if ( token[0] == 0 ) {
|
|
break;
|
|
}
|
|
|
|
if ( !Q_stricmp( token, shadername ) ) {
|
|
return p;
|
|
}
|
|
else {
|
|
// skip the definition
|
|
SkipBracedSection( &p );
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
====================
|
|
ScanAndLoadShaderFiles
|
|
|
|
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
|
|
|
|
typedef map<string,string> ShadersFoundAndFilesPicked_t;
|
|
ShadersFoundAndFilesPicked_t ShadersFoundAndFilesPicked;
|
|
|
|
void KillAllShaderFiles(void)
|
|
{
|
|
SAFEFREE(s_shaderText);
|
|
ShadersFoundAndFilesPicked.clear();
|
|
}
|
|
|
|
|
|
void ScanAndLoadShaderFiles( void )
|
|
{
|
|
if (s_shaderText == NULL && strlen(gamedir))
|
|
{
|
|
char **shaderFiles;
|
|
char *buffers[MAX_SHADER_FILES];
|
|
#ifdef SHADERTEXTHASH
|
|
char *p, *oldp, *token, *hashMem;
|
|
int shaderTextHashTableSizes[MAX_SHADERTEXT_HASH], hash, size;
|
|
#endif
|
|
int numShaders;
|
|
int i;
|
|
long sum = 0;
|
|
|
|
#define sSHADER_DIR va("%sshaders",gamedir)
|
|
|
|
// scan for shader files
|
|
shaderFiles = //ri.FS_ListFiles( "shaders", ".shader", &numShaders );
|
|
Sys_ListFiles( sSHADER_DIR, // const char *directory,
|
|
".shader", // const char *extension,
|
|
NULL, // char *filter,
|
|
&numShaders,// int *numfiles,
|
|
qfalse // qboolean wantsubs
|
|
);
|
|
|
|
if ( !shaderFiles || !numShaders )
|
|
{
|
|
if (!bXMenPathHack)
|
|
{
|
|
ri.Printf( PRINT_WARNING, "WARNING: no shader files found in '%s'\n",sSHADER_DIR );
|
|
}
|
|
s_shaderText = CopyString("// blank shader file to avoid re-scanning shaders\n");
|
|
return;
|
|
}
|
|
|
|
if ( numShaders > MAX_SHADER_FILES ) {
|
|
numShaders = MAX_SHADER_FILES;
|
|
}
|
|
|
|
// load and parse shader files
|
|
for ( i = 0; i < numShaders; i++ )
|
|
{
|
|
char filename[MAX_QPATH];
|
|
|
|
Com_sprintf( filename, sizeof( filename ), "shaders/%s", shaderFiles[i] );
|
|
StatusMessage( va("Loading shader %d/%d: \"%s\"...",i+1,numShaders,filename));
|
|
|
|
//ri.Printf( PRINT_ALL, "...loading '%s'\n", filename );
|
|
sum += ri.FS_ReadFile( filename, (void **)&buffers[i] );
|
|
if ( !buffers[i] ) {
|
|
ri.Error( ERR_DROP, "Couldn't load %s", filename );
|
|
}
|
|
}
|
|
StatusMessage(NULL);
|
|
|
|
// build single large buffer
|
|
s_shaderText = (char *)ri.Hunk_Alloc( sum + numShaders*2 );
|
|
|
|
// free in reverse order, so the temp files are all dumped
|
|
for ( i = numShaders - 1; i >= 0 ; i-- ) {
|
|
strcat( s_shaderText, "\n" );
|
|
strcat( s_shaderText, buffers[i] );
|
|
ri.FS_FreeFile( buffers[i] );
|
|
}
|
|
|
|
// free up memory
|
|
//ri.FS_FreeFileList( shaderFiles );
|
|
Sys_FreeFileList( shaderFiles );
|
|
|
|
#ifdef SHADERTEXTHASH
|
|
memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes));
|
|
size = 0;
|
|
p = s_shaderText;
|
|
// look for label
|
|
while ( 1 ) {
|
|
token = COM_ParseExt( &p, qtrue );
|
|
if ( token[0] == 0 ) {
|
|
break;
|
|
}
|
|
|
|
hash = generateHashValue(token, MAX_SHADERTEXT_HASH);
|
|
shaderTextHashTableSizes[hash]++;
|
|
size++;
|
|
SkipBracedSection(&p);
|
|
}
|
|
size += MAX_SHADERTEXT_HASH;
|
|
|
|
hashMem = (char *)ri.Hunk_Alloc( size * sizeof(char *) );
|
|
|
|
for (i = 0; i < MAX_SHADERTEXT_HASH; i++) {
|
|
shaderTextHashTable[i] = (char **) hashMem;
|
|
hashMem = ((char *) hashMem) + ((shaderTextHashTableSizes[i] + 1) * sizeof(char *));
|
|
}
|
|
|
|
memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes));
|
|
p = s_shaderText;
|
|
// look for label
|
|
while ( 1 ) {
|
|
oldp = p;
|
|
token = COM_ParseExt( &p, qtrue );
|
|
if ( token[0] == 0 ) {
|
|
break;
|
|
}
|
|
|
|
hash = generateHashValue(token, MAX_SHADERTEXT_HASH);
|
|
shaderTextHashTable[hash][shaderTextHashTableSizes[hash]++] = oldp;
|
|
|
|
SkipBracedSection(&p);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
textures/sfx/dclogo
|
|
{
|
|
qer_editorimage textures/gothic_floor/largerblock3b.tga
|
|
nomipmaps
|
|
|
|
|
|
{
|
|
map textures/base_floor/clangdark.tga
|
|
rgbGen identity
|
|
tcmod scale 4 4
|
|
}
|
|
|
|
{
|
|
map $lightmap
|
|
rgbGen identity
|
|
blendfunc gl_dst_color gl_zero
|
|
}
|
|
|
|
|
|
{
|
|
clampmap textures/effects/dreamcast-logo2.tga
|
|
blendfunc gl_one gl_one
|
|
tcmod rotate -75
|
|
rgbGen wave sin .75 .25 0 .5
|
|
}
|
|
|
|
// END
|
|
}
|
|
*/
|
|
static bool CheckForFilenameArg(LPCSTR &psSearchPos, LPCSTR psKeyword)
|
|
{
|
|
LPCSTR psSearchResult = strstr(psSearchPos,psKeyword);
|
|
|
|
if (psSearchResult && isspace(psSearchResult[strlen(psKeyword)]))
|
|
{
|
|
psSearchResult += strlen(psKeyword);
|
|
while (*psSearchResult && isspace(*psSearchResult)) psSearchResult++;
|
|
|
|
if (strlen(psSearchResult) && *psSearchResult != '$' && ((psSearchResult>psSearchPos)?isspace(psSearchResult[-1]):1) ) // note error-check on -1 index
|
|
{
|
|
psSearchPos = psSearchResult;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (psSearchResult)
|
|
{
|
|
// skip to line end...
|
|
//
|
|
while (*psSearchResult++ != '\n'){}
|
|
psSearchResult++;
|
|
psSearchPos = psSearchResult;
|
|
}
|
|
else
|
|
{
|
|
psSearchPos = NULL;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// have a look at the shader text pointed at by the supplied arg and try and work out
|
|
// a suitable image name.
|
|
//
|
|
// First, see if there's an editorimage,
|
|
// else check for a map with a valid texture (ie not '$lightmap' etc)
|
|
// else check for a clampmap
|
|
// else just return NULL, signifying that I'm stumped...
|
|
//
|
|
LPCSTR Shader_ExtractSuitableFilename(LPCSTR psShaderText)
|
|
{
|
|
char *psShaderTextEnd = (char*)psShaderText;
|
|
SkipBracedSection (&psShaderTextEnd);
|
|
|
|
int iShaderChars = psShaderTextEnd - psShaderText;
|
|
|
|
// make a small string with just this shader in, for easier searching...
|
|
//
|
|
char *psThisShader = (char *) malloc(iShaderChars+1); // +1 for trailing zero
|
|
char *psShaderSearch= psThisShader;
|
|
strncpy(psThisShader,psShaderText,iShaderChars);
|
|
psThisShader[iShaderChars]='\0';
|
|
|
|
LPCSTR psAnswer = NULL;
|
|
|
|
char *psShaderSearchTry = psShaderSearch;
|
|
if (CheckForFilenameArg(psShaderSearchTry, "qer_editorimage"))
|
|
{
|
|
psAnswer = psShaderSearchTry;
|
|
}
|
|
|
|
// these next ones I need to search for multiple times, else it hits things like "q3map_blah",
|
|
// doesn't find a space after "map", then gives up, so it should keep trying....
|
|
//
|
|
psShaderSearchTry = psShaderSearch;
|
|
while (psShaderSearchTry && !psAnswer)
|
|
{
|
|
if (CheckForFilenameArg(psShaderSearchTry, "map"))
|
|
{
|
|
psAnswer = psShaderSearchTry;
|
|
}
|
|
}
|
|
|
|
if (!psAnswer)
|
|
{
|
|
psShaderSearchTry = psShaderSearch;
|
|
|
|
while (psShaderSearchTry && !psAnswer)
|
|
{
|
|
if (CheckForFilenameArg(psShaderSearchTry, "clampmap"))
|
|
{
|
|
psAnswer = psShaderSearchTry;
|
|
}
|
|
}
|
|
}
|
|
|
|
static char sReturnName[MAX_QPATH];
|
|
if (psAnswer)
|
|
{
|
|
strncpy(sReturnName,psAnswer,sizeof(sReturnName));
|
|
sReturnName[sizeof(sReturnName)-1]='\0';
|
|
for (int i=0; i<sizeof(sReturnName)-1; i++)
|
|
{
|
|
if (sReturnName[i]=='\0')
|
|
break;
|
|
if (isspace(sReturnName[i]))
|
|
{
|
|
sReturnName[i] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
free(psThisShader);
|
|
return psAnswer?sReturnName:NULL;
|
|
}
|
|
|
|
|
|
// massively hacked-down version of original code, this simply takes an input char * local filename, then
|
|
// modifies it to be either the name of the first MAP arg within the corresponding shader file, else leaves
|
|
// it alone if not found
|
|
const char *R_FindShader( const char *psLocalMaterialName)
|
|
{
|
|
static char sReturnString[MAX_QPATH];
|
|
|
|
char strippedName[MAX_QPATH];
|
|
/* char fileName[MAX_QPATH];
|
|
int i, hash;
|
|
*/ char *shaderText;
|
|
/* image_t *image;
|
|
shader_t *sh;
|
|
|
|
if ( name[0] == 0 ) {
|
|
return tr.defaultShader;
|
|
}
|
|
|
|
// use (fullbright) vertex lighting if the bsp file doesn't have
|
|
// lightmaps
|
|
if ( lightmapIndex[0] >= 0 && lightmapIndex[0] >= tr.numLightmaps ) {
|
|
lightmapIndex[0] = LIGHTMAP_BY_VERTEX;
|
|
}
|
|
*/
|
|
COM_StripExtension( psLocalMaterialName, strippedName );
|
|
strlwr(strippedName);
|
|
|
|
ShadersFoundAndFilesPicked_t::iterator it = ShadersFoundAndFilesPicked.find(strippedName);
|
|
if (it != ShadersFoundAndFilesPicked.end())
|
|
{
|
|
return (*it).second.c_str();
|
|
}
|
|
/*
|
|
hash = generateHashValue(strippedName, FILE_HASH_SIZE);
|
|
|
|
//
|
|
// see if the shader is already loaded
|
|
//
|
|
for (sh = hashTable[hash]; sh; sh = sh->next) {
|
|
// NOTE: if there was no shader or image available with the name strippedName
|
|
// then a default shader is created with lightmapIndex == LIGHTMAP_NONE, so we
|
|
// have to check all default shaders otherwise for every call to R_FindShader
|
|
// with that same strippedName a new default shader is created.
|
|
if (IsShader(sh, strippedName, lightmapIndex, styles))
|
|
{
|
|
return sh;
|
|
}
|
|
}
|
|
*/
|
|
/* // make sure the render thread is stopped, because we are probably
|
|
// going to have to upload an image
|
|
R_SyncRenderThread();
|
|
|
|
// clear the global shader
|
|
ClearGlobalShader();
|
|
memset( &stages, 0, sizeof( stages ) );
|
|
Q_strncpyz(shader.name, strippedName, sizeof(shader.name));
|
|
memcpy(shader.lightmapIndex, lightmapIndex, sizeof(shader.lightmapIndex));
|
|
memcpy(shader.styles, styles, sizeof(shader.styles));
|
|
for ( i = 0 ; i < MAX_SHADER_STAGES ; i++ ) {
|
|
stages[i].bundle[0].texMods = texMods[i];
|
|
}
|
|
|
|
// FIXME: set these "need" values apropriately
|
|
shader.needsNormal = qtrue;
|
|
shader.needsST1 = qtrue;
|
|
shader.needsST2 = qtrue;
|
|
shader.needsColor = qtrue;
|
|
*/
|
|
//
|
|
// attempt to define shader from an explicit parameter file
|
|
//
|
|
shaderText = FindShaderInShaderText( strippedName );
|
|
if ( shaderText )
|
|
{
|
|
/* // enable this when building a pak file to get a global list
|
|
// of all explicit shaders
|
|
if ( r_printShaders->integer ) {
|
|
ri.Printf( PRINT_ALL, "*SHADER* %s\n", name );
|
|
}
|
|
|
|
if ( !ParseShader( &shaderText ) ) {
|
|
// had errors, so use default shader
|
|
shader.defaultShader = qtrue;
|
|
}
|
|
sh = FinishShader();
|
|
return sh;
|
|
*/
|
|
LPCSTR psReturnName = Shader_ExtractSuitableFilename(shaderText);
|
|
if (psReturnName)
|
|
{
|
|
psLocalMaterialName = psReturnName;
|
|
}
|
|
}
|
|
|
|
ShadersFoundAndFilesPicked[strippedName] = psLocalMaterialName;
|
|
return psLocalMaterialName; // return original name
|
|
/*
|
|
//
|
|
// if not defined in the in-memory shader descriptions,
|
|
// look for a single TGA, BMP, or PCX
|
|
//
|
|
Q_strncpyz( fileName, name, sizeof( fileName ) );
|
|
image = R_FindImageFile( fileName, mipRawImage, mipRawImage, mipRawImage ? GL_REPEAT : GL_CLAMP );
|
|
if ( !image ) {
|
|
ri.Printf( PRINT_DEVELOPER, "Couldn't find image for shader %s\n", name );
|
|
shader.defaultShader = qtrue;
|
|
return FinishShader();
|
|
}
|
|
|
|
//
|
|
// create the default shading commands
|
|
//
|
|
if ( shader.lightmapIndex[0] == LIGHTMAP_NONE ) {
|
|
// dynamic colors at vertexes
|
|
stages[0].bundle[0].image[0] = image;
|
|
stages[0].active = qtrue;
|
|
stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE;
|
|
stages[0].stateBits = GLS_DEFAULT;
|
|
} else if ( shader.lightmapIndex[0] == LIGHTMAP_BY_VERTEX ) {
|
|
// explicit colors at vertexes
|
|
stages[0].bundle[0].image[0] = image;
|
|
stages[0].active = qtrue;
|
|
stages[0].rgbGen = CGEN_EXACT_VERTEX;
|
|
stages[0].alphaGen = AGEN_SKIP;
|
|
stages[0].stateBits = GLS_DEFAULT;
|
|
} else if ( shader.lightmapIndex[0] == LIGHTMAP_2D ) {
|
|
// GUI elements
|
|
stages[0].bundle[0].image[0] = image;
|
|
stages[0].active = qtrue;
|
|
stages[0].rgbGen = CGEN_VERTEX;
|
|
stages[0].alphaGen = AGEN_VERTEX;
|
|
stages[0].stateBits = GLS_DEPTHTEST_DISABLE |
|
|
GLS_SRCBLEND_SRC_ALPHA |
|
|
GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA;
|
|
} else if ( shader.lightmapIndex[0] == LIGHTMAP_WHITEIMAGE ) {
|
|
// fullbright level
|
|
stages[0].bundle[0].image[0] = tr.whiteImage;
|
|
stages[0].active = qtrue;
|
|
stages[0].rgbGen = CGEN_IDENTITY_LIGHTING;
|
|
stages[0].stateBits = GLS_DEFAULT;
|
|
|
|
stages[1].bundle[0].image[0] = image;
|
|
stages[1].active = qtrue;
|
|
stages[1].rgbGen = CGEN_IDENTITY;
|
|
stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO;
|
|
} else {
|
|
// two pass lightmap
|
|
stages[0].bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex[0]];
|
|
stages[0].bundle[0].isLightmap = qtrue;
|
|
stages[0].active = qtrue;
|
|
stages[0].rgbGen = CGEN_IDENTITY; // lightmaps are scaled on creation
|
|
// for identitylight
|
|
stages[0].stateBits = GLS_DEFAULT;
|
|
|
|
stages[1].bundle[0].image[0] = image;
|
|
stages[1].active = qtrue;
|
|
stages[1].rgbGen = CGEN_IDENTITY;
|
|
stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO;
|
|
}
|
|
|
|
return FinishShader();
|
|
*/
|
|
}
|
|
|
|
////////////////// eof ////////////////
|
|
|
|
|
|
|