goat3dgfx
view src/shader.cc @ 29:9d581abd0bfb
merged
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sun, 02 Mar 2014 02:18:15 +0200 |
parents | 6f82b9b6d6c3 |
children | 3ba80928b530 |
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 "logger.h"
8 #include "unistate.h"
10 #ifdef _MSC_VER
11 #include <malloc.h>
12 #else
13 #include <alloca.h>
14 #endif
16 using namespace goatgfx;
18 #ifdef __GLEW_H__
19 #define HAVE_GEOMETRY_SHADER
20 #define HAVE_TESSELATION_SHADER
21 #endif
23 static std::string read_source(const char *fname);
25 ShaderProg *ShaderProg::current;
27 void goatgfx::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 *goatgfx::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(datafile_path(fname).c_str(), "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 }
534 // Static functions to use with ShaderSet
535 Shader *Shader::create_shader()
536 {
537 return new Shader;
538 }
540 bool Shader::load_shader(Shader *sdr, const char *fname)
541 {
542 std::string src = read_source(fname);
543 if(src.empty()) {
544 return false;
545 }
547 sdr->src = src;
548 return true;
549 }
551 bool Shader::done_shader(Shader *sdr, unsigned int type)
552 {
553 return sdr->create(sdr->src.c_str(), type);
554 }
556 static bool done_vertex_shader(Shader *sdr)
557 {
558 return Shader::done_shader(sdr, GL_VERTEX_SHADER);
559 }
561 static bool done_pixel_shader(Shader *sdr)
562 {
563 return Shader::done_shader(sdr, GL_FRAGMENT_SHADER);
564 }
566 #ifdef HAVE_GEOMETRY_SHADER
567 static bool done_geom_shader(Shader *sdr)
568 {
569 return Shader::done_shader(sdr, GL_GEOMETRY_SHADER);
570 }
571 #endif
573 #ifdef HAVE_TESSELATION_SHADER
574 static bool done_tc_shader(Shader *sdr)
575 {
576 return Shader::done_shader(sdr, GL_TESS_CONTROL_SHADER);
577 }
579 static bool done_te_shader(Shader *sdr)
580 {
581 return Shader::done_shader(sdr, GL_TESS_EVALUATION_SHADER);
582 }
583 #endif
585 void Shader::destroy_shader(Shader *sdr)
586 {
587 delete sdr;
588 }
590 // ---- ShaderSet ----
592 ShaderSet::ShaderSet(unsigned int type)
593 : DataSet<Shader*>(Shader::create_shader, Shader::load_shader, 0, Shader::destroy_shader)
594 {
595 switch(type) {
596 case GL_VERTEX_SHADER:
597 done = done_vertex_shader;
598 break;
600 case GL_FRAGMENT_SHADER:
601 done = done_pixel_shader;
602 break;
604 #ifdef HAVE_GEOMETRY_SHADER
605 case GL_GEOMETRY_SHADER:
606 done = done_geom_shader;
607 break;
608 #endif
610 #ifdef HAVE_TESSELATION_SHADER
611 case GL_TESS_CONTROL_SHADER:
612 done = done_tc_shader;
613 break;
615 case GL_TESS_EVALUATION_SHADER:
616 done = done_te_shader;
617 break;
618 #endif
620 default:
621 error_log("ShaderSet constructed with invalid shader type!\n");
622 }
623 }
625 static struct { const char *name; int loc; } attr_loc[] = {
626 {"attr_vertex", MESH_ATTR_VERTEX},
627 {"attr_normal", MESH_ATTR_NORMAL},
628 {"attr_tangent", MESH_ATTR_TANGENT},
629 {"attr_texcoord", MESH_ATTR_TEXCOORD},
630 {"attr_color", MESH_ATTR_COLOR},
631 {"attr_boneweights", MESH_ATTR_BONEWEIGHTS},
632 {"attr_boneidx", MESH_ATTR_BONEIDX}
633 };
635 static void bind_standard_attr(const ShaderProg *prog)
636 {
637 // we must link once to find out which are the active attributes
638 glLinkProgram(prog->get_id());
640 int num_attr;
641 glGetProgramiv(prog->get_id(), GL_ACTIVE_ATTRIBUTES, &num_attr);
643 char name[256];
644 for(int i=0; i<num_attr; i++) {
645 GLint sz;
646 GLenum type;
647 glGetActiveAttrib(prog->get_id(), i, sizeof name - 1, 0, &sz, &type, name);
649 for(int j=0; j<(int)(sizeof attr_loc / sizeof *attr_loc); j++) {
650 if(strcmp(name, attr_loc[j].name) == 0) {
651 prog->set_attrib_location(name, attr_loc[j].loc);
652 }
653 }
654 }
655 }
658 /*
659 static const char *strtype(unsigned int type)
660 {
661 switch(type) {
662 case GL_VERTEX_SHADER:
663 return "vertex";
664 case GL_FRAGMENT_SHADER:
665 return "fragment";
666 #ifdef HAVE_GEOMETRY_SHADER
667 case GL_GEOMETRY_SHADER:
668 return "geometry";
669 #endif
670 #ifdef HAVE_TESSELATION_SHADER
671 case GL_TESS_CONTROL_SHADER:
672 return "tesselation control";
673 case GL_TESS_EVALUATION_SHADER:
674 return "tesselation evaluation";
675 #endif
676 default:
677 break;
678 }
679 return "<unknown>";
680 }
681 */