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/material_uid.h>
16 #include <scene_plugin/api/mesh_uid.h>
17 #include <scene_plugin/interface/intf_material.h>
18 
19 #include <meta/ext/concrete_base_object.h>
20 #include <meta/ext/implementation_macros.h>
21 
22 #include "bind_templates.inl"
23 #include "intf_submesh_bridge.h"
24 #include "node_impl.h"
25 #include "submesh_handler_uid.h"
26 #include "task_utils.h"
27 
28 using SCENE_NS::MakeTask;
29 #include "intf_submesh_bridge.h"
30 
31 namespace {
32 class MeshImpl
33     : public META_NS::ConcreteBaseMetaObjectFwd<MeshImpl, NodeImpl, SCENE_NS::ClassId::Mesh, SCENE_NS::IMesh> {
34     static constexpr BASE_NS::string_view MESH_COMPONENT_NAME = "MeshComponent";
35     static constexpr size_t MESH_COMPONENT_NAME_LEN = MESH_COMPONENT_NAME.size() + 1;
36     static constexpr BASE_NS::string_view MESH_AABBMIN = "MeshComponent.aabbMin";
37     static constexpr BASE_NS::string_view MESH_AABBMAX = "MeshComponent.aabbMax";
38 
Build(const IMetadata::Ptr & data)39     bool Build(const IMetadata::Ptr& data) override
40     {
41         bool ret = false;
42         if (ret = NodeImpl::Build(data); ret) {
43             PropertyNameMask()[MESH_COMPONENT_NAME] = { MESH_AABBMIN.substr(MESH_COMPONENT_NAME_LEN),
44                 MESH_AABBMAX.substr(MESH_COMPONENT_NAME_LEN) };
45 
46             // subscribe material changes
47             MaterialOverride()->OnChanged()->AddHandler(
48                 META_NS::MakeCallback<META_NS::IOnChanged>([this]() { SyncMaterialOverrideToSubmeshes(); }));
49         }
50         return ret;
51     }
52     META_IMPLEMENT_INTERFACE_PROPERTY(
53         SCENE_NS::IMesh, SCENE_NS::IMaterial::Ptr, MaterialOverride, {}, )
54     META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(SCENE_NS::IMesh, BASE_NS::string, MaterialOverrideUri, {},
55         META_NS::DEFAULT_PROPERTY_FLAGS | META_NS::ObjectFlagBits::INTERNAL)
56 
57     META_IMPLEMENT_INTERFACE_READONLY_ARRAY_PROPERTY(
58         SCENE_NS::IMesh, SCENE_NS::ISubMesh::Ptr, SubMeshes, {}, )
59     META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(SCENE_NS::IMesh, BASE_NS::Math::Vec3, AABBMin,
60         BASE_NS::Math::Vec3(0.f, 0.f, 0.f), META_NS::DEFAULT_PROPERTY_FLAGS_NO_SER)
61     META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(SCENE_NS::IMesh, BASE_NS::Math::Vec3, AABBMax,
62         BASE_NS::Math::Vec3(0.f, 0.f, 0.f), META_NS::DEFAULT_PROPERTY_FLAGS_NO_SER)
63 
64     void SyncMaterialOverrideToSubmeshes()
65     {
66         auto material = MaterialOverride()->GetValue();
67         if (material) {
68 
69             for (auto& submesh : SubMeshes()->GetValue()) {
70                 auto submeshPrivate = interface_cast<SCENE_NS::ISubMeshPrivate>(submesh);
71                 if (submeshPrivate) {
72                     submeshPrivate->SetOverrideMaterial(material);
73                 }
74             }
75         } else {
76             META_ACCESS_PROPERTY(MaterialOverrideUri)->SetValue({});
77             // Reset submeshes back to original value.
78             for (auto& submesh : SubMeshes()->GetValue()) {
79                 auto submeshPrivate = interface_cast<SCENE_NS::ISubMeshPrivate>(submesh);
80                 if (submeshPrivate) {
81                     submeshPrivate->SetOverrideMaterial({});
82                 }
83             }
84         }
85     }
86 
CompleteInitialization(const BASE_NS::string & path)87     bool CompleteInitialization(const BASE_NS::string& path) override
88     {
89         if (!NodeImpl::CompleteInitialization(path)) {
90             return false;
91         }
92 
93         // Ensure override material is connected to ecs.
94         auto overrideMaterial = GetValue(MaterialOverride());
95         if (overrideMaterial) {
96             BindObject(interface_pointer_cast<INode>(overrideMaterial));
97         }
98 
99         // Ensure submesh materials are connected to ecs.
100         auto submeshes = META_ACCESS_PROPERTY(SubMeshes)->GetValue();
101         for (auto i = 0; i < submeshes.size(); ++i) {
102             auto submesh = submeshes.at(i);
103             auto material = META_NS::GetValue(submesh->Material());
104             if (material) {
105                 BindObject(interface_pointer_cast<INode>(material));
106             }
107         }
108 
109         auto meta = interface_pointer_cast<META_NS::IMetadata>(ecsObject_);
110         BindChanges(propHandler_, META_ACCESS_PROPERTY(AABBMin), meta, MESH_AABBMIN);
111         BindChanges(propHandler_, META_ACCESS_PROPERTY(AABBMax), meta, MESH_AABBMAX);
112 
113         if (auto ecs = EcsScene()->GetEcs()) {
114             submeshHandler_ = GetObjectRegistry().Create<SCENE_NS::ISubMeshBridge>(SCENE_NS::ClassId::SubMeshHandler);
115             submeshHandler_->Initialize(CORE_NS::GetManager<CORE3D_NS::IMeshComponentManager>(*ecs),
116                 EcsObject()->GetEntity(), META_ACCESS_PROPERTY(SubMeshes), GetSelf<INodeEcsInterfacePrivate>());
117         }
118 
119         // Ok, the initialization has reached the point where we have a scene and ecs initialized
120         // check if we have material URIs that should be progressed before we sync the status with ecs
121         auto materialOverrideUri = MaterialOverrideUri()->GetValue();
122         if (!materialOverrideUri.empty()) {
123             SCENE_NS::IMaterial::Ptr material = GetScene()->GetMaterial(materialOverrideUri);
124             if (material) {
125                 MaterialOverride()->SetValue(material);
126             }
127         }
128 
129         // Update material override.
130         SyncMaterialOverrideToSubmeshes();
131 
132         // Also restore submesh materials.
133         for (auto i = 0; i < submeshes.size(); ++i) {
134             auto submesh = submeshes.at(i);
135             auto materialUri = META_NS::GetValue(submesh->MaterialUri());
136             if (!materialUri.empty()) {
137                 submesh->Material()->SetValue(GetScene()->GetMaterial(materialUri));
138             }
139         }
140         return true;
141     }
142 
SetPath(const BASE_NS::string & path,const BASE_NS::string & name,CORE_NS::Entity entity)143     void SetPath(const BASE_NS::string& path, const BASE_NS::string& name, CORE_NS::Entity entity) override
144     {
145         META_ACCESS_PROPERTY(Path)->SetValue(path);
146         META_ACCESS_PROPERTY(Name)->SetValue(name);
147 
148         if (auto iscene = GetScene()) {
149             iscene->UpdateCachedReference(GetSelf<SCENE_NS::INode>());
150         }
151         if (auto scene = EcsScene()) {
152             SetStatus(SCENE_NS::INode::NODE_STATUS_CONNECTING);
153             initializeTaskToken_ = scene->AddEngineTask(
154                 MakeTask(
155                     [name, fullpath = path + name](auto selfObject) {
156                         if (auto self = static_pointer_cast<NodeImpl>(selfObject)) {
157                             if (auto sceneHolder = self->SceneHolder()) {
158                                 CORE_NS::Entity meshEntinty {};
159                                 if (sceneHolder->FindMesh(name, fullpath, meshEntinty)) {
160                                     SCENE_PLUGIN_VERBOSE_LOG("binding mesh: %s", name.c_str());
161                                     if (auto proxyIf =
162                                             interface_pointer_cast<SCENE_NS::IEcsProxyObject>(self->EcsObject())) {
163                                         proxyIf->SetCommonListener(sceneHolder->GetCommonEcsListener());
164                                     }
165                                     self->EcsObject()->DefineTargetProperties(self->PropertyNameMask());
166                                     self->EcsObject()->SetEntity(sceneHolder->GetEcs(), meshEntinty);
167                                     sceneHolder->QueueApplicationTask(
168                                         MakeTask(
169                                             [name](auto selfObject) {
170                                                 if (auto self = static_pointer_cast<NodeImpl>(selfObject)) {
171                                                     self->CompleteInitialization(name);
172                                                     self->SetStatus(SCENE_NS::INode::NODE_STATUS_CONNECTED);
173                                                     if (auto node =
174                                                             interface_pointer_cast<SCENE_NS::INode>(selfObject)) {
175                                                         META_NS::Invoke<META_NS::IOnChanged>(node->OnLoaded());
176                                                     }
177                                                 }
178                                                 return false;
179                                             },
180                                             selfObject),
181                                         false);
182                                 } else {
183                                     CORE_LOG_D("Could not find '%s' mesh", name.c_str());
184                                     sceneHolder->QueueApplicationTask(
185                                         MakeTask(
186                                             [](auto selfObject) {
187                                                 if (auto self = static_pointer_cast<NodeImpl>(selfObject)) {
188                                                     self->SetStatus(SCENE_NS::INode::NODE_STATUS_DISCONNECTED);
189                                                 }
190                                                 return false;
191                                             },
192                                             selfObject),
193                                         false);
194                                 }
195                             }
196                         }
197                         return false;
198                     },
199                     GetSelf()),
200                 false);
201         }
202     }
203 
BuildChildren(SCENE_NS::INode::BuildBehavior)204     bool BuildChildren(SCENE_NS::INode::BuildBehavior) override
205     {
206         // in typical cases we should not have children
207         if (META_NS::GetValue(META_ACCESS_PROPERTY(Status)) == SCENE_NS::INode::NODE_STATUS_CONNECTED) {
208             SetStatus(SCENE_NS::INode::NODE_STATUS_FULLY_CONNECTED);
209             META_NS::Invoke<META_NS::IOnChanged>(OnBound());
210             bound_ = true;
211         }
212         return true;
213     }
214 
ShouldExport() const215     bool ShouldExport() const override
216     {
217         for (auto& submesh : SubMeshes()->GetValue()) {
218             auto meta = interface_pointer_cast<META_NS::IMetadata>(submesh);
219             if (HasChangedProperties(*meta)) {
220                 return true;
221             }
222         }
223         return false;
224     }
225     //todo
226     /*
227     bool Export(
228         META_NS::Serialization::IExportContext& context, META_NS::Serialization::ClassPrimitive& value) const override
229     {
230         BASE_NS::vector<SCENE_NS::IMaterial::Ptr> nonSerializedMaterials;
231 
232         auto hasOverrideMaterialUri = !GetValue(MaterialOverrideUri()).empty();
233         auto overrideMaterial = GetValue(MaterialOverride());
234         if (overrideMaterial && hasOverrideMaterialUri) {
235             if (interface_pointer_cast<META_NS::IObjectFlags>(overrideMaterial)->GetObjectFlags() &
236                 META_NS::ObjectFlagBits::SERIALIZE) {
237                 nonSerializedMaterials.push_back(overrideMaterial);
238             }
239         }
240 
241         for (auto& submesh : SubMeshes()->ToVector()) {
242             auto hasSubmeshMaterialUri = !GetValue(submesh->MaterialUri()).empty();
243             auto submeshMaterial = GetValue(submesh->Material());
244             if (submeshMaterial && hasSubmeshMaterialUri) {
245                 if (interface_pointer_cast<META_NS::IObjectFlags>(submeshMaterial)->GetObjectFlags() &
246                     META_NS::ObjectFlagBits::SERIALIZE) {
247                     nonSerializedMaterials.push_back(submeshMaterial);
248                 }
249             }
250         }
251 
252         // Do not export these materials.
253         for (auto& material : nonSerializedMaterials) {
254             META_NS::SetObjectFlags(material, META_NS::ObjectFlagBits::SERIALIZE, false);
255         }
256 
257         bool result = Fwd::Export(context, value);
258 
259         // Put back material serialization info.
260         for (auto& material : nonSerializedMaterials) {
261             META_NS::SetObjectFlags(material, META_NS::ObjectFlagBits::SERIALIZE, true);
262         }
263 
264         return result;
265     }
266     */
267     // Set given material for all the submeshes.
SetMaterial(const SCENE_NS::IMaterial::Ptr material)268     void SetMaterial(const SCENE_NS::IMaterial::Ptr material) override
269     {
270         MaterialOverride()->SetValue(material);
271     }
272 
273     // Set material for the spesific submesh.
SetMaterial(size_t index,const SCENE_NS::IMaterial::Ptr & material)274     void SetMaterial(size_t index, const SCENE_NS::IMaterial::Ptr& material) override
275     {
276         if (auto node = interface_pointer_cast<SCENE_NS::INode>(material)) {
277             node->Status()->OnChanged()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>(
278                 [mat = BASE_NS::weak_ptr(material)](const auto& self, const auto& status, const auto& index) {
279                     if (self && status && status->GetValue() == SCENE_NS::INode::NODE_STATUS_FULLY_CONNECTED) {
280                         static_cast<MeshImpl*>(self.get())->SetMaterialToScene((int32_t)index, mat.lock());
281                     }
282                 },
283                 GetSelf(), node->Status(), index));
284             if (auto status = META_NS::GetValue(node->Status());
285                 status == SCENE_NS::INode::NODE_STATUS_FULLY_CONNECTED ||
286                 status == SCENE_NS::INode::NODE_STATUS_CONNECTED) {
287                 SetMaterialToScene(static_cast<int64_t>(index), material);
288             }
289         }
290     }
291 
SetMaterialToScene(int64_t index,const SCENE_NS::IMaterial::Ptr & material)292     void SetMaterialToScene(int64_t index, const SCENE_NS::IMaterial::Ptr& material)
293     {
294         auto count = SubMeshes()->GetSize();
295         if (index == -1) {
296             for (auto i = 0; i < count; ++i) {
297                 SubMeshes()->GetValueAt(i)->SetMaterial(material);
298             }
299         } else if (index < count) {
300             SubMeshes()->GetValueAt(index)->SetMaterial(material);
301         }
302     }
303 
GetMaterial(size_t index)304     SCENE_NS::IMaterial::Ptr GetMaterial(size_t index) override
305     {
306         if (index < META_ACCESS_PROPERTY(SubMeshes)->GetSize()) {
307             return META_ACCESS_PROPERTY(SubMeshes)->GetValueAt(index)->Material()->GetValue();
308         }
309 
310         SCENE_NS::IMaterial::Ptr ret {};
311         if (auto iscene = GetScene()) {
312             if (META_NS::GetValue(iscene->Asynchronous()) == false) {
313                 auto entityName = SceneHolder()->GetMaterialName(EcsObject()->GetEntity(), index);
314                 if (!entityName.empty()) {
315                     ret = iscene->GetMaterial(entityName);
316                 }
317             }
318         }
319 
320         // This path is not needed if material exists, in async mode it could still save the day, though
321         if (!ret) {
322             ret = GetObjectRegistry().Create<SCENE_NS::IMaterial>(SCENE_NS::ClassId::Material);
323 
324             if (auto scene = EcsScene()) {
325                 scene->AddEngineTask(
326                     MakeTask(
327                         [ret, index](auto selfObject) {
328                             if (auto self = interface_pointer_cast<SCENE_NS::IEcsObject>(selfObject)) {
329                                 if (auto node = static_pointer_cast<NodeImpl>(selfObject)) {
330                                     if (auto sceneHolder = node->SceneHolder()) {
331                                         auto entityName = sceneHolder->GetMaterialName(self->GetEntity(), index);
332                                         if (!entityName.empty()) {
333                                             BASE_NS::string name(entityName.data(), entityName.size());
334                                             sceneHolder->QueueApplicationTask(
335                                                 MakeTask(
336                                                     [name, ret](auto selfObject) {
337                                                         if (auto node = static_pointer_cast<NodeImpl>(selfObject)) {
338                                                             auto scene = node->EcsScene();
339                                                             auto object = META_NS::GetObjectRegistry().Create(
340                                                                 SCENE_NS::ClassId::EcsObject);
341                                                             if (auto ecsObject =
342                                                                     interface_pointer_cast<SCENE_NS::IEcsObject>(
343                                                                         object)) {
344                                                                 if (auto privateInit = interface_pointer_cast<
345                                                                         INodeEcsInterfacePrivate>(ret)) {
346                                                                     privateInit->Initialize(scene, ecsObject, {}, "",
347                                                                         name, node->SceneHolder(), {});
348                                                                 }
349                                                             }
350                                                         }
351                                                         return false;
352                                                     },
353                                                     selfObject),
354                                                 false);
355                                         }
356                                     }
357                                 }
358                             }
359                             return false;
360                         },
361                         GetSelf()),
362                     false);
363             }
364         }
365         return ret;
366     }
367 
SetRenderSortLayerOrder(size_t index,uint8_t value)368     void SetRenderSortLayerOrder(size_t index, uint8_t value) override
369     {
370         if (submeshHandler_) {
371             submeshHandler_->SetRenderSortLayerOrder(index, value);
372         } else {
373             CORE_LOG_W("%s: mesh is not ready yet, call has no effect", __func__);
374         }
375     }
376 
GetRenderSortLayerOrder(size_t index) const377     uint8_t GetRenderSortLayerOrder(size_t index) const override
378     {
379         if (index < SubMeshes()->GetSize()) {
380             return META_NS::GetValue(SubMeshes()->GetValueAt(index)->RenderSortLayerOrder());
381         }
382         return 0u; // default in mesh component
383     }
384 
385     template<typename INDEX_TYPE>
UpdateMeshFromArrays(SCENE_NS::MeshGeometryArrayPtr<INDEX_TYPE> arrays,const RENDER_NS::IndexType & indexType,bool append=false)386     void UpdateMeshFromArrays(
387         SCENE_NS::MeshGeometryArrayPtr<INDEX_TYPE> arrays, const RENDER_NS::IndexType& indexType, bool append = false)
388     {
389         if (auto sh = SceneHolder()) {
390             if (!append) {
391                 // Need to invalidate materials up front
392                 SetMaterial(SCENE_NS::IMaterial::Ptr {});
393             }
394             SetStatus(SCENE_NS::INode::NODE_STATUS_CONNECTING);
395             sh->QueueEngineTask(MakeTask(
396                                     [arrays, indexType, append](auto privateIntf) {
397                                         // create new mesh entity
398                                         if (auto sh = privateIntf->SceneHolder()) {
399                                             auto existingEntity = privateIntf->EcsObject()->GetEntity();
400                                             auto newEntity = sh->template CreateMeshFromArrays<INDEX_TYPE>(
401                                                 "TempMesh", arrays, indexType, existingEntity, append);
402                                             sh->QueueApplicationTask(
403                                                 MakeTask(
404                                                     [](auto strong) {
405                                                         if (auto self = static_cast<MeshImpl*>(strong.get())) {
406                                                             self->SetStatus(SCENE_NS::INode::NODE_STATUS_CONNECTED);
407                                                             self->SetStatus(
408                                                                 SCENE_NS::INode::NODE_STATUS_FULLY_CONNECTED);
409                                                             if (META_NS::GetValue(self->MaterialOverride())) {
410                                                                 self->SyncMaterialOverrideToSubmeshes();
411                                                             }
412                                                         }
413                                                         return false;
414                                                     },
415                                                     privateIntf),
416                                                 false);
417                                         }
418                                         return false;
419                                     },
420                                     GetSelf<INodeEcsInterfacePrivate>()),
421                 false);
422         }
423     }
424 
UpdateMeshFromArraysI16(SCENE_NS::MeshGeometryArrayPtr<uint16_t> arrays)425     void UpdateMeshFromArraysI16(SCENE_NS::MeshGeometryArrayPtr<uint16_t> arrays) override
426     {
427         UpdateMeshFromArrays<uint16_t>(arrays, RENDER_NS::IndexType::CORE_INDEX_TYPE_UINT16);
428     }
429 
UpdateMeshFromArraysI32(SCENE_NS::MeshGeometryArrayPtr<uint32_t> arrays)430     void UpdateMeshFromArraysI32(SCENE_NS::MeshGeometryArrayPtr<uint32_t> arrays) override
431     {
432         UpdateMeshFromArrays<uint32_t>(arrays, RENDER_NS::IndexType::CORE_INDEX_TYPE_UINT32);
433     }
434 
AddSubmeshesFromArrayI16(SCENE_NS::MeshGeometryArrayPtr<uint16_t> arrays)435     void AddSubmeshesFromArrayI16(SCENE_NS::MeshGeometryArrayPtr<uint16_t> arrays) override
436     {
437         UpdateMeshFromArrays<uint16_t>(arrays, RENDER_NS::IndexType::CORE_INDEX_TYPE_UINT16, true);
438     }
439 
AddSubmeshesFromArraysI32(SCENE_NS::MeshGeometryArrayPtr<uint32_t> arrays)440     void AddSubmeshesFromArraysI32(SCENE_NS::MeshGeometryArrayPtr<uint32_t> arrays) override
441     {
442         UpdateMeshFromArrays<uint32_t>(arrays, RENDER_NS::IndexType::CORE_INDEX_TYPE_UINT32, true);
443     }
444 
CloneSubmesh(SCENE_NS::ISubMesh::Ptr submesh)445     void CloneSubmesh(SCENE_NS::ISubMesh::Ptr submesh) override
446     {
447         if (auto sh = SceneHolder()) {
448             sh->QueueEngineTask(MakeTask(
449                                     [source = interface_pointer_cast<SCENE_NS::ISubMeshPrivate>(submesh)->GetEntity(),
450                                         index = META_NS::GetValue(submesh->Handle())](auto privateIntf) {
451                                         privateIntf->SceneHolder()->CopySubMesh(
452                                             privateIntf->EcsObject()->GetEntity(), source, index);
453 
454                                         return false;
455                                     },
456                                     GetSelf<INodeEcsInterfacePrivate>()),
457                 false);
458         }
459         // Submesh bridge should automatically reflect the updates
460     }
461 
RemoveSubMesh(size_t index)462     void RemoveSubMesh(size_t index) override
463     {
464         if (submeshHandler_) {
465             submeshHandler_->RemoveSubmesh(index);
466         } else {
467             CORE_LOG_W("%s: mesh is not ready yet, call has no effect", __func__);
468         }
469     }
RemoveAllSubmeshes()470     virtual void RemoveAllSubmeshes() override
471     {
472         if (submeshHandler_) {
473             submeshHandler_->RemoveSubmesh(-1);
474         } else {
475             CORE_LOG_W("%s: mesh is not ready yet, call has no effect", __func__);
476         }
477     }
478 
479 private:
480     SCENE_NS::ISubMeshBridge::Ptr submeshHandler_;
481 };
482 } // namespace
SCENE_BEGIN_NAMESPACE()483 SCENE_BEGIN_NAMESPACE()
484 void RegisterMeshImpl()
485 {
486     META_NS::GetObjectRegistry().RegisterObjectType<MeshImpl>();
487 }
UnregisterMeshImpl()488 void UnregisterMeshImpl()
489 {
490     META_NS::GetObjectRegistry().UnregisterObjectType<MeshImpl>();
491 }
492 
493 SCENE_END_NAMESPACE()
494