Exemplo n.º 1
0
void AssimpModelMover::boneUpdate(const string &name, ofQuaternion &q)
{
	BoneNode *bn = getBoneNode(name);
	if (!bn)
		return;

	bn->setOrientation(q);
}
Exemplo n.º 2
0
	void Skeleton::_relate()
	{
		for (BoneNodeMap::iterator it = _boneNodes.begin(); it != _boneNodes.end(); ++it)
		{
			BoneNode* node = it->second;
			if (node)
			{
				Bone* bone = node->getBone();
				if (bone)
				{
					int parentID = bone->parent;
					BoneNode* parentNode = _getBoneNode(parentID);
					if (parentNode)
					{
						parentNode->addChild(node);
					}
					else
					{
						_roots.push_back(bone->name);
					}
				}
			}
		}
		if (_roots.empty())
		{
			throw Zen::Exception("no bone root found!");
		}
// 		if (roots.size() > 1)
// 		{
// 			for (size_t i = 0; i != roots.size(); ++i)
// 			{
// 				std::string name = roots[i];
// 				std::transform(name.begin(), name.end(), name.begin(), std::tolower);
// 				if (name == "root")
// 				{
// 					_rootName = roots[i];
// 					break;
// 				}
// 			}
// 			if (_rootName.empty())
// 			{
// 				_addRootNode();
// 				_relate();
// 			}
// 		}
// 		else
// 		{
// 			_rootName = roots[0];
// 		}
		
		//
		_matrices.resize(_boneNodes.size());
		_matricesFull.resize(_boneNodes.size());
	}
Exemplo n.º 3
0
BoneNode* BoneNode::create()
{
    BoneNode* ret = new (std::nothrow) BoneNode();
    if (ret && ret->init())
    {
        ret->autorelease();
        return ret;
    }
    CC_SAFE_DELETE(ret);
    return nullptr;
}
Exemplo n.º 4
0
void AssimpModelMover::listBones(const struct aiScene *sc, const struct aiNode *nd)
{
	//cout << "node: " << nd->mName.data << endl;
	for (unsigned n = 0; n < nd->mNumMeshes; ++n) //nd->mNumMeshes; ++n)
	{
		const struct aiMesh *mesh = sc->mMeshes[nd->mMeshes[n]];
		//cout << "mesh " << n << " " << mesh->mName.data << endl;

		for (size_t a = 0; a < mesh->mNumBones; ++a)
		{
			const aiBone *bone = mesh->mBones[a];

			map<std::string, BoneNode*>::iterator i = boneNodes.find(bone->mName.data);
			bool alreadyStored = (i != boneNodes.end());

			// find the corresponding node
			aiNode *node = sc->mRootNode->FindNode(bone->mName);
			if (!alreadyStored)
			{
				const string name(bone->mName.data);
				boneNames.push_back(name);
				// TODO: free the boneNodes map pointers
				BoneNode *boneNode = new BoneNode(node);
				boneNodes[name] = boneNode;

				// build hierarchical structure of node
				aiNode *parentNode = node->mParent;
				while (parentNode)
				{
					string parentName = parentNode->mName.data;
					BoneNode *parentBNode = getBoneNode(parentName);
					if (parentBNode == NULL)
					{
						boneNames.push_back(parentName);
						parentBNode = new BoneNode(parentNode);
						boneNodes[parentName] = parentBNode;
					}
					boneNode->setParent(parentBNode);

					boneNode = parentBNode;
					parentNode = parentNode->mParent;
				}
			}
		}
	}

	// process all children
	for (unsigned n = 0; n < nd->mNumChildren; ++n)
	{
		listBones(sc, nd->mChildren[n]);
	}
}
Exemplo n.º 5
0
	BoneNode* Skeleton::_getBoneNode( int id )
	{
		for (BoneNodeMap::iterator it = _boneNodes.begin(); it != _boneNodes.end(); ++it)
		{
			BoneNode* node = it->second;
			Bone* bone = node->getBone();
			if (id == bone->id)
			{
				return node;
			}
		}
		return NULL;
	}
