diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 2b67ef1d..10039848 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -112,6 +112,7 @@ void CSettings::SaveSettings() // Experimental settings GetConfigFile().SetBoolProperty("Experimental", "TerrainShadows", engine->GetTerrainShadows()); + GetConfigFile().SetBoolProperty("Experimental", "PauseBlur", engine->GetPauseBlur()); CInput::GetInstancePointer()->SaveKeyBindings(); @@ -270,6 +271,9 @@ void CSettings::LoadSettings() if (GetConfigFile().GetBoolProperty("Experimental", "TerrainShadows", bValue)) engine->SetTerrainShadows(bValue); + if (GetConfigFile().GetBoolProperty("Experimental", "PauseBlur", bValue)) + engine->SetPauseBlur(bValue); + CInput::GetInstancePointer()->LoadKeyBindings(); diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index aeb6dcb2..b7d0e563 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -59,6 +59,7 @@ #include "ui/controls/interface.h" #include +#include #include template<> Gfx::CEngine* CSingleton::m_instance = nullptr; @@ -148,6 +149,7 @@ CEngine::CEngine(CApplication *app, CSystemUtils* systemUtils) m_editIndentMode = true; m_editIndentValue = 4; m_tracePrecision = 1.0f; + m_pauseBlur = false; m_updateGeometry = false; @@ -3028,6 +3030,16 @@ EngineMouseType CEngine::GetMouseType() return m_mouseType; } +void CEngine::SetPauseBlur(bool enable) +{ + m_pauseBlur = enable; +} + +bool CEngine::GetPauseBlur() +{ + return m_pauseBlur; +} + const Math::Matrix& CEngine::GetMatView() { return m_matView; @@ -3066,6 +3078,10 @@ void CEngine::UpdateMatProj() void CEngine::ApplyChange() { SetFocus(m_focus); + + // recapture 3D scene + if (m_worldCaptured) + m_captureWorld = true; } void CEngine::ClearDisplayCrashSpheres() @@ -3137,8 +3153,8 @@ void CEngine::Render() UseMSAA(true); - DrawBackground(); // draws the background - + if (!m_worldCaptured) + DrawBackground(); // draws the background if (m_drawWorld) Draw3DScene(); @@ -3155,6 +3171,33 @@ void CEngine::Render() void CEngine::Draw3DScene() { + // use currently captured scene for world + if (m_worldCaptured) + { + Math::Matrix identity; + + m_device->SetTransform(TRANSFORM_PROJECTION, identity); + m_device->SetTransform(TRANSFORM_VIEW, identity); + m_device->SetTransform(TRANSFORM_WORLD, identity); + + Vertex vertices[4]; + + vertices[0] = Vertex(Math::Vector(-1.0f, -1.0f, 0.0f), Math::Vector(0.0f, 1.0f, 0.0f), Math::Point(0.0f, 0.0f)); + vertices[1] = Vertex(Math::Vector( 1.0f, -1.0f, 0.0f), Math::Vector(0.0f, 1.0f, 0.0f), Math::Point(1.0f, 0.0f)); + vertices[2] = Vertex(Math::Vector(-1.0f, 1.0f, 0.0f), Math::Vector(0.0f, 1.0f, 0.0f), Math::Point(0.0f, 1.0f)); + vertices[3] = Vertex(Math::Vector( 1.0f, 1.0f, 0.0f), Math::Vector(0.0f, 1.0f, 0.0f), Math::Point(1.0f, 1.0f)); + + m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, false); + + m_device->SetTexture(TEXTURE_PRIMARY, m_capturedWorldTexture); + m_device->SetTextureEnabled(TEXTURE_PRIMARY, true); + m_device->SetTextureEnabled(TEXTURE_SECONDARY, false); + + m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, vertices, 4); + + return; + } + m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, false); UpdateGroundSpotTextures(); @@ -3237,7 +3280,6 @@ void CEngine::Draw3DScene() if (!m_shadowMapping) DrawShadowSpots(); - m_app->StopPerformanceCounter(PCNT_RENDER_TERRAIN); // Draw other objects @@ -3407,6 +3449,117 @@ void CEngine::Draw3DScene() DrawForegroundImage(); // draws the foreground if (! m_overFront) DrawOverColor(); // draws the foreground color + + // marked to capture currently rendered world + if (m_captureWorld) + { + // destroy existing texture + if (m_capturedWorldTexture.Valid()) + { + m_device->DestroyTexture(m_capturedWorldTexture); + m_capturedWorldTexture = Texture(); + } + + // obtain pixels from screen + int width = m_size.x; + int height = m_size.y; + + auto pixels = m_device->GetFrameBufferPixels(); + unsigned char* data = reinterpret_cast(pixels->GetPixelsData()); + + // calculate 2nd mipmap + int newWidth = width / 4; + int newHeight = height / 4; + std::unique_ptr mipmap(new unsigned char[4 * newWidth * newHeight]); + + for (int x = 0; x < newWidth; x++) + { + for (int y = 0; y < newHeight; y++) + { + float color[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + { + int index = 4 * ((4 * x + i) + width * (4 * y + j)); + + for (int k = 0; k < 4; k++) + color[k] += data[index + k]; + } + } + + int index = 4 * (x + newWidth * y); + + for (int k = 0; k < 4; k++) + { + mipmap[index + k] = static_cast(color[k] * (1.0f / 16.0f)); + } + } + } + + // calculate Gaussian blur + std::unique_ptr blured(new unsigned char[4 * newWidth * newHeight]); + + float matrix[7][7] = + { + { 0.00000067f, 0.00002292f, 0.00019117f, 0.00038771f, 0.00019117f, 0.00002292f, 0.00000067f }, + { 0.00002292f, 0.00078634f, 0.00655965f, 0.01330373f, 0.00655965f, 0.00078633f, 0.00002292f }, + { 0.00019117f, 0.00655965f, 0.05472157f, 0.11098164f, 0.05472157f, 0.00655965f, 0.00019117f }, + { 0.00038771f, 0.01330373f, 0.11098164f, 0.22508352f, 0.11098164f, 0.01330373f, 0.00038771f }, + { 0.00019117f, 0.00655965f, 0.05472157f, 0.11098164f, 0.05472157f, 0.00655965f, 0.00019117f }, + { 0.00002292f, 0.00078633f, 0.00655965f, 0.01330373f, 0.00655965f, 0.00078633f, 0.00002292f }, + { 0.00000067f, 0.00002292f, 0.00019117f, 0.00038771f, 0.00019117f, 0.00002292f, 0.00000067f } + }; + + for (int x = 0; x < newWidth; x++) + { + for (int y = 0; y < newHeight; y++) + { + float color[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + + for (int i = -3; i <= 3; i++) + { + for (int j = -3; j <= 3; j++) + { + int xp = Math::Clamp(x + i, 0, newWidth - 1); + int yp = Math::Clamp(y + j, 0, newHeight - 1); + + float weight = matrix[i + 3][j + 3]; + + int index = 4 * (newWidth * yp + xp); + + for (int k = 0; k < 4; k++) + color[k] += weight * mipmap[index + k]; + } + } + + int index = 4 * (newWidth * y + x); + + for (int k = 0; k < 4; k++) + { + float value = Math::Clamp(color[k], 0.0f, 255.0f); + blured[index + k] = static_cast(value); + } + } + } + + // create SDL surface and final texture + ImageData image; + image.surface = SDL_CreateRGBSurfaceFrom(blured.get(), newWidth, newHeight, 32, 0, 0, 0, 0, 0xFF000000); + + TextureCreateParams params; + params.filter = TEX_FILTER_BILINEAR; + params.format = TEX_IMG_RGBA; + params.mipmap = false; + + m_capturedWorldTexture = m_device->CreateTexture(&image, params); + + SDL_FreeSurface(image.surface); + + m_captureWorld = false; + m_worldCaptured = true; + } } void CEngine::DrawCrashSpheres() @@ -5190,6 +5343,22 @@ void CEngine::SetInterfaceCoordinates() m_device->SetTransform(TRANSFORM_WORLD, m_matWorldInterface); } +void CEngine::EnablePauseBlur() +{ + if (!m_pauseBlur) return; + + m_captureWorld = true; + m_worldCaptured = false; +} + +void CEngine::DisablePauseBlur() +{ + if (!m_pauseBlur) return; + + m_captureWorld = false; + m_worldCaptured = false; +} + void CEngine::SetWindowCoordinates() { Math::Matrix matWorldWindow; diff --git a/src/graphics/engine/engine.h b/src/graphics/engine/engine.h index 1f5cfe8f..8fb195a2 100644 --- a/src/graphics/engine/engine.h +++ b/src/graphics/engine/engine.h @@ -1162,6 +1162,12 @@ public: EngineMouseType GetMouseType(); //@} + //@{ + //! Management of pause blur + void SetPauseBlur(bool enable); + bool GetPauseBlur(); + //@} + //! Returns the view matrix const Math::Matrix& GetMatView(); //! Returns the camera center point @@ -1199,6 +1205,9 @@ public: void SetWindowCoordinates(); void SetInterfaceCoordinates(); + void EnablePauseBlur(); + void DisablePauseBlur(); + protected: //! Resets some states and flushes textures after device was changed (e.g. resoulution changed) /** Instead of calling this directly, send EVENT_RESOLUTION_CHANGED event **/ @@ -1408,6 +1417,7 @@ protected: float m_terrainVision; bool m_backForce; float m_tracePrecision; + bool m_pauseBlur; bool m_dirty; bool m_fog; @@ -1502,6 +1512,13 @@ protected: //! Pause the animation updates bool m_pause = false; + + //! true means that current 3D scene was captured and is not to be rendered again + bool m_worldCaptured = false; + //! true means that currently rendered world is to be captured + bool m_captureWorld = false; + //! Texture with captured 3D world + Texture m_capturedWorldTexture; }; diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index e6caa127..e41a0605 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -1601,6 +1601,8 @@ void CRobotMain::StartSuspend() m_suspendInitCamera = m_camera->GetType(); m_camera->SetType(Gfx::CAM_TYPE_DIALOG); + + m_engine->EnablePauseBlur(); } //! End of dialogue during the game @@ -1619,6 +1621,8 @@ void CRobotMain::StopSuspend() m_displayText->HideText(false); m_camera->SetType(m_suspendInitCamera); + + m_engine->DisablePauseBlur(); } diff --git a/src/math/func.h b/src/math/func.h index 66ee974e..b206c89a 100644 --- a/src/math/func.h +++ b/src/math/func.h @@ -93,6 +93,15 @@ inline float Max(float a, float b, float c, float d, float e) return Math::Max( Math::Max(a, b), Math::Max(c, d), e ); } +//! Clamps the value to a range specified by min and max +template +inline T Clamp(T value, T min, T max) +{ + if (value < min) return min; + else if (value > max) return max; + else return value; +} + //! Returns the normalized value (0 .. 1) inline float Norm(float a) {