1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-06 06:32:56 +01:00

Add stable paint sort (as a debug option)

This commit is contained in:
Matt
2024-12-07 14:08:00 +02:00
committed by GitHub
parent 03829f9cea
commit 7154e85c0a
6 changed files with 117 additions and 12 deletions

View File

@@ -3785,3 +3785,4 @@ STR_6706 :{WINDOW_COLOUR_2}Current image file: {BLACK}{STRING}
STR_6707 :(none selected)
STR_6708 :Smooth Strength
STR_6709 :Enter Smooth Strength between {COMMA16} and {COMMA16}
STR_6710 :Stable sort

View File

@@ -5,6 +5,7 @@
- Improved: [#23051] Add large sloped turns and new inversions to the Twister, Vertical Drop, Hyper and Flying Roller Coasters.
- Improved: [#23123] Improve sorting of roller coasters in build new ride menu.
- Improved: [#23211] Add boosters to classic wooden roller coaster (cheats only).
- Improved: [#23229] Add debug option for making the sprite sorting algorithm stable.
- Improved: [#23233] Add diagonal booster to LSM Launched Coaster.
- Fix: [#20070, #22972] Missing and mismatched flat and sloped footpaths on several scenarios.
- Fix: [#22726] Force park rating cheat is not saved with the park.

View File

@@ -455,6 +455,7 @@ namespace OpenRCT2
STR_DEBUG_PAINT_SHOW_DIRTY_VISUALS = 6144,
STR_DEBUG_PAINT_SHOW_SEGMENT_HEIGHTS = 5901,
STR_DEBUG_PAINT_SHOW_WIDE_PATHS = 6261,
STR_DEBUG_PAINT_STABLE_SORT = 6710,
// Window: DemolishRidePrompt
STR_DEMOLISH = 994,

View File

@@ -27,10 +27,11 @@ namespace OpenRCT2::Ui::Windows
WIDX_TOGGLE_SHOW_SEGMENT_HEIGHTS,
WIDX_TOGGLE_SHOW_BOUND_BOXES,
WIDX_TOGGLE_SHOW_DIRTY_VISUALS,
WIDX_TOGGLE_STABLE_PAINT_SORT,
};
constexpr int32_t WINDOW_WIDTH = 200;
constexpr int32_t WINDOW_HEIGHT = 8 + 15 + 15 + 15 + 15 + 11 + 8;
constexpr int32_t WINDOW_HEIGHT = 8 + (15 * 6) + 8;
// clang-format off
static Widget window_debug_paint_widgets[] = {
@@ -40,6 +41,7 @@ namespace OpenRCT2::Ui::Windows
MakeWidget({8, 8 + 15 * 2}, { 185, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_DEBUG_PAINT_SHOW_SEGMENT_HEIGHTS),
MakeWidget({8, 8 + 15 * 3}, { 185, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_DEBUG_PAINT_SHOW_BOUND_BOXES ),
MakeWidget({8, 8 + 15 * 4}, { 185, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_DEBUG_PAINT_SHOW_DIRTY_VISUALS ),
MakeWidget({8, 8 + 15 * 5}, { 185, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_DEBUG_PAINT_STABLE_SORT ),
kWidgetsEnd,
};
// clang-format on
@@ -91,6 +93,11 @@ namespace OpenRCT2::Ui::Windows
gShowDirtyVisuals = !gShowDirtyVisuals;
GfxInvalidateScreen();
break;
case WIDX_TOGGLE_STABLE_PAINT_SORT:
gPaintStableSort = !gPaintStableSort;
GfxInvalidateScreen();
break;
}
}
@@ -136,6 +143,7 @@ namespace OpenRCT2::Ui::Windows
WidgetSetCheckboxValue(*this, WIDX_TOGGLE_SHOW_SEGMENT_HEIGHTS, gShowSupportSegmentHeights);
WidgetSetCheckboxValue(*this, WIDX_TOGGLE_SHOW_BOUND_BOXES, gPaintBoundingBoxes);
WidgetSetCheckboxValue(*this, WIDX_TOGGLE_SHOW_DIRTY_VISUALS, gShowDirtyVisuals);
WidgetSetCheckboxValue(*this, WIDX_TOGGLE_STABLE_PAINT_SORT, gPaintStableSort);
}
void OnDraw(DrawPixelInfo& dpi) override

View File

@@ -57,6 +57,7 @@ static constexpr uint8_t BoundBoxDebugColours[] = {
bool gShowDirtyVisuals;
bool gPaintBoundingBoxes;
bool gPaintBlockedTiles;
bool gPaintStableSort;
static void PaintAttachedPS(DrawPixelInfo& dpi, PaintStruct* ps, uint32_t viewFlags);
static void PaintPSImageWithBoundingBoxes(PaintSession& session, PaintStruct* ps, ImageId imageId, int32_t x, int32_t y);
@@ -403,13 +404,14 @@ static std::pair<PaintStruct*, PaintStruct*> PaintStructsGetNextPending(PaintStr
// Re-orders all nodes after the specified child node and marks the child node as traversed. The resulting
// order of the children is the depth based on rotation and dimensions of the bounding box.
template<uint8_t TRotation>
static void PaintStructsSortQuadrant(PaintStruct* parent, PaintStruct* child)
static void PaintStructsSortQuadrantLegacy(PaintStruct* parent, PaintStruct* child)
{
// Mark visited.
child->SortFlags &= ~PaintSortFlags::PendingVisit;
// Compare all the children below the first child and move them up in the list if they intersect.
const PaintStructBoundBox& initialBBox = child->Bounds;
for (;;)
{
auto* ps = child;
@@ -443,7 +445,78 @@ static void PaintStructsSortQuadrant(PaintStruct* parent, PaintStruct* child)
}
}
// Re-orders all nodes after the specified child node and marks the child node as traversed. The resulting
// order of the children is the depth based on rotation and dimensions of the bounding box.
template<uint8_t TRotation>
static void PaintStructsSortQuadrantStable(PaintStruct* parent, PaintStruct* child)
{
// Mark visited.
child->SortFlags &= ~PaintSortFlags::PendingVisit;
// Compare all the children below the first child and move them up in the list if they intersect.
const PaintStructBoundBox& initialBBox = child->Bounds;
// Create a temporary list to collect sorted nodes in stable order.
PaintStruct* sortedHead = nullptr;
PaintStruct* sortedTail = nullptr;
// Traverse the list and reorder based on intersection.
for (;;)
{
PaintStruct* next = child->NextQuadrantEntry;
if (next != nullptr)
{
PREFETCH(&next->Bounds);
}
// Stop if at the end of the list or outside the quadrant range.
if (next == nullptr || next->SortFlags & PaintSortFlags::OutsideQuadrant)
{
break;
}
// Ignore nodes that are not neighbors.
if (!(next->SortFlags & PaintSortFlags::Neighbour))
{
child = next;
continue;
}
// Detach the current node from the list if it intersects.
if (CheckBoundingBox<TRotation>(initialBBox, next->Bounds))
{
child->NextQuadrantEntry = next->NextQuadrantEntry;
if (sortedHead == nullptr)
{
sortedHead = next;
sortedTail = next;
next->NextQuadrantEntry = nullptr;
}
else
{
sortedTail->NextQuadrantEntry = next;
sortedTail = next;
next->NextQuadrantEntry = nullptr;
}
}
else
{
child = next;
}
}
// Merge the sorted list back into the main list after parent.
if (sortedHead != nullptr)
{
PaintStruct* originalNext = parent->NextQuadrantEntry;
parent->NextQuadrantEntry = sortedHead;
sortedTail->NextQuadrantEntry = originalNext;
}
}
template<bool TStableSort, uint8_t TRotation>
static PaintStruct* PaintArrangeStructsHelperRotation(PaintStruct* psQuadrantEntry, uint16_t quadrantIndex, uint8_t flag)
{
// We keep track of the first node in the quadrant so the next call with a higher quadrant index
@@ -464,7 +537,15 @@ static PaintStruct* PaintArrangeStructsHelperRotation(PaintStruct* psQuadrantEnt
break;
}
PaintStructsSortQuadrant<TRotation>(parent, child);
if constexpr (TStableSort)
{
PaintStructsSortQuadrantStable<TRotation>(parent, child);
}
else
{
PaintStructsSortQuadrantLegacy<TRotation>(parent, child);
}
ps = parent;
}
@@ -496,7 +577,7 @@ static void PaintStructsLinkQuadrants(PaintSessionCore& session, PaintStruct& ps
} while (++quadrantIndex <= session.QuadrantFrontIndex);
}
template<int TRotation>
template<bool TStableSort, int TRotation>
static void PaintSessionArrangeImpl(PaintSessionCore& session)
{
uint32_t quadrantIndex = session.QuadrantBackIndex;
@@ -511,12 +592,13 @@ static void PaintSessionArrangeImpl(PaintSessionCore& session)
PaintStruct psHead{};
PaintStructsLinkQuadrants(session, psHead);
PaintStruct* psNextQuadrant = PaintArrangeStructsHelperRotation<TRotation>(
PaintStruct* psNextQuadrant = PaintArrangeStructsHelperRotation<TStableSort, TRotation>(
&psHead, session.QuadrantBackIndex, PaintSortFlags::Neighbour);
while (++quadrantIndex < session.QuadrantFrontIndex)
{
psNextQuadrant = PaintArrangeStructsHelperRotation<TRotation>(psNextQuadrant, quadrantIndex, PaintSortFlags::None);
psNextQuadrant = PaintArrangeStructsHelperRotation<TStableSort, TRotation>(
psNextQuadrant, quadrantIndex, PaintSortFlags::None);
}
session.PaintHead = psHead.NextQuadrantEntry;
@@ -524,11 +606,18 @@ static void PaintSessionArrangeImpl(PaintSessionCore& session)
using PaintArrangeWithRotation = void (*)(PaintSessionCore& session);
constexpr std::array _paintArrangeFuncs = {
PaintSessionArrangeImpl<0>,
PaintSessionArrangeImpl<1>,
PaintSessionArrangeImpl<2>,
PaintSessionArrangeImpl<3>,
constexpr std::array _paintArrangeFuncsLegacy = {
PaintSessionArrangeImpl<false, 0>,
PaintSessionArrangeImpl<false, 1>,
PaintSessionArrangeImpl<false, 2>,
PaintSessionArrangeImpl<false, 3>,
};
constexpr std::array _paintArrangeFuncsStable = {
PaintSessionArrangeImpl<true, 0>,
PaintSessionArrangeImpl<true, 1>,
PaintSessionArrangeImpl<true, 2>,
PaintSessionArrangeImpl<true, 3>,
};
/**
@@ -538,7 +627,11 @@ constexpr std::array _paintArrangeFuncs = {
void PaintSessionArrange(PaintSessionCore& session)
{
PROFILED_FUNCTION();
return _paintArrangeFuncs[session.CurrentRotation](session);
if (gPaintStableSort)
{
return _paintArrangeFuncsStable[session.CurrentRotation](session);
}
return _paintArrangeFuncsLegacy[session.CurrentRotation](session);
}
static void PaintDrawStruct(PaintSession& session, PaintStruct* ps)

View File

@@ -284,6 +284,7 @@ extern bool gShowDirtyVisuals;
extern bool gPaintBoundingBoxes;
extern bool gPaintBlockedTiles;
extern bool gPaintWidePathsAsGhost;
extern bool gPaintStableSort;
PaintStruct* PaintAddImageAsParent(
PaintSession& session, const ImageId image_id, const CoordsXYZ& offset, const BoundBoxXYZ& boundBox);