mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-22 15:23:01 +01:00
Don't tween entities that are not visible on screen or when zoomed out
This commit is contained in:
@@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#include "../entity/Guest.h"
|
#include "../entity/Guest.h"
|
||||||
#include "../entity/Staff.h"
|
#include "../entity/Staff.h"
|
||||||
|
#include "../interface/Viewport.h"
|
||||||
|
#include "../interface/Window.h"
|
||||||
#include "../ride/Vehicle.h"
|
#include "../ride/Vehicle.h"
|
||||||
#include "EntityList.h"
|
#include "EntityList.h"
|
||||||
#include "EntityRegistry.h"
|
#include "EntityRegistry.h"
|
||||||
@@ -17,114 +19,170 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
void EntityTweener::AddEntity(EntityBase* entity)
|
namespace OpenRCT2
|
||||||
{
|
{
|
||||||
Entities.push_back(entity);
|
static inline ViewportList GetUnzoomedViewports() noexcept
|
||||||
PrePos.emplace_back(entity->GetLocation());
|
|
||||||
}
|
|
||||||
|
|
||||||
void EntityTweener::PopulateEntities()
|
|
||||||
{
|
|
||||||
for (auto ent : EntityList<Guest>())
|
|
||||||
{
|
{
|
||||||
AddEntity(ent);
|
ViewportList viewports;
|
||||||
|
WindowVisitEach([&](WindowBase* w) {
|
||||||
|
if (auto* vp = WindowGetViewport(w); vp != nullptr)
|
||||||
|
{
|
||||||
|
if (!vp->isVisible)
|
||||||
|
{
|
||||||
|
// Ignore viewports that are not visible.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vp->zoom > ZoomLevel{ 0 })
|
||||||
|
{
|
||||||
|
// Ignore viewports that are zoomed out, interpolation wouldn't have much of an effect
|
||||||
|
// due to the loss of detail.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
viewports.push_back(vp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return viewports;
|
||||||
}
|
}
|
||||||
for (auto ent : EntityList<Staff>())
|
|
||||||
{
|
|
||||||
AddEntity(ent);
|
|
||||||
}
|
|
||||||
for (auto ent : EntityList<Vehicle>())
|
|
||||||
{
|
|
||||||
AddEntity(ent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EntityTweener::PreTick()
|
static inline bool IsEntityVisible(const ViewportList& vpList, const EntityBase* entity) noexcept
|
||||||
{
|
|
||||||
Restore();
|
|
||||||
Reset();
|
|
||||||
PopulateEntities();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EntityTweener::PostTick()
|
|
||||||
{
|
|
||||||
for (auto* ent : Entities)
|
|
||||||
{
|
{
|
||||||
if (ent == nullptr)
|
const auto worldLoc = entity->GetLocation();
|
||||||
|
|
||||||
|
for (const auto* vp : vpList)
|
||||||
{
|
{
|
||||||
// Sprite was removed, add a dummy position to keep the index aligned.
|
const auto screenPos = Translate3DTo2DWithZ(vp->rotation, worldLoc);
|
||||||
PostPos.emplace_back(0, 0, 0);
|
if (vp->Contains(screenPos))
|
||||||
|
{
|
||||||
|
// Entity is visible in at least one viewport, tween.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityTweener::AddEntity(const ViewportList& vpList, EntityBase* entity)
|
||||||
|
{
|
||||||
|
if (!IsEntityVisible(vpList, entity))
|
||||||
{
|
{
|
||||||
PostPos.emplace_back(ent->GetLocation());
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entities.push_back(entity);
|
||||||
|
PrePos.emplace_back(entity->GetLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityTweener::PopulateEntities()
|
||||||
|
{
|
||||||
|
const auto vpList = GetUnzoomedViewports();
|
||||||
|
if (vpList.empty())
|
||||||
|
{
|
||||||
|
// No viewports that fit the criteria, bail.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto ent : EntityList<Guest>())
|
||||||
|
{
|
||||||
|
AddEntity(vpList, ent);
|
||||||
|
}
|
||||||
|
for (auto ent : EntityList<Staff>())
|
||||||
|
{
|
||||||
|
AddEntity(vpList, ent);
|
||||||
|
}
|
||||||
|
for (auto ent : EntityList<Vehicle>())
|
||||||
|
{
|
||||||
|
AddEntity(vpList, ent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static bool CanTweenEntity(EntityBase* ent)
|
void EntityTweener::PreTick()
|
||||||
{
|
|
||||||
if (ent->Is<Guest>() || ent->Is<Staff>() || ent->Is<Vehicle>())
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EntityTweener::RemoveEntity(EntityBase* entity)
|
|
||||||
{
|
|
||||||
if (!CanTweenEntity(entity))
|
|
||||||
{
|
{
|
||||||
// Only peeps and vehicles are tweened, bail if type is incorrect.
|
Restore();
|
||||||
return;
|
Reset();
|
||||||
|
PopulateEntities();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto it = std::find(Entities.begin(), Entities.end(), entity);
|
void EntityTweener::PostTick()
|
||||||
if (it != Entities.end())
|
|
||||||
*it = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EntityTweener::Tween(float alpha)
|
|
||||||
{
|
|
||||||
const float inv = (1.0f - alpha);
|
|
||||||
for (size_t i = 0; i < Entities.size(); ++i)
|
|
||||||
{
|
{
|
||||||
auto* ent = Entities[i];
|
for (auto* ent : Entities)
|
||||||
if (ent == nullptr)
|
{
|
||||||
continue;
|
if (ent == nullptr)
|
||||||
|
{
|
||||||
auto& posA = PrePos[i];
|
// Sprite was removed, add a dummy position to keep the index aligned.
|
||||||
auto& posB = PostPos[i];
|
PostPos.emplace_back(0, 0, 0);
|
||||||
|
}
|
||||||
if (posA == posB)
|
else
|
||||||
continue;
|
{
|
||||||
|
PostPos.emplace_back(ent->GetLocation());
|
||||||
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)) });
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void EntityTweener::Restore()
|
static bool CanTweenEntity(EntityBase* ent)
|
||||||
{
|
|
||||||
for (size_t i = 0; i < Entities.size(); ++i)
|
|
||||||
{
|
{
|
||||||
auto* ent = Entities[i];
|
if (ent->Is<Guest>() || ent->Is<Staff>() || ent->Is<Vehicle>())
|
||||||
if (ent == nullptr || PrePos[i] == PostPos[i])
|
return true;
|
||||||
continue;
|
return false;
|
||||||
|
|
||||||
ent->MoveTo(PostPos[i]);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void EntityTweener::Reset()
|
void EntityTweener::RemoveEntity(EntityBase* entity)
|
||||||
{
|
{
|
||||||
Entities.clear();
|
if (!CanTweenEntity(entity))
|
||||||
PrePos.clear();
|
{
|
||||||
PostPos.clear();
|
// Only peeps and vehicles are tweened, bail if type is incorrect.
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
static EntityTweener tweener;
|
auto it = std::find(Entities.begin(), Entities.end(), entity);
|
||||||
|
if (it != Entities.end())
|
||||||
|
*it = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
EntityTweener& EntityTweener::Get()
|
void EntityTweener::Tween(float alpha)
|
||||||
{
|
{
|
||||||
return tweener;
|
const float inv = (1.0f - alpha);
|
||||||
}
|
for (size_t i = 0; i < Entities.size(); ++i)
|
||||||
|
{
|
||||||
|
auto* ent = Entities[i];
|
||||||
|
if (ent == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto& posA = PrePos[i];
|
||||||
|
auto& posB = PostPos[i];
|
||||||
|
|
||||||
|
if (posA == posB)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
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)) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityTweener::Restore()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < Entities.size(); ++i)
|
||||||
|
{
|
||||||
|
auto* ent = Entities[i];
|
||||||
|
if (ent == nullptr || PrePos[i] == PostPos[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ent->MoveTo(PostPos[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityTweener::Reset()
|
||||||
|
{
|
||||||
|
Entities.clear();
|
||||||
|
PrePos.clear();
|
||||||
|
PostPos.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
static EntityTweener tweener;
|
||||||
|
|
||||||
|
EntityTweener& EntityTweener::Get()
|
||||||
|
{
|
||||||
|
return tweener;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace OpenRCT2
|
||||||
|
|||||||
@@ -9,27 +9,38 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "../interface/Window.h"
|
||||||
#include "EntityBase.h"
|
#include "EntityBase.h"
|
||||||
|
|
||||||
|
#include <sfl/static_vector.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class EntityTweener
|
namespace OpenRCT2
|
||||||
{
|
{
|
||||||
std::vector<EntityBase*> Entities;
|
struct Viewport;
|
||||||
std::vector<CoordsXYZ> PrePos;
|
|
||||||
std::vector<CoordsXYZ> PostPos;
|
|
||||||
|
|
||||||
private:
|
// TODO: Move this to somewhere else, currently filters also by zoom.
|
||||||
void PopulateEntities();
|
using ViewportList = sfl::static_vector<Viewport*, kWindowLimitMax>;
|
||||||
void AddEntity(EntityBase* entity);
|
|
||||||
|
|
||||||
public:
|
class EntityTweener
|
||||||
static EntityTweener& Get();
|
{
|
||||||
|
std::vector<EntityBase*> Entities;
|
||||||
|
std::vector<CoordsXYZ> PrePos;
|
||||||
|
std::vector<CoordsXYZ> PostPos;
|
||||||
|
|
||||||
void PreTick();
|
private:
|
||||||
void PostTick();
|
void PopulateEntities();
|
||||||
void RemoveEntity(EntityBase* entity);
|
void AddEntity(const ViewportList& vp, EntityBase* entity);
|
||||||
void Tween(float alpha);
|
|
||||||
void Restore();
|
public:
|
||||||
void Reset();
|
static EntityTweener& Get();
|
||||||
};
|
|
||||||
|
void PreTick();
|
||||||
|
void PostTick();
|
||||||
|
void RemoveEntity(EntityBase* entity);
|
||||||
|
void Tween(float alpha);
|
||||||
|
void Restore();
|
||||||
|
void Reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace OpenRCT2
|
||||||
|
|||||||
Reference in New Issue
Block a user