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) 2012 Scott Lembcke and Howling Moon Software 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 * Code copied & pasted from SpacePatrol game https://github.com/slembcke/SpacePatrol 30 * 31 * Renamed and added some changes for cocos2d 32 * 33 */ 34 cc.v2fzero = function () { 35 return {x: 0, y: 0}; 36 }; 37 38 cc.v2f = function (x, y) { 39 return {x: x, y: y}; 40 }; 41 42 cc.v2fadd = function (v0, v1) { 43 return cc.v2f(v0.x + v1.x, v0.y + v1.y); 44 }; 45 46 cc.v2fsub = function (v0, v1) { 47 return cc.v2f(v0.x - v1.x, v0.y - v1.y); 48 }; 49 50 cc.v2fmult = function (v, s) { 51 return cc.v2f(v.x * s, v.y * s); 52 }; 53 54 cc.v2fperp = function (p0) { 55 return cc.v2f(-p0.y, p0.x); 56 }; 57 58 cc.v2fneg = function (p0) { 59 return cc.v2f(-p0.x, -p0.y); 60 }; 61 62 cc.v2fdot = function (p0, p1) { 63 return p0.x * p1.x + p0.y * p1.y; 64 }; 65 66 cc.v2fforangle = function (_a_) { 67 return cc.v2f(Math.cos(_a_), Math.sin(_a_)); 68 }; 69 70 cc.v2fnormalize = function (p) { 71 var r = cc.pNormalize(cc.p(p.x, p.y)); 72 return cc.v2f(r.x, r.y); 73 }; 74 75 cc.__v2f = function (v) { 76 return cc.v2f(v.x, v.y); 77 }; 78 79 cc.__t = function (v) { 80 return {u: v.x, v: v.y}; 81 }; 82 83 /** 84 * <p>CCDrawNode for Canvas <br/> 85 * Node that draws dots, segments and polygons. <br/> 86 * Faster than the "drawing primitives" since they it draws everything in one single batch.</p> 87 * @class 88 * @extends cc.Node 89 */ 90 cc.DrawNodeCanvas = cc.Node.extend(/** @lends cc.DrawNodeCanvas# */{ 91 _buffer:null, 92 _blendFunc:null, 93 94 // ----common function start ---- 95 getBlendFunc:function () { 96 return this._blendFunc; 97 }, 98 99 setBlendFunc:function (blendFunc) { 100 this._blendFunc = blendFunc; 101 }, 102 // ----common function end ---- 103 104 ctor:function () { 105 cc.Node.prototype.ctor.call(this); 106 this._buffer = []; 107 this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST); 108 }, 109 110 draw:function (ctx) { 111 var context = ctx || cc.renderContext; 112 var locEGL_ScaleX = cc.EGLView.getInstance().getScaleX(); 113 114 if ((this._blendFunc && (this._blendFunc.src == gl.SRC_ALPHA) && (this._blendFunc.dst == gl.ONE))) 115 context.globalCompositeOperation = 'lighter'; 116 117 for (var i = 0; i < this._buffer.length; i++) { 118 var element = this._buffer[i]; 119 if (element.type === cc.DrawNode.TYPE_DOT) { 120 context.fillStyle = "rgba(" + (0 | (element.color.r * 255)) + "," + (0 | (element.color.g * 255)) + "," + (0 | (element.color.b * 255)) + "," + element.color.a + ")"; 121 cc.drawingUtil.drawPoint(element.position, element.radius); 122 } 123 124 if (element.type === cc.DrawNode.TYPE_SEGMENT) { 125 context.strokeStyle = "rgba(" + (0 | (element.color.r * 255)) + "," + (0 | (element.color.g * 255)) + "," + (0 | (element.color.b * 255)) + "," + element.color.a + ")"; 126 context.lineWidth = element.radius * 2 * locEGL_ScaleX; 127 context.lineCap = "round"; 128 cc.drawingUtil.drawLine(element.from, element.to); 129 } 130 131 if (element.type === cc.DrawNode.TYPE_POLY ) { 132 context.fillStyle = "rgba(" + (0 | (element.fillColor.r * 255)) + "," + (0 | (element.fillColor.g * 255)) + "," 133 + (0 | (element.fillColor.b * 255)) + "," + element.fillColor.a + ")"; 134 cc.drawingUtil.drawPoly(element.verts, element.count, false, true); 135 context.lineWidth = element.borderWidth * 2 * locEGL_ScaleX; 136 context.lineCap = "round"; 137 context.strokeStyle = "rgba(" + (0 | (element.borderColor.r * 255)) + "," + (0 | (element.borderColor.g * 255)) + "," 138 + (0 | (element.borderColor.b * 255)) + "," + element.borderColor.a + ")"; 139 cc.drawingUtil.drawPoly(element.verts, element.count, true, false); 140 } 141 } 142 }, 143 144 /** 145 * draw a dot at a position, with a given radius and color 146 * @param {cc.Point} pos 147 * @param {Number} radius 148 * @param {cc.Color4F} color 149 */ 150 drawDot:function (pos, radius, color) { 151 var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_DOT); 152 element.position = pos; 153 element.radius = radius; 154 element.color = color; 155 this._buffer.push(element); 156 }, 157 158 /** 159 * draw a segment with a radius and color 160 * @param {cc.Point} from 161 * @param {cc.Point} to 162 * @param {Number} radius 163 * @param {cc.Color4F} color 164 */ 165 drawSegment:function (from, to, radius, color) { 166 var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_SEGMENT); 167 element.from = from; 168 element.to = to; 169 element.radius = radius; 170 element.color = color; 171 this._buffer.push(element); 172 }, 173 174 /** 175 * draw a polygon with a fill color and line color 176 * @param {Array} verts 177 * @param {cc.Color4F} fillColor 178 * @param {Number} borderWidth 179 * @param {cc.Color4F} borderColor 180 */ 181 drawPoly:function (verts, fillColor, borderWidth, borderColor) { 182 var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY ); 183 element.verts = verts; 184 element.count = verts.length; 185 element.fillColor = fillColor; 186 element.borderWidth = borderWidth; 187 element.borderColor = borderColor; 188 this._buffer.push(element); 189 }, 190 191 /** 192 * Clear the geometry in the node's buffer. 193 */ 194 clear:function () { 195 this._buffer.length = 0; 196 } 197 }); 198 199 /** 200 * <p>CCDrawNode for WebGL <br/> 201 * Node that draws dots, segments and polygons. <br/> 202 * Faster than the "drawing primitives" since they it draws everything in one single batch.</p> 203 * @class 204 * @extends cc.Node 205 */ 206 cc.DrawNodeWebGL = cc.Node.extend(/** @lends cc.DrawNodeWebGL# */{ 207 _bufferCapacity:0, 208 _buffer:null, 209 210 _trianglesArrayBuffer:null, 211 _trianglesWebBuffer:null, 212 _trianglesReader:null, 213 214 _blendFunc:null, 215 _dirty:false, 216 217 // ----common function start ---- 218 getBlendFunc:function () { 219 return this._blendFunc; 220 }, 221 222 setBlendFunc:function (blendFunc) { 223 this._blendFunc = blendFunc; 224 }, 225 // ----common function end ---- 226 227 ctor:function () { 228 cc.Node.prototype.ctor.call(this); 229 this._buffer = []; 230 this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST); 231 }, 232 233 init:function () { 234 if (cc.Node.prototype.init.call(this)) { 235 this.setShaderProgram(cc.ShaderCache.getInstance().programForKey(cc.SHADER_POSITION_LENGTHTEXTURECOLOR)); 236 this._ensureCapacity(512); 237 this._trianglesWebBuffer = cc.renderContext.createBuffer(); 238 this._dirty = true; 239 return true; 240 } 241 return false; 242 }, 243 244 _render:function () { 245 var gl = cc.renderContext; 246 247 cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSCOLORTEX); 248 gl.bindBuffer(gl.ARRAY_BUFFER, this._trianglesWebBuffer); 249 if (this._dirty) { 250 gl.bufferData(gl.ARRAY_BUFFER, this._trianglesArrayBuffer, gl.STREAM_DRAW); 251 this._dirty = false; 252 } 253 var triangleSize = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT; 254 255 // vertex 256 gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, gl.FLOAT, false, triangleSize, 0); 257 // color 258 gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, triangleSize, 8); 259 // texcood 260 gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, triangleSize, 12); 261 262 gl.drawArrays(gl.TRIANGLES, 0, this._buffer.length * 3); 263 cc.INCREMENT_GL_DRAWS(1); 264 //cc.CHECK_GL_ERROR_DEBUG(); 265 }, 266 267 _ensureCapacity:function(count){ 268 if(this._buffer.length + count > this._bufferCapacity){ 269 var TriangleLength = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT; 270 this._bufferCapacity += Math.max(this._bufferCapacity, count); 271 //re alloc 272 if((this._buffer == null) || (this._buffer.length === 0)){ 273 //init 274 this._buffer = []; 275 this._trianglesArrayBuffer = new ArrayBuffer(TriangleLength * this._bufferCapacity); 276 this._trianglesReader = new Uint8Array(this._trianglesArrayBuffer); 277 } else { 278 var newTriangles = this._buffer; 279 newTriangles.length = 0; 280 var newArrayBuffer = new ArrayBuffer(TriangleLength * this._bufferCapacity); 281 282 for(var i = 0; i < this._buffer.length;i++){ 283 newTriangles[i] = new cc.V2F_C4B_T2F_Triangle(this._buffer[i].a,this._buffer[i].b,this._buffer[i].c, 284 newArrayBuffer,i * TriangleLength); 285 } 286 this._trianglesReader = new Uint8Array(newArrayBuffer); 287 this._trianglesArrayBuffer = newArrayBuffer; 288 } 289 } 290 }, 291 292 draw:function () { 293 cc.glBlendFunc(this._blendFunc.src, this._blendFunc.dst); 294 this._shaderProgram.use(); 295 this._shaderProgram.setUniformsForBuiltins(); 296 this._render(); 297 }, 298 299 /** 300 * draw a dot at a position, with a given radius and color 301 * @param {cc.Point} pos 302 * @param {Number} radius 303 * @param {cc.Color4F} color 304 */ 305 drawDot:function (pos, radius, color) { 306 var c4bColor = {r: 0 | (color.r * 255), g: 0 | (color.g * 255), b: 0 | (color.b * 255), a: 0 | (color.a * 255)}; 307 var a = {vertices: {x: pos.x - radius, y: pos.y - radius}, colors: c4bColor, texCoords: {u: -1.0, v: -1.0}}; 308 var b = {vertices: {x: pos.x - radius, y: pos.y + radius}, colors: c4bColor, texCoords: {u: -1.0, v: 1.0}}; 309 var c = {vertices: {x: pos.x + radius, y: pos.y + radius}, colors: c4bColor, texCoords: {u: 1.0, v: 1.0}}; 310 var d = {vertices: {x: pos.x + radius, y: pos.y - radius}, colors: c4bColor, texCoords: {u: 1.0, v: -1.0}}; 311 this._buffer.push(new cc.V2F_C4B_T2F_Triangle(a, b, c, this._trianglesArrayBuffer, this._buffer.length * cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT)); 312 this._buffer.push(new cc.V2F_C4B_T2F_Triangle(a, c, d, this._trianglesArrayBuffer, this._buffer.length * cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT)); 313 this._dirty = true; 314 }, 315 316 /** 317 * draw a segment with a radius and color 318 * @param {cc.Point} from 319 * @param {cc.Point} to 320 * @param {Number} radius 321 * @param {cc.Color4F} color 322 */ 323 drawSegment:function (from, to, radius, color) { 324 var vertexCount = 6*3; 325 this._ensureCapacity(vertexCount); 326 327 var c4bColor = {r: 0 | (color.r * 255), g: 0 | (color.g * 255), b: 0 | (color.b * 255), a: 0 | (color.a * 255)}; 328 var a = cc.__v2f(from); 329 var b = cc.__v2f(to); 330 331 var n = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(b, a))); 332 var t = cc.v2fperp(n); 333 334 var nw = cc.v2fmult(n, radius); 335 var tw = cc.v2fmult(t, radius); 336 var v0 = cc.v2fsub(b, cc.v2fadd(nw, tw)); 337 var v1 = cc.v2fadd(b, cc.v2fsub(nw, tw)); 338 var v2 = cc.v2fsub(b, nw); 339 var v3 = cc.v2fadd(b, nw); 340 var v4 = cc.v2fsub(a, nw); 341 var v5 = cc.v2fadd(a, nw); 342 var v6 = cc.v2fsub(a, cc.v2fsub(nw, tw)); 343 var v7 = cc.v2fadd(a, cc.v2fadd(nw, tw)); 344 345 var TriangleLength = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT, triangleBuffer = this._trianglesArrayBuffer; 346 this._buffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v0, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(cc.v2fadd(n, t)))}, 347 {vertices: v1, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(n, t))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, 348 triangleBuffer, this._buffer.length * TriangleLength)); 349 350 this._buffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)}, 351 {vertices: v1, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(n, t))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, 352 triangleBuffer, this._buffer.length * TriangleLength)); 353 354 this._buffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)}, 355 {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, 356 triangleBuffer, this._buffer.length * TriangleLength)); 357 358 this._buffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)}, 359 {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)}, 360 triangleBuffer, this._buffer.length * TriangleLength)); 361 362 this._buffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v6, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(t, n))}, 363 {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)}, 364 triangleBuffer, this._buffer.length * TriangleLength)); 365 366 this._buffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v6, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(t, n))}, 367 {vertices: v7, colors: c4bColor, texCoords: cc.__t(cc.v2fadd(n, t))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)}, 368 triangleBuffer, this._buffer.length * TriangleLength)); 369 this._dirty = true; 370 }, 371 372 /** 373 * draw a polygon with a fill color and line color 374 * @param {Array} verts 375 * @param {cc.Color4F} fillColor 376 * @param {Number} borderWidth 377 * @param {cc.Color4F} borderColor 378 */ 379 drawPoly:function (verts, fillColor, borderWidth, borderColor) { 380 var c4bFillColor = {r: 0 | (fillColor.r * 255), g: 0 | (fillColor.g * 255), b: 0 | (fillColor.b * 255), a: 0 | (fillColor.a * 255)}; 381 var c4bBorderColor = {r: 0 | (borderColor.r * 255), g: 0 | (borderColor.g * 255), b: 0 | (borderColor.b * 255), a: 0 | (borderColor.a * 255)}; 382 var extrude = [], i; 383 var v0, v1, v2; 384 var count = verts.length; 385 for (i = 0; i < count; i++) { 386 v0 = cc.__v2f(verts[(i - 1 + count) % count]); 387 v1 = cc.__v2f(verts[i]); 388 v2 = cc.__v2f(verts[(i + 1) % count]); 389 var n1 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v1, v0))); 390 var n2 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v2, v1))); 391 var offset = cc.v2fmult(cc.v2fadd(n1, n2), 1.0 / (cc.v2fdot(n1, n2) + 1.0)); 392 extrude[i] = {offset: offset, n: n2}; 393 } 394 var outline = (borderWidth > 0.0); 395 396 var triangleCount = 3 * count -2; 397 var vertexCount = 3 * triangleCount; 398 this._ensureCapacity(vertexCount); 399 400 var triangleBytesLen = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT, trianglesBuffer = this._trianglesArrayBuffer; 401 var locBuffer = this._buffer; 402 var inset = (outline == false ? 0.5 : 0.0); 403 for (i = 0; i < count - 2; i++) { 404 v0 = cc.v2fsub(cc.__v2f(verts[0]), cc.v2fmult(extrude[0].offset, inset)); 405 v1 = cc.v2fsub(cc.__v2f(verts[i + 1]), cc.v2fmult(extrude[i + 1].offset, inset)); 406 v2 = cc.v2fsub(cc.__v2f(verts[i + 2]), cc.v2fmult(extrude[i + 2].offset, inset)); 407 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, 408 {vertices: v1, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, {vertices: v2, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, 409 trianglesBuffer, locBuffer.length * triangleBytesLen)); 410 } 411 412 for (i = 0; i < count; i++) { 413 var j = (i + 1) % count; 414 v0 = cc.__v2f(verts[i]); 415 v1 = cc.__v2f(verts[j]); 416 417 var n0 = extrude[i].n; 418 var offset0 = extrude[i].offset; 419 var offset1 = extrude[j].offset; 420 var inner0 = outline ? cc.v2fsub(v0, cc.v2fmult(offset0, borderWidth)) : cc.v2fsub(v0, cc.v2fmult(offset0, 0.5)); 421 var inner1 = outline ? cc.v2fsub(v1, cc.v2fmult(offset1, borderWidth)) : cc.v2fsub(v1, cc.v2fmult(offset1, 0.5)); 422 var outer0 = outline ? cc.v2fadd(v0, cc.v2fmult(offset0, borderWidth)) : cc.v2fadd(v0, cc.v2fmult(offset0, 0.5)); 423 var outer1 = outline ? cc.v2fadd(v1, cc.v2fmult(offset1, borderWidth)) : cc.v2fadd(v1, cc.v2fmult(offset1, 0.5)); 424 425 if (outline) { 426 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))}, 427 {vertices: inner1, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)}, 428 trianglesBuffer, locBuffer.length * triangleBytesLen)); 429 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))}, 430 {vertices: outer0, colors: c4bBorderColor, texCoords: cc.__t(n0)}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)}, 431 trianglesBuffer, locBuffer.length * triangleBytesLen)); 432 } else { 433 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, 434 {vertices: inner1, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, {vertices: outer1, colors: c4bFillColor, texCoords: cc.__t(n0)}, 435 trianglesBuffer, locBuffer.length * triangleBytesLen)); 436 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, 437 {vertices: outer0, colors: c4bFillColor, texCoords: cc.__t(n0)}, {vertices: outer1, colors: c4bFillColor, texCoords: cc.__t(n0)}, 438 trianglesBuffer, locBuffer.length * triangleBytesLen)); 439 } 440 } 441 extrude = null; 442 this._dirty = true; 443 }, 444 445 /** 446 * Clear the geometry in the node's buffer. 447 */ 448 clear:function () { 449 this._buffer.length = 0; 450 this._dirty = true; 451 } 452 }); 453 454 cc.DrawNode = cc.Browser.supportWebGL ? cc.DrawNodeWebGL : cc.DrawNodeCanvas; 455 456 /** 457 * Creates a DrawNode 458 * @return {cc.DrawNode} 459 */ 460 cc.DrawNode.create = function () { 461 var ret = new cc.DrawNode(); 462 if (ret && ret.init()) 463 return ret; 464 return null; 465 }; 466 467 cc._DrawNodeElement = function (type) { 468 this.type = type; 469 }; 470 471 cc.DrawNode.TYPE_DOT = 0; 472 cc.DrawNode.TYPE_SEGMENT = 1; 473 cc.DrawNode.TYPE_POLY = 2; 474