Cogs.Core
GetBounds.cpp
1#include "GetBounds.h"
2
3#include "Context.h"
4#include "EntityStore.h"
5
6#include "Components/Core/MeshRenderComponent.h"
7#include "Components/Core/SceneComponent.h"
8#include "Components/Core/NearLimitComponent.h"
9
10#include "Foundation/ComponentModel/Component.h"
11#include "Systems/Core/MeshSystem.h"
12#include "Systems/Core/CameraSystem.h"
13#include "Systems/Core/TransformSystem.h"
14#include "Systems/Core/RenderSystem.h"
15#include "Systems/Core/SubMeshRenderSystem.h"
16#include "Systems/Core/StaticModelSystem.h"
17#include "Systems/Core/SpriteRenderSystem.h"
18
19#include "Platform/Instrumentation.h"
20
21#include "Rendering/DataFormat.h"
22
23#include "Resources/MeshManager.h"
24#include "Resources/Buffer.h"
25
26#include "Utilities/Parallel.h"
27#include "Utilities/Simplex.h"
28
29#include "Foundation/Geometry/BoundingBox.hpp"
30#include "Foundation/Geometry/Glm.hpp"
31#include "Foundation/Logging/Logger.h"
32
33#include <glm/gtc/packing.hpp>
34
35#include <array>
36
37using glm::mat4;
38using glm::vec4;
39using namespace Cogs::Geometry;
40using namespace Cogs::Core;
41
42namespace
43{
44 const Cogs::Logging::Log logger = Cogs::Logging::getLogger("GetBounds");
45
46 void entityError(const EntityId entityId)
47 {
48 LOG_ERROR(logger, "Invalid entity id %zd.", size_t(entityId));
49 }
50
51 template<typename T, glm::precision P>
52 glm::tvec3<T, P> transformWithDivision(glm::tvec3<T, P> v, glm::tmat4x4<T, P> m)
53 {
54 auto hV = glm::tvec4<T, P>(v.x, v.y, v.z, 1);
55 auto r = m * hV;
56
57 return glm::tvec3<T, P>(r.x / r.w, r.y / r.w, r.z / r.w);
58 }
59
60 const BoundingBox & getMeshBounds(RenderSystem * renderSystem, const MeshRenderComponent * meshComponent)
61 {
62 return renderSystem->getWorldBounds(meshComponent);
63 }
64
65 const BoundingBox & getMeshBounds(SubMeshRenderSystem * subMeshRenderSystem, const SubMeshRenderComponent * meshComponent)
66 {
67 return subMeshRenderSystem->getWorldBounds(meshComponent);
68 }
69
70 const BoundingBox & getSpriteBounds(Context * context, const SpriteRenderComponent * sprite)
71 {
72 return context->spriteRenderSystem->getData(sprite).boundingBox;
73 }
74
75 [[nodiscard]] BoundingBox getBounds(Context * context, const Entity * entity, bool ignoreVisibility)
76 {
77 const EntityData* entityData = static_cast<const EntityData *>(entity->getUserData());
78
79 if (entityData && entityData->entityContext) {
80 const MeshRenderComponent* meshRenderComponent = entity->getComponent<MeshRenderComponent>();
81 const MeshComponent* meshComp = entity->getComponent<MeshComponent>();
82
83 if (meshRenderComponent && (ignoreVisibility || meshRenderComponent->isVisible()) && meshComp && meshComp->meshHandle) {
84 return getMeshBounds(entityData->entityContext->renderSystem, meshRenderComponent);
85 }
86 } else {
87 const MeshRenderComponent* meshRenderComponent = entity->getComponent<MeshRenderComponent>();
88 const MeshComponent* meshComp = entity->getComponent<MeshComponent>();
89
90 if (meshRenderComponent && (ignoreVisibility || meshRenderComponent->isVisible()) && meshComp && meshComp->meshHandle) {
91 return getMeshBounds(context->renderSystem, meshRenderComponent);
92 }
93
94 const SpriteRenderComponent* spriteRenderComponent = entity->getComponent<SpriteRenderComponent>();
95
96 if (spriteRenderComponent && spriteRenderComponent->isVisible()) {
97 return getSpriteBounds(context, spriteRenderComponent);
98 }
99
100 BoundingBox extensionBounds;
101 for (auto & bounds : context->bounds->getExtensions()) {
102 if (bounds->getBounds(context, entity, extensionBounds, ignoreVisibility))
103 return extensionBounds;
104 }
105 }
106
107 return BoundingBox();
108 }
109
110 BoundingBox getBoundsWithChildren(Context * context, const Entity * entity, bool ignoreVisibility)
111 {
112 StaticModelComponent* staticModelComponent = entity->getComponent<StaticModelComponent>();
113
114 if (staticModelComponent) {
115 auto renderSystem = context->staticModelSystem->getRenderSystem(staticModelComponent);
116
117 BoundingBox bbox;
118
119 if (renderSystem) {
120 for (auto& rc : renderSystem->pool) {
121 bbox += renderSystem->getWorldBounds(&rc);
122 }
123 }
124
125 return bbox;
126 }
127
128 auto box = getBounds(context, entity, ignoreVisibility);
129 const SceneComponent* sceneComponent = entity->getComponent<SceneComponent>();
130
131 if (!sceneComponent) return box;
132
133 for (auto & child : sceneComponent->children) {
134 box += getBoundsWithChildren(context, child.get(), ignoreVisibility);
135 }
136
137 return box;
138 }
139
140
141 bool isInside(const glm::vec3& point, const BoundingBox& bbox)
142 {
143 return point.x >= bbox.min.x && point.x <= bbox.max.x &&
144 point.y >= bbox.min.y && point.y <= bbox.max.y &&
145 point.z >= bbox.min.z && point.z <= bbox.max.z;
146 }
147
148 void findNearFarFromMeshVerts(const Mesh& mesh, const glm::mat4& worldViewProj, const glm::mat4& inverseProj, float& viewNear, float& viewFar)
149 {
150 const uint8_t* posData = nullptr;
151 Cogs::DataFormat posFormat = Cogs::DataFormat::Unknown;
152 uint32_t posStride = 0;
153 uint32_t posCount = 0;
154
155
156 for (size_t i = 0; i < VertexDataType::LastVertexType; ++i) {
158 if (mesh.hasStream(vertexType)) {
159 const DataStream& stream = mesh.streams[mesh.streamIndexes[vertexType]];
160 const Cogs::VertexFormat* vertexFormat = Cogs::VertexFormats::getVertexFormat(stream.format);
161 for (const Cogs::VertexElement& element : vertexFormat->elements) {
162 if (element.semantic == Cogs::ElementSemantic::Position && element.semanticIndex == 0) {
163 posData = static_cast<const uint8_t*>(stream.buffer->data()) + stream.offset + element.offset;
164 posFormat = element.format;
165 posStride = stream.stride;
166 posCount = stream.numElements;
167 break;
168 }
169 }
170 }
171 }
172
173 if (posData == nullptr || posCount == 0) {
174 return;
175 }
176
177 glm::vec3 nearLocal{std::numeric_limits<float>::max()};
178 glm::vec3 farLocal{std::numeric_limits<float>::min()};
179
180 // Looping over vertices will likely be on the hot path, so duplicating the loops to avoid more branching inside.
181 switch(posFormat) {
182 case Cogs::DataFormat::R10G10B10A2_UINT: {
183 for (uint32_t i = 0; i < posCount; i++) {
184 glm::vec3 pos = glm::unpackU3x10_1x2(*reinterpret_cast<const uint32_t*>(posData + posStride * i));
185 const glm::vec4 posInClip = worldViewProj * glm::vec4{ pos, 1.f };
186
187 if (-posInClip.w <= posInClip.x && posInClip.x <= posInClip.w &&
188 -posInClip.w <= posInClip.y && posInClip.y <= posInClip.w) {
189 const float z = posInClip.z / posInClip.w;
190
191 if (z < nearLocal.z) {
192 nearLocal = {posInClip.x / posInClip.w, posInClip.y / posInClip.w, z};
193 }
194 if (z > farLocal.z) {
195 farLocal = {posInClip.x / posInClip.w, posInClip.y / posInClip.w, z};
196 }
197 }
198 // else position outside camera frustum, don't take it into account
199 }
200 break;
201 }
202 case Cogs::DataFormat::R10G10B10A2_UNORM: {
203 const float scaleFactor = (1.f / 1023.f);
204 for (uint32_t i = 0; i < posCount; i++) {
205 glm::vec3 pos = glm::vec3(glm::unpackU3x10_1x2(*reinterpret_cast<const uint32_t*>(posData + posStride * i))) * scaleFactor;
206 const glm::vec4 posInClip = worldViewProj * glm::vec4{ pos, 1.f };
207
208 if (-posInClip.w <= posInClip.x && posInClip.x <= posInClip.w &&
209 -posInClip.w <= posInClip.y && posInClip.y <= posInClip.w) {
210 const float z = posInClip.z / posInClip.w;
211
212 if (z < nearLocal.z) {
213 nearLocal = {posInClip.x / posInClip.w, posInClip.y / posInClip.w, z};
214 }
215 if (z > farLocal.z) {
216 farLocal = {posInClip.x / posInClip.w, posInClip.y / posInClip.w, z};
217 }
218 }
219 // else position outside camera frustum, don't take it into account
220 }
221 break;
222 }
223 case Cogs::DataFormat::R16G16B16_UINT: {
224 const glm::u16vec3* verts = reinterpret_cast<const glm::u16vec3*>(posData);
225 for (uint32_t i = 0; i < posCount; i++) {
226 const glm::vec4 posInClip = worldViewProj * glm::vec4{ verts[i], 1.f };
227
228 if (-posInClip.w <= posInClip.x && posInClip.x <= posInClip.w &&
229 -posInClip.w <= posInClip.y && posInClip.y <= posInClip.w) {
230 const float z = posInClip.z / posInClip.w;
231
232 if (z < nearLocal.z) {
233 nearLocal = {posInClip.x / posInClip.w, posInClip.y / posInClip.w, z};
234 }
235 if (z > farLocal.z) {
236 farLocal = {posInClip.x / posInClip.w, posInClip.y / posInClip.w, z};
237 }
238 }
239 // else position outside camera frustum, don't take it into account
240 }
241 break;
242 }
243 case Cogs::DataFormat::R16G16B16_UNORM: {
244 const glm::u16vec3* verts = reinterpret_cast<const glm::u16vec3*>(posData);
245 const float scaleFactor = (1.f / 0xFFFF);
246 for (uint32_t i = 0; i < posCount; i++) {
247 const glm::vec3 posInLocalSpace = glm::vec3(verts[i]) * scaleFactor;
248 const glm::vec4 posInClip = worldViewProj * glm::vec4{posInLocalSpace , 1.f };
249
250 if (-posInClip.w <= posInClip.x && posInClip.x <= posInClip.w &&
251 -posInClip.w <= posInClip.y && posInClip.y <= posInClip.w) {
252 const float z = posInClip.z / posInClip.w;
253
254 if (z < nearLocal.z) {
255 nearLocal = {posInClip.x / posInClip.w, posInClip.y / posInClip.w, z};
256 }
257 if (z > farLocal.z) {
258 farLocal = {posInClip.x / posInClip.w, posInClip.y / posInClip.w, z};
259 }
260 }
261 // else position outside camera frustum, don't take it into account
262 }
263 break;
264 }
265 case Cogs::DataFormat::R16G16B16_FLOAT: {
266 for (uint32_t i = 0; i < posCount; i++) {
267 glm::vec3 pos = glm::unpackHalf4x16(*reinterpret_cast<const uint64_t*>(posData + posStride * i));
268 const glm::vec4 posInClip = worldViewProj * glm::vec4{ pos, 1.f };
269
270 if (-posInClip.w <= posInClip.x && posInClip.x <= posInClip.w &&
271 -posInClip.w <= posInClip.y && posInClip.y <= posInClip.w) {
272 const float z = posInClip.z / posInClip.w;
273
274 if (z < nearLocal.z) {
275 nearLocal = {posInClip.x / posInClip.w, posInClip.y / posInClip.w, z};
276 }
277 if (z > farLocal.z) {
278 farLocal = {posInClip.x / posInClip.w, posInClip.y / posInClip.w, z};
279 }
280 }
281 // else position outside camera frustum, don't take it into account
282 }
283 break;
284 }
285 case Cogs::DataFormat::R32G32B32_FLOAT: {
286 const glm::vec3* verts = reinterpret_cast<const glm::vec3*>(posData);
287 for (uint32_t i = 0; i < posCount; i++) {
288 const glm::vec4 posInClip = worldViewProj * glm::vec4{ verts[i], 1.f };
289
290 if (-posInClip.w <= posInClip.x && posInClip.x <= posInClip.w &&
291 -posInClip.w <= posInClip.y && posInClip.y <= posInClip.w) {
292 const float z = posInClip.z / posInClip.w;
293
294 if (z < nearLocal.z) {
295 nearLocal = {posInClip.x / posInClip.w, posInClip.y / posInClip.w, z};
296 }
297 if (z > farLocal.z) {
298 farLocal = {posInClip.x / posInClip.w, posInClip.y / posInClip.w, z};
299 }
300 }
301 // else position outside camera frustum, don't take it into account
302 }
303 break;
304 }
305 // Format usually used by points rendering (eg. MarkerPointSet). For simplicity,take into account just the point position.
306 case Cogs::DataFormat::R32G32B32A32_FLOAT: {
307 const glm::vec4* verts = reinterpret_cast<const glm::vec4*>(posData);
308 for (uint32_t i = 0; i < posCount; i++) {
309 const glm::vec4 posInClip = worldViewProj * glm::vec4{ glm::vec3(verts[i]), 1.f };
310
311 if (-posInClip.w <= posInClip.x && posInClip.x <= posInClip.w &&
312 -posInClip.w <= posInClip.y && posInClip.y <= posInClip.w) {
313 const float z = posInClip.z / posInClip.w;
314
315 if (z < nearLocal.z) {
316 nearLocal = {posInClip.x / posInClip.w, posInClip.y / posInClip.w, z};
317 }
318 if (z > farLocal.z) {
319 farLocal = {posInClip.x / posInClip.w, posInClip.y / posInClip.w, z};
320 }
321 }
322 // else position outside camera frustum, don't take it into account
323 }
324 break;
325 }
326 default: {
327 const Cogs::FormatInfo* formatInfo = Cogs::getFormatInfo(posFormat);
328 LOG_ERROR(logger, "Unsupported position format: %s", formatInfo->name);
329 return;
330 }
331 }
332
333 if (nearLocal.z < std::numeric_limits<float>::max()) {
334 const glm::vec4 closest = inverseProj * glm::vec4(nearLocal, 1.f);
335 viewNear = -closest.z / closest.w;
336 }
337 if (farLocal.z > std::numeric_limits<float>::min()) {
338 const glm::vec4 farthest = inverseProj * glm::vec4(farLocal, 1.f);
339 viewFar = -farthest.z / farthest.w;
340 }
341 }
342
343 void updateCameraDepthBounds(Cogs::Core::Context* context,
344 uint8_t* simplexScratch,
345 float& viewNear,
346 float& viewFar,
347 const BoundingBox& bbox,
348 const mat4& bboxViewMatrix,
349 const mat4& bboxViewMatrixInverse,
350 const mat4& bboxViewProjectionMatrix)
351 {
352 if(!(bbox.min.x <= bbox.max.x &&
353 bbox.min.y <= bbox.max.y &&
354 bbox.min.z <= bbox.max.z )){ // Handle empty and NaN Bounding boxes
355 return;
356 }
357 // Classify bounding box w.r.t. frustum
358 float localNear = std::numeric_limits<float>::max();
359 float localFar = -std::numeric_limits<float>::max();
360
361 int insideMask = 0;
362 int outsideMask = 0;
363 for (int i = 0; i < 8; i++) {
364 vec4 t(i & 1 ? bbox.min.x : bbox.max.x,
365 i & 2 ? bbox.min.y : bbox.max.y,
366 i & 4 ? bbox.min.z : bbox.max.z,
367 1.f);
368
369 vec4 c = bboxViewProjectionMatrix * t;
370 int pointMask = 0;
371 if (-c.w <= c.x) pointMask |= 0x1;
372 if (c.x <= c.w) pointMask |= 0x2;
373 if (-c.w <= c.y) pointMask |= 0x4;
374 if (c.y <= c.w) pointMask |= 0x8;
375 if (pointMask == 0xf) {
376 vec4 v = bboxViewMatrix*t;
377 float z = v.z / v.w;
378 assert(std::isfinite(z));
379 localNear = std::min(localNear, -z);
380 localFar = std::max(localFar, -z);
381 }
382 insideMask |= pointMask;
383 outsideMask |= ~pointMask & 0xf;
384 }
385
386 if(localNear >= viewNear && localFar <= viewFar){
387 return;
388 }
389
390 if (insideMask != 0xf) {
391 // All points was completely outside at least one clip-plane.
392 } else if (outsideMask == 0x0) {
393 // If insideMask == 0xf and outsideMask == 0x0, all points we're completely inside.
394
395 viewNear = std::min(viewNear, localNear);
396 viewFar = std::max(viewFar, localFar);
397 } else {
398 // If insideMask == 0xf and outsideMask != 0x0, at least one clip plane was pierced, resort to simplex algorithm
399
400 glm::mat4 bboxMinAlignedViewProjectionMatrix = glm::translate(bboxViewProjectionMatrix, bbox.min);
401 glm::mat4 bboxMinAlignedViewMatrix = glm::translate(bboxViewMatrix, bbox.min);
402
403 //glm::vec4 q = bboxMinAlignedViewProjectionMatrix * glm::vec4(0, 0, 0, 1);
404 //glm::vec4 p = bboxViewProjectionMatrix * glm::vec4(bbox.min, 1);
405
406 glm::vec3 e = bbox.max - bbox.min;
407 float scale = std::max(std::max(e.x, e.y), e.z);
408 if (scale <= 0.f) {
409 // Handle single point
410 viewNear = std::min(viewNear, localNear);
411 viewFar = std::max(viewFar, localNear);
412 return;
413 }
414
415 float epsilon = std::numeric_limits<float>::epsilon()*scale;
416
417 mat4 rows = glm::transpose(bboxMinAlignedViewProjectionMatrix);
418 glm::vec4 l = rows[3] + rows[0];
419 glm::vec4 r = rows[3] - rows[0];
420 glm::vec4 b = rows[3] + rows[1];
421 glm::vec4 t = rows[3] - rows[1];
422
423 // Implicit conditions
424 // * Min box size: x>=0 y>=0 z>=0
425 // Explicit conditions
426 // * Max box size: x<=ex y<=ey z<=ez
427 // * Frustrum conditions:
428 double restrictions[7 * 4] =
429 {
430 // Box conditions
431 1.0, 0.0, 0.0, std::max(epsilon, e.x), // x <= xmax-xmin
432 0.0, 1.0, 0.0, std::max(epsilon, e.y), // y <= ymax-ymin
433 0.0, 0.0, 1.0, std::max(epsilon, e.z), // z <= zmax-zmin
434 // Frustrum conditions
435 -l.x, -l.y, -l.z, l.w,
436 -r.x, -r.y, -r.z, r.w,
437 -b.x, -b.y, -b.z, b.w,
438 -t.x, -t.y, -t.z, t.w,
439 };
440
441 double objectives[2 * 3];
442 double solutions[2 * 3];
443
444 bool find_far = localFar > viewFar;
445 bool find_near = localNear < viewNear;
446 int objective_count = find_near && find_far ? 2 : 1;
447 int max_idx = 0;
448 int min_idx = find_far ? 1 : 0;
449 if(find_far){
450 // Minimize in the view -z direction
451 objectives[max_idx*3+0] = -bboxViewMatrixInverse[2].x;
452 objectives[max_idx*3+1] = -bboxViewMatrixInverse[2].y;
453 objectives[max_idx*3+2] = -bboxViewMatrixInverse[2].z;
454 }
455 if(find_near){
456 // Minimize in the view +z direction
457 objectives[min_idx*3+0] = bboxViewMatrixInverse[2].x;
458 objectives[min_idx*3+1] = bboxViewMatrixInverse[2].y;
459 objectives[min_idx*3+2] = bboxViewMatrixInverse[2].z;
460 }
461
462 auto rv = simplexMaximize(context,
463 (uintptr_t)simplexScratch,
464 solutions, 3,
465 objectives, objective_count,
466 restrictions, 7,
467 150);
468 if (rv == SimplexResult::NoFeasibleSolutions) {
469 // No intersection
470 } else if (rv == SimplexResult::OptimalSolutionFound) {
471 if(find_near){
472 vec4 v = bboxMinAlignedViewMatrix*glm::vec4(solutions[3 * min_idx + 0],
473 solutions[3 * min_idx + 1],
474 solutions[3 * min_idx + 2],
475 1.f);
476 float z = v.z / v.w;
477 assert(std::isfinite(z));
478 viewNear = std::min(viewNear, -z);
479 // viewFar = std::max(viewFar, -z);
480 }
481 if(find_far){
482 vec4 v = bboxMinAlignedViewMatrix*glm::vec4(solutions[3 * max_idx + 0],
483 solutions[3 * max_idx + 1],
484 solutions[3 * max_idx + 2],
485 1.f);
486 float z = v.z / v.w;
487 assert(std::isfinite(z));
488 // viewNear = std::min(viewNear, -z);
489 viewFar = std::max(viewFar, -z);
490 }
491 } else {
492 LOG_DEBUG(logger, "Simplex optimization failed (=%d), reverting to standard approach.", (int)rv);
493 for (int i = 0; i < 8; i++) {
494 vec4 t_(i & 1 ? bbox.min.x : bbox.max.x,
495 i & 2 ? bbox.min.y : bbox.max.y,
496 i & 4 ? bbox.min.z : bbox.max.z,
497 1.f);
498 vec4 v = bboxViewMatrix*t_;
499 float z = v.z / v.w;
500 assert(std::isfinite(z));
501 viewNear = std::min(viewNear, -z);
502 viewFar = std::max(viewFar, -z);
503 }
504
505 }
506 }
507 }
508}
509
510Cogs::Geometry::BoundingBox Cogs::Core::Bounds::getBounds(Context * context, const ComponentModel::Entity * entity, bool ignoreVisibility)
511{
512 return ::getBoundsWithChildren(context, entity, ignoreVisibility);
513}
514
515Cogs::Geometry::BoundingBox Cogs::Core::Bounds::getTransformedBounds(const Cogs::Geometry::BoundingBox& bbox, const glm::mat4 & m)
516{
517 std::array<glm::vec3, 8> points;
518 Cogs::Geometry::corners(bbox, points);
519
520 for (auto & p : points) {
521 p = transform(p, m);
522 }
523
524 return Cogs::Geometry::computeBoundingBox(points.begin(), points.end());
525}
526
527Cogs::Geometry::BoundingBox Cogs::Core::Bounds::getTransformedBoundsWithDivision(const Cogs::Geometry::BoundingBox& bbox, const glm::mat4 & m)
528{
529 std::array<glm::vec3, 8> points;
530 Cogs::Geometry::corners(bbox, points);
531
532 for (auto & p : points) {
533 p = transformWithDivision(p, m);
534 }
535
536 return Cogs::Geometry::computeBoundingBox(points.begin(), points.end());
537}
538
539Cogs::Geometry::BoundingBox Cogs::Core::Bounds::getBounds(Context* context, const EntityId id, bool ignoreVisibility)
540{
541 auto entity = context->store->getEntityPtr(id);
542
543 if (entity)
544 return ::getBoundsWithChildren(context, entity, ignoreVisibility);
545 else {
546 entityError(id);
547 return BoundingBox();
548 }
549}
550Cogs::Geometry::BoundingBox Cogs::Core::Bounds::getBounds(Context* context, std::span<const EntityId> ids, bool ignoreVisibility)
551{
552 Cogs::Geometry::BoundingBox box;
553
554 for (auto& id : ids) {
555 auto entity = context->store->getEntityPtr(id);
556
557 if (entity) {
558 Cogs::Geometry::BoundingBox bb = ::getBoundsWithChildren(context, entity, ignoreVisibility);
559 if (!bb.empty()) {
560 box += bb;
561 }
562 }
563 else {
564 entityError(id);
565 }
566 }
567
568 return box;
569}
570
571Cogs::Geometry::BoundingBox Cogs::Core::Bounds::getSceneBounds(Context* context, RenderLayers layerMask)
572{
573 Cogs::Geometry::BoundingBox box;
574
575 for (auto & renderComponent : context->renderSystem->pool) {
576 if (!renderComponent.isVisibleInLayer(layerMask)) continue;
577
578 const auto& bb = ::getMeshBounds(context->renderSystem, &renderComponent);
579
580 box += bb;
581 }
582
583 for (auto & renderComponent : context->subMeshRenderSystem->pool) {
584 if (!renderComponent.isVisibleInLayer(layerMask)) continue;
585
586 const auto& bb = ::getMeshBounds(context->subMeshRenderSystem, &renderComponent);
587
588 box += bb;
589 }
590
591 // Only include Bounds for sprites rendered in World type coords.
592 for (const SpriteRenderComponent& sprite : context->spriteRenderSystem->pool) {
593 if (!sprite.isVisibleInLayer(layerMask)) continue;
594 const auto& data = context->spriteRenderSystem->getData(&sprite);
595 auto batch = context->spriteRenderSystem->getSpriteBatch(data.batchIndex);
596 if (!batch) {
597 continue;
598 }
599
600 if (batch->positionMode == PositionMode::Pixels || batch->positionMode == PositionMode::Relative) {
601 continue;
602 }
603
604 const auto& bb = ::getSpriteBounds(context, &sprite);
605
606 box += bb;
607 }
608
609 for (auto & bounds : getExtensions()) {
610 BoundingBox extensionBounds;
611
612 float nearPlaneLimit = 0.f;
613 bounds->getBounds(context, extensionBounds, nearPlaneLimit);
614
615 box += extensionBounds;
616 }
617
618 return box;
619}
620
621void Cogs::Core::Bounds::getSceneBounds(Context* context, float* values, RenderLayers layerMask)
622{
623 Cogs::Geometry::BoundingBox box = getSceneBounds(context, layerMask);
624 box.toSpan(std::span<float, 6>(values, 6));
625}
626
627void Cogs::Core::Bounds::getClipPlanes(Context* context, float& nearDist, float& farDist, const CameraComponent & cameraComponent, const glm::mat4 & viewMatrix)
628{
629 BoundingBox sceneBounds;
630
631 {
632 RenderLayers layerMask = cameraComponent.layerMask & ~RenderLayers::Sky;
633
634 for (auto & renderComponent : context->renderSystem->pool) {
635 if (!renderComponent.isVisibleInLayer(layerMask)) continue;
636
637 const BoundingBox& bb = ::getMeshBounds(context->renderSystem, &renderComponent);
638
639 sceneBounds += bb;
640 }
641
642 for (auto & renderComponent : context->subMeshRenderSystem->pool) {
643 if (!renderComponent.isVisibleInLayer(layerMask)) continue;
644
645 const BoundingBox& bb = ::getMeshBounds(context->subMeshRenderSystem, &renderComponent);
646
647 sceneBounds += bb;
648 }
649
650 for (auto & sprite : context->spriteRenderSystem->pool) {
651 if (!sprite.isVisibleInLayer(layerMask)) continue;
652
653 const BoundingBox& bb = ::getSpriteBounds(context, &sprite);
654
655 sceneBounds += bb;
656 }
657
658 for (auto & bounds : getExtensions()) {
659 BoundingBox extensionBounds;
660
661 float nearPlaneLimit = 0.f;
662 bounds->getBounds(context, extensionBounds, nearPlaneLimit);
663
664 if (!extensionBounds.empty()) {
665 if (!std::isnan(nearPlaneLimit) && !std::isinf(nearPlaneLimit) && nearPlaneLimit > 0.0f) {
666 extensionBounds = getTransformedBounds(extensionBounds, viewMatrix);
667
668 nearDist = std::max(std::min(nearDist, -extensionBounds.max.z), nearPlaneLimit);
669 farDist = std::max(farDist, -extensionBounds.min.z);
670 }
671 else {
672 sceneBounds += extensionBounds;
673 }
674 }
675 }
676 }
677
678 if (!sceneBounds.empty()) {
679 sceneBounds = getTransformedBounds(sceneBounds, viewMatrix);
680
681 nearDist = std::min(nearDist, -sceneBounds.max.z);
682 farDist = std::max(farDist, -sceneBounds.min.z);
683 }
684}
685
686void Cogs::Core::Bounds::getCameraDepthBounds(Context* context,
687 float& depthBoundNear,
688 float& depthBoundFar,
689 const CameraComponent & cameraComponent)
690{
691 CpuInstrumentationScope(SCOPE_BOUNDS, "updateClippingPlanes");
692 RenderLayers layerMask = cameraComponent.layerMask & ~RenderLayers::Sky;
693
694 auto & cameraData = context->cameraSystem->getData(&cameraComponent);
695 {
696 CpuInstrumentationScope(SCOPE_BOUNDS, "updateClippingPlanes::meshes");
697
698 std::vector<float> nearBounds(context->meshSystem->pool.size(), depthBoundNear);
699 std::vector<float> farBounds(context->meshSystem->pool.size(), depthBoundFar);
700
701 bool tightBoundsInBBox = context->variables->get("camera.tightBoundsInBBox", false);
702 auto gr1 = Parallel::processComponents(context, context->renderSystem->pool, "Bounds::process",
703 [&](MeshRenderComponent & meshRenderComp, size_t i)
704 {
705 if (!meshRenderComp.isVisibleInLayer(layerMask)) {
706 return;
707 }
708 if (meshRenderComp.lod.currentLod != meshRenderComp.lod.selectedLod) {
709 return;
710 }
711
712
713 const Cogs::Geometry::BoundingBox& bbox = context->renderSystem->getWorldBounds(&meshRenderComp);
714
715 if (!bbox.empty()) {
716 float nearPlaneLimit = cameraComponent.nearPlaneLimit;
717 // if (meshData.nearLimitComponent) {
718 // auto nearComp = meshData.nearLimitComponent.resolveComponent<NearLimitComponent>();
719 // if (nearComp != nullptr && std::isfinite(nearComp->nearPlaneLimit))
720 // nearPlaneLimit = nearComp->nearPlaneLimit;
721 // }
722
723 float nb = nearBounds[i];
724
725 bool cameraInsideBbox = isInside(cameraData.inverseViewMatrix[3], bbox);
726 if (tightBoundsInBBox && cameraInsideBbox) {
727 const MeshRenderData& renderData = context->renderSystem->getData<MeshRenderData>(&meshRenderComp);
728 assert(renderData.meshComponent != ComponentHandle::Empty());
729 const Mesh* mesh = renderData.meshComponent.resolveComponent<MeshComponent>()->meshHandle.resolve();
730 assert(mesh != nullptr);
731 const TransformComponent* transformComp = meshRenderComp.getComponent<TransformComponent>();
732 assert(transformComp != nullptr);
733 const glm::mat4& localToWorld = context->transformSystem->getLocalToWorld(transformComp);
734
735 findNearFarFromMeshVerts(*mesh, cameraData.viewProjection * localToWorld, cameraData.inverseProjectionMatrix, nb, farBounds[i]);
736 }
737 else {
738 std::vector<uint8_t> simplexScratch(simplexScratchSize(3, 2, 7));
739 ::updateCameraDepthBounds(context,
740 simplexScratch.data(),
741 nb, farBounds[i],
742 bbox, cameraData.viewMatrix, cameraData.inverseViewMatrix, cameraData.viewProjection);
743 }
744
745
746
747 nearBounds[i] = std::min(nearBounds[i], std::max(nearPlaneLimit, nb));
748 }
749 });
750
751 context->taskManager->destroy(gr1);
752
753 auto gr2 = Parallel::processComponents(context, context->subMeshRenderSystem->pool, "Bounds::processSubMesh",
754 [&](SubMeshRenderComponent & subMeshRenderComp, size_t i)
755 {
756 if (!subMeshRenderComp.isVisibleInLayer(layerMask)) return;
757 if (subMeshRenderComp.lod.currentLod != subMeshRenderComp.lod.selectedLod) return;
758
759 const Cogs::Geometry::BoundingBox& bbox = context->subMeshRenderSystem->getWorldBounds(&subMeshRenderComp);
760 if (!bbox.empty()) {
761 float nearPlaneLimit = cameraComponent.nearPlaneLimit;
762 // if (meshData.nearLimitComponent) {
763 // auto nearComp = meshData.nearLimitComponent.resolveComponent<NearLimitComponent>();
764 // if (nearComp != nullptr && std::isfinite(nearComp->nearPlaneLimit))
765 // nearPlaneLimit = nearComp->nearPlaneLimit;
766 // }
767
768 float nb = nearBounds[i];
769
770 bool cameraInsideBbox = isInside(cameraData.inverseViewMatrix[3], bbox);
771 if (tightBoundsInBBox && cameraInsideBbox) {
772 const SubMeshRenderData& renderData = context->subMeshRenderSystem->getData<SubMeshRenderData>(&subMeshRenderComp);
773 assert(renderData.meshComponent != ComponentHandle::Empty());
774 const Mesh* mesh = renderData.meshComponent.resolveComponent<MeshComponent>()->meshHandle.resolve();
775 assert(mesh != nullptr);
776 const TransformComponent* transformComp = subMeshRenderComp.getComponent<TransformComponent>();
777 assert(transformComp != nullptr);
778 const glm::mat4& localToWorld = context->transformSystem->getLocalToWorld(transformComp);
779
780 findNearFarFromMeshVerts(*mesh, cameraData.viewProjection * localToWorld, cameraData.inverseProjectionMatrix, nb, farBounds[i]);
781 }
782 else {
783 std::vector<uint8_t> simplexScratch(simplexScratchSize(3, 2, 7));
784 ::updateCameraDepthBounds(context,
785 simplexScratch.data(),
786 nb, farBounds[i],
787 bbox, cameraData.viewMatrix, cameraData.inverseViewMatrix, cameraData.viewProjection);
788 }
789
790 nearBounds[i] = std::min(nearBounds[i], std::max(nearPlaneLimit, nb));
791 }
792 });
793
794 context->taskManager->destroy(gr2);
795
796 for (size_t i = 0; i < nearBounds.size(); ++i) {
797 depthBoundNear = std::min(depthBoundNear, nearBounds[i]);
798 depthBoundFar = std::max(depthBoundFar, farBounds[i]);
799 }
800 }
801
802
803 for (auto & sprite : context->spriteRenderSystem->pool) {
804 if (!sprite.isVisibleInLayer(layerMask)) continue;
805 auto & data = context->spriteRenderSystem->getData(&sprite);
806 if (data.batchIndex == NoBatchIndex) continue;
807 const auto & batch = context->spriteRenderSystem->getSpriteBatch(data.batchIndex);
808
809 glm::vec4 p;
810 if (batch->positionMode == PositionMode::World) {
811 p = cameraData.viewMatrix * glm::vec4(data.position, 1);
812 } else if(batch->positionMode == PositionMode::Local){
813 p = cameraData.viewMatrix * batch->transform * glm::vec4(data.position, 1);
814 } else {
815 continue;
816 }
817
818 glm::vec4 c = cameraData.projectionMatrix * p;
819 int pointMask = 0;
820 if (-c.w <= c.x) pointMask |= 0x1;
821 if (c.x <= c.w) pointMask |= 0x2;
822 if (-c.w <= c.y) pointMask |= 0x4;
823 if (c.y <= c.w) pointMask |= 0x8;
824 int outsideMask = ~pointMask & 0xf;
825 if(outsideMask)
826 continue;
827
828 depthBoundNear = std::min(depthBoundNear, (-p.z) * (1.f - std::numeric_limits<float>::epsilon()));
829 depthBoundFar = std::max(depthBoundFar, (-p.z) * (1.f + std::numeric_limits<float>::epsilon()));
830 }
831
832 {
833 CpuInstrumentationScope(SCOPE_BOUNDS, "updateClippingPlanes::extensions");
834 for (auto & bounds : context->bounds->getExtensions()) {
835
836 float nearPlaneLimit = std::numeric_limits<float>::quiet_NaN();
837 BoundingBox extensionBounds;
838
839 bounds->getBounds(context, extensionBounds, nearPlaneLimit);
840 if (!extensionBounds.empty()) {
841
842 float nb = depthBoundNear;
843 std::vector<uint8_t> simplexScratch(simplexScratchSize(3, 2, 7));
844 ::updateCameraDepthBounds(context,
845 simplexScratch.data(),
846 nb, depthBoundFar,
847 extensionBounds, cameraData.viewMatrix, cameraData.inverseViewMatrix, cameraData.viewProjection);
848 if (!std::isfinite(nearPlaneLimit)) nearPlaneLimit = cameraComponent.nearPlaneLimit;
849
850 depthBoundNear = std::min(depthBoundNear, std::max(nearPlaneLimit, nb));
851 }
852 }
853 }
854
855}
856
857Cogs::Geometry::BoundingBox Cogs::Core::Bounds::getShadowBounds(Context* context)
858{
859 RenderLayers layerMask = RenderLayers::Default;
860 BoundingBox box;
861
862 for (auto & renderComponent : context->renderSystem->pool) {
863 if (!renderComponent.isVisible() || !renderComponent.castShadows()) continue;
864 if (!renderComponent.isVisibleInLayer(layerMask)) continue;
865 BoundingBox bb = ::getMeshBounds(context->renderSystem, &renderComponent);
866 box += bb;
867 }
868
869 for (auto & renderComponent : context->subMeshRenderSystem->pool) {
870 if (!renderComponent.isVisible() || !renderComponent.castShadows()) continue;
871 if (!renderComponent.isVisibleInLayer(layerMask)) continue;
872 BoundingBox bb = ::getMeshBounds(context->subMeshRenderSystem, &renderComponent);
873 box += bb;
874 }
875
876 for (auto & sprite : context->spriteRenderSystem->pool) {
877 if (!sprite.isVisible()) continue;
878 if (!sprite.isVisibleInLayer(layerMask)) continue;
879 if ((sprite.renderFlags & RenderFlags::CastShadows) == 0) continue;
880 BoundingBox bb = ::getSpriteBounds(context, &sprite);
881 box += bb;
882 }
883
884 // TODO
885 // for (auto & bounds : getExtensions()) {
886 // BoundingBox bb;
887 // float nearPlaneLimit = 0.f;
888 // bounds->getBounds(context, bb, nearPlaneLimit);
889 // if (!bb.empty()) box += bb;
890 // }
891
892 return box;
893}
894
896{
897 extensions.push_back(bounds);
898}
899
901{
902 extensions.erase(std::remove(extensions.begin(), extensions.end(), bounds), extensions.end());
903}
SizeType size() const
Gets the current size of the pool, the number components currently active.
Definition: ComponentPool.h:77
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 void * getUserData() const noexcept
Get user data.
Definition: Entity.h:137
COGSCORE_DLL_API void removeBoundsExtension(IGetBounds *bounds)
De-register bounds calculation.
Definition: GetBounds.cpp:900
COGSCORE_DLL_API void addBoundsExtension(IGetBounds *bounds)
Definition: GetBounds.cpp:895
RenderLayers layerMask
Layer mask used to determine which RenderComponent instances should be visible in this cameras viewpo...
float nearPlaneLimit
Smallest value allowed to adjust near plane to.
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 Bounds > bounds
Bounds service instance.
Definition: Context.h:216
class EntityStore * store
Entity store.
Definition: Context.h:231
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
Definition: Context.h:186
std::unique_ptr< class Variables > variables
Variables service instance.
Definition: Context.h:180
ComponentModel::Entity * getEntityPtr(const EntityId entityId)
Get a raw pointer to the entity with the given id.
Contains a handle to a Mesh resource to use when rendering using the MeshRenderComponent.
Definition: MeshComponent.h:15
Renders the contents of a MeshComponent using the given materials.
constexpr bool isVisibleInLayer(RenderLayers layerMask) const
Check if the entity should be visible in the given layer mask.
constexpr bool isVisible() const
Check if the entity is visible or not.
RenderFlags renderFlags
Render flags used to control rendering behavior.
The mesh render system handles MeshRenderComponents.
Definition: RenderSystem.h:75
Contains information on how the entity behaves in the scene.
std::vector< EntityPtr > children
Contains all child entities owned by this component.
Renders a mesh with flexible submesh usage.
The submesh render system handles SubMeshRenderComponents.
Log implementation class.
Definition: LogManager.h:139
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
COGSCORE_DLL_API SimplexResult simplexMaximize(Context *context, uintptr_t scratch, double *solutions, const int variableCount, const double *objectives, const int objectiveCount, const double *restrictions, const int restrictionCount, const int maxIterationCount, const bool printSteps=false)
Find the maximum of a linear programming problem using the simplex method.
Definition: Simplex.cpp:452
@ Relative
Position given in normalized device coordinates, with [-1, -1] corresponding to the lower left of the...
@ Pixels
Position given in screen pixels ranging from [0, 0] in the lower left corner to [viewport....
@ World
Position given in world space coordinates.
@ CastShadows
Casts shadows.
COGSCORE_DLL_API size_t simplexScratchSize(const size_t variableCount, const size_t objectiveCount, const size_t restrictionCount)
Number of bytes of scratch array required by simplexMaximize.
Definition: Simplex.cpp:440
RenderLayers
Contains common render layers.
Contains geometry calculations and generation.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
@ Position
Position semantic.
void * data()
Get a pointer to the buffer data.
Definition: Buffer.h:74
Contains a stream of data used by Mesh resources.
Definition: Mesh.h:80
uint32_t numElements
Number of elements of the type given by format contained in data.
Definition: Mesh.h:108
uint32_t offset
Byte offset from the start of the buffer.
Definition: Mesh.h:102
VertexFormatHandle format
A pointer to the format describing the contents of the byte buffer.
Definition: Mesh.h:99
uint32_t stride
Element stride.
Definition: Mesh.h:105
Cogs::Core::ResourceBufferHandle buffer
Data buffer.
Definition: Mesh.h:96
Holds extra housekeeping data for an Entity instance.
Definition: EntityStore.h:24
ContextBase * entityContext
Pointer to the ContextBase the entity belongs to.
Definition: EntityStore.h:28
Meshes contain streams of vertex data in addition to index data and options defining geometry used fo...
Definition: Mesh.h:265
bool hasStream(VertexDataType::EVertexDataType type) const
Check if the Mesh has a DataStream for the given type.
Definition: Mesh.h:933
uint8_t currentLod
The assigned LoD of the current component.
uint8_t selectedLod
The selected LoD of the composite entity.
EVertexDataType
Contains data types.
Definition: Mesh.h:26
const char * name
Name as a color format (using RGBA as channels).
Definition: DataFormat.h:260
Vertex element structure used to describe a single data element in a vertex for the input assembler.
Definition: VertexFormat.h:38
DataFormat format
Format of the element.
Definition: VertexFormat.h:40
uint16_t offset
Offset in bytes from the vertex position in memory.
Definition: VertexFormat.h:39
uint16_t semanticIndex
Index for the semantic mapping.
Definition: VertexFormat.h:42
ElementSemantic semantic
Semantic mapping of the element (position, normal, etc...).
Definition: VertexFormat.h:41
Vertex format structure used to describe a single vertex for the input assembler.
Definition: VertexFormat.h:60
std::vector< VertexElement > elements
Vector containing all vertex elements of this format.
Definition: VertexFormat.h:62