2012-04-19 14:35:52 +08:00
/****************************************************************************
2012-06-08 14:11:48 +08:00
Copyright ( c ) 2010 - 2012 cocos2d - x . org
2012-04-19 14:35:52 +08:00
Copyright ( c ) 2008 - 2010 Ricardo Quesada
Copyright ( c ) 2011 Zynga Inc .
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 "CCSpriteBatchNode.h"
# include "CCAnimation.h"
# include "CCAnimationCache.h"
# include "ccConfig.h"
# include "CCSprite.h"
# include "CCSpriteFrame.h"
# include "CCSpriteFrameCache.h"
2012-06-19 16:20:46 +08:00
# include "textures/CCTextureCache.h"
2012-11-16 14:23:14 +08:00
# include "draw_nodes/CCDrawingPrimitives.h"
2012-06-19 16:20:46 +08:00
# include "shaders/CCShaderCache.h"
# include "shaders/ccGLStateCache.h"
# include "shaders/CCGLProgram.h"
2012-04-19 14:35:52 +08:00
# include "CCDirector.h"
2012-06-19 16:20:46 +08:00
# include "support/CCPointExtension.h"
# include "cocoa/CCGeometry.h"
# include "textures/CCTexture2D.h"
# include "cocoa/CCAffineTransform.h"
2012-04-19 14:35:52 +08:00
# include "support/TransformUtils.h"
# include "support/CCProfiling.h"
// external
# include "kazmath/GL/matrix.h"
# include <string.h>
using namespace std ;
NS_CC_BEGIN
# if CC_SPRITEBATCHNODE_RENDER_SUBPIXEL
# define RENDER_IN_SUBPIXEL
# else
2012-11-14 18:05:15 +08:00
# define RENDER_IN_SUBPIXEL(__ARGS__) (ceil(__ARGS__))
2012-04-19 14:35:52 +08:00
# endif
2012-07-23 22:49:11 +08:00
CCSprite * CCSprite : : createWithTexture ( CCTexture2D * pTexture )
2012-04-19 14:35:52 +08:00
{
CCSprite * pobSprite = new CCSprite ( ) ;
if ( pobSprite & & pobSprite - > initWithTexture ( pTexture ) )
{
pobSprite - > autorelease ( ) ;
return pobSprite ;
}
CC_SAFE_DELETE ( pobSprite ) ;
return NULL ;
}
2012-07-23 22:49:11 +08:00
CCSprite * CCSprite : : createWithTexture ( CCTexture2D * pTexture , const CCRect & rect )
2012-04-19 14:35:52 +08:00
{
CCSprite * pobSprite = new CCSprite ( ) ;
if ( pobSprite & & pobSprite - > initWithTexture ( pTexture , rect ) )
{
pobSprite - > autorelease ( ) ;
return pobSprite ;
}
CC_SAFE_DELETE ( pobSprite ) ;
return NULL ;
}
2012-06-14 15:13:16 +08:00
CCSprite * CCSprite : : create ( const char * pszFileName )
2012-04-19 14:35:52 +08:00
{
CCSprite * pobSprite = new CCSprite ( ) ;
if ( pobSprite & & pobSprite - > initWithFile ( pszFileName ) )
{
pobSprite - > autorelease ( ) ;
return pobSprite ;
}
CC_SAFE_DELETE ( pobSprite ) ;
return NULL ;
}
2012-06-14 15:13:16 +08:00
CCSprite * CCSprite : : create ( const char * pszFileName , const CCRect & rect )
2012-04-19 14:35:52 +08:00
{
CCSprite * pobSprite = new CCSprite ( ) ;
if ( pobSprite & & pobSprite - > initWithFile ( pszFileName , rect ) )
{
pobSprite - > autorelease ( ) ;
return pobSprite ;
}
CC_SAFE_DELETE ( pobSprite ) ;
return NULL ;
}
2012-07-23 22:49:11 +08:00
CCSprite * CCSprite : : createWithSpriteFrame ( CCSpriteFrame * pSpriteFrame )
2012-04-19 14:35:52 +08:00
{
CCSprite * pobSprite = new CCSprite ( ) ;
2012-09-04 11:16:59 +08:00
if ( pSpriteFrame & & pobSprite & & pobSprite - > initWithSpriteFrame ( pSpriteFrame ) )
2012-04-19 14:35:52 +08:00
{
pobSprite - > autorelease ( ) ;
return pobSprite ;
}
CC_SAFE_DELETE ( pobSprite ) ;
return NULL ;
}
2012-06-14 15:13:16 +08:00
CCSprite * CCSprite : : createWithSpriteFrameName ( const char * pszSpriteFrameName )
2012-04-19 14:35:52 +08:00
{
CCSpriteFrame * pFrame = CCSpriteFrameCache : : sharedSpriteFrameCache ( ) - > spriteFrameByName ( pszSpriteFrameName ) ;
2012-09-04 11:16:59 +08:00
# if COCOS2D_DEBUG > 0
2012-04-19 14:35:52 +08:00
char msg [ 256 ] = { 0 } ;
sprintf ( msg , " Invalid spriteFrameName: %s " , pszSpriteFrameName ) ;
CCAssert ( pFrame ! = NULL , msg ) ;
2012-09-04 11:16:59 +08:00
# endif
2012-07-23 22:49:11 +08:00
return createWithSpriteFrame ( pFrame ) ;
2012-04-19 14:35:52 +08:00
}
2012-06-14 15:13:16 +08:00
CCSprite * CCSprite : : create ( )
2012-04-19 14:35:52 +08:00
{
CCSprite * pSprite = new CCSprite ( ) ;
if ( pSprite & & pSprite - > init ( ) )
{
pSprite - > autorelease ( ) ;
2012-06-02 06:11:42 +08:00
return pSprite ;
2012-04-19 14:35:52 +08:00
}
2012-06-02 06:11:42 +08:00
CC_SAFE_DELETE ( pSprite ) ;
return NULL ;
2012-04-19 14:35:52 +08:00
}
bool CCSprite : : init ( void )
{
return initWithTexture ( NULL , CCRectZero ) ;
}
// designated initializer
bool CCSprite : : initWithTexture ( CCTexture2D * pTexture , const CCRect & rect , bool rotated )
{
m_pobBatchNode = NULL ;
// shader program
setShaderProgram ( CCShaderCache : : sharedShaderCache ( ) - > programForKey ( kCCShader_PositionTextureColor ) ) ;
m_bRecursiveDirty = false ;
setDirty ( false ) ;
m_bOpacityModifyRGB = true ;
m_nOpacity = 255 ;
m_sColor = m_sColorUnmodified = ccWHITE ;
m_sBlendFunc . src = CC_BLEND_SRC ;
m_sBlendFunc . dst = CC_BLEND_DST ;
m_bFlipX = m_bFlipY = false ;
// default transform anchor: center
setAnchorPoint ( ccp ( 0.5f , 0.5f ) ) ;
// zwoptex default values
m_obOffsetPosition = CCPointZero ;
m_bHasChildren = false ;
// clean the Quad
memset ( & m_sQuad , 0 , sizeof ( m_sQuad ) ) ;
// Atlas: Color
ccColor4B tmpColor = { 255 , 255 , 255 , 255 } ;
m_sQuad . bl . colors = tmpColor ;
m_sQuad . br . colors = tmpColor ;
m_sQuad . tl . colors = tmpColor ;
m_sQuad . tr . colors = tmpColor ;
// update texture (calls updateBlendFunc)
setTexture ( pTexture ) ;
setTextureRect ( rect , rotated , rect . size ) ;
// by default use "Self Render".
// if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render"
setBatchNode ( NULL ) ;
return true ;
}
bool CCSprite : : initWithTexture ( CCTexture2D * pTexture , const CCRect & rect )
{
return initWithTexture ( pTexture , rect , false ) ;
}
bool CCSprite : : initWithTexture ( CCTexture2D * pTexture )
{
CCAssert ( pTexture ! = NULL , " Invalid texture for sprite " ) ;
CCRect rect = CCRectZero ;
rect . size = pTexture - > getContentSize ( ) ;
return initWithTexture ( pTexture , rect ) ;
}
bool CCSprite : : initWithFile ( const char * pszFilename )
{
CCAssert ( pszFilename ! = NULL , " Invalid filename for sprite " ) ;
CCTexture2D * pTexture = CCTextureCache : : sharedTextureCache ( ) - > addImage ( pszFilename ) ;
if ( pTexture )
{
CCRect rect = CCRectZero ;
rect . size = pTexture - > getContentSize ( ) ;
return initWithTexture ( pTexture , rect ) ;
}
// don't release here.
// when load texture failed, it's better to get a "transparent" sprite then a crashed program
// this->release();
return false ;
}
bool CCSprite : : initWithFile ( const char * pszFilename , const CCRect & rect )
{
CCAssert ( pszFilename ! = NULL , " " ) ;
CCTexture2D * pTexture = CCTextureCache : : sharedTextureCache ( ) - > addImage ( pszFilename ) ;
if ( pTexture )
{
return initWithTexture ( pTexture , rect ) ;
}
// don't release here.
// when load texture failed, it's better to get a "transparent" sprite then a crashed program
// this->release();
return false ;
}
bool CCSprite : : initWithSpriteFrame ( CCSpriteFrame * pSpriteFrame )
{
CCAssert ( pSpriteFrame ! = NULL , " " ) ;
bool bRet = initWithTexture ( pSpriteFrame - > getTexture ( ) , pSpriteFrame - > getRect ( ) ) ;
setDisplayFrame ( pSpriteFrame ) ;
return bRet ;
}
bool CCSprite : : initWithSpriteFrameName ( const char * pszSpriteFrameName )
{
CCAssert ( pszSpriteFrameName ! = NULL , " " ) ;
CCSpriteFrame * pFrame = CCSpriteFrameCache : : sharedSpriteFrameCache ( ) - > spriteFrameByName ( pszSpriteFrameName ) ;
return initWithSpriteFrame ( pFrame ) ;
}
// XXX: deprecated
/*
CCSprite * CCSprite : : initWithCGImage ( CGImageRef pImage )
{
// todo
2012-09-17 15:02:24 +08:00
// because it is deprecated, so we do not implement it
2012-04-19 14:35:52 +08:00
return NULL ;
}
*/
/*
CCSprite * CCSprite : : initWithCGImage ( CGImageRef pImage , const char * pszKey )
{
CCAssert ( pImage ! = NULL ) ;
// XXX: possible bug. See issue #349. New API should be added
CCTexture2D * pTexture = CCTextureCache : : sharedTextureCache ( ) - > addCGImage ( pImage , pszKey ) ;
2013-01-14 16:06:18 +08:00
const CCSize & size = pTexture - > getContentSize ( ) ;
2012-04-19 14:35:52 +08:00
CCRect rect = CCRectMake ( 0 , 0 , size . width , size . height ) ;
return initWithTexture ( texture , rect ) ;
}
*/
CCSprite : : CCSprite ( void )
: m_pobTexture ( NULL )
, m_bShouldBeHidden ( false )
{
}
CCSprite : : ~ CCSprite ( void )
{
CC_SAFE_RELEASE ( m_pobTexture ) ;
}
void CCSprite : : setTextureRect ( const CCRect & rect )
{
setTextureRect ( rect , false , rect . size ) ;
}
void CCSprite : : setTextureRect ( const CCRect & rect , bool rotated , const CCSize & untrimmedSize )
{
m_bRectRotated = rotated ;
setContentSize ( untrimmedSize ) ;
setVertexRect ( rect ) ;
setTextureCoords ( rect ) ;
CCPoint relativeOffset = m_obUnflippedOffsetPositionFromCenter ;
// issue #732
if ( m_bFlipX )
{
relativeOffset . x = - relativeOffset . x ;
}
if ( m_bFlipY )
{
relativeOffset . y = - relativeOffset . y ;
}
2012-11-14 18:05:15 +08:00
m_obOffsetPosition . x = relativeOffset . x + ( m_obContentSize . width - m_obRect . size . width ) / 2 ;
m_obOffsetPosition . y = relativeOffset . y + ( m_obContentSize . height - m_obRect . size . height ) / 2 ;
2012-04-19 14:35:52 +08:00
// rendering using batch node
if ( m_pobBatchNode )
{
// update dirty_, don't update recursiveDirty_
setDirty ( true ) ;
}
else
{
// self rendering
// Atlas: Vertex
float x1 = 0 + m_obOffsetPosition . x ;
float y1 = 0 + m_obOffsetPosition . y ;
float x2 = x1 + m_obRect . size . width ;
float y2 = y1 + m_obRect . size . height ;
// Don't update Z.
m_sQuad . bl . vertices = vertex3 ( x1 , y1 , 0 ) ;
m_sQuad . br . vertices = vertex3 ( x2 , y1 , 0 ) ;
m_sQuad . tl . vertices = vertex3 ( x1 , y2 , 0 ) ;
m_sQuad . tr . vertices = vertex3 ( x2 , y2 , 0 ) ;
}
}
// override this method to generate "double scale" sprites
void CCSprite : : setVertexRect ( const CCRect & rect )
{
m_obRect = rect ;
}
void CCSprite : : setTextureCoords ( CCRect rect )
{
rect = CC_RECT_POINTS_TO_PIXELS ( rect ) ;
CCTexture2D * tex = m_pobBatchNode ? m_pobTextureAtlas - > getTexture ( ) : m_pobTexture ;
if ( ! tex )
{
return ;
}
float atlasWidth = ( float ) tex - > getPixelsWide ( ) ;
float atlasHeight = ( float ) tex - > getPixelsHigh ( ) ;
float left , right , top , bottom ;
if ( m_bRectRotated )
{
# if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
left = ( 2 * rect . origin . x + 1 ) / ( 2 * atlasWidth ) ;
right = left + ( rect . size . height * 2 - 2 ) / ( 2 * atlasWidth ) ;
top = ( 2 * rect . origin . y + 1 ) / ( 2 * atlasHeight ) ;
bottom = top + ( rect . size . width * 2 - 2 ) / ( 2 * atlasHeight ) ;
# else
left = rect . origin . x / atlasWidth ;
2012-06-08 14:11:48 +08:00
right = ( rect . origin . x + rect . size . height ) / atlasWidth ;
2012-04-19 14:35:52 +08:00
top = rect . origin . y / atlasHeight ;
2012-06-08 14:11:48 +08:00
bottom = ( rect . origin . y + rect . size . width ) / atlasHeight ;
2012-04-19 14:35:52 +08:00
# endif // CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
if ( m_bFlipX )
{
CC_SWAP ( top , bottom , float ) ;
}
if ( m_bFlipY )
{
CC_SWAP ( left , right , float ) ;
}
m_sQuad . bl . texCoords . u = left ;
m_sQuad . bl . texCoords . v = top ;
m_sQuad . br . texCoords . u = left ;
m_sQuad . br . texCoords . v = bottom ;
m_sQuad . tl . texCoords . u = right ;
m_sQuad . tl . texCoords . v = top ;
m_sQuad . tr . texCoords . u = right ;
m_sQuad . tr . texCoords . v = bottom ;
}
else
{
# if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
left = ( 2 * rect . origin . x + 1 ) / ( 2 * atlasWidth ) ;
right = left + ( rect . size . width * 2 - 2 ) / ( 2 * atlasWidth ) ;
top = ( 2 * rect . origin . y + 1 ) / ( 2 * atlasHeight ) ;
bottom = top + ( rect . size . height * 2 - 2 ) / ( 2 * atlasHeight ) ;
# else
left = rect . origin . x / atlasWidth ;
2012-06-08 14:11:48 +08:00
right = ( rect . origin . x + rect . size . width ) / atlasWidth ;
2012-04-19 14:35:52 +08:00
top = rect . origin . y / atlasHeight ;
2012-06-08 14:11:48 +08:00
bottom = ( rect . origin . y + rect . size . height ) / atlasHeight ;
2012-04-19 14:35:52 +08:00
# endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
if ( m_bFlipX )
{
CC_SWAP ( left , right , float ) ;
}
if ( m_bFlipY )
{
CC_SWAP ( top , bottom , float ) ;
}
m_sQuad . bl . texCoords . u = left ;
m_sQuad . bl . texCoords . v = bottom ;
m_sQuad . br . texCoords . u = right ;
m_sQuad . br . texCoords . v = bottom ;
m_sQuad . tl . texCoords . u = left ;
m_sQuad . tl . texCoords . v = top ;
m_sQuad . tr . texCoords . u = right ;
m_sQuad . tr . texCoords . v = top ;
}
}
void CCSprite : : updateTransform ( void )
{
CCAssert ( m_pobBatchNode , " updateTransform is only valid when CCSprite is being rendered using an CCSpriteBatchNode " ) ;
2012-09-17 15:02:24 +08:00
// recalculate matrix only if it is dirty
2012-04-19 14:35:52 +08:00
if ( isDirty ( ) ) {
// If it is not visible, or one of its ancestors is not visible, then do nothing:
2012-11-14 18:05:15 +08:00
if ( ! m_bVisible | | ( m_pParent & & m_pParent ! = m_pobBatchNode & & ( ( CCSprite * ) m_pParent ) - > m_bShouldBeHidden ) )
2012-04-19 14:35:52 +08:00
{
m_sQuad . br . vertices = m_sQuad . tl . vertices = m_sQuad . tr . vertices = m_sQuad . bl . vertices = vertex3 ( 0 , 0 , 0 ) ;
m_bShouldBeHidden = true ;
}
else
{
m_bShouldBeHidden = false ;
if ( ! m_pParent | | m_pParent = = m_pobBatchNode )
{
m_transformToBatch = nodeToParentTransform ( ) ;
}
else
{
CCAssert ( dynamic_cast < CCSprite * > ( m_pParent ) , " Logic error in CCSprite. Parent must be a CCSprite " ) ;
m_transformToBatch = CCAffineTransformConcat ( nodeToParentTransform ( ) , ( ( CCSprite * ) m_pParent ) - > m_transformToBatch ) ;
}
//
// calculate the Quad based on the Affine Matrix
//
CCSize size = m_obRect . size ;
float x1 = m_obOffsetPosition . x ;
float y1 = m_obOffsetPosition . y ;
float x2 = x1 + size . width ;
float y2 = y1 + size . height ;
float x = m_transformToBatch . tx ;
float y = m_transformToBatch . ty ;
float cr = m_transformToBatch . a ;
float sr = m_transformToBatch . b ;
float cr2 = m_transformToBatch . d ;
float sr2 = - m_transformToBatch . c ;
float ax = x1 * cr - y1 * sr2 + x ;
float ay = x1 * sr + y1 * cr2 + y ;
float bx = x2 * cr - y1 * sr2 + x ;
float by = x2 * sr + y1 * cr2 + y ;
float cx = x2 * cr - y2 * sr2 + x ;
float cy = x2 * sr + y2 * cr2 + y ;
float dx = x1 * cr - y2 * sr2 + x ;
float dy = x1 * sr + y2 * cr2 + y ;
m_sQuad . bl . vertices = vertex3 ( RENDER_IN_SUBPIXEL ( ax ) , RENDER_IN_SUBPIXEL ( ay ) , m_fVertexZ ) ;
m_sQuad . br . vertices = vertex3 ( RENDER_IN_SUBPIXEL ( bx ) , RENDER_IN_SUBPIXEL ( by ) , m_fVertexZ ) ;
m_sQuad . tl . vertices = vertex3 ( RENDER_IN_SUBPIXEL ( dx ) , RENDER_IN_SUBPIXEL ( dy ) , m_fVertexZ ) ;
m_sQuad . tr . vertices = vertex3 ( RENDER_IN_SUBPIXEL ( cx ) , RENDER_IN_SUBPIXEL ( cy ) , m_fVertexZ ) ;
}
2012-10-19 23:38:58 +08:00
// MARMALADE CHANGE: ADDED CHECK FOR NULL, TO PERMIT SPRITES WITH NO BATCH NODE / TEXTURE ATLAS
if ( m_pobTextureAtlas )
2012-11-12 10:20:04 +08:00
{
2012-10-19 23:38:58 +08:00
m_pobTextureAtlas - > updateQuad ( & m_sQuad , m_uAtlasIndex ) ;
2012-11-12 10:20:04 +08:00
}
2012-04-19 14:35:52 +08:00
m_bRecursiveDirty = false ;
setDirty ( false ) ;
}
2012-11-14 18:05:15 +08:00
// MARMALADE CHANGED
// recursively iterate over children
/* if( m_bHasChildren )
{
// MARMALADE: CHANGED TO USE CCNode*
// NOTE THAT WE HAVE ALSO DEFINED virtual CCNode::updateTransform()
arrayMakeObjectsPerformSelector ( m_pChildren , updateTransform , CCSprite * ) ;
} */
2012-10-30 10:24:28 +08:00
CCNode : : updateTransform ( ) ;
2012-04-19 14:35:52 +08:00
# if CC_SPRITE_DEBUG_DRAW
// draw bounding box
CCPoint vertices [ 4 ] = {
ccp ( m_sQuad . bl . vertices . x , m_sQuad . bl . vertices . y ) ,
ccp ( m_sQuad . br . vertices . x , m_sQuad . br . vertices . y ) ,
ccp ( m_sQuad . tr . vertices . x , m_sQuad . tr . vertices . y ) ,
ccp ( m_sQuad . tl . vertices . x , m_sQuad . tl . vertices . y ) ,
} ;
ccDrawPoly ( vertices , 4 , true ) ;
# endif // CC_SPRITE_DEBUG_DRAW
}
// draw
void CCSprite : : draw ( void )
{
CC_PROFILER_START_CATEGORY ( kCCProfilerCategorySprite , " CCSprite - draw " ) ;
CCAssert ( ! m_pobBatchNode , " If CCSprite is being rendered by CCSpriteBatchNode, CCSprite#draw SHOULD NOT be called " ) ;
CC_NODE_DRAW_SETUP ( ) ;
ccGLBlendFunc ( m_sBlendFunc . src , m_sBlendFunc . dst ) ;
if ( m_pobTexture ! = NULL )
{
ccGLBindTexture2D ( m_pobTexture - > getName ( ) ) ;
}
else
{
ccGLBindTexture2D ( 0 ) ;
}
//
// Attributes
//
ccGLEnableVertexAttribs ( kCCVertexAttribFlag_PosColorTex ) ;
# define kQuadSize sizeof(m_sQuad.bl)
long offset = ( long ) & m_sQuad ;
// vertex
int diff = offsetof ( ccV3F_C4B_T2F , vertices ) ;
glVertexAttribPointer ( kCCVertexAttrib_Position , 3 , GL_FLOAT , GL_FALSE , kQuadSize , ( void * ) ( offset + diff ) ) ;
// texCoods
diff = offsetof ( ccV3F_C4B_T2F , texCoords ) ;
glVertexAttribPointer ( kCCVertexAttrib_TexCoords , 2 , GL_FLOAT , GL_FALSE , kQuadSize , ( void * ) ( offset + diff ) ) ;
// color
diff = offsetof ( ccV3F_C4B_T2F , colors ) ;
glVertexAttribPointer ( kCCVertexAttrib_Color , 4 , GL_UNSIGNED_BYTE , GL_TRUE , kQuadSize , ( void * ) ( offset + diff ) ) ;
glDrawArrays ( GL_TRIANGLE_STRIP , 0 , 4 ) ;
CHECK_GL_ERROR_DEBUG ( ) ;
# if CC_SPRITE_DEBUG_DRAW == 1
// draw bounding box
CCPoint vertices [ 4 ] = {
ccp ( m_sQuad . tl . vertices . x , m_sQuad . tl . vertices . y ) ,
ccp ( m_sQuad . bl . vertices . x , m_sQuad . bl . vertices . y ) ,
ccp ( m_sQuad . br . vertices . x , m_sQuad . br . vertices . y ) ,
ccp ( m_sQuad . tr . vertices . x , m_sQuad . tr . vertices . y ) ,
} ;
ccDrawPoly ( vertices , 4 , true ) ;
# elif CC_SPRITE_DEBUG_DRAW == 2
// draw texture box
CCSize s = this - > getTextureRect ( ) . size ;
CCPoint offsetPix = this - > getOffsetPosition ( ) ;
CCPoint vertices [ 4 ] = {
ccp ( offsetPix . x , offsetPix . y ) , ccp ( offsetPix . x + s . width , offsetPix . y ) ,
ccp ( offsetPix . x + s . width , offsetPix . y + s . height ) , ccp ( offsetPix . x , offsetPix . y + s . height )
} ;
ccDrawPoly ( vertices , 4 , true ) ;
# endif // CC_SPRITE_DEBUG_DRAW
CC_INCREMENT_GL_DRAWS ( 1 ) ;
CC_PROFILER_STOP_CATEGORY ( kCCProfilerCategorySprite , " CCSprite - draw " ) ;
}
// CCNode overrides
void CCSprite : : addChild ( CCNode * pChild )
{
CCNode : : addChild ( pChild ) ;
}
void CCSprite : : addChild ( CCNode * pChild , int zOrder )
{
CCNode : : addChild ( pChild , zOrder ) ;
}
void CCSprite : : addChild ( CCNode * pChild , int zOrder , int tag )
{
CCAssert ( pChild ! = NULL , " Argument must be non-NULL " ) ;
if ( m_pobBatchNode )
{
CCSprite * pChildSprite = dynamic_cast < CCSprite * > ( pChild ) ;
CCAssert ( pChildSprite , " CCSprite only supports CCSprites as children when using CCSpriteBatchNode " ) ;
CCAssert ( pChildSprite - > getTexture ( ) - > getName ( ) = = m_pobTextureAtlas - > getTexture ( ) - > getName ( ) , " " ) ;
//put it in descendants array of batch node
m_pobBatchNode - > appendChild ( pChildSprite ) ;
if ( ! m_bReorderChildDirty )
{
setReorderChildDirtyRecursively ( ) ;
}
}
//CCNode already sets isReorderChildDirty_ so this needs to be after batchNode check
CCNode : : addChild ( pChild , zOrder , tag ) ;
m_bHasChildren = true ;
}
void CCSprite : : reorderChild ( CCNode * pChild , int zOrder )
{
CCAssert ( pChild ! = NULL , " " ) ;
CCAssert ( m_pChildren - > containsObject ( pChild ) , " " ) ;
if ( zOrder = = pChild - > getZOrder ( ) )
{
return ;
}
if ( m_pobBatchNode & & ! m_bReorderChildDirty )
{
setReorderChildDirtyRecursively ( ) ;
m_pobBatchNode - > reorderBatch ( true ) ;
}
CCNode : : reorderChild ( pChild , zOrder ) ;
}
void CCSprite : : removeChild ( CCNode * pChild , bool bCleanup )
{
if ( m_pobBatchNode )
{
m_pobBatchNode - > removeSpriteFromAtlas ( ( CCSprite * ) ( pChild ) ) ;
}
CCNode : : removeChild ( pChild , bCleanup ) ;
}
void CCSprite : : removeAllChildrenWithCleanup ( bool bCleanup )
{
if ( m_pobBatchNode )
{
CCObject * pObject = NULL ;
CCARRAY_FOREACH ( m_pChildren , pObject )
{
CCSprite * pChild = dynamic_cast < CCSprite * > ( pObject ) ;
if ( pChild )
{
m_pobBatchNode - > removeSpriteFromAtlas ( pChild ) ;
}
}
}
CCNode : : removeAllChildrenWithCleanup ( bCleanup ) ;
m_bHasChildren = false ;
}
void CCSprite : : sortAllChildren ( )
{
if ( m_bReorderChildDirty )
{
int i = 0 , j = 0 , length = m_pChildren - > data - > num ;
CCNode * * x = ( CCNode * * ) m_pChildren - > data - > arr ;
CCNode * tempItem = NULL ;
// insertion sort
for ( i = 1 ; i < length ; i + + )
{
tempItem = x [ i ] ;
j = i - 1 ;
//continue moving element downwards while zOrder is smaller or when zOrder is the same but orderOfArrival is smaller
while ( j > = 0 & & ( tempItem - > getZOrder ( ) < x [ j ] - > getZOrder ( ) | | ( tempItem - > getZOrder ( ) = = x [ j ] - > getZOrder ( ) & & tempItem - > getOrderOfArrival ( ) < x [ j ] - > getOrderOfArrival ( ) ) ) )
{
x [ j + 1 ] = x [ j ] ;
j = j - 1 ;
}
x [ j + 1 ] = tempItem ;
}
if ( m_pobBatchNode )
{
arrayMakeObjectsPerformSelector ( m_pChildren , sortAllChildren , CCSprite * ) ;
}
m_bReorderChildDirty = false ;
}
}
//
// CCNode property overloads
// used only when parent is CCSpriteBatchNode
//
void CCSprite : : setReorderChildDirtyRecursively ( void )
{
//only set parents flag the first time
if ( ! m_bReorderChildDirty )
{
m_bReorderChildDirty = true ;
CCNode * pNode = ( CCNode * ) m_pParent ;
while ( pNode & & pNode ! = m_pobBatchNode )
{
( ( CCSprite * ) pNode ) - > setReorderChildDirtyRecursively ( ) ;
pNode = pNode - > getParent ( ) ;
}
}
}
void CCSprite : : setDirtyRecursively ( bool bValue )
{
m_bRecursiveDirty = bValue ;
setDirty ( bValue ) ;
// recursively set dirty
if ( m_bHasChildren )
{
CCObject * pObject = NULL ;
CCARRAY_FOREACH ( m_pChildren , pObject )
{
CCSprite * pChild = dynamic_cast < CCSprite * > ( pObject ) ;
if ( pChild )
{
pChild - > setDirtyRecursively ( true ) ;
}
}
}
}
// XXX HACK: optimization
# define SET_DIRTY_RECURSIVELY() { \
if ( m_pobBatchNode & & ! m_bRecursiveDirty ) { \
m_bRecursiveDirty = true ; \
setDirty ( true ) ; \
if ( m_bHasChildren ) \
setDirtyRecursively ( true ) ; \
} \
}
void CCSprite : : setPosition ( const CCPoint & pos )
{
CCNode : : setPosition ( pos ) ;
SET_DIRTY_RECURSIVELY ( ) ;
}
void CCSprite : : setRotation ( float fRotation )
{
CCNode : : setRotation ( fRotation ) ;
SET_DIRTY_RECURSIVELY ( ) ;
}
2012-11-14 18:05:15 +08:00
void CCSprite : : setRotationX ( float fRotationX )
{
CCNode : : setRotationX ( fRotationX ) ;
SET_DIRTY_RECURSIVELY ( ) ;
}
void CCSprite : : setRotationY ( float fRotationY )
{
CCNode : : setRotationY ( fRotationY ) ;
SET_DIRTY_RECURSIVELY ( ) ;
}
2012-04-19 14:35:52 +08:00
void CCSprite : : setSkewX ( float sx )
{
CCNode : : setSkewX ( sx ) ;
SET_DIRTY_RECURSIVELY ( ) ;
}
void CCSprite : : setSkewY ( float sy )
{
CCNode : : setSkewY ( sy ) ;
SET_DIRTY_RECURSIVELY ( ) ;
}
void CCSprite : : setScaleX ( float fScaleX )
{
CCNode : : setScaleX ( fScaleX ) ;
SET_DIRTY_RECURSIVELY ( ) ;
}
void CCSprite : : setScaleY ( float fScaleY )
{
CCNode : : setScaleY ( fScaleY ) ;
SET_DIRTY_RECURSIVELY ( ) ;
}
void CCSprite : : setScale ( float fScale )
{
CCNode : : setScale ( fScale ) ;
SET_DIRTY_RECURSIVELY ( ) ;
}
void CCSprite : : setVertexZ ( float fVertexZ )
{
CCNode : : setVertexZ ( fVertexZ ) ;
SET_DIRTY_RECURSIVELY ( ) ;
}
void CCSprite : : setAnchorPoint ( const CCPoint & anchor )
{
CCNode : : setAnchorPoint ( anchor ) ;
SET_DIRTY_RECURSIVELY ( ) ;
}
2012-06-15 15:10:40 +08:00
void CCSprite : : ignoreAnchorPointForPosition ( bool value )
2012-04-19 14:35:52 +08:00
{
2012-06-08 14:11:48 +08:00
CCAssert ( ! m_pobBatchNode , " ignoreAnchorPointForPosition is invalid in CCSprite " ) ;
2012-06-15 15:10:40 +08:00
CCNode : : ignoreAnchorPointForPosition ( value ) ;
2012-04-19 14:35:52 +08:00
}
2012-06-15 16:47:30 +08:00
void CCSprite : : setVisible ( bool bVisible )
2012-04-19 14:35:52 +08:00
{
2012-06-15 15:10:40 +08:00
CCNode : : setVisible ( bVisible ) ;
2012-04-19 14:35:52 +08:00
SET_DIRTY_RECURSIVELY ( ) ;
}
void CCSprite : : setFlipX ( bool bFlipX )
{
if ( m_bFlipX ! = bFlipX )
{
m_bFlipX = bFlipX ;
2012-11-14 18:05:15 +08:00
setTextureRect ( m_obRect , m_bRectRotated , m_obContentSize ) ;
2012-04-19 14:35:52 +08:00
}
}
bool CCSprite : : isFlipX ( void )
{
return m_bFlipX ;
}
void CCSprite : : setFlipY ( bool bFlipY )
{
if ( m_bFlipY ! = bFlipY )
{
m_bFlipY = bFlipY ;
2012-11-14 18:05:15 +08:00
setTextureRect ( m_obRect , m_bRectRotated , m_obContentSize ) ;
2012-04-19 14:35:52 +08:00
}
}
bool CCSprite : : isFlipY ( void )
{
return m_bFlipY ;
}
//
// RGBA protocol
//
void CCSprite : : updateColor ( void )
{
ccColor4B color4 = { m_sColor . r , m_sColor . g , m_sColor . b , m_nOpacity } ;
m_sQuad . bl . colors = color4 ;
m_sQuad . br . colors = color4 ;
m_sQuad . tl . colors = color4 ;
m_sQuad . tr . colors = color4 ;
// renders using batch node
if ( m_pobBatchNode )
{
if ( m_uAtlasIndex ! = CCSpriteIndexNotInitialized )
{
m_pobTextureAtlas - > updateQuad ( & m_sQuad , m_uAtlasIndex ) ;
}
else
{
// no need to set it recursively
// update dirty_, don't update recursiveDirty_
setDirty ( true ) ;
}
}
// self render
// do nothing
}
void CCSprite : : setOpacity ( GLubyte opacity )
{
m_nOpacity = opacity ;
// special opacity for premultiplied textures
if ( m_bOpacityModifyRGB )
{
setColor ( m_sColorUnmodified ) ;
}
updateColor ( ) ;
}
2013-01-14 15:51:53 +08:00
const ccColor3B & CCSprite : : getColor ( void )
2012-04-19 14:35:52 +08:00
{
if ( m_bOpacityModifyRGB )
{
return m_sColorUnmodified ;
}
return m_sColor ;
}
void CCSprite : : setColor ( const ccColor3B & color3 )
{
m_sColor = m_sColorUnmodified = color3 ;
if ( m_bOpacityModifyRGB )
{
m_sColor . r = color3 . r * m_nOpacity / 255.0f ;
m_sColor . g = color3 . g * m_nOpacity / 255.0f ;
m_sColor . b = color3 . b * m_nOpacity / 255.0f ;
}
updateColor ( ) ;
}
2012-06-15 15:10:40 +08:00
void CCSprite : : setOpacityModifyRGB ( bool bValue )
2012-04-19 14:35:52 +08:00
{
ccColor3B oldColor = m_sColor ;
m_bOpacityModifyRGB = bValue ;
m_sColor = oldColor ;
}
2012-06-15 15:10:40 +08:00
bool CCSprite : : isOpacityModifyRGB ( void )
2012-04-19 14:35:52 +08:00
{
return m_bOpacityModifyRGB ;
}
// Frames
void CCSprite : : setDisplayFrame ( CCSpriteFrame * pNewFrame )
{
m_obUnflippedOffsetPositionFromCenter = pNewFrame - > getOffset ( ) ;
CCTexture2D * pNewTexture = pNewFrame - > getTexture ( ) ;
// update texture before updating texture rect
if ( pNewTexture ! = m_pobTexture )
{
setTexture ( pNewTexture ) ;
}
// update rect
m_bRectRotated = pNewFrame - > isRotated ( ) ;
setTextureRect ( pNewFrame - > getRect ( ) , m_bRectRotated , pNewFrame - > getOriginalSize ( ) ) ;
}
void CCSprite : : setDisplayFrameWithAnimationName ( const char * animationName , int frameIndex )
{
CCAssert ( animationName , " CCSprite#setDisplayFrameWithAnimationName. animationName must not be NULL " ) ;
CCAnimation * a = CCAnimationCache : : sharedAnimationCache ( ) - > animationByName ( animationName ) ;
CCAssert ( a , " CCSprite#setDisplayFrameWithAnimationName: Frame not found " ) ;
CCAnimationFrame * frame = ( CCAnimationFrame * ) a - > getFrames ( ) - > objectAtIndex ( frameIndex ) ;
CCAssert ( frame , " CCSprite#setDisplayFrame. Invalid frame " ) ;
setDisplayFrame ( frame - > getSpriteFrame ( ) ) ;
}
bool CCSprite : : isFrameDisplayed ( CCSpriteFrame * pFrame )
{
CCRect r = pFrame - > getRect ( ) ;
2012-08-01 15:30:12 +08:00
return ( r . equals ( m_obRect ) & &
pFrame - > getTexture ( ) - > getName ( ) = = m_pobTexture - > getName ( ) & &
pFrame - > getOffset ( ) . equals ( m_obUnflippedOffsetPositionFromCenter ) ) ;
2012-04-19 14:35:52 +08:00
}
CCSpriteFrame * CCSprite : : displayFrame ( void )
{
2012-07-23 22:49:11 +08:00
return CCSpriteFrame : : createWithTexture ( m_pobTexture ,
2012-04-19 14:35:52 +08:00
CC_RECT_POINTS_TO_PIXELS ( m_obRect ) ,
m_bRectRotated ,
2012-08-22 14:29:23 +08:00
CC_POINT_POINTS_TO_PIXELS ( m_obUnflippedOffsetPositionFromCenter ) ,
2012-11-14 18:05:15 +08:00
CC_SIZE_POINTS_TO_PIXELS ( m_obContentSize ) ) ;
2012-04-19 14:35:52 +08:00
}
CCSpriteBatchNode * CCSprite : : getBatchNode ( void )
{
return m_pobBatchNode ;
}
void CCSprite : : setBatchNode ( CCSpriteBatchNode * pobSpriteBatchNode )
{
m_pobBatchNode = pobSpriteBatchNode ; // weak reference
// self render
if ( ! m_pobBatchNode ) {
m_uAtlasIndex = CCSpriteIndexNotInitialized ;
setTextureAtlas ( NULL ) ;
m_bRecursiveDirty = false ;
setDirty ( false ) ;
float x1 = m_obOffsetPosition . x ;
float y1 = m_obOffsetPosition . y ;
float x2 = x1 + m_obRect . size . width ;
float y2 = y1 + m_obRect . size . height ;
m_sQuad . bl . vertices = vertex3 ( x1 , y1 , 0 ) ;
m_sQuad . br . vertices = vertex3 ( x2 , y1 , 0 ) ;
m_sQuad . tl . vertices = vertex3 ( x1 , y2 , 0 ) ;
m_sQuad . tr . vertices = vertex3 ( x2 , y2 , 0 ) ;
} else {
// using batch
m_transformToBatch = CCAffineTransformIdentity ;
setTextureAtlas ( m_pobBatchNode - > getTextureAtlas ( ) ) ; // weak ref
}
}
// Texture protocol
void CCSprite : : updateBlendFunc ( void )
{
2013-01-14 16:54:20 +08:00
CCAssert ( ! m_pobBatchNode , " CCSprite: updateBlendFunc doesn't work when the sprite is rendered using a CCSpriteBatchNode " ) ;
2012-04-19 14:35:52 +08:00
// it is possible to have an untextured sprite
2012-06-15 15:10:40 +08:00
if ( ! m_pobTexture | | ! m_pobTexture - > hasPremultipliedAlpha ( ) )
2012-04-19 14:35:52 +08:00
{
m_sBlendFunc . src = GL_SRC_ALPHA ;
m_sBlendFunc . dst = GL_ONE_MINUS_SRC_ALPHA ;
2012-06-15 15:10:40 +08:00
setOpacityModifyRGB ( false ) ;
2012-04-19 14:35:52 +08:00
}
else
{
m_sBlendFunc . src = CC_BLEND_SRC ;
m_sBlendFunc . dst = CC_BLEND_DST ;
2012-06-15 15:10:40 +08:00
setOpacityModifyRGB ( true ) ;
2012-04-19 14:35:52 +08:00
}
}
void CCSprite : : setTexture ( CCTexture2D * texture )
{
2012-06-08 14:11:48 +08:00
// If batchnode, then texture id should be the same
2012-06-08 16:22:57 +08:00
CCAssert ( ! m_pobBatchNode | | texture - > getName ( ) = = m_pobBatchNode - > getTexture ( ) - > getName ( ) , " CCSprite: Batched sprites should use the same texture as the batchnode " ) ;
2012-04-19 14:35:52 +08:00
// accept texture==nil as argument
CCAssert ( ! texture | | dynamic_cast < CCTexture2D * > ( texture ) , " setTexture expects a CCTexture2D. Invalid argument " ) ;
2012-06-08 14:11:48 +08:00
if ( ! m_pobBatchNode & & m_pobTexture ! = texture )
2012-04-19 14:35:52 +08:00
{
CC_SAFE_RETAIN ( texture ) ;
CC_SAFE_RELEASE ( m_pobTexture ) ;
m_pobTexture = texture ;
updateBlendFunc ( ) ;
}
}
CCTexture2D * CCSprite : : getTexture ( void )
{
return m_pobTexture ;
}
NS_CC_END