Exemplo n.º 6
0
	void Skeleton::generateBoneTransform( Matrix4* matVec , Matrix4 const& baseTrans , float* frames , float* weights , int num )
	{
		assert( mBaseBone->id == 0 );
		matVec[ 0 ] = baseTrans;

		int size = mBoneVec.size();
		for ( int i = 1 ; i < size ; ++i )
		{
			BoneNode* bone = mBoneVec[i];
			Matrix4& boneTrans = matVec[bone->id];
			bone->calcFrameTransform( boneTrans , frames , weights , num );
			TransformUntility::mul( boneTrans , boneTrans , matVec[ bone->parentId ] );
		}
	}
Exemplo n.º 7
0
BoneNode* BoneNode::create(int length)
{
    BoneNode* ret = new (std::nothrow) BoneNode();
    if (ret && ret->init())
    {
        ret->setDebugDrawLength(length);
        ret->autorelease();
    }
    else
    {
        CC_SAFE_DELETE(ret);
    }
    return ret;
}
Exemplo n.º 8
0
	void BoneNode::update( const AnimationTime& at , Skin& sk)
	{	
		if (_bone->id >= 0 && _bone->id < sk.boneKFs.size())
		{
			sBoneKFs& b = sk.boneKFs[_bone->id];
			//translation
			Vec3 t = b.translationKFs.getFrame(&at);

			//rotation
			Quaternion q = b.rotationKFs.getFrame(-1, &at);

			//scale
			Vec3 s = b.scaleKFs.getFrame(-1, &at);

			Mat4 dynamicMtx = Mat4::IDENTITY;
			dynamicMtx.makeTransform(t, s, q);
			//
			if(0)
			{
				Mat4 tM;
				tM.makeTrans(t);
				Mat4 tQ(q);
				dynamicMtx = tM * tQ;
			}

			//
			if (_parent)
			{
				_mtxTransform =  _parent->_fullMatrix * dynamicMtx;
			}
			else
			{
				_mtxTransform = dynamicMtx;
			}
			_fullMatrix = _mtxTransform;

			//
			_skeleton->_matricesFull[_bone->id] = _mtxTransform;
			_mtxTransform = _fullMatrix * _bone->initialMatrix.inverse();
			//
			_skeleton->_matrices[_bone->id] = _mtxTransform;
		}
		
		NameNodeMap::iterator it = _children.begin();
		for ( ; it != _children.end(); ++it)
		{
			BoneNode* n = it->second;
			n->update(at, sk);
		}
	}
Exemplo n.º 9
0
void testApp::setupBone(const string &name, float degx, float degy, float degz)
{
	BoneNode *bn = model.getBoneNode(name);
	if (bn == NULL)
	{
		cerr << "WARNING: could find bind bone " << name << endl;
		return;
	}

	bn->setInheritOrientation(false);
	bn->bindPoseOrientation.makeRotate(degx, ofVec3f(1, 0, 0),
										degy, ofVec3f(0, 1, 0),
										degz, ofVec3f(0, 0, 1));
	bn->resetOrientation();
}
Exemplo n.º 10
0
	void Skeleton::destroy()
	{
		for (BoneNodeMap::iterator it = _boneNodes.begin(); it != _boneNodes.end(); ++it)
		{
			BoneNode* r = it->second;
			if (r)
			{
				Bone* b = r->getBone();
				delete b;
				r->detachObject();
				delete r;
			}
		}
		
		_clear();
	}
Exemplo n.º 11
0
	void Skeleton::generateBoneTransform( Matrix4* matVec , Matrix4 const& baseTrans , int frame , float fract )
	{
		assert( 0 <= fract && fract < 1.0f );

		//M( blend ) = M(motion) * M( base )

		assert( mBaseBone->id == 0 );
		matVec[ 0 ] = baseTrans;

		int size = mBoneVec.size();
		for ( int i = 1 ; i < size ; ++i )
		{
			BoneNode* bone = mBoneVec[i];
			Matrix4& boneTrans = matVec[bone->id];
			bone->calcFrameTransform( boneTrans , frame , frame + 1 , fract );
			TransformUntility::mul( boneTrans , boneTrans , matVec[ bone->parentId ] );
		}
	}
