1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 #include "globalincs/pstypes.h"
12 #include "globalincs/def_files.h"
13 
14 #include "graphics/2d.h"
15 #include "lighting/lighting.h"
16 #include "graphics/grinternal.h"
17 #include "graphics/gropengl.h"
18 #include "graphics/gropenglextension.h"
19 #include "graphics/gropengltexture.h"
20 #include "graphics/gropengllight.h"
21 #include "graphics/gropengltnl.h"
22 #include "graphics/gropengldraw.h"
23 #include "graphics/gropenglshader.h"
24 #include "graphics/gropenglpostprocessing.h"
25 #include "graphics/gropenglstate.h"
26 
27 #include "math/vecmat.h"
28 #include "render/3d.h"
29 #include "cmdline/cmdline.h"
30 #include "mod_table/mod_table.h"
31 
32 
33 SCP_vector<opengl_shader_t> GL_shader;
34 
35 static char *GLshader_info_log = NULL;
36 static const int GLshader_info_log_size = 8192;
37 GLuint Framebuffer_fallback_texture_id = 0;
38 
39 static int Effect_num = 0;
40 static float Anim_timer = 0.0f;
41 
42 
43 /**
44  * Static lookup reference for main shader uniforms
45  * When adding a new SDR_ flag, list all associated uniforms and attributes here
46  */
47 static opengl_shader_uniform_reference_t GL_Uniform_Reference_Main[] = {
48 	{ SDR_FLAG_LIGHT,		1, {"n_lights"}, 0, { NULL }, "Lighting" },
49 	{ SDR_FLAG_FOG,			0, { NULL }, 0, { NULL }, "Fog Effect" },
50 	{ SDR_FLAG_DIFFUSE_MAP, 5, {"sBasemap", "desaturate", "desaturate_r", "desaturate_g", "desaturate_b"}, 0, { NULL }, "Diffuse Mapping"},
51 	{ SDR_FLAG_GLOW_MAP,	1, {"sGlowmap"}, 0, { NULL }, "Glow Mapping" },
52 	{ SDR_FLAG_SPEC_MAP,	1, {"sSpecmap"}, 0, { NULL }, "Specular Mapping" },
53 	{ SDR_FLAG_NORMAL_MAP,	1, {"sNormalmap"}, 0, { NULL }, "Normal Mapping" },
54 	{ SDR_FLAG_HEIGHT_MAP,	1, {"sHeightmap"}, 0, { NULL }, "Parallax Mapping" },
55 	{ SDR_FLAG_ENV_MAP,		3, {"sEnvmap", "alpha_spec", "envMatrix"}, 0, { NULL }, "Environment Mapping" },
56 	{ SDR_FLAG_ANIMATED,	5, {"sFramebuffer", "effect_num", "anim_timer", "vpwidth", "vpheight"}, 0, { NULL }, "Animated Effects" },
57 	{ SDR_FLAG_MISC_MAP,	1, {"sMiscmap"}, 0, { NULL }, "Utility mapping" },
58 	{ SDR_FLAG_TEAMCOLOR,	2, {"stripe_color", "base_color"}, 0, { NULL }, "Team Colors" },
59 	{ SDR_FLAG_THRUSTER,	1, {"thruster_scale"}, 0, { NULL }, "Thruster scaling" }
60 };
61 
62 static const int Main_shader_flag_references = sizeof(GL_Uniform_Reference_Main) / sizeof(opengl_shader_uniform_reference_t);
63 
64 /**
65  * Static lookup referene for particle shader uniforms
66  */
67 static opengl_shader_uniform_reference_t GL_Uniform_Reference_Particle[] = {
68 	{ (SDR_FLAG_SOFT_QUAD | SDR_FLAG_DISTORTION), 6, {"baseMap", "window_width", "window_height", "distMap", "frameBuffer", "use_offset"}, 1, { "offset_in" }, "Distorted Particles" },
69 	{ (SDR_FLAG_SOFT_QUAD),	6, {"baseMap", "depthMap", "window_width", "window_height", "nearZ", "farZ"}, 1, { "radius_in" }, "Depth-blended Particles" }
70 };
71 
72 static const int Particle_shader_flag_references = sizeof(GL_Uniform_Reference_Particle) / sizeof(opengl_shader_uniform_reference_t);
73 
74 opengl_shader_t *Current_shader = NULL;
75 
76 
77 void opengl_shader_check_info_log(GLhandleARB shader_object);
78 
79 /**
80  * Set the currently active shader
81  * @param shader_obj	Pointer to an opengl_shader_t object. This function calls glUseProgramARB with parameter 0 if shader_obj is NULL or if function is called without parameters, causing OpenGL to revert to fixed-function processing
82  */
opengl_shader_set_current(opengl_shader_t * shader_obj)83 void opengl_shader_set_current(opengl_shader_t *shader_obj)
84 {
85 	if (shader_obj != NULL) {
86 		if(!Current_shader || (Current_shader->program_id != shader_obj->program_id)) {
87 			Current_shader = shader_obj;
88 			vglUseProgramObjectARB(Current_shader->program_id);
89 
90 #ifndef NDEBUG
91 			if ( opengl_check_for_errors("shader_set_current()") ) {
92 				vglValidateProgramARB(Current_shader->program_id);
93 
94 				GLint obj_status = 0;
95 				vglGetObjectParameterivARB(Current_shader->program_id, GL_OBJECT_VALIDATE_STATUS_ARB, &obj_status);
96 
97 				if ( !obj_status ) {
98 					opengl_shader_check_info_log(Current_shader->program_id);
99 
100 					mprintf(("VALIDATE INFO-LOG:\n"));
101 
102 					if (strlen(GLshader_info_log) > 5) {
103 						mprintf(("%s\n", GLshader_info_log));
104 					} else {
105 						mprintf(("<EMPTY>\n"));
106 					}
107 				}
108 			}
109 #endif
110 		}
111 	} else {
112 		Current_shader = NULL;
113 		vglUseProgramObjectARB(0);
114 	}
115 }
116 
117 /**
118  * Given a set of flags, determine whether a shader with these flags exists within the GL_shader vector. If no shader with the requested flags exists, attempt to compile one.
119  *
120  * @param flags	Integer variable, holding a combination of SDR_* flags
121  * @return 		Index into GL_shader, referencing a valid shader, or -1 if shader compilation failed
122  */
gr_opengl_maybe_create_shader(int flags)123 int gr_opengl_maybe_create_shader(int flags)
124 {
125 	if (Use_GLSL < 2)
126 		return -1;
127 
128 	size_t idx;
129 	size_t max = GL_shader.size();
130 
131 	for (idx = 0; idx < max; idx++) {
132 		if (GL_shader[idx].flags == flags) {
133 			return idx;
134 		}
135 	}
136 
137 	// If we are here, it means we need to compile a new shader
138 	opengl_compile_main_shader(flags);
139 	if (GL_shader.back().flags == flags)
140 		return (int)GL_shader.size() - 1;
141 
142 	// If even that has failed, bail
143 	return -1;
144 }
145 
146 /**
147  * Go through GL_shader and call glDeleteObject() for all created shaders, then clear GL_shader
148  */
opengl_shader_shutdown()149 void opengl_shader_shutdown()
150 {
151 	size_t i;
152 
153 	if ( !Use_GLSL ) {
154 		return;
155 	}
156 
157 	for (i = 0; i < GL_shader.size(); i++) {
158 		if (GL_shader[i].program_id) {
159 			vglDeleteObjectARB(GL_shader[i].program_id);
160 			GL_shader[i].program_id = 0;
161 		}
162 
163 		GL_shader[i].uniforms.clear();
164 	}
165 
166 	GL_shader.clear();
167 
168 	if (GLshader_info_log != NULL) {
169 		vm_free(GLshader_info_log);
170 		GLshader_info_log = NULL;
171 	}
172 }
173 
174 /**
175  * Load a shader file from disc or from the builtin defaults in def_files.cpp if none can be found.
176  * This function will also create a list of preprocessor defines for the GLSL compiler based on the shader flags
177  * and the supported GLSL version as reported by the GPU driver.
178  *
179  * @param filename	C-string holding the filename (with extension) of the shader file
180  * @param flags		integer variable holding a combination of SDR_* flags
181  * @return			C-string holding the complete shader source code
182  */
opengl_load_shader(char * filename,int flags)183 static char *opengl_load_shader(char *filename, int flags)
184 {
185 	SCP_string sflags;
186 
187 	if (Use_GLSL >= 4) {
188 		sflags += "#define SHADER_MODEL 4\n";
189 	} else if (Use_GLSL == 3) {
190 		sflags += "#define SHADER_MODEL 3\n";
191 	} else {
192 		sflags += "#define SHADER_MODEL 2\n";
193 	}
194 
195 	if (flags & SDR_FLAG_DIFFUSE_MAP) {
196 		sflags += "#define FLAG_DIFFUSE_MAP\n";
197 	}
198 
199 	if (flags & SDR_FLAG_ENV_MAP) {
200 		sflags += "#define FLAG_ENV_MAP\n";
201 	}
202 
203 	if (flags & SDR_FLAG_FOG) {
204 		sflags += "#define FLAG_FOG\n";
205 	}
206 
207 	if (flags & SDR_FLAG_GLOW_MAP) {
208 		sflags += "#define FLAG_GLOW_MAP\n";
209 	}
210 
211 	if (flags & SDR_FLAG_HEIGHT_MAP) {
212 		sflags += "#define FLAG_HEIGHT_MAP\n";
213 	}
214 
215 	if (flags & SDR_FLAG_LIGHT) {
216 		sflags += "#define FLAG_LIGHT\n";
217 	}
218 
219 	if (flags & SDR_FLAG_NORMAL_MAP) {
220 		sflags += "#define FLAG_NORMAL_MAP\n";
221 	}
222 
223 	if (flags & SDR_FLAG_SPEC_MAP) {
224 		sflags += "#define FLAG_SPEC_MAP\n";
225 	}
226 
227 	if (flags & SDR_FLAG_ANIMATED) {
228 		sflags += "#define FLAG_ANIMATED\n";
229 	}
230 
231 	if (flags & SDR_FLAG_DISTORTION) {
232 		sflags += "#define FLAG_DISTORTION\n";
233 	}
234 
235 	if (flags & SDR_FLAG_MISC_MAP) {
236 		sflags += "#define FLAG_MISC_MAP\n";
237 	}
238 
239 	if (flags & SDR_FLAG_TEAMCOLOR) {
240 		sflags += "#define FLAG_TEAMCOLOR\n";
241 	}
242 
243 	if (flags & SDR_FLAG_THRUSTER) {
244 		sflags += "#define FLAG_THRUSTER\n";
245 	}
246 
247 	const char *shader_flags = sflags.c_str();
248 	int flags_len = strlen(shader_flags);
249 
250 	if (Enable_external_shaders) {
251 		CFILE *cf_shader = cfopen(filename, "rt", CFILE_NORMAL, CF_TYPE_EFFECTS);
252 
253 		if (cf_shader != NULL) {
254 			int len = cfilelength(cf_shader);
255 			char *shader = (char*) vm_malloc(len + flags_len + 1);
256 
257 			strcpy(shader, shader_flags);
258 			memset(shader + flags_len, 0, len + 1);
259 			cfread(shader + flags_len, len + 1, 1, cf_shader);
260 			cfclose(cf_shader);
261 
262 			return shader;
263 		}
264 	}
265 
266 	//If we're still here, proceed with internals
267 	mprintf(("   Loading built-in default shader for: %s\n", filename));
268 	char* def_shader = defaults_get_file(filename);
269 	size_t len = strlen(def_shader);
270 	char *shader = (char*) vm_malloc(len + flags_len + 1);
271 
272 	strcpy(shader, shader_flags);
273 	strcat(shader, def_shader);
274 
275 	return shader;
276 }
277 
278 /**
279  * Compiles a new shader, and creates an opengl_shader_t that will be put into the GL_shader vector
280  * if compilation is successful.
281  * This function is used for main (i.e. model rendering) and particle shaders, post processing shaders use their own infrastructure
282  *
283  * @param flags		Combination of SDR_* flags
284  */
opengl_compile_main_shader(int flags)285 void opengl_compile_main_shader(int flags) {
286 	char *vert = NULL, *frag = NULL;
287 
288 	mprintf(("Compiling new shader:\n"));
289 
290 	bool in_error = false;
291 	opengl_shader_t new_shader;
292 
293 	// choose appropriate files
294 	char vert_name[NAME_LENGTH];
295 	char frag_name[NAME_LENGTH];
296 
297 	if (flags & SDR_FLAG_SOFT_QUAD) {
298 		strcpy_s( vert_name, "soft-v.sdr");
299 		strcpy_s( frag_name, "soft-f.sdr");
300 	} else {
301 		strcpy_s( vert_name, "main-v.sdr");
302 		strcpy_s( frag_name, "main-f.sdr");
303 	}
304 
305 	// read vertex shader
306 	if ( (vert = opengl_load_shader(vert_name, flags)) == NULL ) {
307 		in_error = true;
308 		goto Done;
309 	}
310 
311 	// read fragment shader
312 	if ( (frag = opengl_load_shader(frag_name, flags)) == NULL ) {
313 		in_error = true;
314 		goto Done;
315 	}
316 
317 	Verify( vert != NULL );
318 	Verify( frag != NULL );
319 
320 	new_shader.program_id = opengl_shader_create(vert, frag);
321 
322 	if ( !new_shader.program_id ) {
323 		in_error = true;
324 		goto Done;
325 	}
326 
327 	new_shader.flags = flags;
328 
329 	opengl_shader_set_current( &new_shader );
330 
331 	mprintf(("Shader features:\n"));
332 
333 	//Init all the uniforms
334 	if (new_shader.flags & SDR_FLAG_SOFT_QUAD) {
335 		for (int j = 0; j < Particle_shader_flag_references; j++) {
336 			if (new_shader.flags == GL_Uniform_Reference_Particle[j].flag) {
337 				int k;
338 
339 			// Equality check needed because the combination of SDR_FLAG_SOFT_QUAD and SDR_FLAG_DISTORTION define something very different
340 			// than just SDR_FLAG_SOFT_QUAD alone
341 				for (k = 0; k < GL_Uniform_Reference_Particle[j].num_uniforms; k++) {
342 					opengl_shader_init_uniform( GL_Uniform_Reference_Particle[j].uniforms[k] );
343 				}
344 
345 				for (k = 0; k < GL_Uniform_Reference_Particle[j].num_attributes; k++) {
346 					opengl_shader_init_attribute( GL_Uniform_Reference_Particle[j].attributes[k] );
347 				}
348 
349 				mprintf(("   %s\n", GL_Uniform_Reference_Particle[j].name));
350 			}
351 		}
352 	} else {
353 		for (int j = 0; j < Main_shader_flag_references; j++) {
354 			if (new_shader.flags & GL_Uniform_Reference_Main[j].flag) {
355 				if (GL_Uniform_Reference_Main[j].num_uniforms > 0) {
356 					for (int k = 0; k < GL_Uniform_Reference_Main[j].num_uniforms; k++) {
357 						opengl_shader_init_uniform( GL_Uniform_Reference_Main[j].uniforms[k] );
358 					}
359 				}
360 
361 				if (GL_Uniform_Reference_Main[j].num_attributes > 0) {
362 					for (int k = 0; k < GL_Uniform_Reference_Main[j].num_attributes; k++) {
363 						opengl_shader_init_attribute( GL_Uniform_Reference_Main[j].attributes[k] );
364 					}
365 				}
366 
367 				mprintf(("   %s\n", GL_Uniform_Reference_Main[j].name));
368 			}
369 		}
370 	}
371 
372 	opengl_shader_set_current();
373 
374 	// add it to our list of embedded shaders
375 	GL_shader.push_back( new_shader );
376 
377 Done:
378 	if (vert != NULL) {
379 		vm_free(vert);
380 		vert = NULL;
381 	}
382 
383 	if (frag != NULL) {
384 		vm_free(frag);
385 		frag = NULL;
386 	}
387 
388 	if (in_error) {
389 		// shut off relevant usage things ...
390 		bool dealt_with = false;
391 
392 		if (flags & SDR_FLAG_HEIGHT_MAP) {
393 			mprintf(("  Shader in_error!  Disabling height maps!\n"));
394 			Cmdline_height = 0;
395 			dealt_with = true;
396 		}
397 
398 		if (flags & SDR_FLAG_NORMAL_MAP) {
399 			mprintf(("  Shader in_error!  Disabling normal maps and height maps!\n"));
400 			Cmdline_height = 0;
401 			Cmdline_normal = 0;
402 			dealt_with = true;
403 		}
404 
405 		if (!dealt_with) {
406 			if (flags == 0) {
407 				mprintf(("  Shader in_error!  Disabling GLSL!\n"));
408 
409 				Use_GLSL = 0;
410 				Cmdline_height = 0;
411 				Cmdline_normal = 0;
412 
413 				GL_shader.clear();
414 			} else {
415 				// We died on a lighting shader, probably due to instruction count.
416 				// Drop down to a special var that will use fixed-function rendering
417 				// but still allow for post-processing to work
418 				mprintf(("  Shader in_error!  Disabling GLSL model rendering!\n"));
419 				Use_GLSL = 1;
420 				Cmdline_height = 0;
421 				Cmdline_normal = 0;
422 			}
423 		}
424 	}
425 }
426 
427 /**
428  * Initializes the shader system. Creates a 1x1 texture that can be used as a fallback texture when framebuffer support is missing.
429  * Also compiles the shaders used for particle rendering.
430  */
opengl_shader_init()431 void opengl_shader_init()
432 {
433 	if ( !Use_GLSL ) {
434 		return;
435 	}
436 
437 	glGenTextures(1,&Framebuffer_fallback_texture_id);
438 	GL_state.Texture.SetActiveUnit(0);
439 	GL_state.Texture.SetTarget(GL_TEXTURE_2D);
440 	GL_state.Texture.Enable(Framebuffer_fallback_texture_id);
441 
442 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
443 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
444 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
445 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
446 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
447 	GLuint pixels[4] = {0,0,0,0};
448 	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &pixels);
449 
450 	if (Cmdline_no_glsl_model_rendering) {
451 		Use_GLSL = 1;
452 	}
453 
454 	GL_shader.clear();
455 
456 	// Reserve 32 shader slots. This should cover most use cases in real life.
457 	GL_shader.reserve(32);
458 
459 	// Compile the particle shaders, since these are most definitely going to be used
460 	opengl_compile_main_shader(SDR_FLAG_SOFT_QUAD);
461 	opengl_compile_main_shader(SDR_FLAG_SOFT_QUAD | SDR_FLAG_DISTORTION);
462 
463 	mprintf(("\n"));
464 }
465 
466 /**
467  * Retrieve the compilation log for a given shader object, and store it in the GLshader_info_log global variable
468  *
469  * @param shader_object		OpenGL handle of a shader object
470  */
opengl_shader_check_info_log(GLhandleARB shader_object)471 void opengl_shader_check_info_log(GLhandleARB shader_object)
472 {
473 	if (GLshader_info_log == NULL) {
474 		GLshader_info_log = (char *) vm_malloc(GLshader_info_log_size);
475 	}
476 
477 	memset(GLshader_info_log, 0, GLshader_info_log_size);
478 
479 	vglGetInfoLogARB(shader_object, GLshader_info_log_size-1, 0, GLshader_info_log);
480 }
481 
482 /**
483  * Pass a GLSL shader source to OpenGL and compile it into a usable shader object.
484  * Prints compilation errors (if any) to the log.
485  * Note that this will only compile shaders into objects, linking them into executables happens later
486  *
487  * @param shader_source		GLSL sourcecode for the shader
488  * @param shader_type		OpenGL ID for the type of shader being used, like GL_FRAGMENT_SHADER_ARB, GL_VERTEX_SHADER_ARB
489  * @return 					OpenGL handle for the compiled shader object
490  */
opengl_shader_compile_object(const GLcharARB * shader_source,GLenum shader_type)491 GLhandleARB opengl_shader_compile_object(const GLcharARB *shader_source, GLenum shader_type)
492 {
493 	GLhandleARB shader_object = 0;
494 	GLint status = 0;
495 
496 	shader_object = vglCreateShaderObjectARB(shader_type);
497 
498 	vglShaderSourceARB(shader_object, 1, &shader_source, NULL);
499 	vglCompileShaderARB(shader_object);
500 
501 	// check if the compile was successful
502 	vglGetObjectParameterivARB(shader_object, GL_OBJECT_COMPILE_STATUS_ARB, &status);
503 
504 	opengl_shader_check_info_log(shader_object);
505 
506 	// we failed, bail out now...
507 	if (status == 0) {
508 		// basic error check
509 		mprintf(("%s shader failed to compile:\n%s\n", (shader_type == GL_VERTEX_SHADER_ARB) ? "Vertex" : "Fragment", GLshader_info_log));
510 
511 		// this really shouldn't exist, but just in case
512 		if (shader_object) {
513 			vglDeleteObjectARB(shader_object);
514 		}
515 
516 		return 0;
517 	}
518 
519 	// we succeeded, maybe output warnings too
520 	if (strlen(GLshader_info_log) > 5) {
521 		nprintf(("SHADER-DEBUG", "%s shader compiled with warnings:\n%s\n", (shader_type == GL_VERTEX_SHADER_ARB) ? "Vertex" : "Fragment", GLshader_info_log));
522 	}
523 
524 	return shader_object;
525 }
526 
527 /**
528  * Link a vertex shader object and a fragment shader object into a usable shader executable.
529  * Prints linker errors (if any) to the log.
530  *
531  * @param vertex_object		Compiled vertex shader object
532  * @param fragment_object	Compiled fragment shader object
533  * @return					Shader executable
534  */
opengl_shader_link_object(GLhandleARB vertex_object,GLhandleARB fragment_object)535 GLhandleARB opengl_shader_link_object(GLhandleARB vertex_object, GLhandleARB fragment_object)
536 {
537 	GLhandleARB shader_object = 0;
538 	GLint status = 0;
539 
540 	shader_object = vglCreateProgramObjectARB();
541 
542 	if (vertex_object) {
543 		vglAttachObjectARB(shader_object, vertex_object);
544 	}
545 
546 	if (fragment_object) {
547 		vglAttachObjectARB(shader_object, fragment_object);
548 	}
549 
550 	vglLinkProgramARB(shader_object);
551 
552 	// check if the link was successful
553 	vglGetObjectParameterivARB(shader_object, GL_OBJECT_LINK_STATUS_ARB, &status);
554 
555 	opengl_shader_check_info_log(shader_object);
556 
557 	// we failed, bail out now...
558 	if (status == 0) {
559 		mprintf(("Shader failed to link:\n%s\n", GLshader_info_log));
560 
561 		if (shader_object) {
562 			vglDeleteObjectARB(shader_object);
563 		}
564 
565 		return 0;
566 	}
567 
568 	// we succeeded, maybe output warnings too
569 	if (strlen(GLshader_info_log) > 5) {
570 		nprintf(("SHADER-DEBUG", "Shader linked with warnings:\n%s\n", GLshader_info_log));
571 	}
572 
573 	return shader_object;
574 }
575 
576 /**
577  * Creates an executable shader.
578  *
579  * @param vs	Vertex shader source code
580  * @param fs	Fragment shader source code
581  * @return 		Internal ID of the compiled and linked shader as generated by OpenGL
582  */
opengl_shader_create(const char * vs,const char * fs)583 GLhandleARB opengl_shader_create(const char *vs, const char *fs)
584 {
585 	GLhandleARB vs_o = 0;
586 	GLhandleARB fs_o = 0;
587 	GLhandleARB program = 0;
588 
589 	if (vs) {
590 		vs_o = opengl_shader_compile_object( (const GLcharARB*)vs, GL_VERTEX_SHADER_ARB );
591 
592 		if ( !vs_o ) {
593 			mprintf(("ERROR! Unable to create vertex shader!\n"));
594 			goto Done;
595 		}
596 	}
597 
598 	if (fs) {
599 		fs_o = opengl_shader_compile_object( (const GLcharARB*)fs, GL_FRAGMENT_SHADER_ARB );
600 
601 		if ( !fs_o ) {
602 			mprintf(("ERROR! Unable to create fragment shader!\n"));
603 			goto Done;
604 		}
605 	}
606 
607 	program = opengl_shader_link_object(vs_o, fs_o);
608 
609 	if ( !program ) {
610 		mprintf(("ERROR! Unable to create shader program!\n"));
611 	}
612 
613 Done:
614 	if (vs_o) {
615 		vglDeleteObjectARB(vs_o);
616 	}
617 
618 	if (fs_o) {
619 		vglDeleteObjectARB(fs_o);
620 	}
621 
622 	return program;
623 }
624 
625 /**
626  * Initialize a shader attribute. Requires that the Current_shader global variable is valid.
627  *
628  * @param attribute_text	Name of the attribute to be initialized
629  */
opengl_shader_init_attribute(const char * attribute_text)630 void opengl_shader_init_attribute(const char *attribute_text)
631 {
632 	opengl_shader_uniform_t new_attribute;
633 
634 	if ( ( Current_shader == NULL ) || ( attribute_text == NULL ) ) {
635 		Int3();
636 		return;
637 	}
638 
639 	new_attribute.text_id = attribute_text;
640 	new_attribute.location = vglGetAttribLocationARB(Current_shader->program_id, attribute_text);
641 
642 	if ( new_attribute.location < 0 ) {
643 		nprintf(("SHADER-DEBUG", "WARNING: Unable to get shader attribute location for \"%s\"!\n", attribute_text));
644 		return;
645 	}
646 
647 	Current_shader->attributes.push_back( new_attribute );
648 }
649 
650 /**
651  * Get the internal OpenGL location for a given attribute. Requires that the Current_shader global variable is valid
652  *
653  * @param attribute_text	Name of the attribute
654  * @return					Internal OpenGL location for the attribute
655  */
opengl_shader_get_attribute(const char * attribute_text)656 GLint opengl_shader_get_attribute(const char *attribute_text)
657 {
658 	if ( (Current_shader == NULL) || (attribute_text == NULL) ) {
659 		Int3();
660 		return -1;
661 	}
662 
663 	SCP_vector<opengl_shader_uniform_t>::iterator attribute;
664 
665 	for (attribute = Current_shader->attributes.begin(); attribute != Current_shader->attributes.end(); ++attribute) {
666 		if ( !attribute->text_id.compare(attribute_text) ) {
667 			return attribute->location;
668 		}
669 	}
670 
671 	return -1;
672 }
673 
674 /**
675  * Initialize a shader uniform. Requires that the Current_shader global variable is valid.
676  *
677  * @param uniform_text		Name of the uniform to be initialized
678  */
opengl_shader_init_uniform(const char * uniform_text)679 void opengl_shader_init_uniform(const char *uniform_text)
680 {
681 	opengl_shader_uniform_t new_uniform;
682 
683 	if ( (Current_shader == NULL) || (uniform_text == NULL) ) {
684 		Int3();
685 		return;
686 	}
687 
688 	new_uniform.text_id = uniform_text;
689 	new_uniform.location = vglGetUniformLocationARB(Current_shader->program_id, uniform_text);
690 
691 	if (new_uniform.location < 0) {
692 		nprintf(("SHADER-DEBUG", "WARNING: Unable to get shader uniform location for \"%s\"!\n", uniform_text));
693 		return;
694 	}
695 
696 	Current_shader->uniforms.push_back( new_uniform );
697 }
698 
699 /**
700  * Get the internal OpenGL location for a given uniform. Requires that the Current_shader global variable is valid
701  *
702  * @param uniform_text	Name of the uniform
703  * @return				Internal OpenGL location for the uniform
704  */
opengl_shader_get_uniform(const char * uniform_text)705 GLint opengl_shader_get_uniform(const char *uniform_text)
706 {
707 	if ( (Current_shader == NULL) || (uniform_text == NULL) ) {
708 		Int3();
709 		return -1;
710 	}
711 
712 	SCP_vector<opengl_shader_uniform_t>::iterator uniform;
713 	SCP_vector<opengl_shader_uniform_t>::iterator uniforms_end = Current_shader->uniforms.end();
714 
715 	for (uniform = Current_shader->uniforms.begin(); uniform != uniforms_end; ++uniform) {
716 		if ( !uniform->text_id.compare(uniform_text) ) {
717 			return uniform->location;
718 		}
719 	}
720 
721 	return -1;
722 }
723 
724 /**
725  * Sets the currently active animated effect.
726  *
727  * @param effect	Effect ID, needs to be implemented and checked for in the shader
728  */
opengl_shader_set_animated_effect(int effect)729 void opengl_shader_set_animated_effect(int effect)
730 {
731 	Assert(effect > -1);
732 	Effect_num = effect;
733 }
734 
735 /**
736  * Returns the currently active animated effect ID.
737  *
738  * @return		Currently active effect ID
739  */
opengl_shader_get_animated_effect()740 int opengl_shader_get_animated_effect()
741 {
742 	return Effect_num;
743 }
744 
745 /**
746  * Set the timer for animated effects.
747  *
748  * @param timer		Timer value to be passed to the shader
749  */
opengl_shader_set_animated_timer(float timer)750 void opengl_shader_set_animated_timer(float timer)
751 {
752 	Anim_timer = timer;
753 }
754 
755 /**
756  * Get the timer for animated effects.
757  */
opengl_shader_get_animated_timer()758 float opengl_shader_get_animated_timer()
759 {
760 	return Anim_timer;
761 }
762