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