CCParticleBatchNode.cpp 15 KB

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