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