/* * 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" // Shader version const int C4Shader_Version = 130; // GLSL 1.30 / OpenGL 3.0 // 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_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 GLhandleARB hVert, hFrag, hProg; // shader variables int iUniformCount; GLint *pUniforms; const char **pUniformNames; #endif public: enum VertexAttribIndex { // These correspond to the locations nVidia uses for the // respective gl_* attributes, so make sure whatever you // use for custom ones doesn't conflict with these UNLESS // you're not using the pre-defined ones in your shader VAI_Vertex = 0, VAI_Normal = 2, VAI_Color = 3, VAI_TexCoord0 = 8, // and upwards through TexCoord7 = 15 // Make sure you move these if we implement multitexturing VAI_BoneWeights, VAI_BoneWeightsMax = VAI_BoneWeights + 1, VAI_BoneIndices, VAI_BoneIndicesMax = VAI_BoneIndices + VAI_BoneWeightsMax - VAI_BoneWeights }; bool Initialised() const { #ifndef USE_CONSOLE return hVert != 0; #else return true; #endif } // Uniform getters #ifndef USE_CONSOLE GLint GetUniform(int iUniform) const { return iUniform >= 0 && iUniform < iUniformCount ? pUniforms[iUniform] : -1; } bool HaveUniform(int iUniform) const { return GetUniform(iUniform) != GLint(-1); } #else int GetUniform(int iUniform) const { return -1; } bool HaveUniform(int iUniform) const { return false; } #endif // Shader is composed from various slices 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); #ifndef USE_CONSOLE // Allocate a texture coordinate, returning its ID to be used with glMultiTexCoord. // The texture coordinate will be visible to both shaders under the given name. // Note that in contrast to uniforms, these will not disappear if not used! GLenum AddTexCoord(const char *szName); #endif // Assemble and link the shader. Should be called again after new slices are added. bool Init(const char *szWhat, const char **szUniforms); 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 GLhandleARB Create(GLenum iShaderType, const char *szWhat, const char *szShader); void DumpInfoLog(const char *szWhat, GLhandleARB hShader); int GetObjectStatus(GLhandleARB hObj, GLenum type); #endif public: static bool IsLogging(); }; #ifndef USE_CONSOLE class C4ShaderCall { public: C4ShaderCall(const C4Shader *pShader) : fStarted(false), pShader(pShader), iUnits(0) { } ~C4ShaderCall() { Finish(); } 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)) glUniform1iARB(pShader->GetUniform(iUniform), iX); } void SetUniform1f(int iUniform, float gX) const { if (pShader->HaveUniform(iUniform)) glUniform1fARB(pShader->GetUniform(iUniform), gX); } void SetUniform2f(int iUniform, float gX, float gY) const { if (pShader->HaveUniform(iUniform)) glUniform2fARB(pShader->GetUniform(iUniform), gX, gY); } void SetUniform1iv(int iUniform, int iLength, const int *pVals) const { if (pShader->HaveUniform(iUniform)) glUniform1ivARB(pShader->GetUniform(iUniform), iLength, pVals); } void SetUniform1fv(int iUniform, int iLength, const float *pVals) const { if (pShader->HaveUniform(iUniform)) glUniform1fvARB(pShader->GetUniform(iUniform), iLength, pVals); } void SetUniform2fv(int iUniform, int iLength, const float *pVals) const { if (pShader->HaveUniform(iUniform)) glUniform2fvARB(pShader->GetUniform(iUniform), iLength, pVals); } void SetUniform3fv(int iUniform, int iLength, const float *pVals) const { if (pShader->HaveUniform(iUniform)) glUniform3fvARB(pShader->GetUniform(iUniform), iLength, pVals); } void SetUniform4fv(int iUniform, int iLength, const float *pVals) const { if (pShader->HaveUniform(iUniform)) glUniform4fvARB(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)) glUniformMatrix4fvARB(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 }; glUniformMatrix4fvARB(pShader->GetUniform(iUniform), 1, GL_FALSE, mat); } } void SetUniformMatrix4x4(int iUniform, const StdProjectionMatrix& matrix) { if (pShader->HaveUniform(iUniform)) glUniformMatrix4fvARB(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