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;
29 const glm::mat4& worldFromLocal;
30 const float rayFromLocalScale;
32 bool returnChildEntity;
33 bool hitSomething =
false;
35 void operator()(
const glm::vec4& posLocal,
const float distanceLocal,
size_t index) {
36 glm::vec4 posWorld = worldFromLocal * posLocal;
37 const float distanceWorld = rayFromLocalScale * distanceLocal;
38 if (returnClosest && !hits.empty()) {
39 if (distanceWorld < hits[0].distance) {
40 hits[0] = { comp, returnChildEntity, glm::vec3(posWorld), distanceWorld, glm::vec2(
static_cast<float>(index)) };
46 hits.emplace_back(comp, returnChildEntity, glm::vec3(posWorld), distanceWorld, glm::vec2(
static_cast<float>(index)));
53 void pickFixedScreenSizeDisc(RecordHit& recordHit,
56 const glm::vec3& rayOriginLocal,
57 const glm::vec3& rayUnitDirectionLocal,
58 const glm::mat4& clipFromLocal,
59 const glm::vec2& queryClip,
60 const glm::vec2 viewportSize,
61 const float rayPixelRadius,
62 const float maxDistanceLocal,
66 const float farDistance = std::min(comp.
farDistance, maxDistanceLocal);
67 const float radius = 0.5f * comp.
pointSize;
68 const glm::vec4 viewPlaneLocal = data.viewPlaneLocal;
69 const glm::vec2 pickPosView = queryClip * viewportSize;
71 for (
size_t i = 0; i < comp.
positions.size(); i++) {
72 const glm::vec4 posLocal(comp.
positions[i], 1.f);
73 float viewZ = glm::dot(viewPlaneLocal, posLocal);
75 const glm::vec3 mainCamViewDirLocal(data.viewPlaneLocal);
76 const float rcp = -1.f / glm::dot(mainCamViewDirLocal, rayUnitDirectionLocal);
77 const float mp = glm::dot(mainCamViewDirLocal, rayOriginLocal);
78 if (nearDistance <= viewZ && viewZ <= farDistance) {
79 glm::vec3 pointPosClipSpace = euclidean(clipFromLocal * posLocal);
80 glm::vec2 pointPosView = glm::vec2(pointPosClipSpace) * viewportSize;
81 const float t = rcp * (mp - glm::dot(mainCamViewDirLocal, glm::vec3(posLocal)));
82 float pickableRadius = 2.f * (radius + rayPixelRadius) * dpiScale;
83 if (glm::distance2(pickPosView, pointPosView) <= pickableRadius * pickableRadius) {
84 recordHit(posLocal, t, i);
91 void pickViewAlignedDisc(RecordHit& recordHit,
94 const glm::vec3& rayOriginLocal,
95 const glm::vec3& rayUnitDirectionLocal,
96 const float rayRadiusBiasLocal,
97 const float rayRadiusSlopeLocal,
98 const float maxDistanceLocal)
101 const float farDistance = std::min(comp.
farDistance, maxDistanceLocal);
102 const float radius = 0.5f * comp.
pointSize + rayRadiusBiasLocal;
103 const glm::vec3 mainCamViewDirLocal(data.viewPlaneLocal);
104 const float rcp = -1.f / glm::dot(mainCamViewDirLocal, rayUnitDirectionLocal);
105 const float mp = glm::dot(mainCamViewDirLocal, rayOriginLocal);
106 if (std::isfinite(rcp)) {
107 for (
size_t i = 0; i < comp.
positions.size(); i++) {
108 const glm::vec4 posLocal(comp.
positions[i], 1.f);
109 float viewZ = glm::dot(data.viewPlaneLocal, posLocal);
110 if (nearDistance <= viewZ && viewZ <= farDistance) {
111 const float t = rcp * (mp - glm::dot(mainCamViewDirLocal, glm::vec3(posLocal)));
112 glm::vec3 r = (rayOriginLocal - glm::vec3(posLocal)) + t * rayUnitDirectionLocal;
113 const float adjustedRadius = radius + t * rayRadiusSlopeLocal;
114 if (dot(r, r) <= adjustedRadius * adjustedRadius) {
115 recordHit(posLocal, t, i);
122 void pickObjectSpaceFloorDisc(RecordHit& recordHit,
125 const glm::vec3& rayOriginLocal,
126 const glm::vec3& rayUnitDirectionLocal,
127 const float rayRadiusBiasLocal,
128 const float rayRadiusSlopeLocal,
129 const float maxDistanceLocal)
132 const float farDistance = std::min(comp.
farDistance, maxDistanceLocal);
133 const float pointRadius = 0.5f * comp.
pointSize;
134 const float rcp = -1.f / rayUnitDirectionLocal.z;
135 if (std::isfinite(rcp)) {
136 for (
size_t i = 0; i < comp.
positions.size(); i++) {
137 const glm::vec4 posLocal(comp.
positions[i], 1.f);
138 float viewZ = glm::dot(data.viewPlaneLocal, posLocal);
139 if (nearDistance <= viewZ && viewZ <= farDistance) {
142 const glm::vec3 o3 = rayOriginLocal - glm::vec3(posLocal);
143 const float rayRadiusAtPoint = rayRadiusBiasLocal + rayRadiusSlopeLocal * glm::dot(rayUnitDirectionLocal, -o3);
144 const float testRadius = pointRadius + rayRadiusAtPoint;
146 if (rayRadiusAtPoint < 0.1f * pointRadius) {
149 const float t = rcp * (rayOriginLocal.z - posLocal.z);
150 const glm::vec2 r = (glm::vec2(rayOriginLocal) - glm::vec2(posLocal)) + t * glm::vec2(rayUnitDirectionLocal);
152 if (dot(r, r) <= testRadius * testRadius) {
153 recordHit(posLocal, t, i);
159 const glm::vec2 r(rayUnitDirectionLocal);
160 const glm::vec2 o = glm::vec2(rayOriginLocal) - glm::vec2(posLocal);
163 const float a = glm::dot(r, r);
164 const float b = 2.f * glm::dot(o, r);
165 const float c = glm::dot(o, o) - testRadius * testRadius;
166 const float desc = b * b - 4.f * a * c;
172 const float r0 = (-b - std::copysign(std::sqrt(desc), b)) / (2.f * a);
173 const float r1 = c / (a * r0);
174 float t0 = std::min(r0, r1);
175 float t1 = std::max(r0, r1);
178 if (
float p_rcp = 1.f / rayUnitDirectionLocal.z; std::isfinite(p_rcp)) {
179 float p_r0 = -p_rcp * (o3.z - rayRadiusAtPoint);
180 float p_r1 = -p_rcp * (o3.z + rayRadiusAtPoint);
181 t0 = std::max(t0, std::min(p_r0, p_r1));
182 t1 = std::min(t1, std::max(p_r0, p_r1));
184 else if (o3.z < -rayRadiusAtPoint || rayRadiusAtPoint < o3.z) {
189 recordHit(posLocal, t0, i);
202 const glm::vec2& queryClip,
204 float rayPixelRadius,
208 std::vector<RayPicking::RayPickHit>& hits)
210 const CameraData& cameraData = context->cameraSystem->getData(&camera);
211 const float dpiScale = context->getDefaultView()->
dpiService->getScaleFactor();
213 bool hitSomething =
false;
232 const glm::mat4 worldFromLocal = context->transformSystem->getLocalToWorld(transformComp);
233 const glm::mat4 clipFromLocal = cameraData.rawViewProjection * worldFromLocal;
234 const glm::mat4 localFromClip = glm::inverse(clipFromLocal);
235 const glm::vec2 sigma = glm::vec2(2.f * rayPixelRadius) / cameraData.viewportSize;
236 const glm::vec3 rayOriginLocal = euclidean(localFromClip * glm::vec4(queryClip, -1.f, 1.f));
237 const glm::vec3 rayOriginFarLocal = euclidean(localFromClip * glm::vec4(queryClip, 1.f, 1.f));
238 const glm::vec3 rayDirLocal = rayOriginFarLocal - rayOriginLocal;
239 const glm::vec3 rayOriginSigmaLocal = euclidean(localFromClip * glm::vec4(queryClip + sigma, -1.f, 1.f));
240 const glm::vec3 rayOriginSigmaFarLocal = euclidean(localFromClip * glm::vec4(queryClip + sigma, 1.f, 1.f));
241 const float rayNearSpread = glm::distance(rayOriginLocal, rayOriginSigmaLocal);
242 const float rayFarSpread = glm::distance(rayOriginFarLocal, rayOriginSigmaFarLocal);
243 const float rayLengthRcp = 1.f / glm::length(rayDirLocal);
244 const float rayRadiusBiasLocal = rayNearSpread;
245 const float rayRadiusSlopeLocal = rayLengthRcp * (rayFarSpread - rayNearSpread);
246 const glm::vec3 rayUnitDirLocal = rayLengthRcp * rayDirLocal;
247 const float worldFromLocalScale = glm::determinant(glm::mat3(worldFromLocal));
250 const float maxDistanceLocal = std::min(std::numeric_limits<float>::max(), 2.f * worldFromLocalScale * rayLength);
255 .worldFromLocal = worldFromLocal,
256 .rayFromLocalScale = worldFromLocalScale,
261 switch (comp.
shape) {
263 pickViewAlignedDisc(recordHit, comp, data, rayOriginLocal, rayUnitDirLocal, rayRadiusBiasLocal, rayRadiusSlopeLocal, maxDistanceLocal);
267 pickObjectSpaceFloorDisc(recordHit, comp, data, rayOriginLocal, rayUnitDirLocal, rayRadiusBiasLocal, rayRadiusSlopeLocal, maxDistanceLocal);
271 pickFixedScreenSizeDisc(recordHit, comp, data, rayOriginLocal, rayUnitDirLocal, clipFromLocal, queryClip, cameraData.viewportSize, rayPixelRadius, maxDistanceLocal, dpiScale);
275 assert(
false &&
"Illegal enum value");
278 hitSomething |= recordHit.hitSomething;
282 std::sort(hits.begin(), hits.end());
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 .worldFromLocal = worldFromLocal,
339 .rayFromLocalScale = worldFromLocalScale,
344 switch (comp.
shape) {
346 pickViewAlignedDisc(recordHit, comp, data, rayOriginLocal, rayUnitDirLocal, rayRadiusBiasLocal, rayRadiusSlopeLocal, maxDistanceLocal);
350 pickObjectSpaceFloorDisc(recordHit, comp, data, rayOriginLocal, rayUnitDirLocal, rayRadiusBiasLocal, rayRadiusSlopeLocal, maxDistanceLocal);
357 assert(
false &&
"Illegal enum value");
360 hitSomething |= recordHit.hitSomething;
364 std::sort(hits.begin(), hits.end());
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.
@ AllSorted
Return all hits sorted based on distance from the ray start, closest first.
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.