Initial sauer
[SauerbratenRemote.git] / src / engine / shader.cpp
blob772278b942b363800d698ca40e7dfff8cfb6367e
1 // shader.cpp: OpenGL assembly/GLSL shader management
3 #include "pch.h"
4 #include "engine.h"
6 Shader *Shader::lastshader = NULL;
8 Shader *defaultshader = NULL, *notextureshader = NULL, *nocolorshader = NULL, *foggedshader = NULL, *foggednotextureshader = NULL;
10 static hashtable<const char *, Shader> shaders;
11 static Shader *curshader = NULL;
12 static vector<ShaderParam> curparams;
13 static ShaderParamState vertexparamstate[RESERVEDSHADERPARAMS + MAXSHADERPARAMS], pixelparamstate[RESERVEDSHADERPARAMS + MAXSHADERPARAMS];
14 static int dirtyparams = 0;
16 void loadshaders()
18 exec("data/stdshader.cfg");
19 defaultshader = lookupshaderbyname("default");
20 notextureshader = lookupshaderbyname("notexture");
21 nocolorshader = lookupshaderbyname("nocolor");
22 foggedshader = lookupshaderbyname("fogged");
23 foggednotextureshader = lookupshaderbyname("foggednotexture");
24 if(renderpath!=R_FIXEDFUNCTION)
26 glEnable(GL_VERTEX_PROGRAM_ARB);
27 glEnable(GL_FRAGMENT_PROGRAM_ARB);
29 defaultshader->set();
32 Shader *lookupshaderbyname(const char *name)
34 Shader *s = shaders.access(name);
35 return s && s->altshader ? s->altshader : s;
38 static bool compileasmshader(GLenum type, GLuint &idx, const char *def, const char *tname, const char *name, bool msg = true, bool nativeonly = false)
40 glGenPrograms_(1, &idx);
41 glBindProgram_(type, idx);
42 def += strspn(def, " \t\r\n");
43 glProgramString_(type, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei)strlen(def), def);
44 GLint err, native;
45 glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &err);
46 glGetProgramiv_(type, GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB, &native);
47 if(msg && err!=-1)
49 conoutf("COMPILE ERROR (%s:%s) - %s", tname, name, glGetString(GL_PROGRAM_ERROR_STRING_ARB));
50 if(err>=0 && err<(int)strlen(def))
52 loopi(err) putchar(*def++);
53 puts(" <<HERE>> ");
54 while(*def) putchar(*def++);
57 else if(msg && !native) conoutf("%s:%s EXCEEDED NATIVE LIMITS", tname, name);
58 if(err!=-1 || (!native && nativeonly))
60 glDeletePrograms_(1, &idx);
61 idx = 0;
63 return native!=0;
66 static void showglslinfo(GLhandleARB obj, const char *tname, const char *name)
68 GLint length = 0;
69 glGetObjectParameteriv_(obj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
70 if(length > 1)
72 GLcharARB *log = new GLcharARB[length];
73 glGetInfoLog_(obj, length, &length, log);
74 conoutf("GLSL ERROR (%s:%s)", tname, name);
75 puts(log);
76 delete[] log;
80 static void compileglslshader(GLenum type, GLhandleARB &obj, const char *def, const char *tname, const char *name, bool msg = true)
82 const GLcharARB *source = (const GLcharARB*)(def + strspn(def, " \t\r\n"));
83 obj = glCreateShaderObject_(type);
84 glShaderSource_(obj, 1, &source, NULL);
85 glCompileShader_(obj);
86 GLint success;
87 glGetObjectParameteriv_(obj, GL_OBJECT_COMPILE_STATUS_ARB, &success);
88 if(!success)
90 if(msg) showglslinfo(obj, tname, name);
91 glDeleteObject_(obj);
92 obj = 0;
96 static void linkglslprogram(Shader &s, bool msg = true)
98 s.program = glCreateProgramObject_();
99 GLint success = 0;
100 if(s.program && s.vsobj && s.psobj)
102 glAttachObject_(s.program, s.vsobj);
103 glAttachObject_(s.program, s.psobj);
104 glLinkProgram_(s.program);
105 glGetObjectParameteriv_(s.program, GL_OBJECT_LINK_STATUS_ARB, &success);
107 if(success)
109 glUseProgramObject_(s.program);
110 loopi(8)
112 s_sprintfd(arg)("tex%d", i);
113 GLint loc = glGetUniformLocation_(s.program, arg);
114 if(loc != -1) glUniform1i_(loc, i);
116 loopv(s.defaultparams)
118 ShaderParam &param = s.defaultparams[i];
119 string pname;
120 if(param.type==SHPARAM_UNIFORM) s_strcpy(pname, param.name);
121 else s_sprintf(pname)("%s%d", param.type==SHPARAM_VERTEX ? "v" : "p", param.index);
122 param.loc = glGetUniformLocation_(s.program, pname);
124 glUseProgramObject_(0);
126 else
128 if(s.program)
130 if(msg) showglslinfo(s.program, "PROG", s.name);
131 glDeleteObject_(s.program);
132 s.program = 0;
134 if(s.vsobj) { glDeleteObject_(s.vsobj); s.vsobj = 0; }
135 if(s.psobj) { glDeleteObject_(s.psobj); s.psobj = 0; }
139 bool checkglslsupport()
141 /* check if GLSL profile supports loops
142 * might need to rewrite this if compiler does strength reduction
144 const GLcharARB *source =
145 "uniform int N;\n"
146 "uniform vec4 delta;\n"
147 "void main(void) {\n"
148 " vec4 test = vec4(0.0, 0.0, 0.0, 0.0);\n"
149 " for(int i = 0; i < N; i++) test += delta;\n"
150 " gl_FragColor = test;\n"
151 "}\n";
152 GLhandleARB obj = glCreateShaderObject_(GL_FRAGMENT_SHADER_ARB);
153 if(!obj) return false;
154 glShaderSource_(obj, 1, &source, NULL);
155 glCompileShader_(obj);
156 GLint success;
157 glGetObjectParameteriv_(obj, GL_OBJECT_COMPILE_STATUS_ARB, &success);
158 if(!success)
160 glDeleteObject_(obj);
161 return false;
163 GLhandleARB program = glCreateProgramObject_();
164 if(!program)
166 glDeleteObject_(obj);
167 return false;
169 glAttachObject_(program, obj);
170 glLinkProgram_(program);
171 glGetObjectParameteriv_(program, GL_OBJECT_LINK_STATUS_ARB, &success);
172 glDeleteObject_(obj);
173 glDeleteObject_(program);
174 return success!=0;
177 static LocalShaderParamState unusedextparam;
179 static void allocglsluniformparam(Shader &s, int type, int index, bool local = false)
181 ShaderParamState &val = (type==SHPARAM_VERTEX ? vertexparamstate[index] : pixelparamstate[index]);
182 int loc = val.name ? glGetUniformLocation_(s.program, val.name) : -1;
183 if(loc == -1)
185 s_sprintfd(altname)("%s%d", type==SHPARAM_VERTEX ? "v" : "p", index);
186 loc = glGetUniformLocation_(s.program, val.name);
188 else
190 LocalShaderParamState *alt = (type==SHPARAM_VERTEX ? s.extpixparams[index] : s.extvertparams[index]);
191 if(alt && alt != &unusedextparam && alt->loc == loc)
193 if(type==SHPARAM_VERTEX) s.extvertparams[index] = alt;
194 else s.extpixparams[index] = alt;
195 return;
198 if(loc == -1)
200 if(type==SHPARAM_VERTEX) s.extvertparams[index] = local ? &unusedextparam : NULL;
201 else s.extpixparams[index] = local ? &unusedextparam : NULL;
202 return;
204 LocalShaderParamState &ext = s.extparams.add();
205 ext.name = val.name;
206 ext.type = type;
207 ext.index = local ? -1 : index;
208 ext.loc = loc;
209 if(type==SHPARAM_VERTEX) s.extvertparams[index] = &ext;
210 else s.extpixparams[index] = &ext;
213 void Shader::allocenvparams(Slot *slot)
215 if(!(type & SHADER_GLSLANG)) return;
217 if(slot)
219 #define UNIFORMTEX(name, tmu) \
221 loc = glGetUniformLocation_(program, name); \
222 int val = tmu; \
223 if(loc != -1) glUniform1i_(loc, val); \
225 int loc, tmu = 2;
226 if(type & SHADER_NORMALSLMS)
228 UNIFORMTEX("lmcolor", 1);
229 UNIFORMTEX("lmdir", 2);
230 tmu++;
232 else UNIFORMTEX("lightmap", 1);
233 if(type & SHADER_ENVMAP) UNIFORMTEX("envmap", tmu++);
234 UNIFORMTEX("shadowmap", 7);
235 int stex = 0;
236 loopv(slot->sts)
238 Slot::Tex &t = slot->sts[i];
239 switch(t.type)
241 case TEX_DIFFUSE: UNIFORMTEX("diffusemap", 0); break;
242 case TEX_NORMAL: UNIFORMTEX("normalmap", tmu++); break;
243 case TEX_GLOW: UNIFORMTEX("glowmap", tmu++); break;
244 case TEX_DECAL: UNIFORMTEX("decal", tmu++); break;
245 case TEX_SPEC: if(t.combined<0) UNIFORMTEX("specmap", tmu++); break;
246 case TEX_DEPTH: if(t.combined<0) UNIFORMTEX("depthmap", tmu++); break;
247 case TEX_UNKNOWN:
249 s_sprintfd(sname)("stex%d", stex++);
250 UNIFORMTEX(sname, tmu++);
251 break;
256 loopi(RESERVEDSHADERPARAMS) if(vertexparamstate[i].name && !vertexparamstate[i].local)
257 allocglsluniformparam(*this, SHPARAM_VERTEX, i);
258 loopi(RESERVEDSHADERPARAMS) if(pixelparamstate[i].name && !pixelparamstate[i].local)
259 allocglsluniformparam(*this, SHPARAM_PIXEL, i);
262 void setenvparamf(const char *name, int type, int index, float x, float y, float z, float w)
264 ShaderParamState &val = (type==SHPARAM_VERTEX ? vertexparamstate[index] : pixelparamstate[index]);
265 val.name = name;
266 val.local = false;
267 if(val.val[0]!=x || val.val[1]!=y || val.val[2]!=z || val.val[3]!=w)
269 val.val[0] = x;
270 val.val[1] = y;
271 val.val[2] = z;
272 val.val[3] = w;
273 if(!val.dirty) dirtyparams++;
274 val.dirty = true;
278 void setenvparamfv(const char *name, int type, int index, const float *v)
280 ShaderParamState &val = (type==SHPARAM_VERTEX ? vertexparamstate[index] : pixelparamstate[index]);
281 val.name = name;
282 val.local = false;
283 if(memcmp(val.val, v, sizeof(val.val)))
285 memcpy(val.val, v, sizeof(val.val));
286 if(!val.dirty) dirtyparams++;
287 val.dirty = true;
291 void flushenvparam(int type, int index, bool local)
293 ShaderParamState &val = (type==SHPARAM_VERTEX ? vertexparamstate[index] : pixelparamstate[index]);
294 val.local = local;
295 if(Shader::lastshader && Shader::lastshader->type&SHADER_GLSLANG)
297 LocalShaderParamState *&ext = (type==SHPARAM_VERTEX ? Shader::lastshader->extvertparams[index] : Shader::lastshader->extpixparams[index]);
298 if(!ext) allocglsluniformparam(*Shader::lastshader, type, index, local);
299 if(!ext || ext == &unusedextparam) return;
300 if(!memcmp(ext->curval, val.val, sizeof(ext->curval))) return;
301 memcpy(ext->curval, val.val, sizeof(ext->curval));
302 glUniform4fv_(ext->loc, 1, ext->curval);
304 else if(val.dirty)
306 glProgramEnvParameter4fv_(type==SHPARAM_VERTEX ? GL_VERTEX_PROGRAM_ARB : GL_FRAGMENT_PROGRAM_ARB, index, val.val);
307 dirtyparams--;
308 val.dirty = false;
312 void setlocalparamf(const char *name, int type, int index, float x, float y, float z, float w)
314 setenvparamf(name, type, index, x, y, z, w);
315 flushenvparam(type, index, true);
318 void setlocalparamfv(const char *name, int type, int index, const float *v)
320 setenvparamfv(name, type, index, v);
321 flushenvparam(type, index, true);
324 void Shader::flushenvparams(Slot *slot)
326 if(type & SHADER_GLSLANG)
328 if(!used) allocenvparams(slot);
330 loopv(extparams)
332 LocalShaderParamState &ext = extparams[i];
333 if(ext.index<0) continue;
334 float *val = ext.type==SHPARAM_VERTEX ? vertexparamstate[ext.index].val : pixelparamstate[ext.index].val;
335 if(!memcmp(ext.curval, val, sizeof(ext.val))) continue;
336 memcpy(ext.curval, val, sizeof(ext.val));
337 glUniform4fv_(ext.loc, 1, ext.curval);
340 else if(dirtyparams)
342 loopi(RESERVEDSHADERPARAMS)
344 ShaderParamState &val = vertexparamstate[i];
345 if(val.local || !val.dirty) continue;
346 glProgramEnvParameter4fv_(GL_VERTEX_PROGRAM_ARB, i, val.val);
347 val.dirty = false;
348 dirtyparams--;
350 loopi(RESERVEDSHADERPARAMS)
352 ShaderParamState &val = pixelparamstate[i];
353 if(val.local || !val.dirty) continue;
354 glProgramEnvParameter4fv_(GL_FRAGMENT_PROGRAM_ARB, i, val.val);
355 val.dirty = false;
356 dirtyparams--;
359 used = true;
362 void Shader::setslotparams(Slot &slot)
364 uint unimask = 0, vertmask = 0, pixmask = 0;
365 loopv(slot.params)
367 ShaderParam &p = slot.params[i];
368 if(type & SHADER_GLSLANG)
370 LocalShaderParamState &l = defaultparams[p.index];
371 unimask |= p.index;
372 if(!memcmp(l.curval, p.val, sizeof(l.curval))) continue;
373 memcpy(l.curval, p.val, sizeof(l.curval));
374 glUniform4fv_(l.loc, 1, l.curval);
376 else if(p.type!=SHPARAM_UNIFORM)
378 ShaderParamState &val = (p.type==SHPARAM_VERTEX ? vertexparamstate[RESERVEDSHADERPARAMS+p.index] : pixelparamstate[RESERVEDSHADERPARAMS+p.index]);
379 if(p.type==SHPARAM_VERTEX) vertmask |= 1<<p.index;
380 else pixmask |= 1<<p.index;
381 if(memcmp(val.val, p.val, sizeof(val.val))) memcpy(val.val, p.val, sizeof(val.val));
382 else if(!val.dirty) continue;
383 glProgramEnvParameter4fv_(p.type==SHPARAM_VERTEX ? GL_VERTEX_PROGRAM_ARB : GL_FRAGMENT_PROGRAM_ARB, RESERVEDSHADERPARAMS+p.index, val.val);
384 if(val.dirty) dirtyparams--;
385 val.local = true;
386 val.dirty = false;
389 loopv(defaultparams)
391 LocalShaderParamState &l = defaultparams[i];
392 if(type & SHADER_GLSLANG)
394 if(unimask&(1<<i)) continue;
395 if(!memcmp(l.curval, l.val, sizeof(l.curval))) continue;
396 memcpy(l.curval, l.val, sizeof(l.curval));
397 glUniform4fv_(l.loc, 1, l.curval);
399 else if(l.type!=SHPARAM_UNIFORM)
401 if(l.type==SHPARAM_VERTEX)
403 if(vertmask & (1<<l.index)) continue;
405 else if(pixmask & (1<<l.index)) continue;
406 ShaderParamState &val = (l.type==SHPARAM_VERTEX ? vertexparamstate[RESERVEDSHADERPARAMS+l.index] : pixelparamstate[RESERVEDSHADERPARAMS+l.index]);
407 if(memcmp(val.val, l.val, sizeof(val.val))) memcpy(val.val, l.val, sizeof(val.val));
408 else if(!val.dirty) continue;
409 glProgramEnvParameter4fv_(l.type==SHPARAM_VERTEX ? GL_VERTEX_PROGRAM_ARB : GL_FRAGMENT_PROGRAM_ARB, RESERVEDSHADERPARAMS+l.index, val.val);
410 if(val.dirty) dirtyparams--;
411 val.local = true;
412 val.dirty = false;
417 void Shader::bindprograms()
419 if(this==lastshader) return;
420 if(type & SHADER_GLSLANG)
422 glUseProgramObject_(program);
424 else
426 if(lastshader && lastshader->type & SHADER_GLSLANG) glUseProgramObject_(0);
428 glBindProgram_(GL_VERTEX_PROGRAM_ARB, vs);
429 glBindProgram_(GL_FRAGMENT_PROGRAM_ARB, ps);
431 lastshader = this;
434 VARFN(shaders, useshaders, -1, -1, 1, initwarning());
435 VARF(shaderprecision, 0, 0, 2, initwarning());
436 VARP(shaderdetail, 0, MAXSHADERDETAIL, MAXSHADERDETAIL);
438 Shader *newshader(int type, char *name, char *vs, char *ps, Shader *variant = NULL, int row = 0)
440 char *rname = newstring(name);
441 Shader &s = shaders[rname];
442 s.name = rname;
443 s.type = type;
444 loopi(MAXSHADERDETAIL) s.fastshader[i] = &s;
445 memset(s.extvertparams, 0, sizeof(s.extvertparams));
446 memset(s.extpixparams, 0, sizeof(s.extpixparams));
447 if(variant) loopv(variant->defaultparams) s.defaultparams.add(variant->defaultparams[i]);
448 else loopv(curparams) s.defaultparams.add(curparams[i]);
449 if(renderpath!=R_FIXEDFUNCTION)
451 if(type & SHADER_GLSLANG)
453 compileglslshader(GL_VERTEX_SHADER_ARB, s.vsobj, vs, "VS", name, !variant);
454 compileglslshader(GL_FRAGMENT_SHADER_ARB, s.psobj, ps, "PS", name, !variant);
455 linkglslprogram(s, !variant);
457 else
459 if(!compileasmshader(GL_VERTEX_PROGRAM_ARB, s.vs, vs, "VS", name, !variant, variant!=NULL))
460 s.native = false;
461 if(!compileasmshader(GL_FRAGMENT_PROGRAM_ARB, s.ps, ps, "PS", name, !variant, variant!=NULL))
462 s.native = false;
463 if(!s.vs || !s.ps || (variant && !s.native))
465 if(s.vs) { glDeletePrograms_(1, &s.vs); s.vs = 0; }
466 if(s.ps) { glDeletePrograms_(1, &s.ps); s.ps = 0; }
469 if(!s.program && !s.vs && !s.ps)
471 shaders.remove(rname);
472 return NULL;
475 if(variant) variant->variants[row].add(&s);
476 return &s;
479 static uint findusedtexcoords(char *str)
481 uint used = 0;
482 for(;;)
484 char *tc = strstr(str, "result.texcoord[");
485 if(!tc) break;
486 tc += strlen("result.texcoord[");
487 int n = strtol(tc, &str, 10);
488 if(n<0 || n>=16) continue;
489 used |= 1<<n;
491 return used;
494 static bool findunusedtexcoordcomponent(char *str, int &texcoord, int &component)
496 uchar texcoords[16];
497 memset(texcoords, 0, sizeof(texcoords));
498 for(;;)
500 char *tc = strstr(str, "result.texcoord[");
501 if(!tc) break;
502 tc += strlen("result.texcoord[");
503 int n = strtol(tc, &str, 10);
504 if(n<0 || n>=(int)sizeof(texcoords)) continue;
505 while(*str && *str!=']') str++;
506 if(*str==']')
508 if(*++str!='.') { texcoords[n] = 0xF; continue; }
509 for(;;)
511 switch(*++str)
513 case 'r': case 'x': texcoords[n] |= 1; continue;
514 case 'g': case 'y': texcoords[n] |= 2; continue;
515 case 'b': case 'z': texcoords[n] |= 4; continue;
516 case 'a': case 'w': texcoords[n] |= 8; continue;
518 break;
522 loopi(sizeof(texcoords)) if(texcoords[i]>0 && texcoords[i]<0xF)
524 loopk(4) if(!(texcoords[i]&(1<<k))) { texcoord = i; component = k; return true; }
526 return false;
529 #define EMUFOGVS(cond, vsbuf, start, end, fogcoord, fogtc, fogcomp) \
530 if(cond) \
532 vsbuf.put(start, fogcoord-start); \
533 const char *afterfogcoord = fogcoord + strlen("result.fogcoord"); \
534 if(*afterfogcoord=='.') afterfogcoord += 2; \
535 s_sprintfd(repfogcoord)("result.texcoord[%d].%c", fogtc, fogcomp==3 ? 'w' : 'x'+fogcomp); \
536 vsbuf.put(repfogcoord, strlen(repfogcoord)); \
537 vsbuf.put(afterfogcoord, end-afterfogcoord); \
539 else vsbuf.put(start, end-start);
541 #define EMUFOGPS(cond, psbuf, fogtc, fogcomp) \
542 if(cond) \
544 char *fogoption = strstr(psbuf.getbuf(), "OPTION ARB_fog_linear;"); \
545 /* OPTION ARB_fog_linear; */ \
546 const char *tmpdef = "TEMP emufogcolor; "; \
547 if(fogoption) while(*tmpdef) *fogoption++ = *tmpdef++; \
548 /* result.color */\
549 const char *tmpuse = " emufogcolor"; \
550 char *str = psbuf.getbuf(); \
551 for(;;) \
553 str = strstr(str, "result.color"); \
554 if(!str) break; \
555 if(str[12]!='.' || (str[13]!='a' && str[13]!='w')) memcpy(str, tmpuse, strlen(tmpuse)); \
556 str += 12; \
558 s_sprintfd(fogtcstr)("fragment.texcoord[%d].%c", fogtc, fogcomp==3 ? 'w' : 'x'+fogcomp); \
559 str = strstr(psbuf.getbuf(), "fragment.fogcoord.x"); \
560 if(str) \
562 int fogtclen = strlen(fogtcstr); \
563 memcpy(str, fogtcstr, 19); \
564 psbuf.insert(&str[19] - psbuf.getbuf(), &fogtcstr[19], fogtclen-19); \
566 char *end = strstr(psbuf.getbuf(), "END"); \
567 if(end) psbuf.setsizenodelete(end - psbuf.getbuf()); \
568 s_sprintfd(calcfog)( \
569 "TEMP emufog;\n" \
570 "SUB emufog, state.fog.params.z, %s;\n" \
571 "MUL_SAT emufog, emufog, state.fog.params.w;\n" \
572 "LRP result.color.rgb, emufog, emufogcolor, state.fog.color;\n" \
573 "END\n", \
574 fogtcstr); \
575 psbuf.put(calcfog, strlen(calcfog)+1); \
578 VAR(reserveshadowmaptc, 1, 0, 0);
579 VAR(reservedynlighttc, 1, 0, 0);
581 static void gendynlightvariant(Shader &s, char *sname, char *vs, char *ps, int row = 0)
583 int numlights = 0, lights[MAXDYNLIGHTS];
584 int emufogtc = -1, emufogcomp = -1;
585 const char *emufogcoord = NULL;
586 if(s.type & SHADER_GLSLANG) numlights = MAXDYNLIGHTS;
587 else
589 uint usedtc = findusedtexcoords(vs);
590 GLint maxtc = 0;
591 glGetIntegerv(GL_MAX_TEXTURE_COORDS_ARB, &maxtc);
592 int reservetc = row%2 ? reserveshadowmaptc : reservedynlighttc;
593 if(maxtc-reservetc<0) return;
594 loopi(maxtc-reservetc) if(!(usedtc&(1<<i)))
596 lights[numlights++] = i;
597 if(numlights>=MAXDYNLIGHTS) break;
599 extern int emulatefog;
600 if(emulatefog && reservetc>0 && numlights+1<MAXDYNLIGHTS && !(usedtc&(1<<(maxtc-reservetc))) && strstr(ps, "OPTION ARB_fog_linear;"))
602 emufogcoord = strstr(vs, "result.fogcoord");
603 if(emufogcoord)
605 emufogtc = maxtc-reservetc;
606 emufogcomp = 3;
607 lights[numlights++] = maxtc-reservetc;
610 if(!numlights) return;
613 char *vspragma = strstr(vs, "#pragma CUBE2_dynlight"), *pspragma = strstr(ps, "#pragma CUBE2_dynlight");
614 string pslight;
615 vspragma += strcspn(vspragma, "\n");
616 if(*vspragma) vspragma++;
618 if(sscanf(pspragma, "#pragma CUBE2_dynlight %s", pslight)!=1) return;
620 pspragma += strcspn(pspragma, "\n");
621 if(*pspragma) pspragma++;
623 vector<char> vsdl, psdl;
624 loopi(numlights)
626 vsdl.setsizenodelete(0);
627 psdl.setsizenodelete(0);
629 if(s.type & SHADER_GLSLANG)
631 loopk(i+1)
633 s_sprintfd(pos)("%sdynlight%dpos%s", !k ? "uniform vec4 " : " ", k, k==i ? ";\n" : ",");
634 vsdl.put(pos, strlen(pos));
636 s_sprintfd(color)("%sdynlight%dcolor%s", !k ? "uniform vec4 " : " ", k, k==i ? ";\n" : ",");
637 psdl.put(color, strlen(color));
639 loopk(i+1)
641 s_sprintfd(dir)("%sdynlight%ddir%s", !k ? "varying vec3 " : " ", k, k==i ? ";\n" : ",");
642 vsdl.put(dir, strlen(dir));
643 psdl.put(dir, strlen(dir));
647 EMUFOGVS(emufogcoord && i+1==numlights && emufogcoord < vspragma, vsdl, vs, vspragma, emufogcoord, emufogtc, emufogcomp);
648 psdl.put(ps, pspragma-ps);
650 loopk(i+1)
652 string tc, dl;
653 if(s.type & SHADER_GLSLANG) s_sprintf(tc)(
654 "dynlight%ddir = gl_Vertex.xyz - dynlight%dpos.xyz;\n",
655 k, k);
656 else s_sprintf(tc)(
657 "SUB result.texcoord[%d].xyz, vertex.position, program.env[%d];\n",
658 lights[k], 10+k);
659 vsdl.put(tc, strlen(tc));
661 if(s.type & SHADER_GLSLANG) s_sprintf(dl)(
662 "%s.rgb += dynlight%dcolor.rgb * clamp(1.0 - dynlight%dcolor.a*dot(dynlight%ddir, dynlight%ddir), 0.0, 1.0);\n",
663 pslight, k, k, k, k);
664 else s_sprintf(dl)(
665 "%s"
666 "DP3 dynlight, fragment.texcoord[%d], fragment.texcoord[%d];\n"
667 "MAD_SAT dynlight, dynlight, program.env[%d].a, 1;\n"
668 "MAD %s.rgb, program.env[%d], dynlight, %s;\n",
670 !k ? "TEMP dynlight;\n" : "",
671 lights[k], lights[k],
672 10+k,
673 pslight, 10+k, pslight);
674 psdl.put(dl, strlen(dl));
677 EMUFOGVS(emufogcoord && i+1==numlights && emufogcoord >= vspragma, vsdl, vspragma, vspragma+strlen(vspragma)+1, emufogcoord, emufogtc, emufogcomp);
678 psdl.put(pspragma, strlen(pspragma)+1);
680 EMUFOGPS(emufogcoord && i+1==numlights, psdl, emufogtc, emufogcomp);
682 s_sprintfd(name)("<dynlight %d>%s", i+1, sname);
683 Shader *variant = newshader(s.type, name, vsdl.getbuf(), psdl.getbuf(), &s, row);
684 if(!variant) return;
688 static void genshadowmapvariant(Shader &s, char *sname, char *vs, char *ps, int row = 1)
690 int smtc = -1, emufogtc = -1, emufogcomp = -1;
691 const char *emufogcoord = NULL;
692 if(!(s.type & SHADER_GLSLANG))
694 uint usedtc = findusedtexcoords(vs);
695 GLint maxtc = 0;
696 glGetIntegerv(GL_MAX_TEXTURE_COORDS_ARB, &maxtc);
697 if(maxtc-reserveshadowmaptc<0) return;
698 loopi(maxtc-reserveshadowmaptc) if(!(usedtc&(1<<i))) { smtc = i; break; }
699 extern int emulatefog;
700 if(smtc<0 && emulatefog && reserveshadowmaptc>0 && !(usedtc&(1<<(maxtc-reserveshadowmaptc))) && strstr(ps, "OPTION ARB_fog_linear;"))
702 emufogcoord = strstr(vs, "result.fogcoord");
703 if(!emufogcoord || !findunusedtexcoordcomponent(vs, emufogtc, emufogcomp)) return;
704 smtc = maxtc-reserveshadowmaptc;
706 if(smtc<0) return;
709 char *vspragma = strstr(vs, "#pragma CUBE2_shadowmap"), *pspragma = strstr(ps, "#pragma CUBE2_shadowmap");
710 string pslight;
711 vspragma += strcspn(vspragma, "\n");
712 if(*vspragma) vspragma++;
714 if(sscanf(pspragma, "#pragma CUBE2_shadowmap %s", pslight)!=1) return;
716 pspragma += strcspn(pspragma, "\n");
717 if(*pspragma) pspragma++;
719 vector<char> vssm, pssm;
721 if(s.type & SHADER_GLSLANG)
723 const char *tc = "varying vec3 shadowmaptc;\n";
724 vssm.put(tc, strlen(tc));
725 pssm.put(tc, strlen(tc));
726 const char *smtex =
727 "uniform sampler2D shadowmap;\n"
728 "uniform vec4 shadowmapambient;\n";
729 pssm.put(smtex, strlen(smtex));
730 if(!strstr(ps, "ambient"))
732 const char *amb = "uniform vec4 ambient;\n";
733 pssm.put(amb, strlen(amb));
737 EMUFOGVS(emufogcoord && emufogcoord < vspragma, vssm, vs, vspragma, emufogcoord, emufogtc, emufogcomp);
738 pssm.put(ps, pspragma-ps);
740 if(s.type & SHADER_GLSLANG)
742 const char *tc =
743 "shadowmaptc = vec3(gl_TextureMatrix[2] * gl_Vertex);\n";
744 vssm.put(tc, strlen(tc));
745 const char *sm =
746 "vec3 smvals = texture2D(shadowmap, shadowmaptc.xy).xyz;\n"
747 "vec2 smdiff = shadowmaptc.zz*smvals.y + smvals.xz;\n"
748 "float shadowed = smdiff.x > 0.0 && smdiff.y < 0.0 ? smvals.y : 0.0;\n";
749 pssm.put(sm, strlen(sm));
750 s_sprintfd(smlight)(
751 "%s.rgb = mix(%s.rgb, min(%s.rgb, shadowmapambient.rgb), shadowed);\n",
752 pslight, pslight, pslight);
753 pssm.put(smlight, strlen(smlight));
755 else
757 s_sprintfd(tc)(
758 "DP4 result.texcoord[%d].x, state.matrix.texture[2].row[0], vertex.position;\n"
759 "DP4 result.texcoord[%d].y, state.matrix.texture[2].row[1], vertex.position;\n"
760 "DP4 result.texcoord[%d].z, state.matrix.texture[2].row[2], vertex.position;\n",
761 smtc, smtc, smtc);
762 vssm.put(tc, strlen(tc));
764 s_sprintfd(sm)(
765 "TEMP smvals, shadowed, smambient;\n"
766 "TEX smvals, fragment.texcoord[%d], texture[7], 2D;\n"
767 "MAD smvals.xz, fragment.texcoord[%d].z, smvals.y, smvals;\n",
768 smtc, smtc);
769 pssm.put(sm, strlen(sm));
770 s_sprintf(sm)(
771 "CMP shadowed, -smvals.x, smvals.y, 0;\n"
772 "CMP shadowed, smvals.z, shadowed, 0;\n"
773 "MIN smambient.rgb, program.env[7], %s;\n"
774 "LRP %s.rgb, shadowed, smambient, %s;\n",
775 pslight, pslight, pslight);
776 pssm.put(sm, strlen(sm));
779 EMUFOGVS(emufogcoord && emufogcoord >= vspragma, vssm, vspragma, vspragma+strlen(vspragma)+1, emufogcoord, emufogtc, emufogcomp);
780 pssm.put(pspragma, strlen(pspragma)+1);
782 EMUFOGPS(emufogcoord, pssm, emufogtc, emufogcomp);
784 s_sprintfd(name)("<shadowmap>%s", sname);
785 Shader *variant = newshader(s.type, name, vssm.getbuf(), pssm.getbuf(), &s, row);
786 if(!variant) return;
788 if(strstr(vs, "#pragma CUBE2_dynlight")) gendynlightvariant(s, name, vssm.getbuf(), pssm.getbuf(), row);
791 static void genwatervariant(Shader &s, char *sname, char *vs, char *ps, int row = 2)
793 char *pspragma = strstr(ps, "#pragma CUBE2_water");
794 pspragma += strcspn(pspragma, "\n");
795 if(*pspragma) pspragma++;
797 vector<char> psw;
798 psw.put(ps, pspragma-ps);
799 const char *fogtoalpha = s.type & SHADER_GLSLANG ? "gl_FragColor.a = gl_FogFragCoord*0.25 + 0.5;\n" : "MAD result.color.a, fragment.fogcoord.x, 0.25, 0.5;\n";
800 psw.put(fogtoalpha, strlen(fogtoalpha));
801 psw.put(pspragma, strlen(pspragma)+1);
803 s_sprintfd(name)("<water>%s", sname);
804 Shader *variant = newshader(s.type, name, vs, psw.getbuf(), &s, row);
805 if(!variant) return;
807 if(strstr(vs, "#pragma CUBE2_shadowmap")) genshadowmapvariant(s, name, vs, psw.getbuf(), row+1);
808 if(strstr(vs, "#pragma CUBE2_dynlight")) gendynlightvariant(s, name, vs, psw.getbuf(), row);
811 void shader(int *type, char *name, char *vs, char *ps)
813 if(lookupshaderbyname(name)) return;
815 if(renderpath!=R_FIXEDFUNCTION)
817 if((renderpath!=R_GLSLANG && *type & SHADER_GLSLANG) ||
818 (!hasCM && strstr(ps, *type & SHADER_GLSLANG ? "textureCube" : "CUBE")) ||
819 (!hasTR && strstr(ps, *type & SHADER_GLSLANG ? "texture2DRect" : "RECT")))
821 loopv(curparams)
823 if(curparams[i].name) delete[] curparams[i].name;
825 curparams.setsize(0);
826 return;
828 s_sprintfd(info)("shader %s", name);
829 show_out_of_renderloop_progress(0.0, info);
831 Shader *s = newshader(*type, name, vs, ps);
832 if(s && renderpath!=R_FIXEDFUNCTION)
834 // '#' is a comment in vertex/fragment programs, while '#pragma' allows an escape for GLSL, so can handle both at once
835 if(strstr(ps, "#pragma CUBE2_water")) genwatervariant(*s, s->name, vs, ps);
836 if(strstr(vs, "#pragma CUBE2_shadowmap")) genshadowmapvariant(*s, s->name, vs, ps);
837 if(strstr(vs, "#pragma CUBE2_dynlight")) gendynlightvariant(*s, s->name, vs, ps);
839 curparams.setsize(0);
842 void setshader(char *name)
844 Shader *s = lookupshaderbyname(name);
845 if(!s)
847 if(renderpath!=R_FIXEDFUNCTION) conoutf("no such shader: %s", name);
849 else curshader = s;
850 loopv(curparams)
852 if(curparams[i].name) delete[] curparams[i].name;
854 curparams.setsize(0);
857 ShaderParam *findshaderparam(Slot &s, const char *name, int type, int index)
859 if(!s.shader) return NULL;
860 loopv(s.params)
862 ShaderParam &param = s.params[i];
863 if((name && param.name && !strcmp(name, param.name)) || (param.type==type && param.index==index)) return &param;
865 loopv(s.shader->defaultparams)
867 ShaderParam &param = s.shader->defaultparams[i];
868 if((name && param.name && !strcmp(name, param.name)) || (param.type==type && param.index==index)) return &param;
870 return NULL;
873 void setslotshader(Slot &s)
875 s.shader = curshader ? curshader : defaultshader;
876 if(!s.shader) return;
877 loopv(curparams)
879 ShaderParam &param = curparams[i], *defaultparam = findshaderparam(s, param.name, param.type, param.index);
880 if(!defaultparam || !memcmp(param.val, defaultparam->val, sizeof(param.val))) continue;
881 ShaderParam &override = s.params.add(param);
882 override.name = defaultparam->name;
883 if(s.shader->type&SHADER_GLSLANG) override.index = (LocalShaderParamState *)defaultparam - &s.shader->defaultparams[0];
887 VAR(nativeshaders, 0, 1, 1);
889 void altshader(char *origname, char *altname)
891 Shader *alt = lookupshaderbyname(altname);
892 if(!alt) return;
893 Shader *orig = lookupshaderbyname(origname);
894 if(orig)
896 if(nativeshaders && !orig->native) orig->altshader = alt;
897 return;
899 char *rname = newstring(origname);
900 Shader &s = shaders[rname];
901 s.name = rname;
902 s.altshader = alt;
905 void fastshader(char *nice, char *fast, int *detail)
907 Shader *ns = shaders.access(nice);
908 if(!ns || ns->altshader) return;
909 Shader *fs = lookupshaderbyname(fast);
910 if(!fs) return;
911 loopi(min(*detail+1, MAXSHADERDETAIL)) ns->fastshader[i] = fs;
914 COMMAND(shader, "isss");
915 COMMAND(setshader, "s");
916 COMMAND(altshader, "ss");
917 COMMAND(fastshader, "ssi");
919 void isshaderdefined(char *name)
921 Shader *s = lookupshaderbyname(name);
922 intret(s ? 1 : 0);
925 void isshadernative(char *name)
927 Shader *s = lookupshaderbyname(name);
928 intret(s && s->native ? 1 : 0);
931 COMMAND(isshaderdefined, "s");
932 COMMAND(isshadernative, "s");
934 void setshaderparam(char *name, int type, int n, float x, float y, float z, float w)
936 if(!name && (n<0 || n>=MAXSHADERPARAMS))
938 conoutf("shader param index must be 0..%d\n", MAXSHADERPARAMS-1);
939 return;
941 loopv(curparams)
943 ShaderParam &param = curparams[i];
944 if(param.type == type && (name ? !strstr(param.name, name) : param.index == n))
946 param.val[0] = x;
947 param.val[1] = y;
948 param.val[2] = z;
949 param.val[3] = w;
950 return;
953 ShaderParam param = {name ? newstring(name) : NULL, type, n, -1, {x, y, z, w}};
954 curparams.add(param);
957 void setvertexparam(int *n, float *x, float *y, float *z, float *w)
959 setshaderparam(NULL, SHPARAM_VERTEX, *n, *x, *y, *z, *w);
962 void setpixelparam(int *n, float *x, float *y, float *z, float *w)
964 setshaderparam(NULL, SHPARAM_PIXEL, *n, *x, *y, *z, *w);
967 void setuniformparam(char *name, float *x, float *y, float *z, float *w)
969 setshaderparam(name, SHPARAM_UNIFORM, -1, *x, *y, *z, *w);
972 COMMAND(setvertexparam, "iffff");
973 COMMAND(setpixelparam, "iffff");
974 COMMAND(setuniformparam, "sffff");
976 const int NUMSCALE = 7;
977 Shader *fsshader = NULL, *scaleshader = NULL, *initshader = NULL;
978 GLuint rendertarget[NUMSCALE];
979 GLuint fsfb[NUMSCALE-1];
980 GLfloat fsparams[4];
981 int fs_w = 0, fs_h = 0, fspasses = NUMSCALE, fsskip = 1;
983 void setfullscreenshader(char *name, int *x, int *y, int *z, int *w)
985 if(!hasTR || !*name)
987 fsshader = NULL;
989 else
991 Shader *s = lookupshaderbyname(name);
992 if(!s) return conoutf("no such fullscreen shader: %s", name);
993 fsshader = s;
994 s_sprintfd(ssname)("%s_scale", name);
995 s_sprintfd(isname)("%s_init", name);
996 scaleshader = lookupshaderbyname(ssname);
997 initshader = lookupshaderbyname(isname);
998 fspasses = NUMSCALE;
999 fsskip = 1;
1000 if(scaleshader)
1002 int len = strlen(name);
1003 char c = name[--len];
1004 if(isdigit(c))
1006 if(len>0 && isdigit(name[--len]))
1008 fsskip = c-'0';
1009 fspasses = name[len]-'0';
1011 else fspasses = c-'0';
1014 conoutf("now rendering with: %s", name);
1015 fsparams[0] = *x/255.0f;
1016 fsparams[1] = *y/255.0f;
1017 fsparams[2] = *z/255.0f;
1018 fsparams[3] = *w/255.0f;
1022 COMMAND(setfullscreenshader, "siiii");
1024 void renderfsquad(int w, int h, Shader *s)
1026 s->set();
1027 glViewport(0, 0, w, h);
1028 if(s==scaleshader || s==initshader)
1030 w <<= fsskip;
1031 h <<= fsskip;
1033 glBegin(GL_QUADS);
1034 glTexCoord2i(0, 0); glVertex3f(-1, -1, 0);
1035 glTexCoord2i(w, 0); glVertex3f( 1, -1, 0);
1036 glTexCoord2i(w, h); glVertex3f( 1, 1, 0);
1037 glTexCoord2i(0, h); glVertex3f(-1, 1, 0);
1038 glEnd();
1041 void renderfullscreenshader(int w, int h)
1043 if(!fsshader || renderpath==R_FIXEDFUNCTION) return;
1045 glDisable(GL_DEPTH_TEST);
1046 glDepthMask(GL_FALSE);
1047 glEnable(GL_TEXTURE_RECTANGLE_ARB);
1049 if(fs_w != w || fs_h != h)
1051 if(!fs_w && !fs_h)
1053 glGenTextures(NUMSCALE, rendertarget);
1054 if(hasFBO) glGenFramebuffers_(NUMSCALE-1, fsfb);
1056 loopi(NUMSCALE)
1057 createtexture(rendertarget[i], w>>i, h>>i, NULL, 3, false, GL_RGB, GL_TEXTURE_RECTANGLE_ARB);
1058 fs_w = w;
1059 fs_h = h;
1060 if(fsfb[0])
1062 loopi(NUMSCALE-1)
1064 glBindFramebuffer_(GL_FRAMEBUFFER_EXT, fsfb[i]);
1065 glFramebufferTexture2D_(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, rendertarget[i+1], 0);
1067 glBindFramebuffer_(GL_FRAMEBUFFER_EXT, 0);
1071 setenvparamfv("fsparams", SHPARAM_PIXEL, 0, fsparams);
1072 setenvparamf("millis", SHPARAM_VERTEX, 1, lastmillis/1000.0f, lastmillis/1000.0f, lastmillis/1000.0f);
1074 int nw = w, nh = h;
1076 loopi(fspasses)
1078 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, rendertarget[i*fsskip]);
1079 glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, 0, 0, nw, nh);
1080 if(i>=fspasses-1 || !scaleshader || fsfb[0]) break;
1081 renderfsquad(nw >>= fsskip, nh >>= fsskip, !i && initshader ? initshader : scaleshader);
1083 if(scaleshader && fsfb[0])
1085 loopi(fspasses-1)
1087 if(i) glBindTexture(GL_TEXTURE_RECTANGLE_ARB, rendertarget[i*fsskip]);
1088 glBindFramebuffer_(GL_FRAMEBUFFER_EXT, fsfb[(i+1)*fsskip-1]);
1089 renderfsquad(nw >>= fsskip, nh >>= fsskip, !i && initshader ? initshader : scaleshader);
1091 glBindFramebuffer_(GL_FRAMEBUFFER_EXT, 0);
1094 if(scaleshader) loopi(fspasses)
1096 glActiveTexture_(GL_TEXTURE0_ARB+i);
1097 glEnable(GL_TEXTURE_RECTANGLE_ARB);
1098 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, rendertarget[i*fsskip]);
1100 renderfsquad(w, h, fsshader);
1102 if(scaleshader) loopi(fspasses)
1104 glActiveTexture_(GL_TEXTURE0_ARB+i);
1105 glDisable(GL_TEXTURE_RECTANGLE_ARB);
1108 glActiveTexture_(GL_TEXTURE0_ARB);
1109 glDepthMask(GL_TRUE);
1110 glEnable(GL_DEPTH_TEST);
1113 struct tmufunc
1115 GLenum combine, sources[3], ops[3];
1116 int scale;
1119 struct tmu
1121 GLenum mode;
1122 GLfloat color[4];
1123 tmufunc rgb, alpha;
1126 #define INVALIDTMU \
1128 0, \
1129 { -1, -1, -1, -1 }, \
1130 { 0, { 0, 0, 0, }, { 0, 0, 0 }, 0 }, \
1131 { 0, { 0, 0, 0, }, { 0, 0, 0 }, 0 } \
1134 #define INITTMU \
1136 GL_MODULATE, \
1137 { 0, 0, 0, 0 }, \
1138 { GL_MODULATE, { GL_TEXTURE, GL_PREVIOUS_ARB, GL_CONSTANT_ARB }, { GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_ALPHA }, 1 }, \
1139 { GL_MODULATE, { GL_TEXTURE, GL_PREVIOUS_ARB, GL_CONSTANT_ARB }, { GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA }, 1 } \
1142 #define MAXTMUS 8
1144 tmu tmus[MAXTMUS] =
1146 INVALIDTMU,
1147 INVALIDTMU,
1148 INVALIDTMU,
1149 INVALIDTMU,
1150 INVALIDTMU,
1151 INVALIDTMU,
1152 INVALIDTMU,
1153 INVALIDTMU
1156 VAR(maxtmus, 1, 0, 0);
1158 void parsetmufunc(tmufunc &f, const char *s)
1160 int arg = -1;
1161 while(*s) switch(tolower(*s++))
1163 case 't': f.sources[++arg] = GL_TEXTURE; f.ops[arg] = GL_SRC_COLOR; break;
1164 case 'p': f.sources[++arg] = GL_PREVIOUS_ARB; f.ops[arg] = GL_SRC_COLOR; break;
1165 case 'k': f.sources[++arg] = GL_CONSTANT_ARB; f.ops[arg] = GL_SRC_COLOR; break;
1166 case 'c': f.sources[++arg] = GL_PRIMARY_COLOR_ARB; f.ops[arg] = GL_SRC_COLOR; break;
1167 case '~': f.ops[arg] = GL_ONE_MINUS_SRC_COLOR; break;
1168 case 'a': f.ops[arg] = f.ops[arg]==GL_ONE_MINUS_SRC_COLOR ? GL_ONE_MINUS_SRC_ALPHA : GL_SRC_ALPHA; break;
1169 case '=': f.combine = GL_REPLACE; break;
1170 case '*': f.combine = GL_MODULATE; break;
1171 case '+': f.combine = GL_ADD; break;
1172 case '-': f.combine = GL_SUBTRACT_ARB; break;
1173 case ',':
1174 case '@': f.combine = GL_INTERPOLATE_ARB; break;
1175 case '.': f.combine = GL_DOT3_RGB_ARB; break;
1176 case 'x': while(!isdigit(*s)) s++; f.scale = *s++-'0'; break;
1180 void committmufunc(bool rgb, tmufunc &dst, tmufunc &src)
1182 if(dst.combine!=src.combine) glTexEnvi(GL_TEXTURE_ENV, rgb ? GL_COMBINE_RGB_ARB : GL_COMBINE_ALPHA_ARB, src.combine);
1183 loopi(3)
1185 if(dst.sources[i]!=src.sources[i]) glTexEnvi(GL_TEXTURE_ENV, (rgb ? GL_SOURCE0_RGB_ARB : GL_SOURCE0_ALPHA_ARB)+i, src.sources[i]);
1186 if(dst.ops[i]!=src.ops[i]) glTexEnvi(GL_TEXTURE_ENV, (rgb ? GL_OPERAND0_RGB_ARB : GL_OPERAND0_ALPHA_ARB)+i, src.ops[i]);
1188 if(dst.scale!=src.scale) glTexEnvi(GL_TEXTURE_ENV, rgb ? GL_RGB_SCALE_ARB : GL_ALPHA_SCALE, src.scale);
1191 void committmu(int n, tmu &f)
1193 if(renderpath!=R_FIXEDFUNCTION || n>=maxtmus) return;
1194 if(tmus[n].mode!=f.mode) glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, f.mode);
1195 if(memcmp(tmus[n].color, f.color, sizeof(f.color))) glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, f.color);
1196 committmufunc(true, tmus[n].rgb, f.rgb);
1197 committmufunc(false, tmus[n].alpha, f.alpha);
1198 tmus[n] = f;
1201 void resettmu(int n)
1203 tmu f = tmus[n];
1204 f.mode = GL_MODULATE;
1205 f.rgb.scale = 1;
1206 f.alpha.scale = 1;
1207 committmu(n, f);
1210 void scaletmu(int n, int rgbscale, int alphascale)
1212 tmu f = tmus[n];
1213 if(rgbscale) f.rgb.scale = rgbscale;
1214 if(alphascale) f.alpha.scale = alphascale;
1215 committmu(n, f);
1218 void colortmu(int n, float r, float g, float b, float a)
1220 tmu f = tmus[n];
1221 f.color[0] = r;
1222 f.color[1] = g;
1223 f.color[2] = b;
1224 f.color[3] = a;
1225 committmu(n, f);
1228 void setuptmu(int n, const char *rgbfunc, const char *alphafunc)
1230 static tmu init = INITTMU;
1231 tmu f = tmus[n];
1233 f.mode = GL_COMBINE_ARB;
1234 if(rgbfunc) parsetmufunc(f.rgb, rgbfunc);
1235 else f.rgb = init.rgb;
1236 if(alphafunc) parsetmufunc(f.alpha, alphafunc);
1237 else f.alpha = init.alpha;
1239 committmu(n, f);
1242 VAR(nolights, 1, 0, 0);
1243 VAR(nowater, 1, 0, 0);
1244 VAR(nomasks, 1, 0, 0);
1246 void inittmus()
1248 if(hasTE && hasMT)
1250 glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, (GLint *)&maxtmus);
1251 maxtmus = max(1, min(MAXTMUS, maxtmus));
1252 loopi(maxtmus)
1254 glActiveTexture_(GL_TEXTURE0_ARB+i);
1255 resettmu(i);
1257 glActiveTexture_(GL_TEXTURE0_ARB);
1259 if(renderpath==R_FIXEDFUNCTION)
1261 if(maxtmus<4) caustics = 0;
1262 if(maxtmus<2)
1264 nolights = nowater = nomasks = 1;
1265 extern int lightmodels;
1266 lightmodels = 0;
1267 refractfog = 0;