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