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 ¶m = 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 ¶m = s.params[i];
414 if(name == param.name) return ¶m;
415 }
416 loopv(s.shader->defaultparams)
417 {
418 SlotShaderParamState ¶m = s.shader->defaultparams[i];
419 if(name == param.name) return ¶m;
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 ¶m = s.params[i];
429 if(name == param.name) return ¶m;
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> ¶ms, Shader *sh, bool load)
1160 {
1161 if(sh) loopv(params)
1162 {
1163 int loc = -1;
1164 SlotShaderParam ¶m = 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 ¶m = 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 ¶ms)
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