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