diff --git a/src/graphics/core/device.h b/src/graphics/core/device.h index 875f613d..4f6aef54 100644 --- a/src/graphics/core/device.h +++ b/src/graphics/core/device.h @@ -51,6 +51,7 @@ namespace Gfx class CFramebuffer; class CUIRenderer; +class CTerrainRenderer; struct FramebufferParams; struct Light; struct Material; @@ -463,6 +464,8 @@ public: virtual void SetRenderMode(RenderMode mode) = 0; //! Returns UI renderer virtual CUIRenderer* GetUIRenderer() = 0; + //! Returns terrain renderer + virtual CTerrainRenderer* GetTerrainRenderer() = 0; //! Restores device rendering mode virtual void Restore() = 0; diff --git a/src/graphics/core/renderers.h b/src/graphics/core/renderers.h index 63691472..a933a461 100644 --- a/src/graphics/core/renderers.h +++ b/src/graphics/core/renderers.h @@ -32,6 +32,7 @@ namespace Gfx { +class CVertexBuffer; enum PrimitiveType; struct Texture; @@ -68,4 +69,29 @@ public: virtual void DrawPrimitive(PrimitiveType type, int count, const Vertex2D* vertices) = 0; }; +class CTerrainRenderer : public CRenderer +{ +public: + virtual ~CTerrainRenderer() { } + + virtual void Begin() = 0; + + virtual void End() = 0; + + //! Sets projection matrix + virtual void SetProjectionMatrix(const glm::mat4& matrix) = 0; + //! Sets view matrix + virtual void SetViewMatrix(const glm::mat4& matrix) = 0; + //! Sets model matrix + virtual void SetModelMatrix(const glm::mat4& matrix) = 0; + + //! Sets primary texture, setting texture 0 means using white texture + virtual void SetPrimaryTexture(const Texture& texture) = 0; + //! Sets secondary texture + virtual void SetSecondaryTexture(const Texture& texture) = 0; + + //! Draws terrain object + virtual void DrawObject(const glm::mat4& matrix, const CVertexBuffer* buffer) = 0; +}; + } // namespace Gfx diff --git a/src/graphics/opengl/gl33device.cpp b/src/graphics/opengl/gl33device.cpp index 7e2723fa..154694fd 100644 --- a/src/graphics/opengl/gl33device.cpp +++ b/src/graphics/opengl/gl33device.cpp @@ -499,6 +499,7 @@ bool CGL33Device::Create() } m_uiRenderer = std::make_unique(this); + m_terrainRenderer = std::make_unique(this); SetRenderMode(RENDER_MODE_NORMAL); @@ -565,6 +566,7 @@ void CGL33Device::Destroy() m_buffers.clear(); m_uiRenderer = nullptr; + m_terrainRenderer = nullptr; } void CGL33Device::ConfigChanged(const DeviceConfig& newConfig) @@ -641,6 +643,11 @@ CUIRenderer* CGL33Device::GetUIRenderer() return m_uiRenderer.get(); } +CTerrainRenderer* CGL33Device::GetTerrainRenderer() +{ + return m_terrainRenderer.get(); +} + void CGL33Device::Restore() { switch (m_mode) diff --git a/src/graphics/opengl/gl33device.h b/src/graphics/opengl/gl33device.h index 6baa2af2..11043058 100644 --- a/src/graphics/opengl/gl33device.h +++ b/src/graphics/opengl/gl33device.h @@ -81,6 +81,7 @@ public: }; class CGL33UIRenderer; +class CGL33TerrainRenderer; /** \class CGL33Device @@ -116,6 +117,7 @@ public: void SetRenderMode(RenderMode mode) override; CUIRenderer* GetUIRenderer() override; + CTerrainRenderer* GetTerrainRenderer() override; void Restore() override; @@ -308,6 +310,8 @@ private: //! Interface renderer std::unique_ptr m_uiRenderer; + //! Terrain renderer + std::unique_ptr m_terrainRenderer; }; } // namespace Gfx diff --git a/src/graphics/opengl/gl33renderers.cpp b/src/graphics/opengl/gl33renderers.cpp index fefbb6cb..ac4b3ac2 100644 --- a/src/graphics/opengl/gl33renderers.cpp +++ b/src/graphics/opengl/gl33renderers.cpp @@ -17,7 +17,9 @@ namespace Gfx CGL33UIRenderer::CGL33UIRenderer(CGL33Device* device) : m_device(device) { - GLint shaders[2]; + GetLogger()->Info("Creating CGL33UIRenderer\n"); + + GLint shaders[2] = {}; shaders[0] = LoadShader(GL_VERTEX_SHADER, "shaders/gl33/ui_vs.glsl"); if (shaders[0] == 0) @@ -88,6 +90,8 @@ CGL33UIRenderer::CGL33UIRenderer(CGL33Device* device) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE); glUseProgram(0); + + GetLogger()->Info("CGL33UIRenderer created successfully\n"); } CGL33UIRenderer::~CGL33UIRenderer() @@ -219,4 +223,180 @@ void CGL33UIRenderer::UpdateUniforms() glBindBuffer(GL_COPY_WRITE_BUFFER, 0); } + +CGL33TerrainRenderer::CGL33TerrainRenderer(CGL33Device* device) + : m_device(device) +{ + GetLogger()->Info("Creating CGL33TerrainRenderer\n"); + + GLint shaders[2] = {}; + + shaders[0] = LoadShader(GL_VERTEX_SHADER, "shaders/gl33/terrain_vs.glsl"); + if (shaders[0] == 0) + { + GetLogger()->Error("Cound not create vertex shader from file 'terrain_vs.glsl'\n"); + return; + } + + shaders[1] = LoadShader(GL_FRAGMENT_SHADER, "shaders/gl33/terrain_fs.glsl"); + if (shaders[1] == 0) + { + GetLogger()->Error("Cound not create fragment shader from file 'terrain_fs.glsl'\n"); + return; + } + + m_program = LinkProgram(2, shaders); + if (m_program == 0) + { + GetLogger()->Error("Cound not link shader program for terrain renderer\n"); + return; + } + + glDeleteShader(shaders[0]); + glDeleteShader(shaders[1]); + + glUseProgram(m_program); + + // Setup uniforms + auto identity = glm::identity(); + + m_uniforms.projectionMatrix = identity; + m_uniforms.viewMatrix = identity; + m_uniforms.modelMatrix = identity; + + glGenBuffers(1, &m_uniformBuffer); + glBindBuffer(GL_COPY_WRITE_BUFFER, m_uniformBuffer); + glBufferData(GL_COPY_WRITE_BUFFER, sizeof(Uniforms), &m_uniforms, GL_STREAM_DRAW); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); + + // Bind uniform block to uniform buffer binding + GLuint blockIndex = glGetUniformBlockIndex(m_program, "Uniforms"); + + glBindBufferBase(GL_UNIFORM_BUFFER, 0, m_uniformBuffer); + glUniformBlockBinding(m_program, blockIndex, 0); + + // Set texture units to 10th and 11th + auto texture = glGetUniformLocation(m_program, "uni_PrimaryTexture"); + glUniform1i(texture, 10); + + texture = glGetUniformLocation(m_program, "uni_SecondaryTexture"); + glUniform1i(texture, 11); + + // White texture + glActiveTexture(GL_TEXTURE0); + glGenTextures(1, &m_whiteTexture); + glBindTexture(GL_TEXTURE_2D, m_whiteTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_ONE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_ONE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_ONE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE); + + glUseProgram(0); + + GetLogger()->Info("CGL33TerrainRenderer created successfully\n"); +} + +CGL33TerrainRenderer::~CGL33TerrainRenderer() +{ + glDeleteProgram(m_program); + glDeleteTextures(1, &m_whiteTexture); +} + +void CGL33TerrainRenderer::Begin() +{ + glUseProgram(m_program); +} + +void CGL33TerrainRenderer::End() +{ + m_device->Restore(); +} + +void CGL33TerrainRenderer::SetProjectionMatrix(const glm::mat4& matrix) +{ + m_uniforms.projectionMatrix = matrix; + m_uniformsDirty = true; +} + +void CGL33TerrainRenderer::SetViewMatrix(const glm::mat4& matrix) +{ + glm::mat4 scale(1.0f); + scale[2][2] = -1.0f; + + m_uniforms.viewMatrix = scale * matrix; + m_uniformsDirty = true; +} + +void CGL33TerrainRenderer::SetModelMatrix(const glm::mat4& matrix) +{ + m_uniforms.modelMatrix = matrix; + m_uniformsDirty = true; +} + +void CGL33TerrainRenderer::SetPrimaryTexture(const Texture& texture) +{ + if (m_primaryTexture == texture.id) return; + + m_primaryTexture = texture.id; + + glActiveTexture(GL_TEXTURE10); + + if (texture.id == 0) + glBindTexture(GL_TEXTURE_2D, m_whiteTexture); + else + glBindTexture(GL_TEXTURE_2D, texture.id); +} + +void CGL33TerrainRenderer::SetSecondaryTexture(const Texture& texture) +{ + if (m_secondaryTexture == texture.id) return; + + m_secondaryTexture = texture.id; + + glActiveTexture(GL_TEXTURE11); + + if (texture.id == 0) + glBindTexture(GL_TEXTURE_2D, m_whiteTexture); + else + glBindTexture(GL_TEXTURE_2D, texture.id); +} + +void CGL33TerrainRenderer::DrawObject(const glm::mat4& matrix, const CVertexBuffer* buffer) +{ + auto b = dynamic_cast(buffer); + + if (b == nullptr) + { + GetLogger()->Error("No vertex buffer"); + return; + } + + SetModelMatrix(matrix); + + if (m_uniformsDirty) + { + glBindBuffer(GL_COPY_WRITE_BUFFER, m_uniformBuffer); + glBufferData(GL_COPY_WRITE_BUFFER, sizeof(Uniforms), nullptr, GL_STREAM_DRAW); + glBufferSubData(GL_COPY_WRITE_BUFFER, 0, sizeof(Uniforms), &m_uniforms); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); + + m_uniformsDirty = false; + } + + glBindBufferBase(GL_UNIFORM_BUFFER, 0, m_uniformBuffer); + glBindVertexArray(b->GetVAO()); + + glDrawArrays(TranslateGfxPrimitive(b->GetType()), 0, b->Size()); +} + +void CGL33TerrainRenderer::Flush() +{ + +} + } // namespace Gfx diff --git a/src/graphics/opengl/gl33renderers.h b/src/graphics/opengl/gl33renderers.h index 74810abe..27a37455 100644 --- a/src/graphics/opengl/gl33renderers.h +++ b/src/graphics/opengl/gl33renderers.h @@ -62,7 +62,8 @@ private: glm::mat4 projectionMatrix; glm::vec4 color; }; - Uniforms m_uniforms; + Uniforms m_uniforms = {}; + // true means uniforms need to be updated bool m_uniformsDirty = false; @@ -91,4 +92,60 @@ private: GLuint m_currentTexture = 0; }; +class CGL33TerrainRenderer : public CTerrainRenderer +{ +public: + CGL33TerrainRenderer(CGL33Device* device); + virtual ~CGL33TerrainRenderer(); + + virtual void Begin() override; + + virtual void End() override; + + //! Sets projection matrix + virtual void SetProjectionMatrix(const glm::mat4& matrix) override; + //! Sets view matrix + virtual void SetViewMatrix(const glm::mat4& matrix) override; + //! Sets model matrix + virtual void SetModelMatrix(const glm::mat4& matrix) override; + + //! Sets primary texture, setting texture 0 means using white texture + virtual void SetPrimaryTexture(const Texture& texture) override; + //! Sets secondary texture + virtual void SetSecondaryTexture(const Texture& texture) override; + + //! Draws terrain object + virtual void DrawObject(const glm::mat4& matrix, const CVertexBuffer* buffer) override; + + virtual void Flush() override; + +private: + CGL33Device* const m_device; + + // Uniform data + struct Uniforms + { + glm::mat4 projectionMatrix; + glm::mat4 viewMatrix; + glm::mat4 modelMatrix; + }; + + Uniforms m_uniforms = {}; + + // true means uniforms need to be updated + bool m_uniformsDirty = false; + + GLuint m_uniformBuffer = 0; + + // Shader program + GLuint m_program = 0; + + // 1x1 white texture + GLuint m_whiteTexture = 0; + // Currently bound primary texture + GLuint m_primaryTexture = 0; + // Currently bound secondary texture + GLuint m_secondaryTexture = 0; +}; + } // namespace Gfx diff --git a/src/graphics/opengl/shaders/gl33/terrain_fs.glsl b/src/graphics/opengl/shaders/gl33/terrain_fs.glsl new file mode 100644 index 00000000..145c44af --- /dev/null +++ b/src/graphics/opengl/shaders/gl33/terrain_fs.glsl @@ -0,0 +1,48 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.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 + */ + +// FRAGMENT SHADER - TERRAIN RENDERER +#version 330 core + +uniform sampler2D uni_PrimaryTexture; +uniform sampler2D uni_SecondaryTexture; + +in VertexData +{ + vec4 Color; + vec2 TexCoord0; + vec2 TexCoord1; + vec3 Normal; + vec4 ShadowCoord; + vec4 LightColor; + float Distance; + vec3 CameraDirection; +} data; + +out vec4 out_FragColor; + +void main() +{ + vec4 color = data.Color; + + color = color * texture(uni_PrimaryTexture, data.TexCoord0); + color = color * texture(uni_SecondaryTexture, data.TexCoord1); + + out_FragColor = color; +} diff --git a/src/graphics/opengl/shaders/gl33/terrain_vs.glsl b/src/graphics/opengl/shaders/gl33/terrain_vs.glsl new file mode 100644 index 00000000..7528dd97 --- /dev/null +++ b/src/graphics/opengl/shaders/gl33/terrain_vs.glsl @@ -0,0 +1,62 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.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 + */ + +// VERTEX SHADER - TERRAIN RENDERER +#version 330 core + +uniform Uniforms +{ + mat4 uni_ProjectionMatrix; + mat4 uni_ViewMatrix; + mat4 uni_ModelMatrix; +}; + +layout(location = 0) in vec4 in_VertexCoord; +layout(location = 1) in vec3 in_Normal; +layout(location = 2) in vec4 in_Color; +layout(location = 3) in vec2 in_TexCoord0; +layout(location = 4) in vec2 in_TexCoord1; + +out VertexData +{ + vec4 Color; + vec2 TexCoord0; + vec2 TexCoord1; + vec3 Normal; + vec4 ShadowCoord; + vec4 LightColor; + float Distance; + vec3 CameraDirection; +} data; + +void main() +{ + vec4 position = uni_ModelMatrix * in_VertexCoord; + vec4 eyeSpace = uni_ViewMatrix * position; + gl_Position = uni_ProjectionMatrix * eyeSpace; + //vec4 shadowCoord = uni_ShadowMatrix * position; + + data.Color = in_Color; + data.TexCoord0 = in_TexCoord0; + data.TexCoord1 = in_TexCoord1; + data.Normal = in_Normal;//normalize((uni_NormalMatrix * vec4(in_Normal, 0.0f)).xyz); + //data.ShadowCoord = vec4(shadowCoord.xyz / shadowCoord.w, 1.0f); + //data.Distance = abs(eyeSpace.z); + //data.CameraDirection = uni_CameraPosition - position.xyz; +}