1#include "BadgeSetPicker.h"
2#include "BadgeSetSystem.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 "Resources/Texture.h"
13#include "Systems/Core/CameraSystem.h"
14#include "Utilities/Math.h"
15#include "Systems/ComponentSystem.h"
17#include "Rendering/IGraphicsDevice.h"
18#include "Rendering/ICapabilities.h"
20#include "Foundation/Logging/Logger.h"
32 BoundingBox (
float minX,
float minY,
float maxX,
float maxY) {
39 bool isPointWithin(
const glm::vec2 &mouse)
const {
40 return mouse.x >= min.x && mouse.x <= max.x && mouse.y >= min.y && mouse.y <= max.y;
46 std::vector<RayPicking::RayPickHit>& hits;
50 const glm::mat4& worldFromLocal;
51 const float rayFromLocalScale;
53 bool returnChildEntity;
54 bool hitSomething =
false;
56 void operator()(
const glm::vec4& posLocal,
const float distanceLocal,
size_t index) {
57 glm::vec4 posWorld = worldFromLocal * posLocal;
58 const float distanceWorld = rayFromLocalScale * distanceLocal;
59 if (returnClosest && !hits.empty()) {
60 if (hits[0].isBehind(ordinal, distanceWorld)) {
61 hits[0] = { renderComp, returnChildEntity, glm::vec3(posWorld), ordinal, distanceWorld, glm::vec2(
static_cast<float>(index)) };
67 hits.emplace_back(renderComp, returnChildEntity, glm::vec3(posWorld), ordinal, distanceWorld, glm::vec2(
static_cast<float>(index)));
75 constexpr int styleMaskOpaque = 1;
77 constexpr int styleMaskTransparent = 0;
79 constexpr int styleMaskUnknown = -1;
93 int sampleStyleMask(
const BadgeSetComponent& comp,
size_t badgeIndex,
const glm::vec2& quadUV)
95 const std::vector<int>& mask = comp.
masks;
96 if (mask.size() < 3) {
97 return styleMaskUnknown;
100 const int width = mask[0];
101 const int height = mask[1];
102 const int layers = mask[2];
103 if (width <= 0 || height <= 0 || layers <= 0) {
104 return styleMaskUnknown;
109 if (badgeIndex < comp.
styles.size()) {
110 const int styleIndex = comp.
styles[badgeIndex];
111 if (styleIndex < 0 || styleIndex >= layers) {
112 return styleMaskUnknown;
117 const size_t wordsPerLayer = (
static_cast<size_t>(width) *
static_cast<size_t>(height) + 31u) / 32u;
118 const size_t expectedSize = 3u + wordsPerLayer *
static_cast<size_t>(layers);
119 if (mask.size() < expectedSize) {
120 return styleMaskUnknown;
123 const float u = glm::clamp(quadUV.x, 0.f, 1.f);
124 const float v = glm::clamp(quadUV.y, 0.f, 1.f);
125 const int px = std::min(
static_cast<int>(u *
static_cast<float>(width)), width - 1);
127 const int py = std::min(
static_cast<int>((1.f - v) *
static_cast<float>(height)), height - 1);
128 const size_t p =
static_cast<size_t>(py) *
static_cast<size_t>(width) +
static_cast<size_t>(px);
129 const size_t layerBase = 3u + wordsPerLayer *
static_cast<size_t>(layer);
130 const uint32_t word =
static_cast<uint32_t
>(mask[layerBase + (p >> 5)]);
131 const bool opaque = (word & (1u << (p & 31u))) != 0u;
132 return opaque ? styleMaskOpaque : styleMaskTransparent;
135 void pickScaledFixedScreenSizeBox(RecordHit& recordHit,
138 const glm::vec3& rayOriginLocal,
139 const glm::vec3& rayUnitDirectionLocal,
140 const glm::mat4& clipFromLocal,
141 const glm::vec2& queryClip,
142 const glm::vec2 viewportSize,
143 const float maxDistanceLocal,
144 const float dpiScale)
148 const float size = comp.
badgeSize * dpiScale;
149 const glm::vec4 viewPlaneLocal = data.viewPlaneLocal;
150 const glm::vec3 mainCamViewDirLocal(data.viewPlaneLocal);
151 const glm::vec2 pickPosView = queryClip * viewportSize;
152 const float rcp = -1.f / glm::dot(mainCamViewDirLocal, rayUnitDirectionLocal);
153 const float mp = glm::dot(mainCamViewDirLocal, rayOriginLocal);
154 if (std::isfinite(rcp)) {
155 for (
size_t i = 0; i < comp.
positions.size(); i++) {
156 const glm::vec4 posLocal(comp.
positions[i], 1.f);
157 float viewZ = glm::dot(viewPlaneLocal, posLocal);
159 float adjustedPointSize = size * scaleFactor;
162 glm::vec2 corners[2] = {
167 glm::vec3 cornerBottomLeft = glm::vec3(posLocal) + ((adjustedPointSize * corners[0].x) * data.viewXLocal + (adjustedPointSize * corners[0].y) * data.viewYLocal);
168 glm::vec3 cornerTopRight = glm::vec3(posLocal) + ((adjustedPointSize * corners[1].x) * data.viewXLocal + (adjustedPointSize * corners[1].y) * data.viewYLocal);
170 if (nearDistance <= viewZ && viewZ <= farDistance) {
172 glm::vec3 cornerBottomLeftClipSpace = euclidean(clipFromLocal * glm::vec4(cornerBottomLeft, 1));
173 glm::vec2 cornerBottomLeftView = glm::vec2(cornerBottomLeftClipSpace) * viewportSize;
175 glm::vec3 cornerTopRightClipSpace = euclidean(clipFromLocal * glm::vec4(cornerTopRight, 1));
176 glm::vec2 cornerTopRightView = glm::vec2(cornerTopRightClipSpace) * viewportSize;
178 BoundingBox box(cornerBottomLeftView.x, cornerBottomLeftView.y, cornerTopRightView.x, cornerTopRightView.y);
179 const float t = rcp * (mp - glm::dot(glm::vec3(data.viewPlaneLocal), glm::vec3(posLocal)));
181 if (box.isPointWithin(pickPosView)) {
190 const glm::vec2 boxSpan = cornerTopRightView - cornerBottomLeftView;
191 const glm::vec2 boxFraction = {
192 boxSpan.x != 0.f ? (pickPosView.x - cornerBottomLeftView.x) / boxSpan.x : 0.f,
193 boxSpan.y != 0.f ? (pickPosView.y - cornerBottomLeftView.y) / boxSpan.y : 0.f,
196 const int maskState = sampleStyleMask(comp, i, texCoord);
197 if (maskState != styleMaskTransparent) {
198 recordHit(posLocal, t, i);
210 const glm::vec2& queryClip,
212 float rayPixelRadius,
216 std::vector<RayPicking::RayPickHit>& hits)
218 const CameraData& cameraData = context->cameraSystem->getData(&camera);
219 const float dpiScale = context->getDefaultView()->
dpiService->getScaleFactor();
221 bool hitSomething =
false;
240 const glm::mat4 worldFromLocal = context->transformSystem->getLocalToWorld(transformComp);
241 const glm::mat4 clipFromLocal = cameraData.rawViewProjection * worldFromLocal;
242 const glm::mat4 localFromClip = glm::inverse(clipFromLocal);
243 const glm::vec2 sigma = glm::vec2(2.f * rayPixelRadius) / cameraData.viewportSize;
244 const glm::vec3 rayOriginLocal = euclidean(localFromClip * glm::vec4(queryClip, -1.f, 1.f));
245 const glm::vec3 rayOriginFarLocal = euclidean(localFromClip * glm::vec4(queryClip, 1.f, 1.f));
246 const glm::vec3 rayDirLocal = rayOriginFarLocal - rayOriginLocal;
247 const glm::vec3 rayOriginSigmaLocal = euclidean(localFromClip * glm::vec4(queryClip + sigma, -1.f, 1.f));
248 const glm::vec3 rayOriginSigmaFarLocal = euclidean(localFromClip * glm::vec4(queryClip + sigma, 1.f, 1.f));
249 const float rayLengthRcp = 1.f / glm::length(rayDirLocal);
250 const glm::vec3 rayUnitDirLocal = rayLengthRcp * rayDirLocal;
251 const float worldFromLocalScale = glm::determinant(glm::mat3(worldFromLocal));
254 const float maxDistanceLocal = std::min(std::numeric_limits<float>::max(), 2.f * worldFromLocalScale * rayLength);
259 .ordinal = filter.getOrdinal(*renderComp),
260 .renderComp = *renderComp,
261 .worldFromLocal = worldFromLocal,
262 .rayFromLocalScale = worldFromLocalScale,
267 pickScaledFixedScreenSizeBox(recordHit, comp, data, rayOriginLocal, rayUnitDirLocal, clipFromLocal, queryClip, cameraData.viewportSize, maxDistanceLocal, dpiScale);
269 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.
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.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
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.
std::vector< int > masks
Packed alpha bit-mask for the style textures, used by picking to reject transparent pixels.
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.
std::vector< int > styles
Style (index) used for background behind icon.
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...
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.