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
16 #include "material.h"
17
18 #include <scene/ext/util.h>
19
20 #include <core/log.h>
21
22 #include <meta/api/engine/util.h>
23 #include <meta/api/make_callback.h>
24
25 #include "../entity_converting_value.h"
26 #include "meta/interface/engine/intf_engine_value_manager.h"
27 #include "texture.h"
28
29 SCENE_BEGIN_NAMESPACE()
30 namespace {
31 struct ShaderConverter {
ShaderConverter__anon835064240111::ShaderConverter32 ShaderConverter(const IInternalScene::Ptr& scene, META_NS::Property<CORE3D_NS::MaterialComponent::Shader> p)
33 : scene_(scene), p_(p)
34 {}
35
36 using SourceType = IShader::Ptr;
37 using TargetType = CORE3D_NS::MaterialComponent::Shader;
38
ConvertToSource__anon835064240111::ShaderConverter39 SourceType ConvertToSource(META_NS::IAny& any, const TargetType& v) const
40 {
41 auto p = META_NS::GetPointer<IShader>(any);
42 if (auto scene = scene_.lock()) {
43 scene
44 ->AddTask([&] {
45 if (!p) {
46 p = interface_pointer_cast<IShader>(scene->CreateObject(ClassId::Shader));
47 }
48 if (auto i = interface_cast<IShaderState>(p)) {
49 if (auto rhman = static_cast<CORE3D_NS::IRenderHandleComponentManager*>(
50 scene->GetEcsContext().FindComponent<CORE3D_NS::RenderHandleComponent>())) {
51 i->SetShaderState(scene, rhman->GetRenderHandleReference(v.shader), v.shader,
52 rhman->GetRenderHandleReference(v.graphicsState), v.graphicsState);
53
54 if (auto ig = interface_cast<IGraphicsState>(p)) {
55 auto s = ig->GetGraphicsState();
56 if (s != v.graphicsState) {
57 auto copy = v;
58 copy.graphicsState = s;
59
60 p_.GetUnlockedAccess().SetValue(copy);
61 }
62 }
63 }
64 }
65 })
66 .Wait();
67 }
68 return p;
69 }
ConvertToTarget__anon835064240111::ShaderConverter70 TargetType ConvertToTarget(const SourceType& v) const
71 {
72 TargetType res;
73 if (auto scene = scene_.lock()) {
74 if (auto i = interface_cast<IEcsResource>(v)) {
75 res.shader = scene->GetEcsContext().GetEntityReference(i->GetEntity());
76 }
77 if (auto i = interface_cast<IGraphicsState>(v)) {
78 res.graphicsState = i->GetGraphicsState();
79 }
80 }
81 return res;
82 }
83
84 private:
85 IInternalScene::WeakPtr scene_;
86 META_NS::Property<CORE3D_NS::MaterialComponent::Shader> p_;
87 };
88 struct RenderSortConverter {
89 using SourceType = SCENE_NS::RenderSort;
90 using TargetType = CORE3D_NS::MaterialComponent::RenderSort;
91
ConvertToSource__anon835064240111::RenderSortConverter92 SourceType ConvertToSource(META_NS::IAny&, const TargetType& v) const
93 {
94 return SourceType { v.renderSortLayer, v.renderSortLayerOrder };
95 }
ConvertToTarget__anon835064240111::RenderSortConverter96 TargetType ConvertToTarget(const SourceType& v) const
97 {
98 return TargetType { v.renderSortLayer, v.renderSortLayerOrder };
99 }
100 };
101 struct LightingFlagsConverter {
102 using SourceType = SCENE_NS::LightingFlags;
103 using TargetType = uint32_t;
104
ConvertToSource__anon835064240111::LightingFlagsConverter105 SourceType ConvertToSource(META_NS::IAny&, const TargetType& v) const
106 {
107 return static_cast<SourceType>(v);
108 }
ConvertToTarget__anon835064240111::LightingFlagsConverter109 TargetType ConvertToTarget(const SourceType& v) const
110 {
111 return static_cast<TargetType>(v);
112 }
113 };
114 } // namespace
115
CreateEntity(const IInternalScene::Ptr & scene)116 CORE_NS::Entity Material::CreateEntity(const IInternalScene::Ptr& scene)
117 {
118 const auto& ecs = scene->GetEcsContext().GetNativeEcs();
119 const auto materialManager = CORE_NS::GetManager<CORE3D_NS::IMaterialComponentManager>(*ecs);
120 if (!materialManager) {
121 return {};
122 }
123 auto ent = ecs->GetEntityManager().Create();
124 materialManager->Create(ent);
125 return ent;
126 }
127
SetEcsObject(const IEcsObject::Ptr & obj)128 bool Material::SetEcsObject(const IEcsObject::Ptr& obj)
129 {
130 if (Super::SetEcsObject(obj)) {
131 if (auto att = GetSelf<META_NS::IAttach>()) {
132 if (auto attc = att->GetAttachmentContainer(false)) {
133 if (auto c = attc->FindByName<IInternalMaterial>("MaterialComponent")) {
134 material_ = c;
135 }
136 }
137 }
138 if (!material_) {
139 auto p = META_NS::GetObjectRegistry().Create<IInternalMaterial>(ClassId::MaterialComponent);
140 if (auto acc = interface_cast<IEcsObjectAccess>(p)) {
141 if (acc->SetEcsObject(obj)) {
142 material_ = p;
143 }
144 }
145 }
146 }
147 return material_ != nullptr;
148 }
149
InitDynamicProperty(const META_NS::IProperty::Ptr & p,BASE_NS::string_view path)150 bool Material::InitDynamicProperty(const META_NS::IProperty::Ptr& p, BASE_NS::string_view path)
151 {
152 BASE_NS::string name = p->GetName();
153 if (name == "MaterialShader" || name == "DepthShader") {
154 auto ep = object_->CreateEngineProperty(path).GetResult();
155 auto i = interface_cast<META_NS::IStackProperty>(p);
156 return ep && i &&
157 i->PushValue(
158 META_NS::IValue::Ptr(new ConvertingValue<ShaderConverter>(ep, { object_->GetScene(), ep })));
159 }
160 if (name == "Textures") {
161 return ConstructTextures(p);
162 }
163 if (name == "RenderSort") {
164 auto ep = material_->RenderSort();
165 auto i = interface_cast<META_NS::IStackProperty>(p);
166 return ep && i && i->PushValue(META_NS::IValue::Ptr(new ConvertingValue<RenderSortConverter>(ep)));
167 }
168 if (name == "LightingFlags") {
169 auto ep = material_->LightingFlags();
170 auto i = interface_cast<META_NS::IStackProperty>(p);
171 return ep && i && i->PushValue(META_NS::IValue::Ptr(new ConvertingValue<LightingFlagsConverter>(ep)));
172 }
173 return false;
174 }
175
176 // clang-format off
177 const char* const TEXTURE_NAMES[] = {
178 "BASE_COLOR",
179 "NORMAL",
180 "MATERIAL",
181 "EMISSIVE",
182 "AO",
183 "CLEARCOAT",
184 "CLEARCOAT_ROUGHNESS",
185 "CLEARCOAT_NORMAL",
186 "SHEEN",
187 "TRANSMISSION",
188 "SPECULAR"
189 };
190 constexpr size_t TEXTURE_NAMES_SIZE = sizeof(TEXTURE_NAMES) / sizeof(*TEXTURE_NAMES);
191 // clang-format on
192
ConstructTextures(const META_NS::IProperty::Ptr & p)193 bool Material::ConstructTextures(const META_NS::IProperty::Ptr& p)
194 {
195 auto& r = META_NS::GetObjectRegistry();
196 BASE_NS::vector<ITexture::Ptr> texs;
197 auto mtex = material_->Textures()->GetValue();
198 auto mdata = r.Create<META_NS::IMetadata>(META_NS::ClassId::Object);
199 auto index = META_NS::ConstructProperty<size_t>("Index");
200 mdata->AddProperty(index);
201 for (size_t i = 0; i != mtex.size(); ++i) {
202 index->SetValue(i);
203 auto t = r.Create<ITexture>(ClassId::Texture, mdata);
204 if (auto access = interface_cast<IEcsObjectAccess>(t)) {
205 access->SetEcsObject(GetEcsObject());
206 if (auto named = interface_cast<META_NS::INamed>(t); named && i < TEXTURE_NAMES_SIZE) {
207 named->Name()->SetValue(TEXTURE_NAMES[i]);
208 }
209 texs.push_back(t);
210 } else {
211 CORE_LOG_E("Failed to create texture for material");
212 return false;
213 }
214 }
215 META_NS::ArrayProperty<ITexture::Ptr> prop { p };
216 if (prop) {
217 prop->SetValue(texs);
218 }
219 return static_cast<bool>(prop);
220 }
221
GetCustomProperties() const222 META_NS::IMetadata::Ptr Material::GetCustomProperties() const
223 {
224 META_NS::IMetadata::Ptr customs;
225 if (const auto obj = GetEcsObject()) {
226 BASE_NS::vector<META_NS::IEngineValue::Ptr> values;
227 if (SyncCustomProperties(&values)) {
228 customs = META_NS::GetObjectRegistry().Create<META_NS::IMetadata>(META_NS::ClassId::Object);
229 for (auto&& v : values) {
230 BASE_NS::string pname(SCENE_NS::PropertyName(v->GetName()));
231 if (auto prop = customs->GetProperty(pname)) {
232 SetEngineValueToProperty(prop, v);
233 } else {
234 customs->AddProperty(META_NS::PropertyFromEngineValue(pname, v));
235 }
236 }
237 }
238 }
239 return customs;
240 }
241
GetCustomProperty(BASE_NS::string_view name) const242 META_NS::IProperty::Ptr Material::GetCustomProperty(BASE_NS::string_view name) const
243 {
244 const auto obj = GetEcsObject();
245 if (!obj) {
246 return nullptr;
247 }
248 const auto manager = obj->GetEngineValueManager();
249 if (!manager) {
250 return nullptr;
251 }
252
253 if (!SyncCustomProperties({})) {
254 CORE_LOG_W("Syncing properties to engine values failed. Using old values if available.");
255 }
256 BASE_NS::string fullName { name };
257 if (!name.starts_with("MaterialComponent.customProperties.")) {
258 fullName = "MaterialComponent.customProperties." + fullName;
259 }
260 return META_NS::PropertyFromEngineValue(fullName, manager->GetEngineValue(fullName));
261 }
262
SyncCustomProperties(BASE_NS::vector<META_NS::IEngineValue::Ptr> * synced_values) const263 bool Material::SyncCustomProperties(BASE_NS::vector<META_NS::IEngineValue::Ptr>* synced_values) const
264 {
265 const auto obj = GetEcsObject();
266 if (!material_ || !obj) {
267 return false;
268 }
269 const auto manager = obj->GetEngineValueManager();
270 const auto allCustomProps = META_NS::GetEngineValueFromProperty(material_->CustomProperties());
271 if (!manager || !allCustomProps) {
272 return false;
273 }
274
275 auto doSync = [&]() -> bool {
276 // syncing material shader updates the custom properties
277 obj->GetScene()->SyncProperty(MaterialShader(), META_NS::EngineSyncDirection::AUTO);
278 obj->GetScene()->SyncProperty(material_->CustomProperties(), META_NS::EngineSyncDirection::FROM_ENGINE);
279 return manager->ConstructValues(allCustomProps, { "", synced_values });
280 };
281 return obj->GetScene()->AddTask(doSync).Wait();
282 }
283
284 SCENE_END_NAMESPACE()
285