mirror of https://github.com/OpenRCT2/OpenRCT2.git
Improve performance of tweening entities between ticks
This commit is contained in:
parent
48c8e66d7b
commit
c442ef6207
|
@ -624,7 +624,7 @@ namespace OpenRCT2
|
|||
gFirstTimeSaving = true;
|
||||
game_fix_save_vars();
|
||||
AutoCreateMapAnimations();
|
||||
sprite_position_tween_reset();
|
||||
EntityTweener::Get().Reset();
|
||||
gScreenAge = 0;
|
||||
gLastAutoSaveUpdate = AUTOSAVE_PAUSE;
|
||||
|
||||
|
@ -1003,10 +1003,12 @@ namespace OpenRCT2
|
|||
{
|
||||
uint32_t currentTick = platform_get_ticks();
|
||||
|
||||
auto& tweener = EntityTweener::Get();
|
||||
|
||||
bool draw = !_isWindowMinimised && !gOpenRCT2Headless;
|
||||
if (_lastTick == 0)
|
||||
{
|
||||
sprite_position_tween_reset();
|
||||
tweener.Reset();
|
||||
_lastTick = currentTick;
|
||||
}
|
||||
|
||||
|
@ -1021,7 +1023,7 @@ namespace OpenRCT2
|
|||
{
|
||||
// Get the original position of each sprite
|
||||
if (draw)
|
||||
sprite_position_tween_store_a();
|
||||
tweener.PreTick();
|
||||
|
||||
Update();
|
||||
|
||||
|
@ -1029,20 +1031,18 @@ namespace OpenRCT2
|
|||
|
||||
// Get the next position of each sprite
|
||||
if (draw)
|
||||
sprite_position_tween_store_b();
|
||||
tweener.PostTick();
|
||||
}
|
||||
|
||||
if (draw)
|
||||
{
|
||||
const float alpha = std::min(static_cast<float>(_accumulator) / GAME_UPDATE_TIME_MS, 1.0f);
|
||||
sprite_position_tween_all(alpha);
|
||||
tweener.Tween(alpha);
|
||||
|
||||
_drawingEngine->BeginDraw();
|
||||
_painter->Paint(*_drawingEngine);
|
||||
_drawingEngine->EndDraw();
|
||||
|
||||
sprite_position_tween_restore();
|
||||
|
||||
// Note: It's important to call UpdateWindows after restoring the sprite positions, not in between,
|
||||
// otherwise the window updates to positions of sprites could be reverted.
|
||||
// This can be observed when changing ride settings using the mouse wheel that removes all
|
||||
|
|
|
@ -530,7 +530,7 @@ namespace OpenRCT2
|
|||
|
||||
importer->Import();
|
||||
|
||||
sprite_position_tween_reset();
|
||||
EntityTweener::Get().Reset();
|
||||
|
||||
// Load all map global variables.
|
||||
DataSerialiser parkParamsDs(false, data.parkParams);
|
||||
|
|
|
@ -2718,7 +2718,7 @@ bool NetworkBase::LoadMap(IStream* stream)
|
|||
objManager.LoadObjects(loadResult.RequiredObjects.data(), loadResult.RequiredObjects.size());
|
||||
importer->Import();
|
||||
|
||||
sprite_position_tween_reset();
|
||||
EntityTweener::Get().Reset();
|
||||
AutoCreateMapAnimations();
|
||||
|
||||
// Read checksum
|
||||
|
|
|
@ -766,7 +766,7 @@ std::unique_ptr<GameActions::Result> Peep::Place(const TileCoordsXYZ& location,
|
|||
ActionSpriteImageOffset = 0;
|
||||
ActionSpriteType = PeepActionSpriteType::None;
|
||||
PathCheckOptimisation = 0;
|
||||
sprite_position_tween_reset();
|
||||
EntityTweener::Get().Reset();
|
||||
|
||||
if (AssignedPeepType == PeepType::Guest)
|
||||
{
|
||||
|
|
|
@ -1709,7 +1709,7 @@ void load_from_sv6(const char* path)
|
|||
s6Importer->Import();
|
||||
game_fix_save_vars();
|
||||
AutoCreateMapAnimations();
|
||||
sprite_position_tween_reset();
|
||||
EntityTweener::Get().Reset();
|
||||
gScreenAge = 0;
|
||||
gLastAutoSaveUpdate = AUTOSAVE_PAUSE;
|
||||
}
|
||||
|
@ -1749,7 +1749,7 @@ void load_from_sc6(const char* path)
|
|||
s6Importer->Import();
|
||||
game_fix_save_vars();
|
||||
AutoCreateMapAnimations();
|
||||
sprite_position_tween_reset();
|
||||
EntityTweener::Get().Reset();
|
||||
return;
|
||||
}
|
||||
catch (const ObjectLoadException& loadError)
|
||||
|
|
|
@ -46,9 +46,6 @@ const rct_string_id litterNames[12] = { STR_LITTER_VOMIT,
|
|||
STR_SHOP_ITEM_SINGULAR_EMPTY_JUICE_CUP,
|
||||
STR_SHOP_ITEM_SINGULAR_EMPTY_BOWL_BLUE };
|
||||
|
||||
static CoordsXYZ _spritelocations1[MAX_SPRITES];
|
||||
static CoordsXYZ _spritelocations2[MAX_SPRITES];
|
||||
|
||||
static size_t GetSpatialIndexOffset(int32_t x, int32_t y);
|
||||
static void move_sprite_to_list(SpriteBase* sprite, EntityListId newListIndex);
|
||||
|
||||
|
@ -928,106 +925,106 @@ uint16_t remove_floating_sprites()
|
|||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether it's worth tweening a sprite or not when frame smoothing is on.
|
||||
*/
|
||||
static bool sprite_should_tween(SpriteBase* sprite)
|
||||
static bool IsValidEntity(SpriteBase* ent)
|
||||
{
|
||||
switch (sprite->sprite_identifier)
|
||||
{
|
||||
case SpriteIdentifier::Peep:
|
||||
case SpriteIdentifier::Vehicle:
|
||||
return true;
|
||||
case SpriteIdentifier::Misc:
|
||||
case SpriteIdentifier::Litter:
|
||||
case SpriteIdentifier::Null:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
if (ent->sprite_identifier == SpriteIdentifier::Null)
|
||||
return false;
|
||||
if (ent->sprite_index == SPRITE_INDEX_NULL)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void store_sprite_locations(CoordsXYZ* sprite_locations)
|
||||
void EntityTweener::PopulateEntities(EntityListId id)
|
||||
{
|
||||
for (uint16_t i = 0; i < MAX_SPRITES; i++)
|
||||
for (auto ent : EntityList(id))
|
||||
{
|
||||
// skip going through `get_sprite` to not get stalled on assert,
|
||||
// this can get very expensive for busy parks with uncap FPS option on
|
||||
const rct_sprite* sprite = &_spriteList[i];
|
||||
sprite_locations[i].x = sprite->generic.x;
|
||||
sprite_locations[i].y = sprite->generic.y;
|
||||
sprite_locations[i].z = sprite->generic.z;
|
||||
_ents.push_back(&(*ent));
|
||||
_prePos.push_back({ ent->x, ent->y, ent->z });
|
||||
}
|
||||
}
|
||||
|
||||
void sprite_position_tween_store_a()
|
||||
void EntityTweener::PreTick()
|
||||
{
|
||||
store_sprite_locations(_spritelocations1);
|
||||
Restore();
|
||||
Reset();
|
||||
PopulateEntities(EntityListId::Peep);
|
||||
PopulateEntities(EntityListId::Vehicle);
|
||||
PopulateEntities(EntityListId::TrainHead);
|
||||
}
|
||||
|
||||
void sprite_position_tween_store_b()
|
||||
void EntityTweener::PostTick()
|
||||
{
|
||||
store_sprite_locations(_spritelocations2);
|
||||
for (auto ent : _ents)
|
||||
{
|
||||
if (!IsValidEntity(ent))
|
||||
{
|
||||
// Sprite was removed, add a dummy position to keep the index aligned.
|
||||
_postPos.push_back({});
|
||||
}
|
||||
else
|
||||
{
|
||||
_postPos.push_back({ ent->x, ent->y, ent->z });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sprite_position_tween_all(float alpha)
|
||||
void EntityTweener::Tween(float alpha)
|
||||
{
|
||||
const float inv = (1.0f - alpha);
|
||||
|
||||
for (uint16_t i = 0; i < MAX_SPRITES; i++)
|
||||
for (size_t i = 0; i < _ents.size(); ++i)
|
||||
{
|
||||
auto* sprite = GetEntity(i);
|
||||
if (sprite != nullptr && sprite_should_tween(sprite))
|
||||
{
|
||||
auto posA = _spritelocations1[i];
|
||||
auto posB = _spritelocations2[i];
|
||||
if (posA == posB)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
sprite_set_coordinates(
|
||||
{ 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)) },
|
||||
sprite);
|
||||
sprite->Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the real positions of the sprites so they aren't left at the mid-tween positions
|
||||
*/
|
||||
void sprite_position_tween_restore()
|
||||
{
|
||||
for (uint16_t i = 0; i < MAX_SPRITES; i++)
|
||||
{
|
||||
auto* sprite = GetEntity(i);
|
||||
if (sprite != nullptr && sprite_should_tween(sprite))
|
||||
{
|
||||
sprite->Invalidate();
|
||||
|
||||
auto pos = _spritelocations2[i];
|
||||
sprite_set_coordinates(pos, sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sprite_position_tween_reset()
|
||||
{
|
||||
for (uint16_t i = 0; i < MAX_SPRITES; i++)
|
||||
{
|
||||
auto* sprite = GetEntity(i);
|
||||
if (sprite == nullptr)
|
||||
auto* ent = _ents[i];
|
||||
if (!IsValidEntity(ent))
|
||||
{
|
||||
// Sprite was removed, leave untouched.
|
||||
continue;
|
||||
}
|
||||
|
||||
_spritelocations1[i].x = _spritelocations2[i].x = sprite->x;
|
||||
_spritelocations1[i].y = _spritelocations2[i].y = sprite->y;
|
||||
_spritelocations1[i].z = _spritelocations2[i].z = sprite->z;
|
||||
auto& posA = _prePos[i];
|
||||
auto& posB = _postPos[i];
|
||||
|
||||
if (posA == posB)
|
||||
continue;
|
||||
|
||||
sprite_set_coordinates(
|
||||
{ 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();
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTweener::Restore()
|
||||
{
|
||||
for (size_t i = 0; i < _ents.size(); ++i)
|
||||
{
|
||||
auto* ent = _ents[i];
|
||||
if (!IsValidEntity(ent))
|
||||
{
|
||||
// Sprite was removed, leave untouched.
|
||||
continue;
|
||||
}
|
||||
|
||||
sprite_set_coordinates(_postPos[i], ent);
|
||||
ent->Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTweener::Reset()
|
||||
{
|
||||
_ents.clear();
|
||||
_prePos.clear();
|
||||
_postPos.clear();
|
||||
}
|
||||
|
||||
static EntityTweener tweener;
|
||||
|
||||
EntityTweener& EntityTweener::Get()
|
||||
{
|
||||
return tweener;
|
||||
}
|
||||
|
||||
void sprite_set_flashing(SpriteBase* sprite, bool flashing)
|
||||
{
|
||||
assert(sprite->sprite_index < MAX_SPRITES);
|
||||
|
|
|
@ -246,11 +246,6 @@ uint16_t remove_floating_sprites();
|
|||
void sprite_misc_explosion_cloud_create(const CoordsXYZ& cloudPos);
|
||||
void sprite_misc_explosion_flare_create(const CoordsXYZ& flarePos);
|
||||
uint16_t sprite_get_first_in_quadrant(const CoordsXY& spritePos);
|
||||
void sprite_position_tween_store_a();
|
||||
void sprite_position_tween_store_b();
|
||||
void sprite_position_tween_all(float nudge);
|
||||
void sprite_position_tween_restore();
|
||||
void sprite_position_tween_reset();
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// Balloon
|
||||
|
@ -378,4 +373,23 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class EntityTweener
|
||||
{
|
||||
std::vector<SpriteBase*> _ents;
|
||||
std::vector<CoordsXYZ> _prePos;
|
||||
std::vector<CoordsXYZ> _postPos;
|
||||
|
||||
private:
|
||||
void PopulateEntities(EntityListId id);
|
||||
|
||||
public:
|
||||
static EntityTweener& Get();
|
||||
|
||||
void PreTick();
|
||||
void PostTick();
|
||||
void Tween(float alpha);
|
||||
void Restore();
|
||||
void Reset();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -54,7 +54,7 @@ static std::unique_ptr<IContext> localStartGame(const std::string& parkPath)
|
|||
scenery_set_default_placement_configuration();
|
||||
load_palette();
|
||||
map_reorganise_elements();
|
||||
sprite_position_tween_reset();
|
||||
EntityTweener::Get().Reset();
|
||||
AutoCreateMapAnimations();
|
||||
fix_invalid_vehicle_sprite_sizes();
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ static void GameInit(bool retainSpatialIndices)
|
|||
scenery_set_default_placement_configuration();
|
||||
load_palette();
|
||||
map_reorganise_elements();
|
||||
sprite_position_tween_reset();
|
||||
EntityTweener::Get().Reset();
|
||||
AutoCreateMapAnimations();
|
||||
fix_invalid_vehicle_sprite_sizes();
|
||||
|
||||
|
|
Loading…
Reference in New Issue