c205433d011ae5e31ca50b622cde7d3fb3d42fc3
[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                  list      = Stage::GetCurrent().GetRenderTaskList();
98       [[maybe_unused]] auto taskCount = list.GetTaskCount();
99       renderTask                      = list.GetTask(list.GetTaskCount() - 1);
100
101       if(HitTestAlgorithm::BuildPickingRay(renderTask, result, origin, direction))
102       {
103         for(auto& mesh : meshData)
104         {
105           // Set transform for the collider mesh
106           const_cast<Dali::Scene3D::Algorithm::ColliderMesh&>(mesh.colliderMesh).SetSceneTransform(mesh.worldMatrix);
107           auto res = mesh.colliderMesh.RayFaceIntersect(origin, direction);
108           if(res != Dali::Scene3D::Algorithm::NavigationMesh::NULL_FACE)
109           {
110             Scene3D::Model            model     = mesh.model;
111             Scene3D::Internal::Model& modelImpl = GetImpl(model);
112             retVal                              = modelImpl.EmitMeshHitSignal(mesh.modelNode);
113             break;
114           }
115         }
116       }
117     }
118     return retVal;
119   }
120 };
121
122 } // unnamed namespace
123
124 ColliderMeshProcessor::ColliderMeshProcessor()
125 {
126   Adaptor::Get().RegisterProcessor(*this, true);
127 }
128
129 ColliderMeshProcessor::~ColliderMeshProcessor()
130 {
131   if(Adaptor::IsAvailable())
132   {
133     Adaptor::Get().UnregisterProcessor(*this, true);
134   }
135 }
136
137 void ColliderMeshProcessor::ColliderMeshChanged(Scene3D::Model model)
138 {
139   if(model.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
140   {
141     AddSceneViewParentToProcessingQueue(model);
142   }
143   else
144   {
145     // TODO: Check if signal already connected
146     model.OnSceneSignal().Connect(this, &ColliderMeshProcessor::ModelOnScene);
147   }
148 }
149
150 void ColliderMeshProcessor::ModelOnScene(Actor actor)
151 {
152   Scene3D::Model model = Scene3D::Model::DownCast(actor);
153   if(model)
154   {
155     AddSceneViewParentToProcessingQueue(model);
156   }
157   model.OnSceneSignal().Disconnect(this, &ColliderMeshProcessor::ModelOnScene);
158 }
159
160 void ColliderMeshProcessor::AddSceneViewParentToProcessingQueue(Scene3D::Model model)
161 {
162   Actor actor = model;
163   do
164   {
165     actor = actor.GetParent();
166     Scene3D::SceneView sceneView(Scene3D::SceneView::DownCast(actor));
167     if(sceneView)
168     {
169       mSceneViewsToProcess.push_back(sceneView);
170       break;
171     }
172   } while(actor);
173 }
174
175 void ColliderMeshProcessor::Process(bool /* postProcess */)
176 {
177   // Remove any duplicates
178   std::sort(mSceneViewsToProcess.begin(), mSceneViewsToProcess.end());
179   mSceneViewsToProcess.erase(std::unique(mSceneViewsToProcess.begin(), mSceneViewsToProcess.end()), mSceneViewsToProcess.end());
180
181   for(auto& sceneView : mSceneViewsToProcess)
182   {
183     std::vector<ColliderMeshData> meshData;
184     IterateThroughChildren(sceneView, meshData);
185
186     if(meshData.size())
187     {
188       // TODO: Get SceneView Camera parameters and calculate bounding box
189       // for now, it's a pass-thru algorthm
190       if(std::find(mConnectedSceneViews.begin(), mConnectedSceneViews.end(), sceneView) == mConnectedSceneViews.end())
191       {
192         mConnectedSceneViews.push_back(sceneView);
193         // TODO: Consider passing in camera parameters and meshData into SceneViewTouchHandler
194         sceneView.TouchedSignal().Connect(this, SceneViewTouchHandler());
195       }
196     }
197   }
198   mSceneViewsToProcess.clear();
199 }
200
201 } // namespace Dali::Scene3D::Internal