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) 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                                                <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  * @name cc.DrawNode
 89  * @extends cc.Node
 90  */
 91 cc.DrawNodeCanvas = cc.Node.extend(/** @lends cc.DrawNode# */{                  //TODO need refactor
 92     _buffer: null,
 93     _blendFunc: null,
 94     _lineWidth: 1,
 95     _drawColor: null,
 96     _className:"DrawNodeCanvas",
 97 
 98     /**
 99      * <p>The cc.DrawNodeCanvas's constructor. <br/>
100      * This function will automatically be invoked when you create a node using new construction: "var node = new cc.DrawNodeCanvas()".<br/>
101      * Override it to extend its behavior, remember to call "this._super()" in the extended "ctor" function.</p>
102      */
103     ctor: function () {
104         cc.Node.prototype.ctor.call(this);
105         var locCmd = this._renderCmd;
106         locCmd._buffer = this._buffer = [];
107         locCmd._drawColor = this._drawColor = cc.color(255, 255, 255, 255);
108         locCmd._blendFunc = this._blendFunc = new cc.BlendFunc(cc.SRC_ALPHA, cc.ONE_MINUS_SRC_ALPHA);
109 
110 		this.init();
111     },
112 
113     // ----common function start ----
114     /**
115      * Gets the blend func
116      * @returns {Object}
117      */
118     getBlendFunc: function () {
119         return this._blendFunc;
120     },
121 
122     /**
123      * Set the blend func
124      * @param blendFunc
125      * @param dst
126      */
127     setBlendFunc: function (blendFunc, dst) {
128         if (dst === undefined) {
129             this._blendFunc.src = blendFunc.src;
130             this._blendFunc.dst = blendFunc.dst;
131         } else {
132             this._blendFunc.src = blendFunc;
133             this._blendFunc.dst = dst;
134         }
135     },
136 
137     /**
138      * line width setter
139      * @param {Number} width
140      */
141     setLineWidth: function (width) {
142         this._lineWidth = width;
143     },
144 
145     /**
146      * line width getter
147      * @returns {Number}
148      */
149     getLineWidth: function () {
150         return this._lineWidth;
151     },
152 
153     /**
154      * draw color setter
155      * @param {cc.Color} color
156      */
157     setDrawColor: function (color) {
158         var locDrawColor = this._drawColor;
159         locDrawColor.r = color.r;
160         locDrawColor.g = color.g;
161         locDrawColor.b = color.b;
162         locDrawColor.a = (color.a == null) ? 255 : color.a;
163     },
164 
165     /**
166      * draw color getter
167      * @returns {cc.Color}
168      */
169     getDrawColor: function () {
170         return  cc.color(this._drawColor.r, this._drawColor.g, this._drawColor.b, this._drawColor.a);
171     },
172     // ----common function end ----
173 
174 
175     /**
176      * draws a rectangle given the origin and destination point measured in points.
177      * @param {cc.Point} origin
178      * @param {cc.Point} destination
179      * @param {cc.Color} fillColor
180      * @param {Number} lineWidth
181      * @param {cc.Color} lineColor
182      */
183     drawRect: function (origin, destination, fillColor, lineWidth, lineColor) {
184         lineWidth = (lineWidth == null) ? this._lineWidth : lineWidth;
185         lineColor = lineColor || this.getDrawColor();
186         if(lineColor.a == null)
187             lineColor.a = 255;
188 
189         var vertices = [
190             origin,
191             cc.p(destination.x, origin.y),
192             destination,
193             cc.p(origin.x, destination.y)
194         ];
195         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
196         element.verts = vertices;
197         element.lineWidth = lineWidth;
198         element.lineColor = lineColor;
199         element.isClosePolygon = true;
200         element.isStroke = true;
201         element.lineCap = "butt";
202         element.fillColor = fillColor;
203         if (fillColor) {
204             if(fillColor.a == null)
205                 fillColor.a = 255;
206             element.isFill = true;
207         }
208         this._buffer.push(element);
209     },
210 
211     /**
212      * draws a circle given the center, radius and number of segments.
213      * @override
214      * @param {cc.Point} center center of circle
215      * @param {Number} radius
216      * @param {Number} angle angle in radians
217      * @param {Number} segments
218      * @param {Boolean} drawLineToCenter
219      * @param {Number} lineWidth
220      * @param {cc.Color} color
221      */
222     drawCircle: function (center, radius, angle, segments, drawLineToCenter, lineWidth, color) {
223         lineWidth = lineWidth || this._lineWidth;
224         color = color || this.getDrawColor();
225         if (color.a == null)
226             color.a = 255;
227 
228         var coef = 2.0 * Math.PI / segments;
229         var vertices = [];
230         for (var i = 0; i <= segments; i++) {
231             var rads = i * coef;
232             var j = radius * Math.cos(rads + angle) + center.x;
233             var k = radius * Math.sin(rads + angle) + center.y;
234             vertices.push(cc.p(j, k));
235         }
236         if (drawLineToCenter) {
237             vertices.push(cc.p(center.x, center.y));
238         }
239 
240         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
241         element.verts = vertices;
242         element.lineWidth = lineWidth;
243         element.lineColor = color;
244         element.isClosePolygon = true;
245         element.isStroke = true;
246         this._buffer.push(element);
247     },
248 
249     /**
250      * draws a quad bezier path
251      * @override
252      * @param {cc.Point} origin
253      * @param {cc.Point} control
254      * @param {cc.Point} destination
255      * @param {Number} segments
256      * @param {Number} lineWidth
257      * @param {cc.Color} color
258      */
259     drawQuadBezier: function (origin, control, destination, segments, lineWidth, color) {
260         lineWidth = lineWidth || this._lineWidth;
261         color = color || this.getDrawColor();
262         if (color.a == null)
263             color.a = 255;
264 
265         var vertices = [], t = 0.0;
266         for (var i = 0; i < segments; i++) {
267             var x = Math.pow(1 - t, 2) * origin.x + 2.0 * (1 - t) * t * control.x + t * t * destination.x;
268             var y = Math.pow(1 - t, 2) * origin.y + 2.0 * (1 - t) * t * control.y + t * t * destination.y;
269             vertices.push(cc.p(x, y));
270             t += 1.0 / segments;
271         }
272         vertices.push(cc.p(destination.x, destination.y));
273 
274         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
275         element.verts = vertices;
276         element.lineWidth = lineWidth;
277         element.lineColor = color;
278         element.isStroke = true;
279         element.lineCap = "round";
280         this._buffer.push(element);
281     },
282 
283     /**
284      * draws a cubic bezier path
285      * @override
286      * @param {cc.Point} origin
287      * @param {cc.Point} control1
288      * @param {cc.Point} control2
289      * @param {cc.Point} destination
290      * @param {Number} segments
291      * @param {Number} lineWidth
292      * @param {cc.Color} color
293      */
294     drawCubicBezier: function (origin, control1, control2, destination, segments, lineWidth, color) {
295         lineWidth = lineWidth || this._lineWidth;
296         color = color || this.getDrawColor();
297         if (color.a == null)
298             color.a = 255;
299 
300         var vertices = [], t = 0;
301         for (var i = 0; i < segments; i++) {
302             var x = Math.pow(1 - t, 3) * origin.x + 3.0 * Math.pow(1 - t, 2) * t * control1.x + 3.0 * (1 - t) * t * t * control2.x + t * t * t * destination.x;
303             var y = Math.pow(1 - t, 3) * origin.y + 3.0 * Math.pow(1 - t, 2) * t * control1.y + 3.0 * (1 - t) * t * t * control2.y + t * t * t * destination.y;
304             vertices.push(cc.p(x, y));
305             t += 1.0 / segments;
306         }
307         vertices.push(cc.p(destination.x, destination.y));
308 
309         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
310         element.verts = vertices;
311         element.lineWidth = lineWidth;
312         element.lineColor = color;
313         element.isStroke = true;
314         element.lineCap = "round";
315         this._buffer.push(element);
316     },
317 
318     /**
319      * draw a CatmullRom curve
320      * @override
321      * @param {Array} points
322      * @param {Number} segments
323      * @param {Number} lineWidth
324      * @param {cc.Color} color
325      */
326     drawCatmullRom: function (points, segments, lineWidth, color) {
327         this.drawCardinalSpline(points, 0.5, segments, lineWidth, color);
328     },
329 
330     /**
331      * draw a cardinal spline path
332      * @override
333      * @param {Array} config
334      * @param {Number} tension
335      * @param {Number} segments
336      * @param {Number} lineWidth
337      * @param {cc.Color} color
338      */
339     drawCardinalSpline: function (config, tension, segments, lineWidth, color) {
340         lineWidth = lineWidth || this._lineWidth;
341         color = color || this.getDrawColor();
342         if(color.a == null)
343             color.a = 255;
344 
345         var vertices = [], p, lt, deltaT = 1.0 / config.length;
346         for (var i = 0; i < segments + 1; i++) {
347             var dt = i / segments;
348             // border
349             if (dt === 1) {
350                 p = config.length - 1;
351                 lt = 1;
352             } else {
353                 p = 0 | (dt / deltaT);
354                 lt = (dt - deltaT * p) / deltaT;
355             }
356 
357             // Interpolate
358             var newPos = cc.cardinalSplineAt(
359                 cc.getControlPointAt(config, p - 1),
360                 cc.getControlPointAt(config, p - 0),
361                 cc.getControlPointAt(config, p + 1),
362                 cc.getControlPointAt(config, p + 2),
363                 tension, lt);
364             vertices.push(newPos);
365         }
366 
367         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
368         element.verts = vertices;
369         element.lineWidth = lineWidth;
370         element.lineColor = color;
371         element.isStroke = true;
372         element.lineCap = "round";
373         this._buffer.push(element);
374     },
375 
376     /**
377      * draw a dot at a position, with a given radius and color
378      * @param {cc.Point} pos
379      * @param {Number} radius
380      * @param {cc.Color} color
381      */
382     drawDot: function (pos, radius, color) {
383         color = color || this.getDrawColor();
384         if (color.a == null)
385             color.a = 255;
386         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_DOT);
387         element.verts = [pos];
388         element.lineWidth = radius;
389         element.fillColor = color;
390         this._buffer.push(element);
391     },
392 
393     /**
394      * draws an array of points.
395      * @override
396      * @param {Array} points point of array
397      * @param {Number} radius
398      * @param {cc.Color} color
399      */
400     drawDots: function(points, radius, color){
401         if(!points || points.length == 0)
402             return;
403         color = color || this.getDrawColor();
404         if (color.a == null)
405             color.a = 255;
406         for(var i = 0, len = points.length; i < len; i++)
407            this.drawDot(points[i], radius, color);
408     },
409 
410     /**
411      * draw a segment with a radius and color
412      * @param {cc.Point} from
413      * @param {cc.Point} to
414      * @param {Number} lineWidth
415      * @param {cc.Color} color
416      */
417     drawSegment: function (from, to, lineWidth, color) {
418         lineWidth = lineWidth || this._lineWidth;
419         color = color || this.getDrawColor();
420         if (color.a == null)
421             color.a = 255;
422         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
423         element.verts = [from, to];
424         element.lineWidth = lineWidth * 2;
425         element.lineColor = color;
426         element.isStroke = true;
427         element.lineCap = "round";
428         this._buffer.push(element);
429     },
430 
431     /**
432      * draw a polygon with a fill color and line color without copying the vertex list
433      * @param {Array} verts
434      * @param {cc.Color} fillColor
435      * @param {Number} lineWidth
436      * @param {cc.Color} color
437      */
438     drawPoly_: function (verts, fillColor, lineWidth, color) {
439         lineWidth = (lineWidth == null ) ? this._lineWidth : lineWidth;
440         color = color || this.getDrawColor();
441         if (color.a == null)
442             color.a = 255;
443         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
444         
445         element.verts = verts;
446         element.fillColor = fillColor;
447         element.lineWidth = lineWidth;
448         element.lineColor = color;
449         element.isClosePolygon = true;
450         element.isStroke = true;
451         element.lineCap = "round";
452         if (fillColor)
453             element.isFill = true;
454         this._buffer.push(element);
455     },
456     
457     /**
458      * draw a polygon with a fill color and line color, copying the vertex list
459      * @param {Array} verts
460      * @param {cc.Color} fillColor
461      * @param {Number} lineWidth
462      * @param {cc.Color} color
463      */
464     drawPoly: function (verts, fillColor, lineWidth, color) {
465         var vertsCopy = [];
466         for (var i=0; i < verts.length; i++) {
467             vertsCopy.push(cc.p(verts[i].x, verts[i].y));
468         }
469         return this.drawPoly_(vertsCopy, fillColor, lineWidth, color);     
470     },
471 
472     /**
473      * Clear the geometry in the node's buffer.
474      */
475     clear: function () {
476         this._buffer.length = 0;
477     },
478 
479     _createRenderCmd: function(){
480         return new cc.DrawNode.CanvasRenderCmd(this);
481     }
482 });
483 
484 //Just only a note
485 cc.DrawNodeWebGL = cc.Node.extend({
486     _bufferCapacity:0,
487     _buffer:null,
488 
489     _trianglesArrayBuffer:null,
490     _trianglesWebBuffer:null,
491     _trianglesReader:null,
492 
493     _lineWidth: 1,
494     _drawColor: null,
495 
496     _blendFunc:null,
497     _dirty:false,
498     _className:"DrawNodeWebGL",
499 
500     // ----common function start ----
501     getBlendFunc:function () {
502         return this._blendFunc;
503     },
504 
505     setBlendFunc:function (blendFunc, dst) {
506         if (dst === undefined) {
507             this._blendFunc.src = blendFunc.src;
508             this._blendFunc.dst = blendFunc.dst;
509         } else {
510             this._blendFunc.src = blendFunc;
511             this._blendFunc.dst = dst;
512         }
513     },
514     // ----common function end ----
515 
516     ctor:function () {
517         cc.Node.prototype.ctor.call(this);
518         this._buffer = [];
519         this._blendFunc = new cc.BlendFunc(cc.SRC_ALPHA, cc.ONE_MINUS_SRC_ALPHA);
520         this._drawColor = cc.color(255,255,255,255);
521 
522 	    this.init();
523     },
524 
525     init:function () {
526         if (cc.Node.prototype.init.call(this)) {
527             this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_LENGTHTEXTURECOLOR);
528             this._ensureCapacity(64);
529             this._trianglesWebBuffer = cc._renderContext.createBuffer();
530             this._dirty = true;
531             return true;
532         }
533         return false;
534     },
535 
536     setLineWidth: function (width) {
537         this._lineWidth = width;
538     },
539 
540     getLineWidth: function () {
541         return this._lineWidth;
542     },
543 
544     setDrawColor: function (color) {
545         var locDrawColor = this._drawColor;
546         locDrawColor.r = color.r;
547         locDrawColor.g = color.g;
548         locDrawColor.b = color.b;
549         locDrawColor.a = color.a;
550     },
551 
552     getDrawColor: function () {
553         return  cc.color(this._drawColor.r, this._drawColor.g, this._drawColor.b, this._drawColor.a);
554     },
555 
556     drawRect: function (origin, destination, fillColor, lineWidth, lineColor) {
557         lineWidth = (lineWidth == null) ? this._lineWidth : lineWidth;
558         lineColor = lineColor || this.getDrawColor();
559         if (lineColor.a == null)
560             lineColor.a = 255;
561         var vertices = [origin, cc.p(destination.x, origin.y), destination, cc.p(origin.x, destination.y)];
562         if(fillColor == null)
563             this._drawSegments(vertices, lineWidth, lineColor, true);
564         else
565             this.drawPoly(vertices, fillColor, lineWidth, lineColor);
566     },
567 
568     drawCircle: function (center, radius, angle, segments, drawLineToCenter, lineWidth, color) {
569         lineWidth = lineWidth || this._lineWidth;
570         color = color || this.getDrawColor();
571         if (color.a == null)
572             color.a = 255;
573         var coef = 2.0 * Math.PI / segments, vertices = [], i, len;
574         for (i = 0; i <= segments; i++) {
575             var rads = i * coef;
576             var j = radius * Math.cos(rads + angle) + center.x;
577             var k = radius * Math.sin(rads + angle) + center.y;
578             vertices.push(cc.p(j, k));
579         }
580         if (drawLineToCenter)
581             vertices.push(cc.p(center.x, center.y));
582 
583         lineWidth *= 0.5;
584         for (i = 0, len = vertices.length; i < len - 1; i++)
585             this.drawSegment(vertices[i], vertices[i + 1], lineWidth, color);
586     },
587 
588     drawQuadBezier: function (origin, control, destination, segments, lineWidth, color) {
589         lineWidth = lineWidth || this._lineWidth;
590         color = color || this.getDrawColor();
591         if (color.a == null)
592             color.a = 255;
593         var vertices = [], t = 0.0;
594         for (var i = 0; i < segments; i++) {
595             var x = Math.pow(1 - t, 2) * origin.x + 2.0 * (1 - t) * t * control.x + t * t * destination.x;
596             var y = Math.pow(1 - t, 2) * origin.y + 2.0 * (1 - t) * t * control.y + t * t * destination.y;
597             vertices.push(cc.p(x, y));
598             t += 1.0 / segments;
599         }
600         vertices.push(cc.p(destination.x, destination.y));
601         this._drawSegments(vertices, lineWidth, color, false);
602     },
603 
604     drawCubicBezier: function (origin, control1, control2, destination, segments, lineWidth, color) {
605         lineWidth = lineWidth || this._lineWidth;
606         color = color || this.getDrawColor();
607         if (color.a == null)
608             color.a = 255;
609         var vertices = [], t = 0;
610         for (var i = 0; i < segments; i++) {
611             var x = Math.pow(1 - t, 3) * origin.x + 3.0 * Math.pow(1 - t, 2) * t * control1.x + 3.0 * (1 - t) * t * t * control2.x + t * t * t * destination.x;
612             var y = Math.pow(1 - t, 3) * origin.y + 3.0 * Math.pow(1 - t, 2) * t * control1.y + 3.0 * (1 - t) * t * t * control2.y + t * t * t * destination.y;
613             vertices.push(cc.p(x, y));
614             t += 1.0 / segments;
615         }
616         vertices.push(cc.p(destination.x, destination.y));
617         this._drawSegments(vertices, lineWidth, color, false);
618     },
619 
620     drawCatmullRom: function (points, segments, lineWidth, color) {
621         this.drawCardinalSpline(points, 0.5, segments, lineWidth, color);
622     },
623 
624     drawCardinalSpline: function (config, tension, segments, lineWidth, color) {
625         lineWidth = lineWidth || this._lineWidth;
626         color = color || this.getDrawColor();
627         if (color.a == null)
628             color.a = 255;
629         var vertices = [], p, lt, deltaT = 1.0 / config.length;
630 
631         for (var i = 0; i < segments + 1; i++) {
632             var dt = i / segments;
633 
634             // border
635             if (dt === 1) {
636                 p = config.length - 1;
637                 lt = 1;
638             } else {
639                 p = 0 | (dt / deltaT);
640                 lt = (dt - deltaT * p) / deltaT;
641             }
642 
643             // Interpolate
644             var newPos = cc.cardinalSplineAt(
645                 cc.getControlPointAt(config, p - 1),
646                 cc.getControlPointAt(config, p - 0),
647                 cc.getControlPointAt(config, p + 1),
648                 cc.getControlPointAt(config, p + 2),
649                 tension, lt);
650             vertices.push(newPos);
651         }
652 
653         lineWidth *= 0.5;
654         for (var j = 0, len = vertices.length; j < len - 1; j++)
655             this.drawSegment(vertices[j], vertices[j + 1], lineWidth, color);
656     },
657 
658     _render:function () {
659         var gl = cc._renderContext;
660 
661         cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);
662         gl.bindBuffer(gl.ARRAY_BUFFER, this._trianglesWebBuffer);
663         if (this._dirty) {
664             gl.bufferData(gl.ARRAY_BUFFER, this._trianglesArrayBuffer, gl.STREAM_DRAW);
665             this._dirty = false;
666         }
667         var triangleSize = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT;
668 
669         // vertex
670         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, gl.FLOAT, false, triangleSize, 0);
671         // color
672         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, triangleSize, 8);
673         // texcood
674         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, triangleSize, 12);
675 
676         gl.drawArrays(gl.TRIANGLES, 0, this._buffer.length * 3);
677         cc.incrementGLDraws(1);
678         //cc.checkGLErrorDebug();
679     },
680 
681     _ensureCapacity:function(count){
682         var _t = this;
683         var locBuffer = _t._buffer;
684         if(locBuffer.length + count > _t._bufferCapacity){
685             var TriangleLength = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT;
686             _t._bufferCapacity += Math.max(_t._bufferCapacity, count);
687             //re alloc
688             if((locBuffer == null) || (locBuffer.length === 0)){
689                 //init
690                 _t._buffer = [];
691                 _t._trianglesArrayBuffer = new ArrayBuffer(TriangleLength * _t._bufferCapacity);
692                 _t._trianglesReader = new Uint8Array(_t._trianglesArrayBuffer);
693             } else {
694                 var newTriangles = [];
695                 var newArrayBuffer = new ArrayBuffer(TriangleLength * _t._bufferCapacity);
696                 for(var i = 0; i < locBuffer.length;i++){
697                     newTriangles[i] = new cc.V2F_C4B_T2F_Triangle(locBuffer[i].a,locBuffer[i].b,locBuffer[i].c,
698                         newArrayBuffer, i * TriangleLength);
699                 }
700                 _t._trianglesReader = new Uint8Array(newArrayBuffer);
701                 _t._trianglesArrayBuffer = newArrayBuffer;
702                 _t._buffer = newTriangles;
703             }
704         }
705     },
706 
707     drawDot:function (pos, radius, color) {
708         color = color || this.getDrawColor();
709         if (color.a == null)
710             color.a = 255;
711         var c4bColor = {r: 0 | color.r, g: 0 | color.g, b: 0 | color.b, a: 0 | color.a};
712         var a = {vertices: {x: pos.x - radius, y: pos.y - radius}, colors: c4bColor, texCoords: {u: -1.0, v: -1.0}};
713         var b = {vertices: {x: pos.x - radius, y: pos.y + radius}, colors: c4bColor, texCoords: {u: -1.0, v: 1.0}};
714         var c = {vertices: {x: pos.x + radius, y: pos.y + radius}, colors: c4bColor, texCoords: {u: 1.0, v: 1.0}};
715         var d = {vertices: {x: pos.x + radius, y: pos.y - radius}, colors: c4bColor, texCoords: {u: 1.0, v: -1.0}};
716 
717         this._ensureCapacity(2*3);
718 
719         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));
720         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));
721         this._dirty = true;
722     },
723 
724     drawDots: function(points, radius,color) {
725         if(!points || points.length === 0)
726             return;
727         color = color || this.getDrawColor();
728         if (color.a == null)
729             color.a = 255;
730         for(var i = 0, len = points.length; i < len; i++)
731             this.drawDot(points[i], radius, color);
732     },
733 
734     drawSegment:function (from, to, radius, color) {
735         color = color || this.getDrawColor();
736         if (color.a == null)
737             color.a = 255;
738         radius = radius || (this._lineWidth * 0.5);
739         var vertexCount = 6*3;
740         this._ensureCapacity(vertexCount);
741 
742         var c4bColor = {r: 0 | color.r, g: 0 | color.g, b: 0 | color.b, a: 0 | color.a};
743         var a = cc.__v2f(from), b = cc.__v2f(to);
744         var n = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(b, a))), t = cc.v2fperp(n);
745         var nw = cc.v2fmult(n, radius), tw = cc.v2fmult(t, radius);
746 
747         var v0 = cc.v2fsub(b, cc.v2fadd(nw, tw));
748         var v1 = cc.v2fadd(b, cc.v2fsub(nw, tw));
749         var v2 = cc.v2fsub(b, nw);
750         var v3 = cc.v2fadd(b, nw);
751         var v4 = cc.v2fsub(a, nw);
752         var v5 = cc.v2fadd(a, nw);
753         var v6 = cc.v2fsub(a, cc.v2fsub(nw, tw));
754         var v7 = cc.v2fadd(a, cc.v2fadd(nw, tw));
755 
756         var TriangleLength = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT, triangleBuffer = this._trianglesArrayBuffer, locBuffer = this._buffer;
757         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v0, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(cc.v2fadd(n, t)))},
758             {vertices: v1, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(n, t))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))},
759             triangleBuffer, locBuffer.length * TriangleLength));
760 
761         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)},
762             {vertices: v1, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(n, t))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))},
763             triangleBuffer, locBuffer.length * TriangleLength));
764 
765         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)},
766             {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))},
767             triangleBuffer, locBuffer.length * TriangleLength));
768 
769         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)},
770             {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)},
771             triangleBuffer, locBuffer.length * TriangleLength));
772 
773         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v6, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(t, n))},
774             {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)},
775             triangleBuffer, locBuffer.length * TriangleLength));
776 
777         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v6, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(t, n))},
778             {vertices: v7, colors: c4bColor, texCoords: cc.__t(cc.v2fadd(n, t))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)},
779             triangleBuffer, locBuffer.length * TriangleLength));
780         this._dirty = true;
781     },
782 
783     drawPoly:function (verts, fillColor, borderWidth, borderColor) {
784         if(fillColor == null){
785             this._drawSegments(verts, borderWidth, borderColor, true);
786             return;
787         }
788         if (fillColor.a == null)
789             fillColor.a = 255;
790         if (borderColor.a == null)
791             borderColor.a = 255;
792         borderWidth = (borderWidth == null)? this._lineWidth : borderWidth;
793         borderWidth *= 0.5;
794         var c4bFillColor = {r: 0 | fillColor.r, g: 0 | fillColor.g, b: 0 | fillColor.b, a: 0 | fillColor.a};
795         var c4bBorderColor = {r: 0 | borderColor.r, g: 0 | borderColor.g, b: 0 | borderColor.b, a: 0 | borderColor.a};
796         var extrude = [], i, v0, v1, v2, count = verts.length;
797         for (i = 0; i < count; i++) {
798             v0 = cc.__v2f(verts[(i - 1 + count) % count]);
799             v1 = cc.__v2f(verts[i]);
800             v2 = cc.__v2f(verts[(i + 1) % count]);
801             var n1 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v1, v0)));
802             var n2 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v2, v1)));
803             var offset = cc.v2fmult(cc.v2fadd(n1, n2), 1.0 / (cc.v2fdot(n1, n2) + 1.0));
804             extrude[i] = {offset: offset, n: n2};
805         }
806         var outline = (borderWidth > 0.0), triangleCount = 3 * count - 2, vertexCount = 3 * triangleCount;
807         this._ensureCapacity(vertexCount);
808 
809         var triangleBytesLen = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT, trianglesBuffer = this._trianglesArrayBuffer;
810         var locBuffer = this._buffer;
811         var inset = (outline == false ? 0.5 : 0.0);
812         for (i = 0; i < count - 2; i++) {
813             v0 = cc.v2fsub(cc.__v2f(verts[0]), cc.v2fmult(extrude[0].offset, inset));
814             v1 = cc.v2fsub(cc.__v2f(verts[i + 1]), cc.v2fmult(extrude[i + 1].offset, inset));
815             v2 = cc.v2fsub(cc.__v2f(verts[i + 2]), cc.v2fmult(extrude[i + 2].offset, inset));
816             locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())},
817                 {vertices: v1, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, {vertices: v2, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())},
818                 trianglesBuffer, locBuffer.length * triangleBytesLen));
819         }
820 
821         for (i = 0; i < count; i++) {
822             var j = (i + 1) % count;
823             v0 = cc.__v2f(verts[i]);
824             v1 = cc.__v2f(verts[j]);
825 
826             var n0 = extrude[i].n;
827             var offset0 = extrude[i].offset;
828             var offset1 = extrude[j].offset;
829             var inner0 = outline ? cc.v2fsub(v0, cc.v2fmult(offset0, borderWidth)) : cc.v2fsub(v0, cc.v2fmult(offset0, 0.5));
830             var inner1 = outline ? cc.v2fsub(v1, cc.v2fmult(offset1, borderWidth)) : cc.v2fsub(v1, cc.v2fmult(offset1, 0.5));
831             var outer0 = outline ? cc.v2fadd(v0, cc.v2fmult(offset0, borderWidth)) : cc.v2fadd(v0, cc.v2fmult(offset0, 0.5));
832             var outer1 = outline ? cc.v2fadd(v1, cc.v2fmult(offset1, borderWidth)) : cc.v2fadd(v1, cc.v2fmult(offset1, 0.5));
833 
834             if (outline) {
835                 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))},
836                     {vertices: inner1, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)},
837                     trianglesBuffer, locBuffer.length * triangleBytesLen));
838                 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))},
839                     {vertices: outer0, colors: c4bBorderColor, texCoords: cc.__t(n0)}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)},
840                     trianglesBuffer, locBuffer.length * triangleBytesLen));
841             } else {
842                 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())},
843                     {vertices: inner1, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, {vertices: outer1, colors: c4bFillColor, texCoords: cc.__t(n0)},
844                     trianglesBuffer, locBuffer.length * triangleBytesLen));
845                 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())},
846                     {vertices: outer0, colors: c4bFillColor, texCoords: cc.__t(n0)}, {vertices: outer1, colors: c4bFillColor, texCoords: cc.__t(n0)},
847                     trianglesBuffer, locBuffer.length * triangleBytesLen));
848             }
849         }
850         extrude = null;
851         this._dirty = true;
852     },
853 
854     _drawSegments: function(verts, borderWidth, borderColor, closePoly){
855         borderWidth = (borderWidth == null) ? this._lineWidth : borderWidth;
856         borderColor = borderColor || this._drawColor;
857         if(borderColor.a == null)
858             borderColor.a = 255;
859         borderWidth *= 0.5;
860         if (borderWidth <= 0)
861             return;
862 
863         var c4bBorderColor = {r: 0 | borderColor.r, g: 0 | borderColor.g, b: 0 | borderColor.b, a: 0 | borderColor.a };
864         var extrude = [], i, v0, v1, v2, count = verts.length;
865         for (i = 0; i < count; i++) {
866             v0 = cc.__v2f(verts[(i - 1 + count) % count]);
867             v1 = cc.__v2f(verts[i]);
868             v2 = cc.__v2f(verts[(i + 1) % count]);
869             var n1 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v1, v0)));
870             var n2 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v2, v1)));
871             var offset = cc.v2fmult(cc.v2fadd(n1, n2), 1.0 / (cc.v2fdot(n1, n2) + 1.0));
872             extrude[i] = {offset: offset, n: n2};
873         }
874 
875         var triangleCount = 3 * count - 2, vertexCount = 3 * triangleCount;
876         this._ensureCapacity(vertexCount);
877 
878         var triangleBytesLen = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT, trianglesBuffer = this._trianglesArrayBuffer;
879         var locBuffer = this._buffer;
880         var len = closePoly ? count : count - 1;
881         for (i = 0; i < len; i++) {
882             var j = (i + 1) % count;
883             v0 = cc.__v2f(verts[i]);
884             v1 = cc.__v2f(verts[j]);
885 
886             var n0 = extrude[i].n;
887             var offset0 = extrude[i].offset;
888             var offset1 = extrude[j].offset;
889             var inner0 = cc.v2fsub(v0, cc.v2fmult(offset0, borderWidth));
890             var inner1 = cc.v2fsub(v1, cc.v2fmult(offset1, borderWidth));
891             var outer0 = cc.v2fadd(v0, cc.v2fmult(offset0, borderWidth));
892             var outer1 = cc.v2fadd(v1, cc.v2fmult(offset1, borderWidth));
893             locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))},
894                 {vertices: inner1, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)},
895                 trianglesBuffer, locBuffer.length * triangleBytesLen));
896             locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))},
897                 {vertices: outer0, colors: c4bBorderColor, texCoords: cc.__t(n0)}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)},
898                 trianglesBuffer, locBuffer.length * triangleBytesLen));
899         }
900         extrude = null;
901         this._dirty = true;
902     },
903 
904     clear:function () {
905         this._buffer.length = 0;
906         this._dirty = true;
907     },
908 
909     _createRenderCmd: function () {
910         return new cc.DrawNode.WebGLRenderCmd(this);
911     }
912 });
913 
914 cc.DrawNode = cc._renderType === cc._RENDER_TYPE_WEBGL ? cc.DrawNodeWebGL : cc.DrawNodeCanvas;
915 
916 /**
917  * Creates a DrawNode
918  * @deprecated since v3.0 please use new cc.DrawNode() instead.
919  * @return {cc.DrawNode}
920  */
921 cc.DrawNode.create = function () {
922     return new cc.DrawNode();
923 };
924 
925 cc._DrawNodeElement = function (type, verts, fillColor, lineWidth, lineColor, lineCap, isClosePolygon, isFill, isStroke) {
926     var _t = this;
927     _t.type = type;
928     _t.verts = verts || null;
929     _t.fillColor = fillColor || null;
930     _t.lineWidth = lineWidth || 0;
931     _t.lineColor = lineColor || null;
932     _t.lineCap = lineCap || "butt";
933     _t.isClosePolygon = isClosePolygon || false;
934     _t.isFill = isFill || false;
935     _t.isStroke = isStroke || false;
936 };
937 
938 cc.DrawNode.TYPE_DOT = 0;
939 cc.DrawNode.TYPE_SEGMENT = 1;
940 cc.DrawNode.TYPE_POLY = 2;
941