// // RedBakeNode.cpp // cocos2d_libs // // Created by Liang zhong on 2022/11/11. // #include "RedBakeNode.h" #include "RedMixAnimationBakeModel.h" #include "CommandBuff.h" #include "RedSpineBakeManage.h" RedBakeNode *RedBakeNode::create(const std::string aFileName){ RedBakeNode *rb = new RedBakeNode(aFileName); rb->autorelease(); return rb; } RedBakeNode *RedBakeNode::create(){ RedBakeNode *rb = new RedBakeNode(); rb->autorelease(); return rb; } RedBakeNode::RedBakeNode() { zeroPos = Vec2(0,0); _drawSelf = false; setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP, NULL)); } RedBakeNode::RedBakeNode(const std::string aFileName){ _fileName = aFileName; zeroPos = Vec2(0,0); setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP, NULL)); _schUpdate = CC_SCHEDULE_SELECTOR(RedBakeNode::update); this->schedule(_schUpdate, 0); } RedBakeNode::~RedBakeNode(){ CC_SAFE_RELEASE(_texture); this->unschedule(CC_SCHEDULE_SELECTOR(RedBakeNode::update)); for (int i=0; i<_slotFrameArray.size(); i++) { delete[] _slotFrameArray[i].triangles.verts; } for (V3F_C4B_T2F *item : _vertArr){ delete[] item; } _vertArr.clear(); // 清空容器 } void RedBakeNode::setBakeDataFile(const std::string& aFileName) { _fileNameFull = aFileName; if (_fileNameFull.empty()) { _fileName = ""; _stopSelfDraw(); return; } std::string path = ""; auto p = _fileNameFull.rfind("/"); if (p != std::string::npos) { path = _fileNameFull.substr(0, p+1); _fileName = _fileNameFull.substr(p+1); } else { _fileName = _fileNameFull; } if (_fileName.find(".rb") != std::string::npos) { _fileName = _fileName.substr(0, _fileName.size()-3); } // FIXME: RedSpineBakeManage::getInstance()->loadAnimationFile(_fileName, path); } const std::string& RedBakeNode::getBakeDataFile() const { return _fileNameFull; } std::vector RedBakeNode::getAnimations() { std::vector anims; RedSpineBakeManage::getInstance()->fetchAnimations(_fileName, anims); return anims; } void RedBakeNode::setBakeFrame(RedBakeNodeFrame frame) { _curFrame = frame; if (_curFrame.animationName.size() == 0) { // 清空显示 _stopSelfDraw(); } else { if (switchAnimation(_curFrame.animationName, _curFrame.loop)) { stopAutoDraw(); _startSelfDraw(); updateSelfFunc(_curFrame.elapsedTime, true); _firstDraw = false; } } } RedBakeNodeFrame RedBakeNode::getBakeFrame() { return _curFrame; } float RedBakeNode::getAnimDuration(const std::string& tl) { auto anim = RedSpineBakeManage::getInstance()->getAnimateByName(_fileName, tl); if (anim) { return (float)anim->getTotalBakeFrame() / (float)anim->getCurrentFrameRate(); } return .0; } void RedBakeNode::drawBake (cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t transformFlags){ cocos2d::Texture2D* texture = _texture; if (_isFlipX) { newTransform = transform; newTransform.m[0] *= -1; // 将X轴缩放值取反 } //如果当前节点没有现实,每次都判断是否在屏幕内,如果已经显示每五帧判断一次 if (isInScreen==true) { if (_currentBakeFrame%5==0) { animationIsInscreen(); } }else{ animationIsInscreen(); } if (isInScreen) { Point offsetPos = {0,0}; if (_fatherNode!=nullptr) { if (_fatherNodePosIndex==-1) { _fatherNodePosIndex = _fatherNode->getFatherBoneOffsetIndex(_fatherNodeBoneName); } Point posInt; if (_fatherNodePosIndex==-1) { posInt = _fatherNode->getFatherBoneOffset(_fatherNodeBoneName); }else{ posInt = _fatherNode->getFatherBoneOffsetByIndex(_fatherNodePosIndex); } offsetPos.x = (float)posInt.x + _disPos.x; offsetPos.y = (float)posInt.y + _disPos.y; } for (int i = 0; i<_currentBakeAnimation->getSlotCount(); i++) { RedSlotBakeModel *slotBake = _currentBakeAnimation->getSlotByDrawOrder(i, _currentBakeFrame,_currentDrawOrderIndex); const std::string& slotName = slotBake->getSlotName(); BKAE_FRAME_PLAY &bfp = *_slotFrameArrayVect[slotBake->getSlotIndex()];//这行性能远快于下面的map,作用一样 if(_attachNodeMapSize>0){ if(bfp.hasAttachNode){ if (_singleAttachNode==nullptr) { _attachNodeMap[slotName]->drawSelfFunc(renderer, transform, transformFlags); }else{ _singleAttachNode->drawSelfFunc(renderer, transform, transformFlags); } } } if(slotBake->slotShoudBeDrawSlot()){ //判断当前slot在当前帧是否需要跳过渲染,判断是否在屏幕内 if(slotBake->getFrameSkip(_currentBakeFrame)){ slotBake->setFrameSkip(_currentBakeFrame, bfp); }else{ if (texture==nullptr) { texture = slotBake->getTexture(_currentBakeFrame); } BlendFunc &blendFuc = slotBake->getBlendFunc(_currentBakeFrame); V3F_C4B_T2F *beginVert = nullptr; bool shouldMerge = false; if (_currentBakeFrame>1&&_currentBakeFrame<=_totalBakeFrame) { shouldMerge = true; } beginVert = _vertArr[bfp.vertMapIndex]; slotBake->getTriangles(_currentBakeFrame,bfp, offsetPos, _currentFrameFracpart, beginVert,shouldMerge); cocos2d::TrianglesCommand::Triangles triangles = bfp.triangles; triangles.verts = beginVert; if(slotName == "slot_duihua"){ _speechPos = Vec2(offsetPos.x,offsetPos.y); // CCLOG("[sc bake : speechPos %f, %f", _speechPos.x, _speechPos.y); } #if COCOS2D_VERSION < 0x00040000 if(_isFlipX){ CommandBuff::getInstance()->addCommand(renderer, _globalZOrder, texture, _glProgramState, blendFuc, triangles, newTransform, transformFlags); }else{ CommandBuff::getInstance()->addCommand(renderer, _globalZOrder, texture, _glProgramState, blendFuc, triangles, transform, transformFlags); } #else if(_isFlipX){ CommandBuff::getInstance()->addCommand(renderer, _globalZOrder, texture, blendFuc, triangles, newTransform, transformFlags); }else{ CommandBuff::getInstance()->addCommand(renderer, _globalZOrder, texture, blendFuc, triangles, transform, transformFlags); } #endif } } } } } void RedBakeNode::drawMixBake (cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t transformFlags){ RedMixAnimationBakeModel *mixAni = (RedMixAnimationBakeModel *)_currentBakeAnimation; if (isInScreen==true) { if (_currentBakeFrame%5==0) { animationIsInscreen(); } }else{ animationIsInscreen(); } if (isInScreen) { Point offsetPos = {0,0}; if (_fatherNode!=nullptr) { Point posInt = _fatherNode->getFatherBoneOffset(_fatherNodeBoneName); offsetPos.x = (float)posInt.x + _disPos.x; offsetPos.y = (float)posInt.y + _disPos.y; // CCLOG("%d/%d---%f,%f",_currentBakeFrame,_totalBakeFrame,offsetPos.x,offsetPos.y); } std::vector keys = mixAni->getMixBakeFrameArrAllKey(); for (int i = 0; igetTexture(zOrderIndex); BlendFunc blendFuc = mixAni->getBlendFunc(zOrderIndex); cocos2d::TrianglesCommand::Triangles triangles = mixAni->getTriangles(zOrderIndex,_realCurrentBakeFrame); for (int j=0; jaddCommand(renderer, _globalZOrder, texture, _glProgramState, blendFuc, triangles, transform, transformFlags); #else CommandBuff::getInstance()->addCommand(renderer, _globalZOrder, texture, blendFuc, triangles, transform, transformFlags); #endif } } } void RedBakeNode::draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transform, uint32_t transformFlags) { if (_drawSelf) { drawSelfFunc(renderer, transform, transformFlags); } } void RedBakeNode::update (float deltaTime){ if (_drawSelf==false) { return; } updateSelfFunc(deltaTime); // CCLOG("%d",_currentBakeFrame); } void RedBakeNode::updateSelfFunc(float deltaTime, bool bInFrameMode){ if (_currentBakeAnimation==nullptr) { return ; } if (_attachNodeMapSize>0) { if (_singleAttachNode) { _singleAttachNode->updateSelfFunc(deltaTime, bInFrameMode); if (_attachNodeMapHasChanged == true) { _attachNodeMapHasChanged = false; } }else{ for (auto it = _attachNodeMap.begin(); it != _attachNodeMap.end(); ++it) { RedBakeNode* value = it->second; value->updateSelfFunc(deltaTime, bInFrameMode); if (_attachNodeMapHasChanged == true) { _attachNodeMapHasChanged = false; break; } } } } if (_currentAnimationIsFinished==false) { //如果动画还没播放完毕 if (_currentAnimationIsPaused==false) { float delta = deltaTime*(float)_currentBakeAnimation->getCurrentFrameRate(); _realCurrentBakeFrame += delta; // CCLOG("%f",_realCurrentBakeFrame); float intpart; _currentFrameFracpart = modf(_realCurrentBakeFrame, &intpart); // _currentBakeFrame = intpart+1; _currentBakeFrame = MIN(intpart+1, _totalBakeFrame); // CCLOG("sc : 帧数 %d", _currentBakeFrame); // CCLOG("%d",_currentBakeFrame); // _currentBakeFrame++;//如果没有暂停且时间够一帧就播放一个动画 } //因为最后一帧的时长不一定是一整帧,我们是按15帧烘培的,spine是按30帧K的,所以可能存在最后一帧误差,烘培的时候算上了这个 if (_realCurrentBakeFrame>=(_totalBakeFrame-1.0+_currentBakeAnimation->getLastDrawOverTime())) { if (_currentBakeAnimation->isMixAnimation()) { //如果是动画混合播完,就接下一个动画 RedMixAnimationBakeModel *mixAni = (RedMixAnimationBakeModel *)_currentBakeAnimation; if (_listener) { _listener(_currentBakeAnimation->getAimationName(),RBN_STATE::MixPreAniEnd); } _currentBakeAnimation = mixAni->getNextAnimation(); delete mixAni; resetPlayInfo(); _currentAnimationIsFinished = false; _currentAnimationIsPaused = false; }else{ if(_isLoop==true){ // 单帧模式下不能每次都设置到第一、二帧 if (bInFrameMode) { _currentBakeFrame = (int(_realCurrentBakeFrame) % _totalBakeFrame) + 1; } else { // float tmp_realCurrentBakeFrame =_realCurrentBakeFrame-_totalBakeFrame; // resetPlayInfo();//如果循环就重制 _realCurrentBakeFrame = 1+_currentFrameFracpart;//把刚刚上次循环多余的时间也加上 float intpart; _currentFrameFracpart = modf(_realCurrentBakeFrame, &intpart); _currentBakeFrame = MIN(intpart+1, _totalBakeFrame); } } else { //如果不循环就停滞,同时通知 _currentBakeFrame = _totalBakeFrame; _currentAnimationIsFinished = true; if (_listener) { _listener(_currentBakeAnimation->getAimationName(),RBN_STATE::Complete); } } } } } } bool RedBakeNode::switchAnimation(const std::string& animationName, bool loop){ if (_currentAnimationIsFinished==false) { stopCurrentAnimation();//如果有正在播放的,就先停下来 } _currentBakeAnimation = RedSpineBakeManage::getInstance()->getAnimateByName(_fileName, animationName); if (_currentBakeAnimation==nullptr) { CCASSERT(0, ("动画没有找到" + animationName).c_str()); return false; } _isFirstReset = true; resetPlayInfo(); _isLoop = loop; _currentAnimationIsFinished = false; _currentAnimationIsPaused = false; _reloadVertMap(); return true; } bool RedBakeNode::playAnimation (const std::string& animationName, bool loop, float spdRate) { if (_fileName.empty()) { _stopSelfDraw(); return false; } if (switchAnimation(animationName, loop)) { startAutoDraw(); } float frameRate = 15; frameRate*=spdRate; if(_currentBakeAnimation){ _currentBakeAnimation->setCurrentFrameRate(frameRate); handleEvent(_eventCallBack); } return true; } bool RedBakeNode::stopCurrentAnimation(){ if (_currentAnimationIsFinished) { return false; } _currentAnimationIsFinished = true; _currentAnimationIsPaused = false; if (_listener) { _listener(_currentBakeAnimation->getAimationName(),RBN_STATE::Break); } return true; } bool RedBakeNode::IscurrentAnimationFinished(){ return _currentAnimationIsFinished; } bool RedBakeNode::pauseCurrentAnimation(){ if (_currentAnimationIsFinished) { return false; } _currentAnimationIsPaused = true; if (_listener) { _listener(_currentBakeAnimation->getAimationName(),RBN_STATE::Pause); } return true; } bool RedBakeNode::resumeCurrentAnimation(){ if (_currentAnimationIsFinished) { return false; } _currentAnimationIsPaused = false; if (_listener) { _listener(_currentBakeAnimation->getAimationName(),RBN_STATE::Resume); } return true; } bool RedBakeNode::currentAnimationIsPaused(){ return _currentAnimationIsPaused; } bool RedBakeNode::playMixAnimation(const std::string& animationName, bool loop,float aMixTime){ Point pos; pos.x = 0; pos.y = 0; return playMixAnimation(animationName, loop, aMixTime, pos); } bool RedBakeNode::playMixAnimation(const std::string& animationName, bool loop,float aMixTime,Point aDis){ if (_currentAnimationIsFinished==false) { //如果当前动画没有结束 if(_currentBakeAnimation->isMixAnimation()){ return false; } } RedMixAnimationBakeModel *mixAnimation = new RedMixAnimationBakeModel(); const std::map aPreAniBakeFrame = _slotFrameArray; bool success = mixAnimation->setMixInfo(_currentBakeFrame, aMixTime, aPreAniBakeFrame, animationName,_currentBakeAnimation->getAimationName(), _fileName,aDis); if (success) { _currentBakeAnimation = mixAnimation; _currentBakeFrame = 0; _isLoop = loop; _totalBakeFrame = _currentBakeAnimation->getTotalBakeFrame(); _currentAnimationIsFinished = false; }else{ delete mixAnimation; playAnimation(animationName, loop); } return true; } void RedBakeNode::updateLoop(bool loop){ _isLoop = loop; } //当前动画是否X翻转 void RedBakeNode::setFlipX(bool aFlipX){ _isFlipX = aFlipX; } //当前动画是否翻转 bool RedBakeNode::isFlipX(){ return _isFlipX; } void RedBakeNode::setBakeSpineID(std::string aId){ _bakeSpineID = aId; } void RedBakeNode::resetPlayInfo(){ _firstDraw = true; _currentBakeFrame = 1; _realCurrentBakeFrame = 1.0; _currentFrameFracpart = 0; _currentDrawOrderIndex = 0; _totalBakeFrame = _currentBakeAnimation->getTotalBakeFrame(); if (_slotFrameArray.size()==0||_isFirstReset==true) { int maxIndex = 1; for (int i = 0; i<_currentBakeAnimation->getSlotCount(); i++) { int key = _currentBakeAnimation->getAllSlotKey()[i]; RedSlotBakeModel* slot = _currentBakeAnimation->getSlotFromKey(key); _slotFrameArray.insert(std::pair(slot->getSlotIndex(), BKAE_FRAME_PLAY())); if (slot->getSlotIndex()>maxIndex) { maxIndex = slot->getSlotIndex(); } } _slotFrameArrayVect.clear(); _slotFrameArrayVect.resize(maxIndex+1); for (auto it = _slotFrameArray.begin(); it != _slotFrameArray.end(); ++it){ int key = it->first; BKAE_FRAME_PLAY &value = it->second; _slotFrameArrayVect[key] = &value; } } for (int i = 0; i<_currentBakeAnimation->getSlotCount(); i++) { int key = _currentBakeAnimation->getAllSlotKey()[i]; _currentBakeAnimation->getSlotFromKey(key)->initBKAE_FRAME_PLAY(_slotFrameArray.at(key)); } _isFirstReset = false; } void RedBakeNode::setRBNListener(RBNListener& aListener){ _listener = aListener; } //判断某个Slot是否在屏幕内 bool RedBakeNode::animationIsInscreen(){ if(_currentBakeAnimation==nullptr){ isInScreen = false; return isInScreen; } int bigMore = 100; Vec2 leftBottom = _currentBakeAnimation->getLeftBottomSubSizePos(); Vec2 rightTop = _currentBakeAnimation->getRightTopSubSizePos(); Vec2 globalPos = convertToWorldSpace(zeroPos); Vec2 leftBottomWorld = leftBottom+globalPos; Vec2 rightTopWorld = rightTop+globalPos; auto winSize = Director::getInstance()->getWinSize(); if (leftBottomWorld.x<(0-bigMore)||leftBottomWorld.y<(0-bigMore)||rightTopWorld.x>(winSize.width+bigMore)||rightTopWorld.y>(winSize.height+bigMore)) { // CCLOG("跑出去了------------%s",_currentBakeAnimation->getAimationName().c_str()); isInScreen = false; return isInScreen; } isInScreen = true; return isInScreen; } void RedBakeNode::mergeTwoVert(V3F_C4B_T2F *beginVert,V3F_C4B_T2F *endVert,int aCount,Point offsetPos){ if (aCount==0) { return; } float dis = _currentFrameFracpart; if (beginVert[0].colors!=endVert[0].colors) { for (int i=0; ifirst); }else{ _singleAttachNode = nullptr; } _attachNodeMapSize = (int)_attachNodeMap.size(); } void RedBakeNode::addAttachNodeBeforeSlot(std::string aSlotName,RedBakeNode * aBakeNode){ aBakeNode->_stopSelfDraw(); _attachNodeMap.insert(std::pair(aSlotName,aBakeNode)); _attachNodeMapHasChanged = true; attachNodeBeUpdated(); } void RedBakeNode::removeAttachNode(std::string aSlotName){ auto iter = _attachNodeMap.find(aSlotName); if (iter != _attachNodeMap.end()) { RedBakeNode * aBakeNode = iter->second; aBakeNode->_startSelfDraw(); _attachNodeMap.erase(iter); _attachNodeMapHasChanged = true; } attachNodeBeUpdated(); } //删除全部绘制附着slot void RedBakeNode::removeAllAttachNode(){ for (auto it = _attachNodeMap.begin(); it != _attachNodeMap.end(); ++it) { RedBakeNode* value = it->second; value->_startSelfDraw(); } _attachNodeMap.clear(); _attachNodeMapHasChanged = true; attachNodeBeUpdated(); } void RedBakeNode::startAutoDraw() { _startSelfDraw(); if (_schUpdate == nullptr) { _schUpdate = CC_SCHEDULE_SELECTOR(RedBakeNode::update); this->schedule(_schUpdate, 0); } } void RedBakeNode::stopAutoDraw() { if (_schUpdate) { this->unschedule(_schUpdate); _schUpdate = nullptr; } } //关闭自我绘制功能,由其他RedBakeNode来绘制 void RedBakeNode::_stopSelfDraw(){ _drawSelf = false; } //开启自我绘制功能 void RedBakeNode::_startSelfDraw(){ _drawSelf = true; } void RedBakeNode::drawSelfFunc(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transform, uint32_t transformFlags){ if (_currentBakeAnimation==nullptr) { return ; } if (_firstDraw) { _firstDraw = false; updateSelfFunc(0); } if(_currentBakeAnimation!=nullptr){ if (_totalBakeFrame<1) { _totalBakeFrame = _currentBakeAnimation->getTotalBakeFrame(); resetPlayInfo(); } if(_currentBakeAnimation->isMixAnimation()){ drawMixBake(renderer, transform, transformFlags); }else{ drawBake(renderer, transform, transformFlags); } } } void RedBakeNode::handleEvent(std::function cb){ _currentBakeAnimation->handleEventByFrame(this, cb); } //添加父Bakenode,确保自己和父node一起运动,aBoneName是父node关于自己的连接点 void RedBakeNode::addFatherNodeBindMove(std::string aBoneName,RedBakeNode * aBakeNode){ if(aBoneName.length()>0&&aBakeNode){ _fatherNode = aBakeNode; _fatherNodeBoneName = aBoneName; _fatherNodePosIndex = -1; } } //添加父Bakenode,确保自己和父node一起运动,aBoneName是父node关于自己的连接点 void RedBakeNode::removeFatherNode(){ _fatherNode = nullptr; _fatherNodeBoneName = ""; } //获取父节点对应bone的偏移量 const Point RedBakeNode::getFatherBoneOffset(std::string aBoneName){ // if(_currentBakeAnimation==nullptr){ // return Point(0,0); // } if (_currentBakeFrame<=1) {//_currentBakeFrame是从1开始的,getABakeModePoint是从0 return _currentBakeAnimation->getABakeModePoint(aBoneName, 0); } const Point &begin = _currentBakeAnimation->getABakeModePoint(aBoneName, _currentBakeFrame-2); const Point &end = _currentBakeAnimation->getABakeModePoint(aBoneName, _currentBakeFrame-1); Point res; res.x = begin.x + _currentFrameFracpart*(end.x - begin.x); res.y = begin.y + _currentFrameFracpart*(end.y - begin.y); return res; } //获取父节点对应bone的偏移量 const Point RedBakeNode::getFatherBoneOffsetByIndex(int aIndex){ if (_currentBakeFrame<=1) {//_currentBakeFrame是从1开始的,getABakeModePoint是从0 return _currentBakeAnimation->getABakeModePoint(aIndex, 0); } const Point &begin = _currentBakeAnimation->getABakeModePoint(aIndex, _currentBakeFrame-2); const Point &end = _currentBakeAnimation->getABakeModePoint(aIndex, _currentBakeFrame-1); Point res; res.x = begin.x + _currentFrameFracpart*(end.x - begin.x); res.y = begin.y + _currentFrameFracpart*(end.y - begin.y); return res; } //获取父节点对应bone的偏移量对应的数组索引,用来加速性能 int RedBakeNode::getFatherBoneOffsetIndex(std::string aBoneName){ return _currentBakeAnimation->getBoneAttachsSearchIndex(aBoneName); } void RedBakeNode::_reloadVertMap(){ for (V3F_C4B_T2F *item : _vertArr){ delete[] item; } _vertArr.clear(); // 清空容器 for (int i = 0; i<_currentBakeAnimation->getSlotCount(); i++) { int key = _currentBakeAnimation->getAllSlotKey()[i]; RedSlotBakeModel *slotBake = _currentBakeAnimation->getSlotFromKey(key); int vertCount = slotBake->getVertCount(); _vertArr.push_back(new V3F_C4B_T2F[vertCount]); BKAE_FRAME_PLAY &bfp = _slotFrameArray.at(slotBake->getSlotIndex()); if (_attachNodeMap.count(slotBake->getSlotName()) > 0) { bfp.hasAttachNode = true; } bfp.vertMapIndex =(int)_vertArr.size()-1; } } //用来记录多个spine合并播放时,骨骼连接点初始的位置差 void RedBakeNode::setDisPos(Point aPos){ _disPos.x = aPos.x; _disPos.y = aPos.y; } //用来指定动画起始位子,这样上下半身走路可以同步,不会同手同脚 void RedBakeNode::updateRealCurrentBakeFrame(float aReal){ _realCurrentBakeFrame = aReal; float intpart; _currentFrameFracpart = modf(_realCurrentBakeFrame, &intpart); _currentBakeFrame = MIN(intpart+1, _totalBakeFrame); } //获取当前播放的位置 float RedBakeNode::getRealCurrentBakeFrame(){ return _realCurrentBakeFrame; } void RedBakeNode::setTexture(Texture2D * texture) { CC_SAFE_RETAIN(texture); CC_SAFE_RELEASE(_texture); _texture = texture; } void RedBakeNode::setEventCallBack(std::function cb){ _eventCallBack = cb; } Vec2 RedBakeNode::getSpeechPos(){ Vec2 pos = _speechPos; return pos; }