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