Exemplo n.º 12
0
void AssimpModelMover::drawNodes()
{
	glEnable(GL_NORMALIZE);

	ofPushMatrix();

	ofTranslate(pos);

	ofRotate(180, 0, 0, 1);
	ofTranslate(-scene_center.x, -scene_center.y, scene_center.z);

	if(normalizeScale)
	{
		ofScale(normalizedScale, normalizedScale, normalizedScale);
	}

	for(int i = 0; i < (int)rotAngle.size(); i++){
		ofRotate(rotAngle[i], rotAxis[i].x, rotAxis[i].y, rotAxis[i].z);
	}

	ofScale(scale.x, scale.y, scale.z);

	//glEnable(GL_DEPTH_TEST);
	//glEnable(GL_NORMALIZE);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_TEXTURE_2D);
	glDisable(GL_LIGHTING);
	//light.enable();
	//cam.begin();
	map<string, BoneNode *>::iterator i = boneNodes.begin();
	for(; i != boneNodes.end(); ++i)
	{
		BoneNode *bn = i->second;
		bn->draw();
	}
	//cam.end();
	//light.disable();
	//glDisable(GL_LIGHTING);
	ofPopMatrix();
}
Exemplo n.º 13
0
	void Skeleton::destroy()
	{
		//STLDeleteAssociate(_boneNodes);
// 		BoneNodeMap::iterator it = _boneNodes.begin();
// 		for (; it != _boneNodes.end(); ++it)
// 		{
// 			BoneNode* n = it->second;
// 			if (n)
// 			{
// 				delete n;
// 				n = NULL;
// 			}
// 		}
		BoneNode* r = getRootBoneNode();
		if (r)
		{
			r->detachObject();
			r->release();
			//r = NULL;
		}
		_boneNodes.clear();
	}
Exemplo n.º 14
0
	void Skeleton::generateInvLoacalTransform()
	{
		assert( mUseInvLocalTrans );

		std::vector< Matrix4 > globalTransVec;
		globalTransVec.resize( mBoneVec.size() );
		globalTransVec[0] = Matrix4::Identity();
		mBaseBone->invLocalTrans = Matrix4::Identity();

		assert( mBaseBone->id == 0 );
		int size = mBoneVec.size();
		for ( int i = 1 ; i < size ; ++i )
		{
			BoneNode* bone = mBoneVec[i];
			Matrix4 const& parentTrans = globalTransVec[ bone->parentId ];

			Matrix4& worldTrans = globalTransVec[ bone->id ];
			bone->calcFrameTransform( worldTrans , 0 );
			TransformUntility::mul( worldTrans , worldTrans , parentTrans );

			float det;
			bool result = worldTrans.inverseAffine( bone->invLocalTrans , det );
		}
	}
