From 081a2f44d9cd7e4043c3a1a49a54829744a3f29f Mon Sep 17 00:00:00 2001 From: David Sungaila Date: Sat, 22 Feb 2025 11:09:32 +0100 Subject: [PATCH] Add see-through option to the "Cut-away View" (#23759) --- contributors.md | 1 + data/language/en-GB.txt | 2 + distribution/changelog.txt | 1 + src/openrct2-ui/UiStringIds.h | 2 + src/openrct2-ui/windows/ViewClipping.cpp | 32 ++++++++++---- src/openrct2/interface/Viewport.cpp | 43 ++++++++++++++++--- src/openrct2/interface/Viewport.h | 3 +- src/openrct2/paint/Paint.Entity.cpp | 9 +++- .../paint/tile_element/Paint.TileElement.cpp | 11 ++++- 9 files changed, 85 insertions(+), 19 deletions(-) diff --git a/contributors.md b/contributors.md index 2e45dc504d..007ba77088 100644 --- a/contributors.md +++ b/contributors.md @@ -248,6 +248,7 @@ Appreciation for contributors who have provided substantial work, but are no lon * Tom Matalenas (tmatale) * Brendan Heinonen (staticinvocation) * (QuestionableDeer) +* David Sungaila (sungaila) ## Toolchain * (Balletie) - macOS diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index d2b9085080..ceb8e7ebc0 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -3795,3 +3795,5 @@ STR_6729 :Cable lift hill must start immediately after station or block brake STR_6730 :Export emscripten data STR_6731 :Import emscripten data STR_6732 :Show a button in the toolbar to rotate the view anti-clockwise +STR_6733 :See-Through +STR_6734 :Map elements that are at or above the cutting height are rendered translucent. diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 0ab11dee83..2b4cbd1a01 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -1,6 +1,7 @@ 0.4.20 (in development) ------------------------------------------------------------------------ - Feature: [#22905] Add diagonal downward-inclined brakes to hybrid coaster and single rail coaster. +- Feature: [#23759] Add see-through option to the “Cut-away View“. - Improved: [#23677] Building new ride track now inherits the colour scheme from the previous piece. - Improved: [#23720] Text fields now allow cutting to clipboard (Ctrl+X) in addition to copy and paste. - Fix: [#1972, #11679] Vehicles passing by toilets can cause them to glitch (original bug). diff --git a/src/openrct2-ui/UiStringIds.h b/src/openrct2-ui/UiStringIds.h index 13bac563a2..869d4f5e5e 100644 --- a/src/openrct2-ui/UiStringIds.h +++ b/src/openrct2-ui/UiStringIds.h @@ -2263,6 +2263,8 @@ namespace OpenRCT2 STR_VIEW_CLIPPING_HORIZONTAL_CLIPPING = 6240, STR_VIEW_CLIPPING_SELECT_AREA = 6241, STR_VIEW_CLIPPING_VERTICAL_CLIPPING = 6239, + STR_VIEW_CLIPPING_VERTICAL_CLIPPING_SEE_THROUGH = 6733, + STR_VIEW_CLIPPING_VERTICAL_CLIPPING_SEE_THROUGH_TIP = 6734, // Window: Viewport STR_LOCATE_SUBJECT_TIP = 1027, diff --git a/src/openrct2-ui/windows/ViewClipping.cpp b/src/openrct2-ui/windows/ViewClipping.cpp index 73529c3ac1..049e6554ae 100644 --- a/src/openrct2-ui/windows/ViewClipping.cpp +++ b/src/openrct2-ui/windows/ViewClipping.cpp @@ -34,6 +34,7 @@ namespace OpenRCT2::Ui::Windows WIDX_CLIP_HEIGHT_INCREASE, WIDX_CLIP_HEIGHT_DECREASE, WIDX_CLIP_HEIGHT_SLIDER, + WIDX_CLIP_SEE_THROUGH_CHECKBOX_ENABLE, WIDX_GROUPBOX_HORIZONTAL, WIDX_CLIP_SELECTOR, WIDX_CLIP_CLEAR, @@ -49,18 +50,19 @@ namespace OpenRCT2::Ui::Windows static constexpr StringId WINDOW_TITLE = STR_VIEW_CLIPPING_TITLE; static constexpr int32_t WW = 180; - static constexpr int32_t WH = 155; + static constexpr int32_t WH = 172; // clang-format off static constexpr Widget _viewClippingWidgets[] = { WINDOW_SHIM(WINDOW_TITLE, WW, WH), - MakeWidget ({ 11, 19}, { 159, 11}, WindowWidgetType::Checkbox, WindowColour::Primary, STR_VIEW_CLIPPING_HEIGHT_ENABLE, STR_VIEW_CLIPPING_HEIGHT_ENABLE_TIP ), // clip enable/disable check box - MakeWidget ({ 5, 36}, {WW - 10, 48}, WindowWidgetType::Groupbox, WindowColour::Primary, STR_VIEW_CLIPPING_VERTICAL_CLIPPING ), - MakeSpinnerWidgets({ 90, 51}, { 79, 12}, WindowWidgetType::Spinner, WindowColour::Primary, kStringIdNone, STR_VIEW_CLIPPING_HEIGHT_VALUE_TOGGLE), // clip height (3 widgets) - MakeWidget ({ 11, 66}, { 158, 13}, WindowWidgetType::Scroll, WindowColour::Primary, SCROLL_HORIZONTAL, STR_VIEW_CLIPPING_HEIGHT_SCROLL_TIP ), // clip height scrollbar - MakeWidget ({ 5, 90}, {WW - 10, 60}, WindowWidgetType::Groupbox, WindowColour::Primary, STR_VIEW_CLIPPING_HORIZONTAL_CLIPPING ), - MakeWidget ({ 11, 105}, { 158, 17}, WindowWidgetType::Button, WindowColour::Primary, STR_VIEW_CLIPPING_SELECT_AREA ), // selector - MakeWidget ({ 11, 126}, { 158, 18}, WindowWidgetType::Button, WindowColour::Primary, STR_VIEW_CLIPPING_CLEAR_SELECTION ), // clear + MakeWidget ({ 11, 19}, { 159, 11}, WindowWidgetType::Checkbox, WindowColour::Primary, STR_VIEW_CLIPPING_HEIGHT_ENABLE, STR_VIEW_CLIPPING_HEIGHT_ENABLE_TIP ), // clip enable/disable check box + MakeWidget ({ 5, 36}, {WW - 10, 65}, WindowWidgetType::Groupbox, WindowColour::Primary, STR_VIEW_CLIPPING_VERTICAL_CLIPPING ), + MakeSpinnerWidgets({ 90, 51}, { 79, 12}, WindowWidgetType::Spinner, WindowColour::Primary, kStringIdNone, STR_VIEW_CLIPPING_HEIGHT_VALUE_TOGGLE ), // clip height (3 widgets) + MakeWidget ({ 11, 66}, { 158, 13}, WindowWidgetType::Scroll, WindowColour::Primary, SCROLL_HORIZONTAL, STR_VIEW_CLIPPING_HEIGHT_SCROLL_TIP ), // clip height scrollbar + MakeWidget ({ 11, 83}, { 159, 11}, WindowWidgetType::Checkbox, WindowColour::Primary, STR_VIEW_CLIPPING_VERTICAL_CLIPPING_SEE_THROUGH, STR_VIEW_CLIPPING_VERTICAL_CLIPPING_SEE_THROUGH_TIP), // clip height enable/disable see-through check box + MakeWidget ({ 5, 107}, {WW - 10, 60}, WindowWidgetType::Groupbox, WindowColour::Primary, STR_VIEW_CLIPPING_HORIZONTAL_CLIPPING ), + MakeWidget ({ 11, 122}, { 158, 17}, WindowWidgetType::Button, WindowColour::Primary, STR_VIEW_CLIPPING_SELECT_AREA ), // selector + MakeWidget ({ 11, 143}, { 158, 18}, WindowWidgetType::Button, WindowColour::Primary, STR_VIEW_CLIPPING_CLEAR_SELECTION ), // clear }; // clang-format on @@ -137,6 +139,17 @@ namespace OpenRCT2::Ui::Windows gClipSelectionB = { kMaximumMapSizeBig - 1, kMaximumMapSizeBig - 1 }; GfxInvalidateScreen(); break; + case WIDX_CLIP_SEE_THROUGH_CHECKBOX_ENABLE: + { + // Toggle height clipping see-through. + if (auto mainWindow = WindowGetMain(); mainWindow != nullptr) + { + mainWindow->viewport->flags ^= VIEWPORT_FLAG_CLIP_VIEW_SEE_THROUGH; + mainWindow->Invalidate(); + } + Invalidate(); + break; + } } } @@ -262,6 +275,9 @@ namespace OpenRCT2::Ui::Windows if (mainWindow != nullptr) { WidgetSetCheckboxValue(*this, WIDX_CLIP_CHECKBOX_ENABLE, mainWindow->viewport->flags & VIEWPORT_FLAG_CLIP_VIEW); + WidgetSetCheckboxValue( + *this, WIDX_CLIP_SEE_THROUGH_CHECKBOX_ENABLE, + mainWindow->viewport->flags & VIEWPORT_FLAG_CLIP_VIEW_SEE_THROUGH); } if (IsActive()) diff --git a/src/openrct2/interface/Viewport.cpp b/src/openrct2/interface/Viewport.cpp index ea852ad53c..75a9008f3b 100644 --- a/src/openrct2/interface/Viewport.cpp +++ b/src/openrct2/interface/Viewport.cpp @@ -1403,6 +1403,18 @@ namespace OpenRCT2 VisibilityKind GetPaintStructVisibility(const PaintStruct* ps, uint32_t viewFlags) { + // the cut-away view is active and see-through is activated + auto cutAwayViewWithTransparency = (viewFlags & VIEWPORT_FLAG_CLIP_VIEW) + && (viewFlags & VIEWPORT_FLAG_CLIP_VIEW_SEE_THROUGH); + + // the element is above the cut-off height + auto clipped = cutAwayViewWithTransparency && ps->Element == nullptr && ps->Entity != nullptr + && ps->Entity->GetLocation().z > (gClipHeight * kCoordsZStep); + + // the entity is above the cut-off height + clipped |= cutAwayViewWithTransparency && ps->Element != nullptr + && (ps->Element->GetBaseZ() > gClipHeight * kCoordsZStep); + switch (ps->InteractionItem) { case ViewportInteractionItem::Entity: @@ -1412,14 +1424,14 @@ namespace OpenRCT2 { case EntityType::Vehicle: { - if (viewFlags & VIEWPORT_FLAG_HIDE_VEHICLES) + if (viewFlags & VIEWPORT_FLAG_HIDE_VEHICLES || clipped) { return (viewFlags & VIEWPORT_FLAG_INVISIBLE_VEHICLES) ? VisibilityKind::Hidden : VisibilityKind::Partial; } // Rides without track can technically have a 'vehicle': // these should be hidden if 'hide rides' is enabled - if (viewFlags & VIEWPORT_FLAG_HIDE_RIDES) + if (viewFlags & VIEWPORT_FLAG_HIDE_RIDES || clipped) { auto vehicle = ps->Entity->As(); if (vehicle == nullptr) @@ -1439,20 +1451,32 @@ namespace OpenRCT2 { return VisibilityKind::Hidden; } + else if (clipped) + { + return VisibilityKind::Partial; + } break; case EntityType::Staff: if (viewFlags & VIEWPORT_FLAG_HIDE_STAFF) { return VisibilityKind::Hidden; } + else if (clipped) + { + return VisibilityKind::Partial; + } break; default: + if (clipped) + { + return VisibilityKind::Partial; + } break; } } break; case ViewportInteractionItem::Ride: - if (viewFlags & VIEWPORT_FLAG_HIDE_RIDES) + if (viewFlags & VIEWPORT_FLAG_HIDE_RIDES || clipped) { return (viewFlags & VIEWPORT_FLAG_INVISIBLE_RIDES) ? VisibilityKind::Hidden : VisibilityKind::Partial; } @@ -1460,7 +1484,7 @@ namespace OpenRCT2 case ViewportInteractionItem::Footpath: case ViewportInteractionItem::PathAddition: case ViewportInteractionItem::Banner: - if (viewFlags & VIEWPORT_FLAG_HIDE_PATHS) + if (viewFlags & VIEWPORT_FLAG_HIDE_PATHS || clipped) { return (viewFlags & VIEWPORT_FLAG_INVISIBLE_PATHS) ? VisibilityKind::Hidden : VisibilityKind::Partial; } @@ -1472,7 +1496,7 @@ namespace OpenRCT2 { if (IsTileElementVegetation(ps->Element)) { - if (viewFlags & VIEWPORT_FLAG_HIDE_VEGETATION) + if (viewFlags & VIEWPORT_FLAG_HIDE_VEGETATION || clipped) { return (viewFlags & VIEWPORT_FLAG_INVISIBLE_VEGETATION) ? VisibilityKind::Hidden : VisibilityKind::Partial; @@ -1480,19 +1504,24 @@ namespace OpenRCT2 } else { - if (viewFlags & VIEWPORT_FLAG_HIDE_SCENERY) + if (viewFlags & VIEWPORT_FLAG_HIDE_SCENERY || clipped) { return (viewFlags & VIEWPORT_FLAG_INVISIBLE_SCENERY) ? VisibilityKind::Hidden : VisibilityKind::Partial; } } } - if (ps->InteractionItem == ViewportInteractionItem::Wall && (viewFlags & VIEWPORT_FLAG_UNDERGROUND_INSIDE)) + if (ps->InteractionItem == ViewportInteractionItem::Wall + && (viewFlags & VIEWPORT_FLAG_UNDERGROUND_INSIDE || clipped)) { return VisibilityKind::Partial; } break; default: + if (clipped) + { + return VisibilityKind::Partial; + } break; } return VisibilityKind::Visible; diff --git a/src/openrct2/interface/Viewport.h b/src/openrct2/interface/Viewport.h index bcaeab07c4..ea5790c4c2 100644 --- a/src/openrct2/interface/Viewport.h +++ b/src/openrct2/interface/Viewport.h @@ -87,7 +87,8 @@ namespace OpenRCT2 VIEWPORT_FLAG_LAND_OWNERSHIP = (1u << 8), VIEWPORT_FLAG_CONSTRUCTION_RIGHTS = (1u << 9), VIEWPORT_FLAG_HIDE_ENTITIES = (1u << 14), - VIEWPORT_FLAG_CLIP_VIEW = (1u << 17), + VIEWPORT_FLAG_CLIP_VIEW = (1u << 15), + VIEWPORT_FLAG_CLIP_VIEW_SEE_THROUGH = (1u << 17), VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES = (1u << 18), VIEWPORT_FLAG_TRANSPARENT_BACKGROUND = (1u << 19), diff --git a/src/openrct2/paint/Paint.Entity.cpp b/src/openrct2/paint/Paint.Entity.cpp index 7da6bc92d3..94272905ff 100644 --- a/src/openrct2/paint/Paint.Entity.cpp +++ b/src/openrct2/paint/Paint.Entity.cpp @@ -83,11 +83,16 @@ void EntityPaintSetup(PaintSession& session, const CoordsXY& pos) // Here converting from land/path/etc height scale to pixel height scale. // Note: peeps/scenery on slopes will be above the base // height of the slope element, and consequently clipped. - if ((session.ViewFlags & VIEWPORT_FLAG_CLIP_VIEW)) + if (session.ViewFlags & VIEWPORT_FLAG_CLIP_VIEW) { if (entityPos.z > (gClipHeight * kCoordsZStep)) { - continue; + // see-through off: don't paint this entity at all + // see-through on: paint this entity as partial or hidden later on + if ((session.ViewFlags & VIEWPORT_FLAG_CLIP_VIEW_SEE_THROUGH) == 0) + { + continue; + } } if (entityPos.x < gClipSelectionA.x || entityPos.x > (gClipSelectionB.x + kCoordsXYStep - 1)) { diff --git a/src/openrct2/paint/tile_element/Paint.TileElement.cpp b/src/openrct2/paint/tile_element/Paint.TileElement.cpp index 20c54c7728..60cf265456 100644 --- a/src/openrct2/paint/tile_element/Paint.TileElement.cpp +++ b/src/openrct2/paint/tile_element/Paint.TileElement.cpp @@ -221,7 +221,16 @@ static void PaintTileElementBase(PaintSession& session, const CoordsXY& origCoor // Only paint tile_elements below the clip height. if ((session.ViewFlags & VIEWPORT_FLAG_CLIP_VIEW) && (tile_element->GetBaseZ() > gClipHeight * kCoordsZStep)) - continue; + { + // see-through off: don't paint this tile_element at all + // see-through on: paint this tile_element as partial or hidden later on + // note: surface elements are not painted even with see-through turned on + if ((session.ViewFlags & VIEWPORT_FLAG_CLIP_VIEW_SEE_THROUGH) == 0 + || tile_element->GetType() == TileElementType::Surface) + { + continue; + } + } Direction direction = tile_element->GetDirectionWithOffset(rotation); int32_t baseZ = tile_element->GetBaseZ();