CCParticleBatchNode.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. /*
  2. * Copyright (C) 2009 Matt Oswald
  3. * Copyright (c) 2009-2010 Ricardo Quesada
  4. * Copyright (c) 2010-2012 cocos2d-x.org
  5. * Copyright (c) 2011 Zynga Inc.
  6. * Copyright (c) 2011 Marco Tillemans
  7. * Copyright (c) 2013-2016 Chukong Technologies Inc.
  8. * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
  9. *
  10. * http://www.cocos2d-x.org
  11. *
  12. * Permission is hereby granted, free of charge, to any person obtaining a copy
  13. * of this software and associated documentation files (the "Software"), to deal
  14. * in the Software without restriction, including without limitation the rights
  15. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  16. * copies of the Software, and to permit persons to whom the Software is
  17. * furnished to do so, subject to the following conditions:
  18. *
  19. * The above copyright notice and this permission notice shall be included in
  20. * all copies or substantial portions of the Software.
  21. *
  22. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  23. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  24. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  25. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  26. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  27. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  28. * THE SOFTWARE.
  29. *
  30. */
  31. #include "2d/CCParticleBatchNode.h"
  32. #include "2d/CCGrid.h"
  33. #include "2d/CCParticleSystem.h"
  34. #include "renderer/CCTextureCache.h"
  35. #include "renderer/CCQuadCommand.h"
  36. #include "renderer/CCRenderer.h"
  37. #include "renderer/CCTextureAtlas.h"
  38. #include "base/CCProfiling.h"
  39. #include "base/ccUTF8.h"
  40. NS_CC_BEGIN
  41. ParticleBatchNode::ParticleBatchNode()
  42. : _textureAtlas(nullptr)
  43. {
  44. }
  45. ParticleBatchNode::~ParticleBatchNode()
  46. {
  47. CC_SAFE_RELEASE(_textureAtlas);
  48. }
  49. /*
  50. * creation with Texture2D
  51. */
  52. ParticleBatchNode* ParticleBatchNode::createWithTexture(Texture2D *tex, int capacity/* = kParticleDefaultCapacity*/)
  53. {
  54. ParticleBatchNode * p = new (std::nothrow) ParticleBatchNode();
  55. if( p && p->initWithTexture(tex, capacity))
  56. {
  57. p->autorelease();
  58. return p;
  59. }
  60. CC_SAFE_DELETE(p);
  61. return nullptr;
  62. }
  63. /*
  64. * creation with File Image
  65. */
  66. ParticleBatchNode* ParticleBatchNode::create(const std::string& imageFile, int capacity/* = kParticleDefaultCapacity*/)
  67. {
  68. ParticleBatchNode * p = new (std::nothrow) ParticleBatchNode();
  69. if( p && p->initWithFile(imageFile, capacity))
  70. {
  71. p->autorelease();
  72. return p;
  73. }
  74. CC_SAFE_DELETE(p);
  75. return nullptr;
  76. }
  77. /*
  78. * init with Texture2D
  79. */
  80. bool ParticleBatchNode::initWithTexture(Texture2D *tex, int capacity)
  81. {
  82. _textureAtlas = new (std::nothrow) TextureAtlas();
  83. _textureAtlas->initWithTexture(tex, capacity);
  84. _children.reserve(capacity);
  85. _blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
  86. setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR, tex));
  87. return true;
  88. }
  89. /*
  90. * init with FileImage
  91. */
  92. bool ParticleBatchNode::initWithFile(const std::string& fileImage, int capacity)
  93. {
  94. Texture2D *tex = Director::getInstance()->getTextureCache()->addImage(fileImage);
  95. return initWithTexture(tex, capacity);
  96. }
  97. // ParticleBatchNode - composition
  98. // override visit.
  99. // Don't call visit on it's children
  100. void ParticleBatchNode::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags)
  101. {
  102. // CAREFUL:
  103. // This visit is almost identical to Node#visit
  104. // with the exception that it doesn't call visit on it's children
  105. //
  106. // The alternative is to have a void Sprite#visit, but
  107. // although this is less maintainable, is faster
  108. //
  109. if (!_visible)
  110. {
  111. return;
  112. }
  113. uint32_t flags = processParentFlags(parentTransform, parentFlags);
  114. if (isVisitableByVisitingCamera())
  115. {
  116. // IMPORTANT:
  117. // To ease the migration to v3.0, we still support the Mat4 stack,
  118. // but it is deprecated and your code should not rely on it
  119. Director* director = Director::getInstance();
  120. director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
  121. director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
  122. draw(renderer, _modelViewTransform, flags);
  123. director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
  124. }
  125. }
  126. // override addChild:
  127. void ParticleBatchNode::addChild(Node * aChild, int zOrder, int tag)
  128. {
  129. CCASSERT( aChild != nullptr, "Argument must be non-nullptr");
  130. CCASSERT( dynamic_cast<ParticleSystem*>(aChild) != nullptr, "CCParticleBatchNode only supports QuadParticleSystems as children");
  131. ParticleSystem* child = static_cast<ParticleSystem*>(aChild);
  132. CCASSERT( child->getTexture()->getName() == _textureAtlas->getTexture()->getName(), "CCParticleSystem is not using the same texture id");
  133. addChildByTagOrName(child, zOrder, tag, "", true);
  134. }
  135. void ParticleBatchNode::addChild(Node * aChild, int zOrder, const std::string &name)
  136. {
  137. CCASSERT( aChild != nullptr, "Argument must be non-nullptr");
  138. CCASSERT( dynamic_cast<ParticleSystem*>(aChild) != nullptr, "CCParticleBatchNode only supports QuadParticleSystems as children");
  139. ParticleSystem* child = static_cast<ParticleSystem*>(aChild);
  140. CCASSERT( child->getTexture()->getName() == _textureAtlas->getTexture()->getName(), "CCParticleSystem is not using the same texture id");
  141. addChildByTagOrName(child, zOrder, 0, name, false);
  142. }
  143. void ParticleBatchNode::addChildByTagOrName(ParticleSystem* child, int zOrder, int tag, const std::string &name, bool setTag)
  144. {
  145. // If this is the 1st children, then copy blending function
  146. if (_children.empty())
  147. {
  148. setBlendFunc(child->getBlendFunc());
  149. }
  150. CCASSERT( _blendFunc.src == child->getBlendFunc().src && _blendFunc.dst == child->getBlendFunc().dst, "Can't add a ParticleSystem that uses a different blending function");
  151. //no lazy sorting, so don't call super addChild, call helper instead
  152. int pos = 0;
  153. if (setTag)
  154. pos = addChildHelper(child, zOrder, tag, "", true);
  155. else
  156. pos = addChildHelper(child, zOrder, 0, name, false);
  157. //get new atlasIndex
  158. int atlasIndex = 0;
  159. if (pos != 0)
  160. {
  161. ParticleSystem* p = static_cast<ParticleSystem*>(_children.at(pos-1));
  162. atlasIndex = p->getAtlasIndex() + p->getTotalParticles();
  163. }
  164. else
  165. {
  166. atlasIndex = 0;
  167. }
  168. insertChild(child, atlasIndex);
  169. // update quad info
  170. child->setBatchNode(this);
  171. }
  172. // don't use lazy sorting, reordering the particle systems quads afterwards would be too complex
  173. // FIXME: research whether lazy sorting + freeing current quads and calloc a new block with size of capacity would be faster
  174. // FIXME: or possibly using vertexZ for reordering, that would be fastest
  175. // this helper is almost equivalent to Node's addChild, but doesn't make use of the lazy sorting
  176. int ParticleBatchNode::addChildHelper(ParticleSystem* child, int z, int aTag, const std::string &name, bool setTag)
  177. {
  178. CCASSERT( child != nullptr, "Argument must be non-nil");
  179. CCASSERT( child->getParent() == nullptr, "child already added. It can't be added again");
  180. _children.reserve(4);
  181. //don't use a lazy insert
  182. auto pos = searchNewPositionInChildrenForZ(z);
  183. _children.insert(pos, child);
  184. if (setTag)
  185. child->setTag(aTag);
  186. else
  187. child->setName(name);
  188. child->setLocalZOrder(z);
  189. child->setParent(this);
  190. if( _running )
  191. {
  192. child->onEnter();
  193. child->onEnterTransitionDidFinish();
  194. }
  195. return pos;
  196. }
  197. // Reorder will be done in this function, no "lazy" reorder to particles
  198. void ParticleBatchNode::reorderChild(Node * aChild, int zOrder)
  199. {
  200. CCASSERT( aChild != nullptr, "Child must be non-nullptr");
  201. CCASSERT( dynamic_cast<ParticleSystem*>(aChild) != nullptr, "CCParticleBatchNode only supports QuadParticleSystems as children");
  202. CCASSERT( _children.contains(aChild), "Child doesn't belong to batch" );
  203. ParticleSystem* child = static_cast<ParticleSystem*>(aChild);
  204. if( zOrder == child->getLocalZOrder() )
  205. {
  206. return;
  207. }
  208. // no reordering if only 1 child
  209. if (!_children.empty())
  210. {
  211. int newIndex = 0, oldIndex = 0;
  212. getCurrentIndex(&oldIndex, &newIndex, child, zOrder);
  213. if( oldIndex != newIndex )
  214. {
  215. // reorder _children->array
  216. child->retain();
  217. _children.erase(oldIndex);
  218. _children.insert(newIndex, child);
  219. child->release();
  220. // save old altasIndex
  221. int oldAtlasIndex = child->getAtlasIndex();
  222. // update atlas index
  223. updateAllAtlasIndexes();
  224. // Find new AtlasIndex
  225. int newAtlasIndex = 0;
  226. for (const auto& iter : _children)
  227. {
  228. auto node = static_cast<ParticleSystem*>(iter);
  229. if( node == child )
  230. {
  231. newAtlasIndex = child->getAtlasIndex();
  232. break;
  233. }
  234. }
  235. // reorder textureAtlas quads
  236. _textureAtlas->moveQuadsFromIndex(oldAtlasIndex, child->getTotalParticles(), newAtlasIndex);
  237. child->updateWithNoTime();
  238. }
  239. }
  240. child->setLocalZOrder(zOrder);
  241. }
  242. void ParticleBatchNode::getCurrentIndex(int* oldIndex, int* newIndex, Node* child, int z)
  243. {
  244. bool foundCurrentIdx = false;
  245. bool foundNewIdx = false;
  246. int minusOne = 0;
  247. auto count = _children.size();
  248. for( int i=0; i < count; i++ )
  249. {
  250. Node* pNode = _children.at(i);
  251. // new index
  252. if( pNode->getLocalZOrder() > z && ! foundNewIdx )
  253. {
  254. *newIndex = i;
  255. foundNewIdx = true;
  256. if( foundCurrentIdx && foundNewIdx )
  257. {
  258. break;
  259. }
  260. }
  261. // current index
  262. if( child == pNode )
  263. {
  264. *oldIndex = i;
  265. foundCurrentIdx = true;
  266. if( ! foundNewIdx )
  267. {
  268. minusOne = -1;
  269. }
  270. if( foundCurrentIdx && foundNewIdx )
  271. {
  272. break;
  273. }
  274. }
  275. }
  276. if( ! foundNewIdx )
  277. {
  278. *newIndex = static_cast<int>(count);
  279. }
  280. *newIndex += minusOne;
  281. }
  282. int ParticleBatchNode::searchNewPositionInChildrenForZ(int z)
  283. {
  284. auto count = _children.size();
  285. for( int i=0; i < count; i++ )
  286. {
  287. Node *child = _children.at(i);
  288. if (child->getLocalZOrder() > z)
  289. {
  290. return i;
  291. }
  292. }
  293. return static_cast<int>(count);
  294. }
  295. // override removeChild:
  296. void ParticleBatchNode::removeChild(Node* aChild, bool cleanup)
  297. {
  298. // explicit nil handling
  299. if (aChild == nullptr)
  300. return;
  301. CCASSERT( dynamic_cast<ParticleSystem*>(aChild) != nullptr, "CCParticleBatchNode only supports QuadParticleSystems as children");
  302. CCASSERT(_children.contains(aChild), "CCParticleBatchNode doesn't contain the sprite. Can't remove it");
  303. ParticleSystem* child = static_cast<ParticleSystem*>(aChild);
  304. // remove child helper
  305. _textureAtlas->removeQuadsAtIndex(child->getAtlasIndex(), child->getTotalParticles());
  306. // after memmove of data, empty the quads at the end of array
  307. _textureAtlas->fillWithEmptyQuadsFromIndex(_textureAtlas->getTotalQuads(), child->getTotalParticles());
  308. // particle could be reused for self rendering
  309. child->setBatchNode(nullptr);
  310. Node::removeChild(child, cleanup);
  311. updateAllAtlasIndexes();
  312. }
  313. void ParticleBatchNode::removeChildAtIndex(int index, bool doCleanup)
  314. {
  315. removeChild(_children.at(index), doCleanup);
  316. }
  317. void ParticleBatchNode::removeAllChildrenWithCleanup(bool doCleanup)
  318. {
  319. for(const auto &child : _children)
  320. static_cast<ParticleSystem*>(child)->setBatchNode(nullptr);
  321. Node::removeAllChildrenWithCleanup(doCleanup);
  322. _textureAtlas->removeAllQuads();
  323. }
  324. void ParticleBatchNode::draw(Renderer* renderer, const Mat4 & /*transform*/, uint32_t flags)
  325. {
  326. CC_PROFILER_START("CCParticleBatchNode - draw");
  327. if( _textureAtlas->getTotalQuads() == 0 )
  328. {
  329. return;
  330. }
  331. _batchCommand.init(_globalZOrder, getGLProgram(), _blendFunc, _textureAtlas, _modelViewTransform, flags);
  332. renderer->addCommand(&_batchCommand);
  333. CC_PROFILER_STOP("CCParticleBatchNode - draw");
  334. }
  335. void ParticleBatchNode::increaseAtlasCapacityTo(ssize_t quantity)
  336. {
  337. CCLOG("cocos2d: ParticleBatchNode: resizing TextureAtlas capacity from [%lu] to [%lu].",
  338. (long)_textureAtlas->getCapacity(),
  339. (long)quantity);
  340. if( ! _textureAtlas->resizeCapacity(quantity) ) {
  341. // serious problems
  342. CCLOGWARN("cocos2d: WARNING: Not enough memory to resize the atlas");
  343. CCASSERT(false,"XXX: ParticleBatchNode #increaseAtlasCapacity SHALL handle this assert");
  344. }
  345. }
  346. //sets a 0'd quad into the quads array
  347. void ParticleBatchNode::disableParticle(int particleIndex)
  348. {
  349. V3F_C4B_T2F_Quad* quad = &((_textureAtlas->getQuads())[particleIndex]);
  350. quad->br.vertices.x = quad->br.vertices.y = quad->tr.vertices.x = quad->tr.vertices.y = quad->tl.vertices.x = quad->tl.vertices.y = quad->bl.vertices.x = quad->bl.vertices.y = 0.0f;
  351. }
  352. // ParticleBatchNode - add / remove / reorder helper methods
  353. // add child helper
  354. void ParticleBatchNode::insertChild(ParticleSystem* system, int index)
  355. {
  356. system->setAtlasIndex(index);
  357. if(_textureAtlas->getTotalQuads() + system->getTotalParticles() > _textureAtlas->getCapacity())
  358. {
  359. increaseAtlasCapacityTo(_textureAtlas->getTotalQuads() + system->getTotalParticles());
  360. // after a realloc empty quads of textureAtlas can be filled with gibberish (realloc doesn't perform calloc), insert empty quads to prevent it
  361. _textureAtlas->fillWithEmptyQuadsFromIndex(_textureAtlas->getCapacity() - system->getTotalParticles(), system->getTotalParticles());
  362. }
  363. // make room for quads, not necessary for last child
  364. if (system->getAtlasIndex() + system->getTotalParticles() != _textureAtlas->getTotalQuads())
  365. {
  366. _textureAtlas->moveQuadsFromIndex(index, index+system->getTotalParticles());
  367. }
  368. // increase totalParticles here for new particles, update method of particle-system will fill the quads
  369. _textureAtlas->increaseTotalQuadsWith(system->getTotalParticles());
  370. updateAllAtlasIndexes();
  371. }
  372. //rebuild atlas indexes
  373. void ParticleBatchNode::updateAllAtlasIndexes()
  374. {
  375. int index = 0;
  376. for(const auto &child : _children) {
  377. ParticleSystem* partiSys = static_cast<ParticleSystem*>(child);
  378. partiSys->setAtlasIndex(index);
  379. index += partiSys->getTotalParticles();
  380. }
  381. }
  382. // ParticleBatchNode - CocosNodeTexture protocol
  383. void ParticleBatchNode::updateBlendFunc()
  384. {
  385. if( ! _textureAtlas->getTexture()->hasPremultipliedAlpha())
  386. _blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;
  387. }
  388. void ParticleBatchNode::setTexture(Texture2D* texture)
  389. {
  390. _textureAtlas->setTexture(texture);
  391. // If the new texture has No premultiplied alpha, AND the blendFunc hasn't been changed, then update it
  392. if( texture && ! texture->hasPremultipliedAlpha() && ( _blendFunc.src == CC_BLEND_SRC && _blendFunc.dst == CC_BLEND_DST ) )
  393. {
  394. _blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;
  395. }
  396. }
  397. Texture2D* ParticleBatchNode::getTexture() const
  398. {
  399. return _textureAtlas->getTexture();
  400. }
  401. void ParticleBatchNode::setBlendFunc(const BlendFunc &blendFunc)
  402. {
  403. _blendFunc = blendFunc;
  404. }
  405. // returns the blending function used for the texture
  406. const BlendFunc& ParticleBatchNode::getBlendFunc() const
  407. {
  408. return _blendFunc;
  409. }
  410. NS_CC_END