1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #include "Scene/BsSceneObject.h" |
4 | #include "Scene/BsComponent.h" |
5 | #include "Scene/BsSceneManager.h" |
6 | #include "Error/BsException.h" |
7 | #include "Debug/BsDebug.h" |
8 | #include "Private/RTTI/BsSceneObjectRTTI.h" |
9 | #include "Serialization/BsMemorySerializer.h" |
10 | #include "Scene/BsGameObjectManager.h" |
11 | #include "Scene/BsPrefabUtility.h" |
12 | #include "Math/BsMatrix3.h" |
13 | #include "BsCoreApplication.h" |
14 | |
15 | namespace bs |
16 | { |
17 | SceneObject::SceneObject(const String& name, UINT32 flags) |
18 | : GameObject(), mFlags(flags) |
19 | { |
20 | setName(name); |
21 | } |
22 | |
23 | SceneObject::~SceneObject() |
24 | { |
25 | if(!mThisHandle.isDestroyed()) |
26 | { |
27 | LOGWRN("Object is being deleted without being destroyed first? " + mName); |
28 | destroyInternal(mThisHandle, true); |
29 | } |
30 | } |
31 | |
32 | HSceneObject SceneObject::create(const String& name, UINT32 flags) |
33 | { |
34 | HSceneObject newObject = createInternal(name, flags); |
35 | |
36 | if (newObject->isInstantiated()) |
37 | gSceneManager().registerNewSO(newObject); |
38 | |
39 | return newObject; |
40 | } |
41 | |
42 | HSceneObject SceneObject::createInternal(const String& name, UINT32 flags) |
43 | { |
44 | SPtr<SceneObject> sceneObjectPtr = SPtr<SceneObject>(new (bs_alloc<SceneObject>()) SceneObject(name, flags), |
45 | &bs_delete<SceneObject>, StdAlloc<SceneObject>()); |
46 | sceneObjectPtr->mUUID = UUIDGenerator::generateRandom(); |
47 | |
48 | HSceneObject sceneObject = static_object_cast<SceneObject>( |
49 | GameObjectManager::instance().registerObject(sceneObjectPtr)); |
50 | sceneObject->mThisHandle = sceneObject; |
51 | |
52 | return sceneObject; |
53 | } |
54 | |
55 | HSceneObject SceneObject::createInternal(const SPtr<SceneObject>& soPtr) |
56 | { |
57 | HSceneObject sceneObject = static_object_cast<SceneObject>( |
58 | GameObjectManager::instance().registerObject(soPtr)); |
59 | sceneObject->mThisHandle = sceneObject; |
60 | |
61 | return sceneObject; |
62 | } |
63 | |
64 | void SceneObject::destroy(bool immediate) |
65 | { |
66 | // Parent is our owner, so when his reference to us is removed, delete might be called. |
67 | // So make sure this is the last thing we do. |
68 | if(mParent != nullptr) |
69 | { |
70 | if(!mParent.isDestroyed()) |
71 | mParent->removeChild(mThisHandle); |
72 | |
73 | mParent = nullptr; |
74 | } |
75 | |
76 | destroyInternal(mThisHandle, immediate); |
77 | } |
78 | |
79 | void SceneObject::destroyInternal(GameObjectHandleBase& handle, bool immediate) |
80 | { |
81 | if (immediate) |
82 | { |
83 | for (auto iter = mChildren.begin(); iter != mChildren.end(); ++iter) |
84 | (*iter)->destroyInternal(*iter, true); |
85 | |
86 | mChildren.clear(); |
87 | |
88 | // It's important to remove the elements from the array as soon as they're destroyed, as OnDestroy callbacks |
89 | // for components might query the SO's components, and we want to only return live ones |
90 | while (!mComponents.empty()) |
91 | { |
92 | HComponent component = mComponents.back(); |
93 | component->_setIsDestroyed(); |
94 | |
95 | if (isInstantiated()) |
96 | gSceneManager()._notifyComponentDestroyed(component, immediate); |
97 | |
98 | component->destroyInternal(component, true); |
99 | mComponents.erase(mComponents.end() - 1); |
100 | } |
101 | |
102 | GameObjectManager::instance().unregisterObject(handle); |
103 | } |
104 | else |
105 | GameObjectManager::instance().queueForDestroy(handle); |
106 | } |
107 | |
108 | void SceneObject::_setInstanceData(GameObjectInstanceDataPtr& other) |
109 | { |
110 | GameObject::_setInstanceData(other); |
111 | |
112 | // Instance data changed, so make sure to refresh the handles to reflect that |
113 | SPtr<SceneObject> thisPtr = mThisHandle.getInternalPtr(); |
114 | mThisHandle._setHandleData(thisPtr); |
115 | } |
116 | |
117 | UUID SceneObject::getPrefabLink(bool onlyDirect) const |
118 | { |
119 | const SceneObject* curObj = this; |
120 | |
121 | while (curObj != nullptr) |
122 | { |
123 | if (!curObj->mPrefabLinkUUID.empty()) |
124 | return curObj->mPrefabLinkUUID; |
125 | |
126 | if (curObj->mParent != nullptr && !onlyDirect) |
127 | curObj = curObj->mParent.get(); |
128 | else |
129 | curObj = nullptr; |
130 | } |
131 | |
132 | return UUID::EMPTY; |
133 | } |
134 | |
135 | HSceneObject SceneObject::getPrefabParent() const |
136 | { |
137 | HSceneObject curObj = mThisHandle; |
138 | |
139 | while (curObj != nullptr) |
140 | { |
141 | if (!curObj->mPrefabLinkUUID.empty()) |
142 | return curObj; |
143 | |
144 | if (curObj->mParent != nullptr) |
145 | curObj = curObj->mParent; |
146 | else |
147 | curObj = nullptr; |
148 | } |
149 | |
150 | return curObj; |
151 | } |
152 | |
153 | void SceneObject::breakPrefabLink() |
154 | { |
155 | SceneObject* rootObj = this; |
156 | |
157 | while (rootObj != nullptr) |
158 | { |
159 | if (!rootObj->mPrefabLinkUUID.empty()) |
160 | break; |
161 | |
162 | if (rootObj->mParent != nullptr) |
163 | rootObj = rootObj->mParent.get(); |
164 | else |
165 | rootObj = nullptr; |
166 | } |
167 | |
168 | if (rootObj != nullptr) |
169 | { |
170 | rootObj->mPrefabLinkUUID = UUID::EMPTY; |
171 | rootObj->mPrefabDiff = nullptr; |
172 | PrefabUtility::clearPrefabIds(rootObj->getHandle(), true, false); |
173 | } |
174 | } |
175 | |
176 | bool SceneObject::hasFlag(UINT32 flag) const |
177 | { |
178 | return (mFlags & flag) != 0; |
179 | } |
180 | |
181 | void SceneObject::_setFlags(UINT32 flags) |
182 | { |
183 | mFlags |= flags; |
184 | |
185 | for (auto& child : mChildren) |
186 | child->_setFlags(flags); |
187 | } |
188 | |
189 | void SceneObject::_unsetFlags(UINT32 flags) |
190 | { |
191 | mFlags &= ~flags; |
192 | |
193 | for (auto& child : mChildren) |
194 | child->_unsetFlags(flags); |
195 | } |
196 | |
197 | void SceneObject::_instantiate(bool prefabOnly) |
198 | { |
199 | std::function<void(SceneObject*)> instantiateRecursive = [&](SceneObject* obj) |
200 | { |
201 | obj->mFlags &= ~SOF_DontInstantiate; |
202 | |
203 | if (obj->mParent == nullptr) |
204 | gSceneManager().registerNewSO(obj->mThisHandle); |
205 | |
206 | for (auto& component : obj->mComponents) |
207 | component->_instantiate(); |
208 | |
209 | for (auto& child : obj->mChildren) |
210 | { |
211 | if(!prefabOnly || child->mPrefabLinkUUID.empty()) |
212 | instantiateRecursive(child.get()); |
213 | } |
214 | }; |
215 | |
216 | std::function<void(SceneObject*)> triggerEventsRecursive = [&](SceneObject* obj) |
217 | { |
218 | for (auto& component : obj->mComponents) |
219 | gSceneManager()._notifyComponentCreated(component, obj->getActive()); |
220 | |
221 | for (auto& child : obj->mChildren) |
222 | { |
223 | if (!prefabOnly || child->mPrefabLinkUUID.empty()) |
224 | triggerEventsRecursive(child.get()); |
225 | } |
226 | }; |
227 | |
228 | instantiateRecursive(this); |
229 | triggerEventsRecursive(this); |
230 | } |
231 | |
232 | /************************************************************************/ |
233 | /* Transform */ |
234 | /************************************************************************/ |
235 | |
236 | void SceneObject::setPosition(const Vector3& position) |
237 | { |
238 | if (mMobility == ObjectMobility::Movable) |
239 | { |
240 | mLocalTfrm.setPosition(position); |
241 | notifyTransformChanged(TCF_Transform); |
242 | } |
243 | } |
244 | |
245 | void SceneObject::setRotation(const Quaternion& rotation) |
246 | { |
247 | if (mMobility == ObjectMobility::Movable) |
248 | { |
249 | mLocalTfrm.setRotation(rotation); |
250 | notifyTransformChanged(TCF_Transform); |
251 | } |
252 | } |
253 | |
254 | void SceneObject::setScale(const Vector3& scale) |
255 | { |
256 | if (mMobility == ObjectMobility::Movable) |
257 | { |
258 | mLocalTfrm.setScale(scale); |
259 | notifyTransformChanged(TCF_Transform); |
260 | } |
261 | } |
262 | |
263 | void SceneObject::setWorldPosition(const Vector3& position) |
264 | { |
265 | if(mMobility != ObjectMobility::Movable) |
266 | return; |
267 | |
268 | if (mParent != nullptr) |
269 | mLocalTfrm.setWorldPosition(position, mParent->getTransform()); |
270 | else |
271 | mLocalTfrm.setPosition(position); |
272 | |
273 | notifyTransformChanged(TCF_Transform); |
274 | } |
275 | |
276 | void SceneObject::setWorldRotation(const Quaternion& rotation) |
277 | { |
278 | if (mMobility != ObjectMobility::Movable) |
279 | return; |
280 | |
281 | if (mParent != nullptr) |
282 | mLocalTfrm.setWorldRotation(rotation, mParent->getTransform()); |
283 | else |
284 | mLocalTfrm.setRotation(rotation); |
285 | |
286 | notifyTransformChanged(TCF_Transform); |
287 | } |
288 | |
289 | void SceneObject::setWorldScale(const Vector3& scale) |
290 | { |
291 | if (mMobility != ObjectMobility::Movable) |
292 | return; |
293 | |
294 | if (mParent != nullptr) |
295 | mLocalTfrm.setWorldScale(scale, mParent->getTransform()); |
296 | else |
297 | mLocalTfrm.setScale(scale); |
298 | |
299 | notifyTransformChanged(TCF_Transform); |
300 | } |
301 | |
302 | const Transform& SceneObject::getTransform() const |
303 | { |
304 | if (!isCachedWorldTfrmUpToDate()) |
305 | updateWorldTfrm(); |
306 | |
307 | return mWorldTfrm; |
308 | } |
309 | |
310 | void SceneObject::lookAt(const Vector3& location, const Vector3& up) |
311 | { |
312 | const Transform& worldTfrm = getTransform(); |
313 | |
314 | Vector3 forward = location - worldTfrm.getPosition(); |
315 | |
316 | Quaternion rotation = worldTfrm.getRotation(); |
317 | rotation.lookRotation(forward, up); |
318 | setWorldRotation(rotation); |
319 | } |
320 | |
321 | const Matrix4& SceneObject::getWorldMatrix() const |
322 | { |
323 | if (!isCachedWorldTfrmUpToDate()) |
324 | updateWorldTfrm(); |
325 | |
326 | return mCachedWorldTfrm; |
327 | } |
328 | |
329 | Matrix4 SceneObject::getInvWorldMatrix() const |
330 | { |
331 | if (!isCachedWorldTfrmUpToDate()) |
332 | updateWorldTfrm(); |
333 | |
334 | Matrix4 worldToLocal = mWorldTfrm.getInvMatrix(); |
335 | return worldToLocal; |
336 | } |
337 | |
338 | const Matrix4& SceneObject::getLocalMatrix() const |
339 | { |
340 | if (!isCachedLocalTfrmUpToDate()) |
341 | updateLocalTfrm(); |
342 | |
343 | return mCachedLocalTfrm; |
344 | } |
345 | |
346 | void SceneObject::move(const Vector3& vec) |
347 | { |
348 | if (mMobility == ObjectMobility::Movable) |
349 | { |
350 | mLocalTfrm.move(vec); |
351 | notifyTransformChanged(TCF_Transform); |
352 | } |
353 | } |
354 | |
355 | void SceneObject::moveRelative(const Vector3& vec) |
356 | { |
357 | if (mMobility == ObjectMobility::Movable) |
358 | { |
359 | mLocalTfrm.moveRelative(vec); |
360 | notifyTransformChanged(TCF_Transform); |
361 | } |
362 | } |
363 | |
364 | void SceneObject::rotate(const Vector3& axis, const Radian& angle) |
365 | { |
366 | if (mMobility == ObjectMobility::Movable) |
367 | { |
368 | mLocalTfrm.rotate(axis, angle); |
369 | notifyTransformChanged(TCF_Transform); |
370 | } |
371 | } |
372 | |
373 | void SceneObject::rotate(const Quaternion& q) |
374 | { |
375 | if (mMobility == ObjectMobility::Movable) |
376 | { |
377 | mLocalTfrm.rotate(q); |
378 | notifyTransformChanged(TCF_Transform); |
379 | } |
380 | } |
381 | |
382 | void SceneObject::roll(const Radian& angle) |
383 | { |
384 | if (mMobility == ObjectMobility::Movable) |
385 | { |
386 | mLocalTfrm.roll(angle); |
387 | notifyTransformChanged(TCF_Transform); |
388 | } |
389 | } |
390 | |
391 | void SceneObject::yaw(const Radian& angle) |
392 | { |
393 | if (mMobility == ObjectMobility::Movable) |
394 | { |
395 | mLocalTfrm.yaw(angle); |
396 | notifyTransformChanged(TCF_Transform); |
397 | } |
398 | } |
399 | |
400 | void SceneObject::pitch(const Radian& angle) |
401 | { |
402 | if (mMobility == ObjectMobility::Movable) |
403 | { |
404 | mLocalTfrm.pitch(angle); |
405 | notifyTransformChanged(TCF_Transform); |
406 | } |
407 | } |
408 | |
409 | void SceneObject::setForward(const Vector3& forwardDir) |
410 | { |
411 | const Transform& worldTfrm = getTransform(); |
412 | |
413 | Quaternion currentRotation = worldTfrm.getRotation(); |
414 | currentRotation.lookRotation(forwardDir); |
415 | setWorldRotation(currentRotation); |
416 | } |
417 | |
418 | void SceneObject::updateTransformsIfDirty() |
419 | { |
420 | if (!isCachedLocalTfrmUpToDate()) |
421 | updateLocalTfrm(); |
422 | |
423 | if (!isCachedWorldTfrmUpToDate()) |
424 | updateWorldTfrm(); |
425 | } |
426 | |
427 | void SceneObject::notifyTransformChanged(TransformChangedFlags flags) const |
428 | { |
429 | // If object is immovable, don't send transform changed events nor mark the transform dirty |
430 | TransformChangedFlags componentFlags = flags; |
431 | if (mMobility != ObjectMobility::Movable) |
432 | componentFlags = (TransformChangedFlags)(componentFlags & ~TCF_Transform); |
433 | else |
434 | { |
435 | mDirtyFlags |= DirtyFlags::LocalTfrmDirty | DirtyFlags::WorldTfrmDirty; |
436 | mDirtyHash++; |
437 | } |
438 | |
439 | // Only send component flags if we haven't removed them all |
440 | if (componentFlags != 0) |
441 | { |
442 | for (auto& entry : mComponents) |
443 | { |
444 | if (entry->supportsNotify(flags)) |
445 | { |
446 | bool alwaysRun = entry->hasFlag(ComponentFlag::AlwaysRun); |
447 | if (alwaysRun || gSceneManager().isRunning()) |
448 | entry->onTransformChanged(componentFlags); |
449 | } |
450 | } |
451 | } |
452 | |
453 | // Mobility flag is only relevant for this scene object |
454 | flags = (TransformChangedFlags)(flags & ~TCF_Mobility); |
455 | if (flags != 0) |
456 | { |
457 | for (auto& entry : mChildren) |
458 | entry->notifyTransformChanged(flags); |
459 | } |
460 | } |
461 | |
462 | void SceneObject::updateWorldTfrm() const |
463 | { |
464 | mWorldTfrm = mLocalTfrm; |
465 | |
466 | // Don't allow movement from parent when not movable |
467 | if (mParent != nullptr && mMobility == ObjectMobility::Movable) |
468 | { |
469 | mWorldTfrm.makeWorld(mParent->getTransform()); |
470 | |
471 | mCachedWorldTfrm = mWorldTfrm.getMatrix(); |
472 | } |
473 | else |
474 | { |
475 | mCachedWorldTfrm = getLocalMatrix(); |
476 | } |
477 | |
478 | mDirtyFlags &= ~DirtyFlags::WorldTfrmDirty; |
479 | } |
480 | |
481 | void SceneObject::updateLocalTfrm() const |
482 | { |
483 | mCachedLocalTfrm = mLocalTfrm.getMatrix(); |
484 | mDirtyFlags &= ~DirtyFlags::LocalTfrmDirty; |
485 | } |
486 | |
487 | /************************************************************************/ |
488 | /* Hierarchy */ |
489 | /************************************************************************/ |
490 | |
491 | void SceneObject::setParent(const HSceneObject& parent, bool keepWorldTransform) |
492 | { |
493 | if (parent.isDestroyed()) |
494 | return; |
495 | |
496 | #if BS_IS_BANSHEE3D |
497 | UUID originalPrefab = getPrefabLink(); |
498 | #endif |
499 | |
500 | if (mMobility != ObjectMobility::Movable) |
501 | keepWorldTransform = true; |
502 | |
503 | _setParent(parent, keepWorldTransform); |
504 | |
505 | #if BS_IS_BANSHEE3D |
506 | if (gCoreApplication().isEditor()) |
507 | { |
508 | UUID newPrefab = getPrefabLink(); |
509 | if (originalPrefab != newPrefab) |
510 | PrefabUtility::clearPrefabIds(mThisHandle); |
511 | } |
512 | #endif |
513 | } |
514 | |
515 | void SceneObject::_setParent(const HSceneObject& parent, bool keepWorldTransform) |
516 | { |
517 | if (mThisHandle == parent) |
518 | return; |
519 | |
520 | if (mParent == nullptr || mParent != parent) |
521 | { |
522 | Transform worldTfrm; |
523 | |
524 | // Make sure the object keeps its world coordinates |
525 | if (keepWorldTransform) |
526 | worldTfrm = getTransform(); |
527 | |
528 | if (mParent != nullptr) |
529 | mParent->removeChild(mThisHandle); |
530 | |
531 | if (parent != nullptr) |
532 | { |
533 | parent->addChild(mThisHandle); |
534 | setScene(parent->mParentScene); |
535 | } |
536 | else |
537 | setScene(nullptr); |
538 | |
539 | mParent = parent; |
540 | |
541 | if (keepWorldTransform) |
542 | { |
543 | mLocalTfrm = worldTfrm; |
544 | |
545 | if (mParent != nullptr) |
546 | mLocalTfrm.makeLocal(mParent->getTransform()); |
547 | } |
548 | |
549 | bool isInstantiated = (mFlags & SOF_DontInstantiate) == 0; |
550 | if(isInstantiated) |
551 | notifyTransformChanged((TransformChangedFlags)(TCF_Parent | TCF_Transform)); |
552 | } |
553 | } |
554 | |
555 | const SPtr<SceneInstance>& SceneObject::getScene() const |
556 | { |
557 | if(mParentScene) |
558 | return mParentScene; |
559 | |
560 | LOGWRN("Attempting to access a scene of a SceneObject with no scene, returning main scene instead." ); |
561 | return gSceneManager().getMainScene(); |
562 | |
563 | } |
564 | |
565 | void SceneObject::setScene(const SPtr<SceneInstance>& scene) |
566 | { |
567 | if(mParentScene == scene) |
568 | return; |
569 | |
570 | mParentScene = scene; |
571 | |
572 | for(auto& child : mChildren) |
573 | child->setScene(scene); |
574 | } |
575 | |
576 | HSceneObject SceneObject::getChild(UINT32 idx) const |
577 | { |
578 | if(idx >= mChildren.size()) |
579 | { |
580 | BS_EXCEPT(InternalErrorException, "Child index out of range." ); |
581 | } |
582 | |
583 | return mChildren[idx]; |
584 | } |
585 | |
586 | int SceneObject::indexOfChild(const HSceneObject& child) const |
587 | { |
588 | for(int i = 0; i < (int)mChildren.size(); i++) |
589 | { |
590 | if(mChildren[i] == child) |
591 | return i; |
592 | } |
593 | |
594 | return -1; |
595 | } |
596 | |
597 | void SceneObject::addChild(const HSceneObject& object) |
598 | { |
599 | mChildren.push_back(object); |
600 | |
601 | object->_setFlags(mFlags); |
602 | } |
603 | |
604 | void SceneObject::removeChild(const HSceneObject& object) |
605 | { |
606 | auto result = find(mChildren.begin(), mChildren.end(), object); |
607 | |
608 | if(result != mChildren.end()) |
609 | mChildren.erase(result); |
610 | else |
611 | { |
612 | BS_EXCEPT(InternalErrorException, |
613 | "Trying to remove a child but it's not a child of the transform." ); |
614 | } |
615 | } |
616 | |
617 | HSceneObject SceneObject::findPath(const String& path) const |
618 | { |
619 | if (path.empty()) |
620 | return HSceneObject(); |
621 | |
622 | String trimmedPath = path; |
623 | StringUtil::trim(trimmedPath, "/" ); |
624 | |
625 | Vector<String> entries = StringUtil::split(trimmedPath, "/" ); |
626 | |
627 | // Find scene object referenced by the path |
628 | HSceneObject so = getHandle(); |
629 | UINT32 pathIdx = 0; |
630 | for (; pathIdx < (UINT32)entries.size(); pathIdx++) |
631 | { |
632 | String entry = entries[pathIdx]; |
633 | |
634 | if (entry.empty()) |
635 | continue; |
636 | |
637 | // This character signifies not-a-scene-object. This is allowed to support |
638 | // paths used by the scripting system (which can point to properties of |
639 | // components on scene objects). |
640 | if (entry[0] != '!') |
641 | break; |
642 | |
643 | String childName = entry.substr(1, entry.size() - 1); |
644 | so = so->findChild(childName); |
645 | |
646 | if (so == nullptr) |
647 | break; |
648 | } |
649 | |
650 | return so; |
651 | } |
652 | |
653 | HSceneObject SceneObject::findChild(const String& name, bool recursive) |
654 | { |
655 | for (auto& child : mChildren) |
656 | { |
657 | if (child->getName() == name) |
658 | return child; |
659 | } |
660 | |
661 | if (recursive) |
662 | { |
663 | for (auto& child : mChildren) |
664 | { |
665 | HSceneObject foundObject = child->findChild(name, true); |
666 | if (foundObject != nullptr) |
667 | return foundObject; |
668 | } |
669 | } |
670 | |
671 | return HSceneObject(); |
672 | } |
673 | |
674 | Vector<HSceneObject> SceneObject::findChildren(const String& name, bool recursive) |
675 | { |
676 | std::function<void(const HSceneObject&, Vector<HSceneObject>&)> findChildrenInternal = |
677 | [&](const HSceneObject& so, Vector<HSceneObject>& output) |
678 | { |
679 | for (auto& child : so->mChildren) |
680 | { |
681 | if (child->getName() == name) |
682 | output.push_back(child); |
683 | } |
684 | |
685 | if (recursive) |
686 | { |
687 | for (auto& child : so->mChildren) |
688 | findChildrenInternal(child, output); |
689 | } |
690 | }; |
691 | |
692 | Vector<HSceneObject> output; |
693 | findChildrenInternal(mThisHandle, output); |
694 | |
695 | return output; |
696 | } |
697 | |
698 | void SceneObject::setActive(bool active) |
699 | { |
700 | mActiveSelf = active; |
701 | setActiveHierarchy(active); |
702 | } |
703 | |
704 | void SceneObject::setActiveHierarchy(bool active, bool triggerEvents) |
705 | { |
706 | bool activeHierarchy = active && mActiveSelf; |
707 | |
708 | if (mActiveHierarchy != activeHierarchy) |
709 | { |
710 | mActiveHierarchy = activeHierarchy; |
711 | |
712 | if (triggerEvents) |
713 | { |
714 | if (activeHierarchy) |
715 | { |
716 | for (auto& component : mComponents) |
717 | gSceneManager()._notifyComponentActivated(component, triggerEvents); |
718 | } |
719 | else |
720 | { |
721 | for (auto& component : mComponents) |
722 | gSceneManager()._notifyComponentDeactivated(component, triggerEvents); |
723 | } |
724 | } |
725 | } |
726 | |
727 | for (auto child : mChildren) |
728 | { |
729 | child->setActiveHierarchy(mActiveHierarchy, triggerEvents); |
730 | } |
731 | } |
732 | |
733 | bool SceneObject::getActive(bool self) const |
734 | { |
735 | if (self) |
736 | return mActiveSelf; |
737 | else |
738 | return mActiveHierarchy; |
739 | } |
740 | |
741 | void SceneObject::setMobility(ObjectMobility mobility) |
742 | { |
743 | if(mMobility != mobility) |
744 | { |
745 | mMobility = mobility; |
746 | |
747 | // If mobility changed to movable, update both the mobility flag and transform, otherwise just mobility |
748 | if (mMobility == ObjectMobility::Movable) |
749 | notifyTransformChanged((TransformChangedFlags)(TCF_Transform | TCF_Mobility)); |
750 | else |
751 | notifyTransformChanged(TCF_Mobility); |
752 | } |
753 | } |
754 | |
755 | HSceneObject SceneObject::clone(bool instantiate, bool preserveUUIDs) |
756 | { |
757 | const bool isInstantiated = !hasFlag(SOF_DontInstantiate); |
758 | |
759 | if (!instantiate) |
760 | _setFlags(SOF_DontInstantiate); |
761 | else |
762 | _unsetFlags(SOF_DontInstantiate); |
763 | |
764 | UINT32 bufferSize = 0; |
765 | |
766 | MemorySerializer serializer; |
767 | UINT8* buffer = serializer.encode(this, bufferSize, (void*(*)(size_t))&bs_alloc); |
768 | |
769 | int flags = GODM_RestoreExternal | GODM_UseNewIds; |
770 | if(!preserveUUIDs) |
771 | flags |= GODM_UseNewUUID; |
772 | |
773 | CoreSerializationContext serzContext; |
774 | serzContext.goState = bs_shared_ptr_new<GameObjectDeserializationState>(flags); |
775 | |
776 | SPtr<SceneObject> cloneObj = std::static_pointer_cast<SceneObject>( |
777 | serializer.decode(buffer, bufferSize, &serzContext)); |
778 | bs_free(buffer); |
779 | |
780 | if(isInstantiated) |
781 | _unsetFlags(SOF_DontInstantiate); |
782 | else |
783 | _setFlags(SOF_DontInstantiate); |
784 | |
785 | return cloneObj->mThisHandle; |
786 | } |
787 | |
788 | HComponent SceneObject::getComponent(RTTITypeBase* type) const |
789 | { |
790 | if(type != Component::getRTTIStatic()) |
791 | { |
792 | for (auto& entry : mComponents) |
793 | { |
794 | if (entry->getRTTI()->isDerivedFrom(type)) |
795 | return entry; |
796 | } |
797 | } |
798 | |
799 | return HComponent(); |
800 | } |
801 | |
802 | void SceneObject::destroyComponent(const HComponent component, bool immediate) |
803 | { |
804 | if(component == nullptr) |
805 | { |
806 | LOGDBG("Trying to remove a null component" ); |
807 | return; |
808 | } |
809 | |
810 | auto iter = std::find(mComponents.begin(), mComponents.end(), component); |
811 | |
812 | if(iter != mComponents.end()) |
813 | { |
814 | (*iter)->_setIsDestroyed(); |
815 | |
816 | if (isInstantiated()) |
817 | gSceneManager()._notifyComponentDestroyed(*iter, immediate); |
818 | |
819 | (*iter)->destroyInternal(*iter, immediate); |
820 | mComponents.erase(iter); |
821 | } |
822 | else |
823 | LOGDBG("Trying to remove a component that doesn't exist on this SceneObject." ); |
824 | } |
825 | |
826 | void SceneObject::destroyComponent(Component* component, bool immediate) |
827 | { |
828 | auto iterFind = std::find_if(mComponents.begin(), mComponents.end(), |
829 | [component](const HComponent& x) |
830 | { |
831 | if(x.isDestroyed()) |
832 | return false; |
833 | |
834 | return x._getHandleData()->mPtr->object.get() == component; } |
835 | ); |
836 | |
837 | if(iterFind != mComponents.end()) |
838 | { |
839 | destroyComponent(*iterFind, immediate); |
840 | } |
841 | } |
842 | |
843 | HComponent SceneObject::addComponent(UINT32 typeId) |
844 | { |
845 | SPtr<IReflectable> newObj = rtti_create(typeId); |
846 | |
847 | if(!rtti_is_subclass<Component>(newObj.get())) |
848 | { |
849 | LOGERR("Specified type is not a valid Component." ); |
850 | return HComponent(); |
851 | } |
852 | |
853 | SPtr<Component> componentPtr = std::static_pointer_cast<Component>(newObj); |
854 | |
855 | // Clean up the self-reference assigned by the RTTI system |
856 | componentPtr->mRTTIData = nullptr; |
857 | |
858 | HComponent newComponent = static_object_cast<Component>(GameObjectManager::instance().registerObject(componentPtr)); |
859 | newComponent->mParent = mThisHandle; |
860 | |
861 | addAndInitializeComponent(newComponent); |
862 | return newComponent; |
863 | } |
864 | |
865 | void SceneObject::addComponentInternal(const SPtr<Component>& component) |
866 | { |
867 | HComponent newComponent = static_object_cast<Component>( |
868 | GameObjectManager::instance().getObject(component->getInstanceId())); |
869 | newComponent->mParent = mThisHandle; |
870 | newComponent->mThisHandle = newComponent; |
871 | |
872 | mComponents.push_back(newComponent); |
873 | } |
874 | |
875 | void SceneObject::addAndInitializeComponent(const HComponent& component) |
876 | { |
877 | component->mThisHandle = component; |
878 | |
879 | if(component->mUUID.empty()) |
880 | component->mUUID = UUIDGenerator::generateRandom(); |
881 | |
882 | mComponents.push_back(component); |
883 | |
884 | if (isInstantiated()) |
885 | { |
886 | component->_instantiate(); |
887 | |
888 | gSceneManager()._notifyComponentCreated(component, getActive()); |
889 | } |
890 | } |
891 | |
892 | void SceneObject::addAndInitializeComponent(const SPtr<Component>& component) |
893 | { |
894 | HComponent newComponent = static_object_cast<Component>( |
895 | GameObjectManager::instance().getObject(component->getInstanceId())); |
896 | newComponent->mParent = mThisHandle; |
897 | |
898 | addAndInitializeComponent(newComponent); |
899 | } |
900 | |
901 | RTTITypeBase* SceneObject::getRTTIStatic() |
902 | { |
903 | return SceneObjectRTTI::instance(); |
904 | } |
905 | |
906 | RTTITypeBase* SceneObject::getRTTI() const |
907 | { |
908 | return SceneObject::getRTTIStatic(); |
909 | } |
910 | } |
911 | |