You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
885 lines
26 KiB
885 lines
26 KiB
/****************************************************************************
|
|
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
|
|
|
|
http://www.cocos.com
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights to
|
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
of the Software, and to permit persons to whom the Software is furnished to do so,
|
|
subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include "core/scene-graph/Node.h"
|
|
#include "base/StringUtil.h"
|
|
#include "core/data/Object.h"
|
|
#include "core/memop/CachedArray.h"
|
|
#include "core/platform/Debug.h"
|
|
#include "core/scene-graph/NodeEnum.h"
|
|
#include "core/scene-graph/Scene.h"
|
|
#include "core/utils/IDGenerator.h"
|
|
#include "math/Utils.h"
|
|
|
|
namespace cc {
|
|
|
|
// static variables
|
|
|
|
uint32_t Node::clearFrame{0};
|
|
uint32_t Node::clearRound{1000};
|
|
const uint32_t Node::TRANSFORM_ON{1 << 0};
|
|
uint32_t Node::globalFlagChangeVersion{0};
|
|
|
|
namespace {
|
|
const ccstd::string EMPTY_NODE_NAME;
|
|
IDGenerator idGenerator("Node");
|
|
} // namespace
|
|
|
|
Node::Node() : Node(EMPTY_NODE_NAME) {
|
|
}
|
|
|
|
Node::Node(const ccstd::string &name) {
|
|
#define NODE_SHARED_MEMORY_BYTE_LENGTH (20)
|
|
static_assert(offsetof(Node, _padding) + sizeof(_padding) - offsetof(Node, _eventMask) == NODE_SHARED_MEMORY_BYTE_LENGTH, "Wrong shared memory size");
|
|
_sharedMemoryActor.initialize(&_eventMask, NODE_SHARED_MEMORY_BYTE_LENGTH);
|
|
#undef NODE_SHARED_MEMORY_BYTE_LENGTH
|
|
|
|
_id = idGenerator.getNewId();
|
|
if (name.empty()) {
|
|
_name.append("New Node");
|
|
} else {
|
|
_name = name;
|
|
}
|
|
// _eventProcessor = ccnew NodeEventProcessor(this);
|
|
}
|
|
|
|
Node::~Node() {
|
|
if (!_children.empty()) {
|
|
// Reset children's _parent to nullptr to avoid dangerous pointer
|
|
for (const auto &child : _children) {
|
|
child->_parent = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Node::onBatchCreated(bool dontChildPrefab) {
|
|
// onBatchCreated was implemented in TS, so code should never go here.
|
|
CC_ABORT();
|
|
emit<BatchCreated>(dontChildPrefab);
|
|
}
|
|
|
|
Node *Node::instantiate(Node *cloned, bool isSyncedNode) {
|
|
if (!cloned) {
|
|
CC_ABORT();
|
|
// TODO(): cloned = legacyCC.instantiate._clone(this, this);
|
|
return nullptr;
|
|
}
|
|
// TODO():
|
|
// const newPrefabInfo = cloned._prefab;
|
|
// if (EDITOR && newPrefabInfo) {
|
|
// if (cloned == = newPrefabInfo.root) {
|
|
// // newPrefabInfo.fileId = '';
|
|
// } else {
|
|
// // var PrefabUtils = Editor.require('scene://utils/prefab');
|
|
// // PrefabUtils.unlinkPrefab(cloned);
|
|
// }
|
|
//}
|
|
// if (EDITOR && legacyCC.GAME_VIEW) {
|
|
// const syncing = newPrefabInfo&& cloned == = newPrefabInfo.root && newPrefabInfo.sync;
|
|
// if (!syncing) {
|
|
// cloned._name += ' (Clone)';
|
|
// }
|
|
//}
|
|
cloned->_parent = nullptr;
|
|
cloned->onBatchCreated(isSyncedNode);
|
|
return cloned;
|
|
}
|
|
|
|
void Node::onHierarchyChangedBase(Node *oldParent) { // NOLINT(misc-unused-parameters)
|
|
Node *newParent = _parent;
|
|
auto *scene = dynamic_cast<Scene *>(newParent);
|
|
if (isPersistNode() && scene == nullptr) {
|
|
emit<RemovePersistRootNode>();
|
|
#if CC_EDITOR
|
|
debug::warnID(1623);
|
|
#endif
|
|
}
|
|
#if CC_EDITOR
|
|
auto *curScene = getScene();
|
|
const bool inCurrentSceneBefore = oldParent && oldParent->isChildOf(curScene);
|
|
const bool inCurrentSceneNow = newParent && newParent->isChildOf(curScene);
|
|
if (!inCurrentSceneBefore && inCurrentSceneNow) {
|
|
// attached
|
|
this->notifyEditorAttached(true);
|
|
} else if (inCurrentSceneBefore && !inCurrentSceneNow) {
|
|
// detached
|
|
this->notifyEditorAttached(false);
|
|
}
|
|
// conflict detection
|
|
// _Scene.DetectConflict.afterAddChild(this);
|
|
#endif
|
|
|
|
bool shouldActiveNow = isActive() && !!(newParent && newParent->isActiveInHierarchy());
|
|
if (isActiveInHierarchy() != shouldActiveNow) {
|
|
// Director::getInstance()->getNodeActivator()->activateNode(this, shouldActiveNow); // TODO(xwx): use TS temporarily
|
|
emit<ActiveNode>(shouldActiveNow);
|
|
}
|
|
}
|
|
|
|
void Node::setActive(bool isActive) {
|
|
uint8_t isActiveU8 = isActive ? 1 : 0;
|
|
if (_active != isActiveU8) {
|
|
_active = isActiveU8;
|
|
Node *parent = _parent;
|
|
if (parent) {
|
|
bool couldActiveInScene = parent->isActiveInHierarchy();
|
|
if (couldActiveInScene) {
|
|
// Director::getInstance()->getNodeActivator()->activateNode(this, isActive); // TODO(xwx): use TS temporarily
|
|
emit<ActiveNode>(isActive);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Node::setParent(Node *parent, bool isKeepWorld /* = false */) {
|
|
if (isKeepWorld) {
|
|
updateWorldTransform();
|
|
}
|
|
|
|
if (_parent == parent) {
|
|
return;
|
|
}
|
|
|
|
Node *oldParent = _parent;
|
|
Node *newParent = parent;
|
|
#if CC_DEBUG > 0
|
|
if (oldParent && (oldParent->_objFlags & Flags::DEACTIVATING) == Flags::DEACTIVATING) {
|
|
debug::errorID(3821);
|
|
}
|
|
#endif
|
|
_parent = newParent;
|
|
_siblingIndex = 0;
|
|
onSetParent(oldParent, isKeepWorld);
|
|
emit<ParentChanged>(oldParent);
|
|
if (oldParent) {
|
|
if (!(oldParent->_objFlags & Flags::DESTROYING)) {
|
|
index_t removeAt = getIdxOfChild(oldParent->_children, this);
|
|
// TODO(): DEV
|
|
/*if (DEV && removeAt < 0) {
|
|
errorID(1633);
|
|
return;
|
|
}*/
|
|
if (removeAt < 0) {
|
|
return;
|
|
}
|
|
oldParent->_children.erase(oldParent->_children.begin() + removeAt);
|
|
oldParent->updateSiblingIndex();
|
|
oldParent->emit<ChildRemoved>(this);
|
|
}
|
|
}
|
|
if (newParent) {
|
|
#if CC_DEBUG > 0
|
|
if ((newParent->_objFlags & Flags::DEACTIVATING) == Flags::DEACTIVATING) {
|
|
debug::errorID(3821);
|
|
}
|
|
#endif
|
|
newParent->_children.emplace_back(this);
|
|
_siblingIndex = static_cast<index_t>(newParent->_children.size() - 1);
|
|
newParent->emit<ChildAdded>(this);
|
|
}
|
|
onHierarchyChanged(oldParent);
|
|
}
|
|
|
|
void Node::walk(const WalkCallback &preFunc) {
|
|
walk(preFunc, nullptr);
|
|
}
|
|
|
|
void Node::walk(const WalkCallback &preFunc, const WalkCallback &postFunc) { // NOLINT(misc-no-recursion)
|
|
if (preFunc) {
|
|
preFunc(this);
|
|
}
|
|
|
|
for (const auto &child : _children) {
|
|
if (child) {
|
|
child->walk(preFunc, postFunc);
|
|
}
|
|
}
|
|
|
|
if (postFunc) {
|
|
postFunc(this);
|
|
}
|
|
}
|
|
|
|
// Component *Node::addComponent(Component *comp) {
|
|
// comp->_node = this; // cjh TODO: shared_ptr
|
|
// _components.emplace_back(comp);
|
|
//
|
|
// if (isActiveInHierarchy()) {
|
|
// NodeActivator::activateComp(comp);
|
|
// }
|
|
//
|
|
// return comp;
|
|
// }
|
|
//
|
|
// void Node::removeComponent(Component *comp) {
|
|
// auto iteComp = std::find(_components.begin(), _components.end(), comp);
|
|
// if (iteComp != _components.end()) {
|
|
// _components.erase(iteComp);
|
|
// }
|
|
// }
|
|
|
|
bool Node::onPreDestroyBase() {
|
|
Flags destroyingFlag = Flags::DESTROYING;
|
|
_objFlags |= destroyingFlag;
|
|
bool destroyByParent = (!!_parent) && (!!(_parent->_objFlags & destroyingFlag));
|
|
#if CC_EDITOR
|
|
if (!destroyByParent) {
|
|
this->notifyEditorAttached(false);
|
|
}
|
|
#endif
|
|
if (isPersistNode()) {
|
|
emit<RemovePersistRootNode>();
|
|
}
|
|
if (!destroyByParent) {
|
|
if (_parent) {
|
|
emit<ParentChanged>(this);
|
|
index_t childIdx = getIdxOfChild(_parent->_children, this);
|
|
if (childIdx != -1) {
|
|
_parent->_children.erase(_parent->_children.begin() + childIdx);
|
|
}
|
|
_siblingIndex = 0;
|
|
_parent->updateSiblingIndex();
|
|
_parent->emit<ChildRemoved>(this);
|
|
}
|
|
}
|
|
|
|
// NOTE: The following code is not needed now since we override Node._onPreDestroy in node.jsb.ts
|
|
// and the logic will be done in TS.
|
|
// emit(NodeEventType::NODE_DESTROYED, this);
|
|
// for (const auto &child : _children) {
|
|
// child->destroyImmediate();
|
|
// }
|
|
//
|
|
// emit(EventTypesToJS::NODE_DESTROY_COMPONENTS);
|
|
|
|
offAll();
|
|
return destroyByParent;
|
|
}
|
|
|
|
Node *Node::getChildByName(const ccstd::string &name) const {
|
|
if (name.empty()) {
|
|
CC_LOG_INFO("Invalid name");
|
|
return nullptr;
|
|
}
|
|
for (const auto &child : _children) {
|
|
if (child->_name == name) {
|
|
return child;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void Node::setScene(Node *node) {
|
|
node->updateScene();
|
|
}
|
|
|
|
void Node::updateScene() {
|
|
if (_parent == nullptr) {
|
|
return;
|
|
}
|
|
_scene = _parent->_scene;
|
|
emit<SceneUpdated>(_scene);
|
|
}
|
|
|
|
/* static */
|
|
index_t Node::getIdxOfChild(const ccstd::vector<IntrusivePtr<Node>> &child, Node *target) {
|
|
auto iteChild = std::find(child.begin(), child.end(), target);
|
|
if (iteChild != child.end()) {
|
|
return static_cast<index_t>(iteChild - child.begin());
|
|
}
|
|
return CC_INVALID_INDEX;
|
|
}
|
|
|
|
Node *Node::getChildByUuid(const ccstd::string &uuid) const {
|
|
if (uuid.empty()) {
|
|
CC_LOG_INFO("Invalid uuid");
|
|
return nullptr;
|
|
}
|
|
for (const auto &child : _children) {
|
|
if (child->_id == uuid) {
|
|
return child;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool Node::isChildOf(Node *parent) const {
|
|
const Node *child = this;
|
|
do {
|
|
if (child == parent) {
|
|
return true;
|
|
}
|
|
child = child->_parent;
|
|
} while (child);
|
|
return false;
|
|
}
|
|
|
|
void Node::removeAllChildren() {
|
|
for (auto i = static_cast<index_t>(_children.size() - 1); i >= 0; --i) {
|
|
if (_children[i]) {
|
|
_children[i]->setParent(nullptr);
|
|
}
|
|
}
|
|
_children.clear();
|
|
}
|
|
|
|
void Node::setSiblingIndex(index_t index) {
|
|
if (!_parent) {
|
|
return;
|
|
}
|
|
if (!!(_parent->_objFlags & Flags::DEACTIVATING)) {
|
|
debug::errorID(3821);
|
|
return;
|
|
}
|
|
ccstd::vector<IntrusivePtr<Node>> &siblings = _parent->_children;
|
|
index = index != -1 ? index : static_cast<index_t>(siblings.size()) - 1;
|
|
index_t oldIdx = getIdxOfChild(siblings, this);
|
|
if (index != oldIdx) {
|
|
if (oldIdx != CC_INVALID_INDEX) {
|
|
siblings.erase(siblings.begin() + oldIdx);
|
|
}
|
|
if (index < siblings.size()) {
|
|
siblings.insert(siblings.begin() + index, this);
|
|
} else {
|
|
siblings.emplace_back(this);
|
|
}
|
|
_parent->updateSiblingIndex();
|
|
emit<SiblingIndexChanged>(index);
|
|
}
|
|
}
|
|
|
|
Node *Node::getChildByPath(const ccstd::string &path) const {
|
|
size_t end = 0;
|
|
ccstd::vector<ccstd::string> segments = StringUtil::split(path, "/");
|
|
auto *lastNode = const_cast<Node *>(this);
|
|
for (const ccstd::string &segment : segments) {
|
|
if (segment.empty()) {
|
|
continue;
|
|
}
|
|
Node *next{nullptr};
|
|
if (lastNode) {
|
|
for (const auto &child : lastNode->_children) {
|
|
if (child->_name == segment) {
|
|
next = child;
|
|
break;
|
|
}
|
|
}
|
|
lastNode = next;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return lastNode;
|
|
}
|
|
|
|
//
|
|
void Node::setPositionInternal(float x, float y, float z, bool calledFromJS) {
|
|
_localPosition.set(x, y, z);
|
|
invalidateChildren(TransformBit::POSITION);
|
|
|
|
if (_eventMask & TRANSFORM_ON) {
|
|
emit<TransformChanged>(TransformBit::POSITION);
|
|
}
|
|
|
|
if (!calledFromJS) {
|
|
notifyLocalPositionUpdated();
|
|
}
|
|
}
|
|
|
|
void Node::setRotationInternal(float x, float y, float z, float w, bool calledFromJS) {
|
|
_localRotation.set(x, y, z, w);
|
|
_eulerDirty = true;
|
|
|
|
invalidateChildren(TransformBit::ROTATION);
|
|
|
|
if (_eventMask & TRANSFORM_ON) {
|
|
emit<TransformChanged>(TransformBit::ROTATION);
|
|
}
|
|
|
|
if (!calledFromJS) {
|
|
notifyLocalRotationUpdated();
|
|
}
|
|
}
|
|
|
|
void Node::setRotationFromEuler(float x, float y, float z) {
|
|
_euler.set(x, y, z);
|
|
Quaternion::fromEuler(x, y, z, &_localRotation);
|
|
_eulerDirty = false;
|
|
invalidateChildren(TransformBit::ROTATION);
|
|
if (_eventMask & TRANSFORM_ON) {
|
|
emit<TransformChanged>(TransformBit::ROTATION);
|
|
}
|
|
|
|
notifyLocalRotationUpdated();
|
|
}
|
|
|
|
void Node::setScaleInternal(float x, float y, float z, bool calledFromJS) {
|
|
_localScale.set(x, y, z);
|
|
|
|
invalidateChildren(TransformBit::SCALE);
|
|
if (_eventMask & TRANSFORM_ON) {
|
|
emit<TransformChanged>(TransformBit::SCALE);
|
|
}
|
|
|
|
if (!calledFromJS) {
|
|
notifyLocalScaleUpdated();
|
|
}
|
|
}
|
|
void Node::updateWorldTransform() { // NOLINT(misc-no-recursion)
|
|
uint32_t dirtyBits = 0;
|
|
updateWorldTransformRecursive(dirtyBits);
|
|
}
|
|
|
|
void Node::updateWorldTransformRecursive(uint32_t &dirtyBits) { // NOLINT(misc-no-recursion)
|
|
const uint32_t currDirtyBits = _transformFlags;
|
|
if (!currDirtyBits) {
|
|
return;
|
|
}
|
|
|
|
Node *parent = getParent();
|
|
if (parent && parent->_transformFlags) {
|
|
parent->updateWorldTransformRecursive(dirtyBits);
|
|
}
|
|
dirtyBits |= currDirtyBits;
|
|
if (parent) {
|
|
if (dirtyBits & static_cast<uint32_t>(TransformBit::POSITION)) {
|
|
_worldPosition.transformMat4(_localPosition, parent->_worldMatrix);
|
|
_worldMatrix.m[12] = _worldPosition.x;
|
|
_worldMatrix.m[13] = _worldPosition.y;
|
|
_worldMatrix.m[14] = _worldPosition.z;
|
|
}
|
|
if (dirtyBits & static_cast<uint32_t>(TransformBit::RS)) {
|
|
Mat4::fromRTS(_localRotation, _localPosition, _localScale, &_worldMatrix);
|
|
Mat4::multiply(parent->_worldMatrix, _worldMatrix, &_worldMatrix);
|
|
const bool rotChanged = dirtyBits & static_cast<uint32_t>(TransformBit::ROTATION);
|
|
Quaternion *rotTmp = rotChanged ? &_worldRotation : nullptr;
|
|
Mat4::toRTS(_worldMatrix, rotTmp, nullptr, &_worldScale);
|
|
}
|
|
} else {
|
|
if (dirtyBits & static_cast<uint32_t>(TransformBit::POSITION)) {
|
|
_worldPosition.set(_localPosition);
|
|
_worldMatrix.m[12] = _worldPosition.x;
|
|
_worldMatrix.m[13] = _worldPosition.y;
|
|
_worldMatrix.m[14] = _worldPosition.z;
|
|
}
|
|
if (dirtyBits & static_cast<uint32_t>(TransformBit::RS)) {
|
|
if (dirtyBits & static_cast<uint32_t>(TransformBit::ROTATION)) {
|
|
_worldRotation.set(_localRotation);
|
|
}
|
|
if (dirtyBits & static_cast<uint32_t>(TransformBit::SCALE)) {
|
|
_worldScale.set(_localScale);
|
|
}
|
|
Mat4::fromRTS(_worldRotation, _worldPosition, _worldScale, &_worldMatrix);
|
|
}
|
|
}
|
|
_transformFlags = (static_cast<uint32_t>(TransformBit::NONE));
|
|
}
|
|
|
|
const Mat4 &Node::getWorldMatrix() const { // NOLINT(misc-no-recursion)
|
|
const_cast<Node *>(this)->updateWorldTransform();
|
|
return _worldMatrix;
|
|
}
|
|
|
|
Mat4 Node::getWorldRS() {
|
|
updateWorldTransform();
|
|
Mat4 target{_worldMatrix};
|
|
target.m[12] = target.m[13] = target.m[14] = 0;
|
|
return target;
|
|
}
|
|
|
|
Mat4 Node::getWorldRT() {
|
|
updateWorldTransform();
|
|
Mat4 target;
|
|
Mat4::fromRT(_worldRotation, _worldPosition, &target);
|
|
return target;
|
|
}
|
|
|
|
void Node::invalidateChildren(TransformBit dirtyBit) { // NOLINT(misc-no-recursion)
|
|
auto curDirtyBit{static_cast<uint32_t>(dirtyBit)};
|
|
const uint32_t hasChangedFlags = getChangedFlags();
|
|
const uint32_t transformFlags = _transformFlags;
|
|
if (isValid() && (transformFlags & hasChangedFlags & curDirtyBit) != curDirtyBit) {
|
|
_transformFlags = (transformFlags | curDirtyBit);
|
|
setChangedFlags(hasChangedFlags | curDirtyBit);
|
|
|
|
for (Node *child : getChildren()) {
|
|
child->invalidateChildren(dirtyBit | TransformBit::POSITION);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Node::setWorldPosition(float x, float y, float z) {
|
|
_worldPosition.set(x, y, z);
|
|
if (_parent) {
|
|
_parent->updateWorldTransform();
|
|
Mat4 invertWMat{_parent->_worldMatrix};
|
|
invertWMat.inverse();
|
|
_localPosition.transformMat4(_worldPosition, invertWMat);
|
|
} else {
|
|
_localPosition.set(_worldPosition);
|
|
}
|
|
notifyLocalPositionUpdated();
|
|
|
|
invalidateChildren(TransformBit::POSITION);
|
|
|
|
if (_eventMask & TRANSFORM_ON) {
|
|
emit<TransformChanged>(TransformBit::POSITION);
|
|
}
|
|
}
|
|
|
|
const Vec3 &Node::getWorldPosition() const {
|
|
const_cast<Node *>(this)->updateWorldTransform();
|
|
return _worldPosition;
|
|
}
|
|
|
|
void Node::setWorldRotation(float x, float y, float z, float w) {
|
|
_worldRotation.set(x, y, z, w);
|
|
if (_parent) {
|
|
_parent->updateWorldTransform();
|
|
_localRotation.set(_parent->_worldRotation.getConjugated());
|
|
_localRotation.multiply(_worldRotation);
|
|
} else {
|
|
_localRotation.set(_worldRotation);
|
|
}
|
|
|
|
_eulerDirty = true;
|
|
|
|
notifyLocalRotationUpdated();
|
|
|
|
invalidateChildren(TransformBit::ROTATION);
|
|
|
|
if (_eventMask & TRANSFORM_ON) {
|
|
emit<TransformChanged>(TransformBit::ROTATION);
|
|
}
|
|
}
|
|
|
|
const Quaternion &Node::getWorldRotation() const { // NOLINT(misc-no-recursion)
|
|
const_cast<Node *>(this)->updateWorldTransform();
|
|
return _worldRotation;
|
|
}
|
|
|
|
void Node::setWorldScale(float x, float y, float z) {
|
|
if (_parent != nullptr) {
|
|
updateWorldTransform(); // ensure reentryability
|
|
Vec3 oldWorldScale = _worldScale;
|
|
_worldScale.set(x, y, z);
|
|
Mat3 localRS;
|
|
Mat3 localRotInv;
|
|
Mat4 worldMatrixTmp = _worldMatrix;
|
|
Vec3 rescaleFactor = _worldScale / oldWorldScale;
|
|
// apply new world scale to temp world matrix
|
|
worldMatrixTmp.scale(rescaleFactor); // need opt
|
|
// get temp local matrix
|
|
Mat4 tmpLocalTransform = _parent->getWorldMatrix().getInversed() * worldMatrixTmp;
|
|
// convert to Matrix 3 x 3
|
|
Mat3::fromMat4(tmpLocalTransform, &localRS);
|
|
Mat3::fromQuat(_localRotation.getConjugated(), &localRotInv);
|
|
// remove rotation part of the local matrix
|
|
Mat3::multiply(localRotInv, localRS, &localRS);
|
|
|
|
// extract scaling part from local matrix
|
|
_localScale.x = Vec3{localRS.m[0], localRS.m[1], localRS.m[2]}.length();
|
|
_localScale.y = Vec3{localRS.m[3], localRS.m[4], localRS.m[5]}.length();
|
|
_localScale.z = Vec3{localRS.m[6], localRS.m[7], localRS.m[8]}.length();
|
|
} else {
|
|
_worldScale.set(x, y, z);
|
|
_localScale = _worldScale;
|
|
}
|
|
|
|
notifyLocalScaleUpdated();
|
|
|
|
invalidateChildren(TransformBit::SCALE);
|
|
if (_eventMask & TRANSFORM_ON) {
|
|
emit<TransformChanged>(TransformBit::SCALE);
|
|
}
|
|
}
|
|
|
|
const Vec3 &Node::getWorldScale() const {
|
|
const_cast<Node *>(this)->updateWorldTransform();
|
|
return _worldScale;
|
|
}
|
|
|
|
void Node::setForward(const Vec3 &dir) {
|
|
const float len = dir.length();
|
|
Vec3 v3Temp = dir * (-1.F / len);
|
|
Quaternion qTemp{Quaternion::identity()};
|
|
Quaternion::fromViewUp(v3Temp, &qTemp);
|
|
setWorldRotation(qTemp);
|
|
}
|
|
|
|
void Node::setAngle(float val) {
|
|
_euler.set(0, 0, val);
|
|
Quaternion::createFromAngleZ(val, &_localRotation);
|
|
_eulerDirty = false;
|
|
invalidateChildren(TransformBit::ROTATION);
|
|
if (_eventMask & TRANSFORM_ON) {
|
|
emit<TransformChanged>(TransformBit::ROTATION);
|
|
}
|
|
|
|
notifyLocalRotationUpdated();
|
|
}
|
|
|
|
void Node::onSetParent(Node *oldParent, bool keepWorldTransform) {
|
|
if (_parent) {
|
|
if ((oldParent == nullptr || oldParent->_scene != _parent->_scene) && _parent->_scene != nullptr) {
|
|
walk(setScene);
|
|
}
|
|
}
|
|
|
|
if (keepWorldTransform) {
|
|
if (_parent) {
|
|
_parent->updateWorldTransform();
|
|
if (mathutils::approx<float>(_parent->_worldMatrix.determinant(), 0.F, mathutils::EPSILON)) {
|
|
CC_LOG_WARNING("14300");
|
|
_transformFlags |= static_cast<uint32_t>(TransformBit::TRS);
|
|
updateWorldTransform();
|
|
} else {
|
|
Mat4 tmpMat4 = _parent->_worldMatrix.getInversed() * _worldMatrix;
|
|
Mat4::toRTS(tmpMat4, &_localRotation, &_localPosition, &_localScale);
|
|
}
|
|
} else {
|
|
_localPosition.set(_worldPosition);
|
|
_localRotation.set(_worldRotation);
|
|
_localScale.set(_worldScale);
|
|
}
|
|
|
|
notifyLocalPositionRotationScaleUpdated();
|
|
_eulerDirty = true;
|
|
}
|
|
invalidateChildren(TransformBit::TRS);
|
|
}
|
|
|
|
void Node::rotate(const Quaternion &rot, NodeSpace ns /* = NodeSpace::LOCAL*/, bool calledFromJS /* = false*/) {
|
|
Quaternion qTempA{rot};
|
|
qTempA.normalize();
|
|
if (ns == NodeSpace::LOCAL) {
|
|
_localRotation *= qTempA;
|
|
} else if (ns == NodeSpace::WORLD) {
|
|
Quaternion qTempB{Quaternion::identity()};
|
|
qTempB = qTempA * getWorldRotation();
|
|
qTempA = _worldRotation;
|
|
qTempA.inverse();
|
|
qTempB = qTempA * qTempB;
|
|
_localRotation = _localRotation * qTempB;
|
|
}
|
|
_eulerDirty = true;
|
|
invalidateChildren(TransformBit::ROTATION);
|
|
if (_eventMask & TRANSFORM_ON) {
|
|
emit<TransformChanged>(TransformBit::ROTATION);
|
|
}
|
|
|
|
if (!calledFromJS) {
|
|
notifyLocalRotationUpdated();
|
|
}
|
|
}
|
|
|
|
void Node::lookAt(const Vec3 &pos, const Vec3 &up) {
|
|
Vec3 vTemp = getWorldPosition();
|
|
Quaternion qTemp{Quaternion::identity()};
|
|
vTemp -= pos;
|
|
vTemp.normalize();
|
|
Quaternion::fromViewUp(vTemp, up, &qTemp);
|
|
setWorldRotation(qTemp);
|
|
}
|
|
|
|
Vec3 Node::inverseTransformPoint(const Vec3 &p) { // NOLINT(misc-no-recursion)
|
|
Vec3 out(p);
|
|
inverseTransformPointRecursive(out);
|
|
return out;
|
|
}
|
|
|
|
void Node::inverseTransformPointRecursive(Vec3 &out) const { // NOLINT(misc-no-recursion)
|
|
auto *parent = getParent();
|
|
if (!parent) {
|
|
return;
|
|
}
|
|
parent->inverseTransformPointRecursive(out);
|
|
Vec3::transformInverseRTS(out, getRotation(), getPosition(), getScale(), &out);
|
|
}
|
|
|
|
void Node::setMatrix(const Mat4 &val) {
|
|
val.decompose(&_localScale, &_localRotation, &_localPosition);
|
|
notifyLocalPositionRotationScaleUpdated();
|
|
|
|
invalidateChildren(TransformBit::TRS);
|
|
_eulerDirty = true;
|
|
if (_eventMask & TRANSFORM_ON) {
|
|
emit<TransformChanged>(TransformBit::TRS);
|
|
}
|
|
}
|
|
|
|
void Node::setWorldRotationFromEuler(float x, float y, float z) {
|
|
Quaternion::fromEuler(x, y, z, &_worldRotation);
|
|
if (_parent) {
|
|
_parent->updateWorldTransform();
|
|
_localRotation = _parent->_worldRotation.getConjugated() * _worldRotation;
|
|
} else {
|
|
_localRotation = _worldRotation;
|
|
}
|
|
_eulerDirty = true;
|
|
|
|
invalidateChildren(TransformBit::ROTATION);
|
|
if (_eventMask & TRANSFORM_ON) {
|
|
emit<TransformChanged>(TransformBit::ROTATION);
|
|
}
|
|
|
|
notifyLocalRotationUpdated();
|
|
}
|
|
|
|
void Node::setRTSInternal(Quaternion *rot, Vec3 *pos, Vec3 *scale, bool calledFromJS) {
|
|
uint32_t dirtyBit = 0;
|
|
if (rot) {
|
|
dirtyBit |= static_cast<uint32_t>(TransformBit::ROTATION);
|
|
_localRotation = *rot;
|
|
_eulerDirty = true;
|
|
}
|
|
if (pos) {
|
|
_localPosition = *pos;
|
|
dirtyBit |= static_cast<uint32_t>(TransformBit::POSITION);
|
|
}
|
|
if (scale) {
|
|
_localScale = *scale;
|
|
dirtyBit |= static_cast<uint32_t>(TransformBit::SCALE);
|
|
}
|
|
|
|
if (!calledFromJS) {
|
|
notifyLocalPositionRotationScaleUpdated();
|
|
}
|
|
|
|
if (dirtyBit) {
|
|
invalidateChildren(static_cast<TransformBit>(dirtyBit));
|
|
if (_eventMask & TRANSFORM_ON) {
|
|
emit<TransformChanged>(static_cast<TransformBit>(dirtyBit));
|
|
}
|
|
}
|
|
}
|
|
|
|
void Node::resetChangedFlags() {
|
|
globalFlagChangeVersion++;
|
|
}
|
|
|
|
void Node::clearNodeArray() {
|
|
if (clearFrame < clearRound) {
|
|
clearFrame++;
|
|
} else {
|
|
clearFrame = 0;
|
|
}
|
|
}
|
|
|
|
ccstd::string Node::getPathInHierarchy() const {
|
|
ccstd::string result = getName();
|
|
Node *curNode = getParent();
|
|
while (curNode && curNode->getParent()) {
|
|
result.insert(0, "/").insert(0, curNode->getName());
|
|
curNode = curNode->getParent();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void Node::translate(const Vec3 &trans, NodeSpace ns) {
|
|
Vec3 v3Temp{trans};
|
|
if (ns == NodeSpace::LOCAL) {
|
|
v3Temp.transformQuat(_localRotation);
|
|
_localPosition.x += v3Temp.x;
|
|
_localPosition.y += v3Temp.y;
|
|
_localPosition.z += v3Temp.z;
|
|
} else if (ns == NodeSpace::WORLD) {
|
|
if (_parent) {
|
|
Quaternion qTemp = _parent->getWorldRotation();
|
|
qTemp.inverse();
|
|
v3Temp.transformQuat(qTemp);
|
|
Vec3 scale{_worldScale};
|
|
_localPosition.x += v3Temp.x / scale.x;
|
|
_localPosition.y += v3Temp.y / scale.y;
|
|
_localPosition.z += v3Temp.z / scale.z;
|
|
} else {
|
|
_localPosition.x += trans.x;
|
|
_localPosition.y += trans.y;
|
|
_localPosition.z += trans.z;
|
|
}
|
|
}
|
|
|
|
notifyLocalPositionUpdated();
|
|
|
|
invalidateChildren(TransformBit::POSITION);
|
|
if (_eventMask & TRANSFORM_ON) {
|
|
emit<TransformChanged>(TransformBit::POSITION);
|
|
}
|
|
}
|
|
|
|
bool Node::onPreDestroy() {
|
|
bool result = onPreDestroyBase();
|
|
// TODO(Lenovo): bookOfChange free
|
|
return result;
|
|
}
|
|
|
|
void Node::onHierarchyChanged(Node *oldParent) {
|
|
emit<Reattach>();
|
|
onHierarchyChangedBase(oldParent);
|
|
}
|
|
|
|
/* static */
|
|
// Node *Node::find(const ccstd::string &path, Node *referenceNode /* = nullptr*/) {
|
|
// return cc::find(path, referenceNode);
|
|
// }
|
|
|
|
// For deserialization
|
|
// void Node::_setChild(index_t i, Node *child) {
|
|
// if (i < _children.size()) {
|
|
// _children[i] = child;
|
|
// } else {
|
|
// CC_LOG_ERROR("Invalid index (%d) for Node children (size: %u)", i, static_cast<uint32_t>(_children.size()));
|
|
// }
|
|
//}
|
|
//
|
|
// Node *Node::_getChild(index_t i) {
|
|
// if (i < _children.size()) {
|
|
// return _children[i];
|
|
// }
|
|
// CC_LOG_ERROR("Invalid index (%d) for Node children (size: %u)", i, static_cast<uint32_t>(_children.size()));
|
|
// return nullptr;
|
|
//}
|
|
//
|
|
// void Node::_setChildrenSize(uint32_t size) {
|
|
// _children.resize(size);
|
|
//}
|
|
//
|
|
// uint32_t Node::_getChildrenSize() {
|
|
// return _children.size();
|
|
//}
|
|
//
|
|
void Node::_setChildren(ccstd::vector<IntrusivePtr<Node>> &&children) {
|
|
_children = std::move(children);
|
|
}
|
|
|
|
void Node::destruct() {
|
|
CCObject::destruct();
|
|
_children.clear();
|
|
_scene = nullptr;
|
|
_userData = nullptr;
|
|
}
|
|
|
|
//
|
|
|
|
} // namespace cc
|
|
|