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 #include "graphics/opengl/gropenglshader.h"
11 
12 #include "ShaderProgram.h"
13 
14 #include "cfile/cfile.h"
15 #include "cmdline/cmdline.h"
16 #include "def_files/def_files.h"
17 #include "graphics/2d.h"
18 #include "graphics/grinternal.h"
19 #include "graphics/matrix.h"
20 #include "graphics/opengl/gropengldraw.h"
21 #include "graphics/opengl/gropenglpostprocessing.h"
22 #include "graphics/opengl/gropenglstate.h"
23 #include "graphics/opengl/gropengltexture.h"
24 #include "graphics/opengl/gropengltnl.h"
25 #include "graphics/util/uniform_structs.h"
26 #include "lighting/lighting.h"
27 #include "math/vecmat.h"
28 #include "mod_table/mod_table.h"
29 #include "render/3d.h"
30 
31 #include <jansson.h>
32 #include <md5.h>
33 
34 SCP_vector<opengl_shader_t> GL_shader;
35 
36 typedef std::pair<int, uint32_t> shader_descriptor_t;
37 
38 struct key_hasher
39 {
operator ()key_hasher40     size_t operator()(const shader_descriptor_t &obj) const
41     {
42         return obj.first ^ obj.second;
43     }
44 };
45 
46 SCP_unordered_map<shader_descriptor_t, size_t, key_hasher> GL_shader_map;
47 
48 GLuint Framebuffer_fallback_texture_id = 0;
49 
50 SCP_vector<opengl_vert_attrib> GL_vertex_attrib_info =
51 	{
52 		{ opengl_vert_attrib::POSITION,		"vertPosition",		{{{ 0.0f, 0.0f, 0.0f, 1.0f }}} },
53 		{ opengl_vert_attrib::COLOR,		"vertColor",		{{{ 1.0f, 1.0f, 1.0f, 1.0f }}} },
54 		{ opengl_vert_attrib::TEXCOORD,		"vertTexCoord",		{{{ 1.0f, 1.0f, 1.0f, 1.0f }}} },
55 		{ opengl_vert_attrib::NORMAL,		"vertNormal",		{{{ 0.0f, 0.0f, 1.0f, 0.0f }}} },
56 		{ opengl_vert_attrib::TANGENT,		"vertTangent",		{{{ 1.0f, 0.0f, 0.0f, 0.0f }}} },
57 		{ opengl_vert_attrib::MODEL_ID,		"vertModelID",		{{{ 0.0f, 0.0f, 0.0f, 0.0f }}} },
58 		{ opengl_vert_attrib::RADIUS,		"vertRadius",		{{{ 1.0f, 0.0f, 0.0f, 0.0f }}} },
59 		{ opengl_vert_attrib::UVEC,			"vertUvec",			{{{ 0.0f, 1.0f, 0.0f, 0.0f }}} },
60 		{ opengl_vert_attrib::WORLD_MATRIX,	"vertWorldMatrix",	{{{ 1.0f, 0.0f, 0.0f, 0.0f }}} },
61 	};
62 
63 struct opengl_uniform_block_binding {
64 	uniform_block_type block_type;
65 	const char* name;
66 };
67 
68 opengl_uniform_block_binding GL_uniform_blocks[] = {
69     {uniform_block_type::Lights, "lightData"},
70     {uniform_block_type::ModelData, "modelData"},
71     {uniform_block_type::NanoVGData, "NanoVGUniformData"},
72     {uniform_block_type::DecalInfo, "decalInfoData"},
73     {uniform_block_type::DecalGlobals, "decalGlobalData"},
74     {uniform_block_type::DeferredGlobals, "globalDeferredData"},
75     {uniform_block_type::Matrices, "matrixData"},
76     {uniform_block_type::GenericData, "genericData"},
77 };
78 
79 /**
80  * Static lookup reference for shader uniforms
81  * When adding a new shader, list all associated uniforms and attributes here
82  */
83 // clang-format off
84 static opengl_shader_type_t GL_shader_types[] = {
85 	{ SDR_TYPE_MODEL, "main-v.sdr", "main-f.sdr", "main-g.sdr",
86 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD, opengl_vert_attrib::NORMAL, opengl_vert_attrib::TANGENT, opengl_vert_attrib::MODEL_ID }, "Model Rendering", false },
87 
88 	{ SDR_TYPE_EFFECT_PARTICLE, "effect-v.sdr", "effect-f.sdr", "effect-g.sdr",
89 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD, opengl_vert_attrib::RADIUS, opengl_vert_attrib::COLOR }, "Particle Effects", false },
90 
91 	{ SDR_TYPE_EFFECT_DISTORTION, "effect-distort-v.sdr", "effect-distort-f.sdr", 0,
92 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD, opengl_vert_attrib::RADIUS, opengl_vert_attrib::COLOR }, "Distortion Effects", false },
93 
94 	{ SDR_TYPE_POST_PROCESS_MAIN, "post-v.sdr", "post-f.sdr", 0,
95 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Post Processing", false },
96 
97 	{ SDR_TYPE_POST_PROCESS_BLUR, "post-v.sdr", "blur-f.sdr", 0,
98 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Gaussian Blur", false },
99 
100 	{ SDR_TYPE_POST_PROCESS_BLOOM_COMP, "post-v.sdr", "bloom-comp-f.sdr", 0,
101 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Bloom Compositing", false },
102 
103 	{ SDR_TYPE_POST_PROCESS_BRIGHTPASS, "post-v.sdr", "brightpass-f.sdr", 0,
104 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Bloom Brightpass", false },
105 
106 	{ SDR_TYPE_POST_PROCESS_FXAA, "fxaa-v.sdr", "fxaa-f.sdr", 0,
107 		{ opengl_vert_attrib::POSITION }, "FXAA", false },
108 
109 	{ SDR_TYPE_POST_PROCESS_FXAA_PREPASS, "post-v.sdr", "fxaapre-f.sdr", 0,
110 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "FXAA Prepass", false },
111 
112 	{ SDR_TYPE_POST_PROCESS_LIGHTSHAFTS, "post-v.sdr", "ls-f.sdr", 0,
113 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Lightshafts", false },
114 
115 	{ SDR_TYPE_POST_PROCESS_TONEMAPPING, "post-v.sdr", "tonemapping-f.sdr", 0,
116 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Tonemapping", false },
117 
118 	{ SDR_TYPE_DEFERRED_LIGHTING, "deferred-v.sdr", "deferred-f.sdr", 0,
119 		{ opengl_vert_attrib::POSITION }, "Deferred Lighting", false },
120 
121 	{ SDR_TYPE_DEFERRED_CLEAR, "deferred-clear-v.sdr", "deferred-clear-f.sdr", 0,
122 		{ opengl_vert_attrib::POSITION }, "Clear Deferred Lighting Buffer", false },
123 
124 	{ SDR_TYPE_VIDEO_PROCESS, "video-v.sdr", "video-f.sdr", 0,
125 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Video Playback", false },
126 
127 	{ SDR_TYPE_PASSTHROUGH_RENDER, "passthrough-v.sdr", "passthrough-f.sdr", 0,
128 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD, opengl_vert_attrib::COLOR }, "Passthrough", false },
129 
130 	{ SDR_TYPE_SHIELD_DECAL, "shield-impact-v.sdr",	"shield-impact-f.sdr", 0,
131 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::NORMAL }, "Shield Decals", false },
132 
133 	{ SDR_TYPE_BATCHED_BITMAP, "batched-v.sdr", "batched-f.sdr", nullptr,
134 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD, opengl_vert_attrib::COLOR }, "Batched bitmaps", false },
135 
136 	{ SDR_TYPE_DEFAULT_MATERIAL, "default-material.vert.spv.glsl", "default-material.frag.spv.glsl", nullptr,
137 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD, opengl_vert_attrib::COLOR }, "Default material", true },
138 
139 	{ SDR_TYPE_NANOVG, "nanovg-v.sdr", "nanovg-f.sdr", nullptr,
140 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "NanoVG shader", false },
141 
142 	{ SDR_TYPE_DECAL, "decal-v.sdr", "decal-f.sdr", nullptr,
143 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::WORLD_MATRIX }, "Decal rendering", false },
144 
145 	{ SDR_TYPE_SCENE_FOG, "post-v.sdr", "fog-f.sdr", nullptr,
146 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Scene fogging", false },
147 
148 	{ SDR_TYPE_ROCKET_UI, "rocketui-v.sdr",	"rocketui-f.sdr", nullptr,
149 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::COLOR, opengl_vert_attrib::TEXCOORD }, "libRocket UI", false },
150 
151 	{ SDR_TYPE_POST_PROCESS_SMAA_EDGE, "smaa-edge-v.sdr", "smaa-edge-f.sdr", nullptr,
152 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "SMAA Edge detection", false },
153 
154 	{ SDR_TYPE_POST_PROCESS_SMAA_BLENDING_WEIGHT, "smaa-blend-v.sdr", "smaa-blend-f.sdr", nullptr,
155 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "SMAA Blending weight calculation", false },
156 
157 	{ SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING, "smaa-neighbour-v.sdr", "smaa-neighbour-f.sdr", nullptr,
158 		{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "SMAA Neighborhood Blending", false },
159 };
160 // clang-format on
161 
162 /**
163  * Static lookup reference for shader variant uniforms
164  * When adding a new shader variant for a shader, list all associated uniforms and attributes here
165  */
166 static opengl_shader_variant_t GL_shader_variants[] = {
167 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_LIGHT, "FLAG_LIGHT", {}, "Lighting"},
168 
169 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_FOG, "FLAG_FOG", {}, "Fog Effect"},
170 
171 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_DIFFUSE_MAP, "FLAG_DIFFUSE_MAP", {}, "Diffuse Mapping"},
172 
173 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_GLOW_MAP, "FLAG_GLOW_MAP", {}, "Glow Mapping"},
174 
175 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_SPEC_MAP, "FLAG_SPEC_MAP", {}, "Specular Mapping"},
176 
177 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_NORMAL_MAP, "FLAG_NORMAL_MAP", {}, "Normal Mapping"},
178 
179 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_HEIGHT_MAP, "FLAG_HEIGHT_MAP", {}, "Parallax Mapping"},
180 
181 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_ENV_MAP, "FLAG_ENV_MAP", {}, "Environment Mapping"},
182 
183 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_ANIMATED, "FLAG_ANIMATED", {}, "Animated Effects"},
184 
185 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_MISC_MAP, "FLAG_MISC_MAP", {}, "Utility mapping"},
186 
187 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_TEAMCOLOR, "FLAG_TEAMCOLOR", {}, "Team Colors"},
188 
189 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_DEFERRED, "FLAG_DEFERRED", {}, "Deferred lighting"},
190 
191 	{SDR_TYPE_MODEL, true, SDR_FLAG_MODEL_SHADOW_MAP, "FLAG_SHADOW_MAP", {}, "Shadow Mapping"},
192 
193 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_SHADOWS, "FLAG_SHADOWS", {}, "Shadows"},
194 
195 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_THRUSTER, "FLAG_THRUSTER", {}, "Thruster scaling"},
196 
197 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_TRANSFORM, "FLAG_TRANSFORM", {}, "Submodel Transforms"},
198 
199 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_CLIP, "FLAG_CLIP", {}, "Clip Plane"},
200 
201 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_HDR, "FLAG_HDR", {}, "High Dynamic Range"},
202 
203 	{ SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_AMBIENT_MAP, "FLAG_AMBIENT_MAP",
204 		{  }, "Ambient Occlusion Map"},
205 
206 	{SDR_TYPE_MODEL, false, SDR_FLAG_MODEL_NORMAL_ALPHA, "FLAG_NORMAL_ALPHA", {}, "Normal Alpha"},
207 
208 	{SDR_TYPE_MODEL, true, SDR_FLAG_MODEL_THICK_OUTLINES, "FLAG_THICK_OUTLINE", {}, "Thick outlines"},
209 
210 	{SDR_TYPE_EFFECT_PARTICLE,
211 	 true,
212 	 SDR_FLAG_PARTICLE_POINT_GEN,
213 	 "FLAG_EFFECT_GEOMETRY",
214 	 {opengl_vert_attrib::UVEC},
215 	 "Geometry shader point-based particles"},
216 
217 	{SDR_TYPE_POST_PROCESS_BLUR, false, SDR_FLAG_BLUR_HORIZONTAL, "PASS_0", {}, "Horizontal blur pass"},
218 
219 	{SDR_TYPE_POST_PROCESS_BLUR, false, SDR_FLAG_BLUR_VERTICAL, "PASS_1", {}, "Vertical blur pass"},
220 
221 	{SDR_TYPE_NANOVG, false, SDR_FLAG_NANOVG_EDGE_AA, "EDGE_AA", {}, "NanoVG edge anti-alias"},
222 
223 	{SDR_TYPE_DECAL, false, SDR_FLAG_DECAL_USE_NORMAL_MAP, "USE_NORMAL_MAP", {}, "Decal use scene normal map"},
224 };
225 
226 static const int GL_num_shader_variants = sizeof(GL_shader_variants) / sizeof(opengl_shader_variant_t);
227 
228 opengl_shader_t *Current_shader = NULL;
229 
opengl_shader_t()230 opengl_shader_t::opengl_shader_t() : shader(SDR_TYPE_NONE), flags(0), flags2(0)
231 {
232 }
opengl_shader_t(opengl_shader_t && other)233 opengl_shader_t::opengl_shader_t(opengl_shader_t&& other) noexcept {
234 	*this = std::move(other);
235 }
236 // NOLINTNEXTLINE(misc-unconventional-assign-operator)
operator =(opengl_shader_t && other)237 opengl_shader_t& opengl_shader_t::operator=(opengl_shader_t&& other) noexcept {
238 	// VS2013 doesn't support implicit move constructors so we need to explicitly declare it
239 	shader = other.shader;
240 	flags = other.flags;
241 	flags2 = other.flags2;
242 
243 	program = std::move(other.program);
244 
245 	return *this;
246 }
247 
248 /**
249  * Set the currently active shader
250  * @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
251  */
opengl_shader_set_current(opengl_shader_t * shader_obj)252 void opengl_shader_set_current(opengl_shader_t *shader_obj)
253 {
254 	if (Current_shader != shader_obj) {
255 		GR_DEBUG_SCOPE("Set shader");
256 
257 		GL_state.Array.ResetVertexAttribs();
258 
259 		if(shader_obj) {
260 			shader_obj->program->use();
261 		} else {
262 			GL_state.UseProgram(0);
263 		}
264 
265 		Current_shader = shader_obj;
266 	}
267 }
268 
opengl_shader_set_current(int handle)269 void opengl_shader_set_current(int handle)
270 {
271 	Assert(handle >= 0);
272 	Assert(handle < (int)GL_shader.size());
273 
274 	opengl_shader_set_current(&GL_shader[handle]);
275 }
276 
opengl_get_shader_idx(shader_type shader_t,unsigned int flags)277 size_t opengl_get_shader_idx(shader_type shader_t, unsigned int flags)
278 {
279 	auto found = GL_shader_map.find(shader_descriptor_t(shader_t, flags));
280 	if (found != GL_shader_map.end()) {
281 		return found->second;
282 	}
283 	return GL_shader.size();
284 }
285 
286 /**
287  * 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.
288  *
289  * @param shader_t  shader_type variable, a reference to the shader program needed
290  * @param flags	Integer variable, holding a combination of SDR_* flags
291  * @return 		Index into GL_shader, referencing a valid shader, or -1 if shader compilation failed
292  */
gr_opengl_maybe_create_shader(shader_type shader_t,unsigned int flags)293 int gr_opengl_maybe_create_shader(shader_type shader_t, unsigned int flags)
294 {
295 	size_t idx = opengl_get_shader_idx(shader_t, flags);
296 
297 	if (idx < GL_shader.size())
298 		return (int)idx;
299 
300 	// If we are here, it means we need to compile a new shader
301 	return opengl_compile_shader(shader_t, flags);
302 }
303 
opengl_delete_shader(int sdr_handle)304 void opengl_delete_shader(int sdr_handle)
305 {
306 	Assert(sdr_handle >= 0);
307 	Assert(sdr_handle < (int)GL_shader.size());
308 	opengl_shader_t &victim = GL_shader[sdr_handle];
309 	GL_shader_map.erase(shader_descriptor_t(victim.shader, victim.flags));
310 
311 	GL_shader[sdr_handle].program.reset();
312 
313 	GL_shader[sdr_handle].flags = 0;
314 	GL_shader[sdr_handle].flags2 = 0;
315 	GL_shader[sdr_handle].shader = NUM_SHADER_TYPES;
316 }
317 
318 /**
319  * Go through GL_shader and call glDeleteObject() for all created shaders, then clear GL_shader
320  */
opengl_shader_shutdown()321 void opengl_shader_shutdown()
322 {
323 	GL_shader.clear();
324 	GL_shader_map.clear();
325 }
326 
opengl_shader_get_header(shader_type type_id,int flags,bool has_geo_shader)327 static SCP_string opengl_shader_get_header(shader_type type_id, int flags, bool has_geo_shader) {
328 	SCP_stringstream sflags;
329 
330 	sflags << "#version " << GLSL_version << " core\n";
331 
332 	if (Detail.lighting < 3) {
333 		sflags << "#define FLAG_LIGHT_MODEL_BLINN_PHONG\n";
334 	}
335 
336 	if (has_geo_shader) {
337 		// If there is a geometry shader then we define a special preprocessor symbol to make writing shaders easier
338 		sflags << "#define HAS_GEOMETRY_SHADER\n";
339 	}
340 
341 	if (type_id == SDR_TYPE_POST_PROCESS_MAIN || type_id == SDR_TYPE_POST_PROCESS_LIGHTSHAFTS ||
342 	    type_id == SDR_TYPE_POST_PROCESS_FXAA || type_id == SDR_TYPE_POST_PROCESS_SMAA_EDGE ||
343 	    type_id == SDR_TYPE_POST_PROCESS_SMAA_BLENDING_WEIGHT ||
344 	    type_id == SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING) {
345 		// ignore looking for variants. main post process, lightshafts, and FXAA shaders need special headers to be hacked in
346 		opengl_post_shader_header(sflags, type_id, flags);
347 	} else {
348 		for (int i = 0; i < GL_num_shader_variants; ++i) {
349 			opengl_shader_variant_t &variant = GL_shader_variants[i];
350 
351 			if (type_id == variant.type_id && flags & variant.flag) {
352 				sflags << "#define " << variant.flag_text << "\n";
353 			}
354 		}
355 	}
356 
357 	return sflags.str();
358 }
359 
360 /**
361  * Load a shader file from disc or from the builtin defaults in def_files.cpp if none can be found.
362  * This function will also create a list of preprocessor defines for the GLSL compiler based on the shader flags
363  * and the supported GLSL version as reported by the GPU driver.
364  *
365  * @param shader	shader_type enum defined with which shader we're loading
366  * @param filename	C-string holding the filename (with extension) of the shader file
367  * @param flags		integer variable holding a combination of SDR_* flags
368  * @return			C-string holding the complete shader source code
369  */
opengl_load_shader(const char * filename)370 static SCP_string opengl_load_shader(const char* filename) {
371 	SCP_string content;
372 	if (Enable_external_shaders) {
373 		CFILE* cf_shader = cfopen(filename, "rt", CFILE_NORMAL, CF_TYPE_EFFECTS);
374 
375 		if (cf_shader != NULL) {
376 			int len = cfilelength(cf_shader);
377 			content.resize(len);
378 
379 			cfread(&content[0], len + 1, 1, cf_shader);
380 			cfclose(cf_shader);
381 
382 			return content;
383 		}
384 	}
385 
386 	//If we're still here, proceed with internals
387 	mprintf(("   Loading built-in default shader for: %s\n", filename));
388 	auto def_shader = defaults_get_file(filename);
389 	content.assign(reinterpret_cast<const char*>(def_shader.data), def_shader.size);
390 
391 	return content;
392 }
393 
handle_includes_impl(SCP_vector<SCP_string> & include_stack,SCP_stringstream & output,int & include_counter,const SCP_string & filename,const SCP_string & original)394 static void handle_includes_impl(SCP_vector<SCP_string>& include_stack,
395 								 SCP_stringstream& output,
396 								 int& include_counter,
397 								 const SCP_string& filename,
398 								 const SCP_string& original) {
399 	include_stack.emplace_back(filename);
400 	auto current_source_number = include_counter + 1;
401 
402 	const char* INCLUDE_STRING = "#include";
403 	SCP_stringstream input(original);
404 
405 	int line_num = 1;
406 	for (SCP_string line; std::getline(input, line);) {
407 		auto include_start = line.find(INCLUDE_STRING);
408 		if (include_start != SCP_string::npos) {
409 			auto first_quote = line.find('"', include_start + strlen(INCLUDE_STRING));
410 			auto second_quote = line.find('"', first_quote + 1);
411 
412 			if (first_quote == SCP_string::npos || second_quote == SCP_string::npos) {
413 				Error(LOCATION,
414 					  "Shader %s:%d: Malformed include line. Could not find both quote charaters.",
415 					  filename.c_str(),
416 					  line_num);
417 			}
418 
419 			auto file_name = line.substr(first_quote + 1, second_quote - first_quote - 1);
420 			auto existing_name = std::find_if(include_stack.begin(), include_stack.end(), [&file_name](const SCP_string& str) {
421 				return str == file_name;
422 			});
423 			if (existing_name != include_stack.end()) {
424 				SCP_stringstream stack_string;
425 				for (auto& name : include_stack) {
426 					stack_string << "\t" << name << "\n";
427 				}
428 
429 				Error(LOCATION,
430 					  "Shader %s:%d: Detected cyclic include! Previous includes (top level file first):\n%s",
431 					  filename.c_str(),
432 					  line_num,
433 					  stack_string.str().c_str());
434 			}
435 
436 			++include_counter;
437 			// The second parameter defines which source string we are currently working with. We keep track of how many
438 			// excludes have been in the file so far to specify this
439 			output << "#line 1 " << include_counter + 1 << "\n";
440 
441 			handle_includes_impl(include_stack,
442 								 output,
443 								 include_counter,
444 								 file_name,
445 								 opengl_load_shader(file_name.c_str()));
446 
447 			// We are done with the include file so now we can return to the original file
448 			output << "#line " << line_num + 1 << " " << current_source_number << "\n";
449 		} else {
450 			output << line << "\n";
451 		}
452 
453 		++line_num;
454 	}
455 
456 	include_stack.pop_back();
457 }
458 
handle_includes(const char * filename,const SCP_string & original)459 static SCP_string handle_includes(const char* filename, const SCP_string& original) {
460 	SCP_stringstream output;
461 	SCP_vector<SCP_string> include_stack;
462 	auto include_counter = 0;
463 
464 	handle_includes_impl(include_stack, output, include_counter, filename, original);
465 
466 	return output.str();
467 }
468 
469 static SCP_vector<SCP_string>
opengl_get_shader_content(shader_type type_id,const char * filename,int flags,bool has_geo_shader,bool spirv_shader)470 opengl_get_shader_content(shader_type type_id, const char* filename, int flags, bool has_geo_shader, bool spirv_shader)
471 {
472 	SCP_vector<SCP_string> parts;
473 	if (spirv_shader) {
474 		// No need to add a header here or handle includes since the original compiler did that
475 		parts.push_back(opengl_load_shader(filename));
476 	} else {
477 		parts.push_back(opengl_shader_get_header(type_id, flags, has_geo_shader));
478 
479 		parts.push_back(handle_includes(filename, opengl_load_shader(filename)));
480 	}
481 
482 	return parts;
483 }
484 
add_shader_parts(MD5 & md5,const SCP_vector<SCP_string> & parts)485 static void add_shader_parts(MD5& md5, const SCP_vector<SCP_string>& parts) {
486 	for (auto& part : parts) {
487 		md5.update(part.c_str(), (MD5::size_type) part.size());
488 	}
489 }
490 
get_shader_hash(const SCP_vector<SCP_string> & vert,const SCP_vector<SCP_string> & geom_content,const SCP_vector<SCP_string> & frag)491 static SCP_string get_shader_hash(const SCP_vector<SCP_string>& vert,
492 								  const SCP_vector<SCP_string>& geom_content,
493 								  const SCP_vector<SCP_string>& frag) {
494 	MD5 md5;
495 	add_shader_parts(md5, vert);
496 	add_shader_parts(md5, geom_content);
497 	add_shader_parts(md5, frag);
498 
499 	// Add the attribute locations so that changes get detected
500 	for (uint32_t i = 0; i < (uint32_t)GL_vertex_attrib_info.size(); ++i) {
501 		md5.update(GL_vertex_attrib_info[i].name.c_str(), (MD5::size_type) GL_vertex_attrib_info[i].name.size());
502 		md5.update(reinterpret_cast<const char*>(&i), sizeof(i));
503 	}
504 
505 	md5.update(GL_implementation_id.data(),
506 	           (MD5::size_type)GL_implementation_id.size() * sizeof(SCP_string::value_type));
507 
508 	md5.finalize();
509 
510 	return md5.hexdigest();
511 }
512 
do_shader_caching()513 static bool do_shader_caching() {
514 	if (!GLAD_GL_ARB_get_program_binary) {
515 		// Not supported until OpenGL 4.1
516 		return false;
517 	}
518 	if (Cmdline_noshadercache) {
519 		// Shader cache is disabled
520 		return false;
521 	}
522 	return true;
523 }
524 
load_cached_shader_binary(opengl::ShaderProgram * program,const SCP_string & hash)525 static bool load_cached_shader_binary(opengl::ShaderProgram* program, const SCP_string& hash) {
526 	if (!do_shader_caching()) {
527 		return false;
528 	}
529 
530 	auto base_filename = SCP_string("ogl_shader-") + hash;
531 
532 	auto metadata = base_filename + ".json";
533 	auto binary = base_filename + ".bin";
534 
535 	auto metadata_fp = cfopen(metadata.c_str(), "rb", CFILE_NORMAL, CF_TYPE_CACHE, false,
536 	                          CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
537 	if (!metadata_fp) {
538 		nprintf(("ShaderCache", "Metadata file does not exist.\n"));
539 		return false;
540 	}
541 
542 	auto size = cfilelength(metadata_fp);
543 	SCP_string metadata_content;
544 	metadata_content.resize((size_t) size);
545 	cfread(&metadata_content[0], 1, size, metadata_fp);
546 
547 	cfclose(metadata_fp);
548 
549 	auto metadata_root = json_loads(metadata_content.c_str(), 0, nullptr);
550 	if (!metadata_root) {
551 		mprintf(("Loading of cache metadata failed! Falling back to GLSL shader...\n"));
552 		return false;
553 	}
554 
555 	json_int_t format;
556 	if (json_unpack(metadata_root, "{sI}", "format", &format) != 0) {
557 		mprintf(("Failed to unpack values from metadata JSON! Falling back to GLSL shader...\n"));
558 		return false;
559 	}
560 	auto binary_format = (GLenum) format;
561 	json_decref(metadata_root);
562 
563 	bool supported = false;
564 	for (auto supported_fmt : GL_binary_formats) {
565 		if ((GLenum)supported_fmt == binary_format) {
566 			supported = true;
567 			break;
568 		}
569 	}
570 
571 	if (!supported) {
572 		// This can happen in case an implementation stops supporting a particular binary format
573 		nprintf(("ShaderCache", "Unsupported binary format %d encountered in shader cache.\n", binary_format));
574 		return false;
575 	}
576 
577 	auto binary_fp = cfopen(binary.c_str(), "rb", CFILE_NORMAL, CF_TYPE_CACHE, false,
578 	                        CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
579 	if (!binary_fp) {
580 		nprintf(("ShaderCache", "Binary file does not exist.\n"));
581 		return false;
582 	}
583 
584 	GR_DEBUG_SCOPE("Loading cached shader");
585 
586 	SCP_vector<uint8_t> buffer;
587 	int length = cfilelength(binary_fp);
588 	buffer.resize((size_t) length);
589 	cfread(&buffer[0], 1, length, binary_fp);
590 
591 	cfclose(binary_fp);
592 
593 	// Load the data!
594 	glProgramBinary(program->getShaderHandle(), binary_format, buffer.data(), (GLsizei) buffer.size());
595 
596 	// Check the status...
597 	GLint status;
598 	glGetProgramiv(program->getShaderHandle(), GL_LINK_STATUS, &status);
599 
600 	return status == GL_TRUE;
601 }
json_write_callback(const char * buffer,size_t size,void * data)602 static int json_write_callback(const char *buffer, size_t size, void *data) {
603 	CFILE* cfp = (CFILE*)data;
604 
605 	if ((size_t)cfwrite(buffer, 1, (int)size, cfp) != size) {
606 		return -1; // Error
607 	} else {
608 		return 0; // Success
609 	}
610 }
611 
cache_program_binary(GLuint program,const SCP_string & hash)612 static void cache_program_binary(GLuint program, const SCP_string& hash) {
613 	if (!do_shader_caching()) {
614 		return;
615 	}
616 
617 	GR_DEBUG_SCOPE("Saving shader binary");
618 
619 	GLint size;
620 	glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &size);
621 
622 	if (size <= 0) {
623 		// No binary available (I'm looking at you Mesa...)
624 		return;
625 	}
626 
627 	SCP_vector<uint8_t> binary;
628 	binary.resize((size_t) size);
629 	GLenum binary_fmt;
630 	GLsizei length;
631 	glGetProgramBinary(program, (GLsizei) binary.size(), &length, &binary_fmt, binary.data());
632 	if (length == 0) {
633 		return;
634 	}
635 
636 	auto base_filename = SCP_string("ogl_shader-") + hash;
637 
638 	auto metadata_name = base_filename + ".json";
639 	auto binary_name = base_filename + ".bin";
640 
641 	auto metadata_fp = cfopen(metadata_name.c_str(), "wb", CFILE_NORMAL, CF_TYPE_CACHE, false,
642 	                          CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
643 	if (!metadata_fp) {
644 		mprintf(("Could not open shader cache metadata file!\n"));
645 		return;
646 	}
647 
648 	auto metadata = json_pack("{sI}", "format", (json_int_t)binary_fmt);
649 	if (json_dump_callback(metadata, json_write_callback, metadata_fp, 0) != 0) {
650 		mprintf(("Failed to write shader cache metadata file!\n"));
651 		cfclose(metadata_fp);
652 		return;
653 	}
654 	cfclose(metadata_fp);
655 	json_decref(metadata);
656 
657 	auto binary_fp = cfopen(binary_name.c_str(), "wb", CFILE_NORMAL, CF_TYPE_CACHE, false,
658 	                        CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
659 	if (!binary_fp) {
660 		mprintf(("Could not open shader cache binary file!\n"));
661 		return;
662 	}
663 	cfwrite(binary.data(), 1, (int) binary.size(), binary_fp);
664 	cfclose(binary_fp);
665 }
666 
opengl_set_default_uniforms(const opengl_shader_t & sdr)667 static void opengl_set_default_uniforms(const opengl_shader_t& sdr) {
668 	switch (sdr.shader) {
669 	case SDR_TYPE_DEFERRED_LIGHTING:
670 		Current_shader->program->Uniforms.setTextureUniform("ColorBuffer", 0);
671 		Current_shader->program->Uniforms.setTextureUniform("NormalBuffer", 1);
672 		Current_shader->program->Uniforms.setTextureUniform("PositionBuffer", 2);
673 		Current_shader->program->Uniforms.setTextureUniform("SpecBuffer", 3);
674 		Current_shader->program->Uniforms.setTextureUniform("shadow_map", 4);
675 		break;
676 
677 	case SDR_TYPE_PASSTHROUGH_RENDER:
678 		Current_shader->program->Uniforms.setTextureUniform("baseMap", 0);
679 		Current_shader->program->Uniforms.setTextureUniform("clipEnabled", 0);
680 		break;
681 
682 	default:
683 		// No default values for this shader type.
684 		break;
685 	}
686 }
687 
opengl_compile_shader_actual(shader_type sdr,const uint & flags,opengl_shader_t & new_shader)688 void opengl_compile_shader_actual(shader_type sdr, const uint &flags, opengl_shader_t &new_shader)
689 {
690 	opengl_shader_type_t *sdr_info = &GL_shader_types[sdr];
691 
692 	Assert(sdr_info->type_id == sdr);
693 	mprintf(("Compiling new shader:\n"));
694 	mprintf(("	%s\n", sdr_info->description));
695 
696 	// figure out if the variant requested needs a geometry shader
697 	bool use_geo_sdr = false;
698 
699 	// do we even have a geometry shader?
700 	if (sdr_info->geo != NULL) {
701 		for (int i = 0; i < GL_num_shader_variants; ++i) {
702 			opengl_shader_variant_t *variant = &GL_shader_variants[i];
703 
704 			if (variant->type_id == sdr && flags & variant->flag && variant->use_geometry_sdr) {
705 				use_geo_sdr = true;
706 				break;
707 			}
708 		}
709 	}
710 
711 	auto vert_content =
712 		opengl_get_shader_content(sdr_info->type_id, sdr_info->vert, flags, use_geo_sdr, sdr_info->spirv_shader);
713 	auto frag_content =
714 		opengl_get_shader_content(sdr_info->type_id, sdr_info->frag, flags, use_geo_sdr, sdr_info->spirv_shader);
715 	SCP_vector<SCP_string> geom_content;
716 
717 	if (use_geo_sdr) {
718 		// read geometry shader
719 		geom_content =
720 			opengl_get_shader_content(sdr_info->type_id, sdr_info->geo, flags, use_geo_sdr, sdr_info->spirv_shader);
721 	}
722 
723 	auto shader_hash = get_shader_hash(vert_content, geom_content, frag_content);
724 	std::unique_ptr<opengl::ShaderProgram> program(new opengl::ShaderProgram(sdr_info->description));
725 
726 	if (!load_cached_shader_binary(program.get(), shader_hash)) {
727 		GR_DEBUG_SCOPE("Compiling shader code");
728 		try {
729 			program->addShaderCode(opengl::STAGE_VERTEX, sdr_info->vert, vert_content);
730 			program->addShaderCode(opengl::STAGE_FRAGMENT, sdr_info->frag, frag_content);
731 			if (use_geo_sdr) {
732 				program->addShaderCode(opengl::STAGE_GEOMETRY, sdr_info->geo, geom_content);
733 			}
734 
735 			for (size_t i = 0; i < GL_vertex_attrib_info.size(); ++i) {
736 				// Check that the enum values match the position in the vector to make accessing that information more efficient
737 				Assertion(GL_vertex_attrib_info[i].attribute_id == (int)i, "Mistmatch between enum values and attribute vector detected!");
738 
739 				// assign vert attribute binding locations before we link the shader
740 				glBindAttribLocation(program->getShaderHandle(), (GLint)i, GL_vertex_attrib_info[i].name.c_str());
741 			}
742 
743 			// bind fragment data locations before we link the shader
744 			glBindFragDataLocation(program->getShaderHandle(), 0, "fragOut0");
745 			glBindFragDataLocation(program->getShaderHandle(), 1, "fragOut1");
746 			glBindFragDataLocation(program->getShaderHandle(), 2, "fragOut2");
747 			glBindFragDataLocation(program->getShaderHandle(), 3, "fragOut3");
748 			glBindFragDataLocation(program->getShaderHandle(), 4, "fragOut4");
749 
750 			if (do_shader_caching()) {
751 				// Enable shader caching
752 				glProgramParameteri(program->getShaderHandle(), GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
753 			}
754 
755 			program->linkProgram();
756 		}
757 		catch (const std::exception&) {
758 			// Since all shaders are required a compilation failure is a fatal error
759 			Error(LOCATION, "A shader failed to compile! Check the debug log for more information.");
760 		}
761 
762 		cache_program_binary(program->getShaderHandle(), shader_hash);
763 	}
764 
765 	new_shader.shader = sdr_info->type_id;
766 	new_shader.flags = flags;
767 	new_shader.program = std::move(program);
768 
769 	opengl_shader_set_current(&new_shader);
770 
771 	// initialize the attributes
772 	for (auto& attr : sdr_info->attributes) {
773 		new_shader.program->initAttribute(GL_vertex_attrib_info[attr].name, GL_vertex_attrib_info[attr].attribute_id, GL_vertex_attrib_info[attr].default_value);
774 	}
775 
776 	for (auto& uniform_block : GL_uniform_blocks) {
777 		auto blockIndex = glGetUniformBlockIndex(new_shader.program->getShaderHandle(), uniform_block.name);
778 
779 		if (blockIndex != GL_INVALID_INDEX) {
780 			glUniformBlockBinding(new_shader.program->getShaderHandle(), blockIndex, static_cast<GLuint>(uniform_block.block_type));
781 		}
782 	}
783 
784 	mprintf(("Shader Variant Features:\n"));
785 
786 	// initialize all uniforms and attributes that are specific to this variant
787 	for (int i = 0; i < GL_num_shader_variants; ++i) {
788 		opengl_shader_variant_t &variant = GL_shader_variants[i];
789 
790 		if (sdr_info->type_id == variant.type_id && variant.flag & flags) {
791 			for (auto& attr : variant.attributes) {
792 				auto& attr_info = GL_vertex_attrib_info[attr];
793 				new_shader.program->initAttribute(attr_info.name, attr_info.attribute_id, attr_info.default_value);
794 			}
795 
796 			mprintf(("	%s\n", variant.description));
797 		}
798 	}
799 
800 	opengl_set_default_uniforms(new_shader);
801 }
802 
803 /**
804  * Compiles a new shader, and creates an opengl_shader_t that will be put into the GL_shader vector
805  * if compilation is successful.
806  *
807  * @param sdr		Identifier defined with the program we wish to compile
808  * @param flags		Combination of SDR_* flags
809  * @param replacement_idx	The index of the shader this replaces. If -1, the newly compiled shader will be appended to the GL_shader vector
810  *					or inserted at the first available empty slot
811  */
opengl_compile_shader(shader_type sdr,uint flags)812 int opengl_compile_shader(shader_type sdr, uint flags)
813 {
814 	GR_DEBUG_SCOPE("Creating new shader");
815 
816 	int sdr_index = -1;
817 	int empty_idx;
818 	opengl_shader_t new_shader;
819 
820 	Assert(sdr < NUM_SHADER_TYPES);
821 
822 	opengl_compile_shader_actual(sdr, flags, new_shader);
823 
824 	opengl_shader_set_current();
825 
826 	// add it to our list of embedded shaders
827 	// see if we have empty shader slots
828 	empty_idx = -1;
829 	for (int i = 0; i < (int)GL_shader.size(); ++i) {
830 		if (GL_shader[i].shader == NUM_SHADER_TYPES) {
831 			empty_idx = i;
832 			break;
833 		}
834 	}
835 
836 	int new_shader_shader = new_shader.shader;
837 	uint32_t new_shader_flags = new_shader.flags;
838 	// then insert it at an empty slot or at the end
839 	if ( empty_idx >= 0 ) {
840 		GL_shader[empty_idx] = std::move(new_shader);
841 		sdr_index = empty_idx;
842 	} else {
843 		sdr_index = (int)GL_shader.size();
844 		GL_shader.push_back(std::move(new_shader));
845 	}
846 	GL_shader_map[shader_descriptor_t(new_shader_shader, new_shader_flags)] = sdr_index;
847 	return sdr_index;
848 }
849 
gr_opengl_recompile_all_shaders(const std::function<void (size_t,size_t)> & progress_callback)850 void gr_opengl_recompile_all_shaders(const std::function<void(size_t, size_t)>& progress_callback)
851 {
852 	for (auto sdr = GL_shader.begin(); sdr != GL_shader.end(); ++sdr)
853 	{
854 		if (progress_callback)
855 			progress_callback(std::distance(GL_shader.begin(), sdr), GL_shader.size());
856 		sdr->program.reset();
857 		opengl_compile_shader_actual(sdr->shader, sdr->flags, *sdr);
858 	}
859 }
860 
opengl_purge_shader_cache_type(const char * ext)861 static void opengl_purge_shader_cache_type(const char* ext) {
862 
863 	SCP_string filter("*.");
864 	filter += ext;
865 
866 	// Previously the cache files were stored in the mod directory. Since we have a better system now, those files
867 	// should be cleaned out. This is only needed if we have a mod directory since otherwise we would delete the actual
868 	// cache files
869 	if (Cmdline_mod != nullptr && strlen(Cmdline_mod) > 0) {
870 		SCP_vector<SCP_string> cache_files;
871 		cf_get_file_list(cache_files, CF_TYPE_CACHE, filter.c_str(), CF_SORT_NONE, nullptr,
872 		                 CF_LOCATION_TYPE_PRIMARY_MOD | CF_LOCATION_TYPE_SECONDARY_MODS);
873 
874 		for (auto& file : cache_files) {
875 			cf_delete((file + "." + ext).c_str(), CF_TYPE_CACHE,
876 			          CF_LOCATION_TYPE_PRIMARY_MOD | CF_LOCATION_TYPE_SECONDARY_MODS);
877 		}
878 	}
879 
880 	SCP_vector<SCP_string> cache_files;
881 	SCP_vector<file_list_info> file_info;
882 	cf_get_file_list(cache_files, CF_TYPE_CACHE, filter.c_str(), CF_SORT_NONE, &file_info,
883 	                 CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
884 
885 	Assertion(cache_files.size() == file_info.size(),
886 			  "cf_get_file_list returned different sizes for file names and file informations!");
887 
888 	const auto TIMEOUT = 2.0 * 30.0 * 24.0 * 60.0 * 60.0; // purge timeout in seconds which is ~2 months
889 	const SCP_string PREFIX = "ogl_shader-";
890 
891 	auto now = std::time(nullptr);
892 	for (size_t i = 0; i < cache_files.size(); ++i) {
893 		auto& name = cache_files[i];
894 		auto write_time = file_info[i].write_time;
895 
896 		if (name.compare(0, PREFIX.size(), PREFIX) != 0) {
897 			// Not an OpenGL cache file
898 			continue;
899 		}
900 
901 		auto diff = std::difftime(now, write_time);
902 
903 		if (diff > TIMEOUT) {
904 			auto full_name = name + "." + ext;
905 
906 			cf_delete(full_name.c_str(), CF_TYPE_CACHE);
907 		}
908 	}
909 }
910 
opengl_purge_old_shader_cache()911 static void opengl_purge_old_shader_cache()
912 {
913 	opengl_purge_shader_cache_type("json");
914 	opengl_purge_shader_cache_type("bin");
915 }
916 
917 /**
918  * Initializes the shader system. Creates a 1x1 texture that can be used as a fallback texture when framebuffer support is missing.
919  * Also compiles the shaders used for particle rendering.
920  */
opengl_shader_init()921 void opengl_shader_init()
922 {
923 	glGenTextures(1,&Framebuffer_fallback_texture_id);
924 	GL_state.Texture.SetActiveUnit(0);
925 	GL_state.Texture.SetTarget(GL_TEXTURE_2D);
926 	GL_state.Texture.Enable(Framebuffer_fallback_texture_id);
927 
928 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
929 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
930 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
931 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
932 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
933 	GLuint pixels[4] = {0,0,0,0};
934 	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &pixels);
935 
936 	GL_shader.clear();
937 	GL_shader_map.clear();
938 
939 	// Reserve 32 shader slots. This should cover most use cases in real life.
940 	GL_shader.reserve(32);
941 
942 	opengl_purge_old_shader_cache();
943 
944 	// compile effect shaders
945 	gr_opengl_maybe_create_shader(SDR_TYPE_EFFECT_PARTICLE, 0);
946 	gr_opengl_maybe_create_shader(SDR_TYPE_EFFECT_PARTICLE, SDR_FLAG_PARTICLE_POINT_GEN);
947 	gr_opengl_maybe_create_shader(SDR_TYPE_EFFECT_DISTORTION, 0);
948 
949 	gr_opengl_maybe_create_shader(SDR_TYPE_SHIELD_DECAL, 0);
950 
951 	// compile deferred lighting shaders
952 	gr_opengl_maybe_create_shader(SDR_TYPE_DEFERRED_LIGHTING, 0);
953 	gr_opengl_maybe_create_shader(SDR_TYPE_DEFERRED_CLEAR, 0);
954 
955 	// compile passthrough shader
956 	mprintf(("Compiling passthrough shader...\n"));
957 	gr_opengl_maybe_create_shader(SDR_TYPE_PASSTHROUGH_RENDER, 0);
958 
959 	mprintf(("\n"));
960 }
961 
962 /**
963  * Get the internal OpenGL location for a given attribute. Requires that the Current_shader global variable is valid
964  *
965  * @param attribute_text	Name of the attribute
966  * @return					Internal OpenGL location for the attribute
967  */
opengl_shader_get_attribute(opengl_vert_attrib::attrib_id attribute)968 GLint opengl_shader_get_attribute(opengl_vert_attrib::attrib_id attribute)
969 {
970 	Assertion(Current_shader != nullptr, "Current shader may not be null!");
971 
972 	return Current_shader->program->getAttributeLocation(attribute);
973 }
974 
opengl_shader_set_passthrough(bool textured,bool hdr)975 void opengl_shader_set_passthrough(bool textured, bool hdr)
976 {
977 	opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_PASSTHROUGH_RENDER, 0));
978 
979 	gr_matrix_set_uniforms();
980 
981 	opengl_set_generic_uniform_data<graphics::generic_data::passthrough_data>(
982 		[=](graphics::generic_data::passthrough_data* data) {
983 			data->noTexturing = textured ? 0 : 1;
984 			data->srgb        = hdr ? 1 : 0;
985 		});
986 }
987 
opengl_shader_set_default_material(bool textured,bool alpha,vec4 * clr,float color_scale,uint32_t array_index,const material::clip_plane & clip_plane)988 void opengl_shader_set_default_material(bool textured, bool alpha, vec4* clr, float color_scale, uint32_t array_index,
989 										const material::clip_plane& clip_plane)
990 {
991 	Current_shader->program->Uniforms.setTextureUniform("baseMap", 0);
992 
993 	opengl_set_generic_uniform_data<genericData_default_material_vert>(
994 		[=](genericData_default_material_vert* data) {
995 			if (textured) {
996 				data->noTexturing  = 0;
997 				data->baseMapIndex = array_index;
998 			} else {
999 				data->noTexturing = 1;
1000 				// array_index is probably not valid here
1001 				data->baseMapIndex = 0;
1002 			}
1003 
1004 			if (alpha) {
1005 				data->alphaTexture = 1;
1006 			} else {
1007 				data->alphaTexture = 0;
1008 			}
1009 
1010 			if (High_dynamic_range) {
1011 				data->srgb      = 1;
1012 				data->intensity = color_scale;
1013 			} else {
1014 				data->srgb      = 0;
1015 				data->intensity = 1.0f;
1016 			}
1017 
1018 			data->alphaThreshold = GL_alpha_threshold;
1019 
1020 			if (clr != nullptr) {
1021 				data->color = *clr;
1022 			} else {
1023 				data->color.xyzw.x = 1.0f;
1024 				data->color.xyzw.y = 1.0f;
1025 				data->color.xyzw.z = 1.0f;
1026 				data->color.xyzw.w = 1.0f;
1027 			}
1028 
1029 			if (clip_plane.enabled) {
1030 				data->clipEnabled = 1;
1031 
1032 				vec4 clip_equation;
1033 				clip_equation.xyzw.x = clip_plane.normal.xyz.x;
1034 				clip_equation.xyzw.y = clip_plane.normal.xyz.y;
1035 				clip_equation.xyzw.z = clip_plane.normal.xyz.z;
1036 				clip_equation.xyzw.w = -vm_vec_dot(&clip_plane.normal, &clip_plane.position);
1037 
1038 				data->clipEquation = clip_equation;
1039 				data->modelMatrix  = gr_model_matrix_stack.get_transform();
1040 			} else {
1041 				data->clipEnabled = 0;
1042 			}
1043 		});
1044 
1045 	gr_matrix_set_uniforms();
1046 }
1047