From 933609967a72c38906ae94219a07261a2c656e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Kapu=C5=9Bci=C5=84ski?= Date: Tue, 19 May 2015 14:29:31 +0200 Subject: [PATCH] Added OpenGL 3.3 graphics implementation --- src/CMakeLists.txt | 2 + src/app/app.cpp | 31 +- src/app/app.h | 3 + src/graphics/core/device.h | 6 +- src/graphics/core/nulldevice.cpp | 4 - src/graphics/core/nulldevice.h | 1 - src/graphics/engine/engine.cpp | 8 +- src/graphics/opengl/gl33device.cpp | 1960 ++++++++++++++++++++++++++++ src/graphics/opengl/gl33device.h | 300 +++++ src/graphics/opengl/gldevice.cpp | 193 +-- src/graphics/opengl/gldevice.h | 42 +- src/graphics/opengl/glutil.cpp | 151 +++ src/graphics/opengl/glutil.h | 85 ++ 13 files changed, 2570 insertions(+), 216 deletions(-) create mode 100644 src/graphics/opengl/gl33device.cpp create mode 100644 src/graphics/opengl/gl33device.h create mode 100644 src/graphics/opengl/glutil.cpp create mode 100644 src/graphics/opengl/glutil.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5c2cdb76..12de2912 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -110,6 +110,8 @@ set(BASE_SOURCES graphics/engine/text.cpp graphics/engine/water.cpp graphics/opengl/gldevice.cpp + graphics/opengl/gl33device.cpp + graphics/opengl/glutil.cpp object/auto/auto.cpp object/auto/autobase.cpp object/auto/autoconvert.cpp diff --git a/src/app/app.cpp b/src/app/app.cpp index 5f98b4c0..cb748f50 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -36,6 +36,7 @@ #include "graphics/engine/modelmanager.h" #include "graphics/core/nulldevice.h" #include "graphics/opengl/gldevice.h" +#include "graphics/opengl/gl33device.h" #include "object/robotmain.h" #include "object/objman.h" @@ -162,6 +163,8 @@ CApplication::CApplication() m_language = LANGUAGE_ENV; m_lowCPU = true; + + m_graphics = "opengl"; } CApplication::~CApplication() @@ -224,7 +227,8 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) OPT_MOD, OPT_VBO, OPT_RESOLUTION, - OPT_HEADLESS + OPT_HEADLESS, + OPT_DEVICE }; option options[] = @@ -242,6 +246,7 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) { "vbo", required_argument, nullptr, OPT_VBO }, { "resolution", required_argument, nullptr, OPT_RESOLUTION }, { "headless", no_argument, nullptr, OPT_HEADLESS }, + { "graphics", required_argument, nullptr, OPT_DEVICE }, { nullptr, 0, nullptr, 0} }; @@ -285,6 +290,7 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) GetLogger()->Message(" -vbo mode set OpenGL VBO mode (one of: auto, enable, disable)\n"); GetLogger()->Message(" -resolution WxH set resolution\n"); GetLogger()->Message(" -headless headless mode - disables graphics, sound and user interaction\n"); + GetLogger()->Message(" -graphics changes graphics device (defaults to opengl)\n"); return PARSE_ARGS_HELP; } case OPT_DEBUG: @@ -404,6 +410,11 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) m_headless = true; break; } + case OPT_DEVICE: + { + m_graphics = optarg; + break; + } default: assert(false); // should never get here } @@ -549,12 +560,24 @@ bool CApplication::Create() // Don't generate joystick events SDL_JoystickEventState(SDL_IGNORE); - if(!m_headless) { + if(!m_headless) + { // The video is ready, we can create and initalize the graphics device - m_device = new Gfx::CGLDevice(m_deviceConfig); - } else { + if (m_graphics == "opengl") + m_device = new Gfx::CGLDevice(m_deviceConfig); + else if (m_graphics == "gl33") + m_device = new Gfx::CGL33Device(m_deviceConfig); + else + { + m_device = new Gfx::CNullDevice(); + GetLogger()->Error("Unknown graphics device: %s\n", m_graphics.c_str()); + } + } + else + { m_device = new Gfx::CNullDevice(); } + if (! m_device->Create() ) { m_errorMessage = std::string("Error in CDevice::Create()\n") + standardInfoMessage; diff --git a/src/app/app.h b/src/app/app.h index 651e3be3..7e1ecdb2 100644 --- a/src/app/app.h +++ b/src/app/app.h @@ -425,6 +425,9 @@ protected: bool m_simulationSuspended; //@} + //! Graphics device to use + std::string m_graphics; + //! Current mode of mouse MouseMode m_mouseMode; diff --git a/src/graphics/core/device.h b/src/graphics/core/device.h index ba8db6dc..8db8e335 100644 --- a/src/graphics/core/device.h +++ b/src/graphics/core/device.h @@ -91,7 +91,8 @@ enum TransformType { TRANSFORM_WORLD, TRANSFORM_VIEW, - TRANSFORM_PROJECTION + TRANSFORM_PROJECTION, + TRANSFORM_SHADOW }; /** @@ -302,9 +303,6 @@ public: //! Sets the texture coordinate generation mode for given texture unit virtual void SetTextureCoordGeneration(int index, TextureGenerationParams ¶ms) = 0; - //! Sets texture coordinate transform matrix - virtual void SetTextureMatrix(int index, Math::Matrix& matrix) = 0; - //! Renders primitive composed of vertices with single texture virtual void DrawPrimitive(PrimitiveType type, const Vertex *vertices , int vertexCount, Color color = Color(1.0f, 1.0f, 1.0f, 1.0f)) = 0; diff --git a/src/graphics/core/nulldevice.cpp b/src/graphics/core/nulldevice.cpp index 4ab69d13..26956ab7 100644 --- a/src/graphics/core/nulldevice.cpp +++ b/src/graphics/core/nulldevice.cpp @@ -182,10 +182,6 @@ void CNullDevice::SetTextureCoordGeneration(int index, TextureGenerationParams & { } -void CNullDevice::SetTextureMatrix(int index, Math::Matrix& matrix) -{ -} - TextureStageParams CNullDevice::GetTextureStageParams(int index) { return TextureStageParams(); diff --git a/src/graphics/core/nulldevice.h b/src/graphics/core/nulldevice.h index 9dea04a2..e68b9820 100644 --- a/src/graphics/core/nulldevice.h +++ b/src/graphics/core/nulldevice.h @@ -83,7 +83,6 @@ public: virtual void SetTextureStageWrap(int index, Gfx::TexWrapMode wrapS, Gfx::TexWrapMode wrapT); virtual void SetTextureCoordGeneration(int index, TextureGenerationParams ¶ms); - virtual void SetTextureMatrix(int index, Math::Matrix& matrix); virtual void DrawPrimitive(PrimitiveType type, const Vertex *vertices , int vertexCount, Color color = Color(1.0f, 1.0f, 1.0f, 1.0f)); diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index 38196b1c..bdd454ed 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -2212,7 +2212,7 @@ void CEngine::SetState(int state, const Color& color) if (state & ENG_RSTATE_FOG) m_device->SetRenderState(RENDER_STATE_FOG, true); - + bool second = m_groundSpotVisible || m_dirty; @@ -3255,7 +3255,7 @@ void CEngine::Draw3DScene() // Texture Unit 2 m_device->SetTextureEnabled(2, true); m_device->SetTexture(2, m_shadowMap); - m_device->SetTextureMatrix(2, m_shadowTextureMat); + m_device->SetTransform(TRANSFORM_SHADOW, m_shadowTextureMat); Math::Matrix identity; identity.LoadIdentity(); @@ -3338,7 +3338,7 @@ void CEngine::Draw3DScene() // Texture Unit 2 m_device->SetTextureEnabled(2, true); m_device->SetTexture(2, m_shadowMap); - m_device->SetTextureMatrix(2, m_shadowTextureMat); + m_device->SetTransform(TRANSFORM_SHADOW, m_shadowTextureMat); Math::Matrix identity; identity.LoadIdentity(); @@ -3427,7 +3427,7 @@ void CEngine::Draw3DScene() m_device->SetTexture(2, 0); m_device->SetTextureEnabled(2, false); - m_device->SetTextureMatrix(2, identity); + m_device->SetTransform(TRANSFORM_SHADOW, identity); TextureGenerationParams params; diff --git a/src/graphics/opengl/gl33device.cpp b/src/graphics/opengl/gl33device.cpp new file mode 100644 index 00000000..6092a32e --- /dev/null +++ b/src/graphics/opengl/gl33device.cpp @@ -0,0 +1,1960 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsiteс.ch; http://colobot.info; http://github.com/colobot + * + * 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 3 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, see http://gnu.org/licenses + */ + +#include "graphics/opengl/gl33device.h" +#include "graphics/opengl/glutil.h" +#include "graphics/engine/engine.h" +#include "physfs.h" + +#include "common/config.h" +#include "common/image.h" +#include "common/logger.h" + +#include "math/geometry.h" + + +// Using GLEW so only glew.h is needed +#include + +#include + +#include + + +// Graphics module namespace +namespace Gfx { + +CGL33Device::CGL33Device(const GLDeviceConfig &config) +{ + m_config = config; + m_lighting = false; + m_lastVboId = 0; + m_anisotropyAvailable = false; + m_maxAnisotropy = 1; + m_glMajor = 1; + m_glMinor = 1; + + m_framebuffer = 0; + m_colorBuffer = 0; + m_depthBuffer = 0; +} + + +CGL33Device::~CGL33Device() +{ +} + +void CGL33Device::DebugHook() +{ + /* This function is only called here, so it can be used + * as a breakpoint when debugging using gDEBugger */ + glColor3i(0, 0, 0); +} + +void CGL33Device::DebugLights() +{ + Gfx::ColorHSV color(0.0, 1.0, 1.0); + + glLineWidth(3.0f); + glDisable(GL_LIGHTING); + glDepthMask(GL_FALSE); + glDisable(GL_BLEND); + + Math::Matrix saveWorldMat = m_worldMat; + Math::Matrix identity; + identity.LoadIdentity(); + SetTransform(TRANSFORM_WORLD, identity); + + for (int i = 0; i < static_cast( m_lights.size() ); ++i) + { + color.h = static_cast(i) / static_cast(m_lights.size()); + if (m_lightsEnabled[i]) + { + const Light& l = m_lights[i]; + if (l.type == LIGHT_DIRECTIONAL) + { + Gfx::VertexCol v[2]; + v[0].coord = -Math::Normalize(l.direction) * 100.0f + Math::Vector(0.0f, 0.0f, 1.0f) * i; + v[0].color = HSV2RGB(color); + v[1].coord = Math::Normalize(l.direction) * 100.0f + Math::Vector(0.0f, 0.0f, 1.0f) * i; + v[1].color = HSV2RGB(color); + while (v[0].coord.y < 60.0f && v[0].coord.y < 60.0f) + { + v[0].coord.y += 10.0f; + v[1].coord.y += 10.0f; + } + DrawPrimitive(PRIMITIVE_LINES, v, 2); + + v[0].coord = v[1].coord + Math::Normalize(v[0].coord - v[1].coord) * 50.0f; + + glLineWidth(10.0f); + DrawPrimitive(PRIMITIVE_LINES, v, 2); + glLineWidth(3.0f); + } + else if (l.type == LIGHT_POINT) + { + Gfx::VertexCol v[8]; + for (int i = 0; i < 8; ++i) + v[i].color = HSV2RGB(color); + + v[0].coord = l.position + Math::Vector(-1.0f, -1.0f, -1.0f) * 4.0f; + v[1].coord = l.position + Math::Vector( 1.0f, -1.0f, -1.0f) * 4.0f; + v[2].coord = l.position + Math::Vector( 1.0f, 1.0f, -1.0f) * 4.0f; + v[3].coord = l.position + Math::Vector(-1.0f, 1.0f, -1.0f) * 4.0f; + v[4].coord = l.position + Math::Vector(-1.0f, -1.0f, -1.0f) * 4.0f; + DrawPrimitive(PRIMITIVE_LINE_STRIP, v, 5); + + v[0].coord = l.position + Math::Vector(-1.0f, -1.0f, 1.0f) * 4.0f; + v[1].coord = l.position + Math::Vector( 1.0f, -1.0f, 1.0f) * 4.0f; + v[2].coord = l.position + Math::Vector( 1.0f, 1.0f, 1.0f) * 4.0f; + v[3].coord = l.position + Math::Vector(-1.0f, 1.0f, 1.0f) * 4.0f; + v[4].coord = l.position + Math::Vector(-1.0f, -1.0f, 1.0f) * 4.0f; + DrawPrimitive(PRIMITIVE_LINE_STRIP, v, 5); + + v[0].coord = l.position + Math::Vector(-1.0f, -1.0f, -1.0f) * 4.0f; + v[1].coord = l.position + Math::Vector(-1.0f, -1.0f, 1.0f) * 4.0f; + v[2].coord = l.position + Math::Vector( 1.0f, -1.0f, -1.0f) * 4.0f; + v[3].coord = l.position + Math::Vector( 1.0f, -1.0f, 1.0f) * 4.0f; + v[4].coord = l.position + Math::Vector( 1.0f, 1.0f, -1.0f) * 4.0f; + v[5].coord = l.position + Math::Vector( 1.0f, 1.0f, 1.0f) * 4.0f; + v[6].coord = l.position + Math::Vector(-1.0f, 1.0f, -1.0f) * 4.0f; + v[7].coord = l.position + Math::Vector(-1.0f, 1.0f, 1.0f) * 4.0f; + DrawPrimitive(PRIMITIVE_LINES, v, 8); + } + else if (l.type == LIGHT_SPOT) + { + Gfx::VertexCol v[5]; + for (int i = 0; i < 5; ++i) + v[i].color = HSV2RGB(color); + + v[0].coord = l.position + Math::Vector(-1.0f, 0.0f, -1.0f) * 4.0f; + v[1].coord = l.position + Math::Vector( 1.0f, 0.0f, -1.0f) * 4.0f; + v[2].coord = l.position + Math::Vector( 1.0f, 0.0f, 1.0f) * 4.0f; + v[3].coord = l.position + Math::Vector(-1.0f, 0.0f, 1.0f) * 4.0f; + v[4].coord = l.position + Math::Vector(-1.0f, 0.0f, -1.0f) * 4.0f; + DrawPrimitive(PRIMITIVE_LINE_STRIP, v, 5); + + v[0].coord = l.position; + v[1].coord = l.position + Math::Normalize(l.direction) * 100.0f; + glEnable(GL_LINE_STIPPLE); + glLineStipple(3.0, 0xFF); + DrawPrimitive(PRIMITIVE_LINES, v, 2); + glDisable(GL_LINE_STIPPLE); + } + } + } + + glLineWidth(1.0f); + glEnable(GL_LIGHTING); + glDepthMask(GL_TRUE); + glEnable(GL_BLEND); + + SetTransform(TRANSFORM_WORLD, saveWorldMat); +} + +bool CGL33Device::Create() +{ + GetLogger()->Info("Creating CDevice - OpenGL 3.3\n"); + + /*static*/ bool glewInited = false; + + if (!glewInited) + { + glewInited = true; + + glewExperimental = GL_TRUE; + + if (glewInit() != GLEW_OK) + { + GetLogger()->Error("GLEW initialization failed\n"); + return false; + } + + // Extract OpenGL version + const char *version = reinterpret_cast(glGetString(GL_VERSION)); + sscanf(version, "%d.%d", &m_glMajor, &m_glMinor); + + int glVersion = 10 * m_glMajor + m_glMinor; + if (glVersion < 33) + { + GetLogger()->Error("OpenGL 3.3 unavailable. Game might not work at all.\n"); + } + else + { + GetLogger()->Info("OpenGL %d.%d\n", m_glMajor, m_glMinor); + } + + glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &m_maxRenderbufferSize); + GetLogger()->Info("Maximum renderbuffer size: %d\n", m_maxRenderbufferSize); + + // Detect support of anisotropic filtering + m_anisotropyAvailable = glewIsSupported("GL_EXT_texture_filter_anisotropic"); + if(m_anisotropyAvailable) + { + // Obtain maximum anisotropy level available + float level; + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &level); + m_maxAnisotropy = static_cast(level); + + GetLogger()->Info("Anisotropic filtering available\n"); + GetLogger()->Info("Maximum anisotropy: %d\n", m_maxAnisotropy); + } + else + { + GetLogger()->Info("Anisotropic filtering not available\n"); + } + } + + // Set just to be sure + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + glViewport(0, 0, m_config.size.x, m_config.size.y); + + int numLights = 8; + + m_lights = std::vector(numLights, Light()); + m_lightsEnabled = std::vector (numLights, false); + + int maxTextures = 0; + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextures); + GetLogger()->Info("Maximum texture image units: %d\n", maxTextures); + + m_currentTextures = std::vector (maxTextures, Texture()); + m_texturesEnabled = std::vector (maxTextures, false); + m_textureStageParams = std::vector(maxTextures, TextureStageParams()); + + // Create auxilliary vertex buffer + m_vertex = CreateStaticBuffer(PRIMITIVE_POINTS, static_cast(nullptr), 1); + m_vertexTex2 = CreateStaticBuffer(PRIMITIVE_POINTS, static_cast(nullptr), 1); + m_vertexCol = CreateStaticBuffer(PRIMITIVE_POINTS, static_cast(nullptr), 1); + + GetLogger()->Info("CDevice created successfully\n"); + + // Create shader program + GLchar source[65536]; + const GLchar *sources[] = { source }; + + PHYSFS_file *file = PHYSFS_openRead("shaders/vertex_shader_33.glsl"); + if (file == nullptr) + { + CLogger::GetInstance().Error("Cannot read vertex shader code file!\n"); + assert(false); + } + + int length = PHYSFS_read(file, source, 1, 65536); + source[length] = '\0'; + + PHYSFS_close(file); + + GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertexShader, 1, sources, nullptr); + glCompileShader(vertexShader); + + GLint status; + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &status); + + if (status != GL_TRUE) + { + GLint len; + glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &len); + + GLchar *message = new GLchar[len + 1]; + glGetShaderInfoLog(vertexShader, len + 1, nullptr, message); + + GetLogger()->Error("Vertex shader compilation error occured!\n%s\n", message); + + delete[] message; + assert(false); + } + + file = PHYSFS_openRead("shaders/fragment_shader_33.glsl"); + if (file == nullptr) + { + CLogger::GetInstance().Error("Cannot read fragment shader code file!\n"); + assert(false); + } + + length = PHYSFS_read(file, source, 1, 65536); + source[length] = '\0'; + + PHYSFS_close(file); + + GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragmentShader, 1, sources, nullptr); + glCompileShader(fragmentShader); + + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &status); + + if (status != GL_TRUE) + { + GLint len; + glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &len); + + GLchar *message = new GLchar[len + 1]; + glGetShaderInfoLog(fragmentShader, len + 1, nullptr, message); + + GetLogger()->Error("Fragment shader compilation error occured!\n%s\n", message); + + delete[] message; + assert(false); + } + + m_shaderProgram = glCreateProgram(); + glAttachShader(m_shaderProgram, vertexShader); + glAttachShader(m_shaderProgram, fragmentShader); + + glLinkProgram(m_shaderProgram); + + glDetachShader(m_shaderProgram, vertexShader); + glDetachShader(m_shaderProgram, fragmentShader); + + glGetProgramiv(m_shaderProgram, GL_LINK_STATUS, &status); + + if (status != GL_TRUE) + { + GLint len; + glGetProgramiv(m_shaderProgram, GL_INFO_LOG_LENGTH, &len); + + GLchar *message = new GLchar[len + 1]; + glGetProgramInfoLog(m_shaderProgram, len + 1, nullptr, message); + + GetLogger()->Error("Shader program linking error occured!\n%s\n", message); + + delete[] message; + assert(false); + } + + glUseProgram(m_shaderProgram); + + // Obtain uniform locations + uni_ProjectionMatrix = glGetUniformLocation(m_shaderProgram, "uni_ProjectionMatrix"); + uni_ViewMatrix = glGetUniformLocation(m_shaderProgram, "uni_ViewMatrix"); + uni_ModelMatrix = glGetUniformLocation(m_shaderProgram, "uni_ModelMatrix"); + uni_NormalMatrix = glGetUniformLocation(m_shaderProgram, "uni_NormalMatrix"); + uni_ShadowMatrix = glGetUniformLocation(m_shaderProgram, "uni_ShadowMatrix"); + + uni_PrimaryTexture = glGetUniformLocation(m_shaderProgram, "uni_PrimaryTexture"); + uni_SecondaryTexture = glGetUniformLocation(m_shaderProgram, "uni_SecondaryTexture"); + uni_ShadowTexture = glGetUniformLocation(m_shaderProgram, "uni_ShadowTexture"); + + uni_PrimaryTextureEnabled = glGetUniformLocation(m_shaderProgram, "uni_PrimaryTextureEnabled"); + uni_SecondaryTextureEnabled = glGetUniformLocation(m_shaderProgram, "uni_SecondaryTextureEnabled"); + uni_ShadowTextureEnabled = glGetUniformLocation(m_shaderProgram, "uni_ShadowTextureEnabled"); + + uni_FogEnabled = glGetUniformLocation(m_shaderProgram, "uni_FogEnabled"); + uni_FogRange = glGetUniformLocation(m_shaderProgram, "uni_FogRange"); + uni_FogColor = glGetUniformLocation(m_shaderProgram, "uni_FogColor"); + + uni_AlphaTestEnabled = glGetUniformLocation(m_shaderProgram, "uni_AlphaTestEnabled"); + uni_AlphaReference = glGetUniformLocation(m_shaderProgram, "uni_AlphaReference"); + + uni_SmoothShading = glGetUniformLocation(m_shaderProgram, "uni_SmoothShading"); + uni_LightingEnabled = glGetUniformLocation(m_shaderProgram, "uni_LightingEnabled"); + + uni_AmbientColor = glGetUniformLocation(m_shaderProgram, "uni_AmbientColor"); + uni_DiffuseColor = glGetUniformLocation(m_shaderProgram, "uni_DiffuseColor"); + uni_SpecularColor = glGetUniformLocation(m_shaderProgram, "uni_SpecularColor"); + + GLchar name[64]; + for (int i = 0; i < 8; i++) + { + sprintf(name, "uni_Light[%d].Enabled", i); + uni_Light[i].Enabled = glGetUniformLocation(m_shaderProgram, name); + + sprintf(name, "uni_Light[%d].Position", i); + uni_Light[i].Position = glGetUniformLocation(m_shaderProgram, name); + + sprintf(name, "uni_Light[%d].Ambient", i); + uni_Light[i].Ambient = glGetUniformLocation(m_shaderProgram, name); + + sprintf(name, "uni_Light[%d].Diffuse", i); + uni_Light[i].Diffuse = glGetUniformLocation(m_shaderProgram, name); + + sprintf(name, "uni_Light[%d].Specular", i); + uni_Light[i].Specular = glGetUniformLocation(m_shaderProgram, name); + + sprintf(name, "uni_Light[%d].Attenuation", i); + uni_Light[i].Attenuation = glGetUniformLocation(m_shaderProgram, name); + } + + // Set default uniform values + Math::Matrix matrix; + matrix.LoadIdentity(); + + glUniformMatrix4fv(uni_ProjectionMatrix, 1, GL_FALSE, matrix.Array()); + glUniformMatrix4fv(uni_ViewMatrix, 1, GL_FALSE, matrix.Array()); + glUniformMatrix4fv(uni_ModelMatrix, 1, GL_FALSE, matrix.Array()); + glUniformMatrix4fv(uni_NormalMatrix, 1, GL_FALSE, matrix.Array()); + glUniformMatrix4fv(uni_ShadowMatrix, 1, GL_FALSE, matrix.Array()); + + glUniform1i(uni_PrimaryTexture, 0); + glUniform1i(uni_SecondaryTexture, 1); + glUniform1i(uni_ShadowTexture, 2); + + glUniform1i(uni_PrimaryTextureEnabled, 0); + glUniform1i(uni_SecondaryTextureEnabled, 0); + glUniform1i(uni_ShadowTextureEnabled, 0); + + glUniform4f(uni_AmbientColor, 0.4f, 0.4f, 0.4f, 1.0f); + glUniform4f(uni_DiffuseColor, 0.8f, 0.8f, 0.8f, 1.0f); + glUniform4f(uni_SpecularColor, 0.3f, 0.3f, 0.3f, 1.0f); + + glUniform1i(uni_FogEnabled, 0); + glUniform2f(uni_FogRange, 100.0f, 200.0f); + glUniform4f(uni_FogColor, 0.8f, 0.8f, 0.8f, 1.0f); + + glUniform1i(uni_AlphaTestEnabled, 0); + glUniform1f(uni_AlphaReference, 1.0f); + + glUniform1i(uni_LightingEnabled, 0); + + for (int i = 0; i < 8; i++) + glUniform1i(uni_Light[i].Enabled, 0); + + return true; +} + +void CGL33Device::Destroy() +{ + // Delete the remaining textures + // Should not be strictly necessary, but just in case + DestroyAllTextures(); + + m_lights.clear(); + m_lightsEnabled.clear(); + + m_currentTextures.clear(); + m_texturesEnabled.clear(); + m_textureStageParams.clear(); +} + +void CGL33Device::ConfigChanged(const GLDeviceConfig& newConfig) +{ + m_config = newConfig; + + // Reset state + m_lighting = false; + Destroy(); + Create(); +} + +void CGL33Device::BeginScene() +{ + Clear(); + + glUniformMatrix4fv(uni_ProjectionMatrix, 1, GL_FALSE, m_projectionMat.Array()); + glUniformMatrix4fv(uni_ViewMatrix, 1, GL_FALSE, m_viewMat.Array()); + glUniformMatrix4fv(uni_ModelMatrix, 1, GL_FALSE, m_worldMat.Array()); +} + +void CGL33Device::EndScene() +{ +} + +void CGL33Device::Clear() +{ + glDepthMask(GL_TRUE); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void CGL33Device::SetTransform(TransformType type, const Math::Matrix &matrix) +{ + if (type == TRANSFORM_WORLD) + { + m_worldMat = matrix; + glUniformMatrix4fv(uni_ModelMatrix, 1, GL_FALSE, m_worldMat.Array()); + + // normal transform + Math::Matrix normalMat = matrix; + normalMat = normalMat.Inverse(); + + glUniformMatrix4fv(uni_NormalMatrix, 1, GL_TRUE, normalMat.Array()); + } + else if (type == TRANSFORM_VIEW) + { + m_viewMat = matrix; + Math::Matrix scale; + Math::LoadScaleMatrix(scale, Math::Vector(1.0f, 1.0f, -1.0f)); + Math::Matrix temp = Math::MultiplyMatrices(scale, matrix); + glUniformMatrix4fv(uni_ViewMatrix, 1, GL_FALSE, temp.Array()); + } + else if (type == TRANSFORM_PROJECTION) + { + m_projectionMat = matrix; + glUniformMatrix4fv(uni_ProjectionMatrix, 1, GL_FALSE, m_projectionMat.Array()); + } + else if (type == TRANSFORM_SHADOW) + { + Math::Matrix temp = matrix; + glUniformMatrix4fv(uni_ShadowMatrix, 1, GL_FALSE, temp.Array()); + } + else + { + assert(false); + } +} + +void CGL33Device::SetMaterial(const Material &material) +{ + m_material = material; + + glUniform4fv(uni_AmbientColor, 1, m_material.ambient.Array()); + glUniform4fv(uni_DiffuseColor, 1, m_material.diffuse.Array()); + glUniform4fv(uni_SpecularColor, 1, m_material.specular.Array()); +} + +int CGL33Device::GetMaxLightCount() +{ + return m_lights.size(); +} + +void CGL33Device::SetLight(int index, const Light &light) +{ + assert(index >= 0); + assert(index < static_cast( m_lights.size() )); + + m_lights[index] = light; + + glUniform4fv(uni_Light[index].Ambient, 1, light.ambient.Array()); + glUniform4fv(uni_Light[index].Diffuse, 1, light.diffuse.Array()); + glUniform4fv(uni_Light[index].Specular, 1, light.specular.Array()); + glUniform3f(uni_Light[index].Attenuation, light.attenuation0, light.attenuation1, light.attenuation2); + + if (light.type == LIGHT_DIRECTIONAL) + { + glUniform4f(uni_Light[index].Position, -light.direction.x, -light.direction.y, -light.direction.z, 0.0f); + } + else + { + glUniform4f(uni_Light[index].Position, light.position.x, light.position.y, light.position.z, 1.0f); + } + + // TODO: add spotlight params +} + +// probably makes no sense anymore +void CGL33Device::UpdateLightPosition(int index) +{ + assert(index >= 0); + assert(index < static_cast( m_lights.size() )); + + /* + glMatrixMode(GL_MODELVIEW); + + glPushMatrix(); + + glLoadIdentity(); + glScalef(1.0f, 1.0f, -1.0f); + Math::Matrix mat = m_viewMat; + mat.Set(1, 4, 0.0f); + mat.Set(2, 4, 0.0f); + mat.Set(3, 4, 0.0f); + glMultMatrixf(mat.Array()); + + if (m_lights[index].type == LIGHT_SPOT) + { + GLfloat direction[4] = { -m_lights[index].direction.x, -m_lights[index].direction.y, -m_lights[index].direction.z, 1.0f }; + glLightfv(GL_LIGHT0 + index, GL_SPOT_DIRECTION, direction); + } + + if (m_lights[index].type == LIGHT_DIRECTIONAL) + { + GLfloat position[4] = { -m_lights[index].direction.x, -m_lights[index].direction.y, -m_lights[index].direction.z, 0.0f }; + glLightfv(GL_LIGHT0 + index, GL_POSITION, position); + } + else + { + glLoadIdentity(); + glScalef(1.0f, 1.0f, -1.0f); + glMultMatrixf(m_viewMat.Array()); + + GLfloat position[4] = { m_lights[index].position.x, m_lights[index].position.y, m_lights[index].position.z, 1.0f }; + glLightfv(GL_LIGHT0 + index, GL_POSITION, position); + } + + glPopMatrix(); + */ +} + +void CGL33Device::SetLightEnabled(int index, bool enabled) +{ + assert(index >= 0); + assert(index < static_cast( m_lights.size() )); + + m_lightsEnabled[index] = enabled; + + glUniform1i(uni_Light[index].Enabled, enabled ? 1 : 0); +} + +/** If image is invalid, returns invalid texture. + Otherwise, returns pointer to new Texture struct. + This struct must not be deleted in other way than through DeleteTexture() */ +Texture CGL33Device::CreateTexture(CImage *image, const TextureCreateParams ¶ms) +{ + ImageData *data = image->GetData(); + if (data == nullptr) + { + GetLogger()->Error("Invalid texture data\n"); + return Texture(); // invalid texture + } + + Math::IntPoint originalSize = image->GetSize(); + + if (params.padToNearestPowerOfTwo) + image->PadToNearestPowerOfTwo(); + + Texture tex = CreateTexture(data, params); + tex.originalSize = originalSize; + + return tex; +} + +Texture CGL33Device::CreateTexture(ImageData *data, const TextureCreateParams ¶ms) +{ + Texture result; + + result.size.x = data->surface->w; + result.size.y = data->surface->h; + + result.originalSize = result.size; + + glActiveTexture(GL_TEXTURE0); + + glGenTextures(1, &result.id); + glBindTexture(GL_TEXTURE_2D, result.id); + + // Set texture parameters + GLint minF = GL_NEAREST, magF = GL_NEAREST; + int mipmapLevel = 1; + + switch (params.filter) + { + case TEX_FILTER_NEAREST: + minF = GL_NEAREST; + magF = GL_NEAREST; + break; + case TEX_FILTER_BILINEAR: + minF = GL_LINEAR; + magF = GL_LINEAR; + break; + case TEX_FILTER_TRILINEAR: + minF = GL_LINEAR_MIPMAP_LINEAR; + magF = GL_LINEAR; + mipmapLevel = CEngine::GetInstance().GetTextureMipmapLevel(); + break; + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minF); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magF); + + // Set mipmap level and automatic mipmap generation if neccesary + if (params.mipmap) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipmapLevel - 1); + } + else + { + // Has to be set to 0 because no mipmaps are generated + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + } + + // Set anisotropy level if available + if (m_anisotropyAvailable) + { + float level = Math::Min(m_maxAnisotropy, CEngine::GetInstance().GetTextureAnisotropyLevel()); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, level); + } + + bool convert = false; + GLenum sourceFormat = 0; + + if (params.format == TEX_IMG_RGB) + { + sourceFormat = GL_RGB; + result.alpha = false; + } + else if (params.format == TEX_IMG_BGR) + { + sourceFormat = GL_BGR; + result.alpha = false; + } + else if (params.format == TEX_IMG_RGBA) + { + sourceFormat = GL_RGBA; + result.alpha = true; + } + else if (params.format == TEX_IMG_BGRA) + { + sourceFormat = GL_BGRA; + result.alpha = true; + } + else if (params.format == TEX_IMG_AUTO) + { + if (data->surface->format->BytesPerPixel == 4) + { + if ((data->surface->format->Amask == 0xFF000000) && + (data->surface->format->Rmask == 0x00FF0000) && + (data->surface->format->Gmask == 0x0000FF00) && + (data->surface->format->Bmask == 0x000000FF)) + { + sourceFormat = GL_BGRA; + result.alpha = true; + } + else if ((data->surface->format->Amask == 0xFF000000) && + (data->surface->format->Bmask == 0x00FF0000) && + (data->surface->format->Gmask == 0x0000FF00) && + (data->surface->format->Rmask == 0x000000FF)) + { + sourceFormat = GL_RGBA; + result.alpha = true; + } + else + { + sourceFormat = GL_RGBA; + convert = true; + } + } + else if (data->surface->format->BytesPerPixel == 3) + { + if ((data->surface->format->Rmask == 0xFF0000) && + (data->surface->format->Gmask == 0x00FF00) && + (data->surface->format->Bmask == 0x0000FF)) + { + sourceFormat = GL_BGR; + result.alpha = false; + } + else if ((data->surface->format->Bmask == 0xFF0000) && + (data->surface->format->Gmask == 0x00FF00) && + (data->surface->format->Rmask == 0x0000FF)) + { + sourceFormat = GL_RGB; + result.alpha = false; + } + else + { + sourceFormat = GL_RGBA; + convert = true; + } + } + else { + GetLogger()->Error("Unknown data surface format"); + assert(false); + } + } + else + assert(false); + + SDL_Surface* actualSurface = data->surface; + SDL_Surface* convertedSurface = nullptr; + + if (convert) + { + SDL_PixelFormat format; + format.BytesPerPixel = 4; + format.BitsPerPixel = 32; + format.alpha = 0; + format.colorkey = 0; + format.Aloss = format.Bloss = format.Gloss = format.Rloss = 0; + format.Amask = 0xFF000000; + format.Ashift = 24; + format.Bmask = 0x00FF0000; + format.Bshift = 16; + format.Gmask = 0x0000FF00; + format.Gshift = 8; + format.Rmask = 0x000000FF; + format.Rshift = 0; + format.palette = nullptr; + convertedSurface = SDL_ConvertSurface(data->surface, &format, SDL_SWSURFACE); + if (convertedSurface != nullptr) + actualSurface = convertedSurface; + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, actualSurface->w, actualSurface->h, + 0, sourceFormat, GL_UNSIGNED_BYTE, actualSurface->pixels); + + if (params.mipmap) + glGenerateMipmap(GL_TEXTURE_2D); + + SDL_FreeSurface(convertedSurface); + + // Restore the previous state of 1st stage + glBindTexture(GL_TEXTURE_2D, m_currentTextures[0].id); + + return result; +} + +Texture CGL33Device::CreateDepthTexture(int width, int height, int depth) +{ + Texture result; + + result.alpha = false; + result.size.x = width; + result.size.y = height; + + glActiveTexture(GL_TEXTURE0); + + glGenTextures(1, &result.id); + glBindTexture(GL_TEXTURE_2D, result.id); + + GLuint format = GL_DEPTH_COMPONENT; + + switch (depth) + { + case 16: + format = GL_DEPTH_COMPONENT16; + break; + case 24: + format = GL_DEPTH_COMPONENT24; + break; + case 32: + format = GL_DEPTH_COMPONENT32; + break; + } + + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, GL_DEPTH_COMPONENT, GL_INT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + + float color[] = { 1.0f, 1.0f, 1.0f, 1.0f }; + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color); + + glBindTexture(GL_TEXTURE_2D, m_currentTextures[0].id); + + return result; +} + +void CGL33Device::DestroyTexture(const Texture &texture) +{ + // Unbind the texture if in use anywhere + for (int index = 0; index < static_cast( m_currentTextures.size() ); ++index) + { + if (m_currentTextures[index] == texture) + SetTexture(index, Texture()); // set to invalid texture + } + + glDeleteTextures(1, &texture.id); + + auto it = m_allTextures.find(texture); + if (it != m_allTextures.end()) + m_allTextures.erase(it); +} + +void CGL33Device::DestroyAllTextures() +{ + // Unbind all texture stages + for (int index = 0; index < static_cast( m_currentTextures.size() ); ++index) + SetTexture(index, Texture()); + + for (auto it = m_allTextures.begin(); it != m_allTextures.end(); ++it) + glDeleteTextures(1, &(*it).id); + + m_allTextures.clear(); +} + +int CGL33Device::GetMaxTextureStageCount() +{ + return m_currentTextures.size(); +} + +/** + If \a texture is invalid, unbinds the given texture. + If valid, binds the texture and enables the given texture stage. + The setting is remembered, even if texturing is disabled at the moment. */ +void CGL33Device::SetTexture(int index, const Texture &texture) +{ + assert(index >= 0 && index < static_cast( m_currentTextures.size() )); + + bool same = m_currentTextures[index].id == texture.id; + + m_currentTextures[index] = texture; // remember the new value + + if (same) + return; // nothing to do + + glActiveTexture(GL_TEXTURE0 + index); + glBindTexture(GL_TEXTURE_2D, texture.id); + + // Params need to be updated for the new bound texture + UpdateTextureParams(index); +} + +void CGL33Device::SetTexture(int index, unsigned int textureId) +{ + assert(index >= 0 && index < static_cast( m_currentTextures.size() )); + + if (m_currentTextures[index].id == textureId) + return; // nothing to do + + m_currentTextures[index].id = textureId; + + glActiveTexture(GL_TEXTURE0 + index); + glBindTexture(GL_TEXTURE_2D, textureId); + + // Params need to be updated for the new bound texture + UpdateTextureParams(index); +} + +void CGL33Device::SetTextureEnabled(int index, bool enabled) +{ + assert(index >= 0 && index < static_cast( m_currentTextures.size() )); + + bool same = m_texturesEnabled[index] == enabled; + + m_texturesEnabled[index] = enabled; + + if (same) + return; // nothing to do + + UpdateRenderingMode(); +} + +/** + Sets the texture parameters for the given texture stage. + If the given texture was not set (bound) yet, nothing happens. + The settings are remembered, even if texturing is disabled at the moment. */ +void CGL33Device::SetTextureStageParams(int index, const TextureStageParams ¶ms) +{ + assert(index >= 0 && index < static_cast( m_currentTextures.size() )); + + // Remember the settings + m_textureStageParams[index] = params; + + UpdateTextureParams(index); +} + +void CGL33Device::SetTextureCoordGeneration(int index, TextureGenerationParams ¶ms) +{ + // TODO: think about generalized way + /* + glActiveTexture(GL_TEXTURE0 + index); + + for (int i = 0; i < 4; i++) + { + GLuint texCoordGen = textureCoordGen[i]; + GLuint texCoord = textureCoordinates[i]; + + if (params.coords[i].mode == TEX_GEN_NONE) + { + glDisable(texCoordGen); + } + else + { + glEnable(texCoordGen); + + switch (params.coords[i].mode) + { + case TEX_GEN_OBJECT_LINEAR: + glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + glTexGenfv(texCoord, GL_OBJECT_PLANE, params.coords[i].plane); + break; + case TEX_GEN_EYE_LINEAR: + glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); + glTexGenfv(texCoord, GL_EYE_PLANE, params.coords[i].plane); + break; + case TEX_GEN_SPHERE_MAP: + glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); + break; + case TEX_GEN_NORMAL_MAP: + glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP); + break; + case TEX_GEN_REFLECTION_MAP: + glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); + break; + } + } + } + // */ +} + +void CGL33Device::UpdateTextureParams(int index) +{ + assert(index >= 0 && index < static_cast( m_currentTextures.size() )); + + // Don't actually do anything if texture not set + if (! m_currentTextures[index].Valid()) + return; + + const TextureStageParams ¶ms = m_textureStageParams[index]; + + glActiveTexture(GL_TEXTURE0 + index); + + if (params.wrapS == TEX_WRAP_CLAMP) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + else if (params.wrapS == TEX_WRAP_CLAMP_TO_BORDER) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + else if (params.wrapS == TEX_WRAP_REPEAT) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + else assert(false); + + if (params.wrapT == TEX_WRAP_CLAMP) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + else if (params.wrapT == TEX_WRAP_CLAMP_TO_BORDER) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + else if (params.wrapT == TEX_WRAP_REPEAT) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + else assert(false); + + // TODO: this needs to be redone + /* + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, params.factor.Array()); + + // To save some trouble + if ( (params.colorOperation == TEX_MIX_OPER_DEFAULT) && + (params.alphaOperation == TEX_MIX_OPER_DEFAULT) ) + { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + goto after_tex_operations; + } + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + // Only these modes of getting color & alpha are used + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + + // Color operation + + if (params.colorOperation == TEX_MIX_OPER_DEFAULT) + { + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); + goto after_tex_color; + } + else if (params.colorOperation == TEX_MIX_OPER_REPLACE) + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + else if (params.colorOperation == TEX_MIX_OPER_MODULATE) + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + else if (params.colorOperation == TEX_MIX_OPER_ADD) + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD); + else if (params.colorOperation == TEX_MIX_OPER_SUBTRACT) + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT); + else assert(false); + + // Color arg1 + if (params.colorArg1 == TEX_MIX_ARG_TEXTURE) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); + else if (params.colorArg1 == TEX_MIX_ARG_TEXTURE_0) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0); + else if (params.colorArg1 == TEX_MIX_ARG_TEXTURE_1) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE1); + else if (params.colorArg1 == TEX_MIX_ARG_TEXTURE_2) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE2); + else if (params.colorArg1 == TEX_MIX_ARG_TEXTURE_3) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE3); + else if (params.colorArg1 == TEX_MIX_ARG_COMPUTED_COLOR) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); + else if (params.colorArg1 == TEX_MIX_ARG_SRC_COLOR) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR); + else if (params.colorArg1 == TEX_MIX_ARG_FACTOR) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_CONSTANT); + else assert(false); + + // Color arg2 + if (params.colorArg2 == TEX_MIX_ARG_TEXTURE) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); + else if (params.colorArg2 == TEX_MIX_ARG_TEXTURE_0) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE0); + else if (params.colorArg2 == TEX_MIX_ARG_TEXTURE_1) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE1); + else if (params.colorArg2 == TEX_MIX_ARG_TEXTURE_2) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE2); + else if (params.colorArg2 == TEX_MIX_ARG_TEXTURE_3) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE3); + else if (params.colorArg2 == TEX_MIX_ARG_COMPUTED_COLOR) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS); + else if (params.colorArg2 == TEX_MIX_ARG_SRC_COLOR) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + else if (params.colorArg2 == TEX_MIX_ARG_FACTOR) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT); + else assert(false); + + +after_tex_color: + + // Alpha operation + if (params.alphaOperation == TEX_MIX_OPER_DEFAULT) + { + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE); + goto after_tex_operations; + } + else if (params.alphaOperation == TEX_MIX_OPER_REPLACE) + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + else if (params.alphaOperation == TEX_MIX_OPER_MODULATE) + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + else if (params.alphaOperation == TEX_MIX_OPER_ADD) + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD); + else if (params.alphaOperation == TEX_MIX_OPER_SUBTRACT) + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_SUBTRACT); + else assert(false); + + // Alpha arg1 + if (params.alphaArg1 == TEX_MIX_ARG_TEXTURE) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + else if (params.alphaArg1 == TEX_MIX_ARG_TEXTURE_0) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0); + else if (params.alphaArg1 == TEX_MIX_ARG_TEXTURE_1) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE1); + else if (params.alphaArg1 == TEX_MIX_ARG_TEXTURE_2) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE2); + else if (params.alphaArg1 == TEX_MIX_ARG_TEXTURE_3) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE3); + else if (params.alphaArg1 == TEX_MIX_ARG_COMPUTED_COLOR) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); + else if (params.alphaArg1 == TEX_MIX_ARG_SRC_COLOR) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR); + else if (params.alphaArg1 == TEX_MIX_ARG_FACTOR) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT); + else assert(false); + + // Alpha arg2 + if (params.alphaArg2 == TEX_MIX_ARG_TEXTURE) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE); + else if (params.alphaArg2 == TEX_MIX_ARG_TEXTURE_0) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE0); + else if (params.alphaArg2 == TEX_MIX_ARG_TEXTURE_1) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE1); + else if (params.alphaArg2 == TEX_MIX_ARG_TEXTURE_2) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE2); + else if (params.alphaArg2 == TEX_MIX_ARG_TEXTURE_3) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE3); + else if (params.alphaArg2 == TEX_MIX_ARG_COMPUTED_COLOR) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PREVIOUS); + else if (params.alphaArg2 == TEX_MIX_ARG_SRC_COLOR) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); + else if (params.alphaArg2 == TEX_MIX_ARG_FACTOR) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_CONSTANT); + else assert(false); + +after_tex_operations: ; + */ +} + +void CGL33Device::SetTextureStageWrap(int index, TexWrapMode wrapS, TexWrapMode wrapT) +{ + assert(index >= 0 && index < static_cast( m_currentTextures.size() )); + + // Remember the settings + m_textureStageParams[index].wrapS = wrapS; + m_textureStageParams[index].wrapT = wrapT; + + // Don't actually do anything if texture not set + if (! m_currentTextures[index].Valid()) + return; + + glActiveTexture(GL_TEXTURE0 + index); + + if (wrapS == TEX_WRAP_CLAMP) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + else if (wrapS == TEX_WRAP_CLAMP_TO_BORDER) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + else if (wrapS == TEX_WRAP_REPEAT) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + else assert(false); + + if (wrapT == TEX_WRAP_CLAMP) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + else if (wrapT == TEX_WRAP_CLAMP_TO_BORDER) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + else if (wrapT == TEX_WRAP_REPEAT) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + else assert(false); +} + +void CGL33Device::DrawPrimitive(PrimitiveType type, const Vertex *vertices, int vertexCount, Color color) +{ + Vertex* vs = const_cast(vertices); + VertexBufferInfo &info = m_vboObjects[m_vertex]; + + unsigned int size = vertexCount * sizeof(Vertex); + + glBindVertexArray(info.vao); + glBindBuffer(GL_ARRAY_BUFFER, info.vbo); + + // If needed vertex data is too large, increase the size of buffer + if (info.size >= size) + { + glBufferSubData(GL_ARRAY_BUFFER, 0, size, vs); + } + else + { + glBufferData(GL_ARRAY_BUFFER, size, vs, GL_DYNAMIC_DRAW); + info.size = size; + + // Vertex coordinate + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, coord))); + + // Normal + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, normal))); + + // Color + glDisableVertexAttribArray(2); + glVertexAttrib4fv(2, color.Array()); + + // Texture coordinate 0 + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, texCoord))); + + // Texture coordinate 1 + glDisableVertexAttribArray(4); + glVertexAttrib2f(4, 0.0f, 0.0f); + } + + glVertexAttrib4fv(2, color.Array()); + + UpdateRenderingMode(); + + glDrawArrays(TranslateGfxPrimitive(type), 0, vertexCount); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +void CGL33Device::DrawPrimitive(PrimitiveType type, const VertexTex2 *vertices, int vertexCount, Color color) +{ + VertexTex2* vs = const_cast(vertices); + VertexBufferInfo &info = m_vboObjects[m_vertexTex2]; + + unsigned int size = vertexCount * sizeof(VertexTex2); + + glBindVertexArray(info.vao); + glBindBuffer(GL_ARRAY_BUFFER, info.vbo); + + // If needed vertex data is too large, increase the size of buffer + if (info.size >= size) + { + glBufferSubData(GL_ARRAY_BUFFER, 0, size, vs); + } + else + { + glBufferData(GL_ARRAY_BUFFER, size, vs, GL_DYNAMIC_DRAW); + info.size = size; + + // Vertex coordinate + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), reinterpret_cast(offsetof(VertexTex2, coord))); + + // Normal + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), reinterpret_cast(offsetof(VertexTex2, normal))); + + // Color + glDisableVertexAttribArray(2); + glVertexAttrib4fv(2, color.Array()); + + // Texture coordinate 0 + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), reinterpret_cast(offsetof(VertexTex2, texCoord))); + + // Texture coordinate 1 + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), reinterpret_cast(offsetof(VertexTex2, texCoord2))); + } + + glVertexAttrib4fv(2, color.Array()); + + UpdateRenderingMode(); + + glDrawArrays(TranslateGfxPrimitive(type), 0, vertexCount); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +void CGL33Device::DrawPrimitive(PrimitiveType type, const VertexCol *vertices, int vertexCount) +{ + VertexCol* vs = const_cast(vertices); + VertexBufferInfo &info = m_vboObjects[m_vertexCol]; + + unsigned int size = vertexCount * sizeof(VertexCol); + + glBindVertexArray(info.vao); + glBindBuffer(GL_ARRAY_BUFFER, info.vbo); + + // If needed vertex data is too large, increase the size of buffer + if (info.size >= size) + { + glBufferSubData(GL_ARRAY_BUFFER, 0, size, vs); + } + else + { + glBufferData(GL_ARRAY_BUFFER, size, vs, GL_DYNAMIC_DRAW); + info.size = size; + + // Vertex coordinate + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexCol), reinterpret_cast(offsetof(VertexCol, coord))); + + // Normal + glDisableVertexAttribArray(1); + glVertexAttrib3f(1, 0.0f, 0.0f, 1.0f); + + // Color + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(VertexCol), reinterpret_cast(offsetof(VertexCol, color))); + + // Texture coordinate 0 + glDisableVertexAttribArray(3); + glVertexAttrib2f(3, 0.0f, 0.0f); + + // Texture coordinate 1 + glDisableVertexAttribArray(4); + glVertexAttrib2f(4, 0.0f, 0.0f); + } + + UpdateRenderingMode(); + + glDrawArrays(TranslateGfxPrimitive(type), 0, vertexCount); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +unsigned int CGL33Device::CreateStaticBuffer(PrimitiveType primitiveType, const Vertex* vertices, int vertexCount) +{ + unsigned int id = 0; + + id = ++m_lastVboId; + + VertexBufferInfo info; + info.primitiveType = primitiveType; + info.vertexType = VERTEX_TYPE_NORMAL; + info.vertexCount = vertexCount; + info.size = vertexCount * sizeof(Vertex); + + glGenVertexArrays(1, &info.vao); + glBindVertexArray(info.vao); + + glGenBuffers(1, &info.vbo); + glBindBuffer(GL_ARRAY_BUFFER, info.vbo); + glBufferData(GL_ARRAY_BUFFER, info.size, vertices, GL_STATIC_DRAW); + + // Vertex coordinate + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, coord))); + + // Normal + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, normal))); + + // Color + glDisableVertexAttribArray(2); + glVertexAttrib4f(2, 1.0f, 1.0f, 1.0f, 1.0f); + + // Texture coordinate 0 + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, texCoord))); + + // Texture coordinate 1 + glDisableVertexAttribArray(4); + glVertexAttrib2f(4, 0.0f, 0.0f); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + m_vboObjects[id] = info; + + return id; +} + +unsigned int CGL33Device::CreateStaticBuffer(PrimitiveType primitiveType, const VertexTex2* vertices, int vertexCount) +{ + unsigned int id = 0; + + id = ++m_lastVboId; + + VertexBufferInfo info; + info.primitiveType = primitiveType; + info.vertexType = VERTEX_TYPE_TEX2; + info.vertexCount = vertexCount; + info.size = vertexCount * sizeof(VertexTex2); + + glGenVertexArrays(1, &info.vao); + glBindVertexArray(info.vao); + + glGenBuffers(1, &info.vbo); + glBindBuffer(GL_ARRAY_BUFFER, info.vbo); + glBufferData(GL_ARRAY_BUFFER, info.size, vertices, GL_STATIC_DRAW); + + // Vertex coordinate + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), reinterpret_cast(offsetof(VertexTex2, coord))); + + // Normal + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), reinterpret_cast(offsetof(VertexTex2, normal))); + + // Color + glDisableVertexAttribArray(2); + glVertexAttrib4f(2, 1.0f, 1.0f, 1.0f, 1.0f); + + // Texture coordinate 0 + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), reinterpret_cast(offsetof(VertexTex2, texCoord))); + + // Texture coordinate 1 + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), reinterpret_cast(offsetof(VertexTex2, texCoord2))); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + m_vboObjects[id] = info; + + return id; +} + +unsigned int CGL33Device::CreateStaticBuffer(PrimitiveType primitiveType, const VertexCol* vertices, int vertexCount) +{ + unsigned int id = ++m_lastVboId; + + VertexBufferInfo info; + info.primitiveType = primitiveType; + info.vertexType = VERTEX_TYPE_COL; + info.vertexCount = vertexCount; + info.size = vertexCount * sizeof(VertexCol); + + glGenVertexArrays(1, &info.vao); + glBindVertexArray(info.vao); + + glGenBuffers(1, &info.vbo); + glBindBuffer(GL_ARRAY_BUFFER, info.vbo); + glBufferData(GL_ARRAY_BUFFER, info.size, vertices, GL_STATIC_DRAW); + + // Vertex coordinate + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexCol), reinterpret_cast(offsetof(VertexCol, coord))); + + // Normal + glDisableVertexAttribArray(1); + glVertexAttrib3f(1, 0.0f, 0.0f, 1.0f); + + // Color + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(VertexCol), reinterpret_cast(offsetof(VertexCol, color))); + + // Texture coordinate 0 + glDisableVertexAttribArray(3); + glVertexAttrib2f(3, 0.0f, 0.0f); + + // Texture coordinate 1 + glDisableVertexAttribArray(4); + glVertexAttrib2f(4, 0.0f, 0.0f); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + m_vboObjects[id] = info; + + return id; +} + +void CGL33Device::UpdateStaticBuffer(unsigned int bufferId, PrimitiveType primitiveType, const Vertex* vertices, int vertexCount) +{ + auto it = m_vboObjects.find(bufferId); + if (it == m_vboObjects.end()) + return; + + VertexBufferInfo& info = (*it).second; + info.primitiveType = primitiveType; + info.vertexType = VERTEX_TYPE_NORMAL; + info.vertexCount = vertexCount; + + glBindVertexArray(info.vao); + + glBindBuffer(GL_ARRAY_BUFFER, info.vbo); + + unsigned int size = vertexCount * sizeof(Vertex); + + if (info.size < size) + { + glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW); + info.size = size; + } + else + { + glBufferSubData(GL_ARRAY_BUFFER, 0, size, vertices); + } + + // Vertex coordinate + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, coord))); + + // Normal + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, normal))); + + // Color + glDisableVertexAttribArray(2); + glVertexAttrib4f(2, 1.0f, 1.0f, 1.0f, 1.0f); + + // Texture coordinate 0 + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, texCoord))); + + // Texture coordinate 1 + glDisableVertexAttribArray(4); + glVertexAttrib2f(4, 0.0f, 0.0f); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +void CGL33Device::UpdateStaticBuffer(unsigned int bufferId, PrimitiveType primitiveType, const VertexTex2* vertices, int vertexCount) +{ + auto it = m_vboObjects.find(bufferId); + if (it == m_vboObjects.end()) + return; + + VertexBufferInfo& info = (*it).second; + info.primitiveType = primitiveType; + info.vertexType = VERTEX_TYPE_TEX2; + info.vertexCount = vertexCount; + + glBindVertexArray(info.vao); + glBindBuffer(GL_ARRAY_BUFFER, info.vbo); + + unsigned int size = vertexCount * sizeof(VertexTex2); + + if (info.size < size) + { + glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW); + info.size = size; + } + else + { + glBufferSubData(GL_ARRAY_BUFFER, 0, size, vertices); + } + + // Vertex coordinate + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), reinterpret_cast(offsetof(VertexTex2, coord))); + + // Normal + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), reinterpret_cast(offsetof(VertexTex2, normal))); + + // Color + glDisableVertexAttribArray(2); + glVertexAttrib4f(2, 1.0f, 1.0f, 1.0f, 1.0f); + + // Texture coordinate 0 + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), reinterpret_cast(offsetof(VertexTex2, texCoord))); + + // Texture coordinate 1 + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), reinterpret_cast(offsetof(VertexTex2, texCoord2))); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +void CGL33Device::UpdateStaticBuffer(unsigned int bufferId, PrimitiveType primitiveType, const VertexCol* vertices, int vertexCount) +{ + auto it = m_vboObjects.find(bufferId); + if (it == m_vboObjects.end()) + return; + + VertexBufferInfo& info = (*it).second; + info.primitiveType = primitiveType; + info.vertexType = VERTEX_TYPE_COL; + info.vertexCount = vertexCount; + + glBindVertexArray(info.vao); + glBindBuffer(GL_ARRAY_BUFFER, info.vbo); + + unsigned int size = vertexCount * sizeof(VertexCol); + + if (info.size < size) + { + glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW); + info.size = size; + } + else + { + glBufferSubData(GL_ARRAY_BUFFER, 0, size, vertices); + } + + // Vertex coordinate + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexCol), reinterpret_cast(offsetof(VertexCol, coord))); + + // Normal + glDisableVertexAttribArray(1); + glVertexAttrib3f(1, 0.0f, 0.0f, 1.0f); + + // Color + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(VertexCol), reinterpret_cast(offsetof(VertexCol, color))); + + // Texture coordinate 0 + glDisableVertexAttribArray(3); + glVertexAttrib2f(3, 0.0f, 0.0f); + + // Texture coordinate 1 + glDisableVertexAttribArray(4); + glVertexAttrib2f(4, 0.0f, 0.0f); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +void CGL33Device::DrawStaticBuffer(unsigned int bufferId) +{ + auto it = m_vboObjects.find(bufferId); + if (it == m_vboObjects.end()) + return; + + VertexBufferInfo &info = (*it).second; + + UpdateRenderingMode(); + + glBindVertexArray(info.vao); + + GLenum mode = TranslateGfxPrimitive(info.primitiveType); + glDrawArrays(mode, 0, info.vertexCount); + + glBindVertexArray(0); +} + +void CGL33Device::DestroyStaticBuffer(unsigned int bufferId) +{ + auto it = m_vboObjects.find(bufferId); + if (it == m_vboObjects.end()) + return; + + VertexBufferInfo &info = (*it).second; + + glDeleteBuffers(1, &info.vbo); + glDeleteVertexArrays(1, &info.vao); + + info.vbo = 0; + info.vao = 0; + + m_vboObjects.erase(it); +} + +/* Based on libwine's implementation */ + +int CGL33Device::ComputeSphereVisibility(const Math::Vector ¢er, float radius) +{ + Math::Matrix m; + m = Math::MultiplyMatrices(m_worldMat, m); + m = Math::MultiplyMatrices(m_viewMat, m); + Math::Matrix sc; + Math::LoadScaleMatrix(sc, Math::Vector(1.0f, 1.0f, -1.0f)); + m = Math::MultiplyMatrices(sc, m); + m = Math::MultiplyMatrices(m_projectionMat, m); + + Math::Vector vec[6]; + float originPlane[6]; + + // Left plane + vec[0].x = m.Get(4, 1) + m.Get(1, 1); + vec[0].y = m.Get(4, 2) + m.Get(1, 2); + vec[0].z = m.Get(4, 3) + m.Get(1, 3); + float l1 = vec[0].Length(); + vec[0].Normalize(); + originPlane[0] = (m.Get(4, 4) + m.Get(1, 4)) / l1; + + // Right plane + vec[1].x = m.Get(4, 1) - m.Get(1, 1); + vec[1].y = m.Get(4, 2) - m.Get(1, 2); + vec[1].z = m.Get(4, 3) - m.Get(1, 3); + float l2 = vec[1].Length(); + vec[1].Normalize(); + originPlane[1] = (m.Get(4, 4) - m.Get(1, 4)) / l2; + + // Bottom plane + vec[2].x = m.Get(4, 1) + m.Get(2, 1); + vec[2].y = m.Get(4, 2) + m.Get(2, 2); + vec[2].z = m.Get(4, 3) + m.Get(2, 3); + float l3 = vec[2].Length(); + vec[2].Normalize(); + originPlane[2] = (m.Get(4, 4) + m.Get(2, 4)) / l3; + + // Top plane + vec[3].x = m.Get(4, 1) - m.Get(2, 1); + vec[3].y = m.Get(4, 2) - m.Get(2, 2); + vec[3].z = m.Get(4, 3) - m.Get(2, 3); + float l4 = vec[3].Length(); + vec[3].Normalize(); + originPlane[3] = (m.Get(4, 4) - m.Get(2, 4)) / l4; + + // Front plane + vec[4].x = m.Get(4, 1) + m.Get(3, 1); + vec[4].y = m.Get(4, 2) + m.Get(3, 2); + vec[4].z = m.Get(4, 3) + m.Get(3, 3); + float l5 = vec[4].Length(); + vec[4].Normalize(); + originPlane[4] = (m.Get(4, 4) + m.Get(3, 4)) / l5; + + // Back plane + vec[5].x = m.Get(4, 1) - m.Get(3, 1); + vec[5].y = m.Get(4, 2) - m.Get(3, 2); + vec[5].z = m.Get(4, 3) - m.Get(3, 3); + float l6 = vec[5].Length(); + vec[5].Normalize(); + originPlane[5] = (m.Get(4, 4) - m.Get(3, 4)) / l6; + + int result = 0; + + if (InPlane(vec[0], originPlane[0], center, radius)) + result |= FRUSTUM_PLANE_LEFT; + if (InPlane(vec[1], originPlane[1], center, radius)) + result |= FRUSTUM_PLANE_RIGHT; + if (InPlane(vec[2], originPlane[2], center, radius)) + result |= FRUSTUM_PLANE_BOTTOM; + if (InPlane(vec[3], originPlane[3], center, radius)) + result |= FRUSTUM_PLANE_TOP; + if (InPlane(vec[4], originPlane[4], center, radius)) + result |= FRUSTUM_PLANE_FRONT; + if (InPlane(vec[5], originPlane[5], center, radius)) + result |= FRUSTUM_PLANE_BACK; + + return result; +} + +void CGL33Device::SetViewport(int x, int y, int width, int height) +{ + glViewport(x, y, width, height); +} + +void CGL33Device::SetRenderState(RenderState state, bool enabled) +{ + if (state == RENDER_STATE_DEPTH_WRITE) + { + glDepthMask(enabled ? GL_TRUE : GL_FALSE); + return; + } + else if (state == RENDER_STATE_LIGHTING) + { + m_lighting = enabled; + + glUniform1i(uni_LightingEnabled, enabled ? 1 : 0); + + return; + } + else if (state == RENDER_STATE_OFFSCREEN_RENDERING) + { + if (m_framebuffer == 0) + InitOffscreenBuffer(2048, 2048); + + GLuint toBind = (enabled ? m_framebuffer : 0); + + glBindFramebuffer(GL_FRAMEBUFFER, toBind); + + return; + } + else if (state == RENDER_STATE_FOG) + { + glUniform1i(uni_FogEnabled, enabled ? 1 : 0); + + return; + } + else if (state == RENDER_STATE_ALPHA_TEST) + { + glUniform1i(uni_AlphaTestEnabled, enabled ? 1 : 0); + + return; + } + + GLenum flag = 0; + + switch (state) + { + case RENDER_STATE_BLENDING: flag = GL_BLEND; break; + case RENDER_STATE_DEPTH_TEST: flag = GL_DEPTH_TEST; break; + case RENDER_STATE_CULLING: flag = GL_CULL_FACE; break; + case RENDER_STATE_DEPTH_BIAS: flag = GL_POLYGON_OFFSET_FILL; break; + default: assert(false); break; + } + + if (enabled) + glEnable(flag); + else + glDisable(flag); +} + +void CGL33Device::SetColorMask(bool red, bool green, bool blue, bool alpha) +{ + glColorMask(red, green, blue, alpha); +} + +void CGL33Device::SetDepthTestFunc(CompFunc func) +{ + glDepthFunc(TranslateGfxCompFunc(func)); +} + +void CGL33Device::SetDepthBias(float factor, float units) +{ + glPolygonOffset(factor, units); +} + +void CGL33Device::SetAlphaTestFunc(CompFunc func, float refValue) +{ + glUniform1f(uni_AlphaReference, refValue); +} + +void CGL33Device::SetBlendFunc(BlendFunc srcBlend, BlendFunc dstBlend) +{ + glBlendFunc(TranslateGfxBlendFunc(srcBlend), TranslateGfxBlendFunc(dstBlend)); +} + +void CGL33Device::SetClearColor(const Color &color) +{ + glClearColor(color.r, color.g, color.b, color.a); +} + +void CGL33Device::SetGlobalAmbient(const Color &color) +{ + //glLightModelfv(GL_LIGHT_MODEL_AMBIENT, color.Array()); +} + +void CGL33Device::SetFogParams(FogMode mode, const Color &color, float start, float end, float density) +{ + // TODO: reimplement + + glUniform2f(uni_FogRange, start, end); + glUniform4f(uni_FogColor, color.r, color.g, color.b, color.a); + + /* + if (mode == FOG_LINEAR) glFogi(GL_FOG_MODE, GL_LINEAR); + else if (mode == FOG_EXP) glFogi(GL_FOG_MODE, GL_EXP); + else if (mode == FOG_EXP2) glFogi(GL_FOG_MODE, GL_EXP2); + else assert(false); + + glFogf(GL_FOG_START, start); + glFogf(GL_FOG_END, end); + glFogf(GL_FOG_DENSITY, density); + glFogfv(GL_FOG_COLOR, color.Array()); + // */ +} + +void CGL33Device::SetCullMode(CullMode mode) +{ + // Cull clockwise back faces, so front face is the opposite + // (assuming GL_CULL_FACE is GL_BACK) + if (mode == CULL_CW ) glFrontFace(GL_CCW); + else if (mode == CULL_CCW) glFrontFace(GL_CW); + else assert(false); +} + +void CGL33Device::SetShadeModel(ShadeModel model) +{ + glUniform1i(uni_SmoothShading, (model == SHADE_SMOOTH ? 1 : 0)); +} + +void CGL33Device::SetFillMode(FillMode mode) +{ + if (mode == FILL_POINT) glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); + else if (mode == FILL_LINES) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + else if (mode == FILL_POLY) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + else assert(false); +} + +void CGL33Device::InitOffscreenBuffer(int width, int height) +{ + width = Math::Min(width, m_maxRenderbufferSize); + height = Math::Min(height, m_maxRenderbufferSize); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + if (m_colorBuffer != 0) + glDeleteRenderbuffers(1, &m_colorBuffer); + + glGenRenderbuffers(1, &m_colorBuffer); + glBindRenderbuffer(GL_RENDERBUFFER, m_colorBuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + if (m_depthBuffer != 0) + glDeleteRenderbuffers(1, &m_depthBuffer); + + glGenRenderbuffers(1, &m_depthBuffer); + glBindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + if (m_framebuffer == 0) + glGenFramebuffers(1, &m_framebuffer); + + glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_colorBuffer); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer); + glBindFramebufferEXT(GL_FRAMEBUFFER, 0); + + GetLogger()->Info("Initialized offscreen buffer %dx%d\n", width, height); +} + +void CGL33Device::CopyFramebufferToTexture(Texture& texture, int xOffset, int yOffset, int x, int y, int width, int height) +{ + if (texture.id == 0) return; + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture.id); + + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, x, y, width, height); + + // Restore previous texture + glBindTexture(GL_TEXTURE_2D, m_currentTextures[0].id); +} + +void* CGL33Device::GetFrameBufferPixels() const +{ + GLubyte* pixels = new GLubyte[4 * m_config.size.x * m_config.size.y]; + + glReadPixels(0, 0, m_config.size.x, m_config.size.y, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + unsigned int* p = static_cast ( static_cast(pixels) ); + + for (int i = 0; i < m_config.size.x * m_config.size.y; ++i) + p[i] |= 0xFF000000; + + return static_cast(p); +} + +void CGL33Device::UpdateRenderingMode() +{ + bool enabled = m_texturesEnabled[0] && m_currentTextures[0].id != 0; + glUniform1i(uni_PrimaryTextureEnabled, enabled ? 1 : 0); + + enabled = m_texturesEnabled[1] && m_currentTextures[1].id != 0; + glUniform1i(uni_SecondaryTextureEnabled, enabled ? 1 : 0); + + enabled = m_texturesEnabled[2] && m_currentTextures[2].id != 0; + glUniform1i(uni_ShadowTextureEnabled, enabled ? 1 : 0); +} + +} // namespace Gfx diff --git a/src/graphics/opengl/gl33device.h b/src/graphics/opengl/gl33device.h new file mode 100644 index 00000000..698f81b7 --- /dev/null +++ b/src/graphics/opengl/gl33device.h @@ -0,0 +1,300 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsiteс.ch; http://colobot.info; http://github.com/colobot + * + * 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 3 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, see http://gnu.org/licenses + */ + +/** + * \file graphics/opengl/gl33device.h + * \brief OpenGL 3.3 implementation - CGL33Device class + */ + +#pragma once + +#include "graphics/core/device.h" +#include "graphics/opengl/glutil.h" +#include "GL/glew.h" + +#include +#include +#include +#include + + +// Graphics module namespace +namespace Gfx { + +/** + \class CGL33Device + \brief Implementation of CDevice interface in OpenGL 3.3 + + Provides the concrete implementation of 3D device in OpenGL. + + This class should be initialized (by calling Initialize() ) only after + setting the video mode by CApplication, once the OpenGL context is defined. + Because of that, CGLDeviceConfig is outside the CDevice class and must be set + in CApplication. +*/ +class CGL33Device : public CDevice +{ +public: + CGL33Device(const GLDeviceConfig &config); + virtual ~CGL33Device(); + + virtual void DebugHook() OVERRIDE; + virtual void DebugLights() OVERRIDE; + + virtual bool Create() OVERRIDE; + virtual void Destroy() OVERRIDE; + + void ConfigChanged(const GLDeviceConfig &newConfig); + + virtual void BeginScene() OVERRIDE; + virtual void EndScene() OVERRIDE; + + virtual void Clear() OVERRIDE; + + virtual void SetTransform(TransformType type, const Math::Matrix &matrix) OVERRIDE; + + virtual void SetMaterial(const Material &material) OVERRIDE; + + virtual int GetMaxLightCount() OVERRIDE; + virtual void SetLight(int index, const Light &light) OVERRIDE; + virtual void SetLightEnabled(int index, bool enabled) OVERRIDE; + + virtual Texture CreateTexture(CImage *image, const TextureCreateParams ¶ms) OVERRIDE; + virtual Texture CreateTexture(ImageData *data, const TextureCreateParams ¶ms) OVERRIDE; + virtual Texture CreateDepthTexture(int width, int height, int depth) OVERRIDE; + virtual void DestroyTexture(const Texture &texture) OVERRIDE; + virtual void DestroyAllTextures() OVERRIDE; + + virtual int GetMaxTextureStageCount() OVERRIDE; + virtual void SetTexture(int index, const Texture &texture) OVERRIDE; + virtual void SetTexture(int index, unsigned int textureId) OVERRIDE; + virtual void SetTextureEnabled(int index, bool enabled) OVERRIDE; + + virtual void SetTextureStageParams(int index, const TextureStageParams ¶ms) OVERRIDE; + + virtual void SetTextureStageWrap(int index, Gfx::TexWrapMode wrapS, Gfx::TexWrapMode wrapT) OVERRIDE; + virtual void SetTextureCoordGeneration(int index, TextureGenerationParams ¶ms) OVERRIDE; + + virtual void DrawPrimitive(PrimitiveType type, const Vertex *vertices , int vertexCount, + Color color = Color(1.0f, 1.0f, 1.0f, 1.0f)) OVERRIDE; + virtual void DrawPrimitive(PrimitiveType type, const VertexTex2 *vertices, int vertexCount, + Color color = Color(1.0f, 1.0f, 1.0f, 1.0f)) OVERRIDE; + virtual void DrawPrimitive(PrimitiveType type, const VertexCol *vertices , int vertexCount) OVERRIDE; + + virtual unsigned int CreateStaticBuffer(PrimitiveType primitiveType, const Vertex* vertices, int vertexCount) OVERRIDE; + virtual unsigned int CreateStaticBuffer(PrimitiveType primitiveType, const VertexTex2* vertices, int vertexCount) OVERRIDE; + virtual unsigned int CreateStaticBuffer(PrimitiveType primitiveType, const VertexCol* vertices, int vertexCount) OVERRIDE; + virtual void UpdateStaticBuffer(unsigned int bufferId, PrimitiveType primitiveType, const Vertex* vertices, int vertexCount) OVERRIDE; + virtual void UpdateStaticBuffer(unsigned int bufferId, PrimitiveType primitiveType, const VertexTex2* vertices, int vertexCount) OVERRIDE; + virtual void UpdateStaticBuffer(unsigned int bufferId, PrimitiveType primitiveType, const VertexCol* vertices, int vertexCount) OVERRIDE; + virtual void DrawStaticBuffer(unsigned int bufferId) OVERRIDE; + virtual void DestroyStaticBuffer(unsigned int bufferId) OVERRIDE; + + virtual int ComputeSphereVisibility(const Math::Vector ¢er, float radius) OVERRIDE; + + virtual void SetViewport(int x, int y, int width, int height) OVERRIDE; + + virtual void SetRenderState(RenderState state, bool enabled) OVERRIDE; + + virtual void SetColorMask(bool red, bool green, bool blue, bool alpha) OVERRIDE; + + virtual void SetDepthTestFunc(CompFunc func) OVERRIDE; + + virtual void SetDepthBias(float factor, float units) OVERRIDE; + + virtual void SetAlphaTestFunc(CompFunc func, float refValue) OVERRIDE; + + virtual void SetBlendFunc(BlendFunc srcBlend, BlendFunc dstBlend) OVERRIDE; + + virtual void SetClearColor(const Color &color) OVERRIDE; + + virtual void SetGlobalAmbient(const Color &color) OVERRIDE; + + virtual void SetFogParams(FogMode mode, const Color &color, float start, float end, float density) OVERRIDE; + + virtual void SetCullMode(CullMode mode) OVERRIDE; + + virtual void SetShadeModel(ShadeModel model) OVERRIDE; + + virtual void SetFillMode(FillMode mode) OVERRIDE; + + virtual void InitOffscreenBuffer(int width, int height) OVERRIDE; + + virtual void CopyFramebufferToTexture(Texture& texture, int xOffset, int yOffset, int x, int y, int width, int height) OVERRIDE; + + virtual void* GetFrameBufferPixels() const OVERRIDE; + +private: + //! Updates position for given light based on transformation matrices + void UpdateLightPosition(int index); + //! Updates the texture params for given texture stage + void UpdateTextureParams(int index); + //! Updates rendering mode + void UpdateRenderingMode(); + +private: + //! Current config + GLDeviceConfig m_config; + + //! Current world matrix + Math::Matrix m_worldMat; + //! Current view matrix + Math::Matrix m_viewMat; + //! OpenGL modelview matrix = world matrix * view matrix + Math::Matrix m_modelviewMat; + //! Current projection matrix + Math::Matrix m_projectionMat; + + //! The current material + Material m_material; + + //! Whether lighting is enabled + bool m_lighting; + //! Current lights + std::vector m_lights; + //! Current lights enable status + std::vector m_lightsEnabled; + + //! Current textures; \c NULL value means unassigned + std::vector m_currentTextures; + //! Current texture stages enable status + std::vector m_texturesEnabled; + //! Current texture params + std::vector m_textureStageParams; + + //! Set of all created textures + std::set m_allTextures; + + //! Type of vertex structure + enum VertexType + { + VERTEX_TYPE_NORMAL, + VERTEX_TYPE_TEX2, + VERTEX_TYPE_COL, + }; + + //! Info about static VBO buffers + struct VertexBufferInfo + { + PrimitiveType primitiveType; + GLuint vbo; + GLuint vao; + VertexType vertexType; + int vertexCount; + unsigned int size; + }; + + //! Detected capabilities + //! OpenGL version + int m_glMajor, m_glMinor; + //! Whether anisotropic filtering is available + bool m_anisotropyAvailable; + //! Maximum anisotropy level + int m_maxAnisotropy; + //! Map of saved VBO objects + std::map m_vboObjects; + //! Last ID of VBO object + unsigned int m_lastVboId; + + // Offscreen buffer + //! Framebuffer object + GLuint m_framebuffer; + //! Color renderbuffer + GLuint m_colorBuffer; + //! Depth renderbuffer + GLuint m_depthBuffer; + //! Maximum available renderbuffer size + int m_maxRenderbufferSize; + + //! Shader program + GLuint m_shaderProgram; + + //! Auxilliary vertex buffers for general rendering + unsigned int m_vertex; + unsigned int m_vertexTex2; + unsigned int m_vertexCol; + + // Uniforms + //! Projection matrix + GLint uni_ProjectionMatrix; + //! View matrix + GLint uni_ViewMatrix; + //! Model matrix + GLint uni_ModelMatrix; + //! Shadow matrix + GLint uni_ShadowMatrix; + //! Normal matrix + GLint uni_NormalMatrix; + + //! Primary texture sampler + GLint uni_PrimaryTexture; + //! Secondary texture sampler + GLint uni_SecondaryTexture; + //! Shadow texture sampler + GLint uni_ShadowTexture; + + GLint uni_PrimaryTextureEnabled; + GLint uni_SecondaryTextureEnabled; + GLint uni_ShadowTextureEnabled; + + // Fog parameters + //! true enables fog + GLint uni_FogEnabled; + //! Fog range + GLint uni_FogRange; + //! Fog color + GLint uni_FogColor; + + // Alpha test parameters + //! true enables alpha test + GLint uni_AlphaTestEnabled; + //! Alpha test reference value + GLint uni_AlphaReference; + + // Lighting parameters + GLint uni_SmoothShading; + //! true enables lighting + GLint uni_LightingEnabled; + //! Ambient color + GLint uni_AmbientColor; + //! Diffuse color + GLint uni_DiffuseColor; + //! Specular color + GLint uni_SpecularColor; + + struct + { + //! true enables light + GLint Enabled; + //! Light type + GLint Type; + //! Position or direction vector + GLint Position; + //! Ambient color + GLint Ambient; + //! Diffuse color + GLint Diffuse; + //! Specular color + GLint Specular; + //! Attenuation + GLint Attenuation; + } uni_Light[8]; +}; + +} // namespace Gfx diff --git a/src/graphics/opengl/gldevice.cpp b/src/graphics/opengl/gldevice.cpp index 7ba91427..c7fed3de 100644 --- a/src/graphics/opengl/gldevice.cpp +++ b/src/graphics/opengl/gldevice.cpp @@ -39,29 +39,6 @@ // Graphics module namespace namespace Gfx { -GLDeviceConfig::GLDeviceConfig() -{ - LoadDefault(); -} - -void GLDeviceConfig::LoadDefault() -{ - DeviceConfig::LoadDefault(); - - hardwareAccel = true; - - redSize = 8; - blueSize = 8; - greenSize = 8; - alphaSize = 8; - depthSize = 24; - - vboMode = VBO_MODE_AUTO; -} - -GLuint textureCoordinates[] = { GL_S, GL_T, GL_R, GL_Q }; -GLuint textureCoordGen[] = { GL_TEXTURE_GEN_S, GL_TEXTURE_GEN_T, GL_TEXTURE_GEN_R, GL_TEXTURE_GEN_Q }; - CGLDevice::CGLDevice(const GLDeviceConfig &config) { m_config = config; @@ -195,7 +172,7 @@ void CGLDevice::DebugLights() bool CGLDevice::Create() { - GetLogger()->Info("Creating CDevice\n"); + GetLogger()->Info("Creating CDevice - OpenGL 1.4\n"); /*static*/ bool glewInited = false; @@ -416,6 +393,16 @@ void CGLDevice::SetTransform(TransformType type, const Math::Matrix &matrix) glMatrixMode(GL_PROJECTION); glLoadMatrixf(m_projectionMat.Array()); } + else if (type == TRANSFORM_SHADOW) + { + if (!m_multitextureAvailable) + return; + + Math::Matrix temp = matrix; + glActiveTexture(GL_TEXTURE2); + glMatrixMode(GL_TEXTURE); + glLoadMatrixf(temp.Array()); + } else { assert(false); @@ -951,47 +938,37 @@ void CGLDevice::SetTextureCoordGeneration(int index, TextureGenerationParams &pa GLuint texCoordGen = textureCoordGen[i]; GLuint texCoord = textureCoordinates[i]; - if (params.coords[i].mode == TEX_GEN_NONE) + switch (params.coords[i].mode) { + case TEX_GEN_NONE: glDisable(texCoordGen); - } - else - { + break; + case TEX_GEN_OBJECT_LINEAR: glEnable(texCoordGen); - - switch (params.coords[i].mode) - { - case TEX_GEN_OBJECT_LINEAR: - glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); - glTexGenfv(texCoord, GL_OBJECT_PLANE, params.coords[i].plane); - break; - case TEX_GEN_EYE_LINEAR: - glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); - glTexGenfv(texCoord, GL_EYE_PLANE, params.coords[i].plane); - break; - case TEX_GEN_SPHERE_MAP: - glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); - break; - case TEX_GEN_NORMAL_MAP: - glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP); - break; - case TEX_GEN_REFLECTION_MAP: - glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); - break; - } + glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + glTexGenfv(texCoord, GL_OBJECT_PLANE, params.coords[i].plane); + break; + case TEX_GEN_EYE_LINEAR: + glEnable(texCoordGen); + glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); + glTexGenfv(texCoord, GL_EYE_PLANE, params.coords[i].plane); + break; + case TEX_GEN_SPHERE_MAP: + glEnable(texCoordGen); + glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); + break; + case TEX_GEN_NORMAL_MAP: + glEnable(texCoordGen); + glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP); + break; + case TEX_GEN_REFLECTION_MAP: + glEnable(texCoordGen); + glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); + break; } } } -void CGLDevice::SetTextureMatrix(int index, Math::Matrix& matrix) -{ - if (!m_multitextureAvailable && index != 0) - return; - - glMatrixMode(GL_TEXTURE); - glLoadMatrixf(matrix.Array()); -} - void CGLDevice::UpdateTextureParams(int index) { assert(index >= 0 && index < static_cast( m_currentTextures.size() )); @@ -1200,21 +1177,6 @@ void CGLDevice::SetTextureStageWrap(int index, TexWrapMode wrapS, TexWrapMode wr else assert(false); } -GLenum TranslateGfxPrimitive(PrimitiveType type) -{ - GLenum flag = 0; - switch (type) - { - case PRIMITIVE_POINTS: flag = GL_POINTS; break; - case PRIMITIVE_LINES: flag = GL_LINES; break; - case PRIMITIVE_LINE_STRIP: flag = GL_LINE_STRIP; break; - case PRIMITIVE_TRIANGLES: flag = GL_TRIANGLES; break; - case PRIMITIVE_TRIANGLE_STRIP: flag = GL_TRIANGLE_STRIP; break; - default: assert(false); break; - } - return flag; -} - void CGLDevice::DrawPrimitive(PrimitiveType type, const Vertex *vertices, int vertexCount, Color color) { @@ -1654,16 +1616,6 @@ void CGLDevice::DestroyStaticBuffer(unsigned int bufferId) } } -bool InPlane(Math::Vector normal, float originPlane, Math::Vector center, float radius) -{ - float distance = originPlane + Math::DotProduct(normal, center); - - if (distance < -radius) - return false; - - return true; -} - /* Based on libwine's implementation */ int CGLDevice::ComputeSphereVisibility(const Math::Vector ¢er, float radius) @@ -1811,40 +1763,6 @@ void CGLDevice::SetRenderState(RenderState state, bool enabled) glDisable(flag); } -CompFunc TranslateGLCompFunc(GLenum flag) -{ - switch (flag) - { - case GL_NEVER: return COMP_FUNC_NEVER; - case GL_LESS: return COMP_FUNC_LESS; - case GL_EQUAL: return COMP_FUNC_EQUAL; - case GL_NOTEQUAL: return COMP_FUNC_NOTEQUAL; - case GL_LEQUAL: return COMP_FUNC_LEQUAL; - case GL_GREATER: return COMP_FUNC_GREATER; - case GL_GEQUAL: return COMP_FUNC_GEQUAL; - case GL_ALWAYS: return COMP_FUNC_ALWAYS; - default: assert(false); break; - } - return COMP_FUNC_NEVER; -} - -GLenum TranslateGfxCompFunc(CompFunc func) -{ - switch (func) - { - case COMP_FUNC_NEVER: return GL_NEVER; - case COMP_FUNC_LESS: return GL_LESS; - case COMP_FUNC_EQUAL: return GL_EQUAL; - case COMP_FUNC_NOTEQUAL: return GL_NOTEQUAL; - case COMP_FUNC_LEQUAL: return GL_LEQUAL; - case COMP_FUNC_GREATER: return GL_GREATER; - case COMP_FUNC_GEQUAL: return GL_GEQUAL; - case COMP_FUNC_ALWAYS: return GL_ALWAYS; - default: assert(false); break; - } - return 0; -} - void CGLDevice::SetColorMask(bool red, bool green, bool blue, bool alpha) { glColorMask(red, green, blue, alpha); @@ -1865,47 +1783,6 @@ void CGLDevice::SetAlphaTestFunc(CompFunc func, float refValue) glAlphaFunc(TranslateGfxCompFunc(func), refValue); } -BlendFunc TranslateGLBlendFunc(GLenum flag) -{ - switch (flag) - { - case GL_ZERO: return BLEND_ZERO; - case GL_ONE: return BLEND_ONE; - case GL_SRC_COLOR: return BLEND_SRC_COLOR; - case GL_ONE_MINUS_SRC_COLOR: return BLEND_INV_SRC_COLOR; - case GL_DST_COLOR: return BLEND_DST_COLOR; - case GL_ONE_MINUS_DST_COLOR: return BLEND_INV_DST_COLOR; - case GL_SRC_ALPHA: return BLEND_SRC_ALPHA; - case GL_ONE_MINUS_SRC_ALPHA: return BLEND_INV_SRC_ALPHA; - case GL_DST_ALPHA: return BLEND_DST_ALPHA; - case GL_ONE_MINUS_DST_ALPHA: return BLEND_INV_DST_ALPHA; - case GL_SRC_ALPHA_SATURATE: return BLEND_SRC_ALPHA_SATURATE; - default: assert(false); break; - } - - return BLEND_ZERO; -} - -GLenum TranslateGfxBlendFunc(BlendFunc func) -{ - switch (func) - { - case BLEND_ZERO: return GL_ZERO; - case BLEND_ONE: return GL_ONE; - case BLEND_SRC_COLOR: return GL_SRC_COLOR; - case BLEND_INV_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR; - case BLEND_DST_COLOR: return GL_DST_COLOR; - case BLEND_INV_DST_COLOR: return GL_ONE_MINUS_DST_COLOR; - case BLEND_SRC_ALPHA: return GL_SRC_ALPHA; - case BLEND_INV_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA; - case BLEND_DST_ALPHA: return GL_DST_ALPHA; - case BLEND_INV_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA; - case BLEND_SRC_ALPHA_SATURATE: return GL_SRC_ALPHA_SATURATE; - default: assert(false); break; - } - return 0; -} - void CGLDevice::SetBlendFunc(BlendFunc srcBlend, BlendFunc dstBlend) { glBlendFunc(TranslateGfxBlendFunc(srcBlend), TranslateGfxBlendFunc(dstBlend)); diff --git a/src/graphics/opengl/gldevice.h b/src/graphics/opengl/gldevice.h index 61a77360..dd9e8d06 100644 --- a/src/graphics/opengl/gldevice.h +++ b/src/graphics/opengl/gldevice.h @@ -26,6 +26,7 @@ #include "graphics/core/device.h" +#include "graphics/opengl/glutil.h" #include "GL/glew.h" #include @@ -37,17 +38,6 @@ // Graphics module namespace namespace Gfx { -/** - \enum VBOMode - \brief VBO autodetect/override - */ -enum VBOMode -{ - VBO_MODE_ENABLE, //! < override: enable - VBO_MODE_DISABLE, //! < override: disable - VBO_MODE_AUTO //! < autodetect -}; - /** \enum VertexBufferType \brief Specifies type of vertex buffer to use @@ -66,35 +56,6 @@ enum ShadowMappingSupport SMS_CORE //! Core support }; -/** - \struct GLDeviceConfig - \brief Additional config with OpenGL-specific settings */ -struct GLDeviceConfig : public DeviceConfig -{ - //! Size of red channel in bits - int redSize; - //! Size of green channel in bits - int greenSize; - //! Size of blue channel in bits - int blueSize; - //! Size of alpha channel in bits - int alphaSize; - //! Color depth in bits - int depthSize; - - //! Force hardware acceleration (video mode set will fail on lack of hw accel) - bool hardwareAccel; - - //! VBO override/autodetect - VBOMode vboMode; - - //! Constructor calls LoadDefaults() - GLDeviceConfig(); - - //! Loads the default values - void LoadDefault(); -}; - struct GLDevicePrivate; /** @@ -153,7 +114,6 @@ public: virtual void SetTextureStageWrap(int index, Gfx::TexWrapMode wrapS, Gfx::TexWrapMode wrapT) OVERRIDE; virtual void SetTextureCoordGeneration(int index, TextureGenerationParams ¶ms) OVERRIDE; - virtual void SetTextureMatrix(int index, Math::Matrix& matrix) OVERRIDE; virtual void DrawPrimitive(PrimitiveType type, const Vertex *vertices , int vertexCount, Color color = Color(1.0f, 1.0f, 1.0f, 1.0f)) OVERRIDE; diff --git a/src/graphics/opengl/glutil.cpp b/src/graphics/opengl/glutil.cpp new file mode 100644 index 00000000..139a26eb --- /dev/null +++ b/src/graphics/opengl/glutil.cpp @@ -0,0 +1,151 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsiteс.ch; http://colobot.info; http://github.com/colobot + * + * 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 3 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, see http://gnu.org/licenses + */ + +#include "graphics/opengl/glutil.h" +#include "graphics/core/device.h" + +#include "GL/glew.h" + +// Graphics module namespace +namespace Gfx { + +GLDeviceConfig::GLDeviceConfig() +{ + LoadDefault(); +} + +GLuint textureCoordinates[] = { GL_S, GL_T, GL_R, GL_Q }; +GLuint textureCoordGen[] = { GL_TEXTURE_GEN_S, GL_TEXTURE_GEN_T, GL_TEXTURE_GEN_R, GL_TEXTURE_GEN_Q }; + +void GLDeviceConfig::LoadDefault() +{ + DeviceConfig::LoadDefault(); + + hardwareAccel = true; + + redSize = 8; + blueSize = 8; + greenSize = 8; + alphaSize = 8; + depthSize = 24; + + vboMode = VBO_MODE_AUTO; +} + +GLenum TranslateGfxPrimitive(PrimitiveType type) +{ + GLenum flag = 0; + switch (type) + { + case PRIMITIVE_POINTS: flag = GL_POINTS; break; + case PRIMITIVE_LINES: flag = GL_LINES; break; + case PRIMITIVE_LINE_STRIP: flag = GL_LINE_STRIP; break; + case PRIMITIVE_TRIANGLES: flag = GL_TRIANGLES; break; + case PRIMITIVE_TRIANGLE_STRIP: flag = GL_TRIANGLE_STRIP; break; + default: assert(false); break; + } + return flag; +} + +CompFunc TranslateGLCompFunc(GLenum flag) +{ + switch (flag) + { + case GL_NEVER: return COMP_FUNC_NEVER; + case GL_LESS: return COMP_FUNC_LESS; + case GL_EQUAL: return COMP_FUNC_EQUAL; + case GL_NOTEQUAL: return COMP_FUNC_NOTEQUAL; + case GL_LEQUAL: return COMP_FUNC_LEQUAL; + case GL_GREATER: return COMP_FUNC_GREATER; + case GL_GEQUAL: return COMP_FUNC_GEQUAL; + case GL_ALWAYS: return COMP_FUNC_ALWAYS; + default: assert(false); break; + } + return COMP_FUNC_NEVER; +} + +GLenum TranslateGfxCompFunc(CompFunc func) +{ + switch (func) + { + case COMP_FUNC_NEVER: return GL_NEVER; + case COMP_FUNC_LESS: return GL_LESS; + case COMP_FUNC_EQUAL: return GL_EQUAL; + case COMP_FUNC_NOTEQUAL: return GL_NOTEQUAL; + case COMP_FUNC_LEQUAL: return GL_LEQUAL; + case COMP_FUNC_GREATER: return GL_GREATER; + case COMP_FUNC_GEQUAL: return GL_GEQUAL; + case COMP_FUNC_ALWAYS: return GL_ALWAYS; + default: assert(false); break; + } + return 0; +} + +BlendFunc TranslateGLBlendFunc(GLenum flag) +{ + switch (flag) + { + case GL_ZERO: return BLEND_ZERO; + case GL_ONE: return BLEND_ONE; + case GL_SRC_COLOR: return BLEND_SRC_COLOR; + case GL_ONE_MINUS_SRC_COLOR: return BLEND_INV_SRC_COLOR; + case GL_DST_COLOR: return BLEND_DST_COLOR; + case GL_ONE_MINUS_DST_COLOR: return BLEND_INV_DST_COLOR; + case GL_SRC_ALPHA: return BLEND_SRC_ALPHA; + case GL_ONE_MINUS_SRC_ALPHA: return BLEND_INV_SRC_ALPHA; + case GL_DST_ALPHA: return BLEND_DST_ALPHA; + case GL_ONE_MINUS_DST_ALPHA: return BLEND_INV_DST_ALPHA; + case GL_SRC_ALPHA_SATURATE: return BLEND_SRC_ALPHA_SATURATE; + default: assert(false); break; + } + + return BLEND_ZERO; +} + +GLenum TranslateGfxBlendFunc(BlendFunc func) +{ + switch (func) + { + case BLEND_ZERO: return GL_ZERO; + case BLEND_ONE: return GL_ONE; + case BLEND_SRC_COLOR: return GL_SRC_COLOR; + case BLEND_INV_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR; + case BLEND_DST_COLOR: return GL_DST_COLOR; + case BLEND_INV_DST_COLOR: return GL_ONE_MINUS_DST_COLOR; + case BLEND_SRC_ALPHA: return GL_SRC_ALPHA; + case BLEND_INV_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA; + case BLEND_DST_ALPHA: return GL_DST_ALPHA; + case BLEND_INV_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA; + case BLEND_SRC_ALPHA_SATURATE: return GL_SRC_ALPHA_SATURATE; + default: assert(false); break; + } + return 0; +} + +bool InPlane(Math::Vector normal, float originPlane, Math::Vector center, float radius) +{ + float distance = originPlane + Math::DotProduct(normal, center); + + if (distance < -radius) + return false; + + return true; +} + +} diff --git a/src/graphics/opengl/glutil.h b/src/graphics/opengl/glutil.h new file mode 100644 index 00000000..4e866988 --- /dev/null +++ b/src/graphics/opengl/glutil.h @@ -0,0 +1,85 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsiteс.ch; http://colobot.info; http://github.com/colobot + * + * 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 3 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, see http://gnu.org/licenses + */ + +#pragma once + +#include "graphics/core/device.h" +#include "GL/glew.h" + +// Graphics module namespace +namespace Gfx +{ + +GLuint textureCoordinates[]; +GLuint textureCoordGen[]; + +/** +\enum VBOMode +\brief VBO autodetect/override +*/ +enum VBOMode +{ + VBO_MODE_ENABLE, //! < override: enable + VBO_MODE_DISABLE, //! < override: disable + VBO_MODE_AUTO //! < autodetect +}; + +/** +\struct GLDeviceConfig +\brief Additional config with OpenGL-specific settings */ +struct GLDeviceConfig : public DeviceConfig +{ + //! Size of red channel in bits + int redSize; + //! Size of green channel in bits + int greenSize; + //! Size of blue channel in bits + int blueSize; + //! Size of alpha channel in bits + int alphaSize; + //! Color depth in bits + int depthSize; + + //! Force hardware acceleration (video mode set will fail on lack of hw accel) + bool hardwareAccel; + + //! VBO override/autodetect + VBOMode vboMode; + + //! Constructor calls LoadDefaults() + GLDeviceConfig(); + + //! Loads the default values + void LoadDefault(); +}; + +//! Translate Gfx primitive type to OpenGL primitive type +GLenum TranslateGfxPrimitive(PrimitiveType type); + +CompFunc TranslateGLCompFunc(GLenum flag); + +GLenum TranslateGfxCompFunc(CompFunc func); + +BlendFunc TranslateGLBlendFunc(GLenum flag); + +GLenum TranslateGfxBlendFunc(BlendFunc func); + +bool InPlane(Math::Vector normal, float originPlane, Math::Vector center, float radius); + +} // namespace Gfx