From ef1c50ef776e37fe0fa67d912967bb4f95c25874 Mon Sep 17 00:00:00 2001 From: Spoike Date: Tue, 19 Oct 2004 15:56:22 +0000 Subject: [PATCH] q3 compatable shader parsing. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@353 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/gl/gl_shader.c | 1936 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1936 insertions(+) create mode 100644 engine/gl/gl_shader.c diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c new file mode 100644 index 000000000..f794b8c86 --- /dev/null +++ b/engine/gl/gl_shader.c @@ -0,0 +1,1936 @@ +/* +Copyright (C) 2002-2003 Victor Luchits + +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 2 +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. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// r_shader.c - based on code by Stephen C. Taylor + + +#include "quakedef.h" +#include "glquake.h" +#include "shader.h" + + +//Spike: Marked code removal areas with FIZME +//readd as porting progresses + + +#ifdef Q3SHADERS + +cvar_t r_vertexlight = {"r_vertexlight", "0"}; + +#define Q_stricmp stricmp +#define Com_sprintf _snprintf +#define clamp(v,min, max) (v) = (((v)<(min))?(min):(((v)>(max))?(max):(v))); + +int FS_LoadFile(char *name, void **file) +{ + *file = COM_LoadMallocFile(name); + return com_filesize; +} +void FS_FreeFile(void *file) +{ + BZ_Free(file); +} + +typedef union { + float f; + unsigned int i; +} float_int_t; +qbyte FloatToByte( float x ) +{ + static float_int_t f2i; + + // shift float to have 8bit fraction at base of number + f2i.f = x + 32768.0f; + + // then read as integer and kill float bits... + return (qbyte) min(f2i.i & 0x7FFFFF, 255); +} + + + +cvar_t r_detailtextures; + + +#define MAX_SHADERS 1024 + + +#define MAX_TOKEN_CHARS 1024 + +char *COM_ParseExt (char **data_p, qboolean nl) +{ + int c; + int len; + char *data; + qboolean newlines = false; + + data = *data_p; + len = 0; + com_token[0] = 0; + + if (!data) + { + *data_p = NULL; + return ""; + } + +// skip whitespace +skipwhite: + while ( (c = *data) <= ' ') + { + if (c == 0) + { + *data_p = NULL; + return ""; + } + if (c == '\n') + newlines = true; + data++; + } + + if ( newlines && !nl ) { + *data_p = data; + return com_token; + } + +// skip // comments + if (c == '/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + +// handle quoted strings specially + if (c == '\"') + { + data++; + while (1) + { + c = *data++; + if (c=='\"' || !c) + { + com_token[len] = 0; + *data_p = 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; + } 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 = data; + return com_token; +} + + + + + + + +#define HASH_SIZE 128 + +typedef struct shaderkey_s +{ + char *keyword; + void (*func)( shader_t *shader, shaderpass_t *pass, char **ptr ); +} shaderkey_t; + +typedef struct shadercache_s { + char name[MAX_QPATH]; + char *path; + unsigned int offset; + struct shadercache_s *hash_next; +} shadercache_t; + +static shadercache_t *shader_hash[HASH_SIZE]; +static char shaderbuf[MAX_QPATH * 256]; + +shader_t r_shaders[MAX_SHADERS]; + +static char r_skyboxname[MAX_QPATH]; +static float r_skyheight; + +char *Shader_Skip( char *ptr ); +static qboolean Shader_Parsetok( shader_t *shader, shaderpass_t *pass, shaderkey_t *keys, + char *token, char **ptr ); +static void Shader_ParseFunc( char **args, shaderfunc_t *func ); +static void Shader_MakeCache( char *path ); +static void Shader_GetPathAndOffset( char *name, char **path, unsigned int *offset ); + +//=========================================================================== + +static char *Shader_ParseString ( char **ptr ) +{ + char *token; + + if ( !ptr || !(*ptr) ) { + return ""; + } + if ( !**ptr || **ptr == '}' ) { + return ""; + } + + token = COM_ParseExt ( ptr, false ); + strlwr ( token ); + + return token; +} + +static float Shader_ParseFloat ( char **ptr ) +{ + if ( !ptr || !(*ptr) ) { + return 0; + } + if ( !**ptr || **ptr == '}' ) { + return 0; + } + + return atof ( COM_ParseExt ( ptr, false ) ); +} + +static void Shader_ParseVector ( char **ptr, vec3_t v ) +{ + char *token; + qboolean bracket; + + token = Shader_ParseString ( ptr ); + if ( !Q_stricmp (token, "(") ) { + bracket = true; + token = Shader_ParseString ( ptr ); + } else if ( token[0] == '(' ) { + bracket = true; + token = &token[1]; + } else { + bracket = false; + } + + v[0] = atof ( token ); + v[1] = Shader_ParseFloat ( ptr ); + + token = Shader_ParseString ( ptr ); + if ( !token[0] ) { + v[2] = 0; + } else if ( token[strlen(token)-1] == ')' ) { + token[strlen(token)-1] = 0; + v[2] = atof ( token ); + } else { + v[2] = atof ( token ); + if ( bracket ) { + Shader_ParseString ( ptr ); + } + } +} + +/*static void Shader_ParseSkySides ( char **ptr, image_t *images ) +{ + int i; + char *token; + char path[MAX_QPATH]; + static char *suf[6] = { "rt", "bk", "lf", "ft", "up", "dn" }; + + token = Shader_ParseString ( ptr ); + for ( i = 0; i < 6; i++ ) + { + if ( token[0] == '-' ) { + images[i] = 0; + } else { + Com_sprintf ( path, sizeof(path), "%s_%s", token, suf[i] ); + images[i] = Mod_LoadHiResTexture ( path, true, false, true);//|IT_SKY ); + } + } +} +*/ +static void Shader_ParseFunc ( char **ptr, shaderfunc_t *func ) +{ + char *token; + + token = Shader_ParseString ( ptr ); + if ( !Q_stricmp (token, "sin") ) { + func->type = SHADER_FUNC_SIN; + } else if ( !Q_stricmp (token, "triangle") ) { + func->type = SHADER_FUNC_TRIANGLE; + } else if ( !Q_stricmp (token, "square") ) { + func->type = SHADER_FUNC_SQUARE; + } else if ( !Q_stricmp (token, "sawtooth") ) { + func->type = SHADER_FUNC_SAWTOOTH; + } else if (!Q_stricmp (token, "inversesawtooth") ) { + func->type = SHADER_FUNC_INVERSESAWTOOTH; + } else if (!Q_stricmp (token, "noise") ) { + func->type = SHADER_FUNC_NOISE; + } + + func->args[0] = Shader_ParseFloat ( ptr ); + func->args[1] = Shader_ParseFloat ( ptr ); + func->args[2] = Shader_ParseFloat ( ptr ); + func->args[3] = Shader_ParseFloat ( ptr ); +} + +//=========================================================================== + + +enum { + IT_CLAMP = 1<<0, + IT_SKY = 1<<1, + IT_NOMIPMAP = 1<<2, + IT_NOPICMIP = 1<<3 +}; +static int Shader_SetImageFlags ( shader_t *shader ) +{ + int flags = 0; + + if ( shader->flags & SHADER_SKY ) { + flags |= IT_SKY; + } + if ( shader->flags & SHADER_NOMIPMAPS ) { + flags |= IT_NOMIPMAP; + } + if ( shader->flags & SHADER_NOPICMIP ) { + flags |= IT_NOPICMIP; + } + + return flags; +} + +static int Shader_FindImage ( char *name, int flags ) +{ + if ( !Q_stricmp (name, "$whiteimage") ) { + return 0; + } else { + return Mod_LoadHiResTexture(name, !!(flags & IT_NOMIPMAP), true, true);//GL_FindImage ( name, flags ); + } +} + + +/****************** shader keyword functions ************************/ + +static void Shader_Cull ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + char *token; + + shader->flags &= ~(SHADER_CULL_FRONT|SHADER_CULL_BACK); + + token = Shader_ParseString ( ptr ); + if ( !Q_stricmp (token, "disable") || !Q_stricmp (token, "none") || !Q_stricmp (token, "twosided") ) { + } else if ( !Q_stricmp (token, "front") ) { + shader->flags |= SHADER_CULL_FRONT; + } else if ( !Q_stricmp (token, "back") || !Q_stricmp (token, "backside") || !Q_stricmp (token, "backsided") ) { + shader->flags |= SHADER_CULL_BACK; + } else { + shader->flags |= SHADER_CULL_FRONT; + } +} + +static void Shader_NoMipMaps ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + shader->flags |= (SHADER_NOMIPMAPS|SHADER_NOPICMIP); +} + +static void Shader_NoPicMip ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + shader->flags |= SHADER_NOPICMIP; +} + +static void Shader_DeformVertexes ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + char *token; + deformv_t *deformv; + + if ( shader->numdeforms >= SHADER_DEFORM_MAX ) { + return; + } + + deformv = &shader->deforms[shader->numdeforms]; + + token = Shader_ParseString ( ptr ); + if ( !Q_stricmp (token, "wave") ) { + deformv->type = DEFORMV_WAVE; + deformv->args[0] = Shader_ParseFloat ( ptr ); + if ( deformv->args[0] ) { + deformv->args[0] = 1.0f / deformv->args[0]; + } + + Shader_ParseFunc ( ptr, &deformv->func ); + } else if ( !Q_stricmp (token, "normal") ) { + deformv->type = DEFORMV_NORMAL; + deformv->args[0] = Shader_ParseFloat ( ptr ); + deformv->args[1] = Shader_ParseFloat ( ptr ); + } else if ( !Q_stricmp (token, "bulge") ) { + deformv->type = DEFORMV_BULGE; + + Shader_ParseVector ( ptr, deformv->args ); + shader->flags |= SHADER_DEFORMV_BULGE; + } else if ( !Q_stricmp (token, "move") ) { + deformv->type = DEFORMV_MOVE; + + Shader_ParseVector ( ptr, deformv->args ); + Shader_ParseFunc ( ptr, &deformv->func ); + } else if ( !Q_stricmp (token, "autosprite") ) { + deformv->type = DEFORMV_AUTOSPRITE; + shader->flags |= SHADER_AUTOSPRITE; + } else if ( !Q_stricmp (token, "autosprite2") ) { + deformv->type = DEFORMV_AUTOSPRITE2; + shader->flags |= SHADER_AUTOSPRITE; + } else if ( !Q_stricmp (token, "projectionShadow") ) { + deformv->type = DEFORMV_PROJECTION_SHADOW; + } else { + return; + } + + shader->numdeforms++; +} + + +static void Shader_SkyParms ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + //FIZME: skydomes? +/* int i; + skydome_t *skydome; + float skyheight; + + if ( shader->skydome ) { + for ( i = 0; i < 5; i++ ) { + Z_Free ( shader->skydome->meshes[i].xyz_array ); + Z_Free ( shader->skydome->meshes[i].normals_array ); + Z_Free ( shader->skydome->meshes[i].st_array ); + } + + Z_Free ( shader->skydome ); + } + + skydome = (skydome_t *)Z_Malloc ( sizeof(skydome_t) ); + shader->skydome = skydome; + + Shader_ParseSkySides ( ptr, skydome->farbox_textures ); + + skyheight = Shader_ParseFloat ( ptr ); + if ( !skyheight ) { + skyheight = 512.0f; + } + + Shader_ParseSkySides ( ptr, skydome->nearbox_textures ); + + R_CreateSkydome ( shader, skyheight ); +*/ + shader->flags |= SHADER_SKY; + shader->sort = SHADER_SORT_SKY; +} + +static void Shader_FogParms ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + float div; + vec3_t color, fcolor; + +// if ( !r_ignorehwgamma->value ) +// div = 1.0f / pow(2, max(0, floor(r_overbrightbits->value))); +// else + div = 1.0f; + + Shader_ParseVector ( ptr, color ); + VectorScale ( color, div, color ); + ColorNormalize ( color, fcolor ); + + shader->fog_color[0] = FloatToByte ( fcolor[0] ); + shader->fog_color[1] = FloatToByte ( fcolor[1] ); + shader->fog_color[2] = FloatToByte ( fcolor[2] ); + shader->fog_color[3] = 255; + shader->fog_dist = Shader_ParseFloat ( ptr ); + + if ( shader->fog_dist <= 0.0f ) { + shader->fog_dist = 128.0f; + } + shader->fog_dist = 1.0f / shader->fog_dist; +} + +static void Shader_Sort ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + char *token; + + token = Shader_ParseString ( ptr ); + if ( !Q_stricmp( token, "portal" ) ) { + shader->sort = SHADER_SORT_PORTAL; + } else if( !Q_stricmp( token, "sky" ) ) { + shader->sort = SHADER_SORT_SKY; + } else if( !Q_stricmp( token, "opaque" ) ) { + shader->sort = SHADER_SORT_OPAQUE; + } else if( !Q_stricmp( token, "banner" ) ) { + shader->sort = SHADER_SORT_BANNER; + } else if( !Q_stricmp( token, "underwater" ) ) { + shader->sort = SHADER_SORT_UNDERWATER; + } else if( !Q_stricmp( token, "additive" ) ) { + shader->sort = SHADER_SORT_ADDITIVE; + } else if( !Q_stricmp( token, "nearest" ) ) { + shader->sort = SHADER_SORT_NEAREST; + } else { + shader->sort = atoi ( token ); + clamp ( shader->sort, SHADER_SORT_NONE, SHADER_SORT_NEAREST ); + } +} + +static void Shader_Portal ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + shader->sort = SHADER_SORT_PORTAL; +} + +static void Shader_PolygonOffset ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + shader->flags |= SHADER_POLYGONOFFSET; +} + +static void Shader_EntityMergable ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + shader->flags |= SHADER_ENTITY_MERGABLE; +} + +static shaderkey_t shaderkeys[] = +{ + {"cull", Shader_Cull }, + {"skyparms", Shader_SkyParms }, + {"fogparms", Shader_FogParms }, + {"nomipmaps", Shader_NoMipMaps }, + {"nopicmip", Shader_NoPicMip }, + {"polygonoffset", Shader_PolygonOffset }, + {"sort", Shader_Sort }, + {"deformvertexes", Shader_DeformVertexes }, + {"portal", Shader_Portal }, + {"entitymergable", Shader_EntityMergable }, + {NULL, NULL} +}; + +// =============================================================== + +static void Shaderpass_Map ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + int flags; + char *token; + + token = Shader_ParseString ( ptr ); + if ( !Q_stricmp (token, "$lightmap") ) { + pass->tcgen = TC_GEN_LIGHTMAP; + pass->flags |= SHADER_PASS_LIGHTMAP; + pass->anim_frames[0] = 0; + } else { + flags = Shader_SetImageFlags ( shader ); + + pass->tcgen = TC_GEN_BASE; + pass->anim_frames[0] = Shader_FindImage ( token, flags ); + + if ( !pass->anim_frames[0] ) { + pass->anim_frames[0] = 0;//FIZME: r_notexture; + Con_DPrintf ( S_COLOR_YELLOW "Shader %s has a stage with no image: %s.\n", shader->name, token ); + } + } +} + +static void Shaderpass_AnimMap ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + int flags; + char *token; + int image; + + flags = Shader_SetImageFlags ( shader ); + + pass->tcgen = TC_GEN_BASE; + pass->flags |= SHADER_PASS_ANIMMAP; + pass->anim_fps = (int)Shader_ParseFloat ( ptr ); + pass->anim_numframes = 0; + + for ( ; ; ) { + token = Shader_ParseString ( ptr ); + if ( !token[0] ) { + break; + } + + if ( pass->anim_numframes < SHADER_ANIM_FRAMES_MAX ) { + image = Shader_FindImage ( token, flags ); + + if ( !image ) { + pass->anim_frames[pass->anim_numframes++] = 0;//fizme: r_notexture; + Con_DPrintf ( S_COLOR_YELLOW "Shader %s has an animmap with no image: %s.\n", shader->name, token ); + } else { + pass->anim_frames[pass->anim_numframes++] = image; + } + } + } +} + +static void Shaderpass_ClampMap ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + int flags; + char *token; + + token = Shader_ParseString ( ptr ); + flags = Shader_SetImageFlags ( shader ); + + pass->tcgen = TC_GEN_BASE; + pass->anim_frames[0] = Shader_FindImage ( token, flags | IT_CLAMP ); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + if ( !pass->anim_frames[0] ) { + pass->anim_frames[0] = 0;//fizme:r_notexture; + Con_DPrintf ( S_COLOR_YELLOW "Shader %s has a stage with no image: %s.\n", shader->name, token ); + } +} + +static void Shaderpass_VideoMap ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ +/* char *token; + char name[MAX_OSPATH]; + + token = Shader_ParseString ( ptr ); + COM_StripExtension ( token, name ); + + if ( pass->cin ) + Z_Free ( pass->cin ); + + pass->cin = (cinematics_t *)Z_Malloc ( sizeof(cinematics_t) ); + pass->cin->frame = -1; + Com_sprintf ( pass->cin->name, sizeof(pass->cin->name), "video/%s.RoQ", name ); +*/ + pass->flags |= SHADER_PASS_VIDEOMAP; + shader->flags |= SHADER_VIDEOMAP; +} + +static void Shaderpass_RGBGen ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + char *token; + + token = Shader_ParseString ( ptr ); + if ( !Q_stricmp (token, "identitylighting") ) { + pass->rgbgen = RGB_GEN_IDENTITY_LIGHTING; + } else if ( !Q_stricmp (token, "identity") ) { + pass->rgbgen = RGB_GEN_IDENTITY; + } else if ( !Q_stricmp (token, "wave") ) { + pass->rgbgen = RGB_GEN_WAVE; + + Shader_ParseFunc ( ptr, &pass->rgbgen_func ); + } else if ( !Q_stricmp(token, "entity") ) { + pass->rgbgen = RGB_GEN_ENTITY; + } else if ( !Q_stricmp (token, "oneMinusEntity") ) { + pass->rgbgen = RGB_GEN_ONE_MINUS_ENTITY; + } else if ( !Q_stricmp (token, "vertex")) { + pass->rgbgen = RGB_GEN_VERTEX; + } else if ( !Q_stricmp (token, "oneMinusVertex") ) { + pass->rgbgen = RGB_GEN_ONE_MINUS_VERTEX; + } else if ( !Q_stricmp (token, "lightingDiffuse") ) { + pass->rgbgen = RGB_GEN_LIGHTING_DIFFUSE; + } else if ( !Q_stricmp (token, "exactvertex") ) { + pass->rgbgen = RGB_GEN_EXACT_VERTEX; + } else if ( !Q_stricmp (token, "const") || !Q_stricmp (token, "constant") ) { + pass->rgbgen = RGB_GEN_CONST; + pass->rgbgen_func.type = SHADER_FUNC_CONSTANT; + + Shader_ParseVector ( ptr, pass->rgbgen_func.args ); + } +} + +static void Shaderpass_AlphaGen ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + char *token; + + token = Shader_ParseString ( ptr ); + if ( !Q_stricmp (token, "portal") ) { + pass->alphagen = ALPHA_GEN_PORTAL; + shader->flags |= SHADER_AGEN_PORTAL; + } else if ( !Q_stricmp (token, "vertex") ) { + pass->alphagen = ALPHA_GEN_VERTEX; + } else if ( !Q_stricmp (token, "entity") ) { + pass->alphagen = ALPHA_GEN_ENTITY; + } else if ( !Q_stricmp (token, "wave") ) { + pass->alphagen = ALPHA_GEN_WAVE; + + Shader_ParseFunc ( ptr, &pass->alphagen_func ); + } else if ( !Q_stricmp (token, "lightingspecular") ) { + pass->alphagen = ALPHA_GEN_SPECULAR; + } else if ( !Q_stricmp (token, "const") || !Q_stricmp (token, "constant") ) { + pass->alphagen = ALPHA_GEN_CONST; + pass->alphagen_func.type = SHADER_FUNC_CONSTANT; + pass->alphagen_func.args[0] = fabs( Shader_ParseFloat (ptr) ); + } +} + +static void Shaderpass_BlendFunc ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + char *token; + + token = Shader_ParseString ( ptr ); + if ( !Q_stricmp (token, "blend") ) { + pass->blendsrc = GL_SRC_ALPHA; + pass->blenddst = GL_ONE_MINUS_SRC_ALPHA; + } else if ( !Q_stricmp (token, "filter") ) { + pass->blendsrc = GL_DST_COLOR; + pass->blenddst = GL_ZERO; + } else if ( !Q_stricmp (token, "add") ) { + pass->blendsrc = pass->blenddst = GL_ONE; + } else { + int i; + unsigned int *blend; + + for ( i = 0; i < 2; i++ ) + { + blend = (i == 0) ? &pass->blendsrc : &pass->blenddst; + + if ( !Q_stricmp ( token, "gl_zero") ) + *blend = GL_ZERO; + else if ( !Q_stricmp (token, "gl_one") ) + *blend = GL_ONE; + else if ( !Q_stricmp (token, "gl_dst_color") ) + *blend = GL_DST_COLOR; + else if ( !Q_stricmp (token, "gl_one_minus_src_alpha") ) + *blend = GL_ONE_MINUS_SRC_ALPHA; + else if ( !Q_stricmp (token, "gl_src_alpha") ) + *blend = GL_SRC_ALPHA; + else if ( !Q_stricmp (token, "gl_src_color") ) + *blend = GL_SRC_COLOR; + else if ( !Q_stricmp (token, "gl_one_minus_dst_color") ) + *blend = GL_ONE_MINUS_DST_COLOR; + else if ( !Q_stricmp (token, "gl_one_minus_src_color") ) + *blend = GL_ONE_MINUS_SRC_COLOR; + else if ( !Q_stricmp (token, "gl_dst_alpha") ) + *blend = GL_DST_ALPHA; + else if ( !Q_stricmp (token, "gl_one_minus_dst_alpha") ) + *blend = GL_ONE_MINUS_DST_ALPHA; + else + *blend = GL_ONE; + + if ( !i ) { + token = Shader_ParseString ( ptr ); + } + } + } + + pass->flags |= SHADER_PASS_BLEND; +} + +static void Shaderpass_AlphaFunc ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + char *token; + + token = Shader_ParseString ( ptr ); + if ( !Q_stricmp (token, "gt0") ) { + pass->alphafunc = SHADER_ALPHA_GT0; + } else if ( !Q_stricmp (token, "lt128") ) { + pass->alphafunc = SHADER_ALPHA_LT128; + } else if ( !Q_stricmp (token, "ge128") ) { + pass->alphafunc = SHADER_ALPHA_GE128; + } else { + return; + } + + pass->flags |= SHADER_PASS_ALPHAFUNC; +} + +static void Shaderpass_DepthFunc ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + char *token; + + token = Shader_ParseString ( ptr ); + if ( !Q_stricmp (token, "equal") ) + pass->depthfunc = GL_EQUAL; + else if ( !Q_stricmp (token, "lequal") ) + pass->depthfunc = GL_LEQUAL; + else if ( !Q_stricmp (token, "gequal") ) + pass->depthfunc = GL_GEQUAL; +} + +static void Shaderpass_DepthWrite ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + shader->flags |= SHADER_DEPTHWRITE; + pass->flags |= SHADER_PASS_DEPTHWRITE; +} + +static void Shaderpass_TcMod ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + int i; + tcmod_t *tcmod; + char *token; + + if (pass->numtcmods >= SHADER_TCMOD_MAX) { + return; + } + + tcmod = &pass->tcmods[pass->numtcmods]; + + token = Shader_ParseString ( ptr ); + if ( !Q_stricmp (token, "rotate") ) { + tcmod->args[0] = -Shader_ParseFloat ( ptr ) / 360.0f; + if ( !tcmod->args[0] ) { + return; + } + + tcmod->type = SHADER_TCMOD_ROTATE; + } else if ( !Q_stricmp (token, "scale") ) { + tcmod->args[0] = Shader_ParseFloat ( ptr ); + tcmod->args[1] = Shader_ParseFloat ( ptr ); + tcmod->type = SHADER_TCMOD_SCALE; + } else if ( !Q_stricmp (token, "scroll") ) { + tcmod->args[0] = Shader_ParseFloat ( ptr ); + tcmod->args[1] = Shader_ParseFloat ( ptr ); + tcmod->type = SHADER_TCMOD_SCROLL; + } else if ( !Q_stricmp (token, "stretch") ) { + shaderfunc_t func; + + Shader_ParseFunc ( ptr, &func ); + + tcmod->args[0] = func.type; + for (i = 1; i < 5; ++i) + tcmod->args[i] = func.args[i-1]; + tcmod->type = SHADER_TCMOD_STRETCH; + } else if ( !Q_stricmp (token, "transform") ) { + for (i = 0; i < 6; ++i) + tcmod->args[i] = Shader_ParseFloat ( ptr ); + tcmod->type = SHADER_TCMOD_TRANSFORM; + } else if ( !Q_stricmp (token, "turb") ) { + for (i = 0; i < 4; i++) + tcmod->args[i] = Shader_ParseFloat ( ptr ); + tcmod->type = SHADER_TCMOD_TURB; + } else { + return; + } + + pass->numtcmods++; +} + + +static void Shaderpass_TcGen ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + char *token; + + token = Shader_ParseString ( ptr ); + if ( !Q_stricmp (token, "base") ) { + pass->tcgen = TC_GEN_BASE; + } else if ( !Q_stricmp (token, "lightmap") ) { + pass->tcgen = TC_GEN_LIGHTMAP; + } else if ( !Q_stricmp (token, "environment") ) { + pass->tcgen = TC_GEN_ENVIRONMENT; + } else if ( !Q_stricmp (token, "vector") ) { + pass->tcgen = TC_GEN_BASE; + } +} + +static void Shaderpass_Detail ( shader_t *shader, shaderpass_t *pass, char **ptr ) +{ + pass->flags |= SHADER_PASS_DETAIL; +} + +static shaderkey_t shaderpasskeys[] = +{ + {"rgbgen", Shaderpass_RGBGen }, + {"blendfunc", Shaderpass_BlendFunc }, + {"depthfunc", Shaderpass_DepthFunc }, + {"depthwrite", Shaderpass_DepthWrite }, + {"alphafunc", Shaderpass_AlphaFunc }, + {"tcmod", Shaderpass_TcMod }, + {"map", Shaderpass_Map }, + {"animmap", Shaderpass_AnimMap }, + {"clampmap", Shaderpass_ClampMap }, + {"videomap", Shaderpass_VideoMap }, + {"tcgen", Shaderpass_TcGen }, + {"alphagen", Shaderpass_AlphaGen }, + {"detail", Shaderpass_Detail }, + {NULL, NULL } +}; + +// =============================================================== + +int Shader_InitCallback (char *name, int size, void *param) +{ + Shader_MakeCache(name+8); + + return true; +} + +qboolean Shader_Init (void) +{ + COM_EnumerateFiles("scripts/*.shader", Shader_InitCallback, NULL); + + /* + char *dirptr; + int i, dirlen, numdirs; + + Con_Printf ( "Initializing Shaders:\n" ); + + numdirs = FS_GetFileList ( "scripts", "shader", shaderbuf, sizeof(shaderbuf) ); + if ( !numdirs ) { + Con_Printf ("Could not find any shaders!"); + return false; + } + + // now load all the scripts + dirptr = shaderbuf; + memset ( shader_hash, 0, sizeof(shadercache_t *)*HASH_SIZE ); + + for (i=0; i= size ) + break; + + token = COM_ParseExt ( &ptr, true ); + if ( !token[0] || ptr - buf >= size ) + break; + + t = NULL; + Shader_GetPathAndOffset ( token, &t, &i ); + if ( t ) { + ptr = Shader_Skip ( ptr ); + continue; + } + + key = Hash_Key ( token, HASH_SIZE ); + + cache = ( shadercache_t * )Z_Malloc ( sizeof(shadercache_t) ); + cache->hash_next = shader_hash[key]; + cache->path = path; + cache->offset = ptr - buf; + Com_sprintf ( cache->name, MAX_QPATH, token ); + shader_hash[key] = cache; + + ptr = Shader_Skip ( ptr ); + } while ( ptr ); + + FS_FreeFile ( buf ); +} + +char *Shader_Skip ( char *ptr ) +{ + char *tok; + int brace_count; + + // Opening brace + tok = COM_ParseExt ( &ptr, true ); + + if (!ptr) + return NULL; + + if ( tok[0] != '{' ) { + tok = COM_ParseExt ( &ptr, true ); + } + + for (brace_count = 1; brace_count > 0 ; ptr++) + { + tok = COM_ParseExt ( &ptr, true ); + + if ( !tok[0] ) + return NULL; + + if (tok[0] == '{') { + brace_count++; + } else if (tok[0] == '}') { + brace_count--; + } + } + + return ptr; +} + +static void Shader_GetPathAndOffset ( char *name, char **path, unsigned int *offset ) +{ + unsigned int key; + shadercache_t *cache; + + key = Hash_Key ( name, HASH_SIZE ); + cache = shader_hash[key]; + + for ( ; cache; cache = cache->hash_next ) { + if ( !Q_stricmp (cache->name, name) ) { + *path = cache->path; + *offset = cache->offset; + return; + } + } + + path = NULL; +} + +void Shader_FreePass (shaderpass_t *pass) +{ + if ( pass->flags & SHADER_PASS_VIDEOMAP ) { +/* GL_StopCinematic ( pass->cin ); + Z_Free ( pass->cin ); + pass->cin = NULL; +*/ } +} + +void Shader_Free (shader_t *shader) +{ + int i; + shaderpass_t *pass; + + if ( shader->flags & SHADER_SKY ) { +/* for ( i = 0; i < 5; i++ ) { + Z_Free ( shader->skydome->meshes[i].xyz_array ); + Z_Free ( shader->skydome->meshes[i].normals_array ); + Z_Free ( shader->skydome->meshes[i].st_array ); + } + + Z_Free ( shader->skydome ); +*/ } + + pass = shader->passes; + for ( i = 0; i < shader->numpasses; i++, pass++ ) { + Shader_FreePass ( pass ); + } +} + +void Shader_Shutdown (void) +{ + int i; + shader_t *shader; + shadercache_t *cache, *cache_next; + + shader = r_shaders; + for (i = 0; i < MAX_SHADERS; i++, shader++) + { + if ( !shader->registration_sequence ) + continue; + + Shader_Free ( shader ); + } + + for ( i = 0; i < HASH_SIZE; i++ ) { + cache = shader_hash[i]; + + for ( ; cache; cache = cache_next ) { + cache_next = cache->hash_next; + cache->hash_next = NULL; + Z_Free ( cache ); + } + } + + memset (r_shaders, 0, sizeof(shader_t)*MAX_SHADERS); + + memset (shader_hash, 0, sizeof(shader_hash)); +} + +void Shader_SetBlendmode ( shaderpass_t *pass ) +{ + if ( !pass->anim_frames[0] && !(pass->flags & SHADER_PASS_LIGHTMAP) ) { + pass->blendmode = 0; + return; + } + + if ( !(pass->flags & SHADER_PASS_BLEND) && qglMTexCoord2fSGIS ) { + if ( (pass->rgbgen == RGB_GEN_IDENTITY) && (pass->alphagen == ALPHA_GEN_IDENTITY) ) { + pass->blendmode = GL_REPLACE; + } else { + pass->blendsrc = GL_ONE; + pass->blenddst = GL_ZERO; + pass->blendmode = GL_MODULATE; + } + return; + } + + if ( pass->blendsrc == GL_ZERO && pass->blenddst == GL_SRC_COLOR || + pass->blendsrc == GL_DST_COLOR && pass->blenddst == GL_ZERO ) + pass->blendmode = GL_MODULATE; + else if ( pass->blendsrc == GL_ONE && pass->blenddst == GL_ONE ) + pass->blendmode = GL_ADD; + else if ( pass->blendsrc == GL_SRC_ALPHA && pass->blenddst == GL_ONE_MINUS_SRC_ALPHA ) + pass->blendmode = GL_DECAL; + else + pass->blendmode = 0; +} + +void Shader_Readpass (shader_t *shader, char **ptr) +{ + char *token; + shaderpass_t *pass; + qboolean ignore; + static shader_t dummy; + + if ( shader->numpasses >= SHADER_PASS_MAX ) { + ignore = true; + shader = &dummy; + shader->numpasses = 1; + pass = shader->passes; + } else { + ignore = false; + pass = &shader->passes[shader->numpasses++]; + } + + // Set defaults + pass->flags = 0; + pass->anim_frames[0] = 0; + pass->anim_numframes = 0; + pass->depthfunc = GL_LEQUAL; + pass->rgbgen = RGB_GEN_UNKNOWN; + pass->alphagen = ALPHA_GEN_IDENTITY; + pass->tcgen = TC_GEN_BASE; + pass->numtcmods = 0; + + // default to R_RenderMeshGeneric + pass->numMergedPasses = 1; + pass->flush = R_RenderMeshGeneric; + + while ( ptr ) + { + token = COM_ParseExt (ptr, true); + + if ( !token[0] ) { + continue; + } else if ( token[0] == '}' ) { + break; + } else if ( Shader_Parsetok (shader, pass, shaderpasskeys, token, ptr) ) { + break; + } + } + + // check some things + if ( ignore ) { + Shader_Free ( shader ); + return; + } + + if ( (pass->blendsrc == GL_ONE) && (pass->blenddst == GL_ZERO) ) { + pass->flags |= SHADER_PASS_DEPTHWRITE; + shader->flags |= SHADER_DEPTHWRITE; + } + + switch (pass->rgbgen) + { + case RGB_GEN_IDENTITY_LIGHTING: + case RGB_GEN_IDENTITY: + case RGB_GEN_CONST: + case RGB_GEN_WAVE: + case RGB_GEN_ENTITY: + case RGB_GEN_ONE_MINUS_ENTITY: + case RGB_GEN_UNKNOWN: // assume RGB_GEN_IDENTITY or RGB_GEN_IDENTITY_LIGHTING + + switch (pass->alphagen) + { + case ALPHA_GEN_IDENTITY: + case ALPHA_GEN_CONST: + case ALPHA_GEN_WAVE: + case ALPHA_GEN_ENTITY: + pass->flags |= SHADER_PASS_NOCOLORARRAY; + break; + default: + break; + } + + break; + default: + break; + } + + Shader_SetBlendmode ( pass ); + + if ( (shader->flags & SHADER_SKY) && (shader->flags & SHADER_DEPTHWRITE) ) { + if ( pass->flags & SHADER_PASS_DEPTHWRITE ) { + pass->flags &= ~SHADER_PASS_DEPTHWRITE; + } + } +} + +static qboolean Shader_Parsetok (shader_t *shader, shaderpass_t *pass, shaderkey_t *keys, char *token, char **ptr) +{ + shaderkey_t *key; + + for (key = keys; key->keyword != NULL; key++) + { + if (!Q_stricmp (token, key->keyword)) + { + if (key->func) + key->func ( shader, pass, ptr ); + + return ( ptr && *ptr && **ptr == '}' ); + } + } + + // Next Line + while (ptr) + { + token = COM_ParseExt ( ptr, false ); + if ( !token[0] ) { + break; + } + } + + return false; +} + +void Shader_SetPassFlush ( shaderpass_t *pass, shaderpass_t *pass2 ) +{ + if ( ((pass->flags & SHADER_PASS_DETAIL) && !r_detailtextures.value) || + ((pass2->flags & SHADER_PASS_DETAIL) && !r_detailtextures.value) || + (pass->flags & SHADER_PASS_VIDEOMAP) || (pass2->flags & SHADER_PASS_VIDEOMAP) || + ((pass->flags & SHADER_PASS_ALPHAFUNC) && (pass2->depthfunc != GL_EQUAL)) ) { + return; + } + + if ( pass2->rgbgen != RGB_GEN_IDENTITY || pass2->alphagen != ALPHA_GEN_IDENTITY ) { + return; + } + if (pass->rgbgen != RGB_GEN_IDENTITY || pass->alphagen != ALPHA_GEN_IDENTITY ) + return; + + // check if we can use R_RenderMeshCombined +/* + if ( gl_config.tex_env_combine || gl_config.nv_tex_env_combine4 ) + { + if ( pass->blendmode == GL_REPLACE ) + { + if ((pass2->blendmode == GL_DECAL && gl_config.tex_env_combine) || + (pass2->blendmode == GL_ADD && gl_config.env_add) || + (pass2->blendmode && pass2->blendmode != GL_ADD) || gl_config.nv_tex_env_combine4 ) + { + pass->flush = R_RenderMeshCombined; + } + } + else if ( pass->blendmode == GL_ADD && + pass2->blendmode == GL_ADD && gl_config.env_add ) + { + pass->flush = R_RenderMeshCombined; + } else if ( pass->blendmode == GL_MODULATE && + pass2->blendmode == GL_MODULATE ) + { + pass->flush = R_RenderMeshCombined; + } + } + else + */ + if ( qglMTexCoord2fSGIS ) + { + // check if we can use R_RenderMeshMultitextured + if ( pass->blendmode == GL_REPLACE ) + { + if ( pass2->blendmode == GL_ADD && gl_config.env_add ) + { + pass->flush = R_RenderMeshMultitextured; + pass->numMergedPasses = 2; + } + else if ( pass2->blendmode && pass2->blendmode != GL_DECAL ) + { + pass->flush = R_RenderMeshMultitextured; + pass->numMergedPasses = 2; + } + } + else if ( pass->blendmode == GL_MODULATE && + pass2->blendmode == GL_MODULATE ) + { + pass->flush = R_RenderMeshMultitextured; + } + else if ( pass->blendmode == GL_ADD && + pass2->blendmode == GL_ADD && gl_config.env_add ) + { +// pass->flush = R_RenderMeshCombined; + } + } + + if ( pass->flush != R_RenderMeshGeneric ) { + pass->numMergedPasses = 2; + } +} + +void Shader_SetFeatures ( shader_t *s ) +{ + int i; + qboolean trnormals; + shaderpass_t *pass; + + s->features = MF_NONE; + + for ( i = 0, trnormals = true; i < s->numdeforms; i++ ) { + switch ( s->deforms[i].type ) { + case DEFORMV_BULGE: + case DEFORMV_WAVE: + trnormals = false; + case DEFORMV_NORMAL: + s->features |= MF_NORMALS; + break; + case DEFORMV_MOVE: + break; + default: + trnormals = false; + break; + } + } + + if ( trnormals ) { + s->features |= MF_TRNORMALS; + } + + for ( i = 0, pass = s->passes; i < s->numpasses; i++, pass++ ) { + switch ( pass->rgbgen ) { + case RGB_GEN_LIGHTING_DIFFUSE: + s->features |= MF_NORMALS; + break; + case RGB_GEN_VERTEX: + case RGB_GEN_ONE_MINUS_VERTEX: + case RGB_GEN_EXACT_VERTEX: + s->features |= MF_COLORS; + break; + } + + switch ( pass->alphagen ) { + case ALPHA_GEN_SPECULAR: + s->features |= MF_NORMALS; + break; + case ALPHA_GEN_VERTEX: + s->features |= MF_COLORS; + break; + } + + switch ( pass->tcgen ) { + default: + s->features |= MF_STCOORDS; + break; + case TC_GEN_LIGHTMAP: + s->features |= MF_LMCOORDS; + break; + case TC_GEN_ENVIRONMENT: + s->features |= MF_NORMALS; + break; + } + } +} + +void Shader_Finish ( shader_t *s ) +{ + int i; + shaderpass_t *pass; + + if ( !Q_stricmp (s->name, "flareShader") ) { + s->flags |= SHADER_FLARE; + } + + if ( !s->numpasses && !s->sort ) { + s->sort = SHADER_SORT_ADDITIVE; + return; + } + + if ( (s->flags & SHADER_POLYGONOFFSET) && !s->sort ) { + s->sort = SHADER_SORT_ADDITIVE - 1; + } + + if ( r_vertexlight.value ) + { + // do we have a lightmap pass? + pass = s->passes; + for ( i = 0; i < s->numpasses; i++, pass++ ) + { + if ( pass->flags & SHADER_PASS_LIGHTMAP ) + break; + } + + if ( i == s->numpasses ) + { + goto done; + } + + // try to find pass with rgbgen set to RGB_GEN_VERTEX + pass = s->passes; + for ( i = 0; i < s->numpasses; i++, pass++ ) + { + if ( pass->rgbgen == RGB_GEN_VERTEX ) + break; + } + + if ( i < s->numpasses ) + { // we found it + pass->flags |= SHADER_CULL_FRONT; + pass->flags &= ~(SHADER_PASS_BLEND|SHADER_PASS_ANIMMAP); + pass->blendmode = 0; + pass->flags |= SHADER_PASS_DEPTHWRITE; + pass->alphagen = ALPHA_GEN_IDENTITY; + pass->numMergedPasses = 1; + pass->flush = R_RenderMeshGeneric; + s->flags |= SHADER_DEPTHWRITE; + s->sort = SHADER_SORT_OPAQUE; + s->numpasses = 1; + memcpy ( &s->passes[0], pass, sizeof(shaderpass_t) ); + } + else + { // we didn't find it - simply remove all lightmap passes + pass = s->passes; + for ( i = 0; i < s->numpasses; i++, pass++ ) + { + if ( pass->flags & SHADER_PASS_LIGHTMAP ) + break; + } + + if ( i == s->numpasses -1 ) + { + s->numpasses--; + } + else if ( i < s->numpasses - 1 ) + { + for ( ; i < s->numpasses - 1; i++, pass++ ) + { + memcpy ( pass, &s->passes[i+1], sizeof(shaderpass_t) ); + } + s->numpasses--; + } + + if ( s->passes[0].numtcmods ) + { + pass = s->passes; + for ( i = 0; i < s->numpasses; i++, pass++ ) + { + if ( !pass->numtcmods ) + break; + } + + memcpy ( &s->passes[0], pass, sizeof(shaderpass_t) ); + } + + s->passes[0].rgbgen = RGB_GEN_VERTEX; + s->passes[0].alphagen = ALPHA_GEN_IDENTITY; + s->passes[0].blendmode = 0; + s->passes[0].flags &= ~(SHADER_PASS_BLEND|SHADER_PASS_ANIMMAP|SHADER_PASS_NOCOLORARRAY); + s->passes[0].flags |= SHADER_PASS_DEPTHWRITE; + s->passes[0].numMergedPasses = 1; + s->passes[0].flush = R_RenderMeshGeneric; + s->numpasses = 1; + s->flags |= SHADER_DEPTHWRITE; + } + } +done:; + + pass = s->passes; + for ( i = 0; i < s->numpasses; i++, pass++ ) { + if ( !(pass->flags & SHADER_PASS_BLEND) ) { + break; + } + } + + // all passes have blendfuncs + if ( i == s->numpasses ) { + int opaque; + + opaque = -1; + pass = s->passes; + for ( i = 0; i < s->numpasses; i++, pass++ ) { + if ( (pass->blendsrc == GL_ONE) && (pass->blenddst == GL_ZERO) ) { + opaque = i; + } + + if ( pass->rgbgen == RGB_GEN_UNKNOWN ) { + if ( !s->fog_dist && !(pass->flags & SHADER_PASS_LIGHTMAP) ) + pass->rgbgen = RGB_GEN_IDENTITY_LIGHTING; + else + pass->rgbgen = RGB_GEN_IDENTITY; + } + } + + if ( !( s->flags & SHADER_SKY ) && !s->sort ) { + if ( opaque == -1 ) + s->sort = SHADER_SORT_ADDITIVE; + else if ( s->passes[opaque].flags & SHADER_PASS_ALPHAFUNC ) + s->sort = SHADER_SORT_OPAQUE + 1; + else + s->sort = SHADER_SORT_OPAQUE; + } + } else { + int j; + shaderpass_t *sp; + + sp = s->passes; + for ( j = 0; j < s->numpasses; j++, sp++ ) { + if ( sp->rgbgen == RGB_GEN_UNKNOWN ) { + sp->rgbgen = RGB_GEN_IDENTITY; + } + } + + if ( !s->sort ) { + if ( pass->flags & SHADER_PASS_ALPHAFUNC ) + s->sort = SHADER_SORT_OPAQUE + 1; + } + + if ( !( s->flags & SHADER_DEPTHWRITE ) && + !( s->flags & SHADER_SKY ) ) + { + pass->flags |= SHADER_PASS_DEPTHWRITE; + s->flags |= SHADER_DEPTHWRITE; + } + } + + if ( s->numpasses >= 2 ) + { + pass = s->passes; + for ( i = 0; i < s->numpasses; ) + { + if ( i == s->numpasses - 1 ) + break; + + pass = s->passes + i; + Shader_SetPassFlush ( pass, pass + 1 ); + + i += pass->numMergedPasses; + } + } + + if ( !s->sort ) { + s->sort = SHADER_SORT_OPAQUE; + } + + if ( (s->flags & SHADER_SKY) && (s->flags & SHADER_DEPTHWRITE) ) { + s->flags &= ~SHADER_DEPTHWRITE; + } + + Shader_SetFeatures ( s ); +} +/* +void Shader_UpdateRegistration (void) +{ + int i, j, l; + shader_t *shader; + shaderpass_t *pass; + +#ifdef FIZME + if ( chars_shader ) + chars_shader->registration_sequence = registration_sequence; + + if ( propfont1_shader ) + propfont1_shader->registration_sequence = registration_sequence; + + if ( propfont1_glow_shader ) + propfont1_glow_shader->registration_sequence = registration_sequence; + + if ( propfont2_shader ) + propfont2_shader->registration_sequence = registration_sequence; + + if ( particle_shader ) + particle_shader->registration_sequence = registration_sequence; +#endif + shader = r_shaders; + for (i = 0; i < MAX_SHADERS; i++, shader++) + { + if ( !shader->registration_sequence ) + continue; + if ( shader->registration_sequence != registration_sequence ) { + Shader_Free ( shader ); + shader->registration_sequence = 0; + continue; + } +#ifdef FIZME: skydomes + if ( shader->flags & SHADER_SKY && shader->skydome ) { + if ( shader->skydome->farbox_textures[0] ) { + for ( j = 0; j < 6; j++ ) { + if ( shader->skydome->farbox_textures[j] ) + shader->skydome->farbox_textures[j]->registration_sequence = registration_sequence; + } + } + + if ( shader->skydome->nearbox_textures[0] ) { + for ( j = 0; j < 6; j++ ) { + if ( shader->skydome->nearbox_textures[j] ) + shader->skydome->nearbox_textures[j]->registration_sequence = registration_sequence; + } + } + } +#endif + pass = shader->passes; + for (j = 0; j < shader->numpasses; j++, pass++) + { + if ( pass->flags & SHADER_PASS_ANIMMAP ) { + for (l = 0; l < pass->anim_numframes; l++) + { + if ( pass->anim_frames[l] ) + pass->anim_frames[l]->registration_sequence = registration_sequence; + } + } else if ( pass->flags & SHADER_PASS_VIDEOMAP ) { + // Shader_RunCinematic will do the job +// pass->cin->frame = -1; + } else if ( !(pass->flags & SHADER_PASS_LIGHTMAP) ) { + if ( pass->anim_frames[0] ) + pass->anim_frames[0]->registration_sequence = registration_sequence; + } + } + } +} +*/ +void Shader_UploadCinematic (shader_t *shader) +{ + int j; + shaderpass_t *pass; + + // upload cinematics + pass = shader->passes; + for ( j = 0; j < shader->numpasses; j++, pass++ ) { + if ( pass->flags & SHADER_PASS_VIDEOMAP ) { +// pass->anim_frames[0] = GL_ResampleCinematicFrame ( pass ); + } + } +} + +void Shader_RunCinematic (void) +{ + int i, j; + shader_t *shader; + shaderpass_t *pass; + + shader = r_shaders; + for ( i = 0; i < MAX_SHADERS; i++, shader++ ) { + if ( !shader->registration_sequence ) + continue; + if ( !(shader->flags & SHADER_VIDEOMAP) ) + continue; + + pass = shader->passes; + for ( j = 0; j < shader->numpasses; j++, pass++ ) { + if ( !(pass->flags & SHADER_PASS_VIDEOMAP) ) + continue; + +//FIXME: videomaps + // reinitialize +/* if ( pass->cin->frame == -1 ) { + GL_StopCinematic ( pass->cin ); + GL_PlayCinematic( pass->cin ); + + if ( pass->cin->time == 0 ) { // not found + pass->flags &= ~SHADER_PASS_VIDEOMAP; + Z_Free ( pass->cin ); + } + + continue; + } + + GL_RunCinematic ( pass->cin ); +*/ + } + } +} + +int R_LoadShader ( char *name, int type ) +{ + int i, f = -1; + unsigned int offset, length = 0; + char shortname[MAX_QPATH], path[MAX_QPATH]; + char *buf = NULL, *ts = NULL; + shader_t *s; + shaderpass_t *pass; + + COM_StripExtension ( name, shortname ); + + // test if already loaded + for (i = 0; i < MAX_SHADERS; i++) + { + if (!r_shaders[i].registration_sequence) + { + if ( f == -1 ) // free shader + f = i; + continue; + } + + if (!Q_stricmp (shortname, r_shaders[i].name) ) + { + r_shaders[i].registration_sequence = 1;//fizme: registration_sequence; + return i; + } + } + + if ( f == -1 ) + { + Sys_Error( "R_LoadShader: Shader limit exceeded."); + return f; + } + + s = &r_shaders[f]; + memset ( s, 0, sizeof( shader_t ) ); + + Com_sprintf ( s->name, MAX_QPATH, shortname ); + + Shader_GetPathAndOffset( shortname, &ts, &offset ); + + if ( ts ) { + Com_sprintf ( path, sizeof(path), "scripts/%s", ts ); + length = FS_LoadFile ( path, (void **)&buf ); + } + + // the shader is in the shader scripts + if ( ts && buf && (offset < length) ) + { + char *ptr, *token; + + // set defaults + s->flags = SHADER_CULL_FRONT; + s->registration_sequence = 1;//fizme: registration_sequence; + + ptr = buf + offset; + token = COM_ParseExt (&ptr, true); + + if ( !ptr || token[0] != '{' ) { + return -1; + } + + while ( ptr ) + { + token = COM_ParseExt (&ptr, true); + + if ( !token[0] ) { + continue; + } else if ( token[0] == '}' ) { + break; + } else if ( token[0] == '{' ) { + Shader_Readpass ( s, &ptr ); + } else if ( Shader_Parsetok (s, NULL, shaderkeys, token, &ptr ) ) { + break; + } + } + + Shader_Finish ( s ); + FS_FreeFile ( buf ); + } + else // make a default shader + { + switch (type) + { + case SHADER_BSP: + pass = &s->passes[0]; + pass->flags = SHADER_PASS_LIGHTMAP | SHADER_PASS_DEPTHWRITE | SHADER_PASS_NOCOLORARRAY; + pass->tcgen = TC_GEN_LIGHTMAP; + pass->anim_frames[0] = 0; + pass->depthfunc = GL_LEQUAL; + pass->blendmode = GL_REPLACE; + pass->alphagen = ALPHA_GEN_IDENTITY; + pass->rgbgen = RGB_GEN_IDENTITY; + pass->numMergedPasses = 2; + + if ( qglMTexCoord2fSGIS ) + { + pass->numMergedPasses = 2; + pass->flush = R_RenderMeshMultitextured; + } + else + { + pass->numMergedPasses = 1; + pass->flush = R_RenderMeshGeneric; + } + + pass = &s->passes[1]; + pass->flags = SHADER_PASS_BLEND | SHADER_PASS_NOCOLORARRAY; + pass->tcgen = TC_GEN_BASE; + pass->anim_frames[0] = Mod_LoadHiResTexture(shortname, true, false, true);//GL_FindImage (shortname, 0); + pass->blendsrc = GL_ZERO; + pass->blenddst = GL_SRC_COLOR; + pass->blendmode = GL_MODULATE; + pass->depthfunc = GL_LEQUAL; + pass->rgbgen = RGB_GEN_IDENTITY; + pass->alphagen = ALPHA_GEN_IDENTITY; + + pass->numMergedPasses = 1; + pass->flush = R_RenderMeshGeneric; + + if ( !pass->anim_frames[0] ) { + Con_DPrintf ( S_COLOR_YELLOW "Shader %s has a stage with no image: %s.\n", s->name, shortname ); + pass->anim_frames[0] = 0;//fizme:r_notexture; + } + + s->numpasses = 2; + s->numdeforms = 0; + s->flags = SHADER_DEPTHWRITE|SHADER_CULL_FRONT; + s->features = MF_STCOORDS|MF_LMCOORDS|MF_TRNORMALS; + s->sort = SHADER_SORT_OPAQUE; + s->registration_sequence = 1;//fizme: registration_sequence; + break; + + case SHADER_BSP_VERTEX: + pass = &s->passes[0]; + pass->tcgen = TC_GEN_BASE; + pass->anim_frames[0] = Mod_LoadHiResTexture(shortname, true, false, true);//GL_FindImage (shortname, 0); + pass->depthfunc = GL_LEQUAL; + pass->flags = SHADER_PASS_DEPTHWRITE; + pass->rgbgen = RGB_GEN_VERTEX; + pass->alphagen = ALPHA_GEN_IDENTITY; + pass->blendmode = GL_MODULATE; + pass->numMergedPasses = 1; + pass->flush = R_RenderMeshGeneric; + + if ( !pass->anim_frames[0] ) { + Con_DPrintf ( S_COLOR_YELLOW "Shader %s has a stage with no image: %s.\n", s->name, shortname ); + pass->anim_frames[0] = 0;//fizme:r_notexture; + } + + s->numpasses = 1; + s->numdeforms = 0; + s->flags = SHADER_DEPTHWRITE|SHADER_CULL_FRONT; + s->features = MF_STCOORDS|MF_COLORS|MF_TRNORMALS; + s->sort = SHADER_SORT_OPAQUE; + s->registration_sequence = 1;//fizme: registration_sequence; + break; + + case SHADER_BSP_FLARE: + pass = &s->passes[0]; + pass->flags = SHADER_PASS_BLEND | SHADER_PASS_NOCOLORARRAY; + pass->blendsrc = GL_ONE; + pass->blenddst = GL_ONE; + pass->blendmode = GL_MODULATE; + pass->anim_frames[0] = Mod_LoadHiResTexture(shortname, true, true, true);//GL_FindImage (shortname, 0); + pass->depthfunc = GL_LEQUAL; + pass->rgbgen = RGB_GEN_VERTEX; + pass->alphagen = ALPHA_GEN_IDENTITY; + pass->numtcmods = 0; + pass->tcgen = TC_GEN_BASE; + pass->numMergedPasses = 1; + pass->flush = R_RenderMeshGeneric; + + if ( !pass->anim_frames[0] ) { + Con_DPrintf ( S_COLOR_YELLOW "Shader %s has a stage with no image: %s.\n", s->name, shortname ); + pass->anim_frames[0] = 0;//fizme:r_notexture; + } + + s->numpasses = 1; + s->numdeforms = 0; + s->flags = SHADER_FLARE; + s->features = MF_STCOORDS|MF_COLORS; + s->sort = SHADER_SORT_ADDITIVE; + s->registration_sequence = 1;//fizme: registration_sequence; + break; + + case SHADER_MD3: + pass = &s->passes[0]; + pass->flags = SHADER_PASS_DEPTHWRITE; + pass->anim_frames[0] = Mod_LoadHiResTexture(shortname, true, true, true);//GL_FindImage (shortname, 0); + pass->depthfunc = GL_LEQUAL; + pass->rgbgen = RGB_GEN_LIGHTING_DIFFUSE; + pass->numtcmods = 0; + pass->tcgen = TC_GEN_BASE; + pass->blendmode = GL_MODULATE; + pass->numMergedPasses = 1; + pass->flush = R_RenderMeshGeneric; + + if ( !pass->anim_frames[0] ) { + Con_DPrintf ( S_COLOR_YELLOW "Shader %s has a stage with no image: %s.\n", s->name, shortname ); + pass->anim_frames[0] = 0;//fizme:r_notexture; + } + + s->numpasses = 1; + s->numdeforms = 0; + s->flags = SHADER_DEPTHWRITE|SHADER_CULL_FRONT; + s->features = MF_STCOORDS|MF_NORMALS; + s->sort = SHADER_SORT_OPAQUE; + s->registration_sequence = 1;//fizme: registration_sequence; + break; + + case SHADER_2D: + pass = &s->passes[0]; + pass->flags = SHADER_PASS_BLEND | SHADER_PASS_NOCOLORARRAY; + pass->blendsrc = GL_SRC_ALPHA; + pass->blenddst = GL_ONE_MINUS_SRC_ALPHA; + pass->blendmode = GL_MODULATE; + pass->anim_frames[0] = Mod_LoadHiResTexture(shortname, false, true, true);//GL_FindImage (shortname, IT_NOPICMIP|IT_NOMIPMAP); + pass->depthfunc = GL_LEQUAL; + pass->rgbgen = RGB_GEN_VERTEX; + pass->alphagen = ALPHA_GEN_VERTEX; + pass->numtcmods = 0; + pass->tcgen = TC_GEN_BASE; + pass->numMergedPasses = 1; + pass->flush = R_RenderMeshGeneric; + + if ( !pass->anim_frames[0] ) { + Con_DPrintf ( S_COLOR_YELLOW "Shader %s has a stage with no image: %s.\n", s->name, shortname ); + pass->anim_frames[0] = 0;//fizme:r_notexture; + } + + s->numpasses = 1; + s->numdeforms = 0; + s->flags = SHADER_NOPICMIP|SHADER_NOMIPMAPS; + s->features = MF_STCOORDS|MF_COLORS; + s->sort = SHADER_SORT_ADDITIVE; + s->registration_sequence = 1;//fizme: registration_sequence; + break; + + default: + return -1; + } + } + + return f; +} + +shader_t *R_RegisterPic (char *name) +{ + return &r_shaders[R_LoadShader (name, SHADER_2D)]; +} + +shader_t *R_RegisterShader (char *name) +{ + return &r_shaders[R_LoadShader (name, SHADER_BSP)]; +} + +shader_t *R_RegisterShader_Vertex (char *name) +{ + return &r_shaders[R_LoadShader (name, SHADER_BSP_VERTEX)]; +} + +shader_t *R_RegisterShader_Flare (char *name) +{ + return &r_shaders[R_LoadShader (name, SHADER_BSP_FLARE)]; +} + +shader_t *R_RegisterSkin (char *name) +{ + return &r_shaders[R_LoadShader (name, SHADER_MD3)]; +} +#endif +