From: <cn...@us...> - 2009-03-20 03:38:01
|
Revision: 187 http://hgengine.svn.sourceforge.net/hgengine/?rev=187&view=rev Author: cnlohr Date: 2009-03-20 03:37:43 +0000 (Fri, 20 Mar 2009) Log Message: ----------- add shaders (not yet working) Added Paths: ----------- Mercury2/src/Shader.cpp Mercury2/src/Shader.h Added: Mercury2/src/Shader.cpp =================================================================== --- Mercury2/src/Shader.cpp (rev 0) +++ Mercury2/src/Shader.cpp 2009-03-20 03:37:43 UTC (rev 187) @@ -0,0 +1,431 @@ +#include <Shader.h> +#include <RenderableNode.h> +#include <MercuryFile.h> + +#define GL_GLEXT_PROTOTYPES + +#include <GL/gl.h> +#include <GL/glext.h> + +using namespace std; + +REGISTER_ASSET_TYPE( Shader ); + +ShaderAttributesSet SHADERATTRIBUTES; +Shader * Shader::CurrentShader; + +ShaderAttribute * ShaderAttributesSet::GetHandle( const MString & sName ) +{ + ShaderAttribute * ret = m_AllShaderAttributes[sName]; + if( !ret ) + { + ret = new ShaderAttribute(); + m_AllShaderAttributes[sName] = ret; + } + return ret; +} + +Shader::Shader() +{ + iProgramID = (GLhandleARB)NULL; + vertexShader = (GLhandleARB)NULL; + fragmentShader = (GLhandleARB)NULL; + geometryShader = (GLhandleARB)NULL; + iTimeCode[0] = 0; + iTimeCode[1] = 0; + iTimeCode[2] = 0; + +} + +Shader::~Shader() +{ + DestroyShader( ); +} + +void Shader::Init(MercuryNode* node) +{ + MercuryAsset::Init( node ); + RenderableNode* rn; + if ( (rn=RenderableNode::Cast( node )) ) + rn->AddPostRender( this ); +} + +void Shader::Render(const MercuryNode* node) +{ + bool bApply = true; + + //If there's a currnet shader, we may want to abort switching shaders + if( CurrentShader ) + { + if( CurrentShader->fPriority > fPriority ) + bApply = false; + } + if( bApply ) + { + OriginalShader = CurrentShader; + CurrentShader = this; + ActivateShader(); + } +} + +void Shader::PostRender(const MercuryNode* node) +{ + CurrentShader = OriginalShader; + if( OriginalShader ) + OriginalShader->ActivateShader(); + else + DeactivateShader(); +} + +void Shader::LoadFromXML(const XMLNode& node) +{ + sShaderName = node.Attribute("file"); + fPriority = atof( node.Attribute("priority").c_str() ); + LoadShader( ); +} + +bool Shader::LoadShader( ) +{ + GetTimeCodes( iTimeCode ); + MString s1 = sShaderName, s2 = sShaderName, s3 = sShaderName; + MercuryFile * f1; + MercuryFile * f2; + MercuryFile * f3; //geometry + char * Buffer; + int i; + + s1 += ".frag"; + s2 += ".vert"; + s3 += ".geom"; + f1 = FILEMAN.Open( s1 ); + f2 = FILEMAN.Open( s2 ); + f3 = FILEMAN.Open( s3 ); + + if( f1 == 0 || f2 == 0 ) + { + if( !f1 ) + printf( "Could not open %s.\n", (char*)s1.c_str() ); + if( !f2 ) + printf( "Could not open %s.\n", (char*)s2.c_str() ); + return false; + } + if( f1 ) + { + i = f1->Length(); + Buffer = (char*)malloc( i+1 ); + f1->Read( Buffer, i ); + f1->Close(); + printf( "Compiling: %s\n", s1.c_str() ); + Buffer[i] = '\0'; + if( !LoadShaderFrag( Buffer ) ) + { + free( Buffer ); + if( f2 ) + f2->Close(); + if( f3 ) + f3->Close(); + printf( "Reporting failed shaderload. Not linking.\n" ); + return false; + } + free( Buffer ); + } + if( f2 ) + { + i = f2->Length(); + Buffer = (char*)malloc( i+1 ); + f2->Read( Buffer, i ); + f2->Close(); + Buffer[i] = '\0'; + printf( "Compiling: %s\n", s2.c_str() ); + if( !LoadShaderVert( Buffer ) ) + { + if( f3 ) + f3->Close(); + free( Buffer ); + printf( "Reporting failed shaderload. Not linking.\n" ); + return false; + } + free( Buffer ); + } + if( f3 ) + { + i = f3->Length(); + Buffer = (char*)malloc( i+1 ); + f3->Read( Buffer, i ); + f3->Close(); + Buffer[i] = '\0'; + printf( "Compiling: %s\n", s3.c_str() ); + if( !LoadShaderGeom( Buffer ) ) + { + free( Buffer ); + printf( "Reporting failed shaderload. Not linking.\n" ); + return false; + } + free( Buffer ); + } + return LinkShaders(); +} + +bool Shader::LoadShaderFrag( const char * sShaderCode ) +{ + if( strlen( sShaderCode ) < 5 ) + return false; + + GLint bFragCompiled; + GLint stringLength; + fragmentShader = glCreateShaderObjectARB( GL_FRAGMENT_SHADER_ARB ); + glShaderSourceARB( fragmentShader, 1, &sShaderCode, NULL ); + glCompileShaderARB( fragmentShader ); + + glGetObjectParameterivARB( fragmentShader, GL_OBJECT_COMPILE_STATUS_ARB, &bFragCompiled ); + glGetObjectParameterivARB( fragmentShader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &stringLength ); + if ( stringLength > 1 ) + { + char * tmpstr = (char*)malloc( stringLength + 1 ); + glGetInfoLogARB( fragmentShader, stringLength, NULL, tmpstr ); + puts( "Compiling Fragment Shader response follows:" ); + puts( tmpstr ); + free( tmpstr ); + return bFragCompiled!=0; + } + return true; +} + +bool Shader::LoadShaderVert( const char * sShaderCode ) +{ + if( strlen( sShaderCode ) < 5 ) + return false; + + GLint bVertCompiled; + GLint stringLength; + //Create a new vertex shader + vertexShader = glCreateShaderObjectARB( GL_VERTEX_SHADER_ARB ); + //Bind the shader to the text, setting that to be its source. + glShaderSourceARB( vertexShader, 1, &sShaderCode, NULL ); + //Compile the shader + glCompileShaderARB( vertexShader ); + //Did the shader compile? Were there any errors? + glGetObjectParameterivARB( vertexShader, GL_OBJECT_COMPILE_STATUS_ARB, &bVertCompiled ); + glGetObjectParameterivARB( vertexShader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &stringLength ); + if( stringLength > 1 ) + { + char * tmpstr = (char*)malloc( stringLength + 1 ); + glGetInfoLogARB( vertexShader, stringLength, NULL, tmpstr ); + puts( "Compiling Vertex Shader response follows:" ); + puts( tmpstr ); + free( tmpstr ); + return bVertCompiled!=0; + } + + return true; +} + +bool Shader::LoadShaderGeom( const char * sShaderCode ) +{ + if( strlen( sShaderCode ) < 5 ) + return false; + + GLint bGeomCompiled; + GLint stringLength; + //Create a new geometry shader + geometryShader = glCreateShaderObjectARB( GL_GEOMETRY_SHADER_EXT ); + //Bind the shader to the text, setting that to be its source. + glShaderSourceARB( geometryShader, 1, &sShaderCode, NULL ); + //Compile the shader + glCompileShaderARB( geometryShader ); + //Did the shader compile? Were there any errors? + glGetObjectParameterivARB( geometryShader, GL_OBJECT_COMPILE_STATUS_ARB, &bGeomCompiled ); + glGetObjectParameterivARB( geometryShader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &stringLength ); + if( bGeomCompiled == 0 ) + { + char * tmpstr = (char*)malloc( stringLength + 1 ); + glGetInfoLogARB( geometryShader, stringLength, NULL, tmpstr ); + puts( "Compiling Geometry Shader response follows:" ); + puts( tmpstr ); + free( tmpstr ); + return bGeomCompiled!=0; + } + return true; +} + +bool Shader::LinkShaders() +{ + GLint bLinked; + GLint stringLength; + //Create the actual shader prgoram + iProgramID = glCreateProgramObjectARB(); + //Attach the fragment/vertex shader to it. + if( vertexShader ) + glAttachObjectARB( iProgramID, vertexShader ); + if( fragmentShader ) + glAttachObjectARB( iProgramID, fragmentShader ); + if( geometryShader ) + glAttachObjectARB( iProgramID, geometryShader ); + //Attempt to link the shader + glLinkProgramARB( iProgramID ); + + //If we're using a geometry shader, we have to do a little extra. + if( geometryShader ) + { + glProgramParameteriEXT( iProgramID, GL_GEOMETRY_INPUT_TYPE_EXT, GL_TRIANGLES ); + glProgramParameteriEXT( iProgramID, GL_GEOMETRY_OUTPUT_TYPE_EXT, GL_TRIANGLE_STRIP ); + + int ierror, i; + GLint imaxvert; + glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT,&imaxvert); + if( (ierror = glGetError()) != 0 ) + { + puts( "ERROR: You cannot load a geometry shader when there are still errors left in OpenGL." ); + puts( "Please track down the error remaining by using glGetError() to cordon off your code." ); + printf( "The last error received was: %d\n", ierror ); + } + for( i = 1; i < imaxvert; i++ ) + { + glProgramParameteriEXT(iProgramID,GL_GEOMETRY_VERTICES_OUT_EXT,imaxvert/i); + if( glGetError() == 0 ) + break; + } + printf( "Geometry Shader loaded with a total of %d max verticies. Because there are %d max vertices, and %d preceived components per vert.\n", imaxvert/i, imaxvert, i ); + } + + + //See if there were any errors. + glGetObjectParameterivARB( iProgramID, GL_OBJECT_LINK_STATUS_ARB, &bLinked ); + glGetObjectParameterivARB( iProgramID, GL_OBJECT_INFO_LOG_LENGTH_ARB, &stringLength ); + + if ( stringLength > 1 || bLinked == 0 ) + { + char * tmpstr = (char*)malloc( stringLength + 1 ); + glGetInfoLogARB( iProgramID, stringLength, NULL, tmpstr ); + puts( "Linking shaders. response follows:" ); + puts( tmpstr ); + free( tmpstr ); + return bLinked!=0; + } + + //Build the list of uniform tabs. + int iNumUniforms; + glGetObjectParameterivARB( iProgramID, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &iNumUniforms ); + m_vShaderTabs.resize( iNumUniforms ); + for( int i = 0; i < iNumUniforms; ++i ) + { + char buffer[1024]; + int bufflen; + GLint size; + GLenum type; + glGetActiveUniformARB( iProgramID, i, 1024, &bufflen, &size, &type, buffer ); + buffer[bufflen] = 0; + m_vShaderTabs[i] = SHADERATTRIBUTES.GetHandle( buffer ); + } + return true; +} + +void Shader::DestroyShader() +{ + GLhandleARB *objects = NULL; + GLint i, count = -1; + + if( !iProgramID ) + return; + + //If we can't destroy the object, then don't try. + glGetObjectParameterivARB(iProgramID, GL_OBJECT_ATTACHED_OBJECTS_ARB, &count); + + //Iterate through all children. + if (count > 0) + { + objects = (GLhandleARB *)malloc(count*sizeof(GLhandleARB)); + glGetAttachedObjectsARB(iProgramID, count, NULL, objects); + } + else + return; + + for ( i = 0; i < count; ++i) + { + glDetachObjectARB(iProgramID, objects[i]); + } + + glDeleteObjectARB(iProgramID); + free( objects ); + return; +} + +void Shader::CheckForNewer( ) +{ + unsigned long iCurTimes[3]; + GetTimeCodes( iCurTimes ); + if( iCurTimes[0] != iTimeCode[0] || iCurTimes[1] != iTimeCode[1] || iCurTimes[2] != iTimeCode[2] ) + { + DestroyShader( ); + LoadShader( ); + } +} + +void Shader::GetTimeCodes( unsigned long * iOut ) +{ + MercuryFile * f = FILEMAN.Open( sShaderName + ".frag" ); + iOut[0] = f->GetModTime(); + f->Close(); + + f = FILEMAN.Open( sShaderName + ".vert" ); + iOut[1] = f->GetModTime(); + f->Close(); + + f = FILEMAN.Open( sShaderName + ".geom" ); + iOut[2] = f->GetModTime(); + f->Close(); +} + +void Shader::ActivateShader() +{ + glUseProgramObjectARB( iProgramID ); + + for( unsigned i = 0; i < m_vShaderTabs.size(); ++i ) + { + ShaderAttribute * sa = m_vShaderTabs[i]; + switch( sa->typ ) + { + case ShaderAttribute::TYPE_INT: + case ShaderAttribute::TYPE_SAMPLER: + glUniform1iARB( i, sa->sau.iInt ); + break; + case ShaderAttribute::TYPE_FLOAT: + case ShaderAttribute::TYPE_FLOATV4: + glUniform4fvARB( i, 4, &sa->sau.fFloatV4[0] ); + }; + } +} + +void Shader::DeactivateShader() +{ + glUseProgramObjectARB( 0 ); +} + + +/* + * Copyright (c) 2009 Charles Lohr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of the Mercury Engine nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ Added: Mercury2/src/Shader.h =================================================================== --- Mercury2/src/Shader.h (rev 0) +++ Mercury2/src/Shader.h 2009-03-20 03:37:43 UTC (rev 187) @@ -0,0 +1,185 @@ +#ifndef SHADER_H +#define SHADER_H + +#include <MercuryAsset.h> +#include <map> +#include <vector> + +///Basic Attribute for all shaders +class ShaderAttribute +{ +public: + ShaderAttribute() : typ( TYPE_INT ) { sau.iInt = 0; } + + ///Type of ShaderAttribute for shader + enum ShaderAttributeTyp + { + TYPE_INT, ///Synonomous to 'int' when passing into a shader + TYPE_SAMPLER, ///Synonomous to 'sampler2D' when passing into a shader + TYPE_FLOAT, ///Synonomous to 'float' when passing into a shader + TYPE_FLOATV4 ///Synonomous to 'vec4' when passing into a shader + } typ; + + ///Actual data for value. + union ShaderAttributeVal + { + int iInt; ///Synonomous to 'int' + unsigned int iSampler; ///Synonomous to 'sampler2D' + float fFloat; ///Synonomous to 'float' + float fFloatV4[4]; ///Synonomous to 'vec4' + } sau; +}; + +///Shader Attribute Retainer +class ShaderAttributesSet +{ +public: + ShaderAttribute * GetHandle( const MString & sName ); +private: + ///All shader attributes + /** This contains a list of all attributes that are passed in. + If a shader does not have an attribute, it does not insert + it here. Note that if shaders change and an element is + no longer referenced, it will remain in the map, as to + prevent bad pointers. This list is all-enduring. + */ + std::map< MString, ShaderAttribute * > m_AllShaderAttributes; +}; + +extern ShaderAttributesSet SHADERATTRIBUTES; + +///Basic element for turning shaders on and off +/** This class helps aide in the loading and use of shaders. It allows loading of files + through the LoadShader() function that actively looks for .frag and .vert files. By use + of the CheckForNewer() function, the class looks for changes, if any changes are found + the shaders will be re-loaded, compiled and linked. Once this node has a shader loaded, + the shader can be activated or deactivated using the ActivateShader() and + DeactivateShader() functions respectively. Note that shaders do not stack. When you + call DeactiveShader(), it does not bring back previously activated shaders. It simply + turns all shaders off. */ +class Shader : public MercuryAsset +{ +public: + Shader(); + virtual ~Shader(); + + virtual void Init(MercuryNode* node); + virtual void Render(const MercuryNode* node); + virtual void PostRender(const MercuryNode* node); + static Shader* Generate() { return new Shader; } + virtual void LoadFromXML(const XMLNode& node); + + ///Explicitly get the OpenGL ProgramID in the event you need it for advanced techniques + unsigned int GetProgramID() { return iProgramID; } +private: + ///Suggested function for loading shaders. + /** This function looks for {sShaderName}.vert and {sShaderName}.frag. It will + attempt to load, compile and link the files. If any errors are found, they will + be announced at STDOUT. When Geometry shader support is added, it will search + for .geom files */ + bool LoadShader( ); + + ///Explicitly load a fragment shader + /** This function takes on raw code in the sShaderCode string and attemps to compile + it. Any errors it runs into will be displayed at STDOUT. Note you are + discouraged to use this function, in favor of using LoadShader(). */ + bool LoadShaderFrag( const char * sShaderCode ); + + ///Explicitly load a vertex shader + /** This function takes on raw code in the sShaderCode string and attemps to compile + it. Any errors it runs into will be displayed at STDOUT. You should use + LoadShader() instead of this function. */ + bool LoadShaderVert( const char * sShaderCode ); + + ///Explicitly load a geometry shader + /** This function takes on raw code in the sShaderCode string and attemps to compile + it. Any errors it runs into will be displayed at STDOUT. You should use + LoadShader() instead of this function. */ + bool LoadShaderGeom( const char * sShaderCode ); + + ///Explicitly link all shaders currently loaded + /** This takes vertexShader and fragmentShader and converts them into iProgramID. + this function is discouraged when using LoadShader(). */ + bool LinkShaders(); + + ///Check for newer version of 'this' shader + void CheckForNewer( ); + + ///Get the last modified time for sShaderName + /* This function takes on iOut as being where to put the last time the shader was modified. + this value is system dependent and is necessiarly not linked to anything like seconds. */ + void GetTimeCodes( unsigned long * iOut ); + + ///Activate Shader (apply to current OpenGL state) + void ActivateShader(); + + ///Turn all shaders off in OpenGL state. + void DeactivateShader(); + + ///Destroy this shader + void DestroyShader(); + + ///The last time codes (for vertex and fragment shaders respectively) + unsigned long iTimeCode[3]; + + ///The OpenGL Program ID (in OpenGL Land, contains all shaders, linked together) + unsigned int iProgramID; + + ///The OpenGL Geometry Program ID + unsigned int geometryShader; + + ///The OpenGL Vertex Program ID + unsigned int vertexShader; + + ///The OpenGL Fragment Program ID + unsigned int fragmentShader; + + ///Shader attributes + /** This is the system that helps make it possible to blast + through all attributes currently set up by dereferencing + the pointers in the attributes repository. + */ + std::vector< ShaderAttribute * > m_vShaderTabs; + + ///Name of the shader + MString sShaderName; + + ///Priority of THIS shader (if lower than currently active one, it does not become active) + float fPriority; + + ///Global static shader (so shaders can recursively activate and deactivate shaders) + static Shader * CurrentShader; + + ///Original Shader (to re-enable when leaving) + Shader * OriginalShader; +}; + +#endif + +/* + * Copyright (c) 2009 Charles Lohr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of the Mercury Engine nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |