Cogs.Core
BadgeSetPicker.cpp
1#include "BadgeSetPicker.h"
2#include "BadgeSetSystem.h"
3
4#include "Context.h"
5#include "ViewContext.h"
6#include "Components/Core/SceneComponent.h"
7#include "Components/Core/TransformComponent.h"
8#include "Systems/Core/TransformSystem.h"
9#include "Scene/RayPick.h"
10#include "Services/DPIService.h"
11#include "Resources/Mesh.h"
12#include "Systems/Core/CameraSystem.h"
13#include "Utilities/Math.h"
14#include "Systems/ComponentSystem.h"
15
16#include "Rendering/IGraphicsDevice.h"
17#include "Rendering/ICapabilities.h"
18
19#include "Foundation/Logging/Logger.h"
20
21namespace
22{
23 using namespace Cogs::Core;
24
25 Cogs::Logging::Log logger = Cogs::Logging::getLogger("BadgeSetPicker");
26
27 struct BoundingBox
28 {
29 glm::vec2 min;
30 glm::vec2 max;
31 BoundingBox (float minX, float minY, float maxX, float maxY) {
32 min.x = minX;
33 min.y = minY;
34 max.x = maxX;
35 max.y = maxY;
36 }
37
38 bool isPointWithin(const glm::vec2 &mouse) const {
39 return mouse.x >= min.x && mouse.x <= max.x && mouse.y >= min.y && mouse.y <= max.y;
40 }
41 };
42
43 struct RecordHit
44 {
45 std::vector<RayPicking::RayPickHit>& hits;
46 const BadgeSetComponent& comp;
47 RayPicking::Ordinal ordinal;
48 const RenderComponent& renderComp;
49 const glm::mat4& worldFromLocal;
50 const float rayFromLocalScale;
51 bool returnClosest;
52 bool returnChildEntity;
53 bool hitSomething = false;
54
55 void operator()(const glm::vec4& posLocal, const float distanceLocal, size_t index) {
56 glm::vec4 posWorld = worldFromLocal * posLocal;
57 const float distanceWorld = rayFromLocalScale * distanceLocal;
58 if (returnClosest && !hits.empty()) {
59 if (hits[0].isBehind(ordinal, distanceWorld)) {
60 hits[0] = { renderComp, returnChildEntity, glm::vec3(posWorld), ordinal, distanceWorld, glm::vec2(static_cast<float>(index)) };
61 hitSomething = true;
62 }
63 // else, the intersection we found is further, so don't do anything
64 }
65 else {
66 hits.emplace_back(renderComp, returnChildEntity, glm::vec3(posWorld), ordinal, distanceWorld, glm::vec2(static_cast<float>(index)));
67 hitSomething = true;
68 }
69 }
70
71 };
72
73 void pickScaledFixedScreenSizeBox(RecordHit& recordHit,
74 const BadgeSetComponent& comp,
75 const BadgeSetData& data,
76 const glm::vec3& rayOriginLocal,
77 const glm::vec3& rayUnitDirectionLocal,
78 const glm::mat4& clipFromLocal,
79 const glm::vec2& queryClip,
80 const glm::vec2 viewportSize,
81 const float maxDistanceLocal,
82 const float dpiScale)
83 {
84 const float nearDistance = comp.nearVisibilityLimit;
85 const float farDistance = std::min(comp.farVisibilityLimit, maxDistanceLocal);
86 const float size = comp.badgeSize * dpiScale;
87 const glm::vec4 viewPlaneLocal = data.viewPlaneLocal;
88 const glm::vec3 mainCamViewDirLocal(data.viewPlaneLocal);
89 const glm::vec2 pickPosView = queryClip * viewportSize;
90 const float rcp = -1.f / glm::dot(mainCamViewDirLocal, rayUnitDirectionLocal);
91 const float mp = glm::dot(mainCamViewDirLocal, rayOriginLocal);
92 if (std::isfinite(rcp)) {
93 for (size_t i = 0; i < comp.positions.size(); i++) {
94 const glm::vec4 posLocal(comp.positions[i], 1.f);
95 float viewZ = glm::dot(viewPlaneLocal, posLocal);
96 float scaleFactor = viewZ / glm::clamp(viewZ, comp.nearScalingCutoff, comp.farScalingCutoff);
97 float adjustedPointSize = size * scaleFactor;
98
99 // Adjusting the bounding box so that it fits when/if the anchor of the badge changes
100 glm::vec2 corners[2] = {
101 { comp.boundingBoxLower.x - comp.anchorPoint.x, comp.boundingBoxLower.y - (1 - comp.anchorPoint.y)},
102 { comp.boundingBoxUpper.x - comp.anchorPoint.x, comp.boundingBoxUpper.y - (1 - comp.anchorPoint.y)}
103 };
104
105 glm::vec3 cornerBottomLeft = glm::vec3(posLocal) + ((adjustedPointSize * corners[0].x) * data.viewXLocal + (adjustedPointSize * corners[0].y) * data.viewYLocal);
106 glm::vec3 cornerTopRight = glm::vec3(posLocal) + ((adjustedPointSize * corners[1].x) * data.viewXLocal + (adjustedPointSize * corners[1].y) * data.viewYLocal);
107
108 if (nearDistance <= viewZ && viewZ <= farDistance) {
109
110 glm::vec3 cornerBottomLeftClipSpace = euclidean(clipFromLocal * glm::vec4(cornerBottomLeft, 1));
111 glm::vec2 cornerBottomLeftView = glm::vec2(cornerBottomLeftClipSpace) * viewportSize;
112
113 glm::vec3 cornerTopRightClipSpace = euclidean(clipFromLocal * glm::vec4(cornerTopRight, 1));
114 glm::vec2 cornerTopRightView = glm::vec2(cornerTopRightClipSpace) * viewportSize;
115
116 BoundingBox box(cornerBottomLeftView.x, cornerBottomLeftView.y, cornerTopRightView.x, cornerTopRightView.y);
117 const float t = rcp * (mp - glm::dot(glm::vec3(data.viewPlaneLocal), glm::vec3(posLocal)));
118
119 if (box.isPointWithin(pickPosView)) {
120 recordHit(posLocal, t, i);
121 }
122 }
123 }
124 }
125 }
126}
127
128
130 const CameraComponent& camera,
131 const glm::vec2& queryClip,
132 float rayLength,
133 float rayPixelRadius,
134 PickingFlags pickingFlags,
135 PicksReturned returnFlag,
136 const RayPicking::RayPickFilter& filter,
137 std::vector<RayPicking::RayPickHit>& hits)
138{
139 const CameraData& cameraData = context->cameraSystem->getData(&camera);
140 const float dpiScale = context->getDefaultView()->dpiService->getScaleFactor();
141
142 bool hitSomething = false;
143 for (const BadgeSetComponent& comp : system->pool) {
144 if (filter.isUnwantedType(comp)) {
145 continue;
146 }
147
148 const SceneComponent* sceneComp = comp.getComponent<SceneComponent>();
149 if (sceneComp && (!sceneComp->visible || !sceneComp->pickable)) {
150 continue;
151 }
152
153 const RenderComponent* renderComp = comp.getComponent<RenderComponent>();
154 if (renderComp == nullptr || !renderComp->isVisibleInLayer(filter.layerMask)) {
155 continue;
156 }
157
158 const BadgeSetData& data = system->getData(&comp);
159
160 const TransformComponent* transformComp = comp.getComponent<TransformComponent>();
161 const glm::mat4 worldFromLocal = context->transformSystem->getLocalToWorld(transformComp);
162 const glm::mat4 clipFromLocal = cameraData.rawViewProjection * worldFromLocal;
163 const glm::mat4 localFromClip = glm::inverse(clipFromLocal);
164 const glm::vec2 sigma = glm::vec2(2.f * rayPixelRadius) / cameraData.viewportSize;
165 const glm::vec3 rayOriginLocal = euclidean(localFromClip * glm::vec4(queryClip, -1.f, 1.f));
166 const glm::vec3 rayOriginFarLocal = euclidean(localFromClip * glm::vec4(queryClip, 1.f, 1.f));
167 const glm::vec3 rayDirLocal = rayOriginFarLocal - rayOriginLocal;
168 const glm::vec3 rayOriginSigmaLocal = euclidean(localFromClip * glm::vec4(queryClip + sigma, -1.f, 1.f));
169 const glm::vec3 rayOriginSigmaFarLocal = euclidean(localFromClip * glm::vec4(queryClip + sigma, 1.f, 1.f));
170 const float rayLengthRcp = 1.f / glm::length(rayDirLocal);
171 const glm::vec3 rayUnitDirLocal = rayLengthRcp * rayDirLocal;
172 const float worldFromLocalScale = glm::determinant(glm::mat3(worldFromLocal));
173
174 // The min below is to work-around local scale becoming infinity if raylength is max_float.
175 const float maxDistanceLocal = std::min(std::numeric_limits<float>::max(), 2.f * worldFromLocalScale * rayLength);
176
177 RecordHit recordHit{
178 .hits = hits,
179 .comp = comp,
180 .ordinal = filter.getOrdinal(*renderComp),
181 .renderComp = *renderComp,
182 .worldFromLocal = worldFromLocal,
183 .rayFromLocalScale = worldFromLocalScale,
184 .returnClosest = returnFlag == PicksReturned::Closest,
185 .returnChildEntity = (pickingFlags & PickingFlags::ReturnChildEntity) == PickingFlags::ReturnChildEntity
186 };
187
188 pickScaledFixedScreenSizeBox(recordHit, comp, data, rayOriginLocal, rayUnitDirLocal, clipFromLocal, queryClip, cameraData.viewportSize, maxDistanceLocal, dpiScale);
189
190 hitSomething |= recordHit.hitSomething;
191 }
192
193 return hitSomething;
194}
ComponentType * getComponent() const
Definition: Component.h:159
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
Base component for all rendering content.
constexpr bool isVisibleInLayer(RenderLayers layerMask) const
Check if the entity should be visible in the given layer mask.
Contains information on how the entity behaves in the scene.
bool visible
If the entity this component is a member of should be visible.
bool pickable
If the entity this component is a member of should be pickable.
Defines a 4x4 transformation matrix for the entity and a global offset for root entities.
std::unique_ptr< class DPIService > dpiService
DPI service instance.
Definition: ViewContext.h:71
Log implementation class.
Definition: LogManager.h:140
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
PicksReturned
  * Options for returning picking hits.
