1 #include <boost/regex.hpp>
2
3 #include "array_callable.hpp"
4 #include "asserts.hpp"
5 #include "custom_object.hpp"
6 #include "foreach.hpp"
7 #include "formula.hpp"
8 #include "formula_profiler.hpp"
9 #include "graphics.hpp"
10 #include "json_parser.hpp"
11 #include "level.hpp"
12 #include "module.hpp"
13 #include "shaders.hpp"
14 #include "variant_utils.hpp"
15 #if defined(WIN32)
16 #include "win_profile_timer.hpp"
17 #endif
18
19 #define WRITE_LOG(_a,_b) if( !(_a) ) { std::ostringstream _s; _s << __FILE__ << ":" << __LINE__ << " ASSERTION FAILED: " << _b << "\n"; std::cerr << _s.str(); return; }
20
21 namespace gles2 {
22
23 namespace {
24 std::string current_error;
25 }
26
set_runtime_error(const std::string & msg)27 void shader::set_runtime_error(const std::string& msg)
28 {
29 current_error = msg;
30 if(msg == "") {
31 current_error = "UNKNOWN SHADER ERROR";
32 }
33 }
34
get_and_clear_runtime_error()35 std::string shader::get_and_clear_runtime_error()
36 {
37 const std::string result = current_error;
38 current_error = "";
39 return result;
40 }
41
shader(GLenum type,const std::string & name,const std::string & code)42 shader::shader(GLenum type, const std::string& name, const std::string& code)
43 : type_(type), shader_(0), name_(name)
44 {
45 ASSERT_LOG(compile(code), "Error compiling shader for " << name_);
46 }
47
compile(const std::string & code)48 bool shader::compile(const std::string& code)
49 {
50 GLint compiled;
51 if(shader_) {
52 glDeleteShader(shader_);
53 shader_ = 0;
54 }
55
56 shader_ = glCreateShader(type_);
57 if(shader_ == 0) {
58 std::cerr << "Enable to create shader." << std::endl;
59 return false;
60 }
61 const GLchar* shader_code = reinterpret_cast<const GLchar*>(code.c_str());
62 glShaderSource(shader_, 1, &shader_code, NULL);
63 glCompileShader(shader_);
64 glGetShaderiv(shader_, GL_COMPILE_STATUS, &compiled);
65 if(!compiled) {
66 GLint info_len = 0;
67 glGetShaderiv(shader_, GL_INFO_LOG_LENGTH, &info_len);
68 if(info_len > 1) {
69 std::vector<char> info_log;
70 info_log.resize(info_len);
71 glGetShaderInfoLog(shader_, info_log.capacity(), NULL, &info_log[0]);
72 std::string s(info_log.begin(), info_log.end());
73 std::cerr << "Error compiling shader: " << s << std::endl;
74 }
75 glDeleteShader(shader_);
76 shader_ = 0;
77 return false;
78 }
79 return true;
80 }
81
82 namespace {
83 std::map<std::string, gles2::program_ptr> shader_programs;
84 }
85
program()86 program::program()
87 : object_(0)
88 {
89 environ_ = this;
90 }
91
92
program(const std::string & name,const shader & vs,const shader & fs)93 program::program(const std::string& name, const shader& vs, const shader& fs)
94 : object_(0)
95 {
96 environ_ = this;
97 init(name, vs, fs);
98 }
99
init(const std::string & name,const shader & vs,const shader & fs)100 void program::init(const std::string& name, const shader& vs, const shader& fs)
101 {
102 name_ = name;
103 vs_ = vs;
104 fs_ = fs;
105 ASSERT_LOG(link(), "Error linking program: " << name_);
106 }
107
link()108 bool program::link()
109 {
110 if(object_) {
111 glDeleteProgram(object_);
112 object_ = 0;
113 }
114 object_ = glCreateProgram();
115 ASSERT_LOG(object_ != 0, "Unable to create program object.");
116 glAttachShader(object_, vs_.get());
117 glAttachShader(object_, fs_.get());
118 glLinkProgram(object_);
119 GLint linked = 0;
120 glGetProgramiv(object_, GL_LINK_STATUS, &linked);
121 if(!linked) {
122 GLint info_len = 0;
123 glGetProgramiv(object_, GL_INFO_LOG_LENGTH, &info_len);
124 if(info_len > 1) {
125 std::vector<char> info_log;
126 info_log.resize(info_len);
127 glGetProgramInfoLog(object_, info_log.capacity(), NULL, &info_log[0]);
128 std::string s(info_log.begin(), info_log.end());
129 std::cerr << "Error linking object: " << s << std::endl;
130 }
131 glDeleteProgram(object_);
132 object_ = 0;
133 return false;
134 }
135 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
136 return queryUniforms() && queryAttributes();
137 }
138
get_attribute(const std::string & attr) const139 GLuint program::get_attribute(const std::string& attr) const
140 {
141 std::map<std::string, actives>::const_iterator it = attribs_.find(attr);
142 ASSERT_LOG(it != attribs_.end(), "Attribute \"" << attr << "\" not found in list.");
143 return it->second.location;
144 }
145
get_uniform(const std::string & attr) const146 GLuint program::get_uniform(const std::string& attr) const
147 {
148 std::map<std::string, actives>::const_iterator it = uniforms_.find(attr);
149 //ASSERT_LOG(it != uniforms_.end(), "Uniform \"" << attr << "\" not found in list.");
150 if(it == uniforms_.end()) {
151 return 0xffffffffUL;
152 }
153 return it->second.location;
154 }
155
queryAttributes()156 bool program::queryAttributes()
157 {
158 GLint active_attribs;
159 glGetProgramiv(object_, GL_ACTIVE_ATTRIBUTES, &active_attribs);
160 GLint attributes_max_len;
161 glGetProgramiv(object_, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &attributes_max_len);
162 std::vector<char> name;
163 name.resize(attributes_max_len+1);
164 for(int i = 0; i < active_attribs; i++) {
165 actives a;
166 GLsizei size;
167 glGetActiveAttrib(object_, i, name.size(), &size, &a.num_elements, &a.type, &name[0]);
168 a.name = std::string(&name[0], &name[size]);
169 a.location = glGetAttribLocation(object_, a.name.c_str());
170 ASSERT_LOG(a.location >= 0, "Unable to determine the location of the attribute: " << a.name);
171 attribs_[a.name] = a;
172 }
173 return true;
174 }
175
queryUniforms()176 bool program::queryUniforms()
177 {
178 GLint active_uniforms;
179 glGetProgramiv(object_, GL_ACTIVE_UNIFORMS, &active_uniforms);
180 GLint uniform_max_len;
181 glGetProgramiv(object_, GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniform_max_len);
182 std::vector<char> name;
183 name.resize(uniform_max_len+1);
184 for(int i = 0; i < active_uniforms; i++) {
185 actives u;
186 GLsizei size;
187 glGetActiveUniform(object_, i, name.size(), &size, &u.num_elements, &u.type, &name[0]);
188 u.name = std::string(&name[0], &name[size]);
189 u.location = glGetUniformLocation(object_, u.name.c_str());
190 ASSERT_LOG(u.location >= 0, "Unable to determine the location of the uniform: " << u.name);
191 uniforms_[u.name] = u;
192 }
193 return true;
194 }
195
get_uniform_value(const std::string & key) const196 variant program::get_uniform_value(const std::string& key) const
197 {
198 std::map<std::string, actives>::const_iterator it = uniforms_.find(key);
199 ASSERT_LOG(it != uniforms_.end(), "No uniform found with name: " << key);
200 return it->second.last_value;
201 }
202
set_uniform(const std::map<std::string,actives>::iterator & it,const variant & value)203 void program::set_uniform(const std::map<std::string,actives>::iterator& it, const variant& value)
204 {
205 const actives& u = it->second;
206 switch(u.type) {
207 case GL_FLOAT: {
208 glUniform1f(u.location, GLfloat(value.as_decimal().as_float()));
209 break;
210 }
211 case GL_FLOAT_VEC2: {
212 WRITE_LOG(value.num_elements() == 2, "Must be four(2) elements in vector.");
213 std::vector<GLfloat> f;
214 for(size_t n = 0; n < value.num_elements(); ++n) {
215 f.push_back(GLfloat(value[n].as_decimal().as_float()));
216 }
217 glUniform2fv(u.location, u.num_elements, &f[0]);
218 break;
219 }
220 case GL_FLOAT_VEC3: {
221 WRITE_LOG(value.num_elements() == 3, "Must be three(3) elements in vector.");
222 std::vector<GLfloat> f;
223 for(size_t n = 0; n < value.num_elements(); ++n) {
224 f.push_back(GLfloat(value[n].as_decimal().as_float()));
225 }
226 glUniform3fv(u.location, u.num_elements, &f[0]);
227 break;
228 }
229 case GL_FLOAT_VEC4: {
230 WRITE_LOG(value.num_elements() == 4, "Must be four(4) elements in vector.");
231 std::vector<GLfloat> f;
232 for(size_t n = 0; n < value.num_elements(); ++n) {
233 f.push_back(GLfloat(value[n].as_decimal().as_float()));
234 }
235 glUniform4fv(u.location, u.num_elements, &f[0]);
236 break;
237 }
238 case GL_INT: glUniform1i(u.location, value.as_int()); break;
239 case GL_INT_VEC2:
240 WRITE_LOG(value.num_elements() == 2, "Must be two(2) elements in vec.");
241 glUniform2i(u.location, value[0].as_int(), value[1].as_int());
242 break;
243 case GL_INT_VEC3:
244 WRITE_LOG(value.num_elements() == 3, "Must be three(3) elements in vec.");
245 glUniform3iv(u.location, u.num_elements, &value.as_list_int()[0]);
246 break;
247 case GL_INT_VEC4:
248 WRITE_LOG(value.num_elements() == 4, "Must be four(4) elements in vec.");
249 glUniform4iv(u.location, u.num_elements, &value.as_list_int()[0]);
250 break;
251 case GL_BOOL: glUniform1i(u.location, value.as_bool()); break;
252 case GL_BOOL_VEC2:
253 WRITE_LOG(value.num_elements() == 2, "Must be two(2) elements in vec.");
254 glUniform2i(u.location, value[0].as_bool(), value[1].as_bool());
255 break;
256 case GL_BOOL_VEC3:
257 WRITE_LOG(value.num_elements() == 3, "Must be three(3) elements in vec.");
258 glUniform3i(u.location, value[0].as_bool(), value[1].as_bool(), value[2].as_bool());
259 break;
260 case GL_BOOL_VEC4:
261 WRITE_LOG(value.num_elements() == 4, "Must be four(4) elements in vec.");
262 glUniform4i(u.location, value[0].as_bool(), value[1].as_bool(), value[2].as_bool(), value[3].as_bool());
263 break;
264 case GL_FLOAT_MAT2: {
265 WRITE_LOG(value.num_elements() == 4, "Must be four(4) elements in matrix.");
266 std::vector<GLfloat> f;
267 for(size_t n = 0; n < value.num_elements(); ++n) {
268 f.push_back(GLfloat(value[n].as_decimal().as_float()));
269 }
270 glUniformMatrix2fv(u.location, u.num_elements, GL_FALSE, &f[0]);
271 break;
272 }
273 case GL_FLOAT_MAT3: {
274 WRITE_LOG(value.num_elements() == 9, "Must be nine(9) elements in matrix.");
275 std::vector<GLfloat> f;
276 for(size_t n = 0; n < value.num_elements(); ++n) {
277 f.push_back(GLfloat(value[n].as_decimal().as_float()));
278 }
279 glUniformMatrix3fv(u.location, u.num_elements, GL_FALSE, &f[0]);
280 break;
281 }
282 case GL_FLOAT_MAT4: {
283 WRITE_LOG(value.num_elements() == 16, "Must be four(16) elements in matrix.");
284 std::vector<GLfloat> f;
285 for(size_t n = 0; n < value.num_elements(); ++n) {
286 f.push_back(GLfloat(value[n].as_decimal().as_float()));
287 }
288 glUniformMatrix4fv(u.location, u.num_elements, GL_FALSE, &f[0]);
289 break;
290 }
291
292 case GL_SAMPLER_2D: glUniform1i(u.location, value.as_int()); break;
293
294 case GL_SAMPLER_CUBE:
295 default:
296 WRITE_LOG(false, "Unhandled uniform type: " << it->second.type);
297 }
298 }
299
set_uniform_or_defer(const std::string & key,const variant & value)300 void program::set_uniform_or_defer(const std::string& key, const variant& value)
301 {
302 std::map<std::string, actives>::iterator it = uniforms_.find(key);
303 WRITE_LOG(it != uniforms_.end(), "No uniform found with name: " << key);
304 it->second.last_value = value;
305
306 GLint cur_prog;
307 glGetIntegerv(GL_CURRENT_PROGRAM, &cur_prog);
308 if(cur_prog != get()) {
309 uniforms_to_update_.push_back(key);
310 return;
311 }
312 set_uniform(it, value);
313 }
314
315 namespace {
316 class uniforms_callable : public game_logic::formula_callable
317 {
318 program_ptr program_;
get_value(const std::string & key) const319 variant get_value(const std::string& key) const
320 {
321 //GLfloat f[16];
322 //glGetUniformfv(program_->get(), program_->get_uniform(key), f);
323 //GLenum err = glGetError();
324 //ASSERT_LOG(err == GL_NONE, "glGetUniformfv OpenGL error: 0x" << std::hex << err);
325 // XXX fixme to check type of uniform for number of elements to return.
326 //return variant(f[0]);
327 return program_->get_uniform_value(key);
328 }
set_value(const std::string & key,const variant & value)329 void set_value(const std::string& key, const variant& value)
330 {
331 program_->set_uniform_or_defer(key, value);
332 }
333 public:
uniforms_callable(const program & p)334 explicit uniforms_callable(const program& p)
335 : program_(const_cast<program*>(&p))
336 {}
337 };
338
339 class attributes_callable : public game_logic::formula_callable
340 {
341 program_ptr program_;
get_value(const std::string & key) const342 variant get_value(const std::string& key) const
343 {
344 return program_->get_attributes_value(key);
345 }
set_value(const std::string & key,const variant & value)346 void set_value(const std::string& key, const variant& value)
347 {
348 program_->set_attributes(key, value);
349 }
350 public:
attributes_callable(const program & p)351 explicit attributes_callable(const program& p)
352 : program_(const_cast<program*>(&p))
353 {}
354 };
355 }
356
get_value(const std::string & key) const357 variant program::get_value(const std::string& key) const
358 {
359 #if defined(USE_GLES2)
360 if(key == "uniforms") {
361 return variant(new uniforms_callable(*this));
362 } else if(key == "attributes") {
363 return variant(new attributes_callable(*this));
364 } else if(key == "alpha") {
365 return variant(get_alpha());
366 } else if(key == "color") {
367 std::vector<variant> v;
368 for(int n = 0; n < 4; ++n) {
369 v.push_back(variant(get_color()[n]));
370 }
371 return variant(&v);
372 } else if(key == "point_size") {
373 //return variant(get_point_size());
374 GLfloat pt_size;
375 glGetFloatv(GL_POINT_SIZE, &pt_size);
376 return variant(pt_size);
377 } else if(key == "mvp_matrix" ) {
378 std::vector<variant> v;
379 for(size_t n = 0; n < 16; n++) {
380 v.push_back(variant(((GLfloat*)(&gles2::get_mvp_matrix().x.x))[n]));
381 }
382 return variant(&v);
383 }
384 #endif
385 return variant();
386 }
387
set_value(const std::string & key,const variant & value)388 void program::set_value(const std::string& key, const variant& value)
389 {
390 }
391
392 namespace {
convert_mode(const std::string & smode)393 GLenum convert_mode(const std::string& smode)
394 {
395 if(smode == "points") {
396 return GL_POINTS;
397 } else if(smode == "lines") {
398 return GL_LINES;
399 } else if(smode == "line_strips") {
400 return GL_LINE_STRIP;
401 } else if(smode == "line_loop") {
402 return GL_LINE_LOOP;
403 } else if(smode == "triangles") {
404 return GL_TRIANGLES;
405 } else if(smode == "triangle_strip") {
406 return GL_TRIANGLE_STRIP;
407 } else if(smode == "triangle_fan") {
408 return GL_TRIANGLE_FAN;
409 }
410 ASSERT_LOG(false, "Unexpected mode type: " << smode);
411 return GL_POINTS;
412 }
413
get_blend_mode(variant v)414 GLenum get_blend_mode(variant v)
415 {
416 if(v.is_string()) {
417 const std::string s = v.as_string();
418 if(s == "zero") {
419 return GL_ZERO;
420 } else if(s == "one") {
421 return GL_ONE;
422 } else if(s == "src_color") {
423 return GL_SRC_COLOR;
424 } else if(s == "one_minus_src_color") {
425 return GL_ONE_MINUS_SRC_COLOR;
426 } else if(s == "src_alpha") {
427 return GL_SRC_ALPHA;
428 } else if(s == "one_minus_src_alpha") {
429 return GL_ONE_MINUS_SRC_ALPHA;
430 } else if(s == "dst_alpha") {
431 return GL_DST_ALPHA;
432 } else if(s == "one_minus_dst_alpha") {
433 return GL_ONE_MINUS_DST_ALPHA;
434 }
435 ASSERT_LOG(false, "Unrecognised blend mode (maybe needs adding): " << s);
436 } else if(v.is_int()) {
437 return v.as_int();
438 }
439 ASSERT_LOG(false, "Expected blend mode to be a string or integer");
440 return GL_ZERO;
441 }
442
443 class get_mvp_matrix_function : public game_logic::function_expression
444 {
445 public:
get_mvp_matrix_function(const args_list & args)446 explicit get_mvp_matrix_function(const args_list& args)
447 : function_expression("get_mvp_matrix", args, 0, 0)
448 {}
449 private:
execute(const game_logic::formula_callable & variables) const450 variant execute(const game_logic::formula_callable& variables) const {
451 game_logic::formula::fail_if_static_context();
452 std::vector<variant> v;
453 #if defined(USE_GLES2)
454 for(size_t n = 0; n < 16; n++) {
455 v.push_back(variant(((GLfloat*)(&gles2::get_mvp_matrix().x.x))[n]));
456 }
457 #endif
458 return variant(&v);
459 }
460 };
461
462 class draw_arrays_command : public game_logic::command_callable
463 {
464 public:
draw_arrays_command(GLenum mode,GLint first,GLsizei count)465 explicit draw_arrays_command(GLenum mode, GLint first, GLsizei count)
466 : mode_(mode), first_(first), count_(count)
467 {}
execute(formula_callable & ob) const468 virtual void execute(formula_callable& ob) const
469 {
470 glDrawArrays(mode_, first_, count_);
471 }
472 private:
473 GLenum mode_;
474 GLint first_;
475 GLsizei count_;
476 };
477
478 class draw_arrays_function : public game_logic::function_expression
479 {
480 public:
draw_arrays_function(const args_list & args)481 explicit draw_arrays_function(const args_list& args)
482 : function_expression("draw_arrays", args, 3, 3)
483 {}
484 private:
execute(const game_logic::formula_callable & variables) const485 variant execute(const game_logic::formula_callable& variables) const
486 {
487 game_logic::formula::fail_if_static_context();
488 GLenum mode;
489 variant vmode = args()[0]->evaluate(variables);
490 if(vmode.is_string()) {
491 mode = convert_mode(vmode.as_string());
492 } else if(vmode.is_int()) {
493 mode = vmode.as_int();
494 } else {
495 ASSERT_LOG(false, "Unexpected type for mode argument: " << vmode.type());
496 }
497 return variant(new draw_arrays_command(mode,
498 args()[1]->evaluate(variables).as_int(),
499 args()[2]->evaluate(variables).as_int()));
500 }
501 };
502
503 class draw_elements_command : public game_logic::command_callable
504 {
505 public:
draw_elements_command(GLenum mode,std::vector<GLshort> * indicies)506 explicit draw_elements_command(GLenum mode, std::vector<GLshort>* indicies)
507 : mode_(mode)
508 {
509 indicies_.swap(*indicies);
510 }
execute(formula_callable & ob) const511 virtual void execute(formula_callable& ob) const
512 {
513 glDrawElements(mode_, indicies_.size(), GL_SHORT, &indicies_[0]);
514 }
515 private:
516 GLenum mode_;
517 std::vector<GLshort> indicies_;
518 };
519
520 class draw_elements_function : public game_logic::function_expression
521 {
522 public:
draw_elements_function(const args_list & args)523 explicit draw_elements_function(const args_list& args)
524 : function_expression("draw_elements", args, 2, 2)
525 {}
526 private:
execute(const game_logic::formula_callable & variables) const527 variant execute(const game_logic::formula_callable& variables) const
528 {
529 game_logic::formula::fail_if_static_context();
530 GLenum mode;
531 variant vmode = args()[0]->evaluate(variables);
532 if(vmode.is_string()) {
533 mode = convert_mode(vmode.as_string());
534 } else if(vmode.is_int()) {
535 mode = vmode.as_int();
536 } else {
537 ASSERT_LOG(false, "Unexpected type for mode argument: " << vmode.type());
538 }
539 variant ndxs = args()[1]->evaluate(variables);
540 std::vector<GLshort> indicies;
541 for(size_t n = 0; n < ndxs.num_elements(); ++n) {
542 indicies.push_back(GLshort(ndxs[n].as_int()));
543 }
544 return variant(new draw_elements_command(mode, &indicies));
545 }
546 };
547
548 class bind_texture_command : public game_logic::command_callable
549 {
550 public:
bind_texture_command(GLuint tex_id,GLuint active=0)551 explicit bind_texture_command(GLuint tex_id, GLuint active = 0)
552 : tex_id_(tex_id), active_(active)
553 {}
execute(formula_callable & ob) const554 virtual void execute(formula_callable& ob) const
555 {
556 glActiveTexture(GL_TEXTURE0 + active_);
557 glBindTexture(GL_TEXTURE_2D, tex_id_);
558 }
559 private:
560 GLuint tex_id_;
561 GLuint active_;
562 };
563
564 class bind_texture_function : public game_logic::function_expression
565 {
566 public:
bind_texture_function(const args_list & args)567 explicit bind_texture_function(const args_list& args)
568 : function_expression("bind_texture", args, 1, 2)
569 {}
570 private:
execute(const game_logic::formula_callable & variables) const571 variant execute(const game_logic::formula_callable& variables) const
572 {
573 GLuint active_tex = args().size() > 1 ? args()[1]->evaluate(variables).as_int() : 0;
574 return variant(new bind_texture_command(GLuint(args()[0]->evaluate(variables).as_int()), active_tex));
575 }
576 };
577
578 class texture_callable : public game_logic::formula_callable
579 {
580 public:
texture_callable(const graphics::texture & tex)581 explicit texture_callable(const graphics::texture& tex): tex_(tex)
582 {}
get_value(const std::string & key) const583 variant get_value(const std::string& key) const
584 {
585 if(key == "id") {
586 return variant(tex_.get_id());
587 }
588 return variant();
589 }
590 private:
591 graphics::texture tex_;
592 };
593
594 class load_texture_function : public game_logic::function_expression
595 {
596 public:
load_texture_function(const args_list & args)597 explicit load_texture_function(const args_list& args)
598 : function_expression("load_texture", args, 1, 1)
599 {}
600 private:
execute(const game_logic::formula_callable & variables) const601 variant execute(const game_logic::formula_callable& variables) const
602 {
603 const std::string filename = module::map_file(args()[0]->evaluate(variables).as_string());
604 const graphics::texture tex = graphics::texture::get(filename);
605 return variant(new texture_callable(tex));
606 }
607 };
608
609 class blend_mode_command : public game_logic::command_callable
610 {
611 public:
blend_mode_command(GLenum src,GLenum dst)612 explicit blend_mode_command(GLenum src, GLenum dst)
613 : src_(src), dst_(dst)
614 {}
execute(formula_callable & ob) const615 virtual void execute(formula_callable& ob) const
616 {
617 glEnable(GL_BLEND);
618 glBlendFunc(src_, dst_);
619 }
620 private:
621 GLenum src_;
622 GLenum dst_;
623 };
624
625 class blend_mode_function : public game_logic::function_expression
626 {
627 public:
blend_mode_function(const args_list & args)628 explicit blend_mode_function(const args_list& args)
629 : function_expression("blend_mode", args, 2, 2)
630 {}
631 private:
execute(const game_logic::formula_callable & variables) const632 variant execute(const game_logic::formula_callable& variables) const
633 {
634 const GLenum src = get_blend_mode(args()[0]->evaluate(variables));
635 const GLenum dst = get_blend_mode(args()[1]->evaluate(variables));
636 return variant(new blend_mode_command(src, dst));
637 }
638 };
639
640 class shader_symbol_table : public game_logic::function_symbol_table
641 {
642 public:
shader_symbol_table()643 shader_symbol_table()
644 {}
645
create_function(const std::string & fn,const std::vector<game_logic::expression_ptr> & args,const game_logic::formula_callable_definition * callable_def) const646 game_logic::expression_ptr create_function(
647 const std::string& fn,
648 const std::vector<game_logic::expression_ptr>& args,
649 const game_logic::formula_callable_definition* callable_def) const
650 {
651 if(fn == "get_mvp_matrix") {
652 return game_logic::expression_ptr(new get_mvp_matrix_function(args));
653 } else if(fn == "draw_arrays") {
654 return game_logic::expression_ptr(new draw_arrays_function(args));
655 } else if(fn == "draw_elements") {
656 return game_logic::expression_ptr(new draw_elements_function(args));
657 } else if(fn == "bind_texture") {
658 return game_logic::expression_ptr(new bind_texture_function(args));
659 } else if(fn == "load_texture") {
660 return game_logic::expression_ptr(new load_texture_function(args));
661 } else if(fn == "blend_mode") {
662 return game_logic::expression_ptr(new blend_mode_function(args));
663 }
664 return function_symbol_table::create_function(fn, args, callable_def);
665 }
666 };
667
get_shader_symbol_table()668 game_logic::function_symbol_table& get_shader_symbol_table()
669 {
670 static shader_symbol_table table;
671 return table;
672 }
673 }
674
create_formula(const variant & v)675 game_logic::formula_ptr program::create_formula(const variant& v)
676 {
677 return game_logic::formula_ptr(new game_logic::formula(v, &get_shader_symbol_table()));
678 }
679
execute_command(const variant & var)680 bool program::execute_command(const variant& var)
681 {
682 bool result = true;
683 if(var.is_null()) {
684 return result;
685 }
686
687 if(var.is_list()) {
688 const int num_elements = var.num_elements();
689 for(int n = 0; n != num_elements; ++n) {
690 if(var[n].is_null() == false) {
691 result = execute_command(var[n]) && result;
692 }
693 }
694 } else {
695 game_logic::command_callable* cmd = var.try_convert<game_logic::command_callable>();
696 if(cmd != NULL) {
697 cmd->execute(*this);
698 }
699 }
700 return result;
701 }
702
get_attributes_value(const std::string & key) const703 variant program::get_attributes_value(const std::string& key) const
704 {
705 std::map<std::string, actives>::const_iterator it = attribs_.find(key);
706 ASSERT_LOG(it != attribs_.end(), "No attribute found with name: " << key);
707 return it->second.last_value;
708 }
709
set_attributes(const std::string & key,const variant & value)710 void program::set_attributes(const std::string& key, const variant& value)
711 {
712 std::map<std::string, actives>::iterator it = attribs_.find(key);
713 ASSERT_LOG(it != attribs_.end(), "No attribute found with name: " << key << ", prog: " << get());
714 const actives& a = it->second;
715 WRITE_LOG(a.type == GL_FLOAT || a.type == GL_FLOAT_VEC2 || a.type == GL_FLOAT_VEC3 || a.type == GL_FLOAT_VEC4,
716 "Attribute type must be float not: " << a.type);
717 it->second.last_value = value;
718
719 if(value.is_callable()) {
720 boost::intrusive_ptr<game_logic::float_array_callable> f = value.try_convert<game_logic::float_array_callable>();
721 boost::intrusive_ptr<game_logic::short_array_callable> s = value.try_convert<game_logic::short_array_callable>();
722 if(f != NULL) {
723 ::glVertexAttribPointer(a.location, f->num_elements(), GL_FLOAT, GL_FALSE, 0, &(f->floats()[0]));
724 //GLenum err = glGetError();
725 //GLint prog, max_va;
726 //glGetIntegerv(GL_CURRENT_PROGRAM, &prog);
727 //glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_va);
728 //ASSERT_LOG(err == GL_NONE, "Error in OpenGL: 0x"
729 // << std::hex << err << std::dec << " : " << key << " : " << a.location << " : " << a.type << " : " << f->num_elements()
730 // << ", progs " << prog << " : " << get() << ", max vertex attribs: " << max_va);
731 } else if(s != NULL) {
732 ::glVertexAttribPointer(a.location, s->num_elements(), GL_SHORT, GL_FALSE, 0, &(s->shorts()[0]));
733 //GLenum err = glGetError();
734 //ASSERT_LOG(err == GL_NONE, "Error in OpenGL: 0x" << std::hex << err << std::dec << " : " << key << " : " << a.location << " : " << a.type);
735 } else {
736 ASSERT_LOG(false, "Couldn't convert to float_array or short_array type: " << a.name);
737 }
738 ::glEnableVertexAttribArray(a.location);
739 //GLenum err = glGetError();
740 //ASSERT_LOG(err == GL_NONE, "Error in OpenGL: 0x" << std::hex << err << std::dec << " : " << key << " : " << a.location << " : " << a.type);
741 active_attributes_.push_back(a.location);
742 } else {//if(a.num_elements == value.num_elements()) {
743 // Probably just a constant. not an attrib array.
744 if(value.num_elements() == 1) {
745 ASSERT_LOG(value.is_decimal(), "Value not floating point number");
746 glVertexAttrib1f(a.location, GLfloat(value.as_decimal().as_float()));
747 } else if(value.num_elements() == 2) {
748 ASSERT_LOG(value.is_list(), "Value not list");
749 glVertexAttrib2f(a.location,
750 GLfloat(value[0].as_decimal().as_float()),
751 GLfloat(value[1].as_decimal().as_float()));
752 } else if(value.num_elements() == 3) {
753 ASSERT_LOG(value.is_list(), "Value not list");
754 glVertexAttrib3f(a.location,
755 GLfloat(value[0].as_decimal().as_float()),
756 GLfloat(value[1].as_decimal().as_float()),
757 GLfloat(value[2].as_decimal().as_float()));
758 } else if(value.num_elements() == 4) {
759 ASSERT_LOG(value.is_list(), "Value not list");
760 //std::cerr << "set attribute \"" << key << "\" to " << value << std::endl;
761 glVertexAttrib4f(a.location,
762 GLfloat(value[0].as_decimal().as_float()),
763 GLfloat(value[1].as_decimal().as_float()),
764 GLfloat(value[2].as_decimal().as_float()),
765 GLfloat(value[3].as_decimal().as_float()));
766 glDisableVertexAttribArray(a.location);
767 } else {
768 ASSERT_LOG(false, "Unrecognised attribute type: " << value.type() << " : " << a.name << " : " << a.num_elements << "," << value.num_elements());
769 }
770 //} else {
771 // ASSERT_LOG(false, "Unrecognised attribute type: " << value.type() << " : " << a.name << " : " << a.num_elements << "," << value.num_elements());
772 }
773 }
774
disable_vertex_attrib(GLint)775 void program::disable_vertex_attrib(GLint)
776 {
777 for(size_t n = 0; n < active_attributes_.size(); ++n) {
778 ::glDisableVertexAttribArray(active_attributes_[n]);
779 }
780 active_attributes_.clear();
781 }
782
write()783 variant program::write()
784 {
785 variant_builder res;
786 res.add("program", name());
787 res.add("vertex", vs_.name());
788 res.add("fragment", fs_.name());
789 if(stored_attributes_.is_null() == false) {
790 res.add("attributes", stored_attributes_);
791 }
792 return res.build();
793 }
794
vertex_attrib_array(GLint ndx,GLint size,GLenum type,GLboolean normalized,GLsizei stride,const GLvoid * ptr)795 void program::vertex_attrib_array(GLint ndx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
796 {
797 ::glVertexAttribPointer(ndx, size, type, normalized, stride, ptr);
798 ::glEnableVertexAttribArray(ndx);
799 active_attributes_.push_back(ndx);
800 }
801
vertex_array(GLint size,GLenum type,GLboolean normalized,GLsizei stride,const GLvoid * ptr)802 void program::vertex_array(GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
803 {
804 if(stored_attributes_.has_key("vertex")) {
805 const variant& v = stored_attributes_["vertex"];
806 if(v.is_string()) {
807 vertex_attrib_array(get_attribute(v.as_string()), size, type, normalized, stride, ptr);
808 } else {
809 ASSERT_LOG(false, "Expected vertex attribute to be string.");
810 }
811 } else {
812 ASSERT_LOG(false, "No attribute mapping found for: 'vertex', program: " << name());
813 }
814 }
815
texture_array(GLint size,GLenum type,GLboolean normalized,GLsizei stride,const GLvoid * ptr)816 void program::texture_array(GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
817 {
818 if(stored_attributes_.has_key("texcoord")) {
819 const variant& v = stored_attributes_["texcoord"];
820 if(v.is_string()) {
821 vertex_attrib_array(get_attribute(v.as_string()), size, type, normalized, stride, ptr);
822 } else {
823 ASSERT_LOG(false, "Expected texcoord attribute to be string.");
824 }
825 } else {
826 ASSERT_LOG(false, "No attribute mapping found for: 'texcoord', program: " << name());
827 }
828 }
829
color_array(GLint size,GLenum type,GLboolean normalized,GLsizei stride,const GLvoid * ptr)830 void program::color_array(GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
831 {
832 if(stored_attributes_.has_key("color")) {
833 const variant& v = stored_attributes_["color"];
834 if(v.is_string()) {
835 vertex_attrib_array(get_attribute(v.as_string()), size, type, normalized, stride, ptr);
836 } else {
837 ASSERT_LOG(false, "Expected color attribute to be string.");
838 }
839 } else {
840 ASSERT_LOG(false, "No attribute mapping found for: 'color', program: " << name());
841 }
842 }
843
set_fixed_attributes(const variant & node)844 void program::set_fixed_attributes(const variant& node)
845 {
846 stored_attributes_ = node;
847 }
848
load_shaders(const std::string & shader_data)849 void program::load_shaders(const std::string& shader_data)
850 {
851 variant node = json::parse(shader_data);
852 //std::cerr << "load_shaders: " << node << std::endl;
853 ASSERT_LOG(node.is_map() && node.has_key("shaders") && node.has_key("programs"),
854 "shaders.cfg must be a map with \"shaders\" and \"programs\" attributes.");
855 for(size_t n = 0; n < node["programs"].num_elements(); ++n) {
856 const variant& prog = node["programs"][n];
857 ASSERT_LOG(prog.has_key("vertex")
858 && prog.has_key("fragment")
859 && prog.has_key("name"),
860 "Program's must contain \"vertex\", \"fragment\" and \"name\" attributes.");
861 const std::string vs_name = prog["vertex"].as_string();
862 const std::string fs_name = prog["fragment"].as_string();
863
864 ASSERT_LOG(node["shaders"].has_key("vertex")
865 && node["shaders"]["vertex"].has_key(vs_name),
866 "No key \"" << vs_name << "\" found under \"vertex\" attribute.");
867 ASSERT_LOG(node["shaders"].has_key("fragment")
868 && node["shaders"]["fragment"].has_key(fs_name),
869 "No key \"" << vs_name << "\" found under \"fragment\" attribute.");
870 std::string vert_data = node["shaders"]["vertex"][vs_name].as_string();
871 std::string frag_data = node["shaders"]["fragment"][fs_name].as_string();
872
873 // Simple test to differntiate shaders as strings, compared to shaders in files.
874 // i.e. shaders as strings will have "void main" stanzas, it would be kind of
875 // pathological to create a file containing "void main" as part of the filename.
876 const boost::regex re("void\\s+main");
877 if(boost::regex_search(vert_data, re) == false) {
878 // Try loading as file.
879 vert_data = sys::read_file(module::map_file("data/" + vert_data));
880 }
881 if(boost::regex_search(frag_data, re) == false) {
882 // Try loading as file.
883 frag_data = sys::read_file(module::map_file("data/" + frag_data));
884 }
885
886 gles2::shader v_shader(GL_VERTEX_SHADER, vs_name, vert_data);
887 gles2::shader f_shader(GL_FRAGMENT_SHADER, fs_name, frag_data);
888 const std::string& program_name = prog["name"].as_string();
889 add_shader(program_name, v_shader, f_shader, prog["attributes"]);
890
891 std::map<std::string, gles2::program_ptr>::iterator it = shader_programs.find(program_name);
892 ASSERT_LOG(it != shader_programs.end(), "Error! Something bad happened adding the shader.");
893 std::cerr << "Loaded shader program: \"" << program_name << "\"(" << it->second->get() << ") from file. ("
894 << vs_name << ", " << fs_name << ")." << std::endl;
895 }
896 }
897
add_shader(const std::string & program_name,const shader & v_shader,const shader & f_shader,const variant & prog)898 void program::add_shader(const std::string& program_name,
899 const shader& v_shader,
900 const shader& f_shader,
901 const variant& prog)
902 {
903 std::map<std::string, gles2::program_ptr>::iterator it = shader_programs.find(program_name);
904 if(it == shader_programs.end()) {
905 shader_programs[program_name] = program_ptr(new program(program_name, v_shader, f_shader));
906 } else {
907 it->second->init(program_name, v_shader, f_shader);
908 }
909 if(prog.is_null() == false) {
910 shader_programs[program_name]->set_fixed_attributes(prog);
911 }
912 }
913
find_program(const std::string & prog_name)914 program_ptr program::find_program(const std::string& prog_name)
915 {
916 std::map<std::string, gles2::program_ptr>::const_iterator it = shader_programs.find(prog_name);
917 ASSERT_LOG(it != shader_programs.end(), "Shader program \"" << prog_name << "\" not found.");
918 return it->second;
919 }
920
get_shaders()921 std::map<std::string, gles2::program_ptr>& program::get_shaders()
922 {
923 return shader_programs;
924 }
925
clear_shaders()926 void program::clear_shaders()
927 {
928 shader_programs.clear();
929 }
930
set_deferred_uniforms()931 void program::set_deferred_uniforms()
932 {
933 foreach(const std::string& key, uniforms_to_update_) {
934 std::map<std::string, actives>::iterator it = uniforms_.find(key);
935 ASSERT_LOG(it != uniforms_.end(), "No uniform found with name: " << key);
936 set_uniform(it, it->second.last_value);
937 }
938 uniforms_to_update_.clear();
939 }
940
941
942 ///////////////////////////////////////////////////////////////////////////
943 // shader_program
944
shader_program()945 shader_program::shader_program()
946 : vars_(new game_logic::formula_variable_storage()), parent_(NULL), zorder_(-1)
947 {
948 }
949
shader_program(const variant & node,entity * obj)950 shader_program::shader_program(const variant& node, entity* obj)
951 : vars_(new game_logic::formula_variable_storage()), parent_(obj), zorder_(-1)
952 {
953 configure(node, obj);
954 }
955
shader_program(const std::string & program_name)956 shader_program::shader_program(const std::string& program_name)
957 : vars_(new game_logic::formula_variable_storage()), zorder_(-1)
958 {
959 name_ = program_name;
960 program_object_ = program::find_program(name_);
961 }
962
configure(const variant & node,entity * obj)963 void shader_program::configure(const variant& node, entity* obj)
964 {
965 ASSERT_LOG(node.is_map(), "shader attribute must be a map.");
966 name_ = node["program"].as_string();
967 program_object_ = program::find_program(name_);
968 game_logic::formula_callable* e = this;
969 ASSERT_LOG(e != NULL, "Environment was not set.");
970
971 zorder_ = node["zorder"].as_int(-1);
972 if(node.has_key("create")) {
973 const variant& c = node["create"];
974 if(c.is_list()) {
975 for(size_t n = 0; n < c.num_elements(); ++n) {
976 std::string cmd = c[n].as_string();
977 create_commands_.push_back(cmd);
978 ASSERT_LOG(node.has_key(cmd) == true, "No attribute found with name: " << cmd);
979 create_formulas_.push_back(e->create_formula(node[cmd]));
980 }
981 } else if(c.is_string()) {
982 // single formula stored
983 create_formulas_.push_back(e->create_formula(variant(c.as_string())));
984 } else {
985 ASSERT_LOG(false, "create must be string or list");
986 }
987 }
988 if(node.has_key("draw")) {
989 const variant& d = node["draw"];
990 if(d.is_list()) {
991 for(size_t n = 0; n < d.num_elements(); ++n) {
992 std::string cmd = d[n].as_string();
993 draw_commands_.push_back(cmd);
994 ASSERT_LOG(node.has_key(cmd) == true, "No attribute found with name: " << cmd);
995 draw_formulas_.push_back(e->create_formula(node[cmd]));
996 }
997 } else if(d.is_string()) {
998 draw_formulas_.push_back(e->create_formula(variant(d.as_string())));
999 } else {
1000 ASSERT_LOG(false, "draw must be string or list");
1001 }
1002 }
1003
1004 vars_->read(node["vars"]);
1005
1006 //if(obj) {
1007 init(obj);
1008 //}
1009 }
1010
init(entity * obj)1011 void shader_program::init(entity* obj)
1012 {
1013 ASSERT_LOG(name_.empty() != true, "Configure not run, before calling init");
1014 game_logic::formula_callable* e = this;
1015 parent_ = obj;
1016 GLint current_program;
1017 glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program);
1018 glUseProgram(program_object_->get());
1019 for(size_t n = 0; n < create_formulas_.size(); ++n) {
1020 e->execute_command(create_formulas_[n]->execute(*e));
1021 }
1022 glUseProgram(current_program);
1023 }
1024
write()1025 variant shader_program::write()
1026 {
1027 variant_builder res;
1028 res.add("program", name());
1029
1030 if(draw_commands_.size() == 0 && draw_formulas_.size() == 1) {
1031 // write a single formula as a string in "draw" attribute.
1032 res.add("draw", draw_formulas_[0]->str());
1033 } else {
1034 ASSERT_LOG(draw_commands_.size() == draw_formulas_.size(), "commands and formulas not same size");
1035 for(size_t n = 0; n < draw_commands_.size(); ++n) {
1036 res.add("draw", draw_commands_[n]);
1037 res.add(draw_commands_[n], draw_formulas_[n]->str());
1038 }
1039 }
1040
1041 if(create_commands_.size() == 0 && create_formulas_.size() == 1) {
1042 // write a single formula as a string in "draw" attribute.
1043 res.add("create", create_formulas_[0]->str());
1044 } else {
1045 ASSERT_LOG(create_commands_.size() == create_formulas_.size(), "commands and formulas not same size");
1046 for(size_t n = 0; n < create_commands_.size(); ++n) {
1047 res.add("create", create_commands_[n]);
1048 res.add(create_commands_[n], create_formulas_[n]->str());
1049 }
1050 }
1051
1052 if(vars_ != NULL) {
1053 res.add("vars", vars_->write());
1054 }
1055 if(zorder_ != -1) {
1056 res.add("zorder", zorder_);
1057 }
1058 return res.build();
1059 }
1060
prepare_draw()1061 void shader_program::prepare_draw()
1062 {
1063 //#if defined(WIN32)
1064 // profile::manager manager;
1065 //#endif
1066 glUseProgram(program_object_->get());
1067 program_object_->set_deferred_uniforms();
1068 game_logic::formula_callable* e = this;
1069 for(size_t n = 0; n < draw_formulas_.size(); ++n) {
1070 e->execute_command(draw_formulas_[n]->execute(*e));
1071 }
1072 }
1073
get_value(const std::string & key) const1074 variant shader_program::get_value(const std::string& key) const
1075 {
1076 if(key == "vars") {
1077 return variant(vars_.get());
1078 } else if(key == "parent" || key == "object") {
1079 ASSERT_LOG(parent_ != NULL, "Tried to request parent, when value is null");
1080 return variant(parent_);
1081 }
1082 return program_object_->get_value(key);
1083 }
1084
set_value(const std::string & key,const variant & value)1085 void shader_program::set_value(const std::string& key, const variant& value)
1086 {
1087 program_object_->set_value(key, value);
1088 }
1089
shader() const1090 program_ptr shader_program::shader() const
1091 {
1092 ASSERT_LOG(program_object_ != NULL, "null shader program");
1093 return program_object_;
1094 }
1095
clear()1096 void shader_program::clear()
1097 {
1098 program_object_.reset();
1099 name_.clear();
1100 create_commands_.clear();
1101 draw_commands_.clear();
1102 create_formulas_.clear();
1103 draw_formulas_.clear();
1104 }
1105
execute_command(const variant & var)1106 bool shader_program::execute_command(const variant& var)
1107 {
1108 bool result = true;
1109 if(var.is_null()) {
1110 return result;
1111 }
1112
1113 if(var.is_list()) {
1114 const int num_elements = var.num_elements();
1115 for(int n = 0; n != num_elements; ++n) {
1116 if(var[n].is_null() == false) {
1117 result = execute_command(var[n]) && result;
1118 }
1119 }
1120 } else {
1121 game_logic::command_callable* cmd = var.try_convert<game_logic::command_callable>();
1122 if(cmd != NULL) {
1123 cmd->execute(*this);
1124 }
1125 }
1126 return result;
1127 }
1128
create_formula(const variant & v)1129 game_logic::formula_ptr shader_program::create_formula(const variant& v)
1130 {
1131 return game_logic::formula_ptr(new game_logic::formula(v, &get_shader_symbol_table()));
1132 }
1133
1134 }
1135