diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 451d8070c..205bdad2e 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -453,12 +453,12 @@ Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_t return NULL; // never reached } -ShaderEffect *Graphics::newShaderEffect(const std::string &vertcode, const std::string &fragcode) +ShaderEffect *Graphics::newShaderEffect(const std::vector shadersources) { ShaderEffect *effect = NULL; try { - effect = new ShaderEffect(vertcode, fragcode); + effect = new ShaderEffect(shadersources); } catch(love::Exception &e) { diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 982f40e02..e75a2d53a 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -271,7 +271,7 @@ public: Canvas *newCanvas(int width, int height, Canvas::TextureType texture_type = Canvas::TYPE_NORMAL); - ShaderEffect *newShaderEffect(const std::string &vertcode, const std::string &fragcode); + ShaderEffect *newShaderEffect(const std::vector shadersources); /** * Sets the foreground color. diff --git a/src/modules/graphics/opengl/ShaderEffect.cpp b/src/modules/graphics/opengl/ShaderEffect.cpp index 6bfe4f345..d4a37272c 100644 --- a/src/modules/graphics/opengl/ShaderEffect.cpp +++ b/src/modules/graphics/opengl/ShaderEffect.cpp @@ -39,7 +39,7 @@ struct TemporaryAttacher if (preveffect != NULL) preveffect->attach(); else - cureffect->detach(); + love::graphics::opengl::ShaderEffect::detach(); } love::graphics::opengl::ShaderEffect *cureffect; @@ -59,11 +59,17 @@ ShaderEffect *ShaderEffect::current = NULL; GLint ShaderEffect::_max_texture_units = 0; std::vector ShaderEffect::_texture_id_counters; -ShaderEffect::ShaderEffect(const std::string &vertcode, const std::string &fragcode) +ShaderEffect::ShaderEffect(const std::vector &shadersources) : _program(0) - , _vertcode(vertcode) - , _fragcode(fragcode) { + if (shadersources.size() == 0) + throw love::Exception("Cannot create shader effect: no source code!"); + + // copy shader sources from list to this ShaderEffect + std::vector::const_iterator it; + for (it = shadersources.begin(); it != shadersources.end(); ++it) + _shaders.push_back(*it); + GLint maxtextureunits; glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtextureunits); _max_texture_units = std::max(maxtextureunits - 1, 0); @@ -72,6 +78,7 @@ ShaderEffect::ShaderEffect(const std::string &vertcode, const std::string &fragc if (_texture_id_counters.size() < (size_t) _max_texture_units) _texture_id_counters.resize(_max_texture_units, 0); + // load shader source and create program object loadVolatile(); } @@ -83,71 +90,92 @@ ShaderEffect::~ShaderEffect() unloadVolatile(); } -GLuint ShaderEffect::createShader(GLenum type, const std::string &code) +GLuint ShaderEffect::createShader(const ShaderSource &source) { + GLenum shadertype; const char *shadertypename = NULL; - switch (type) + switch (source.type) { - case GL_VERTEX_SHADER: + case TYPE_VERTEX: + shadertype = GL_VERTEX_SHADER; shadertypename = "vertex"; break; - case GL_GEOMETRY_SHADER_ARB: + case TYPE_GEOMETRY: + shadertype = GL_GEOMETRY_SHADER_ARB; shadertypename = "geometry"; - case GL_FRAGMENT_SHADER: - default: + break; + case TYPE_FRAGMENT: + shadertype = GL_FRAGMENT_SHADER; shadertypename = "fragment"; break; + default: + // tesselation control and evaluation shaders aren't recognized by current version of GLee + throw love::Exception("Cannot create shader object: unknown shader type."); + break; } - 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); + // clear existing errors + while (glGetError() != GL_NO_ERROR); - const char *src = code.c_str(); - size_t srclen = code.length(); - glShaderSource(shader, 1, (const GLchar **)&src, (GLint *)&srclen); + GLuint shaderid = glCreateShader(shadertype); - glCompileShader(shader); + if (shaderid == 0) // oh no! + { + GLenum err = glGetError(); + + if (err == GL_INVALID_OPERATION) // should only happen between glBegin() and glEnd() + throw love::Exception("Cannot create %s shader object.", shadertypename); + else if (err == GL_INVALID_ENUM) + throw love::Exception("Cannot create %s shader object: %s shaders not supported.", shadertypename, shadertypename); + } + + const char *src = source.code.c_str(); + size_t srclen = source.code.length(); + glShaderSource(shaderid, 1, (const GLchar **)&src, (GLint *)&srclen); + + glCompileShader(shaderid); GLint compile_status; - glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); + glGetShaderiv(shaderid, GL_COMPILE_STATUS, &compile_status); + if (compile_status == GL_FALSE) { GLint infologlen; - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infologlen); + glGetShaderiv(shaderid, GL_INFO_LOG_LENGTH, &infologlen); GLchar *errorlog = new GLchar[infologlen + 1]; - glGetShaderInfoLog(shader, infologlen, NULL, errorlog); + glGetShaderInfoLog(shaderid, infologlen, NULL, errorlog); std::string tmp(errorlog); delete[] errorlog; - glDeleteShader(shader); + glDeleteShader(shaderid); throw love::Exception("Cannot compile %s shader:\n%s", shadertypename, tmp.c_str()); } - return shader; + return shaderid; } -void ShaderEffect::createProgram(const std::vector &shaders) +void ShaderEffect::createProgram(const std::vector &shaderids) { _program = glCreateProgram(); if (_program == 0) // should only fail when called between glBegin() and glEnd() throw love::Exception("Cannot create shader program object."); std::vector::const_iterator it; - for (it = shaders.begin(); it != shaders.end(); ++it) + for (it = shaderids.begin(); it != shaderids.end(); ++it) glAttachShader(_program, *it); glLinkProgram(_program); - for (it = shaders.begin(); it != shaders.end(); ++it) + for (it = shaderids.begin(); it != shaderids.end(); ++it) glDetachShader(_program, *it); // we can freely detach shaders after linking GLint link_ok; glGetProgramiv(_program, GL_LINK_STATUS, &link_ok); + if (link_ok == GL_FALSE) { const std::string warnings = getWarnings(); @@ -163,32 +191,30 @@ bool ShaderEffect::loadVolatile() _texture_id_list.clear(); _texture_id_list.insert(_texture_id_list.begin(), _max_texture_units, 0); - std::vector shaders; + std::vector shaderids; - if (_vertcode.length() > 0) - shaders.push_back(createShader(GL_VERTEX_SHADER, _vertcode)); + std::vector::const_iterator curshader; + for (curshader = _shaders.begin(); curshader != _shaders.end(); ++curshader) + shaderids.push_back(createShader(*curshader)); - if (_fragcode.length() > 0) - shaders.push_back(createShader(GL_FRAGMENT_SHADER, _fragcode)); - - if (shaders.size() == 0) - throw love::Exception("Cannot create shader effect: no source code!"); + if (shaderids.size() == 0) + throw love::Exception("Cannot create shader effect: no valid source code!"); try { - createProgram(shaders); + createProgram(shaderids); } catch (love::Exception &e) { std::vector::const_iterator it; - for (it = shaders.begin(); it != shaders.end(); ++it) + for (it = shaderids.begin(); it != shaderids.end(); ++it) glDeleteShader(*it); throw; } std::vector::const_iterator it; - for (it = shaders.begin(); it != shaders.end(); ++it) + for (it = shaderids.begin(); it != shaderids.end(); ++it) glDeleteShader(*it); if (current == this) diff --git a/src/modules/graphics/opengl/ShaderEffect.h b/src/modules/graphics/opengl/ShaderEffect.h index 4f43143d0..3f35d4307 100644 --- a/src/modules/graphics/opengl/ShaderEffect.h +++ b/src/modules/graphics/opengl/ShaderEffect.h @@ -39,7 +39,24 @@ namespace opengl class ShaderEffect : public Object, public Volatile { public: - ShaderEffect(const std::string &vertcode, const std::string &fragcode); + enum ShaderType + { + TYPE_VERTEX, + TYPE_TESSCONTROL, + TYPE_TESSEVAL, + TYPE_GEOMETRY, + TYPE_FRAGMENT, + TYPE_MAX_ENUM + }; + + // thin wrapper for GLSL source code + struct ShaderSource + { + std::string code; + ShaderType type; + }; + + ShaderEffect(const std::vector &shadersources); virtual ~ShaderEffect(); std::string getWarnings() const; @@ -62,12 +79,12 @@ public: private: GLint getUniformLocation(const std::string &name); void checkSetUniformError(); - GLuint createShader(GLenum type, const std::string &code); + GLuint createShader(const ShaderSource &source); void createProgram(const std::vector &shaders); - GLuint _program; - std::string _vertcode; - std::string _fragcode; // volatile and stuff + std::vector _shaders; // all shader code attached to this ShaderEffect + + GLuint _program; // volatile // uniform location buffer std::map _uniforms; diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index 431c5d977..b55952824 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -449,14 +449,30 @@ int w_newShaderEffect(lua_State *L) lua_pushvalue(L, 1); lua_pushvalue(L, 2); - // call effectCodeToGLSL + // call effectCodeToGLSL, returned values will be at the top of the stack lua_pcall(L, 2, 2, 0); - // get returned values from the top of the stack - const char *vertcode = luaL_optstring(L, -2, ""); - const char *fragcode = luaL_optstring(L, -1, ""); + std::vector shaderlist; - ShaderEffect *effect = instance->newShaderEffect(vertcode, fragcode); + // vertex shader code + if (lua_isstring(L, -2)) + { + ShaderEffect::ShaderSource vertshader; + vertshader.type = ShaderEffect::TYPE_VERTEX; + vertshader.code = luaL_checkstring(L, -2); + shaderlist.push_back(vertshader); + } + + // fragment shader code + if (lua_isstring(L, -1)) + { + ShaderEffect::ShaderSource fragshader; + fragshader.type = ShaderEffect::TYPE_FRAGMENT; + fragshader.code = luaL_checkstring(L, -1); + shaderlist.push_back(fragshader); + } + + ShaderEffect *effect = instance->newShaderEffect(shaderlist); luax_newtype(L, "ShaderEffect", GRAPHICS_SHADEREFFECT_T, (void *)effect); } catch(const love::Exception &e)