1 /**************************************************************************** 2 Copyright (c) 2010-2012 cocos2d-x.org 3 Copyright (c) 2008-2010 Ricardo Quesada 4 Copyright (c) 2011 Zynga Inc. 5 6 http://www.cocos2d-x.org 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy 9 of this software and associated documentation files (the "Software"), to deal 10 in the Software without restriction, including without limitation the rights 11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 copies of the Software, and to permit persons to whom the Software is 13 furnished to do so, subject to the following conditions: 14 15 The above copyright notice and this permission notice shall be included in 16 all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 THE SOFTWARE. 25 ****************************************************************************/ 26 27 /** 28 * <p> 29 * CCParticleSystemQuad is a subclass of CCParticleSystem<br/> 30 * <br/> 31 * It includes all the features of ParticleSystem.<br/> 32 * <br/> 33 * Special features and Limitations:<br/> 34 * - Particle size can be any float number. <br/> 35 * - The system can be scaled <br/> 36 * - The particles can be rotated <br/> 37 * - It supports subrects <br/> 38 * - It supports batched rendering since 1.1<br/> 39 * </p> 40 * @class 41 * @extends cc.ParticleSystem 42 * @example 43 * //create a particle system 44 * this._emitter = new cc.ParticleSystemQuad(); 45 * this._emitter.initWithTotalParticles(150); 46 */ 47 cc.ParticleSystemQuad = cc.ParticleSystem.extend(/** @lends cc.ParticleSystemQuad# */{ 48 // quads to be rendered 49 _quads:null, 50 // indices 51 _indices:null, 52 53 _VAOname:0, 54 //0: vertex 1: indices 55 _buffersVBO:[], 56 57 _pointRect:null, 58 /** 59 * Constructor 60 * @override 61 */ 62 ctor:function () { 63 this._super(); 64 this._buffersVBO = [0, 0]; 65 this._quads = []; 66 this._indices = []; 67 this._pointRect = cc.RectZero(); 68 }, 69 70 /** 71 * initialices the indices for the vertices 72 */ 73 setupIndices:function () { 74 for (var i = 0; i < this._totalParticles; ++i) { 75 var i6 = i * 6; 76 var i4 = i * 4; 77 this._indices[i6 + 0] = i4 + 0; 78 this._indices[i6 + 1] = i4 + 1; 79 this._indices[i6 + 2] = i4 + 2; 80 81 this._indices[i6 + 5] = i4 + 1; 82 this._indices[i6 + 4] = i4 + 2; 83 this._indices[i6 + 3] = i4 + 3; 84 } 85 }, 86 87 /** 88 * <p> initilizes the texture with a rectangle measured Points<br/> 89 * pointRect should be in Texture coordinates, not pixel coordinates 90 * </p> 91 * @param {cc.Rect} pointRect 92 */ 93 initTexCoordsWithRect:function (pointRect) { 94 // convert to pixels coords 95 var rect = cc.rect( 96 pointRect.origin.x * cc.CONTENT_SCALE_FACTOR(), 97 pointRect.origin.y * cc.CONTENT_SCALE_FACTOR(), 98 pointRect.size.width * cc.CONTENT_SCALE_FACTOR(), 99 pointRect.size.height * cc.CONTENT_SCALE_FACTOR()); 100 101 var wide = pointRect.size.width; 102 var high = pointRect.size.height; 103 104 if (this._texture) { 105 if ((this._texture instanceof HTMLImageElement) || (this._texture instanceof HTMLCanvasElement)) { 106 wide = this._texture.width; 107 high = this._texture.height; 108 } else { 109 wide = this._texture.getPixelsWide(); 110 high = this._texture.getPixelsHigh(); 111 } 112 } 113 114 var left, bottom, right, top; 115 if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { 116 left = (rect.origin.x * 2 + 1) / (wide * 2); 117 bottom = (rect.origin.y * 2 + 1) / (high * 2); 118 right = left + (rect.size.width * 2 - 2) / (wide * 2); 119 top = bottom + (rect.size.height * 2 - 2) / (high * 2); 120 } else { 121 left = rect.origin.x / wide; 122 bottom = rect.origin.y / high; 123 right = left + rect.size.width / wide; 124 top = bottom + rect.size.height / high; 125 } 126 127 // Important. Texture in cocos2d are inverted, so the Y component should be inverted 128 var temp = top; 129 top = bottom; 130 bottom = temp; 131 132 var quads = null; 133 var start = 0, end = 0; 134 if (this._batchNode) { 135 quads = this._batchNode.getTextureAtlas().getQuads(); 136 start = this._atlasIndex; 137 end = this._atlasIndex + this._totalParticles; 138 } else { 139 quads = this._quads; 140 start = 0; 141 end = this._totalParticles; 142 } 143 144 for (var i = start; i < this.end; i++) { 145 if (!quads[i]) { 146 quads[i] = cc.V3F_C4B_T2F_QuadZero(); 147 } 148 149 // bottom-left vertex: 150 quads[i].bl.texCoords.u = left; 151 quads[i].bl.texCoords.v = bottom; 152 // bottom-right vertex: 153 quads[i].br.texCoords.u = right; 154 quads[i].br.texCoords.v = bottom; 155 // top-left vertex: 156 quads[i].tl.texCoords.u = left; 157 quads[i].tl.texCoords.v = top; 158 // top-right vertex: 159 quads[i].tr.texCoords.u = right; 160 quads[i].tr.texCoords.v = top; 161 } 162 }, 163 164 clone:function () { 165 var retParticle = new cc.ParticleSystemQuad(); 166 167 // self, not super 168 if (retParticle.initWithTotalParticles(this._totalParticles)) { 169 // angle 170 retParticle._angle = this._angle; 171 retParticle._angleVar = this._angleVar; 172 173 // duration 174 retParticle._duration = this._duration; 175 176 // blend function 177 retParticle._blendFunc.src = this._blendFunc.src; 178 retParticle._blendFunc.dst = this._blendFunc.dst; 179 180 // color 181 retParticle._startColor.r = this._startColor.r; 182 retParticle._startColor.g = this._startColor.g; 183 retParticle._startColor.b = this._startColor.b; 184 retParticle._startColor.a = this._startColor.a; 185 186 retParticle._startColorVar.r = this._startColorVar.r; 187 retParticle._startColorVar.g = this._startColorVar.g; 188 retParticle._startColorVar.b = this._startColorVar.b; 189 retParticle._startColorVar.a = this._startColorVar.a; 190 191 retParticle._endColor.r = this._endColor.r; 192 retParticle._endColor.g = this._endColor.g; 193 retParticle._endColor.b = this._endColor.b; 194 retParticle._endColor.a = this._endColor.a; 195 196 retParticle._endColorVar.r = this._endColorVar.r; 197 retParticle._endColorVar.g = this._endColorVar.g; 198 retParticle._endColorVar.b = this._endColorVar.b; 199 retParticle._endColorVar.a = this._endColorVar.a; 200 201 // particle size 202 retParticle._startSize = this._startSize; 203 retParticle._startSizeVar = this._startSizeVar; 204 retParticle._endSize = this._endSize; 205 retParticle._endSizeVar = this._endSizeVar; 206 207 // position 208 retParticle.setPosition(new cc.Point(this._position.x, this._position.y)); 209 retParticle._posVar.x = this._posVar.x; 210 retParticle._posVar.y = this._posVar.y; 211 212 // Spinning 213 retParticle._startSpin = this._startSpin; 214 retParticle._startSpinVar = this._startSpinVar; 215 retParticle._endSpin = this._endSpin; 216 retParticle._endSpinVar = this._endSpinVar; 217 218 retParticle._emitterMode = this._emitterMode; 219 220 // Mode A: Gravity + tangential accel + radial accel 221 if (this._emitterMode == cc.PARTICLE_MODE_GRAVITY) { 222 // gravity 223 retParticle.modeA.gravity.x = this.modeA.gravity.x; 224 retParticle.modeA.gravity.y = this.modeA.gravity.y; 225 226 // speed 227 retParticle.modeA.speed = this.modeA.speed; 228 retParticle.modeA.speedVar = this.modeA.speedVar; 229 230 // radial acceleration 231 retParticle.modeA.radialAccel = this.modeA.radialAccel; 232 233 retParticle.modeA.radialAccelVar = this.modeA.radialAccelVar; 234 235 // tangential acceleration 236 retParticle.modeA.tangentialAccel = this.modeA.tangentialAccel; 237 238 retParticle.modeA.tangentialAccelVar = this.modeA.tangentialAccelVar; 239 } else if (this._emitterMode == cc.PARTICLE_MODE_RADIUS) { 240 // or Mode B: radius movement 241 retParticle.modeB.startRadius = this.modeB.startRadius; 242 retParticle.modeB.startRadiusVar = this.modeB.startRadiusVar; 243 retParticle.modeB.endRadius = this.modeB.endRadius; 244 retParticle.modeB.endRadiusVar = this.modeB.endRadiusVar; 245 retParticle.modeB.rotatePerSecond = this.modeB.rotatePerSecond; 246 retParticle.modeB.rotatePerSecondVar = this.modeB.rotatePerSecondVar; 247 } 248 249 // life span 250 retParticle._life = this._life; 251 retParticle._lifeVar = this._lifeVar; 252 253 // emission Rate 254 retParticle._emissionRate = this._emissionRate; 255 256 //don't get the internal texture if a batchNode is used 257 if (!this._batchNode) { 258 // Set a compatible default for the alpha transfer 259 retParticle._opacityModifyRGB = this._opacityModifyRGB; 260 261 // texture 262 if (this._texture instanceof cc.Texture2D) { 263 retParticle._texture = this._texture; 264 } else { 265 retParticle._texture = this._texture; 266 } 267 } 268 } 269 return retParticle; 270 }, 271 272 /** 273 * <p> Sets a new CCSpriteFrame as particle.</br> 274 * WARNING: this method is experimental. Use setTexture:withRect instead. 275 * </p> 276 * @param {cc.SpriteFrame} spriteFrame 277 */ 278 setDisplayFrame:function (spriteFrame) { 279 cc.Assert(cc.Point.CCPointEqualToPoint(spriteFrame.getOffsetInPixels(), cc.PointZero()), "QuadParticle only supports SpriteFrames with no offsets"); 280 281 // update texture before updating texture rect 282 if (!this._texture || spriteFrame.getTexture().getName() != this._texture.getName()) { 283 this.setTexture(spriteFrame.getTexture()); 284 } 285 }, 286 287 /** 288 * Sets a new texture with a rect. The rect is in Points. 289 * @param {cc.Texture2D} texture 290 * @param {cc.Rect} rect 291 */ 292 setTextureWithRect:function (texture, rect) { 293 if (texture instanceof cc.Texture2D) { 294 // Only update the texture if is different from the current one 295 if (!this._texture || texture.getName() != this._texture.getName()) { 296 this.setTexture(texture, true); 297 } 298 this._pointRect = rect; 299 this.initTexCoordsWithRect(rect); 300 } 301 if (texture instanceof HTMLImageElement) { 302 if (!this._texture || texture != this._texture) { 303 this.setTexture(texture, true); 304 } 305 this._pointRect = rect; 306 this.initTexCoordsWithRect(rect); 307 } 308 }, 309 310 // super methods 311 // overriding the init method 312 /** 313 * Initializes a system with a fixed number of particles 314 * @override 315 * @param {Number} numberOfParticles 316 * @return {Boolean} 317 */ 318 initWithTotalParticles:function (numberOfParticles) { 319 // base initialization 320 if (this._super(numberOfParticles)) { 321 // allocating data space 322 if (!this._allocMemory()) { 323 return false; 324 } 325 this.setupIndices(); 326 if (cc.TEXTURE_ATLAS_USE_VAO) { 327 this._setupVBOandVAO(); 328 } else { 329 this._setupVBO(); 330 } 331 332 //this.setShaderProgram(cc.ShaderCache.getInstance().programForKey(kCCShader_PositionTextureColor)); 333 334 // Need to listen the event only when not use batchnode, because it will use VBO 335 //extension.CCNotificationCenter.sharedNotificationCenter().addObserver(this, 336 // callfuncO_selector(cc.ParticleSystemQuad.listenBackToForeground), 337 // EVNET_COME_TO_FOREGROUND, 338 // null); 339 340 return true; 341 } 342 return false; 343 }, 344 345 /** 346 * set Texture of Particle System 347 * @override 348 * @param {HTMLImageElement|HTMLCanvasElement|cc.Texture2D} texture 349 * @param {Boolean} isCallSuper is direct call super method 350 */ 351 setTexture:function (texture, isCallSuper) { 352 if (isCallSuper) { 353 if (isCallSuper == true) { 354 this._super(texture); 355 return; 356 } 357 } 358 var size = null; 359 if ((texture instanceof HTMLImageElement) || (texture instanceof HTMLCanvasElement)) { 360 size = cc.size(texture.width, texture.height); 361 } else { 362 size = texture.getContentSize(); 363 } 364 365 this.setTextureWithRect(texture, cc.rect(0, 0, size.width, size.height)); 366 }, 367 368 /** 369 * update particle's quad 370 * @override 371 * @param {cc.Particle} particle 372 * @param {cc.Point} newPosition 373 */ 374 updateQuadWithParticle:function (particle, newPosition) { 375 // colors 376 var quad = null; 377 if (this._batchNode) { 378 var batchQuads = this._batchNode.getTextureAtlas().getQuads(); 379 quad = batchQuads[this._atlasIndex + particle.atlasIndex] 380 } else { 381 quad = this._quads[this._particleIdx]; 382 } 383 384 var color = (this._opacityModifyRGB) ? 385 new cc.Color4B(0 | (particle.color.r * particle.color.a * 255), 0 | (particle.color.g * particle.color.a * 255), 386 0 | (particle.color.b * particle.color.a * 255), 0 | (particle.color.a * 255)) : 387 new cc.Color4B(0 | (particle.color.r * 255), 0 | (particle.color.g * 255), 0 | (particle.color.b * 255), 0 | (particle.color.a * 255)); 388 389 quad.bl.colors = color; 390 quad.br.colors = color; 391 quad.tl.colors = color; 392 quad.tr.colors = color; 393 394 // vertices 395 var size_2 = particle.size / 2; 396 if (particle.rotation) { 397 var x1 = -size_2; 398 var y1 = -size_2; 399 400 var x2 = size_2; 401 var y2 = size_2; 402 var x = newPosition.x; 403 var y = newPosition.y; 404 405 var r = -cc.DEGREES_TO_RADIANS(particle.rotation); 406 var cr = Math.cos(r); 407 var sr = Math.sin(r); 408 var ax = x1 * cr - y1 * sr + x; 409 var ay = x1 * sr + y1 * cr + y; 410 var bx = x2 * cr - y1 * sr + x; 411 var by = x2 * sr + y1 * cr + y; 412 var cx = x2 * cr - y2 * sr + x; 413 var cy = x2 * sr + y2 * cr + y; 414 var dx = x1 * cr - y2 * sr + x; 415 var dy = x1 * sr + y2 * cr + y; 416 417 // bottom-left 418 quad.bl.vertices.x = ax; 419 quad.bl.vertices.y = ay; 420 421 // bottom-right vertex: 422 quad.br.vertices.x = bx; 423 quad.br.vertices.y = by; 424 425 // top-left vertex: 426 quad.tl.vertices.x = dx; 427 quad.tl.vertices.y = dy; 428 429 // top-right vertex: 430 quad.tr.vertices.x = cx; 431 quad.tr.vertices.y = cy; 432 } else { 433 // bottom-left vertex: 434 quad.bl.vertices.x = newPosition.x - size_2; 435 quad.bl.vertices.y = newPosition.y - size_2; 436 437 // bottom-right vertex: 438 quad.br.vertices.x = newPosition.x + size_2; 439 quad.br.vertices.y = newPosition.y - size_2; 440 441 // top-left vertex: 442 quad.tl.vertices.x = newPosition.x - size_2; 443 quad.tl.vertices.y = newPosition.y + size_2; 444 445 // top-right vertex: 446 quad.tr.vertices.x = newPosition.x + size_2; 447 quad.tr.vertices.y = newPosition.y + size_2; 448 } 449 }, 450 451 /** 452 * override cc.ParticleSystem 453 * @override 454 */ 455 postStep:function () { 456 if (cc.renderContextType == cc.CANVAS) { 457 458 } else { 459 //TODO 460 glBindBuffer(GL_ARRAY_BUFFER, this._buffersVBO[0]); 461 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(this._quads[0]) * particleCount, this._quads); 462 glBindBuffer(GL_ARRAY_BUFFER, 0); 463 464 CHECK_GL_ERROR_DEBUG(); 465 } 466 }, 467 468 /** 469 * draw particle 470 * @param {CanvasContext} ctx CanvasContext 471 * @override 472 */ 473 draw:function (ctx) { 474 cc.Assert(!this._batchNode, "draw should not be called when added to a particleBatchNode"); 475 //this._super(); 476 var context = ctx || cc.renderContext; 477 context.save(); 478 if (this.isBlendAdditive()) 479 context.globalCompositeOperation = 'lighter'; 480 else 481 context.globalCompositeOperation = 'source-over'; 482 483 for (var i = 0; i < this._particleCount; i++) { 484 var particle = this._particles[i]; 485 var lpx = (0 | (particle.size * 0.5)); 486 487 //TODO these are temporary code, need modifier 488 if (this._drawMode == cc.PARTICLE_TEXTURE_MODE) { 489 var drawTexture = this.getTexture(); 490 if (particle.isChangeColor) { 491 var cacheTextureForColor = cc.TextureCache.getInstance().getTextureColors(this.getTexture()); 492 if (cacheTextureForColor) 493 drawTexture = cc.generateTintImage(this.getTexture(), cacheTextureForColor, particle.color, this._pointRect); 494 } 495 context.save(); 496 context.globalAlpha = particle.color.a; 497 context.translate(0 | particle.drawPos.x, -(0 | particle.drawPos.y)); 498 if (particle.rotation) 499 context.rotate(cc.DEGREES_TO_RADIANS(particle.rotation)); 500 context.drawImage(drawTexture, -lpx, -lpx, 501 particle.size, particle.size); 502 context.restore(); 503 } else { 504 context.save(); 505 context.globalAlpha = particle.color.a; 506 context.translate(0 | particle.drawPos.x, -(0 | particle.drawPos.y)); 507 508 if (this._shapeType == cc.PARTICLE_STAR_SHAPE) { 509 if (particle.rotation) 510 context.rotate(cc.DEGREES_TO_RADIANS(particle.rotation)); 511 cc.drawingUtil.drawStar(context, lpx, particle.color); 512 } else 513 cc.drawingUtil.drawColorBall(context, lpx, particle.color); 514 context.restore(); 515 } 516 } 517 context.restore(); 518 519 cc.INCREMENT_GL_DRAWS(1); 520 }, 521 522 _drawForWebGL:function (ctx) { 523 cc.NODE_DRAW_SETUP(); 524 525 ccGLBindTexture2D(this._texture.getName()); 526 ccGLBlendFunc(m_tBlendFunc.src, m_tBlendFunc.dst); 527 528 cc.Assert(this._particleIdx == this._particleCount, "Abnormal error in particle quad"); 529 530 if (cc.TEXTURE_ATLAS_USE_VAO) { 531 // 532 // Using VBO and VAO 533 // 534 glBindVertexArray(this._VAOname); 535 536 if (cc.REBIND_INDICES_BUFFER) 537 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]); 538 539 glDrawElements(GL_TRIANGLES, this._particleIdx * 6, GL_UNSIGNED_SHORT, 0); 540 541 if (cc.REBIND_INDICES_BUFFER) 542 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 543 544 glBindVertexArray(0); 545 } else { 546 // 547 // Using VBO without VAO 548 // 549 var kQuadSize = sizeof(m_pQuads[0].bl); 550 551 ccGLEnableVertexAttribs(kCCVertexAttribFlag_PosColorTex); 552 553 glBindBuffer(GL_ARRAY_BUFFER, this._buffersVBO[0]); 554 // vertices 555 glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, offsetof(ccV3F_C4B_T2F, vertices)); 556 // colors 557 glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, offsetof(ccV3F_C4B_T2F, colors)); 558 // tex coords 559 glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, offsetof(ccV3F_C4B_T2F, texCoords)); 560 561 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]); 562 563 glDrawElements(GL_TRIANGLES, this._particleIdx * 6, GL_UNSIGNED_SHORT, 0); 564 565 glBindBuffer(GL_ARRAY_BUFFER, 0); 566 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 567 } 568 CHECK_GL_ERROR_DEBUG(); 569 570 cc.INCREMENT_GL_DRAWS(1); 571 }, 572 573 setBatchNode:function (batchNode) { 574 if (this._batchNode != batchNode) { 575 var oldBatch = this._batchNode; 576 577 this._super(batchNode); 578 579 // NEW: is self render ? 580 if (!batchNode) { 581 this._allocMemory(); 582 this.setupIndices(); 583 this.setTexture(oldBatch.getTexture()); 584 if (cc.TEXTURE_ATLAS_USE_VAO) 585 this._setupVBOandVAO(); 586 else 587 this._setupVBO(); 588 } 589 // OLD: was it self render ? cleanup 590 else if (!oldBatch) { 591 // copy current state to batch 592 var batchQuads = this._batchNode.getTextureAtlas().getQuads(); 593 var quad = batchQuads[this._atlasIndex]; 594 //memcpy( quad, m_pQuads, m_uTotalParticles * sizeof(m_pQuads[0]) ); 595 596 glDeleteBuffers(2, this._buffersVBO[0]); 597 if (cc.TEXTURE_ATLAS_USE_VAO) 598 glDeleteVertexArrays(1, this._VAOname); 599 } 600 } 601 }, 602 603 setTotalParticles:function (tp) { 604 //TODO 605 if(tp < 200) 606 this._totalParticles = tp; 607 else 608 this._totalParticles = 200; 609 610 return; 611 612 // If we are setting the total numer of particles to a number higher 613 // than what is allocated, we need to allocate new arrays 614 if (tp > m_uAllocatedParticles) { 615 // Allocate new memory 616 var particlesSize = tp * sizeof(tCCParticle); 617 var quadsSize = sizeof(this._quads[0]) * tp * 1; 618 var indicesSize = sizeof(m_pIndices[0]) * tp * 6 * 1; 619 620 //var particlesNew = (tCCParticle*)realloc(m_pParticles, particlesSize); 621 //ccV3F_C4B_T2F_Quad* quadsNew = (ccV3F_C4B_T2F_Quad*)realloc(m_pQuads, quadsSize); 622 //GLushort* indicesNew = (GLushort*)realloc(m_pIndices, indicesSize); 623 624 if (particlesNew && quadsNew && indicesNew) { 625 // Assign pointers 626 m_pParticles = particlesNew; 627 m_pQuads = quadsNew; 628 m_pIndices = indicesNew; 629 630 // Clear the memory 631 memset(m_pParticles, 0, particlesSize); 632 memset(m_pQuads, 0, quadsSize); 633 memset(m_pIndices, 0, indicesSize); 634 635 m_uAllocatedParticles = tp; 636 } else { 637 // Out of memory, failed to resize some array 638 if (particlesNew) m_pParticles = particlesNew; 639 if (quadsNew) m_pQuads = quadsNew; 640 if (indicesNew) m_pIndices = indicesNew; 641 642 cc.log("Particle system: out of memory"); 643 return; 644 } 645 646 m_uTotalParticles = tp; 647 648 // Init particles 649 if (this._batchNode) { 650 for (var i = 0; i < m_uTotalParticles; i++) { 651 this._particles[i].atlasIndex = i; 652 } 653 } 654 655 this.setupIndices(); 656 if (cc.TEXTURE_ATLAS_USE_VAO) 657 this._setupVBOandVAO(); 658 else 659 this._setupVBO(); 660 661 } 662 else { 663 m_uTotalParticles = tp; 664 } 665 }, 666 667 /** 668 * listen the event that coming to foreground on Android 669 * @param {cc.Class} obj 670 */ 671 listenBackToForeground:function (obj) { 672 if (cc.TEXTURE_ATLAS_USE_VAO) 673 this._setupVBOandVAO(); 674 else 675 this._setupVBO(); 676 }, 677 678 _setupVBOandVAO:function () { 679 if (cc.renderContextType == cc.CANVAS) { 680 return; 681 } 682 683 glGenVertexArrays(1, this._VAOname); 684 glBindVertexArray(this._VAOname); 685 686 var kQuadSize = sizeof(m_pQuads[0].bl) 687 688 glGenBuffers(2, this._buffersVBO[0]); 689 690 glBindBuffer(GL_ARRAY_BUFFER, this._buffersVBO[0]); 691 glBufferData(GL_ARRAY_BUFFER, sizeof(this._quads[0]) * this._totalParticles, this._quads, GL_DYNAMIC_DRAW); 692 693 // vertices 694 glEnableVertexAttribArray(kCCVertexAttrib_Position); 695 glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, kQuadSize, offsetof(ccV3F_C4B_T2F, vertices)); 696 697 // colors 698 glEnableVertexAttribArray(kCCVertexAttrib_Color); 699 glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, offsetof(ccV3F_C4B_T2F, colors)); 700 701 // tex coords 702 glEnableVertexAttribArray(kCCVertexAttrib_TexCoords); 703 glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, offsetof(ccV3F_C4B_T2F, texCoords)); 704 705 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]); 706 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(m_pIndices[0]) * m_uTotalParticles * 6, m_pIndices, GL_STATIC_DRAW); 707 708 glBindVertexArray(0); 709 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 710 glBindBuffer(GL_ARRAY_BUFFER, 0); 711 712 CHECK_GL_ERROR_DEBUG(); 713 }, 714 715 _setupVBO:function () { 716 if (cc.renderContextType == cc.CANVAS) { 717 return; 718 } 719 720 glGenBuffers(2, this._buffersVBO[0]); 721 722 glBindBuffer(GL_ARRAY_BUFFER, this._buffersVBO[0]); 723 glBufferData(GL_ARRAY_BUFFER, sizeof(m_pQuads[0]) * m_uTotalParticles, m_pQuads, GL_DYNAMIC_DRAW); 724 glBindBuffer(GL_ARRAY_BUFFER, 0); 725 726 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]); 727 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(m_pIndices[0]) * m_uTotalParticles * 6, m_pIndices, GL_STATIC_DRAW); 728 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 729 730 CHECK_GL_ERROR_DEBUG(); 731 }, 732 733 _allocMemory:function () { 734 //cc.Assert(( !this._quads && !this._indices), "Memory already alloced"); 735 cc.Assert(!this._batchNode, "Memory should not be alloced when not using batchNode"); 736 this._quads = []; 737 this._indices = []; 738 for (var i = 0; i < this._totalParticles; i++) { 739 this._quads[i] = new cc.V3F_C4B_T2F_Quad(); 740 this._indices[i * 6] = 0; 741 this._indices[(i * 6) + 1] = 0; 742 this._indices[(i * 6) + 2] = 0; 743 this._indices[(i * 6) + 3] = 0; 744 this._indices[(i * 6) + 4] = 0; 745 this._indices[(i * 6) + 5] = 0; 746 } 747 748 if (!this._quads || !this._indices) { 749 cc.log("cocos2d: Particle system: not enough memory"); 750 return false; 751 } 752 753 return true; 754 } 755 }); 756 757 /** 758 * <p> 759 * creates an initializes a CCParticleSystemQuad from a plist file.<br/> 760 * This plist files can be creted manually or with Particle Designer:<br/> 761 * http://particledesigner.71squared.com/<br/> 762 * </p> 763 * @param {String} pListFile 764 * @return {cc.ParticleSystem} 765 * @example 766 * //creates an initializes a CCParticleSystemQuad from a plist file. 767 * var system = cc.ParticleSystemQuad.create("Images/SpinningPeas.plist"); 768 */ 769 cc.ParticleSystemQuad.create = function (pListFile) { 770 var ret = new cc.ParticleSystemQuad(); 771 if (!pListFile || typeof(pListFile) === "number") { 772 var ton = pListFile || 100; 773 ret.setDrawMode(cc.PARTICLE_TEXTURE_MODE); 774 ret.initWithTotalParticles(ton); 775 return ret; 776 } 777 778 if (ret && ret.initWithFile(pListFile)) { 779 return ret; 780 } 781 return null; 782 }; 783 784 cc.ARCH_OPTIMAL_PARTICLE_SYSTEM = cc.ParticleSystemQuad; 785