好记性不如铅笔头

C && C++, cocos2dx, 编程

cocos2dx学习笔记:CCSprite

CCSprite类的东西非常多,只能一部分一部分的分析和学习。

CONTENTS

各种创建方法,最终还是调用了initWithTexture 方法。

CCSprite* CCSprite::createWithTexture(CCTexture2D *pTexture)
{
。。。。。//==>>直接调用initWithTexture 
    if (pobSprite && pobSprite->initWithTexture(pTexture))
。。。。。
}

CCSprite* CCSprite::createWithTexture(CCTexture2D *pTexture, const CCRect& rect)
{
。。。。。//==>>直接调用initWithTexture 
    if (pobSprite && pobSprite->initWithTexture(pTexture, rect))
。。。。。
}

CCSprite* CCSprite::create(const char *pszFileName)
{
。。。。。//==>>调用initWithFile,间接调用initWithTexture 
    if (pobSprite && pobSprite->initWithFile(pszFileName))
。。。。。
}

CCSprite* CCSprite::create(const char *pszFileName, const CCRect& rect)
{
。。。。。//==>>调用initWithFile,间接调用initWithTexture 
    if (pobSprite && pobSprite->initWithFile(pszFileName, rect))
。。。。。
}

CCSprite* CCSprite::createWithSpriteFrame(CCSpriteFrame *pSpriteFrame)
{
。。。。。//==>>调用initWithSpriteFrame,间接调用initWithTexture 
    if (pSpriteFrame && pobSprite && pobSprite->initWithSpriteFrame(pSpriteFrame))
。。。。。
}

CCSprite* CCSprite::createWithSpriteFrameName(const char *pszSpriteFrameName)
{
。。。。。//==>>调用createWithSpriteFrame,间接调用initWithTexture 
    return createWithSpriteFrame(pFrame);
}

CCSprite* CCSprite::create()
{
。。。。。//==>>调用init,间接调用initWithTexture 
    if (pSprite && pSprite->init())
。。。。。
}

bool CCSprite::init(void)
{
。。。。。//==>>init方法内部调用initWithTexture 
    return initWithTexture(NULL, CCRectZero);
}

bool CCSprite::initWithTexture(CCTexture2D *pTexture, const CCRect& rect)
{
。。。。。//==>>调用initWithTexture,不旋转
    return initWithTexture(pTexture, rect, false);
}

bool CCSprite::initWithTexture(CCTexture2D *pTexture)
{
。。。。。//==>>调用initWithTexture,大小为texture的大小
    return initWithTexture(pTexture, rect);
}

