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 #include <scene_plugin/api/mesh_uid.h>
16 #include <scene_plugin/interface/intf_material.h>
17 
18 #include <3d/ecs/components/mesh_component.h>
19 
20 #include <meta/ext/object.h>
21 
22 #include "intf_node_private.h"
23 #include "intf_submesh_bridge.h"
24 #include "submesh_handler_uid.h"
25 #include "task_utils.h"
26 
27 using SCENE_NS::MakeTask;
28 
29 namespace {
30 class SubMeshHandler : public META_NS::ObjectFwd<SubMeshHandler, SCENE_NS::ClassId::SubMeshHandler,
31                            META_NS::ClassId::Object, SCENE_NS::IEcsProxyObject, SCENE_NS::ISubMeshBridge> {
32 public:
Initialize(CORE3D_NS::IMeshComponentManager * componentManager,CORE_NS::Entity entity,META_NS::IProperty::Ptr submeshes,INodeEcsInterfacePrivate::Ptr node)33     void Initialize(CORE3D_NS::IMeshComponentManager* componentManager, CORE_NS::Entity entity,
34         META_NS::IProperty::Ptr submeshes, INodeEcsInterfacePrivate::Ptr node) override
35     {
36         node_ = node;
37         if (node) {
38             componentManager_ = componentManager;
39             entity_ = entity;
40             submeshes_ = META_NS::ArrayProperty<SCENE_NS::ISubMesh::Ptr>(submeshes);
41             SetCommonListener(node->SceneHolder()->GetCommonEcsListener());
42             if (auto scene = node->EcsScene()) {
43                 scene->AddEngineTask(MakeTask(
44                                          [](auto obj) {
45                                              if (auto self = static_cast<SubMeshHandler*>(obj.get())) {
46                                                  self->DoComponentEvent(
47                                                      CORE_NS::IEcs::ComponentListener::EventType::MODIFIED,
48                                                      *self->componentManager_, self->entity_);
49                                              }
50                                              return false;
51                                          },
52                                          GetSelf()),
53                     false);
54             }
55         }
56     }
57 
Destroy()58     void Destroy() override
59     {
60         if (listener_) {
61             listener_->RemoveEntity(entity_, GetSelf<SCENE_NS::IEcsProxyObject>(),
62                 *static_cast<CORE_NS::IComponentManager*>(componentManager_));
63         }
64     }
65 
66     // IEcsProxyObject
67 
SetCommonListener(BASE_NS::shared_ptr<SCENE_NS::EcsListener> listener)68     void SetCommonListener(BASE_NS::shared_ptr<SCENE_NS::EcsListener> listener) override
69     {
70         if (listener_) {
71             listener_->RemoveEntity(entity_, GetSelf<SCENE_NS::IEcsProxyObject>(),
72                 *static_cast<CORE_NS::IComponentManager*>(componentManager_));
73         }
74         listener_ = listener;
75         if (listener_) {
76             listener_->AddEntity(entity_, GetSelf<SCENE_NS::IEcsProxyObject>(),
77                 *static_cast<CORE_NS::IComponentManager*>(componentManager_));
78         }
79     }
80 
DoEntityEvent(CORE_NS::IEcs::EntityListener::EventType,const CORE_NS::Entity &)81     void DoEntityEvent(CORE_NS::IEcs::EntityListener::EventType, const CORE_NS::Entity&) override {}
82 
GetMaterialFromEntity(SCENE_NS::IScene::Ptr scene,SceneHolder::Ptr sceneHolder,CORE_NS::Entity entity)83     SCENE_NS::IMaterial::Ptr GetMaterialFromEntity(
84         SCENE_NS::IScene::Ptr scene, SceneHolder::Ptr sceneHolder, CORE_NS::Entity entity)
85     {
86         // First see if we have this material already.
87         auto materials = scene->GetMaterials();
88         for (auto material : materials) {
89             auto ecsObject = interface_pointer_cast<SCENE_NS::IEcsObject>(material);
90             if (ecsObject && ecsObject->GetEntity() == entity) {
91                 return material;
92             }
93         }
94 
95         // Then try to resolve from uri.
96         BASE_NS::string uri;
97         if (sceneHolder->GetEntityUri(entity, uri)) {
98             return scene->GetMaterial(uri);
99         }
100 
101         BASE_NS::string name;
102         if (sceneHolder->GetEntityName(entity, name)) {
103             return scene->GetMaterial(name);
104         }
105 
106         return {};
107     }
108 
109     // ToDo: This runs on "engine queue", someday the property operations could be decoupled
110     // Another todo, this should presumably be closer to SceneHolder instead interleaved into the node impl
DoComponentEvent(CORE_NS::IEcs::ComponentListener::EventType type,const CORE_NS::IComponentManager & componentManager,const CORE_NS::Entity & entity)111     void DoComponentEvent(CORE_NS::IEcs::ComponentListener::EventType type,
112         const CORE_NS::IComponentManager& componentManager, const CORE_NS::Entity& entity) override
113     {
114         if (entity != entity_) {
115             CORE_LOG_W("DoComponentEvent called for wrong entity.");
116             return;
117         }
118 
119         if (type == CORE_NS::IEcs::ComponentListener::EventType::MODIFIED) {
120             // Read data here, we don't want to keep read lock when we update materials below
121             // as it can cause changes to material components further down the pipeline.
122             auto componentData = componentManager_->Get(entity);
123 
124             // This may be nasty from the application point of view, but lessens a risk
125             // that index changes would be propagated incorrectly to client app
126             if (submeshes_->GetSize() > componentData.submeshes.size()) {
127                 submeshes_->Reset();
128             }
129 
130             while (submeshes_->GetSize() < componentData.submeshes.size()) {
131                 auto submesh = GetObjectRegistry().Create<SCENE_NS::ISubMesh>(SCENE_NS::ClassId::SubMesh);
132                 submeshes_->AddValue(submesh);
133             }
134 
135             for (auto i = 0; i < componentData.submeshes.size(); ++i) {
136                 const auto& submesh = componentData.submeshes.at(i);
137                 auto ptr = submeshes_->GetValueAt(i);
138                 // TODO: These could be initialized only once.
139                 ptr->Name()->SetValue(BASE_NS::string("Submesh ") + BASE_NS::to_string(i));
140 
141                 META_NS::SetValue(ptr->AABBMin(), submesh.aabbMin);
142                 META_NS::SetValue(ptr->AABBMax(), submesh.aabbMax);
143                 META_NS::SetValue(ptr->RenderSortLayerOrder(), submesh.renderSortLayerOrder);
144                 META_NS::SetValue(ptr->Handle(), i);
145 
146                 auto priv = interface_pointer_cast<SCENE_NS::ISubMeshPrivate>(ptr);
147                 if (CORE_NS::EntityUtil::IsValid(submesh.material)) {
148                     if (auto node = node_.lock()) {
149                         // This would appreciate more async approach
150                         auto sceneHolder = node->SceneHolder();
151                         auto scene = interface_pointer_cast<SCENE_NS::IScene>(node->EcsScene());
152 
153                         if (scene && sceneHolder) {
154                             auto material = GetMaterialFromEntity(scene, sceneHolder, submesh.material);
155                             priv->SetDefaultMaterial(material);
156                         }
157                     }
158                 } else {
159                     priv->SetDefaultMaterial({});
160                 }
161                 priv->AddSubmeshBrigde(GetSelf<ISubMeshBridge>());
162             }
163         }
164     }
165 
166 public: // ISubMeshBridge
SetRenderSortLayerOrder(size_t index,uint8_t value)167     void SetRenderSortLayerOrder(size_t index, uint8_t value) override
168     {
169 #ifdef SYNC_ACCESS_ECS
170         if (auto handle = componentManager_->Write(entity_)) {
171             if (index >= 0 && index < handle->submeshes.size()) {
172                 handle->submeshes[index].renderSortOrder = value;
173             }
174         }
175 #else
176         if (auto node = node_.lock()) {
177             if (auto scene = node->EcsScene()) {
178                 scene->AddEngineTask(MakeTask(
179                                          [index, value](auto selfNode) {
180                                              if (auto self = interface_pointer_cast<SCENE_NS::IEcsObject>(selfNode)) {
181                                                  if (auto node = interface_cast<INodeEcsInterfacePrivate>(selfNode)) {
182                                                      if (auto sceneHolder = node->SceneHolder()) {
183                                                          sceneHolder->SetSubmeshRenderSortOrder(
184                                                              self->GetEntity(), index, value);
185                                                      }
186                                                  }
187                                              }
188                                              return false;
189                                          },
190                                          node),
191                     false);
192             }
193         }
194 #endif
195         if (index < submeshes_->GetSize()) {
196             META_NS::SetValue(submeshes_->GetValueAt(index)->RenderSortLayerOrder(), value);
197         }
198     }
199 
SetMaterialToEcs(size_t index,SCENE_NS::IMaterial::Ptr & material)200     void SetMaterialToEcs(size_t index, SCENE_NS::IMaterial::Ptr& material) override
201     {
202         if (auto node = node_.lock()) {
203             if (auto scene = node->EcsScene()) {
204                 scene->AddEngineTask(
205                     MakeTask(
206                         [mat = SCENE_NS::IMaterial::WeakPtr(material), index](auto node) {
207                             if (auto self = interface_pointer_cast<SCENE_NS::IEcsObject>(node)) {
208                                 CORE_NS::Entity entity {};
209                                 if (auto ecsObject = interface_pointer_cast<SCENE_NS::IEcsObject>(mat.lock())) {
210                                     entity = ecsObject->GetEntity();
211                                 }
212                                 auto sceneHolder = node->SceneHolder();
213                                 if (sceneHolder) {
214                                     sceneHolder->SetMaterial(self->GetEntity(), entity, index);
215                                 }
216                             }
217                             return false;
218                         },
219                         node_),
220                     false);
221             }
222         }
223     }
224 
SetAABBMin(size_t index,BASE_NS::Math::Vec3 vec)225     void SetAABBMin(size_t index, BASE_NS::Math::Vec3 vec) override
226     {
227 #ifdef SYNC_ACCESS_ECS
228         if (auto handle = componentManager_->Write(entity_)) {
229             if (index >= 0 && index < handle->submeshes.size()) {
230                 handle->submeshes[index].aabbMin = vec;
231                 handle->aabbMin = BASE_NS::Math::min(handle->aabbMin, vec);
232             }
233         }
234 #else
235         if (auto node = node_.lock()) {
236             if (auto scene = node->EcsScene()) {
237                 scene->AddEngineTask(MakeTask(
238                                          [index, vec](auto selfNode) {
239                                              if (auto self = interface_pointer_cast<SCENE_NS::IEcsObject>(selfNode)) {
240                                                  if (auto node = interface_cast<INodeEcsInterfacePrivate>(selfNode)) {
241                                                      if (auto sceneHolder = node->SceneHolder()) {
242                                                          sceneHolder->SetSubmeshAABBMin(self->GetEntity(), index, vec);
243                                                      }
244                                                  }
245                                              }
246                                              return false;
247                                          },
248                                          node),
249                     false);
250             }
251         }
252 #endif
253         if (index < submeshes_->GetSize()) {
254             META_NS::SetValue(submeshes_->GetValueAt(index)->AABBMin(), vec);
255         }
256     }
257 
SetAABBMax(size_t index,BASE_NS::Math::Vec3 vec)258     void SetAABBMax(size_t index, BASE_NS::Math::Vec3 vec) override
259     {
260 #ifdef SYNC_ACCESS_ECS
261         if (auto handle = componentManager_->Write(entity_)) {
262             if (index >= 0 && index < handle->submeshes.size()) {
263                 handle->submeshes[index].aabbMax = vec;
264                 handle->aabbMax = BASE_NS::Math::max(handle->aabbMax, vec);
265             }
266         }
267 #else
268         if (auto node = node_.lock()) {
269             if (auto scene = node->EcsScene()) {
270                 scene->AddEngineTask(MakeTask(
271                                          [index, vec](auto selfNode) {
272                                              if (auto self = interface_pointer_cast<SCENE_NS::IEcsObject>(selfNode)) {
273                                                  if (auto node = interface_cast<INodeEcsInterfacePrivate>(selfNode)) {
274                                                      if (auto sceneHolder = node->SceneHolder()) {
275                                                          sceneHolder->SetSubmeshAABBMax(self->GetEntity(), index, vec);
276                                                      }
277                                                  }
278                                              }
279                                              return false;
280                                          },
281                                          node),
282                     false);
283             }
284         }
285 #endif
286         if (index < submeshes_->GetSize()) {
287             META_NS::SetValue(submeshes_->GetValueAt(index)->AABBMax(), vec);
288         }
289     }
290 
RemoveSubmesh(int32_t index)291     void RemoveSubmesh(int32_t index) override
292     {
293 #ifdef SYNC_ACCESS_ECS
294         if (auto handle = componentManager_->Write(entity_)) {
295             if (index < 0) {
296                 handle->submeshes.clear();
297                 handle->aabbMin = { 0.f, 0.f, 0.f };
298                 handle->aabbMax = { 0.f, 0.f, 0.f };
299             } else if (index < handle->submeshes.size()) {
300                 handle->submeshes.erase(handle->submeshes.begin() + index);
301                 for (const auto& submesh : handle->submeshes) {
302                     handle->aabbMin = BASE_NS::Math::min(handle->aabbMin, submesh.aabbMin);
303                     handle->aabbMax = BASE_NS::Math::max(handle->aabbMax, submesh.aabbMax);
304                 }
305             }
306         }
307 #else
308         if (auto node = node_.lock()) {
309             if (auto scene = node->EcsScene()) {
310                 scene->AddEngineTask(MakeTask(
311                                          [index](auto selfNode) {
312                                              if (auto self = interface_pointer_cast<SCENE_NS::IEcsObject>(selfNode)) {
313                                                  if (auto node = interface_cast<INodeEcsInterfacePrivate>(selfNode)) {
314                                                      if (auto sceneHolder = node->SceneHolder()) {
315                                                          sceneHolder->RemoveSubmesh(self->GetEntity(), index);
316                                                      }
317                                                  }
318                                              }
319                                              return false;
320                                          },
321                                          node_),
322                     false);
323             }
324         }
325 #endif
326     }
327 
GetEntity() const328     CORE_NS::Entity GetEntity() const override
329     {
330         return entity_;
331     }
332 
333     BASE_NS::shared_ptr<SCENE_NS::EcsListener> listener_ {};
334     CORE3D_NS::IMeshComponentManager* componentManager_ {};
335     CORE_NS::Entity entity_ {};
336     META_NS::ArrayProperty<SCENE_NS::ISubMesh::Ptr> submeshes_ {};
337     INodeEcsInterfacePrivate::WeakPtr node_ {};
338 };
339 } // namespace
SCENE_BEGIN_NAMESPACE()340 SCENE_BEGIN_NAMESPACE()
341 
342 void RegisterSubMeshHandler()
343 {
344     META_NS::GetObjectRegistry().RegisterObjectType<SubMeshHandler>();
345 }
UnregisterSubMeshHandler()346 void UnregisterSubMeshHandler()
347 {
348     META_NS::GetObjectRegistry().UnregisterObjectType<SubMeshHandler>();
349 }
350 SCENE_END_NAMESPACE()