Browse Source

added C++side support for combinations of vertex and fragment shaders in pixeleffects

Alexander Szpakowski 12 years ago
parent
commit
28e3b105e9

+ 2 - 1
.hgignore

@@ -22,7 +22,8 @@ glob:*.lib
 glob:*.ncb
 glob:*.exe
 glob:*.bat
-glob:platform/macosx/build
+glob:platform/macosx/build
+glob:platform/macosx/DerivedData
 glob:platform/macosx/love.xcodeproj/bill*
 glob:platform/macosx/love.xcodeproj/xcuserdata
 glob:platform/macosx/love.xcodeproj/project.xcworkspace

+ 1 - 1
src/modules/graphics/opengl/Graphics.cpp

@@ -455,7 +455,7 @@ PixelEffect *Graphics::newPixelEffect(const std::string &code)
 	PixelEffect *effect = NULL;
 	try
 	{
-		effect = new PixelEffect(code);
+		effect = new PixelEffect("", code);
 	}
 	catch(love::Exception &e)
 	{

+ 108 - 49
src/modules/graphics/opengl/PixelEffect.cpp

@@ -69,72 +69,130 @@ GLint PixelEffect::getTextureUnit(const std::string &name)
 	return _current_texture_unit;
 }
 
-PixelEffect::PixelEffect(const std::string &code)
+	PixelEffect::PixelEffect(const std::string &vertcode, const std::string &fragcode)
 	: _program(0)
-	, _code(code)
+	, _vertcode(vertcode)
+	, _fragcode(fragcode)
 {
 	glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &_max_texture_units);
 	loadVolatile();
 }
 
-bool PixelEffect::loadVolatile()
+GLuint PixelEffect::createShader(GLenum type, const std::string &code)
 {
-	_program = glCreateProgram();
-	// should only fail if this is called between a glBegin()/glEnd() pair
-	if (_program == 0)
-		throw love::Exception("Cannot create shader program object.");
-
-	GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
-	// should only fail if this is called between a glBegin()/glEnd() pair
-	if (shader == 0)
+	const char *shadertypename = NULL;
+	switch (type)
 	{
-		glDeleteProgram(_program);
-		throw love::Exception("Cannot create shader object.");
+	case GL_VERTEX_SHADER:
+		shadertypename = "vertex";
+		break;
+	case GL_GEOMETRY_SHADER_ARB:
+		shadertypename = "geometry";
+	case GL_FRAGMENT_SHADER:
+	default:
+		shadertypename = "fragment";
+		break;
 	}
-
-	// compile fragment shader code
-	const char *src = _code.c_str();
-	GLint strlen = _code.length();
-	glShaderSource(shader, 1, (const GLchar **)&src, &strlen);
+	
+	GLuint shader = glCreateShader(type);
+	if (shader == 0) // should only fail when called between glBegin() and glEnd()
+		throw love::Exception("Cannot create %s shader object.", shadertypename);
+	
+	const char *src = code.c_str();
+	size_t srclen = code.length();
+	glShaderSource(shader, 1, (const GLchar **)&src, (GLint *)&srclen);
+	
 	glCompileShader(shader);
-
-	GLint compile_ok;
-	glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_ok);
-	if (GL_FALSE == compile_ok)
+	
+	GLint compile_status;
+	glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
+	if (compile_status == GL_FALSE)
 	{
-		// get compiler error
-		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &strlen);
-		char *error_str = new char[strlen];
-		glGetShaderInfoLog(shader, strlen, NULL, error_str);
-		std::string tmp(error_str);
-
-		// cleanup before throw
-		delete[] error_str;
+		GLint infologlen;
+		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infologlen);
+		
+		GLchar *errorlog = new GLchar[infologlen + 1];
+		glGetShaderInfoLog(shader, infologlen, NULL, errorlog);
+		
+		std::string tmp(errorlog);
+		
+		delete[] errorlog;
 		glDeleteShader(shader);
-		glDeleteProgram(_program);
-
-		// XXX: errorlog may contain escape sequences.
-		throw love::Exception("Cannot compile shader:\n%s", tmp.c_str());
+		
+		throw love::Exception("Cannot compile %s shader:\n%s", shadertypename, tmp.c_str());
 	}
+	
+	return shader;
+}
 
