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#if 1
177 context->rayPicking->pickCamera(context, *cameraComp, glm::vec2(x, y), rayLength, rayRadius,
178 PickingFlags(pickingFlags), PicksReturned(returnFlag), { RenderLayers(layerMask), typeCode }, hits);
179
180#else
181
182 // Utility code for debugging camera-less picking
183 // ----------------------------------------------
184 //
185 // Enable this code to use pickRay instead of pickCamera for doing ray casts,
186 // this code is used by the picking debug-mode raycaster in the cogs.core
187 // demos, and thus, one can exercise this alternative code path using the
188 // same test setup.
189
190 const CameraData& cameraData = context->cameraSystem->getData(cameraComp);
191
192 const glm::vec2 normPos = (2.f * (glm::vec2(x + 0.5f, y + 0.5f)) / cameraData.viewportSize) - glm::vec2(1.f);
193 const float sx = cameraData.viewportSize.x / rayRadius;
194 const float sy = cameraData.viewportSize.y / rayRadius;
195 const glm::mat4 worldPickMatrix = glm::mat4(sx, 0.f, 0.f, 0.f,
196 0.f, sy, 0.f, 0.f,
197 0.f, 0.f, 1.0f, 0.f,
198 -normPos.x * sx, -normPos.y * sy, 0.f, 1.f) * cameraData.rawViewProjection;
199 const glm::vec3 a = euclidean(glm::inverse(worldPickMatrix) * glm::vec4(0.f, 0.f, 1.f, -1.f)); // center at near plane
200 const glm::vec3 b = euclidean(glm::inverse(worldPickMatrix) * glm::vec4(0.f, 0.f, 1.f, 1.f)); // center at far plane
201 const glm::vec3 c = euclidean(glm::inverse(worldPickMatrix) * glm::vec4(1.f, 1.f, 1.f, -1.f)); // corner at near plane
202 const float rayRadiusWorld = glm::distance(a, c);
203 const glm::quat rot = glm::quat(glm::vec3(0.f, 0.f, -1.f), glm::normalize(b - a));
204
205 context->rayPicking->pickRay(context, a, rot, rayLength, rayRadiusWorld,
206 PickingFlags(pickingFlags), PicksReturned(returnFlag), { RenderLayers(layerMask), typeCode }, hits);
207
208#endif
209
210 // Fill buffers with hit data
211 return copyPickResults(context, hits, bufferSize, rootIds, entityIds, coordinates, textureCoords);
212}
213
214int pickWithCameraRayWorldUnit(BridgeContext* ctx, EntityId cameraId, float x, float y,
215 float rayLength, float inRayRadius, int pickingFlags, int returnFlag, int layerMask, const char* entityType,
216 int bufferSize, EntityId* entityIds, EntityId* rootIds, double* coordinates, float* textureCoords)
217{
218 Context* context = static_cast<Context*>(ctx);
219 const Cogs::ComponentModel::Entity* cameraEntity = context->store->getEntityPtr(cameraId);
220 assert(cameraEntity);
221 const CameraComponent* cameraComp = cameraEntity->getComponent<CameraComponent>();
222 assert(cameraComp);
223
224 const CameraData& cameraData = context->cameraSystem->getData(cameraComp);
225 const glm::vec2 p = glm::vec2(x, 1.f - y) * cameraData.viewportSize;
226
227 const float screenRadius = std::max(1.f, inRayRadius * cameraData.viewportSize.y);
228
229 const size_t typeCode = entityType == nullptr ? RayPicking::NoPickEntityType : Cogs::StringView(entityType).hash();
230 std::vector<RayPicking::RayPickHit> hits;
231 context->rayPicking->pickCamera(context, *cameraComp, p, rayLength, screenRadius,
232 PickingFlags(pickingFlags), PicksReturned(returnFlag), { RenderLayers(layerMask), typeCode }, hits);
233
234 // Fill buffers with hit data
235 return copyPickResults(context, hits, bufferSize, rootIds, entityIds, coordinates, textureCoords);
236}
237
238int pickWithWorldRay(BridgeContext* ctx, const double* worldPosition, const float* orientation,
239 float rayLength, float rayRadius, int pickingFlags, int returnFlag, int layerMask, const char* entityType,
240 int bufferSize, EntityId* entityIds, EntityId* rootIds, double* coordinates, float* textureCoords)
241{
242 Context* context = static_cast<Context*>(ctx);
243
244 glm::vec3 pos = context->transformSystem->engineFromWorldCoords(*castToDVec3(worldPosition));
245 glm::quat rot = *castToQuat(orientation);
246 const size_t typeCode = entityType == nullptr ? RayPicking::NoPickEntityType : Cogs::StringView(entityType).hash();
247 std::vector<RayPicking::RayPickHit> hits;
248 context->rayPicking->pickRay(context, pos, rot, rayLength, rayRadius,
249 PickingFlags(pickingFlags), PicksReturned(returnFlag), {RenderLayers(layerMask), typeCode}, hits);
250
251 // Fill buffers with hit data
252 return copyPickResults(context, hits, bufferSize, rootIds, entityIds, coordinates, textureCoords);
253}
254
255EntityId getPickedEntity(BridgeContext* ctx, int x, int y, float * coordinates)
256{
257 return getPickedEntityFull2(ctx, static_cast<float>(x), static_cast<float>(y), int(PickingFlags::None), nullptr, coordinates, nullptr);
258}
259
260EntityId getPickedEntityFull2(BridgeContext* ctx, float x, float y, int pickingFlags, EntityId* rootId, float* coordinates, float* textureCoords)
261{
262 assert(coordinates);
263
264 Context* context = static_cast<Context*>(ctx);
265 const CameraComponent& camera = *context->getDefaultView()->getCameraComponent();
266
267 // TODO: Make sure the scene.rayPick.size.* variables are removed from CogsVariableList.md once they are removed from this file.
268 const glm::vec2 pickingRegionSize(std::max(1.0, context->variables->get("scene.rayPick.size.x", 4.0)),
269 std::max(1.0, context->variables->get("scene.rayPick.size.y", 4.0)));
270 const PickingFlags pickFlags = PickingFlags(pickingFlags);
271 std::vector<RayPicking::RayPickHit> hits;
272 const bool picked = context->rayPicking->pickCamera(context, camera, glm::vec2(x, y), std::numeric_limits<float>::max(),
273 glm::length(pickingRegionSize), pickFlags, PicksReturned::Closest, {}, hits);
274 if (!picked) {
275 return NoEntity;
276 }
277
278 const RayPicking::RayPickHit& hit = hits.front();
279 *castToVec3(coordinates) = hit.position;
280
281 if (rootId) {
282 *rootId = hit.root;
283 }
284
285 if (textureCoords) {
286 textureCoords[0] = hit.textureCoords.x;
287 textureCoords[1] = hit.textureCoords.y;
288 }
289
290 return hit.entity;
291}
292
293int getAllPickedEntities2(BridgeContext* ctx, const float x, const float y, const int pickingFlags, const int bufferSize,
294 EntityId* rootIds, EntityId* entityIds, float* coordinates, float* textureCoords)
295{
296 Context* context = static_cast<Context*>(ctx);
297 const CameraComponent& camera = *context->getDefaultView()->getCameraComponent();
298
299 // TODO: Make sure the scene.rayPick.size.* variables are removed from CogsVariableList.md once they are removed from this file.
300 const glm::vec2 pickingRegionSize(std::max(1.0, context->variables->get("scene.rayPick.size.x", 4.0)),
301 std::max(1.0, context->variables->get("scene.rayPick.size.y", 4.0)));
302 const PickingFlags pickFlags = static_cast<PickingFlags>(pickingFlags);
303 std::vector<RayPicking::RayPickHit> hits;
304 context->rayPicking->pickCamera(context, camera, glm::vec2(x, y), std::numeric_limits<float>::max(), glm::length(pickingRegionSize),
305 pickFlags, PicksReturned::AllSorted, {}, hits);
306
307 // Fill buffers with hit data
308 return copyPickResults(context, hits, bufferSize, rootIds, entityIds, coordinates, textureCoords);
309}
310
311int getAllPickedEntitiesFromRay(BridgeContext* ctx, const float* position, const float* orientation,
312 const float rayLength, const float radius, int pickingFlags, int bufferSize,
313 EntityId* rootIds, EntityId* entityIds, float* coordinates, float* textureCoords)
314{
315 Context* context = static_cast<Context*>(ctx);
316 glm::vec3 pos = *castToVec3(position);
317 glm::quat rot = *castToQuat(orientation);
318 const PickingFlags pickFlags = PickingFlags(pickingFlags);
319 std::vector<RayPicking::RayPickHit> hits;
320 context->rayPicking->pickRay(context, pos, rot, rayLength, radius, pickFlags, PicksReturned::AllSorted, {}, hits);
321
322 // Fill buffers with hit data
323 return copyPickResults(context, hits, bufferSize, rootIds, entityIds, coordinates, textureCoords);
324}
325
326float getDepthWorld(BridgeContext* ctx, EntityId entityId, const double* position)
327{
328 Context* context = static_cast<Context*>(ctx);
329 const glm::dvec3 worldPos = *castToDVec3(position);
330 const glm::vec3 pos = context->transformSystem->engineFromWorldCoords(worldPos);
331
332 return Cogs::Core::getDepth(context, entityId, pos);
333
334}
335
336float getDepth(BridgeContext* ctx, EntityId entityId, const float * position)
337{
338 Context* context = static_cast<Context*>(ctx);
339 return Cogs::Core::getDepth(context, entityId, *castToVec3(position));
340}
341
342void calculateBoundingBoxWorld(BridgeContext* ctx, EntityId entityId, double* values)
343{
344 Context* context = static_cast<Context*>(ctx);
345 Cogs::Geometry::BoundingBox box;
346 calculateBoundingBox(ctx, entityId, box.data());
347
348 Cogs::Geometry::DBoundingBox dBox(context->transformSystem->worldFromEngineCoords(box.min),
349 context->transformSystem->worldFromEngineCoords(box.max));
350 dBox.toSpan(std::span<double, 6>(values, 6));
351}
352
353void calculateBoundingBox(BridgeContext* ctx, EntityId entityId, float * values)
354{
355 Context* context = static_cast<Context*>(ctx);
356 Cogs::Geometry::BoundingBox box = Bounds::getBounds(context, entityId, true);
357 box.toSpan(std::span<float, 6>(values, 6));
358}
359
360void calculateBoundingBoxMultiWorld(BridgeContext* ctx, EntityId* entityIds, int count, double* values)
361{
362 Context* context = static_cast<Context*>(ctx);
363
364 Cogs::Geometry::BoundingBox box;
365 calculateBoundingBoxMulti(ctx, entityIds, count, box.data());
366
367 Cogs::Geometry::DBoundingBox dBox(context->transformSystem->worldFromEngineCoords(box.min),
368 context->transformSystem->worldFromEngineCoords(box.max));
369 dBox.toSpan(std::span<double, 6>(values, 6));
370}
371
372void calculateBoundingBoxMulti(BridgeContext* ctx, EntityId * entityIds, int count, float * values)
373{
374 Context* context = static_cast<Context*>(ctx);
375
376 std::span<const EntityId> ids(entityIds, count);
377 Cogs::Geometry::BoundingBox box = Bounds::getBounds(context, ids, true);
378 box.toSpan(std::span<float, 6>(values, 6));
379}
380
381void calculateSceneBoundingBoxWorld(BridgeContext* ctx, double* values)
382{
383 Context* context = static_cast<Context*>(ctx);
384 Cogs::Geometry::BoundingBox box;
385 calculateSceneBoundingBox(ctx, box.data());
386
387 Cogs::Geometry::DBoundingBox dBox(context->transformSystem->worldFromEngineCoords(box.min),
388 context->transformSystem->worldFromEngineCoords(box.max));
389 dBox.toSpan(std::span<double, 6>(values, 6));
390}
391
393void calculateSceneBoundingBox(BridgeContext* ctx, float* values)
394{
395 Context* context = static_cast<Context*>(ctx);
396
397 context->bounds->getSceneBounds(context, values, RenderLayers::AllStandard & ~RenderLayers::Sky);
398}
399
400void calculateSceneBoundingBoxWorld2(BridgeContext* ctx, int layerMask, double* values)
401{
402 Context* context = static_cast<Context*>(ctx);
403 Cogs::Geometry::BoundingBox box;
404
405 const RenderLayers layerMask_ = RenderLayers(layerMask);
406 context->bounds->getSceneBounds(context, box.data(), layerMask_);
407
408 Cogs::Geometry::DBoundingBox dBox(context->transformSystem->worldFromEngineCoords(box.min),
409 context->transformSystem->worldFromEngineCoords(box.max));
410 dBox.toSpan(std::span<double, 6>(values, 6));
411}
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
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:24
constexpr size_t hash() const noexcept
Get the hash code of the string.
Definition: StringView.h:200
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:103
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:45
EntityId entity
The EntityId picked.
Definition: RayPick.h:38