void AbstractSceneManager::addPickedObjects(std::vector<SceneObject*>& vPickedObjects)
{
	for (size_t i=0; i<m_vPickedList.size(); i++)
	{
		m_vPickedList.at(i)->setPicked(false);
	}
	m_vPickedList.clear();
	m_vPickedList.insert(m_vPickedList.begin(),vPickedObjects.begin(),vPickedObjects.end());
	for (size_t i=0; i<m_vPickedList.size(); i++)
	{
		m_vPickedList.at(i)->setPicked(true);
	}
	if (m_vPickedList.empty())
	{
		emit pickedSeveralSkeletons();
	}
	else if (m_vPickedList.size() == 1)
	{
		//Node* pNode = m_vPickedList.at(0)->m_pNode;
		//if (pNode)
		//{
		//	MotionJointProp mjp;
		//	mjp.strName = pNode->getName();
		//	mjp.vAbsolutePosition = pNode->getAbsolutePosition();
		//	mjp.vRelativePosition = pNode->getPosition();
		//	mjp.eRotation = pNode->getRotation();
		//	emit pickedOneObject(mjp);	
		//}
	}
	else
	{
		int n = 0;
		Skeleton* pSkeleton = NULL;
		m_sPickedSkeleton.clear();
		for (size_t i=1; i<m_vPickedList.size(); i++)
		{
			if (m_vPickedList.at(i)->m_pNode->isBoneNode())
			{
				BoneNode* pBoneNode = (BoneNode*)m_vPickedList.at(i)->m_pNode;
				if (pSkeleton != pBoneNode->getSkeleton())
				{
					n++;
					pSkeleton = pBoneNode->getSkeleton();
					m_sPickedSkeleton.insert(pSkeleton);
				}
			}
		}
		if (n == 1)
		{
			if (pSkeleton)
			{
				MotionClip* pMotionClip = pSkeleton->getMotionClip();
				//if (pMotionClip)
				//{
				//	MotionFileProp mfp;
				//	mfp.strPath = QString::fromStdString(pMotionClip->getFileName());
				//	mfp.fFrameTime = pMotionClip->getFrameTime();
				//	mfp.nFrameCount = pMotionClip->getFrameCount();
				//	mfp.nJointCount = pMotionClip->getJointCount();
				//	mfp.mstate = pSkeleton->getViewState();
				//	mfp.bshowmesh = pSkeleton->isShowMesh();
				//	emit pickedOneSkeleton(mfp);	
				//}
			}
		}
		else
		{
			emit pickedSeveralSkeletons();
		}
	}
}
Exemplo n.º 16
0
	void Skeleton::create(const std::string& fileName)
	{
		//
		std::ifstream f(fileName.c_str(), std::ios::binary);
		if (!f.good())
		{
			return;
		}

		u32 version = 0;
		//	[Tag Size Data]
		int t;
		int s;
		while(f.good())
		{
			t = 0;
			s = 0;
			f.read((char*)&t, sizeof(int));
			f.read((char*)&s, sizeof(int));

			if (s <= 0)
			{
				continue;
			}

			char c[5];
			c[0] = *((char*)&t + 3);
			c[1] = *((char*)&t + 2);
			c[2] = *((char*)&t + 1);
			c[3] = *((char*)&t + 0);
			c[4] = 0;

			switch (t)
			{
			case 'MVER':
				{
					f.read((char*)&_version, sizeof(_version));
				}break;
			case 'MBON': // ¹Ç÷À
				{
					u32 nBones;
					f.read((char*)&nBones,sizeof(nBones));

					for (u32 i = 0;i < nBones;i++)
					{
						//
						Bone* bone = new Bone;
						
						//
						f.read((char*)&bone->id,sizeof(bone->id));

						u8 JointnameLen; 
						f.read((char*)&JointnameLen,sizeof(JointnameLen));

						char name[MODEL_NAME_NODE_SIZE+1];
						f.read((char*)name,JointnameLen);
						name[JointnameLen] = 0;
					
						//
						bone->name = name;

						//ascii2unicode(name, bone->name);

						int parent;
						f.read((char*)&parent,sizeof(parent));

						//
						bone->parent = parent;

						Mat4 initialMatrix = Mat4::IDENTITY;
						{
							f.read((char*)&initialMatrix, sizeof(initialMatrix));
						}
						bone->initialMatrix = initialMatrix;
						//
						BoneNode* node = createBoneNode(bone->name);
						//
						node->attachObject(bone);
					}
					if (nBones != 0)
					{
						_relate();
					}
					
				}break;
			default:
				{
					f.ignore(s);
				}break;
			}
		}
	}
