Make BoneNodes ordered in Skeleton

1. Order skins differently than bones and draw them with unified local z order
2. Check children has been remove when its ansester remove
3. Checkout cocos2d::map exists iter before remove

Squashed from https://github.com/cocos2d/cocos2d-x/pull/13191
This commit is contained in:
geron-cn 2015-08-04 14:13:13 +08:00 committed by pandamicro
parent d00e54ad8a
commit 9980397081
4 changed files with 196 additions and 84 deletions

View File

@ -1,6 +1,6 @@
/****************************************************************************
Copyright (c) 2015 Chukong Technologies Inc.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
@ -105,14 +105,18 @@ void BoneNode::addSkin(SkinNode* skin, bool display)
void BoneNode::removeChild(Node* child, bool cleanup /* = true */)
{
Node::removeChild(child, cleanup);
removeFromChildrenListHelper(child);
ssize_t index = _children.getIndex(child);
if (index != cocos2d::CC_INVALID_INDEX)
{
Node::removeChild(child, cleanup);
removeFromChildrenListHelper(child);
}
}
void BoneNode::removeFromBoneList(BoneNode* bone)
{
auto skeletonNode = dynamic_cast<SkeletonNode*>(bone);
if (skeletonNode == nullptr) //not a nested skeleton
if (skeletonNode == nullptr) // is not a nested skeleton
{
bone->_rootSkeleton = nullptr;
auto subBones = bone->getAllSubBones();
@ -120,45 +124,52 @@ void BoneNode::removeFromBoneList(BoneNode* bone)
for (auto &subBone : subBones)
{
subBone->_rootSkeleton = nullptr;
_rootSkeleton->_subBonesMap.erase(subBone->getName());
if (bone->_isRackShow && bone->_visible)
auto toremoveIter = _rootSkeleton->_subBonesMap.find(subBone->getName());
if (toremoveIter != _rootSkeleton->_subBonesMap.end())
{
_rootSkeleton->_subDrawBonesDirty = true;
_rootSkeleton->_subDrawBonesOrderDirty = true;
_rootSkeleton->_subBonesMap.erase(toremoveIter);
_rootSkeleton->_subBonesDirty = true;
_rootSkeleton->_subBonesOrderDirty = true;
}
}
}
else
{
_rootSkeleton->_subBonesDirty = true;
_rootSkeleton->_subBonesOrderDirty = true;
}
_childBones.eraseObject(bone);
}
void BoneNode::addToBoneList(BoneNode* bone)
{
_childBones.pushBack(bone);
if (bone->_rootSkeleton == nullptr && _rootSkeleton != nullptr)
if (_rootSkeleton != nullptr)
{
auto subBones = bone->getAllSubBones();
subBones.pushBack(bone);
for (auto &subBone : subBones)
auto skeletonNode = dynamic_cast<SkeletonNode*>(bone);
if (skeletonNode == nullptr && bone->_rootSkeleton == nullptr) // not nest skeleton
{
subBone->_rootSkeleton = _rootSkeleton;
auto bonename = subBone->getName();
if (_rootSkeleton->_subBonesMap.find(bonename) == _rootSkeleton->_subBonesMap.end())
auto subBones = bone->getAllSubBones();
subBones.pushBack(bone);
for (auto &subBone : subBones)
{
_rootSkeleton->_subBonesMap.insert(subBone->getName(), subBone);
if (bone->_isRackShow && bone->_visible)
subBone->_rootSkeleton = _rootSkeleton;
auto bonename = subBone->getName();
if (_rootSkeleton->_subBonesMap.find(bonename) == _rootSkeleton->_subBonesMap.end())
{
_rootSkeleton->_subDrawBonesDirty = true;
_rootSkeleton->_subDrawBonesOrderDirty = true;
}
}
else
CCLOG("already has a bone named %s in skeleton %s", bonename.c_str(), _rootSkeleton->getName().c_str());
}
_rootSkeleton->_subBonesMap.insert(subBone->getName(), subBone);
if (bone->_isRackShow && bone->_visible)
_rootSkeleton->_subBonesDirty = true;
_rootSkeleton->_subBonesOrderDirty = true;
}
else
CCLOG("already has a bone named %s in skeleton %s", bonename.c_str(), _rootSkeleton->getName().c_str());
}
}
else
{
_rootSkeleton->_subDrawBonesDirty = true;
_rootSkeleton->_subDrawBonesOrderDirty = true;
_rootSkeleton->_subBonesDirty = true;
_rootSkeleton->_subBonesOrderDirty = true;
}
}
}
@ -287,8 +298,8 @@ void BoneNode::setDebugDrawEnabled(bool isDebugDraw)
_isRackShow = isDebugDraw;
if (_visible && nullptr != _rootSkeleton)
{
_rootSkeleton->_subDrawBonesDirty = true;
_rootSkeleton->_subDrawBonesOrderDirty = true;
_rootSkeleton->_subBonesDirty = true;
_rootSkeleton->_subBonesOrderDirty = true;
}
}
@ -298,6 +309,57 @@ void BoneNode::setDebugDrawColor(const cocos2d::Color4F &color)
updateColor();
}
void BoneNode::visit(cocos2d::Renderer *renderer, const cocos2d::Mat4& parentTransform, uint32_t parentFlags)
{
// quick return if not visible. children won't be drawn.
if (!_visible)
{
return;
}
uint32_t flags = processParentFlags(parentTransform, parentFlags);
// IMPORTANT:
// To ease the migration to v3.0, we still support the Mat4 stack,
// but it is deprecated and your code should not rely on it
_director->pushMatrix(cocos2d::MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
_director->loadMatrix(cocos2d::MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
bool visibleByCamera = isVisitableByVisitingCamera();
int i = 0;
if (!_childBones.empty())
{
sortAllChildren();
// draw children zOrder < 0
for (; i < _childBones.size(); i++)
{
auto bone = _childBones.at(i);
if (bone && bone->getZOrder() < 0)
bone->visit(renderer, _modelViewTransform, flags);
else
break;
}
// self draw
if (visibleByCamera)
this->draw(renderer, _modelViewTransform, flags);
for (auto it = _childBones.cbegin() + i; it != _childBones.cend(); ++it)
(*it)->visit(renderer, _modelViewTransform, flags);
}
else if (visibleByCamera)
{
this->draw(renderer, _modelViewTransform, flags);
}
_director->popMatrix(cocos2d::MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
// FIX ME: Why need to set _orderOfArrival to 0??
// Please refer to https://github.com/cocos2d/cocos2d-x/pull/6920
// reset for next frame
// _orderOfArrival = 0;
}
void BoneNode::draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transform, uint32_t flags)
{
@ -377,7 +439,7 @@ void BoneNode::onDraw(const cocos2d::Mat4 &transform, uint32_t flags)
#ifdef CC_STUDIO_ENABLED_VIEW
glVertexAttribPointer(cocos2d::GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, 0, _noMVPVertices);
glVertexAttribPointer(cocos2d::GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 0, _squareColors);
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
glDrawArrays(GL_LINE_LOOP, 0, 4);
@ -451,7 +513,7 @@ bool BoneNode::isPointOnRack(const cocos2d::Vec2& bonePoint)
{
if (_rackLength != 0.0f && _rackWidth != 0.0f)
{
float a1 = (_squareVertices[2].y - _squareVertices[3].y ) / (_squareVertices[3].x - _squareVertices[0].x);
float a1 = (_squareVertices[2].y - _squareVertices[3].y) / (_squareVertices[3].x - _squareVertices[0].x);
float a2 = (_squareVertices[2].y - _squareVertices[3].y) / (_squareVertices[0].x - _squareVertices[1].x);;
float b1 = a1 * _squareVertices[3].x;
float y1 = bonePoint.y - _squareVertices[1].y;
@ -479,7 +541,7 @@ void BoneNode::batchBoneDrawToSkeleton(BoneNode* bone) const
}
int count = bone->_rootSkeleton->_batchedVeticesCount;
if (count + 8 >(int)(bone->_rootSkeleton->_batchedBoneVetices.capacity()))
if (count + 8 >(int)(bone->_rootSkeleton->_batchedBoneVetices.size()))
{
bone->_rootSkeleton->_batchedBoneVetices.resize(count + 100);
bone->_rootSkeleton->_batchedBoneColors.resize(count + 100);
@ -501,11 +563,45 @@ void BoneNode::batchBoneDrawToSkeleton(BoneNode* bone) const
#endif //CC_STUDIO_ENABLED_VIEW
}
// call after self visit
void BoneNode::visitSkins(cocos2d::Renderer* renderer, BoneNode* bone)
{
// quick return if not visible. children won't be drawn.
if (!bone->_visible)
{
return;
}
// IMPORTANT:
// To ease the migration to v3.0, we still support the Mat4 stack,
// but it is deprecated and your code should not rely on it
_director->pushMatrix(cocos2d::MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
_director->loadMatrix(cocos2d::MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, bone->_modelViewTransform);
bool visibleByCamera = bone->isVisitableByVisitingCamera();
int i = 0;
if (!bone->_boneSkins.empty())
{
bone->sortAllChildren();
for (auto it = bone->_boneSkins.cbegin(); it != bone->_boneSkins.cend(); ++it)
(*it)->visit(renderer, bone->_modelViewTransform, true);
}
_director->popMatrix(cocos2d::MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
// FIX ME: Why need to set _orderOfArrival to 0??
// Please refer to https://github.com/cocos2d/cocos2d-x/pull/6920
// reset for next frame
// _orderOfArrival = 0;
}
void BoneNode::setLocalZOrder(int localZOrder)
{
Node::setLocalZOrder(localZOrder);
if (_rootSkeleton != nullptr && this->_visible && this->_isRackShow)
_rootSkeleton->_subDrawBonesOrderDirty = true;
if (_rootSkeleton != nullptr)
_rootSkeleton->_subBonesOrderDirty = true;
}
void BoneNode::setName(const std::string& name)
@ -549,11 +645,6 @@ void BoneNode::removeFromChildrenListHelper(Node * child)
if (nullptr != bone)
{
removeFromBoneList(bone);
if (bone->_isRackShow)
{
_rootSkeleton->_subDrawBonesDirty = true;
_rootSkeleton->_subDrawBonesOrderDirty = true;
}
}
else
{
@ -571,10 +662,10 @@ void BoneNode::setVisible(bool visible)
return;
Node::setVisible(visible);
if (_isRackShow && _rootSkeleton != nullptr)
if (_rootSkeleton != nullptr)
{
_rootSkeleton->_subDrawBonesDirty = true;
_rootSkeleton->_subDrawBonesOrderDirty = true;
_rootSkeleton->_subBonesDirty = true;
_rootSkeleton->_subBonesOrderDirty = true;
}
}

View File

@ -186,10 +186,16 @@ protected:
virtual void onDraw(const cocos2d::Mat4 &transform, uint32_t flags);
// override Node::visit, just visit bones in children
virtual void visit(cocos2d::Renderer *renderer, const cocos2d::Mat4& parentTransform, uint32_t parentFlags) override;
// a help function for SkeletonNode
// for batch bone's draw to _rootSkeleton
virtual void batchBoneDrawToSkeleton(BoneNode* bone) const;
// a help funciton for SkeletonNode
// @param bone, visit bone's skins
virtual void visitSkins(cocos2d::Renderer* renderer, BoneNode* bone);
protected:
cocos2d::CustomCommand _customCommand;
cocos2d::BlendFunc _blendFunc;

View File

@ -1,6 +1,6 @@
/****************************************************************************
Copyright (c) 2015 Chukong Technologies Inc.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
@ -49,7 +49,7 @@ SkeletonNode* SkeletonNode::create()
bool SkeletonNode::init()
{
_rackLength = _rackWidth = 20;
_rackLength = _rackWidth = 20;
updateVertices();
setGLProgramState(cocos2d::GLProgramState::getOrCreateWithGLProgramName(cocos2d::GLProgram::SHADER_NAME_POSITION_COLOR_NO_MVP));
_rootSkeleton = this;
@ -74,7 +74,7 @@ cocos2d::Rect SkeletonNode::getBoundingBox() const
for (const auto& bone : allbones)
{
cocos2d::Rect r = RectApplyAffineTransform(bone->getVisibleSkinsRect(),
bone->getNodeToParentAffineTransform(bone->getRootSkeletonNode()));
bone->getNodeToParentAffineTransform(bone->getRootSkeletonNode()));
if (r.equals(cocos2d::Rect::ZERO))
continue;
@ -100,10 +100,10 @@ cocos2d::Rect SkeletonNode::getBoundingBox() const
}
SkeletonNode::SkeletonNode()
: BoneNode()
, _subDrawBonesDirty(true)
, _subDrawBonesOrderDirty(true)
, _batchedVeticesCount(0)
: BoneNode()
, _subBonesDirty(true)
, _subBonesOrderDirty(true)
, _batchedVeticesCount(0)
{
}
@ -122,9 +122,9 @@ void SkeletonNode::updateVertices()
_squareVertices[5].y = _squareVertices[2].y = _squareVertices[1].y = _squareVertices[6].y
= _squareVertices[0].x = _squareVertices[4].x = _squareVertices[7].x = _squareVertices[3].x = .0f;
_squareVertices[5].x = -radiusl; _squareVertices[0].y = -radiusw;
_squareVertices[6].x = radiusl; _squareVertices[3].y = radiusw;
_squareVertices[1].x = radiusl_2; _squareVertices[7].y = radiusw_2;
_squareVertices[2].x = - radiusl_2; _squareVertices[4].y = - radiusw_2;
_squareVertices[6].x = radiusl; _squareVertices[3].y = radiusw;
_squareVertices[1].x = radiusl_2; _squareVertices[7].y = radiusw_2;
_squareVertices[2].x = -radiusl_2; _squareVertices[4].y = -radiusw_2;
for (int i = 0; i < 8; i++)
@ -165,6 +165,7 @@ void SkeletonNode::visit(cocos2d::Renderer *renderer, const cocos2d::Mat4& paren
int i = 0;
if (!_children.empty())
{
sortAllChildren();
@ -183,14 +184,18 @@ void SkeletonNode::visit(cocos2d::Renderer *renderer, const cocos2d::Mat4& paren
(*it)->visit(renderer, _modelViewTransform, flags);
}
if (visibleByCamera)
checkSubBonesDirty();
for (const auto& bone : _subOrderedAllBones)
{
this->draw(renderer, _modelViewTransform, flags);
// batch draw all sub bones
_batchBoneCommand.init(_globalZOrder, _modelViewTransform, parentFlags);
_batchBoneCommand.func = CC_CALLBACK_0(SkeletonNode::batchDrawAllSubBones, this, _modelViewTransform);
renderer->addCommand(&_batchBoneCommand);
visitSkins(renderer, bone);
}
this->draw(renderer, _modelViewTransform, flags);
// batch draw all sub bones
_batchBoneCommand.init(_globalZOrder, _modelViewTransform, parentFlags);
_batchBoneCommand.func = CC_CALLBACK_0(SkeletonNode::batchDrawAllSubBones, this, _modelViewTransform);
renderer->addCommand(&_batchBoneCommand);
_director->popMatrix(cocos2d::MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
// FIX ME: Why need to set _orderOfArrival to 0??
// Please refer to https://github.com/cocos2d/cocos2d-x/pull/6920
@ -218,17 +223,13 @@ void SkeletonNode::draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transf
void SkeletonNode::batchDrawAllSubBones(const cocos2d::Mat4 &transform)
{
if (_subDrawBonesDirty)
{
updateAllDrawBones();
}
if (_subDrawBonesOrderDirty)
sortAllDrawBones();
checkSubBonesDirty();
_batchedVeticesCount = 0;
for (const auto& bone : _subDrawBones)
for (const auto& bone : _subOrderedAllBones)
{
batchBoneDrawToSkeleton(bone);
if (bone->isDebugDrawEnabled())
batchBoneDrawToSkeleton(bone);
}
cocos2d::Vec3* vetices = _batchedBoneVetices.data();
cocos2d::Color4F* veticesColor = _batchedBoneColors.data();
@ -242,12 +243,12 @@ void SkeletonNode::batchDrawAllSubBones(const cocos2d::Mat4 &transform)
glVertexAttribPointer(cocos2d::GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 0, veticesColor);
cocos2d::GL::blendFunc(_blendFunc.src, _blendFunc.dst);
#ifdef CC_STUDIO_ENABLED_VIEW
glLineWidth(1);
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
for(int i= 0; i < _batchedVeticesCount; i += 8)
for (int i = 0; i < _batchedVeticesCount; i += 8)
{
glDrawArrays(GL_TRIANGLE_FAN, i, 4);
glDrawArrays(GL_LINE_LOOP, i + 4, 4);
@ -287,7 +288,7 @@ void SkeletonNode::changeSkins(const std::map<std::string, std::string>& boneSki
for (auto &boneskin : boneSkinNameMap)
{
auto bone = getBoneNode(boneskin.first);
if ( nullptr != bone)
if (nullptr != bone)
bone->displaySkin(boneskin.second, true);
}
}
@ -321,37 +322,49 @@ void SkeletonNode::addSkinGroup(std::string groupName, std::map<std::string, std
_skinGroupMap.insert(std::make_pair(groupName, boneSkinNameMap));
}
void SkeletonNode::updateAllDrawBones()
void SkeletonNode::checkSubBonesDirty()
{
_subDrawBones.clear();
// get All Visible SubBones
if (_subBonesDirty)
{
updateOrderedAllbones();
_subBonesDirty = false;
}
if (_subBonesOrderDirty)
{
sortOrderedAllBones();
_subBonesOrderDirty = false;
}
}
void SkeletonNode::updateOrderedAllbones()
{
_subOrderedAllBones.clear();
// update sub bones, get All Visible SubBones
// get all sub bones as visit with visible
std::stack<BoneNode*> boneStack;
for (const auto& bone : _childBones)
{
if (bone->isVisible() && bone->isDebugDrawEnabled())
if (bone->isVisible())
boneStack.push(bone);
}
while (boneStack.size() > 0)
{
auto top = boneStack.top();
_subDrawBones.pushBack(top);
_subOrderedAllBones.pushBack(top);
boneStack.pop();
auto topChildren = top->getChildBones();
for (const auto& childbone : topChildren)
{
if (childbone->isVisible() && childbone->isDebugDrawEnabled())
if (childbone->isVisible())
boneStack.push(childbone);
}
}
_subDrawBonesDirty = false;
}
void SkeletonNode::sortAllDrawBones()
void SkeletonNode::sortOrderedAllBones()
{
std::sort(_subDrawBones.begin(), _subDrawBones.end(), cocos2d::nodeComparisonLess);
_subDrawBonesOrderDirty = false;
std::sort(_subOrderedAllBones.begin(), _subOrderedAllBones.end(), cocos2d::nodeComparisonLess);
}
NS_TIMELINE_END

View File

@ -97,17 +97,19 @@ private:
CC_DISALLOW_COPY_AND_ASSIGN(SkeletonNode);
void checkSubBonesDirty();
// for draw skins as ordered bones' local z
cocos2d::Vector<BoneNode*> _subOrderedAllBones;
void updateOrderedAllbones();
void sortOrderedAllBones();
// for batch draw sub bones
bool _subDrawBonesDirty;
bool _subDrawBonesOrderDirty;
cocos2d::Vector<BoneNode*> _subDrawBones; // for draw faster, cache a list from _subBonesMap, sorted by render order
bool _subBonesDirty;
bool _subBonesOrderDirty;
std::vector<cocos2d::Vec3> _batchedBoneVetices;
std::vector<cocos2d::Color4F> _batchedBoneColors;
int _batchedVeticesCount;
cocos2d::CustomCommand _batchBoneCommand;
void updateAllDrawBones();
void sortAllDrawBones();
void batchDrawAllSubBones(const cocos2d::Mat4 &transform);
void batchSubBone(BoneNode* bone);
};