Cogs.Core
HighlightRegionPicker.cpp
1#include "HighlightRegionPicker.h"
2#include "Systems/Core/CameraSystem.h"
3#include "Systems/Core/TransformSystem.h"
4#include "Utilities/Math.h"
5#include "Context.h"
6
7#include "HighlightRegionSystem.h"
8
9#include <algorithm>
10
11namespace {
12 using namespace Cogs::Core;
13
14 bool pickCameraImpl(Context* context,
15 HighlightRegionSystem& hrSystem,
16 const glm::vec3& rayNear_engine,
17 const glm::vec3& rayFar_engine,
18 const glm::mat4* viewMatrix,
19 const float rayLength, // ray length in engine frame
20 const RayPicking::RayPickFilter& filter,
21 std::vector<RayPicking::RayPickHit>& hits)
22 {
23 if (hits.empty()) return false; // No hits to modify
24
25 bool hitSomething = false;
26 bool haveSorted = false;
27 for (const HighlightRegionComponent& hrComp : hrSystem.pool) {
28 if (!hrComp.isPickable() || filter.isUnwantedType(hrComp)) { continue; }
29
30 const RenderComponent* renderComp = hrComp.getComponent<RenderComponent>();
31 if (renderComp && !renderComp->isVisibleInLayer(filter.layerMask)) { continue; }
32
33 const HighlightRegionData& hrData = hrSystem.getData(&hrComp);
34 const TransformComponent* trComp_hr = hrComp.getComponent<TransformComponent>();
35 const glm::mat4& engineFromLocal_hr = context->transformSystem->getLocalToWorld(trComp_hr);
36 const glm::mat4 localFromEngine_hr = glm::inverse(engineFromLocal_hr);
37
38 const glm::vec3 nPos_local = localFromEngine_hr * glm::vec4(rayNear_engine, 1.f);
39 const glm::vec3 fPos_local = localFromEngine_hr * glm::vec4(rayFar_engine, 1.f);
40 const glm::vec3 dir_local = glm::normalize(fPos_local - nPos_local);
41
42 size_t N = std::min(hrComp.position.size(), hrComp.scale.size());
43 for (size_t i = 0; i < N; i++) {
44 const glm::vec3 a = hrComp.position[i] - nPos_local;
45 const float proj_dir_a = dot(a, dir_local);
46 const glm::vec3 nearestPointOnLine = proj_dir_a * dir_local;
47
48 constexpr float sqrt3 = 1.74f;
49 const float d2 = glm::distance2(a, nearestPointOnLine);
50 const float s = sqrt3 * std::max(hrComp.scale[i].x, std::max(hrComp.scale[i].y, hrComp.scale[i].z));
51 if (s * s < d2) continue;
52
53 const HighlightRegionInstanceData& instance = hrData.instanceData[i];
54
55 glm::mat4 localFromInstance = glm::transpose(glm::mat4(instance.data0[0],
56 instance.data0[1],
57 instance.data0[2],
58 glm::vec4(0, 0, 0, 1)));
59
60 glm::mat4 instanceFromLocal = glm::mat4(glm::transpose(glm::mat3(localFromInstance)));
61 instanceFromLocal[3] = glm::vec4(-glm::mat3(instanceFromLocal) * glm::vec3(localFromInstance[3]), 1.f);
62
63 glm::vec3 scale = glm::vec3(instance.data1[0]);
64
65 glm::vec3 npos_instance = glm::vec3(instanceFromLocal * glm::vec4(nPos_local, 1.f));
66 glm::vec3 fpos_instance = glm::vec3(instanceFromLocal * glm::vec4(fPos_local, 1.f));
67
68 glm::vec3 dir = fpos_instance - npos_instance;
69 glm::vec3 invDir = 1.f / dir;
70
71 glm::vec3 nHits = invDir * (-glm::sign(dir) * scale - npos_instance);
72 glm::vec3 fHits = invDir * ( glm::sign(dir) * scale - npos_instance);
73
74 float t_n = std::max(nHits.x, std::max(nHits.y, nHits.z));
75 float t_f = std::min(fHits.x, std::min(fHits.y, fHits.z));
76 if (t_f < t_n) continue;
77
78
79 float enter, exit;
80 if (viewMatrix) {
81 glm::vec4 n = *viewMatrix * glm::vec4((1.f - t_n) * rayNear_engine + t_n * rayFar_engine, 1.f);
82 glm::vec4 f = *viewMatrix * glm::vec4((1.f - t_f) * rayNear_engine + t_f * rayFar_engine, 1.f);
83 enter = -n.z;
84 exit = -f.z;
85 }
86 else {
87 enter = rayLength * t_n;
88 exit = rayLength * t_f;
89 }
90
91 // First hit, sort hits so we can do binary search.
92 if (!haveSorted) {
93 std::sort(hits.begin(), hits.end());
94 haveSorted = true;
95 }
96 if (auto it = std::lower_bound(hits.begin(), hits.end(), enter,
97 [](const RayPicking::RayPickHit& hit, const float& distance)
98 {
99 return hit.distance < distance;
100 }); it != hits.end())
101 {
102 for (; it != hits.end() && it->distance <= exit; ++it) {
103 it->textureCoords.y = static_cast<float>(hrComp.id[i]);
104 hitSomething = true;
105 }
106 }
107 }
108 }
109
110 return hitSomething;
111 }
112
113
114}
115
117 const CameraComponent& camComp,
118 const glm::vec2& normPosition,
119 float /*rayLength*/,
120 float /*radius*/,
121 PickingFlags /*pickingFlags*/,
122 PicksReturned /*returnFlag*/,
123 const RayPicking::RayPickFilter& filter,
124 std::vector<RayPicking::RayPickHit>& hits)
125{
126 if (!hrSystem) return false;
127
128 const CameraData& camData = context->cameraSystem->getData(&camComp);
129 glm::vec3 rayNear_engine = euclidean(camData.inverseViewProjectionMatrix * glm::vec4(normPosition.x, normPosition.y, -1.f, 1.f));
130 glm::vec3 rayFar_engine = euclidean(camData.inverseViewProjectionMatrix * glm::vec4(normPosition.x, normPosition.y, 1.f, 1.f));
131 float rayLength = glm::length(rayFar_engine - rayNear_engine);
132 return pickCameraImpl(context, *hrSystem, rayNear_engine, rayFar_engine, &camData.viewMatrix, rayLength, filter, hits);
133}
134
136 const glm::vec3& startPos,
137 const glm::quat& rot,
138 float rayLength,
139 float /*radius*/,
140 PickingFlags /*pickingFlags*/,
141 PicksReturned /*returnFlag*/,
142 const RayPicking::RayPickFilter& filter,
143 std::vector<RayPicking::RayPickHit>& hits)
144{
145 if (!hrSystem) return false;
146 const glm::vec3 endPos = startPos - rot * glm::vec3(0, 0, rayLength);
147 return pickCameraImpl(context, *hrSystem, startPos, endPos, nullptr, rayLength, filter, hits);
148}
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
std::vector< glm::vec3 > position
Instance positions.
std::vector< glm::vec3 > scale
Instance scale factors.
std::vector< uint32_t > id
Instance ids.
Base component for all rendering content.
constexpr bool isVisibleInLayer(RenderLayers layerMask) const
Check if the entity should be visible in the given layer mask.
constexpr bool isPickable() const
Check if the entity is pickable or not.
Defines a 4x4 transformation matrix for the entity and a global offset for root entities.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
PicksReturned
  * Options for returning picking hits.
Definition: PickingFlags.h:40
PickingFlags
Options for COGS picking.
Definition: PickingFlags.h:12
Contains data describing a Camera instance and its derived data structured such as matrix data and vi...
Definition: CameraSystem.h:67
bool pickCamera(Context *context, const CameraComponent &camera, const glm::vec2 &normPosition, float, float radius, PickingFlags pickingFlags, PicksReturned returnFlag, const RayPicking::RayPickFilter &filter, std::vector< RayPicking::RayPickHit > &hits) override
Do a ray pick from a normalized screen space position in the camera direction and return all hits.
bool pickRay(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.
Definition: RayPick.cpp:188
RenderLayers layerMask
Limit picking to the specified render layers. Pick all layers by default.
Definition: RayPick.h:70