conworlds
view src/shader.cc @ 13:283cdfa7dda2
added a crapload of code from goat3dgfx
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sun, 24 Aug 2014 09:41:24 +0300 |
parents | |
children |
line source
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdarg.h>
4 #include <errno.h>
5 #include "opengl.h"
6 #include "shader.h"
7 #include "mesh.h"
8 #include "logger.h"
9 #include "unistate.h"
11 #ifdef _MSC_VER
12 #include <malloc.h>
13 #else
14 #include <alloca.h>
15 #endif
17 #ifdef __GLEW_H__
18 #define HAVE_GEOMETRY_SHADER
19 #define HAVE_TESSELATION_SHADER
20 #endif
22 static std::string read_source(const char *fname);
23 static void bind_standard_attr(const ShaderProg *prog);
25 ShaderProg *ShaderProg::current;
27 void bind_shader(const ShaderProg *sdr)
28 {
29 if(sdr) {
30 sdr->bind();
31 } else {
32 #ifndef GL_ES_VERSION_2_0
33 glUseProgram(0);
34 ShaderProg::current = 0;
35 #endif
36 }
37 }
39 const ShaderProg *get_current_shader()
40 {
41 return ShaderProg::current;
42 }
44 Shader::Shader()
45 {
46 sdr = type = 0;
47 }
49 Shader::~Shader()
50 {
51 destroy();
52 }
54 unsigned int Shader::get_id() const
55 {
56 return sdr;
57 }
59 void Shader::set_name(const char *name)
60 {
61 this->name = std::string(name);
62 }
64 const char *Shader::get_name() const
65 {
66 return name.c_str();
67 }
69 bool Shader::create(const char *src, unsigned int type)
70 {
71 #if !GL_ES_VERSION_2_0
72 const char *src_arr[] = {src};
73 #else
74 const char *src_arr[] = { "precision mediump float; ", src };
75 #endif
77 // keep a copy of the source code
78 this->src = std::string(src);
80 if(!sdr) {
81 sdr = glCreateShader(type);
82 }
84 info_log("compiling shader: %s... ", name.c_str());
86 glShaderSource(sdr, sizeof src_arr / sizeof *src_arr, src_arr, 0);
87 glCompileShader(sdr);
89 int status;
90 glGetShaderiv(sdr, GL_COMPILE_STATUS, &status);
92 info_log(status ? "success\n" : "failed\n");
94 int info_len;
95 glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &info_len);
96 if(info_len > 1) {
97 char *buf = (char*)alloca(info_len);
98 glGetShaderInfoLog(sdr, info_len, 0, buf);
99 buf[info_len - 1] = 0;
101 if(status) {
102 info_log("%s\n", buf);
103 } else {
104 error_log("%s\n", buf);
105 }
106 }
108 return status == GL_TRUE;
109 }
111 void Shader::destroy()
112 {
113 if(sdr) {
114 glDeleteShader(sdr);
115 }
116 sdr = type = 0;
118 src.clear();
119 name.clear();
120 }
122 static std::string read_source(const char *fname)
123 {
124 FILE *fp;
126 if(!(fp = fopen(fname, "rb"))) {
127 error_log("failed to load shader: %s: %s\n", fname, strerror(errno));
128 return std::string();
129 }
131 fseek(fp, 0, SEEK_END);
132 long sz = ftell(fp);
133 rewind(fp);
135 char *src = (char*)alloca(sz + 1);
136 if(fread(src, 1, sz, fp) < (size_t)sz) {
137 error_log("failed to load shader: %s: %s\n", fname, strerror(errno));
138 fclose(fp);
139 delete [] src;
140 return std::string();
141 }
142 src[sz] = 0;
143 fclose(fp);
145 return std::string(src);
146 }
148 bool Shader::load(const char *fname, unsigned int type)
149 {
150 std::string src = read_source(fname);
151 if(src.empty()) {
152 return false;
153 }
154 set_name(fname);
155 return create(src.c_str(), type);
156 }
158 // ---- shader program ----
159 ShaderProg::ShaderProg()
160 {
161 prog = 0;
162 must_link = true;
163 }
165 ShaderProg::~ShaderProg()
166 {
167 destroy();
168 }
170 unsigned int ShaderProg::get_id() const
171 {
172 return prog;
173 }
175 bool ShaderProg::create(const char *src, unsigned int type, ...)
176 {
177 va_list ap;
179 va_start(ap, type);
180 bool res = create(src, type, ap);
181 va_end(ap);
183 return res;
184 }
186 bool ShaderProg::create(const char *src, unsigned int type, va_list ap)
187 {
188 destroy();
189 prog = glCreateProgram();
191 while(src) {
192 Shader *sdr = new Shader;
193 if(!sdr->create(src, type)) {
194 va_end(ap);
195 return false;
196 }
197 add_shader(sdr);
198 src = va_arg(ap, const char*);
199 type = va_arg(ap, unsigned int);
200 }
202 return link();
203 }
205 bool ShaderProg::create(const char *vsrc, const char *psrc)
206 {
207 return create(VSDR(vsrc), PSDR(psrc), 0);
208 }
210 bool ShaderProg::create(Shader *sdr, ...)
211 {
212 va_list ap;
214 va_start(ap, sdr);
215 bool res = create(sdr, ap);
216 va_end(ap);
218 return res;
219 }
221 bool ShaderProg::create(Shader *sdr, va_list ap)
222 {
223 destroy();
224 prog = glCreateProgram();
226 while(sdr) {
227 add_shader(sdr);
228 sdr = va_arg(ap, Shader*);
229 }
230 return link();
231 }
233 bool ShaderProg::create(Shader *vsdr, Shader *psdr)
234 {
235 return create(vsdr, psdr, 0);
236 }
238 void ShaderProg::destroy()
239 {
240 if(prog) {
241 glDeleteProgram(prog);
242 }
243 prog = 0;
245 shaders.clear();
246 // don't actually destroy the shaders, let the ShaderSet own them
247 }
249 bool ShaderProg::load(const char *fname, unsigned int type, ...)
250 {
251 va_list ap;
252 va_start(ap, type);
253 bool res = load(fname, type, ap);
254 va_end(ap);
256 return res;
257 }
259 bool ShaderProg::load(const char *fname, unsigned int type, va_list ap)
260 {
261 destroy();
262 prog = glCreateProgram();
264 while(fname) {
265 Shader *sdr = new Shader;
266 if(!sdr->load(fname, type)) {
267 delete sdr;
268 return false;
269 }
270 add_shader(sdr);
272 if((fname = va_arg(ap, const char*))) {
273 type = va_arg(ap, unsigned int);
274 }
275 }
277 return link();
278 }
280 bool ShaderProg::load(const char *vfname, const char *pfname)
281 {
282 return load(VSDR(vfname), PSDR(pfname), 0);
283 }
285 void ShaderProg::add_shader(Shader *sdr)
286 {
287 glAttachShader(prog, sdr->get_id());
288 }
290 bool ShaderProg::link() const
291 {
292 bind_standard_attr(this);
294 CHECKGLERR;
295 info_log("linking program ... ");
296 glLinkProgram(prog);
298 int status;
299 glGetProgramiv(prog, GL_LINK_STATUS, &status);
301 info_log(status ? "success\n" : "failed\n");
303 int info_len;
304 glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &info_len);
305 if(info_len > 1) {
306 char *buf = (char*)alloca(info_len);
307 glGetProgramInfoLog(prog, info_len, 0, buf);
308 buf[info_len - 1] = 0;
310 if(status) {
311 info_log("%s\n", buf);
312 } else {
313 error_log("%s\n", buf);
314 }
315 }
317 if(status) {
318 must_link = false;
319 cache_state_uniforms();
320 return true;
321 }
322 return false;
323 }
325 void ShaderProg::bind() const
326 {
327 CHECKGLERR;
328 if(must_link) {
329 if(!link()) {
330 return;
331 }
332 }
333 CHECKGLERR;
334 glUseProgram(prog);
335 ShaderProg::current = (ShaderProg*)this;
337 setup_state_uniforms();
338 }
341 int ShaderProg::get_attrib_location(const char *name) const
342 {
343 glUseProgram(prog);
344 return glGetAttribLocation(prog, name);
345 }
347 void ShaderProg::set_attrib_location(const char *name, int loc) const
348 {
349 glBindAttribLocation(prog, loc, name);
350 must_link = true;
351 }
353 int ShaderProg::get_uniform_location(const char *name) const
354 {
355 glUseProgram(prog);
356 return glGetUniformLocation(prog, name);
357 }
359 bool ShaderProg::set_uniform(int loc, int val) const
360 {
361 glUseProgram(prog);
362 if(loc >= 0) {
363 glUniform1i(loc, val);
364 return true;
365 }
366 return false;
367 }
369 bool ShaderProg::set_uniform(int loc, float val) const
370 {
371 glUseProgram(prog);
372 if(loc >= 0) {
373 glUniform1f(loc, val);
374 return true;
375 }
376 return false;
377 }
379 bool ShaderProg::set_uniform(int loc, const Vector2 &v) const
380 {
381 glUseProgram(prog);
382 if(loc >= 0) {
383 glUniform2f(loc, v.x, v.y);
384 return true;
385 }
386 return false;
387 }
389 bool ShaderProg::set_uniform(int loc, const Vector3 &v) const
390 {
391 glUseProgram(prog);
392 if(loc >= 0) {
393 glUniform3f(loc, v.x, v.y, v.z);
394 return true;
395 }
396 return false;
397 }
399 bool ShaderProg::set_uniform(int loc, const Vector4 &v) const
400 {
401 glUseProgram(prog);
402 if(loc >= 0) {
403 glUniform4f(loc, v.x, v.y, v.z, v.w);
404 return true;
405 }
406 return false;
407 }
409 bool ShaderProg::set_uniform(int loc, const Matrix3x3 &m) const
410 {
411 glUseProgram(prog);
412 if(loc >= 0) {
413 glUniformMatrix3fv(loc, 1, GL_TRUE, m[0]);
414 return true;
415 }
416 return false;
417 }
419 bool ShaderProg::set_uniform(int loc, const Matrix4x4 &m) const
420 {
421 glUseProgram(prog);
422 if(loc >= 0) {
423 glUniformMatrix4fv(loc, 1, GL_TRUE, m[0]);
424 return true;
425 }
426 return false;
427 }
430 bool ShaderProg::set_uniform(const char *name, int val) const
431 {
432 return set_uniform(get_uniform_location(name), val);
433 }
435 bool ShaderProg::set_uniform(const char *name, float val) const
436 {
437 return set_uniform(get_uniform_location(name), val);
438 }
440 bool ShaderProg::set_uniform(const char *name, const Vector2 &v) const
441 {
442 return set_uniform(get_uniform_location(name), v);
443 }
445 bool ShaderProg::set_uniform(const char *name, const Vector3 &v) const
446 {
447 return set_uniform(get_uniform_location(name), v);
448 }
450 bool ShaderProg::set_uniform(const char *name, const Vector4 &v) const
451 {
452 return set_uniform(get_uniform_location(name), v);
453 }
455 bool ShaderProg::set_uniform(const char *name, const Matrix3x3 &m) const
456 {
457 return set_uniform(get_uniform_location(name), m);
458 }
460 bool ShaderProg::set_uniform(const char *name, const Matrix4x4 &m) const
461 {
462 return set_uniform(get_uniform_location(name), m);
463 }
465 static StType unist_type(GLenum type)
466 {
467 switch(type) {
468 case GL_FLOAT:
469 return ST_FLOAT;
470 case GL_FLOAT_VEC2:
471 return ST_FLOAT2;
472 case GL_FLOAT_VEC3:
473 return ST_FLOAT3;
474 case GL_FLOAT_VEC4:
475 return ST_FLOAT4;
476 case GL_INT:
477 case GL_SAMPLER_2D:
478 case GL_SAMPLER_CUBE:
479 #if !GL_ES_VERSION_2_0
480 case GL_SAMPLER_1D:
481 case GL_SAMPLER_3D:
482 case GL_SAMPLER_1D_SHADOW:
483 case GL_SAMPLER_2D_SHADOW:
484 #endif
485 return ST_INT;
486 case GL_INT_VEC2:
487 return ST_INT2;
488 case GL_INT_VEC3:
489 return ST_INT3;
490 case GL_INT_VEC4:
491 return ST_INT4;
492 case GL_FLOAT_MAT3:
493 return ST_MATRIX3;
494 case GL_FLOAT_MAT4:
495 return ST_MATRIX4;
496 default:
497 break;
498 }
499 return ST_UNKNOWN;
500 }
502 void ShaderProg::cache_state_uniforms() const
503 {
504 if(!glIsProgram(prog)) {
505 return;
506 }
508 int num_uni;
509 glGetProgramiv(prog, GL_ACTIVE_UNIFORMS, &num_uni);
511 char name[256];
512 for(int i=0; i<num_uni; i++) {
513 GLint sz;
514 GLenum type;
515 glGetActiveUniform(prog, i, sizeof name - 1, 0, &sz, &type, name);
517 if(strstr(name, "st_") == name) {
518 StateLocCache s;
519 s.sidx = add_unistate(name, unist_type(type));
520 s.loc = glGetUniformLocation(prog, name);
521 stloc_cache.push_back(s);
522 }
523 }
524 }
526 void ShaderProg::setup_state_uniforms() const
527 {
528 for(size_t i=0; i<stloc_cache.size(); i++) {
529 setup_unistate(stloc_cache[i].sidx, this, stloc_cache[i].loc);
530 CHECKGLERR;
531 }
532 }
535 // ---- ShaderSet ----
536 static Shader *load_shader(const char *fname, unsigned int type)
537 {
538 Shader *sdr = new Shader;
539 if(!sdr->load(fname, type)) {
540 delete sdr;
541 return 0;
542 }
543 return sdr;
544 }
546 static Shader *load_vertex_shader(const char *fname)
547 {
548 return load_shader(fname, GL_VERTEX_SHADER);
549 }
551 static Shader *load_pixel_shader(const char *fname)
552 {
553 return load_shader(fname, GL_FRAGMENT_SHADER);
554 }
556 #ifdef HAVE_GEOMETRY_SHADER
557 static Shader *load_geom_shader(const char *fname)
558 {
559 return load_shader(fname, GL_GEOMETRY_SHADER);
560 }
561 #endif
563 #ifdef HAVE_TESSELATION_SHADER
564 static Shader *load_tc_shader(const char *fname)
565 {
566 return load_shader(fname, GL_TESS_CONTROL_SHADER);
567 }
569 static Shader *load_te_shader(const char *fname)
570 {
571 return load_shader(fname, GL_TESS_EVALUATION_SHADER);
572 }
573 #endif
575 static void destroy_shader(Shader *sdr)
576 {
577 delete sdr;
578 }
580 ShaderSet::ShaderSet(unsigned int type)
581 : DataSet<Shader*>(0, destroy_shader)
582 {
583 this->type = type;
585 switch(type) {
586 case GL_VERTEX_SHADER:
587 load = load_vertex_shader;
588 break;
590 case GL_FRAGMENT_SHADER:
591 load = load_pixel_shader;
592 break;
594 #ifdef HAVE_GEOMETRY_SHADER
595 case GL_GEOMETRY_SHADER:
596 load = load_geom_shader;
597 break;
598 #endif
600 #ifdef HAVE_TESSELATION_SHADER
601 case GL_TESS_CONTROL_SHADER:
602 load = load_tc_shader;
603 break;
605 case GL_TESS_EVALUATION_SHADER:
606 load = load_te_shader;
607 break;
608 #endif
610 default:
611 error_log("ShaderSet constructed with invalid shader type!\n");
612 }
613 }
615 static struct { const char *name; int loc; } attr_loc[] = {
616 {"attr_vertex", MESH_ATTR_VERTEX},
617 {"attr_normal", MESH_ATTR_NORMAL},
618 {"attr_tangent", MESH_ATTR_TANGENT},
619 {"attr_texcoord", MESH_ATTR_TEXCOORD},
620 {"attr_color", MESH_ATTR_COLOR},
621 {"attr_boneweights", MESH_ATTR_BONEWEIGHTS},
622 {"attr_boneidx", MESH_ATTR_BONEIDX}
623 };
625 static void bind_standard_attr(const ShaderProg *prog)
626 {
627 // we must link once to find out which are the active attributes
628 glLinkProgram(prog->get_id());
630 int num_attr;
631 glGetProgramiv(prog->get_id(), GL_ACTIVE_ATTRIBUTES, &num_attr);
633 char name[256];
634 for(int i=0; i<num_attr; i++) {
635 GLint sz;
636 GLenum type;
637 glGetActiveAttrib(prog->get_id(), i, sizeof name - 1, 0, &sz, &type, name);
639 for(int j=0; j<(int)(sizeof attr_loc / sizeof *attr_loc); j++) {
640 if(strcmp(name, attr_loc[j].name) == 0) {
641 prog->set_attrib_location(name, attr_loc[j].loc);
642 }
643 }
644 }
645 }
648 /*
649 static const char *strtype(unsigned int type)
650 {
651 switch(type) {
652 case GL_VERTEX_SHADER:
653 return "vertex";
654 case GL_FRAGMENT_SHADER:
655 return "fragment";
656 #ifdef HAVE_GEOMETRY_SHADER
657 case GL_GEOMETRY_SHADER:
658 return "geometry";
659 #endif
660 #ifdef HAVE_TESSELATION_SHADER
661 case GL_TESS_CONTROL_SHADER:
662 return "tesselation control";
663 case GL_TESS_EVALUATION_SHADER:
664 return "tesselation evaluation";
665 #endif
666 default:
667 break;
668 }
669 return "<unknown>";
670 }
671 */