diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a3d00185..cce3b78f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -147,6 +147,8 @@ add_library(colobotbase STATIC graphics/opengl/gl33renderers.h graphics/opengl/gl33objectrenderer.cpp graphics/opengl/gl33objectrenderer.h + graphics/opengl/gl33particlerenderer.cpp + graphics/opengl/gl33particlerenderer.h graphics/opengl/glframebuffer.cpp graphics/opengl/glframebuffer.h graphics/opengl/glutil.cpp diff --git a/src/graphics/core/device.h b/src/graphics/core/device.h index 5f737018..d16f55f8 100644 --- a/src/graphics/core/device.h +++ b/src/graphics/core/device.h @@ -48,6 +48,8 @@ class CUIRenderer; class CTerrainRenderer; class CShadowRenderer; class CObjectRenderer; +class CParticleRenderer; + struct FramebufferParams; struct Light; struct Material; @@ -442,6 +444,8 @@ public: virtual CTerrainRenderer* GetTerrainRenderer() = 0; //! Returns object renderer virtual CObjectRenderer* GetObjectRenderer() = 0; + //! Returns particle renderer + virtual CParticleRenderer* GetParticleRenderer() = 0; //! Returns shadow renderer virtual CShadowRenderer* GetShadowRenderer() = 0; diff --git a/src/graphics/core/renderers.h b/src/graphics/core/renderers.h index d4e23949..ac705c01 100644 --- a/src/graphics/core/renderers.h +++ b/src/graphics/core/renderers.h @@ -182,6 +182,38 @@ public: virtual void DrawObject(const CVertexBuffer* buffer) = 0; }; +/** + * \class CParticleRenderer + * \brief Abstract interface for particle renderers + */ +class CParticleRenderer : public CRenderer +{ +public: + virtual ~CParticleRenderer() { } + + 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 color + virtual void SetColor(const glm::vec4& color) = 0; + //! Sets texture + virtual void SetTexture(const Texture& texture) = 0; + + //! Sets transparency mode + virtual void SetTransparency(TransparencyMode mode) = 0; + + //! Draws particles + virtual void DrawParticle(PrimitiveType type, int count, const Vertex3D* vertices) = 0; +}; + /** * \class CShadowRenderer * \brief Abstract interface for shadow renderers diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index a794f061..3dd8e144 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -3672,10 +3672,17 @@ void CEngine::Draw3DScene() } m_displayGoto.clear(); + auto particleRenderer = m_device->GetParticleRenderer(); + particleRenderer->Begin(); + particleRenderer->SetProjectionMatrix(m_matProj); + particleRenderer->SetViewMatrix(m_matView); + CProfiler::StartPerformanceCounter(PCNT_RENDER_PARTICLE_WORLD); m_particle->DrawParticle(SH_WORLD); // draws the particles of the 3D world CProfiler::StopPerformanceCounter(PCNT_RENDER_PARTICLE_WORLD); + particleRenderer->End(); + m_device->SetRenderState(RENDER_STATE_LIGHTING, true); m_lightning->Draw(); // draws lightning @@ -4293,9 +4300,17 @@ void CEngine::DrawInterface() if (!m_screenshotMode && m_renderInterface) { + auto particleRenderer = m_device->GetParticleRenderer(); + particleRenderer->Begin(); + particleRenderer->SetProjectionMatrix(m_matProjInterface); + particleRenderer->SetViewMatrix(m_matViewInterface); + particleRenderer->SetModelMatrix(m_matWorldInterface); + CProfiler::StartPerformanceCounter(PCNT_RENDER_PARTICLE_IFACE); m_particle->DrawParticle(SH_INTERFACE); // draws the particles of the interface CProfiler::StopPerformanceCounter(PCNT_RENDER_PARTICLE_IFACE); + + particleRenderer->End(); } // 3D objects drawn in front of interface @@ -4376,8 +4391,15 @@ void CEngine::DrawInterface() renderer->End(); + auto particleRenderer = m_device->GetParticleRenderer(); + particleRenderer->Begin(); + particleRenderer->SetProjectionMatrix(m_matProj); + particleRenderer->SetViewMatrix(m_matView); + m_particle->DrawParticle(SH_FRONT); // draws the particles of the 3D world + particleRenderer->End(); + m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, false); m_device->SetRenderState(RENDER_STATE_LIGHTING, false); m_device->SetRenderState(RENDER_STATE_FOG, false); diff --git a/src/graphics/engine/particle.cpp b/src/graphics/engine/particle.cpp index c64e3bbf..741aad60 100644 --- a/src/graphics/engine/particle.cpp +++ b/src/graphics/engine/particle.cpp @@ -25,6 +25,7 @@ #include "common/logger.h" #include "graphics/core/device.h" +#include "graphics/core/renderers.h" #include "graphics/engine/engine.h" #include "graphics/engine/terrain.h" @@ -84,6 +85,7 @@ CParticle::~CParticle() void CParticle::SetDevice(CDevice* device) { m_device = device; + m_renderer = device->GetParticleRenderer(); } void CParticle::FlushParticle() @@ -2505,7 +2507,7 @@ void CParticle::TrackDraw(int i, ParticleType type) } glm::mat4 mat = glm::mat4(1.0f); - m_device->SetTransform(TRANSFORM_WORLD, mat); + m_renderer->SetModelMatrix(mat); glm::vec2 texInf{ 0, 0 }, texSup{ 0, 0 }; @@ -2601,7 +2603,7 @@ void CParticle::TrackDraw(int i, ParticleType type) glm::vec3 eye = m_engine->GetEyePt(); float a = Math::RotateAngle(eye.x-p1.x, eye.z-p1.z); - Vertex vertex[4]; + Vertex3D vertex[4]; glm::vec3 corner[4]; for (int counter = 0; counter < m_track[i].posUsed-1; counter++) @@ -2640,22 +2642,24 @@ void CParticle::TrackDraw(int i, ParticleType type) corner[3].y = p2.y; corner[3].z = rot.y; + glm::u8vec4 white(255); + if (p2.y < p1.y) { - vertex[0] = { corner[1], n, { texSup.x, texSup.y } }; - vertex[1] = { corner[0], n, { texInf.x, texSup.y } }; - vertex[2] = { corner[3], n, { texSup.x, texInf.y } }; - vertex[3] = { corner[2], n, { texInf.x, texInf.y } }; + vertex[0] = { corner[1], white, { texSup.x, texSup.y } }; + vertex[1] = { corner[0], white, { texInf.x, texSup.y } }; + vertex[2] = { corner[3], white, { texSup.x, texInf.y } }; + vertex[3] = { corner[2], white, { texInf.x, texInf.y } }; } else { - vertex[0] = { corner[0], n, { texSup.x, texSup.y } }; - vertex[1] = { corner[1], n, { texInf.x, texSup.y } }; - vertex[2] = { corner[2], n, { texSup.x, texInf.y } }; - vertex[3] = { corner[3], n, { texInf.x, texInf.y } }; + vertex[0] = { corner[0], white, { texSup.x, texSup.y } }; + vertex[1] = { corner[1], white, { texInf.x, texSup.y } }; + vertex[2] = { corner[2], white, { texSup.x, texInf.y } }; + vertex[3] = { corner[3], white, { texInf.x, texInf.y } }; } - m_device->DrawPrimitive(PrimitiveType::TRIANGLE_STRIP, vertex, 4); + m_renderer->DrawParticle(PrimitiveType::TRIANGLE_STRIP, 4, vertex); m_engine->AddStatisticTriangle(2); if (f2 < 0.0f) break; @@ -2685,9 +2689,9 @@ void CParticle::DrawParticleTriangle(int i) mat[3][0] = pos.x; mat[3][1] = pos.y; mat[3][2] = pos.z; - m_device->SetTransform(TRANSFORM_WORLD, mat); + m_renderer->SetModelMatrix(mat); - m_device->DrawPrimitive(PrimitiveType::TRIANGLES, m_triangle[i].triangle, 3); + m_renderer->DrawParticle(PrimitiveType::TRIANGLES, 3, m_triangle[i].triangle); m_engine->AddStatisticTriangle(1); } @@ -2700,7 +2704,7 @@ void CParticle::DrawParticleNorm(int i) glm::vec3 corner[4]; - Vertex vertex[4]; + Vertex3D vertex[4]; if (m_particle[i].sheet == SH_INTERFACE) { @@ -2728,12 +2732,14 @@ void CParticle::DrawParticleNorm(int i) corner[3].y = pos.y-dim.y; corner[3].z = 0.0f; - vertex[0] = { corner[1], n, { m_particle[i].texSup.x, m_particle[i].texSup.y } }; - vertex[1] = { corner[0], n, { m_particle[i].texInf.x, m_particle[i].texSup.y } }; - vertex[2] = { corner[3], n, { m_particle[i].texSup.x, m_particle[i].texInf.y } }; - vertex[3] = { corner[2], n, { m_particle[i].texInf.x, m_particle[i].texInf.y } }; + glm::u8vec4 white(255); - m_device->DrawPrimitive(PrimitiveType::TRIANGLE_STRIP, vertex, 4); + vertex[0] = { corner[1], white, { m_particle[i].texSup.x, m_particle[i].texSup.y } }; + vertex[1] = { corner[0], white, { m_particle[i].texInf.x, m_particle[i].texSup.y } }; + vertex[2] = { corner[3], white, { m_particle[i].texSup.x, m_particle[i].texInf.y } }; + vertex[3] = { corner[2], white, { m_particle[i].texInf.x, m_particle[i].texInf.y } }; + + m_renderer->DrawParticle(PrimitiveType::TRIANGLE_STRIP, 4, vertex); m_engine->AddStatisticTriangle(2); } else @@ -2755,7 +2761,7 @@ void CParticle::DrawParticleNorm(int i) mat[3][0] = pos.x; mat[3][1] = pos.y; mat[3][2] = pos.z; - m_device->SetTransform(TRANSFORM_WORLD, mat); + m_renderer->SetModelMatrix(mat); glm::vec3 n(0.0f, 0.0f, -1.0f); @@ -2779,12 +2785,14 @@ void CParticle::DrawParticleNorm(int i) corner[3].y = -dim.y; corner[3].z = 0.0f; - vertex[0] = { corner[1], n, { m_particle[i].texSup.x, m_particle[i].texSup.y } }; - vertex[1] = { corner[0], n, { m_particle[i].texInf.x, m_particle[i].texSup.y } }; - vertex[2] = { corner[3], n, { m_particle[i].texSup.x, m_particle[i].texInf.y } }; - vertex[3] = { corner[2], n, { m_particle[i].texInf.x, m_particle[i].texInf.y } }; + glm::u8vec4 color = ColorToIntColor(m_particle[i].color); - m_device->DrawPrimitive(PrimitiveType::TRIANGLE_STRIP, vertex, 4, m_particle[i].color); + vertex[0] = { corner[1], color, { m_particle[i].texSup.x, m_particle[i].texSup.y } }; + vertex[1] = { corner[0], color, { m_particle[i].texInf.x, m_particle[i].texSup.y } }; + vertex[2] = { corner[3], color, { m_particle[i].texSup.x, m_particle[i].texInf.y } }; + vertex[3] = { corner[2], color, { m_particle[i].texInf.x, m_particle[i].texInf.y } }; + + m_renderer->DrawParticle(PrimitiveType::TRIANGLE_STRIP, 4, vertex); m_engine->AddStatisticTriangle(2); } } @@ -2817,7 +2825,7 @@ void CParticle::DrawParticleFlat(int i) mat[3][0] = pos.x; mat[3][1] = pos.y; mat[3][2] = pos.z; - m_device->SetTransform(TRANSFORM_WORLD, mat); + m_renderer->SetModelMatrix(mat); glm::vec3 n(0.0f, 0.0f, -1.0f); @@ -2842,13 +2850,15 @@ void CParticle::DrawParticleFlat(int i) corner[3].y = -dim.y; corner[3].z = 0.0f; - Vertex vertex[4]; - vertex[0] = { corner[1], n, { m_particle[i].texSup.x, m_particle[i].texSup.y } }; - vertex[1] = { corner[0], n, { m_particle[i].texInf.x, m_particle[i].texSup.y } }; - vertex[2] = { corner[3], n, { m_particle[i].texSup.x, m_particle[i].texInf.y } }; - vertex[3] = { corner[2], n, { m_particle[i].texInf.x, m_particle[i].texInf.y } }; + glm::u8vec4 white(255); - m_device->DrawPrimitive(PrimitiveType::TRIANGLE_STRIP, vertex, 4); + Vertex3D vertex[4]; + vertex[0] = { corner[1], white, { m_particle[i].texSup.x, m_particle[i].texSup.y } }; + vertex[1] = { corner[0], white, { m_particle[i].texInf.x, m_particle[i].texSup.y } }; + vertex[2] = { corner[3], white, { m_particle[i].texSup.x, m_particle[i].texInf.y } }; + vertex[3] = { corner[2], white, { m_particle[i].texInf.x, m_particle[i].texInf.y } }; + + m_renderer->DrawParticle(PrimitiveType::TRIANGLE_STRIP, 4, vertex); m_engine->AddStatisticTriangle(2); } @@ -2906,9 +2916,7 @@ void CParticle::DrawParticleFog(int i) mat[3][0] = pos.x; mat[3][1] = pos.y; mat[3][2] = pos.z; - m_device->SetTransform(TRANSFORM_WORLD, mat); - - glm::vec3 n(0.0f, 0.0f, -1.0f); + m_renderer->SetModelMatrix(mat); glm::vec3 corner[4]; @@ -2928,14 +2936,16 @@ void CParticle::DrawParticleFog(int i) corner[3].y = -dim.y; corner[3].z = 0.0f; - Vertex vertex[4]; + glm::u8vec4 white(255); - vertex[0] = { corner[1], n, { m_particle[i].texSup.x, m_particle[i].texSup.y } }; - vertex[1] = { corner[0], n, { m_particle[i].texInf.x, m_particle[i].texSup.y } }; - vertex[2] = { corner[3], n, { m_particle[i].texSup.x, m_particle[i].texInf.y } }; - vertex[3] = { corner[2], n, { m_particle[i].texInf.x, m_particle[i].texInf.y } }; + Vertex3D vertex[4]; - m_device->DrawPrimitive(PrimitiveType::TRIANGLE_STRIP, vertex, 4); + vertex[0] = { corner[1], white, { m_particle[i].texSup.x, m_particle[i].texSup.y } }; + vertex[1] = { corner[0], white, { m_particle[i].texInf.x, m_particle[i].texSup.y } }; + vertex[2] = { corner[3], white, { m_particle[i].texSup.x, m_particle[i].texInf.y } }; + vertex[3] = { corner[2], white, { m_particle[i].texInf.x, m_particle[i].texInf.y } }; + + m_renderer->DrawParticle(PrimitiveType::TRIANGLE_STRIP, 4, vertex); m_engine->AddStatisticTriangle(2); } @@ -2967,7 +2977,7 @@ void CParticle::DrawParticleRay(int i) mat[3][0] = pos.x; mat[3][1] = pos.y; mat[3][2] = pos.z; - m_device->SetTransform(TRANSFORM_WORLD, mat); + m_renderer->SetModelMatrix(mat); glm::vec3 n(0.0f, 0.0f, left ? 1.0f : -1.0f); @@ -3056,7 +3066,9 @@ void CParticle::DrawParticleRay(int i) corner[2].z = (Math::Rand()-0.5f)*vario1; corner[3].z = (Math::Rand()-0.5f)*vario1; - Vertex vertex[4]; + Vertex3D vertex[4]; + + glm::u8vec4 white(255); for (int rank = 0; rank < step; rank++) { @@ -3084,12 +3096,12 @@ void CParticle::DrawParticleRay(int i) if (r % 4 < 2) Math::Swap(texInf.y, texSup.y); - vertex[0] = { corner[1], n, { texSup.x, texSup.y } }; - vertex[1] = { corner[0], n, { texInf.x, texSup.y } }; - vertex[2] = { corner[3], n, { texSup.x, texInf.y } }; - vertex[3] = { corner[2], n, { texInf.x, texInf.y } }; + vertex[0] = { corner[1], white, { texSup.x, texSup.y } }; + vertex[1] = { corner[0], white, { texInf.x, texSup.y } }; + vertex[2] = { corner[3], white, { texSup.x, texInf.y } }; + vertex[3] = { corner[2], white, { texInf.x, texInf.y } }; - m_device->DrawPrimitive(PrimitiveType::TRIANGLE_STRIP, vertex, 4); + m_renderer->DrawParticle(PrimitiveType::TRIANGLE_STRIP, 4, vertex); m_engine->AddStatisticTriangle(2); } adv += dim.x*2.0f; @@ -3102,8 +3114,8 @@ void CParticle::DrawParticleSphere(int i) if (zoom == 0.0f) return; - m_engine->SetState(ENG_RSTATE_TTEXTURE_BLACK | ENG_RSTATE_2FACE | ENG_RSTATE_WRAP, - IntensityToColor(m_particle[i].intensity)); + m_renderer->SetTransparency(TransparencyMode::BLACK); + m_renderer->SetColor(IntensityToColor(m_particle[i].intensity)); glm::mat4 mat = glm::mat4(1.0f); mat[0][0] = zoom; @@ -3124,7 +3136,7 @@ void CParticle::DrawParticleSphere(int i) mat = mat * rot; } - m_device->SetTransform(TRANSFORM_WORLD, mat); + m_renderer->SetModelMatrix(mat); glm::vec2 ts, ti; ts.x = m_particle[i].texSup.x; @@ -3151,7 +3163,9 @@ void CParticle::DrawParticleSphere(int i) float deltaRingAngle = Math::PI/numRings; float deltaSegAngle = 2.0f*Math::PI/numSegments; - Vertex vertex[2*16*(16+1)]; + std::vector vertex(2*16*(16+1)); + + glm::u8vec4 white(255); // Generate the group of rings for the sphere. int j = 0; @@ -3183,15 +3197,16 @@ void CParticle::DrawParticleSphere(int i) tu0 = ts.x+(ti.x-ts.x)*tu0; float tu1 = tu0; - vertex[j++] = { v0, v0, { tu0, tv0 } }; - vertex[j++] = { v1, v1, { tu1, tv1 } }; + vertex[j++] = { v0, white, { tu0, tv0 } }; + vertex[j++] = { v1, white, { tu1, tv1 } }; } } - m_device->DrawPrimitive(PrimitiveType::TRIANGLE_STRIP, vertex, j); + m_renderer->DrawParticle(PrimitiveType::TRIANGLE_STRIP, j, vertex.data()); m_engine->AddStatisticTriangle(j); - m_engine->SetState(ENG_RSTATE_TTEXTURE_BLACK, IntensityToColor(m_particle[i].intensity)); + m_renderer->SetColor(IntensityToColor(m_particle[i].intensity)); + m_renderer->SetTransparency(TransparencyMode::BLACK); } //! Returns the height depending on the progress @@ -3210,8 +3225,8 @@ void CParticle::DrawParticleCylinder(int i) float diam = m_particle[i].dim.y; if (progress >= 1.0f || zoom == 0.0f) return; - m_engine->SetState(ENG_RSTATE_TTEXTURE_BLACK | ENG_RSTATE_2FACE | ENG_RSTATE_WRAP, - IntensityToColor(m_particle[i].intensity)); + m_renderer->SetColor(IntensityToColor(m_particle[i].intensity)); + m_renderer->SetTransparency(TransparencyMode::BLACK); glm::mat4 mat = glm::mat4(1.0f); mat[0][0] = zoom; @@ -3221,7 +3236,7 @@ void CParticle::DrawParticleCylinder(int i) mat[3][1] = m_particle[i].pos.y; mat[3][2] = m_particle[i].pos.z; - m_device->SetTransform(TRANSFORM_WORLD, mat); + m_renderer->SetModelMatrix(mat); glm::vec2 ts, ti; ts.x = m_particle[i].texSup.x; @@ -3249,7 +3264,9 @@ void CParticle::DrawParticleCylinder(int i) } } - Vertex vertex[2*5*(10+1)]; + std::vector vertex(2*5*(10+1)); + + glm::u8vec4 white(255); int j = 0; for (int ring = 0; ring < numRings; ring++) @@ -3278,15 +3295,16 @@ void CParticle::DrawParticleCylinder(int i) tu0 = ts.x+(ti.x-ts.x)*tu0; float tu1 = tu0; - vertex[j++] = { v0, v0, { tu0, tv0 } }; - vertex[j++] = { v1, v1, { tu1, tv1 } }; + vertex[j++] = { v0, white, { tu0, tv0 } }; + vertex[j++] = { v1, white, { tu1, tv1 } }; } } - m_device->DrawPrimitive(PrimitiveType::TRIANGLE_STRIP, vertex, j); + m_renderer->DrawParticle(PrimitiveType::TRIANGLE_STRIP, j, vertex.data()); m_engine->AddStatisticTriangle(j); - m_engine->SetState(ENG_RSTATE_TTEXTURE_BLACK, IntensityToColor(m_particle[i].intensity)); + m_renderer->SetColor(IntensityToColor(m_particle[i].intensity)); + m_renderer->SetTransparency(TransparencyMode::BLACK); } void CParticle::DrawParticleText(int i) @@ -3294,8 +3312,9 @@ void CParticle::DrawParticleText(int i) CharTexture tex = m_engine->GetText()->GetCharTexture(static_cast(m_particle[i].text), FONT_STUDIO, FONT_SIZE_BIG*2.0f); if (tex.id == 0) return; - m_device->SetTexture(0, tex.id); - m_engine->SetState(ENG_RSTATE_TTEXTURE_ALPHA, IntensityToColor(m_particle[i].intensity)); + m_renderer->SetTexture({ tex.id }); + m_renderer->SetColor(IntensityToColor(m_particle[i].intensity)); + m_renderer->SetTransparency(TransparencyMode::ALPHA); glm::ivec2 fontTextureSize = m_engine->GetText()->GetFontTextureSize(); m_particle[i].texSup.x = static_cast(tex.charPos.x) / fontTextureSize.x; @@ -3312,10 +3331,14 @@ void CParticle::DrawParticleWheel(int i) float dist = Math::DistanceProjected(m_engine->GetEyePt(), m_wheelTrace[i].pos[0]); if (dist > 300.0f) return; + glm::u8vec4 white(255); + if (m_wheelTrace[i].color == TraceColor::BlackArrow || m_wheelTrace[i].color == TraceColor::RedArrow) { - m_engine->SetTexture("textures/effect03.png"); - m_engine->SetState(ENG_RSTATE_ALPHA); + auto texture = m_engine->LoadTexture("textures/effect03.png"); + m_renderer->SetTexture(texture); + m_renderer->SetTransparency(TransparencyMode::ALPHA); + m_renderer->SetColor({ 1.0f, 1.0f, 1.0f, 1.0f }); glm::vec3 pos[4]; pos[0] = m_wheelTrace[i].pos[0]; @@ -3334,16 +3357,16 @@ void CParticle::DrawParticleWheel(int i) ti.x = ti.x-dp; ti.y = ti.y-dp; - Vertex vertex[4]; - vertex[0] = { pos[0], n, { ts.x, ts.y } }; - vertex[1] = { pos[1], n, { ti.x, ts.y } }; - vertex[2] = { pos[2], n, { ts.x, ti.y } }; - vertex[3] = { pos[3], n, { ti.x, ti.y } }; + auto color = ColorToIntColor(TraceColorColor(m_wheelTrace[i].color)); - m_device->DrawPrimitive(PrimitiveType::TRIANGLE_STRIP, vertex, 4, TraceColorColor(m_wheelTrace[i].color)); + Vertex3D vertex[4]; + vertex[0] = { pos[0], color, { ts.x, ts.y } }; + vertex[1] = { pos[1], color, { ti.x, ts.y } }; + vertex[2] = { pos[2], color, { ts.x, ti.y } }; + vertex[3] = { pos[3], color, { ti.x, ti.y } }; + + m_renderer->DrawParticle(PrimitiveType::TRIANGLE_STRIP, 4, vertex); m_engine->AddStatisticTriangle(2); - - m_engine->SetState(ENG_RSTATE_OPAQUE_COLOR); } else { @@ -3353,30 +3376,21 @@ void CParticle::DrawParticleWheel(int i) pos[2] = m_wheelTrace[i].pos[2]; pos[3] = m_wheelTrace[i].pos[3]; - glm::vec3 n(0.0f, 1.0f, 0.0f); + auto color = ColorToIntColor(TraceColorColor(m_wheelTrace[i].color)); - Vertex vertex[4]; - vertex[0] = { pos[0], n }; - vertex[1] = { pos[1], n }; - vertex[2] = { pos[2], n }; - vertex[3] = { pos[3], n }; + Vertex3D vertex[4]; + vertex[0] = { pos[0], color }; + vertex[1] = { pos[1], color }; + vertex[2] = { pos[2], color }; + vertex[3] = { pos[3], color }; - m_device->DrawPrimitive(PrimitiveType::TRIANGLE_STRIP, vertex, 4, TraceColorColor(m_wheelTrace[i].color)); + m_renderer->DrawParticle(PrimitiveType::TRIANGLE_STRIP, 4, vertex); m_engine->AddStatisticTriangle(2); } } void CParticle::DrawParticle(int sheet) { - Material mat; - mat.diffuse.r = 1.0f; - mat.diffuse.g = 1.0f; - mat.diffuse.b = 1.0f; // white - mat.ambient.r = 0.5f; - mat.ambient.g = 0.5f; - mat.ambient.b = 0.5f; - m_engine->SetMaterial(mat); - // Draw the basic particles of triangles. if (m_totalInterface[0][sheet] > 0) { @@ -3386,8 +3400,11 @@ void CParticle::DrawParticle(int sheet) if (m_particle[i].sheet != sheet) continue; if (m_particle[i].type == PARTIPART) continue; - m_engine->SetTexture(!m_triangle[i].tex1Name.empty() ? "textures/"+m_triangle[i].tex1Name : ""); - m_engine->SetState(m_triangle[i].state); + auto texture = m_engine->LoadTexture(!m_triangle[i].tex1Name.empty() + ? "textures/" + m_triangle[i].tex1Name : ""); + + m_renderer->SetTexture(texture); + //m_engine->SetState(m_triangle[i].state); DrawParticleTriangle(i); } } @@ -3395,9 +3412,10 @@ void CParticle::DrawParticle(int sheet) // Draw tire marks. if (m_wheelTraceTotal > 0 && sheet == SH_WORLD) { - m_engine->SetState(ENG_RSTATE_OPAQUE_COLOR); glm::mat4 matrix = glm::mat4(1.0f); - m_device->SetTransform(TRANSFORM_WORLD, matrix); + m_renderer->SetModelMatrix(matrix); + m_renderer->SetTransparency(TransparencyMode::NONE); + m_renderer->SetColor({ 1.0f, 1.0f, 1.0f, 1.0f }); for (int i = 0; i < m_wheelTraceTotal; i++) DrawParticleWheel(i); @@ -3409,10 +3427,15 @@ void CParticle::DrawParticle(int sheet) bool loadTexture = false; - int state; - if (t == 4) state = ENG_RSTATE_TTEXTURE_WHITE; // effect03.png - else state = ENG_RSTATE_TTEXTURE_BLACK; // effect[00..02].png - m_engine->SetState(state); + TransparencyMode mode = TransparencyMode::ALPHA; + + if (t == 4) + mode = TransparencyMode::WHITE; + else + mode = TransparencyMode::BLACK; + + m_renderer->SetTransparency(mode); + m_renderer->SetColor({ 1.0f, 1.0f, 1.0f, 1.0f }); for (int j = 0; j < MAXPARTICULE; j++) { @@ -3424,19 +3447,23 @@ void CParticle::DrawParticle(int sheet) { std::string name; NameParticle(name, t); - m_engine->SetTexture("textures/"+name); + auto texture = m_engine->LoadTexture("textures/" + name); + m_renderer->SetTexture(texture); loadTexture = true; } int r = m_particle[i].trackRank; if (r != -1) { - m_engine->SetState(state); + m_renderer->SetTransparency(mode); + m_renderer->SetColor({ 1.0f, 1.0f, 1.0f, 1.0f }); + //m_engine->SetState(state); TrackDraw(r, m_particle[i].type); // draws the drag if (!m_track[r].drawParticle) continue; } - m_engine->SetState(state, IntensityToColor(m_particle[i].intensity)); + m_renderer->SetTransparency(mode); + m_renderer->SetColor(IntensityToColor(m_particle[i].intensity)); if (m_particle[i].ray) // ray? { diff --git a/src/graphics/engine/particle.h b/src/graphics/engine/particle.h index d4e71124..b0dfff6d 100644 --- a/src/graphics/engine/particle.h +++ b/src/graphics/engine/particle.h @@ -41,6 +41,8 @@ class CSoundInterface; namespace Gfx { +class CParticleRenderer; + const short MAXPARTICULE = 500; const short MAXPARTITYPE = 6; const short MAXTRACK = 100; @@ -339,6 +341,7 @@ protected: CWater* m_water = nullptr; CRobotMain* m_main = nullptr; CSoundInterface* m_sound = nullptr; + CParticleRenderer* m_renderer = nullptr; Particle m_particle[MAXPARTICULE*MAXPARTITYPE]; EngineTriangle m_triangle[MAXPARTICULE]; // triangle if PartiType == 0 diff --git a/src/graphics/opengl/gl33device.cpp b/src/graphics/opengl/gl33device.cpp index 6882c7e6..15f7c650 100644 --- a/src/graphics/opengl/gl33device.cpp +++ b/src/graphics/opengl/gl33device.cpp @@ -33,6 +33,7 @@ #include "graphics/opengl/glframebuffer.h" #include "graphics/opengl/gl33renderers.h" #include "graphics/opengl/gl33objectrenderer.h" +#include "graphics/opengl/gl33particlerenderer.h" #include "math/geometry.h" @@ -434,6 +435,7 @@ bool CGL33Device::Create() m_uiRenderer = std::make_unique(this); m_terrainRenderer = std::make_unique(this); m_objectRenderer = std::make_unique(this); + m_particleRenderer = std::make_unique(this); m_shadowRenderer = std::make_unique(this); glUseProgram(m_normalProgram); @@ -501,6 +503,9 @@ void CGL33Device::Destroy() m_uiRenderer = nullptr; m_terrainRenderer = nullptr; + m_objectRenderer = nullptr; + m_particleRenderer = nullptr; + m_shadowRenderer = nullptr; } void CGL33Device::ConfigChanged(const DeviceConfig& newConfig) @@ -561,6 +566,11 @@ CObjectRenderer* CGL33Device::GetObjectRenderer() return m_objectRenderer.get(); } +CParticleRenderer* CGL33Device::GetParticleRenderer() +{ + return m_particleRenderer.get(); +} + CShadowRenderer* CGL33Device::GetShadowRenderer() { return m_shadowRenderer.get(); diff --git a/src/graphics/opengl/gl33device.h b/src/graphics/opengl/gl33device.h index 41e02e6e..a248b012 100644 --- a/src/graphics/opengl/gl33device.h +++ b/src/graphics/opengl/gl33device.h @@ -81,6 +81,7 @@ public: class CGL33UIRenderer; class CGL33TerrainRenderer; class CGL33ObjectRenderer; +class CGL33ParticleRenderer; class CGL33ShadowRenderer; /** @@ -118,6 +119,7 @@ public: CUIRenderer* GetUIRenderer() override; CTerrainRenderer* GetTerrainRenderer() override; CObjectRenderer* GetObjectRenderer() override; + CParticleRenderer* GetParticleRenderer() override; CShadowRenderer* GetShadowRenderer() override; void Restore() override; @@ -300,6 +302,8 @@ private: std::unique_ptr m_terrainRenderer; //! Object renderer std::unique_ptr m_objectRenderer; + //! Particle renderer + std::unique_ptr m_particleRenderer; //! Shadow renderer std::unique_ptr m_shadowRenderer; }; diff --git a/src/graphics/opengl/gl33particlerenderer.cpp b/src/graphics/opengl/gl33particlerenderer.cpp new file mode 100644 index 00000000..f6792ed2 --- /dev/null +++ b/src/graphics/opengl/gl33particlerenderer.cpp @@ -0,0 +1,227 @@ +#include "graphics/opengl/gl33particlerenderer.h" + +#include "graphics/opengl/gl33device.h" +#include "graphics/opengl/glutil.h" + +#include "graphics/core/vertex.h" + +#include "common/logger.h" + +#include + +#include +#include + +using namespace Gfx; + +CGL33ParticleRenderer::CGL33ParticleRenderer(CGL33Device* device) + : m_device(device) +{ + GetLogger()->Info("Creating CGL33ParticleRenderer\n"); + + std::string preamble = LoadSource("shaders/gl33/preamble.glsl"); + std::string shadowSource = LoadSource("shaders/gl33/shadow.glsl"); + std::string vsSource = LoadSource("shaders/gl33/particle_vs.glsl"); + std::string fsSource = LoadSource("shaders/gl33/particle_fs.glsl"); + + GLint vsShader = CreateShader(GL_VERTEX_SHADER, { preamble, shadowSource, vsSource }); + if (vsShader == 0) + { + GetLogger()->Error("Cound not create vertex shader from file 'particle_vs.glsl'\n"); + return; + } + + GLint fsShader = CreateShader(GL_FRAGMENT_SHADER, { preamble, shadowSource, fsSource }); + if (fsShader == 0) + { + GetLogger()->Error("Cound not create fragment shader from file 'particle_fs.glsl'\n"); + return; + } + + m_program = LinkProgram({ vsShader, fsShader }); + + glDeleteShader(vsShader); + glDeleteShader(fsShader); + + glUseProgram(m_program); + + // Setup uniforms + glm::mat4 identity(1.0f); + + m_projectionMatrix = glGetUniformLocation(m_program, "uni_ProjectionMatrix"); + m_viewMatrix = glGetUniformLocation(m_program, "uni_ViewMatrix"); + m_modelMatrix = glGetUniformLocation(m_program, "uni_ModelMatrix"); + m_fogRange = glGetUniformLocation(m_program, "uni_FogRange"); + m_fogColor = glGetUniformLocation(m_program, "uni_FogColor"); + m_color = glGetUniformLocation(m_program, "uni_Color"); + m_alphaScissor = glGetUniformLocation(m_program, "uni_AlphaScissor"); + + // Set texture units + auto texture = glGetUniformLocation(m_program, "uni_Texture"); + glUniform1i(texture, 10); + + glUniform1f(m_alphaScissor, 0.5f); + + // 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); + + // Generic buffer + glGenBuffers(1, &m_bufferVBO); + glBindBuffer(GL_COPY_WRITE_BUFFER, m_bufferVBO); + glBufferData(GL_COPY_WRITE_BUFFER, m_bufferCapacity, nullptr, GL_STREAM_DRAW); + + glGenVertexArrays(1, &m_bufferVAO); + glBindVertexArray(m_bufferVAO); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + + GetLogger()->Info("CGL33ParticleRenderer created successfully\n"); +} + +CGL33ParticleRenderer::~CGL33ParticleRenderer() +{ + glDeleteProgram(m_program); + glDeleteTextures(1, &m_whiteTexture); +} + +void CGL33ParticleRenderer::Begin() +{ + glUseProgram(m_program); + + glActiveTexture(GL_TEXTURE10); + glBindTexture(GL_TEXTURE_2D, m_whiteTexture); + + m_texture = 0; + + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glDisable(GL_CULL_FACE); + + glUniform4f(m_color, 1.0f, 1.0f, 1.0f, 1.0f); + + glBindVertexArray(m_bufferVAO); + glBindBuffer(GL_ARRAY_BUFFER, m_bufferVBO); +} + +void CGL33ParticleRenderer::End() +{ + glActiveTexture(GL_TEXTURE10); + glBindTexture(GL_TEXTURE_2D, 0); + + m_texture = 0; + + glDepthMask(GL_TRUE); + + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + m_device->Restore(); +} + +void CGL33ParticleRenderer::SetProjectionMatrix(const glm::mat4& matrix) +{ + glUniformMatrix4fv(m_projectionMatrix, 1, GL_FALSE, value_ptr(matrix)); +} + +void CGL33ParticleRenderer::SetViewMatrix(const glm::mat4& matrix) +{ + glm::mat4 scale(1.0f); + scale[2][2] = -1.0f; + + auto viewMatrix = scale * matrix; + + glUniformMatrix4fv(m_viewMatrix, 1, GL_FALSE, value_ptr(viewMatrix)); +} + +void CGL33ParticleRenderer::SetModelMatrix(const glm::mat4& matrix) +{ + glUniformMatrix4fv(m_modelMatrix, 1, GL_FALSE, value_ptr(matrix)); +} + +void CGL33ParticleRenderer::SetColor(const glm::vec4& color) +{ + glUniform4f(m_color, color.r, color.g, color.b, color.a); +} + +void CGL33ParticleRenderer::SetTexture(const Texture& texture) +{ + if (m_texture == texture.id) return; + + m_texture = texture.id; + + glActiveTexture(GL_TEXTURE10); + + if (texture.id == 0) + glBindTexture(GL_TEXTURE_2D, m_whiteTexture); + else + glBindTexture(GL_TEXTURE_2D, texture.id); +} + + +void CGL33ParticleRenderer::SetTransparency(TransparencyMode mode) +{ + switch (mode) + { + case TransparencyMode::NONE: + glDisable(GL_BLEND); + glDepthMask(GL_TRUE); + break; + case TransparencyMode::ALPHA: + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendEquation(GL_FUNC_ADD); + glDepthMask(GL_TRUE); + break; + case TransparencyMode::BLACK: + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); + glBlendEquation(GL_FUNC_ADD); + glDepthMask(GL_FALSE); + break; + case TransparencyMode::WHITE: + glEnable(GL_BLEND); + glBlendFunc(GL_DST_COLOR, GL_ZERO); + glBlendEquation(GL_FUNC_ADD); + glDepthMask(GL_FALSE); + break; + } +} + +void CGL33ParticleRenderer::DrawParticle(PrimitiveType type, int count, const Vertex3D* vertices) +{ + size_t size = count * sizeof(Vertex3D); + + if (m_bufferCapacity < size) + m_bufferCapacity = size; + + // Send new vertices to GPU + glBindBuffer(GL_ARRAY_BUFFER, m_bufferVBO); + glBufferData(GL_ARRAY_BUFFER, m_bufferCapacity, nullptr, GL_STREAM_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, size, vertices); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), + reinterpret_cast(offsetof(Vertex3D, position))); + + glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex3D), + reinterpret_cast(offsetof(Vertex3D, color))); + + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), + reinterpret_cast(offsetof(Vertex3D, uv))); + + glDrawArrays(TranslateGfxPrimitive(type), 0, count); +} diff --git a/src/graphics/opengl/gl33particlerenderer.h b/src/graphics/opengl/gl33particlerenderer.h new file mode 100644 index 00000000..cf1bf577 --- /dev/null +++ b/src/graphics/opengl/gl33particlerenderer.h @@ -0,0 +1,96 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2021, 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 + */ + + /** + * \file graphics/opengl/gl33objectrenderer.h + * \brief OpenGL 3.3 object renderer + */ + +#pragma once + +#include "graphics/core/renderers.h" + +#include + +#include +#include + + // Graphics module namespace +namespace Gfx +{ + +class CGL33Device; + +class CGL33ParticleRenderer : public CParticleRenderer +{ +public: + CGL33ParticleRenderer(CGL33Device* device); + virtual ~CGL33ParticleRenderer(); + + 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 color + virtual void SetColor(const glm::vec4& color) override; + //! Sets texture + virtual void SetTexture(const Texture& texture) override; + + //! Sets transparency mode + virtual void SetTransparency(TransparencyMode mode) override; + + //! Draws particles + virtual void DrawParticle(PrimitiveType type, int count, const Vertex3D* vertices) override; + +private: + CGL33Device* const m_device; + + // Uniform data + GLint m_projectionMatrix = -1; + GLint m_viewMatrix = -1; + GLint m_modelMatrix = -1; + GLint m_fogRange = -1; + GLint m_fogColor = -1; + GLint m_color = -1; + GLint m_alphaScissor = -1; + + // Shader program + GLuint m_program = 0; + + // 1x1 white texture + GLuint m_whiteTexture = 0; + // Currently bound primary texture + GLuint m_texture = 0; + + // Vertex buffer object + GLuint m_bufferVBO = 0; + // Vertex array object + GLuint m_bufferVAO = 0; + // VBO capacity + GLsizei m_bufferCapacity = 8 * sizeof(Vertex3D); +}; + +} diff --git a/src/graphics/opengl/shaders/gl33/particle_fs.glsl b/src/graphics/opengl/shaders/gl33/particle_fs.glsl new file mode 100644 index 00000000..f85ef4c4 --- /dev/null +++ b/src/graphics/opengl/shaders/gl33/particle_fs.glsl @@ -0,0 +1,41 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2021, 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 - PARTICLE RENDERER + +uniform sampler2D uni_Texture; + +uniform float uni_AlphaScissor; + +in VertexData +{ + vec4 Color; + vec2 TexCoord; +} data; + +out vec4 out_FragColor; + +void main() +{ + vec4 color = texture(uni_Texture, data.TexCoord); + + if (color.a < uni_AlphaScissor) discard; + + out_FragColor = data.Color * color; +} diff --git a/src/graphics/opengl/shaders/gl33/particle_vs.glsl b/src/graphics/opengl/shaders/gl33/particle_vs.glsl new file mode 100644 index 00000000..30450b6c --- /dev/null +++ b/src/graphics/opengl/shaders/gl33/particle_vs.glsl @@ -0,0 +1,42 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2021, 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 - PARTICLE RENDERER + +layout(location = 0) in vec4 in_VertexCoord; +layout(location = 1) in vec4 in_Color; +layout(location = 2) in vec2 in_TexCoord; + +uniform mat4 uni_ProjectionMatrix; +uniform mat4 uni_ViewMatrix; +uniform mat4 uni_ModelMatrix; + +out VertexData +{ + vec4 Color; + vec2 TexCoord; +} data; + +void main() +{ + gl_Position = uni_ProjectionMatrix * uni_ViewMatrix * uni_ModelMatrix * in_VertexCoord; + + data.Color = in_Color; + data.TexCoord = in_TexCoord; +}