• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "picking.h"
17 
18 #include <algorithm>
19 #include <cinttypes>
20 #include <limits>
21 
22 #include <3d/ecs/components/camera_component.h>
23 #include <3d/ecs/components/joint_matrices_component.h>
24 #include <3d/ecs/components/layer_component.h>
25 #include <3d/ecs/components/local_matrix_component.h>
26 #include <3d/ecs/components/mesh_component.h>
27 #include <3d/ecs/components/render_mesh_component.h>
28 #include <3d/ecs/components/transform_component.h>
29 #include <3d/ecs/components/world_matrix_component.h>
30 #include <3d/ecs/systems/intf_node_system.h>
31 #include <base/containers/fixed_string.h>
32 #include <base/math/mathf.h>
33 #include <base/math/matrix_util.h>
34 #include <base/math/vector_util.h>
35 #include <core/ecs/intf_ecs.h>
36 #include <core/implementation_uids.h>
37 #include <core/namespace.h>
38 #include <core/plugin/intf_class_factory.h>
39 #include <core/plugin/intf_class_register.h>
40 #include <core/plugin/intf_plugin_register.h>
41 #include <core/property/intf_property_handle.h>
42 
43 #include "util/scene_util.h"
44 
45 CORE3D_BEGIN_NAMESPACE()
46 using namespace BASE_NS;
47 using namespace CORE_NS;
48 using namespace RENDER_NS;
49 
50 namespace {
GetWorldAABB(const Math::Mat4X4 & world,const Math::Vec3 & aabbMin,const Math::Vec3 & aabbMax)51 MinAndMax GetWorldAABB(const Math::Mat4X4& world, const Math::Vec3& aabbMin, const Math::Vec3& aabbMax)
52 {
53     // Based on https://gist.github.com/cmf028/81e8d3907035640ee0e3fdd69ada543f
54     const auto center = (aabbMin + aabbMax) * 0.5f;
55     const auto extents = aabbMax - center;
56 
57     const auto centerW = Math::MultiplyPoint3X4(world, center);
58 
59     Math::Mat3X3 absWorld;
60     for (auto i = 0U; i < countof(absWorld.base); ++i) {
61         absWorld.base[i].x = Math::abs(world.base[i].x);
62         absWorld.base[i].y = Math::abs(world.base[i].y);
63         absWorld.base[i].z = Math::abs(world.base[i].z);
64     }
65 
66     const auto extentsW = absWorld * extents;
67 
68     return MinAndMax { centerW - extentsW, centerW + extentsW };
69 }
70 
IntersectAabb(Math::Vec3 aabbMin,Math::Vec3 aabbMax,Math::Vec3 start,Math::Vec3 invDirection,float & hitDistance)71 constexpr bool IntersectAabb(
72     Math::Vec3 aabbMin, Math::Vec3 aabbMax, Math::Vec3 start, Math::Vec3 invDirection, float& hitDistance)
73 {
74     const float tx1 = (aabbMin.x - start.x) * invDirection.x;
75     const float tx2 = (aabbMax.x - start.x) * invDirection.x;
76 
77     float tmin = Math::min(tx1, tx2);
78     float tmax = Math::max(tx1, tx2);
79 
80     const float ty1 = (aabbMin.y - start.y) * invDirection.y;
81     const float ty2 = (aabbMax.y - start.y) * invDirection.y;
82 
83     tmin = Math::max(tmin, Math::min(ty1, ty2));
84     tmax = Math::min(tmax, Math::max(ty1, ty2));
85 
86     const float tz1 = (aabbMin.z - start.z) * invDirection.z;
87     const float tz2 = (aabbMax.z - start.z) * invDirection.z;
88 
89     tmin = Math::max(tmin, Math::min(tz1, tz2));
90     tmax = Math::min(tmax, Math::max(tz1, tz2));
91 
92     hitDistance = tmin;
93     // If hitDistance < 0, ray origin was inside the AABB.
94     if (hitDistance < 0.0f) {
95         hitDistance = tmax;
96         // No hit, set distance to 0.
97         if (hitDistance < 0.0f) {
98             hitDistance = 0.0f;
99         }
100     }
101 
102     return tmax >= tmin && tmax > 0.0f;
103 }
104 
IntersectTriangle(const Math::Vec3 triangle[3],const Math::Vec3 start,const Math::Vec3 direction,float & hitDistance,Math::Vec2 & uv)105 bool IntersectTriangle(const Math::Vec3 triangle[3], const Math::Vec3 start, const Math::Vec3 direction,
106     float& hitDistance, Math::Vec2& uv)
107 {
108     const Math::Vec3 v0v1 = triangle[1] - triangle[0];
109     const Math::Vec3 v0v2 = triangle[2] - triangle[0];
110 
111     const Math::Vec3 pvec = Math::Cross(direction, v0v2);
112     const float det = Math::Dot(v0v1, pvec);
113 
114     // ray and triangle are parallel and backface culling
115     if (det < Math::EPSILON) {
116         hitDistance = 0.f;
117         return false;
118     }
119 
120     const float invDet = 1.f / det;
121     const Math::Vec3 tvec = start - triangle[0];
122 
123     const float u = Math::Dot(tvec, pvec) * invDet;
124     if (u < 0 || u > 1) {
125         hitDistance = 0.f;
126         uv = Math::Vec2(0, 0);
127         return false;
128     }
129 
130     const Math::Vec3 qvec = Math::Cross(tvec, v0v1);
131     const float v = Math::Dot(direction, qvec) * invDet;
132     if (v < 0 || u + v > 1) {
133         hitDistance = 0.f;
134         uv = Math::Vec2(0, 0);
135         return false;
136     }
137 
138     hitDistance = Math::Dot(v0v2, qvec) * invDet;
139     uv = Math::Vec2(u, v);
140     return true;
141 }
142 
143 // Calculates AABB using WorldMatrixComponent.
UpdateRecursiveAABB(const IRenderMeshComponentManager & renderMeshComponentManager,const IWorldMatrixComponentManager & worldMatrixComponentManager,const IJointMatricesComponentManager & jointMatricesComponentManager,const IMeshComponentManager & meshManager,const ISceneNode & sceneNode,bool isRecursive,MinAndMax & mamInOut)144 void UpdateRecursiveAABB(const IRenderMeshComponentManager& renderMeshComponentManager,
145     const IWorldMatrixComponentManager& worldMatrixComponentManager,
146     const IJointMatricesComponentManager& jointMatricesComponentManager, const IMeshComponentManager& meshManager,
147     const ISceneNode& sceneNode, bool isRecursive, MinAndMax& mamInOut)
148 {
149     const Entity entity = sceneNode.GetEntity();
150     if (const auto jointMatrices = jointMatricesComponentManager.Read(entity); jointMatrices) {
151         // Take skinning into account.
152         mamInOut.minAABB = Math::min(mamInOut.minAABB, jointMatrices->jointsAabbMin);
153         mamInOut.maxAABB = Math::max(mamInOut.maxAABB, jointMatrices->jointsAabbMax);
154     } else {
155         const auto worldMatrixId = worldMatrixComponentManager.GetComponentId(entity);
156         const auto renderMeshId = renderMeshComponentManager.GetComponentId(entity);
157         if (worldMatrixId != IComponentManager::INVALID_COMPONENT_ID &&
158             renderMeshId != IComponentManager::INVALID_COMPONENT_ID) {
159             const Math::Mat4X4& worldMatrix = worldMatrixComponentManager.Get(worldMatrixId).matrix;
160 
161             const RenderMeshComponent rmc = renderMeshComponentManager.Get(renderMeshId);
162             if (const auto meshHandle = meshManager.Read(rmc.mesh); meshHandle) {
163                 const MinAndMax meshMam = GetWorldAABB(worldMatrix, meshHandle->aabbMin, meshHandle->aabbMax);
164                 mamInOut.minAABB = Math::min(mamInOut.minAABB, meshMam.minAABB);
165                 mamInOut.maxAABB = Math::max(mamInOut.maxAABB, meshMam.maxAABB);
166             }
167         }
168     }
169 
170     if (isRecursive) {
171         for (ISceneNode* child : sceneNode.GetChildren()) {
172             if (child) {
173                 UpdateRecursiveAABB(renderMeshComponentManager, worldMatrixComponentManager,
174                     jointMatricesComponentManager, meshManager, *child, isRecursive, mamInOut);
175             }
176         }
177     }
178 }
179 
180 // Calculates AABB using TransformComponent.
UpdateRecursiveAABB(const IRenderMeshComponentManager & renderMeshComponentManager,const ITransformComponentManager & transformComponentManager,const ILocalMatrixComponentManager & localMatrixComponentManager,const IMeshComponentManager & meshManager,const ISceneNode & sceneNode,const Math::Mat4X4 & parentWorld,bool isRecursive,MinAndMax & mamInOut)181 void UpdateRecursiveAABB(const IRenderMeshComponentManager& renderMeshComponentManager,
182     const ITransformComponentManager& transformComponentManager,
183     const ILocalMatrixComponentManager& localMatrixComponentManager, const IMeshComponentManager& meshManager,
184     const ISceneNode& sceneNode, const Math::Mat4X4& parentWorld, bool isRecursive, MinAndMax& mamInOut)
185 {
186     const Entity entity = sceneNode.GetEntity();
187     Math::Mat4X4 worldMatrix = parentWorld;
188 
189     if (const ScopedHandle<const TransformComponent> tc = transformComponentManager.Read(entity)) {
190         const Math::Mat4X4 localMatrix = Math::Trs(tc->position, tc->rotation, tc->scale);
191         worldMatrix = worldMatrix * localMatrix;
192     } else {
193         if (const ScopedHandle<const LocalMatrixComponent> lmc = localMatrixComponentManager.Read(entity)) {
194             worldMatrix = worldMatrix * lmc->matrix;
195         }
196     }
197 
198     if (const auto renderMeshHandle = renderMeshComponentManager.Read(entity)) {
199         if (const auto meshHandle = meshManager.Read(renderMeshHandle->mesh); meshHandle) {
200             const MinAndMax meshMam = GetWorldAABB(worldMatrix, meshHandle->aabbMin, meshHandle->aabbMax);
201             mamInOut.minAABB = Math::min(mamInOut.minAABB, meshMam.minAABB);
202             mamInOut.maxAABB = Math::max(mamInOut.maxAABB, meshMam.maxAABB);
203         }
204     }
205 
206     // Recurse to children.
207     if (isRecursive) {
208         for (ISceneNode* child : sceneNode.GetChildren()) {
209             if (child) {
210                 UpdateRecursiveAABB(renderMeshComponentManager, transformComponentManager, localMatrixComponentManager,
211                     meshManager, *child, worldMatrix, isRecursive, mamInOut);
212             }
213         }
214     }
215 }
216 
HitTestNode(ISceneNode & node,const MeshComponent & mesh,const Math::Mat4X4 & matrix,const Math::Vec3 & start,const Math::Vec3 & invDir)217 RayCastResult HitTestNode(ISceneNode& node, const MeshComponent& mesh, const Math::Mat4X4& matrix,
218     const Math::Vec3& start, const Math::Vec3& invDir)
219 {
220     RayCastResult raycastResult;
221     const auto direction = Math::Vec3(1.f / invDir.x, 1.f / invDir.y, 1.f / invDir.z);
222 
223     const MinAndMax meshMinMax = GetWorldAABB(matrix, mesh.aabbMin, mesh.aabbMax);
224     float distance = 0;
225     if (IntersectAabb(meshMinMax.minAABB, meshMinMax.maxAABB, start, invDir, distance)) {
226         if (mesh.submeshes.size() > 1) {
227             raycastResult.centerDistance = std::numeric_limits<float>::max();
228 
229             for (auto const& submesh : mesh.submeshes) {
230                 const MinAndMax submeshMinMax = GetWorldAABB(matrix, submesh.aabbMin, submesh.aabbMax);
231                 if (IntersectAabb(submeshMinMax.minAABB, submeshMinMax.maxAABB, start, invDir, distance)) {
232                     const float centerDistance =
233                         Math::Magnitude((submeshMinMax.maxAABB + submeshMinMax.minAABB) / 2.f - start);
234                     if (centerDistance < raycastResult.centerDistance) {
235                         raycastResult.node = &node;
236                         raycastResult.centerDistance = centerDistance;
237                         raycastResult.distance = distance;
238                         raycastResult.worldPosition = start + direction * distance;
239                     }
240                 }
241             }
242         } else {
243             raycastResult.centerDistance = Math::Magnitude((meshMinMax.minAABB + meshMinMax.maxAABB) / 2.f - start);
244             raycastResult.distance = distance;
245             raycastResult.node = &node;
246             raycastResult.worldPosition = start + direction * raycastResult.distance;
247         }
248     }
249 
250     return raycastResult;
251 }
252 
ScreenToWorld(const CameraComponent & cameraComponent,const Math::Mat4X4 & cameraWorldMatrix,Math::Vec3 screenCoordinate)253 Math::Vec3 ScreenToWorld(
254     const CameraComponent& cameraComponent, const Math::Mat4X4& cameraWorldMatrix, Math::Vec3 screenCoordinate)
255 {
256     screenCoordinate.x = (screenCoordinate.x - 0.5f) * 2.f;
257     screenCoordinate.y = (screenCoordinate.y - 0.5f) * 2.f;
258 
259     bool isCameraNegative = false;
260     Math::Mat4X4 projToView =
261         Math::Inverse(CameraMatrixUtil::CalculateProjectionMatrix(cameraComponent, isCameraNegative));
262 
263     auto const& worldFromView = cameraWorldMatrix;
264     const auto viewCoordinate =
265         (projToView * Math::Vec4(screenCoordinate.x, screenCoordinate.y, screenCoordinate.z, 1.f));
266     auto worldCoordinate = worldFromView * viewCoordinate;
267     worldCoordinate /= worldCoordinate.w;
268     return Math::Vec3 { worldCoordinate.x, worldCoordinate.y, worldCoordinate.z };
269 }
270 
271 struct Ray {
272     Math::Vec3 origin;
273     Math::Vec3 direction;
274 };
275 
RayFromCamera(const CameraComponent & cameraComponent,const WorldMatrixComponent & cameraWorldMatrixComponent,Math::Vec2 screenCoordinate)276 Ray RayFromCamera(const CameraComponent& cameraComponent, const WorldMatrixComponent& cameraWorldMatrixComponent,
277     Math::Vec2 screenCoordinate)
278 {
279     if (cameraComponent.projection == CORE3D_NS::CameraComponent::Projection::ORTHOGRAPHIC) {
280         const Math::Vec3 worldPos = CORE3D_NS::ScreenToWorld(cameraComponent, cameraWorldMatrixComponent.matrix,
281             Math::Vec3(screenCoordinate.x, screenCoordinate.y, 0.0f));
282         const auto direction = cameraWorldMatrixComponent.matrix * Math::Vec4(0.0f, 0.0f, -1.0f, 0.0f);
283         return Ray { worldPos, direction };
284     }
285     // Ray origin is the camera world position.
286     const Math::Vec3& rayOrigin = Math::Vec3(cameraWorldMatrixComponent.matrix.w);
287     const Math::Vec3 targetPos = CORE3D_NS::ScreenToWorld(
288         cameraComponent, cameraWorldMatrixComponent.matrix, Math::Vec3(screenCoordinate.x, screenCoordinate.y, 1.0f));
289     const Math::Vec3 direction = Math::Normalize(targetPos - rayOrigin);
290     return Ray { rayOrigin, direction };
291 }
292 
GetWorldMatrix(const ITransformComponentManager & transformComponentManager,const INodeSystem & nodeSystem,Entity entity)293 Math::Mat4X4 GetWorldMatrix(
294     const ITransformComponentManager& transformComponentManager, const INodeSystem& nodeSystem, Entity entity)
295 {
296     Math::Mat4X4 local = Math::IDENTITY_4X4;
297     if (!EntityUtil::IsValid(entity)) {
298         return local;
299     }
300     if (const auto transformId = transformComponentManager.GetComponentId(entity);
301         transformId != IComponentManager::INVALID_COMPONENT_ID) {
302         const TransformComponent tc = transformComponentManager.Get(transformId);
303         local = Math::Trs(tc.position, tc.rotation, tc.scale);
304     }
305 
306     auto node = nodeSystem.GetNode(entity);
307     if (!node) {
308         return local;
309     }
310     auto parent = node->GetParent();
311     if (!parent) {
312         return local;
313     }
314     return GetWorldMatrix(transformComponentManager, nodeSystem, parent->GetEntity()) * local;
315 }
316 } // namespace
317 
ScreenToWorld(IEcs const & ecs,Entity cameraEntity,Math::Vec3 screenCoordinate) const318 Math::Vec3 Picking::ScreenToWorld(IEcs const& ecs, Entity cameraEntity, Math::Vec3 screenCoordinate) const
319 {
320     if (!EntityUtil::IsValid(cameraEntity)) {
321         return {};
322     }
323 
324     auto cameraComponentManager = GetManager<ICameraComponentManager>(ecs);
325     const auto cameraId = cameraComponentManager->GetComponentId(cameraEntity);
326     if (cameraId == IComponentManager::INVALID_COMPONENT_ID) {
327         return {};
328     }
329 
330     const Math::Mat4X4 worldMatrix =
331         GetWorldMatrix(*GetManager<ITransformComponentManager>(ecs), *GetSystem<INodeSystem>(ecs), cameraEntity);
332     return CORE3D_NS::ScreenToWorld(*cameraComponentManager->Read(cameraId), worldMatrix, screenCoordinate);
333 }
334 
WorldToScreen(IEcs const & ecs,Entity cameraEntity,Math::Vec3 worldCoordinate) const335 Math::Vec3 Picking::WorldToScreen(IEcs const& ecs, Entity cameraEntity, Math::Vec3 worldCoordinate) const
336 {
337     if (!EntityUtil::IsValid(cameraEntity)) {
338         return {};
339     }
340 
341     const Math::Mat4X4 worldMatrix =
342         GetWorldMatrix(*GetManager<ITransformComponentManager>(ecs), *GetSystem<INodeSystem>(ecs), cameraEntity);
343     auto const worldToView = Math::Inverse(worldMatrix);
344     const auto viewCoordinate = worldToView * Math::Vec4(worldCoordinate.x, worldCoordinate.y, worldCoordinate.z, 1.f);
345 
346     auto cameraComponentManager = GetManager<ICameraComponentManager>(ecs);
347     const auto cameraId = cameraComponentManager->GetComponentId(cameraEntity);
348     if (cameraId == IComponentManager::INVALID_COMPONENT_ID) {
349         return {};
350     }
351     const CameraComponent cameraComponent = cameraComponentManager->Get(cameraId);
352     bool isCameraNegative = false;
353     Math::Mat4X4 viewToProj = CameraMatrixUtil::CalculateProjectionMatrix(cameraComponent, isCameraNegative);
354 
355     auto screenCoordinate = viewToProj * viewCoordinate;
356 
357     // Give sane results also when the point is behind the camera.
358     if (screenCoordinate.w < 0.0f) {
359         screenCoordinate.x *= -1.0f;
360         screenCoordinate.y *= -1.0f;
361         screenCoordinate.z *= -1.0f;
362     }
363 
364     screenCoordinate /= screenCoordinate.w;
365     screenCoordinate.x = screenCoordinate.x * 0.5f + 0.5f;
366     screenCoordinate.y = screenCoordinate.y * 0.5f + 0.5f;
367 
368     return Math::Vec3 { screenCoordinate.x, screenCoordinate.y, screenCoordinate.z };
369 }
370 
RayCast(const IEcs & ecs,const Math::Vec3 & start,const Math::Vec3 & direction) const371 vector<RayCastResult> Picking::RayCast(const IEcs& ecs, const Math::Vec3& start, const Math::Vec3& direction) const
372 {
373     vector<RayCastResult> result;
374 
375     auto nodeSystem = GetSystem<INodeSystem>(ecs);
376     auto const& renderMeshComponentManager = GetManager<IRenderMeshComponentManager>(ecs);
377     auto const& worldMatrixComponentManager = GetManager<IWorldMatrixComponentManager>(ecs);
378     auto const& jointMatricesComponentManager = GetManager<IJointMatricesComponentManager>(ecs);
379     auto const& meshComponentManager = *GetManager<IMeshComponentManager>(ecs);
380     float distance = 0;
381 
382     auto const invDir = DirectionVectorInverse(direction);
383     for (IComponentManager::ComponentId i = 0; i < renderMeshComponentManager->GetComponentCount(); i++) {
384         const Entity id = renderMeshComponentManager->GetEntity(i);
385         auto node = nodeSystem->GetNode(id);
386         if (!node) {
387             continue;
388         }
389         if (const auto jointMatrices = jointMatricesComponentManager->Read(id); jointMatrices) {
390             // Use the skinned aabb's.
391             const auto& jointMatricesComponent = *jointMatrices;
392             if (IntersectAabb(jointMatricesComponent.jointsAabbMin, jointMatricesComponent.jointsAabbMax, start, invDir,
393                     distance)) {
394                 const float centerDistance = Math::Magnitude(
395                     (jointMatricesComponent.jointsAabbMax + jointMatricesComponent.jointsAabbMin) * 0.5f - start);
396                 const Math::Vec3 hitPosition = start + direction * distance;
397                 result.push_back(RayCastResult { node, centerDistance, distance, hitPosition });
398             }
399         } else if (const auto worldMatrixId = worldMatrixComponentManager->GetComponentId(id);
400                    worldMatrixId != IComponentManager::INVALID_COMPONENT_ID) {
401             auto const renderMeshComponent = renderMeshComponentManager->Get(i);
402             if (const auto meshHandle = meshComponentManager.Read(renderMeshComponent.mesh); meshHandle) {
403                 auto const worldMatrixComponent = worldMatrixComponentManager->Get(worldMatrixId);
404                 const auto raycastResult = HitTestNode(*node, *meshHandle, worldMatrixComponent.matrix, start, invDir);
405                 if (raycastResult.node) {
406                     result.push_back(raycastResult);
407                 }
408             } else {
409                 CORE_LOG_W(
410                     "no mesh resource for entity %" PRIx64 ", resource %" PRIx64, id.id, renderMeshComponent.mesh.id);
411             }
412         }
413     }
414 
415     std::sort(
416         result.begin(), result.end(), [](const auto& lhs, const auto& rhs) { return (lhs.distance < rhs.distance); });
417 
418     return result;
419 }
420 
RayCast(const IEcs & ecs,const Math::Vec3 & start,const Math::Vec3 & direction,uint64_t layerMask) const421 vector<RayCastResult> Picking::RayCast(
422     const IEcs& ecs, const Math::Vec3& start, const Math::Vec3& direction, uint64_t layerMask) const
423 {
424     vector<RayCastResult> result;
425 
426     auto nodeSystem = GetSystem<INodeSystem>(ecs);
427     auto const& renderMeshComponentManager = GetManager<IRenderMeshComponentManager>(ecs);
428     auto const& layerComponentManager = GetManager<ILayerComponentManager>(ecs);
429     auto const& worldMatrixComponentManager = GetManager<IWorldMatrixComponentManager>(ecs);
430     auto const& jointMatricesComponentManager = GetManager<IJointMatricesComponentManager>(ecs);
431     auto const& meshComponentManager = *GetManager<IMeshComponentManager>(ecs);
432 
433     auto const invDir = DirectionVectorInverse(direction);
434     float distance = 0;
435     for (IComponentManager::ComponentId i = 0; i < renderMeshComponentManager->GetComponentCount(); i++) {
436         const Entity id = renderMeshComponentManager->GetEntity(i);
437         if (auto node = nodeSystem->GetNode(id); node) {
438             if (layerComponentManager->Get(id).layerMask & layerMask) {
439                 if (const auto jointMatrices = jointMatricesComponentManager->Read(id); jointMatrices) {
440                     // Use the skinned aabb's.
441                     const auto& jointMatricesComponent = *jointMatrices;
442                     if (IntersectAabb(jointMatricesComponent.jointsAabbMin, jointMatricesComponent.jointsAabbMax, start,
443                             invDir, distance)) {
444                         const float centerDistance = Math::Magnitude(
445                             (jointMatricesComponent.jointsAabbMax + jointMatricesComponent.jointsAabbMin) * 0.5f -
446                             start);
447                         const Math::Vec3 hitPosition = start + direction * distance;
448                         result.push_back(RayCastResult { node, centerDistance, distance, hitPosition });
449                     }
450                 } else {
451                     if (const auto worldMatrixId = worldMatrixComponentManager->GetComponentId(id);
452                         worldMatrixId != IComponentManager::INVALID_COMPONENT_ID) {
453                         auto const renderMeshComponent = renderMeshComponentManager->Get(i);
454                         if (const auto meshHandle = meshComponentManager.Read(renderMeshComponent.mesh); meshHandle) {
455                             auto const worldMatrixComponent = worldMatrixComponentManager->Get(worldMatrixId);
456                             const auto raycastResult =
457                                 HitTestNode(*node, *meshHandle, worldMatrixComponent.matrix, start, invDir);
458                             if (raycastResult.node) {
459                                 result.push_back(raycastResult);
460                             }
461                         } else {
462                             CORE_LOG_W("no mesh resource for entity %" PRIx64 ", resource %" PRIx64, id.id,
463                                 renderMeshComponent.mesh.id);
464                         }
465                     }
466                 }
467             }
468         }
469     }
470 
471     std::sort(
472         result.begin(), result.end(), [](const auto& lhs, const auto& rhs) { return (lhs.distance < rhs.distance); });
473 
474     return result;
475 }
476 
RayCast(const BASE_NS::Math::Vec3 & start,const BASE_NS::Math::Vec3 & direction,BASE_NS::array_view<const BASE_NS::Math::Vec3> triangles) const477 BASE_NS::vector<RayTriangleCastResult> Core3D::Picking::RayCast(const BASE_NS::Math::Vec3& start,
478     const BASE_NS::Math::Vec3& direction, BASE_NS::array_view<const BASE_NS::Math::Vec3> triangles) const
479 {
480     vector<RayTriangleCastResult> result;
481 
482     if (triangles.size() % 3 != 0) {
483         CORE_LOG_W("Number of triangles vertices not divisible by 3!");
484         return result;
485     }
486 
487     float distance = 0.f;
488     Math::Vec2 uv;
489     for (size_t ii = 0; ii < triangles.size(); ii += 3) {
490         if (IntersectTriangle(&triangles[ii], start, direction, distance, uv)) {
491             const Math::Vec3 hitPosition = start + direction * distance;
492 
493             result.push_back(RayTriangleCastResult { distance, hitPosition, uv, static_cast<uint64_t>(ii / 3) });
494         }
495     }
496 
497     return result;
498 }
499 
RayCastFromCamera(IEcs const & ecs,Entity camera,const Math::Vec2 & screenPos) const500 vector<RayCastResult> Picking::RayCastFromCamera(IEcs const& ecs, Entity camera, const Math::Vec2& screenPos) const
501 {
502     const auto* worldMatrixManager = GetManager<IWorldMatrixComponentManager>(ecs);
503     const auto* cameraManager = GetManager<ICameraComponentManager>(ecs);
504     if (!worldMatrixManager || !cameraManager) {
505         return vector<RayCastResult>();
506     }
507 
508     const auto wmcId = worldMatrixManager->GetComponentId(camera);
509     const auto ccId = cameraManager->GetComponentId(camera);
510     if (wmcId != IComponentManager::INVALID_COMPONENT_ID && ccId != IComponentManager::INVALID_COMPONENT_ID) {
511         const auto cameraComponent = cameraManager->Read(ccId);
512         const auto worldMatrixComponent = worldMatrixManager->Get(wmcId);
513         const Ray ray = RayFromCamera(*cameraComponent, worldMatrixComponent, screenPos);
514         return RayCast(ecs, ray.origin, ray.direction);
515     }
516 
517     return vector<RayCastResult>();
518 }
519 
RayCastFromCamera(IEcs const & ecs,Entity camera,const Math::Vec2 & screenPos,uint64_t layerMask) const520 vector<RayCastResult> Picking::RayCastFromCamera(
521     IEcs const& ecs, Entity camera, const Math::Vec2& screenPos, uint64_t layerMask) const
522 {
523     const auto* worldMatrixManager = GetManager<IWorldMatrixComponentManager>(ecs);
524     const auto* cameraManager = GetManager<ICameraComponentManager>(ecs);
525     if (!worldMatrixManager || !cameraManager) {
526         return vector<RayCastResult>();
527     }
528 
529     const auto wmcId = worldMatrixManager->GetComponentId(camera);
530     const auto ccId = cameraManager->GetComponentId(camera);
531     if (wmcId != IComponentManager::INVALID_COMPONENT_ID && ccId != IComponentManager::INVALID_COMPONENT_ID) {
532         const auto cameraComponent = cameraManager->Read(ccId);
533         const auto worldMatrixComponent = worldMatrixManager->Get(wmcId);
534         const Ray ray = RayFromCamera(*cameraComponent, worldMatrixComponent, screenPos);
535         return RayCast(ecs, ray.origin, ray.direction, layerMask);
536     }
537 
538     return vector<RayCastResult>();
539 }
540 
RayCastFromCamera(CORE_NS::IEcs const & ecs,CORE_NS::Entity camera,const BASE_NS::Math::Vec2 & screenPos,BASE_NS::array_view<const BASE_NS::Math::Vec3> triangles) const541 BASE_NS::vector<RayTriangleCastResult> Core3D::Picking::RayCastFromCamera(CORE_NS::IEcs const& ecs,
542     CORE_NS::Entity camera, const BASE_NS::Math::Vec2& screenPos,
543     BASE_NS::array_view<const BASE_NS::Math::Vec3> triangles) const
544 {
545     const auto* worldMatrixManager = GetManager<IWorldMatrixComponentManager>(ecs);
546     const auto* cameraManager = GetManager<ICameraComponentManager>(ecs);
547     if (!worldMatrixManager || !cameraManager) {
548         return vector<RayTriangleCastResult>();
549     }
550 
551     const auto wmcId = worldMatrixManager->GetComponentId(camera);
552     const auto ccId = cameraManager->GetComponentId(camera);
553     if (wmcId != IComponentManager::INVALID_COMPONENT_ID && ccId != IComponentManager::INVALID_COMPONENT_ID) {
554         const auto cameraComponent = cameraManager->Read(ccId);
555         const auto worldMatrixComponent = worldMatrixManager->Get(wmcId);
556         const Ray ray = RayFromCamera(*cameraComponent, worldMatrixComponent, screenPos);
557 
558         return RayCast(ray.origin, ray.direction, triangles);
559     }
560 
561     return BASE_NS::vector<RayTriangleCastResult>();
562 }
563 
GetWorldAABB(const Math::Mat4X4 & world,const Math::Vec3 & aabbMin,const Math::Vec3 & aabbMax) const564 MinAndMax Picking::GetWorldAABB(const Math::Mat4X4& world, const Math::Vec3& aabbMin, const Math::Vec3& aabbMax) const
565 {
566     return CORE3D_NS::GetWorldAABB(world, aabbMin, aabbMax);
567 }
568 
GetWorldMatrixComponentAABB(Entity entity,bool isRecursive,IEcs & ecs) const569 MinAndMax Picking::GetWorldMatrixComponentAABB(Entity entity, bool isRecursive, IEcs& ecs) const
570 {
571     MinAndMax mam;
572 
573     if (ISceneNode* node = GetSystem<INodeSystem>(ecs)->GetNode(entity); node) {
574         auto& renderMeshComponentManager = *GetManager<IRenderMeshComponentManager>(ecs);
575         auto& worldMatrixComponentManager = *GetManager<IWorldMatrixComponentManager>(ecs);
576         auto& jointworldMatrixComponentManager = *GetManager<IJointMatricesComponentManager>(ecs);
577         auto& meshComponentManager = *GetManager<IMeshComponentManager>(ecs);
578 
579         UpdateRecursiveAABB(renderMeshComponentManager, worldMatrixComponentManager, jointworldMatrixComponentManager,
580             meshComponentManager, *node, isRecursive, mam);
581     }
582 
583     return mam;
584 }
585 
GetTransformComponentAABB(Entity entity,bool isRecursive,IEcs & ecs) const586 MinAndMax Picking::GetTransformComponentAABB(Entity entity, bool isRecursive, IEcs& ecs) const
587 {
588     MinAndMax mam;
589 
590     if (ISceneNode* node = GetSystem<INodeSystem>(ecs)->GetNode(entity); node) {
591         auto& renderMeshComponentManager = *GetManager<IRenderMeshComponentManager>(ecs);
592         auto& transformComponentManager = *GetManager<ITransformComponentManager>(ecs);
593         auto& localMatrixComponentManager = *GetManager<ILocalMatrixComponentManager>(ecs);
594         auto& meshComponentManager = *GetManager<IMeshComponentManager>(ecs);
595 
596         UpdateRecursiveAABB(renderMeshComponentManager, transformComponentManager, localMatrixComponentManager,
597             meshComponentManager, *node, Math::Mat4X4(1.0f), isRecursive, mam);
598     }
599 
600     return mam;
601 }
602 
GetInterface(const Uid & uid) const603 const IInterface* Picking::GetInterface(const Uid& uid) const
604 {
605     if ((uid == IPicking::UID) || (uid == IInterface::UID)) {
606         return this;
607     }
608     return nullptr;
609 }
610 
GetInterface(const Uid & uid)611 IInterface* Picking::GetInterface(const Uid& uid)
612 {
613     if ((uid == IPicking::UID) || (uid == IInterface::UID)) {
614         return this;
615     }
616     return nullptr;
617 }
618 
Ref()619 void Picking::Ref() {}
620 
Unref()621 void Picking::Unref() {}
622 CORE3D_END_NAMESPACE()
623