/* ResidualVM - A 3D game interpreter * * ResidualVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the AUTHORS * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "common/scummsys.h" #if defined(USE_OPENGL) #include "graphics/opengl/surfacerenderer.h" #include "graphics/opengl/context.h" #include "graphics/opengl/texture.h" #if defined(USE_OPENGL_SHADERS) || defined(USE_GLES2) #include "graphics/opengl/shader.h" #endif namespace OpenGL { SurfaceRenderer *createBestSurfaceRenderer() { #if defined(USE_OPENGL_SHADERS) || defined(USE_GLES2) if (OpenGLContext.shadersSupported) { return new ShaderSurfaceRenderer(); } #endif #ifndef USE_GLES2 return new FixedSurfaceRenderer(); #else error("Could not create an appropriate surface renderer for the current OpenGL context"); #endif } SurfaceRenderer::SurfaceRenderer() : _flipY(false), _alphaBlending(false) { } SurfaceRenderer::~SurfaceRenderer() { } void SurfaceRenderer::setFlipY(bool flipY) { _flipY = flipY; } void SurfaceRenderer::enableAlphaBlending(bool enable) { if (_alphaBlending != enable) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { glDisable(GL_BLEND); } _alphaBlending = enable; } #ifndef USE_GLES2 FixedSurfaceRenderer::~FixedSurfaceRenderer() { } void FixedSurfaceRenderer::prepareState() { // Save current state glPushAttrib(GL_TRANSFORM_BIT | GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_SCISSOR_BIT); // prepare view glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, 1.0, 1.0, 0, 0, 1); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_TEXTURE); glPushMatrix(); glLoadIdentity(); glDisable(GL_LIGHTING); glEnable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); glDisable(GL_ALPHA_TEST); glDepthMask(GL_FALSE); } void FixedSurfaceRenderer::render(const Texture *tex, const Math::Rect2d &dest) { float texcropX = tex->getWidth() / float(tex->getTexWidth()); float texcropY = tex->getHeight() / float(tex->getTexHeight()); float texTop = _flipY ? 0.0f : texcropY; float texBottom = _flipY ? texcropY : 0.0f; float offsetX = dest.getTopLeft().getX(); float offsetY = _flipY ? dest.getTopLeft().getY() : 1.0f - dest.getTopLeft().getY() - dest.getHeight(); float sizeX = fabsf(dest.getWidth()); float sizeY = fabsf(dest.getHeight()); glColor4f(1.0, 1.0, 1.0, 1.0); glBindTexture(GL_TEXTURE_2D, tex->getTextureName()); glBegin(GL_QUADS); glTexCoord2f(0, texTop); glVertex2f(offsetX, offsetY); glTexCoord2f(texcropX, texTop); glVertex2f(offsetX + sizeX, offsetY); glTexCoord2f(texcropX, texBottom); glVertex2f(offsetX + sizeX, offsetY + sizeY); glTexCoord2f(0.0, texBottom); glVertex2f(offsetX, offsetY + sizeY); glEnd(); } void FixedSurfaceRenderer::restorePreviousState() { glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glMatrixMode(GL_TEXTURE); glPopMatrix(); glPopAttrib(); _flipY = false; _alphaBlending = false; } #endif #if defined(USE_OPENGL_SHADERS) || defined(USE_GLES2) ShaderSurfaceRenderer::ShaderSurfaceRenderer() { const GLfloat vertices[] = { 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, }; // Setup the box shader used to render the overlay const char *attributes[] = { "position", "texcoord", nullptr }; _boxShader = Shader::fromStrings("box", BuiltinShaders::boxVertex, BuiltinShaders::boxFragment, attributes); _boxVerticesVBO = Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(vertices), vertices); _boxShader->enableVertexAttribute("position", _boxVerticesVBO, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(float), 0); _boxShader->enableVertexAttribute("texcoord", _boxVerticesVBO, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(float), 0); } void ShaderSurfaceRenderer::prepareState() { _boxShader->use(); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); } void ShaderSurfaceRenderer::render(const Texture *tex, const Math::Rect2d &dest) { glBindTexture(GL_TEXTURE_2D, tex->getTextureName()); float texcropX = tex->getWidth() / float(tex->getTexWidth()); float texcropY = tex->getHeight() / float(tex->getTexHeight()); _boxShader->setUniform("texcrop", Math::Vector2d(texcropX, texcropY)); _boxShader->setUniform("flipY", _flipY); _boxShader->setUniform("offsetXY", dest.getTopLeft()); _boxShader->setUniform("sizeWH", Math::Vector2d(fabsf(dest.getWidth()), fabsf(dest.getHeight()))); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glBindTexture(GL_TEXTURE_2D, 0); } void ShaderSurfaceRenderer::restorePreviousState() { glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); _flipY = false; if (_alphaBlending) { enableAlphaBlending(false); } _boxShader->unbind(); } ShaderSurfaceRenderer::~ShaderSurfaceRenderer() { Shader::freeBuffer(_boxVerticesVBO); delete _boxShader; } #endif } // End of namespace OpenGL #endif