1 /**************************************************************************** 2 Copyright (c) 2010-2012 cocos2d-x.org 3 Copyright (c) 2008-2010 Ricardo Quesada 4 Copyright (c) 2011 Zynga 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.NodeRGBA 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 */ 43 cc.MotionStreak = cc.NodeRGBA.extend(/** @lends cc.MotionStreak# */{ 44 texture:null, 45 fastMode:false, 46 startingPositionInitialized:false, 47 48 _blendFunc:null, 49 50 _stroke:0, 51 _fadeDelta:0, 52 _minSeg:0, 53 54 _maxPoints:0, 55 _nuPoints:0, 56 _previousNuPoints:0, 57 58 /** Pointers */ 59 _pointVertexes:null, 60 _pointState:null, 61 62 // webgl 63 _vertices:null, 64 _colorPointer:null, 65 _texCoords:null, 66 67 _verticesBuffer:null, 68 _colorPointerBuffer:null, 69 _texCoordsBuffer:null, 70 _className:"MotionStreak", 71 72 /** 73 * Constructor 74 */ 75 ctor: function () { 76 cc.NodeRGBA.prototype.ctor.call(this); 77 this._positionR = cc.p(0, 0); 78 this._blendFunc = new cc.BlendFunc(cc.SRC_ALPHA, cc.ONE_MINUS_SRC_ALPHA); 79 this._vertexWebGLBuffer = cc._renderContext.createBuffer(); 80 81 this.fastMode = false; 82 this.startingPositionInitialized = false; 83 84 this.texture = null; 85 86 this._stroke = 0; 87 this._fadeDelta = 0; 88 this._minSeg = 0; 89 90 this._maxPoints = 0; 91 this._nuPoints = 0; 92 this._previousNuPoints = 0; 93 94 /** Pointers */ 95 this._pointVertexes = null; 96 this._pointState = null; 97 98 // webgl 99 this._vertices = null; 100 this._colorPointer = null; 101 this._texCoords = null; 102 103 this._verticesBuffer = null; 104 this._colorPointerBuffer = null; 105 this._texCoordsBuffer = null; 106 }, 107 108 /** 109 * @return {cc.Texture2D} 110 */ 111 getTexture:function () { 112 return this.texture; 113 }, 114 115 /** 116 * @param {cc.Texture2D} texture 117 */ 118 setTexture:function (texture) { 119 if (this.texture != texture) 120 this.texture = texture; 121 }, 122 123 /** 124 * @return {cc.BlendFunc} 125 */ 126 getBlendFunc:function () { 127 return this._blendFunc; 128 }, 129 130 /** 131 * @param {Number} src 132 * @param {Number} dst 133 */ 134 setBlendFunc:function (src, dst) { 135 if (dst === undefined) { 136 this._blendFunc = src; 137 } else { 138 this._blendFunc.src = src; 139 this._blendFunc.dst = dst; 140 } 141 }, 142 143 getOpacity:function () { 144 cc.log("cc.MotionStreak.getOpacity has not been supported."); 145 return 0; 146 }, 147 148 setOpacity:function (opacity) { 149 cc.log("cc.MotionStreak.setOpacity has not been supported."); 150 }, 151 152 setOpacityModifyRGB:function (value) { 153 }, 154 155 isOpacityModifyRGB:function () { 156 return false; 157 }, 158 159 onExit:function(){ 160 cc.Node.prototype.onExit.call(this); 161 if(this._verticesBuffer) 162 cc._renderContext.deleteBuffer(this._verticesBuffer); 163 if(this._texCoordsBuffer) 164 cc._renderContext.deleteBuffer(this._texCoordsBuffer); 165 if(this._colorPointerBuffer) 166 cc._renderContext.deleteBuffer(this._colorPointerBuffer); 167 }, 168 169 isFastMode:function () { 170 return this.fastMode; 171 }, 172 173 /** 174 * set fast mode 175 * @param {Boolean} fastMode 176 */ 177 setFastMode:function (fastMode) { 178 this.fastMode = fastMode; 179 }, 180 181 isStartingPositionInitialized:function () { 182 return this.startingPositionInitialized; 183 }, 184 185 setStartingPositionInitialized:function (startingPositionInitialized) { 186 this.startingPositionInitialized = startingPositionInitialized; 187 }, 188 189 /** 190 * initializes a motion streak with fade in seconds, minimum segments, stroke's width, color and texture filename or texture 191 * @param {Number} fade time to fade 192 * @param {Number} minSeg minimum segment size 193 * @param {Number} stroke stroke's width 194 * @param {Number} color 195 * @param {string|cc.Texture2D} texture texture filename or texture 196 * @return {Boolean} 197 */ 198 initWithFade:function (fade, minSeg, stroke, color, texture) { 199 if(!texture) 200 throw "cc.MotionStreak.initWithFade(): Invalid filename or texture"; 201 202 if (typeof(texture) === "string") 203 texture = cc.textureCache.addImage(texture); 204 205 cc.Node.prototype.setPosition.call(this, cc.p(0,0)); 206 this.anchorX = 0; 207 this.anchorY = 0; 208 this.ignoreAnchor = true; 209 this.startingPositionInitialized = false; 210 211 this.fastMode = true; 212 this._minSeg = (minSeg == -1.0) ? (stroke / 5.0) : minSeg; 213 this._minSeg *= this._minSeg; 214 215 this._stroke = stroke; 216 this._fadeDelta = 1.0 / fade; 217 218 var locMaxPoints = (0 | (fade * 60)) + 2; 219 this._nuPoints = 0; 220 this._pointState = new Float32Array(locMaxPoints); 221 this._pointVertexes = new Float32Array(locMaxPoints * 2); 222 223 this._vertices = new Float32Array(locMaxPoints * 4); 224 this._texCoords = new Float32Array(locMaxPoints * 4); 225 this._colorPointer = new Uint8Array(locMaxPoints * 8); 226 this._maxPoints = locMaxPoints; 227 228 var gl = cc._renderContext; 229 230 this._verticesBuffer = gl.createBuffer(); 231 this._texCoordsBuffer = gl.createBuffer(); 232 this._colorPointerBuffer = gl.createBuffer(); 233 234 // Set blend mode 235 this._blendFunc.src = gl.SRC_ALPHA; 236 this._blendFunc.dst = gl.ONE_MINUS_SRC_ALPHA; 237 238 // shader program 239 this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR); 240 241 this.texture = texture; 242 this.color = color; 243 this.scheduleUpdate(); 244 245 //bind buffer 246 gl.bindBuffer(gl.ARRAY_BUFFER, this._verticesBuffer); 247 gl.bufferData(gl.ARRAY_BUFFER, this._vertices, gl.DYNAMIC_DRAW); 248 gl.bindBuffer(gl.ARRAY_BUFFER, this._texCoordsBuffer); 249 gl.bufferData(gl.ARRAY_BUFFER, this._texCoords, gl.DYNAMIC_DRAW); 250 gl.bindBuffer(gl.ARRAY_BUFFER, this._colorPointerBuffer); 251 gl.bufferData(gl.ARRAY_BUFFER, this._colorPointer, gl.DYNAMIC_DRAW); 252 253 return true; 254 }, 255 256 /** 257 * color used for the tint 258 * @param {cc.Color} color 259 */ 260 tintWithColor:function (color) { 261 this.color = color; 262 263 // Fast assignation 264 var locColorPointer = this._colorPointer; 265 for (var i = 0, len = this._nuPoints * 2; i < len; i++) { 266 locColorPointer[i * 4] = color.r; 267 locColorPointer[i * 4 + 1] = color.g; 268 locColorPointer[i * 4 + 2] = color.b; 269 } 270 }, 271 272 /** 273 * Remove all living segments of the ribbon 274 */ 275 reset:function () { 276 this._nuPoints = 0; 277 }, 278 279 /** 280 * @override 281 * @param {cc.Point} position 282 */ 283 setPosition:function (position, yValue) { 284 this.startingPositionInitialized = true; 285 if(yValue === undefined){ 286 this._positionR.x = position.x; 287 this._positionR.y = position.y; 288 } else { 289 this._positionR.x = position; 290 this._positionR.y = yValue; 291 } 292 }, 293 294 /** 295 * @return {Number} 296 */ 297 getPositionX:function () { 298 return this._positionR.x; 299 }, 300 301 /** 302 * @param {Number} x 303 */ 304 setPositionX:function (x) { 305 this._positionR.x = x; 306 if(!this.startingPositionInitialized) 307 this.startingPositionInitialized = true; 308 }, 309 310 /** 311 * @return {Number} 312 */ 313 getPositionY:function () { 314 return this._positionR.y; 315 }, 316 317 /** 318 * @param {Number} y 319 */ 320 setPositionY:function (y) { 321 this._positionR.y = y; 322 if(!this.startingPositionInitialized) 323 this.startingPositionInitialized = true; 324 }, 325 326 /** 327 * @override 328 * @param {WebGLRenderingContext} ctx 329 */ 330 draw:function (ctx) { 331 if (this._nuPoints <= 1) 332 return; 333 334 if(this.texture && this.texture.isLoaded()){ 335 ctx = ctx || cc._renderContext; 336 cc.NODE_DRAW_SETUP(this); 337 cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); 338 cc.glBlendFunc(this._blendFunc.src, this._blendFunc.dst); 339 340 cc.glBindTexture2D(this.texture); 341 342 //position 343 ctx.bindBuffer(ctx.ARRAY_BUFFER, this._verticesBuffer); 344 ctx.bufferData(ctx.ARRAY_BUFFER, this._vertices, ctx.DYNAMIC_DRAW); 345 ctx.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, ctx.FLOAT, false, 0, 0); 346 347 //texcoords 348 ctx.bindBuffer(ctx.ARRAY_BUFFER, this._texCoordsBuffer); 349 ctx.bufferData(ctx.ARRAY_BUFFER, this._texCoords, ctx.DYNAMIC_DRAW); 350 ctx.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, ctx.FLOAT, false, 0, 0); 351 352 //colors 353 ctx.bindBuffer(ctx.ARRAY_BUFFER, this._colorPointerBuffer); 354 ctx.bufferData(ctx.ARRAY_BUFFER, this._colorPointer, ctx.DYNAMIC_DRAW); 355 ctx.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, ctx.UNSIGNED_BYTE, true, 0, 0); 356 357 ctx.drawArrays(ctx.TRIANGLE_STRIP, 0, this._nuPoints * 2); 358 cc.g_NumberOfDraws ++; 359 } 360 }, 361 362 /** 363 * @override 364 * @param {Number} delta 365 */ 366 update:function (delta) { 367 if (!this.startingPositionInitialized) 368 return; 369 370 delta *= this._fadeDelta; 371 372 var newIdx, newIdx2, i, i2; 373 var mov = 0; 374 375 // Update current points 376 var locNuPoints = this._nuPoints; 377 var locPointState = this._pointState, locPointVertexes = this._pointVertexes, locVertices = this._vertices; 378 var locColorPointer = this._colorPointer; 379 380 for (i = 0; i < locNuPoints; i++) { 381 locPointState[i] -= delta; 382 383 if (locPointState[i] <= 0) 384 mov++; 385 else { 386 newIdx = i - mov; 387 if (mov > 0) { 388 // Move data 389 locPointState[newIdx] = locPointState[i]; 390 // Move point 391 locPointVertexes[newIdx * 2] = locPointVertexes[i * 2]; 392 locPointVertexes[newIdx * 2 + 1] = locPointVertexes[i * 2 + 1]; 393 394 // Move vertices 395 i2 = i * 2; 396 newIdx2 = newIdx * 2; 397 locVertices[newIdx2 * 2] = locVertices[i2 * 2]; 398 locVertices[newIdx2 * 2 + 1] = locVertices[i2 * 2 + 1]; 399 locVertices[(newIdx2 + 1) * 2] = locVertices[(i2 + 1) * 2]; 400 locVertices[(newIdx2 + 1) * 2 + 1] = locVertices[(i2 + 1) * 2 + 1]; 401 402 // Move color 403 i2 *= 4; 404 newIdx2 *= 4; 405 locColorPointer[newIdx2 + 0] = locColorPointer[i2 + 0]; 406 locColorPointer[newIdx2 + 1] = locColorPointer[i2 + 1]; 407 locColorPointer[newIdx2 + 2] = locColorPointer[i2 + 2]; 408 locColorPointer[newIdx2 + 4] = locColorPointer[i2 + 4]; 409 locColorPointer[newIdx2 + 5] = locColorPointer[i2 + 5]; 410 locColorPointer[newIdx2 + 6] = locColorPointer[i2 + 6]; 411 } else 412 newIdx2 = newIdx * 8; 413 414 var op = locPointState[newIdx] * 255.0; 415 locColorPointer[newIdx2 + 3] = op; 416 locColorPointer[newIdx2 + 7] = op; 417 } 418 } 419 locNuPoints -= mov; 420 421 // Append new point 422 var appendNewPoint = true; 423 if (locNuPoints >= this._maxPoints) 424 appendNewPoint = false; 425 else if (locNuPoints > 0) { 426 var a1 = cc.pDistanceSQ(cc.p(locPointVertexes[(locNuPoints - 1) * 2], locPointVertexes[(locNuPoints - 1) * 2 + 1]), 427 this._positionR) < this._minSeg; 428 var a2 = (locNuPoints == 1) ? false : (cc.pDistanceSQ( 429 cc.p(locPointVertexes[(locNuPoints - 2) * 2], locPointVertexes[(locNuPoints - 2) * 2 + 1]), this._positionR) < (this._minSeg * 2.0)); 430 if (a1 || a2) 431 appendNewPoint = false; 432 } 433 434 if (appendNewPoint) { 435 locPointVertexes[locNuPoints * 2] = this._positionR.x; 436 locPointVertexes[locNuPoints * 2 + 1] = this._positionR.y; 437 locPointState[locNuPoints] = 1.0; 438 439 // Color assignment 440 var offset = locNuPoints * 8; 441 442 var locDisplayedColor = this._displayedColor; 443 locColorPointer[offset] = locDisplayedColor.r; 444 locColorPointer[offset + 1] = locDisplayedColor.g; 445 locColorPointer[offset + 2] = locDisplayedColor.b; 446 //*((ccColor3B*)(m_pColorPointer + offset+4)) = this._color; 447 locColorPointer[offset + 4] = locDisplayedColor.r; 448 locColorPointer[offset + 5] = locDisplayedColor.g; 449 locColorPointer[offset + 6] = locDisplayedColor.b; 450 451 // Opacity 452 locColorPointer[offset + 3] = 255; 453 locColorPointer[offset + 7] = 255; 454 455 // Generate polygon 456 if (locNuPoints > 0 && this.fastMode) { 457 if (locNuPoints > 1) 458 cc.vertexLineToPolygon(locPointVertexes, this._stroke, this._vertices, locNuPoints, 1); 459 else 460 cc.vertexLineToPolygon(locPointVertexes, this._stroke, this._vertices, 0, 2); 461 } 462 locNuPoints++; 463 } 464 465 if (!this.fastMode) 466 cc.vertexLineToPolygon(locPointVertexes, this._stroke, this._vertices, 0, locNuPoints); 467 468 // Updated Tex Coords only if they are different than previous step 469 if (locNuPoints && this._previousNuPoints != locNuPoints) { 470 var texDelta = 1.0 / locNuPoints; 471 var locTexCoords = this._texCoords; 472 for (i = 0; i < locNuPoints; i++) { 473 locTexCoords[i * 4] = 0; 474 locTexCoords[i * 4 + 1] = texDelta * i; 475 476 locTexCoords[(i * 2 + 1) * 2] = 1; 477 locTexCoords[(i * 2 + 1) * 2 + 1] = texDelta * i; 478 } 479 480 this._previousNuPoints = locNuPoints; 481 } 482 483 this._nuPoints = locNuPoints; 484 } 485 }); 486 487 /** 488 * creates and initializes a motion streak with fade in seconds, minimum segments, stroke's width, color, texture filename or texture 489 * @param {Number} fade time to fade 490 * @param {Number} minSeg minimum segment size 491 * @param {Number} stroke stroke's width 492 * @param {Number} color 493 * @param {string|cc.Texture2D} texture texture filename or texture 494 * @return {cc.MotionStreak} 495 */ 496 cc.MotionStreak.create = function (fade, minSeg, stroke, color, texture) { 497 var ret = new cc.MotionStreak(); 498 if (ret && ret.initWithFade(fade, minSeg, stroke, color, texture)) 499 return ret; 500 return null; 501 }; 502