Definition: PickingFlags.h:40
@ Closest
Return just the closest hit.
PickingFlags
Options for COGS picking.
Definition: PickingFlags.h:12
@ ReturnChildEntity
Return ID if sub-entity picked, not set: return root parent entity.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181
Component for showing lots of badges.
float nearScalingCutoff
Determines the near limit where the badge stops scaling freely.
float farScalingCutoff
Determines the far limit where the badge stops scaling freely.
std::vector< glm::vec3 > positions
Badge positions.
float farVisibilityLimit
Don't display badges further than this distance from the camera.
glm::vec2 boundingBoxLower
Position of the lower corner of the bounding box.
float nearVisibilityLimit
Don't display badges closer than this distance from the camera.
float badgeSize
Size of the badge.
glm::vec2 boundingBoxUpper
Position of the upper corner of the bounding box.
glm::vec2 anchorPoint
The location where the badge is anchored.
bool pickCamera(Context *context, const CameraComponent &camera, const glm::vec2 &queryClip, float, float rayRadius, PickingFlags pickingFlags, PicksReturned returnFlag, const RayPicking::RayPickFilter &filter, std::vector< RayPicking::RayPickHit > &hits) override
Contains data describing a Camera instance and its derived data structured such as matrix data and vi...
Definition: CameraSystem.h:67
bool isUnwantedType(const ComponentModel::Component &comp) const
Helper function used to determine if a given component belongs to an accepted entity type.
Definition: RayPick.cpp:202
RenderLayers layerMask
Limit picking to the specified render layers. Pick all layers by default.
Definition: RayPick.h:62