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