bool CCSprite::initWithFile(const char *pszFilename)
{
。。。。。//==>>先把文件读入到texturecache的缓存中去,然后调用initWithTexture
    CCTexture2D *pTexture = CCTextureCache::sharedTextureCache()->addImage(pszFilename);
    if (pTexture)
    {
        CCRect rect = CCRectZero;
        rect.size = pTexture->getContentSize();
        return initWithTexture(pTexture, rect);
。。。。。
}

bool CCSprite::initWithFile(const char *pszFilename, const CCRect& rect)
{
。。。。。//==>>先把文件读入到texturecache的缓存中去,然后调用initWithTexture
    CCTexture2D *pTexture = CCTextureCache::sharedTextureCache()->addImage(pszFilename);
    if (pTexture)
    {
        return initWithTexture(pTexture, rect);
。。。。。
}

bool CCSprite::initWithSpriteFrame(CCSpriteFrame *pSpriteFrame)
{
    CCAssert(pSpriteFrame != NULL, "");
//==>>从精灵帧中取出texture,然后调用initWithTexture
    bool bRet = initWithTexture(pSpriteFrame->getTexture(), pSpriteFrame->getRect());
    setDisplayFrame(pSpriteFrame);

    return bRet;
}

bool CCSprite::initWithSpriteFrameName(const char *pszSpriteFrameName)
{
。。。。。//==>>从精灵帧缓存中取出精灵帧,然后调用initWithSpriteFrame
    CCSpriteFrame *pFrame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(pszSpriteFrameName);
    return initWithSpriteFrame(pFrame);
}

initWithTexture代码分析

bool CCSprite::initWithTexture(CCTexture2D *pTexture, const CCRect& rect, bool rotated)
{
    if (CCNodeRGBA::init())
    {
        m_pobBatchNode = NULL;//==>>不使用批量创建node方法
         
        //==>>m_bRecursiveDirty: Whether all of the sprite's children needs to be updated
        m_bRecursiveDirty = false;//==>>级联置脏为false
        setDirty(false);//==>>设置m_bDirty为false
        
        m_bOpacityModifyRGB = true;//==>>CCSprite继承自CCNodeRGBA,因此设置该属性为true
        
        m_sBlendFunc.src = CC_BLEND_SRC;//==>>设置混合模式
        m_sBlendFunc.dst = CC_BLEND_DST;
        
        m_bFlipX = m_bFlipY = false;    //==>>x,y均不翻转
        
        // default transform anchor: center
        setAnchorPoint(ccp(0.5f, 0.5f));//==>>设置锚点
        
        // zwoptex default values
        m_obOffsetPosition = CCPointZero;//==>>偏移量,默认为0,0
        
        m_bHasChildren = false;//==>>初始化时没有子节点
        
        // clean the Quad
        memset(&m_sQuad, 0, sizeof(m_sQuad));//==>>初始化时正方形纹理4个角的信息都为0
        
        // Atlas: Color
        ccColor4B tmpColor = { 255, 255, 255, 255 };//==>>初始化时设置正方形纹理4个角的颜色信息
        m_sQuad.bl.colors = tmpColor;
        m_sQuad.br.colors = tmpColor;
        m_sQuad.tl.colors = tmpColor;
        m_sQuad.tr.colors = tmpColor;

        // shader program   //==>>设置shader
        setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));
        
        // 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;
    }
    else
    {
        return false;
    }
}

setTexture代码分析

void CCSprite::setTexture(CCTexture2D *texture)
{
//==>>正确性判断,
    // If batchnode, then texture id should be the same
    CCAssert(! m_pobBatchNode || texture->getName() == m_pobBatchNode->getTexture()->getName(), "CCSprite: Batched sprites should use the same texture as the batchnode");
    // accept texture==nil as argument
    CCAssert( !texture || dynamic_cast<CCTexture2D*>(texture), "setTexture expects a CCTexture2D. Invalid argument");

//==>>如果texture为NULL 就找一个默认的。
    if (NULL == texture)
    {
        // Gets the texture by key firstly.
        texture = CCTextureCache::sharedTextureCache()->textureForKey(CC_2x2_WHITE_IMAGE_KEY);

        // If texture wasn't in cache, create it from RAW data.
        if (NULL == texture)
        {
            CCImage* image = new CCImage();
            bool isOK = image->initWithImageData(cc_2x2_white_image, sizeof(cc_2x2_white_image), CCImage::kFmtRawData, 2, 2, 8);
            CCAssert(isOK, "The 2x2 empty texture was created unsuccessfully.");

            texture = CCTextureCache::sharedTextureCache()->addUIImage(image, CC_2x2_WHITE_IMAGE_KEY);
            CC_SAFE_RELEASE(image);
        }
    }

//==>>如果m_pobBatchNode为NULL,并且texture和之前存放的不同,那么就更新下。
    if (!m_pobBatchNode && m_pobTexture != texture)
    {
        CC_SAFE_RETAIN(texture);
        CC_SAFE_RELEASE(m_pobTexture);
        m_pobTexture = texture;
        updateBlendFunc();
    }
}

//更新混合模式。
void CCSprite::updateBlendFunc(void)
{
    CCAssert (! m_pobBatchNode, "CCSprite: updateBlendFunc doesn't work when the sprite is rendered using a CCSpriteBatchNode");

    // it is possible to have an untextured sprite
    if (! m_pobTexture || ! m_pobTexture->hasPremultipliedAlpha())
    {
        m_sBlendFunc.src = GL_SRC_ALPHA;
        m_sBlendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
        setOpacityModifyRGB(false);
    }
    else
    {
        m_sBlendFunc.src = CC_BLEND_SRC;
        m_sBlendFunc.dst = CC_BLEND_DST;
        setOpacityModifyRGB(true);
    }
}
//==>>设置RGB模式
void CCSprite::setOpacityModifyRGB(bool modify)
{
    if (m_bOpacityModifyRGB != modify)
    {
        m_bOpacityModifyRGB = modify;
        updateColor();
    }
}

