123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590 |
- /**
- * Copyright (c) 2008-2010 Ricardo Quesada
- * Copyright (c) 2011-2012 cocos2d-x.org
- * Copyright (c) 2013-2014 Chukong Technologies Inc.
- * Copyright (C) 2009 Matt Oswald
- * Copyright (c) 2011 Marco Tillemans
- *
- * 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.
- *
- */
- /**
- * paticle default capacity
- * @constant
- * @type Number
- */
- cc.PARTICLE_DEFAULT_CAPACITY = 500;
- /**
- * <p>
- * cc.ParticleBatchNode is like a batch node: if it contains children, it will draw them in 1 single OpenGL call <br/>
- * (often known as "batch draw"). </br>
- *
- * A cc.ParticleBatchNode can reference one and only one texture (one image file, one texture atlas).<br/>
- * Only the cc.ParticleSystems that are contained in that texture can be added to the cc.SpriteBatchNode.<br/>
- * All cc.ParticleSystems added to a cc.SpriteBatchNode are drawn in one OpenGL ES draw call.<br/>
- * If the cc.ParticleSystems are not added to a cc.ParticleBatchNode then an OpenGL ES draw call will be needed for each one, which is less efficient.</br>
- *
- * Limitations:<br/>
- * - At the moment only cc.ParticleSystem is supported<br/>
- * - All systems need to be drawn with the same parameters, blend function, aliasing, texture<br/>
- *
- * Most efficient usage<br/>
- * - Initialize the ParticleBatchNode with the texture and enough capacity for all the particle systems<br/>
- * - Initialize all particle systems and add them as child to the batch node<br/>
- * </p>
- * @class
- * @extends cc.ParticleSystem
- * @param {String|cc.Texture2D} fileImage
- * @param {Number} capacity
- *
- * @property {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture - The used texture
- * @property {cc.TextureAtlas} textureAtlas - The texture atlas used for drawing the quads
- *
- * @example
- * 1.
- * //Create a cc.ParticleBatchNode with image path and capacity
- * var particleBatchNode = new cc.ParticleBatchNode("res/grossini_dance.png",30);
- *
- * 2.
- * //Create a cc.ParticleBatchNode with a texture and capacity
- * var texture = cc.TextureCache.getInstance().addImage("res/grossini_dance.png");
- * var particleBatchNode = new cc.ParticleBatchNode(texture, 30);
- */
- cc.ParticleBatchNode = cc.Node.extend(/** @lends cc.ParticleBatchNode# */{
- textureAtlas:null,
- TextureProtocol:true,
- //the blend function used for drawing the quads
- _blendFunc:null,
- _className:"ParticleBatchNode",
- /**
- * initializes the particle system with the name of a file on disk (for a list of supported formats look at the cc.Texture2D class), a capacity of particles
- * Constructor of cc.ParticleBatchNode
- * @param {String|cc.Texture2D} fileImage
- * @param {Number} capacity
- * @example
- * 1.
- * //Create a cc.ParticleBatchNode with image path and capacity
- * var particleBatchNode = new cc.ParticleBatchNode("res/grossini_dance.png",30);
- *
- * 2.
- * //Create a cc.ParticleBatchNode with a texture and capacity
- * var texture = cc.TextureCache.getInstance().addImage("res/grossini_dance.png");
- * var particleBatchNode = new cc.ParticleBatchNode(texture, 30);
- */
- ctor:function (fileImage, capacity) {
- cc.Node.prototype.ctor.call(this);
- this._blendFunc = {src:cc.BLEND_SRC, dst:cc.BLEND_DST};
- if (typeof(fileImage) == "string") {
- this.init(fileImage, capacity);
- } else if (fileImage instanceof cc.Texture2D) {
- this.initWithTexture(fileImage, capacity);
- }
- },
- /**
- * initializes the particle system with cc.Texture2D, a capacity of particles
- * @param {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture
- * @param {Number} capacity
- * @return {Boolean}
- */
- initWithTexture:function (texture, capacity) {
- this.textureAtlas = new cc.TextureAtlas();
- this.textureAtlas.initWithTexture(texture, capacity);
- // no lazy alloc in this node
- this._children.length = 0;
- if (cc._renderType === cc._RENDER_TYPE_WEBGL)
- this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR);
- return true;
- },
- /**
- * initializes the particle system with the name of a file on disk (for a list of supported formats look at the cc.Texture2D class), a capacity of particles
- * @param {String} fileImage
- * @param {Number} capacity
- * @return {Boolean}
- */
- initWithFile:function (fileImage, capacity) {
- var tex = cc.textureCache.addImage(fileImage);
- return this.initWithTexture(tex, capacity);
- },
- /**
- * initializes the particle system with the name of a file on disk (for a list of supported formats look at the cc.Texture2D class), a capacity of particles
- * @param {String} fileImage
- * @param {Number} capacity
- * @return {Boolean}
- */
- init:function (fileImage, capacity) {
- var tex = cc.TextureCache.getInstance().addImage(fileImage);
- return this.initWithTexture(tex, capacity);
- },
- /**
- * Add a child into the cc.ParticleBatchNode
- * @param {cc.ParticleSystem} child
- * @param {Number} zOrder
- * @param {Number} tag
- */
- addChild:function (child, zOrder, tag) {
- if(!child)
- throw "cc.ParticleBatchNode.addChild() : child should be non-null";
- if(!(child instanceof cc.ParticleSystem))
- throw "cc.ParticleBatchNode.addChild() : only supports cc.ParticleSystem as children";
- zOrder = (zOrder == null) ? child.zIndex : zOrder;
- tag = (tag == null) ? child.tag : tag;
- if(child.getTexture() != this.textureAtlas.texture)
- throw "cc.ParticleSystem.addChild() : the child is not using the same texture id";
- // If this is the 1st children, then copy blending function
- var childBlendFunc = child.getBlendFunc();
- if (this._children.length === 0)
- this.setBlendFunc(childBlendFunc);
- else{
- if((childBlendFunc.src != this._blendFunc.src) || (childBlendFunc.dst != this._blendFunc.dst)){
- cc.log("cc.ParticleSystem.addChild() : Can't add a ParticleSystem that uses a different blending function");
- return;
- }
- }
- //no lazy sorting, so don't call super addChild, call helper instead
- var pos = this._addChildHelper(child, zOrder, tag);
- //get new atlasIndex
- var atlasIndex = 0;
- if (pos != 0) {
- var p = this._children[pos - 1];
- atlasIndex = p.getAtlasIndex() + p.getTotalParticles();
- } else
- atlasIndex = 0;
- this.insertChild(child, atlasIndex);
- // update quad info
- child.setBatchNode(this);
- },
- /**
- * Inserts a child into the cc.ParticleBatchNode
- * @param {cc.ParticleSystem} pSystem
- * @param {Number} index
- */
- insertChild:function (pSystem, index) {
- var totalParticles = pSystem.getTotalParticles();
- var locTextureAtlas = this.textureAtlas;
- var totalQuads = locTextureAtlas.totalQuads;
- pSystem.setAtlasIndex(index);
- if (totalQuads + totalParticles > locTextureAtlas.getCapacity()) {
- this._increaseAtlasCapacityTo(totalQuads + totalParticles);
- // after a realloc empty quads of textureAtlas can be filled with gibberish (realloc doesn't perform calloc), insert empty quads to prevent it
- locTextureAtlas.fillWithEmptyQuadsFromIndex(locTextureAtlas.getCapacity() - totalParticles, totalParticles);
- }
- // make room for quads, not necessary for last child
- if (pSystem.getAtlasIndex() + totalParticles != totalQuads)
- locTextureAtlas.moveQuadsFromIndex(index, index + totalParticles);
- // increase totalParticles here for new particles, update method of particlesystem will fill the quads
- locTextureAtlas.increaseTotalQuadsWith(totalParticles);
- this._updateAllAtlasIndexes();
- },
- /**
- * @param {cc.ParticleSystem} child
- * @param {Boolean} cleanup
- */
- removeChild:function (child, cleanup) {
- // explicit nil handling
- if (child == null)
- return;
- if(!(child instanceof cc.ParticleSystem))
- throw "cc.ParticleBatchNode.removeChild(): only supports cc.ParticleSystem as children";
- if(this._children.indexOf(child) == -1){
- cc.log("cc.ParticleBatchNode.removeChild(): doesn't contain the sprite. Can't remove it");
- return;
- }
- cc.Node.prototype.removeChild.call(this, child, cleanup);
- var locTextureAtlas = this.textureAtlas;
- // remove child helper
- locTextureAtlas.removeQuadsAtIndex(child.getAtlasIndex(), child.getTotalParticles());
- // after memmove of data, empty the quads at the end of array
- locTextureAtlas.fillWithEmptyQuadsFromIndex(locTextureAtlas.totalQuads, child.getTotalParticles());
- // paticle could be reused for self rendering
- child.setBatchNode(null);
- this._updateAllAtlasIndexes();
- },
- /**
- * Reorder will be done in this function, no "lazy" reorder to particles
- * @param {cc.ParticleSystem} child
- * @param {Number} zOrder
- */
- reorderChild:function (child, zOrder) {
- if(!child)
- throw "cc.ParticleBatchNode.reorderChild(): child should be non-null";
- if(!(child instanceof cc.ParticleSystem))
- throw "cc.ParticleBatchNode.reorderChild(): only supports cc.QuadParticleSystems as children";
- if(this._children.indexOf(child) === -1){
- cc.log("cc.ParticleBatchNode.reorderChild(): Child doesn't belong to batch");
- return;
- }
- if (zOrder == child.zIndex)
- return;
- // no reordering if only 1 child
- if (this._children.length > 1) {
- var getIndexes = this._getCurrentIndex(child, zOrder);
- if (getIndexes.oldIndex != getIndexes.newIndex) {
- // reorder m_pChildren.array
- this._children.splice(getIndexes.oldIndex, 1)
- this._children.splice(getIndexes.newIndex, 0, child);
- // save old altasIndex
- var oldAtlasIndex = child.getAtlasIndex();
- // update atlas index
- this._updateAllAtlasIndexes();
- // Find new AtlasIndex
- var newAtlasIndex = 0;
- var locChildren = this._children;
- for (var i = 0; i < locChildren.length; i++) {
- var pNode = locChildren[i];
- if (pNode == child) {
- newAtlasIndex = child.getAtlasIndex();
- break;
- }
- }
- // reorder textureAtlas quads
- this.textureAtlas.moveQuadsFromIndex(oldAtlasIndex, child.getTotalParticles(), newAtlasIndex);
- child.updateWithNoTime();
- }
- }
- child._setLocalZOrder(zOrder);
- },
- /**
- * @param {Number} index
- * @param {Boolean} doCleanup
- */
- removeChildAtIndex:function (index, doCleanup) {
- this.removeChild(this._children[i], doCleanup);
- },
- /**
- * @param {Boolean} doCleanup
- */
- removeAllChildren:function (doCleanup) {
- var locChildren = this._children;
- for (var i = 0; i < locChildren.length; i++) {
- locChildren[i].setBatchNode(null);
- }
- cc.Node.prototype.removeAllChildren.call(this, doCleanup);
- this.textureAtlas.removeAllQuads();
- },
- /**
- * disables a particle by inserting a 0'd quad into the texture atlas
- * @param {Number} particleIndex
- */
- disableParticle:function (particleIndex) {
- var quad = this.textureAtlas.quads[particleIndex];
- quad.br.vertices.x = quad.br.vertices.y = quad.tr.vertices.x = quad.tr.vertices.y =
- quad.tl.vertices.x = quad.tl.vertices.y = quad.bl.vertices.x = quad.bl.vertices.y = 0.0;
- this.textureAtlas._setDirty(true);
- },
- /**
- * Render function using the canvas 2d context or WebGL context, internal usage only, please do not call this function
- * @function
- * @param {CanvasRenderingContext2D | WebGLRenderingContext} ctx The render context
- */
- draw:function (ctx) {
- //cc.PROFILER_STOP("CCParticleBatchNode - draw");
- if (cc._renderType === cc._RENDER_TYPE_CANVAS)
- return;
- if (this.textureAtlas.totalQuads == 0)
- return;
- cc.nodeDrawSetup(this);
- cc.glBlendFuncForParticle(this._blendFunc.src, this._blendFunc.dst);
- this.textureAtlas.drawQuads();
- //cc.PROFILER_STOP("CCParticleBatchNode - draw");
- },
- /**
- * returns the used texture
- * @return {cc.Texture2D|HTMLImageElement|HTMLCanvasElement}
- */
- getTexture:function () {
- return this.textureAtlas.texture;
- },
- /**
- * sets a new texture. it will be retained
- * @param {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture
- */
- setTexture:function (texture) {
- this.textureAtlas.texture = texture;
- // If the new texture has No premultiplied alpha, AND the blendFunc hasn't been changed, then update it
- var locBlendFunc = this._blendFunc;
- if (texture && !texture.hasPremultipliedAlpha() && ( locBlendFunc.src == cc.BLEND_SRC && locBlendFunc.dst == cc.BLEND_DST )) {
- locBlendFunc.src = cc.SRC_ALPHA;
- locBlendFunc.dst = cc.ONE_MINUS_SRC_ALPHA;
- }
- },
- /**
- * set the blending function used for the texture
- * @param {Number|Object} src
- * @param {Number} dst
- */
- setBlendFunc:function (src, dst) {
- if (dst === undefined){
- this._blendFunc.src = src.src;
- this._blendFunc.dst = src.dst;
- } else{
- this._blendFunc.src = src;
- this._blendFunc.src = dst;
- }
- },
- /**
- * returns the blending function used for the texture
- * @return {cc.BlendFunc}
- */
- getBlendFunc:function () {
- return {src:this._blendFunc.src, dst:this._blendFunc.dst};
- },
- // override visit.
- // Don't call visit on it's children
- /**
- * Recursive method that visit its children and draw them
- * @function
- * @param {CanvasRenderingContext2D|WebGLRenderingContext} ctx
- */
- visit:function (ctx) {
- if (cc._renderType === cc._RENDER_TYPE_CANVAS)
- return;
- // CAREFUL:
- // This visit is almost identical to cc.Node#visit
- // with the exception that it doesn't call visit on it's children
- //
- // The alternative is to have a void cc.Sprite#visit, but
- // although this is less mantainable, is faster
- //
- if (!this._visible)
- return;
- cc.kmGLPushMatrix();
- if (this.grid && this.grid.isActive()) {
- this.grid.beforeDraw();
- this.transformAncestors();
- }
- this.transform(ctx);
- this.draw(ctx);
- if (this.grid && this.grid.isActive())
- this.grid.afterDraw(this);
- cc.kmGLPopMatrix();
- },
- _updateAllAtlasIndexes:function () {
- var index = 0;
- var locChildren = this._children;
- for (var i = 0; i < locChildren.length; i++) {
- var child = locChildren[i];
- child.setAtlasIndex(index);
- index += child.getTotalParticles();
- }
- },
- _increaseAtlasCapacityTo:function (quantity) {
- cc.log("cocos2d: cc.ParticleBatchNode: resizing TextureAtlas capacity from [" + this.textureAtlas.getCapacity()
- + "] to [" + quantity + "].");
- if (!this.textureAtlas.resizeCapacity(quantity)) {
- // serious problems
- cc.log("cc.ParticleBatchNode._increaseAtlasCapacityTo() : WARNING: Not enough memory to resize the atlas");
- }
- },
- _searchNewPositionInChildrenForZ:function (z) {
- var locChildren = this._children;
- var count = locChildren.length;
- for (var i = 0; i < count; i++) {
- if (locChildren[i].zIndex > z)
- return i;
- }
- return count;
- },
- _getCurrentIndex:function (child, z) {
- var foundCurrentIdx = false;
- var foundNewIdx = false;
- var newIndex = 0;
- var oldIndex = 0;
- var minusOne = 0, locChildren = this._children;
- var count = locChildren.length;
- for (var i = 0; i < count; i++) {
- var pNode = locChildren[i];
- // new index
- if (pNode.zIndex > z && !foundNewIdx) {
- newIndex = i;
- foundNewIdx = true;
- if (foundCurrentIdx && foundNewIdx)
- break;
- }
- // current index
- if (child == pNode) {
- oldIndex = i;
- foundCurrentIdx = true;
- if (!foundNewIdx)
- minusOne = -1;
- if (foundCurrentIdx && foundNewIdx)
- break;
- }
- }
- if (!foundNewIdx)
- newIndex = count;
- newIndex += minusOne;
- return {newIndex:newIndex, oldIndex:oldIndex};
- },
- //
- // <p>
- // don't use lazy sorting, reordering the particle systems quads afterwards would be too complex <br/>
- // XXX research whether lazy sorting + freeing current quads and calloc a new block with size of capacity would be faster <br/>
- // XXX or possibly using vertexZ for reordering, that would be fastest <br/>
- // this helper is almost equivalent to CCNode's addChild, but doesn't make use of the lazy sorting <br/>
- // </p>
- // @param {cc.ParticleSystem} child
- // @param {Number} z
- // @param {Number} aTag
- // @return {Number}
- // @private
- //
- _addChildHelper:function (child, z, aTag) {
- if(!child)
- throw "cc.ParticleBatchNode._addChildHelper(): child should be non-null";
- if(child.parent){
- cc.log("cc.ParticleBatchNode._addChildHelper(): child already added. It can't be added again");
- return null;
- }
- if (!this._children)
- this._children = [];
- //don't use a lazy insert
- var pos = this._searchNewPositionInChildrenForZ(z);
- this._children.splice(pos, 0, child);
- child.tag = aTag;
- child._setLocalZOrder(z);
- child.parent = this;
- if (this._running) {
- child.onEnter();
- child.onEnterTransitionDidFinish();
- }
- return pos;
- },
- _updateBlendFunc:function () {
- if (!this.textureAtlas.texture.hasPremultipliedAlpha()) {
- this._blendFunc.src = cc.SRC_ALPHA;
- this._blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA;
- }
- },
- /**
- * return the texture atlas used for drawing the quads
- * @return {cc.TextureAtlas}
- */
- getTextureAtlas:function () {
- return this.textureAtlas;
- },
- /**
- * set the texture atlas used for drawing the quads
- * @param {cc.TextureAtlas} textureAtlas
- */
- setTextureAtlas:function (textureAtlas) {
- this.textureAtlas = textureAtlas;
- }
- });
- var _p = cc.ParticleBatchNode.prototype;
- // Extended properties
- /** @expose */
- _p.texture;
- cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture);
- /**
- * initializes the particle system with the name of a file on disk (for a list of supported formats look at the cc.Texture2D class), a capacity of particles
- * @deprecated since v3.0 please use new cc.ParticleBatchNode(filename, capacity) instead.
- * @param {String|cc.Texture2D} fileImage
- * @param {Number} capacity
- * @return {cc.ParticleBatchNode}
- * @example
- * 1.
- * //Create a cc.ParticleBatchNode with image path and capacity
- * var particleBatchNode = cc.ParticleBatchNode.create("res/grossini_dance.png",30);
- *
- * 2.
- * //Create a cc.ParticleBatchNode with a texture and capacity
- * var texture = cc.TextureCache.getInstance().addImage("res/grossini_dance.png");
- * var particleBatchNode = cc.ParticleBatchNode.create(texture, 30);
- */
- cc.ParticleBatchNode.create = function (fileImage, capacity) {
- return new cc.ParticleBatchNode(fileImage, capacity);
- };
|