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