1 // shader.cpp: OpenGL GLSL shader management
2 
3 #include "engine.h"
4 
5 Shader *Shader::lastshader = NULL;
6 
7 Shader *nullshader = NULL, *hudshader = NULL, *hudnotextureshader = NULL, *textureshader = NULL, *notextureshader = NULL, *nocolorshader = NULL, *foggedshader = NULL, *foggednotextureshader = NULL, *stdworldshader = NULL;
8 
9 static hashnameset<GlobalShaderParamState> globalparams(256);
10 static hashtable<const char *, int> localparams(256);
11 static hashnameset<Shader> shaders(256);
12 static Shader *slotshader = NULL;
13 static vector<SlotShaderParam> slotparams;
14 static bool standardshaders = false, forceshaders = true, loadedshaders = false;
15 
16 VAR(0, reservevpparams, 1, 16, 0);
17 VAR(0, maxvsuniforms, 1, 0, 0);
18 VAR(0, maxfsuniforms, 1, 0, 0);
19 VAR(0, maxvaryings, 1, 0, 0);
20 VAR(0, dbgshader, 0, 0, 2);
21 
loadshaders()22 void loadshaders()
23 {
24     standardshaders = true;
25     execfile("config/glsl.cfg");
26     standardshaders = false;
27 
28     nullshader = lookupshaderbyname("null");
29     hudshader = lookupshaderbyname("hud");
30     hudnotextureshader = lookupshaderbyname("hudnotexture");
31     stdworldshader = lookupshaderbyname("stdworld");
32     if(!nullshader || !hudshader || !hudnotextureshader || !stdworldshader) fatal("cannot find shader definitions");
33 
34     dummyslot.shader = stdworldshader;
35 
36     textureshader = lookupshaderbyname("texture");
37     notextureshader = lookupshaderbyname("notexture");
38     nocolorshader = lookupshaderbyname("nocolor");
39     foggedshader = lookupshaderbyname("fogged");
40     foggednotextureshader = lookupshaderbyname("foggednotexture");
41 
42     nullshader->set();
43 
44     loadedshaders = true;
45 }
46 
lookupshaderbyname(const char * name)47 Shader *lookupshaderbyname(const char *name)
48 {
49     Shader *s = shaders.access(name);
50     return s && s->loaded() ? s : NULL;
51 }
52 
generateshader(const char * name,const char * fmt,...)53 Shader *generateshader(const char *name, const char *fmt, ...)
54 {
55     if(!loadedshaders) return NULL;
56     Shader *s = name ? lookupshaderbyname(name) : NULL;
57     if(!s)
58     {
59         defvformatstring(cmd, fmt, fmt);
60         standardshaders = true;
61         execute(cmd, true);
62         standardshaders = false;
63         s = name ? lookupshaderbyname(name) : NULL;
64         if(!s) s = nullshader;
65     }
66     return s;
67 }
68 
showglslinfo(GLenum type,GLuint obj,const char * name,const char ** parts=NULL,int numparts=0)69 static void showglslinfo(GLenum type, GLuint obj, const char *name, const char **parts = NULL, int numparts = 0)
70 {
71     GLint length = 0;
72     if(type) glGetShaderiv_(obj, GL_INFO_LOG_LENGTH, &length);
73     else glGetProgramiv_(obj, GL_INFO_LOG_LENGTH, &length);
74     if(length > 1)
75     {
76         conoutf("\frGLSL ERROR (%s:%s)", type == GL_VERTEX_SHADER ? "VS" : (type == GL_FRAGMENT_SHADER ? "FS" : "PROG"), name);
77         FILE *l = getlogfile();
78         if(l)
79         {
80             GLchar *log = new GLchar[length];
81             if(type) glGetShaderInfoLog_(obj, length, &length, log);
82             else glGetProgramInfoLog_(obj, length, &length, log);
83             fprintf(l, "%s\n", log);
84             bool partlines = log[0] != '0';
85             int line = 0;
86             loopi(numparts)
87             {
88                 const char *part = parts[i];
89                 int startline = line;
90                 while(*part)
91                 {
92                     const char *next = strchr(part, '\n');
93                     if(++line > 1000) goto done;
94                     if(partlines) fprintf(l, "%d(%d): ", i, line - startline); else fprintf(l, "%d: ", line);
95                     fwrite(part, 1, next ? next - part + 1 : strlen(part), l);
96                     if(!next) { fputc('\n', l); break; }
97                     part = next + 1;
98                 }
99             }
100         done:
101             delete[] log;
102         }
103     }
104 }
105 
compileglslshader(GLenum type,GLuint & obj,const char * def,const char * name,bool msg=true)106 static void compileglslshader(GLenum type, GLuint &obj, const char *def, const char *name, bool msg = true)
107 {
108     const char *source = def + strspn(def, " \t\r\n");
109     const char *parts[16];
110     int numparts = 0;
111     static const struct { int version; const char * const header; } glslversions[] =
112     {
113         { 330, "#version 330\n" },
114         { 150, "#version 150\n" },
115         { 130, "#version 130\n" },
116         { 120, "#version 120\n" }
117     };
118     loopi(sizeof(glslversions)/sizeof(glslversions[0])) if(glslversion >= glslversions[i].version)
119     {
120         parts[numparts++] = glslversions[i].header;
121         break;
122     }
123     if(glslversion >= 130)
124     {
125         if(type == GL_VERTEX_SHADER) parts[numparts++] =
126             "#define attribute in\n"
127             "#define varying out\n";
128         else if(type == GL_FRAGMENT_SHADER)
129         {
130             parts[numparts++] = "#define varying in\n";
131             if(glslversion < 150) parts[numparts++] = "precision highp float;\n";
132             if(glversion >= 300) parts[numparts++] =
133                 "out vec4 cube2_FragColor;\n"
134                 "#define gl_FragColor cube2_FragColor\n";
135         }
136         parts[numparts++] =
137             "#define texture2D(sampler, coords) texture(sampler, coords)\n"
138             "#define texture2DProj(sampler, coords) textureProj(sampler, coords)\n"
139             "#define textureCube(sampler, coords) texture(sampler, coords)\n";
140     }
141     parts[numparts++] = source;
142 
143     obj = glCreateShader_(type);
144     glShaderSource_(obj, numparts, (const GLchar **)parts, NULL);
145     glCompileShader_(obj);
146     GLint success;
147     glGetShaderiv_(obj, GL_COMPILE_STATUS, &success);
148     if(!success)
149     {
150         if(msg) showglslinfo(type, obj, name, parts, numparts);
151         glDeleteShader_(obj);
152         obj = 0;
153     }
154     else if(dbgshader > 1 && msg) showglslinfo(type, obj, name, parts, numparts);
155 }
156 
157 VAR(0, dbgubo, 0, 0, 1);
158 
bindglsluniform(Shader & s,UniformLoc & u)159 static void bindglsluniform(Shader &s, UniformLoc &u)
160 {
161     u.loc = glGetUniformLocation_(s.program, u.name);
162     if(!u.blockname || !hasUBO) return;
163     GLuint bidx = glGetUniformBlockIndex_(s.program, u.blockname);
164     GLuint uidx = GL_INVALID_INDEX;
165     glGetUniformIndices_(s.program, 1, &u.name, &uidx);
166     if(bidx != GL_INVALID_INDEX && uidx != GL_INVALID_INDEX)
167     {
168         GLint sizeval = 0, offsetval = 0, strideval = 0;
169         glGetActiveUniformBlockiv_(s.program, bidx, GL_UNIFORM_BLOCK_DATA_SIZE, &sizeval);
170         if(sizeval <= 0) return;
171         glGetActiveUniformsiv_(s.program, 1, &uidx, GL_UNIFORM_OFFSET, &offsetval);
172         if(u.stride > 0)
173         {
174             glGetActiveUniformsiv_(s.program, 1, &uidx, GL_UNIFORM_ARRAY_STRIDE, &strideval);
175             if(strideval > u.stride) return;
176         }
177         u.offset = offsetval;
178         u.size = sizeval;
179         glUniformBlockBinding_(s.program, bidx, u.binding);
180         if(dbgubo) conoutf("UBO: %s:%s:%d, offset: %d, size: %d, stride: %d", u.name, u.blockname, u.binding, offsetval, sizeval, strideval);
181     }
182 }
183 
linkglslprogram(Shader & s,bool msg=true)184 static void linkglslprogram(Shader &s, bool msg = true)
185 {
186     s.program = s.vsobj && s.psobj ? glCreateProgram_() : 0;
187     GLint success = 0;
188     if(s.program)
189     {
190         glAttachShader_(s.program, s.vsobj);
191         glAttachShader_(s.program, s.psobj);
192         uint attribs = 0;
193         loopv(s.attriblocs)
194         {
195             AttribLoc &a = s.attriblocs[i];
196             glBindAttribLocation_(s.program, a.loc, a.name);
197             attribs |= 1<<a.loc;
198         }
199         loopi(gle::MAXATTRIBS) if(!(attribs&(1<<i))) glBindAttribLocation_(s.program, i, gle::attribnames[i]);
200         if(glversion >= 300)
201         {
202             glBindFragDataLocation_(s.program, 0, "cube2_FragColor");
203         }
204         glLinkProgram_(s.program);
205         glGetProgramiv_(s.program, GL_LINK_STATUS, &success);
206     }
207     if(success)
208     {
209         glUseProgram_(s.program);
210         loopi(8)
211         {
212             static const char * const texnames[8] = { "tex0", "tex1", "tex2", "tex3", "tex4", "tex5", "tex6", "tex7" };
213             GLint loc = glGetUniformLocation_(s.program, texnames[i]);
214             if(loc != -1) glUniform1i_(loc, i);
215         }
216         loopv(s.defaultparams)
217         {
218             SlotShaderParamState &param = s.defaultparams[i];
219             param.loc = glGetUniformLocation_(s.program, param.name);
220         }
221         loopv(s.uniformlocs) bindglsluniform(s, s.uniformlocs[i]);
222         glUseProgram_(0);
223     }
224     else if(s.program)
225     {
226         if(msg) showglslinfo(GL_FALSE, s.program, s.name);
227         glDeleteProgram_(s.program);
228         s.program = 0;
229     }
230 }
231 
getlocalparam(const char * name)232 int getlocalparam(const char *name)
233 {
234     return localparams.access(name, int(localparams.numelems));
235 }
236 
addlocalparam(Shader & s,const char * name,int loc,int size,GLenum format)237 static int addlocalparam(Shader &s, const char *name, int loc, int size, GLenum format)
238 {
239     int idx = getlocalparam(name);
240     if(idx >= s.localparamremap.length())
241     {
242         int n = idx + 1 - s.localparamremap.length();
243         memset(s.localparamremap.pad(n), 0xFF, n);
244     }
245     s.localparamremap[idx] = s.localparams.length();
246 
247     LocalShaderParamState &l = s.localparams.add();
248     l.name = name;
249     l.loc = loc;
250     l.size = size;
251     l.format = format;
252     return idx;
253 }
254 
getglobalparam(const char * name)255 GlobalShaderParamState *getglobalparam(const char *name)
256 {
257     GlobalShaderParamState *param = globalparams.access(name);
258     if(!param)
259     {
260         param = &globalparams[name];
261         param->name = name;
262         memset(param->buf, -1, sizeof(param->buf));
263         param->version = -1;
264     }
265     return param;
266 }
267 
addglobalparam(Shader & s,GlobalShaderParamState * param,int loc,int size,GLenum format)268 static GlobalShaderParamUse *addglobalparam(Shader &s, GlobalShaderParamState *param, int loc, int size, GLenum format)
269 {
270     GlobalShaderParamUse &g = s.globalparams.add();
271     g.param = param;
272     g.version = -2;
273     g.loc = loc;
274     g.size = size;
275     g.format = format;
276     return &g;
277 }
278 
setglsluniformformat(Shader & s,const char * name,GLenum format,int size)279 static void setglsluniformformat(Shader &s, const char *name, GLenum format, int size)
280 {
281     switch(format)
282     {
283         case GL_FLOAT:
284         case GL_FLOAT_VEC2:
285         case GL_FLOAT_VEC3:
286         case GL_FLOAT_VEC4:
287         case GL_INT:
288         case GL_INT_VEC2:
289         case GL_INT_VEC3:
290         case GL_INT_VEC4:
291         case GL_BOOL:
292         case GL_BOOL_VEC2:
293         case GL_BOOL_VEC3:
294         case GL_BOOL_VEC4:
295         case GL_FLOAT_MAT2:
296         case GL_FLOAT_MAT3:
297         case GL_FLOAT_MAT4:
298             break;
299         default:
300             return;
301     }
302     if(!strncmp(name, "gl_", 3)) return;
303 
304     int loc = glGetUniformLocation_(s.program, name);
305     if(loc < 0) return;
306     loopvj(s.defaultparams) if(s.defaultparams[j].loc == loc)
307     {
308         s.defaultparams[j].format = format;
309         return;
310     }
311     loopvj(s.uniformlocs) if(s.uniformlocs[j].loc == loc) return;
312     loopvj(s.globalparams) if(s.globalparams[j].loc == loc) return;
313     loopvj(s.localparams) if(s.localparams[j].loc == loc) return;
314 
315     name = getshaderparamname(name);
316     GlobalShaderParamState *param = globalparams.access(name);
317     if(param) addglobalparam(s, param, loc, size, format);
318     else addlocalparam(s, name, loc, size, format);
319 }
320 
allocglslactiveuniforms(Shader & s)321 static void allocglslactiveuniforms(Shader &s)
322 {
323     GLint numactive = 0;
324     glGetProgramiv_(s.program, GL_ACTIVE_UNIFORMS, &numactive);
325     string name;
326     loopi(numactive)
327     {
328         GLsizei namelen = 0;
329         GLint size = 0;
330         GLenum format = GL_FLOAT_VEC4;
331         name[0] = '\0';
332         glGetActiveUniform_(s.program, i, sizeof(name)-1, &namelen, &size, &format, name);
333         if(namelen <= 0 || size <= 0) continue;
334         name[clamp(int(namelen), 0, (int)sizeof(name)-2)] = '\0';
335         char *brak = strchr(name, '[');
336         if(brak) *brak = '\0';
337         setglsluniformformat(s, name, format, size);
338     }
339 }
340 
allocparams(Slot * slot)341 void Shader::allocparams(Slot *slot)
342 {
343     if(slot)
344     {
345 #define UNIFORMTEX(name, tmu) \
346         { \
347             loc = glGetUniformLocation_(program, name); \
348             int val = tmu; \
349             if(loc != -1) glUniform1i_(loc, val); \
350         }
351         int loc, tmu = 2;
352         if(type & SHADER_NORMALSLMS)
353         {
354             UNIFORMTEX("lmcolor", 1);
355             UNIFORMTEX("lmdir", 2);
356             tmu++;
357         }
358         else UNIFORMTEX("lightmap", 1);
359         if(type & SHADER_ENVMAP) UNIFORMTEX("envmap", tmu++);
360         UNIFORMTEX("shadowmap", 7);
361         int stex = 0;
362         loopv(slot->sts)
363         {
364             Slot::Tex &t = slot->sts[i];
365             switch(t.type)
366             {
367                 case TEX_DIFFUSE: UNIFORMTEX("diffusemap", 0); break;
368                 case TEX_NORMAL: UNIFORMTEX("normalmap", tmu++); break;
369                 case TEX_GLOW: UNIFORMTEX("glowmap", tmu++); break;
370                 case TEX_DECAL: UNIFORMTEX("decal", tmu++); break;
371                 case TEX_SPEC: if(t.combined<0) UNIFORMTEX("specmap", tmu++); break;
372                 case TEX_DEPTH: if(t.combined<0) UNIFORMTEX("depthmap", tmu++); break;
373                 case TEX_UNKNOWN:
374                 {
375                     defformatstring(sname, "stex%d", stex++);
376                     UNIFORMTEX(sname, tmu++);
377                     break;
378                 }
379             }
380         }
381     }
382     allocglslactiveuniforms(*this);
383 }
384 
385 int GlobalShaderParamState::nextversion = 0;
386 
resetversions()387 void GlobalShaderParamState::resetversions()
388 {
389     enumerate(shaders, Shader, s,
390     {
391         loopv(s.globalparams)
392         {
393             GlobalShaderParamUse &u = s.globalparams[i];
394             if(u.version != u.param->version) u.version = -2;
395         }
396     });
397     nextversion = 0;
398     enumerate(globalparams, GlobalShaderParamState, g, { g.version = ++nextversion; });
399     enumerate(shaders, Shader, s,
400     {
401         loopv(s.globalparams)
402         {
403             GlobalShaderParamUse &u = s.globalparams[i];
404             if(u.version >= 0) u.version = u.param->version;
405         }
406     });
407 }
408 
findslotparam(Slot & s,const char * name)409 static SlotShaderParamValue *findslotparam(Slot &s, const char *name)
410 {
411     loopv(s.params)
412     {
413         SlotShaderParam &param = s.params[i];
414         if(name == param.name) return &param;
415     }
416     loopv(s.shader->defaultparams)
417     {
418         SlotShaderParamState &param = s.shader->defaultparams[i];
419         if(name == param.name) return &param;
420     }
421     return NULL;
422 }
423 
findslotparam(VSlot & s,const char * name)424 static SlotShaderParamValue *findslotparam(VSlot &s, const char *name)
425 {
426     loopv(s.params)
427     {
428         SlotShaderParam &param = s.params[i];
429         if(name == param.name) return &param;
430     }
431     return findslotparam(*s.slot, name);
432 }
433 
findslotparam(VSlot & s,const char * name,float * noval)434 static inline float *findslotparam(VSlot &s, const char *name, float *noval)
435 {
436     SlotShaderParamValue *p = findslotparam(s, name);
437     return p ? p->val : noval;
438 }
439 
setslotparam(SlotShaderParamState & l,const float * val)440 static inline void setslotparam(SlotShaderParamState &l, const float *val)
441 {
442     switch(l.format)
443     {
444         case GL_BOOL:
445         case GL_FLOAT:      glUniform1fv_(l.loc, 1, val); break;
446         case GL_BOOL_VEC2:
447         case GL_FLOAT_VEC2: glUniform2fv_(l.loc, 1, val); break;
448         case GL_BOOL_VEC3:
449         case GL_FLOAT_VEC3: glUniform3fv_(l.loc, 1, val); break;
450         case GL_BOOL_VEC4:
451         case GL_FLOAT_VEC4: glUniform4fv_(l.loc, 1, val); break;
452         case GL_INT:      glUniform1i_(l.loc, int(val[0])); break;
453         case GL_INT_VEC2: glUniform2i_(l.loc, int(val[0]), int(val[1])); break;
454         case GL_INT_VEC3: glUniform3i_(l.loc, int(val[0]), int(val[1]), int(val[2])); break;
455         case GL_INT_VEC4: glUniform4i_(l.loc, int(val[0]), int(val[1]), int(val[2]), int(val[3])); break;
456     }
457 }
458 
459 #define SETSLOTPARAM(l, mask, i, val) do { \
460     if(!(mask&(1<<i))) { \
461         mask |= 1<<i; \
462         setslotparam(l, val); \
463     } \
464 } while(0)
465 
466 #define SETSLOTPARAMS(slotparams) \
467     loopv(slotparams) \
468     { \
469         SlotShaderParam &p = slotparams[i]; \
470         if(!defaultparams.inrange(p.loc)) continue; \
471         SlotShaderParamState &l = defaultparams[p.loc]; \
472         SETSLOTPARAM(l, unimask, p.loc, p.val); \
473     }
474 #define SETDEFAULTPARAMS \
475     loopv(defaultparams) \
476     { \
477         SlotShaderParamState &l = defaultparams[i]; \
478         SETSLOTPARAM(l, unimask, i, l.val); \
479     }
480 
setslotparams(Slot & slot)481 void Shader::setslotparams(Slot &slot)
482 {
483     uint unimask = 0;
484     SETSLOTPARAMS(slot.params)
485     SETDEFAULTPARAMS
486 }
487 
setslotparams(Slot & slot,VSlot & vslot)488 void Shader::setslotparams(Slot &slot, VSlot &vslot)
489 {
490     uint unimask = 0;
491     if(vslot.slot == &slot)
492     {
493         SETSLOTPARAMS(vslot.params)
494         SETSLOTPARAMS(slot.params)
495         SETDEFAULTPARAMS
496     }
497     else
498     {
499         SETSLOTPARAMS(slot.params)
500         SETDEFAULTPARAMS
501     }
502 }
503 
bindprograms()504 void Shader::bindprograms()
505 {
506     if(this == lastshader || type&(SHADER_DEFERRED|SHADER_INVALID)) return;
507     glUseProgram_(program);
508     lastshader = this;
509 }
510 
compile()511 bool Shader::compile()
512 {
513     if(!vsstr) vsobj = !reusevs || reusevs->invalid() ? 0 : reusevs->vsobj;
514     else compileglslshader(GL_VERTEX_SHADER,   vsobj, vsstr, name, dbgshader || !variantshader);
515     if(!psstr) psobj = !reuseps || reuseps->invalid() ? 0 : reuseps->psobj;
516     else compileglslshader(GL_FRAGMENT_SHADER, psobj, psstr, name, dbgshader || !variantshader);
517     linkglslprogram(*this, !variantshader);
518     return program!=0;
519 }
520 
cleanup(bool invalid)521 void Shader::cleanup(bool invalid)
522 {
523     detailshader = NULL;
524     used = false;
525     if(vsobj) { if(!reusevs) glDeleteShader_(vsobj); vsobj = 0; }
526     if(psobj) { if(!reuseps) glDeleteShader_(psobj); psobj = 0; }
527     if(program) { glDeleteProgram_(program); program = 0; }
528     localparams.setsize(0);
529     localparamremap.setsize(0);
530     globalparams.setsize(0);
531     if(standard || invalid)
532     {
533         type = SHADER_INVALID;
534         DELETEA(vsstr);
535         DELETEA(psstr);
536         DELETEA(defer);
537         variants.setsize(0);
538         DELETEA(variantrows);
539         defaultparams.setsize(0);
540         attriblocs.setsize(0);
541         uniformlocs.setsize(0);
542         altshader = NULL;
543         loopi(MAXSHADERDETAIL) fastshader[i] = this;
544         reusevs = reuseps = NULL;
545     }
546     else loopv(defaultparams) defaultparams[i].loc = -1;
547 }
548 
genattriblocs(Shader & s,const char * vs,const char * ps,Shader * reusevs,Shader * reuseps)549 static void genattriblocs(Shader &s, const char *vs, const char *ps, Shader *reusevs, Shader *reuseps)
550 {
551     static int len = strlen("//:attrib");
552     string name;
553     int loc;
554     if(reusevs) s.attriblocs = reusevs->attriblocs;
555     else while((vs = strstr(vs, "//:attrib")))
556     {
557         if(sscanf(vs, "//:attrib %100s %d", name, &loc) == 2)
558             s.attriblocs.add(AttribLoc(getshaderparamname(name), loc));
559         vs += len;
560     }
561 }
562 
genuniformlocs(Shader & s,const char * vs,const char * ps,Shader * reusevs,Shader * reuseps)563 static void genuniformlocs(Shader &s, const char *vs, const char *ps, Shader *reusevs, Shader *reuseps)
564 {
565     static int len = strlen("//:uniform");
566     string name, blockname;
567     int binding, stride;
568     if(reusevs) s.uniformlocs = reusevs->uniformlocs;
569     else while((vs = strstr(vs, "//:uniform")))
570     {
571         int numargs = sscanf(vs, "//:uniform %100s %100s %d %d", name, blockname, &binding, &stride);
572         if(numargs >= 3) s.uniformlocs.add(UniformLoc(getshaderparamname(name), getshaderparamname(blockname), binding, numargs >= 4 ? stride : 0));
573         else if(numargs >= 1) s.uniformlocs.add(UniformLoc(getshaderparamname(name)));
574         vs += len;
575     }
576 }
577 
newshader(int type,const char * name,const char * vs,const char * ps,Shader * variant=NULL,int row=0)578 Shader *newshader(int type, const char *name, const char *vs, const char *ps, Shader *variant = NULL, int row = 0)
579 {
580     if(Shader::lastshader)
581     {
582         glUseProgram_(0);
583         Shader::lastshader = NULL;
584     }
585 
586     Shader *exists = shaders.access(name);
587     char *rname = exists ? exists->name : newstring(name);
588     Shader &s = shaders[rname];
589     s.name = rname;
590     s.vsstr = newstring(vs);
591     s.psstr = newstring(ps);
592     DELETEA(s.defer);
593     s.type = type;
594     s.variantshader = variant;
595     s.standard = standardshaders;
596     if(forceshaders) s.forced = true;
597     s.reusevs = s.reuseps = NULL;
598     if(variant)
599     {
600         int row = 0, col = 0;
601         if(!vs[0] || sscanf(vs, "%d , %d", &row, &col) >= 1)
602         {
603             DELETEA(s.vsstr);
604             s.reusevs = !vs[0] ? variant : variant->getvariant(col, row);
605         }
606         row = col = 0;
607         if(!ps[0] || sscanf(ps, "%d , %d", &row, &col) >= 1)
608         {
609             DELETEA(s.psstr);
610             s.reuseps = !ps[0] ? variant : variant->getvariant(col, row);
611         }
612     }
613     if(variant) loopv(variant->defaultparams) s.defaultparams.add(variant->defaultparams[i]);
614     else loopv(slotparams) s.defaultparams.add(slotparams[i]);
615     s.attriblocs.setsize(0);
616     s.uniformlocs.setsize(0);
617     genattriblocs(s, vs, ps, s.reusevs, s.reuseps);
618     genuniformlocs(s, vs, ps, s.reusevs, s.reuseps);
619     if(!s.compile())
620     {
621         s.cleanup(true);
622         if(variant) shaders.remove(rname);
623         return NULL;
624     }
625     if(variant) variant->addvariant(row, &s);
626     s.fixdetailshader();
627     return &s;
628 }
629 
setupshaders()630 void setupshaders()
631 {
632     GLint val;
633     glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &val);
634     maxvsuniforms = val/4;
635     glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &val);
636     maxfsuniforms = val/4;
637     if(glversion < 300)
638     {
639         glGetIntegerv(GL_MAX_VARYING_COMPONENTS, &val);
640         maxvaryings = val;
641     }
642 
643     standardshaders = true;
644     nullshader = newshader(0, "<init>null",
645         "attribute vec4 vvertex;\n"
646         "void main(void) {\n"
647         "    gl_Position = vvertex;\n"
648         "}\n",
649         "void main(void) {\n"
650         "    gl_FragColor = vec4(1.0, 0.0, 1.0, 0.0);\n"
651         "}\n");
652     hudshader = newshader(0, "<init>hud",
653         "attribute vec4 vvertex, vcolor;\n"
654         "attribute vec2 vtexcoord0;\n"
655         "uniform mat4 hudmatrix;\n"
656         "varying vec2 texcoord0;\n"
657         "varying vec4 color;\n"
658         "void main(void) {\n"
659         "    gl_Position = hudmatrix * vvertex;\n"
660         "    texcoord0 = vtexcoord0;\n"
661         "    color = vcolor;\n"
662         "}\n",
663         "varying vec2 texcoord0;\n"
664         "varying vec4 color;\n"
665         "uniform sampler2D tex0;\n"
666         "void main(void) {\n"
667         "    gl_FragColor = color * texture2D(tex0, texcoord0);\n"
668         "}\n");
669     hudnotextureshader = newshader(0, "<init>hudnotexture",
670         "attribute vec4 vvertex, vcolor;\n"
671         "attribute vec2 vtexcoord0;\n"
672         "uniform mat4 hudmatrix;\n"
673         "varying vec4 color;\n"
674         "void main(void) {\n"
675         "    gl_Position = hudmatrix * vvertex;\n"
676         "    color = vcolor;\n"
677         "}\n",
678         "varying vec4 color;\n"
679         "void main(void) {\n"
680         "    gl_FragColor = color;\n"
681         "}\n");
682     standardshaders = false;
683 
684     if(!nullshader || !hudshader || !hudnotextureshader) fatal("failed to setup shaders");
685 
686     dummyslot.shader = nullshader;
687 }
688 
findglslmain(const char * s)689 static const char *findglslmain(const char *s)
690 {
691     const char *main = strstr(s, "main");
692     if(!main) return NULL;
693     for(; main >= s; main--) switch(*main) { case '\r': case '\n': case ';': return main + 1; }
694     return s;
695 }
696 
gengenericvariant(Shader & s,const char * sname,const char * vs,const char * ps,int row=0)697 static void gengenericvariant(Shader &s, const char *sname, const char *vs, const char *ps, int row = 0)
698 {
699     int rowoffset = 0;
700     bool vschanged = false, pschanged = false;
701     vector<char> vsv, psv;
702     vsv.put(vs, strlen(vs)+1);
703     psv.put(ps, strlen(ps)+1);
704 
705     static const int len = strlen("//:variant"), olen = strlen("override");
706     for(char *vspragma = vsv.getbuf();; vschanged = true)
707     {
708         vspragma = strstr(vspragma, "//:variant");
709         if(!vspragma) break;
710         if(sscanf(vspragma + len, "row %d", &rowoffset) == 1) continue;
711         memset(vspragma, ' ', len);
712         vspragma += len;
713         if(!strncmp(vspragma, "override", olen))
714         {
715             memset(vspragma, ' ', olen);
716             vspragma += olen;
717             char *end = vspragma + strcspn(vspragma, "\n\r");
718             end += strspn(end, "\n\r");
719             int endlen = strcspn(end, "\n\r");
720             memset(end, ' ', endlen);
721         }
722     }
723     for(char *pspragma = psv.getbuf();; pschanged = true)
724     {
725         pspragma = strstr(pspragma, "//:variant");
726         if(!pspragma) break;
727         if(sscanf(pspragma + len, "row %d", &rowoffset) == 1) continue;
728         memset(pspragma, ' ', len);
729         pspragma += len;
730         if(!strncmp(pspragma, "override", olen))
731         {
732             memset(pspragma, ' ', olen);
733             pspragma += olen;
734             char *end = pspragma + strcspn(pspragma, "\n\r");
735             end += strspn(end, "\n\r");
736             int endlen = strcspn(end, "\n\r");
737             memset(end, ' ', endlen);
738         }
739     }
740     row += rowoffset;
741     if(row < 0 || row >= MAXVARIANTROWS) return;
742     int col = s.numvariants(row);
743     defformatstring(varname, "<variant:%d,%d>%s", col, row, sname);
744     string reuse;
745     if(col) formatstring(reuse, "%d", row);
746     else copystring(reuse, "");
747     newshader(s.type, varname, vschanged ? vsv.getbuf() : reuse, pschanged ? psv.getbuf() : reuse, &s, row);
748 }
749 
genwatervariant(Shader & s,const char * sname,const char * vs,const char * ps,int row=2)750 static bool genwatervariant(Shader &s, const char *sname, const char *vs, const char *ps, int row = 2)
751 {
752     if(!strstr(vs, "//:water") && !strstr(ps, "//:water")) return false;
753 
754     vector<char> vsw, psw;
755 
756     const char *vsmain = findglslmain(vs), *vsend = strrchr(vs, '}');
757     if(!vsmain || !vsend) return false;
758     vsw.put(vs, vsmain - vs);
759     const char *fadeparams = "\nuniform vec4 waterfadeparams;\nvarying float fadedepth;\n";
760     vsw.put(fadeparams, strlen(fadeparams));
761     vsw.put(vsmain, vsend - vsmain);
762     const char *fadedef = "\nfadedepth = vvertex.z*waterfadeparams.x + waterfadeparams.y;\n";
763     vsw.put(fadedef, strlen(fadedef));
764     vsw.put(vsend, strlen(vsend)+1);
765 
766     const char *psmain = findglslmain(ps), *psend = strrchr(ps, '}');
767     if(!psmain || !psend) return false;
768     psw.put(ps, psmain - ps);
769     const char *fadeinterp = "\nvarying float fadedepth;\n";
770     psw.put(fadeinterp, strlen(fadeinterp));
771     psw.put(psmain, psend - psmain);
772     const char *fade = "\ngl_FragColor.a = fadedepth;\n";
773     psw.put(fade, strlen(fade));
774     psw.put(psend, strlen(psend)+1);
775 
776     defformatstring(name, "<water>%s", sname);
777     Shader *variant = newshader(s.type, name, vsw.getbuf(), psw.getbuf(), &s, row);
778     return variant!=NULL;
779 }
780 
minimizedynlighttcusage()781 bool minimizedynlighttcusage() { return glversion < 300 && maxvaryings < 48; }
782 
gendynlightvariant(Shader & s,const char * sname,const char * vs,const char * ps,int row=0)783 static void gendynlightvariant(Shader &s, const char *sname, const char *vs, const char *ps, int row = 0)
784 {
785     int numlights = minimizedynlighttcusage() ? 1 : MAXDYNLIGHTS;
786 
787     const char *vspragma = strstr(vs, "//:dynlight"), *pspragma = strstr(ps, "//:dynlight");
788     if(!vspragma || !pspragma) return;
789 
790     string pslight;
791     vspragma += strcspn(vspragma, "\n");
792     if(*vspragma) vspragma++;
793 
794     if(sscanf(pspragma, "//:dynlight %100s", pslight)!=1) return;
795 
796     pspragma += strcspn(pspragma, "\n");
797     if(*pspragma) pspragma++;
798 
799     const char *vsmain = findglslmain(vs), *psmain = findglslmain(ps);
800     if(vsmain > vspragma) vsmain = vs;
801     if(psmain > pspragma) psmain = ps;
802 
803     vector<char> vsdl, psdl;
804     loopi(MAXDYNLIGHTS)
805     {
806         vsdl.setsize(0);
807         psdl.setsize(0);
808         if(vsmain >= vs) vsdl.put(vs, vsmain - vs);
809         if(psmain >= ps) psdl.put(ps, psmain - ps);
810 
811         defformatstring(pos, "uniform vec4 dynlightpos[%d];\n", i+1);
812         vsdl.put(pos, strlen(pos));
813         psdl.put(pos, strlen(pos));
814         defformatstring(color, "uniform vec3 dynlightcolor[%d];\n", i+1);
815         psdl.put(color, strlen(color));
816 
817         loopk(min(i+1, numlights))
818         {
819             defformatstring(dir, "%sdynlight%ddir%s", !k ? "varying vec3 " : " ", k, k==i || k+1==numlights ? ";\n" : ",");
820             vsdl.put(dir, strlen(dir));
821             psdl.put(dir, strlen(dir));
822         }
823 
824         vsdl.put(vsmain, vspragma-vsmain);
825         psdl.put(psmain, pspragma-psmain);
826 
827         loopk(i+1)
828         {
829             defformatstring(tc,
830                 k<numlights ?
831                     "dynlight%ddir = vvertex.xyz*dynlightpos[%d].w + dynlightpos[%d].xyz;\n" :
832                     "vec3 dynlight%ddir = dynlight0dir*dynlightpos[%d].w + dynlightpos[%d].xyz;\n",
833                 k, k, k);
834             if(k < numlights) vsdl.put(tc, strlen(tc));
835             else psdl.put(tc, strlen(tc));
836 
837             defformatstring(dl,
838                 "%s.rgb += dynlightcolor[%d] * (1.0 - clamp(dot(dynlight%ddir, dynlight%ddir), 0.0, 1.0));\n",
839                 pslight, k, k, k);
840             psdl.put(dl, strlen(dl));
841         }
842 
843         vsdl.put(vspragma, strlen(vspragma)+1);
844         psdl.put(pspragma, strlen(pspragma)+1);
845 
846         defformatstring(name, "<dynlight %d>%s", i+1, sname);
847         Shader *variant = newshader(s.type, name, vsdl.getbuf(), psdl.getbuf(), &s, row);
848         if(!variant) return;
849         if(row < 4) genwatervariant(s, name, vsdl.getbuf(), psdl.getbuf(), row+2);
850     }
851 }
852 
genshadowmapvariant(Shader & s,const char * sname,const char * vs,const char * ps,int row=1)853 static void genshadowmapvariant(Shader &s, const char *sname, const char *vs, const char *ps, int row = 1)
854 {
855     const char *vspragma = strstr(vs, "//:shadowmap"), *pspragma = strstr(ps, "//:shadowmap");
856     if(!vspragma || !pspragma) return;
857 
858     string pslight;
859     vspragma += strcspn(vspragma, "\n");
860     if(*vspragma) vspragma++;
861 
862     if(sscanf(pspragma, "//:shadowmap %100s", pslight)!=1) return;
863 
864     pspragma += strcspn(pspragma, "\n");
865     if(*pspragma) pspragma++;
866 
867     const char *vsmain = findglslmain(vs), *psmain = findglslmain(ps);
868     if(vsmain > vspragma) vsmain = vs;
869     if(psmain > pspragma) psmain = ps;
870 
871     vector<char> vssm, pssm;
872     if(vsmain >= vs) vssm.put(vs, vsmain - vs);
873     if(psmain >= ps) pssm.put(ps, psmain - ps);
874 
875     const char *vsdecl =
876         "uniform mat4 shadowmapproject;\n"
877         "varying vec3 shadowmaptc;\n";
878     vssm.put(vsdecl, strlen(vsdecl));
879 
880     const char *psdecl =
881         "uniform sampler2D shadowmap;\n"
882         "uniform vec4 shadowmapambient;\n"
883         "varying vec3 shadowmaptc;\n";
884     pssm.put(psdecl, strlen(psdecl));
885 
886     vssm.put(vsmain, vspragma-vsmain);
887     pssm.put(psmain, pspragma-psmain);
888 
889     extern int smoothshadowmappeel;
890     const char *tcgen =
891         "shadowmaptc = vec3(shadowmapproject * vvertex);\n";
892     vssm.put(tcgen, strlen(tcgen));
893     const char *sm =
894         smoothshadowmappeel ?
895             "vec4 smvals = texture2D(shadowmap, shadowmaptc.xy);\n"
896             "vec2 smdiff = clamp(smvals.xz - shadowmaptc.zz*smvals.y, 0.0, 1.0);\n"
897             "float shadowed = clamp((smdiff.x > 0.0 ? smvals.w : 0.0) - 8.0*smdiff.y, 0.0, 1.0);\n" :
898 
899             "vec4 smvals = texture2D(shadowmap, shadowmaptc.xy);\n"
900             "float smtest = shadowmaptc.z*smvals.y;\n"
901             "float shadowed = smtest < smvals.x && smtest > smvals.z ? smvals.w : 0.0;\n";
902     pssm.put(sm, strlen(sm));
903     defformatstring(smlight,
904         "%s.rgb -= shadowed*clamp(%s.rgb - shadowmapambient.rgb, 0.0, 1.0);\n",
905         pslight, pslight);
906     pssm.put(smlight, strlen(smlight));
907 
908     vssm.put(vspragma, strlen(vspragma)+1);
909     pssm.put(pspragma, strlen(pspragma)+1);
910 
911     defformatstring(name, "<shadowmap>%s", sname);
912     Shader *variant = newshader(s.type, name, vssm.getbuf(), pssm.getbuf(), &s, row);
913     if(!variant) return;
914     genwatervariant(s, name, vssm.getbuf(), pssm.getbuf(), row+2);
915 
916     if(strstr(vs, "//:dynlight")) gendynlightvariant(s, name, vssm.getbuf(), pssm.getbuf(), row);
917 }
918 
genfogshader(vector<char> & vsbuf,vector<char> & psbuf,const char * vs,const char * ps)919 static void genfogshader(vector<char> &vsbuf, vector<char> &psbuf, const char *vs, const char *ps)
920 {
921     const char *vspragma = strstr(vs, "//:fog"), *pspragma = strstr(ps, "//:fog");
922     if(!vspragma && !pspragma) return;
923     static const int pragmalen = strlen("//:fog");
924     const char *vsmain = findglslmain(vs), *vsend = strrchr(vs, '}');
925     if(vsmain && vsend)
926     {
927         vsbuf.put(vs, vsmain - vs);
928         const char *fogparams = "\nuniform vec4 fogplane;\nvarying float fogcoord;\n";
929         vsbuf.put(fogparams, strlen(fogparams));
930         vsbuf.put(vsmain, vsend - vsmain);
931         const char *vsfog = "\nfogcoord = dot(fogplane, gl_Position);\n";
932         vsbuf.put(vsfog, strlen(vsfog));
933         vsbuf.put(vsend, strlen(vsend)+1);
934     }
935     const char *psmain = findglslmain(ps), *psend = strrchr(ps, '}');
936     if(psmain && psend)
937     {
938         psbuf.put(ps, psmain - ps);
939         const char *fogparams =
940             "\nuniform vec3 fogcolor;\n"
941             "uniform vec2 fogparams;\n"
942             "varying float fogcoord;\n";
943         psbuf.put(fogparams, strlen(fogparams));
944         psbuf.put(psmain, psend - psmain);
945         const char *psdef = "\n#define FOG_COLOR ";
946         const char *psfog =
947             pspragma && !strncmp(pspragma+pragmalen, "rgba", 4) ?
948                 "\ngl_FragColor = mix((FOG_COLOR), gl_FragColor, clamp(fogcoord*fogparams.x + fogparams.y, 0.0, 1.0));\n" :
949                 "\ngl_FragColor.rgb = mix((FOG_COLOR).rgb, gl_FragColor.rgb, clamp(fogcoord*fogparams.x + fogparams.y, 0.0, 1.0));\n";
950         int clen = 0;
951         if(pspragma)
952         {
953             pspragma += pragmalen;
954             while(iscubealpha(*pspragma)) pspragma++;
955             while(*pspragma && !iscubespace(*pspragma)) pspragma++;
956             pspragma += strspn(pspragma, " \t\v\f");
957             clen = strcspn(pspragma, "\r\n");
958         }
959         if(clen <= 0) { pspragma = "fogcolor"; clen = strlen(pspragma); }
960         psbuf.put(psdef, strlen(psdef));
961         psbuf.put(pspragma, clen);
962         psbuf.put(psfog, strlen(psfog));
963         psbuf.put(psend, strlen(psend)+1);
964     }
965 }
966 
genuniformdefs(vector<char> & vsbuf,vector<char> & psbuf,const char * vs,const char * ps,Shader * variant=NULL)967 static void genuniformdefs(vector<char> &vsbuf, vector<char> &psbuf, const char *vs, const char *ps, Shader *variant = NULL)
968 {
969     if(variant ? variant->defaultparams.empty() : slotparams.empty()) return;
970     const char *vsmain = findglslmain(vs), *psmain = findglslmain(ps);
971     if(!vsmain || !psmain) return;
972     vsbuf.put(vs, vsmain - vs);
973     psbuf.put(ps, psmain - ps);
974     if(variant) loopv(variant->defaultparams)
975     {
976         defformatstring(uni, "\nuniform vec4 %s;\n", variant->defaultparams[i].name);
977         vsbuf.put(uni, strlen(uni));
978         psbuf.put(uni, strlen(uni));
979     }
980     else loopv(slotparams)
981     {
982         defformatstring(uni, "\nuniform vec4 %s;\n", slotparams[i].name);
983         vsbuf.put(uni, strlen(uni));
984         psbuf.put(uni, strlen(uni));
985     }
986     vsbuf.put(vsmain, strlen(vsmain)+1);
987     psbuf.put(psmain, strlen(psmain)+1);
988 }
989 
990 VAR(0, defershaders, 0, 1, 1);
991 
defershader(int * type,const char * name,const char * contents)992 void defershader(int *type, const char *name, const char *contents)
993 {
994     Shader *exists = shaders.access(name);
995     if(exists && !exists->invalid()) return;
996     if(!defershaders) { execute(contents, true); return; }
997     char *rname = exists ? exists->name : newstring(name);
998     Shader &s = shaders[rname];
999     s.name = rname;
1000     DELETEA(s.defer);
1001     s.defer = newstring(contents);
1002     s.type = SHADER_DEFERRED | *type;
1003     s.standard = standardshaders;
1004 }
1005 
force()1006 void Shader::force()
1007 {
1008     if(!deferred()) return;
1009 
1010     char *cmd = defer;
1011     defer = NULL;
1012     bool wasstandard = standardshaders, wasforcing = forceshaders;
1013     standardshaders = standard;
1014     forceshaders = false;
1015     slotparams.shrink(0);
1016     execute(cmd, true);
1017     forceshaders = wasforcing;
1018     standardshaders = wasstandard;
1019     delete[] cmd;
1020 
1021     if(deferred())
1022     {
1023         DELETEA(defer);
1024         type = SHADER_INVALID;
1025     }
1026 }
1027 
fixshaderdetail()1028 void fixshaderdetail()
1029 {
1030     // must null out separately because fixdetailshader can recursively set it
1031     enumerate(shaders, Shader, s, { if(!s.forced) s.detailshader = NULL; });
1032     enumerate(shaders, Shader, s, { if(s.forced) s.fixdetailshader(); });
1033     linkslotshaders();
1034 }
1035 
uniformlocversion()1036 int Shader::uniformlocversion()
1037 {
1038     static int version = 0;
1039     if(++version >= 0) return version;
1040     version = 0;
1041     enumerate(shaders, Shader, s, { loopvj(s.uniformlocs) s.uniformlocs[j].version = -1; });
1042     return version;
1043 }
1044 
1045 VARF(IDF_PERSIST, shaderdetail, 0, MAXSHADERDETAIL, MAXSHADERDETAIL, fixshaderdetail());
1046 
fixdetailshader(bool shouldforce,bool recurse)1047 void Shader::fixdetailshader(bool shouldforce, bool recurse)
1048 {
1049     Shader *alt = this;
1050     detailshader = NULL;
1051     do
1052     {
1053         Shader *cur = shaderdetail < MAXSHADERDETAIL ? alt->fastshader[shaderdetail] : alt;
1054         if(cur->deferred() && shouldforce) cur->force();
1055         if(!cur->invalid())
1056         {
1057             if(cur->deferred()) break;
1058             detailshader = cur;
1059             break;
1060         }
1061         alt = alt->altshader;
1062     } while(alt && alt!=this);
1063 
1064     if(recurse && detailshader) loopv(detailshader->variants) detailshader->variants[i]->fixdetailshader(shouldforce, false);
1065 }
1066 
useshaderbyname(const char * name)1067 Shader *useshaderbyname(const char *name)
1068 {
1069     Shader *s = shaders.access(name);
1070     if(!s) return NULL;
1071     if(!s->detailshader) s->fixdetailshader();
1072     s->forced = true;
1073     return s;
1074 }
1075 
shader(int * type,char * name,char * vs,char * ps)1076 void shader(int *type, char *name, char *vs, char *ps)
1077 {
1078     if(lookupshaderbyname(name)) return;
1079 
1080     defformatstring(info, "shader %s", name);
1081     progress(loadprogress, info);
1082 
1083     vector<char> vsbuf, psbuf, vsbak, psbak;
1084 #define GENSHADER(cond, body) \
1085     if(cond) \
1086     { \
1087         if(vsbuf.length()) { vsbak.setsize(0); vsbak.put(vs, strlen(vs)+1); vs = vsbak.getbuf(); vsbuf.setsize(0); } \
1088         if(psbuf.length()) { psbak.setsize(0); psbak.put(ps, strlen(ps)+1); ps = psbak.getbuf(); psbuf.setsize(0); } \
1089         body; \
1090         if(vsbuf.length()) vs = vsbuf.getbuf(); \
1091         if(psbuf.length()) ps = psbuf.getbuf(); \
1092     }
1093     GENSHADER(slotparams.length(), genuniformdefs(vsbuf, psbuf, vs, ps));
1094     GENSHADER(strstr(vs, "//:fog") || strstr(ps, "//:fog"), genfogshader(vsbuf, psbuf, vs, ps));
1095     Shader *s = newshader(*type, name, vs, ps);
1096     if(s)
1097     {
1098         if(strstr(vs, "//:water")) genwatervariant(*s, s->name, vs, ps);
1099         if(strstr(vs, "//:shadowmap")) genshadowmapvariant(*s, s->name, vs, ps);
1100         if(strstr(vs, "//:dynlight")) gendynlightvariant(*s, s->name, vs, ps);
1101     }
1102     slotparams.shrink(0);
1103 }
1104 
variantshader(int * type,char * name,int * row,char * vs,char * ps)1105 void variantshader(int *type, char *name, int *row, char *vs, char *ps)
1106 {
1107     if(*row < 0)
1108     {
1109         shader(type, name, vs, ps);
1110         return;
1111     }
1112     else if(*row >= MAXVARIANTROWS) return;
1113 
1114     Shader *s = lookupshaderbyname(name);
1115     if(!s) return;
1116 
1117     defformatstring(varname, "<variant:%d,%d>%s", s->numvariants(*row), *row, name);
1118     //defformatstring(info, "shader %s", varname);
1119     //progress(loadprogress, info);
1120     vector<char> vsbuf, psbuf, vsbak, psbak;
1121     GENSHADER(s->defaultparams.length(), genuniformdefs(vsbuf, psbuf, vs, ps, s));
1122     GENSHADER(strstr(vs, "//:fog") || strstr(ps, "//:fog"), genfogshader(vsbuf, psbuf, vs, ps));
1123     Shader *v = newshader(*type, varname, vs, ps, s, *row);
1124     if(v)
1125     {
1126         if(strstr(vs, "//:dynlight")) gendynlightvariant(*s, varname, vs, ps, *row);
1127         if(strstr(ps, "//:variant") || strstr(vs, "//:variant")) gengenericvariant(*s, varname, vs, ps, *row);
1128     }
1129 }
1130 
setshader(char * name)1131 void setshader(char *name)
1132 {
1133     slotparams.shrink(0);
1134     Shader *s = shaders.access(name);
1135     if(!s)
1136     {
1137         conoutf("\frno such shader: %s", name);
1138     }
1139     else slotshader = s;
1140 }
1141 
resetslotshader()1142 void resetslotshader()
1143 {
1144     slotshader = NULL;
1145     slotparams.shrink(0);
1146 }
1147 
setslotshader(Slot & s)1148 void setslotshader(Slot &s)
1149 {
1150     s.shader = slotshader;
1151     if(!s.shader)
1152     {
1153         s.shader = stdworldshader;
1154         return;
1155     }
1156     loopv(slotparams) s.params.add(slotparams[i]);
1157 }
1158 
linkslotshaderparams(vector<SlotShaderParam> & params,Shader * sh,bool load)1159 static void linkslotshaderparams(vector<SlotShaderParam> &params, Shader *sh, bool load)
1160 {
1161     if(sh) loopv(params)
1162     {
1163         int loc = -1;
1164         SlotShaderParam &param = params[i];
1165         loopv(sh->defaultparams)
1166         {
1167             SlotShaderParamState &dparam = sh->defaultparams[i];
1168             if(dparam.name==param.name)
1169             {
1170                 if(param.palette != dparam.palette || param.palindex != dparam.palindex ||
1171                    memcmp(param.val, dparam.val, sizeof(param.val)))
1172                     loc = i;
1173                 break;
1174             }
1175         }
1176         param.loc = loc;
1177     }
1178     else if(load) loopv(params) params[i].loc = -1;
1179 }
1180 
linkslotshader(Slot & s,bool load)1181 void linkslotshader(Slot &s, bool load)
1182 {
1183     if(!s.shader) return;
1184 
1185     if(load && !s.shader->detailshader) s.shader->fixdetailshader();
1186 
1187     linkslotshaderparams(s.params, s.shader->detailshader, load);
1188 }
1189 
linkvslotshader(VSlot & s,bool load)1190 void linkvslotshader(VSlot &s, bool load)
1191 {
1192     if(!s.slot->shader) return;
1193 
1194     Shader *sh = s.slot->shader->detailshader;
1195     linkslotshaderparams(s.params, sh, load);
1196 
1197     if(!sh) return;
1198 
1199     if(s.slot->texmask&(1<<TEX_GLOW))
1200     {
1201         static const char *paramname = getshaderparamname("glowcolor");
1202         s.glowcolor = findslotparam(s, paramname);
1203     }
1204 }
1205 
altshader(char * origname,char * altname)1206 void altshader(char *origname, char *altname)
1207 {
1208     Shader *orig = shaders.access(origname), *alt = shaders.access(altname);
1209     if(!orig || !alt) return;
1210     orig->altshader = alt;
1211     orig->fixdetailshader(false);
1212 }
1213 
fastshader(char * nice,char * fast,int * detail)1214 void fastshader(char *nice, char *fast, int *detail)
1215 {
1216     Shader *ns = shaders.access(nice), *fs = shaders.access(fast);
1217     if(!ns || !fs) return;
1218     loopi(min(*detail+1, MAXSHADERDETAIL)) ns->fastshader[i] = fs;
1219     ns->fixdetailshader(false);
1220 }
1221 
1222 COMMAND(0, shader, "isss");
1223 COMMAND(0, variantshader, "isiss");
1224 COMMAND(0, setshader, "s");
1225 COMMAND(0, altshader, "ss");
1226 COMMAND(0, fastshader, "ssi");
1227 COMMAND(0, defershader, "iss");
1228 ICOMMAND(0, forceshader, "s", (const char *name), useshaderbyname(name));
1229 
1230 ICOMMAND(0, isshaderdefined, "s", (char *name), intret(lookupshaderbyname(name) ? 1 : 0));
1231 
1232 static hashset<const char *> shaderparamnames(256);
1233 
getshaderparamname(const char * name,bool insert)1234 const char *getshaderparamname(const char *name, bool insert)
1235 {
1236     const char *exists = shaderparamnames.find(name, NULL);
1237     if(exists || !insert) return exists;
1238     return shaderparamnames.add(newstring(name));
1239 }
1240 
addslotparam(const char * name,float x,float y,float z,float w,int palette=0,int palindex=0)1241 void addslotparam(const char *name, float x, float y, float z, float w, int palette = 0, int palindex = 0)
1242 {
1243     if(name) name = getshaderparamname(name);
1244     loopv(slotparams)
1245     {
1246         SlotShaderParam &param = slotparams[i];
1247         if(param.name==name)
1248         {
1249             param.palette = palette;
1250             param.palindex = palindex;
1251             param.val[0] = x;
1252             param.val[1] = y;
1253             param.val[2] = z;
1254             param.val[3] = w;
1255             return;
1256         }
1257     }
1258     slotparams.add(SlotShaderParam(name, palette, palindex, x, y, z, w));
1259 }
1260 
1261 ICOMMAND(0, setuniformparam, "sffffii", (char *name, float *x, float *y, float *z, float *w, int *p, int *pidx), addslotparam(name, *x, *y, *z, *w, *p, *pidx));
1262 ICOMMAND(0, setshaderparam, "sffffii", (char *name, float *x, float *y, float *z, float *w, int *p, int *pidx), addslotparam(name, *x, *y, *z, *w, *p, *pidx));
1263 ICOMMAND(0, defuniformparam, "sffffii", (char *name, float *x, float *y, float *z, float *w, int *p, int *pidx), addslotparam(name, *x, *y, *z, *w, *p, *pidx));
1264 
1265 #define NUMPOSTFXBINDS 10
1266 
1267 struct postfxtex
1268 {
1269     GLuint id;
1270     int scale, used;
1271 
postfxtexpostfxtex1272     postfxtex() : id(0), scale(0), used(-1) {}
1273 };
1274 vector<postfxtex> postfxtexs;
1275 int postfxbinds[NUMPOSTFXBINDS];
1276 GLuint postfxfb = 0;
1277 int postfxw = 0, postfxh = 0;
1278 
1279 struct postfxpass
1280 {
1281     Shader *shader;
1282     vec4 params;
1283     uint inputs, freeinputs;
1284     int outputbind, outputscale;
1285 
postfxpasspostfxpass1286     postfxpass() : shader(NULL), inputs(1), freeinputs(1), outputbind(0), outputscale(0) {}
1287 };
1288 vector<postfxpass> postfxpasses;
1289 
allocatepostfxtex(int scale)1290 static int allocatepostfxtex(int scale)
1291 {
1292     loopv(postfxtexs)
1293     {
1294         postfxtex &t = postfxtexs[i];
1295         if(t.scale==scale && t.used < 0) return i;
1296     }
1297     postfxtex &t = postfxtexs.add();
1298     t.scale = scale;
1299     glGenTextures(1, &t.id);
1300     createtexture(t.id, max(screenw>>scale, 1), max(screenh>>scale, 1), NULL, 3, 1, GL_RGB);
1301     return postfxtexs.length()-1;
1302 }
1303 
cleanuppostfx(bool fullclean)1304 void cleanuppostfx(bool fullclean)
1305 {
1306     if(fullclean && postfxfb)
1307     {
1308         glDeleteFramebuffers_(1, &postfxfb);
1309         postfxfb = 0;
1310     }
1311 
1312     loopv(postfxtexs) glDeleteTextures(1, &postfxtexs[i].id);
1313     postfxtexs.shrink(0);
1314 
1315     postfxw = 0;
1316     postfxh = 0;
1317 }
1318 
renderpostfx()1319 void renderpostfx()
1320 {
1321     if(postfxpasses.empty()) return;
1322 
1323     if(postfxw != screenw || postfxh != screenh)
1324     {
1325         cleanuppostfx(false);
1326         postfxw = screenw;
1327         postfxh = screenh;
1328     }
1329 
1330     int binds[NUMPOSTFXBINDS];
1331     loopi(NUMPOSTFXBINDS) binds[i] = -1;
1332     loopv(postfxtexs) postfxtexs[i].used = -1;
1333 
1334     binds[0] = allocatepostfxtex(0);
1335     postfxtexs[binds[0]].used = 0;
1336     glBindTexture(GL_TEXTURE_2D, postfxtexs[binds[0]].id);
1337     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, screenw, screenh);
1338 
1339     if(postfxpasses.length() > 1)
1340     {
1341         if(!postfxfb) glGenFramebuffers_(1, &postfxfb);
1342         glBindFramebuffer_(GL_FRAMEBUFFER, postfxfb);
1343     }
1344 
1345     GLOBALPARAMF(millis, lastmillis/1000.0f);
1346 
1347     loopv(postfxpasses)
1348     {
1349         postfxpass &p = postfxpasses[i];
1350 
1351         int tex = -1;
1352         if(!postfxpasses.inrange(i+1))
1353         {
1354             if(postfxpasses.length() > 1) glBindFramebuffer_(GL_FRAMEBUFFER, 0);
1355         }
1356         else
1357         {
1358             tex = allocatepostfxtex(p.outputscale);
1359             glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, postfxtexs[tex].id, 0);
1360         }
1361 
1362         int w = tex >= 0 ? max(screenw>>postfxtexs[tex].scale, 1) : screenw,
1363             h = tex >= 0 ? max(screenh>>postfxtexs[tex].scale, 1) : screenh;
1364         glViewport(0, 0, w, h);
1365         p.shader->set();
1366         LOCALPARAM(params, p.params);
1367         int tw = w, th = h, tmu = 0;
1368         loopj(NUMPOSTFXBINDS) if(p.inputs&(1<<j) && binds[j] >= 0)
1369         {
1370             if(!tmu)
1371             {
1372                 tw = max(screenw>>postfxtexs[binds[j]].scale, 1);
1373                 th = max(screenh>>postfxtexs[binds[j]].scale, 1);
1374             }
1375             else glActiveTexture_(GL_TEXTURE0 + tmu);
1376             glBindTexture(GL_TEXTURE_2D, postfxtexs[binds[j]].id);
1377             ++tmu;
1378         }
1379         if(tmu) glActiveTexture_(GL_TEXTURE0);
1380         LOCALPARAMF(postfxscale, 1.0f/tw, 1.0f/th);
1381         screenquad(1, 1);
1382 
1383         loopj(NUMPOSTFXBINDS) if(p.freeinputs&(1<<j) && binds[j] >= 0)
1384         {
1385             postfxtexs[binds[j]].used = -1;
1386             binds[j] = -1;
1387         }
1388         if(tex >= 0)
1389         {
1390             if(binds[p.outputbind] >= 0) postfxtexs[binds[p.outputbind]].used = -1;
1391             binds[p.outputbind] = tex;
1392             postfxtexs[tex].used = p.outputbind;
1393         }
1394     }
1395 }
1396 
addpostfx(const char * name,int outputbind,int outputscale,uint inputs,uint freeinputs,const vec4 & params)1397 static bool addpostfx(const char *name, int outputbind, int outputscale, uint inputs, uint freeinputs, const vec4 &params)
1398 {
1399     if(!*name) return false;
1400     Shader *s = useshaderbyname(name);
1401     if(!s)
1402     {
1403         conoutf("no such postfx shader: %s", name);
1404         return false;
1405     }
1406     postfxpass &p = postfxpasses.add();
1407     p.shader = s;
1408     p.outputbind = outputbind;
1409     p.outputscale = outputscale;
1410     p.inputs = inputs;
1411     p.freeinputs = freeinputs;
1412     p.params = params;
1413     return true;
1414 }
1415 
clearpostfx()1416 void clearpostfx()
1417 {
1418     postfxpasses.shrink(0);
1419     cleanuppostfx(false);
1420 }
1421 
1422 COMMAND(0, clearpostfx, "");
1423 
1424 ICOMMAND(0, addpostfx, "siisffff", (char *name, int *bind, int *scale, char *inputs, float *x, float *y, float *z, float *w),
1425 {
1426     int inputmask = inputs[0] ? 0 : 1;
1427     int freemask = inputs[0] ? 0 : 1;
1428     bool freeinputs = true;
1429     for(; *inputs; inputs++) if(isdigit(*inputs))
1430     {
1431         inputmask |= 1<<(*inputs-'0');
1432         if(freeinputs) freemask |= 1<<(*inputs-'0');
1433     }
1434     else if(*inputs=='+') freeinputs = false;
1435     else if(*inputs=='-') freeinputs = true;
1436     inputmask &= (1<<NUMPOSTFXBINDS)-1;
1437     freemask &= (1<<NUMPOSTFXBINDS)-1;
1438     addpostfx(name, clamp(*bind, 0, NUMPOSTFXBINDS-1), max(*scale, 0), inputmask, freemask, vec4(*x, *y, *z, *w));
1439 });
1440 
1441 ICOMMAND(0, setpostfx, "sffff", (char *name, float *x, float *y, float *z, float *w),
1442 {
1443     clearpostfx();
1444     if(name[0]) addpostfx(name, 0, 0, 1, 1, vec4(*x, *y, *z, *w));
1445 });
1446 
cleanupshaders()1447 void cleanupshaders()
1448 {
1449     cleanuppostfx(true);
1450 
1451     nullshader = hudshader = hudnotextureshader = textureshader = notextureshader = nocolorshader = foggedshader = foggednotextureshader = stdworldshader = NULL;
1452     enumerate(shaders, Shader, s, s.cleanup());
1453     Shader::lastshader = NULL;
1454     glUseProgram_(0);
1455 }
1456 
reloadshaders()1457 void reloadshaders()
1458 {
1459     loadshaders();
1460     linkslotshaders();
1461     enumerate(shaders, Shader, s,
1462     {
1463         if(!s.standard && !(s.type&(SHADER_DEFERRED|SHADER_INVALID)) && !s.variantshader)
1464         {
1465             defformatstring(info, "shader %s", s.name);
1466             progress(0.0, info);
1467             if(!s.compile()) s.cleanup(true);
1468             loopv(s.variants)
1469             {
1470                 Shader *v = s.variants[i];
1471                 if((v->reusevs && v->reusevs->invalid()) ||
1472                    (v->reuseps && v->reuseps->invalid()) ||
1473                    !v->compile())
1474                     v->cleanup(true);
1475             }
1476         }
1477         if(s.forced && !s.detailshader) s.fixdetailshader();
1478     });
1479 }
1480 
setupblurkernel(int radius,float sigma,float * weights,float * offsets)1481 void setupblurkernel(int radius, float sigma, float *weights, float *offsets)
1482 {
1483     if(radius<1 || radius>MAXBLURRADIUS) return;
1484     sigma *= 2*radius;
1485     float total = 1.0f/sigma;
1486     weights[0] = total;
1487     offsets[0] = 0;
1488     // rely on bilinear filtering to sample 2 pixels at once
1489     // transforms a*X + b*Y into (u+v)*[X*u/(u+v) + Y*(1 - u/(u+v))]
1490     loopi(radius)
1491     {
1492         float weight1 = exp(-((2*i)*(2*i)) / (2*sigma*sigma)) / sigma,
1493               weight2 = exp(-((2*i+1)*(2*i+1)) / (2*sigma*sigma)) / sigma,
1494               scale = weight1 + weight2,
1495               offset = 2*i+1 + weight2 / scale;
1496         weights[i+1] = scale;
1497         offsets[i+1] = offset;
1498         total += 2*scale;
1499     }
1500     loopi(radius+1) weights[i] /= total;
1501     for(int i = radius+1; i <= MAXBLURRADIUS; i++) weights[i] = offsets[i] = 0;
1502 }
1503 
setblurshader(int pass,int size,int radius,float * weights,float * offsets)1504 void setblurshader(int pass, int size, int radius, float *weights, float *offsets)
1505 {
1506     if(radius<1 || radius>MAXBLURRADIUS) return;
1507     static Shader *blurshader[7][2] = { { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL } };
1508     Shader *&s = blurshader[radius-1][pass];
1509     if(!s)
1510     {
1511         defformatstring(name, "blur%c%d", 'x'+pass, radius);
1512         s = lookupshaderbyname(name);
1513     }
1514     s->set();
1515     LOCALPARAMV(weights, weights, 8);
1516     float scaledoffsets[8];
1517     loopk(8) scaledoffsets[k] = offsets[k]/size;
1518     LOCALPARAMV(offsets, scaledoffsets, 8);
1519 }
1520 
1521