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  * WebGLを用いた描画ライブラリ
 11  * enchant.js と組み合わせることで高度な3D描画と、2D描画を組み合わせることができる
 12  *
 13  * @detail
 14  * ベクトル・行列演算にgl-matrix.jsを使用しています.
 15  * gl-matrix.js:
 16  * https://github.com/toji/gl-matrix/
 17  */
 18 
 19 /**
 20  * enchantにgl.enchant.jsのクラスをエクスポートする.
 21  */
 22 enchant.gl = {};
 23 
 24 if (typeof glMatrixArrayType === 'undefined') {
 25     throw new Error('should load gl-matrix.js before loading gl.enchant.js');
 26 }
 27 
 28 (function() {
 29 
 30     var CONTEXT_NAME = 'experimental-webgl';
 31 
 32     var parentModule = null;
 33     (function() {
 34         enchant();
 35         if (enchant.nineleap !== undefined) {
 36             if (enchant.nineleap.memory !== undefined &&
 37                 Object.getPrototypeOf(enchant.nineleap.memory) === Object.prototype) {
 38                 parentModule = enchant.nineleap.memory;
 39             } else if (enchant.nineleap !== undefined &&
 40                 Object.getPrototypeOf(enchant.nineleap) === Object.prototype) {
 41                 parentModule = enchant.nineleap;
 42             }
 43         } else {
 44             parentModule = enchant;
 45         }
 46     }());
 47 
 48     enchant.gl.Core = enchant.Class.create(parentModule.Core, {
 49         initialize: function(width, height) {
 50             parentModule.Core.call(this, width, height);
 51             this.GL = new GLUtil();
 52             this.currentScene3D = null;
 53             this.addEventListener('enterframe', function(e) {
 54                 if (!this.currentScene3D) {
 55                     return;
 56                 }
 57                 var nodes = this.currentScene3D.childNodes.slice();
 58                 var push = Array.prototype.push;
 59                 while (nodes.length) {
 60                     var node = nodes.pop();
 61                     node.dispatchEvent(e);
 62                     node.age++;
 63                     if (node.childNodes) {
 64                         push.apply(nodes, node.childNodes);
 65                     }
 66                 }
 67             });
 68         },
 69         debug: function() {
 70             this.GL._enableDebugContext();
 71             this._debug = true;
 72             this.addEventListener("enterframe", function(time) {
 73                 this._actualFps = (1000 / time.elapsed);
 74             });
 75             this.start();
 76         }
 77     });
 78 
 79     var GLUtil = enchant.Class.create({
 80         initialize: function() {
 81             var core = enchant.Core.instance;
 82             if (typeof core.GL !== 'undefined') {
 83                 return core.GL;
 84             }
 85             this._createStage(core.width, core.height, core.scale);
 86             this._prepare();
 87             this.textureManager = new TextureManager();
 88             this.detectColorManager = new DetectColorManager();
 89             this.detectFrameBuffer = new enchant.gl.FrameBuffer(core.width, core.height);
 90             this.defaultProgram = new enchant.gl.Shader(DEFAULT_VERTEX_SHADER_SOURCE, DEFAULT_FRAGMENT_SHADER_SOURCE);
 91             this.setDefaultProgram();
 92         },
 93         setDefaultProgram: function() {
 94             this.setProgram(this.defaultProgram);
 95         },
 96         setProgram: function(program) {
 97             program.use();
 98             this.currentProgram = program;
 99         },
100         _prepare: function() {
101             var width = this._canvas.width;
102             var height = this._canvas.height;
103             gl.clearColor(0.0, 0.0, 0.0, 1.0);
104             gl.viewport(0, 0, width, height);
105             gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
106             gl.enable(gl.DEPTH_TEST);
107             gl.enable(gl.CULL_FACE);
108         },
109         _createStage: function(width, height, scale) {
110             var div = createParentDiv();
111             var that = this;
112             var stage = document.getElementById('enchant-stage');
113             var cvs = this._canvas = createGLCanvas(width, height, scale);
114             var detect = new enchant.Sprite(width, height);
115             var core = enchant.Core.instance;
116             (function() {
117                 var color = new Uint8Array(4);
118                 var touching = null;
119                 var sprite;
120                 detect.addEventListener('touchstart', function(e) {
121                     var scene = core.currentScene3D;
122                     var x = parseInt(e.x, 10);
123                     var y = parseInt(this.height - e.y, 10);
124                     that.detectFrameBuffer.bind();
125                     scene._draw('detect');
126                     gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, color);
127                     sprite = that.detectColorManager.getSpriteByColor(color);
128                     if (sprite) {
129                         touching = sprite;
130                         touching.dispatchEvent(e);
131                     }
132                     that.detectFrameBuffer.unbind();
133                 });
134                 detect.addEventListener('touchmove', function(e) {
135                     if (touching !== null) {
136                         touching.dispatchEvent(e);
137                     }
138                 });
139                 detect.addEventListener('touchend', function(e) {
140                     if (touching !== null) {
141                         touching.dispatchEvent(e);
142                     }
143                     touching = null;
144                 });
145             }());
146             window['gl'] = this._gl = this._getContext(cvs);
147             div.appendChild(cvs);
148             stage.insertBefore(div, core.rootScene._element);
149             core.rootScene.addChild(detect);
150         },
151         _getContext: function(canvas, debug) {
152             var ctx = canvas.getContext(CONTEXT_NAME);
153             if (!ctx) {
154                 window['alert']('could not initialized WebGL');
155                 throw new Error('could not initialized WebGL');
156             }
157             if (debug) {
158                 ctx = createDebugContext(ctx);
159             }
160             return ctx;
161         },
162         _enableDebugContext: function() {
163             window['gl'] = this._gl = createDebugContext(this._gl);
164         },
165         parseColor: function(string) {
166             return parseColor(string);
167         },
168         renderElements: function(buffer, offset, length, attributes, uniforms) {
169             if (attributes) {
170                 this.currentProgram.setAttributes(attributes);
171             }
172             if (uniforms) {
173                 this.currentProgram.setUniforms(uniforms);
174             }
175             buffer.bind();
176             gl.drawElements(gl.TRIANGLES, length, gl.UNSIGNED_SHORT, offset);
177             buffer.unbind();
178         }
179     });
180 
181     var parseColor = function(string) {
182         var color = [];
183         if (typeof string === 'string') {
184             if (string.match(/#/)) {
185                 string.match(/[0-9a-fA-F]{2}/g).forEach(function(n) {
186                     color[color.length] = ('0x' + n - 0) / 255;
187                 });
188                 color[color.length] = 1.0;
189             } else if (string.match(/rgba/)) {
190                 string.match(/[0-9]{1,3},/g).forEach(function(n) {
191                     color[color.length] = parseInt(n, 10) / 255;
192                 });
193                 color[color.length] = parseFloat(string.match(/[0-9]\.[0-9]{1,}/)[0]);
194             } else if (string.match(/rgb/)) {
195                 string.match(/[0-9]{1,3},/g).forEach(function(n) {
196                     color[color.length] = parseInt(n, 10) / 255;
197                 });
198                 color[color.length] = 1.0;
199             }
200         } else if (string instanceof Array) {
201             color = string;
202         }
203         return color;
204     };
205 
206     var createDebugContext = function(context) {
207         var ctx = {};
208         var names = {};
209         var type = '';
210         var val;
211         var makeFakedMethod = function(context, prop) {
212             return function() {
213                 var value, error;
214                 value = context[prop].apply(context, arguments);
215                 error = context.getError();
216                 if (error) {
217                     window['console'].log(names[error] + '(' + error + ')' + ': ' + prop);
218                     window['console'].log(arguments);
219                 }
220                 return value;
221             };
222         };
223         for (var prop in context) {
224             type = typeof context[prop];
225             val = context[prop];
226             if (type === 'function') {
227                 ctx[prop] = makeFakedMethod(context, prop);
228             } else if (type === 'number') {
229                 names[val] = prop;
230                 ctx[prop] = val;
231             } else {
232                 ctx[prop] = val;
233             }
234         }
235         ctx.getNameById = function(i) {
236             return names[i];
237         };
238         return ctx;
239     };
240 
241     var createParentDiv = function() {
242         var div = document.createElement('div');
243         div.style['position'] = 'absolute';
244         div.style['z-index'] = -1;
245         div.style[enchant.ENV.VENDOR_PREFIX + 'TransformOrigin'] = '0 0';
246         return div;
247     };
248 
249     var createGLCanvas = function(width, height, scale) {
250         var cvs = document.createElement('canvas');
251         cvs.width = width;
252         cvs.height = height;
253         cvs.style['position'] = 'absolute';
254         cvs.style['z-index'] = -1;
255         cvs.style[enchant.ENV.VENDOR_PREFIX + 'Transform'] = 'scale(' + scale + ')';
256         cvs.style[enchant.ENV.VENDOR_PREFIX + 'TransformOrigin'] = '0 0';
257         return cvs;
258     };
259 
260     var TextureManager = enchant.Class.create({
261         initialize: function() {
262             this.storage = {};
263         },
264         hasTexture: function(src) {
265             return src in this.storage;
266         },
267         getWebGLTexture: function(image, flip, wrap, mipmap) {
268             var ret;
269             if (this.hasTexture(image.src)) {
270                 ret = this.storage[image.src];
271             } else {
272                 ret = this.createWebGLTexture(image, flip, wrap, mipmap);
273             }
274             return ret;
275         },
276         isPowerOfTwo: function(n) {
277             return (n > 0) && ((n & (n - 1)) === 0);
278         },
279         setTextureParameter: function(power, target, wrap, mipmap) {
280             var filter;
281             if (mipmap) {
282                 filter = gl.LINEAR_MIPMAP_LINEAR;
283             } else {
284                 filter = gl.NEAREST;
285             }
286             if (!power) {
287                 wrap = gl.CLAMP_TO_EDGE;
288             }
289             gl.texParameteri(target, gl.TEXTURE_WRAP_S, wrap);
290             gl.texParameteri(target, gl.TEXTURE_WRAP_T, wrap);
291             gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, filter);
292             gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, filter);
293         },
294         _texImage: function(image, target) {
295             gl.texImage2D(target, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
296         },
297         _writeWebGLTexture: function(image, target, wrap, mipmap) {
298             var power = this.isPowerOfTwo(image.width) && this.isPowerOfTwo(image.height);
299             if (typeof target === 'undefined') {
300                 target = gl.TEXTURE_2D;
301             }
302             if (typeof wrap === 'undefined') {
303                 wrap = gl.REPEAT;
304             }
305             this.setTextureParameter(power, target, wrap, mipmap);
306 
307             this._texImage(image, target);
308             if (mipmap) {
309                 gl.generateMipmap(target);
310             }
311         },
312         createWebGLTexture: function(image, flip, wrap, mipmap) {
313             var tex = gl.createTexture();
314             var target = gl.TEXTURE_2D;
315             gl.bindTexture(target, tex);
316             gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flip);
317             this._writeWebGLTexture(image, target, wrap, mipmap);
318             gl.bindTexture(target, null);
319             this.storage[image.src] = tex;
320             return tex;
321         },
322         createWebGLCubeMapTexture: function(images, wrap, mipmap) {
323             var faceTargets = [
324                 gl.TEXTURE_CUBE_MAP_POSITIVE_X,
325                 gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
326                 gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
327                 gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
328                 gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,
329                 gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
330             ];
331 
332             var tex = gl.createTexture();
333             var target, image;
334             gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex);
335             gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
336             for (var i = 0, l = images.length; i < l; i++) {
337                 target = faceTargets[i];
338                 image = images[i];
339                 this._writeWebGLTexture(image, target, wrap, mipmap);
340             }
341             gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
342             return tex;
343         }
344     });
345 
346     var DetectColorManager = enchant.Class.create({
347         initialize: function() {
348             this.reference = [];
349             this.detectColorNum = 0;
350         },
351         attachDetectColor: function(sprite) {
352             this.detectColorNum += 1;
353             this.reference[this.detectColorNum] = sprite;
354             return this._createNewColor();
355         },
356         _createNewColor: function() {
357             var n = this.detectColorNum;
358             return [
359                 parseInt(n / 65536, 10) / 255,
360                 parseInt(n / 256, 10) / 255,
361                 parseInt(n % 256, 10) / 255, 1.0
362             ];
363         },
364         _decodeDetectColor: function(color) {
365             return Math.floor(color[0] * 65536) +
366                 Math.floor(color[1] * 256) +
367                 Math.floor(color[2]);
368         },
369         getSpriteByColor: function(color) {
370             return this.reference[this._decodeDetectColor(color)];
371         }
372     });
373 
374     /**
375      * @scope enchant.gl.Framebuffer.prototype
376      */
377     enchant.gl.FrameBuffer = enchant.Class.create({
378         /**
379          * WebGLのフレームバッファを管理するクラス.
380          * @param {String} width フレームバッファの横幅
381          * @param {String} height フレームバッファの縦幅
382          * @constructs
383          */
384         initialize: function(width, height) {
385             var core = enchant.Core.instance;
386             if (typeof width === 'undefined') {
387                 width = core.width;
388             }
389             if (typeof height === 'undefined') {
390                 height = core.height;
391             }
392             this.framebuffer = gl.createFramebuffer();
393             this.colorbuffer = gl.createRenderbuffer();
394             this.depthbuffer = gl.createRenderbuffer();
395 
396             gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
397 
398             gl.bindRenderbuffer(gl.RENDERBUFFER, this.colorbuffer);
399             gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, width, height);
400             gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, this.colorbuffer);
401 
402             gl.bindRenderbuffer(gl.RENDERBUFFER, this.depthbuffer);
403             gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
404             gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.depthbuffer);
405 
406             gl.bindFramebuffer(gl.FRAMEBUFFER, null);
407             gl.bindRenderbuffer(gl.RENDERBUFFER, null);
408         },
409         /**
410          * フレームバッファをバインドする.
411          */
412         bind: function() {
413             gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
414         },
415         /**
416          * フレームバッファをアンバインドする.
417          */
418         unbind: function() {
419             gl.bindFramebuffer(gl.FRAMEBUFFER, null);
420         },
421         /**
422          * オブジェクトを破棄する.
423          */
424         destroy: function() {
425             gl.deleteFramebuffer(this.framebuffer);
426             gl.deleteFramebuffer(this.colorbuffer);
427             gl.deleteFramebuffer(this.depthbuffer);
428         }
429     });
430 
431     /**
432      * @scope enchant.gl.Shader.prototype
433      */
434     enchant.gl.Shader = enchant.Class.create({
435         /**
436          * WebGLのシェーダプログラムを管理するクラス.
437          * バーテックスシェーダのソースとフラグメントシェーダのソースを渡すことでシェーダプログラムが作成される.
438          * @param {String} vshader バーテックスシェーダのソース
439          * @param {String} fshader フラグメントシェーダのソース
440          * @constructs
441          */
442         initialize: function(vshader, fshader) {
443             this._vShaderSource = '';
444             this._fShaderSource = '';
445             this._updatedVShaderSource = false;
446             this._updatedFShaderSource = false;
447             this._vShaderProgram = null;
448             this._fShaderProgram = null;
449             this._program = null;
450             this._uniforms = {};
451             this._attributes = {};
452             this._attribLocs = {};
453             this._samplersNum = 0;
454 
455             if (typeof vshader === 'string') {
456                 this.vShaderSource = vshader;
457             }
458             if (typeof fshader === 'string') {
459                 this.fShaderSource = fshader;
460             }
461             if (this._updatedVShaderSource && this._updatedFShaderSource) {
462                 this.compile();
463             }
464         },
465         /**
466          * バーテックスシェーダのソース
467          * @type String
468          */
469         vShaderSource: {
470             get: function() {
471                 return this._vShaderSource;
472             },
473             set: function(string) {
474                 this._vShaderSource = string;
475                 this._updatedVShaderSource = true;
476             }
477         },
478         /**
479          * フラグメントシェーダのソース
480          * @type String
481          */
482         fShaderSource: {
483             get: function() {
484                 return this._fShaderSource;
485             },
486             set: function(string) {
487                 this._fShaderSource = string;
488                 this._updatedFShaderSource = true;
489             }
490         },
491         /**
492          * シェーダプログラムをコンパイルする.
493          * コンストラクタからシェーダソースを渡した場合は自動的にコンパイルされる.
494          * @example
495          * var shader = new Shader();
496          * // シェーダプログラムのソースを渡す.
497          * shader.vShaderSource = vert;
498          * shader.fShaderSource = frag;
499          * // コンパイル.
500          * shader.compile();
501          */
502         compile: function() {
503             if (this._updatedVShaderSource) {
504                 this._prepareVShader();
505             }
506             if (this._updatedFShaderSource) {
507                 this._prepareFShader();
508             }
509             if (this._program === null) {
510                 this._program = gl.createProgram();
511             } else {
512                 gl.detachShader(this._program, this._vShaderProgram);
513                 gl.detachShader(this._program, this._fShaderProgram);
514             }
515             gl.attachShader(this._program, this._vShaderProgram);
516             gl.attachShader(this._program, this._fShaderProgram);
517             gl.linkProgram(this._program);
518             if (!gl.getProgramParameter(this._program, gl.LINK_STATUS)) {
519                 this._logShadersInfo();
520                 throw 'could not compile shader';
521             }
522             this._getAttributesProperties();
523             this._getUniformsProperties();
524         },
525         /**
526          * シェーダプログラムを使用するように設定する.
527          */
528         use: function() {
529             gl.useProgram(this._program);
530         },
531         /**
532          * シェーダプログラムにattribute変数をセットする.
533          * enchant.gl.Sprite3Dの内部などで使用される.
534          * @param {*} 値
535          * @example
536          * var shader = new Shader(vert, frag);
537          * shader.setAttributes({
538          *     aVertexPosition: indices,
539          *     aNormal: normals
540          * });
541          */
542         setAttributes: function(params) {
543             for (var prop in params) {
544                 if (params.hasOwnProperty(prop)) {
545                     this._attributes[prop] = params[prop];
546                 }
547             }
548         },
549         /**
550          * シェーダプログラムにuniform変数をセットする.
551          * enchant.gl.Sprite3Dの内部などで使用される.
552          * @param {*} params 値
553          * @example
554          * var shader = new Shader(vert, frag);
555          * shader.setUniforms({
556          *     uDiffuse: diffuse,
557          *     uLightColor: lightColor
558          * });
559          */
560         setUniforms: function(params) {
561             for (var prop in params) {
562                 if (params.hasOwnProperty(prop)) {
563                     this._uniforms[prop] = params[prop];
564                 }
565             }
566         },
567         _prepareVShader: function() {
568             if (this._vShaderProgram === null) {
569                 this._vShaderProgram = gl.createShader(gl.VERTEX_SHADER);
570             }
571             gl.shaderSource(this._vShaderProgram, this._vShaderSource);
572             gl.compileShader(this._vShaderProgram);
573             this._updatedVShaderSource = false;
574         },
575         _prepareFShader: function() {
576             if (this._fShaderProgram === null) {
577                 this._fShaderProgram = gl.createShader(gl.FRAGMENT_SHADER);
578             }
579             gl.shaderSource(this._fShaderProgram, this._fShaderSource);
580             gl.compileShader(this._fShaderProgram);
581             this._updatedFShaderSource = false;
582         },
583         _logShadersInfo: function() {
584             window['console'].log(gl.getShaderInfoLog(this._vShaderProgram));
585             window['console'].log(gl.getShaderInfoLog(this._fShaderProgram));
586         },
587         _getAttributesProperties: function() {
588             var n;
589             n = gl.getProgramParameter(this._program, gl.ACTIVE_ATTRIBUTES);
590             for (var i = 0; i < n; i++) {
591                 var info = gl.getActiveAttrib(this._program, i);
592                 this._attribLocs[info.name] = i;
593                 addAttributesProperty(this, info);
594             }
595         },
596         _getUniformsProperties: function() {
597             var n;
598             n = gl.getProgramParameter(this._program, gl.ACTIVE_UNIFORMS);
599             for (var i = 0; i < n; i++) {
600                 var info = gl.getActiveUniform(this._program, i);
601                 addUniformsProperty(this, info);
602             }
603         },
604         /**
605          * オブジェクトを破棄する.
606          */
607         destroy: function() {
608             gl.deleteProgram(this._vShaderProgram);
609             gl.deleteProgram(this._fShaderProgram);
610             gl.deleteProgram(this._program);
611         }
612     });
613 
614     var addAttributesProperty = function(program, info) {
615         var name = info.name;
616         var loc = program._attribLocs[name];
617         /**
618          * @type {Object}
619          * @memberOf Object.
620          */
621         var desc = {
622             get: function() {
623                 return 'attrib';
624             },
625             set: (function(loc) {
626                 return function(buf) {
627                     gl.enableVertexAttribArray(loc);
628                     buf._setToAttrib(loc);
629                 };
630             }(loc))
631         };
632         Object.defineProperty(program._attributes, name, desc);
633     };
634 
635     var addUniformsProperty = function(program, info) {
636         var name = (info.name.slice(-3) === '[0]') ? info.name.slice(0, -3) : info.name;
637         var loc = gl.getUniformLocation(program._program, info.name);
638         var suffix;
639         var sampler = false;
640         var matrix = false;
641         /**
642          * @type {Object}
643          */
644         var desc = {
645             get: function() {
646                 return 'uniform';
647             }
648         };
649         switch (info.type) {
650             case gl.FLOAT:
651                 suffix = '1f';
652                 break;
653 
654             case gl.FLOAT_MAT2:
655                 matrix = true;
656                 /* falls through */
657             case gl.FLOAT_VEC2:
658                 suffix = '2fv';
659                 break;
660 
661             case gl.FLOAT_MAT3:
662                 matrix = true;
663                 /* falls through */
664             case gl.FLOAT_VEC3:
665                 suffix = '3fv';
666                 break;
667 
668             case gl.FLOAT_MAT4:
669                 matrix = true;
670                 /* falls through */
671             case gl.FLOAT_VEC4:
672                 suffix = '4fv';
673                 break;
674 
675             case gl.SAMPLER_2D:
676             case gl.SAMPLER_CUBE:
677                 sampler = true;
678                 /* falls through */
679             case gl.INT:
680             case gl.BOOL:
681                 suffix = '1i';
682                 break;
683 
684             case gl.INT_VEC2:
685             case gl.BOOL_VEC2:
686                 suffix = '2iv';
687                 break;
688 
689             case gl.INT_VEC3:
690             case gl.BOOL_VEC3:
691                 suffix = '3iv';
692                 break;
693 
694             case gl.INT_VEC4:
695             case gl.BOOL_VEC4:
696                 suffix = '4iv';
697                 break;
698             default:
699                 throw new Error('no match');
700         }
701         if (matrix) {
702             desc.set = (function(loc, suffix) {
703                 return function(value) {
704                     gl['uniformMatrix' + suffix](loc, false, value);
705                 };
706             }(loc, suffix));
707         } else if (sampler) {
708             desc.set = (function(loc, suffix, samplersNum) {
709                 return function(texture) {
710                     gl.activeTexture(gl.TEXTURE0 + samplersNum);
711                     gl.bindTexture(gl.TEXTURE_2D, texture._glTexture);
712                     gl['uniform' + suffix](loc, samplersNum);
713                 };
714             }(loc, suffix, program._samplersNum));
715             program._samplersNum++;
716         } else {
717             desc.set = (function(loc, suffix) {
718                 return function(value) {
719                     gl['uniform' + suffix](loc, value);
720                 };
721             }(loc, suffix));
722         }
723         Object.defineProperty(program._uniforms, name, desc);
724     };
725 
726     /**
727      * @scope enchant.gl.Quat.prototype
728      */
729     enchant.gl.Quat = enchant.Class.create({
730         /**
731          * クォータニオンを簡単に使用するクラス.
732          * @param {Number} x
733          * @param {Number} y
734          * @param {Number} z
735          * @param {Number} rad
736          * @constructs
737          */
738         initialize: function(x, y, z, rad) {
739             var l = Math.sqrt(x * x + y * y + z * z);
740             if (l) {
741                 x /= l;
742                 y /= l;
743                 z /= l;
744             }
745             var s = Math.sin(rad / 2);
746             var w = Math.cos(rad / 2);
747             this._quat = quat4.create([x * s, y * s, z * s, w]);
748         },
749 
750         /**
751          * クォータニオン同士で球面線形補間を行う.
752          * 自身ともう一つのクォータニオンの間の回転移動を補完したクォータニオンを計算する.
753          * 回転の度合いは0から1の値で表される. 0が自身側, 1がもう一つ側.
754          * 新しいインスタンスが返される.
755          * @param {enchant.gl.Quat} another Quaternion
756          * @param {Number} ratio
757          * @return {enchant.gl.Quat}
758          */
759         slerp: function(another, ratio) {
760             var q = new enchant.gl.Quat(0, 0, 0, 0);
761             quat4.slerp(this._quat, another._quat, ratio, q._quat);
762             return q;
763         },
764         /**
765          * クォータニオン同士で球面線形補間を行う.
766          * 自身ともう一つのクォータニオンの間の回転移動を補完したクォータニオンを計算する.
767          * 回転の度合いは0から1の値で表される. 0が自身側, 1がもう一つ側.
768          * 自身の値が上書きされる.
769          * @param {enchant.gl.Quat} another Quaternion
770          * @param {Number} ratio
771          * @return {enchant.gl.Quat}
772          */
773         slerpApply: function(another, ratio) {
774             quat4.slerp(this._quat, another._quat, ratio);
775             return this;
776         },
777         /**
778          * クォータニオンを回転行列に変換する.
779          * @param {Number[]} matrix
780          * @return {Number[]}
781          */
782         toMat4: function(matrix) {
783             quat4.toMat4(this._quat, matrix);
784             return matrix;
785         },
786         /**
787          * クォータニオンをベクトルに適用する.
788          * @param {Number[]} vector
789          * @return {Number[]}
790          */
791         multiplyVec3: function(vector) {
792             quat4.multiplyVec3(this._quat, vector);
793             return vector;
794         }
795     });
796 
797     /**
798      * @scope enchant.gl.Light3D.prototype
799      */
800     enchant.gl.Light3D = enchant.Class.create(enchant.EventTarget, {
801         /**
802          * 光源クラスの親となるクラス.
803          *
804          * @constructs
805          * @extends enchant.EventTarget
806          */
807         initialize: function() {
808             this._changedColor = true;
809             this._color = [0.8, 0.8, 0.8];
810         },
811 
812         /**
813          * 光源の光の色
814          * @type Number[]
815          */
816         color: {
817             set: function(array) {
818                 this._color = array;
819                 this._changedColor = true;
820             },
821             get: function() {
822                 return this._color;
823             }
824         }
825     });
826 
827     /**
828      * @scope enchant.gl.AmbientLight.prototype
829      */
830     enchant.gl.AmbientLight = enchant.Class.create(enchant.gl.Light3D, {
831         /**
832          * 3Dシーンでの光源を設定するクラス.
833          * 環境光を設定する.
834          * @example
835          *   var scene = new Scene3D();
836          *   var light = new AmbientLight();
837          *   light.color = [1.0, 1.0, 0.0];
838          *   light.directionY = 10;
839          *   scene.setAmbientLight(light);
840          *
841          * @constructs
842          * @extends enchant.gl.Light3D
843          */
844         initialize: function() {
845             enchant.gl.Light3D.call(this);
846         }
847     });
848 
849     /**
850      * @scope enchant.gl.DirectionalLight.prototype
851      */
852     enchant.gl.DirectionalLight = enchant.Class.create(enchant.gl.Light3D, {
853         /**
854          * 3Dシーンでの光源を設定するクラス.
855          * 位置を持たない方向光源.
856          * @example
857          *   var scene = new Scene3D();
858          *   var light = new DirectionalLight();
859          *   light.color = [1.0, 1.0, 0.0];
860          *   light.directionY = 10;
861          *   scene.setDirectionalLight(light);
862          *
863          * @constructs
864          * @extends enchant.gl.Light3D
865          */
866         initialize: function() {
867             enchant.gl.Light3D.call(this);
868             this._directionX = 0.5;
869             this._directionY = 0.5;
870             this._directionZ = 1.0;
871             this._changedDirection = true;
872         }
873     });
874 
875     /**
876      * 光源の照射方向のx成分
877      * @type Number
878      */
879     enchant.gl.DirectionalLight.prototype.directionX = 0.5;
880 
881     /**
882      * 光源の照射方向のy成分
883      * @type Number
884      */
885     enchant.gl.DirectionalLight.prototype.directionY = 0.5;
886 
887     /**
888      * 光源の照射方向のz成分
889      * @type Number
890      */
891     enchant.gl.DirectionalLight.prototype.directionZ = 1.0;
892 
893     /**
894      * 光源の照射方向
895      * @type {Number}
896      */
897     'directionX directionY directionZ'.split(' ').forEach(function(prop) {
898         Object.defineProperty(enchant.gl.DirectionalLight.prototype, prop, {
899             get: function() {
900                 return this['_' + prop];
901             },
902             set: function(n) {
903                 this['_' + prop] = n;
904                 this._changedDirection = true;
905             }
906         });
907         enchant.gl.DirectionalLight.prototype[prop] = 0;
908     });
909 
910     /**
911      * @scope enchant.gl.PointLight.prototype
912      */
913     enchant.gl.PointLight = enchant.Class.create(enchant.gl.Light3D, {
914         /**
915          * 3Dシーンでの光源を設定するクラス.
916          * 方向を持たない点光源.
917          * 現在, シーンに追加しても適用されない.
918          * @example
919          *   var scene = new Scene3D();
920          *   var light = new PointLight();
921          *   light.color = [1.0, 1.0, 0.0];
922          *   light.y = 10;
923          *   scene.addLight(light);
924          *
925          * @constructs
926          * @extends enchant.gl.Light3D
927          */
928         initialize: function() {
929             enchant.gl.Light3D.call(this);
930             this._x = 0;
931             this._y = 0;
932             this._z = 0;
933             this._changedPosition = true;
934         }
935     });
936 
937     /**
938      * 光源のx座標
939      * @type Number
940      */
941     enchant.gl.PointLight.prototype.x = 0;
942 
943     /**
944      * 光源のy座標
945      * @type Number
946      */
947     enchant.gl.PointLight.prototype.y = 0;
948 
949     /**
950      * 光源のz座標
951      * @type Number
952      */
953     enchant.gl.PointLight.prototype.z = 0;
954 
955     'x y z'.split(' ').forEach(function(prop) {
956         Object.defineProperty(enchant.gl.PointLight.prototype, prop, {
957             get: function() {
958                 return this['_' + prop];
959             },
960             set: function(n) {
961                 this['_' + prop] = n;
962                 this._changedPosition = true;
963             }
964         });
965         enchant.gl.PointLight.prototype[prop] = 0;
966     });
967 
968 
969     /**
970      * @scope enchant.gl.Texture.prototype
971      */
972     enchant.gl.Texture = enchant.Class.create({
973         /**
974          * テクスチャ情報を格納するクラス.
975          * @example
976          *   var sprite = new Sprite3D();
977          *   var texture = new Texture();
978          *   texture.src = "http://example.com/texture.png";
979          *   // 以下のようにも宣言できる.
980          *   // var texture = new Texture("http://example.com/texture.png");
981          *   sprite.texture = texture;
982          * @constructs
983          */
984         initialize: function(src, opt) {
985             /**
986              * 環境光のパラメータ
987              * @type Number[]
988              */
989             this.ambient = [ 0.1, 0.1, 0.1, 1.0 ];
990 
991             /**
992              * 拡散光のパラメータ
993              * @type Number[]
994              */
995             this.diffuse = [ 1.0, 1.0, 1.0, 1.0];
996 
997             /**
998              * 光の反射量
999              * @type Number[]
1000              */
1001             this.specular = [ 1.0, 1.0, 1.0, 1.0 ];
1002 
1003             /**
1004              * 光の発光量
1005              * @type Number[]
1006              */
1007             this.emission = [ 0.0, 0.0, 0.0, 1.0 ];
1008 
1009             /**
1010              * 鏡面計数
1011              * @type Number
1012              */
1013             this.shininess = 20;
1014 
1015             this._glTexture = null;
1016             this._image = null;
1017             this._wrap = 10497;
1018             this._mipmap = false;
1019             this._flipY = true;
1020             if (opt) {
1021                 var valid = ['flipY', 'wrap', 'mipmap'];
1022                 for (var prop in opt) {
1023                     if (opt.hasOwnProperty(prop)) {
1024                         if (valid.indexOf(prop) !== -1) {
1025                             this['_' + prop] = opt[prop];
1026                         }
1027                     }
1028                 }
1029             }
1030             if (src) {
1031                 this.src = src;
1032             }
1033         },
1034 
1035         _write: function() {
1036             gl.bindTexture(gl.TEXTURE_2D, this._glTexture);
1037             enchant.Core.instance.GL.textureManager._writeWebGLTexture(this._image, gl.TEXTURE_2D, this._wrap, this._mipmap);
1038             gl.bindTexture(gl.TEXTURE_2D, null);
1039         },
1040 
1041         /**
1042          * テクスチャ画像のソース.
1043          * URLかcore.assets内のデータを指定できる.
1044          * @type String
1045          * @type enchant.Surface
1046          */
1047         src: {
1048             get: function() {
1049                 return this._src;
1050             },
1051             set: function(source) {
1052                 if (typeof source === 'undefined' ||
1053                     source === null) {
1054                     return;
1055                 }
1056                 var that = this;
1057                 var core = enchant.Core.instance;
1058                 var onload = (function(that) {
1059                     return function() {
1060                         that._glTexture = core.GL.textureManager.getWebGLTexture(that._image, that._flipY, that._wrap, that._mipmap);
1061                     };
1062                 }(that));
1063                 if (source instanceof Image) {
1064                     this._image = source;
1065                     onload();
1066                 } else if (source instanceof enchant.Surface) {
1067                     this._image = source._element;
1068                     onload();
1069                 } else if (typeof source === 'string') {
1070                     this._image = new Image();
1071                     this._image.onload = onload;
1072                     this._image.src = source;
1073                 } else {
1074                     this._image = source;
1075                     this._image.src = "c" + Math.random();
1076                     onload();
1077                 }
1078             }
1079         }
1080     });
1081 
1082     /**
1083      * @scope enchant.gl.Buffer.prototype
1084      */
1085     enchant.gl.Buffer = enchant.Class.create({
1086         /**
1087          * 頂点などの配列情報を管理する.
1088          * enchant.gl.Meshのプロパティとして使用される.
1089          * @param {*} params parameter
1090          * @param {Number[]} array
1091          *
1092          * @example
1093          * var index = [ 0, 1, 2, 2, 3, 0 ];
1094          * var indices = new Buffer(Buffer.INDICES, index);
1095          *
1096          * @constructs
1097          */
1098         initialize: function(params, array) {
1099             this._setParams(params);
1100             if (typeof array !== 'undefined') {
1101                 this._array = array;
1102             } else {
1103                 this._array = [];
1104             }
1105             this._buffer = null;
1106         },
1107         /**
1108          * バッファをバインドする.
1109          */
1110         bind: function() {
1111             gl.bindBuffer(this.btype, this._buffer);
1112         },
1113         /**
1114          * バッファをアンバインドする.
1115          */
1116         unbind: function() {
1117             gl.bindBuffer(this.btype, null);
1118         },
1119         _setParams: function(params) {
1120             for (var prop in params) {
1121                 if (params.hasOwnProperty(prop)) {
1122                     this[prop] = params[prop];
1123                 }
1124             }
1125         },
1126         _create: function() {
1127             this._buffer = gl.createBuffer();
1128         },
1129         _delete: function() {
1130             gl.deleteBuffer(this._buffer);
1131         },
1132         _bufferData: function() {
1133             this.bind();
1134             gl.bufferData(this.btype, new this.Atype(this._array), this.usage);
1135             this.unbind();
1136         },
1137         _bufferDataFast: function() {
1138             this.bind();
1139             gl.bufferData(this.btype, this._array, this.usage);
1140             this.unbind();
1141         },
1142         _setToAttrib: function(loc) {
1143             this.bind();
1144             gl.vertexAttribPointer(loc, this.size, this.type, this.norm, this.stride, this.offset);
1145             this.unbind();
1146         },
1147         /**
1148          * オブジェクトを破棄する.
1149          */
1150         destroy: function() {
1151             this._delete();
1152         }
1153     });
1154 
1155     var bufferProto = Object.getPrototypeOf(enchant.gl.Buffer);
1156     bufferProto.VERTICES = bufferProto.NORMALS = {
1157         size: 3,
1158         type: 5126,
1159         norm: false,
1160         stride: 0,
1161         offset: 0,
1162         btype: 34962,
1163         usage: 35044,
1164         Atype: Float32Array
1165     };
1166     bufferProto.TEXCOORDS = {
1167         size: 2,
1168         type: 5126,
1169         normed: false,
1170         stride: 0,
1171         ptr: 0,
1172         btype: 34962,
1173         usage: 35044,
1174         Atype: Float32Array
1175     };
1176     bufferProto.COLORS = {
1177         size: 4,
1178         type: 5126,
1179         normed: false,
1180         stride: 0,
1181         ptr: 0,
1182         btype: 34962,
1183         usage: 35044,
1184         Atype: Float32Array
1185     };
1186     bufferProto.INDICES = {
1187         size: 3,
1188         type: 5123,
1189         normed: false,
1190         stride: 0,
1191         offset: 0,
1192         btype: 34963,
1193         usage: 35044,
1194         Atype: Uint16Array
1195     };
1196 
1197     /**
1198      * @scope enchant.gl.Mesh.prototype
1199      */
1200     enchant.gl.Mesh = enchant.Class.create({
1201         /**
1202          * 頂点配列やテクスチャを格納するクラス.
1203          * enchant.gl.Sprite3Dのプロパティとして使用される.
1204          * @constructs
1205          */
1206         initialize: function() {
1207             this.__count = 0;
1208             this._appear = false;
1209             this._vertices = new enchant.gl.Buffer(enchant.gl.Buffer.VERTICES);
1210             this._normals = new enchant.gl.Buffer(enchant.gl.Buffer.NORMALS);
1211             this._colors = new enchant.gl.Buffer(enchant.gl.Buffer.COLORS);
1212             this._texCoords = new enchant.gl.Buffer(enchant.gl.Buffer.TEXCOORDS);
1213             this._indices = new enchant.gl.Buffer(enchant.gl.Buffer.INDICES);
1214             this.texture = new enchant.gl.Texture();
1215         },
1216         /**
1217          * Meshの色を変更する.
1218          * Mesh.colorsを指定した色の頂点配列にする.
1219          * @param {Number[]|String} color z z軸方向の平行移動量
1220          * @example
1221          *   var sprite = new Sprite3D();
1222          *   //紫色に設定. どれも同じ結果が得られる.
1223          *   sprite.mesh.setBaseColor([1.0, 0.0, 1.0, 0.0]);
1224          *   sprite.mesh.setBaseColor('#ff00ff');
1225          *   sprite.mesh.setBaseColor('rgb(255, 0, 255');
1226          *   sprite.mesh.setBaseColor('rgba(255, 0, 255, 1.0');
1227          */
1228         setBaseColor: function(color) {
1229             var c = enchant.Core.instance.GL.parseColor(color);
1230             var newColors = [];
1231             for (var i = 0, l = this.vertices.length / 3; i < l; i++) {
1232                 Array.prototype.push.apply(newColors, c);
1233             }
1234             this.colors = newColors;
1235         },
1236         /**
1237          * メッシュの面の向きと法線の向きを反転させる.
1238          */
1239         reverse: function() {
1240             var norm = this.normals;
1241             var idx = this.indices;
1242             var t, i, l;
1243             for (i = 0, l = norm.length; i < l; i++) {
1244                 norm[i] *= -1;
1245             }
1246             for (i = 0, l = idx.length; i < l; i += 3) {
1247                 t = idx[i + 1];
1248                 idx[i + 1] = idx[i + 2];
1249                 idx[i + 2] = t;
1250             }
1251             this._normals._bufferData();
1252             this._indices._bufferData();
1253         },
1254         _createBuffer: function() {
1255             for (var prop in this) {
1256                 if (this.hasOwnProperty(prop)) {
1257                     if (this[prop] instanceof enchant.gl.Buffer) {
1258                         this[prop]._create();
1259                         this[prop]._bufferData();
1260                     }
1261                 }
1262             }
1263         },
1264         _deleteBuffer: function() {
1265             for (var prop in this) {
1266                 if (this.hasOwnProperty(prop)) {
1267                     if (this[prop] instanceof enchant.gl.Buffer) {
1268                         this[prop]._delete();
1269                     }
1270                 }
1271             }
1272         },
1273         _controlBuffer: function() {
1274             if (this._appear) {
1275                 if (this.__count <= 0) {
1276                     this._appear = false;
1277                     this._deleteBuffer();
1278                 }
1279             } else {
1280                 if (this.__count > 0) {
1281                     this._appear = true;
1282                     this._createBuffer();
1283                 }
1284             }
1285         },
1286         /**
1287          * @type {Number}
1288          */
1289         _count: {
1290             get: function() {
1291                 return this.__count;
1292             },
1293             set: function(c) {
1294                 this.__count = c;
1295                 this._controlBuffer();
1296             }
1297         },
1298         _join: function(another, ox, oy, oz) {
1299             var triangles = this.vertices.length / 3,
1300                 vertices = this.vertices.slice(0),
1301                 i, l;
1302             for (i = 0, l = another.vertices.length; i < l; i += 3) {
1303                 vertices.push(another.vertices[i] + ox);
1304                 vertices.push(another.vertices[i + 1] + oy);
1305                 vertices.push(another.vertices[i + 2] + oz);
1306             }
1307             this.vertices = vertices;
1308             this.normals = this.normals.concat(another.normals);
1309             this.texCoords = this.texCoords.concat(another.texCoords);
1310             this.colors = this.colors.concat(another.colors);
1311             var indices = this.indices.slice(0);
1312             for (i = 0, l = another.indices.length; i < l; i++) {
1313                 indices.push(another.indices[i] + triangles);
1314             }
1315             this.indices = indices;
1316         },
1317         /**
1318          * オブジェクトを破棄する.
1319          */
1320         destroy: function() {
1321             this._deleteBuffer();
1322         }
1323     });
1324 
1325     /**
1326      * Meshの頂点配列.
1327      * 3つの要素を一組として頂点を指定する. 全体の要素数は, 頂点の個数nに対して3nとなる.
1328      * 3n, 3n+1, 3n+2番目の要素はそれぞれ, n番目の頂点のx, y, z座標である.
1329      * @example
1330      *   var sprite = new Sprite3D();
1331      *   //頂点配列を代入
1332      *   //データはx, y, z, x, y, z...の順に格納する
1333      *   sprite.mesh.vertices = [
1334      *       0.0, 0.0, 0.0,  //0番目の頂点(0.0, 0.0, 0.0)
1335      *       1.0, 0.0, 0.0,  //1番目の頂点(1.0, 0.0, 0.0)
1336      *       1.0, 1.0, 0.0,  //2番目の頂点(1.0, 1.0, 0.0)
1337      *       0.0, 1.0, 0.0   //3番目の頂点(0.0, 1.0, 0.0)
1338      *   ];
1339      * @type Number[]
1340      * @see enchant.gl.Mesh#indices
1341      * @see enchant.gl.Mesh#normals
1342      * @see enchant.gl.Mesh#texCoords
1343      */
1344     enchant.gl.Mesh.prototype.vertices = [];
1345 
1346     /**
1347      * Meshの頂点法線ベクトル配列.
1348      * 3つの要素を一組として法線ベクトルを指定する. 全体の要素数は, 法線ベクトルの個数nに対して3nとなる.
1349      * 3n, 3n+1, 3n+2番目の要素はそれぞれ, n番目の頂点の法線ベクトルのx, y, z成分である.
1350      * 法線ベクトルはライティングの影の計算に利用される.
1351      * @example
1352      *   var sprite = new Sprite3D();
1353      *   //頂点配列を代入
1354      *   //データはx, y, z, x, y, z...の順に格納する
1355      *   sprite.mesh.vertices = [
1356      *       0.0, 0.0, 0.0,  //0番目の頂点(0.0, 0.0, 0.0)
1357      *       1.0, 0.0, 0.0,  //1番目の頂点(1.0, 0.0, 0.0)
1358      *       1.0, 1.0, 0.0,  //2番目の頂点(1.0, 1.0, 0.0)
1359      *       0.0, 1.0, 0.0   //3番目の頂点(0.0, 1.0, 0.0)
1360      *   ];
1361      *
1362      *   //法線ベクトル配列を代入
1363      *   //データはx, y, z, x, y, z...の順に格納する
1364      *   sprite.normals = [
1365      *       0.0, 0.0, 0.0,  //0番目の頂点の法線ベクトル(0.0, 0.0, 0.0)
1366      *       1.0, 0.0, 0.0,  //1番目の頂点の法線ベクトル(1.0, 0.0, 0.0)
1367      *       1.0, 1.0, 0.0,  //2番目の頂点の法線ベクトル(1.0, 1.0, 0.0)
1368      *       0.0, 1.0, 0.0   //3番目の頂点の法線ベクトル(0.0, 1.0, 0.0)
1369      *   ];
1370      * @type Number[]
1371      * @see enchant.gl.Mesh#vertices
1372      * @see enchant.gl.Mesh#indices
1373      * @see enchant.gl.Mesh#texCoords
1374      */
1375     enchant.gl.Mesh.prototype.normals = [];
1376 
1377     /**
1378      * Meshのテクスチャマッピング配列.
1379      * 2つの要素を一組としてuv座標を指定する. 全体の要素数は, 頂点の個数nに対して2nとなる.
1380      * 2n, 2n+1番目の要素はそれぞれ, n番目の頂点のテクスチャのu, v座標である.
1381      * それぞれの座標のとりうる値は0<=u,v<=1である.
1382      * @example
1383      *   var sprite = new Sprite3D();
1384      *   var texture = new Texture();
1385      *   texture.src = "texture.png";
1386      *   sprite.mesh.texture = texture;
1387      *
1388      *   //頂点配列を代入
1389      *   //データはx, y, z, x, y, z...の順に格納する
1390      *   sprite.mesh.vertices = [
1391      *       0.0, 0.0, 0.0,  //0番目の頂点(0.0, 0.0, 0.0)
1392      *       1.0, 0.0, 0.0,  //1番目の頂点(1.0, 0.0, 0.0)
1393      *       1.0, 1.0, 0.0,  //2番目の頂点(1.0, 1.0, 0.0)
1394      *       0.0, 1.0, 0.0   //3番目の頂点(0.0, 1.0, 0.0)
1395      *   ];
1396      *
1397      *   //uv座標配列を代入
1398      *   //データはu, v, u, v...の順に格納する
1399      *   sprite.mesh.texCoords = [
1400      *       0.0, 0.0,  //0番目の頂点のuv座標(0.0, 0.0)
1401      *       1.0, 0.0,  //1番目の頂点のuv座標(1.0, 0.0)
1402      *       1.0, 1.0,  //2番目の頂点のuv座標(1.0, 1.0)
1403      *       0.0, 1.0   //3番目の頂点のuv座標(0.0, 1.0)
1404      *   ];
1405      * @type Number[]
1406      * @see enchant.gl.Mesh#vertices
1407      * @see enchant.gl.Mesh#indices
1408      * @see enchant.gl.Mesh#normals
1409      * @see enchant.gl.Mesh#texture#
1410      */
1411     enchant.gl.Mesh.prototype.texCoords = [];
1412 
1413     /**
1414      * Meshの頂点インデックス配列.
1415      * 3つの要素を一組として三角形を指定する.全体の要素数は, 三角形の個数nに対して3nとなる.
1416      * インデックスの値は, {@link enchant.gl.Mesh#vertices}で指定した頂点の番号である.
1417      * @example
1418      *   var sprite = new Sprite3D();
1419      *   //頂点配列を代入
1420      *   //データはx, y, z, x, y, z...の順に格納する
1421      *   sprite.vertices = [
1422      *       0.0, 0.0, 0.0,  //0番目の頂点(0.0, 0.0, 0.0)
1423      *       1.0, 0.0, 0.0,  //1番目の頂点(1.0, 0.0, 0.0)
1424      *       1.0, 1.0, 0.0,  //2番目の頂点(1.0, 1.0, 0.0)
1425      *       0.0, 1.0, 0.0   //3番目の頂点(0.0, 1.0, 0.0)
1426      *   ];
1427      *
1428      *   //頂点インデックスを代入
1429      *   //3要素一組として, 三角形を描画する
1430      *   //この例では(0,0,0), (1,0,0), (1,1,0)の三角形と
1431      *   //(1,1,0), (0,1,0), (0,0,0)の三角形の計二つを描画する
1432      *   sprite.indices = [
1433      *       0, 1, 2,
1434      *       2, 3, 0
1435      *   ];
1436      *   var scene = new Scene3D();
1437      *   scene.addChild(sprite);
1438      * @type Integer[]
1439      * @see enchant.gl.Mesh#vertices
1440      * @see enchant.gl.Mesh#normals
1441      * @see enchant.gl.Mesh#texCoords
1442      */
1443     enchant.gl.Mesh.prototype.indices = [];
1444 
1445     /**
1446      * Meshの頂点色配列.
1447      * 4つの要素を一組として頂点色を指定する. 全体の要素数は, 頂点の個数nに対して4nとなる.
1448      * 4n, 4n+1, 4n+2, 4n+3番目の要素はそれぞれ, n番目の頂点の色のr, g, b, a成分である.
1449      * 頂点色はMeshのtextureにテクスチャが割り当てられていない場合の描画に使用される.
1450      * {@link enchant.gl.Mesh#setBaseColor}で一括して変更することができる.
1451      * @example
1452      *   var sprite = new Sprite3D();
1453      *   //頂点配列を代入
1454      *   //データはx, y, z, x, y, z...の順に格納する
1455      *   sprite.mesh.vertices = [
1456      *       0.0, 0.0, 0.0,  //0番目の頂点(0.0, 0.0, 0.0)
1457      *       1.0, 0.0, 0.0,  //1番目の頂点(1.0, 0.0, 0.0)
1458      *       1.0, 1.0, 0.0,  //2番目の頂点(1.0, 1.0, 0.0)
1459      *       0.0, 1.0, 0.0   //3番目の頂点(0.0, 1.0, 0.0)
1460      *   ];
1461      *
1462      *   //頂点色配列を代入
1463      *   //データはr, g, b, ,a, r, g, b, a...の順に格納する
1464      *   sprite.mesh.normals = [
1465      *       0.0, 0.0, 1.0, 1.0, //0番目の頂点の色(0.0, 0.0, 1.0, 1.0)
1466      *       0.0, 1.0, 0.0, 1.0, //1番目の頂点の色(0.0, 1.0, 0.0, 1.0)
1467      *       0.0, 1.0, 1.0, 1.0, //2番目の頂点の色(0.0, 1.0, 1.0, 1.0)
1468      *       1.0, 0.0, 0.0, 1.0  //3番目の頂点の色(1.0, 0.0, 0.0, 1.0)
1469      *   ];
1470      * @type Number[]
1471      * @see enchant.gl.Mesh#setBaseColor
1472      */
1473     enchant.gl.Mesh.prototype.colors = [];
1474 
1475     'vertices normals colors texCoords indices'.split(' ').forEach(function(prop) {
1476         Object.defineProperty(enchant.gl.Mesh.prototype, prop, {
1477             get: function() {
1478                 return this['_' + prop]._array;
1479             },
1480             set: function(array) {
1481                 this['_' + prop]._array = array;
1482                 if (this._appear) {
1483                     this['_' + prop]._bufferData();
1484                 }
1485             }
1486         });
1487     });
1488 
1489     /**
1490      * @scope enchant.gl.Sprite3D.prototype
1491      */
1492     enchant.gl.Sprite3D = enchant.Class.create(enchant.EventTarget, {
1493         /**
1494          * Sprite3D表示機能を持ったクラス.
1495          * <p>{@link enchant.gl.Scene3D}のインスタンスに追加することで, 画面上に表示することができる.
1496          * {@link enchant.gl.Sprite3D#vertices}, {@link enchant.gl.Sprite3D#indices},
1497          * {@link enchant.gl.Sprite3D#normals}などを変更することで, 任意のSprite3Dを描画することもでき,
1498          * テクスチャなども貼付けることができる.</p>
1499          * <p>また, Sprite3Dを子として追加することも可能で, 子は全て親を基準とした座標系で描画される.</p>
1500          * @example
1501          *   //シーンの初期化
1502          *   var scene = new Scene3D();
1503          *   //Sprite3Dの初期化
1504          *   var sprite = new Sprite3D();
1505          *   //Sprite3Dをシーンに追加
1506          *   scene.addChild(sprite);
1507          * @constructs
1508          * @extends enchant.EventTarget
1509          */
1510         initialize: function() {
1511             enchant.EventTarget.call(this);
1512 
1513             /**
1514              * 子Sprite3D要素の配列.
1515              * この要素に子として追加されているSprite3Dの一覧を取得できる.
1516              * 子を追加したり削除したりする場合には, この配列を直接操作せずに,
1517              * {@link enchant.gl.Sprite3D#addChild}や{@link enchant.gl.Sprite3D#removeChild}を利用する.
1518              * @type enchant.gl.Sprite3D[]
1519              * @see enchant.gl.Sprite3D#addChild
1520              * @see enchant.gl.Sprite3D#removeChild
1521              */
1522             this.childNodes = [];
1523 
1524             /**
1525              * このSprite3Dが現在追加されているシーンオブジェクト.
1526              * どのシーンにも追加されていないときにはnull.
1527              * @type enchant.gl.Scene3D
1528              * @see enchant.gl.Scene3D#addChild
1529              */
1530             this.scene = null;
1531 
1532             /**
1533              * Sprite3Dの親要素.
1534              * 親が存在しない場合にはnull.
1535              * @type enchant.gl.Sprite3D|enchant.gl.Scene3D
1536              */
1537             this.parentNode = null;
1538 
1539             /**
1540              * Sprite3Dに適用されるメッシュオブジェクト.
1541              * @type enchant.gl.Mesh
1542              * @example
1543              *   var sprite = new Sprite3D();
1544              *   sprite.mesh = new Mesh();
1545              */
1546             this._mesh = null;
1547 
1548             this.program = null;
1549 
1550             this.bounding = new enchant.gl.collision.BS();
1551             this.bounding.parent = this;
1552 
1553             this.age = 0;
1554 
1555             this._x = 0;
1556             this._y = 0;
1557             this._z = 0;
1558             this._scaleX = 1;
1559             this._scaleY = 1;
1560             this._scaleZ = 1;
1561             this._changedTranslation = true;
1562             this._changedRotation = true;
1563             this._changedScale = true;
1564             this._touchable = true;
1565 
1566             this._global = vec3.create();
1567             this.globalX = 0;
1568             this.globalY = 0;
1569             this.globalZ = 0;
1570 
1571             this._matrix = mat4.identity();
1572             this.tmpMat = mat4.identity();
1573             this.modelMat = mat4.identity();
1574             this._rotation = mat4.identity();
1575             this._normMat = mat3.identity();
1576 
1577             var core = enchant.Core.instance;
1578             this.detectColor = core.GL.detectColorManager.attachDetectColor(this);
1579 
1580             var parentEvent = function(e) {
1581                 if (this.parentNode instanceof enchant.gl.Sprite3D) {
1582                     this.parentNode.dispatchEvent(e);
1583                 }
1584             };
1585             this.addEventListener('touchstart', parentEvent);
1586             this.addEventListener('touchmove', parentEvent);
1587             this.addEventListener('touchend', parentEvent);
1588 
1589             var added = function(e) {
1590                 if (this.mesh !== null) {
1591                     this.mesh._count++;
1592                 }
1593                 if (this.childNodes.length) {
1594                     for (var i = 0, l = this.childNodes.length; i < l; i++) {
1595                         this.childNodes[i].scene = this.scene;
1596                         this.childNodes[i].dispatchEvent(e);
1597                     }
1598                 }
1599             };
1600             this.addEventListener('addedtoscene', added);
1601 
1602             var removed = function(e) {
1603                 if (this.mesh !== null) {
1604                     this.mesh._count--;
1605                 }
1606                 if (this.childNodes.length) {
1607                     for (var i = 0, l = this.childNodes.length; i < l; i++) {
1608                         this.childNodes[i].scene = null;
1609                         this.childNodes[i].dispatchEvent(e);
1610                     }
1611                 }
1612             };
1613             this.addEventListener('removedfromscene', removed);
1614 
1615         },
1616 
1617         /**
1618          * Sprite3Dの複製を作成する.
1619          * 位置,回転行列などがコピーされた新しいインスタンスが返される.
1620          * @example
1621          *   var sp = new Sprite3D();
1622          *   sp.x = 15;
1623          *   var sp2 = sp.clone();
1624          *   //sp2.x = 15;
1625          * @return {enchant.gl.Sprite3D}
1626          */
1627         clone: function() {
1628             var clone = new enchant.gl.Sprite3D();
1629             for (var prop in this) {
1630                 if (typeof this[prop] === 'number' ||
1631                     typeof this[prop] === 'string') {
1632                     clone[prop] = this[prop];
1633                 } else if (this[prop] instanceof WebGLBuffer) {
1634                     clone[prop] = this[prop];
1635                 } else if (this[prop] instanceof Float32Array) {
1636                     clone[prop] = new Float32Array(this[prop]);
1637                 } else if (this[prop] instanceof Array &&
1638                     prop !== 'childNodes' &&
1639                     prop !== 'detectColor') {
1640                     clone[prop] = this[prop].slice(0);
1641                 }
1642             }
1643             if (this.mesh !== null) {
1644                 clone.mesh = this.mesh;
1645             }
1646             if (this.childNodes) {
1647                 for (var i = 0, l = this.childNodes.length; i < l; i++) {
1648                     clone.addChild(this.childNodes[i].clone());
1649                 }
1650             }
1651             return clone;
1652         },
1653 
1654         /**
1655          * 他のSprite3Dの状態をセットする.
1656          * Colladaファイルを読み込んだassetsに対して使用できる.
1657          * @example
1658          *   var sp = new Sprite3D();
1659          *   sp.set(core.assets['sample.dae']);
1660          *   //sample.daeのモデル情報を持ったSprite3Dになる
1661          */
1662         set: function(sprite) {
1663             for (var prop in sprite) {
1664                 if (typeof sprite[prop] === 'number' ||
1665                     typeof sprite[prop] === 'string') {
1666                     this[prop] = sprite[prop];
1667                 } else if (sprite[prop] instanceof WebGLBuffer) {
1668                     this[prop] = sprite[prop];
1669                 } else if (sprite[prop] instanceof Float32Array) {
1670                     this[prop] = new Float32Array(sprite[prop]);
1671                 } else if (sprite[prop] instanceof Array &&
1672                     prop !== 'childNodes' &&
1673                     prop !== 'detectColor') {
1674                     this[prop] = sprite[prop].slice(0);
1675                 }
1676             }
1677             if (sprite.mesh !== null) {
1678                 this.mesh = sprite.mesh;
1679             }
1680             if (sprite.childNodes) {
1681                 for (var i = 0, l = sprite.childNodes.length; i < l; i++) {
1682                     this.addChild(sprite.childNodes[i].clone());
1683                 }
1684             }
1685         },
1686 
1687         /**
1688          * 子Sprite3Dを追加する.
1689          * 追加が完了すると, 子Sprite3Dに対してaddedイベントが発生する.
1690          * 親が既にシーンに追加されていた場合には, そのシーンに追加され,
1691          * addedtosceneイベントが発生する.
1692          * @param {enchant.gl.Sprite3D} sprite 追加する子Sprite3D.
1693          * @example
1694          *   var parent = new Sprite3D();
1695          *   var child = new Sprite3D();
1696          *   //Sprite3Dを別のSprite3Dに子として追加
1697          *   parent.addChild(child);
1698          * @see enchant.gl.Sprite3D#removeChild
1699          * @see enchant.gl.Sprite3D#childNodes
1700          * @see enchant.gl.Sprite3D#parentNode
1701          */
1702         addChild: function(sprite) {
1703             this.childNodes.push(sprite);
1704             sprite.parentNode = this;
1705             sprite.dispatchEvent(new enchant.Event('added'));
1706             if (this.scene) {
1707                 sprite.scene = this.scene;
1708                 sprite.dispatchEvent(new enchant.Event('addedtoscene'));
1709             }
1710         },
1711 
1712         /**
1713          * 指定された子Sprite3Dを削除する.
1714          * 削除が完了すると, 子Sprite3Dに対してremovedイベントが発生する.
1715          * シーンに追加されていた場合には, そのシーンからも削除され,
1716          * removedfromsceneイベントが発生する.
1717          * @param {enchant.gl.Sprite3D} sprite 削除する子Sprite3D.
1718          * @example
1719          *   var scene = new Scene3D();
1720          *   //sceneの一番目の子を削除
1721          *   scene.removeChild(scene.childNodes[0]);
1722          * @see enchant.gl.Sprite3D#addChild
1723          * @see enchant.gl.Sprite3D#childNodes
1724          * @see enchant.gl.Sprite3D#parentNode
1725          */
1726         removeChild: function(sprite) {
1727             var i;
1728             if ((i = this.childNodes.indexOf(sprite)) !== -1) {
1729                 this.childNodes.splice(i, 1);
1730                 sprite.parentNode = null;
1731                 sprite.dispatchEvent(new enchant.Event('removed'));
1732                 if (this.scene) {
1733                     sprite.scene = null;
1734                     sprite.dispatchEvent(new enchant.Event('removedfromscene'));
1735                 }
1736             }
1737         },
1738 
1739 
1740         /**
1741          * 他のオブジェクトとの衝突判定.
1742          * 衝突判定オブジェクトか, x, y, zプロパティを持っているオブジェクトとの衝突を判定することができる.
1743          * @param {enchant.gl.Sprite3D} bounding 対象のオブジェクト
1744          * @return {Boolean}
1745          */
1746         intersect: function(another) {
1747             return this.bounding.intersect(another.bounding);
1748         },
1749 
1750         /**
1751          * Sprite3Dを平行移動する.
1752          * 現在表示されている位置から, 各軸に対して指定された分だけ平行移動をする.
1753          * @param {Number} x x軸方向の平行移動量
1754          * @param {Number} y y軸方向の平行移動量
1755          * @param {Number} z z軸方向の平行移動量
1756          * @example
1757          *   var sprite = new Sprite3D();
1758          *   //x軸方向に10, y軸方向に3, z軸方向に-20平行移動
1759          *   sprite.translate(10, 3, -20);
1760          * @see enchant.gl.Sprite3D#x
1761          * @see enchant.gl.Sprite3D#y
1762          * @see enchant.gl.Sprite3D#z
1763          * @see enchant.gl.Sprite3D#scale
1764          */
1765         translate: function(x, y, z) {
1766             this._x += x;
1767             this._y += y;
1768             this._z += z;
1769             this._changedTranslation = true;
1770         },
1771 
1772         /**
1773          * Sprite3DをローカルのZ軸方向に動かす.
1774          * @param {Number} speed
1775          */
1776         forward: function(speed) {
1777             var x = this._rotation[8] * speed;
1778             var y = this._rotation[9] * speed;
1779             var z = this._rotation[10] * speed;
1780             this.translate(x, y, z);
1781         },
1782 
1783         /**
1784          * Sprite3DをローカルのX軸方向に動かす.
1785          * @param {Number} speed
1786          */
1787         sidestep: function(speed) {
1788             var x = this._rotation[0] * speed;
1789             var y = this._rotation[1] * speed;
1790             var z = this._rotation[2] * speed;
1791             this.translate(x, y, z);
1792         },
1793 
1794         /**
1795          * Sprite3DをローカルのY軸方向に動かす.
1796          * @param {Number} speed
1797          */
1798         altitude: function(speed) {
1799             var x = this._rotation[4] * speed;
1800             var y = this._rotation[5] * speed;
1801             var z = this._rotation[6] * speed;
1802             this.translate(x, y, z);
1803         },
1804 
1805         /**
1806          * Sprite3Dを拡大縮小する.
1807          * 現在の拡大率から, 各軸に対して指定された倍率分だけ拡大縮小をする.
1808          * @param {Number} x x軸方向の拡大率
1809          * @param {Number} y y軸方向の拡大率
1810          * @param {Number} z z軸方向の拡大率
1811          * @example
1812          *   var sprite = new Sprite3D();
1813          *   //x軸方向に2.0倍, y軸方向に3.0倍, z軸方向に0.5倍に拡大する
1814          *   sprite.scale(2,0, 3.0, 0.5);
1815          * @see enchant.gl.Sprite3D#scaleX
1816          * @see enchant.gl.Sprite3D#scaleY
1817          * @see enchant.gl.Sprite3D#scaleZ
1818          * @see enchant.gl.Sprite3D#translate
1819          */
1820         scale: function(x, y, z) {
1821             this._scaleX *= x;
1822             this._scaleY *= y;
1823             this._scaleZ *= z;
1824             this._changedScale = true;
1825         },
1826 
1827         /**
1828          * Sprite3Dの名前
1829          * @type String
1830          */
1831         name: {
1832             get: function() {
1833                 return this._name;
1834             },
1835             set: function(name) {
1836                 this._name = name;
1837             }
1838         },
1839 
1840         /**
1841          * Sprite3Dの回転行列.
1842          * 配列は長さ16の一次元配列であり, 行優先の4x4行列として解釈される.
1843          * @example
1844          *   var sprite = new Sprite3D();
1845          *   //x軸周りに45度回転
1846          *   var rotX = Math.PI() / 4;
1847          *   sprite.rotation = [
1848          *       1, 0, 0, 0,
1849          *       0, Math.cos(rotX), -Math.sin(rotX), 0,
1850          *       0, Math.sin(rotX), Math.cos(rotX), 0,
1851          *       0, 0, 0, 1
1852          *   ];
1853          * @type Number[]
1854          */
1855         rotation: {
1856             get: function() {
1857                 return this._rotation;
1858             },
1859             set: function(rotation) {
1860                 this._rotation = rotation;
1861                 this._changedRotation = true;
1862             }
1863         },
1864 
1865         /**
1866          * 回転行列にクォータニオンから得られる回転行列をセットする.
1867          * @param {enchant.gl.Quat} quat
1868          */
1869         rotationSet: function(quat) {
1870             quat.toMat4(this._rotation);
1871             this._changedRotation = true;
1872         },
1873 
1874         /**
1875          * 回転行列にクォータニオンから得られる回転行列を適用する.
1876          * @param {enchant.gl.Quat} quat
1877          */
1878         rotationApply: function(quat) {
1879             quat.toMat4(this.tmpMat);
1880             mat4.multiply(this._rotation, this.tmpMat);
1881             this._changedRotation = true;
1882         },
1883 
1884         /**
1885          * Sprite3DのローカルのZ軸を軸に回転させる.
1886          * @param {Number} radius
1887          */
1888         rotateRoll: function(rad) {
1889             this.rotationApply(new enchant.gl.Quat(0, 0, 1, rad));
1890             this._changedRotation = true;
1891         },
1892 
1893         /**
1894          * Sprite3DのローカルのX軸を軸に回転させる.
1895          * @param {Number} radius
1896          */
1897         rotatePitch: function(rad) {
1898             this.rotationApply(new enchant.gl.Quat(1, 0, 0, rad));
1899             this._changedRotation = true;
1900         },
1901 
1902         /**
1903          * Sprite3DのローカルのY軸を軸に回転させる.
1904          * @param {Number} radius
1905          */
1906         rotateYaw: function(rad) {
1907             this.rotationApply(new enchant.gl.Quat(0, 1, 0, rad));
1908             this._changedRotation = true;
1909         },
1910 
1911         /**
1912          * @type {enchant.gl.Mesh}
1913          */
1914         mesh: {
1915             get: function() {
1916                 return this._mesh;
1917             },
1918             set: function(mesh) {
1919                 if (this.scene !== null) {
1920                     this._mesh._count -= 1;
1921                     mesh._count += 1;
1922                 }
1923                 this._mesh = mesh;
1924             }
1925         },
1926 
1927         /**
1928          * Sprite3Dに適用する変換行列.
1929          * @deprecated
1930          * @type Number[]
1931          */
1932         matrix: {
1933             get: function() {
1934                 return this._matrix;
1935             },
1936             set: function(matrix) {
1937                 this._matrix = matrix;
1938             }
1939         },
1940 
1941         /**
1942          * Sprite3Dの当たり判定に利用されるオブジェクト.
1943          * @type enchant.gl.Bounding | enchant.gl.BS | enchant.gl.AABB
1944          */
1945         bounding: {
1946             get: function() {
1947                 return this._bounding;
1948             },
1949             set: function(bounding) {
1950                 this._bounding = bounding;
1951                 this._bounding.parent = this;
1952             }
1953         },
1954 
1955         /**
1956          * Sprite3Dをタッチ可能にするか決定する.
1957          * falseに設定するとタッチ判定の際無視される.
1958          * @type bool
1959          */
1960         touchable: {
1961             get: function() {
1962                 return this._touchable;
1963             },
1964             set: function(bool) {
1965                 this._touchable = bool;
1966                 if (this._touchable) {
1967                     this.detectColor[3] = 1.0;
1968                 } else {
1969                     this.detectColor[3] = 0.0;
1970                 }
1971             }
1972         },
1973 
1974         _transform: function(baseMatrix) {
1975             if (this._changedTranslation ||
1976                 this._changedRotation ||
1977                 this._changedScale) {
1978                 mat4.identity(this.modelMat);
1979                 mat4.translate(this.modelMat, [this._x, this._y, this._z]);
1980                 mat4.multiply(this.modelMat, this._rotation, this.modelMat);
1981                 mat4.scale(this.modelMat, [this._scaleX, this._scaleY, this._scaleZ]);
1982                 mat4.multiply(this.modelMat, this._matrix, this.modelMat);
1983                 this._changedTranslation = false;
1984                 this._changedRotation = false;
1985                 this._changedScale = false;
1986             }
1987 
1988             mat4.multiply(baseMatrix, this.modelMat, this.tmpMat);
1989 
1990             this._global[0] = this._x;
1991             this._global[1] = this._y;
1992             this._global[2] = this._z;
1993             mat4.multiplyVec3(this.tmpMat, this._global);
1994             this.globalX = this._global[0];
1995             this.globalY = this._global[1];
1996             this.globalZ = this._global[2];
1997         },
1998 
1999         _render: function(detectTouch) {
2000             var useTexture = this.mesh.texture._image ? 1.0 : 0.0;
2001 
2002             mat4.toInverseMat3(this.tmpMat, this._normMat);
2003             mat3.transpose(this._normMat);
2004 
2005             var attributes = {
2006                 aVertexPosition: this.mesh._vertices,
2007                 aVertexColor: this.mesh._colors,
2008                 aNormal: this.mesh._normals,
2009                 aTextureCoord: this.mesh._texCoords
2010             };
2011 
2012             var uniforms = {
2013                 uModelMat: this.tmpMat,
2014                 uDetectColor: this.detectColor,
2015                 uSpecular: this.mesh.texture.specular,
2016                 uDiffuse: this.mesh.texture.diffuse,
2017                 uEmission: this.mesh.texture.emission,
2018                 uAmbient: this.mesh.texture.ambient,
2019                 uShininess: this.mesh.texture.shininess,
2020                 uNormMat: this._normMat,
2021                 uSampler: this.mesh.texture,
2022                 uUseTexture: useTexture
2023             };
2024 
2025             var length = this.mesh.indices.length;
2026             enchant.Core.instance.GL.renderElements(this.mesh._indices, 0, length, attributes, uniforms);
2027         },
2028 
2029         _draw: function(scene, detectTouch, baseMatrix) {
2030 
2031             this._transform(baseMatrix);
2032 
2033             if (this.childNodes.length) {
2034                 for (var i = 0, l = this.childNodes.length; i < l; i++) {
2035                     this.childNodes[i]._draw(scene, detectTouch, this.tmpMat);
2036                 }
2037             }
2038 
2039             this.dispatchEvent(new enchant.Event('prerender'));
2040 
2041             if (this.mesh !== null) {
2042                 if (this.program !== null) {
2043                     enchant.Core.instance.GL.setProgram(this.program);
2044                     this._render(detectTouch);
2045                     enchant.Core.instance.GL.setDefaultProgram();
2046                 } else {
2047                     this._render(detectTouch);
2048                 }
2049             }
2050 
2051             this.dispatchEvent(new enchant.Event('render'));
2052         }
2053     });
2054 
2055     /**
2056      * Sprite3Dのx座標.
2057      * @default 0
2058      * @type Number
2059      * @see enchant.gl.Sprite3D#translate
2060      */
2061     enchant.gl.Sprite3D.prototype.x = 0;
2062 
2063     /**
2064      * Sprite3Dのy座標.
2065      * @default 0
2066      * @type Number
2067      * @see enchant.gl.Sprite3D#translate
2068      */
2069     enchant.gl.Sprite3D.prototype.y = 0;
2070 
2071     /**
2072      * Sprite3Dのz座標.
2073      * @default 0
2074      * @type Number
2075      * @see enchant.gl.Sprite3D#translate
2076      */
2077     enchant.gl.Sprite3D.prototype.z = 0;
2078 
2079     'x y z'.split(' ').forEach(function(prop) {
2080         Object.defineProperty(enchant.gl.Sprite3D.prototype, prop, {
2081             get: function() {
2082                 return this['_' + prop];
2083             },
2084             set: function(n) {
2085                 this['_' + prop] = n;
2086                 this._changedTranslation = true;
2087             }
2088         });
2089     });
2090 
2091     /**
2092      * Sprite3Dのx軸方向に対する拡大率
2093      * @default 1.0
2094      * @type Number
2095      * @see enchant.gl.Sprite3D#scale
2096      */
2097     enchant.gl.Sprite3D.prototype.scaleX = 1;
2098 
2099     /**
2100      * Sprite3Dのy軸方向に対する拡大率
2101      * @default 1.0
2102      * @type Number
2103      * @see enchant.gl.Sprite3D#scale
2104      */
2105     enchant.gl.Sprite3D.prototype.scaleY = 1;
2106     /**
2107      * Sprite3Dのz軸方向に対する拡大率
2108      * @default 1.0
2109      * @type Number
2110      * @see enchant.gl.Sprite3D#scale
2111      */
2112     enchant.gl.Sprite3D.prototype.scaleZ = 1;
2113 
2114     'scaleX scaleY scaleZ'.split(' ').forEach(function(prop) {
2115         Object.defineProperty(enchant.gl.Sprite3D.prototype, prop, {
2116             get: function() {
2117                 return this['_' + prop];
2118             },
2119             set: function(scale) {
2120                 this['_' + prop] = scale;
2121                 this._changedScale = true;
2122             }
2123         });
2124     });
2125 
2126     /**
2127      * Sprite3Dのグローバルのx座標.
2128      * @default 0
2129      * @type Number
2130      * @see enchant.gl.Sprite3D#translate
2131      */
2132     enchant.gl.Sprite3D.prototype.globalX = 0;
2133 
2134     /**
2135      * Sprite3Dのグローバルのy座標.
2136      * @default 0
2137      * @type Number
2138      * @see enchant.gl.Sprite3D#translate
2139      */
2140     enchant.gl.Sprite3D.prototype.globalY = 0;
2141 
2142     /**
2143      * Sprite3Dのグローバルのz座標.
2144      * @default 0
2145      * @type Number
2146      * @see enchant.gl.Sprite3D#translate
2147      */
2148     enchant.gl.Sprite3D.prototype.globalZ = 0;
2149 
2150     /**
2151      * @scope enchant.gl.Camera3D.prototype
2152      */
2153     enchant.gl.Camera3D = enchant.Class.create({
2154         /**
2155          * 3Dシーンのカメラを設定するクラス
2156          * @example
2157          * var scene = new Scene3D();
2158          * var camera = new Camera3D();
2159          * camera.x = 0;
2160          * camera.y = 0;
2161          * camera.z = 10;
2162          * scene.setCamera(camera);
2163          * @constructs
2164          */
2165         initialize: function() {
2166             var core = enchant.Core.instance;
2167             this.mat = mat4.identity();
2168             this.invMat = mat4.identity();
2169             this.invMatY = mat4.identity();
2170             this._projMat = mat4.create();
2171             mat4.perspective(20, core.width / core.height, 1.0, 1000.0, this._projMat);
2172             this._changedPosition = false;
2173             this._changedCenter = false;
2174             this._changedUpVector = false;
2175             this._changedProjection = false;
2176             this._x = 0;
2177             this._y = 0;
2178             this._z = 10;
2179             this._centerX = 0;
2180             this._centerY = 0;
2181             this._centerZ = 0;
2182             this._upVectorX = 0;
2183             this._upVectorY = 1;
2184             this._upVectorZ = 0;
2185         },
2186         /**
2187          * projection matrix
2188          */
2189         projMat: {
2190             get: function() {
2191                 return this._projMat;
2192             },
2193             set: function(mat) {
2194                 this._projMat = mat;
2195                 this._changedProjection = true;
2196             }
2197         },
2198         /**
2199          * カメラの注視点をSprite3Dの位置に合わせる.
2200          * @param {enchant.gl.Sprite3D} sprite 注視するSprite3D
2201          */
2202         lookAt: function(sprite) {
2203             if (sprite instanceof enchant.gl.Sprite3D) {
2204                 this._centerX = sprite.x;
2205                 this._centerY = sprite.y;
2206                 this._centerZ = sprite.z;
2207                 this._changedCenter = true;
2208             }
2209         },
2210         /**
2211          * カメラの位置をSprite3Dに近づける.
2212          * @param {enchant.gl.Sprite3D} sprite 対象のSprite3D
2213          * @param {Number} position 対象との距離
2214          * @param {Number} speed 対象に近づく速度
2215          * @example
2216          * var sp = new Sprite3D();
2217          * var camera = new Camera3D();
2218          * // spを注視しながら後ろ10にカメラの位置を近づけ続ける.
2219          * sp.addEventListener('enterframe', function() {
2220          *     camera.lookAt(sp);
2221          *     camera.chase(sp, -10, 20);
2222          * });
2223          */
2224         chase: function(sprite, position, speed) {
2225             if (sprite instanceof enchant.gl.Sprite3D) {
2226                 var vx = sprite.x + sprite.rotation[8] * position;
2227                 var vy = sprite.y + sprite.rotation[9] * position;
2228                 var vz = sprite.z + sprite.rotation[10] * position;
2229                 this._x += (vx - this._x) / speed;
2230                 this._y += (vy - this._y) / speed;
2231                 this._z += (vz - this._z) / speed;
2232                 this._changedPosition = true;
2233             }
2234         },
2235         _getForwardVec: function() {
2236             var x = this._centerX - this._x;
2237             var y = this._centerY - this._y;
2238             var z = this._centerZ - this._z;
2239             return vec3.normalize([x, y, z]);
2240         },
2241         _getSideVec: function() {
2242             var f = this._getForwardVec();
2243             var u = this._getUpVec();
2244             return vec3.cross(u, f);
2245         },
2246         _getUpVec: function() {
2247             var x = this._upVectorX;
2248             var y = this._upVectorY;
2249             var z = this._upVectorZ;
2250             return [x, y, z];
2251         },
2252         _move: function(v, s) {
2253             v[0] *= s;
2254             v[1] *= s;
2255             v[2] *= s;
2256             this._x += v[0];
2257             this._y += v[1];
2258             this._z += v[2];
2259             this._centerX += v[0];
2260             this._centerY += v[1];
2261             this._centerZ += v[2];
2262         },
2263         /**
2264          * Camera3DをローカルのZ軸方向に動かす.
2265          * @param {Number} speed
2266          */
2267         forward: function(s) {
2268             var v = this._getForwardVec();
2269             this._move(v, s);
2270         },
2271         /**
2272          * Camera3DをローカルのX軸方向に動かす.
2273          * @param {Number} speed
2274          */
2275         sidestep: function(s) {
2276             var v = this._getSideVec();
2277             this._move(v, s);
2278         },
2279         /**
2280          * Camera3DをローカルのY軸方向に動かす.
2281          * @param {Number} speed
2282          */
2283         altitude: function(s) {
2284             var v = this._getUpVec();
2285             this._move(v, s);
2286         },
2287         /**
2288          * Camera3DのローカルのZ軸を軸に回転させる.
2289          * @param {Number} radius
2290          */
2291         rotateRoll: function(rad) {
2292             var u = this._getUpVec();
2293             var f = this._getForwardVec();
2294             var x = f[0];
2295             var y = f[1];
2296             var z = f[2];
2297             var quat = new enchant.gl.Quat(x, y, z, -rad);
2298             var vec = quat.multiplyVec3(u);
2299             this._upVectorX = vec[0];
2300             this._upVectorY = vec[1];
2301             this._upVectorZ = vec[2];
2302             this._changedUpVector = true;
2303         },
2304         /**
2305          * Camera3DのローカルのX軸を軸に回転させる.
2306          * @param {Number} radius
2307          */
2308         rotatePitch: function(rad) {
2309             var u = this._getUpVec();
2310             var f = this._getForwardVec();
2311             var s = this._getSideVec();
2312             var sx = s[0];
2313             var sy = s[1];
2314             var sz = s[2];
2315             var quat = new enchant.gl.Quat(sx, sy, sz, -rad);
2316             var vec = quat.multiplyVec3(f);
2317             this._centerX = this._x + vec[0];
2318             this._centerY = this._y + vec[1];
2319             this._centerZ = this._z + vec[2];
2320             vec = vec3.normalize(quat.multiplyVec3(u));
2321             this._upVectorX = vec[0];
2322             this._upVectorY = vec[1];
2323             this._upVectorZ = vec[2];
2324             this._changedCenter = true;
2325             this._changedUpVector = true;
2326         },
2327         /**
2328          * Camera3DのローカルのY軸を軸に回転させる.
2329          * @param {Number} radius
2330          */
2331         rotateYaw: function(rad) {
2332             var u = this._getUpVec();
2333             var ux = u[0];
2334             var uy = u[1];
2335             var uz = u[2];
2336             var f = this._getForwardVec();
2337             var quat = new enchant.gl.Quat(ux, uy, uz, -rad);
2338             var vec = quat.multiplyVec3(f);
2339             this._centerX = this._x + vec[0];
2340             this._centerY = this._y + vec[1];
2341             this._centerZ = this._z + vec[2];
2342             this._changedCenter = true;
2343         },
2344         _updateMatrix: function() {
2345             mat4.lookAt(
2346                 [this._x, this._y, this._z],
2347                 [this._centerX, this._centerY, this._centerZ],
2348                 [this._upVectorX, this._upVectorY, this._upVectorZ],
2349                 this.mat);
2350             mat4.lookAt(
2351                 [0, 0, 0],
2352                 [-this._x + this._centerX,
2353                     -this._y + this._centerY,
2354                     -this._z + this._centerZ],
2355                 [this._upVectorX, this._upVectorY, this._upVectorZ],
2356                 this.invMat);
2357             mat4.inverse(this.invMat);
2358             mat4.lookAt(
2359                 [0, 0, 0],
2360                 [-this._x + this._centerX,
2361                     0,
2362                     -this._z + this._centerZ],
2363                 [this._upVectorX, this._upVectorY, this._upVectorZ],
2364                 this.invMatY);
2365             mat4.inverse(this.invMatY);
2366         }
2367     });
2368 
2369     /**
2370      * カメラのx座標
2371      * @type Number
2372      */
2373     enchant.gl.Camera3D.prototype.x = 0;
2374 
2375     /**
2376      * カメラのy座標
2377      * @type Number
2378      */
2379     enchant.gl.Camera3D.prototype.y = 0;
2380 
2381     /**
2382      * カメラのz座標
2383      * @type Number
2384      */
2385     enchant.gl.Camera3D.prototype.z = 0;
2386 
2387     'x y z'.split(' ').forEach(function(prop) {
2388         Object.defineProperty(enchant.gl.Camera3D.prototype, prop, {
2389             get: function() {
2390                 return this['_' + prop];
2391             },
2392             set: function(n) {
2393                 this['_' + prop] = n;
2394                 this._changedPosition = true;
2395             }
2396         });
2397     });
2398 
2399     /**
2400      * カメラの視点のx座標
2401      * @type Number
2402      */
2403     enchant.gl.Camera3D.prototype.centerX = 0;
2404 
2405     /**
2406      * カメラの視点のy座標
2407      * @type Number
2408      */
2409     enchant.gl.Camera3D.prototype.centerY = 0;
2410 
2411     /**
2412      * カメラの視点のz座標
2413      * @type Number
2414      */
2415     enchant.gl.Camera3D.prototype.centerZ = 0;
2416 
2417     'centerX centerY centerZ'.split(' ').forEach(function(prop) {
2418         Object.defineProperty(enchant.gl.Camera3D.prototype, prop, {
2419             get: function() {
2420                 return this['_' + prop];
2421             },
2422             set: function(n) {
2423                 this['_' + prop] = n;
2424                 this._changedCenter = true;
2425             }
2426         });
2427     });
2428 
2429     /**
2430      * カメラの上方向ベクトルのx成分
2431      * @type Number
2432      */
2433     enchant.gl.Camera3D.prototype.upVectorX = 0;
2434 
2435     /**
2436      * カメラの上方向ベクトルのy成分
2437      * @type Number
2438      */
2439     enchant.gl.Camera3D.prototype.upVectorY = 1;
2440 
2441     /**
2442      * カメラの上方向ベクトルのz成分
2443      * @type Number
2444      */
2445     enchant.gl.Camera3D.prototype.upVectorZ = 0;
2446 
2447     'upVectorX upVectorY upVectorZ'.split(' ').forEach(function(prop) {
2448         Object.defineProperty(enchant.gl.Camera3D.prototype, prop, {
2449             get: function() {
2450                 return this['_' + prop];
2451             },
2452             set: function(n) {
2453                 this['_' + prop] = n;
2454                 this._changedUpVector = true;
2455             }
2456         });
2457     });
2458 
2459     /**
2460      * @scope enchant.gl.Scene3D.prototype
2461      */
2462     enchant.gl.Scene3D = enchant.Class.create(enchant.EventTarget, {
2463         /**
2464          * 表示Sprite3Dツリーのルートになるクラス.
2465          * 現在, 複数定義することは出来ず, 最初に定義したScene3Dが返される.
2466          *
2467          * @example
2468          *   var scene = new Scene3D();
2469          *   var sprite = new Sprite3D();
2470          *   scene.addChild(sprite);
2471          *
2472          * @constructs
2473          * @extends enchant.EventTarget
2474          */
2475         initialize: function() {
2476             var core = enchant.Core.instance;
2477             if (core.currentScene3D) {
2478                 return core.currentScene3D;
2479             }
2480             enchant.EventTarget.call(this);
2481             /**
2482              * 子要素の配列.
2483              * このシーンに子として追加されているSprite3Dの一覧を取得できる.
2484              * 子を追加したり削除したりする場合には, この配列を直接操作せずに,
2485              * {@link enchant.gl.Scene3D#addChild}や{@link enchant.gl.Scene3D#removeChild}を利用する.
2486              * @type enchant.gl.Sprite3D[]
2487              */
2488             this.childNodes = [];
2489 
2490             /**
2491              * 照明の配列.
2492              * 現在, シーンに適用される光源は0番目のみ.
2493              * このシーンに追加されている光源の一覧を取得する.
2494              * 照明を追加したり削除したりする場合には, この配列を直接操作せずに,
2495              * {@link enchant.gl.Scene3D#addLight}や{@link enchant.gl.Scene3D#removeLight}を利用する.
2496              * @type enchant.gl.PointLight[]
2497              */
2498             this.lights = [];
2499 
2500             this.identityMat = mat4.identity();
2501             this._backgroundColor = [0.0, 0.0, 0.0, 1.0];
2502 
2503             var listener = function(e) {
2504                 for (var i = 0, len = this.childNodes.length; i < len; i++) {
2505                     var sprite = this.childNodes[i];
2506                     sprite.dispatchEvent(e);
2507                 }
2508             };
2509             this.addEventListener('added', listener);
2510             this.addEventListener('removed', listener);
2511             this.addEventListener('addedtoscene', listener);
2512             this.addEventListener('removedfromscene', listener);
2513 
2514             var that = this;
2515             var func = function() {
2516                 that._draw();
2517             };
2518             core.addEventListener('enterframe', func);
2519 
2520 
2521             var uniforms = {};
2522             uniforms['uUseCamera'] = 0.0;
2523             gl.activeTexture(gl.TEXTURE0);
2524             core.GL.defaultProgram.setUniforms(uniforms);
2525 
2526             if (core.currentScene3D === null) {
2527                 core.currentScene3D = this;
2528             }
2529 
2530             this.setAmbientLight(new enchant.gl.AmbientLight());
2531             this.setDirectionalLight(new enchant.gl.DirectionalLight());
2532             this.setCamera(new enchant.gl.Camera3D());
2533         },
2534 
2535         /**
2536          * Scene3Dの背景色
2537          * @type Number[]
2538          */
2539         backgroundColor: {
2540             get: function() {
2541                 return this._backgroundColor;
2542             },
2543             set: function(arg) {
2544                 var c = enchant.Core.instance.GL.parseColor(arg);
2545                 this._backgroundColor = c;
2546                 gl.clearColor(c[0], c[1], c[2], c[3]);
2547 
2548             }
2549         },
2550 
2551         /**
2552          * シーンにSprite3Dを追加する.
2553          * 引数に渡されたSprite3Dと, その子を全てシーンに追加する.
2554          * シーンに追加されると自動的にSprite3Dは画面上に表示される.
2555          * 一度追加したオブジェクトを削除するには{@link enchant.gl.Scene3D#removeChild}を利用する.
2556          * @param {enchant.gl.Sprite3D} sprite 追加するSprite3D
2557          * @see enchant.gl.Scene3D#removeChild
2558          * @see enchant.gl.Scene3D#childNodes
2559          */
2560         addChild: function(sprite) {
2561             this.childNodes.push(sprite);
2562             sprite.parentNode = sprite.scene = this;
2563             sprite.dispatchEvent(new enchant.Event('added'));
2564             sprite.dispatchEvent(new enchant.Event('addedtoscene'));
2565             sprite.dispatchEvent(new enchant.Event('render'));
2566         },
2567 
2568         /**
2569          * シーンからSprite3Dを削除する.
2570          * シーンから指定されたSprite3Dを削除する.
2571          * 削除されたSprite3Dは画面上に表示されなくなる.
2572          * Sprite3Dを追加するには{@link enchant.gl.Scene3D#addChild}を利用する.
2573          * @param {enchant.gl.Sprite3D} sprite 削除するSprite3D
2574          * @see enchant.gl.Scene3D#addChild
2575          * @see enchant.gl.Scene3D#childNodes
2576          */
2577         removeChild: function(sprite) {
2578             var i;
2579             if ((i = this.childNodes.indexOf(sprite)) !== -1) {
2580                 this.childNodes.splice(i, 1);
2581                 sprite.parentNode = sprite.scene = null;
2582                 sprite.dispatchEvent(new enchant.Event('removed'));
2583                 sprite.dispatchEvent(new enchant.Event('removedfromscene'));
2584             }
2585         },
2586 
2587         /**
2588          * シーンのカメラ位置をセットする.
2589          * @param {enchant.gl.Camera3D} camera セットするカメラ
2590          * @see enchant.gl.Camera3D
2591          */
2592         setCamera: function(camera) {
2593             camera._changedPosition = true;
2594             camera._changedCenter = true;
2595             camera._changedUpVector = true;
2596             camera._changedProjection = true;
2597             this._camera = camera;
2598             enchant.Core.instance.GL.defaultProgram.setUniforms({
2599                 uUseCamera: 1.0
2600             });
2601         },
2602 
2603         /**
2604          * シーンに設定されているカメラを取得する.
2605          * @see enchant.gl.Camera3D
2606          * @return {enchant.gl.Camera}
2607          */
2608         getCamera: function() {
2609             return this._camera;
2610         },
2611 
2612         /**
2613          * シーンに環境光源を設定する.
2614          * @param {enchant.gl.AmbientLight} light 設定する照明
2615          * @see enchant.gl.AmbientLight
2616          */
2617         setAmbientLight: function(light) {
2618             this.ambientLight = light;
2619         },
2620 
2621         /**
2622          * シーンに設定されている環境光源を取得する.
2623          * @see enchant.gl.AmbientLight
2624          * @return {enchant.gl.AmbientLight}
2625          */
2626         getAmbientLight: function() {
2627             return this.ambientLight;
2628         },
2629 
2630         /**
2631          * シーンに平行光源を設定する.
2632          * @param {enchant.gl.DirectionalLight} light 設定する照明
2633          * @see enchant.gl.DirectionalLight
2634          */
2635         setDirectionalLight: function(light) {
2636             this.directionalLight = light;
2637             this.useDirectionalLight = true;
2638             enchant.Core.instance.GL.defaultProgram.setUniforms({
2639                 uUseDirectionalLight: 1.0
2640             });
2641         },
2642 
2643         /**
2644          * シーンに設定されている平行光源を取得する.
2645          * @see enchant.gl.DirectionalLight
2646          * @return {enchant.gl.DirectionalLight}
2647          */
2648         getDirectionalLight: function() {
2649             return this.directionalLight;
2650         },
2651 
2652         /**
2653          * シーンに照明を追加する.
2654          * 現在, シーンに追加しても適用されない.
2655          * @param {enchant.gl.PointLight} light 追加する照明
2656          * @see enchant.gl.PointLight
2657          */
2658         addLight: function(light) {
2659             this.lights.push(light);
2660             this.usePointLight = true;
2661         },
2662 
2663         /**
2664          * シーンから照明を削除する
2665          * @param {enchant.gl.PointLight} light 削除する照明
2666          * @see enchant.gl.PointLight.
2667          */
2668         removeLight: function(light) {
2669             var i;
2670             if ((i = this.lights.indexOf(light)) !== -1) {
2671                 this.lights.splice(i, 1);
2672             }
2673         },
2674 
2675         _draw: function(detectTouch) {
2676             var core = enchant.Core.instance;
2677             var program = core.GL.defaultProgram;
2678 
2679             gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
2680 
2681             var detect = (detectTouch === 'detect') ? 1.0 : 0.0;
2682 
2683             var uniforms = { uDetectTouch: detect };
2684 
2685             if (this.ambientLight._changedColor) {
2686                 uniforms['uAmbientLightColor'] = this.ambientLight.color;
2687                 this.ambientLight._changedColor = false;
2688             }
2689             if (this.useDirectionalLight) {
2690                 if (this.directionalLight._changedDirection) {
2691                     uniforms['uLightDirection'] = [
2692                         this.directionalLight.directionX,
2693                         this.directionalLight.directionY,
2694                         this.directionalLight.directionZ
2695                     ];
2696                     this.directionalLight._changedDirection = false;
2697                 }
2698                 if (this.directionalLight._changedColor) {
2699                     uniforms['uLightColor'] = this.directionalLight.color;
2700                     this.directionalLight._changedColor = false;
2701                 }
2702             }
2703 
2704             if (this._camera) {
2705                 if (this._camera._changedPosition ||
2706                     this._camera._changedCenter ||
2707                     this._camera._changedUpVector ||
2708                     this._camera._changedProjection) {
2709                     this._camera._updateMatrix();
2710                     uniforms['uCameraMat'] = this._camera.mat;
2711                     uniforms['uProjMat'] = this._camera._projMat;
2712                     uniforms['uLookVec'] = [
2713                         this._camera._centerX - this._camera._x,
2714                         this._camera._centerY - this._camera._y,
2715                         this._camera._centerZ - this._camera._z
2716                     ];
2717                     this._camera._changedPosition = false;
2718                     this._camera._changedCenter = false;
2719                     this._camera._changedUpVector = false;
2720                     this._camera._changedProjection = false;
2721                 }
2722             }
2723             program.setUniforms(uniforms);
2724 
2725             mat4.identity(this.identityMat);
2726             for (var i = 0, l = this.childNodes.length; i < l; i++) {
2727                 this.childNodes[i]._draw(this, detectTouch, this.identityMat);
2728             }
2729 
2730         }
2731     });
2732 
2733     /**
2734      * @type {Object}
2735      */
2736     enchant.gl.collision = {};
2737 
2738     var point2point = function(p1, p2) {
2739         var vx = p1.x + p1.parent.x - p2.x - p2.parent.x;
2740         var vy = p1.y + p1.parent.y - p2.y - p2.parent.y;
2741         var vz = p1.z + p1.parent.z - p2.z - p2.parent.z;
2742         return (vx * vx + vy * vy + vz * vz);
2743     };
2744 
2745     var point2BS = function(p, bs) {
2746         return (point2point(p, bs) - bs.radius * bs.radius);
2747     };
2748 
2749     var point2AABB = function(p, aabb) {
2750         var ppx = p.x + p.parent.x;
2751         var ppy = p.y + p.parent.y;
2752         var ppz = p.z + p.parent.z;
2753         var px = aabb.parent.x + aabb.x + aabb.scale;
2754         var py = aabb.parent.y + aabb.y + aabb.scale;
2755         var pz = aabb.parent.z + aabb.z + aabb.scale;
2756         var nx = aabb.parent.x + (aabb.x - aabb.scale);
2757         var ny = aabb.parent.y + (aabb.y - aabb.scale);
2758         var nz = aabb.parent.z + (aabb.z - aabb.scale);
2759         var dist = 0;
2760         if (ppx < nx) {
2761             dist += (ppx - nx) * (ppx - nx);
2762         } else if (px < ppx) {
2763             dist += (ppx - px) * (ppx - px);
2764         }
2765         if (ppy < ny) {
2766             dist += (ppy - ny) * (ppy - ny);
2767         } else if (py < ppy) {
2768             dist += (ppy - py) * (ppy - py);
2769         }
2770         if (ppz < nz) {
2771             dist += (ppz - nz) * (ppz - nz);
2772         } else if (pz < ppz) {
2773             dist += (ppz - pz) * (ppz - pz);
2774         }
2775         return dist;
2776     };
2777 
2778     var point2OBB = function(p, obb) {
2779         return 1;
2780     };
2781 
2782     var BS2BS = function(bs1, bs2) {
2783         return (point2point(bs1, bs2) - (bs1.radius + bs2.radius) * (bs1.radius + bs2.radius));
2784     };
2785 
2786     var BS2AABB = function(bs, aabb) {
2787         return (point2AABB(bs, aabb) - bs.radius * bs.radius);
2788     };
2789 
2790     var BS2OBB = function(bs, obb) {
2791         return 1;
2792     };
2793 
2794     var AABB2AABB = function(aabb1, aabb2) {
2795         var px1 = aabb1.parent.x + aabb1.x + aabb1.scale;
2796         var py1 = aabb1.parent.y + aabb1.y + aabb1.scale;
2797         var pz1 = aabb1.parent.z + aabb1.z + aabb1.scale;
2798 
2799         var nx1 = aabb1.parent.x + (aabb1.x - aabb1.scale);
2800         var ny1 = aabb1.parent.y + (aabb1.y - aabb1.scale);
2801         var nz1 = aabb1.parent.z + (aabb1.z - aabb1.scale);
2802 
2803         var px2 = aabb2.parent.x + aabb2.x + aabb2.scale;
2804         var py2 = aabb2.parent.y + aabb2.y + aabb2.scale;
2805         var pz2 = aabb2.parent.z + aabb2.z + aabb2.scale;
2806 
2807         var nx2 = aabb2.parent.x + (aabb2.x - aabb2.scale);
2808         var ny2 = aabb2.parent.y + (aabb2.y - aabb2.scale);
2809         var nz2 = aabb2.parent.z + (aabb2.z - aabb2.scale);
2810         return ((nx2 <= px1) && (nx1 <= px2) &&
2811             (ny2 <= py1) && (ny1 <= py2) &&
2812             (nz2 <= pz1) && (nz1 <= pz2)) ? 0.0 : 1.0;
2813     };
2814 
2815     var AABB2OBB = function(aabb, obb) {
2816         return 1;
2817     };
2818 
2819     var OBB2OBB = function(obb1, obb2) {
2820         return 1;
2821     };
2822 
2823     /**
2824      * @scope enchant.gl.collision.Bounding.prototype
2825      */
2826     enchant.gl.collision.Bounding = enchant.Class.create({
2827         /**
2828          * Sprite3Dの衝突判定を設定するクラス.
2829          * 点として定義されている.
2830          * enchant.gl.collision.Boundingを継承したクラスとして,
2831          * {@link enchant.gl.collision.BS}, {@link enchant.gl.collision.AABB},
2832          * {@link enchant.gl.collision.OBB}, がある.
2833          * 現在, OBBはサポートされていない.
2834          * @constructs
2835          */
2836         initialize: function() {
2837             this.type = 'point';
2838             this.threshold = 0.0001;
2839             this.x = 0;
2840             this.y = 0;
2841             this.z = 0;
2842             this.parent = {
2843                 x: 0,
2844                 y: 0,
2845                 z: 0
2846             };
2847         },
2848         /**
2849          * 点との距離を計算する.
2850          * @param {enchant.gl.collision.Bounding} bounding 衝突点オブジェクト
2851          * @return {Number}
2852          */
2853         toBounding: function(another) {
2854             return point2point(this, another);
2855         },
2856         /**
2857          * 球との距離を計算する.
2858          * @param {enchant.gl.collision.BS} boudning 衝突球オブジェクト
2859          * @return {Number}
2860          */
2861         toBS: function(another) {
2862             return point2BS(this, another);
2863         },
2864         /**
2865          * 回転しない立方体との距離を計算する.
2866          * 現在, 衝突していなければ1, 衝突していれば0が返される.
2867          * @param {enchant.gl.collision.AABB} bounding AABB
2868          * @return {Number}
2869          */
2870         toAABB: function(another) {
2871             return point2AABB(this, another);
2872         },
2873         /**
2874          * 回転する直方体との距離を計算する.
2875          * 現在, サポートされていない.
2876          * @param {enchant.gl.collision.OBB} bounding OBB
2877          * @return {Number}
2878          */
2879         toOBB: function(another) {
2880             return point2OBB(this, another);
2881         },
2882         /**
2883          * 他の衝突判定オブジェクトとの衝突判定.
2884          * 衝突判定オブジェクトか, x, y, zプロパティを持っているオブジェクトとの衝突を判定することができる.
2885          * @param {enchant.gl.collision.Bounding|enchant.gl.collision.BS|enchant.gl.collision.AABB|enchant.gl.collision.OBB} bounding 衝突判定オブジェクト
2886          * @return {Boolean}
2887          */
2888         intersect: function(another) {
2889             switch (another.type) {
2890                 case 'point':
2891                     return (this.toBounding(another) < this.threshold);
2892                 case 'BS':
2893                     return (this.toBS(another) < this.threshold);
2894                 case 'AABB':
2895                     return (this.toAABB(another) < this.threshold);
2896                 case 'OBB':
2897                     return (this.toOBB(another) < this.threshold);
2898                 default:
2899                     return false;
2900             }
2901         }
2902     });
2903 
2904     /**
2905      * @scope enchant.gl.collision.BS.prototype
2906      */
2907     enchant.gl.collision.BS = enchant.Class.create(enchant.gl.collision.Bounding, {
2908         /**
2909          * Sprite3Dの衝突判定を設定するクラス.
2910          * 球として定義されている.
2911          * @constructs
2912          * @see enchant.gl.collision.Bounding
2913          */
2914         initialize: function() {
2915             enchant.gl.collision.Bounding.call(this);
2916             this.type = 'BS';
2917             this.radius = 0.5;
2918         },
2919         toBounding: function(another) {
2920             return point2BS(another, this);
2921         },
2922         toBS: function(another) {
2923             return BS2BS(this, another);
2924         },
2925         toAABB: function(another) {
2926             return BS2AABB(this, another);
2927         },
2928         toOBB: function(another) {
2929             return BS2OBB(this, another);
2930         }
2931     });
2932 
2933     /**
2934      * @scope enchant.gl.collision.AABB.prototype
2935      */
2936     enchant.gl.collision.AABB = enchant.Class.create(enchant.gl.collision.Bounding, {
2937         /**
2938          * Sprite3Dの衝突判定を設定するクラス.
2939          * 回転しない立方体として定義されている.
2940          * @constructs
2941          * @see enchant.gl.collision.Bounding
2942          */
2943         initialize: function() {
2944             enchant.gl.collision.Bounding.call(this);
2945             this.type = 'AABB';
2946             this.scale = 0.5;
2947         },
2948         toBounding: function(another) {
2949             return point2AABB(another, this);
2950         },
2951         toBS: function(another) {
2952             return BS2AABB(another, this);
2953         },
2954         toAABB: function(another) {
2955             return AABB2AABB(this, another);
2956         },
2957         toOBB: function(another) {
2958             return AABB2OBB(this, another);
2959         }
2960     });
2961 
2962     /**
2963      * @scope enchant.gl.collision.OBB.prototype
2964      */
2965     enchant.gl.collision.OBB = enchant.Class.create(enchant.gl.collision.Bounding, {
2966         /**
2967          * Sprite3Dの衝突判定を設定するクラス.
2968          * 回転するとして定義されている.
2969          * @constructs
2970          * @see enchant.gl.collision.Bounding
2971 
2972          */
2973         initialize: function() {
2974             enchant.gl.collision.Bounding.call(this);
2975             this.type = 'OBB';
2976         },
2977         toBounding: function(another) {
2978             return point2OBB(another, this);
2979         },
2980         toBS: function(another) {
2981             return BS2OBB(another, this);
2982         },
2983         toAABB: function(another) {
2984             return AABB2OBB(another, this);
2985         },
2986         toOBB: function(another) {
2987             return OBB2OBB(this, another);
2988         }
2989     });
2990 
2991     // borrowed from MMD.js
2992     var bezierp = function(x1, x2, y1, y2, x) {
2993         var t, tt, v;
2994         t = x;
2995         while (true) {
2996             v = ipfunc(t, x1, x2) - x;
2997             if (v * v < 0.0000001) {
2998                 break;
2999             }
3000             tt = ipfuncd(t, x1, x2);
3001             if (tt === 0) {
3002                 break;
3003             }
3004             t -= v / tt;
3005         }
3006         return ipfunc(t, y1, y2);
3007     };
3008     var ipfunc = function(t, p1, p2) {
3009         return (1 + 3 * p1 - 3 * p2) * t * t * t + (3 * p2 - 6 * p1) * t * t + 3 * p1 * t;
3010     };
3011     var ipfuncd = function(t, p1, p2) {
3012         return (3 + 9 * p1 - 9 * p2) * t * t + (6 * p2 - 12 * p1) * t + 3 * p1;
3013     };
3014     var frac = function(n1, n2, t) {
3015         return (t - n1) / (n2 - n1);
3016     };
3017     var lerp = function(n1, n2, r) {
3018         return n1 + r * (n2 - n1);
3019     };
3020 
3021     var _tmpve = vec3.create();
3022     var _tmpvt = vec3.create();
3023     var _tmpaxis = vec3.create();
3024     var _tmpquat = quat4.create();
3025     var _tmpinv = quat4.create();
3026 
3027     /**
3028      * @scope enchant.gl.State.prototype
3029      */
3030     enchant.gl.State = enchant.Class.create({
3031         /**
3032          * アニメーションの状態を表すための基底クラス.
3033          * @param {Number[]} position
3034          * @param {Number[]} rotation
3035          * @constructs
3036          */
3037         initialize: function(position, rotation) {
3038             this._position = vec3.create();
3039             vec3.set(position, this._position);
3040             this._rotation = quat4.create();
3041             quat4.set(rotation, this._rotation);
3042         },
3043         /**
3044          * 位置・回転をセットする.
3045          */
3046         set: function(pose) {
3047             vec3.set(pose._position, this._position);
3048             quat4.set(pose._rotation, this._rotation);
3049         }
3050     });
3051 
3052     /**
3053      * @scope enchant.gl.Pose.prototype
3054      */
3055     enchant.gl.Pose = enchant.Class.create(enchant.gl.State, {
3056         /**
3057          * 姿勢を処理するためのクラス.
3058          * @param {Number[]} position
3059          * @param {Number[]} rotation
3060          * @constructs
3061          * @extends enchant.gl.State
3062          */
3063         initialize: function(position, rotation) {
3064             enchant.gl.State.call(this, position, rotation);
3065         },
3066         /**
3067          * 他の姿勢との補間を行う.
3068          * @param {enchant.gl.Pose} another
3069          * @param {Number} ratio
3070          * @return {enchant.gl.Pose}
3071          */
3072         getInterpolation: function(another, ratio) {
3073             vec3.lerp(this._position, another._position, ratio, _tmpve);
3074             quat4.slerp(this._rotation, another._rotation, ratio, _tmpquat);
3075             return new enchant.gl.Pose(_tmpve, _tmpquat);
3076         },
3077         _bezierp: function(x1, y1, x2, y2, x) {
3078             return bezierp(x1, x2, y1, y2, x);
3079         }
3080     });
3081 
3082     /**
3083      * @scope enchant.gl.KeyFrameManager.prototype
3084      */
3085     enchant.gl.KeyFrameManager = enchant.Class.create({
3086         /**
3087          * キーフレームアニメーションを実現するためのクラス.
3088          * enchant.gl.Poseに限らず様々なデータを扱える.
3089          * @constructs
3090          */
3091         initialize: function() {
3092             this._frames = [];
3093             this._units = [];
3094             this.length = -1;
3095             this._lastPose = null;
3096         },
3097         /**
3098          * フレームを追加する.
3099          * @param {*} pose キーフレーム.
3100          * @param {Number} frame フレーム番号.
3101          */
3102         addFrame: function(pose, frame) {
3103             if (typeof frame !== 'number') {
3104                 this.length += 1;
3105                 frame = this.length;
3106             }
3107             if (frame > this.length) {
3108                 this.length = frame;
3109                 this._lastPose = pose;
3110             }
3111             this._frames.push(frame);
3112             this._units[frame] = pose;
3113         },
3114         /**
3115          * 指定されたフレーム番号の情報を返す.
3116          * 指定されたフレーム番号に該当するデータがない場合, 指定されたフレーム番号の前後のデータから補間したデータを取得する.
3117          * @param {Number} frame フレーム番号
3118          * @return {*}
3119          */
3120         getFrame: function(frame) {
3121             var prev, next, index, pidx, nidx;
3122             var ratio = 0;
3123             if (frame >= this.length) {
3124                 return this._lastPose;
3125             }
3126             if (this._units[frame]) {
3127                 return this._units[frame];
3128             } else {
3129                 index = this._getPrevFrameIndex(frame);
3130                 pidx = this._frames[index];
3131                 nidx = this._frames[index + 1];
3132                 prev = this._units[pidx];
3133                 next = this._units[nidx];
3134                 ratio = this._frac(pidx, nidx, frame);
3135                 return this._interpole(prev, next, ratio);
3136             }
3137         },
3138         bake: function() {
3139             var state;
3140             for (var i = 0, l = this.length; i < l; i++) {
3141                 if (this._units[i]) {
3142                     continue;
3143                 }
3144                 state = this.getFrame(i);
3145                 this.addFrame(state, i);
3146             }
3147             this._sort();
3148         },
3149         _frac: function(p, n, t) {
3150             return frac(p, n, t);
3151         },
3152         _interpole: function(prev, next, ratio) {
3153             return prev.getInterpolation(next, ratio);
3154         },
3155         _sort: function() {
3156             this._frames.sort(function(a, b) {
3157                 return a - b;
3158             });
3159         },
3160         _getPrevFrameIndex: function(frame) {
3161             for (var i = 0, l = this._frames.length; i < l; i++) {
3162                 if (this._frames[i] > frame) {
3163                     break;
3164                 }
3165             }
3166             return i - 1;
3167         }
3168     });
3169 
3170     /**
3171      * @scope enchant.gl.Bone.prototype
3172      */
3173     enchant.gl.Bone = enchant.Class.create(enchant.gl.State, {
3174         /**
3175          * ボーンの状態を表すクラス.
3176          * @param {String} name
3177          * @param {Number} head
3178          * @param {Number} position
3179          * @param {Number} rotation
3180          * @constructs
3181          * @extends enchant.gl.State
3182          */
3183         initialize: function(name, head, position, rotation) {
3184             enchant.gl.State.call(this, position, rotation);
3185             this._name = name;
3186             this._origin = vec3.create();
3187 
3188             vec3.set(head, this._origin);
3189 
3190             this._globalpos = vec3.create();
3191             vec3.set(head, this._globalpos);
3192 
3193             this._globalrot = quat4.identity();
3194 
3195             this.parentNode = null;
3196             this.childNodes = [];
3197 
3198             /**
3199              * IK解決の際にクォータニオンに変換をかける関数を設定する.
3200              */
3201             this.constraint = null;
3202         },
3203         /**
3204          * ボーンに子のボーンを追加する.
3205          * @param {enchant.gl.Bone} child
3206          */
3207         addChild: function(child) {
3208             this.childNodes.push(child);
3209             child.parentNode = this;
3210         },
3211         /**
3212          * ボーンから子のボーンを削除する.
3213          * @param {enchant.gl.Bone} child
3214          */
3215         removeChild: function(child) {
3216             var i;
3217             if ((i = this.childNodes.indexOf(child)) !== -1) {
3218                 this.childNodes.splice(i, 1);
3219             }
3220             child.parentNode = null;
3221         },
3222         /**
3223          * ボーンの姿勢をセットする.
3224          * @param {*} poses
3225          */
3226         setPoses: function(poses) {
3227             var child;
3228             if (poses[this._name]) {
3229                 this.set(poses[this._name]);
3230             }
3231             for (var i = 0, l = this.childNodes.length; i < l; i++) {
3232                 child = this.childNodes[i];
3233                 child.setPoses(poses);
3234             }
3235         },
3236         _applyPose: function(){
3237             var parent = this.parentNode;
3238             quat4.multiply(parent._globalrot, this._rotation, this._globalrot);
3239             quat4.multiplyVec3(parent._globalrot, this._position, this._globalpos);
3240             vec3.add(parent._globalpos, this._globalpos, this._globalpos);
3241         },
3242         _solveFK: function() {
3243             var child;
3244             this._applyPose();
3245             for (var i = 0, l = this.childNodes.length; i < l; i++) {
3246                 child = this.childNodes[i];
3247                 child._solveFK();
3248             }
3249         },
3250         _solve: function(quat) {
3251             quat4.normalize(quat, this._rotation);
3252             this._solveFK();
3253         }
3254     });
3255 
3256     /**
3257      * @scope enchant.gl.Skeleton.prototype
3258      */
3259     enchant.gl.Skeleton = enchant.Class.create({
3260         /**
3261          * ボーンの構造のルートになるクラス.
3262          * @constructs
3263          */
3264         initialize: function() {
3265             this.childNodes = [];
3266             this._origin = vec3.create();
3267             this._position = vec3.create();
3268             this._rotation = quat4.identity();
3269             this._globalpos = vec3.create();
3270             this._globalrot = quat4.identity();
3271             this._iks = [];
3272         },
3273         /**
3274          * Skeletonに子のボーンを追加する.
3275          * @param {enchant.gl.Bone} child
3276          */
3277         addChild: function(bone) {
3278             this.childNodes.push(bone);
3279             bone.parentNode = this;
3280         },
3281         /**
3282          * Skeletonから子のボーンを削除する.
3283          * @param {enchant.gl.Bone} child
3284          */
3285         removeChild: function(bone) {
3286             var i;
3287             if ((i = this.childNodes.indexOf(bone)) !== -1) {
3288                 this.childNodes.splice(i, 1);
3289             }
3290             bone.parentNode = null;
3291         },
3292         /**
3293          * 姿勢をセットする.
3294          * @param {*} poses
3295          */
3296         setPoses: function(poses) {
3297             var child;
3298             for (var i = 0, l = this.childNodes.length; i < l; i++) {
3299                 child = this.childNodes[i];
3300                 child.setPoses(poses);
3301             }
3302         },
3303         /**
3304          * FKによって姿勢解決を行う.
3305          * セットされた姿勢情報から姿勢をつくる.
3306          */
3307         solveFKs: function() {
3308             var child;
3309             for (var i = 0, l = this.childNodes.length; i < l; i++) {
3310                 child = this.childNodes[i];
3311                 child._solveFK();
3312             }
3313         },
3314         /**
3315          * IKの制御情報を追加する.
3316          * @param {enchant.gl.Bone} effector
3317          * @param {enchant.gl.Bone} target
3318          * @param {enchant.gl.Bone[]} bones
3319          * @param {Number} maxangle
3320          * @param {Number} iteration
3321          * @see enchant.gl.Skeleton#solveIKs
3322          */
3323         addIKControl: function(effector, target, bones, maxangle, iteration) {
3324             this._iks.push(arguments);
3325         },
3326         // by ccd
3327         /**
3328          * IKによって姿勢解決を行う.
3329          * {@link enchant.gl.Skeleton#addIKControl}によって追加された情報をもとにする.
3330          */
3331         solveIKs: function() {
3332             var param;
3333             for (var i = 0, l = this._iks.length; i < l; i++) {
3334                 param = this._iks[i];
3335                 this._solveIK.apply(this, param);
3336             }
3337         },
3338         _solveIK: function(effector, target, bones, maxangle, iteration) {
3339             var len, origin;
3340             vec3.subtract(target._origin, target.parentNode._origin, _tmpinv);
3341             var threshold = vec3.length(_tmpinv) * 0.1;
3342             for (var i = 0; i < iteration; i++) {
3343                 vec3.subtract(target._globalpos, effector._globalpos, _tmpinv);
3344                 len = vec3.length(_tmpinv);
3345                 if (len < threshold) {
3346                     break;
3347                 }
3348                 for (var j = 0, ll = bones.length; j < ll; j++) {
3349                     origin = bones[j];
3350                     this._ccd(effector, target, origin, maxangle, threshold);
3351                 }
3352             }
3353         },
3354         _ccd: function(effector, target, origin, maxangle, threshold) {
3355             vec3.subtract(effector._globalpos, origin._globalpos, _tmpve);
3356             vec3.subtract(target._globalpos, origin._globalpos, _tmpvt);
3357             vec3.cross(_tmpvt, _tmpve, _tmpaxis);
3358             var elen = vec3.length(_tmpve);
3359             var tlen = vec3.length(_tmpvt);
3360             var alen = vec3.length(_tmpaxis);
3361 
3362             if (elen < threshold || tlen < threshold || alen < threshold) {
3363                 return;
3364             }
3365             var rad = Math.acos(vec3.dot(_tmpve, _tmpvt) / elen / tlen);
3366 
3367             if (rad > maxangle) {
3368                 rad = maxangle;
3369             }
3370             vec3.scale(_tmpaxis, Math.sin(rad / 2) / alen, _tmpquat);
3371             _tmpquat[3] = Math.cos(rad / 2);
3372             quat4.inverse(origin.parentNode._globalrot, _tmpinv);
3373             quat4.multiply(_tmpinv, _tmpquat, _tmpquat);
3374             quat4.multiply(_tmpquat, origin._globalrot, _tmpquat);
3375 
3376 
3377             if (origin.constraint) {
3378                 origin.constraint(_tmpquat);
3379             }
3380 
3381             origin._solve(_tmpquat);
3382         }
3383     });
3384 
3385     var DEFAULT_VERTEX_SHADER_SOURCE = '\n\
3386     attribute vec3 aVertexPosition;\n\
3387     attribute vec4 aVertexColor;\n\
3388     \n\
3389     attribute vec3 aNormal;\n\
3390     attribute vec2 aTextureCoord;\n\
3391     \n\
3392     uniform mat4 uModelMat;\n\
3393     uniform mat4 uRotMat;\n\
3394     uniform mat4 uCameraMat;\n\
3395     uniform mat4 uProjMat;\n\
3396     uniform mat3 uNormMat;\n\
3397     uniform float uUseCamera;\n\
3398     \n\
3399     varying vec2 vTextureCoord;\n\
3400     varying vec4 vColor;\n\
3401     varying vec3 vNormal;\n\
3402     \n\
3403     void main() {\n\
3404         vec4 p = uModelMat * vec4(aVertexPosition, 1.0);\n\
3405         gl_Position = uProjMat * (uCameraMat * uUseCamera) * p + uProjMat * p * (1.0 - uUseCamera);\n\
3406         vTextureCoord = aTextureCoord;\n\
3407         vColor = aVertexColor;\n\
3408         vNormal = uNormMat * aNormal;\n\
3409     }';
3410 
3411     var DEFAULT_FRAGMENT_SHADER_SOURCE = '\n\
3412     precision highp float;\n\
3413     \n\
3414     uniform sampler2D uSampler;\n\
3415     uniform float uUseDirectionalLight;\n\
3416     uniform vec3 uAmbientLightColor;\n\
3417     uniform vec3 uLightColor;\n\
3418     uniform vec3 uLookVec;\n\
3419     uniform vec4 uAmbient;\n\
3420     uniform vec4 uDiffuse;\n\
3421     uniform vec4 uSpecular;\n\
3422     uniform vec4 uEmission;\n\
3423     uniform vec4 uDetectColor;\n\
3424     uniform float uDetectTouch;\n\
3425     uniform float uUseTexture;\n\
3426     uniform float uShininess;\n\
3427     uniform vec3 uLightDirection;\n\
3428     \n\
3429     varying vec2 vTextureCoord;\n\
3430     varying vec4 vColor;\n\
3431     varying vec3 vNormal;\n\
3432     \n\
3433     \n\
3434     void main() {\n\
3435         float pi = 4.0 * atan(1.0);\n\
3436         vec4 texColor = texture2D(uSampler, vTextureCoord);\n\
3437         vec4 baseColor = vColor;\n\
3438         baseColor *= texColor * uUseTexture + vec4(1.0, 1.0, 1.0, 1.0) * (1.0 - uUseTexture);\n\
3439         float alpha = baseColor.a * uDetectColor.a * uDetectTouch + baseColor.a * (1.0 - uDetectTouch);\n\
3440         if (alpha < 0.2) {\n\
3441             discard;\n\
3442         }\n\
3443         else {\n\
3444             vec4 amb = uAmbient * vec4(uAmbientLightColor, 1.0);\n\
3445             vec3 N = normalize(vNormal);\n\
3446             vec3 L = normalize(uLightDirection);\n\
3447             vec3 E = normalize(uLookVec);\n\
3448             vec3 R = reflect(-L, N);\n\
3449             float lamber = max(dot(N, L) , 0.0);\n\
3450             vec4 dif = uDiffuse * lamber;\n\
3451             float s = max(dot(R, -E), 0.0);\n\
3452             vec4 specularColor = (uShininess + 2.0) / (2.0 * pi) * uSpecular * pow(s, uShininess) * sign(lamber);\n\
3453             gl_FragColor = (vec4(((amb + vec4(uLightColor, 1.0) * (dif + specularColor)) * baseColor).rgb, baseColor.a) \
3454                 * uUseDirectionalLight + baseColor * (1.0 - uUseDirectionalLight)) \
3455                 * (1.0 - uDetectTouch) + uDetectColor * uDetectTouch;\n\
3456         }\n\
3457     }';
3458 
3459 }());
3460