1 /**************************************************************************** 2 Copyright (c) 2008-2010 Ricardo Quesada 3 Copyright (c) 2011-2012 cocos2d-x.org 4 Copyright (c) 2013-2014 Chukong Technologies Inc. 5 Copyright (c) 2008-2009 Jason Booth 6 7 http://www.cocos2d-x.org 8 9 Permission is hereby granted, free of charge, to any person obtaining a copy 10 of this software and associated documentation files (the "Software"), to deal 11 in the Software without restriction, including without limitation the rights 12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 copies of the Software, and to permit persons to whom the Software is 14 furnished to do so, subject to the following conditions: 15 16 The above copyright notice and this permission notice shall be included in 17 all copies or substantial portions of the Software. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 THE SOFTWARE. 26 ****************************************************************************/ 27 28 /** 29 * cc.MotionStreak manages a Ribbon based on it's motion in absolute space. <br/> 30 * You construct it with a fadeTime, minimum segment size, texture path, texture <br/> 31 * length and color. The fadeTime controls how long it takes each vertex in <br/> 32 * the streak to fade out, the minimum segment size it how many pixels the <br/> 33 * streak will move before adding a new ribbon segment, and the texture <br/> 34 * length is the how many pixels the texture is stretched across. The texture <br/> 35 * is vertically aligned along the streak segment. 36 * @class 37 * @extends cc.Node 38 * 39 * @property {cc.Texture2D} texture - Texture used for the motion streak. 40 * @property {Boolean} fastMode - Indicate whether use fast mode. 41 * @property {Boolean} startingPositionInitialized - Indicate whether starting position initialized. 42 * @example 43 * //example 44 * new cc.MotionStreak(2, 3, 32, cc.color.GREEN, s_streak); 45 */ 46 cc.MotionStreak = cc.Node.extend(/** @lends cc.MotionStreak# */{ 47 texture:null, 48 fastMode:false, 49 startingPositionInitialized:false, 50 51 _blendFunc:null, 52 53 _stroke:0, 54 _fadeDelta:0, 55 _minSeg:0, 56 57 _maxPoints:0, 58 _nuPoints:0, 59 _previousNuPoints:0, 60 61 /* Pointers */ 62 _pointVertexes:null, 63 _pointState:null, 64 65 // webgl 66 _vertices:null, 67 _colorPointer:null, 68 _texCoords:null, 69 70 _verticesBuffer:null, 71 _colorPointerBuffer:null, 72 _texCoordsBuffer:null, 73 _className:"MotionStreak", 74 75 /** 76 * creates and initializes a motion streak with fade in seconds, minimum segments, stroke's width, color, texture filename or texture <br/> 77 * Constructor of cc.MotionStreak 78 * @param {Number} fade time to fade 79 * @param {Number} minSeg minimum segment size 80 * @param {Number} stroke stroke's width 81 * @param {Number} color 82 * @param {string|cc.Texture2D} texture texture filename or texture 83 */ 84 ctor: function (fade, minSeg, stroke, color, texture) { 85 cc.Node.prototype.ctor.call(this); 86 this._positionR = cc.p(0, 0); 87 this._blendFunc = new cc.BlendFunc(cc.SRC_ALPHA, cc.ONE_MINUS_SRC_ALPHA); 88 this._vertexWebGLBuffer = cc._renderContext.createBuffer(); 89 90 this.fastMode = false; 91 this.startingPositionInitialized = false; 92 93 this.texture = null; 94 95 this._stroke = 0; 96 this._fadeDelta = 0; 97 this._minSeg = 0; 98 99 this._maxPoints = 0; 100 this._nuPoints = 0; 101 this._previousNuPoints = 0; 102 103 /** Pointers */ 104 this._pointVertexes = null; 105 this._pointState = null; 106 107 // webgl 108 this._vertices = null; 109 this._colorPointer = null; 110 this._texCoords = null; 111 112 this._verticesBuffer = null; 113 this._colorPointerBuffer = null; 114 this._texCoordsBuffer = null; 115 116 if(texture !== undefined) 117 this.initWithFade(fade, minSeg, stroke, color, texture); 118 }, 119 120 /** 121 * Gets the texture. 122 * @return {cc.Texture2D} 123 */ 124 getTexture:function () { 125 return this.texture; 126 }, 127 128 /** 129 * Set the texture. 130 * @param {cc.Texture2D} texture 131 */ 132 setTexture:function (texture) { 133 if (this.texture != texture) 134 this.texture = texture; 135 }, 136 137 /** 138 * Gets the blend func. 139 * @return {cc.BlendFunc} 140 */ 141 getBlendFunc:function () { 142 return this._blendFunc; 143 }, 144 145 /** 146 * Set the blend func. 147 * @param {Number} src 148 * @param {Number} dst 149 */ 150 setBlendFunc:function (src, dst) { 151 if (dst === undefined) { 152 this._blendFunc = src; 153 } else { 154 this._blendFunc.src = src; 155 this._blendFunc.dst = dst; 156 } 157 }, 158 159 /** 160 * Gets opacity. 161 * @warning cc.MotionStreak.getOpacity has not been supported. 162 * @returns {number} 163 */ 164 getOpacity:function () { 165 cc.log("cc.MotionStreak.getOpacity has not been supported."); 166 return 0; 167 }, 168 169 /** 170 * Set opacity. 171 * @warning cc.MotionStreak.setOpacity has not been supported. 172 * @param opacity 173 */ 174 setOpacity:function (opacity) { 175 cc.log("cc.MotionStreak.setOpacity has not been supported."); 176 }, 177 178 /** 179 * set opacity modify RGB. 180 * @warning cc.MotionStreak.setOpacityModifyRGB has not been supported. 181 * @param value 182 */ 183 setOpacityModifyRGB:function (value) { 184 }, 185 186 /** 187 * Checking OpacityModifyRGB. 188 * @returns {boolean} 189 */ 190 isOpacityModifyRGB:function () { 191 return false; 192 }, 193 194 /** 195 * <p> 196 * callback that is called every time the node leaves the 'stage'. <br/> 197 * If the node leaves the 'stage' with a transition, this callback is called when the transition finishes. <br/> 198 * During onExit you can't access a sibling node. <br/> 199 * If you override onExit, you shall call its parent's onExit with this._super(). 200 * </p> 201 * @function 202 */ 203 onExit:function(){ 204 cc.Node.prototype.onExit.call(this); 205 if(this._verticesBuffer) 206 cc._renderContext.deleteBuffer(this._verticesBuffer); 207 if(this._texCoordsBuffer) 208 cc._renderContext.deleteBuffer(this._texCoordsBuffer); 209 if(this._colorPointerBuffer) 210 cc._renderContext.deleteBuffer(this._colorPointerBuffer); 211 }, 212 213 /** 214 * Checking fast mode. 215 * @returns {boolean} 216 */ 217 isFastMode:function () { 218 return this.fastMode; 219 }, 220 221 /** 222 * set fast mode 223 * @param {Boolean} fastMode 224 */ 225 setFastMode:function (fastMode) { 226 this.fastMode = fastMode; 227 }, 228 229 /** 230 * Checking starting position initialized. 231 * @returns {boolean} 232 */ 233 isStartingPositionInitialized:function () { 234 return this.startingPositionInitialized; 235 }, 236 237 /** 238 * Set Starting Position Initialized. 239 * @param {Boolean} startingPositionInitialized 240 */ 241 setStartingPositionInitialized:function (startingPositionInitialized) { 242 this.startingPositionInitialized = startingPositionInitialized; 243 }, 244 245 /** 246 * initializes a motion streak with fade in seconds, minimum segments, stroke's width, color and texture filename or texture 247 * @param {Number} fade time to fade 248 * @param {Number} minSeg minimum segment size 249 * @param {Number} stroke stroke's width 250 * @param {Number} color 251 * @param {string|cc.Texture2D} texture texture filename or texture 252 * @return {Boolean} 253 */ 254 initWithFade:function (fade, minSeg, stroke, color, texture) { 255 if(!texture) 256 throw "cc.MotionStreak.initWithFade(): Invalid filename or texture"; 257 258 if (cc.isString(texture)) 259 texture = cc.textureCache.addImage(texture); 260 261 cc.Node.prototype.setPosition.call(this, cc.p(0,0)); 262 this.anchorX = 0; 263 this.anchorY = 0; 264 this.ignoreAnchor = true; 265 this.startingPositionInitialized = false; 266 267 this.fastMode = true; 268 this._minSeg = (minSeg == -1.0) ? (stroke / 5.0) : minSeg; 269 this._minSeg *= this._minSeg; 270 271 this._stroke = stroke; 272 this._fadeDelta = 1.0 / fade; 273 274 var locMaxPoints = (0 | (fade * 60)) + 2; 275 this._nuPoints = 0; 276 this._pointState = new Float32Array(locMaxPoints); 277 this._pointVertexes = new Float32Array(locMaxPoints * 2); 278 279 this._vertices = new Float32Array(locMaxPoints * 4); 280 this._texCoords = new Float32Array(locMaxPoints * 4); 281 this._colorPointer = new Uint8Array(locMaxPoints * 8); 282 this._maxPoints = locMaxPoints; 283 284 var gl = cc._renderContext; 285 286 this._verticesBuffer = gl.createBuffer(); 287 this._texCoordsBuffer = gl.createBuffer(); 288 this._colorPointerBuffer = gl.createBuffer(); 289 290 // Set blend mode 291 this._blendFunc.src = gl.SRC_ALPHA; 292 this._blendFunc.dst = gl.ONE_MINUS_SRC_ALPHA; 293 294 // shader program 295 this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR); 296 297 this.texture = texture; 298 this.color = color; 299 this.scheduleUpdate(); 300 301 //bind buffer 302 gl.bindBuffer(gl.ARRAY_BUFFER, this._verticesBuffer); 303 gl.bufferData(gl.ARRAY_BUFFER, this._vertices, gl.DYNAMIC_DRAW); 304 gl.bindBuffer(gl.ARRAY_BUFFER, this._texCoordsBuffer); 305 gl.bufferData(gl.ARRAY_BUFFER, this._texCoords, gl.DYNAMIC_DRAW); 306 gl.bindBuffer(gl.ARRAY_BUFFER, this._colorPointerBuffer); 307 gl.bufferData(gl.ARRAY_BUFFER, this._colorPointer, gl.DYNAMIC_DRAW); 308 309 return true; 310 }, 311 312 /** 313 * color used for the tint 314 * @param {cc.Color} color 315 */ 316 tintWithColor:function (color) { 317 this.color = color; 318 319 // Fast assignation 320 var locColorPointer = this._colorPointer; 321 for (var i = 0, len = this._nuPoints * 2; i < len; i++) { 322 locColorPointer[i * 4] = color.r; 323 locColorPointer[i * 4 + 1] = color.g; 324 locColorPointer[i * 4 + 2] = color.b; 325 } 326 }, 327 328 /** 329 * Remove all living segments of the ribbon 330 */ 331 reset:function () { 332 this._nuPoints = 0; 333 }, 334 335 /** 336 * Set the position. <br /> 337 * 338 * @param {cc.Point|Number} position 339 * @param {Number} [yValue=undefined] If not exists, the first parameter must be cc.Point. 340 */ 341 setPosition:function (position, yValue) { 342 this.startingPositionInitialized = true; 343 if(yValue === undefined){ 344 this._positionR.x = position.x; 345 this._positionR.y = position.y; 346 } else { 347 this._positionR.x = position; 348 this._positionR.y = yValue; 349 } 350 }, 351 352 /** 353 * Gets the position.x 354 * @return {Number} 355 */ 356 getPositionX:function () { 357 return this._positionR.x; 358 }, 359 360 /** 361 * Set the position.x 362 * @param {Number} x 363 */ 364 setPositionX:function (x) { 365 this._positionR.x = x; 366 if(!this.startingPositionInitialized) 367 this.startingPositionInitialized = true; 368 }, 369 370 /** 371 * Gets the position.y 372 * @return {Number} 373 */ 374 getPositionY:function () { 375 return this._positionR.y; 376 }, 377 378 /** 379 * Set the position.y 380 * @param {Number} y 381 */ 382 setPositionY:function (y) { 383 this._positionR.y = y; 384 if(!this.startingPositionInitialized) 385 this.startingPositionInitialized = true; 386 }, 387 388 /** 389 * Render function using the canvas 2d context or WebGL context, internal usage only, please do not call this function 390 * @function 391 * @param {CanvasRenderingContext2D | WebGLRenderingContext} ctx The render context 392 */ 393 draw:function (ctx) { 394 if (this._nuPoints <= 1) 395 return; 396 397 if(this.texture && this.texture.isLoaded()){ 398 ctx = ctx || cc._renderContext; 399 cc.nodeDrawSetup(this); 400 cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); 401 cc.glBlendFunc(this._blendFunc.src, this._blendFunc.dst); 402 403 cc.glBindTexture2D(this.texture); 404 405 //position 406 ctx.bindBuffer(ctx.ARRAY_BUFFER, this._verticesBuffer); 407 ctx.bufferData(ctx.ARRAY_BUFFER, this._vertices, ctx.DYNAMIC_DRAW); 408 ctx.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, ctx.FLOAT, false, 0, 0); 409 410 //texcoords 411 ctx.bindBuffer(ctx.ARRAY_BUFFER, this._texCoordsBuffer); 412 ctx.bufferData(ctx.ARRAY_BUFFER, this._texCoords, ctx.DYNAMIC_DRAW); 413 ctx.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, ctx.FLOAT, false, 0, 0); 414 415 //colors 416 ctx.bindBuffer(ctx.ARRAY_BUFFER, this._colorPointerBuffer); 417 ctx.bufferData(ctx.ARRAY_BUFFER, this._colorPointer, ctx.DYNAMIC_DRAW); 418 ctx.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, ctx.UNSIGNED_BYTE, true, 0, 0); 419 420 ctx.drawArrays(ctx.TRIANGLE_STRIP, 0, this._nuPoints * 2); 421 cc.g_NumberOfDraws ++; 422 } 423 }, 424 425 /** 426 * <p>schedules the "update" method. <br/> 427 * It will use the order number 0. This method will be called every frame. <br/> 428 * Scheduled methods with a lower order value will be called before the ones that have a higher order value.<br/> 429 * Only one "update" method could be scheduled per node.</p> 430 * @param {Number} delta 431 */ 432 update:function (delta) { 433 if (!this.startingPositionInitialized) 434 return; 435 436 delta *= this._fadeDelta; 437 438 var newIdx, newIdx2, i, i2; 439 var mov = 0; 440 441 // Update current points 442 var locNuPoints = this._nuPoints; 443 var locPointState = this._pointState, locPointVertexes = this._pointVertexes, locVertices = this._vertices; 444 var locColorPointer = this._colorPointer; 445 446 for (i = 0; i < locNuPoints; i++) { 447 locPointState[i] -= delta; 448 449 if (locPointState[i] <= 0) 450 mov++; 451 else { 452 newIdx = i - mov; 453 if (mov > 0) { 454 // Move data 455 locPointState[newIdx] = locPointState[i]; 456 // Move point 457 locPointVertexes[newIdx * 2] = locPointVertexes[i * 2]; 458 locPointVertexes[newIdx * 2 + 1] = locPointVertexes[i * 2 + 1]; 459 460 // Move vertices 461 i2 = i * 2; 462 newIdx2 = newIdx * 2; 463 locVertices[newIdx2 * 2] = locVertices[i2 * 2]; 464 locVertices[newIdx2 * 2 + 1] = locVertices[i2 * 2 + 1]; 465 locVertices[(newIdx2 + 1) * 2] = locVertices[(i2 + 1) * 2]; 466 locVertices[(newIdx2 + 1) * 2 + 1] = locVertices[(i2 + 1) * 2 + 1]; 467 468 // Move color 469 i2 *= 4; 470 newIdx2 *= 4; 471 locColorPointer[newIdx2 + 0] = locColorPointer[i2 + 0]; 472 locColorPointer[newIdx2 + 1] = locColorPointer[i2 + 1]; 473 locColorPointer[newIdx2 + 2] = locColorPointer[i2 + 2]; 474 locColorPointer[newIdx2 + 4] = locColorPointer[i2 + 4]; 475 locColorPointer[newIdx2 + 5] = locColorPointer[i2 + 5]; 476 locColorPointer[newIdx2 + 6] = locColorPointer[i2 + 6]; 477 } else 478 newIdx2 = newIdx * 8; 479 480 var op = locPointState[newIdx] * 255.0; 481 locColorPointer[newIdx2 + 3] = op; 482 locColorPointer[newIdx2 + 7] = op; 483 } 484 } 485 locNuPoints -= mov; 486 487 // Append new point 488 var appendNewPoint = true; 489 if (locNuPoints >= this._maxPoints) 490 appendNewPoint = false; 491 else if (locNuPoints > 0) { 492 var a1 = cc.pDistanceSQ(cc.p(locPointVertexes[(locNuPoints - 1) * 2], locPointVertexes[(locNuPoints - 1) * 2 + 1]), 493 this._positionR) < this._minSeg; 494 var a2 = (locNuPoints == 1) ? false : (cc.pDistanceSQ( 495 cc.p(locPointVertexes[(locNuPoints - 2) * 2], locPointVertexes[(locNuPoints - 2) * 2 + 1]), this._positionR) < (this._minSeg * 2.0)); 496 if (a1 || a2) 497 appendNewPoint = false; 498 } 499 500 if (appendNewPoint) { 501 locPointVertexes[locNuPoints * 2] = this._positionR.x; 502 locPointVertexes[locNuPoints * 2 + 1] = this._positionR.y; 503 locPointState[locNuPoints] = 1.0; 504 505 // Color assignment 506 var offset = locNuPoints * 8; 507 508 var locDisplayedColor = this._displayedColor; 509 locColorPointer[offset] = locDisplayedColor.r; 510 locColorPointer[offset + 1] = locDisplayedColor.g; 511 locColorPointer[offset + 2] = locDisplayedColor.b; 512 //*((ccColor3B*)(m_pColorPointer + offset+4)) = this._color; 513 locColorPointer[offset + 4] = locDisplayedColor.r; 514 locColorPointer[offset + 5] = locDisplayedColor.g; 515 locColorPointer[offset + 6] = locDisplayedColor.b; 516 517 // Opacity 518 locColorPointer[offset + 3] = 255; 519 locColorPointer[offset + 7] = 255; 520 521 // Generate polygon 522 if (locNuPoints > 0 && this.fastMode) { 523 if (locNuPoints > 1) 524 cc.vertexLineToPolygon(locPointVertexes, this._stroke, this._vertices, locNuPoints, 1); 525 else 526 cc.vertexLineToPolygon(locPointVertexes, this._stroke, this._vertices, 0, 2); 527 } 528 locNuPoints++; 529 } 530 531 if (!this.fastMode) 532 cc.vertexLineToPolygon(locPointVertexes, this._stroke, this._vertices, 0, locNuPoints); 533 534 // Updated Tex Coords only if they are different than previous step 535 if (locNuPoints && this._previousNuPoints != locNuPoints) { 536 var texDelta = 1.0 / locNuPoints; 537 var locTexCoords = this._texCoords; 538 for (i = 0; i < locNuPoints; i++) { 539 locTexCoords[i * 4] = 0; 540 locTexCoords[i * 4 + 1] = texDelta * i; 541 542 locTexCoords[(i * 2 + 1) * 2] = 1; 543 locTexCoords[(i * 2 + 1) * 2 + 1] = texDelta * i; 544 } 545 546 this._previousNuPoints = locNuPoints; 547 } 548 549 this._nuPoints = locNuPoints; 550 } 551 }); 552 553 /** 554 * Please use new cc.MotionStreak instead. <br /> 555 * Creates and initializes a motion streak with fade in seconds, minimum segments, stroke's width, color, texture filename or texture 556 * @deprecated since v3.0 please use new cc.MotionStreak instead. 557 * @param {Number} fade time to fade 558 * @param {Number} minSeg minimum segment size 559 * @param {Number} stroke stroke's width 560 * @param {Number} color 561 * @param {string|cc.Texture2D} texture texture filename or texture 562 * @return {cc.MotionStreak} 563 * @example 564 * //example 565 * new cc.MotionStreak(2, 3, 32, cc.color.GREEN, s_streak); 566 */ 567 cc.MotionStreak.create = function (fade, minSeg, stroke, color, texture) { 568 return new cc.MotionStreak(fade, minSeg, stroke, color, texture); 569 }; 570