Cocos2d-x2.0 粒子系统深入分析三部曲(二)

来源:互联网 发布:卓智网络 锐捷 编辑:程序博客网 时间:2024/06/05 06:38
 

Cocos2d-x2.0 粒子系统深入分析三部曲(二)

分类: Cocos2d-x学习 10604人阅读 评论(6) 收藏 举报

目录(?)[+]

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址:http://blog.csdn.net/honghaier]

                           红孩儿Cocos2d-X学习园地QQ2群:44208467加群写:Cocos2d-x
                           红孩儿Cocos2d-X学习园地QQ群:249941957加群写:Cocos2d-x

    

           Cocos2d-x2.0 粒子系统深入分析三部曲(二)


另:本章所用Cocos2d-x版本为:

cocos2d-2.0-x-2.0.2@ Aug 30 2012

http://cn.cocos2d-x.org/download


上一节我们了解了粒子系统的原理,也学习了Cocos2d-x中的两个有关粒子系统的类:

(1)      CCParticleSystem :粒子系统的基类,提供对粒子的创建和更新管理。

(2)      CCParticleBatchNode:粒子系统的批次结点,用于将使用相同纹理的粒子系统进行同批次渲染优化处理。

 

在学习CCParticleSystem时,我们留下了一些疑问,什么时候调用setBatchNode?以及做为基类,CCParticleSystem提供了两个供子类重载的纯虚函数postStep()和updateQuadWithParticle,它们的具体用法是什么?我们仍然有一些迷茫。

 

我们今天来了解一下CCParticleSystemQuad,这个类是CCParticleSystem的子类。它将解开我们的这些疑惑。

        

            打开CCParticleSystemQuad.h:

[cpp] view plaincopy
  1. class CC_DLL CCParticleSystemQuad : public CCParticleSystem  
  2. {  
  3. protected:  
  4.        //如果当前粒子系统未使用批次结点,则需要为粒子系统创建单独的顶点缓冲及索引缓冲以及OPENGL进行渲染的一些相关物件。  
  5.     ccV3F_C4B_T2F_Quad    *m_pQuads;    // OPENGL渲染图形所用的四边形顶点缓冲。  
  6.     GLushort            *m_pIndices;    // OPENGL渲染图形所用的索引缓冲。  
  7. //这里有宏判断当前OPENGL版本是否支持使用VAO处理顶点缓冲(VAO是什么?这个问题问的好,VAO是OPENGL3.X以上引入的新特性,VBO是Vertex Buffer Object, VAO是Vertex Array Object。 VAO是OpenGL 3.0以后才引入的新东西,但是在2.0版本中做为扩展接口。VBO其实就是显卡中的显存,为了提高渲染速度,可以将要绘制的顶点数据缓存在显存中,这样就不需要将要绘制的顶点数据重复从CPU发送到GPU, 浪费带宽资源。而VAO则是一个容器,可以包括多个VBO, 它类似于以前的call list, 由于它进一步将VBO容于其中,所以绘制效率将在VBO的基础上更进一步。)  
  8. #if CC_TEXTURE_ATLAS_USE_VAO  
  9.     GLuint                m_uVAOname;           //VAO的句柄。  
  10. #endif  
  11.        GLuint                m_pBuffersVBO[2];  //VBO的两个句柄,第一个句柄对应顶点缓冲,第二个句柄对应索引缓冲。  
  12. public:  
  13.        //构造函数。  
  14.     CCParticleSystemQuad();  
  15.        //析构函数。  
  16.     virtual ~CCParticleSystemQuad();  
  17.   
  18.     //创建函数,参为为PLIST,内部调用create实现。  
  19.     CC_DEPRECATED_ATTRIBUTE static CCParticleSystemQuad * particleWithFile(const char *plistFile);  
  20.   
  21.     //上面函数的create实现。  
  22.     static CCParticleSystemQuad * create(const char *plistFile);  
  23.   
  24.     //初始化索引缓冲。  
  25.     void setupIndices();  
  26.   
  27.     //初始化方理坐标。  
  28.     void initTexCoordsWithRect(const CCRect& rect);  
  29.   
  30.     //设置显示一个精灵帧。  
  31.     void setDisplayFrame(CCSpriteFrame *spriteFrame);  
  32.   
  33.     //设置使用纹理对象上指定的矩形图像区域做为粒子系统的贴图。  
  34.     void setTextureWithRect(CCTexture2D *texture, const CCRect& rect);  
  35.     // 重载基类粒子系统的相应函数。  
  36.        //初始化粒子数量。  
  37.     virtual bool initWithTotalParticles(unsigned int numberOfParticles);  
  38.        //设置所用的纹理对象指针  
  39.     virtual void setTexture(CCTexture2D* texture);  
  40.        //上一篇留下的疑问,虽然明显是更新粒子顶点缓冲中的位置数据。但上一篇为什么没有实现?  
  41.     virtual void updateQuadWithParticle(tCCParticle* particle, const CCPoint& newPosition);  
  42.        //上一篇留下的疑问,不知道是做什么,咱们到CPP中看吧。  
  43.     virtual void postStep();  
  44.        //渲染处理。  
  45.     virtual void draw();  
  46.        //设置批次结点。  
  47.     virtual void setBatchNode(CCParticleBatchNode* batchNode);  
  48.        //设置总的粒子数量。  
  49.     virtual void setTotalParticles(unsigned int tp);  
  50.     //监听响应当前结点的EVNET_COME_TO_FOREGROUND事件的回调函数。  
  51.     void listenBackToForeground(CCObject *obj);  
  52.   
  53.     //创建一个当前实例结点,内部调用create实现。  
  54.     CC_DEPRECATED_ATTRIBUTE static CCParticleSystemQuad * node();  
  55.        //上面的create实现。  
  56.     static CCParticleSystemQuad * create();  
  57. private:  
  58.         //如果使用VAO  
  59. #if CC_TEXTURE_ATLAS_USE_VAO  
  60.        //初始化VAO和VBO  
  61.     void setupVBOandVAO();  
  62. #else  
  63.     //初始化VBO  
  64.     void setupVBO();  
  65. #endif  
  66.        //申请内存。  
  67.     bool allocMemory();  
  68. };  

