480 lines
No EOL
12 KiB
C
480 lines
No EOL
12 KiB
C
/*
|
|
Copyright (C) 2001-2002 Charles Hollemeersch
|
|
|
|
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.
|
|
|
|
PENTA: the whole file is freakin penta...
|
|
|
|
"Shader" loading an management
|
|
*/
|
|
#include "quakedef.h"
|
|
|
|
#define SHADER_MAX_NAME 128
|
|
#define SHADER_MAX_STAGES 8
|
|
#define SHADER_MAX_TCMOD 8
|
|
#define SHADER_MAX_STATUSES 8
|
|
|
|
typedef enum {STAGE_SIMPLE, STAGE_COLOR, STAGE_BUMP, STAGE_GLOSS} stagetype_t;
|
|
typedef enum {TCMOD_ROTATE, TCMOD_SCROLL, TCMOD_SCALE, TCMOD_STRETCH} tcmodtype_t;
|
|
/**
|
|
* A single stage in the shader,
|
|
* a stage is one block { } in a shader definition
|
|
* this corresponds to a single pass for simple shaders
|
|
* or a single texture definition for bumpmapping.
|
|
*/
|
|
typedef struct tcmod_s {
|
|
float params[7];
|
|
tcmodtype_t type;
|
|
} tcmod_t;
|
|
|
|
typedef struct stage_s {
|
|
stagetype_t type;
|
|
int numtcmods;
|
|
tcmod_t tcmods[SHADER_MAX_TCMOD];
|
|
int numtextures;
|
|
gltexture_t *texture[8]; //animations
|
|
int src_blend, dst_blend; //have special values for bumpmap passes
|
|
int alphatresh;
|
|
} stage_t;
|
|
|
|
#define SURF_NOSHADOW 0x40000 //don't cast stencil shadows
|
|
#define SURF_BUMP 0x80000 //do diffuse bumpmapping and gloss if gloss is enabled too
|
|
#define SURF_GLOSS 0x100000//do gloss
|
|
#define SURF_PPLIGHT 0x200000//do per pixel lighting...
|
|
//if bump is unset it uses a flat bumpmap and no gloss
|
|
//if bump is set and gloss unet it does only diffuse bumps
|
|
|
|
/**
|
|
* A shader, holds the stages and the general info for that shader.
|
|
*/
|
|
typedef struct shader_s {
|
|
char name[SHADER_MAX_NAME];
|
|
int flags;
|
|
int numstages;
|
|
stage_t stages[SHADER_MAX_STAGES];
|
|
stage_t colorStage;
|
|
stage_t bumpStage;
|
|
stage_t glossStage;
|
|
vec3_t fog_color;
|
|
float fog_dist;
|
|
int numstatus;
|
|
struct shader_s *status[SHADER_MAX_STATUSES]; //if the object the shader is on it's status is > 0 this shader will be used insead of the base shader...
|
|
struct shader_s *next; //in the shader linked list
|
|
} shader_t;
|
|
|
|
static shader_t *shaderList;
|
|
|
|
/**
|
|
* Generates a clean stage before one is parsed over it
|
|
*/
|
|
void clearStage(stage_t *stage) {
|
|
memset(stage,0,sizeof(stage_t));
|
|
stage->type = STAGE_SIMPLE;
|
|
}
|
|
|
|
/**
|
|
* Generates a default shader from the given name.
|
|
*/
|
|
void initDefaultStage(char *texname, stage_t *stage, stagetype_t type) {
|
|
memset(stage,0,sizeof(stage_t));
|
|
stage->numtextures = 1;
|
|
if (type == STAGE_BUMP) {
|
|
stage->texture[0] = GL_CacheTexture(texname,true,TEXTURE_NORMAL);
|
|
} else
|
|
stage->texture[0] = GL_CacheTexture(texname,true,TEXTURE_RGB);
|
|
stage->numtcmods = 0;
|
|
stage->type = type;
|
|
}
|
|
|
|
/**
|
|
* Generates a default shader from the given name.
|
|
*/
|
|
void initDefaultShader(char *name, shader_t *shader) {
|
|
char namebuff[256];
|
|
|
|
strncpy(shader->name,name,sizeof(shader->name));
|
|
shader->flags = 0;
|
|
shader->numstages = 3;
|
|
initDefaultStage(name, &shader->stages[0], STAGE_COLOR);
|
|
sprintf(namebuff,"%s%s",name,"_normal");
|
|
initDefaultStage(namebuff, &shader->stages[1], STAGE_BUMP);
|
|
sprintf(namebuff,"%s%s",name,"_gloss");
|
|
initDefaultStage(namebuff, &shader->stages[2], STAGE_GLOSS);
|
|
}
|
|
|
|
/*
|
|
================
|
|
GL_ShaderForName
|
|
|
|
Load a shader, searches for the shader in the shader list
|
|
and returns a pointer to the shader
|
|
================
|
|
*/
|
|
shader_t *GL_ShaderForName(char *name) {
|
|
|
|
shader_t *s;
|
|
s = shaderList;
|
|
|
|
while(s) {
|
|
if (!strcmp(name,s->name)) {
|
|
return s;
|
|
}
|
|
s = s->next;
|
|
}
|
|
|
|
s = malloc(sizeof(shader_t));
|
|
if (!s) {
|
|
Sys_Error("Malloc failed!");
|
|
}
|
|
|
|
//make a shader with the default filenames (_bump, _gloss, _normal)
|
|
initDefaultShader(name,s);
|
|
s->next = shaderList;
|
|
shaderList = s;
|
|
|
|
return s;
|
|
}
|
|
|
|
// parse some stuff but make sure you give some errors
|
|
#define GET_SAFE_TOKEN data = COM_Parse (data);\
|
|
if (!data)\
|
|
Sys_Error ("ED_ParseEntity: EOF without closing brace");\
|
|
if (com_token[0] == '}')\
|
|
Sys_Error ("ED_ParseEntity: '}' not expected here")
|
|
|
|
/**
|
|
* Parse a tcmod command out of the shader file
|
|
*/
|
|
char *ParceTcMod(char *data, stage_t *stage) {
|
|
|
|
GET_SAFE_TOKEN;
|
|
|
|
if (stage->numtcmods >= SHADER_MAX_TCMOD) {
|
|
Sys_Error("More than %i tcmods in stage\n",SHADER_MAX_TCMOD);
|
|
}
|
|
|
|
if (!strcmp(com_token, "rotate")) {
|
|
|
|
stage->tcmods[stage->numtcmods].type = TCMOD_ROTATE;
|
|
GET_SAFE_TOKEN;
|
|
stage->tcmods[stage->numtcmods].params[0] = atoi(com_token);
|
|
|
|
} else if (!strcmp(com_token, "scroll")) {
|
|
|
|
stage->tcmods[stage->numtcmods].type = TCMOD_SCROLL;
|
|
GET_SAFE_TOKEN;
|
|
stage->tcmods[stage->numtcmods].params[0] = atoi(com_token);
|
|
GET_SAFE_TOKEN;
|
|
stage->tcmods[stage->numtcmods].params[1] = atoi(com_token);
|
|
|
|
} else if (!strcmp(com_token, "scale")) {
|
|
|
|
stage->tcmods[stage->numtcmods].type = TCMOD_SCALE;
|
|
GET_SAFE_TOKEN;
|
|
stage->tcmods[stage->numtcmods].params[0] = atoi(com_token);
|
|
GET_SAFE_TOKEN;
|
|
stage->tcmods[stage->numtcmods].params[1] = atoi(com_token);
|
|
|
|
} else if (!strcmp(com_token, "stretch")) {
|
|
|
|
stage->tcmods[stage->numtcmods].type = TCMOD_STRETCH;
|
|
GET_SAFE_TOKEN;
|
|
stage->tcmods[stage->numtcmods].params[0] = atoi(com_token);
|
|
GET_SAFE_TOKEN;
|
|
stage->tcmods[stage->numtcmods].params[1] = atoi(com_token);
|
|
GET_SAFE_TOKEN;
|
|
stage->tcmods[stage->numtcmods].params[2] = atoi(com_token);
|
|
GET_SAFE_TOKEN;
|
|
stage->tcmods[stage->numtcmods].params[3] = atoi(com_token);
|
|
GET_SAFE_TOKEN;
|
|
stage->tcmods[stage->numtcmods].params[4] = atoi(com_token);
|
|
|
|
} else if (!strcmp(com_token, "turb")) {
|
|
|
|
//parse it and give a warning that it's not supported
|
|
stage->tcmods[stage->numtcmods].type = TCMOD_SCALE;
|
|
GET_SAFE_TOKEN;
|
|
stage->tcmods[stage->numtcmods].params[0] = atoi(com_token);
|
|
GET_SAFE_TOKEN;
|
|
stage->tcmods[stage->numtcmods].params[1] = atoi(com_token);
|
|
GET_SAFE_TOKEN;
|
|
stage->tcmods[stage->numtcmods].params[2] = atoi(com_token);
|
|
GET_SAFE_TOKEN;
|
|
stage->tcmods[stage->numtcmods].params[3] = atoi(com_token);
|
|
Con_Printf("Warning: turb not supported by Tenebrae.\n");
|
|
|
|
} else {
|
|
Con_Printf("Sorry not supported yet ... maybe never\n");
|
|
}
|
|
|
|
stage->numtcmods++;
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* Parse a single shader stage out of the shader file
|
|
* returns the new position in the file
|
|
*/
|
|
char *ParseStage (char *data, shader_t *shader)
|
|
{
|
|
char command[256];
|
|
char texture[256];
|
|
stage_t stage;
|
|
|
|
//Con_Printf("Parsing shader stage...\n");
|
|
clearStage(&stage);
|
|
|
|
// go through all the dictionary pairs
|
|
while (1)
|
|
{
|
|
data = COM_Parse (data);
|
|
|
|
//end of stage
|
|
if (com_token[0] == '}') {
|
|
//end of stage cleanup...
|
|
|
|
//stage->texture = GL_CacheTexture(com_token);
|
|
if (stage.type == STAGE_GLOSS) {
|
|
shader->glossStage = stage;
|
|
shader->flags = shader->flags | SURF_GLOSS;
|
|
} else if (stage.type == STAGE_COLOR) {
|
|
shader->colorStage = stage;
|
|
shader->flags = shader->flags | SURF_PPLIGHT;
|
|
} else if (stage.type == STAGE_BUMP) {
|
|
shader->bumpStage = stage;
|
|
shader->flags = shader->flags | SURF_BUMP;
|
|
} else {
|
|
if (shader->numstages >= SHADER_MAX_STAGES)
|
|
return NULL;
|
|
shader->stages[shader->numstages] = stage;
|
|
shader->numstages++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//ugh nasty
|
|
if (!data)
|
|
Sys_Error ("ParseStage: EOF without '}'");
|
|
|
|
//see what command it is
|
|
strncpy (command, com_token,sizeof(command));
|
|
if (!strcmp(command, "stage")) {
|
|
|
|
GET_SAFE_TOKEN;
|
|
if (!strcmp(com_token, "diffusemap")) {
|
|
stage.type = STAGE_COLOR;
|
|
} else if (!strcmp(com_token, "bumpmap")) {
|
|
stage.type = STAGE_BUMP;
|
|
} else if (!strcmp(com_token, "specularmap")) {
|
|
stage.type = STAGE_GLOSS;
|
|
} else {
|
|
Con_Printf("Unknown stage type %s\n",com_token);
|
|
}
|
|
|
|
} else if (!strcmp(command, "map")) {
|
|
|
|
GET_SAFE_TOKEN;
|
|
//add the textures to the known texture list
|
|
//stage.texture = GL_CacheTexture(com_token);
|
|
//Con_Printf("Cache texture %s\n",com_token);
|
|
strncpy(texture,com_token,sizeof(texture));
|
|
|
|
} else if (!strcmp(command, "tcMod")) {
|
|
|
|
data = ParceTcMod(data,&stage);
|
|
|
|
} else if (!strcmp(command, "blendfunc")) {
|
|
|
|
GET_SAFE_TOKEN;
|
|
stage.src_blend = SHADER_BlendModeForName(com_token);
|
|
if (stage.src_blend < 0) {
|
|
if (!strcmp(com_token,"blend")) {
|
|
stage.src_blend = GL_SRC_ALPHA;
|
|
stage.dst_blend = GL_ONE_MINUS_SRC_ALPHA;
|
|
} else if (!strcmp(com_token,"add")) {
|
|
stage.src_blend = GL_ONE;
|
|
stage.dst_blend = GL_ONE;
|
|
}
|
|
} else {
|
|
GET_SAFE_TOKEN;
|
|
stage.dst_blend = SC_BlendModeForName(com_token);
|
|
}
|
|
|
|
if (stage.type != STAGE_SIMPLE) {
|
|
Con_Printf("Warning: Blendfunc with nonsimpel stage type will be ignored.\n");
|
|
}
|
|
|
|
} else if (!strcmp(command, "alphafunc")) {
|
|
|
|
GET_SAFE_TOKEN;
|
|
if (!strcmp(com_token,"GE128")) {
|
|
stage.alphatresh = 128;
|
|
}
|
|
|
|
if ((stage.type == STAGE_BUMP) || (stage.type == STAGE_GLOSS)){
|
|
Con_Printf("Warning: Alphafunc with bump or normal stage type will be ignored.\n");
|
|
}
|
|
|
|
} else {
|
|
|
|
Con_Printf("Unknown statement %s\n",command);
|
|
data = COM_SkipLine(data);
|
|
//return NULL; //stop parsing
|
|
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* Automatically generate a bump stage with the bumpmap keyword
|
|
*/
|
|
void normalStage(shader_t *shader, char *name) {
|
|
clearStage(&shader->bumpStage);
|
|
shader->bumpStage.type = STAGE_BUMP;
|
|
//shader->bumpstage->texture = GL_CacheTexture(com_token);
|
|
shader->flags = shader->flags | SURF_BUMP;
|
|
}
|
|
|
|
/**
|
|
* Automatically generate a specular stage with the specularmap keyword
|
|
*/
|
|
void specularStage(shader_t *shader, char *name) {
|
|
clearStage(&shader->glossStage);
|
|
shader->glossStage.type = STAGE_GLOSS;
|
|
//shader->glossStage->texture = GL_CacheTexture(com_token);
|
|
shader->flags = shader->flags | SURF_GLOSS;
|
|
}
|
|
|
|
/**
|
|
* Automatically generate a bump stage with the colormap keyword
|
|
*/
|
|
void diffuseStage(shader_t *shader, char *name) {
|
|
clearStage(&shader->colorStage);
|
|
shader->colorStage.type = STAGE_COLOR;
|
|
//shader->colorStage->texture = GL_CacheTexture(com_token);
|
|
shader->flags = shader->flags | SURF_PPLIGHT;
|
|
}
|
|
|
|
/**
|
|
* Parse a single shader out of the shader file
|
|
*/
|
|
char *ParseShader (char *data, shader_t *shader)
|
|
{
|
|
char command[256];
|
|
|
|
while (1)
|
|
{
|
|
data = COM_Parse (data);
|
|
|
|
//end of shader
|
|
if (com_token[0] == '}')
|
|
break;
|
|
|
|
//ugh nasty
|
|
if (!data)
|
|
Sys_Error ("ParseShader: EOF without '}'");
|
|
|
|
//see what command it is
|
|
strncpy (command, com_token,sizeof(command));
|
|
if (command[0] == '{') {
|
|
data = ParseStage(data, shader);
|
|
} else if (!strcmp(command,"normalmap")) {
|
|
GET_SAFE_TOKEN;
|
|
normalStage(shader,com_token);
|
|
} else if (!strcmp(command,"diffusemap")) {
|
|
GET_SAFE_TOKEN;
|
|
diffuseStage(shader,com_token);
|
|
} else if (!strcmp(command,"specularmap")) {
|
|
GET_SAFE_TOKEN;
|
|
specularStage(shader,com_token);
|
|
} else if (!strcmp(command,"surfaceparm")) {
|
|
data = COM_SkipLine(data);
|
|
} else {
|
|
|
|
//ignore q3map and radiant commands
|
|
if (strncmp("qer_",com_token,4) && strncmp(com_token,"q3map_",5))
|
|
/*Con_Printf("Unknown statement %s\n",command)*/;
|
|
data = COM_SkipLine(data);
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
void LoadShadersFromString (char *data)
|
|
{
|
|
shader_t shader;
|
|
// parse shaders
|
|
memset(&shader,0,sizeof(shader));
|
|
while (1)
|
|
{
|
|
data = COM_Parse (data);
|
|
if (!data)
|
|
break;
|
|
|
|
strncpy(shader.name,com_token,sizeof(shader.name));
|
|
|
|
data = COM_Parse (data);
|
|
if (!data)
|
|
break;
|
|
if (com_token[0] != '{')
|
|
Sys_Error ("ED_LoadFromFile: found %s when expecting {",com_token);
|
|
|
|
//Con_Printf("Parsing shader %s\n",shader.name);
|
|
data = ParseShader (data, &shader);
|
|
}
|
|
|
|
if ((!fog_start.value) && (!fog_end.value)) {
|
|
Cvar_SetValue ("gl_fog",0.0);
|
|
}
|
|
}
|
|
|
|
void AddShaderFile(const char *filename) {
|
|
char *buffer = COM_LoadTempFile (filename);
|
|
Con_Printf("Parsing shaderscript: %s\n",filename);
|
|
if (!buffer) return;
|
|
LoadShadersFromString(buffer);
|
|
}
|
|
|
|
/**
|
|
* This should be called once during engine startup.
|
|
*
|
|
*/
|
|
void R_InitShaders() {
|
|
//clear list
|
|
shaderList = NULL;
|
|
//load all scripts
|
|
Con_Printf("=================================\n");
|
|
Con_Printf("Shader_Init: Initializing shaders\n");
|
|
COM_FindAllExt("scripts","shader",AddShaderFile);
|
|
Con_Printf("=================================\n");
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
//warn non win32 devs they should free the memory...
|
|
#error Call this routine from vid shutdown!
|
|
#endif
|
|
void R_ShutdownShaders() {
|
|
shader_t *s;
|
|
|
|
while (shaderList) {
|
|
s = shaderList;
|
|
shaderList = shaderList->next;
|
|
free(s);
|
|
}
|
|
} |