//==>>更新颜色
void CCSprite::updateColor(void)
{
    ccColor4B color4 = { _displayedColor.r, _displayedColor.g, _displayedColor.b, _displayedOpacity };
    
    // special opacity for premultiplied textures
	if (m_bOpacityModifyRGB)
    {
		color4.r *= _displayedOpacity/255.0f;
		color4.g *= _displayedOpacity/255.0f;
		color4.b *= _displayedOpacity/255.0f;
    }
//==>>更新正方形4个角的颜色值
    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
}

setTextureRect代码分析

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;
    }

    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;

    // 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);
    }
}

void CCNode::setContentSize(const CCSize & size)
{
//==>>如果大小没变,就直接返回。如果变了,就更新下大小,然后根据锚点比例,重新计算锚点的实际位置,将dirty置true。
    if ( ! size.equals(m_obContentSize))
    {
        m_obContentSize = size;
				//==>>m_obAnchorPoint就是我们经常在程序中设置的锚点比例。
				//==>>m_obAnchorPointInPoints是实际的锚点位置
        m_obAnchorPointInPoints = ccp(m_obContentSize.width * m_obAnchorPoint.x, m_obContentSize.height * m_obAnchorPoint.y );
        m_bTransformDirty = m_bInverseDirty = true;
    }
}

    /**
     * Sets the vertex rect.
     * It will be called internally by setTextureRect.
     * Useful if you want to create 2x images from SD images in Retina Display.
     * Do not call it manually. Use setTextureRect instead.
     */
void CCSprite::setVertexRect(const CCRect& rect)
{//==>>设置顶点矩阵的大小
    m_obRect = rect;
}

//==>>根据纹理的大小和sprite的大小设置纹理坐标
void CCSprite::setTextureCoords(CCRect rect)
{
//==>>首先将sprite的大小转换为像素大小
    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)
    {
    。。。。。
    //==>>旋转后的纹理坐标设置,和不旋转的代码差不多,只是坐标点是反的。
    。。。。。
    }
    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
//==>> 根据sprite的像素大小和纹理的像素宽高计算正方形纹理坐标的4个点。
        left    = rect.origin.x/atlasWidth;
        right    = (rect.origin.x + rect.size.width) / atlasWidth;
        top        = rect.origin.y/atlasHeight;
        bottom    = (rect.origin.y + 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);
        }

//==>>将计算好的4个点根据规则设置到正方形纹理的4个角中。
//==>>参考网址【 http://blog.sina.com.cn/s/blog_6d8189930100nfvw.html 】
        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;
    }
}

更新==》》

draw代码分析

在分析draw方法前,先看先以前的一部分学习记录:
首先是CCSprite使用的shader:

bool CCSprite::initWithTexture(CCTexture2D *pTexture, const CCRect& rect, bool rotated)
{
。。。。。
        setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));
。。。。。
}

我们在回过头来看下kCCUniformSampler中的Shader_PositionTextureColor的有关代码:

