1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2014-2016, The OpenClonk Team and contributors
5  *
6  * Distributed under the terms of the ISC license; see accompanying file
7  * "COPYING" for details.
8  *
9  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
10  * See accompanying file "TRADEMARK" for details.
11  *
12  * To redistribute this file separately, substitute the full license texts
13  * for the above references.
14  */
15 
16 // Shader implementation somewhere in the middle between easy and extensible.
17 
18 #ifndef INC_C4Shader
19 #define INC_C4Shader
20 
21 #include "C4ForbidLibraryCompilation.h"
22 #include "lib/StdMeshMath.h"
23 #include "graphics/C4Surface.h"
24 
25 #ifdef _WIN32
26 #include "platform/C4windowswrapper.h"
27 #endif
28 
29 #ifndef USE_CONSOLE
30 #include <GL/glew.h>
31 #endif
32 
33 #include <stack>
34 
35 // Shader version
36 const int C4Shader_Version = 150; // GLSL 1.50 / OpenGL 3.2
37 
38 // Maximum number of texture coordinates
39 const int C4Shader_MaxTexCoords = 8;
40 
41 // Maximum number of texture units per shader call
42 const int C4ShaderCall_MaxUnits = 32;
43 
44 // Positions in fragment shader
45 const int C4Shader_PositionInit = 0;
46 const int C4Shader_PositionCoordinate = 20;
47 const int C4Shader_PositionTexture = 40;
48 const int C4Shader_PositionMaterial = 60;
49 const int C4Shader_PositionNormal = 80;
50 const int C4Shader_PositionLight = 100;
51 const int C4Shader_PositionColor = 120;
52 const int C4Shader_PositionFinish = 140;
53 const int C4Shader_LastPosition = 256;
54 
55 // Positions in vertex shader
56 const int C4Shader_Vertex_TexCoordPos = 50;
57 const int C4Shader_Vertex_NormalPos = 60;
58 const int C4Shader_Vertex_ColorPos = 70;
59 const int C4Shader_Vertex_PositionPos = 80;
60 
61 class C4Shader
62 {
63 	friend class C4ShaderCall;
64 	friend class C4ScriptUniform;
65 public:
66 	C4Shader();
67 	~C4Shader();
68 
69 private:
70 
71 	StdStrBuf Name;
72 
73 	// Program texts
74 	struct ShaderSlice {
75 		int Position;
76 		StdCopyStrBuf Text;
77 		StdCopyStrBuf Source;
78 		int SourceLine;
79 		int SourceTime;
80 	};
81 	typedef std::list<ShaderSlice> ShaderSliceList;
82 	ShaderSliceList VertexSlices, FragmentSlices;
83 	std::vector<std::string> SourceFiles;
84 	std::vector<std::string> Categories;
85 	std::set<int> ScriptShaders;
86 
87 	int GetSourceFileId(const char *file) const;
88 
89 	// Last refresh check
90 	C4TimeMilliseconds LastRefresh;
91 	bool ScriptSlicesLoaded = false;
92 
93 	// Used texture coordinates
94 	int iTexCoords{0};
95 
96 #ifndef USE_CONSOLE
97 	// shaders
98 	GLuint hProg{0};
99 	// shader variables
100 	struct Variable { int address; const char* name; };
101 	std::vector<Variable> Uniforms;
102 	std::vector<Variable> Attributes;
103 #endif
104 
105 public:
Initialised()106 	bool Initialised() const
107 	{
108 #ifndef USE_CONSOLE
109 		return hProg != 0;
110 #else
111 		return true;
112 #endif
113 	}
114 
115 	// Uniform getters
116 #ifndef USE_CONSOLE
GetUniform(int iUniform)117 	GLint GetUniform(int iUniform) const
118 	{
119 		return iUniform >= 0 && static_cast<unsigned int>(iUniform) < Uniforms.size() ? Uniforms[iUniform].address : -1;
120 	}
121 
HaveUniform(int iUniform)122 	bool HaveUniform(int iUniform) const
123 	{
124 		return GetUniform(iUniform) != GLint(-1);
125 	}
126 
GetAttribute(int iAttribute)127 	GLint GetAttribute(int iAttribute) const
128 	{
129 		return iAttribute >= 0 && static_cast<unsigned int>(iAttribute) < Attributes.size() ? Attributes[iAttribute].address : -1;
130 	}
131 
132 #else
GetUniform(int iUniform)133 	int GetUniform(int iUniform) const
134 	{
135 		return -1;
136 	}
HaveUniform(int iUniform)137 	bool HaveUniform(int iUniform) const
138 	{
139 		return false;
140 	}
GetAttribute(int iAttribute)141 	int GetAttribute(int iAttribute) const
142 	{
143 		return -1;
144 	}
145 #endif
146 
147 	// Shader is composed from various slices
148 	void AddDefine(const char* name);
149 	void AddVertexSlice(int iPos, const char *szText);
150 	void AddFragmentSlice(int iPos, const char *szText);
151 	void AddVertexSlices(const char *szWhat, const char *szText, const char *szSource = "", int iFileTime = 0);
152 	void AddFragmentSlices(const char *szWhat, const char *szText, const char *szSource = "", int iFileTime = 0);
153 	bool LoadFragmentSlices(C4GroupSet *pGroupSet, const char *szFile);
154 	bool LoadVertexSlices(C4GroupSet *pGroupSet, const char *szFile);
155 	void SetScriptCategories(const std::vector<std::string>& categories);
156 
157 	// Assemble and link the shader. Should be called again after new slices are added.
158 	bool Init(const char *szWhat, const char **szUniforms, const char **szAttributes);
159 	bool Refresh();
160 
161 	void ClearSlices();
162 	void Clear();
163 
164 private:
165 	void AddSlice(ShaderSliceList& slices, int iPos, const char *szText, const char *szSource, int line, int iFileTime);
166 	void AddSlices(ShaderSliceList& slices, const char *szWhat, const char *szText, const char *szSource, int iFileTime);
167 	bool LoadSlices(ShaderSliceList& slices, C4GroupSet *pGroupSet, const char *szFile);
168 	int ParsePosition(const char *szWhat, const char **ppPos);
169 
170 	void LoadScriptSlices();
171 	void LoadScriptSlice(int id);
172 
173 	StdStrBuf Build(const ShaderSliceList &Slices, bool fDebug = false);
174 
175 #ifndef USE_CONSOLE
176 	GLuint Create(GLenum iShaderType, const char *szWhat, const char *szShader);
177 	void DumpInfoLog(const char *szWhat, GLuint hShader, bool forProgram);
178 #endif
179 
180 public:
181 	static bool IsLogging();
182 };
183 
184 #ifndef USE_CONSOLE
185 class C4ShaderCall
186 {
187 	friend class C4ScriptUniform;
188 public:
C4ShaderCall(const C4Shader * pShader)189 	C4ShaderCall(const C4Shader *pShader)
190 		: fStarted(false), pShader(pShader), iUnits(0)
191 	{ }
~C4ShaderCall()192 	~C4ShaderCall() { Finish(); }
193 
GetAttribute(int iAttribute)194 	GLint GetAttribute(int iAttribute) const
195 	{
196 		return pShader->GetAttribute(iAttribute);
197 	}
198 
199 private:
200 	bool fStarted;
201 	const C4Shader *pShader;
202 	int iUnits;
203 
204 public:
205 	GLint AllocTexUnit(int iUniform);
206 
207 	// Setting uniforms... Lots of code duplication here, not quite sure whether
208 	// something could be done about it.
SetUniform1i(int iUniform,int iX)209 	void SetUniform1i(int iUniform, int iX) const {
210 		if (pShader->HaveUniform(iUniform))
211 			glUniform1i(pShader->GetUniform(iUniform), iX);
212 	}
SetUniform2i(int iUniform,int iX,int iY)213 	void SetUniform2i(int iUniform, int iX, int iY) const {
214 		if (pShader->HaveUniform(iUniform))
215 			glUniform2i(pShader->GetUniform(iUniform), iX, iY);
216 	}
SetUniform3i(int iUniform,int iX,int iY,int iZ)217 	void SetUniform3i(int iUniform, int iX, int iY, int iZ) const {
218 		if (pShader->HaveUniform(iUniform))
219 			glUniform3i(pShader->GetUniform(iUniform), iX, iY, iZ);
220 	}
SetUniform4i(int iUniform,int iX,int iY,int iZ,int iW)221 	void SetUniform4i(int iUniform, int iX, int iY, int iZ, int iW) const {
222 		if (pShader->HaveUniform(iUniform))
223 			glUniform4i(pShader->GetUniform(iUniform), iX, iY, iZ, iW);
224 	}
SetUniform1ui(int iUniform,unsigned int iX)225 	void SetUniform1ui(int iUniform, unsigned int iX) const {
226 		if (pShader->HaveUniform(iUniform))
227 			glUniform1ui(pShader->GetUniform(iUniform), iX);
228 	}
Setuniform2ui(int iUniform,unsigned int iX,unsigned int iY)229 	void Setuniform2ui(int iUniform, unsigned int iX, unsigned int iY) const {
230 		if (pShader->HaveUniform(iUniform))
231 			glUniform2ui(pShader->GetUniform(iUniform), iX, iY);
232 	}
Setuniform3ui(int iUniform,unsigned int iX,unsigned int iY,unsigned int iZ)233 	void Setuniform3ui(int iUniform, unsigned int iX, unsigned int iY, unsigned int iZ) const {
234 		if (pShader->HaveUniform(iUniform))
235 			glUniform3ui(pShader->GetUniform(iUniform), iX, iY, iZ);
236 	}
Setuniform4ui(int iUniform,unsigned int iX,unsigned int iY,unsigned int iZ,unsigned int iW)237 	void Setuniform4ui(int iUniform, unsigned int iX, unsigned int iY, unsigned int iZ, unsigned int iW) const {
238 		if (pShader->HaveUniform(iUniform))
239 			glUniform4ui(pShader->GetUniform(iUniform), iX, iY, iZ, iW);
240 	}
SetUniform1f(int iUniform,float gX)241 	void SetUniform1f(int iUniform, float gX) const {
242 		if (pShader->HaveUniform(iUniform))
243 			glUniform1f(pShader->GetUniform(iUniform), gX);
244 	}
SetUniform2f(int iUniform,float gX,float gY)245 	void SetUniform2f(int iUniform, float gX, float gY) const {
246 		if (pShader->HaveUniform(iUniform))
247 			glUniform2f(pShader->GetUniform(iUniform), gX, gY);
248 	}
SetUniform3f(int iUniform,float gX,float gY,float gZ)249 	void SetUniform3f(int iUniform, float gX, float gY, float gZ) const {
250 		if (pShader->HaveUniform(iUniform))
251 			glUniform3f(pShader->GetUniform(iUniform), gX, gY, gZ);
252 	}
SetUniform4f(int iUniform,float gX,float gY,float gZ,float gW)253 	void SetUniform4f(int iUniform, float gX, float gY, float gZ, float gW) const {
254 		if (pShader->HaveUniform(iUniform))
255 			glUniform4f(pShader->GetUniform(iUniform), gX, gY, gZ, gW);
256 	}
SetUniform1iv(int iUniform,int iLength,const int * pVals)257 	void SetUniform1iv(int iUniform, int iLength, const int *pVals) const {
258 		if (pShader->HaveUniform(iUniform))
259 			glUniform1iv(pShader->GetUniform(iUniform), iLength, pVals);
260 	}
SetUniform2iv(int iUniform,int iLength,const int * pVals)261 	void SetUniform2iv(int iUniform, int iLength, const int *pVals) const {
262 		if (pShader->HaveUniform(iUniform))
263 			glUniform2iv(pShader->GetUniform(iUniform), iLength, pVals);
264 	}
SetUniform3iv(int iUniform,int iLength,const int * pVals)265 	void SetUniform3iv(int iUniform, int iLength, const int *pVals) const {
266 		if (pShader->HaveUniform(iUniform))
267 			glUniform3iv(pShader->GetUniform(iUniform), iLength, pVals);
268 	}
SetUniform4iv(int iUniform,int iLength,const int * pVals)269 	void SetUniform4iv(int iUniform, int iLength, const int *pVals) const {
270 		if (pShader->HaveUniform(iUniform))
271 			glUniform4iv(pShader->GetUniform(iUniform), iLength, pVals);
272 	}
SetUniform1uiv(int iUniform,int iLength,const unsigned int * pVals)273 	void SetUniform1uiv(int iUniform, int iLength, const unsigned int *pVals) const {
274 		if (pShader->HaveUniform(iUniform))
275 			glUniform1uiv(pShader->GetUniform(iUniform), iLength, pVals);
276 	}
SetUniform2uiv(int iUniform,int iLength,const unsigned int * pVals)277 	void SetUniform2uiv(int iUniform, int iLength, const unsigned int *pVals) const {
278 		if (pShader->HaveUniform(iUniform))
279 			glUniform2uiv(pShader->GetUniform(iUniform), iLength, pVals);
280 	}
SetUniform3uiv(int iUniform,int iLength,const unsigned int * pVals)281 	void SetUniform3uiv(int iUniform, int iLength, const unsigned int *pVals) const {
282 		if (pShader->HaveUniform(iUniform))
283 			glUniform3uiv(pShader->GetUniform(iUniform), iLength, pVals);
284 	}
SetUniform4uiv(int iUniform,int iLength,const unsigned int * pVals)285 	void SetUniform4uiv(int iUniform, int iLength, const unsigned int *pVals) const {
286 		if (pShader->HaveUniform(iUniform))
287 			glUniform4uiv(pShader->GetUniform(iUniform), iLength, pVals);
288 	}
SetUniform1fv(int iUniform,int iLength,const float * pVals)289 	void SetUniform1fv(int iUniform, int iLength, const float *pVals) const {
290 		if (pShader->HaveUniform(iUniform))
291 			glUniform1fv(pShader->GetUniform(iUniform), iLength, pVals);
292 	}
SetUniform2fv(int iUniform,int iLength,const float * pVals)293 	void SetUniform2fv(int iUniform, int iLength, const float *pVals) const {
294 		if (pShader->HaveUniform(iUniform))
295 			glUniform2fv(pShader->GetUniform(iUniform), iLength, pVals);
296 	}
SetUniform3fv(int iUniform,int iLength,const float * pVals)297 	void SetUniform3fv(int iUniform, int iLength, const float *pVals) const {
298 		if (pShader->HaveUniform(iUniform))
299 			glUniform3fv(pShader->GetUniform(iUniform), iLength, pVals);
300 	}
SetUniform4fv(int iUniform,int iLength,const float * pVals)301 	void SetUniform4fv(int iUniform, int iLength, const float *pVals) const {
302 		if (pShader->HaveUniform(iUniform))
303 			glUniform4fv(pShader->GetUniform(iUniform), iLength, pVals);
304 	}
305 
306 	// Matrices are in row-major order
SetUniformMatrix2x3fv(int iUniform,int iLength,const float * pVals)307 	void SetUniformMatrix2x3fv(int iUniform, int iLength, const float* pVals) const {
308 		if (pShader->HaveUniform(iUniform))
309 			glUniformMatrix3x2fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals);
310 	}
311 
SetUniformMatrix3x3fv(int iUniform,int iLength,const float * pVals)312 	void SetUniformMatrix3x3fv(int iUniform, int iLength, const float *pVals) const {
313 		if (pShader->HaveUniform(iUniform))
314 			glUniformMatrix3fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals);
315 	}
316 
SetUniformMatrix3x4fv(int iUniform,int iLength,const float * pVals)317 	void SetUniformMatrix3x4fv(int iUniform, int iLength, const float *pVals) const {
318 		if (pShader->HaveUniform(iUniform))
319 			glUniformMatrix4x3fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals);
320 	}
321 
SetUniformMatrix4x4fv(int iUniform,int iLength,const float * pVals)322 	void SetUniformMatrix4x4fv(int iUniform, int iLength, const float* pVals) const {
323 		if (pShader->HaveUniform(iUniform))
324 			glUniformMatrix4fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals);
325 	}
326 
SetUniformMatrix3x3(int iUniform,const StdMeshMatrix & matrix)327 	void SetUniformMatrix3x3(int iUniform, const StdMeshMatrix& matrix)
328 	{
329 		if (pShader->HaveUniform(iUniform))
330 		{
331 			const float mat[9] = { matrix(0, 0), matrix(1, 0), matrix(2, 0), matrix(0, 1), matrix(1, 1), matrix(2, 1), matrix(0, 2), matrix(1, 2), matrix(2, 2) };
332 			glUniformMatrix3fv(pShader->GetUniform(iUniform), 1, GL_FALSE, mat);
333 		}
334 	}
335 
SetUniformMatrix3x3Transpose(int iUniform,const StdMeshMatrix & matrix)336 	void SetUniformMatrix3x3Transpose(int iUniform, const StdMeshMatrix& matrix)
337 	{
338 		if (pShader->HaveUniform(iUniform))
339 		{
340 			const float mat[9] = { matrix(0, 0), matrix(0, 1), matrix(0, 2), matrix(1, 0), matrix(1, 1), matrix(1, 2), matrix(2, 0), matrix(2, 1), matrix(2, 2) };
341 			glUniformMatrix3fv(pShader->GetUniform(iUniform), 1, GL_FALSE, mat);
342 		}
343 	}
344 
SetUniformMatrix3x4(int iUniform,const StdMeshMatrix & matrix)345 	void SetUniformMatrix3x4(int iUniform, const StdMeshMatrix& matrix)
346 	{
347 		if (pShader->HaveUniform(iUniform))
348 			glUniformMatrix4x3fv(pShader->GetUniform(iUniform), 1, GL_TRUE, matrix.data());
349 	}
350 
SetUniformMatrix4x4(int iUniform,const StdMeshMatrix & matrix)351 	void SetUniformMatrix4x4(int iUniform, const StdMeshMatrix& matrix)
352 	{
353 		if (pShader->HaveUniform(iUniform))
354 		{
355 			const float mat[16] = { matrix(0, 0), matrix(1, 0), matrix(2, 0), 0.0f, matrix(0, 1), matrix(1, 1), matrix(2, 1), 0.0f, matrix(0, 2), matrix(1, 2), matrix(2, 2), 0.0f, matrix(0, 3), matrix(1, 3), matrix(2, 3), 1.0f };
356 			glUniformMatrix4fv(pShader->GetUniform(iUniform), 1, GL_FALSE, mat);
357 		}
358 	}
359 
SetUniformMatrix4x4(int iUniform,const StdProjectionMatrix & matrix)360 	void SetUniformMatrix4x4(int iUniform, const StdProjectionMatrix& matrix)
361 	{
362 		if (pShader->HaveUniform(iUniform))
363 			glUniformMatrix4fv(pShader->GetUniform(iUniform), 1, GL_TRUE, matrix.data());
364 	}
365 
366 	void Start();
367 	void Finish();
368 };
369 #else // USE_CONSOLE
370 class C4ShaderCall {
371 	public:
C4ShaderCall(const C4Shader *)372 	C4ShaderCall(const C4Shader *) {};
373 };
374 #endif
375 
376 class C4ScriptShader
377 {
378 	friend class C4Shader;
379 	friend class C4ShaderCall;
380 
381 public:
382 	enum ShaderType
383 	{
384 		VertexShader, // Note: Reloading is currently only implemented for fragment shaders.
385 		FragmentShader,
386 	};
387 private:
388 	struct ShaderInstance
389 	{
390 		ShaderType type;
391 		std::string source;
392 	};
393 
394 	// Map of shader names -> ids. The indirection is there as each C4Shader
395 	// may load script shaders from multiple categories.
396 	std::map<std::string, std::set<int>> categories;
397 	// Map of ids -> script-loaded shaders.
398 	std::map<int, ShaderInstance> shaders;
399 	int NextID = 0;
400 	uint32_t LastUpdate = 0;
401 
402 protected: // Interface for C4Shader friend class
403 	std::set<int> GetShaderIDs(const std::vector<std::string>& cats);
404 
405 public: // Interface for script
406 	// Adds a shader, returns its id for removal.
407 	int Add(const std::string& shaderName, ShaderType type, const std::string& source);
408 	// Removes a shader, returning true on success.
409 	bool Remove(int id);
410 };
411 
412 extern C4ScriptShader ScriptShader;
413 
414 class C4ScriptUniform
415 {
416 	friend class C4Shader;
417 
418 	struct Uniform
419 	{
420 #ifndef USE_CONSOLE
421 		GLenum type;
422 		union
423 		{
424 			int intVec[4];
425 			// TODO: Support for other uniform types.
426 		};
427 #endif
428 	};
429 
430 	typedef std::map<std::string, Uniform> UniformMap;
431 	std::stack<UniformMap> uniformStack;
432 
433 public:
434 	class Popper
435 	{
436 		C4ScriptUniform* p;
437 		size_t size;
438 	public:
Popper(C4ScriptUniform * p)439 		Popper(C4ScriptUniform* p) : p(p), size(p->uniformStack.size()) { }
~Popper()440 		~Popper() { assert(size == p->uniformStack.size()); p->uniformStack.pop(); }
441 	};
442 
443 	// Remove all uniforms.
444 	void Clear();
445 	// Walk the proplist `proplist.Uniforms` and add uniforms. Automatically pops when the return value is destroyed.
446 	std::unique_ptr<Popper> Push(C4PropList* proplist);
447 	// Apply uniforms to a shader call.
448 	void Apply(C4ShaderCall& call);
449 
C4ScriptUniform()450 	C4ScriptUniform() { Clear(); }
451 };
452 
453 #endif // INC_C4Shader
454