diff --git a/TombEngine/Game/Hud/DrawItems/DisplayItem.cpp b/TombEngine/Game/Hud/DrawItems/DisplayItem.cpp index 99108ee9b2..b7e576a34e 100644 --- a/TombEngine/Game/Hud/DrawItems/DisplayItem.cpp +++ b/TombEngine/Game/Hud/DrawItems/DisplayItem.cpp @@ -265,6 +265,28 @@ namespace TEN::Hud return true; } + bool DisplayItem::HasScissor() const + { + return _hasScissor; + } + + void DisplayItem::SetScissor(const TEN::Renderer::Structures::RendererRectangle& rect) + { + _hasScissor = true; + _scissorRect = rect; + } + + void DisplayItem::ClearScissor() + { + _hasScissor = false; + _scissorRect = {}; + } + + const TEN::Renderer::Structures::RendererRectangle& DisplayItem::GetScissorRect() const + { + return _scissorRect; + } + void DisplayItem::StoreInterpolationData() { _prevPosition = _position; diff --git a/TombEngine/Game/Hud/DrawItems/DisplayItem.h b/TombEngine/Game/Hud/DrawItems/DisplayItem.h index c3f6796697..da77b18c73 100644 --- a/TombEngine/Game/Hud/DrawItems/DisplayItem.h +++ b/TombEngine/Game/Hud/DrawItems/DisplayItem.h @@ -2,6 +2,7 @@ #include "Math/Constants.h" #include "Objects/game_object_ids.h" +#include "Renderer/Structures/RendererRectangle.h" #include "Specific/Structures/BitField.h" using namespace TEN::Math; @@ -34,6 +35,9 @@ namespace TEN::Hud int _frameNumber = 0; int _prevFrameNumber = 0; + bool _hasScissor = false; + TEN::Renderer::Structures::RendererRectangle _scissorRect = {}; + Vector3 _prevPosition = Vector3::Zero; EulerAngles _prevOrientation = EulerAngles::Identity; Vector3 _prevScale = Vector3::Zero; @@ -92,9 +96,13 @@ namespace TEN::Hud // Inquirers bool MeshExists(int meshIndex) const; + bool HasScissor() const; // Utilities + void SetScissor(const TEN::Renderer::Structures::RendererRectangle& rect); + void ClearScissor(); + const TEN::Renderer::Structures::RendererRectangle& GetScissorRect() const; void StoreInterpolationData(); }; } diff --git a/TombEngine/Renderer/RendererDrawMenu.cpp b/TombEngine/Renderer/RendererDrawMenu.cpp index 42a079279d..19c44416f8 100644 --- a/TombEngine/Renderer/RendererDrawMenu.cpp +++ b/TombEngine/Renderer/RendererDrawMenu.cpp @@ -1004,6 +1004,11 @@ namespace TEN::Renderer if (color.A() <= EPSILON) return; + // Apply scissor rect if set. + bool hasScissor = item.HasScissor(); + if (hasScissor) + SetScissor(item.GetScissorRect()); + auto objectNumber = item.GetObjectID(); auto pos = item.GetInterpolatedPosition(alpha); auto orient = item.GetInterpolatedOrientation(alpha); @@ -1165,6 +1170,10 @@ namespace TEN::Renderer } } } + + // Reset scissor rect if it was set. + if (hasScissor) + ResetScissor(); } void Renderer::RenderTitleImage() diff --git a/TombEngine/Scripting/Internal/TEN/View/DisplayArea/ScriptDisplayArea.cpp b/TombEngine/Scripting/Internal/TEN/View/DisplayArea/ScriptDisplayArea.cpp index d5bebbdda3..b498d9a283 100644 --- a/TombEngine/Scripting/Internal/TEN/View/DisplayArea/ScriptDisplayArea.cpp +++ b/TombEngine/Scripting/Internal/TEN/View/DisplayArea/ScriptDisplayArea.cpp @@ -115,10 +115,10 @@ namespace TEN::Scripting::DisplayArea } /// Add a display item to the area. - // Items can be View.DisplayString or View.DisplaySprite objects. + // Items can be View.DisplayString, View.DisplaySprite, or View.DisplayItem objects. // Arguments are forwarded to the item's Draw() method. // @function DisplayArea:AddItem - // @tparam object item A DisplayString or DisplaySprite to clip within this area. + // @tparam object item A DisplayString, DisplaySprite, or DisplayItem to clip within this area. // @tparam[opt] table args Draw arguments forwarded to the item, e.g. { priority, alignMode, scaleMode, blendMode }. void ScriptDisplayArea::AddItem(sol::object item, sol::optional args) { diff --git a/TombEngine/Scripting/Internal/TEN/View/DisplayItem/ScriptDisplayItem.cpp b/TombEngine/Scripting/Internal/TEN/View/DisplayItem/ScriptDisplayItem.cpp index 55fbc7d71a..d07dbc7696 100644 --- a/TombEngine/Scripting/Internal/TEN/View/DisplayItem/ScriptDisplayItem.cpp +++ b/TombEngine/Scripting/Internal/TEN/View/DisplayItem/ScriptDisplayItem.cpp @@ -11,6 +11,7 @@ #include "Scripting/Internal/TEN/Types/Rotation/Rotation.h" #include "Scripting/Internal/TEN/Types/Vec2/Vec2.h" #include "Scripting/Internal/TEN/Types/Vec3/Vec3.h" +#include "Scripting/Internal/TEN/View/DisplayAnchors/ScriptDisplayAnchors.h" #include "Specific/configuration.h" using namespace TEN::Effects::DisplaySprite; @@ -64,6 +65,7 @@ namespace TEN::Scripting::DisplayItem ScriptReserved_GetEndFrame, &ScriptDisplayItem::GetEndFrame, ScriptReserved_GetBounds, &ScriptDisplayItem::GetBounds, + ScriptReserved_DisplayStringGetAnchors, &ScriptDisplayItem::GetAnchors, ScriptReserved_DisplayItemSetAmbientLight, &ScriptDisplayItem::SetAmbientLight, ScriptReserved_DisplayItemSetCamera, &ScriptDisplayItem::SetCameraPosition, @@ -540,6 +542,58 @@ namespace TEN::Scripting::DisplayItem return sol::nullopt; } + /// Get the anchor points of the display item's projected bounding box. + // Projects the display item into 2D screen space and returns 9 anchor points + // (corners, edge midpoints, and center) as percent coordinates. + // @function DisplayItem:GetAnchors + // @treturn[1] View.DisplayAnchors An object containing the anchor points. + // @treturn[2] DisplayAnchors with default (zero) values if the display item does not exist or has no bounds. + // @usage + // local anchors = item:GetAnchors() + // print("Center: " .. tostring(anchors.CENTER)) + ScriptDisplayAnchors ScriptDisplayItem::GetAnchors() const + { + ScriptDisplayAnchors anchors; + + if (auto* item = TryGetItem()) + { + auto bounds = item->GetBounds(); + if (!bounds.has_value()) + return anchors; + + float screenWidth = (float)g_Configuration.ScreenWidth; + float screenHeight = (float)g_Configuration.ScreenHeight; + + const auto& center = bounds->first; + const auto& size = bounds->second; + + // Calculate half extents. + float halfW = size.x * 0.5f; + float halfH = size.y * 0.5f; + + // Helper lambda for percent conversion with rounding. + auto toPercent = [&](float x, float y) -> Vec2 + { + return Vec2( + std::round((x / screenWidth) * 10000.0f) / 100.0f, + std::round((y / screenHeight) * 10000.0f) / 100.0f); + }; + + // Populate anchors from axis-aligned bounding box. + anchors.TOP_LEFT = toPercent(center.x - halfW, center.y - halfH); + anchors.TOP_CENTER = toPercent(center.x, center.y - halfH); + anchors.TOP_RIGHT = toPercent(center.x + halfW, center.y - halfH); + anchors.CENTER_LEFT = toPercent(center.x - halfW, center.y); + anchors.CENTER = toPercent(center.x, center.y); + anchors.CENTER_RIGHT = toPercent(center.x + halfW, center.y); + anchors.BOTTOM_LEFT = toPercent(center.x - halfW, center.y + halfH); + anchors.BOTTOM_CENTER = toPercent(center.x, center.y + halfH); + anchors.BOTTOM_RIGHT = toPercent(center.x + halfW, center.y + halfH); + } + + return anchors; + } + /// Draw the display item in display space for the current frame. // @function DisplayItem:Draw // @usage @@ -547,6 +601,14 @@ namespace TEN::Scripting::DisplayItem void ScriptDisplayItem::Draw() { if (auto* item = TryGetItem()) + { item->SetVisible(true); + + // Capture active scissor state (set by DisplayArea). + if (HasActiveDisplayScissor()) + item->SetScissor(GetActiveDisplayScissor()); + else + item->ClearScissor(); + } } } diff --git a/TombEngine/Scripting/Internal/TEN/View/DisplayItem/ScriptDisplayItem.h b/TombEngine/Scripting/Internal/TEN/View/DisplayItem/ScriptDisplayItem.h index 25506ae341..9d1d11bc2f 100644 --- a/TombEngine/Scripting/Internal/TEN/View/DisplayItem/ScriptDisplayItem.h +++ b/TombEngine/Scripting/Internal/TEN/View/DisplayItem/ScriptDisplayItem.h @@ -6,6 +6,7 @@ #include "Scripting/Internal/TEN/Types/Vec2/Vec2.h" #include "Scripting/Internal/TEN/Types/Vec3/Vec3.h" #include "Scripting/Internal/TEN/Types/Rotation/Rotation.h" +#include "Scripting/Internal/TEN/View/DisplayAnchors/ScriptDisplayAnchors.h" using namespace TEN::Scripting::Types; @@ -64,6 +65,7 @@ namespace TEN::Scripting::DisplayItem sol::optional GetFrameNumber() const; sol::optional GetEndFrame() const; sol::optional> GetBounds() const; + ScriptDisplayAnchors GetAnchors() const; // Methods