[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / internal / event / collider-mesh-processor-impl.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-scene3d/internal/event/collider-mesh-processor-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/actors/actor-devel.h>
23 #include <dali/devel-api/events/hit-test-algorithm.h>
24 #include <dali/integration-api/adaptor-framework/adaptor.h>
25 #include <dali/public-api/events/touch-event.h>
26 #include <dali/public-api/render-tasks/render-task-list.h>
27 #include <algorithm>
28
29 // INTERNAL INCLUDES
30 #include <dali-scene3d/internal/controls/model/model-impl.h>
31 #include <dali-scene3d/internal/controls/scene-view/scene-view-impl.h>
32 #include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
33
34 namespace Dali::Scene3D::Internal
35 {
36 namespace
37 {
38 struct ColliderMeshData
39 {
40   Scene3D::Model                                model;
41   Scene3D::ModelNode                            modelNode;
42   const Dali::Scene3D::Algorithm::ColliderMesh& colliderMesh;
43   Matrix                                        worldMatrix{false};
44 };
45 using ColliderMeshDataContainer = std::vector<ColliderMeshData>;
46
47 void IterateThroughChildren(Actor actor, ColliderMeshDataContainer& meshData)
48 {
49   if(actor)
50   {
51     const auto childCount = actor.GetChildCount();
52     for(auto i = 0u; i < childCount; ++i)
53     {
54       Actor          child = actor.GetChildAt(i);
55       Scene3D::Model model = Scene3D::Model::DownCast(child);
56       if(model)
57       {
58         const Model::ColliderMeshContainer& colliderMeshes = GetImpl(model).GetNodeColliderMeshContainer();
59         for(const auto& colliderMeshItem : colliderMeshes)
60         {
61           // If actor name is empty, then assume the mesh if for the model itself
62           [[maybe_unused]] int     actorId   = colliderMeshItem.first;
63           Dali::Scene3D::ModelNode modelNode = colliderMeshItem.second;
64           if(modelNode)
65           {
66             meshData.push_back({model, modelNode, GetImplementation(modelNode).GetColliderMesh(), DevelActor::GetWorldTransform(modelNode)});
67           }
68         }
69       }
70       IterateThroughChildren(child, meshData);
71     }
72   }
73 }
74
75 class SceneViewTouchHandler
76 {
77 public:
78   bool operator()(Actor actor, const TouchEvent& touchEvent)
79   {
80     Scene3D::SceneView sceneView = Scene3D::SceneView::DownCast(actor);
81     bool               retVal(false);
82     if(sceneView)
83     {
84       // Get nav-mesh information for the children
85       std::vector<ColliderMeshData> meshData;
86       IterateThroughChildren(sceneView, meshData);
87
88       auto renderTask  = touchEvent.GetRenderTask();
89       auto cameraActor = renderTask.GetCameraActor();
90
91       const auto& result = touchEvent.GetScreenPosition(0);
92       float       x = 0.0f, y = 0.0f;
93       Vector3     origin;
94       Vector3     direction;
95       cameraActor.ScreenToLocal(x, y, result.x, result.y);
96
97       auto sceneViewRenderTask = GetImpl(sceneView).GetRenderTask();
98
99       if(sceneViewRenderTask && HitTestAlgorithm::BuildPickingRay(sceneViewRenderTask, result, origin, direction))
100       {
101         for(auto& mesh : meshData)
102         {
103           // Set transform for the collider mesh
104           const_cast<Dali::Scene3D::Algorithm::ColliderMesh&>(mesh.colliderMesh).SetSceneTransform(mesh.worldMatrix);
105           auto res = mesh.colliderMesh.RayFaceIntersect(origin, direction);
106           if(res != Dali::Scene3D::Algorithm::NavigationMesh::NULL_FACE)
107           {
108             Scene3D::Model            model     = mesh.model;
109             Scene3D::Internal::Model& modelImpl = GetImpl(model);
110             retVal                              = modelImpl.EmitMeshHitSignal(mesh.modelNode);
111             break;
112           }
113         }
114       }
115     }
116     return retVal;
117   }
118 };
119
120 } // unnamed namespace
121
122 ColliderMeshProcessor::ColliderMeshProcessor()
123 {
124   Adaptor::Get().RegisterProcessor(*this, true);
125 }
126
127 ColliderMeshProcessor::~ColliderMeshProcessor()
128 {
129   if(Adaptor::IsAvailable())
130   {
131     Adaptor::Get().UnregisterProcessor(*this, true);
132   }
133 }
134
135 void ColliderMeshProcessor::ColliderMeshChanged(Scene3D::Model model)
136 {
137   if(model.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
138   {
139     AddSceneViewParentToProcessingQueue(model);
140   }
141   else
142   {
143     // TODO: Check if signal already connected
144     model.OnSceneSignal().Connect(this, &ColliderMeshProcessor::ModelOnScene);
145   }
146 }
147
148 void ColliderMeshProcessor::ModelOnScene(Actor actor)
149 {
150   Scene3D::Model model = Scene3D::Model::DownCast(actor);
151   if(model)
152   {
153     AddSceneViewParentToProcessingQueue(model);
154   }
155   model.OnSceneSignal().Disconnect(this, &ColliderMeshProcessor::ModelOnScene);
156 }
157
158 void ColliderMeshProcessor::AddSceneViewParentToProcessingQueue(Scene3D::Model model)
159 {
160   Actor actor = model;
161   do
162   {
163     actor = actor.GetParent();
164     Scene3D::SceneView sceneView(Scene3D::SceneView::DownCast(actor));
165     if(sceneView)
166     {
167       mSceneViewsToProcess.push_back(sceneView);
168       break;
169     }
170   } while(actor);
171 }
172
173 void ColliderMeshProcessor::Process(bool /* postProcess */)
174 {
175   // Remove any duplicates
176   std::sort(mSceneViewsToProcess.begin(), mSceneViewsToProcess.end());
177   mSceneViewsToProcess.erase(std::unique(mSceneViewsToProcess.begin(), mSceneViewsToProcess.end()), mSceneViewsToProcess.end());
178
179   for(auto& sceneView : mSceneViewsToProcess)
180   {
181     std::vector<ColliderMeshData> meshData;
182     IterateThroughChildren(sceneView, meshData);
183
184     if(meshData.size())
185     {
186       // TODO: Get SceneView Camera parameters and calculate bounding box
187       // for now, it's a pass-thru algorthm
188       if(std::find(mConnectedSceneViews.begin(), mConnectedSceneViews.end(), sceneView) == mConnectedSceneViews.end())
189       {
190         mConnectedSceneViews.push_back(sceneView);
191         // TODO: Consider passing in camera parameters and meshData into SceneViewTouchHandler
192         sceneView.TouchedSignal().Connect(this, SceneViewTouchHandler());
193       }
194     }
195   }
196   mSceneViewsToProcess.clear();
197 }
198
199 } // namespace Dali::Scene3D::Internal