Cogs.Core
ScreenSizeSystem.cpp
1#include "ScreenSizeSystem.h"
2
3#include "Context.h"
4
5#include "Components/Core/SceneComponent.h"
6
7#include "Services/Variables.h"
8
9#include "Systems/Core/CameraSystem.h"
10#include "Systems/Core/TransformSystem.h"
11#include "Systems/Core/RenderSystem.h"
12
13#include "Scene/GetBounds.h"
14
15#include "Utilities/Parallel.h"
16
17#include "Foundation/Logging/Logger.h"
18
19using namespace Cogs::Core;
20
21namespace
22{
23 Cogs::Logging::Log logger = Cogs::Logging::getLogger("ScreenSizeSystem");
24
25 template<typename FunT>
26 void ForEachEntityPreOrder(Cogs::ComponentModel::Entity* entity, FunT f)
27 {
28 f(entity);
29
30 auto sceneComponent = entity->getComponent<SceneComponent>();
31 if (sceneComponent != nullptr && sceneComponent->children.size() > 0)
32 {
33 for (auto & child : sceneComponent->children)
34 {
35 ForEachEntityPreOrder(child.get(), f);
36 }
37 }
38 }
39
40 void PredictWorldBoundingBoxes(Context* context, bool workParallel, ScreenSizeComponent const & component, ScreenSizeData& data)
41 {
42 auto rootTransform = component.getComponent<TransformComponent>();
43 auto rootLocalToWorldInverse = glm::inverse(context->transformSystem->getLocalToWorld(rootTransform));
44 auto parentLocalToWorld = rootTransform->parent
45 ? context->transformSystem->getLocalToWorld(rootTransform->parent.resolveComponent<TransformComponent>())
46 : glm::translate(glm::vec3(rootTransform->coordinates - context->transformSystem->getOrigin()));
47 auto totalParentTransform = parentLocalToWorld * rootLocalToWorldInverse;
48
49 data.childWorldBoundingBoxes.resize(data.children.size(), Cogs::Geometry::makeEmptyBoundingBox<Cogs::Geometry::BoundingBox>());
50
51 auto entityBBoxCalculationFun = [context, &data, &totalParentTransform](size_t index)
52 {
53 auto entity = data.children[index];
54
55 auto transformComponent = entity->getComponent<TransformComponent>();
56 auto meshRenderComponent = entity->getComponent<MeshRenderComponent>();
57 assert(meshRenderComponent != nullptr && "MeshRenderComponent should not be null");
58
59 // The local bounds are frame old
60 auto & localBounds = context->renderSystem->getLocalBounds(meshRenderComponent);
61 auto parentLocalTransform = totalParentTransform * context->transformSystem->getLocalToWorld(transformComponent);
62 data.childWorldBoundingBoxes[index] = Bounds::getTransformedBounds(localBounds, parentLocalTransform);
63 };
64
65 if (workParallel) {
66 Parallel::forEach(context, data.children.size(), entityBBoxCalculationFun, "PredictWorldBoundingBoxes");
67 }
68 else {
69 for (size_t entityIndex = 0; entityIndex < data.children.size(); ++entityIndex) {
70 entityBBoxCalculationFun(entityIndex);
71 }
72 }
73 }
74
75 Cogs::Geometry::BoundingBox AccumulateBoudingBoxes(ScreenSizeData& data)
76 {
77 auto result = Cogs::Geometry::makeEmptyBoundingBox<Cogs::Geometry::BoundingBox>();
78
79 for (auto const & bb : data.childWorldBoundingBoxes) {
80 result += bb;
81 }
82
83 return result;
84 }
85
86 float CalculateProjectedSize(Context* context, ScreenSizeComponent const & component, Cogs::Geometry::BoundingBox& worldBounds)
87 {
88 CameraComponent* cameraComponent = nullptr;
89 TransformComponent* cameraTransform = nullptr;
90 if (component.camera != nullptr)
91 {
92 cameraComponent = component.camera->getComponent<CameraComponent>();
93 cameraTransform = component.camera->getComponent<TransformComponent>();
94 }
95 else
96 {
97 cameraComponent = context->cameraSystem->getMainCamera();
98 cameraTransform = context->cameraSystem->getMainCamera()->getContainer()->getComponent<TransformComponent>();
99 }
100
101 if (cameraComponent == nullptr || cameraTransform == nullptr)
102 {
103 LOG_WARNING(logger, "Unable to get camera data for entity id %zu.", component.getContainer()->getId());
104 return std::numeric_limits<float>::max();
105 }
106
107 auto & cameraData = context->cameraSystem->getData(cameraComponent);
108 auto cameraPosition = glm::vec3(context->transformSystem->getLocalToWorld(cameraTransform) * glm::vec4(0, 0, 0, 1));
109 auto cameraDir = glm::normalize(glm::vec3(cameraData.inverseViewMatrix * glm::vec4(0, 0, -1, 0)));
110
111 auto worldCenter = (worldBounds.max + worldBounds.min) / 2.0f;
112 auto worldRadius = glm::length(worldBounds.max - worldCenter);
113
114 auto cameraDistanceAlongViewDir = glm::abs(glm::dot(worldCenter - cameraPosition, cameraDir));
115 auto projectedRadius = worldRadius / (cameraDistanceAlongViewDir * glm::tan(cameraData.fieldOfView / 2.0f));
116 return 2 * projectedRadius;
117 }
118
119 glm::mat4 CalculateNewTransform(Context* context, ScreenSizeComponent const & component, ScreenSizeData& /*data*/, Cogs::Geometry::BoundingBox const & worldBounds, float desiredScale)
120 {
121 auto transformComponent = component.getComponent<TransformComponent>();
122 auto worldCenter = (worldBounds.min + worldBounds.max) / 2.0f;
123 auto coreTransform = glm::translate(glm::scale(glm::translate(worldCenter), glm::vec3(desiredScale, desiredScale, desiredScale)), -worldCenter);
124 auto localTransfrom = transformComponent->parent ? glm::mat4() : glm::translate(glm::vec3(transformComponent->coordinates - context->transformSystem->getOrigin()));
125 return glm::inverse(localTransfrom) * coreTransform * localTransfrom;
126 }
127}
128
130{
132
133 taskGroup = context->taskManager->createGroup();
134}
135
137{
138 CpuInstrumentationScope(SCOPE_SYSTEMS, "ScreenSizeSystem::update");
139
140 const bool workParallel = context->engine->workParallel();
141
142 calcluateNewScales(context, workParallel);
143 updateTransforms(context, workParallel);
144}
145
147{
148 if (taskGroup.isValid()) {
149 context->taskManager->destroy(taskGroup);
150 }
151}
152
153void Cogs::Core::ScreenSizeSystem::calcluateNewScales(Context* context, bool workParallel)
154{
155 constexpr static const float Epsilon = 1e-5f;
156
157 CpuInstrumentationScope(SCOPE_SYSTEMS, "ScreenSizeSystem::calcluateNewScales");
158
159 auto calcluateNewScalesParallel = [this, context, workParallel](ScreenSizeComponent const & screenSizeComponent, size_t)
160 {
161 auto & data = this->getData<ScreenSizeData>(&screenSizeComponent);
162
163 data.children.clear();
164 data.transformUpdateRequired = false;
165
166 ForEachEntityPreOrder(screenSizeComponent.getContainer(), [&data](Cogs::ComponentModel::Entity* entity) {
167 auto meshRenderComponent = entity->getComponent<MeshRenderComponent>();
168 if (meshRenderComponent != nullptr && meshRenderComponent->isVisible())
169 {
170 data.children.push_back(entity);
171 }
172 });
173
174 PredictWorldBoundingBoxes(context, workParallel, screenSizeComponent, data);
175 auto worldBounds = AccumulateBoudingBoxes(data);
176 auto newSize = CalculateProjectedSize(context, screenSizeComponent, worldBounds);
177
178 auto currentScale = std::isfinite(data.currentSize) ? screenSizeComponent.minSize / data.currentSize : 1.0f;
179 auto newScale = std::isfinite(newSize) ? screenSizeComponent.minSize / newSize : 1.0f;
180
181 if (newScale + Epsilon <= 1.0f)
182 {
183 if(currentScale + Epsilon > 1.0f)
184 {
185 auto transformComponent = screenSizeComponent.getComponent<TransformComponent>();
186 transformComponent->transform = {};
187 transformComponent->transformFlags = 1;
188 transformComponent->setChanged();
189
190 data.transformUpdateRequired = true;
191 }
192 }
193 else if(newSize != data.currentSize)
194 {
195 auto transformComponent = screenSizeComponent.getComponent<TransformComponent>();
196 transformComponent->transform = CalculateNewTransform(context, screenSizeComponent, data, worldBounds, newScale);
197 transformComponent->transformFlags = 1;
198 transformComponent->setChanged();
199
200 data.transformUpdateRequired = true;
201 }
202
203 data.currentSize = newSize;
204 };
205
206 if (workParallel) {
207 Parallel::processComponents(context, pool, "ScreenSizeSystem::calcluateNewScalesParallel", calcluateNewScalesParallel, taskGroup);
208 context->taskManager->wait(taskGroup);
209 }
210 else {
211 Serial::processComponents(pool, calcluateNewScalesParallel);
212 }
213}
214
215void Cogs::Core::ScreenSizeSystem::updateTransforms(Context* context, bool workParallel)
216{
217 CpuInstrumentationScope(SCOPE_SYSTEMS, "ScreenSizeSystem::updateTransforms");
218
219 auto updateTransformsParallel = [this, context](ScreenSizeComponent const & screenSizeComponent, size_t)
220 {
221 auto & data = this->getData<ScreenSizeData>(&screenSizeComponent);
222
223 if (!data.transformUpdateRequired)
224 {
225 return;
226 }
227
228 auto transformComponent = screenSizeComponent.getComponent<TransformComponent>();
229 context->transformSystem->updateLocalTransform(*transformComponent);
230
231 ForEachEntityPreOrder(screenSizeComponent.getContainer(), [context, transformComponent](Cogs::ComponentModel::Entity* /*entity*/) {
232 context->transformSystem->getData<Core::TransformState>(transformComponent).cached = false;
233 context->transformSystem->getData<Core::TransformState>(transformComponent).changed = true;
234 context->transformSystem->updateLocalToWorldTransform(*transformComponent);
235 });
236 };
237
238 if (workParallel) {
239 Parallel::processComponents(context, pool, "ScreenSizeSystem::updateTransformsParallel", updateTransformsParallel, taskGroup);
240 context->taskManager->wait(taskGroup);
241 }
242 else {
243 Serial::processComponents(pool, updateTransformsParallel);
244 }
245}
ComponentType * getComponent() const
Definition: Component.h:159
class Entity * getContainer() const
Get the container currently owning this component instance.
Definition: Component.h:151
Container for components, providing composition of dynamic entities.
Definition: Entity.h:18
T * getComponent() const
Get a pointer to the first component implementing the given type in the entity.
Definition: Entity.h:35
constexpr size_t getId() const noexcept
Get the unique identifier of this entity.
Definition: Entity.h:113
Context * context
Pointer to the Context instance the system lives in.
virtual void initialize(Context *context)
Initialize the system.
void update()
Updates the system state to that of the current frame.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
Definition: Context.h:186
std::unique_ptr< class Engine > engine
Engine instance.
Definition: Context.h:222
Renders the contents of a MeshComponent using the given materials.
Contains information on how the entity behaves in the scene.
Allows to control the screen size of the entity.
void initialize(Context *context) override
Initialize the system.
void cleanup(Context *context) override
Provided for custom cleanup logic in derived systems.
Defines a 4x4 transformation matrix for the entity and a global offset for root entities.
glm::mat4 transform
Complete transformation.
glm::dvec3 getOrigin() const
Gets the Origin offset of the scene.
Log implementation class.
Definition: LogManager.h:139
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180