Cogs.Core
SceneFunctions.cpp
1#include "SceneFunctions.h"
2
3#include "Utilities/Math.h"
4
5#include "Scene/RayPick.h"
6#include "Scene/GetBounds.h"
7#include "Scene/GetDepth.h"
8
9#include "Context.h"
10#include "Engine.h"
11#include "EntityStore.h"
12#include "ViewContext.h"
13
14#include "Systems/Core/TransformSystem.h"
15#include "Systems/Core/CameraSystem.h"
16
17#include "Utilities/CameraUtils.h"
18
19#include "Renderer/IRenderer.h"
20
21#include "Math/Projection.h"
22
23#include <glm/glm.hpp>
24#include <set>
25#include <tuple>
26#include <type_traits>
27
28using namespace Cogs::Core;
29
30namespace {
33 inline glm::vec3* castToVec3(float* input) {
34 return reinterpret_cast<glm::vec3*>(input);
35 }
36 inline const glm::vec3* castToVec3(const float* input) {
37 return reinterpret_cast<const glm::vec3*>(input);
38 }
39
40 inline glm::dvec3* castToDVec3(double* input) {
41 return reinterpret_cast<glm::dvec3*>(input);
42 }
43 inline const glm::dvec3* castToDVec3(const double* input) {
44 return reinterpret_cast<const glm::dvec3*>(input);
45 }
46
47 inline const glm::quat* castToQuat(const float* input) {
48 return reinterpret_cast<const glm::quat*>(input);
49 }
50
51 // Utility for returning All picks, optionally filtered.
52 template<typename CoordType>
53 int copyPickResults(const Context* context, const std::vector<RayPicking::RayPickHit>& hits, const int bufferSize,
54 EntityId* rootIds, EntityId* entityIds, CoordType* coordinates, float* textureCoords)
55 {
56 size_t resultsLength = std::min(hits.size(), static_cast<size_t>(bufferSize));
57 for (size_t i = 0; i < resultsLength; i++) {
58 // Fill buffers with hit data
59 const RayPicking::RayPickHit& hit = hits[i];
60 if (rootIds) {
61 rootIds[i] = hit.root;
62 }
63 if (entityIds) {
64 entityIds[i] = hit.entity;
65 }
66 if (coordinates) {
67 if constexpr (std::is_same_v<CoordType, float>) {
68 coordinates[3 * i + 0] = hit.position.x;
69 coordinates[3 * i + 1] = hit.position.y;
70 coordinates[3 * i + 2] = hit.position.z;
71 }
72 else if constexpr (std::is_same_v<CoordType, double>) {
73 const glm::dvec3 wc = context->transformSystem->worldFromEngineCoords(hit.position);
74 coordinates[3 * i + 0] = wc.x;
75 coordinates[3 * i + 1] = wc.y;
76 coordinates[3 * i + 2] = wc.z;
77 }
78 }
79 if (textureCoords) {
80 textureCoords[2 * i + 0] = hit.textureCoords.x;
81 textureCoords[2 * i + 1] = hit.textureCoords.y;
82 }
83 }
84
85 // Return total number of hits, even if the buffer is insufficient,
86 // so that the client can try again with a buffer that is large enough.
87 return static_cast<int>(hits.size());
88 }
89}
90
91void setOrigin(BridgeContext* ctx, const double * origin)
92{
93 Context* context = static_cast<Context *>(ctx);
94
95 context->transformSystem->setOrigin(*castToDVec3(origin));
96}
97
98void getOrigin(BridgeContext* ctx, double* origin)
99{
100 Context* context = static_cast<Context*>(ctx);
101
102 auto sceneOrigin = context->transformSystem->getOrigin();
103 origin[0] = sceneOrigin.x;
104 origin[1] = sceneOrigin.y;
105 origin[2] = sceneOrigin.z;
106}
107
108CogsBool getProjectedCoordinates(BridgeContext* ctx, int x, int y, const float* normal, const float* position, float* coordinates) {
109 ViewContext* view = static_cast<Context*>(ctx)->getDefaultView();
110
111 return view ? getProjectedCoordinatesInView(view, x, y, normal, position, coordinates) : false;
112}
113
114CogsBool getProjectedCoordinatesWorld(BridgeContext* ctx, float x, float y, const float* normal, const double* worldPosition, double* coordinates) {
115 ViewContext* view = static_cast<Context*>(ctx)->getDefaultView();
116
117 return view ? getProjectedCoordinatesInViewWorld(view, x, y, normal, worldPosition, coordinates) : false;
118}
119
120CogsBool getProjectedCoordinatesWorldUnit(BridgeContext* ctx, float x, float y, const float* normal, const double* worldPosition, double* worldResult)
121{
122 Context* context = static_cast<Context*>(ctx);
123 ViewContext* view = context->getDefaultView();
124 const CameraData& camData = context->cameraSystem->getData(view->getCameraComponent());
125 glm::vec3 enginePosition = context->transformSystem->engineFromWorldCoords(*castToDVec3(worldPosition));
126 glm::vec3 engineResult;
127 if (Cogs::Utility::CameraUtils::getProjectedCoordinates(glm::vec2{ x, y } *camData.viewportSize,
128 camData,
129 normal ? *castToVec3(normal) : glm::vec3(0.f),
130 enginePosition,
131 engineResult))
132 {
133 *castToDVec3(worldResult) = context->transformSystem->worldFromEngineCoords(engineResult);
134 return true;
135 }
136 return false;
137}
138
139
140CogsBool getProjectedCoordinatesInView(BridgeView* bv, int x, int y, const float* normal, const float* position, float* coordinates) {
141 ViewContext* view = static_cast<ViewContext*>(bv);
142 const CameraData& camData = view->getContext()->cameraSystem->getData(view->getCameraComponent());
143
145 camData,
146 *castToVec3(normal),
147 *castToVec3(position),
148 *castToVec3(coordinates));
149}
150
151CogsBool getProjectedCoordinatesInViewWorld(BridgeView* bv, float x, float y, const float* normal, const double* worldPosition, double* coordinates) {
152 ViewContext* view = static_cast<ViewContext*>(bv);
153
154 glm::vec3 coords;
155 glm::vec3 position = view->getContext()->transformSystem->engineFromWorldCoords(*castToDVec3(worldPosition));
156 bool result = getProjectedCoordinatesInView(bv, int(x), int(y), normal, &position[0], &coords[0]);
157 if (result) {
158 *castToDVec3(coordinates) = view->getContext()->transformSystem->worldFromEngineCoords(coords);
159 }
160 return result;
161}
162
163int pickWithCameraRay(BridgeContext* ctx, EntityId cameraId, float x, float y,
164 float rayLength, float rayRadius, int pickingFlags, int returnFlag, int layerMask, const char* entityType,
165 int bufferSize, EntityId* entityIds, EntityId* rootIds, double* coordinates, float* textureCoords)
166{
167 Context* context = static_cast<Context*>(ctx);
168 const Cogs::ComponentModel::Entity* cameraEntity = context->store->getEntityPtr(cameraId);
169 assert(cameraEntity);
170 const CameraComponent* cameraComp = cameraEntity->getComponent<CameraComponent>();
171 assert(cameraComp);
172
173 const size_t typeCode = entityType == nullptr ? RayPicking::NoPickEntityType : Cogs::StringView(entityType).hash();
174 std::vector<RayPicking::RayPickHit> hits;
175
176 RayPicking::RayPickFilter filter(cameraComp->pickingOrder, RenderLayers(layerMask), typeCode);
177#if 1
178 context->rayPicking->pickCamera(context, *cameraComp, glm::vec2(x, y), rayLength, rayRadius,
179 PickingFlags(pickingFlags), PicksReturned(returnFlag), filter, hits);
180
181#else
182
183 // Utility code for debugging camera-less picking
184 // ----------------------------------------------
185 //
186 // Enable this code to use pickRay instead of pickCamera for doing ray casts,
187 // this code is used by the picking debug-mode raycaster in the cogs.core
188 // demos, and thus, one can exercise this alternative code path using the
189 // same test setup.
190
191 const CameraData& cameraData = context->cameraSystem->getData(cameraComp);
192
193 const glm::vec2 normPos = (2.f * (glm::vec2(x + 0.5f, y + 0.5f)) / cameraData.viewportSize) - glm::vec2(1.f);
194 const float sx = cameraData.viewportSize.x / rayRadius;
195 const float sy = cameraData.viewportSize.y / rayRadius;
196 const glm::mat4 worldPickMatrix = glm::mat4(sx, 0.f, 0.f, 0.f,
197 0.f, sy, 0.f, 0.f,
198 0.f, 0.f, 1.0f, 0.f,
199 -normPos.x * sx, -normPos.y * sy, 0.f, 1.f) * cameraData.rawViewProjection;
200 const glm::vec3 a = euclidean(glm::inverse(worldPickMatrix) * glm::vec4(0.f, 0.f, 1.f, -1.f)); // center at near plane
201 const glm::vec3 b = euclidean(glm::inverse(worldPickMatrix) * glm::vec4(0.f, 0.f, 1.f, 1.f)); // center at far plane
202 const glm::vec3 c = euclidean(glm::inverse(worldPickMatrix) * glm::vec4(1.f, 1.f, 1.f, -1.f)); // corner at near plane
203 const float rayRadiusWorld = glm::distance(a, c);
204 const glm::quat rot = glm::quat(glm::vec3(0.f, 0.f, -1.f), glm::normalize(b - a));
205
206 context->rayPicking->pickRay(context, a, rot, rayLength, rayRadiusWorld,
207 PickingFlags(pickingFlags), PicksReturned(returnFlag), filter, hits);
208
209#endif
210
211 // Fill buffers with hit data
212 return copyPickResults(context, hits, bufferSize, rootIds, entityIds, coordinates, textureCoords);
213}
214
215int pickWithCameraRayWorldUnit(BridgeContext* ctx, EntityId cameraId, float x, float y,
216 float rayLength, float inRayRadius, int pickingFlags, int returnFlag, int layerMask, const char* entityType,
217 int bufferSize, EntityId* entityIds, EntityId* rootIds, double* coordinates, float* textureCoords)
218{
219 Context* context = static_cast<Context*>(ctx);
220 const Cogs::ComponentModel::Entity* cameraEntity = context->store->getEntityPtr(cameraId);
221 assert(cameraEntity);
222 const CameraComponent* cameraComp = cameraEntity->getComponent<CameraComponent>();
223 assert(cameraComp);
224
225 const CameraData& cameraData = context->cameraSystem->getData(cameraComp);
226 const glm::vec2 p = glm::vec2(x, 1.f - y) * cameraData.viewportSize;
227
228 const float screenRadius = std::max(1.f, inRayRadius * cameraData.viewportSize.y);
229
230 const size_t typeCode = entityType == nullptr ? RayPicking::NoPickEntityType : Cogs::StringView(entityType).hash();
231 std::vector<RayPicking::RayPickHit> hits;
232
233 RayPicking::RayPickFilter filter(cameraComp->pickingOrder, RenderLayers(layerMask), typeCode);
234
235 context->rayPicking->pickCamera(context, *cameraComp, p, rayLength, screenRadius,
236 PickingFlags(pickingFlags), PicksReturned(returnFlag), filter, hits);
237
238 // Fill buffers with hit data
239 return copyPickResults(context, hits, bufferSize, rootIds, entityIds, coordinates, textureCoords);
240}
241
242int pickWithWorldRay(BridgeContext* ctx, const double* worldPosition, const float* orientation,
243 float rayLength, float rayRadius, int pickingFlags, int returnFlag, int layerMask, const char* entityType,
244 int bufferSize, EntityId* entityIds, EntityId* rootIds, double* coordinates, float* textureCoords)
245{
246 Context* context = static_cast<Context*>(ctx);
247
248 glm::vec3 pos = context->transformSystem->engineFromWorldCoords(*castToDVec3(worldPosition));
249 glm::quat rot = *castToQuat(orientation);
250 const size_t typeCode = entityType == nullptr ? RayPicking::NoPickEntityType : Cogs::StringView(entityType).hash();
251 std::vector<RayPicking::RayPickHit> hits;
252 context->rayPicking->pickRay(context, pos, rot, rayLength, rayRadius,
253 PickingFlags(pickingFlags), PicksReturned(returnFlag), {RenderLayers(layerMask), typeCode}, hits);
254
255 // Fill buffers with hit data
256 return copyPickResults(context, hits, bufferSize, rootIds, entityIds, coordinates, textureCoords);
257}
258
259EntityId getPickedEntity(BridgeContext* ctx, float x, float y, float * coordinates)
260{
261 return getPickedEntityFull2(ctx, x, y, int(PickingFlags::None), nullptr, coordinates, nullptr);
262}
263
264EntityId getPickedEntityFull2(BridgeContext* ctx, float x, float y, int pickingFlags, EntityId* rootId, float* coordinates, float* textureCoords)
265{
266 assert(coordinates);
267
268 Context* context = static_cast<Context*>(ctx);
269 const CameraComponent& camera = *context->getDefaultView()->getCameraComponent();
270
271 // TODO: Make sure the scene.rayPick.size.* variables are removed from CogsVariableList.md once they are removed from this file.
272 const glm::vec2 pickingRegionSize(std::max(1.0, context->variables->get("scene.rayPick.size.x", 4.0)),
273 std::max(1.0, context->variables->get("scene.rayPick.size.y", 4.0)));
274 const PickingFlags pickFlags = PickingFlags(pickingFlags);
275 std::vector<RayPicking::RayPickHit> hits;
276
277 RayPicking::RayPickFilter filter(camera.pickingOrder, RenderLayers::All, RayPicking::NoPickEntityType);
278 const bool picked = context->rayPicking->pickCamera(context, camera, glm::vec2(x, y), std::numeric_limits<float>::max(),
279 glm::length(pickingRegionSize), pickFlags, PicksReturned::Closest, filter, hits);
280 if (!picked) {
281 return NoEntity;
282 }
283
284 const RayPicking::RayPickHit& hit = hits.front();
285 *castToVec3(coordinates) = hit.position;
286
287 if (rootId) {
288 *rootId = hit.root;
289 }
290
291 if (textureCoords) {
292 textureCoords[0] = hit.textureCoords.x;
293 textureCoords[1] = hit.textureCoords.y;
294 }
295
296 return hit.entity;
297}
298
299int getAllPickedEntities2(BridgeContext* ctx, const float x, const float y, const int pickingFlags, const int bufferSize,
300 EntityId* rootIds, EntityId* entityIds, float* coordinates, float* textureCoords)
301{
302 Context* context = static_cast<Context*>(ctx);
303 const CameraComponent& camera = *context->getDefaultView()->getCameraComponent();
304
305 // TODO: Make sure the scene.rayPick.size.* variables are removed from CogsVariableList.md once they are removed from this file.
306 const glm::vec2 pickingRegionSize(std::max(1.0, context->variables->get("scene.rayPick.size.x", 4.0)),
307 std::max(1.0, context->variables->get("scene.rayPick.size.y", 4.0)));
308 const PickingFlags pickFlags = static_cast<PickingFlags>(pickingFlags);
309 std::vector<RayPicking::RayPickHit> hits;
310 RayPicking::RayPickFilter filter(camera.pickingOrder, RenderLayers::All, RayPicking::NoPickEntityType);
311 context->rayPicking->pickCamera(context, camera, glm::vec2(x, y), std::numeric_limits<float>::max(), glm::length(pickingRegionSize),
312 pickFlags, PicksReturned::AllSorted, filter, hits);
313
314 // Fill buffers with hit data
315 return copyPickResults(context, hits, bufferSize, rootIds, entityIds, coordinates, textureCoords);
316}
317
318int getAllPickedEntitiesFromRay(BridgeContext* ctx, const float* position, const float* orientation,
319 const float rayLength, const float radius, int pickingFlags, int bufferSize,
320 EntityId* rootIds, EntityId* entityIds, float* coordinates, float* textureCoords)
321{
322 Context* context = static_cast<Context*>(ctx);
323 glm::vec3 pos = *castToVec3(position);
324 glm::quat rot = *castToQuat(orientation);
325 const PickingFlags pickFlags = PickingFlags(pickingFlags);
326 std::vector<RayPicking::RayPickHit> hits;
327 context->rayPicking->pickRay(context, pos, rot, rayLength, radius, pickFlags, PicksReturned::AllSorted, {}, hits);
328
329 // Fill buffers with hit data
330 return copyPickResults(context, hits, bufferSize, rootIds, entityIds, coordinates, textureCoords);
331}
332
333float getDepthWorld(BridgeContext* ctx, EntityId entityId, const double* position)
334{
335 Context* context = static_cast<Context*>(ctx);
336 const glm::dvec3 worldPos = *castToDVec3(position);
337 const glm::vec3 pos = context->transformSystem->engineFromWorldCoords(worldPos);
338
339 return Cogs::Core::getDepth(context, entityId, pos);
340
341}
342
343float getDepth(BridgeContext* ctx, EntityId entityId, const float * position)
344{
345 Context* context = static_cast<Context*>(ctx);
346 return Cogs::Core::getDepth(context, entityId, *castToVec3(position));
347}
348
349void calculateBoundingBoxWorld(BridgeContext* ctx, EntityId entityId, double* values)
350{
351 Context* context = static_cast<Context*>(ctx);
352 Cogs::Geometry::BoundingBox box;
353 calculateBoundingBox(ctx, entityId, box.data());
354
355 Cogs::Geometry::DBoundingBox dBox(context->transformSystem->worldFromEngineCoords(box.min),
356 context->transformSystem->worldFromEngineCoords(box.max));
357 dBox.toSpan(std::span<double, 6>(values, 6));
358}
359
360void calculateBoundingBox(BridgeContext* ctx, EntityId entityId, float * values)
361{
362 Context* context = static_cast<Context*>(ctx);
363 Cogs::Geometry::BoundingBox box = Bounds::getBounds(context, entityId, true);
364 box.toSpan(std::span<float, 6>(values, 6));
365}
366
367void calculateBoundingBoxMultiWorld(BridgeContext* ctx, EntityId* entityIds, int count, double* values)
368{
369 Context* context = static_cast<Context*>(ctx);
370
371 Cogs::Geometry::BoundingBox box;
372 calculateBoundingBoxMulti(ctx, entityIds, count, box.data());
373
374 Cogs::Geometry::DBoundingBox dBox(context->transformSystem->worldFromEngineCoords(box.min),
375 context->transformSystem->worldFromEngineCoords(box.max));
376 dBox.toSpan(std::span<double, 6>(values, 6));
377}
378
379void calculateBoundingBoxMulti(BridgeContext* ctx, EntityId * entityIds, int count, float * values)
380{
381 Context* context = static_cast<Context*>(ctx);
382
383 std::span<const EntityId> ids(entityIds, count);
384 Cogs::Geometry::BoundingBox box = Bounds::getBounds(context, ids, true);
385 box.toSpan(std::span<float, 6>(values, 6));
386}
387
388void calculateSceneBoundingBoxWorld(BridgeContext* ctx, double* values)
389{
390 Context* context = static_cast<Context*>(ctx);
391 Cogs::Geometry::BoundingBox box;
392 calculateSceneBoundingBox(ctx, box.data());
393
394 Cogs::Geometry::DBoundingBox dBox(context->transformSystem->worldFromEngineCoords(box.min),
395 context->transformSystem->worldFromEngineCoords(box.max));
396 dBox.toSpan(std::span<double, 6>(values, 6));
397}
398
400void calculateSceneBoundingBox(BridgeContext* ctx, float* values)
401{
402 Context* context = static_cast<Context*>(ctx);
403
404 context->bounds->getSceneBounds(context, values, RenderLayers::AllStandard & ~RenderLayers::Sky);
405}
406
407void calculateSceneBoundingBoxWorld2(BridgeContext* ctx, int layerMask, double* values)
408{
409 Context* context = static_cast<Context*>(ctx);
410 Cogs::Geometry::BoundingBox box;
411
412 const RenderLayers layerMask_ = RenderLayers(layerMask);
413 context->bounds->getSceneBounds(context, box.data(), layerMask_);
414
415 Cogs::Geometry::DBoundingBox dBox(context->transformSystem->worldFromEngineCoords(box.min),
416 context->transformSystem->worldFromEngineCoords(box.max));
417 dBox.toSpan(std::span<double, 6>(values, 6));
418}
Container for components, providing composition of dynamic entities.
Definition: Entity.h:18
T * getComponent() const
Get a pointer to the first component implementing the given type in the entity.
Definition: Entity.h:35
std::vector< RenderLayers > pickingOrder
Sequence of masks that specify the ordering of picking results. Anything not matching will be ordered...
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
std::unique_ptr< class Bounds > bounds
Bounds service instance.
Definition: Context.h:216
class EntityStore * store
Entity store.
Definition: Context.h:231
std::unique_ptr< class RayPicking > rayPicking
RayPicking service instance.
Definition: Context.h:213
std::unique_ptr< class Variables > variables
Variables service instance.
Definition: Context.h:180
ComponentModel::Entity * getEntityPtr(const EntityId entityId)
Get a raw pointer to the entity with the given id.
void setOrigin(const glm::dvec3 &newOrigin)
Sets the Origin offset of the scene.
glm::dvec3 getOrigin() const
Gets the Origin offset of the scene.
CameraComponent * getCameraComponent() const
Utility to get the CameraComponent from.
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:50
constexpr size_t hash() const noexcept
Get the hash code of the string.
Definition: StringView.h:226
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
RenderLayers
Contains common render layers.
bool getProjectedCoordinates(const glm::vec2 &coordinatesToProject, const CameraData &cameraData, const glm::vec3 &targetPlaneNormal, const glm::vec3 &targetPlanePosition, glm::vec3 &projectedCoordinates)
Project a screen coordinate onto a plane in 3d space.
Definition: CameraUtils.h:132
Contains data describing a Camera instance and its derived data structured such as matrix data and vi...
Definition: CameraSystem.h:67
glm::vec3 position
The picked position.
Definition: RayPick.h:79
EntityId entity
The EntityId picked.
Definition: RayPick.h:72