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