1 /**
  2  * @fileOverview
  3  * gl.enchant.js
  4  * @version 0.3.7
  5  * @require enchant.js v0.4.5+
  6  * @require gl-matrix.js 1.3.7+
  7  * @author UEI Corporation
  8  *
  9  * @description
 10  * gl-matrix.js:
 11  * https://github.com/toji/gl-matrix/
 12  */
 13 
 14 /**
 15  */
 16 enchant.gl = {};
 17 
 18 if (typeof glMatrixArrayType === 'undefined') {
 19     throw new Error('should load gl-matrix.js before loading gl.enchant.js');
 20 }
 21 
 22 (function() {
 23 
 24     var CONTEXT_NAME = 'experimental-webgl';
 25 
 26     var parentModule = null;
 27     (function() {
 28         enchant();
 29         if (enchant.nineleap !== undefined) {
 30             if (enchant.nineleap.memory !== undefined &&
 31                 Object.getPrototypeOf(enchant.nineleap.memory) === Object.prototype) {
 32                 parentModule = enchant.nineleap.memory;
 33             } else if (enchant.nineleap !== undefined &&
 34                 Object.getPrototypeOf(enchant.nineleap) === Object.prototype) {
 35                 parentModule = enchant.nineleap;
 36             }
 37         } else {
 38             parentModule = enchant;
 39         }
 40     }());
 41 
 42     enchant.gl.Core = enchant.Class.create(parentModule.Core, {
 43         initialize: function(width, height) {
 44             parentModule.Core.call(this, width, height);
 45             this.GL = new GLUtil();
 46             this.currentScene3D = null;
 47             this.addEventListener('enterframe', function(e) {
 48                 if (!this.currentScene3D) {
 49                     return;
 50                 }
 51                 var nodes = this.currentScene3D.childNodes.slice();
 52                 var push = Array.prototype.push;
 53                 while (nodes.length) {
 54                     var node = nodes.pop();
 55                     node.dispatchEvent(e);
 56                     node.age++;
 57                     if (node.childNodes) {
 58                         push.apply(nodes, node.childNodes);
 59                     }
 60                 }
 61             });
 62         },
 63         debug: function() {
 64             this.GL._enableDebugContext();
 65             this._debug = true;
 66             this.addEventListener("enterframe", function(time) {
 67                 this._actualFps = (1000 / time.elapsed);
 68             });
 69             this.start();
 70         }
 71     });
 72 
 73     var GLUtil = enchant.Class.create({
 74         initialize: function() {
 75             var core = enchant.Core.instance;
 76             if (typeof core.GL !== 'undefined') {
 77                 return core.GL;
 78             }
 79             this._createStage(core.width, core.height, core.scale);
 80             this._prepare();
 81             this.textureManager = new TextureManager();
 82             this.detectColorManager = new DetectColorManager();
 83             this.detectFrameBuffer = new enchant.gl.FrameBuffer(core.width, core.height);
 84             this.defaultProgram = new enchant.gl.Shader(DEFAULT_VERTEX_SHADER_SOURCE, DEFAULT_FRAGMENT_SHADER_SOURCE);
 85             this.setDefaultProgram();
 86         },
 87         setDefaultProgram: function() {
 88             this.setProgram(this.defaultProgram);
 89         },
 90         setProgram: function(program) {
 91             program.use();
 92             this.currentProgram = program;
 93         },
 94         _prepare: function() {
 95             var width = this._canvas.width;
 96             var height = this._canvas.height;
 97             gl.clearColor(0.0, 0.0, 0.0, 1.0);
 98             gl.viewport(0, 0, width, height);
 99             gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
100             gl.enable(gl.DEPTH_TEST);
101             gl.enable(gl.CULL_FACE);
102         },
103         _createStage: function(width, height, scale) {
104             var div = createParentDiv();
105             var that = this;
106             var stage = document.getElementById('enchant-stage');
107             var cvs = this._canvas = createGLCanvas(width, height, scale);
108             var detect = new enchant.Sprite(width, height);
109             var core = enchant.Core.instance;
110             (function() {
111                 var color = new Uint8Array(4);
112                 var touching = null;
113                 var sprite;
114                 detect.addEventListener('touchstart', function(e) {
115                     var scene = core.currentScene3D;
116                     var x = parseInt(e.x, 10);
117                     var y = parseInt(this.height - e.y, 10);
118                     that.detectFrameBuffer.bind();
119                     scene._draw('detect');
120                     gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, color);
121                     sprite = that.detectColorManager.getSpriteByColor(color);
122                     if (sprite) {
123                         touching = sprite;
124                         touching.dispatchEvent(e);
125                     }
126                     that.detectFrameBuffer.unbind();
127                 });
128                 detect.addEventListener('touchmove', function(e) {
129                     if (touching !== null) {
130                         touching.dispatchEvent(e);
131                     }
132                 });
133                 detect.addEventListener('touchend', function(e) {
134                     if (touching !== null) {
135                         touching.dispatchEvent(e);
136                     }
137                     touching = null;
138                 });
139             }());
140             window['gl'] = this._gl = this._getContext(cvs);
141             div.appendChild(cvs);
142             stage.insertBefore(div, core.rootScene._element);
143             core.rootScene.addChild(detect);
144         },
145         _getContext: function(canvas, debug) {
146             var ctx = canvas.getContext(CONTEXT_NAME);
147             if (!ctx) {
148                 window['alert']('could not initialized WebGL');
149                 throw new Error('could not initialized WebGL');
150             }
151             if (debug) {
152                 ctx = createDebugContext(ctx);
153             }
154             return ctx;
155         },
156         _enableDebugContext: function() {
157             window['gl'] = this._gl = createDebugContext(this._gl);
158         },
159         parseColor: function(string) {
160             return parseColor(string);
161         },
162         renderElements: function(buffer, offset, length, attributes, uniforms) {
163             if (attributes) {
164                 this.currentProgram.setAttributes(attributes);
165             }
166             if (uniforms) {
167                 this.currentProgram.setUniforms(uniforms);
168             }
169             buffer.bind();
170             gl.drawElements(gl.TRIANGLES, length, gl.UNSIGNED_SHORT, offset);
171             buffer.unbind();
172         }
173     });
174 
175     var parseColor = function(string) {
176         var color = [];
177         if (typeof string === 'string') {
178             if (string.match(/#/)) {
179                 string.match(/[0-9a-fA-F]{2}/g).forEach(function(n) {
180                     color[color.length] = ('0x' + n - 0) / 255;
181                 });
182                 color[color.length] = 1.0;
183             } else if (string.match(/rgba/)) {
184                 string.match(/[0-9]{1,3},/g).forEach(function(n) {
185                     color[color.length] = parseInt(n, 10) / 255;
186                 });
187                 color[color.length] = parseFloat(string.match(/[0-9]\.[0-9]{1,}/)[0]);
188             } else if (string.match(/rgb/)) {
189                 string.match(/[0-9]{1,3},/g).forEach(function(n) {
190                     color[color.length] = parseInt(n, 10) / 255;
191                 });
192                 color[color.length] = 1.0;
193             }
194         } else if (string instanceof Array) {
195             color = string;
196         }
197         return color;
198     };
199 
200     var createDebugContext = function(context) {
201         var ctx = {};
202         var names = {};
203         var type = '';
204         var val;
205         var makeFakedMethod = function(context, prop) {
206             return function() {
207                 var value, error;
208                 value = context[prop].apply(context, arguments);
209                 error = context.getError();
210                 if (error) {
211                     window['console'].log(names[error] + '(' + error + ')' + ': ' + prop);
212                     window['console'].log(arguments);
213                 }
214                 return value;
215             };
216         };
217         for (var prop in context) {
218             type = typeof context[prop];
219             val = context[prop];
220             if (type === 'function') {
221                 ctx[prop] = makeFakedMethod(context, prop);
222             } else if (type === 'number') {
223                 names[val] = prop;
224                 ctx[prop] = val;
225             } else {
226                 ctx[prop] = val;
227             }
228         }
229         ctx.getNameById = function(i) {
230             return names[i];
231         };
232         return ctx;
233     };
234 
235     var createParentDiv = function() {
236         var div = document.createElement('div');
237         div.style['position'] = 'absolute';
238         div.style['z-index'] = -1;
239         div.style[enchant.ENV.VENDOR_PREFIX + 'TransformOrigin'] = '0 0';
240         return div;
241     };
242 
243     var createGLCanvas = function(width, height, scale) {
244         var cvs = document.createElement('canvas');
245         cvs.width = width;
246         cvs.height = height;
247         cvs.style['position'] = 'absolute';
248         cvs.style['z-index'] = -1;
249         cvs.style[enchant.ENV.VENDOR_PREFIX + 'Transform'] = 'scale(' + scale + ')';
250         cvs.style[enchant.ENV.VENDOR_PREFIX + 'TransformOrigin'] = '0 0';
251         return cvs;
252     };
253 
254     var TextureManager = enchant.Class.create({
255         initialize: function() {
256             this.storage = {};
257         },
258         hasTexture: function(src) {
259             return src in this.storage;
260         },
261         getWebGLTexture: function(image, flip, wrap, mipmap) {
262             var ret;
263             if (this.hasTexture(image.src)) {
264                 ret = this.storage[image.src];
265             } else {
266                 ret = this.createWebGLTexture(image, flip, wrap, mipmap);
267             }
268             return ret;
269         },
270         isPowerOfTwo: function(n) {
271             return (n > 0) && ((n & (n - 1)) === 0);
272         },
273         setTextureParameter: function(power, target, wrap, mipmap) {
274             var filter;
275             if (mipmap) {
276                 filter = gl.LINEAR_MIPMAP_LINEAR;
277             } else {
278                 filter = gl.NEAREST;
279             }
280             if (!power) {
281                 wrap = gl.CLAMP_TO_EDGE;
282             }
283             gl.texParameteri(target, gl.TEXTURE_WRAP_S, wrap);
284             gl.texParameteri(target, gl.TEXTURE_WRAP_T, wrap);
285             gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, filter);
286             gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, filter);
287         },
288         _texImage: function(image, target) {
289             gl.texImage2D(target, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
290         },
291         _writeWebGLTexture: function(image, target, wrap, mipmap) {
292             var power = this.isPowerOfTwo(image.width) && this.isPowerOfTwo(image.height);
293             if (typeof target === 'undefined') {
294                 target = gl.TEXTURE_2D;
295             }
296             if (typeof wrap === 'undefined') {
297                 wrap = gl.REPEAT;
298             }
299             this.setTextureParameter(power, target, wrap, mipmap);
300 
301             this._texImage(image, target);
302             if (mipmap) {
303                 gl.generateMipmap(target);
304             }
305         },
306         createWebGLTexture: function(image, flip, wrap, mipmap) {
307             var tex = gl.createTexture();
308             var target = gl.TEXTURE_2D;
309             gl.bindTexture(target, tex);
310             gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flip);
311             this._writeWebGLTexture(image, target, wrap, mipmap);
312             gl.bindTexture(target, null);
313             this.storage[image.src] = tex;
314             return tex;
315         },
316         createWebGLCubeMapTexture: function(images, wrap, mipmap) {
317             var faceTargets = [
318                 gl.TEXTURE_CUBE_MAP_POSITIVE_X,
319                 gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
320                 gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
321                 gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
322                 gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,
323                 gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
324             ];
325 
326             var tex = gl.createTexture();
327             var target, image;
328             gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex);
329             gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
330             for (var i = 0, l = images.length; i < l; i++) {
331                 target = faceTargets[i];
332                 image = images[i];
333                 this._writeWebGLTexture(image, target, wrap, mipmap);
334             }
335             gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
336             return tex;
337         }
338     });
339 
340     var DetectColorManager = enchant.Class.create({
341         initialize: function() {
342             this.reference = [];
343             this.detectColorNum = 0;
344         },
345         attachDetectColor: function(sprite) {
346             this.detectColorNum += 1;
347             this.reference[this.detectColorNum] = sprite;
348             return this._createNewColor();
349         },
350         _createNewColor: function() {
351             var n = this.detectColorNum;
352             return [
353                 parseInt(n / 65536, 10) / 255,
354                 parseInt(n / 256, 10) / 255,
355                 parseInt(n % 256, 10) / 255, 1.0
356             ];
357         },
358         _decodeDetectColor: function(color) {
359             return Math.floor(color[0] * 65536) +
360                 Math.floor(color[1] * 256) +
361                 Math.floor(color[2]);
362         },
363         getSpriteByColor: function(color) {
364             return this.reference[this._decodeDetectColor(color)];
365         }
366     });
367 
368     /**
369      * @scope enchant.gl.Framebuffer.prototype
370      */
371     enchant.gl.FrameBuffer = enchant.Class.create({
372         /**
373          */
374         initialize: function(width, height) {
375             var core = enchant.Core.instance;
376             if (typeof width === 'undefined') {
377                 width = core.width;
378             }
379             if (typeof height === 'undefined') {
380                 height = core.height;
381             }
382             this.framebuffer = gl.createFramebuffer();
383             this.colorbuffer = gl.createRenderbuffer();
384             this.depthbuffer = gl.createRenderbuffer();
385 
386             gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
387 
388             gl.bindRenderbuffer(gl.RENDERBUFFER, this.colorbuffer);
389             gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, width, height);
390             gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, this.colorbuffer);
391 
392             gl.bindRenderbuffer(gl.RENDERBUFFER, this.depthbuffer);
393             gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
394             gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.depthbuffer);
395 
396             gl.bindFramebuffer(gl.FRAMEBUFFER, null);
397             gl.bindRenderbuffer(gl.RENDERBUFFER, null);
398         },
399         /**
400          */
401         bind: function() {
402             gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
403         },
404         /**
405          */
406         unbind: function() {
407             gl.bindFramebuffer(gl.FRAMEBUFFER, null);
408         },
409         /**
410          */
411         destroy: function() {
412             gl.deleteFramebuffer(this.framebuffer);
413             gl.deleteFramebuffer(this.colorbuffer);
414             gl.deleteFramebuffer(this.depthbuffer);
415         }
416     });
417 
418     /**
419      * @scope enchant.gl.Shader.prototype
420      */
421     enchant.gl.Shader = enchant.Class.create({
422         /**
423          */
424         initialize: function(vshader, fshader) {
425             this._vShaderSource = '';
426             this._fShaderSource = '';
427             this._updatedVShaderSource = false;
428             this._updatedFShaderSource = false;
429             this._vShaderProgram = null;
430             this._fShaderProgram = null;
431             this._program = null;
432             this._uniforms = {};
433             this._attributes = {};
434             this._attribLocs = {};
435             this._samplersNum = 0;
436 
437             if (typeof vshader === 'string') {
438                 this.vShaderSource = vshader;
439             }
440             if (typeof fshader === 'string') {
441                 this.fShaderSource = fshader;
442             }
443             if (this._updatedVShaderSource && this._updatedFShaderSource) {
444                 this.compile();
445             }
446         },
447         /**
448          */
449         vShaderSource: {
450             get: function() {
451                 return this._vShaderSource;
452             },
453             set: function(string) {
454                 this._vShaderSource = string;
455                 this._updatedVShaderSource = true;
456             }
457         },
458         /**
459          */
460         fShaderSource: {
461             get: function() {
462                 return this._fShaderSource;
463             },
464             set: function(string) {
465                 this._fShaderSource = string;
466                 this._updatedFShaderSource = true;
467             }
468         },
469         /**
470          */
471         compile: function() {
472             if (this._updatedVShaderSource) {
473                 this._prepareVShader();
474             }
475             if (this._updatedFShaderSource) {
476                 this._prepareFShader();
477             }
478             if (this._program === null) {
479                 this._program = gl.createProgram();
480             } else {
481                 gl.detachShader(this._program, this._vShaderProgram);
482                 gl.detachShader(this._program, this._fShaderProgram);
483             }
484             gl.attachShader(this._program, this._vShaderProgram);
485             gl.attachShader(this._program, this._fShaderProgram);
486             gl.linkProgram(this._program);
487             if (!gl.getProgramParameter(this._program, gl.LINK_STATUS)) {
488                 this._logShadersInfo();
489                 throw 'could not compile shader';
490             }
491             this._getAttributesProperties();
492             this._getUniformsProperties();
493         },
494         /**
495          */
496         use: function() {
497             gl.useProgram(this._program);
498         },
499         /**
500          */
501         setAttributes: function(params) {
502             for (var prop in params) {
503                 if (params.hasOwnProperty(prop)) {
504                     this._attributes[prop] = params[prop];
505                 }
506             }
507         },
508         /**
509          */
510         setUniforms: function(params) {
511             for (var prop in params) {
512                 if (params.hasOwnProperty(prop)) {
513                     this._uniforms[prop] = params[prop];
514                 }
515             }
516         },
517         _prepareVShader: function() {
518             if (this._vShaderProgram === null) {
519                 this._vShaderProgram = gl.createShader(gl.VERTEX_SHADER);
520             }
521             gl.shaderSource(this._vShaderProgram, this._vShaderSource);
522             gl.compileShader(this._vShaderProgram);
523             this._updatedVShaderSource = false;
524         },
525         _prepareFShader: function() {
526             if (this._fShaderProgram === null) {
527                 this._fShaderProgram = gl.createShader(gl.FRAGMENT_SHADER);
528             }
529             gl.shaderSource(this._fShaderProgram, this._fShaderSource);
530             gl.compileShader(this._fShaderProgram);
531             this._updatedFShaderSource = false;
532         },
533         _logShadersInfo: function() {
534             window['console'].log(gl.getShaderInfoLog(this._vShaderProgram));
535             window['console'].log(gl.getShaderInfoLog(this._fShaderProgram));
536         },
537         _getAttributesProperties: function() {
538             var n;
539             n = gl.getProgramParameter(this._program, gl.ACTIVE_ATTRIBUTES);
540             for (var i = 0; i < n; i++) {
541                 var info = gl.getActiveAttrib(this._program, i);
542                 this._attribLocs[info.name] = i;
543                 addAttributesProperty(this, info);
544             }
545         },
546         _getUniformsProperties: function() {
547             var n;
548             n = gl.getProgramParameter(this._program, gl.ACTIVE_UNIFORMS);
549             for (var i = 0; i < n; i++) {
550                 var info = gl.getActiveUniform(this._program, i);
551                 addUniformsProperty(this, info);
552             }
553         },
554         /**
555          */
556         destroy: function() {
557             gl.deleteProgram(this._vShaderProgram);
558             gl.deleteProgram(this._fShaderProgram);
559             gl.deleteProgram(this._program);
560         }
561     });
562 
563     var addAttributesProperty = function(program, info) {
564         var name = info.name;
565         var loc = program._attribLocs[name];
566         /**
567          * @type {Object}
568          * @memberOf Object.
569          */
570         var desc = {
571             get: function() {
572                 return 'attrib';
573             },
574             set: (function(loc) {
575                 return function(buf) {
576                     gl.enableVertexAttribArray(loc);
577                     buf._setToAttrib(loc);
578                 };
579             }(loc))
580         };
581         Object.defineProperty(program._attributes, name, desc);
582     };
583 
584     var addUniformsProperty = function(program, info) {
585         var name = (info.name.slice(-3) === '[0]') ? info.name.slice(0, -3) : info.name;
586         var loc = gl.getUniformLocation(program._program, info.name);
587         var suffix;
588         var sampler = false;
589         var matrix = false;
590         /**
591          * @type {Object}
592          */
593         var desc = {
594             get: function() {
595                 return 'uniform';
596             }
597         };
598         switch (info.type) {
599             case gl.FLOAT:
600                 suffix = '1f';
601                 break;
602 
603             case gl.FLOAT_MAT2:
604                 matrix = true;
605                 /* falls through */
606             case gl.FLOAT_VEC2:
607                 suffix = '2fv';
608                 break;
609 
610             case gl.FLOAT_MAT3:
611                 matrix = true;
612                 /* falls through */
613             case gl.FLOAT_VEC3:
614                 suffix = '3fv';
615                 break;
616 
617             case gl.FLOAT_MAT4:
618                 matrix = true;
619                 /* falls through */
620             case gl.FLOAT_VEC4:
621                 suffix = '4fv';
622                 break;
623 
624             case gl.SAMPLER_2D:
625             case gl.SAMPLER_CUBE:
626                 sampler = true;
627                 /* falls through */
628             case gl.INT:
629             case gl.BOOL:
630                 suffix = '1i';
631                 break;
632 
633             case gl.INT_VEC2:
634             case gl.BOOL_VEC2:
635                 suffix = '2iv';
636                 break;
637 
638             case gl.INT_VEC3:
639             case gl.BOOL_VEC3:
640                 suffix = '3iv';
641                 break;
642 
643             case gl.INT_VEC4:
644             case gl.BOOL_VEC4:
645                 suffix = '4iv';
646                 break;
647             default:
648                 throw new Error('no match');
649         }
650         if (matrix) {
651             desc.set = (function(loc, suffix) {
652                 return function(value) {
653                     gl['uniformMatrix' + suffix](loc, false, value);
654                 };
655             }(loc, suffix));
656         } else if (sampler) {
657             desc.set = (function(loc, suffix, samplersNum) {
658                 return function(texture) {
659                     gl.activeTexture(gl.TEXTURE0 + samplersNum);
660                     gl.bindTexture(gl.TEXTURE_2D, texture._glTexture);
661                     gl['uniform' + suffix](loc, samplersNum);
662                 };
663             }(loc, suffix, program._samplersNum));
664             program._samplersNum++;
665         } else {
666             desc.set = (function(loc, suffix) {
667                 return function(value) {
668                     gl['uniform' + suffix](loc, value);
669                 };
670             }(loc, suffix));
671         }
672         Object.defineProperty(program._uniforms, name, desc);
673     };
674 
675     /**
676      * @scope enchant.gl.Quat.prototype
677      */
678     enchant.gl.Quat = enchant.Class.create({
679         /**
680          */
681         initialize: function(x, y, z, rad) {
682             var l = Math.sqrt(x * x + y * y + z * z);
683             if (l) {
684                 x /= l;
685                 y /= l;
686                 z /= l;
687             }
688             var s = Math.sin(rad / 2);
689             var w = Math.cos(rad / 2);
690             this._quat = quat4.create([x * s, y * s, z * s, w]);
691         },
692 
693         /**
694          */
695         slerp: function(another, ratio) {
696             var q = new enchant.gl.Quat(0, 0, 0, 0);
697             quat4.slerp(this._quat, another._quat, ratio, q._quat);
698             return q;
699         },
700         /**
701          */
702         slerpApply: function(another, ratio) {
703             quat4.slerp(this._quat, another._quat, ratio);
704             return this;
705         },
706         /**
707          */
708         toMat4: function(matrix) {
709             quat4.toMat4(this._quat, matrix);
710             return matrix;
711         },
712         /**
713          */
714         multiplyVec3: function(vector) {
715             quat4.multiplyVec3(this._quat, vector);
716             return vector;
717         }
718     });
719 
720     /**
721      * @scope enchant.gl.Light3D.prototype
722      */
723     enchant.gl.Light3D = enchant.Class.create(enchant.EventTarget, {
724         /**
725          */
726         initialize: function() {
727             this._changedColor = true;
728             this._color = [0.8, 0.8, 0.8];
729         },
730 
731         /**
732          */
733         color: {
734             set: function(array) {
735                 this._color = array;
736                 this._changedColor = true;
737             },
738             get: function() {
739                 return this._color;
740             }
741         }
742     });
743 
744     /**
745      * @scope enchant.gl.AmbientLight.prototype
746      */
747     enchant.gl.AmbientLight = enchant.Class.create(enchant.gl.Light3D, {
748         /**
749          */
750         initialize: function() {
751             enchant.gl.Light3D.call(this);
752         }
753     });
754 
755     /**
756      * @scope enchant.gl.DirectionalLight.prototype
757      */
758     enchant.gl.DirectionalLight = enchant.Class.create(enchant.gl.Light3D, {
759         /**
760          */
761         initialize: function() {
762             enchant.gl.Light3D.call(this);
763             this._directionX = 0.5;
764             this._directionY = 0.5;
765             this._directionZ = 1.0;
766             this._changedDirection = true;
767         }
768     });
769 
770     /**
771      */
772     enchant.gl.DirectionalLight.prototype.directionX = 0.5;
773 
774     /**
775      */
776     enchant.gl.DirectionalLight.prototype.directionY = 0.5;
777 
778     /**
779      */
780     enchant.gl.DirectionalLight.prototype.directionZ = 1.0;
781 
782     /**
783      */
784     'directionX directionY directionZ'.split(' ').forEach(function(prop) {
785         Object.defineProperty(enchant.gl.DirectionalLight.prototype, prop, {
786             get: function() {
787                 return this['_' + prop];
788             },
789             set: function(n) {
790                 this['_' + prop] = n;
791                 this._changedDirection = true;
792             }
793         });
794         enchant.gl.DirectionalLight.prototype[prop] = 0;
795     });
796 
797     /**
798      * @scope enchant.gl.PointLight.prototype
799      */
800     enchant.gl.PointLight = enchant.Class.create(enchant.gl.Light3D, {
801         /**
802          */
803         initialize: function() {
804             enchant.gl.Light3D.call(this);
805             this._x = 0;
806             this._y = 0;
807             this._z = 0;
808             this._changedPosition = true;
809         }
810     });
811 
812     /**
813      */
814     enchant.gl.PointLight.prototype.x = 0;
815 
816     /**
817      */
818     enchant.gl.PointLight.prototype.y = 0;
819 
820     /**
821      */
822     enchant.gl.PointLight.prototype.z = 0;
823 
824     'x y z'.split(' ').forEach(function(prop) {
825         Object.defineProperty(enchant.gl.PointLight.prototype, prop, {
826             get: function() {
827                 return this['_' + prop];
828             },
829             set: function(n) {
830                 this['_' + prop] = n;
831                 this._changedPosition = true;
832             }
833         });
834         enchant.gl.PointLight.prototype[prop] = 0;
835     });
836 
837 
838     /**
839      * @scope enchant.gl.Texture.prototype
840      */
841     enchant.gl.Texture = enchant.Class.create({
842         /**
843          */
844         initialize: function(src, opt) {
845             /**
846              */
847             this.ambient = [ 0.1, 0.1, 0.1, 1.0 ];
848 
849             /**
850              */
851             this.diffuse = [ 1.0, 1.0, 1.0, 1.0];
852 
853             /**
854              */
855             this.specular = [ 1.0, 1.0, 1.0, 1.0 ];
856 
857             /**
858              */
859             this.emission = [ 0.0, 0.0, 0.0, 1.0 ];
860 
861             /**
862              */
863             this.shininess = 20;
864 
865             this._glTexture = null;
866             this._image = null;
867             this._wrap = 10497;
868             this._mipmap = false;
869             this._flipY = true;
870             if (opt) {
871                 var valid = ['flipY', 'wrap', 'mipmap'];
872                 for (var prop in opt) {
873                     if (opt.hasOwnProperty(prop)) {
874                         if (valid.indexOf(prop) !== -1) {
875                             this['_' + prop] = opt[prop];
876                         }
877                     }
878                 }
879             }
880             if (src) {
881                 this.src = src;
882             }
883         },
884 
885         _write: function() {
886             gl.bindTexture(gl.TEXTURE_2D, this._glTexture);
887             enchant.Core.instance.GL.textureManager._writeWebGLTexture(this._image, gl.TEXTURE_2D, this._wrap, this._mipmap);
888             gl.bindTexture(gl.TEXTURE_2D, null);
889         },
890 
891         /**
892          */
893         src: {
894             get: function() {
895                 return this._src;
896             },
897             set: function(source) {
898                 if (typeof source === 'undefined' ||
899                     source === null) {
900                     return;
901                 }
902                 var that = this;
903                 var core = enchant.Core.instance;
904                 var onload = (function(that) {
905                     return function() {
906                         that._glTexture = core.GL.textureManager.getWebGLTexture(that._image, that._flipY, that._wrap, that._mipmap);
907                     };
908                 }(that));
909                 if (source instanceof Image) {
910                     this._image = source;
911                     onload();
912                 } else if (source instanceof enchant.Surface) {
913                     this._image = source._element;
914                     onload();
915                 } else if (typeof source === 'string') {
916                     this._image = new Image();
917                     this._image.onload = onload;
918                     this._image.src = source;
919                 } else {
920                     this._image = source;
921                     this._image.src = "c" + Math.random();
922                     onload();
923                 }
924             }
925         }
926     });
927 
928     /**
929      * @scope enchant.gl.Buffer.prototype
930      */
931     enchant.gl.Buffer = enchant.Class.create({
932         /**
933          */
934         initialize: function(params, array) {
935             this._setParams(params);
936             if (typeof array !== 'undefined') {
937                 this._array = array;
938             } else {
939                 this._array = [];
940             }
941             this._buffer = null;
942         },
943         /**
944          */
945         bind: function() {
946             gl.bindBuffer(this.btype, this._buffer);
947         },
948         /**
949          */
950         unbind: function() {
951             gl.bindBuffer(this.btype, null);
952         },
953         _setParams: function(params) {
954             for (var prop in params) {
955                 if (params.hasOwnProperty(prop)) {
956                     this[prop] = params[prop];
957                 }
958             }
959         },
960         _create: function() {
961             this._buffer = gl.createBuffer();
962         },
963         _delete: function() {
964             gl.deleteBuffer(this._buffer);
965         },
966         _bufferData: function() {
967             this.bind();
968             gl.bufferData(this.btype, new this.Atype(this._array), this.usage);
969             this.unbind();
970         },
971         _bufferDataFast: function() {
972             this.bind();
973             gl.bufferData(this.btype, this._array, this.usage);
974             this.unbind();
975         },
976         _setToAttrib: function(loc) {
977             this.bind();
978             gl.vertexAttribPointer(loc, this.size, this.type, this.norm, this.stride, this.offset);
979             this.unbind();
980         },
981         /**
982          */
983         destroy: function() {
984             this._delete();
985         }
986     });
987 
988     var bufferProto = Object.getPrototypeOf(enchant.gl.Buffer);
989     bufferProto.VERTICES = bufferProto.NORMALS = {
990         size: 3,
991         type: 5126,
992         norm: false,
993         stride: 0,
994         offset: 0,
995         btype: 34962,
996         usage: 35044,
997         Atype: Float32Array
998     };
999     bufferProto.TEXCOORDS = {
1000         size: 2,
1001         type: 5126,
1002         normed: false,
1003         stride: 0,
1004         ptr: 0,
1005         btype: 34962,
1006         usage: 35044,
1007         Atype: Float32Array
1008     };
1009     bufferProto.COLORS = {
1010         size: 4,
1011         type: 5126,
1012         normed: false,
1013         stride: 0,
1014         ptr: 0,
1015         btype: 34962,
1016         usage: 35044,
1017         Atype: Float32Array
1018     };
1019     bufferProto.INDICES = {
1020         size: 3,
1021         type: 5123,
1022         normed: false,
1023         stride: 0,
1024         offset: 0,
1025         btype: 34963,
1026         usage: 35044,
1027         Atype: Uint16Array
1028     };
1029 
1030     /**
1031      * @scope enchant.gl.Mesh.prototype
1032      */
1033     enchant.gl.Mesh = enchant.Class.create({
1034         /**
1035          */
1036         initialize: function() {
1037             this.__count = 0;
1038             this._appear = false;
1039             this._vertices = new enchant.gl.Buffer(enchant.gl.Buffer.VERTICES);
1040             this._normals = new enchant.gl.Buffer(enchant.gl.Buffer.NORMALS);
1041             this._colors = new enchant.gl.Buffer(enchant.gl.Buffer.COLORS);
1042             this._texCoords = new enchant.gl.Buffer(enchant.gl.Buffer.TEXCOORDS);
1043             this._indices = new enchant.gl.Buffer(enchant.gl.Buffer.INDICES);
1044             this.texture = new enchant.gl.Texture();
1045         },
1046         /**
1047          */
1048         setBaseColor: function(color) {
1049             var c = enchant.Core.instance.GL.parseColor(color);
1050             var newColors = [];
1051             for (var i = 0, l = this.vertices.length / 3; i < l; i++) {
1052                 Array.prototype.push.apply(newColors, c);
1053             }
1054             this.colors = newColors;
1055         },
1056         /**
1057          */
1058         reverse: function() {
1059             var norm = this.normals;
1060             var idx = this.indices;
1061             var t, i, l;
1062             for (i = 0, l = norm.length; i < l; i++) {
1063                 norm[i] *= -1;
1064             }
1065             for (i = 0, l = idx.length; i < l; i += 3) {
1066                 t = idx[i + 1];
1067                 idx[i + 1] = idx[i + 2];
1068                 idx[i + 2] = t;
1069             }
1070             this._normals._bufferData();
1071             this._indices._bufferData();
1072         },
1073         _createBuffer: function() {
1074             for (var prop in this) {
1075                 if (this.hasOwnProperty(prop)) {
1076                     if (this[prop] instanceof enchant.gl.Buffer) {
1077                         this[prop]._create();
1078                         this[prop]._bufferData();
1079                     }
1080                 }
1081             }
1082         },
1083         _deleteBuffer: function() {
1084             for (var prop in this) {
1085                 if (this.hasOwnProperty(prop)) {
1086                     if (this[prop] instanceof enchant.gl.Buffer) {
1087                         this[prop]._delete();
1088                     }
1089                 }
1090             }
1091         },
1092         _controlBuffer: function() {
1093             if (this._appear) {
1094                 if (this.__count <= 0) {
1095                     this._appear = false;
1096                     this._deleteBuffer();
1097                 }
1098             } else {
1099                 if (this.__count > 0) {
1100                     this._appear = true;
1101                     this._createBuffer();
1102                 }
1103             }
1104         },
1105         /**
1106          * @type {Number}
1107          */
1108         _count: {
1109             get: function() {
1110                 return this.__count;
1111             },
1112             set: function(c) {
1113                 this.__count = c;
1114                 this._controlBuffer();
1115             }
1116         },
1117         _join: function(another, ox, oy, oz) {
1118             var triangles = this.vertices.length / 3,
1119                 vertices = this.vertices.slice(0),
1120                 i, l;
1121             for (i = 0, l = another.vertices.length; i < l; i += 3) {
1122                 vertices.push(another.vertices[i] + ox);
1123                 vertices.push(another.vertices[i + 1] + oy);
1124                 vertices.push(another.vertices[i + 2] + oz);
1125             }
1126             this.vertices = vertices;
1127             this.normals = this.normals.concat(another.normals);
1128             this.texCoords = this.texCoords.concat(another.texCoords);
1129             this.colors = this.colors.concat(another.colors);
1130             var indices = this.indices.slice(0);
1131             for (i = 0, l = another.indices.length; i < l; i++) {
1132                 indices.push(another.indices[i] + triangles);
1133             }
1134             this.indices = indices;
1135         },
1136         /**
1137          */
1138         destroy: function() {
1139             this._deleteBuffer();
1140         }
1141     });
1142 
1143     /**
1144      */
1145     enchant.gl.Mesh.prototype.vertices = [];
1146 
1147     /**
1148      */
1149     enchant.gl.Mesh.prototype.normals = [];
1150 
1151     /**
1152      */
1153     enchant.gl.Mesh.prototype.texCoords = [];
1154 
1155     /**
1156      */
1157     enchant.gl.Mesh.prototype.indices = [];
1158 
1159     /**
1160      */
1161     enchant.gl.Mesh.prototype.colors = [];
1162 
1163     'vertices normals colors texCoords indices'.split(' ').forEach(function(prop) {
1164         Object.defineProperty(enchant.gl.Mesh.prototype, prop, {
1165             get: function() {
1166                 return this['_' + prop]._array;
1167             },
1168             set: function(array) {
1169                 this['_' + prop]._array = array;
1170                 if (this._appear) {
1171                     this['_' + prop]._bufferData();
1172                 }
1173             }
1174         });
1175     });
1176 
1177     /**
1178      * @scope enchant.gl.Sprite3D.prototype
1179      */
1180     enchant.gl.Sprite3D = enchant.Class.create(enchant.EventTarget, {
1181         /**
1182          * @constructs
1183          * @extends enchant.EventTarget
1184          */
1185         initialize: function() {
1186             enchant.EventTarget.call(this);
1187 
1188             /**
1189              */
1190             this.childNodes = [];
1191 
1192             /**
1193              */
1194             this.scene = null;
1195 
1196             /**
1197              */
1198             this.parentNode = null;
1199 
1200             /**
1201              */
1202             this._mesh = null;
1203 
1204             this.program = null;
1205 
1206             this.bounding = new enchant.gl.collision.BS();
1207             this.bounding.parent = this;
1208 
1209             this.age = 0;
1210 
1211             this._x = 0;
1212             this._y = 0;
1213             this._z = 0;
1214             this._scaleX = 1;
1215             this._scaleY = 1;
1216             this._scaleZ = 1;
1217             this._changedTranslation = true;
1218             this._changedRotation = true;
1219             this._changedScale = true;
1220             this._touchable = true;
1221 
1222             this._global = vec3.create();
1223             this.globalX = 0;
1224             this.globalY = 0;
1225             this.globalZ = 0;
1226 
1227             this._matrix = mat4.identity();
1228             this.tmpMat = mat4.identity();
1229             this.modelMat = mat4.identity();
1230             this._rotation = mat4.identity();
1231             this._normMat = mat3.identity();
1232 
1233             var core = enchant.Core.instance;
1234             this.detectColor = core.GL.detectColorManager.attachDetectColor(this);
1235 
1236             var parentEvent = function(e) {
1237                 if (this.parentNode instanceof enchant.gl.Sprite3D) {
1238                     this.parentNode.dispatchEvent(e);
1239                 }
1240             };
1241             this.addEventListener('touchstart', parentEvent);
1242             this.addEventListener('touchmove', parentEvent);
1243             this.addEventListener('touchend', parentEvent);
1244 
1245             var added = function(e) {
1246                 if (this.mesh !== null) {
1247                     this.mesh._count++;
1248                 }
1249                 if (this.childNodes.length) {
1250                     for (var i = 0, l = this.childNodes.length; i < l; i++) {
1251                         this.childNodes[i].scene = this.scene;
1252                         this.childNodes[i].dispatchEvent(e);
1253                     }
1254                 }
1255             };
1256             this.addEventListener('addedtoscene', added);
1257 
1258             var removed = function(e) {
1259                 if (this.mesh !== null) {
1260                     this.mesh._count--;
1261                 }
1262                 if (this.childNodes.length) {
1263                     for (var i = 0, l = this.childNodes.length; i < l; i++) {
1264                         this.childNodes[i].scene = null;
1265                         this.childNodes[i].dispatchEvent(e);
1266                     }
1267                 }
1268             };
1269             this.addEventListener('removedfromscene', removed);
1270 
1271         },
1272 
1273         /**
1274          */
1275         clone: function() {
1276             var clone = new enchant.gl.Sprite3D();
1277             for (var prop in this) {
1278                 if (typeof this[prop] === 'number' ||
1279                     typeof this[prop] === 'string') {
1280                     clone[prop] = this[prop];
1281                 } else if (this[prop] instanceof WebGLBuffer) {
1282                     clone[prop] = this[prop];
1283                 } else if (this[prop] instanceof Float32Array) {
1284                     clone[prop] = new Float32Array(this[prop]);
1285                 } else if (this[prop] instanceof Array &&
1286                     prop !== 'childNodes' &&
1287                     prop !== 'detectColor') {
1288                     clone[prop] = this[prop].slice(0);
1289                 }
1290             }
1291             if (this.mesh !== null) {
1292                 clone.mesh = this.mesh;
1293             }
1294             if (this.childNodes) {
1295                 for (var i = 0, l = this.childNodes.length; i < l; i++) {
1296                     clone.addChild(this.childNodes[i].clone());
1297                 }
1298             }
1299             return clone;
1300         },
1301 
1302         /**
1303          */
1304         set: function(sprite) {
1305             for (var prop in sprite) {
1306                 if (typeof sprite[prop] === 'number' ||
1307                     typeof sprite[prop] === 'string') {
1308                     this[prop] = sprite[prop];
1309                 } else if (sprite[prop] instanceof WebGLBuffer) {
1310                     this[prop] = sprite[prop];
1311                 } else if (sprite[prop] instanceof Float32Array) {
1312                     this[prop] = new Float32Array(sprite[prop]);
1313                 } else if (sprite[prop] instanceof Array &&
1314                     prop !== 'childNodes' &&
1315                     prop !== 'detectColor') {
1316                     this[prop] = sprite[prop].slice(0);
1317                 }
1318             }
1319             if (sprite.mesh !== null) {
1320                 this.mesh = sprite.mesh;
1321             }
1322             if (sprite.childNodes) {
1323                 for (var i = 0, l = sprite.childNodes.length; i < l; i++) {
1324                     this.addChild(sprite.childNodes[i].clone());
1325                 }
1326             }
1327         },
1328 
1329         /**
1330          */
1331         addChild: function(sprite) {
1332             this.childNodes.push(sprite);
1333             sprite.parentNode = this;
1334             sprite.dispatchEvent(new enchant.Event('added'));
1335             if (this.scene) {
1336                 sprite.scene = this.scene;
1337                 sprite.dispatchEvent(new enchant.Event('addedtoscene'));
1338             }
1339         },
1340 
1341         /**
1342          */
1343         removeChild: function(sprite) {
1344             var i;
1345             if ((i = this.childNodes.indexOf(sprite)) !== -1) {
1346                 this.childNodes.splice(i, 1);
1347                 sprite.parentNode = null;
1348                 sprite.dispatchEvent(new enchant.Event('removed'));
1349                 if (this.scene) {
1350                     sprite.scene = null;
1351                     sprite.dispatchEvent(new enchant.Event('removedfromscene'));
1352                 }
1353             }
1354         },
1355 
1356 
1357         /**
1358          */
1359         intersect: function(another) {
1360             return this.bounding.intersect(another.bounding);
1361         },
1362 
1363         /**
1364          */
1365         translate: function(x, y, z) {
1366             this._x += x;
1367             this._y += y;
1368             this._z += z;
1369             this._changedTranslation = true;
1370         },
1371 
1372         /**
1373          */
1374         forward: function(speed) {
1375             var x = this._rotation[8] * speed;
1376             var y = this._rotation[9] * speed;
1377             var z = this._rotation[10] * speed;
1378             this.translate(x, y, z);
1379         },
1380 
1381         /**
1382          */
1383         sidestep: function(speed) {
1384             var x = this._rotation[0] * speed;
1385             var y = this._rotation[1] * speed;
1386             var z = this._rotation[2] * speed;
1387             this.translate(x, y, z);
1388         },
1389 
1390         /**
1391          */
1392         altitude: function(speed) {
1393             var x = this._rotation[4] * speed;
1394             var y = this._rotation[5] * speed;
1395             var z = this._rotation[6] * speed;
1396             this.translate(x, y, z);
1397         },
1398 
1399         /**
1400          */
1401         scale: function(x, y, z) {
1402             this._scaleX *= x;
1403             this._scaleY *= y;
1404             this._scaleZ *= z;
1405             this._changedScale = true;
1406         },
1407 
1408         /**
1409          */
1410         name: {
1411             get: function() {
1412                 return this._name;
1413             },
1414             set: function(name) {
1415                 this._name = name;
1416             }
1417         },
1418 
1419         /**
1420          */
1421         rotation: {
1422             get: function() {
1423                 return this._rotation;
1424             },
1425             set: function(rotation) {
1426                 this._rotation = rotation;
1427                 this._changedRotation = true;
1428             }
1429         },
1430 
1431         /**
1432          */
1433         rotationSet: function(quat) {
1434             quat.toMat4(this._rotation);
1435             this._changedRotation = true;
1436         },
1437 
1438         /**
1439          */
1440         rotationApply: function(quat) {
1441             quat.toMat4(this.tmpMat);
1442             mat4.multiply(this._rotation, this.tmpMat);
1443             this._changedRotation = true;
1444         },
1445 
1446         /**
1447          */
1448         rotateRoll: function(rad) {
1449             this.rotationApply(new enchant.gl.Quat(0, 0, 1, rad));
1450             this._changedRotation = true;
1451         },
1452 
1453         /**
1454          */
1455         rotatePitch: function(rad) {
1456             this.rotationApply(new enchant.gl.Quat(1, 0, 0, rad));
1457             this._changedRotation = true;
1458         },
1459 
1460         /**
1461          */
1462         rotateYaw: function(rad) {
1463             this.rotationApply(new enchant.gl.Quat(0, 1, 0, rad));
1464             this._changedRotation = true;
1465         },
1466 
1467         /**
1468          * @type {enchant.gl.Mesh}
1469          */
1470         mesh: {
1471             get: function() {
1472                 return this._mesh;
1473             },
1474             set: function(mesh) {
1475                 if (this.scene !== null) {
1476                     this._mesh._count -= 1;
1477                     mesh._count += 1;
1478                 }
1479                 this._mesh = mesh;
1480             }
1481         },
1482 
1483         /**
1484          */
1485         matrix: {
1486             get: function() {
1487                 return this._matrix;
1488             },
1489             set: function(matrix) {
1490                 this._matrix = matrix;
1491             }
1492         },
1493 
1494         /**
1495          */
1496         bounding: {
1497             get: function() {
1498                 return this._bounding;
1499             },
1500             set: function(bounding) {
1501                 this._bounding = bounding;
1502                 this._bounding.parent = this;
1503             }
1504         },
1505 
1506         /**
1507          */
1508         touchable: {
1509             get: function() {
1510                 return this._touchable;
1511             },
1512             set: function(bool) {
1513                 this._touchable = bool;
1514                 if (this._touchable) {
1515                     this.detectColor[3] = 1.0;
1516                 } else {
1517                     this.detectColor[3] = 0.0;
1518                 }
1519             }
1520         },
1521 
1522         _transform: function(baseMatrix) {
1523             if (this._changedTranslation ||
1524                 this._changedRotation ||
1525                 this._changedScale) {
1526                 mat4.identity(this.modelMat);
1527                 mat4.translate(this.modelMat, [this._x, this._y, this._z]);
1528                 mat4.multiply(this.modelMat, this._rotation, this.modelMat);
1529                 mat4.scale(this.modelMat, [this._scaleX, this._scaleY, this._scaleZ]);
1530                 mat4.multiply(this.modelMat, this._matrix, this.modelMat);
1531                 this._changedTranslation = false;
1532                 this._changedRotation = false;
1533                 this._changedScale = false;
1534             }
1535 
1536             mat4.multiply(baseMatrix, this.modelMat, this.tmpMat);
1537 
1538             this._global[0] = this._x;
1539             this._global[1] = this._y;
1540             this._global[2] = this._z;
1541             mat4.multiplyVec3(this.tmpMat, this._global);
1542             this.globalX = this._global[0];
1543             this.globalY = this._global[1];
1544             this.globalZ = this._global[2];
1545         },
1546 
1547         _render: function(detectTouch) {
1548             var useTexture = this.mesh.texture._image ? 1.0 : 0.0;
1549 
1550             mat4.toInverseMat3(this.tmpMat, this._normMat);
1551             mat3.transpose(this._normMat);
1552 
1553             var attributes = {
1554                 aVertexPosition: this.mesh._vertices,
1555                 aVertexColor: this.mesh._colors,
1556                 aNormal: this.mesh._normals,
1557                 aTextureCoord: this.mesh._texCoords
1558             };
1559 
1560             var uniforms = {
1561                 uModelMat: this.tmpMat,
1562                 uDetectColor: this.detectColor,
1563                 uSpecular: this.mesh.texture.specular,
1564                 uDiffuse: this.mesh.texture.diffuse,
1565                 uEmission: this.mesh.texture.emission,
1566                 uAmbient: this.mesh.texture.ambient,
1567                 uShininess: this.mesh.texture.shininess,
1568                 uNormMat: this._normMat,
1569                 uSampler: this.mesh.texture,
1570                 uUseTexture: useTexture
1571             };
1572 
1573             var length = this.mesh.indices.length;
1574             enchant.Core.instance.GL.renderElements(this.mesh._indices, 0, length, attributes, uniforms);
1575         },
1576 
1577         _draw: function(scene, detectTouch, baseMatrix) {
1578 
1579             this._transform(baseMatrix);
1580 
1581             if (this.childNodes.length) {
1582                 for (var i = 0, l = this.childNodes.length; i < l; i++) {
1583                     this.childNodes[i]._draw(scene, detectTouch, this.tmpMat);
1584                 }
1585             }
1586 
1587             this.dispatchEvent(new enchant.Event('prerender'));
1588 
1589             if (this.mesh !== null) {
1590                 if (this.program !== null) {
1591                     enchant.Core.instance.GL.setProgram(this.program);
1592                     this._render(detectTouch);
1593                     enchant.Core.instance.GL.setDefaultProgram();
1594                 } else {
1595                     this._render(detectTouch);
1596                 }
1597             }
1598 
1599             this.dispatchEvent(new enchant.Event('render'));
1600         }
1601     });
1602 
1603     /**
1604      */
1605     enchant.gl.Sprite3D.prototype.x = 0;
1606 
1607     /**
1608      */
1609     enchant.gl.Sprite3D.prototype.y = 0;
1610 
1611     /**
1612      */
1613     enchant.gl.Sprite3D.prototype.z = 0;
1614 
1615     'x y z'.split(' ').forEach(function(prop) {
1616         Object.defineProperty(enchant.gl.Sprite3D.prototype, prop, {
1617             get: function() {
1618                 return this['_' + prop];
1619             },
1620             set: function(n) {
1621                 this['_' + prop] = n;
1622                 this._changedTranslation = true;
1623             }
1624         });
1625     });
1626 
1627     /**
1628      */
1629     enchant.gl.Sprite3D.prototype.scaleX = 1;
1630 
1631     /**
1632      */
1633     enchant.gl.Sprite3D.prototype.scaleY = 1;
1634     /**
1635      */
1636     enchant.gl.Sprite3D.prototype.scaleZ = 1;
1637 
1638     'scaleX scaleY scaleZ'.split(' ').forEach(function(prop) {
1639         Object.defineProperty(enchant.gl.Sprite3D.prototype, prop, {
1640             get: function() {
1641                 return this['_' + prop];
1642             },
1643             set: function(scale) {
1644                 this['_' + prop] = scale;
1645                 this._changedScale = true;
1646             }
1647         });
1648     });
1649 
1650     /**
1651      */
1652     enchant.gl.Sprite3D.prototype.globalX = 0;
1653 
1654     /**
1655      */
1656     enchant.gl.Sprite3D.prototype.globalY = 0;
1657 
1658     /**
1659      */
1660     enchant.gl.Sprite3D.prototype.globalZ = 0;
1661 
1662     /**
1663      * @scope enchant.gl.Camera3D.prototype
1664      */
1665     enchant.gl.Camera3D = enchant.Class.create({
1666         /**
1667          */
1668         initialize: function() {
1669             var core = enchant.Core.instance;
1670             this.mat = mat4.identity();
1671             this.invMat = mat4.identity();
1672             this.invMatY = mat4.identity();
1673             this._projMat = mat4.create();
1674             mat4.perspective(20, core.width / core.height, 1.0, 1000.0, this._projMat);
1675             this._changedPosition = false;
1676             this._changedCenter = false;
1677             this._changedUpVector = false;
1678             this._changedProjection = false;
1679             this._x = 0;
1680             this._y = 0;
1681             this._z = 10;
1682             this._centerX = 0;
1683             this._centerY = 0;
1684             this._centerZ = 0;
1685             this._upVectorX = 0;
1686             this._upVectorY = 1;
1687             this._upVectorZ = 0;
1688         },
1689         /**
1690          * projection matrix
1691          */
1692         projMat: {
1693             get: function() {
1694                 return this._projMat;
1695             },
1696             set: function(mat) {
1697                 this._projMat = mat;
1698                 this._changedProjection = true;
1699             }
1700         },
1701         /**
1702          */
1703         lookAt: function(sprite) {
1704             if (sprite instanceof enchant.gl.Sprite3D) {
1705                 this._centerX = sprite.x;
1706                 this._centerY = sprite.y;
1707                 this._centerZ = sprite.z;
1708                 this._changedCenter = true;
1709             }
1710         },
1711         /**
1712          */
1713         chase: function(sprite, position, speed) {
1714             if (sprite instanceof enchant.gl.Sprite3D) {
1715                 var vx = sprite.x + sprite.rotation[8] * position;
1716                 var vy = sprite.y + sprite.rotation[9] * position;
1717                 var vz = sprite.z + sprite.rotation[10] * position;
1718                 this._x += (vx - this._x) / speed;
1719                 this._y += (vy - this._y) / speed;
1720                 this._z += (vz - this._z) / speed;
1721                 this._changedPosition = true;
1722             }
1723         },
1724         _getForwardVec: function() {
1725             var x = this._centerX - this._x;
1726             var y = this._centerY - this._y;
1727             var z = this._centerZ - this._z;
1728             return vec3.normalize([x, y, z]);
1729         },
1730         _getSideVec: function() {
1731             var f = this._getForwardVec();
1732             var u = this._getUpVec();
1733             return vec3.cross(u, f);
1734         },
1735         _getUpVec: function() {
1736             var x = this._upVectorX;
1737             var y = this._upVectorY;
1738             var z = this._upVectorZ;
1739             return [x, y, z];
1740         },
1741         _move: function(v, s) {
1742             v[0] *= s;
1743             v[1] *= s;
1744             v[2] *= s;
1745             this._x += v[0];
1746             this._y += v[1];
1747             this._z += v[2];
1748             this._centerX += v[0];
1749             this._centerY += v[1];
1750             this._centerZ += v[2];
1751         },
1752         /**
1753          */
1754         forward: function(s) {
1755             var v = this._getForwardVec();
1756             this._move(v, s);
1757         },
1758         /**
1759          */
1760         sidestep: function(s) {
1761             var v = this._getSideVec();
1762             this._move(v, s);
1763         },
1764         /**
1765          */
1766         altitude: function(s) {
1767             var v = this._getUpVec();
1768             this._move(v, s);
1769         },
1770         /**
1771          */
1772         rotateRoll: function(rad) {
1773             var u = this._getUpVec();
1774             var f = this._getForwardVec();
1775             var x = f[0];
1776             var y = f[1];
1777             var z = f[2];
1778             var quat = new enchant.gl.Quat(x, y, z, -rad);
1779             var vec = quat.multiplyVec3(u);
1780             this._upVectorX = vec[0];
1781             this._upVectorY = vec[1];
1782             this._upVectorZ = vec[2];
1783             this._changedUpVector = true;
1784         },
1785         /**
1786          */
1787         rotatePitch: function(rad) {
1788             var u = this._getUpVec();
1789             var f = this._getForwardVec();
1790             var s = this._getSideVec();
1791             var sx = s[0];
1792             var sy = s[1];
1793             var sz = s[2];
1794             var quat = new enchant.gl.Quat(sx, sy, sz, -rad);
1795             var vec = quat.multiplyVec3(f);
1796             this._centerX = this._x + vec[0];
1797             this._centerY = this._y + vec[1];
1798             this._centerZ = this._z + vec[2];
1799             vec = vec3.normalize(quat.multiplyVec3(u));
1800             this._upVectorX = vec[0];
1801             this._upVectorY = vec[1];
1802             this._upVectorZ = vec[2];
1803             this._changedCenter = true;
1804             this._changedUpVector = true;
1805         },
1806         /**
1807          */
1808         rotateYaw: function(rad) {
1809             var u = this._getUpVec();
1810             var ux = u[0];
1811             var uy = u[1];
1812             var uz = u[2];
1813             var f = this._getForwardVec();
1814             var quat = new enchant.gl.Quat(ux, uy, uz, -rad);
1815             var vec = quat.multiplyVec3(f);
1816             this._centerX = this._x + vec[0];
1817             this._centerY = this._y + vec[1];
1818             this._centerZ = this._z + vec[2];
1819             this._changedCenter = true;
1820         },
1821         _updateMatrix: function() {
1822             mat4.lookAt(
1823                 [this._x, this._y, this._z],
1824                 [this._centerX, this._centerY, this._centerZ],
1825                 [this._upVectorX, this._upVectorY, this._upVectorZ],
1826                 this.mat);
1827             mat4.lookAt(
1828                 [0, 0, 0],
1829                 [-this._x + this._centerX,
1830                     -this._y + this._centerY,
1831                     -this._z + this._centerZ],
1832                 [this._upVectorX, this._upVectorY, this._upVectorZ],
1833                 this.invMat);
1834             mat4.inverse(this.invMat);
1835             mat4.lookAt(
1836                 [0, 0, 0],
1837                 [-this._x + this._centerX,
1838                     0,
1839                     -this._z + this._centerZ],
1840                 [this._upVectorX, this._upVectorY, this._upVectorZ],
1841                 this.invMatY);
1842             mat4.inverse(this.invMatY);
1843         }
1844     });
1845 
1846     /**
1847      */
1848     enchant.gl.Camera3D.prototype.x = 0;
1849 
1850     /**
1851      */
1852     enchant.gl.Camera3D.prototype.y = 0;
1853 
1854     /**
1855      */
1856     enchant.gl.Camera3D.prototype.z = 0;
1857 
1858     'x y z'.split(' ').forEach(function(prop) {
1859         Object.defineProperty(enchant.gl.Camera3D.prototype, prop, {
1860             get: function() {
1861                 return this['_' + prop];
1862             },
1863             set: function(n) {
1864                 this['_' + prop] = n;
1865                 this._changedPosition = true;
1866             }
1867         });
1868     });
1869 
1870     /**
1871      */
1872     enchant.gl.Camera3D.prototype.centerX = 0;
1873 
1874     /**
1875      */
1876     enchant.gl.Camera3D.prototype.centerY = 0;
1877 
1878     /**
1879      */
1880     enchant.gl.Camera3D.prototype.centerZ = 0;
1881 
1882     'centerX centerY centerZ'.split(' ').forEach(function(prop) {
1883         Object.defineProperty(enchant.gl.Camera3D.prototype, prop, {
1884             get: function() {
1885                 return this['_' + prop];
1886             },
1887             set: function(n) {
1888                 this['_' + prop] = n;
1889                 this._changedCenter = true;
1890             }
1891         });
1892     });
1893 
1894     /**
1895      */
1896     enchant.gl.Camera3D.prototype.upVectorX = 0;
1897 
1898     /**
1899      */
1900     enchant.gl.Camera3D.prototype.upVectorY = 1;
1901 
1902     /**
1903      */
1904     enchant.gl.Camera3D.prototype.upVectorZ = 0;
1905 
1906     'upVectorX upVectorY upVectorZ'.split(' ').forEach(function(prop) {
1907         Object.defineProperty(enchant.gl.Camera3D.prototype, prop, {
1908             get: function() {
1909                 return this['_' + prop];
1910             },
1911             set: function(n) {
1912                 this['_' + prop] = n;
1913                 this._changedUpVector = true;
1914             }
1915         });
1916     });
1917 
1918     /**
1919      * @scope enchant.gl.Scene3D.prototype
1920      */
1921     enchant.gl.Scene3D = enchant.Class.create(enchant.EventTarget, {
1922         /**
1923          */
1924         initialize: function() {
1925             var core = enchant.Core.instance;
1926             if (core.currentScene3D) {
1927                 return core.currentScene3D;
1928             }
1929             enchant.EventTarget.call(this);
1930             /**
1931              */
1932             this.childNodes = [];
1933 
1934             /**
1935              */
1936             this.lights = [];
1937 
1938             this.identityMat = mat4.identity();
1939             this._backgroundColor = [0.0, 0.0, 0.0, 1.0];
1940 
1941             var listener = function(e) {
1942                 for (var i = 0, len = this.childNodes.length; i < len; i++) {
1943                     var sprite = this.childNodes[i];
1944                     sprite.dispatchEvent(e);
1945                 }
1946             };
1947             this.addEventListener('added', listener);
1948             this.addEventListener('removed', listener);
1949             this.addEventListener('addedtoscene', listener);
1950             this.addEventListener('removedfromscene', listener);
1951 
1952             var that = this;
1953             var func = function() {
1954                 that._draw();
1955             };
1956             core.addEventListener('enterframe', func);
1957 
1958 
1959             var uniforms = {};
1960             uniforms['uUseCamera'] = 0.0;
1961             gl.activeTexture(gl.TEXTURE0);
1962             core.GL.defaultProgram.setUniforms(uniforms);
1963 
1964             if (core.currentScene3D === null) {
1965                 core.currentScene3D = this;
1966             }
1967 
1968             this.setAmbientLight(new enchant.gl.AmbientLight());
1969             this.setDirectionalLight(new enchant.gl.DirectionalLight());
1970             this.setCamera(new enchant.gl.Camera3D());
1971         },
1972 
1973         /**
1974          */
1975         backgroundColor: {
1976             get: function() {
1977                 return this._backgroundColor;
1978             },
1979             set: function(arg) {
1980                 var c = enchant.Core.instance.GL.parseColor(arg);
1981                 this._backgroundColor = c;
1982                 gl.clearColor(c[0], c[1], c[2], c[3]);
1983 
1984             }
1985         },
1986 
1987         /**
1988          */
1989         addChild: function(sprite) {
1990             this.childNodes.push(sprite);
1991             sprite.parentNode = sprite.scene = this;
1992             sprite.dispatchEvent(new enchant.Event('added'));
1993             sprite.dispatchEvent(new enchant.Event('addedtoscene'));
1994             sprite.dispatchEvent(new enchant.Event('render'));
1995         },
1996 
1997         /**
1998          */
1999         removeChild: function(sprite) {
2000             var i;
2001             if ((i = this.childNodes.indexOf(sprite)) !== -1) {
2002                 this.childNodes.splice(i, 1);
2003                 sprite.parentNode = sprite.scene = null;
2004                 sprite.dispatchEvent(new enchant.Event('removed'));
2005                 sprite.dispatchEvent(new enchant.Event('removedfromscene'));
2006             }
2007         },
2008 
2009         /**
2010          */
2011         setCamera: function(camera) {
2012             camera._changedPosition = true;
2013             camera._changedCenter = true;
2014             camera._changedUpVector = true;
2015             camera._changedProjection = true;
2016             this._camera = camera;
2017             enchant.Core.instance.GL.defaultProgram.setUniforms({
2018                 uUseCamera: 1.0
2019             });
2020         },
2021 
2022         /**
2023          */
2024         getCamera: function() {
2025             return this._camera;
2026         },
2027 
2028         /**
2029          */
2030         setAmbientLight: function(light) {
2031             this.ambientLight = light;
2032         },
2033 
2034         /**
2035          */
2036         getAmbientLight: function() {
2037             return this.ambientLight;
2038         },
2039 
2040         /**
2041          */
2042         setDirectionalLight: function(light) {
2043             this.directionalLight = light;
2044             this.useDirectionalLight = true;
2045             enchant.Core.instance.GL.defaultProgram.setUniforms({
2046                 uUseDirectionalLight: 1.0
2047             });
2048         },
2049 
2050         /**
2051          */
2052         getDirectionalLight: function() {
2053             return this.directionalLight;
2054         },
2055 
2056         /**
2057          */
2058         addLight: function(light) {
2059             this.lights.push(light);
2060             this.usePointLight = true;
2061         },
2062 
2063         /**
2064          */
2065         removeLight: function(light) {
2066             var i;
2067             if ((i = this.lights.indexOf(light)) !== -1) {
2068                 this.lights.splice(i, 1);
2069             }
2070         },
2071 
2072         _draw: function(detectTouch) {
2073             var core = enchant.Core.instance;
2074             var program = core.GL.defaultProgram;
2075 
2076             gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
2077 
2078             var detect = (detectTouch === 'detect') ? 1.0 : 0.0;
2079 
2080             var uniforms = { uDetectTouch: detect };
2081 
2082             if (this.ambientLight._changedColor) {
2083                 uniforms['uAmbientLightColor'] = this.ambientLight.color;
2084                 this.ambientLight._changedColor = false;
2085             }
2086             if (this.useDirectionalLight) {
2087                 if (this.directionalLight._changedDirection) {
2088                     uniforms['uLightDirection'] = [
2089                         this.directionalLight.directionX,
2090                         this.directionalLight.directionY,
2091                         this.directionalLight.directionZ
2092                     ];
2093                     this.directionalLight._changedDirection = false;
2094                 }
2095                 if (this.directionalLight._changedColor) {
2096                     uniforms['uLightColor'] = this.directionalLight.color;
2097                     this.directionalLight._changedColor = false;
2098                 }
2099             }
2100 
2101             if (this._camera) {
2102                 if (this._camera._changedPosition ||
2103                     this._camera._changedCenter ||
2104                     this._camera._changedUpVector ||
2105                     this._camera._changedProjection) {
2106                     this._camera._updateMatrix();
2107                     uniforms['uCameraMat'] = this._camera.mat;
2108                     uniforms['uProjMat'] = this._camera._projMat;
2109                     uniforms['uLookVec'] = [
2110                         this._camera._centerX - this._camera._x,
2111                         this._camera._centerY - this._camera._y,
2112                         this._camera._centerZ - this._camera._z
2113                     ];
2114                     this._camera._changedPosition = false;
2115                     this._camera._changedCenter = false;
2116                     this._camera._changedUpVector = false;
2117                     this._camera._changedProjection = false;
2118                 }
2119             }
2120             program.setUniforms(uniforms);
2121 
2122             mat4.identity(this.identityMat);
2123             for (var i = 0, l = this.childNodes.length; i < l; i++) {
2124                 this.childNodes[i]._draw(this, detectTouch, this.identityMat);
2125             }
2126 
2127         }
2128     });
2129 
2130     /**
2131      * @type {Object}
2132      */
2133     enchant.gl.collision = {};
2134 
2135     var point2point = function(p1, p2) {
2136         var vx = p1.x + p1.parent.x - p2.x - p2.parent.x;
2137         var vy = p1.y + p1.parent.y - p2.y - p2.parent.y;
2138         var vz = p1.z + p1.parent.z - p2.z - p2.parent.z;
2139         return (vx * vx + vy * vy + vz * vz);
2140     };
2141 
2142     var point2BS = function(p, bs) {
2143         return (point2point(p, bs) - bs.radius * bs.radius);
2144     };
2145 
2146     var point2AABB = function(p, aabb) {
2147         var ppx = p.x + p.parent.x;
2148         var ppy = p.y + p.parent.y;
2149         var ppz = p.z + p.parent.z;
2150         var px = aabb.parent.x + aabb.x + aabb.scale;
2151         var py = aabb.parent.y + aabb.y + aabb.scale;
2152         var pz = aabb.parent.z + aabb.z + aabb.scale;
2153         var nx = aabb.parent.x + (aabb.x - aabb.scale);
2154         var ny = aabb.parent.y + (aabb.y - aabb.scale);
2155         var nz = aabb.parent.z + (aabb.z - aabb.scale);
2156         var dist = 0;
2157         if (ppx < nx) {
2158             dist += (ppx - nx) * (ppx - nx);
2159         } else if (px < ppx) {
2160             dist += (ppx - px) * (ppx - px);
2161         }
2162         if (ppy < ny) {
2163             dist += (ppy - ny) * (ppy - ny);
2164         } else if (py < ppy) {
2165             dist += (ppy - py) * (ppy - py);
2166         }
2167         if (ppz < nz) {
2168             dist += (ppz - nz) * (ppz - nz);
2169         } else if (pz < ppz) {
2170             dist += (ppz - pz) * (ppz - pz);
2171         }
2172         return dist;
2173     };
2174 
2175     var point2OBB = function(p, obb) {
2176         return 1;
2177     };
2178 
2179     var BS2BS = function(bs1, bs2) {
2180         return (point2point(bs1, bs2) - (bs1.radius + bs2.radius) * (bs1.radius + bs2.radius));
2181     };
2182 
2183     var BS2AABB = function(bs, aabb) {
2184         return (point2AABB(bs, aabb) - bs.radius * bs.radius);
2185     };
2186 
2187     var BS2OBB = function(bs, obb) {
2188         return 1;
2189     };
2190 
2191     var AABB2AABB = function(aabb1, aabb2) {
2192         var px1 = aabb1.parent.x + aabb1.x + aabb1.scale;
2193         var py1 = aabb1.parent.y + aabb1.y + aabb1.scale;
2194         var pz1 = aabb1.parent.z + aabb1.z + aabb1.scale;
2195 
2196         var nx1 = aabb1.parent.x + (aabb1.x - aabb1.scale);
2197         var ny1 = aabb1.parent.y + (aabb1.y - aabb1.scale);
2198         var nz1 = aabb1.parent.z + (aabb1.z - aabb1.scale);
2199 
2200         var px2 = aabb2.parent.x + aabb2.x + aabb2.scale;
2201         var py2 = aabb2.parent.y + aabb2.y + aabb2.scale;
2202         var pz2 = aabb2.parent.z + aabb2.z + aabb2.scale;
2203 
2204         var nx2 = aabb2.parent.x + (aabb2.x - aabb2.scale);
2205         var ny2 = aabb2.parent.y + (aabb2.y - aabb2.scale);
2206         var nz2 = aabb2.parent.z + (aabb2.z - aabb2.scale);
2207         return ((nx2 <= px1) && (nx1 <= px2) &&
2208             (ny2 <= py1) && (ny1 <= py2) &&
2209             (nz2 <= pz1) && (nz1 <= pz2)) ? 0.0 : 1.0;
2210     };
2211 
2212     var AABB2OBB = function(aabb, obb) {
2213         return 1;
2214     };
2215 
2216     var OBB2OBB = function(obb1, obb2) {
2217         return 1;
2218     };
2219 
2220     /**
2221      * @scope enchant.gl.collision.Bounding.prototype
2222      */
2223     enchant.gl.collision.Bounding = enchant.Class.create({
2224         /**
2225          */
2226         initialize: function() {
2227             this.type = 'point';
2228             this.threshold = 0.0001;
2229             this.x = 0;
2230             this.y = 0;
2231             this.z = 0;
2232             this.parent = {
2233                 x: 0,
2234                 y: 0,
2235                 z: 0
2236             };
2237         },
2238         /**
2239          */
2240         toBounding: function(another) {
2241             return point2point(this, another);
2242         },
2243         /**
2244          */
2245         toBS: function(another) {
2246             return point2BS(this, another);
2247         },
2248         /**
2249          */
2250         toAABB: function(another) {
2251             return point2AABB(this, another);
2252         },
2253         /**
2254          */
2255         toOBB: function(another) {
2256             return point2OBB(this, another);
2257         },
2258         /**
2259          */
2260         intersect: function(another) {
2261             switch (another.type) {
2262                 case 'point':
2263                     return (this.toBounding(another) < this.threshold);
2264                 case 'BS':
2265                     return (this.toBS(another) < this.threshold);
2266                 case 'AABB':
2267                     return (this.toAABB(another) < this.threshold);
2268                 case 'OBB':
2269                     return (this.toOBB(another) < this.threshold);
2270                 default:
2271                     return false;
2272             }
2273         }
2274     });
2275 
2276     /**
2277      * @scope enchant.gl.collision.BS.prototype
2278      */
2279     enchant.gl.collision.BS = enchant.Class.create(enchant.gl.collision.Bounding, {
2280         /**
2281          */
2282         initialize: function() {
2283             enchant.gl.collision.Bounding.call(this);
2284             this.type = 'BS';
2285             this.radius = 0.5;
2286         },
2287         toBounding: function(another) {
2288             return point2BS(another, this);
2289         },
2290         toBS: function(another) {
2291             return BS2BS(this, another);
2292         },
2293         toAABB: function(another) {
2294             return BS2AABB(this, another);
2295         },
2296         toOBB: function(another) {
2297             return BS2OBB(this, another);
2298         }
2299     });
2300 
2301     /**
2302      * @scope enchant.gl.collision.AABB.prototype
2303      */
2304     enchant.gl.collision.AABB = enchant.Class.create(enchant.gl.collision.Bounding, {
2305         /**
2306          */
2307         initialize: function() {
2308             enchant.gl.collision.Bounding.call(this);
2309             this.type = 'AABB';
2310             this.scale = 0.5;
2311         },
2312         toBounding: function(another) {
2313             return point2AABB(another, this);
2314         },
2315         toBS: function(another) {
2316             return BS2AABB(another, this);
2317         },
2318         toAABB: function(another) {
2319             return AABB2AABB(this, another);
2320         },
2321         toOBB: function(another) {
2322             return AABB2OBB(this, another);
2323         }
2324     });
2325 
2326     /**
2327      * @scope enchant.gl.collision.OBB.prototype
2328      */
2329     enchant.gl.collision.OBB = enchant.Class.create(enchant.gl.collision.Bounding, {
2330         /**
2331 
2332          */
2333         initialize: function() {
2334             enchant.gl.collision.Bounding.call(this);
2335             this.type = 'OBB';
2336         },
2337         toBounding: function(another) {
2338             return point2OBB(another, this);
2339         },
2340         toBS: function(another) {
2341             return BS2OBB(another, this);
2342         },
2343         toAABB: function(another) {
2344             return AABB2OBB(another, this);
2345         },
2346         toOBB: function(another) {
2347             return OBB2OBB(this, another);
2348         }
2349     });
2350 
2351     // borrowed from MMD.js
2352     var bezierp = function(x1, x2, y1, y2, x) {
2353         var t, tt, v;
2354         t = x;
2355         while (true) {
2356             v = ipfunc(t, x1, x2) - x;
2357             if (v * v < 0.0000001) {
2358                 break;
2359             }
2360             tt = ipfuncd(t, x1, x2);
2361             if (tt === 0) {
2362                 break;
2363             }
2364             t -= v / tt;
2365         }
2366         return ipfunc(t, y1, y2);
2367     };
2368     var ipfunc = function(t, p1, p2) {
2369         return (1 + 3 * p1 - 3 * p2) * t * t * t + (3 * p2 - 6 * p1) * t * t + 3 * p1 * t;
2370     };
2371     var ipfuncd = function(t, p1, p2) {
2372         return (3 + 9 * p1 - 9 * p2) * t * t + (6 * p2 - 12 * p1) * t + 3 * p1;
2373     };
2374     var frac = function(n1, n2, t) {
2375         return (t - n1) / (n2 - n1);
2376     };
2377     var lerp = function(n1, n2, r) {
2378         return n1 + r * (n2 - n1);
2379     };
2380 
2381     var _tmpve = vec3.create();
2382     var _tmpvt = vec3.create();
2383     var _tmpaxis = vec3.create();
2384     var _tmpquat = quat4.create();
2385     var _tmpinv = quat4.create();
2386 
2387     /**
2388      * @scope enchant.gl.State.prototype
2389      */
2390     enchant.gl.State = enchant.Class.create({
2391         /**
2392          * @param {Number[]} position
2393          * @param {Number[]} rotation
2394          * @constructs
2395          */
2396         initialize: function(position, rotation) {
2397             this._position = vec3.create();
2398             vec3.set(position, this._position);
2399             this._rotation = quat4.create();
2400             quat4.set(rotation, this._rotation);
2401         },
2402         /**
2403          */
2404         set: function(pose) {
2405             vec3.set(pose._position, this._position);
2406             quat4.set(pose._rotation, this._rotation);
2407         }
2408     });
2409 
2410     /**
2411      * @scope enchant.gl.Pose.prototype
2412      */
2413     enchant.gl.Pose = enchant.Class.create(enchant.gl.State, {
2414         /**
2415          */
2416         initialize: function(position, rotation) {
2417             enchant.gl.State.call(this, position, rotation);
2418         },
2419         /**
2420          */
2421         getInterpolation: function(another, ratio) {
2422             vec3.lerp(this._position, another._position, ratio, _tmpve);
2423             quat4.slerp(this._rotation, another._rotation, ratio, _tmpquat);
2424             return new enchant.gl.Pose(_tmpve, _tmpquat);
2425         },
2426         _bezierp: function(x1, y1, x2, y2, x) {
2427             return bezierp(x1, x2, y1, y2, x);
2428         }
2429     });
2430 
2431     /**
2432      * @scope enchant.gl.KeyFrameManager.prototype
2433      */
2434     enchant.gl.KeyFrameManager = enchant.Class.create({
2435         /**
2436          */
2437         initialize: function() {
2438             this._frames = [];
2439             this._units = [];
2440             this.length = -1;
2441             this._lastPose = null;
2442         },
2443         /**
2444          */
2445         addFrame: function(pose, frame) {
2446             if (typeof frame !== 'number') {
2447                 this.length += 1;
2448                 frame = this.length;
2449             }
2450             if (frame > this.length) {
2451                 this.length = frame;
2452                 this._lastPose = pose;
2453             }
2454             this._frames.push(frame);
2455             this._units[frame] = pose;
2456         },
2457         /**
2458          */
2459         getFrame: function(frame) {
2460             var prev, next, index, pidx, nidx;
2461             var ratio = 0;
2462             if (frame >= this.length) {
2463                 return this._lastPose;
2464             }
2465             if (this._units[frame]) {
2466                 return this._units[frame];
2467             } else {
2468                 index = this._getPrevFrameIndex(frame);
2469                 pidx = this._frames[index];
2470                 nidx = this._frames[index + 1];
2471                 prev = this._units[pidx];
2472                 next = this._units[nidx];
2473                 ratio = this._frac(pidx, nidx, frame);
2474                 return this._interpole(prev, next, ratio);
2475             }
2476         },
2477         bake: function() {
2478             var state;
2479             for (var i = 0, l = this.length; i < l; i++) {
2480                 if (this._units[i]) {
2481                     continue;
2482                 }
2483                 state = this.getFrame(i);
2484                 this.addFrame(state, i);
2485             }
2486             this._sort();
2487         },
2488         _frac: function(p, n, t) {
2489             return frac(p, n, t);
2490         },
2491         _interpole: function(prev, next, ratio) {
2492             return prev.getInterpolation(next, ratio);
2493         },
2494         _sort: function() {
2495             this._frames.sort(function(a, b) {
2496                 return a - b;
2497             });
2498         },
2499         _getPrevFrameIndex: function(frame) {
2500             for (var i = 0, l = this._frames.length; i < l; i++) {
2501                 if (this._frames[i] > frame) {
2502                     break;
2503                 }
2504             }
2505             return i - 1;
2506         }
2507     });
2508 
2509     /**
2510      * @scope enchant.gl.Bone.prototype
2511      */
2512     enchant.gl.Bone = enchant.Class.create(enchant.gl.State, {
2513         /**
2514          */
2515         initialize: function(name, head, position, rotation) {
2516             enchant.gl.State.call(this, position, rotation);
2517             this._name = name;
2518             this._origin = vec3.create();
2519 
2520             vec3.set(head, this._origin);
2521 
2522             this._globalpos = vec3.create();
2523             vec3.set(head, this._globalpos);
2524 
2525             this._globalrot = quat4.identity();
2526 
2527             this.parentNode = null;
2528             this.childNodes = [];
2529 
2530             /**
2531              */
2532             this.constraint = null;
2533         },
2534         /**
2535          */
2536         addChild: function(child) {
2537             this.childNodes.push(child);
2538             child.parentNode = this;
2539         },
2540         /**
2541          */
2542         removeChild: function(child) {
2543             var i;
2544             if ((i = this.childNodes.indexOf(child)) !== -1) {
2545                 this.childNodes.splice(i, 1);
2546             }
2547             child.parentNode = null;
2548         },
2549         /**
2550          */
2551         setPoses: function(poses) {
2552             var child;
2553             if (poses[this._name]) {
2554                 this.set(poses[this._name]);
2555             }
2556             for (var i = 0, l = this.childNodes.length; i < l; i++) {
2557                 child = this.childNodes[i];
2558                 child.setPoses(poses);
2559             }
2560         },
2561         _applyPose: function(){
2562             var parent = this.parentNode;
2563             quat4.multiply(parent._globalrot, this._rotation, this._globalrot);
2564             quat4.multiplyVec3(parent._globalrot, this._position, this._globalpos);
2565             vec3.add(parent._globalpos, this._globalpos, this._globalpos);
2566         },
2567         _solveFK: function() {
2568             var child;
2569             this._applyPose();
2570             for (var i = 0, l = this.childNodes.length; i < l; i++) {
2571                 child = this.childNodes[i];
2572                 child._solveFK();
2573             }
2574         },
2575         _solve: function(quat) {
2576             quat4.normalize(quat, this._rotation);
2577             this._solveFK();
2578         }
2579     });
2580 
2581     /**
2582      * @scope enchant.gl.Skeleton.prototype
2583      */
2584     enchant.gl.Skeleton = enchant.Class.create({
2585         /**
2586          */
2587         initialize: function() {
2588             this.childNodes = [];
2589             this._origin = vec3.create();
2590             this._position = vec3.create();
2591             this._rotation = quat4.identity();
2592             this._globalpos = vec3.create();
2593             this._globalrot = quat4.identity();
2594             this._iks = [];
2595         },
2596         /**
2597          */
2598         addChild: function(bone) {
2599             this.childNodes.push(bone);
2600             bone.parentNode = this;
2601         },
2602         /**
2603          */
2604         removeChild: function(bone) {
2605             var i;
2606             if ((i = this.childNodes.indexOf(bone)) !== -1) {
2607                 this.childNodes.splice(i, 1);
2608             }
2609             bone.parentNode = null;
2610         },
2611         /**
2612          */
2613         setPoses: function(poses) {
2614             var child;
2615             for (var i = 0, l = this.childNodes.length; i < l; i++) {
2616                 child = this.childNodes[i];
2617                 child.setPoses(poses);
2618             }
2619         },
2620         /**
2621          */
2622         solveFKs: function() {
2623             var child;
2624             for (var i = 0, l = this.childNodes.length; i < l; i++) {
2625                 child = this.childNodes[i];
2626                 child._solveFK();
2627             }
2628         },
2629         /**
2630          */
2631         addIKControl: function(effector, target, bones, maxangle, iteration) {
2632             this._iks.push(arguments);
2633         },
2634         // by ccd
2635         /**
2636          */
2637         solveIKs: function() {
2638             var param;
2639             for (var i = 0, l = this._iks.length; i < l; i++) {
2640                 param = this._iks[i];
2641                 this._solveIK.apply(this, param);
2642             }
2643         },
2644         _solveIK: function(effector, target, bones, maxangle, iteration) {
2645             var len, origin;
2646             vec3.subtract(target._origin, target.parentNode._origin, _tmpinv);
2647             var threshold = vec3.length(_tmpinv) * 0.1;
2648             for (var i = 0; i < iteration; i++) {
2649                 vec3.subtract(target._globalpos, effector._globalpos, _tmpinv);
2650                 len = vec3.length(_tmpinv);
2651                 if (len < threshold) {
2652                     break;
2653                 }
2654                 for (var j = 0, ll = bones.length; j < ll; j++) {
2655                     origin = bones[j];
2656                     this._ccd(effector, target, origin, maxangle, threshold);
2657                 }
2658             }
2659         },
2660         _ccd: function(effector, target, origin, maxangle, threshold) {
2661             vec3.subtract(effector._globalpos, origin._globalpos, _tmpve);
2662             vec3.subtract(target._globalpos, origin._globalpos, _tmpvt);
2663             vec3.cross(_tmpvt, _tmpve, _tmpaxis);
2664             var elen = vec3.length(_tmpve);
2665             var tlen = vec3.length(_tmpvt);
2666             var alen = vec3.length(_tmpaxis);
2667 
2668             if (elen < threshold || tlen < threshold || alen < threshold) {
2669                 return;
2670             }
2671             var rad = Math.acos(vec3.dot(_tmpve, _tmpvt) / elen / tlen);
2672 
2673             if (rad > maxangle) {
2674                 rad = maxangle;
2675             }
2676             vec3.scale(_tmpaxis, Math.sin(rad / 2) / alen, _tmpquat);
2677             _tmpquat[3] = Math.cos(rad / 2);
2678             quat4.inverse(origin.parentNode._globalrot, _tmpinv);
2679             quat4.multiply(_tmpinv, _tmpquat, _tmpquat);
2680             quat4.multiply(_tmpquat, origin._globalrot, _tmpquat);
2681 
2682 
2683             if (origin.constraint) {
2684                 origin.constraint(_tmpquat);
2685             }
2686 
2687             origin._solve(_tmpquat);
2688         }
2689     });
2690 
2691     var DEFAULT_VERTEX_SHADER_SOURCE = '\n\
2692     attribute vec3 aVertexPosition;\n\
2693     attribute vec4 aVertexColor;\n\
2694     \n\
2695     attribute vec3 aNormal;\n\
2696     attribute vec2 aTextureCoord;\n\
2697     \n\
2698     uniform mat4 uModelMat;\n\
2699     uniform mat4 uRotMat;\n\
2700     uniform mat4 uCameraMat;\n\
2701     uniform mat4 uProjMat;\n\
2702     uniform mat3 uNormMat;\n\
2703     uniform float uUseCamera;\n\
2704     \n\
2705     varying vec2 vTextureCoord;\n\
2706     varying vec4 vColor;\n\
2707     varying vec3 vNormal;\n\
2708     \n\
2709     void main() {\n\
2710         vec4 p = uModelMat * vec4(aVertexPosition, 1.0);\n\
2711         gl_Position = uProjMat * (uCameraMat * uUseCamera) * p + uProjMat * p * (1.0 - uUseCamera);\n\
2712         vTextureCoord = aTextureCoord;\n\
2713         vColor = aVertexColor;\n\
2714         vNormal = uNormMat * aNormal;\n\
2715     }';
2716 
2717     var DEFAULT_FRAGMENT_SHADER_SOURCE = '\n\
2718     precision highp float;\n\
2719     \n\
2720     uniform sampler2D uSampler;\n\
2721     uniform float uUseDirectionalLight;\n\
2722     uniform vec3 uAmbientLightColor;\n\
2723     uniform vec3 uLightColor;\n\
2724     uniform vec3 uLookVec;\n\
2725     uniform vec4 uAmbient;\n\
2726     uniform vec4 uDiffuse;\n\
2727     uniform vec4 uSpecular;\n\
2728     uniform vec4 uEmission;\n\
2729     uniform vec4 uDetectColor;\n\
2730     uniform float uDetectTouch;\n\
2731     uniform float uUseTexture;\n\
2732     uniform float uShininess;\n\
2733     uniform vec3 uLightDirection;\n\
2734     \n\
2735     varying vec2 vTextureCoord;\n\
2736     varying vec4 vColor;\n\
2737     varying vec3 vNormal;\n\
2738     \n\
2739     \n\
2740     void main() {\n\
2741         float pi = 4.0 * atan(1.0);\n\
2742         vec4 texColor = texture2D(uSampler, vTextureCoord);\n\
2743         vec4 baseColor = vColor;\n\
2744         baseColor *= texColor * uUseTexture + vec4(1.0, 1.0, 1.0, 1.0) * (1.0 - uUseTexture);\n\
2745         float alpha = baseColor.a * uDetectColor.a * uDetectTouch + baseColor.a * (1.0 - uDetectTouch);\n\
2746         if (alpha < 0.2) {\n\
2747             discard;\n\
2748         }\n\
2749         else {\n\
2750             vec4 amb = uAmbient * vec4(uAmbientLightColor, 1.0);\n\
2751             vec3 N = normalize(vNormal);\n\
2752             vec3 L = normalize(uLightDirection);\n\
2753             vec3 E = normalize(uLookVec);\n\
2754             vec3 R = reflect(-L, N);\n\
2755             float lamber = max(dot(N, L) , 0.0);\n\
2756             vec4 dif = uDiffuse * lamber;\n\
2757             float s = max(dot(R, -E), 0.0);\n\
2758             vec4 specularColor = (uShininess + 2.0) / (2.0 * pi) * uSpecular * pow(s, uShininess) * sign(lamber);\n\
2759             gl_FragColor = (vec4(((amb + vec4(uLightColor, 1.0) * (dif + specularColor)) * baseColor).rgb, baseColor.a) \
2760                 * uUseDirectionalLight + baseColor * (1.0 - uUseDirectionalLight)) \
2761                 * (1.0 - uDetectTouch) + uDetectColor * uDetectTouch;\n\
2762         }\n\
2763     }';
2764 
2765 }());
2766