对应的实现:


[cpp] view plaincopy
  1. //重载粒子系统基类的初始化函数,创建相应数量的粒子。  
  2. bool CCParticleSystemQuad::initWithTotalParticles(unsigned int numberOfParticles)  
  3. {  
  4.     //调用基类的相应函数。  
  5.     if( CCParticleSystem::initWithTotalParticles(numberOfParticles) )   
  6.     {  
  7.         // 创建顶点和索引缓冲,如果失败释放并返回。  
  8.         if( ! this->allocMemory() ) {  
  9.             this->release();  
  10.             return false;  
  11.         }  
  12.         //填充索引缓冲。  
  13.         setupIndices();  
  14.         //如果当前OPENGL版本支持VAO,就创建VAO,如果不支持,只创建VBO。  
  15. #if CC_TEXTURE_ATLAS_USE_VAO  
  16.         setupVBOandVAO();  
  17. #else  
  18.         setupVBO();  
  19. #endif  
  20. //设置使用顶点格式为“位置+纹理+顶点色”的顶点格式组合。  
  21. setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));  
  22.   
  23. //告诉通知中心,注册函数listenBackToForeground用来响应当前结点的EVNET_COME_TO_FOREGROUND事件。这个事件的意义是程序将由后面返回到前台。这个事件响应时可做资源的重新载入。  
  24.   
  25. CCNotificationCenter::sharedNotificationCenter()->addObserver(this,  
  26.                                                                       callfuncO_selector(CCParticleSystemQuad::listenBackToForeground),  
  27.                                                                       EVNET_COME_TO_FOREGROUND,  
  28.                                                                       NULL);  
  29.           
  30.         return true;  
  31.     }  
  32.     return false;  
  33. }  
  34. //构造函数。  
  35. CCParticleSystemQuad::CCParticleSystemQuad()  
  36. :m_pQuads(NULL)  
  37. ,m_pIndices(NULL)  
  38. #if CC_TEXTURE_ATLAS_USE_VAO  
  39. ,m_uVAOname(0)  
  40. #endif  
  41. {  
  42.     memset(m_pBuffersVBO, 0, sizeof(m_pBuffersVBO));  
  43. }  
  44. //析构函数。  
  45. CCParticleSystemQuad::~CCParticleSystemQuad()  
  46. {  
  47.     //对所创建的顶点缓冲,索引缓冲,以及VBO,VA0进行释放。  
  48.     if (NULL == m_pBatchNode)  
  49.     {  
  50.         CC_SAFE_FREE(m_pQuads);  
  51.         CC_SAFE_FREE(m_pIndices);  
  52.         glDeleteBuffers(2, &m_pBuffersVBO[0]);  
  53. #if CC_TEXTURE_ATLAS_USE_VAO  
  54.         glDeleteVertexArrays(1, &m_uVAOname);  
  55. #endif  
  56.     }  
  57.       
  58. //注销对通知管理器注册的相应事件的响应处理函数。  
  59. CCNotificationCenter::sharedNotificationCenter()->removeObserver(this, EVNET_COME_TO_FOREGROUND);  
  60. }  
  61.   
  62. // 静态创建函数。由PLIST文件创建相应的当前实例对象,内部调用create实现。  
  63. CCParticleSystemQuad * CCParticleSystemQuad::particleWithFile(const char *plistFile)  
  64. {  
  65.     return CCParticleSystemQuad::create(plistFile);  
  66. }  
  67. //上面的create实现。  
  68. CCParticleSystemQuad * CCParticleSystemQuad::create(const char *plistFile)  
  69. {  
  70.     //创建一个CCParticleSystemQuad实例对象,进行初始化后交由内存管理器进行引用计数器的管理。  
  71.     CCParticleSystemQuad *pRet = new CCParticleSystemQuad();  
  72.     if (pRet && pRet->initWithFile(plistFile))  
  73.     {  
  74.         pRet->autorelease();  
  75.         return pRet;  
  76.     }  
  77.     //如果失败,删除并置空,返回NULL。  
  78.     CC_SAFE_DELETE(pRet);  
  79.     return pRet;  
  80. }  
  81.   
  82. // 初始化纹理坐标。  
  83. void CCParticleSystemQuad::initTexCoordsWithRect(const CCRect& pointRect)  
  84. {  
  85.     // 创建出相应的矩形。  
  86.     CCRect rect = CCRectMake(  
  87.         pointRect.origin.x * CC_CONTENT_SCALE_FACTOR(),  
  88.         pointRect.origin.y * CC_CONTENT_SCALE_FACTOR(),  
  89.         pointRect.size.width * CC_CONTENT_SCALE_FACTOR(),  
  90.         pointRect.size.height * CC_CONTENT_SCALE_FACTOR());  
  91.     //默认使用的是批次结点,以批次结点的纹理对设置所用的图像区域矩形宽高。  
  92.     GLfloat wide = (GLfloat) pointRect.size.width;  
  93.     GLfloat high = (GLfloat) pointRect.size.height;  
  94.     //如果使用单纹理对象,取得纹理的宽高。  
  95.     if (m_pTexture)  
  96.     {  
  97.         wide = (GLfloat)m_pTexture->getPixelsWide();  
  98.         high = (GLfloat)m_pTexture->getPixelsHigh();  
  99.     }  
  100.     //此宏是为了解决精灵边缘黑线而做的纹理坐标的微调。  
  101. #if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL  
  102.     GLfloat left = (rect.origin.x*2+1) / (wide*2);  
  103.     GLfloat bottom = (rect.origin.y*2+1) / (high*2);  
  104.     GLfloat right = left + (rect.size.width*2-2) / (wide*2);  
  105.     GLfloat top = bottom + (rect.size.height*2-2) / (high*2);  
  106. #else  
  107.     GLfloat left = rect.origin.x / wide;  
  108.     GLfloat bottom = rect.origin.y / high;  
  109.     GLfloat right = left + rect.size.width / wide;  
  110.     GLfloat top = bottom + rect.size.height / high;  
  111. #endif   
  112.   
  113.     //将top与bottom交换一下,因为在Cococs2d-x中坐标系Y轴是向上为正,这里处理一下后面可以做为顶点位置数据。  
  114.     CC_SWAP( top, bottom, float);  
  115.     //根据是否使用批次结点来取得相应的矩形顶点缓冲数组。  
  116.     ccV3F_C4B_T2F_Quad *quads = NULL;  
  117.     unsigned int start = 0, end = 0;  
  118.     if (m_pBatchNode)  
  119.     {   //如果使用批次结点,这里取得批次结点中所有粒子所对应的矩形顶点数组。  
  120.         quads = m_pBatchNode->getTextureAtlas()->getQuads();  
  121.         //取得起始和结束的粒子所对应的矩形索引。  
  122.         start = m_uAtlasIndex;  
  123.         end = m_uAtlasIndex + m_uTotalParticles;  
  124.     }  
  125.     else  
  126.     {   //如果使用单纹理对象,则起始矩形索引就是0,结束矩形索引就是最大粒子数量。  
  127.         quads = m_pQuads;  
  128.         start = 0;  
  129.         end = m_uTotalParticles;  
  130.     }  
  131.     //遍历所有的矩形顶点。  
  132.     for(unsigned int i=start; i<end; i++)   
  133.     {  
  134.         // 设置四个顶点的纹理坐标。  
  135.         quads[i].bl.texCoords.u = left;  
  136.         quads[i].bl.texCoords.v = bottom;  
  137.         quads[i].br.texCoords.u = right;  
  138.         quads[i].br.texCoords.v = bottom;  
  139.         quads[i].tl.texCoords.u = left;  
  140.         quads[i].tl.texCoords.v = top;  
  141.         quads[i].tr.texCoords.u = right;  
  142.         quads[i].tr.texCoords.v = top;  
  143.     }  
  144. }  
  145. //设置使用纹理对象上指定的矩形图像区域做为粒子系统的贴图。  
  146. void CCParticleSystemQuad::setTextureWithRect(CCTexture2D *texture, const CCRect& rect)  
  147. {  
  148.     // 如果当前尚无纹理对或者使用的纹理与参数指定的纹理不同。则设置使用参数指定的纹理。  
  149.     if( !m_pTexture || texture->getName() != m_pTexture->getName() )  
  150.     {  
  151.         CCParticleSystem::setTexture(texture);  
  152.     }  
  153.     //设置指定的矩形图像区域做为贴图计算粒子系统的纹理坐标.  
  154.     this->initTexCoordsWithRect(rect);  
  155. }  
  156. //设置使用的纹理对象。  
  157. void CCParticleSystemQuad::setTexture(CCTexture2D* texture)  
  158. {  
  159.     //取得纹理的大小。  
  160.     const CCSize& s = texture->getContentSize();  
  161.     //设置使用纹理对象上指定的矩形图像区域做为粒子系统的贴图。      
  162.     this->setTextureWithRect(texture, CCRectMake(0, 0, s.width, s.height));  
  163. }  
  164. //设置使用精灵帧中的纹理。  
  165. void CCParticleSystemQuad::setDisplayFrame(CCSpriteFrame *spriteFrame)  
  166. {  
  167.     //有效性判断。  
  168.     CCAssert(spriteFrame->getOffsetInPixels().equals(CCPointZero),   
  169.              "QuadParticle only supports SpriteFrames with no offsets");  
  170.   
  171.     // 如果当前尚无纹理对或者使用的纹理与参数指定的纹理不同。则设置使用参数指定的纹理。  
  172.     if ( !m_pTexture || spriteFrame->getTexture()->getName() != m_pTexture->getName())  
  173.     {  
  174.         //设置使用精灵帧中的纹理.  
  175.         this->setTexture(spriteFrame->getTexture());  
  176.     }  
  177. }  
  178. //填充索引缓冲。  
  179. void CCParticleSystemQuad::setupIndices()  
  180. {  
  181.     //遍历粒子数目计算索引缓冲值。  
  182.     for(unsigned int i = 0; i < m_uTotalParticles; ++i)  
  183.     {  
  184.         const unsigned int i6 = i*6;  
  185.         const unsigned int i4 = i*4;  
  186.         m_pIndices[i6+0] = (GLushort) i4+0;  
  187.         m_pIndices[i6+1] = (GLushort) i4+1;  
  188.         m_pIndices[i6+2] = (GLushort) i4+2;  
  189.   
  190.         m_pIndices[i6+5] = (GLushort) i4+1;  
  191.         m_pIndices[i6+4] = (GLushort) i4+2;  
  192.         m_pIndices[i6+3] = (GLushort) i4+3;  
  193.     }  
  194. }  
  195. //更新指定粒子的顶点缓冲中的位置数据。  
  196. void CCParticleSystemQuad::updateQuadWithParticle(tCCParticle* particle, const CCPoint& newPosition)  
  197. {  
  198.     //定义临时指针变量用于取得相应的粒子所对应的矩形顶点缓冲。  
  199.     ccV3F_C4B_T2F_Quad *quad;  
  200.     //如果使用了批次结点。  
  201.     if (m_pBatchNode)  
  202.     {  
  203.         //取得批次结点中对应的矩形顶点缓冲数组指针。  
  204.         ccV3F_C4B_T2F_Quad *batchQuads = m_pBatchNode->getTextureAtlas()->getQuads();  
  205.         //通过索引取得相应的矩形顶点缓冲。  
  206.         quad = &(batchQuads[m_uAtlasIndex+particle->atlasIndex]);  
  207.     }  
  208.     else  
  209.     {  
  210.         //如果没有使用批次结点,直接取得相应的粒子的矩形顶点缓冲。  
  211.         quad = &(m_pQuads[m_uParticleIdx]);  
  212.     }  
  213.     //根据是否由ALPHA值来设定RGB取得相应的颜色值。  
  214.     ccColor4B color = (m_bOpacityModifyRGB)  
  215.         ? ccc4( particle->color.r*particle->color.a*255, particle->color.g*particle->color.a*255, particle->color.b*particle->color.a*255, particle->color.a*255)  
  216.         : ccc4( particle->color.r*255, particle->color.g*255, particle->color.b*255, particle->color.a*255);  
  217.     //填真顶点缓冲中的颜色信息。  
  218.     quad->bl.colors = color;  
  219.     quad->br.colors = color;  
  220.     quad->tl.colors = color;  
  221.     quad->tr.colors = color;  
  222.   
  223.     // 设置顶点的位置信息  
  224.     GLfloat size_2 = particle->size/2;  
  225.     //判断是否进行旋转。  
  226.     if (particle->rotation)   
  227.     {  
  228.         //定义临时变量来存放以size_2为半径的矩形外圆上的四个顶点。  
  229.         GLfloat x1 = -size_2;  
  230.         GLfloat y1 = -size_2;  
  231.   
  232.         GLfloat x2 = size_2;  
  233.         GLfloat y2 = size_2;  
  234.         //定义临时变量来存放圆心。  
  235.         GLfloat x = newPosition.x;  
  236.         GLfloat y = newPosition.y;  
  237.         //求得旋转角度。  
  238.         GLfloat r = (GLfloat)-CC_DEGREES_TO_RADIANS(particle->rotation);  
  239.         //通过sin,cos来计算旋转后的矩形外圆上的四个角的顶点位置。  
  240.         GLfloat cr = cosf(r);  
  241.         GLfloat sr = sinf(r);  
  242.         GLfloat ax = x1 * cr - y1 * sr + x;  
  243.         GLfloat ay = x1 * sr + y1 * cr + y;  
  244.         GLfloat bx = x2 * cr - y1 * sr + x;  
  245.         GLfloat by = x2 * sr + y1 * cr + y;  
  246.         GLfloat cx = x2 * cr - y2 * sr + x;  
  247.         GLfloat cy = x2 * sr + y2 * cr + y;  
  248.         GLfloat dx = x1 * cr - y2 * sr + x;  
  249.         GLfloat dy = x1 * sr + y2 * cr + y;  
  250.   
  251.         // 填充计算旋转后的顶点位置信息。  
  252.         quad->bl.vertices.x = ax;  
  253.         quad->bl.vertices.y = ay;  
  254.         quad->br.vertices.x = bx;  
  255.         quad->br.vertices.y = by;  
  256.         quad->tl.vertices.x = dx;  
  257.         quad->tl.vertices.y = dy;  
  258.         quad->tr.vertices.x = cx;  
  259.         quad->tr.vertices.y = cy;  
  260.     }   
  261.     else   
  262.     {  
  263.         //如果不旋转,直接填充位置信息。  
  264.         quad->bl.vertices.x = newPosition.x - size_2;  
  265.         quad->bl.vertices.y = newPosition.y - size_2;  
  266.         quad->br.vertices.x = newPosition.x + size_2;  
  267.         quad->br.vertices.y = newPosition.y - size_2;  
  268.         quad->tl.vertices.x = newPosition.x - size_2;  
  269.         quad->tl.vertices.y = newPosition.y + size_2;  
  270.         quad->tr.vertices.x = newPosition.x + size_2;  
  271.         quad->tr.vertices.y = newPosition.y + size_2;                  
  272.     }  
  273. }  
  274. //我们一直想知道在CCParticleSystem中每次update时,如果粒子批次结点为空时为什么要调用postStep?它倒底是干什么的,看完下面的代码,就很清楚了。  
  275. void CCParticleSystemQuad::postStep()  
  276. {  
  277.     //绑定顶点缓冲区对象。  
  278.     glBindBuffer(GL_ARRAY_BUFFER, m_pBuffersVBO[0] );  
  279.     //用m_pQuads中数据更新绑定的缓冲区数据。  
  280.     glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(m_pQuads[0])*m_uParticleCount, m_pQuads);  
  281.     //取消绑定缓冲区对象。  
  282.     glBindBuffer(GL_ARRAY_BUFFER, 0);  
  283.     CHECK_GL_ERROR_DEBUG();  
  284.     //清楚了,原来这个函数是针对不使用批次结点时的VBO顶点缓冲的更新。  
  285. }  
  286.   
  287. // 绘制粒子系统。  
  288. void CCParticleSystemQuad::draw()  
  289. {  
  290.     //如果当前在粒子的批次结点有值,则draw()不应该被调用!为什么呢?因为有批次结点的话,渲染交给批次结点的draw()函数而不是当前粒子系统的draw()函数来处理。  
  291.     CCAssert(!m_pBatchNode,"draw should not be called when added to a particleBatchNode");  
  292.     //使用相应的Shader  
  293.     CC_NODE_DRAW_SETUP();  
  294.     //绑定所用的纹理对象。  
  295.     ccGLBindTexture2D( m_pTexture->getName() );  
  296.     //设定所用的ALPHA混合方案。  
  297.     ccGLBlendFunc( m_tBlendFunc.src, m_tBlendFunc.dst );  
  298.     //判断当前粒子索引是否为粒子总数,也就是判断是否已经update完成未出错。  
  299.     CCAssert( m_uParticleIdx == m_uParticleCount, "Abnormal error in particle quad");  
  300.     //如果使用VAO。  
  301. #if CC_TEXTURE_ATLAS_USE_VAO  
  302.     //使用VAO绑定的顶点数组  
  303.     glBindVertexArray( m_uVAOname );  
  304.     //绑定索引数组  
  305. #if CC_REBIND_INDICES_BUFFER  
  306.     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_pBuffersVBO[1]);  
  307. #endif  
  308.     //渲染调用  
  309.     glDrawElements(GL_TRIANGLES, (GLsizei) m_uParticleIdx*6, GL_UNSIGNED_SHORT, 0);  
  310.     //渲染完取消绑定索引数组  
  311. #if CC_REBIND_INDICES_BUFFER  
  312.     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);  
  313. #endif  
  314.     //取消绑定顶点数组  
  315.     glBindVertexArray( 0 );  
  316.   
  317. #else  
  318.     //  
  319.     // Using VBO without VAO  
  320.     //  
  321.     //  
  322.     #define kQuadSize sizeof(m_pQuads[0].bl)  
  323.     //设置使用相应的顶点格式为:位置+颜色+纹理坐标  
  324.     ccGLEnableVertexAttribs( kCCVertexAttribFlag_PosColorTex );  
  325.     // 绑定顶点缓冲  
  326.     glBindBuffer(GL_ARRAY_BUFFER, m_pBuffersVBO[0]);  
  327.     // 设置顶点缓冲中位置数据的描述  
  328.     glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices));  
  329.     //设置顶点缓冲中颜色数据的描述  
  330.     glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors));  
  331.     //设置顶点缓冲中纹理坐标数据的描述  
  332.     glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords));  
  333.     //绑定索引缓冲  
  334.     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_pBuffersVBO[1]);  
  335.     //渲染调用  
  336.     glDrawElements(GL_TRIANGLES, (GLsizei) m_uParticleIdx*6, GL_UNSIGNED_SHORT, 0);  
  337.     //取消绑定  
  338.     glBindBuffer(GL_ARRAY_BUFFER, 0);  
  339.     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);  
  340.   
  341. #endif  
  342.     //渲染调用计数器加一  
  343.     CC_INCREMENT_GL_DRAWS(1);  
  344.     CHECK_GL_ERROR_DEBUG();  
  345. }  
  346. //设置粒子数量  
  347. void CCParticleSystemQuad::setTotalParticles(unsigned int tp)  
  348. {  
  349.     // 如果要申请内存的粒子数量大于之前已经申请内存的粒子数量。  
  350.     if( tp > m_uAllocatedParticles )  
  351.     {  
  352.         // 计算要申请的内存大小。  
  353.         //粒子信息结构数组大小。   
  354.         size_t particlesSize = tp * sizeof(tCCParticle);  
  355.         //顶点缓冲的大小  
  356.         size_t quadsSize = sizeof(m_pQuads[0]) * tp * 1;  
  357.         //索引缓冲的大小  
  358.         size_t indicesSize = sizeof(m_pIndices[0]) * tp * 6 * 1;  
  359.         //在m_pParticles指定的内存位置申请相应大小的内存用于存储粒子信息结构数组。  
  360.         tCCParticle* particlesNew = (tCCParticle*)realloc(m_pParticles, particlesSize);  
  361.         //在m_pQuads指定的内存位置申请相应大小的内存用于填充顶点缓冲。  
  362.         ccV3F_C4B_T2F_Quad* quadsNew = (ccV3F_C4B_T2F_Quad*)realloc(m_pQuads, quadsSize);  
  363.         //在m_pIndices指定的内存位置申请相应大小的内存用于填充索引缓冲。  
  364.         GLushort* indicesNew = (GLushort*)realloc(m_pIndices, indicesSize);  
  365.         //如果申请都成功。  
  366.         if (particlesNew && quadsNew && indicesNew)  
  367.         {  
  368.             // 将内存地址传值给成员指针。  
  369.             m_pParticles = particlesNew;  
  370.             m_pQuads = quadsNew;  
  371.             m_pIndices = indicesNew;  
  372.   
  373.             // 内存清零  
  374.             memset(m_pParticles, 0, particlesSize);  
  375.             memset(m_pQuads, 0, quadsSize);  
  376.             memset(m_pIndices, 0, indicesSize);  
  377.             //记录申请内存的粒子数量。  
  378.             m_uAllocatedParticles = tp;  
  379.         }  
  380.         else  
  381.         {  
  382.             // 如果失败,记录成功申请的内存地址打印出错LOG。  
  383.             if (particlesNew) m_pParticles = particlesNew;  
  384.             if (quadsNew) m_pQuads = quadsNew;  
  385.             if (indicesNew) m_pIndices = indicesNew;  
  386.   
  387.             CCLOG("Particle system: out of memory");  
  388.             return;  
  389.         }  
  390.         //更新粒子数量。  
  391.         m_uTotalParticles = tp;  
  392.   
  393.         // 根据是否使用批次结点来设定每个粒子对应的矩形顶点块的索引。  
  394.         if (m_pBatchNode)  
  395.         {  
  396.             for (unsigned int i = 0; i < m_uTotalParticles; i++)  
  397.             {  
  398.                 m_pParticles[i].atlasIndex=i;  
  399.             }  
  400.         }  
  401.         //填充索引缓冲。  
  402.         setupIndices();  
  403.         //初始化VAO  
  404. #if CC_TEXTURE_ATLAS_USE_VAO  
  405.         setupVBOandVAO();  
  406. #else  
  407.         //初始化VBO  
  408.         setupVBO();  
  409. #endif  
  410.     }  
  411.     else  
  412.     {  
  413.         //如果要申请内存的粒子数量小于原来申请的粒子数量,直接修改一下最大粒子数量就OK。  
  414.         m_uTotalParticles = tp;  
  415.     }  
  416. }  
  417. //如果当前OPENGL版本支持VAO。  
  418. #if CC_TEXTURE_ATLAS_USE_VAO  
  419. //初始化VBO与VAO  
  420. void CCParticleSystemQuad::setupVBOandVAO()  
  421. {   //初始化1个顶点数组对象,产生VAO对象的句柄  
  422.     glGenVertexArrays(1, &m_uVAOname);  
  423.     //绑定VAO。  
  424.     glBindVertexArray(m_uVAOname);  
  425.   
  426. #define kQuadSize sizeof(m_pQuads[0].bl)  
  427.     //创建2个VBO缓冲区对象,产生两个句柄填充到句柄数组中。  
  428.     glGenBuffers(2, &m_pBuffersVBO[0]);  
  429.     //绑定第一个VBO缓冲区对象。   
  430.     glBindBuffer(GL_ARRAY_BUFFER, m_pBuffersVBO[0]);  
  431.     //将顶点数据拷贝到绑定的缓冲区。  
  432.     glBufferData(GL_ARRAY_BUFFER, sizeof(m_pQuads[0]) * m_uTotalParticles, m_pQuads, GL_DYNAMIC_DRAW);  
  433.   
  434.     //设置使用位置数据  
  435.     glEnableVertexAttribArray(kCCVertexAttrib_Position);  
  436.     //设置位置数据在顶点缓冲中的描述  
  437.     glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices));  
  438.   
  439.     //设置使用颜色数据  
  440.     glEnableVertexAttribArray(kCCVertexAttrib_Color);  
  441.     //设置颜色数据在顶点缓冲中的描述  
  442.     glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors));  
  443.   
  444.     //设置使用纹理坐标数据  
  445.     glEnableVertexAttribArray(kCCVertexAttrib_TexCoords);  
  446.     //设置纹理坐标数据在顶点缓冲中的描述  
  447.     glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords));  
  448.   
  449.     //绑定VBO的第二个缓冲区  
  450.     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_pBuffersVBO[1]);  
  451.     //将索引缓冲区数据拷贝到绑定的缓冲区  
  452.     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(m_pIndices[0]) * m_uTotalParticles * 6, m_pIndices, GL_STATIC_DRAW);  
  453.     //取消绑定  
  454.     glBindVertexArray(0);  
  455.     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);  
  456.     glBindBuffer(GL_ARRAY_BUFFER, 0);  
  457.   
  458.     CHECK_GL_ERROR_DEBUG();  
  459. }  
  460. #else  
  461. //否则只使用VBO  
  462. void CCParticleSystemQuad::setupVBO()  
  463. {  
  464.     //创建2个VBO缓冲区对象,产生两个句柄填充到句柄数组中。  
  465.     glGenBuffers(2, &m_pBuffersVBO[0]);  
  466.     //绑定第一个VBO缓冲区对象并填充数据。  
  467.     glBindBuffer(GL_ARRAY_BUFFER, m_pBuffersVBO[0]);  
  468.     glBufferData(GL_ARRAY_BUFFER, sizeof(m_pQuads[0]) * m_uTotalParticles, m_pQuads, GL_DYNAMIC_DRAW);  
  469.     glBindBuffer(GL_ARRAY_BUFFER, 0);  
  470.     //绑定第二个VBO缓冲区对象并填充数据。  
  471.     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_pBuffersVBO[1]);  
  472.     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(m_pIndices[0]) * m_uTotalParticles * 6, m_pIndices, GL_STATIC_DRAW);  
  473.     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);  
  474.       
  475.     CHECK_GL_ERROR_DEBUG();  
  476. }  
  477.   
  478. #endif  
  479. //响应当前结点的EVNET_COME_TO_FOREGROUND事件。这个事件的意义是程序将由后面返回到前台。这个事件响应时可做资源的重新载入。  
  480. void CCParticleSystemQuad::listenBackToForeground(CCObject *obj)  
  481. {  
  482.     //重新初始化VBO和VAO。  
  483. #if CC_TEXTURE_ATLAS_USE_VAO  
  484.         setupVBOandVAO();  
  485. #else  
  486.         setupVBO();  
  487. #endif  
  488. }  
  489. //创建顶点缓冲和索引缓冲区。  
  490. bool CCParticleSystemQuad::allocMemory()  
  491. {  
  492.     //如果已经申请则中断。  
  493.     CCAssert( ( !m_pQuads && !m_pIndices), "Memory already alloced");  
  494.     CCAssert( !m_pBatchNode, "Memory should not be alloced when not using batchNode");  
  495.     //释放顶点与索引缓冲  
  496.     CC_SAFE_FREE(m_pQuads);  
  497.     CC_SAFE_FREE(m_pIndices);  
  498.     //为顶点与索引缓冲申请内存。  
  499.     m_pQuads = (ccV3F_C4B_T2F_Quad*)malloc(m_uTotalParticles * sizeof(ccV3F_C4B_T2F_Quad));  
  500.     m_pIndices = (GLushort*)malloc(m_uTotalParticles * 6 * sizeof(GLushort));  
  501.     //如果出现失败,则提示LOG并释放置空返回。  
  502.     if( !m_pQuads || !m_pIndices)   
  503.     {  
  504.         CCLOG("cocos2d: Particle system: not enough memory");  
  505.         CC_SAFE_FREE(m_pQuads);  
  506.         CC_SAFE_FREE(m_pIndices);  
  507.   
  508.         return false;  
  509.     }  
  510.     //如果成功,置零操作返回true.  
  511.     memset(m_pQuads, 0, m_uTotalParticles * sizeof(ccV3F_C4B_T2F_Quad));  
  512.     memset(m_pIndices, 0, m_uTotalParticles * 6 * sizeof(GLushort));  
  513.   
  514.     return true;  
  515. }  
  516. //设置CCParticleSystemQuad使用的粒子批次结点。   
  517. void CCParticleSystemQuad::setBatchNode(CCParticleBatchNode * batchNode)  
  518. {  
  519.     //如果当前使用的粒子批次结点与参数不同,则进行更换处理。  
  520.     if( m_pBatchNode != batchNode )   
  521.     {  
  522.         //先记录一下当前使用的。  
  523.         CCParticleBatchNode* oldBatch = m_pBatchNode;  
  524.         //将后调用基类相应函数,设置当前使用的粒子批次结点。  
  525.         CCParticleSystem::setBatchNode(batchNode);  
  526.   
  527.         // 如果参数值为空,代表不使用批次结点。则进行顶点缓冲的相关初始化。  
  528.         if( ! batchNode )   
  529.         {  
  530.             //创建顶点缓冲,填充索引,并根据是否可用VAO设置顶点缓冲,设置纹理。  
  531.             allocMemory();  
  532.             setupIndices();  
  533.             setTexture(oldBatch->getTexture());  
  534. #if CC_TEXTURE_ATLAS_USE_VAO  
  535.             setupVBOandVAO();  
  536. #else  
  537.             setupVBO();  
  538. #endif  
  539.         }  
  540.         // 如果批次结点有效,且更换前没有使用粒子批次结点。  
  541.         else if( !oldBatch )  
  542.         {  
  543.             // 取得相应粒子批次结点的矩形数组,取出相应索引位置的矩形数据,将相应的顶点缓冲数据拷到矩形数组中。  
  544.             ccV3F_C4B_T2F_Quad *batchQuads = m_pBatchNode->getTextureAtlas()->getQuads();  
  545.             ccV3F_C4B_T2F_Quad *quad = &(batchQuads[m_uAtlasIndex] );  
  546.             memcpy( quad, m_pQuads, m_uTotalParticles * sizeof(m_pQuads[0]) );  
  547.             //释放当前用的顶点缓冲  
  548.             CC_SAFE_FREE(m_pQuads);  
  549.             CC_SAFE_FREE(m_pIndices);  
  550.             //释放所用的VBO顶点对象与AVO名称  
  551.             glDeleteBuffers(2, &m_pBuffersVBO[0]);  
  552. #if CC_TEXTURE_ATLAS_USE_VAO  
  553.             glDeleteVertexArrays(1, &m_uVAOname);  
  554. #endif  
  555.         }  
  556.     }  
  557. }  
  558. //创建一个CCParticleSystemQuad,内部调用create实现。  
  559. CCParticleSystemQuad * CCParticleSystemQuad::node()  
  560. {  
  561.     return CCParticleSystemQuad::create();  
  562. }  
  563. //上面函数的create实现。  
  564. CCParticleSystemQuad * CCParticleSystemQuad::create() {  
  565.     //创建一个CCParticleSystemQuad的实例对象。  
  566.     CCParticleSystemQuad *pParticleSystemQuad = new CCParticleSystemQuad();  
  567.     //如果创建成功,进行初始化。成功后交由内存管理器进行引用计数器的管理。  
  568.     if (pParticleSystemQuad && pParticleSystemQuad->init())  
  569.     {  
  570.         pParticleSystemQuad->autorelease();  
  571.         return pParticleSystemQuad;  
  572.     }  
  573.     //如果失败,释放置空并返回NULL。  
  574.     CC_SAFE_DELETE(pParticleSystemQuad);  
  575.     return NULL;  
  576. }  

         总结: 类CCParticleSystemQuad的源码进一步完善了粒子系统的功能,使我们可以在不需要批次结点时也能够实现粒子系统的OPENGL顶点和索引缓冲的创建和渲染,这么看来CCParticleSystem是一个不完整的粒子系统的类,它只提供了使用粒子批次结点的粒子系统的渲染方案。

 

        到这里,我们基本算领悟了Cocos2d-x中粒子系统的完整功能基类。后面我们将由此进入到多彩的粒子系统的演示中去!下课~

0 0
原创粉丝点击