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