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 == nullptr || !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 RayPicking::Ordinal ordinal = filter.getOrdinal(*renderComp);
43
44 size_t N = std::min(hrComp.position.size(), hrComp.scale.size());
45 for (size_t i = 0; i < N; i++) {
46 const glm::vec3 a = hrComp.position[i] - nPos_local;
47 const float proj_dir_a = dot(a, dir_local);
48 const glm::vec3 nearestPointOnLine = proj_dir_a * dir_local;
49
50 constexpr float sqrt3 = 1.74f;
51 const float d2 = glm::distance2(a, nearestPointOnLine);
52 const float s = sqrt3 * std::max(hrComp.scale[i].x, std::max(hrComp.scale[i].y, hrComp.scale[i].z));
53 if (s * s < d2) continue;
54
55 const HighlightRegionInstanceData& instance = hrData.instanceData[i];
56
57 glm::mat4 localFromInstance = glm::transpose(glm::mat4(instance.data0[0],
58 instance.data0[1],
59 instance.data0[2],
60 glm::vec4(0, 0, 0, 1)));
61
62 glm::mat4 instanceFromLocal = glm::mat4(glm::transpose(glm::mat3(localFromInstance)));
63 instanceFromLocal[3] = glm::vec4(-glm::mat3(instanceFromLocal) * glm::vec3(localFromInstance[3]), 1.f);
64
65 glm::vec3 scale = glm::vec3(instance.data1[0]);
66
67 glm::vec3 npos_instance = glm::vec3(instanceFromLocal * glm::vec4(nPos_local, 1.f));
68 glm::vec3 fpos_instance = glm::vec3(instanceFromLocal * glm::vec4(fPos_local, 1.f));
69
70 glm::vec3 dir = fpos_instance - npos_instance;
71 glm::vec3 invDir = 1.f / dir;
72
73 glm::vec3 nHits = invDir * (-glm::sign(dir) * scale - npos_instance);
74 glm::vec3 fHits = invDir * ( glm::sign(dir) * scale - npos_instance);
75
76 float t_n = std::max(nHits.x, std::max(nHits.y, nHits.z));
77 float t_f = std::min(fHits.x, std::min(fHits.y, fHits.z));
78 if (t_f < t_n) continue;
79
80
81 float enter, exit;
82 if (viewMatrix) {
83 glm::vec4 n = *viewMatrix * glm::vec4((1.f - t_n) * rayNear_engine + t_n * rayFar_engine, 1.f);
84 glm::vec4 f = *viewMatrix * glm::vec4((1.f - t_f) * rayNear_engine + t_f * rayFar_engine, 1.f);
85 enter = -n.z;
86 exit = -f.z;
87 }
88 else {
89 enter = rayLength * t_n;
90 exit = rayLength * t_f;
91 }
92
93 // First hit, sort hits so we can do binary search.
94 if (!haveSorted) {
95 std::sort(hits.begin(), hits.end());
96 haveSorted = true;
97 }
98 if (auto it = std::lower_bound(hits.begin(), hits.end(), enter,
99 [ordinal](const RayPicking::RayPickHit& hit, const float& distance)
100 {
101 // hit.distance < distance;
102 return hit.isInfront(ordinal, distance);
103 }); it != hits.end())
104 {
105 // it->distance <= exit
106 for (; it != hits.end() && !it->isBehind(ordinal, exit); ++it) {
107 it->textureCoords.y = static_cast<float>(hrComp.id[i]);
108 hitSomething = true;
109 }
110 }
111 }
112 }
113
114 return hitSomething;
115 }
116
117
118}
119
121 const CameraComponent& camComp,
122 const glm::vec2& normPosition,
123 float /*rayLength*/,
124 float /*radius*/,
125 PickingFlags /*pickingFlags*/,
126 PicksReturned /*returnFlag*/,
127 const RayPicking::RayPickFilter& filter,
128 std::vector<RayPicking::RayPickHit>& hits)
129{
130 if (!hrSystem) return false;
131
132 const CameraData& camData = context->cameraSystem->getData(&camComp);
133 glm::vec3 rayNear_engine = euclidean(camData.inverseViewProjectionMatrix * glm::vec4(normPosition.x, normPosition.y, -1.f, 1.f));
134 glm::vec3 rayFar_engine = euclidean(camData.inverseViewProjectionMatrix * glm::vec4(normPosition.x, normPosition.y, 1.f, 1.f));
135 float rayLength = glm::length(rayFar_engine - rayNear_engine);
136 return pickCameraImpl(context, *hrSystem, rayNear_engine, rayFar_engine, &camData.viewMatrix, rayLength, filter, hits);
137}
138
140 const glm::vec3& startPos,
141 const glm::quat& rot,
142 float rayLength,
143 float /*radius*/,
144 PickingFlags /*pickingFlags*/,
145 PicksReturned /*returnFlag*/,
146 const RayPicking::RayPickFilter& filter,
147 std::vector<RayPicking::RayPickHit>& hits)
148{
149 if (!hrSystem) return false;
150 const glm::vec3 endPos = startPos - rot * glm::vec3(0, 0, rayLength);
151 return pickCameraImpl(context, *hrSystem, startPos, endPos, nullptr, rayLength, filter, hits);
152}
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:202
RenderLayers layerMask
Limit picking to the specified render layers. Pick all layers by default.
Definition: RayPick.h:62