2012-03-14 14:55:17 +08:00
/*
* Copyright ( c ) 2010 - 2011 cocos2d - x . org
* Copyright ( C ) 2009 Matt Oswald
* Copyright ( c ) 2009 - 2010 Ricardo Quesada
* Copyright ( c ) 2011 Zynga Inc .
* Copyright ( c ) 2011 Marco Tillemans
*
* http : //www.cocos2d-x.org
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to deal
* in the Software without restriction , including without limitation the rights
* to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE .
*
*/
# include "CCParticleBatchNode.h"
# include "CCTextureCache.h"
# include "CCTextureAtlas.h"
# include "ccConfig.h"
# include "ccMacros.h"
# include "effects/CCGrid.h"
# include "CCPointExtension.h"
# include "CCParticleSystem.h"
# include "CCShaderCache.h"
# include "CCGLProgram.h"
2012-03-15 10:42:22 +08:00
# include "ccGLStateCache.h"
2012-03-14 14:55:17 +08:00
# include "support/base64.h"
# include "support/zip_support/ZipUtils.h"
# include "CCFileUtils.h"
# include "kazmath/GL/matrix.h"
2012-03-29 11:25:08 +08:00
2012-03-14 14:55:17 +08:00
NS_CC_BEGIN
# define kCCParticleDefaultCapacity 500
CCParticleBatchNode : : CCParticleBatchNode ( )
: m_pTextureAtlas ( NULL )
{
}
CCParticleBatchNode : : ~ CCParticleBatchNode ( )
{
CC_SAFE_RELEASE ( m_pTextureAtlas ) ;
}
/*
* creation with CCTexture2D
*/
CCParticleBatchNode * CCParticleBatchNode : : batchNodeWithTexture ( CCTexture2D * tex )
{
CCParticleBatchNode * p = new CCParticleBatchNode ( ) ;
if ( p & & p - > initWithTexture ( tex , kCCParticleDefaultCapacity ) )
{
p - > autorelease ( ) ;
return p ;
}
CC_SAFE_DELETE ( p ) ;
return NULL ;
}
CCParticleBatchNode * CCParticleBatchNode : : batchNodeWithTexture ( CCTexture2D * tex , unsigned int capacity )
{
CCParticleBatchNode * p = new CCParticleBatchNode ( ) ;
if ( p & & p - > initWithTexture ( tex , capacity ) )
{
p - > autorelease ( ) ;
return p ;
}
CC_SAFE_DELETE ( p ) ;
return NULL ;
}
/*
* creation with File Image
*/
CCParticleBatchNode * CCParticleBatchNode : : batchNodeWithFile ( const char * imageFile , unsigned int capacity )
{
CCParticleBatchNode * p = new CCParticleBatchNode ( ) ;
if ( p & & p - > initWithFile ( imageFile , capacity ) )
{
p - > autorelease ( ) ;
return p ;
}
CC_SAFE_DELETE ( p ) ;
return NULL ;
}
CCParticleBatchNode * CCParticleBatchNode : : batchNodeWithFile ( const char * imageFile )
{
CCParticleBatchNode * p = new CCParticleBatchNode ( ) ;
if ( p & & p - > initWithFile ( imageFile , kCCParticleDefaultCapacity ) )
{
p - > autorelease ( ) ;
return p ;
}
CC_SAFE_DELETE ( p ) ;
return NULL ;
}
/*
* init with CCTexture2D
*/
bool CCParticleBatchNode : : initWithTexture ( CCTexture2D * tex , unsigned int capacity )
{
m_pTextureAtlas = new CCTextureAtlas ( ) ;
m_pTextureAtlas - > initWithTexture ( tex , capacity ) ;
// no lazy alloc in this node
2012-03-27 13:48:14 +08:00
m_pChildren = new CCArray ( ) ;
m_pChildren - > initWithCapacity ( capacity ) ;
2012-03-14 14:55:17 +08:00
m_tBlendFunc . src = CC_BLEND_SRC ;
m_tBlendFunc . dst = CC_BLEND_DST ;
setShaderProgram ( CCShaderCache : : sharedShaderCache ( ) - > programForKey ( kCCShader_PositionTextureColor ) ) ;
return true ;
}
/*
* init with FileImage
*/
bool CCParticleBatchNode : : initWithFile ( const char * fileImage , unsigned int capacity )
{
CCTexture2D * tex = CCTextureCache : : sharedTextureCache ( ) - > addImage ( fileImage ) ;
return initWithTexture ( tex , capacity ) ;
}
// CCParticleBatchNode - composition
// override visit.
// Don't call visit on it's children
void CCParticleBatchNode : : visit ( )
{
// CAREFUL:
// This visit is almost identical to CCNode#visit
// with the exception that it doesn't call visit on it's children
//
// The alternative is to have a void CCSprite#visit, but
// although this is less mantainable, is faster
//
if ( ! m_bIsVisible )
2012-03-29 11:25:08 +08:00
{
2012-03-14 14:55:17 +08:00
return ;
2012-03-29 11:25:08 +08:00
}
2012-03-14 14:55:17 +08:00
kmGLPushMatrix ( ) ;
2012-03-29 11:25:08 +08:00
if ( m_pGrid & & m_pGrid - > isActive ( ) )
{
2012-03-14 14:55:17 +08:00
m_pGrid - > beforeDraw ( ) ;
transformAncestors ( ) ;
}
transform ( ) ;
draw ( ) ;
if ( m_pGrid & & m_pGrid - > isActive ( ) )
2012-03-29 11:25:08 +08:00
{
2012-03-14 14:55:17 +08:00
m_pGrid - > afterDraw ( this ) ;
2012-03-29 11:25:08 +08:00
}
2012-03-14 14:55:17 +08:00
kmGLPopMatrix ( ) ;
}
// override addChild:
2012-03-22 18:29:07 +08:00
void CCParticleBatchNode : : addChild ( CCNode * child )
{
CCNode : : addChild ( child ) ;
}
void CCParticleBatchNode : : addChild ( CCNode * child , int zOrder )
{
CCNode : : addChild ( child , zOrder ) ;
}
2012-03-14 14:55:17 +08:00
void CCParticleBatchNode : : addChild ( CCNode * child , int zOrder , int tag )
{
CCAssert ( child ! = NULL , " Argument must be non-NULL " ) ;
2012-03-27 13:48:14 +08:00
CCAssert ( dynamic_cast < CCParticleSystem * > ( child ) ! = NULL , " CCParticleBatchNode only supports CCQuadParticleSystems as children " ) ;
CCParticleSystem * pChild = ( CCParticleSystem * ) child ;
CCAssert ( pChild - > getTexture ( ) - > getName ( ) = = m_pTextureAtlas - > getTexture ( ) - > getName ( ) , " CCParticleSystem is not using the same texture id " ) ;
2012-03-14 14:55:17 +08:00
// If this is the 1st children, then copy blending function
2012-03-27 13:48:14 +08:00
if ( m_pChildren - > count ( ) = = 0 )
{
2012-03-14 14:55:17 +08:00
setBlendFunc ( pChild - > getBlendFunc ( ) ) ;
}
CCAssert ( m_tBlendFunc . src = = pChild - > getBlendFunc ( ) . src & & m_tBlendFunc . dst = = pChild - > getBlendFunc ( ) . dst , " Can't add a PaticleSystem that uses a differnt blending function " ) ;
//no lazy sorting, so don't call super addChild, call helper instead
unsigned int pos = addChildHelper ( pChild , zOrder , tag ) ;
//get new atlasIndex
unsigned int atlasIndex = 0 ;
2012-03-27 13:48:14 +08:00
if ( pos ! = 0 )
{
2012-03-14 14:55:17 +08:00
CCParticleSystem * p = ( CCParticleSystem * ) m_pChildren - > objectAtIndex ( pos - 1 ) ;
atlasIndex = p - > getAtlasIndex ( ) + p - > getTotalParticles ( ) ;
}
2012-03-27 13:48:14 +08:00
else
{
2012-03-14 14:55:17 +08:00
atlasIndex = 0 ;
}
insertChild ( pChild , atlasIndex ) ;
// update quad info
pChild - > setBatchNode ( this ) ;
}
// don't use lazy sorting, reordering the particle systems quads afterwards would be too complex
// XXX research whether lazy sorting + freeing current quads and calloc a new block with size of capacity would be faster
// XXX or possibly using vertexZ for reordering, that would be fastest
// this helper is almost equivalent to CCNode's addChild, but doesn't make use of the lazy sorting
unsigned int CCParticleBatchNode : : addChildHelper ( CCParticleSystem * child , int z , int aTag )
{
CCAssert ( child ! = NULL , " Argument must be non-nil " ) ;
CCAssert ( child - > getParent ( ) = = NULL , " child already added. It can't be added again " ) ;
2012-03-29 11:25:08 +08:00
if ( ! m_pChildren )
{
m_pChildren = new CCArray ( ) ;
m_pChildren - > initWithCapacity ( 4 ) ;
2012-03-14 14:55:17 +08:00
}
//don't use a lazy insert
unsigned int pos = searchNewPositionInChildrenForZ ( z ) ;
m_pChildren - > insertObject ( child , pos ) ;
child - > setTag ( aTag ) ;
2012-03-16 13:42:53 +08:00
child - > _setZOrder ( z ) ;
2012-03-14 14:55:17 +08:00
child - > setParent ( this ) ;
2012-03-29 11:25:08 +08:00
if ( m_bIsRunning )
{
2012-03-14 14:55:17 +08:00
child - > onEnter ( ) ;
child - > onEnterTransitionDidFinish ( ) ;
}
return pos ;
}
// Reorder will be done in this function, no "lazy" reorder to particles
void CCParticleBatchNode : : reorderChild ( CCNode * child , int zOrder )
{
CCAssert ( child ! = NULL , " Child must be non-NULL " ) ;
2012-03-29 11:25:08 +08:00
CCAssert ( dynamic_cast < CCParticleSystem * > ( child ) ! = NULL , " CCParticleBatchNode only supports CCQuadParticleSystems as children " ) ;
2012-03-14 14:55:17 +08:00
CCAssert ( m_pChildren - > containsObject ( child ) , " Child doesn't belong to batch " ) ;
2012-03-29 11:25:08 +08:00
CCParticleSystem * pChild = ( CCParticleSystem * ) ( child ) ;
if ( zOrder = = child - > getZOrder ( ) )
{
2012-03-14 14:55:17 +08:00
return ;
}
2012-03-29 11:25:08 +08:00
2012-03-14 14:55:17 +08:00
// no reordering if only 1 child
if ( m_pChildren - > count ( ) > 1 )
{
unsigned int newIndex = 0 , oldIndex = 0 ;
getCurrentIndex ( & oldIndex , & newIndex , pChild , zOrder ) ;
2012-03-29 11:25:08 +08:00
if ( oldIndex ! = newIndex )
{
2012-03-14 14:55:17 +08:00
// reorder m_pChildren->array
pChild - > retain ( ) ;
m_pChildren - > removeObjectAtIndex ( oldIndex ) ;
m_pChildren - > insertObject ( pChild , newIndex ) ;
pChild - > release ( ) ;
// save old altasIndex
unsigned int oldAtlasIndex = pChild - > getAtlasIndex ( ) ;
// update atlas index
updateAllAtlasIndexes ( ) ;
// Find new AtlasIndex
unsigned int newAtlasIndex = 0 ;
2012-03-29 11:25:08 +08:00
for ( unsigned int i = 0 ; i < m_pChildren - > count ( ) ; i + + )
{
CCParticleSystem * pNode = ( CCParticleSystem * ) m_pChildren - > objectAtIndex ( i ) ;
if ( pNode = = pChild )
{
2012-03-14 14:55:17 +08:00
newAtlasIndex = pChild - > getAtlasIndex ( ) ;
break ;
}
}
// reorder textureAtlas quads
m_pTextureAtlas - > moveQuadsFromIndex ( oldAtlasIndex , pChild - > getTotalParticles ( ) , newAtlasIndex ) ;
pChild - > updateWithNoTime ( ) ;
}
}
2012-03-16 13:42:53 +08:00
pChild - > _setZOrder ( zOrder ) ;
2012-03-14 14:55:17 +08:00
}
void CCParticleBatchNode : : getCurrentIndex ( unsigned int * oldIndex , unsigned int * newIndex , CCNode * child , int z )
{
bool foundCurrentIdx = false ;
bool foundNewIdx = false ;
int minusOne = 0 ;
unsigned int count = m_pChildren - > count ( ) ;
2012-03-29 11:25:08 +08:00
for ( unsigned int i = 0 ; i < count ; i + + )
{
CCNode * pNode = ( CCNode * ) m_pChildren - > objectAtIndex ( i ) ;
2012-03-14 14:55:17 +08:00
// new index
2012-03-29 11:25:08 +08:00
if ( pNode - > getZOrder ( ) > z & & ! foundNewIdx )
{
2012-03-14 14:55:17 +08:00
* newIndex = i ;
foundNewIdx = true ;
if ( foundCurrentIdx & & foundNewIdx )
2012-03-29 11:25:08 +08:00
{
2012-03-14 14:55:17 +08:00
break ;
2012-03-29 11:25:08 +08:00
}
2012-03-14 14:55:17 +08:00
}
// current index
2012-03-29 11:25:08 +08:00
if ( child = = pNode )
{
2012-03-14 14:55:17 +08:00
* oldIndex = i ;
foundCurrentIdx = true ;
if ( ! foundNewIdx )
2012-03-29 11:25:08 +08:00
{
2012-03-14 14:55:17 +08:00
minusOne = - 1 ;
2012-03-29 11:25:08 +08:00
}
2012-03-14 14:55:17 +08:00
if ( foundCurrentIdx & & foundNewIdx )
2012-03-29 11:25:08 +08:00
{
2012-03-14 14:55:17 +08:00
break ;
2012-03-29 11:25:08 +08:00
}
2012-03-14 14:55:17 +08:00
}
}
if ( ! foundNewIdx )
2012-03-29 11:25:08 +08:00
{
2012-03-14 14:55:17 +08:00
* newIndex = count ;
2012-03-29 11:25:08 +08:00
}
2012-03-14 14:55:17 +08:00
* newIndex + = minusOne ;
}
unsigned int CCParticleBatchNode : : searchNewPositionInChildrenForZ ( int z )
{
unsigned int count = m_pChildren - > count ( ) ;
2012-03-29 11:25:08 +08:00
for ( unsigned int i = 0 ; i < count ; i + + )
{
2012-03-14 14:55:17 +08:00
CCNode * child = ( CCNode * ) m_pChildren - > objectAtIndex ( i ) ;
if ( child - > getZOrder ( ) > z )
2012-03-29 11:25:08 +08:00
{
2012-03-14 14:55:17 +08:00
return i ;
2012-03-29 11:25:08 +08:00
}
2012-03-14 14:55:17 +08:00
}
return count ;
}
// override removeChild:
void CCParticleBatchNode : : removeChild ( CCNode * child , bool cleanup )
{
// explicit nil handling
if ( child = = NULL )
2012-03-29 11:25:08 +08:00
{
2012-03-14 14:55:17 +08:00
return ;
2012-03-29 11:25:08 +08:00
}
CCAssert ( dynamic_cast < CCParticleSystem * > ( child ) ! = NULL , " CCParticleBatchNode only supports CCQuadParticleSystems as children " ) ;
CCAssert ( m_pChildren - > containsObject ( child ) , " CCParticleBatchNode doesn't contain the sprite. Can't remove it " ) ;
2012-03-14 14:55:17 +08:00
2012-03-29 11:25:08 +08:00
CCParticleSystem * pChild = ( CCParticleSystem * ) child ;
2012-03-14 14:55:17 +08:00
CCNode : : removeChild ( pChild , cleanup ) ;
// remove child helper
m_pTextureAtlas - > removeQuadsAtIndex ( pChild - > getAtlasIndex ( ) , pChild - > getTotalParticles ( ) ) ;
// after memmove of data, empty the quads at the end of array
m_pTextureAtlas - > fillWithEmptyQuadsFromIndex ( m_pTextureAtlas - > getTotalQuads ( ) , pChild - > getTotalParticles ( ) ) ;
// paticle could be reused for self rendering
pChild - > setBatchNode ( NULL ) ;
updateAllAtlasIndexes ( ) ;
}
void CCParticleBatchNode : : removeChildAtIndex ( unsigned int index , bool doCleanup )
{
removeChild ( ( CCParticleSystem * ) m_pChildren - > objectAtIndex ( index ) , doCleanup ) ;
}
void CCParticleBatchNode : : removeAllChildrenWithCleanup ( bool doCleanup )
{
2012-03-29 14:30:13 +08:00
arrayMakeObjectsPerformSelectorWithObject ( m_pChildren , setBatchNode , NULL , CCParticleSystem * ) ;
2012-03-14 14:55:17 +08:00
CCNode : : removeAllChildrenWithCleanup ( doCleanup ) ;
m_pTextureAtlas - > removeAllQuads ( ) ;
}
void CCParticleBatchNode : : draw ( void )
{
CC_PROFILER_STOP ( " CCParticleBatchNode - draw " ) ;
if ( m_pTextureAtlas - > getTotalQuads ( ) = = 0 )
2012-03-29 11:25:08 +08:00
{
2012-03-14 14:55:17 +08:00
return ;
2012-03-29 11:25:08 +08:00
}
2012-03-14 14:55:17 +08:00
CC_NODE_DRAW_SETUP ( ) ;
ccGLBlendFunc ( m_tBlendFunc . src , m_tBlendFunc . dst ) ;
m_pTextureAtlas - > drawQuads ( ) ;
CC_PROFILER_STOP ( " CCParticleBatchNode - draw " ) ;
}
void CCParticleBatchNode : : increaseAtlasCapacityTo ( unsigned int quantity )
{
CCLOG ( " cocos2d: CCParticleBatchNode: resizing TextureAtlas capacity from [%lu] to [%lu]. " ,
( long ) m_pTextureAtlas - > getCapacity ( ) ,
( long ) quantity ) ;
if ( ! m_pTextureAtlas - > resizeCapacity ( quantity ) ) {
// serious problems
CCLOG ( " cocos2d: WARNING: Not enough memory to resize the atlas " ) ;
CCAssert ( false , " XXX: CCParticleBatchNode #increaseAtlasCapacity SHALL handle this assert " ) ;
}
}
//sets a 0'd quad into the quads array
void CCParticleBatchNode : : disableParticle ( unsigned int particleIndex )
{
ccV3F_C4B_T2F_Quad * quad = & ( ( m_pTextureAtlas - > getQuads ( ) ) [ particleIndex ] ) ;
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 ;
}
// CCParticleBatchNode - add / remove / reorder helper methods
// add child helper
void CCParticleBatchNode : : insertChild ( CCParticleSystem * pSystem , unsigned int index )
{
pSystem - > setAtlasIndex ( index ) ;
if ( m_pTextureAtlas - > getTotalQuads ( ) + pSystem - > getTotalParticles ( ) > m_pTextureAtlas - > getCapacity ( ) )
{
increaseAtlasCapacityTo ( m_pTextureAtlas - > getTotalQuads ( ) + pSystem - > getTotalParticles ( ) ) ;
// after a realloc empty quads of textureAtlas can be filled with gibberish (realloc doesn't perform calloc), insert empty quads to prevent it
m_pTextureAtlas - > fillWithEmptyQuadsFromIndex ( m_pTextureAtlas - > getCapacity ( ) - pSystem - > getTotalParticles ( ) , pSystem - > getTotalParticles ( ) ) ;
}
// make room for quads, not necessary for last child
if ( pSystem - > getAtlasIndex ( ) + pSystem - > getTotalParticles ( ) ! = m_pTextureAtlas - > getTotalQuads ( ) )
m_pTextureAtlas - > moveQuadsFromIndex ( index , index + pSystem - > getTotalParticles ( ) ) ;
// increase totalParticles here for new particles, update method of particlesystem will fill the quads
m_pTextureAtlas - > increaseTotalQuadsWith ( pSystem - > getTotalParticles ( ) ) ;
updateAllAtlasIndexes ( ) ;
}
//rebuild atlas indexes
void CCParticleBatchNode : : updateAllAtlasIndexes ( )
{
CCObject * pObj = NULL ;
unsigned int index = 0 ;
CCARRAY_FOREACH ( m_pChildren , pObj )
{
CCParticleSystem * child = ( CCParticleSystem * ) pObj ;
child - > setAtlasIndex ( index ) ;
index + = child - > getTotalParticles ( ) ;
}
}
// CCParticleBatchNode - CocosNodeTexture protocol
void CCParticleBatchNode : : updateBlendFunc ( void )
{
if ( ! m_pTextureAtlas - > getTexture ( ) - > getHasPremultipliedAlpha ( ) ) {
m_tBlendFunc . src = GL_SRC_ALPHA ;
m_tBlendFunc . dst = GL_ONE_MINUS_SRC_ALPHA ;
}
}
void CCParticleBatchNode : : setTexture ( CCTexture2D * texture )
{
m_pTextureAtlas - > setTexture ( texture ) ;
// If the new texture has No premultiplied alpha, AND the blendFunc hasn't been changed, then update it
if ( texture & & ! texture - > getHasPremultipliedAlpha ( ) & & ( m_tBlendFunc . src = = CC_BLEND_SRC & & m_tBlendFunc . dst = = CC_BLEND_DST ) )
{
m_tBlendFunc . src = GL_SRC_ALPHA ;
m_tBlendFunc . dst = GL_ONE_MINUS_SRC_ALPHA ;
}
}
CCTexture2D * CCParticleBatchNode : : getTexture ( void )
{
return m_pTextureAtlas - > getTexture ( ) ;
}
void CCParticleBatchNode : : setBlendFunc ( ccBlendFunc blendFunc )
{
m_tBlendFunc = blendFunc ;
}
// returns the blending function used for the texture
ccBlendFunc CCParticleBatchNode : : getBlendFunc ( void )
{
return m_tBlendFunc ;
}
NS_CC_END