void CCShaderCache::loadDefaultShader(CCGLProgram *p, int type)
{
    switch (type) {
        case kCCShaderType_PositionTextureColor:
            p->initWithVertexShaderByteArray(ccPositionTextureColor_vert, ccPositionTextureColor_frag);
            
            p->addAttribute(kCCAttributeNamePosition, kCCVertexAttrib_Position);
            p->addAttribute(kCCAttributeNameColor, kCCVertexAttrib_Color);
            p->addAttribute(kCCAttributeNameTexCoord, kCCVertexAttrib_TexCoords);
            
            break;
。。。。。
    p->updateUniforms();
。。。。。
}

CCGLProgram中的有关代码:

void CCGLProgram::updateUniforms()
{
  m_uUniforms[kCCUniformPMatrix] = glGetUniformLocation(m_uProgram, kCCUniformPMatrix_s);
	m_uUniforms[kCCUniformMVMatrix] = glGetUniformLocation(m_uProgram, kCCUniformMVMatrix_s);
	m_uUniforms[kCCUniformMVPMatrix] = glGetUniformLocation(m_uProgram, kCCUniformMVPMatrix_s);
	
	m_uUniforms[kCCUniformTime] = glGetUniformLocation(m_uProgram, kCCUniformTime_s);
	m_uUniforms[kCCUniformSinTime] = glGetUniformLocation(m_uProgram, kCCUniformSinTime_s);
	m_uUniforms[kCCUniformCosTime] = glGetUniformLocation(m_uProgram, kCCUniformCosTime_s);
	
	m_bUsesTime = (
                 m_uUniforms[kCCUniformTime] != -1 ||
                 m_uUniforms[kCCUniformSinTime] != -1 ||
                 m_uUniforms[kCCUniformCosTime] != -1
                 );
    
		m_uUniforms[kCCUniformRandom01] = glGetUniformLocation(m_uProgram, kCCUniformRandom01_s);

    m_uUniforms[kCCUniformSampler] = glGetUniformLocation(m_uProgram, kCCUniformSampler_s);

    this->use();
    
    // Since sample most probably won't change, set it to 0 now.
    this->setUniformLocationWith1i(m_uUniforms[kCCUniformSampler], 0);
}
void CCGLProgram::setUniformLocationWith1i(GLint location, GLint i1)
{
。。。。。
        glUniform1i( (GLint)location, i1);
。。。。。
}

其中,kCCUniformSampler_s这个宏的定义为

#define kCCUniformSampler_s				"CC_Texture0"

再翻下shader的代码:

ccShader_PositionTextureColor_frag.h
"											\n\
#ifdef GL_ES								\n\
precision lowp float;						\n\
#endif										\n\
											\n\
varying vec4 v_fragmentColor;				\n\
varying vec2 v_texCoord;					\n\
uniform sampler2D CC_Texture0;				\n\
											\n\
void main()									\n\
{											\n\
	gl_FragColor = v_fragmentColor * texture2D(CC_Texture0, v_texCoord);			\n\
}											\n\
";

到了这里就很清楚了,在把shader读入到缓存的过程中,CC_Texture0这个uniform 最终被绑定到了‘0’这个位置上。
这里在分析一个draw中用到的一个函数:

//==>>如果调用了ccGLBindTexture2D,那么就直接将图像纹理textureId绑定到位置0上
void ccGLBindTexture2D(GLuint textureId)
{
    ccGLBindTexture2DN(0, textureId);
}
//==>>将纹理textureId绑定到位置textureUnit上
void ccGLBindTexture2DN(GLuint textureUnit, GLuint textureId)
{
#if CC_ENABLE_GL_STATE_CACHE
    CCAssert(textureUnit < kCCMaxActiveTexture, "textureUnit is too big");
    if (s_uCurrentBoundTexture[textureUnit] != textureId)
    {
        s_uCurrentBoundTexture[textureUnit] = textureId;
        //==>>glActiveTexture 函数设定当前的贴图单元,以至于后面的函数glBindTexture 绑定贴图到当前的单元
        glActiveTexture(GL_TEXTURE0 + textureUnit);
        glBindTexture(GL_TEXTURE_2D, textureId);//==>>将纹理textureId绑定到已经激活的位置上
    }
#else
    glActiveTexture(GL_TEXTURE0 + textureUnit);
    glBindTexture(GL_TEXTURE_2D, textureId);
#endif
}

然后来看下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 );
		//==>>将纹理绑定到opengl上
    ccGLBindTexture2D( m_pobTexture->getName() );
    //==>>允许顶点,纹理位置,颜色等坐标输入
    ccGLEnableVertexAttribs( kCCVertexAttribFlag_PosColorTex );

#define kQuadSize sizeof(m_sQuad.bl)
#ifdef EMSCRIPTEN
    long offset = 0;
    setGLBufferData(&m_sQuad, 4 * kQuadSize, 0);
#else
    long offset = (long)&m_sQuad;
#endif // EMSCRIPTEN

//==>>我们知道了ccV3F_C4B_T2F这个结构体的类型和指针,那么就可以通过offset方式来获取到里面指定成员变量的位置,然后就可以
//==>>通过(void*) (offset + diff)获取指定成员变量的位置。
    // 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);
。。。。。
。。。。。
}

 

发表评论

9 + 15 =

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据