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