mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-21 05:53:02 +01:00
Merge pull request #22836 from ZehMatt/spatial-index
Refactor spatial indexing to be its own phase
This commit is contained in:
@@ -80,9 +80,9 @@ set(OPENMSX_VERSION "1.6")
|
||||
set(OPENMSX_URL "https://github.com/OpenRCT2/OpenMusic/releases/download/v${OPENMSX_VERSION}/openmusic.zip")
|
||||
set(OPENMSX_SHA1 "ba170fa6d777b309c15420f4b6eb3fa25082a9d1")
|
||||
|
||||
set(REPLAYS_VERSION "0.0.81")
|
||||
set(REPLAYS_VERSION "0.0.83")
|
||||
set(REPLAYS_URL "https://github.com/OpenRCT2/replays/releases/download/v${REPLAYS_VERSION}/replays.zip")
|
||||
set(REPLAYS_SHA1 "F698526D1D2DABEF80F350A6363223D67DBC96B1")
|
||||
set(REPLAYS_SHA1 "FFC98C36AFEC68DC6A48E863413D4E2364A202B3")
|
||||
|
||||
option(FORCE32 "Force 32-bit build. It will add `-m32` to compiler flags.")
|
||||
option(WITH_TESTS "Build tests")
|
||||
|
||||
@@ -51,8 +51,8 @@
|
||||
<OpenSFXSha1>b1b1f1b241d2cbff63a1889c4dc5a09bdf769bfb</OpenSFXSha1>
|
||||
<OpenMSXUrl>https://github.com/OpenRCT2/OpenMusic/releases/download/v1.6/openmusic.zip</OpenMSXUrl>
|
||||
<OpenMSXSha1>ba170fa6d777b309c15420f4b6eb3fa25082a9d1</OpenMSXSha1>
|
||||
<ReplaysUrl>https://github.com/OpenRCT2/replays/releases/download/v0.0.81/replays.zip</ReplaysUrl>
|
||||
<ReplaysSha1>F698526D1D2DABEF80F350A6363223D67DBC96B1</ReplaysSha1>
|
||||
<ReplaysUrl>https://github.com/OpenRCT2/replays/releases/download/v0.0.83/replays.zip</ReplaysUrl>
|
||||
<ReplaysSha1>FFC98C36AFEC68DC6A48E863413D4E2364A202B3</ReplaysSha1>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -332,6 +332,8 @@ namespace OpenRCT2
|
||||
// Update windows
|
||||
// WindowDispatchUpdateAll();
|
||||
|
||||
UpdateEntitiesSpatialIndex();
|
||||
|
||||
// Start autosave timer after update
|
||||
if (gLastAutoSaveUpdate == kAutosavePause)
|
||||
{
|
||||
|
||||
@@ -20,18 +20,6 @@ template<> bool EntityBase::Is<EntityBase>() const
|
||||
return true;
|
||||
}
|
||||
|
||||
CoordsXYZ EntityBase::GetLocation() const
|
||||
{
|
||||
return { x, y, z };
|
||||
}
|
||||
|
||||
void EntityBase::SetLocation(const CoordsXYZ& newLocation)
|
||||
{
|
||||
x = newLocation.x;
|
||||
y = newLocation.y;
|
||||
z = newLocation.z;
|
||||
}
|
||||
|
||||
void EntityBase::Invalidate()
|
||||
{
|
||||
if (x == kLocationNull)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -46,28 +46,36 @@ static std::vector<EntityId> _freeIdList;
|
||||
|
||||
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 const uint32_t kSpatialIndexSize = (kMaximumMapSizeTechnical * kMaximumMapSizeTechnical) + 1;
|
||||
static constexpr uint32_t kSpatialIndexNullBucket = kSpatialIndexSize - 1;
|
||||
|
||||
static std::array<std::vector<EntityId>, SPATIAL_INDEX_SIZE> gEntitySpatialIndex;
|
||||
static constexpr uint32_t kInvalidSpatialIndex = 0xFFFFFFFFu;
|
||||
static constexpr uint32_t kSpatialIndexDirtyMask = 1u << 31;
|
||||
|
||||
static std::array<std::vector<EntityId>, kSpatialIndexSize> 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;
|
||||
return kSpatialIndexNullBucket;
|
||||
|
||||
// NOTE: The input coordinate is rotated and can have negative components.
|
||||
const auto tileX = std::abs(loc.x) / kCoordsXYStep;
|
||||
const auto tileY = std::abs(loc.y) / kCoordsXYStep;
|
||||
|
||||
if (tileX >= kMaximumMapSizeTechnical || tileY >= kMaximumMapSizeTechnical)
|
||||
return SPATIAL_INDEX_LOCATION_NULL;
|
||||
return kSpatialIndexNullBucket;
|
||||
|
||||
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 +129,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 +212,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 +320,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 +414,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 +438,43 @@ 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;
|
||||
for (auto& entityList : gEntityLists)
|
||||
{
|
||||
for (auto& entityId : entityList)
|
||||
{
|
||||
auto* entity = GetEntity(entityId);
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CoordsXYZ EntityBase::GetLocation() const
|
||||
{
|
||||
return { x, y, z };
|
||||
}
|
||||
|
||||
void EntityBase::SetLocation(const CoordsXYZ& newLocation)
|
||||
{
|
||||
x = newLocation.x;
|
||||
y = newLocation.y;
|
||||
z = newLocation.z;
|
||||
SpatialIndex |= kSpatialIndexDirtyMask;
|
||||
}
|
||||
|
||||
void EntityBase::MoveTo(const CoordsXYZ& newLocation)
|
||||
@@ -452,13 +491,9 @@ void EntityBase::MoveTo(const CoordsXYZ& newLocation)
|
||||
loc.x = kLocationNull;
|
||||
}
|
||||
|
||||
EntitySpatialMove(this, loc);
|
||||
|
||||
if (loc.x == kLocationNull)
|
||||
{
|
||||
x = loc.x;
|
||||
y = loc.y;
|
||||
z = loc.z;
|
||||
SetLocation(loc);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -97,14 +97,12 @@ void EntityTweener::Tween(float alpha)
|
||||
if (posA == posB)
|
||||
continue;
|
||||
|
||||
ent->Invalidate();
|
||||
EntitySetCoordinates(
|
||||
{ static_cast<int32_t>(std::round(posB.x * alpha + posA.x * inv)),
|
||||
static_cast<int32_t>(std::round(posB.y * alpha + posA.y * inv)),
|
||||
static_cast<int32_t>(std::round(posB.z * alpha + posA.z * inv)) },
|
||||
ent);
|
||||
ent->Invalidate();
|
||||
ent->MoveTo({ static_cast<int32_t>(std::round(posB.x * alpha + posA.x * inv)),
|
||||
static_cast<int32_t>(std::round(posB.y * alpha + posA.y * inv)),
|
||||
static_cast<int32_t>(std::round(posB.z * alpha + posA.z * inv)) });
|
||||
}
|
||||
|
||||
UpdateEntitiesSpatialIndex();
|
||||
}
|
||||
|
||||
void EntityTweener::Restore()
|
||||
|
||||
@@ -49,7 +49,7 @@ using namespace OpenRCT2;
|
||||
// It is used for making sure only compatible builds get connected, even within
|
||||
// single OpenRCT2 version.
|
||||
|
||||
constexpr uint8_t kNetworkStreamVersion = 6;
|
||||
constexpr uint8_t kNetworkStreamVersion = 7;
|
||||
|
||||
const std::string kNetworkStreamID = std::string(OPENRCT2_VERSION) + "-" + std::to_string(kNetworkStreamVersion);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user