From 99803970811689ae5cbb2052ef5bdd93a09db285 Mon Sep 17 00:00:00 2001 From: geron-cn Date: Tue, 4 Aug 2015 14:13:13 +0800 Subject: [PATCH] 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 --- .../cocostudio/ActionTimeline/CCBoneNode.cpp | 175 +++++++++++++----- .../cocostudio/ActionTimeline/CCBoneNode.h | 6 + .../ActionTimeline/CCSkeletonNode.cpp | 87 +++++---- .../ActionTimeline/CCSkeletonNode.h | 12 +- 4 files changed, 196 insertions(+), 84 deletions(-) diff --git a/cocos/editor-support/cocostudio/ActionTimeline/CCBoneNode.cpp b/cocos/editor-support/cocostudio/ActionTimeline/CCBoneNode.cpp index 531c037b68..74ebf4292d 100644 --- a/cocos/editor-support/cocostudio/ActionTimeline/CCBoneNode.cpp +++ b/cocos/editor-support/cocostudio/ActionTimeline/CCBoneNode.cpp @@ -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(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(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; } } diff --git a/cocos/editor-support/cocostudio/ActionTimeline/CCBoneNode.h b/cocos/editor-support/cocostudio/ActionTimeline/CCBoneNode.h index b72984ccbf..f321ca9f7a 100644 --- a/cocos/editor-support/cocostudio/ActionTimeline/CCBoneNode.h +++ b/cocos/editor-support/cocostudio/ActionTimeline/CCBoneNode.h @@ -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; diff --git a/cocos/editor-support/cocostudio/ActionTimeline/CCSkeletonNode.cpp b/cocos/editor-support/cocostudio/ActionTimeline/CCSkeletonNode.cpp index 77b248f865..b1959c0310 100644 --- a/cocos/editor-support/cocostudio/ActionTimeline/CCSkeletonNode.cpp +++ b/cocos/editor-support/cocostudio/ActionTimeline/CCSkeletonNode.cpp @@ -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& 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 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 \ No newline at end of file diff --git a/cocos/editor-support/cocostudio/ActionTimeline/CCSkeletonNode.h b/cocos/editor-support/cocostudio/ActionTimeline/CCSkeletonNode.h index 8fc4d99fa9..be3700472d 100644 --- a/cocos/editor-support/cocostudio/ActionTimeline/CCSkeletonNode.h +++ b/cocos/editor-support/cocostudio/ActionTimeline/CCSkeletonNode.h @@ -97,17 +97,19 @@ private: CC_DISALLOW_COPY_AND_ASSIGN(SkeletonNode); + void checkSubBonesDirty(); + // for draw skins as ordered bones' local z + cocos2d::Vector _subOrderedAllBones; + void updateOrderedAllbones(); + void sortOrderedAllBones(); // for batch draw sub bones - bool _subDrawBonesDirty; - bool _subDrawBonesOrderDirty; - cocos2d::Vector _subDrawBones; // for draw faster, cache a list from _subBonesMap, sorted by render order + bool _subBonesDirty; + bool _subBonesOrderDirty; std::vector _batchedBoneVetices; std::vector _batchedBoneColors; int _batchedVeticesCount; cocos2d::CustomCommand _batchBoneCommand; - void updateAllDrawBones(); - void sortAllDrawBones(); void batchDrawAllSubBones(const cocos2d::Mat4 &transform); void batchSubBone(BoneNode* bone); };