From 28306e3e3f5ebea8c19a3006b7b9169407874258 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 00:07:38 +0000 Subject: [PATCH 1/3] Fix merge conflicts: Update RendererString for Vector2 Scale and new features Agent-Logs-Url: https://github.com/TrainWrack/TombEngine/sessions/eeb30453-a1e0-423e-9279-9fc2a1199c56 Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com> --- TombEngine/Game/effects/DisplaySprite.cpp | 26 ++++ TombEngine/Game/effects/DisplaySprite.h | 9 ++ TombEngine/Renderer/Renderer.h | 3 + TombEngine/Renderer/RendererString.cpp | 119 ++++++++++++++++-- .../Structures/RendererStringToDraw.h | 11 +- 5 files changed, 154 insertions(+), 14 deletions(-) diff --git a/TombEngine/Game/effects/DisplaySprite.cpp b/TombEngine/Game/effects/DisplaySprite.cpp index dd3713d6b3..0b195cf683 100644 --- a/TombEngine/Game/effects/DisplaySprite.cpp +++ b/TombEngine/Game/effects/DisplaySprite.cpp @@ -13,6 +13,30 @@ namespace TEN::Effects::DisplaySprite { std::vector DisplaySprites = {}; + static bool g_hasActiveDisplayScissor = false; + static TEN::Renderer::Structures::RendererRectangle g_activeDisplayScissorRect = {}; + + void SetActiveDisplayScissor(const TEN::Renderer::Structures::RendererRectangle& rect) + { + g_hasActiveDisplayScissor = true; + g_activeDisplayScissorRect = rect; + } + + void ClearActiveDisplayScissor() + { + g_hasActiveDisplayScissor = false; + } + + bool HasActiveDisplayScissor() + { + return g_hasActiveDisplayScissor; + } + + const TEN::Renderer::Structures::RendererRectangle& GetActiveDisplayScissor() + { + return g_activeDisplayScissorRect; + } + void AddDisplaySprite(GAME_OBJECT_ID objectID, int spriteID, const Vector2& pos, short orient, const Vector2& scale, const Vector4& color, int priority, DisplaySpriteAlignMode alignMode, DisplaySpriteScaleMode scaleMode, BlendMode blendMode, DisplaySpritePhase source) @@ -29,6 +53,8 @@ namespace TEN::Effects::DisplaySprite displaySprite.ScaleMode = scaleMode; displaySprite.BlendMode = blendMode; displaySprite.Source = source; + displaySprite.HasScissor = g_hasActiveDisplayScissor; + displaySprite.ScissorRect = g_activeDisplayScissorRect; DisplaySprites.push_back(displaySprite); } diff --git a/TombEngine/Game/effects/DisplaySprite.h b/TombEngine/Game/effects/DisplaySprite.h index cb325ea63b..103310fe52 100644 --- a/TombEngine/Game/effects/DisplaySprite.h +++ b/TombEngine/Game/effects/DisplaySprite.h @@ -2,6 +2,7 @@ #include "Math/Math.h" #include "Objects/game_object_ids.h" #include "Renderer/RendererEnums.h" +#include "Renderer/Structures/RendererRectangle.h" namespace TEN::Effects::DisplaySprite { @@ -47,6 +48,9 @@ namespace TEN::Effects::DisplaySprite BlendMode BlendMode = BlendMode::AlphaBlend; DisplaySpritePhase Source = DisplaySpritePhase::Control; + + bool HasScissor = false; + TEN::Renderer::Structures::RendererRectangle ScissorRect = {}; }; // Result of display sprite layout calculation. @@ -65,6 +69,11 @@ namespace TEN::Effects::DisplaySprite void ClearAllDisplaySprites(); void ClearDrawPhaseDisplaySprites(); + void SetActiveDisplayScissor(const TEN::Renderer::Structures::RendererRectangle& rect); + void ClearActiveDisplayScissor(); + bool HasActiveDisplayScissor(); + const TEN::Renderer::Structures::RendererRectangle& GetActiveDisplayScissor(); + // Calculate complete layout data for a display sprite. // // NOTE: This function is defined inline in the header for performance reasons. diff --git a/TombEngine/Renderer/Renderer.h b/TombEngine/Renderer/Renderer.h index 030f91b5fb..81d073aeac 100644 --- a/TombEngine/Renderer/Renderer.h +++ b/TombEngine/Renderer/Renderer.h @@ -429,6 +429,7 @@ namespace TEN::Renderer void InitializeGameBars(); void InitializeMenuBars(int y); void InitializeSky(); + void AddStringInternal(const std::string& string, const Vector2& pos, const Vector2& prevPos, const Vector2& area, const Color& color, const Vector2& scale, float rotation, int flags, int priority, BlendMode blendMode); void DrawAllStrings(); void PrepareDynamicLight(RendererLight& light); void PrepareLaserBarriers(RenderView& view); @@ -741,6 +742,8 @@ namespace TEN::Renderer void AddString(const std::string& string, const Vector2& pos, const Color& color, float scale, int flags); void AddString(const std::string& string, const Vector2& pos, const Vector2& area, const Color& color, float scale, int flags); void AddString(const std::string& string, const Vector2& currentPos, const Vector2& prevPos, const Vector2& area, const Color& color, float scale, int flags); + void AddString(const std::string& string, const Vector2& pos, const Vector2& prevPos, const Vector2& area, const Color& color, const Vector2& scale, float rotation, int flags, int priority, BlendMode blendMode); + Vector2 GetDisplayStringSize(const std::string& text, const Vector2& scale = Vector2::One) const; void AddDebugString(const std::string& string, const Vector2& pos, const Color& color, float scale, RendererDebugPage page = RendererDebugPage::None); void FreeRendererData(); void AddDynamicPointLight(const Vector3& pos, float radius, const Color& color, bool castShadows, int hash = 0); diff --git a/TombEngine/Renderer/RendererString.cpp b/TombEngine/Renderer/RendererString.cpp index a064e7c947..6777db3e6e 100644 --- a/TombEngine/Renderer/RendererString.cpp +++ b/TombEngine/Renderer/RendererString.cpp @@ -1,9 +1,15 @@ #include "framework.h" #include "Renderer/Renderer.h" +#include + +#include "Game/effects/DisplaySprite.h" #include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h" #include "Specific/trutils.h" +using namespace TEN::Effects::DisplaySprite; +using namespace TEN::Utils; + namespace TEN::Renderer { void Renderer::AddDebugString(const std::string& string, const Vector2& pos, const Color& color, float scale, RendererDebugPage page) @@ -24,6 +30,26 @@ namespace TEN::Renderer AddString(string, Vector2(x, y), Color(color), 1.0f, flags); } + Vector2 Renderer::GetDisplayStringSize(const std::string& text, const Vector2& scale) const + { + if (text.empty() || _gameFont == nullptr) + return Vector2::Zero; + + auto screenRes = GetScreenResolution(); + auto factor = Vector2((float)screenRes.x / DISPLAY_SPACE_RES.x, (float)screenRes.y / DISPLAY_SPACE_RES.y); + float uiScale = (screenRes.x > screenRes.y) ? factor.y : factor.x; + float fontSpacing = _gameFont->GetLineSpacing(); + float fontScale = REFERENCE_FONT_SIZE / fontSpacing; + auto stringScale = Vector2(uiScale * fontScale) * scale; + float baseScale = stringScale.y; + + auto wtext = TEN::Utils::ToWString(text); + auto measured = Vector2(_gameFont->MeasureString(wtext.c_str())) * baseScale; + + // Convert pixel size back to display space (800x600 units). + return Vector2(measured.x / factor.x, measured.y / factor.y); + } + void Renderer::AddString(const std::string& string, const Vector2& pos, const Color& color, float scale, int flags) { AddString(string, pos, Vector2::Zero, Color(color), scale, flags); @@ -34,7 +60,21 @@ namespace TEN::Renderer AddString(string, pos, pos, area, color, scale, flags); } + void Renderer::AddString(const std::string& string, const Vector2& pos, const Vector2& prevPos, const Vector2& area, + const Color& color, const Vector2& scale, float rotation, int flags, + int priority, BlendMode blendMode) + { + AddStringInternal(string, pos, prevPos, area, color, scale, rotation, flags, priority, blendMode); + } + void Renderer::AddString(const std::string& string, const Vector2& pos, const Vector2& prevPos, const Vector2& area, const Color& color, float scale, int flags) + { + AddStringInternal(string, pos, prevPos, area, color, Vector2(scale), 0.0f, flags, 0, BlendMode::AlphaBlend); + } + + void Renderer::AddStringInternal(const std::string& string, const Vector2& pos, const Vector2& prevPos, const Vector2& area, + const Color& color, const Vector2& scale, float rotation, int flags, + int priority, BlendMode blendMode) { if (_isLocked) return; @@ -49,8 +89,9 @@ namespace TEN::Renderer float uiScale = (screenRes.x > screenRes.y) ? factor.y : factor.x; float fontSpacing = _gameFont->GetLineSpacing(); float fontScale = REFERENCE_FONT_SIZE / fontSpacing; - float stringScale = (uiScale * fontScale) * scale; - float spaceWidth = Vector3(_gameFont->MeasureString(L" ")).x * stringScale; + auto stringScale = Vector2(uiScale * fontScale) * scale; + float baseScale = stringScale.y; + float spaceWidth = Vector3(_gameFont->MeasureString(L" ")).x * baseScale; std::vector stringLines; @@ -74,7 +115,7 @@ namespace TEN::Renderer for (const auto& word : words) { - float wordWidth = Vector3(_gameFont->MeasureString(word.c_str())).x * stringScale; + float wordWidth = Vector3(_gameFont->MeasureString(word.c_str())).x * baseScale; if (!currentLine.empty() && (currentLineWidth + wordWidth + spaceWidth > area.x * factor.x)) { @@ -107,9 +148,9 @@ namespace TEN::Renderer for (const auto& line : stringLines) { if (line.empty()) - totalHeight += fontSpacing * stringScale; + totalHeight += fontSpacing * baseScale; else - totalHeight += Vector2(_gameFont->MeasureString(line.c_str())).y * stringScale; + totalHeight += Vector2(_gameFont->MeasureString(line.c_str())).y * stringScale.y; } // Calculate maximum textbox height. @@ -142,9 +183,15 @@ namespace TEN::Renderer rString.Position = Vector2::Zero; rString.Color = color; rString.Scale = stringScale; + rString.Rotation = rotation; + rString.Priority = priority; + rString.Blend = blendMode; + rString.HasScissor = HasActiveDisplayScissor(); + if (rString.HasScissor) + rString.ScissorRect = GetActiveDisplayScissor(); // Measure string. - auto stringSize = line.empty() ? Vector2(0, fontSpacing * rString.Scale) : Vector2(_gameFont->MeasureString(line.c_str())) * rString.Scale; + auto stringSize = line.empty() ? Vector2(0, fontSpacing * rString.Scale.y) : Vector2(_gameFont->MeasureString(line.c_str())) * rString.Scale.y; // If height clipping enabled, stop drawing when exceeding maxHeight. if (maxHeight > 0.0f && (yOffset + stringSize.y) > maxHeight) @@ -164,7 +211,7 @@ namespace TEN::Renderer else { // Calculate indentation to account for string scaling. - auto indent = line.empty() ? 0 : _gameFont->FindGlyph(line.at(0))->XAdvance * rString.Scale; + auto indent = line.empty() ? 0 : _gameFont->FindGlyph(line.at(0))->XAdvance * rString.Scale.y; rString.Position.x = pos.x * factor.x + indent; rString.PrevPosition.x = prevPos.x * factor.x + indent; @@ -194,35 +241,81 @@ namespace TEN::Renderer if (_stringsToDraw.empty()) return; - SetBlendMode(BlendMode::AlphaBlend); + // Sort by priority (lower priority draws first, i.e. behind higher). + std::stable_sort(_stringsToDraw.begin(), _stringsToDraw.end(), + [](const auto& a, const auto& b) { return a.Priority < b.Priority; }); float shadowOffset = 1.5f / (REFERENCE_FONT_SIZE / _gameFont->GetLineSpacing()); auto shadowColor = (Vector4)g_GameFlow->GetSettings()->UI.ShadowTextColor; - _spriteBatch->Begin(); + ResetScissor(); + _spriteBatch->Begin(SpriteSortMode_Deferred, nullptr, nullptr, nullptr, _cullNoneRasterizerState.Get()); + + auto currentBlend = BlendMode::AlphaBlend; + SetBlendMode(currentBlend); + + bool currentHasScissor = false; + auto currentScissor = RendererRectangle{}; for (const auto& rString : _stringsToDraw) { + // Switch blend mode per string if needed. + if (rString.Blend != currentBlend) + { + _spriteBatch->End(); + currentBlend = rString.Blend; + SetBlendMode(currentBlend); + _spriteBatch->Begin(SpriteSortMode_Deferred, nullptr, nullptr, nullptr, _cullNoneRasterizerState.Get()); + } + + // Handle scissor rect changes. + bool scissorChanged = (rString.HasScissor != currentHasScissor) || + (rString.HasScissor && + (rString.ScissorRect.Left != currentScissor.Left || + rString.ScissorRect.Top != currentScissor.Top || + rString.ScissorRect.Right != currentScissor.Right || + rString.ScissorRect.Bottom != currentScissor.Bottom)); + + if (scissorChanged) + { + _spriteBatch->End(); + + if (rString.HasScissor) + SetScissor(rString.ScissorRect); + else + ResetScissor(); + + currentHasScissor = rString.HasScissor; + currentScissor = rString.ScissorRect; + _spriteBatch->Begin(SpriteSortMode_Deferred, nullptr, nullptr, nullptr, _cullNoneRasterizerState.Get()); + } + auto drawPos = Vector2::Lerp(rString.PrevPosition, rString.Position, GetInterpolationFactor()); // Draw shadow. if (rString.Flags & (int)PrintStringFlags::Outline) { + auto shadowPos = Vector2(drawPos.x + shadowOffset * rString.Scale.y, drawPos.y + shadowOffset * rString.Scale.y); + _gameFont->DrawString( _spriteBatch.get(), rString.String.c_str(), - Vector2(drawPos.x + shadowOffset * rString.Scale, drawPos.y + shadowOffset * rString.Scale), + shadowPos, (shadowColor * rString.Color.w * shadowColor.w) * ScreenFadeCurrent, - 0.0f, Vector4::Zero, rString.Scale); + 0.0f, Vector2::Zero, rString.Scale.y); } // Draw string. _gameFont->DrawString( _spriteBatch.get(), rString.String.c_str(), - Vector2(drawPos.x, drawPos.y), + drawPos, (rString.Color * rString.Color.w) * ScreenFadeCurrent, - 0.0f, Vector4::Zero, rString.Scale); + 0.0f, Vector2::Zero, rString.Scale.y); } _spriteBatch->End(); + + // Reset scissor if it was active. + if (currentHasScissor) + ResetScissor(); } } diff --git a/TombEngine/Renderer/Structures/RendererStringToDraw.h b/TombEngine/Renderer/Structures/RendererStringToDraw.h index 379dba178d..93a28572d8 100644 --- a/TombEngine/Renderer/Structures/RendererStringToDraw.h +++ b/TombEngine/Renderer/Structures/RendererStringToDraw.h @@ -1,6 +1,9 @@ #pragma once #include +#include "Renderer/RendererEnums.h" +#include "Renderer/Structures/RendererRectangle.h" + namespace TEN::Renderer::Structures { using namespace DirectX::SimpleMath; @@ -12,6 +15,12 @@ namespace TEN::Renderer::Structures int Flags; std::wstring String; Vector4 Color; - float Scale; + Vector2 Scale; + float Rotation = 0.0f; + int Priority = 0; + BlendMode Blend = BlendMode::AlphaBlend; + + bool HasScissor = false; + RendererRectangle ScissorRect = {}; }; } From 35934cafb258c2353ebc4fae807800ee6b5e4843 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 00:08:35 +0000 Subject: [PATCH 2/3] Fix DrawString calls to use Vector2 scale and rotation Agent-Logs-Url: https://github.com/TrainWrack/TombEngine/sessions/eeb30453-a1e0-423e-9279-9fc2a1199c56 Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com> --- TombEngine/Renderer/RendererString.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TombEngine/Renderer/RendererString.cpp b/TombEngine/Renderer/RendererString.cpp index 6777db3e6e..949dbd3450 100644 --- a/TombEngine/Renderer/RendererString.cpp +++ b/TombEngine/Renderer/RendererString.cpp @@ -301,7 +301,7 @@ namespace TEN::Renderer _spriteBatch.get(), rString.String.c_str(), shadowPos, (shadowColor * rString.Color.w * shadowColor.w) * ScreenFadeCurrent, - 0.0f, Vector2::Zero, rString.Scale.y); + rString.Rotation, Vector2::Zero, rString.Scale); } // Draw string. @@ -309,7 +309,7 @@ namespace TEN::Renderer _spriteBatch.get(), rString.String.c_str(), drawPos, (rString.Color * rString.Color.w) * ScreenFadeCurrent, - 0.0f, Vector2::Zero, rString.Scale.y); + rString.Rotation, Vector2::Zero, rString.Scale); } _spriteBatch->End(); From 8661bc851fb5cb7ec8f4996ed483dc77ee70baec Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 00:09:31 +0000 Subject: [PATCH 3/3] Add scissor support to RendererSprite2D structure Agent-Logs-Url: https://github.com/TrainWrack/TombEngine/sessions/eeb30453-a1e0-423e-9279-9fc2a1199c56 Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com> --- TombEngine/Renderer/Structures/RendererSprite2D.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TombEngine/Renderer/Structures/RendererSprite2D.h b/TombEngine/Renderer/Structures/RendererSprite2D.h index 50c4ecb011..ed4389dbd2 100644 --- a/TombEngine/Renderer/Structures/RendererSprite2D.h +++ b/TombEngine/Renderer/Structures/RendererSprite2D.h @@ -2,6 +2,7 @@ #include "Renderer/Structures/RendererSprite.h" #include "Renderer/RendererEnums.h" +#include "Renderer/Structures/RendererRectangle.h" namespace TEN::Renderer::Structures { @@ -18,5 +19,8 @@ namespace TEN::Renderer::Structures BlendMode BlendMode = BlendMode::AlphaBlend; Vector2 AspectCorrection = Vector2::One; + + bool HasScissor = false; + RendererRectangle ScissorRect = {}; }; }