1#include "MarkerPointSetPicker.h"
2#include "MarkerPointSetSystem.h"
5#include "Components/Core/SceneComponent.h"
6#include "Components/Core/TransformComponent.h"
7#include "Scene/RayPick.h"
8#include "Services/DPIService.h"
9#include "Resources/Mesh.h"
10#include "Systems/Core/CameraSystem.h"
11#include "Utilities/Math.h"
13#include "Rendering/IGraphicsDevice.h"
14#include "Rendering/ICapabilities.h"
16#include "Foundation/Logging/Logger.h"
27 std::vector<RayPicking::RayPickHit>& hits;
31 const glm::mat4& worldFromLocal;
32 const float rayFromLocalScale;
34 bool returnChildEntity;
35 bool hitSomething =
false;
37 void operator()(
const glm::vec4& posLocal,
const float distanceLocal,
size_t index) {
38 glm::vec4 posWorld = worldFromLocal * posLocal;
39 const float distanceWorld = rayFromLocalScale * distanceLocal;
40 if (returnClosest && !hits.empty()) {
41 if (hits[0].isBehind(ordinal, distanceWorld)) {
42 hits[0] = { renderComp, returnChildEntity, glm::vec3(posWorld), ordinal, distanceWorld, glm::vec2(
static_cast<float>(index)) };
48 hits.emplace_back(renderComp, returnChildEntity, glm::vec3(posWorld), ordinal, distanceWorld, glm::vec2(
static_cast<float>(index)));
55 void pickFixedScreenSizeDisc(RecordHit& recordHit,
58 const glm::vec3& rayOriginLocal,
59 const glm::vec3& rayUnitDirectionLocal,
60 const glm::mat4& clipFromLocal,
61 const glm::vec2& queryClip,
62 const glm::vec2 viewportSize,
63 const float rayPixelRadius,
64 const float maxDistanceLocal,
68 const float farDistance = std::min(comp.
farDistance, maxDistanceLocal);
69 const float radius = 0.5f * comp.
pointSize;
70 const glm::vec4 viewPlaneLocal = data.viewPlaneLocal;
71 const glm::vec2 pickPosView = queryClip * viewportSize;
73 for (
size_t i = 0; i < comp.
positions.size(); i++) {
74 const glm::vec4 posLocal(comp.
positions[i], 1.f);
75 float viewZ = glm::dot(viewPlaneLocal, posLocal);
77 const glm::vec3 mainCamViewDirLocal(data.viewPlaneLocal);
78 const float rcp = -1.f / glm::dot(mainCamViewDirLocal, rayUnitDirectionLocal);
79 const float mp = glm::dot(mainCamViewDirLocal, rayOriginLocal);
80 if (nearDistance <= viewZ && viewZ <= farDistance) {
81 glm::vec3 pointPosClipSpace = euclidean(clipFromLocal * posLocal);
82 glm::vec2 pointPosView = glm::vec2(pointPosClipSpace) * viewportSize;
83 const float t = rcp * (mp - glm::dot(mainCamViewDirLocal, glm::vec3(posLocal)));
84 float pickableRadius = 2.f * (radius + rayPixelRadius) * dpiScale;
85 if (glm::distance2(pickPosView, pointPosView) <= pickableRadius * pickableRadius) {
86 recordHit(posLocal, t, i);
93 void pickViewAlignedDisc(RecordHit& recordHit,
96 const glm::vec3& rayOriginLocal,
97 const glm::vec3& rayUnitDirectionLocal,
98 const float rayRadiusBiasLocal,
99 const float rayRadiusSlopeLocal,
100 const float maxDistanceLocal)
103 const float farDistance = std::min(comp.
farDistance, maxDistanceLocal);
104 const float radius = 0.5f * comp.
pointSize + rayRadiusBiasLocal;
105 const glm::vec3 mainCamViewDirLocal(data.viewPlaneLocal);
106 const float rcp = -1.f / glm::dot(mainCamViewDirLocal, rayUnitDirectionLocal);
107 const float mp = glm::dot(mainCamViewDirLocal, rayOriginLocal);
108 if (std::isfinite(rcp)) {
109 for (
size_t i = 0; i < comp.
positions.size(); i++) {
110 const glm::vec4 posLocal(comp.
positions[i], 1.f);
111 float viewZ = glm::dot(data.viewPlaneLocal, posLocal);
112 if (nearDistance <= viewZ && viewZ <= farDistance) {
113 const float t = rcp * (mp - glm::dot(mainCamViewDirLocal, glm::vec3(posLocal)));
114 glm::vec3 r = (rayOriginLocal - glm::vec3(posLocal)) + t * rayUnitDirectionLocal;
115 const float adjustedRadius = radius + t * rayRadiusSlopeLocal;
116 if (dot(r, r) <= adjustedRadius * adjustedRadius) {
117 recordHit(posLocal, t, i);
124 void pickObjectSpaceFloorDisc(RecordHit& recordHit,
127 const glm::vec3& rayOriginLocal,
128 const glm::vec3& rayUnitDirectionLocal,
129 const float rayRadiusBiasLocal,
130 const float rayRadiusSlopeLocal,
131 const float maxDistanceLocal)
134 const float farDistance = std::min(comp.
farDistance, maxDistanceLocal);
135 const float pointRadius = 0.5f * comp.
pointSize;
136 const float rcp = -1.f / rayUnitDirectionLocal.z;
137 if (std::isfinite(rcp)) {
138 for (
size_t i = 0; i < comp.
positions.size(); i++) {
139 const glm::vec4 posLocal(comp.
positions[i], 1.f);
140 float viewZ = glm::dot(data.viewPlaneLocal, posLocal);
141 if (nearDistance <= viewZ && viewZ <= farDistance) {
144 const glm::vec3 o3 = rayOriginLocal - glm::vec3(posLocal);
145 const float rayRadiusAtPoint = rayRadiusBiasLocal + rayRadiusSlopeLocal * glm::dot(rayUnitDirectionLocal, -o3);
146 const float testRadius = pointRadius + rayRadiusAtPoint;
148 if (rayRadiusAtPoint < 0.1f * pointRadius) {
151 const float t = rcp * (rayOriginLocal.z - posLocal.z);
152 const glm::vec2 r = (glm::vec2(rayOriginLocal) - glm::vec2(posLocal)) + t * glm::vec2(rayUnitDirectionLocal);
154 if (dot(r, r) <= testRadius * testRadius) {
155 recordHit(posLocal, t, i);
161 const glm::vec2 r(rayUnitDirectionLocal);
162 const glm::vec2 o = glm::vec2(rayOriginLocal) - glm::vec2(posLocal);
165 const float a = glm::dot(r, r);
166 const float b = 2.f * glm::dot(o, r);
167 const float c = glm::dot(o, o) - testRadius * testRadius;
168 const float desc = b * b - 4.f * a * c;
174 const float r0 = (-b - std::copysign(std::sqrt(desc), b)) / (2.f * a);
175 const float r1 = c / (a * r0);
176 float t0 = std::min(r0, r1);
177 float t1 = std::max(r0, r1);
180 if (
float p_rcp = 1.f / rayUnitDirectionLocal.z; std::isfinite(p_rcp)) {
181 float p_r0 = -p_rcp * (o3.z - rayRadiusAtPoint);
182 float p_r1 = -p_rcp * (o3.z + rayRadiusAtPoint);
183 t0 = std::max(t0, std::min(p_r0, p_r1));
184 t1 = std::min(t1, std::max(p_r0, p_r1));
186 else if (o3.z < -rayRadiusAtPoint || rayRadiusAtPoint < o3.z) {
191 recordHit(posLocal, t0, i);
204 const glm::vec2& queryClip,
206 float rayPixelRadius,
210 std::vector<RayPicking::RayPickHit>& hits)
212 const CameraData& cameraData = context->cameraSystem->getData(&camera);
213 const float dpiScale = context->getDefaultView()->
dpiService->getScaleFactor();
215 bool hitSomething =
false;
234 const glm::mat4 worldFromLocal = context->transformSystem->getLocalToWorld(transformComp);
235 const glm::mat4 clipFromLocal = cameraData.rawViewProjection * worldFromLocal;
236 const glm::mat4 localFromClip = glm::inverse(clipFromLocal);
237 const glm::vec2 sigma = glm::vec2(2.f * rayPixelRadius) / cameraData.viewportSize;
238 const glm::vec3 rayOriginLocal = euclidean(localFromClip * glm::vec4(queryClip, -1.f, 1.f));
239 const glm::vec3 rayOriginFarLocal = euclidean(localFromClip * glm::vec4(queryClip, 1.f, 1.f));
240 const glm::vec3 rayDirLocal = rayOriginFarLocal - rayOriginLocal;
241 const glm::vec3 rayOriginSigmaLocal = euclidean(localFromClip * glm::vec4(queryClip + sigma, -1.f, 1.f));
242 const glm::vec3 rayOriginSigmaFarLocal = euclidean(localFromClip * glm::vec4(queryClip + sigma, 1.f, 1.f));
243 const float rayNearSpread = glm::distance(rayOriginLocal, rayOriginSigmaLocal);
244 const float rayFarSpread = glm::distance(rayOriginFarLocal, rayOriginSigmaFarLocal);
245 const float rayLengthRcp = 1.f / glm::length(rayDirLocal);
246 const float rayRadiusBiasLocal = rayNearSpread;
247 const float rayRadiusSlopeLocal = rayLengthRcp * (rayFarSpread - rayNearSpread);
248 const glm::vec3 rayUnitDirLocal = rayLengthRcp * rayDirLocal;
249 const float worldFromLocalScale = glm::determinant(glm::mat3(worldFromLocal));
252 const float maxDistanceLocal = std::min(std::numeric_limits<float>::max(), 2.f * worldFromLocalScale * rayLength);
257 .ordinal = filter.getOrdinal(*renderComp),
258 .renderComp = *renderComp,
259 .worldFromLocal = worldFromLocal,
260 .rayFromLocalScale = worldFromLocalScale,
265 switch (comp.
shape) {
267 pickViewAlignedDisc(recordHit, comp, data, rayOriginLocal, rayUnitDirLocal, rayRadiusBiasLocal, rayRadiusSlopeLocal, maxDistanceLocal);
271 pickObjectSpaceFloorDisc(recordHit, comp, data, rayOriginLocal, rayUnitDirLocal, rayRadiusBiasLocal, rayRadiusSlopeLocal, maxDistanceLocal);
275 pickFixedScreenSizeDisc(recordHit, comp, data, rayOriginLocal, rayUnitDirLocal, clipFromLocal, queryClip, cameraData.viewportSize, rayPixelRadius, maxDistanceLocal, dpiScale);
279 assert(
false &&
"Illegal enum value");
282 hitSomething |= recordHit.hitSomething;
289 const glm::vec3& startPos,
290 const glm::quat& rot,
296 std::vector<RayPicking::RayPickHit>& hits)
298 glm::mat4 worldFromRay = glm::mat4(1.f, 0.f, 0.f, 0.f,
301 startPos.x, startPos.y, startPos.z, 1.f) * glm::toMat4(rot);
303 bool hitSomething =
false;
322 const glm::mat4 worldFromLocal = context->transformSystem->getLocalToWorld(transformComp);
323 const glm::mat4 localFromWorld = glm::inverse(worldFromLocal);
324 const float worldFromLocalScale = glm::determinant(glm::mat3(worldFromLocal));
326 const glm::vec3 rayOriginLocal = euclidean(localFromWorld * (worldFromRay * glm::vec4(0, 0, 0, 1)));
327 const glm::vec3 rayOriginFarLocal = euclidean(localFromWorld * (worldFromRay * glm::vec4(0, 0, -1, 1)));
328 const glm::vec3 rayUnitDirLocal = normalize(rayOriginFarLocal - rayOriginLocal);
329 const float rayRadiusBiasLocal = rayRadius / worldFromLocalScale;
330 const float rayRadiusSlopeLocal = 0.f;
333 const float maxDistanceLocal = std::min(std::numeric_limits<float>::max(), 2.f * worldFromLocalScale * rayLength);
338 .ordinal = filter.getOrdinal(*renderComp),
339 .renderComp = *renderComp,
340 .worldFromLocal = worldFromLocal,
341 .rayFromLocalScale = worldFromLocalScale,
346 switch (comp.
shape) {
348 pickViewAlignedDisc(recordHit, comp, data, rayOriginLocal, rayUnitDirLocal, rayRadiusBiasLocal, rayRadiusSlopeLocal, maxDistanceLocal);
352 pickObjectSpaceFloorDisc(recordHit, comp, data, rayOriginLocal, rayUnitDirLocal, rayRadiusBiasLocal, rayRadiusSlopeLocal, maxDistanceLocal);
359 assert(
false &&
"Illegal enum value");
362 hitSomething |= recordHit.hitSomething;
ComponentType * getComponent() const
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.
! Generate a set of points for the given position array.
float farDistance
Don't display points further than this distance from the camera.
MarkerPointSetShape shape
Specify the shape of the points.
float nearDistance
Don't display points closer than this distance from the camera.
std::vector< glm::vec3 > positions
Marker points positions.
float pointSize
Points size.
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.
std::unique_ptr< class DPIService > dpiService
DPI service instance.
Log implementation class.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
PicksReturned
* Options for returning picking hits.
@ Closest
Return just the closest hit.
PickingFlags
Options for COGS picking.
@ ReturnChildEntity
Return ID if sub-entity picked, not set: return root parent entity.
@ ViewAlignedDisc
Point is a flat disc aligned with the main camera view.
@ ObjectSpaceFloorDisc
Point is a flat disc on the XY-plane, point size is object-space diameter of disc.
@ FixedScreenSizeDisc
Point is a screen-space disc, point size is screen-space diameter of disc, won't work with multiple c...
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Contains data describing a Camera instance and its derived data structured such as matrix data and vi...
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
bool pickRay(Context *context, const glm::vec3 &startPos, const glm::quat &rot, float rayLength, float radius, PickingFlags pickingFlags, PicksReturned returnFlag, const RayPicking::RayPickFilter &filter, std::vector< RayPicking::RayPickHit > &hits) override
Do a ray pick from a position and orientation in world space and return all hits.
bool isUnwantedType(const ComponentModel::Component &comp) const
Helper function used to determine if a given component belongs to an accepted entity type.
RenderLayers layerMask
Limit picking to the specified render layers. Pick all layers by default.