diff --git a/examples/island/Island.cpp b/examples/island/Island.cpp index 50a8f1ead..8b5a760bd 100644 --- a/examples/island/Island.cpp +++ b/examples/island/Island.cpp @@ -115,6 +115,12 @@ int main() // Check whether the prerequisites are suppprted bool prerequisitesSupported = sf::VertexBuffer::isAvailable() && sf::Shader::isAvailable(); + // Bind the shader to SFML's drawable interface + terrainShader.setPositionAttribute ("positionAttribute") + .setColorAttribute ("colorAttribute") + .setTextureCoordinateAttribute("texCoordAttribute") + .setModelViewProjectionMatrix ("modelViewProjectionMatrix"); + // Set up our graphics resources and set the status text accordingly if (!prerequisitesSupported) { diff --git a/examples/island/resources/terrain.frag b/examples/island/resources/terrain.frag index ae1818713..b28e93490 100644 --- a/examples/island/resources/terrain.frag +++ b/examples/island/resources/terrain.frag @@ -1,3 +1,5 @@ +varying vec4 frontColor; + varying vec3 normal; uniform float lightFactor; @@ -7,5 +9,5 @@ void main() vec3 eyePosition = vec3(0.0, 0.0, 1.0); vec3 halfVector = normalize(lightPosition + eyePosition); float intensity = lightFactor + (1.0 - lightFactor) * dot(normalize(normal), normalize(halfVector)); - gl_FragColor = gl_Color * vec4(intensity, intensity, intensity, 1.0); + gl_FragColor = frontColor * vec4(intensity, intensity, intensity, 1.0); } diff --git a/examples/island/resources/terrain.vert b/examples/island/resources/terrain.vert index a06996df5..fae3e06f1 100644 --- a/examples/island/resources/terrain.vert +++ b/examples/island/resources/terrain.vert @@ -1,8 +1,16 @@ +attribute vec2 positionAttribute; +attribute vec4 colorAttribute; +attribute vec2 texCoordAttribute; + +uniform mat4 modelViewProjectionMatrix; + +varying vec4 frontColor; + varying vec3 normal; void main() { - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; - gl_FrontColor = gl_Color; - normal = vec3(gl_MultiTexCoord0.xy, 1.0); + gl_Position = modelViewProjectionMatrix * vec4(positionAttribute, 0.0, 1.0); + frontColor = colorAttribute; + normal = vec3(texCoordAttribute, 1.0); } diff --git a/examples/shader/Shader.cpp b/examples/shader/Shader.cpp index 8a81fde38..4ef7cf678 100644 --- a/examples/shader/Shader.cpp +++ b/examples/shader/Shader.cpp @@ -28,8 +28,15 @@ public: return false; m_sprite.setTexture(m_texture); + // Bind the shader to SFML's drawable interface + m_shader.setPositionAttribute ("positionAttribute") + .setColorAttribute ("colorAttribute") + .setTextureCoordinateAttribute("texCoordAttribute") + .setModelViewProjectionMatrix ("modelViewProjectionMatrix") + .setTextureMatrix ("textureMatrix"); + // Load the shader - if (!m_shader.loadFromFile("resources/pixelate.frag", sf::Shader::Fragment)) + if (!m_shader.loadFromFile("resources/pixelate.vert", "resources/pixelate.frag")) return false; m_shader.setUniform("texture", sf::Shader::CurrentTexture); @@ -92,6 +99,13 @@ public: m_text.setCharacterSize(22); m_text.setPosition(30, 20); + // Bind the shader to SFML's drawable interface + m_shader.setPositionAttribute ("positionAttribute") + .setColorAttribute ("colorAttribute") + .setTextureCoordinateAttribute("texCoordAttribute") + .setModelViewProjectionMatrix ("modelViewProjectionMatrix") + .setTextureMatrix ("textureMatrix"); + // Load the shader if (!m_shader.loadFromFile("resources/wave.vert", "resources/blur.frag")) return false; @@ -145,6 +159,12 @@ public: m_points.append(sf::Vertex(sf::Vector2f(x, y), sf::Color(r, g, b))); } + // Bind the shader to SFML's drawable interface + m_shader.setPositionAttribute("positionAttribute") + .setColorAttribute ("colorAttribute") + .setModelViewMatrix ("modelViewMatrix") + .setProjectionMatrix ("projectionMatrix"); + // Load the shader if (!m_shader.loadFromFile("resources/storm.vert", "resources/blink.frag")) return false; @@ -212,8 +232,15 @@ public: m_entities.push_back(entity); } + // Bind the shader to SFML's drawable interface + m_shader.setPositionAttribute ("positionAttribute") + .setColorAttribute ("colorAttribute") + .setTextureCoordinateAttribute("texCoordAttribute") + .setModelViewProjectionMatrix ("modelViewProjectionMatrix") + .setTextureMatrix ("textureMatrix"); + // Load the shader - if (!m_shader.loadFromFile("resources/edge.frag", sf::Shader::Fragment)) + if (!m_shader.loadFromFile("resources/edge.vert", "resources/edge.frag")) return false; m_shader.setUniform("texture", sf::Shader::CurrentTexture); @@ -290,6 +317,10 @@ public: if (!m_logoTexture.loadFromFile("resources/logo.png")) return false; + // Bind the shader to SFML's drawable interface + m_shader.setPositionAttribute ("positionAttribute") + .setModelViewProjectionMatrix("modelViewProjectionMatrix"); + // Load the shader if (!m_shader.loadFromFile("resources/billboard.vert", "resources/billboard.geom", "resources/billboard.frag")) return false; diff --git a/examples/shader/resources/billboard.frag b/examples/shader/resources/billboard.frag index 3057f64b0..9eb2c8e9d 100644 --- a/examples/shader/resources/billboard.frag +++ b/examples/shader/resources/billboard.frag @@ -4,8 +4,10 @@ uniform sampler2D texture; in vec2 tex_coord; +out vec4 fragColor; + void main() { // Read and apply a color from the texture - gl_FragColor = texture2D(texture, tex_coord); + fragColor = texture2D(texture, tex_coord); } diff --git a/examples/shader/resources/billboard.vert b/examples/shader/resources/billboard.vert index 3a899052c..251821f0c 100644 --- a/examples/shader/resources/billboard.vert +++ b/examples/shader/resources/billboard.vert @@ -1,5 +1,11 @@ +#version 150 + +in vec2 positionAttribute; + +uniform mat4 modelViewProjectionMatrix; + void main() { // Transform the vertex position - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_Position = modelViewProjectionMatrix * vec4(positionAttribute, 0.0, 1.0); } diff --git a/examples/shader/resources/blink.frag b/examples/shader/resources/blink.frag index 50a04a510..3a973029f 100644 --- a/examples/shader/resources/blink.frag +++ b/examples/shader/resources/blink.frag @@ -1,9 +1,10 @@ -uniform sampler2D texture; uniform float blink_alpha; +varying vec4 frontColor; + void main() { - vec4 pixel = gl_Color; + vec4 pixel = frontColor; pixel.a = blink_alpha; gl_FragColor = pixel; } diff --git a/examples/shader/resources/blur.frag b/examples/shader/resources/blur.frag index b8aba3803..365f2290e 100644 --- a/examples/shader/resources/blur.frag +++ b/examples/shader/resources/blur.frag @@ -1,20 +1,23 @@ uniform sampler2D texture; uniform float blur_radius; +varying vec4 texCoord; +varying vec4 frontColor; + void main() { vec2 offx = vec2(blur_radius, 0.0); vec2 offy = vec2(0.0, blur_radius); - vec4 pixel = texture2D(texture, gl_TexCoord[0].xy) * 4.0 + - texture2D(texture, gl_TexCoord[0].xy - offx) * 2.0 + - texture2D(texture, gl_TexCoord[0].xy + offx) * 2.0 + - texture2D(texture, gl_TexCoord[0].xy - offy) * 2.0 + - texture2D(texture, gl_TexCoord[0].xy + offy) * 2.0 + - texture2D(texture, gl_TexCoord[0].xy - offx - offy) * 1.0 + - texture2D(texture, gl_TexCoord[0].xy - offx + offy) * 1.0 + - texture2D(texture, gl_TexCoord[0].xy + offx - offy) * 1.0 + - texture2D(texture, gl_TexCoord[0].xy + offx + offy) * 1.0; + vec4 pixel = texture2D(texture, texCoord.xy) * 4.0 + + texture2D(texture, texCoord.xy - offx) * 2.0 + + texture2D(texture, texCoord.xy + offx) * 2.0 + + texture2D(texture, texCoord.xy - offy) * 2.0 + + texture2D(texture, texCoord.xy + offy) * 2.0 + + texture2D(texture, texCoord.xy - offx - offy) * 1.0 + + texture2D(texture, texCoord.xy - offx + offy) * 1.0 + + texture2D(texture, texCoord.xy + offx - offy) * 1.0 + + texture2D(texture, texCoord.xy + offx + offy) * 1.0; - gl_FragColor = gl_Color * (pixel / 16.0); + gl_FragColor = frontColor * (pixel / 16.0); } diff --git a/examples/shader/resources/edge.frag b/examples/shader/resources/edge.frag index 7f869f534..fe0a269ab 100644 --- a/examples/shader/resources/edge.frag +++ b/examples/shader/resources/edge.frag @@ -1,29 +1,32 @@ uniform sampler2D texture; uniform float edge_threshold; +varying vec4 texCoord; +varying vec4 frontColor; + void main() { const float offset = 1.0 / 512.0; vec2 offx = vec2(offset, 0.0); vec2 offy = vec2(0.0, offset); - vec4 hEdge = texture2D(texture, gl_TexCoord[0].xy - offy) * -2.0 + - texture2D(texture, gl_TexCoord[0].xy + offy) * 2.0 + - texture2D(texture, gl_TexCoord[0].xy - offx - offy) * -1.0 + - texture2D(texture, gl_TexCoord[0].xy - offx + offy) * 1.0 + - texture2D(texture, gl_TexCoord[0].xy + offx - offy) * -1.0 + - texture2D(texture, gl_TexCoord[0].xy + offx + offy) * 1.0; + vec4 hEdge = texture2D(texture, texCoord.xy - offy) * -2.0 + + texture2D(texture, texCoord.xy + offy) * 2.0 + + texture2D(texture, texCoord.xy - offx - offy) * -1.0 + + texture2D(texture, texCoord.xy - offx + offy) * 1.0 + + texture2D(texture, texCoord.xy + offx - offy) * -1.0 + + texture2D(texture, texCoord.xy + offx + offy) * 1.0; - vec4 vEdge = texture2D(texture, gl_TexCoord[0].xy - offx) * 2.0 + - texture2D(texture, gl_TexCoord[0].xy + offx) * -2.0 + - texture2D(texture, gl_TexCoord[0].xy - offx - offy) * 1.0 + - texture2D(texture, gl_TexCoord[0].xy - offx + offy) * -1.0 + - texture2D(texture, gl_TexCoord[0].xy + offx - offy) * 1.0 + - texture2D(texture, gl_TexCoord[0].xy + offx + offy) * -1.0; + vec4 vEdge = texture2D(texture, texCoord.xy - offx) * 2.0 + + texture2D(texture, texCoord.xy + offx) * -2.0 + + texture2D(texture, texCoord.xy - offx - offy) * 1.0 + + texture2D(texture, texCoord.xy - offx + offy) * -1.0 + + texture2D(texture, texCoord.xy + offx - offy) * 1.0 + + texture2D(texture, texCoord.xy + offx + offy) * -1.0; vec3 result = sqrt(hEdge.rgb * hEdge.rgb + vEdge.rgb * vEdge.rgb); float edge = length(result); - vec4 pixel = gl_Color * texture2D(texture, gl_TexCoord[0].xy); + vec4 pixel = frontColor * texture2D(texture, texCoord.xy); if (edge > (edge_threshold * 8.0)) pixel.rgb = vec3(0.0, 0.0, 0.0); else diff --git a/examples/shader/resources/edge.vert b/examples/shader/resources/edge.vert new file mode 100644 index 000000000..3dbbc1098 --- /dev/null +++ b/examples/shader/resources/edge.vert @@ -0,0 +1,19 @@ +attribute vec2 positionAttribute; +attribute vec4 colorAttribute; +attribute vec2 texCoordAttribute; + +uniform mat4 modelViewProjectionMatrix; +uniform mat4 textureMatrix; + +varying vec4 texCoord; +varying vec4 frontColor; + +void main() +{ + // Transform the vertex position + gl_Position = modelViewProjectionMatrix * vec4(positionAttribute, 0.0, 1.0); + + frontColor = colorAttribute; + + texCoord = textureMatrix * vec4(texCoordAttribute, 0.0, 1.0); +} diff --git a/examples/shader/resources/pixelate.frag b/examples/shader/resources/pixelate.frag index 79f88683f..772ebed7c 100644 --- a/examples/shader/resources/pixelate.frag +++ b/examples/shader/resources/pixelate.frag @@ -1,9 +1,12 @@ uniform sampler2D texture; uniform float pixel_threshold; +varying vec4 texCoord; +varying vec4 frontColor; + void main() { float factor = 1.0 / (pixel_threshold + 0.001); - vec2 pos = floor(gl_TexCoord[0].xy * factor + 0.5) / factor; - gl_FragColor = texture2D(texture, pos) * gl_Color; + vec2 pos = floor(texCoord.xy * factor + 0.5) / factor; + gl_FragColor = texture2D(texture, pos) * frontColor; } diff --git a/examples/shader/resources/pixelate.vert b/examples/shader/resources/pixelate.vert new file mode 100644 index 000000000..9fa9e63c0 --- /dev/null +++ b/examples/shader/resources/pixelate.vert @@ -0,0 +1,16 @@ +attribute vec2 positionAttribute; +attribute vec4 colorAttribute; +attribute vec2 texCoordAttribute; + +uniform mat4 modelViewProjectionMatrix; +uniform mat4 textureMatrix; + +varying vec4 texCoord; +varying vec4 frontColor; + +void main() +{ + gl_Position = modelViewProjectionMatrix * vec4(positionAttribute, 0.0, 1.0); + texCoord = textureMatrix * vec4(texCoordAttribute, 0.0, 1.0); + frontColor = colorAttribute; +} diff --git a/examples/shader/resources/storm.vert b/examples/shader/resources/storm.vert index fab9da41b..3da10baad 100644 --- a/examples/shader/resources/storm.vert +++ b/examples/shader/resources/storm.vert @@ -2,9 +2,17 @@ uniform vec2 storm_position; uniform float storm_total_radius; uniform float storm_inner_radius; +attribute vec2 positionAttribute; +attribute vec4 colorAttribute; + +uniform mat4 modelViewMatrix; +uniform mat4 projectionMatrix; + +varying vec4 frontColor; + void main() { - vec4 vertex = gl_ModelViewMatrix * gl_Vertex; + vec4 vertex = modelViewMatrix * vec4(positionAttribute, 0.0, 1.0); vec2 offset = vertex.xy - storm_position; float len = length(offset); if (len < storm_total_radius) @@ -13,7 +21,6 @@ void main() vertex.xy = storm_position + normalize(offset) * push_distance; } - gl_Position = gl_ProjectionMatrix * vertex; - gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; - gl_FrontColor = gl_Color; + gl_Position = projectionMatrix * vertex; + frontColor = colorAttribute; } diff --git a/examples/shader/resources/wave.vert b/examples/shader/resources/wave.vert index 278a53b45..2ed1975e8 100644 --- a/examples/shader/resources/wave.vert +++ b/examples/shader/resources/wave.vert @@ -1,15 +1,25 @@ uniform float wave_phase; uniform vec2 wave_amplitude; +attribute vec2 positionAttribute; +attribute vec4 colorAttribute; +attribute vec2 texCoordAttribute; + +uniform mat4 modelViewProjectionMatrix; +uniform mat4 textureMatrix; + +varying vec4 texCoord; +varying vec4 frontColor; + void main() { - vec4 vertex = gl_Vertex; - vertex.x += cos(gl_Vertex.y * 0.02 + wave_phase * 3.8) * wave_amplitude.x - + sin(gl_Vertex.y * 0.02 + wave_phase * 6.3) * wave_amplitude.x * 0.3; - vertex.y += sin(gl_Vertex.x * 0.02 + wave_phase * 2.4) * wave_amplitude.y - + cos(gl_Vertex.x * 0.02 + wave_phase * 5.2) * wave_amplitude.y * 0.3; + vec4 vertex = vec4(positionAttribute, 0.0, 1.0); + vertex.x += cos(positionAttribute.y * 0.02 + wave_phase * 3.8) * wave_amplitude.x + + sin(positionAttribute.y * 0.02 + wave_phase * 6.3) * wave_amplitude.x * 0.3; + vertex.y += sin(positionAttribute.x * 0.02 + wave_phase * 2.4) * wave_amplitude.y + + cos(positionAttribute.x * 0.02 + wave_phase * 5.2) * wave_amplitude.y * 0.3; - gl_Position = gl_ModelViewProjectionMatrix * vertex; - gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; - gl_FrontColor = gl_Color; + gl_Position = modelViewProjectionMatrix * vertex; + texCoord = textureMatrix * vec4(texCoordAttribute, 0.0, 1.0); + frontColor = colorAttribute; } diff --git a/include/SFML/Graphics/Shader.hpp b/include/SFML/Graphics/Shader.hpp index 664dd865f..2a9fcd885 100644 --- a/include/SFML/Graphics/Shader.hpp +++ b/include/SFML/Graphics/Shader.hpp @@ -53,6 +53,17 @@ class SFML_GRAPHICS_API Shader : GlResource, NonCopyable { public: + //////////////////////////////////////////////////////////// + /// \brief Vertex attribute indices + /// + //////////////////////////////////////////////////////////// + enum VertexAttributeIndices + { + PositionIndex = 0, ///< Position attribute index + ColorIndex = 1, ///< Color attribute index + TextureCoordinateIndex = 2 ///< Texture coordinate attribute index + }; + //////////////////////////////////////////////////////////// /// \brief Types of shaders /// @@ -287,6 +298,118 @@ public: //////////////////////////////////////////////////////////// bool loadFromStream(InputStream& vertexShaderStream, InputStream& geometryShaderStream, InputStream& fragmentShaderStream); + //////////////////////////////////////////////////////////// + /// \brief Specify the name of the position attribute in GLSL + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// + /// \param name Name of the position attribute in GLSL + /// + /// \return Reference to *this + /// + /// \see setColorAttribute, setTextureCoordinateAttribute, setModelViewMatrix, + /// setProjectionMatrix, setModelViewProjectionMatrix, setTextureMatrix + /// + //////////////////////////////////////////////////////////// + Shader& setPositionAttribute(const std::string& name); + + //////////////////////////////////////////////////////////// + /// \brief Specify the name of the color attribute in GLSL + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// + /// \param name Name of the color attribute in GLSL + /// + /// \return Reference to *this + /// + /// \see setPositionAttribute, setTextureCoordinateAttribute, setModelViewMatrix, + /// setProjectionMatrix, setModelViewProjectionMatrix, setTextureMatrix + /// + //////////////////////////////////////////////////////////// + Shader& setColorAttribute(const std::string& name); + + //////////////////////////////////////////////////////////// + /// \brief Specify the name of the texture coordinate attribute in GLSL + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// + /// \param name Name of the texture coordinate attribute in GLSL + /// + /// \return Reference to *this + /// + /// \see setPositionAttribute, setColorAttribute, setModelViewMatrix, + /// setProjectionMatrix, setModelViewProjectionMatrix, setTextureMatrix + /// + //////////////////////////////////////////////////////////// + Shader& setTextureCoordinateAttribute(const std::string& name); + + //////////////////////////////////////////////////////////// + /// \brief Specify the name of the modelview matrix uniform in GLSL + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// + /// \param name Name of the modelview matrix uniform in GLSL + /// + /// \return Reference to *this + /// + /// \see setPositionAttribute, setColorAttribute, setTextureCoordinateAttribute, + /// setProjectionMatrix, setModelViewProjectionMatrix, setTextureMatrix + /// + //////////////////////////////////////////////////////////// + Shader& setModelViewMatrix(const std::string& name); + + //////////////////////////////////////////////////////////// + /// \brief Specify the name of the projection matrix uniform in GLSL + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// + /// \param name Name of the projection matrix uniform in GLSL + /// + /// \return Reference to *this + /// + /// \see setPositionAttribute, setColorAttribute, setTextureCoordinateAttribute, + /// setModelViewMatrix, setModelViewProjectionMatrix, setTextureMatrix + /// + //////////////////////////////////////////////////////////// + Shader& setProjectionMatrix(const std::string& name); + + //////////////////////////////////////////////////////////// + /// \brief Specify the name of the pre-multiplied modelview-projection matrix uniform in GLSL + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// + /// \param name Name of the pre-multiplied modelview-projection matrix uniform in GLSL + /// + /// \return Reference to *this + /// + /// \see setPositionAttribute, setColorAttribute, setTextureCoordinateAttribute, + /// setModelViewMatrix, setProjectionMatrix, setTextureMatrix + /// + //////////////////////////////////////////////////////////// + Shader& setModelViewProjectionMatrix(const std::string& name); + + //////////////////////////////////////////////////////////// + /// \brief Specify the name of the texture matrix uniform in GLSL + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// + /// \param name Name of the texture matrix uniform in GLSL + /// + /// \return Reference to *this + /// + /// \see setPositionAttribute, setColorAttribute, setTextureCoordinateAttribute, + /// setModelViewMatrix, setProjectionMatrix, setModelViewProjectionMatrix + /// + //////////////////////////////////////////////////////////// + Shader& setTextureMatrix(const std::string& name); + //////////////////////////////////////////////////////////// /// \brief Specify value for \p float uniform /// @@ -693,6 +816,8 @@ public: private: + friend class RenderTarget; + //////////////////////////////////////////////////////////// /// \brief Compile the shader(s) and create the program /// @@ -736,6 +861,14 @@ private: //////////////////////////////////////////////////////////// struct UniformBinder; + //////////////////////////////////////////////////////////// + /// \brief Shader to Drawable interface specification + /// + /// Implementation is private in the .cpp file. + /// + //////////////////////////////////////////////////////////// + struct Interface; + //////////////////////////////////////////////////////////// // Types //////////////////////////////////////////////////////////// @@ -745,10 +878,15 @@ private: //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - unsigned int m_shaderProgram; ///< OpenGL identifier for the program - int m_currentTexture; ///< Location of the current texture in the shader - TextureTable m_textures; ///< Texture variables in the shader, mapped to their location - UniformTable m_uniforms; ///< Parameters location cache + unsigned int m_shaderProgram; ///< OpenGL identifier for the program + int m_currentTexture; ///< Location of the current texture in the shader + TextureTable m_textures; ///< Texture variables in the shader, mapped to their location + UniformTable m_uniforms; ///< Parameters location cache + int m_modelViewMatrixIndex; ///< Index of the modelview matrix + int m_projectionMatrixIndex; ///< Index of the projection matrix + int m_modelViewProjectionMatrixIndex; ///< Index of the modelview-projection matrix + int m_textureMatrixIndex; ///< Index of the texture matrix + Interface* m_interface; ///< Shader to drawable interface specification }; } // namespace sf diff --git a/src/SFML/Graphics/GLExtensions.hpp b/src/SFML/Graphics/GLExtensions.hpp index 687cf3762..f6d9e8599 100644 --- a/src/SFML/Graphics/GLExtensions.hpp +++ b/src/SFML/Graphics/GLExtensions.hpp @@ -245,6 +245,11 @@ // Core since 2.0 - ARB_vertex_shader #define GLEXT_vertex_shader sfogl_ext_ARB_vertex_shader + #define GLEXT_glVertexAttribPointer glVertexAttribPointerARB + #define GLEXT_glEnableVertexAttribArray glEnableVertexAttribArrayARB + #define GLEXT_glDisableVertexAttribArray glDisableVertexAttribArrayARB + #define GLEXT_glBindAttribLocation glBindAttribLocationARB + #define GLEXT_glGetAttribLocation glGetAttribLocationARB #define GLEXT_GL_VERTEX_SHADER GL_VERTEX_SHADER_ARB #define GLEXT_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB diff --git a/src/SFML/Graphics/RenderTarget.cpp b/src/SFML/Graphics/RenderTarget.cpp index 59e9e202f..c1b3bb04c 100644 --- a/src/SFML/Graphics/RenderTarget.cpp +++ b/src/SFML/Graphics/RenderTarget.cpp @@ -147,7 +147,19 @@ void RenderTarget::clear(const Color& color) if (isActive(m_id) || setActive(true)) { // Unbind texture to fix RenderTexture preventing clear - applyTexture(NULL); + { + // Bind no texture + glCheck(glBindTexture(GL_TEXTURE_2D, 0)); + + // Reset the texture matrix + glCheck(glMatrixMode(GL_TEXTURE)); + glCheck(glLoadIdentity()); + + // Go back to model-view mode + glCheck(glMatrixMode(GL_MODELVIEW)); + + m_cache.lastTextureId = 0; + } glCheck(glClearColor(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f)); glCheck(glClear(GL_COLOR_BUFFER_BIT)); @@ -283,9 +295,19 @@ void RenderTarget::draw(const Vertex* vertices, std::size_t vertexCount, if (!m_cache.enable || (enableTexCoordsArray != m_cache.texCoordsArrayEnabled)) { if (enableTexCoordsArray) + { glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + + if (Shader::isAvailable()) + glCheck(GLEXT_glEnableVertexAttribArray(Shader::TextureCoordinateIndex)); + } else + { glCheck(glDisableClientState(GL_TEXTURE_COORD_ARRAY)); + + if (Shader::isAvailable()) + glCheck(GLEXT_glDisableVertexAttribArray(Shader::TextureCoordinateIndex)); + } } // If we switch between non-cache and cache mode or enable texture @@ -302,6 +324,15 @@ void RenderTarget::draw(const Vertex* vertices, std::size_t vertexCount, glCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), data + 8)); if (enableTexCoordsArray) glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + 12)); + + if (Shader::isAvailable()) + { + glCheck(GLEXT_glVertexAttribPointer(Shader::PositionIndex, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), data + 0)); + glCheck(GLEXT_glVertexAttribPointer(Shader::ColorIndex, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), data + 8)); + + if (enableTexCoordsArray) + glCheck(GLEXT_glVertexAttribPointer(Shader::TextureCoordinateIndex, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), data + 12)); + } } else if (enableTexCoordsArray && !m_cache.texCoordsArrayEnabled) { @@ -309,6 +340,9 @@ void RenderTarget::draw(const Vertex* vertices, std::size_t vertexCount, const char* data = reinterpret_cast(m_cache.vertexCache); glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + 12)); + + if (Shader::isAvailable()) + glCheck(GLEXT_glVertexAttribPointer(Shader::TextureCoordinateIndex, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), data + 12)); } drawPrimitives(type, 0, vertexCount); @@ -368,12 +402,24 @@ void RenderTarget::draw(const VertexBuffer& vertexBuffer, std::size_t firstVerte // Always enable texture coordinates if (!m_cache.enable || !m_cache.texCoordsArrayEnabled) + { glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + if (Shader::isAvailable()) + glCheck(GLEXT_glEnableVertexAttribArray(Shader::TextureCoordinateIndex)); + } + glCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast(0))); glCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), reinterpret_cast(8))); glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast(12))); + if (Shader::isAvailable()) + { + glCheck(GLEXT_glVertexAttribPointer(Shader::PositionIndex, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(0))); + glCheck(GLEXT_glVertexAttribPointer(Shader::ColorIndex, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), reinterpret_cast(8))); + glCheck(GLEXT_glVertexAttribPointer(Shader::TextureCoordinateIndex, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(12))); + } + drawPrimitives(vertexBuffer.getPrimitiveType(), firstVertex, vertexCount); // Unbind vertex buffer @@ -519,10 +565,29 @@ void RenderTarget::resetGLStates() // Apply the default SFML states applyBlendMode(BlendAlpha); - applyTexture(NULL); + + // Bind no texture + glCheck(glBindTexture(GL_TEXTURE_2D, 0)); + + // Reset the texture matrix + glCheck(glMatrixMode(GL_TEXTURE)); + glCheck(glLoadIdentity()); + + // Go back to model-view mode + glCheck(glMatrixMode(GL_MODELVIEW)); + + m_cache.lastTextureId = 0; + if (shaderAvailable) + { applyShader(NULL); + // Enable all available vertex attribute arrays + glCheck(GLEXT_glEnableVertexAttribArray(Shader::PositionIndex)); + glCheck(GLEXT_glEnableVertexAttribArray(Shader::ColorIndex)); + glCheck(GLEXT_glEnableVertexAttribArray(Shader::TextureCoordinateIndex)); + } + if (vertexBufferAvailable) glCheck(VertexBuffer::bind(NULL)); @@ -622,23 +687,16 @@ void RenderTarget::applyBlendMode(const BlendMode& mode) //////////////////////////////////////////////////////////// -void RenderTarget::applyTransform(const Transform& transform) +void RenderTarget::applyTransform(const Transform&) { - // No need to call glMatrixMode(GL_MODELVIEW), it is always the - // current mode (for optimization purpose, since it's the most used) - if (transform == Transform::Identity) - glCheck(glLoadIdentity()); - else - glCheck(glLoadMatrixf(transform.getMatrix())); + // Function kept for compatibility, no-op now } //////////////////////////////////////////////////////////// -void RenderTarget::applyTexture(const Texture* texture) +void RenderTarget::applyTexture(const Texture*) { - Texture::bind(texture, Texture::Pixels); - - m_cache.lastTextureId = texture ? texture->m_cacheId : 0; + // Function kept for compatibility, no-op now } @@ -652,19 +710,45 @@ void RenderTarget::applyShader(const Shader* shader) //////////////////////////////////////////////////////////// void RenderTarget::setupDraw(bool useVertexCache, const RenderStates& states) { + static const GLfloat identityMatrix[] = + { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + // First set the persistent OpenGL states if it's the very first call if (!m_cache.glStatesSet) resetGLStates(); + const GLfloat* modelViewMatrix = identityMatrix; + if (useVertexCache) { // Since vertices are transformed, we must use an identity transform to render them - if (!m_cache.enable || !m_cache.useVertexCache) + if (!m_cache.enable || + !m_cache.useVertexCache || + (states.shader && (states.shader->m_modelViewMatrixIndex >= 0))) + { glCheck(glLoadIdentity()); + modelViewMatrix = identityMatrix; + } } else { - applyTransform(states.transform); + // No need to call glMatrixMode(GL_MODELVIEW), it is always the + // current mode (for optimization purpose, since it's the most used) + if (states.transform == Transform::Identity) + { + glCheck(glLoadIdentity()); + modelViewMatrix = identityMatrix; + } + else + { + glCheck(glLoadMatrixf(states.transform.getMatrix())); + modelViewMatrix = states.transform.getMatrix(); + } } // Apply the view @@ -675,8 +759,12 @@ void RenderTarget::setupDraw(bool useVertexCache, const RenderStates& states) if (!m_cache.enable || (states.blendMode != m_cache.lastBlendMode)) applyBlendMode(states.blendMode); + bool applyStatesTexture = false; + // Apply the texture - if (!m_cache.enable || (states.texture && states.texture->m_fboAttachment)) + if (!m_cache.enable || + (states.texture && states.texture->m_fboAttachment) || + (states.shader && (states.shader->m_textureMatrixIndex >= 0))) { // If the texture is an FBO attachment, always rebind it // in order to inform the OpenGL driver that we want changes @@ -684,18 +772,105 @@ void RenderTarget::setupDraw(bool useVertexCache, const RenderStates& states) // This saves us from having to call glFlush() in // RenderTextureImplFBO which can be quite costly // See: https://www.khronos.org/opengl/wiki/Memory_Model - applyTexture(states.texture); + applyStatesTexture = true; } else { Uint64 textureId = states.texture ? states.texture->m_cacheId : 0; if (textureId != m_cache.lastTextureId) - applyTexture(states.texture); + applyStatesTexture = true; + } + + // Temporary matrix we use to store both the texture matrix + // and if required the pre-multiplied modelview-projection matrix + GLfloat tempMatrix[16] = + { + 1.f, 0.f, 0.f, 0.f, + 0.f, 1.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 0.f, 0.f, 0.f, 1.f + }; + + const GLfloat* textureMatrix = identityMatrix; + + if (applyStatesTexture) + { + if (states.texture && states.texture->m_texture) + { + // Bind the texture + glCheck(glBindTexture(GL_TEXTURE_2D, states.texture->m_texture)); + + // If non-normalized coordinates (= pixels) are requested, we need to + // setup scale factors that convert the range [0 .. size] to [0 .. 1] + tempMatrix[0] = 1.f / states.texture->m_actualSize.x; + tempMatrix[5] = 1.f / states.texture->m_actualSize.y; + + // If pixels are flipped we must invert the Y axis + if (states.texture->m_pixelsFlipped) + { + tempMatrix[5] = -tempMatrix[5]; + tempMatrix[13] = static_cast(states.texture->m_size.y) / states.texture->m_actualSize.y; + } + + // Load the matrix + glCheck(glMatrixMode(GL_TEXTURE)); + glCheck(glLoadMatrixf(tempMatrix)); + textureMatrix = tempMatrix; + + // Go back to model-view mode + glCheck(glMatrixMode(GL_MODELVIEW)); + } + else + { + // Bind no texture + glCheck(glBindTexture(GL_TEXTURE_2D, 0)); + + // Reset the texture matrix + glCheck(glMatrixMode(GL_TEXTURE)); + glCheck(glLoadIdentity()); + textureMatrix = identityMatrix; + + // Go back to model-view mode + glCheck(glMatrixMode(GL_MODELVIEW)); + } + + m_cache.lastTextureId = states.texture ? states.texture->m_cacheId : 0; } // Apply the shader if (states.shader) + { applyShader(states.shader); + + // Set all available uniforms + if (states.shader->m_modelViewMatrixIndex >= 0) + glCheck(GLEXT_glUniformMatrix4fv(states.shader->m_modelViewMatrixIndex, 1, GL_FALSE, modelViewMatrix)); + + if (states.shader->m_projectionMatrixIndex >= 0) + glCheck(GLEXT_glUniformMatrix4fv(states.shader->m_projectionMatrixIndex, 1, GL_FALSE, m_view.getTransform().getMatrix())); + + if (states.shader->m_textureMatrixIndex >= 0) + glCheck(GLEXT_glUniformMatrix4fv(states.shader->m_textureMatrixIndex, 1, GL_FALSE, textureMatrix)); + + if (states.shader->m_modelViewProjectionMatrixIndex >= 0) + { + const GLfloat* A = m_view.getTransform().getMatrix(); + const GLfloat* B = modelViewMatrix; + + for (int i = 0; i < 4 * 4; i += 4) + { + for (int j = 0; j < 4; ++j) + { + tempMatrix[i + j] = A[4 * 0 + j] * B[i + 0] + + A[4 * 1 + j] * B[i + 1] + + A[4 * 2 + j] * B[i + 2] + + A[4 * 3 + j] * B[i + 3]; + } + } + + glCheck(GLEXT_glUniformMatrix4fv(states.shader->m_modelViewProjectionMatrixIndex, 1, GL_FALSE, tempMatrix)); + } + } } @@ -722,7 +897,19 @@ void RenderTarget::cleanupDraw(const RenderStates& states) // If the texture we used to draw belonged to a RenderTexture, then forcibly unbind that texture. // This prevents a bug where some drivers do not clear RenderTextures properly. if (states.texture && states.texture->m_fboAttachment) - applyTexture(NULL); + { + // Bind no texture + glCheck(glBindTexture(GL_TEXTURE_2D, 0)); + + // Reset the texture matrix + glCheck(glMatrixMode(GL_TEXTURE)); + glCheck(glLoadIdentity()); + + // Go back to model-view mode + glCheck(glMatrixMode(GL_MODELVIEW)); + + m_cache.lastTextureId = 0; + } // Re-enable the cache at the end of the draw if it was disabled m_cache.enable = true; diff --git a/src/SFML/Graphics/Shader.cpp b/src/SFML/Graphics/Shader.cpp index 353a72dfa..4e5e98dcc 100644 --- a/src/SFML/Graphics/Shader.cpp +++ b/src/SFML/Graphics/Shader.cpp @@ -218,12 +218,29 @@ struct Shader::UniformBinder : private NonCopyable }; +//////////////////////////////////////////////////////////// +struct Shader::Interface +{ + std::string positionAttribute; + std::string colorAttribute; + std::string texCoordAttribute; + + std::string modelViewMatrix; + std::string projectionMatrix; + std::string modelViewProjectionMatrix; + std::string textureMatrix; +}; + + //////////////////////////////////////////////////////////// Shader::Shader() : -m_shaderProgram (0), -m_currentTexture(-1), -m_textures (), -m_uniforms () +m_shaderProgram (0), +m_currentTexture (-1), +m_modelViewMatrixIndex (-1), +m_projectionMatrixIndex (-1), +m_modelViewProjectionMatrixIndex(-1), +m_textureMatrixIndex (-1), +m_interface (0) { } @@ -231,6 +248,9 @@ m_uniforms () //////////////////////////////////////////////////////////// Shader::~Shader() { + // Destroy the interface specification if we created one + delete m_interface; + TransientContextLock lock; // Destroy effect program @@ -422,6 +442,90 @@ bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& geomet } +//////////////////////////////////////////////////////////// +Shader& Shader::setPositionAttribute(const std::string& positionAttribute) +{ + if (!m_interface) + m_interface = new Interface; + + m_interface->positionAttribute = positionAttribute; + + return *this; +} + + +//////////////////////////////////////////////////////////// +Shader& Shader::setColorAttribute(const std::string& name) +{ + if (!m_interface) + m_interface = new Interface; + + m_interface->colorAttribute = name; + + return *this; +} + + +//////////////////////////////////////////////////////////// +Shader& Shader::setTextureCoordinateAttribute(const std::string& name) +{ + if (!m_interface) + m_interface = new Interface; + + m_interface->texCoordAttribute = name; + + return *this; +} + + +//////////////////////////////////////////////////////////// +Shader& Shader::setModelViewMatrix(const std::string& name) +{ + if (!m_interface) + m_interface = new Interface; + + m_interface->modelViewMatrix = name; + + return *this; +} + + +//////////////////////////////////////////////////////////// +Shader& Shader::setProjectionMatrix(const std::string& name) +{ + if (!m_interface) + m_interface = new Interface; + + m_interface->projectionMatrix = name; + + return *this; +} + + +//////////////////////////////////////////////////////////// +Shader& Shader::setModelViewProjectionMatrix(const std::string& name) +{ + if (!m_interface) + m_interface = new Interface; + + m_interface->modelViewProjectionMatrix = name; + + return *this; +} + + +//////////////////////////////////////////////////////////// +Shader& Shader::setTextureMatrix(const std::string& name) +{ + if (!m_interface) + m_interface = new Interface; + + m_interface->textureMatrix = name; + + return *this; +} + + //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, float x) { @@ -855,6 +959,11 @@ bool Shader::compile(const char* vertexShaderCode, const char* geometryShaderCod m_textures.clear(); m_uniforms.clear(); + m_modelViewMatrixIndex = -1; + m_projectionMatrixIndex = -1; + m_modelViewProjectionMatrixIndex = -1; + m_textureMatrixIndex = -1; + // Create the program GLEXT_GLhandle shaderProgram; glCheck(shaderProgram = GLEXT_glCreateProgramObject()); @@ -942,6 +1051,19 @@ bool Shader::compile(const char* vertexShaderCode, const char* geometryShaderCod glCheck(GLEXT_glDeleteObject(fragmentShader)); } + // Bind all user-specified attributes to our pre-defined indices + if (m_interface) + { + if (!m_interface->positionAttribute.empty()) + glCheck(GLEXT_glBindAttribLocation(shaderProgram, PositionIndex, m_interface->positionAttribute.c_str())); + + if (!m_interface->colorAttribute.empty()) + glCheck(GLEXT_glBindAttribLocation(shaderProgram, ColorIndex, m_interface->colorAttribute.c_str())); + + if (!m_interface->texCoordAttribute.empty()) + glCheck(GLEXT_glBindAttribLocation(shaderProgram, TextureCoordinateIndex, m_interface->texCoordAttribute.c_str())); + } + // Link the program glCheck(GLEXT_glLinkProgram(shaderProgram)); @@ -960,6 +1082,53 @@ bool Shader::compile(const char* vertexShaderCode, const char* geometryShaderCod m_shaderProgram = castFromGlHandle(shaderProgram); + if (m_interface) + { + // Check if attribute binding resulted in the attributes having the indices we expect + if (!m_interface->positionAttribute.empty()) + { + int location = -1; + + glCheck(location = GLEXT_glGetAttribLocation(shaderProgram, m_interface->positionAttribute.c_str())); + + if (location != PositionIndex) + err() << "Failed to bind vertex position attribute to index 0" << std::endl; + } + + if (!m_interface->colorAttribute.empty()) + { + int location = -1; + + glCheck(location = GLEXT_glGetAttribLocation(shaderProgram, m_interface->colorAttribute.c_str())); + + if (location != ColorIndex) + err() << "Failed to bind vertex color attribute to index 1" << std::endl; + } + + if (!m_interface->texCoordAttribute.empty()) + { + int location = -1; + + glCheck(location = GLEXT_glGetAttribLocation(shaderProgram, m_interface->texCoordAttribute.c_str())); + + if (location != TextureCoordinateIndex) + err() << "Failed to bind vertex texture coordinate attribute to index 2" << std::endl; + } + + // Populate the drawable interface uniform indices + if (!m_interface->modelViewMatrix.empty()) + m_modelViewMatrixIndex = getUniformLocation(m_interface->modelViewMatrix); + + if (!m_interface->projectionMatrix.empty()) + m_projectionMatrixIndex = getUniformLocation(m_interface->projectionMatrix); + + if (!m_interface->modelViewProjectionMatrix.empty()) + m_modelViewProjectionMatrixIndex = getUniformLocation(m_interface->modelViewProjectionMatrix); + + if (!m_interface->textureMatrix.empty()) + m_textureMatrixIndex = getUniformLocation(m_interface->textureMatrix); + } + // Force an OpenGL flush, so that the shader will appear updated // in all contexts immediately (solves problems in multi-threaded apps) glCheck(glFlush());