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/mesh_uid.h> 16#include <scene_plugin/interface/intf_material.h> 17 18#include <3d/ecs/components/mesh_component.h> 19 20#include <meta/ext/object.h> 21 22#include "intf_node_private.h" 23#include "intf_submesh_bridge.h" 24#include "submesh_handler_uid.h" 25#include "task_utils.h" 26 27using SCENE_NS::MakeTask; 28 29namespace { 30class SubMeshHandler : public META_NS::ObjectFwd<SubMeshHandler, SCENE_NS::ClassId::SubMeshHandler, 31 META_NS::ClassId::Object, SCENE_NS::IEcsProxyObject, SCENE_NS::ISubMeshBridge> { 32public: 33 void Initialize(CORE3D_NS::IMeshComponentManager* componentManager, CORE_NS::Entity entity, 34 META_NS::IProperty::Ptr submeshes, INodeEcsInterfacePrivate::Ptr node) override 35 { 36 node_ = node; 37 if (node) { 38 componentManager_ = componentManager; 39 entity_ = entity; 40 submeshes_ = META_NS::ArrayProperty<SCENE_NS::ISubMesh::Ptr>(submeshes); 41 SetCommonListener(node->SceneHolder()->GetCommonEcsListener()); 42 if (auto scene = node->EcsScene()) { 43 scene->AddEngineTask(MakeTask( 44 [](auto obj) { 45 if (auto self = static_cast<SubMeshHandler*>(obj.get())) { 46 self->DoComponentEvent( 47 CORE_NS::IEcs::ComponentListener::EventType::MODIFIED, 48 *self->componentManager_, self->entity_); 49 } 50 return false; 51 }, 52 GetSelf()), 53 false); 54 } 55 } 56 } 57 58 void Destroy() override 59 { 60 if (listener_) { 61 listener_->RemoveEntity(entity_, GetSelf<SCENE_NS::IEcsProxyObject>(), 62 *static_cast<CORE_NS::IComponentManager*>(componentManager_)); 63 } 64 } 65 66 // IEcsProxyObject 67 68 void SetCommonListener(BASE_NS::shared_ptr<SCENE_NS::EcsListener> listener) override 69 { 70 if (listener_) { 71 listener_->RemoveEntity(entity_, GetSelf<SCENE_NS::IEcsProxyObject>(), 72 *static_cast<CORE_NS::IComponentManager*>(componentManager_)); 73 } 74 listener_ = listener; 75 if (listener_) { 76 listener_->AddEntity(entity_, GetSelf<SCENE_NS::IEcsProxyObject>(), 77 *static_cast<CORE_NS::IComponentManager*>(componentManager_)); 78 } 79 } 80 81 void DoEntityEvent(CORE_NS::IEcs::EntityListener::EventType, const CORE_NS::Entity&) override {} 82 83 SCENE_NS::IMaterial::Ptr GetMaterialFromEntity( 84 SCENE_NS::IScene::Ptr scene, SceneHolder::Ptr sceneHolder, CORE_NS::Entity entity) 85 { 86 // First see if we have this material already. 87 auto materials = scene->GetMaterials(); 88 for (auto material : materials) { 89 auto ecsObject = interface_pointer_cast<SCENE_NS::IEcsObject>(material); 90 if (ecsObject && ecsObject->GetEntity() == entity) { 91 return material; 92 } 93 } 94 95 // Then try to resolve from uri. 96 BASE_NS::string uri; 97 if (sceneHolder->GetEntityUri(entity, uri)) { 98 return scene->GetMaterial(uri); 99 } 100 101 BASE_NS::string name; 102 if (sceneHolder->GetEntityName(entity, name)) { 103 return scene->GetMaterial(name); 104 } 105 106 return {}; 107 } 108 109 // ToDo: This runs on "engine queue", someday the property operations could be decoupled 110 // Another todo, this should presumably be closer to SceneHolder instead interleaved into the node impl 111 void DoComponentEvent(CORE_NS::IEcs::ComponentListener::EventType type, 112 const CORE_NS::IComponentManager& componentManager, const CORE_NS::Entity& entity) override 113 { 114 if (entity != entity_) { 115 CORE_LOG_W("DoComponentEvent called for wrong entity."); 116 return; 117 } 118 119 if (type == CORE_NS::IEcs::ComponentListener::EventType::MODIFIED) { 120 // Read data here, we don't want to keep read lock when we update materials below 121 // as it can cause changes to material components further down the pipeline. 122 auto componentData = componentManager_->Get(entity); 123 124 // This may be nasty from the application point of view, but lessens a risk 125 // that index changes would be propagated incorrectly to client app 126 if (submeshes_->GetSize() > componentData.submeshes.size()) { 127 submeshes_->Reset(); 128 } 129 130 while (submeshes_->GetSize() < componentData.submeshes.size()) { 131 auto submesh = GetObjectRegistry().Create<SCENE_NS::ISubMesh>(SCENE_NS::ClassId::SubMesh); 132 submeshes_->AddValue(submesh); 133 } 134 135 for (auto i = 0; i < componentData.submeshes.size(); ++i) { 136 const auto& submesh = componentData.submeshes.at(i); 137 auto ptr = submeshes_->GetValueAt(i); 138 // TODO: These could be initialized only once. 139 ptr->Name()->SetValue(BASE_NS::string("Submesh ") + BASE_NS::to_string(i)); 140 141 META_NS::SetValue(ptr->AABBMin(), submesh.aabbMin); 142 META_NS::SetValue(ptr->AABBMax(), submesh.aabbMax); 143 META_NS::SetValue(ptr->RenderSortLayerOrder(), submesh.renderSortLayerOrder); 144 META_NS::SetValue(ptr->Handle(), i); 145 146 auto priv = interface_pointer_cast<SCENE_NS::ISubMeshPrivate>(ptr); 147 if (CORE_NS::EntityUtil::IsValid(submesh.material)) { 148 if (auto node = node_.lock()) { 149 // This would appreciate more async approach 150 auto sceneHolder = node->SceneHolder(); 151 auto scene = interface_pointer_cast<SCENE_NS::IScene>(node->EcsScene()); 152 153 if (scene && sceneHolder) { 154 auto material = GetMaterialFromEntity(scene, sceneHolder, submesh.material); 155 priv->SetDefaultMaterial(material); 156 } 157 } 158 } else { 159 priv->SetDefaultMaterial({}); 160 } 161 priv->AddSubmeshBrigde(GetSelf<ISubMeshBridge>()); 162 } 163 } 164 } 165 166public: // ISubMeshBridge 167 void SetRenderSortLayerOrder(size_t index, uint8_t value) override 168 { 169#ifdef SYNC_ACCESS_ECS 170 if (auto handle = componentManager_->Write(entity_)) { 171 if (index >= 0 && index < handle->submeshes.size()) { 172 handle->submeshes[index].renderSortOrder = value; 173 } 174 } 175#else 176 if (auto node = node_.lock()) { 177 if (auto scene = node->EcsScene()) { 178 scene->AddEngineTask(MakeTask( 179 [index, value](auto selfNode) { 180 if (auto self = interface_pointer_cast<SCENE_NS::IEcsObject>(selfNode)) { 181 if (auto node = interface_cast<INodeEcsInterfacePrivate>(selfNode)) { 182 if (auto sceneHolder = node->SceneHolder()) { 183 sceneHolder->SetSubmeshRenderSortOrder( 184 self->GetEntity(), index, value); 185 } 186 } 187 } 188 return false; 189 }, 190 node), 191 false); 192 } 193 } 194#endif 195 if (index < submeshes_->GetSize()) { 196 META_NS::SetValue(submeshes_->GetValueAt(index)->RenderSortLayerOrder(), value); 197 } 198 } 199 200 void SetMaterialToEcs(size_t index, SCENE_NS::IMaterial::Ptr& material) override 201 { 202 if (auto node = node_.lock()) { 203 if (auto scene = node->EcsScene()) { 204 scene->AddEngineTask( 205 MakeTask( 206 [mat = SCENE_NS::IMaterial::WeakPtr(material), index](auto node) { 207 if (auto self = interface_pointer_cast<SCENE_NS::IEcsObject>(node)) { 208 CORE_NS::Entity entity {}; 209 if (auto ecsObject = interface_pointer_cast<SCENE_NS::IEcsObject>(mat.lock())) { 210 entity = ecsObject->GetEntity(); 211 } 212 auto sceneHolder = node->SceneHolder(); 213 if (sceneHolder) { 214 sceneHolder->SetMaterial(self->GetEntity(), entity, index); 215 } 216 } 217 return false; 218 }, 219 node_), 220 false); 221 } 222 } 223 } 224 225 void SetAABBMin(size_t index, BASE_NS::Math::Vec3 vec) override 226 { 227#ifdef SYNC_ACCESS_ECS 228 if (auto handle = componentManager_->Write(entity_)) { 229 if (index >= 0 && index < handle->submeshes.size()) { 230 handle->submeshes[index].aabbMin = vec; 231 handle->aabbMin = BASE_NS::Math::min(handle->aabbMin, vec); 232 } 233 } 234#else 235 if (auto node = node_.lock()) { 236 if (auto scene = node->EcsScene()) { 237 scene->AddEngineTask(MakeTask( 238 [index, vec](auto selfNode) { 239 if (auto self = interface_pointer_cast<SCENE_NS::IEcsObject>(selfNode)) { 240 if (auto node = interface_cast<INodeEcsInterfacePrivate>(selfNode)) { 241 if (auto sceneHolder = node->SceneHolder()) { 242 sceneHolder->SetSubmeshAABBMin(self->GetEntity(), index, vec); 243 } 244 } 245 } 246 return false; 247 }, 248 node), 249 false); 250 } 251 } 252#endif 253 if (index < submeshes_->GetSize()) { 254 META_NS::SetValue(submeshes_->GetValueAt(index)->AABBMin(), vec); 255 } 256 } 257 258 void SetAABBMax(size_t index, BASE_NS::Math::Vec3 vec) override 259 { 260#ifdef SYNC_ACCESS_ECS 261 if (auto handle = componentManager_->Write(entity_)) { 262 if (index >= 0 && index < handle->submeshes.size()) { 263 handle->submeshes[index].aabbMax = vec; 264 handle->aabbMax = BASE_NS::Math::max(handle->aabbMax, vec); 265 } 266 } 267#else 268 if (auto node = node_.lock()) { 269 if (auto scene = node->EcsScene()) { 270 scene->AddEngineTask(MakeTask( 271 [index, vec](auto selfNode) { 272 if (auto self = interface_pointer_cast<SCENE_NS::IEcsObject>(selfNode)) { 273 if (auto node = interface_cast<INodeEcsInterfacePrivate>(selfNode)) { 274 if (auto sceneHolder = node->SceneHolder()) { 275 sceneHolder->SetSubmeshAABBMax(self->GetEntity(), index, vec); 276 } 277 } 278 } 279 return false; 280 }, 281 node), 282 false); 283 } 284 } 285#endif 286 if (index < submeshes_->GetSize()) { 287 META_NS::SetValue(submeshes_->GetValueAt(index)->AABBMax(), vec); 288 } 289 } 290 291 void RemoveSubmesh(int32_t index) override 292 { 293#ifdef SYNC_ACCESS_ECS 294 if (auto handle = componentManager_->Write(entity_)) { 295 if (index < 0) { 296 handle->submeshes.clear(); 297 handle->aabbMin = { 0.f, 0.f, 0.f }; 298 handle->aabbMax = { 0.f, 0.f, 0.f }; 299 } else if (index < handle->submeshes.size()) { 300 handle->submeshes.erase(handle->submeshes.begin() + index); 301 for (const auto& submesh : handle->submeshes) { 302 handle->aabbMin = BASE_NS::Math::min(handle->aabbMin, submesh.aabbMin); 303 handle->aabbMax = BASE_NS::Math::max(handle->aabbMax, submesh.aabbMax); 304 } 305 } 306 } 307#else 308 if (auto node = node_.lock()) { 309 if (auto scene = node->EcsScene()) { 310 scene->AddEngineTask(MakeTask( 311 [index](auto selfNode) { 312 if (auto self = interface_pointer_cast<SCENE_NS::IEcsObject>(selfNode)) { 313 if (auto node = interface_cast<INodeEcsInterfacePrivate>(selfNode)) { 314 if (auto sceneHolder = node->SceneHolder()) { 315 sceneHolder->RemoveSubmesh(self->GetEntity(), index); 316 } 317 } 318 } 319 return false; 320 }, 321 node_), 322 false); 323 } 324 } 325#endif 326 } 327 328 CORE_NS::Entity GetEntity() const override 329 { 330 return entity_; 331 } 332 333 BASE_NS::shared_ptr<SCENE_NS::EcsListener> listener_ {}; 334 CORE3D_NS::IMeshComponentManager* componentManager_ {}; 335 CORE_NS::Entity entity_ {}; 336 META_NS::ArrayProperty<SCENE_NS::ISubMesh::Ptr> submeshes_ {}; 337 INodeEcsInterfacePrivate::WeakPtr node_ {}; 338}; 339} // namespace 340SCENE_BEGIN_NAMESPACE() 341 342void RegisterSubMeshHandler() 343{ 344 META_NS::GetObjectRegistry().RegisterObjectType<SubMeshHandler>(); 345} 346void UnregisterSubMeshHandler() 347{ 348 META_NS::GetObjectRegistry().UnregisterObjectType<SubMeshHandler>(); 349} 350SCENE_END_NAMESPACE()