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