From 9e30512cb9ca5ac0b69a6d4bfaf6b8eed406fbd6 Mon Sep 17 00:00:00 2001 From: Spacek531 Date: Thu, 18 Apr 2024 17:04:52 -0700 Subject: [PATCH 1/2] add boundbox properties in sceneryEntries --- src/openrct2/libopenrct2.vcxproj | 4 +- src/openrct2/object/LargeSceneryEntry.h | 3 + src/openrct2/object/LargeSceneryObject.cpp | 54 ++++ src/openrct2/object/SceneryBoundingBox.cpp | 261 ++++++++++++++++++ src/openrct2/object/SceneryBoundingBox.h | 49 ++++ src/openrct2/object/SmallSceneryEntry.h | 3 + src/openrct2/object/SmallSceneryObject.cpp | 60 ++++ src/openrct2/object/SmallSceneryObject.h | 1 + .../paint/tile_element/Paint.LargeScenery.cpp | 19 +- .../paint/tile_element/Paint.SmallScenery.cpp | 83 ++++-- src/openrct2/world/Scenery.h | 3 + 11 files changed, 509 insertions(+), 31 deletions(-) create mode 100644 src/openrct2/object/SceneryBoundingBox.cpp create mode 100644 src/openrct2/object/SceneryBoundingBox.h diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj index 3dda36af25..df292a4b0e 100644 --- a/src/openrct2/libopenrct2.vcxproj +++ b/src/openrct2/libopenrct2.vcxproj @@ -339,6 +339,7 @@ + @@ -842,6 +843,7 @@ + @@ -1065,4 +1067,4 @@ - + \ No newline at end of file diff --git a/src/openrct2/object/LargeSceneryEntry.h b/src/openrct2/object/LargeSceneryEntry.h index 13260f0771..53b675514d 100644 --- a/src/openrct2/object/LargeSceneryEntry.h +++ b/src/openrct2/object/LargeSceneryEntry.h @@ -11,6 +11,7 @@ #include "../common.h" #include "../interface/Cursors.h" #include "../world/Location.hpp" +#include "../world/Scenery.h" #include "ObjectTypes.h" struct LargeSceneryText; @@ -23,6 +24,8 @@ struct LargeSceneryTile uint8_t z_clearance; // CCCC WWWW 0SS0 0000 uint16_t flags; + SceneryBoundBoxes boundBoxes = {}; + CoordsXYZ spriteOffset = {}; }; enum diff --git a/src/openrct2/object/LargeSceneryObject.cpp b/src/openrct2/object/LargeSceneryObject.cpp index eda305cd0c..b89c949e6c 100644 --- a/src/openrct2/object/LargeSceneryObject.cpp +++ b/src/openrct2/object/LargeSceneryObject.cpp @@ -19,10 +19,54 @@ #include "../localisation/Language.h" #include "../world/Banner.h" #include "../world/Location.hpp" +#include "SceneryBoundingBox.h" #include #include +static DefaultBoundingBoxType boundBoxTypes[16] = { + DefaultBoundingBoxType::FullTileBox, // 0000 + DefaultBoundingBoxType::FullTileSouthQuadrantBox, // 0001 + DefaultBoundingBoxType::FullTileWestQuadrantBox, // 0010 + DefaultBoundingBoxType::FullTileSouthwestSideBox, // 0011 + DefaultBoundingBoxType::FullTileNorthQuadrantBox, // 0100 + DefaultBoundingBoxType::FullTileBox, // 0101 (diagonal of South and North corners) + DefaultBoundingBoxType::FullTileNorthwestSideBox, // 0110 + DefaultBoundingBoxType::FullTileBox, // 0111 (triangle of South, West, and North corners) + DefaultBoundingBoxType::FullTileEastQuadrantBox, // 1000 + DefaultBoundingBoxType::FullTileSoutheastSideBox, // 1001 + DefaultBoundingBoxType::FullTileBox, // 1010 (diagonal of East and West corners) + DefaultBoundingBoxType::FullTileBox, // 1011 (triangle of South, West, and East corners) + DefaultBoundingBoxType::FullTileNortheastSideBox, // 1100 + DefaultBoundingBoxType::FullTileBox, // 1101 (triangle of South, West, and North corners) + DefaultBoundingBoxType::FullTileBox, // 1110 (triangle of West, North, and East corners) + DefaultBoundingBoxType::FullTileBox, // 1111 +}; + +static int32_t getBoundBoxHeight(uint8_t clearanceHeight) +{ + return std::min(clearanceHeight, 128) - 3; +} + +static void SetTileBoundingBox(LargeSceneryTile& tile) +{ + if (tile.flags & 0xF00) + { + tile.boundBoxes = GetDefaultSceneryBoundBoxes(boundBoxTypes[(tile.flags & 0xF000) >> 12]); + } + else + { + tile.boundBoxes = GetDefaultSceneryBoundBoxes(DefaultBoundingBoxType::FullTileLargeBox); + } + tile.spriteOffset = GetDefaultSpriteOffset(DefaultSpriteOffsetType::LargeSceneryOffset); + + auto clearanceHeight = getBoundBoxHeight(tile.z_clearance); + for (uint8_t i = 0; i < NumOrthogonalDirections; i++) + { + tile.boundBoxes[i].length.z = clearanceHeight; + } +} + static RCTLargeSceneryText ReadLegacy3DFont(OpenRCT2::IStream& stream) { RCTLargeSceneryText _3dFontLegacy = {}; @@ -168,6 +212,7 @@ std::vector LargeSceneryObject::ReadTiles(OpenRCT2::IStream* s tile.z_offset = stream->ReadValue(); tile.z_clearance = stream->ReadValue(); tile.flags = stream->ReadValue(); + SetTileBoundingBox(tile); return tile; }; @@ -256,6 +301,15 @@ std::vector LargeSceneryObject::ReadJsonTiles(json_t& jTiles) auto walls = Json::GetNumber(jTile["walls"]); tile.flags |= (walls & 0xFF) << 8; + SetTileBoundingBox(tile); + auto jBBox = jTile["boundingBox"]; + if (!jBBox.empty()) + { + tile.boundBoxes = ReadBoundBoxes(jBBox, tile.boundBoxes[0].length.z, false); + } + auto jSpriteOffset = jTile["spriteOffsetCoordinates"]; + if (!jSpriteOffset.empty()) + tile.spriteOffset = ReadSpriteOffset(jSpriteOffset); tiles.push_back(std::move(tile)); } diff --git a/src/openrct2/object/SceneryBoundingBox.cpp b/src/openrct2/object/SceneryBoundingBox.cpp new file mode 100644 index 0000000000..11a50a13fe --- /dev/null +++ b/src/openrct2/object/SceneryBoundingBox.cpp @@ -0,0 +1,261 @@ +/***************************************************************************** + * Copyright (c) 2014-2024 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#include "SceneryBoundingBox.h" + +constexpr std::array DefaultSpriteOffsets = { + CoordsXYZ(7, 7, 0), // quarter tile + CoordsXYZ(15, 15, 0), // small scenery full tile w/o VOFFSET_CENTRE + CoordsXYZ(3, 3, 0), // small scenery halftile/VOFFSET_CENTRE + CoordsXYZ(1, 1, 0), // small scenery VOFFSET_CENTER+NO_WALLS + CoordsXYZ(0, 0, 0), // large scenery +}; + +constexpr SceneryBoundBoxes QuarterTile = { + BoundBoxXYZ({ 7, 7, 0 }, { 2, 2, 0 }), + BoundBoxXYZ({ 7, 7, 0 }, { 2, 2, 0 }), + BoundBoxXYZ({ 7, 7, 0 }, { 2, 2, 0 }), + BoundBoxXYZ({ 7, 7, 0 }, { 2, 2, 0 }), +}; + +constexpr SceneryBoundBoxes HalfTile = { + BoundBoxXYZ({ 3, 3, 0 }, { 12, 26, 0 }), + BoundBoxXYZ({ 3, 17, 0 }, { 26, 12, 0 }), + BoundBoxXYZ({ 17, 3, 0 }, { 12, 26, 0 }), + BoundBoxXYZ({ 3, 3, 0 }, { 26, 12, 0 }), +}; + +// LargeSpecial corner/side match sub-fulltile large scenery boundboxes +constexpr SceneryBoundBoxes FullTileNorthQuadrant = { + BoundBoxXYZ({ 3, 3, 0 }, { 12, 12, 0 }), + BoundBoxXYZ({ 3, 17, 0 }, { 12, 12, 0 }), + BoundBoxXYZ({ 17, 17, 0 }, { 12, 12, 0 }), + BoundBoxXYZ({ 17, 3, 0 }, { 12, 12, 0 }), +}; +constexpr SceneryBoundBoxes FullTileNortheastSide = { + BoundBoxXYZ({ 3, 3, 0 }, { 12, 28, 0 }), + BoundBoxXYZ({ 3, 17, 0 }, { 26, 12, 0 }), + BoundBoxXYZ({ 17, 3, 0 }, { 12, 26, 0 }), + BoundBoxXYZ({ 3, 3, 0 }, { 28, 12, 0 }), +}; +constexpr SceneryBoundBoxes FullTileEastQuadrant = { + BoundBoxXYZ({ 3, 17, 0 }, { 12, 12, 0 }), + BoundBoxXYZ({ 17, 17, 0 }, { 12, 12, 0 }), + BoundBoxXYZ({ 17, 3, 0 }, { 12, 12, 0 }), + BoundBoxXYZ({ 3, 3, 0 }, { 12, 12, 0 }), +}; +constexpr SceneryBoundBoxes FullTileSoutheastSide = { + BoundBoxXYZ({ 3, 17, 0 }, { 26, 12, 0 }), + BoundBoxXYZ({ 17, 3, 0 }, { 12, 26, 0 }), + BoundBoxXYZ({ 3, 3, 0 }, { 28, 12, 0 }), + BoundBoxXYZ({ 3, 3, 0 }, { 12, 28, 0 }), +}; +constexpr SceneryBoundBoxes FullTileSouthQuadrant = { + BoundBoxXYZ({ 17, 17, 0 }, { 12, 12, 0 }), + BoundBoxXYZ({ 17, 3, 0 }, { 12, 12, 0 }), + BoundBoxXYZ({ 3, 3, 0 }, { 12, 12, 0 }), + BoundBoxXYZ({ 3, 17, 0 }, { 12, 12, 0 }), +}; +constexpr SceneryBoundBoxes FullTileSouthwestSide = { + BoundBoxXYZ({ 17, 3, 0 }, { 12, 26, 0 }), + BoundBoxXYZ({ 3, 3, 0 }, { 28, 12, 0 }), + BoundBoxXYZ({ 3, 3, 0 }, { 12, 28, 0 }), + BoundBoxXYZ({ 3, 17, 0 }, { 26, 12, 0 }), +}; +constexpr SceneryBoundBoxes FullTileWestQuadrant = { + BoundBoxXYZ({ 17, 3, 0 }, { 12, 12, 0 }), + BoundBoxXYZ({ 3, 3, 0 }, { 12, 12, 0 }), + BoundBoxXYZ({ 3, 17, 0 }, { 12, 12, 0 }), + BoundBoxXYZ({ 17, 17, 0 }, { 12, 12, 0 }), +}; +constexpr SceneryBoundBoxes FullTileNorthwestSide = { + BoundBoxXYZ({ 3, 3, 0 }, { 28, 12, 0 }), + BoundBoxXYZ({ 3, 3, 0 }, { 12, 28, 0 }), + BoundBoxXYZ({ 3, 17, 0 }, { 26, 12, 0 }), + BoundBoxXYZ({ 17, 3, 0 }, { 12, 26, 0 }), +}; + +// LargeSpecialCenter matches large scenery with allowed walls and small scenery with SMALL_SCENERY_FLAG_VOFFSET_CENTRE +constexpr SceneryBoundBoxes FullTile = { + BoundBoxXYZ({ 3, 3, 0 }, { 26, 26, 0 }), + BoundBoxXYZ({ 3, 3, 0 }, { 26, 26, 0 }), + BoundBoxXYZ({ 3, 3, 0 }, { 26, 26, 0 }), + BoundBoxXYZ({ 3, 3, 0 }, { 26, 26, 0 }), +}; + +// Large matches large scenery and small scenery that do not allow walls. +constexpr SceneryBoundBoxes FullTileLarge = { + BoundBoxXYZ({ 1, 1, 0 }, { 30, 30, 0 }), + BoundBoxXYZ({ 1, 1, 0 }, { 30, 30, 0 }), + BoundBoxXYZ({ 1, 1, 0 }, { 30, 30, 0 }), + BoundBoxXYZ({ 1, 1, 0 }, { 30, 30, 0 }), +}; + +// Small Scenery without VOFFSET_CENTRE flag set +constexpr SceneryBoundBoxes FullTileThin = { + BoundBoxXYZ({ 15, 15, 0 }, { 2, 2, 0 }), + BoundBoxXYZ({ 15, 15, 0 }, { 2, 2, 0 }), + BoundBoxXYZ({ 15, 15, 0 }, { 2, 2, 0 }), + BoundBoxXYZ({ 15, 15, 0 }, { 2, 2, 0 }), +}; + +static const std::array boundBoxes = { + QuarterTile, + HalfTile, + FullTileNorthQuadrant, + FullTileNortheastSide, + FullTileEastQuadrant, + FullTileSoutheastSide, + FullTileSouthQuadrant, + FullTileSouthwestSide, + FullTileWestQuadrant, + FullTileNorthwestSide, + FullTile, + FullTileLarge, + FullTileThin, +}; + +#pragma endregion + +static const EnumMap BBoxTypeLookup{ + { "quarterTile", DefaultBoundingBoxType::QuarterTileBox }, + { "halfTile", DefaultBoundingBoxType::HalfTileBox }, + { "cornerNorth", DefaultBoundingBoxType::FullTileNorthQuadrantBox }, + { "sideNortheast", DefaultBoundingBoxType::FullTileNortheastSideBox }, + { "cornerEast", DefaultBoundingBoxType::FullTileEastQuadrantBox }, + { "sideSoutheast", DefaultBoundingBoxType::FullTileSoutheastSideBox }, + { "cornerSouth", DefaultBoundingBoxType::FullTileSouthQuadrantBox }, + { "sideSouthwest", DefaultBoundingBoxType::FullTileSouthwestSideBox }, + { "cornerEast", DefaultBoundingBoxType::FullTileWestQuadrantBox }, + { "sideNorthwest", DefaultBoundingBoxType::FullTileNorthwestSideBox }, + { "fullTile", DefaultBoundingBoxType::FullTileBox }, + { "fullTileLarge", DefaultBoundingBoxType::FullTileLargeBox }, + { "fullTileThin", DefaultBoundingBoxType::FullTileThinBox } +}; + +static DefaultBoundingBoxType GetBoundingBoxTypeFromString(const std::string& s) +{ + auto result = BBoxTypeLookup.find(s); + return (result != BBoxTypeLookup.end()) ? result->second : DefaultBoundingBoxType::FullTileBox; +} + +SceneryBoundBoxes GetDefaultSceneryBoundBoxes(DefaultBoundingBoxType type) +{ + if (type >= DefaultBoundingBoxType::CountBox) + return boundBoxes[DefaultBoundingBoxType::FullTileBox]; + return boundBoxes[type]; +} + +static BoundBoxXYZ ReadBoundBox(json_t& box) +{ + auto jOffset = box["offset"]; + auto jLength = box["size"]; + + Guard::Assert( + jOffset.is_array() && jLength.is_array() && jOffset.size() >= 3 && jLength.size() >= 3, + "ReadBoundBox expects arrays 'offset' and 'size' with 3 elements"); + BoundBoxXYZ boundBox = { + { + Json::GetNumber(jOffset[0], 0), + Json::GetNumber(jOffset[1], 0), + Json::GetNumber(jOffset[2], 0), + }, + { + Json::GetNumber(jLength[0], 0), + Json::GetNumber(jLength[1], 0), + Json::GetNumber(jLength[2], 0), + }, + }; + return boundBox; +} + +// Rotates a BoundBoxXYZ clockwise 90 degrees around the vertical axis. +static BoundBoxXYZ RotateBoundBox(BoundBoxXYZ box, CoordsXY rotationCenter) +{ + CoordsXYZ rotatedLength = { box.length.y, box.length.x, box.length.z }; + // equations are performed in double scale to avoid decimals + CoordsXY relativeCentroid = box.offset * 2 + box.length - rotationCenter * 2; + CoordsXY rotatedCentroid = { relativeCentroid.y, -relativeCentroid.x }; + CoordsXY newCorner = (rotatedCentroid - rotatedLength) / 2 + rotationCenter; + return { { newCorner.x, newCorner.y, box.offset.z }, rotatedLength }; +} + +SceneryBoundBoxes ReadBoundBoxes(json_t& jBBox, int32_t defaultHeight, bool fullTile) +{ + SceneryBoundBoxes boxes; + if (jBBox.is_array()) + { + Guard::Assert(jBBox.size() >= 4, "boundBox arrays require four elements, one for each view angle"); + // array of four bboxes + for (uint8_t i = 0; i < NumOrthogonalDirections; i++) + boxes[i] = ReadBoundBox(jBBox[i]); + } + else if (jBBox.is_object()) + { + // single box, rotated around (16, 16) if fulltile or (8,8) if quarter tile + CoordsXY rotationCenter = { 8, 8 }; + if (fullTile) + { + rotationCenter = { 16, 16 }; + } + auto bBox = ReadBoundBox(jBBox); + boxes[0] = bBox; + boxes[1] = RotateBoundBox(bBox, rotationCenter); + boxes[2] = RotateBoundBox(boxes[1], rotationCenter); + boxes[3] = RotateBoundBox(boxes[2], rotationCenter); + } + else + { + Guard::Assert( + jBBox.is_string(), + "boundBox must be an array of four boundBox objects, a single boundBox object, or a string matching the " + "DefaultBoundingBoxType enum."); + boxes = GetDefaultSceneryBoundBoxes(GetBoundingBoxTypeFromString(Json::GetString(jBBox))); + for (uint8_t i = 0; i < NumOrthogonalDirections; i++) + boxes[i].length.z = defaultHeight; + } + return boxes; +} + +static const EnumMap SpriteOffsetLookup{ + { "quarterTile", DefaultSpriteOffsetType::QuarterTileOffset }, + { "fullTileThin", DefaultSpriteOffsetType::FullTileThinOffset }, + { "halfTile", DefaultSpriteOffsetType::FullTileOffset }, + { "fullTile", DefaultSpriteOffsetType::FullTileOffset }, + { "fullTileLarge", DefaultSpriteOffsetType::FullTileLargeOffset }, + { "largeScenery", DefaultSpriteOffsetType::LargeSceneryOffset }, +}; + +static DefaultSpriteOffsetType GetSpriteOffsetTypeFromString(const std::string& s) +{ + auto result = SpriteOffsetLookup.find(s); + return (result != SpriteOffsetLookup.end()) ? result->second : DefaultSpriteOffsetType::LargeSceneryOffset; +} + +CoordsXYZ GetDefaultSpriteOffset(DefaultSpriteOffsetType type) +{ + if (type >= DefaultSpriteOffsetType::CountOffset) + return DefaultSpriteOffsets[DefaultSpriteOffsetType::LargeSceneryOffset]; + return DefaultSpriteOffsets[type]; +} + +CoordsXYZ ReadSpriteOffset(json_t& jCoords) +{ + if (jCoords.is_string()) + { + return DefaultSpriteOffsets[GetSpriteOffsetTypeFromString(Json::GetString(jCoords))]; + } + Guard::Assert(jCoords.is_array() && jCoords.size() >= 3, "spriteOffsetCoordinates must be an array with three elements"); + CoordsXYZ coordinates = { + Json::GetNumber(jCoords[0], 0), + Json::GetNumber(jCoords[1], 0), + Json::GetNumber(jCoords[2], 0), + }; + return coordinates; +} diff --git a/src/openrct2/object/SceneryBoundingBox.h b/src/openrct2/object/SceneryBoundingBox.h new file mode 100644 index 0000000000..92aac01280 --- /dev/null +++ b/src/openrct2/object/SceneryBoundingBox.h @@ -0,0 +1,49 @@ +/***************************************************************************** + * Copyright (c) 2014-2024 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../core/EnumMap.hpp" +#include "../core/Json.hpp" +#include "../core/Memory.hpp" +#include "../paint/Boundbox.h" +#include "../world/Scenery.h" + +enum DefaultBoundingBoxType : uint8_t +{ + QuarterTileBox, + HalfTileBox, + FullTileNorthQuadrantBox, + FullTileNortheastSideBox, + FullTileEastQuadrantBox, + FullTileSoutheastSideBox, + FullTileSouthQuadrantBox, + FullTileSouthwestSideBox, + FullTileWestQuadrantBox, + FullTileNorthwestSideBox, + FullTileBox, + FullTileLargeBox, + FullTileThinBox, + CountBox +}; + +enum DefaultSpriteOffsetType : uint8_t +{ + QuarterTileOffset, + FullTileThinOffset, + FullTileOffset, + FullTileLargeOffset, + LargeSceneryOffset, + CountOffset +}; + +SceneryBoundBoxes GetDefaultSceneryBoundBoxes(DefaultBoundingBoxType type); +SceneryBoundBoxes ReadBoundBoxes(json_t& jBBox, int32_t defaultHeight, bool fullTile); +CoordsXYZ GetDefaultSpriteOffset(DefaultSpriteOffsetType type); +CoordsXYZ ReadSpriteOffset(json_t& jCoords); diff --git a/src/openrct2/object/SmallSceneryEntry.h b/src/openrct2/object/SmallSceneryEntry.h index 0b6b0d028c..26dc38569c 100644 --- a/src/openrct2/object/SmallSceneryEntry.h +++ b/src/openrct2/object/SmallSceneryEntry.h @@ -11,6 +11,7 @@ #include "../common.h" #include "../interface/Cursors.h" +#include "../world/Scenery.h" #include "ObjectTypes.h" enum SMALL_SCENERY_FLAGS : uint32_t @@ -67,6 +68,8 @@ struct SmallSceneryEntry uint16_t animation_mask; uint16_t num_frames; ObjectEntryIndex scenery_tab_id; + SceneryBoundBoxes boundBoxes; + CoordsXYZ spriteOffset; constexpr bool HasFlag(const uint32_t _flags) const { diff --git a/src/openrct2/object/SmallSceneryObject.cpp b/src/openrct2/object/SmallSceneryObject.cpp index 0067615077..18f1bef287 100644 --- a/src/openrct2/object/SmallSceneryObject.cpp +++ b/src/openrct2/object/SmallSceneryObject.cpp @@ -19,6 +19,7 @@ #include "../interface/Cursors.h" #include "../localisation/Language.h" #include "../world/Scenery.h" +#include "SceneryBoundingBox.h" #include @@ -35,6 +36,7 @@ void SmallSceneryObject::ReadLegacy(IReadObjectContext* context, OpenRCT2::IStre _legacyType.animation_mask = stream->ReadValue(); _legacyType.num_frames = stream->ReadValue(); _legacyType.scenery_tab_id = OBJECT_ENTRY_INDEX_NULL; + SetBoundingBoxFromFlags(); GetStringTable().Read(context, stream, ObjectStringID::NAME); @@ -247,6 +249,17 @@ void SmallSceneryObject::ReadJson(IReadObjectContext* context, json_t& root) } } } + SetBoundingBoxFromFlags(); + auto jBBox = properties["boundingBox"]; + if (!jBBox.empty()) + { + _legacyType.boundBoxes = ReadBoundBoxes( + jBBox, _legacyType.boundBoxes[0].length.z, + _legacyType.HasFlag(SMALL_SCENERY_FLAG_FULL_TILE) || _legacyType.HasFlag(SMALL_SCENERY_FLAG_HALF_SPACE)); + } + auto jSpriteOffset = properties["spriteOffsetCoordinates"]; + if (!jSpriteOffset.empty()) + _legacyType.spriteOffset = ReadSpriteOffset(jSpriteOffset); auto jFrameOffsets = properties["frameOffsets"]; if (jFrameOffsets.is_array()) @@ -271,3 +284,50 @@ std::vector SmallSceneryObject::ReadJsonFrameOffsets(json_t& jFrameOffs } return offsets; } + +static int32_t getBoundBoxHeight(uint8_t clearanceHeight) +{ + int32_t height = clearanceHeight - 4; + if (height > 128 || height < 0) + { + height = 128; + } + return height - 1; +} + +void SmallSceneryObject::SetBoundingBoxFromFlags() +{ + DefaultBoundingBoxType boundBoxType = DefaultBoundingBoxType::QuarterTileBox; + DefaultSpriteOffsetType spriteOffsetType = DefaultSpriteOffsetType::QuarterTileOffset; + if (_legacyType.HasFlag(SMALL_SCENERY_FLAG_FULL_TILE)) + { + boundBoxType = DefaultBoundingBoxType::FullTileThinBox; + spriteOffsetType = DefaultSpriteOffsetType::FullTileThinOffset; + if (_legacyType.HasFlag(SMALL_SCENERY_FLAG_HALF_SPACE)) + { + spriteOffsetType = DefaultSpriteOffsetType::FullTileOffset; + boundBoxType = DefaultBoundingBoxType::HalfTileBox; + } + else + { + if (_legacyType.HasFlag(SMALL_SCENERY_FLAG_VOFFSET_CENTRE)) + { + boundBoxType = DefaultBoundingBoxType::FullTileBox; + spriteOffsetType = DefaultSpriteOffsetType::FullTileOffset; + if (_legacyType.HasFlag(SMALL_SCENERY_FLAG_NO_WALLS)) + { + boundBoxType = DefaultBoundingBoxType::FullTileLargeBox; + spriteOffsetType = DefaultSpriteOffsetType::FullTileLargeOffset; + } + } + } + } + _legacyType.spriteOffset = GetDefaultSpriteOffset(spriteOffsetType); + _legacyType.boundBoxes = GetDefaultSceneryBoundBoxes(boundBoxType); + + auto clearanceHeight = getBoundBoxHeight(_legacyType.height); + for (uint8_t i = 0; i < NumOrthogonalDirections; i++) + { + _legacyType.boundBoxes[i].length.z = clearanceHeight; + } +} diff --git a/src/openrct2/object/SmallSceneryObject.h b/src/openrct2/object/SmallSceneryObject.h index fe2051bd18..1f2c2108d4 100644 --- a/src/openrct2/object/SmallSceneryObject.h +++ b/src/openrct2/object/SmallSceneryObject.h @@ -38,4 +38,5 @@ private: static std::vector ReadFrameOffsets(OpenRCT2::IStream* stream); static std::vector ReadJsonFrameOffsets(json_t& jFrameOffsets); void PerformFixes(); + void SetBoundingBoxFromFlags(); }; diff --git a/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp b/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp index 67a4292d6b..91eeca6e14 100644 --- a/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp +++ b/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp @@ -387,6 +387,12 @@ void PaintLargeScenery(PaintSession& session, uint8_t direction, uint16_t height } } + auto boundBox = tile->boundBoxes[direction]; + auto offset = tile->spriteOffset; + boundBox.offset.z += height; + offset.z += height; + +#pragma region LegacyVerification auto boxlengthZ = std::min(tile->z_clearance, 128) - 3; auto flags = tile->flags; auto bbIndex = 16; @@ -399,8 +405,16 @@ void PaintLargeScenery(PaintSession& session, uint8_t direction, uint16_t height const CoordsXYZ& bbOffset = { LargeSceneryBoundBoxes[bbIndex].offset, height }; const CoordsXYZ& bbLength = { LargeSceneryBoundBoxes[bbIndex].length, boxlengthZ }; + auto boxOffsetDelta = bbOffset - boundBox.offset; + auto boxLengthDelta = bbLength - boundBox.length; + auto offsetDelta = CoordsXYZ(0, 0, height) - offset; + assert(boxOffsetDelta.x == 0 && boxOffsetDelta.y == 0 && boxOffsetDelta.z == 0); + assert(boxLengthDelta.x == 0 && boxLengthDelta.y == 0 && boxLengthDelta.z == 0); + assert(offsetDelta.x == 0 && offsetDelta.y == 0 && offsetDelta.z == 0); +#pragma endregion + auto imageIndex = sceneryEntry->image + 4 + (sequenceNum << 2) + direction; - PaintAddImageAsParent(session, imageTemplate.WithIndex(imageIndex), { 0, 0, height }, { bbOffset, bbLength }); + PaintAddImageAsParent(session, imageTemplate.WithIndex(imageIndex), offset, boundBox); if (sceneryEntry->scrolling_mode != SCROLLING_MODE_NONE && direction != 1 && direction != 2) { @@ -413,7 +427,8 @@ void PaintLargeScenery(PaintSession& session, uint8_t direction, uint16_t height auto sequenceDirection2 = (tileElement.GetSequenceIndex() - 1) & 3; if (sequenceDirection2 == direction) { - PaintLargeSceneryScrollingText(session, *sceneryEntry, tileElement, direction, height, bbOffset, isGhost); + PaintLargeSceneryScrollingText( + session, *sceneryEntry, tileElement, direction, height, boundBox.offset, isGhost); } } } diff --git a/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp b/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp index 5934ae8700..4d70986973 100644 --- a/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp +++ b/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp @@ -26,6 +26,13 @@ using namespace OpenRCT2; +static constexpr CoordsXY newQuadrantOffsets[NumOrthogonalDirections] = { + { 0, 0 }, + { 0, 16 }, + { 16, 16 }, + { 16, 0 }, +}; + static constexpr CoordsXY lengths[] = { { 12, 26 }, { 26, 12 }, @@ -119,9 +126,21 @@ static void PaintSmallSceneryBody( { PROFILED_FUNCTION(); - BoundBoxXYZ boundBox = { { 0, 0, height }, { 2, 2, 0 } }; + BoundBoxXYZ boundBox = sceneryEntry->boundBoxes[direction]; + CoordsXYZ offset = sceneryEntry->spriteOffset; + boundBox.offset.z += height; + offset.z += height; - CoordsXYZ offset = { 0, 0, height }; + if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE)) + { + uint8_t quadrant = (sceneryElement.GetSceneryQuadrant() + session.CurrentRotation) & 3; + boundBox.offset += newQuadrantOffsets[quadrant]; + offset += newQuadrantOffsets[quadrant]; + } + +#pragma region LegacyVerification + BoundBoxXYZ boundBoxLegacy = { { 0, 0, height }, { 2, 2, 0 } }; + CoordsXYZ offsetLegacy = { 0, 0, height }; if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE)) { if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HALF_SPACE)) @@ -132,50 +151,58 @@ static void PaintSmallSceneryBody( { 17, 3 }, { 3, 3 }, }; - boundBox.offset.x = sceneryHalfTileOffsets[direction].x; - boundBox.offset.y = sceneryHalfTileOffsets[direction].y; - boundBox.length.x = lengths[direction].x; - boundBox.length.y = lengths[direction].y; - offset.x = 3; - offset.y = 3; + boundBoxLegacy.offset.x = sceneryHalfTileOffsets[direction].x; + boundBoxLegacy.offset.y = sceneryHalfTileOffsets[direction].y; + boundBoxLegacy.length.x = lengths[direction].x; + boundBoxLegacy.length.y = lengths[direction].y; + offsetLegacy.x = 3; + offsetLegacy.y = 3; } else { - offset.x = 15; - offset.y = 15; + offsetLegacy.x = 15; + offsetLegacy.y = 15; if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_VOFFSET_CENTRE)) { - offset.x = 3; - offset.y = 3; - boundBox.length.x = 26; - boundBox.length.y = 26; + offsetLegacy.x = 3; + offsetLegacy.y = 3; + boundBoxLegacy.length.x = 26; + boundBoxLegacy.length.y = 26; if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_NO_WALLS)) { - offset.x = 1; - offset.y = 1; - boundBox.length.x = 30; - boundBox.length.y = 30; + offsetLegacy.x = 1; + offsetLegacy.y = 1; + boundBoxLegacy.length.x = 30; + boundBoxLegacy.length.y = 30; } } - boundBox.offset.x = offset.x; - boundBox.offset.y = offset.y; + boundBoxLegacy.offset.x = offset.x; + boundBoxLegacy.offset.y = offset.y; } } else { uint8_t quadrant = (sceneryElement.GetSceneryQuadrant() + session.CurrentRotation) & 3; // -1 to maintain compatibility with existing CSOs in context of issue #17616 - offset.x = SceneryQuadrantOffsets[quadrant].x - 1; - offset.y = SceneryQuadrantOffsets[quadrant].y - 1; - boundBox.offset.x = offset.x; - boundBox.offset.y = offset.y; + offsetLegacy.x = SceneryQuadrantOffsets[quadrant].x - 1; + offsetLegacy.y = SceneryQuadrantOffsets[quadrant].y - 1; + boundBoxLegacy.offset.x = offsetLegacy.x; + boundBoxLegacy.offset.y = offsetLegacy.y; } - boundBox.length.z = sceneryEntry->height - 4; - if (boundBox.length.z > 128 || boundBox.length.z < 0) + boundBoxLegacy.length.z = sceneryEntry->height - 4; + if (boundBoxLegacy.length.z > 128 || boundBoxLegacy.length.z < 0) { - boundBox.length.z = 128; + boundBoxLegacy.length.z = 128; } - boundBox.length.z--; + boundBoxLegacy.length.z--; + + auto boxOffsetDelta = boundBoxLegacy.offset - boundBox.offset; + auto boxLengthDelta = boundBoxLegacy.length - boundBox.length; + auto offsetDelta = offsetLegacy - offset; + assert(boxOffsetDelta.x == 0 && boxOffsetDelta.y == 0 && boxOffsetDelta.z == 0); + assert(boxLengthDelta.x == 0 && boxLengthDelta.y == 0 && boxLengthDelta.z == 0); + assert(offsetDelta.x == 0 && offsetDelta.y == 0 && offsetDelta.z == 0); +#pragma endregion ImageIndex baseImageIndex = sceneryEntry->image + direction; if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_CAN_WITHER)) diff --git a/src/openrct2/world/Scenery.h b/src/openrct2/world/Scenery.h index b30c1795b4..c4b0d39408 100644 --- a/src/openrct2/world/Scenery.h +++ b/src/openrct2/world/Scenery.h @@ -10,6 +10,7 @@ #pragma once #include "../common.h" +#include "../paint/Boundbox.h" #include "Location.hpp" #include "ScenerySelection.h" @@ -18,6 +19,8 @@ constexpr uint8_t kSceneryWitherAgeThreshold1 = 0x28; constexpr uint8_t kSceneryWitherAgeThreshold2 = 0x37; +using SceneryBoundBoxes = std::array; + enum { SCENERY_TYPE_SMALL, From 8da2c36ea5b3b2a48ba0f67d4eb181ed669b56d5 Mon Sep 17 00:00:00 2001 From: Spacek531 Date: Sat, 20 Apr 2024 23:54:16 -0700 Subject: [PATCH 2/2] remove pseudo-test from code --- .../paint/tile_element/Paint.LargeScenery.cpp | 43 ----------- .../paint/tile_element/Paint.SmallScenery.cpp | 73 ------------------- 2 files changed, 116 deletions(-) diff --git a/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp b/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp index 91eeca6e14..f4b78d71da 100644 --- a/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp +++ b/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp @@ -33,28 +33,6 @@ using namespace OpenRCT2; -// clang-format off -static constexpr BoundBoxXY LargeSceneryBoundBoxes[] = { - { { 3, 3 }, { 26, 26 } }, - { { 17, 17 }, { 12, 12 } }, - { { 17, 3 }, { 12, 12 } }, - { { 17, 3 }, { 12, 26 } }, - { { 3, 3 }, { 12, 12 } }, - { { 3, 3 }, { 26, 26 } }, - { { 3, 3 }, { 28, 12 } }, - { { 3, 3 }, { 26, 26 } }, - { { 3, 17 }, { 12, 12 } }, - { { 3, 17 }, { 26, 12 } }, - { { 3, 3 }, { 26, 26 } }, - { { 3, 3 }, { 26, 26 } }, - { { 3, 3 }, { 12, 28 } }, - { { 3, 3 }, { 26, 26 } }, - { { 3, 3 }, { 26, 26 } }, - { { 3, 3 }, { 26, 26 } }, - { { 1, 1 }, { 30, 30 } }, -}; -// clang-format on - static void PaintLargeScenerySupports( PaintSession& session, uint8_t direction, uint16_t height, const LargeSceneryElement& tileElement, ImageId imageTemplate, const LargeSceneryTile& tile) @@ -392,27 +370,6 @@ void PaintLargeScenery(PaintSession& session, uint8_t direction, uint16_t height boundBox.offset.z += height; offset.z += height; -#pragma region LegacyVerification - auto boxlengthZ = std::min(tile->z_clearance, 128) - 3; - auto flags = tile->flags; - auto bbIndex = 16; - if (flags & 0xF00) - { - flags &= 0xF000; - flags = Numerics::rol16(flags, direction); - bbIndex = (flags & 0xF) | (flags >> 12); - } - const CoordsXYZ& bbOffset = { LargeSceneryBoundBoxes[bbIndex].offset, height }; - const CoordsXYZ& bbLength = { LargeSceneryBoundBoxes[bbIndex].length, boxlengthZ }; - - auto boxOffsetDelta = bbOffset - boundBox.offset; - auto boxLengthDelta = bbLength - boundBox.length; - auto offsetDelta = CoordsXYZ(0, 0, height) - offset; - assert(boxOffsetDelta.x == 0 && boxOffsetDelta.y == 0 && boxOffsetDelta.z == 0); - assert(boxLengthDelta.x == 0 && boxLengthDelta.y == 0 && boxLengthDelta.z == 0); - assert(offsetDelta.x == 0 && offsetDelta.y == 0 && offsetDelta.z == 0); -#pragma endregion - auto imageIndex = sceneryEntry->image + 4 + (sequenceNum << 2) + direction; PaintAddImageAsParent(session, imageTemplate.WithIndex(imageIndex), offset, boundBox); diff --git a/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp b/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp index 4d70986973..dab5529dbb 100644 --- a/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp +++ b/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp @@ -33,13 +33,6 @@ static constexpr CoordsXY newQuadrantOffsets[NumOrthogonalDirections] = { { 16, 0 }, }; -static constexpr CoordsXY lengths[] = { - { 12, 26 }, - { 26, 12 }, - { 12, 26 }, - { 26, 12 }, -}; - static void PaintSmallScenerySupports( PaintSession& session, const SmallSceneryEntry& sceneryEntry, const SmallSceneryElement& sceneryElement, Direction direction, int32_t height, ImageId imageTemplate) @@ -138,72 +131,6 @@ static void PaintSmallSceneryBody( offset += newQuadrantOffsets[quadrant]; } -#pragma region LegacyVerification - BoundBoxXYZ boundBoxLegacy = { { 0, 0, height }, { 2, 2, 0 } }; - CoordsXYZ offsetLegacy = { 0, 0, height }; - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE)) - { - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HALF_SPACE)) - { - static constexpr CoordsXY sceneryHalfTileOffsets[] = { - { 3, 3 }, - { 3, 17 }, - { 17, 3 }, - { 3, 3 }, - }; - boundBoxLegacy.offset.x = sceneryHalfTileOffsets[direction].x; - boundBoxLegacy.offset.y = sceneryHalfTileOffsets[direction].y; - boundBoxLegacy.length.x = lengths[direction].x; - boundBoxLegacy.length.y = lengths[direction].y; - offsetLegacy.x = 3; - offsetLegacy.y = 3; - } - else - { - offsetLegacy.x = 15; - offsetLegacy.y = 15; - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_VOFFSET_CENTRE)) - { - offsetLegacy.x = 3; - offsetLegacy.y = 3; - boundBoxLegacy.length.x = 26; - boundBoxLegacy.length.y = 26; - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_NO_WALLS)) - { - offsetLegacy.x = 1; - offsetLegacy.y = 1; - boundBoxLegacy.length.x = 30; - boundBoxLegacy.length.y = 30; - } - } - boundBoxLegacy.offset.x = offset.x; - boundBoxLegacy.offset.y = offset.y; - } - } - else - { - uint8_t quadrant = (sceneryElement.GetSceneryQuadrant() + session.CurrentRotation) & 3; - // -1 to maintain compatibility with existing CSOs in context of issue #17616 - offsetLegacy.x = SceneryQuadrantOffsets[quadrant].x - 1; - offsetLegacy.y = SceneryQuadrantOffsets[quadrant].y - 1; - boundBoxLegacy.offset.x = offsetLegacy.x; - boundBoxLegacy.offset.y = offsetLegacy.y; - } - boundBoxLegacy.length.z = sceneryEntry->height - 4; - if (boundBoxLegacy.length.z > 128 || boundBoxLegacy.length.z < 0) - { - boundBoxLegacy.length.z = 128; - } - boundBoxLegacy.length.z--; - - auto boxOffsetDelta = boundBoxLegacy.offset - boundBox.offset; - auto boxLengthDelta = boundBoxLegacy.length - boundBox.length; - auto offsetDelta = offsetLegacy - offset; - assert(boxOffsetDelta.x == 0 && boxOffsetDelta.y == 0 && boxOffsetDelta.z == 0); - assert(boxLengthDelta.x == 0 && boxLengthDelta.y == 0 && boxLengthDelta.z == 0); - assert(offsetDelta.x == 0 && offsetDelta.y == 0 && offsetDelta.z == 0); -#pragma endregion - ImageIndex baseImageIndex = sceneryEntry->image + direction; if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_CAN_WITHER)) {