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 28using CORE3D_NS::MaterialComponent; 29META_BEGIN_NAMESPACE() 30META_TYPE(MaterialComponent::TextureInfo); 31META_END_NAMESPACE() 32 33using SCENE_NS::MakeTask; 34#define GET_HOLDER(info) interface_pointer_cast<SCENE_NS::IProxyObjectHolder>(info)->Holder() 35 36namespace { 37template<typename... Types> 38inline void BindMetaProperty( 39 PropertyHandlerArrayHolder& handler, META_NS::IProperty::Ptr& clone, META_NS::IProperty::Ptr& prop) 40{ 41 (BindIfCorrectType<Types>(handler, clone, prop) || ...); 42} 43 44void 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 53bool 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 82bool IsGltfImage(BASE_NS::string_view uri) 83{ 84 return IsGltfResource(uri, "/images"); 85} 86 87bool IsGltfMaterial(BASE_NS::string_view uri) 88{ 89 return IsGltfResource(uri, "/materials"); 90} 91 92class 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 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 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 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 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 228public: // ISerialization 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 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 */ 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 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 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 500 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1314SCENE_BEGIN_NAMESPACE() 1315 1316void RegisterMaterialImpl() 1317{ 1318 META_NS::GetObjectRegistry().RegisterObjectType<MaterialImpl>(); 1319} 1320void UnregisterMaterialImpl() 1321{ 1322 META_NS::GetObjectRegistry().UnregisterObjectType<MaterialImpl>(); 1323} 1324 1325SCENE_END_NAMESPACE() 1326