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
27 using SCENE_NS::MakeTask;
28
29 namespace {
30 class SubMeshHandler : public META_NS::ObjectFwd<SubMeshHandler, SCENE_NS::ClassId::SubMeshHandler,
31 META_NS::ClassId::Object, SCENE_NS::IEcsProxyObject, SCENE_NS::ISubMeshBridge> {
32 public:
Initialize(CORE3D_NS::IMeshComponentManager * componentManager,CORE_NS::Entity entity,META_NS::IProperty::Ptr submeshes,INodeEcsInterfacePrivate::Ptr node)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
Destroy()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
SetCommonListener(BASE_NS::shared_ptr<SCENE_NS::EcsListener> listener)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
DoEntityEvent(CORE_NS::IEcs::EntityListener::EventType,const CORE_NS::Entity &)81 void DoEntityEvent(CORE_NS::IEcs::EntityListener::EventType, const CORE_NS::Entity&) override {}
82
GetMaterialFromEntity(SCENE_NS::IScene::Ptr scene,SceneHolder::Ptr sceneHolder,CORE_NS::Entity entity)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
DoComponentEvent(CORE_NS::IEcs::ComponentListener::EventType type,const CORE_NS::IComponentManager & componentManager,const CORE_NS::Entity & entity)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
166 public: // ISubMeshBridge
SetRenderSortLayerOrder(size_t index,uint8_t value)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
SetMaterialToEcs(size_t index,SCENE_NS::IMaterial::Ptr & material)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
SetAABBMin(size_t index,BASE_NS::Math::Vec3 vec)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
SetAABBMax(size_t index,BASE_NS::Math::Vec3 vec)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
RemoveSubmesh(int32_t index)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
GetEntity() const328 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
SCENE_BEGIN_NAMESPACE()340 SCENE_BEGIN_NAMESPACE()
341
342 void RegisterSubMeshHandler()
343 {
344 META_NS::GetObjectRegistry().RegisterObjectType<SubMeshHandler>();
345 }
UnregisterSubMeshHandler()346 void UnregisterSubMeshHandler()
347 {
348 META_NS::GetObjectRegistry().UnregisterObjectType<SubMeshHandler>();
349 }
350 SCENE_END_NAMESPACE()