mirror of https://github.com/axmolengine/axmol.git
1100 lines
26 KiB
C++
1100 lines
26 KiB
C++
/****************************************************************************
|
|
Copyright (c) 2010 cocos2d-x.org
|
|
|
|
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 "CCSpriteSheet.h"
|
|
#include "ccConfig.h"
|
|
#include "CCSprite.h"
|
|
#include "CCSpriteFrame.h"
|
|
#include "CCSpriteFrameCache.h"
|
|
#include "CCTextureCache.h"
|
|
#include "CGPointExtension.h"
|
|
#include "CCDrawingPrimitives.h"
|
|
#include "CGGeometry.h"
|
|
#include "CCTexture2D.h"
|
|
#include "CGAffineTransform.h"
|
|
|
|
#include <string.h>
|
|
|
|
using namespace std;
|
|
namespace cocos2d {
|
|
|
|
#if CC_SPRITEBATCHNODE_RENDER_SUBPIXEL
|
|
#define RENDER_IN_SUBPIXEL
|
|
#else
|
|
#define RENDER_IN_SUBPIXEL(__A__) ( (int)(__A__))
|
|
#endif
|
|
|
|
// XXX: Optmization
|
|
struct transformValues_ {
|
|
CGPoint pos; // position x and y
|
|
CGPoint scale; // scale x and y
|
|
float rotation;
|
|
CGPoint ap; // anchor point in pixels
|
|
bool visible;
|
|
};
|
|
|
|
CCSprite* CCSprite::spriteWithBatchNode(CCSpriteBatchNode *batchNode, CGRect rect)
|
|
{
|
|
CCSprite *pobSprite = new CCSprite();
|
|
if (pobSprite->initWithBatchNode(batchNode, rect))
|
|
{
|
|
pobSprite->autorelease();
|
|
}
|
|
else
|
|
{
|
|
delete pobSprite;
|
|
pobSprite = NULL;
|
|
}
|
|
|
|
return pobSprite;
|
|
}
|
|
|
|
bool CCSprite::initWithBatchNode(CCSpriteBatchNode *batchNode, CGRect rect)
|
|
{
|
|
bool ret = initWithTexture(batchNode->getTexture(), rect);
|
|
useBatchNode(batchNode);
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool CCSprite::initWithBatchNodeRectInPixels(CCSpriteBatchNode *batchNode, CGRect rect)
|
|
{
|
|
bool ret = initWithTexture(batchNode->getTexture());
|
|
setTextureRectInPixels(rect, false, rect.size);
|
|
useBatchNode(batchNode);
|
|
|
|
return ret;
|
|
}
|
|
|
|
CCSprite* CCSprite::spriteWithTexture(CCTexture2D *pTexture)
|
|
{
|
|
CCSprite *pobSprite = new CCSprite();
|
|
pobSprite->initWithTexture(pTexture);
|
|
pobSprite->autorelease();
|
|
|
|
return pobSprite;
|
|
}
|
|
|
|
CCSprite* CCSprite::spriteWithTexture(CCTexture2D *pTexture, CGRect rect)
|
|
{
|
|
CCSprite *pobSprite = new CCSprite();
|
|
pobSprite->initWithTexture(pTexture, rect);
|
|
pobSprite->autorelease();
|
|
|
|
return pobSprite;
|
|
}
|
|
|
|
CCSprite* CCSprite::spriteWithTexture(CCTexture2D *pTexture, CGRect rect, CGPoint offset)
|
|
{
|
|
// not implement
|
|
assert(0);
|
|
return NULL;
|
|
}
|
|
|
|
CCSprite* CCSprite::spriteWithFile(const char *pszFileName)
|
|
{
|
|
CCSprite *pobSprite = new CCSprite();
|
|
pobSprite->initWithFile(pszFileName);
|
|
pobSprite->autorelease();
|
|
|
|
return pobSprite;
|
|
}
|
|
|
|
CCSprite* CCSprite::spriteWithFile(const char *pszFileName, CGRect rect)
|
|
{
|
|
CCSprite *pobSprite = new CCSprite();
|
|
pobSprite->initWithFile(pszFileName, rect);
|
|
pobSprite->autorelease();
|
|
|
|
return pobSprite;
|
|
}
|
|
|
|
CCSprite* CCSprite::spriteWithSpriteFrame(CCSpriteFrame *pSpriteFrame)
|
|
{
|
|
CCSprite *pobSprite = new CCSprite();
|
|
pobSprite->initWithSpriteFrame(pSpriteFrame);
|
|
pobSprite->autorelease();
|
|
|
|
return pobSprite;
|
|
}
|
|
|
|
CCSprite* CCSprite::spriteWithSpriteFrameName(const char *pszSpriteFrameName)
|
|
{
|
|
CCSpriteFrame *pFrame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(pszSpriteFrameName);
|
|
return spriteWithSpriteFrame(pFrame);
|
|
}
|
|
|
|
CCSprite* CCSprite::spriteWithSpriteSheet(CCSpriteSheetInternalOnly *pSpriteSheet, CGRect rect)
|
|
{
|
|
return spriteWithBatchNode(pSpriteSheet, rect);
|
|
}
|
|
|
|
bool CCSprite::init(void)
|
|
{
|
|
m_bDirty = m_bRecursiveDirty = false;
|
|
|
|
// by default use "Self Render".
|
|
// if the sprite is added to an SpriteSheet, then it will automatically switch to "SpriteSheet Render"
|
|
useSelfRender();
|
|
|
|
m_bOpacityModifyRGB = true;
|
|
m_nOpacity = 255;
|
|
m_sColor = m_sColorUnmodified = ccWHITE;
|
|
|
|
m_sBlendFunc.src = CC_BLEND_SRC;
|
|
m_sBlendFunc.dst = CC_BLEND_DST;
|
|
|
|
// update texture (calls updateBlendFunc)
|
|
setTexture(NULL);
|
|
|
|
// clean the Quad
|
|
memset(&m_sQuad, 0, sizeof(m_sQuad));
|
|
|
|
m_bFlipX = m_bFlipY = false;
|
|
|
|
// lazy alloc
|
|
m_pAnimations = NULL;
|
|
|
|
// default transform anchor: center
|
|
m_tAnchorPoint = ccp(0.5f, 0.5f);
|
|
|
|
// zwoptex default values
|
|
m_obOffsetPositionInPixels = CGPointZero;
|
|
|
|
m_eHonorParentTransform = CC_HONOR_PARENT_TRANSFORM_ALL;
|
|
m_bHasChildren = false;
|
|
|
|
// 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;
|
|
|
|
// Atlas: Vertex
|
|
|
|
// updated in "useSelfRender"
|
|
|
|
// Atlas: TexCoords
|
|
setTextureRectInPixels(CGRectZero, false, CGSizeZero);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CCSprite::initWithTexture(CCTexture2D *pTexture, CGRect rect)
|
|
{
|
|
assert(pTexture != NULL);
|
|
// IMPORTANT: [self init] and not [super init];
|
|
init();
|
|
setTexture(pTexture);
|
|
setTextureRect(rect);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CCSprite::initWithTexture(CCTexture2D *pTexture)
|
|
{
|
|
assert(pTexture != NULL);
|
|
|
|
CGRect rect = CGRectZero;
|
|
rect.size = pTexture->getContentSize();
|
|
|
|
return initWithTexture(pTexture, rect);
|
|
}
|
|
|
|
bool CCSprite::initWithFile(const char *pszFilename)
|
|
{
|
|
assert(pszFilename != NULL);
|
|
|
|
CCTexture2D *pTexture = CCTextureCache::sharedTextureCache()->addImage(pszFilename);
|
|
if (pTexture)
|
|
{
|
|
CGRect rect = CGRectZero;
|
|
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, CGRect rect)
|
|
{
|
|
assert(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)
|
|
{
|
|
assert(pSpriteFrame != NULL);
|
|
|
|
bool bRet = initWithTexture(pSpriteFrame->getTexture(), pSpriteFrame->getRect());
|
|
setDisplayFrame(pSpriteFrame);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
bool CCSprite::initWithSpriteFrameName(const char *pszSpriteFrameName)
|
|
{
|
|
assert(pszSpriteFrameName != NULL);
|
|
|
|
CCSpriteFrame *pFrame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(pszSpriteFrameName);
|
|
return initWithSpriteFrame(pFrame);
|
|
}
|
|
|
|
// XXX: deprecated
|
|
/*
|
|
CCSprite* CCSprite::initWithCGImage(CGImageRef pImage)
|
|
{
|
|
// todo
|
|
// because it is deprecated, so we do not impelment it
|
|
|
|
return NULL;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
CCSprite* CCSprite::initWithCGImage(CGImageRef pImage, const char *pszKey)
|
|
{
|
|
assert(pImage != NULL);
|
|
|
|
// XXX: possible bug. See issue #349. New API should be added
|
|
CCTexture2D *pTexture = CCTextureCache::sharedTextureCache()->addCGImage(pImage, pszKey);
|
|
|
|
CGSize size = pTexture->getContentSize();
|
|
CGRect rect = CGRectMake(0 ,0, size.width, size.height);
|
|
|
|
return initWithTexture(texture, rect);
|
|
}
|
|
*/
|
|
|
|
bool CCSprite::initWithSpriteSheet(CCSpriteSheetInternalOnly *pSpriteSheet, CGRect rect)
|
|
{
|
|
return initWithBatchNode(pSpriteSheet, rect);
|
|
}
|
|
|
|
CCSprite::CCSprite()
|
|
: m_pobTexture(NULL)
|
|
{
|
|
init();
|
|
}
|
|
|
|
CCSprite::~CCSprite(void)
|
|
{
|
|
CCX_SAFE_RELEASE(m_pobTexture);
|
|
CCX_SAFE_RELEASE(m_pAnimations);
|
|
}
|
|
|
|
void CCSprite::useSelfRender(void)
|
|
{
|
|
m_uAtlasIndex = CCSpriteIndexNotInitialized;
|
|
m_bUsesBatchNode = false;
|
|
m_pobTextureAtlas = NULL;
|
|
m_pobBatchNode = NULL;
|
|
m_bDirty = m_bRecursiveDirty = false;
|
|
|
|
float x1 = 0 + m_obOffsetPositionInPixels.x;
|
|
float y1 = 0 + m_obOffsetPositionInPixels.y;
|
|
float x2 = x1 + m_obRectInPixels.size.width;
|
|
float y2 = y1 + m_obRectInPixels.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);
|
|
}
|
|
|
|
void CCSprite::useBatchNode(CCSpriteBatchNode *batchNode)
|
|
{
|
|
m_bUsesBatchNode = true;
|
|
m_pobTextureAtlas = m_pobBatchNode->getTextureAtlas(); // weak ref
|
|
}
|
|
|
|
void CCSprite::useSpriteSheetRender(CCSpriteSheetInternalOnly *pSpriteSheet)
|
|
{
|
|
useBatchNode(pSpriteSheet);
|
|
}
|
|
|
|
void CCSprite::initAnimationDictionary(void)
|
|
{
|
|
m_pAnimations = new NSMutableDictionary<string, CCAnimation*>();
|
|
}
|
|
|
|
void CCSprite::setTextureRect(CGRect rect)
|
|
{
|
|
CGRect rectInPixels = CC_RECT_POINTS_TO_PIXELS(rect);
|
|
setTextureRectInPixels(rectInPixels, false, rectInPixels.size);
|
|
}
|
|
|
|
|
|
void CCSprite::setTextureRectInPixels(CGRect rect, bool rotated, CGSize size)
|
|
{
|
|
m_obRectInPixels = rect;
|
|
m_obRect = CC_RECT_PIXELS_TO_POINTS(rect);
|
|
m_bRectRotated = rotated;
|
|
|
|
setContentSizeInPixels(size);
|
|
updateTextureCoords(m_obRectInPixels);
|
|
|
|
CGPoint relativeOffsetInPixels = m_obUnflippedOffsetPositionFromCenter;
|
|
|
|
// issue #732
|
|
if (m_bFlipX)
|
|
{
|
|
relativeOffsetInPixels.x = -relativeOffsetInPixels.x;
|
|
}
|
|
if (m_bFlipY)
|
|
{
|
|
relativeOffsetInPixels.y = -relativeOffsetInPixels.y;
|
|
}
|
|
|
|
m_obOffsetPositionInPixels.x = relativeOffsetInPixels.x + (m_tContentSizeInPixels.width - m_obRectInPixels.size.width) / 2;
|
|
m_obOffsetPositionInPixels.y = relativeOffsetInPixels.y + (m_tContentSizeInPixels.height - m_obRectInPixels.size.height) / 2;
|
|
|
|
// rendering using SpriteSheet
|
|
if (m_bUsesBatchNode)
|
|
{
|
|
// update dirty_, don't update recursiveDirty_
|
|
m_bDirty = true;
|
|
}
|
|
else
|
|
{
|
|
// self rendering
|
|
|
|
// Atlas: Vertex
|
|
float x1 = 0 + m_obOffsetPositionInPixels.x;
|
|
float y1 = 0 + m_obOffsetPositionInPixels.y;
|
|
float x2 = x1 + m_obRectInPixels.size.width;
|
|
float y2 = y1 + m_obRectInPixels.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);
|
|
}
|
|
}
|
|
|
|
|
|
void CCSprite::updateTextureCoords(CGRect rect)
|
|
{
|
|
CCTexture2D *tex = m_bUsesBatchNode ? 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;
|
|
right = left+(rect.size.height/atlasWidth);
|
|
top = rect.origin.y/atlasHeight;
|
|
bottom = top+(rect.size.width/atlasHeight);
|
|
#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;
|
|
right = left + rect.size.width/atlasWidth;
|
|
top = rect.origin.y/atlasHeight;
|
|
bottom = top + rect.size.height/atlasHeight;
|
|
#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)
|
|
{
|
|
assert(m_bUsesBatchNode);
|
|
|
|
// optimization. Quick return if not dirty
|
|
if (! m_bDirty)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CGAffineTransform matrix;
|
|
|
|
// Optimization: if it is not visible, then do nothing
|
|
if (! m_bIsVisible)
|
|
{
|
|
m_sQuad.br.vertices = m_sQuad.tl.vertices = m_sQuad.tr.vertices = m_sQuad.bl.vertices = vertex3(0,0,0);
|
|
m_pobTextureAtlas->updateQuad(&m_sQuad, m_uAtlasIndex);
|
|
m_bDirty = m_bRecursiveDirty = false;
|
|
return;
|
|
}
|
|
|
|
// Optimization: If parent is batchnode, or parent is nil
|
|
// build Affine transform manually
|
|
if (! m_pParent || m_pParent == m_pobBatchNode)
|
|
{
|
|
float radians = -CC_DEGREES_TO_RADIANS(m_fRotation);
|
|
float c = cosf(radians);
|
|
float s = sinf(radians);
|
|
|
|
matrix = CGAffineTransformMake(c * m_fScaleX, s * m_fScaleX,
|
|
-s * m_fScaleY, c * m_fScaleY,
|
|
m_tPositionInPixels.x, m_tPositionInPixels.y);
|
|
matrix = CGAffineTransformTranslate(matrix, -m_tAnchorPointInPixels.x, -m_tAnchorPointInPixels.y);
|
|
} else // parent_ != batchNode_
|
|
{
|
|
// else do affine transformation according to the HonorParentTransform
|
|
matrix = CGAffineTransformIdentity;
|
|
ccHonorParentTransform prevHonor = CC_HONOR_PARENT_TRANSFORM_ALL;
|
|
|
|
for (CCNode *p = this; p && p != m_pobBatchNode; p = p->getParent())
|
|
{
|
|
// Might happen. Issue #1053
|
|
// how to implement, we can not use dynamic
|
|
// NSAssert( [p isKindOfClass:[CCSprite class]], @"CCSprite should be a CCSprite subclass. Probably you initialized an sprite with a batchnode, but you didn't add it to the batch node." );
|
|
struct transformValues_ tv;
|
|
((CCSprite*)p)->getTransformValues(&tv);
|
|
|
|
// If any of the parents are not visible, then don't draw this node
|
|
if (! tv.visible)
|
|
{
|
|
m_sQuad.br.vertices = m_sQuad.tl.vertices = m_sQuad.tr.vertices = m_sQuad.bl.vertices = vertex3(0,0,0);
|
|
m_pobTextureAtlas->updateQuad(&m_sQuad, m_uAtlasIndex);
|
|
m_bDirty = m_bRecursiveDirty = false;
|
|
|
|
return;
|
|
}
|
|
|
|
CGAffineTransform newMatrix = CGAffineTransformIdentity;
|
|
|
|
// 2nd: Translate, Rotate, Scale
|
|
if( prevHonor & CC_HONOR_PARENT_TRANSFORM_TRANSLATE )
|
|
{
|
|
newMatrix = CGAffineTransformTranslate(newMatrix, tv.pos.x, tv.pos.y);
|
|
}
|
|
|
|
if( prevHonor & CC_HONOR_PARENT_TRANSFORM_ROTATE )
|
|
{
|
|
newMatrix = CGAffineTransformRotate(newMatrix, -CC_DEGREES_TO_RADIANS(tv.rotation));
|
|
}
|
|
|
|
if( prevHonor & CC_HONOR_PARENT_TRANSFORM_SCALE )
|
|
{
|
|
newMatrix = CGAffineTransformScale(newMatrix, tv.scale.x, tv.scale.y);
|
|
}
|
|
|
|
// 3rd: Translate anchor point
|
|
newMatrix = CGAffineTransformTranslate(newMatrix, -tv.ap.x, -tv.ap.y);
|
|
|
|
// 4th: Matrix multiplication
|
|
matrix = CGAffineTransformConcat( matrix, newMatrix);
|
|
|
|
prevHonor = ((CCSprite*)p)->getHornorParentTransform();
|
|
}
|
|
}
|
|
|
|
//
|
|
// calculate the Quad based on the Affine Matrix
|
|
//
|
|
CGSize size = m_obRectInPixels.size;
|
|
|
|
float x1 = m_obOffsetPositionInPixels.x;
|
|
float y1 = m_obOffsetPositionInPixels.y;
|
|
|
|
float x2 = x1 + size.width;
|
|
float y2 = y1 + size.height;
|
|
float x = matrix.tx;
|
|
float y = matrix.ty;
|
|
|
|
float cr = matrix.a;
|
|
float sr = matrix.b;
|
|
float cr2 = matrix.d;
|
|
float sr2 = -matrix.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((float)RENDER_IN_SUBPIXEL(ax), (float)RENDER_IN_SUBPIXEL(ay), m_fVertexZ);
|
|
m_sQuad.br.vertices = vertex3((float)RENDER_IN_SUBPIXEL(bx), (float)RENDER_IN_SUBPIXEL(by), m_fVertexZ);
|
|
m_sQuad.tl.vertices = vertex3((float)RENDER_IN_SUBPIXEL(dx), (float)RENDER_IN_SUBPIXEL(dy), m_fVertexZ);
|
|
m_sQuad.tr.vertices = vertex3((float)RENDER_IN_SUBPIXEL(cx), (float)RENDER_IN_SUBPIXEL(cy), m_fVertexZ);
|
|
|
|
m_pobTextureAtlas->updateQuad(&m_sQuad, m_uAtlasIndex);
|
|
m_bDirty = m_bRecursiveDirty = false;
|
|
}
|
|
|
|
// XXX: Optimization: instead of calling 5 times the parent sprite to obtain: position, scale.x, scale.y, anchorpoint and rotation,
|
|
// this fuction return the 5 values in 1 single call
|
|
void CCSprite::getTransformValues(struct transformValues_ *tv)
|
|
{
|
|
tv->pos = m_tPositionInPixels;
|
|
tv->scale.x = m_fScaleX;
|
|
tv->scale.y = m_fScaleY;
|
|
tv->rotation = m_fRotation;
|
|
tv->ap = m_tAnchorPointInPixels;
|
|
tv->visible = m_bIsVisible;
|
|
}
|
|
|
|
// draw
|
|
|
|
void CCSprite::draw(void)
|
|
{
|
|
assert(! m_bUsesBatchNode);
|
|
|
|
// Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
|
|
// Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
|
|
// Unneeded states: -
|
|
bool newBlend = m_sBlendFunc.src != CC_BLEND_SRC || m_sBlendFunc.dst != CC_BLEND_DST;
|
|
if (newBlend)
|
|
{
|
|
glBlendFunc(m_sBlendFunc.src, m_sBlendFunc.dst);
|
|
}
|
|
|
|
#define kQuadSize sizeof(m_sQuad.bl)
|
|
if (m_pobTexture)
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, m_pobTexture->getName());
|
|
}
|
|
else
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
long offset = (long)&m_sQuad;
|
|
|
|
// vertex
|
|
int diff = offsetof(ccV3F_C4B_T2F, vertices);
|
|
glVertexPointer(3, GL_FLOAT, kQuadSize, (void*)(offset + diff));
|
|
|
|
// color
|
|
diff = offsetof( ccV3F_C4B_T2F, colors);
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (void*)(offset + diff));
|
|
|
|
// tex coords
|
|
diff = offsetof( ccV3F_C4B_T2F, texCoords);
|
|
glTexCoordPointer(2, GL_FLOAT, kQuadSize, (void*)(offset + diff));
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
if( newBlend )
|
|
{
|
|
glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
|
|
}
|
|
|
|
#if CC_SPRITE_DEBUG_DRAW
|
|
CGSize s = m_tContentSize;
|
|
CGPoint vertices[4]={
|
|
ccp(0,0),ccp(s.width,0),
|
|
ccp(s.width,s.height),ccp(0,s.height),
|
|
};
|
|
ccDrawPoly(vertices, 4, true);
|
|
#endif // CC_SPRITE_DEBUG_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)
|
|
{
|
|
assert(pChild != NULL);
|
|
CCNode::addChild(pChild, zOrder, tag);
|
|
|
|
if (m_bUsesBatchNode)
|
|
{
|
|
assert(((CCSprite*)pChild)->getTexture()->getName() == m_pobTextureAtlas->getTexture()->getName());
|
|
unsigned int index = m_pobBatchNode->atlasIndexForChild((CCSprite*)(pChild), zOrder);
|
|
m_pobBatchNode->insertChild((CCSprite*)(pChild), index);
|
|
}
|
|
|
|
m_bHasChildren = true;
|
|
}
|
|
|
|
void CCSprite::reorderChild(CCNode *pChild, int zOrder)
|
|
{
|
|
assert(pChild != NULL);
|
|
assert(m_pChildren->containsObject(pChild));
|
|
|
|
if (zOrder == pChild->getZOrder())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_bUsesBatchNode)
|
|
{
|
|
// XXX: Instead of removing/adding, it is more efficient to reorder manually
|
|
pChild->retain();
|
|
removeChild(pChild, false);
|
|
addChild(pChild, zOrder);
|
|
pChild->release();
|
|
}
|
|
else
|
|
{
|
|
CCNode::reorderChild(pChild, zOrder);
|
|
}
|
|
}
|
|
|
|
void CCSprite::removeChild(CCNode *pChild, bool bCleanup)
|
|
{
|
|
if (m_bUsesBatchNode)
|
|
{
|
|
m_pobBatchNode->removeSpriteFromAtlas((CCSprite*)(pChild));
|
|
}
|
|
|
|
CCNode::removeChild(pChild, bCleanup);
|
|
|
|
}
|
|
|
|
void CCSprite::removeAllChildrenWithCleanup(bool bCleanup)
|
|
{
|
|
if (m_bUsesBatchNode)
|
|
{
|
|
CCSprite *pChild;
|
|
NSMutableArray<CCNode*>::NSMutableArrayIterator iter;
|
|
for (iter = m_pChildren->begin(); iter != m_pChildren->end(); ++iter)
|
|
{
|
|
pChild = (CCSprite*)(*iter);
|
|
CCX_BREAK_IF(! pChild);
|
|
m_pobBatchNode->removeSpriteFromAtlas(pChild);
|
|
}
|
|
}
|
|
|
|
CCNode::removeAllChildrenWithCleanup(bCleanup);
|
|
|
|
m_bHasChildren = false;
|
|
}
|
|
|
|
//
|
|
// CCNode property overloads
|
|
// used only when parent is CCSpriteBatchNode
|
|
//
|
|
|
|
void CCSprite::setDirtyRecursively(bool bValue)
|
|
{
|
|
m_bDirty = m_bRecursiveDirty = bValue;
|
|
// recursively set dirty
|
|
if (m_bHasChildren)
|
|
{
|
|
CCSprite *pChild;
|
|
NSMutableArray<CCNode*>::NSMutableArrayIterator iter;
|
|
for (iter = m_pChildren->begin(); iter != m_pChildren->end(); ++iter)
|
|
{
|
|
pChild = (CCSprite*)(*iter);
|
|
CCX_BREAK_IF(! pChild);
|
|
pChild->setDirtyRecursively(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// XXX HACK: optimization
|
|
#define SET_DIRTY_RECURSIVELY() { \
|
|
if (m_bUsesBatchNode && ! m_bRecursiveDirty) { \
|
|
m_bDirty = m_bRecursiveDirty = true; \
|
|
if ( m_bHasChildren) \
|
|
setDirtyRecursively(true); \
|
|
} \
|
|
}
|
|
|
|
void CCSprite::setPosition(CGPoint pos)
|
|
{
|
|
CCNode::setPosition(pos);
|
|
SET_DIRTY_RECURSIVELY();
|
|
}
|
|
|
|
void CCSprite::setPositionInPixels(CGPoint pos)
|
|
{
|
|
CCNode::setPositionInPixels(pos);
|
|
SET_DIRTY_RECURSIVELY();
|
|
}
|
|
|
|
void CCSprite::setRotation(float fRotation)
|
|
{
|
|
CCNode::setRotation(fRotation);
|
|
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(CGPoint anchor)
|
|
{
|
|
CCNode::setAnchorPoint(anchor);
|
|
SET_DIRTY_RECURSIVELY();
|
|
}
|
|
|
|
void CCSprite::setIsRelativeAnchorPoint(bool bRelative)
|
|
{
|
|
assert(! m_bUsesBatchNode);
|
|
CCNode::setIsRelativeAnchorPoint(bRelative);
|
|
}
|
|
|
|
void CCSprite::setIsVisible(bool bVisible)
|
|
{
|
|
CCNode::setIsVisible(bVisible);
|
|
SET_DIRTY_RECURSIVELY();
|
|
}
|
|
|
|
void CCSprite::setFlipX(bool bFlipX)
|
|
{
|
|
if (m_bFlipX != bFlipX)
|
|
{
|
|
m_bFlipX = bFlipX;
|
|
setTextureRectInPixels(m_obRectInPixels, m_bRectRotated, m_obRectInPixels.size);
|
|
}
|
|
}
|
|
|
|
bool CCSprite::isFlipX(void)
|
|
{
|
|
return m_bFlipX;
|
|
}
|
|
|
|
void CCSprite::setFlipY(bool bFlipY)
|
|
{
|
|
if (m_bFlipY != bFlipY)
|
|
{
|
|
m_bFlipY = bFlipY;
|
|
setTextureRectInPixels(m_obRectInPixels, m_bRectRotated, m_obRectInPixels.size);
|
|
}
|
|
}
|
|
|
|
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 Sprite Manager
|
|
if (m_bUsesBatchNode)
|
|
{
|
|
if (m_uAtlasIndex != CCSpriteIndexNotInitialized)
|
|
{
|
|
m_pobTextureAtlas->updateQuad(&m_sQuad, m_uAtlasIndex);
|
|
}
|
|
else
|
|
{
|
|
// no need to set it recursively
|
|
// update dirty_, don't update recursiveDirty_
|
|
m_bDirty = true;
|
|
}
|
|
}
|
|
|
|
// self render
|
|
// do nothing
|
|
}
|
|
|
|
GLubyte CCSprite::getOpacity(void)
|
|
{
|
|
return m_nOpacity;
|
|
}
|
|
|
|
void CCSprite::setOpacity(GLubyte opacity)
|
|
{
|
|
m_nOpacity = opacity;
|
|
|
|
// special opacity for premultiplied textures
|
|
if (m_bOpacityModifyRGB)
|
|
{
|
|
setColor(m_sColorUnmodified);
|
|
}
|
|
|
|
updateColor();
|
|
}
|
|
|
|
ccColor3B CCSprite::getColor(void)
|
|
{
|
|
if (m_bOpacityModifyRGB)
|
|
{
|
|
return m_sColorUnmodified;
|
|
}
|
|
|
|
return m_sColor;
|
|
}
|
|
|
|
void CCSprite::setColor(ccColor3B color3)
|
|
{
|
|
m_sColor = m_sColorUnmodified = color3;
|
|
|
|
if (m_bOpacityModifyRGB)
|
|
{
|
|
m_sColor.r = color3.r * m_nOpacity/255;
|
|
m_sColor.g = color3.g * m_nOpacity/255;
|
|
m_sColor.b = color3.b * m_nOpacity/255;
|
|
}
|
|
|
|
updateColor();
|
|
}
|
|
|
|
void CCSprite::setIsOpacityModifyRGB(bool bValue)
|
|
{
|
|
ccColor3B oldColor = m_sColor;
|
|
m_bOpacityModifyRGB = bValue;
|
|
m_sColor = oldColor;
|
|
}
|
|
|
|
bool CCSprite::getIsOpacityModifyRGB(void)
|
|
{
|
|
return m_bOpacityModifyRGB;
|
|
}
|
|
|
|
// Frames
|
|
|
|
void CCSprite::setDisplayFrame(CCSpriteFrame *pNewFrame)
|
|
{
|
|
m_obUnflippedOffsetPositionFromCenter = pNewFrame->getOffsetInPixels();
|
|
|
|
CCTexture2D *pNewTexture = pNewFrame->getTexture();
|
|
// update texture before updating texture rect
|
|
if (!m_pobTexture ||
|
|
pNewTexture->getName() != m_pobTexture->getName())
|
|
{
|
|
setTexture(pNewTexture);
|
|
}
|
|
|
|
// update rect
|
|
m_bRectRotated = pNewFrame->isRotated();
|
|
setTextureRectInPixels(pNewFrame->getRectInPixels(), pNewFrame->isRotated(), pNewFrame->getOriginalSizeInPixels());
|
|
}
|
|
|
|
// XXX deprecated
|
|
void CCSprite::setDisplayFrame(const char *pszAnimationName, int nFrameIndex)
|
|
{
|
|
if (! m_pAnimations)
|
|
{
|
|
initAnimationDictionary();
|
|
}
|
|
|
|
CCAnimation *pAnimation = m_pAnimations->objectForKey(std::string(pszAnimationName));
|
|
CCSpriteFrame *pFrame = pAnimation->getFrames()->getObjectAtIndex(nFrameIndex);
|
|
|
|
assert(pFrame);
|
|
|
|
setDisplayFrame(pFrame);
|
|
}
|
|
|
|
void CCSprite::setDisplayFrameWithAnimationName(const char *animationName, int frameIndex)
|
|
{
|
|
assert(animationName);
|
|
|
|
CCAnimation *a = CCAnimationCache::sharedAnimationCache()->animationByName(animationName);
|
|
|
|
assert(a);
|
|
|
|
CCSpriteFrame *frame = a->getFrames()->getObjectAtIndex(frameIndex);
|
|
|
|
assert(frame);
|
|
|
|
setDisplayFrame(frame);
|
|
}
|
|
|
|
bool CCSprite::isFrameDisplayed(CCSpriteFrame *pFrame)
|
|
{
|
|
CGRect r = pFrame->getRect();
|
|
|
|
return (CGRect::CGRectEqualToRect(r, m_obRect) &&
|
|
pFrame->getTexture()->getName() == m_pobTexture->getName());
|
|
}
|
|
|
|
CCSpriteFrame* CCSprite::displayedFrame(void)
|
|
{
|
|
return CCSpriteFrame::frameWithTexture(m_pobTexture, m_obRect);
|
|
}
|
|
|
|
void CCSprite::addAnimation(CCAnimation *pAnimation)
|
|
{
|
|
// lazy alloc
|
|
if (! m_pAnimations)
|
|
{
|
|
initAnimationDictionary();
|
|
}
|
|
|
|
m_pAnimations->setObject(pAnimation, pAnimation->getName());
|
|
}
|
|
|
|
CCAnimation* CCSprite::animationByName(const char *pszAnimationName)
|
|
{
|
|
assert(pszAnimationName != NULL);
|
|
|
|
return m_pAnimations->objectForKey(std::string(pszAnimationName));
|
|
}
|
|
|
|
// Texture protocol
|
|
|
|
void CCSprite::updateBlendFunc(void)
|
|
{
|
|
// CCSprite: updateBlendFunc doesn't work when the sprite is rendered using a CCSpriteSheet
|
|
assert (! m_bUsesBatchNode);
|
|
|
|
// it's possible to have an untextured sprite
|
|
if (! m_pobTexture || ! m_pobTexture->getHasPremultipliedAlpha())
|
|
{
|
|
m_sBlendFunc.src = GL_SRC_ALPHA;
|
|
m_sBlendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
|
|
setIsOpacityModifyRGB(false);
|
|
}
|
|
else
|
|
{
|
|
m_sBlendFunc.src = CC_BLEND_SRC;
|
|
m_sBlendFunc.dst = CC_BLEND_DST;
|
|
setIsOpacityModifyRGB(true);
|
|
}
|
|
}
|
|
|
|
void CCSprite::setTexture(CCTexture2D *texture)
|
|
{
|
|
// CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteSheet
|
|
assert(! m_bUsesBatchNode);
|
|
|
|
// we can not use RTTI, so we do not known the type of object
|
|
// accept texture==nil as argument
|
|
/*assert((! texture) || dynamic_cast<CCTexture2D*>(texture));*/
|
|
|
|
CCX_SAFE_RELEASE(m_pobTexture);
|
|
|
|
m_pobTexture = texture;
|
|
if (texture)
|
|
{
|
|
texture->retain();
|
|
}
|
|
|
|
updateBlendFunc();
|
|
}
|
|
|
|
CCTexture2D* CCSprite::getTexture(void)
|
|
{
|
|
return m_pobTexture;
|
|
}
|
|
}//namespace cocos2d
|