1/* 2 * Copyright (c) 2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include <algorithm> 17#include <scene_plugin/api/material_uid.h> 18#include <scene_plugin/interface/compatibility.h> 19#include <scene_plugin/interface/intf_bitmap.h> 20 21#include <scene_plugin/interface/intf_scene.h> 22#include <meta/api/engine/util.h> 23#include <meta/api/property/property_event_handler.h> 24 25#include "intf_postprocess_private.h" 26#include "PropertyHandlerArrayHolder.h" 27#include "scene_holder.h" 28 29 30BASE_BEGIN_NAMESPACE() 31 32// our vector does not have op== 33template<typename T> 34bool operator==(const BASE_NS::vector<T>& l, const BASE_NS::vector<T>& r) 35{ 36 if (l.size() != r.size()) { 37 return false; 38 } 39 auto it1 = l.begin(); 40 auto it2 = r.begin(); 41 for (; it1 != l.end(); ++it1, ++it2) { 42 if constexpr (META_NS::HasEqualOperator_v<T>) { 43 if (!(*it1 == *it2)) { 44 return false; 45 } 46 } else { 47 return false; 48 } 49 } 50 return true; 51} 52 53template<typename T> 54bool operator!=(const BASE_NS::vector<T>& l, const BASE_NS::vector<T>& r) 55{ 56 return !(l == r); 57} 58 59BASE_END_NAMESPACE() 60 61bool HasChangedProperties(META_NS::IMetadata& meta); 62 63template<class EcsType, class UiType> 64struct StaticConverter { 65 static inline EcsType ToEcs(SceneHolder& sceneHolder, const UiType& v) 66 { 67 return static_cast<EcsType>(v); 68 } 69 static inline UiType ToUi(SceneHolder& sceneHolder, const EcsType& v) 70 { 71 return static_cast<UiType>(v); 72 } 73}; 74 75struct ImageConverter { 76 static inline CORE_NS::EntityReference ToEcs(SceneHolder& sceneHolder, const SCENE_NS::IBitmap::Ptr& v) 77 { 78 // Get ecs image handle from bitmap. 79 if (v) { 80 if (auto rh = v->GetRenderHandle()) { 81 auto uri = META_NS::GetValue(v->Uri()); 82 return sceneHolder.LoadImage(uri, rh); 83 } else { 84 auto uri = META_NS::GetValue(v->Uri()); 85 if (!uri.empty()) { 86 return sceneHolder.LoadImage(uri); 87 } 88 } 89 } 90 return {}; 91 } 92 93 static inline SCENE_NS::IBitmap::Ptr ToUi(SceneHolder& sceneHolder, const CORE_NS::EntityReference& v) 94 { 95 CORE_NS::Entity image; 96 BASE_NS::string uri; 97 if (sceneHolder.GetEntityUri(v, uri)) { 98 RENDER_NS::GpuImageDesc desc; 99 RENDER_NS::RenderHandleReference rh; 100 auto uriBitmap = META_NS::GetObjectRegistry().Create<SCENE_NS::IBitmap>(SCENE_NS::ClassId::Bitmap); 101 if (sceneHolder.GetImageHandle(v, rh, desc)) { 102 uriBitmap->SetRenderHandle(rh, { desc.width, desc.height }); 103 } 104 META_NS::SetValue(uriBitmap->Uri(), uri); 105 return uriBitmap; 106 } 107 108 return {}; 109 } 110}; 111 112template<class ObjectType> 113struct EntityConverter { 114 static inline CORE_NS::Entity ToEcs(SceneHolder& sceneHolder, const typename ObjectType::Ptr& v) 115 { 116 // Get ecs object to entity. 117 if (auto ecsObject = interface_cast<SCENE_NS::IEcsObject>(v)) { 118 return ecsObject->GetEntity(); 119 } 120 if (auto ippp = interface_cast<IPostProcessPrivate>(v)) { 121 return ippp->GetEntity(); 122 } 123 return {}; 124 } 125 126 static inline typename ObjectType::Ptr ToUi(SceneHolder& sceneHolder, const CORE_NS::Entity& v) 127 { 128 return {}; 129 } 130}; 131 132template<class originalType> 133inline auto& GetValueRef(originalType& from, size_t slot) 134{ 135 return from[slot]; 136} 137 138template<> 139inline auto& GetValueRef<SCENE_NS::Color>(SCENE_NS::Color& from, size_t slot) 140{ 141 if (slot == 0) { 142 return from.x; 143 } else if (slot == 1) { 144 return from.y; 145 } else if (slot == 2) { // 2 slot index 146 return from.z; 147 } 148 return from.w; 149} 150 151template<class originalType, class valueType> 152inline bool SetValueFromComponent(META_NS::Property<valueType> to, 153 const META_NS::Property<originalType>& from, size_t slot, bool setAsDefault = false) 154{ 155 originalType fromValue = from->GetValue(); 156 157 if (to->GetValue() != fromValue[slot]) { 158 if (setAsDefault) { 159 to->SetDefaultValue(fromValue[slot], true); 160 } else { 161 to->SetValue(fromValue[slot]); 162 } 163 return true; 164 } 165 return false; 166} 167 168template<> 169inline bool SetValueFromComponent<SCENE_NS::Color, float>(META_NS::Property<float> to, 170 const META_NS::Property<SCENE_NS::Color>& from, size_t slot, bool setAsDefault) 171{ 172 SCENE_NS::Color fromValue = from->GetValue(); 173 174 if (slot == 0) { 175 if (to->GetValue() != fromValue.x) { 176 if (setAsDefault) { 177 to->SetDefaultValue(fromValue.x, true); 178 } else { 179 to->SetValue(fromValue.x); 180 } 181 } 182 } else if (slot == 1) { 183 if (to->GetValue() != fromValue.y) { 184 if (setAsDefault) { 185 to->SetDefaultValue(fromValue.y, true); 186 } else { 187 to->SetValue(fromValue.y); 188 } 189 } 190 } else if (slot == 2) { 191 if (to->GetValue() != fromValue.z) { 192 if (setAsDefault) { 193 to->SetDefaultValue(fromValue.z, true); 194 } else { 195 to->SetValue(fromValue.z); 196 } 197 } 198 } else if (slot == 3) { 199 if (to->GetValue() != fromValue.w) { 200 if (setAsDefault) { 201 to->SetDefaultValue(fromValue.w, true); 202 } else { 203 to->SetValue(fromValue.w); 204 } 205 } 206 } else { 207 return false; 208 } 209 return true; 210} 211 212// Bending the implementation a bit, implicit conversion from TimeSpan to Seconds in float 213template<> 214inline bool SetValueFromComponent<float, META_NS::TimeSpan>(META_NS::Property<META_NS::TimeSpan> to, 215 const META_NS::Property<float>& from, size_t /*slot*/, bool setAsDefault) 216{ 217 META_NS::TimeSpan value; 218 value.SetSeconds(from->GetValue()); 219 if (setAsDefault) { 220 to->SetDefaultValue(value, true); 221 } else { 222 to->SetValue(value); 223 } 224 return true; 225} 226 227template<class originalType, class valueType> 228inline bool SetValueToComponent(META_NS::Property<originalType> to, 229 const META_NS::Property<valueType>& from, size_t slot) 230{ 231 valueType fromValue = from->GetValue(); 232 originalType toValue = to->GetValue(); 233 234 if (toValue[slot] != fromValue) { 235 toValue[slot] = fromValue; 236 to->SetValue(toValue); 237 return true; 238 } 239 return false; 240} 241 242// Bending the implementation a bit, implicit conversion from TimeSpan to Seconds in float 243template<> 244inline bool SetValueToComponent<float, META_NS::TimeSpan>(META_NS::Property<float> to, 245 const META_NS::Property<META_NS::TimeSpan>& from, size_t /*slot*/) 246{ 247 to->SetValue(from->GetValue().ToSecondsFloat()); 248 return true; 249} 250 251template<> 252inline bool SetValueToComponent<SCENE_NS::Color, float>(META_NS::Property<SCENE_NS::Color> to, 253 const META_NS::Property<float>& from, size_t slot) 254{ 255 float fromValue = from->GetValue(); 256 SCENE_NS::Color toValue = to->GetValue(); 257 258 if (slot == 0) { 259 if (toValue.x != fromValue) { 260 toValue.x = fromValue; 261 } 262 } else if (slot == 1) { 263 if (toValue.y != fromValue) { 264 toValue.y = fromValue; 265 } 266 } else if (slot == 2) { // 2 slot index 267 if (toValue.z != fromValue) { 268 toValue.z = fromValue; 269 } 270 } else if (slot == 3) { // 3 slot index 271 if (toValue.w != fromValue) { 272 toValue.w = fromValue; 273 } 274 } else { 275 return false; 276 } 277 278 to->SetValue(toValue); 279 return true; 280} 281 282enum PropertyBindingType { ONE_WAY_TO_ECS, ONE_WAY_TO_UI, TWO_WAY }; 283 284template<class valueType, class sourceType, class Conv = StaticConverter<sourceType, valueType>> 285inline bool BindPropChanges(PropertyHandlerArrayHolder& handler, 286 META_NS::Property<valueType> propertyInstance, 287 META_NS::Property<sourceType> prop, PropertyBindingType type = TWO_WAY) 288{ 289 if (!prop || !propertyInstance) { 290 return false; 291 } 292 293 auto sceneHolder = handler.GetSceneHolder(); 294 if (!sceneHolder) { 295 return false; 296 } 297 298 bool useEcsPropertyValue = handler.GetUseEcsDefaults(); 299 if (propertyInstance->IsValueSet()) { 300 useEcsPropertyValue = false; 301 } 302 303 // If we don't have local value, initialize value from ECS> 304 if (useEcsPropertyValue) { 305 auto ecsValue = Conv::ToUi(*sceneHolder, prop->GetValue()); 306 // try to keep our value intact if the engine default value is the same as ours, otherwise we should 307 // update our default 308 if (ecsValue != propertyInstance->GetValue()) { 309 // Set to default value. 310 propertyInstance->SetDefaultValue(ecsValue, true); 311 } 312 } else { // otherwise reflect our value to ecs directly 313 prop->SetValue(Conv::ToEcs(*sceneHolder, propertyInstance->GetValue())); 314 } 315 if (type == ONE_WAY_TO_ECS || type == TWO_WAY) { 316 // and reflect the changes from us to ecs from now on 317 handler.NewHandler(prop, propertyInstance) 318 .Subscribe(propertyInstance, META_NS::MakeCallback<META_NS::IOnChanged>( 319 [propertyInstance, sh = sceneHolder](const auto& prop) { 320 if (prop) { 321 prop->SetValue(Conv::ToEcs(*sh, propertyInstance->GetValue())); 322 //hack 323 /*if (auto v = META_NS::GetEngineValueFromProperty(prop.GetProperty())) { 324 v->Sync(META_NS::EngineSyncDirection::TO_ENGINE); 325 }*/ 326 } 327 }, 328 prop)); 329 } 330 331 if (type == ONE_WAY_TO_UI || type == TWO_WAY) { 332 // EcsObject reports changes back only if they take place on ecs side, so this should not cause race 333 // They anyway don't support bindings properly, so having the feedback channel this way 334 handler.NewHandler(nullptr, nullptr) 335 .Subscribe(prop, META_NS::MakeCallback<META_NS::IOnChanged>( 336 [propertyInstance, sh = sceneHolder](const auto& prop) { 337 if (prop) { 338 if (!propertyInstance->GetBind()) { 339 propertyInstance->SetValue(Conv::ToUi(*sh, prop->GetValue())); 340 } 341 } 342 }, 343 prop)); 344 } 345 346 return true; 347} 348 349template<class valueType, class sourceType, class Conv = StaticConverter<BASE_NS::vector<sourceType>, BASE_NS::vector<valueType>>> 350inline bool BindPropChanges(PropertyHandlerArrayHolder& handler, META_NS::ArrayProperty<valueType> propertyInstance, 351 META_NS::ArrayProperty<sourceType> prop, PropertyBindingType type = TWO_WAY) 352{ 353 if (!prop || !propertyInstance) { 354 return false; 355 } 356 357 auto sceneHolder = handler.GetSceneHolder(); 358 if (!sceneHolder) { 359 return false; 360 } 361 362 bool useEcsPropertyValue = handler.GetUseEcsDefaults(); 363 if (propertyInstance->IsValueSet()) { 364 useEcsPropertyValue = false; 365 } 366 367 // If we don't have local value, initialize value from ECS> 368 if (useEcsPropertyValue) { 369 auto ecsValue = Conv::ToUi(*sceneHolder, prop->GetValue()); 370 // try to keep our value intact if the engine default value is the same as ours, otherwise we should 371 // update our default 372 if (ecsValue != propertyInstance->GetValue()) { 373 // Set to default value. 374 propertyInstance->SetDefaultValue(ecsValue, true); 375 } 376 } else { // otherwise reflect our value to ecs directly 377 prop->SetValue(Conv::ToEcs(*sceneHolder, propertyInstance->GetValue())); 378 } 379 if (type == ONE_WAY_TO_ECS || type == TWO_WAY) { 380 // and reflect the changes from us to ecs from now on 381 handler.NewHandler(prop, propertyInstance) 382 .Subscribe( 383 propertyInstance, META_NS::MakeCallback<META_NS::IOnChanged>( 384 [propertyInstance, sh = sceneHolder](const auto& prop) { 385 if (prop) { 386 prop->SetValue(Conv::ToEcs(*sh, propertyInstance->GetValue())); 387 // hack 388 } 389 }, 390 prop)); 391 } 392 393 if (type == ONE_WAY_TO_UI || type == TWO_WAY) { 394 // EcsObject reports changes back only if they take place on ecs side, so this should not cause race 395 // They anyway don't support bindings properly, so having the feedback channel this way 396 handler.NewHandler(nullptr, nullptr) 397 .Subscribe(prop, META_NS::MakeCallback<META_NS::IOnChanged>( 398 [propertyInstance, sh = sceneHolder](const auto& prop) { 399 if (prop) { 400 if (!propertyInstance->GetBind()) { 401 propertyInstance->SetValue(Conv::ToUi(*sh, prop->GetValue())); 402 } 403 } 404 }, 405 prop)); 406 } 407 408 return true; 409} 410 411template<class valueType> 412inline bool BindChanges(PropertyHandlerArrayHolder& handler, 413 META_NS::Property<valueType> propertyInstance, META_NS::IMetadata::Ptr meta, 414 const BASE_NS::string_view name) 415{ 416 if (!meta || !propertyInstance) { 417 return false; 418 } 419 420 if (auto prop = meta->GetPropertyByName<valueType>(name)) { 421 // check if the target is already a proxy 422 if (auto target = handler.GetTarget(prop)) { 423 // this could go wrong if the target and proxy are different, mapped types 424 if (META_NS::Property<valueType> typed { target }) { 425 prop = typed; 426 } else { 427 CORE_LOG_W("%s: could not match the types for %s", __func__, BASE_NS::string(name).c_str()); 428 } 429 } 430 return BindPropChanges<valueType>(handler, propertyInstance, prop); 431 } 432 433 SCENE_PLUGIN_VERBOSE_LOG("%s: could not find '%s'", __func__, name.data()); 434 return false; 435} 436 437template<class valueType> 438inline bool BindChanges(PropertyHandlerArrayHolder& handler, META_NS::ArrayProperty<valueType> propertyInstance, 439 META_NS::IMetadata::Ptr meta, const BASE_NS::string_view name) 440{ 441 if (!meta || !propertyInstance) { 442 return false; 443 } 444 445 if (auto prop = meta->GetArrayPropertyByName<valueType>(name)) { 446 // check if the target is already a proxy 447 if (auto target = handler.GetTarget(prop)) { 448 // this could go wrong if the target and proxy are different, mapped types 449 if (META_NS::ArrayProperty<valueType> typed { target }) { 450 prop = typed; 451 } else { 452 CORE_LOG_W("%s: could not match the types for %s", __func__, BASE_NS::string(name).c_str()); 453 } 454 } 455 return BindPropChanges<valueType>(handler, propertyInstance, prop); 456 } 457 458 SCENE_PLUGIN_VERBOSE_LOG("%s: could not find '%s'", __func__, name.data()); 459 return false; 460} 461 462template<class originalType, class valueType> 463inline bool setComponentValues(META_NS::Property<originalType> to, 464 const META_NS::Property<valueType>& from, BASE_NS::vector<size_t> slots, 465 bool setAsDefault = false) 466{ 467 bool changed { false }; 468 valueType fromValue = from->GetValue(); 469 originalType toValue = to->GetValue(); 470 471 for (size_t ix = 1; ix < slots.size();) { 472 auto slot1 = slots[ix - 1]; 473 auto slot2 = slots[ix]; 474 auto& value = GetValueRef<originalType>(toValue, slot1); 475 auto& value2 = GetValueRef<valueType>(fromValue, slot2); 476 if (value != value2) { 477 if (value != value && value2 != value2) { 478 CORE_LOG_D("omitting changes on NaN"); 479 } else { 480 changed = true; 481 value = value2; 482 } 483 } 484 ix += 2; // 2 index shift 485 } 486 if (changed) { 487 if (setAsDefault) { 488 to->SetDefaultValue(toValue, true); 489 } else { 490 to->SetValue(toValue); 491 } 492 } 493 return changed; 494} 495 496template<class originalType, class valueType> 497inline bool BindSlottedChanges(PropertyHandlerArrayHolder& handler, 498 META_NS::Property<valueType> propertyInstance, META_NS::IMetadata::Ptr meta, 499 const BASE_NS::string_view name, BASE_NS::vector<size_t> slots) 500{ 501 if (!meta || !propertyInstance) { 502 return false; 503 } 504 505 if (auto prop = meta->GetPropertyByName<originalType>(name)) { 506 // If we don't have local value, initialize value from ECS> 507 bool useEcsPropertyValue = handler.GetUseEcsDefaults(); 508 if (propertyInstance->IsValueSet()) { 509 useEcsPropertyValue = false; 510 } 511 512 // If we don't have local value, initialize value from ECS 513 if (useEcsPropertyValue) { 514 // Set default value. 515 setComponentValues(propertyInstance, prop, slots, true); 516 } else { // otherwise reflect our value to ecs directly 517 setComponentValues(prop, propertyInstance, slots); 518 } 519 // and reflect the changes from us to ecs from now on 520 handler.NewHandler(prop, propertyInstance) 521 .Subscribe(propertyInstance, META_NS::MakeCallback<META_NS::IOnChanged>( 522 [propertyInstance, slots](const auto& prop) { 523 if (prop) { 524 setComponentValues(prop, propertyInstance, slots); 525 } 526 }, 527 prop)); 528 handler.NewHandler(nullptr, nullptr) 529 .Subscribe(prop, META_NS::MakeCallback<META_NS::IOnChanged>( 530 [propertyInstance, slots](const auto& prop) { 531 if (prop) { 532 setComponentValues(propertyInstance, prop, slots); 533 } 534 }, 535 prop)); 536 537 return true; 538 } 539 CORE_LOG_W("%s: could not find '%s'", __func__, name.data()); 540 return false; 541} 542 543template<class valueType, class sourceType, class Conv = StaticConverter<sourceType, valueType>> 544inline bool ConvertBindChanges(PropertyHandlerArrayHolder& handler, 545 META_NS::Property<valueType> propertyInstance, META_NS::IMetadata::Ptr meta, 546 const BASE_NS::string_view name, PropertyBindingType type = TWO_WAY) 547{ 548 if (!meta || !propertyInstance) { 549 return false; 550 } 551 552 if (auto prop = meta->GetPropertyByName<sourceType>(name)) { 553 return BindPropChanges<valueType, sourceType, Conv>(handler, propertyInstance, prop, type); 554 } 555 556 SCENE_PLUGIN_VERBOSE_LOG("%s: could not find '%s'", __func__, name.data()); 557 return false; 558} 559 560 561template<class valueType> 562inline bool BindIfCorrectType( 563 PropertyHandlerArrayHolder& handler, META_NS::IProperty::Ptr& clone, META_NS::IProperty::Ptr& prop) 564{ 565 if (clone->IsCompatible(META_NS::UidFromType<valueType>())) { 566 auto typed = META_NS::Property<valueType>(clone); 567 BindPropChanges<valueType>(handler, typed, META_NS::Property<valueType>(prop)); 568 return true; 569 } 570 571 return false; 572} 573 574template<class originalType, class valueType> 575inline bool BindChanges(PropertyHandlerArrayHolder& handler, 576 META_NS::Property<valueType> propertyInstance, META_NS::IMetadata::Ptr meta, 577 const BASE_NS::string_view name, size_t slot) 578{ 579 if (!meta || !propertyInstance) { 580 return false; 581 } 582 583 if (auto prop = meta->GetPropertyByName<originalType>(name)) { 584 // check if the target is already a proxy 585 if (auto target = handler.GetTarget(prop)) { 586 // this could go wrong if the target and proxy are different, mapped types 587 if (auto typed = META_NS::Property<originalType>(target)) { 588 prop = typed; 589 } else { 590 CORE_LOG_W("%s: could not match the types for %s", __func__, BASE_NS::string(name).c_str()); 591 } 592 } 593 594 bool useEcsPropertyValue = handler.GetUseEcsDefaults(); 595 if (propertyInstance->IsValueSet()) { 596 useEcsPropertyValue = false; 597 } 598 599 // If we don't have local value, initialize value from ECS 600 if (useEcsPropertyValue) { 601 // Set to default value 602 SetValueFromComponent(propertyInstance, prop, slot, true); 603 } else { // otherwise reflect our value to ecs directly 604 SetValueToComponent(prop, propertyInstance, slot); 605 } 606 // and reflect the changes from us to ecs from now on 607 handler.NewHandler(prop, propertyInstance) 608 .Subscribe(propertyInstance, META_NS::MakeCallback<META_NS::IOnChanged>( 609 [propertyInstance, slot](const auto& prop) { 610 if (prop) { 611 SetValueToComponent(prop, propertyInstance, slot); 612 } 613 }, 614 prop)); 615 handler.NewHandler(nullptr, nullptr) 616 .Subscribe(prop, META_NS::MakeCallback<META_NS::IOnChanged>( 617 [propertyInstance, slot](const auto& prop) { 618 if (prop) { 619 SetValueFromComponent(propertyInstance, prop, slot); 620 } 621 }, 622 prop)); 623 624 return true; 625 } 626 CORE_LOG_W("%s: could not find '%s'", __func__, name.data()); 627 return false; 628} 629