/* * OpenClonk, http://www.openclonk.org * * Copyright (c) 2014-2015, The OpenClonk Team and contributors * * Distributed under the terms of the ISC license; see accompanying file * "COPYING" for details. * * "Clonk" is a registered trademark of Matthes Bender, used with permission. * See accompanying file "TRADEMARK" for details. * * To redistribute this file separately, substitute the full license texts * for the above references. */ // Shader implementation somewhere in the middle between easy and extensible. #ifndef INC_C4Shader #define INC_C4Shader #include "StdBuf.h" #include "StdMeshMath.h" #include "C4Surface.h" #include // Shader version const int C4Shader_Version = 150; // GLSL 1.50 / OpenGL 3.2 // Maximum number of texture coordinates const int C4Shader_MaxTexCoords = 8; // Maximum number of texture units per shader call const int C4ShaderCall_MaxUnits = 32; // Positions in fragment shader const int C4Shader_PositionInit = 0; const int C4Shader_PositionCoordinate = 20; const int C4Shader_PositionTexture = 40; const int C4Shader_PositionMaterial = 60; const int C4Shader_PositionNormal = 80; const int C4Shader_PositionLight = 100; const int C4Shader_PositionColor = 120; const int C4Shader_PositionFinish = 140; const int C4Shader_LastPosition = 256; // Positions in vertex shader const int C4Shader_Vertex_TexCoordPos = 50; const int C4Shader_Vertex_NormalPos = 60; const int C4Shader_Vertex_ColorPos = 70; const int C4Shader_Vertex_PositionPos = 80; class C4Shader { friend class C4ShaderCall; public: C4Shader(); ~C4Shader(); private: StdStrBuf Name; // Program texts struct ShaderSlice { int Position; StdCopyStrBuf Text; StdCopyStrBuf Source; int SourceTime; }; typedef std::list ShaderSliceList; ShaderSliceList VertexSlices, FragmentSlices; // Last refresh check C4TimeMilliseconds LastRefresh; // Used texture coordinates int iTexCoords; #ifndef USE_CONSOLE // shaders GLuint hProg; // shader variables struct Variable { int address; const char* name; }; std::vector Uniforms; std::vector Attributes; #endif public: bool Initialised() const { #ifndef USE_CONSOLE return hProg != 0; #else return true; #endif } // Uniform getters #ifndef USE_CONSOLE GLint GetUniform(int iUniform) const { return iUniform >= 0 && static_cast(iUniform) < Uniforms.size() ? Uniforms[iUniform].address : -1; } bool HaveUniform(int iUniform) const { return GetUniform(iUniform) != GLint(-1); } GLint GetAttribute(int iAttribute) const { return iAttribute >= 0 && static_cast(iAttribute) < Attributes.size() ? Attributes[iAttribute].address : -1; } #else int GetUniform(int iUniform) const { return -1; } bool HaveUniform(int iUniform) const { return false; } int GetAttribute(int iAttribute) const { return -1; } #endif // Shader is composed from various slices void AddDefine(const char* name); void AddVertexSlice(int iPos, const char *szText); void AddFragmentSlice(int iPos, const char *szText, const char *szSource = "", int iFileTime = 0); void AddVertexSlices(const char *szWhat, const char *szText, const char *szSource = "", int iFileTime = 0); void AddFragmentSlices(const char *szWhat, const char *szText, const char *szSource = "", int iFileTime = 0); bool LoadFragmentSlices(C4GroupSet *pGroupSet, const char *szFile); bool LoadVertexSlices(C4GroupSet *pGroupSet, const char *szFile); // Assemble and link the shader. Should be called again after new slices are added. bool Init(const char *szWhat, const char **szUniforms, const char **szAttributes); bool Refresh(); void ClearSlices(); void Clear(); private: void AddSlice(ShaderSliceList& slices, int iPos, const char *szText, const char *szSource, int iFileTime); void AddSlices(ShaderSliceList& slices, const char *szWhat, const char *szText, const char *szSource, int iFileTime); bool LoadSlices(ShaderSliceList& slices, C4GroupSet *pGroupSet, const char *szFile); int ParsePosition(const char *szWhat, const char **ppPos); StdStrBuf Build(const ShaderSliceList &Slices, bool fDebug = false); #ifndef USE_CONSOLE GLuint Create(GLenum iShaderType, const char *szWhat, const char *szShader); void DumpInfoLog(const char *szWhat, GLuint hShader, bool forProgram); #endif public: static bool IsLogging(); }; #ifndef USE_CONSOLE class C4ShaderCall { public: C4ShaderCall(const C4Shader *pShader) : fStarted(false), pShader(pShader), iUnits(0) { } ~C4ShaderCall() { Finish(); } GLint GetAttribute(int iAttribute) const { return pShader->GetAttribute(iAttribute); } private: bool fStarted; const C4Shader *pShader; int iUnits; public: GLint AllocTexUnit(int iUniform); // Setting uniforms... Lots of code duplication here, not quite sure whether // something could be done about it. void SetUniform1i(int iUniform, int iX) const { if (pShader->HaveUniform(iUniform)) glUniform1i(pShader->GetUniform(iUniform), iX); } void SetUniform1f(int iUniform, float gX) const { if (pShader->HaveUniform(iUniform)) glUniform1f(pShader->GetUniform(iUniform), gX); } void SetUniform2f(int iUniform, float gX, float gY) const { if (pShader->HaveUniform(iUniform)) glUniform2f(pShader->GetUniform(iUniform), gX, gY); } void SetUniform1iv(int iUniform, int iLength, const int *pVals) const { if (pShader->HaveUniform(iUniform)) glUniform1iv(pShader->GetUniform(iUniform), iLength, pVals); } void SetUniform1fv(int iUniform, int iLength, const float *pVals) const { if (pShader->HaveUniform(iUniform)) glUniform1fv(pShader->GetUniform(iUniform), iLength, pVals); } void SetUniform2fv(int iUniform, int iLength, const float *pVals) const { if (pShader->HaveUniform(iUniform)) glUniform2fv(pShader->GetUniform(iUniform), iLength, pVals); } void SetUniform3fv(int iUniform, int iLength, const float *pVals) const { if (pShader->HaveUniform(iUniform)) glUniform3fv(pShader->GetUniform(iUniform), iLength, pVals); } void SetUniform4fv(int iUniform, int iLength, const float *pVals) const { if (pShader->HaveUniform(iUniform)) glUniform4fv(pShader->GetUniform(iUniform), iLength, pVals); } // Matrices are in row-major order void SetUniformMatrix2x3fv(int iUniform, int iLength, const float* pVals) const { if (pShader->HaveUniform(iUniform)) glUniformMatrix3x2fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals); } void SetUniformMatrix3x3fv(int iUniform, int iLength, const float *pVals) const { if (pShader->HaveUniform(iUniform)) glUniformMatrix3fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals); } void SetUniformMatrix3x4fv(int iUniform, int iLength, const float *pVals) const { if (pShader->HaveUniform(iUniform)) glUniformMatrix4x3fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals); } void SetUniformMatrix4x4fv(int iUniform, int iLength, const float* pVals) const { if (pShader->HaveUniform(iUniform)) glUniformMatrix4fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals); } void SetUniformMatrix3x3(int iUniform, const StdMeshMatrix& matrix) { if (pShader->HaveUniform(iUniform)) { 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) }; glUniformMatrix3fv(pShader->GetUniform(iUniform), 1, GL_FALSE, mat); } } void SetUniformMatrix3x3Transpose(int iUniform, const StdMeshMatrix& matrix) { if (pShader->HaveUniform(iUniform)) { 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) }; glUniformMatrix3fv(pShader->GetUniform(iUniform), 1, GL_FALSE, mat); } } void SetUniformMatrix3x4(int iUniform, const StdMeshMatrix& matrix) { if (pShader->HaveUniform(iUniform)) glUniformMatrix4x3fv(pShader->GetUniform(iUniform), 1, GL_TRUE, matrix.data()); } void SetUniformMatrix4x4(int iUniform, const StdMeshMatrix& matrix) { if (pShader->HaveUniform(iUniform)) { 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 }; glUniformMatrix4fv(pShader->GetUniform(iUniform), 1, GL_FALSE, mat); } } void SetUniformMatrix4x4(int iUniform, const StdProjectionMatrix& matrix) { if (pShader->HaveUniform(iUniform)) glUniformMatrix4fv(pShader->GetUniform(iUniform), 1, GL_TRUE, matrix.data()); } void Start(); void Finish(); }; #else // USE_CONSOLE class C4ShaderCall { public: C4ShaderCall(const C4Shader *) {}; }; #endif #endif // INC_C4Shader