1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-21 22:13:07 +01:00

Refactor the spatial indexing into a separate phase

This commit is contained in:
ζeh Matt
2024-09-27 11:34:05 +03:00
parent 2c9509f988
commit 2666a9937e
4 changed files with 48 additions and 15 deletions

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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<std::vector<EntityId>, 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<EntityId>& 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)
{

View File

@@ -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