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 <3d/ecs/components/material_component.h>
17 #include <atomic>
18 #include <meta/api/make_callback.h>
19 #include <meta/interface/detail/array_property.h>
20 #include <meta/api/property/array_element_bind.h>
21 #include <meta/ext/concrete_base_object.h>
22 
23 #include "bind_templates.inl"
24 #include "intf_proxy_object_holder.h"
25 #include "node_impl.h"
26 #include "task_utils.h"
27 
28 using CORE3D_NS::MaterialComponent;
29 META_BEGIN_NAMESPACE()
30 META_TYPE(MaterialComponent::TextureInfo);
31 META_END_NAMESPACE()
32 
33 using SCENE_NS::MakeTask;
34 #define GET_HOLDER(info) interface_pointer_cast<SCENE_NS::IProxyObjectHolder>(info)->Holder()
35 
36 namespace {
37 template<typename... Types>
BindMetaProperty(PropertyHandlerArrayHolder & handler,META_NS::IProperty::Ptr & clone,META_NS::IProperty::Ptr & prop)38 inline void BindMetaProperty(
39     PropertyHandlerArrayHolder& handler, META_NS::IProperty::Ptr& clone, META_NS::IProperty::Ptr& prop)
40 {
41     (BindIfCorrectType<Types>(handler, clone, prop) || ...);
42 }
43 
BindMetaProperty(PropertyHandlerArrayHolder & handler,META_NS::IProperty::Ptr & clone,META_NS::IProperty::Ptr & prop)44 void BindMetaProperty(
45     PropertyHandlerArrayHolder& handler, META_NS::IProperty::Ptr& clone, META_NS::IProperty::Ptr& prop)
46 {
47     BindMetaProperty<BASE_NS::Math::Vec4, BASE_NS::Math::UVec4, BASE_NS::Math::IVec4, BASE_NS::Math::Vec3,
48         BASE_NS::Math::UVec3, BASE_NS::Math::IVec3, BASE_NS::Math::Vec2, BASE_NS::Math::UVec2, BASE_NS::Math::IVec2,
49         float, int32_t, uint32_t, BASE_NS::Math::Mat3X3, BASE_NS::Math::Mat4X4, bool, CORE_NS::Entity,
50         CORE_NS::EntityReference>(handler, clone, prop);
51 }
52 
IsGltfResource(BASE_NS::string_view uri,BASE_NS::string_view resourcePath)53 bool IsGltfResource(BASE_NS::string_view uri, BASE_NS::string_view resourcePath)
54 {
55     // Image uris loaded form a glTF are in a format like this: "file://model.gltf/images/0"
56 
57     // There must be an identifier string at the end of the uri after the '/'.
58     const auto idSeparator = uri.find_last_of('/');
59     if (idSeparator == BASE_NS::string_view::npos) {
60         return false;
61     }
62 
63     // We don't care what is after the separator, but it should be preceded by <resourcePath>" e.g. /images".
64     auto rest = uri.substr(0, idSeparator);
65     if (!rest.ends_with(resourcePath)) {
66         return false;
67     }
68     rest = rest.substr(0, rest.size() - resourcePath.size());
69 
70     // Take the last 5 characters in lower case to check the extension.
71     if (rest.size() < 5) {
72         return false;
73     }
74     const auto extension = BASE_NS::string(rest.substr(rest.size() - 5, 5)).toLower();
75     if (!extension.ends_with(".glb") && !extension.ends_with(".gltf")) {
76         return false;
77     }
78 
79     return true;
80 }
81 
IsGltfImage(BASE_NS::string_view uri)82 bool IsGltfImage(BASE_NS::string_view uri)
83 {
84     return IsGltfResource(uri, "/images");
85 }
86 
IsGltfMaterial(BASE_NS::string_view uri)87 bool IsGltfMaterial(BASE_NS::string_view uri)
88 {
89     return IsGltfResource(uri, "/materials");
90 }
91 
92 class MaterialImpl : public META_NS::ConcreteBaseMetaObjectFwd<MaterialImpl, NodeImpl, SCENE_NS::ClassId::Material,
93                          SCENE_NS::IMaterial, ITextureStorage> {
94     static constexpr BASE_NS::string_view MATERIAL_COMPONENT_NAME = "MaterialComponent";
95 
96     static constexpr BASE_NS::string_view CUSTOM_PREFIX = "MaterialComponent.customProperties.";
97     static constexpr size_t CUSTOM_PREFIX_SIZE = CUSTOM_PREFIX.size();
98     static constexpr BASE_NS::string_view TEXTURE_PREFIX = "MaterialComponent.TextureInfo.";
99     static constexpr size_t TEXTURE_PREFIX_SIZE = TEXTURE_PREFIX.size();
100 
Build(const IMetadata::Ptr & data)101     bool Build(const IMetadata::Ptr& data) override
102     {
103         bool ret = false;
104         if (ret = NodeImpl::Build(data); ret) {
105             // bind everything from material component, otherwise obey the wishes of other components
106             PropertyNameMask()[MATERIAL_COMPONENT_NAME] = {};
107             OnTypeChanged(); // initialize inputs
108         }
109         return ret;
110     }
111 
112     META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IMaterial, uint8_t, Type, 0)
113 
114     META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IMaterial, float, AlphaCutoff, 1.f)
115     META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IMaterial, uint32_t, LightingFlags,
116         CORE3D_NS::MaterialComponent::LightingFlagBits::SHADOW_RECEIVER_BIT |
117             CORE3D_NS::MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT |
118             CORE3D_NS::MaterialComponent::LightingFlagBits::PUNCTUAL_LIGHT_RECEIVER_BIT |
119             CORE3D_NS::MaterialComponent::LightingFlagBits::INDIRECT_LIGHT_RECEIVER_BIT)
120     META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IMaterial, SCENE_NS::IShader::Ptr, MaterialShader, {})
121     META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IMaterial, SCENE_NS::IGraphicsState::Ptr, MaterialShaderState, {})
122     META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IMaterial, SCENE_NS::IShader::Ptr, DepthShader, {})
123     META_IMPLEMENT_INTERFACE_PROPERTY(SCENE_NS::IMaterial, SCENE_NS::IGraphicsState::Ptr, DepthShaderState, {})
124 
125     META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(SCENE_NS::IMaterial, META_NS::IObject::Ptr, CustomProperties, {})
126     META_IMPLEMENT_INTERFACE_ARRAY_PROPERTY(SCENE_NS::IMaterial, SCENE_NS::ITextureInfo::Ptr, Inputs, {})
127     META_IMPLEMENT_INTERFACE_ARRAY_PROPERTY(SCENE_NS::IMaterial, SCENE_NS::ITextureInfo::Ptr, Textures, {})
128 
129 
130     bool updatingInputs_ { false };
131 
Activate()132     void Activate() override
133     {
134         auto sceneHolder = SceneHolder();
135         if (!sceneHolder) {
136             return;
137         }
138 
139         auto task = META_NS::MakeCallback<META_NS::ITaskQueueTask>(
140             [e = ecsObject_->GetEntity(), w = BASE_NS::weak_ptr(sceneHolder)] {
141                 auto sh = w.lock();
142                 if (sh) {
143                     sh->SetEntityActive(e, true);
144                 }
145                 return false;
146             });
147         sceneHolder->QueueEngineTask(task, false);
148     }
149 
Deactivate()150     void Deactivate() override
151     {
152         auto sceneHolder = SceneHolder();
153         if (!sceneHolder) {
154             return;
155         }
156 
157         auto task = META_NS::MakeCallback<META_NS::ITaskQueueTask>(
158             [e = ecsObject_->GetEntity(), w = BASE_NS::weak_ptr(sceneHolder)] {
159                 auto sh = w.lock();
160                 if (sh) {
161                     sh->SetEntityActive(e, false);
162                 }
163                 return false;
164             });
165         sceneHolder->QueueEngineTask(task, false);
166     }
167 
SetPath(const BASE_NS::string & path,const BASE_NS::string & name,CORE_NS::Entity entity)168     void SetPath(const BASE_NS::string& path, const BASE_NS::string& name, CORE_NS::Entity entity) override
169     {
170         META_ACCESS_PROPERTY(Path)->SetValue(path);
171         META_ACCESS_PROPERTY(Name)->SetValue(name);
172 
173         if (auto iscene = GetScene()) {
174             iscene->UpdateCachedReference(GetSelf<SCENE_NS::INode>());
175         }
176         if (auto scene = EcsScene()) {
177             SetStatus(SCENE_NS::INode::NODE_STATUS_CONNECTING);
178 
179             initializeTaskToken_ = scene->AddEngineTask(MakeTask([me = BASE_NS::weak_ptr(GetSelf()),
180                                                                      materialName = name,
181                                                                      fullpath = path + name, entity]() {
182                 if (auto self = static_pointer_cast<NodeImpl>(me.lock())) {
183                     if (auto sceneHolder = self->SceneHolder()) {
184                         CORE_NS::Entity materialEntity = entity;
185 
186                         if (CORE_NS::EntityUtil::IsValid(materialEntity)) {
187                             SCENE_PLUGIN_VERBOSE_LOG("binding material: %s", materialName.c_str());
188                             if (auto proxyIf = interface_pointer_cast<SCENE_NS::IEcsProxyObject>(self->EcsObject())) {
189                                 proxyIf->SetCommonListener(sceneHolder->GetCommonEcsListener());
190                             }
191                             self->EcsObject()->DefineTargetProperties(self->PropertyNameMask());
192                             self->EcsObject()->SetEntity(sceneHolder->GetEcs(), materialEntity);
193                             sceneHolder->QueueApplicationTask(
194                                 MakeTask(
195                                     [fullpath](auto selfObject) {
196                                         if (auto self = static_pointer_cast<NodeImpl>(selfObject)) {
197                                             self->CompleteInitialization(fullpath);
198                                             self->SetStatus(SCENE_NS::INode::NODE_STATUS_CONNECTED);
199                                             if (auto node = interface_pointer_cast<SCENE_NS::INode>(selfObject)) {
200                                                 META_NS::Invoke<META_NS::IOnChanged>(node->OnLoaded());
201                                             }
202                                         }
203                                         return false;
204                                     },
205                                     me),
206                                 false);
207                         } else {
208                             CORE_LOG_W("Could not find '%s' material", fullpath.c_str());
209                             sceneHolder->QueueApplicationTask(
210                                 MakeTask(
211                                     [](auto selfObject) {
212                                         if (auto self = static_pointer_cast<NodeImpl>(selfObject)) {
213                                             self->SetStatus(SCENE_NS::INode::NODE_STATUS_DISCONNECTED);
214                                         }
215                                         return false;
216                                     },
217                                     me),
218                                 false);
219                         }
220                     }
221                 }
222                 return false;
223             }),
224                 false);
225         }
226     }
227 
228 public: // ISerialization
ApplyTextureInfoImage(size_t arrayIndex,SCENE_NS::ITextureInfo & info)229     void ApplyTextureInfoImage(size_t arrayIndex, SCENE_NS::ITextureInfo& info)
230     {
231         if (auto sceneHolder = SceneHolder()) {
232             bool shouldReset { true };
233             auto value = info.Image()->GetValue();
234             if (auto uriBitmap = interface_cast<SCENE_NS::IBitmap>(value)) {
235                 auto uri = META_NS::GetValue(uriBitmap->Uri());
236                 if (!uri.empty()) {
237                     shouldReset = false;
238 
239                     if (IsGltfImage(uri)) {
240                         return;
241                     }
242 
243                     sceneHolder->QueueEngineTask(
244                         MakeTask(
245                             [uri, entityId = EcsObject()->GetEntity().id, arrayIndex](auto sh) {
246                                 CORE_NS::Entity target { entityId };
247                                 auto image = sh->LoadImage(uri);
248                                 sh->SetTexture(arrayIndex, target, image);
249 
250                                 return false;
251                             },
252                             sceneHolder),
253                         false);
254                 }
255             }
256             if (shouldReset) {
257                 sceneHolder->QueueEngineTask(MakeTask(
258                                                  [entity = EcsObject()->GetEntity(), arrayIndex](auto sh) {
259                                                      sh->SetTexture(arrayIndex, entity, {});
260                                                      return false;
261                                                  },
262                                                  sceneHolder),
263                     false);
264             }
265         }
266     }
267 
SubscribeToTextureInfo(size_t arrayIndex,SCENE_NS::ITextureInfo::Ptr info)268     void SubscribeToTextureInfo(size_t arrayIndex, SCENE_NS::ITextureInfo::Ptr info)
269     {
270         info->SetTextureSlotIndex(arrayIndex);
271 
272         // EcsObject would provide use entity ref while we would like to use the enums to define which
273         // sampler to use Perhaps we create new factory method for scene-interface and then apply the direct
274         // biding from now on
275         GET_HOLDER(info)
276             .NewHandler(info->Sampler(), nullptr)
277             .Subscribe(info->Sampler(), [this, prop = info->Sampler(), arrayIndex]() {
278                 if (auto sceneHolder = SceneHolder()) {
279                     sceneHolder->QueueEngineTask(
280                         MakeTask(
281                             [value = prop->GetValue(), entityId = EcsObject()->GetEntity().id, arrayIndex](auto sh) {
282                                 CORE_NS::Entity target { entityId };
283                                 sh->SetSampler(
284                                     arrayIndex, target, static_cast<SCENE_NS::ITextureInfo::SamplerId>(value));
285 
286                                 return false;
287                             },
288                             sceneHolder),
289                         false);
290                 }
291             });
292 
293         GET_HOLDER(info)
294             .NewHandler(info->Image(), nullptr)
295             .Subscribe(info->Image(),
296                 [this, textureInfo = info.get(), arrayIndex]() { ApplyTextureInfoImage(arrayIndex, *textureInfo); });
297     }
298     /*
299     bool Export(
300         META_NS::Serialization::IExportContext& context, META_NS::Serialization::ClassPrimitive& value) const override
301     {
302         SCENE_PLUGIN_VERBOSE_LOG("MaterialImpl::%s %s", __func__, Name()->Get().c_str());
303 
304         // This is not a shared material and therefore we will export it here.
305         return Fwd::Export(context, value);
306     }
307 
308     bool Import(
309         META_NS::Serialization::IImportContext& context, const META_NS::Serialization::ClassPrimitive& value) override
310     {
311         META_ACCESS_PROPERTY(Inputs)->Reset();
312         META_ACCESS_PROPERTY(CustomProperties)->Reset();
313 
314         if (!Fwd::Import(context, value)) {
315             return false;
316         }
317 
318         if (Inputs()) {
319             // Subscribe all texture infos.
320             for (auto i = 0; i < Inputs()->Size(); ++i) {
321                 auto info = Inputs()->Get(i);
322 
323                 auto textureSlotIndex = info->GetTextureSlotIndex();
324                 if (textureSlotIndex == ~0u) {
325                     textureSlotIndex = i;
326                 }
327 
328                 info->SetTextureSlotIndex(textureSlotIndex);
329             }
330         }
331 
332         return true;
333     }
334     */
GetTextureInfoByIndex(uint32_t index,BASE_NS::vector<SCENE_NS::ITextureInfo::Ptr> & textures)335     SCENE_NS::ITextureInfo::Ptr GetTextureInfoByIndex(
336         uint32_t index, BASE_NS::vector<SCENE_NS::ITextureInfo::Ptr>& textures)
337     {
338         // First, look from current inputs.
339         auto existingInfo = GetTextureInfo(index);
340         if (existingInfo) {
341             // Found existing info, we absolutely want to re-use it.
342             // This is to keep e.g. property animations working (they bind to this exact object).
343             auto it = std::find(textures.begin(), textures.end(), existingInfo);
344             if (it == textures.end()) {
345                 // Add this info to textures list.
346                 textures.push_back(existingInfo);
347             }
348             return existingInfo;
349         }
350 
351         for (auto& info : textures) {
352             if (info->GetTextureSlotIndex() == index) {
353                 return info;
354             }
355         }
356 
357         return {};
358     }
359 
BindTextureSlot(const BASE_NS::string_view & propName,META_NS::IMetadata::Ptr & meta,BASE_NS::vector<SCENE_NS::ITextureInfo::Ptr> & textures)360     SCENE_NS::ITextureInfo::Ptr BindTextureSlot(const BASE_NS::string_view& propName, META_NS::IMetadata::Ptr& meta,
361         BASE_NS::vector<SCENE_NS::ITextureInfo::Ptr>& textures)
362     {
363         SCENE_NS::ITextureInfo::Ptr result;
364 
365         // fork out the identifier
366         size_t ix = TEXTURE_PREFIX_SIZE;
367         ix = propName.find('.', ix + 1);
368         size_t textureSlotId = 0;
369 
370         if (ix == BASE_NS::string_view::npos) {
371             return result;
372         } else {
373             // how cool is this
374             for (int ii = TEXTURE_PREFIX_SIZE; ii < ix; ii++) {
375                 textureSlotId += (propName[ii] - '0') * pow(10, (ix - ii - 1)); // 10 exponent
376             }
377         }
378 
379         auto textureSlotIndex = textureSlotId - 1;
380 
381         bool applyPropertyToEcs = false;
382         if (textureSlotId) {
383             auto info = GetTextureInfoByIndex(textureSlotIndex, textures);
384             if (!info) {
385                 info = GetObjectRegistry().Create<SCENE_NS::ITextureInfo>(SCENE_NS::ClassId::TextureInfo);
386                 if (!info) {
387                     return {};
388                 }
389 
390                 info->SetTextureSlotIndex(textureSlotIndex);
391                 textures.push_back(info);
392             }
393 
394             GET_HOLDER(info).SetSceneHolder(SceneHolder());
395 
396             {
397                 // Update name.
398                 BASE_NS::string textureSlotName(propName.substr(ix + 1));
399                 size_t dotIndex = textureSlotName.find('.');
400                 if (dotIndex != BASE_NS::string_view::npos && dotIndex != 0) {
401                     textureSlotName = textureSlotName.substr(0, dotIndex);
402                 }
403                 if (GetValue(info->Name()) != textureSlotName) {
404                     SetValue(info->Name(), textureSlotName);
405                 }
406             }
407 
408             // progress one more dot
409             ix = propName.find('.', ix + 1);
410 
411             BASE_NS::string propertyName(propName.substr(ix));
412             if (propertyName == ".factor") {
413                 BindChanges<BASE_NS::Math::Vec4>(GET_HOLDER(info), info->Factor(), meta, propName);
414             } else if (propertyName == ".transform.translation") {
415                 BindChanges<BASE_NS::Math::Vec2>(GET_HOLDER(info), info->Translation(), meta, propName);
416             } else if (propertyName == ".transform.rotation") {
417                 BindChanges<float>(GET_HOLDER(info), info->Rotation(), meta, propName);
418             } else if (propertyName == ".transform.scale") {
419                 BindChanges<BASE_NS::Math::Vec2>(GET_HOLDER(info), info->Scale(), meta, propName);
420             } else if (propertyName == ".image") {
421                 auto prop = meta->GetPropertyByName(propName);
422 
423                 // Here we will initialize the image property.
424                 if (info->Image()->IsValueSet()) {
425                     // If the property value is set by the user, we will propagate it to ecs.
426                     ApplyTextureInfoImage(textureSlotIndex, *info);
427                 } else {
428                     // If the property value is not set, we will fetch it from ecs.
429                     CheckImageHandle(textureSlotIndex, BASE_NS::weak_ptr<SCENE_NS::ITextureInfo>(info));
430                 }
431 
432                 prop->OnChanged()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>(
433                     [this, textureSlotIndex, w_info = Base::weak_ptr(info)]() {
434                         CheckImageHandle(textureSlotIndex, w_info);
435                     }));
436                 GET_HOLDER(info).MarkRelated(info->Image(), prop);
437 
438                 SubscribeToTextureInfo(textureSlotIndex, info);
439             } else if (propertyName == ".sampler") {
440                 GET_HOLDER(info).MarkRelated(info->Sampler(), meta->GetPropertyByName(propName));
441             }
442 
443             result = info;
444         }
445 
446         return result;
447     }
448 
CheckImageHandle(size_t arrayIx,BASE_NS::weak_ptr<SCENE_NS::ITextureInfo> w_info)449     void CheckImageHandle(size_t arrayIx, BASE_NS::weak_ptr<SCENE_NS::ITextureInfo> w_info)
450     {
451         // Queue engine task
452         if (auto sceneHolder = SceneHolder()) {
453             sceneHolder->QueueEngineTask(
454                 MakeTask(
455                     [entity = EcsObject()->GetEntity(), arrayIx, w_info](SceneHolder::Ptr sh) {
456                         // resolve entity
457                         CORE_NS::Entity image;
458                         BASE_NS::string name;
459                         RENDER_NS::GpuImageDesc imageDesc;
460                         RENDER_NS::RenderHandleReference handle;
461                         if (sh->GetImageEntity(entity, arrayIx, image)) {
462                             // get entity by uri
463                             sh->GetEntityUri(image, name);
464                             sh->GetImageHandle(image, handle, imageDesc);
465                         }
466                         SCENE_PLUGIN_VERBOSE_LOG("%s: resolved name: %s", __func__, name.c_str());
467                         // if we get something valid out of those
468                         // let the toolkit side check if we have a bitmap and its uri matches
469                         if (!name.empty()) {
470                             sh->QueueEngineTask(
471                                 MakeTask(
472                                     [name](auto info, RENDER_NS::RenderHandleReference handle, auto size) {
473                                         if (auto bitmap = META_NS::GetValue(info->Image())) {
474                                             if (auto uriBitmap = interface_cast<SCENE_NS::IBitmap>(bitmap)) {
475                                                 if (auto uri = META_NS::GetValue(uriBitmap->Uri());
476                                                     !uri.empty() && (uri == name)) {
477                                                     return false;
478                                                 }
479                                             }
480                                         }
481                                         auto uriBitmap = META_NS::GetObjectRegistry().Create<SCENE_NS::IBitmap>(
482                                             SCENE_NS::ClassId::Bitmap);
483                                         META_NS::SetValue(uriBitmap->Uri(), name);
484                                         // TODO: should parse the renderhandle from the entity and set it to the bitmap
485                                         // also. as we "create" it from the ecs, so the image SHOULD already be loaded.
486                                         uriBitmap->SetRenderHandle(handle, size);
487                                         info->Image()->SetDefaultValue(uriBitmap, true);
488                                         return false;
489                                     },
490                                     w_info, handle, BASE_NS::Math::UVec2(imageDesc.width, imageDesc.height)),
491                                 false);
492                         }
493                         return false;
494                     },
495                     sceneHolder),
496                 false);
497         }
498     }
499 
GetTextureInfo(size_t ix) const500     SCENE_NS::ITextureInfo::Ptr GetTextureInfo(size_t ix) const override
501     {
502         if (Inputs()) {
503             for (auto& info : Inputs()->GetValue()) {
504                 if (info->GetTextureSlotIndex() == ix) {
505                     return info;
506                 }
507             }
508         }
509 
510         return SCENE_NS::ITextureInfo::Ptr {};
511     }
512 
UpdateInputProperties()513     void UpdateInputProperties()
514     {
515         auto type = META_NS::GetValue(META_ACCESS_PROPERTY(Type));
516         bool includeDefaultMappings =
517             (type == SCENE_NS::IMaterial::METALLIC_ROUGHNESS || type == SCENE_NS::IMaterial::SPECULAR_GLOSSINESS ||
518                 type == SCENE_NS::IMaterial::UNLIT || type == SCENE_NS::IMaterial::UNLIT_SHADOW_ALPHA);
519 
520         // Base Color
521         if (auto meta =
522                 interface_pointer_cast<META_NS::IMetadata>(GetTextureInfo(CORE3D_NS::MaterialComponent::BASE_COLOR))) {
523             if (auto color = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_COLOR)) {
524                 if (!includeDefaultMappings) {
525                     meta->RemoveProperty(color);
526                 }
527             } else {
528                 auto c = META_NS::ConstructProperty<SCENE_NS::Color>(SCENE_NS::IMaterial::MAPPED_INPUTS_COLOR);
529                 meta->AddProperty(c);
530             }
531         }
532 
533         // Normal
534         if (auto meta =
535                 interface_pointer_cast<META_NS::IMetadata>(GetTextureInfo(CORE3D_NS::MaterialComponent::NORMAL))) {
536             if (auto scale = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_NORMAL_SCALE)) {
537                 if (!includeDefaultMappings) {
538                     meta->RemoveProperty(scale);
539                 }
540             } else {
541                 auto s = META_NS::ConstructProperty<float>(SCENE_NS::IMaterial::MAPPED_INPUTS_NORMAL_SCALE);
542                 meta->AddProperty(s);
543             }
544         }
545 
546         //  Material
547         if (auto meta =
548                 interface_pointer_cast<META_NS::IMetadata>(GetTextureInfo(CORE3D_NS::MaterialComponent::MATERIAL))) {
549             // SCENE_NS::IMaterial::METALLIC_ROUGHNESS
550             if (auto roughness = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_ROUGHNESS)) {
551                 if (type != SCENE_NS::IMaterial::METALLIC_ROUGHNESS) {
552                     meta->RemoveProperty(roughness);
553                 }
554             } else if (type == SCENE_NS::IMaterial::METALLIC_ROUGHNESS) {
555                 auto roughness = META_NS::ConstructProperty<float>(SCENE_NS::IMaterial::MAPPED_INPUTS_ROUGHNESS);
556                 meta->AddProperty(roughness);
557             }
558             if (auto metallic = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_METALLIC)) {
559                 if (type != SCENE_NS::IMaterial::METALLIC_ROUGHNESS) {
560                     meta->RemoveProperty(metallic);
561                 }
562             } else if (type == SCENE_NS::IMaterial::METALLIC_ROUGHNESS) {
563                 auto metallic = META_NS::ConstructProperty<float>(SCENE_NS::IMaterial::MAPPED_INPUTS_METALLIC);
564                 meta->AddProperty(metallic);
565             }
566             if (auto reflectance = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_REFLECTANCE)) {
567                 if (type != SCENE_NS::IMaterial::METALLIC_ROUGHNESS) {
568                     meta->RemoveProperty(reflectance);
569                 }
570             } else if (type == SCENE_NS::IMaterial::METALLIC_ROUGHNESS) {
571                 auto reflectance = META_NS::ConstructProperty<float>(SCENE_NS::IMaterial::MAPPED_INPUTS_REFLECTANCE);
572                 meta->AddProperty(reflectance);
573             }
574             // SCENE_NS::IMaterial::SPECULAR_GLOSSINESS
575             if (auto colorRGB = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_COLOR)) {
576                 if (type != SCENE_NS::IMaterial::SPECULAR_GLOSSINESS) {
577                     meta->RemoveProperty(colorRGB);
578                 }
579             } else if (type == SCENE_NS::IMaterial::SPECULAR_GLOSSINESS) {
580                 auto colorRGB = META_NS::ConstructProperty<SCENE_NS::Color>(SCENE_NS::IMaterial::MAPPED_INPUTS_COLOR);
581                 meta->AddProperty(colorRGB);
582             }
583             if (auto glossiness = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_GLOSSINESS)) {
584                 if (type != SCENE_NS::IMaterial::SPECULAR_GLOSSINESS) {
585                     meta->RemoveProperty(glossiness);
586                 }
587             } else if (type == SCENE_NS::IMaterial::SPECULAR_GLOSSINESS) {
588                 auto glossiness = META_NS::ConstructProperty<float>(SCENE_NS::IMaterial::MAPPED_INPUTS_GLOSSINESS);
589                 meta->AddProperty(glossiness);
590             }
591         }
592 
593         // Emissive
594         if (auto meta =
595                 interface_pointer_cast<META_NS::IMetadata>(GetTextureInfo(CORE3D_NS::MaterialComponent::EMISSIVE))) {
596             if (auto color = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_COLOR)) {
597                 if (!includeDefaultMappings) {
598                     meta->RemoveProperty(color);
599                 }
600             } else {
601                 auto c = META_NS::ConstructProperty<SCENE_NS::Color>(SCENE_NS::IMaterial::MAPPED_INPUTS_COLOR);
602                 meta->AddProperty(c);
603             }
604             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_INTENSITY)) {
605                 if (!includeDefaultMappings) {
606                     meta->RemoveProperty(prop);
607                 }
608             } else {
609                 auto p = META_NS::ConstructProperty<float>(SCENE_NS::IMaterial::MAPPED_INPUTS_INTENSITY);
610                 meta->AddProperty(p);
611             }
612         }
613 
614         // Ambient Occlusion
615         if (auto meta = interface_pointer_cast<META_NS::IMetadata>(GetTextureInfo(CORE3D_NS::MaterialComponent::AO))) {
616             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_STRENGTH)) {
617                 if (!includeDefaultMappings) {
618                     meta->RemoveProperty(prop);
619                 }
620             } else {
621                 auto p = META_NS::ConstructProperty<float>(SCENE_NS::IMaterial::MAPPED_INPUTS_STRENGTH);
622                 meta->AddProperty(p);
623             }
624         }
625 
626         // Clear Coat
627         if (auto meta =
628                 interface_pointer_cast<META_NS::IMetadata>(GetTextureInfo(CORE3D_NS::MaterialComponent::CLEARCOAT))) {
629             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_INTENSITY)) {
630                 if (!includeDefaultMappings) {
631                     meta->RemoveProperty(prop);
632                 }
633             } else {
634                 auto p = META_NS::ConstructProperty<float>(SCENE_NS::IMaterial::MAPPED_INPUTS_INTENSITY);
635                 meta->AddProperty(p);
636             }
637         }
638 
639         // Clear Coat Roughness
640         if (auto meta = interface_pointer_cast<META_NS::IMetadata>(
641                 GetTextureInfo(CORE3D_NS::MaterialComponent::CLEARCOAT_ROUGHNESS))) {
642             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_ROUGHNESS)) {
643                 if (!includeDefaultMappings) {
644                     meta->RemoveProperty(prop);
645                 }
646             } else {
647                 auto p = META_NS::ConstructProperty<float>(SCENE_NS::IMaterial::MAPPED_INPUTS_ROUGHNESS);
648                 meta->AddProperty(p);
649             }
650         }
651 
652         // Clear Coat Normal
653         if (auto meta = interface_pointer_cast<META_NS::IMetadata>(
654                 GetTextureInfo(CORE3D_NS::MaterialComponent::CLEARCOAT_NORMAL))) {
655             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_NORMAL_SCALE)) {
656                 if (!includeDefaultMappings) {
657                     meta->RemoveProperty(prop);
658                 }
659             } else {
660                 auto p = META_NS::ConstructProperty<float>(SCENE_NS::IMaterial::MAPPED_INPUTS_NORMAL_SCALE);
661                 meta->AddProperty(p);
662             }
663         }
664 
665         // Sheen
666         if (auto meta =
667                 interface_pointer_cast<META_NS::IMetadata>(GetTextureInfo(CORE3D_NS::MaterialComponent::SHEEN))) {
668             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_COLOR)) {
669                 if (!includeDefaultMappings) {
670                     meta->RemoveProperty(prop);
671                 }
672             } else {
673                 auto p = META_NS::ConstructProperty<SCENE_NS::Color>(SCENE_NS::IMaterial::MAPPED_INPUTS_COLOR);
674                 meta->AddProperty(p);
675             }
676             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_ROUGHNESS)) {
677                 if (!includeDefaultMappings) {
678                     meta->RemoveProperty(prop);
679                 }
680             } else {
681                 auto p = META_NS::ConstructProperty<float>(SCENE_NS::IMaterial::MAPPED_INPUTS_ROUGHNESS);
682                 meta->AddProperty(p);
683             }
684         }
685 
686         // TRANSMISSION
687         if (auto meta = interface_pointer_cast<META_NS::IMetadata>(
688                 GetTextureInfo(CORE3D_NS::MaterialComponent::TRANSMISSION))) {
689             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_TRANSMISSION)) {
690                 if (!includeDefaultMappings) {
691                     meta->RemoveProperty(prop);
692                 }
693             } else {
694                 auto p = META_NS::ConstructProperty<float>(SCENE_NS::IMaterial::MAPPED_INPUTS_TRANSMISSION);
695                 meta->AddProperty(p);
696             }
697         }
698 
699         // Specular
700         if (auto meta =
701                 interface_pointer_cast<META_NS::IMetadata>(GetTextureInfo(CORE3D_NS::MaterialComponent::SPECULAR))) {
702             if (auto color = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_COLOR)) {
703                 if (!includeDefaultMappings) {
704                     meta->RemoveProperty(color);
705                 }
706             } else {
707                 auto c = META_NS::ConstructProperty<SCENE_NS::Color>(SCENE_NS::IMaterial::MAPPED_INPUTS_COLOR);
708                 meta->AddProperty(c);
709             }
710             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_STRENGTH)) {
711                 if (!includeDefaultMappings) {
712                     meta->RemoveProperty(prop);
713                 }
714             } else {
715                 auto p = META_NS::ConstructProperty<float>(SCENE_NS::IMaterial::MAPPED_INPUTS_STRENGTH);
716                 meta->AddProperty(p);
717             }
718         }
719     }
720 
BindInputProperties()721     void BindInputProperties()
722     {
723         auto type = META_NS::GetValue(META_ACCESS_PROPERTY(Type));
724 
725         // Base Color
726         if (auto meta =
727                 interface_pointer_cast<META_NS::IMetadata>(GetTextureInfo(CORE3D_NS::MaterialComponent::BASE_COLOR))) {
728             if (auto color = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_COLOR)) {
729                 ConvertBindChanges<SCENE_NS::Color, BASE_NS::Math::Vec4>(GET_HOLDER(meta),
730                     META_NS::Property<SCENE_NS::Color>(color), meta, SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR);
731             }
732         }
733 
734         // Normal
735         if (auto meta =
736                 interface_pointer_cast<META_NS::IMetadata>(GetTextureInfo(CORE3D_NS::MaterialComponent::NORMAL))) {
737             if (auto scale = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_NORMAL_SCALE)) {
738                 BindChanges<BASE_NS::Math::Vec4, float>(GET_HOLDER(meta), META_NS::Property<float>(scale), meta,
739                     SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR, 0);
740             }
741         }
742 
743         //  Material
744         if (auto meta =
745                 interface_pointer_cast<META_NS::IMetadata>(GetTextureInfo(CORE3D_NS::MaterialComponent::MATERIAL))) {
746             // SCENE_NS::IMaterial::METALLIC_ROUGHNESS
747             if (type == SCENE_NS::IMaterial::METALLIC_ROUGHNESS) {
748                 if (auto roughness = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_ROUGHNESS)) {
749                     BindChanges<BASE_NS::Math::Vec4, float>(GET_HOLDER(meta), META_NS::Property<float>(roughness),
750                         meta, SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR, 1);
751                 }
752                 if (auto metallic = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_METALLIC)) {
753                     BindChanges<BASE_NS::Math::Vec4, float>(GET_HOLDER(meta), META_NS::Property<float>(metallic),
754                         meta, SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR, 2); // 2 factor
755                 }
756                 if (auto reflectance = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_REFLECTANCE)) {
757                     BindChanges<BASE_NS::Math::Vec4, float>(GET_HOLDER(meta), META_NS::Property<float>(reflectance),
758                         meta, SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR, 3); // 3 factor
759                 }
760             }
761 
762             // SCENE_NS::IMaterial::SPECULAR_GLOSSINESS
763             if (type == SCENE_NS::IMaterial::SPECULAR_GLOSSINESS) {
764                 if (auto colorRGB = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_COLOR)) {
765                     BASE_NS::vector<size_t> slots = { 0, 0, 1, 1, 2, 2 };
766                     BindSlottedChanges<BASE_NS::Math::Vec4, SCENE_NS::Color>(GET_HOLDER(meta),
767                         META_NS::Property<SCENE_NS::Color>(colorRGB), meta, SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR,
768                         slots);
769                 }
770                 if (auto glossiness = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_GLOSSINESS)) {
771                     BindChanges<BASE_NS::Math::Vec4, float>(GET_HOLDER(meta), META_NS::Property<float>(glossiness),
772                         meta, SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR, 3);
773                 }
774             }
775         }
776 
777         // Emissive
778         if (auto meta =
779                 interface_pointer_cast<META_NS::IMetadata>(GetTextureInfo(CORE3D_NS::MaterialComponent::EMISSIVE))) {
780             if (auto color = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_COLOR)) {
781                 BASE_NS::vector<size_t> slots = { 0, 0, 1, 1, 2, 2 };
782                 BindSlottedChanges<BASE_NS::Math::Vec4, SCENE_NS::Color>(GET_HOLDER(meta),
783                     META_NS::Property<SCENE_NS::Color>(color), meta, SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR,
784                     slots);
785             }
786             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_INTENSITY)) {
787                 BindChanges<BASE_NS::Math::Vec4, float>(GET_HOLDER(meta), META_NS::Property<float>(prop), meta,
788                     SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR, 3);
789             }
790         }
791 
792         // Ambient Occlusion
793         if (auto meta = interface_pointer_cast<META_NS::IMetadata>(GetTextureInfo(CORE3D_NS::MaterialComponent::AO))) {
794             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_STRENGTH)) {
795                 BindChanges<BASE_NS::Math::Vec4, float>(GET_HOLDER(meta), META_NS::Property<float>(prop), meta,
796                     SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR, 0);
797             }
798         }
799 
800         // Clear Coat
801         if (auto meta =
802                 interface_pointer_cast<META_NS::IMetadata>(GetTextureInfo(CORE3D_NS::MaterialComponent::CLEARCOAT))) {
803             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_INTENSITY)) {
804                 BindChanges<BASE_NS::Math::Vec4, float>(GET_HOLDER(meta), META_NS::Property<float>(prop), meta,
805                     SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR, 0);
806             }
807         }
808 
809         // Clear Coat Roughness
810         if (auto meta = interface_pointer_cast<META_NS::IMetadata>(
811                 GetTextureInfo(CORE3D_NS::MaterialComponent::CLEARCOAT_ROUGHNESS))) {
812             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_ROUGHNESS)) {
813                 BindChanges<BASE_NS::Math::Vec4, float>(GET_HOLDER(meta), META_NS::Property<float>(prop), meta,
814                     SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR, 1);
815             }
816         }
817 
818         // Clear Coat Normal
819         if (auto meta = interface_pointer_cast<META_NS::IMetadata>(
820                 GetTextureInfo(CORE3D_NS::MaterialComponent::CLEARCOAT_NORMAL))) {
821             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_NORMAL_SCALE)) {
822                 BindChanges<BASE_NS::Math::Vec4, float>(GET_HOLDER(meta), META_NS::Property<float>(prop), meta,
823                     SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR, 0);
824             }
825         }
826 
827         // Sheen
828         if (auto meta =
829                 interface_pointer_cast<META_NS::IMetadata>(GetTextureInfo(CORE3D_NS::MaterialComponent::SHEEN))) {
830             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_COLOR)) {
831                 BASE_NS::vector<size_t> slots = { 0, 0, 1, 1, 2, 2 };
832                 BindSlottedChanges<BASE_NS::Math::Vec4, SCENE_NS::Color>(GET_HOLDER(meta),
833                     META_NS::Property<SCENE_NS::Color>(prop), meta, SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR, slots);
834             }
835             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_ROUGHNESS)) {
836                 BindChanges<BASE_NS::Math::Vec4, float>(GET_HOLDER(meta), META_NS::Property<float>(prop), meta,
837                     SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR, 3);
838             }
839         }
840 
841         // TRANSMISSION
842         if (auto meta = interface_pointer_cast<META_NS::IMetadata>(
843                 GetTextureInfo(CORE3D_NS::MaterialComponent::TRANSMISSION))) {
844             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_TRANSMISSION)) {
845                 BindChanges<BASE_NS::Math::Vec4, float>(GET_HOLDER(meta), META_NS::Property<float>(prop), meta,
846                     SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR, 0);
847             }
848         }
849 
850         // Specular
851         if (auto meta =
852                 interface_pointer_cast<META_NS::IMetadata>(GetTextureInfo(CORE3D_NS::MaterialComponent::SPECULAR))) {
853             if (auto color = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_COLOR)) {
854                 BASE_NS::vector<size_t> slots = { 0, 0, 1, 1, 2, 2 };
855                 BindSlottedChanges<BASE_NS::Math::Vec4, SCENE_NS::Color>(GET_HOLDER(meta),
856                     META_NS::Property<SCENE_NS::Color>(color), meta, SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR,
857                     slots);
858             }
859             if (auto prop = meta->GetPropertyByName(SCENE_NS::IMaterial::MAPPED_INPUTS_STRENGTH)) {
860                 BindChanges<BASE_NS::Math::Vec4, float>(GET_HOLDER(meta), META_NS::Property<float>(prop), meta,
861                     SCENE_NS::IMaterial::MAPPED_INPUTS_FACTOR, 3); // 3 factor
862             }
863         }
864     }
865 
OnTypeChanged()866     void OnTypeChanged()
867     {
868         UpdateInputProperties();
869         BindInputProperties();
870     }
871 
872     // We intend to define the object only on demand
873     // If we have a previous object, we could try to preserve it
UpdateCustomProperties()874     void UpdateCustomProperties()
875     {
876         // Store and remove old custom properties.
877         BASE_NS::vector<META_NS::IProperty::Ptr> oldCustomProperties;
878 
879         auto properties = interface_pointer_cast<META_NS::IMetadata>(META_ACCESS_PROPERTY(CustomProperties)->GetValue());
880         if (properties) {
881             oldCustomProperties = properties->GetAllProperties();
882             properties->GetPropertyContainer()->RemoveAll();
883         }
884 
885         // Find new custom properties.
886         BASE_NS::vector<META_NS::IProperty::Ptr> newCustomProperties;
887 
888         auto meta = interface_pointer_cast<META_NS::IMetadata>(ecsObject_);
889         if (meta) {
890             // Collect all custom properties from ECS object.
891             auto metaProps = meta->GetAllProperties();
892             for (auto&& prop : metaProps) {
893                 if (prop->GetName().compare(0, CUSTOM_PREFIX_SIZE, CUSTOM_PREFIX) == 0) {
894                     newCustomProperties.push_back(prop);
895                 }
896             }
897         }
898 
899         // If there are no custom properties, then reset.
900         if (newCustomProperties.empty()) {
901             META_ACCESS_PROPERTY(CustomProperties)->Reset();
902             return;
903         }
904 
905         // Otherwise ensure that we have container for the custom properties.
906         if (!properties) {
907             properties = GetObjectRegistry().Create<META_NS::IMetadata>(SCENE_NS::ClassId::CustomPropertiesHolder);
908             META_ACCESS_PROPERTY(CustomProperties)->SetValue(interface_pointer_cast<META_NS::IObject>(properties));
909         }
910 
911         GET_HOLDER(properties).SetSceneHolder(sceneHolder_.lock());
912 
913         // Then create and bind all the custom properties.
914         for (auto& prop : newCustomProperties) {
915             // This is the new custom property.
916             META_NS::IProperty::Ptr property;
917 
918             BASE_NS::string propertyName(prop->GetName().substr(CUSTOM_PREFIX_SIZE));
919 
920             // Try to re-use the old custom property if we have one, it may have a meaningful value set.
921             for (auto& existing : oldCustomProperties) {
922                 if (existing->GetName() == propertyName && existing->GetTypeId() == prop->GetTypeId()) {
923                     property = existing;
924                     break;
925                 }
926             }
927 
928             if (!property) {
929                 // Clone property.
930                 property = META_NS::DuplicatePropertyType(META_NS::GetObjectRegistry(), prop, propertyName);
931             }
932 
933             // Add it to target.
934             properties->AddProperty(property);
935 
936             // Bind it to ecs property.
937             BindMetaProperty(GET_HOLDER(properties), property, prop);
938         }
939     }
940 
UpdateInputs(bool forceRecursive=false,bool forceFullReset=true)941     void UpdateInputs(bool forceRecursive = false, bool forceFullReset = true)
942     {
943         if (updatingInputs_ && !forceRecursive) {
944             return;
945         }
946 
947         if (forceFullReset) {
948             updatingInputs_ = true;
949 
950             propHandler_.Reset();
951             META_ACCESS_PROPERTY(Inputs)->Reset();
952             META_ACCESS_PROPERTY(CustomProperties)->Reset();
953 
954             auto entity = EcsObject()->GetEntity();
955             auto ecs = EcsObject()->GetEcs();
956 
957             // Reset the object
958             EcsObject()->BindObject(nullptr, entity);
959 
960             auto scene = EcsScene();
961             auto ecsObject = EcsObject();
962 
963             Initialize(
964                 scene, ecsObject, {}, META_NS::GetValue(Path()), META_NS::GetValue(Name()), sceneHolder_, entity);
965 
966             updatingInputs_ = false;
967         } else {
968             // This will potentially go wrong if the proxy properties contain set values
969             // The values previously set will replace the values from engine
970             if (UpdateAllInputProperties()) {
971                 updatingInputs_ = true;
972                 META_ACCESS_PROPERTY(Inputs)->Reset();
973                 META_ACCESS_PROPERTY(CustomProperties)->Reset();
974                 CompleteInitialization(META_NS::GetValue(Name()));
975                 updatingInputs_ = false;
976             }
977         }
978     }
979 
980     static constexpr BASE_NS::string_view MATERIAL_SHADER_NAME { "MaterialComponent.materialShader.shader" };
981     static constexpr BASE_NS::string_view DEPTH_SHADER_NAME { "MaterialComponent.depthShader.shader" };
982 
FindTextureInfo(BASE_NS::string_view name)983     SCENE_NS::ITextureInfo::Ptr FindTextureInfo(BASE_NS::string_view name)
984     {
985         if (META_ACCESS_PROPERTY(Inputs)) {
986             for (auto& info : Inputs()->GetValue()) {
987                 if (META_NS::GetValue(info->Name()) == name) {
988                     return info;
989                 }
990             }
991         }
992         return {};
993     }
994 
CopyTextureInfoProperties(SCENE_NS::ITextureInfo::Ptr from,SCENE_NS::ITextureInfo::Ptr to)995     void CopyTextureInfoProperties(SCENE_NS::ITextureInfo::Ptr from, SCENE_NS::ITextureInfo::Ptr to)
996     {
997         if (from->Factor()->IsValueSet()) {
998             to->Factor()->SetValue(from->Factor()->GetValue());
999         }
1000 
1001         if (from->Rotation()->IsValueSet()) {
1002             to->Rotation()->SetValue(from->Rotation()->GetValue());
1003         }
1004 
1005         if (from->Scale()->IsValueSet()) {
1006             to->Scale()->SetValue(from->Scale()->GetValue());
1007         }
1008 
1009         if (from->Translation()->IsValueSet()) {
1010             to->Translation()->SetValue(from->Translation()->GetValue());
1011         }
1012 
1013         // Sampler enumeration works now using enums, it should follow uri
1014         // but uri information is not available from engine yet
1015         if (from->Sampler()->IsValueSet()) {
1016             to->Sampler()->SetValue(from->Sampler()->GetValue());
1017         }
1018 
1019         // image goes through uri implementation
1020         if (auto data = META_NS::GetValue(from->Image())) {
1021             to->Image()->SetValue(from->Image()->GetValue());
1022         }
1023     }
1024 
SynchronizeInputsFromMetadata()1025     bool SynchronizeInputsFromMetadata()
1026     {
1027         auto meta = interface_pointer_cast<META_NS::IMetadata>(ecsObject_);
1028         if (!meta) {
1029             return false;
1030         }
1031         // Texture slots need to be bound before other properties
1032 	BASE_NS::vector<SCENE_NS::ITextureInfo::Ptr> textures;
1033 	auto prop =
1034             meta->GetArrayPropertyByName<CORE3D_NS::MaterialComponent::TextureInfo>("MaterialComponent.textures");
1035 	// slot names (unsure-what-they-were-before-but-matches-core3d-enum)
1036 	const char* TextureIndexName[] = { "BASE_COLOR", "NORMAL", "MATERIAL", "EMISSIVE", "AO", "CLEARCOAT",
1037 	    "CLEARCOAT_ROUGHNESS", "CLEARCOAT_NORMAL", "SHEEN", "TRANSMISSION", "SPECULAR" };
1038 	auto v = prop->GetValue();
1039 	auto shp = SceneHolder();
1040 	for (auto t : v) {
1041 	    int textureSlotIndex = textures.size();
1042 	    // create wrapping things..
1043 	    auto info = GetTextureInfoByIndex(textureSlotIndex, textures);
1044 	    if (!info) {
1045 	        auto& obr = GetObjectRegistry();
1046 		auto params = obr.ConstructMetadata();
1047 		using IntfPtr = META_NS::SharedPtrIInterface;
1048 		params->AddProperty(META_NS::ConstructProperty<META_NS::IProperty::Ptr>(
1049 		    "textureInfoArray", nullptr, META_NS::ObjectFlagBits::INTERNAL | META_NS::ObjectFlagBits::NATIVE));
1050 		params->AddProperty(META_NS::ConstructProperty<uint32_t>(
1051 		    "textureSlotIndex", 0, META_NS::ObjectFlagBits::INTERNAL | META_NS::ObjectFlagBits::NATIVE));
1052 		params->AddProperty(META_NS::ConstructProperty<uintptr_t>(
1053 		    "sceneHolder", 0, META_NS::ObjectFlagBits::INTERNAL | META_NS::ObjectFlagBits::NATIVE));
1054 
1055 		// yes this is ugly.
1056 		params->GetPropertyByName<uintptr_t>("sceneHolder")->SetValue((uintptr_t)&shp);
1057 		params->GetPropertyByName<IntfPtr>("textureInfoArray")
1058 		    ->SetValue(interface_pointer_cast<CORE_NS::IInterface>(prop.GetProperty()));
1059 		params->GetPropertyByName<uint32_t>("textureSlotIndex")->SetValue(textureSlotIndex);
1060 		info = obr.Create<SCENE_NS::ITextureInfo>(SCENE_NS::ClassId::TextureInfo, params);
1061 		if (!info) {
1062 		    return {};
1063 		}
1064 
1065 		info->SetTextureSlotIndex(textureSlotIndex);
1066 		textures.push_back(info);
1067 	    } else {
1068 	        textures.push_back(nullptr);
1069 	    }
1070 	}
1071 
1072         BASE_NS::vector<SCENE_NS::ITextureInfo::Ptr> prevInputs;
1073         if (META_ACCESS_PROPERTY(Inputs)) {
1074             auto size = META_ACCESS_PROPERTY(Inputs)->GetSize();
1075             for (size_t ii = 0; ii < size; ii++) {
1076                 prevInputs.push_back(META_ACCESS_PROPERTY(Inputs)->GetValueAt(ii));
1077             }
1078         }
1079 
1080         META_ACCESS_PROPERTY(Inputs)->Reset();
1081 
1082         // Sort texture infos.
1083         std::sort(textures.begin(), textures.end(), [](const auto& a, const auto& b) {
1084             // Sort based on texture-slot index.
1085             return a->GetTextureSlotIndex() < b->GetTextureSlotIndex();
1086         });
1087 
1088         // Assign to property.
1089         Inputs()->SetValue(textures);
1090 
1091         // Copy values from old inputs to new ones.
1092         for (auto& from : prevInputs) {
1093             if (auto to = FindTextureInfo(META_NS::GetValue(from->Name()))) {
1094                 CopyTextureInfoProperties(from, to);
1095             }
1096         }
1097 
1098         return true;
1099     }
1100 
1101     // return true if something changes
UpdateAllInputProperties()1102     bool UpdateAllInputProperties()
1103     {
1104         auto meta = interface_pointer_cast<META_NS::IMetadata>(EcsObject());
1105         if (!meta) {
1106             return false;
1107         }
1108 
1109         auto oldCount = meta->GetPropertyContainer()->GetSize();
1110 
1111         // This updates all properties from ecs and detaches properties that do not exist any more.
1112         EcsObject()->DefineTargetProperties(PropertyNameMask());
1113 
1114         auto newCount = meta->GetPropertyContainer()->GetSize();
1115 
1116         auto allProperties = meta->GetAllProperties();
1117 
1118         // if we add or remove something these all cannot match
1119         return !((newCount == oldCount) && (oldCount != meta->GetPropertyContainer()->GetSize()));
1120     }
1121 
CompleteInitialization(const BASE_NS::string & path)1122     bool CompleteInitialization(const BASE_NS::string& path) override
1123     {
1124         if (!NodeImpl::CompleteInitialization(path)) {
1125             return false;
1126         }
1127 
1128         auto meta = interface_pointer_cast<META_NS::IMetadata>(ecsObject_);
1129         if (!meta) {
1130             return false;
1131         }
1132 
1133 
1134         if (DepthShader()->GetValue()) {
1135             SceneHolder()->SetShader(
1136                 EcsObject()->GetEntity(), SceneHolder::ShaderType::DEPTH_SHADER, DepthShader()->GetValue());
1137         } else {
1138             // Try to introspect depth shader from material.
1139             auto shader = SceneHolder()->GetShader(EcsObject()->GetEntity(), SceneHolder::ShaderType::DEPTH_SHADER);
1140             if (shader) {
1141                 DepthShader()->SetValue(shader);
1142             }
1143         }
1144 
1145         if (DepthShaderState()->GetValue()) {
1146             SceneHolder()->SetGraphicsState(
1147                 EcsObject()->GetEntity(), SceneHolder::ShaderType::DEPTH_SHADER, DepthShaderState()->GetValue());
1148         } else {
1149             // Try to introspect depth shader from material.
1150             auto state =
1151                 SceneHolder()->GetGraphicsState(EcsObject()->GetEntity(), SceneHolder::ShaderType::DEPTH_SHADER);
1152             if (state) {
1153                 DepthShaderState()->SetValue(state);
1154             }
1155         }
1156 
1157         if (MaterialShader()->GetValue()) {
1158             SceneHolder()->SetShader(
1159                 EcsObject()->GetEntity(), SceneHolder::ShaderType::MATERIAL_SHADER, MaterialShader()->GetValue());
1160         } else {
1161             // Try to introspect material shader from material.
1162             auto shader = SceneHolder()->GetShader(EcsObject()->GetEntity(), SceneHolder::ShaderType::MATERIAL_SHADER);
1163             if (shader) {
1164                 MaterialShader()->SetValue(shader);
1165             }
1166         }
1167 
1168         if (MaterialShaderState()->GetValue()) {
1169             SceneHolder()->SetGraphicsState(
1170                 EcsObject()->GetEntity(), SceneHolder::ShaderType::MATERIAL_SHADER, MaterialShaderState()->GetValue());
1171         } else {
1172             // Try to introspect depth shader from material.
1173             auto state =
1174                 SceneHolder()->GetGraphicsState(EcsObject()->GetEntity(), SceneHolder::ShaderType::MATERIAL_SHADER);
1175             if (state) {
1176                 MaterialShaderState()->SetValue(state);
1177             }
1178         }
1179 
1180         // Shader may have changed, so update all properties.
1181         UpdateAllInputProperties();
1182 
1183         // Properties up-to-date, synchronize all inputs.
1184         SynchronizeInputsFromMetadata();
1185 
1186         propHandler_.NewHandler(nullptr, nullptr).Subscribe(META_ACCESS_PROPERTY(Type), [this]() { OnTypeChanged(); });
1187 
1188         BindChanges(propHandler_, META_ACCESS_PROPERTY(Type), meta, "MaterialComponent.type");
1189 
1190         // Shader will either come as an entity ref from the engine, or from serialized info. have a listener
1191         // that attaches the new ecs object to entity if one appears
1192         propHandler_.NewHandler(nullptr, nullptr).Subscribe(DepthShader(), [this]() {
1193             SceneHolder()->SetShader(
1194                 EcsObject()->GetEntity(), SceneHolder::ShaderType::DEPTH_SHADER, DepthShader()->GetValue());
1195             UpdateInputs();
1196         });
1197 
1198         propHandler_.NewHandler(nullptr, nullptr).Subscribe(MaterialShader(), [this]() {
1199             // Material shader has changed.
1200             SceneHolder()->SetShader(
1201                 EcsObject()->GetEntity(), SceneHolder::ShaderType::MATERIAL_SHADER, MaterialShader()->GetValue());
1202             UpdateInputs();
1203         });
1204 
1205         propHandler_.NewHandler(nullptr, nullptr).Subscribe(DepthShaderState(), [this]() {
1206             SceneHolder()->SetGraphicsState(
1207                 EcsObject()->GetEntity(), SceneHolder::ShaderType::DEPTH_SHADER, DepthShaderState()->GetValue());
1208         });
1209 
1210         propHandler_.NewHandler(nullptr, nullptr).Subscribe(MaterialShaderState(), [this]() {
1211             // Material shader has changed.
1212             SceneHolder()->SetGraphicsState(
1213                 EcsObject()->GetEntity(), SceneHolder::ShaderType::MATERIAL_SHADER, MaterialShaderState()->GetValue());
1214         });
1215 
1216         propHandler_.MarkRelated(MaterialShader(), meta->GetPropertyByName("MaterialComponent.materialShader.shader"));
1217         propHandler_.MarkRelated(DepthShader(), meta->GetPropertyByName("MaterialComponent.depthShader.shader"));
1218         propHandler_.MarkRelated(
1219             MaterialShaderState(), meta->GetPropertyByName("MaterialComponent.materialShader.graphicsState"));
1220         propHandler_.MarkRelated(
1221             DepthShaderState(), meta->GetPropertyByName("MaterialComponent.depthShader.graphicsState"));
1222 
1223         // make sure that inputs are up to date
1224         OnTypeChanged();
1225 
1226         // Update custom properties.
1227         UpdateCustomProperties();
1228 
1229         BindChanges(propHandler_, META_ACCESS_PROPERTY(AlphaCutoff), meta, "MaterialComponent.alphaCutoff");
1230         BindChanges(propHandler_, META_ACCESS_PROPERTY(LightingFlags), meta, "MaterialComponent.materialLightingFlags");
1231         return true;
1232     }
1233 
BuildChildren(SCENE_NS::INode::BuildBehavior)1234     bool BuildChildren(SCENE_NS::INode::BuildBehavior) override
1235     {
1236         // in typical cases we should not have children
1237         if (META_NS::GetValue(META_ACCESS_PROPERTY(Status)) == SCENE_NS::INode::NODE_STATUS_CONNECTED) {
1238             SetStatus(SCENE_NS::INode::NODE_STATUS_FULLY_CONNECTED);
1239             META_NS::Invoke<META_NS::IOnChanged>(OnBound());
1240             bound_ = true;
1241         }
1242         return true;
1243     }
1244 
SetImage(SCENE_NS::IBitmap::Ptr bitmap,BASE_NS::string_view textureSlot)1245     void SetImage(SCENE_NS::IBitmap::Ptr bitmap, BASE_NS::string_view textureSlot) override
1246     {
1247         auto size = Inputs()->GetSize();
1248         for (size_t ii = 0; ii < size; ii++) {
1249             if (META_NS::GetValue(Inputs()->GetValueAt(ii)->Name()).compare(textureSlot) == 0) {
1250                 SetImage(bitmap, ii);
1251                 break;
1252             }
1253         }
1254     }
1255 
SetImage(SCENE_NS::IBitmap::Ptr bitmap,size_t index)1256     void SetImage(SCENE_NS::IBitmap::Ptr bitmap, size_t index) override
1257     {
1258         if (bitmap) {
1259             auto status = META_NS::GetValue(bitmap->Status());
1260             if (status == SCENE_NS::IBitmap::BitmapStatus::COMPLETED) {
1261                 if (auto sceneHolder = SceneHolder()) {
1262                     sceneHolder->QueueEngineTask(
1263                         MakeTask(
1264                             [bitmap, index, weakSelf = BASE_NS::weak_ptr(GetSelf())](auto sh) {
1265                                 CORE_NS::Entity imageEntity = sh->BindUIBitmap(bitmap, true);
1266                                 auto image = sh->GetEcs()->GetEntityManager().GetReferenceCounted(imageEntity);
1267                                 sh->SetTexture(index,
1268                                     interface_pointer_cast<INodeEcsInterfacePrivate>(weakSelf)
1269                                         ->EcsObject()
1270                                         ->GetEntity(),
1271                                     image);
1272                                 sh->QueueApplicationTask(MakeTask([bitmap, index, weakSelf]() {
1273                                     if (auto me = interface_pointer_cast<SCENE_NS::IMaterial>(weakSelf)) {
1274                                         if (auto input = me->Inputs()->GetValueAt(index)) {
1275                                             input->Image()->SetValue(bitmap);
1276                                         }
1277                                     }
1278                                     return false;
1279                                 }),
1280                                     false);
1281 
1282                                 return false;
1283                             },
1284                             sceneHolder),
1285                         false);
1286                 }
1287 
1288             } else {
1289                 // should basically subscribe to dynamic content instead
1290                 // Give uri based loading a shot
1291                 if (auto input = Inputs()->GetValueAt(index)) {
1292                     input->Image()->SetValue(bitmap);
1293                 }
1294             }
1295         } else { // reset existing image if there is one
1296             if (auto sceneHolder = SceneHolder()) {
1297                 sceneHolder->QueueEngineTask(MakeTask(
1298                                                  [entityId = EcsObject()->GetEntity().id](auto sh) {
1299                                                      CORE_NS::Entity target { entityId };
1300                                                      // The assumption is that using base color ix is correct thing to
1301                                                      // do
1302                                                      sh->SetTexture(
1303                                                          CORE3D_NS::MaterialComponent::BASE_COLOR, target, {});
1304 
1305                                                      return false;
1306                                                  },
1307                                                  sceneHolder),
1308                     false);
1309             }
1310         }
1311     }
1312 };
1313 } // namespace
SCENE_BEGIN_NAMESPACE()1314 SCENE_BEGIN_NAMESPACE()
1315 
1316 void RegisterMaterialImpl()
1317 {
1318     META_NS::GetObjectRegistry().RegisterObjectType<MaterialImpl>();
1319 }
UnregisterMaterialImpl()1320 void UnregisterMaterialImpl()
1321 {
1322     META_NS::GetObjectRegistry().UnregisterObjectType<MaterialImpl>();
1323 }
1324 
1325 SCENE_END_NAMESPACE()
1326