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 89 this.fastMode = false; 90 this.startingPositionInitialized = false; 91 92 this.texture = null; 93 94 this._stroke = 0; 95 this._fadeDelta = 0; 96 this._minSeg = 0; 97 98 this._maxPoints = 0; 99 this._nuPoints = 0; 100 this._previousNuPoints = 0; 101 102 /** Pointers */ 103 this._pointVertexes = null; 104 this._pointState = null; 105 106 // webgl 107 this._vertices = null; 108 this._colorPointer = null; 109 this._texCoords = null; 110 111 this._verticesBuffer = null; 112 this._colorPointerBuffer = null; 113 this._texCoordsBuffer = null; 114 115 if(texture !== undefined) 116 this.initWithFade(fade, minSeg, stroke, color, texture); 117 }, 118 119 /** 120 * Gets the texture. 121 * @return {cc.Texture2D} 122 */ 123 getTexture:function () { 124 return this.texture; 125 }, 126 127 /** 128 * Set the texture. 129 * @param {cc.Texture2D} texture 130 */ 131 setTexture:function (texture) { 132 if (this.texture !== texture) 133 this.texture = texture; 134 }, 135 136 /** 137 * Gets the blend func. 138 * @return {cc.BlendFunc} 139 */ 140 getBlendFunc:function () { 141 return this._blendFunc; 142 }, 143 144 /** 145 * Set the blend func. 146 * @param {Number} src 147 * @param {Number} dst 148 */ 149 setBlendFunc:function (src, dst) { 150 if (dst === undefined) { 151 this._blendFunc = src; 152 } else { 153 this._blendFunc.src = src; 154 this._blendFunc.dst = dst; 155 } 156 }, 157 158 /** 159 * Gets opacity. 160 * @warning cc.MotionStreak.getOpacity has not been supported. 161 * @returns {number} 162 */ 163 getOpacity:function () { 164 cc.log("cc.MotionStreak.getOpacity has not been supported."); 165 return 0; 166 }, 167 168 /** 169 * Set opacity. 170 * @warning cc.MotionStreak.setOpacity has not been supported. 171 * @param opacity 172 */ 173 setOpacity:function (opacity) { 174 cc.log("cc.MotionStreak.setOpacity has not been supported."); 175 }, 176 177 /** 178 * set opacity modify RGB. 179 * @warning cc.MotionStreak.setOpacityModifyRGB has not been supported. 180 * @param value 181 */ 182 setOpacityModifyRGB:function (value) { 183 }, 184 185 /** 186 * Checking OpacityModifyRGB. 187 * @returns {boolean} 188 */ 189 isOpacityModifyRGB:function () { 190 return false; 191 }, 192 193 /** 194 * Checking fast mode. 195 * @returns {boolean} 196 */ 197 isFastMode:function () { 198 return this.fastMode; 199 }, 200 201 /** 202 * set fast mode 203 * @param {Boolean} fastMode 204 */ 205 setFastMode:function (fastMode) { 206 this.fastMode = fastMode; 207 }, 208 209 /** 210 * Checking starting position initialized. 211 * @returns {boolean} 212 */ 213 isStartingPositionInitialized:function () { 214 return this.startingPositionInitialized; 215 }, 216 217 /** 218 * Set Starting Position Initialized. 219 * @param {Boolean} startingPositionInitialized 220 */ 221 setStartingPositionInitialized:function (startingPositionInitialized) { 222 this.startingPositionInitialized = startingPositionInitialized; 223 }, 224 225 /** 226 * Get stroke. 227 * @returns {Number} stroke 228 */ 229 getStroke:function () { 230 return this._stroke; 231 }, 232 233 /** 234 * Set stroke. 235 * @param {Number} stroke 236 */ 237 setStroke:function (stroke) { 238 this._stroke = stroke; 239 }, 240 241 /** 242 * initializes a motion streak with fade in seconds, minimum segments, stroke's width, color and texture filename or texture 243 * @param {Number} fade time to fade 244 * @param {Number} minSeg minimum segment size 245 * @param {Number} stroke stroke's width 246 * @param {Number} color 247 * @param {string|cc.Texture2D} texture texture filename or texture 248 * @return {Boolean} 249 */ 250 initWithFade:function (fade, minSeg, stroke, color, texture) { 251 if(!texture) 252 throw new Error("cc.MotionStreak.initWithFade(): Invalid filename or texture"); 253 254 if (cc.isString(texture)) 255 texture = cc.textureCache.addImage(texture); 256 257 cc.Node.prototype.setPosition.call(this, cc.p(0,0)); 258 this.anchorX = 0; 259 this.anchorY = 0; 260 this.ignoreAnchor = true; 261 this.startingPositionInitialized = false; 262 263 this.fastMode = true; 264 this._minSeg = (minSeg === -1.0) ? (stroke / 5.0) : minSeg; 265 this._minSeg *= this._minSeg; 266 267 this._stroke = stroke; 268 this._fadeDelta = 1.0 / fade; 269 270 var locMaxPoints = (0 | (fade * 60)) + 2; 271 this._maxPoints = locMaxPoints; 272 this._nuPoints = 0; 273 this._pointState = new Float32Array(locMaxPoints); 274 this._pointVertexes = new Float32Array(locMaxPoints * 2); 275 276 this._vertices = new Float32Array(locMaxPoints * 4); 277 this._texCoords = new Float32Array(locMaxPoints * 4); 278 this._colorPointer = new Uint8Array(locMaxPoints * 8); 279 280 this._verticesBuffer = gl.createBuffer(); 281 this._texCoordsBuffer = gl.createBuffer(); 282 this._colorPointerBuffer = gl.createBuffer(); 283 284 // Set blend mode 285 this._blendFunc.src = gl.SRC_ALPHA; 286 this._blendFunc.dst = gl.ONE_MINUS_SRC_ALPHA; 287 288 this.texture = texture; 289 this.color = color; 290 this.scheduleUpdate(); 291 292 //bind buffer 293 gl.bindBuffer(gl.ARRAY_BUFFER, this._verticesBuffer); 294 gl.bufferData(gl.ARRAY_BUFFER, this._vertices, gl.DYNAMIC_DRAW); 295 gl.bindBuffer(gl.ARRAY_BUFFER, this._texCoordsBuffer); 296 gl.bufferData(gl.ARRAY_BUFFER, this._texCoords, gl.DYNAMIC_DRAW); 297 gl.bindBuffer(gl.ARRAY_BUFFER, this._colorPointerBuffer); 298 gl.bufferData(gl.ARRAY_BUFFER, this._colorPointer, gl.DYNAMIC_DRAW); 299 300 return true; 301 }, 302 303 /** 304 * color used for the tint 305 * @param {cc.Color} color 306 */ 307 tintWithColor:function (color) { 308 this.color = color; 309 310 // Fast assignation 311 var locColorPointer = this._colorPointer; 312 for (var i = 0, len = this._nuPoints * 2; i < len; i++) { 313 locColorPointer[i * 4] = color.r; 314 locColorPointer[i * 4 + 1] = color.g; 315 locColorPointer[i * 4 + 2] = color.b; 316 } 317 }, 318 319 /** 320 * Remove all living segments of the ribbon 321 */ 322 reset:function () { 323 this._nuPoints = 0; 324 }, 325 326 /** 327 * Set the position. <br /> 328 * 329 * @param {cc.Point|Number} position 330 * @param {Number} [yValue=undefined] If not exists, the first parameter must be cc.Point. 331 */ 332 setPosition:function (position, yValue) { 333 this.startingPositionInitialized = true; 334 if(yValue === undefined){ 335 this._positionR.x = position.x; 336 this._positionR.y = position.y; 337 } else { 338 this._positionR.x = position; 339 this._positionR.y = yValue; 340 } 341 }, 342 343 /** 344 * Gets the position.x 345 * @return {Number} 346 */ 347 getPositionX:function () { 348 return this._positionR.x; 349 }, 350 351 /** 352 * Set the position.x 353 * @param {Number} x 354 */ 355 setPositionX:function (x) { 356 this._positionR.x = x; 357 if(!this.startingPositionInitialized) 358 this.startingPositionInitialized = true; 359 }, 360 361 /** 362 * Gets the position.y 363 * @return {Number} 364 */ 365 getPositionY:function () { 366 return this._positionR.y; 367 }, 368 369 /** 370 * Set the position.y 371 * @param {Number} y 372 */ 373 setPositionY:function (y) { 374 this._positionR.y = y; 375 if(!this.startingPositionInitialized) 376 this.startingPositionInitialized = true; 377 }, 378 379 /** 380 * <p>schedules the "update" method. <br/> 381 * It will use the order number 0. This method will be called every frame. <br/> 382 * Scheduled methods with a lower order value will be called before the ones that have a higher order value.<br/> 383 * Only one "update" method could be scheduled per node.</p> 384 * @param {Number} delta 385 */ 386 update:function (delta) { 387 if (!this.startingPositionInitialized) 388 return; 389 390 //TODO update the color (need move to render cmd) 391 this._renderCmd._updateDisplayColor(); 392 393 delta *= this._fadeDelta; 394 395 var newIdx, newIdx2, i, i2; 396 var mov = 0; 397 398 // Update current points 399 var locNuPoints = this._nuPoints; 400 var locPointState = this._pointState, locPointVertexes = this._pointVertexes, locVertices = this._vertices; 401 var locColorPointer = this._colorPointer; 402 403 for (i = 0; i < locNuPoints; i++) { 404 locPointState[i] -= delta; 405 406 if (locPointState[i] <= 0) 407 mov++; 408 else { 409 newIdx = i - mov; 410 if (mov > 0) { 411 // Move data 412 locPointState[newIdx] = locPointState[i]; 413 // Move point 414 locPointVertexes[newIdx * 2] = locPointVertexes[i * 2]; 415 locPointVertexes[newIdx * 2 + 1] = locPointVertexes[i * 2 + 1]; 416 417 // Move vertices 418 i2 = i * 2; 419 newIdx2 = newIdx * 2; 420 locVertices[newIdx2 * 2] = locVertices[i2 * 2]; 421 locVertices[newIdx2 * 2 + 1] = locVertices[i2 * 2 + 1]; 422 locVertices[(newIdx2 + 1) * 2] = locVertices[(i2 + 1) * 2]; 423 locVertices[(newIdx2 + 1) * 2 + 1] = locVertices[(i2 + 1) * 2 + 1]; 424 425 // Move color 426 i2 *= 4; 427 newIdx2 *= 4; 428 locColorPointer[newIdx2 + 0] = locColorPointer[i2 + 0]; 429 locColorPointer[newIdx2 + 1] = locColorPointer[i2 + 1]; 430 locColorPointer[newIdx2 + 2] = locColorPointer[i2 + 2]; 431 locColorPointer[newIdx2 + 4] = locColorPointer[i2 + 4]; 432 locColorPointer[newIdx2 + 5] = locColorPointer[i2 + 5]; 433 locColorPointer[newIdx2 + 6] = locColorPointer[i2 + 6]; 434 } else 435 newIdx2 = newIdx * 8; 436 437 var op = locPointState[newIdx] * 255.0; 438 locColorPointer[newIdx2 + 3] = op; 439 locColorPointer[newIdx2 + 7] = op; 440 } 441 } 442 locNuPoints -= mov; 443 444 // Append new point 445 var appendNewPoint = true; 446 if (locNuPoints >= this._maxPoints) 447 appendNewPoint = false; 448 else if (locNuPoints > 0) { 449 var a1 = cc.pDistanceSQ(cc.p(locPointVertexes[(locNuPoints - 1) * 2], locPointVertexes[(locNuPoints - 1) * 2 + 1]), 450 this._positionR) < this._minSeg; 451 var a2 = (locNuPoints === 1) ? false : (cc.pDistanceSQ( 452 cc.p(locPointVertexes[(locNuPoints - 2) * 2], locPointVertexes[(locNuPoints - 2) * 2 + 1]), this._positionR) < (this._minSeg * 2.0)); 453 if (a1 || a2) 454 appendNewPoint = false; 455 } 456 457 if (appendNewPoint) { 458 locPointVertexes[locNuPoints * 2] = this._positionR.x; 459 locPointVertexes[locNuPoints * 2 + 1] = this._positionR.y; 460 locPointState[locNuPoints] = 1.0; 461 462 // Color assignment 463 var offset = locNuPoints * 8; 464 465 var locDisplayedColor = this.getDisplayedColor(); 466 locColorPointer[offset] = locDisplayedColor.r; 467 locColorPointer[offset + 1] = locDisplayedColor.g; 468 locColorPointer[offset + 2] = locDisplayedColor.b; 469 //*((ccColor3B*)(m_pColorPointer + offset+4)) = this._color; 470 locColorPointer[offset + 4] = locDisplayedColor.r; 471 locColorPointer[offset + 5] = locDisplayedColor.g; 472 locColorPointer[offset + 6] = locDisplayedColor.b; 473 474 // Opacity 475 locColorPointer[offset + 3] = 255; 476 locColorPointer[offset + 7] = 255; 477 478 // Generate polygon 479 if (locNuPoints > 0 && this.fastMode) { 480 if (locNuPoints > 1) 481 cc.vertexLineToPolygon(locPointVertexes, this._stroke, this._vertices, locNuPoints, 1); 482 else 483 cc.vertexLineToPolygon(locPointVertexes, this._stroke, this._vertices, 0, 2); 484 } 485 locNuPoints++; 486 } 487 488 if (!this.fastMode) 489 cc.vertexLineToPolygon(locPointVertexes, this._stroke, this._vertices, 0, locNuPoints); 490 491 // Updated Tex Coords only if they are different than previous step 492 if (locNuPoints && this._previousNuPoints !== locNuPoints) { 493 var texDelta = 1.0 / locNuPoints; 494 var locTexCoords = this._texCoords; 495 for (i = 0; i < locNuPoints; i++) { 496 locTexCoords[i * 4] = 0; 497 locTexCoords[i * 4 + 1] = texDelta * i; 498 499 locTexCoords[(i * 2 + 1) * 2] = 1; 500 locTexCoords[(i * 2 + 1) * 2 + 1] = texDelta * i; 501 } 502 503 this._previousNuPoints = locNuPoints; 504 } 505 506 this._nuPoints = locNuPoints; 507 }, 508 509 _createRenderCmd: function(){ 510 if(cc._renderType === cc._RENDER_TYPE_WEBGL) 511 return new cc.MotionStreak.WebGLRenderCmd(this); 512 else 513 return null; //MotionStreak doesn't support Canvas mode 514 } 515 }); 516 517 /** 518 * Please use new cc.MotionStreak instead. <br /> 519 * Creates and initializes a motion streak with fade in seconds, minimum segments, stroke's width, color, texture filename or texture 520 * @deprecated since v3.0 please use new cc.MotionStreak instead. 521 * @param {Number} fade time to fade 522 * @param {Number} minSeg minimum segment size 523 * @param {Number} stroke stroke's width 524 * @param {Number} color 525 * @param {string|cc.Texture2D} texture texture filename or texture 526 * @return {cc.MotionStreak} 527 * @example 528 * //example 529 * new cc.MotionStreak(2, 3, 32, cc.color.GREEN, s_streak); 530 */ 531 cc.MotionStreak.create = function (fade, minSeg, stroke, color, texture) { 532 return new cc.MotionStreak(fade, minSeg, stroke, color, texture); 533 }; 534