Exemplo n.º 17
0
void AssimpModelMover::updateSkeleton()
{
    // update mesh position for the animation
	for (unsigned int i = 0; i < modelMeshes.size(); ++i)
	{
		// current mesh we are introspecting
		const aiMesh *mesh = modelMeshes[i].mesh;

		// calculate bone matrices
		std::vector<ofMatrix4x4> boneMatrices(mesh->mNumBones);
		for (size_t a = 0; a < mesh->mNumBones; ++a)
		{
			const aiBone *bone = mesh->mBones[a];

			// find the corresponding node by again looking recursively through the node hierarchy for the same name
			map<string, BoneNode *>::iterator it = boneNodes.find(bone->mName.data);
			assert(it != boneNodes.end());
			BoneNode *bn = it->second;

			// start with the mesh-to-bone matrix
			//boneMatrices[a] = aiMatrix4x4ToOfMatrix44(bone->mOffsetMatrix) * bn->getGlobalTransformMatrix();
			boneMatrices[a] = aiMatrix4x4ToOfMatrix44(bone->mOffsetMatrix) * bn->getDerivedTransformMatrix();

			modelMeshes[i].hasChanged = true;
			modelMeshes[i].validCache = false;
		}

		modelMeshes[i].animatedPos.assign(modelMeshes[i].animatedPos.size(),0);
		if(mesh->HasNormals()){
			modelMeshes[i].animatedNorm.assign(modelMeshes[i].animatedNorm.size(),0);
		}
		// loop through all vertex weights of all bones
		for( size_t a = 0; a < mesh->mNumBones; ++a)
		{
			const aiBone* bone = mesh->mBones[a];
			const aiMatrix4x4& posTrafo = ofMatrix4x4ToAiMatrix44(boneMatrices[a]);

			for( size_t b = 0; b < bone->mNumWeights; ++b)
			{
				const aiVertexWeight& weight = bone->mWeights[b];

				size_t vertexId = weight.mVertexId;
				const aiVector3D& srcPos = mesh->mVertices[vertexId];

				modelMeshes[i].animatedPos[vertexId] += weight.mWeight * (posTrafo * srcPos);
			}
			if(mesh->HasNormals()){
				// 3x3 matrix, contains the bone matrix without the translation, only with rotation and possibly scaling
				aiMatrix3x3 normTrafo = aiMatrix3x3( posTrafo);
				for( size_t b = 0; b < bone->mNumWeights; ++b)
				{
					const aiVertexWeight& weight = bone->mWeights[b];
					size_t vertexId = weight.mVertexId;

					const aiVector3D& srcNorm = mesh->mNormals[vertexId];
					modelMeshes[i].animatedNorm[vertexId] += weight.mWeight * (normTrafo * srcNorm);

				}
			}
		}
	}
	updateGLResources();
}
Exemplo n.º 18
0
//TestActionTimelineSkeleton
void TestActionTimelineSkeleton::onEnter()
{
    ActionTimelineBaseTest::onEnter();

    _changedDisplays = _changedDisplay = false;
    Node* node = CSLoader::createNode("ActionTimeline/DemoPlayer_skeleton.csb");
    ActionTimeline* action = CSLoader::createTimeline("ActionTimeline/DemoPlayer_skeleton.csb");
    node->runAction(action);
    node->setScale(0.2f);
    node->setPosition(150, 150);
    action->gotoFrameAndPlay(0);
    addChild(node);

    auto skeletonNode = static_cast<SkeletonNode*>(node);
    const std::string weapBoneName = "Layer20";
    auto weaponHandeBone = skeletonNode->getBoneNode(weapBoneName);

    /***********   debug draw bones  *************/
    auto boneDrawsBtn = cocos2d::ui::Button::create();
    addChild(boneDrawsBtn);
    boneDrawsBtn->setPosition(Vec2(VisibleRect::right().x - 30, VisibleRect::top().y - 30));
    boneDrawsBtn->setTitleText("Draw bone");

    skeletonNode->setDebugDrawEnabled(true);
    boneDrawsBtn->addClickEventListener([skeletonNode, this](Ref* sender)
    {
        skeletonNode->setDebugDrawEnabled(!skeletonNode->isDebugDrawEnabled());
    });


    /***************** change bone display **************************/

    // add display
    auto weapSkinToAdd = Sprite::create("ActionTimeline/testAnimationResource/girl_arms.png");
    weapSkinToAdd->setName("Knife");
    weapSkinToAdd->setPosition(Vec2(135, 23));
    weapSkinToAdd->setScale(3.0f);
    weapSkinToAdd->setRotation(86);
    weaponHandeBone->addSkin(weapSkinToAdd, false);

    // change display
    auto changeBoneDispBtn = cocos2d::ui::Button::create();
    addChild(changeBoneDispBtn);
    changeBoneDispBtn->setPosition(Vec2(VisibleRect::right().x - 60, VisibleRect::top().y - 60));
    changeBoneDispBtn->setTitleText("change bone display");
    changeBoneDispBtn->addClickEventListener([weapSkinToAdd, weaponHandeBone](Ref* sender)
    {
        // or use skeletonNode->display(bone name, skin name, hide)
        if (weapSkinToAdd->isVisible())
            weaponHandeBone->displaySkin("3", true);
        else
        {
            weaponHandeBone->displaySkin(weapSkinToAdd, true);
        }
    });


    /*************** debug draw boundingbox and transforms ***************/
    auto debugDrawNode = DrawNode::create();
    addChild(debugDrawNode);

    auto drawBoxBtn = cocos2d::ui::Button::create();
    addChild(drawBoxBtn);
    drawBoxBtn->setPosition(Vec2(VisibleRect::right().x - 30, VisibleRect::top().y - 45));
    drawBoxBtn->setTitleText("Draw Box");


    drawBoxBtn->addClickEventListener([debugDrawNode](Ref* sender)
    {
        debugDrawNode->setVisible(!debugDrawNode->isVisible());
    });
    skeletonNode->schedule([skeletonNode, weaponHandeBone, debugDrawNode](float interval)
    {
        if (debugDrawNode->isVisible())
        {
            debugDrawNode->clear();
            // skeleton boundingbox
            auto rect = skeletonNode->getBoundingBox();
            cocos2d::Vec2 leftbottom(rect.getMinX(), rect.getMinY());
            cocos2d::Vec2 righttop(rect.getMaxX(), rect.getMaxY());
            debugDrawNode->drawRect(leftbottom, righttop, cocos2d::Color4F::YELLOW);

            // bone boundingbox
            rect = weaponHandeBone->getBoundingBox();
            leftbottom.x = rect.getMinX();
            leftbottom.y = rect.getMinY();
            righttop.x = rect.getMaxX();
            righttop.y = rect.getMaxY();
            cocos2d::Vec2 lefttop(rect.getMinX(), rect.getMaxY());
            cocos2d::Vec2 rightbottom(rect.getMaxX(), rect.getMinY());
            auto skeletonToP = skeletonNode->getNodeToParentAffineTransform();
            auto bonePtoSkeletonPTrans = AffineTransformConcat(
                                             static_cast<BoneNode*>((weaponHandeBone->getParent())
                                                                   )->getNodeToParentAffineTransform(skeletonNode),
                                             skeletonToP);
            leftbottom = PointApplyAffineTransform(leftbottom, bonePtoSkeletonPTrans);
            righttop = PointApplyAffineTransform(righttop, bonePtoSkeletonPTrans);
            lefttop = PointApplyAffineTransform(lefttop, bonePtoSkeletonPTrans);
            rightbottom = PointApplyAffineTransform(rightbottom, bonePtoSkeletonPTrans);
            debugDrawNode->drawLine(leftbottom, rightbottom, Color4F::BLUE);
            debugDrawNode->drawLine(rightbottom, righttop, Color4F::BLUE);
            debugDrawNode->drawLine(righttop, lefttop, Color4F::BLUE);
            debugDrawNode->drawLine(lefttop, leftbottom, Color4F::BLUE);

            // skin boundingbox

            // get displaying nodes
            auto currentskin = weaponHandeBone->getVisibleSkins().front();
            rect = currentskin->getBoundingBox();
            leftbottom.x = rect.getMinX();
            leftbottom.y = rect.getMinY();
            righttop.x = rect.getMaxX();
            righttop.y = rect.getMaxY();
            lefttop.x = rect.getMinX();
            lefttop.y =  rect.getMaxY();
            rightbottom.x = rect.getMaxX();
            rightbottom.y = rect.getMinY();
            auto boneToSkeletonParentTrans = AffineTransformConcat(
                                                 weaponHandeBone->getNodeToParentAffineTransform(skeletonNode), skeletonToP);
            leftbottom = PointApplyAffineTransform(leftbottom, boneToSkeletonParentTrans);
            righttop = PointApplyAffineTransform(righttop, boneToSkeletonParentTrans);
            lefttop = PointApplyAffineTransform(lefttop, boneToSkeletonParentTrans);
            rightbottom = PointApplyAffineTransform(rightbottom, boneToSkeletonParentTrans);

            debugDrawNode->drawLine(leftbottom, rightbottom, Color4F::GREEN);
            debugDrawNode->drawLine(rightbottom, righttop, Color4F::GREEN);
            debugDrawNode->drawLine(righttop, lefttop, Color4F::GREEN);
            debugDrawNode->drawLine(lefttop, leftbottom, Color4F::GREEN);
        }
    }, 0, "update debug draw");


    // change displays , can be use for dress up a skeleton
    auto changeBoneDispsBtn = cocos2d::ui::Button::create();
    addChild(changeBoneDispsBtn);
    changeBoneDispsBtn->setPosition(Vec2(VisibleRect::right().x - 60, VisibleRect::top().y - 75));
    changeBoneDispsBtn->setTitleText("change bone displays");

    std::map < std::string, std::string> boneSkinNames;
    boneSkinNames.insert(std::make_pair("Layer20", "fire"));
    boneSkinNames.insert(std::make_pair("Layer14", "fruit"));
    skeletonNode->addSkinGroup("fruitKnife", boneSkinNames);

    std::map < std::string, std::string> boneSkinNames2;
    boneSkinNames2.insert(std::make_pair("Layer20", "3"));
    boneSkinNames2.insert(std::make_pair("Layer14", "hat"));
    skeletonNode->addSkinGroup("cowboy", boneSkinNames2);

    changeBoneDispsBtn->addClickEventListener([skeletonNode, this](Ref* sender)
    {
        if (!_changedDisplays)
        {
            skeletonNode->changeSkins("fruitKnife");
            _changedDisplays = true;
        }
        else
        {
            skeletonNode->changeSkins("cowboy");
            _changedDisplays = false;
        }
    });


    /*********** test cases for bugs        **********/
    // bug: #13060 https://github.com/cocos2d/cocos2d-x/issues/13060
    // bug: bone draw at the other edge when move to outside right edge.
    BoneNode* bugtestBoneNode = BoneNode::create(500);
    bugtestBoneNode->setRotation(-10);
    bugtestBoneNode->retain();
    bugtestBoneNode->setDebugDrawEnabled(true);
    bugtestBoneNode->setPosition(Vec2(1500, VisibleRect::top().y - 90));
    auto bug13060Btn = cocos2d::ui::Button::create();
    bug13060Btn->setPosition(Vec2(VisibleRect::right().x - 30, VisibleRect::top().y - 90));
    bug13060Btn->setTitleText("bug #13060");
    addChild(bug13060Btn);
    bug13060Btn->addClickEventListener([bugtestBoneNode, skeletonNode](Ref* sender)
    {
        if (bugtestBoneNode->getParent() == nullptr)
            skeletonNode->addChild(bugtestBoneNode);
        else
            bugtestBoneNode->removeFromParent();
        // bug fixed while bugtestBoneNode not be drawn at the bottom edge
    });

    // bug: #13005 https://github.com/cocos2d/cocos2d-x/issues/#13005
    // bug: BoneNode 's debugdraw can not be controlled by ancestor's visible
    auto leftleg = skeletonNode->getBoneNode("Layer26");
    auto bug13005Btn = cocos2d::ui::Button::create();
    addChild(bug13005Btn);
    bug13005Btn->setPosition(Vec2(VisibleRect::right().x - 30, VisibleRect::top().y - 105));
    bug13005Btn->setTitleText("bug #13005");
    bug13005Btn->addClickEventListener([leftleg](Ref* sender)
    {
        leftleg->setVisible(!leftleg->isVisible());
        // bug fixed while leftleg's child hide with leftleg's visible
    });


    /*************    Skeleton nest Skeleton test       *************/
    auto nestSkeletonBtn = cocos2d::ui::Button::create();
    nestSkeletonBtn->setTitleText("Skeleton Nest");
    nestSkeletonBtn->setPosition(Vec2(VisibleRect::right().x - 40, VisibleRect::top().y - 120));
    addChild(nestSkeletonBtn);
    auto nestSkeleton = static_cast<SkeletonNode*>(CSLoader::createNode("ActionTimeline/DemoPlayer_skeleton.csb"));
    nestSkeleton->retain();
    ActionTimeline* nestSkeletonAction = action->clone();
    nestSkeletonAction->retain();
    nestSkeleton->runAction(nestSkeletonAction);
    nestSkeleton->setScale(0.2f);
    nestSkeleton->setPosition(150, 300);
    nestSkeletonAction->gotoFrameAndPlay(0);
    // show debug draws, or comment this for hide bones draws
    for (auto& nestbonechild : nestSkeleton->getAllSubBonesMap())
    {
        nestbonechild.second->setDebugDrawEnabled(true);
    }

    nestSkeletonBtn->addClickEventListener([leftleg, nestSkeleton, nestSkeletonAction](Ref* sender)
    {
        if (nestSkeleton->getParent() == nullptr)
        {
            leftleg->addChild(nestSkeleton);
        }
        else
        {
            nestSkeleton->removeFromParentAndCleanup(false);
        }
    });
}