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