From 2666a9937e1dffd3707842bfc8b64ec3b19a78e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Fri, 27 Sep 2024 11:34:05 +0300 Subject: [PATCH] Refactor the spatial indexing into a separate phase --- src/openrct2/Context.cpp | 3 ++ src/openrct2/entity/EntityBase.h | 1 + src/openrct2/entity/EntityRegistry.cpp | 58 +++++++++++++++++++------- src/openrct2/entity/EntityRegistry.h | 1 + 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index 9eff422afe..b1a46bb472 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -1322,6 +1322,7 @@ namespace OpenRCT2 if (ShouldDraw()) { + UpdateEntitiesSpatialIndex(); Draw(); } } @@ -1358,6 +1359,8 @@ namespace OpenRCT2 const float alpha = std::min(_ticksAccumulator / kGameUpdateTimeMS, 1.0f); tweener.Tween(alpha); + UpdateEntitiesSpatialIndex(); + Draw(); } } diff --git a/src/openrct2/entity/EntityBase.h b/src/openrct2/entity/EntityBase.h index 9c43108192..bee06c1021 100644 --- a/src/openrct2/entity/EntityBase.h +++ b/src/openrct2/entity/EntityBase.h @@ -44,6 +44,7 @@ struct EntityBase EntitySpriteData SpriteData; // Used as direction or rotation depending on the entity. uint8_t Orientation; + uint32_t SpatialIndex; /** * Moves a sprite to a new location, invalidates the current position if valid diff --git a/src/openrct2/entity/EntityRegistry.cpp b/src/openrct2/entity/EntityRegistry.cpp index 18ffc5b881..38303df7b8 100644 --- a/src/openrct2/entity/EntityRegistry.cpp +++ b/src/openrct2/entity/EntityRegistry.cpp @@ -48,12 +48,14 @@ static bool _entityFlashingList[MAX_ENTITIES]; constexpr const uint32_t SPATIAL_INDEX_SIZE = (kMaximumMapSizeTechnical * kMaximumMapSizeTechnical) + 1; constexpr uint32_t SPATIAL_INDEX_LOCATION_NULL = SPATIAL_INDEX_SIZE - 1; +static constexpr uint32_t kInvalidSpatialIndex = 0xFFFFFFFFu; +static constexpr uint32_t kSpatialIndexDirtyMask = 1u << 31; static std::array, SPATIAL_INDEX_SIZE> gEntitySpatialIndex; static void FreeEntity(EntityBase& entity); -static constexpr size_t GetSpatialIndexOffset(const CoordsXY& loc) +static constexpr uint32_t ComputeSpatialIndex(const CoordsXY& loc) { if (loc.IsNull()) return SPATIAL_INDEX_LOCATION_NULL; @@ -68,6 +70,11 @@ static constexpr size_t GetSpatialIndexOffset(const CoordsXY& loc) return tileX * kMaximumMapSizeTechnical + tileY; } +static constexpr uint32_t GetSpatialIndex(EntityBase* entity) +{ + return entity->SpatialIndex & ~kSpatialIndexDirtyMask; +} + constexpr bool EntityTypeIsMiscEntity(const EntityType type) { switch (type) @@ -121,7 +128,7 @@ EntityBase* GetEntity(EntityId entityIndex) const std::vector& GetEntityTileList(const CoordsXY& spritePos) { - return gEntitySpatialIndex[GetSpatialIndexOffset(spritePos)]; + return gEntitySpatialIndex[ComputeSpatialIndex(spritePos)]; } static void ResetEntityLists() @@ -204,10 +211,10 @@ void ResetEntitySpatialIndices() } for (EntityId::UnderlyingType i = 0; i < MAX_ENTITIES; i++) { - auto* spr = GetEntity(EntityId::FromUnderlying(i)); - if (spr != nullptr && spr->Type != EntityType::Null) + auto* entity = GetEntity(EntityId::FromUnderlying(i)); + if (entity != nullptr && entity->Type != EntityType::Null) { - EntitySpatialInsert(spr, { spr->x, spr->y }); + EntitySpatialInsert(entity, { entity->x, entity->y }); } } } @@ -312,6 +319,7 @@ static void PrepareNewEntity(EntityBase* base, const EntityType type) base->SpriteData.HeightMin = 0x14; base->SpriteData.HeightMax = 0x8; base->SpriteData.SpriteRect = {}; + base->SpatialIndex = kInvalidSpatialIndex; EntitySpatialInsert(base, { kLocationNull, 0 }); } @@ -405,15 +413,19 @@ void UpdateMoneyEffect() // Performs a search to ensure that insert keeps next_in_quadrant in sprite_index order static void EntitySpatialInsert(EntityBase* entity, const CoordsXY& newLoc) { - size_t newIndex = GetSpatialIndexOffset(newLoc); + const auto newIndex = ComputeSpatialIndex(newLoc); + auto& spatialVector = gEntitySpatialIndex[newIndex]; auto index = std::lower_bound(std::begin(spatialVector), std::end(spatialVector), entity->Id); spatialVector.insert(index, entity->Id); + + entity->SpatialIndex = newIndex; } static void EntitySpatialRemove(EntityBase* entity) { - size_t currentIndex = GetSpatialIndexOffset({ entity->x, entity->y }); + const auto currentIndex = GetSpatialIndex(entity); + auto& spatialVector = gEntitySpatialIndex[currentIndex]; auto index = BinaryFind(std::begin(spatialVector), std::end(spatialVector), entity->Id); if (index != std::end(spatialVector)) @@ -425,17 +437,28 @@ static void EntitySpatialRemove(EntityBase* entity) LOG_WARNING("Bad sprite spatial index. Rebuilding the spatial index..."); ResetEntitySpatialIndices(); } + + entity->SpatialIndex = kInvalidSpatialIndex; } -static void EntitySpatialMove(EntityBase* entity, const CoordsXY& newLoc) +void UpdateEntitiesSpatialIndex() { - size_t newIndex = GetSpatialIndexOffset(newLoc); - size_t currentIndex = GetSpatialIndexOffset({ entity->x, entity->y }); - if (newIndex == currentIndex) - return; + // TODO: This is not optimal, we should only iterate active entities. + for (EntityId::UnderlyingType i = 0; i < MAX_ENTITIES; i++) + { + auto* entity = GetEntity(EntityId::FromUnderlying(i)); + if (entity == nullptr || entity->Type == EntityType::Null) + continue; - EntitySpatialRemove(entity); - EntitySpatialInsert(entity, newLoc); + if (entity->SpatialIndex & kSpatialIndexDirtyMask) + { + if (entity->SpatialIndex != kInvalidSpatialIndex) + { + EntitySpatialRemove(entity); + } + EntitySpatialInsert(entity, { entity->x, entity->y }); + } + } } void EntityBase::MoveTo(const CoordsXYZ& newLocation) @@ -452,7 +475,12 @@ void EntityBase::MoveTo(const CoordsXYZ& newLocation) loc.x = kLocationNull; } - EntitySpatialMove(this, loc); + // EntitySpatialMove(this, loc); + const auto newSpatialIndex = ComputeSpatialIndex(loc); + if (newSpatialIndex != GetSpatialIndex(this)) + { + SpatialIndex |= kSpatialIndexDirtyMask; + } if (loc.x == kLocationNull) { diff --git a/src/openrct2/entity/EntityRegistry.h b/src/openrct2/entity/EntityRegistry.h index 68affac7e7..7fea52cd94 100644 --- a/src/openrct2/entity/EntityRegistry.h +++ b/src/openrct2/entity/EntityRegistry.h @@ -66,6 +66,7 @@ void UpdateMoneyEffect(); void EntitySetCoordinates(const CoordsXYZ& entityPos, EntityBase* entity); void EntityRemove(EntityBase* entity); uint16_t RemoveFloatingEntities(); +void UpdateEntitiesSpatialIndex(); #pragma pack(push, 1) struct EntitiesChecksum