OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4Shader.h
Go to the documentation of this file.
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 
22 #include "lib/StdBuf.h"
23 #include "lib/StdMeshMath.h"
24 #include "graphics/C4Surface.h"
25 
26 #ifdef _WIN32
28 #endif
29 
30 #ifndef USE_CONSOLE
31 #include <GL/glew.h>
32 #endif
33 
34 #include <stack>
35 
36 // Shader version
37 const int C4Shader_Version = 150; // GLSL 1.50 / OpenGL 3.2
38 
39 // Maximum number of texture coordinates
40 const int C4Shader_MaxTexCoords = 8;
41 
42 // Maximum number of texture units per shader call
43 const int C4ShaderCall_MaxUnits = 32;
44 
45 // Positions in fragment shader
46 const int C4Shader_PositionInit = 0;
48 const int C4Shader_PositionTexture = 40;
50 const int C4Shader_PositionNormal = 80;
51 const int C4Shader_PositionLight = 100;
52 const int C4Shader_PositionColor = 120;
53 const int C4Shader_PositionFinish = 140;
54 const int C4Shader_LastPosition = 256;
55 
56 // Positions in vertex shader
59 const int C4Shader_Vertex_ColorPos = 70;
61 
62 class C4Shader
63 {
64  friend class C4ShaderCall;
65  friend class C4ScriptUniform;
66 public:
67  C4Shader();
68  ~C4Shader();
69 
70 private:
71 
72  StdStrBuf Name;
73 
74  // Program texts
75  struct ShaderSlice {
76  int Position;
77  StdCopyStrBuf Text;
78  StdCopyStrBuf Source;
79  int SourceLine;
80  int SourceTime;
81  };
82  typedef std::list<ShaderSlice> ShaderSliceList;
83  ShaderSliceList VertexSlices, FragmentSlices;
84  std::vector<std::string> SourceFiles;
85  std::vector<std::string> Categories;
86  std::set<int> ScriptShaders;
87 
88  int GetSourceFileId(const char *file) const;
89 
90  // Last refresh check
91  C4TimeMilliseconds LastRefresh;
92  bool ScriptSlicesLoaded = false;
93 
94  // Used texture coordinates
95  int iTexCoords;
96 
97 #ifndef USE_CONSOLE
98  // shaders
99  GLuint hProg;
100  // shader variables
101  struct Variable { int address; const char* name; };
102  std::vector<Variable> Uniforms;
103  std::vector<Variable> Attributes;
104 #endif
105 
106 public:
107  bool Initialised() const
108  {
109 #ifndef USE_CONSOLE
110  return hProg != 0;
111 #else
112  return true;
113 #endif
114  }
115 
116  // Uniform getters
117 #ifndef USE_CONSOLE
118  GLint GetUniform(int iUniform) const
119  {
120  return iUniform >= 0 && static_cast<unsigned int>(iUniform) < Uniforms.size() ? Uniforms[iUniform].address : -1;
121  }
122 
123  bool HaveUniform(int iUniform) const
124  {
125  return GetUniform(iUniform) != GLint(-1);
126  }
127 
128  GLint GetAttribute(int iAttribute) const
129  {
130  return iAttribute >= 0 && static_cast<unsigned int>(iAttribute) < Attributes.size() ? Attributes[iAttribute].address : -1;
131  }
132 
133 #else
134  int GetUniform(int iUniform) const
135  {
136  return -1;
137  }
138  bool HaveUniform(int iUniform) const
139  {
140  return false;
141  }
142  int GetAttribute(int iAttribute) const
143  {
144  return -1;
145  }
146 #endif
147 
148  // Shader is composed from various slices
149  void AddDefine(const char* name);
150  void AddVertexSlice(int iPos, const char *szText);
151  void AddFragmentSlice(int iPos, const char *szText);
152  void AddVertexSlices(const char *szWhat, const char *szText, const char *szSource = "", int iFileTime = 0);
153  void AddFragmentSlices(const char *szWhat, const char *szText, const char *szSource = "", int iFileTime = 0);
154  bool LoadFragmentSlices(C4GroupSet *pGroupSet, const char *szFile);
155  bool LoadVertexSlices(C4GroupSet *pGroupSet, const char *szFile);
156  void SetScriptCategories(const std::vector<std::string>& categories);
157 
158  // Assemble and link the shader. Should be called again after new slices are added.
159  bool Init(const char *szWhat, const char **szUniforms, const char **szAttributes);
160  bool Refresh();
161 
162  void ClearSlices();
163  void Clear();
164 
165 private:
166  void AddSlice(ShaderSliceList& slices, int iPos, const char *szText, const char *szSource, int line, int iFileTime);
167  void AddSlices(ShaderSliceList& slices, const char *szWhat, const char *szText, const char *szSource, int iFileTime);
168  bool LoadSlices(ShaderSliceList& slices, C4GroupSet *pGroupSet, const char *szFile);
169  int ParsePosition(const char *szWhat, const char **ppPos);
170 
171  void LoadScriptSlices();
172  void LoadScriptSlice(int id);
173 
174  StdStrBuf Build(const ShaderSliceList &Slices, bool fDebug = false);
175 
176 #ifndef USE_CONSOLE
177  GLuint Create(GLenum iShaderType, const char *szWhat, const char *szShader);
178  void DumpInfoLog(const char *szWhat, GLuint hShader, bool forProgram);
179 #endif
180 
181 public:
182  static bool IsLogging();
183 };
184 
185 #ifndef USE_CONSOLE
187 {
188  friend class C4ScriptUniform;
189 public:
190  C4ShaderCall(const C4Shader *pShader)
191  : fStarted(false), pShader(pShader), iUnits(0)
192  { }
194 
195  GLint GetAttribute(int iAttribute) const
196  {
197  return pShader->GetAttribute(iAttribute);
198  }
199 
200 private:
201  bool fStarted;
202  const C4Shader *pShader;
203  int iUnits;
204 
205 public:
206  GLint AllocTexUnit(int iUniform);
207 
208  // Setting uniforms... Lots of code duplication here, not quite sure whether
209  // something could be done about it.
210  void SetUniform1i(int iUniform, int iX) const {
211  if (pShader->HaveUniform(iUniform))
212  glUniform1i(pShader->GetUniform(iUniform), iX);
213  }
214  void SetUniform1f(int iUniform, float gX) const {
215  if (pShader->HaveUniform(iUniform))
216  glUniform1f(pShader->GetUniform(iUniform), gX);
217  }
218  void SetUniform2f(int iUniform, float gX, float gY) const {
219  if (pShader->HaveUniform(iUniform))
220  glUniform2f(pShader->GetUniform(iUniform), gX, gY);
221  }
222  void SetUniform1iv(int iUniform, int iLength, const int *pVals) const {
223  if (pShader->HaveUniform(iUniform))
224  glUniform1iv(pShader->GetUniform(iUniform), iLength, pVals);
225  }
226  void SetUniform1fv(int iUniform, int iLength, const float *pVals) const {
227  if (pShader->HaveUniform(iUniform))
228  glUniform1fv(pShader->GetUniform(iUniform), iLength, pVals);
229  }
230  void SetUniform2fv(int iUniform, int iLength, const float *pVals) const {
231  if (pShader->HaveUniform(iUniform))
232  glUniform2fv(pShader->GetUniform(iUniform), iLength, pVals);
233  }
234  void SetUniform3fv(int iUniform, int iLength, const float *pVals) const {
235  if (pShader->HaveUniform(iUniform))
236  glUniform3fv(pShader->GetUniform(iUniform), iLength, pVals);
237  }
238  void SetUniform4fv(int iUniform, int iLength, const float *pVals) const {
239  if (pShader->HaveUniform(iUniform))
240  glUniform4fv(pShader->GetUniform(iUniform), iLength, pVals);
241  }
242 
243  // Matrices are in row-major order
244  void SetUniformMatrix2x3fv(int iUniform, int iLength, const float* pVals) const {
245  if (pShader->HaveUniform(iUniform))
246  glUniformMatrix3x2fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals);
247  }
248 
249  void SetUniformMatrix3x3fv(int iUniform, int iLength, const float *pVals) const {
250  if (pShader->HaveUniform(iUniform))
251  glUniformMatrix3fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals);
252  }
253 
254  void SetUniformMatrix3x4fv(int iUniform, int iLength, const float *pVals) const {
255  if (pShader->HaveUniform(iUniform))
256  glUniformMatrix4x3fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals);
257  }
258 
259  void SetUniformMatrix4x4fv(int iUniform, int iLength, const float* pVals) const {
260  if (pShader->HaveUniform(iUniform))
261  glUniformMatrix4fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals);
262  }
263 
264  void SetUniformMatrix3x3(int iUniform, const StdMeshMatrix& matrix)
265  {
266  if (pShader->HaveUniform(iUniform))
267  {
268  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) };
269  glUniformMatrix3fv(pShader->GetUniform(iUniform), 1, GL_FALSE, mat);
270  }
271  }
272 
273  void SetUniformMatrix3x3Transpose(int iUniform, const StdMeshMatrix& matrix)
274  {
275  if (pShader->HaveUniform(iUniform))
276  {
277  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) };
278  glUniformMatrix3fv(pShader->GetUniform(iUniform), 1, GL_FALSE, mat);
279  }
280  }
281 
282  void SetUniformMatrix3x4(int iUniform, const StdMeshMatrix& matrix)
283  {
284  if (pShader->HaveUniform(iUniform))
285  glUniformMatrix4x3fv(pShader->GetUniform(iUniform), 1, GL_TRUE, matrix.data());
286  }
287 
288  void SetUniformMatrix4x4(int iUniform, const StdMeshMatrix& matrix)
289  {
290  if (pShader->HaveUniform(iUniform))
291  {
292  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 };
293  glUniformMatrix4fv(pShader->GetUniform(iUniform), 1, GL_FALSE, mat);
294  }
295  }
296 
297  void SetUniformMatrix4x4(int iUniform, const StdProjectionMatrix& matrix)
298  {
299  if (pShader->HaveUniform(iUniform))
300  glUniformMatrix4fv(pShader->GetUniform(iUniform), 1, GL_TRUE, matrix.data());
301  }
302 
303  void Start();
304  void Finish();
305 };
306 #else // USE_CONSOLE
307 class C4ShaderCall {
308  public:
309  C4ShaderCall(const C4Shader *) {};
310 };
311 #endif
312 
314 {
315  friend class C4Shader;
316  friend class C4ShaderCall;
317 
318 public:
320  {
321  VertexShader, // Note: Reloading is currently only implemented for fragment shaders.
323  };
324 private:
325  struct ShaderInstance
326  {
327  ShaderType type;
328  std::string source;
329  };
330 
331  // Map of shader names -> ids. The indirection is there as each C4Shader
332  // may load script shaders from multiple categories.
333  std::map<std::string, std::set<int>> categories;
334  // Map of ids -> script-loaded shaders.
335  std::map<int, ShaderInstance> shaders;
336  int NextID = 0;
337  uint32_t LastUpdate = 0;
338 
339 protected: // Interface for C4Shader friend class
340  std::set<int> GetShaderIDs(const std::vector<std::string>& cats);
341 
342 public: // Interface for script
343  // Adds a shader, returns its id for removal.
344  int Add(const std::string& shaderName, ShaderType type, const std::string& source);
345  // Removes a shader, returning true on success.
346  bool Remove(int id);
347 };
348 
350 
352 {
353  friend class C4Shader;
354 
355  struct Uniform
356  {
357 #ifndef USE_CONSOLE
358  GLenum type;
359  union
360  {
361  int intVec[4];
362  // TODO: Support for other uniform types.
363  };
364 #endif
365  };
366 
367  typedef std::map<std::string, Uniform> UniformMap;
368  std::stack<UniformMap> uniformStack;
369 
370 public:
371  class Popper
372  {
373  C4ScriptUniform* p;
374  size_t size;
375  public:
376  Popper(C4ScriptUniform* p) : p(p), size(p->uniformStack.size()) { }
377  ~Popper() { assert(size == p->uniformStack.size()); p->uniformStack.pop(); }
378  };
379 
380  // Remove all uniforms.
381  void Clear();
382  // Walk the proplist `proplist.Uniforms` and add uniforms. Automatically pops when the return value is destroyed.
383  std::unique_ptr<Popper> Push(C4PropList* proplist);
384  // Apply uniforms to a shader call.
385  void Apply(C4ShaderCall& call);
386 
388 };
389 
390 #endif // INC_C4Shader
void AddFragmentSlices(const char *szWhat, const char *szText, const char *szSource="", int iFileTime=0)
Definition: C4Shader.cpp:89
void SetUniform1i(int iUniform, int iX) const
Definition: C4Shader.h:210
const int C4Shader_Version
Definition: C4Shader.h:37
const int C4Shader_PositionInit
Definition: C4Shader.h:46
C4ShaderCall(const C4Shader *pShader)
Definition: C4Shader.h:190
const int C4Shader_PositionColor
Definition: C4Shader.h:52
void SetUniform2f(int iUniform, float gX, float gY) const
Definition: C4Shader.h:218
void SetScriptCategories(const std::vector< std::string > &categories)
Definition: C4Shader.cpp:104
GLint GetUniform(int iUniform) const
Definition: C4Shader.h:118
void AddFragmentSlice(int iPos, const char *szText)
Definition: C4Shader.cpp:79
void SetUniform1iv(int iUniform, int iLength, const int *pVals) const
Definition: C4Shader.h:222
void AddVertexSlices(const char *szWhat, const char *szText, const char *szSource="", int iFileTime=0)
Definition: C4Shader.cpp:84
GLint GetAttribute(int iAttribute) const
Definition: C4Shader.h:195
bool LoadFragmentSlices(C4GroupSet *pGroupSet, const char *szFile)
Definition: C4Shader.cpp:94
void Apply(C4ShaderCall &call)
Definition: C4Shader.cpp:830
void AddDefine(const char *name)
Definition: C4Shader.cpp:67
const int C4Shader_Vertex_PositionPos
Definition: C4Shader.h:60
const int C4Shader_PositionLight
Definition: C4Shader.h:51
const int C4Shader_Vertex_NormalPos
Definition: C4Shader.h:58
void SetUniformMatrix4x4fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:259
const int C4Shader_Vertex_ColorPos
Definition: C4Shader.h:59
void ClearSlices()
Definition: C4Shader.cpp:326
Popper(C4ScriptUniform *p)
Definition: C4Shader.h:376
const int C4Shader_PositionTexture
Definition: C4Shader.h:48
bool Refresh()
Definition: C4Shader.cpp:447
static bool IsLogging()
Definition: C4Shader.cpp:667
C4Shader()
Definition: C4Shader.cpp:45
void Clear()
Definition: C4Shader.cpp:337
const int C4Shader_PositionMaterial
Definition: C4Shader.h:49
void SetUniformMatrix3x3Transpose(int iUniform, const StdMeshMatrix &matrix)
Definition: C4Shader.h:273
~C4Shader()
Definition: C4Shader.cpp:55
bool Remove(int id)
Definition: C4Shader.cpp:744
const int C4Shader_PositionNormal
Definition: C4Shader.h:50
void Finish()
Definition: C4Shader.cpp:707
const int C4ShaderCall_MaxUnits
Definition: C4Shader.h:43
bool LoadVertexSlices(C4GroupSet *pGroupSet, const char *szFile)
Definition: C4Shader.cpp:99
void SetUniformMatrix2x3fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:244
bool HaveUniform(int iUniform) const
Definition: C4Shader.h:123
const int C4Shader_Vertex_TexCoordPos
Definition: C4Shader.h:57
GLint GetAttribute(int iAttribute) const
Definition: C4Shader.h:128
const int C4Shader_PositionFinish
Definition: C4Shader.h:53
void SetUniform1fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:226
bool Initialised() const
Definition: C4Shader.h:107
void SetUniform3fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:234
void SetUniformMatrix3x4(int iUniform, const StdMeshMatrix &matrix)
Definition: C4Shader.h:282
bool Init(const char *szWhat, const char **szUniforms, const char **szAttributes)
Definition: C4Shader.cpp:350
void SetUniform1f(int iUniform, float gX) const
Definition: C4Shader.h:214
const int C4Shader_MaxTexCoords
Definition: C4Shader.h:40
void SetUniformMatrix3x4fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:254
std::set< int > GetShaderIDs(const std::vector< std::string > &cats)
Definition: C4Shader.cpp:723
GLint AllocTexUnit(int iUniform)
Definition: C4Shader.cpp:670
C4ScriptShader ScriptShader
Definition: C4Shader.cpp:721
void SetUniformMatrix4x4(int iUniform, const StdMeshMatrix &matrix)
Definition: C4Shader.h:288
void Start()
Definition: C4Shader.cpp:693
int Add(const std::string &shaderName, ShaderType type, const std::string &source)
Definition: C4Shader.cpp:732
const int C4Shader_PositionCoordinate
Definition: C4Shader.h:47
const int C4Shader_LastPosition
Definition: C4Shader.h:54
void SetUniformMatrix3x3(int iUniform, const StdMeshMatrix &matrix)
Definition: C4Shader.h:264
void AddVertexSlice(int iPos, const char *szText)
Definition: C4Shader.cpp:74
std::unique_ptr< Popper > Push(C4PropList *proplist)
Definition: C4Shader.cpp:759
void SetUniformMatrix4x4(int iUniform, const StdProjectionMatrix &matrix)
Definition: C4Shader.h:297
void SetUniform2fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:230
void SetUniform4fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:238
void SetUniformMatrix3x3fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:249