Cogs.Core
LodSystem.cpp
1#include "LodSystem.h"
2
3#include "Utilities/Parsing.h"
4
5#include "Context.h"
6#include "CameraSystem.h"
7#include "TransformSystem.h"
8#include "Services/Variables.h"
9
10#include "Components/Core/TransformComponent.h"
11#include "Components/Core/SceneComponent.h"
12#include "Components/Core/RenderComponent.h"
13#include "Components/Core/MeshComponent.h"
14#include "Components/Core/SceneComponent.h"
15#include "Systems/Core/RenderSystem.h"
16
17#include "Foundation/ComponentModel/Entity.h"
18#include "Foundation/Logging/Logger.h"
19
20namespace
21{
22 using namespace Cogs::Core;
23
24 Cogs::Logging::Log logger = Cogs::Logging::getLogger("LodSystem");
25
26
27 float getMinDistanceToBBox(Context* context, Entity* entity, const glm::vec3& refWorld)
28 {
29 auto minDistance = std::numeric_limits<float>::max();
30 if (auto * sceneComp = entity->getComponent<SceneComponent>()) {
31 for (auto & child : sceneComp->children) {
32 minDistance = std::min(minDistance, getMinDistanceToBBox(context, child.get(), refWorld));
33 }
34 }
35
36 if (auto * renderComp = entity->getComponent<MeshRenderComponent>()) {
37 const auto & bbox = context->renderSystem->getWorldBounds(renderComp);
38 if (bbox.min.x <= bbox.max.x) {
39 auto p = glm::clamp(refWorld, bbox.min, bbox.max);
40 auto l = p - refWorld;
41 auto d2 = glm::dot(l, l);
42 if (d2 < minDistance*minDistance) {
43 minDistance = std::sqrt(d2);
44 }
45 }
46 }
47 return minDistance;
48 }
49
50
51}
52
53namespace Cogs
54{
55 namespace Core
56 {
57 void calculateLevelOfDetail(Context * context,
58 const LodComponent & component,
59 LodData & lodData,
60 const glm::vec3 & cameraCenter,
61 const CameraData & cameraData,
62 bool cameraChanged)
63 {
64 auto transform = component.getComponent<TransformComponent>();
65 bool transformChanged = context->transformSystem->hasChanged(transform);
66
67 if (!transformChanged && !cameraChanged) return;
68
69 switch (component.policy) {
71 {
72 glm::vec3 center = glm::vec3(context->transformSystem->getLocalToWorld(transform) * glm::vec4(0, 0, 0, 1));
73
74 const float distance = glm::clamp(glm::length(center - cameraCenter), 0.0f, std::numeric_limits<float>::max());
75
76 size_t index = 0;
77
78 for (size_t i = 0; i < component.thresholds.size(); ++i) {
79 if (distance > component.thresholds[i]) index = i;
80 }
81
82 lodData.currentLodIndex = static_cast<uint8_t>(index);
83 }
84 break;
86 {
87 glm::mat4 localToEye = cameraData.viewMatrix * context->transformSystem->getLocalToWorld(transform);
88
89 // Determine approximate eye-space radius of local space unit sphere
90 float lx2 = glm::dot(localToEye[0], localToEye[0]);
91 float ly2 = glm::dot(localToEye[1], localToEye[1]);
92 float lz2 = glm::dot(localToEye[2], localToEye[2]);
93 float r = glm::sqrt(glm::max(glm::max(lx2, ly2), lz2));
94
95 // Project two points to the screen and determine pixel distance
96 glm::vec4 ah = cameraData.projectionMatrix * localToEye[3];
97 glm::vec4 bh = cameraData.projectionMatrix * (localToEye[3] + glm::vec4(r, 0.f, 0.f, 0.f));
98 float w = 0.5f * cameraData.viewportSize.x * glm::distance((1.f / ah.w) * glm::vec2(ah.x, ah.y), (1.f / bh.w) * glm::vec2(bh.x, bh.y));
99
100 lodData.detailLevel = std::ceil(glm::pi<float>() / std::acos(1.f - component.geometricTolerance / std::max(std::numeric_limits<float>::epsilon(), w)));
101 }
102 break;
103
104 default:
105 break;
106 }
107 }
108 }
109}
110
112{
113 geometricErrorKind = parseEnum(context->variables->get("systems.lod.geometricerrorkind", "AreaBased"), GeometricErrorKind::AreaBased);
114 previousFrameStickyness = glm::clamp(context->variables->get("systems.lod.previousframestickyness", 0.1f), -0.f, -0.99f);
115
116 auto camera = context->cameraSystem->getMainCamera();
117 auto cameraTransform = camera->getComponent<TransformComponent>();
118 const auto & cameraData = context->cameraSystem->getData(camera);
119 const glm::mat4 & worldToClip = cameraData.viewProjection;
120 const glm::mat4 & clipToWorld = cameraData.inverseViewProjectionMatrix;
121
122 glm::vec3 cameraCenter = glm::vec3(context->transformSystem->getLocalToWorld(cameraTransform) * glm::vec4(0, 0, 0, 1));
123
124 for (const auto & component : pool) {
125 auto & lodData = getData<LodData>(&component);
126
127 if (component.policy == LodPolicy::None) continue;
128
129 auto transformComponent = component.getComponent<TransformComponent>();
130
131 lodData.localToClip = worldToClip * context->transformSystem->getLocalToWorld(transformComponent);
132 lodData.clipToLocal = glm::inverse(context->transformSystem->getLocalToWorld(transformComponent)) * clipToWorld;
133
134 auto sceneComponent = component.getComponent<SceneComponent>();
135
136 if (!sceneComponent) continue;
137
138 calculateLevelOfDetail(context, component, lodData, cameraCenter, cameraData, true);
139
140 if (component.policy == LodPolicy::GeometricTolerance || component.policy == LodPolicy::None) continue;
141
142 lodData.previousLodIndex = lodData.currentLodIndex;
143
144 const uint8_t numLevels = static_cast<uint8_t>(sceneComponent->children.size());
145
147
148 if (component.separateHierarchies) {
149 for (size_t i = 0; i < sceneComponent->children.size(); i++) {
150 //updateVisibility(context, sceneComponent->children.get, i == lodData.currentLodIndex);
151 auto * childSceneComp = sceneComponent->children[i]->getComponent<SceneComponent>();
152 childSceneComp->visible = i == lodData.currentLodIndex;
153 childSceneComp->setChanged();
154 }
155 }
156 else {
157
158 for (uint8_t i = 0; i < numLevels; ++i) {
159 auto & child = sceneComponent->children[i];
160
161 child->getComponents(renderComponents);
162
163 for (auto & renderComponent : renderComponents) {
164 renderComponent.lod.selectedLod = lodData.currentLodIndex;
165 renderComponent.lod.currentLod = i;
166 renderComponent.lod.numLods = numLevels;
167 renderComponent.lod.lodFraction = 0;
168 }
169
170 const auto childSceneComponent = child->getComponent<SceneComponent>();
171
172 std::function<void(SceneComponent &)> updateChildren = [&, i](const SceneComponent & component)
173 {
174 for (auto & child : component.children) {
175 child->getComponents(renderComponents);
176
177 for (auto & renderComponent : renderComponents) {
178 renderComponent.lod.selectedLod = lodData.currentLodIndex;
179 renderComponent.lod.currentLod = i;
180 renderComponent.lod.numLods = numLevels;
181 renderComponent.lod.lodFraction = 0;
182 }
183
184 auto childSceneComponent = child->getComponent<SceneComponent>();
185
186 if (childSceneComponent) {
187 updateChildren(*childSceneComponent);
188 }
189 }
190 };
191
192 updateChildren(*childSceneComponent);
193 }
194 }
195
196 }
197}
ComponentType * getComponent() const
Definition: Component.h:159
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
Context * context
Pointer to the Context instance the system lives in.
void update()
Updates the system state to that of the current frame.
ComponentPool< ComponentType > pool
Pool of components managed by the system.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
std::unique_ptr< class Variables > variables
Variables service instance.
Definition: Context.h:180
Contains data describing level of detail behavior for the entity the component belongs to.
Definition: LodComponent.h:43
std::vector< float > thresholds
Threshold values to switch levels of detail at when the policy in use is distance based (for example ...
Definition: LodComponent.h:63
LodPolicy policy
The policy used for determining the level of detail for this entity.
Definition: LodComponent.h:51
float geometricTolerance
Geometric tolerance value applied when the lodPolicy field is set to LodPolicy::GeometricTolerance.
Definition: LodComponent.h:68
bool separateHierarchies
Enable old-style lod'ing with a completely separate hierarchy for each lod-level.
Definition: LodComponent.h:73
GeometricErrorKind geometricErrorKind
Specifies what kind of error measure that is preferred.
Definition: LodSystem.h:49
float previousFrameStickyness
A number in [0,1] that indicates resilience to change, used to minimize popping.
Definition: LodSystem.h:46
Renders the contents of a MeshComponent using the given materials.
Contains information on how the entity behaves in the scene.
bool visible
If the entity this component is a member of should be visible.
Defines a 4x4 transformation matrix for the entity and a global offset for root entities.
Log implementation class.
Definition: LogManager.h:139
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
@ GeometricTolerance
Use a geometric error bound to determine how refined geometry needs to be at its current position.
@ None
No policy applied, the LodSystem will take no action on the entity.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
Contains data describing a Camera instance and its derived data structured such as matrix data and vi...
Definition: CameraSystem.h:67
Defines level of detail data calculated by the LodSystem.
Definition: LodSystem.h:15