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, &current_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