• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "material.h"
16 
17 #include <mutex>
18 #include <scene/ext/util.h>
19 #include <set>
20 
21 #include <3d/ecs/components/name_component.h>
22 #include <core/log.h>
23 
24 #include <meta/api/engine/util.h>
25 #include <meta/api/make_callback.h>
26 #include <meta/interface/engine/intf_engine_value_manager.h>
27 
28 #include "entity_converting_value.h"
29 #include "mesh/texture.h"
30 #include "util_interfaces.h"
31 
32 SCENE_BEGIN_NAMESPACE()
33 namespace {
34 struct ShaderConverter {
ShaderConverter__anon1fbf93410111::ShaderConverter35     ShaderConverter(const IInternalScene::Ptr& scene) : scene_(scene) {}
36 
37     using SourceType = IShader::Ptr;
38     using TargetType = CORE3D_NS::MaterialComponent::Shader;
39 
ConvertToSource__anon1fbf93410111::ShaderConverter40     SourceType ConvertToSource(META_NS::IAny& any, const TargetType& v) const
41     {
42         auto p = META_NS::GetPointer<IShader>(any);
43         if (auto scene = scene_.lock()) {
44             scene
45                 ->AddTask([&] {
46                     if (auto rhman = static_cast<CORE3D_NS::IRenderHandleComponentManager*>(
47                             scene->GetEcsContext().FindComponent<CORE3D_NS::RenderHandleComponent>())) {
48                         bool changed = false;
49                         if (!p) {
50                             p = interface_pointer_cast<IShader>(scene->CreateObject(ClassId::Shader));
51                             changed = true;
52                         }
53                         auto sha = rhman->GetRenderHandleReference(v.shader);
54                         auto gs = rhman->GetRenderHandleReference(v.graphicsState);
55 
56                         if (auto i = interface_cast<IRenderResource>(p)) {
57                             changed |= i->GetRenderHandle().GetHandle() != sha.GetHandle();
58                         }
59                         if (auto i = interface_cast<IGraphicsState>(p)) {
60                             changed |= i->GetGraphicsState().GetHandle() != gs.GetHandle();
61                         }
62                         if (auto i = interface_cast<IShaderState>(p); changed && i) {
63                             i->SetShaderState(sha, gs);
64                         }
65                     }
66                 })
67                 .Wait();
68         }
69         return p;
70     }
ConvertToTarget__anon1fbf93410111::ShaderConverter71     TargetType ConvertToTarget(const SourceType& v) const
72     {
73         TargetType res;
74         if (auto scene = scene_.lock()) {
75             if (!v) {
76                 res.shader = {};
77                 res.graphicsState = {};
78             } else {
79                 if (auto i = interface_cast<IRenderResource>(v)) {
80                     res.shader = HandleFromRenderResource(scene, i->GetRenderHandle());
81                 }
82                 if (auto i = interface_cast<IGraphicsState>(v)) {
83                     res.graphicsState = HandleFromRenderResource(scene, i->GetGraphicsState());
84                 }
85             }
86         }
87         return res;
88     }
89 
90 private:
91     IInternalScene::WeakPtr scene_;
92 };
93 struct RenderSortConverter {
94     using SourceType = SCENE_NS::RenderSort;
95     using TargetType = CORE3D_NS::MaterialComponent::RenderSort;
96 
ConvertToSource__anon1fbf93410111::RenderSortConverter97     SourceType ConvertToSource(META_NS::IAny&, const TargetType& v) const
98     {
99         return SourceType { v.renderSortLayer, v.renderSortLayerOrder };
100     }
ConvertToTarget__anon1fbf93410111::RenderSortConverter101     TargetType ConvertToTarget(const SourceType& v) const
102     {
103         return TargetType { v.renderSortLayer, v.renderSortLayerOrder };
104     }
105 };
106 struct LightingFlagsConverter {
107     using SourceType = SCENE_NS::LightingFlags;
108     using TargetType = uint32_t;
109 
ConvertToSource__anon1fbf93410111::LightingFlagsConverter110     SourceType ConvertToSource(META_NS::IAny&, const TargetType& v) const
111     {
112         return static_cast<SourceType>(v);
113     }
ConvertToTarget__anon1fbf93410111::LightingFlagsConverter114     TargetType ConvertToTarget(const SourceType& v) const
115     {
116         return static_cast<TargetType>(v);
117     }
118 };
119 } // namespace
120 
CreateEntity(const IInternalScene::Ptr & scene)121 CORE_NS::Entity Material::CreateEntity(const IInternalScene::Ptr& scene)
122 {
123     const auto& ecs = scene->GetEcsContext().GetNativeEcs();
124     const auto materialManager = CORE_NS::GetManager<CORE3D_NS::IMaterialComponentManager>(*ecs);
125     if (!materialManager) {
126         return {};
127     }
128     const auto nameManager = CORE_NS::GetManager<CORE3D_NS::INameComponentManager>(*ecs);
129     if (!nameManager) {
130         return {};
131     }
132     auto ent = ecs->GetEntityManager().Create();
133     materialManager->Create(ent);
134     nameManager->Create(ent);
135     return ent;
136 }
137 
SetEcsObject(const IEcsObject::Ptr & obj)138 bool Material::SetEcsObject(const IEcsObject::Ptr& obj)
139 {
140     shaderChanged_.Unsubscribe();
141     if (Super::SetEcsObject(obj)) {
142         if (auto att = GetSelf<META_NS::IAttach>()) {
143             if (auto attc = att->GetAttachmentContainer(false)) {
144                 if (auto c = attc->FindByName<IInternalMaterial>("MaterialComponent")) {
145                     material_ = c;
146                 }
147             }
148         }
149         if (!material_) {
150             auto p = META_NS::GetObjectRegistry().Create<IInternalMaterial>(ClassId::MaterialComponent);
151             if (auto acc = interface_cast<IEcsObjectAccess>(p)) {
152                 if (acc->SetEcsObject(obj)) {
153                     material_ = p;
154                     IAttach::Attach(material_);
155                 }
156             }
157         }
158     }
159     return material_ != nullptr;
160 }
161 
InitDynamicProperty(const META_NS::IProperty::Ptr & p,BASE_NS::string_view path)162 bool Material::InitDynamicProperty(const META_NS::IProperty::Ptr& p, BASE_NS::string_view path)
163 {
164     BASE_NS::string name = p->GetName();
165     if (name == "MaterialShader") {
166         auto ep = material_->MaterialShader();
167         shaderChanged_.Subscribe(ep, [this]() {
168             UpdateTextures(nullptr);
169             UpdateCustoms(nullptr);
170         });
171 
172         auto i = interface_cast<META_NS::IStackProperty>(p);
173         return ep && i &&
174                i->PushValue(META_NS::IValue::Ptr(new ConvertingValue<ShaderConverter>(ep, { object_->GetScene() })));
175     }
176     if (name == "DepthShader") {
177         auto ep = material_->DepthShader();
178         auto i = interface_cast<META_NS::IStackProperty>(p);
179         return ep && i &&
180                i->PushValue(META_NS::IValue::Ptr(new ConvertingValue<ShaderConverter>(ep, { object_->GetScene() })));
181     }
182     if (name == "Textures") {
183         UpdateTextures(p).Wait();
184         return true;
185     }
186     if (name == "CustomProperties") {
187         UpdateCustoms(p).Wait();
188         return true;
189     }
190     if (name == "RenderSort") {
191         auto ep = material_->RenderSort();
192         auto i = interface_cast<META_NS::IStackProperty>(p);
193         return ep && i && i->PushValue(META_NS::IValue::Ptr(new ConvertingValue<RenderSortConverter>(ep)));
194     }
195     if (name == "LightingFlags") {
196         auto ep = material_->LightingFlags();
197         auto i = interface_cast<META_NS::IStackProperty>(p);
198         return ep && i && i->PushValue(META_NS::IValue::Ptr(new ConvertingValue<LightingFlagsConverter>(ep)));
199     }
200     return false;
201 }
202 
UpdateTextures(const META_NS::IProperty::Ptr & p)203 Future<bool> Material::UpdateTextures(const META_NS::IProperty::Ptr& p)
204 {
205     auto property = p;
206     if (!property) {
207         property = GetProperty("Textures", META_NS::MetadataQuery::EXISTING);
208     }
209     if (const auto obj = GetEcsObject(); obj && property) {
210         if (!textureSyncScheduled_.exchange(true)) {
211             return obj->GetScene()->AddTask([this, property]() {
212                 if (textureSyncScheduled_.load()) {
213                     bool success =
214                         ConstructTextures(property); // Avoid callback due to ConstructTextures changing target
215                     textureSyncScheduled_ = false;
216                 }
217                 return false;
218             });
219         }
220     }
221     return {};
222 }
223 
UpdateCustoms(const META_NS::IProperty::Ptr & p)224 Future<bool> Material::UpdateCustoms(const META_NS::IProperty::Ptr& p)
225 {
226     auto property = p;
227     if (!property) {
228         property = GetProperty("CustomProperties", META_NS::MetadataQuery::EXISTING);
229     }
230     if (const auto obj = GetEcsObject(); obj && property) {
231         if (!customsSyncScheduled_.exchange(true)) {
232             return obj->GetScene()->AddTask([this, property]() {
233                 if (customsSyncScheduled_.load()) {
234                     bool success =
235                         UpdateCustomProperties(property); // Avoid callback due to ConstructTextures changing target
236                     customsSyncScheduled_ = false;
237                 }
238                 return false;
239             });
240         }
241     }
242     return {};
243 }
244 
245 // clang-format off
246 const char* const METALLIC_ROUGHNESS_TEXTURE_NAMES[] = {
247     "BASE_COLOR",
248     "NORMAL",
249     "MATERIAL",
250     "EMISSIVE",
251     "AO",
252     "CLEARCOAT",
253     "CLEARCOAT_ROUGHNESS",
254     "CLEARCOAT_NORMAL",
255     "SHEEN",
256     "TRANSMISSION",
257     "SPECULAR"
258 };
259 const char* const METALLIC_ROUGHNESS_ECS_TEXTURE_NAMES[] = {
260     "baseColor",
261     "normal",
262     "material",
263     "emissive",
264     "ambientOcclusion",
265     "clearcoat",
266     "clearcoatRoughness",
267     "clearcoatNormal",
268     "sheen",
269     "transmission",
270     "specular"
271 };
272 constexpr size_t TEXTURE_NAMES_SIZE = sizeof(METALLIC_ROUGHNESS_TEXTURE_NAMES) / sizeof(*METALLIC_ROUGHNESS_TEXTURE_NAMES);
273 // clang-format on
274 
UpdateTextureNames(const BASE_NS::vector<ITexture::Ptr> & textures,const IInternalMaterial::ActiveTextureSlotInfo & tsi)275 bool Material::UpdateTextureNames(
276     const BASE_NS::vector<ITexture::Ptr>& textures, const IInternalMaterial::ActiveTextureSlotInfo& tsi)
277 {
278     bool updated = false;
279     for (size_t i = 0; i < textures.size(); ++i) {
280         if (auto& t = textures[i]) {
281             if (auto named = interface_cast<META_NS::INamed>(t)) {
282                 auto nameProp = named->Name();
283                 const auto& name = tsi.slots[i].name;
284                 if (i < TEXTURE_NAMES_SIZE && name == METALLIC_ROUGHNESS_ECS_TEXTURE_NAMES[i]) {
285                     updated |=
286                         nameProp->SetDefaultValue(METALLIC_ROUGHNESS_TEXTURE_NAMES[i]) == META_NS::AnyReturn::SUCCESS;
287                 } else {
288                     // Different name to the default ecs texture slot name
289                     updated |= nameProp->SetDefaultValue(name) == META_NS::AnyReturn::SUCCESS;
290                 }
291             }
292         }
293     }
294     return updated;
295 }
296 
ConstructTextures(const META_NS::IProperty::Ptr & p)297 bool Material::ConstructTextures(const META_NS::IProperty::Ptr& p)
298 {
299     auto textures = META_NS::ArrayProperty<ITexture::Ptr>(p);
300     if (!textures || !material_) {
301         return false;
302     }
303     BASE_NS::vector<ITexture::Ptr> texs = textures->GetValue();
304     auto tsi = material_->GetActiveTextureSlotInfo();
305     if (texs.size() == tsi.count) {
306         if (UpdateTextureNames(texs, tsi)) {
307             textures->NotifyChange();
308         }
309         return true;
310     }
311     auto mtex = material_->Textures()->GetValue();
312     texs.resize(tsi.count);
313     auto& r = META_NS::GetObjectRegistry();
314     for (size_t i = 0; i < texs.size(); ++i) {
315         auto& t = texs[i];
316         if (!t) {
317             t = r.Create<ITexture>(ClassId::Texture);
318             if (auto si = interface_cast<IArrayElementIndex>(t)) {
319                 si->SetIndex(i);
320             }
321             if (auto access = interface_cast<IEcsObjectAccess>(t)) {
322                 access->SetEcsObject(GetEcsObject());
323             }
324             texs[i] = t;
325         }
326         if (!t) {
327             CORE_LOG_E("Failed to create texture for material");
328             return false;
329         }
330     }
331     UpdateTextureNames(texs, tsi);
332     if (!texs.empty()) {
333         textures->SetValue(texs);
334     } else {
335         textures->ResetValue();
336     }
337     return true;
338 }
339 
UpdateCustomProperties(const META_NS::IProperty::Ptr & p)340 bool Material::UpdateCustomProperties(const META_NS::IProperty::Ptr& p)
341 {
342     auto ecsobj = GetEcsObject();
343     auto ccs = META_NS::Property<META_NS::IMetadata::Ptr>(p);
344     if (!ccs || !material_ || !ecsobj) {
345         return false;
346     }
347     auto meta = ccs->GetValue();
348     if (!meta) {
349         meta = META_NS::GetObjectRegistry().Create<META_NS::IMetadata>(META_NS::ClassId::Object);
350     }
351     std::set<BASE_NS::string> props;
352     BASE_NS::vector<META_NS::IEngineValue::Ptr> values;
353     if (SyncCustomProperties(&values)) {
354         for (auto&& v : values) {
355             BASE_NS::string pname(SCENE_NS::PropertyName(v->GetName()));
356             auto p = meta->GetProperty(pname);
357             if (p && GetEngineValueFromProperty(p) != v) {
358                 auto iany = META_NS::GetInternalAny(p);
359                 auto eval = interface_cast<META_NS::IValue>(v);
360                 if (!iany || !eval || !META_NS::IsCompatibleWith(*iany, eval->GetValue())) {
361                     CORE_LOG_W("Removing incompatible custom property: %s", pname.c_str());
362                     meta->RemoveProperty(p);
363                     p = nullptr;
364                 }
365             }
366             if (p) {
367                 if (GetEngineValueFromProperty(p) != v) {
368                     auto value = p->GetValue().Clone();
369                     if (value && ecsobj->AttachProperty(p, v).GetResult()) {
370                         // set the value from the users property
371                         p->SetValue(*value);
372                     } else {
373                         CORE_LOG_W("Failed to attach custom property: %s", pname.c_str());
374                         meta->RemoveProperty(p);
375                     }
376                 }
377             } else {
378                 p = ecsobj->CreateProperty(v).GetResult();
379                 if (p) {
380                     meta->AddProperty(p);
381                 }
382             }
383             props.insert(pname);
384         }
385         for (auto&& p : meta->GetProperties()) {
386             // we remove properties with engine access, user added, not yet attached properties are left
387             if (!props.count(p->GetName()) && GetEngineValueFromProperty(p)) {
388                 meta->RemoveProperty(p);
389             }
390         }
391         ccs->SetValue(meta);
392     }
393     return true;
394 }
395 
GetCustomProperties() const396 META_NS::IMetadata::Ptr Material::GetCustomProperties() const
397 {
398     return CustomProperties()->GetValue();
399 }
400 
GetCustomProperty(BASE_NS::string_view name) const401 META_NS::IProperty::Ptr Material::GetCustomProperty(BASE_NS::string_view name) const
402 {
403     const auto obj = GetEcsObject();
404     if (!obj) {
405         return nullptr;
406     }
407     const auto manager = obj->GetEngineValueManager();
408     if (!manager) {
409         return nullptr;
410     }
411 
412     if (!SyncCustomProperties({})) {
413         CORE_LOG_W("Syncing properties to engine values failed. Using old values if available.");
414     }
415     BASE_NS::string fullName { name };
416     if (!name.starts_with("MaterialComponent.customProperties.")) {
417         fullName = "MaterialComponent.customProperties." + fullName;
418     }
419     return META_NS::PropertyFromEngineValue(fullName, manager->GetEngineValue(fullName));
420 }
421 
SyncCustomProperties(BASE_NS::vector<META_NS::IEngineValue::Ptr> * synced_values) const422 bool Material::SyncCustomProperties(BASE_NS::vector<META_NS::IEngineValue::Ptr>* synced_values) const
423 {
424     const auto obj = GetEcsObject();
425     if (!material_ || !obj) {
426         return false;
427     }
428     const auto manager = obj->GetEngineValueManager();
429     const auto allCustomProps = META_NS::GetEngineValueFromProperty(material_->CustomProperties());
430     if (!manager || !allCustomProps) {
431         return false;
432     }
433 
434     auto doSync = [&]() -> bool {
435         // syncing material shader updates the custom properties
436         obj->GetScene()->SyncProperty(material_->MaterialShader(), META_NS::EngineSyncDirection::AUTO);
437         obj->GetScene()->SyncProperty(material_->CustomProperties(), META_NS::EngineSyncDirection::FROM_ENGINE);
438         return manager->ConstructValues(allCustomProps, { "", synced_values });
439     };
440     return obj->GetScene()->AddTask(doSync).Wait();
441 }
442 
CopyTextureData(const META_NS::IProperty::ConstPtr & p)443 void Material::CopyTextureData(const META_NS::IProperty::ConstPtr& p)
444 {
445     if (META_NS::ArrayProperty<const ITexture::Ptr> arr { p }) {
446         for (size_t i = 0; i != arr->GetSize(); ++i) {
447             if (auto in = interface_cast<META_NS::IMetadata>(arr->GetValueAt(i))) {
448                 if (auto out = interface_cast<META_NS::IMetadata>(Textures()->GetValueAt(i))) {
449                     META_NS::CopyToDefaultAndReset(*in, *out);
450                 }
451             }
452         }
453     }
454 }
455 
SetResource(const CORE_NS::IResource::Ptr & p)456 bool Material::SetResource(const CORE_NS::IResource::Ptr& p)
457 {
458     auto ores = interface_cast<META_NS::IObjectResource>(p);
459     if (!ores || p->GetResourceType() != resourceType_.ToUid()) {
460         CORE_LOG_W("Invalid resource type");
461         return false;
462     }
463     if (auto resm = interface_cast<META_NS::IMetadata>(p)) {
464         if (auto m = static_cast<META_NS::IMetadata*>(this)) {
465             for (auto&& p : resm->GetProperties()) {
466                 if (p->GetName() == "Textures") {
467                     CopyTextureData(p);
468                 } else if (p->GetName() == "CustomProperties") {
469                     // skip for now
470                 } else {
471                     META_NS::CopyToDefaultAndReset(p, *m);
472                 }
473             }
474         }
475     }
476     std::unique_lock lock { mutex_ };
477     resource_ = p->GetResourceId();
478     return true;
479 }
CreateResource() const480 CORE_NS::IResource::Ptr Material::CreateResource() const
481 {
482     auto r = Super::CreateResource();
483     if (auto i = interface_cast<META_NS::IMetadata>(r)) {
484         if (auto p = i->GetProperty("CustomProperties")) {
485             i->RemoveProperty(p);
486         }
487     }
488     return r;
489 }
490 
491 SCENE_END_NAMESPACE()
492