/* * 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 "C4Surface.h" // Shader version const int C4Shader_Version = 120; // GLSL 1.20 / OpenGL 2.1 // 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: // Program texts struct ShaderSlice { int Position; StdCopyStrBuf Text; StdCopyStrBuf Source; int SourceTime; }; typedef std::list ShaderSliceList; ShaderSliceList VertexSlices, FragmentSlices; // Used texture coordinates int iTexCoords; #ifndef USE_CONSOLE // shaders GLhandleARB hVert, hFrag, hProg; // shader variables int iUniformCount; GLint *pUniforms; #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 LoadSlices(C4GroupSet *pGroupSet, const char *szFile); // Add default vertex code (2D - no transformation) void AddVertexDefaults(); #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(const char *szWhat, const char **szUniforms); 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); 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; GLenum hUnit[C4ShaderCall_MaxUnits]; public: GLint AllocTexUnit(int iUniform, GLenum iType); // 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 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 Start(); void Finish(); }; #endif #endif // INC_C4Shader