-	// link fragment shader
+GLuint PixelEffect::createProgram(const std::vector<GLuint> &shaders)
+{
+	GLuint program = glCreateProgram();
+	if (program == 0) // should only fail when called between glBegin() and glEnd()
+		throw love::Exception("Cannot create shader program object.");
+	
+	std::vector<GLuint>::const_iterator it;
+	for (it = shaders.begin(); it != shaders.end(); ++it)
+		glAttachShader(program, *it);
+	
+	glLinkProgram(program);
+	
+	for (it = shaders.begin(); it != shaders.end(); ++it)
+		glDetachShader(program, *it);
+	
 	GLint link_ok;
-	glAttachShader(_program, shader);
-	glLinkProgram(_program);
-	glGetProgramiv(_program, GL_LINK_STATUS, &link_ok);
-	if (GL_FALSE == link_ok)
+	glGetProgramiv(program, GL_LINK_STATUS, &link_ok);
+	if (link_ok == GL_FALSE)
 	{
-		// this should not happen if compiling is ok, but one can never be too careful
-		// get linker error
-		std::string tmp(getWarnings());
-
-		// cleanup before throw
-		glDeleteShader(shader);
-		glDeleteProgram(_program);
-		throw love::Exception("Cannot compile shader:\n%s", tmp.c_str());
+		GLint strlen, nullpos;
+		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &strlen);
+		
+		char *temp_str = new char[strlen+1];
+		// be extra sure that the error string will be 0-terminated
+		memset(temp_str, '\0', strlen+1);
+		glGetProgramInfoLog(program, strlen, &nullpos, temp_str);
+		temp_str[nullpos] = '\0';
+		
+		std::string warnings(temp_str);
+		delete[] temp_str;
+
+		glDeleteProgram(program);
+		
+		throw love::Exception("Cannot link shader program object:\n%s", warnings.c_str());
 	}
+	
+	return program;
+}
 
-	glDeleteShader(shader);
+bool PixelEffect::loadVolatile()
+{	
+	std::vector<GLuint> shaders;
+	
+	if (_vertcode.length() > 0)
+		shaders.push_back(createShader(GL_VERTEX_SHADER, _vertcode));
+	
+	if (_fragcode.length() > 0)
+		shaders.push_back(createShader(GL_FRAGMENT_SHADER, _fragcode));
+	
+	if (shaders.size() == 0)
+		throw love::Exception("Cannot create PixelEffect: no source code!");
+	
+	try
+	{
+		_program = createProgram(shaders);
+	}
+	catch (love::Exception &e)
+	{
+		std::vector<GLuint>::iterator it;
+		for (it = shaders.begin(); it != shaders.end(); ++it)
+			glDeleteShader(*it);
+		
+		throw;
+	}
+	
+	std::vector<GLuint>::iterator it;
+	for (it = shaders.begin(); it != shaders.end(); ++it)
+		glDeleteShader(*it);
 
 	return true;
 }
@@ -146,7 +204,8 @@ PixelEffect::~PixelEffect()
 
 void PixelEffect::unloadVolatile()
 {
-	glDeleteProgram(_program);
+	if (_program != 0)
+		glDeleteProgram(_program);
 }
 
 std::string PixelEffect::getGLSLVersion()

+ 7 - 2
src/modules/graphics/opengl/PixelEffect.h

@@ -24,6 +24,7 @@
 #include "common/Object.h"
 #include <string>
 #include <map>
+#include <vector>
 #include "OpenGL.h"
 #include "Image.h"
 #include "Canvas.h"
@@ -38,7 +39,7 @@ namespace opengl
 class PixelEffect : public Object, public Volatile
 {
 public:
-	PixelEffect(const std::string &code);
+	PixelEffect(const std::string &vertcode, const std::string &fragcode);
 	virtual ~PixelEffect();
 	std::string getWarnings() const;
 
@@ -60,8 +61,12 @@ public:
 private:
 	GLint getUniformLocation(const std::string &name);
 	void checkSetUniformError();
+	GLuint createShader(GLenum type, const std::string &code);
+	GLuint createProgram(const std::vector<GLuint> &shaders);
+	
 	GLuint _program;
-	std::string _code; // volatile and stuff
+	std::string _vertcode;
+	std::string _fragcode; // volatile and stuff
 
 	// uniform location buffer
 	std::map<std::string, GLint> _uniforms;