diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..482e34b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +*.swp +*.swo diff --git a/CNAME b/CNAME new file mode 100644 index 00000000..a76d3154 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +photovrse.com diff --git a/CONTRIBUTING b/CONTRIBUTING new file mode 100644 index 00000000..43b6883e --- /dev/null +++ b/CONTRIBUTING @@ -0,0 +1,22 @@ +# Building + +This project uses browserify to manage dependencies and build. Watchify is +especially convenient to preserve the write-and-reload model of development. +This package lives in the npm index. There is also a bower version of it, but it +is deprecated. + +Relevant commands: + + npm build - builds the module. + npm build-analytics - builds the module with analytics support. + npm watch - auto-builds the module whenever any source changes. + + +# Updating the npm entry + +Once changes are made, a new version can be published to the index using the +following commands: + + npm version + npm publish + git push diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..dfc59b7f --- /dev/null +++ b/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2015 Google Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 00000000..80508426 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +VR Views +======== + +Please read the documentation available at +. diff --git a/build/device-motion-sender.min.js b/build/device-motion-sender.min.js new file mode 100644 index 00000000..d43248e9 --- /dev/null +++ b/build/device-motion-sender.min.js @@ -0,0 +1 @@ +function DeviceMotionSender(){if(!this.isIOS_()){return}window.addEventListener("devicemotion",this.onDeviceMotion_.bind(this),false);this.iframes=document.querySelectorAll("iframe.vrview")}DeviceMotionSender.prototype.onDeviceMotion_=function(e){var message={type:"DeviceMotion",deviceMotionEvent:this.cloneDeviceMotionEvent_(e)};for(var i=0;it;t+=2){var e=X[t],n=X[t+1];e(n),X[t]=void 0,X[t+1]=void 0}G=0}function f(){try{var t=_dereq_,e=t("vertx");return U=e.runOnLoop||e.runOnContext,i()}catch(n){return c()}}function l(t,e){var n=this,r=n._state;if(r===et&&!t||r===nt&&!e)return this;var o=new this.constructor(p),i=n._result;if(r){var s=arguments[r-1];H(function(){C(r,o,s,i)})}else j(n,o,t,e);return o}function h(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var n=new e(p);return g(n,t),n}function p(){}function _(){return new TypeError("You cannot resolve a promise with itself")}function v(){return new TypeError("A promises callback cannot return that same promise.")}function d(t){try{return t.then}catch(e){return rt.error=e,rt}}function y(t,e,n,r){try{t.call(e,n,r)}catch(o){return o}}function m(t,e,n){H(function(t){var r=!1,o=y(n,e,function(n){r||(r=!0,e!==n?g(t,n):E(t,n))},function(e){r||(r=!0,S(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&o&&(r=!0,S(t,o))},t)}function w(t,e){e._state===et?E(t,e._result):e._state===nt?S(t,e._result):j(e,void 0,function(e){g(t,e)},function(e){S(t,e)})}function b(t,n,r){n.constructor===t.constructor&&r===Z&&constructor.resolve===$?w(t,n):r===rt?S(t,rt.error):void 0===r?E(t,n):e(r)?m(t,n,r):E(t,n)}function g(e,n){e===n?S(e,_()):t(n)?b(e,n,d(n)):E(e,n)}function A(t){t._onerror&&t._onerror(t._result),T(t)}function E(t,e){t._state===tt&&(t._result=e,t._state=et,0!==t._subscribers.length&&H(T,t))}function S(t,e){t._state===tt&&(t._state=nt,t._result=e,H(A,t))}function j(t,e,n,r){var o=t._subscribers,i=o.length;t._onerror=null,o[i]=e,o[i+et]=n,o[i+nt]=r,0===i&&t._state&&H(T,t)}function T(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r,o,i=t._result,s=0;ss;s++)j(r.resolve(t[s]),void 0,e,n);return o}function Y(t){var e=this,n=new e(p);return S(n,t),n}function q(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function F(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function D(t){this._id=ct++,this._state=void 0,this._result=void 0,this._subscribers=[],p!==t&&("function"!=typeof t&&q(),this instanceof D?M(this,t):F())}function K(t,e){this._instanceConstructor=t,this.promise=new t(p),Array.isArray(e)?(this._input=e,this.length=e.length,this._remaining=e.length,this._result=new Array(this.length),0===this.length?E(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&E(this.promise,this._result))):S(this.promise,this._validationError())}function L(){var t;if("undefined"!=typeof global)t=global;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;(!n||"[object Promise]"!==Object.prototype.toString.call(n.resolve())||n.cast)&&(t.Promise=at)}var N;N=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var U,W,z,B=N,G=0,H=function(t,e){X[G]=t,X[G+1]=e,G+=2,2===G&&(W?W(a):z())},I="undefined"!=typeof window?window:void 0,J=I||{},Q=J.MutationObserver||J.WebKitMutationObserver,R="undefined"!=typeof process&&"[object process]"==={}.toString.call(process),V="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,X=new Array(1e3);z=R?o():Q?s():V?u():void 0===I&&"function"==typeof _dereq_?f():c();var Z=l,$=h,tt=void 0,et=1,nt=2,rt=new P,ot=new P,it=O,st=k,ut=Y,ct=0,at=D;D.all=it,D.race=st,D.resolve=$,D.reject=ut,D._setScheduler=n,D._setAsap=r,D._asap=H,D.prototype={constructor:D,then:Z,"catch":function(t){return this.then(null,t)}};var ft=K;K.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},K.prototype._enumerate=function(){for(var t=this.length,e=this._input,n=0;this._state===tt&&t>n;n++)this._eachEntry(e[n],n)},K.prototype._eachEntry=function(t,e){var n=this._instanceConstructor,r=n.resolve;if(r===$){var o=d(t);if(o===Z&&t._state!==tt)this._settledAt(t._state,e,t._result);else if("function"!=typeof o)this._remaining--,this._result[e]=t;else if(n===at){var i=new n(p);b(i,t,o),this._willSettleAt(i,e)}else this._willSettleAt(new n(function(e){e(t)}),e)}else this._willSettleAt(r(t),e)},K.prototype._settledAt=function(t,e,n){var r=this.promise;r._state===tt&&(this._remaining--,t===nt?S(r,n):this._result[e]=n),0===this._remaining&&E(r,this._result)},K.prototype._willSettleAt=function(t,e){var n=this;j(t,void 0,function(t){n._settledAt(et,e,t)},function(t){n._settledAt(nt,e,t)})};var lt=L,ht={Promise:at,polyfill:lt};"function"==typeof define&&define.amd?define(function(){return ht}):"undefined"!=typeof module&&module.exports?module.exports=ht:"undefined"!=typeof this&&(this.ES6Promise=ht),lt()}).call(this); +}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"_process":20}],2:[function(_dereq_,module,exports){ +// stats.js - http://github.com/mrdoob/stats.js +var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px"; +i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div"); +k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display= +"block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:12,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height= +a+"px",m=b,r=0);return b},update:function(){l=this.end()}}};"object"===typeof module&&(module.exports=Stats); + +},{}],3:[function(_dereq_,module,exports){ +/** + * @author dmarcos / https://github.com/dmarcos + * @author mrdoob / http://mrdoob.com + */ + +var VRControls = function ( object, onError ) { + + var scope = this; + + var vrInputs = []; + + function filterInvalidDevices( devices ) { + + // Exclude Cardboard position sensor if Oculus exists. + + var oculusDevices = devices.filter( function ( device ) { + + return device.deviceName.toLowerCase().indexOf( 'oculus' ) !== - 1; + + } ); + + if ( oculusDevices.length >= 1 ) { + + return devices.filter( function ( device ) { + + return device.deviceName.toLowerCase().indexOf( 'cardboard' ) === - 1; + + } ); + + } else { + + return devices; + + } + + } + + function gotVRDevices( devices ) { + + devices = filterInvalidDevices( devices ); + + for ( var i = 0; i < devices.length; i ++ ) { + + if ( devices[ i ] instanceof PositionSensorVRDevice ) { + + vrInputs.push( devices[ i ] ); + + } + + } + + if ( onError ) onError( 'HMD not available' ); + + } + + if ( navigator.getVRDevices ) { + + navigator.getVRDevices().then( gotVRDevices ); + + } + + // the Rift SDK returns the position in meters + // this scale factor allows the user to define how meters + // are converted to scene units. + + this.scale = 1; + + this.update = function () { + + for ( var i = 0; i < vrInputs.length; i ++ ) { + + var vrInput = vrInputs[ i ]; + + var state = vrInput.getState(); + + if ( state.orientation !== null ) { + + object.quaternion.copy( state.orientation ); + + } + + if ( state.position !== null ) { + + object.position.copy( state.position ).multiplyScalar( scope.scale ); + + } + + } + + }; + + this.resetSensor = function () { + + for ( var i = 0; i < vrInputs.length; i ++ ) { + + var vrInput = vrInputs[ i ]; + + if ( vrInput.resetSensor !== undefined ) { + + vrInput.resetSensor(); + + } else if ( vrInput.zeroSensor !== undefined ) { + + vrInput.zeroSensor(); + + } + + } + + }; + + this.zeroSensor = function () { + + console.warn( 'THREE.VRControls: .zeroSensor() is now .resetSensor().' ); + this.resetSensor(); + + }; + + this.dispose = function () { + + vrInputs = []; + + }; + +}; + +try { + module.exports = VRControls; +} catch (e) { + THREE.VRControls = VRControls; +} + +},{}],4:[function(_dereq_,module,exports){ +/** + * @author dmarcos / https://github.com/dmarcos + * @author mrdoob / http://mrdoob.com + * + * WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html + * + * Firefox: http://mozvr.com/downloads/ + * Chromium: https://drive.google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ&usp=sharing#list + * + */ + +var VREffect = function ( renderer, onError ) { + + var vrHMD; + var eyeTranslationL, eyeFOVL, rectL; + var eyeTranslationR, eyeFOVR, rectR; + + function gotVRDevices( devices ) { + + for ( var i = 0; i < devices.length; i ++ ) { + + if ( devices[ i ] instanceof HMDVRDevice ) { + + vrHMD = devices[ i ]; + + break; // We keep the first we encounter + + } + + } + + if ( vrHMD === undefined ) { + + if ( onError ) onError( 'HMD not available' ); + + } + + } + + if ( navigator.getVRDevices ) { + + navigator.getVRDevices().then( gotVRDevices ); + + } + + // + + this.scale = 1; + + this.setSize = function( width, height ) { + + renderer.setSize( width, height ); + + }; + + // fullscreen + + var isFullscreen = false; + + var canvas = renderer.domElement; + var fullscreenchange = canvas.mozRequestFullScreen ? 'mozfullscreenchange' : 'webkitfullscreenchange'; + + document.addEventListener( fullscreenchange, function ( event ) { + + isFullscreen = document.mozFullScreenElement || document.webkitFullscreenElement; + + }, false ); + + this.setFullScreen = function ( boolean ) { + + if ( vrHMD === undefined ) return; + if ( isFullscreen === boolean ) return; + + if ( canvas.mozRequestFullScreen ) { + + canvas.mozRequestFullScreen( { vrDisplay: vrHMD } ); + + } else if ( canvas.webkitRequestFullscreen ) { + + canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } ); + + } + + }; + + // render + + var cameraL = new THREE.PerspectiveCamera(); + var cameraR = new THREE.PerspectiveCamera(); + + this.render = function ( scene, camera ) { + + if ( vrHMD ) { + + var eyeParamsL = vrHMD.getEyeParameters( 'left' ); + var eyeParamsR = vrHMD.getEyeParameters( 'right' ); + + eyeTranslationL = eyeParamsL.eyeTranslation; + eyeTranslationR = eyeParamsR.eyeTranslation; + eyeFOVL = eyeParamsL.recommendedFieldOfView; + eyeFOVR = eyeParamsR.recommendedFieldOfView; + rectL = eyeParamsL.renderRect; + rectR = eyeParamsR.renderRect; + + var sceneL, sceneR; + + if ( Array.isArray( scene ) ) { + + sceneL = scene[ 0 ]; + sceneR = scene[ 1 ]; + + } else { + + sceneL = scene; + sceneR = scene; + + } + + var size = renderer.getSize(); + size.width /= 2; + + renderer.enableScissorTest( true ); + renderer.clear(); + + if ( camera.parent === null ) camera.updateMatrixWorld(); + + cameraL.projectionMatrix = fovToProjection( eyeFOVL, true, camera.near, camera.far ); + cameraR.projectionMatrix = fovToProjection( eyeFOVR, true, camera.near, camera.far ); + + camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale ); + camera.matrixWorld.decompose( cameraR.position, cameraR.quaternion, cameraR.scale ); + + cameraL.translateX( eyeTranslationL.x * this.scale ); + cameraR.translateX( eyeTranslationR.x * this.scale ); + + // render left eye + if ( rectL ) { + + renderer.setViewport( rectL.x, rectL.y, rectL.width, rectL.height ); + renderer.setScissor( rectL.x, rectL.y, rectL.width, rectL.height ); + + } else { + + renderer.setViewport( 0, 0, size.width, size.height ); + renderer.setScissor( 0, 0, size.width, size.height ); + + } + renderer.render( sceneL, cameraL ); + + // render right eye + if ( rectR ) { + + renderer.setViewport( rectR.x, rectR.y, rectR.width, rectR.height ); + renderer.setScissor( rectR.x, rectR.y, rectR.width, rectR.height ); + + } else { + + renderer.setViewport( size.width, 0, size.width, size.height ); + renderer.setScissor( size.width, 0, size.width, size.height ); + + } + renderer.render( sceneR, cameraR ); + + renderer.enableScissorTest( false ); + + return; + + } + + // Regular render mode if not HMD + + if ( Array.isArray( scene ) ) scene = scene[ 0 ]; + + renderer.render( scene, camera ); + + }; + + // + + function fovToNDCScaleOffset( fov ) { + + var pxscale = 2.0 / ( fov.leftTan + fov.rightTan ); + var pxoffset = ( fov.leftTan - fov.rightTan ) * pxscale * 0.5; + var pyscale = 2.0 / ( fov.upTan + fov.downTan ); + var pyoffset = ( fov.upTan - fov.downTan ) * pyscale * 0.5; + return { scale: [ pxscale, pyscale ], offset: [ pxoffset, pyoffset ] }; + + } + + function fovPortToProjection( fov, rightHanded, zNear, zFar ) { + + rightHanded = rightHanded === undefined ? true : rightHanded; + zNear = zNear === undefined ? 0.01 : zNear; + zFar = zFar === undefined ? 10000.0 : zFar; + + var handednessScale = rightHanded ? - 1.0 : 1.0; + + // start with an identity matrix + var mobj = new THREE.Matrix4(); + var m = mobj.elements; + + // and with scale/offset info for normalized device coords + var scaleAndOffset = fovToNDCScaleOffset( fov ); + + // X result, map clip edges to [-w,+w] + m[ 0 * 4 + 0 ] = scaleAndOffset.scale[ 0 ]; + m[ 0 * 4 + 1 ] = 0.0; + m[ 0 * 4 + 2 ] = scaleAndOffset.offset[ 0 ] * handednessScale; + m[ 0 * 4 + 3 ] = 0.0; + + // Y result, map clip edges to [-w,+w] + // Y offset is negated because this proj matrix transforms from world coords with Y=up, + // but the NDC scaling has Y=down (thanks D3D?) + m[ 1 * 4 + 0 ] = 0.0; + m[ 1 * 4 + 1 ] = scaleAndOffset.scale[ 1 ]; + m[ 1 * 4 + 2 ] = - scaleAndOffset.offset[ 1 ] * handednessScale; + m[ 1 * 4 + 3 ] = 0.0; + + // Z result (up to the app) + m[ 2 * 4 + 0 ] = 0.0; + m[ 2 * 4 + 1 ] = 0.0; + m[ 2 * 4 + 2 ] = zFar / ( zNear - zFar ) * - handednessScale; + m[ 2 * 4 + 3 ] = ( zFar * zNear ) / ( zNear - zFar ); + + // W result (= Z in) + m[ 3 * 4 + 0 ] = 0.0; + m[ 3 * 4 + 1 ] = 0.0; + m[ 3 * 4 + 2 ] = handednessScale; + m[ 3 * 4 + 3 ] = 0.0; + + mobj.transpose(); + + return mobj; + + } + + function fovToProjection( fov, rightHanded, zNear, zFar ) { + + var DEG2RAD = Math.PI / 180.0; + + var fovPort = { + upTan: Math.tan( fov.upDegrees * DEG2RAD ), + downTan: Math.tan( fov.downDegrees * DEG2RAD ), + leftTan: Math.tan( fov.leftDegrees * DEG2RAD ), + rightTan: Math.tan( fov.rightDegrees * DEG2RAD ) + }; + + return fovPortToProjection( fovPort, rightHanded, zNear, zFar ); + + } + +}; + +try { + module.exports = VREffect; +} catch (e) { + THREE.VREffect = VREffect; +} + +},{}],5:[function(_dereq_,module,exports){ +var self = self || {};// File:src/Three.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +var THREE = { REVISION: '73' }; + +// + +if ( typeof define === 'function' && define.amd ) { + + define( 'three', THREE ); + +} else if ( 'undefined' !== typeof exports && 'undefined' !== typeof module ) { + + module.exports = THREE; + +} + + +// polyfills + +if ( self.requestAnimationFrame === undefined || self.cancelAnimationFrame === undefined ) { + + // Missing in Android stock browser. + + ( function () { + + var lastTime = 0; + var vendors = [ 'ms', 'moz', 'webkit', 'o' ]; + + for ( var x = 0; x < vendors.length && ! self.requestAnimationFrame; ++ x ) { + + self.requestAnimationFrame = self[ vendors[ x ] + 'RequestAnimationFrame' ]; + self.cancelAnimationFrame = self[ vendors[ x ] + 'CancelAnimationFrame' ] || self[ vendors[ x ] + 'CancelRequestAnimationFrame' ]; + + } + + if ( self.requestAnimationFrame === undefined && self.setTimeout !== undefined ) { + + self.requestAnimationFrame = function ( callback ) { + + var currTime = Date.now(), timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) ); + var id = self.setTimeout( function () { + + callback( currTime + timeToCall ); + + }, timeToCall ); + lastTime = currTime + timeToCall; + return id; + + }; + + } + + if ( self.cancelAnimationFrame === undefined && self.clearTimeout !== undefined ) { + + self.cancelAnimationFrame = function ( id ) { + + self.clearTimeout( id ); + + }; + + } + + } )(); + +} + +// + +if ( self.performance === undefined ) { + + self.performance = {}; + +} + +if ( self.performance.now === undefined ) { + + ( function () { + + var start = Date.now(); + + self.performance.now = function () { + + return Date.now() - start; + + } + + } )(); + +} + +// + +if ( Number.EPSILON === undefined ) { + + Number.EPSILON = Math.pow( 2, -52 ); + +} + +// + +if ( Math.sign === undefined ) { + + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign + + Math.sign = function ( x ) { + + return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x; + + }; + +} + +if ( Function.prototype.name === undefined && Object.defineProperty !== undefined ) { + + // Missing in IE9-11. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name + + Object.defineProperty( Function.prototype, 'name', { + + get: function () { + + return this.toString().match( /^\s*function\s*(\S*)\s*\(/ )[ 1 ]; + + } + + } ); + +} + +// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.button + +THREE.MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 }; + +// GL STATE CONSTANTS + +THREE.CullFaceNone = 0; +THREE.CullFaceBack = 1; +THREE.CullFaceFront = 2; +THREE.CullFaceFrontBack = 3; + +THREE.FrontFaceDirectionCW = 0; +THREE.FrontFaceDirectionCCW = 1; + +// SHADOWING TYPES + +THREE.BasicShadowMap = 0; +THREE.PCFShadowMap = 1; +THREE.PCFSoftShadowMap = 2; + +// MATERIAL CONSTANTS + +// side + +THREE.FrontSide = 0; +THREE.BackSide = 1; +THREE.DoubleSide = 2; + +// shading + +THREE.FlatShading = 1; +THREE.SmoothShading = 2; + +// colors + +THREE.NoColors = 0; +THREE.FaceColors = 1; +THREE.VertexColors = 2; + +// blending modes + +THREE.NoBlending = 0; +THREE.NormalBlending = 1; +THREE.AdditiveBlending = 2; +THREE.SubtractiveBlending = 3; +THREE.MultiplyBlending = 4; +THREE.CustomBlending = 5; + +// custom blending equations +// (numbers start from 100 not to clash with other +// mappings to OpenGL constants defined in Texture.js) + +THREE.AddEquation = 100; +THREE.SubtractEquation = 101; +THREE.ReverseSubtractEquation = 102; +THREE.MinEquation = 103; +THREE.MaxEquation = 104; + +// custom blending destination factors + +THREE.ZeroFactor = 200; +THREE.OneFactor = 201; +THREE.SrcColorFactor = 202; +THREE.OneMinusSrcColorFactor = 203; +THREE.SrcAlphaFactor = 204; +THREE.OneMinusSrcAlphaFactor = 205; +THREE.DstAlphaFactor = 206; +THREE.OneMinusDstAlphaFactor = 207; + +// custom blending source factors + +//THREE.ZeroFactor = 200; +//THREE.OneFactor = 201; +//THREE.SrcAlphaFactor = 204; +//THREE.OneMinusSrcAlphaFactor = 205; +//THREE.DstAlphaFactor = 206; +//THREE.OneMinusDstAlphaFactor = 207; +THREE.DstColorFactor = 208; +THREE.OneMinusDstColorFactor = 209; +THREE.SrcAlphaSaturateFactor = 210; + +// depth modes + +THREE.NeverDepth = 0; +THREE.AlwaysDepth = 1; +THREE.LessDepth = 2; +THREE.LessEqualDepth = 3; +THREE.EqualDepth = 4; +THREE.GreaterEqualDepth = 5; +THREE.GreaterDepth = 6; +THREE.NotEqualDepth = 7; + + +// TEXTURE CONSTANTS + +THREE.MultiplyOperation = 0; +THREE.MixOperation = 1; +THREE.AddOperation = 2; + +// Mapping modes + +THREE.UVMapping = 300; + +THREE.CubeReflectionMapping = 301; +THREE.CubeRefractionMapping = 302; + +THREE.EquirectangularReflectionMapping = 303; +THREE.EquirectangularRefractionMapping = 304; + +THREE.SphericalReflectionMapping = 305; + +// Wrapping modes + +THREE.RepeatWrapping = 1000; +THREE.ClampToEdgeWrapping = 1001; +THREE.MirroredRepeatWrapping = 1002; + +// Filters + +THREE.NearestFilter = 1003; +THREE.NearestMipMapNearestFilter = 1004; +THREE.NearestMipMapLinearFilter = 1005; +THREE.LinearFilter = 1006; +THREE.LinearMipMapNearestFilter = 1007; +THREE.LinearMipMapLinearFilter = 1008; + +// Data types + +THREE.UnsignedByteType = 1009; +THREE.ByteType = 1010; +THREE.ShortType = 1011; +THREE.UnsignedShortType = 1012; +THREE.IntType = 1013; +THREE.UnsignedIntType = 1014; +THREE.FloatType = 1015; +THREE.HalfFloatType = 1025; + +// Pixel types + +//THREE.UnsignedByteType = 1009; +THREE.UnsignedShort4444Type = 1016; +THREE.UnsignedShort5551Type = 1017; +THREE.UnsignedShort565Type = 1018; + +// Pixel formats + +THREE.AlphaFormat = 1019; +THREE.RGBFormat = 1020; +THREE.RGBAFormat = 1021; +THREE.LuminanceFormat = 1022; +THREE.LuminanceAlphaFormat = 1023; +// THREE.RGBEFormat handled as THREE.RGBAFormat in shaders +THREE.RGBEFormat = THREE.RGBAFormat; //1024; + +// DDS / ST3C Compressed texture formats + +THREE.RGB_S3TC_DXT1_Format = 2001; +THREE.RGBA_S3TC_DXT1_Format = 2002; +THREE.RGBA_S3TC_DXT3_Format = 2003; +THREE.RGBA_S3TC_DXT5_Format = 2004; + + +// PVRTC compressed texture formats + +THREE.RGB_PVRTC_4BPPV1_Format = 2100; +THREE.RGB_PVRTC_2BPPV1_Format = 2101; +THREE.RGBA_PVRTC_4BPPV1_Format = 2102; +THREE.RGBA_PVRTC_2BPPV1_Format = 2103; + +// Loop styles for AnimationAction + +THREE.LoopOnce = 2200; +THREE.LoopRepeat = 2201; +THREE.LoopPingPong = 2202; + +// DEPRECATED + +THREE.Projector = function () { + + console.error( 'THREE.Projector has been moved to /examples/js/renderers/Projector.js.' ); + + this.projectVector = function ( vector, camera ) { + + console.warn( 'THREE.Projector: .projectVector() is now vector.project().' ); + vector.project( camera ); + + }; + + this.unprojectVector = function ( vector, camera ) { + + console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' ); + vector.unproject( camera ); + + }; + + this.pickingRay = function ( vector, camera ) { + + console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' ); + + }; + +}; + +THREE.CanvasRenderer = function () { + + console.error( 'THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js' ); + + this.domElement = document.createElement( 'canvas' ); + this.clear = function () {}; + this.render = function () {}; + this.setClearColor = function () {}; + this.setSize = function () {}; + +}; + +// File:src/math/Color.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Color = function ( color ) { + + if ( arguments.length === 3 ) { + + return this.fromArray( arguments ); + + } + + return this.set( color ); + +}; + +THREE.Color.prototype = { + + constructor: THREE.Color, + + r: 1, g: 1, b: 1, + + set: function ( value ) { + + if ( value instanceof THREE.Color ) { + + this.copy( value ); + + } else if ( typeof value === 'number' ) { + + this.setHex( value ); + + } else if ( typeof value === 'string' ) { + + this.setStyle( value ); + + } + + return this; + + }, + + setHex: function ( hex ) { + + hex = Math.floor( hex ); + + this.r = ( hex >> 16 & 255 ) / 255; + this.g = ( hex >> 8 & 255 ) / 255; + this.b = ( hex & 255 ) / 255; + + return this; + + }, + + setRGB: function ( r, g, b ) { + + this.r = r; + this.g = g; + this.b = b; + + return this; + + }, + + setHSL: function () { + + function hue2rgb( p, q, t ) { + + if ( t < 0 ) t += 1; + if ( t > 1 ) t -= 1; + if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; + if ( t < 1 / 2 ) return q; + if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); + return p; + + } + + return function ( h, s, l ) { + + // h,s,l ranges are in 0.0 - 1.0 + h = THREE.Math.euclideanModulo( h, 1 ); + s = THREE.Math.clamp( s, 0, 1 ); + l = THREE.Math.clamp( l, 0, 1 ); + + if ( s === 0 ) { + + this.r = this.g = this.b = l; + + } else { + + var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); + var q = ( 2 * l ) - p; + + this.r = hue2rgb( q, p, h + 1 / 3 ); + this.g = hue2rgb( q, p, h ); + this.b = hue2rgb( q, p, h - 1 / 3 ); + + } + + return this; + + }; + + }(), + + setStyle: function ( style ) { + + function handleAlpha( string ) { + + if ( string === undefined ) return; + + if ( parseFloat( string ) < 1 ) { + + console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); + + } + + } + + + var m; + + if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) { + + // rgb / hsl + + var color; + var name = m[ 1 ]; + var components = m[ 2 ]; + + switch ( name ) { + + case 'rgb': + case 'rgba': + + if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { + + // rgb(255,0,0) rgba(255,0,0,0.5) + this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; + this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; + this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; + + handleAlpha( color[ 5 ] ); + + return this; + + } + + if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { + + // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) + this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; + this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; + this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; + + handleAlpha( color[ 5 ] ); + + return this; + + } + + break; + + case 'hsl': + case 'hsla': + + if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { + + // hsl(120,50%,50%) hsla(120,50%,50%,0.5) + var h = parseFloat( color[ 1 ] ) / 360; + var s = parseInt( color[ 2 ], 10 ) / 100; + var l = parseInt( color[ 3 ], 10 ) / 100; + + handleAlpha( color[ 5 ] ); + + return this.setHSL( h, s, l ); + + } + + break; + + } + + } else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) { + + // hex color + + var hex = m[ 1 ]; + var size = hex.length; + + if ( size === 3 ) { + + // #ff0 + this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255; + this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255; + this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255; + + return this; + + } else if ( size === 6 ) { + + // #ff0000 + this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255; + this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255; + this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255; + + return this; + + } + + } + + if ( style && style.length > 0 ) { + + // color keywords + var hex = THREE.ColorKeywords[ style ]; + + if ( hex !== undefined ) { + + // red + this.setHex( hex ); + + } else { + + // unknown color + console.warn( 'THREE.Color: Unknown color ' + style ); + + } + + } + + return this; + + }, + + clone: function () { + + return new this.constructor( this.r, this.g, this.b ); + + }, + + copy: function ( color ) { + + this.r = color.r; + this.g = color.g; + this.b = color.b; + + return this; + + }, + + copyGammaToLinear: function ( color, gammaFactor ) { + + if ( gammaFactor === undefined ) gammaFactor = 2.0; + + this.r = Math.pow( color.r, gammaFactor ); + this.g = Math.pow( color.g, gammaFactor ); + this.b = Math.pow( color.b, gammaFactor ); + + return this; + + }, + + copyLinearToGamma: function ( color, gammaFactor ) { + + if ( gammaFactor === undefined ) gammaFactor = 2.0; + + var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0; + + this.r = Math.pow( color.r, safeInverse ); + this.g = Math.pow( color.g, safeInverse ); + this.b = Math.pow( color.b, safeInverse ); + + return this; + + }, + + convertGammaToLinear: function () { + + var r = this.r, g = this.g, b = this.b; + + this.r = r * r; + this.g = g * g; + this.b = b * b; + + return this; + + }, + + convertLinearToGamma: function () { + + this.r = Math.sqrt( this.r ); + this.g = Math.sqrt( this.g ); + this.b = Math.sqrt( this.b ); + + return this; + + }, + + getHex: function () { + + return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; + + }, + + getHexString: function () { + + return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 ); + + }, + + getHSL: function ( optionalTarget ) { + + // h,s,l ranges are in 0.0 - 1.0 + + var hsl = optionalTarget || { h: 0, s: 0, l: 0 }; + + var r = this.r, g = this.g, b = this.b; + + var max = Math.max( r, g, b ); + var min = Math.min( r, g, b ); + + var hue, saturation; + var lightness = ( min + max ) / 2.0; + + if ( min === max ) { + + hue = 0; + saturation = 0; + + } else { + + var delta = max - min; + + saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); + + switch ( max ) { + + case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; + case g: hue = ( b - r ) / delta + 2; break; + case b: hue = ( r - g ) / delta + 4; break; + + } + + hue /= 6; + + } + + hsl.h = hue; + hsl.s = saturation; + hsl.l = lightness; + + return hsl; + + }, + + getStyle: function () { + + return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')'; + + }, + + offsetHSL: function ( h, s, l ) { + + var hsl = this.getHSL(); + + hsl.h += h; hsl.s += s; hsl.l += l; + + this.setHSL( hsl.h, hsl.s, hsl.l ); + + return this; + + }, + + add: function ( color ) { + + this.r += color.r; + this.g += color.g; + this.b += color.b; + + return this; + + }, + + addColors: function ( color1, color2 ) { + + this.r = color1.r + color2.r; + this.g = color1.g + color2.g; + this.b = color1.b + color2.b; + + return this; + + }, + + addScalar: function ( s ) { + + this.r += s; + this.g += s; + this.b += s; + + return this; + + }, + + multiply: function ( color ) { + + this.r *= color.r; + this.g *= color.g; + this.b *= color.b; + + return this; + + }, + + multiplyScalar: function ( s ) { + + this.r *= s; + this.g *= s; + this.b *= s; + + return this; + + }, + + lerp: function ( color, alpha ) { + + this.r += ( color.r - this.r ) * alpha; + this.g += ( color.g - this.g ) * alpha; + this.b += ( color.b - this.b ) * alpha; + + return this; + + }, + + equals: function ( c ) { + + return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.r = array[ offset ]; + this.g = array[ offset + 1 ]; + this.b = array[ offset + 2 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.r; + array[ offset + 1 ] = this.g; + array[ offset + 2 ] = this.b; + + return array; + + } + +}; + +THREE.ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, +'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, +'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, +'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B, +'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B, +'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F, +'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3, +'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222, +'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700, +'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4, +'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00, +'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3, +'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA, +'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32, +'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3, +'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC, +'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD, +'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6, +'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9, +'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F, +'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE, +'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA, +'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0, +'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 }; + +// File:src/math/Quaternion.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://clara.io + */ + +THREE.Quaternion = function ( x, y, z, w ) { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._w = ( w !== undefined ) ? w : 1; + +}; + +THREE.Quaternion.prototype = { + + constructor: THREE.Quaternion, + + get x () { + + return this._x; + + }, + + set x ( value ) { + + this._x = value; + this.onChangeCallback(); + + }, + + get y () { + + return this._y; + + }, + + set y ( value ) { + + this._y = value; + this.onChangeCallback(); + + }, + + get z () { + + return this._z; + + }, + + set z ( value ) { + + this._z = value; + this.onChangeCallback(); + + }, + + get w () { + + return this._w; + + }, + + set w ( value ) { + + this._w = value; + this.onChangeCallback(); + + }, + + set: function ( x, y, z, w ) { + + this._x = x; + this._y = y; + this._z = z; + this._w = w; + + this.onChangeCallback(); + + return this; + + }, + + clone: function () { + + return new this.constructor( this._x, this._y, this._z, this._w ); + + }, + + copy: function ( quaternion ) { + + this._x = quaternion.x; + this._y = quaternion.y; + this._z = quaternion.z; + this._w = quaternion.w; + + this.onChangeCallback(); + + return this; + + }, + + setFromEuler: function ( euler, update ) { + + if ( euler instanceof THREE.Euler === false ) { + + throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + + } + + // http://www.mathworks.com/matlabcentral/fileexchange/ + // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ + // content/SpinCalc.m + + var c1 = Math.cos( euler._x / 2 ); + var c2 = Math.cos( euler._y / 2 ); + var c3 = Math.cos( euler._z / 2 ); + var s1 = Math.sin( euler._x / 2 ); + var s2 = Math.sin( euler._y / 2 ); + var s3 = Math.sin( euler._z / 2 ); + + var order = euler.order; + + if ( order === 'XYZ' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( order === 'YXZ' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } else if ( order === 'ZXY' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( order === 'ZYX' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } else if ( order === 'YZX' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( order === 'XZY' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } + + if ( update !== false ) this.onChangeCallback(); + + return this; + + }, + + setFromAxisAngle: function ( axis, angle ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm + + // assumes axis is normalized + + var halfAngle = angle / 2, s = Math.sin( halfAngle ); + + this._x = axis.x * s; + this._y = axis.y * s; + this._z = axis.z * s; + this._w = Math.cos( halfAngle ); + + this.onChangeCallback(); + + return this; + + }, + + setFromRotationMatrix: function ( m ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements, + + m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], + m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], + m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], + + trace = m11 + m22 + m33, + s; + + if ( trace > 0 ) { + + s = 0.5 / Math.sqrt( trace + 1.0 ); + + this._w = 0.25 / s; + this._x = ( m32 - m23 ) * s; + this._y = ( m13 - m31 ) * s; + this._z = ( m21 - m12 ) * s; + + } else if ( m11 > m22 && m11 > m33 ) { + + s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); + + this._w = ( m32 - m23 ) / s; + this._x = 0.25 * s; + this._y = ( m12 + m21 ) / s; + this._z = ( m13 + m31 ) / s; + + } else if ( m22 > m33 ) { + + s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); + + this._w = ( m13 - m31 ) / s; + this._x = ( m12 + m21 ) / s; + this._y = 0.25 * s; + this._z = ( m23 + m32 ) / s; + + } else { + + s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); + + this._w = ( m21 - m12 ) / s; + this._x = ( m13 + m31 ) / s; + this._y = ( m23 + m32 ) / s; + this._z = 0.25 * s; + + } + + this.onChangeCallback(); + + return this; + + }, + + setFromUnitVectors: function () { + + // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final + + // assumes direction vectors vFrom and vTo are normalized + + var v1, r; + + var EPS = 0.000001; + + return function ( vFrom, vTo ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + r = vFrom.dot( vTo ) + 1; + + if ( r < EPS ) { + + r = 0; + + if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { + + v1.set( - vFrom.y, vFrom.x, 0 ); + + } else { + + v1.set( 0, - vFrom.z, vFrom.y ); + + } + + } else { + + v1.crossVectors( vFrom, vTo ); + + } + + this._x = v1.x; + this._y = v1.y; + this._z = v1.z; + this._w = r; + + this.normalize(); + + return this; + + } + + }(), + + inverse: function () { + + this.conjugate().normalize(); + + return this; + + }, + + conjugate: function () { + + this._x *= - 1; + this._y *= - 1; + this._z *= - 1; + + this.onChangeCallback(); + + return this; + + }, + + dot: function ( v ) { + + return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; + + }, + + lengthSq: function () { + + return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; + + }, + + length: function () { + + return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); + + }, + + normalize: function () { + + var l = this.length(); + + if ( l === 0 ) { + + this._x = 0; + this._y = 0; + this._z = 0; + this._w = 1; + + } else { + + l = 1 / l; + + this._x = this._x * l; + this._y = this._y * l; + this._z = this._z * l; + this._w = this._w * l; + + } + + this.onChangeCallback(); + + return this; + + }, + + multiply: function ( q, p ) { + + if ( p !== undefined ) { + + console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); + return this.multiplyQuaternions( q, p ); + + } + + return this.multiplyQuaternions( this, q ); + + }, + + multiplyQuaternions: function ( a, b ) { + + // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm + + var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; + var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; + + this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; + this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; + this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; + this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; + + this.onChangeCallback(); + + return this; + + }, + + multiplyVector3: function ( vector ) { + + console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); + return vector.applyQuaternion( this ); + + }, + + slerp: function ( qb, t ) { + + if ( t === 0 ) return this; + if ( t === 1 ) return this.copy( qb ); + + var x = this._x, y = this._y, z = this._z, w = this._w; + + // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + + var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; + + if ( cosHalfTheta < 0 ) { + + this._w = - qb._w; + this._x = - qb._x; + this._y = - qb._y; + this._z = - qb._z; + + cosHalfTheta = - cosHalfTheta; + + } else { + + this.copy( qb ); + + } + + if ( cosHalfTheta >= 1.0 ) { + + this._w = w; + this._x = x; + this._y = y; + this._z = z; + + return this; + + } + + var halfTheta = Math.acos( cosHalfTheta ); + var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); + + if ( Math.abs( sinHalfTheta ) < 0.001 ) { + + this._w = 0.5 * ( w + this._w ); + this._x = 0.5 * ( x + this._x ); + this._y = 0.5 * ( y + this._y ); + this._z = 0.5 * ( z + this._z ); + + return this; + + } + + var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, + ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; + + this._w = ( w * ratioA + this._w * ratioB ); + this._x = ( x * ratioA + this._x * ratioB ); + this._y = ( y * ratioA + this._y * ratioB ); + this._z = ( z * ratioA + this._z * ratioB ); + + this.onChangeCallback(); + + return this; + + }, + + equals: function ( quaternion ) { + + return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this._x = array[ offset ]; + this._y = array[ offset + 1 ]; + this._z = array[ offset + 2 ]; + this._w = array[ offset + 3 ]; + + this.onChangeCallback(); + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this._x; + array[ offset + 1 ] = this._y; + array[ offset + 2 ] = this._z; + array[ offset + 3 ] = this._w; + + return array; + + }, + + onChange: function ( callback ) { + + this.onChangeCallback = callback; + + return this; + + }, + + onChangeCallback: function () {} + +}; + +THREE.Quaternion.slerp = function ( qa, qb, qm, t ) { + + return qm.copy( qa ).slerp( qb, t ); + +}; + +// File:src/math/Vector2.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author philogb / http://blog.thejit.org/ + * @author egraether / http://egraether.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.Vector2 = function ( x, y ) { + + this.x = x || 0; + this.y = y || 0; + +}; + +THREE.Vector2.prototype = { + + constructor: THREE.Vector2, + + get width() { return this.x }, + set width( value ) { this.x = value }, + + get height() { return this.y }, + set height( value ) { this.y = value }, + + // + + set: function ( x, y ) { + + this.x = x; + this.y = y; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + clone: function () { + + return new this.constructor( this.x, this.y ); + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + + return this; + + }, + + addScaledVector: function ( v, s ) { + + this.x += v.x * s; + this.y += v.y * s; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + + return this; + + }, + + subScalar: function ( s ) { + + this.x -= s; + this.y -= s; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + + return this; + + }, + + multiply: function ( v ) { + + this.x *= v.x; + this.y *= v.y; + + return this; + + }, + + multiplyScalar: function ( scalar ) { + + if ( isFinite( scalar ) ) { + this.x *= scalar; + this.y *= scalar; + } else { + this.x = 0; + this.y = 0; + } + + return this; + + }, + + divide: function ( v ) { + + this.x /= v.x; + this.y /= v.y; + + return this; + + }, + + divideScalar: function ( scalar ) { + + return this.multiplyScalar( 1 / scalar ); + + }, + + min: function ( v ) { + + this.x = Math.min( this.x, v.x ); + this.y = Math.min( this.y, v.y ); + + return this; + + }, + + max: function ( v ) { + + this.x = Math.max( this.x, v.x ); + this.y = Math.max( this.y, v.y ); + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + this.x = Math.max( min.x, Math.min( max.x, this.x ) ); + this.y = Math.max( min.y, Math.min( max.y, this.y ) ); + + return this; + + }, + + clampScalar: function () { + + var min, max; + + return function clampScalar( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector2(); + max = new THREE.Vector2(); + + } + + min.set( minVal, minVal ); + max.set( maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + }(), + + clampLength: function ( min, max ) { + + var length = this.length(); + + this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length ); + + return this; + + }, + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y ); + + }, + + lengthManhattan: function() { + + return Math.abs( this.x ) + Math.abs( this.y ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + distanceTo: function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared: function ( v ) { + + var dx = this.x - v.x, dy = this.y - v.y; + return dx * dx + dy * dy; + + }, + + setLength: function ( length ) { + + return this.multiplyScalar( length / this.length() ); + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + + return this; + + }, + + lerpVectors: function ( v1, v2, alpha ) { + + this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + + return array; + + }, + + fromAttribute: function ( attribute, index, offset ) { + + if ( offset === undefined ) offset = 0; + + index = index * attribute.itemSize + offset; + + this.x = attribute.array[ index ]; + this.y = attribute.array[ index + 1 ]; + + return this; + + }, + + rotateAround: function ( center, angle ) { + + var c = Math.cos( angle ), s = Math.sin( angle ); + + var x = this.x - center.x; + var y = this.y - center.y; + + this.x = x * c - y * s + center.x; + this.y = x * s + y * c + center.y; + + return this; + + } + +}; + +// File:src/math/Vector3.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author *kile / http://kile.stravaganza.org/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Vector3 = function ( x, y, z ) { + + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + +}; + +THREE.Vector3.prototype = { + + constructor: THREE.Vector3, + + set: function ( x, y, z ) { + + this.x = x; + this.y = y; + this.z = z; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setZ: function ( z ) { + + this.z = z; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + case 2: this.z = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + case 2: return this.z; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + clone: function () { + + return new this.constructor( this.x, this.y, this.z ); + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + this.z = v.z; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + this.z += v.z; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + this.z += s; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + + return this; + + }, + + addScaledVector: function ( v, s ) { + + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + + return this; + + }, + + subScalar: function ( s ) { + + this.x -= s; + this.y -= s; + this.z -= s; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + + return this; + + }, + + multiply: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); + return this.multiplyVectors( v, w ); + + } + + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; + + return this; + + }, + + multiplyScalar: function ( scalar ) { + + if ( isFinite( scalar ) ) { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + } else { + this.x = 0; + this.y = 0; + this.z = 0; + } + + return this; + + }, + + multiplyVectors: function ( a, b ) { + + this.x = a.x * b.x; + this.y = a.y * b.y; + this.z = a.z * b.z; + + return this; + + }, + + applyEuler: function () { + + var quaternion; + + return function applyEuler( euler ) { + + if ( euler instanceof THREE.Euler === false ) { + + console.error( 'THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + + } + + if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); + + this.applyQuaternion( quaternion.setFromEuler( euler ) ); + + return this; + + }; + + }(), + + applyAxisAngle: function () { + + var quaternion; + + return function applyAxisAngle( axis, angle ) { + + if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); + + this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); + + return this; + + }; + + }(), + + applyMatrix3: function ( m ) { + + var x = this.x; + var y = this.y; + var z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; + this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; + this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; + + return this; + + }, + + applyMatrix4: function ( m ) { + + // input: THREE.Matrix4 affine matrix + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ]; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ]; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ]; + + return this; + + }, + + applyProjection: function ( m ) { + + // input: THREE.Matrix4 projection matrix + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide + + this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d; + this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d; + this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d; + + return this; + + }, + + applyQuaternion: function ( q ) { + + var x = this.x; + var y = this.y; + var z = this.z; + + var qx = q.x; + var qy = q.y; + var qz = q.z; + var qw = q.w; + + // calculate quat * vector + + var ix = qw * x + qy * z - qz * y; + var iy = qw * y + qz * x - qx * z; + var iz = qw * z + qx * y - qy * x; + var iw = - qx * x - qy * y - qz * z; + + // calculate result * inverse quat + + this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; + this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; + this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; + + return this; + + }, + + project: function () { + + var matrix; + + return function project( camera ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + + matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) ); + return this.applyProjection( matrix ); + + }; + + }(), + + unproject: function () { + + var matrix; + + return function unproject( camera ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + + matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) ); + return this.applyProjection( matrix ); + + }; + + }(), + + transformDirection: function ( m ) { + + // input: THREE.Matrix4 affine matrix + // vector interpreted as a direction + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; + + this.normalize(); + + return this; + + }, + + divide: function ( v ) { + + this.x /= v.x; + this.y /= v.y; + this.z /= v.z; + + return this; + + }, + + divideScalar: function ( scalar ) { + + return this.multiplyScalar( 1 / scalar ); + + }, + + min: function ( v ) { + + this.x = Math.min( this.x, v.x ); + this.y = Math.min( this.y, v.y ); + this.z = Math.min( this.z, v.z ); + + return this; + + }, + + max: function ( v ) { + + this.x = Math.max( this.x, v.x ); + this.y = Math.max( this.y, v.y ); + this.z = Math.max( this.z, v.z ); + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + this.x = Math.max( min.x, Math.min( max.x, this.x ) ); + this.y = Math.max( min.y, Math.min( max.y, this.y ) ); + this.z = Math.max( min.z, Math.min( max.z, this.z ) ); + + return this; + + }, + + clampScalar: function () { + + var min, max; + + return function clampScalar( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector3(); + max = new THREE.Vector3(); + + } + + min.set( minVal, minVal, minVal ); + max.set( maxVal, maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + }(), + + clampLength: function ( min, max ) { + + var length = this.length(); + + this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length ); + + return this; + + }, + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y + this.z * v.z; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y + this.z * this.z; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); + + }, + + lengthManhattan: function () { + + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + setLength: function ( length ) { + + return this.multiplyScalar( length / this.length() ); + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; + + return this; + + }, + + lerpVectors: function ( v1, v2, alpha ) { + + this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); + + return this; + + }, + + cross: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); + return this.crossVectors( v, w ); + + } + + var x = this.x, y = this.y, z = this.z; + + this.x = y * v.z - z * v.y; + this.y = z * v.x - x * v.z; + this.z = x * v.y - y * v.x; + + return this; + + }, + + crossVectors: function ( a, b ) { + + var ax = a.x, ay = a.y, az = a.z; + var bx = b.x, by = b.y, bz = b.z; + + this.x = ay * bz - az * by; + this.y = az * bx - ax * bz; + this.z = ax * by - ay * bx; + + return this; + + }, + + projectOnVector: function () { + + var v1, dot; + + return function projectOnVector( vector ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + v1.copy( vector ).normalize(); + + dot = this.dot( v1 ); + + return this.copy( v1 ).multiplyScalar( dot ); + + }; + + }(), + + projectOnPlane: function () { + + var v1; + + return function projectOnPlane( planeNormal ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + v1.copy( this ).projectOnVector( planeNormal ); + + return this.sub( v1 ); + + } + + }(), + + reflect: function () { + + // reflect incident vector off plane orthogonal to normal + // normal is assumed to have unit length + + var v1; + + return function reflect( normal ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); + + } + + }(), + + angleTo: function ( v ) { + + var theta = this.dot( v ) / ( this.length() * v.length() ); + + // clamp, to handle numerical problems + + return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) ); + + }, + + distanceTo: function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared: function ( v ) { + + var dx = this.x - v.x; + var dy = this.y - v.y; + var dz = this.z - v.z; + + return dx * dx + dy * dy + dz * dz; + + }, + + setEulerFromRotationMatrix: function ( m, order ) { + + console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); + + }, + + setEulerFromQuaternion: function ( q, order ) { + + console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); + + }, + + getPositionFromMatrix: function ( m ) { + + console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); + + return this.setFromMatrixPosition( m ); + + }, + + getScaleFromMatrix: function ( m ) { + + console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); + + return this.setFromMatrixScale( m ); + + }, + + getColumnFromMatrix: function ( index, matrix ) { + + console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); + + return this.setFromMatrixColumn( index, matrix ); + + }, + + setFromMatrixPosition: function ( m ) { + + this.x = m.elements[ 12 ]; + this.y = m.elements[ 13 ]; + this.z = m.elements[ 14 ]; + + return this; + + }, + + setFromMatrixScale: function ( m ) { + + var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length(); + var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length(); + var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length(); + + this.x = sx; + this.y = sy; + this.z = sz; + + return this; + + }, + + setFromMatrixColumn: function ( index, matrix ) { + + var offset = index * 4; + + var me = matrix.elements; + + this.x = me[ offset ]; + this.y = me[ offset + 1 ]; + this.z = me[ offset + 2 ]; + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; + + return array; + + }, + + fromAttribute: function ( attribute, index, offset ) { + + if ( offset === undefined ) offset = 0; + + index = index * attribute.itemSize + offset; + + this.x = attribute.array[ index ]; + this.y = attribute.array[ index + 1 ]; + this.z = attribute.array[ index + 2 ]; + + return this; + + } + +}; + +// File:src/math/Vector4.js + +/** + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Vector4 = function ( x, y, z, w ) { + + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + this.w = ( w !== undefined ) ? w : 1; + +}; + +THREE.Vector4.prototype = { + + constructor: THREE.Vector4, + + set: function ( x, y, z, w ) { + + this.x = x; + this.y = y; + this.z = z; + this.w = w; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setZ: function ( z ) { + + this.z = z; + + return this; + + }, + + setW: function ( w ) { + + this.w = w; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + case 2: this.z = value; break; + case 3: this.w = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + case 2: return this.z; + case 3: return this.w; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + clone: function () { + + return new this.constructor( this.x, this.y, this.z, this.w ); + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + this.z = v.z; + this.w = ( v.w !== undefined ) ? v.w : 1; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + this.z += v.z; + this.w += v.w; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + this.z += s; + this.w += s; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + this.w = a.w + b.w; + + return this; + + }, + + addScaledVector: function ( v, s ) { + + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; + this.w += v.w * s; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + this.w -= v.w; + + return this; + + }, + + subScalar: function ( s ) { + + this.x -= s; + this.y -= s; + this.z -= s; + this.w -= s; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + this.w = a.w - b.w; + + return this; + + }, + + multiplyScalar: function ( scalar ) { + + if ( isFinite( scalar ) ) { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + this.w *= scalar; + } else { + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 0; + } + + return this; + + }, + + applyMatrix4: function ( m ) { + + var x = this.x; + var y = this.y; + var z = this.z; + var w = this.w; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; + this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; + + return this; + + }, + + divideScalar: function ( scalar ) { + + return this.multiplyScalar( 1 / scalar ); + + }, + + setAxisAngleFromQuaternion: function ( q ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm + + // q is assumed to be normalized + + this.w = 2 * Math.acos( q.w ); + + var s = Math.sqrt( 1 - q.w * q.w ); + + if ( s < 0.0001 ) { + + this.x = 1; + this.y = 0; + this.z = 0; + + } else { + + this.x = q.x / s; + this.y = q.y / s; + this.z = q.z / s; + + } + + return this; + + }, + + setAxisAngleFromRotationMatrix: function ( m ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var angle, x, y, z, // variables for result + epsilon = 0.01, // margin to allow for rounding errors + epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees + + te = m.elements, + + m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], + m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], + m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; + + if ( ( Math.abs( m12 - m21 ) < epsilon ) + && ( Math.abs( m13 - m31 ) < epsilon ) + && ( Math.abs( m23 - m32 ) < epsilon ) ) { + + // singularity found + // first check for identity matrix which must have +1 for all terms + // in leading diagonal and zero in other terms + + if ( ( Math.abs( m12 + m21 ) < epsilon2 ) + && ( Math.abs( m13 + m31 ) < epsilon2 ) + && ( Math.abs( m23 + m32 ) < epsilon2 ) + && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { + + // this singularity is identity matrix so angle = 0 + + this.set( 1, 0, 0, 0 ); + + return this; // zero angle, arbitrary axis + + } + + // otherwise this singularity is angle = 180 + + angle = Math.PI; + + var xx = ( m11 + 1 ) / 2; + var yy = ( m22 + 1 ) / 2; + var zz = ( m33 + 1 ) / 2; + var xy = ( m12 + m21 ) / 4; + var xz = ( m13 + m31 ) / 4; + var yz = ( m23 + m32 ) / 4; + + if ( ( xx > yy ) && ( xx > zz ) ) { + + // m11 is the largest diagonal term + + if ( xx < epsilon ) { + + x = 0; + y = 0.707106781; + z = 0.707106781; + + } else { + + x = Math.sqrt( xx ); + y = xy / x; + z = xz / x; + + } + + } else if ( yy > zz ) { + + // m22 is the largest diagonal term + + if ( yy < epsilon ) { + + x = 0.707106781; + y = 0; + z = 0.707106781; + + } else { + + y = Math.sqrt( yy ); + x = xy / y; + z = yz / y; + + } + + } else { + + // m33 is the largest diagonal term so base result on this + + if ( zz < epsilon ) { + + x = 0.707106781; + y = 0.707106781; + z = 0; + + } else { + + z = Math.sqrt( zz ); + x = xz / z; + y = yz / z; + + } + + } + + this.set( x, y, z, angle ); + + return this; // return 180 deg rotation + + } + + // as we have reached here there are no singularities so we can handle normally + + var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + + ( m13 - m31 ) * ( m13 - m31 ) + + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize + + if ( Math.abs( s ) < 0.001 ) s = 1; + + // prevent divide by zero, should not happen if matrix is orthogonal and should be + // caught by singularity test above, but I've left it in just in case + + this.x = ( m32 - m23 ) / s; + this.y = ( m13 - m31 ) / s; + this.z = ( m21 - m12 ) / s; + this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); + + return this; + + }, + + min: function ( v ) { + + this.x = Math.min( this.x, v.x ); + this.y = Math.min( this.y, v.y ); + this.z = Math.min( this.z, v.z ); + this.w = Math.min( this.w, v.w ); + + return this; + + }, + + max: function ( v ) { + + this.x = Math.max( this.x, v.x ); + this.y = Math.max( this.y, v.y ); + this.z = Math.max( this.z, v.z ); + this.w = Math.max( this.w, v.w ); + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + this.x = Math.max( min.x, Math.min( max.x, this.x ) ); + this.y = Math.max( min.y, Math.min( max.y, this.y ) ); + this.z = Math.max( min.z, Math.min( max.z, this.z ) ); + this.w = Math.max( min.w, Math.min( max.w, this.w ) ); + + return this; + + }, + + clampScalar: function () { + + var min, max; + + return function clampScalar( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector4(); + max = new THREE.Vector4(); + + } + + min.set( minVal, minVal, minVal, minVal ); + max.set( maxVal, maxVal, maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + }(), + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); + this.w = Math.floor( this.w ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); + this.w = Math.ceil( this.w ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); + this.w = Math.round( this.w ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); + this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; + this.w = - this.w; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); + + }, + + lengthManhattan: function () { + + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + setLength: function ( length ) { + + return this.multiplyScalar( length / this.length() ); + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; + this.w += ( v.w - this.w ) * alpha; + + return this; + + }, + + lerpVectors: function ( v1, v2, alpha ) { + + this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; + this.w = array[ offset + 3 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; + array[ offset + 3 ] = this.w; + + return array; + + }, + + fromAttribute: function ( attribute, index, offset ) { + + if ( offset === undefined ) offset = 0; + + index = index * attribute.itemSize + offset; + + this.x = attribute.array[ index ]; + this.y = attribute.array[ index + 1 ]; + this.z = attribute.array[ index + 2 ]; + this.w = attribute.array[ index + 3 ]; + + return this; + + } + +}; + +// File:src/math/Euler.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://clara.io + */ + +THREE.Euler = function ( x, y, z, order ) { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._order = order || THREE.Euler.DefaultOrder; + +}; + +THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; + +THREE.Euler.DefaultOrder = 'XYZ'; + +THREE.Euler.prototype = { + + constructor: THREE.Euler, + + get x () { + + return this._x; + + }, + + set x ( value ) { + + this._x = value; + this.onChangeCallback(); + + }, + + get y () { + + return this._y; + + }, + + set y ( value ) { + + this._y = value; + this.onChangeCallback(); + + }, + + get z () { + + return this._z; + + }, + + set z ( value ) { + + this._z = value; + this.onChangeCallback(); + + }, + + get order () { + + return this._order; + + }, + + set order ( value ) { + + this._order = value; + this.onChangeCallback(); + + }, + + set: function ( x, y, z, order ) { + + this._x = x; + this._y = y; + this._z = z; + this._order = order || this._order; + + this.onChangeCallback(); + + return this; + + }, + + clone: function () { + + return new this.constructor( this._x, this._y, this._z, this._order); + + }, + + copy: function ( euler ) { + + this._x = euler._x; + this._y = euler._y; + this._z = euler._z; + this._order = euler._order; + + this.onChangeCallback(); + + return this; + + }, + + setFromRotationMatrix: function ( m, order, update ) { + + var clamp = THREE.Math.clamp; + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements; + var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; + var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; + var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; + + order = order || this._order; + + if ( order === 'XYZ' ) { + + this._y = Math.asin( clamp( m13, - 1, 1 ) ); + + if ( Math.abs( m13 ) < 0.99999 ) { + + this._x = Math.atan2( - m23, m33 ); + this._z = Math.atan2( - m12, m11 ); + + } else { + + this._x = Math.atan2( m32, m22 ); + this._z = 0; + + } + + } else if ( order === 'YXZ' ) { + + this._x = Math.asin( - clamp( m23, - 1, 1 ) ); + + if ( Math.abs( m23 ) < 0.99999 ) { + + this._y = Math.atan2( m13, m33 ); + this._z = Math.atan2( m21, m22 ); + + } else { + + this._y = Math.atan2( - m31, m11 ); + this._z = 0; + + } + + } else if ( order === 'ZXY' ) { + + this._x = Math.asin( clamp( m32, - 1, 1 ) ); + + if ( Math.abs( m32 ) < 0.99999 ) { + + this._y = Math.atan2( - m31, m33 ); + this._z = Math.atan2( - m12, m22 ); + + } else { + + this._y = 0; + this._z = Math.atan2( m21, m11 ); + + } + + } else if ( order === 'ZYX' ) { + + this._y = Math.asin( - clamp( m31, - 1, 1 ) ); + + if ( Math.abs( m31 ) < 0.99999 ) { + + this._x = Math.atan2( m32, m33 ); + this._z = Math.atan2( m21, m11 ); + + } else { + + this._x = 0; + this._z = Math.atan2( - m12, m22 ); + + } + + } else if ( order === 'YZX' ) { + + this._z = Math.asin( clamp( m21, - 1, 1 ) ); + + if ( Math.abs( m21 ) < 0.99999 ) { + + this._x = Math.atan2( - m23, m22 ); + this._y = Math.atan2( - m31, m11 ); + + } else { + + this._x = 0; + this._y = Math.atan2( m13, m33 ); + + } + + } else if ( order === 'XZY' ) { + + this._z = Math.asin( - clamp( m12, - 1, 1 ) ); + + if ( Math.abs( m12 ) < 0.99999 ) { + + this._x = Math.atan2( m32, m22 ); + this._y = Math.atan2( m13, m11 ); + + } else { + + this._x = Math.atan2( - m23, m33 ); + this._y = 0; + + } + + } else { + + console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order ) + + } + + this._order = order; + + if ( update !== false ) this.onChangeCallback(); + + return this; + + }, + + setFromQuaternion: function () { + + var matrix; + + return function ( q, order, update ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + matrix.makeRotationFromQuaternion( q ); + this.setFromRotationMatrix( matrix, order, update ); + + return this; + + }; + + }(), + + setFromVector3: function ( v, order ) { + + return this.set( v.x, v.y, v.z, order || this._order ); + + }, + + reorder: function () { + + // WARNING: this discards revolution information -bhouston + + var q = new THREE.Quaternion(); + + return function ( newOrder ) { + + q.setFromEuler( this ); + this.setFromQuaternion( q, newOrder ); + + }; + + }(), + + equals: function ( euler ) { + + return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); + + }, + + fromArray: function ( array ) { + + this._x = array[ 0 ]; + this._y = array[ 1 ]; + this._z = array[ 2 ]; + if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; + + this.onChangeCallback(); + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this._x; + array[ offset + 1 ] = this._y; + array[ offset + 2 ] = this._z; + array[ offset + 3 ] = this._order; + + return array; + + }, + + toVector3: function ( optionalResult ) { + + if ( optionalResult ) { + + return optionalResult.set( this._x, this._y, this._z ); + + } else { + + return new THREE.Vector3( this._x, this._y, this._z ); + + } + + }, + + onChange: function ( callback ) { + + this.onChangeCallback = callback; + + return this; + + }, + + onChangeCallback: function () {} + +}; + +// File:src/math/Line3.js + +/** + * @author bhouston / http://clara.io + */ + +THREE.Line3 = function ( start, end ) { + + this.start = ( start !== undefined ) ? start : new THREE.Vector3(); + this.end = ( end !== undefined ) ? end : new THREE.Vector3(); + +}; + +THREE.Line3.prototype = { + + constructor: THREE.Line3, + + set: function ( start, end ) { + + this.start.copy( start ); + this.end.copy( end ); + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( line ) { + + this.start.copy( line.start ); + this.end.copy( line.end ); + + return this; + + }, + + center: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); + + }, + + delta: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.subVectors( this.end, this.start ); + + }, + + distanceSq: function () { + + return this.start.distanceToSquared( this.end ); + + }, + + distance: function () { + + return this.start.distanceTo( this.end ); + + }, + + at: function ( t, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + return this.delta( result ).multiplyScalar( t ).add( this.start ); + + }, + + closestPointToPointParameter: function () { + + var startP = new THREE.Vector3(); + var startEnd = new THREE.Vector3(); + + return function ( point, clampToLine ) { + + startP.subVectors( point, this.start ); + startEnd.subVectors( this.end, this.start ); + + var startEnd2 = startEnd.dot( startEnd ); + var startEnd_startP = startEnd.dot( startP ); + + var t = startEnd_startP / startEnd2; + + if ( clampToLine ) { + + t = THREE.Math.clamp( t, 0, 1 ); + + } + + return t; + + }; + + }(), + + closestPointToPoint: function ( point, clampToLine, optionalTarget ) { + + var t = this.closestPointToPointParameter( point, clampToLine ); + + var result = optionalTarget || new THREE.Vector3(); + + return this.delta( result ).multiplyScalar( t ).add( this.start ); + + }, + + applyMatrix4: function ( matrix ) { + + this.start.applyMatrix4( matrix ); + this.end.applyMatrix4( matrix ); + + return this; + + }, + + equals: function ( line ) { + + return line.start.equals( this.start ) && line.end.equals( this.end ); + + } + +}; + +// File:src/math/Box2.js + +/** + * @author bhouston / http://clara.io + */ + +THREE.Box2 = function ( min, max ) { + + this.min = ( min !== undefined ) ? min : new THREE.Vector2( Infinity, Infinity ); + this.max = ( max !== undefined ) ? max : new THREE.Vector2( - Infinity, - Infinity ); + +}; + +THREE.Box2.prototype = { + + constructor: THREE.Box2, + + set: function ( min, max ) { + + this.min.copy( min ); + this.max.copy( max ); + + return this; + + }, + + setFromPoints: function ( points ) { + + this.makeEmpty(); + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + this.expandByPoint( points[ i ] ) + + } + + return this; + + }, + + setFromCenterAndSize: function () { + + var v1 = new THREE.Vector2(); + + return function ( center, size ) { + + var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); + this.min.copy( center ).sub( halfSize ); + this.max.copy( center ).add( halfSize ); + + return this; + + }; + + }(), + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( box ) { + + this.min.copy( box.min ); + this.max.copy( box.max ); + + return this; + + }, + + makeEmpty: function () { + + this.min.x = this.min.y = Infinity; + this.max.x = this.max.y = - Infinity; + + return this; + + }, + + empty: function () { + + // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + + return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); + + }, + + center: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector2(); + return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); + + }, + + size: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector2(); + return result.subVectors( this.max, this.min ); + + }, + + expandByPoint: function ( point ) { + + this.min.min( point ); + this.max.max( point ); + + return this; + + }, + + expandByVector: function ( vector ) { + + this.min.sub( vector ); + this.max.add( vector ); + + return this; + + }, + + expandByScalar: function ( scalar ) { + + this.min.addScalar( - scalar ); + this.max.addScalar( scalar ); + + return this; + + }, + + containsPoint: function ( point ) { + + if ( point.x < this.min.x || point.x > this.max.x || + point.y < this.min.y || point.y > this.max.y ) { + + return false; + + } + + return true; + + }, + + containsBox: function ( box ) { + + if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) && + ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) ) { + + return true; + + } + + return false; + + }, + + getParameter: function ( point, optionalTarget ) { + + // This can potentially have a divide by zero if the box + // has a size dimension of 0. + + var result = optionalTarget || new THREE.Vector2(); + + return result.set( + ( point.x - this.min.x ) / ( this.max.x - this.min.x ), + ( point.y - this.min.y ) / ( this.max.y - this.min.y ) + ); + + }, + + isIntersectionBox: function ( box ) { + + // using 6 splitting planes to rule out intersections. + + if ( box.max.x < this.min.x || box.min.x > this.max.x || + box.max.y < this.min.y || box.min.y > this.max.y ) { + + return false; + + } + + return true; + + }, + + clampPoint: function ( point, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector2(); + return result.copy( point ).clamp( this.min, this.max ); + + }, + + distanceToPoint: function () { + + var v1 = new THREE.Vector2(); + + return function ( point ) { + + var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); + return clampedPoint.sub( point ).length(); + + }; + + }(), + + intersect: function ( box ) { + + this.min.max( box.min ); + this.max.min( box.max ); + + return this; + + }, + + union: function ( box ) { + + this.min.min( box.min ); + this.max.max( box.max ); + + return this; + + }, + + translate: function ( offset ) { + + this.min.add( offset ); + this.max.add( offset ); + + return this; + + }, + + equals: function ( box ) { + + return box.min.equals( this.min ) && box.max.equals( this.max ); + + } + +}; + +// File:src/math/Box3.js + +/** + * @author bhouston / http://clara.io + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Box3 = function ( min, max ) { + + this.min = ( min !== undefined ) ? min : new THREE.Vector3( Infinity, Infinity, Infinity ); + this.max = ( max !== undefined ) ? max : new THREE.Vector3( - Infinity, - Infinity, - Infinity ); + +}; + +THREE.Box3.prototype = { + + constructor: THREE.Box3, + + set: function ( min, max ) { + + this.min.copy( min ); + this.max.copy( max ); + + return this; + + }, + + setFromPoints: function ( points ) { + + this.makeEmpty(); + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + this.expandByPoint( points[ i ] ); + + } + + return this; + + }, + + setFromCenterAndSize: function () { + + var v1 = new THREE.Vector3(); + + return function ( center, size ) { + + var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); + + this.min.copy( center ).sub( halfSize ); + this.max.copy( center ).add( halfSize ); + + return this; + + }; + + }(), + + setFromObject: function () { + + // Computes the world-axis-aligned bounding box of an object (including its children), + // accounting for both the object's, and children's, world transforms + + var v1 = new THREE.Vector3(); + + return function ( object ) { + + var scope = this; + + object.updateMatrixWorld( true ); + + this.makeEmpty(); + + object.traverse( function ( node ) { + + var geometry = node.geometry; + + if ( geometry !== undefined ) { + + if ( geometry instanceof THREE.Geometry ) { + + var vertices = geometry.vertices; + + for ( var i = 0, il = vertices.length; i < il; i ++ ) { + + v1.copy( vertices[ i ] ); + + v1.applyMatrix4( node.matrixWorld ); + + scope.expandByPoint( v1 ); + + } + + } else if ( geometry instanceof THREE.BufferGeometry && geometry.attributes[ 'position' ] !== undefined ) { + + var positions = geometry.attributes[ 'position' ].array; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + v1.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + + v1.applyMatrix4( node.matrixWorld ); + + scope.expandByPoint( v1 ); + + } + + } + + } + + } ); + + return this; + + }; + + }(), + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( box ) { + + this.min.copy( box.min ); + this.max.copy( box.max ); + + return this; + + }, + + makeEmpty: function () { + + this.min.x = this.min.y = this.min.z = Infinity; + this.max.x = this.max.y = this.max.z = - Infinity; + + return this; + + }, + + empty: function () { + + // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + + return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); + + }, + + center: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); + + }, + + size: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.subVectors( this.max, this.min ); + + }, + + expandByPoint: function ( point ) { + + this.min.min( point ); + this.max.max( point ); + + return this; + + }, + + expandByVector: function ( vector ) { + + this.min.sub( vector ); + this.max.add( vector ); + + return this; + + }, + + expandByScalar: function ( scalar ) { + + this.min.addScalar( - scalar ); + this.max.addScalar( scalar ); + + return this; + + }, + + containsPoint: function ( point ) { + + if ( point.x < this.min.x || point.x > this.max.x || + point.y < this.min.y || point.y > this.max.y || + point.z < this.min.z || point.z > this.max.z ) { + + return false; + + } + + return true; + + }, + + containsBox: function ( box ) { + + if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) && + ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) && + ( this.min.z <= box.min.z ) && ( box.max.z <= this.max.z ) ) { + + return true; + + } + + return false; + + }, + + getParameter: function ( point, optionalTarget ) { + + // This can potentially have a divide by zero if the box + // has a size dimension of 0. + + var result = optionalTarget || new THREE.Vector3(); + + return result.set( + ( point.x - this.min.x ) / ( this.max.x - this.min.x ), + ( point.y - this.min.y ) / ( this.max.y - this.min.y ), + ( point.z - this.min.z ) / ( this.max.z - this.min.z ) + ); + + }, + + isIntersectionBox: function ( box ) { + + // using 6 splitting planes to rule out intersections. + + if ( box.max.x < this.min.x || box.min.x > this.max.x || + box.max.y < this.min.y || box.min.y > this.max.y || + box.max.z < this.min.z || box.min.z > this.max.z ) { + + return false; + + } + + return true; + + }, + + clampPoint: function ( point, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.copy( point ).clamp( this.min, this.max ); + + }, + + distanceToPoint: function () { + + var v1 = new THREE.Vector3(); + + return function ( point ) { + + var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); + return clampedPoint.sub( point ).length(); + + }; + + }(), + + getBoundingSphere: function () { + + var v1 = new THREE.Vector3(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Sphere(); + + result.center = this.center(); + result.radius = this.size( v1 ).length() * 0.5; + + return result; + + }; + + }(), + + intersect: function ( box ) { + + this.min.max( box.min ); + this.max.min( box.max ); + + return this; + + }, + + union: function ( box ) { + + this.min.min( box.min ); + this.max.max( box.max ); + + return this; + + }, + + applyMatrix4: function () { + + var points = [ + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3() + ]; + + return function ( matrix ) { + + // NOTE: I am using a binary pattern to specify all 2^3 combinations below + points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 + points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 + points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 + points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 + points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 + points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 + points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 + points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 + + this.makeEmpty(); + this.setFromPoints( points ); + + return this; + + }; + + }(), + + translate: function ( offset ) { + + this.min.add( offset ); + this.max.add( offset ); + + return this; + + }, + + equals: function ( box ) { + + return box.min.equals( this.min ) && box.max.equals( this.max ); + + } + +}; + +// File:src/math/Matrix3.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://clara.io + */ + +THREE.Matrix3 = function () { + + this.elements = new Float32Array( [ + + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + + ] ); + + if ( arguments.length > 0 ) { + + console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); + + } + +}; + +THREE.Matrix3.prototype = { + + constructor: THREE.Matrix3, + + set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { + + var te = this.elements; + + te[ 0 ] = n11; te[ 3 ] = n12; te[ 6 ] = n13; + te[ 1 ] = n21; te[ 4 ] = n22; te[ 7 ] = n23; + te[ 2 ] = n31; te[ 5 ] = n32; te[ 8 ] = n33; + + return this; + + }, + + identity: function () { + + this.set( + + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + + ); + + return this; + + }, + + clone: function () { + + return new this.constructor().fromArray( this.elements ); + + }, + + copy: function ( m ) { + + var me = m.elements; + + this.set( + + me[ 0 ], me[ 3 ], me[ 6 ], + me[ 1 ], me[ 4 ], me[ 7 ], + me[ 2 ], me[ 5 ], me[ 8 ] + + ); + + return this; + + }, + + multiplyVector3: function ( vector ) { + + console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); + return vector.applyMatrix3( this ); + + }, + + multiplyVector3Array: function ( a ) { + + console.warn( 'THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); + return this.applyToVector3Array( a ); + + }, + + applyToVector3Array: function () { + + var v1; + + return function ( array, offset, length ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + if ( offset === undefined ) offset = 0; + if ( length === undefined ) length = array.length; + + for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) { + + v1.fromArray( array, j ); + v1.applyMatrix3( this ); + v1.toArray( array, j ); + + } + + return array; + + }; + + }(), + + applyToBuffer: function () { + + var v1; + + return function applyToBuffer( buffer, offset, length ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + if ( offset === undefined ) offset = 0; + if ( length === undefined ) length = buffer.length / buffer.itemSize; + + for ( var i = 0, j = offset; i < length; i ++, j ++ ) { + + v1.x = buffer.getX( j ); + v1.y = buffer.getY( j ); + v1.z = buffer.getZ( j ); + + v1.applyMatrix3( this ); + + buffer.setXYZ( v1.x, v1.y, v1.z ); + + } + + return buffer; + + }; + + }(), + + multiplyScalar: function ( s ) { + + var te = this.elements; + + te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; + te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; + te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; + + return this; + + }, + + determinant: function () { + + var te = this.elements; + + var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], + d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], + g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; + + return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; + + }, + + getInverse: function ( matrix, throwOnInvertible ) { + + // input: THREE.Matrix4 + // ( based on http://code.google.com/p/webgl-mjs/ ) + + var me = matrix.elements; + var te = this.elements; + + te[ 0 ] = me[ 10 ] * me[ 5 ] - me[ 6 ] * me[ 9 ]; + te[ 1 ] = - me[ 10 ] * me[ 1 ] + me[ 2 ] * me[ 9 ]; + te[ 2 ] = me[ 6 ] * me[ 1 ] - me[ 2 ] * me[ 5 ]; + te[ 3 ] = - me[ 10 ] * me[ 4 ] + me[ 6 ] * me[ 8 ]; + te[ 4 ] = me[ 10 ] * me[ 0 ] - me[ 2 ] * me[ 8 ]; + te[ 5 ] = - me[ 6 ] * me[ 0 ] + me[ 2 ] * me[ 4 ]; + te[ 6 ] = me[ 9 ] * me[ 4 ] - me[ 5 ] * me[ 8 ]; + te[ 7 ] = - me[ 9 ] * me[ 0 ] + me[ 1 ] * me[ 8 ]; + te[ 8 ] = me[ 5 ] * me[ 0 ] - me[ 1 ] * me[ 4 ]; + + var det = me[ 0 ] * te[ 0 ] + me[ 1 ] * te[ 3 ] + me[ 2 ] * te[ 6 ]; + + // no inverse + + if ( det === 0 ) { + + var msg = "Matrix3.getInverse(): can't invert matrix, determinant is 0"; + + if ( throwOnInvertible || false ) { + + throw new Error( msg ); + + } else { + + console.warn( msg ); + + } + + this.identity(); + + return this; + + } + + this.multiplyScalar( 1.0 / det ); + + return this; + + }, + + transpose: function () { + + var tmp, m = this.elements; + + tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; + tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; + tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; + + return this; + + }, + + flattenToArrayOffset: function ( array, offset ) { + + var te = this.elements; + + array[ offset ] = te[ 0 ]; + array[ offset + 1 ] = te[ 1 ]; + array[ offset + 2 ] = te[ 2 ]; + + array[ offset + 3 ] = te[ 3 ]; + array[ offset + 4 ] = te[ 4 ]; + array[ offset + 5 ] = te[ 5 ]; + + array[ offset + 6 ] = te[ 6 ]; + array[ offset + 7 ] = te[ 7 ]; + array[ offset + 8 ] = te[ 8 ]; + + return array; + + }, + + getNormalMatrix: function ( m ) { + + // input: THREE.Matrix4 + + this.getInverse( m ).transpose(); + + return this; + + }, + + transposeIntoArray: function ( r ) { + + var m = this.elements; + + r[ 0 ] = m[ 0 ]; + r[ 1 ] = m[ 3 ]; + r[ 2 ] = m[ 6 ]; + r[ 3 ] = m[ 1 ]; + r[ 4 ] = m[ 4 ]; + r[ 5 ] = m[ 7 ]; + r[ 6 ] = m[ 2 ]; + r[ 7 ] = m[ 5 ]; + r[ 8 ] = m[ 8 ]; + + return this; + + }, + + fromArray: function ( array ) { + + this.elements.set( array ); + + return this; + + }, + + toArray: function () { + + var te = this.elements; + + return [ + te[ 0 ], te[ 1 ], te[ 2 ], + te[ 3 ], te[ 4 ], te[ 5 ], + te[ 6 ], te[ 7 ], te[ 8 ] + ]; + + } + +}; + +// File:src/math/Matrix4.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author philogb / http://blog.thejit.org/ + * @author jordi_ros / http://plattsoft.com + * @author D1plo1d / http://github.com/D1plo1d + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author timknip / http://www.floorplanner.com/ + * @author bhouston / http://clara.io + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Matrix4 = function () { + + this.elements = new Float32Array( [ + + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ] ); + + if ( arguments.length > 0 ) { + + console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); + + } + +}; + +THREE.Matrix4.prototype = { + + constructor: THREE.Matrix4, + + set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { + + var te = this.elements; + + te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; + te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; + te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; + te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; + + return this; + + }, + + identity: function () { + + this.set( + + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + clone: function () { + + return new THREE.Matrix4().fromArray( this.elements ); + + }, + + copy: function ( m ) { + + this.elements.set( m.elements ); + + return this; + + }, + + extractPosition: function ( m ) { + + console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); + return this.copyPosition( m ); + + }, + + copyPosition: function ( m ) { + + var te = this.elements; + var me = m.elements; + + te[ 12 ] = me[ 12 ]; + te[ 13 ] = me[ 13 ]; + te[ 14 ] = me[ 14 ]; + + return this; + + }, + + extractBasis: function ( xAxis, yAxis, zAxis ) { + + var te = this.elements; + + xAxis.set( te[ 0 ], te[ 1 ], te[ 2 ] ); + yAxis.set( te[ 4 ], te[ 5 ], te[ 6 ] ); + zAxis.set( te[ 8 ], te[ 9 ], te[ 10 ] ); + + return this; + + }, + + makeBasis: function ( xAxis, yAxis, zAxis ) { + + this.set( + xAxis.x, yAxis.x, zAxis.x, 0, + xAxis.y, yAxis.y, zAxis.y, 0, + xAxis.z, yAxis.z, zAxis.z, 0, + 0, 0, 0, 1 + ); + + return this; + + }, + + extractRotation: function () { + + var v1; + + return function ( m ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + var te = this.elements; + var me = m.elements; + + var scaleX = 1 / v1.set( me[ 0 ], me[ 1 ], me[ 2 ] ).length(); + var scaleY = 1 / v1.set( me[ 4 ], me[ 5 ], me[ 6 ] ).length(); + var scaleZ = 1 / v1.set( me[ 8 ], me[ 9 ], me[ 10 ] ).length(); + + te[ 0 ] = me[ 0 ] * scaleX; + te[ 1 ] = me[ 1 ] * scaleX; + te[ 2 ] = me[ 2 ] * scaleX; + + te[ 4 ] = me[ 4 ] * scaleY; + te[ 5 ] = me[ 5 ] * scaleY; + te[ 6 ] = me[ 6 ] * scaleY; + + te[ 8 ] = me[ 8 ] * scaleZ; + te[ 9 ] = me[ 9 ] * scaleZ; + te[ 10 ] = me[ 10 ] * scaleZ; + + return this; + + }; + + }(), + + makeRotationFromEuler: function ( euler ) { + + if ( euler instanceof THREE.Euler === false ) { + + console.error( 'THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + + } + + var te = this.elements; + + var x = euler.x, y = euler.y, z = euler.z; + var a = Math.cos( x ), b = Math.sin( x ); + var c = Math.cos( y ), d = Math.sin( y ); + var e = Math.cos( z ), f = Math.sin( z ); + + if ( euler.order === 'XYZ' ) { + + var ae = a * e, af = a * f, be = b * e, bf = b * f; + + te[ 0 ] = c * e; + te[ 4 ] = - c * f; + te[ 8 ] = d; + + te[ 1 ] = af + be * d; + te[ 5 ] = ae - bf * d; + te[ 9 ] = - b * c; + + te[ 2 ] = bf - ae * d; + te[ 6 ] = be + af * d; + te[ 10 ] = a * c; + + } else if ( euler.order === 'YXZ' ) { + + var ce = c * e, cf = c * f, de = d * e, df = d * f; + + te[ 0 ] = ce + df * b; + te[ 4 ] = de * b - cf; + te[ 8 ] = a * d; + + te[ 1 ] = a * f; + te[ 5 ] = a * e; + te[ 9 ] = - b; + + te[ 2 ] = cf * b - de; + te[ 6 ] = df + ce * b; + te[ 10 ] = a * c; + + } else if ( euler.order === 'ZXY' ) { + + var ce = c * e, cf = c * f, de = d * e, df = d * f; + + te[ 0 ] = ce - df * b; + te[ 4 ] = - a * f; + te[ 8 ] = de + cf * b; + + te[ 1 ] = cf + de * b; + te[ 5 ] = a * e; + te[ 9 ] = df - ce * b; + + te[ 2 ] = - a * d; + te[ 6 ] = b; + te[ 10 ] = a * c; + + } else if ( euler.order === 'ZYX' ) { + + var ae = a * e, af = a * f, be = b * e, bf = b * f; + + te[ 0 ] = c * e; + te[ 4 ] = be * d - af; + te[ 8 ] = ae * d + bf; + + te[ 1 ] = c * f; + te[ 5 ] = bf * d + ae; + te[ 9 ] = af * d - be; + + te[ 2 ] = - d; + te[ 6 ] = b * c; + te[ 10 ] = a * c; + + } else if ( euler.order === 'YZX' ) { + + var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + + te[ 0 ] = c * e; + te[ 4 ] = bd - ac * f; + te[ 8 ] = bc * f + ad; + + te[ 1 ] = f; + te[ 5 ] = a * e; + te[ 9 ] = - b * e; + + te[ 2 ] = - d * e; + te[ 6 ] = ad * f + bc; + te[ 10 ] = ac - bd * f; + + } else if ( euler.order === 'XZY' ) { + + var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + + te[ 0 ] = c * e; + te[ 4 ] = - f; + te[ 8 ] = d * e; + + te[ 1 ] = ac * f + bd; + te[ 5 ] = a * e; + te[ 9 ] = ad * f - bc; + + te[ 2 ] = bc * f - ad; + te[ 6 ] = b * e; + te[ 10 ] = bd * f + ac; + + } + + // last column + te[ 3 ] = 0; + te[ 7 ] = 0; + te[ 11 ] = 0; + + // bottom row + te[ 12 ] = 0; + te[ 13 ] = 0; + te[ 14 ] = 0; + te[ 15 ] = 1; + + return this; + + }, + + setRotationFromQuaternion: function ( q ) { + + console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); + + return this.makeRotationFromQuaternion( q ); + + }, + + makeRotationFromQuaternion: function ( q ) { + + var te = this.elements; + + var x = q.x, y = q.y, z = q.z, w = q.w; + var x2 = x + x, y2 = y + y, z2 = z + z; + var xx = x * x2, xy = x * y2, xz = x * z2; + var yy = y * y2, yz = y * z2, zz = z * z2; + var wx = w * x2, wy = w * y2, wz = w * z2; + + te[ 0 ] = 1 - ( yy + zz ); + te[ 4 ] = xy - wz; + te[ 8 ] = xz + wy; + + te[ 1 ] = xy + wz; + te[ 5 ] = 1 - ( xx + zz ); + te[ 9 ] = yz - wx; + + te[ 2 ] = xz - wy; + te[ 6 ] = yz + wx; + te[ 10 ] = 1 - ( xx + yy ); + + // last column + te[ 3 ] = 0; + te[ 7 ] = 0; + te[ 11 ] = 0; + + // bottom row + te[ 12 ] = 0; + te[ 13 ] = 0; + te[ 14 ] = 0; + te[ 15 ] = 1; + + return this; + + }, + + lookAt: function () { + + var x, y, z; + + return function ( eye, target, up ) { + + if ( x === undefined ) x = new THREE.Vector3(); + if ( y === undefined ) y = new THREE.Vector3(); + if ( z === undefined ) z = new THREE.Vector3(); + + var te = this.elements; + + z.subVectors( eye, target ).normalize(); + + if ( z.lengthSq() === 0 ) { + + z.z = 1; + + } + + x.crossVectors( up, z ).normalize(); + + if ( x.lengthSq() === 0 ) { + + z.x += 0.0001; + x.crossVectors( up, z ).normalize(); + + } + + y.crossVectors( z, x ); + + + te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x; + te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y; + te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z; + + return this; + + }; + + }(), + + multiply: function ( m, n ) { + + if ( n !== undefined ) { + + console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); + return this.multiplyMatrices( m, n ); + + } + + return this.multiplyMatrices( this, m ); + + }, + + multiplyMatrices: function ( a, b ) { + + var ae = a.elements; + var be = b.elements; + var te = this.elements; + + var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; + var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; + var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; + var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; + + var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; + var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; + var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; + var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; + + te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; + te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; + te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; + te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; + + te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; + te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; + te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; + te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; + + te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; + te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; + te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; + te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; + + te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; + te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; + te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; + te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; + + return this; + + }, + + multiplyToArray: function ( a, b, r ) { + + var te = this.elements; + + this.multiplyMatrices( a, b ); + + r[ 0 ] = te[ 0 ]; r[ 1 ] = te[ 1 ]; r[ 2 ] = te[ 2 ]; r[ 3 ] = te[ 3 ]; + r[ 4 ] = te[ 4 ]; r[ 5 ] = te[ 5 ]; r[ 6 ] = te[ 6 ]; r[ 7 ] = te[ 7 ]; + r[ 8 ] = te[ 8 ]; r[ 9 ] = te[ 9 ]; r[ 10 ] = te[ 10 ]; r[ 11 ] = te[ 11 ]; + r[ 12 ] = te[ 12 ]; r[ 13 ] = te[ 13 ]; r[ 14 ] = te[ 14 ]; r[ 15 ] = te[ 15 ]; + + return this; + + }, + + multiplyScalar: function ( s ) { + + var te = this.elements; + + te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; + te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; + te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; + te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; + + return this; + + }, + + multiplyVector3: function ( vector ) { + + console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.' ); + return vector.applyProjection( this ); + + }, + + multiplyVector4: function ( vector ) { + + console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); + + }, + + multiplyVector3Array: function ( a ) { + + console.warn( 'THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); + return this.applyToVector3Array( a ); + + }, + + applyToVector3Array: function () { + + var v1; + + return function ( array, offset, length ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + if ( offset === undefined ) offset = 0; + if ( length === undefined ) length = array.length; + + for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) { + + v1.fromArray( array, j ); + v1.applyMatrix4( this ); + v1.toArray( array, j ); + + } + + return array; + + }; + + }(), + + applyToBuffer: function () { + + var v1; + + return function applyToBuffer( buffer, offset, length ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + if ( offset === undefined ) offset = 0; + if ( length === undefined ) length = buffer.length / buffer.itemSize; + + for ( var i = 0, j = offset; i < length; i ++, j ++ ) { + + v1.x = buffer.getX( j ); + v1.y = buffer.getY( j ); + v1.z = buffer.getZ( j ); + + v1.applyMatrix4( this ); + + buffer.setXYZ( v1.x, v1.y, v1.z ); + + } + + return buffer; + + }; + + }(), + + rotateAxis: function ( v ) { + + console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); + + v.transformDirection( this ); + + }, + + crossVector: function ( vector ) { + + console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); + + }, + + determinant: function () { + + var te = this.elements; + + var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; + var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; + var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; + var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; + + //TODO: make this more efficient + //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) + + return ( + n41 * ( + + n14 * n23 * n32 + - n13 * n24 * n32 + - n14 * n22 * n33 + + n12 * n24 * n33 + + n13 * n22 * n34 + - n12 * n23 * n34 + ) + + n42 * ( + + n11 * n23 * n34 + - n11 * n24 * n33 + + n14 * n21 * n33 + - n13 * n21 * n34 + + n13 * n24 * n31 + - n14 * n23 * n31 + ) + + n43 * ( + + n11 * n24 * n32 + - n11 * n22 * n34 + - n14 * n21 * n32 + + n12 * n21 * n34 + + n14 * n22 * n31 + - n12 * n24 * n31 + ) + + n44 * ( + - n13 * n22 * n31 + - n11 * n23 * n32 + + n11 * n22 * n33 + + n13 * n21 * n32 + - n12 * n21 * n33 + + n12 * n23 * n31 + ) + + ); + + }, + + transpose: function () { + + var te = this.elements; + var tmp; + + tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; + tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; + tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; + + tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; + tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; + tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; + + return this; + + }, + + flattenToArrayOffset: function ( array, offset ) { + + var te = this.elements; + + array[ offset ] = te[ 0 ]; + array[ offset + 1 ] = te[ 1 ]; + array[ offset + 2 ] = te[ 2 ]; + array[ offset + 3 ] = te[ 3 ]; + + array[ offset + 4 ] = te[ 4 ]; + array[ offset + 5 ] = te[ 5 ]; + array[ offset + 6 ] = te[ 6 ]; + array[ offset + 7 ] = te[ 7 ]; + + array[ offset + 8 ] = te[ 8 ]; + array[ offset + 9 ] = te[ 9 ]; + array[ offset + 10 ] = te[ 10 ]; + array[ offset + 11 ] = te[ 11 ]; + + array[ offset + 12 ] = te[ 12 ]; + array[ offset + 13 ] = te[ 13 ]; + array[ offset + 14 ] = te[ 14 ]; + array[ offset + 15 ] = te[ 15 ]; + + return array; + + }, + + getPosition: function () { + + var v1; + + return function () { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); + + var te = this.elements; + return v1.set( te[ 12 ], te[ 13 ], te[ 14 ] ); + + }; + + }(), + + setPosition: function ( v ) { + + var te = this.elements; + + te[ 12 ] = v.x; + te[ 13 ] = v.y; + te[ 14 ] = v.z; + + return this; + + }, + + getInverse: function ( m, throwOnInvertible ) { + + // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm + var te = this.elements; + var me = m.elements; + + var n11 = me[ 0 ], n12 = me[ 4 ], n13 = me[ 8 ], n14 = me[ 12 ]; + var n21 = me[ 1 ], n22 = me[ 5 ], n23 = me[ 9 ], n24 = me[ 13 ]; + var n31 = me[ 2 ], n32 = me[ 6 ], n33 = me[ 10 ], n34 = me[ 14 ]; + var n41 = me[ 3 ], n42 = me[ 7 ], n43 = me[ 11 ], n44 = me[ 15 ]; + + te[ 0 ] = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44; + te[ 4 ] = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44; + te[ 8 ] = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44; + te[ 12 ] = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; + te[ 1 ] = n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44; + te[ 5 ] = n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44; + te[ 9 ] = n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44; + te[ 13 ] = n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34; + te[ 2 ] = n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44; + te[ 6 ] = n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44; + te[ 10 ] = n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44; + te[ 14 ] = n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34; + te[ 3 ] = n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43; + te[ 7 ] = n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43; + te[ 11 ] = n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43; + te[ 15 ] = n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33; + + var det = n11 * te[ 0 ] + n21 * te[ 4 ] + n31 * te[ 8 ] + n41 * te[ 12 ]; + + if ( det === 0 ) { + + var msg = "THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0"; + + if ( throwOnInvertible || false ) { + + throw new Error( msg ); + + } else { + + console.warn( msg ); + + } + + this.identity(); + + return this; + + } + + this.multiplyScalar( 1 / det ); + + return this; + + }, + + translate: function ( v ) { + + console.error( 'THREE.Matrix4: .translate() has been removed.' ); + + }, + + rotateX: function ( angle ) { + + console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); + + }, + + rotateY: function ( angle ) { + + console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); + + }, + + rotateZ: function ( angle ) { + + console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); + + }, + + rotateByAxis: function ( axis, angle ) { + + console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); + + }, + + scale: function ( v ) { + + var te = this.elements; + var x = v.x, y = v.y, z = v.z; + + te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; + te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; + te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; + te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; + + return this; + + }, + + getMaxScaleOnAxis: function () { + + var te = this.elements; + + var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; + var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; + var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; + + return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); + + }, + + makeTranslation: function ( x, y, z ) { + + this.set( + + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationX: function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + 1, 0, 0, 0, + 0, c, - s, 0, + 0, s, c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationY: function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + c, 0, s, 0, + 0, 1, 0, 0, + - s, 0, c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationZ: function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + c, - s, 0, 0, + s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationAxis: function ( axis, angle ) { + + // Based on http://www.gamedev.net/reference/articles/article1199.asp + + var c = Math.cos( angle ); + var s = Math.sin( angle ); + var t = 1 - c; + var x = axis.x, y = axis.y, z = axis.z; + var tx = t * x, ty = t * y; + + this.set( + + tx * x + c, tx * y - s * z, tx * z + s * y, 0, + tx * y + s * z, ty * y + c, ty * z - s * x, 0, + tx * z - s * y, ty * z + s * x, t * z * z + c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeScale: function ( x, y, z ) { + + this.set( + + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + compose: function ( position, quaternion, scale ) { + + this.makeRotationFromQuaternion( quaternion ); + this.scale( scale ); + this.setPosition( position ); + + return this; + + }, + + decompose: function () { + + var vector, matrix; + + return function ( position, quaternion, scale ) { + + if ( vector === undefined ) vector = new THREE.Vector3(); + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + + var te = this.elements; + + var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); + var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); + var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); + + // if determine is negative, we need to invert one scale + var det = this.determinant(); + if ( det < 0 ) { + + sx = - sx; + + } + + position.x = te[ 12 ]; + position.y = te[ 13 ]; + position.z = te[ 14 ]; + + // scale the rotation part + + matrix.elements.set( this.elements ); // at this point matrix is incomplete so we can't use .copy() + + var invSX = 1 / sx; + var invSY = 1 / sy; + var invSZ = 1 / sz; + + matrix.elements[ 0 ] *= invSX; + matrix.elements[ 1 ] *= invSX; + matrix.elements[ 2 ] *= invSX; + + matrix.elements[ 4 ] *= invSY; + matrix.elements[ 5 ] *= invSY; + matrix.elements[ 6 ] *= invSY; + + matrix.elements[ 8 ] *= invSZ; + matrix.elements[ 9 ] *= invSZ; + matrix.elements[ 10 ] *= invSZ; + + quaternion.setFromRotationMatrix( matrix ); + + scale.x = sx; + scale.y = sy; + scale.z = sz; + + return this; + + }; + + }(), + + makeFrustum: function ( left, right, bottom, top, near, far ) { + + var te = this.elements; + var x = 2 * near / ( right - left ); + var y = 2 * near / ( top - bottom ); + + var a = ( right + left ) / ( right - left ); + var b = ( top + bottom ) / ( top - bottom ); + var c = - ( far + near ) / ( far - near ); + var d = - 2 * far * near / ( far - near ); + + te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; + te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; + te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; + te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; + + return this; + + }, + + makePerspective: function ( fov, aspect, near, far ) { + + var ymax = near * Math.tan( THREE.Math.degToRad( fov * 0.5 ) ); + var ymin = - ymax; + var xmin = ymin * aspect; + var xmax = ymax * aspect; + + return this.makeFrustum( xmin, xmax, ymin, ymax, near, far ); + + }, + + makeOrthographic: function ( left, right, top, bottom, near, far ) { + + var te = this.elements; + var w = right - left; + var h = top - bottom; + var p = far - near; + + var x = ( right + left ) / w; + var y = ( top + bottom ) / h; + var z = ( far + near ) / p; + + te[ 0 ] = 2 / w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; + te[ 1 ] = 0; te[ 5 ] = 2 / h; te[ 9 ] = 0; te[ 13 ] = - y; + te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 / p; te[ 14 ] = - z; + te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; + + return this; + + }, + + equals: function ( matrix ) { + + var te = this.elements; + var me = matrix.elements; + + for ( var i = 0; i < 16; i ++ ) { + + if ( te[ i ] !== me[ i ] ) return false; + + } + + return true; + + }, + + fromArray: function ( array ) { + + this.elements.set( array ); + + return this; + + }, + + toArray: function () { + + var te = this.elements; + + return [ + te[ 0 ], te[ 1 ], te[ 2 ], te[ 3 ], + te[ 4 ], te[ 5 ], te[ 6 ], te[ 7 ], + te[ 8 ], te[ 9 ], te[ 10 ], te[ 11 ], + te[ 12 ], te[ 13 ], te[ 14 ], te[ 15 ] + ]; + + } + +}; + +// File:src/math/Ray.js + +/** + * @author bhouston / http://clara.io + */ + +THREE.Ray = function ( origin, direction ) { + + this.origin = ( origin !== undefined ) ? origin : new THREE.Vector3(); + this.direction = ( direction !== undefined ) ? direction : new THREE.Vector3(); + +}; + +THREE.Ray.prototype = { + + constructor: THREE.Ray, + + set: function ( origin, direction ) { + + this.origin.copy( origin ); + this.direction.copy( direction ); + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( ray ) { + + this.origin.copy( ray.origin ); + this.direction.copy( ray.direction ); + + return this; + + }, + + at: function ( t, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + return result.copy( this.direction ).multiplyScalar( t ).add( this.origin ); + + }, + + recast: function () { + + var v1 = new THREE.Vector3(); + + return function ( t ) { + + this.origin.copy( this.at( t, v1 ) ); + + return this; + + }; + + }(), + + closestPointToPoint: function ( point, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + result.subVectors( point, this.origin ); + var directionDistance = result.dot( this.direction ); + + if ( directionDistance < 0 ) { + + return result.copy( this.origin ); + + } + + return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + + }, + + distanceToPoint: function ( point ) { + + return Math.sqrt( this.distanceSqToPoint( point ) ); + + }, + + distanceSqToPoint: function () { + + var v1 = new THREE.Vector3(); + + return function ( point ) { + + var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction ); + + // point behind the ray + + if ( directionDistance < 0 ) { + + return this.origin.distanceToSquared( point ); + + } + + v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + + return v1.distanceToSquared( point ); + + }; + + }(), + + distanceSqToSegment: function () { + + var segCenter = new THREE.Vector3(); + var segDir = new THREE.Vector3(); + var diff = new THREE.Vector3(); + + return function ( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { + + // from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp + // It returns the min distance between the ray and the segment + // defined by v0 and v1 + // It can also set two optional targets : + // - The closest point on the ray + // - The closest point on the segment + + segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); + segDir.copy( v1 ).sub( v0 ).normalize(); + diff.copy( this.origin ).sub( segCenter ); + + var segExtent = v0.distanceTo( v1 ) * 0.5; + var a01 = - this.direction.dot( segDir ); + var b0 = diff.dot( this.direction ); + var b1 = - diff.dot( segDir ); + var c = diff.lengthSq(); + var det = Math.abs( 1 - a01 * a01 ); + var s0, s1, sqrDist, extDet; + + if ( det > 0 ) { + + // The ray and segment are not parallel. + + s0 = a01 * b1 - b0; + s1 = a01 * b0 - b1; + extDet = segExtent * det; + + if ( s0 >= 0 ) { + + if ( s1 >= - extDet ) { + + if ( s1 <= extDet ) { + + // region 0 + // Minimum at interior points of ray and segment. + + var invDet = 1 / det; + s0 *= invDet; + s1 *= invDet; + sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; + + } else { + + // region 1 + + s1 = segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + } else { + + // region 5 + + s1 = - segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + } else { + + if ( s1 <= - extDet ) { + + // region 4 + + s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } else if ( s1 <= extDet ) { + + // region 3 + + s0 = 0; + s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = s1 * ( s1 + 2 * b1 ) + c; + + } else { + + // region 2 + + s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + } + + } else { + + // Ray and segment are parallel. + + s1 = ( a01 > 0 ) ? - segExtent : segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + if ( optionalPointOnRay ) { + + optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); + + } + + if ( optionalPointOnSegment ) { + + optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter ); + + } + + return sqrDist; + + }; + + }(), + + + isIntersectionSphere: function ( sphere ) { + + return this.distanceToPoint( sphere.center ) <= sphere.radius; + + }, + + intersectSphere: function () { + + // from http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-sphere-intersection/ + + var v1 = new THREE.Vector3(); + + return function ( sphere, optionalTarget ) { + + v1.subVectors( sphere.center, this.origin ); + + var tca = v1.dot( this.direction ); + + var d2 = v1.dot( v1 ) - tca * tca; + + var radius2 = sphere.radius * sphere.radius; + + if ( d2 > radius2 ) return null; + + var thc = Math.sqrt( radius2 - d2 ); + + // t0 = first intersect point - entrance on front of sphere + var t0 = tca - thc; + + // t1 = second intersect point - exit point on back of sphere + var t1 = tca + thc; + + // test to see if both t0 and t1 are behind the ray - if so, return null + if ( t0 < 0 && t1 < 0 ) return null; + + // test to see if t0 is behind the ray: + // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, + // in order to always return an intersect point that is in front of the ray. + if ( t0 < 0 ) return this.at( t1, optionalTarget ); + + // else t0 is in front of the ray, so return the first collision point scaled by t0 + return this.at( t0, optionalTarget ); + + } + + }(), + + isIntersectionPlane: function ( plane ) { + + // check if the ray lies on the plane first + + var distToPoint = plane.distanceToPoint( this.origin ); + + if ( distToPoint === 0 ) { + + return true; + + } + + var denominator = plane.normal.dot( this.direction ); + + if ( denominator * distToPoint < 0 ) { + + return true; + + } + + // ray origin is behind the plane (and is pointing behind it) + + return false; + + }, + + distanceToPlane: function ( plane ) { + + var denominator = plane.normal.dot( this.direction ); + if ( denominator === 0 ) { + + // line is coplanar, return origin + if ( plane.distanceToPoint( this.origin ) === 0 ) { + + return 0; + + } + + // Null is preferable to undefined since undefined means.... it is undefined + + return null; + + } + + var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; + + // Return if the ray never intersects the plane + + return t >= 0 ? t : null; + + }, + + intersectPlane: function ( plane, optionalTarget ) { + + var t = this.distanceToPlane( plane ); + + if ( t === null ) { + + return null; + + } + + return this.at( t, optionalTarget ); + + }, + + isIntersectionBox: function () { + + var v = new THREE.Vector3(); + + return function ( box ) { + + return this.intersectBox( box, v ) !== null; + + }; + + }(), + + intersectBox: function ( box, optionalTarget ) { + + // http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/ + + var tmin, tmax, tymin, tymax, tzmin, tzmax; + + var invdirx = 1 / this.direction.x, + invdiry = 1 / this.direction.y, + invdirz = 1 / this.direction.z; + + var origin = this.origin; + + if ( invdirx >= 0 ) { + + tmin = ( box.min.x - origin.x ) * invdirx; + tmax = ( box.max.x - origin.x ) * invdirx; + + } else { + + tmin = ( box.max.x - origin.x ) * invdirx; + tmax = ( box.min.x - origin.x ) * invdirx; + + } + + if ( invdiry >= 0 ) { + + tymin = ( box.min.y - origin.y ) * invdiry; + tymax = ( box.max.y - origin.y ) * invdiry; + + } else { + + tymin = ( box.max.y - origin.y ) * invdiry; + tymax = ( box.min.y - origin.y ) * invdiry; + + } + + if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; + + // These lines also handle the case where tmin or tmax is NaN + // (result of 0 * Infinity). x !== x returns true if x is NaN + + if ( tymin > tmin || tmin !== tmin ) tmin = tymin; + + if ( tymax < tmax || tmax !== tmax ) tmax = tymax; + + if ( invdirz >= 0 ) { + + tzmin = ( box.min.z - origin.z ) * invdirz; + tzmax = ( box.max.z - origin.z ) * invdirz; + + } else { + + tzmin = ( box.max.z - origin.z ) * invdirz; + tzmax = ( box.min.z - origin.z ) * invdirz; + + } + + if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; + + if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; + + if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; + + //return point closest to the ray (positive side) + + if ( tmax < 0 ) return null; + + return this.at( tmin >= 0 ? tmin : tmax, optionalTarget ); + + }, + + intersectTriangle: function () { + + // Compute the offset origin, edges, and normal. + var diff = new THREE.Vector3(); + var edge1 = new THREE.Vector3(); + var edge2 = new THREE.Vector3(); + var normal = new THREE.Vector3(); + + return function ( a, b, c, backfaceCulling, optionalTarget ) { + + // from http://www.geometrictools.com/LibMathematics/Intersection/Wm5IntrRay3Triangle3.cpp + + edge1.subVectors( b, a ); + edge2.subVectors( c, a ); + normal.crossVectors( edge1, edge2 ); + + // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, + // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by + // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) + // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) + // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) + var DdN = this.direction.dot( normal ); + var sign; + + if ( DdN > 0 ) { + + if ( backfaceCulling ) return null; + sign = 1; + + } else if ( DdN < 0 ) { + + sign = - 1; + DdN = - DdN; + + } else { + + return null; + + } + + diff.subVectors( this.origin, a ); + var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) ); + + // b1 < 0, no intersection + if ( DdQxE2 < 0 ) { + + return null; + + } + + var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) ); + + // b2 < 0, no intersection + if ( DdE1xQ < 0 ) { + + return null; + + } + + // b1+b2 > 1, no intersection + if ( DdQxE2 + DdE1xQ > DdN ) { + + return null; + + } + + // Line intersects triangle, check if ray does. + var QdN = - sign * diff.dot( normal ); + + // t < 0, no intersection + if ( QdN < 0 ) { + + return null; + + } + + // Ray intersects triangle. + return this.at( QdN / DdN, optionalTarget ); + + }; + + }(), + + applyMatrix4: function ( matrix4 ) { + + this.direction.add( this.origin ).applyMatrix4( matrix4 ); + this.origin.applyMatrix4( matrix4 ); + this.direction.sub( this.origin ); + this.direction.normalize(); + + return this; + + }, + + equals: function ( ray ) { + + return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); + + } + +}; + +// File:src/math/Sphere.js + +/** + * @author bhouston / http://clara.io + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Sphere = function ( center, radius ) { + + this.center = ( center !== undefined ) ? center : new THREE.Vector3(); + this.radius = ( radius !== undefined ) ? radius : 0; + +}; + +THREE.Sphere.prototype = { + + constructor: THREE.Sphere, + + set: function ( center, radius ) { + + this.center.copy( center ); + this.radius = radius; + + return this; + + }, + + setFromPoints: function () { + + var box = new THREE.Box3(); + + return function ( points, optionalCenter ) { + + var center = this.center; + + if ( optionalCenter !== undefined ) { + + center.copy( optionalCenter ); + + } else { + + box.setFromPoints( points ).center( center ); + + } + + var maxRadiusSq = 0; + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); + + } + + this.radius = Math.sqrt( maxRadiusSq ); + + return this; + + }; + + }(), + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( sphere ) { + + this.center.copy( sphere.center ); + this.radius = sphere.radius; + + return this; + + }, + + empty: function () { + + return ( this.radius <= 0 ); + + }, + + containsPoint: function ( point ) { + + return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); + + }, + + distanceToPoint: function ( point ) { + + return ( point.distanceTo( this.center ) - this.radius ); + + }, + + intersectsSphere: function ( sphere ) { + + var radiusSum = this.radius + sphere.radius; + + return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); + + }, + + clampPoint: function ( point, optionalTarget ) { + + var deltaLengthSq = this.center.distanceToSquared( point ); + + var result = optionalTarget || new THREE.Vector3(); + result.copy( point ); + + if ( deltaLengthSq > ( this.radius * this.radius ) ) { + + result.sub( this.center ).normalize(); + result.multiplyScalar( this.radius ).add( this.center ); + + } + + return result; + + }, + + getBoundingBox: function ( optionalTarget ) { + + var box = optionalTarget || new THREE.Box3(); + + box.set( this.center, this.center ); + box.expandByScalar( this.radius ); + + return box; + + }, + + applyMatrix4: function ( matrix ) { + + this.center.applyMatrix4( matrix ); + this.radius = this.radius * matrix.getMaxScaleOnAxis(); + + return this; + + }, + + translate: function ( offset ) { + + this.center.add( offset ); + + return this; + + }, + + equals: function ( sphere ) { + + return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); + + } + +}; + +// File:src/math/Frustum.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author bhouston / http://clara.io + */ + +THREE.Frustum = function ( p0, p1, p2, p3, p4, p5 ) { + + this.planes = [ + + ( p0 !== undefined ) ? p0 : new THREE.Plane(), + ( p1 !== undefined ) ? p1 : new THREE.Plane(), + ( p2 !== undefined ) ? p2 : new THREE.Plane(), + ( p3 !== undefined ) ? p3 : new THREE.Plane(), + ( p4 !== undefined ) ? p4 : new THREE.Plane(), + ( p5 !== undefined ) ? p5 : new THREE.Plane() + + ]; + +}; + +THREE.Frustum.prototype = { + + constructor: THREE.Frustum, + + set: function ( p0, p1, p2, p3, p4, p5 ) { + + var planes = this.planes; + + planes[ 0 ].copy( p0 ); + planes[ 1 ].copy( p1 ); + planes[ 2 ].copy( p2 ); + planes[ 3 ].copy( p3 ); + planes[ 4 ].copy( p4 ); + planes[ 5 ].copy( p5 ); + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( frustum ) { + + var planes = this.planes; + + for ( var i = 0; i < 6; i ++ ) { + + planes[ i ].copy( frustum.planes[ i ] ); + + } + + return this; + + }, + + setFromMatrix: function ( m ) { + + var planes = this.planes; + var me = m.elements; + var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; + var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; + var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; + var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; + + planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); + planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); + planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); + planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); + planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); + planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); + + return this; + + }, + + intersectsObject: function () { + + var sphere = new THREE.Sphere(); + + return function ( object ) { + + var geometry = object.geometry; + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( object.matrixWorld ); + + return this.intersectsSphere( sphere ); + + }; + + }(), + + intersectsSphere: function ( sphere ) { + + var planes = this.planes; + var center = sphere.center; + var negRadius = - sphere.radius; + + for ( var i = 0; i < 6; i ++ ) { + + var distance = planes[ i ].distanceToPoint( center ); + + if ( distance < negRadius ) { + + return false; + + } + + } + + return true; + + }, + + intersectsBox: function () { + + var p1 = new THREE.Vector3(), + p2 = new THREE.Vector3(); + + return function ( box ) { + + var planes = this.planes; + + for ( var i = 0; i < 6 ; i ++ ) { + + var plane = planes[ i ]; + + p1.x = plane.normal.x > 0 ? box.min.x : box.max.x; + p2.x = plane.normal.x > 0 ? box.max.x : box.min.x; + p1.y = plane.normal.y > 0 ? box.min.y : box.max.y; + p2.y = plane.normal.y > 0 ? box.max.y : box.min.y; + p1.z = plane.normal.z > 0 ? box.min.z : box.max.z; + p2.z = plane.normal.z > 0 ? box.max.z : box.min.z; + + var d1 = plane.distanceToPoint( p1 ); + var d2 = plane.distanceToPoint( p2 ); + + // if both outside plane, no intersection + + if ( d1 < 0 && d2 < 0 ) { + + return false; + + } + + } + + return true; + + }; + + }(), + + + containsPoint: function ( point ) { + + var planes = this.planes; + + for ( var i = 0; i < 6; i ++ ) { + + if ( planes[ i ].distanceToPoint( point ) < 0 ) { + + return false; + + } + + } + + return true; + + } + +}; + +// File:src/math/Plane.js + +/** + * @author bhouston / http://clara.io + */ + +THREE.Plane = function ( normal, constant ) { + + this.normal = ( normal !== undefined ) ? normal : new THREE.Vector3( 1, 0, 0 ); + this.constant = ( constant !== undefined ) ? constant : 0; + +}; + +THREE.Plane.prototype = { + + constructor: THREE.Plane, + + set: function ( normal, constant ) { + + this.normal.copy( normal ); + this.constant = constant; + + return this; + + }, + + setComponents: function ( x, y, z, w ) { + + this.normal.set( x, y, z ); + this.constant = w; + + return this; + + }, + + setFromNormalAndCoplanarPoint: function ( normal, point ) { + + this.normal.copy( normal ); + this.constant = - point.dot( this.normal ); // must be this.normal, not normal, as this.normal is normalized + + return this; + + }, + + setFromCoplanarPoints: function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + + return function ( a, b, c ) { + + var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize(); + + // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? + + this.setFromNormalAndCoplanarPoint( normal, a ); + + return this; + + }; + + }(), + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( plane ) { + + this.normal.copy( plane.normal ); + this.constant = plane.constant; + + return this; + + }, + + normalize: function () { + + // Note: will lead to a divide by zero if the plane is invalid. + + var inverseNormalLength = 1.0 / this.normal.length(); + this.normal.multiplyScalar( inverseNormalLength ); + this.constant *= inverseNormalLength; + + return this; + + }, + + negate: function () { + + this.constant *= - 1; + this.normal.negate(); + + return this; + + }, + + distanceToPoint: function ( point ) { + + return this.normal.dot( point ) + this.constant; + + }, + + distanceToSphere: function ( sphere ) { + + return this.distanceToPoint( sphere.center ) - sphere.radius; + + }, + + projectPoint: function ( point, optionalTarget ) { + + return this.orthoPoint( point, optionalTarget ).sub( point ).negate(); + + }, + + orthoPoint: function ( point, optionalTarget ) { + + var perpendicularMagnitude = this.distanceToPoint( point ); + + var result = optionalTarget || new THREE.Vector3(); + return result.copy( this.normal ).multiplyScalar( perpendicularMagnitude ); + + }, + + isIntersectionLine: function ( line ) { + + // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. + + var startSign = this.distanceToPoint( line.start ); + var endSign = this.distanceToPoint( line.end ); + + return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); + + }, + + intersectLine: function () { + + var v1 = new THREE.Vector3(); + + return function ( line, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + var direction = line.delta( v1 ); + + var denominator = this.normal.dot( direction ); + + if ( denominator === 0 ) { + + // line is coplanar, return origin + if ( this.distanceToPoint( line.start ) === 0 ) { + + return result.copy( line.start ); + + } + + // Unsure if this is the correct method to handle this case. + return undefined; + + } + + var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; + + if ( t < 0 || t > 1 ) { + + return undefined; + + } + + return result.copy( direction ).multiplyScalar( t ).add( line.start ); + + }; + + }(), + + + coplanarPoint: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.copy( this.normal ).multiplyScalar( - this.constant ); + + }, + + applyMatrix4: function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var m1 = new THREE.Matrix3(); + + return function ( matrix, optionalNormalMatrix ) { + + // compute new normal based on theory here: + // http://www.songho.ca/opengl/gl_normaltransform.html + var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix ); + var newNormal = v1.copy( this.normal ).applyMatrix3( normalMatrix ); + + var newCoplanarPoint = this.coplanarPoint( v2 ); + newCoplanarPoint.applyMatrix4( matrix ); + + this.setFromNormalAndCoplanarPoint( newNormal, newCoplanarPoint ); + + return this; + + }; + + }(), + + translate: function ( offset ) { + + this.constant = this.constant - offset.dot( this.normal ); + + return this; + + }, + + equals: function ( plane ) { + + return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); + + } + +}; + +// File:src/math/Math.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Math = { + + generateUUID: function () { + + // http://www.broofa.com/Tools/Math.uuid.htm + + var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' ); + var uuid = new Array( 36 ); + var rnd = 0, r; + + return function () { + + for ( var i = 0; i < 36; i ++ ) { + + if ( i === 8 || i === 13 || i === 18 || i === 23 ) { + + uuid[ i ] = '-'; + + } else if ( i === 14 ) { + + uuid[ i ] = '4'; + + } else { + + if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0; + r = rnd & 0xf; + rnd = rnd >> 4; + uuid[ i ] = chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ]; + + } + + } + + return uuid.join( '' ); + + }; + + }(), + + clamp: function ( value, min, max ) { + + return Math.max( min, Math.min( max, value ) ); + + }, + + // compute euclidian modulo of m % n + // https://en.wikipedia.org/wiki/Modulo_operation + + euclideanModulo: function ( n, m ) { + + return ( ( n % m ) + m ) % m; + + }, + + // Linear mapping from range to range + + mapLinear: function ( x, a1, a2, b1, b2 ) { + + return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); + + }, + + // http://en.wikipedia.org/wiki/Smoothstep + + smoothstep: function ( x, min, max ) { + + if ( x <= min ) return 0; + if ( x >= max ) return 1; + + x = ( x - min ) / ( max - min ); + + return x * x * ( 3 - 2 * x ); + + }, + + smootherstep: function ( x, min, max ) { + + if ( x <= min ) return 0; + if ( x >= max ) return 1; + + x = ( x - min ) / ( max - min ); + + return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); + + }, + + // Random float from <0, 1> with 16 bits of randomness + // (standard Math.random() creates repetitive patterns when applied over larger space) + + random16: function () { + + return ( 65280 * Math.random() + 255 * Math.random() ) / 65535; + + }, + + // Random integer from interval + + randInt: function ( low, high ) { + + return low + Math.floor( Math.random() * ( high - low + 1 ) ); + + }, + + // Random float from interval + + randFloat: function ( low, high ) { + + return low + Math.random() * ( high - low ); + + }, + + // Random float from <-range/2, range/2> interval + + randFloatSpread: function ( range ) { + + return range * ( 0.5 - Math.random() ); + + }, + + degToRad: function () { + + var degreeToRadiansFactor = Math.PI / 180; + + return function ( degrees ) { + + return degrees * degreeToRadiansFactor; + + }; + + }(), + + radToDeg: function () { + + var radianToDegreesFactor = 180 / Math.PI; + + return function ( radians ) { + + return radians * radianToDegreesFactor; + + }; + + }(), + + isPowerOfTwo: function ( value ) { + + return ( value & ( value - 1 ) ) === 0 && value !== 0; + + }, + + nearestPowerOfTwo: function ( value ) { + + return Math.pow( 2, Math.round( Math.log( value ) / Math.LN2 ) ); + + }, + + nextPowerOfTwo: function ( value ) { + + value --; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value ++; + + return value; + + } + +}; + +// File:src/math/Spline.js + +/** + * Spline from Tween.js, slightly optimized (and trashed) + * http://sole.github.com/tween.js/examples/05_spline.html + * + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Spline = function ( points ) { + + this.points = points; + + var c = [], v3 = { x: 0, y: 0, z: 0 }, + point, intPoint, weight, w2, w3, + pa, pb, pc, pd; + + this.initFromArray = function ( a ) { + + this.points = []; + + for ( var i = 0; i < a.length; i ++ ) { + + this.points[ i ] = { x: a[ i ][ 0 ], y: a[ i ][ 1 ], z: a[ i ][ 2 ] }; + + } + + }; + + this.getPoint = function ( k ) { + + point = ( this.points.length - 1 ) * k; + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > this.points.length - 2 ? this.points.length - 1 : intPoint + 1; + c[ 3 ] = intPoint > this.points.length - 3 ? this.points.length - 1 : intPoint + 2; + + pa = this.points[ c[ 0 ] ]; + pb = this.points[ c[ 1 ] ]; + pc = this.points[ c[ 2 ] ]; + pd = this.points[ c[ 3 ] ]; + + w2 = weight * weight; + w3 = weight * w2; + + v3.x = interpolate( pa.x, pb.x, pc.x, pd.x, weight, w2, w3 ); + v3.y = interpolate( pa.y, pb.y, pc.y, pd.y, weight, w2, w3 ); + v3.z = interpolate( pa.z, pb.z, pc.z, pd.z, weight, w2, w3 ); + + return v3; + + }; + + this.getControlPointsArray = function () { + + var i, p, l = this.points.length, + coords = []; + + for ( i = 0; i < l; i ++ ) { + + p = this.points[ i ]; + coords[ i ] = [ p.x, p.y, p.z ]; + + } + + return coords; + + }; + + // approximate length by summing linear segments + + this.getLength = function ( nSubDivisions ) { + + var i, index, nSamples, position, + point = 0, intPoint = 0, oldIntPoint = 0, + oldPosition = new THREE.Vector3(), + tmpVec = new THREE.Vector3(), + chunkLengths = [], + totalLength = 0; + + // first point has 0 length + + chunkLengths[ 0 ] = 0; + + if ( ! nSubDivisions ) nSubDivisions = 100; + + nSamples = this.points.length * nSubDivisions; + + oldPosition.copy( this.points[ 0 ] ); + + for ( i = 1; i < nSamples; i ++ ) { + + index = i / nSamples; + + position = this.getPoint( index ); + tmpVec.copy( position ); + + totalLength += tmpVec.distanceTo( oldPosition ); + + oldPosition.copy( position ); + + point = ( this.points.length - 1 ) * index; + intPoint = Math.floor( point ); + + if ( intPoint !== oldIntPoint ) { + + chunkLengths[ intPoint ] = totalLength; + oldIntPoint = intPoint; + + } + + } + + // last point ends with total length + + chunkLengths[ chunkLengths.length ] = totalLength; + + return { chunks: chunkLengths, total: totalLength }; + + }; + + this.reparametrizeByArcLength = function ( samplingCoef ) { + + var i, j, + index, indexCurrent, indexNext, + realDistance, + sampling, position, + newpoints = [], + tmpVec = new THREE.Vector3(), + sl = this.getLength(); + + newpoints.push( tmpVec.copy( this.points[ 0 ] ).clone() ); + + for ( i = 1; i < this.points.length; i ++ ) { + + //tmpVec.copy( this.points[ i - 1 ] ); + //linearDistance = tmpVec.distanceTo( this.points[ i ] ); + + realDistance = sl.chunks[ i ] - sl.chunks[ i - 1 ]; + + sampling = Math.ceil( samplingCoef * realDistance / sl.total ); + + indexCurrent = ( i - 1 ) / ( this.points.length - 1 ); + indexNext = i / ( this.points.length - 1 ); + + for ( j = 1; j < sampling - 1; j ++ ) { + + index = indexCurrent + j * ( 1 / sampling ) * ( indexNext - indexCurrent ); + + position = this.getPoint( index ); + newpoints.push( tmpVec.copy( position ).clone() ); + + } + + newpoints.push( tmpVec.copy( this.points[ i ] ).clone() ); + + } + + this.points = newpoints; + + }; + + // Catmull-Rom + + function interpolate( p0, p1, p2, p3, t, t2, t3 ) { + + var v0 = ( p2 - p0 ) * 0.5, + v1 = ( p3 - p1 ) * 0.5; + + return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + } + +}; + +// File:src/math/Triangle.js + +/** + * @author bhouston / http://clara.io + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Triangle = function ( a, b, c ) { + + this.a = ( a !== undefined ) ? a : new THREE.Vector3(); + this.b = ( b !== undefined ) ? b : new THREE.Vector3(); + this.c = ( c !== undefined ) ? c : new THREE.Vector3(); + +}; + +THREE.Triangle.normal = function () { + + var v0 = new THREE.Vector3(); + + return function ( a, b, c, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + result.subVectors( c, b ); + v0.subVectors( a, b ); + result.cross( v0 ); + + var resultLengthSq = result.lengthSq(); + if ( resultLengthSq > 0 ) { + + return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) ); + + } + + return result.set( 0, 0, 0 ); + + }; + +}(); + +// static/instance method to calculate barycentric coordinates +// based on: http://www.blackpawn.com/texts/pointinpoly/default.html +THREE.Triangle.barycoordFromPoint = function () { + + var v0 = new THREE.Vector3(); + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + + return function ( point, a, b, c, optionalTarget ) { + + v0.subVectors( c, a ); + v1.subVectors( b, a ); + v2.subVectors( point, a ); + + var dot00 = v0.dot( v0 ); + var dot01 = v0.dot( v1 ); + var dot02 = v0.dot( v2 ); + var dot11 = v1.dot( v1 ); + var dot12 = v1.dot( v2 ); + + var denom = ( dot00 * dot11 - dot01 * dot01 ); + + var result = optionalTarget || new THREE.Vector3(); + + // collinear or singular triangle + if ( denom === 0 ) { + + // arbitrary location outside of triangle? + // not sure if this is the best idea, maybe should be returning undefined + return result.set( - 2, - 1, - 1 ); + + } + + var invDenom = 1 / denom; + var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; + var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; + + // barycentric coordinates must always sum to 1 + return result.set( 1 - u - v, v, u ); + + }; + +}(); + +THREE.Triangle.containsPoint = function () { + + var v1 = new THREE.Vector3(); + + return function ( point, a, b, c ) { + + var result = THREE.Triangle.barycoordFromPoint( point, a, b, c, v1 ); + + return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 ); + + }; + +}(); + +THREE.Triangle.prototype = { + + constructor: THREE.Triangle, + + set: function ( a, b, c ) { + + this.a.copy( a ); + this.b.copy( b ); + this.c.copy( c ); + + return this; + + }, + + setFromPointsAndIndices: function ( points, i0, i1, i2 ) { + + this.a.copy( points[ i0 ] ); + this.b.copy( points[ i1 ] ); + this.c.copy( points[ i2 ] ); + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( triangle ) { + + this.a.copy( triangle.a ); + this.b.copy( triangle.b ); + this.c.copy( triangle.c ); + + return this; + + }, + + area: function () { + + var v0 = new THREE.Vector3(); + var v1 = new THREE.Vector3(); + + return function () { + + v0.subVectors( this.c, this.b ); + v1.subVectors( this.a, this.b ); + + return v0.cross( v1 ).length() * 0.5; + + }; + + }(), + + midpoint: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); + + }, + + normal: function ( optionalTarget ) { + + return THREE.Triangle.normal( this.a, this.b, this.c, optionalTarget ); + + }, + + plane: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Plane(); + + return result.setFromCoplanarPoints( this.a, this.b, this.c ); + + }, + + barycoordFromPoint: function ( point, optionalTarget ) { + + return THREE.Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget ); + + }, + + containsPoint: function ( point ) { + + return THREE.Triangle.containsPoint( point, this.a, this.b, this.c ); + + }, + + equals: function ( triangle ) { + + return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); + + } + +}; + +// File:src/core/Channels.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Channels = function () { + + this.mask = 1; + +}; + +THREE.Channels.prototype = { + + constructor: THREE.Channels, + + set: function ( channel ) { + + this.mask = 1 << channel; + + }, + + enable: function ( channel ) { + + this.mask |= 1 << channel; + + }, + + toggle: function ( channel ) { + + this.mask ^= 1 << channel; + + }, + + disable: function ( channel ) { + + this.mask &= ~ ( 1 << channel ); + + } + +}; + +// File:src/core/Clock.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Clock = function ( autoStart ) { + + this.autoStart = ( autoStart !== undefined ) ? autoStart : true; + + this.startTime = 0; + this.oldTime = 0; + this.elapsedTime = 0; + + this.running = false; + +}; + +THREE.Clock.prototype = { + + constructor: THREE.Clock, + + start: function () { + + this.startTime = self.performance.now(); + + this.oldTime = this.startTime; + this.running = true; + + }, + + stop: function () { + + this.getElapsedTime(); + this.running = false; + + }, + + getElapsedTime: function () { + + this.getDelta(); + return this.elapsedTime; + + }, + + getDelta: function () { + + var diff = 0; + + if ( this.autoStart && ! this.running ) { + + this.start(); + + } + + if ( this.running ) { + + var newTime = self.performance.now(); + + diff = 0.001 * ( newTime - this.oldTime ); + this.oldTime = newTime; + + this.elapsedTime += diff; + + } + + return diff; + + } + +}; + +// File:src/core/EventDispatcher.js + +/** + * https://github.com/mrdoob/eventdispatcher.js/ + */ + +THREE.EventDispatcher = function () {}; + +THREE.EventDispatcher.prototype = { + + constructor: THREE.EventDispatcher, + + apply: function ( object ) { + + object.addEventListener = THREE.EventDispatcher.prototype.addEventListener; + object.hasEventListener = THREE.EventDispatcher.prototype.hasEventListener; + object.removeEventListener = THREE.EventDispatcher.prototype.removeEventListener; + object.dispatchEvent = THREE.EventDispatcher.prototype.dispatchEvent; + + }, + + addEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) this._listeners = {}; + + var listeners = this._listeners; + + if ( listeners[ type ] === undefined ) { + + listeners[ type ] = []; + + } + + if ( listeners[ type ].indexOf( listener ) === - 1 ) { + + listeners[ type ].push( listener ); + + } + + }, + + hasEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) return false; + + var listeners = this._listeners; + + if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) { + + return true; + + } + + return false; + + }, + + removeEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) return; + + var listeners = this._listeners; + var listenerArray = listeners[ type ]; + + if ( listenerArray !== undefined ) { + + var index = listenerArray.indexOf( listener ); + + if ( index !== - 1 ) { + + listenerArray.splice( index, 1 ); + + } + + } + + }, + + dispatchEvent: function ( event ) { + + if ( this._listeners === undefined ) return; + + var listeners = this._listeners; + var listenerArray = listeners[ event.type ]; + + if ( listenerArray !== undefined ) { + + event.target = this; + + var array = []; + var length = listenerArray.length; + + for ( var i = 0; i < length; i ++ ) { + + array[ i ] = listenerArray[ i ]; + + } + + for ( var i = 0; i < length; i ++ ) { + + array[ i ].call( this, event ); + + } + + } + + } + +}; + +// File:src/core/Raycaster.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author bhouston / http://clara.io/ + * @author stephomi / http://stephaneginier.com/ + */ + +( function ( THREE ) { + + THREE.Raycaster = function ( origin, direction, near, far ) { + + this.ray = new THREE.Ray( origin, direction ); + // direction is assumed to be normalized (for accurate distance calculations) + + this.near = near || 0; + this.far = far || Infinity; + + this.params = { + Mesh: {}, + Line: {}, + LOD: {}, + Points: { threshold: 1 }, + Sprite: {} + }; + + Object.defineProperties( this.params, { + PointCloud: { + get: function () { + console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' ); + return this.Points; + } + } + } ); + + }; + + function descSort( a, b ) { + + return a.distance - b.distance; + + } + + function intersectObject( object, raycaster, intersects, recursive ) { + + if ( object.visible === false ) return; + + object.raycast( raycaster, intersects ); + + if ( recursive === true ) { + + var children = object.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + intersectObject( children[ i ], raycaster, intersects, true ); + + } + + } + + } + + // + + THREE.Raycaster.prototype = { + + constructor: THREE.Raycaster, + + linePrecision: 1, + + set: function ( origin, direction ) { + + // direction is assumed to be normalized (for accurate distance calculations) + + this.ray.set( origin, direction ); + + }, + + setFromCamera: function ( coords, camera ) { + + if ( camera instanceof THREE.PerspectiveCamera ) { + + this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); + this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); + + } else if ( camera instanceof THREE.OrthographicCamera ) { + + this.ray.origin.set( coords.x, coords.y, - 1 ).unproject( camera ); + this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); + + } else { + + console.error( 'THREE.Raycaster: Unsupported camera type.' ); + + } + + }, + + intersectObject: function ( object, recursive ) { + + var intersects = []; + + intersectObject( object, this, intersects, recursive ); + + intersects.sort( descSort ); + + return intersects; + + }, + + intersectObjects: function ( objects, recursive ) { + + var intersects = []; + + if ( Array.isArray( objects ) === false ) { + + console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' ); + return intersects; + + } + + for ( var i = 0, l = objects.length; i < l; i ++ ) { + + intersectObject( objects[ i ], this, intersects, recursive ); + + } + + intersects.sort( descSort ); + + return intersects; + + } + + }; + +}( THREE ) ); + +// File:src/core/Object3D.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author elephantatwork / www.elephantatwork.ch + */ + +THREE.Object3D = function () { + + Object.defineProperty( this, 'id', { value: THREE.Object3DIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.type = 'Object3D'; + + this.parent = null; + this.channels = new THREE.Channels(); + this.children = []; + + this.up = THREE.Object3D.DefaultUp.clone(); + + var position = new THREE.Vector3(); + var rotation = new THREE.Euler(); + var quaternion = new THREE.Quaternion(); + var scale = new THREE.Vector3( 1, 1, 1 ); + + function onRotationChange() { + + quaternion.setFromEuler( rotation, false ); + + } + + function onQuaternionChange() { + + rotation.setFromQuaternion( quaternion, undefined, false ); + + } + + rotation.onChange( onRotationChange ); + quaternion.onChange( onQuaternionChange ); + + Object.defineProperties( this, { + position: { + enumerable: true, + value: position + }, + rotation: { + enumerable: true, + value: rotation + }, + quaternion: { + enumerable: true, + value: quaternion + }, + scale: { + enumerable: true, + value: scale + }, + modelViewMatrix: { + value: new THREE.Matrix4() + }, + normalMatrix: { + value: new THREE.Matrix3() + } + } ); + + this.rotationAutoUpdate = true; + + this.matrix = new THREE.Matrix4(); + this.matrixWorld = new THREE.Matrix4(); + + this.matrixAutoUpdate = THREE.Object3D.DefaultMatrixAutoUpdate; + this.matrixWorldNeedsUpdate = false; + + this.visible = true; + + this.castShadow = false; + this.receiveShadow = false; + + this.frustumCulled = true; + this.renderOrder = 0; + + this.userData = {}; + +}; + +THREE.Object3D.DefaultUp = new THREE.Vector3( 0, 1, 0 ); +THREE.Object3D.DefaultMatrixAutoUpdate = true; + +THREE.Object3D.prototype = { + + constructor: THREE.Object3D, + + get eulerOrder () { + + console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); + + return this.rotation.order; + + }, + + set eulerOrder ( value ) { + + console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); + + this.rotation.order = value; + + }, + + get useQuaternion () { + + console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + + }, + + set useQuaternion ( value ) { + + console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + + }, + + set renderDepth ( value ) { + + console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); + + }, + + // + + applyMatrix: function ( matrix ) { + + this.matrix.multiplyMatrices( matrix, this.matrix ); + + this.matrix.decompose( this.position, this.quaternion, this.scale ); + + }, + + setRotationFromAxisAngle: function ( axis, angle ) { + + // assumes axis is normalized + + this.quaternion.setFromAxisAngle( axis, angle ); + + }, + + setRotationFromEuler: function ( euler ) { + + this.quaternion.setFromEuler( euler, true ); + + }, + + setRotationFromMatrix: function ( m ) { + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + this.quaternion.setFromRotationMatrix( m ); + + }, + + setRotationFromQuaternion: function ( q ) { + + // assumes q is normalized + + this.quaternion.copy( q ); + + }, + + rotateOnAxis: function () { + + // rotate object on axis in object space + // axis is assumed to be normalized + + var q1 = new THREE.Quaternion(); + + return function ( axis, angle ) { + + q1.setFromAxisAngle( axis, angle ); + + this.quaternion.multiply( q1 ); + + return this; + + }; + + }(), + + rotateX: function () { + + var v1 = new THREE.Vector3( 1, 0, 0 ); + + return function ( angle ) { + + return this.rotateOnAxis( v1, angle ); + + }; + + }(), + + rotateY: function () { + + var v1 = new THREE.Vector3( 0, 1, 0 ); + + return function ( angle ) { + + return this.rotateOnAxis( v1, angle ); + + }; + + }(), + + rotateZ: function () { + + var v1 = new THREE.Vector3( 0, 0, 1 ); + + return function ( angle ) { + + return this.rotateOnAxis( v1, angle ); + + }; + + }(), + + translateOnAxis: function () { + + // translate object by distance along axis in object space + // axis is assumed to be normalized + + var v1 = new THREE.Vector3(); + + return function ( axis, distance ) { + + v1.copy( axis ).applyQuaternion( this.quaternion ); + + this.position.add( v1.multiplyScalar( distance ) ); + + return this; + + }; + + }(), + + translate: function ( distance, axis ) { + + console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); + return this.translateOnAxis( axis, distance ); + + }, + + translateX: function () { + + var v1 = new THREE.Vector3( 1, 0, 0 ); + + return function ( distance ) { + + return this.translateOnAxis( v1, distance ); + + }; + + }(), + + translateY: function () { + + var v1 = new THREE.Vector3( 0, 1, 0 ); + + return function ( distance ) { + + return this.translateOnAxis( v1, distance ); + + }; + + }(), + + translateZ: function () { + + var v1 = new THREE.Vector3( 0, 0, 1 ); + + return function ( distance ) { + + return this.translateOnAxis( v1, distance ); + + }; + + }(), + + localToWorld: function ( vector ) { + + return vector.applyMatrix4( this.matrixWorld ); + + }, + + worldToLocal: function () { + + var m1 = new THREE.Matrix4(); + + return function ( vector ) { + + return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) ); + + }; + + }(), + + lookAt: function () { + + // This routine does not support objects with rotated and/or translated parent(s) + + var m1 = new THREE.Matrix4(); + + return function ( vector ) { + + m1.lookAt( vector, this.position, this.up ); + + this.quaternion.setFromRotationMatrix( m1 ); + + }; + + }(), + + add: function ( object ) { + + if ( arguments.length > 1 ) { + + for ( var i = 0; i < arguments.length; i ++ ) { + + this.add( arguments[ i ] ); + + } + + return this; + + } + + if ( object === this ) { + + console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object ); + return this; + + } + + if ( object instanceof THREE.Object3D ) { + + if ( object.parent !== null ) { + + object.parent.remove( object ); + + } + + object.parent = this; + object.dispatchEvent( { type: 'added' } ); + + this.children.push( object ); + + } else { + + console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); + + } + + return this; + + }, + + remove: function ( object ) { + + if ( arguments.length > 1 ) { + + for ( var i = 0; i < arguments.length; i ++ ) { + + this.remove( arguments[ i ] ); + + } + + } + + var index = this.children.indexOf( object ); + + if ( index !== - 1 ) { + + object.parent = null; + + object.dispatchEvent( { type: 'removed' } ); + + this.children.splice( index, 1 ); + + } + + }, + + getChildByName: function ( name ) { + + console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); + return this.getObjectByName( name ); + + }, + + getObjectById: function ( id ) { + + return this.getObjectByProperty( 'id', id ); + + }, + + getObjectByName: function ( name ) { + + return this.getObjectByProperty( 'name', name ); + + }, + + getObjectByProperty: function ( name, value ) { + + if ( this[ name ] === value ) return this; + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + var child = this.children[ i ]; + var object = child.getObjectByProperty( name, value ); + + if ( object !== undefined ) { + + return object; + + } + + } + + return undefined; + + }, + + getWorldPosition: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + this.updateMatrixWorld( true ); + + return result.setFromMatrixPosition( this.matrixWorld ); + + }, + + getWorldQuaternion: function () { + + var position = new THREE.Vector3(); + var scale = new THREE.Vector3(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Quaternion(); + + this.updateMatrixWorld( true ); + + this.matrixWorld.decompose( position, result, scale ); + + return result; + + }; + + }(), + + getWorldRotation: function () { + + var quaternion = new THREE.Quaternion(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Euler(); + + this.getWorldQuaternion( quaternion ); + + return result.setFromQuaternion( quaternion, this.rotation.order, false ); + + }; + + }(), + + getWorldScale: function () { + + var position = new THREE.Vector3(); + var quaternion = new THREE.Quaternion(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + this.updateMatrixWorld( true ); + + this.matrixWorld.decompose( position, quaternion, result ); + + return result; + + }; + + }(), + + getWorldDirection: function () { + + var quaternion = new THREE.Quaternion(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + this.getWorldQuaternion( quaternion ); + + return result.set( 0, 0, 1 ).applyQuaternion( quaternion ); + + }; + + }(), + + raycast: function () {}, + + traverse: function ( callback ) { + + callback( this ); + + var children = this.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + children[ i ].traverse( callback ); + + } + + }, + + traverseVisible: function ( callback ) { + + if ( this.visible === false ) return; + + callback( this ); + + var children = this.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + children[ i ].traverseVisible( callback ); + + } + + }, + + traverseAncestors: function ( callback ) { + + var parent = this.parent; + + if ( parent !== null ) { + + callback( parent ); + + parent.traverseAncestors( callback ); + + } + + }, + + updateMatrix: function () { + + this.matrix.compose( this.position, this.quaternion, this.scale ); + + this.matrixWorldNeedsUpdate = true; + + }, + + updateMatrixWorld: function ( force ) { + + if ( this.matrixAutoUpdate === true ) this.updateMatrix(); + + if ( this.matrixWorldNeedsUpdate === true || force === true ) { + + if ( this.parent === null ) { + + this.matrixWorld.copy( this.matrix ); + + } else { + + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + + } + + this.matrixWorldNeedsUpdate = false; + + force = true; + + } + + // update children + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].updateMatrixWorld( force ); + + } + + }, + + toJSON: function ( meta ) { + + var isRootObject = ( meta === undefined ); + + var output = {}; + + // meta is a hash used to collect geometries, materials. + // not providing it implies that this is the root object + // being serialized. + if ( isRootObject ) { + + // initialize meta obj + meta = { + geometries: {}, + materials: {}, + textures: {}, + images: {} + }; + + output.metadata = { + version: 4.4, + type: 'Object', + generator: 'Object3D.toJSON' + }; + + } + + // standard Object3D serialization + + var object = {}; + + object.uuid = this.uuid; + object.type = this.type; + + if ( this.name !== '' ) object.name = this.name; + if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData; + if ( this.castShadow === true ) object.castShadow = true; + if ( this.receiveShadow === true ) object.receiveShadow = true; + if ( this.visible === false ) object.visible = false; + + object.matrix = this.matrix.toArray(); + + // + + if ( this.geometry !== undefined ) { + + if ( meta.geometries[ this.geometry.uuid ] === undefined ) { + + meta.geometries[ this.geometry.uuid ] = this.geometry.toJSON( meta ); + + } + + object.geometry = this.geometry.uuid; + + } + + if ( this.material !== undefined ) { + + if ( meta.materials[ this.material.uuid ] === undefined ) { + + meta.materials[ this.material.uuid ] = this.material.toJSON( meta ); + + } + + object.material = this.material.uuid; + + } + + // + + if ( this.children.length > 0 ) { + + object.children = []; + + for ( var i = 0; i < this.children.length; i ++ ) { + + object.children.push( this.children[ i ].toJSON( meta ).object ); + + } + + } + + if ( isRootObject ) { + + var geometries = extractFromCache( meta.geometries ); + var materials = extractFromCache( meta.materials ); + var textures = extractFromCache( meta.textures ); + var images = extractFromCache( meta.images ); + + if ( geometries.length > 0 ) output.geometries = geometries; + if ( materials.length > 0 ) output.materials = materials; + if ( textures.length > 0 ) output.textures = textures; + if ( images.length > 0 ) output.images = images; + + } + + output.object = object; + + return output; + + // extract data from the cache hash + // remove metadata on each item + // and return as array + function extractFromCache ( cache ) { + + var values = []; + for ( var key in cache ) { + + var data = cache[ key ]; + delete data.metadata; + values.push( data ); + + } + return values; + + } + + }, + + clone: function ( recursive ) { + + return new this.constructor().copy( this, recursive ); + + }, + + copy: function ( source, recursive ) { + + if ( recursive === undefined ) recursive = true; + + this.name = source.name; + + this.up.copy( source.up ); + + this.position.copy( source.position ); + this.quaternion.copy( source.quaternion ); + this.scale.copy( source.scale ); + + this.rotationAutoUpdate = source.rotationAutoUpdate; + + this.matrix.copy( source.matrix ); + this.matrixWorld.copy( source.matrixWorld ); + + this.matrixAutoUpdate = source.matrixAutoUpdate; + this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; + + this.visible = source.visible; + + this.castShadow = source.castShadow; + this.receiveShadow = source.receiveShadow; + + this.frustumCulled = source.frustumCulled; + this.renderOrder = source.renderOrder; + + this.userData = JSON.parse( JSON.stringify( source.userData ) ); + + if ( recursive === true ) { + + for ( var i = 0; i < source.children.length; i ++ ) { + + var child = source.children[ i ]; + this.add( child.clone() ); + + } + + } + + return this; + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Object3D.prototype ); + +THREE.Object3DIdCount = 0; + +// File:src/core/Face3.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Face3 = function ( a, b, c, normal, color, materialIndex ) { + + this.a = a; + this.b = b; + this.c = c; + + this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3(); + this.vertexNormals = Array.isArray( normal ) ? normal : []; + + this.color = color instanceof THREE.Color ? color : new THREE.Color(); + this.vertexColors = Array.isArray( color ) ? color : []; + + this.materialIndex = materialIndex !== undefined ? materialIndex : 0; + +}; + +THREE.Face3.prototype = { + + constructor: THREE.Face3, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( source ) { + + this.a = source.a; + this.b = source.b; + this.c = source.c; + + this.normal.copy( source.normal ); + this.color.copy( source.color ); + + this.materialIndex = source.materialIndex; + + for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) { + + this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); + + } + + for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) { + + this.vertexColors[ i ] = source.vertexColors[ i ].clone(); + + } + + return this; + + } + +}; + +// File:src/core/Face4.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) { + + console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' ); + return new THREE.Face3( a, b, c, normal, color, materialIndex ); + +}; + +// File:src/core/BufferAttribute.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BufferAttribute = function ( array, itemSize ) { + + this.uuid = THREE.Math.generateUUID(); + + this.array = array; + this.itemSize = itemSize; + + this.dynamic = false; + this.updateRange = { offset: 0, count: - 1 }; + + this.version = 0; + +}; + +THREE.BufferAttribute.prototype = { + + constructor: THREE.BufferAttribute, + + get length() { + + console.warn( 'THREE.BufferAttribute: .length has been deprecated. Please use .count.' ); + return this.array.length; + + }, + + get count() { + + return this.array.length / this.itemSize; + + }, + + set needsUpdate( value ) { + + if ( value === true ) this.version ++; + + }, + + setDynamic: function ( value ) { + + this.dynamic = value; + + return this; + + }, + + copy: function ( source ) { + + this.array = new source.array.constructor( source.array ); + this.itemSize = source.itemSize; + + this.dynamic = source.dynamic; + + return this; + + }, + + copyAt: function ( index1, attribute, index2 ) { + + index1 *= this.itemSize; + index2 *= attribute.itemSize; + + for ( var i = 0, l = this.itemSize; i < l; i ++ ) { + + this.array[ index1 + i ] = attribute.array[ index2 + i ]; + + } + + return this; + + }, + + copyArray: function ( array ) { + + this.array.set( array ); + + return this; + + }, + + copyColorsArray: function ( colors ) { + + var array = this.array, offset = 0; + + for ( var i = 0, l = colors.length; i < l; i ++ ) { + + var color = colors[ i ]; + + if ( color === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); + color = new THREE.Color(); + + } + + array[ offset ++ ] = color.r; + array[ offset ++ ] = color.g; + array[ offset ++ ] = color.b; + + } + + return this; + + }, + + copyIndicesArray: function ( indices ) { + + var array = this.array, offset = 0; + + for ( var i = 0, l = indices.length; i < l; i ++ ) { + + var index = indices[ i ]; + + array[ offset ++ ] = index.a; + array[ offset ++ ] = index.b; + array[ offset ++ ] = index.c; + + } + + return this; + + }, + + copyVector2sArray: function ( vectors ) { + + var array = this.array, offset = 0; + + for ( var i = 0, l = vectors.length; i < l; i ++ ) { + + var vector = vectors[ i ]; + + if ( vector === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); + vector = new THREE.Vector2(); + + } + + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; + + } + + return this; + + }, + + copyVector3sArray: function ( vectors ) { + + var array = this.array, offset = 0; + + for ( var i = 0, l = vectors.length; i < l; i ++ ) { + + var vector = vectors[ i ]; + + if ( vector === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); + vector = new THREE.Vector3(); + + } + + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; + array[ offset ++ ] = vector.z; + + } + + return this; + + }, + + copyVector4sArray: function ( vectors ) { + + var array = this.array, offset = 0; + + for ( var i = 0, l = vectors.length; i < l; i ++ ) { + + var vector = vectors[ i ]; + + if ( vector === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); + vector = new THREE.Vector4(); + + } + + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; + array[ offset ++ ] = vector.z; + array[ offset ++ ] = vector.w; + + } + + return this; + + }, + + set: function ( value, offset ) { + + if ( offset === undefined ) offset = 0; + + this.array.set( value, offset ); + + return this; + + }, + + getX: function ( index ) { + + return this.array[ index * this.itemSize ]; + + }, + + setX: function ( index, x ) { + + this.array[ index * this.itemSize ] = x; + + return this; + + }, + + getY: function ( index ) { + + return this.array[ index * this.itemSize + 1 ]; + + }, + + setY: function ( index, y ) { + + this.array[ index * this.itemSize + 1 ] = y; + + return this; + + }, + + getZ: function ( index ) { + + return this.array[ index * this.itemSize + 2 ]; + + }, + + setZ: function ( index, z ) { + + this.array[ index * this.itemSize + 2 ] = z; + + return this; + + }, + + getW: function ( index ) { + + return this.array[ index * this.itemSize + 3 ]; + + }, + + setW: function ( index, w ) { + + this.array[ index * this.itemSize + 3 ] = w; + + return this; + + }, + + setXY: function ( index, x, y ) { + + index *= this.itemSize; + + this.array[ index + 0 ] = x; + this.array[ index + 1 ] = y; + + return this; + + }, + + setXYZ: function ( index, x, y, z ) { + + index *= this.itemSize; + + this.array[ index + 0 ] = x; + this.array[ index + 1 ] = y; + this.array[ index + 2 ] = z; + + return this; + + }, + + setXYZW: function ( index, x, y, z, w ) { + + index *= this.itemSize; + + this.array[ index + 0 ] = x; + this.array[ index + 1 ] = y; + this.array[ index + 2 ] = z; + this.array[ index + 3 ] = w; + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + } + +}; + +// + +THREE.Int8Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Int8Array( array ), itemSize ); + +}; + +THREE.Uint8Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Uint8Array( array ), itemSize ); + +}; + +THREE.Uint8ClampedAttribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Uint8ClampedArray( array ), itemSize ); + +}; + +THREE.Int16Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Int16Array( array ), itemSize ); + +}; + +THREE.Uint16Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Uint16Array( array ), itemSize ); + +}; + +THREE.Int32Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Int32Array( array ), itemSize ); + +}; + +THREE.Uint32Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Uint32Array( array ), itemSize ); + +}; + +THREE.Float32Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Float32Array( array ), itemSize ); + +}; + +THREE.Float64Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Float64Array( array ), itemSize ); + +}; + + +// Deprecated + +THREE.DynamicBufferAttribute = function ( array, itemSize ) { + + console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' ); + return new THREE.BufferAttribute( array, itemSize ).setDynamic( true ); + +}; + +// File:src/core/InstancedBufferAttribute.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.InstancedBufferAttribute = function ( array, itemSize, meshPerAttribute ) { + + THREE.BufferAttribute.call( this, array, itemSize ); + + this.meshPerAttribute = meshPerAttribute || 1; + +}; + +THREE.InstancedBufferAttribute.prototype = Object.create( THREE.BufferAttribute.prototype ); +THREE.InstancedBufferAttribute.prototype.constructor = THREE.InstancedBufferAttribute; + +THREE.InstancedBufferAttribute.prototype.copy = function ( source ) { + + THREE.BufferAttribute.prototype.copy.call( this, source ); + + this.meshPerAttribute = source.meshPerAttribute; + + return this; + +}; + +// File:src/core/InterleavedBuffer.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.InterleavedBuffer = function ( array, stride ) { + + this.uuid = THREE.Math.generateUUID(); + + this.array = array; + this.stride = stride; + + this.dynamic = false; + this.updateRange = { offset: 0, count: - 1 }; + + this.version = 0; + +}; + +THREE.InterleavedBuffer.prototype = { + + constructor: THREE.InterleavedBuffer, + + get length () { + + return this.array.length; + + }, + + get count () { + + return this.array.length / this.stride; + + }, + + set needsUpdate( value ) { + + if ( value === true ) this.version ++; + + }, + + setDynamic: function ( value ) { + + this.dynamic = value; + + return this; + + }, + + copy: function ( source ) { + + this.array = new source.array.constructor( source.array ); + this.stride = source.stride; + this.dynamic = source.dynamic; + + }, + + copyAt: function ( index1, attribute, index2 ) { + + index1 *= this.stride; + index2 *= attribute.stride; + + for ( var i = 0, l = this.stride; i < l; i ++ ) { + + this.array[ index1 + i ] = attribute.array[ index2 + i ]; + + } + + return this; + + }, + + set: function ( value, offset ) { + + if ( offset === undefined ) offset = 0; + + this.array.set( value, offset ); + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + } + +}; + +// File:src/core/InstancedInterleavedBuffer.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.InstancedInterleavedBuffer = function ( array, stride, meshPerAttribute ) { + + THREE.InterleavedBuffer.call( this, array, stride ); + + this.meshPerAttribute = meshPerAttribute || 1; + +}; + +THREE.InstancedInterleavedBuffer.prototype = Object.create( THREE.InterleavedBuffer.prototype ); +THREE.InstancedInterleavedBuffer.prototype.constructor = THREE.InstancedInterleavedBuffer; + +THREE.InstancedInterleavedBuffer.prototype.copy = function ( source ) { + + THREE.InterleavedBuffer.prototype.copy.call( this, source ); + + this.meshPerAttribute = source.meshPerAttribute; + + return this; + +}; + +// File:src/core/InterleavedBufferAttribute.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.InterleavedBufferAttribute = function ( interleavedBuffer, itemSize, offset ) { + + this.uuid = THREE.Math.generateUUID(); + + this.data = interleavedBuffer; + this.itemSize = itemSize; + this.offset = offset; + +}; + + +THREE.InterleavedBufferAttribute.prototype = { + + constructor: THREE.InterleavedBufferAttribute, + + get length() { + + console.warn( 'THREE.BufferAttribute: .length has been deprecated. Please use .count.' ); + return this.array.length; + + }, + + get count() { + + return this.data.array.length / this.data.stride; + + }, + + setX: function ( index, x ) { + + this.data.array[ index * this.data.stride + this.offset ] = x; + + return this; + + }, + + setY: function ( index, y ) { + + this.data.array[ index * this.data.stride + this.offset + 1 ] = y; + + return this; + + }, + + setZ: function ( index, z ) { + + this.data.array[ index * this.data.stride + this.offset + 2 ] = z; + + return this; + + }, + + setW: function ( index, w ) { + + this.data.array[ index * this.data.stride + this.offset + 3 ] = w; + + return this; + + }, + + getX: function ( index ) { + + return this.data.array[ index * this.data.stride + this.offset ]; + + }, + + getY: function ( index ) { + + return this.data.array[ index * this.data.stride + this.offset + 1 ]; + + }, + + getZ: function ( index ) { + + return this.data.array[ index * this.data.stride + this.offset + 2 ]; + + }, + + getW: function ( index ) { + + return this.data.array[ index * this.data.stride + this.offset + 3 ]; + + }, + + setXY: function ( index, x, y ) { + + index = index * this.data.stride + this.offset; + + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + + return this; + + }, + + setXYZ: function ( index, x, y, z ) { + + index = index * this.data.stride + this.offset; + + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + this.data.array[ index + 2 ] = z; + + return this; + + }, + + setXYZW: function ( index, x, y, z, w ) { + + index = index * this.data.stride + this.offset; + + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + this.data.array[ index + 2 ] = z; + this.data.array[ index + 3 ] = w; + + return this; + + } + +}; + +// File:src/core/Geometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author kile / http://kile.stravaganza.org/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author bhouston / http://clara.io + */ + +THREE.Geometry = function () { + + Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.type = 'Geometry'; + + this.vertices = []; + this.colors = []; + this.faces = []; + this.faceVertexUvs = [ [] ]; + + this.morphTargets = []; + this.morphNormals = []; + + this.skinWeights = []; + this.skinIndices = []; + + this.lineDistances = []; + + this.boundingBox = null; + this.boundingSphere = null; + + // update flags + + this.verticesNeedUpdate = false; + this.elementsNeedUpdate = false; + this.uvsNeedUpdate = false; + this.normalsNeedUpdate = false; + this.colorsNeedUpdate = false; + this.lineDistancesNeedUpdate = false; + this.groupsNeedUpdate = false; + +}; + +THREE.Geometry.prototype = { + + constructor: THREE.Geometry, + + applyMatrix: function ( matrix ) { + + var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + + for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { + + var vertex = this.vertices[ i ]; + vertex.applyMatrix4( matrix ); + + } + + for ( var i = 0, il = this.faces.length; i < il; i ++ ) { + + var face = this.faces[ i ]; + face.normal.applyMatrix3( normalMatrix ).normalize(); + + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + + face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); + + } + + } + + if ( this.boundingBox !== null ) { + + this.computeBoundingBox(); + + } + + if ( this.boundingSphere !== null ) { + + this.computeBoundingSphere(); + + } + + this.verticesNeedUpdate = true; + this.normalsNeedUpdate = true; + + }, + + rotateX: function () { + + // rotate geometry around world x-axis + + var m1; + + return function rotateX( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationX( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + rotateY: function () { + + // rotate geometry around world y-axis + + var m1; + + return function rotateY( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationY( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + rotateZ: function () { + + // rotate geometry around world z-axis + + var m1; + + return function rotateZ( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationZ( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + translate: function () { + + // translate geometry + + var m1; + + return function translate( x, y, z ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeTranslation( x, y, z ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + scale: function () { + + // scale geometry + + var m1; + + return function scale( x, y, z ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeScale( x, y, z ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + lookAt: function () { + + var obj; + + return function lookAt( vector ) { + + if ( obj === undefined ) obj = new THREE.Object3D(); + + obj.lookAt( vector ); + + obj.updateMatrix(); + + this.applyMatrix( obj.matrix ); + + }; + + }(), + + fromBufferGeometry: function ( geometry ) { + + var scope = this; + + var indices = geometry.index !== null ? geometry.index.array : undefined; + var attributes = geometry.attributes; + + var vertices = attributes.position.array; + var normals = attributes.normal !== undefined ? attributes.normal.array : undefined; + var colors = attributes.color !== undefined ? attributes.color.array : undefined; + var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined; + var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined; + + if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = []; + + var tempNormals = []; + var tempUVs = []; + var tempUVs2 = []; + + for ( var i = 0, j = 0, k = 0; i < vertices.length; i += 3, j += 2, k += 4 ) { + + scope.vertices.push( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) ); + + if ( normals !== undefined ) { + + tempNormals.push( new THREE.Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) ); + + } + + if ( colors !== undefined ) { + + scope.colors.push( new THREE.Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) ); + + } + + if ( uvs !== undefined ) { + + tempUVs.push( new THREE.Vector2( uvs[ j ], uvs[ j + 1 ] ) ); + + } + + if ( uvs2 !== undefined ) { + + tempUVs2.push( new THREE.Vector2( uvs2[ j ], uvs2[ j + 1 ] ) ); + + } + + } + + function addFace( a, b, c ) { + + var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : []; + var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : []; + + var face = new THREE.Face3( a, b, c, vertexNormals, vertexColors ); + + scope.faces.push( face ); + + if ( uvs !== undefined ) { + + scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] ); + + } + + if ( uvs2 !== undefined ) { + + scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] ); + + } + + }; + + if ( indices !== undefined ) { + + var groups = geometry.groups; + + if ( groups.length > 0 ) { + + for ( var i = 0; i < groups.length; i ++ ) { + + var group = groups[ i ]; + + var start = group.start; + var count = group.count; + + for ( var j = start, jl = start + count; j < jl; j += 3 ) { + + addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ] ); + + } + + } + + } else { + + for ( var i = 0; i < indices.length; i += 3 ) { + + addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); + + } + + } + + } else { + + for ( var i = 0; i < vertices.length / 3; i += 3 ) { + + addFace( i, i + 1, i + 2 ); + + } + + } + + this.computeFaceNormals(); + + if ( geometry.boundingBox !== null ) { + + this.boundingBox = geometry.boundingBox.clone(); + + } + + if ( geometry.boundingSphere !== null ) { + + this.boundingSphere = geometry.boundingSphere.clone(); + + } + + return this; + + }, + + center: function () { + + this.computeBoundingBox(); + + var offset = this.boundingBox.center().negate(); + + this.translate( offset.x, offset.y, offset.z ); + + return offset; + + }, + + normalize: function () { + + this.computeBoundingSphere(); + + var center = this.boundingSphere.center; + var radius = this.boundingSphere.radius; + + var s = radius === 0 ? 1 : 1.0 / radius; + + var matrix = new THREE.Matrix4(); + matrix.set( + s, 0, 0, - s * center.x, + 0, s, 0, - s * center.y, + 0, 0, s, - s * center.z, + 0, 0, 0, 1 + ); + + this.applyMatrix( matrix ); + + return this; + + }, + + computeFaceNormals: function () { + + var cb = new THREE.Vector3(), ab = new THREE.Vector3(); + + for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { + + var face = this.faces[ f ]; + + var vA = this.vertices[ face.a ]; + var vB = this.vertices[ face.b ]; + var vC = this.vertices[ face.c ]; + + cb.subVectors( vC, vB ); + ab.subVectors( vA, vB ); + cb.cross( ab ); + + cb.normalize(); + + face.normal.copy( cb ); + + } + + }, + + computeVertexNormals: function ( areaWeighted ) { + + var v, vl, f, fl, face, vertices; + + vertices = new Array( this.vertices.length ); + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + vertices[ v ] = new THREE.Vector3(); + + } + + if ( areaWeighted ) { + + // vertex normals weighted by triangle areas + // http://www.iquilezles.org/www/articles/normals/normals.htm + + var vA, vB, vC; + var cb = new THREE.Vector3(), ab = new THREE.Vector3(); + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + vA = this.vertices[ face.a ]; + vB = this.vertices[ face.b ]; + vC = this.vertices[ face.c ]; + + cb.subVectors( vC, vB ); + ab.subVectors( vA, vB ); + cb.cross( ab ); + + vertices[ face.a ].add( cb ); + vertices[ face.b ].add( cb ); + vertices[ face.c ].add( cb ); + + } + + } else { + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + vertices[ face.a ].add( face.normal ); + vertices[ face.b ].add( face.normal ); + vertices[ face.c ].add( face.normal ); + + } + + } + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + vertices[ v ].normalize(); + + } + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + var vertexNormals = face.vertexNormals; + + if ( vertexNormals.length === 3 ) { + + vertexNormals[ 0 ].copy( vertices[ face.a ] ); + vertexNormals[ 1 ].copy( vertices[ face.b ] ); + vertexNormals[ 2 ].copy( vertices[ face.c ] ); + + } else { + + vertexNormals[ 0 ] = vertices[ face.a ].clone(); + vertexNormals[ 1 ] = vertices[ face.b ].clone(); + vertexNormals[ 2 ] = vertices[ face.c ].clone(); + + } + + } + + }, + + computeMorphNormals: function () { + + var i, il, f, fl, face; + + // save original normals + // - create temp variables on first access + // otherwise just copy (for faster repeated calls) + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + if ( ! face.__originalFaceNormal ) { + + face.__originalFaceNormal = face.normal.clone(); + + } else { + + face.__originalFaceNormal.copy( face.normal ); + + } + + if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; + + for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { + + if ( ! face.__originalVertexNormals[ i ] ) { + + face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); + + } else { + + face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); + + } + + } + + } + + // use temp geometry to compute face and vertex normals for each morph + + var tmpGeo = new THREE.Geometry(); + tmpGeo.faces = this.faces; + + for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { + + // create on first access + + if ( ! this.morphNormals[ i ] ) { + + this.morphNormals[ i ] = {}; + this.morphNormals[ i ].faceNormals = []; + this.morphNormals[ i ].vertexNormals = []; + + var dstNormalsFace = this.morphNormals[ i ].faceNormals; + var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; + + var faceNormal, vertexNormals; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + faceNormal = new THREE.Vector3(); + vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() }; + + dstNormalsFace.push( faceNormal ); + dstNormalsVertex.push( vertexNormals ); + + } + + } + + var morphNormals = this.morphNormals[ i ]; + + // set vertices to morph target + + tmpGeo.vertices = this.morphTargets[ i ].vertices; + + // compute morph normals + + tmpGeo.computeFaceNormals(); + tmpGeo.computeVertexNormals(); + + // store morph normals + + var faceNormal, vertexNormals; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + faceNormal = morphNormals.faceNormals[ f ]; + vertexNormals = morphNormals.vertexNormals[ f ]; + + faceNormal.copy( face.normal ); + + vertexNormals.a.copy( face.vertexNormals[ 0 ] ); + vertexNormals.b.copy( face.vertexNormals[ 1 ] ); + vertexNormals.c.copy( face.vertexNormals[ 2 ] ); + + } + + } + + // restore original normals + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + face.normal = face.__originalFaceNormal; + face.vertexNormals = face.__originalVertexNormals; + + } + + }, + + computeTangents: function () { + + console.warn( 'THREE.Geometry: .computeTangents() has been removed.' ); + + }, + + computeLineDistances: function () { + + var d = 0; + var vertices = this.vertices; + + for ( var i = 0, il = vertices.length; i < il; i ++ ) { + + if ( i > 0 ) { + + d += vertices[ i ].distanceTo( vertices[ i - 1 ] ); + + } + + this.lineDistances[ i ] = d; + + } + + }, + + computeBoundingBox: function () { + + if ( this.boundingBox === null ) { + + this.boundingBox = new THREE.Box3(); + + } + + this.boundingBox.setFromPoints( this.vertices ); + + }, + + computeBoundingSphere: function () { + + if ( this.boundingSphere === null ) { + + this.boundingSphere = new THREE.Sphere(); + + } + + this.boundingSphere.setFromPoints( this.vertices ); + + }, + + merge: function ( geometry, matrix, materialIndexOffset ) { + + if ( geometry instanceof THREE.Geometry === false ) { + + console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); + return; + + } + + var normalMatrix, + vertexOffset = this.vertices.length, + vertices1 = this.vertices, + vertices2 = geometry.vertices, + faces1 = this.faces, + faces2 = geometry.faces, + uvs1 = this.faceVertexUvs[ 0 ], + uvs2 = geometry.faceVertexUvs[ 0 ]; + + if ( materialIndexOffset === undefined ) materialIndexOffset = 0; + + if ( matrix !== undefined ) { + + normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + + } + + // vertices + + for ( var i = 0, il = vertices2.length; i < il; i ++ ) { + + var vertex = vertices2[ i ]; + + var vertexCopy = vertex.clone(); + + if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); + + vertices1.push( vertexCopy ); + + } + + // faces + + for ( i = 0, il = faces2.length; i < il; i ++ ) { + + var face = faces2[ i ], faceCopy, normal, color, + faceVertexNormals = face.vertexNormals, + faceVertexColors = face.vertexColors; + + faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); + faceCopy.normal.copy( face.normal ); + + if ( normalMatrix !== undefined ) { + + faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); + + } + + for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { + + normal = faceVertexNormals[ j ].clone(); + + if ( normalMatrix !== undefined ) { + + normal.applyMatrix3( normalMatrix ).normalize(); + + } + + faceCopy.vertexNormals.push( normal ); + + } + + faceCopy.color.copy( face.color ); + + for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { + + color = faceVertexColors[ j ]; + faceCopy.vertexColors.push( color.clone() ); + + } + + faceCopy.materialIndex = face.materialIndex + materialIndexOffset; + + faces1.push( faceCopy ); + + } + + // uvs + + for ( i = 0, il = uvs2.length; i < il; i ++ ) { + + var uv = uvs2[ i ], uvCopy = []; + + if ( uv === undefined ) { + + continue; + + } + + for ( var j = 0, jl = uv.length; j < jl; j ++ ) { + + uvCopy.push( uv[ j ].clone() ); + + } + + uvs1.push( uvCopy ); + + } + + }, + + mergeMesh: function ( mesh ) { + + if ( mesh instanceof THREE.Mesh === false ) { + + console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh ); + return; + + } + + mesh.matrixAutoUpdate && mesh.updateMatrix(); + + this.merge( mesh.geometry, mesh.matrix ); + + }, + + /* + * Checks for duplicate vertices with hashmap. + * Duplicated vertices are removed + * and faces' vertices are updated. + */ + + mergeVertices: function () { + + var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) + var unique = [], changes = []; + + var v, key; + var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 + var precision = Math.pow( 10, precisionPoints ); + var i, il, face; + var indices, j, jl; + + for ( i = 0, il = this.vertices.length; i < il; i ++ ) { + + v = this.vertices[ i ]; + key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); + + if ( verticesMap[ key ] === undefined ) { + + verticesMap[ key ] = i; + unique.push( this.vertices[ i ] ); + changes[ i ] = unique.length - 1; + + } else { + + //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); + changes[ i ] = changes[ verticesMap[ key ] ]; + + } + + } + + + // if faces are completely degenerate after merging vertices, we + // have to remove them from the geometry. + var faceIndicesToRemove = []; + + for ( i = 0, il = this.faces.length; i < il; i ++ ) { + + face = this.faces[ i ]; + + face.a = changes[ face.a ]; + face.b = changes[ face.b ]; + face.c = changes[ face.c ]; + + indices = [ face.a, face.b, face.c ]; + + var dupIndex = - 1; + + // if any duplicate vertices are found in a Face3 + // we have to remove the face as nothing can be saved + for ( var n = 0; n < 3; n ++ ) { + + if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { + + dupIndex = n; + faceIndicesToRemove.push( i ); + break; + + } + + } + + } + + for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { + + var idx = faceIndicesToRemove[ i ]; + + this.faces.splice( idx, 1 ); + + for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { + + this.faceVertexUvs[ j ].splice( idx, 1 ); + + } + + } + + // Use unique set of vertices + + var diff = this.vertices.length - unique.length; + this.vertices = unique; + return diff; + + }, + + sortFacesByMaterialIndex: function () { + + var faces = this.faces; + var length = faces.length; + + // tag faces + + for ( var i = 0; i < length; i ++ ) { + + faces[ i ]._id = i; + + } + + // sort faces + + function materialIndexSort( a, b ) { + + return a.materialIndex - b.materialIndex; + + } + + faces.sort( materialIndexSort ); + + // sort uvs + + var uvs1 = this.faceVertexUvs[ 0 ]; + var uvs2 = this.faceVertexUvs[ 1 ]; + + var newUvs1, newUvs2; + + if ( uvs1 && uvs1.length === length ) newUvs1 = []; + if ( uvs2 && uvs2.length === length ) newUvs2 = []; + + for ( var i = 0; i < length; i ++ ) { + + var id = faces[ i ]._id; + + if ( newUvs1 ) newUvs1.push( uvs1[ id ] ); + if ( newUvs2 ) newUvs2.push( uvs2[ id ] ); + + } + + if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1; + if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2; + + }, + + toJSON: function () { + + var data = { + metadata: { + version: 4.4, + type: 'Geometry', + generator: 'Geometry.toJSON' + } + }; + + // standard Geometry serialization + + data.uuid = this.uuid; + data.type = this.type; + if ( this.name !== '' ) data.name = this.name; + + if ( this.parameters !== undefined ) { + + var parameters = this.parameters; + + for ( var key in parameters ) { + + if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; + + } + + return data; + + } + + var vertices = []; + + for ( var i = 0; i < this.vertices.length; i ++ ) { + + var vertex = this.vertices[ i ]; + vertices.push( vertex.x, vertex.y, vertex.z ); + + } + + var faces = []; + var normals = []; + var normalsHash = {}; + var colors = []; + var colorsHash = {}; + var uvs = []; + var uvsHash = {}; + + for ( var i = 0; i < this.faces.length; i ++ ) { + + var face = this.faces[ i ]; + + var hasMaterial = false; // face.materialIndex !== undefined; + var hasFaceUv = false; // deprecated + var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; + var hasFaceNormal = face.normal.length() > 0; + var hasFaceVertexNormal = face.vertexNormals.length > 0; + var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; + var hasFaceVertexColor = face.vertexColors.length > 0; + + var faceType = 0; + + faceType = setBit( faceType, 0, 0 ); + faceType = setBit( faceType, 1, hasMaterial ); + faceType = setBit( faceType, 2, hasFaceUv ); + faceType = setBit( faceType, 3, hasFaceVertexUv ); + faceType = setBit( faceType, 4, hasFaceNormal ); + faceType = setBit( faceType, 5, hasFaceVertexNormal ); + faceType = setBit( faceType, 6, hasFaceColor ); + faceType = setBit( faceType, 7, hasFaceVertexColor ); + + faces.push( faceType ); + faces.push( face.a, face.b, face.c ); + + if ( hasFaceVertexUv ) { + + var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; + + faces.push( + getUvIndex( faceVertexUvs[ 0 ] ), + getUvIndex( faceVertexUvs[ 1 ] ), + getUvIndex( faceVertexUvs[ 2 ] ) + ); + + } + + if ( hasFaceNormal ) { + + faces.push( getNormalIndex( face.normal ) ); + + } + + if ( hasFaceVertexNormal ) { + + var vertexNormals = face.vertexNormals; + + faces.push( + getNormalIndex( vertexNormals[ 0 ] ), + getNormalIndex( vertexNormals[ 1 ] ), + getNormalIndex( vertexNormals[ 2 ] ) + ); + + } + + if ( hasFaceColor ) { + + faces.push( getColorIndex( face.color ) ); + + } + + if ( hasFaceVertexColor ) { + + var vertexColors = face.vertexColors; + + faces.push( + getColorIndex( vertexColors[ 0 ] ), + getColorIndex( vertexColors[ 1 ] ), + getColorIndex( vertexColors[ 2 ] ) + ); + + } + + } + + function setBit( value, position, enabled ) { + + return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); + + } + + function getNormalIndex( normal ) { + + var hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); + + if ( normalsHash[ hash ] !== undefined ) { + + return normalsHash[ hash ]; + + } + + normalsHash[ hash ] = normals.length / 3; + normals.push( normal.x, normal.y, normal.z ); + + return normalsHash[ hash ]; + + } + + function getColorIndex( color ) { + + var hash = color.r.toString() + color.g.toString() + color.b.toString(); + + if ( colorsHash[ hash ] !== undefined ) { + + return colorsHash[ hash ]; + + } + + colorsHash[ hash ] = colors.length; + colors.push( color.getHex() ); + + return colorsHash[ hash ]; + + } + + function getUvIndex( uv ) { + + var hash = uv.x.toString() + uv.y.toString(); + + if ( uvsHash[ hash ] !== undefined ) { + + return uvsHash[ hash ]; + + } + + uvsHash[ hash ] = uvs.length / 2; + uvs.push( uv.x, uv.y ); + + return uvsHash[ hash ]; + + } + + data.data = {}; + + data.data.vertices = vertices; + data.data.normals = normals; + if ( colors.length > 0 ) data.data.colors = colors; + if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility + data.data.faces = faces; + + return data; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( source ) { + + this.vertices = []; + this.faces = []; + this.faceVertexUvs = [ [] ]; + + var vertices = source.vertices; + + for ( var i = 0, il = vertices.length; i < il; i ++ ) { + + this.vertices.push( vertices[ i ].clone() ); + + } + + var faces = source.faces; + + for ( var i = 0, il = faces.length; i < il; i ++ ) { + + this.faces.push( faces[ i ].clone() ); + + } + + for ( var i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { + + var faceVertexUvs = source.faceVertexUvs[ i ]; + + if ( this.faceVertexUvs[ i ] === undefined ) { + + this.faceVertexUvs[ i ] = []; + + } + + for ( var j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { + + var uvs = faceVertexUvs[ j ], uvsCopy = []; + + for ( var k = 0, kl = uvs.length; k < kl; k ++ ) { + + var uv = uvs[ k ]; + + uvsCopy.push( uv.clone() ); + + } + + this.faceVertexUvs[ i ].push( uvsCopy ); + + } + + } + + return this; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Geometry.prototype ); + +THREE.GeometryIdCount = 0; + +// File:src/core/DirectGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.DirectGeometry = function () { + + Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.type = 'DirectGeometry'; + + this.indices = []; + this.vertices = []; + this.normals = []; + this.colors = []; + this.uvs = []; + this.uvs2 = []; + + this.groups = []; + + this.morphTargets = {}; + + this.skinWeights = []; + this.skinIndices = []; + + // this.lineDistances = []; + + this.boundingBox = null; + this.boundingSphere = null; + + // update flags + + this.verticesNeedUpdate = false; + this.normalsNeedUpdate = false; + this.colorsNeedUpdate = false; + this.uvsNeedUpdate = false; + this.groupsNeedUpdate = false; + +}; + +THREE.DirectGeometry.prototype = { + + constructor: THREE.DirectGeometry, + + computeBoundingBox: THREE.Geometry.prototype.computeBoundingBox, + computeBoundingSphere: THREE.Geometry.prototype.computeBoundingSphere, + + computeFaceNormals: function () { + + console.warn( 'THREE.DirectGeometry: computeFaceNormals() is not a method of this type of geometry.' ); + + }, + + computeVertexNormals: function () { + + console.warn( 'THREE.DirectGeometry: computeVertexNormals() is not a method of this type of geometry.' ); + + }, + + computeGroups: function ( geometry ) { + + var group; + var groups = []; + var materialIndex; + + var faces = geometry.faces; + + for ( var i = 0; i < faces.length; i ++ ) { + + var face = faces[ i ]; + + // materials + + if ( face.materialIndex !== materialIndex ) { + + materialIndex = face.materialIndex; + + if ( group !== undefined ) { + + group.count = ( i * 3 ) - group.start; + groups.push( group ); + + } + + group = { + start: i * 3, + materialIndex: materialIndex + }; + + } + + } + + if ( group !== undefined ) { + + group.count = ( i * 3 ) - group.start; + groups.push( group ); + + } + + this.groups = groups; + + }, + + fromGeometry: function ( geometry ) { + + var faces = geometry.faces; + var vertices = geometry.vertices; + var faceVertexUvs = geometry.faceVertexUvs; + + var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; + var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; + + // morphs + + var morphTargets = geometry.morphTargets; + var morphTargetsLength = morphTargets.length; + + if ( morphTargetsLength > 0 ) { + + var morphTargetsPosition = []; + + for ( var i = 0; i < morphTargetsLength; i ++ ) { + + morphTargetsPosition[ i ] = []; + + } + + this.morphTargets.position = morphTargetsPosition; + + } + + var morphNormals = geometry.morphNormals; + var morphNormalsLength = morphNormals.length; + + if ( morphNormalsLength > 0 ) { + + var morphTargetsNormal = []; + + for ( var i = 0; i < morphNormalsLength; i ++ ) { + + morphTargetsNormal[ i ] = []; + + } + + this.morphTargets.normal = morphTargetsNormal; + + } + + // skins + + var skinIndices = geometry.skinIndices; + var skinWeights = geometry.skinWeights; + + var hasSkinIndices = skinIndices.length === vertices.length; + var hasSkinWeights = skinWeights.length === vertices.length; + + // + + for ( var i = 0; i < faces.length; i ++ ) { + + var face = faces[ i ]; + + this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); + + var vertexNormals = face.vertexNormals; + + if ( vertexNormals.length === 3 ) { + + this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); + + } else { + + var normal = face.normal; + + this.normals.push( normal, normal, normal ); + + } + + var vertexColors = face.vertexColors; + + if ( vertexColors.length === 3 ) { + + this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); + + } else { + + var color = face.color; + + this.colors.push( color, color, color ); + + } + + if ( hasFaceVertexUv === true ) { + + var vertexUvs = faceVertexUvs[ 0 ][ i ]; + + if ( vertexUvs !== undefined ) { + + this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); + + } else { + + console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i ); + + this.uvs.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ); + + } + + } + + if ( hasFaceVertexUv2 === true ) { + + var vertexUvs = faceVertexUvs[ 1 ][ i ]; + + if ( vertexUvs !== undefined ) { + + this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); + + } else { + + console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i ); + + this.uvs2.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ); + + } + + } + + // morphs + + for ( var j = 0; j < morphTargetsLength; j ++ ) { + + var morphTarget = morphTargets[ j ].vertices; + + morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); + + } + + for ( var j = 0; j < morphNormalsLength; j ++ ) { + + var morphNormal = morphNormals[ j ].vertexNormals[ i ]; + + morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c ); + + } + + // skins + + if ( hasSkinIndices ) { + + this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); + + } + + if ( hasSkinWeights ) { + + this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); + + } + + } + + this.computeGroups( geometry ); + + this.verticesNeedUpdate = geometry.verticesNeedUpdate; + this.normalsNeedUpdate = geometry.normalsNeedUpdate; + this.colorsNeedUpdate = geometry.colorsNeedUpdate; + this.uvsNeedUpdate = geometry.uvsNeedUpdate; + this.groupsNeedUpdate = geometry.groupsNeedUpdate; + + return this; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.DirectGeometry.prototype ); + +// File:src/core/BufferGeometry.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BufferGeometry = function () { + + Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.type = 'BufferGeometry'; + + this.index = null; + this.attributes = {}; + + this.morphAttributes = {}; + + this.groups = []; + + this.boundingBox = null; + this.boundingSphere = null; + + this.drawRange = { start: 0, count: Infinity }; + +}; + +THREE.BufferGeometry.prototype = { + + constructor: THREE.BufferGeometry, + + addIndex: function ( index ) { + + console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); + this.setIndex( index ); + + }, + + getIndex: function () { + + return this.index; + + }, + + setIndex: function ( index ) { + + this.index = index; + + }, + + addAttribute: function ( name, attribute ) { + + if ( attribute instanceof THREE.BufferAttribute === false && attribute instanceof THREE.InterleavedBufferAttribute === false ) { + + console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); + + this.addAttribute( name, new THREE.BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); + + return; + + } + + if ( name === 'index' ) { + + console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); + this.setIndex( attribute ); + + return; + + } + + this.attributes[ name ] = attribute; + + }, + + getAttribute: function ( name ) { + + return this.attributes[ name ]; + + }, + + removeAttribute: function ( name ) { + + delete this.attributes[ name ]; + + }, + + get drawcalls() { + + console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' ); + return this.groups; + + }, + + get offsets() { + + console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' ); + return this.groups; + + }, + + addDrawCall: function ( start, count, indexOffset ) { + + if ( indexOffset !== undefined ) { + + console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); + + } + + console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); + this.addGroup( start, count ); + + }, + + clearDrawCalls: function () { + + console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); + this.clearGroups(); + + }, + + addGroup: function ( start, count, materialIndex ) { + + this.groups.push( { + + start: start, + count: count, + materialIndex: materialIndex !== undefined ? materialIndex : 0 + + } ); + + }, + + clearGroups: function () { + + this.groups = []; + + }, + + setDrawRange: function ( start, count ) { + + this.drawRange.start = start; + this.drawRange.count = count; + + }, + + applyMatrix: function ( matrix ) { + + var position = this.attributes.position; + + if ( position !== undefined ) { + + matrix.applyToVector3Array( position.array ); + position.needsUpdate = true; + + } + + var normal = this.attributes.normal; + + if ( normal !== undefined ) { + + var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + + normalMatrix.applyToVector3Array( normal.array ); + normal.needsUpdate = true; + + } + + if ( this.boundingBox !== null ) { + + this.computeBoundingBox(); + + } + + if ( this.boundingSphere !== null ) { + + this.computeBoundingSphere(); + + } + + }, + + rotateX: function () { + + // rotate geometry around world x-axis + + var m1; + + return function rotateX( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationX( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + rotateY: function () { + + // rotate geometry around world y-axis + + var m1; + + return function rotateY( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationY( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + rotateZ: function () { + + // rotate geometry around world z-axis + + var m1; + + return function rotateZ( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationZ( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + translate: function () { + + // translate geometry + + var m1; + + return function translate( x, y, z ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeTranslation( x, y, z ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + scale: function () { + + // scale geometry + + var m1; + + return function scale( x, y, z ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeScale( x, y, z ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + lookAt: function () { + + var obj; + + return function lookAt( vector ) { + + if ( obj === undefined ) obj = new THREE.Object3D(); + + obj.lookAt( vector ); + + obj.updateMatrix(); + + this.applyMatrix( obj.matrix ); + + }; + + }(), + + center: function () { + + this.computeBoundingBox(); + + var offset = this.boundingBox.center().negate(); + + this.translate( offset.x, offset.y, offset.z ); + + return offset; + + }, + + setFromObject: function ( object ) { + + // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this ); + + var geometry = object.geometry; + + if ( object instanceof THREE.Points || object instanceof THREE.Line ) { + + var positions = new THREE.Float32Attribute( geometry.vertices.length * 3, 3 ); + var colors = new THREE.Float32Attribute( geometry.colors.length * 3, 3 ); + + this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) ); + this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) ); + + if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { + + var lineDistances = new THREE.Float32Attribute( geometry.lineDistances.length, 1 ); + + this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) ); + + } + + if ( geometry.boundingSphere !== null ) { + + this.boundingSphere = geometry.boundingSphere.clone(); + + } + + if ( geometry.boundingBox !== null ) { + + this.boundingBox = geometry.boundingBox.clone(); + + } + + } else if ( object instanceof THREE.Mesh ) { + + if ( geometry instanceof THREE.Geometry ) { + + this.fromGeometry( geometry ); + + } + + } + + return this; + + }, + + updateFromObject: function ( object ) { + + var geometry = object.geometry; + + if ( object instanceof THREE.Mesh ) { + + var direct = geometry.__directGeometry; + + if ( direct === undefined ) { + + return this.fromGeometry( geometry ); + + } + + direct.verticesNeedUpdate = geometry.verticesNeedUpdate; + direct.normalsNeedUpdate = geometry.normalsNeedUpdate; + direct.colorsNeedUpdate = geometry.colorsNeedUpdate; + direct.uvsNeedUpdate = geometry.uvsNeedUpdate; + direct.groupsNeedUpdate = geometry.groupsNeedUpdate; + + geometry.verticesNeedUpdate = false; + geometry.normalsNeedUpdate = false; + geometry.colorsNeedUpdate = false; + geometry.uvsNeedUpdate = false; + geometry.groupsNeedUpdate = false; + + geometry = direct; + + } + + if ( geometry.verticesNeedUpdate === true ) { + + var attribute = this.attributes.position; + + if ( attribute !== undefined ) { + + attribute.copyVector3sArray( geometry.vertices ); + attribute.needsUpdate = true; + + } + + geometry.verticesNeedUpdate = false; + + } + + if ( geometry.normalsNeedUpdate === true ) { + + var attribute = this.attributes.normal; + + if ( attribute !== undefined ) { + + attribute.copyVector3sArray( geometry.normals ); + attribute.needsUpdate = true; + + } + + geometry.normalsNeedUpdate = false; + + } + + if ( geometry.colorsNeedUpdate === true ) { + + var attribute = this.attributes.color; + + if ( attribute !== undefined ) { + + attribute.copyColorsArray( geometry.colors ); + attribute.needsUpdate = true; + + } + + geometry.colorsNeedUpdate = false; + + } + + if ( geometry.uvsNeedUpdate ) { + + var attribute = this.attributes.uv; + + if ( attribute !== undefined ) { + + attribute.copyVector2sArray( geometry.uvs ); + attribute.needsUpdate = true; + + } + + geometry.uvsNeedUpdate = false; + + } + + if ( geometry.lineDistancesNeedUpdate ) { + + var attribute = this.attributes.lineDistance; + + if ( attribute !== undefined ) { + + attribute.copyArray( geometry.lineDistances ); + attribute.needsUpdate = true; + + } + + geometry.lineDistancesNeedUpdate = false; + + } + + if ( geometry.groupsNeedUpdate ) { + + geometry.computeGroups( object.geometry ); + this.groups = geometry.groups; + + geometry.groupsNeedUpdate = false; + + } + + return this; + + }, + + fromGeometry: function ( geometry ) { + + geometry.__directGeometry = new THREE.DirectGeometry().fromGeometry( geometry ); + + return this.fromDirectGeometry( geometry.__directGeometry ); + + }, + + fromDirectGeometry: function ( geometry ) { + + var positions = new Float32Array( geometry.vertices.length * 3 ); + this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); + + if ( geometry.normals.length > 0 ) { + + var normals = new Float32Array( geometry.normals.length * 3 ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); + + } + + if ( geometry.colors.length > 0 ) { + + var colors = new Float32Array( geometry.colors.length * 3 ); + this.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); + + } + + if ( geometry.uvs.length > 0 ) { + + var uvs = new Float32Array( geometry.uvs.length * 2 ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); + + } + + if ( geometry.uvs2.length > 0 ) { + + var uvs2 = new Float32Array( geometry.uvs2.length * 2 ); + this.addAttribute( 'uv2', new THREE.BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); + + } + + if ( geometry.indices.length > 0 ) { + + var TypeArray = geometry.vertices.length > 65535 ? Uint32Array : Uint16Array; + var indices = new TypeArray( geometry.indices.length * 3 ); + this.setIndex( new THREE.BufferAttribute( indices, 1 ).copyIndicesArray( geometry.indices ) ); + + } + + // groups + + this.groups = geometry.groups; + + // morphs + + for ( var name in geometry.morphTargets ) { + + var array = []; + var morphTargets = geometry.morphTargets[ name ]; + + for ( var i = 0, l = morphTargets.length; i < l; i ++ ) { + + var morphTarget = morphTargets[ i ]; + + var attribute = new THREE.Float32Attribute( morphTarget.length * 3, 3 ); + + array.push( attribute.copyVector3sArray( morphTarget ) ); + + } + + this.morphAttributes[ name ] = array; + + } + + // skinning + + if ( geometry.skinIndices.length > 0 ) { + + var skinIndices = new THREE.Float32Attribute( geometry.skinIndices.length * 4, 4 ); + this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) ); + + } + + if ( geometry.skinWeights.length > 0 ) { + + var skinWeights = new THREE.Float32Attribute( geometry.skinWeights.length * 4, 4 ); + this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) ); + + } + + // + + if ( geometry.boundingSphere !== null ) { + + this.boundingSphere = geometry.boundingSphere.clone(); + + } + + if ( geometry.boundingBox !== null ) { + + this.boundingBox = geometry.boundingBox.clone(); + + } + + return this; + + }, + + computeBoundingBox: function () { + + var vector = new THREE.Vector3(); + + return function () { + + if ( this.boundingBox === null ) { + + this.boundingBox = new THREE.Box3(); + + } + + var positions = this.attributes.position.array; + + if ( positions ) { + + var bb = this.boundingBox; + bb.makeEmpty(); + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + vector.fromArray( positions, i ); + bb.expandByPoint( vector ); + + } + + } + + if ( positions === undefined || positions.length === 0 ) { + + this.boundingBox.min.set( 0, 0, 0 ); + this.boundingBox.max.set( 0, 0, 0 ); + + } + + if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { + + console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); + + } + + }; + + }(), + + computeBoundingSphere: function () { + + var box = new THREE.Box3(); + var vector = new THREE.Vector3(); + + return function () { + + if ( this.boundingSphere === null ) { + + this.boundingSphere = new THREE.Sphere(); + + } + + var positions = this.attributes.position.array; + + if ( positions ) { + + box.makeEmpty(); + + var center = this.boundingSphere.center; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + vector.fromArray( positions, i ); + box.expandByPoint( vector ); + + } + + box.center( center ); + + // hoping to find a boundingSphere with a radius smaller than the + // boundingSphere of the boundingBox: sqrt(3) smaller in the best case + + var maxRadiusSq = 0; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + vector.fromArray( positions, i ); + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); + + } + + this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); + + if ( isNaN( this.boundingSphere.radius ) ) { + + console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); + + } + + } + + }; + + }(), + + computeFaceNormals: function () { + + // backwards compatibility + + }, + + computeVertexNormals: function () { + + var index = this.index; + var attributes = this.attributes; + var groups = this.groups; + + if ( attributes.position ) { + + var positions = attributes.position.array; + + if ( attributes.normal === undefined ) { + + this.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( positions.length ), 3 ) ); + + } else { + + // reset existing normals to zero + + var normals = attributes.normal.array; + + for ( var i = 0, il = normals.length; i < il; i ++ ) { + + normals[ i ] = 0; + + } + + } + + var normals = attributes.normal.array; + + var vA, vB, vC, + + pA = new THREE.Vector3(), + pB = new THREE.Vector3(), + pC = new THREE.Vector3(), + + cb = new THREE.Vector3(), + ab = new THREE.Vector3(); + + // indexed elements + + if ( index ) { + + var indices = index.array; + + if ( groups.length === 0 ) { + + this.addGroup( 0, indices.length ); + + } + + for ( var j = 0, jl = groups.length; j < jl; ++ j ) { + + var group = groups[ j ]; + + var start = group.start; + var count = group.count; + + for ( var i = start, il = start + count; i < il; i += 3 ) { + + vA = indices[ i + 0 ] * 3; + vB = indices[ i + 1 ] * 3; + vC = indices[ i + 2 ] * 3; + + pA.fromArray( positions, vA ); + pB.fromArray( positions, vB ); + pC.fromArray( positions, vC ); + + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); + + normals[ vA ] += cb.x; + normals[ vA + 1 ] += cb.y; + normals[ vA + 2 ] += cb.z; + + normals[ vB ] += cb.x; + normals[ vB + 1 ] += cb.y; + normals[ vB + 2 ] += cb.z; + + normals[ vC ] += cb.x; + normals[ vC + 1 ] += cb.y; + normals[ vC + 2 ] += cb.z; + + } + + } + + } else { + + // non-indexed elements (unconnected triangle soup) + + for ( var i = 0, il = positions.length; i < il; i += 9 ) { + + pA.fromArray( positions, i ); + pB.fromArray( positions, i + 3 ); + pC.fromArray( positions, i + 6 ); + + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); + + normals[ i ] = cb.x; + normals[ i + 1 ] = cb.y; + normals[ i + 2 ] = cb.z; + + normals[ i + 3 ] = cb.x; + normals[ i + 4 ] = cb.y; + normals[ i + 5 ] = cb.z; + + normals[ i + 6 ] = cb.x; + normals[ i + 7 ] = cb.y; + normals[ i + 8 ] = cb.z; + + } + + } + + this.normalizeNormals(); + + attributes.normal.needsUpdate = true; + + } + + }, + + computeTangents: function () { + + console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' ); + + }, + + computeOffsets: function ( size ) { + + console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.') + + }, + + merge: function ( geometry, offset ) { + + if ( geometry instanceof THREE.BufferGeometry === false ) { + + console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry ); + return; + + } + + if ( offset === undefined ) offset = 0; + + var attributes = this.attributes; + + for ( var key in attributes ) { + + if ( geometry.attributes[ key ] === undefined ) continue; + + var attribute1 = attributes[ key ]; + var attributeArray1 = attribute1.array; + + var attribute2 = geometry.attributes[ key ]; + var attributeArray2 = attribute2.array; + + var attributeSize = attribute2.itemSize; + + for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) { + + attributeArray1[ j ] = attributeArray2[ i ]; + + } + + } + + return this; + + }, + + normalizeNormals: function () { + + var normals = this.attributes.normal.array; + + var x, y, z, n; + + for ( var i = 0, il = normals.length; i < il; i += 3 ) { + + x = normals[ i ]; + y = normals[ i + 1 ]; + z = normals[ i + 2 ]; + + n = 1.0 / Math.sqrt( x * x + y * y + z * z ); + + normals[ i ] *= n; + normals[ i + 1 ] *= n; + normals[ i + 2 ] *= n; + + } + + }, + + toJSON: function () { + + var data = { + metadata: { + version: 4.4, + type: 'BufferGeometry', + generator: 'BufferGeometry.toJSON' + } + }; + + // standard BufferGeometry serialization + + data.uuid = this.uuid; + data.type = this.type; + if ( this.name !== '' ) data.name = this.name; + + if ( this.parameters !== undefined ) { + + var parameters = this.parameters; + + for ( var key in parameters ) { + + if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; + + } + + return data; + + } + + data.data = { attributes: {} }; + + var index = this.index; + + if ( index !== null ) { + + var array = Array.prototype.slice.call( index.array ); + + data.data.index = { + type: index.array.constructor.name, + array: array + }; + + } + + var attributes = this.attributes; + + for ( var key in attributes ) { + + var attribute = attributes[ key ]; + + var array = Array.prototype.slice.call( attribute.array ); + + data.data.attributes[ key ] = { + itemSize: attribute.itemSize, + type: attribute.array.constructor.name, + array: array + }; + + } + + var groups = this.groups; + + if ( groups.length > 0 ) { + + data.data.groups = JSON.parse( JSON.stringify( groups ) ); + + } + + var boundingSphere = this.boundingSphere; + + if ( boundingSphere !== null ) { + + data.data.boundingSphere = { + center: boundingSphere.center.toArray(), + radius: boundingSphere.radius + }; + + } + + return data; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( source ) { + + var index = source.index; + + if ( index !== null ) { + + this.setIndex( index.clone() ); + + } + + var attributes = source.attributes; + + for ( var name in attributes ) { + + var attribute = attributes[ name ]; + this.addAttribute( name, attribute.clone() ); + + } + + var groups = source.groups; + + for ( var i = 0, l = groups.length; i < l; i ++ ) { + + var group = groups[ i ]; + this.addGroup( group.start, group.count ); + + } + + return this; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype ); + +THREE.BufferGeometry.MaxIndex = 65535; + +// File:src/core/InstancedBufferGeometry.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.InstancedBufferGeometry = function () { + + THREE.BufferGeometry.call( this ); + + this.type = 'InstancedBufferGeometry'; + this.maxInstancedCount = undefined; + +}; + +THREE.InstancedBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.InstancedBufferGeometry.prototype.constructor = THREE.InstancedBufferGeometry; + +THREE.InstancedBufferGeometry.prototype.addGroup = function ( start, count, instances ) { + + this.groups.push( { + + start: start, + count: count, + instances: instances + + } ); + +}; + +THREE.InstancedBufferGeometry.prototype.copy = function ( source ) { + + var index = source.index; + + if ( index !== null ) { + + this.setIndex( index.clone() ); + + } + + var attributes = source.attributes; + + for ( var name in attributes ) { + + var attribute = attributes[ name ]; + this.addAttribute( name, attribute.clone() ); + + } + + var groups = source.groups; + + for ( var i = 0, l = groups.length; i < l; i ++ ) { + + var group = groups[ i ]; + this.addGroup( group.start, group.count, group.instances ); + + } + + return this; + +}; + +THREE.EventDispatcher.prototype.apply( THREE.InstancedBufferGeometry.prototype ); + +// File:src/animation/AnimationAction.js + +/** + * + * A clip that has been explicitly scheduled. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.AnimationAction = function ( clip, startTime, timeScale, weight, loop ) { + + if ( clip === undefined ) throw new Error( 'clip is null' ); + this.clip = clip; + this.localRoot = null; + this.startTime = startTime || 0; + this.timeScale = timeScale || 1; + this.weight = weight || 1; + this.loop = loop || THREE.LoopRepeat; + this.loopCount = 0; + this.enabled = true; // allow for easy disabling of the action. + + this.actionTime = - this.startTime; + this.clipTime = 0; + + this.propertyBindings = []; +}; + +/* +THREE.LoopOnce = 2200; +THREE.LoopRepeat = 2201; +THREE.LoopPingPing = 2202; +*/ + +THREE.AnimationAction.prototype = { + + constructor: THREE.AnimationAction, + + setLocalRoot: function( localRoot ) { + + this.localRoot = localRoot; + + return this; + + }, + + updateTime: function( clipDeltaTime ) { + + var previousClipTime = this.clipTime; + var previousLoopCount = this.loopCount; + var previousActionTime = this.actionTime; + + var duration = this.clip.duration; + + this.actionTime = this.actionTime + clipDeltaTime; + + if ( this.loop === THREE.LoopOnce ) { + + this.loopCount = 0; + this.clipTime = Math.min( Math.max( this.actionTime, 0 ), duration ); + + // if time is changed since last time, see if we have hit a start/end limit + if ( this.clipTime !== previousClipTime ) { + + if ( this.clipTime === duration ) { + + this.mixer.dispatchEvent( { type: 'finished', action: this, direction: 1 } ); + + } else if ( this.clipTime === 0 ) { + + this.mixer.dispatchEvent( { type: 'finished', action: this, direction: -1 } ); + + } + + } + + + return this.clipTime; + + } + + this.loopCount = Math.floor( this.actionTime / duration ); + + var newClipTime = this.actionTime - this.loopCount * duration; + newClipTime = newClipTime % duration; + + // if we are ping pong looping, ensure that we go backwards when appropriate + if ( this.loop == THREE.LoopPingPong ) { + + if ( Math.abs( this.loopCount % 2 ) === 1 ) { + + newClipTime = duration - newClipTime; + + } + + } + + this.clipTime = newClipTime; + + if ( this.loopCount !== previousLoopCount ) { + + this.mixer.dispatchEvent( { type: 'loop', action: this, loopDelta: ( this.loopCount - this.loopCount ) } ); + + } + + return this.clipTime; + + }, + + syncWith: function( action ) { + + this.actionTime = action.actionTime; + this.timeScale = action.timeScale; + + return this; + }, + + warpToDuration: function( duration ) { + + this.timeScale = this.clip.duration / duration; + + return this; + }, + + init: function( time ) { + + this.clipTime = time - this.startTime; + + return this; + + }, + + update: function( clipDeltaTime ) { + + this.updateTime( clipDeltaTime ); + + var clipResults = this.clip.getAt( this.clipTime ); + + return clipResults; + + }, + + getTimeScaleAt: function( time ) { + + if ( this.timeScale.getAt ) { + // pass in time, not clip time, allows for fadein/fadeout across multiple loops of the clip + return this.timeScale.getAt( time ); + + } + + return this.timeScale; + + }, + + getWeightAt: function( time ) { + + if ( this.weight.getAt ) { + // pass in time, not clip time, allows for fadein/fadeout across multiple loops of the clip + return this.weight.getAt( time ); + + } + + return this.weight; + + } + +}; + +// File:src/animation/AnimationClip.js + +/** + * + * Reusable set of Tracks that represent an animation. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.AnimationClip = function ( name, duration, tracks ) { + + this.name = name; + this.tracks = tracks; + this.duration = ( duration !== undefined ) ? duration : -1; + + // this means it should figure out its duration by scanning the tracks + if ( this.duration < 0 ) { + for ( var i = 0; i < this.tracks.length; i ++ ) { + var track = this.tracks[i]; + this.duration = Math.max( track.keys[ track.keys.length - 1 ].time ); + } + } + + // maybe only do these on demand, as doing them here could potentially slow down loading + // but leaving these here during development as this ensures a lot of testing of these functions + this.trim(); + this.optimize(); + + this.results = []; + +}; + +THREE.AnimationClip.prototype = { + + constructor: THREE.AnimationClip, + + getAt: function( clipTime ) { + + clipTime = Math.max( 0, Math.min( clipTime, this.duration ) ); + + for ( var i = 0; i < this.tracks.length; i ++ ) { + + var track = this.tracks[ i ]; + + this.results[ i ] = track.getAt( clipTime ); + + } + + return this.results; + }, + + trim: function() { + + for ( var i = 0; i < this.tracks.length; i ++ ) { + + this.tracks[ i ].trim( 0, this.duration ); + + } + + return this; + + }, + + optimize: function() { + + for ( var i = 0; i < this.tracks.length; i ++ ) { + + this.tracks[ i ].optimize(); + + } + + return this; + + } + +}; + + +THREE.AnimationClip.CreateFromMorphTargetSequence = function( name, morphTargetSequence, fps ) { + + + var numMorphTargets = morphTargetSequence.length; + var tracks = []; + + for ( var i = 0; i < numMorphTargets; i ++ ) { + + var keys = []; + + keys.push( { time: ( i + numMorphTargets - 1 ) % numMorphTargets, value: 0 } ); + keys.push( { time: i, value: 1 } ); + keys.push( { time: ( i + 1 ) % numMorphTargets, value: 0 } ); + + keys.sort( THREE.KeyframeTrack.keyComparer ); + + // if there is a key at the first frame, duplicate it as the last frame as well for perfect loop. + if ( keys[0].time === 0 ) { + keys.push( { + time: numMorphTargets, + value: keys[0].value + }); + } + + tracks.push( new THREE.NumberKeyframeTrack( '.morphTargetInfluences[' + morphTargetSequence[i].name + ']', keys ).scale( 1.0 / fps ) ); + } + + return new THREE.AnimationClip( name, -1, tracks ); + +}; + +THREE.AnimationClip.findByName = function( clipArray, name ) { + + for ( var i = 0; i < clipArray.length; i ++ ) { + + if ( clipArray[i].name === name ) { + + return clipArray[i]; + + } + } + + return null; + +}; + +THREE.AnimationClip.CreateClipsFromMorphTargetSequences = function( morphTargets, fps ) { + + var animationToMorphTargets = {}; + + // tested with https://regex101.com/ on trick sequences such flamingo_flyA_003, flamingo_run1_003, crdeath0059 + var pattern = /^([\w-]*?)([\d]+)$/; + + // sort morph target names into animation groups based patterns like Walk_001, Walk_002, Run_001, Run_002 + for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { + + var morphTarget = morphTargets[ i ]; + var parts = morphTarget.name.match( pattern ); + + if ( parts && parts.length > 1 ) { + + var name = parts[ 1 ]; + + var animationMorphTargets = animationToMorphTargets[ name ]; + if ( ! animationMorphTargets ) { + animationToMorphTargets[ name ] = animationMorphTargets = []; + } + + animationMorphTargets.push( morphTarget ); + + } + + } + + var clips = []; + + for ( var name in animationToMorphTargets ) { + + clips.push( THREE.AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps ) ); + } + + return clips; + +}; + +// parse the standard JSON format for clips +THREE.AnimationClip.parse = function( json ) { + + var tracks = []; + + for ( var i = 0; i < json.tracks.length; i ++ ) { + + tracks.push( THREE.KeyframeTrack.parse( json.tracks[i] ).scale( 1.0 / json.fps ) ); + + } + + return new THREE.AnimationClip( json.name, json.duration, tracks ); + +}; + + +// parse the animation.hierarchy format +THREE.AnimationClip.parseAnimation = function( animation, bones, nodeName ) { + + if ( ! animation ) { + console.error( " no animation in JSONLoader data" ); + return null; + } + + var convertTrack = function( trackName, animationKeys, propertyName, trackType, animationKeyToValueFunc ) { + + var keys = []; + + for ( var k = 0; k < animationKeys.length; k ++ ) { + + var animationKey = animationKeys[k]; + + if ( animationKey[propertyName] !== undefined ) { + + keys.push( { time: animationKey.time, value: animationKeyToValueFunc( animationKey ) } ); + } + + } + + // only return track if there are actually keys. + if ( keys.length > 0 ) { + + return new trackType( trackName, keys ); + + } + + return null; + + }; + + var tracks = []; + + var clipName = animation.name || 'default'; + var duration = animation.length || -1; // automatic length determination in AnimationClip. + var fps = animation.fps || 30; + + var hierarchyTracks = animation.hierarchy || []; + + for ( var h = 0; h < hierarchyTracks.length; h ++ ) { + + var animationKeys = hierarchyTracks[ h ].keys; + + // skip empty tracks + if ( ! animationKeys || animationKeys.length == 0 ) { + continue; + } + + // process morph targets in a way exactly compatible with AnimationHandler.init( animation ) + if ( animationKeys[0].morphTargets ) { + + // figure out all morph targets used in this track + var morphTargetNames = {}; + for ( var k = 0; k < animationKeys.length; k ++ ) { + + if ( animationKeys[k].morphTargets ) { + for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) { + + morphTargetNames[ animationKeys[k].morphTargets[m] ] = -1; + } + } + + } + + // create a track for each morph target with all zero morphTargetInfluences except for the keys in which the morphTarget is named. + for ( var morphTargetName in morphTargetNames ) { + + var keys = []; + + for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) { + + var animationKey = animationKeys[k]; + + keys.push( { + time: animationKey.time, + value: (( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ) + }); + + } + + tracks.push( new THREE.NumberKeyframeTrack( nodeName + '.morphTargetInfluence[' + morphTargetName + ']', keys ) ); + + } + + duration = morphTargetNames.length * ( fps || 1.0 ); + + } else { + + var boneName = nodeName + '.bones[' + bones[ h ].name + ']'; + + // track contains positions... + var positionTrack = convertTrack( boneName + '.position', animationKeys, 'pos', THREE.VectorKeyframeTrack, function( animationKey ) { + return new THREE.Vector3().fromArray( animationKey.pos ) + } ); + + if ( positionTrack ) tracks.push( positionTrack ); + + // track contains quaternions... + var quaternionTrack = convertTrack( boneName + '.quaternion', animationKeys, 'rot', THREE.QuaternionKeyframeTrack, function( animationKey ) { + if ( animationKey.rot.slerp ) { + return animationKey.rot.clone(); + } else { + return new THREE.Quaternion().fromArray( animationKey.rot ); + } + } ); + + if ( quaternionTrack ) tracks.push( quaternionTrack ); + + // track contains quaternions... + var scaleTrack = convertTrack( boneName + '.scale', animationKeys, 'scl', THREE.VectorKeyframeTrack, function( animationKey ) { + return new THREE.Vector3().fromArray( animationKey.scl ) + } ); + + if ( scaleTrack ) tracks.push( scaleTrack ); + + } + } + + if ( tracks.length === 0 ) { + + return null; + + } + + var clip = new THREE.AnimationClip( clipName, duration, tracks ); + + return clip; + +}; + +// File:src/animation/AnimationMixer.js + +/** + * + * Mixes together the AnimationClips scheduled by AnimationActions and applies them to the root and subtree + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.AnimationMixer = function( root ) { + + this.root = root; + this.time = 0; + this.timeScale = 1.0; + this.actions = []; + this.propertyBindingMap = {}; + +}; + +THREE.AnimationMixer.prototype = { + + constructor: THREE.AnimationMixer, + + addAction: function( action ) { + + // TODO: check for duplicate action names? Or provide each action with a UUID? + + this.actions.push( action ); + action.init( this.time ); + action.mixer = this; + + var tracks = action.clip.tracks; + + var root = action.localRoot || this.root; + + for ( var i = 0; i < tracks.length; i ++ ) { + + var track = tracks[ i ]; + + var propertyBindingKey = root.uuid + '-' + track.name; + var propertyBinding = this.propertyBindingMap[ propertyBindingKey ]; + + if ( propertyBinding === undefined ) { + + propertyBinding = new THREE.PropertyBinding( root, track.name ); + this.propertyBindingMap[ propertyBindingKey ] = propertyBinding; + + } + + // push in the same order as the tracks. + action.propertyBindings.push( propertyBinding ); + + // track usages of shared property bindings, because if we leave too many around, the mixer can get slow + propertyBinding.referenceCount += 1; + + } + + }, + + removeAllActions: function() { + + for ( var i = 0; i < this.actions.length; i ++ ) { + + this.actions[i].mixer = null; + + } + + // unbind all property bindings + for ( var properyBindingKey in this.propertyBindingMap ) { + + this.propertyBindingMap[ properyBindingKey ].unbind(); + + } + + this.actions = []; + this.propertyBindingMap = {}; + + return this; + + }, + + removeAction: function( action ) { + + var index = this.actions.indexOf( action ); + + if ( index !== - 1 ) { + + this.actions.splice( index, 1 ); + action.mixer = null; + + } + + + // remove unused property bindings because if we leave them around the mixer can get slow + var root = action.localRoot || this.root; + var tracks = action.clip.tracks; + + for ( var i = 0; i < tracks.length; i ++ ) { + + var track = tracks[ i ]; + + var propertyBindingKey = root.uuid + '-' + track.name; + var propertyBinding = this.propertyBindingMap[ propertyBindingKey ]; + + propertyBinding.referenceCount -= 1; + + if ( propertyBinding.referenceCount <= 0 ) { + + propertyBinding.unbind(); + + delete this.propertyBindingMap[ propertyBindingKey ]; + + } + } + + return this; + + }, + + // can be optimized if needed + findActionByName: function( name ) { + + for ( var i = 0; i < this.actions.length; i ++ ) { + + if ( this.actions[i].name === name ) return this.actions[i]; + + } + + return null; + + }, + + play: function( action, optionalFadeInDuration ) { + + action.startTime = this.time; + this.addAction( action ); + + return this; + + }, + + fadeOut: function( action, duration ) { + + var keys = []; + + keys.push( { time: this.time, value: 1 } ); + keys.push( { time: this.time + duration, value: 0 } ); + + action.weight = new THREE.NumberKeyframeTrack( "weight", keys ); + + return this; + + }, + + fadeIn: function( action, duration ) { + + var keys = []; + + keys.push( { time: this.time, value: 0 } ); + keys.push( { time: this.time + duration, value: 1 } ); + + action.weight = new THREE.NumberKeyframeTrack( "weight", keys ); + + return this; + + }, + + warp: function( action, startTimeScale, endTimeScale, duration ) { + + var keys = []; + + keys.push( { time: this.time, value: startTimeScale } ); + keys.push( { time: this.time + duration, value: endTimeScale } ); + + action.timeScale = new THREE.NumberKeyframeTrack( "timeScale", keys ); + + return this; + + }, + + crossFade: function( fadeOutAction, fadeInAction, duration, warp ) { + + this.fadeOut( fadeOutAction, duration ); + this.fadeIn( fadeInAction, duration ); + + if ( warp ) { + + var startEndRatio = fadeOutAction.clip.duration / fadeInAction.clip.duration; + var endStartRatio = 1.0 / startEndRatio; + + this.warp( fadeOutAction, 1.0, startEndRatio, duration ); + this.warp( fadeInAction, endStartRatio, 1.0, duration ); + + } + + return this; + + }, + + update: function( deltaTime ) { + + var mixerDeltaTime = deltaTime * this.timeScale; + this.time += mixerDeltaTime; + + for ( var i = 0; i < this.actions.length; i ++ ) { + + var action = this.actions[i]; + + var weight = action.getWeightAt( this.time ); + + var actionTimeScale = action.getTimeScaleAt( this.time ); + var actionDeltaTime = mixerDeltaTime * actionTimeScale; + + var actionResults = action.update( actionDeltaTime ); + + if ( action.weight <= 0 || ! action.enabled ) continue; + + for ( var j = 0; j < actionResults.length; j ++ ) { + + var name = action.clip.tracks[j].name; + + action.propertyBindings[ j ].accumulate( actionResults[j], weight ); + + } + + } + + // apply to nodes + for ( var propertyBindingKey in this.propertyBindingMap ) { + + this.propertyBindingMap[ propertyBindingKey ].apply(); + + } + + return this; + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.AnimationMixer.prototype ); + +// File:src/animation/AnimationUtils.js + +/** + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.AnimationUtils = { + + getEqualsFunc: function( exemplarValue ) { + + if ( exemplarValue.equals ) { + return function equals_object( a, b ) { + return a.equals( b ); + } + } + + return function equals_primitive( a, b ) { + return ( a === b ); + }; + + }, + + clone: function( exemplarValue ) { + + var typeName = typeof exemplarValue; + if ( typeName === "object" ) { + if ( exemplarValue.clone ) { + return exemplarValue.clone(); + } + console.error( "can not figure out how to copy exemplarValue", exemplarValue ); + } + + return exemplarValue; + + }, + + lerp: function( a, b, alpha, interTrack ) { + + var lerpFunc = THREE.AnimationUtils.getLerpFunc( a, interTrack ); + + return lerpFunc( a, b, alpha ); + + }, + + lerp_object: function( a, b, alpha ) { + return a.lerp( b, alpha ); + }, + + slerp_object: function( a, b, alpha ) { + return a.slerp( b, alpha ); + }, + + lerp_number: function( a, b, alpha ) { + return a * ( 1 - alpha ) + b * alpha; + }, + + lerp_boolean: function( a, b, alpha ) { + return ( alpha < 0.5 ) ? a : b; + }, + + lerp_boolean_immediate: function( a, b, alpha ) { + return a; + }, + + lerp_string: function( a, b, alpha ) { + return ( alpha < 0.5 ) ? a : b; + }, + + lerp_string_immediate: function( a, b, alpha ) { + return a; + }, + + // NOTE: this is an accumulator function that modifies the first argument (e.g. a). This is to minimize memory alocations. + getLerpFunc: function( exemplarValue, interTrack ) { + + if ( exemplarValue === undefined || exemplarValue === null ) throw new Error( "examplarValue is null" ); + + var typeName = typeof exemplarValue; + + switch( typeName ) { + + case "object": + if ( exemplarValue.lerp ) { + return THREE.AnimationUtils.lerp_object; + } + + if ( exemplarValue.slerp ) { + return THREE.AnimationUtils.slerp_object; + } + break; + + case "number": + return THREE.AnimationUtils.lerp_number; + + case "boolean": + if ( interTrack ) { + return THREE.AnimationUtils.lerp_boolean; + } else { + return THREE.AnimationUtils.lerp_boolean_immediate; + } + + case "string": + if ( interTrack ) { + return THREE.AnimationUtils.lerp_string; + } else { + return THREE.AnimationUtils.lerp_string_immediate; + } + + } + + } + +}; + +// File:src/animation/KeyframeTrack.js + +/** + * + * A Track that returns a keyframe interpolated value, currently linearly interpolated + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.KeyframeTrack = function ( name, keys ) { + + if ( name === undefined ) throw new Error( "track name is undefined" ); + if ( keys === undefined || keys.length === 0 ) throw new Error( "no keys in track named " + name ); + + this.name = name; + this.keys = keys; // time in seconds, value as value + + // the index of the last result, used as a starting point for local search. + this.lastIndex = 0; + + this.validate(); + this.optimize(); + +}; + +THREE.KeyframeTrack.prototype = { + + constructor: THREE.KeyframeTrack, + + getAt: function( time ) { + + + // this can not go higher than this.keys.length. + while( ( this.lastIndex < this.keys.length ) && ( time >= this.keys[this.lastIndex].time ) ) { + this.lastIndex ++; + }; + + // this can not go lower than 0. + while( ( this.lastIndex > 0 ) && ( time < this.keys[this.lastIndex - 1].time ) ) { + this.lastIndex --; + } + + if ( this.lastIndex >= this.keys.length ) { + + this.setResult( this.keys[ this.keys.length - 1 ].value ); + + return this.result; + + } + + if ( this.lastIndex === 0 ) { + + this.setResult( this.keys[ 0 ].value ); + + return this.result; + + } + + var prevKey = this.keys[ this.lastIndex - 1 ]; + this.setResult( prevKey.value ); + + // if true, means that prev/current keys are identical, thus no interpolation required. + if ( prevKey.constantToNext ) { + + return this.result; + + } + + // linear interpolation to start with + var currentKey = this.keys[ this.lastIndex ]; + var alpha = ( time - prevKey.time ) / ( currentKey.time - prevKey.time ); + this.result = this.lerpValues( this.result, currentKey.value, alpha ); + + return this.result; + + }, + + // move all keyframes either forwards or backwards in time + shift: function( timeOffset ) { + + if ( timeOffset !== 0.0 ) { + + for ( var i = 0; i < this.keys.length; i ++ ) { + this.keys[i].time += timeOffset; + } + + } + + return this; + + }, + + // scale all keyframe times by a factor (useful for frame <-> seconds conversions) + scale: function( timeScale ) { + + if ( timeScale !== 1.0 ) { + + for ( var i = 0; i < this.keys.length; i ++ ) { + this.keys[i].time *= timeScale; + } + + } + + return this; + + }, + + // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. + // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values + trim: function( startTime, endTime ) { + + var firstKeysToRemove = 0; + for ( var i = 1; i < this.keys.length; i ++ ) { + if ( this.keys[i] <= startTime ) { + firstKeysToRemove ++; + } + } + + var lastKeysToRemove = 0; + for ( var i = this.keys.length - 2; i > 0; i ++ ) { + if ( this.keys[i] >= endTime ) { + lastKeysToRemove ++; + } else { + break; + } + } + + // remove last keys first because it doesn't affect the position of the first keys (the otherway around doesn't work as easily) + if ( ( firstKeysToRemove + lastKeysToRemove ) > 0 ) { + this.keys = this.keys.splice( firstKeysToRemove, this.keys.length - lastKeysToRemove - firstKeysToRemove );; + } + + return this; + + }, + + /* NOTE: This is commented out because we really shouldn't have to handle unsorted key lists + Tracks with out of order keys should be considered to be invalid. - bhouston + sort: function() { + + this.keys.sort( THREE.KeyframeTrack.keyComparer ); + + return this; + + },*/ + + // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable + // One could eventually ensure that all key.values in a track are all of the same type (otherwise interpolation makes no sense.) + validate: function() { + + var prevKey = null; + + if ( this.keys.length === 0 ) { + console.error( " track is empty, no keys", this ); + return; + } + + for ( var i = 0; i < this.keys.length; i ++ ) { + + var currKey = this.keys[i]; + + if ( ! currKey ) { + console.error( " key is null in track", this, i ); + return; + } + + if ( ( typeof currKey.time ) !== 'number' || isNaN( currKey.time ) ) { + console.error( " key.time is not a valid number", this, i, currKey ); + return; + } + + if ( currKey.value === undefined || currKey.value === null) { + console.error( " key.value is null in track", this, i, currKey ); + return; + } + + if ( prevKey && prevKey.time > currKey.time ) { + console.error( " key.time is less than previous key time, out of order keys", this, i, currKey, prevKey ); + return; + } + + prevKey = currKey; + + } + + return this; + + }, + + // currently only removes equivalent sequential keys (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0), which are common in morph target animations + optimize: function() { + + var newKeys = []; + var prevKey = this.keys[0]; + newKeys.push( prevKey ); + + var equalsFunc = THREE.AnimationUtils.getEqualsFunc( prevKey.value ); + + for ( var i = 1; i < this.keys.length - 1; i ++ ) { + var currKey = this.keys[i]; + var nextKey = this.keys[i+1]; + + // if prevKey & currKey are the same time, remove currKey. If you want immediate adjacent keys, use an epsilon offset + // it is not possible to have two keys at the same time as we sort them. The sort is not stable on keys with the same time. + if ( ( prevKey.time === currKey.time ) ) { + + continue; + + } + + // remove completely unnecessary keyframes that are the same as their prev and next keys + if ( this.compareValues( prevKey.value, currKey.value ) && this.compareValues( currKey.value, nextKey.value ) ) { + + continue; + + } + + // determine if interpolation is required + prevKey.constantToNext = this.compareValues( prevKey.value, currKey.value ); + + newKeys.push( currKey ); + prevKey = currKey; + } + newKeys.push( this.keys[ this.keys.length - 1 ] ); + + this.keys = newKeys; + + return this; + + } + +}; + +THREE.KeyframeTrack.keyComparer = function keyComparator(key0, key1) { + return key0.time - key1.time; +}; + +THREE.KeyframeTrack.parse = function( json ) { + + if ( json.type === undefined ) throw new Error( "track type undefined, can not parse" ); + + var trackType = THREE.KeyframeTrack.GetTrackTypeForTypeName( json.type ); + + return trackType.parse( json ); + +}; + +THREE.KeyframeTrack.GetTrackTypeForTypeName = function( typeName ) { + switch( typeName.toLowerCase() ) { + case "vector": + case "vector2": + case "vector3": + case "vector4": + return THREE.VectorKeyframeTrack; + + case "quaternion": + return THREE.QuaternionKeyframeTrack; + + case "integer": + case "scalar": + case "double": + case "float": + case "number": + return THREE.NumberKeyframeTrack; + + case "bool": + case "boolean": + return THREE.BooleanKeyframeTrack; + + case "string": + return THREE.StringKeyframeTrack; + }; + + throw new Error( "Unsupported typeName: " + typeName ); +}; + +// File:src/animation/PropertyBinding.js + +/** + * + * A track bound to a real value in the scene graph. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.PropertyBinding = function ( rootNode, trackName ) { + + this.rootNode = rootNode; + this.trackName = trackName; + this.referenceCount = 0; + this.originalValue = null; // the value of the property before it was controlled by this binding + + var parseResults = THREE.PropertyBinding.parseTrackName( trackName ); + + this.directoryName = parseResults.directoryName; + this.nodeName = parseResults.nodeName; + this.objectName = parseResults.objectName; + this.objectIndex = parseResults.objectIndex; + this.propertyName = parseResults.propertyName; + this.propertyIndex = parseResults.propertyIndex; + + this.node = THREE.PropertyBinding.findNode( rootNode, this.nodeName ) || rootNode; + + this.cumulativeValue = null; + this.cumulativeWeight = 0; +}; + +THREE.PropertyBinding.prototype = { + + constructor: THREE.PropertyBinding, + + reset: function() { + + this.cumulativeValue = null; + this.cumulativeWeight = 0; + + }, + + accumulate: function( value, weight ) { + + if ( ! this.isBound ) this.bind(); + + if ( this.cumulativeWeight === 0 ) { + + if ( weight > 0 ) { + + if ( this.cumulativeValue === null ) { + this.cumulativeValue = THREE.AnimationUtils.clone( value ); + } + this.cumulativeWeight = weight; + + } + + } else { + + var lerpAlpha = weight / ( this.cumulativeWeight + weight ); + this.cumulativeValue = this.lerpValue( this.cumulativeValue, value, lerpAlpha ); + this.cumulativeWeight += weight; + + } + + }, + + unbind: function() { + + if ( ! this.isBound ) return; + + this.setValue( this.originalValue ); + + this.setValue = null; + this.getValue = null; + this.lerpValue = null; + this.equalsValue = null; + this.triggerDirty = null; + this.isBound = false; + + }, + + // bind to the real property in the scene graph, remember original value, memorize various accessors for speed/inefficiency + bind: function() { + + if ( this.isBound ) return; + + var targetObject = this.node; + + // ensure there is a value node + if ( ! targetObject ) { + console.error( " trying to update node for track: " + this.trackName + " but it wasn't found." ); + return; + } + + if ( this.objectName ) { + // special case were we need to reach deeper into the hierarchy to get the face materials.... + if ( this.objectName === "materials" ) { + if ( ! targetObject.material ) { + console.error( ' can not bind to material as node does not have a material', this ); + return; + } + if ( ! targetObject.material.materials ) { + console.error( ' can not bind to material.materials as node.material does not have a materials array', this ); + return; + } + targetObject = targetObject.material.materials; + } else if ( this.objectName === "bones" ) { + if ( ! targetObject.skeleton ) { + console.error( ' can not bind to bones as node does not have a skeleton', this ); + return; + } + // potential future optimization: skip this if propertyIndex is already an integer, and convert the integer string to a true integer. + + targetObject = targetObject.skeleton.bones; + + // support resolving morphTarget names into indices. + for ( var i = 0; i < targetObject.length; i ++ ) { + if ( targetObject[i].name === this.objectIndex ) { + this.objectIndex = i; + break; + } + } + } else { + + if ( targetObject[ this.objectName ] === undefined ) { + console.error( ' can not bind to objectName of node, undefined', this ); + return; + } + targetObject = targetObject[ this.objectName ]; + } + + if ( this.objectIndex !== undefined ) { + if ( targetObject[ this.objectIndex ] === undefined ) { + console.error( " trying to bind to objectIndex of objectName, but is undefined:", this, targetObject ); + return; + } + + targetObject = targetObject[ this.objectIndex ]; + } + + } + + // special case mappings + var nodeProperty = targetObject[ this.propertyName ]; + if ( ! nodeProperty ) { + console.error( " trying to update property for track: " + this.nodeName + '.' + this.propertyName + " but it wasn't found.", targetObject ); + return; + } + + // access a sub element of the property array (only primitives are supported right now) + if ( this.propertyIndex !== undefined ) { + + if ( this.propertyName === "morphTargetInfluences" ) { + // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. + + // support resolving morphTarget names into indices. + if ( ! targetObject.geometry ) { + console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry', this ); + } + if ( ! targetObject.geometry.morphTargets ) { + console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry.morphTargets', this ); + } + + for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) { + if ( targetObject.geometry.morphTargets[i].name === this.propertyIndex ) { + this.propertyIndex = i; + break; + } + } + } + + this.setValue = function setValue_propertyIndexed( value ) { + if ( ! this.equalsValue( nodeProperty[ this.propertyIndex ], value ) ) { + nodeProperty[ this.propertyIndex ] = value; + return true; + } + return false; + }; + + this.getValue = function getValue_propertyIndexed() { + return nodeProperty[ this.propertyIndex ]; + }; + + } + // must use copy for Object3D.Euler/Quaternion + else if ( nodeProperty.copy ) { + + this.setValue = function setValue_propertyObject( value ) { + if ( ! this.equalsValue( nodeProperty, value ) ) { + nodeProperty.copy( value ); + return true; + } + return false; + } + + this.getValue = function getValue_propertyObject() { + return nodeProperty; + }; + + } + // otherwise just set the property directly on the node (do not use nodeProperty as it may not be a reference object) + else { + + this.setValue = function setValue_property( value ) { + if ( ! this.equalsValue( targetObject[ this.propertyName ], value ) ) { + targetObject[ this.propertyName ] = value; + return true; + } + return false; + } + + this.getValue = function getValue_property() { + return targetObject[ this.propertyName ]; + }; + + } + + // trigger node dirty + if ( targetObject.needsUpdate !== undefined ) { // material + + this.triggerDirty = function triggerDirty_needsUpdate() { + this.node.needsUpdate = true; + } + + } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform + + this.triggerDirty = function triggerDirty_matrixWorldNeedsUpdate() { + targetObject.matrixWorldNeedsUpdate = true; + } + + } + + this.originalValue = this.getValue(); + + this.equalsValue = THREE.AnimationUtils.getEqualsFunc( this.originalValue ); + this.lerpValue = THREE.AnimationUtils.getLerpFunc( this.originalValue, true ); + + this.isBound = true; + + }, + + apply: function() { + + // for speed capture the setter pattern as a closure (sort of a memoization pattern: https://en.wikipedia.org/wiki/Memoization) + if ( ! this.isBound ) this.bind(); + + // early exit if there is nothing to apply. + if ( this.cumulativeWeight > 0 ) { + + // blend with original value + if ( this.cumulativeWeight < 1 ) { + + var remainingWeight = 1 - this.cumulativeWeight; + var lerpAlpha = remainingWeight / ( this.cumulativeWeight + remainingWeight ); + this.cumulativeValue = this.lerpValue( this.cumulativeValue, this.originalValue, lerpAlpha ); + + } + + var valueChanged = this.setValue( this.cumulativeValue ); + + if ( valueChanged && this.triggerDirty ) { + this.triggerDirty(); + } + + // reset accumulator + this.cumulativeValue = null; + this.cumulativeWeight = 0; + + } + } + +}; + + +THREE.PropertyBinding.parseTrackName = function( trackName ) { + + // matches strings in the form of: + // nodeName.property + // nodeName.property[accessor] + // nodeName.material.property[accessor] + // uuid.property[accessor] + // uuid.objectName[objectIndex].propertyName[propertyIndex] + // parentName/nodeName.property + // parentName/parentName/nodeName.property[index] + // .bone[Armature.DEF_cog].position + // created and tested via https://regex101.com/#javascript + + var re = /^(([\w]+\/)*)([\w-\d]+)?(\.([\w]+)(\[([\w\d\[\]\_. ]+)\])?)?(\.([\w.]+)(\[([\w\d\[\]\_. ]+)\])?)$/; + var matches = re.exec(trackName); + + if ( ! matches ) { + throw new Error( "cannot parse trackName at all: " + trackName ); + } + + if (matches.index === re.lastIndex) { + re.lastIndex++; + } + + var results = { + directoryName: matches[1], + nodeName: matches[3], // allowed to be null, specified root node. + objectName: matches[5], + objectIndex: matches[7], + propertyName: matches[9], + propertyIndex: matches[11] // allowed to be null, specifies that the whole property is set. + }; + + if ( results.propertyName === null || results.propertyName.length === 0 ) { + throw new Error( "can not parse propertyName from trackName: " + trackName ); + } + + return results; + +}; + +THREE.PropertyBinding.findNode = function( root, nodeName ) { + + function searchSkeleton( skeleton ) { + + for ( var i = 0; i < skeleton.bones.length; i ++ ) { + + var bone = skeleton.bones[i]; + + if ( bone.name === nodeName ) { + + return bone; + + } + } + + return null; + + } + + function searchNodeSubtree( children ) { + + for ( var i = 0; i < children.length; i ++ ) { + + var childNode = children[i]; + + if ( childNode.name === nodeName || childNode.uuid === nodeName ) { + + return childNode; + + } + + var result = searchNodeSubtree( childNode.children ); + + if ( result ) return result; + + } + + return null; + + } + + // + + if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) { + + return root; + + } + + // search into skeleton bones. + if ( root.skeleton ) { + + var bone = searchSkeleton( root.skeleton ); + + if ( bone ) { + + return bone; + + } + } + + // search into node subtree. + if ( root.children ) { + + var subTreeNode = searchNodeSubtree( root.children ); + + if ( subTreeNode ) { + + return subTreeNode; + + } + + } + + return null; +} + +// File:src/animation/tracks/VectorKeyframeTrack.js + +/** + * + * A Track that interpolates Vectors + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.VectorKeyframeTrack = function ( name, keys ) { + + THREE.KeyframeTrack.call( this, name, keys ); + + // local cache of value type to avoid allocations during runtime. + this.result = this.keys[0].value.clone(); + +}; + +THREE.VectorKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype ); + +THREE.VectorKeyframeTrack.prototype.constructor = THREE.VectorKeyframeTrack; + +THREE.VectorKeyframeTrack.prototype.setResult = function( value ) { + + this.result.copy( value ); + +}; + +// memoization of the lerp function for speed. +// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis. +THREE.VectorKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) { + + return value0.lerp( value1, alpha ); + +}; + +THREE.VectorKeyframeTrack.prototype.compareValues = function( value0, value1 ) { + + return value0.equals( value1 ); + +}; + +THREE.VectorKeyframeTrack.prototype.clone = function() { + + var clonedKeys = []; + + for ( var i = 0; i < this.keys.length; i ++ ) { + + var key = this.keys[i]; + clonedKeys.push( { + time: key.time, + value: key.value.clone() + } ); + } + + return new THREE.VectorKeyframeTrack( this.name, clonedKeys ); + +}; + +THREE.VectorKeyframeTrack.parse = function( json ) { + + var elementCount = json.keys[0].value.length; + var valueType = THREE[ 'Vector' + elementCount ]; + + var keys = []; + + for ( var i = 0; i < json.keys.length; i ++ ) { + var jsonKey = json.keys[i]; + keys.push( { + value: new valueType().fromArray( jsonKey.value ), + time: jsonKey.time + } ); + } + + return new THREE.VectorKeyframeTrack( json.name, keys ); + +}; + +// File:src/animation/tracks/QuaternionKeyframeTrack.js + +/** + * + * A Track that interpolates Quaternion + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.QuaternionKeyframeTrack = function ( name, keys ) { + + THREE.KeyframeTrack.call( this, name, keys ); + + // local cache of value type to avoid allocations during runtime. + this.result = this.keys[0].value.clone(); + +}; + +THREE.QuaternionKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype ); + +THREE.QuaternionKeyframeTrack.prototype.constructor = THREE.QuaternionKeyframeTrack; + +THREE.QuaternionKeyframeTrack.prototype.setResult = function( value ) { + + this.result.copy( value ); + +}; + +// memoization of the lerp function for speed. +// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis. +THREE.QuaternionKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) { + + return value0.slerp( value1, alpha ); + +}; + +THREE.QuaternionKeyframeTrack.prototype.compareValues = function( value0, value1 ) { + + return value0.equals( value1 ); + +}; + +THREE.QuaternionKeyframeTrack.prototype.multiply = function( quat ) { + + for ( var i = 0; i < this.keys.length; i ++ ) { + + this.keys[i].value.multiply( quat ); + + } + + return this; + +}; + +THREE.QuaternionKeyframeTrack.prototype.clone = function() { + + var clonedKeys = []; + + for ( var i = 0; i < this.keys.length; i ++ ) { + + var key = this.keys[i]; + clonedKeys.push( { + time: key.time, + value: key.value.clone() + } ); + } + + return new THREE.QuaternionKeyframeTrack( this.name, clonedKeys ); + +}; + +THREE.QuaternionKeyframeTrack.parse = function( json ) { + + var keys = []; + + for ( var i = 0; i < json.keys.length; i ++ ) { + var jsonKey = json.keys[i]; + keys.push( { + value: new THREE.Quaternion().fromArray( jsonKey.value ), + time: jsonKey.time + } ); + } + + return new THREE.QuaternionKeyframeTrack( json.name, keys ); + +}; + +// File:src/animation/tracks/StringKeyframeTrack.js + +/** + * + * A Track that interpolates Strings + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.StringKeyframeTrack = function ( name, keys ) { + + THREE.KeyframeTrack.call( this, name, keys ); + + // local cache of value type to avoid allocations during runtime. + this.result = this.keys[0].value; + +}; + +THREE.StringKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype ); + +THREE.StringKeyframeTrack.prototype.constructor = THREE.StringKeyframeTrack; + +THREE.StringKeyframeTrack.prototype.setResult = function( value ) { + + this.result = value; + +}; + +// memoization of the lerp function for speed. +// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis. +THREE.StringKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) { + + return ( alpha < 1.0 ) ? value0 : value1; + +}; + +THREE.StringKeyframeTrack.prototype.compareValues = function( value0, value1 ) { + + return ( value0 === value1 ); + +}; + +THREE.StringKeyframeTrack.prototype.clone = function() { + + var clonedKeys = []; + + for ( var i = 0; i < this.keys.length; i ++ ) { + + var key = this.keys[i]; + clonedKeys.push( { + time: key.time, + value: key.value + } ); + } + + return new THREE.StringKeyframeTrack( this.name, clonedKeys ); + +}; + +THREE.StringKeyframeTrack.parse = function( json ) { + + return new THREE.StringKeyframeTrack( json.name, json.keys ); + +}; + +// File:src/animation/tracks/BooleanKeyframeTrack.js + +/** + * + * A Track that interpolates Boolean + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.BooleanKeyframeTrack = function ( name, keys ) { + + THREE.KeyframeTrack.call( this, name, keys ); + + // local cache of value type to avoid allocations during runtime. + this.result = this.keys[0].value; + +}; + +THREE.BooleanKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype ); + +THREE.BooleanKeyframeTrack.prototype.constructor = THREE.BooleanKeyframeTrack; + +THREE.BooleanKeyframeTrack.prototype.setResult = function( value ) { + + this.result = value; + +}; + +// memoization of the lerp function for speed. +// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis. +THREE.BooleanKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) { + + return ( alpha < 1.0 ) ? value0 : value1; + +}; + +THREE.BooleanKeyframeTrack.prototype.compareValues = function( value0, value1 ) { + + return ( value0 === value1 ); + +}; + +THREE.BooleanKeyframeTrack.prototype.clone = function() { + + var clonedKeys = []; + + for ( var i = 0; i < this.keys.length; i ++ ) { + + var key = this.keys[i]; + clonedKeys.push( { + time: key.time, + value: key.value + } ); + } + + return new THREE.BooleanKeyframeTrack( this.name, clonedKeys ); + +}; + +THREE.BooleanKeyframeTrack.parse = function( json ) { + + return new THREE.BooleanKeyframeTrack( json.name, json.keys ); + +}; + +// File:src/animation/tracks/NumberKeyframeTrack.js + +/** + * + * A Track that interpolates Numbers + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.NumberKeyframeTrack = function ( name, keys ) { + + THREE.KeyframeTrack.call( this, name, keys ); + + // local cache of value type to avoid allocations during runtime. + this.result = this.keys[0].value; + +}; + +THREE.NumberKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype ); + +THREE.NumberKeyframeTrack.prototype.constructor = THREE.NumberKeyframeTrack; + +THREE.NumberKeyframeTrack.prototype.setResult = function( value ) { + + this.result = value; + +}; + +// memoization of the lerp function for speed. +// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis. +THREE.NumberKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) { + + return value0 * ( 1 - alpha ) + value1 * alpha; + +}; + +THREE.NumberKeyframeTrack.prototype.compareValues = function( value0, value1 ) { + + return ( value0 === value1 ); + +}; + +THREE.NumberKeyframeTrack.prototype.clone = function() { + + var clonedKeys = []; + + for ( var i = 0; i < this.keys.length; i ++ ) { + + var key = this.keys[i]; + clonedKeys.push( { + time: key.time, + value: key.value + } ); + } + + return new THREE.NumberKeyframeTrack( this.name, clonedKeys ); + +}; + +THREE.NumberKeyframeTrack.parse = function( json ) { + + return new THREE.NumberKeyframeTrack( json.name, json.keys ); + +}; + +// File:src/cameras/Camera.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.Camera = function () { + + THREE.Object3D.call( this ); + + this.type = 'Camera'; + + this.matrixWorldInverse = new THREE.Matrix4(); + this.projectionMatrix = new THREE.Matrix4(); + +}; + +THREE.Camera.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Camera.prototype.constructor = THREE.Camera; + +THREE.Camera.prototype.getWorldDirection = function () { + + var quaternion = new THREE.Quaternion(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + this.getWorldQuaternion( quaternion ); + + return result.set( 0, 0, - 1 ).applyQuaternion( quaternion ); + + }; + +}(); + +THREE.Camera.prototype.lookAt = function () { + + // This routine does not support cameras with rotated and/or translated parent(s) + + var m1 = new THREE.Matrix4(); + + return function ( vector ) { + + m1.lookAt( this.position, vector, this.up ); + + this.quaternion.setFromRotationMatrix( m1 ); + + }; + +}(); + +THREE.Camera.prototype.clone = function () { + + return new this.constructor().copy( this ); + +}; + +THREE.Camera.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source ); + + this.matrixWorldInverse.copy( source.matrixWorldInverse ); + this.projectionMatrix.copy( source.projectionMatrix ); + + return this; + +}; + +// File:src/cameras/CubeCamera.js + +/** + * Camera for rendering cube maps + * - renders scene into axis-aligned cube + * + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.CubeCamera = function ( near, far, cubeResolution ) { + + THREE.Object3D.call( this ); + + this.type = 'CubeCamera'; + + var fov = 90, aspect = 1; + + var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPX.up.set( 0, - 1, 0 ); + cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) ); + this.add( cameraPX ); + + var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNX.up.set( 0, - 1, 0 ); + cameraNX.lookAt( new THREE.Vector3( - 1, 0, 0 ) ); + this.add( cameraNX ); + + var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPY.up.set( 0, 0, 1 ); + cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) ); + this.add( cameraPY ); + + var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNY.up.set( 0, 0, - 1 ); + cameraNY.lookAt( new THREE.Vector3( 0, - 1, 0 ) ); + this.add( cameraNY ); + + var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPZ.up.set( 0, - 1, 0 ); + cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) ); + this.add( cameraPZ ); + + var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNZ.up.set( 0, - 1, 0 ); + cameraNZ.lookAt( new THREE.Vector3( 0, 0, - 1 ) ); + this.add( cameraNZ ); + + this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } ); + + this.updateCubeMap = function ( renderer, scene ) { + + if ( this.parent === null ) this.updateMatrixWorld(); + + var renderTarget = this.renderTarget; + var generateMipmaps = renderTarget.texture.generateMipmaps; + + renderTarget.texture.generateMipmaps = false; + + renderTarget.activeCubeFace = 0; + renderer.render( scene, cameraPX, renderTarget ); + + renderTarget.activeCubeFace = 1; + renderer.render( scene, cameraNX, renderTarget ); + + renderTarget.activeCubeFace = 2; + renderer.render( scene, cameraPY, renderTarget ); + + renderTarget.activeCubeFace = 3; + renderer.render( scene, cameraNY, renderTarget ); + + renderTarget.activeCubeFace = 4; + renderer.render( scene, cameraPZ, renderTarget ); + + renderTarget.texture.generateMipmaps = generateMipmaps; + + renderTarget.activeCubeFace = 5; + renderer.render( scene, cameraNZ, renderTarget ); + + renderer.setRenderTarget( null ); + + }; + +}; + +THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype ); +THREE.CubeCamera.prototype.constructor = THREE.CubeCamera; + +// File:src/cameras/OrthographicCamera.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) { + + THREE.Camera.call( this ); + + this.type = 'OrthographicCamera'; + + this.zoom = 1; + + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; + + this.near = ( near !== undefined ) ? near : 0.1; + this.far = ( far !== undefined ) ? far : 2000; + + this.updateProjectionMatrix(); + +}; + +THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype ); +THREE.OrthographicCamera.prototype.constructor = THREE.OrthographicCamera; + +THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () { + + var dx = ( this.right - this.left ) / ( 2 * this.zoom ); + var dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); + var cx = ( this.right + this.left ) / 2; + var cy = ( this.top + this.bottom ) / 2; + + this.projectionMatrix.makeOrthographic( cx - dx, cx + dx, cy + dy, cy - dy, this.near, this.far ); + +}; + +THREE.OrthographicCamera.prototype.copy = function ( source ) { + + THREE.Camera.prototype.copy.call( this, source ); + + this.left = source.left; + this.right = source.right; + this.top = source.top; + this.bottom = source.bottom; + this.near = source.near; + this.far = source.far; + + this.zoom = source.zoom; + + return this; + +}; + +THREE.OrthographicCamera.prototype.toJSON = function ( meta ) { + + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + + data.object.zoom = this.zoom; + data.object.left = this.left; + data.object.right = this.right; + data.object.top = this.top; + data.object.bottom = this.bottom; + data.object.near = this.near; + data.object.far = this.far; + + return data; + +}; + +// File:src/cameras/PerspectiveCamera.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author greggman / http://games.greggman.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.PerspectiveCamera = function ( fov, aspect, near, far ) { + + THREE.Camera.call( this ); + + this.type = 'PerspectiveCamera'; + + this.zoom = 1; + + this.fov = fov !== undefined ? fov : 50; + this.aspect = aspect !== undefined ? aspect : 1; + this.near = near !== undefined ? near : 0.1; + this.far = far !== undefined ? far : 2000; + + this.updateProjectionMatrix(); + +}; + +THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype ); +THREE.PerspectiveCamera.prototype.constructor = THREE.PerspectiveCamera; + + +/** + * Uses Focal Length (in mm) to estimate and set FOV + * 35mm (full-frame) camera is used if frame size is not specified; + * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html + */ + +THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) { + + if ( frameHeight === undefined ) frameHeight = 24; + + this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) ); + this.updateProjectionMatrix(); + +}; + + +/** + * Sets an offset in a larger frustum. This is useful for multi-window or + * multi-monitor/multi-machine setups. + * + * For example, if you have 3x2 monitors and each monitor is 1920x1080 and + * the monitors are in grid like this + * + * +---+---+---+ + * | A | B | C | + * +---+---+---+ + * | D | E | F | + * +---+---+---+ + * + * then for each monitor you would call it like this + * + * var w = 1920; + * var h = 1080; + * var fullWidth = w * 3; + * var fullHeight = h * 2; + * + * --A-- + * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); + * --B-- + * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); + * --C-- + * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); + * --D-- + * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); + * --E-- + * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); + * --F-- + * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); + * + * Note there is no reason monitors have to be the same size or in a grid. + */ + +THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) { + + this.fullWidth = fullWidth; + this.fullHeight = fullHeight; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + + this.updateProjectionMatrix(); + +}; + + +THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () { + + var fov = THREE.Math.radToDeg( 2 * Math.atan( Math.tan( THREE.Math.degToRad( this.fov ) * 0.5 ) / this.zoom ) ); + + if ( this.fullWidth ) { + + var aspect = this.fullWidth / this.fullHeight; + var top = Math.tan( THREE.Math.degToRad( fov * 0.5 ) ) * this.near; + var bottom = - top; + var left = aspect * bottom; + var right = aspect * top; + var width = Math.abs( right - left ); + var height = Math.abs( top - bottom ); + + this.projectionMatrix.makeFrustum( + left + this.x * width / this.fullWidth, + left + ( this.x + this.width ) * width / this.fullWidth, + top - ( this.y + this.height ) * height / this.fullHeight, + top - this.y * height / this.fullHeight, + this.near, + this.far + ); + + } else { + + this.projectionMatrix.makePerspective( fov, this.aspect, this.near, this.far ); + + } + +}; + +THREE.PerspectiveCamera.prototype.copy = function ( source ) { + + THREE.Camera.prototype.copy.call( this, source ); + + this.fov = source.fov; + this.aspect = source.aspect; + this.near = source.near; + this.far = source.far; + + this.zoom = source.zoom; + + return this; + +}; + +THREE.PerspectiveCamera.prototype.toJSON = function ( meta ) { + + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + + data.object.zoom = this.zoom; + data.object.fov = this.fov; + data.object.aspect = this.aspect; + data.object.near = this.near; + data.object.far = this.far; + + return data; + +}; + +// File:src/lights/Light.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Light = function ( color ) { + + THREE.Object3D.call( this ); + + this.type = 'Light'; + + this.color = new THREE.Color( color ); + + this.receiveShadow = undefined; + +}; + +THREE.Light.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Light.prototype.constructor = THREE.Light; + +Object.defineProperties( THREE.Light.prototype, { + onlyShadow: { + set: function ( value ) { + console.warn( 'THREE.Light: .onlyShadow has been removed.' ); + } + }, + shadowCameraFov: { + set: function ( value ) { + this.shadow.camera.fov = value; + } + }, + shadowCameraLeft: { + set: function ( value ) { + this.shadow.camera.left = value; + } + }, + shadowCameraRight: { + set: function ( value ) { + this.shadow.camera.right = value; + } + }, + shadowCameraTop: { + set: function ( value ) { + this.shadow.camera.top = value; + } + }, + shadowCameraBottom: { + set: function ( value ) { + this.shadow.camera.bottom = value; + } + }, + shadowCameraNear: { + set: function ( value ) { + this.shadow.camera.near = value; + } + }, + shadowCameraFar: { + set: function ( value ) { + this.shadow.camera.far = value; + } + }, + shadowCameraVisible: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow ) instead.' ); + } + }, + shadowBias: { + set: function ( value ) { + this.shadow.bias = value; + } + }, + shadowDarkness: { + set: function ( value ) { + this.shadow.darkness = value; + } + }, + shadowMapWidth: { + set: function ( value ) { + this.shadow.mapSize.width = value; + } + }, + shadowMapHeight: { + set: function ( value ) { + this.shadow.mapSize.height = value; + } + } +} ); + +THREE.Light.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + + return this; + +}; + +THREE.Light.prototype.toJSON = function ( meta ) { + + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + + data.object.color = this.color.getHex(); + if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); + + if ( this.intensity !== undefined ) data.object.intensity = this.intensity; + if ( this.distance !== undefined ) data.object.distance = this.distance; + if ( this.angle !== undefined ) data.object.angle = this.angle; + if ( this.decay !== undefined ) data.object.decay = this.decay; + if ( this.exponent !== undefined ) data.object.exponent = this.exponent; + + return data; + +}; + +// File:src/lights/LightShadow.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LightShadow = function ( camera ) { + + this.camera = camera; + + this.bias = 0; + this.darkness = 1; + + this.mapSize = new THREE.Vector2( 512, 512 ); + + this.map = null; + this.matrix = null; + +}; + +THREE.LightShadow.prototype = { + + constructor: THREE.LightShadow, + + copy: function ( source ) { + + this.camera = source.camera.clone(); + + this.bias = source.bias; + this.darkness = source.darkness; + + this.mapSize.copy( source.mapSize ); + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + } + +}; + +// File:src/lights/AmbientLight.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AmbientLight = function ( color ) { + + THREE.Light.call( this, color ); + + this.type = 'AmbientLight'; + + this.castShadow = undefined; + +}; + +THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype ); +THREE.AmbientLight.prototype.constructor = THREE.AmbientLight; + +// File:src/lights/DirectionalLight.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.DirectionalLight = function ( color, intensity ) { + + THREE.Light.call( this, color ); + + this.type = 'DirectionalLight'; + + this.position.set( 0, 1, 0 ); + this.updateMatrix(); + + this.target = new THREE.Object3D(); + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + + this.shadow = new THREE.LightShadow( new THREE.OrthographicCamera( - 500, 500, 500, - 500, 50, 5000 ) ); + +}; + +THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype ); +THREE.DirectionalLight.prototype.constructor = THREE.DirectionalLight; + +THREE.DirectionalLight.prototype.copy = function ( source ) { + + THREE.Light.prototype.copy.call( this, source ); + + this.intensity = source.intensity; + this.target = source.target.clone(); + + this.shadow = source.shadow.clone(); + + return this; + +}; + +// File:src/lights/HemisphereLight.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) { + + THREE.Light.call( this, skyColor ); + + this.type = 'HemisphereLight'; + + this.castShadow = undefined; + + this.position.set( 0, 1, 0 ); + this.updateMatrix(); + + this.groundColor = new THREE.Color( groundColor ); + this.intensity = ( intensity !== undefined ) ? intensity : 1; + +}; + +THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype ); +THREE.HemisphereLight.prototype.constructor = THREE.HemisphereLight; + +THREE.HemisphereLight.prototype.copy = function ( source ) { + + THREE.Light.prototype.copy.call( this, source ); + + this.groundColor.copy( source.groundColor ); + this.intensity = source.intensity; + + return this; + +}; + +// File:src/lights/PointLight.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + + +THREE.PointLight = function ( color, intensity, distance, decay ) { + + THREE.Light.call( this, color ); + + this.type = 'PointLight'; + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + this.distance = ( distance !== undefined ) ? distance : 0; + this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. + + this.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 90, 1, 1, 500 ) ); + +}; + +THREE.PointLight.prototype = Object.create( THREE.Light.prototype ); +THREE.PointLight.prototype.constructor = THREE.PointLight; + +THREE.PointLight.prototype.copy = function ( source ) { + + THREE.Light.prototype.copy.call( this, source ); + + this.intensity = source.intensity; + this.distance = source.distance; + this.decay = source.decay; + + this.shadow = source.shadow.clone(); + + return this; + +}; + +// File:src/lights/SpotLight.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SpotLight = function ( color, intensity, distance, angle, exponent, decay ) { + + THREE.Light.call( this, color ); + + this.type = 'SpotLight'; + + this.position.set( 0, 1, 0 ); + this.updateMatrix(); + + this.target = new THREE.Object3D(); + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + this.distance = ( distance !== undefined ) ? distance : 0; + this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; + this.exponent = ( exponent !== undefined ) ? exponent : 10; + this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. + + this.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 50, 1, 50, 5000 ) ); + +}; + +THREE.SpotLight.prototype = Object.create( THREE.Light.prototype ); +THREE.SpotLight.prototype.constructor = THREE.SpotLight; + +THREE.SpotLight.prototype.copy = function ( source ) { + + THREE.Light.prototype.copy.call( this, source ); + + this.intensity = source.intensity; + this.distance = source.distance; + this.angle = source.angle; + this.exponent = source.exponent; + this.decay = source.decay; + + this.target = source.target.clone(); + + this.shadow = source.shadow.clone(); + + return this; + +}; + +// File:src/loaders/Cache.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Cache = { + + enabled: false, + + files: {}, + + add: function ( key, file ) { + + if ( this.enabled === false ) return; + + // console.log( 'THREE.Cache', 'Adding key:', key ); + + this.files[ key ] = file; + + }, + + get: function ( key ) { + + if ( this.enabled === false ) return; + + // console.log( 'THREE.Cache', 'Checking key:', key ); + + return this.files[ key ]; + + }, + + remove: function ( key ) { + + delete this.files[ key ]; + + }, + + clear: function () { + + this.files = {}; + + } + +}; + +// File:src/loaders/Loader.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Loader = function () { + + this.onLoadStart = function () {}; + this.onLoadProgress = function () {}; + this.onLoadComplete = function () {}; + +}; + +THREE.Loader.prototype = { + + constructor: THREE.Loader, + + crossOrigin: undefined, + + extractUrlBase: function ( url ) { + + var parts = url.split( '/' ); + + if ( parts.length === 1 ) return './'; + + parts.pop(); + + return parts.join( '/' ) + '/'; + + }, + + initMaterials: function ( materials, texturePath, crossOrigin ) { + + var array = []; + + for ( var i = 0; i < materials.length; ++ i ) { + + array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); + + } + + return array; + + }, + + createMaterial: ( function () { + + var color, textureLoader, materialLoader; + + return function ( m, texturePath, crossOrigin ) { + + if ( color === undefined ) color = new THREE.Color(); + if ( textureLoader === undefined ) textureLoader = new THREE.TextureLoader(); + if ( materialLoader === undefined ) materialLoader = new THREE.MaterialLoader(); + + // convert from old material format + + var textures = {}; + + function loadTexture( path, repeat, offset, wrap, anisotropy ) { + + var fullPath = texturePath + path; + var loader = THREE.Loader.Handlers.get( fullPath ); + + var texture; + + if ( loader !== null ) { + + texture = loader.load( fullPath ); + + } else { + + textureLoader.setCrossOrigin( crossOrigin ); + texture = textureLoader.load( fullPath ); + + } + + if ( repeat !== undefined ) { + + texture.repeat.fromArray( repeat ); + + if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping; + if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping; + + } + + if ( offset !== undefined ) { + + texture.offset.fromArray( offset ); + + } + + if ( wrap !== undefined ) { + + if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = THREE.RepeatWrapping; + if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = THREE.MirroredRepeatWrapping; + + if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = THREE.RepeatWrapping; + if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = THREE.MirroredRepeatWrapping; + + } + + if ( anisotropy !== undefined ) { + + texture.anisotropy = anisotropy; + + } + + var uuid = THREE.Math.generateUUID(); + + textures[ uuid ] = texture; + + return uuid; + + } + + // + + var json = { + uuid: THREE.Math.generateUUID(), + type: 'MeshLambertMaterial' + }; + + for ( var name in m ) { + + var value = m[ name ]; + + switch ( name ) { + case 'DbgColor': + json.color = value; + break; + case 'DbgIndex': + case 'opticalDensity': + case 'illumination': + // These were never supported + break; + case 'DbgName': + json.name = value; + break; + case 'blending': + json.blending = THREE[ value ]; + break; + case 'colorDiffuse': + json.color = color.fromArray( value ).getHex(); + break; + case 'colorSpecular': + json.specular = color.fromArray( value ).getHex(); + break; + case 'colorEmissive': + json.emissive = color.fromArray( value ).getHex(); + break; + case 'specularCoef': + json.shininess = value; + break; + case 'shading': + if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial'; + if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial'; + break; + case 'mapDiffuse': + json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); + break; + case 'mapDiffuseRepeat': + case 'mapDiffuseOffset': + case 'mapDiffuseWrap': + case 'mapDiffuseAnisotropy': + break; + case 'mapLight': + json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); + break; + case 'mapLightRepeat': + case 'mapLightOffset': + case 'mapLightWrap': + case 'mapLightAnisotropy': + break; + case 'mapAO': + json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy ); + break; + case 'mapAORepeat': + case 'mapAOOffset': + case 'mapAOWrap': + case 'mapAOAnisotropy': + break; + case 'mapBump': + json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); + break; + case 'mapBumpScale': + json.bumpScale = value; + break; + case 'mapBumpRepeat': + case 'mapBumpOffset': + case 'mapBumpWrap': + case 'mapBumpAnisotropy': + break; + case 'mapNormal': + json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); + break; + case 'mapNormalFactor': + json.normalScale = [ value, value ]; + break; + case 'mapNormalRepeat': + case 'mapNormalOffset': + case 'mapNormalWrap': + case 'mapNormalAnisotropy': + break; + case 'mapSpecular': + json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); + break; + case 'mapSpecularRepeat': + case 'mapSpecularOffset': + case 'mapSpecularWrap': + case 'mapSpecularAnisotropy': + break; + case 'mapAlpha': + json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); + break; + case 'mapAlphaRepeat': + case 'mapAlphaOffset': + case 'mapAlphaWrap': + case 'mapAlphaAnisotropy': + break; + case 'flipSided': + json.side = THREE.BackSide; + break; + case 'doubleSided': + json.side = THREE.DoubleSide; + break; + case 'transparency': + console.warn( 'THREE.Loader: transparency has been renamed to opacity' ); + json.opacity = value; + break; + case 'opacity': + case 'transparent': + case 'depthTest': + case 'depthWrite': + case 'transparent': + case 'visible': + case 'wireframe': + json[ name ] = value; + break; + case 'vertexColors': + if ( value === true ) json.vertexColors = THREE.VertexColors; + if ( value === 'face' ) json.vertexColors = THREE.FaceColors; + break; + default: + console.error( 'Loader.createMaterial: Unsupported', name, value ); + break; + } + + } + + if ( json.type !== 'MeshPhongMaterial' ) delete json.specular; + if ( json.opacity < 1 ) json.transparent = true; + + materialLoader.setTextures( textures ); + + return materialLoader.parse( json ); + + }; + + } )() + +}; + +THREE.Loader.Handlers = { + + handlers: [], + + add: function ( regex, loader ) { + + this.handlers.push( regex, loader ); + + }, + + get: function ( file ) { + + var handlers = this.handlers; + + for ( var i = 0, l = handlers.length; i < l; i += 2 ) { + + var regex = handlers[ i ]; + var loader = handlers[ i + 1 ]; + + if ( regex.test( file ) ) { + + return loader; + + } + + } + + return null; + + } + +}; + +// File:src/loaders/XHRLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.XHRLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.XHRLoader.prototype = { + + constructor: THREE.XHRLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var cached = THREE.Cache.get( url ); + + if ( cached !== undefined ) { + + if ( onLoad ) { + + setTimeout( function () { + + onLoad( cached ); + + }, 0 ); + + } + + return cached; + + } + + var request = new XMLHttpRequest(); + request.open( 'GET', url, true ); + + request.addEventListener( 'load', function ( event ) { + + var response = event.target.response; + + THREE.Cache.add( url, response ); + + if ( onLoad ) onLoad( response ); + + scope.manager.itemEnd( url ); + + }, false ); + + if ( onProgress !== undefined ) { + + request.addEventListener( 'progress', function ( event ) { + + onProgress( event ); + + }, false ); + + } + + request.addEventListener( 'error', function ( event ) { + + if ( onError ) onError( event ); + + scope.manager.itemError( url ); + + }, false ); + + if ( this.crossOrigin !== undefined ) request.crossOrigin = this.crossOrigin; + if ( this.responseType !== undefined ) request.responseType = this.responseType; + if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; + + request.send( null ); + + scope.manager.itemStart( url ); + + return request; + + }, + + setResponseType: function ( value ) { + + this.responseType = value; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setWithCredentials: function ( value ) { + + this.withCredentials = value; + + } + +}; + +// File:src/loaders/ImageLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.ImageLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.ImageLoader.prototype = { + + constructor: THREE.ImageLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var cached = THREE.Cache.get( url ); + + if ( cached !== undefined ) { + + scope.manager.itemStart( url ); + + if ( onLoad ) { + + setTimeout( function () { + + onLoad( cached ); + + scope.manager.itemEnd( url ); + + }, 0 ); + + } else { + + scope.manager.itemEnd( url ); + + } + + return cached; + + } + + var image = document.createElement( 'img' ); + + image.addEventListener( 'load', function ( event ) { + + THREE.Cache.add( url, this ); + + if ( onLoad ) onLoad( this ); + + scope.manager.itemEnd( url ); + + }, false ); + + if ( onProgress !== undefined ) { + + image.addEventListener( 'progress', function ( event ) { + + onProgress( event ); + + }, false ); + + } + + image.addEventListener( 'error', function ( event ) { + + if ( onError ) onError( event ); + + scope.manager.itemError( url ); + + }, false ); + + if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; + + scope.manager.itemStart( url ); + + image.src = url; + + return image; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; + +// File:src/loaders/JSONLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.JSONLoader = function ( manager ) { + + if ( typeof manager === 'boolean' ) { + + console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' ); + manager = undefined; + + } + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + this.withCredentials = false; + +}; + +THREE.JSONLoader.prototype = { + + constructor: THREE.JSONLoader, + + // Deprecated + + get statusDomElement () { + + if ( this._statusDomElement === undefined ) { + + this._statusDomElement = document.createElement( 'div' ); + + } + + console.warn( 'THREE.JSONLoader: .statusDomElement has been removed.' ); + return this._statusDomElement; + + }, + + load: function( url, onLoad, onProgress, onError ) { + + var scope = this; + + var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : THREE.Loader.prototype.extractUrlBase( url ); + + var loader = new THREE.XHRLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setWithCredentials( this.withCredentials ); + loader.load( url, function ( text ) { + + var json = JSON.parse( text ); + var metadata = json.metadata; + + if ( metadata !== undefined ) { + + if ( metadata.type === 'object' ) { + + console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' ); + return; + + } + + if ( metadata.type === 'scene' ) { + + console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' ); + return; + + } + + } + + var object = scope.parse( json, texturePath ); + onLoad( object.geometry, object.materials ); + + } ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setTexturePath: function ( value ) { + + this.texturePath = value; + + }, + + parse: function ( json, texturePath ) { + + var geometry = new THREE.Geometry(), + scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; + + parseModel( scale ); + + parseSkin(); + parseMorphing( scale ); + parseAnimations(); + + geometry.computeFaceNormals(); + geometry.computeBoundingSphere(); + + function parseModel( scale ) { + + function isBitSet( value, position ) { + + return value & ( 1 << position ); + + } + + var i, j, fi, + + offset, zLength, + + colorIndex, normalIndex, uvIndex, materialIndex, + + type, + isQuad, + hasMaterial, + hasFaceVertexUv, + hasFaceNormal, hasFaceVertexNormal, + hasFaceColor, hasFaceVertexColor, + + vertex, face, faceA, faceB, hex, normal, + + uvLayer, uv, u, v, + + faces = json.faces, + vertices = json.vertices, + normals = json.normals, + colors = json.colors, + + nUvLayers = 0; + + if ( json.uvs !== undefined ) { + + // disregard empty arrays + + for ( i = 0; i < json.uvs.length; i ++ ) { + + if ( json.uvs[ i ].length ) nUvLayers ++; + + } + + for ( i = 0; i < nUvLayers; i ++ ) { + + geometry.faceVertexUvs[ i ] = []; + + } + + } + + offset = 0; + zLength = vertices.length; + + while ( offset < zLength ) { + + vertex = new THREE.Vector3(); + + vertex.x = vertices[ offset ++ ] * scale; + vertex.y = vertices[ offset ++ ] * scale; + vertex.z = vertices[ offset ++ ] * scale; + + geometry.vertices.push( vertex ); + + } + + offset = 0; + zLength = faces.length; + + while ( offset < zLength ) { + + type = faces[ offset ++ ]; + + + isQuad = isBitSet( type, 0 ); + hasMaterial = isBitSet( type, 1 ); + hasFaceVertexUv = isBitSet( type, 3 ); + hasFaceNormal = isBitSet( type, 4 ); + hasFaceVertexNormal = isBitSet( type, 5 ); + hasFaceColor = isBitSet( type, 6 ); + hasFaceVertexColor = isBitSet( type, 7 ); + + // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); + + if ( isQuad ) { + + faceA = new THREE.Face3(); + faceA.a = faces[ offset ]; + faceA.b = faces[ offset + 1 ]; + faceA.c = faces[ offset + 3 ]; + + faceB = new THREE.Face3(); + faceB.a = faces[ offset + 1 ]; + faceB.b = faces[ offset + 2 ]; + faceB.c = faces[ offset + 3 ]; + + offset += 4; + + if ( hasMaterial ) { + + materialIndex = faces[ offset ++ ]; + faceA.materialIndex = materialIndex; + faceB.materialIndex = materialIndex; + + } + + // to get face <=> uv index correspondence + + fi = geometry.faces.length; + + if ( hasFaceVertexUv ) { + + for ( i = 0; i < nUvLayers; i ++ ) { + + uvLayer = json.uvs[ i ]; + + geometry.faceVertexUvs[ i ][ fi ] = []; + geometry.faceVertexUvs[ i ][ fi + 1 ] = []; + + for ( j = 0; j < 4; j ++ ) { + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + uv = new THREE.Vector2( u, v ); + + if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); + if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); + + } + + } + + } + + if ( hasFaceNormal ) { + + normalIndex = faces[ offset ++ ] * 3; + + faceA.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + faceB.normal.copy( faceA.normal ); + + } + + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < 4; i ++ ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + + if ( i !== 2 ) faceA.vertexNormals.push( normal ); + if ( i !== 0 ) faceB.vertexNormals.push( normal ); + + } + + } + + + if ( hasFaceColor ) { + + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; + + faceA.color.setHex( hex ); + faceB.color.setHex( hex ); + + } + + + if ( hasFaceVertexColor ) { + + for ( i = 0; i < 4; i ++ ) { + + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; + + if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) ); + if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) ); + + } + + } + + geometry.faces.push( faceA ); + geometry.faces.push( faceB ); + + } else { + + face = new THREE.Face3(); + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; + + if ( hasMaterial ) { + + materialIndex = faces[ offset ++ ]; + face.materialIndex = materialIndex; + + } + + // to get face <=> uv index correspondence + + fi = geometry.faces.length; + + if ( hasFaceVertexUv ) { + + for ( i = 0; i < nUvLayers; i ++ ) { + + uvLayer = json.uvs[ i ]; + + geometry.faceVertexUvs[ i ][ fi ] = []; + + for ( j = 0; j < 3; j ++ ) { + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + uv = new THREE.Vector2( u, v ); + + geometry.faceVertexUvs[ i ][ fi ].push( uv ); + + } + + } + + } + + if ( hasFaceNormal ) { + + normalIndex = faces[ offset ++ ] * 3; + + face.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + } + + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < 3; i ++ ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + face.vertexNormals.push( normal ); + + } + + } + + + if ( hasFaceColor ) { + + colorIndex = faces[ offset ++ ]; + face.color.setHex( colors[ colorIndex ] ); + + } + + + if ( hasFaceVertexColor ) { + + for ( i = 0; i < 3; i ++ ) { + + colorIndex = faces[ offset ++ ]; + face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) ); + + } + + } + + geometry.faces.push( face ); + + } + + } + + }; + + function parseSkin() { + + var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; + + if ( json.skinWeights ) { + + for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { + + var x = json.skinWeights[ i ]; + var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; + var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; + var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; + + geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) ); + + } + + } + + if ( json.skinIndices ) { + + for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { + + var a = json.skinIndices[ i ]; + var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; + var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; + var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; + + geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) ); + + } + + } + + geometry.bones = json.bones; + + if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { + + console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + + geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); + + } + + }; + + function parseMorphing( scale ) { + + if ( json.morphTargets !== undefined ) { + + for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) { + + geometry.morphTargets[ i ] = {}; + geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; + geometry.morphTargets[ i ].vertices = []; + + var dstVertices = geometry.morphTargets[ i ].vertices; + var srcVertices = json.morphTargets[ i ].vertices; + + for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) { + + var vertex = new THREE.Vector3(); + vertex.x = srcVertices[ v ] * scale; + vertex.y = srcVertices[ v + 1 ] * scale; + vertex.z = srcVertices[ v + 2 ] * scale; + + dstVertices.push( vertex ); + + } + + } + + } + + if ( json.morphColors !== undefined && json.morphColors.length > 0 ) { + + console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' ); + + var faces = geometry.faces; + var morphColors = json.morphColors[ 0 ].colors; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + faces[ i ].color.fromArray( morphColors, i * 3 ); + + } + + } + + } + + function parseAnimations() { + + var outputAnimations = []; + + // parse old style Bone/Hierarchy animations + var animations = []; + if ( json.animation !== undefined ) { + animations.push( json.animation ); + } + if ( json.animations !== undefined ) { + if ( json.animations.length ) { + animations = animations.concat( json.animations ); + } else { + animations.push( json.animations ); + } + } + + for ( var i = 0; i < animations.length; i ++ ) { + + var clip = THREE.AnimationClip.parseAnimation( animations[i], geometry.bones ); + if ( clip ) outputAnimations.push( clip ); + + } + + // parse implicit morph animations + if ( geometry.morphTargets ) { + + // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary. + var morphAnimationClips = THREE.AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ); + outputAnimations = outputAnimations.concat( morphAnimationClips ); + + } + + if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations; + + }; + + if ( json.materials === undefined || json.materials.length === 0 ) { + + return { geometry: geometry }; + + } else { + + var materials = THREE.Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); + + return { geometry: geometry, materials: materials }; + + } + + } + +}; + +// File:src/loaders/LoadingManager.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LoadingManager = function ( onLoad, onProgress, onError ) { + + var scope = this; + + var isLoading = false, itemsLoaded = 0, itemsTotal = 0; + + this.onStart = undefined; + this.onLoad = onLoad; + this.onProgress = onProgress; + this.onError = onError; + + this.itemStart = function ( url ) { + + itemsTotal ++; + + if ( isLoading === false ) { + + if ( scope.onStart !== undefined ) { + + scope.onStart( url, itemsLoaded, itemsTotal ); + + } + + } + + isLoading = true; + + }; + + this.itemEnd = function ( url ) { + + itemsLoaded ++; + + if ( scope.onProgress !== undefined ) { + + scope.onProgress( url, itemsLoaded, itemsTotal ); + + } + + if ( itemsLoaded === itemsTotal ) { + + isLoading = false; + + if ( scope.onLoad !== undefined ) { + + scope.onLoad(); + + } + + } + + }; + + this.itemError = function ( url ) { + + if ( scope.onError !== undefined ) { + + scope.onError( url ); + + } + + }; + +}; + +THREE.DefaultLoadingManager = new THREE.LoadingManager(); + +// File:src/loaders/BufferGeometryLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BufferGeometryLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.BufferGeometryLoader.prototype = { + + constructor: THREE.BufferGeometryLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( json ) { + + var geometry = new THREE.BufferGeometry(); + + var index = json.data.index; + + if ( index !== undefined ) { + + var typedArray = new self[ index.type ]( index.array ); + geometry.setIndex( new THREE.BufferAttribute( typedArray, 1 ) ); + + } + + var attributes = json.data.attributes; + + for ( var key in attributes ) { + + var attribute = attributes[ key ]; + var typedArray = new self[ attribute.type ]( attribute.array ); + + geometry.addAttribute( key, new THREE.BufferAttribute( typedArray, attribute.itemSize ) ); + + } + + var groups = json.data.groups || json.data.drawcalls || json.data.offsets; + + if ( groups !== undefined ) { + + for ( var i = 0, n = groups.length; i !== n; ++ i ) { + + var group = groups[ i ]; + + geometry.addGroup( group.start, group.count ); + + } + + } + + var boundingSphere = json.data.boundingSphere; + + if ( boundingSphere !== undefined ) { + + var center = new THREE.Vector3(); + + if ( boundingSphere.center !== undefined ) { + + center.fromArray( boundingSphere.center ); + + } + + geometry.boundingSphere = new THREE.Sphere( center, boundingSphere.radius ); + + } + + return geometry; + + } + +}; + +// File:src/loaders/MaterialLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.MaterialLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + this.textures = {}; + +}; + +THREE.MaterialLoader.prototype = { + + constructor: THREE.MaterialLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setTextures: function ( value ) { + + this.textures = value; + + }, + + getTexture: function ( name ) { + + var textures = this.textures; + + if ( textures[ name ] === undefined ) { + + console.warn( 'THREE.MaterialLoader: Undefined texture', name ); + + } + + return textures[ name ]; + + }, + + parse: function ( json ) { + + var material = new THREE[ json.type ]; + material.uuid = json.uuid; + + if ( json.name !== undefined ) material.name = json.name; + if ( json.color !== undefined ) material.color.setHex( json.color ); + if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); + if ( json.specular !== undefined ) material.specular.setHex( json.specular ); + if ( json.shininess !== undefined ) material.shininess = json.shininess; + if ( json.uniforms !== undefined ) material.uniforms = json.uniforms; + if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; + if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; + if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; + if ( json.shading !== undefined ) material.shading = json.shading; + if ( json.blending !== undefined ) material.blending = json.blending; + if ( json.side !== undefined ) material.side = json.side; + if ( json.opacity !== undefined ) material.opacity = json.opacity; + if ( json.transparent !== undefined ) material.transparent = json.transparent; + if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; + if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; + if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; + if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; + if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; + + // for PointsMaterial + if ( json.size !== undefined ) material.size = json.size; + if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; + + // maps + + if ( json.map !== undefined ) material.map = this.getTexture( json.map ); + + if ( json.alphaMap !== undefined ) { + + material.alphaMap = this.getTexture( json.alphaMap ); + material.transparent = true; + + } + + if ( json.bumpMap !== undefined ) material.bumpMap = this.getTexture( json.bumpMap ); + if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; + + if ( json.normalMap !== undefined ) material.normalMap = this.getTexture( json.normalMap ); + if ( json.normalScale ) material.normalScale = new THREE.Vector2( json.normalScale, json.normalScale ); + + if ( json.displacementMap !== undefined ) material.displacementMap = this.getTexture( json.displacementMap ); + if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; + if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; + + if ( json.specularMap !== undefined ) material.specularMap = this.getTexture( json.specularMap ); + + if ( json.envMap !== undefined ) { + + material.envMap = this.getTexture( json.envMap ); + material.combine = THREE.MultiplyOperation; + + } + + if ( json.reflectivity ) material.reflectivity = json.reflectivity; + + if ( json.lightMap !== undefined ) material.lightMap = this.getTexture( json.lightMap ); + if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; + + if ( json.aoMap !== undefined ) material.aoMap = this.getTexture( json.aoMap ); + if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; + + // MeshFaceMaterial + + if ( json.materials !== undefined ) { + + for ( var i = 0, l = json.materials.length; i < l; i ++ ) { + + material.materials.push( this.parse( json.materials[ i ] ) ); + + } + + } + + return material; + + } + +}; + +// File:src/loaders/ObjectLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.ObjectLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + this.texturePath = ''; + +}; + +THREE.ObjectLoader.prototype = { + + constructor: THREE.ObjectLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + if ( this.texturePath === '' ) { + + this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 ); + + } + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + scope.parse( JSON.parse( text ), onLoad ); + + }, onProgress, onError ); + + }, + + setTexturePath: function ( value ) { + + this.texturePath = value; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( json, onLoad ) { + + var geometries = this.parseGeometries( json.geometries ); + + var images = this.parseImages( json.images, function () { + + if ( onLoad !== undefined ) onLoad( object ); + + } ); + + var textures = this.parseTextures( json.textures, images ); + var materials = this.parseMaterials( json.materials, textures ); + + var object = this.parseObject( json.object, geometries, materials ); + + if ( json.animations ) { + + object.animations = this.parseAnimations( json.animations ); + + } + + if ( json.images === undefined || json.images.length === 0 ) { + + if ( onLoad !== undefined ) onLoad( object ); + + } + + return object; + + }, + + parseGeometries: function ( json ) { + + var geometries = {}; + + if ( json !== undefined ) { + + var geometryLoader = new THREE.JSONLoader(); + var bufferGeometryLoader = new THREE.BufferGeometryLoader(); + + for ( var i = 0, l = json.length; i < l; i ++ ) { + + var geometry; + var data = json[ i ]; + + switch ( data.type ) { + + case 'PlaneGeometry': + case 'PlaneBufferGeometry': + + geometry = new THREE[ data.type ]( + data.width, + data.height, + data.widthSegments, + data.heightSegments + ); + + break; + + case 'BoxGeometry': + case 'CubeGeometry': // backwards compatible + + geometry = new THREE.BoxGeometry( + data.width, + data.height, + data.depth, + data.widthSegments, + data.heightSegments, + data.depthSegments + ); + + break; + + case 'CircleBufferGeometry': + + geometry = new THREE.CircleBufferGeometry( + data.radius, + data.segments, + data.thetaStart, + data.thetaLength + ); + + break; + + case 'CircleGeometry': + + geometry = new THREE.CircleGeometry( + data.radius, + data.segments, + data.thetaStart, + data.thetaLength + ); + + break; + + case 'CylinderGeometry': + + geometry = new THREE.CylinderGeometry( + data.radiusTop, + data.radiusBottom, + data.height, + data.radialSegments, + data.heightSegments, + data.openEnded, + data.thetaStart, + data.thetaLength + ); + + break; + + case 'SphereGeometry': + + geometry = new THREE.SphereGeometry( + data.radius, + data.widthSegments, + data.heightSegments, + data.phiStart, + data.phiLength, + data.thetaStart, + data.thetaLength + ); + + break; + + case 'SphereBufferGeometry': + + geometry = new THREE.SphereBufferGeometry( + data.radius, + data.widthSegments, + data.heightSegments, + data.phiStart, + data.phiLength, + data.thetaStart, + data.thetaLength + ); + + break; + + case 'DodecahedronGeometry': + + geometry = new THREE.DodecahedronGeometry( + data.radius, + data.detail + ); + + break; + + case 'IcosahedronGeometry': + + geometry = new THREE.IcosahedronGeometry( + data.radius, + data.detail + ); + + break; + + case 'OctahedronGeometry': + + geometry = new THREE.OctahedronGeometry( + data.radius, + data.detail + ); + + break; + + case 'TetrahedronGeometry': + + geometry = new THREE.TetrahedronGeometry( + data.radius, + data.detail + ); + + break; + + case 'RingGeometry': + + geometry = new THREE.RingGeometry( + data.innerRadius, + data.outerRadius, + data.thetaSegments, + data.phiSegments, + data.thetaStart, + data.thetaLength + ); + + break; + + case 'TorusGeometry': + + geometry = new THREE.TorusGeometry( + data.radius, + data.tube, + data.radialSegments, + data.tubularSegments, + data.arc + ); + + break; + + case 'TorusKnotGeometry': + + geometry = new THREE.TorusKnotGeometry( + data.radius, + data.tube, + data.radialSegments, + data.tubularSegments, + data.p, + data.q, + data.heightScale + ); + + break; + + case 'BufferGeometry': + + geometry = bufferGeometryLoader.parse( data ); + + break; + + case 'Geometry': + + geometry = geometryLoader.parse( data.data, this.texturePath ).geometry; + + break; + + default: + + console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); + + continue; + + } + + geometry.uuid = data.uuid; + + if ( data.name !== undefined ) geometry.name = data.name; + + geometries[ data.uuid ] = geometry; + + } + + } + + return geometries; + + }, + + parseMaterials: function ( json, textures ) { + + var materials = {}; + + if ( json !== undefined ) { + + var loader = new THREE.MaterialLoader(); + loader.setTextures( textures ); + + for ( var i = 0, l = json.length; i < l; i ++ ) { + + var material = loader.parse( json[ i ] ); + materials[ material.uuid ] = material; + + } + + } + + return materials; + + }, + + parseAnimations: function ( json ) { + + var animations = []; + + for ( var i = 0; i < json.length; i ++ ) { + + var clip = THREE.AnimationClip.parse( json[i] ); + + animations.push( clip ); + + } + + return animations; + + }, + + parseImages: function ( json, onLoad ) { + + var scope = this; + var images = {}; + + function loadImage( url ) { + + scope.manager.itemStart( url ); + + return loader.load( url, function () { + + scope.manager.itemEnd( url ); + + } ); + + } + + if ( json !== undefined && json.length > 0 ) { + + var manager = new THREE.LoadingManager( onLoad ); + + var loader = new THREE.ImageLoader( manager ); + loader.setCrossOrigin( this.crossOrigin ); + + for ( var i = 0, l = json.length; i < l; i ++ ) { + + var image = json[ i ]; + var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url; + + images[ image.uuid ] = loadImage( path ); + + } + + } + + return images; + + }, + + parseTextures: function ( json, images ) { + + function parseConstant( value ) { + + if ( typeof( value ) === 'number' ) return value; + + console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); + + return THREE[ value ]; + + } + + var textures = {}; + + if ( json !== undefined ) { + + for ( var i = 0, l = json.length; i < l; i ++ ) { + + var data = json[ i ]; + + if ( data.image === undefined ) { + + console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); + + } + + if ( images[ data.image ] === undefined ) { + + console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); + + } + + var texture = new THREE.Texture( images[ data.image ] ); + texture.needsUpdate = true; + + texture.uuid = data.uuid; + + if ( data.name !== undefined ) texture.name = data.name; + if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping ); + if ( data.offset !== undefined ) texture.offset = new THREE.Vector2( data.offset[ 0 ], data.offset[ 1 ] ); + if ( data.repeat !== undefined ) texture.repeat = new THREE.Vector2( data.repeat[ 0 ], data.repeat[ 1 ] ); + if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter ); + if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter ); + if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; + if ( Array.isArray( data.wrap ) ) { + + texture.wrapS = parseConstant( data.wrap[ 0 ] ); + texture.wrapT = parseConstant( data.wrap[ 1 ] ); + + } + + textures[ data.uuid ] = texture; + + } + + } + + return textures; + + }, + + parseObject: function () { + + var matrix = new THREE.Matrix4(); + + return function ( data, geometries, materials ) { + + var object; + + function getGeometry( name ) { + + if ( geometries[ name ] === undefined ) { + + console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); + + } + + return geometries[ name ]; + + } + + function getMaterial( name ) { + + if ( name === undefined ) return undefined; + + if ( materials[ name ] === undefined ) { + + console.warn( 'THREE.ObjectLoader: Undefined material', name ); + + } + + return materials[ name ]; + + } + + switch ( data.type ) { + + case 'Scene': + + object = new THREE.Scene(); + + break; + + case 'PerspectiveCamera': + + object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); + + break; + + case 'OrthographicCamera': + + object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); + + break; + + case 'AmbientLight': + + object = new THREE.AmbientLight( data.color ); + + break; + + case 'DirectionalLight': + + object = new THREE.DirectionalLight( data.color, data.intensity ); + + break; + + case 'PointLight': + + object = new THREE.PointLight( data.color, data.intensity, data.distance, data.decay ); + + break; + + case 'SpotLight': + + object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent, data.decay ); + + break; + + case 'HemisphereLight': + + object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity ); + + break; + + case 'Mesh': + + object = new THREE.Mesh( getGeometry( data.geometry ), getMaterial( data.material ) ); + + break; + + case 'LOD': + + object = new THREE.LOD(); + + break; + + case 'Line': + + object = new THREE.Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); + + break; + + case 'PointCloud': + case 'Points': + + object = new THREE.Points( getGeometry( data.geometry ), getMaterial( data.material ) ); + + break; + + case 'Sprite': + + object = new THREE.Sprite( getMaterial( data.material ) ); + + break; + + case 'Group': + + object = new THREE.Group(); + + break; + + default: + + object = new THREE.Object3D(); + + } + + object.uuid = data.uuid; + + if ( data.name !== undefined ) object.name = data.name; + if ( data.matrix !== undefined ) { + + matrix.fromArray( data.matrix ); + matrix.decompose( object.position, object.quaternion, object.scale ); + + } else { + + if ( data.position !== undefined ) object.position.fromArray( data.position ); + if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); + if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); + + } + + if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; + if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; + + if ( data.visible !== undefined ) object.visible = data.visible; + if ( data.userData !== undefined ) object.userData = data.userData; + + if ( data.children !== undefined ) { + + for ( var child in data.children ) { + + object.add( this.parseObject( data.children[ child ], geometries, materials ) ); + + } + + } + + if ( data.type === 'LOD' ) { + + var levels = data.levels; + + for ( var l = 0; l < levels.length; l ++ ) { + + var level = levels[ l ]; + var child = object.getObjectByProperty( 'uuid', level.object ); + + if ( child !== undefined ) { + + object.addLevel( child, level.distance ); + + } + + } + + } + + return object; + + } + + }() + +}; + +// File:src/loaders/TextureLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.TextureLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.TextureLoader.prototype = { + + constructor: THREE.TextureLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var texture = new THREE.Texture(); + + var loader = new THREE.ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( image ) { + + texture.image = image; + texture.needsUpdate = true; + + if ( onLoad !== undefined ) { + + onLoad( texture ); + + } + + }, onProgress, onError ); + + return texture; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; + +// File:src/loaders/CubeTextureLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CubeTextureLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.CubeTextureLoader.prototype = { + + constructor: THREE.CubeTextureLoader, + + load: function ( urls, onLoad, onProgress, onError ) { + + var texture = new THREE.CubeTexture( [] ); + + var loader = new THREE.ImageLoader(); + loader.setCrossOrigin( this.crossOrigin ); + + var loaded = 0; + + function loadTexture( i ) { + + loader.load( urls[ i ], function ( image ) { + + texture.images[ i ] = image; + + loaded ++; + + if ( loaded === 6 ) { + + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + } + + }, undefined, onError ); + + } + + for ( var i = 0; i < urls.length; ++ i ) { + + loadTexture( i ); + + } + + return texture; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; + +// File:src/loaders/BinaryTextureLoader.js + +/** + * @author Nikos M. / https://github.com/foo123/ + * + * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) + */ + +THREE.DataTextureLoader = THREE.BinaryTextureLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + // override in sub classes + this._parser = null; + +}; + +THREE.BinaryTextureLoader.prototype = { + + constructor: THREE.BinaryTextureLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var texture = new THREE.DataTexture(); + + var loader = new THREE.XHRLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setResponseType( 'arraybuffer' ); + + loader.load( url, function ( buffer ) { + + var texData = scope._parser( buffer ); + + if ( ! texData ) return; + + if ( undefined !== texData.image ) { + + texture.image = texData.image; + + } else if ( undefined !== texData.data ) { + + texture.image.width = texData.width; + texture.image.height = texData.height; + texture.image.data = texData.data; + + } + + texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : THREE.ClampToEdgeWrapping; + texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : THREE.ClampToEdgeWrapping; + + texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : THREE.LinearFilter; + texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : THREE.LinearMipMapLinearFilter; + + texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1; + + if ( undefined !== texData.format ) { + + texture.format = texData.format; + + } + if ( undefined !== texData.type ) { + + texture.type = texData.type; + + } + + if ( undefined !== texData.mipmaps ) { + + texture.mipmaps = texData.mipmaps; + + } + + if ( 1 === texData.mipmapCount ) { + + texture.minFilter = THREE.LinearFilter; + + } + + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture, texData ); + + }, onProgress, onError ); + + + return texture; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; + +// File:src/loaders/CompressedTextureLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + * + * Abstract Base class to block based textures loader (dds, pvr, ...) + */ + +THREE.CompressedTextureLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + // override in sub classes + this._parser = null; + +}; + + +THREE.CompressedTextureLoader.prototype = { + + constructor: THREE.CompressedTextureLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var images = []; + + var texture = new THREE.CompressedTexture(); + texture.image = images; + + var loader = new THREE.XHRLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setResponseType( 'arraybuffer' ); + + if ( Array.isArray( url ) ) { + + var loaded = 0; + + var loadTexture = function ( i ) { + + loader.load( url[ i ], function ( buffer ) { + + var texDatas = scope._parser( buffer, true ); + + images[ i ] = { + width: texDatas.width, + height: texDatas.height, + format: texDatas.format, + mipmaps: texDatas.mipmaps + }; + + loaded += 1; + + if ( loaded === 6 ) { + + if ( texDatas.mipmapCount === 1 ) + texture.minFilter = THREE.LinearFilter; + + texture.format = texDatas.format; + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + } + + }, onProgress, onError ); + + }; + + for ( var i = 0, il = url.length; i < il; ++ i ) { + + loadTexture( i ); + + } + + } else { + + // compressed cubemap texture stored in a single DDS file + + loader.load( url, function ( buffer ) { + + var texDatas = scope._parser( buffer, true ); + + if ( texDatas.isCubemap ) { + + var faces = texDatas.mipmaps.length / texDatas.mipmapCount; + + for ( var f = 0; f < faces; f ++ ) { + + images[ f ] = { mipmaps : [] }; + + for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { + + images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); + images[ f ].format = texDatas.format; + images[ f ].width = texDatas.width; + images[ f ].height = texDatas.height; + + } + + } + + } else { + + texture.image.width = texDatas.width; + texture.image.height = texDatas.height; + texture.mipmaps = texDatas.mipmaps; + + } + + if ( texDatas.mipmapCount === 1 ) { + + texture.minFilter = THREE.LinearFilter; + + } + + texture.format = texDatas.format; + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + }, onProgress, onError ); + + } + + return texture; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; + +// File:src/materials/Material.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Material = function () { + + Object.defineProperty( this, 'id', { value: THREE.MaterialIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.type = 'Material'; + + this.side = THREE.FrontSide; + + this.opacity = 1; + this.transparent = false; + + this.blending = THREE.NormalBlending; + + this.blendSrc = THREE.SrcAlphaFactor; + this.blendDst = THREE.OneMinusSrcAlphaFactor; + this.blendEquation = THREE.AddEquation; + this.blendSrcAlpha = null; + this.blendDstAlpha = null; + this.blendEquationAlpha = null; + + this.depthFunc = THREE.LessEqualDepth; + this.depthTest = true; + this.depthWrite = true; + + this.colorWrite = true; + + this.precision = null; // override the renderer's default precision for this material + + this.polygonOffset = false; + this.polygonOffsetFactor = 0; + this.polygonOffsetUnits = 0; + + this.alphaTest = 0; + + this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer + + this.visible = true; + + this._needsUpdate = true; + +}; + +THREE.Material.prototype = { + + constructor: THREE.Material, + + get needsUpdate () { + + return this._needsUpdate; + + }, + + set needsUpdate ( value ) { + + if ( value === true ) this.update(); + + this._needsUpdate = value; + + }, + + setValues: function ( values ) { + + if ( values === undefined ) return; + + for ( var key in values ) { + + var newValue = values[ key ]; + + if ( newValue === undefined ) { + + console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); + continue; + + } + + var currentValue = this[ key ]; + + if ( currentValue === undefined ) { + + console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." ); + continue; + + } + + if ( currentValue instanceof THREE.Color ) { + + currentValue.set( newValue ); + + } else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) { + + currentValue.copy( newValue ); + + } else if ( key === 'overdraw' ) { + + // ensure overdraw is backwards-compatible with legacy boolean type + this[ key ] = Number( newValue ); + + } else { + + this[ key ] = newValue; + + } + + } + + }, + + toJSON: function ( meta ) { + + var data = { + metadata: { + version: 4.4, + type: 'Material', + generator: 'Material.toJSON' + } + }; + + // standard Material serialization + data.uuid = this.uuid; + data.type = this.type; + if ( this.name !== '' ) data.name = this.name; + + if ( this.color instanceof THREE.Color ) data.color = this.color.getHex(); + if ( this.emissive instanceof THREE.Color ) data.emissive = this.emissive.getHex(); + if ( this.specular instanceof THREE.Color ) data.specular = this.specular.getHex(); + if ( this.shininess !== undefined ) data.shininess = this.shininess; + + if ( this.map instanceof THREE.Texture ) data.map = this.map.toJSON( meta ).uuid; + if ( this.alphaMap instanceof THREE.Texture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; + if ( this.lightMap instanceof THREE.Texture ) data.lightMap = this.lightMap.toJSON( meta ).uuid; + if ( this.bumpMap instanceof THREE.Texture ) { + + data.bumpMap = this.bumpMap.toJSON( meta ).uuid; + data.bumpScale = this.bumpScale; + + } + if ( this.normalMap instanceof THREE.Texture ) { + + data.normalMap = this.normalMap.toJSON( meta ).uuid; + data.normalScale = this.normalScale; // Removed for now, causes issue in editor ui.js + + } + if ( this.displacementMap instanceof THREE.Texture ) { + + data.displacementMap = this.displacementMap.toJSON( meta ).uuid; + data.displacementScale = this.displacementScale; + data.displacementBias = this.displacementBias; + + } + if ( this.specularMap instanceof THREE.Texture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; + if ( this.envMap instanceof THREE.Texture ) { + + data.envMap = this.envMap.toJSON( meta ).uuid; + data.reflectivity = this.reflectivity; // Scale behind envMap + + } + + if ( this.size !== undefined ) data.size = this.size; + if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; + + if ( this.vertexColors !== undefined && this.vertexColors !== THREE.NoColors ) data.vertexColors = this.vertexColors; + if ( this.shading !== undefined && this.shading !== THREE.SmoothShading ) data.shading = this.shading; + if ( this.blending !== undefined && this.blending !== THREE.NormalBlending ) data.blending = this.blending; + if ( this.side !== undefined && this.side !== THREE.FrontSide ) data.side = this.side; + + if ( this.opacity < 1 ) data.opacity = this.opacity; + if ( this.transparent === true ) data.transparent = this.transparent; + if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; + if ( this.wireframe === true ) data.wireframe = this.wireframe; + if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; + + return data; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( source ) { + + this.name = source.name; + + this.side = source.side; + + this.opacity = source.opacity; + this.transparent = source.transparent; + + this.blending = source.blending; + + this.blendSrc = source.blendSrc; + this.blendDst = source.blendDst; + this.blendEquation = source.blendEquation; + this.blendSrcAlpha = source.blendSrcAlpha; + this.blendDstAlpha = source.blendDstAlpha; + this.blendEquationAlpha = source.blendEquationAlpha; + + this.depthFunc = source.depthFunc; + this.depthTest = source.depthTest; + this.depthWrite = source.depthWrite; + + this.precision = source.precision; + + this.polygonOffset = source.polygonOffset; + this.polygonOffsetFactor = source.polygonOffsetFactor; + this.polygonOffsetUnits = source.polygonOffsetUnits; + + this.alphaTest = source.alphaTest; + + this.overdraw = source.overdraw; + + this.visible = source.visible; + + return this; + + }, + + update: function () { + + this.dispatchEvent( { type: 'update' } ); + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + }, + + // Deprecated + + get wrapAround () { + + console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' ); + + }, + + set wrapAround ( boolean ) { + + console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' ); + + }, + + get wrapRGB () { + + console.warn( 'THREE.' + this.type + ': .wrapRGB has been removed.' ); + return new THREE.Color(); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Material.prototype ); + +THREE.MaterialIdCount = 0; + +// File:src/materials/LineBasicMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * linewidth: , + * linecap: "round", + * linejoin: "round", + * + * vertexColors: + * + * fog: + * } + */ + +THREE.LineBasicMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'LineBasicMaterial'; + + this.color = new THREE.Color( 0xffffff ); + + this.linewidth = 1; + this.linecap = 'round'; + this.linejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.fog = true; + + this.setValues( parameters ); + +}; + +THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.LineBasicMaterial.prototype.constructor = THREE.LineBasicMaterial; + +THREE.LineBasicMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + + this.linewidth = source.linewidth; + this.linecap = source.linecap; + this.linejoin = source.linejoin; + + this.vertexColors = source.vertexColors; + + this.fog = source.fog; + + return this; + +}; + +// File:src/materials/LineDashedMaterial.js + +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * linewidth: , + * + * scale: , + * dashSize: , + * gapSize: , + * + * vertexColors: + * + * fog: + * } + */ + +THREE.LineDashedMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'LineDashedMaterial'; + + this.color = new THREE.Color( 0xffffff ); + + this.linewidth = 1; + + this.scale = 1; + this.dashSize = 3; + this.gapSize = 1; + + this.vertexColors = false; + + this.fog = true; + + this.setValues( parameters ); + +}; + +THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.LineDashedMaterial.prototype.constructor = THREE.LineDashedMaterial; + +THREE.LineDashedMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + + this.linewidth = source.linewidth; + + this.scale = source.scale; + this.dashSize = source.dashSize; + this.gapSize = source.gapSize; + + this.vertexColors = source.vertexColors; + + this.fog = source.fog; + + return this; + +}; + +// File:src/materials/MeshBasicMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * + * fog: + * } + */ + +THREE.MeshBasicMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'MeshBasicMaterial'; + + this.color = new THREE.Color( 0xffffff ); // emissive + + this.map = null; + + this.aoMap = null; + this.aoMapIntensity = 1.0; + + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.fog = true; + + this.shading = THREE.SmoothShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + + this.setValues( parameters ); + +}; + +THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshBasicMaterial.prototype.constructor = THREE.MeshBasicMaterial; + +THREE.MeshBasicMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + + this.map = source.map; + + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + + this.specularMap = source.specularMap; + + this.alphaMap = source.alphaMap; + + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; + + this.fog = source.fog; + + this.shading = source.shading; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + + this.vertexColors = source.vertexColors; + + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + + return this; + +}; + +// File:src/materials/MeshLambertMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * emissive: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } + */ + +THREE.MeshLambertMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'MeshLambertMaterial'; + + this.color = new THREE.Color( 0xffffff ); // diffuse + this.emissive = new THREE.Color( 0x000000 ); + + this.map = null; + + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.fog = true; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; + + this.setValues( parameters ); + +}; + +THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshLambertMaterial.prototype.constructor = THREE.MeshLambertMaterial; + +THREE.MeshLambertMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + this.emissive.copy( source.emissive ); + + this.map = source.map; + + this.specularMap = source.specularMap; + + this.alphaMap = source.alphaMap; + + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; + + this.fog = source.fog; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + + this.vertexColors = source.vertexColors; + + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; + + return this; + +}; + +// File:src/materials/MeshPhongMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * emissive: , + * specular: , + * shininess: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissiveMap: new THREE.Texture( ), + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalScale: , + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } + */ + +THREE.MeshPhongMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'MeshPhongMaterial'; + + this.color = new THREE.Color( 0xffffff ); // diffuse + this.emissive = new THREE.Color( 0x000000 ); + this.specular = new THREE.Color( 0x111111 ); + this.shininess = 30; + + this.metal = false; + + this.map = null; + + this.lightMap = null; + this.lightMapIntensity = 1.0; + + this.aoMap = null; + this.aoMapIntensity = 1.0; + + this.emissiveMap = null; + + this.bumpMap = null; + this.bumpScale = 1; + + this.normalMap = null; + this.normalScale = new THREE.Vector2( 1, 1 ); + + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; + + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.fog = true; + + this.shading = THREE.SmoothShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; + + this.setValues( parameters ); + +}; + +THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshPhongMaterial.prototype.constructor = THREE.MeshPhongMaterial; + +THREE.MeshPhongMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + this.emissive.copy( source.emissive ); + this.specular.copy( source.specular ); + this.shininess = source.shininess; + + this.metal = source.metal; + + this.map = source.map; + + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; + + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + + this.emissiveMap = source.emissiveMap; + + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; + + this.normalMap = source.normalMap; + this.normalScale.copy( source.normalScale ); + + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + + this.specularMap = source.specularMap; + + this.alphaMap = source.alphaMap; + + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; + + this.fog = source.fog; + + this.shading = source.shading; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + + this.vertexColors = source.vertexColors; + + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; + + return this; + +}; + +// File:src/materials/MeshDepthMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * opacity: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: + * } + */ + +THREE.MeshDepthMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'MeshDepthMaterial'; + + this.morphTargets = false; + this.wireframe = false; + this.wireframeLinewidth = 1; + + this.setValues( parameters ); + +}; + +THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshDepthMaterial.prototype.constructor = THREE.MeshDepthMaterial; + +THREE.MeshDepthMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + + return this; + +}; + +// File:src/materials/MeshNormalMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * + * parameters = { + * opacity: , + * + * shading: THREE.FlatShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: + * } + */ + +THREE.MeshNormalMaterial = function ( parameters ) { + + THREE.Material.call( this, parameters ); + + this.type = 'MeshNormalMaterial'; + + this.wireframe = false; + this.wireframeLinewidth = 1; + + this.morphTargets = false; + + this.setValues( parameters ); + +}; + +THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshNormalMaterial.prototype.constructor = THREE.MeshNormalMaterial; + +THREE.MeshNormalMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + + return this; + +}; + +// File:src/materials/MultiMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.MultiMaterial = function ( materials ) { + + this.uuid = THREE.Math.generateUUID(); + + this.type = 'MultiMaterial'; + + this.materials = materials instanceof Array ? materials : []; + + this.visible = true; + +}; + +THREE.MultiMaterial.prototype = { + + constructor: THREE.MultiMaterial, + + toJSON: function () { + + var output = { + metadata: { + version: 4.2, + type: 'material', + generator: 'MaterialExporter' + }, + uuid: this.uuid, + type: this.type, + materials: [] + }; + + for ( var i = 0, l = this.materials.length; i < l; i ++ ) { + + output.materials.push( this.materials[ i ].toJSON() ); + + } + + output.visible = this.visible; + + return output; + + }, + + clone: function () { + + var material = new this.constructor(); + + for ( var i = 0; i < this.materials.length; i ++ ) { + + material.materials.push( this.materials[ i ].clone() ); + + } + + material.visible = this.visible; + + return material; + + } + +}; + +// backwards compatibility + +THREE.MeshFaceMaterial = THREE.MultiMaterial; + +// File:src/materials/PointsMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * size: , + * sizeAttenuation: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * vertexColors: , + * + * fog: + * } + */ + +THREE.PointsMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'PointsMaterial'; + + this.color = new THREE.Color( 0xffffff ); + + this.map = null; + + this.size = 1; + this.sizeAttenuation = true; + + this.vertexColors = THREE.NoColors; + + this.fog = true; + + this.setValues( parameters ); + +}; + +THREE.PointsMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.PointsMaterial.prototype.constructor = THREE.PointsMaterial; + +THREE.PointsMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + + this.map = source.map; + + this.size = source.size; + this.sizeAttenuation = source.sizeAttenuation; + + this.vertexColors = source.vertexColors; + + this.fog = source.fog; + + return this; + +}; + +// backwards compatibility + +THREE.PointCloudMaterial = function ( parameters ) { + + console.warn( 'THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.' ); + return new THREE.PointsMaterial( parameters ); + +}; + +THREE.ParticleBasicMaterial = function ( parameters ) { + + console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.' ); + return new THREE.PointsMaterial( parameters ); + +}; + +THREE.ParticleSystemMaterial = function ( parameters ) { + + console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.' ); + return new THREE.PointsMaterial( parameters ); + +}; + +// File:src/materials/ShaderMaterial.js + +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * defines: { "label" : "value" }, + * uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } }, + * + * fragmentShader: , + * vertexShader: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * lights: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } + */ + +THREE.ShaderMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'ShaderMaterial'; + + this.defines = {}; + this.uniforms = {}; + + this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}'; + this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}'; + + this.shading = THREE.SmoothShading; + + this.linewidth = 1; + + this.wireframe = false; + this.wireframeLinewidth = 1; + + this.fog = false; // set to use scene fog + + this.lights = false; // set to use scene lights + + this.vertexColors = THREE.NoColors; // set to use "color" attribute stream + + this.skinning = false; // set to use skinning attribute streams + + this.morphTargets = false; // set to use morph targets + this.morphNormals = false; // set to use morph normals + + this.derivatives = false; // set to use derivatives + + // When rendered geometry doesn't include these attributes but the material does, + // use these default values in WebGL. This avoids errors when buffer data is missing. + this.defaultAttributeValues = { + 'color': [ 1, 1, 1 ], + 'uv': [ 0, 0 ], + 'uv2': [ 0, 0 ] + }; + + this.index0AttributeName = undefined; + + if ( parameters !== undefined ) { + + if ( parameters.attributes !== undefined ) { + + console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); + + } + + this.setValues( parameters ); + + } + +}; + +THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.ShaderMaterial.prototype.constructor = THREE.ShaderMaterial; + +THREE.ShaderMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.fragmentShader = source.fragmentShader; + this.vertexShader = source.vertexShader; + + this.uniforms = THREE.UniformsUtils.clone( source.uniforms ); + + this.attributes = source.attributes; + this.defines = source.defines; + + this.shading = source.shading; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + + this.fog = source.fog; + + this.lights = source.lights; + + this.vertexColors = source.vertexColors; + + this.skinning = source.skinning; + + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; + + this.derivatives = source.derivatives; + + return this; + +}; + +THREE.ShaderMaterial.prototype.toJSON = function ( meta ) { + + var data = THREE.Material.prototype.toJSON.call( this, meta ); + + data.uniforms = this.uniforms; + data.attributes = this.attributes; + data.vertexShader = this.vertexShader; + data.fragmentShader = this.fragmentShader; + + return data; + +}; + +// File:src/materials/RawShaderMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RawShaderMaterial = function ( parameters ) { + + THREE.ShaderMaterial.call( this, parameters ); + + this.type = 'RawShaderMaterial'; + +}; + +THREE.RawShaderMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype ); +THREE.RawShaderMaterial.prototype.constructor = THREE.RawShaderMaterial; +// File:src/materials/SpriteMaterial.js + +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * uvOffset: new THREE.Vector2(), + * uvScale: new THREE.Vector2(), + * + * fog: + * } + */ + +THREE.SpriteMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'SpriteMaterial'; + + this.color = new THREE.Color( 0xffffff ); + this.map = null; + + this.rotation = 0; + + this.fog = false; + + // set parameters + + this.setValues( parameters ); + +}; + +THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.SpriteMaterial.prototype.constructor = THREE.SpriteMaterial; + +THREE.SpriteMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + this.map = source.map; + + this.rotation = source.rotation; + + this.fog = source.fog; + + return this; + +}; + +// File:src/textures/Texture.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ + +THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + Object.defineProperty( this, 'id', { value: THREE.TextureIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.sourceFile = ''; + + this.image = image !== undefined ? image : THREE.Texture.DEFAULT_IMAGE; + this.mipmaps = []; + + this.mapping = mapping !== undefined ? mapping : THREE.Texture.DEFAULT_MAPPING; + + this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping; + + this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter; + this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter; + + this.anisotropy = anisotropy !== undefined ? anisotropy : 1; + + this.format = format !== undefined ? format : THREE.RGBAFormat; + this.type = type !== undefined ? type : THREE.UnsignedByteType; + + this.offset = new THREE.Vector2( 0, 0 ); + this.repeat = new THREE.Vector2( 1, 1 ); + + this.generateMipmaps = true; + this.premultiplyAlpha = false; + this.flipY = true; + this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) + + this.version = 0; + this.onUpdate = null; + +}; + +THREE.Texture.DEFAULT_IMAGE = undefined; +THREE.Texture.DEFAULT_MAPPING = THREE.UVMapping; + +THREE.Texture.prototype = { + + constructor: THREE.Texture, + + set needsUpdate ( value ) { + + if ( value === true ) this.version ++; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( source ) { + + this.image = source.image; + this.mipmaps = source.mipmaps.slice( 0 ); + + this.mapping = source.mapping; + + this.wrapS = source.wrapS; + this.wrapT = source.wrapT; + + this.magFilter = source.magFilter; + this.minFilter = source.minFilter; + + this.anisotropy = source.anisotropy; + + this.format = source.format; + this.type = source.type; + + this.offset.copy( source.offset ); + this.repeat.copy( source.repeat ); + + this.generateMipmaps = source.generateMipmaps; + this.premultiplyAlpha = source.premultiplyAlpha; + this.flipY = source.flipY; + this.unpackAlignment = source.unpackAlignment; + + return this; + + }, + + toJSON: function ( meta ) { + + if ( meta.textures[ this.uuid ] !== undefined ) { + + return meta.textures[ this.uuid ]; + + } + + function getDataURL( image ) { + + var canvas; + + if ( image.toDataURL !== undefined ) { + + canvas = image; + + } else { + + canvas = document.createElement( 'canvas' ); + canvas.width = image.width; + canvas.height = image.height; + + canvas.getContext( '2d' ).drawImage( image, 0, 0, image.width, image.height ); + + } + + if ( canvas.width > 2048 || canvas.height > 2048 ) { + + return canvas.toDataURL( 'image/jpeg', 0.6 ); + + } else { + + return canvas.toDataURL( 'image/png' ); + + } + + } + + var output = { + metadata: { + version: 4.4, + type: 'Texture', + generator: 'Texture.toJSON' + }, + + uuid: this.uuid, + name: this.name, + + mapping: this.mapping, + + repeat: [ this.repeat.x, this.repeat.y ], + offset: [ this.offset.x, this.offset.y ], + wrap: [ this.wrapS, this.wrapT ], + + minFilter: this.minFilter, + magFilter: this.magFilter, + anisotropy: this.anisotropy + }; + + if ( this.image !== undefined ) { + + // TODO: Move to THREE.Image + + var image = this.image; + + if ( image.uuid === undefined ) { + + image.uuid = THREE.Math.generateUUID(); // UGH + + } + + if ( meta.images[ image.uuid ] === undefined ) { + + meta.images[ image.uuid ] = { + uuid: image.uuid, + url: getDataURL( image ) + }; + + } + + output.image = image.uuid; + + } + + meta.textures[ this.uuid ] = output; + + return output; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + }, + + transformUv: function ( uv ) { + + if ( this.mapping !== THREE.UVMapping ) return; + + uv.multiply( this.repeat ); + uv.add( this.offset ); + + if ( uv.x < 0 || uv.x > 1 ) { + + switch ( this.wrapS ) { + + case THREE.RepeatWrapping: + + uv.x = uv.x - Math.floor( uv.x ); + break; + + case THREE.ClampToEdgeWrapping: + + uv.x = uv.x < 0 ? 0 : 1; + break; + + case THREE.MirroredRepeatWrapping: + + if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { + + uv.x = Math.ceil( uv.x ) - uv.x; + + } else { + + uv.x = uv.x - Math.floor( uv.x ); + + } + break; + + } + + } + + if ( uv.y < 0 || uv.y > 1 ) { + + switch ( this.wrapT ) { + + case THREE.RepeatWrapping: + + uv.y = uv.y - Math.floor( uv.y ); + break; + + case THREE.ClampToEdgeWrapping: + + uv.y = uv.y < 0 ? 0 : 1; + break; + + case THREE.MirroredRepeatWrapping: + + if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { + + uv.y = Math.ceil( uv.y ) - uv.y; + + } else { + + uv.y = uv.y - Math.floor( uv.y ); + + } + break; + + } + + } + + if ( this.flipY ) { + + uv.y = 1 - uv.y; + + } + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype ); + +THREE.TextureIdCount = 0; + +// File:src/textures/CanvasTexture.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CanvasTexture = function ( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + THREE.Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.needsUpdate = true; + +}; + +THREE.CanvasTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.CanvasTexture.prototype.constructor = THREE.CanvasTexture; + +// File:src/textures/CubeTexture.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CubeTexture = function ( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + mapping = mapping !== undefined ? mapping : THREE.CubeReflectionMapping; + + THREE.Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.images = images; + this.flipY = false; + +}; + +THREE.CubeTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.CubeTexture.prototype.constructor = THREE.CubeTexture; + +THREE.CubeTexture.prototype.copy = function ( source ) { + + THREE.Texture.prototype.copy.call( this, source ); + + this.images = source.images; + + return this; + +}; +// File:src/textures/CompressedTexture.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { + + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.image = { width: width, height: height }; + this.mipmaps = mipmaps; + + // no flipping for cube textures + // (also flipping doesn't work for compressed textures ) + + this.flipY = false; + + // can't generate mipmaps for compressed textures + // mips must be embedded in DDS files + + this.generateMipmaps = false; + +}; + +THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.CompressedTexture.prototype.constructor = THREE.CompressedTexture; + +// File:src/textures/DataTexture.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { + + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.image = { data: data, width: width, height: height }; + + this.magFilter = magFilter !== undefined ? magFilter : THREE.NearestFilter; + this.minFilter = minFilter !== undefined ? minFilter : THREE.NearestFilter; + + this.flipY = false; + this.generateMipmaps = false; + +}; + +THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.DataTexture.prototype.constructor = THREE.DataTexture; + +// File:src/textures/VideoTexture.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.VideoTexture = function ( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + THREE.Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.generateMipmaps = false; + + var scope = this; + + function update() { + + requestAnimationFrame( update ); + + if ( video.readyState === video.HAVE_ENOUGH_DATA ) { + + scope.needsUpdate = true; + + } + + } + + update(); + +}; + +THREE.VideoTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.VideoTexture.prototype.constructor = THREE.VideoTexture; + +// File:src/objects/Group.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Group = function () { + + THREE.Object3D.call( this ); + + this.type = 'Group'; + +}; + +THREE.Group.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Group.prototype.constructor = THREE.Group; +// File:src/objects/Points.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Points = function ( geometry, material ) { + + THREE.Object3D.call( this ); + + this.type = 'Points'; + + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.PointsMaterial( { color: Math.random() * 0xffffff } ); + +}; + +THREE.Points.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Points.prototype.constructor = THREE.Points; + +THREE.Points.prototype.raycast = ( function () { + + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + + return function raycast( raycaster, intersects ) { + + var object = this; + var geometry = object.geometry; + var threshold = raycaster.params.Points.threshold; + + inverseMatrix.getInverse( this.matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + + if ( geometry.boundingBox !== null ) { + + if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) { + + return; + + } + + } + + var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + var localThresholdSq = localThreshold * localThreshold; + var position = new THREE.Vector3(); + + function testPoint( point, index ) { + + var rayPointDistanceSq = ray.distanceSqToPoint( point ); + + if ( rayPointDistanceSq < localThresholdSq ) { + + var intersectPoint = ray.closestPointToPoint( point ); + intersectPoint.applyMatrix4( object.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( intersectPoint ); + + if ( distance < raycaster.near || distance > raycaster.far ) return; + + intersects.push( { + + distance: distance, + distanceToRay: Math.sqrt( rayPointDistanceSq ), + point: intersectPoint.clone(), + index: index, + face: null, + object: object + + } ); + + } + + } + + if ( geometry instanceof THREE.BufferGeometry ) { + + var index = geometry.index; + var attributes = geometry.attributes; + var positions = attributes.position.array; + + if ( index !== null ) { + + var indices = index.array; + + for ( var i = 0, il = indices.length; i < il; i ++ ) { + + var a = indices[ i ]; + + position.fromArray( positions, a * 3 ); + + testPoint( position, a ); + + } + + } else { + + for ( var i = 0, l = positions.length / 3; i < l; i ++ ) { + + position.fromArray( positions, i * 3 ); + + testPoint( position, i ); + + } + + } + + } else { + + var vertices = geometry.vertices; + + for ( var i = 0, l = vertices.length; i < l; i ++ ) { + + testPoint( vertices[ i ], i ); + + } + + } + + }; + +}() ); + +THREE.Points.prototype.clone = function () { + + return new this.constructor( this.geometry, this.material ).copy( this ); + +}; + +// Backwards compatibility + +THREE.PointCloud = function ( geometry, material ) { + + console.warn( 'THREE.PointCloud has been renamed to THREE.Points.' ); + return new THREE.Points( geometry, material ); + +}; + +THREE.ParticleSystem = function ( geometry, material ) { + + console.warn( 'THREE.ParticleSystem has been renamed to THREE.Points.' ); + return new THREE.Points( geometry, material ); + +}; + +// File:src/objects/Line.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Line = function ( geometry, material, mode ) { + + if ( mode === 1 ) { + + console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' ); + return new THREE.LineSegments( geometry, material ); + + } + + THREE.Object3D.call( this ); + + this.type = 'Line'; + + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } ); + +}; + +THREE.Line.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Line.prototype.constructor = THREE.Line; + +THREE.Line.prototype.raycast = ( function () { + + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + var sphere = new THREE.Sphere(); + + return function raycast( raycaster, intersects ) { + + var precision = raycaster.linePrecision; + var precisionSq = precision * precision; + + var geometry = this.geometry; + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + // Checking boundingSphere distance to ray + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( this.matrixWorld ); + + if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { + + return; + + } + + inverseMatrix.getInverse( this.matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + + var vStart = new THREE.Vector3(); + var vEnd = new THREE.Vector3(); + var interSegment = new THREE.Vector3(); + var interRay = new THREE.Vector3(); + var step = this instanceof THREE.LineSegments ? 2 : 1; + + if ( geometry instanceof THREE.BufferGeometry ) { + + var index = geometry.index; + var attributes = geometry.attributes; + + if ( index !== null ) { + + var indices = index.array; + var positions = attributes.position.array; + + for ( var i = 0, l = indices.length - 1; i < l; i += step ) { + + var a = indices[ i ]; + var b = indices[ i + 1 ]; + + vStart.fromArray( positions, a * 3 ); + vEnd.fromArray( positions, b * 3 ); + + var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + + if ( distSq > precisionSq ) continue; + + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + + var distance = raycaster.ray.origin.distanceTo( interRay ); + + if ( distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this + + } ); + + } + + } else { + + var positions = attributes.position.array; + + for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) { + + vStart.fromArray( positions, 3 * i ); + vEnd.fromArray( positions, 3 * i + 3 ); + + var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + + if ( distSq > precisionSq ) continue; + + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + + var distance = raycaster.ray.origin.distanceTo( interRay ); + + if ( distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this + + } ); + + } + + } + + } else if ( geometry instanceof THREE.Geometry ) { + + var vertices = geometry.vertices; + var nbVertices = vertices.length; + + for ( var i = 0; i < nbVertices - 1; i += step ) { + + var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); + + if ( distSq > precisionSq ) continue; + + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + + var distance = raycaster.ray.origin.distanceTo( interRay ); + + if ( distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this + + } ); + + } + + } + + }; + +}() ); + +THREE.Line.prototype.clone = function () { + + return new this.constructor( this.geometry, this.material ).copy( this ); + +}; + +// DEPRECATED + +THREE.LineStrip = 0; +THREE.LinePieces = 1; + +// File:src/objects/LineSegments.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LineSegments = function ( geometry, material ) { + + THREE.Line.call( this, geometry, material ); + + this.type = 'LineSegments'; + +}; + +THREE.LineSegments.prototype = Object.create( THREE.Line.prototype ); +THREE.LineSegments.prototype.constructor = THREE.LineSegments; + +// File:src/objects/Mesh.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author jonobr1 / http://jonobr1.com/ + */ + +THREE.Mesh = function ( geometry, material ) { + + THREE.Object3D.call( this ); + + this.type = 'Mesh'; + + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } ); + + this.updateMorphTargets(); + +}; + +THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Mesh.prototype.constructor = THREE.Mesh; + +THREE.Mesh.prototype.updateMorphTargets = function () { + + if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) { + + this.morphTargetBase = - 1; + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; + + for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) { + + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m; + + } + + } + +}; + +THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) { + + if ( this.morphTargetDictionary[ name ] !== undefined ) { + + return this.morphTargetDictionary[ name ]; + + } + + console.warn( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' ); + + return 0; + +}; + + +THREE.Mesh.prototype.raycast = ( function () { + + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + var sphere = new THREE.Sphere(); + + var vA = new THREE.Vector3(); + var vB = new THREE.Vector3(); + var vC = new THREE.Vector3(); + + var tempA = new THREE.Vector3(); + var tempB = new THREE.Vector3(); + var tempC = new THREE.Vector3(); + + var uvA = new THREE.Vector2(); + var uvB = new THREE.Vector2(); + var uvC = new THREE.Vector2(); + + var barycoord = new THREE.Vector3(); + + var intersectionPoint = new THREE.Vector3(); + var intersectionPointWorld = new THREE.Vector3(); + + function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) { + + THREE.Triangle.barycoordFromPoint( point, p1, p2, p3, barycoord ); + + uv1.multiplyScalar( barycoord.x ); + uv2.multiplyScalar( barycoord.y ); + uv3.multiplyScalar( barycoord.z ); + + uv1.add( uv2 ).add( uv3 ); + + return uv1.clone(); + + } + + function checkIntersection( object, raycaster, ray, pA, pB, pC, point ){ + + var intersect; + var material = object.material; + + if ( material.side === THREE.BackSide ) { + + intersect = ray.intersectTriangle( pC, pB, pA, true, point ); + + } else { + + intersect = ray.intersectTriangle( pA, pB, pC, material.side !== THREE.DoubleSide, point ); + + } + + if ( intersect === null ) return null; + + intersectionPointWorld.copy( point ); + intersectionPointWorld.applyMatrix4( object.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); + + if ( distance < raycaster.near || distance > raycaster.far ) return null; + + return { + distance: distance, + point: intersectionPointWorld.clone(), + object: object + }; + + } + + function checkBufferGeometryIntersection( object, raycaster, ray, positions, uvs, a, b, c ) { + + vA.fromArray( positions, a * 3 ); + vB.fromArray( positions, b * 3 ); + vC.fromArray( positions, c * 3 ); + + var intersection = checkIntersection( object, raycaster, ray, vA, vB, vC, intersectionPoint ); + + if ( intersection ) { + + if ( uvs ) { + + uvA.fromArray( uvs, a * 2 ); + uvB.fromArray( uvs, b * 2 ); + uvC.fromArray( uvs, c * 2 ); + + intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC ); + + } + + intersection.face = new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ); + intersection.faceIndex = a; + + } + + return intersection; + + } + + return function raycast( raycaster, intersects ) { + + var geometry = this.geometry; + var material = this.material; + + if ( material === undefined ) return; + + // Checking boundingSphere distance to ray + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + var matrixWorld = this.matrixWorld; + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( matrixWorld ); + + if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) return; + + // Check boundingBox before continuing + + inverseMatrix.getInverse( matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + + if ( geometry.boundingBox !== null ) { + + if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) return; + + } + + var uvs, intersection; + + if ( geometry instanceof THREE.BufferGeometry ) { + + var a, b, c; + var index = geometry.index; + var attributes = geometry.attributes; + var positions = attributes.position.array; + + if ( attributes.uv !== undefined ){ + + uvs = attributes.uv.array; + + } + + if ( index !== null ) { + + var indices = index.array; + + for ( var i = 0, l = indices.length; i < l; i += 3 ) { + + a = indices[ i ]; + b = indices[ i + 1 ]; + c = indices[ i + 2 ]; + + intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c ); + + if ( intersection ) { + + intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indices buffer semantics + intersects.push( intersection ); + + } + + } + + } else { + + + for ( var i = 0, l = positions.length; i < l; i += 9 ) { + + a = i / 3; + b = a + 1; + c = a + 2; + + intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c ); + + if ( intersection ) { + + intersection.index = a; // triangle number in positions buffer semantics + intersects.push( intersection ); + + } + + } + + } + + } else if ( geometry instanceof THREE.Geometry ) { + + var fvA, fvB, fvC; + var isFaceMaterial = material instanceof THREE.MeshFaceMaterial; + var materials = isFaceMaterial === true ? material.materials : null; + + var vertices = geometry.vertices; + var faces = geometry.faces; + var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; + if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs; + + for ( var f = 0, fl = faces.length; f < fl; f ++ ) { + + var face = faces[ f ]; + var faceMaterial = isFaceMaterial === true ? materials[ face.materialIndex ] : material; + + if ( faceMaterial === undefined ) continue; + + fvA = vertices[ face.a ]; + fvB = vertices[ face.b ]; + fvC = vertices[ face.c ]; + + if ( faceMaterial.morphTargets === true ) { + + var morphTargets = geometry.morphTargets; + var morphInfluences = this.morphTargetInfluences; + + vA.set( 0, 0, 0 ); + vB.set( 0, 0, 0 ); + vC.set( 0, 0, 0 ); + + for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { + + var influence = morphInfluences[ t ]; + + if ( influence === 0 ) continue; + + var targets = morphTargets[ t ].vertices; + + vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence ); + vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence ); + vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence ); + + } + + vA.add( fvA ); + vB.add( fvB ); + vC.add( fvC ); + + fvA = vA; + fvB = vB; + fvC = vC; + + } + + intersection = checkIntersection( this, raycaster, ray, fvA, fvB, fvC, intersectionPoint ); + + if ( intersection ) { + + if ( uvs ) { + + var uvs_f = uvs[ f ]; + uvA.copy( uvs_f[ 0 ] ); + uvB.copy( uvs_f[ 1 ] ); + uvC.copy( uvs_f[ 2 ] ); + + intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC ); + + } + + intersection.face = face; + intersection.faceIndex = f; + intersects.push( intersection ); + + } + + } + + } + + }; + +}() ); + +THREE.Mesh.prototype.clone = function () { + + return new this.constructor( this.geometry, this.material ).copy( this ); + +}; + +// File:src/objects/Bone.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author ikerr / http://verold.com + */ + +THREE.Bone = function ( skin ) { + + THREE.Object3D.call( this ); + + this.type = 'Bone'; + + this.skin = skin; + +}; + +THREE.Bone.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Bone.prototype.constructor = THREE.Bone; + +THREE.Bone.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source ); + + this.skin = source.skin; + + return this; + +}; + +// File:src/objects/Skeleton.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author michael guerrero / http://realitymeltdown.com + * @author ikerr / http://verold.com + */ + +THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) { + + this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true; + + this.identityMatrix = new THREE.Matrix4(); + + // copy the bone array + + bones = bones || []; + + this.bones = bones.slice( 0 ); + + // create a bone texture or an array of floats + + if ( this.useVertexTexture ) { + + // layout (1 matrix = 4 pixels) + // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) + // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) + // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) + // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) + // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) + + + var size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix + size = THREE.Math.nextPowerOfTwo( Math.ceil( size ) ); + size = Math.max( size, 4 ); + + this.boneTextureWidth = size; + this.boneTextureHeight = size; + + this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel + this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType ); + + } else { + + this.boneMatrices = new Float32Array( 16 * this.bones.length ); + + } + + // use the supplied bone inverses or calculate the inverses + + if ( boneInverses === undefined ) { + + this.calculateInverses(); + + } else { + + if ( this.bones.length === boneInverses.length ) { + + this.boneInverses = boneInverses.slice( 0 ); + + } else { + + console.warn( 'THREE.Skeleton bonInverses is the wrong length.' ); + + this.boneInverses = []; + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + this.boneInverses.push( new THREE.Matrix4() ); + + } + + } + + } + +}; + +THREE.Skeleton.prototype.calculateInverses = function () { + + this.boneInverses = []; + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + var inverse = new THREE.Matrix4(); + + if ( this.bones[ b ] ) { + + inverse.getInverse( this.bones[ b ].matrixWorld ); + + } + + this.boneInverses.push( inverse ); + + } + +}; + +THREE.Skeleton.prototype.pose = function () { + + var bone; + + // recover the bind-time world matrices + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + bone = this.bones[ b ]; + + if ( bone ) { + + bone.matrixWorld.getInverse( this.boneInverses[ b ] ); + + } + + } + + // compute the local matrices, positions, rotations and scales + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + bone = this.bones[ b ]; + + if ( bone ) { + + if ( bone.parent ) { + + bone.matrix.getInverse( bone.parent.matrixWorld ); + bone.matrix.multiply( bone.matrixWorld ); + + } else { + + bone.matrix.copy( bone.matrixWorld ); + + } + + bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); + + } + + } + +}; + +THREE.Skeleton.prototype.update = ( function () { + + var offsetMatrix = new THREE.Matrix4(); + + return function update() { + + // flatten bone matrices to array + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + // compute the offset between the current and the original transform + + var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix; + + offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] ); + offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 ); + + } + + if ( this.useVertexTexture ) { + + this.boneTexture.needsUpdate = true; + + } + + }; + +} )(); + +THREE.Skeleton.prototype.clone = function () { + + return new THREE.Skeleton( this.bones, this.boneInverses, this.useVertexTexture ); + +}; + +// File:src/objects/SkinnedMesh.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author ikerr / http://verold.com + */ + +THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) { + + THREE.Mesh.call( this, geometry, material ); + + this.type = 'SkinnedMesh'; + + this.bindMode = "attached"; + this.bindMatrix = new THREE.Matrix4(); + this.bindMatrixInverse = new THREE.Matrix4(); + + // init bones + + // TODO: remove bone creation as there is no reason (other than + // convenience) for THREE.SkinnedMesh to do this. + + var bones = []; + + if ( this.geometry && this.geometry.bones !== undefined ) { + + var bone, gbone; + + for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { + + gbone = this.geometry.bones[ b ]; + + bone = new THREE.Bone( this ); + bones.push( bone ); + + bone.name = gbone.name; + bone.position.fromArray( gbone.pos ); + bone.quaternion.fromArray( gbone.rotq ); + if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); + + } + + for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { + + gbone = this.geometry.bones[ b ]; + + if ( gbone.parent !== - 1 && gbone.parent !== null) { + + bones[ gbone.parent ].add( bones[ b ] ); + + } else { + + this.add( bones[ b ] ); + + } + + } + + } + + this.normalizeSkinWeights(); + + this.updateMatrixWorld( true ); + this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ), this.matrixWorld ); + +}; + + +THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype ); +THREE.SkinnedMesh.prototype.constructor = THREE.SkinnedMesh; + +THREE.SkinnedMesh.prototype.bind = function( skeleton, bindMatrix ) { + + this.skeleton = skeleton; + + if ( bindMatrix === undefined ) { + + this.updateMatrixWorld( true ); + + this.skeleton.calculateInverses(); + + bindMatrix = this.matrixWorld; + + } + + this.bindMatrix.copy( bindMatrix ); + this.bindMatrixInverse.getInverse( bindMatrix ); + +}; + +THREE.SkinnedMesh.prototype.pose = function () { + + this.skeleton.pose(); + +}; + +THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () { + + if ( this.geometry instanceof THREE.Geometry ) { + + for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) { + + var sw = this.geometry.skinWeights[ i ]; + + var scale = 1.0 / sw.lengthManhattan(); + + if ( scale !== Infinity ) { + + sw.multiplyScalar( scale ); + + } else { + + sw.set( 1 ); // this will be normalized by the shader anyway + + } + + } + + } else { + + // skinning weights assumed to be normalized for THREE.BufferGeometry + + } + +}; + +THREE.SkinnedMesh.prototype.updateMatrixWorld = function( force ) { + + THREE.Mesh.prototype.updateMatrixWorld.call( this, true ); + + if ( this.bindMode === "attached" ) { + + this.bindMatrixInverse.getInverse( this.matrixWorld ); + + } else if ( this.bindMode === "detached" ) { + + this.bindMatrixInverse.getInverse( this.bindMatrix ); + + } else { + + console.warn( 'THREE.SkinnedMesh unrecognized bindMode: ' + this.bindMode ); + + } + +}; + +THREE.SkinnedMesh.prototype.clone = function() { + + return new this.constructor( this.geometry, this.material, this.useVertexTexture ).copy( this ); + +}; + +// File:src/objects/LOD.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LOD = function () { + + THREE.Object3D.call( this ); + + this.type = 'LOD'; + + Object.defineProperties( this, { + levels: { + enumerable: true, + value: [] + }, + objects: { + get: function () { + + console.warn( 'THREE.LOD: .objects has been renamed to .levels.' ); + return this.levels; + + } + } + } ); + +}; + + +THREE.LOD.prototype = Object.create( THREE.Object3D.prototype ); +THREE.LOD.prototype.constructor = THREE.LOD; + +THREE.LOD.prototype.addLevel = function ( object, distance ) { + + if ( distance === undefined ) distance = 0; + + distance = Math.abs( distance ); + + var levels = this.levels; + + for ( var l = 0; l < levels.length; l ++ ) { + + if ( distance < levels[ l ].distance ) { + + break; + + } + + } + + levels.splice( l, 0, { distance: distance, object: object } ); + + this.add( object ); + +}; + +THREE.LOD.prototype.getObjectForDistance = function ( distance ) { + + var levels = this.levels; + + for ( var i = 1, l = levels.length; i < l; i ++ ) { + + if ( distance < levels[ i ].distance ) { + + break; + + } + + } + + return levels[ i - 1 ].object; + +}; + +THREE.LOD.prototype.raycast = ( function () { + + var matrixPosition = new THREE.Vector3(); + + return function raycast( raycaster, intersects ) { + + matrixPosition.setFromMatrixPosition( this.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( matrixPosition ); + + this.getObjectForDistance( distance ).raycast( raycaster, intersects ); + + }; + +}() ); + +THREE.LOD.prototype.update = function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + + return function update( camera ) { + + var levels = this.levels; + + if ( levels.length > 1 ) { + + v1.setFromMatrixPosition( camera.matrixWorld ); + v2.setFromMatrixPosition( this.matrixWorld ); + + var distance = v1.distanceTo( v2 ); + + levels[ 0 ].object.visible = true; + + for ( var i = 1, l = levels.length; i < l; i ++ ) { + + if ( distance >= levels[ i ].distance ) { + + levels[ i - 1 ].object.visible = false; + levels[ i ].object.visible = true; + + } else { + + break; + + } + + } + + for ( ; i < l; i ++ ) { + + levels[ i ].object.visible = false; + + } + + } + + }; + +}(); + +THREE.LOD.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source, false ); + + var levels = source.levels; + + for ( var i = 0, l = levels.length; i < l; i ++ ) { + + var level = levels[ i ]; + + this.addLevel( level.object.clone(), level.distance ); + + } + + return this; + +}; + +THREE.LOD.prototype.toJSON = function ( meta ) { + + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + + data.object.levels = []; + + var levels = this.levels; + + for ( var i = 0, l = levels.length; i < l; i ++ ) { + + var level = levels[ i ]; + + data.object.levels.push( { + object: level.object.uuid, + distance: level.distance + } ); + + } + + return data; + +}; + +// File:src/objects/Sprite.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Sprite = ( function () { + + var indices = new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] ); + var vertices = new Float32Array( [ - 0.5, - 0.5, 0, 0.5, - 0.5, 0, 0.5, 0.5, 0, - 0.5, 0.5, 0 ] ); + var uvs = new Float32Array( [ 0, 0, 1, 0, 1, 1, 0, 1 ] ); + + var geometry = new THREE.BufferGeometry(); + geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) ); + geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + + return function Sprite( material ) { + + THREE.Object3D.call( this ); + + this.type = 'Sprite'; + + this.geometry = geometry; + this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial(); + + }; + +} )(); + +THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Sprite.prototype.constructor = THREE.Sprite; + +THREE.Sprite.prototype.raycast = ( function () { + + var matrixPosition = new THREE.Vector3(); + + return function raycast( raycaster, intersects ) { + + matrixPosition.setFromMatrixPosition( this.matrixWorld ); + + var distanceSq = raycaster.ray.distanceSqToPoint( matrixPosition ); + var guessSizeSq = this.scale.x * this.scale.y; + + if ( distanceSq > guessSizeSq ) { + + return; + + } + + intersects.push( { + + distance: Math.sqrt( distanceSq ), + point: this.position, + face: null, + object: this + + } ); + + }; + +}() ); + +THREE.Sprite.prototype.clone = function () { + + return new this.constructor( this.material ).copy( this ); + +}; + +// Backwards compatibility + +THREE.Particle = THREE.Sprite; + +// File:src/objects/LensFlare.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.LensFlare = function ( texture, size, distance, blending, color ) { + + THREE.Object3D.call( this ); + + this.lensFlares = []; + + this.positionScreen = new THREE.Vector3(); + this.customUpdateCallback = undefined; + + if ( texture !== undefined ) { + + this.add( texture, size, distance, blending, color ); + + } + +}; + +THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype ); +THREE.LensFlare.prototype.constructor = THREE.LensFlare; + + +/* + * Add: adds another flare + */ + +THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) { + + if ( size === undefined ) size = - 1; + if ( distance === undefined ) distance = 0; + if ( opacity === undefined ) opacity = 1; + if ( color === undefined ) color = new THREE.Color( 0xffffff ); + if ( blending === undefined ) blending = THREE.NormalBlending; + + distance = Math.min( distance, Math.max( 0, distance ) ); + + this.lensFlares.push( { + texture: texture, // THREE.Texture + size: size, // size in pixels (-1 = use texture.width) + distance: distance, // distance (0-1) from light source (0=at light source) + x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is in front z = 1 is back + scale: 1, // scale + rotation: 0, // rotation + opacity: opacity, // opacity + color: color, // color + blending: blending // blending + } ); + +}; + +/* + * Update lens flares update positions on all flares based on the screen position + * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way. + */ + +THREE.LensFlare.prototype.updateLensFlares = function () { + + var f, fl = this.lensFlares.length; + var flare; + var vecX = - this.positionScreen.x * 2; + var vecY = - this.positionScreen.y * 2; + + for ( f = 0; f < fl; f ++ ) { + + flare = this.lensFlares[ f ]; + + flare.x = this.positionScreen.x + vecX * flare.distance; + flare.y = this.positionScreen.y + vecY * flare.distance; + + flare.wantedRotation = flare.x * Math.PI * 0.25; + flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; + + } + +}; + +THREE.LensFlare.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source ); + + this.positionScreen.copy( source.positionScreen ); + this.customUpdateCallback = source.customUpdateCallback; + + for ( var i = 0, l = source.lensFlares.length; i < l; i ++ ) { + + this.lensFlares.push( source.lensFlares[ i ] ); + + } + + return this; + +}; + +// File:src/scenes/Scene.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Scene = function () { + + THREE.Object3D.call( this ); + + this.type = 'Scene'; + + this.fog = null; + this.overrideMaterial = null; + + this.autoUpdate = true; // checked by the renderer + +}; + +THREE.Scene.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Scene.prototype.constructor = THREE.Scene; + +THREE.Scene.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source ); + + if ( source.fog !== null ) this.fog = source.fog.clone(); + if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); + + this.autoUpdate = source.autoUpdate; + this.matrixAutoUpdate = source.matrixAutoUpdate; + + return this; + +}; + +// File:src/scenes/Fog.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Fog = function ( color, near, far ) { + + this.name = ''; + + this.color = new THREE.Color( color ); + + this.near = ( near !== undefined ) ? near : 1; + this.far = ( far !== undefined ) ? far : 1000; + +}; + +THREE.Fog.prototype.clone = function () { + + return new THREE.Fog( this.color.getHex(), this.near, this.far ); + +}; + +// File:src/scenes/FogExp2.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.FogExp2 = function ( color, density ) { + + this.name = ''; + + this.color = new THREE.Color( color ); + this.density = ( density !== undefined ) ? density : 0.00025; + +}; + +THREE.FogExp2.prototype.clone = function () { + + return new THREE.FogExp2( this.color.getHex(), this.density ); + +}; + +// File:src/renderers/shaders/ShaderChunk.js + +THREE.ShaderChunk = {}; + +// File:src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl + +THREE.ShaderChunk[ 'alphamap_fragment'] = "#ifdef USE_ALPHAMAP\n\n diffuseColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl + +THREE.ShaderChunk[ 'alphamap_pars_fragment'] = "#ifdef USE_ALPHAMAP\n\n uniform sampler2D alphaMap;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl + +THREE.ShaderChunk[ 'alphatest_fragment'] = "#ifdef ALPHATEST\n\n if ( diffuseColor.a < ALPHATEST ) discard;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/aomap_fragment.glsl + +THREE.ShaderChunk[ 'aomap_fragment'] = "#ifdef USE_AOMAP\n\n totalAmbientLight *= ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/aomap_pars_fragment.glsl + +THREE.ShaderChunk[ 'aomap_pars_fragment'] = "#ifdef USE_AOMAP\n\n uniform sampler2D aoMap;\n uniform float aoMapIntensity;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/begin_vertex.glsl + +THREE.ShaderChunk[ 'begin_vertex'] = "\nvec3 transformed = vec3( position );\n"; + +// File:src/renderers/shaders/ShaderChunk/beginnormal_vertex.glsl + +THREE.ShaderChunk[ 'beginnormal_vertex'] = "\nvec3 objectNormal = vec3( normal );\n"; + +// File:src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'bumpmap_pars_fragment'] = "#ifdef USE_BUMPMAP\n\n uniform sampler2D bumpMap;\n uniform float bumpScale;\n\n\n\n vec2 dHdxy_fwd() {\n\n vec2 dSTdx = dFdx( vUv );\n vec2 dSTdy = dFdy( vUv );\n\n float Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n return vec2( dBx, dBy );\n\n }\n\n vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n vec3 vSigmaX = dFdx( surf_pos );\n vec3 vSigmaY = dFdy( surf_pos );\n vec3 vN = surf_norm;\n vec3 R1 = cross( vSigmaY, vN );\n vec3 R2 = cross( vN, vSigmaX );\n\n float fDet = dot( vSigmaX, R1 );\n\n vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n return normalize( abs( fDet ) * surf_norm - vGrad );\n\n }\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/color_fragment.glsl + +THREE.ShaderChunk[ 'color_fragment'] = "#ifdef USE_COLOR\n\n diffuseColor.rgb *= vColor;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl + +THREE.ShaderChunk[ 'color_pars_fragment'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl + +THREE.ShaderChunk[ 'color_pars_vertex'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/color_vertex.glsl + +THREE.ShaderChunk[ 'color_vertex'] = "#ifdef USE_COLOR\n\n vColor.xyz = color.xyz;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/common.glsl + +THREE.ShaderChunk[ 'common'] = "#define PI 3.14159\n#define PI2 6.28318\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\n\nvec3 transformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( matrix * vec4( normal, 0.0 ) ).xyz );\n\n}\n\nvec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );\n\n}\n\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n float distance = dot( planeNormal, point - pointOnPlane );\n\n return - distance * planeNormal + point;\n\n}\n\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return sign( dot( point - pointOnPlane, planeNormal ) );\n\n}\n\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n\n}\n\nfloat calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) {\n\n if ( decayExponent > 0.0 ) {\n\n return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\n }\n\n return 1.0;\n\n}\n\nvec3 F_Schlick( in vec3 specularColor, in float dotLH ) {\n\n\n float fresnel = exp2( ( -5.55437 * dotLH - 6.98316 ) * dotLH );\n\n return ( 1.0 - specularColor ) * fresnel + specularColor;\n\n}\n\nfloat G_BlinnPhong_Implicit( /* in float dotNL, in float dotNV */ ) {\n\n\n return 0.25;\n\n}\n\nfloat D_BlinnPhong( in float shininess, in float dotNH ) {\n\n\n return ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n\n}\n\nvec3 BRDF_BlinnPhong( in vec3 specularColor, in float shininess, in vec3 normal, in vec3 lightDir, in vec3 viewDir ) {\n\n vec3 halfDir = normalize( lightDir + viewDir );\n\n float dotNH = saturate( dot( normal, halfDir ) );\n float dotLH = saturate( dot( lightDir, halfDir ) );\n\n vec3 F = F_Schlick( specularColor, dotLH );\n\n float G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );\n\n float D = D_BlinnPhong( shininess, dotNH );\n\n return F * G * D;\n\n}\n\nvec3 inputToLinear( in vec3 a ) {\n\n #ifdef GAMMA_INPUT\n\n return pow( a, vec3( float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n\nvec3 linearToOutput( in vec3 a ) {\n\n #ifdef GAMMA_OUTPUT\n\n return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n"; + +// File:src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl + +THREE.ShaderChunk[ 'defaultnormal_vertex'] = "#ifdef FLIP_SIDED\n\n objectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;\n"; + +// File:src/renderers/shaders/ShaderChunk/displacementmap_vertex.glsl + +THREE.ShaderChunk[ 'displacementmap_vertex'] = "#ifdef USE_DISPLACEMENTMAP\n\n transformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/displacementmap_pars_vertex.glsl + +THREE.ShaderChunk[ 'displacementmap_pars_vertex'] = "#ifdef USE_DISPLACEMENTMAP\n\n uniform sampler2D displacementMap;\n uniform float displacementScale;\n uniform float displacementBias;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl + +THREE.ShaderChunk[ 'emissivemap_fragment'] = "#ifdef USE_EMISSIVEMAP\n\n vec4 emissiveColor = texture2D( emissiveMap, vUv );\n\n emissiveColor.rgb = inputToLinear( emissiveColor.rgb );\n\n totalEmissiveLight *= emissiveColor.rgb;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/emissivemap_pars_fragment.glsl + +THREE.ShaderChunk[ 'emissivemap_pars_fragment'] = "#ifdef USE_EMISSIVEMAP\n\n uniform sampler2D emissiveMap;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl + +THREE.ShaderChunk[ 'envmap_fragment'] = "#ifdef USE_ENVMAP\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n #else\n\n vec3 reflectVec = vReflect;\n\n #endif\n\n #ifdef DOUBLE_SIDED\n float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n #else\n float flipNormal = 1.0;\n #endif\n\n #ifdef ENVMAP_TYPE_CUBE\n vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #elif defined( ENVMAP_TYPE_EQUIREC )\n vec2 sampleUV;\n sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n vec4 envColor = texture2D( envMap, sampleUV );\n\n #elif defined( ENVMAP_TYPE_SPHERE )\n vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n #endif\n\n envColor.xyz = inputToLinear( envColor.xyz );\n\n #ifdef ENVMAP_BLENDING_MULTIPLY\n\n outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_MIX )\n\n outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_ADD )\n\n outgoingLight += envColor.xyz * specularStrength * reflectivity;\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'envmap_pars_fragment'] = "#ifdef USE_ENVMAP\n\n uniform float reflectivity;\n #ifdef ENVMAP_TYPE_CUBE\n uniform samplerCube envMap;\n #else\n uniform sampler2D envMap;\n #endif\n uniform float flipEnvMap;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n uniform float refractionRatio;\n\n #else\n\n varying vec3 vReflect;\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl + +THREE.ShaderChunk[ 'envmap_pars_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n varying vec3 vReflect;\n\n uniform float refractionRatio;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl + +THREE.ShaderChunk[ 'envmap_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vReflect = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/fog_fragment.glsl + +THREE.ShaderChunk[ 'fog_fragment'] = "#ifdef USE_FOG\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n float depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n #else\n\n float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n #endif\n\n #ifdef FOG_EXP2\n\n float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\n\n #else\n\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n\n #endif\n \n outgoingLight = mix( outgoingLight, fogColor, fogFactor );\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl + +THREE.ShaderChunk[ 'fog_pars_fragment'] = "#ifdef USE_FOG\n\n uniform vec3 fogColor;\n\n #ifdef FOG_EXP2\n\n uniform float fogDensity;\n\n #else\n\n uniform float fogNear;\n uniform float fogFar;\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/hemilight_fragment.glsl + +THREE.ShaderChunk[ 'hemilight_fragment'] = "#if MAX_HEMI_LIGHTS > 0\n\n for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lightDir = hemisphereLightDirection[ i ];\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vec3 lightColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n totalAmbientLight += lightColor;\n\n }\n\n#endif\n\n"; + +// File:src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl + +THREE.ShaderChunk[ 'lightmap_fragment'] = "#ifdef USE_LIGHTMAP\n\n totalAmbientLight += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'lightmap_pars_fragment'] = "#ifdef USE_LIGHTMAP\n\n uniform sampler2D lightMap;\n uniform float lightMapIntensity;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl + +THREE.ShaderChunk[ 'lights_lambert_pars_vertex'] = "#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl + +THREE.ShaderChunk[ 'lights_lambert_vertex'] = "vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vec3( 0.0 );\n\n#endif\n\nvec3 normal = normalize( transformedNormal );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lVector = pointLightPosition[ i ] - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n #endif\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n #endif\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * saturate( - dotProduct );\n\n #endif\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lightDir = hemisphereLightDirection[ i ];\n\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n #ifdef DOUBLE_SIDED\n\n float hemiDiffuseWeightBack = - 0.5 * dotProduct + 0.5;\n\n vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n #endif\n\n }\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl + +THREE.ShaderChunk[ 'lights_phong_fragment'] = "vec3 viewDir = normalize( vViewPosition );\n\nvec3 totalDiffuseLight = vec3( 0.0 );\nvec3 totalSpecularLight = vec3( 0.0 );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lightPosition = pointLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * cosineTerm;\n\n }\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl + +THREE.ShaderChunk[ 'lights_phong_pars_fragment'] = "uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\nvarying vec3 vViewPosition;\n\n#ifndef FLAT_SHADED\n\n varying vec3 vNormal;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl + +THREE.ShaderChunk[ 'lights_phong_pars_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_vertex.glsl + +THREE.ShaderChunk[ 'lights_phong_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n vWorldPosition = worldPosition.xyz;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl + +THREE.ShaderChunk[ 'linear_to_gamma_fragment'] = "\n outgoingLight = linearToOutput( outgoingLight );\n"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl + +THREE.ShaderChunk[ 'logdepthbuf_fragment'] = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl + +THREE.ShaderChunk[ 'logdepthbuf_pars_fragment'] = "#ifdef USE_LOGDEPTHBUF\n\n uniform float logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl + +THREE.ShaderChunk[ 'logdepthbuf_pars_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n uniform float logDepthBufFC;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl + +THREE.ShaderChunk[ 'logdepthbuf_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n vFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/map_fragment.glsl + +THREE.ShaderChunk[ 'map_fragment'] = "#ifdef USE_MAP\n\n vec4 texelColor = texture2D( map, vUv );\n\n texelColor.xyz = inputToLinear( texelColor.xyz );\n\n diffuseColor *= texelColor;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl + +THREE.ShaderChunk[ 'map_pars_fragment'] = "#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl + +THREE.ShaderChunk[ 'map_particle_fragment'] = "#ifdef USE_MAP\n\n diffuseColor *= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl + +THREE.ShaderChunk[ 'map_particle_pars_fragment'] = "#ifdef USE_MAP\n\n uniform vec4 offsetRepeat;\n uniform sampler2D map;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl + +THREE.ShaderChunk[ 'morphnormal_vertex'] = "#ifdef USE_MORPHNORMALS\n\n objectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n objectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n objectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n objectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl + +THREE.ShaderChunk[ 'morphtarget_pars_vertex'] = "#ifdef USE_MORPHTARGETS\n\n #ifndef USE_MORPHNORMALS\n\n uniform float morphTargetInfluences[ 8 ];\n\n #else\n\n uniform float morphTargetInfluences[ 4 ];\n\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl + +THREE.ShaderChunk[ 'morphtarget_vertex'] = "#ifdef USE_MORPHTARGETS\n\n transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n #ifndef USE_MORPHNORMALS\n\n transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/normal_phong_fragment.glsl + +THREE.ShaderChunk[ 'normal_phong_fragment'] = "#ifndef FLAT_SHADED\n\n vec3 normal = normalize( vNormal );\n\n #ifdef DOUBLE_SIDED\n\n normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n #endif\n\n#else\n\n vec3 fdx = dFdx( vViewPosition );\n vec3 fdy = dFdy( vViewPosition );\n vec3 normal = normalize( cross( fdx, fdy ) );\n\n#endif\n\n#ifdef USE_NORMALMAP\n\n normal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\n"; + +// File:src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'normalmap_pars_fragment'] = "#ifdef USE_NORMALMAP\n\n uniform sampler2D normalMap;\n uniform vec2 normalScale;\n\n\n vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n vec3 q0 = dFdx( eye_pos.xyz );\n vec3 q1 = dFdy( eye_pos.xyz );\n vec2 st0 = dFdx( vUv.st );\n vec2 st1 = dFdy( vUv.st );\n\n vec3 S = normalize( q0 * st1.t - q1 * st0.t );\n vec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n vec3 N = normalize( surf_norm );\n\n vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n mapN.xy = normalScale * mapN.xy;\n mat3 tsn = mat3( S, T, N );\n return normalize( tsn * mapN );\n\n }\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/project_vertex.glsl + +THREE.ShaderChunk[ 'project_vertex'] = "#ifdef USE_SKINNING\n\n vec4 mvPosition = modelViewMatrix * skinned;\n\n#else\n\n vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;\n"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl + +THREE.ShaderChunk[ 'shadowmap_fragment'] = "#ifdef USE_SHADOWMAP\n\n for ( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n float texelSizeY = 1.0 / shadowMapSize[ i ].y;\n\n float shadow = 0.0;\n\n#if defined( POINT_LIGHT_SHADOWS )\n\n bool isPointLight = shadowDarkness[ i ] < 0.0;\n\n if ( isPointLight ) {\n\n float realShadowDarkness = abs( shadowDarkness[ i ] );\n\n vec3 lightToPosition = vShadowCoord[ i ].xyz;\n\n #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n vec3 bd3D = normalize( lightToPosition );\n float dp = length( lightToPosition );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D, texelSizeY ) ), shadowBias[ i ], shadow );\n\n\n #if defined( SHADOWMAP_TYPE_PCF )\n const float Dr = 1.25;\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n const float Dr = 2.25;\n #endif\n\n float os = Dr * 2.0 * texelSizeY;\n\n const vec3 Gsd = vec3( - 1, 0, 1 );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zyz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xyz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zyx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xyx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n\n shadow *= realShadowDarkness * ( 1.0 / 21.0 );\n\n #else \n vec3 bd3D = normalize( lightToPosition );\n float dp = length( lightToPosition );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D, texelSizeY ) ), shadowBias[ i ], shadow );\n\n shadow *= realShadowDarkness;\n\n #endif\n\n } else {\n\n#endif \n float texelSizeX = 1.0 / shadowMapSize[ i ].x;\n\n vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n\n bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n bool inFrustum = all( inFrustumVec );\n\n bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n bool frustumTest = all( frustumTestVec );\n\n if ( frustumTest ) {\n\n #if defined( SHADOWMAP_TYPE_PCF )\n\n\n /*\n for ( float y = -1.25; y <= 1.25; y += 1.25 )\n for ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n if ( fDepth < shadowCoord.z )\n shadow += 1.0;\n }\n shadow /= 9.0;\n */\n\n shadowCoord.z += shadowBias[ i ];\n\n const float ShadowDelta = 1.0 / 9.0;\n\n float xPixelOffset = texelSizeX;\n float yPixelOffset = texelSizeY;\n\n float dx0 = - 1.25 * xPixelOffset;\n float dy0 = - 1.25 * yPixelOffset;\n float dx1 = 1.25 * xPixelOffset;\n float dy1 = 1.25 * yPixelOffset;\n\n float fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n shadow *= shadowDarkness[ i ];\n\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n\n shadowCoord.z += shadowBias[ i ];\n\n float xPixelOffset = texelSizeX;\n float yPixelOffset = texelSizeY;\n\n float dx0 = - 1.0 * xPixelOffset;\n float dy0 = - 1.0 * yPixelOffset;\n float dx1 = 1.0 * xPixelOffset;\n float dy1 = 1.0 * yPixelOffset;\n\n mat3 shadowKernel;\n mat3 depthKernel;\n\n depthKernel[ 0 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n depthKernel[ 0 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n depthKernel[ 0 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n depthKernel[ 1 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n depthKernel[ 1 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n depthKernel[ 1 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n depthKernel[ 2 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n depthKernel[ 2 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n depthKernel[ 2 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n vec3 shadowZ = vec3( shadowCoord.z );\n shadowKernel[ 0 ] = vec3( lessThan( depthKernel[ 0 ], shadowZ ) );\n shadowKernel[ 0 ] *= vec3( 0.25 );\n\n shadowKernel[ 1 ] = vec3( lessThan( depthKernel[ 1 ], shadowZ ) );\n shadowKernel[ 1 ] *= vec3( 0.25 );\n\n shadowKernel[ 2 ] = vec3( lessThan( depthKernel[ 2 ], shadowZ ) );\n shadowKernel[ 2 ] *= vec3( 0.25 );\n\n vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[ i ].xy );\n\n shadowKernel[ 0 ] = mix( shadowKernel[ 1 ], shadowKernel[ 0 ], fractionalCoord.x );\n shadowKernel[ 1 ] = mix( shadowKernel[ 2 ], shadowKernel[ 1 ], fractionalCoord.x );\n\n vec4 shadowValues;\n shadowValues.x = mix( shadowKernel[ 0 ][ 1 ], shadowKernel[ 0 ][ 0 ], fractionalCoord.y );\n shadowValues.y = mix( shadowKernel[ 0 ][ 2 ], shadowKernel[ 0 ][ 1 ], fractionalCoord.y );\n shadowValues.z = mix( shadowKernel[ 1 ][ 1 ], shadowKernel[ 1 ][ 0 ], fractionalCoord.y );\n shadowValues.w = mix( shadowKernel[ 1 ][ 2 ], shadowKernel[ 1 ][ 1 ], fractionalCoord.y );\n\n shadow = dot( shadowValues, vec4( 1.0 ) ) * shadowDarkness[ i ];\n\n #else \n shadowCoord.z += shadowBias[ i ];\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n shadow = shadowDarkness[ i ];\n\n #endif\n\n }\n\n#ifdef SHADOWMAP_DEBUG\n\n if ( inFrustum ) {\n\n if ( i == 0 ) {\n\n outgoingLight *= vec3( 1.0, 0.5, 0.0 );\n\n } else if ( i == 1 ) {\n\n outgoingLight *= vec3( 0.0, 1.0, 0.8 );\n\n } else {\n\n outgoingLight *= vec3( 0.0, 0.5, 1.0 );\n\n }\n\n }\n\n#endif\n\n#if defined( POINT_LIGHT_SHADOWS )\n\n }\n\n#endif\n\n shadowMask = shadowMask * vec3( 1.0 - shadow );\n\n }\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'shadowmap_pars_fragment'] = "#ifdef USE_SHADOWMAP\n\n uniform sampler2D shadowMap[ MAX_SHADOWS ];\n uniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform float shadowBias[ MAX_SHADOWS ];\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n float unpackDepth( const in vec4 rgba_depth ) {\n\n const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n float depth = dot( rgba_depth, bit_shift );\n return depth;\n\n }\n\n #if defined(POINT_LIGHT_SHADOWS)\n\n\n void adjustShadowValue1K( const float testDepth, const vec4 textureData, const float bias, inout float shadowValue ) {\n\n const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n if ( testDepth >= dot( textureData, bitSh ) * 1000.0 + bias )\n shadowValue += 1.0;\n\n }\n\n\n vec2 cubeToUV( vec3 v, float texelSizeY ) {\n\n\n vec3 absV = abs( v );\n\n\n float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n absV *= scaleToCube;\n\n\n v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\n\n\n vec2 planar = v.xy;\n\n float almostATexel = 1.5 * texelSizeY;\n float almostOne = 1.0 - almostATexel;\n\n if ( absV.z >= almostOne ) {\n\n if ( v.z > 0.0 )\n planar.x = 4.0 - v.x;\n\n } else if ( absV.x >= almostOne ) {\n\n float signX = sign( v.x );\n planar.x = v.z * signX + 2.0 * signX;\n\n } else if ( absV.y >= almostOne ) {\n\n float signY = sign( v.y );\n planar.x = v.x + 2.0 * signY + 2.0;\n planar.y = v.z * signY - 2.0;\n\n }\n\n\n return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\n }\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl + +THREE.ShaderChunk[ 'shadowmap_pars_vertex'] = "#ifdef USE_SHADOWMAP\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform mat4 shadowMatrix[ MAX_SHADOWS ];\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl + +THREE.ShaderChunk[ 'shadowmap_vertex'] = "#ifdef USE_SHADOWMAP\n\n for ( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n }\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl + +THREE.ShaderChunk[ 'skinbase_vertex'] = "#ifdef USE_SKINNING\n\n mat4 boneMatX = getBoneMatrix( skinIndex.x );\n mat4 boneMatY = getBoneMatrix( skinIndex.y );\n mat4 boneMatZ = getBoneMatrix( skinIndex.z );\n mat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl + +THREE.ShaderChunk[ 'skinning_pars_vertex'] = "#ifdef USE_SKINNING\n\n uniform mat4 bindMatrix;\n uniform mat4 bindMatrixInverse;\n\n #ifdef BONE_TEXTURE\n\n uniform sampler2D boneTexture;\n uniform int boneTextureWidth;\n uniform int boneTextureHeight;\n\n mat4 getBoneMatrix( const in float i ) {\n\n float j = i * 4.0;\n float x = mod( j, float( boneTextureWidth ) );\n float y = floor( j / float( boneTextureWidth ) );\n\n float dx = 1.0 / float( boneTextureWidth );\n float dy = 1.0 / float( boneTextureHeight );\n\n y = dy * ( y + 0.5 );\n\n vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n mat4 bone = mat4( v1, v2, v3, v4 );\n\n return bone;\n\n }\n\n #else\n\n uniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n mat4 getBoneMatrix( const in float i ) {\n\n mat4 bone = boneGlobalMatrices[ int(i) ];\n return bone;\n\n }\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/skinning_vertex.glsl + +THREE.ShaderChunk[ 'skinning_vertex'] = "#ifdef USE_SKINNING\n\n vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\n vec4 skinned = vec4( 0.0 );\n skinned += boneMatX * skinVertex * skinWeight.x;\n skinned += boneMatY * skinVertex * skinWeight.y;\n skinned += boneMatZ * skinVertex * skinWeight.z;\n skinned += boneMatW * skinVertex * skinWeight.w;\n skinned = bindMatrixInverse * skinned;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl + +THREE.ShaderChunk[ 'skinnormal_vertex'] = "#ifdef USE_SKINNING\n\n mat4 skinMatrix = mat4( 0.0 );\n skinMatrix += skinWeight.x * boneMatX;\n skinMatrix += skinWeight.y * boneMatY;\n skinMatrix += skinWeight.z * boneMatZ;\n skinMatrix += skinWeight.w * boneMatW;\n skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\n objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl + +THREE.ShaderChunk[ 'specularmap_fragment'] = "float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n vec4 texelSpecular = texture2D( specularMap, vUv );\n specularStrength = texelSpecular.r;\n\n#else\n\n specularStrength = 1.0;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'specularmap_pars_fragment'] = "#ifdef USE_SPECULARMAP\n\n uniform sampler2D specularMap;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/uv2_pars_fragment.glsl + +THREE.ShaderChunk[ 'uv2_pars_fragment'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n varying vec2 vUv2;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/uv2_pars_vertex.glsl + +THREE.ShaderChunk[ 'uv2_pars_vertex'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n attribute vec2 uv2;\n varying vec2 vUv2;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/uv2_vertex.glsl + +THREE.ShaderChunk[ 'uv2_vertex'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n vUv2 = uv2;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl + +THREE.ShaderChunk[ 'uv_pars_fragment'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl + +THREE.ShaderChunk[ 'uv_pars_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n uniform vec4 offsetRepeat;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/uv_vertex.glsl + +THREE.ShaderChunk[ 'uv_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl + +THREE.ShaderChunk[ 'worldpos_vertex'] = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n #ifdef USE_SKINNING\n\n vec4 worldPosition = modelMatrix * skinned;\n\n #else\n\n vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/UniformsUtils.js + +/** + * Uniform Utilities + */ + +THREE.UniformsUtils = { + + merge: function ( uniforms ) { + + var merged = {}; + + for ( var u = 0; u < uniforms.length; u ++ ) { + + var tmp = this.clone( uniforms[ u ] ); + + for ( var p in tmp ) { + + merged[ p ] = tmp[ p ]; + + } + + } + + return merged; + + }, + + clone: function ( uniforms_src ) { + + var uniforms_dst = {}; + + for ( var u in uniforms_src ) { + + uniforms_dst[ u ] = {}; + + for ( var p in uniforms_src[ u ] ) { + + var parameter_src = uniforms_src[ u ][ p ]; + + if ( parameter_src instanceof THREE.Color || + parameter_src instanceof THREE.Vector2 || + parameter_src instanceof THREE.Vector3 || + parameter_src instanceof THREE.Vector4 || + parameter_src instanceof THREE.Matrix3 || + parameter_src instanceof THREE.Matrix4 || + parameter_src instanceof THREE.Texture ) { + + uniforms_dst[ u ][ p ] = parameter_src.clone(); + + } else if ( Array.isArray( parameter_src ) ) { + + uniforms_dst[ u ][ p ] = parameter_src.slice(); + + } else { + + uniforms_dst[ u ][ p ] = parameter_src; + + } + + } + + } + + return uniforms_dst; + + } + +}; + +// File:src/renderers/shaders/UniformsLib.js + +/** + * Uniforms library for shared webgl shaders + */ + +THREE.UniformsLib = { + + common: { + + "diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity" : { type: "f", value: 1.0 }, + + "map" : { type: "t", value: null }, + "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, + + "specularMap" : { type: "t", value: null }, + "alphaMap" : { type: "t", value: null }, + + "envMap" : { type: "t", value: null }, + "flipEnvMap" : { type: "f", value: - 1 }, + "reflectivity" : { type: "f", value: 1.0 }, + "refractionRatio" : { type: "f", value: 0.98 } + + }, + + aomap: { + + "aoMap" : { type: "t", value: null }, + "aoMapIntensity" : { type: "f", value: 1 }, + + }, + + lightmap: { + + "lightMap" : { type: "t", value: null }, + "lightMapIntensity" : { type: "f", value: 1 }, + + }, + + emissivemap: { + + "emissiveMap" : { type: "t", value: null }, + + }, + + bumpmap: { + + "bumpMap" : { type: "t", value: null }, + "bumpScale" : { type: "f", value: 1 } + + }, + + normalmap: { + + "normalMap" : { type: "t", value: null }, + "normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) } + + }, + + displacementmap: { + + "displacementMap" : { type: "t", value: null }, + "displacementScale" : { type: "f", value: 1 }, + "displacementBias" : { type: "f", value: 0 } + + }, + + fog : { + + "fogDensity" : { type: "f", value: 0.00025 }, + "fogNear" : { type: "f", value: 1 }, + "fogFar" : { type: "f", value: 2000 }, + "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } + + }, + + lights: { + + "ambientLightColor" : { type: "fv", value: [] }, + + "directionalLightDirection" : { type: "fv", value: [] }, + "directionalLightColor" : { type: "fv", value: [] }, + + "hemisphereLightDirection" : { type: "fv", value: [] }, + "hemisphereLightSkyColor" : { type: "fv", value: [] }, + "hemisphereLightGroundColor" : { type: "fv", value: [] }, + + "pointLightColor" : { type: "fv", value: [] }, + "pointLightPosition" : { type: "fv", value: [] }, + "pointLightDistance" : { type: "fv1", value: [] }, + "pointLightDecay" : { type: "fv1", value: [] }, + + "spotLightColor" : { type: "fv", value: [] }, + "spotLightPosition" : { type: "fv", value: [] }, + "spotLightDirection" : { type: "fv", value: [] }, + "spotLightDistance" : { type: "fv1", value: [] }, + "spotLightAngleCos" : { type: "fv1", value: [] }, + "spotLightExponent" : { type: "fv1", value: [] }, + "spotLightDecay" : { type: "fv1", value: [] } + + }, + + points: { + + "psColor" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity" : { type: "f", value: 1.0 }, + "size" : { type: "f", value: 1.0 }, + "scale" : { type: "f", value: 1.0 }, + "map" : { type: "t", value: null }, + "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, + + "fogDensity" : { type: "f", value: 0.00025 }, + "fogNear" : { type: "f", value: 1 }, + "fogFar" : { type: "f", value: 2000 }, + "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } + + }, + + shadowmap: { + + "shadowMap": { type: "tv", value: [] }, + "shadowMapSize": { type: "v2v", value: [] }, + + "shadowBias" : { type: "fv1", value: [] }, + "shadowDarkness": { type: "fv1", value: [] }, + + "shadowMatrix" : { type: "m4v", value: [] } + + } + +}; + +// File:src/renderers/shaders/ShaderLib.js + +/** + * Webgl Shader Library for three.js + * + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + */ + + +THREE.ShaderLib = { + + 'basic': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "aomap" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "shadowmap" ] + + ] ), + + vertexShader: [ + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "uv_pars_vertex" ], + THREE.ShaderChunk[ "uv2_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "uv_vertex" ], + THREE.ShaderChunk[ "uv2_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + + " #ifdef USE_ENVMAP", + + THREE.ShaderChunk[ "beginnormal_vertex" ], + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], + + " #endif", + + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform float opacity;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "uv_pars_fragment" ], + THREE.ShaderChunk[ "uv2_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "aomap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vec4( diffuse, opacity );", + " vec3 totalAmbientLight = vec3( 1.0 );", // hardwired + " vec3 shadowMask = vec3( 1.0 );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + THREE.ShaderChunk[ "aomap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + " outgoingLight = diffuseColor.rgb * totalAmbientLight * shadowMask;", + + THREE.ShaderChunk[ "envmap_fragment" ], + + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + + THREE.ShaderChunk[ "fog_fragment" ], + + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + + "}" + + ].join( "\n" ) + + }, + + 'lambert': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], + + { + "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) } + } + + ] ), + + vertexShader: [ + + "#define LAMBERT", + + "varying vec3 vLightFront;", + + "#ifdef DOUBLE_SIDED", + + " varying vec3 vLightBack;", + + "#endif", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "uv_pars_vertex" ], + THREE.ShaderChunk[ "uv2_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "lights_lambert_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "uv_vertex" ], + THREE.ShaderChunk[ "uv2_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + + THREE.ShaderChunk[ "beginnormal_vertex" ], + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], + + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "lights_lambert_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform vec3 emissive;", + "uniform float opacity;", + + "uniform vec3 ambientLightColor;", + + "varying vec3 vLightFront;", + + "#ifdef DOUBLE_SIDED", + + " varying vec3 vLightBack;", + + "#endif", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "uv_pars_fragment" ], + THREE.ShaderChunk[ "uv2_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " vec3 outgoingLight = vec3( 0.0 );", // outgoing light does not have an alpha, the surface does + " vec4 diffuseColor = vec4( diffuse, opacity );", + " vec3 totalAmbientLight = ambientLightColor;", + " vec3 shadowMask = vec3( 1.0 );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + " #ifdef DOUBLE_SIDED", + + " if ( gl_FrontFacing )", + " outgoingLight += diffuseColor.rgb * ( vLightFront * shadowMask + totalAmbientLight ) + emissive;", + " else", + " outgoingLight += diffuseColor.rgb * ( vLightBack * shadowMask + totalAmbientLight ) + emissive;", + + " #else", + + " outgoingLight += diffuseColor.rgb * ( vLightFront * shadowMask + totalAmbientLight ) + emissive;", + + " #endif", + + THREE.ShaderChunk[ "envmap_fragment" ], + + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + + THREE.ShaderChunk[ "fog_fragment" ], + + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + + "}" + + ].join( "\n" ) + + }, + + 'phong': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "aomap" ], + THREE.UniformsLib[ "lightmap" ], + THREE.UniformsLib[ "emissivemap" ], + THREE.UniformsLib[ "bumpmap" ], + THREE.UniformsLib[ "normalmap" ], + THREE.UniformsLib[ "displacementmap" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], + + { + "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, + "specular" : { type: "c", value: new THREE.Color( 0x111111 ) }, + "shininess": { type: "f", value: 30 } + } + + ] ), + + vertexShader: [ + + "#define PHONG", + + "varying vec3 vViewPosition;", + + "#ifndef FLAT_SHADED", + + " varying vec3 vNormal;", + + "#endif", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "uv_pars_vertex" ], + THREE.ShaderChunk[ "uv2_pars_vertex" ], + THREE.ShaderChunk[ "displacementmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "lights_phong_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "uv_vertex" ], + THREE.ShaderChunk[ "uv2_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + + THREE.ShaderChunk[ "beginnormal_vertex" ], + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], + + "#ifndef FLAT_SHADED", // Normal computed with derivatives when FLAT_SHADED + + " vNormal = normalize( transformedNormal );", + + "#endif", + + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "displacementmap_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + " vViewPosition = - mvPosition.xyz;", + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "lights_phong_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "#define PHONG", + + "uniform vec3 diffuse;", + "uniform vec3 emissive;", + "uniform vec3 specular;", + "uniform float shininess;", + "uniform float opacity;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "uv_pars_fragment" ], + THREE.ShaderChunk[ "uv2_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "aomap_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "emissivemap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "lights_phong_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "bumpmap_pars_fragment" ], + THREE.ShaderChunk[ "normalmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vec4( diffuse, opacity );", + " vec3 totalAmbientLight = ambientLightColor;", + " vec3 totalEmissiveLight = emissive;", + " vec3 shadowMask = vec3( 1.0 );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + THREE.ShaderChunk[ "normal_phong_fragment" ], + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "hemilight_fragment" ], + THREE.ShaderChunk[ "aomap_fragment" ], + THREE.ShaderChunk[ "emissivemap_fragment" ], + + THREE.ShaderChunk[ "lights_phong_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + "totalDiffuseLight *= shadowMask;", + "totalSpecularLight *= shadowMask;", + + "#ifdef METAL", + + " outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) * specular + totalSpecularLight + totalEmissiveLight;", + + "#else", + + " outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) + totalSpecularLight + totalEmissiveLight;", + + "#endif", + + THREE.ShaderChunk[ "envmap_fragment" ], + + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + + THREE.ShaderChunk[ "fog_fragment" ], + + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + + "}" + + ].join( "\n" ) + + }, + + 'points': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "points" ], + THREE.UniformsLib[ "shadowmap" ] + + ] ), + + vertexShader: [ + + "uniform float size;", + "uniform float scale;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "color_vertex" ], + + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + + " #ifdef USE_SIZEATTENUATION", + " gl_PointSize = size * ( scale / length( mvPosition.xyz ) );", + " #else", + " gl_PointSize = size;", + " #endif", + + " gl_Position = projectionMatrix * mvPosition;", + + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 psColor;", + "uniform float opacity;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_particle_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vec4( psColor, opacity );", + " vec3 shadowMask = vec3( 1.0 );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_particle_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + " outgoingLight = diffuseColor.rgb * shadowMask;", + + THREE.ShaderChunk[ "fog_fragment" ], + + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + + "}" + + ].join( "\n" ) + + }, + + 'dashed': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + + { + "scale" : { type: "f", value: 1 }, + "dashSize" : { type: "f", value: 1 }, + "totalSize": { type: "f", value: 2 } + } + + ] ), + + vertexShader: [ + + "uniform float scale;", + "attribute float lineDistance;", + + "varying float vLineDistance;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "color_vertex" ], + + " vLineDistance = scale * lineDistance;", + + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + " gl_Position = projectionMatrix * mvPosition;", + + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform float opacity;", + + "uniform float dashSize;", + "uniform float totalSize;", + + "varying float vLineDistance;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " if ( mod( vLineDistance, totalSize ) > dashSize ) {", + + " discard;", + + " }", + + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vec4( diffuse, opacity );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + + " outgoingLight = diffuseColor.rgb;", // simple shader + + THREE.ShaderChunk[ "fog_fragment" ], + + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + + "}" + + ].join( "\n" ) + + }, + + 'depth': { + + uniforms: { + + "mNear": { type: "f", value: 1.0 }, + "mFar" : { type: "f", value: 2000.0 }, + "opacity" : { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform float mNear;", + "uniform float mFar;", + "uniform float opacity;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + " #ifdef USE_LOGDEPTHBUF_EXT", + + " float depth = gl_FragDepthEXT / gl_FragCoord.w;", + + " #else", + + " float depth = gl_FragCoord.z / gl_FragCoord.w;", + + " #endif", + + " float color = 1.0 - smoothstep( mNear, mFar, depth );", + " gl_FragColor = vec4( vec3( color ), opacity );", + + "}" + + ].join( "\n" ) + + }, + + 'normal': { + + uniforms: { + + "opacity" : { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + "varying vec3 vNormal;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + " vNormal = normalize( normalMatrix * normal );", + + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform float opacity;", + "varying vec3 vNormal;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + "}" + + ].join( "\n" ) + + }, + + /* ------------------------------------------------------------------------- + // Cube map shader + ------------------------------------------------------------------------- */ + + 'cube': { + + uniforms: { "tCube": { type: "t", value: null }, + "tFlip": { type: "f", value: - 1 } }, + + vertexShader: [ + + "varying vec3 vWorldPosition;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + " vWorldPosition = transformDirection( position, modelMatrix );", + + " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform samplerCube tCube;", + "uniform float tFlip;", + + "varying vec3 vWorldPosition;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + "}" + + ].join( "\n" ) + + }, + + /* ------------------------------------------------------------------------- + // Cube map shader + ------------------------------------------------------------------------- */ + + 'equirect': { + + uniforms: { "tEquirect": { type: "t", value: null }, + "tFlip": { type: "f", value: - 1 } }, + + vertexShader: [ + + "varying vec3 vWorldPosition;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + " vWorldPosition = transformDirection( position, modelMatrix );", + + " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tEquirect;", + "uniform float tFlip;", + + "varying vec3 vWorldPosition;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + // " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", + "vec3 direction = normalize( vWorldPosition );", + "vec2 sampleUV;", + "sampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );", + "sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;", + "gl_FragColor = texture2D( tEquirect, sampleUV );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + "}" + + ].join( "\n" ) + + }, + + /* Depth encoding into RGBA texture + * + * based on SpiderGL shadow map example + * http://spidergl.org/example.php?id=6 + * + * originally from + * http://www.gamedev.net/topic/442138-packing-a-float-into-a-a8r8g8b8-texture-shader/page__whichpage__1%25EF%25BF%25BD + * + * see also + * http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ + */ + + 'depthRGBA': { + + uniforms: {}, + + vertexShader: [ + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "skinbase_vertex" ], + + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "vec4 pack_depth( const in float depth ) {", + + " const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );", + " const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );", + " vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );", // " vec4 res = fract( depth * bit_shift );", + " res -= res.xxyz * bit_mask;", + " return res;", + + "}", + + "void main() {", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + " #ifdef USE_LOGDEPTHBUF_EXT", + + " gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );", + + " #else", + + " gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );", + + " #endif", + + //"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z / gl_FragCoord.w );", + //"float z = ( ( gl_FragCoord.z / gl_FragCoord.w ) - 3.0 ) / ( 4000.0 - 3.0 );", + //"gl_FragData[ 0 ] = pack_depth( z );", + //"gl_FragData[ 0 ] = vec4( z, z, z, 1.0 );", + + "}" + + ].join( "\n" ) + + }, + + + 'distanceRGBA': { + + uniforms: { + + "lightPos": { type: "v3", value: new THREE.Vector3( 0, 0, 0 ) } + + }, + + vertexShader: [ + + "varying vec4 vWorldPosition;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "worldpos_vertex" ], + + "vWorldPosition = worldPosition;", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 lightPos;", + "varying vec4 vWorldPosition;", + + THREE.ShaderChunk[ "common" ], + + "vec4 pack1K ( float depth ) {", + + " depth /= 1000.0;", + " const vec4 bitSh = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );", + " const vec4 bitMsk = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );", + " vec4 res = fract( depth * bitSh );", + " res -= res.xxyz * bitMsk;", + " return res; ", + + "}", + + "float unpack1K ( vec4 color ) {", + + " const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );", + " return dot( color, bitSh ) * 1000.0;", + + "}", + + "void main () {", + + " gl_FragColor = pack1K( length( vWorldPosition.xyz - lightPos.xyz ) );", + + "}" + + ].join( "\n" ) + + } + +}; + +// File:src/renderers/WebGLRenderer.js + +/** + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ + +THREE.WebGLRenderer = function ( parameters ) { + + console.log( 'THREE.WebGLRenderer', THREE.REVISION ); + + parameters = parameters || {}; + + var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ), + _context = parameters.context !== undefined ? parameters.context : null, + + _width = _canvas.width, + _height = _canvas.height, + + pixelRatio = 1, + + _alpha = parameters.alpha !== undefined ? parameters.alpha : false, + _depth = parameters.depth !== undefined ? parameters.depth : true, + _stencil = parameters.stencil !== undefined ? parameters.stencil : true, + _antialias = parameters.antialias !== undefined ? parameters.antialias : false, + _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, + _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, + + _clearColor = new THREE.Color( 0x000000 ), + _clearAlpha = 0; + + var lights = []; + + var opaqueObjects = []; + var opaqueObjectsLastIndex = - 1; + var transparentObjects = []; + var transparentObjectsLastIndex = - 1; + + var morphInfluences = new Float32Array( 8 ); + + + var sprites = []; + var lensFlares = []; + + // public properties + + this.domElement = _canvas; + this.context = null; + + // clearing + + this.autoClear = true; + this.autoClearColor = true; + this.autoClearDepth = true; + this.autoClearStencil = true; + + // scene graph + + this.sortObjects = true; + + // physically based shading + + this.gammaFactor = 2.0; // for backwards compatibility + this.gammaInput = false; + this.gammaOutput = false; + + // morphs + + this.maxMorphTargets = 8; + this.maxMorphNormals = 4; + + // flags + + this.autoScaleCubemaps = true; + + // internal properties + + var _this = this, + + // internal state cache + + _currentProgram = null, + _currentFramebuffer = null, + _currentMaterialId = - 1, + _currentGeometryProgram = '', + _currentCamera = null, + + _usedTextureUnits = 0, + + _viewportX = 0, + _viewportY = 0, + _viewportWidth = _canvas.width, + _viewportHeight = _canvas.height, + _currentWidth = 0, + _currentHeight = 0, + + // frustum + + _frustum = new THREE.Frustum(), + + // camera matrices cache + + _projScreenMatrix = new THREE.Matrix4(), + + _vector3 = new THREE.Vector3(), + + // light arrays cache + + _direction = new THREE.Vector3(), + + _lightsNeedUpdate = true, + + _lights = { + + ambient: [ 0, 0, 0 ], + directional: { length: 0, colors: [], positions: [] }, + point: { length: 0, colors: [], positions: [], distances: [], decays: [] }, + spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [], decays: [] }, + hemi: { length: 0, skyColors: [], groundColors: [], positions: [] } + + }, + + // info + + _infoMemory = { + + geometries: 0, + textures: 0 + + }, + + _infoRender = { + + calls: 0, + vertices: 0, + faces: 0, + points: 0 + + }; + + this.info = { + + render: _infoRender, + memory: _infoMemory, + programs: null + + }; + + + // initialize + + var _gl; + + try { + + var attributes = { + alpha: _alpha, + depth: _depth, + stencil: _stencil, + antialias: _antialias, + premultipliedAlpha: _premultipliedAlpha, + preserveDrawingBuffer: _preserveDrawingBuffer + }; + + _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); + + if ( _gl === null ) { + + if ( _canvas.getContext( 'webgl' ) !== null ) { + + throw 'Error creating WebGL context with your selected attributes.'; + + } else { + + throw 'Error creating WebGL context.'; + + } + + } + + _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); + + } catch ( error ) { + + console.error( 'THREE.WebGLRenderer: ' + error ); + + } + + var extensions = new THREE.WebGLExtensions( _gl ); + + extensions.get( 'OES_texture_float' ); + extensions.get( 'OES_texture_float_linear' ); + extensions.get( 'OES_texture_half_float' ); + extensions.get( 'OES_texture_half_float_linear' ); + extensions.get( 'OES_standard_derivatives' ); + extensions.get( 'ANGLE_instanced_arrays' ); + + if ( extensions.get( 'OES_element_index_uint' ) ) { + + THREE.BufferGeometry.MaxIndex = 4294967296; + + } + + var capabilities = new THREE.WebGLCapabilities( _gl, extensions, parameters ); + + var state = new THREE.WebGLState( _gl, extensions, paramThreeToGL ); + var properties = new THREE.WebGLProperties(); + var objects = new THREE.WebGLObjects( _gl, properties, this.info ); + var programCache = new THREE.WebGLPrograms( this, capabilities ); + + this.info.programs = programCache.programs; + + var bufferRenderer = new THREE.WebGLBufferRenderer( _gl, extensions, _infoRender ); + var indexedBufferRenderer = new THREE.WebGLIndexedBufferRenderer( _gl, extensions, _infoRender ); + + // + + function glClearColor( r, g, b, a ) { + + if ( _premultipliedAlpha === true ) { + + r *= a; g *= a; b *= a; + + } + + _gl.clearColor( r, g, b, a ); + + } + + function setDefaultGLState() { + + state.init(); + + _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + + glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + } + + function resetGLState() { + + _currentProgram = null; + _currentCamera = null; + + _currentGeometryProgram = ''; + _currentMaterialId = - 1; + + _lightsNeedUpdate = true; + + state.reset(); + + } + + setDefaultGLState(); + + this.context = _gl; + this.capabilities = capabilities; + this.extensions = extensions; + this.state = state; + + // shadow map + + var shadowMap = new THREE.WebGLShadowMap( this, lights, objects ); + + this.shadowMap = shadowMap; + + + // Plugins + + var spritePlugin = new THREE.SpritePlugin( this, sprites ); + var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares ); + + // API + + this.getContext = function () { + + return _gl; + + }; + + this.getContextAttributes = function () { + + return _gl.getContextAttributes(); + + }; + + this.forceContextLoss = function () { + + extensions.get( 'WEBGL_lose_context' ).loseContext(); + + }; + + this.getMaxAnisotropy = ( function () { + + var value; + + return function getMaxAnisotropy() { + + if ( value !== undefined ) return value; + + var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); + + if ( extension !== null ) { + + value = _gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); + + } else { + + value = 0; + + } + + return value; + + } + + } )(); + + this.getPrecision = function () { + + return capabilities.precision; + + }; + + this.getPixelRatio = function () { + + return pixelRatio; + + }; + + this.setPixelRatio = function ( value ) { + + if ( value !== undefined ) pixelRatio = value; + + }; + + this.getSize = function () { + + return { + width: _width, + height: _height + }; + + }; + + this.setSize = function ( width, height, updateStyle ) { + + _width = width; + _height = height; + + _canvas.width = width * pixelRatio; + _canvas.height = height * pixelRatio; + + if ( updateStyle !== false ) { + + _canvas.style.width = width + 'px'; + _canvas.style.height = height + 'px'; + + } + + this.setViewport( 0, 0, width, height ); + + }; + + this.setViewport = function ( x, y, width, height ) { + + _viewportX = x * pixelRatio; + _viewportY = y * pixelRatio; + + _viewportWidth = width * pixelRatio; + _viewportHeight = height * pixelRatio; + + _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + + }; + + this.getViewport = function ( dimensions ) { + + dimensions.x = _viewportX / pixelRatio; + dimensions.y = _viewportY / pixelRatio; + + dimensions.z = _viewportWidth / pixelRatio; + dimensions.w = _viewportHeight / pixelRatio; + + }; + + this.setScissor = function ( x, y, width, height ) { + + _gl.scissor( + x * pixelRatio, + y * pixelRatio, + width * pixelRatio, + height * pixelRatio + ); + + }; + + this.enableScissorTest = function ( boolean ) { + + state.setScissorTest( boolean ); + + }; + + // Clearing + + this.getClearColor = function () { + + return _clearColor; + + }; + + this.setClearColor = function ( color, alpha ) { + + _clearColor.set( color ); + + _clearAlpha = alpha !== undefined ? alpha : 1; + + glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + }; + + this.getClearAlpha = function () { + + return _clearAlpha; + + }; + + this.setClearAlpha = function ( alpha ) { + + _clearAlpha = alpha; + + glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + }; + + this.clear = function ( color, depth, stencil ) { + + var bits = 0; + + if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; + if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; + if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; + + _gl.clear( bits ); + + }; + + this.clearColor = function () { + + _gl.clear( _gl.COLOR_BUFFER_BIT ); + + }; + + this.clearDepth = function () { + + _gl.clear( _gl.DEPTH_BUFFER_BIT ); + + }; + + this.clearStencil = function () { + + _gl.clear( _gl.STENCIL_BUFFER_BIT ); + + }; + + this.clearTarget = function ( renderTarget, color, depth, stencil ) { + + this.setRenderTarget( renderTarget ); + this.clear( color, depth, stencil ); + + }; + + // Reset + + this.resetGLState = resetGLState; + + this.dispose = function() { + + _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); + + }; + + // Events + + function onContextLost( event ) { + + event.preventDefault(); + + resetGLState(); + setDefaultGLState(); + + properties.clear(); + + }; + + function onTextureDispose( event ) { + + var texture = event.target; + + texture.removeEventListener( 'dispose', onTextureDispose ); + + deallocateTexture( texture ); + + _infoMemory.textures --; + + + } + + function onRenderTargetDispose( event ) { + + var renderTarget = event.target; + + renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); + + deallocateRenderTarget( renderTarget ); + + _infoMemory.textures --; + + } + + function onMaterialDispose( event ) { + + var material = event.target; + + material.removeEventListener( 'dispose', onMaterialDispose ); + + deallocateMaterial( material ); + + } + + // Buffer deallocation + + function deallocateTexture( texture ) { + + var textureProperties = properties.get( texture ); + + if ( texture.image && textureProperties.__image__webglTextureCube ) { + + // cube texture + + _gl.deleteTexture( textureProperties.__image__webglTextureCube ); + + } else { + + // 2D texture + + if ( textureProperties.__webglInit === undefined ) return; + + _gl.deleteTexture( textureProperties.__webglTexture ); + + } + + // remove all webgl properties + properties.delete( texture ); + + } + + function deallocateRenderTarget( renderTarget ) { + + var renderTargetProperties = properties.get( renderTarget ); + var textureProperties = properties.get( renderTarget.texture ); + + if ( ! renderTarget || textureProperties.__webglTexture === undefined ) return; + + _gl.deleteTexture( textureProperties.__webglTexture ); + + if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { + + for ( var i = 0; i < 6; i ++ ) { + + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); + _gl.deleteRenderbuffer( renderTargetProperties.__webglRenderbuffer[ i ] ); + + } + + } else { + + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); + _gl.deleteRenderbuffer( renderTargetProperties.__webglRenderbuffer ); + + } + + properties.delete( renderTarget.texture ); + properties.delete( renderTarget ); + + } + + function deallocateMaterial( material ) { + + releaseMaterialProgramReference( material ); + + properties.delete( material ); + + } + + + function releaseMaterialProgramReference( material ) { + + var programInfo = properties.get( material ).program; + + material.program = undefined; + + if ( programInfo !== undefined ) { + + programCache.releaseProgram( programInfo ); + + } + + } + + // Buffer rendering + + this.renderBufferImmediate = function ( object, program, material ) { + + state.initAttributes(); + + var buffers = properties.get( object ); + + if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); + if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); + if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); + if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); + + var attributes = program.getAttributes(); + + if ( object.hasPositions ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); + + state.enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal ); + + if ( material.type !== 'MeshPhongMaterial' && material.shading === THREE.FlatShading ) { + + for ( var i = 0, l = object.count * 3; i < l; i += 9 ) { + + var array = object.normalArray; + + var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3; + var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3; + var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3; + + array[ i + 0 ] = nx; + array[ i + 1 ] = ny; + array[ i + 2 ] = nz; + + array[ i + 3 ] = nx; + array[ i + 4 ] = ny; + array[ i + 5 ] = nz; + + array[ i + 6 ] = nx; + array[ i + 7 ] = ny; + array[ i + 8 ] = nz; + + } + + } + + _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); + + state.enableAttribute( attributes.normal ); + + _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasUvs && material.map ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); + + state.enableAttribute( attributes.uv ); + + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasColors && material.vertexColors !== THREE.NoColors ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); + + state.enableAttribute( attributes.color ); + + _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); + + } + + state.disableUnusedAttributes(); + + _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); + + object.count = 0; + + }; + + this.renderBufferDirect = function ( camera, lights, fog, geometry, material, object, group ) { + + setMaterial( material ); + + var program = setProgram( camera, lights, fog, material, object ); + + var updateBuffers = false; + var geometryProgram = geometry.id + '_' + program.id + '_' + material.wireframe; + + if ( geometryProgram !== _currentGeometryProgram ) { + + _currentGeometryProgram = geometryProgram; + updateBuffers = true; + + } + + // morph targets + + var morphTargetInfluences = object.morphTargetInfluences; + + if ( morphTargetInfluences !== undefined ) { + + var activeInfluences = []; + + for ( var i = 0, l = morphTargetInfluences.length; i < l; i ++ ) { + + var influence = morphTargetInfluences[ i ]; + activeInfluences.push( [ influence, i ] ); + + } + + activeInfluences.sort( numericalSort ); + + if ( activeInfluences.length > 8 ) { + + activeInfluences.length = 8; + + } + + var morphAttributes = geometry.morphAttributes; + + for ( var i = 0, l = activeInfluences.length; i < l; i ++ ) { + + var influence = activeInfluences[ i ]; + morphInfluences[ i ] = influence[ 0 ]; + + if ( influence[ 0 ] !== 0 ) { + + var index = influence[ 1 ]; + + if ( material.morphTargets === true && morphAttributes.position ) geometry.addAttribute( 'morphTarget' + i, morphAttributes.position[ index ] ); + if ( material.morphNormals === true && morphAttributes.normal ) geometry.addAttribute( 'morphNormal' + i, morphAttributes.normal[ index ] ); + + } else { + + if ( material.morphTargets === true ) geometry.removeAttribute( 'morphTarget' + i ); + if ( material.morphNormals === true ) geometry.removeAttribute( 'morphNormal' + i ); + + } + + } + + var uniforms = program.getUniforms(); + + if ( uniforms.morphTargetInfluences !== null ) { + + _gl.uniform1fv( uniforms.morphTargetInfluences, morphInfluences ); + + } + + updateBuffers = true; + + } + + // + + var index = geometry.index; + var position = geometry.attributes.position; + + if ( material.wireframe === true ) { + + index = objects.getWireframeAttribute( geometry ); + + } + + var renderer; + + if ( index !== null ) { + + renderer = indexedBufferRenderer; + renderer.setIndex( index ); + + } else { + + renderer = bufferRenderer; + + } + + if ( updateBuffers ) { + + setupVertexAttributes( material, program, geometry ); + + if ( index !== null ) { + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, objects.getAttributeBuffer( index ) ); + + } + + } + + // + + var dataStart = 0; + var dataCount = Infinity; + + if ( index !== null ) { + + dataCount = index.count + + } else if ( position !== undefined ) { + + dataCount = position.count; + + } + + var rangeStart = geometry.drawRange.start; + var rangeCount = geometry.drawRange.count; + + var groupStart = group !== null ? group.start : 0; + var groupCount = group !== null ? group.count : Infinity; + + var drawStart = Math.max( dataStart, rangeStart, groupStart ); + var drawEnd = Math.min( dataStart + dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; + + var drawCount = Math.max( 0, drawEnd - drawStart + 1 ); + + // + + if ( object instanceof THREE.Mesh ) { + + if ( material.wireframe === true ) { + + state.setLineWidth( material.wireframeLinewidth * pixelRatio ); + renderer.setMode( _gl.LINES ); + + } else { + + renderer.setMode( _gl.TRIANGLES ); + + } + + if ( geometry instanceof THREE.InstancedBufferGeometry && geometry.maxInstancedCount > 0 ) { + + renderer.renderInstances( geometry ); + + } else { + + renderer.render( drawStart, drawCount ); + + } + + } else if ( object instanceof THREE.Line ) { + + var lineWidth = material.linewidth; + + if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material + + state.setLineWidth( lineWidth * pixelRatio ); + + if ( object instanceof THREE.LineSegments ) { + + renderer.setMode( _gl.LINES ); + + } else { + + renderer.setMode( _gl.LINE_STRIP ); + + } + + renderer.render( drawStart, drawCount ); + + } else if ( object instanceof THREE.Points ) { + + renderer.setMode( _gl.POINTS ); + renderer.render( drawStart, drawCount ); + + } + + }; + + function setupVertexAttributes( material, program, geometry, startIndex ) { + + var extension; + + if ( geometry instanceof THREE.InstancedBufferGeometry ) { + + extension = extensions.get( 'ANGLE_instanced_arrays' ); + + if ( extension === null ) { + + console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; + + } + + } + + if ( startIndex === undefined ) startIndex = 0; + + state.initAttributes(); + + var geometryAttributes = geometry.attributes; + + var programAttributes = program.getAttributes(); + + var materialDefaultAttributeValues = material.defaultAttributeValues; + + for ( var name in programAttributes ) { + + var programAttribute = programAttributes[ name ]; + + if ( programAttribute >= 0 ) { + + var geometryAttribute = geometryAttributes[ name ]; + + if ( geometryAttribute !== undefined ) { + + var size = geometryAttribute.itemSize; + var buffer = objects.getAttributeBuffer( geometryAttribute ); + + if ( geometryAttribute instanceof THREE.InterleavedBufferAttribute ) { + + var data = geometryAttribute.data; + var stride = data.stride; + var offset = geometryAttribute.offset; + + if ( data instanceof THREE.InstancedInterleavedBuffer ) { + + state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute, extension ); + + if ( geometry.maxInstancedCount === undefined ) { + + geometry.maxInstancedCount = data.meshPerAttribute * data.count; + + } + + } else { + + state.enableAttribute( programAttribute ); + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); + _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, stride * data.array.BYTES_PER_ELEMENT, ( startIndex * stride + offset ) * data.array.BYTES_PER_ELEMENT ); + + } else { + + if ( geometryAttribute instanceof THREE.InstancedBufferAttribute ) { + + state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute, extension ); + + if ( geometry.maxInstancedCount === undefined ) { + + geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; + + } + + } else { + + state.enableAttribute( programAttribute ); + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); + _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32 + + } + + } else if ( materialDefaultAttributeValues !== undefined ) { + + var value = materialDefaultAttributeValues[ name ]; + + if ( value !== undefined ) { + + switch ( value.length ) { + + case 2: + _gl.vertexAttrib2fv( programAttribute, value ); + break; + + case 3: + _gl.vertexAttrib3fv( programAttribute, value ); + break; + + case 4: + _gl.vertexAttrib4fv( programAttribute, value ); + break; + + default: + _gl.vertexAttrib1fv( programAttribute, value ); + + } + + } + + } + + } + + } + + state.disableUnusedAttributes(); + + } + + // Sorting + + function numericalSort ( a, b ) { + + return b[ 0 ] - a[ 0 ]; + + } + + function painterSortStable ( a, b ) { + + if ( a.object.renderOrder !== b.object.renderOrder ) { + + return a.object.renderOrder - b.object.renderOrder; + + } else if ( a.material.id !== b.material.id ) { + + return a.material.id - b.material.id; + + } else if ( a.z !== b.z ) { + + return a.z - b.z; + + } else { + + return a.id - b.id; + + } + + } + + function reversePainterSortStable ( a, b ) { + + if ( a.object.renderOrder !== b.object.renderOrder ) { + + return a.object.renderOrder - b.object.renderOrder; + + } if ( a.z !== b.z ) { + + return b.z - a.z; + + } else { + + return a.id - b.id; + + } + + } + + // Rendering + + this.render = function ( scene, camera, renderTarget, forceClear ) { + + if ( camera instanceof THREE.Camera === false ) { + + console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); + return; + + } + + var fog = scene.fog; + + // reset caching for this frame + + _currentGeometryProgram = ''; + _currentMaterialId = - 1; + _currentCamera = null; + _lightsNeedUpdate = true; + + // update scene graph + + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + + // update camera matrices and frustum + + if ( camera.parent === null ) camera.updateMatrixWorld(); + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + lights.length = 0; + + opaqueObjectsLastIndex = - 1; + transparentObjectsLastIndex = - 1; + + sprites.length = 0; + lensFlares.length = 0; + + projectObject( scene, camera ); + + opaqueObjects.length = opaqueObjectsLastIndex + 1; + transparentObjects.length = transparentObjectsLastIndex + 1; + + if ( _this.sortObjects === true ) { + + opaqueObjects.sort( painterSortStable ); + transparentObjects.sort( reversePainterSortStable ); + + } + + // + + shadowMap.render( scene ); + + // + + _infoRender.calls = 0; + _infoRender.vertices = 0; + _infoRender.faces = 0; + _infoRender.points = 0; + + this.setRenderTarget( renderTarget ); + + if ( this.autoClear || forceClear ) { + + this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil ); + + } + + // + + if ( scene.overrideMaterial ) { + + var overrideMaterial = scene.overrideMaterial; + + renderObjects( opaqueObjects, camera, lights, fog, overrideMaterial ); + renderObjects( transparentObjects, camera, lights, fog, overrideMaterial ); + + } else { + + // opaque pass (front-to-back order) + + state.setBlending( THREE.NoBlending ); + renderObjects( opaqueObjects, camera, lights, fog ); + + // transparent pass (back-to-front order) + + renderObjects( transparentObjects, camera, lights, fog ); + + } + + // custom render plugins (post pass) + + spritePlugin.render( scene, camera ); + lensFlarePlugin.render( scene, camera, _currentWidth, _currentHeight ); + + // Generate mipmap if we're using any kind of mipmap filtering + + if ( renderTarget ) { + + var texture = renderTarget.texture; + var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ); + if ( texture.generateMipmaps && isTargetPowerOfTwo && texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) { + + updateRenderTargetMipmap( renderTarget ); + + } + + } + + // Ensure depth buffer writing is enabled so it can be cleared on next render + + state.setDepthTest( true ); + state.setDepthWrite( true ); + state.setColorWrite( true ); + + // _gl.finish(); + + }; + + function pushRenderItem( object, geometry, material, z, group ) { + + var array, index; + + // allocate the next position in the appropriate array + + if ( material.transparent ) { + + array = transparentObjects; + index = ++ transparentObjectsLastIndex; + + } else { + + array = opaqueObjects; + index = ++ opaqueObjectsLastIndex; + + } + + // recycle existing render item or grow the array + + var renderItem = array[ index ]; + + if ( renderItem !== undefined ) { + + renderItem.id = object.id; + renderItem.object = object; + renderItem.geometry = geometry; + renderItem.material = material; + renderItem.z = _vector3.z; + renderItem.group = group; + + } else { + + renderItem = { + id: object.id, + object: object, + geometry: geometry, + material: material, + z: _vector3.z, + group: group + }; + + // assert( index === array.length ); + array.push( renderItem ); + + } + + } + + function projectObject( object, camera ) { + + if ( object.visible === false ) return; + + if ( ( object.channels.mask & camera.channels.mask ) !== 0 ) { + + if ( object instanceof THREE.Light ) { + + lights.push( object ); + + } else if ( object instanceof THREE.Sprite ) { + + sprites.push( object ); + + } else if ( object instanceof THREE.LensFlare ) { + + lensFlares.push( object ); + + } else if ( object instanceof THREE.ImmediateRenderObject ) { + + if ( _this.sortObjects === true ) { + + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyProjection( _projScreenMatrix ); + + } + + pushRenderItem( object, null, object.material, _vector3.z, null ); + + } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) { + + if ( object instanceof THREE.SkinnedMesh ) { + + object.skeleton.update(); + + } + + if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) { + + var material = object.material; + + if ( material.visible === true ) { + + if ( _this.sortObjects === true ) { + + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyProjection( _projScreenMatrix ); + + } + + var geometry = objects.update( object ); + + if ( material instanceof THREE.MeshFaceMaterial ) { + + var groups = geometry.groups; + var materials = material.materials; + + for ( var i = 0, l = groups.length; i < l; i ++ ) { + + var group = groups[ i ]; + var groupMaterial = materials[ group.materialIndex ]; + + if ( groupMaterial.visible === true ) { + + pushRenderItem( object, geometry, groupMaterial, _vector3.z, group ); + + } + + } + + } else { + + pushRenderItem( object, geometry, material, _vector3.z, null ); + + } + + } + + } + + } + + } + + var children = object.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + projectObject( children[ i ], camera ); + + } + + } + + function renderObjects( renderList, camera, lights, fog, overrideMaterial ) { + + for ( var i = 0, l = renderList.length; i < l; i ++ ) { + + var renderItem = renderList[ i ]; + + var object = renderItem.object; + var geometry = renderItem.geometry; + var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial; + var group = renderItem.group; + + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); + + if ( object instanceof THREE.ImmediateRenderObject ) { + + setMaterial( material ); + + var program = setProgram( camera, lights, fog, material, object ); + + _currentGeometryProgram = ''; + + object.render( function ( object ) { + + _this.renderBufferImmediate( object, program, material ); + + } ); + + } else { + + _this.renderBufferDirect( camera, lights, fog, geometry, material, object, group ); + + } + + } + + } + + function initMaterial( material, lights, fog, object ) { + + var materialProperties = properties.get( material ); + + var parameters = programCache.getParameters( material, lights, fog, object ); + var code = programCache.getProgramCode( material, parameters ); + + var program = materialProperties.program; + var programChange = true; + + if ( program === undefined ) { + + // new material + material.addEventListener( 'dispose', onMaterialDispose ); + + } else if ( program.code !== code ) { + + // changed glsl or parameters + releaseMaterialProgramReference( material ); + + } else if ( parameters.shaderID !== undefined ) { + + // same glsl and uniform list + return; + + } else { + + // only rebuild uniform list + programChange = false; + + } + + if ( programChange ) { + + if ( parameters.shaderID ) { + + var shader = THREE.ShaderLib[ parameters.shaderID ]; + + materialProperties.__webglShader = { + name: material.type, + uniforms: THREE.UniformsUtils.clone( shader.uniforms ), + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader + }; + + } else { + + materialProperties.__webglShader = { + name: material.type, + uniforms: material.uniforms, + vertexShader: material.vertexShader, + fragmentShader: material.fragmentShader + }; + + } + + material.__webglShader = materialProperties.__webglShader; + + program = programCache.acquireProgram( material, parameters, code ); + + materialProperties.program = program; + material.program = program; + + } + + var attributes = program.getAttributes(); + + if ( material.morphTargets ) { + + material.numSupportedMorphTargets = 0; + + for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { + + if ( attributes[ 'morphTarget' + i ] >= 0 ) { + + material.numSupportedMorphTargets ++; + + } + + } + + } + + if ( material.morphNormals ) { + + material.numSupportedMorphNormals = 0; + + for ( i = 0; i < _this.maxMorphNormals; i ++ ) { + + if ( attributes[ 'morphNormal' + i ] >= 0 ) { + + material.numSupportedMorphNormals ++; + + } + + } + + } + + materialProperties.uniformsList = []; + + var uniformLocations = materialProperties.program.getUniforms(); + + for ( var u in materialProperties.__webglShader.uniforms ) { + + var location = uniformLocations[ u ]; + + if ( location ) { + + materialProperties.uniformsList.push( [ materialProperties.__webglShader.uniforms[ u ], location ] ); + + } + + } + + } + + function setMaterial( material ) { + + setMaterialFaces( material ); + + if ( material.transparent === true ) { + + state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha ); + + } else { + + state.setBlending( THREE.NoBlending ); + + } + + state.setDepthFunc( material.depthFunc ); + state.setDepthTest( material.depthTest ); + state.setDepthWrite( material.depthWrite ); + state.setColorWrite( material.colorWrite ); + state.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + } + + function setMaterialFaces( material ) { + + material.side !== THREE.DoubleSide ? state.enable( _gl.CULL_FACE ) : state.disable( _gl.CULL_FACE ); + state.setFlipSided( material.side === THREE.BackSide ); + + } + + function setProgram( camera, lights, fog, material, object ) { + + _usedTextureUnits = 0; + + var materialProperties = properties.get( material ); + + if ( material.needsUpdate || ! materialProperties.program ) { + + initMaterial( material, lights, fog, object ); + material.needsUpdate = false; + + } + + var refreshProgram = false; + var refreshMaterial = false; + var refreshLights = false; + + var program = materialProperties.program, + p_uniforms = program.getUniforms(), + m_uniforms = materialProperties.__webglShader.uniforms; + + if ( program.id !== _currentProgram ) { + + _gl.useProgram( program.program ); + _currentProgram = program.id; + + refreshProgram = true; + refreshMaterial = true; + refreshLights = true; + + } + + if ( material.id !== _currentMaterialId ) { + + if ( _currentMaterialId === - 1 ) refreshLights = true; + _currentMaterialId = material.id; + + refreshMaterial = true; + + } + + if ( refreshProgram || camera !== _currentCamera ) { + + _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + + if ( capabilities.logarithmicDepthBuffer ) { + + _gl.uniform1f( p_uniforms.logDepthBufFC, 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); + + } + + + if ( camera !== _currentCamera ) _currentCamera = camera; + + // load material specific uniforms + // (shader material also gets them for the sake of genericity) + + if ( material instanceof THREE.ShaderMaterial || + material instanceof THREE.MeshPhongMaterial || + material.envMap ) { + + if ( p_uniforms.cameraPosition !== undefined ) { + + _vector3.setFromMatrixPosition( camera.matrixWorld ); + _gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z ); + + } + + } + + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.ShaderMaterial || + material.skinning ) { + + if ( p_uniforms.viewMatrix !== undefined ) { + + _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements ); + + } + + } + + } + + // skinning uniforms must be set even if material didn't change + // auto-setting of texture unit for bone texture must go before other textures + // not sure why, but otherwise weird things happen + + if ( material.skinning ) { + + if ( object.bindMatrix && p_uniforms.bindMatrix !== undefined ) { + + _gl.uniformMatrix4fv( p_uniforms.bindMatrix, false, object.bindMatrix.elements ); + + } + + if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== undefined ) { + + _gl.uniformMatrix4fv( p_uniforms.bindMatrixInverse, false, object.bindMatrixInverse.elements ); + + } + + if ( capabilities.floatVertexTextures && object.skeleton && object.skeleton.useVertexTexture ) { + + if ( p_uniforms.boneTexture !== undefined ) { + + var textureUnit = getTextureUnit(); + + _gl.uniform1i( p_uniforms.boneTexture, textureUnit ); + _this.setTexture( object.skeleton.boneTexture, textureUnit ); + + } + + if ( p_uniforms.boneTextureWidth !== undefined ) { + + _gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth ); + + } + + if ( p_uniforms.boneTextureHeight !== undefined ) { + + _gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight ); + + } + + } else if ( object.skeleton && object.skeleton.boneMatrices ) { + + if ( p_uniforms.boneGlobalMatrices !== undefined ) { + + _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices ); + + } + + } + + } + + if ( refreshMaterial ) { + + // refresh uniforms common to several materials + + if ( fog && material.fog ) { + + refreshUniformsFog( m_uniforms, fog ); + + } + + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material.lights ) { + + if ( _lightsNeedUpdate ) { + + refreshLights = true; + setupLights( lights, camera ); + _lightsNeedUpdate = false; + + } + + if ( refreshLights ) { + + refreshUniformsLights( m_uniforms, _lights ); + markUniformsLightsNeedsUpdate( m_uniforms, true ); + + } else { + + markUniformsLightsNeedsUpdate( m_uniforms, false ); + + } + + } + + if ( material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshPhongMaterial ) { + + refreshUniformsCommon( m_uniforms, material ); + + } + + // refresh single material specific uniforms + + if ( material instanceof THREE.LineBasicMaterial ) { + + refreshUniformsLine( m_uniforms, material ); + + } else if ( material instanceof THREE.LineDashedMaterial ) { + + refreshUniformsLine( m_uniforms, material ); + refreshUniformsDash( m_uniforms, material ); + + } else if ( material instanceof THREE.PointsMaterial ) { + + refreshUniformsParticle( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshPhongMaterial ) { + + refreshUniformsPhong( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshDepthMaterial ) { + + m_uniforms.mNear.value = camera.near; + m_uniforms.mFar.value = camera.far; + m_uniforms.opacity.value = material.opacity; + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + m_uniforms.opacity.value = material.opacity; + + } + + if ( object.receiveShadow && ! material._shadowPass ) { + + refreshUniformsShadow( m_uniforms, lights, camera ); + + } + + // load common uniforms + + loadUniformsGeneric( materialProperties.uniformsList ); + + } + + loadUniformsMatrices( p_uniforms, object ); + + if ( p_uniforms.modelMatrix !== undefined ) { + + _gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements ); + + } + + return program; + + } + + // Uniforms (refresh uniforms objects) + + function refreshUniformsCommon ( uniforms, material ) { + + uniforms.opacity.value = material.opacity; + + uniforms.diffuse.value = material.color; + + if ( material.emissive ) { + + uniforms.emissive.value = material.emissive; + + } + + uniforms.map.value = material.map; + uniforms.specularMap.value = material.specularMap; + uniforms.alphaMap.value = material.alphaMap; + + if ( material.aoMap ) { + + uniforms.aoMap.value = material.aoMap; + uniforms.aoMapIntensity.value = material.aoMapIntensity; + + } + + // uv repeat and offset setting priorities + // 1. color map + // 2. specular map + // 3. normal map + // 4. bump map + // 5. alpha map + // 6. emissive map + + var uvScaleMap; + + if ( material.map ) { + + uvScaleMap = material.map; + + } else if ( material.specularMap ) { + + uvScaleMap = material.specularMap; + + } else if ( material.displacementMap ) { + + uvScaleMap = material.displacementMap; + + } else if ( material.normalMap ) { + + uvScaleMap = material.normalMap; + + } else if ( material.bumpMap ) { + + uvScaleMap = material.bumpMap; + + } else if ( material.alphaMap ) { + + uvScaleMap = material.alphaMap; + + } else if ( material.emissiveMap ) { + + uvScaleMap = material.emissiveMap; + + } + + if ( uvScaleMap !== undefined ) { + + if ( uvScaleMap instanceof THREE.WebGLRenderTarget ) uvScaleMap = uvScaleMap.texture; + var offset = uvScaleMap.offset; + var repeat = uvScaleMap.repeat; + + uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); + + } + + uniforms.envMap.value = material.envMap; + uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : - 1; + + uniforms.reflectivity.value = material.reflectivity; + uniforms.refractionRatio.value = material.refractionRatio; + + } + + function refreshUniformsLine ( uniforms, material ) { + + uniforms.diffuse.value = material.color; + uniforms.opacity.value = material.opacity; + + } + + function refreshUniformsDash ( uniforms, material ) { + + uniforms.dashSize.value = material.dashSize; + uniforms.totalSize.value = material.dashSize + material.gapSize; + uniforms.scale.value = material.scale; + + } + + function refreshUniformsParticle ( uniforms, material ) { + + uniforms.psColor.value = material.color; + uniforms.opacity.value = material.opacity; + uniforms.size.value = material.size; + uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this. + + uniforms.map.value = material.map; + + if ( material.map !== null ) { + + var offset = material.map.offset; + var repeat = material.map.repeat; + + uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); + + } + + } + + function refreshUniformsFog ( uniforms, fog ) { + + uniforms.fogColor.value = fog.color; + + if ( fog instanceof THREE.Fog ) { + + uniforms.fogNear.value = fog.near; + uniforms.fogFar.value = fog.far; + + } else if ( fog instanceof THREE.FogExp2 ) { + + uniforms.fogDensity.value = fog.density; + + } + + } + + function refreshUniformsPhong ( uniforms, material ) { + + uniforms.specular.value = material.specular; + uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) + + if ( material.lightMap ) { + + uniforms.lightMap.value = material.lightMap; + uniforms.lightMapIntensity.value = material.lightMapIntensity; + + } + + if ( material.emissiveMap ) { + + uniforms.emissiveMap.value = material.emissiveMap; + + } + + if ( material.bumpMap ) { + + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + + } + + if ( material.normalMap ) { + + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); + + } + + if ( material.displacementMap ) { + + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; + + } + + } + + function refreshUniformsLights ( uniforms, lights ) { + + uniforms.ambientLightColor.value = lights.ambient; + + uniforms.directionalLightColor.value = lights.directional.colors; + uniforms.directionalLightDirection.value = lights.directional.positions; + + uniforms.pointLightColor.value = lights.point.colors; + uniforms.pointLightPosition.value = lights.point.positions; + uniforms.pointLightDistance.value = lights.point.distances; + uniforms.pointLightDecay.value = lights.point.decays; + + uniforms.spotLightColor.value = lights.spot.colors; + uniforms.spotLightPosition.value = lights.spot.positions; + uniforms.spotLightDistance.value = lights.spot.distances; + uniforms.spotLightDirection.value = lights.spot.directions; + uniforms.spotLightAngleCos.value = lights.spot.anglesCos; + uniforms.spotLightExponent.value = lights.spot.exponents; + uniforms.spotLightDecay.value = lights.spot.decays; + + uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors; + uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors; + uniforms.hemisphereLightDirection.value = lights.hemi.positions; + + } + + // If uniforms are marked as clean, they don't need to be loaded to the GPU. + + function markUniformsLightsNeedsUpdate ( uniforms, value ) { + + uniforms.ambientLightColor.needsUpdate = value; + + uniforms.directionalLightColor.needsUpdate = value; + uniforms.directionalLightDirection.needsUpdate = value; + + uniforms.pointLightColor.needsUpdate = value; + uniforms.pointLightPosition.needsUpdate = value; + uniforms.pointLightDistance.needsUpdate = value; + uniforms.pointLightDecay.needsUpdate = value; + + uniforms.spotLightColor.needsUpdate = value; + uniforms.spotLightPosition.needsUpdate = value; + uniforms.spotLightDistance.needsUpdate = value; + uniforms.spotLightDirection.needsUpdate = value; + uniforms.spotLightAngleCos.needsUpdate = value; + uniforms.spotLightExponent.needsUpdate = value; + uniforms.spotLightDecay.needsUpdate = value; + + uniforms.hemisphereLightSkyColor.needsUpdate = value; + uniforms.hemisphereLightGroundColor.needsUpdate = value; + uniforms.hemisphereLightDirection.needsUpdate = value; + + } + + function refreshUniformsShadow ( uniforms, lights, camera ) { + + if ( uniforms.shadowMatrix ) { + + var j = 0; + + for ( var i = 0, il = lights.length; i < il; i ++ ) { + + var light = lights[ i ]; + + if ( light.castShadow === true ) { + + if ( light instanceof THREE.PointLight || light instanceof THREE.SpotLight || light instanceof THREE.DirectionalLight ) { + + var shadow = light.shadow; + + if ( light instanceof THREE.PointLight ) { + + // for point lights we set the shadow matrix to be a translation-only matrix + // equal to inverse of the light's position + _vector3.setFromMatrixPosition( light.matrixWorld ).negate(); + shadow.matrix.identity().setPosition( _vector3 ); + + // for point lights we set the sign of the shadowDarkness uniform to be negative + uniforms.shadowDarkness.value[ j ] = - shadow.darkness; + + } else { + + uniforms.shadowDarkness.value[ j ] = shadow.darkness; + + } + + uniforms.shadowMatrix.value[ j ] = shadow.matrix; + uniforms.shadowMap.value[ j ] = shadow.map; + uniforms.shadowMapSize.value[ j ] = shadow.mapSize; + uniforms.shadowBias.value[ j ] = shadow.bias; + + j ++; + + } + + } + + } + + } + + } + + // Uniforms (load to GPU) + + function loadUniformsMatrices ( uniforms, object ) { + + _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object.modelViewMatrix.elements ); + + if ( uniforms.normalMatrix ) { + + _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object.normalMatrix.elements ); + + } + + } + + function getTextureUnit() { + + var textureUnit = _usedTextureUnits; + + if ( textureUnit >= capabilities.maxTextures ) { + + console.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures ); + + } + + _usedTextureUnits += 1; + + return textureUnit; + + } + + function loadUniformsGeneric ( uniforms ) { + + var texture, textureUnit; + + for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) { + + var uniform = uniforms[ j ][ 0 ]; + + // needsUpdate property is not added to all uniforms. + if ( uniform.needsUpdate === false ) continue; + + var type = uniform.type; + var value = uniform.value; + var location = uniforms[ j ][ 1 ]; + + switch ( type ) { + + case '1i': + _gl.uniform1i( location, value ); + break; + + case '1f': + _gl.uniform1f( location, value ); + break; + + case '2f': + _gl.uniform2f( location, value[ 0 ], value[ 1 ] ); + break; + + case '3f': + _gl.uniform3f( location, value[ 0 ], value[ 1 ], value[ 2 ] ); + break; + + case '4f': + _gl.uniform4f( location, value[ 0 ], value[ 1 ], value[ 2 ], value[ 3 ] ); + break; + + case '1iv': + _gl.uniform1iv( location, value ); + break; + + case '3iv': + _gl.uniform3iv( location, value ); + break; + + case '1fv': + _gl.uniform1fv( location, value ); + break; + + case '2fv': + _gl.uniform2fv( location, value ); + break; + + case '3fv': + _gl.uniform3fv( location, value ); + break; + + case '4fv': + _gl.uniform4fv( location, value ); + break; + + case 'Matrix3fv': + _gl.uniformMatrix3fv( location, false, value ); + break; + + case 'Matrix4fv': + _gl.uniformMatrix4fv( location, false, value ); + break; + + // + + case 'i': + + // single integer + _gl.uniform1i( location, value ); + + break; + + case 'f': + + // single float + _gl.uniform1f( location, value ); + + break; + + case 'v2': + + // single THREE.Vector2 + _gl.uniform2f( location, value.x, value.y ); + + break; + + case 'v3': + + // single THREE.Vector3 + _gl.uniform3f( location, value.x, value.y, value.z ); + + break; + + case 'v4': + + // single THREE.Vector4 + _gl.uniform4f( location, value.x, value.y, value.z, value.w ); + + break; + + case 'c': + + // single THREE.Color + _gl.uniform3f( location, value.r, value.g, value.b ); + + break; + + case 'iv1': + + // flat array of integers (JS or typed array) + _gl.uniform1iv( location, value ); + + break; + + case 'iv': + + // flat array of integers with 3 x N size (JS or typed array) + _gl.uniform3iv( location, value ); + + break; + + case 'fv1': + + // flat array of floats (JS or typed array) + _gl.uniform1fv( location, value ); + + break; + + case 'fv': + + // flat array of floats with 3 x N size (JS or typed array) + _gl.uniform3fv( location, value ); + + break; + + case 'v2v': + + // array of THREE.Vector2 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 2 * value.length ); + + } + + for ( var i = 0, i2 = 0, il = value.length; i < il; i ++, i2 += 2 ) { + + uniform._array[ i2 + 0 ] = value[ i ].x; + uniform._array[ i2 + 1 ] = value[ i ].y; + + } + + _gl.uniform2fv( location, uniform._array ); + + break; + + case 'v3v': + + // array of THREE.Vector3 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 3 * value.length ); + + } + + for ( var i = 0, i3 = 0, il = value.length; i < il; i ++, i3 += 3 ) { + + uniform._array[ i3 + 0 ] = value[ i ].x; + uniform._array[ i3 + 1 ] = value[ i ].y; + uniform._array[ i3 + 2 ] = value[ i ].z; + + } + + _gl.uniform3fv( location, uniform._array ); + + break; + + case 'v4v': + + // array of THREE.Vector4 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 4 * value.length ); + + } + + for ( var i = 0, i4 = 0, il = value.length; i < il; i ++, i4 += 4 ) { + + uniform._array[ i4 + 0 ] = value[ i ].x; + uniform._array[ i4 + 1 ] = value[ i ].y; + uniform._array[ i4 + 2 ] = value[ i ].z; + uniform._array[ i4 + 3 ] = value[ i ].w; + + } + + _gl.uniform4fv( location, uniform._array ); + + break; + + case 'm3': + + // single THREE.Matrix3 + _gl.uniformMatrix3fv( location, false, value.elements ); + + break; + + case 'm3v': + + // array of THREE.Matrix3 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 9 * value.length ); + + } + + for ( var i = 0, il = value.length; i < il; i ++ ) { + + value[ i ].flattenToArrayOffset( uniform._array, i * 9 ); + + } + + _gl.uniformMatrix3fv( location, false, uniform._array ); + + break; + + case 'm4': + + // single THREE.Matrix4 + _gl.uniformMatrix4fv( location, false, value.elements ); + + break; + + case 'm4v': + + // array of THREE.Matrix4 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 16 * value.length ); + + } + + for ( var i = 0, il = value.length; i < il; i ++ ) { + + value[ i ].flattenToArrayOffset( uniform._array, i * 16 ); + + } + + _gl.uniformMatrix4fv( location, false, uniform._array ); + + break; + + case 't': + + // single THREE.Texture (2d or cube) + + texture = value; + textureUnit = getTextureUnit(); + + _gl.uniform1i( location, textureUnit ); + + if ( ! texture ) continue; + + if ( texture instanceof THREE.CubeTexture || + ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { + + // CompressedTexture can have Array in image :/ + + setCubeTexture( texture, textureUnit ); + + } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { + + setCubeTextureDynamic( texture.texture, textureUnit ); + + } else if ( texture instanceof THREE.WebGLRenderTarget ) { + + _this.setTexture( texture.texture, textureUnit ); + + } else { + + _this.setTexture( texture, textureUnit ); + + } + + break; + + case 'tv': + + // array of THREE.Texture (2d or cube) + + if ( uniform._array === undefined ) { + + uniform._array = []; + + } + + for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { + + uniform._array[ i ] = getTextureUnit(); + + } + + _gl.uniform1iv( location, uniform._array ); + + for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { + + texture = uniform.value[ i ]; + textureUnit = uniform._array[ i ]; + + if ( ! texture ) continue; + + if ( texture instanceof THREE.CubeTexture || + ( texture.image instanceof Array && texture.image.length === 6 ) ) { + + // CompressedTexture can have Array in image :/ + + setCubeTexture( texture, textureUnit ); + + } else if ( texture instanceof THREE.WebGLRenderTarget ) { + + _this.setTexture( texture.texture, textureUnit ); + + } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { + + setCubeTextureDynamic( texture.texture, textureUnit ); + + } else { + + _this.setTexture( texture, textureUnit ); + + } + + } + + break; + + default: + + console.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type ); + + } + + } + + } + + function setColorLinear( array, offset, color, intensity ) { + + array[ offset + 0 ] = color.r * intensity; + array[ offset + 1 ] = color.g * intensity; + array[ offset + 2 ] = color.b * intensity; + + } + + function setupLights ( lights, camera ) { + + var l, ll, light, + r = 0, g = 0, b = 0, + color, skyColor, groundColor, + intensity, + distance, + + zlights = _lights, + + viewMatrix = camera.matrixWorldInverse, + + dirColors = zlights.directional.colors, + dirPositions = zlights.directional.positions, + + pointColors = zlights.point.colors, + pointPositions = zlights.point.positions, + pointDistances = zlights.point.distances, + pointDecays = zlights.point.decays, + + spotColors = zlights.spot.colors, + spotPositions = zlights.spot.positions, + spotDistances = zlights.spot.distances, + spotDirections = zlights.spot.directions, + spotAnglesCos = zlights.spot.anglesCos, + spotExponents = zlights.spot.exponents, + spotDecays = zlights.spot.decays, + + hemiSkyColors = zlights.hemi.skyColors, + hemiGroundColors = zlights.hemi.groundColors, + hemiPositions = zlights.hemi.positions, + + dirLength = 0, + pointLength = 0, + spotLength = 0, + hemiLength = 0, + + dirCount = 0, + pointCount = 0, + spotCount = 0, + hemiCount = 0, + + dirOffset = 0, + pointOffset = 0, + spotOffset = 0, + hemiOffset = 0; + + for ( l = 0, ll = lights.length; l < ll; l ++ ) { + + light = lights[ l ]; + + color = light.color; + intensity = light.intensity; + distance = light.distance; + + if ( light instanceof THREE.AmbientLight ) { + + if ( ! light.visible ) continue; + + r += color.r; + g += color.g; + b += color.b; + + } else if ( light instanceof THREE.DirectionalLight ) { + + dirCount += 1; + + if ( ! light.visible ) continue; + + _direction.setFromMatrixPosition( light.matrixWorld ); + _vector3.setFromMatrixPosition( light.target.matrixWorld ); + _direction.sub( _vector3 ); + _direction.transformDirection( viewMatrix ); + + dirOffset = dirLength * 3; + + dirPositions[ dirOffset + 0 ] = _direction.x; + dirPositions[ dirOffset + 1 ] = _direction.y; + dirPositions[ dirOffset + 2 ] = _direction.z; + + setColorLinear( dirColors, dirOffset, color, intensity ); + + dirLength += 1; + + } else if ( light instanceof THREE.PointLight ) { + + pointCount += 1; + + if ( ! light.visible ) continue; + + pointOffset = pointLength * 3; + + setColorLinear( pointColors, pointOffset, color, intensity ); + + _vector3.setFromMatrixPosition( light.matrixWorld ); + _vector3.applyMatrix4( viewMatrix ); + + pointPositions[ pointOffset + 0 ] = _vector3.x; + pointPositions[ pointOffset + 1 ] = _vector3.y; + pointPositions[ pointOffset + 2 ] = _vector3.z; + + // distance is 0 if decay is 0, because there is no attenuation at all. + pointDistances[ pointLength ] = distance; + pointDecays[ pointLength ] = ( light.distance === 0 ) ? 0.0 : light.decay; + + pointLength += 1; + + } else if ( light instanceof THREE.SpotLight ) { + + spotCount += 1; + + if ( ! light.visible ) continue; + + spotOffset = spotLength * 3; + + setColorLinear( spotColors, spotOffset, color, intensity ); + + _direction.setFromMatrixPosition( light.matrixWorld ); + _vector3.copy( _direction ).applyMatrix4( viewMatrix ); + + spotPositions[ spotOffset + 0 ] = _vector3.x; + spotPositions[ spotOffset + 1 ] = _vector3.y; + spotPositions[ spotOffset + 2 ] = _vector3.z; + + spotDistances[ spotLength ] = distance; + + _vector3.setFromMatrixPosition( light.target.matrixWorld ); + _direction.sub( _vector3 ); + _direction.transformDirection( viewMatrix ); + + spotDirections[ spotOffset + 0 ] = _direction.x; + spotDirections[ spotOffset + 1 ] = _direction.y; + spotDirections[ spotOffset + 2 ] = _direction.z; + + spotAnglesCos[ spotLength ] = Math.cos( light.angle ); + spotExponents[ spotLength ] = light.exponent; + spotDecays[ spotLength ] = ( light.distance === 0 ) ? 0.0 : light.decay; + + spotLength += 1; + + } else if ( light instanceof THREE.HemisphereLight ) { + + hemiCount += 1; + + if ( ! light.visible ) continue; + + _direction.setFromMatrixPosition( light.matrixWorld ); + _direction.transformDirection( viewMatrix ); + + hemiOffset = hemiLength * 3; + + hemiPositions[ hemiOffset + 0 ] = _direction.x; + hemiPositions[ hemiOffset + 1 ] = _direction.y; + hemiPositions[ hemiOffset + 2 ] = _direction.z; + + skyColor = light.color; + groundColor = light.groundColor; + + setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity ); + setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity ); + + hemiLength += 1; + + } + + } + + // null eventual remains from removed lights + // (this is to avoid if in shader) + + for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0; + for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0; + for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0; + for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0; + for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0; + + zlights.directional.length = dirLength; + zlights.point.length = pointLength; + zlights.spot.length = spotLength; + zlights.hemi.length = hemiLength; + + zlights.ambient[ 0 ] = r; + zlights.ambient[ 1 ] = g; + zlights.ambient[ 2 ] = b; + + } + + // GL state setting + + this.setFaceCulling = function ( cullFace, frontFaceDirection ) { + + if ( cullFace === THREE.CullFaceNone ) { + + state.disable( _gl.CULL_FACE ); + + } else { + + if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) { + + _gl.frontFace( _gl.CW ); + + } else { + + _gl.frontFace( _gl.CCW ); + + } + + if ( cullFace === THREE.CullFaceBack ) { + + _gl.cullFace( _gl.BACK ); + + } else if ( cullFace === THREE.CullFaceFront ) { + + _gl.cullFace( _gl.FRONT ); + + } else { + + _gl.cullFace( _gl.FRONT_AND_BACK ); + + } + + state.enable( _gl.CULL_FACE ); + + } + + }; + + // Textures + + function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) { + + var extension; + + if ( isImagePowerOfTwo ) { + + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) ); + + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) ); + + } else { + + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + + if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) { + + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.', texture ); + + } + + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); + + if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) { + + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.', texture ); + + } + + } + + extension = extensions.get( 'EXT_texture_filter_anisotropic' ); + + if ( extension ) { + + if ( texture.type === THREE.FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return; + if ( texture.type === THREE.HalfFloatType && extensions.get( 'OES_texture_half_float_linear' ) === null ) return; + + if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { + + _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _this.getMaxAnisotropy() ) ); + properties.get( texture ).__currentAnisotropy = texture.anisotropy; + + } + + } + + } + + function uploadTexture( textureProperties, texture, slot ) { + + if ( textureProperties.__webglInit === undefined ) { + + textureProperties.__webglInit = true; + + texture.addEventListener( 'dispose', onTextureDispose ); + + textureProperties.__webglTexture = _gl.createTexture(); + + _infoMemory.textures ++; + + } + + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); + + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); + _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); + + texture.image = clampToMaxSize( texture.image, capabilities.maxTextureSize ); + + if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( texture.image ) === false ) { + + texture.image = makePowerOfTwo( texture.image ); + + } + + var image = texture.image, + isImagePowerOfTwo = isPowerOfTwo( image ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); + + setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo ); + + var mipmap, mipmaps = texture.mipmaps; + + if ( texture instanceof THREE.DataTexture ) { + + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels + + if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + + } + + texture.generateMipmaps = false; + + } else { + + state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); + + } + + } else if ( texture instanceof THREE.CompressedTexture ) { + + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + + if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { + + if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { + + state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + + } else { + + console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); + + } + + } else { + + state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + + } + + } + + } else { + + // regular Texture (image, video, canvas) + + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels + + if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); + + } + + texture.generateMipmaps = false; + + } else { + + state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image ); + + } + + } + + if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + + textureProperties.__version = texture.version; + + if ( texture.onUpdate ) texture.onUpdate( texture ); + + } + + this.setTexture = function ( texture, slot ) { + + var textureProperties = properties.get( texture ); + + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + + var image = texture.image; + + if ( image === undefined ) { + + console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined', texture ); + return; + + } + + if ( image.complete === false ) { + + console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete', texture ); + return; + + } + + uploadTexture( textureProperties, texture, slot ); + + return; + + } + + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); + + }; + + function clampToMaxSize ( image, maxSize ) { + + if ( image.width > maxSize || image.height > maxSize ) { + + // Warning: Scaling through the canvas will only work with images that use + // premultiplied alpha. + + var scale = maxSize / Math.max( image.width, image.height ); + + var canvas = document.createElement( 'canvas' ); + canvas.width = Math.floor( image.width * scale ); + canvas.height = Math.floor( image.height * scale ); + + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); + + console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image ); + + return canvas; + + } + + return image; + + } + + function isPowerOfTwo( image ) { + + return THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ); + + } + + function textureNeedsPowerOfTwo( texture ) { + + if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) return true; + if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) return true; + + return false; + + } + + function makePowerOfTwo( image ) { + + if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement ) { + + var canvas = document.createElement( 'canvas' ); + canvas.width = THREE.Math.nearestPowerOfTwo( image.width ); + canvas.height = THREE.Math.nearestPowerOfTwo( image.height ); + + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, canvas.width, canvas.height ); + + console.warn( 'THREE.WebGLRenderer: image is not power of two (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image ); + + return canvas; + + } + + return image; + + } + + function setCubeTexture ( texture, slot ) { + + var textureProperties = properties.get( texture ); + + if ( texture.image.length === 6 ) { + + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + + if ( ! textureProperties.__image__webglTextureCube ) { + + texture.addEventListener( 'dispose', onTextureDispose ); + + textureProperties.__image__webglTextureCube = _gl.createTexture(); + + _infoMemory.textures ++; + + } + + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); + + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + + var isCompressed = texture instanceof THREE.CompressedTexture; + var isDataTexture = texture.image[ 0 ] instanceof THREE.DataTexture; + + var cubeImage = []; + + for ( var i = 0; i < 6; i ++ ) { + + if ( _this.autoScaleCubemaps && ! isCompressed && ! isDataTexture ) { + + cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize ); + + } else { + + cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; + + } + + } + + var image = cubeImage[ 0 ], + isImagePowerOfTwo = isPowerOfTwo( image ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); + + setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo ); + + for ( var i = 0; i < 6; i ++ ) { + + if ( ! isCompressed ) { + + if ( isDataTexture ) { + + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); + + } else { + + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); + + } + + } else { + + var mipmap, mipmaps = cubeImage[ i ].mipmaps; + + for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { + + mipmap = mipmaps[ j ]; + + if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { + + if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { + + state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + + } else { + + console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setCubeTexture()" ); + + } + + } else { + + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + + } + + } + + } + + } + + if ( texture.generateMipmaps && isImagePowerOfTwo ) { + + _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + + } + + textureProperties.__version = texture.version; + + if ( texture.onUpdate ) texture.onUpdate( texture ); + + } else { + + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); + + } + + } + + } + + function setCubeTextureDynamic ( texture, slot ) { + + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture ); + + } + + // Render targets + + function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); + + } + + function setupRenderBuffer ( renderbuffer, renderTarget ) { + + _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); + + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + + /* For some reason this is not working. Defaulting to RGBA4. + } else if ( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + */ + + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + + } else { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); + + } + + } + + this.setRenderTarget = function ( renderTarget ) { + + var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); + + if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { + + var renderTargetProperties = properties.get( renderTarget ); + var textureProperties = properties.get( renderTarget.texture ); + + if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true; + if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true; + + renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); + + textureProperties.__webglTexture = _gl.createTexture(); + + _infoMemory.textures ++; + + // Setup texture, create render and frame buffers + + var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ), + glFormat = paramThreeToGL( renderTarget.texture.format ), + glType = paramThreeToGL( renderTarget.texture.type ); + + if ( isCube ) { + + renderTargetProperties.__webglFramebuffer = []; + renderTargetProperties.__webglRenderbuffer = []; + + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture ); + + setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo ); + + for ( var i = 0; i < 6; i ++ ) { + + renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); + renderTargetProperties.__webglRenderbuffer[ i ] = _gl.createRenderbuffer(); + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + + setupFrameBuffer( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); + setupRenderBuffer( renderTargetProperties.__webglRenderbuffer[ i ], renderTarget ); + + } + + if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + + } else { + + renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); + + if ( renderTarget.shareDepthFrom ) { + + renderTargetProperties.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer; + + } else { + + renderTargetProperties.__webglRenderbuffer = _gl.createRenderbuffer(); + + } + + state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); + setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo ); + + state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + + setupFrameBuffer( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D ); + + if ( renderTarget.shareDepthFrom ) { + + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTargetProperties.__webglRenderbuffer ); + + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTargetProperties.__webglRenderbuffer ); + + } + + } else { + + setupRenderBuffer( renderTargetProperties.__webglRenderbuffer, renderTarget ); + + } + + if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + + } + + // Release everything + + if ( isCube ) { + + state.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + + } else { + + state.bindTexture( _gl.TEXTURE_2D, null ); + + } + + _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); + + } + + var framebuffer, width, height, vx, vy; + + if ( renderTarget ) { + + var renderTargetProperties = properties.get( renderTarget ); + + if ( isCube ) { + + framebuffer = renderTargetProperties.__webglFramebuffer[ renderTarget.activeCubeFace ]; + + } else { + + framebuffer = renderTargetProperties.__webglFramebuffer; + + } + + width = renderTarget.width; + height = renderTarget.height; + + vx = 0; + vy = 0; + + } else { + + framebuffer = null; + + width = _viewportWidth; + height = _viewportHeight; + + vx = _viewportX; + vy = _viewportY; + + } + + if ( framebuffer !== _currentFramebuffer ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.viewport( vx, vy, width, height ); + + _currentFramebuffer = framebuffer; + + } + + if ( isCube ) { + + var textureProperties = properties.get( renderTarget.texture ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, 0 ); + + } + + _currentWidth = width; + _currentHeight = height; + + }; + + this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) { + + if ( renderTarget instanceof THREE.WebGLRenderTarget === false ) { + + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); + return; + + } + + var framebuffer = properties.get( renderTarget ).__webglFramebuffer; + + if ( framebuffer ) { + + var restore = false; + + if ( framebuffer !== _currentFramebuffer ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + + restore = true; + + } + + try { + + var texture = renderTarget.texture; + + if ( texture.format !== THREE.RGBAFormat + && paramThreeToGL( texture.format ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { + + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); + return; + + } + + if ( texture.type !== THREE.UnsignedByteType + && paramThreeToGL( texture.type ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) + && ! ( texture.type === THREE.FloatType && extensions.get( 'WEBGL_color_buffer_float' ) ) + && ! ( texture.type === THREE.HalfFloatType && extensions.get( 'EXT_color_buffer_half_float' ) ) ) { + + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); + return; + + } + + if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) { + + _gl.readPixels( x, y, width, height, paramThreeToGL( texture.format ), paramThreeToGL( texture.type ), buffer ); + + } else { + + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); + + } + + } finally { + + if ( restore ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer ); + + } + + } + + } + + }; + + function updateRenderTargetMipmap( renderTarget ) { + + var target = renderTarget instanceof THREE.WebGLRenderTargetCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; + var texture = properties.get( renderTarget.texture ).__webglTexture; + + state.bindTexture( target, texture ); + _gl.generateMipmap( target ); + state.bindTexture( target, null ); + + } + + // Fallback filters for non-power-of-2 textures + + function filterFallback ( f ) { + + if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) { + + return _gl.NEAREST; + + } + + return _gl.LINEAR; + + } + + // Map three.js constants to WebGL constants + + function paramThreeToGL ( p ) { + + var extension; + + if ( p === THREE.RepeatWrapping ) return _gl.REPEAT; + if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE; + if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT; + + if ( p === THREE.NearestFilter ) return _gl.NEAREST; + if ( p === THREE.NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST; + if ( p === THREE.NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR; + + if ( p === THREE.LinearFilter ) return _gl.LINEAR; + if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST; + if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR; + + if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE; + if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4; + if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1; + if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5; + + if ( p === THREE.ByteType ) return _gl.BYTE; + if ( p === THREE.ShortType ) return _gl.SHORT; + if ( p === THREE.UnsignedShortType ) return _gl.UNSIGNED_SHORT; + if ( p === THREE.IntType ) return _gl.INT; + if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT; + if ( p === THREE.FloatType ) return _gl.FLOAT; + + extension = extensions.get( 'OES_texture_half_float' ); + + if ( extension !== null ) { + + if ( p === THREE.HalfFloatType ) return extension.HALF_FLOAT_OES; + + } + + if ( p === THREE.AlphaFormat ) return _gl.ALPHA; + if ( p === THREE.RGBFormat ) return _gl.RGB; + if ( p === THREE.RGBAFormat ) return _gl.RGBA; + if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE; + if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA; + + if ( p === THREE.AddEquation ) return _gl.FUNC_ADD; + if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT; + if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT; + + if ( p === THREE.ZeroFactor ) return _gl.ZERO; + if ( p === THREE.OneFactor ) return _gl.ONE; + if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR; + if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR; + if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA; + if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA; + if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA; + if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA; + + if ( p === THREE.DstColorFactor ) return _gl.DST_COLOR; + if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR; + if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE; + + extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); + + if ( extension !== null ) { + + if ( p === THREE.RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; + if ( p === THREE.RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; + if ( p === THREE.RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; + if ( p === THREE.RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; + + } + + extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + + if ( extension !== null ) { + + if ( p === THREE.RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + if ( p === THREE.RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + if ( p === THREE.RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + if ( p === THREE.RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + + } + + extension = extensions.get( 'EXT_blend_minmax' ); + + if ( extension !== null ) { + + if ( p === THREE.MinEquation ) return extension.MIN_EXT; + if ( p === THREE.MaxEquation ) return extension.MAX_EXT; + + } + + return 0; + + } + + // DEPRECATED + + this.supportsFloatTextures = function () { + + console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); + return extensions.get( 'OES_texture_float' ); + + }; + + this.supportsHalfFloatTextures = function () { + + console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); + return extensions.get( 'OES_texture_half_float' ); + + }; + + this.supportsStandardDerivatives = function () { + + console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); + return extensions.get( 'OES_standard_derivatives' ); + + }; + + this.supportsCompressedTextureS3TC = function () { + + console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); + return extensions.get( 'WEBGL_compressed_texture_s3tc' ); + + }; + + this.supportsCompressedTexturePVRTC = function () { + + console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); + return extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + + }; + + this.supportsBlendMinMax = function () { + + console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); + return extensions.get( 'EXT_blend_minmax' ); + + }; + + this.supportsVertexTextures = function () { + + return capabilities.vertexTextures; + + }; + + this.supportsInstancedArrays = function () { + + console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); + return extensions.get( 'ANGLE_instanced_arrays' ); + + }; + + // + + this.initMaterial = function () { + + console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); + + }; + + this.addPrePlugin = function () { + + console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); + + }; + + this.addPostPlugin = function () { + + console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); + + }; + + this.updateShadowMap = function () { + + console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); + + }; + + Object.defineProperties( this, { + shadowMapEnabled: { + get: function () { + + return shadowMap.enabled; + + }, + set: function ( value ) { + + console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); + shadowMap.enabled = value; + + } + }, + shadowMapType: { + get: function () { + + return shadowMap.type; + + }, + set: function ( value ) { + + console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); + shadowMap.type = value; + + } + }, + shadowMapCullFace: { + get: function () { + + return shadowMap.cullFace; + + }, + set: function ( value ) { + + console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace.' ); + shadowMap.cullFace = value; + + } + }, + shadowMapDebug: { + get: function () { + + return shadowMap.debug; + + }, + set: function ( value ) { + + console.warn( 'THREE.WebGLRenderer: .shadowMapDebug is now .shadowMap.debug.' ); + shadowMap.debug = value; + + } + } + } ); + +}; + +// File:src/renderers/WebGLRenderTarget.js + +/** + * @author szimek / https://github.com/szimek/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.WebGLRenderTarget = function ( width, height, options ) { + + this.uuid = THREE.Math.generateUUID(); + + this.width = width; + this.height = height; + + options = options || {}; + + if ( options.minFilter === undefined ) options.minFilter = THREE.LinearFilter; + + this.texture = new THREE.Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy ); + + this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; + this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; + + this.shareDepthFrom = options.shareDepthFrom !== undefined ? options.shareDepthFrom : null; + +}; + +THREE.WebGLRenderTarget.prototype = { + + constructor: THREE.WebGLRenderTarget, + + get wrapS() { + + console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); + + return this.texture.wrapS; + + }, + + set wrapS( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); + + this.texture.wrapS = value; + + }, + + get wrapT() { + + console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); + + return this.texture.wrapT; + + }, + + set wrapT( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); + + this.texture.wrapT = value; + + }, + + get magFilter() { + + console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); + + return this.texture.magFilter; + + }, + + set magFilter( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); + + this.texture.magFilter = value; + + }, + + get minFilter() { + + console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); + + return this.texture.minFilter; + + }, + + set minFilter( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); + + this.texture.minFilter = value; + + }, + + get anisotropy() { + + console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); + + return this.texture.anisotropy; + + }, + + set anisotropy( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); + + this.texture.anisotropy = value; + + }, + + get offset() { + + console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); + + return this.texture.offset; + + }, + + set offset( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); + + this.texture.offset = value; + + }, + + get repeat() { + + console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); + + return this.texture.repeat; + + }, + + set repeat( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); + + this.texture.repeat = value; + + }, + + get format() { + + console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); + + return this.texture.format; + + }, + + set format( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); + + this.texture.format = value; + + }, + + get type() { + + console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); + + return this.texture.type; + + }, + + set type( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); + + this.texture.type = value; + + }, + + get generateMipmaps() { + + console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); + + return this.texture.generateMipmaps; + + }, + + set generateMipmaps( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); + + this.texture.generateMipmaps = value; + + }, + + // + + setSize: function ( width, height ) { + + if ( this.width !== width || this.height !== height ) { + + this.width = width; + this.height = height; + + this.dispose(); + + } + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( source ) { + + this.width = source.width; + this.height = source.height; + + this.texture = source.texture.clone(); + + this.depthBuffer = source.depthBuffer; + this.stencilBuffer = source.stencilBuffer; + + this.shareDepthFrom = source.shareDepthFrom; + + return this; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.WebGLRenderTarget.prototype ); + +// File:src/renderers/WebGLRenderTargetCube.js + +/** + * @author alteredq / http://alteredqualia.com + */ + +THREE.WebGLRenderTargetCube = function ( width, height, options ) { + + THREE.WebGLRenderTarget.call( this, width, height, options ); + + this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 + +}; + +THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype ); +THREE.WebGLRenderTargetCube.prototype.constructor = THREE.WebGLRenderTargetCube; + +// File:src/renderers/webgl/WebGLBufferRenderer.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLBufferRenderer = function ( _gl, extensions, _infoRender ) { + + var mode; + + function setMode( value ) { + + mode = value; + + } + + function render( start, count ) { + + _gl.drawArrays( mode, start, count ); + + _infoRender.calls ++; + _infoRender.vertices += count; + if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3; + + } + + function renderInstances( geometry ) { + + var extension = extensions.get( 'ANGLE_instanced_arrays' ); + + if ( extension === null ) { + + console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; + + } + + var position = geometry.attributes.position; + + if ( position instanceof THREE.InterleavedBufferAttribute ) { + + extension.drawArraysInstancedANGLE( mode, 0, position.data.count, geometry.maxInstancedCount ); + + } else { + + extension.drawArraysInstancedANGLE( mode, 0, position.count, geometry.maxInstancedCount ); + + } + + } + + this.setMode = setMode; + this.render = render; + this.renderInstances = renderInstances; + +}; + +// File:src/renderers/webgl/WebGLIndexedBufferRenderer.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLIndexedBufferRenderer = function ( _gl, extensions, _infoRender ) { + + var mode; + + function setMode( value ) { + + mode = value; + + } + + var type, size; + + function setIndex( index ) { + + if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { + + type = _gl.UNSIGNED_INT; + size = 4; + + } else { + + type = _gl.UNSIGNED_SHORT; + size = 2; + + } + + } + + function render( start, count ) { + + _gl.drawElements( mode, count, type, start * size ); + + _infoRender.calls ++; + _infoRender.vertices += count; + if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3; + + } + + function renderInstances( geometry ) { + + var extension = extensions.get( 'ANGLE_instanced_arrays' ); + + if ( extension === null ) { + + console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; + + } + + var index = geometry.index; + + extension.drawElementsInstancedANGLE( mode, index.array.length, type, 0, geometry.maxInstancedCount ); + + } + + this.setMode = setMode; + this.setIndex = setIndex; + this.render = render; + this.renderInstances = renderInstances; + +}; + +// File:src/renderers/webgl/WebGLExtensions.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLExtensions = function ( gl ) { + + var extensions = {}; + + this.get = function ( name ) { + + if ( extensions[ name ] !== undefined ) { + + return extensions[ name ]; + + } + + var extension; + + switch ( name ) { + + case 'EXT_texture_filter_anisotropic': + extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); + break; + + case 'WEBGL_compressed_texture_s3tc': + extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); + break; + + case 'WEBGL_compressed_texture_pvrtc': + extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); + break; + + default: + extension = gl.getExtension( name ); + + } + + if ( extension === null ) { + + console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); + + } + + extensions[ name ] = extension; + + return extension; + + }; + +}; + +// File:src/renderers/webgl/WebGLCapabilities.js + +THREE.WebGLCapabilities = function ( gl, extensions, parameters ) { + + function getMaxPrecision( precision ) { + + if ( precision === 'highp' ) { + + if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && + gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { + + return 'highp'; + + } + + precision = 'mediump'; + + } + + if ( precision === 'mediump' ) { + + if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && + gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { + + return 'mediump'; + + } + + } + + return 'lowp'; + + } + + this.getMaxPrecision = getMaxPrecision; + + this.precision = parameters.precision !== undefined ? parameters.precision : 'highp', + this.logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false; + + this.maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); + this.maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); + this.maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE ); + this.maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ); + + this.maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); + this.maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS ); + this.maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); + this.maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); + + this.vertexTextures = this.maxVertexTextures > 0; + this.floatFragmentTextures = !! extensions.get( 'OES_texture_float' ); + this.floatVertexTextures = this.vertexTextures && this.floatFragmentTextures; + + var _maxPrecision = getMaxPrecision( this.precision ); + + if ( _maxPrecision !== this.precision ) { + + console.warn( 'THREE.WebGLRenderer:', this.precision, 'not supported, using', _maxPrecision, 'instead.' ); + this.precision = _maxPrecision; + + } + + if ( this.logarithmicDepthBuffer ) { + + this.logarithmicDepthBuffer = !! extensions.get( 'EXT_frag_depth' ); + + } + +}; + +// File:src/renderers/webgl/WebGLGeometries.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLGeometries = function ( gl, properties, info ) { + + var geometries = {}; + + function get( object ) { + + var geometry = object.geometry; + + if ( geometries[ geometry.id ] !== undefined ) { + + return geometries[ geometry.id ]; + + } + + geometry.addEventListener( 'dispose', onGeometryDispose ); + + var buffergeometry; + + if ( geometry instanceof THREE.BufferGeometry ) { + + buffergeometry = geometry; + + } else if ( geometry instanceof THREE.Geometry ) { + + if ( geometry._bufferGeometry === undefined ) { + + geometry._bufferGeometry = new THREE.BufferGeometry().setFromObject( object ); + + } + + buffergeometry = geometry._bufferGeometry; + + } + + geometries[ geometry.id ] = buffergeometry; + + info.memory.geometries ++; + + return buffergeometry; + + } + + function onGeometryDispose( event ) { + + var geometry = event.target; + var buffergeometry = geometries[ geometry.id ]; + + deleteAttributes( buffergeometry.attributes ); + + geometry.removeEventListener( 'dispose', onGeometryDispose ); + + delete geometries[ geometry.id ]; + + var property = properties.get( geometry ); + if ( property.wireframe ) deleteAttribute( property.wireframe ); + + info.memory.geometries --; + + } + + function getAttributeBuffer( attribute ) { + + if ( attribute instanceof THREE.InterleavedBufferAttribute ) { + + return properties.get( attribute.data ).__webglBuffer; + + } + + return properties.get( attribute ).__webglBuffer; + + } + + function deleteAttribute( attribute ) { + + var buffer = getAttributeBuffer( attribute ); + + if ( buffer !== undefined ) { + + gl.deleteBuffer( buffer ); + removeAttributeBuffer( attribute ); + + } + + } + + function deleteAttributes( attributes ) { + + for ( var name in attributes ) { + + deleteAttribute( attributes[ name ] ); + + } + + } + + function removeAttributeBuffer( attribute ) { + + if ( attribute instanceof THREE.InterleavedBufferAttribute ) { + + properties.delete( attribute.data ); + + } else { + + properties.delete( attribute ); + + } + + } + + this.get = get; + +}; + +// File:src/renderers/webgl/WebGLObjects.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLObjects = function ( gl, properties, info ) { + + var geometries = new THREE.WebGLGeometries( gl, properties, info ); + + // + + function update( object ) { + + // TODO: Avoid updating twice (when using shadowMap). Maybe add frame counter. + + var geometry = geometries.get( object ); + + if ( object.geometry instanceof THREE.Geometry ) { + + geometry.updateFromObject( object ); + + } + + var index = geometry.index; + var attributes = geometry.attributes; + + if ( index !== null ) { + + updateAttribute( index, gl.ELEMENT_ARRAY_BUFFER ); + + } + + for ( var name in attributes ) { + + updateAttribute( attributes[ name ], gl.ARRAY_BUFFER ); + + } + + // morph targets + + var morphAttributes = geometry.morphAttributes; + + for ( var name in morphAttributes ) { + + var array = morphAttributes[ name ]; + + for ( var i = 0, l = array.length; i < l; i ++ ) { + + updateAttribute( array[ i ], gl.ARRAY_BUFFER ); + + } + + } + + return geometry; + + } + + function updateAttribute( attribute, bufferType ) { + + var data = ( attribute instanceof THREE.InterleavedBufferAttribute ) ? attribute.data : attribute; + + var attributeProperties = properties.get( data ); + + if ( attributeProperties.__webglBuffer === undefined ) { + + createBuffer( attributeProperties, data, bufferType ); + + } else if ( attributeProperties.version !== data.version ) { + + updateBuffer( attributeProperties, data, bufferType ); + + } + + } + + function createBuffer( attributeProperties, data, bufferType ) { + + attributeProperties.__webglBuffer = gl.createBuffer(); + gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); + + var usage = data.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW; + + gl.bufferData( bufferType, data.array, usage ); + + attributeProperties.version = data.version; + + } + + function updateBuffer( attributeProperties, data, bufferType ) { + + gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); + + if ( data.dynamic === false || data.updateRange.count === - 1 ) { + + // Not using update ranges + + gl.bufferSubData( bufferType, 0, data.array ); + + } else if ( data.updateRange.count === 0 ) { + + console.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' ); + + } else { + + gl.bufferSubData( bufferType, data.updateRange.offset * data.array.BYTES_PER_ELEMENT, + data.array.subarray( data.updateRange.offset, data.updateRange.offset + data.updateRange.count ) ); + + data.updateRange.count = 0; // reset range + + } + + attributeProperties.version = data.version; + + } + + function getAttributeBuffer( attribute ) { + + if ( attribute instanceof THREE.InterleavedBufferAttribute ) { + + return properties.get( attribute.data ).__webglBuffer; + + } + + return properties.get( attribute ).__webglBuffer; + + } + + function getWireframeAttribute( geometry ) { + + var property = properties.get( geometry ); + + if ( property.wireframe !== undefined ) { + + return property.wireframe; + + } + + var indices = []; + + var index = geometry.index; + var attributes = geometry.attributes; + var position = attributes.position; + + // console.time( 'wireframe' ); + + if ( index !== null ) { + + var edges = {}; + var array = index.array; + + for ( var i = 0, l = array.length; i < l; i += 3 ) { + + var a = array[ i + 0 ]; + var b = array[ i + 1 ]; + var c = array[ i + 2 ]; + + if ( checkEdge( edges, a, b ) ) indices.push( a, b ); + if ( checkEdge( edges, b, c ) ) indices.push( b, c ); + if ( checkEdge( edges, c, a ) ) indices.push( c, a ); + + } + + } else { + + var array = attributes.position.array; + + for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { + + var a = i + 0; + var b = i + 1; + var c = i + 2; + + indices.push( a, b, b, c, c, a ); + + } + + } + + // console.timeEnd( 'wireframe' ); + + var TypeArray = position.count > 65535 ? Uint32Array : Uint16Array; + var attribute = new THREE.BufferAttribute( new TypeArray( indices ), 1 ); + + updateAttribute( attribute, gl.ELEMENT_ARRAY_BUFFER ); + + property.wireframe = attribute; + + return attribute; + + } + + function checkEdge( edges, a, b ) { + + if ( a > b ) { + + var tmp = a; + a = b; + b = tmp; + + } + + var list = edges[ a ]; + + if ( list === undefined ) { + + edges[ a ] = [ b ]; + return true; + + } else if ( list.indexOf( b ) === -1 ) { + + list.push( b ); + return true; + + } + + return false; + + } + + this.getAttributeBuffer = getAttributeBuffer; + this.getWireframeAttribute = getWireframeAttribute; + + this.update = update; + +}; + +// File:src/renderers/webgl/WebGLProgram.js + +THREE.WebGLProgram = ( function () { + + var programIdCount = 0; + + function generateDefines( defines ) { + + var chunks = []; + + for ( var name in defines ) { + + var value = defines[ name ]; + + if ( value === false ) continue; + + chunks.push( '#define ' + name + ' ' + value ); + + } + + return chunks.join( '\n' ); + + } + + function fetchUniformLocations( gl, program, identifiers ) { + + var uniforms = {}; + + var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); + + for ( var i = 0; i < n; i ++ ) { + + var info = gl.getActiveUniform( program, i ); + var name = info.name; + var location = gl.getUniformLocation( program, name ); + + // console.log("THREE.WebGLProgram: ACTIVE UNIFORM:", name); + + var suffixPos = name.lastIndexOf( '[0]' ); + if ( suffixPos !== - 1 && suffixPos === name.length - 3 ) { + + uniforms[ name.substr( 0, suffixPos ) ] = location; + + } + + uniforms[ name ] = location; + + } + + return uniforms; + + } + + function fetchAttributeLocations( gl, program, identifiers ) { + + var attributes = {}; + + var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); + + for ( var i = 0; i < n; i ++ ) { + + var info = gl.getActiveAttrib( program, i ); + var name = info.name; + + // console.log("THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); + + attributes[ name ] = gl.getAttribLocation( program, name ); + + } + + return attributes; + + } + + function filterEmptyLine( string ) { + + return string !== ''; + + } + + return function WebGLProgram( renderer, code, material, parameters ) { + + var gl = renderer.context; + + var defines = material.defines; + + var vertexShader = material.__webglShader.vertexShader; + var fragmentShader = material.__webglShader.fragmentShader; + + var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; + + if ( parameters.shadowMapType === THREE.PCFShadowMap ) { + + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; + + } else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) { + + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; + + } + + var envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + var envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; + var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; + + if ( parameters.envMap ) { + + switch ( material.envMap.mapping ) { + + case THREE.CubeReflectionMapping: + case THREE.CubeRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + break; + + case THREE.EquirectangularReflectionMapping: + case THREE.EquirectangularRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC'; + break; + + case THREE.SphericalReflectionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_SPHERE'; + break; + + } + + switch ( material.envMap.mapping ) { + + case THREE.CubeRefractionMapping: + case THREE.EquirectangularRefractionMapping: + envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; + break; + + } + + switch ( material.combine ) { + + case THREE.MultiplyOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; + break; + + case THREE.MixOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; + break; + + case THREE.AddOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; + break; + + } + + } + + var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; + + // console.log( 'building new program ' ); + + // + + var customDefines = generateDefines( defines ); + + // + + var program = gl.createProgram(); + + var prefixVertex, prefixFragment; + + if ( material instanceof THREE.RawShaderMaterial ) { + + prefixVertex = ''; + prefixFragment = ''; + + } else { + + prefixVertex = [ + + 'precision ' + parameters.precision + ' float;', + 'precision ' + parameters.precision + ' int;', + + '#define SHADER_NAME ' + material.__webglShader.name, + + customDefines, + + parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', + + renderer.gammaInput ? '#define GAMMA_INPUT' : '', + renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '', + '#define GAMMA_FACTOR ' + gammaFactorDefine, + + '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights, + '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights, + '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights, + '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights, + + '#define MAX_SHADOWS ' + parameters.maxShadows, + + '#define MAX_BONES ' + parameters.maxBones, + + parameters.map ? '#define USE_MAP' : '', + parameters.envMap ? '#define USE_ENVMAP' : '', + parameters.envMap ? '#define ' + envMapModeDefine : '', + parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.aoMap ? '#define USE_AOMAP' : '', + parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', + parameters.bumpMap ? '#define USE_BUMPMAP' : '', + parameters.normalMap ? '#define USE_NORMALMAP' : '', + parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', + parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + parameters.vertexColors ? '#define USE_COLOR' : '', + + parameters.flatShading ? '#define FLAT_SHADED' : '', + + parameters.skinning ? '#define USE_SKINNING' : '', + parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', + + parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', + parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', + + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', + parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '', + parameters.pointLightShadows > 0 ? '#define POINT_LIGHT_SHADOWS' : '', + + parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', + + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', + + + 'uniform mat4 modelMatrix;', + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform mat4 viewMatrix;', + 'uniform mat3 normalMatrix;', + 'uniform vec3 cameraPosition;', + + 'attribute vec3 position;', + 'attribute vec3 normal;', + 'attribute vec2 uv;', + + '#ifdef USE_COLOR', + + ' attribute vec3 color;', + + '#endif', + + '#ifdef USE_MORPHTARGETS', + + ' attribute vec3 morphTarget0;', + ' attribute vec3 morphTarget1;', + ' attribute vec3 morphTarget2;', + ' attribute vec3 morphTarget3;', + + ' #ifdef USE_MORPHNORMALS', + + ' attribute vec3 morphNormal0;', + ' attribute vec3 morphNormal1;', + ' attribute vec3 morphNormal2;', + ' attribute vec3 morphNormal3;', + + ' #else', + + ' attribute vec3 morphTarget4;', + ' attribute vec3 morphTarget5;', + ' attribute vec3 morphTarget6;', + ' attribute vec3 morphTarget7;', + + ' #endif', + + '#endif', + + '#ifdef USE_SKINNING', + + ' attribute vec4 skinIndex;', + ' attribute vec4 skinWeight;', + + '#endif', + + '\n' + + ].filter( filterEmptyLine ).join( '\n' ); + + prefixFragment = [ + + parameters.bumpMap || parameters.normalMap || parameters.flatShading || material.derivatives ? '#extension GL_OES_standard_derivatives : enable' : '', + parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '', + + 'precision ' + parameters.precision + ' float;', + 'precision ' + parameters.precision + ' int;', + + '#define SHADER_NAME ' + material.__webglShader.name, + + customDefines, + + '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights, + '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights, + '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights, + '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights, + + '#define MAX_SHADOWS ' + parameters.maxShadows, + + parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '', + + renderer.gammaInput ? '#define GAMMA_INPUT' : '', + renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '', + '#define GAMMA_FACTOR ' + gammaFactorDefine, + + ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', + ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '', + + parameters.map ? '#define USE_MAP' : '', + parameters.envMap ? '#define USE_ENVMAP' : '', + parameters.envMap ? '#define ' + envMapTypeDefine : '', + parameters.envMap ? '#define ' + envMapModeDefine : '', + parameters.envMap ? '#define ' + envMapBlendingDefine : '', + parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.aoMap ? '#define USE_AOMAP' : '', + parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', + parameters.bumpMap ? '#define USE_BUMPMAP' : '', + parameters.normalMap ? '#define USE_NORMALMAP' : '', + parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + parameters.vertexColors ? '#define USE_COLOR' : '', + + parameters.flatShading ? '#define FLAT_SHADED' : '', + + parameters.metal ? '#define METAL' : '', + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', + + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', + parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '', + parameters.pointLightShadows > 0 ? '#define POINT_LIGHT_SHADOWS' : '', + + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', + + 'uniform mat4 viewMatrix;', + 'uniform vec3 cameraPosition;', + + '\n' + + ].filter( filterEmptyLine ).join( '\n' ); + + } + + var vertexGlsl = prefixVertex + vertexShader; + var fragmentGlsl = prefixFragment + fragmentShader; + + var glVertexShader = THREE.WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); + var glFragmentShader = THREE.WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); + + gl.attachShader( program, glVertexShader ); + gl.attachShader( program, glFragmentShader ); + + // Force a particular attribute to index 0. + + if ( material.index0AttributeName !== undefined ) { + + gl.bindAttribLocation( program, 0, material.index0AttributeName ); + + } else if ( parameters.morphTargets === true ) { + + // programs with morphTargets displace position out of attribute 0 + gl.bindAttribLocation( program, 0, 'position' ); + + } + + gl.linkProgram( program ); + + var programLog = gl.getProgramInfoLog( program ); + var vertexLog = gl.getShaderInfoLog( glVertexShader ); + var fragmentLog = gl.getShaderInfoLog( glFragmentShader ); + + var runnable = true; + var haveDiagnostics = true; + + if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { + + runnable = false; + + console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog ); + + } else if ( programLog !== '' ) { + + console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); + + } else if ( vertexLog === '' || fragmentLog === '' ) { + + haveDiagnostics = false; + + } + + if ( haveDiagnostics ) { + + this.diagnostics = { + + runnable: runnable, + material: material, + + programLog: programLog, + + vertexShader: { + + log: vertexLog, + prefix: prefixVertex + + }, + + fragmentShader: { + + log: fragmentLog, + prefix: prefixFragment + + } + + }; + + } + + // clean up + + gl.deleteShader( glVertexShader ); + gl.deleteShader( glFragmentShader ); + + // set up caching for uniform locations + + var cachedUniforms; + + this.getUniforms = function() { + + if ( cachedUniforms === undefined ) { + + cachedUniforms = fetchUniformLocations( gl, program ); + + } + + return cachedUniforms; + + }; + + // set up caching for attribute locations + + var cachedAttributes; + + this.getAttributes = function() { + + if ( cachedAttributes === undefined ) { + + cachedAttributes = fetchAttributeLocations( gl, program ); + + } + + return cachedAttributes; + + }; + + // free resource + + this.destroy = function() { + + gl.deleteProgram( program ); + this.program = undefined; + + }; + + // DEPRECATED + + Object.defineProperties( this, { + + uniforms: { + get: function() { + + console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' ); + return this.getUniforms(); + + } + }, + + attributes: { + get: function() { + + console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' ); + return this.getAttributes(); + + } + } + + } ); + + + // + + this.id = programIdCount ++; + this.code = code; + this.usedTimes = 1; + this.program = program; + this.vertexShader = glVertexShader; + this.fragmentShader = glFragmentShader; + + return this; + + }; + +} )(); + +// File:src/renderers/webgl/WebGLPrograms.js + +THREE.WebGLPrograms = function ( renderer, capabilities ) { + + var programs = []; + + var shaderIDs = { + MeshDepthMaterial: 'depth', + MeshNormalMaterial: 'normal', + MeshBasicMaterial: 'basic', + MeshLambertMaterial: 'lambert', + MeshPhongMaterial: 'phong', + LineBasicMaterial: 'basic', + LineDashedMaterial: 'dashed', + PointsMaterial: 'points' + }; + + var parameterNames = [ + "precision", "supportsVertexTextures", "map", "envMap", "envMapMode", + "lightMap", "aoMap", "emissiveMap", "bumpMap", "normalMap", "displacementMap", "specularMap", + "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp", + "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", + "maxBones", "useVertexTexture", "morphTargets", "morphNormals", + "maxMorphTargets", "maxMorphNormals", "maxDirLights", "maxPointLights", + "maxSpotLights", "maxHemiLights", "maxShadows", "shadowMapEnabled", "pointLightShadows", + "shadowMapType", "shadowMapDebug", "alphaTest", "metal", "doubleSided", + "flipSided" + ]; + + + function allocateBones ( object ) { + + if ( capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture ) { + + return 1024; + + } else { + + // default for when object is not specified + // ( for example when prebuilding shader to be used with multiple objects ) + // + // - leave some extra space for other uniforms + // - limit here is ANGLE's 254 max uniform vectors + // (up to 54 should be safe) + + var nVertexUniforms = capabilities.maxVertexUniforms; + var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); + + var maxBones = nVertexMatrices; + + if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { + + maxBones = Math.min( object.skeleton.bones.length, maxBones ); + + if ( maxBones < object.skeleton.bones.length ) { + + console.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' ); + + } + + } + + return maxBones; + + } + + } + + function allocateLights( lights ) { + + var dirLights = 0; + var pointLights = 0; + var spotLights = 0; + var hemiLights = 0; + + for ( var l = 0, ll = lights.length; l < ll; l ++ ) { + + var light = lights[ l ]; + + if ( light.visible === false ) continue; + + if ( light instanceof THREE.DirectionalLight ) dirLights ++; + if ( light instanceof THREE.PointLight ) pointLights ++; + if ( light instanceof THREE.SpotLight ) spotLights ++; + if ( light instanceof THREE.HemisphereLight ) hemiLights ++; + + } + + return { 'directional': dirLights, 'point': pointLights, 'spot': spotLights, 'hemi': hemiLights }; + + } + + function allocateShadows( lights ) { + + var maxShadows = 0; + var pointLightShadows = 0; + + for ( var l = 0, ll = lights.length; l < ll; l ++ ) { + + var light = lights[ l ]; + + if ( ! light.castShadow ) continue; + + if ( light instanceof THREE.SpotLight || light instanceof THREE.DirectionalLight ) maxShadows ++; + if ( light instanceof THREE.PointLight ) { + + maxShadows ++; + pointLightShadows ++; + + } + + } + + return { 'maxShadows': maxShadows, 'pointLightShadows': pointLightShadows }; + + } + + this.getParameters = function ( material, lights, fog, object ) { + + var shaderID = shaderIDs[ material.type ]; + // heuristics to create shader parameters according to lights in the scene + // (not to blow over maxLights budget) + + var maxLightCount = allocateLights( lights ); + var allocatedShadows = allocateShadows( lights ); + var maxBones = allocateBones( object ); + var precision = renderer.getPrecision(); + + if ( material.precision !== null ) { + + precision = capabilities.getMaxPrecision( material.precision ); + + if ( precision !== material.precision ) { + + console.warn( 'THREE.WebGLRenderer.initMaterial:', material.precision, 'not supported, using', precision, 'instead.' ); + + } + + } + + var parameters = { + + shaderID: shaderID, + + precision: precision, + supportsVertexTextures: capabilities.vertexTextures, + + map: !! material.map, + envMap: !! material.envMap, + envMapMode: material.envMap && material.envMap.mapping, + lightMap: !! material.lightMap, + aoMap: !! material.aoMap, + emissiveMap: !! material.emissiveMap, + bumpMap: !! material.bumpMap, + normalMap: !! material.normalMap, + displacementMap: !! material.displacementMap, + specularMap: !! material.specularMap, + alphaMap: !! material.alphaMap, + + combine: material.combine, + + vertexColors: material.vertexColors, + + fog: fog, + useFog: material.fog, + fogExp: fog instanceof THREE.FogExp2, + + flatShading: material.shading === THREE.FlatShading, + + sizeAttenuation: material.sizeAttenuation, + logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer, + + skinning: material.skinning, + maxBones: maxBones, + useVertexTexture: capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture, + + morphTargets: material.morphTargets, + morphNormals: material.morphNormals, + maxMorphTargets: renderer.maxMorphTargets, + maxMorphNormals: renderer.maxMorphNormals, + + maxDirLights: maxLightCount.directional, + maxPointLights: maxLightCount.point, + maxSpotLights: maxLightCount.spot, + maxHemiLights: maxLightCount.hemi, + + maxShadows: allocatedShadows.maxShadows, + pointLightShadows: allocatedShadows.pointLightShadows, + shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && allocatedShadows.maxShadows > 0, + shadowMapType: renderer.shadowMap.type, + shadowMapDebug: renderer.shadowMap.debug, + + alphaTest: material.alphaTest, + metal: material.metal, + doubleSided: material.side === THREE.DoubleSide, + flipSided: material.side === THREE.BackSide + + }; + + return parameters; + + }; + + this.getProgramCode = function ( material, parameters ) { + + var chunks = []; + + if ( parameters.shaderID ) { + + chunks.push( parameters.shaderID ); + + } else { + + chunks.push( material.fragmentShader ); + chunks.push( material.vertexShader ); + + } + + if ( material.defines !== undefined ) { + + for ( var name in material.defines ) { + + chunks.push( name ); + chunks.push( material.defines[ name ] ); + + } + + } + + for ( var i = 0; i < parameterNames.length; i ++ ) { + + var parameterName = parameterNames[ i ]; + chunks.push( parameterName ); + chunks.push( parameters[ parameterName ] ); + + } + + return chunks.join(); + + }; + + this.acquireProgram = function ( material, parameters, code ) { + + var program; + + // Check if code has been already compiled + for ( var p = 0, pl = programs.length; p < pl; p ++ ) { + + var programInfo = programs[ p ]; + + if ( programInfo.code === code ) { + + program = programInfo; + ++ program.usedTimes; + + break; + + } + + } + + if ( program === undefined ) { + + program = new THREE.WebGLProgram( renderer, code, material, parameters ); + programs.push( program ); + + } + + return program; + + }; + + this.releaseProgram = function( program ) { + + if ( -- program.usedTimes === 0 ) { + + // Remove from unordered set + var i = programs.indexOf( program ); + programs[ i ] = programs[ programs.length - 1 ]; + programs.pop(); + + // Free WebGL resources + program.destroy(); + + } + + }; + + // Exposed for resource monitoring & error feedback via renderer.info: + this.programs = programs; + +}; + +// File:src/renderers/webgl/WebGLProperties.js + +/** +* @author fordacious / fordacious.github.io +*/ + +THREE.WebGLProperties = function () { + + var properties = {}; + + this.get = function ( object ) { + + var uuid = object.uuid; + var map = properties[ uuid ]; + + if ( map === undefined ) { + + map = {}; + properties[ uuid ] = map; + + } + + return map; + + }; + + this.delete = function ( object ) { + + delete properties[ object.uuid ]; + + }; + + this.clear = function () { + + properties = {}; + + }; + +}; + +// File:src/renderers/webgl/WebGLShader.js + +THREE.WebGLShader = ( function () { + + function addLineNumbers( string ) { + + var lines = string.split( '\n' ); + + for ( var i = 0; i < lines.length; i ++ ) { + + lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; + + } + + return lines.join( '\n' ); + + } + + return function WebGLShader( gl, type, string ) { + + var shader = gl.createShader( type ); + + gl.shaderSource( shader, string ); + gl.compileShader( shader ); + + if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { + + console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); + + } + + if ( gl.getShaderInfoLog( shader ) !== '' ) { + + console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) ); + + } + + // --enable-privileged-webgl-extension + // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); + + return shader; + + }; + +} )(); + +// File:src/renderers/webgl/WebGLShadowMap.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { + + var _gl = _renderer.context, + _state = _renderer.state, + _frustum = new THREE.Frustum(), + _projScreenMatrix = new THREE.Matrix4(), + + _min = new THREE.Vector3(), + _max = new THREE.Vector3(), + + _lookTarget = new THREE.Vector3(), + _lightPositionWorld = new THREE.Vector3(), + + _renderList = [], + + _MorphingFlag = 1, + _SkinningFlag = 2, + + _NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1, + + _depthMaterials = new Array( _NumberOfMaterialVariants ), + _distanceMaterials = new Array( _NumberOfMaterialVariants ); + + var cubeDirections = [ + new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( - 1, 0, 0 ), new THREE.Vector3( 0, 0, 1 ), + new THREE.Vector3( 0, 0, - 1 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, - 1, 0 ) + ]; + + var cubeUps = [ + new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ), + new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 0, 1 ), new THREE.Vector3( 0, 0, - 1 ) + ]; + + var cube2DViewPorts = [ + new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4(), + new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4() + ]; + + var _vector4 = new THREE.Vector4(); + + // init + + var depthShader = THREE.ShaderLib[ "depthRGBA" ]; + var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); + + var distanceShader = THREE.ShaderLib[ "distanceRGBA" ]; + var distanceUniforms = THREE.UniformsUtils.clone( distanceShader.uniforms ); + + for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) { + + var useMorphing = ( i & _MorphingFlag ) !== 0; + var useSkinning = ( i & _SkinningFlag ) !== 0; + + var depthMaterial = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader, + morphTargets: useMorphing, + skinning: useSkinning + } ); + + depthMaterial._shadowPass = true; + + _depthMaterials[ i ] = depthMaterial; + + var distanceMaterial = new THREE.ShaderMaterial( { + uniforms: distanceUniforms, + vertexShader: distanceShader.vertexShader, + fragmentShader: distanceShader.fragmentShader, + morphTargets: useMorphing, + skinning: useSkinning + } ); + + distanceMaterial._shadowPass = true; + + _distanceMaterials[ i ] = distanceMaterial; + + } + + // + + var scope = this; + + this.enabled = false; + + this.autoUpdate = true; + this.needsUpdate = false; + + this.type = THREE.PCFShadowMap; + this.cullFace = THREE.CullFaceFront; + + this.render = function ( scene ) { + + var faceCount, isPointLight; + + if ( scope.enabled === false ) return; + if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; + + // Set GL state for depth map. + _gl.clearColor( 1, 1, 1, 1 ); + _state.disable( _gl.BLEND ); + _state.enable( _gl.CULL_FACE ); + _gl.frontFace( _gl.CCW ); + _gl.cullFace( scope.cullFace === THREE.CullFaceFront ? _gl.FRONT : _gl.BACK ); + _state.setDepthTest( true ); + + // save the existing viewport so it can be restored later + _renderer.getViewport( _vector4 ); + + // render depth map + + for ( var i = 0, il = _lights.length; i < il; i ++ ) { + + var light = _lights[ i ]; + + if ( light.castShadow === true ) { + + var shadow = light.shadow; + var shadowCamera = shadow.camera; + var shadowMapSize = shadow.mapSize; + + if ( light instanceof THREE.PointLight ) { + + faceCount = 6; + isPointLight = true; + + var vpWidth = shadowMapSize.x / 4.0; + var vpHeight = shadowMapSize.y / 2.0; + + // These viewports map a cube-map onto a 2D texture with the + // following orientation: + // + // xzXZ + // y Y + // + // X - Positive x direction + // x - Negative x direction + // Y - Positive y direction + // y - Negative y direction + // Z - Positive z direction + // z - Negative z direction + + // positive X + cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight ); + // negative X + cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight ); + // positive Z + cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight ); + // negative Z + cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight ); + // positive Y + cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight ); + // negative Y + cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight ); + + } else { + + faceCount = 1; + isPointLight = false; + + } + + if ( shadow.map === null ) { + + var shadowFilter = THREE.LinearFilter; + + if ( scope.type === THREE.PCFSoftShadowMap ) { + + shadowFilter = THREE.NearestFilter; + + } + + var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat }; + + shadow.map = new THREE.WebGLRenderTarget( shadowMapSize.x, shadowMapSize.y, pars ); + shadow.matrix = new THREE.Matrix4(); + + // + + if ( light instanceof THREE.SpotLight ) { + + shadowCamera.aspect = shadowMapSize.x / shadowMapSize.y; + + } + + shadowCamera.updateProjectionMatrix(); + + } + + var shadowMap = shadow.map; + var shadowMatrix = shadow.matrix; + + _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); + shadowCamera.position.copy( _lightPositionWorld ); + + _renderer.setRenderTarget( shadowMap ); + _renderer.clear(); + + // render shadow map for each cube face (if omni-directional) or + // run a single pass if not + + for ( var face = 0; face < faceCount; face ++ ) { + + if ( isPointLight ) { + + _lookTarget.copy( shadowCamera.position ); + _lookTarget.add( cubeDirections[ face ] ); + shadowCamera.up.copy( cubeUps[ face ] ); + shadowCamera.lookAt( _lookTarget ); + var vpDimensions = cube2DViewPorts[ face ]; + _renderer.setViewport( vpDimensions.x, vpDimensions.y, vpDimensions.z, vpDimensions.w ); + + } else { + + _lookTarget.setFromMatrixPosition( light.target.matrixWorld ); + shadowCamera.lookAt( _lookTarget ); + + } + + shadowCamera.updateMatrixWorld(); + shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); + + // compute shadow matrix + + shadowMatrix.set( + 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0 + ); + + shadowMatrix.multiply( shadowCamera.projectionMatrix ); + shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); + + // update camera matrices and frustum + + _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + // set object matrices & frustum culling + + _renderList.length = 0; + + projectObject( scene, shadowCamera ); + + // render shadow map + // render regular objects + + for ( var j = 0, jl = _renderList.length; j < jl; j ++ ) { + + var object = _renderList[ j ]; + var geometry = _objects.update( object ); + var material = object.material; + + if ( material instanceof THREE.MeshFaceMaterial ) { + + var groups = geometry.groups; + var materials = material.materials; + + for ( var k = 0, kl = groups.length; k < kl; k ++ ) { + + var group = groups[ k ]; + var groupMaterial = materials[ group.materialIndex ]; + + if ( groupMaterial.visible === true ) { + + var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld ); + _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial, object, group ); + + } + + } + + } else { + + var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld ); + _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial, object, null ); + + } + + } + + } + + // We must call _renderer.resetGLState() at the end of each iteration of + // the light loop in order to force material updates for each light. + _renderer.resetGLState(); + + } + + } + + _renderer.setViewport( _vector4.x, _vector4.y, _vector4.z, _vector4.w ); + + // Restore GL state. + var clearColor = _renderer.getClearColor(), + clearAlpha = _renderer.getClearAlpha(); + _renderer.setClearColor( clearColor, clearAlpha ); + _state.enable( _gl.BLEND ); + + if ( scope.cullFace === THREE.CullFaceFront ) { + + _gl.cullFace( _gl.BACK ); + + } + + _renderer.resetGLState(); + + scope.needsUpdate = false; + + }; + + function getDepthMaterial( object, material, isPointLight, lightPositionWorld ) { + + var geometry = object.geometry; + + var newMaterial = null; + + var materialVariants = _depthMaterials; + var customMaterial = object.customDepthMaterial; + + if ( isPointLight ) { + + materialVariants = _distanceMaterials; + customMaterial = object.customDistanceMaterial; + + } + + if ( ! customMaterial ) { + + var useMorphing = geometry.morphTargets !== undefined && + geometry.morphTargets.length > 0 && material.morphTargets; + + var useSkinning = object instanceof THREE.SkinnedMesh && material.skinning; + + var variantIndex = 0; + + if ( useMorphing ) variantIndex |= _MorphingFlag; + if ( useSkinning ) variantIndex |= _SkinningFlag; + + newMaterial = materialVariants[ variantIndex ]; + + } else { + + newMaterial = customMaterial; + + } + + newMaterial.visible = material.visible; + newMaterial.wireframe = material.wireframe; + newMaterial.wireframeLinewidth = material.wireframeLinewidth; + + if ( isPointLight && newMaterial.uniforms.lightPos !== undefined ) { + + newMaterial.uniforms.lightPos.value.copy( lightPositionWorld ); + + } + + return newMaterial; + + } + + function projectObject( object, camera ) { + + if ( object.visible === false ) return; + + if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) { + + if ( object.castShadow && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) { + + var material = object.material; + + if ( material.visible === true ) { + + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + _renderList.push( object ); + + } + + } + + } + + var children = object.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + projectObject( children[ i ], camera ); + + } + + } + +}; + +// File:src/renderers/webgl/WebGLState.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLState = function ( gl, extensions, paramThreeToGL ) { + + var _this = this; + + var newAttributes = new Uint8Array( 16 ); + var enabledAttributes = new Uint8Array( 16 ); + var attributeDivisors = new Uint8Array( 16 ); + + var capabilities = {}; + + var compressedTextureFormats = null; + + var currentBlending = null; + var currentBlendEquation = null; + var currentBlendSrc = null; + var currentBlendDst = null; + var currentBlendEquationAlpha = null; + var currentBlendSrcAlpha = null; + var currentBlendDstAlpha = null; + + var currentDepthFunc = null; + var currentDepthWrite = null; + + var currentColorWrite = null; + + var currentFlipSided = null; + + var currentLineWidth = null; + + var currentPolygonOffsetFactor = null; + var currentPolygonOffsetUnits = null; + + var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); + + var currentTextureSlot = undefined; + var currentBoundTextures = {}; + + this.init = function () { + + gl.clearColor( 0, 0, 0, 1 ); + gl.clearDepth( 1 ); + gl.clearStencil( 0 ); + + this.enable( gl.DEPTH_TEST ); + gl.depthFunc( gl.LEQUAL ); + + gl.frontFace( gl.CCW ); + gl.cullFace( gl.BACK ); + this.enable( gl.CULL_FACE ); + + this.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); + + }; + + this.initAttributes = function () { + + for ( var i = 0, l = newAttributes.length; i < l; i ++ ) { + + newAttributes[ i ] = 0; + + } + + }; + + this.enableAttribute = function ( attribute ) { + + newAttributes[ attribute ] = 1; + + if ( enabledAttributes[ attribute ] === 0 ) { + + gl.enableVertexAttribArray( attribute ); + enabledAttributes[ attribute ] = 1; + + } + + if ( attributeDivisors[ attribute ] !== 0 ) { + + var extension = extensions.get( 'ANGLE_instanced_arrays' ); + + extension.vertexAttribDivisorANGLE( attribute, 0 ); + attributeDivisors[ attribute ] = 0; + + } + + }; + + this.enableAttributeAndDivisor = function ( attribute, meshPerAttribute, extension ) { + + newAttributes[ attribute ] = 1; + + if ( enabledAttributes[ attribute ] === 0 ) { + + gl.enableVertexAttribArray( attribute ); + enabledAttributes[ attribute ] = 1; + + } + + if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { + + extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute ); + attributeDivisors[ attribute ] = meshPerAttribute; + + } + + }; + + this.disableUnusedAttributes = function () { + + for ( var i = 0, l = enabledAttributes.length; i < l; i ++ ) { + + if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { + + gl.disableVertexAttribArray( i ); + enabledAttributes[ i ] = 0; + + } + + } + + }; + + this.enable = function ( id ) { + + if ( capabilities[ id ] !== true ) { + + gl.enable( id ); + capabilities[ id ] = true; + + } + + }; + + this.disable = function ( id ) { + + if ( capabilities[ id ] !== false ) { + + gl.disable( id ); + capabilities[ id ] = false; + + } + + }; + + this.getCompressedTextureFormats = function () { + + if ( compressedTextureFormats === null ) { + + compressedTextureFormats = []; + + if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || + extensions.get( 'WEBGL_compressed_texture_s3tc' ) ) { + + var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS ); + + for ( var i = 0; i < formats.length; i ++ ) { + + compressedTextureFormats.push( formats[ i ] ); + + } + + } + + } + + return compressedTextureFormats; + + }; + + this.setBlending = function ( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha ) { + + if ( blending !== currentBlending ) { + + if ( blending === THREE.NoBlending ) { + + this.disable( gl.BLEND ); + + } else if ( blending === THREE.AdditiveBlending ) { + + this.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); + + } else if ( blending === THREE.SubtractiveBlending ) { + + // TODO: Find blendFuncSeparate() combination + + this.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR ); + + } else if ( blending === THREE.MultiplyBlending ) { + + // TODO: Find blendFuncSeparate() combination + + this.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); + + } else if ( blending === THREE.CustomBlending ) { + + this.enable( gl.BLEND ); + + } else { + + this.enable( gl.BLEND ); + gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); + gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); + + } + + currentBlending = blending; + + } + + if ( blending === THREE.CustomBlending ) { + + blendEquationAlpha = blendEquationAlpha || blendEquation; + blendSrcAlpha = blendSrcAlpha || blendSrc; + blendDstAlpha = blendDstAlpha || blendDst; + + if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { + + gl.blendEquationSeparate( paramThreeToGL( blendEquation ), paramThreeToGL( blendEquationAlpha ) ); + + currentBlendEquation = blendEquation; + currentBlendEquationAlpha = blendEquationAlpha; + + } + + if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { + + gl.blendFuncSeparate( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ), paramThreeToGL( blendSrcAlpha ), paramThreeToGL( blendDstAlpha ) ); + + currentBlendSrc = blendSrc; + currentBlendDst = blendDst; + currentBlendSrcAlpha = blendSrcAlpha; + currentBlendDstAlpha = blendDstAlpha; + + } + + } else { + + currentBlendEquation = null; + currentBlendSrc = null; + currentBlendDst = null; + currentBlendEquationAlpha = null; + currentBlendSrcAlpha = null; + currentBlendDstAlpha = null; + + } + + }; + + this.setDepthFunc = function ( depthFunc ) { + + if ( currentDepthFunc !== depthFunc ) { + + if ( depthFunc ) { + + switch ( depthFunc ) { + + case THREE.NeverDepth: + + gl.depthFunc( gl.NEVER ); + break; + + case THREE.AlwaysDepth: + + gl.depthFunc( gl.ALWAYS ); + break; + + case THREE.LessDepth: + + gl.depthFunc( gl.LESS ); + break; + + case THREE.LessEqualDepth: + + gl.depthFunc( gl.LEQUAL ); + break; + + case THREE.EqualDepth: + + gl.depthFunc( gl.EQUAL ); + break; + + case THREE.GreaterEqualDepth: + + gl.depthFunc( gl.GEQUAL ); + break; + + case THREE.GreaterDepth: + + gl.depthFunc( gl.GREATER ); + break; + + case THREE.NotEqualDepth: + + gl.depthFunc( gl.NOTEQUAL ); + break; + + default: + + gl.depthFunc( gl.LEQUAL ); + + } + + } else { + + gl.depthFunc( gl.LEQUAL ); + + } + + currentDepthFunc = depthFunc; + + } + + }; + + this.setDepthTest = function ( depthTest ) { + + if ( depthTest ) { + + this.enable( gl.DEPTH_TEST ); + + } else { + + this.disable( gl.DEPTH_TEST ); + + } + + }; + + this.setDepthWrite = function ( depthWrite ) { + + if ( currentDepthWrite !== depthWrite ) { + + gl.depthMask( depthWrite ); + currentDepthWrite = depthWrite; + + } + + }; + + this.setColorWrite = function ( colorWrite ) { + + if ( currentColorWrite !== colorWrite ) { + + gl.colorMask( colorWrite, colorWrite, colorWrite, colorWrite ); + currentColorWrite = colorWrite; + + } + + }; + + this.setFlipSided = function ( flipSided ) { + + if ( currentFlipSided !== flipSided ) { + + if ( flipSided ) { + + gl.frontFace( gl.CW ); + + } else { + + gl.frontFace( gl.CCW ); + + } + + currentFlipSided = flipSided; + + } + + }; + + this.setLineWidth = function ( width ) { + + if ( width !== currentLineWidth ) { + + gl.lineWidth( width ); + + currentLineWidth = width; + + } + + }; + + this.setPolygonOffset = function ( polygonOffset, factor, units ) { + + if ( polygonOffset ) { + + this.enable( gl.POLYGON_OFFSET_FILL ); + + } else { + + this.disable( gl.POLYGON_OFFSET_FILL ); + + } + + if ( polygonOffset && ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) ) { + + gl.polygonOffset( factor, units ); + + currentPolygonOffsetFactor = factor; + currentPolygonOffsetUnits = units; + + } + + }; + + this.setScissorTest = function ( scissorTest ) { + + if ( scissorTest ) { + + this.enable( gl.SCISSOR_TEST ); + + } else { + + this.disable( gl.SCISSOR_TEST ); + + } + + }; + + // texture + + this.activeTexture = function ( webglSlot ) { + + if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; + + if ( currentTextureSlot !== webglSlot ) { + + gl.activeTexture( webglSlot ); + currentTextureSlot = webglSlot; + + } + + } + + this.bindTexture = function ( webglType, webglTexture ) { + + if ( currentTextureSlot === undefined ) { + + _this.activeTexture(); + + } + + var boundTexture = currentBoundTextures[ currentTextureSlot ]; + + if ( boundTexture === undefined ) { + + boundTexture = { type: undefined, texture: undefined }; + currentBoundTextures[ currentTextureSlot ] = boundTexture; + + } + + if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { + + gl.bindTexture( webglType, webglTexture ); + + boundTexture.type = webglType; + boundTexture.texture = webglTexture; + + } + + }; + + this.compressedTexImage2D = function () { + + try { + + gl.compressedTexImage2D.apply( gl, arguments ); + + } catch ( error ) { + + console.error( error ); + + } + + }; + + this.texImage2D = function () { + + try { + + gl.texImage2D.apply( gl, arguments ); + + } catch ( error ) { + + console.error( error ); + + } + + }; + + // + + this.reset = function () { + + for ( var i = 0; i < enabledAttributes.length; i ++ ) { + + if ( enabledAttributes[ i ] === 1 ) { + + gl.disableVertexAttribArray( i ); + enabledAttributes[ i ] = 0; + + } + + } + + capabilities = {}; + + compressedTextureFormats = null; + + currentBlending = null; + + currentDepthWrite = null; + currentColorWrite = null; + + currentFlipSided = null; + + }; + +}; + +// File:src/renderers/webgl/plugins/LensFlarePlugin.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.LensFlarePlugin = function ( renderer, flares ) { + + var gl = renderer.context; + var state = renderer.state; + + var vertexBuffer, elementBuffer; + var program, attributes, uniforms; + var hasVertexTexture; + + var tempTexture, occlusionTexture; + + function init() { + + var vertices = new Float32Array( [ + - 1, - 1, 0, 0, + 1, - 1, 1, 0, + 1, 1, 1, 1, + - 1, 1, 0, 1 + ] ); + + var faces = new Uint16Array( [ + 0, 1, 2, + 0, 2, 3 + ] ); + + // buffers + + vertexBuffer = gl.createBuffer(); + elementBuffer = gl.createBuffer(); + + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); + + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); + + // textures + + tempTexture = gl.createTexture(); + occlusionTexture = gl.createTexture(); + + state.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); + + state.bindTexture( gl.TEXTURE_2D, occlusionTexture ); + gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); + + hasVertexTexture = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 0; + + var shader; + + if ( hasVertexTexture ) { + + shader = { + + vertexShader: [ + + "uniform lowp int renderType;", + + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", + + "uniform sampler2D occlusionMap;", + + "attribute vec2 position;", + "attribute vec2 uv;", + + "varying vec2 vUV;", + "varying float vVisibility;", + + "void main() {", + + "vUV = uv;", + + "vec2 pos = position;", + + "if ( renderType == 2 ) {", + + "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );", + + "vVisibility = visibility.r / 9.0;", + "vVisibility *= 1.0 - visibility.g / 9.0;", + "vVisibility *= visibility.b / 9.0;", + "vVisibility *= 1.0 - visibility.a / 9.0;", + + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + + "}", + + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform lowp int renderType;", + + "uniform sampler2D map;", + "uniform float opacity;", + "uniform vec3 color;", + + "varying vec2 vUV;", + "varying float vVisibility;", + + "void main() {", + + // pink square + + "if ( renderType == 0 ) {", + + "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", + + // restore + + "} else if ( renderType == 1 ) {", + + "gl_FragColor = texture2D( map, vUV );", + + // flare + + "} else {", + + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * vVisibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", + + "}", + + "}" + + ].join( "\n" ) + + }; + + } else { + + shader = { + + vertexShader: [ + + "uniform lowp int renderType;", + + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", + + "attribute vec2 position;", + "attribute vec2 uv;", + + "varying vec2 vUV;", + + "void main() {", + + "vUV = uv;", + + "vec2 pos = position;", + + "if ( renderType == 2 ) {", + + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + + "}", + + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "precision mediump float;", + + "uniform lowp int renderType;", + + "uniform sampler2D map;", + "uniform sampler2D occlusionMap;", + "uniform float opacity;", + "uniform vec3 color;", + + "varying vec2 vUV;", + + "void main() {", + + // pink square + + "if ( renderType == 0 ) {", + + "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );", + + // restore + + "} else if ( renderType == 1 ) {", + + "gl_FragColor = texture2D( map, vUV );", + + // flare + + "} else {", + + "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;", + "visibility = ( 1.0 - visibility / 4.0 );", + + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * visibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", + + "}", + + "}" + + ].join( "\n" ) + + }; + + } + + program = createProgram( shader ); + + attributes = { + vertex: gl.getAttribLocation ( program, "position" ), + uv: gl.getAttribLocation ( program, "uv" ) + }; + + uniforms = { + renderType: gl.getUniformLocation( program, "renderType" ), + map: gl.getUniformLocation( program, "map" ), + occlusionMap: gl.getUniformLocation( program, "occlusionMap" ), + opacity: gl.getUniformLocation( program, "opacity" ), + color: gl.getUniformLocation( program, "color" ), + scale: gl.getUniformLocation( program, "scale" ), + rotation: gl.getUniformLocation( program, "rotation" ), + screenPosition: gl.getUniformLocation( program, "screenPosition" ) + }; + + } + + /* + * Render lens flares + * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, + * reads these back and calculates occlusion. + */ + + this.render = function ( scene, camera, viewportWidth, viewportHeight ) { + + if ( flares.length === 0 ) return; + + var tempPosition = new THREE.Vector3(); + + var invAspect = viewportHeight / viewportWidth, + halfViewportWidth = viewportWidth * 0.5, + halfViewportHeight = viewportHeight * 0.5; + + var size = 16 / viewportHeight, + scale = new THREE.Vector2( size * invAspect, size ); + + var screenPosition = new THREE.Vector3( 1, 1, 0 ), + screenPositionPixels = new THREE.Vector2( 1, 1 ); + + if ( program === undefined ) { + + init(); + + } + + gl.useProgram( program ); + + state.initAttributes(); + state.enableAttribute( attributes.vertex ); + state.enableAttribute( attributes.uv ); + state.disableUnusedAttributes(); + + // loop through all lens flares to update their occlusion and positions + // setup gl and common used attribs/uniforms + + gl.uniform1i( uniforms.occlusionMap, 0 ); + gl.uniform1i( uniforms.map, 1 ); + + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.vertexAttribPointer( attributes.vertex, 2, gl.FLOAT, false, 2 * 8, 0 ); + gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); + + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + + state.disable( gl.CULL_FACE ); + gl.depthMask( false ); + + for ( var i = 0, l = flares.length; i < l; i ++ ) { + + size = 16 / viewportHeight; + scale.set( size * invAspect, size ); + + // calc object screen position + + var flare = flares[ i ]; + + tempPosition.set( flare.matrixWorld.elements[ 12 ], flare.matrixWorld.elements[ 13 ], flare.matrixWorld.elements[ 14 ] ); + + tempPosition.applyMatrix4( camera.matrixWorldInverse ); + tempPosition.applyProjection( camera.projectionMatrix ); + + // setup arrays for gl programs + + screenPosition.copy( tempPosition ); + + screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth; + screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight; + + // screen cull + + if ( hasVertexTexture || ( + screenPositionPixels.x > 0 && + screenPositionPixels.x < viewportWidth && + screenPositionPixels.y > 0 && + screenPositionPixels.y < viewportHeight ) ) { + + // save current RGB to temp texture + + state.activeTexture( gl.TEXTURE0 ); + state.bindTexture( gl.TEXTURE_2D, null ); + state.activeTexture( gl.TEXTURE1 ); + state.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + + + // render pink quad + + gl.uniform1i( uniforms.renderType, 0 ); + gl.uniform2f( uniforms.scale, scale.x, scale.y ); + gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + + state.disable( gl.BLEND ); + state.enable( gl.DEPTH_TEST ); + + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + + // copy result to occlusionMap + + state.activeTexture( gl.TEXTURE0 ); + state.bindTexture( gl.TEXTURE_2D, occlusionTexture ); + gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + + + // restore graphics + + gl.uniform1i( uniforms.renderType, 1 ); + state.disable( gl.DEPTH_TEST ); + + state.activeTexture( gl.TEXTURE1 ); + state.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + + // update object positions + + flare.positionScreen.copy( screenPosition ); + + if ( flare.customUpdateCallback ) { + + flare.customUpdateCallback( flare ); + + } else { + + flare.updateLensFlares(); + + } + + // render flares + + gl.uniform1i( uniforms.renderType, 2 ); + state.enable( gl.BLEND ); + + for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { + + var sprite = flare.lensFlares[ j ]; + + if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { + + screenPosition.x = sprite.x; + screenPosition.y = sprite.y; + screenPosition.z = sprite.z; + + size = sprite.size * sprite.scale / viewportHeight; + + scale.x = size * invAspect; + scale.y = size; + + gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + gl.uniform2f( uniforms.scale, scale.x, scale.y ); + gl.uniform1f( uniforms.rotation, sprite.rotation ); + + gl.uniform1f( uniforms.opacity, sprite.opacity ); + gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); + + state.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); + renderer.setTexture( sprite.texture, 1 ); + + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + } + + } + + } + + } + + // restore gl + + state.enable( gl.CULL_FACE ); + state.enable( gl.DEPTH_TEST ); + gl.depthMask( true ); + + renderer.resetGLState(); + + }; + + function createProgram ( shader ) { + + var program = gl.createProgram(); + + var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); + var vertexShader = gl.createShader( gl.VERTEX_SHADER ); + + var prefix = "precision " + renderer.getPrecision() + " float;\n"; + + gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); + gl.shaderSource( vertexShader, prefix + shader.vertexShader ); + + gl.compileShader( fragmentShader ); + gl.compileShader( vertexShader ); + + gl.attachShader( program, fragmentShader ); + gl.attachShader( program, vertexShader ); + + gl.linkProgram( program ); + + return program; + + } + +}; + +// File:src/renderers/webgl/plugins/SpritePlugin.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SpritePlugin = function ( renderer, sprites ) { + + var gl = renderer.context; + var state = renderer.state; + + var vertexBuffer, elementBuffer; + var program, attributes, uniforms; + + var texture; + + // decompose matrixWorld + + var spritePosition = new THREE.Vector3(); + var spriteRotation = new THREE.Quaternion(); + var spriteScale = new THREE.Vector3(); + + function init() { + + var vertices = new Float32Array( [ + - 0.5, - 0.5, 0, 0, + 0.5, - 0.5, 1, 0, + 0.5, 0.5, 1, 1, + - 0.5, 0.5, 0, 1 + ] ); + + var faces = new Uint16Array( [ + 0, 1, 2, + 0, 2, 3 + ] ); + + vertexBuffer = gl.createBuffer(); + elementBuffer = gl.createBuffer(); + + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); + + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); + + program = createProgram(); + + attributes = { + position: gl.getAttribLocation ( program, 'position' ), + uv: gl.getAttribLocation ( program, 'uv' ) + }; + + uniforms = { + uvOffset: gl.getUniformLocation( program, 'uvOffset' ), + uvScale: gl.getUniformLocation( program, 'uvScale' ), + + rotation: gl.getUniformLocation( program, 'rotation' ), + scale: gl.getUniformLocation( program, 'scale' ), + + color: gl.getUniformLocation( program, 'color' ), + map: gl.getUniformLocation( program, 'map' ), + opacity: gl.getUniformLocation( program, 'opacity' ), + + modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ), + projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ), + + fogType: gl.getUniformLocation( program, 'fogType' ), + fogDensity: gl.getUniformLocation( program, 'fogDensity' ), + fogNear: gl.getUniformLocation( program, 'fogNear' ), + fogFar: gl.getUniformLocation( program, 'fogFar' ), + fogColor: gl.getUniformLocation( program, 'fogColor' ), + + alphaTest: gl.getUniformLocation( program, 'alphaTest' ) + }; + + var canvas = document.createElement( 'canvas' ); + canvas.width = 8; + canvas.height = 8; + + var context = canvas.getContext( '2d' ); + context.fillStyle = 'white'; + context.fillRect( 0, 0, 8, 8 ); + + texture = new THREE.Texture( canvas ); + texture.needsUpdate = true; + + } + + this.render = function ( scene, camera ) { + + if ( sprites.length === 0 ) return; + + // setup gl + + if ( program === undefined ) { + + init(); + + } + + gl.useProgram( program ); + + state.initAttributes(); + state.enableAttribute( attributes.position ); + state.enableAttribute( attributes.uv ); + state.disableUnusedAttributes(); + + state.disable( gl.CULL_FACE ); + state.enable( gl.BLEND ); + + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 ); + gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); + + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + + gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + + state.activeTexture( gl.TEXTURE0 ); + gl.uniform1i( uniforms.map, 0 ); + + var oldFogType = 0; + var sceneFogType = 0; + var fog = scene.fog; + + if ( fog ) { + + gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); + + if ( fog instanceof THREE.Fog ) { + + gl.uniform1f( uniforms.fogNear, fog.near ); + gl.uniform1f( uniforms.fogFar, fog.far ); + + gl.uniform1i( uniforms.fogType, 1 ); + oldFogType = 1; + sceneFogType = 1; + + } else if ( fog instanceof THREE.FogExp2 ) { + + gl.uniform1f( uniforms.fogDensity, fog.density ); + + gl.uniform1i( uniforms.fogType, 2 ); + oldFogType = 2; + sceneFogType = 2; + + } + + } else { + + gl.uniform1i( uniforms.fogType, 0 ); + oldFogType = 0; + sceneFogType = 0; + + } + + + // update positions and sort + + for ( var i = 0, l = sprites.length; i < l; i ++ ) { + + var sprite = sprites[ i ]; + + sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); + sprite.z = - sprite.modelViewMatrix.elements[ 14 ]; + + } + + sprites.sort( painterSortStable ); + + // render all sprites + + var scale = []; + + for ( var i = 0, l = sprites.length; i < l; i ++ ) { + + var sprite = sprites[ i ]; + var material = sprite.material; + + gl.uniform1f( uniforms.alphaTest, material.alphaTest ); + gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements ); + + sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale ); + + scale[ 0 ] = spriteScale.x; + scale[ 1 ] = spriteScale.y; + + var fogType = 0; + + if ( scene.fog && material.fog ) { + + fogType = sceneFogType; + + } + + if ( oldFogType !== fogType ) { + + gl.uniform1i( uniforms.fogType, fogType ); + oldFogType = fogType; + + } + + if ( material.map !== null ) { + + gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); + gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); + + } else { + + gl.uniform2f( uniforms.uvOffset, 0, 0 ); + gl.uniform2f( uniforms.uvScale, 1, 1 ); + + } + + gl.uniform1f( uniforms.opacity, material.opacity ); + gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); + + gl.uniform1f( uniforms.rotation, material.rotation ); + gl.uniform2fv( uniforms.scale, scale ); + + state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + state.setDepthTest( material.depthTest ); + state.setDepthWrite( material.depthWrite ); + + if ( material.map && material.map.image && material.map.image.width ) { + + renderer.setTexture( material.map, 0 ); + + } else { + + renderer.setTexture( texture, 0 ); + + } + + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + } + + // restore gl + + state.enable( gl.CULL_FACE ); + + renderer.resetGLState(); + + }; + + function createProgram () { + + var program = gl.createProgram(); + + var vertexShader = gl.createShader( gl.VERTEX_SHADER ); + var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); + + gl.shaderSource( vertexShader, [ + + 'precision ' + renderer.getPrecision() + ' float;', + + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform float rotation;', + 'uniform vec2 scale;', + 'uniform vec2 uvOffset;', + 'uniform vec2 uvScale;', + + 'attribute vec2 position;', + 'attribute vec2 uv;', + + 'varying vec2 vUV;', + + 'void main() {', + + 'vUV = uvOffset + uv * uvScale;', + + 'vec2 alignedPosition = position * scale;', + + 'vec2 rotatedPosition;', + 'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;', + 'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;', + + 'vec4 finalPosition;', + + 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );', + 'finalPosition.xy += rotatedPosition;', + 'finalPosition = projectionMatrix * finalPosition;', + + 'gl_Position = finalPosition;', + + '}' + + ].join( '\n' ) ); + + gl.shaderSource( fragmentShader, [ + + 'precision ' + renderer.getPrecision() + ' float;', + + 'uniform vec3 color;', + 'uniform sampler2D map;', + 'uniform float opacity;', + + 'uniform int fogType;', + 'uniform vec3 fogColor;', + 'uniform float fogDensity;', + 'uniform float fogNear;', + 'uniform float fogFar;', + 'uniform float alphaTest;', + + 'varying vec2 vUV;', + + 'void main() {', + + 'vec4 texture = texture2D( map, vUV );', + + 'if ( texture.a < alphaTest ) discard;', + + 'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );', + + 'if ( fogType > 0 ) {', + + 'float depth = gl_FragCoord.z / gl_FragCoord.w;', + 'float fogFactor = 0.0;', + + 'if ( fogType == 1 ) {', + + 'fogFactor = smoothstep( fogNear, fogFar, depth );', + + '} else {', + + 'const float LOG2 = 1.442695;', + 'fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );', + 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );', + + '}', + + 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );', + + '}', + + '}' + + ].join( '\n' ) ); + + gl.compileShader( vertexShader ); + gl.compileShader( fragmentShader ); + + gl.attachShader( program, vertexShader ); + gl.attachShader( program, fragmentShader ); + + gl.linkProgram( program ); + + return program; + + } + + function painterSortStable ( a, b ) { + + if ( a.z !== b.z ) { + + return b.z - a.z; + + } else { + + return b.id - a.id; + + } + + } + +}; + +// File:src/extras/CurveUtils.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.CurveUtils = { + + tangentQuadraticBezier: function ( t, p0, p1, p2 ) { + + return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 ); + + }, + + // Puay Bing, thanks for helping with this derivative! + + tangentCubicBezier: function ( t, p0, p1, p2, p3 ) { + + return - 3 * p0 * ( 1 - t ) * ( 1 - t ) + + 3 * p1 * ( 1 - t ) * ( 1 - t ) - 6 * t * p1 * ( 1 - t ) + + 6 * t * p2 * ( 1 - t ) - 3 * t * t * p2 + + 3 * t * t * p3; + + }, + + tangentSpline: function ( t, p0, p1, p2, p3 ) { + + // To check if my formulas are correct + + var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1 + var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t + var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2 + var h11 = 3 * t * t - 2 * t; // t3 − t2 + + return h00 + h10 + h01 + h11; + + }, + + // Catmull-Rom + + interpolate: function( p0, p1, p2, p3, t ) { + + var v0 = ( p2 - p0 ) * 0.5; + var v1 = ( p3 - p1 ) * 0.5; + var t2 = t * t; + var t3 = t * t2; + return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + } + +}; + +// File:src/extras/GeometryUtils.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.GeometryUtils = { + + merge: function ( geometry1, geometry2, materialIndexOffset ) { + + console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' ); + + var matrix; + + if ( geometry2 instanceof THREE.Mesh ) { + + geometry2.matrixAutoUpdate && geometry2.updateMatrix(); + + matrix = geometry2.matrix; + geometry2 = geometry2.geometry; + + } + + geometry1.merge( geometry2, matrix, materialIndexOffset ); + + }, + + center: function ( geometry ) { + + console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' ); + return geometry.center(); + + } + +}; + +// File:src/extras/ImageUtils.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author Daosheng Mu / https://github.com/DaoshengMu/ + */ + +THREE.ImageUtils = { + + crossOrigin: undefined, + + loadTexture: function ( url, mapping, onLoad, onError ) { + + console.warn( 'THREE.ImageUtils.loadTexture is being deprecated. Use THREE.TextureLoader() instead.' ); + + var loader = new THREE.TextureLoader(); + loader.setCrossOrigin( this.crossOrigin ); + + var texture = loader.load( url, onLoad, undefined, onError ); + + if ( mapping ) texture.mapping = mapping; + + return texture; + + }, + + loadTextureCube: function ( urls, mapping, onLoad, onError ) { + + console.warn( 'THREE.ImageUtils.loadTextureCube is being deprecated. Use THREE.CubeTextureLoader() instead.' ); + + var loader = new THREE.CubeTextureLoader(); + loader.setCrossOrigin( this.crossOrigin ); + + var texture = loader.load( urls, onLoad, undefined, onError ); + + if ( mapping ) texture.mapping = mapping; + + return texture; + + }, + + loadCompressedTexture: function () { + + console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ) + + }, + + loadCompressedTextureCube: function () { + + console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ) + + } + +}; + +// File:src/extras/SceneUtils.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SceneUtils = { + + createMultiMaterialObject: function ( geometry, materials ) { + + var group = new THREE.Group(); + + for ( var i = 0, l = materials.length; i < l; i ++ ) { + + group.add( new THREE.Mesh( geometry, materials[ i ] ) ); + + } + + return group; + + }, + + detach: function ( child, parent, scene ) { + + child.applyMatrix( parent.matrixWorld ); + parent.remove( child ); + scene.add( child ); + + }, + + attach: function ( child, scene, parent ) { + + var matrixWorldInverse = new THREE.Matrix4(); + matrixWorldInverse.getInverse( parent.matrixWorld ); + child.applyMatrix( matrixWorldInverse ); + + scene.remove( child ); + parent.add( child ); + + } + +}; + +// File:src/extras/ShapeUtils.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.ShapeUtils = { + + // calculate area of the contour polygon + + area: function ( contour ) { + + var n = contour.length; + var a = 0.0; + + for ( var p = n - 1, q = 0; q < n; p = q ++ ) { + + a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; + + } + + return a * 0.5; + + }, + + triangulate: ( function () { + + /** + * This code is a quick port of code written in C++ which was submitted to + * flipcode.com by John W. Ratcliff // July 22, 2000 + * See original code and more information here: + * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml + * + * ported to actionscript by Zevan Rosser + * www.actionsnippet.com + * + * ported to javascript by Joshua Koo + * http://www.lab4games.net/zz85/blog + * + */ + + function snip( contour, u, v, w, n, verts ) { + + var p; + var ax, ay, bx, by; + var cx, cy, px, py; + + ax = contour[ verts[ u ] ].x; + ay = contour[ verts[ u ] ].y; + + bx = contour[ verts[ v ] ].x; + by = contour[ verts[ v ] ].y; + + cx = contour[ verts[ w ] ].x; + cy = contour[ verts[ w ] ].y; + + if ( Number.EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false; + + var aX, aY, bX, bY, cX, cY; + var apx, apy, bpx, bpy, cpx, cpy; + var cCROSSap, bCROSScp, aCROSSbp; + + aX = cx - bx; aY = cy - by; + bX = ax - cx; bY = ay - cy; + cX = bx - ax; cY = by - ay; + + for ( p = 0; p < n; p ++ ) { + + px = contour[ verts[ p ] ].x; + py = contour[ verts[ p ] ].y; + + if ( ( ( px === ax ) && ( py === ay ) ) || + ( ( px === bx ) && ( py === by ) ) || + ( ( px === cx ) && ( py === cy ) ) ) continue; + + apx = px - ax; apy = py - ay; + bpx = px - bx; bpy = py - by; + cpx = px - cx; cpy = py - cy; + + // see if p is inside triangle abc + + aCROSSbp = aX * bpy - aY * bpx; + cCROSSap = cX * apy - cY * apx; + bCROSScp = bX * cpy - bY * cpx; + + if ( ( aCROSSbp >= - Number.EPSILON ) && ( bCROSScp >= - Number.EPSILON ) && ( cCROSSap >= - Number.EPSILON ) ) return false; + + } + + return true; + + } + + // takes in an contour array and returns + + return function ( contour, indices ) { + + var n = contour.length; + + if ( n < 3 ) return null; + + var result = [], + verts = [], + vertIndices = []; + + /* we want a counter-clockwise polygon in verts */ + + var u, v, w; + + if ( THREE.ShapeUtils.area( contour ) > 0.0 ) { + + for ( v = 0; v < n; v ++ ) verts[ v ] = v; + + } else { + + for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v; + + } + + var nv = n; + + /* remove nv - 2 vertices, creating 1 triangle every time */ + + var count = 2 * nv; /* error detection */ + + for ( v = nv - 1; nv > 2; ) { + + /* if we loop, it is probably a non-simple polygon */ + + if ( ( count -- ) <= 0 ) { + + //** Triangulate: ERROR - probable bad polygon! + + //throw ( "Warning, unable to triangulate polygon!" ); + //return null; + // Sometimes warning is fine, especially polygons are triangulated in reverse. + console.warn( 'THREE.ShapeUtils: Unable to triangulate polygon! in triangulate()' ); + + if ( indices ) return vertIndices; + return result; + + } + + /* three consecutive vertices in current polygon, */ + + u = v; if ( nv <= u ) u = 0; /* previous */ + v = u + 1; if ( nv <= v ) v = 0; /* new v */ + w = v + 1; if ( nv <= w ) w = 0; /* next */ + + if ( snip( contour, u, v, w, nv, verts ) ) { + + var a, b, c, s, t; + + /* true names of the vertices */ + + a = verts[ u ]; + b = verts[ v ]; + c = verts[ w ]; + + /* output Triangle */ + + result.push( [ contour[ a ], + contour[ b ], + contour[ c ] ] ); + + + vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); + + /* remove v from the remaining polygon */ + + for ( s = v, t = v + 1; t < nv; s ++, t ++ ) { + + verts[ s ] = verts[ t ]; + + } + + nv --; + + /* reset error detection counter */ + + count = 2 * nv; + + } + + } + + if ( indices ) return vertIndices; + return result; + + } + + } )(), + + triangulateShape: function ( contour, holes ) { + + function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) { + + // inOtherPt needs to be collinear to the inSegment + if ( inSegPt1.x !== inSegPt2.x ) { + + if ( inSegPt1.x < inSegPt2.x ) { + + return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) ); + + } else { + + return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) ); + + } + + } else { + + if ( inSegPt1.y < inSegPt2.y ) { + + return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) ); + + } else { + + return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) ); + + } + + } + + } + + function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) { + + var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y; + var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y; + + var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x; + var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y; + + var limit = seg1dy * seg2dx - seg1dx * seg2dy; + var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy; + + if ( Math.abs( limit ) > Number.EPSILON ) { + + // not parallel + + var perpSeg2; + if ( limit > 0 ) { + + if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return []; + perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; + if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return []; + + } else { + + if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return []; + perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; + if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return []; + + } + + // i.e. to reduce rounding errors + // intersection at endpoint of segment#1? + if ( perpSeg2 === 0 ) { + + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; + return [ inSeg1Pt1 ]; + + } + if ( perpSeg2 === limit ) { + + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; + return [ inSeg1Pt2 ]; + + } + // intersection at endpoint of segment#2? + if ( perpSeg1 === 0 ) return [ inSeg2Pt1 ]; + if ( perpSeg1 === limit ) return [ inSeg2Pt2 ]; + + // return real intersection point + var factorSeg1 = perpSeg2 / limit; + return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx, + y: inSeg1Pt1.y + factorSeg1 * seg1dy } ]; + + } else { + + // parallel or collinear + if ( ( perpSeg1 !== 0 ) || + ( seg2dy * seg1seg2dx !== seg2dx * seg1seg2dy ) ) return []; + + // they are collinear or degenerate + var seg1Pt = ( ( seg1dx === 0 ) && ( seg1dy === 0 ) ); // segment1 is just a point? + var seg2Pt = ( ( seg2dx === 0 ) && ( seg2dy === 0 ) ); // segment2 is just a point? + // both segments are points + if ( seg1Pt && seg2Pt ) { + + if ( ( inSeg1Pt1.x !== inSeg2Pt1.x ) || + ( inSeg1Pt1.y !== inSeg2Pt1.y ) ) return []; // they are distinct points + return [ inSeg1Pt1 ]; // they are the same point + + } + // segment#1 is a single point + if ( seg1Pt ) { + + if ( ! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2 + return [ inSeg1Pt1 ]; + + } + // segment#2 is a single point + if ( seg2Pt ) { + + if ( ! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1 + return [ inSeg2Pt1 ]; + + } + + // they are collinear segments, which might overlap + var seg1min, seg1max, seg1minVal, seg1maxVal; + var seg2min, seg2max, seg2minVal, seg2maxVal; + if ( seg1dx !== 0 ) { + + // the segments are NOT on a vertical line + if ( inSeg1Pt1.x < inSeg1Pt2.x ) { + + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x; + + } else { + + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x; + + } + if ( inSeg2Pt1.x < inSeg2Pt2.x ) { + + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x; + + } else { + + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x; + + } + + } else { + + // the segments are on a vertical line + if ( inSeg1Pt1.y < inSeg1Pt2.y ) { + + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y; + + } else { + + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y; + + } + if ( inSeg2Pt1.y < inSeg2Pt2.y ) { + + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y; + + } else { + + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y; + + } + + } + if ( seg1minVal <= seg2minVal ) { + + if ( seg1maxVal < seg2minVal ) return []; + if ( seg1maxVal === seg2minVal ) { + + if ( inExcludeAdjacentSegs ) return []; + return [ seg2min ]; + + } + if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ]; + return [ seg2min, seg2max ]; + + } else { + + if ( seg1minVal > seg2maxVal ) return []; + if ( seg1minVal === seg2maxVal ) { + + if ( inExcludeAdjacentSegs ) return []; + return [ seg1min ]; + + } + if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ]; + return [ seg1min, seg2max ]; + + } + + } + + } + + function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) { + + // The order of legs is important + + // translation of all points, so that Vertex is at (0,0) + var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y; + var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y; + var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y; + + // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg. + var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX; + var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX; + + if ( Math.abs( from2toAngle ) > Number.EPSILON ) { + + // angle != 180 deg. + + var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX; + // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle ); + + if ( from2toAngle > 0 ) { + + // main angle < 180 deg. + return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) ); + + } else { + + // main angle > 180 deg. + return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) ); + + } + + } else { + + // angle == 180 deg. + // console.log( "from2to: 180 deg., from2other: " + from2otherAngle ); + return ( from2otherAngle > 0 ); + + } + + } + + + function removeHoles( contour, holes ) { + + var shape = contour.concat(); // work on this shape + var hole; + + function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) { + + // Check if hole point lies within angle around shape point + var lastShapeIdx = shape.length - 1; + + var prevShapeIdx = inShapeIdx - 1; + if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx; + + var nextShapeIdx = inShapeIdx + 1; + if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0; + + var insideAngle = isPointInsideAngle( shape[ inShapeIdx ], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[ inHoleIdx ] ); + if ( ! insideAngle ) { + + // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y ); + return false; + + } + + // Check if shape point lies within angle around hole point + var lastHoleIdx = hole.length - 1; + + var prevHoleIdx = inHoleIdx - 1; + if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx; + + var nextHoleIdx = inHoleIdx + 1; + if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0; + + insideAngle = isPointInsideAngle( hole[ inHoleIdx ], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[ inShapeIdx ] ); + if ( ! insideAngle ) { + + // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y ); + return false; + + } + + return true; + + } + + function intersectsShapeEdge( inShapePt, inHolePt ) { + + // checks for intersections with shape edges + var sIdx, nextIdx, intersection; + for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) { + + nextIdx = sIdx + 1; nextIdx %= shape.length; + intersection = intersect_segments_2D( inShapePt, inHolePt, shape[ sIdx ], shape[ nextIdx ], true ); + if ( intersection.length > 0 ) return true; + + } + + return false; + + } + + var indepHoles = []; + + function intersectsHoleEdge( inShapePt, inHolePt ) { + + // checks for intersections with hole edges + var ihIdx, chkHole, + hIdx, nextIdx, intersection; + for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) { + + chkHole = holes[ indepHoles[ ihIdx ]]; + for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) { + + nextIdx = hIdx + 1; nextIdx %= chkHole.length; + intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[ hIdx ], chkHole[ nextIdx ], true ); + if ( intersection.length > 0 ) return true; + + } + + } + return false; + + } + + var holeIndex, shapeIndex, + shapePt, holePt, + holeIdx, cutKey, failedCuts = [], + tmpShape1, tmpShape2, + tmpHole1, tmpHole2; + + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + + indepHoles.push( h ); + + } + + var minShapeIndex = 0; + var counter = indepHoles.length * 2; + while ( indepHoles.length > 0 ) { + + counter --; + if ( counter < 0 ) { + + console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" ); + break; + + } + + // search for shape-vertex and hole-vertex, + // which can be connected without intersections + for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) { + + shapePt = shape[ shapeIndex ]; + holeIndex = - 1; + + // search for hole which can be reached without intersections + for ( var h = 0; h < indepHoles.length; h ++ ) { + + holeIdx = indepHoles[ h ]; + + // prevent multiple checks + cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx; + if ( failedCuts[ cutKey ] !== undefined ) continue; + + hole = holes[ holeIdx ]; + for ( var h2 = 0; h2 < hole.length; h2 ++ ) { + + holePt = hole[ h2 ]; + if ( ! isCutLineInsideAngles( shapeIndex, h2 ) ) continue; + if ( intersectsShapeEdge( shapePt, holePt ) ) continue; + if ( intersectsHoleEdge( shapePt, holePt ) ) continue; + + holeIndex = h2; + indepHoles.splice( h, 1 ); + + tmpShape1 = shape.slice( 0, shapeIndex + 1 ); + tmpShape2 = shape.slice( shapeIndex ); + tmpHole1 = hole.slice( holeIndex ); + tmpHole2 = hole.slice( 0, holeIndex + 1 ); + + shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); + + minShapeIndex = shapeIndex; + + // Debug only, to show the selected cuts + // glob_CutLines.push( [ shapePt, holePt ] ); + + break; + + } + if ( holeIndex >= 0 ) break; // hole-vertex found + + failedCuts[ cutKey ] = true; // remember failure + + } + if ( holeIndex >= 0 ) break; // hole-vertex found + + } + + } + + return shape; /* shape with no holes */ + + } + + + var i, il, f, face, + key, index, + allPointsMap = {}; + + // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first. + + var allpoints = contour.concat(); + + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + + Array.prototype.push.apply( allpoints, holes[ h ] ); + + } + + //console.log( "allpoints",allpoints, allpoints.length ); + + // prepare all points map + + for ( i = 0, il = allpoints.length; i < il; i ++ ) { + + key = allpoints[ i ].x + ":" + allpoints[ i ].y; + + if ( allPointsMap[ key ] !== undefined ) { + + console.warn( "THREE.Shape: Duplicate point", key ); + + } + + allPointsMap[ key ] = i; + + } + + // remove holes by cutting paths to holes and adding them to the shape + var shapeWithoutHoles = removeHoles( contour, holes ); + + var triangles = THREE.ShapeUtils.triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape + //console.log( "triangles",triangles, triangles.length ); + + // check all face vertices against all points map + + for ( i = 0, il = triangles.length; i < il; i ++ ) { + + face = triangles[ i ]; + + for ( f = 0; f < 3; f ++ ) { + + key = face[ f ].x + ":" + face[ f ].y; + + index = allPointsMap[ key ]; + + if ( index !== undefined ) { + + face[ f ] = index; + + } + + } + + } + + return triangles.concat(); + + }, + + isClockWise: function ( pts ) { + + return THREE.ShapeUtils.area( pts ) < 0; + + }, + + // Bezier Curves formulas obtained from + // http://en.wikipedia.org/wiki/B%C3%A9zier_curve + + // Quad Bezier Functions + + b2: ( function () { + + function b2p0( t, p ) { + + var k = 1 - t; + return k * k * p; + + } + + function b2p1( t, p ) { + + return 2 * ( 1 - t ) * t * p; + + } + + function b2p2( t, p ) { + + return t * t * p; + + } + + return function ( t, p0, p1, p2 ) { + + return b2p0( t, p0 ) + b2p1( t, p1 ) + b2p2( t, p2 ); + + }; + + } )(), + + // Cubic Bezier Functions + + b3: ( function () { + + function b3p0( t, p ) { + + var k = 1 - t; + return k * k * k * p; + + } + + function b3p1( t, p ) { + + var k = 1 - t; + return 3 * k * k * t * p; + + } + + function b3p2( t, p ) { + + var k = 1 - t; + return 3 * k * t * t * p; + + } + + function b3p3( t, p ) { + + return t * t * t * p; + + } + + return function ( t, p0, p1, p2, p3 ) { + + return b3p0( t, p0 ) + b3p1( t, p1 ) + b3p2( t, p2 ) + b3p3( t, p3 ); + + }; + + } )() + +}; + +// File:src/extras/audio/Audio.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Audio = function ( listener ) { + + THREE.Object3D.call( this ); + + this.type = 'Audio'; + + this.context = listener.context; + this.source = this.context.createBufferSource(); + this.source.onended = this.onEnded.bind( this ); + + this.gain = this.context.createGain(); + this.gain.connect( this.context.destination ); + + this.panner = this.context.createPanner(); + this.panner.connect( this.gain ); + + this.autoplay = false; + + this.startTime = 0; + this.playbackRate = 1; + this.isPlaying = false; + +}; + +THREE.Audio.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Audio.prototype.constructor = THREE.Audio; + +THREE.Audio.prototype.load = function ( file ) { + + var scope = this; + + var request = new XMLHttpRequest(); + request.open( 'GET', file, true ); + request.responseType = 'arraybuffer'; + request.onload = function ( e ) { + + scope.context.decodeAudioData( this.response, function ( buffer ) { + + scope.source.buffer = buffer; + + if ( scope.autoplay ) scope.play(); + + } ); + + }; + request.send(); + + return this; + +}; + +THREE.Audio.prototype.play = function () { + + if ( this.isPlaying === true ) { + + console.warn( 'THREE.Audio: Audio is already playing.' ); + return; + + } + + var source = this.context.createBufferSource(); + + source.buffer = this.source.buffer; + source.loop = this.source.loop; + source.onended = this.source.onended; + source.start( 0, this.startTime ); + source.playbackRate.value = this.playbackRate; + + this.isPlaying = true; + + this.source = source; + + this.connect(); + +}; + +THREE.Audio.prototype.pause = function () { + + this.source.stop(); + this.startTime = this.context.currentTime; + +}; + +THREE.Audio.prototype.stop = function () { + + this.source.stop(); + this.startTime = 0; + +}; + +THREE.Audio.prototype.connect = function () { + + if ( this.filter !== undefined ) { + + this.source.connect( this.filter ); + this.filter.connect( this.panner ); + + } else { + + this.source.connect( this.panner ); + + } + +}; + +THREE.Audio.prototype.disconnect = function () { + + if ( this.filter !== undefined ) { + + this.source.disconnect( this.filter ); + this.filter.disconnect( this.panner ); + + } else { + + this.source.disconnect( this.panner ); + + } + +}; + +THREE.Audio.prototype.setFilter = function ( value ) { + + if ( this.isPlaying === true ) { + + this.disconnect(); + this.filter = value; + this.connect(); + + } else { + + this.filter = value; + + } + +}; + +THREE.Audio.prototype.getFilter = function () { + + return this.filter; + +}; + +THREE.Audio.prototype.setPlaybackRate = function ( value ) { + + this.playbackRate = value; + + if ( this.isPlaying === true ) { + + this.source.playbackRate.value = this.playbackRate; + + } + +}; + +THREE.Audio.prototype.getPlaybackRate = function () { + + return this.playbackRate; + +}; + +THREE.Audio.prototype.onEnded = function() { + + this.isPlaying = false; + +}; + +THREE.Audio.prototype.setLoop = function ( value ) { + + this.source.loop = value; + +}; + +THREE.Audio.prototype.getLoop = function () { + + return this.source.loop; + +}; + +THREE.Audio.prototype.setRefDistance = function ( value ) { + + this.panner.refDistance = value; + +}; + +THREE.Audio.prototype.getRefDistance = function () { + + return this.panner.refDistance; + +}; + +THREE.Audio.prototype.setRolloffFactor = function ( value ) { + + this.panner.rolloffFactor = value; + +}; + +THREE.Audio.prototype.getRolloffFactor = function () { + + return this.panner.rolloffFactor; + +}; + +THREE.Audio.prototype.setVolume = function ( value ) { + + this.gain.gain.value = value; + +}; + +THREE.Audio.prototype.getVolume = function () { + + return this.gain.gain.value; + +}; + +THREE.Audio.prototype.updateMatrixWorld = ( function () { + + var position = new THREE.Vector3(); + + return function updateMatrixWorld( force ) { + + THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); + + position.setFromMatrixPosition( this.matrixWorld ); + + this.panner.setPosition( position.x, position.y, position.z ); + + }; + +} )(); + +// File:src/extras/audio/AudioListener.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AudioListener = function () { + + THREE.Object3D.call( this ); + + this.type = 'AudioListener'; + + this.context = new ( window.AudioContext || window.webkitAudioContext )(); + +}; + +THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype ); +THREE.AudioListener.prototype.constructor = THREE.AudioListener; + +THREE.AudioListener.prototype.updateMatrixWorld = ( function () { + + var position = new THREE.Vector3(); + var quaternion = new THREE.Quaternion(); + var scale = new THREE.Vector3(); + + var orientation = new THREE.Vector3(); + + return function updateMatrixWorld( force ) { + + THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); + + var listener = this.context.listener; + var up = this.up; + + this.matrixWorld.decompose( position, quaternion, scale ); + + orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion ); + + listener.setPosition( position.x, position.y, position.z ); + listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z ); + + }; + +} )(); + +// File:src/extras/core/Curve.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Extensible curve object + * + * Some common of Curve methods + * .getPoint(t), getTangent(t) + * .getPointAt(u), getTagentAt(u) + * .getPoints(), .getSpacedPoints() + * .getLength() + * .updateArcLengths() + * + * This following classes subclasses THREE.Curve: + * + * -- 2d classes -- + * THREE.LineCurve + * THREE.QuadraticBezierCurve + * THREE.CubicBezierCurve + * THREE.SplineCurve + * THREE.ArcCurve + * THREE.EllipseCurve + * + * -- 3d classes -- + * THREE.LineCurve3 + * THREE.QuadraticBezierCurve3 + * THREE.CubicBezierCurve3 + * THREE.SplineCurve3 + * THREE.ClosedSplineCurve3 + * + * A series of curves can be represented as a THREE.CurvePath + * + **/ + +/************************************************************** + * Abstract Curve base class + **************************************************************/ + +THREE.Curve = function () { + +}; + +THREE.Curve.prototype = { + + constructor: THREE.Curve, + + // Virtual base class method to overwrite and implement in subclasses + // - t [0 .. 1] + + getPoint: function ( t ) { + + console.warn( "THREE.Curve: Warning, getPoint() not implemented!" ); + return null; + + }, + + // Get point at relative position in curve according to arc length + // - u [0 .. 1] + + getPointAt: function ( u ) { + + var t = this.getUtoTmapping( u ); + return this.getPoint( t ); + + }, + + // Get sequence of points using getPoint( t ) + + getPoints: function ( divisions ) { + + if ( ! divisions ) divisions = 5; + + var d, pts = []; + + for ( d = 0; d <= divisions; d ++ ) { + + pts.push( this.getPoint( d / divisions ) ); + + } + + return pts; + + }, + + // Get sequence of points using getPointAt( u ) + + getSpacedPoints: function ( divisions ) { + + if ( ! divisions ) divisions = 5; + + var d, pts = []; + + for ( d = 0; d <= divisions; d ++ ) { + + pts.push( this.getPointAt( d / divisions ) ); + + } + + return pts; + + }, + + // Get total curve arc length + + getLength: function () { + + var lengths = this.getLengths(); + return lengths[ lengths.length - 1 ]; + + }, + + // Get list of cumulative segment lengths + + getLengths: function ( divisions ) { + + if ( ! divisions ) divisions = ( this.__arcLengthDivisions ) ? ( this.__arcLengthDivisions ) : 200; + + if ( this.cacheArcLengths + && ( this.cacheArcLengths.length === divisions + 1 ) + && ! this.needsUpdate ) { + + //console.log( "cached", this.cacheArcLengths ); + return this.cacheArcLengths; + + } + + this.needsUpdate = false; + + var cache = []; + var current, last = this.getPoint( 0 ); + var p, sum = 0; + + cache.push( 0 ); + + for ( p = 1; p <= divisions; p ++ ) { + + current = this.getPoint ( p / divisions ); + sum += current.distanceTo( last ); + cache.push( sum ); + last = current; + + } + + this.cacheArcLengths = cache; + + return cache; // { sums: cache, sum:sum }; Sum is in the last element. + + }, + + updateArcLengths: function() { + + this.needsUpdate = true; + this.getLengths(); + + }, + + // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant + + getUtoTmapping: function ( u, distance ) { + + var arcLengths = this.getLengths(); + + var i = 0, il = arcLengths.length; + + var targetArcLength; // The targeted u distance value to get + + if ( distance ) { + + targetArcLength = distance; + + } else { + + targetArcLength = u * arcLengths[ il - 1 ]; + + } + + //var time = Date.now(); + + // binary search for the index with largest value smaller than target u distance + + var low = 0, high = il - 1, comparison; + + while ( low <= high ) { + + i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats + + comparison = arcLengths[ i ] - targetArcLength; + + if ( comparison < 0 ) { + + low = i + 1; + + } else if ( comparison > 0 ) { + + high = i - 1; + + } else { + + high = i; + break; + + // DONE + + } + + } + + i = high; + + //console.log('b' , i, low, high, Date.now()- time); + + if ( arcLengths[ i ] === targetArcLength ) { + + var t = i / ( il - 1 ); + return t; + + } + + // we could get finer grain at lengths, or use simple interpolation between two points + + var lengthBefore = arcLengths[ i ]; + var lengthAfter = arcLengths[ i + 1 ]; + + var segmentLength = lengthAfter - lengthBefore; + + // determine where we are between the 'before' and 'after' points + + var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + + // add that fractional amount to t + + var t = ( i + segmentFraction ) / ( il - 1 ); + + return t; + + }, + + // Returns a unit vector tangent at t + // In case any sub curve does not implement its tangent derivation, + // 2 points a small delta apart will be used to find its gradient + // which seems to give a reasonable approximation + + getTangent: function( t ) { + + var delta = 0.0001; + var t1 = t - delta; + var t2 = t + delta; + + // Capping in case of danger + + if ( t1 < 0 ) t1 = 0; + if ( t2 > 1 ) t2 = 1; + + var pt1 = this.getPoint( t1 ); + var pt2 = this.getPoint( t2 ); + + var vec = pt2.clone().sub( pt1 ); + return vec.normalize(); + + }, + + getTangentAt: function ( u ) { + + var t = this.getUtoTmapping( u ); + return this.getTangent( t ); + + } + +} + +THREE.Curve.Utils = THREE.CurveUtils; // backwards compatibility + +// TODO: Transformation for Curves? + +/************************************************************** + * 3D Curves + **************************************************************/ + +// A Factory method for creating new curve subclasses + +THREE.Curve.create = function ( constructor, getPointFunc ) { + + constructor.prototype = Object.create( THREE.Curve.prototype ); + constructor.prototype.constructor = constructor; + constructor.prototype.getPoint = getPointFunc; + + return constructor; + +}; + +// File:src/extras/core/CurvePath.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + **/ + +/************************************************************** + * Curved Path - a curve path is simply a array of connected + * curves, but retains the api of a curve + **************************************************************/ + +THREE.CurvePath = function () { + + this.curves = []; + + this.autoClose = false; // Automatically closes the path + +}; + +THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype ); +THREE.CurvePath.prototype.constructor = THREE.CurvePath; + +THREE.CurvePath.prototype.add = function ( curve ) { + + this.curves.push( curve ); + +}; + +/* +THREE.CurvePath.prototype.checkConnection = function() { + // TODO + // If the ending of curve is not connected to the starting + // or the next curve, then, this is not a real path +}; +*/ + +THREE.CurvePath.prototype.closePath = function() { + + // TODO Test + // and verify for vector3 (needs to implement equals) + // Add a line curve if start and end of lines are not connected + var startPoint = this.curves[ 0 ].getPoint( 0 ); + var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); + + if ( ! startPoint.equals( endPoint ) ) { + + this.curves.push( new THREE.LineCurve( endPoint, startPoint ) ); + + } + +}; + +// To get accurate point with reference to +// entire path distance at time t, +// following has to be done: + +// 1. Length of each sub path have to be known +// 2. Locate and identify type of curve +// 3. Get t for the curve +// 4. Return curve.getPointAt(t') + +THREE.CurvePath.prototype.getPoint = function( t ) { + + var d = t * this.getLength(); + var curveLengths = this.getCurveLengths(); + var i = 0; + + // To think about boundaries points. + + while ( i < curveLengths.length ) { + + if ( curveLengths[ i ] >= d ) { + + var diff = curveLengths[ i ] - d; + var curve = this.curves[ i ]; + + var u = 1 - diff / curve.getLength(); + + return curve.getPointAt( u ); + + } + + i ++; + + } + + return null; + + // loop where sum != 0, sum > d , sum+1 0 ) { + + laste = points[ points.length - 1 ]; + + cpx0 = laste.x; + cpy0 = laste.y; + + } else { + + laste = this.actions[ i - 1 ].args; + + cpx0 = laste[ laste.length - 2 ]; + cpy0 = laste[ laste.length - 1 ]; + + } + + for ( var j = 1; j <= divisions; j ++ ) { + + var t = j / divisions; + + tx = b2( t, cpx0, cpx1, cpx ); + ty = b2( t, cpy0, cpy1, cpy ); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + break; + + case 'bezierCurveTo': + + cpx = args[ 4 ]; + cpy = args[ 5 ]; + + cpx1 = args[ 0 ]; + cpy1 = args[ 1 ]; + + cpx2 = args[ 2 ]; + cpy2 = args[ 3 ]; + + if ( points.length > 0 ) { + + laste = points[ points.length - 1 ]; + + cpx0 = laste.x; + cpy0 = laste.y; + + } else { + + laste = this.actions[ i - 1 ].args; + + cpx0 = laste[ laste.length - 2 ]; + cpy0 = laste[ laste.length - 1 ]; + + } + + + for ( var j = 1; j <= divisions; j ++ ) { + + var t = j / divisions; + + tx = b3( t, cpx0, cpx1, cpx2, cpx ); + ty = b3( t, cpy0, cpy1, cpy2, cpy ); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + break; + + case 'splineThru': + + laste = this.actions[ i - 1 ].args; + + var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] ); + var spts = [ last ]; + + var n = divisions * args[ 0 ].length; + + spts = spts.concat( args[ 0 ] ); + + var spline = new THREE.SplineCurve( spts ); + + for ( var j = 1; j <= n; j ++ ) { + + points.push( spline.getPointAt( j / n ) ); + + } + + break; + + case 'arc': + + var aX = args[ 0 ], aY = args[ 1 ], + aRadius = args[ 2 ], + aStartAngle = args[ 3 ], aEndAngle = args[ 4 ], + aClockwise = !! args[ 5 ]; + + var deltaAngle = aEndAngle - aStartAngle; + var angle; + var tdivisions = divisions * 2; + + for ( var j = 1; j <= tdivisions; j ++ ) { + + var t = j / tdivisions; + + if ( ! aClockwise ) { + + t = 1 - t; + + } + + angle = aStartAngle + t * deltaAngle; + + tx = aX + aRadius * Math.cos( angle ); + ty = aY + aRadius * Math.sin( angle ); + + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + //console.log(points); + + break; + + case 'ellipse': + + var aX = args[ 0 ], aY = args[ 1 ], + xRadius = args[ 2 ], + yRadius = args[ 3 ], + aStartAngle = args[ 4 ], aEndAngle = args[ 5 ], + aClockwise = !! args[ 6 ], + aRotation = args[ 7 ]; + + + var deltaAngle = aEndAngle - aStartAngle; + var angle; + var tdivisions = divisions * 2; + + var cos, sin; + if ( aRotation !== 0 ) { + + cos = Math.cos( aRotation ); + sin = Math.sin( aRotation ); + + } + + for ( var j = 1; j <= tdivisions; j ++ ) { + + var t = j / tdivisions; + + if ( ! aClockwise ) { + + t = 1 - t; + + } + + angle = aStartAngle + t * deltaAngle; + + tx = aX + xRadius * Math.cos( angle ); + ty = aY + yRadius * Math.sin( angle ); + + if ( aRotation !== 0 ) { + + var x = tx, y = ty; + + // Rotate the point about the center of the ellipse. + tx = ( x - aX ) * cos - ( y - aY ) * sin + aX; + ty = ( x - aX ) * sin + ( y - aY ) * cos + aY; + + } + + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + //console.log(points); + + break; + + } // end switch + + } + + + + // Normalize to remove the closing point by default. + var lastPoint = points[ points.length - 1 ]; + if ( Math.abs( lastPoint.x - points[ 0 ].x ) < Number.EPSILON && + Math.abs( lastPoint.y - points[ 0 ].y ) < Number.EPSILON ) + points.splice( points.length - 1, 1 ); + if ( closedPath ) { + + points.push( points[ 0 ] ); + + } + + return points; + +}; + +// +// Breaks path into shapes +// +// Assumptions (if parameter isCCW==true the opposite holds): +// - solid shapes are defined clockwise (CW) +// - holes are defined counterclockwise (CCW) +// +// If parameter noHoles==true: +// - all subPaths are regarded as solid shapes +// - definition order CW/CCW has no relevance +// + +THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { + + function extractSubpaths( inActions ) { + + var subPaths = [], lastPath = new THREE.Path(); + + for ( var i = 0, l = inActions.length; i < l; i ++ ) { + + var item = inActions[ i ]; + + var args = item.args; + var action = item.action; + + if ( action === 'moveTo' ) { + + if ( lastPath.actions.length !== 0 ) { + + subPaths.push( lastPath ); + lastPath = new THREE.Path(); + + } + + } + + lastPath[ action ].apply( lastPath, args ); + + } + + if ( lastPath.actions.length !== 0 ) { + + subPaths.push( lastPath ); + + } + + // console.log(subPaths); + + return subPaths; + + } + + function toShapesNoHoles( inSubpaths ) { + + var shapes = []; + + for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) { + + var tmpPath = inSubpaths[ i ]; + + var tmpShape = new THREE.Shape(); + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; + + shapes.push( tmpShape ); + + } + + //console.log("shape", shapes); + + return shapes; + + } + + function isPointInsidePolygon( inPt, inPolygon ) { + + var polyLen = inPolygon.length; + + // inPt on polygon contour => immediate success or + // toggling of inside/outside at every single! intersection point of an edge + // with the horizontal line through inPt, left of inPt + // not counting lowerY endpoints of edges and whole edges on that line + var inside = false; + for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { + + var edgeLowPt = inPolygon[ p ]; + var edgeHighPt = inPolygon[ q ]; + + var edgeDx = edgeHighPt.x - edgeLowPt.x; + var edgeDy = edgeHighPt.y - edgeLowPt.y; + + if ( Math.abs( edgeDy ) > Number.EPSILON ) { + + // not parallel + if ( edgeDy < 0 ) { + + edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; + edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; + + } + if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; + + if ( inPt.y === edgeLowPt.y ) { + + if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? + // continue; // no intersection or edgeLowPt => doesn't count !!! + + } else { + + var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); + if ( perpEdge === 0 ) return true; // inPt is on contour ? + if ( perpEdge < 0 ) continue; + inside = ! inside; // true intersection left of inPt + + } + + } else { + + // parallel or collinear + if ( inPt.y !== edgeLowPt.y ) continue; // parallel + // edge lies on the same horizontal line as inPt + if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || + ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! + // continue; + + } + + } + + return inside; + + } + + var isClockWise = THREE.ShapeUtils.isClockWise; + + var subPaths = extractSubpaths( this.actions ); + if ( subPaths.length === 0 ) return []; + + if ( noHoles === true ) return toShapesNoHoles( subPaths ); + + + var solid, tmpPath, tmpShape, shapes = []; + + if ( subPaths.length === 1 ) { + + tmpPath = subPaths[ 0 ]; + tmpShape = new THREE.Shape(); + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; + shapes.push( tmpShape ); + return shapes; + + } + + var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); + holesFirst = isCCW ? ! holesFirst : holesFirst; + + // console.log("Holes first", holesFirst); + + var betterShapeHoles = []; + var newShapes = []; + var newShapeHoles = []; + var mainIdx = 0; + var tmpPoints; + + newShapes[ mainIdx ] = undefined; + newShapeHoles[ mainIdx ] = []; + + for ( var i = 0, l = subPaths.length; i < l; i ++ ) { + + tmpPath = subPaths[ i ]; + tmpPoints = tmpPath.getPoints(); + solid = isClockWise( tmpPoints ); + solid = isCCW ? ! solid : solid; + + if ( solid ) { + + if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; + + newShapes[ mainIdx ] = { s: new THREE.Shape(), p: tmpPoints }; + newShapes[ mainIdx ].s.actions = tmpPath.actions; + newShapes[ mainIdx ].s.curves = tmpPath.curves; + + if ( holesFirst ) mainIdx ++; + newShapeHoles[ mainIdx ] = []; + + //console.log('cw', i); + + } else { + + newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); + + //console.log('ccw', i); + + } + + } + + // only Holes? -> probably all Shapes with wrong orientation + if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); + + + if ( newShapes.length > 1 ) { + + var ambiguous = false; + var toChange = []; + + for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + + betterShapeHoles[ sIdx ] = []; + + } + + for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + + var sho = newShapeHoles[ sIdx ]; + + for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) { + + var ho = sho[ hIdx ]; + var hole_unassigned = true; + + for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { + + if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { + + if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); + if ( hole_unassigned ) { + + hole_unassigned = false; + betterShapeHoles[ s2Idx ].push( ho ); + + } else { + + ambiguous = true; + + } + + } + + } + if ( hole_unassigned ) { + + betterShapeHoles[ sIdx ].push( ho ); + + } + + } + + } + // console.log("ambiguous: ", ambiguous); + if ( toChange.length > 0 ) { + + // console.log("to change: ", toChange); + if ( ! ambiguous ) newShapeHoles = betterShapeHoles; + + } + + } + + var tmpHoles; + + for ( var i = 0, il = newShapes.length; i < il; i ++ ) { + + tmpShape = newShapes[ i ].s; + shapes.push( tmpShape ); + tmpHoles = newShapeHoles[ i ]; + + for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) { + + tmpShape.holes.push( tmpHoles[ j ].h ); + + } + + } + + //console.log("shape", shapes); + + return shapes; + +}; + +// File:src/extras/core/Shape.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Defines a 2d shape plane using paths. + **/ + +// STEP 1 Create a path. +// STEP 2 Turn path into shape. +// STEP 3 ExtrudeGeometry takes in Shape/Shapes +// STEP 3a - Extract points from each shape, turn to vertices +// STEP 3b - Triangulate each shape, add faces. + +THREE.Shape = function () { + + THREE.Path.apply( this, arguments ); + + this.holes = []; + +}; + +THREE.Shape.prototype = Object.create( THREE.Path.prototype ); +THREE.Shape.prototype.constructor = THREE.Shape; + +// Convenience method to return ExtrudeGeometry + +THREE.Shape.prototype.extrude = function ( options ) { + + return new THREE.ExtrudeGeometry( this, options ); + +}; + +// Convenience method to return ShapeGeometry + +THREE.Shape.prototype.makeGeometry = function ( options ) { + + return new THREE.ShapeGeometry( this, options ); + +}; + +// Get points of holes + +THREE.Shape.prototype.getPointsHoles = function ( divisions ) { + + var holesPts = []; + + for ( var i = 0, l = this.holes.length; i < l; i ++ ) { + + holesPts[ i ] = this.holes[ i ].getPoints( divisions ); + + } + + return holesPts; + +}; + + +// Get points of shape and holes (keypoints based on segments parameter) + +THREE.Shape.prototype.extractAllPoints = function ( divisions ) { + + return { + + shape: this.getPoints( divisions ), + holes: this.getPointsHoles( divisions ) + + }; + +}; + +THREE.Shape.prototype.extractPoints = function ( divisions ) { + + return this.extractAllPoints( divisions ); + +}; + +THREE.Shape.Utils = THREE.ShapeUtils; // backwards compatibility + +// File:src/extras/curves/LineCurve.js + +/************************************************************** + * Line + **************************************************************/ + +THREE.LineCurve = function ( v1, v2 ) { + + this.v1 = v1; + this.v2 = v2; + +}; + +THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.LineCurve.prototype.constructor = THREE.LineCurve; + +THREE.LineCurve.prototype.getPoint = function ( t ) { + + var point = this.v2.clone().sub( this.v1 ); + point.multiplyScalar( t ).add( this.v1 ); + + return point; + +}; + +// Line curve is linear, so we can overwrite default getPointAt + +THREE.LineCurve.prototype.getPointAt = function ( u ) { + + return this.getPoint( u ); + +}; + +THREE.LineCurve.prototype.getTangent = function( t ) { + + var tangent = this.v2.clone().sub( this.v1 ); + + return tangent.normalize(); + +}; + +// File:src/extras/curves/QuadraticBezierCurve.js + +/************************************************************** + * Quadratic Bezier curve + **************************************************************/ + + +THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + +}; + +THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.QuadraticBezierCurve.prototype.constructor = THREE.QuadraticBezierCurve; + + +THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) { + + var b2 = THREE.ShapeUtils.b2; + + return new THREE.Vector2( + b2( t, this.v0.x, this.v1.x, this.v2.x ), + b2( t, this.v0.y, this.v1.y, this.v2.y ) + ); + +}; + + +THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) { + + var tangentQuadraticBezier = THREE.CurveUtils.tangentQuadraticBezier; + + return new THREE.Vector2( + tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x ), + tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y ) + ).normalize(); + +}; + +// File:src/extras/curves/CubicBezierCurve.js + +/************************************************************** + * Cubic Bezier curve + **************************************************************/ + +THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + +}; + +THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.CubicBezierCurve.prototype.constructor = THREE.CubicBezierCurve; + +THREE.CubicBezierCurve.prototype.getPoint = function ( t ) { + + var b3 = THREE.ShapeUtils.b3; + + return new THREE.Vector2( + b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), + b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ) + ); + +}; + +THREE.CubicBezierCurve.prototype.getTangent = function( t ) { + + var tangentCubicBezier = THREE.CurveUtils.tangentCubicBezier; + + return new THREE.Vector2( + tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), + tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ) + ).normalize(); + +}; + +// File:src/extras/curves/SplineCurve.js + +/************************************************************** + * Spline curve + **************************************************************/ + +THREE.SplineCurve = function ( points /* array of Vector2 */ ) { + + this.points = ( points == undefined ) ? [] : points; + +}; + +THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.SplineCurve.prototype.constructor = THREE.SplineCurve; + +THREE.SplineCurve.prototype.getPoint = function ( t ) { + + var points = this.points; + var point = ( points.length - 1 ) * t; + + var intPoint = Math.floor( point ); + var weight = point - intPoint; + + var point0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; + var point1 = points[ intPoint ]; + var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; + var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; + + var interpolate = THREE.CurveUtils.interpolate; + + return new THREE.Vector2( + interpolate( point0.x, point1.x, point2.x, point3.x, weight ), + interpolate( point0.y, point1.y, point2.y, point3.y, weight ) + ); + +}; + +// File:src/extras/curves/EllipseCurve.js + +/************************************************************** + * Ellipse curve + **************************************************************/ + +THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + + this.aX = aX; + this.aY = aY; + + this.xRadius = xRadius; + this.yRadius = yRadius; + + this.aStartAngle = aStartAngle; + this.aEndAngle = aEndAngle; + + this.aClockwise = aClockwise; + + this.aRotation = aRotation || 0; + +}; + +THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.EllipseCurve.prototype.constructor = THREE.EllipseCurve; + +THREE.EllipseCurve.prototype.getPoint = function ( t ) { + + var deltaAngle = this.aEndAngle - this.aStartAngle; + + if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2; + if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2; + + var angle; + + if ( this.aClockwise === true ) { + + angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle ); + + } else { + + angle = this.aStartAngle + t * deltaAngle; + + } + + var x = this.aX + this.xRadius * Math.cos( angle ); + var y = this.aY + this.yRadius * Math.sin( angle ); + + if ( this.aRotation !== 0 ) { + + var cos = Math.cos( this.aRotation ); + var sin = Math.sin( this.aRotation ); + + var tx = x, ty = y; + + // Rotate the point about the center of the ellipse. + x = ( tx - this.aX ) * cos - ( ty - this.aY ) * sin + this.aX; + y = ( tx - this.aX ) * sin + ( ty - this.aY ) * cos + this.aY; + + } + + return new THREE.Vector2( x, y ); + +}; + +// File:src/extras/curves/ArcCurve.js + +/************************************************************** + * Arc curve + **************************************************************/ + +THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + + THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + +}; + +THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype ); +THREE.ArcCurve.prototype.constructor = THREE.ArcCurve; + +// File:src/extras/curves/LineCurve3.js + +/************************************************************** + * Line3D + **************************************************************/ + +THREE.LineCurve3 = THREE.Curve.create( + + function ( v1, v2 ) { + + this.v1 = v1; + this.v2 = v2; + + }, + + function ( t ) { + + var vector = new THREE.Vector3(); + + vector.subVectors( this.v2, this.v1 ); // diff + vector.multiplyScalar( t ); + vector.add( this.v1 ); + + return vector; + + } + +); + +// File:src/extras/curves/QuadraticBezierCurve3.js + +/************************************************************** + * Quadratic Bezier 3D curve + **************************************************************/ + +THREE.QuadraticBezierCurve3 = THREE.Curve.create( + + function ( v0, v1, v2 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + + }, + + function ( t ) { + + var b2 = THREE.ShapeUtils.b2; + + return new THREE.Vector3( + b2( t, this.v0.x, this.v1.x, this.v2.x ), + b2( t, this.v0.y, this.v1.y, this.v2.y ), + b2( t, this.v0.z, this.v1.z, this.v2.z ) + ); + + } + +); + +// File:src/extras/curves/CubicBezierCurve3.js + +/************************************************************** + * Cubic Bezier 3D curve + **************************************************************/ + +THREE.CubicBezierCurve3 = THREE.Curve.create( + + function ( v0, v1, v2, v3 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + + }, + + function ( t ) { + + var b3 = THREE.ShapeUtils.b3; + + return new THREE.Vector3( + b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), + b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ), + b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z ) + ); + + } + +); + +// File:src/extras/curves/SplineCurve3.js + +/************************************************************** + * Spline 3D curve + **************************************************************/ + + +THREE.SplineCurve3 = THREE.Curve.create( + + function ( points /* array of Vector3 */ ) { + + console.warn( 'THREE.SplineCurve3 will be deprecated. Please use THREE.CatmullRomCurve3' ); + this.points = ( points == undefined ) ? [] : points; + + }, + + function ( t ) { + + var points = this.points; + var point = ( points.length - 1 ) * t; + + var intPoint = Math.floor( point ); + var weight = point - intPoint; + + var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ]; + var point1 = points[ intPoint ]; + var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; + var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; + + var interpolate = THREE.CurveUtils.interpolate; + + return new THREE.Vector3( + interpolate( point0.x, point1.x, point2.x, point3.x, weight ), + interpolate( point0.y, point1.y, point2.y, point3.y, weight ), + interpolate( point0.z, point1.z, point2.z, point3.z, weight ) + ); + + } + +); + +// File:src/extras/curves/CatmullRomCurve3.js + +/** + * @author zz85 https://github.com/zz85 + * + * Centripetal CatmullRom Curve - which is useful for avoiding + * cusps and self-intersections in non-uniform catmull rom curves. + * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf + * + * curve.type accepts centripetal(default), chordal and catmullrom + * curve.tension is used for catmullrom which defaults to 0.5 + */ + +THREE.CatmullRomCurve3 = ( function() { + + var + tmp = new THREE.Vector3(), + px = new CubicPoly(), + py = new CubicPoly(), + pz = new CubicPoly(); + + /* + Based on an optimized c++ solution in + - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ + - http://ideone.com/NoEbVM + + This CubicPoly class could be used for reusing some variables and calculations, + but for three.js curve use, it could be possible inlined and flatten into a single function call + which can be placed in CurveUtils. + */ + + function CubicPoly() { + + } + + /* + * Compute coefficients for a cubic polynomial + * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 + * such that + * p(0) = x0, p(1) = x1 + * and + * p'(0) = t0, p'(1) = t1. + */ + CubicPoly.prototype.init = function( x0, x1, t0, t1 ) { + + this.c0 = x0; + this.c1 = t0; + this.c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; + this.c3 = 2 * x0 - 2 * x1 + t0 + t1; + + }; + + CubicPoly.prototype.initNonuniformCatmullRom = function( x0, x1, x2, x3, dt0, dt1, dt2 ) { + + // compute tangents when parameterized in [t1,t2] + var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; + var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; + + // rescale tangents for parametrization in [0,1] + t1 *= dt1; + t2 *= dt1; + + // initCubicPoly + this.init( x1, x2, t1, t2 ); + + }; + + // standard Catmull-Rom spline: interpolate between x1 and x2 with previous/following points x1/x4 + CubicPoly.prototype.initCatmullRom = function( x0, x1, x2, x3, tension ) { + + this.init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); + + }; + + CubicPoly.prototype.calc = function( t ) { + + var t2 = t * t; + var t3 = t2 * t; + return this.c0 + this.c1 * t + this.c2 * t2 + this.c3 * t3; + + }; + + // Subclass Three.js curve + return THREE.Curve.create( + + function ( p /* array of Vector3 */ ) { + + this.points = p || []; + + }, + + function ( t ) { + + var points = this.points, + point, intPoint, weight, l; + + l = points.length; + + if ( l < 2 ) console.log( 'duh, you need at least 2 points' ); + + point = ( l - 1 ) * t; + intPoint = Math.floor( point ); + weight = point - intPoint; + + if ( weight === 0 && intPoint === l - 1 ) { + + intPoint = l - 2; + weight = 1; + + } + + var p0, p1, p2, p3; + + if ( intPoint === 0 ) { + + // extrapolate first point + tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); + p0 = tmp; + + } else { + + p0 = points[ intPoint - 1 ]; + + } + + p1 = points[ intPoint ]; + p2 = points[ intPoint + 1 ]; + + if ( intPoint + 2 < l ) { + + p3 = points[ intPoint + 2 ] + + } else { + + // extrapolate last point + tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 2 ] ); + p3 = tmp; + + } + + if ( this.type === undefined || this.type === 'centripetal' || this.type === 'chordal' ) { + + // init Centripetal / Chordal Catmull-Rom + var pow = this.type === 'chordal' ? 0.5 : 0.25; + var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); + var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); + var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); + + // safety check for repeated points + if ( dt1 < 1e-4 ) dt1 = 1.0; + if ( dt0 < 1e-4 ) dt0 = dt1; + if ( dt2 < 1e-4 ) dt2 = dt1; + + px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); + py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); + pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); + + } else if ( this.type === 'catmullrom' ) { + + var tension = this.tension !== undefined ? this.tension : 0.5; + px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, tension ); + py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, tension ); + pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, tension ); + + } + + var v = new THREE.Vector3( + px.calc( weight ), + py.calc( weight ), + pz.calc( weight ) + ); + + return v; + + } + + ); + +} )(); + +// File:src/extras/curves/ClosedSplineCurve3.js + +/************************************************************** + * Closed Spline 3D curve + **************************************************************/ + + +THREE.ClosedSplineCurve3 = THREE.Curve.create( + + function ( points /* array of Vector3 */ ) { + + this.points = ( points == undefined ) ? [] : points; + + }, + + function ( t ) { + + var points = this.points; + var point = ( points.length - 0 ) * t; // This needs to be from 0-length +1 + + var intPoint = Math.floor( point ); + var weight = point - intPoint; + + intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; + + var point0 = points[ ( intPoint - 1 ) % points.length ]; + var point1 = points[ ( intPoint ) % points.length ]; + var point2 = points[ ( intPoint + 1 ) % points.length ]; + var point3 = points[ ( intPoint + 2 ) % points.length ]; + + var interpolate = THREE.CurveUtils.interpolate; + + return new THREE.Vector3( + interpolate( point0.x, point1.x, point2.x, point3.x, weight ), + interpolate( point0.y, point1.y, point2.y, point3.y, weight ), + interpolate( point0.z, point1.z, point2.z, point3.z, weight ) + ); + + } + +); + +// File:src/extras/geometries/BoxGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as + */ + +THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) { + + THREE.Geometry.call( this ); + + this.type = 'BoxGeometry'; + + this.parameters = { + width: width, + height: height, + depth: depth, + widthSegments: widthSegments, + heightSegments: heightSegments, + depthSegments: depthSegments + }; + + this.widthSegments = widthSegments || 1; + this.heightSegments = heightSegments || 1; + this.depthSegments = depthSegments || 1; + + var scope = this; + + var width_half = width / 2; + var height_half = height / 2; + var depth_half = depth / 2; + + buildPlane( 'z', 'y', - 1, - 1, depth, height, width_half, 0 ); // px + buildPlane( 'z', 'y', 1, - 1, depth, height, - width_half, 1 ); // nx + buildPlane( 'x', 'z', 1, 1, width, depth, height_half, 2 ); // py + buildPlane( 'x', 'z', 1, - 1, width, depth, - height_half, 3 ); // ny + buildPlane( 'x', 'y', 1, - 1, width, height, depth_half, 4 ); // pz + buildPlane( 'x', 'y', - 1, - 1, width, height, - depth_half, 5 ); // nz + + function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) { + + var w, ix, iy, + gridX = scope.widthSegments, + gridY = scope.heightSegments, + width_half = width / 2, + height_half = height / 2, + offset = scope.vertices.length; + + if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) { + + w = 'z'; + + } else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) { + + w = 'y'; + gridY = scope.depthSegments; + + } else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) { + + w = 'x'; + gridX = scope.depthSegments; + + } + + var gridX1 = gridX + 1, + gridY1 = gridY + 1, + segment_width = width / gridX, + segment_height = height / gridY, + normal = new THREE.Vector3(); + + normal[ w ] = depth > 0 ? 1 : - 1; + + for ( iy = 0; iy < gridY1; iy ++ ) { + + for ( ix = 0; ix < gridX1; ix ++ ) { + + var vector = new THREE.Vector3(); + vector[ u ] = ( ix * segment_width - width_half ) * udir; + vector[ v ] = ( iy * segment_height - height_half ) * vdir; + vector[ w ] = depth; + + scope.vertices.push( vector ); + + } + + } + + for ( iy = 0; iy < gridY; iy ++ ) { + + for ( ix = 0; ix < gridX; ix ++ ) { + + var a = ix + gridX1 * iy; + var b = ix + gridX1 * ( iy + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); + var d = ( ix + 1 ) + gridX1 * iy; + + var uva = new THREE.Vector2( ix / gridX, 1 - iy / gridY ); + var uvb = new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY ); + var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iy + 1 ) / gridY ); + var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY ); + + var face = new THREE.Face3( a + offset, b + offset, d + offset ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + face.materialIndex = materialIndex; + + scope.faces.push( face ); + scope.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + face = new THREE.Face3( b + offset, c + offset, d + offset ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + face.materialIndex = materialIndex; + + scope.faces.push( face ); + scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + + } + + } + + this.mergeVertices(); + +}; + +THREE.BoxGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.BoxGeometry.prototype.constructor = THREE.BoxGeometry; + +THREE.BoxGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.BoxGeometry( + parameters.width, + parameters.height, + parameters.depth, + parameters.widthSegments, + parameters.heightSegments, + parameters.depthSegments + ); + +}; + +THREE.CubeGeometry = THREE.BoxGeometry; // backwards compatibility + +// File:src/extras/geometries/CircleGeometry.js + +/** + * @author hughes + */ + +THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + this.type = 'CircleGeometry'; + + this.parameters = { + radius: radius, + segments: segments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + this.fromBufferGeometry( new THREE.CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) ); + +}; + +THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.CircleGeometry.prototype.constructor = THREE.CircleGeometry; + +THREE.CircleGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.CircleGeometry( + parameters.radius, + parameters.segments, + parameters.thetaStart, + parameters.thetaLength + ); + +}; + +// File:src/extras/geometries/CircleBufferGeometry.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.CircleBufferGeometry = function ( radius, segments, thetaStart, thetaLength ) { + + THREE.BufferGeometry.call( this ); + + this.type = 'CircleBufferGeometry'; + + this.parameters = { + radius: radius, + segments: segments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + radius = radius || 50; + segments = segments !== undefined ? Math.max( 3, segments ) : 8; + + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + + var vertices = segments + 2; + + var positions = new Float32Array( vertices * 3 ); + var normals = new Float32Array( vertices * 3 ); + var uvs = new Float32Array( vertices * 2 ); + + // center data is already zero, but need to set a few extras + normals[ 2 ] = 1.0; + uvs[ 0 ] = 0.5; + uvs[ 1 ] = 0.5; + + for ( var s = 0, i = 3, ii = 2 ; s <= segments; s ++, i += 3, ii += 2 ) { + + var segment = thetaStart + s / segments * thetaLength; + + positions[ i ] = radius * Math.cos( segment ); + positions[ i + 1 ] = radius * Math.sin( segment ); + + normals[ i + 2 ] = 1; // normal z + + uvs[ ii ] = ( positions[ i ] / radius + 1 ) / 2; + uvs[ ii + 1 ] = ( positions[ i + 1 ] / radius + 1 ) / 2; + + } + + var indices = []; + + for ( var i = 1; i <= segments; i ++ ) { + + indices.push( i, i + 1, 0 ); + + } + + this.setIndex( new THREE.BufferAttribute( new Uint16Array( indices ), 1 ) ); + this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + +}; + +THREE.CircleBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.CircleBufferGeometry.prototype.constructor = THREE.CircleBufferGeometry; + +THREE.CircleBufferGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.CircleBufferGeometry( + parameters.radius, + parameters.segments, + parameters.thetaStart, + parameters.thetaLength + ); + +}; + +// File:src/extras/geometries/CylinderGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + this.type = 'CylinderGeometry'; + + this.parameters = { + radiusTop: radiusTop, + radiusBottom: radiusBottom, + height: height, + radialSegments: radialSegments, + heightSegments: heightSegments, + openEnded: openEnded, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + radiusTop = radiusTop !== undefined ? radiusTop : 20; + radiusBottom = radiusBottom !== undefined ? radiusBottom : 20; + height = height !== undefined ? height : 100; + + radialSegments = radialSegments || 8; + heightSegments = heightSegments || 1; + + openEnded = openEnded !== undefined ? openEnded : false; + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : 2 * Math.PI; + + var heightHalf = height / 2; + + var x, y, vertices = [], uvs = []; + + for ( y = 0; y <= heightSegments; y ++ ) { + + var verticesRow = []; + var uvsRow = []; + + var v = y / heightSegments; + var radius = v * ( radiusBottom - radiusTop ) + radiusTop; + + for ( x = 0; x <= radialSegments; x ++ ) { + + var u = x / radialSegments; + + var vertex = new THREE.Vector3(); + vertex.x = radius * Math.sin( u * thetaLength + thetaStart ); + vertex.y = - v * height + heightHalf; + vertex.z = radius * Math.cos( u * thetaLength + thetaStart ); + + this.vertices.push( vertex ); + + verticesRow.push( this.vertices.length - 1 ); + uvsRow.push( new THREE.Vector2( u, 1 - v ) ); + + } + + vertices.push( verticesRow ); + uvs.push( uvsRow ); + + } + + var tanTheta = ( radiusBottom - radiusTop ) / height; + var na, nb; + + for ( x = 0; x < radialSegments; x ++ ) { + + if ( radiusTop !== 0 ) { + + na = this.vertices[ vertices[ 0 ][ x ] ].clone(); + nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone(); + + } else { + + na = this.vertices[ vertices[ 1 ][ x ] ].clone(); + nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone(); + + } + + na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize(); + nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize(); + + for ( y = 0; y < heightSegments; y ++ ) { + + var v1 = vertices[ y ][ x ]; + var v2 = vertices[ y + 1 ][ x ]; + var v3 = vertices[ y + 1 ][ x + 1 ]; + var v4 = vertices[ y ][ x + 1 ]; + + var n1 = na.clone(); + var n2 = na.clone(); + var n3 = nb.clone(); + var n4 = nb.clone(); + + var uv1 = uvs[ y ][ x ].clone(); + var uv2 = uvs[ y + 1 ][ x ].clone(); + var uv3 = uvs[ y + 1 ][ x + 1 ].clone(); + var uv4 = uvs[ y ][ x + 1 ].clone(); + + this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] ); + + this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] ); + + } + + } + + // top cap + + if ( openEnded === false && radiusTop > 0 ) { + + this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) ); + + for ( x = 0; x < radialSegments; x ++ ) { + + var v1 = vertices[ 0 ][ x ]; + var v2 = vertices[ 0 ][ x + 1 ]; + var v3 = this.vertices.length - 1; + + var n1 = new THREE.Vector3( 0, 1, 0 ); + var n2 = new THREE.Vector3( 0, 1, 0 ); + var n3 = new THREE.Vector3( 0, 1, 0 ); + + var uv1 = uvs[ 0 ][ x ].clone(); + var uv2 = uvs[ 0 ][ x + 1 ].clone(); + var uv3 = new THREE.Vector2( uv2.x, 0 ); + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ], undefined, 1 ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + + } + + } + + // bottom cap + + if ( openEnded === false && radiusBottom > 0 ) { + + this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) ); + + for ( x = 0; x < radialSegments; x ++ ) { + + var v1 = vertices[ heightSegments ][ x + 1 ]; + var v2 = vertices[ heightSegments ][ x ]; + var v3 = this.vertices.length - 1; + + var n1 = new THREE.Vector3( 0, - 1, 0 ); + var n2 = new THREE.Vector3( 0, - 1, 0 ); + var n3 = new THREE.Vector3( 0, - 1, 0 ); + + var uv1 = uvs[ heightSegments ][ x + 1 ].clone(); + var uv2 = uvs[ heightSegments ][ x ].clone(); + var uv3 = new THREE.Vector2( uv2.x, 1 ); + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ], undefined, 2 ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + + } + + } + + this.computeFaceNormals(); + +}; + +THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.CylinderGeometry.prototype.constructor = THREE.CylinderGeometry; + +THREE.CylinderGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.CylinderGeometry( + parameters.radiusTop, + parameters.radiusBottom, + parameters.height, + parameters.radialSegments, + parameters.heightSegments, + parameters.openEnded, + parameters.thetaStart, + parameters.thetaLength + ); + +}; + +// File:src/extras/geometries/EdgesGeometry.js + +/** + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.EdgesGeometry = function ( geometry, thresholdAngle ) { + + THREE.BufferGeometry.call( this ); + + thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; + + var thresholdDot = Math.cos( THREE.Math.degToRad( thresholdAngle ) ); + + var edge = [ 0, 0 ], hash = {}; + + function sortFunction( a, b ) { + + return a - b; + + } + + var keys = [ 'a', 'b', 'c' ]; + + var geometry2; + + if ( geometry instanceof THREE.BufferGeometry ) { + + geometry2 = new THREE.Geometry(); + geometry2.fromBufferGeometry( geometry ); + + } else { + + geometry2 = geometry.clone(); + + } + + geometry2.mergeVertices(); + geometry2.computeFaceNormals(); + + var vertices = geometry2.vertices; + var faces = geometry2.faces; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0; j < 3; j ++ ) { + + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); + + var key = edge.toString(); + + if ( hash[ key ] === undefined ) { + + hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined }; + + } else { + + hash[ key ].face2 = i; + + } + + } + + } + + var coords = []; + + for ( var key in hash ) { + + var h = hash[ key ]; + + if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) <= thresholdDot ) { + + var vertex = vertices[ h.vert1 ]; + coords.push( vertex.x ); + coords.push( vertex.y ); + coords.push( vertex.z ); + + vertex = vertices[ h.vert2 ]; + coords.push( vertex.x ); + coords.push( vertex.y ); + coords.push( vertex.z ); + + } + + } + + this.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( coords ), 3 ) ); + +}; + +THREE.EdgesGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.EdgesGeometry.prototype.constructor = THREE.EdgesGeometry; + +// File:src/extras/geometries/ExtrudeGeometry.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + * Creates extruded geometry from a path shape. + * + * parameters = { + * + * curveSegments: , // number of points on the curves + * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too + * amount: , // Depth to extrude the shape + * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into the original shape bevel goes + * bevelSize: , // how far from shape outline is bevel + * bevelSegments: , // number of bevel layers + * + * extrudePath: // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined) + * frames: // containing arrays of tangents, normals, binormals + * + * uvGenerator: // object that provides UV generator functions + * + * } + **/ + +THREE.ExtrudeGeometry = function ( shapes, options ) { + + if ( typeof( shapes ) === "undefined" ) { + + shapes = []; + return; + + } + + THREE.Geometry.call( this ); + + this.type = 'ExtrudeGeometry'; + + shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; + + this.addShapeList( shapes, options ); + + this.computeFaceNormals(); + + // can't really use automatic vertex normals + // as then front and back sides get smoothed too + // should do separate smoothing just for sides + + //this.computeVertexNormals(); + + //console.log( "took", ( Date.now() - startTime ) ); + +}; + +THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ExtrudeGeometry.prototype.constructor = THREE.ExtrudeGeometry; + +THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) { + + var sl = shapes.length; + + for ( var s = 0; s < sl; s ++ ) { + + var shape = shapes[ s ]; + this.addShape( shape, options ); + + } + +}; + +THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { + + var amount = options.amount !== undefined ? options.amount : 100; + + var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10 + var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8 + var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; + + var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false + + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + + var steps = options.steps !== undefined ? options.steps : 1; + + var extrudePath = options.extrudePath; + var extrudePts, extrudeByPath = false; + + // Use default WorldUVGenerator if no UV generators are specified. + var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator; + + var splineTube, binormal, normal, position2; + if ( extrudePath ) { + + extrudePts = extrudePath.getSpacedPoints( steps ); + + extrudeByPath = true; + bevelEnabled = false; // bevels not supported for path extrusion + + // SETUP TNB variables + + // Reuse TNB from TubeGeomtry for now. + // TODO1 - have a .isClosed in spline? + + splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames( extrudePath, steps, false ); + + // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); + + binormal = new THREE.Vector3(); + normal = new THREE.Vector3(); + position2 = new THREE.Vector3(); + + } + + // Safeguards if bevels are not enabled + + if ( ! bevelEnabled ) { + + bevelSegments = 0; + bevelThickness = 0; + bevelSize = 0; + + } + + // Variables initialization + + var ahole, h, hl; // looping of holes + var scope = this; + + var shapesOffset = this.vertices.length; + + var shapePoints = shape.extractPoints( curveSegments ); + + var vertices = shapePoints.shape; + var holes = shapePoints.holes; + + var reverse = ! THREE.ShapeUtils.isClockWise( vertices ); + + if ( reverse ) { + + vertices = vertices.reverse(); + + // Maybe we should also check if holes are in the opposite direction, just to be safe ... + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + if ( THREE.ShapeUtils.isClockWise( ahole ) ) { + + holes[ h ] = ahole.reverse(); + + } + + } + + reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! + + } + + + var faces = THREE.ShapeUtils.triangulateShape( vertices, holes ); + + /* Vertices */ + + var contour = vertices; // vertices has all points but contour has only points of circumference + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + vertices = vertices.concat( ahole ); + + } + + + function scalePt2 ( pt, vec, size ) { + + if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); + + return vec.clone().multiplyScalar( size ).add( pt ); + + } + + var b, bs, t, z, + vert, vlen = vertices.length, + face, flen = faces.length; + + + // Find directions for point movement + + + function getBevelVec( inPt, inPrev, inNext ) { + + // computes for inPt the corresponding point inPt' on a new contour + // shifted by 1 unit (length of normalized vector) to the left + // if we walk along contour clockwise, this new contour is outside the old one + // + // inPt' is the intersection of the two lines parallel to the two + // adjacent edges of inPt at a distance of 1 unit on the left side. + + var v_trans_x, v_trans_y, shrink_by = 1; // resulting translation vector for inPt + + // good reading for geometry algorithms (here: line-line intersection) + // http://geomalgorithms.com/a05-_intersect-1.html + + var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; + var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; + + var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); + + // check for collinear edges + var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + + if ( Math.abs( collinear0 ) > Number.EPSILON ) { + + // not collinear + + // length of vectors for normalizing + + var v_prev_len = Math.sqrt( v_prev_lensq ); + var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); + + // shift adjacent points by unit vectors to the left + + var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); + var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); + + var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); + var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); + + // scaling factor for v_prev to intersection point + + var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - + ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / + ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + + // vector from inPt to intersection point + + v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); + v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); + + // Don't normalize!, otherwise sharp corners become ugly + // but prevent crazy spikes + var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); + if ( v_trans_lensq <= 2 ) { + + return new THREE.Vector2( v_trans_x, v_trans_y ); + + } else { + + shrink_by = Math.sqrt( v_trans_lensq / 2 ); + + } + + } else { + + // handle special case of collinear edges + + var direction_eq = false; // assumes: opposite + if ( v_prev_x > Number.EPSILON ) { + + if ( v_next_x > Number.EPSILON ) { + + direction_eq = true; + + } + + } else { + + if ( v_prev_x < - Number.EPSILON ) { + + if ( v_next_x < - Number.EPSILON ) { + + direction_eq = true; + + } + + } else { + + if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { + + direction_eq = true; + + } + + } + + } + + if ( direction_eq ) { + + // console.log("Warning: lines are a straight sequence"); + v_trans_x = - v_prev_y; + v_trans_y = v_prev_x; + shrink_by = Math.sqrt( v_prev_lensq ); + + } else { + + // console.log("Warning: lines are a straight spike"); + v_trans_x = v_prev_x; + v_trans_y = v_prev_y; + shrink_by = Math.sqrt( v_prev_lensq / 2 ); + + } + + } + + return new THREE.Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); + + } + + + var contourMovements = []; + + for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + + if ( j === il ) j = 0; + if ( k === il ) k = 0; + + // (j)---(i)---(k) + // console.log('i,j,k', i, j , k) + + contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); + + } + + var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + oneHoleMovements = []; + + for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + + if ( j === il ) j = 0; + if ( k === il ) k = 0; + + // (j)---(i)---(k) + oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); + + } + + holesMovements.push( oneHoleMovements ); + verticesMovements = verticesMovements.concat( oneHoleMovements ); + + } + + + // Loop bevelSegments, 1 for the front, 1 for the back + + for ( b = 0; b < bevelSegments; b ++ ) { + + //for ( b = bevelSegments; b > 0; b -- ) { + + t = b / bevelSegments; + z = bevelThickness * ( 1 - t ); + + //z = bevelThickness * t; + bs = bevelSize * ( Math.sin ( t * Math.PI / 2 ) ); // curved + //bs = bevelSize * t; // linear + + // contract shape + + for ( i = 0, il = contour.length; i < il; i ++ ) { + + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + + v( vert.x, vert.y, - z ); + + } + + // expand holes + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; + + for ( i = 0, il = ahole.length; i < il; i ++ ) { + + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + + v( vert.x, vert.y, - z ); + + } + + } + + } + + bs = bevelSize; + + // Back facing vertices + + for ( i = 0; i < vlen; i ++ ) { + + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + + if ( ! extrudeByPath ) { + + v( vert.x, vert.y, 0 ); + + } else { + + // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); + + normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); + + position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); + + v( position2.x, position2.y, position2.z ); + + } + + } + + // Add stepped vertices... + // Including front facing vertices + + var s; + + for ( s = 1; s <= steps; s ++ ) { + + for ( i = 0; i < vlen; i ++ ) { + + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + + if ( ! extrudeByPath ) { + + v( vert.x, vert.y, amount / steps * s ); + + } else { + + // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); + + normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); + + position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); + + v( position2.x, position2.y, position2.z ); + + } + + } + + } + + + // Add bevel segments planes + + //for ( b = 1; b <= bevelSegments; b ++ ) { + for ( b = bevelSegments - 1; b >= 0; b -- ) { + + t = b / bevelSegments; + z = bevelThickness * ( 1 - t ); + //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) ); + bs = bevelSize * Math.sin ( t * Math.PI / 2 ); + + // contract shape + + for ( i = 0, il = contour.length; i < il; i ++ ) { + + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + v( vert.x, vert.y, amount + z ); + + } + + // expand holes + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; + + for ( i = 0, il = ahole.length; i < il; i ++ ) { + + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + + if ( ! extrudeByPath ) { + + v( vert.x, vert.y, amount + z ); + + } else { + + v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); + + } + + } + + } + + } + + /* Faces */ + + // Top and bottom faces + + buildLidFaces(); + + // Sides faces + + buildSideFaces(); + + + ///// Internal functions + + function buildLidFaces() { + + if ( bevelEnabled ) { + + var layer = 0; // steps + 1 + var offset = vlen * layer; + + // Bottom faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); + + } + + layer = steps + bevelSegments * 2; + offset = vlen * layer; + + // Top faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); + + } + + } else { + + // Bottom faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 2 ], face[ 1 ], face[ 0 ] ); + + } + + // Top faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); + + } + + } + + } + + // Create faces for the z-sides of the shape + + function buildSideFaces() { + + var layeroffset = 0; + sidewalls( contour, layeroffset ); + layeroffset += contour.length; + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + sidewalls( ahole, layeroffset ); + + //, true + layeroffset += ahole.length; + + } + + } + + function sidewalls( contour, layeroffset ) { + + var j, k; + i = contour.length; + + while ( -- i >= 0 ) { + + j = i; + k = i - 1; + if ( k < 0 ) k = contour.length - 1; + + //console.log('b', i,j, i-1, k,vertices.length); + + var s = 0, sl = steps + bevelSegments * 2; + + for ( s = 0; s < sl; s ++ ) { + + var slen1 = vlen * s; + var slen2 = vlen * ( s + 1 ); + + var a = layeroffset + j + slen1, + b = layeroffset + k + slen1, + c = layeroffset + k + slen2, + d = layeroffset + j + slen2; + + f4( a, b, c, d, contour, s, sl, j, k ); + + } + + } + + } + + + function v( x, y, z ) { + + scope.vertices.push( new THREE.Vector3( x, y, z ) ); + + } + + function f3( a, b, c ) { + + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; + + scope.faces.push( new THREE.Face3( a, b, c, null, null, 0 ) ); + + var uvs = uvgen.generateTopUV( scope, a, b, c ); + + scope.faceVertexUvs[ 0 ].push( uvs ); + + } + + function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) { + + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; + d += shapesOffset; + + scope.faces.push( new THREE.Face3( a, b, d, null, null, 1 ) ); + scope.faces.push( new THREE.Face3( b, c, d, null, null, 1 ) ); + + var uvs = uvgen.generateSideWallUV( scope, a, b, c, d ); + + scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] ); + scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] ); + + } + +}; + +THREE.ExtrudeGeometry.WorldUVGenerator = { + + generateTopUV: function ( geometry, indexA, indexB, indexC ) { + + var vertices = geometry.vertices; + + var a = vertices[ indexA ]; + var b = vertices[ indexB ]; + var c = vertices[ indexC ]; + + return [ + new THREE.Vector2( a.x, a.y ), + new THREE.Vector2( b.x, b.y ), + new THREE.Vector2( c.x, c.y ) + ]; + + }, + + generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) { + + var vertices = geometry.vertices; + + var a = vertices[ indexA ]; + var b = vertices[ indexB ]; + var c = vertices[ indexC ]; + var d = vertices[ indexD ]; + + if ( Math.abs( a.y - b.y ) < 0.01 ) { + + return [ + new THREE.Vector2( a.x, 1 - a.z ), + new THREE.Vector2( b.x, 1 - b.z ), + new THREE.Vector2( c.x, 1 - c.z ), + new THREE.Vector2( d.x, 1 - d.z ) + ]; + + } else { + + return [ + new THREE.Vector2( a.y, 1 - a.z ), + new THREE.Vector2( b.y, 1 - b.z ), + new THREE.Vector2( c.y, 1 - c.z ), + new THREE.Vector2( d.y, 1 - d.z ) + ]; + + } + + } +}; + +// File:src/extras/geometries/ShapeGeometry.js + +/** + * @author jonobr1 / http://jonobr1.com + * + * Creates a one-sided polygonal geometry from a path shape. Similar to + * ExtrudeGeometry. + * + * parameters = { + * + * curveSegments: , // number of points on the curves. NOT USED AT THE MOMENT. + * + * material: // material index for front and back faces + * uvGenerator: // object that provides UV generator functions + * + * } + **/ + +THREE.ShapeGeometry = function ( shapes, options ) { + + THREE.Geometry.call( this ); + + this.type = 'ShapeGeometry'; + + if ( Array.isArray( shapes ) === false ) shapes = [ shapes ]; + + this.addShapeList( shapes, options ); + + this.computeFaceNormals(); + +}; + +THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ShapeGeometry.prototype.constructor = THREE.ShapeGeometry; + +/** + * Add an array of shapes to THREE.ShapeGeometry. + */ +THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) { + + for ( var i = 0, l = shapes.length; i < l; i ++ ) { + + this.addShape( shapes[ i ], options ); + + } + + return this; + +}; + +/** + * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry. + */ +THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { + + if ( options === undefined ) options = {}; + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + + var material = options.material; + var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator; + + // + + var i, l, hole; + + var shapesOffset = this.vertices.length; + var shapePoints = shape.extractPoints( curveSegments ); + + var vertices = shapePoints.shape; + var holes = shapePoints.holes; + + var reverse = ! THREE.ShapeUtils.isClockWise( vertices ); + + if ( reverse ) { + + vertices = vertices.reverse(); + + // Maybe we should also check if holes are in the opposite direction, just to be safe... + + for ( i = 0, l = holes.length; i < l; i ++ ) { + + hole = holes[ i ]; + + if ( THREE.ShapeUtils.isClockWise( hole ) ) { + + holes[ i ] = hole.reverse(); + + } + + } + + reverse = false; + + } + + var faces = THREE.ShapeUtils.triangulateShape( vertices, holes ); + + // Vertices + + for ( i = 0, l = holes.length; i < l; i ++ ) { + + hole = holes[ i ]; + vertices = vertices.concat( hole ); + + } + + // + + var vert, vlen = vertices.length; + var face, flen = faces.length; + + for ( i = 0; i < vlen; i ++ ) { + + vert = vertices[ i ]; + + this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) ); + + } + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + + var a = face[ 0 ] + shapesOffset; + var b = face[ 1 ] + shapesOffset; + var c = face[ 2 ] + shapesOffset; + + this.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); + this.faceVertexUvs[ 0 ].push( uvgen.generateTopUV( this, a, b, c ) ); + + } + +}; + +// File:src/extras/geometries/LatheGeometry.js + +/** + * @author astrodud / http://astrodud.isgreat.org/ + * @author zz85 / https://github.com/zz85 + * @author bhouston / http://clara.io + */ + +// points - to create a closed torus, one must use a set of points +// like so: [ a, b, c, d, a ], see first is the same as last. +// segments - the number of circumference segments to create +// phiStart - the starting radian +// phiLength - the radian (0 to 2*PI) range of the lathed section +// 2*pi is a closed lathe, less than 2PI is a portion. + +THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) { + + THREE.Geometry.call( this ); + + this.type = 'LatheGeometry'; + + this.parameters = { + points: points, + segments: segments, + phiStart: phiStart, + phiLength: phiLength + }; + + segments = segments || 12; + phiStart = phiStart || 0; + phiLength = phiLength || 2 * Math.PI; + + var inversePointLength = 1.0 / ( points.length - 1 ); + var inverseSegments = 1.0 / segments; + + for ( var i = 0, il = segments; i <= il; i ++ ) { + + var phi = phiStart + i * inverseSegments * phiLength; + + var c = Math.cos( phi ), + s = Math.sin( phi ); + + for ( var j = 0, jl = points.length; j < jl; j ++ ) { + + var pt = points[ j ]; + + var vertex = new THREE.Vector3(); + + vertex.x = c * pt.x - s * pt.y; + vertex.y = s * pt.x + c * pt.y; + vertex.z = pt.z; + + this.vertices.push( vertex ); + + } + + } + + var np = points.length; + + for ( var i = 0, il = segments; i < il; i ++ ) { + + for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) { + + var base = j + np * i; + var a = base; + var b = base + np; + var c = base + 1 + np; + var d = base + 1; + + var u0 = i * inverseSegments; + var v0 = j * inversePointLength; + var u1 = u0 + inverseSegments; + var v1 = v0 + inversePointLength; + + this.faces.push( new THREE.Face3( a, b, d ) ); + + this.faceVertexUvs[ 0 ].push( [ + + new THREE.Vector2( u0, v0 ), + new THREE.Vector2( u1, v0 ), + new THREE.Vector2( u0, v1 ) + + ] ); + + this.faces.push( new THREE.Face3( b, c, d ) ); + + this.faceVertexUvs[ 0 ].push( [ + + new THREE.Vector2( u1, v0 ), + new THREE.Vector2( u1, v1 ), + new THREE.Vector2( u0, v1 ) + + ] ); + + + } + + } + + this.mergeVertices(); + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.LatheGeometry.prototype.constructor = THREE.LatheGeometry; + +// File:src/extras/geometries/PlaneGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as + */ + +THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) { + + THREE.Geometry.call( this ); + + this.type = 'PlaneGeometry'; + + this.parameters = { + width: width, + height: height, + widthSegments: widthSegments, + heightSegments: heightSegments + }; + + this.fromBufferGeometry( new THREE.PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); + +}; + +THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.PlaneGeometry.prototype.constructor = THREE.PlaneGeometry; + +THREE.PlaneGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.PlaneGeometry( + parameters.width, + parameters.height, + parameters.widthSegments, + parameters.heightSegments + ); + +}; + +// File:src/extras/geometries/PlaneBufferGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as + */ + +THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegments ) { + + THREE.BufferGeometry.call( this ); + + this.type = 'PlaneBufferGeometry'; + + this.parameters = { + width: width, + height: height, + widthSegments: widthSegments, + heightSegments: heightSegments + }; + + var width_half = width / 2; + var height_half = height / 2; + + var gridX = Math.floor( widthSegments ) || 1; + var gridY = Math.floor( heightSegments ) || 1; + + var gridX1 = gridX + 1; + var gridY1 = gridY + 1; + + var segment_width = width / gridX; + var segment_height = height / gridY; + + var vertices = new Float32Array( gridX1 * gridY1 * 3 ); + var normals = new Float32Array( gridX1 * gridY1 * 3 ); + var uvs = new Float32Array( gridX1 * gridY1 * 2 ); + + var offset = 0; + var offset2 = 0; + + for ( var iy = 0; iy < gridY1; iy ++ ) { + + var y = iy * segment_height - height_half; + + for ( var ix = 0; ix < gridX1; ix ++ ) { + + var x = ix * segment_width - width_half; + + vertices[ offset ] = x; + vertices[ offset + 1 ] = - y; + + normals[ offset + 2 ] = 1; + + uvs[ offset2 ] = ix / gridX; + uvs[ offset2 + 1 ] = 1 - ( iy / gridY ); + + offset += 3; + offset2 += 2; + + } + + } + + offset = 0; + + var indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 ); + + for ( var iy = 0; iy < gridY; iy ++ ) { + + for ( var ix = 0; ix < gridX; ix ++ ) { + + var a = ix + gridX1 * iy; + var b = ix + gridX1 * ( iy + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); + var d = ( ix + 1 ) + gridX1 * iy; + + indices[ offset ] = a; + indices[ offset + 1 ] = b; + indices[ offset + 2 ] = d; + + indices[ offset + 3 ] = b; + indices[ offset + 4 ] = c; + indices[ offset + 5 ] = d; + + offset += 6; + + } + + } + + this.setIndex( new THREE.BufferAttribute( indices, 1 ) ); + this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + +}; + +THREE.PlaneBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.PlaneBufferGeometry.prototype.constructor = THREE.PlaneBufferGeometry; + +THREE.PlaneBufferGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.PlaneBufferGeometry( + parameters.width, + parameters.height, + parameters.widthSegments, + parameters.heightSegments + ); + +}; + +// File:src/extras/geometries/RingGeometry.js + +/** + * @author Kaleb Murphy + */ + +THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + this.type = 'RingGeometry'; + + this.parameters = { + innerRadius: innerRadius, + outerRadius: outerRadius, + thetaSegments: thetaSegments, + phiSegments: phiSegments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + innerRadius = innerRadius || 0; + outerRadius = outerRadius || 50; + + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + + thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; + phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 8; + + var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); + + for ( i = 0; i < phiSegments + 1; i ++ ) { + + // concentric circles inside ring + + for ( o = 0; o < thetaSegments + 1; o ++ ) { + + // number of segments per circle + + var vertex = new THREE.Vector3(); + var segment = thetaStart + o / thetaSegments * thetaLength; + vertex.x = radius * Math.cos( segment ); + vertex.y = radius * Math.sin( segment ); + + this.vertices.push( vertex ); + uvs.push( new THREE.Vector2( ( vertex.x / outerRadius + 1 ) / 2, ( vertex.y / outerRadius + 1 ) / 2 ) ); + + } + + radius += radiusStep; + + } + + var n = new THREE.Vector3( 0, 0, 1 ); + + for ( i = 0; i < phiSegments; i ++ ) { + + // concentric circles inside ring + + var thetaSegment = i * ( thetaSegments + 1 ); + + for ( o = 0; o < thetaSegments ; o ++ ) { + + // number of segments per circle + + var segment = o + thetaSegment; + + var v1 = segment; + var v2 = segment + thetaSegments + 1; + var v3 = segment + thetaSegments + 2; + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ] ); + + v1 = segment; + v2 = segment + thetaSegments + 2; + v3 = segment + 1; + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ] ); + + } + + } + + this.computeFaceNormals(); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + +}; + +THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.RingGeometry.prototype.constructor = THREE.RingGeometry; + +THREE.RingGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.RingGeometry( + parameters.innerRadius, + parameters.outerRadius, + parameters.thetaSegments, + parameters.phiSegments, + parameters.thetaStart, + parameters.thetaLength + ); + +}; + +// File:src/extras/geometries/SphereGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + this.type = 'SphereGeometry'; + + this.parameters = { + radius: radius, + widthSegments: widthSegments, + heightSegments: heightSegments, + phiStart: phiStart, + phiLength: phiLength, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + this.fromBufferGeometry( new THREE.SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) ); + +}; + +THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.SphereGeometry.prototype.constructor = THREE.SphereGeometry; + +THREE.SphereGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.SphereGeometry( + parameters.radius, + parameters.widthSegments, + parameters.heightSegments, + parameters.phiStart, + parameters.phiLength, + parameters.thetaStart, + parameters.thetaLength + ); + +}; + +// File:src/extras/geometries/SphereBufferGeometry.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + * based on THREE.SphereGeometry + */ + +THREE.SphereBufferGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { + + THREE.BufferGeometry.call( this ); + + this.type = 'SphereBufferGeometry'; + + this.parameters = { + radius: radius, + widthSegments: widthSegments, + heightSegments: heightSegments, + phiStart: phiStart, + phiLength: phiLength, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + radius = radius || 50; + + widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); + heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); + + phiStart = phiStart !== undefined ? phiStart : 0; + phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; + + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; + + var thetaEnd = thetaStart + thetaLength; + + var vertexCount = ( ( widthSegments + 1 ) * ( heightSegments + 1 ) ); + + var positions = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var normals = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var uvs = new THREE.BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); + + var index = 0, vertices = [], normal = new THREE.Vector3(); + + for ( var y = 0; y <= heightSegments; y ++ ) { + + var verticesRow = []; + + var v = y / heightSegments; + + for ( var x = 0; x <= widthSegments; x ++ ) { + + var u = x / widthSegments; + + var px = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + var py = radius * Math.cos( thetaStart + v * thetaLength ); + var pz = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + + normal.set( px, py, pz ).normalize(); + + positions.setXYZ( index, px, py, pz ); + normals.setXYZ( index, normal.x, normal.y, normal.z ); + uvs.setXY( index, u, 1 - v ); + + verticesRow.push( index ); + + index ++; + + } + + vertices.push( verticesRow ); + + } + + var indices = []; + + for ( var y = 0; y < heightSegments; y ++ ) { + + for ( var x = 0; x < widthSegments; x ++ ) { + + var v1 = vertices[ y ][ x + 1 ]; + var v2 = vertices[ y ][ x ]; + var v3 = vertices[ y + 1 ][ x ]; + var v4 = vertices[ y + 1 ][ x + 1 ]; + + if ( y !== 0 || thetaStart > 0 ) indices.push( v1, v2, v4 ); + if ( y !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( v2, v3, v4 ); + + } + + } + + this.setIndex( new ( positions.count > 65535 ? THREE.Uint32Attribute : THREE.Uint16Attribute )( indices, 1 ) ); + this.addAttribute( 'position', positions ); + this.addAttribute( 'normal', normals ); + this.addAttribute( 'uv', uvs ); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + +}; + +THREE.SphereBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.SphereBufferGeometry.prototype.constructor = THREE.SphereBufferGeometry; + +THREE.SphereBufferGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.SphereBufferGeometry( + parameters.radius, + parameters.widthSegments, + parameters.heightSegments, + parameters.phiStart, + parameters.phiLength, + parameters.thetaStart, + parameters.thetaLength + ); + +}; + +// File:src/extras/geometries/TorusGeometry.js + +/** + * @author oosmoxiecode + * @author mrdoob / http://mrdoob.com/ + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888 + */ + +THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) { + + THREE.Geometry.call( this ); + + this.type = 'TorusGeometry'; + + this.parameters = { + radius: radius, + tube: tube, + radialSegments: radialSegments, + tubularSegments: tubularSegments, + arc: arc + }; + + radius = radius || 100; + tube = tube || 40; + radialSegments = radialSegments || 8; + tubularSegments = tubularSegments || 6; + arc = arc || Math.PI * 2; + + var center = new THREE.Vector3(), uvs = [], normals = []; + + for ( var j = 0; j <= radialSegments; j ++ ) { + + for ( var i = 0; i <= tubularSegments; i ++ ) { + + var u = i / tubularSegments * arc; + var v = j / radialSegments * Math.PI * 2; + + center.x = radius * Math.cos( u ); + center.y = radius * Math.sin( u ); + + var vertex = new THREE.Vector3(); + vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); + vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); + vertex.z = tube * Math.sin( v ); + + this.vertices.push( vertex ); + + uvs.push( new THREE.Vector2( i / tubularSegments, j / radialSegments ) ); + normals.push( vertex.clone().sub( center ).normalize() ); + + } + + } + + for ( var j = 1; j <= radialSegments; j ++ ) { + + for ( var i = 1; i <= tubularSegments; i ++ ) { + + var a = ( tubularSegments + 1 ) * j + i - 1; + var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; + var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; + var d = ( tubularSegments + 1 ) * j + i; + + var face = new THREE.Face3( a, b, d, [ normals[ a ].clone(), normals[ b ].clone(), normals[ d ].clone() ] ); + this.faces.push( face ); + this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ d ].clone() ] ); + + face = new THREE.Face3( b, c, d, [ normals[ b ].clone(), normals[ c ].clone(), normals[ d ].clone() ] ); + this.faces.push( face ); + this.faceVertexUvs[ 0 ].push( [ uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] ); + + } + + } + + this.computeFaceNormals(); + +}; + +THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.TorusGeometry.prototype.constructor = THREE.TorusGeometry; + +THREE.TorusGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.TorusGeometry( + parameters.radius, + parameters.tube, + parameters.radialSegments, + parameters.tubularSegments, + parameters.arc + ); + +}; + +// File:src/extras/geometries/TorusKnotGeometry.js + +/** + * @author oosmoxiecode + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473 + */ + +THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) { + + THREE.Geometry.call( this ); + + this.type = 'TorusKnotGeometry'; + + this.parameters = { + radius: radius, + tube: tube, + radialSegments: radialSegments, + tubularSegments: tubularSegments, + p: p, + q: q, + heightScale: heightScale + }; + + radius = radius || 100; + tube = tube || 40; + radialSegments = radialSegments || 64; + tubularSegments = tubularSegments || 8; + p = p || 2; + q = q || 3; + heightScale = heightScale || 1; + + var grid = new Array( radialSegments ); + var tang = new THREE.Vector3(); + var n = new THREE.Vector3(); + var bitan = new THREE.Vector3(); + + for ( var i = 0; i < radialSegments; ++ i ) { + + grid[ i ] = new Array( tubularSegments ); + var u = i / radialSegments * 2 * p * Math.PI; + var p1 = getPos( u, q, p, radius, heightScale ); + var p2 = getPos( u + 0.01, q, p, radius, heightScale ); + tang.subVectors( p2, p1 ); + n.addVectors( p2, p1 ); + + bitan.crossVectors( tang, n ); + n.crossVectors( bitan, tang ); + bitan.normalize(); + n.normalize(); + + for ( var j = 0; j < tubularSegments; ++ j ) { + + var v = j / tubularSegments * 2 * Math.PI; + var cx = - tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + var cy = tube * Math.sin( v ); + + var pos = new THREE.Vector3(); + pos.x = p1.x + cx * n.x + cy * bitan.x; + pos.y = p1.y + cx * n.y + cy * bitan.y; + pos.z = p1.z + cx * n.z + cy * bitan.z; + + grid[ i ][ j ] = this.vertices.push( pos ) - 1; + + } + + } + + for ( var i = 0; i < radialSegments; ++ i ) { + + for ( var j = 0; j < tubularSegments; ++ j ) { + + var ip = ( i + 1 ) % radialSegments; + var jp = ( j + 1 ) % tubularSegments; + + var a = grid[ i ][ j ]; + var b = grid[ ip ][ j ]; + var c = grid[ ip ][ jp ]; + var d = grid[ i ][ jp ]; + + var uva = new THREE.Vector2( i / radialSegments, j / tubularSegments ); + var uvb = new THREE.Vector2( ( i + 1 ) / radialSegments, j / tubularSegments ); + var uvc = new THREE.Vector2( ( i + 1 ) / radialSegments, ( j + 1 ) / tubularSegments ); + var uvd = new THREE.Vector2( i / radialSegments, ( j + 1 ) / tubularSegments ); + + this.faces.push( new THREE.Face3( a, b, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + this.faces.push( new THREE.Face3( b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + + } + + this.computeFaceNormals(); + this.computeVertexNormals(); + + function getPos( u, in_q, in_p, radius, heightScale ) { + + var cu = Math.cos( u ); + var su = Math.sin( u ); + var quOverP = in_q / in_p * u; + var cs = Math.cos( quOverP ); + + var tx = radius * ( 2 + cs ) * 0.5 * cu; + var ty = radius * ( 2 + cs ) * su * 0.5; + var tz = heightScale * radius * Math.sin( quOverP ) * 0.5; + + return new THREE.Vector3( tx, ty, tz ); + + } + +}; + +THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.TorusKnotGeometry.prototype.constructor = THREE.TorusKnotGeometry; + +THREE.TorusKnotGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.TorusKnotGeometry( + parameters.radius, + parameters.tube, + parameters.radialSegments, + parameters.tubularSegments, + parameters.p, + parameters.q, + parameters.heightScale + ); + +}; + +// File:src/extras/geometries/TubeGeometry.js + +/** + * @author WestLangley / https://github.com/WestLangley + * @author zz85 / https://github.com/zz85 + * @author miningold / https://github.com/miningold + * @author jonobr1 / https://github.com/jonobr1 + * + * Modified from the TorusKnotGeometry by @oosmoxiecode + * + * Creates a tube which extrudes along a 3d spline + * + * Uses parallel transport frames as described in + * http://www.cs.indiana.edu/pub/techreports/TR425.pdf + */ + +THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed, taper ) { + + THREE.Geometry.call( this ); + + this.type = 'TubeGeometry'; + + this.parameters = { + path: path, + segments: segments, + radius: radius, + radialSegments: radialSegments, + closed: closed, + taper: taper + }; + + segments = segments || 64; + radius = radius || 1; + radialSegments = radialSegments || 8; + closed = closed || false; + taper = taper || THREE.TubeGeometry.NoTaper; + + var grid = []; + + var scope = this, + + tangent, + normal, + binormal, + + numpoints = segments + 1, + + u, v, r, + + cx, cy, + pos, pos2 = new THREE.Vector3(), + i, j, + ip, jp, + a, b, c, d, + uva, uvb, uvc, uvd; + + var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ), + tangents = frames.tangents, + normals = frames.normals, + binormals = frames.binormals; + + // proxy internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; + + function vert( x, y, z ) { + + return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1; + + } + + // construct the grid + + for ( i = 0; i < numpoints; i ++ ) { + + grid[ i ] = []; + + u = i / ( numpoints - 1 ); + + pos = path.getPointAt( u ); + + tangent = tangents[ i ]; + normal = normals[ i ]; + binormal = binormals[ i ]; + + r = radius * taper( u ); + + for ( j = 0; j < radialSegments; j ++ ) { + + v = j / radialSegments * 2 * Math.PI; + + cx = - r * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + cy = r * Math.sin( v ); + + pos2.copy( pos ); + pos2.x += cx * normal.x + cy * binormal.x; + pos2.y += cx * normal.y + cy * binormal.y; + pos2.z += cx * normal.z + cy * binormal.z; + + grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z ); + + } + + } + + + // construct the mesh + + for ( i = 0; i < segments; i ++ ) { + + for ( j = 0; j < radialSegments; j ++ ) { + + ip = ( closed ) ? ( i + 1 ) % segments : i + 1; + jp = ( j + 1 ) % radialSegments; + + a = grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! *** + b = grid[ ip ][ j ]; + c = grid[ ip ][ jp ]; + d = grid[ i ][ jp ]; + + uva = new THREE.Vector2( i / segments, j / radialSegments ); + uvb = new THREE.Vector2( ( i + 1 ) / segments, j / radialSegments ); + uvc = new THREE.Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments ); + uvd = new THREE.Vector2( i / segments, ( j + 1 ) / radialSegments ); + + this.faces.push( new THREE.Face3( a, b, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + this.faces.push( new THREE.Face3( b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + + } + + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.TubeGeometry.prototype.constructor = THREE.TubeGeometry; +THREE.TubeGeometry.prototype.clone = function() { + + return new this.constructor( this.parameters.path, + this.parameters.segments, this.parameters.radius, this.parameters.radialSegments, + this.parameters.closed, this.parameters.taper + ); + +}; + +THREE.TubeGeometry.NoTaper = function ( u ) { + + return 1; + +}; + +THREE.TubeGeometry.SinusoidalTaper = function ( u ) { + + return Math.sin( Math.PI * u ); + +}; + +// For computing of Frenet frames, exposing the tangents, normals and binormals the spline +THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) { + + var normal = new THREE.Vector3(), + + tangents = [], + normals = [], + binormals = [], + + vec = new THREE.Vector3(), + mat = new THREE.Matrix4(), + + numpoints = segments + 1, + theta, + smallest, + + tx, ty, tz, + i, u; + + + // expose internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; + + // compute the tangent vectors for each segment on the path + + for ( i = 0; i < numpoints; i ++ ) { + + u = i / ( numpoints - 1 ); + + tangents[ i ] = path.getTangentAt( u ); + tangents[ i ].normalize(); + + } + + initialNormal3(); + + /* + function initialNormal1(lastBinormal) { + // fixed start binormal. Has dangers of 0 vectors + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 ); + normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize(); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + } + + function initialNormal2() { + + // This uses the Frenet-Serret formula for deriving binormal + var t2 = path.getTangentAt( epsilon ); + + normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize(); + binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] ); + + normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + + } + */ + + function initialNormal3() { + + // select an initial normal vector perpendicular to the first tangent vector, + // and in the direction of the smallest tangent xyz component + + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + smallest = Number.MAX_VALUE; + tx = Math.abs( tangents[ 0 ].x ); + ty = Math.abs( tangents[ 0 ].y ); + tz = Math.abs( tangents[ 0 ].z ); + + if ( tx <= smallest ) { + + smallest = tx; + normal.set( 1, 0, 0 ); + + } + + if ( ty <= smallest ) { + + smallest = ty; + normal.set( 0, 1, 0 ); + + } + + if ( tz <= smallest ) { + + normal.set( 0, 0, 1 ); + + } + + vec.crossVectors( tangents[ 0 ], normal ).normalize(); + + normals[ 0 ].crossVectors( tangents[ 0 ], vec ); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); + + } + + + // compute the slowly-varying normal and binormal vectors for each segment on the path + + for ( i = 1; i < numpoints; i ++ ) { + + normals[ i ] = normals[ i - 1 ].clone(); + + binormals[ i ] = binormals[ i - 1 ].clone(); + + vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); + + if ( vec.length() > Number.EPSILON ) { + + vec.normalize(); + + theta = Math.acos( THREE.Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors + + normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); + + } + + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + + } + + + // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same + + if ( closed ) { + + theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints - 1 ] ), - 1, 1 ) ); + theta /= ( numpoints - 1 ); + + if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints - 1 ] ) ) > 0 ) { + + theta = - theta; + + } + + for ( i = 1; i < numpoints; i ++ ) { + + // twist a little... + normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + + } + + } + +}; + +// File:src/extras/geometries/PolyhedronGeometry.js + +/** + * @author clockworkgeek / https://github.com/clockworkgeek + * @author timothypratley / https://github.com/timothypratley + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) { + + THREE.Geometry.call( this ); + + this.type = 'PolyhedronGeometry'; + + this.parameters = { + vertices: vertices, + indices: indices, + radius: radius, + detail: detail + }; + + radius = radius || 1; + detail = detail || 0; + + var that = this; + + for ( var i = 0, l = vertices.length; i < l; i += 3 ) { + + prepare( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) ); + + } + + var p = this.vertices; + + var faces = []; + + for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) { + + var v1 = p[ indices[ i ] ]; + var v2 = p[ indices[ i + 1 ] ]; + var v3 = p[ indices[ i + 2 ] ]; + + faces[ j ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ], undefined, j ); + + } + + var centroid = new THREE.Vector3(); + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + subdivide( faces[ i ], detail ); + + } + + + // Handle case when face straddles the seam + + for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) { + + var uvs = this.faceVertexUvs[ 0 ][ i ]; + + var x0 = uvs[ 0 ].x; + var x1 = uvs[ 1 ].x; + var x2 = uvs[ 2 ].x; + + var max = Math.max( x0, x1, x2 ); + var min = Math.min( x0, x1, x2 ); + + if ( max > 0.9 && min < 0.1 ) { + + // 0.9 is somewhat arbitrary + + if ( x0 < 0.2 ) uvs[ 0 ].x += 1; + if ( x1 < 0.2 ) uvs[ 1 ].x += 1; + if ( x2 < 0.2 ) uvs[ 2 ].x += 1; + + } + + } + + + // Apply radius + + for ( var i = 0, l = this.vertices.length; i < l; i ++ ) { + + this.vertices[ i ].multiplyScalar( radius ); + + } + + + // Merge vertices + + this.mergeVertices(); + + this.computeFaceNormals(); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + + + // Project vector onto sphere's surface + + function prepare( vector ) { + + var vertex = vector.normalize().clone(); + vertex.index = that.vertices.push( vertex ) - 1; + + // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle. + + var u = azimuth( vector ) / 2 / Math.PI + 0.5; + var v = inclination( vector ) / Math.PI + 0.5; + vertex.uv = new THREE.Vector2( u, 1 - v ); + + return vertex; + + } + + + // Approximate a curved face with recursively sub-divided triangles. + + function make( v1, v2, v3, materialIndex ) { + + var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ], undefined, materialIndex ); + that.faces.push( face ); + + centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 ); + + var azi = azimuth( centroid ); + + that.faceVertexUvs[ 0 ].push( [ + correctUV( v1.uv, v1, azi ), + correctUV( v2.uv, v2, azi ), + correctUV( v3.uv, v3, azi ) + ] ); + + } + + + // Analytically subdivide a face to the required detail level. + + function subdivide( face, detail ) { + + var cols = Math.pow( 2, detail ); + var a = prepare( that.vertices[ face.a ] ); + var b = prepare( that.vertices[ face.b ] ); + var c = prepare( that.vertices[ face.c ] ); + var v = []; + + var materialIndex = face.materialIndex; + + // Construct all of the vertices for this subdivision. + + for ( var i = 0 ; i <= cols; i ++ ) { + + v[ i ] = []; + + var aj = prepare( a.clone().lerp( c, i / cols ) ); + var bj = prepare( b.clone().lerp( c, i / cols ) ); + var rows = cols - i; + + for ( var j = 0; j <= rows; j ++ ) { + + if ( j === 0 && i === cols ) { + + v[ i ][ j ] = aj; + + } else { + + v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) ); + + } + + } + + } + + // Construct all of the faces. + + for ( var i = 0; i < cols ; i ++ ) { + + for ( var j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { + + var k = Math.floor( j / 2 ); + + if ( j % 2 === 0 ) { + + make( + v[ i ][ k + 1 ], + v[ i + 1 ][ k ], + v[ i ][ k ], + materialIndex + ); + + } else { + + make( + v[ i ][ k + 1 ], + v[ i + 1 ][ k + 1 ], + v[ i + 1 ][ k ], + materialIndex + ); + + } + + } + + } + + } + + + // Angle around the Y axis, counter-clockwise when looking from above. + + function azimuth( vector ) { + + return Math.atan2( vector.z, - vector.x ); + + } + + + // Angle above the XZ plane. + + function inclination( vector ) { + + return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); + + } + + + // Texture fixing helper. Spheres have some odd behaviours. + + function correctUV( uv, vector, azimuth ) { + + if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new THREE.Vector2( uv.x - 1, uv.y ); + if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new THREE.Vector2( azimuth / 2 / Math.PI + 0.5, uv.y ); + return uv.clone(); + + } + + +}; + +THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.PolyhedronGeometry.prototype.constructor = THREE.PolyhedronGeometry; + +THREE.PolyhedronGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.PolyhedronGeometry( + parameters.vertices, + parameters.indices, + parameters.radius, + parameters.detail + ); + +}; + +// File:src/extras/geometries/DodecahedronGeometry.js + +/** + * @author Abe Pazos / https://hamoid.com + */ + +THREE.DodecahedronGeometry = function ( radius, detail ) { + + var t = ( 1 + Math.sqrt( 5 ) ) / 2; + var r = 1 / t; + + var vertices = [ + + // (±1, ±1, ±1) + - 1, - 1, - 1, - 1, - 1, 1, + - 1, 1, - 1, - 1, 1, 1, + 1, - 1, - 1, 1, - 1, 1, + 1, 1, - 1, 1, 1, 1, + + // (0, ±1/φ, ±φ) + 0, - r, - t, 0, - r, t, + 0, r, - t, 0, r, t, + + // (±1/φ, ±φ, 0) + - r, - t, 0, - r, t, 0, + r, - t, 0, r, t, 0, + + // (±φ, 0, ±1/φ) + - t, 0, - r, t, 0, - r, + - t, 0, r, t, 0, r + ]; + + var indices = [ + 3, 11, 7, 3, 7, 15, 3, 15, 13, + 7, 19, 17, 7, 17, 6, 7, 6, 15, + 17, 4, 8, 17, 8, 10, 17, 10, 6, + 8, 0, 16, 8, 16, 2, 8, 2, 10, + 0, 12, 1, 0, 1, 18, 0, 18, 16, + 6, 10, 2, 6, 2, 13, 6, 13, 15, + 2, 16, 18, 2, 18, 3, 2, 3, 13, + 18, 1, 9, 18, 9, 11, 18, 11, 3, + 4, 14, 12, 4, 12, 0, 4, 0, 8, + 11, 9, 5, 11, 5, 19, 11, 19, 7, + 19, 5, 14, 19, 14, 4, 19, 4, 17, + 1, 12, 14, 1, 14, 5, 1, 5, 9 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + + this.type = 'DodecahedronGeometry'; + + this.parameters = { + radius: radius, + detail: detail + }; + +}; + +THREE.DodecahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype ); +THREE.DodecahedronGeometry.prototype.constructor = THREE.DodecahedronGeometry; + +THREE.DodecahedronGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.DodecahedronGeometry( + parameters.radius, + parameters.detail + ); + +}; + +// File:src/extras/geometries/IcosahedronGeometry.js + +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.IcosahedronGeometry = function ( radius, detail ) { + + var t = ( 1 + Math.sqrt( 5 ) ) / 2; + + var vertices = [ + - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, + 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, + t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 + ]; + + var indices = [ + 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, + 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, + 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, + 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + + this.type = 'IcosahedronGeometry'; + + this.parameters = { + radius: radius, + detail: detail + }; + +}; + +THREE.IcosahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype ); +THREE.IcosahedronGeometry.prototype.constructor = THREE.IcosahedronGeometry; + +THREE.IcosahedronGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.IcosahedronGeometry( + parameters.radius, + parameters.detail + ); + +}; + +// File:src/extras/geometries/OctahedronGeometry.js + +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.OctahedronGeometry = function ( radius, detail ) { + + var vertices = [ + 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1 + ]; + + var indices = [ + 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + + this.type = 'OctahedronGeometry'; + + this.parameters = { + radius: radius, + detail: detail + }; + +}; + +THREE.OctahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype ); +THREE.OctahedronGeometry.prototype.constructor = THREE.OctahedronGeometry; + +THREE.OctahedronGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.OctahedronGeometry( + parameters.radius, + parameters.detail + ); + +}; + +// File:src/extras/geometries/TetrahedronGeometry.js + +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.TetrahedronGeometry = function ( radius, detail ) { + + var vertices = [ + 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 + ]; + + var indices = [ + 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + + this.type = 'TetrahedronGeometry'; + + this.parameters = { + radius: radius, + detail: detail + }; + +}; + +THREE.TetrahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype ); +THREE.TetrahedronGeometry.prototype.constructor = THREE.TetrahedronGeometry; + +THREE.TetrahedronGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.TetrahedronGeometry( + parameters.radius, + parameters.detail + ); + +}; + +// File:src/extras/geometries/ParametricGeometry.js + +/** + * @author zz85 / https://github.com/zz85 + * Parametric Surfaces Geometry + * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 + * + * new THREE.ParametricGeometry( parametricFunction, uSegments, ySegements ); + * + */ + +THREE.ParametricGeometry = function ( func, slices, stacks ) { + + THREE.Geometry.call( this ); + + this.type = 'ParametricGeometry'; + + this.parameters = { + func: func, + slices: slices, + stacks: stacks + }; + + var verts = this.vertices; + var faces = this.faces; + var uvs = this.faceVertexUvs[ 0 ]; + + var i, j, p; + var u, v; + + var sliceCount = slices + 1; + + for ( i = 0; i <= stacks; i ++ ) { + + v = i / stacks; + + for ( j = 0; j <= slices; j ++ ) { + + u = j / slices; + + p = func( u, v ); + verts.push( p ); + + } + + } + + var a, b, c, d; + var uva, uvb, uvc, uvd; + + for ( i = 0; i < stacks; i ++ ) { + + for ( j = 0; j < slices; j ++ ) { + + a = i * sliceCount + j; + b = i * sliceCount + j + 1; + c = ( i + 1 ) * sliceCount + j + 1; + d = ( i + 1 ) * sliceCount + j; + + uva = new THREE.Vector2( j / slices, i / stacks ); + uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks ); + uvc = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks ); + uvd = new THREE.Vector2( j / slices, ( i + 1 ) / stacks ); + + faces.push( new THREE.Face3( a, b, d ) ); + uvs.push( [ uva, uvb, uvd ] ); + + faces.push( new THREE.Face3( b, c, d ) ); + uvs.push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + + } + + // console.log(this); + + // magic bullet + // var diff = this.mergeVertices(); + // console.log('removed ', diff, ' vertices by merging'); + + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ParametricGeometry.prototype.constructor = THREE.ParametricGeometry; + +// File:src/extras/geometries/WireframeGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.WireframeGeometry = function ( geometry ) { + + THREE.BufferGeometry.call( this ); + + var edge = [ 0, 0 ], hash = {}; + + function sortFunction( a, b ) { + + return a - b; + + } + + var keys = [ 'a', 'b', 'c' ]; + + if ( geometry instanceof THREE.Geometry ) { + + var vertices = geometry.vertices; + var faces = geometry.faces; + var numEdges = 0; + + // allocate maximal size + var edges = new Uint32Array( 6 * faces.length ); + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0; j < 3; j ++ ) { + + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); + + var key = edge.toString(); + + if ( hash[ key ] === undefined ) { + + edges[ 2 * numEdges ] = edge[ 0 ]; + edges[ 2 * numEdges + 1 ] = edge[ 1 ]; + hash[ key ] = true; + numEdges ++; + + } + + } + + } + + var coords = new Float32Array( numEdges * 2 * 3 ); + + for ( var i = 0, l = numEdges; i < l; i ++ ) { + + for ( var j = 0; j < 2; j ++ ) { + + var vertex = vertices[ edges [ 2 * i + j ] ]; + + var index = 6 * i + 3 * j; + coords[ index + 0 ] = vertex.x; + coords[ index + 1 ] = vertex.y; + coords[ index + 2 ] = vertex.z; + + } + + } + + this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + + } else if ( geometry instanceof THREE.BufferGeometry ) { + + if ( geometry.index !== null ) { + + // Indexed BufferGeometry + + var indices = geometry.index.array; + var vertices = geometry.attributes.position; + var drawcalls = geometry.drawcalls; + var numEdges = 0; + + if ( drawcalls.length === 0 ) { + + geometry.addGroup( 0, indices.length ); + + } + + // allocate maximal size + var edges = new Uint32Array( 2 * indices.length ); + + for ( var o = 0, ol = drawcalls.length; o < ol; ++ o ) { + + var drawcall = drawcalls[ o ]; + + var start = drawcall.start; + var count = drawcall.count; + + for ( var i = start, il = start + count; i < il; i += 3 ) { + + for ( var j = 0; j < 3; j ++ ) { + + edge[ 0 ] = indices[ i + j ]; + edge[ 1 ] = indices[ i + ( j + 1 ) % 3 ]; + edge.sort( sortFunction ); + + var key = edge.toString(); + + if ( hash[ key ] === undefined ) { + + edges[ 2 * numEdges ] = edge[ 0 ]; + edges[ 2 * numEdges + 1 ] = edge[ 1 ]; + hash[ key ] = true; + numEdges ++; + + } + + } + + } + + } + + var coords = new Float32Array( numEdges * 2 * 3 ); + + for ( var i = 0, l = numEdges; i < l; i ++ ) { + + for ( var j = 0; j < 2; j ++ ) { + + var index = 6 * i + 3 * j; + var index2 = edges[ 2 * i + j ]; + + coords[ index + 0 ] = vertices.getX( index2 ); + coords[ index + 1 ] = vertices.getY( index2 ); + coords[ index + 2 ] = vertices.getZ( index2 ); + + } + + } + + this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + + } else { + + // non-indexed BufferGeometry + + var vertices = geometry.attributes.position.array; + var numEdges = vertices.length / 3; + var numTris = numEdges / 3; + + var coords = new Float32Array( numEdges * 2 * 3 ); + + for ( var i = 0, l = numTris; i < l; i ++ ) { + + for ( var j = 0; j < 3; j ++ ) { + + var index = 18 * i + 6 * j; + + var index1 = 9 * i + 3 * j; + coords[ index + 0 ] = vertices[ index1 ]; + coords[ index + 1 ] = vertices[ index1 + 1 ]; + coords[ index + 2 ] = vertices[ index1 + 2 ]; + + var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 ); + coords[ index + 3 ] = vertices[ index2 ]; + coords[ index + 4 ] = vertices[ index2 + 1 ]; + coords[ index + 5 ] = vertices[ index2 + 2 ]; + + } + + } + + this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + + } + + } + +}; + +THREE.WireframeGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.WireframeGeometry.prototype.constructor = THREE.WireframeGeometry; + +// File:src/extras/helpers/AxisHelper.js + +/** + * @author sroucheray / http://sroucheray.org/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AxisHelper = function ( size ) { + + size = size || 1; + + var vertices = new Float32Array( [ + 0, 0, 0, size, 0, 0, + 0, 0, 0, 0, size, 0, + 0, 0, 0, 0, 0, size + ] ); + + var colors = new Float32Array( [ + 1, 0, 0, 1, 0.6, 0, + 0, 1, 0, 0.6, 1, 0, + 0, 0, 1, 0, 0.6, 1 + ] ); + + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); + + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); + + THREE.LineSegments.call( this, geometry, material ); + +}; + +THREE.AxisHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.AxisHelper.prototype.constructor = THREE.AxisHelper; + +// File:src/extras/helpers/ArrowHelper.js + +/** + * @author WestLangley / http://github.com/WestLangley + * @author zz85 / http://github.com/zz85 + * @author bhouston / http://clara.io + * + * Creates an arrow for visualizing directions + * + * Parameters: + * dir - Vector3 + * origin - Vector3 + * length - Number + * color - color in hex value + * headLength - Number + * headWidth - Number + */ + +THREE.ArrowHelper = ( function () { + + var lineGeometry = new THREE.Geometry(); + lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) ); + + var coneGeometry = new THREE.CylinderGeometry( 0, 0.5, 1, 5, 1 ); + coneGeometry.translate( 0, - 0.5, 0 ); + + return function ArrowHelper( dir, origin, length, color, headLength, headWidth ) { + + // dir is assumed to be normalized + + THREE.Object3D.call( this ); + + if ( color === undefined ) color = 0xffff00; + if ( length === undefined ) length = 1; + if ( headLength === undefined ) headLength = 0.2 * length; + if ( headWidth === undefined ) headWidth = 0.2 * headLength; + + this.position.copy( origin ); + + if ( headLength < length ) { + this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) ); + this.line.matrixAutoUpdate = false; + this.add( this.line ); + } + + this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) ); + this.cone.matrixAutoUpdate = false; + this.add( this.cone ); + + this.setDirection( dir ); + this.setLength( length, headLength, headWidth ); + + } + +}() ); + +THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype ); +THREE.ArrowHelper.prototype.constructor = THREE.ArrowHelper; + +THREE.ArrowHelper.prototype.setDirection = ( function () { + + var axis = new THREE.Vector3(); + var radians; + + return function setDirection( dir ) { + + // dir is assumed to be normalized + + if ( dir.y > 0.99999 ) { + + this.quaternion.set( 0, 0, 0, 1 ); + + } else if ( dir.y < - 0.99999 ) { + + this.quaternion.set( 1, 0, 0, 0 ); + + } else { + + axis.set( dir.z, 0, - dir.x ).normalize(); + + radians = Math.acos( dir.y ); + + this.quaternion.setFromAxisAngle( axis, radians ); + + } + + }; + +}() ); + +THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) { + + if ( headLength === undefined ) headLength = 0.2 * length; + if ( headWidth === undefined ) headWidth = 0.2 * headLength; + + if ( headLength < length ){ + this.line.scale.set( 1, length - headLength, 1 ); + this.line.updateMatrix(); + } + + this.cone.scale.set( headWidth, headLength, headWidth ); + this.cone.position.y = length; + this.cone.updateMatrix(); + +}; + +THREE.ArrowHelper.prototype.setColor = function ( color ) { + + if ( this.line !== undefined ) this.line.material.color.set( color ); + this.cone.material.color.set( color ); + +}; + +// File:src/extras/helpers/BoxHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BoxHelper = function ( object ) { + + var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); + var positions = new Float32Array( 8 * 3 ); + + var geometry = new THREE.BufferGeometry(); + geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) ); + geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + + THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ) ); + + if ( object !== undefined ) { + + this.update( object ); + + } + +}; + +THREE.BoxHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.BoxHelper.prototype.constructor = THREE.BoxHelper; + +THREE.BoxHelper.prototype.update = ( function () { + + var box = new THREE.Box3(); + + return function ( object ) { + + box.setFromObject( object ); + + if ( box.empty() ) return; + + var min = box.min; + var max = box.max; + + /* + 5____4 + 1/___0/| + | 6__|_7 + 2/___3/ + + 0: max.x, max.y, max.z + 1: min.x, max.y, max.z + 2: min.x, min.y, max.z + 3: max.x, min.y, max.z + 4: max.x, max.y, min.z + 5: min.x, max.y, min.z + 6: min.x, min.y, min.z + 7: max.x, min.y, min.z + */ + + var position = this.geometry.attributes.position; + var array = position.array; + + array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z; + array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z; + array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z; + array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z; + array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z; + array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z; + array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z; + array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z; + + position.needsUpdate = true; + + this.geometry.computeBoundingSphere(); + + } + +} )(); + +// File:src/extras/helpers/BoundingBoxHelper.js + +/** + * @author WestLangley / http://github.com/WestLangley + */ + +// a helper to show the world-axis-aligned bounding box for an object + +THREE.BoundingBoxHelper = function ( object, hex ) { + + var color = ( hex !== undefined ) ? hex : 0x888888; + + this.object = object; + + this.box = new THREE.Box3(); + + THREE.Mesh.call( this, new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) ); + +}; + +THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype ); +THREE.BoundingBoxHelper.prototype.constructor = THREE.BoundingBoxHelper; + +THREE.BoundingBoxHelper.prototype.update = function () { + + this.box.setFromObject( this.object ); + + this.box.size( this.scale ); + + this.box.center( this.position ); + +}; + +// File:src/extras/helpers/CameraHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * + * - shows frustum, line of sight and up of the camera + * - suitable for fast updates + * - based on frustum visualization in lightgl.js shadowmap example + * http://evanw.github.com/lightgl.js/tests/shadowmap.html + */ + +THREE.CameraHelper = function ( camera ) { + + var geometry = new THREE.Geometry(); + var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } ); + + var pointMap = {}; + + // colors + + var hexFrustum = 0xffaa00; + var hexCone = 0xff0000; + var hexUp = 0x00aaff; + var hexTarget = 0xffffff; + var hexCross = 0x333333; + + // near + + addLine( "n1", "n2", hexFrustum ); + addLine( "n2", "n4", hexFrustum ); + addLine( "n4", "n3", hexFrustum ); + addLine( "n3", "n1", hexFrustum ); + + // far + + addLine( "f1", "f2", hexFrustum ); + addLine( "f2", "f4", hexFrustum ); + addLine( "f4", "f3", hexFrustum ); + addLine( "f3", "f1", hexFrustum ); + + // sides + + addLine( "n1", "f1", hexFrustum ); + addLine( "n2", "f2", hexFrustum ); + addLine( "n3", "f3", hexFrustum ); + addLine( "n4", "f4", hexFrustum ); + + // cone + + addLine( "p", "n1", hexCone ); + addLine( "p", "n2", hexCone ); + addLine( "p", "n3", hexCone ); + addLine( "p", "n4", hexCone ); + + // up + + addLine( "u1", "u2", hexUp ); + addLine( "u2", "u3", hexUp ); + addLine( "u3", "u1", hexUp ); + + // target + + addLine( "c", "t", hexTarget ); + addLine( "p", "c", hexCross ); + + // cross + + addLine( "cn1", "cn2", hexCross ); + addLine( "cn3", "cn4", hexCross ); + + addLine( "cf1", "cf2", hexCross ); + addLine( "cf3", "cf4", hexCross ); + + function addLine( a, b, hex ) { + + addPoint( a, hex ); + addPoint( b, hex ); + + } + + function addPoint( id, hex ) { + + geometry.vertices.push( new THREE.Vector3() ); + geometry.colors.push( new THREE.Color( hex ) ); + + if ( pointMap[ id ] === undefined ) { + + pointMap[ id ] = []; + + } + + pointMap[ id ].push( geometry.vertices.length - 1 ); + + } + + THREE.LineSegments.call( this, geometry, material ); + + this.camera = camera; + this.camera.updateProjectionMatrix(); + + this.matrix = camera.matrixWorld; + this.matrixAutoUpdate = false; + + this.pointMap = pointMap; + + this.update(); + +}; + +THREE.CameraHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.CameraHelper.prototype.constructor = THREE.CameraHelper; + +THREE.CameraHelper.prototype.update = function () { + + var geometry, pointMap; + + var vector = new THREE.Vector3(); + var camera = new THREE.Camera(); + + function setPoint( point, x, y, z ) { + + vector.set( x, y, z ).unproject( camera ); + + var points = pointMap[ point ]; + + if ( points !== undefined ) { + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + geometry.vertices[ points[ i ] ].copy( vector ); + + } + + } + + } + + return function () { + + geometry = this.geometry; + pointMap = this.pointMap; + + var w = 1, h = 1; + + // we need just camera projection matrix + // world matrix must be identity + + camera.projectionMatrix.copy( this.camera.projectionMatrix ); + + // center / target + + setPoint( "c", 0, 0, - 1 ); + setPoint( "t", 0, 0, 1 ); + + // near + + setPoint( "n1", - w, - h, - 1 ); + setPoint( "n2", w, - h, - 1 ); + setPoint( "n3", - w, h, - 1 ); + setPoint( "n4", w, h, - 1 ); + + // far + + setPoint( "f1", - w, - h, 1 ); + setPoint( "f2", w, - h, 1 ); + setPoint( "f3", - w, h, 1 ); + setPoint( "f4", w, h, 1 ); + + // up + + setPoint( "u1", w * 0.7, h * 1.1, - 1 ); + setPoint( "u2", - w * 0.7, h * 1.1, - 1 ); + setPoint( "u3", 0, h * 2, - 1 ); + + // cross + + setPoint( "cf1", - w, 0, 1 ); + setPoint( "cf2", w, 0, 1 ); + setPoint( "cf3", 0, - h, 1 ); + setPoint( "cf4", 0, h, 1 ); + + setPoint( "cn1", - w, 0, - 1 ); + setPoint( "cn2", w, 0, - 1 ); + setPoint( "cn3", 0, - h, - 1 ); + setPoint( "cn4", 0, h, - 1 ); + + geometry.verticesNeedUpdate = true; + + }; + +}(); + +// File:src/extras/helpers/DirectionalLightHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.DirectionalLightHelper = function ( light, size ) { + + THREE.Object3D.call( this ); + + this.light = light; + this.light.updateMatrixWorld(); + + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; + + size = size || 1; + + var geometry = new THREE.Geometry(); + geometry.vertices.push( + new THREE.Vector3( - size, size, 0 ), + new THREE.Vector3( size, size, 0 ), + new THREE.Vector3( size, - size, 0 ), + new THREE.Vector3( - size, - size, 0 ), + new THREE.Vector3( - size, size, 0 ) + ); + + var material = new THREE.LineBasicMaterial( { fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + this.lightPlane = new THREE.Line( geometry, material ); + this.add( this.lightPlane ); + + geometry = new THREE.Geometry(); + geometry.vertices.push( + new THREE.Vector3(), + new THREE.Vector3() + ); + + material = new THREE.LineBasicMaterial( { fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + this.targetLine = new THREE.Line( geometry, material ); + this.add( this.targetLine ); + + this.update(); + +}; + +THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype ); +THREE.DirectionalLightHelper.prototype.constructor = THREE.DirectionalLightHelper; + +THREE.DirectionalLightHelper.prototype.dispose = function () { + + this.lightPlane.geometry.dispose(); + this.lightPlane.material.dispose(); + this.targetLine.geometry.dispose(); + this.targetLine.material.dispose(); + +}; + +THREE.DirectionalLightHelper.prototype.update = function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var v3 = new THREE.Vector3(); + + return function () { + + v1.setFromMatrixPosition( this.light.matrixWorld ); + v2.setFromMatrixPosition( this.light.target.matrixWorld ); + v3.subVectors( v2, v1 ); + + this.lightPlane.lookAt( v3 ); + this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + this.targetLine.geometry.vertices[ 1 ].copy( v3 ); + this.targetLine.geometry.verticesNeedUpdate = true; + this.targetLine.material.color.copy( this.lightPlane.material.color ); + + }; + +}(); + +// File:src/extras/helpers/EdgesHelper.js + +/** + * @author WestLangley / http://github.com/WestLangley + * @param object THREE.Mesh whose geometry will be used + * @param hex line color + * @param thresholdAngle the minimum angle (in degrees), + * between the face normals of adjacent faces, + * that is required to render an edge. A value of 10 means + * an edge is only rendered if the angle is at least 10 degrees. + */ + +THREE.EdgesHelper = function ( object, hex, thresholdAngle ) { + + var color = ( hex !== undefined ) ? hex : 0xffffff; + + THREE.LineSegments.call( this, new THREE.EdgesGeometry( object.geometry, thresholdAngle ), new THREE.LineBasicMaterial( { color: color } ) ); + + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; + +}; + +THREE.EdgesHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.EdgesHelper.prototype.constructor = THREE.EdgesHelper; + +// File:src/extras/helpers/FaceNormalsHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) { + + // FaceNormalsHelper only supports THREE.Geometry + + this.object = object; + + this.size = ( size !== undefined ) ? size : 1; + + var color = ( hex !== undefined ) ? hex : 0xffff00; + + var width = ( linewidth !== undefined ) ? linewidth : 1; + + // + + var nNormals = 0; + + var objGeometry = this.object.geometry; + + if ( objGeometry instanceof THREE.Geometry ) { + + nNormals = objGeometry.faces.length; + + } else { + + console.warn( 'THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead.' ); + + } + + // + + var geometry = new THREE.BufferGeometry(); + + var positions = new THREE.Float32Attribute( nNormals * 2 * 3, 3 ); + + geometry.addAttribute( 'position', positions ); + + THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ) ); + + // + + this.matrixAutoUpdate = false; + this.update(); + +}; + +THREE.FaceNormalsHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.FaceNormalsHelper.prototype.constructor = THREE.FaceNormalsHelper; + +THREE.FaceNormalsHelper.prototype.update = ( function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var normalMatrix = new THREE.Matrix3(); + + return function update() { + + this.object.updateMatrixWorld( true ); + + normalMatrix.getNormalMatrix( this.object.matrixWorld ); + + var matrixWorld = this.object.matrixWorld; + + var position = this.geometry.attributes.position; + + // + + var objGeometry = this.object.geometry; + + var vertices = objGeometry.vertices; + + var faces = objGeometry.faces; + + var idx = 0; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + var normal = face.normal; + + v1.copy( vertices[ face.a ] ) + .add( vertices[ face.b ] ) + .add( vertices[ face.c ] ) + .divideScalar( 3 ) + .applyMatrix4( matrixWorld ); + + v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); + + position.setXYZ( idx, v1.x, v1.y, v1.z ); + + idx = idx + 1; + + position.setXYZ( idx, v2.x, v2.y, v2.z ); + + idx = idx + 1; + + } + + position.needsUpdate = true; + + return this; + + } + +}() ); + +// File:src/extras/helpers/GridHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.GridHelper = function ( size, step ) { + + var geometry = new THREE.Geometry(); + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); + + this.color1 = new THREE.Color( 0x444444 ); + this.color2 = new THREE.Color( 0x888888 ); + + for ( var i = - size; i <= size; i += step ) { + + geometry.vertices.push( + new THREE.Vector3( - size, 0, i ), new THREE.Vector3( size, 0, i ), + new THREE.Vector3( i, 0, - size ), new THREE.Vector3( i, 0, size ) + ); + + var color = i === 0 ? this.color1 : this.color2; + + geometry.colors.push( color, color, color, color ); + + } + + THREE.LineSegments.call( this, geometry, material ); + +}; + +THREE.GridHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.GridHelper.prototype.constructor = THREE.GridHelper; + +THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) { + + this.color1.set( colorCenterLine ); + this.color2.set( colorGrid ); + + this.geometry.colorsNeedUpdate = true; + +}; + +// File:src/extras/helpers/HemisphereLightHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.HemisphereLightHelper = function ( light, sphereSize ) { + + THREE.Object3D.call( this ); + + this.light = light; + this.light.updateMatrixWorld(); + + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; + + this.colors = [ new THREE.Color(), new THREE.Color() ]; + + var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); + geometry.rotateX( - Math.PI / 2 ); + + for ( var i = 0, il = 8; i < il; i ++ ) { + + geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ]; + + } + + var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } ); + + this.lightSphere = new THREE.Mesh( geometry, material ); + this.add( this.lightSphere ); + + this.update(); + +}; + +THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype ); +THREE.HemisphereLightHelper.prototype.constructor = THREE.HemisphereLightHelper; + +THREE.HemisphereLightHelper.prototype.dispose = function () { + + this.lightSphere.geometry.dispose(); + this.lightSphere.material.dispose(); + +}; + +THREE.HemisphereLightHelper.prototype.update = function () { + + var vector = new THREE.Vector3(); + + return function () { + + this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity ); + this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity ); + + this.lightSphere.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() ); + this.lightSphere.geometry.colorsNeedUpdate = true; + + } + +}(); + +// File:src/extras/helpers/PointLightHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.PointLightHelper = function ( light, sphereSize ) { + + this.light = light; + this.light.updateMatrixWorld(); + + var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); + var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + THREE.Mesh.call( this, geometry, material ); + + this.matrix = this.light.matrixWorld; + this.matrixAutoUpdate = false; + + /* + var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); + var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); + + this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); + this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); + + var d = light.distance; + + if ( d === 0.0 ) { + + this.lightDistance.visible = false; + + } else { + + this.lightDistance.scale.set( d, d, d ); + + } + + this.add( this.lightDistance ); + */ + +}; + +THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype ); +THREE.PointLightHelper.prototype.constructor = THREE.PointLightHelper; + +THREE.PointLightHelper.prototype.dispose = function () { + + this.geometry.dispose(); + this.material.dispose(); + +}; + +THREE.PointLightHelper.prototype.update = function () { + + this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + /* + var d = this.light.distance; + + if ( d === 0.0 ) { + + this.lightDistance.visible = false; + + } else { + + this.lightDistance.visible = true; + this.lightDistance.scale.set( d, d, d ); + + } + */ + +}; + +// File:src/extras/helpers/SkeletonHelper.js + +/** + * @author Sean Griffin / http://twitter.com/sgrif + * @author Michael Guerrero / http://realitymeltdown.com + * @author mrdoob / http://mrdoob.com/ + * @author ikerr / http://verold.com + */ + +THREE.SkeletonHelper = function ( object ) { + + this.bones = this.getBoneList( object ); + + var geometry = new THREE.Geometry(); + + for ( var i = 0; i < this.bones.length; i ++ ) { + + var bone = this.bones[ i ]; + + if ( bone.parent instanceof THREE.Bone ) { + + geometry.vertices.push( new THREE.Vector3() ); + geometry.vertices.push( new THREE.Vector3() ); + geometry.colors.push( new THREE.Color( 0, 0, 1 ) ); + geometry.colors.push( new THREE.Color( 0, 1, 0 ) ); + + } + + } + + geometry.dynamic = true; + + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } ); + + THREE.LineSegments.call( this, geometry, material ); + + this.root = object; + + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; + + this.update(); + +}; + + +THREE.SkeletonHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.SkeletonHelper.prototype.constructor = THREE.SkeletonHelper; + +THREE.SkeletonHelper.prototype.getBoneList = function( object ) { + + var boneList = []; + + if ( object instanceof THREE.Bone ) { + + boneList.push( object ); + + } + + for ( var i = 0; i < object.children.length; i ++ ) { + + boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) ); + + } + + return boneList; + +}; + +THREE.SkeletonHelper.prototype.update = function () { + + var geometry = this.geometry; + + var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld ); + + var boneMatrix = new THREE.Matrix4(); + + var j = 0; + + for ( var i = 0; i < this.bones.length; i ++ ) { + + var bone = this.bones[ i ]; + + if ( bone.parent instanceof THREE.Bone ) { + + boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld ); + geometry.vertices[ j ].setFromMatrixPosition( boneMatrix ); + + boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld ); + geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix ); + + j += 2; + + } + + } + + geometry.verticesNeedUpdate = true; + + geometry.computeBoundingSphere(); + +}; + +// File:src/extras/helpers/SpotLightHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.SpotLightHelper = function ( light ) { + + THREE.Object3D.call( this ); + + this.light = light; + this.light.updateMatrixWorld(); + + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; + + var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true ); + + geometry.translate( 0, - 0.5, 0 ); + geometry.rotateX( - Math.PI / 2 ); + + var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); + + this.cone = new THREE.Mesh( geometry, material ); + this.add( this.cone ); + + this.update(); + +}; + +THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype ); +THREE.SpotLightHelper.prototype.constructor = THREE.SpotLightHelper; + +THREE.SpotLightHelper.prototype.dispose = function () { + + this.cone.geometry.dispose(); + this.cone.material.dispose(); + +}; + +THREE.SpotLightHelper.prototype.update = function () { + + var vector = new THREE.Vector3(); + var vector2 = new THREE.Vector3(); + + return function () { + + var coneLength = this.light.distance ? this.light.distance : 10000; + var coneWidth = coneLength * Math.tan( this.light.angle ); + + this.cone.scale.set( coneWidth, coneWidth, coneLength ); + + vector.setFromMatrixPosition( this.light.matrixWorld ); + vector2.setFromMatrixPosition( this.light.target.matrixWorld ); + + this.cone.lookAt( vector2.sub( vector ) ); + + this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + }; + +}(); + +// File:src/extras/helpers/VertexNormalsHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) { + + this.object = object; + + this.size = ( size !== undefined ) ? size : 1; + + var color = ( hex !== undefined ) ? hex : 0xff0000; + + var width = ( linewidth !== undefined ) ? linewidth : 1; + + // + + var nNormals = 0; + + var objGeometry = this.object.geometry; + + if ( objGeometry instanceof THREE.Geometry ) { + + nNormals = objGeometry.faces.length * 3; + + } else if ( objGeometry instanceof THREE.BufferGeometry ) { + + nNormals = objGeometry.attributes.normal.count + + } + + // + + var geometry = new THREE.BufferGeometry(); + + var positions = new THREE.Float32Attribute( nNormals * 2 * 3, 3 ); + + geometry.addAttribute( 'position', positions ); + + THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ) ); + + // + + this.matrixAutoUpdate = false; + + this.update(); + +}; + +THREE.VertexNormalsHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.VertexNormalsHelper.prototype.constructor = THREE.VertexNormalsHelper; + +THREE.VertexNormalsHelper.prototype.update = ( function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var normalMatrix = new THREE.Matrix3(); + + return function update() { + + var keys = [ 'a', 'b', 'c' ]; + + this.object.updateMatrixWorld( true ); + + normalMatrix.getNormalMatrix( this.object.matrixWorld ); + + var matrixWorld = this.object.matrixWorld; + + var position = this.geometry.attributes.position; + + // + + var objGeometry = this.object.geometry; + + if ( objGeometry instanceof THREE.Geometry ) { + + var vertices = objGeometry.vertices; + + var faces = objGeometry.faces; + + var idx = 0; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + + var vertex = vertices[ face[ keys[ j ] ] ]; + + var normal = face.vertexNormals[ j ]; + + v1.copy( vertex ).applyMatrix4( matrixWorld ); + + v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); + + position.setXYZ( idx, v1.x, v1.y, v1.z ); + + idx = idx + 1; + + position.setXYZ( idx, v2.x, v2.y, v2.z ); + + idx = idx + 1; + + } + + } + + } else if ( objGeometry instanceof THREE.BufferGeometry ) { + + var objPos = objGeometry.attributes.position; + + var objNorm = objGeometry.attributes.normal; + + var idx = 0; + + // for simplicity, ignore index and drawcalls, and render every normal + + for ( var j = 0, jl = objPos.count; j < jl; j ++ ) { + + v1.set( objPos.getX( j ), objPos.getY( j ), objPos.getZ( j ) ).applyMatrix4( matrixWorld ); + + v2.set( objNorm.getX( j ), objNorm.getY( j ), objNorm.getZ( j ) ); + + v2.applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); + + position.setXYZ( idx, v1.x, v1.y, v1.z ); + + idx = idx + 1; + + position.setXYZ( idx, v2.x, v2.y, v2.z ); + + idx = idx + 1; + + } + + } + + position.needsUpdate = true; + + return this; + + } + +}() ); + +// File:src/extras/helpers/WireframeHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.WireframeHelper = function ( object, hex ) { + + var color = ( hex !== undefined ) ? hex : 0xffffff; + + THREE.LineSegments.call( this, new THREE.WireframeGeometry( object.geometry ), new THREE.LineBasicMaterial( { color: color } ) ); + + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; + +}; + +THREE.WireframeHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.WireframeHelper.prototype.constructor = THREE.WireframeHelper; + +// File:src/extras/objects/ImmediateRenderObject.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ImmediateRenderObject = function ( material ) { + + THREE.Object3D.call( this ); + + this.material = material; + this.render = function ( renderCallback ) {}; + +}; + +THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype ); +THREE.ImmediateRenderObject.prototype.constructor = THREE.ImmediateRenderObject; + +// File:src/extras/objects/MorphBlendMesh.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.MorphBlendMesh = function( geometry, material ) { + + THREE.Mesh.call( this, geometry, material ); + + this.animationsMap = {}; + this.animationsList = []; + + // prepare default animation + // (all frames played together in 1 second) + + var numFrames = this.geometry.morphTargets.length; + + var name = "__default"; + + var startFrame = 0; + var endFrame = numFrames - 1; + + var fps = numFrames / 1; + + this.createAnimation( name, startFrame, endFrame, fps ); + this.setAnimationWeight( name, 1 ); + +}; + +THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype ); +THREE.MorphBlendMesh.prototype.constructor = THREE.MorphBlendMesh; + +THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) { + + var animation = { + + start: start, + end: end, + + length: end - start + 1, + + fps: fps, + duration: ( end - start ) / fps, + + lastFrame: 0, + currentFrame: 0, + + active: false, + + time: 0, + direction: 1, + weight: 1, + + directionBackwards: false, + mirroredLoop: false + + }; + + this.animationsMap[ name ] = animation; + this.animationsList.push( animation ); + +}; + +THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) { + + var pattern = /([a-z]+)_?(\d+)/; + + var firstAnimation, frameRanges = {}; + + var geometry = this.geometry; + + for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { + + var morph = geometry.morphTargets[ i ]; + var chunks = morph.name.match( pattern ); + + if ( chunks && chunks.length > 1 ) { + + var name = chunks[ 1 ]; + + if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity }; + + var range = frameRanges[ name ]; + + if ( i < range.start ) range.start = i; + if ( i > range.end ) range.end = i; + + if ( ! firstAnimation ) firstAnimation = name; + + } + + } + + for ( var name in frameRanges ) { + + var range = frameRanges[ name ]; + this.createAnimation( name, range.start, range.end, fps ); + + } + + this.firstAnimation = firstAnimation; + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.direction = 1; + animation.directionBackwards = false; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.direction = - 1; + animation.directionBackwards = true; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.fps = fps; + animation.duration = ( animation.end - animation.start ) / animation.fps; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.duration = duration; + animation.fps = ( animation.end - animation.start ) / animation.duration; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.weight = weight; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.time = time; + + } + +}; + +THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) { + + var time = 0; + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + time = animation.time; + + } + + return time; + +}; + +THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) { + + var duration = - 1; + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + duration = animation.duration; + + } + + return duration; + +}; + +THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.time = 0; + animation.active = true; + + } else { + + console.warn( "THREE.MorphBlendMesh: animation[" + name + "] undefined in .playAnimation()" ); + + } + +}; + +THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.active = false; + + } + +}; + +THREE.MorphBlendMesh.prototype.update = function ( delta ) { + + for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) { + + var animation = this.animationsList[ i ]; + + if ( ! animation.active ) continue; + + var frameTime = animation.duration / animation.length; + + animation.time += animation.direction * delta; + + if ( animation.mirroredLoop ) { + + if ( animation.time > animation.duration || animation.time < 0 ) { + + animation.direction *= - 1; + + if ( animation.time > animation.duration ) { + + animation.time = animation.duration; + animation.directionBackwards = true; + + } + + if ( animation.time < 0 ) { + + animation.time = 0; + animation.directionBackwards = false; + + } + + } + + } else { + + animation.time = animation.time % animation.duration; + + if ( animation.time < 0 ) animation.time += animation.duration; + + } + + var keyframe = animation.start + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 ); + var weight = animation.weight; + + if ( keyframe !== animation.currentFrame ) { + + this.morphTargetInfluences[ animation.lastFrame ] = 0; + this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight; + + this.morphTargetInfluences[ keyframe ] = 0; + + animation.lastFrame = animation.currentFrame; + animation.currentFrame = keyframe; + + } + + var mix = ( animation.time % frameTime ) / frameTime; + + if ( animation.directionBackwards ) mix = 1 - mix; + + if ( animation.currentFrame !== animation.lastFrame ) { + + this.morphTargetInfluences[ animation.currentFrame ] = mix * weight; + this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight; + + } else { + + this.morphTargetInfluences[ animation.currentFrame ] = weight; + + } + + } + +}; + + +// Export the THREE object for **Node.js**, with +// backwards-compatibility for the old `require()` API. If we're in +// the browser, add `_` as a global object via a string identifier, +// for Closure Compiler "advanced" mode. +if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = THREE; + } + exports.THREE = THREE; +} else { + this['THREE'] = THREE; +} + +},{}],6:[function(_dereq_,module,exports){ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof _dereq_=="function"&&_dereq_;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof _dereq_=="function"&&_dereq_;for(var o=0;o 0.0 && abs(vUV.x - 0.5) < .001) {', + // Don't render the divider, since it's rendered in HTML. + //'gl_FragColor = dividerColor;', + '} else if (a.x < 0.0 || a.x > 1.0 || a.y < 0.0 || a.y > 1.0) {', + 'gl_FragColor = backgroundColor;', + '} else {', + 'gl_FragColor = texture2D(texture, vec2(a.x * 0.5 + (vUV.x < 0.5 ? 0.0 : 0.5), a.y));', + '}', + '}' + + ].join('\n') +}; + +module.exports = BarrelDistortionFragment; + +},{}],6:[function(_dereq_,module,exports){ +/** + * TODO(smus): Implement coefficient inversion. + */ +function Distortion(coefficients) { + this.coefficients = coefficients; +} + +/** + * Calculates the inverse distortion for a radius. + *

+ * Allows to compute the original undistorted radius from a distorted one. + * See also getApproximateInverseDistortion() for a faster but potentially + * less accurate method. + * + * @param {Number} radius Distorted radius from the lens center in tan-angle units. + * @return {Number} The undistorted radius in tan-angle units. + */ +Distortion.prototype.distortInverse = function(radius) { + // Secant method. + var r0 = radius / 0.9; + var r1 = radius * 0.9; + var dr0 = radius - this.distort(r0); + while (Math.abs(r1 - r0) > 0.0001 /** 0.1mm */) { + var dr1 = radius - this.distort(r1); + var r2 = r1 - dr1 * ((r1 - r0) / (dr1 - dr0)); + r0 = r1; + r1 = r2; + dr0 = dr1; + } + return r1; +} + + +/** + * Distorts a radius by its distortion factor from the center of the lenses. + * + * @param {Number} radius Radius from the lens center in tan-angle units. + * @return {Number} The distorted radius in tan-angle units. + */ +Distortion.prototype.distort = function(radius) { + return radius * this.distortionFactor_(radius); +} + +/** + * Returns the distortion factor of a point. + * + * @param {Number} radius Radius of the point from the lens center in tan-angle units. + * @return {Number} The distortion factor. Multiply by this factor to distort points. + */ +Distortion.prototype.distortionFactor_ = function(radius) { + var result = 1.0; + var rFactor = 1.0; + var rSquared = radius * radius; + + for (var i = 0; i < this.coefficients.length; i++) { + var ki = this.coefficients[i]; + rFactor *= rSquared; + result += ki * rFactor; + } + + return result; +} + +module.exports = Distortion; + +},{}],7:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * DPDB cache. + */ +var DPDB_CACHE = { + "format": 1, + "last_updated": "2016-01-26T23:11:18Z", + "devices": [ + { + "type": "android", + "rules": [ + { "mdmh": "asus/*/Nexus 7/*" }, + { "ua": "Nexus 7" } + ], + "dpi": [ 320.8, 323.0 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "asus/*/ASUS_Z00AD/*" }, + { "ua": "ASUS_Z00AD" } + ], + "dpi": [ 403.0, 404.6 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "HTC/*/HTC6435LVW/*" }, + { "ua": "HTC6435LVW" } + ], + "dpi": [ 449.7, 443.3 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "HTC/*/HTC One XL/*" }, + { "ua": "HTC One XL" } + ], + "dpi": [ 315.3, 314.6 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "htc/*/Nexus 9/*" }, + { "ua": "Nexus 9" } + ], + "dpi": 289.0, + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "HTC/*/HTC One M9/*" }, + { "ua": "HTC One M9" } + ], + "dpi": [ 442.5, 443.3 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "HTC/*/HTC One_M8/*" }, + { "ua": "HTC One_M8" } + ], + "dpi": [ 449.7, 447.4 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "HTC/*/HTC One/*" }, + { "ua": "HTC One" } + ], + "dpi": 472.8, + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Huawei/*/Nexus 6P/*" }, + { "ua": "Nexus 6P" } + ], + "dpi": [ 515.1, 518.0 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/Nexus 5X/*" }, + { "ua": "Nexus 5X" } + ], + "dpi": [ 422.0, 419.9 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/LGMS345/*" }, + { "ua": "LGMS345" } + ], + "dpi": [ 221.7, 219.1 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/LG-D800/*" }, + { "ua": "LG-D800" } + ], + "dpi": [ 422.0, 424.1 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/LG-D850/*" }, + { "ua": "LG-D850" } + ], + "dpi": [ 537.9, 541.9 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/VS985 4G/*" }, + { "ua": "VS985 4G" } + ], + "dpi": [ 537.9, 535.6 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/Nexus 5/*" }, + { "ua": "Nexus 5 B" } + ], + "dpi": [ 442.4, 444.8 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/Nexus 4/*" }, + { "ua": "Nexus 4" } + ], + "dpi": [ 319.8, 318.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/LG-P769/*" }, + { "ua": "LG-P769" } + ], + "dpi": [ 240.6, 247.5 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/LGMS323/*" }, + { "ua": "LGMS323" } + ], + "dpi": [ 206.6, 204.6 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/LGLS996/*" }, + { "ua": "LGLS996" } + ], + "dpi": [ 403.4, 401.5 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Micromax/*/4560MMX/*" }, + { "ua": "4560MMX" } + ], + "dpi": [ 240.0, 219.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Micromax/*/A250/*" }, + { "ua": "Micromax A250" } + ], + "dpi": [ 480.0, 446.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Micromax/*/Micromax AQ4501/*" }, + { "ua": "Micromax AQ4501" } + ], + "dpi": 240.0, + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/DROID RAZR/*" }, + { "ua": "DROID RAZR" } + ], + "dpi": [ 368.1, 256.7 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT830C/*" }, + { "ua": "XT830C" } + ], + "dpi": [ 254.0, 255.9 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1021/*" }, + { "ua": "XT1021" } + ], + "dpi": [ 254.0, 256.7 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1023/*" }, + { "ua": "XT1023" } + ], + "dpi": [ 254.0, 256.7 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1028/*" }, + { "ua": "XT1028" } + ], + "dpi": [ 326.6, 327.6 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1034/*" }, + { "ua": "XT1034" } + ], + "dpi": [ 326.6, 328.4 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1053/*" }, + { "ua": "XT1053" } + ], + "dpi": [ 315.3, 316.1 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1562/*" }, + { "ua": "XT1562" } + ], + "dpi": [ 403.4, 402.7 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/Nexus 6/*" }, + { "ua": "Nexus 6 B" } + ], + "dpi": [ 494.3, 489.7 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1063/*" }, + { "ua": "XT1063" } + ], + "dpi": [ 295.0, 296.6 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1064/*" }, + { "ua": "XT1064" } + ], + "dpi": [ 295.0, 295.6 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1092/*" }, + { "ua": "XT1092" } + ], + "dpi": [ 422.0, 424.1 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1095/*" }, + { "ua": "XT1095" } + ], + "dpi": [ 422.0, 423.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "OnePlus/*/A0001/*" }, + { "ua": "A0001" } + ], + "dpi": [ 403.4, 401.0 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "OnePlus/*/ONE E1005/*" }, + { "ua": "ONE E1005" } + ], + "dpi": [ 442.4, 441.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "OnePlus/*/ONE A2005/*" }, + { "ua": "ONE A2005" } + ], + "dpi": [ 391.9, 405.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "OPPO/*/X909/*" }, + { "ua": "X909" } + ], + "dpi": [ 442.4, 444.1 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/GT-I9082/*" }, + { "ua": "GT-I9082" } + ], + "dpi": [ 184.7, 185.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G360P/*" }, + { "ua": "SM-G360P" } + ], + "dpi": [ 196.7, 205.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/Nexus S/*" }, + { "ua": "Nexus S" } + ], + "dpi": [ 234.5, 229.8 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/GT-I9300/*" }, + { "ua": "GT-I9300" } + ], + "dpi": [ 304.8, 303.9 ], + "bw": 5, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-T230NU/*" }, + { "ua": "SM-T230NU" } + ], + "dpi": 216.0, + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SGH-T399/*" }, + { "ua": "SGH-T399" } + ], + "dpi": [ 217.7, 231.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-N9005/*" }, + { "ua": "SM-N9005" } + ], + "dpi": [ 386.4, 387.0 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SAMSUNG-SM-N900A/*" }, + { "ua": "SAMSUNG-SM-N900A" } + ], + "dpi": [ 386.4, 387.7 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/GT-I9500/*" }, + { "ua": "GT-I9500" } + ], + "dpi": [ 442.5, 443.3 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/GT-I9505/*" }, + { "ua": "GT-I9505" } + ], + "dpi": 439.4, + "bw": 4, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G900F/*" }, + { "ua": "SM-G900F" } + ], + "dpi": [ 415.6, 431.6 ], + "bw": 5, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G900M/*" }, + { "ua": "SM-G900M" } + ], + "dpi": [ 415.6, 431.6 ], + "bw": 5, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G800F/*" }, + { "ua": "SM-G800F" } + ], + "dpi": 326.8, + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G906S/*" }, + { "ua": "SM-G906S" } + ], + "dpi": [ 562.7, 572.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/GT-I9300/*" }, + { "ua": "GT-I9300" } + ], + "dpi": [ 306.7, 304.8 ], + "bw": 5, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-T535/*" }, + { "ua": "SM-T535" } + ], + "dpi": [ 142.6, 136.4 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-N920C/*" }, + { "ua": "SM-N920C" } + ], + "dpi": [ 515.1, 518.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/GT-I9300I/*" }, + { "ua": "GT-I9300I" } + ], + "dpi": [ 304.8, 305.8 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/GT-I9195/*" }, + { "ua": "GT-I9195" } + ], + "dpi": [ 249.4, 256.7 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SPH-L520/*" }, + { "ua": "SPH-L520" } + ], + "dpi": [ 249.4, 255.9 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SAMSUNG-SGH-I717/*" }, + { "ua": "SAMSUNG-SGH-I717" } + ], + "dpi": 285.8, + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SPH-D710/*" }, + { "ua": "SPH-D710" } + ], + "dpi": [ 217.7, 204.2 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/GT-N7100/*" }, + { "ua": "GT-N7100" } + ], + "dpi": 265.1, + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SCH-I605/*" }, + { "ua": "SCH-I605" } + ], + "dpi": 265.1, + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/Galaxy Nexus/*" }, + { "ua": "Galaxy Nexus" } + ], + "dpi": [ 315.3, 314.2 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-N910H/*" }, + { "ua": "SM-N910H" } + ], + "dpi": [ 515.1, 518.0 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-N910C/*" }, + { "ua": "SM-N910C" } + ], + "dpi": [ 515.2, 520.2 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G130M/*" }, + { "ua": "SM-G130M" } + ], + "dpi": [ 165.9, 164.8 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G928I/*" }, + { "ua": "SM-G928I" } + ], + "dpi": [ 515.1, 518.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G920F/*" }, + { "ua": "SM-G920F" } + ], + "dpi": 580.6, + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G920P/*" }, + { "ua": "SM-G920P" } + ], + "dpi": [ 522.5, 577.0 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G925F/*" }, + { "ua": "SM-G925F" } + ], + "dpi": 580.6, + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G925V/*" }, + { "ua": "SM-G925V" } + ], + "dpi": [ 522.5, 576.6 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Sony/*/C6903/*" }, + { "ua": "C6903" } + ], + "dpi": [ 442.5, 443.3 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Sony/*/D6653/*" }, + { "ua": "D6653" } + ], + "dpi": [ 428.6, 427.6 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Sony/*/E6653/*" }, + { "ua": "E6653" } + ], + "dpi": [ 428.6, 425.7 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Sony/*/E6853/*" }, + { "ua": "E6853" } + ], + "dpi": [ 403.4, 401.9 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Sony/*/SGP321/*" }, + { "ua": "SGP321" } + ], + "dpi": [ 224.7, 224.1 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "TCT/*/ALCATEL ONE TOUCH Fierce/*" }, + { "ua": "ALCATEL ONE TOUCH Fierce" } + ], + "dpi": [ 240.0, 247.5 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "THL/*/thl 5000/*" }, + { "ua": "thl 5000" } + ], + "dpi": [ 480.0, 443.3 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "ZTE/*/ZTE Blade L2/*" }, + { "ua": "ZTE Blade L2" } + ], + "dpi": 240.0, + "bw": 3, + "ac": 500 + }, + { + "type": "ios", + "rules": [ { "res": [ 640, 960 ] } ], + "dpi": [ 325.1, 328.4 ], + "bw": 4, + "ac": 1000 + }, + { + "type": "ios", + "rules": [ { "res": [ 640, 1136 ] } ], + "dpi": [ 317.1, 320.2 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "ios", + "rules": [ { "res": [ 750, 1334 ] } ], + "dpi": 326.4, + "bw": 4, + "ac": 1000 + }, + { + "type": "ios", + "rules": [ { "res": [ 1242, 2208 ] } ], + "dpi": [ 453.6, 458.4 ], + "bw": 4, + "ac": 1000 + }, + { + "type": "ios", + "rules": [ { "res": [ 1125, 2001 ] } ], + "dpi": [ 410.9, 415.4 ], + "bw": 4, + "ac": 1000 + } +]}; + +module.exports = DPDB_CACHE; + + +},{}],8:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Offline cache of the DPDB, to be used until we load the online one (and +// as a fallback in case we can't load the online one). +var DPDB_CACHE = _dereq_('./dpdb-cache.js'); +var Util = _dereq_('./util.js'); + +// Online DPDB URL. +var ONLINE_DPDB_URL = 'https://storage.googleapis.com/cardboard-dpdb/dpdb.json'; + +/** + * Calculates device parameters based on the DPDB (Device Parameter Database). + * Initially, uses the cached DPDB values. + * + * If fetchOnline == true, then this object tries to fetch the online version + * of the DPDB and updates the device info if a better match is found. + * Calls the onDeviceParamsUpdated callback when there is an update to the + * device information. + */ +function Dpdb(fetchOnline, onDeviceParamsUpdated) { + // Start with the offline DPDB cache while we are loading the real one. + this.dpdb = DPDB_CACHE; + + // Calculate device params based on the offline version of the DPDB. + this.recalculateDeviceParams_(); + + // XHR to fetch online DPDB file, if requested. + if (fetchOnline) { + // Set the callback. + this.onDeviceParamsUpdated = onDeviceParamsUpdated; + + console.log('Fetching DPDB...'); + var xhr = new XMLHttpRequest(); + var obj = this; + xhr.open('GET', ONLINE_DPDB_URL, true); + xhr.addEventListener('load', function() { + obj.loading = false; + if (xhr.status >= 200 && xhr.status <= 299) { + // Success. + console.log('Successfully loaded online DPDB.'); + obj.dpdb = JSON.parse(xhr.response); + obj.recalculateDeviceParams_(); + } else { + // Error loading the DPDB. + console.error('Error loading online DPDB!'); + } + }); + xhr.send(); + } +} + +// Returns the current device parameters. +Dpdb.prototype.getDeviceParams = function() { + return this.deviceParams; +}; + +// Recalculates this device's parameters based on the DPDB. +Dpdb.prototype.recalculateDeviceParams_ = function() { + console.log('Recalculating device params.'); + var newDeviceParams = this.calcDeviceParams_(); + console.log('New device parameters:'); + console.log(newDeviceParams); + if (newDeviceParams) { + this.deviceParams = newDeviceParams; + // Invoke callback, if it is set. + if (this.onDeviceParamsUpdated) { + this.onDeviceParamsUpdated(this.deviceParams); + } + } else { + console.warn('Failed to recalculate device parameters.'); + } +}; + +// Returns a DeviceParams object that represents the best guess as to this +// device's parameters. Can return null if the device does not match any +// known devices. +Dpdb.prototype.calcDeviceParams_ = function() { + var db = this.dpdb; // shorthand + if (!db) { + console.error('DPDB not available.'); + return null; + } + if (db.format != 1) { + console.error('DPDB has unexpected format version.'); + return null; + } + if (!db.devices || !db.devices.length) { + console.error('DPDB does not have a devices section.'); + return null; + } + + // Get the actual user agent and screen dimensions in pixels. + var userAgent = navigator.userAgent || navigator.vendor || window.opera; + var width = Util.getScreenWidth(); + var height = Util.getScreenHeight(); + console.log('User agent: ' + userAgent); + console.log('Pixel width: ' + width); + console.log('Pixel height: ' + height); + + if (!db.devices) { + console.error('DPDB has no devices section.'); + return null; + } + + for (var i = 0; i < db.devices.length; i++) { + var device = db.devices[i]; + if (!device.rules) { + console.warn('Device[' + i + '] has no rules section.'); + continue; + } + + if (device.type != 'ios' && device.type != 'android') { + console.warn('Device[' + i + '] has invalid type.'); + continue; + } + + // See if this device is of the appropriate type. + if (Util.isIOS() != (device.type == 'ios')) continue; + + // See if this device matches any of the rules: + var matched = false; + for (var j = 0; j < device.rules.length; j++) { + var rule = device.rules[j]; + if (this.matchRule_(rule, userAgent, width, height)) { + console.log('Rule matched:'); + console.log(rule); + matched = true; + break; + } + } + if (!matched) continue; + + // device.dpi might be an array of [ xdpi, ydpi] or just a scalar. + var xdpi = device.dpi[0] || device.dpi; + var ydpi = device.dpi[1] || device.dpi; + + return new DeviceParams({ xdpi: xdpi, ydpi: ydpi, bevelMm: device.bw }); + } + + console.warn('No DPDB device match.'); + return null; +}; + +Dpdb.prototype.matchRule_ = function(rule, ua, screenWidth, screenHeight) { + // We can only match 'ua' and 'res' rules, not other types like 'mdmh' + // (which are meant for native platforms). + if (!rule.ua && !rule.res) return false; + + // If our user agent string doesn't contain the indicated user agent string, + // the match fails. + if (rule.ua && ua.indexOf(rule.ua) < 0) return false; + + // If the rule specifies screen dimensions that don't correspond to ours, + // the match fails. + if (rule.res) { + if (!rule.res[0] || !rule.res[1]) return false; + var resX = rule.res[0]; + var resY = rule.res[1]; + // Compare min and max so as to make the order not matter, i.e., it should + // be true that 640x480 == 480x640. + if (Math.min(screenWidth, screenHeight) != Math.min(resX, resY) || + (Math.max(screenWidth, screenHeight) != Math.max(resX, resY))) { + return false; + } + } + + return true; +} + +function DeviceParams(params) { + this.xdpi = params.xdpi; + this.ydpi = params.ydpi; + this.bevelMm = params.bevelMm; +} + +module.exports = Dpdb; + +},{"./dpdb-cache.js":7,"./util.js":13}],9:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function Emitter() { + this.callbacks = {}; +} + +Emitter.prototype.emit = function(eventName) { + var callbacks = this.callbacks[eventName]; + if (!callbacks) { + //console.log('No valid callback specified.'); + return; + } + var args = [].slice.call(arguments); + // Eliminate the first param (the callback). + args.shift(); + for (var i = 0; i < callbacks.length; i++) { + callbacks[i].apply(this, args); + } +}; + +Emitter.prototype.on = function(eventName, callback) { + if (eventName in this.callbacks) { + this.callbacks[eventName].push(callback); + } else { + this.callbacks[eventName] = [callback]; + } +}; + +module.exports = Emitter; + +},{}],10:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var WebVRManager = _dereq_('./webvr-manager.js'); + +window.WebVRConfig = window.WebVRConfig || {}; +window.WebVRManager = WebVRManager; + +},{"./webvr-manager.js":16}],11:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var Modes = { + UNKNOWN: 0, + // Not fullscreen, just tracking. + NORMAL: 1, + // Magic window immersive mode. + MAGIC_WINDOW: 2, + // Full screen split screen VR mode. + VR: 3, +}; + +module.exports = Modes; + +},{}],12:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var Util = _dereq_('./util.js'); + +function RotateInstructions() { + this.loadIcon_(); + + var overlay = document.createElement('div'); + var s = overlay.style; + s.position = 'fixed'; + s.top = 0; + s.right = 0; + s.bottom = 0; + s.left = 0; + s.backgroundColor = 'gray'; + s.fontFamily = 'sans-serif'; + + var img = document.createElement('img'); + img.src = this.icon; + var s = img.style; + s.marginLeft = '25%'; + s.marginTop = '25%'; + s.width = '50%'; + overlay.appendChild(img); + + var text = document.createElement('div'); + var s = text.style; + s.textAlign = 'center'; + s.fontSize = '16px'; + s.lineHeight = '24px'; + s.margin = '24px 25%'; + s.width = '50%'; + text.innerHTML = 'Place your phone into your Cardboard viewer.'; + overlay.appendChild(text); + + var snackbar = document.createElement('div'); + var s = snackbar.style; + s.backgroundColor = '#CFD8DC'; + s.position = 'fixed'; + s.bottom = 0; + s.width = '100%'; + s.height = '48px'; + s.padding = '14px 24px'; + s.boxSizing = 'border-box'; + s.color = '#656A6B'; + overlay.appendChild(snackbar); + + var snackbarText = document.createElement('div'); + snackbarText.style.float = 'left'; + snackbarText.innerHTML = 'No Cardboard viewer?'; + + var snackbarButton = document.createElement('a'); + snackbarButton.href = 'https://www.google.com/get/cardboard/get-cardboard/'; + snackbarButton.innerHTML = 'get one'; + snackbarButton.target = '_blank'; + var s = snackbarButton.style; + s.float = 'right'; + s.fontWeight = 600; + s.textTransform = 'uppercase'; + s.borderLeft = '1px solid gray'; + s.paddingLeft = '24px'; + s.textDecoration = 'none'; + s.color = '#656A6B'; + + snackbar.appendChild(snackbarText); + snackbar.appendChild(snackbarButton); + + this.overlay = overlay; + this.text = text; + document.body.appendChild(overlay); + + this.hide(); +} + +RotateInstructions.prototype.show = function() { + this.overlay.style.display = 'block'; + + var img = this.overlay.querySelector('img'); + var s = img.style; + + if (Util.isLandscapeMode()) { + s.width = '20%'; + s.marginLeft = '40%'; + s.marginTop = '3%'; + } else { + s.width = '50%'; + s.marginLeft = '25%'; + s.marginTop = '25%'; + } +}; + +RotateInstructions.prototype.hide = function() { + this.overlay.style.display = 'none'; +}; + +RotateInstructions.prototype.showTemporarily = function(ms) { + this.show(); + this.timer = setTimeout(this.hide.bind(this), ms); +}; + +RotateInstructions.prototype.disableShowTemporarily = function() { + clearTimeout(this.timer); +}; + +RotateInstructions.prototype.loadIcon_ = function() { + // Encoded asset_src/rotate-instructions.svg + this.icon = Util.base64('image/svg+xml', 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjE5OHB4IiBoZWlnaHQ9IjI0MHB4IiB2aWV3Qm94PSIwIDAgMTk4IDI0MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxuczpza2V0Y2g9Imh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaC9ucyI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDMuMy4zICgxMjA4MSkgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+dHJhbnNpdGlvbjwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPjwvZGVmcz4KICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHNrZXRjaDp0eXBlPSJNU1BhZ2UiPgogICAgICAgIDxnIGlkPSJ0cmFuc2l0aW9uIiBza2V0Y2g6dHlwZT0iTVNBcnRib2FyZEdyb3VwIj4KICAgICAgICAgICAgPGcgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTQtKy1JbXBvcnRlZC1MYXllcnMtQ29weS0rLUltcG9ydGVkLUxheWVycy1Db3B5LTItQ29weSIgc2tldGNoOnR5cGU9Ik1TTGF5ZXJHcm91cCI+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHktNCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDEwNy4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjYyNSwyLjUyNyBDMTQ5LjYyNSwyLjUyNyAxNTUuODA1LDYuMDk2IDE1Ni4zNjIsNi40MTggTDE1Ni4zNjIsNy4zMDQgQzE1Ni4zNjIsNy40ODEgMTU2LjM3NSw3LjY2NCAxNTYuNCw3Ljg1MyBDMTU2LjQxLDcuOTM0IDE1Ni40Miw4LjAxNSAxNTYuNDI3LDguMDk1IEMxNTYuNTY3LDkuNTEgMTU3LjQwMSwxMS4wOTMgMTU4LjUzMiwxMi4wOTQgTDE2NC4yNTIsMTcuMTU2IEwxNjQuMzMzLDE3LjA2NiBDMTY0LjMzMywxNy4wNjYgMTY4LjcxNSwxNC41MzYgMTY5LjU2OCwxNC4wNDIgQzE3MS4wMjUsMTQuODgzIDE5NS41MzgsMjkuMDM1IDE5NS41MzgsMjkuMDM1IEwxOTUuNTM4LDgzLjAzNiBDMTk1LjUzOCw4My44MDcgMTk1LjE1Miw4NC4yNTMgMTk0LjU5LDg0LjI1MyBDMTk0LjM1Nyw4NC4yNTMgMTk0LjA5NSw4NC4xNzcgMTkzLjgxOCw4NC4wMTcgTDE2OS44NTEsNzAuMTc5IEwxNjkuODM3LDcwLjIwMyBMMTQyLjUxNSw4NS45NzggTDE0MS42NjUsODQuNjU1IEMxMzYuOTM0LDgzLjEyNiAxMzEuOTE3LDgxLjkxNSAxMjYuNzE0LDgxLjA0NSBDMTI2LjcwOSw4MS4wNiAxMjYuNzA3LDgxLjA2OSAxMjYuNzA3LDgxLjA2OSBMMTIxLjY0LDk4LjAzIEwxMTMuNzQ5LDEwMi41ODYgTDExMy43MTIsMTAyLjUyMyBMMTEzLjcxMiwxMzAuMTEzIEMxMTMuNzEyLDEzMC44ODUgMTEzLjMyNiwxMzEuMzMgMTEyLjc2NCwxMzEuMzMgQzExMi41MzIsMTMxLjMzIDExMi4yNjksMTMxLjI1NCAxMTEuOTkyLDEzMS4wOTQgTDY5LjUxOSwxMDYuNTcyIEM2OC41NjksMTA2LjAyMyA2Ny43OTksMTA0LjY5NSA2Ny43OTksMTAzLjYwNSBMNjcuNzk5LDEwMi41NyBMNjcuNzc4LDEwMi42MTcgQzY3LjI3LDEwMi4zOTMgNjYuNjQ4LDEwMi4yNDkgNjUuOTYyLDEwMi4yMTggQzY1Ljg3NSwxMDIuMjE0IDY1Ljc4OCwxMDIuMjEyIDY1LjcwMSwxMDIuMjEyIEM2NS42MDYsMTAyLjIxMiA2NS41MTEsMTAyLjIxNSA2NS40MTYsMTAyLjIxOSBDNjUuMTk1LDEwMi4yMjkgNjQuOTc0LDEwMi4yMzUgNjQuNzU0LDEwMi4yMzUgQzY0LjMzMSwxMDIuMjM1IDYzLjkxMSwxMDIuMjE2IDYzLjQ5OCwxMDIuMTc4IEM2MS44NDMsMTAyLjAyNSA2MC4yOTgsMTAxLjU3OCA1OS4wOTQsMTAwLjg4MiBMMTIuNTE4LDczLjk5MiBMMTIuNTIzLDc0LjAwNCBMMi4yNDUsNTUuMjU0IEMxLjI0NCw1My40MjcgMi4wMDQsNTEuMDM4IDMuOTQzLDQ5LjkxOCBMNTkuOTU0LDE3LjU3MyBDNjAuNjI2LDE3LjE4NSA2MS4zNSwxNy4wMDEgNjIuMDUzLDE3LjAwMSBDNjMuMzc5LDE3LjAwMSA2NC42MjUsMTcuNjYgNjUuMjgsMTguODU0IEw2NS4yODUsMTguODUxIEw2NS41MTIsMTkuMjY0IEw2NS41MDYsMTkuMjY4IEM2NS45MDksMjAuMDAzIDY2LjQwNSwyMC42OCA2Ni45ODMsMjEuMjg2IEw2Ny4yNiwyMS41NTYgQzY5LjE3NCwyMy40MDYgNzEuNzI4LDI0LjM1NyA3NC4zNzMsMjQuMzU3IEM3Ni4zMjIsMjQuMzU3IDc4LjMyMSwyMy44NCA4MC4xNDgsMjIuNzg1IEM4MC4xNjEsMjIuNzg1IDg3LjQ2NywxOC41NjYgODcuNDY3LDE4LjU2NiBDODguMTM5LDE4LjE3OCA4OC44NjMsMTcuOTk0IDg5LjU2NiwxNy45OTQgQzkwLjg5MiwxNy45OTQgOTIuMTM4LDE4LjY1MiA5Mi43OTIsMTkuODQ3IEw5Ni4wNDIsMjUuNzc1IEw5Ni4wNjQsMjUuNzU3IEwxMDIuODQ5LDI5LjY3NCBMMTAyLjc0NCwyOS40OTIgTDE0OS42MjUsMi41MjcgTTE0OS42MjUsMC44OTIgQzE0OS4zNDMsMC44OTIgMTQ5LjA2MiwwLjk2NSAxNDguODEsMS4xMSBMMTAyLjY0MSwyNy42NjYgTDk3LjIzMSwyNC41NDIgTDk0LjIyNiwxOS4wNjEgQzkzLjMxMywxNy4zOTQgOTEuNTI3LDE2LjM1OSA4OS41NjYsMTYuMzU4IEM4OC41NTUsMTYuMzU4IDg3LjU0NiwxNi42MzIgODYuNjQ5LDE3LjE1IEM4My44NzgsMTguNzUgNzkuNjg3LDIxLjE2OSA3OS4zNzQsMjEuMzQ1IEM3OS4zNTksMjEuMzUzIDc5LjM0NSwyMS4zNjEgNzkuMzMsMjEuMzY5IEM3Ny43OTgsMjIuMjU0IDc2LjA4NCwyMi43MjIgNzQuMzczLDIyLjcyMiBDNzIuMDgxLDIyLjcyMiA2OS45NTksMjEuODkgNjguMzk3LDIwLjM4IEw2OC4xNDUsMjAuMTM1IEM2Ny43MDYsMTkuNjcyIDY3LjMyMywxOS4xNTYgNjcuMDA2LDE4LjYwMSBDNjYuOTg4LDE4LjU1OSA2Ni45NjgsMTguNTE5IDY2Ljk0NiwxOC40NzkgTDY2LjcxOSwxOC4wNjUgQzY2LjY5LDE4LjAxMiA2Ni42NTgsMTcuOTYgNjYuNjI0LDE3LjkxMSBDNjUuNjg2LDE2LjMzNyA2My45NTEsMTUuMzY2IDYyLjA1MywxNS4zNjYgQzYxLjA0MiwxNS4zNjYgNjAuMDMzLDE1LjY0IDU5LjEzNiwxNi4xNTggTDMuMTI1LDQ4LjUwMiBDMC40MjYsNTAuMDYxIC0wLjYxMyw1My40NDIgMC44MTEsNTYuMDQgTDExLjA4OSw3NC43OSBDMTEuMjY2LDc1LjExMyAxMS41MzcsNzUuMzUzIDExLjg1LDc1LjQ5NCBMNTguMjc2LDEwMi4yOTggQzU5LjY3OSwxMDMuMTA4IDYxLjQzMywxMDMuNjMgNjMuMzQ4LDEwMy44MDYgQzYzLjgxMiwxMDMuODQ4IDY0LjI4NSwxMDMuODcgNjQuNzU0LDEwMy44NyBDNjUsMTAzLjg3IDY1LjI0OSwxMDMuODY0IDY1LjQ5NCwxMDMuODUyIEM2NS41NjMsMTAzLjg0OSA2NS42MzIsMTAzLjg0NyA2NS43MDEsMTAzLjg0NyBDNjUuNzY0LDEwMy44NDcgNjUuODI4LDEwMy44NDkgNjUuODksMTAzLjg1MiBDNjUuOTg2LDEwMy44NTYgNjYuMDgsMTAzLjg2MyA2Ni4xNzMsMTAzLjg3NCBDNjYuMjgyLDEwNS40NjcgNjcuMzMyLDEwNy4xOTcgNjguNzAyLDEwNy45ODggTDExMS4xNzQsMTMyLjUxIEMxMTEuNjk4LDEzMi44MTIgMTEyLjIzMiwxMzIuOTY1IDExMi43NjQsMTMyLjk2NSBDMTE0LjI2MSwxMzIuOTY1IDExNS4zNDcsMTMxLjc2NSAxMTUuMzQ3LDEzMC4xMTMgTDExNS4zNDcsMTAzLjU1MSBMMTIyLjQ1OCw5OS40NDYgQzEyMi44MTksOTkuMjM3IDEyMy4wODcsOTguODk4IDEyMy4yMDcsOTguNDk4IEwxMjcuODY1LDgyLjkwNSBDMTMyLjI3OSw4My43MDIgMTM2LjU1Nyw4NC43NTMgMTQwLjYwNyw4Ni4wMzMgTDE0MS4xNCw4Ni44NjIgQzE0MS40NTEsODcuMzQ2IDE0MS45NzcsODcuNjEzIDE0Mi41MTYsODcuNjEzIEMxNDIuNzk0LDg3LjYxMyAxNDMuMDc2LDg3LjU0MiAxNDMuMzMzLDg3LjM5MyBMMTY5Ljg2NSw3Mi4wNzYgTDE5Myw4NS40MzMgQzE5My41MjMsODUuNzM1IDE5NC4wNTgsODUuODg4IDE5NC41OSw4NS44ODggQzE5Ni4wODcsODUuODg4IDE5Ny4xNzMsODQuNjg5IDE5Ny4xNzMsODMuMDM2IEwxOTcuMTczLDI5LjAzNSBDMTk3LjE3MywyOC40NTEgMTk2Ljg2MSwyNy45MTEgMTk2LjM1NSwyNy42MTkgQzE5Ni4zNTUsMjcuNjE5IDE3MS44NDMsMTMuNDY3IDE3MC4zODUsMTIuNjI2IEMxNzAuMTMyLDEyLjQ4IDE2OS44NSwxMi40MDcgMTY5LjU2OCwxMi40MDcgQzE2OS4yODUsMTIuNDA3IDE2OS4wMDIsMTIuNDgxIDE2OC43NDksMTIuNjI3IEMxNjguMTQzLDEyLjk3OCAxNjUuNzU2LDE0LjM1NyAxNjQuNDI0LDE1LjEyNSBMMTU5LjYxNSwxMC44NyBDMTU4Ljc5NiwxMC4xNDUgMTU4LjE1NCw4LjkzNyAxNTguMDU0LDcuOTM0IEMxNTguMDQ1LDcuODM3IDE1OC4wMzQsNy43MzkgMTU4LjAyMSw3LjY0IEMxNTguMDA1LDcuNTIzIDE1Ny45OTgsNy40MSAxNTcuOTk4LDcuMzA0IEwxNTcuOTk4LDYuNDE4IEMxNTcuOTk4LDUuODM0IDE1Ny42ODYsNS4yOTUgMTU3LjE4MSw1LjAwMiBDMTU2LjYyNCw0LjY4IDE1MC40NDIsMS4xMTEgMTUwLjQ0MiwxLjExMSBDMTUwLjE4OSwwLjk2NSAxNDkuOTA3LDAuODkyIDE0OS42MjUsMC44OTIiIGlkPSJGaWxsLTEiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTYuMDI3LDI1LjYzNiBMMTQyLjYwMyw1Mi41MjcgQzE0My44MDcsNTMuMjIyIDE0NC41ODIsNTQuMTE0IDE0NC44NDUsNTUuMDY4IEwxNDQuODM1LDU1LjA3NSBMNjMuNDYxLDEwMi4wNTcgTDYzLjQ2LDEwMi4wNTcgQzYxLjgwNiwxMDEuOTA1IDYwLjI2MSwxMDEuNDU3IDU5LjA1NywxMDAuNzYyIEwxMi40ODEsNzMuODcxIEw5Ni4wMjcsMjUuNjM2IiBpZD0iRmlsbC0yIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYzLjQ2MSwxMDIuMTc0IEM2My40NTMsMTAyLjE3NCA2My40NDYsMTAyLjE3NCA2My40MzksMTAyLjE3MiBDNjEuNzQ2LDEwMi4wMTYgNjAuMjExLDEwMS41NjMgNTguOTk4LDEwMC44NjMgTDEyLjQyMiw3My45NzMgQzEyLjM4Niw3My45NTIgMTIuMzY0LDczLjkxNCAxMi4zNjQsNzMuODcxIEMxMi4zNjQsNzMuODMgMTIuMzg2LDczLjc5MSAxMi40MjIsNzMuNzcgTDk1Ljk2OCwyNS41MzUgQzk2LjAwNCwyNS41MTQgOTYuMDQ5LDI1LjUxNCA5Ni4wODUsMjUuNTM1IEwxNDIuNjYxLDUyLjQyNiBDMTQzLjg4OCw1My4xMzQgMTQ0LjY4Miw1NC4wMzggMTQ0Ljk1Nyw1NS4wMzcgQzE0NC45Nyw1NS4wODMgMTQ0Ljk1Myw1NS4xMzMgMTQ0LjkxNSw1NS4xNjEgQzE0NC45MTEsNTUuMTY1IDE0NC44OTgsNTUuMTc0IDE0NC44OTQsNTUuMTc3IEw2My41MTksMTAyLjE1OCBDNjMuNTAxLDEwMi4xNjkgNjMuNDgxLDEwMi4xNzQgNjMuNDYxLDEwMi4xNzQgTDYzLjQ2MSwxMDIuMTc0IFogTTEyLjcxNCw3My44NzEgTDU5LjExNSwxMDAuNjYxIEM2MC4yOTMsMTAxLjM0MSA2MS43ODYsMTAxLjc4MiA2My40MzUsMTAxLjkzNyBMMTQ0LjcwNyw1NS4wMTUgQzE0NC40MjgsNTQuMTA4IDE0My42ODIsNTMuMjg1IDE0Mi41NDQsNTIuNjI4IEw5Ni4wMjcsMjUuNzcxIEwxMi43MTQsNzMuODcxIEwxMi43MTQsNzMuODcxIFoiIGlkPSJGaWxsLTMiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ4LjMyNyw1OC40NzEgQzE0OC4xNDUsNTguNDggMTQ3Ljk2Miw1OC40OCAxNDcuNzgxLDU4LjQ3MiBDMTQ1Ljg4Nyw1OC4zODkgMTQ0LjQ3OSw1Ny40MzQgMTQ0LjYzNiw1Ni4zNCBDMTQ0LjY4OSw1NS45NjcgMTQ0LjY2NCw1NS41OTcgMTQ0LjU2NCw1NS4yMzUgTDYzLjQ2MSwxMDIuMDU3IEM2NC4wODksMTAyLjExNSA2NC43MzMsMTAyLjEzIDY1LjM3OSwxMDIuMDk5IEM2NS41NjEsMTAyLjA5IDY1Ljc0MywxMDIuMDkgNjUuOTI1LDEwMi4wOTggQzY3LjgxOSwxMDIuMTgxIDY5LjIyNywxMDMuMTM2IDY5LjA3LDEwNC4yMyBMMTQ4LjMyNyw1OC40NzEiIGlkPSJGaWxsLTQiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNjkuMDcsMTA0LjM0NyBDNjkuMDQ4LDEwNC4zNDcgNjkuMDI1LDEwNC4zNCA2OS4wMDUsMTA0LjMyNyBDNjguOTY4LDEwNC4zMDEgNjguOTQ4LDEwNC4yNTcgNjguOTU1LDEwNC4yMTMgQzY5LDEwMy44OTYgNjguODk4LDEwMy41NzYgNjguNjU4LDEwMy4yODggQzY4LjE1MywxMDIuNjc4IDY3LjEwMywxMDIuMjY2IDY1LjkyLDEwMi4yMTQgQzY1Ljc0MiwxMDIuMjA2IDY1LjU2MywxMDIuMjA3IDY1LjM4NSwxMDIuMjE1IEM2NC43NDIsMTAyLjI0NiA2NC4wODcsMTAyLjIzMiA2My40NSwxMDIuMTc0IEM2My4zOTksMTAyLjE2OSA2My4zNTgsMTAyLjEzMiA2My4zNDcsMTAyLjA4MiBDNjMuMzM2LDEwMi4wMzMgNjMuMzU4LDEwMS45ODEgNjMuNDAyLDEwMS45NTYgTDE0NC41MDYsNTUuMTM0IEMxNDQuNTM3LDU1LjExNiAxNDQuNTc1LDU1LjExMyAxNDQuNjA5LDU1LjEyNyBDMTQ0LjY0Miw1NS4xNDEgMTQ0LjY2OCw1NS4xNyAxNDQuNjc3LDU1LjIwNCBDMTQ0Ljc4MSw1NS41ODUgMTQ0LjgwNiw1NS45NzIgMTQ0Ljc1MSw1Ni4zNTcgQzE0NC43MDYsNTYuNjczIDE0NC44MDgsNTYuOTk0IDE0NS4wNDcsNTcuMjgyIEMxNDUuNTUzLDU3Ljg5MiAxNDYuNjAyLDU4LjMwMyAxNDcuNzg2LDU4LjM1NSBDMTQ3Ljk2NCw1OC4zNjMgMTQ4LjE0Myw1OC4zNjMgMTQ4LjMyMSw1OC4zNTQgQzE0OC4zNzcsNTguMzUyIDE0OC40MjQsNTguMzg3IDE0OC40MzksNTguNDM4IEMxNDguNDU0LDU4LjQ5IDE0OC40MzIsNTguNTQ1IDE0OC4zODUsNTguNTcyIEw2OS4xMjksMTA0LjMzMSBDNjkuMTExLDEwNC4zNDIgNjkuMDksMTA0LjM0NyA2OS4wNywxMDQuMzQ3IEw2OS4wNywxMDQuMzQ3IFogTTY1LjY2NSwxMDEuOTc1IEM2NS43NTQsMTAxLjk3NSA2NS44NDIsMTAxLjk3NyA2NS45MywxMDEuOTgxIEM2Ny4xOTYsMTAyLjAzNyA2OC4yODMsMTAyLjQ2OSA2OC44MzgsMTAzLjEzOSBDNjkuMDY1LDEwMy40MTMgNjkuMTg4LDEwMy43MTQgNjkuMTk4LDEwNC4wMjEgTDE0Ny44ODMsNTguNTkyIEMxNDcuODQ3LDU4LjU5MiAxNDcuODExLDU4LjU5MSAxNDcuNzc2LDU4LjU4OSBDMTQ2LjUwOSw1OC41MzMgMTQ1LjQyMiw1OC4xIDE0NC44NjcsNTcuNDMxIEMxNDQuNTg1LDU3LjA5MSAxNDQuNDY1LDU2LjcwNyAxNDQuNTIsNTYuMzI0IEMxNDQuNTYzLDU2LjAyMSAxNDQuNTUyLDU1LjcxNiAxNDQuNDg4LDU1LjQxNCBMNjMuODQ2LDEwMS45NyBDNjQuMzUzLDEwMi4wMDIgNjQuODY3LDEwMi4wMDYgNjUuMzc0LDEwMS45ODIgQzY1LjQ3MSwxMDEuOTc3IDY1LjU2OCwxMDEuOTc1IDY1LjY2NSwxMDEuOTc1IEw2NS42NjUsMTAxLjk3NSBaIiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTIuMjA4LDU1LjEzNCBDMS4yMDcsNTMuMzA3IDEuOTY3LDUwLjkxNyAzLjkwNiw0OS43OTcgTDU5LjkxNywxNy40NTMgQzYxLjg1NiwxNi4zMzMgNjQuMjQxLDE2LjkwNyA2NS4yNDMsMTguNzM0IEw2NS40NzUsMTkuMTQ0IEM2NS44NzIsMTkuODgyIDY2LjM2OCwyMC41NiA2Ni45NDUsMjEuMTY1IEw2Ny4yMjMsMjEuNDM1IEM3MC41NDgsMjQuNjQ5IDc1LjgwNiwyNS4xNTEgODAuMTExLDIyLjY2NSBMODcuNDMsMTguNDQ1IEM4OS4zNywxNy4zMjYgOTEuNzU0LDE3Ljg5OSA5Mi43NTUsMTkuNzI3IEw5Ni4wMDUsMjUuNjU1IEwxMi40ODYsNzMuODg0IEwyLjIwOCw1NS4xMzQgWiIgaWQ9IkZpbGwtNiIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMi40ODYsNzQuMDAxIEMxMi40NzYsNzQuMDAxIDEyLjQ2NSw3My45OTkgMTIuNDU1LDczLjk5NiBDMTIuNDI0LDczLjk4OCAxMi4zOTksNzMuOTY3IDEyLjM4NCw3My45NCBMMi4xMDYsNTUuMTkgQzEuMDc1LDUzLjMxIDEuODU3LDUwLjg0NSAzLjg0OCw0OS42OTYgTDU5Ljg1OCwxNy4zNTIgQzYwLjUyNSwxNi45NjcgNjEuMjcxLDE2Ljc2NCA2Mi4wMTYsMTYuNzY0IEM2My40MzEsMTYuNzY0IDY0LjY2NiwxNy40NjYgNjUuMzI3LDE4LjY0NiBDNjUuMzM3LDE4LjY1NCA2NS4zNDUsMTguNjYzIDY1LjM1MSwxOC42NzQgTDY1LjU3OCwxOS4wODggQzY1LjU4NCwxOS4xIDY1LjU4OSwxOS4xMTIgNjUuNTkxLDE5LjEyNiBDNjUuOTg1LDE5LjgzOCA2Ni40NjksMjAuNDk3IDY3LjAzLDIxLjA4NSBMNjcuMzA1LDIxLjM1MSBDNjkuMTUxLDIzLjEzNyA3MS42NDksMjQuMTIgNzQuMzM2LDI0LjEyIEM3Ni4zMTMsMjQuMTIgNzguMjksMjMuNTgyIDgwLjA1MywyMi41NjMgQzgwLjA2NCwyMi41NTcgODAuMDc2LDIyLjU1MyA4MC4wODgsMjIuNTUgTDg3LjM3MiwxOC4zNDQgQzg4LjAzOCwxNy45NTkgODguNzg0LDE3Ljc1NiA4OS41MjksMTcuNzU2IEM5MC45NTYsMTcuNzU2IDkyLjIwMSwxOC40NzIgOTIuODU4LDE5LjY3IEw5Ni4xMDcsMjUuNTk5IEM5Ni4xMzgsMjUuNjU0IDk2LjExOCwyNS43MjQgOTYuMDYzLDI1Ljc1NiBMMTIuNTQ1LDczLjk4NSBDMTIuNTI2LDczLjk5NiAxMi41MDYsNzQuMDAxIDEyLjQ4Niw3NC4wMDEgTDEyLjQ4Niw3NC4wMDEgWiBNNjIuMDE2LDE2Ljk5NyBDNjEuMzEyLDE2Ljk5NyA2MC42MDYsMTcuMTkgNTkuOTc1LDE3LjU1NCBMMy45NjUsNDkuODk5IEMyLjA4Myw1MC45ODUgMS4zNDEsNTMuMzA4IDIuMzEsNTUuMDc4IEwxMi41MzEsNzMuNzIzIEw5NS44NDgsMjUuNjExIEw5Mi42NTMsMTkuNzgyIEM5Mi4wMzgsMTguNjYgOTAuODcsMTcuOTkgODkuNTI5LDE3Ljk5IEM4OC44MjUsMTcuOTkgODguMTE5LDE4LjE4MiA4Ny40ODksMTguNTQ3IEw4MC4xNzIsMjIuNzcyIEM4MC4xNjEsMjIuNzc4IDgwLjE0OSwyMi43ODIgODAuMTM3LDIyLjc4NSBDNzguMzQ2LDIzLjgxMSA3Ni4zNDEsMjQuMzU0IDc0LjMzNiwyNC4zNTQgQzcxLjU4OCwyNC4zNTQgNjkuMDMzLDIzLjM0NyA2Ny4xNDIsMjEuNTE5IEw2Ni44NjQsMjEuMjQ5IEM2Ni4yNzcsMjAuNjM0IDY1Ljc3NCwxOS45NDcgNjUuMzY3LDE5LjIwMyBDNjUuMzYsMTkuMTkyIDY1LjM1NiwxOS4xNzkgNjUuMzU0LDE5LjE2NiBMNjUuMTYzLDE4LjgxOSBDNjUuMTU0LDE4LjgxMSA2NS4xNDYsMTguODAxIDY1LjE0LDE4Ljc5IEM2NC41MjUsMTcuNjY3IDYzLjM1NywxNi45OTcgNjIuMDE2LDE2Ljk5NyBMNjIuMDE2LDE2Ljk5NyBaIiBpZD0iRmlsbC03IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTQyLjQzNCw0OC44MDggTDQyLjQzNCw0OC44MDggQzM5LjkyNCw0OC44MDcgMzcuNzM3LDQ3LjU1IDM2LjU4Miw0NS40NDMgQzM0Ljc3MSw0Mi4xMzkgMzYuMTQ0LDM3LjgwOSAzOS42NDEsMzUuNzg5IEw1MS45MzIsMjguNjkxIEM1My4xMDMsMjguMDE1IDU0LjQxMywyNy42NTggNTUuNzIxLDI3LjY1OCBDNTguMjMxLDI3LjY1OCA2MC40MTgsMjguOTE2IDYxLjU3MywzMS4wMjMgQzYzLjM4NCwzNC4zMjcgNjIuMDEyLDM4LjY1NyA1OC41MTQsNDAuNjc3IEw0Ni4yMjMsNDcuNzc1IEM0NS4wNTMsNDguNDUgNDMuNzQyLDQ4LjgwOCA0Mi40MzQsNDguODA4IEw0Mi40MzQsNDguODA4IFogTTU1LjcyMSwyOC4xMjUgQzU0LjQ5NSwyOC4xMjUgNTMuMjY1LDI4LjQ2MSA1Mi4xNjYsMjkuMDk2IEwzOS44NzUsMzYuMTk0IEMzNi41OTYsMzguMDg3IDM1LjMwMiw0Mi4xMzYgMzYuOTkyLDQ1LjIxOCBDMzguMDYzLDQ3LjE3MyA0MC4wOTgsNDguMzQgNDIuNDM0LDQ4LjM0IEM0My42NjEsNDguMzQgNDQuODksNDguMDA1IDQ1Ljk5LDQ3LjM3IEw1OC4yODEsNDAuMjcyIEM2MS41NiwzOC4zNzkgNjIuODUzLDM0LjMzIDYxLjE2NCwzMS4yNDggQzYwLjA5MiwyOS4yOTMgNTguMDU4LDI4LjEyNSA1NS43MjEsMjguMTI1IEw1NS43MjEsMjguMTI1IFoiIGlkPSJGaWxsLTgiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjU4OCwyLjQwNyBDMTQ5LjU4OCwyLjQwNyAxNTUuNzY4LDUuOTc1IDE1Ni4zMjUsNi4yOTcgTDE1Ni4zMjUsNy4xODQgQzE1Ni4zMjUsNy4zNiAxNTYuMzM4LDcuNTQ0IDE1Ni4zNjIsNy43MzMgQzE1Ni4zNzMsNy44MTQgMTU2LjM4Miw3Ljg5NCAxNTYuMzksNy45NzUgQzE1Ni41Myw5LjM5IDE1Ny4zNjMsMTAuOTczIDE1OC40OTUsMTEuOTc0IEwxNjUuODkxLDE4LjUxOSBDMTY2LjA2OCwxOC42NzUgMTY2LjI0OSwxOC44MTQgMTY2LjQzMiwxOC45MzQgQzE2OC4wMTEsMTkuOTc0IDE2OS4zODIsMTkuNCAxNjkuNDk0LDE3LjY1MiBDMTY5LjU0MywxNi44NjggMTY5LjU1MSwxNi4wNTcgMTY5LjUxNywxNS4yMjMgTDE2OS41MTQsMTUuMDYzIEwxNjkuNTE0LDEzLjkxMiBDMTcwLjc4LDE0LjY0MiAxOTUuNTAxLDI4LjkxNSAxOTUuNTAxLDI4LjkxNSBMMTk1LjUwMSw4Mi45MTUgQzE5NS41MDEsODQuMDA1IDE5NC43MzEsODQuNDQ1IDE5My43ODEsODMuODk3IEwxNTEuMzA4LDU5LjM3NCBDMTUwLjM1OCw1OC44MjYgMTQ5LjU4OCw1Ny40OTcgMTQ5LjU4OCw1Ni40MDggTDE0OS41ODgsMjIuMzc1IiBpZD0iRmlsbC05IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE5NC41NTMsODQuMjUgQzE5NC4yOTYsODQuMjUgMTk0LjAxMyw4NC4xNjUgMTkzLjcyMiw4My45OTcgTDE1MS4yNSw1OS40NzYgQzE1MC4yNjksNTguOTA5IDE0OS40NzEsNTcuNTMzIDE0OS40NzEsNTYuNDA4IEwxNDkuNDcxLDIyLjM3NSBMMTQ5LjcwNSwyMi4zNzUgTDE0OS43MDUsNTYuNDA4IEMxNDkuNzA1LDU3LjQ1OSAxNTAuNDUsNTguNzQ0IDE1MS4zNjYsNTkuMjc0IEwxOTMuODM5LDgzLjc5NSBDMTk0LjI2Myw4NC4wNCAxOTQuNjU1LDg0LjA4MyAxOTQuOTQyLDgzLjkxNyBDMTk1LjIyNyw4My43NTMgMTk1LjM4NCw4My4zOTcgMTk1LjM4NCw4Mi45MTUgTDE5NS4zODQsMjguOTgyIEMxOTQuMTAyLDI4LjI0MiAxNzIuMTA0LDE1LjU0MiAxNjkuNjMxLDE0LjExNCBMMTY5LjYzNCwxNS4yMiBDMTY5LjY2OCwxNi4wNTIgMTY5LjY2LDE2Ljg3NCAxNjkuNjEsMTcuNjU5IEMxNjkuNTU2LDE4LjUwMyAxNjkuMjE0LDE5LjEyMyAxNjguNjQ3LDE5LjQwNSBDMTY4LjAyOCwxOS43MTQgMTY3LjE5NywxOS41NzggMTY2LjM2NywxOS4wMzIgQzE2Ni4xODEsMTguOTA5IDE2NS45OTUsMTguNzY2IDE2NS44MTQsMTguNjA2IEwxNTguNDE3LDEyLjA2MiBDMTU3LjI1OSwxMS4wMzYgMTU2LjQxOCw5LjQzNyAxNTYuMjc0LDcuOTg2IEMxNTYuMjY2LDcuOTA3IDE1Ni4yNTcsNy44MjcgMTU2LjI0Nyw3Ljc0OCBDMTU2LjIyMSw3LjU1NSAxNTYuMjA5LDcuMzY1IDE1Ni4yMDksNy4xODQgTDE1Ni4yMDksNi4zNjQgQzE1NS4zNzUsNS44ODMgMTQ5LjUyOSwyLjUwOCAxNDkuNTI5LDIuNTA4IEwxNDkuNjQ2LDIuMzA2IEMxNDkuNjQ2LDIuMzA2IDE1NS44MjcsNS44NzQgMTU2LjM4NCw2LjE5NiBMMTU2LjQ0Miw2LjIzIEwxNTYuNDQyLDcuMTg0IEMxNTYuNDQyLDcuMzU1IDE1Ni40NTQsNy41MzUgMTU2LjQ3OCw3LjcxNyBDMTU2LjQ4OSw3LjggMTU2LjQ5OSw3Ljg4MiAxNTYuNTA3LDcuOTYzIEMxNTYuNjQ1LDkuMzU4IDE1Ny40NTUsMTAuODk4IDE1OC41NzIsMTEuODg2IEwxNjUuOTY5LDE4LjQzMSBDMTY2LjE0MiwxOC41ODQgMTY2LjMxOSwxOC43MiAxNjYuNDk2LDE4LjgzNyBDMTY3LjI1NCwxOS4zMzYgMTY4LDE5LjQ2NyAxNjguNTQzLDE5LjE5NiBDMTY5LjAzMywxOC45NTMgMTY5LjMyOSwxOC40MDEgMTY5LjM3NywxNy42NDUgQzE2OS40MjcsMTYuODY3IDE2OS40MzQsMTYuMDU0IDE2OS40MDEsMTUuMjI4IEwxNjkuMzk3LDE1LjA2NSBMMTY5LjM5NywxMy43MSBMMTY5LjU3MiwxMy44MSBDMTcwLjgzOSwxNC41NDEgMTk1LjU1OSwyOC44MTQgMTk1LjU1OSwyOC44MTQgTDE5NS42MTgsMjguODQ3IEwxOTUuNjE4LDgyLjkxNSBDMTk1LjYxOCw4My40ODQgMTk1LjQyLDgzLjkxMSAxOTUuMDU5LDg0LjExOSBDMTk0LjkwOCw4NC4yMDYgMTk0LjczNyw4NC4yNSAxOTQuNTUzLDg0LjI1IiBpZD0iRmlsbC0xMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDUuNjg1LDU2LjE2MSBMMTY5LjgsNzAuMDgzIEwxNDMuODIyLDg1LjA4MSBMMTQyLjM2LDg0Ljc3NCBDMTM1LjgyNiw4Mi42MDQgMTI4LjczMiw4MS4wNDYgMTIxLjM0MSw4MC4xNTggQzExNi45NzYsNzkuNjM0IDExMi42NzgsODEuMjU0IDExMS43NDMsODMuNzc4IEMxMTEuNTA2LDg0LjQxNCAxMTEuNTAzLDg1LjA3MSAxMTEuNzMyLDg1LjcwNiBDMTEzLjI3LDg5Ljk3MyAxMTUuOTY4LDk0LjA2OSAxMTkuNzI3LDk3Ljg0MSBMMTIwLjI1OSw5OC42ODYgQzEyMC4yNiw5OC42ODUgOTQuMjgyLDExMy42ODMgOTQuMjgyLDExMy42ODMgTDcwLjE2Nyw5OS43NjEgTDE0NS42ODUsNTYuMTYxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik05NC4yODIsMTEzLjgxOCBMOTQuMjIzLDExMy43ODUgTDY5LjkzMyw5OS43NjEgTDcwLjEwOCw5OS42NiBMMTQ1LjY4NSw1Ni4wMjYgTDE0NS43NDMsNTYuMDU5IEwxNzAuMDMzLDcwLjA4MyBMMTQzLjg0Miw4NS4yMDUgTDE0My43OTcsODUuMTk1IEMxNDMuNzcyLDg1LjE5IDE0Mi4zMzYsODQuODg4IDE0Mi4zMzYsODQuODg4IEMxMzUuNzg3LDgyLjcxNCAxMjguNzIzLDgxLjE2MyAxMjEuMzI3LDgwLjI3NCBDMTIwLjc4OCw4MC4yMDkgMTIwLjIzNiw4MC4xNzcgMTE5LjY4OSw4MC4xNzcgQzExNS45MzEsODAuMTc3IDExMi42MzUsODEuNzA4IDExMS44NTIsODMuODE5IEMxMTEuNjI0LDg0LjQzMiAxMTEuNjIxLDg1LjA1MyAxMTEuODQyLDg1LjY2NyBDMTEzLjM3Nyw4OS45MjUgMTE2LjA1OCw5My45OTMgMTE5LjgxLDk3Ljc1OCBMMTE5LjgyNiw5Ny43NzkgTDEyMC4zNTIsOTguNjE0IEMxMjAuMzU0LDk4LjYxNyAxMjAuMzU2LDk4LjYyIDEyMC4zNTgsOTguNjI0IEwxMjAuNDIyLDk4LjcyNiBMMTIwLjMxNyw5OC43ODcgQzEyMC4yNjQsOTguODE4IDk0LjU5OSwxMTMuNjM1IDk0LjM0LDExMy43ODUgTDk0LjI4MiwxMTMuODE4IEw5NC4yODIsMTEzLjgxOCBaIE03MC40MDEsOTkuNzYxIEw5NC4yODIsMTEzLjU0OSBMMTE5LjA4NCw5OS4yMjkgQzExOS42Myw5OC45MTQgMTE5LjkzLDk4Ljc0IDEyMC4xMDEsOTguNjU0IEwxMTkuNjM1LDk3LjkxNCBDMTE1Ljg2NCw5NC4xMjcgMTEzLjE2OCw5MC4wMzMgMTExLjYyMiw4NS43NDYgQzExMS4zODIsODUuMDc5IDExMS4zODYsODQuNDA0IDExMS42MzMsODMuNzM4IEMxMTIuNDQ4LDgxLjUzOSAxMTUuODM2LDc5Ljk0MyAxMTkuNjg5LDc5Ljk0MyBDMTIwLjI0Niw3OS45NDMgMTIwLjgwNiw3OS45NzYgMTIxLjM1NSw4MC4wNDIgQzEyOC43NjcsODAuOTMzIDEzNS44NDYsODIuNDg3IDE0Mi4zOTYsODQuNjYzIEMxNDMuMjMyLDg0LjgzOCAxNDMuNjExLDg0LjkxNyAxNDMuNzg2LDg0Ljk2NyBMMTY5LjU2Niw3MC4wODMgTDE0NS42ODUsNTYuMjk1IEw3MC40MDEsOTkuNzYxIEw3MC40MDEsOTkuNzYxIFoiIGlkPSJGaWxsLTEyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2Ny4yMywxOC45NzkgTDE2Ny4yMyw2OS44NSBMMTM5LjkwOSw4NS42MjMgTDEzMy40NDgsNzEuNDU2IEMxMzIuNTM4LDY5LjQ2IDEzMC4wMiw2OS43MTggMTI3LjgyNCw3Mi4wMyBDMTI2Ljc2OSw3My4xNCAxMjUuOTMxLDc0LjU4NSAxMjUuNDk0LDc2LjA0OCBMMTE5LjAzNCw5Ny42NzYgTDkxLjcxMiwxMTMuNDUgTDkxLjcxMiw2Mi41NzkgTDE2Ny4yMywxOC45NzkiIGlkPSJGaWxsLTEzIiBmaWxsPSIjRkZGRkZGIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTkxLjcxMiwxMTMuNTY3IEM5MS42OTIsMTEzLjU2NyA5MS42NzIsMTEzLjU2MSA5MS42NTMsMTEzLjU1MSBDOTEuNjE4LDExMy41MyA5MS41OTUsMTEzLjQ5MiA5MS41OTUsMTEzLjQ1IEw5MS41OTUsNjIuNTc5IEM5MS41OTUsNjIuNTM3IDkxLjYxOCw2Mi40OTkgOTEuNjUzLDYyLjQ3OCBMMTY3LjE3MiwxOC44NzggQzE2Ny4yMDgsMTguODU3IDE2Ny4yNTIsMTguODU3IDE2Ny4yODgsMTguODc4IEMxNjcuMzI0LDE4Ljg5OSAxNjcuMzQ3LDE4LjkzNyAxNjcuMzQ3LDE4Ljk3OSBMMTY3LjM0Nyw2OS44NSBDMTY3LjM0Nyw2OS44OTEgMTY3LjMyNCw2OS45MyAxNjcuMjg4LDY5Ljk1IEwxMzkuOTY3LDg1LjcyNSBDMTM5LjkzOSw4NS43NDEgMTM5LjkwNSw4NS43NDUgMTM5Ljg3Myw4NS43MzUgQzEzOS44NDIsODUuNzI1IDEzOS44MTYsODUuNzAyIDEzOS44MDIsODUuNjcyIEwxMzMuMzQyLDcxLjUwNCBDMTMyLjk2Nyw3MC42ODIgMTMyLjI4LDcwLjIyOSAxMzEuNDA4LDcwLjIyOSBDMTMwLjMxOSw3MC4yMjkgMTI5LjA0NCw3MC45MTUgMTI3LjkwOCw3Mi4xMSBDMTI2Ljg3NCw3My4yIDEyNi4wMzQsNzQuNjQ3IDEyNS42MDYsNzYuMDgyIEwxMTkuMTQ2LDk3LjcwOSBDMTE5LjEzNyw5Ny43MzggMTE5LjExOCw5Ny43NjIgMTE5LjA5Miw5Ny43NzcgTDkxLjc3LDExMy41NTEgQzkxLjc1MiwxMTMuNTYxIDkxLjczMiwxMTMuNTY3IDkxLjcxMiwxMTMuNTY3IEw5MS43MTIsMTEzLjU2NyBaIE05MS44MjksNjIuNjQ3IEw5MS44MjksMTEzLjI0OCBMMTE4LjkzNSw5Ny41OTggTDEyNS4zODIsNzYuMDE1IEMxMjUuODI3LDc0LjUyNSAxMjYuNjY0LDczLjA4MSAxMjcuNzM5LDcxLjk1IEMxMjguOTE5LDcwLjcwOCAxMzAuMjU2LDY5Ljk5NiAxMzEuNDA4LDY5Ljk5NiBDMTMyLjM3Nyw2OS45OTYgMTMzLjEzOSw3MC40OTcgMTMzLjU1NCw3MS40MDcgTDEzOS45NjEsODUuNDU4IEwxNjcuMTEzLDY5Ljc4MiBMMTY3LjExMywxOS4xODEgTDkxLjgyOSw2Mi42NDcgTDkxLjgyOSw2Mi42NDcgWiIgaWQ9IkZpbGwtMTQiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTY4LjU0MywxOS4yMTMgTDE2OC41NDMsNzAuMDgzIEwxNDEuMjIxLDg1Ljg1NyBMMTM0Ljc2MSw3MS42ODkgQzEzMy44NTEsNjkuNjk0IDEzMS4zMzMsNjkuOTUxIDEyOS4xMzcsNzIuMjYzIEMxMjguMDgyLDczLjM3NCAxMjcuMjQ0LDc0LjgxOSAxMjYuODA3LDc2LjI4MiBMMTIwLjM0Niw5Ny45MDkgTDkzLjAyNSwxMTMuNjgzIEw5My4wMjUsNjIuODEzIEwxNjguNTQzLDE5LjIxMyIgaWQ9IkZpbGwtMTUiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTMuMDI1LDExMy44IEM5My4wMDUsMTEzLjggOTIuOTg0LDExMy43OTUgOTIuOTY2LDExMy43ODUgQzkyLjkzMSwxMTMuNzY0IDkyLjkwOCwxMTMuNzI1IDkyLjkwOCwxMTMuNjg0IEw5Mi45MDgsNjIuODEzIEM5Mi45MDgsNjIuNzcxIDkyLjkzMSw2Mi43MzMgOTIuOTY2LDYyLjcxMiBMMTY4LjQ4NCwxOS4xMTIgQzE2OC41MiwxOS4wOSAxNjguNTY1LDE5LjA5IDE2OC42MDEsMTkuMTEyIEMxNjguNjM3LDE5LjEzMiAxNjguNjYsMTkuMTcxIDE2OC42NiwxOS4yMTIgTDE2OC42Niw3MC4wODMgQzE2OC42Niw3MC4xMjUgMTY4LjYzNyw3MC4xNjQgMTY4LjYwMSw3MC4xODQgTDE0MS4yOCw4NS45NTggQzE0MS4yNTEsODUuOTc1IDE0MS4yMTcsODUuOTc5IDE0MS4xODYsODUuOTY4IEMxNDEuMTU0LDg1Ljk1OCAxNDEuMTI5LDg1LjkzNiAxNDEuMTE1LDg1LjkwNiBMMTM0LjY1NSw3MS43MzggQzEzNC4yOCw3MC45MTUgMTMzLjU5Myw3MC40NjMgMTMyLjcyLDcwLjQ2MyBDMTMxLjYzMiw3MC40NjMgMTMwLjM1Nyw3MS4xNDggMTI5LjIyMSw3Mi4zNDQgQzEyOC4xODYsNzMuNDMzIDEyNy4zNDcsNzQuODgxIDEyNi45MTksNzYuMzE1IEwxMjAuNDU4LDk3Ljk0MyBDMTIwLjQ1LDk3Ljk3MiAxMjAuNDMxLDk3Ljk5NiAxMjAuNDA1LDk4LjAxIEw5My4wODMsMTEzLjc4NSBDOTMuMDY1LDExMy43OTUgOTMuMDQ1LDExMy44IDkzLjAyNSwxMTMuOCBMOTMuMDI1LDExMy44IFogTTkzLjE0Miw2Mi44ODEgTDkzLjE0MiwxMTMuNDgxIEwxMjAuMjQ4LDk3LjgzMiBMMTI2LjY5NSw3Ni4yNDggQzEyNy4xNCw3NC43NTggMTI3Ljk3Nyw3My4zMTUgMTI5LjA1Miw3Mi4xODMgQzEzMC4yMzEsNzAuOTQyIDEzMS41NjgsNzAuMjI5IDEzMi43Miw3MC4yMjkgQzEzMy42ODksNzAuMjI5IDEzNC40NTIsNzAuNzMxIDEzNC44NjcsNzEuNjQxIEwxNDEuMjc0LDg1LjY5MiBMMTY4LjQyNiw3MC4wMTYgTDE2OC40MjYsMTkuNDE1IEw5My4xNDIsNjIuODgxIEw5My4xNDIsNjIuODgxIFoiIGlkPSJGaWxsLTE2IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS44LDcwLjA4MyBMMTQyLjQ3OCw4NS44NTcgTDEzNi4wMTgsNzEuNjg5IEMxMzUuMTA4LDY5LjY5NCAxMzIuNTksNjkuOTUxIDEzMC4zOTMsNzIuMjYzIEMxMjkuMzM5LDczLjM3NCAxMjguNSw3NC44MTkgMTI4LjA2NCw3Ni4yODIgTDEyMS42MDMsOTcuOTA5IEw5NC4yODIsMTEzLjY4MyBMOTQuMjgyLDYyLjgxMyBMMTY5LjgsMTkuMjEzIEwxNjkuOCw3MC4wODMgWiIgaWQ9IkZpbGwtMTciIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTQuMjgyLDExMy45MTcgQzk0LjI0MSwxMTMuOTE3IDk0LjIwMSwxMTMuOTA3IDk0LjE2NSwxMTMuODg2IEM5NC4wOTMsMTEzLjg0NSA5NC4wNDgsMTEzLjc2NyA5NC4wNDgsMTEzLjY4NCBMOTQuMDQ4LDYyLjgxMyBDOTQuMDQ4LDYyLjczIDk0LjA5Myw2Mi42NTIgOTQuMTY1LDYyLjYxMSBMMTY5LjY4MywxOS4wMSBDMTY5Ljc1NSwxOC45NjkgMTY5Ljg0NCwxOC45NjkgMTY5LjkxNywxOS4wMSBDMTY5Ljk4OSwxOS4wNTIgMTcwLjAzMywxOS4xMjkgMTcwLjAzMywxOS4yMTIgTDE3MC4wMzMsNzAuMDgzIEMxNzAuMDMzLDcwLjE2NiAxNjkuOTg5LDcwLjI0NCAxNjkuOTE3LDcwLjI4NSBMMTQyLjU5NSw4Ni4wNiBDMTQyLjUzOCw4Ni4wOTIgMTQyLjQ2OSw4Ni4xIDE0Mi40MDcsODYuMDggQzE0Mi4zNDQsODYuMDYgMTQyLjI5Myw4Ni4wMTQgMTQyLjI2Niw4NS45NTQgTDEzNS44MDUsNzEuNzg2IEMxMzUuNDQ1LDcwLjk5NyAxMzQuODEzLDcwLjU4IDEzMy45NzcsNzAuNTggQzEzMi45MjEsNzAuNTggMTMxLjY3Niw3MS4yNTIgMTMwLjU2Miw3Mi40MjQgQzEyOS41NCw3My41MDEgMTI4LjcxMSw3NC45MzEgMTI4LjI4Nyw3Ni4zNDggTDEyMS44MjcsOTcuOTc2IEMxMjEuODEsOTguMDM0IDEyMS43NzEsOTguMDgyIDEyMS43Miw5OC4xMTIgTDk0LjM5OCwxMTMuODg2IEM5NC4zNjIsMTEzLjkwNyA5NC4zMjIsMTEzLjkxNyA5NC4yODIsMTEzLjkxNyBMOTQuMjgyLDExMy45MTcgWiBNOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDExMy4yNzkgTDEyMS40MDYsOTcuNzU0IEwxMjcuODQsNzYuMjE1IEMxMjguMjksNzQuNzA4IDEyOS4xMzcsNzMuMjQ3IDEzMC4yMjQsNzIuMTAzIEMxMzEuNDI1LDcwLjgzOCAxMzIuNzkzLDcwLjExMiAxMzMuOTc3LDcwLjExMiBDMTM0Ljk5NSw3MC4xMTIgMTM1Ljc5NSw3MC42MzggMTM2LjIzLDcxLjU5MiBMMTQyLjU4NCw4NS41MjYgTDE2OS41NjYsNjkuOTQ4IEwxNjkuNTY2LDE5LjYxNyBMOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDYyLjk0OCBaIiBpZD0iRmlsbC0xOCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMDkuODk0LDkyLjk0MyBMMTA5Ljg5NCw5Mi45NDMgQzEwOC4xMiw5Mi45NDMgMTA2LjY1Myw5Mi4yMTggMTA1LjY1LDkwLjgyMyBDMTA1LjU4Myw5MC43MzEgMTA1LjU5Myw5MC42MSAxMDUuNjczLDkwLjUyOSBDMTA1Ljc1Myw5MC40NDggMTA1Ljg4LDkwLjQ0IDEwNS45NzQsOTAuNTA2IEMxMDYuNzU0LDkxLjA1MyAxMDcuNjc5LDkxLjMzMyAxMDguNzI0LDkxLjMzMyBDMTEwLjA0Nyw5MS4zMzMgMTExLjQ3OCw5MC44OTQgMTEyLjk4LDkwLjAyNyBDMTE4LjI5MSw4Ni45NiAxMjIuNjExLDc5LjUwOSAxMjIuNjExLDczLjQxNiBDMTIyLjYxMSw3MS40ODkgMTIyLjE2OSw2OS44NTYgMTIxLjMzMyw2OC42OTIgQzEyMS4yNjYsNjguNiAxMjEuMjc2LDY4LjQ3MyAxMjEuMzU2LDY4LjM5MiBDMTIxLjQzNiw2OC4zMTEgMTIxLjU2Myw2OC4yOTkgMTIxLjY1Niw2OC4zNjUgQzEyMy4zMjcsNjkuNTM3IDEyNC4yNDcsNzEuNzQ2IDEyNC4yNDcsNzQuNTg0IEMxMjQuMjQ3LDgwLjgyNiAxMTkuODIxLDg4LjQ0NyAxMTQuMzgyLDkxLjU4NyBDMTEyLjgwOCw5Mi40OTUgMTExLjI5OCw5Mi45NDMgMTA5Ljg5NCw5Mi45NDMgTDEwOS44OTQsOTIuOTQzIFogTTEwNi45MjUsOTEuNDAxIEMxMDcuNzM4LDkyLjA1MiAxMDguNzQ1LDkyLjI3OCAxMDkuODkzLDkyLjI3OCBMMTA5Ljg5NCw5Mi4yNzggQzExMS4yMTUsOTIuMjc4IDExMi42NDcsOTEuOTUxIDExNC4xNDgsOTEuMDg0IEMxMTkuNDU5LDg4LjAxNyAxMjMuNzgsODAuNjIxIDEyMy43OCw3NC41MjggQzEyMy43OCw3Mi41NDkgMTIzLjMxNyw3MC45MjkgMTIyLjQ1NCw2OS43NjcgQzEyMi44NjUsNzAuODAyIDEyMy4wNzksNzIuMDQyIDEyMy4wNzksNzMuNDAyIEMxMjMuMDc5LDc5LjY0NSAxMTguNjUzLDg3LjI4NSAxMTMuMjE0LDkwLjQyNSBDMTExLjY0LDkxLjMzNCAxMTAuMTMsOTEuNzQyIDEwOC43MjQsOTEuNzQyIEMxMDguMDgzLDkxLjc0MiAxMDcuNDgxLDkxLjU5MyAxMDYuOTI1LDkxLjQwMSBMMTA2LjkyNSw5MS40MDEgWiIgaWQ9IkZpbGwtMTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjA5Nyw5MC4yMyBDMTE4LjQ4MSw4Ny4xMjIgMTIyLjg0NSw3OS41OTQgMTIyLjg0NSw3My40MTYgQzEyMi44NDUsNzEuMzY1IDEyMi4zNjIsNjkuNzI0IDEyMS41MjIsNjguNTU2IEMxMTkuNzM4LDY3LjMwNCAxMTcuMTQ4LDY3LjM2MiAxMTQuMjY1LDY5LjAyNiBDMTA4Ljg4MSw3Mi4xMzQgMTA0LjUxNyw3OS42NjIgMTA0LjUxNyw4NS44NCBDMTA0LjUxNyw4Ny44OTEgMTA1LDg5LjUzMiAxMDUuODQsOTAuNyBDMTA3LjYyNCw5MS45NTIgMTEwLjIxNCw5MS44OTQgMTEzLjA5Nyw5MC4yMyIgaWQ9IkZpbGwtMjAiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTA4LjcyNCw5MS42MTQgTDEwOC43MjQsOTEuNjE0IEMxMDcuNTgyLDkxLjYxNCAxMDYuNTY2LDkxLjQwMSAxMDUuNzA1LDkwLjc5NyBDMTA1LjY4NCw5MC43ODMgMTA1LjY2NSw5MC44MTEgMTA1LjY1LDkwLjc5IEMxMDQuNzU2LDg5LjU0NiAxMDQuMjgzLDg3Ljg0MiAxMDQuMjgzLDg1LjgxNyBDMTA0LjI4Myw3OS41NzUgMTA4LjcwOSw3MS45NTMgMTE0LjE0OCw2OC44MTIgQzExNS43MjIsNjcuOTA0IDExNy4yMzIsNjcuNDQ5IDExOC42MzgsNjcuNDQ5IEMxMTkuNzgsNjcuNDQ5IDEyMC43OTYsNjcuNzU4IDEyMS42NTYsNjguMzYyIEMxMjEuNjc4LDY4LjM3NyAxMjEuNjk3LDY4LjM5NyAxMjEuNzEyLDY4LjQxOCBDMTIyLjYwNiw2OS42NjIgMTIzLjA3OSw3MS4zOSAxMjMuMDc5LDczLjQxNSBDMTIzLjA3OSw3OS42NTggMTE4LjY1Myw4Ny4xOTggMTEzLjIxNCw5MC4zMzggQzExMS42NCw5MS4yNDcgMTEwLjEzLDkxLjYxNCAxMDguNzI0LDkxLjYxNCBMMTA4LjcyNCw5MS42MTQgWiBNMTA2LjAwNiw5MC41MDUgQzEwNi43OCw5MS4wMzcgMTA3LjY5NCw5MS4yODEgMTA4LjcyNCw5MS4yODEgQzExMC4wNDcsOTEuMjgxIDExMS40NzgsOTAuODY4IDExMi45OCw5MC4wMDEgQzExOC4yOTEsODYuOTM1IDEyMi42MTEsNzkuNDk2IDEyMi42MTEsNzMuNDAzIEMxMjIuNjExLDcxLjQ5NCAxMjIuMTc3LDY5Ljg4IDEyMS4zNTYsNjguNzE4IEMxMjAuNTgyLDY4LjE4NSAxMTkuNjY4LDY3LjkxOSAxMTguNjM4LDY3LjkxOSBDMTE3LjMxNSw2Ny45MTkgMTE1Ljg4Myw2OC4zNiAxMTQuMzgyLDY5LjIyNyBDMTA5LjA3MSw3Mi4yOTMgMTA0Ljc1MSw3OS43MzMgMTA0Ljc1MSw4NS44MjYgQzEwNC43NTEsODcuNzM1IDEwNS4xODUsODkuMzQzIDEwNi4wMDYsOTAuNTA1IEwxMDYuMDA2LDkwLjUwNSBaIiBpZD0iRmlsbC0yMSIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDkuMzE4LDcuMjYyIEwxMzkuMzM0LDE2LjE0IEwxNTUuMjI3LDI3LjE3MSBMMTYwLjgxNiwyMS4wNTkgTDE0OS4zMTgsNy4yNjIiIGlkPSJGaWxsLTIyIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS42NzYsMTMuODQgTDE1OS45MjgsMTkuNDY3IEMxNTYuMjg2LDIxLjU3IDE1MC40LDIxLjU4IDE0Ni43ODEsMTkuNDkxIEMxNDMuMTYxLDE3LjQwMiAxNDMuMTgsMTQuMDAzIDE0Ni44MjIsMTEuOSBMMTU2LjMxNyw2LjI5MiBMMTQ5LjU4OCwyLjQwNyBMNjcuNzUyLDQ5LjQ3OCBMMTEzLjY3NSw3NS45OTIgTDExNi43NTYsNzQuMjEzIEMxMTcuMzg3LDczLjg0OCAxMTcuNjI1LDczLjMxNSAxMTcuMzc0LDcyLjgyMyBDMTE1LjAxNyw2OC4xOTEgMTE0Ljc4MSw2My4yNzcgMTE2LjY5MSw1OC41NjEgQzEyMi4zMjksNDQuNjQxIDE0MS4yLDMzLjc0NiAxNjUuMzA5LDMwLjQ5MSBDMTczLjQ3OCwyOS4zODggMTgxLjk4OSwyOS41MjQgMTkwLjAxMywzMC44ODUgQzE5MC44NjUsMzEuMDMgMTkxLjc4OSwzMC44OTMgMTkyLjQyLDMwLjUyOCBMMTk1LjUwMSwyOC43NSBMMTY5LjY3NiwxMy44NCIgaWQ9IkZpbGwtMjMiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3Ni40NTkgQzExMy41OTQsNzYuNDU5IDExMy41MTQsNzYuNDM4IDExMy40NDIsNzYuMzk3IEw2Ny41MTgsNDkuODgyIEM2Ny4zNzQsNDkuNzk5IDY3LjI4NCw0OS42NDUgNjcuMjg1LDQ5LjQ3OCBDNjcuMjg1LDQ5LjMxMSA2Ny4zNzQsNDkuMTU3IDY3LjUxOSw0OS4wNzMgTDE0OS4zNTUsMi4wMDIgQzE0OS40OTksMS45MTkgMTQ5LjY3NywxLjkxOSAxNDkuODIxLDIuMDAyIEwxNTYuNTUsNS44ODcgQzE1Ni43NzQsNi4wMTcgMTU2Ljg1LDYuMzAyIDE1Ni43MjIsNi41MjYgQzE1Ni41OTIsNi43NDkgMTU2LjMwNyw2LjgyNiAxNTYuMDgzLDYuNjk2IEwxNDkuNTg3LDIuOTQ2IEw2OC42ODcsNDkuNDc5IEwxMTMuNjc1LDc1LjQ1MiBMMTE2LjUyMyw3My44MDggQzExNi43MTUsNzMuNjk3IDExNy4xNDMsNzMuMzk5IDExNi45NTgsNzMuMDM1IEMxMTQuNTQyLDY4LjI4NyAxMTQuMyw2My4yMjEgMTE2LjI1OCw1OC4zODUgQzExOS4wNjQsNTEuNDU4IDEyNS4xNDMsNDUuMTQzIDEzMy44NCw0MC4xMjIgQzE0Mi40OTcsMzUuMTI0IDE1My4zNTgsMzEuNjMzIDE2NS4yNDcsMzAuMDI4IEMxNzMuNDQ1LDI4LjkyMSAxODIuMDM3LDI5LjA1OCAxOTAuMDkxLDMwLjQyNSBDMTkwLjgzLDMwLjU1IDE5MS42NTIsMzAuNDMyIDE5Mi4xODYsMzAuMTI0IEwxOTQuNTY3LDI4Ljc1IEwxNjkuNDQyLDE0LjI0NCBDMTY5LjIxOSwxNC4xMTUgMTY5LjE0MiwxMy44MjkgMTY5LjI3MSwxMy42MDYgQzE2OS40LDEzLjM4MiAxNjkuNjg1LDEzLjMwNiAxNjkuOTA5LDEzLjQzNSBMMTk1LjczNCwyOC4zNDUgQzE5NS44NzksMjguNDI4IDE5NS45NjgsMjguNTgzIDE5NS45NjgsMjguNzUgQzE5NS45NjgsMjguOTE2IDE5NS44NzksMjkuMDcxIDE5NS43MzQsMjkuMTU0IEwxOTIuNjUzLDMwLjkzMyBDMTkxLjkzMiwzMS4zNSAxOTAuODksMzEuNTA4IDE4OS45MzUsMzEuMzQ2IEMxODEuOTcyLDI5Ljk5NSAxNzMuNDc4LDI5Ljg2IDE2NS4zNzIsMzAuOTU0IEMxNTMuNjAyLDMyLjU0MyAxNDIuODYsMzUuOTkzIDEzNC4zMDcsNDAuOTMxIEMxMjUuNzkzLDQ1Ljg0NyAxMTkuODUxLDUyLjAwNCAxMTcuMTI0LDU4LjczNiBDMTE1LjI3LDYzLjMxNCAxMTUuNTAxLDY4LjExMiAxMTcuNzksNzIuNjExIEMxMTguMTYsNzMuMzM2IDExNy44NDUsNzQuMTI0IDExNi45OSw3NC42MTcgTDExMy45MDksNzYuMzk3IEMxMTMuODM2LDc2LjQzOCAxMTMuNzU2LDc2LjQ1OSAxMTMuNjc1LDc2LjQ1OSIgaWQ9IkZpbGwtMjQiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUzLjMxNiwyMS4yNzkgQzE1MC45MDMsMjEuMjc5IDE0OC40OTUsMjAuNzUxIDE0Ni42NjQsMTkuNjkzIEMxNDQuODQ2LDE4LjY0NCAxNDMuODQ0LDE3LjIzMiAxNDMuODQ0LDE1LjcxOCBDMTQzLjg0NCwxNC4xOTEgMTQ0Ljg2LDEyLjc2MyAxNDYuNzA1LDExLjY5OCBMMTU2LjE5OCw2LjA5MSBDMTU2LjMwOSw2LjAyNSAxNTYuNDUyLDYuMDYyIDE1Ni41MTgsNi4xNzMgQzE1Ni41ODMsNi4yODQgMTU2LjU0Nyw2LjQyNyAxNTYuNDM2LDYuNDkzIEwxNDYuOTQsMTIuMTAyIEMxNDUuMjQ0LDEzLjA4MSAxNDQuMzEyLDE0LjM2NSAxNDQuMzEyLDE1LjcxOCBDMTQ0LjMxMiwxNy4wNTggMTQ1LjIzLDE4LjMyNiAxNDYuODk3LDE5LjI4OSBDMTUwLjQ0NiwyMS4zMzggMTU2LjI0LDIxLjMyNyAxNTkuODExLDE5LjI2NSBMMTY5LjU1OSwxMy42MzcgQzE2OS42NywxMy41NzMgMTY5LjgxMywxMy42MTEgMTY5Ljg3OCwxMy43MjMgQzE2OS45NDMsMTMuODM0IDE2OS45MDQsMTMuOTc3IDE2OS43OTMsMTQuMDQyIEwxNjAuMDQ1LDE5LjY3IEMxNTguMTg3LDIwLjc0MiAxNTUuNzQ5LDIxLjI3OSAxNTMuMzE2LDIxLjI3OSIgaWQ9IkZpbGwtMjUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3NS45OTIgTDY3Ljc2Miw0OS40ODQiIGlkPSJGaWxsLTI2IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMy42NzUsNzYuMzQyIEMxMTMuNjE1LDc2LjM0MiAxMTMuNTU1LDc2LjMyNyAxMTMuNSw3Ni4yOTUgTDY3LjU4Nyw0OS43ODcgQzY3LjQxOSw0OS42OSA2Ny4zNjIsNDkuNDc2IDY3LjQ1OSw0OS4zMDkgQzY3LjU1Niw0OS4xNDEgNjcuNzcsNDkuMDgzIDY3LjkzNyw0OS4xOCBMMTEzLjg1LDc1LjY4OCBDMTE0LjAxOCw3NS43ODUgMTE0LjA3NSw3NiAxMTMuOTc4LDc2LjE2NyBDMTEzLjkxNCw3Ni4yNzkgMTEzLjc5Niw3Ni4zNDIgMTEzLjY3NSw3Ni4zNDIiIGlkPSJGaWxsLTI3IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY3Ljc2Miw0OS40ODQgTDY3Ljc2MiwxMDMuNDg1IEM2Ny43NjIsMTA0LjU3NSA2OC41MzIsMTA1LjkwMyA2OS40ODIsMTA2LjQ1MiBMMTExLjk1NSwxMzAuOTczIEMxMTIuOTA1LDEzMS41MjIgMTEzLjY3NSwxMzEuMDgzIDExMy42NzUsMTI5Ljk5MyBMMTEzLjY3NSw3NS45OTIiIGlkPSJGaWxsLTI4IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMi43MjcsMTMxLjU2MSBDMTEyLjQzLDEzMS41NjEgMTEyLjEwNywxMzEuNDY2IDExMS43OCwxMzEuMjc2IEw2OS4zMDcsMTA2Ljc1NSBDNjguMjQ0LDEwNi4xNDIgNjcuNDEyLDEwNC43MDUgNjcuNDEyLDEwMy40ODUgTDY3LjQxMiw0OS40ODQgQzY3LjQxMiw0OS4yOSA2Ny41NjksNDkuMTM0IDY3Ljc2Miw0OS4xMzQgQzY3Ljk1Niw0OS4xMzQgNjguMTEzLDQ5LjI5IDY4LjExMyw0OS40ODQgTDY4LjExMywxMDMuNDg1IEM2OC4xMTMsMTA0LjQ0NSA2OC44MiwxMDUuNjY1IDY5LjY1NywxMDYuMTQ4IEwxMTIuMTMsMTMwLjY3IEMxMTIuNDc0LDEzMC44NjggMTEyLjc5MSwxMzAuOTEzIDExMywxMzAuNzkyIEMxMTMuMjA2LDEzMC42NzMgMTEzLjMyNSwxMzAuMzgxIDExMy4zMjUsMTI5Ljk5MyBMMTEzLjMyNSw3NS45OTIgQzExMy4zMjUsNzUuNzk4IDExMy40ODIsNzUuNjQxIDExMy42NzUsNzUuNjQxIEMxMTMuODY5LDc1LjY0MSAxMTQuMDI1LDc1Ljc5OCAxMTQuMDI1LDc1Ljk5MiBMMTE0LjAyNSwxMjkuOTkzIEMxMTQuMDI1LDEzMC42NDggMTEzLjc4NiwxMzEuMTQ3IDExMy4zNSwxMzEuMzk5IEMxMTMuMTYyLDEzMS41MDcgMTEyLjk1MiwxMzEuNTYxIDExMi43MjcsMTMxLjU2MSIgaWQ9IkZpbGwtMjkiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEyLjg2LDQwLjUxMiBDMTEyLjg2LDQwLjUxMiAxMTIuODYsNDAuNTEyIDExMi44NTksNDAuNTEyIEMxMTAuNTQxLDQwLjUxMiAxMDguMzYsMzkuOTkgMTA2LjcxNywzOS4wNDEgQzEwNS4wMTIsMzguMDU3IDEwNC4wNzQsMzYuNzI2IDEwNC4wNzQsMzUuMjkyIEMxMDQuMDc0LDMzLjg0NyAxMDUuMDI2LDMyLjUwMSAxMDYuNzU0LDMxLjUwNCBMMTE4Ljc5NSwyNC41NTEgQzEyMC40NjMsMjMuNTg5IDEyMi42NjksMjMuMDU4IDEyNS4wMDcsMjMuMDU4IEMxMjcuMzI1LDIzLjA1OCAxMjkuNTA2LDIzLjU4MSAxMzEuMTUsMjQuNTMgQzEzMi44NTQsMjUuNTE0IDEzMy43OTMsMjYuODQ1IDEzMy43OTMsMjguMjc4IEMxMzMuNzkzLDI5LjcyNCAxMzIuODQxLDMxLjA2OSAxMzEuMTEzLDMyLjA2NyBMMTE5LjA3MSwzOS4wMTkgQzExNy40MDMsMzkuOTgyIDExNS4xOTcsNDAuNTEyIDExMi44Niw0MC41MTIgTDExMi44Niw0MC41MTIgWiBNMTI1LjAwNywyMy43NTkgQzEyMi43OSwyMy43NTkgMTIwLjcwOSwyNC4yNTYgMTE5LjE0NiwyNS4xNTggTDEwNy4xMDQsMzIuMTEgQzEwNS42MDIsMzIuOTc4IDEwNC43NzQsMzQuMTA4IDEwNC43NzQsMzUuMjkyIEMxMDQuNzc0LDM2LjQ2NSAxMDUuNTg5LDM3LjU4MSAxMDcuMDY3LDM4LjQzNCBDMTA4LjYwNSwzOS4zMjMgMTEwLjY2MywzOS44MTIgMTEyLjg1OSwzOS44MTIgTDExMi44NiwzOS44MTIgQzExNS4wNzYsMzkuODEyIDExNy4xNTgsMzkuMzE1IDExOC43MjEsMzguNDEzIEwxMzAuNzYyLDMxLjQ2IEMxMzIuMjY0LDMwLjU5MyAxMzMuMDkyLDI5LjQ2MyAxMzMuMDkyLDI4LjI3OCBDMTMzLjA5MiwyNy4xMDYgMTMyLjI3OCwyNS45OSAxMzAuOCwyNS4xMzYgQzEyOS4yNjEsMjQuMjQ4IDEyNy4yMDQsMjMuNzU5IDEyNS4wMDcsMjMuNzU5IEwxMjUuMDA3LDIzLjc1OSBaIiBpZD0iRmlsbC0zMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNjUuNjMsMTYuMjE5IEwxNTkuODk2LDE5LjUzIEMxNTYuNzI5LDIxLjM1OCAxNTEuNjEsMjEuMzY3IDE0OC40NjMsMTkuNTUgQzE0NS4zMTYsMTcuNzMzIDE0NS4zMzIsMTQuNzc4IDE0OC40OTksMTIuOTQ5IEwxNTQuMjMzLDkuNjM5IEwxNjUuNjMsMTYuMjE5IiBpZD0iRmlsbC0zMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNTQuMjMzLDEwLjQ0OCBMMTY0LjIyOCwxNi4yMTkgTDE1OS41NDYsMTguOTIzIEMxNTguMTEyLDE5Ljc1IDE1Ni4xOTQsMjAuMjA2IDE1NC4xNDcsMjAuMjA2IEMxNTIuMTE4LDIwLjIwNiAxNTAuMjI0LDE5Ljc1NyAxNDguODE0LDE4Ljk0MyBDMTQ3LjUyNCwxOC4xOTkgMTQ2LjgxNCwxNy4yNDkgMTQ2LjgxNCwxNi4yNjkgQzE0Ni44MTQsMTUuMjc4IDE0Ny41MzcsMTQuMzE0IDE0OC44NSwxMy41NTYgTDE1NC4yMzMsMTAuNDQ4IE0xNTQuMjMzLDkuNjM5IEwxNDguNDk5LDEyLjk0OSBDMTQ1LjMzMiwxNC43NzggMTQ1LjMxNiwxNy43MzMgMTQ4LjQ2MywxOS41NSBDMTUwLjAzMSwyMC40NTUgMTUyLjA4NiwyMC45MDcgMTU0LjE0NywyMC45MDcgQzE1Ni4yMjQsMjAuOTA3IDE1OC4zMDYsMjAuNDQ3IDE1OS44OTYsMTkuNTMgTDE2NS42MywxNi4yMTkgTDE1NC4yMzMsOS42MzkiIGlkPSJGaWxsLTMyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NS40NDUsNzIuNjY3IEwxNDUuNDQ1LDcyLjY2NyBDMTQzLjY3Miw3Mi42NjcgMTQyLjIwNCw3MS44MTcgMTQxLjIwMiw3MC40MjIgQzE0MS4xMzUsNzAuMzMgMTQxLjE0NSw3MC4xNDcgMTQxLjIyNSw3MC4wNjYgQzE0MS4zMDUsNjkuOTg1IDE0MS40MzIsNjkuOTQ2IDE0MS41MjUsNzAuMDExIEMxNDIuMzA2LDcwLjU1OSAxNDMuMjMxLDcwLjgyMyAxNDQuMjc2LDcwLjgyMiBDMTQ1LjU5OCw3MC44MjIgMTQ3LjAzLDcwLjM3NiAxNDguNTMyLDY5LjUwOSBDMTUzLjg0Miw2Ni40NDMgMTU4LjE2Myw1OC45ODcgMTU4LjE2Myw1Mi44OTQgQzE1OC4xNjMsNTAuOTY3IDE1Ny43MjEsNDkuMzMyIDE1Ni44ODQsNDguMTY4IEMxNTYuODE4LDQ4LjA3NiAxNTYuODI4LDQ3Ljk0OCAxNTYuOTA4LDQ3Ljg2NyBDMTU2Ljk4OCw0Ny43ODYgMTU3LjExNCw0Ny43NzQgMTU3LjIwOCw0Ny44NCBDMTU4Ljg3OCw0OS4wMTIgMTU5Ljc5OCw1MS4yMiAxNTkuNzk4LDU0LjA1OSBDMTU5Ljc5OCw2MC4zMDEgMTU1LjM3Myw2OC4wNDYgMTQ5LjkzMyw3MS4xODYgQzE0OC4zNiw3Mi4wOTQgMTQ2Ljg1LDcyLjY2NyAxNDUuNDQ1LDcyLjY2NyBMMTQ1LjQ0NSw3Mi42NjcgWiBNMTQyLjQ3Niw3MSBDMTQzLjI5LDcxLjY1MSAxNDQuMjk2LDcyLjAwMiAxNDUuNDQ1LDcyLjAwMiBDMTQ2Ljc2Nyw3Mi4wMDIgMTQ4LjE5OCw3MS41NSAxNDkuNyw3MC42ODIgQzE1NS4wMSw2Ny42MTcgMTU5LjMzMSw2MC4xNTkgMTU5LjMzMSw1NC4wNjUgQzE1OS4zMzEsNTIuMDg1IDE1OC44NjgsNTAuNDM1IDE1OC4wMDYsNDkuMjcyIEMxNTguNDE3LDUwLjMwNyAxNTguNjMsNTEuNTMyIDE1OC42Myw1Mi44OTIgQzE1OC42Myw1OS4xMzQgMTU0LjIwNSw2Ni43NjcgMTQ4Ljc2NSw2OS45MDcgQzE0Ny4xOTIsNzAuODE2IDE0NS42ODEsNzEuMjgzIDE0NC4yNzYsNzEuMjgzIEMxNDMuNjM0LDcxLjI4MyAxNDMuMDMzLDcxLjE5MiAxNDIuNDc2LDcxIEwxNDIuNDc2LDcxIFoiIGlkPSJGaWxsLTMzIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0OC42NDgsNjkuNzA0IEMxNTQuMDMyLDY2LjU5NiAxNTguMzk2LDU5LjA2OCAxNTguMzk2LDUyLjg5MSBDMTU4LjM5Niw1MC44MzkgMTU3LjkxMyw0OS4xOTggMTU3LjA3NCw0OC4wMyBDMTU1LjI4OSw0Ni43NzggMTUyLjY5OSw0Ni44MzYgMTQ5LjgxNiw0OC41MDEgQzE0NC40MzMsNTEuNjA5IDE0MC4wNjgsNTkuMTM3IDE0MC4wNjgsNjUuMzE0IEMxNDAuMDY4LDY3LjM2NSAxNDAuNTUyLDY5LjAwNiAxNDEuMzkxLDcwLjE3NCBDMTQzLjE3Niw3MS40MjcgMTQ1Ljc2NSw3MS4zNjkgMTQ4LjY0OCw2OS43MDQiIGlkPSJGaWxsLTM0IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NC4yNzYsNzEuMjc2IEwxNDQuMjc2LDcxLjI3NiBDMTQzLjEzMyw3MS4yNzYgMTQyLjExOCw3MC45NjkgMTQxLjI1Nyw3MC4zNjUgQzE0MS4yMzYsNzAuMzUxIDE0MS4yMTcsNzAuMzMyIDE0MS4yMDIsNzAuMzExIEMxNDAuMzA3LDY5LjA2NyAxMzkuODM1LDY3LjMzOSAxMzkuODM1LDY1LjMxNCBDMTM5LjgzNSw1OS4wNzMgMTQ0LjI2LDUxLjQzOSAxNDkuNyw0OC4yOTggQzE1MS4yNzMsNDcuMzkgMTUyLjc4NCw0Ni45MjkgMTU0LjE4OSw0Ni45MjkgQzE1NS4zMzIsNDYuOTI5IDE1Ni4zNDcsNDcuMjM2IDE1Ny4yMDgsNDcuODM5IEMxNTcuMjI5LDQ3Ljg1NCAxNTcuMjQ4LDQ3Ljg3MyAxNTcuMjYzLDQ3Ljg5NCBDMTU4LjE1Nyw0OS4xMzggMTU4LjYzLDUwLjg2NSAxNTguNjMsNTIuODkxIEMxNTguNjMsNTkuMTMyIDE1NC4yMDUsNjYuNzY2IDE0OC43NjUsNjkuOTA3IEMxNDcuMTkyLDcwLjgxNSAxNDUuNjgxLDcxLjI3NiAxNDQuMjc2LDcxLjI3NiBMMTQ0LjI3Niw3MS4yNzYgWiBNMTQxLjU1OCw3MC4xMDQgQzE0Mi4zMzEsNzAuNjM3IDE0My4yNDUsNzEuMDA1IDE0NC4yNzYsNzEuMDA1IEMxNDUuNTk4LDcxLjAwNSAxNDcuMDMsNzAuNDY3IDE0OC41MzIsNjkuNiBDMTUzLjg0Miw2Ni41MzQgMTU4LjE2Myw1OS4wMzMgMTU4LjE2Myw1Mi45MzkgQzE1OC4xNjMsNTEuMDMxIDE1Ny43MjksNDkuMzg1IDE1Ni45MDcsNDguMjIzIEMxNTYuMTMzLDQ3LjY5MSAxNTUuMjE5LDQ3LjQwOSAxNTQuMTg5LDQ3LjQwOSBDMTUyLjg2Nyw0Ny40MDkgMTUxLjQzNSw0Ny44NDIgMTQ5LjkzMyw0OC43MDkgQzE0NC42MjMsNTEuNzc1IDE0MC4zMDIsNTkuMjczIDE0MC4zMDIsNjUuMzY2IEMxNDAuMzAyLDY3LjI3NiAxNDAuNzM2LDY4Ljk0MiAxNDEuNTU4LDcwLjEwNCBMMTQxLjU1OCw3MC4xMDQgWiIgaWQ9IkZpbGwtMzUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUwLjcyLDY1LjM2MSBMMTUwLjM1Nyw2NS4wNjYgQzE1MS4xNDcsNjQuMDkyIDE1MS44NjksNjMuMDQgMTUyLjUwNSw2MS45MzggQzE1My4zMTMsNjAuNTM5IDE1My45NzgsNTkuMDY3IDE1NC40ODIsNTcuNTYzIEwxNTQuOTI1LDU3LjcxMiBDMTU0LjQxMiw1OS4yNDUgMTUzLjczMyw2MC43NDUgMTUyLjkxLDYyLjE3MiBDMTUyLjI2Miw2My4yOTUgMTUxLjUyNSw2NC4zNjggMTUwLjcyLDY1LjM2MSIgaWQ9IkZpbGwtMzYiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE1LjkxNyw4NC41MTQgTDExNS41NTQsODQuMjIgQzExNi4zNDQsODMuMjQ1IDExNy4wNjYsODIuMTk0IDExNy43MDIsODEuMDkyIEMxMTguNTEsNzkuNjkyIDExOS4xNzUsNzguMjIgMTE5LjY3OCw3Ni43MTcgTDEyMC4xMjEsNzYuODY1IEMxMTkuNjA4LDc4LjM5OCAxMTguOTMsNzkuODk5IDExOC4xMDYsODEuMzI2IEMxMTcuNDU4LDgyLjQ0OCAxMTYuNzIyLDgzLjUyMSAxMTUuOTE3LDg0LjUxNCIgaWQ9IkZpbGwtMzciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE0LDEzMC40NzYgTDExNCwxMzAuMDA4IEwxMTQsNzYuMDUyIEwxMTQsNzUuNTg0IEwxMTQsNzYuMDUyIEwxMTQsMTMwLjAwOCBMMTE0LDEzMC40NzYiIGlkPSJGaWxsLTM4IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYyLjAwMDAwMCwgMC4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTkuODIyLDM3LjQ3NCBDMTkuODM5LDM3LjMzOSAxOS43NDcsMzcuMTk0IDE5LjU1NSwzNy4wODIgQzE5LjIyOCwzNi44OTQgMTguNzI5LDM2Ljg3MiAxOC40NDYsMzcuMDM3IEwxMi40MzQsNDAuNTA4IEMxMi4zMDMsNDAuNTg0IDEyLjI0LDQwLjY4NiAxMi4yNDMsNDAuNzkzIEMxMi4yNDUsNDAuOTI1IDEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQxLjM3MSBMMTIuMjQ1LDQxLjQxNCBMMTIuMjM4LDQxLjU0MiBDOC4xNDgsNDMuODg3IDUuNjQ3LDQ1LjMyMSA1LjY0Nyw0NS4zMjEgQzUuNjQ2LDQ1LjMyMSAzLjU3LDQ2LjM2NyAyLjg2LDUwLjUxMyBDMi44Niw1MC41MTMgMS45NDgsNTcuNDc0IDEuOTYyLDcwLjI1OCBDMS45NzcsODIuODI4IDIuNTY4LDg3LjMyOCAzLjEyOSw5MS42MDkgQzMuMzQ5LDkzLjI5MyA2LjEzLDkzLjczNCA2LjEzLDkzLjczNCBDNi40NjEsOTMuNzc0IDYuODI4LDkzLjcwNyA3LjIxLDkzLjQ4NiBMODIuNDgzLDQ5LjkzNSBDODQuMjkxLDQ4Ljg2NiA4NS4xNSw0Ni4yMTYgODUuNTM5LDQzLjY1MSBDODYuNzUyLDM1LjY2MSA4Ny4yMTQsMTAuNjczIDg1LjI2NCwzLjc3MyBDODUuMDY4LDMuMDggODQuNzU0LDIuNjkgODQuMzk2LDIuNDkxIEw4Mi4zMSwxLjcwMSBDODEuNTgzLDEuNzI5IDgwLjg5NCwyLjE2OCA4MC43NzYsMi4yMzYgQzgwLjYzNiwyLjMxNyA0MS44MDcsMjQuNTg1IDIwLjAzMiwzNy4wNzIgTDE5LjgyMiwzNy40NzQiIGlkPSJGaWxsLTEiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNODIuMzExLDEuNzAxIEw4NC4zOTYsMi40OTEgQzg0Ljc1NCwyLjY5IDg1LjA2OCwzLjA4IDg1LjI2NCwzLjc3MyBDODcuMjEzLDEwLjY3MyA4Ni43NTEsMzUuNjYgODUuNTM5LDQzLjY1MSBDODUuMTQ5LDQ2LjIxNiA4NC4yOSw0OC44NjYgODIuNDgzLDQ5LjkzNSBMNy4yMSw5My40ODYgQzYuODk3LDkzLjY2NyA2LjU5NSw5My43NDQgNi4zMTQsOTMuNzQ0IEw2LjEzMSw5My43MzMgQzYuMTMxLDkzLjczNCAzLjM0OSw5My4yOTMgMy4xMjgsOTEuNjA5IEMyLjU2OCw4Ny4zMjcgMS45NzcsODIuODI4IDEuOTYzLDcwLjI1OCBDMS45NDgsNTcuNDc0IDIuODYsNTAuNTEzIDIuODYsNTAuNTEzIEMzLjU3LDQ2LjM2NyA1LjY0Nyw0NS4zMjEgNS42NDcsNDUuMzIxIEM1LjY0Nyw0NS4zMjEgOC4xNDgsNDMuODg3IDEyLjIzOCw0MS41NDIgTDEyLjI0NSw0MS40MTQgTDEyLjI0NSw0MS4zNzEgQzEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQwLjkyNSAxMi4yNDMsNDAuNzkzIEMxMi4yNCw0MC42ODYgMTIuMzAyLDQwLjU4MyAxMi40MzQsNDAuNTA4IEwxOC40NDYsMzcuMDM2IEMxOC41NzQsMzYuOTYyIDE4Ljc0NiwzNi45MjYgMTguOTI3LDM2LjkyNiBDMTkuMTQ1LDM2LjkyNiAxOS4zNzYsMzYuOTc5IDE5LjU1NCwzNy4wODIgQzE5Ljc0NywzNy4xOTQgMTkuODM5LDM3LjM0IDE5LjgyMiwzNy40NzQgTDIwLjAzMywzNy4wNzIgQzQxLjgwNiwyNC41ODUgODAuNjM2LDIuMzE4IDgwLjc3NywyLjIzNiBDODAuODk0LDIuMTY4IDgxLjU4MywxLjcyOSA4Mi4zMTEsMS43MDEgTTgyLjMxMSwwLjcwNCBMODIuMjcyLDAuNzA1IEM4MS42NTQsMC43MjggODAuOTg5LDAuOTQ5IDgwLjI5OCwxLjM2MSBMODAuMjc3LDEuMzczIEM4MC4xMjksMS40NTggNTkuNzY4LDEzLjEzNSAxOS43NTgsMzYuMDc5IEMxOS41LDM1Ljk4MSAxOS4yMTQsMzUuOTI5IDE4LjkyNywzNS45MjkgQzE4LjU2MiwzNS45MjkgMTguMjIzLDM2LjAxMyAxNy45NDcsMzYuMTczIEwxMS45MzUsMzkuNjQ0IEMxMS40OTMsMzkuODk5IDExLjIzNiw0MC4zMzQgMTEuMjQ2LDQwLjgxIEwxMS4yNDcsNDAuOTYgTDUuMTY3LDQ0LjQ0NyBDNC43OTQsNDQuNjQ2IDIuNjI1LDQ1Ljk3OCAxLjg3Nyw1MC4zNDUgTDEuODcxLDUwLjM4NCBDMS44NjIsNTAuNDU0IDAuOTUxLDU3LjU1NyAwLjk2NSw3MC4yNTkgQzAuOTc5LDgyLjg3OSAxLjU2OCw4Ny4zNzUgMi4xMzcsOTEuNzI0IEwyLjEzOSw5MS43MzkgQzIuNDQ3LDk0LjA5NCA1LjYxNCw5NC42NjIgNS45NzUsOTQuNzE5IEw2LjAwOSw5NC43MjMgQzYuMTEsOTQuNzM2IDYuMjEzLDk0Ljc0MiA2LjMxNCw5NC43NDIgQzYuNzksOTQuNzQyIDcuMjYsOTQuNjEgNy43MSw5NC4zNSBMODIuOTgzLDUwLjc5OCBDODQuNzk0LDQ5LjcyNyA4NS45ODIsNDcuMzc1IDg2LjUyNSw0My44MDEgQzg3LjcxMSwzNS45ODcgODguMjU5LDEwLjcwNSA4Ni4yMjQsMy41MDIgQzg1Ljk3MSwyLjYwOSA4NS41MiwxLjk3NSA4NC44ODEsMS42MiBMODQuNzQ5LDEuNTU4IEw4Mi42NjQsMC43NjkgQzgyLjU1MSwwLjcyNSA4Mi40MzEsMC43MDQgODIuMzExLDAuNzA0IiBpZD0iRmlsbC0yIiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY2LjI2NywxMS41NjUgTDY3Ljc2MiwxMS45OTkgTDExLjQyMyw0NC4zMjUiIGlkPSJGaWxsLTMiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMjAyLDkwLjU0NSBDMTIuMDI5LDkwLjU0NSAxMS44NjIsOTAuNDU1IDExLjc2OSw5MC4yOTUgQzExLjYzMiw5MC4wNTcgMTEuNzEzLDg5Ljc1MiAxMS45NTIsODkuNjE0IEwzMC4zODksNzguOTY5IEMzMC42MjgsNzguODMxIDMwLjkzMyw3OC45MTMgMzEuMDcxLDc5LjE1MiBDMzEuMjA4LDc5LjM5IDMxLjEyNyw3OS42OTYgMzAuODg4LDc5LjgzMyBMMTIuNDUxLDkwLjQ3OCBMMTIuMjAyLDkwLjU0NSIgaWQ9IkZpbGwtNCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMy43NjQsNDIuNjU0IEwxMy42NTYsNDIuNTkyIEwxMy43MDIsNDIuNDIxIEwxOC44MzcsMzkuNDU3IEwxOS4wMDcsMzkuNTAyIEwxOC45NjIsMzkuNjczIEwxMy44MjcsNDIuNjM3IEwxMy43NjQsNDIuNjU0IiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTguNTIsOTAuMzc1IEw4LjUyLDQ2LjQyMSBMOC41ODMsNDYuMzg1IEw3NS44NCw3LjU1NCBMNzUuODQsNTEuNTA4IEw3NS43NzgsNTEuNTQ0IEw4LjUyLDkwLjM3NSBMOC41Miw5MC4zNzUgWiBNOC43Nyw0Ni41NjQgTDguNzcsODkuOTQ0IEw3NS41OTEsNTEuMzY1IEw3NS41OTEsNy45ODUgTDguNzcsNDYuNTY0IEw4Ljc3LDQ2LjU2NCBaIiBpZD0iRmlsbC02IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTI0Ljk4Niw4My4xODIgQzI0Ljc1Niw4My4zMzEgMjQuMzc0LDgzLjU2NiAyNC4xMzcsODMuNzA1IEwxMi42MzIsOTAuNDA2IEMxMi4zOTUsOTAuNTQ1IDEyLjQyNiw5MC42NTggMTIuNyw5MC42NTggTDEzLjI2NSw5MC42NTggQzEzLjU0LDkwLjY1OCAxMy45NTgsOTAuNTQ1IDE0LjE5NSw5MC40MDYgTDI1LjcsODMuNzA1IEMyNS45MzcsODMuNTY2IDI2LjEyOCw4My40NTIgMjYuMTI1LDgzLjQ0OSBDMjYuMTIyLDgzLjQ0NyAyNi4xMTksODMuMjIgMjYuMTE5LDgyLjk0NiBDMjYuMTE5LDgyLjY3MiAyNS45MzEsODIuNTY5IDI1LjcwMSw4Mi43MTkgTDI0Ljk4Niw4My4xODIiIGlkPSJGaWxsLTciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTMuMjY2LDkwLjc4MiBMMTIuNyw5MC43ODIgQzEyLjUsOTAuNzgyIDEyLjM4NCw5MC43MjYgMTIuMzU0LDkwLjYxNiBDMTIuMzI0LDkwLjUwNiAxMi4zOTcsOTAuMzk5IDEyLjU2OSw5MC4yOTkgTDI0LjA3NCw4My41OTcgQzI0LjMxLDgzLjQ1OSAyNC42ODksODMuMjI2IDI0LjkxOCw4My4wNzggTDI1LjYzMyw4Mi42MTQgQzI1LjcyMyw4Mi41NTUgMjUuODEzLDgyLjUyNSAyNS44OTksODIuNTI1IEMyNi4wNzEsODIuNTI1IDI2LjI0NCw4Mi42NTUgMjYuMjQ0LDgyLjk0NiBDMjYuMjQ0LDgzLjE2IDI2LjI0NSw4My4zMDkgMjYuMjQ3LDgzLjM4MyBMMjYuMjUzLDgzLjM4NyBMMjYuMjQ5LDgzLjQ1NiBDMjYuMjQ2LDgzLjUzMSAyNi4yNDYsODMuNTMxIDI1Ljc2Myw4My44MTIgTDE0LjI1OCw5MC41MTQgQzE0LDkwLjY2NSAxMy41NjQsOTAuNzgyIDEzLjI2Niw5MC43ODIgTDEzLjI2Niw5MC43ODIgWiBNMTIuNjY2LDkwLjUzMiBMMTIuNyw5MC41MzMgTDEzLjI2Niw5MC41MzMgQzEzLjUxOCw5MC41MzMgMTMuOTE1LDkwLjQyNSAxNC4xMzIsOTAuMjk5IEwyNS42MzcsODMuNTk3IEMyNS44MDUsODMuNDk5IDI1LjkzMSw4My40MjQgMjUuOTk4LDgzLjM4MyBDMjUuOTk0LDgzLjI5OSAyNS45OTQsODMuMTY1IDI1Ljk5NCw4Mi45NDYgTDI1Ljg5OSw4Mi43NzUgTDI1Ljc2OCw4Mi44MjQgTDI1LjA1NCw4My4yODcgQzI0LjgyMiw4My40MzcgMjQuNDM4LDgzLjY3MyAyNC4yLDgzLjgxMiBMMTIuNjk1LDkwLjUxNCBMMTIuNjY2LDkwLjUzMiBMMTIuNjY2LDkwLjUzMiBaIiBpZD0iRmlsbC04IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEzLjI2Niw4OS44NzEgTDEyLjcsODkuODcxIEMxMi41LDg5Ljg3MSAxMi4zODQsODkuODE1IDEyLjM1NCw4OS43MDUgQzEyLjMyNCw4OS41OTUgMTIuMzk3LDg5LjQ4OCAxMi41NjksODkuMzg4IEwyNC4wNzQsODIuNjg2IEMyNC4zMzIsODIuNTM1IDI0Ljc2OCw4Mi40MTggMjUuMDY3LDgyLjQxOCBMMjUuNjMyLDgyLjQxOCBDMjUuODMyLDgyLjQxOCAyNS45NDgsODIuNDc0IDI1Ljk3OCw4Mi41ODQgQzI2LjAwOCw4Mi42OTQgMjUuOTM1LDgyLjgwMSAyNS43NjMsODIuOTAxIEwxNC4yNTgsODkuNjAzIEMxNCw4OS43NTQgMTMuNTY0LDg5Ljg3MSAxMy4yNjYsODkuODcxIEwxMy4yNjYsODkuODcxIFogTTEyLjY2Niw4OS42MjEgTDEyLjcsODkuNjIyIEwxMy4yNjYsODkuNjIyIEMxMy41MTgsODkuNjIyIDEzLjkxNSw4OS41MTUgMTQuMTMyLDg5LjM4OCBMMjUuNjM3LDgyLjY4NiBMMjUuNjY3LDgyLjY2OCBMMjUuNjMyLDgyLjY2NyBMMjUuMDY3LDgyLjY2NyBDMjQuODE1LDgyLjY2NyAyNC40MTgsODIuNzc1IDI0LjIsODIuOTAxIEwxMi42OTUsODkuNjAzIEwxMi42NjYsODkuNjIxIEwxMi42NjYsODkuNjIxIFoiIGlkPSJGaWxsLTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMzcsOTAuODAxIEwxMi4zNyw4OS41NTQgTDEyLjM3LDkwLjgwMSIgaWQ9IkZpbGwtMTAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNi4xMyw5My45MDEgQzUuMzc5LDkzLjgwOCA0LjgxNiw5My4xNjQgNC42OTEsOTIuNTI1IEMzLjg2LDg4LjI4NyAzLjU0LDgzLjc0MyAzLjUyNiw3MS4xNzMgQzMuNTExLDU4LjM4OSA0LjQyMyw1MS40MjggNC40MjMsNTEuNDI4IEM1LjEzNCw0Ny4yODIgNy4yMSw0Ni4yMzYgNy4yMSw0Ni4yMzYgQzcuMjEsNDYuMjM2IDgxLjY2NywzLjI1IDgyLjA2OSwzLjAxNyBDODIuMjkyLDIuODg4IDg0LjU1NiwxLjQzMyA4NS4yNjQsMy45NCBDODcuMjE0LDEwLjg0IDg2Ljc1MiwzNS44MjcgODUuNTM5LDQzLjgxOCBDODUuMTUsNDYuMzgzIDg0LjI5MSw0OS4wMzMgODIuNDgzLDUwLjEwMSBMNy4yMSw5My42NTMgQzYuODI4LDkzLjg3NCA2LjQ2MSw5My45NDEgNi4xMyw5My45MDEgQzYuMTMsOTMuOTAxIDMuMzQ5LDkzLjQ2IDMuMTI5LDkxLjc3NiBDMi41NjgsODcuNDk1IDEuOTc3LDgyLjk5NSAxLjk2Miw3MC40MjUgQzEuOTQ4LDU3LjY0MSAyLjg2LDUwLjY4IDIuODYsNTAuNjggQzMuNTcsNDYuNTM0IDUuNjQ3LDQ1LjQ4OSA1LjY0Nyw0NS40ODkgQzUuNjQ2LDQ1LjQ4OSA4LjA2NSw0NC4wOTIgMTIuMjQ1LDQxLjY3OSBMMTMuMTE2LDQxLjU2IEwxOS43MTUsMzcuNzMgTDE5Ljc2MSwzNy4yNjkgTDYuMTMsOTMuOTAxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjMxNyw5NC4xNjEgTDYuMTAyLDk0LjE0OCBMNi4xMDEsOTQuMTQ4IEw1Ljg1Nyw5NC4xMDEgQzUuMTM4LDkzLjk0NSAzLjA4NSw5My4zNjUgMi44ODEsOTEuODA5IEMyLjMxMyw4Ny40NjkgMS43MjcsODIuOTk2IDEuNzEzLDcwLjQyNSBDMS42OTksNTcuNzcxIDIuNjA0LDUwLjcxOCAyLjYxMyw1MC42NDggQzMuMzM4LDQ2LjQxNyA1LjQ0NSw0NS4zMSA1LjUzNSw0NS4yNjYgTDEyLjE2Myw0MS40MzkgTDEzLjAzMyw0MS4zMiBMMTkuNDc5LDM3LjU3OCBMMTkuNTEzLDM3LjI0NCBDMTkuNTI2LDM3LjEwNyAxOS42NDcsMzcuMDA4IDE5Ljc4NiwzNy4wMjEgQzE5LjkyMiwzNy4wMzQgMjAuMDIzLDM3LjE1NiAyMC4wMDksMzcuMjkzIEwxOS45NSwzNy44ODIgTDEzLjE5OCw0MS44MDEgTDEyLjMyOCw0MS45MTkgTDUuNzcyLDQ1LjcwNCBDNS43NDEsNDUuNzIgMy43ODIsNDYuNzcyIDMuMTA2LDUwLjcyMiBDMy4wOTksNTAuNzgyIDIuMTk4LDU3LjgwOCAyLjIxMiw3MC40MjQgQzIuMjI2LDgyLjk2MyAyLjgwOSw4Ny40MiAzLjM3Myw5MS43MjkgQzMuNDY0LDkyLjQyIDQuMDYyLDkyLjg4MyA0LjY4Miw5My4xODEgQzQuNTY2LDkyLjk4NCA0LjQ4Niw5Mi43NzYgNC40NDYsOTIuNTcyIEMzLjY2NSw4OC41ODggMy4yOTEsODQuMzcgMy4yNzYsNzEuMTczIEMzLjI2Miw1OC41MiA0LjE2Nyw1MS40NjYgNC4xNzYsNTEuMzk2IEM0LjkwMSw0Ny4xNjUgNy4wMDgsNDYuMDU5IDcuMDk4LDQ2LjAxNCBDNy4wOTQsNDYuMDE1IDgxLjU0MiwzLjAzNCA4MS45NDQsMi44MDIgTDgxLjk3MiwyLjc4NSBDODIuODc2LDIuMjQ3IDgzLjY5MiwyLjA5NyA4NC4zMzIsMi4zNTIgQzg0Ljg4NywyLjU3MyA4NS4yODEsMy4wODUgODUuNTA0LDMuODcyIEM4Ny41MTgsMTEgODYuOTY0LDM2LjA5MSA4NS43ODUsNDMuODU1IEM4NS4yNzgsNDcuMTk2IDg0LjIxLDQ5LjM3IDgyLjYxLDUwLjMxNyBMNy4zMzUsOTMuODY5IEM2Ljk5OSw5NC4wNjMgNi42NTgsOTQuMTYxIDYuMzE3LDk0LjE2MSBMNi4zMTcsOTQuMTYxIFogTTYuMTcsOTMuNjU0IEM2LjQ2Myw5My42OSA2Ljc3NCw5My42MTcgNy4wODUsOTMuNDM3IEw4Mi4zNTgsNDkuODg2IEM4NC4xODEsNDguODA4IDg0Ljk2LDQ1Ljk3MSA4NS4yOTIsNDMuNzggQzg2LjQ2NiwzNi4wNDkgODcuMDIzLDExLjA4NSA4NS4wMjQsNC4wMDggQzg0Ljg0NiwzLjM3NyA4NC41NTEsMi45NzYgODQuMTQ4LDIuODE2IEM4My42NjQsMi42MjMgODIuOTgyLDIuNzY0IDgyLjIyNywzLjIxMyBMODIuMTkzLDMuMjM0IEM4MS43OTEsMy40NjYgNy4zMzUsNDYuNDUyIDcuMzM1LDQ2LjQ1MiBDNy4zMDQsNDYuNDY5IDUuMzQ2LDQ3LjUyMSA0LjY2OSw1MS40NzEgQzQuNjYyLDUxLjUzIDMuNzYxLDU4LjU1NiAzLjc3NSw3MS4xNzMgQzMuNzksODQuMzI4IDQuMTYxLDg4LjUyNCA0LjkzNiw5Mi40NzYgQzUuMDI2LDkyLjkzNyA1LjQxMiw5My40NTkgNS45NzMsOTMuNjE1IEM2LjA4Nyw5My42NCA2LjE1OCw5My42NTIgNi4xNjksOTMuNjU0IEw2LjE3LDkzLjY1NCBMNi4xNyw5My42NTQgWiIgaWQ9IkZpbGwtMTIiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4zMTcsNjguOTgyIEM3LjgwNiw2OC43MDEgOC4yMDIsNjguOTI2IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNi44MjksNzEuMjk0IDYuNDMzLDcxLjA2OSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIiBpZD0iRmlsbC0xMyIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjkyLDcxLjEzMyBDNi42MzEsNzEuMTMzIDYuNDMzLDcwLjkwNSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIEM3LjQ2LDY4LjkgNy41OTUsNjguODYxIDcuNzE0LDY4Ljg2MSBDOC4wMDMsNjguODYxIDguMjAyLDY5LjA5IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNy4xNzQsNzEuMDk0IDcuMDM5LDcxLjEzMyA2LjkyLDcxLjEzMyBNNy43MTQsNjguNjc0IEM3LjU1Nyw2OC42NzQgNy4zOTIsNjguNzIzIDcuMjI0LDY4LjgyMSBDNi42NzYsNjkuMTM4IDYuMjQ2LDY5Ljg3OSA2LjI0Niw3MC41MDggQzYuMjQ2LDcwLjk5NCA2LjUxNyw3MS4zMiA2LjkyLDcxLjMyIEM3LjA3OCw3MS4zMiA3LjI0Myw3MS4yNzEgNy40MTEsNzEuMTc0IEM3Ljk1OSw3MC44NTcgOC4zODksNzAuMTE3IDguMzg5LDY5LjQ4NyBDOC4zODksNjkuMDAxIDguMTE3LDY4LjY3NCA3LjcxNCw2OC42NzQiIGlkPSJGaWxsLTE0IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYuOTIsNzAuOTQ3IEM2LjY0OSw3MC45NDcgNi42MjEsNzAuNjQgNi42MjEsNzAuNTA4IEM2LjYyMSw3MC4wMTcgNi45ODIsNjkuMzkyIDcuNDExLDY5LjE0NSBDNy41MjEsNjkuMDgyIDcuNjI1LDY5LjA0OSA3LjcxNCw2OS4wNDkgQzcuOTg2LDY5LjA0OSA4LjAxNSw2OS4zNTUgOC4wMTUsNjkuNDg3IEM4LjAxNSw2OS45NzggNy42NTIsNzAuNjAzIDcuMjI0LDcwLjg1MSBDNy4xMTUsNzAuOTE0IDcuMDEsNzAuOTQ3IDYuOTIsNzAuOTQ3IE03LjcxNCw2OC44NjEgQzcuNTk1LDY4Ljg2MSA3LjQ2LDY4LjkgNy4zMTcsNjguOTgyIEM2LjgyOSw2OS4yNjUgNi40MzMsNjkuOTQ4IDYuNDMzLDcwLjUwOCBDNi40MzMsNzAuOTA1IDYuNjMxLDcxLjEzMyA2LjkyLDcxLjEzMyBDNy4wMzksNzEuMTMzIDcuMTc0LDcxLjA5NCA3LjMxNyw3MS4wMTIgQzcuODA2LDcwLjczIDguMjAyLDcwLjA0NyA4LjIwMiw2OS40ODcgQzguMjAyLDY5LjA5IDguMDAzLDY4Ljg2MSA3LjcxNCw2OC44NjEiIGlkPSJGaWxsLTE1IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTcuNDQ0LDg1LjM1IEM3LjcwOCw4NS4xOTggNy45MjEsODUuMzE5IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuOTI1IDcuNzA4LDg2LjI5MiA3LjQ0NCw4Ni40NDQgQzcuMTgxLDg2LjU5NyA2Ljk2Nyw4Ni40NzUgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IiBpZD0iRmlsbC0xNiIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik03LjIzLDg2LjUxIEM3LjA3NCw4Ni41MSA2Ljk2Nyw4Ni4zODcgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IEM3LjUyMSw4NS4zMDUgNy41OTQsODUuMjg0IDcuNjU4LDg1LjI4NCBDNy44MTQsODUuMjg0IDcuOTIxLDg1LjQwOCA3LjkyMSw4NS42MjIgQzcuOTIxLDg1LjkyNSA3LjcwOCw4Ni4yOTIgNy40NDQsODYuNDQ0IEM3LjM2Nyw4Ni40ODkgNy4yOTQsODYuNTEgNy4yMyw4Ni41MSBNNy42NTgsODUuMDk4IEM3LjU1OCw4NS4wOTggNy40NTUsODUuMTI3IDcuMzUxLDg1LjE4OCBDNy4wMzEsODUuMzczIDYuNzgxLDg1LjgwNiA2Ljc4MSw4Ni4xNzMgQzYuNzgxLDg2LjQ4MiA2Ljk2Niw4Ni42OTcgNy4yMyw4Ni42OTcgQzcuMzMsODYuNjk3IDcuNDMzLDg2LjY2NiA3LjUzOCw4Ni42MDcgQzcuODU4LDg2LjQyMiA4LjEwOCw4NS45ODkgOC4xMDgsODUuNjIyIEM4LjEwOCw4NS4zMTMgNy45MjMsODUuMDk4IDcuNjU4LDg1LjA5OCIgaWQ9IkZpbGwtMTciIGZpbGw9IiM4MDk3QTIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4yMyw4Ni4zMjIgTDcuMTU0LDg2LjE3MyBDNy4xNTQsODUuOTM4IDcuMzMzLDg1LjYyOSA3LjUzOCw4NS41MTIgTDcuNjU4LDg1LjQ3MSBMNy43MzQsODUuNjIyIEM3LjczNCw4NS44NTYgNy41NTUsODYuMTY0IDcuMzUxLDg2LjI4MiBMNy4yMyw4Ni4zMjIgTTcuNjU4LDg1LjI4NCBDNy41OTQsODUuMjg0IDcuNTIxLDg1LjMwNSA3LjQ0NCw4NS4zNSBDNy4xODEsODUuNTAyIDYuOTY3LDg1Ljg3MSA2Ljk2Nyw4Ni4xNzMgQzYuOTY3LDg2LjM4NyA3LjA3NCw4Ni41MSA3LjIzLDg2LjUxIEM3LjI5NCw4Ni41MSA3LjM2Nyw4Ni40ODkgNy40NDQsODYuNDQ0IEM3LjcwOCw4Ni4yOTIgNy45MjEsODUuOTI1IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuNDA4IDcuODE0LDg1LjI4NCA3LjY1OCw4NS4yODQiIGlkPSJGaWxsLTE4IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTc3LjI3OCw3Ljc2OSBMNzcuMjc4LDUxLjQzNiBMMTAuMjA4LDkwLjE2IEwxMC4yMDgsNDYuNDkzIEw3Ny4yNzgsNy43NjkiIGlkPSJGaWxsLTE5IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEwLjA4Myw5MC4zNzUgTDEwLjA4Myw0Ni40MjEgTDEwLjE0Niw0Ni4zODUgTDc3LjQwMyw3LjU1NCBMNzcuNDAzLDUxLjUwOCBMNzcuMzQxLDUxLjU0NCBMMTAuMDgzLDkwLjM3NSBMMTAuMDgzLDkwLjM3NSBaIE0xMC4zMzMsNDYuNTY0IEwxMC4zMzMsODkuOTQ0IEw3Ny4xNTQsNTEuMzY1IEw3Ny4xNTQsNy45ODUgTDEwLjMzMyw0Ni41NjQgTDEwLjMzMyw0Ni41NjQgWiIgaWQ9IkZpbGwtMjAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMjUuNzM3LDg4LjY0NyBMMTE4LjA5OCw5MS45ODEgTDExOC4wOTgsODQgTDEwNi42MzksODguNzEzIEwxMDYuNjM5LDk2Ljk4MiBMOTksMTAwLjMxNSBMMTEyLjM2OSwxMDMuOTYxIEwxMjUuNzM3LDg4LjY0NyIgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTIiIGZpbGw9IiM0NTVBNjQiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+'); +}; + +module.exports = RotateInstructions; + +},{"./util.js":13}],13:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var Util = {}; + +Util.base64 = function(mimeType, base64) { + return 'data:' + mimeType + ';base64,' + base64; +}; + +Util.isMobile = function() { + var check = false; + (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); + return check; +}; + +Util.isFirefox = function() { + return /firefox/i.test(navigator.userAgent); +}; + +Util.isIOS = function() { + return /(iPad|iPhone|iPod)/g.test(navigator.userAgent); +}; + +Util.isIFrame = function() { + try { + return window.self !== window.top; + } catch (e) { + return true; + } +}; + +Util.appendQueryParameter = function(url, key, value) { + // Determine delimiter based on if the URL already GET parameters in it. + var delimiter = (url.indexOf('?') < 0 ? '?' : '&'); + url += delimiter + key + '=' + value; + return url; +}; + +// From http://goo.gl/4WX3tg +Util.getQueryParameter = function(name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), + results = regex.exec(location.search); + return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); +}; + +Util.isLandscapeMode = function() { + return (window.orientation == 90 || window.orientation == -90); +}; + +Util.getScreenWidth = function() { + return Math.max(window.screen.width, window.screen.height) * + window.devicePixelRatio; +}; + +Util.getScreenHeight = function() { + return Math.min(window.screen.width, window.screen.height) * + window.devicePixelRatio; +}; + +/** + * Utility to convert the projection matrix to a vector accepted by the shader. + * + * @param {Object} opt_params A rectangle to scale this vector by. + */ +Util.projectionMatrixToVector_ = function(matrix, opt_params) { + var params = opt_params || {}; + var xScale = params.xScale || 1; + var yScale = params.yScale || 1; + var xTrans = params.xTrans || 0; + var yTrans = params.yTrans || 0; + + var elements = matrix.elements; + var vec = new THREE.Vector4(); + vec.set(elements[4*0 + 0] * xScale, + elements[4*1 + 1] * yScale, + elements[4*2 + 0] - 1 - xTrans, + elements[4*2 + 1] - 1 - yTrans).divideScalar(2); + return vec; +}; + +Util.leftProjectionVectorToRight_ = function(left) { + //projectionLeft + vec4(0.0, 0.0, 1.0, 0.0)) * vec4(1.0, 1.0, -1.0, 1.0); + var out = new THREE.Vector4(0, 0, 1, 0); + out.add(left); // out = left + (0, 0, 1, 0). + out.z *= -1; // Flip z. + + return out; +}; + +module.exports = Util; + +},{}],14:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var Emitter = _dereq_('./emitter.js'); +var Util = _dereq_('./util.js'); + +var DEFAULT_VIEWER = 'CardboardV1'; +var VIEWER_KEY = 'WEBVR_CARDBOARD_VIEWER'; + +/** + * Creates a viewer selector with the options specified. Supports being shown + * and hidden. Generates events when viewer parameters change. Also supports + * saving the currently selected index in localStorage. + * + * @param {Object} options Option labels for all valid selections {name: index}. + */ +function ViewerSelector(options) { + // Try to load the selected key from local storage. If none exists, use the + // default key. + try { + this.selectedKey = localStorage.getItem(VIEWER_KEY) || DEFAULT_VIEWER; + } catch(error) { + console.error('Failed to load viewer profile: %s', error); + } + this.dialog = this.createDialog_(options); + this.options = options; + document.body.appendChild(this.dialog); +} +ViewerSelector.prototype = new Emitter(); + +ViewerSelector.prototype.show = function() { + //console.log('ViewerSelector.show'); + + // Ensure the currently selected item is checked. + var selected = this.dialog.querySelector('#' + this.selectedKey); + selected.checked = true; + + // Show the UI. + this.dialog.style.display = 'block'; +}; + +ViewerSelector.prototype.hide = function() { + //console.log('ViewerSelector.hide'); + this.dialog.style.display = 'none'; +}; + +ViewerSelector.prototype.getSelectedKey_ = function() { + var input = this.dialog.querySelector('input[name=field]:checked'); + if (input) { + return input.id; + } + return null; +}; + +ViewerSelector.prototype.onSave_ = function() { + this.selectedKey = this.getSelectedKey_(); + if (!this.selectedKey || !this.options[this.selectedKey]) { + console.error('ViewerSelector.onSave_: this should never happen!'); + return; + } + + this.emit('change', this.options[this.selectedKey]); + + // Attempt to save the viewer profile, but fails in private mode. + try { + localStorage.setItem(VIEWER_KEY, this.selectedKey); + } catch(error) { + console.error('Failed to save viewer profile: %s', error); + } + this.hide(); +}; + +/** + * Creates the dialog. + */ +ViewerSelector.prototype.createDialog_ = function(options) { + var container = document.createElement('div'); + container.style.display = 'none'; + // Create an overlay that dims the background, and which goes away when you + // tap it. + var overlay = document.createElement('div'); + var s = overlay.style; + s.position = 'fixed'; + s.left = 0; + s.top = 0; + s.width = '100%'; + s.height = '100%'; + s.background = 'rgba(0, 0, 0, 0.3)'; + overlay.addEventListener('click', this.hide.bind(this)); + + var width = 280; + var dialog = document.createElement('div'); + var s = dialog.style; + s.boxSizing = 'border-box'; + s.position = 'fixed'; + s.top = '24px'; + s.left = '50%'; + s.marginLeft = (-width/2) + 'px'; + s.width = width + 'px'; + s.padding = '24px'; + s.overflow = 'hidden'; + s.background = '#fafafa'; + s.fontFamily = "'Roboto', sans-serif"; + s.boxShadow = '0px 5px 20px #666'; + + dialog.appendChild(this.createH1_('Select your viewer')); + for (var id in options) { + dialog.appendChild(this.createChoice_(id, options[id].label)); + } + dialog.appendChild(this.createButton_('Save', this.onSave_.bind(this))); + + container.appendChild(overlay); + container.appendChild(dialog); + + return container; +}; + +ViewerSelector.prototype.createH1_ = function(name) { + var h1 = document.createElement('h1'); + var s = h1.style; + s.color = 'black'; + s.fontSize = '20px'; + s.fontWeight = 'bold'; + s.marginTop = 0; + s.marginBottom = '24px'; + h1.innerHTML = name; + return h1; +}; + +ViewerSelector.prototype.createChoice_ = function(id, name) { + /* +

+ + +
+ */ + var div = document.createElement('div'); + div.style.marginTop = '8px'; + div.style.color = 'black'; + + var input = document.createElement('input'); + input.style.fontSize = '30px'; + input.setAttribute('id', id); + input.setAttribute('type', 'radio'); + input.setAttribute('value', id); + input.setAttribute('name', 'field'); + + var label = document.createElement('label'); + label.style.marginLeft = '4px'; + label.setAttribute('for', id); + label.innerHTML = name; + + div.appendChild(input); + div.appendChild(label); + + return div; +}; + +ViewerSelector.prototype.createButton_ = function(label, onclick) { + var button = document.createElement('button'); + button.innerHTML = label; + var s = button.style; + s.float = 'right'; + s.textTransform = 'uppercase'; + s.color = '#1094f7'; + s.fontSize = '14px'; + s.letterSpacing = 0; + s.border = 0; + s.background = 'none'; + s.marginTop = '16px'; + + button.addEventListener('click', onclick); + + return button; +}; + +module.exports = ViewerSelector; + +},{"./emitter.js":9,"./util.js":13}],15:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var Util = _dereq_('./util.js'); + +/** + * Android and iOS compatible wakelock implementation. + * + * Refactored thanks to dkovalev@. + */ +function AndroidWakeLock() { + var video = document.createElement('video'); + + video.addEventListener('ended', function() { + video.play(); + }); + + this.request = function() { + if (video.paused) { + // Base64 version of videos_src/no-sleep-120s.mp4. + video.src = Util.base64('video/mp4', 'AAAAGGZ0eXBpc29tAAAAAG1wNDFhdmMxAAAIA21vb3YAAABsbXZoZAAAAADSa9v60mvb+gABX5AAlw/gAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAdkdHJhawAAAFx0a2hkAAAAAdJr2/rSa9v6AAAAAQAAAAAAlw/gAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAQAAAAHAAAAAAAJGVkdHMAAAAcZWxzdAAAAAAAAAABAJcP4AAAAAAAAQAAAAAG3G1kaWEAAAAgbWRoZAAAAADSa9v60mvb+gAPQkAGjneAFccAAAAAAC1oZGxyAAAAAAAAAAB2aWRlAAAAAAAAAAAAAAAAVmlkZW9IYW5kbGVyAAAABodtaW5mAAAAFHZtaGQAAAABAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAAZHc3RibAAAAJdzdHNkAAAAAAAAAAEAAACHYXZjMQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAMABwASAAAAEgAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABj//wAAADFhdmNDAWQAC//hABlnZAALrNlfllw4QAAAAwBAAAADAKPFCmWAAQAFaOvssiwAAAAYc3R0cwAAAAAAAAABAAAAbgAPQkAAAAAUc3RzcwAAAAAAAAABAAAAAQAAA4BjdHRzAAAAAAAAAG4AAAABAD0JAAAAAAEAehIAAAAAAQA9CQAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEALcbAAAAAHHN0c2MAAAAAAAAAAQAAAAEAAABuAAAAAQAAAcxzdHN6AAAAAAAAAAAAAABuAAADCQAAABgAAAAOAAAADgAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABMAAAAUc3RjbwAAAAAAAAABAAAIKwAAACt1ZHRhAAAAI6llbmMAFwAAdmxjIDIuMi4xIHN0cmVhbSBvdXRwdXQAAAAId2lkZQAACRRtZGF0AAACrgX//6vcRem95tlIt5Ys2CDZI+7veDI2NCAtIGNvcmUgMTQyIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTMgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MzoweDEzIG1lPWhleCBzdWJtZT03IHBzeT0xIHBzeV9yZD0xLjAwOjAuMDAgbWl4ZWRfcmVmPTEgbWVfcmFuZ2U9MTYgY2hyb21hX21lPTEgdHJlbGxpcz0xIDh4OGRjdD0xIGNxbT0wIGRlYWR6b25lPTIxLDExIGZhc3RfcHNraXA9MSBjaHJvbWFfcXBfb2Zmc2V0PS0yIHRocmVhZHM9MTIgbG9va2FoZWFkX3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRlcmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9MSBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRiPTEgb3Blbl9nb3A9MCB3ZWlnaHRwPTIga2V5aW50PTI1MCBrZXlpbnRfbWluPTEgc2NlbmVjdXQ9NDAgaW50cmFfcmVmcmVzaD0wIHJjX2xvb2thaGVhZD00MCByYz1hYnIgbWJ0cmVlPTEgYml0cmF0ZT0xMDAgcmF0ZXRvbD0xLjAgcWNvbXA9MC42MCBxcG1pbj0xMCBxcG1heD01MSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAU2WIhAAQ/8ltlOe+cTZuGkKg+aRtuivcDZ0pBsfsEi9p/i1yU9DxS2lq4dXTinViF1URBKXgnzKBd/Uh1bkhHtMrwrRcOJslD01UB+fyaL6ef+DBAAAAFEGaJGxBD5B+v+a+4QqF3MgBXz9MAAAACkGeQniH/+94r6EAAAAKAZ5hdEN/8QytwAAAAAgBnmNqQ3/EgQAAAA5BmmhJqEFomUwIIf/+4QAAAApBnoZFESw//76BAAAACAGepXRDf8SBAAAACAGep2pDf8SAAAAADkGarEmoQWyZTAgh//7gAAAACkGeykUVLD//voEAAAAIAZ7pdEN/xIAAAAAIAZ7rakN/xIAAAAAOQZrwSahBbJlMCCH//uEAAAAKQZ8ORRUsP/++gQAAAAgBny10Q3/EgQAAAAgBny9qQ3/EgAAAAA5BmzRJqEFsmUwIIf/+4AAAAApBn1JFFSw//76BAAAACAGfcXRDf8SAAAAACAGfc2pDf8SAAAAADkGbeEmoQWyZTAgh//7hAAAACkGflkUVLD//voAAAAAIAZ+1dEN/xIEAAAAIAZ+3akN/xIEAAAAOQZu8SahBbJlMCCH//uAAAAAKQZ/aRRUsP/++gQAAAAgBn/l0Q3/EgAAAAAgBn/tqQ3/EgQAAAA5Bm+BJqEFsmUwIIf/+4QAAAApBnh5FFSw//76AAAAACAGePXRDf8SAAAAACAGeP2pDf8SBAAAADkGaJEmoQWyZTAgh//7gAAAACkGeQkUVLD//voEAAAAIAZ5hdEN/xIAAAAAIAZ5jakN/xIEAAAAOQZpoSahBbJlMCCH//uEAAAAKQZ6GRRUsP/++gQAAAAgBnqV0Q3/EgQAAAAgBnqdqQ3/EgAAAAA5BmqxJqEFsmUwIIf/+4AAAAApBnspFFSw//76BAAAACAGe6XRDf8SAAAAACAGe62pDf8SAAAAADkGa8EmoQWyZTAgh//7hAAAACkGfDkUVLD//voEAAAAIAZ8tdEN/xIEAAAAIAZ8vakN/xIAAAAAOQZs0SahBbJlMCCH//uAAAAAKQZ9SRRUsP/++gQAAAAgBn3F0Q3/EgAAAAAgBn3NqQ3/EgAAAAA5Bm3hJqEFsmUwIIf/+4QAAAApBn5ZFFSw//76AAAAACAGftXRDf8SBAAAACAGft2pDf8SBAAAADkGbvEmoQWyZTAgh//7gAAAACkGf2kUVLD//voEAAAAIAZ/5dEN/xIAAAAAIAZ/7akN/xIEAAAAOQZvgSahBbJlMCCH//uEAAAAKQZ4eRRUsP/++gAAAAAgBnj10Q3/EgAAAAAgBnj9qQ3/EgQAAAA5BmiRJqEFsmUwIIf/+4AAAAApBnkJFFSw//76BAAAACAGeYXRDf8SAAAAACAGeY2pDf8SBAAAADkGaaEmoQWyZTAgh//7hAAAACkGehkUVLD//voEAAAAIAZ6ldEN/xIEAAAAIAZ6nakN/xIAAAAAOQZqsSahBbJlMCCH//uAAAAAKQZ7KRRUsP/++gQAAAAgBnul0Q3/EgAAAAAgBnutqQ3/EgAAAAA5BmvBJqEFsmUwIIf/+4QAAAApBnw5FFSw//76BAAAACAGfLXRDf8SBAAAACAGfL2pDf8SAAAAADkGbNEmoQWyZTAgh//7gAAAACkGfUkUVLD//voEAAAAIAZ9xdEN/xIAAAAAIAZ9zakN/xIAAAAAOQZt4SahBbJlMCCH//uEAAAAKQZ+WRRUsP/++gAAAAAgBn7V0Q3/EgQAAAAgBn7dqQ3/EgQAAAA5Bm7xJqEFsmUwIIf/+4AAAAApBn9pFFSw//76BAAAACAGf+XRDf8SAAAAACAGf+2pDf8SBAAAADkGb4EmoQWyZTAgh//7hAAAACkGeHkUVLD//voAAAAAIAZ49dEN/xIAAAAAIAZ4/akN/xIEAAAAOQZokSahBbJlMCCH//uAAAAAKQZ5CRRUsP/++gQAAAAgBnmF0Q3/EgAAAAAgBnmNqQ3/EgQAAAA5BmmhJqEFsmUwIIf/+4QAAAApBnoZFFSw//76BAAAACAGepXRDf8SBAAAACAGep2pDf8SAAAAADkGarEmoQWyZTAgh//7gAAAACkGeykUVLD//voEAAAAIAZ7pdEN/xIAAAAAIAZ7rakN/xIAAAAAPQZruSahBbJlMFEw3//7B'); + video.play(); + } + }; + + this.release = function() { + video.pause(); + video.src = ''; + }; +} + +function iOSWakeLock() { + var timer = null; + + this.request = function() { + if (!timer) { + timer = setInterval(function() { + window.location = window.location; + setTimeout(window.stop, 0); + }, 30000); + } + } + + this.release = function() { + if (timer) { + clearInterval(timer); + timer = null; + } + } +} + + +function getWakeLock() { + var userAgent = navigator.userAgent || navigator.vendor || window.opera; + if (userAgent.match(/iPhone/i) || userAgent.match(/iPod/i)) { + return iOSWakeLock; + } else { + return AndroidWakeLock; + } +} + +module.exports = getWakeLock(); + +},{"./util.js":13}],16:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var ButtonManager = _dereq_('./button-manager.js'); +var CardboardDistorter = _dereq_('./cardboard-distorter.js'); +var DeviceInfo = _dereq_('./device-info.js'); +var Dpdb = _dereq_('./dpdb.js'); +var Emitter = _dereq_('./emitter.js'); +var Modes = _dereq_('./modes.js'); +var RotateInstructions = _dereq_('./rotate-instructions.js'); +var Util = _dereq_('./util.js'); +var ViewerSelector = _dereq_('./viewer-selector.js'); +var Wakelock = _dereq_('./wakelock.js'); + +/** + * Helper for getting in and out of VR mode. + * Here we assume VR mode == full screen mode. + * + * 1. Detects whether or not VR mode is possible by feature detecting for + * WebVR (or polyfill). + * + * 2. If WebVR is available, shows a button that lets you enter VR mode. + * + * 3. Provides Cardboard-style distortion if the webvr-polyfill is being used. + * + * 4. Provides best practices while in VR mode. + * - Full screen + * - Wake lock + * - Orientation lock (mobile only) + */ +function WebVRManager(renderer, effect, params) { + this.params = params || {}; + + this.mode = Modes.UNKNOWN; + + // Set option to hide the button. + this.hideButton = this.params.hideButton || false; + // Whether or not the FOV should be distorted or un-distorted. By default, it + // should be distorted, but in the case of vertex shader based distortion, + // ensure that we use undistorted parameters. + this.isUndistorted = !!this.params.isUndistorted; + + // Save the THREE.js renderer and effect for later. + this.renderer = renderer; + this.effect = effect; + this.button = new ButtonManager(); + this.rotateInstructions = new RotateInstructions(); + this.viewerSelector = new ViewerSelector(DeviceInfo.Viewers); + + // Load the DPDB. + var shouldFetch = !WebVRConfig.NO_DPDB_FETCH; + this.dpdb = new Dpdb(shouldFetch, this.onDeviceParamsUpdated_.bind(this)); + + // Create device info and set the correct default viewer. + this.deviceInfo = new DeviceInfo(this.dpdb.getDeviceParams()); + this.deviceInfo.viewer = DeviceInfo.Viewers[this.viewerSelector.selectedKey]; + console.log('Using the %s viewer.', this.getViewer().label); + + this.distorter = new CardboardDistorter(renderer); + this.distorter.updateDeviceInfo(this.deviceInfo); + + this.isVRCompatible = false; + this.isFullscreenDisabled = !!Util.getQueryParameter('no_fullscreen'); + this.startMode = Modes.NORMAL; + var startModeParam = parseInt(Util.getQueryParameter('start_mode')); + if (!isNaN(startModeParam)) { + this.startMode = startModeParam; + } + + // Set the correct viewer profile, but only if this is Cardboard. + if (Util.isMobile()) { + this.onViewerChanged_(this.getViewer()); + } + // Listen for changes to the viewer. + this.viewerSelector.on('change', this.onViewerChanged_.bind(this)); + + if (this.hideButton) { + this.button.setVisibility(false); + } + + // Check if the browser is compatible with WebVR. + this.getDeviceByType_(HMDVRDevice).then(function(hmd) { + // Activate either VR or Immersive mode. + if (WebVRConfig.FORCE_DISTORTION) { + this.distorter.setActive(true); + this.isVRCompatible = true; + } else if (hmd) { + this.isVRCompatible = true; + // Only enable distortion if we are dealing using the polyfill, we have a + // perfect device match, and it's not prevented via configuration. + if (hmd.deviceName.indexOf('webvr-polyfill') == 0 && this.deviceInfo.getDevice() && + !WebVRConfig.PREVENT_DISTORTION) { + this.distorter.setActive(true); + } + this.hmd = hmd; + } + // Set the right mode. + switch (this.startMode) { + case Modes.MAGIC_WINDOW: + this.normalToMagicWindow_(); + this.setMode_(Modes.MAGIC_WINDOW); + break; + case Modes.VR: + this.anyModeToVR_(); + this.setMode_(Modes.VR); + break; + default: + this.setMode_(Modes.NORMAL); + } + this.button.on('fs', this.onFSClick_.bind(this)); + this.button.on('vr', this.onVRClick_.bind(this)); + this.button.on('back', this.onBackClick_.bind(this)); + this.button.on('settings', this.onSettingsClick_.bind(this)); + this.emit('initialized'); + }.bind(this)); + + // Save the input device for later sending timing data. + this.getDeviceByType_(PositionSensorVRDevice).then(function(input) { + this.input = input; + }.bind(this)); + + // Whenever we enter fullscreen, we are entering VR or immersive mode. + document.addEventListener('webkitfullscreenchange', + this.onFullscreenChange_.bind(this)); + document.addEventListener('mozfullscreenchange', + this.onFullscreenChange_.bind(this)); + document.addEventListener('MSFullscreenChange', + this.onFullscreenChange_.bind(this)); + window.addEventListener('orientationchange', + this.onOrientationChange_.bind(this)); + + // Create the necessary elements for wake lock to work. + this.wakelock = new Wakelock(); + + // Save whether or not we want the touch panner to be enabled or disabled by + // default. + this.isTouchPannerEnabled = !WebVRConfig.TOUCH_PANNER_DISABLED; + +} + +WebVRManager.prototype = new Emitter(); + +// Expose these values externally. +WebVRManager.Modes = Modes; + +/** + * Promise returns true if there is at least one HMD device available. + */ +WebVRManager.prototype.getDeviceByType_ = function(type) { + return new Promise(function(resolve, reject) { + navigator.getVRDevices().then(function(devices) { + // Promise succeeds, but check if there are any devices actually. + for (var i = 0; i < devices.length; i++) { + if (devices[i] instanceof type) { + resolve(devices[i]); + break; + } + } + resolve(null); + }, function() { + // No devices are found. + resolve(null); + }); + }); +}; + +WebVRManager.prototype.isVRMode = function() { + return this.mode == Modes.VR; +}; + +WebVRManager.prototype.getViewer = function() { + return this.deviceInfo.viewer; +}; + +WebVRManager.prototype.getDevice = function() { + return this.deviceInfo.device; +}; + +WebVRManager.prototype.getDeviceInfo = function() { + return this.deviceInfo; +}; + +WebVRManager.prototype.render = function(scene, camera, timestamp) { + this.camera = camera; + + this.resizeIfNeeded_(camera); + + if (this.isVRMode()) { + this.distorter.preRender(); + this.effect.render(scene, camera); + this.distorter.postRender(); + } else { + // Scene may be an array of two scenes, one for each eye. + if (scene instanceof Array) { + this.renderer.render(scene[0], camera); + } else { + this.renderer.render(scene, camera); + } + } +}; + + +WebVRManager.prototype.setMode_ = function(mode) { + var oldMode = this.mode; + if (mode == this.mode) { + console.error('Not changing modes, already in %s', mode); + return; + } + console.log('Mode change: %s => %s', this.mode, mode); + this.mode = mode; + this.button.setMode(mode, this.isVRCompatible); + + if (this.mode == Modes.VR && Util.isLandscapeMode() && Util.isMobile()) { + // In landscape mode, temporarily show the "put into Cardboard" + // interstitial. Otherwise, do the default thing. + this.rotateInstructions.showTemporarily(3000); + } else { + this.updateRotateInstructions_(); + } + + // Also hide the viewer selector. + this.viewerSelector.hide(); + + // Emit an event indicating the mode changed. + this.emit('modechange', mode, oldMode); + + // Note: This is a nasty hack since we need to communicate to the polyfill + // that touch panning is disabled, and the only way to do this currently is + // via WebVRConfig. + // TODO: Maybe move touch panning to the boilerplate to eliminate the hack. + // + // If we are in VR mode, always disable touch panning. + if (this.isTouchPannerEnabled) { + if (this.mode == Modes.VR) { + WebVRConfig.TOUCH_PANNER_DISABLED = true; + } else { + WebVRConfig.TOUCH_PANNER_DISABLED = false; + } + } + + if (this.mode == Modes.VR) { + // In VR mode, set the HMDVRDevice parameters. + this.setHMDVRDeviceParams_(this.getViewer()); + } +}; + +/** + * Main button was clicked. + */ +WebVRManager.prototype.onFSClick_ = function() { + switch (this.mode) { + case Modes.NORMAL: + // TODO: Remove this hack when iOS has fullscreen mode. + // If this is an iframe on iOS, break out and open in no_fullscreen mode. + if (Util.isIOS() && Util.isIFrame()) { + var url = window.location.href; + url = Util.appendQueryParameter(url, 'no_fullscreen', 'true'); + url = Util.appendQueryParameter(url, 'start_mode', Modes.MAGIC_WINDOW); + top.location.href = url; + return; + } + this.normalToMagicWindow_(); + this.setMode_(Modes.MAGIC_WINDOW); + break; + case Modes.MAGIC_WINDOW: + if (this.isFullscreenDisabled) { + window.history.back(); + } else { + this.anyModeToNormal_(); + this.setMode_(Modes.NORMAL); + } + break; + } +}; + +/** + * The VR button was clicked. + */ +WebVRManager.prototype.onVRClick_ = function() { + // TODO: Remove this hack when iOS has fullscreen mode. + // If this is an iframe on iOS, break out and open in no_fullscreen mode. + if (this.mode == Modes.NORMAL && Util.isIOS() && Util.isIFrame()) { + var url = window.location.href; + url = Util.appendQueryParameter(url, 'no_fullscreen', 'true'); + url = Util.appendQueryParameter(url, 'start_mode', Modes.VR); + top.location.href = url; + return; + } + this.anyModeToVR_(); + this.setMode_(Modes.VR); +}; + +/** + * Back button was clicked. + */ +WebVRManager.prototype.onBackClick_ = function() { + if (this.isFullscreenDisabled) { + window.history.back(); + } else { + this.anyModeToNormal_(); + this.setMode_(Modes.NORMAL); + } +}; + +WebVRManager.prototype.onSettingsClick_ = function() { + // Show the viewer selection dialog. + this.viewerSelector.show(); +}; + +/** + * + * Methods to go between modes. + * + */ +WebVRManager.prototype.normalToMagicWindow_ = function() { + // TODO: Re-enable pointer lock after debugging. + //this.requestPointerLock_(); + this.requestFullscreen_(); + this.wakelock.request(); +}; + +WebVRManager.prototype.anyModeToVR_ = function() { + // Don't do orientation locking for consistency. + //this.requestOrientationLock_(); + this.requestFullscreen_(); + //this.effect.setFullScreen(true); + this.wakelock.request(); + this.distorter.patch(); +}; + +WebVRManager.prototype.vrToMagicWindow_ = function() { + //this.releaseOrientationLock_(); + this.distorter.unpatch(); + + // Android bug: when returning from VR, resize the effect. + this.resize_(); +} + +WebVRManager.prototype.anyModeToNormal_ = function() { + //this.effect.setFullScreen(false); + this.exitFullscreen_(); + //this.releaseOrientationLock_(); + this.releasePointerLock_(); + this.wakelock.release(); + this.distorter.unpatch(); + + // Android bug: when returning from VR, resize the effect. + this.resize_(); +}; + +WebVRManager.prototype.resizeIfNeeded_ = function(camera) { + // Only resize the canvas if it needs to be resized. + var size = this.renderer.getSize(); + if (size.width != window.innerWidth || size.height != window.innerHeight) { + this.resize_(); + } +}; + +WebVRManager.prototype.resize_ = function() { + this.effect.setSize(window.innerWidth, window.innerHeight); + if (this.camera) { + this.camera.aspect = window.innerWidth / window.innerHeight; + this.camera.updateProjectionMatrix(); + } +}; + +WebVRManager.prototype.onOrientationChange_ = function(e) { + this.updateRotateInstructions_(); + // Also hide the viewer selector. + this.viewerSelector.hide(); +}; + +WebVRManager.prototype.updateRotateInstructions_ = function() { + this.rotateInstructions.disableShowTemporarily(); + // In portrait VR mode, tell the user to rotate to landscape. + if (this.mode == Modes.VR && !Util.isLandscapeMode() && Util.isMobile()) { + this.rotateInstructions.show(); + } else { + this.rotateInstructions.hide(); + } +}; + +WebVRManager.prototype.onFullscreenChange_ = function(e) { + // If we leave full-screen, go back to normal mode. + if (document.webkitFullscreenElement === null || + document.mozFullScreenElement === null) { + this.anyModeToNormal_(); + this.setMode_(Modes.NORMAL); + } +}; + +WebVRManager.prototype.requestPointerLock_ = function() { + var canvas = this.renderer.domElement; + canvas.requestPointerLock = canvas.requestPointerLock || + canvas.mozRequestPointerLock || + canvas.webkitRequestPointerLock; + + if (canvas.requestPointerLock) { + canvas.requestPointerLock(); + } +}; + +WebVRManager.prototype.releasePointerLock_ = function() { + document.exitPointerLock = document.exitPointerLock || + document.mozExitPointerLock || + document.webkitExitPointerLock; + + if (document.exitPointerLock) { + document.exitPointerLock(); + } +}; + +WebVRManager.prototype.requestOrientationLock_ = function() { + if (screen.orientation && Util.isMobile()) { + screen.orientation.lock('landscape'); + } +}; + +WebVRManager.prototype.releaseOrientationLock_ = function() { + if (screen.orientation) { + screen.orientation.unlock(); + } +}; + +WebVRManager.prototype.requestFullscreen_ = function() { + var canvas = document.body; + //var canvas = this.renderer.domElement; + if (canvas.requestFullscreen) { + canvas.requestFullscreen(); + } else if (canvas.mozRequestFullScreen) { + canvas.mozRequestFullScreen({vrDisplay: this.hmd}); + } else if (canvas.webkitRequestFullscreen) { + canvas.webkitRequestFullscreen({vrDisplay: this.hmd}); + } else if (canvas.msRequestFullscreen) { + canvas.msRequestFullscreen({vrDisplay: this.hmd}); + } +}; + +WebVRManager.prototype.exitFullscreen_ = function() { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } +}; + +WebVRManager.prototype.onViewerChanged_ = function(viewer) { + this.deviceInfo.setViewer(viewer); + + // Update the distortion appropriately. + this.distorter.updateDeviceInfo(this.deviceInfo); + + // And update the HMDVRDevice parameters. + this.setHMDVRDeviceParams_(viewer); + + // Notify anyone interested in this event. + this.emit('viewerchange', viewer); +}; + +/** + * Sets parameters on CardboardHMDVRDevice. These changes are ultimately handled + * by VREffect. + */ +WebVRManager.prototype.setHMDVRDeviceParams_ = function(viewer) { + this.getDeviceByType_(HMDVRDevice).then(function(hmd) { + if (!hmd) { + return; + } + + // If we can set fields of view, do that now. + if (hmd.setFieldOfView) { + // Calculate the optimal field of view for each eye. + hmd.setFieldOfView(this.deviceInfo.getFieldOfViewLeftEye(this.isUndistorted), + this.deviceInfo.getFieldOfViewRightEye(this.isUndistorted)); + } + + // Note: setInterpupillaryDistance is not part of the WebVR standard. + if (hmd.setInterpupillaryDistance) { + hmd.setInterpupillaryDistance(viewer.interLensDistance); + } + }.bind(this)); +}; + +WebVRManager.prototype.onDeviceParamsUpdated_ = function(newParams) { + console.log('DPDB reported that device params were updated.'); + this.deviceInfo.updateDeviceParams(newParams); + this.distorter.updateDeviceInfo(this.deviceInfo); +}; + +module.exports = WebVRManager; + +},{"./button-manager.js":2,"./cardboard-distorter.js":3,"./device-info.js":4,"./dpdb.js":8,"./emitter.js":9,"./modes.js":11,"./rotate-instructions.js":12,"./util.js":13,"./viewer-selector.js":14,"./wakelock.js":15}]},{},[10]); + +},{}],7:[function(_dereq_,module,exports){ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof _dereq_=="function"&&_dereq_;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof _dereq_=="function"&&_dereq_;for(var o=0;o Util.MAX_TIMESTEP) { + console.warn('Invalid timestamps detected. Time step between successive ' + + 'gyroscope sensor samples is very small or not monotonic'); + this.previousTimestampS = timestampS; + return; + } + this.accelerometer.set(-accGravity.x, -accGravity.y, -accGravity.z); + this.gyroscope.set(rotRate.alpha, rotRate.beta, rotRate.gamma); + + // With iOS and Firefox Android, rotationRate is reported in degrees, + // so we first convert to radians. + if (this.isIOS || this.isFirefoxAndroid) { + this.gyroscope.multiplyScalar(Math.PI / 180); + } + + this.filter.addAccelMeasurement(this.accelerometer, timestampS); + this.filter.addGyroMeasurement(this.gyroscope, timestampS); + + this.previousTimestampS = timestampS; +}; + +FusionPositionSensorVRDevice.prototype.onScreenOrientationChange_ = + function(screenOrientation) { + this.setScreenTransform_(); +}; + +FusionPositionSensorVRDevice.prototype.setScreenTransform_ = function() { + this.worldToScreenQ.set(0, 0, 0, 1); + switch (window.orientation) { + case 0: + break; + case 90: + this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0, 0, 1), -Math.PI/2); + break; + case -90: + this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI/2); + break; + case 180: + // TODO. + break; + } +}; + + +module.exports = FusionPositionSensorVRDevice; + +},{"./base.js":1,"./complementary-filter.js":3,"./pose-predictor.js":7,"./three-math.js":9,"./touch-panner.js":10,"./util.js":11}],5:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var WebVRPolyfill = _dereq_('./webvr-polyfill.js'); + +// Initialize a WebVRConfig just in case. +window.WebVRConfig = window.WebVRConfig || {}; +new WebVRPolyfill(); + +},{"./webvr-polyfill.js":12}],6:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var PositionSensorVRDevice = _dereq_('./base.js').PositionSensorVRDevice; +var THREE = _dereq_('./three-math.js'); +var Util = _dereq_('./util.js'); + +// How much to rotate per key stroke. +var KEY_SPEED = 0.15; +var KEY_ANIMATION_DURATION = 80; + +// How much to rotate for mouse events. +var MOUSE_SPEED_X = 0.5; +var MOUSE_SPEED_Y = 0.3; + +/** + * A virtual position sensor, implemented using keyboard and + * mouse APIs. This is designed as for desktops/laptops where no Device* + * events work. + */ +function MouseKeyboardPositionSensorVRDevice() { + this.deviceId = 'webvr-polyfill:mouse-keyboard'; + this.deviceName = 'VR Position Device (webvr-polyfill:mouse-keyboard)'; + + // Attach to mouse and keyboard events. + window.addEventListener('keydown', this.onKeyDown_.bind(this)); + window.addEventListener('mousemove', this.onMouseMove_.bind(this)); + window.addEventListener('mousedown', this.onMouseDown_.bind(this)); + window.addEventListener('mouseup', this.onMouseUp_.bind(this)); + + this.phi = 0; + this.theta = 0; + + // Variables for keyboard-based rotation animation. + this.targetAngle = null; + + // State variables for calculations. + this.euler = new THREE.Euler(); + this.orientation = new THREE.Quaternion(); + + // Variables for mouse-based rotation. + this.rotateStart = new THREE.Vector2(); + this.rotateEnd = new THREE.Vector2(); + this.rotateDelta = new THREE.Vector2(); +} +MouseKeyboardPositionSensorVRDevice.prototype = new PositionSensorVRDevice(); + +/** + * Returns {orientation: {x,y,z,w}, position: null}. + * Position is not supported for parity with other PositionSensors. + */ +MouseKeyboardPositionSensorVRDevice.prototype.getState = function() { + this.euler.set(this.phi, this.theta, 0, 'YXZ'); + this.orientation.setFromEuler(this.euler); + + return { + hasOrientation: true, + orientation: this.orientation, + hasPosition: false, + position: null + } +}; + +MouseKeyboardPositionSensorVRDevice.prototype.onKeyDown_ = function(e) { + // Track WASD and arrow keys. + if (e.keyCode == 38) { // Up key. + this.animatePhi_(this.phi + KEY_SPEED); + } else if (e.keyCode == 39) { // Right key. + this.animateTheta_(this.theta - KEY_SPEED); + } else if (e.keyCode == 40) { // Down key. + this.animatePhi_(this.phi - KEY_SPEED); + } else if (e.keyCode == 37) { // Left key. + this.animateTheta_(this.theta + KEY_SPEED); + } +}; + +MouseKeyboardPositionSensorVRDevice.prototype.animateTheta_ = function(targetAngle) { + this.animateKeyTransitions_('theta', targetAngle); +}; + +MouseKeyboardPositionSensorVRDevice.prototype.animatePhi_ = function(targetAngle) { + // Prevent looking too far up or down. + targetAngle = Util.clamp(targetAngle, -Math.PI/2, Math.PI/2); + this.animateKeyTransitions_('phi', targetAngle); +}; + +/** + * Start an animation to transition an angle from one value to another. + */ +MouseKeyboardPositionSensorVRDevice.prototype.animateKeyTransitions_ = function(angleName, targetAngle) { + // If an animation is currently running, cancel it. + if (this.angleAnimation) { + clearInterval(this.angleAnimation); + } + var startAngle = this[angleName]; + var startTime = new Date(); + // Set up an interval timer to perform the animation. + this.angleAnimation = setInterval(function() { + // Once we're finished the animation, we're done. + var elapsed = new Date() - startTime; + if (elapsed >= KEY_ANIMATION_DURATION) { + this[angleName] = targetAngle; + clearInterval(this.angleAnimation); + return; + } + // Linearly interpolate the angle some amount. + var percent = elapsed / KEY_ANIMATION_DURATION; + this[angleName] = startAngle + (targetAngle - startAngle) * percent; + }.bind(this), 1000/60); +}; + +MouseKeyboardPositionSensorVRDevice.prototype.onMouseDown_ = function(e) { + this.rotateStart.set(e.clientX, e.clientY); + this.isDragging = true; +}; + +// Very similar to https://gist.github.com/mrflix/8351020 +MouseKeyboardPositionSensorVRDevice.prototype.onMouseMove_ = function(e) { + if (!this.isDragging && !this.isPointerLocked_()) { + return; + } + // Support pointer lock API. + if (this.isPointerLocked_()) { + var movementX = e.movementX || e.mozMovementX || 0; + var movementY = e.movementY || e.mozMovementY || 0; + this.rotateEnd.set(this.rotateStart.x - movementX, this.rotateStart.y - movementY); + } else { + this.rotateEnd.set(e.clientX, e.clientY); + } + // Calculate how much we moved in mouse space. + this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart); + this.rotateStart.copy(this.rotateEnd); + + // Keep track of the cumulative euler angles. + var element = document.body; + this.phi += 2 * Math.PI * this.rotateDelta.y / element.clientHeight * MOUSE_SPEED_Y; + this.theta += 2 * Math.PI * this.rotateDelta.x / element.clientWidth * MOUSE_SPEED_X; + + // Prevent looking too far up or down. + this.phi = Util.clamp(this.phi, -Math.PI/2, Math.PI/2); +}; + +MouseKeyboardPositionSensorVRDevice.prototype.onMouseUp_ = function(e) { + this.isDragging = false; +}; + +MouseKeyboardPositionSensorVRDevice.prototype.isPointerLocked_ = function() { + var el = document.pointerLockElement || document.mozPointerLockElement || + document.webkitPointerLockElement; + return el !== undefined; +}; + +MouseKeyboardPositionSensorVRDevice.prototype.resetSensor = function() { + console.error('Not implemented yet.'); +}; + +module.exports = MouseKeyboardPositionSensorVRDevice; + +},{"./base.js":1,"./three-math.js":9,"./util.js":11}],7:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var THREE = _dereq_('./three-math.js'); + +var DEBUG = false; + +/** + * Given an orientation and the gyroscope data, predicts the future orientation + * of the head. This makes rendering appear faster. + * + * Also see: http://msl.cs.uiuc.edu/~lavalle/papers/LavYerKatAnt14.pdf + * + * @param {Number} predictionTimeS time from head movement to the appearance of + * the corresponding image. + */ +function PosePredictor(predictionTimeS) { + this.predictionTimeS = predictionTimeS; + + // The quaternion corresponding to the previous state. + this.previousQ = new THREE.Quaternion(); + // Previous time a prediction occurred. + this.previousTimestampS = null; + + // The delta quaternion that adjusts the current pose. + this.deltaQ = new THREE.Quaternion(); + // The output quaternion. + this.outQ = new THREE.Quaternion(); +} + +PosePredictor.prototype.getPrediction = function(currentQ, gyro, timestampS) { + if (!this.previousTimestampS) { + this.previousQ.copy(currentQ); + this.previousTimestampS = timestampS; + return currentQ; + } + + // Calculate axis and angle based on gyroscope rotation rate data. + var axis = new THREE.Vector3(); + axis.copy(gyro); + axis.normalize(); + + var angularSpeed = gyro.length(); + + // If we're rotating slowly, don't do prediction. + if (angularSpeed < THREE.Math.degToRad(20)) { + if (DEBUG) { + console.log('Moving slowly, at %s deg/s: no prediction', + THREE.Math.radToDeg(angularSpeed).toFixed(1)); + } + this.outQ.copy(currentQ); + this.previousQ.copy(currentQ); + return this.outQ; + } + + // Get the predicted angle based on the time delta and latency. + var deltaT = timestampS - this.previousTimestampS; + var predictAngle = angularSpeed * this.predictionTimeS; + + this.deltaQ.setFromAxisAngle(axis, predictAngle); + this.outQ.copy(this.previousQ); + this.outQ.multiply(this.deltaQ); + + this.previousQ.copy(currentQ); + + return this.outQ; +}; + + +module.exports = PosePredictor; + +},{"./three-math.js":9}],8:[function(_dereq_,module,exports){ +function SensorSample(sample, timestampS) { + this.set(sample, timestampS); +}; + +SensorSample.prototype.set = function(sample, timestampS) { + this.sample = sample; + this.timestampS = timestampS; +}; + +SensorSample.prototype.copy = function(sensorSample) { + this.set(sensorSample.sample, sensorSample.timestampS); +}; + +module.exports = SensorSample; + +},{}],9:[function(_dereq_,module,exports){ +/* + * A subset of THREE.js, providing mostly quaternion and euler-related + * operations, manually lifted from + * https://github.com/mrdoob/three.js/tree/master/src/math, as of 9c30286b38df039fca389989ff06ea1c15d6bad1 + */ + +// Only use if the real THREE is not provided. +var THREE = window.THREE || {}; + +// If some piece of THREE is missing, fill it in here. +if (!THREE.Quaternion || !THREE.Vector3 || !THREE.Vector2 || !THREE.Euler || !THREE.Math) { +console.log('No THREE.js found.'); + + +/*** START Quaternion ***/ + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://exocortex.com + */ + +THREE.Quaternion = function ( x, y, z, w ) { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._w = ( w !== undefined ) ? w : 1; + +}; + +THREE.Quaternion.prototype = { + + constructor: THREE.Quaternion, + + _x: 0,_y: 0, _z: 0, _w: 0, + + get x () { + + return this._x; + + }, + + set x ( value ) { + + this._x = value; + this.onChangeCallback(); + + }, + + get y () { + + return this._y; + + }, + + set y ( value ) { + + this._y = value; + this.onChangeCallback(); + + }, + + get z () { + + return this._z; + + }, + + set z ( value ) { + + this._z = value; + this.onChangeCallback(); + + }, + + get w () { + + return this._w; + + }, + + set w ( value ) { + + this._w = value; + this.onChangeCallback(); + + }, + + set: function ( x, y, z, w ) { + + this._x = x; + this._y = y; + this._z = z; + this._w = w; + + this.onChangeCallback(); + + return this; + + }, + + copy: function ( quaternion ) { + + this._x = quaternion.x; + this._y = quaternion.y; + this._z = quaternion.z; + this._w = quaternion.w; + + this.onChangeCallback(); + + return this; + + }, + + setFromEuler: function ( euler, update ) { + + if ( euler instanceof THREE.Euler === false ) { + + throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + } + + // http://www.mathworks.com/matlabcentral/fileexchange/ + // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ + // content/SpinCalc.m + + var c1 = Math.cos( euler._x / 2 ); + var c2 = Math.cos( euler._y / 2 ); + var c3 = Math.cos( euler._z / 2 ); + var s1 = Math.sin( euler._x / 2 ); + var s2 = Math.sin( euler._y / 2 ); + var s3 = Math.sin( euler._z / 2 ); + + if ( euler.order === 'XYZ' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( euler.order === 'YXZ' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } else if ( euler.order === 'ZXY' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( euler.order === 'ZYX' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } else if ( euler.order === 'YZX' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( euler.order === 'XZY' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } + + if ( update !== false ) this.onChangeCallback(); + + return this; + + }, + + setFromAxisAngle: function ( axis, angle ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm + + // assumes axis is normalized + + var halfAngle = angle / 2, s = Math.sin( halfAngle ); + + this._x = axis.x * s; + this._y = axis.y * s; + this._z = axis.z * s; + this._w = Math.cos( halfAngle ); + + this.onChangeCallback(); + + return this; + + }, + + setFromRotationMatrix: function ( m ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements, + + m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], + m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], + m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], + + trace = m11 + m22 + m33, + s; + + if ( trace > 0 ) { + + s = 0.5 / Math.sqrt( trace + 1.0 ); + + this._w = 0.25 / s; + this._x = ( m32 - m23 ) * s; + this._y = ( m13 - m31 ) * s; + this._z = ( m21 - m12 ) * s; + + } else if ( m11 > m22 && m11 > m33 ) { + + s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); + + this._w = ( m32 - m23 ) / s; + this._x = 0.25 * s; + this._y = ( m12 + m21 ) / s; + this._z = ( m13 + m31 ) / s; + + } else if ( m22 > m33 ) { + + s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); + + this._w = ( m13 - m31 ) / s; + this._x = ( m12 + m21 ) / s; + this._y = 0.25 * s; + this._z = ( m23 + m32 ) / s; + + } else { + + s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); + + this._w = ( m21 - m12 ) / s; + this._x = ( m13 + m31 ) / s; + this._y = ( m23 + m32 ) / s; + this._z = 0.25 * s; + + } + + this.onChangeCallback(); + + return this; + + }, + + setFromUnitVectors: function () { + + // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final + + // assumes direction vectors vFrom and vTo are normalized + + var v1, r; + + var EPS = 0.000001; + + return function ( vFrom, vTo ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + r = vFrom.dot( vTo ) + 1; + + if ( r < EPS ) { + + r = 0; + + if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { + + v1.set( - vFrom.y, vFrom.x, 0 ); + + } else { + + v1.set( 0, - vFrom.z, vFrom.y ); + + } + + } else { + + v1.crossVectors( vFrom, vTo ); + + } + + this._x = v1.x; + this._y = v1.y; + this._z = v1.z; + this._w = r; + + this.normalize(); + + return this; + + } + + }(), + + inverse: function () { + + this.conjugate().normalize(); + + return this; + + }, + + conjugate: function () { + + this._x *= - 1; + this._y *= - 1; + this._z *= - 1; + + this.onChangeCallback(); + + return this; + + }, + + dot: function ( v ) { + + return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; + + }, + + lengthSq: function () { + + return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; + + }, + + length: function () { + + return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); + + }, + + normalize: function () { + + var l = this.length(); + + if ( l === 0 ) { + + this._x = 0; + this._y = 0; + this._z = 0; + this._w = 1; + + } else { + + l = 1 / l; + + this._x = this._x * l; + this._y = this._y * l; + this._z = this._z * l; + this._w = this._w * l; + + } + + this.onChangeCallback(); + + return this; + + }, + + multiply: function ( q, p ) { + + if ( p !== undefined ) { + + console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); + return this.multiplyQuaternions( q, p ); + + } + + return this.multiplyQuaternions( this, q ); + + }, + + multiplyQuaternions: function ( a, b ) { + + // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm + + var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; + var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; + + this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; + this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; + this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; + this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; + + this.onChangeCallback(); + + return this; + + }, + + multiplyVector3: function ( vector ) { + + console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); + return vector.applyQuaternion( this ); + + }, + + slerp: function ( qb, t ) { + + if ( t === 0 ) return this; + if ( t === 1 ) return this.copy( qb ); + + var x = this._x, y = this._y, z = this._z, w = this._w; + + // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + + var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; + + if ( cosHalfTheta < 0 ) { + + this._w = - qb._w; + this._x = - qb._x; + this._y = - qb._y; + this._z = - qb._z; + + cosHalfTheta = - cosHalfTheta; + + } else { + + this.copy( qb ); + + } + + if ( cosHalfTheta >= 1.0 ) { + + this._w = w; + this._x = x; + this._y = y; + this._z = z; + + return this; + + } + + var halfTheta = Math.acos( cosHalfTheta ); + var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); + + if ( Math.abs( sinHalfTheta ) < 0.001 ) { + + this._w = 0.5 * ( w + this._w ); + this._x = 0.5 * ( x + this._x ); + this._y = 0.5 * ( y + this._y ); + this._z = 0.5 * ( z + this._z ); + + return this; + + } + + var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, + ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; + + this._w = ( w * ratioA + this._w * ratioB ); + this._x = ( x * ratioA + this._x * ratioB ); + this._y = ( y * ratioA + this._y * ratioB ); + this._z = ( z * ratioA + this._z * ratioB ); + + this.onChangeCallback(); + + return this; + + }, + + equals: function ( quaternion ) { + + return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this._x = array[ offset ]; + this._y = array[ offset + 1 ]; + this._z = array[ offset + 2 ]; + this._w = array[ offset + 3 ]; + + this.onChangeCallback(); + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this._x; + array[ offset + 1 ] = this._y; + array[ offset + 2 ] = this._z; + array[ offset + 3 ] = this._w; + + return array; + + }, + + onChange: function ( callback ) { + + this.onChangeCallback = callback; + + return this; + + }, + + onChangeCallback: function () {}, + + clone: function () { + + return new THREE.Quaternion( this._x, this._y, this._z, this._w ); + + } + +}; + +THREE.Quaternion.slerp = function ( qa, qb, qm, t ) { + + return qm.copy( qa ).slerp( qb, t ); + +} + +/*** END Quaternion ***/ +/*** START Vector2 ***/ +/** + * @author mrdoob / http://mrdoob.com/ + * @author philogb / http://blog.thejit.org/ + * @author egraether / http://egraether.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.Vector2 = function ( x, y ) { + + this.x = x || 0; + this.y = y || 0; + +}; + +THREE.Vector2.prototype = { + + constructor: THREE.Vector2, + + set: function ( x, y ) { + + this.x = x; + this.y = y; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + + return this; + + }, + + multiply: function ( v ) { + + this.x *= v.x; + this.y *= v.y; + + return this; + + }, + + multiplyScalar: function ( s ) { + + this.x *= s; + this.y *= s; + + return this; + + }, + + divide: function ( v ) { + + this.x /= v.x; + this.y /= v.y; + + return this; + + }, + + divideScalar: function ( scalar ) { + + if ( scalar !== 0 ) { + + var invScalar = 1 / scalar; + + this.x *= invScalar; + this.y *= invScalar; + + } else { + + this.x = 0; + this.y = 0; + + } + + return this; + + }, + + min: function ( v ) { + + if ( this.x > v.x ) { + + this.x = v.x; + + } + + if ( this.y > v.y ) { + + this.y = v.y; + + } + + return this; + + }, + + max: function ( v ) { + + if ( this.x < v.x ) { + + this.x = v.x; + + } + + if ( this.y < v.y ) { + + this.y = v.y; + + } + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + if ( this.x < min.x ) { + + this.x = min.x; + + } else if ( this.x > max.x ) { + + this.x = max.x; + + } + + if ( this.y < min.y ) { + + this.y = min.y; + + } else if ( this.y > max.y ) { + + this.y = max.y; + + } + + return this; + }, + + clampScalar: ( function () { + + var min, max; + + return function ( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector2(); + max = new THREE.Vector2(); + + } + + min.set( minVal, minVal ); + max.set( maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + } )(), + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + distanceTo: function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared: function ( v ) { + + var dx = this.x - v.x, dy = this.y - v.y; + return dx * dx + dy * dy; + + }, + + setLength: function ( l ) { + + var oldLength = this.length(); + + if ( oldLength !== 0 && l !== oldLength ) { + + this.multiplyScalar( l / oldLength ); + } + + return this; + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + + return array; + + }, + + fromAttribute: function ( attribute, index, offset ) { + + if ( offset === undefined ) offset = 0; + + index = index * attribute.itemSize + offset; + + this.x = attribute.array[ index ]; + this.y = attribute.array[ index + 1 ]; + + return this; + + }, + + clone: function () { + + return new THREE.Vector2( this.x, this.y ); + + } + +}; +/*** END Vector2 ***/ +/*** START Vector3 ***/ + +/** + * @author mrdoob / http://mrdoob.com/ + * @author *kile / http://kile.stravaganza.org/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Vector3 = function ( x, y, z ) { + + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + +}; + +THREE.Vector3.prototype = { + + constructor: THREE.Vector3, + + set: function ( x, y, z ) { + + this.x = x; + this.y = y; + this.z = z; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setZ: function ( z ) { + + this.z = z; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + case 2: this.z = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + case 2: return this.z; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + this.z = v.z; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + this.z += v.z; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + this.z += s; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + + return this; + + }, + + multiply: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); + return this.multiplyVectors( v, w ); + + } + + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; + + return this; + + }, + + multiplyScalar: function ( scalar ) { + + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + + return this; + + }, + + multiplyVectors: function ( a, b ) { + + this.x = a.x * b.x; + this.y = a.y * b.y; + this.z = a.z * b.z; + + return this; + + }, + + applyEuler: function () { + + var quaternion; + + return function ( euler ) { + + if ( euler instanceof THREE.Euler === false ) { + + console.error( 'THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + + } + + if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); + + this.applyQuaternion( quaternion.setFromEuler( euler ) ); + + return this; + + }; + + }(), + + applyAxisAngle: function () { + + var quaternion; + + return function ( axis, angle ) { + + if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); + + this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); + + return this; + + }; + + }(), + + applyMatrix3: function ( m ) { + + var x = this.x; + var y = this.y; + var z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; + this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; + this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; + + return this; + + }, + + applyMatrix4: function ( m ) { + + // input: THREE.Matrix4 affine matrix + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ]; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ]; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ]; + + return this; + + }, + + applyProjection: function ( m ) { + + // input: THREE.Matrix4 projection matrix + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide + + this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d; + this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d; + this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d; + + return this; + + }, + + applyQuaternion: function ( q ) { + + var x = this.x; + var y = this.y; + var z = this.z; + + var qx = q.x; + var qy = q.y; + var qz = q.z; + var qw = q.w; + + // calculate quat * vector + + var ix = qw * x + qy * z - qz * y; + var iy = qw * y + qz * x - qx * z; + var iz = qw * z + qx * y - qy * x; + var iw = - qx * x - qy * y - qz * z; + + // calculate result * inverse quat + + this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; + this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; + this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; + + return this; + + }, + + project: function () { + + var matrix; + + return function ( camera ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + + matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) ); + return this.applyProjection( matrix ); + + }; + + }(), + + unproject: function () { + + var matrix; + + return function ( camera ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + + matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) ); + return this.applyProjection( matrix ); + + }; + + }(), + + transformDirection: function ( m ) { + + // input: THREE.Matrix4 affine matrix + // vector interpreted as a direction + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; + + this.normalize(); + + return this; + + }, + + divide: function ( v ) { + + this.x /= v.x; + this.y /= v.y; + this.z /= v.z; + + return this; + + }, + + divideScalar: function ( scalar ) { + + if ( scalar !== 0 ) { + + var invScalar = 1 / scalar; + + this.x *= invScalar; + this.y *= invScalar; + this.z *= invScalar; + + } else { + + this.x = 0; + this.y = 0; + this.z = 0; + + } + + return this; + + }, + + min: function ( v ) { + + if ( this.x > v.x ) { + + this.x = v.x; + + } + + if ( this.y > v.y ) { + + this.y = v.y; + + } + + if ( this.z > v.z ) { + + this.z = v.z; + + } + + return this; + + }, + + max: function ( v ) { + + if ( this.x < v.x ) { + + this.x = v.x; + + } + + if ( this.y < v.y ) { + + this.y = v.y; + + } + + if ( this.z < v.z ) { + + this.z = v.z; + + } + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + if ( this.x < min.x ) { + + this.x = min.x; + + } else if ( this.x > max.x ) { + + this.x = max.x; + + } + + if ( this.y < min.y ) { + + this.y = min.y; + + } else if ( this.y > max.y ) { + + this.y = max.y; + + } + + if ( this.z < min.z ) { + + this.z = min.z; + + } else if ( this.z > max.z ) { + + this.z = max.z; + + } + + return this; + + }, + + clampScalar: ( function () { + + var min, max; + + return function ( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector3(); + max = new THREE.Vector3(); + + } + + min.set( minVal, minVal, minVal ); + max.set( maxVal, maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + } )(), + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y + this.z * v.z; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y + this.z * this.z; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); + + }, + + lengthManhattan: function () { + + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + setLength: function ( l ) { + + var oldLength = this.length(); + + if ( oldLength !== 0 && l !== oldLength ) { + + this.multiplyScalar( l / oldLength ); + } + + return this; + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; + + return this; + + }, + + cross: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); + return this.crossVectors( v, w ); + + } + + var x = this.x, y = this.y, z = this.z; + + this.x = y * v.z - z * v.y; + this.y = z * v.x - x * v.z; + this.z = x * v.y - y * v.x; + + return this; + + }, + + crossVectors: function ( a, b ) { + + var ax = a.x, ay = a.y, az = a.z; + var bx = b.x, by = b.y, bz = b.z; + + this.x = ay * bz - az * by; + this.y = az * bx - ax * bz; + this.z = ax * by - ay * bx; + + return this; + + }, + + projectOnVector: function () { + + var v1, dot; + + return function ( vector ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + v1.copy( vector ).normalize(); + + dot = this.dot( v1 ); + + return this.copy( v1 ).multiplyScalar( dot ); + + }; + + }(), + + projectOnPlane: function () { + + var v1; + + return function ( planeNormal ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + v1.copy( this ).projectOnVector( planeNormal ); + + return this.sub( v1 ); + + } + + }(), + + reflect: function () { + + // reflect incident vector off plane orthogonal to normal + // normal is assumed to have unit length + + var v1; + + return function ( normal ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); + + } + + }(), + + angleTo: function ( v ) { + + var theta = this.dot( v ) / ( this.length() * v.length() ); + + // clamp, to handle numerical problems + + return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) ); + + }, + + distanceTo: function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared: function ( v ) { + + var dx = this.x - v.x; + var dy = this.y - v.y; + var dz = this.z - v.z; + + return dx * dx + dy * dy + dz * dz; + + }, + + setEulerFromRotationMatrix: function ( m, order ) { + + console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); + + }, + + setEulerFromQuaternion: function ( q, order ) { + + console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); + + }, + + getPositionFromMatrix: function ( m ) { + + console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); + + return this.setFromMatrixPosition( m ); + + }, + + getScaleFromMatrix: function ( m ) { + + console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); + + return this.setFromMatrixScale( m ); + }, + + getColumnFromMatrix: function ( index, matrix ) { + + console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); + + return this.setFromMatrixColumn( index, matrix ); + + }, + + setFromMatrixPosition: function ( m ) { + + this.x = m.elements[ 12 ]; + this.y = m.elements[ 13 ]; + this.z = m.elements[ 14 ]; + + return this; + + }, + + setFromMatrixScale: function ( m ) { + + var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length(); + var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length(); + var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length(); + + this.x = sx; + this.y = sy; + this.z = sz; + + return this; + }, + + setFromMatrixColumn: function ( index, matrix ) { + + var offset = index * 4; + + var me = matrix.elements; + + this.x = me[ offset ]; + this.y = me[ offset + 1 ]; + this.z = me[ offset + 2 ]; + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; + + return array; + + }, + + fromAttribute: function ( attribute, index, offset ) { + + if ( offset === undefined ) offset = 0; + + index = index * attribute.itemSize + offset; + + this.x = attribute.array[ index ]; + this.y = attribute.array[ index + 1 ]; + this.z = attribute.array[ index + 2 ]; + + return this; + + }, + + clone: function () { + + return new THREE.Vector3( this.x, this.y, this.z ); + + } + +}; +/*** END Vector3 ***/ +/*** START Euler ***/ +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://exocortex.com + */ + +THREE.Euler = function ( x, y, z, order ) { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._order = order || THREE.Euler.DefaultOrder; + +}; + +THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; + +THREE.Euler.DefaultOrder = 'XYZ'; + +THREE.Euler.prototype = { + + constructor: THREE.Euler, + + _x: 0, _y: 0, _z: 0, _order: THREE.Euler.DefaultOrder, + + get x () { + + return this._x; + + }, + + set x ( value ) { + + this._x = value; + this.onChangeCallback(); + + }, + + get y () { + + return this._y; + + }, + + set y ( value ) { + + this._y = value; + this.onChangeCallback(); + + }, + + get z () { + + return this._z; + + }, + + set z ( value ) { + + this._z = value; + this.onChangeCallback(); + + }, + + get order () { + + return this._order; + + }, + + set order ( value ) { + + this._order = value; + this.onChangeCallback(); + + }, + + set: function ( x, y, z, order ) { + + this._x = x; + this._y = y; + this._z = z; + this._order = order || this._order; + + this.onChangeCallback(); + + return this; + + }, + + copy: function ( euler ) { + + this._x = euler._x; + this._y = euler._y; + this._z = euler._z; + this._order = euler._order; + + this.onChangeCallback(); + + return this; + + }, + + setFromRotationMatrix: function ( m, order, update ) { + + var clamp = THREE.Math.clamp; + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements; + var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; + var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; + var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; + + order = order || this._order; + + if ( order === 'XYZ' ) { + + this._y = Math.asin( clamp( m13, - 1, 1 ) ); + + if ( Math.abs( m13 ) < 0.99999 ) { + + this._x = Math.atan2( - m23, m33 ); + this._z = Math.atan2( - m12, m11 ); + + } else { + + this._x = Math.atan2( m32, m22 ); + this._z = 0; + + } + + } else if ( order === 'YXZ' ) { + + this._x = Math.asin( - clamp( m23, - 1, 1 ) ); + + if ( Math.abs( m23 ) < 0.99999 ) { + + this._y = Math.atan2( m13, m33 ); + this._z = Math.atan2( m21, m22 ); + + } else { + + this._y = Math.atan2( - m31, m11 ); + this._z = 0; + + } + + } else if ( order === 'ZXY' ) { + + this._x = Math.asin( clamp( m32, - 1, 1 ) ); + + if ( Math.abs( m32 ) < 0.99999 ) { + + this._y = Math.atan2( - m31, m33 ); + this._z = Math.atan2( - m12, m22 ); + + } else { + + this._y = 0; + this._z = Math.atan2( m21, m11 ); + + } + + } else if ( order === 'ZYX' ) { + + this._y = Math.asin( - clamp( m31, - 1, 1 ) ); + + if ( Math.abs( m31 ) < 0.99999 ) { + + this._x = Math.atan2( m32, m33 ); + this._z = Math.atan2( m21, m11 ); + + } else { + + this._x = 0; + this._z = Math.atan2( - m12, m22 ); + + } + + } else if ( order === 'YZX' ) { + + this._z = Math.asin( clamp( m21, - 1, 1 ) ); + + if ( Math.abs( m21 ) < 0.99999 ) { + + this._x = Math.atan2( - m23, m22 ); + this._y = Math.atan2( - m31, m11 ); + + } else { + + this._x = 0; + this._y = Math.atan2( m13, m33 ); + + } + + } else if ( order === 'XZY' ) { + + this._z = Math.asin( - clamp( m12, - 1, 1 ) ); + + if ( Math.abs( m12 ) < 0.99999 ) { + + this._x = Math.atan2( m32, m22 ); + this._y = Math.atan2( m13, m11 ); + + } else { + + this._x = Math.atan2( - m23, m33 ); + this._y = 0; + + } + + } else { + + console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order ) + + } + + this._order = order; + + if ( update !== false ) this.onChangeCallback(); + + return this; + + }, + + setFromQuaternion: function () { + + var matrix; + + return function ( q, order, update ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + matrix.makeRotationFromQuaternion( q ); + this.setFromRotationMatrix( matrix, order, update ); + + return this; + + }; + + }(), + + setFromVector3: function ( v, order ) { + + return this.set( v.x, v.y, v.z, order || this._order ); + + }, + + reorder: function () { + + // WARNING: this discards revolution information -bhouston + + var q = new THREE.Quaternion(); + + return function ( newOrder ) { + + q.setFromEuler( this ); + this.setFromQuaternion( q, newOrder ); + + }; + + }(), + + equals: function ( euler ) { + + return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); + + }, + + fromArray: function ( array ) { + + this._x = array[ 0 ]; + this._y = array[ 1 ]; + this._z = array[ 2 ]; + if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; + + this.onChangeCallback(); + + return this; + + }, + + toArray: function () { + + return [ this._x, this._y, this._z, this._order ]; + + }, + + toVector3: function ( optionalResult ) { + + if ( optionalResult ) { + + return optionalResult.set( this._x, this._y, this._z ); + + } else { + + return new THREE.Vector3( this._x, this._y, this._z ); + + } + + }, + + onChange: function ( callback ) { + + this.onChangeCallback = callback; + + return this; + + }, + + onChangeCallback: function () {}, + + clone: function () { + + return new THREE.Euler( this._x, this._y, this._z, this._order ); + + } + +}; +/*** END Euler ***/ +/*** START Math ***/ +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Math = { + + generateUUID: function () { + + // http://www.broofa.com/Tools/Math.uuid.htm + + var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' ); + var uuid = new Array( 36 ); + var rnd = 0, r; + + return function () { + + for ( var i = 0; i < 36; i ++ ) { + + if ( i == 8 || i == 13 || i == 18 || i == 23 ) { + + uuid[ i ] = '-'; + + } else if ( i == 14 ) { + + uuid[ i ] = '4'; + + } else { + + if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0; + r = rnd & 0xf; + rnd = rnd >> 4; + uuid[ i ] = chars[ ( i == 19 ) ? ( r & 0x3 ) | 0x8 : r ]; + + } + } + + return uuid.join( '' ); + + }; + + }(), + + // Clamp value to range + + clamp: function ( x, a, b ) { + + return ( x < a ) ? a : ( ( x > b ) ? b : x ); + + }, + + // Clamp value to range to range + + mapLinear: function ( x, a1, a2, b1, b2 ) { + + return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); + + }, + + // http://en.wikipedia.org/wiki/Smoothstep + + smoothstep: function ( x, min, max ) { + + if ( x <= min ) return 0; + if ( x >= max ) return 1; + + x = ( x - min ) / ( max - min ); + + return x * x * ( 3 - 2 * x ); + + }, + + smootherstep: function ( x, min, max ) { + + if ( x <= min ) return 0; + if ( x >= max ) return 1; + + x = ( x - min ) / ( max - min ); + + return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); + + }, + + // Random float from <0, 1> with 16 bits of randomness + // (standard Math.random() creates repetitive patterns when applied over larger space) + + random16: function () { + + return ( 65280 * Math.random() + 255 * Math.random() ) / 65535; + + }, + + // Random integer from interval + + randInt: function ( low, high ) { + + return Math.floor( this.randFloat( low, high ) ); + + }, + + // Random float from interval + + randFloat: function ( low, high ) { + + return low + Math.random() * ( high - low ); + + }, + + // Random float from <-range/2, range/2> interval + + randFloatSpread: function ( range ) { + + return range * ( 0.5 - Math.random() ); + + }, + + degToRad: function () { + + var degreeToRadiansFactor = Math.PI / 180; + + return function ( degrees ) { + + return degrees * degreeToRadiansFactor; + + }; + + }(), + + radToDeg: function () { + + var radianToDegreesFactor = 180 / Math.PI; + + return function ( radians ) { + + return radians * radianToDegreesFactor; + + }; + + }(), + + isPowerOfTwo: function ( value ) { + + return ( value & ( value - 1 ) ) === 0 && value !== 0; + + }, + + nextPowerOfTwo: function ( value ) { + + value --; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value ++; + + return value; + } + +}; + +/*** END Math ***/ + +} + +module.exports = THREE; + +},{}],10:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var THREE = _dereq_('./three-math.js'); +var Util = _dereq_('./util.js'); + +var ROTATE_SPEED = 0.5; +/** + * Provides a quaternion responsible for pre-panning the scene before further + * transformations due to device sensors. + */ +function TouchPanner() { + window.addEventListener('touchstart', this.onTouchStart_.bind(this)); + window.addEventListener('touchmove', this.onTouchMove_.bind(this)); + window.addEventListener('touchend', this.onTouchEnd_.bind(this)); + + this.isTouching = false; + this.rotateStart = new THREE.Vector2(); + this.rotateEnd = new THREE.Vector2(); + this.rotateDelta = new THREE.Vector2(); + + this.theta = 0; + this.orientation = new THREE.Quaternion(); +} + +TouchPanner.prototype.getOrientation = function() { + this.orientation.setFromEuler(new THREE.Euler(0, 0, this.theta)); + return this.orientation; +}; + +TouchPanner.prototype.resetSensor = function() { + this.theta = 0; +}; + +TouchPanner.prototype.onTouchStart_ = function(e) { + // Only respond if there is exactly one touch. + if (e.touches.length != 1) { + return; + } + this.rotateStart.set(e.touches[0].pageX, e.touches[0].pageY); + this.isTouching = true; +}; + +TouchPanner.prototype.onTouchMove_ = function(e) { + if (!this.isTouching) { + return; + } + this.rotateEnd.set(e.touches[0].pageX, e.touches[0].pageY); + this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart); + this.rotateStart.copy(this.rotateEnd); + + // On iOS, direction is inverted. + if (Util.isIOS()) { + this.rotateDelta.x *= -1; + } + + var element = document.body; + this.theta += 2 * Math.PI * this.rotateDelta.x / element.clientWidth * ROTATE_SPEED; +}; + +TouchPanner.prototype.onTouchEnd_ = function(e) { + this.isTouching = false; +}; + +module.exports = TouchPanner; + +},{"./three-math.js":9,"./util.js":11}],11:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var Util = window.Util || {}; + +Util.MIN_TIMESTEP = 0.001; +Util.MAX_TIMESTEP = 1; + +Util.clamp = function(value, min, max) { + return Math.min(Math.max(min, value), max); +}; + +Util.isIOS = function() { + return /iPad|iPhone|iPod/.test(navigator.platform); +}; + +Util.isFirefoxAndroid = function() { + return navigator.userAgent.indexOf('Firefox') !== -1 && navigator.userAgent.indexOf('Android') !== -1; +} + +// Helper method to validate the time steps of sensor timestamps. +Util.isTimestampDeltaValid = function(timestampDeltaS) { + if (isNaN(timestampDeltaS)) { + return false; + } + if (timestampDeltaS <= Util.MIN_TIMESTEP) { + return false; + } + if (timestampDeltaS > Util.MAX_TIMESTEP) { + return false; + } + return true; +} + +module.exports = Util; + +},{}],12:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var CardboardHMDVRDevice = _dereq_('./cardboard-hmd-vr-device.js'); +//var OrientationPositionSensorVRDevice = require('./orientation-position-sensor-vr-device.js'); +var FusionPositionSensorVRDevice = _dereq_('./fusion-position-sensor-vr-device.js'); +var MouseKeyboardPositionSensorVRDevice = _dereq_('./mouse-keyboard-position-sensor-vr-device.js'); +// Uncomment to add positional tracking via webcam. +//var WebcamPositionSensorVRDevice = require('./webcam-position-sensor-vr-device.js'); +var HMDVRDevice = _dereq_('./base.js').HMDVRDevice; +var PositionSensorVRDevice = _dereq_('./base.js').PositionSensorVRDevice; + +function WebVRPolyfill() { + this.devices = []; + + if (!this.isWebVRAvailable()) { + this.enablePolyfill(); + } +} + +WebVRPolyfill.prototype.isWebVRAvailable = function() { + return ('getVRDevices' in navigator) || ('mozGetVRDevices' in navigator); +}; + + +WebVRPolyfill.prototype.enablePolyfill = function() { + // Initialize our virtual VR devices. + if (this.isCardboardCompatible()) { + this.devices.push(new CardboardHMDVRDevice()); + } + + // Polyfill using the right position sensor. + if (this.isMobile()) { + //this.devices.push(new OrientationPositionSensorVRDevice()); + this.devices.push(new FusionPositionSensorVRDevice()); + } else { + if (!WebVRConfig.MOUSE_KEYBOARD_CONTROLS_DISABLED) { + this.devices.push(new MouseKeyboardPositionSensorVRDevice()); + } + // Uncomment to add positional tracking via webcam. + //this.devices.push(new WebcamPositionSensorVRDevice()); + } + + // Provide navigator.getVRDevices. + navigator.getVRDevices = this.getVRDevices.bind(this); + + // Provide the CardboardHMDVRDevice and PositionSensorVRDevice objects. + window.HMDVRDevice = HMDVRDevice; + window.PositionSensorVRDevice = PositionSensorVRDevice; +}; + +WebVRPolyfill.prototype.getVRDevices = function() { + var devices = this.devices; + return new Promise(function(resolve, reject) { + try { + resolve(devices); + } catch (e) { + reject(e); + } + }); +}; + +/** + * Determine if a device is mobile. + */ +WebVRPolyfill.prototype.isMobile = function() { + return /Android/i.test(navigator.userAgent) || + /iPhone|iPad|iPod/i.test(navigator.userAgent); +}; + +WebVRPolyfill.prototype.isCardboardCompatible = function() { + // For now, support all iOS and Android devices. + // Also enable the WebVRConfig.FORCE_VR flag for debugging. + return this.isMobile() || WebVRConfig.FORCE_ENABLE_VR; +}; + +module.exports = WebVRPolyfill; + +},{"./base.js":1,"./cardboard-hmd-vr-device.js":2,"./fusion-position-sensor-vr-device.js":4,"./mouse-keyboard-position-sensor-vr-device.js":6}]},{},[5]); + +},{}],8:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var MODE_LABELS = { + 0: 'UNKNOWN', + 1: 'NORMAL', + 2: 'MAGIC_WINDOW', + 3: 'VR' +}; + +function Analytics() { + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-35315454-8', 'auto'); + ga('send', 'pageview'); + + this.lastModeChangeTime = window.performance.now(); + this.lastModeLabel = WebVRManager.Modes.UNKNOWN; +} + +Analytics.prototype.logModeChanged = function(mode) { + var modeLabel = MODE_LABELS[mode]; + var lastModeLabel = MODE_LABELS[this.lastMode]; + + console.log('Analytics: going from mode %s to %s', lastModeLabel, modeLabel); + + ga('send', 'screenview', { + appName: 'EmbedVR', + screenName: modeLabel + }); + + var now = window.performance.now(); + var msSinceLastModeChange = Math.round(now - this.lastModeChangeTime); + ga('send', 'timing', 'Time spent in mode', lastModeLabel, msSinceLastModeChange); + + this.lastModeChangeTime = now; + this.lastMode = mode; +} + +window.analytics = new Analytics(); + +},{}],9:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * Sits in an embedded iframe, receiving DeviceMotion messages from a containing + * iFrame. These messages are converted into synthetic DeviceMotion events. + * + * This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=150072. + */ +function DeviceMotionReceiver() { + window.addEventListener('message', this.onMessage_.bind(this), false); +} + +DeviceMotionReceiver.prototype.onMessage_ = function(event) { + var message = event.data; + if (message.type !== 'DeviceMotion') { + console.warn('Got unknown message of type %s from %s', message.type, message.origin); + return; + } + + console.log('onMessage_', event); + + // Synthesize a DeviceMotion event. + this.synthesizeDeviceMotionEvent_(message.deviceMotionEvent); +}; + +DeviceMotionReceiver.prototype.synthesizeDeviceMotionEvent_ = function(eventData) { + var type = 'devicemotion-iframe'; + var canBubble = false; + var cancelable = false; + + var dme = document.createEvent('DeviceMotionEvent'); + dme.initDeviceMotionEvent(type, canBubble, cancelable, + eventData.acceleration, + eventData.accelerationIncludingGravity, + eventData.rotationRate, + eventData.interval); + + window.dispatchEvent(dme); +}; + +module.exports = DeviceMotionReceiver; + +},{}],10:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +function Emitter() { + this.initEmitter(); +} + +Emitter.prototype.initEmitter = function() { + this.callbacks = {}; +}; + +Emitter.prototype.emit = function(eventName) { + var callbacks = this.callbacks[eventName]; + if (!callbacks) { + console.log('No valid callback specified.'); + return; + } + var args = [].slice.call(arguments) + // Eliminate the first param (the callback). + args.shift(); + for (var i = 0; i < callbacks.length; i++) { + callbacks[i].apply(this, args); + } +}; + +Emitter.prototype.on = function(eventName, callback) { + if (eventName in this.callbacks) { + var cbs = this.callbacks[eventName] + if (cbs.indexOf(callback) == -1) { + cbs.push(callback); + } + } else { + this.callbacks[eventName] = [callback]; + } +}; + +Emitter.prototype.removeListener = function(eventName, callback) { + if (!(eventName in this.callbacks)) { + return; + } + var cbs = this.callbacks[eventName]; + var ind = cbs.indexOf(callback); + if (ind == -1) { + console.warn('No matching callback found'); + return; + } + cbs.splice(ind, 1); +}; + +module.exports = Emitter; + +},{}],11:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var Eyes = { + LEFT: 0, + RIGHT: 1 +}; + +module.exports = Eyes; + +},{}],12:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Shows a 2D loading indicator while various pieces of EmbedVR load. + */ +function LoadingIndicator() { + this.el = this.build_(); + document.body.appendChild(this.el); + this.show(); +} + +LoadingIndicator.prototype.build_ = function() { + var overlay = document.createElement('div'); + var s = overlay.style; + s.position = 'fixed'; + s.top = 0; + s.left = 0; + s.width = '100%'; + s.height = '100%'; + s.background = '#eee'; + var img = document.createElement('img'); + img.src = 'images/loading.gif'; + var s = img.style; + s.position = 'absolute'; + s.top = '50%'; + s.left = '50%'; + s.transform = 'translate(-50%, -50%)'; + + overlay.appendChild(img); + return overlay; +}; + +LoadingIndicator.prototype.hide = function() { + this.el.style.display = 'none'; +}; + +LoadingIndicator.prototype.show = function() { + this.el.style.display = 'block'; +}; + +module.exports = LoadingIndicator; + +},{}],13:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Disable distortion provided by the boilerplate because we are doing +// vertex-based distortion. +WebVRConfig = window.WebVRConfig || {} +WebVRConfig.PREVENT_DISTORTION = true; + +// Initialize the loading indicator as quickly as possible to give the user +// immediate feedback. +var LoadingIndicator = _dereq_('./loading-indicator'); +var loadIndicator = new LoadingIndicator(); + +// Include relevant polyfills. +_dereq_('../node_modules/webvr-polyfill/build/webvr-polyfill'); +var ES6Promise = _dereq_('../node_modules/es6-promise/dist/es6-promise.min'); +// Polyfill ES6 promises for IE. +ES6Promise.polyfill(); + +var PhotosphereRenderer = _dereq_('./photosphere-renderer'); +var SceneLoader = _dereq_('./scene-loader'); +var Stats = _dereq_('../node_modules/stats-js/build/stats.min'); +var Util = _dereq_('./util'); + +// Include the DeviceMotionReceiver for the iOS cross domain iframe workaround. +// This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=150072. +var DeviceMotionReceiver = _dereq_('./device-motion-receiver'); +var dmr = new DeviceMotionReceiver(); + + +window.addEventListener('load', init); + +var stats = new Stats(); + +var loader = new SceneLoader(); +loader.on('error', onSceneError); +loader.on('load', onSceneLoad); + +var renderer = new PhotosphereRenderer(); +renderer.on('error', onRenderError); + +var videoElement = null; +// TODO: Make this not global. +// Currently global in order to allow callbacks. +var loadedScene = null; + +function init() { + if (!Util.isWebGLEnabled()) { + showError('WebGL not supported.'); + return; + } + // Load the scene. + loader.loadScene(); + + if (Util.getQueryParameter('debug')) { + showStats(); + } +} + +function loadImage(src, params) { + renderer.on('load', onRenderLoad); + renderer.setPhotosphere(src, params); +} + +function onSceneLoad(scene) { + if (!scene || !scene.isComplete()) { + showError('Scene failed to load'); + return; + } + + loadedScene = scene; + + var params = { + isStereo: scene.isStereo, + } + renderer.setDefaultLookDirection(scene.yaw || 0); + + if (scene.preview) { + var onPreviewLoad = function() { + loadIndicator.hide(); + renderer.removeListener('load', onPreviewLoad); + renderer.setPhotosphere(scene.image, params); + } + renderer.removeListener('load', onRenderLoad); + renderer.on('load', onPreviewLoad); + renderer.setPhotosphere(scene.preview, params); + } else if (scene.video) { + if (Util.isIOS() || Util.isIE11()) { + // On iOS and IE 11, if an 'image' param is provided, load it instead of + // showing an error. + // + // TODO(smus): Once video textures are supported, remove this fallback. + if (scene.image) { + loadImage(scene.image, params); + } else { + showError('Video is not supported on this platform (iOS or IE11).'); + } + } else { + // Load the video element. + videoElement = document.createElement('video'); + videoElement.src = scene.video; + videoElement.loop = true; + videoElement.setAttribute('crossorigin', 'anonymous'); + videoElement.addEventListener('canplaythrough', onVideoLoad); + videoElement.addEventListener('error', onVideoError); + } + } else if (scene.image) { + // Otherwise, just render the photosphere. + loadImage(scene.image, params); + } + + console.log('Loaded scene', scene); +} + +function onVideoLoad() { + // Render the stereo video. + var params = { + isStereo: loadedScene.isStereo, + } + renderer.set360Video(videoElement, params); + + // On mobile, tell the user they need to tap to start. Otherwise, autoplay. + if (!Util.isMobile()) { + // Hide loading indicator. + loadIndicator.hide(); + // Autoplay the video on desktop. + videoElement.play(); + } else { + // Tell user to tap to start. + showError('Tap to start video', 'Play'); + document.body.addEventListener('touchend', onVideoTap); + } + + // Prevent onVideoLoad from firing multiple times. + videoElement.removeEventListener('canplaythrough', onVideoLoad); +} + +function onVideoTap() { + hideError(); + videoElement.play(); + + // Prevent multiple play() calls on the video element. + document.body.removeEventListener('touchend', onVideoTap); +} + +function onRenderLoad() { + // Hide loading indicator. + loadIndicator.hide(); +} + +function onSceneError(message) { + showError('Loader: ' + message); +} + +function onRenderError(message) { + showError('Render: ' + message); +} + +function onVideoError(e) { + showError('Video load error'); + console.log(e); +} + +function showError(message, opt_title) { + // Hide loading indicator. + loadIndicator.hide(); + + var error = document.querySelector('#error'); + error.classList.add('visible'); + error.querySelector('.message').innerHTML = message; + + var title = (opt_title !== undefined ? opt_title : 'Error'); + error.querySelector('.title').innerHTML = title; +} + +function hideError() { + var error = document.querySelector('#error'); + error.classList.remove('visible'); +} + +function showStats() { + stats.setMode(0); // 0: fps, 1: ms + + // Align bottom-left. + stats.domElement.style.position = 'absolute'; + stats.domElement.style.left = '0px'; + stats.domElement.style.bottom = '0px'; + document.body.appendChild(stats.domElement); +} + +function loop(time) { + stats.begin(); + renderer.render(time); + stats.end(); + requestAnimationFrame(loop); +} +requestAnimationFrame(loop); + +},{"../node_modules/es6-promise/dist/es6-promise.min":1,"../node_modules/stats-js/build/stats.min":2,"../node_modules/webvr-polyfill/build/webvr-polyfill":7,"./device-motion-receiver":9,"./loading-indicator":12,"./photosphere-renderer":14,"./scene-loader":16,"./util":17}],14:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var Emitter = _dereq_('./emitter'); +var Eyes = _dereq_('./eyes'); +var THREE = _dereq_('../node_modules/three/three'); +THREE.VRControls = _dereq_('../node_modules/three/examples/js/controls/VRControls'); +THREE.VREffect = _dereq_('../node_modules/three/examples/js/effects/VREffect'); +var Util = _dereq_('./util'); +var VertexDistorter = _dereq_('./vertex-distorter'); +_dereq_('../node_modules/webvr-boilerplate/build/webvr-manager'); + +function PhotosphereRenderer() { + this.init(); +} +PhotosphereRenderer.prototype = new Emitter(); + +PhotosphereRenderer.prototype.init = function() { + var container = document.querySelector('body'); + var camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.1, 100); + var cameraDummy = new THREE.Object3D(); + cameraDummy.add(camera); + + // Antialiasing temporarily disabled to improve performance. + var renderer = new THREE.WebGLRenderer({antialias: false}); + renderer.setClearColor(0x000000, 0); + renderer.setSize(window.innerWidth, window.innerHeight); + + // Round down fractional DPR values for better performance. + renderer.setPixelRatio(Math.floor(window.devicePixelRatio)); + container.appendChild(renderer.domElement); + + var controls = new THREE.VRControls(camera); + var effect = new THREE.VREffect(renderer); + effect.setSize(window.innerWidth, window.innerHeight); + + this.camera = camera; + this.renderer = renderer; + this.effect = effect; + this.controls = controls; + this.manager = new WebVRManager(renderer, effect, {isUndistorted: true}); + + this.initScenes_(); + + // The vertex distorter. + this.distorter = new VertexDistorter(this.manager.getDeviceInfo()); + + this.manager.on('modechange', this.onModeChange_.bind(this)); + this.manager.on('viewerchange', this.onViewerChange_.bind(this)); + + // Watch the resize event. + window.addEventListener('resize', this.onResize_.bind(this)); + + var that = this; + navigator.getVRDevices().then(function(devices) { + devices.forEach(function(device) { + if (device instanceof HMDVRDevice) { + that.hmd = device; + } + }); + }); +}; + +PhotosphereRenderer.prototype.render = function(timestamp) { + this.controls.update(); + this.manager.render(this.scenes, this.camera, timestamp); +}; + +PhotosphereRenderer.prototype.setDefaultLookDirection = function(phi) { + // Rotate the camera parent to take into account the scene's rotation. + this.camera.parent.rotation.y = phi; +}; + +/** + * Sets the photosphere based on the image in the source. Supports stereo and + * mono photospheres. + * + * Emits 'load' and 'error' events. + */ +PhotosphereRenderer.prototype.setPhotosphere = function(src, opt_params) { + var params = opt_params || {}; + + this.isStereo = !!params.isStereo; + this.src = src; + + // Load texture. + var loader = new THREE.TextureLoader(); + loader.crossOrigin = 'anonymous'; + loader.load(src, this.onTextureLoaded_.bind(this), null, + this.onTextureError_.bind(this)); +}; + +PhotosphereRenderer.prototype.set360Video = function(videoElement, opt_params) { + var params = opt_params || {}; + + this.isStereo = !!params.isStereo; + + // Load the video texture. + var videoTexture = new THREE.VideoTexture(videoElement); + videoTexture.minFilter = THREE.LinearFilter; + videoTexture.magFilter = THREE.LinearFilter; + videoTexture.format = THREE.RGBFormat; + videoTexture.generateMipmaps = false; + videoTexture.needsUpdate = true; + + this.onTextureLoaded_(videoTexture); +}; + +PhotosphereRenderer.prototype.initScenes_ = function() { + this.sceneLeft = this.createScene_(); + this.sceneRight = this.createScene_(); + this.sceneLeft.add(this.camera.parent); + + this.scenes = [this.sceneLeft, this.sceneRight]; + this.eyes = [Eyes.LEFT, Eyes.RIGHT]; +}; + +PhotosphereRenderer.prototype.onTextureLoaded_ = function(texture) { + var sphereLeft; + var sphereRight; + if (this.isStereo) { + sphereLeft = this.createPhotosphere_(texture, {offsetY: 0.5, scaleY: 0.5}); + sphereRight = this.createPhotosphere_(texture, {offsetY: 0, scaleY: 0.5}); + } else { + sphereLeft = this.createPhotosphere_(texture); + sphereRight = this.createPhotosphere_(texture); + } + + this.sceneLeft.getObjectByName('photo').children = [sphereLeft]; + this.sceneRight.getObjectByName('photo').children = [sphereRight]; + + this.emit('load'); +}; + +PhotosphereRenderer.prototype.onTextureError_ = function(error) { + this.emit('error', 'Unable to load texture from ' + this.src); +}; + + +PhotosphereRenderer.prototype.createPhotosphere_ = function(texture, opt_params) { + var p = opt_params || {}; + p.scaleX = p.scaleX || 1; + p.scaleY = p.scaleY || 1; + p.offsetX = p.offsetX || 0; + p.offsetY = p.offsetY || 0; + p.phiStart = p.phiStart || 0; + p.phiLength = p.phiLength || Math.PI * 2; + p.thetaStart = p.thetaStart || 0; + p.thetaLength = p.thetaLength || Math.PI; + + var geometry = new THREE.SphereGeometry(1, 48, 48, + p.phiStart, p.phiLength, p.thetaStart, p.thetaLength); + geometry.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1)); + var uvs = geometry.faceVertexUvs[0]; + for (var i = 0; i < uvs.length; i ++) { + for (var j = 0; j < 3; j ++) { + uvs[i][j].x *= p.scaleX; + uvs[i][j].x += p.offsetX; + uvs[i][j].y *= p.scaleY; + uvs[i][j].y += p.offsetY; + } + } + + var material = new THREE.MeshBasicMaterial({ map: texture }); + this.distorter.setMap(texture); + var out = new THREE.Mesh(geometry, material); + out.renderOrder = -1; + return out; +}; + +PhotosphereRenderer.prototype.createScene_ = function(opt_params) { + var scene = new THREE.Scene(); + // Add a light. + scene.add(new THREE.PointLight(0xFFFFFF)); + + // Add a group for the photosphere. + var photoGroup = new THREE.Object3D(); + photoGroup.name = 'photo'; + scene.add(photoGroup); + + return scene; +}; + +PhotosphereRenderer.prototype.updateMaterial_ = function(material_FOO) { + for (var i = 0; i < this.scenes.length; i++) { + var eye = this.eyes[i]; + var material = this.distorter.getShaderMaterial(eye); + var scene = this.scenes[i]; + var children = scene.getObjectByName('photo').children; + for (var j = 0; j < children.length; j++) { + var child = children[j]; + child.material = material; + child.material.needsUpdate = true; + } + } +}; + +PhotosphereRenderer.prototype.updateRenderRect_ = function() { + if (this.hmd && this.hmd.setRenderRect) { + var deviceInfo = this.manager.getDeviceInfo(); + var leftRect = deviceInfo.getUndistortedViewportLeftEye(); + var dpr = window.devicePixelRatio; + leftRect.x /= dpr; + leftRect.y /= dpr; + leftRect.width /= dpr; + leftRect.height /= dpr; + + var rightRect = Util.clone(leftRect); + rightRect.x = (window.innerWidth - leftRect.x) - leftRect.width; + + this.hmd.setRenderRect(leftRect, rightRect); + } +}; + +PhotosphereRenderer.prototype.onModeChange_ = function(newMode, oldMode) { + console.log('onModeChange_', newMode); + + var coefficients; + if (newMode == WebVRManager.Modes.VR) { + // Entering VR mode. + this.distorter.setEnabled(true); + this.updateMaterial_(); + } else if (oldMode == WebVRManager.Modes.VR) { + // Leaving VR mode. + this.distorter.setEnabled(false); + this.updateMaterial_(); + } + + if (window.analytics) { + analytics.logModeChanged(newMode); + } +}; + +PhotosphereRenderer.prototype.onViewerChange_ = function(newViewer) { + console.log('onViewerChange_', newViewer); + + // Reset the photosphere with new coefficients. + this.updateMaterial_(); + this.updateRenderRect_(); +}; + +PhotosphereRenderer.prototype.onResize_ = function() { + this.updateRenderRect_(); +}; + +module.exports = PhotosphereRenderer; + +},{"../node_modules/three/examples/js/controls/VRControls":3,"../node_modules/three/examples/js/effects/VREffect":4,"../node_modules/three/three":5,"../node_modules/webvr-boilerplate/build/webvr-manager":6,"./emitter":10,"./eyes":11,"./util":17,"./vertex-distorter":18}],15:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Contains all information about a given scene, including the photosphere asset, + * background music. + */ +function SceneInfo(opt_params) { + var params = opt_params || {}; + this.id = params.id; + this.title = params.title; + this.image = params.image; + this.preview = params.preview; + this.isStereo = !!params.isStereo; + this.audio = params.audio; + this.video = params.video; + this.yaw = params.yaw || 0; + this.isYawOnly = params.isYawOnly; +} + +SceneInfo.prototype.isComplete = function() { + return !!this.image || !!this.video; +}; + +SceneInfo.prototype.toObject = function() { + return { + id: this.id || null, + title: this.title || null, + image: this.image, + preview: this.preview, + isStereo: this.isStereo, + audio: this.audio, + yaw: this.yaw || null, + video: this.video || null, + }; +}; + +SceneInfo.prototype.isImageDataURI = function() { + return this.image.indexOf('data:') == 0; +}; + + +module.exports = SceneInfo; + +},{}],16:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var Emitter = _dereq_('./emitter'); +var SceneInfo = _dereq_('./scene-info'); + +var Query = { + JSON_URL: 'url', + VIDEO_URL: 'video', + OBJECT_URL: 'object', + IMAGE_URL: 'image', + PREVIEW_URL: 'preview', + IS_STEREO: 'is_stereo', + AUDIO_URL: 'audio', + START_YAW: 'start_yaw', + IS_YAW_ONLY: 'is_yaw_only', +}; + +function SceneLoader() { +} +SceneLoader.prototype = new Emitter(); + +/** + * Loads a scene from a JSON file. + */ +SceneLoader.prototype.loadFromJson_ = function(url, callback) { + // XHR to fetch the JSON. + var xhr = new XMLHttpRequest(); + xhr.open('GET', url); + var that = this; + xhr.onload = function(e) { + try { + var jsonObj = JSON.parse(this.response); + } catch (e) { + that.emit('error', 'Invalid JSON at ' + url + '.'); + return; + } + var labelObjects = {}; + var labels = jsonObj.labels || []; + // Go through the labels in the data and objectify them. + for (var i = 0; i < labels.length; i++) { + var label = new Label(labels[i]); + labelObjects[label.id] = label; + } + jsonObj.labels = labelObjects; + var scene = new SceneInfo(jsonObj); + that.emit('load', scene); + }; + xhr.send(); +}; + +/** + * Parse out GET parameters from the URL string and load the scene. + */ +SceneLoader.prototype.loadFromGetParams_ = function() { + var params = { + image: Util.getQueryParameter(Query.IMAGE_URL), + video: Util.getQueryParameter(Query.VIDEO_URL), + object: Util.getQueryParameter(Query.OBJECT_URL), + preview: Util.getQueryParameter(Query.PREVIEW_URL), + isStereo: this.parseBoolean_(Util.getQueryParameter(Query.IS_STEREO)), + audio: Util.getQueryParameter(Query.AUDIO_URL), + isYawOnly: this.parseBoolean_(Util.getQueryParameter(Query.IS_YAW_ONLY)), + yaw: THREE.Math.degToRad(Util.getQueryParameter(Query.START_YAW)), + }; + + var count = 0; + count += (params[Query.IMAGE_URL] ? 1 : 0); + count += (params[Query.VIDEO_URL] ? 1 : 0); + count += (params[Query.OBJECT_URL] ? 1 : 0); + // Validate this. + if (count == 0) { + this.emit('error', 'Either "image", "video", or "object" GET parameter is required.'); + return; + } + + var scene = new SceneInfo(params); + this.emit('load', scene); +}; + +SceneLoader.prototype.parseBoolean_ = function(value) { + if (value == 'false') { + return false; + } + return !!value; +}; + +SceneLoader.prototype.loadScene = function(callback) { + // If there's a url param specified, try loading from JSON. + var url = Util.getQueryParameter('url'); + var image = Util.getQueryParameter('image'); + var video = Util.getQueryParameter('video'); + var object = Util.getQueryParameter('object'); + if (url) { + this.loadFromJson_(url); + } else if (image || video || object) { + // Otherwise, try loading from URL parameters. + this.loadFromGetParams_(); + } else { + // If it fails, throw an exception. + this.emit('error', 'Unable to load scene. Required parameter missing.'); + } +}; + +module.exports = SceneLoader; + +},{"./emitter":10,"./scene-info":15}],17:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +Util = window.Util || {}; + +Util.isDataURI = function(src) { + return src && src.indexOf('data:') == 0; +}; + +Util.generateUUID = function() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); +}; + +Util.isMobile = function() { + var check = false; + (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); + return check; +}; + +Util.isIOS = function() { + return /(iPad|iPhone|iPod)/g.test(navigator.userAgent); +}; + +Util.cloneObject = function(obj) { + var out = {}; + for (key in obj) { + out[key] = obj[key]; + } + return out; +}; + +Util.hashCode = function(s) { + return s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0); +}; + +Util.loadTrackSrc = function(context, src, callback, opt_progressCallback) { + var request = new XMLHttpRequest(); + request.open('GET', src, true); + request.responseType = 'arraybuffer'; + + // Decode asynchronously. + request.onload = function() { + context.decodeAudioData(request.response, function(buffer) { + callback(buffer); + }, function(e) { + console.error(e); + }); + }; + if (opt_progressCallback) { + request.onprogress = function(e) { + var percent = e.loaded / e.total; + opt_progressCallback(percent); + }; + } + request.send(); +}; + +Util.isPow2 = function(n) { + return (n & (n - 1)) == 0; +}; + +Util.capitalize = function(s) { + return s.charAt(0).toUpperCase() + s.slice(1); +}; + +Util.isIFrame = function() { + try { + return window.self !== window.top; + } catch (e) { + return true; + } +}; + +// From http://goo.gl/4WX3tg +Util.getQueryParameter = function(name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), + results = regex.exec(location.search); + return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); +}; + + +// From http://stackoverflow.com/questions/11871077/proper-way-to-detect-webgl-support. +Util.isWebGLEnabled = function() { + var canvas = document.createElement('canvas'); + try { gl = canvas.getContext("webgl"); } + catch (x) { gl = null; } + + if (gl == null) { + try { gl = canvas.getContext("experimental-webgl"); experimental = true; } + catch (x) { gl = null; } + } + return !!gl; +}; + +Util.clone = function(obj) { + return JSON.parse(JSON.stringify(obj)); +}; + +// From http://stackoverflow.com/questions/10140604/fastest-hypotenuse-in-javascript +Util.hypot = Math.hypot || function(x, y) { + return Math.sqrt(x*x + y*y); +}; + +// From http://stackoverflow.com/a/17447718/693934 +Util.isIE11 = function() { + return navigator.userAgent.match(/Trident/); +}; + +module.exports = Util; + +},{}],18:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +THREE = _dereq_('../node_modules/three/three'); +var Eyes = _dereq_('./eyes'); +var Util = _dereq_('./util'); + +var DEFAULT_FOV = 40; +var NO_DISTORTION = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; +/** + * Responsible for distortion correction by pre-distorting the vertex geometry + * so that a second shader pass is not needed. This is intended to greatly + * optimize the rendering process. + */ +function VertexDistorter(deviceInfo) { + this.texture = null; + this.isEnabled = false; + + this.deviceInfo = deviceInfo; +} + +VertexDistorter.prototype.setEnabled = function(isEnabled) { + this.isEnabled = isEnabled; +} + +/** + * Sets the texture that is used to render this photosphere. + */ +VertexDistorter.prototype.setMap = function(texture) { + this.texture = texture; +}; + +VertexDistorter.prototype.getVertexShader_ = function() { + return [ + '#ifdef GL_ES', + 'precision highp float;', + '#endif', + + 'varying vec2 vUV;', + + this.getDistortionInclude_(), + + 'void main() {', + // Pass through texture coordinates to the fragment shader. + 'vUV = uv;', + + // Here, we want to ensure that we are using an undistorted projection + // matrix. By setting isUndistorted: true in the WebVRManager, we + // guarantee this. + 'vec4 pos = projectionMatrix * modelViewMatrix * vec4(position, 1.0);', + + 'gl_Position = Distort(pos);', + '}' + ].join('\n'); +}; + +VertexDistorter.prototype.getNoopVertexShader_ = function() { + return [ + '#ifdef GL_ES', + 'precision highp float;', + '#endif', + + 'varying vec2 vUV;', + + 'void main() {', + 'gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);', + 'vUV = uv;', + '}' + ].join('\n'); +}; + +VertexDistorter.prototype.getFragmentShader_ = function() { + return [ + '#ifdef GL_ES', + 'precision highp float;', + '#endif', + + 'varying vec2 vUV;', + 'uniform sampler2D texture;', + + 'void main() {', + 'gl_FragColor = texture2D(texture, vUV);', + '}' + ].join('\n'); +}; + +VertexDistorter.prototype.getNoopDistortionInclude_ = function() { + return [ + 'vec4 Distort(vec4 point) {', + 'return point;', + '}' + ].join('\n'); +}; + +VertexDistorter.prototype.getDistortionInclude_ = function() { + return [ + 'uniform float uDistortionCoefficients[12];', + 'uniform float uDistortionMaxFovSquared;', + 'uniform vec2 uDistortionFovOffset;', + 'uniform vec2 uDistortionFovScale;', + + // Returns a scalar to distort a point; computed in reverse via the polynomial approximation: + // r' = 1 + Σ_i (uDistortionCoefficients[i] rSquared^(i+1)) i=[0..11] + // where rSquared is the squared radius of an undistorted point in tan-angle space. + // See {@link Distortion} for more information. + 'float DistortionFactor(float rSquared) {', + 'float ret = 0.0;', + 'rSquared = min(uDistortionMaxFovSquared, rSquared);', + 'ret = rSquared * (ret + uDistortionCoefficients[11]);', + 'ret = rSquared * (ret + uDistortionCoefficients[10]);', + 'ret = rSquared * (ret + uDistortionCoefficients[9]);', + 'ret = rSquared * (ret + uDistortionCoefficients[8]);', + 'ret = rSquared * (ret + uDistortionCoefficients[7]);', + 'ret = rSquared * (ret + uDistortionCoefficients[6]);', + 'ret = rSquared * (ret + uDistortionCoefficients[5]);', + 'ret = rSquared * (ret + uDistortionCoefficients[4]);', + 'ret = rSquared * (ret + uDistortionCoefficients[3]);', + 'ret = rSquared * (ret + uDistortionCoefficients[2]);', + 'ret = rSquared * (ret + uDistortionCoefficients[1]);', + 'ret = rSquared * (ret + uDistortionCoefficients[0]);', + 'return ret + 1.0;', + '}', + + // Given a point in clip space, distort the point according to the coefficients stored in + // uDistortionCoefficients and the field of view (FOV) specified in uDistortionFovOffset and + // uDistortionFovScale. + // Returns the distorted point in clip space, with its Z untouched. + 'vec4 Distort(vec4 point) {', + // Put point into normalized device coordinates (NDC), [(-1, -1, -1) to (1, 1, 1)]. + 'vec3 pointNdc = point.xyz / point.w;', + // Throw away the Z coordinate and map the point to the unit square, [(0, 0) to (1, 1)]. + 'vec2 pointUnitSquare = (pointNdc.xy + vec2(1.0)) / 2.0;', + // Map the point into FOV tan-angle space. + 'vec2 pointTanAngle = pointUnitSquare * uDistortionFovScale - uDistortionFovOffset;', + 'float radiusSquared = dot(pointTanAngle, pointTanAngle);', + 'float distortionFactor = DistortionFactor(radiusSquared);', + //'float distortionFactor = 2.0;', + 'vec2 distortedPointTanAngle = pointTanAngle * distortionFactor;', + // Reverse the mappings above to bring the distorted point back into NDC space. + 'vec2 distortedPointUnitSquare = (distortedPointTanAngle + uDistortionFovOffset)', + '/ uDistortionFovScale;', + 'vec3 distortedPointNdc = vec3(distortedPointUnitSquare * 2.0 - vec2(1.0), pointNdc.z);', + // Convert the point into clip space before returning in case any operations are done after. + 'return vec4(distortedPointNdc, 1.0) * point.w;', + '}', + ].join('\n'); +}; + +VertexDistorter.prototype.getDistortionMaxFovSquared_ = function() { + var fov = this.getFov_(); + var maxFov = Util.hypot( + Math.tan(THREE.Math.degToRad(Math.max(fov.leftDegrees, fov.rightDegrees))), + Math.tan(THREE.Math.degToRad(Math.max(fov.downDegrees, fov.upDegrees)))); + return maxFov * maxFov; +}; + +VertexDistorter.prototype.getDistortionCoefficients_ = function() { + var viewer = this.deviceInfo.viewer; + return this.isEnabled ? viewer.inverseCoefficients : NO_DISTORTION; +}; + +VertexDistorter.prototype.getDistortionFovOffset_ = function(eye) { + var fov = this.getFov_(eye); + /* + var sideDegrees = Math.min(fov.leftDegrees, fov.rightDegrees); + var left = Math.tan(THREE.Math.degToRad(sideDegrees)); + */ + var left = Math.tan(THREE.Math.degToRad(fov.leftDegrees)); + var down = Math.tan(THREE.Math.degToRad(fov.downDegrees)); + return new THREE.Vector2(left, down); +}; + +VertexDistorter.prototype.getDistortionFovScale_ = function() { + var fov = this.getFov_(); + var left = Math.tan(THREE.Math.degToRad(fov.leftDegrees)); + var right = Math.tan(THREE.Math.degToRad(fov.rightDegrees)); + var up = Math.tan(THREE.Math.degToRad(fov.upDegrees)); + var down = Math.tan(THREE.Math.degToRad(fov.downDegrees)); + return new THREE.Vector2(left + right, up + down); +}; + + +VertexDistorter.prototype.getUniforms_ = function(eye) { + return { + texture: { + type: 't', + value: this.texture + }, + uDistortionCoefficients: { + type: 'fv1', + value: this.getDistortionCoefficients_() + }, + uDistortionMaxFovSquared: { + type: 'f', + value: this.getDistortionMaxFovSquared_() + }, + uDistortionFovOffset: { + type: 'v2', + value: this.getDistortionFovOffset_(eye) + }, + uDistortionFovScale: { + type: 'v2', + value: this.getDistortionFovScale_() + } + }; +}; + +VertexDistorter.prototype.getShaderMaterial = function(eye) { + var uniforms = this.getUniforms_(eye); + return new THREE.ShaderMaterial({ + uniforms: uniforms, + vertexShader: this.getVertexShader_(), + fragmentShader: this.getFragmentShader_() + }); +}; + +VertexDistorter.prototype.getFov_ = function(opt_eye) { + var eye = opt_eye || Eyes.LEFT; + + if (eye == Eyes.LEFT) { + return this.deviceInfo.getFieldOfViewLeftEye(true); + } + if (eye == Eyes.RIGHT) { + return this.deviceInfo.getFieldOfViewRightEye(true); + } + return null; +}; + +module.exports = VertexDistorter; + +},{"../node_modules/three/three":5,"./eyes":11,"./util":17}],19:[function(_dereq_,module,exports){ +// Load EmbedVR. +_dereq_('./main'); + +// Load Analytics for EmbedVR. +_dereq_('./analytics'); + +},{"./analytics":8,"./main":13}],20:[function(_dereq_,module,exports){ +// shim for using process in browser + +var process = module.exports = {}; +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = setTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + clearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + setTimeout(drainQueue, 0); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}]},{},[19]); diff --git a/build/vrview-analytics.min.js b/build/vrview-analytics.min.js new file mode 100644 index 00000000..fab26f84 --- /dev/null +++ b/build/vrview-analytics.min.js @@ -0,0 +1,22 @@ +!function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;ot;t+=2){var e=X[t],n=X[t+1];e(n),X[t]=void 0,X[t+1]=void 0}G=0}function f(){try{var t=_dereq_,e=t("vertx");return U=e.runOnLoop||e.runOnContext,i()}catch(n){return c()}}function l(t,e){var n=this,r=n._state;if(r===et&&!t||r===nt&&!e)return this;var o=new this.constructor(p),i=n._result;if(r){var s=arguments[r-1];H(function(){C(r,o,s,i)})}else j(n,o,t,e);return o}function h(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var n=new e(p);return g(n,t),n}function p(){}function _(){return new TypeError("You cannot resolve a promise with itself")}function v(){return new TypeError("A promises callback cannot return that same promise.")}function d(t){try{return t.then}catch(e){return rt.error=e,rt}}function y(t,e,n,r){try{t.call(e,n,r)}catch(o){return o}}function m(t,e,n){H(function(t){var r=!1,o=y(n,e,function(n){r||(r=!0,e!==n?g(t,n):E(t,n))},function(e){r||(r=!0,S(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&o&&(r=!0,S(t,o))},t)}function w(t,e){e._state===et?E(t,e._result):e._state===nt?S(t,e._result):j(e,void 0,function(e){g(t,e)},function(e){S(t,e)})}function b(t,n,r){n.constructor===t.constructor&&r===Z&&constructor.resolve===$?w(t,n):r===rt?S(t,rt.error):void 0===r?E(t,n):e(r)?m(t,n,r):E(t,n)}function g(e,n){e===n?S(e,_()):t(n)?b(e,n,d(n)):E(e,n)}function A(t){t._onerror&&t._onerror(t._result),T(t)}function E(t,e){t._state===tt&&(t._result=e,t._state=et,0!==t._subscribers.length&&H(T,t))}function S(t,e){t._state===tt&&(t._state=nt,t._result=e,H(A,t))}function j(t,e,n,r){var o=t._subscribers,i=o.length;t._onerror=null,o[i]=e,o[i+et]=n,o[i+nt]=r,0===i&&t._state&&H(T,t)}function T(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r,o,i=t._result,s=0;ss;s++)j(r.resolve(t[s]),void 0,e,n);return o}function Y(t){var e=this,n=new e(p);return S(n,t),n}function q(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function F(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function D(t){this._id=ct++,this._state=void 0,this._result=void 0,this._subscribers=[],p!==t&&("function"!=typeof t&&q(),this instanceof D?M(this,t):F())}function K(t,e){this._instanceConstructor=t,this.promise=new t(p),Array.isArray(e)?(this._input=e,this.length=e.length,this._remaining=e.length,this._result=new Array(this.length),0===this.length?E(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&E(this.promise,this._result))):S(this.promise,this._validationError())}function L(){var t;if("undefined"!=typeof global)t=global;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;(!n||"[object Promise]"!==Object.prototype.toString.call(n.resolve())||n.cast)&&(t.Promise=at)}var N;N=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var U,W,z,B=N,G=0,H=function(t,e){X[G]=t,X[G+1]=e,G+=2,2===G&&(W?W(a):z())},I="undefined"!=typeof window?window:void 0,J=I||{},Q=J.MutationObserver||J.WebKitMutationObserver,R="undefined"!=typeof process&&"[object process]"==={}.toString.call(process),V="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,X=new Array(1e3);z=R?o():Q?s():V?u():void 0===I&&"function"==typeof _dereq_?f():c();var Z=l,$=h,tt=void 0,et=1,nt=2,rt=new P,ot=new P,it=O,st=k,ut=Y,ct=0,at=D;D.all=it,D.race=st,D.resolve=$,D.reject=ut,D._setScheduler=n,D._setAsap=r,D._asap=H,D.prototype={constructor:D,then:Z,"catch":function(t){return this.then(null,t)}};var ft=K;K.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},K.prototype._enumerate=function(){for(var t=this.length,e=this._input,n=0;this._state===tt&&t>n;n++)this._eachEntry(e[n],n)},K.prototype._eachEntry=function(t,e){var n=this._instanceConstructor,r=n.resolve;if(r===$){var o=d(t);if(o===Z&&t._state!==tt)this._settledAt(t._state,e,t._result);else if("function"!=typeof o)this._remaining--,this._result[e]=t;else if(n===at){var i=new n(p);b(i,t,o),this._willSettleAt(i,e)}else this._willSettleAt(new n(function(e){e(t)}),e)}else this._willSettleAt(r(t),e)},K.prototype._settledAt=function(t,e,n){var r=this.promise;r._state===tt&&(this._remaining--,t===nt?S(r,n):this._result[e]=n),0===this._remaining&&E(r,this._result)},K.prototype._willSettleAt=function(t,e){var n=this;j(t,void 0,function(t){n._settledAt(et,e,t)},function(t){n._settledAt(nt,e,t)})};var lt=L,ht={Promise:at,polyfill:lt};"function"==typeof define&&define.amd?define(function(){return ht}):"undefined"!=typeof module&&module.exports?module.exports=ht:"undefined"!=typeof this&&(this.ES6Promise=ht),lt()}).call(this)}).call(this,_dereq_("_process"),"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{_process:20}],2:[function(_dereq_,module,exports){var Stats=function(){var l=Date.now(),m=l,g=0,n=1/0,o=0,h=0,p=1/0,q=0,r=0,s=0,f=document.createElement("div");f.id="stats",f.addEventListener("mousedown",function(b){b.preventDefault(),t(++s%2)},!1),f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps",a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002",f.appendChild(a);var i=document.createElement("div");i.id="fpsText",i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px",i.innerHTML="FPS",a.appendChild(i);var c=document.createElement("div");for(c.id="fpsGraph",c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff",a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113",c.appendChild(j)}var d=document.createElement("div");d.id="ms",d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none",f.appendChild(d);var k=document.createElement("div");k.id="msText",k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px",k.innerHTML="MS",d.appendChild(k);var e=document.createElement("div");for(e.id="msGraph",e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0",d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){switch(s=b){case 0:a.style.display="block",d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:12,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l,n=Math.min(n,g),o=Math.max(o,g),k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));return e.appendChild(e.firstChild).style.height=a+"px",r++,b>m+1e3&&(h=Math.round(1e3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height=a+"px",m=b,r=0),b},update:function(){l=this.end()}}};"object"==typeof module&&(module.exports=Stats)},{}],3:[function(_dereq_,module,exports){var VRControls=function(object,onError){function filterInvalidDevices(devices){var oculusDevices=devices.filter(function(device){return-1!==device.deviceName.toLowerCase().indexOf("oculus")});return oculusDevices.length>=1?devices.filter(function(device){return-1===device.deviceName.toLowerCase().indexOf("cardboard")}):devices}function gotVRDevices(devices){devices=filterInvalidDevices(devices);for(var i=0;ix?-1:x>0?1:+x}),void 0===Function.prototype.name&&void 0!==Object.defineProperty&&Object.defineProperty(Function.prototype,"name",{get:function(){return this.toString().match(/^\s*function\s*(\S*)\s*\(/)[1]}}),THREE.MOUSE={LEFT:0,MIDDLE:1,RIGHT:2},THREE.CullFaceNone=0,THREE.CullFaceBack=1,THREE.CullFaceFront=2,THREE.CullFaceFrontBack=3,THREE.FrontFaceDirectionCW=0,THREE.FrontFaceDirectionCCW=1,THREE.BasicShadowMap=0,THREE.PCFShadowMap=1,THREE.PCFSoftShadowMap=2,THREE.FrontSide=0,THREE.BackSide=1,THREE.DoubleSide=2,THREE.FlatShading=1,THREE.SmoothShading=2,THREE.NoColors=0,THREE.FaceColors=1,THREE.VertexColors=2,THREE.NoBlending=0,THREE.NormalBlending=1,THREE.AdditiveBlending=2,THREE.SubtractiveBlending=3,THREE.MultiplyBlending=4,THREE.CustomBlending=5,THREE.AddEquation=100,THREE.SubtractEquation=101,THREE.ReverseSubtractEquation=102,THREE.MinEquation=103,THREE.MaxEquation=104,THREE.ZeroFactor=200,THREE.OneFactor=201,THREE.SrcColorFactor=202,THREE.OneMinusSrcColorFactor=203,THREE.SrcAlphaFactor=204,THREE.OneMinusSrcAlphaFactor=205,THREE.DstAlphaFactor=206,THREE.OneMinusDstAlphaFactor=207,THREE.DstColorFactor=208,THREE.OneMinusDstColorFactor=209,THREE.SrcAlphaSaturateFactor=210,THREE.NeverDepth=0,THREE.AlwaysDepth=1,THREE.LessDepth=2,THREE.LessEqualDepth=3,THREE.EqualDepth=4,THREE.GreaterEqualDepth=5,THREE.GreaterDepth=6,THREE.NotEqualDepth=7,THREE.MultiplyOperation=0,THREE.MixOperation=1,THREE.AddOperation=2,THREE.UVMapping=300,THREE.CubeReflectionMapping=301,THREE.CubeRefractionMapping=302,THREE.EquirectangularReflectionMapping=303,THREE.EquirectangularRefractionMapping=304,THREE.SphericalReflectionMapping=305,THREE.RepeatWrapping=1e3,THREE.ClampToEdgeWrapping=1001,THREE.MirroredRepeatWrapping=1002,THREE.NearestFilter=1003,THREE.NearestMipMapNearestFilter=1004,THREE.NearestMipMapLinearFilter=1005,THREE.LinearFilter=1006,THREE.LinearMipMapNearestFilter=1007,THREE.LinearMipMapLinearFilter=1008,THREE.UnsignedByteType=1009,THREE.ByteType=1010,THREE.ShortType=1011,THREE.UnsignedShortType=1012,THREE.IntType=1013,THREE.UnsignedIntType=1014,THREE.FloatType=1015,THREE.HalfFloatType=1025,THREE.UnsignedShort4444Type=1016,THREE.UnsignedShort5551Type=1017,THREE.UnsignedShort565Type=1018,THREE.AlphaFormat=1019,THREE.RGBFormat=1020,THREE.RGBAFormat=1021,THREE.LuminanceFormat=1022,THREE.LuminanceAlphaFormat=1023,THREE.RGBEFormat=THREE.RGBAFormat,THREE.RGB_S3TC_DXT1_Format=2001,THREE.RGBA_S3TC_DXT1_Format=2002,THREE.RGBA_S3TC_DXT3_Format=2003,THREE.RGBA_S3TC_DXT5_Format=2004,THREE.RGB_PVRTC_4BPPV1_Format=2100,THREE.RGB_PVRTC_2BPPV1_Format=2101,THREE.RGBA_PVRTC_4BPPV1_Format=2102,THREE.RGBA_PVRTC_2BPPV1_Format=2103,THREE.LoopOnce=2200,THREE.LoopRepeat=2201,THREE.LoopPingPong=2202,THREE.Projector=function(){console.error("THREE.Projector has been moved to /examples/js/renderers/Projector.js."),this.projectVector=function(vector,camera){console.warn("THREE.Projector: .projectVector() is now vector.project()."),vector.project(camera)},this.unprojectVector=function(vector,camera){console.warn("THREE.Projector: .unprojectVector() is now vector.unproject()."),vector.unproject(camera)},this.pickingRay=function(vector,camera){console.error("THREE.Projector: .pickingRay() is now raycaster.setFromCamera().")}},THREE.CanvasRenderer=function(){console.error("THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js"),this.domElement=document.createElement("canvas"),this.clear=function(){},this.render=function(){},this.setClearColor=function(){},this.setSize=function(){}},THREE.Color=function(color){return 3===arguments.length?this.fromArray(arguments):this.set(color)},THREE.Color.prototype={constructor:THREE.Color,r:1,g:1,b:1,set:function(value){return value instanceof THREE.Color?this.copy(value):"number"==typeof value?this.setHex(value):"string"==typeof value&&this.setStyle(value),this},setHex:function(hex){return hex=Math.floor(hex),this.r=(hex>>16&255)/255,this.g=(hex>>8&255)/255,this.b=(255&hex)/255,this},setRGB:function(r,g,b){return this.r=r,this.g=g,this.b=b,this},setHSL:function(){function hue2rgb(p,q,t){return 0>t&&(t+=1),t>1&&(t-=1),1/6>t?p+6*(q-p)*t:.5>t?q:2/3>t?p+6*(q-p)*(2/3-t):p}return function(h,s,l){if(h=THREE.Math.euclideanModulo(h,1),s=THREE.Math.clamp(s,0,1),l=THREE.Math.clamp(l,0,1),0===s)this.r=this.g=this.b=l;else{var p=.5>=l?l*(1+s):l+s-l*s,q=2*l-p;this.r=hue2rgb(q,p,h+1/3),this.g=hue2rgb(q,p,h),this.b=hue2rgb(q,p,h-1/3)}return this}}(),setStyle:function(style){function handleAlpha(string){void 0!==string&&parseFloat(string)<1&&console.warn("THREE.Color: Alpha component of "+style+" will be ignored.")}var m;if(m=/^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec(style)){var color,name=m[1],components=m[2];switch(name){case"rgb":case"rgba":if(color=/^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(components))return this.r=Math.min(255,parseInt(color[1],10))/255,this.g=Math.min(255,parseInt(color[2],10))/255,this.b=Math.min(255,parseInt(color[3],10))/255,handleAlpha(color[5]),this;if(color=/^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(components))return this.r=Math.min(100,parseInt(color[1],10))/100,this.g=Math.min(100,parseInt(color[2],10))/100,this.b=Math.min(100,parseInt(color[3],10))/100,handleAlpha(color[5]),this;break;case"hsl":case"hsla":if(color=/^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(components)){var h=parseFloat(color[1])/360,s=parseInt(color[2],10)/100,l=parseInt(color[3],10)/100;return handleAlpha(color[5]),this.setHSL(h,s,l)}}}else if(m=/^\#([A-Fa-f0-9]+)$/.exec(style)){var hex=m[1],size=hex.length;if(3===size)return this.r=parseInt(hex.charAt(0)+hex.charAt(0),16)/255,this.g=parseInt(hex.charAt(1)+hex.charAt(1),16)/255,this.b=parseInt(hex.charAt(2)+hex.charAt(2),16)/255,this;if(6===size)return this.r=parseInt(hex.charAt(0)+hex.charAt(1),16)/255,this.g=parseInt(hex.charAt(2)+hex.charAt(3),16)/255,this.b=parseInt(hex.charAt(4)+hex.charAt(5),16)/255,this}if(style&&style.length>0){var hex=THREE.ColorKeywords[style];void 0!==hex?this.setHex(hex):console.warn("THREE.Color: Unknown color "+style)}return this},clone:function(){return new this.constructor(this.r,this.g,this.b)},copy:function(color){return this.r=color.r,this.g=color.g,this.b=color.b,this},copyGammaToLinear:function(color,gammaFactor){return void 0===gammaFactor&&(gammaFactor=2),this.r=Math.pow(color.r,gammaFactor),this.g=Math.pow(color.g,gammaFactor),this.b=Math.pow(color.b,gammaFactor),this},copyLinearToGamma:function(color,gammaFactor){void 0===gammaFactor&&(gammaFactor=2);var safeInverse=gammaFactor>0?1/gammaFactor:1;return this.r=Math.pow(color.r,safeInverse),this.g=Math.pow(color.g,safeInverse),this.b=Math.pow(color.b,safeInverse),this},convertGammaToLinear:function(){var r=this.r,g=this.g,b=this.b;return this.r=r*r,this.g=g*g,this.b=b*b,this},convertLinearToGamma:function(){return this.r=Math.sqrt(this.r),this.g=Math.sqrt(this.g),this.b=Math.sqrt(this.b),this},getHex:function(){return 255*this.r<<16^255*this.g<<8^255*this.b<<0},getHexString:function(){return("000000"+this.getHex().toString(16)).slice(-6)},getHSL:function(optionalTarget){var hue,saturation,hsl=optionalTarget||{h:0,s:0,l:0},r=this.r,g=this.g,b=this.b,max=Math.max(r,g,b),min=Math.min(r,g,b),lightness=(min+max)/2;if(min===max)hue=0,saturation=0;else{var delta=max-min;switch(saturation=.5>=lightness?delta/(max+min):delta/(2-max-min),max){case r:hue=(g-b)/delta+(b>g?6:0);break;case g:hue=(b-r)/delta+2;break;case b:hue=(r-g)/delta+4}hue/=6}return hsl.h=hue,hsl.s=saturation,hsl.l=lightness,hsl},getStyle:function(){return"rgb("+(255*this.r|0)+","+(255*this.g|0)+","+(255*this.b|0)+")"},offsetHSL:function(h,s,l){var hsl=this.getHSL();return hsl.h+=h,hsl.s+=s,hsl.l+=l,this.setHSL(hsl.h,hsl.s,hsl.l),this},add:function(color){return this.r+=color.r,this.g+=color.g,this.b+=color.b,this},addColors:function(color1,color2){return this.r=color1.r+color2.r,this.g=color1.g+color2.g,this.b=color1.b+color2.b,this},addScalar:function(s){return this.r+=s,this.g+=s,this.b+=s,this},multiply:function(color){return this.r*=color.r,this.g*=color.g,this.b*=color.b,this},multiplyScalar:function(s){return this.r*=s,this.g*=s,this.b*=s,this},lerp:function(color,alpha){return this.r+=(color.r-this.r)*alpha,this.g+=(color.g-this.g)*alpha,this.b+=(color.b-this.b)*alpha,this},equals:function(c){return c.r===this.r&&c.g===this.g&&c.b===this.b},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.r=array[offset],this.g=array[offset+1],this.b=array[offset+2],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.r,array[offset+1]=this.g,array[offset+2]=this.b,array}},THREE.ColorKeywords={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},THREE.Quaternion=function(x,y,z,w){this._x=x||0,this._y=y||0,this._z=z||0,this._w=void 0!==w?w:1},THREE.Quaternion.prototype={constructor:THREE.Quaternion,get x(){return this._x},set x(value){this._x=value,this.onChangeCallback()},get y(){return this._y},set y(value){this._y=value,this.onChangeCallback()},get z(){return this._z},set z(value){this._z=value,this.onChangeCallback()},get w(){return this._w},set w(value){this._w=value,this.onChangeCallback()},set:function(x,y,z,w){return this._x=x,this._y=y,this._z=z,this._w=w,this.onChangeCallback(),this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._w)},copy:function(quaternion){return this._x=quaternion.x,this._y=quaternion.y,this._z=quaternion.z,this._w=quaternion.w,this.onChangeCallback(),this},setFromEuler:function(euler,update){if(euler instanceof THREE.Euler==!1)throw new Error("THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var c1=Math.cos(euler._x/2),c2=Math.cos(euler._y/2),c3=Math.cos(euler._z/2),s1=Math.sin(euler._x/2),s2=Math.sin(euler._y/2),s3=Math.sin(euler._z/2),order=euler.order;return"XYZ"===order?(this._x=s1*c2*c3+c1*s2*s3,this._y=c1*s2*c3-s1*c2*s3,this._z=c1*c2*s3+s1*s2*c3,this._w=c1*c2*c3-s1*s2*s3):"YXZ"===order?(this._x=s1*c2*c3+c1*s2*s3,this._y=c1*s2*c3-s1*c2*s3,this._z=c1*c2*s3-s1*s2*c3,this._w=c1*c2*c3+s1*s2*s3):"ZXY"===order?(this._x=s1*c2*c3-c1*s2*s3,this._y=c1*s2*c3+s1*c2*s3,this._z=c1*c2*s3+s1*s2*c3,this._w=c1*c2*c3-s1*s2*s3):"ZYX"===order?(this._x=s1*c2*c3-c1*s2*s3,this._y=c1*s2*c3+s1*c2*s3,this._z=c1*c2*s3-s1*s2*c3,this._w=c1*c2*c3+s1*s2*s3):"YZX"===order?(this._x=s1*c2*c3+c1*s2*s3,this._y=c1*s2*c3+s1*c2*s3,this._z=c1*c2*s3-s1*s2*c3,this._w=c1*c2*c3-s1*s2*s3):"XZY"===order&&(this._x=s1*c2*c3-c1*s2*s3,this._y=c1*s2*c3-s1*c2*s3,this._z=c1*c2*s3+s1*s2*c3,this._w=c1*c2*c3+s1*s2*s3),update!==!1&&this.onChangeCallback(),this},setFromAxisAngle:function(axis,angle){var halfAngle=angle/2,s=Math.sin(halfAngle);return this._x=axis.x*s,this._y=axis.y*s,this._z=axis.z*s,this._w=Math.cos(halfAngle),this.onChangeCallback(),this},setFromRotationMatrix:function(m){var s,te=m.elements,m11=te[0],m12=te[4],m13=te[8],m21=te[1],m22=te[5],m23=te[9],m31=te[2],m32=te[6],m33=te[10],trace=m11+m22+m33;return trace>0?(s=.5/Math.sqrt(trace+1),this._w=.25/s,this._x=(m32-m23)*s,this._y=(m13-m31)*s,this._z=(m21-m12)*s):m11>m22&&m11>m33?(s=2*Math.sqrt(1+m11-m22-m33),this._w=(m32-m23)/s,this._x=.25*s,this._y=(m12+m21)/s,this._z=(m13+m31)/s):m22>m33?(s=2*Math.sqrt(1+m22-m11-m33),this._w=(m13-m31)/s,this._x=(m12+m21)/s,this._y=.25*s,this._z=(m23+m32)/s):(s=2*Math.sqrt(1+m33-m11-m22),this._w=(m21-m12)/s,this._x=(m13+m31)/s,this._y=(m23+m32)/s,this._z=.25*s),this.onChangeCallback(),this},setFromUnitVectors:function(){var v1,r,EPS=1e-6;return function(vFrom,vTo){return void 0===v1&&(v1=new THREE.Vector3),r=vFrom.dot(vTo)+1,EPS>r?(r=0,Math.abs(vFrom.x)>Math.abs(vFrom.z)?v1.set(-vFrom.y,vFrom.x,0):v1.set(0,-vFrom.z,vFrom.y)):v1.crossVectors(vFrom,vTo),this._x=v1.x,this._y=v1.y,this._z=v1.z,this._w=r,this.normalize(),this}}(),inverse:function(){return this.conjugate().normalize(),this},conjugate:function(){return this._x*=-1,this._y*=-1,this._z*=-1,this.onChangeCallback(),this},dot:function(v){return this._x*v._x+this._y*v._y+this._z*v._z+this._w*v._w},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var l=this.length();return 0===l?(this._x=0,this._y=0,this._z=0,this._w=1):(l=1/l,this._x=this._x*l,this._y=this._y*l,this._z=this._z*l,this._w=this._w*l),this.onChangeCallback(),this},multiply:function(q,p){return void 0!==p?(console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(q,p)):this.multiplyQuaternions(this,q)},multiplyQuaternions:function(a,b){var qax=a._x,qay=a._y,qaz=a._z,qaw=a._w,qbx=b._x,qby=b._y,qbz=b._z,qbw=b._w;return this._x=qax*qbw+qaw*qbx+qay*qbz-qaz*qby,this._y=qay*qbw+qaw*qby+qaz*qbx-qax*qbz,this._z=qaz*qbw+qaw*qbz+qax*qby-qay*qbx,this._w=qaw*qbw-qax*qbx-qay*qby-qaz*qbz,this.onChangeCallback(),this},multiplyVector3:function(vector){return console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead."),vector.applyQuaternion(this)},slerp:function(qb,t){if(0===t)return this;if(1===t)return this.copy(qb);var x=this._x,y=this._y,z=this._z,w=this._w,cosHalfTheta=w*qb._w+x*qb._x+y*qb._y+z*qb._z;if(0>cosHalfTheta?(this._w=-qb._w,this._x=-qb._x,this._y=-qb._y,this._z=-qb._z,cosHalfTheta=-cosHalfTheta):this.copy(qb),cosHalfTheta>=1)return this._w=w,this._x=x,this._y=y,this._z=z,this;var halfTheta=Math.acos(cosHalfTheta),sinHalfTheta=Math.sqrt(1-cosHalfTheta*cosHalfTheta); +if(Math.abs(sinHalfTheta)<.001)return this._w=.5*(w+this._w),this._x=.5*(x+this._x),this._y=.5*(y+this._y),this._z=.5*(z+this._z),this;var ratioA=Math.sin((1-t)*halfTheta)/sinHalfTheta,ratioB=Math.sin(t*halfTheta)/sinHalfTheta;return this._w=w*ratioA+this._w*ratioB,this._x=x*ratioA+this._x*ratioB,this._y=y*ratioA+this._y*ratioB,this._z=z*ratioA+this._z*ratioB,this.onChangeCallback(),this},equals:function(quaternion){return quaternion._x===this._x&&quaternion._y===this._y&&quaternion._z===this._z&&quaternion._w===this._w},fromArray:function(array,offset){return void 0===offset&&(offset=0),this._x=array[offset],this._y=array[offset+1],this._z=array[offset+2],this._w=array[offset+3],this.onChangeCallback(),this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this._x,array[offset+1]=this._y,array[offset+2]=this._z,array[offset+3]=this._w,array},onChange:function(callback){return this.onChangeCallback=callback,this},onChangeCallback:function(){}},THREE.Quaternion.slerp=function(qa,qb,qm,t){return qm.copy(qa).slerp(qb,t)},THREE.Vector2=function(x,y){this.x=x||0,this.y=y||0},THREE.Vector2.prototype={constructor:THREE.Vector2,get width(){return this.x},set width(value){this.x=value},get height(){return this.y},set height(value){this.y=value},set:function(x,y){return this.x=x,this.y=y,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+index)}},clone:function(){return new this.constructor(this.x,this.y)},copy:function(v){return this.x=v.x,this.y=v.y,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this)},addScalar:function(s){return this.x+=s,this.y+=s,this},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this},addScaledVector:function(v,s){return this.x+=v.x*s,this.y+=v.y*s,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this)},subScalar:function(s){return this.x-=s,this.y-=s,this},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this},multiply:function(v){return this.x*=v.x,this.y*=v.y,this},multiplyScalar:function(scalar){return isFinite(scalar)?(this.x*=scalar,this.y*=scalar):(this.x=0,this.y=0),this},divide:function(v){return this.x/=v.x,this.y/=v.y,this},divideScalar:function(scalar){return this.multiplyScalar(1/scalar)},min:function(v){return this.x=Math.min(this.x,v.x),this.y=Math.min(this.y,v.y),this},max:function(v){return this.x=Math.max(this.x,v.x),this.y=Math.max(this.y,v.y),this},clamp:function(min,max){return this.x=Math.max(min.x,Math.min(max.x,this.x)),this.y=Math.max(min.y,Math.min(max.y,this.y)),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector2,max=new THREE.Vector2),min.set(minVal,minVal),max.set(maxVal,maxVal),this.clamp(min,max)}}(),clampLength:function(min,max){var length=this.length();return this.multiplyScalar(Math.max(min,Math.min(max,length))/length),this},floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this},negate:function(){return this.x=-this.x,this.y=-this.y,this},dot:function(v){return this.x*v.x+this.y*v.y},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)},normalize:function(){return this.divideScalar(this.length())},distanceTo:function(v){return Math.sqrt(this.distanceToSquared(v))},distanceToSquared:function(v){var dx=this.x-v.x,dy=this.y-v.y;return dx*dx+dy*dy},setLength:function(length){return this.multiplyScalar(length/this.length())},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this},lerpVectors:function(v1,v2,alpha){return this.subVectors(v2,v1).multiplyScalar(alpha).add(v1),this},equals:function(v){return v.x===this.x&&v.y===this.y},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this},rotateAround:function(center,angle){var c=Math.cos(angle),s=Math.sin(angle),x=this.x-center.x,y=this.y-center.y;return this.x=x*c-y*s+center.x,this.y=x*s+y*c+center.y,this}},THREE.Vector3=function(x,y,z){this.x=x||0,this.y=y||0,this.z=z||0},THREE.Vector3.prototype={constructor:THREE.Vector3,set:function(x,y,z){return this.x=x,this.y=y,this.z=z,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setZ:function(z){return this.z=z,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;case 2:this.z=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("index is out of range: "+index)}},clone:function(){return new this.constructor(this.x,this.y,this.z)},copy:function(v){return this.x=v.x,this.y=v.y,this.z=v.z,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this.z+=v.z,this)},addScalar:function(s){return this.x+=s,this.y+=s,this.z+=s,this},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this.z=a.z+b.z,this},addScaledVector:function(v,s){return this.x+=v.x*s,this.y+=v.y*s,this.z+=v.z*s,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this.z-=v.z,this)},subScalar:function(s){return this.x-=s,this.y-=s,this.z-=s,this},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this.z=a.z-b.z,this},multiply:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(v,w)):(this.x*=v.x,this.y*=v.y,this.z*=v.z,this)},multiplyScalar:function(scalar){return isFinite(scalar)?(this.x*=scalar,this.y*=scalar,this.z*=scalar):(this.x=0,this.y=0,this.z=0),this},multiplyVectors:function(a,b){return this.x=a.x*b.x,this.y=a.y*b.y,this.z=a.z*b.z,this},applyEuler:function(){var quaternion;return function(euler){return euler instanceof THREE.Euler==!1&&console.error("THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order."),void 0===quaternion&&(quaternion=new THREE.Quaternion),this.applyQuaternion(quaternion.setFromEuler(euler)),this}}(),applyAxisAngle:function(){var quaternion;return function(axis,angle){return void 0===quaternion&&(quaternion=new THREE.Quaternion),this.applyQuaternion(quaternion.setFromAxisAngle(axis,angle)),this}}(),applyMatrix3:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[3]*y+e[6]*z,this.y=e[1]*x+e[4]*y+e[7]*z,this.z=e[2]*x+e[5]*y+e[8]*z,this},applyMatrix4:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z+e[12],this.y=e[1]*x+e[5]*y+e[9]*z+e[13],this.z=e[2]*x+e[6]*y+e[10]*z+e[14],this},applyProjection:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements,d=1/(e[3]*x+e[7]*y+e[11]*z+e[15]);return this.x=(e[0]*x+e[4]*y+e[8]*z+e[12])*d,this.y=(e[1]*x+e[5]*y+e[9]*z+e[13])*d,this.z=(e[2]*x+e[6]*y+e[10]*z+e[14])*d,this},applyQuaternion:function(q){var x=this.x,y=this.y,z=this.z,qx=q.x,qy=q.y,qz=q.z,qw=q.w,ix=qw*x+qy*z-qz*y,iy=qw*y+qz*x-qx*z,iz=qw*z+qx*y-qy*x,iw=-qx*x-qy*y-qz*z;return this.x=ix*qw+iw*-qx+iy*-qz-iz*-qy,this.y=iy*qw+iw*-qy+iz*-qx-ix*-qz,this.z=iz*qw+iw*-qz+ix*-qy-iy*-qx,this},project:function(){var matrix;return function(camera){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.multiplyMatrices(camera.projectionMatrix,matrix.getInverse(camera.matrixWorld)),this.applyProjection(matrix)}}(),unproject:function(){var matrix;return function(camera){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.multiplyMatrices(camera.matrixWorld,matrix.getInverse(camera.projectionMatrix)),this.applyProjection(matrix)}}(),transformDirection:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z,this.y=e[1]*x+e[5]*y+e[9]*z,this.z=e[2]*x+e[6]*y+e[10]*z,this.normalize(),this},divide:function(v){return this.x/=v.x,this.y/=v.y,this.z/=v.z,this},divideScalar:function(scalar){return this.multiplyScalar(1/scalar)},min:function(v){return this.x=Math.min(this.x,v.x),this.y=Math.min(this.y,v.y),this.z=Math.min(this.z,v.z),this},max:function(v){return this.x=Math.max(this.x,v.x),this.y=Math.max(this.y,v.y),this.z=Math.max(this.z,v.z),this},clamp:function(min,max){return this.x=Math.max(min.x,Math.min(max.x,this.x)),this.y=Math.max(min.y,Math.min(max.y,this.y)),this.z=Math.max(min.z,Math.min(max.z,this.z)),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector3,max=new THREE.Vector3),min.set(minVal,minVal,minVal),max.set(maxVal,maxVal,maxVal),this.clamp(min,max)}}(),clampLength:function(min,max){var length=this.length();return this.multiplyScalar(Math.max(min,Math.min(max,length))/length),this},floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this},negate:function(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this},dot:function(v){return this.x*v.x+this.y*v.y+this.z*v.z},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length())},setLength:function(length){return this.multiplyScalar(length/this.length())},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this.z+=(v.z-this.z)*alpha,this},lerpVectors:function(v1,v2,alpha){return this.subVectors(v2,v1).multiplyScalar(alpha).add(v1),this},cross:function(v,w){if(void 0!==w)return console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(v,w);var x=this.x,y=this.y,z=this.z;return this.x=y*v.z-z*v.y,this.y=z*v.x-x*v.z,this.z=x*v.y-y*v.x,this},crossVectors:function(a,b){var ax=a.x,ay=a.y,az=a.z,bx=b.x,by=b.y,bz=b.z;return this.x=ay*bz-az*by,this.y=az*bx-ax*bz,this.z=ax*by-ay*bx,this},projectOnVector:function(){var v1,dot;return function(vector){return void 0===v1&&(v1=new THREE.Vector3),v1.copy(vector).normalize(),dot=this.dot(v1),this.copy(v1).multiplyScalar(dot)}}(),projectOnPlane:function(){var v1;return function(planeNormal){return void 0===v1&&(v1=new THREE.Vector3),v1.copy(this).projectOnVector(planeNormal),this.sub(v1)}}(),reflect:function(){var v1;return function(normal){return void 0===v1&&(v1=new THREE.Vector3),this.sub(v1.copy(normal).multiplyScalar(2*this.dot(normal)))}}(),angleTo:function(v){var theta=this.dot(v)/(this.length()*v.length());return Math.acos(THREE.Math.clamp(theta,-1,1))},distanceTo:function(v){return Math.sqrt(this.distanceToSquared(v))},distanceToSquared:function(v){var dx=this.x-v.x,dy=this.y-v.y,dz=this.z-v.z;return dx*dx+dy*dy+dz*dz},setEulerFromRotationMatrix:function(m,order){console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.")},setEulerFromQuaternion:function(q,order){console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.")},getPositionFromMatrix:function(m){return console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()."),this.setFromMatrixPosition(m)},getScaleFromMatrix:function(m){return console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()."),this.setFromMatrixScale(m)},getColumnFromMatrix:function(index,matrix){return console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()."),this.setFromMatrixColumn(index,matrix)},setFromMatrixPosition:function(m){return this.x=m.elements[12],this.y=m.elements[13],this.z=m.elements[14],this},setFromMatrixScale:function(m){var sx=this.set(m.elements[0],m.elements[1],m.elements[2]).length(),sy=this.set(m.elements[4],m.elements[5],m.elements[6]).length(),sz=this.set(m.elements[8],m.elements[9],m.elements[10]).length();return this.x=sx,this.y=sy,this.z=sz,this},setFromMatrixColumn:function(index,matrix){var offset=4*index,me=matrix.elements;return this.x=me[offset],this.y=me[offset+1],this.z=me[offset+2],this},equals:function(v){return v.x===this.x&&v.y===this.y&&v.z===this.z},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this.z=array[offset+2],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array[offset+2]=this.z,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this.z=attribute.array[index+2],this}},THREE.Vector4=function(x,y,z,w){this.x=x||0,this.y=y||0,this.z=z||0,this.w=void 0!==w?w:1},THREE.Vector4.prototype={constructor:THREE.Vector4,set:function(x,y,z,w){return this.x=x,this.y=y,this.z=z,this.w=w,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setZ:function(z){return this.z=z,this},setW:function(w){return this.w=w,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;case 2:this.z=value;break;case 3:this.w=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("index is out of range: "+index)}},clone:function(){return new this.constructor(this.x,this.y,this.z,this.w)},copy:function(v){return this.x=v.x,this.y=v.y,this.z=v.z,this.w=void 0!==v.w?v.w:1,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this.z+=v.z,this.w+=v.w,this)},addScalar:function(s){return this.x+=s,this.y+=s,this.z+=s,this.w+=s,this},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this.z=a.z+b.z,this.w=a.w+b.w,this},addScaledVector:function(v,s){return this.x+=v.x*s,this.y+=v.y*s,this.z+=v.z*s,this.w+=v.w*s,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this.z-=v.z,this.w-=v.w,this)},subScalar:function(s){return this.x-=s,this.y-=s,this.z-=s,this.w-=s,this},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this.z=a.z-b.z,this.w=a.w-b.w,this},multiplyScalar:function(scalar){return isFinite(scalar)?(this.x*=scalar,this.y*=scalar,this.z*=scalar,this.w*=scalar):(this.x=0,this.y=0,this.z=0,this.w=0),this},applyMatrix4:function(m){var x=this.x,y=this.y,z=this.z,w=this.w,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z+e[12]*w,this.y=e[1]*x+e[5]*y+e[9]*z+e[13]*w,this.z=e[2]*x+e[6]*y+e[10]*z+e[14]*w,this.w=e[3]*x+e[7]*y+e[11]*z+e[15]*w,this},divideScalar:function(scalar){return this.multiplyScalar(1/scalar)},setAxisAngleFromQuaternion:function(q){this.w=2*Math.acos(q.w);var s=Math.sqrt(1-q.w*q.w);return 1e-4>s?(this.x=1,this.y=0,this.z=0):(this.x=q.x/s,this.y=q.y/s,this.z=q.z/s),this},setAxisAngleFromRotationMatrix:function(m){var angle,x,y,z,epsilon=.01,epsilon2=.1,te=m.elements,m11=te[0],m12=te[4],m13=te[8],m21=te[1],m22=te[5],m23=te[9],m31=te[2],m32=te[6],m33=te[10];if(Math.abs(m12-m21)yy&&xx>zz?epsilon>xx?(x=0,y=.707106781,z=.707106781):(x=Math.sqrt(xx),y=xy/x,z=xz/x):yy>zz?epsilon>yy?(x=.707106781,y=0,z=.707106781):(y=Math.sqrt(yy),x=xy/y,z=yz/y):epsilon>zz?(x=.707106781,y=.707106781,z=0):(z=Math.sqrt(zz),x=xz/z,y=yz/z),this.set(x,y,z,angle),this}var s=Math.sqrt((m32-m23)*(m32-m23)+(m13-m31)*(m13-m31)+(m21-m12)*(m21-m12));return Math.abs(s)<.001&&(s=1),this.x=(m32-m23)/s,this.y=(m13-m31)/s,this.z=(m21-m12)/s,this.w=Math.acos((m11+m22+m33-1)/2),this},min:function(v){return this.x=Math.min(this.x,v.x),this.y=Math.min(this.y,v.y),this.z=Math.min(this.z,v.z),this.w=Math.min(this.w,v.w),this},max:function(v){return this.x=Math.max(this.x,v.x),this.y=Math.max(this.y,v.y),this.z=Math.max(this.z,v.z),this.w=Math.max(this.w,v.w),this},clamp:function(min,max){return this.x=Math.max(min.x,Math.min(max.x,this.x)),this.y=Math.max(min.y,Math.min(max.y,this.y)),this.z=Math.max(min.z,Math.min(max.z,this.z)),this.w=Math.max(min.w,Math.min(max.w,this.w)),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector4,max=new THREE.Vector4),min.set(minVal,minVal,minVal,minVal),max.set(maxVal,maxVal,maxVal,maxVal),this.clamp(min,max)}}(),floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this.w=Math.floor(this.w),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this.w=Math.ceil(this.w),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this.w=Math.round(this.w),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this.w=this.w<0?Math.ceil(this.w):Math.floor(this.w),this},negate:function(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this.w=-this.w,this},dot:function(v){return this.x*v.x+this.y*v.y+this.z*v.z+this.w*v.w},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)},normalize:function(){return this.divideScalar(this.length())},setLength:function(length){return this.multiplyScalar(length/this.length())},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this.z+=(v.z-this.z)*alpha,this.w+=(v.w-this.w)*alpha,this},lerpVectors:function(v1,v2,alpha){return this.subVectors(v2,v1).multiplyScalar(alpha).add(v1),this},equals:function(v){return v.x===this.x&&v.y===this.y&&v.z===this.z&&v.w===this.w},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this.z=array[offset+2],this.w=array[offset+3],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array[offset+2]=this.z,array[offset+3]=this.w,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this.z=attribute.array[index+2],this.w=attribute.array[index+3],this}},THREE.Euler=function(x,y,z,order){this._x=x||0,this._y=y||0,this._z=z||0,this._order=order||THREE.Euler.DefaultOrder},THREE.Euler.RotationOrders=["XYZ","YZX","ZXY","XZY","YXZ","ZYX"],THREE.Euler.DefaultOrder="XYZ",THREE.Euler.prototype={constructor:THREE.Euler,get x(){return this._x},set x(value){this._x=value,this.onChangeCallback()},get y(){return this._y},set y(value){this._y=value,this.onChangeCallback()},get z(){return this._z},set z(value){this._z=value,this.onChangeCallback()},get order(){return this._order},set order(value){this._order=value,this.onChangeCallback()},set:function(x,y,z,order){return this._x=x,this._y=y,this._z=z,this._order=order||this._order,this.onChangeCallback(),this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._order)},copy:function(euler){return this._x=euler._x,this._y=euler._y,this._z=euler._z,this._order=euler._order,this.onChangeCallback(),this},setFromRotationMatrix:function(m,order,update){var clamp=THREE.Math.clamp,te=m.elements,m11=te[0],m12=te[4],m13=te[8],m21=te[1],m22=te[5],m23=te[9],m31=te[2],m32=te[6],m33=te[10];return order=order||this._order,"XYZ"===order?(this._y=Math.asin(clamp(m13,-1,1)),Math.abs(m13)<.99999?(this._x=Math.atan2(-m23,m33),this._z=Math.atan2(-m12,m11)):(this._x=Math.atan2(m32,m22),this._z=0)):"YXZ"===order?(this._x=Math.asin(-clamp(m23,-1,1)),Math.abs(m23)<.99999?(this._y=Math.atan2(m13,m33),this._z=Math.atan2(m21,m22)):(this._y=Math.atan2(-m31,m11),this._z=0)):"ZXY"===order?(this._x=Math.asin(clamp(m32,-1,1)),Math.abs(m32)<.99999?(this._y=Math.atan2(-m31,m33),this._z=Math.atan2(-m12,m22)):(this._y=0,this._z=Math.atan2(m21,m11))):"ZYX"===order?(this._y=Math.asin(-clamp(m31,-1,1)),Math.abs(m31)<.99999?(this._x=Math.atan2(m32,m33),this._z=Math.atan2(m21,m11)):(this._x=0,this._z=Math.atan2(-m12,m22))):"YZX"===order?(this._z=Math.asin(clamp(m21,-1,1)),Math.abs(m21)<.99999?(this._x=Math.atan2(-m23,m22),this._y=Math.atan2(-m31,m11)):(this._x=0,this._y=Math.atan2(m13,m33))):"XZY"===order?(this._z=Math.asin(-clamp(m12,-1,1)),Math.abs(m12)<.99999?(this._x=Math.atan2(m32,m22),this._y=Math.atan2(m13,m11)):(this._x=Math.atan2(-m23,m33),this._y=0)):console.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+order),this._order=order,update!==!1&&this.onChangeCallback(),this},setFromQuaternion:function(){var matrix;return function(q,order,update){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.makeRotationFromQuaternion(q),this.setFromRotationMatrix(matrix,order,update),this}}(),setFromVector3:function(v,order){return this.set(v.x,v.y,v.z,order||this._order)},reorder:function(){var q=new THREE.Quaternion;return function(newOrder){q.setFromEuler(this),this.setFromQuaternion(q,newOrder)}}(),equals:function(euler){return euler._x===this._x&&euler._y===this._y&&euler._z===this._z&&euler._order===this._order},fromArray:function(array){return this._x=array[0],this._y=array[1],this._z=array[2],void 0!==array[3]&&(this._order=array[3]),this.onChangeCallback(),this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this._x,array[offset+1]=this._y,array[offset+2]=this._z,array[offset+3]=this._order,array},toVector3:function(optionalResult){return optionalResult?optionalResult.set(this._x,this._y,this._z):new THREE.Vector3(this._x,this._y,this._z)},onChange:function(callback){return this.onChangeCallback=callback,this},onChangeCallback:function(){}},THREE.Line3=function(start,end){this.start=void 0!==start?start:new THREE.Vector3,this.end=void 0!==end?end:new THREE.Vector3},THREE.Line3.prototype={constructor:THREE.Line3,set:function(start,end){return this.start.copy(start),this.end.copy(end),this},clone:function(){return(new this.constructor).copy(this)},copy:function(line){return this.start.copy(line.start),this.end.copy(line.end),this},center:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.addVectors(this.start,this.end).multiplyScalar(.5)},delta:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.subVectors(this.end,this.start)},distanceSq:function(){return this.start.distanceToSquared(this.end)},distance:function(){return this.start.distanceTo(this.end)},at:function(t,optionalTarget){var result=optionalTarget||new THREE.Vector3;return this.delta(result).multiplyScalar(t).add(this.start)},closestPointToPointParameter:function(){var startP=new THREE.Vector3,startEnd=new THREE.Vector3;return function(point,clampToLine){startP.subVectors(point,this.start),startEnd.subVectors(this.end,this.start);var startEnd2=startEnd.dot(startEnd),startEnd_startP=startEnd.dot(startP),t=startEnd_startP/startEnd2;return clampToLine&&(t=THREE.Math.clamp(t,0,1)),t}}(),closestPointToPoint:function(point,clampToLine,optionalTarget){var t=this.closestPointToPointParameter(point,clampToLine),result=optionalTarget||new THREE.Vector3;return this.delta(result).multiplyScalar(t).add(this.start)},applyMatrix4:function(matrix){return this.start.applyMatrix4(matrix),this.end.applyMatrix4(matrix),this},equals:function(line){return line.start.equals(this.start)&&line.end.equals(this.end)}},THREE.Box2=function(min,max){this.min=void 0!==min?min:new THREE.Vector2(1/0,1/0),this.max=void 0!==max?max:new THREE.Vector2(-(1/0),-(1/0))},THREE.Box2.prototype={constructor:THREE.Box2,set:function(min,max){return this.min.copy(min),this.max.copy(max),this},setFromPoints:function(points){this.makeEmpty();for(var i=0,il=points.length;il>i;i++)this.expandByPoint(points[i]);return this},setFromCenterAndSize:function(){var v1=new THREE.Vector2;return function(center,size){var halfSize=v1.copy(size).multiplyScalar(.5);return this.min.copy(center).sub(halfSize),this.max.copy(center).add(halfSize),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(box){return this.min.copy(box.min),this.max.copy(box.max),this},makeEmpty:function(){return this.min.x=this.min.y=1/0,this.max.x=this.max.y=-(1/0),this},empty:function(){return this.max.xthis.max.x||point.ythis.max.y)},containsBox:function(box){return this.min.x<=box.min.x&&box.max.x<=this.max.x&&this.min.y<=box.min.y&&box.max.y<=this.max.y},getParameter:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector2;return result.set((point.x-this.min.x)/(this.max.x-this.min.x),(point.y-this.min.y)/(this.max.y-this.min.y))},isIntersectionBox:function(box){return!(box.max.xthis.max.x||box.max.ythis.max.y)},clampPoint:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector2;return result.copy(point).clamp(this.min,this.max)},distanceToPoint:function(){var v1=new THREE.Vector2;return function(point){var clampedPoint=v1.copy(point).clamp(this.min,this.max);return clampedPoint.sub(point).length()}}(),intersect:function(box){return this.min.max(box.min),this.max.min(box.max),this},union:function(box){return this.min.min(box.min),this.max.max(box.max),this},translate:function(offset){return this.min.add(offset),this.max.add(offset),this},equals:function(box){return box.min.equals(this.min)&&box.max.equals(this.max)}},THREE.Box3=function(min,max){this.min=void 0!==min?min:new THREE.Vector3(1/0,1/0,1/0),this.max=void 0!==max?max:new THREE.Vector3(-(1/0),-(1/0),-(1/0))},THREE.Box3.prototype={constructor:THREE.Box3,set:function(min,max){return this.min.copy(min),this.max.copy(max),this},setFromPoints:function(points){this.makeEmpty();for(var i=0,il=points.length;il>i;i++)this.expandByPoint(points[i]);return this},setFromCenterAndSize:function(){var v1=new THREE.Vector3;return function(center,size){var halfSize=v1.copy(size).multiplyScalar(.5);return this.min.copy(center).sub(halfSize),this.max.copy(center).add(halfSize),this}}(),setFromObject:function(){var v1=new THREE.Vector3;return function(object){var scope=this;return object.updateMatrixWorld(!0),this.makeEmpty(),object.traverse(function(node){var geometry=node.geometry;if(void 0!==geometry)if(geometry instanceof THREE.Geometry)for(var vertices=geometry.vertices,i=0,il=vertices.length;il>i;i++)v1.copy(vertices[i]),v1.applyMatrix4(node.matrixWorld),scope.expandByPoint(v1);else if(geometry instanceof THREE.BufferGeometry&&void 0!==geometry.attributes.position)for(var positions=geometry.attributes.position.array,i=0,il=positions.length;il>i;i+=3)v1.set(positions[i],positions[i+1],positions[i+2]),v1.applyMatrix4(node.matrixWorld),scope.expandByPoint(v1)}),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(box){return this.min.copy(box.min),this.max.copy(box.max),this},makeEmpty:function(){return this.min.x=this.min.y=this.min.z=1/0,this.max.x=this.max.y=this.max.z=-(1/0),this},empty:function(){return this.max.xthis.max.x||point.ythis.max.y||point.zthis.max.z)},containsBox:function(box){return this.min.x<=box.min.x&&box.max.x<=this.max.x&&this.min.y<=box.min.y&&box.max.y<=this.max.y&&this.min.z<=box.min.z&&box.max.z<=this.max.z},getParameter:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.set((point.x-this.min.x)/(this.max.x-this.min.x),(point.y-this.min.y)/(this.max.y-this.min.y),(point.z-this.min.z)/(this.max.z-this.min.z))},isIntersectionBox:function(box){return!(box.max.xthis.max.x||box.max.ythis.max.y||box.max.zthis.max.z)},clampPoint:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.copy(point).clamp(this.min,this.max)},distanceToPoint:function(){var v1=new THREE.Vector3;return function(point){ +var clampedPoint=v1.copy(point).clamp(this.min,this.max);return clampedPoint.sub(point).length()}}(),getBoundingSphere:function(){var v1=new THREE.Vector3;return function(optionalTarget){var result=optionalTarget||new THREE.Sphere;return result.center=this.center(),result.radius=.5*this.size(v1).length(),result}}(),intersect:function(box){return this.min.max(box.min),this.max.min(box.max),this},union:function(box){return this.min.min(box.min),this.max.max(box.max),this},applyMatrix4:function(){var points=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3];return function(matrix){return points[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(matrix),points[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(matrix),points[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(matrix),points[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(matrix),points[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(matrix),points[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(matrix),points[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(matrix),points[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(matrix),this.makeEmpty(),this.setFromPoints(points),this}}(),translate:function(offset){return this.min.add(offset),this.max.add(offset),this},equals:function(box){return box.min.equals(this.min)&&box.max.equals(this.max)}},THREE.Matrix3=function(){this.elements=new Float32Array([1,0,0,0,1,0,0,0,1]),arguments.length>0&&console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.")},THREE.Matrix3.prototype={constructor:THREE.Matrix3,set:function(n11,n12,n13,n21,n22,n23,n31,n32,n33){var te=this.elements;return te[0]=n11,te[3]=n12,te[6]=n13,te[1]=n21,te[4]=n22,te[7]=n23,te[2]=n31,te[5]=n32,te[8]=n33,this},identity:function(){return this.set(1,0,0,0,1,0,0,0,1),this},clone:function(){return(new this.constructor).fromArray(this.elements)},copy:function(m){var me=m.elements;return this.set(me[0],me[3],me[6],me[1],me[4],me[7],me[2],me[5],me[8]),this},multiplyVector3:function(vector){return console.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead."),vector.applyMatrix3(this)},multiplyVector3Array:function(a){return console.warn("THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead."),this.applyToVector3Array(a)},applyToVector3Array:function(){var v1;return function(array,offset,length){void 0===v1&&(v1=new THREE.Vector3),void 0===offset&&(offset=0),void 0===length&&(length=array.length);for(var i=0,j=offset;length>i;i+=3,j+=3)v1.fromArray(array,j),v1.applyMatrix3(this),v1.toArray(array,j);return array}}(),applyToBuffer:function(){var v1;return function(buffer,offset,length){void 0===v1&&(v1=new THREE.Vector3),void 0===offset&&(offset=0),void 0===length&&(length=buffer.length/buffer.itemSize);for(var i=0,j=offset;length>i;i++,j++)v1.x=buffer.getX(j),v1.y=buffer.getY(j),v1.z=buffer.getZ(j),v1.applyMatrix3(this),buffer.setXYZ(v1.x,v1.y,v1.z);return buffer}}(),multiplyScalar:function(s){var te=this.elements;return te[0]*=s,te[3]*=s,te[6]*=s,te[1]*=s,te[4]*=s,te[7]*=s,te[2]*=s,te[5]*=s,te[8]*=s,this},determinant:function(){var te=this.elements,a=te[0],b=te[1],c=te[2],d=te[3],e=te[4],f=te[5],g=te[6],h=te[7],i=te[8];return a*e*i-a*f*h-b*d*i+b*f*g+c*d*h-c*e*g},getInverse:function(matrix,throwOnInvertible){var me=matrix.elements,te=this.elements;te[0]=me[10]*me[5]-me[6]*me[9],te[1]=-me[10]*me[1]+me[2]*me[9],te[2]=me[6]*me[1]-me[2]*me[5],te[3]=-me[10]*me[4]+me[6]*me[8],te[4]=me[10]*me[0]-me[2]*me[8],te[5]=-me[6]*me[0]+me[2]*me[4],te[6]=me[9]*me[4]-me[5]*me[8],te[7]=-me[9]*me[0]+me[1]*me[8],te[8]=me[5]*me[0]-me[1]*me[4];var det=me[0]*te[0]+me[1]*te[3]+me[2]*te[6];if(0===det){var msg="Matrix3.getInverse(): can't invert matrix, determinant is 0";if(throwOnInvertible)throw new Error(msg);return console.warn(msg),this.identity(),this}return this.multiplyScalar(1/det),this},transpose:function(){var tmp,m=this.elements;return tmp=m[1],m[1]=m[3],m[3]=tmp,tmp=m[2],m[2]=m[6],m[6]=tmp,tmp=m[5],m[5]=m[7],m[7]=tmp,this},flattenToArrayOffset:function(array,offset){var te=this.elements;return array[offset]=te[0],array[offset+1]=te[1],array[offset+2]=te[2],array[offset+3]=te[3],array[offset+4]=te[4],array[offset+5]=te[5],array[offset+6]=te[6],array[offset+7]=te[7],array[offset+8]=te[8],array},getNormalMatrix:function(m){return this.getInverse(m).transpose(),this},transposeIntoArray:function(r){var m=this.elements;return r[0]=m[0],r[1]=m[3],r[2]=m[6],r[3]=m[1],r[4]=m[4],r[5]=m[7],r[6]=m[2],r[7]=m[5],r[8]=m[8],this},fromArray:function(array){return this.elements.set(array),this},toArray:function(){var te=this.elements;return[te[0],te[1],te[2],te[3],te[4],te[5],te[6],te[7],te[8]]}},THREE.Matrix4=function(){this.elements=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),arguments.length>0&&console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.")},THREE.Matrix4.prototype={constructor:THREE.Matrix4,set:function(n11,n12,n13,n14,n21,n22,n23,n24,n31,n32,n33,n34,n41,n42,n43,n44){var te=this.elements;return te[0]=n11,te[4]=n12,te[8]=n13,te[12]=n14,te[1]=n21,te[5]=n22,te[9]=n23,te[13]=n24,te[2]=n31,te[6]=n32,te[10]=n33,te[14]=n34,te[3]=n41,te[7]=n42,te[11]=n43,te[15]=n44,this},identity:function(){return this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),this},clone:function(){return(new THREE.Matrix4).fromArray(this.elements)},copy:function(m){return this.elements.set(m.elements),this},extractPosition:function(m){return console.warn("THREE.Matrix4: .extractPosition() has been renamed to .copyPosition()."),this.copyPosition(m)},copyPosition:function(m){var te=this.elements,me=m.elements;return te[12]=me[12],te[13]=me[13],te[14]=me[14],this},extractBasis:function(xAxis,yAxis,zAxis){var te=this.elements;return xAxis.set(te[0],te[1],te[2]),yAxis.set(te[4],te[5],te[6]),zAxis.set(te[8],te[9],te[10]),this},makeBasis:function(xAxis,yAxis,zAxis){return this.set(xAxis.x,yAxis.x,zAxis.x,0,xAxis.y,yAxis.y,zAxis.y,0,xAxis.z,yAxis.z,zAxis.z,0,0,0,0,1),this},extractRotation:function(){var v1;return function(m){void 0===v1&&(v1=new THREE.Vector3);var te=this.elements,me=m.elements,scaleX=1/v1.set(me[0],me[1],me[2]).length(),scaleY=1/v1.set(me[4],me[5],me[6]).length(),scaleZ=1/v1.set(me[8],me[9],me[10]).length();return te[0]=me[0]*scaleX,te[1]=me[1]*scaleX,te[2]=me[2]*scaleX,te[4]=me[4]*scaleY,te[5]=me[5]*scaleY,te[6]=me[6]*scaleY,te[8]=me[8]*scaleZ,te[9]=me[9]*scaleZ,te[10]=me[10]*scaleZ,this}}(),makeRotationFromEuler:function(euler){euler instanceof THREE.Euler==!1&&console.error("THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var te=this.elements,x=euler.x,y=euler.y,z=euler.z,a=Math.cos(x),b=Math.sin(x),c=Math.cos(y),d=Math.sin(y),e=Math.cos(z),f=Math.sin(z);if("XYZ"===euler.order){var ae=a*e,af=a*f,be=b*e,bf=b*f;te[0]=c*e,te[4]=-c*f,te[8]=d,te[1]=af+be*d,te[5]=ae-bf*d,te[9]=-b*c,te[2]=bf-ae*d,te[6]=be+af*d,te[10]=a*c}else if("YXZ"===euler.order){var ce=c*e,cf=c*f,de=d*e,df=d*f;te[0]=ce+df*b,te[4]=de*b-cf,te[8]=a*d,te[1]=a*f,te[5]=a*e,te[9]=-b,te[2]=cf*b-de,te[6]=df+ce*b,te[10]=a*c}else if("ZXY"===euler.order){var ce=c*e,cf=c*f,de=d*e,df=d*f;te[0]=ce-df*b,te[4]=-a*f,te[8]=de+cf*b,te[1]=cf+de*b,te[5]=a*e,te[9]=df-ce*b,te[2]=-a*d,te[6]=b,te[10]=a*c}else if("ZYX"===euler.order){var ae=a*e,af=a*f,be=b*e,bf=b*f;te[0]=c*e,te[4]=be*d-af,te[8]=ae*d+bf,te[1]=c*f,te[5]=bf*d+ae,te[9]=af*d-be,te[2]=-d,te[6]=b*c,te[10]=a*c}else if("YZX"===euler.order){var ac=a*c,ad=a*d,bc=b*c,bd=b*d;te[0]=c*e,te[4]=bd-ac*f,te[8]=bc*f+ad,te[1]=f,te[5]=a*e,te[9]=-b*e,te[2]=-d*e,te[6]=ad*f+bc,te[10]=ac-bd*f}else if("XZY"===euler.order){var ac=a*c,ad=a*d,bc=b*c,bd=b*d;te[0]=c*e,te[4]=-f,te[8]=d*e,te[1]=ac*f+bd,te[5]=a*e,te[9]=ad*f-bc,te[2]=bc*f-ad,te[6]=b*e,te[10]=bd*f+ac}return te[3]=0,te[7]=0,te[11]=0,te[12]=0,te[13]=0,te[14]=0,te[15]=1,this},setRotationFromQuaternion:function(q){return console.warn("THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion()."),this.makeRotationFromQuaternion(q)},makeRotationFromQuaternion:function(q){var te=this.elements,x=q.x,y=q.y,z=q.z,w=q.w,x2=x+x,y2=y+y,z2=z+z,xx=x*x2,xy=x*y2,xz=x*z2,yy=y*y2,yz=y*z2,zz=z*z2,wx=w*x2,wy=w*y2,wz=w*z2;return te[0]=1-(yy+zz),te[4]=xy-wz,te[8]=xz+wy,te[1]=xy+wz,te[5]=1-(xx+zz),te[9]=yz-wx,te[2]=xz-wy,te[6]=yz+wx,te[10]=1-(xx+yy),te[3]=0,te[7]=0,te[11]=0,te[12]=0,te[13]=0,te[14]=0,te[15]=1,this},lookAt:function(){var x,y,z;return function(eye,target,up){void 0===x&&(x=new THREE.Vector3),void 0===y&&(y=new THREE.Vector3),void 0===z&&(z=new THREE.Vector3);var te=this.elements;return z.subVectors(eye,target).normalize(),0===z.lengthSq()&&(z.z=1),x.crossVectors(up,z).normalize(),0===x.lengthSq()&&(z.x+=1e-4,x.crossVectors(up,z).normalize()),y.crossVectors(z,x),te[0]=x.x,te[4]=y.x,te[8]=z.x,te[1]=x.y,te[5]=y.y,te[9]=z.y,te[2]=x.z,te[6]=y.z,te[10]=z.z,this}}(),multiply:function(m,n){return void 0!==n?(console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."),this.multiplyMatrices(m,n)):this.multiplyMatrices(this,m)},multiplyMatrices:function(a,b){var ae=a.elements,be=b.elements,te=this.elements,a11=ae[0],a12=ae[4],a13=ae[8],a14=ae[12],a21=ae[1],a22=ae[5],a23=ae[9],a24=ae[13],a31=ae[2],a32=ae[6],a33=ae[10],a34=ae[14],a41=ae[3],a42=ae[7],a43=ae[11],a44=ae[15],b11=be[0],b12=be[4],b13=be[8],b14=be[12],b21=be[1],b22=be[5],b23=be[9],b24=be[13],b31=be[2],b32=be[6],b33=be[10],b34=be[14],b41=be[3],b42=be[7],b43=be[11],b44=be[15];return te[0]=a11*b11+a12*b21+a13*b31+a14*b41,te[4]=a11*b12+a12*b22+a13*b32+a14*b42,te[8]=a11*b13+a12*b23+a13*b33+a14*b43,te[12]=a11*b14+a12*b24+a13*b34+a14*b44,te[1]=a21*b11+a22*b21+a23*b31+a24*b41,te[5]=a21*b12+a22*b22+a23*b32+a24*b42,te[9]=a21*b13+a22*b23+a23*b33+a24*b43,te[13]=a21*b14+a22*b24+a23*b34+a24*b44,te[2]=a31*b11+a32*b21+a33*b31+a34*b41,te[6]=a31*b12+a32*b22+a33*b32+a34*b42,te[10]=a31*b13+a32*b23+a33*b33+a34*b43,te[14]=a31*b14+a32*b24+a33*b34+a34*b44,te[3]=a41*b11+a42*b21+a43*b31+a44*b41,te[7]=a41*b12+a42*b22+a43*b32+a44*b42,te[11]=a41*b13+a42*b23+a43*b33+a44*b43,te[15]=a41*b14+a42*b24+a43*b34+a44*b44,this},multiplyToArray:function(a,b,r){var te=this.elements;return this.multiplyMatrices(a,b),r[0]=te[0],r[1]=te[1],r[2]=te[2],r[3]=te[3],r[4]=te[4],r[5]=te[5],r[6]=te[6],r[7]=te[7],r[8]=te[8],r[9]=te[9],r[10]=te[10],r[11]=te[11],r[12]=te[12],r[13]=te[13],r[14]=te[14],r[15]=te[15],this},multiplyScalar:function(s){var te=this.elements;return te[0]*=s,te[4]*=s,te[8]*=s,te[12]*=s,te[1]*=s,te[5]*=s,te[9]*=s,te[13]*=s,te[2]*=s,te[6]*=s,te[10]*=s,te[14]*=s,te[3]*=s,te[7]*=s,te[11]*=s,te[15]*=s,this},multiplyVector3:function(vector){return console.warn("THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead."),vector.applyProjection(this)},multiplyVector4:function(vector){return console.warn("THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead."),vector.applyMatrix4(this)},multiplyVector3Array:function(a){return console.warn("THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead."),this.applyToVector3Array(a)},applyToVector3Array:function(){var v1;return function(array,offset,length){void 0===v1&&(v1=new THREE.Vector3),void 0===offset&&(offset=0),void 0===length&&(length=array.length);for(var i=0,j=offset;length>i;i+=3,j+=3)v1.fromArray(array,j),v1.applyMatrix4(this),v1.toArray(array,j);return array}}(),applyToBuffer:function(){var v1;return function(buffer,offset,length){void 0===v1&&(v1=new THREE.Vector3),void 0===offset&&(offset=0),void 0===length&&(length=buffer.length/buffer.itemSize);for(var i=0,j=offset;length>i;i++,j++)v1.x=buffer.getX(j),v1.y=buffer.getY(j),v1.z=buffer.getZ(j),v1.applyMatrix4(this),buffer.setXYZ(v1.x,v1.y,v1.z);return buffer}}(),rotateAxis:function(v){console.warn("THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead."),v.transformDirection(this)},crossVector:function(vector){return console.warn("THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead."),vector.applyMatrix4(this)},determinant:function(){var te=this.elements,n11=te[0],n12=te[4],n13=te[8],n14=te[12],n21=te[1],n22=te[5],n23=te[9],n24=te[13],n31=te[2],n32=te[6],n33=te[10],n34=te[14],n41=te[3],n42=te[7],n43=te[11],n44=te[15];return n41*(+n14*n23*n32-n13*n24*n32-n14*n22*n33+n12*n24*n33+n13*n22*n34-n12*n23*n34)+n42*(+n11*n23*n34-n11*n24*n33+n14*n21*n33-n13*n21*n34+n13*n24*n31-n14*n23*n31)+n43*(+n11*n24*n32-n11*n22*n34-n14*n21*n32+n12*n21*n34+n14*n22*n31-n12*n24*n31)+n44*(-n13*n22*n31-n11*n23*n32+n11*n22*n33+n13*n21*n32-n12*n21*n33+n12*n23*n31)},transpose:function(){var tmp,te=this.elements;return tmp=te[1],te[1]=te[4],te[4]=tmp,tmp=te[2],te[2]=te[8],te[8]=tmp,tmp=te[6],te[6]=te[9],te[9]=tmp,tmp=te[3],te[3]=te[12],te[12]=tmp,tmp=te[7],te[7]=te[13],te[13]=tmp,tmp=te[11],te[11]=te[14],te[14]=tmp,this},flattenToArrayOffset:function(array,offset){var te=this.elements;return array[offset]=te[0],array[offset+1]=te[1],array[offset+2]=te[2],array[offset+3]=te[3],array[offset+4]=te[4],array[offset+5]=te[5],array[offset+6]=te[6],array[offset+7]=te[7],array[offset+8]=te[8],array[offset+9]=te[9],array[offset+10]=te[10],array[offset+11]=te[11],array[offset+12]=te[12],array[offset+13]=te[13],array[offset+14]=te[14],array[offset+15]=te[15],array},getPosition:function(){var v1;return function(){void 0===v1&&(v1=new THREE.Vector3),console.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.");var te=this.elements;return v1.set(te[12],te[13],te[14])}}(),setPosition:function(v){var te=this.elements;return te[12]=v.x,te[13]=v.y,te[14]=v.z,this},getInverse:function(m,throwOnInvertible){var te=this.elements,me=m.elements,n11=me[0],n12=me[4],n13=me[8],n14=me[12],n21=me[1],n22=me[5],n23=me[9],n24=me[13],n31=me[2],n32=me[6],n33=me[10],n34=me[14],n41=me[3],n42=me[7],n43=me[11],n44=me[15];te[0]=n23*n34*n42-n24*n33*n42+n24*n32*n43-n22*n34*n43-n23*n32*n44+n22*n33*n44,te[4]=n14*n33*n42-n13*n34*n42-n14*n32*n43+n12*n34*n43+n13*n32*n44-n12*n33*n44,te[8]=n13*n24*n42-n14*n23*n42+n14*n22*n43-n12*n24*n43-n13*n22*n44+n12*n23*n44,te[12]=n14*n23*n32-n13*n24*n32-n14*n22*n33+n12*n24*n33+n13*n22*n34-n12*n23*n34,te[1]=n24*n33*n41-n23*n34*n41-n24*n31*n43+n21*n34*n43+n23*n31*n44-n21*n33*n44,te[5]=n13*n34*n41-n14*n33*n41+n14*n31*n43-n11*n34*n43-n13*n31*n44+n11*n33*n44,te[9]=n14*n23*n41-n13*n24*n41-n14*n21*n43+n11*n24*n43+n13*n21*n44-n11*n23*n44,te[13]=n13*n24*n31-n14*n23*n31+n14*n21*n33-n11*n24*n33-n13*n21*n34+n11*n23*n34,te[2]=n22*n34*n41-n24*n32*n41+n24*n31*n42-n21*n34*n42-n22*n31*n44+n21*n32*n44,te[6]=n14*n32*n41-n12*n34*n41-n14*n31*n42+n11*n34*n42+n12*n31*n44-n11*n32*n44,te[10]=n12*n24*n41-n14*n22*n41+n14*n21*n42-n11*n24*n42-n12*n21*n44+n11*n22*n44,te[14]=n14*n22*n31-n12*n24*n31-n14*n21*n32+n11*n24*n32+n12*n21*n34-n11*n22*n34,te[3]=n23*n32*n41-n22*n33*n41-n23*n31*n42+n21*n33*n42+n22*n31*n43-n21*n32*n43,te[7]=n12*n33*n41-n13*n32*n41+n13*n31*n42-n11*n33*n42-n12*n31*n43+n11*n32*n43,te[11]=n13*n22*n41-n12*n23*n41-n13*n21*n42+n11*n23*n42+n12*n21*n43-n11*n22*n43,te[15]=n12*n23*n31-n13*n22*n31+n13*n21*n32-n11*n23*n32-n12*n21*n33+n11*n22*n33;var det=n11*te[0]+n21*te[4]+n31*te[8]+n41*te[12];if(0===det){var msg="THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0";if(throwOnInvertible)throw new Error(msg);return console.warn(msg),this.identity(),this}return this.multiplyScalar(1/det),this},translate:function(v){console.error("THREE.Matrix4: .translate() has been removed.")},rotateX:function(angle){console.error("THREE.Matrix4: .rotateX() has been removed.")},rotateY:function(angle){console.error("THREE.Matrix4: .rotateY() has been removed.")},rotateZ:function(angle){console.error("THREE.Matrix4: .rotateZ() has been removed.")},rotateByAxis:function(axis,angle){console.error("THREE.Matrix4: .rotateByAxis() has been removed.")},scale:function(v){var te=this.elements,x=v.x,y=v.y,z=v.z;return te[0]*=x,te[4]*=y,te[8]*=z,te[1]*=x,te[5]*=y,te[9]*=z,te[2]*=x,te[6]*=y,te[10]*=z,te[3]*=x,te[7]*=y,te[11]*=z,this},getMaxScaleOnAxis:function(){var te=this.elements,scaleXSq=te[0]*te[0]+te[1]*te[1]+te[2]*te[2],scaleYSq=te[4]*te[4]+te[5]*te[5]+te[6]*te[6],scaleZSq=te[8]*te[8]+te[9]*te[9]+te[10]*te[10];return Math.sqrt(Math.max(scaleXSq,scaleYSq,scaleZSq))},makeTranslation:function(x,y,z){return this.set(1,0,0,x,0,1,0,y,0,0,1,z,0,0,0,1),this},makeRotationX:function(theta){var c=Math.cos(theta),s=Math.sin(theta);return this.set(1,0,0,0,0,c,-s,0,0,s,c,0,0,0,0,1),this},makeRotationY:function(theta){var c=Math.cos(theta),s=Math.sin(theta);return this.set(c,0,s,0,0,1,0,0,-s,0,c,0,0,0,0,1),this},makeRotationZ:function(theta){var c=Math.cos(theta),s=Math.sin(theta);return this.set(c,-s,0,0,s,c,0,0,0,0,1,0,0,0,0,1),this},makeRotationAxis:function(axis,angle){var c=Math.cos(angle),s=Math.sin(angle),t=1-c,x=axis.x,y=axis.y,z=axis.z,tx=t*x,ty=t*y;return this.set(tx*x+c,tx*y-s*z,tx*z+s*y,0,tx*y+s*z,ty*y+c,ty*z-s*x,0,tx*z-s*y,ty*z+s*x,t*z*z+c,0,0,0,0,1),this},makeScale:function(x,y,z){return this.set(x,0,0,0,0,y,0,0,0,0,z,0,0,0,0,1),this},compose:function(position,quaternion,scale){return this.makeRotationFromQuaternion(quaternion),this.scale(scale),this.setPosition(position),this},decompose:function(){var vector,matrix;return function(position,quaternion,scale){void 0===vector&&(vector=new THREE.Vector3),void 0===matrix&&(matrix=new THREE.Matrix4);var te=this.elements,sx=vector.set(te[0],te[1],te[2]).length(),sy=vector.set(te[4],te[5],te[6]).length(),sz=vector.set(te[8],te[9],te[10]).length(),det=this.determinant();0>det&&(sx=-sx),position.x=te[12],position.y=te[13],position.z=te[14],matrix.elements.set(this.elements);var invSX=1/sx,invSY=1/sy,invSZ=1/sz;return matrix.elements[0]*=invSX,matrix.elements[1]*=invSX,matrix.elements[2]*=invSX,matrix.elements[4]*=invSY,matrix.elements[5]*=invSY,matrix.elements[6]*=invSY,matrix.elements[8]*=invSZ,matrix.elements[9]*=invSZ,matrix.elements[10]*=invSZ,quaternion.setFromRotationMatrix(matrix),scale.x=sx,scale.y=sy,scale.z=sz,this}}(),makeFrustum:function(left,right,bottom,top,near,far){var te=this.elements,x=2*near/(right-left),y=2*near/(top-bottom),a=(right+left)/(right-left),b=(top+bottom)/(top-bottom),c=-(far+near)/(far-near),d=-2*far*near/(far-near);return te[0]=x,te[4]=0,te[8]=a,te[12]=0,te[1]=0,te[5]=y,te[9]=b,te[13]=0,te[2]=0,te[6]=0,te[10]=c,te[14]=d,te[3]=0,te[7]=0,te[11]=-1,te[15]=0,this},makePerspective:function(fov,aspect,near,far){var ymax=near*Math.tan(THREE.Math.degToRad(.5*fov)),ymin=-ymax,xmin=ymin*aspect,xmax=ymax*aspect;return this.makeFrustum(xmin,xmax,ymin,ymax,near,far)},makeOrthographic:function(left,right,top,bottom,near,far){var te=this.elements,w=right-left,h=top-bottom,p=far-near,x=(right+left)/w,y=(top+bottom)/h,z=(far+near)/p;return te[0]=2/w,te[4]=0,te[8]=0,te[12]=-x,te[1]=0,te[5]=2/h,te[9]=0,te[13]=-y,te[2]=0,te[6]=0,te[10]=-2/p,te[14]=-z,te[3]=0,te[7]=0,te[11]=0,te[15]=1,this},equals:function(matrix){for(var te=this.elements,me=matrix.elements,i=0;16>i;i++)if(te[i]!==me[i])return!1;return!0},fromArray:function(array){return this.elements.set(array),this},toArray:function(){var te=this.elements;return[te[0],te[1],te[2],te[3],te[4],te[5],te[6],te[7],te[8],te[9],te[10],te[11],te[12],te[13],te[14],te[15]]}},THREE.Ray=function(origin,direction){this.origin=void 0!==origin?origin:new THREE.Vector3,this.direction=void 0!==direction?direction:new THREE.Vector3},THREE.Ray.prototype={constructor:THREE.Ray,set:function(origin,direction){return this.origin.copy(origin),this.direction.copy(direction),this},clone:function(){return(new this.constructor).copy(this)},copy:function(ray){return this.origin.copy(ray.origin),this.direction.copy(ray.direction),this},at:function(t,optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.copy(this.direction).multiplyScalar(t).add(this.origin)},recast:function(){var v1=new THREE.Vector3;return function(t){return this.origin.copy(this.at(t,v1)),this}}(),closestPointToPoint:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector3;result.subVectors(point,this.origin);var directionDistance=result.dot(this.direction);return 0>directionDistance?result.copy(this.origin):result.copy(this.direction).multiplyScalar(directionDistance).add(this.origin)},distanceToPoint:function(point){return Math.sqrt(this.distanceSqToPoint(point))},distanceSqToPoint:function(){var v1=new THREE.Vector3;return function(point){var directionDistance=v1.subVectors(point,this.origin).dot(this.direction);return 0>directionDistance?this.origin.distanceToSquared(point):(v1.copy(this.direction).multiplyScalar(directionDistance).add(this.origin),v1.distanceToSquared(point))}}(),distanceSqToSegment:function(){var segCenter=new THREE.Vector3,segDir=new THREE.Vector3,diff=new THREE.Vector3;return function(v0,v1,optionalPointOnRay,optionalPointOnSegment){segCenter.copy(v0).add(v1).multiplyScalar(.5),segDir.copy(v1).sub(v0).normalize(),diff.copy(this.origin).sub(segCenter);var s0,s1,sqrDist,extDet,segExtent=.5*v0.distanceTo(v1),a01=-this.direction.dot(segDir),b0=diff.dot(this.direction),b1=-diff.dot(segDir),c=diff.lengthSq(),det=Math.abs(1-a01*a01);if(det>0)if(s0=a01*b1-b0,s1=a01*b0-b1,extDet=segExtent*det,s0>=0)if(s1>=-extDet)if(extDet>=s1){var invDet=1/det;s0*=invDet,s1*=invDet,sqrDist=s0*(s0+a01*s1+2*b0)+s1*(a01*s0+s1+2*b1)+c}else s1=segExtent,s0=Math.max(0,-(a01*s1+b0)),sqrDist=-s0*s0+s1*(s1+2*b1)+c;else s1=-segExtent,s0=Math.max(0,-(a01*s1+b0)),sqrDist=-s0*s0+s1*(s1+2*b1)+c;else-extDet>=s1?(s0=Math.max(0,-(-a01*segExtent+b0)),s1=s0>0?-segExtent:Math.min(Math.max(-segExtent,-b1),segExtent),sqrDist=-s0*s0+s1*(s1+2*b1)+c):extDet>=s1?(s0=0,s1=Math.min(Math.max(-segExtent,-b1),segExtent),sqrDist=s1*(s1+2*b1)+c):(s0=Math.max(0,-(a01*segExtent+b0)),s1=s0>0?segExtent:Math.min(Math.max(-segExtent,-b1),segExtent),sqrDist=-s0*s0+s1*(s1+2*b1)+c);else s1=a01>0?-segExtent:segExtent,s0=Math.max(0,-(a01*s1+b0)),sqrDist=-s0*s0+s1*(s1+2*b1)+c;return optionalPointOnRay&&optionalPointOnRay.copy(this.direction).multiplyScalar(s0).add(this.origin),optionalPointOnSegment&&optionalPointOnSegment.copy(segDir).multiplyScalar(s1).add(segCenter),sqrDist}}(),isIntersectionSphere:function(sphere){return this.distanceToPoint(sphere.center)<=sphere.radius},intersectSphere:function(){var v1=new THREE.Vector3;return function(sphere,optionalTarget){v1.subVectors(sphere.center,this.origin);var tca=v1.dot(this.direction),d2=v1.dot(v1)-tca*tca,radius2=sphere.radius*sphere.radius;if(d2>radius2)return null;var thc=Math.sqrt(radius2-d2),t0=tca-thc,t1=tca+thc;return 0>t0&&0>t1?null:0>t0?this.at(t1,optionalTarget):this.at(t0,optionalTarget)}}(),isIntersectionPlane:function(plane){var distToPoint=plane.distanceToPoint(this.origin);if(0===distToPoint)return!0;var denominator=plane.normal.dot(this.direction);return 0>denominator*distToPoint},distanceToPlane:function(plane){var denominator=plane.normal.dot(this.direction);if(0===denominator)return 0===plane.distanceToPoint(this.origin)?0:null;var t=-(this.origin.dot(plane.normal)+plane.constant)/denominator;return t>=0?t:null},intersectPlane:function(plane,optionalTarget){var t=this.distanceToPlane(plane);return null===t?null:this.at(t,optionalTarget)},isIntersectionBox:function(){var v=new THREE.Vector3;return function(box){return null!==this.intersectBox(box,v)}}(),intersectBox:function(box,optionalTarget){var tmin,tmax,tymin,tymax,tzmin,tzmax,invdirx=1/this.direction.x,invdiry=1/this.direction.y,invdirz=1/this.direction.z,origin=this.origin;return invdirx>=0?(tmin=(box.min.x-origin.x)*invdirx,tmax=(box.max.x-origin.x)*invdirx):(tmin=(box.max.x-origin.x)*invdirx,tmax=(box.min.x-origin.x)*invdirx),invdiry>=0?(tymin=(box.min.y-origin.y)*invdiry,tymax=(box.max.y-origin.y)*invdiry):(tymin=(box.max.y-origin.y)*invdiry,tymax=(box.min.y-origin.y)*invdiry),tmin>tymax||tymin>tmax?null:((tymin>tmin||tmin!==tmin)&&(tmin=tymin),(tmax>tymax||tmax!==tmax)&&(tmax=tymax),invdirz>=0?(tzmin=(box.min.z-origin.z)*invdirz,tzmax=(box.max.z-origin.z)*invdirz):(tzmin=(box.max.z-origin.z)*invdirz,tzmax=(box.min.z-origin.z)*invdirz),tmin>tzmax||tzmin>tmax?null:((tzmin>tmin||tmin!==tmin)&&(tmin=tzmin),(tmax>tzmax||tmax!==tmax)&&(tmax=tzmax),0>tmax?null:this.at(tmin>=0?tmin:tmax,optionalTarget)))},intersectTriangle:function(){var diff=new THREE.Vector3,edge1=new THREE.Vector3,edge2=new THREE.Vector3,normal=new THREE.Vector3;return function(a,b,c,backfaceCulling,optionalTarget){edge1.subVectors(b,a),edge2.subVectors(c,a),normal.crossVectors(edge1,edge2);var sign,DdN=this.direction.dot(normal);if(DdN>0){if(backfaceCulling)return null;sign=1}else{if(!(0>DdN))return null;sign=-1,DdN=-DdN}diff.subVectors(this.origin,a);var DdQxE2=sign*this.direction.dot(edge2.crossVectors(diff,edge2));if(0>DdQxE2)return null;var DdE1xQ=sign*this.direction.dot(edge1.cross(diff));if(0>DdE1xQ)return null;if(DdQxE2+DdE1xQ>DdN)return null;var QdN=-sign*diff.dot(normal);return 0>QdN?null:this.at(QdN/DdN,optionalTarget)}}(),applyMatrix4:function(matrix4){return this.direction.add(this.origin).applyMatrix4(matrix4),this.origin.applyMatrix4(matrix4),this.direction.sub(this.origin),this.direction.normalize(),this},equals:function(ray){return ray.origin.equals(this.origin)&&ray.direction.equals(this.direction)}},THREE.Sphere=function(center,radius){this.center=void 0!==center?center:new THREE.Vector3,this.radius=void 0!==radius?radius:0},THREE.Sphere.prototype={constructor:THREE.Sphere,set:function(center,radius){return this.center.copy(center),this.radius=radius,this},setFromPoints:function(){var box=new THREE.Box3;return function(points,optionalCenter){var center=this.center;void 0!==optionalCenter?center.copy(optionalCenter):box.setFromPoints(points).center(center);for(var maxRadiusSq=0,i=0,il=points.length;il>i;i++)maxRadiusSq=Math.max(maxRadiusSq,center.distanceToSquared(points[i]));return this.radius=Math.sqrt(maxRadiusSq),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(sphere){return this.center.copy(sphere.center),this.radius=sphere.radius,this},empty:function(){return this.radius<=0},containsPoint:function(point){return point.distanceToSquared(this.center)<=this.radius*this.radius},distanceToPoint:function(point){return point.distanceTo(this.center)-this.radius},intersectsSphere:function(sphere){var radiusSum=this.radius+sphere.radius;return sphere.center.distanceToSquared(this.center)<=radiusSum*radiusSum},clampPoint:function(point,optionalTarget){var deltaLengthSq=this.center.distanceToSquared(point),result=optionalTarget||new THREE.Vector3;return result.copy(point),deltaLengthSq>this.radius*this.radius&&(result.sub(this.center).normalize(),result.multiplyScalar(this.radius).add(this.center)),result},getBoundingBox:function(optionalTarget){var box=optionalTarget||new THREE.Box3;return box.set(this.center,this.center),box.expandByScalar(this.radius),box},applyMatrix4:function(matrix){return this.center.applyMatrix4(matrix),this.radius=this.radius*matrix.getMaxScaleOnAxis(),this},translate:function(offset){return this.center.add(offset),this},equals:function(sphere){return sphere.center.equals(this.center)&&sphere.radius===this.radius}},THREE.Frustum=function(p0,p1,p2,p3,p4,p5){this.planes=[void 0!==p0?p0:new THREE.Plane,void 0!==p1?p1:new THREE.Plane,void 0!==p2?p2:new THREE.Plane,void 0!==p3?p3:new THREE.Plane,void 0!==p4?p4:new THREE.Plane,void 0!==p5?p5:new THREE.Plane]},THREE.Frustum.prototype={constructor:THREE.Frustum,set:function(p0,p1,p2,p3,p4,p5){var planes=this.planes;return planes[0].copy(p0),planes[1].copy(p1),planes[2].copy(p2),planes[3].copy(p3),planes[4].copy(p4),planes[5].copy(p5),this},clone:function(){return(new this.constructor).copy(this)},copy:function(frustum){for(var planes=this.planes,i=0;6>i;i++)planes[i].copy(frustum.planes[i]);return this},setFromMatrix:function(m){var planes=this.planes,me=m.elements,me0=me[0],me1=me[1],me2=me[2],me3=me[3],me4=me[4],me5=me[5],me6=me[6],me7=me[7],me8=me[8],me9=me[9],me10=me[10],me11=me[11],me12=me[12],me13=me[13],me14=me[14],me15=me[15];return planes[0].setComponents(me3-me0,me7-me4,me11-me8,me15-me12).normalize(),planes[1].setComponents(me3+me0,me7+me4,me11+me8,me15+me12).normalize(),planes[2].setComponents(me3+me1,me7+me5,me11+me9,me15+me13).normalize(),planes[3].setComponents(me3-me1,me7-me5,me11-me9,me15-me13).normalize(),planes[4].setComponents(me3-me2,me7-me6,me11-me10,me15-me14).normalize(),planes[5].setComponents(me3+me2,me7+me6,me11+me10,me15+me14).normalize(),this},intersectsObject:function(){var sphere=new THREE.Sphere;return function(object){var geometry=object.geometry;return null===geometry.boundingSphere&&geometry.computeBoundingSphere(),sphere.copy(geometry.boundingSphere),sphere.applyMatrix4(object.matrixWorld),this.intersectsSphere(sphere)}}(),intersectsSphere:function(sphere){for(var planes=this.planes,center=sphere.center,negRadius=-sphere.radius,i=0;6>i;i++){var distance=planes[i].distanceToPoint(center);if(negRadius>distance)return!1}return!0},intersectsBox:function(){var p1=new THREE.Vector3,p2=new THREE.Vector3;return function(box){for(var planes=this.planes,i=0;6>i;i++){var plane=planes[i];p1.x=plane.normal.x>0?box.min.x:box.max.x,p2.x=plane.normal.x>0?box.max.x:box.min.x,p1.y=plane.normal.y>0?box.min.y:box.max.y,p2.y=plane.normal.y>0?box.max.y:box.min.y,p1.z=plane.normal.z>0?box.min.z:box.max.z,p2.z=plane.normal.z>0?box.max.z:box.min.z;var d1=plane.distanceToPoint(p1),d2=plane.distanceToPoint(p2);if(0>d1&&0>d2)return!1}return!0}}(),containsPoint:function(point){for(var planes=this.planes,i=0;6>i;i++)if(planes[i].distanceToPoint(point)<0)return!1;return!0}},THREE.Plane=function(normal,constant){this.normal=void 0!==normal?normal:new THREE.Vector3(1,0,0),this.constant=void 0!==constant?constant:0},THREE.Plane.prototype={constructor:THREE.Plane,set:function(normal,constant){return this.normal.copy(normal),this.constant=constant,this},setComponents:function(x,y,z,w){return this.normal.set(x,y,z),this.constant=w,this},setFromNormalAndCoplanarPoint:function(normal,point){return this.normal.copy(normal),this.constant=-point.dot(this.normal),this},setFromCoplanarPoints:function(){var v1=new THREE.Vector3,v2=new THREE.Vector3;return function(a,b,c){var normal=v1.subVectors(c,b).cross(v2.subVectors(a,b)).normalize();return this.setFromNormalAndCoplanarPoint(normal,a),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(plane){return this.normal.copy(plane.normal),this.constant=plane.constant,this},normalize:function(){var inverseNormalLength=1/this.normal.length();return this.normal.multiplyScalar(inverseNormalLength),this.constant*=inverseNormalLength,this},negate:function(){return this.constant*=-1,this.normal.negate(),this},distanceToPoint:function(point){return this.normal.dot(point)+this.constant},distanceToSphere:function(sphere){return this.distanceToPoint(sphere.center)-sphere.radius},projectPoint:function(point,optionalTarget){return this.orthoPoint(point,optionalTarget).sub(point).negate()},orthoPoint:function(point,optionalTarget){var perpendicularMagnitude=this.distanceToPoint(point),result=optionalTarget||new THREE.Vector3;return result.copy(this.normal).multiplyScalar(perpendicularMagnitude)},isIntersectionLine:function(line){var startSign=this.distanceToPoint(line.start),endSign=this.distanceToPoint(line.end);return 0>startSign&&endSign>0||0>endSign&&startSign>0},intersectLine:function(){var v1=new THREE.Vector3;return function(line,optionalTarget){var result=optionalTarget||new THREE.Vector3,direction=line.delta(v1),denominator=this.normal.dot(direction);if(0!==denominator){var t=-(line.start.dot(this.normal)+this.constant)/denominator;if(!(0>t||t>1))return result.copy(direction).multiplyScalar(t).add(line.start); +}else if(0===this.distanceToPoint(line.start))return result.copy(line.start)}}(),coplanarPoint:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.copy(this.normal).multiplyScalar(-this.constant)},applyMatrix4:function(){var v1=new THREE.Vector3,v2=new THREE.Vector3,m1=new THREE.Matrix3;return function(matrix,optionalNormalMatrix){var normalMatrix=optionalNormalMatrix||m1.getNormalMatrix(matrix),newNormal=v1.copy(this.normal).applyMatrix3(normalMatrix),newCoplanarPoint=this.coplanarPoint(v2);return newCoplanarPoint.applyMatrix4(matrix),this.setFromNormalAndCoplanarPoint(newNormal,newCoplanarPoint),this}}(),translate:function(offset){return this.constant=this.constant-offset.dot(this.normal),this},equals:function(plane){return plane.normal.equals(this.normal)&&plane.constant===this.constant}},THREE.Math={generateUUID:function(){var r,chars="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),uuid=new Array(36),rnd=0;return function(){for(var i=0;36>i;i++)8===i||13===i||18===i||23===i?uuid[i]="-":14===i?uuid[i]="4":(2>=rnd&&(rnd=33554432+16777216*Math.random()|0),r=15&rnd,rnd>>=4,uuid[i]=chars[19===i?3&r|8:r]);return uuid.join("")}}(),clamp:function(value,min,max){return Math.max(min,Math.min(max,value))},euclideanModulo:function(n,m){return(n%m+m)%m},mapLinear:function(x,a1,a2,b1,b2){return b1+(x-a1)*(b2-b1)/(a2-a1)},smoothstep:function(x,min,max){return min>=x?0:x>=max?1:(x=(x-min)/(max-min),x*x*(3-2*x))},smootherstep:function(x,min,max){return min>=x?0:x>=max?1:(x=(x-min)/(max-min),x*x*x*(x*(6*x-15)+10))},random16:function(){return(65280*Math.random()+255*Math.random())/65535},randInt:function(low,high){return low+Math.floor(Math.random()*(high-low+1))},randFloat:function(low,high){return low+Math.random()*(high-low)},randFloatSpread:function(range){return range*(.5-Math.random())},degToRad:function(){var degreeToRadiansFactor=Math.PI/180;return function(degrees){return degrees*degreeToRadiansFactor}}(),radToDeg:function(){var radianToDegreesFactor=180/Math.PI;return function(radians){return radians*radianToDegreesFactor}}(),isPowerOfTwo:function(value){return 0===(value&value-1)&&0!==value},nearestPowerOfTwo:function(value){return Math.pow(2,Math.round(Math.log(value)/Math.LN2))},nextPowerOfTwo:function(value){return value--,value|=value>>1,value|=value>>2,value|=value>>4,value|=value>>8,value|=value>>16,value++,value}},THREE.Spline=function(points){function interpolate(p0,p1,p2,p3,t,t2,t3){var v0=.5*(p2-p0),v1=.5*(p3-p1);return(2*(p1-p2)+v0+v1)*t3+(-3*(p1-p2)-2*v0-v1)*t2+v0*t+p1}this.points=points;var point,intPoint,weight,w2,w3,pa,pb,pc,pd,c=[],v3={x:0,y:0,z:0};this.initFromArray=function(a){this.points=[];for(var i=0;ithis.points.length-2?this.points.length-1:intPoint+1,c[3]=intPoint>this.points.length-3?this.points.length-1:intPoint+2,pa=this.points[c[0]],pb=this.points[c[1]],pc=this.points[c[2]],pd=this.points[c[3]],w2=weight*weight,w3=weight*w2,v3.x=interpolate(pa.x,pb.x,pc.x,pd.x,weight,w2,w3),v3.y=interpolate(pa.y,pb.y,pc.y,pd.y,weight,w2,w3),v3.z=interpolate(pa.z,pb.z,pc.z,pd.z,weight,w2,w3),v3},this.getControlPointsArray=function(){var i,p,l=this.points.length,coords=[];for(i=0;l>i;i++)p=this.points[i],coords[i]=[p.x,p.y,p.z];return coords},this.getLength=function(nSubDivisions){var i,index,nSamples,position,point=0,intPoint=0,oldIntPoint=0,oldPosition=new THREE.Vector3,tmpVec=new THREE.Vector3,chunkLengths=[],totalLength=0;for(chunkLengths[0]=0,nSubDivisions||(nSubDivisions=100),nSamples=this.points.length*nSubDivisions,oldPosition.copy(this.points[0]),i=1;nSamples>i;i++)index=i/nSamples,position=this.getPoint(index),tmpVec.copy(position),totalLength+=tmpVec.distanceTo(oldPosition),oldPosition.copy(position),point=(this.points.length-1)*index,intPoint=Math.floor(point),intPoint!==oldIntPoint&&(chunkLengths[intPoint]=totalLength,oldIntPoint=intPoint);return chunkLengths[chunkLengths.length]=totalLength,{chunks:chunkLengths,total:totalLength}},this.reparametrizeByArcLength=function(samplingCoef){var i,j,index,indexCurrent,indexNext,realDistance,sampling,position,newpoints=[],tmpVec=new THREE.Vector3,sl=this.getLength();for(newpoints.push(tmpVec.copy(this.points[0]).clone()),i=1;ij;j++)index=indexCurrent+j*(1/sampling)*(indexNext-indexCurrent),position=this.getPoint(index),newpoints.push(tmpVec.copy(position).clone());newpoints.push(tmpVec.copy(this.points[i]).clone())}this.points=newpoints}},THREE.Triangle=function(a,b,c){this.a=void 0!==a?a:new THREE.Vector3,this.b=void 0!==b?b:new THREE.Vector3,this.c=void 0!==c?c:new THREE.Vector3},THREE.Triangle.normal=function(){var v0=new THREE.Vector3;return function(a,b,c,optionalTarget){var result=optionalTarget||new THREE.Vector3;result.subVectors(c,b),v0.subVectors(a,b),result.cross(v0);var resultLengthSq=result.lengthSq();return resultLengthSq>0?result.multiplyScalar(1/Math.sqrt(resultLengthSq)):result.set(0,0,0)}}(),THREE.Triangle.barycoordFromPoint=function(){var v0=new THREE.Vector3,v1=new THREE.Vector3,v2=new THREE.Vector3;return function(point,a,b,c,optionalTarget){v0.subVectors(c,a),v1.subVectors(b,a),v2.subVectors(point,a);var dot00=v0.dot(v0),dot01=v0.dot(v1),dot02=v0.dot(v2),dot11=v1.dot(v1),dot12=v1.dot(v2),denom=dot00*dot11-dot01*dot01,result=optionalTarget||new THREE.Vector3;if(0===denom)return result.set(-2,-1,-1);var invDenom=1/denom,u=(dot11*dot02-dot01*dot12)*invDenom,v=(dot00*dot12-dot01*dot02)*invDenom;return result.set(1-u-v,v,u)}}(),THREE.Triangle.containsPoint=function(){var v1=new THREE.Vector3;return function(point,a,b,c){var result=THREE.Triangle.barycoordFromPoint(point,a,b,c,v1);return result.x>=0&&result.y>=0&&result.x+result.y<=1}}(),THREE.Triangle.prototype={constructor:THREE.Triangle,set:function(a,b,c){return this.a.copy(a),this.b.copy(b),this.c.copy(c),this},setFromPointsAndIndices:function(points,i0,i1,i2){return this.a.copy(points[i0]),this.b.copy(points[i1]),this.c.copy(points[i2]),this},clone:function(){return(new this.constructor).copy(this)},copy:function(triangle){return this.a.copy(triangle.a),this.b.copy(triangle.b),this.c.copy(triangle.c),this},area:function(){var v0=new THREE.Vector3,v1=new THREE.Vector3;return function(){return v0.subVectors(this.c,this.b),v1.subVectors(this.a,this.b),.5*v0.cross(v1).length()}}(),midpoint:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},normal:function(optionalTarget){return THREE.Triangle.normal(this.a,this.b,this.c,optionalTarget)},plane:function(optionalTarget){var result=optionalTarget||new THREE.Plane;return result.setFromCoplanarPoints(this.a,this.b,this.c)},barycoordFromPoint:function(point,optionalTarget){return THREE.Triangle.barycoordFromPoint(point,this.a,this.b,this.c,optionalTarget)},containsPoint:function(point){return THREE.Triangle.containsPoint(point,this.a,this.b,this.c)},equals:function(triangle){return triangle.a.equals(this.a)&&triangle.b.equals(this.b)&&triangle.c.equals(this.c)}},THREE.Channels=function(){this.mask=1},THREE.Channels.prototype={constructor:THREE.Channels,set:function(channel){this.mask=1<i;i++)array[i]=listenerArray[i];for(var i=0;length>i;i++)array[i].call(this,event)}}}},function(THREE){function descSort(a,b){return a.distance-b.distance}function intersectObject(object,raycaster,intersects,recursive){if(object.visible!==!1&&(object.raycast(raycaster,intersects),recursive===!0))for(var children=object.children,i=0,l=children.length;l>i;i++)intersectObject(children[i],raycaster,intersects,!0)}THREE.Raycaster=function(origin,direction,near,far){this.ray=new THREE.Ray(origin,direction),this.near=near||0,this.far=far||1/0,this.params={Mesh:{},Line:{},LOD:{},Points:{threshold:1},Sprite:{}},Object.defineProperties(this.params,{PointCloud:{get:function(){return console.warn("THREE.Raycaster: params.PointCloud has been renamed to params.Points."),this.Points}}})},THREE.Raycaster.prototype={constructor:THREE.Raycaster,linePrecision:1,set:function(origin,direction){this.ray.set(origin,direction)},setFromCamera:function(coords,camera){camera instanceof THREE.PerspectiveCamera?(this.ray.origin.setFromMatrixPosition(camera.matrixWorld),this.ray.direction.set(coords.x,coords.y,.5).unproject(camera).sub(this.ray.origin).normalize()):camera instanceof THREE.OrthographicCamera?(this.ray.origin.set(coords.x,coords.y,-1).unproject(camera),this.ray.direction.set(0,0,-1).transformDirection(camera.matrixWorld)):console.error("THREE.Raycaster: Unsupported camera type.")},intersectObject:function(object,recursive){var intersects=[];return intersectObject(object,this,intersects,recursive),intersects.sort(descSort),intersects},intersectObjects:function(objects,recursive){var intersects=[];if(Array.isArray(objects)===!1)return console.warn("THREE.Raycaster.intersectObjects: objects is not an Array."),intersects;for(var i=0,l=objects.length;l>i;i++)intersectObject(objects[i],this,intersects,recursive);return intersects.sort(descSort),intersects}}}(THREE),THREE.Object3D=function(){function onRotationChange(){quaternion.setFromEuler(rotation,!1)}function onQuaternionChange(){rotation.setFromQuaternion(quaternion,void 0,!1)}Object.defineProperty(this,"id",{value:THREE.Object3DIdCount++}),this.uuid=THREE.Math.generateUUID(),this.name="",this.type="Object3D",this.parent=null,this.channels=new THREE.Channels,this.children=[],this.up=THREE.Object3D.DefaultUp.clone();var position=new THREE.Vector3,rotation=new THREE.Euler,quaternion=new THREE.Quaternion,scale=new THREE.Vector3(1,1,1);rotation.onChange(onRotationChange),quaternion.onChange(onQuaternionChange),Object.defineProperties(this,{position:{enumerable:!0,value:position},rotation:{enumerable:!0,value:rotation},quaternion:{enumerable:!0,value:quaternion},scale:{enumerable:!0,value:scale},modelViewMatrix:{value:new THREE.Matrix4},normalMatrix:{value:new THREE.Matrix3}}),this.rotationAutoUpdate=!0,this.matrix=new THREE.Matrix4,this.matrixWorld=new THREE.Matrix4,this.matrixAutoUpdate=THREE.Object3D.DefaultMatrixAutoUpdate,this.matrixWorldNeedsUpdate=!1,this.visible=!0,this.castShadow=!1,this.receiveShadow=!1,this.frustumCulled=!0,this.renderOrder=0,this.userData={}},THREE.Object3D.DefaultUp=new THREE.Vector3(0,1,0),THREE.Object3D.DefaultMatrixAutoUpdate=!0,THREE.Object3D.prototype={constructor:THREE.Object3D,get eulerOrder(){return console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."),this.rotation.order},set eulerOrder(value){console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."),this.rotation.order=value},get useQuaternion(){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},set useQuaternion(value){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},set renderDepth(value){console.warn("THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.")},applyMatrix:function(matrix){this.matrix.multiplyMatrices(matrix,this.matrix),this.matrix.decompose(this.position,this.quaternion,this.scale)},setRotationFromAxisAngle:function(axis,angle){this.quaternion.setFromAxisAngle(axis,angle)},setRotationFromEuler:function(euler){this.quaternion.setFromEuler(euler,!0)},setRotationFromMatrix:function(m){this.quaternion.setFromRotationMatrix(m)},setRotationFromQuaternion:function(q){this.quaternion.copy(q)},rotateOnAxis:function(){var q1=new THREE.Quaternion;return function(axis,angle){return q1.setFromAxisAngle(axis,angle),this.quaternion.multiply(q1),this}}(),rotateX:function(){var v1=new THREE.Vector3(1,0,0);return function(angle){return this.rotateOnAxis(v1,angle)}}(),rotateY:function(){var v1=new THREE.Vector3(0,1,0);return function(angle){return this.rotateOnAxis(v1,angle)}}(),rotateZ:function(){var v1=new THREE.Vector3(0,0,1);return function(angle){return this.rotateOnAxis(v1,angle)}}(),translateOnAxis:function(){var v1=new THREE.Vector3;return function(axis,distance){return v1.copy(axis).applyQuaternion(this.quaternion),this.position.add(v1.multiplyScalar(distance)),this}}(),translate:function(distance,axis){return console.warn("THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead."),this.translateOnAxis(axis,distance)},translateX:function(){var v1=new THREE.Vector3(1,0,0);return function(distance){return this.translateOnAxis(v1,distance)}}(),translateY:function(){var v1=new THREE.Vector3(0,1,0);return function(distance){return this.translateOnAxis(v1,distance)}}(),translateZ:function(){var v1=new THREE.Vector3(0,0,1);return function(distance){return this.translateOnAxis(v1,distance)}}(),localToWorld:function(vector){return vector.applyMatrix4(this.matrixWorld)},worldToLocal:function(){var m1=new THREE.Matrix4;return function(vector){return vector.applyMatrix4(m1.getInverse(this.matrixWorld))}}(),lookAt:function(){var m1=new THREE.Matrix4;return function(vector){m1.lookAt(vector,this.position,this.up),this.quaternion.setFromRotationMatrix(m1)}}(),add:function(object){if(arguments.length>1){for(var i=0;i1)for(var i=0;ii;i++){var child=this.children[i],object=child.getObjectByProperty(name,value);if(void 0!==object)return object}},getWorldPosition:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return this.updateMatrixWorld(!0),result.setFromMatrixPosition(this.matrixWorld)},getWorldQuaternion:function(){var position=new THREE.Vector3,scale=new THREE.Vector3;return function(optionalTarget){var result=optionalTarget||new THREE.Quaternion;return this.updateMatrixWorld(!0),this.matrixWorld.decompose(position,result,scale),result}}(),getWorldRotation:function(){var quaternion=new THREE.Quaternion;return function(optionalTarget){var result=optionalTarget||new THREE.Euler;return this.getWorldQuaternion(quaternion),result.setFromQuaternion(quaternion,this.rotation.order,!1)}}(),getWorldScale:function(){var position=new THREE.Vector3,quaternion=new THREE.Quaternion;return function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return this.updateMatrixWorld(!0),this.matrixWorld.decompose(position,quaternion,result),result}}(),getWorldDirection:function(){var quaternion=new THREE.Quaternion;return function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return this.getWorldQuaternion(quaternion),result.set(0,0,1).applyQuaternion(quaternion)}}(),raycast:function(){},traverse:function(callback){callback(this);for(var children=this.children,i=0,l=children.length;l>i;i++)children[i].traverse(callback)},traverseVisible:function(callback){if(this.visible!==!1){callback(this);for(var children=this.children,i=0,l=children.length;l>i;i++)children[i].traverseVisible(callback)}},traverseAncestors:function(callback){var parent=this.parent;null!==parent&&(callback(parent),parent.traverseAncestors(callback))},updateMatrix:function(){this.matrix.compose(this.position,this.quaternion,this.scale),this.matrixWorldNeedsUpdate=!0},updateMatrixWorld:function(force){this.matrixAutoUpdate===!0&&this.updateMatrix(),this.matrixWorldNeedsUpdate!==!0&&force!==!0||(null===this.parent?this.matrixWorld.copy(this.matrix):this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix),this.matrixWorldNeedsUpdate=!1,force=!0);for(var i=0,l=this.children.length;l>i;i++)this.children[i].updateMatrixWorld(force)},toJSON:function(meta){function extractFromCache(cache){var values=[];for(var key in cache){var data=cache[key];delete data.metadata,values.push(data)}return values}var isRootObject=void 0===meta,output={};isRootObject&&(meta={geometries:{},materials:{},textures:{},images:{}},output.metadata={version:4.4,type:"Object",generator:"Object3D.toJSON"});var object={};if(object.uuid=this.uuid,object.type=this.type,""!==this.name&&(object.name=this.name),"{}"!==JSON.stringify(this.userData)&&(object.userData=this.userData),this.castShadow===!0&&(object.castShadow=!0),this.receiveShadow===!0&&(object.receiveShadow=!0),this.visible===!1&&(object.visible=!1),object.matrix=this.matrix.toArray(),void 0!==this.geometry&&(void 0===meta.geometries[this.geometry.uuid]&&(meta.geometries[this.geometry.uuid]=this.geometry.toJSON(meta)),object.geometry=this.geometry.uuid),void 0!==this.material&&(void 0===meta.materials[this.material.uuid]&&(meta.materials[this.material.uuid]=this.material.toJSON(meta)),object.material=this.material.uuid),this.children.length>0){object.children=[];for(var i=0;i0&&(output.geometries=geometries),materials.length>0&&(output.materials=materials),textures.length>0&&(output.textures=textures),images.length>0&&(output.images=images)}return output.object=object,output},clone:function(recursive){return(new this.constructor).copy(this,recursive)},copy:function(source,recursive){if(void 0===recursive&&(recursive=!0),this.name=source.name,this.up.copy(source.up),this.position.copy(source.position),this.quaternion.copy(source.quaternion),this.scale.copy(source.scale),this.rotationAutoUpdate=source.rotationAutoUpdate,this.matrix.copy(source.matrix),this.matrixWorld.copy(source.matrixWorld),this.matrixAutoUpdate=source.matrixAutoUpdate,this.matrixWorldNeedsUpdate=source.matrixWorldNeedsUpdate,this.visible=source.visible,this.castShadow=source.castShadow,this.receiveShadow=source.receiveShadow,this.frustumCulled=source.frustumCulled,this.renderOrder=source.renderOrder,this.userData=JSON.parse(JSON.stringify(source.userData)),recursive===!0)for(var i=0;ii;i++)this.vertexNormals[i]=source.vertexNormals[i].clone();for(var i=0,il=source.vertexColors.length;il>i;i++)this.vertexColors[i]=source.vertexColors[i].clone();return this}},THREE.Face4=function(a,b,c,d,normal,color,materialIndex){return console.warn("THREE.Face4 has been removed. A THREE.Face3 will be created instead."),new THREE.Face3(a,b,c,normal,color,materialIndex)},THREE.BufferAttribute=function(array,itemSize){this.uuid=THREE.Math.generateUUID(),this.array=array,this.itemSize=itemSize,this.dynamic=!1,this.updateRange={offset:0,count:-1},this.version=0},THREE.BufferAttribute.prototype={constructor:THREE.BufferAttribute,get length(){return console.warn("THREE.BufferAttribute: .length has been deprecated. Please use .count."),this.array.length},get count(){return this.array.length/this.itemSize},set needsUpdate(value){value===!0&&this.version++},setDynamic:function(value){return this.dynamic=value,this},copy:function(source){return this.array=new source.array.constructor(source.array),this.itemSize=source.itemSize,this.dynamic=source.dynamic,this},copyAt:function(index1,attribute,index2){index1*=this.itemSize,index2*=attribute.itemSize;for(var i=0,l=this.itemSize;l>i;i++)this.array[index1+i]=attribute.array[index2+i];return this},copyArray:function(array){return this.array.set(array),this},copyColorsArray:function(colors){for(var array=this.array,offset=0,i=0,l=colors.length;l>i;i++){var color=colors[i];void 0===color&&(console.warn("THREE.BufferAttribute.copyColorsArray(): color is undefined",i),color=new THREE.Color),array[offset++]=color.r,array[offset++]=color.g,array[offset++]=color.b}return this},copyIndicesArray:function(indices){for(var array=this.array,offset=0,i=0,l=indices.length;l>i;i++){var index=indices[i];array[offset++]=index.a,array[offset++]=index.b,array[offset++]=index.c}return this},copyVector2sArray:function(vectors){for(var array=this.array,offset=0,i=0,l=vectors.length;l>i;i++){var vector=vectors[i];void 0===vector&&(console.warn("THREE.BufferAttribute.copyVector2sArray(): vector is undefined",i),vector=new THREE.Vector2),array[offset++]=vector.x,array[offset++]=vector.y}return this},copyVector3sArray:function(vectors){for(var array=this.array,offset=0,i=0,l=vectors.length;l>i;i++){var vector=vectors[i];void 0===vector&&(console.warn("THREE.BufferAttribute.copyVector3sArray(): vector is undefined",i),vector=new THREE.Vector3),array[offset++]=vector.x,array[offset++]=vector.y,array[offset++]=vector.z}return this},copyVector4sArray:function(vectors){for(var array=this.array,offset=0,i=0,l=vectors.length;l>i;i++){var vector=vectors[i];void 0===vector&&(console.warn("THREE.BufferAttribute.copyVector4sArray(): vector is undefined",i),vector=new THREE.Vector4),array[offset++]=vector.x,array[offset++]=vector.y,array[offset++]=vector.z,array[offset++]=vector.w}return this},set:function(value,offset){return void 0===offset&&(offset=0),this.array.set(value,offset),this},getX:function(index){return this.array[index*this.itemSize]},setX:function(index,x){return this.array[index*this.itemSize]=x,this},getY:function(index){return this.array[index*this.itemSize+1]},setY:function(index,y){return this.array[index*this.itemSize+1]=y,this},getZ:function(index){return this.array[index*this.itemSize+2]},setZ:function(index,z){return this.array[index*this.itemSize+2]=z,this},getW:function(index){return this.array[index*this.itemSize+3]},setW:function(index,w){return this.array[index*this.itemSize+3]=w,this},setXY:function(index,x,y){return index*=this.itemSize,this.array[index+0]=x,this.array[index+1]=y,this},setXYZ:function(index,x,y,z){return index*=this.itemSize,this.array[index+0]=x,this.array[index+1]=y,this.array[index+2]=z,this},setXYZW:function(index,x,y,z,w){return index*=this.itemSize,this.array[index+0]=x,this.array[index+1]=y,this.array[index+2]=z,this.array[index+3]=w,this},clone:function(){return(new this.constructor).copy(this)}},THREE.Int8Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Int8Array(array),itemSize)},THREE.Uint8Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Uint8Array(array),itemSize)},THREE.Uint8ClampedAttribute=function(array,itemSize){return new THREE.BufferAttribute(new Uint8ClampedArray(array),itemSize)},THREE.Int16Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Int16Array(array),itemSize)},THREE.Uint16Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Uint16Array(array),itemSize)},THREE.Int32Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Int32Array(array),itemSize)},THREE.Uint32Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Uint32Array(array),itemSize)},THREE.Float32Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Float32Array(array),itemSize)},THREE.Float64Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Float64Array(array),itemSize)},THREE.DynamicBufferAttribute=function(array,itemSize){return console.warn("THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead."),new THREE.BufferAttribute(array,itemSize).setDynamic(!0)},THREE.InstancedBufferAttribute=function(array,itemSize,meshPerAttribute){THREE.BufferAttribute.call(this,array,itemSize),this.meshPerAttribute=meshPerAttribute||1},THREE.InstancedBufferAttribute.prototype=Object.create(THREE.BufferAttribute.prototype),THREE.InstancedBufferAttribute.prototype.constructor=THREE.InstancedBufferAttribute,THREE.InstancedBufferAttribute.prototype.copy=function(source){return THREE.BufferAttribute.prototype.copy.call(this,source),this.meshPerAttribute=source.meshPerAttribute,this},THREE.InterleavedBuffer=function(array,stride){this.uuid=THREE.Math.generateUUID(),this.array=array,this.stride=stride,this.dynamic=!1,this.updateRange={offset:0,count:-1},this.version=0},THREE.InterleavedBuffer.prototype={constructor:THREE.InterleavedBuffer,get length(){return this.array.length},get count(){return this.array.length/this.stride},set needsUpdate(value){value===!0&&this.version++},setDynamic:function(value){return this.dynamic=value,this},copy:function(source){this.array=new source.array.constructor(source.array),this.stride=source.stride,this.dynamic=source.dynamic},copyAt:function(index1,attribute,index2){index1*=this.stride,index2*=attribute.stride;for(var i=0,l=this.stride;l>i;i++)this.array[index1+i]=attribute.array[index2+i];return this},set:function(value,offset){return void 0===offset&&(offset=0),this.array.set(value,offset),this},clone:function(){return(new this.constructor).copy(this)}},THREE.InstancedInterleavedBuffer=function(array,stride,meshPerAttribute){THREE.InterleavedBuffer.call(this,array,stride),this.meshPerAttribute=meshPerAttribute||1},THREE.InstancedInterleavedBuffer.prototype=Object.create(THREE.InterleavedBuffer.prototype),THREE.InstancedInterleavedBuffer.prototype.constructor=THREE.InstancedInterleavedBuffer,THREE.InstancedInterleavedBuffer.prototype.copy=function(source){return THREE.InterleavedBuffer.prototype.copy.call(this,source),this.meshPerAttribute=source.meshPerAttribute,this},THREE.InterleavedBufferAttribute=function(interleavedBuffer,itemSize,offset){this.uuid=THREE.Math.generateUUID(),this.data=interleavedBuffer,this.itemSize=itemSize,this.offset=offset},THREE.InterleavedBufferAttribute.prototype={constructor:THREE.InterleavedBufferAttribute,get length(){return console.warn("THREE.BufferAttribute: .length has been deprecated. Please use .count."),this.array.length},get count(){return this.data.array.length/this.data.stride},setX:function(index,x){return this.data.array[index*this.data.stride+this.offset]=x,this},setY:function(index,y){return this.data.array[index*this.data.stride+this.offset+1]=y,this},setZ:function(index,z){return this.data.array[index*this.data.stride+this.offset+2]=z,this},setW:function(index,w){return this.data.array[index*this.data.stride+this.offset+3]=w,this},getX:function(index){return this.data.array[index*this.data.stride+this.offset]},getY:function(index){return this.data.array[index*this.data.stride+this.offset+1]},getZ:function(index){return this.data.array[index*this.data.stride+this.offset+2]},getW:function(index){return this.data.array[index*this.data.stride+this.offset+3]},setXY:function(index,x,y){return index=index*this.data.stride+this.offset,this.data.array[index+0]=x,this.data.array[index+1]=y,this},setXYZ:function(index,x,y,z){return index=index*this.data.stride+this.offset,this.data.array[index+0]=x,this.data.array[index+1]=y,this.data.array[index+2]=z,this},setXYZW:function(index,x,y,z,w){return index=index*this.data.stride+this.offset,this.data.array[index+0]=x,this.data.array[index+1]=y,this.data.array[index+2]=z,this.data.array[index+3]=w,this}},THREE.Geometry=function(){Object.defineProperty(this,"id",{value:THREE.GeometryIdCount++}),this.uuid=THREE.Math.generateUUID(),this.name="",this.type="Geometry",this.vertices=[],this.colors=[],this.faces=[],this.faceVertexUvs=[[]],this.morphTargets=[],this.morphNormals=[],this.skinWeights=[],this.skinIndices=[],this.lineDistances=[],this.boundingBox=null,this.boundingSphere=null,this.verticesNeedUpdate=!1,this.elementsNeedUpdate=!1,this.uvsNeedUpdate=!1,this.normalsNeedUpdate=!1,this.colorsNeedUpdate=!1,this.lineDistancesNeedUpdate=!1,this.groupsNeedUpdate=!1},THREE.Geometry.prototype={constructor:THREE.Geometry,applyMatrix:function(matrix){for(var normalMatrix=(new THREE.Matrix3).getNormalMatrix(matrix),i=0,il=this.vertices.length;il>i;i++){var vertex=this.vertices[i];vertex.applyMatrix4(matrix)}for(var i=0,il=this.faces.length;il>i;i++){var face=this.faces[i];face.normal.applyMatrix3(normalMatrix).normalize();for(var j=0,jl=face.vertexNormals.length;jl>j;j++)face.vertexNormals[j].applyMatrix3(normalMatrix).normalize(); +}null!==this.boundingBox&&this.computeBoundingBox(),null!==this.boundingSphere&&this.computeBoundingSphere(),this.verticesNeedUpdate=!0,this.normalsNeedUpdate=!0},rotateX:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationX(angle),this.applyMatrix(m1),this}}(),rotateY:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationY(angle),this.applyMatrix(m1),this}}(),rotateZ:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationZ(angle),this.applyMatrix(m1),this}}(),translate:function(){var m1;return function(x,y,z){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeTranslation(x,y,z),this.applyMatrix(m1),this}}(),scale:function(){var m1;return function(x,y,z){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeScale(x,y,z),this.applyMatrix(m1),this}}(),lookAt:function(){var obj;return function(vector){void 0===obj&&(obj=new THREE.Object3D),obj.lookAt(vector),obj.updateMatrix(),this.applyMatrix(obj.matrix)}}(),fromBufferGeometry:function(geometry){function addFace(a,b,c){var vertexNormals=void 0!==normals?[tempNormals[a].clone(),tempNormals[b].clone(),tempNormals[c].clone()]:[],vertexColors=void 0!==colors?[scope.colors[a].clone(),scope.colors[b].clone(),scope.colors[c].clone()]:[],face=new THREE.Face3(a,b,c,vertexNormals,vertexColors);scope.faces.push(face),void 0!==uvs&&scope.faceVertexUvs[0].push([tempUVs[a].clone(),tempUVs[b].clone(),tempUVs[c].clone()]),void 0!==uvs2&&scope.faceVertexUvs[1].push([tempUVs2[a].clone(),tempUVs2[b].clone(),tempUVs2[c].clone()])}var scope=this,indices=null!==geometry.index?geometry.index.array:void 0,attributes=geometry.attributes,vertices=attributes.position.array,normals=void 0!==attributes.normal?attributes.normal.array:void 0,colors=void 0!==attributes.color?attributes.color.array:void 0,uvs=void 0!==attributes.uv?attributes.uv.array:void 0,uvs2=void 0!==attributes.uv2?attributes.uv2.array:void 0;void 0!==uvs2&&(this.faceVertexUvs[1]=[]);for(var tempNormals=[],tempUVs=[],tempUVs2=[],i=0,j=0,k=0;i0)for(var i=0;ij;j+=3)addFace(indices[j],indices[j+1],indices[j+2]);else for(var i=0;if;f++){var face=this.faces[f],vA=this.vertices[face.a],vB=this.vertices[face.b],vC=this.vertices[face.c];cb.subVectors(vC,vB),ab.subVectors(vA,vB),cb.cross(ab),cb.normalize(),face.normal.copy(cb)}},computeVertexNormals:function(areaWeighted){var v,vl,f,fl,face,vertices;for(vertices=new Array(this.vertices.length),v=0,vl=this.vertices.length;vl>v;v++)vertices[v]=new THREE.Vector3;if(areaWeighted){var vA,vB,vC,cb=new THREE.Vector3,ab=new THREE.Vector3;for(f=0,fl=this.faces.length;fl>f;f++)face=this.faces[f],vA=this.vertices[face.a],vB=this.vertices[face.b],vC=this.vertices[face.c],cb.subVectors(vC,vB),ab.subVectors(vA,vB),cb.cross(ab),vertices[face.a].add(cb),vertices[face.b].add(cb),vertices[face.c].add(cb)}else for(f=0,fl=this.faces.length;fl>f;f++)face=this.faces[f],vertices[face.a].add(face.normal),vertices[face.b].add(face.normal),vertices[face.c].add(face.normal);for(v=0,vl=this.vertices.length;vl>v;v++)vertices[v].normalize();for(f=0,fl=this.faces.length;fl>f;f++){face=this.faces[f];var vertexNormals=face.vertexNormals;3===vertexNormals.length?(vertexNormals[0].copy(vertices[face.a]),vertexNormals[1].copy(vertices[face.b]),vertexNormals[2].copy(vertices[face.c])):(vertexNormals[0]=vertices[face.a].clone(),vertexNormals[1]=vertices[face.b].clone(),vertexNormals[2]=vertices[face.c].clone())}},computeMorphNormals:function(){var i,il,f,fl,face;for(f=0,fl=this.faces.length;fl>f;f++)for(face=this.faces[f],face.__originalFaceNormal?face.__originalFaceNormal.copy(face.normal):face.__originalFaceNormal=face.normal.clone(),face.__originalVertexNormals||(face.__originalVertexNormals=[]),i=0,il=face.vertexNormals.length;il>i;i++)face.__originalVertexNormals[i]?face.__originalVertexNormals[i].copy(face.vertexNormals[i]):face.__originalVertexNormals[i]=face.vertexNormals[i].clone();var tmpGeo=new THREE.Geometry;for(tmpGeo.faces=this.faces,i=0,il=this.morphTargets.length;il>i;i++){if(!this.morphNormals[i]){this.morphNormals[i]={},this.morphNormals[i].faceNormals=[],this.morphNormals[i].vertexNormals=[];var faceNormal,vertexNormals,dstNormalsFace=this.morphNormals[i].faceNormals,dstNormalsVertex=this.morphNormals[i].vertexNormals;for(f=0,fl=this.faces.length;fl>f;f++)faceNormal=new THREE.Vector3,vertexNormals={a:new THREE.Vector3,b:new THREE.Vector3,c:new THREE.Vector3},dstNormalsFace.push(faceNormal),dstNormalsVertex.push(vertexNormals)}var morphNormals=this.morphNormals[i];tmpGeo.vertices=this.morphTargets[i].vertices,tmpGeo.computeFaceNormals(),tmpGeo.computeVertexNormals();var faceNormal,vertexNormals;for(f=0,fl=this.faces.length;fl>f;f++)face=this.faces[f],faceNormal=morphNormals.faceNormals[f],vertexNormals=morphNormals.vertexNormals[f],faceNormal.copy(face.normal),vertexNormals.a.copy(face.vertexNormals[0]),vertexNormals.b.copy(face.vertexNormals[1]),vertexNormals.c.copy(face.vertexNormals[2])}for(f=0,fl=this.faces.length;fl>f;f++)face=this.faces[f],face.normal=face.__originalFaceNormal,face.vertexNormals=face.__originalVertexNormals},computeTangents:function(){console.warn("THREE.Geometry: .computeTangents() has been removed.")},computeLineDistances:function(){for(var d=0,vertices=this.vertices,i=0,il=vertices.length;il>i;i++)i>0&&(d+=vertices[i].distanceTo(vertices[i-1])),this.lineDistances[i]=d},computeBoundingBox:function(){null===this.boundingBox&&(this.boundingBox=new THREE.Box3),this.boundingBox.setFromPoints(this.vertices)},computeBoundingSphere:function(){null===this.boundingSphere&&(this.boundingSphere=new THREE.Sphere),this.boundingSphere.setFromPoints(this.vertices)},merge:function(geometry,matrix,materialIndexOffset){if(geometry instanceof THREE.Geometry==!1)return void console.error("THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.",geometry);var normalMatrix,vertexOffset=this.vertices.length,vertices1=this.vertices,vertices2=geometry.vertices,faces1=this.faces,faces2=geometry.faces,uvs1=this.faceVertexUvs[0],uvs2=geometry.faceVertexUvs[0];void 0===materialIndexOffset&&(materialIndexOffset=0),void 0!==matrix&&(normalMatrix=(new THREE.Matrix3).getNormalMatrix(matrix));for(var i=0,il=vertices2.length;il>i;i++){var vertex=vertices2[i],vertexCopy=vertex.clone();void 0!==matrix&&vertexCopy.applyMatrix4(matrix),vertices1.push(vertexCopy)}for(i=0,il=faces2.length;il>i;i++){var faceCopy,normal,color,face=faces2[i],faceVertexNormals=face.vertexNormals,faceVertexColors=face.vertexColors;faceCopy=new THREE.Face3(face.a+vertexOffset,face.b+vertexOffset,face.c+vertexOffset),faceCopy.normal.copy(face.normal),void 0!==normalMatrix&&faceCopy.normal.applyMatrix3(normalMatrix).normalize();for(var j=0,jl=faceVertexNormals.length;jl>j;j++)normal=faceVertexNormals[j].clone(),void 0!==normalMatrix&&normal.applyMatrix3(normalMatrix).normalize(),faceCopy.vertexNormals.push(normal);faceCopy.color.copy(face.color);for(var j=0,jl=faceVertexColors.length;jl>j;j++)color=faceVertexColors[j],faceCopy.vertexColors.push(color.clone());faceCopy.materialIndex=face.materialIndex+materialIndexOffset,faces1.push(faceCopy)}for(i=0,il=uvs2.length;il>i;i++){var uv=uvs2[i],uvCopy=[];if(void 0!==uv){for(var j=0,jl=uv.length;jl>j;j++)uvCopy.push(uv[j].clone());uvs1.push(uvCopy)}}},mergeMesh:function(mesh){return mesh instanceof THREE.Mesh==!1?void console.error("THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.",mesh):(mesh.matrixAutoUpdate&&mesh.updateMatrix(),void this.merge(mesh.geometry,mesh.matrix))},mergeVertices:function(){var v,key,i,il,face,indices,j,jl,verticesMap={},unique=[],changes=[],precisionPoints=4,precision=Math.pow(10,precisionPoints);for(i=0,il=this.vertices.length;il>i;i++)v=this.vertices[i],key=Math.round(v.x*precision)+"_"+Math.round(v.y*precision)+"_"+Math.round(v.z*precision),void 0===verticesMap[key]?(verticesMap[key]=i,unique.push(this.vertices[i]),changes[i]=unique.length-1):changes[i]=changes[verticesMap[key]];var faceIndicesToRemove=[];for(i=0,il=this.faces.length;il>i;i++){face=this.faces[i],face.a=changes[face.a],face.b=changes[face.b],face.c=changes[face.c],indices=[face.a,face.b,face.c];for(var dupIndex=-1,n=0;3>n;n++)if(indices[n]===indices[(n+1)%3]){dupIndex=n,faceIndicesToRemove.push(i);break}}for(i=faceIndicesToRemove.length-1;i>=0;i--){var idx=faceIndicesToRemove[i];for(this.faces.splice(idx,1),j=0,jl=this.faceVertexUvs.length;jl>j;j++)this.faceVertexUvs[j].splice(idx,1)}var diff=this.vertices.length-unique.length;return this.vertices=unique,diff},sortFacesByMaterialIndex:function(){function materialIndexSort(a,b){return a.materialIndex-b.materialIndex}for(var faces=this.faces,length=faces.length,i=0;length>i;i++)faces[i]._id=i;faces.sort(materialIndexSort);var newUvs1,newUvs2,uvs1=this.faceVertexUvs[0],uvs2=this.faceVertexUvs[1];uvs1&&uvs1.length===length&&(newUvs1=[]),uvs2&&uvs2.length===length&&(newUvs2=[]);for(var i=0;length>i;i++){var id=faces[i]._id;newUvs1&&newUvs1.push(uvs1[id]),newUvs2&&newUvs2.push(uvs2[id])}newUvs1&&(this.faceVertexUvs[0]=newUvs1),newUvs2&&(this.faceVertexUvs[1]=newUvs2)},toJSON:function(){function setBit(value,position,enabled){return enabled?value|1<0,hasFaceVertexNormal=face.vertexNormals.length>0,hasFaceColor=1!==face.color.r||1!==face.color.g||1!==face.color.b,hasFaceVertexColor=face.vertexColors.length>0,faceType=0;if(faceType=setBit(faceType,0,0),faceType=setBit(faceType,1,hasMaterial),faceType=setBit(faceType,2,hasFaceUv),faceType=setBit(faceType,3,hasFaceVertexUv),faceType=setBit(faceType,4,hasFaceNormal),faceType=setBit(faceType,5,hasFaceVertexNormal),faceType=setBit(faceType,6,hasFaceColor),faceType=setBit(faceType,7,hasFaceVertexColor),faces.push(faceType),faces.push(face.a,face.b,face.c),hasFaceVertexUv){var faceVertexUvs=this.faceVertexUvs[0][i];faces.push(getUvIndex(faceVertexUvs[0]),getUvIndex(faceVertexUvs[1]),getUvIndex(faceVertexUvs[2]))}if(hasFaceNormal&&faces.push(getNormalIndex(face.normal)),hasFaceVertexNormal){var vertexNormals=face.vertexNormals;faces.push(getNormalIndex(vertexNormals[0]),getNormalIndex(vertexNormals[1]),getNormalIndex(vertexNormals[2]))}if(hasFaceColor&&faces.push(getColorIndex(face.color)),hasFaceVertexColor){var vertexColors=face.vertexColors;faces.push(getColorIndex(vertexColors[0]),getColorIndex(vertexColors[1]),getColorIndex(vertexColors[2]))}}return data.data={},data.data.vertices=vertices,data.data.normals=normals,colors.length>0&&(data.data.colors=colors),uvs.length>0&&(data.data.uvs=[uvs]),data.data.faces=faces,data},clone:function(){return(new this.constructor).copy(this)},copy:function(source){this.vertices=[],this.faces=[],this.faceVertexUvs=[[]];for(var vertices=source.vertices,i=0,il=vertices.length;il>i;i++)this.vertices.push(vertices[i].clone());for(var faces=source.faces,i=0,il=faces.length;il>i;i++)this.faces.push(faces[i].clone());for(var i=0,il=source.faceVertexUvs.length;il>i;i++){var faceVertexUvs=source.faceVertexUvs[i];void 0===this.faceVertexUvs[i]&&(this.faceVertexUvs[i]=[]);for(var j=0,jl=faceVertexUvs.length;jl>j;j++){for(var uvs=faceVertexUvs[j],uvsCopy=[],k=0,kl=uvs.length;kl>k;k++){var uv=uvs[k];uvsCopy.push(uv.clone())}this.faceVertexUvs[i].push(uvsCopy)}}return this},dispose:function(){this.dispatchEvent({type:"dispose"})}},THREE.EventDispatcher.prototype.apply(THREE.Geometry.prototype),THREE.GeometryIdCount=0,THREE.DirectGeometry=function(){Object.defineProperty(this,"id",{value:THREE.GeometryIdCount++}),this.uuid=THREE.Math.generateUUID(),this.name="",this.type="DirectGeometry",this.indices=[],this.vertices=[],this.normals=[],this.colors=[],this.uvs=[],this.uvs2=[],this.groups=[],this.morphTargets={},this.skinWeights=[],this.skinIndices=[],this.boundingBox=null,this.boundingSphere=null,this.verticesNeedUpdate=!1,this.normalsNeedUpdate=!1,this.colorsNeedUpdate=!1,this.uvsNeedUpdate=!1,this.groupsNeedUpdate=!1},THREE.DirectGeometry.prototype={constructor:THREE.DirectGeometry,computeBoundingBox:THREE.Geometry.prototype.computeBoundingBox,computeBoundingSphere:THREE.Geometry.prototype.computeBoundingSphere,computeFaceNormals:function(){console.warn("THREE.DirectGeometry: computeFaceNormals() is not a method of this type of geometry.")},computeVertexNormals:function(){console.warn("THREE.DirectGeometry: computeVertexNormals() is not a method of this type of geometry.")},computeGroups:function(geometry){for(var group,materialIndex,groups=[],faces=geometry.faces,i=0;i0,hasFaceVertexUv2=faceVertexUvs[1]&&faceVertexUvs[1].length>0,morphTargets=geometry.morphTargets,morphTargetsLength=morphTargets.length;if(morphTargetsLength>0){for(var morphTargetsPosition=[],i=0;morphTargetsLength>i;i++)morphTargetsPosition[i]=[];this.morphTargets.position=morphTargetsPosition}var morphNormals=geometry.morphNormals,morphNormalsLength=morphNormals.length;if(morphNormalsLength>0){for(var morphTargetsNormal=[],i=0;morphNormalsLength>i;i++)morphTargetsNormal[i]=[];this.morphTargets.normal=morphTargetsNormal}for(var skinIndices=geometry.skinIndices,skinWeights=geometry.skinWeights,hasSkinIndices=skinIndices.length===vertices.length,hasSkinWeights=skinWeights.length===vertices.length,i=0;ij;j++){var morphTarget=morphTargets[j].vertices;morphTargetsPosition[j].push(morphTarget[face.a],morphTarget[face.b],morphTarget[face.c])}for(var j=0;morphNormalsLength>j;j++){var morphNormal=morphNormals[j].vertexNormals[i];morphTargetsNormal[j].push(morphNormal.a,morphNormal.b,morphNormal.c)}hasSkinIndices&&this.skinIndices.push(skinIndices[face.a],skinIndices[face.b],skinIndices[face.c]),hasSkinWeights&&this.skinWeights.push(skinWeights[face.a],skinWeights[face.b],skinWeights[face.c])}return this.computeGroups(geometry),this.verticesNeedUpdate=geometry.verticesNeedUpdate,this.normalsNeedUpdate=geometry.normalsNeedUpdate,this.colorsNeedUpdate=geometry.colorsNeedUpdate,this.uvsNeedUpdate=geometry.uvsNeedUpdate,this.groupsNeedUpdate=geometry.groupsNeedUpdate,this},dispose:function(){this.dispatchEvent({type:"dispose"})}},THREE.EventDispatcher.prototype.apply(THREE.DirectGeometry.prototype),THREE.BufferGeometry=function(){Object.defineProperty(this,"id",{value:THREE.GeometryIdCount++}),this.uuid=THREE.Math.generateUUID(),this.name="",this.type="BufferGeometry",this.index=null,this.attributes={},this.morphAttributes={},this.groups=[],this.boundingBox=null,this.boundingSphere=null,this.drawRange={start:0,count:1/0}},THREE.BufferGeometry.prototype={constructor:THREE.BufferGeometry,addIndex:function(index){console.warn("THREE.BufferGeometry: .addIndex() has been renamed to .setIndex()."),this.setIndex(index)},getIndex:function(){return this.index},setIndex:function(index){this.index=index},addAttribute:function(name,attribute){return attribute instanceof THREE.BufferAttribute==!1&&attribute instanceof THREE.InterleavedBufferAttribute==!1?(console.warn("THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )."),void this.addAttribute(name,new THREE.BufferAttribute(arguments[1],arguments[2]))):"index"===name?(console.warn("THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute."),void this.setIndex(attribute)):void(this.attributes[name]=attribute)},getAttribute:function(name){return this.attributes[name]},removeAttribute:function(name){delete this.attributes[name]},get drawcalls(){return console.error("THREE.BufferGeometry: .drawcalls has been renamed to .groups."),this.groups},get offsets(){return console.warn("THREE.BufferGeometry: .offsets has been renamed to .groups."),this.groups},addDrawCall:function(start,count,indexOffset){void 0!==indexOffset&&console.warn("THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset."),console.warn("THREE.BufferGeometry: .addDrawCall() is now .addGroup()."),this.addGroup(start,count)},clearDrawCalls:function(){console.warn("THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups()."),this.clearGroups()},addGroup:function(start,count,materialIndex){this.groups.push({start:start,count:count,materialIndex:void 0!==materialIndex?materialIndex:0})},clearGroups:function(){this.groups=[]},setDrawRange:function(start,count){this.drawRange.start=start,this.drawRange.count=count},applyMatrix:function(matrix){var position=this.attributes.position;void 0!==position&&(matrix.applyToVector3Array(position.array),position.needsUpdate=!0);var normal=this.attributes.normal;if(void 0!==normal){var normalMatrix=(new THREE.Matrix3).getNormalMatrix(matrix);normalMatrix.applyToVector3Array(normal.array),normal.needsUpdate=!0}null!==this.boundingBox&&this.computeBoundingBox(),null!==this.boundingSphere&&this.computeBoundingSphere()},rotateX:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationX(angle),this.applyMatrix(m1),this}}(),rotateY:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationY(angle),this.applyMatrix(m1),this}}(),rotateZ:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationZ(angle),this.applyMatrix(m1),this}}(),translate:function(){var m1;return function(x,y,z){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeTranslation(x,y,z),this.applyMatrix(m1),this}}(),scale:function(){var m1;return function(x,y,z){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeScale(x,y,z),this.applyMatrix(m1),this}}(),lookAt:function(){var obj;return function(vector){void 0===obj&&(obj=new THREE.Object3D),obj.lookAt(vector),obj.updateMatrix(),this.applyMatrix(obj.matrix)}}(),center:function(){this.computeBoundingBox();var offset=this.boundingBox.center().negate();return this.translate(offset.x,offset.y,offset.z),offset},setFromObject:function(object){var geometry=object.geometry;if(object instanceof THREE.Points||object instanceof THREE.Line){var positions=new THREE.Float32Attribute(3*geometry.vertices.length,3),colors=new THREE.Float32Attribute(3*geometry.colors.length,3);if(this.addAttribute("position",positions.copyVector3sArray(geometry.vertices)),this.addAttribute("color",colors.copyColorsArray(geometry.colors)),geometry.lineDistances&&geometry.lineDistances.length===geometry.vertices.length){var lineDistances=new THREE.Float32Attribute(geometry.lineDistances.length,1);this.addAttribute("lineDistance",lineDistances.copyArray(geometry.lineDistances))}null!==geometry.boundingSphere&&(this.boundingSphere=geometry.boundingSphere.clone()),null!==geometry.boundingBox&&(this.boundingBox=geometry.boundingBox.clone())}else object instanceof THREE.Mesh&&geometry instanceof THREE.Geometry&&this.fromGeometry(geometry);return this},updateFromObject:function(object){var geometry=object.geometry;if(object instanceof THREE.Mesh){var direct=geometry.__directGeometry;if(void 0===direct)return this.fromGeometry(geometry);direct.verticesNeedUpdate=geometry.verticesNeedUpdate,direct.normalsNeedUpdate=geometry.normalsNeedUpdate,direct.colorsNeedUpdate=geometry.colorsNeedUpdate,direct.uvsNeedUpdate=geometry.uvsNeedUpdate,direct.groupsNeedUpdate=geometry.groupsNeedUpdate,geometry.verticesNeedUpdate=!1,geometry.normalsNeedUpdate=!1,geometry.colorsNeedUpdate=!1,geometry.uvsNeedUpdate=!1,geometry.groupsNeedUpdate=!1,geometry=direct}if(geometry.verticesNeedUpdate===!0){var attribute=this.attributes.position;void 0!==attribute&&(attribute.copyVector3sArray(geometry.vertices),attribute.needsUpdate=!0),geometry.verticesNeedUpdate=!1}if(geometry.normalsNeedUpdate===!0){var attribute=this.attributes.normal;void 0!==attribute&&(attribute.copyVector3sArray(geometry.normals),attribute.needsUpdate=!0),geometry.normalsNeedUpdate=!1}if(geometry.colorsNeedUpdate===!0){var attribute=this.attributes.color;void 0!==attribute&&(attribute.copyColorsArray(geometry.colors),attribute.needsUpdate=!0),geometry.colorsNeedUpdate=!1}if(geometry.uvsNeedUpdate){var attribute=this.attributes.uv;void 0!==attribute&&(attribute.copyVector2sArray(geometry.uvs),attribute.needsUpdate=!0),geometry.uvsNeedUpdate=!1}if(geometry.lineDistancesNeedUpdate){var attribute=this.attributes.lineDistance;void 0!==attribute&&(attribute.copyArray(geometry.lineDistances),attribute.needsUpdate=!0),geometry.lineDistancesNeedUpdate=!1}return geometry.groupsNeedUpdate&&(geometry.computeGroups(object.geometry),this.groups=geometry.groups,geometry.groupsNeedUpdate=!1),this},fromGeometry:function(geometry){return geometry.__directGeometry=(new THREE.DirectGeometry).fromGeometry(geometry),this.fromDirectGeometry(geometry.__directGeometry)},fromDirectGeometry:function(geometry){var positions=new Float32Array(3*geometry.vertices.length);if(this.addAttribute("position",new THREE.BufferAttribute(positions,3).copyVector3sArray(geometry.vertices)),geometry.normals.length>0){var normals=new Float32Array(3*geometry.normals.length);this.addAttribute("normal",new THREE.BufferAttribute(normals,3).copyVector3sArray(geometry.normals))}if(geometry.colors.length>0){var colors=new Float32Array(3*geometry.colors.length);this.addAttribute("color",new THREE.BufferAttribute(colors,3).copyColorsArray(geometry.colors))}if(geometry.uvs.length>0){var uvs=new Float32Array(2*geometry.uvs.length);this.addAttribute("uv",new THREE.BufferAttribute(uvs,2).copyVector2sArray(geometry.uvs))}if(geometry.uvs2.length>0){var uvs2=new Float32Array(2*geometry.uvs2.length);this.addAttribute("uv2",new THREE.BufferAttribute(uvs2,2).copyVector2sArray(geometry.uvs2))}if(geometry.indices.length>0){var TypeArray=geometry.vertices.length>65535?Uint32Array:Uint16Array,indices=new TypeArray(3*geometry.indices.length);this.setIndex(new THREE.BufferAttribute(indices,1).copyIndicesArray(geometry.indices))}this.groups=geometry.groups;for(var name in geometry.morphTargets){for(var array=[],morphTargets=geometry.morphTargets[name],i=0,l=morphTargets.length;l>i;i++){var morphTarget=morphTargets[i],attribute=new THREE.Float32Attribute(3*morphTarget.length,3);array.push(attribute.copyVector3sArray(morphTarget))}this.morphAttributes[name]=array}if(geometry.skinIndices.length>0){var skinIndices=new THREE.Float32Attribute(4*geometry.skinIndices.length,4);this.addAttribute("skinIndex",skinIndices.copyVector4sArray(geometry.skinIndices))}if(geometry.skinWeights.length>0){var skinWeights=new THREE.Float32Attribute(4*geometry.skinWeights.length,4);this.addAttribute("skinWeight",skinWeights.copyVector4sArray(geometry.skinWeights))}return null!==geometry.boundingSphere&&(this.boundingSphere=geometry.boundingSphere.clone()),null!==geometry.boundingBox&&(this.boundingBox=geometry.boundingBox.clone()),this},computeBoundingBox:function(){var vector=new THREE.Vector3;return function(){null===this.boundingBox&&(this.boundingBox=new THREE.Box3);var positions=this.attributes.position.array;if(positions){var bb=this.boundingBox;bb.makeEmpty();for(var i=0,il=positions.length;il>i;i+=3)vector.fromArray(positions,i),bb.expandByPoint(vector)}void 0!==positions&&0!==positions.length||(this.boundingBox.min.set(0,0,0),this.boundingBox.max.set(0,0,0)),(isNaN(this.boundingBox.min.x)||isNaN(this.boundingBox.min.y)||isNaN(this.boundingBox.min.z))&&console.error('THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.',this)}}(),computeBoundingSphere:function(){var box=new THREE.Box3,vector=new THREE.Vector3;return function(){null===this.boundingSphere&&(this.boundingSphere=new THREE.Sphere);var positions=this.attributes.position.array;if(positions){box.makeEmpty();for(var center=this.boundingSphere.center,i=0,il=positions.length;il>i;i+=3)vector.fromArray(positions,i),box.expandByPoint(vector);box.center(center);for(var maxRadiusSq=0,i=0,il=positions.length;il>i;i+=3)vector.fromArray(positions,i),maxRadiusSq=Math.max(maxRadiusSq,center.distanceToSquared(vector));this.boundingSphere.radius=Math.sqrt(maxRadiusSq),isNaN(this.boundingSphere.radius)&&console.error('THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.',this)}}}(),computeFaceNormals:function(){},computeVertexNormals:function(){var index=this.index,attributes=this.attributes,groups=this.groups;if(attributes.position){var positions=attributes.position.array;if(void 0===attributes.normal)this.addAttribute("normal",new THREE.BufferAttribute(new Float32Array(positions.length),3));else for(var normals=attributes.normal.array,i=0,il=normals.length;il>i;i++)normals[i]=0;var vA,vB,vC,normals=attributes.normal.array,pA=new THREE.Vector3,pB=new THREE.Vector3,pC=new THREE.Vector3,cb=new THREE.Vector3,ab=new THREE.Vector3;if(index){var indices=index.array;0===groups.length&&this.addGroup(0,indices.length);for(var j=0,jl=groups.length;jl>j;++j)for(var group=groups[j],start=group.start,count=group.count,i=start,il=start+count;il>i;i+=3)vA=3*indices[i+0],vB=3*indices[i+1],vC=3*indices[i+2],pA.fromArray(positions,vA),pB.fromArray(positions,vB),pC.fromArray(positions,vC),cb.subVectors(pC,pB),ab.subVectors(pA,pB),cb.cross(ab),normals[vA]+=cb.x,normals[vA+1]+=cb.y,normals[vA+2]+=cb.z,normals[vB]+=cb.x,normals[vB+1]+=cb.y,normals[vB+2]+=cb.z,normals[vC]+=cb.x,normals[vC+1]+=cb.y,normals[vC+2]+=cb.z}else for(var i=0,il=positions.length;il>i;i+=9)pA.fromArray(positions,i),pB.fromArray(positions,i+3),pC.fromArray(positions,i+6),cb.subVectors(pC,pB),ab.subVectors(pA,pB),cb.cross(ab),normals[i]=cb.x,normals[i+1]=cb.y,normals[i+2]=cb.z,normals[i+3]=cb.x,normals[i+4]=cb.y,normals[i+5]=cb.z,normals[i+6]=cb.x,normals[i+7]=cb.y,normals[i+8]=cb.z;this.normalizeNormals(),attributes.normal.needsUpdate=!0}},computeTangents:function(){console.warn("THREE.BufferGeometry: .computeTangents() has been removed.")},computeOffsets:function(size){console.warn("THREE.BufferGeometry: .computeOffsets() has been removed.")},merge:function(geometry,offset){if(geometry instanceof THREE.BufferGeometry==!1)return void console.error("THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.",geometry);void 0===offset&&(offset=0);var attributes=this.attributes;for(var key in attributes)if(void 0!==geometry.attributes[key])for(var attribute1=attributes[key],attributeArray1=attribute1.array,attribute2=geometry.attributes[key],attributeArray2=attribute2.array,attributeSize=attribute2.itemSize,i=0,j=attributeSize*offset;ii;i+=3)x=normals[i],y=normals[i+1],z=normals[i+2],n=1/Math.sqrt(x*x+y*y+z*z),normals[i]*=n,normals[i+1]*=n,normals[i+2]*=n},toJSON:function(){var data={metadata:{version:4.4,type:"BufferGeometry",generator:"BufferGeometry.toJSON"}};if(data.uuid=this.uuid,data.type=this.type,""!==this.name&&(data.name=this.name),void 0!==this.parameters){var parameters=this.parameters;for(var key in parameters)void 0!==parameters[key]&&(data[key]=parameters[key]);return data}data.data={attributes:{}};var index=this.index;if(null!==index){var array=Array.prototype.slice.call(index.array);data.data.index={type:index.array.constructor.name,array:array}}var attributes=this.attributes;for(var key in attributes){var attribute=attributes[key],array=Array.prototype.slice.call(attribute.array);data.data.attributes[key]={itemSize:attribute.itemSize,type:attribute.array.constructor.name,array:array}}var groups=this.groups;groups.length>0&&(data.data.groups=JSON.parse(JSON.stringify(groups)));var boundingSphere=this.boundingSphere;return null!==boundingSphere&&(data.data.boundingSphere={center:boundingSphere.center.toArray(),radius:boundingSphere.radius}),data},clone:function(){return(new this.constructor).copy(this); +},copy:function(source){var index=source.index;null!==index&&this.setIndex(index.clone());var attributes=source.attributes;for(var name in attributes){var attribute=attributes[name];this.addAttribute(name,attribute.clone())}for(var groups=source.groups,i=0,l=groups.length;l>i;i++){var group=groups[i];this.addGroup(group.start,group.count)}return this},dispose:function(){this.dispatchEvent({type:"dispose"})}},THREE.EventDispatcher.prototype.apply(THREE.BufferGeometry.prototype),THREE.BufferGeometry.MaxIndex=65535,THREE.InstancedBufferGeometry=function(){THREE.BufferGeometry.call(this),this.type="InstancedBufferGeometry",this.maxInstancedCount=void 0},THREE.InstancedBufferGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.InstancedBufferGeometry.prototype.constructor=THREE.InstancedBufferGeometry,THREE.InstancedBufferGeometry.prototype.addGroup=function(start,count,instances){this.groups.push({start:start,count:count,instances:instances})},THREE.InstancedBufferGeometry.prototype.copy=function(source){var index=source.index;null!==index&&this.setIndex(index.clone());var attributes=source.attributes;for(var name in attributes){var attribute=attributes[name];this.addAttribute(name,attribute.clone())}for(var groups=source.groups,i=0,l=groups.length;l>i;i++){var group=groups[i];this.addGroup(group.start,group.count,group.instances)}return this},THREE.EventDispatcher.prototype.apply(THREE.InstancedBufferGeometry.prototype),THREE.AnimationAction=function(clip,startTime,timeScale,weight,loop){if(void 0===clip)throw new Error("clip is null");this.clip=clip,this.localRoot=null,this.startTime=startTime||0,this.timeScale=timeScale||1,this.weight=weight||1,this.loop=loop||THREE.LoopRepeat,this.loopCount=0,this.enabled=!0,this.actionTime=-this.startTime,this.clipTime=0,this.propertyBindings=[]},THREE.AnimationAction.prototype={constructor:THREE.AnimationAction,setLocalRoot:function(localRoot){return this.localRoot=localRoot,this},updateTime:function(clipDeltaTime){var previousClipTime=this.clipTime,previousLoopCount=this.loopCount,duration=(this.actionTime,this.clip.duration);if(this.actionTime=this.actionTime+clipDeltaTime,this.loop===THREE.LoopOnce)return this.loopCount=0,this.clipTime=Math.min(Math.max(this.actionTime,0),duration),this.clipTime!==previousClipTime&&(this.clipTime===duration?this.mixer.dispatchEvent({type:"finished",action:this,direction:1}):0===this.clipTime&&this.mixer.dispatchEvent({type:"finished",action:this,direction:-1})),this.clipTime;this.loopCount=Math.floor(this.actionTime/duration);var newClipTime=this.actionTime-this.loopCount*duration;return newClipTime%=duration,this.loop==THREE.LoopPingPong&&1===Math.abs(this.loopCount%2)&&(newClipTime=duration-newClipTime),this.clipTime=newClipTime,this.loopCount!==previousLoopCount&&this.mixer.dispatchEvent({type:"loop",action:this,loopDelta:this.loopCount-this.loopCount}),this.clipTime},syncWith:function(action){return this.actionTime=action.actionTime,this.timeScale=action.timeScale,this},warpToDuration:function(duration){return this.timeScale=this.clip.duration/duration,this},init:function(time){return this.clipTime=time-this.startTime,this},update:function(clipDeltaTime){this.updateTime(clipDeltaTime);var clipResults=this.clip.getAt(this.clipTime);return clipResults},getTimeScaleAt:function(time){return this.timeScale.getAt?this.timeScale.getAt(time):this.timeScale},getWeightAt:function(time){return this.weight.getAt?this.weight.getAt(time):this.weight}},THREE.AnimationClip=function(name,duration,tracks){if(this.name=name,this.tracks=tracks,this.duration=void 0!==duration?duration:-1,this.duration<0)for(var i=0;ii;i++){var keys=[];keys.push({time:(i+numMorphTargets-1)%numMorphTargets,value:0}),keys.push({time:i,value:1}),keys.push({time:(i+1)%numMorphTargets,value:0}),keys.sort(THREE.KeyframeTrack.keyComparer),0===keys[0].time&&keys.push({time:numMorphTargets,value:keys[0].value}),tracks.push(new THREE.NumberKeyframeTrack(".morphTargetInfluences["+morphTargetSequence[i].name+"]",keys).scale(1/fps))}return new THREE.AnimationClip(name,-1,tracks)},THREE.AnimationClip.findByName=function(clipArray,name){for(var i=0;ii;i++){var morphTarget=morphTargets[i],parts=morphTarget.name.match(pattern);if(parts&&parts.length>1){var name=parts[1],animationMorphTargets=animationToMorphTargets[name];animationMorphTargets||(animationToMorphTargets[name]=animationMorphTargets=[]),animationMorphTargets.push(morphTarget)}}var clips=[];for(var name in animationToMorphTargets)clips.push(THREE.AnimationClip.CreateFromMorphTargetSequence(name,animationToMorphTargets[name],fps));return clips},THREE.AnimationClip.parse=function(json){for(var tracks=[],i=0;i0?new trackType(trackName,keys):null},tracks=[],clipName=animation.name||"default",duration=animation.length||-1,fps=animation.fps||30,hierarchyTracks=animation.hierarchy||[],h=0;halpha?a:b},lerp_boolean_immediate:function(a,b,alpha){return a},lerp_string:function(a,b,alpha){return.5>alpha?a:b},lerp_string_immediate:function(a,b,alpha){return a},getLerpFunc:function(exemplarValue,interTrack){if(void 0===exemplarValue||null===exemplarValue)throw new Error("examplarValue is null");var typeName=typeof exemplarValue;switch(typeName){case"object":if(exemplarValue.lerp)return THREE.AnimationUtils.lerp_object;if(exemplarValue.slerp)return THREE.AnimationUtils.slerp_object;break;case"number":return THREE.AnimationUtils.lerp_number;case"boolean":return interTrack?THREE.AnimationUtils.lerp_boolean:THREE.AnimationUtils.lerp_boolean_immediate;case"string":return interTrack?THREE.AnimationUtils.lerp_string:THREE.AnimationUtils.lerp_string_immediate}}},THREE.KeyframeTrack=function(name,keys){if(void 0===name)throw new Error("track name is undefined");if(void 0===keys||0===keys.length)throw new Error("no keys in track named "+name);this.name=name,this.keys=keys,this.lastIndex=0,this.validate(),this.optimize()},THREE.KeyframeTrack.prototype={constructor:THREE.KeyframeTrack,getAt:function(time){for(;this.lastIndex=this.keys[this.lastIndex].time;)this.lastIndex++;for(;this.lastIndex>0&&time=this.keys.length)return this.setResult(this.keys[this.keys.length-1].value),this.result;if(0===this.lastIndex)return this.setResult(this.keys[0].value),this.result;var prevKey=this.keys[this.lastIndex-1];if(this.setResult(prevKey.value),prevKey.constantToNext)return this.result;var currentKey=this.keys[this.lastIndex],alpha=(time-prevKey.time)/(currentKey.time-prevKey.time);return this.result=this.lerpValues(this.result,currentKey.value,alpha),this.result},shift:function(timeOffset){if(0!==timeOffset)for(var i=0;i0&&this.keys[i]>=endTime;i++)lastKeysToRemove++;return firstKeysToRemove+lastKeysToRemove>0&&(this.keys=this.keys.splice(firstKeysToRemove,this.keys.length-lastKeysToRemove-firstKeysToRemove)),this},validate:function(){var prevKey=null;if(0===this.keys.length)return void console.error(" track is empty, no keys",this);for(var i=0;icurrKey.time)return void console.error(" key.time is less than previous key time, out of order keys",this,i,currKey,prevKey);prevKey=currKey}return this},optimize:function(){var newKeys=[],prevKey=this.keys[0];newKeys.push(prevKey);for(var i=(THREE.AnimationUtils.getEqualsFunc(prevKey.value),1);i0&&(null===this.cumulativeValue&&(this.cumulativeValue=THREE.AnimationUtils.clone(value)),this.cumulativeWeight=weight);else{var lerpAlpha=weight/(this.cumulativeWeight+weight);this.cumulativeValue=this.lerpValue(this.cumulativeValue,value,lerpAlpha),this.cumulativeWeight+=weight}},unbind:function(){this.isBound&&(this.setValue(this.originalValue),this.setValue=null,this.getValue=null,this.lerpValue=null,this.equalsValue=null,this.triggerDirty=null,this.isBound=!1)},bind:function(){if(!this.isBound){var targetObject=this.node;if(!targetObject)return void console.error(" trying to update node for track: "+this.trackName+" but it wasn't found.");if(this.objectName){if("materials"===this.objectName){if(!targetObject.material)return void console.error(" can not bind to material as node does not have a material",this);if(!targetObject.material.materials)return void console.error(" can not bind to material.materials as node.material does not have a materials array",this);targetObject=targetObject.material.materials}else if("bones"===this.objectName){if(!targetObject.skeleton)return void console.error(" can not bind to bones as node does not have a skeleton",this);targetObject=targetObject.skeleton.bones;for(var i=0;i0){if(this.cumulativeWeight<1){var remainingWeight=1-this.cumulativeWeight,lerpAlpha=remainingWeight/(this.cumulativeWeight+remainingWeight);this.cumulativeValue=this.lerpValue(this.cumulativeValue,this.originalValue,lerpAlpha)}var valueChanged=this.setValue(this.cumulativeValue);valueChanged&&this.triggerDirty&&this.triggerDirty(),this.cumulativeValue=null,this.cumulativeWeight=0}}},THREE.PropertyBinding.parseTrackName=function(trackName){var re=/^(([\w]+\/)*)([\w-\d]+)?(\.([\w]+)(\[([\w\d\[\]\_. ]+)\])?)?(\.([\w.]+)(\[([\w\d\[\]\_. ]+)\])?)$/,matches=re.exec(trackName);if(!matches)throw new Error("cannot parse trackName at all: "+trackName);matches.index===re.lastIndex&&re.lastIndex++;var results={directoryName:matches[1],nodeName:matches[3],objectName:matches[5],objectIndex:matches[7],propertyName:matches[9],propertyIndex:matches[11]};if(null===results.propertyName||0===results.propertyName.length)throw new Error("can not parse propertyName from trackName: "+trackName);return results},THREE.PropertyBinding.findNode=function(root,nodeName){function searchSkeleton(skeleton){for(var i=0;ialpha?value0:value1},THREE.StringKeyframeTrack.prototype.compareValues=function(value0,value1){return value0===value1},THREE.StringKeyframeTrack.prototype.clone=function(){for(var clonedKeys=[],i=0;ialpha?value0:value1},THREE.BooleanKeyframeTrack.prototype.compareValues=function(value0,value1){return value0===value1},THREE.BooleanKeyframeTrack.prototype.clone=function(){for(var clonedKeys=[],i=0;ii;i+=2){var regex=handlers[i],loader=handlers[i+1];if(regex.test(file))return loader}return null}},THREE.XHRLoader=function(manager){this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager},THREE.XHRLoader.prototype={constructor:THREE.XHRLoader,load:function(url,onLoad,onProgress,onError){var scope=this,cached=THREE.Cache.get(url);if(void 0!==cached)return onLoad&&setTimeout(function(){onLoad(cached)},0),cached;var request=new XMLHttpRequest;return request.open("GET",url,!0),request.addEventListener("load",function(event){var response=event.target.response;THREE.Cache.add(url,response),onLoad&&onLoad(response),scope.manager.itemEnd(url)},!1),void 0!==onProgress&&request.addEventListener("progress",function(event){onProgress(event)},!1),request.addEventListener("error",function(event){onError&&onError(event),scope.manager.itemError(url)},!1),void 0!==this.crossOrigin&&(request.crossOrigin=this.crossOrigin),void 0!==this.responseType&&(request.responseType=this.responseType),void 0!==this.withCredentials&&(request.withCredentials=this.withCredentials),request.send(null),scope.manager.itemStart(url),request},setResponseType:function(value){this.responseType=value},setCrossOrigin:function(value){this.crossOrigin=value},setWithCredentials:function(value){this.withCredentials=value}},THREE.ImageLoader=function(manager){this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager},THREE.ImageLoader.prototype={constructor:THREE.ImageLoader,load:function(url,onLoad,onProgress,onError){var scope=this,cached=THREE.Cache.get(url);if(void 0!==cached)return scope.manager.itemStart(url),onLoad?setTimeout(function(){onLoad(cached),scope.manager.itemEnd(url)},0):scope.manager.itemEnd(url),cached;var image=document.createElement("img");return image.addEventListener("load",function(event){THREE.Cache.add(url,this),onLoad&&onLoad(this),scope.manager.itemEnd(url)},!1),void 0!==onProgress&&image.addEventListener("progress",function(event){onProgress(event)},!1),image.addEventListener("error",function(event){onError&&onError(event),scope.manager.itemError(url)},!1),void 0!==this.crossOrigin&&(image.crossOrigin=this.crossOrigin),scope.manager.itemStart(url),image.src=url,image},setCrossOrigin:function(value){this.crossOrigin=value}},THREE.JSONLoader=function(manager){"boolean"==typeof manager&&(console.warn("THREE.JSONLoader: showStatus parameter has been removed from constructor."),manager=void 0),this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager,this.withCredentials=!1},THREE.JSONLoader.prototype={constructor:THREE.JSONLoader,get statusDomElement(){return void 0===this._statusDomElement&&(this._statusDomElement=document.createElement("div")),console.warn("THREE.JSONLoader: .statusDomElement has been removed."),this._statusDomElement},load:function(url,onLoad,onProgress,onError){var scope=this,texturePath=this.texturePath&&"string"==typeof this.texturePath?this.texturePath:THREE.Loader.prototype.extractUrlBase(url),loader=new THREE.XHRLoader(this.manager);loader.setCrossOrigin(this.crossOrigin),loader.setWithCredentials(this.withCredentials),loader.load(url,function(text){var json=JSON.parse(text),metadata=json.metadata;if(void 0!==metadata){if("object"===metadata.type)return void console.error("THREE.JSONLoader: "+url+" should be loaded with THREE.ObjectLoader instead.");if("scene"===metadata.type)return void console.error("THREE.JSONLoader: "+url+" should be loaded with THREE.SceneLoader instead.")}var object=scope.parse(json,texturePath);onLoad(object.geometry,object.materials)})},setCrossOrigin:function(value){this.crossOrigin=value},setTexturePath:function(value){this.texturePath=value},parse:function(json,texturePath){function parseModel(scale){function isBitSet(value,position){return value&1<i;i++)geometry.faceVertexUvs[i]=[]}for(offset=0,zLength=vertices.length;zLength>offset;)vertex=new THREE.Vector3,vertex.x=vertices[offset++]*scale,vertex.y=vertices[offset++]*scale,vertex.z=vertices[offset++]*scale,geometry.vertices.push(vertex);for(offset=0,zLength=faces.length;zLength>offset;)if(type=faces[offset++],isQuad=isBitSet(type,0),hasMaterial=isBitSet(type,1),hasFaceVertexUv=isBitSet(type,3),hasFaceNormal=isBitSet(type,4),hasFaceVertexNormal=isBitSet(type,5),hasFaceColor=isBitSet(type,6),hasFaceVertexColor=isBitSet(type,7),isQuad){if(faceA=new THREE.Face3,faceA.a=faces[offset],faceA.b=faces[offset+1],faceA.c=faces[offset+3],faceB=new THREE.Face3,faceB.a=faces[offset+1],faceB.b=faces[offset+2],faceB.c=faces[offset+3],offset+=4,hasMaterial&&(materialIndex=faces[offset++],faceA.materialIndex=materialIndex,faceB.materialIndex=materialIndex),fi=geometry.faces.length,hasFaceVertexUv)for(i=0;nUvLayers>i;i++)for(uvLayer=json.uvs[i],geometry.faceVertexUvs[i][fi]=[],geometry.faceVertexUvs[i][fi+1]=[],j=0;4>j;j++)uvIndex=faces[offset++],u=uvLayer[2*uvIndex],v=uvLayer[2*uvIndex+1],uv=new THREE.Vector2(u,v),2!==j&&geometry.faceVertexUvs[i][fi].push(uv),0!==j&&geometry.faceVertexUvs[i][fi+1].push(uv);if(hasFaceNormal&&(normalIndex=3*faces[offset++],faceA.normal.set(normals[normalIndex++],normals[normalIndex++],normals[normalIndex]),faceB.normal.copy(faceA.normal)),hasFaceVertexNormal)for(i=0;4>i;i++)normalIndex=3*faces[offset++],normal=new THREE.Vector3(normals[normalIndex++],normals[normalIndex++],normals[normalIndex]),2!==i&&faceA.vertexNormals.push(normal),0!==i&&faceB.vertexNormals.push(normal);if(hasFaceColor&&(colorIndex=faces[offset++],hex=colors[colorIndex],faceA.color.setHex(hex),faceB.color.setHex(hex)),hasFaceVertexColor)for(i=0;4>i;i++)colorIndex=faces[offset++],hex=colors[colorIndex],2!==i&&faceA.vertexColors.push(new THREE.Color(hex)),0!==i&&faceB.vertexColors.push(new THREE.Color(hex));geometry.faces.push(faceA),geometry.faces.push(faceB)}else{if(face=new THREE.Face3,face.a=faces[offset++],face.b=faces[offset++],face.c=faces[offset++],hasMaterial&&(materialIndex=faces[offset++],face.materialIndex=materialIndex),fi=geometry.faces.length,hasFaceVertexUv)for(i=0;nUvLayers>i;i++)for(uvLayer=json.uvs[i],geometry.faceVertexUvs[i][fi]=[],j=0;3>j;j++)uvIndex=faces[offset++],u=uvLayer[2*uvIndex],v=uvLayer[2*uvIndex+1],uv=new THREE.Vector2(u,v),geometry.faceVertexUvs[i][fi].push(uv);if(hasFaceNormal&&(normalIndex=3*faces[offset++],face.normal.set(normals[normalIndex++],normals[normalIndex++],normals[normalIndex])),hasFaceVertexNormal)for(i=0;3>i;i++)normalIndex=3*faces[offset++],normal=new THREE.Vector3(normals[normalIndex++],normals[normalIndex++],normals[normalIndex]),face.vertexNormals.push(normal);if(hasFaceColor&&(colorIndex=faces[offset++],face.color.setHex(colors[colorIndex])),hasFaceVertexColor)for(i=0;3>i;i++)colorIndex=faces[offset++],face.vertexColors.push(new THREE.Color(colors[colorIndex]));geometry.faces.push(face)}}function parseSkin(){var influencesPerVertex=void 0!==json.influencesPerVertex?json.influencesPerVertex:2;if(json.skinWeights)for(var i=0,l=json.skinWeights.length;l>i;i+=influencesPerVertex){var x=json.skinWeights[i],y=influencesPerVertex>1?json.skinWeights[i+1]:0,z=influencesPerVertex>2?json.skinWeights[i+2]:0,w=influencesPerVertex>3?json.skinWeights[i+3]:0;geometry.skinWeights.push(new THREE.Vector4(x,y,z,w))}if(json.skinIndices)for(var i=0,l=json.skinIndices.length;l>i;i+=influencesPerVertex){var a=json.skinIndices[i],b=influencesPerVertex>1?json.skinIndices[i+1]:0,c=influencesPerVertex>2?json.skinIndices[i+2]:0,d=influencesPerVertex>3?json.skinIndices[i+3]:0;geometry.skinIndices.push(new THREE.Vector4(a,b,c,d))}geometry.bones=json.bones,geometry.bones&&geometry.bones.length>0&&(geometry.skinWeights.length!==geometry.skinIndices.length||geometry.skinIndices.length!==geometry.vertices.length)&&console.warn("When skinning, number of vertices ("+geometry.vertices.length+"), skinIndices ("+geometry.skinIndices.length+"), and skinWeights ("+geometry.skinWeights.length+") should match.")}function parseMorphing(scale){if(void 0!==json.morphTargets)for(var i=0,l=json.morphTargets.length;l>i;i++){geometry.morphTargets[i]={},geometry.morphTargets[i].name=json.morphTargets[i].name,geometry.morphTargets[i].vertices=[];for(var dstVertices=geometry.morphTargets[i].vertices,srcVertices=json.morphTargets[i].vertices,v=0,vl=srcVertices.length;vl>v;v+=3){var vertex=new THREE.Vector3;vertex.x=srcVertices[v]*scale,vertex.y=srcVertices[v+1]*scale,vertex.z=srcVertices[v+2]*scale,dstVertices.push(vertex)}}if(void 0!==json.morphColors&&json.morphColors.length>0){console.warn('THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.');for(var faces=geometry.faces,morphColors=json.morphColors[0].colors,i=0,l=faces.length;l>i;i++)faces[i].color.fromArray(morphColors,3*i)}}function parseAnimations(){var outputAnimations=[],animations=[];void 0!==json.animation&&animations.push(json.animation),void 0!==json.animations&&(json.animations.length?animations=animations.concat(json.animations):animations.push(json.animations));for(var i=0;i0&&(geometry.animations=outputAnimations)}var geometry=new THREE.Geometry,scale=void 0!==json.scale?1/json.scale:1;if(parseModel(scale),parseSkin(),parseMorphing(scale),parseAnimations(),geometry.computeFaceNormals(),geometry.computeBoundingSphere(),void 0===json.materials||0===json.materials.length)return{geometry:geometry};var materials=THREE.Loader.prototype.initMaterials(json.materials,texturePath,this.crossOrigin);return{geometry:geometry,materials:materials}}},THREE.LoadingManager=function(onLoad,onProgress,onError){var scope=this,isLoading=!1,itemsLoaded=0,itemsTotal=0;this.onStart=void 0,this.onLoad=onLoad,this.onProgress=onProgress,this.onError=onError,this.itemStart=function(url){itemsTotal++,isLoading===!1&&void 0!==scope.onStart&&scope.onStart(url,itemsLoaded,itemsTotal),isLoading=!0},this.itemEnd=function(url){itemsLoaded++,void 0!==scope.onProgress&&scope.onProgress(url,itemsLoaded,itemsTotal),itemsLoaded===itemsTotal&&(isLoading=!1,void 0!==scope.onLoad&&scope.onLoad())},this.itemError=function(url){void 0!==scope.onError&&scope.onError(url)}},THREE.DefaultLoadingManager=new THREE.LoadingManager,THREE.BufferGeometryLoader=function(manager){this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager},THREE.BufferGeometryLoader.prototype={constructor:THREE.BufferGeometryLoader,load:function(url,onLoad,onProgress,onError){var scope=this,loader=new THREE.XHRLoader(scope.manager);loader.setCrossOrigin(this.crossOrigin),loader.load(url,function(text){onLoad(scope.parse(JSON.parse(text)))},onProgress,onError)},setCrossOrigin:function(value){this.crossOrigin=value},parse:function(json){var geometry=new THREE.BufferGeometry,index=json.data.index;if(void 0!==index){var typedArray=new self[index.type](index.array);geometry.setIndex(new THREE.BufferAttribute(typedArray,1))}var attributes=json.data.attributes;for(var key in attributes){var attribute=attributes[key],typedArray=new self[attribute.type](attribute.array);geometry.addAttribute(key,new THREE.BufferAttribute(typedArray,attribute.itemSize))}var groups=json.data.groups||json.data.drawcalls||json.data.offsets;if(void 0!==groups)for(var i=0,n=groups.length;i!==n;++i){var group=groups[i];geometry.addGroup(group.start,group.count)}var boundingSphere=json.data.boundingSphere;if(void 0!==boundingSphere){var center=new THREE.Vector3;void 0!==boundingSphere.center&¢er.fromArray(boundingSphere.center),geometry.boundingSphere=new THREE.Sphere(center,boundingSphere.radius)}return geometry}},THREE.MaterialLoader=function(manager){this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager,this.textures={}},THREE.MaterialLoader.prototype={constructor:THREE.MaterialLoader,load:function(url,onLoad,onProgress,onError){var scope=this,loader=new THREE.XHRLoader(scope.manager);loader.setCrossOrigin(this.crossOrigin),loader.load(url,function(text){onLoad(scope.parse(JSON.parse(text)))},onProgress,onError)},setCrossOrigin:function(value){this.crossOrigin=value},setTextures:function(value){this.textures=value},getTexture:function(name){var textures=this.textures;return void 0===textures[name]&&console.warn("THREE.MaterialLoader: Undefined texture",name),textures[name]},parse:function(json){var material=new THREE[json.type];if(material.uuid=json.uuid,void 0!==json.name&&(material.name=json.name),void 0!==json.color&&material.color.setHex(json.color),void 0!==json.emissive&&material.emissive.setHex(json.emissive),void 0!==json.specular&&material.specular.setHex(json.specular),void 0!==json.shininess&&(material.shininess=json.shininess),void 0!==json.uniforms&&(material.uniforms=json.uniforms),void 0!==json.vertexShader&&(material.vertexShader=json.vertexShader),void 0!==json.fragmentShader&&(material.fragmentShader=json.fragmentShader),void 0!==json.vertexColors&&(material.vertexColors=json.vertexColors),void 0!==json.shading&&(material.shading=json.shading),void 0!==json.blending&&(material.blending=json.blending),void 0!==json.side&&(material.side=json.side),void 0!==json.opacity&&(material.opacity=json.opacity),void 0!==json.transparent&&(material.transparent=json.transparent),void 0!==json.alphaTest&&(material.alphaTest=json.alphaTest),void 0!==json.depthTest&&(material.depthTest=json.depthTest),void 0!==json.depthWrite&&(material.depthWrite=json.depthWrite),void 0!==json.wireframe&&(material.wireframe=json.wireframe),void 0!==json.wireframeLinewidth&&(material.wireframeLinewidth=json.wireframeLinewidth),void 0!==json.size&&(material.size=json.size),void 0!==json.sizeAttenuation&&(material.sizeAttenuation=json.sizeAttenuation),void 0!==json.map&&(material.map=this.getTexture(json.map)),void 0!==json.alphaMap&&(material.alphaMap=this.getTexture(json.alphaMap),material.transparent=!0),void 0!==json.bumpMap&&(material.bumpMap=this.getTexture(json.bumpMap)),void 0!==json.bumpScale&&(material.bumpScale=json.bumpScale),void 0!==json.normalMap&&(material.normalMap=this.getTexture(json.normalMap)),json.normalScale&&(material.normalScale=new THREE.Vector2(json.normalScale,json.normalScale)),void 0!==json.displacementMap&&(material.displacementMap=this.getTexture(json.displacementMap)),void 0!==json.displacementScale&&(material.displacementScale=json.displacementScale),void 0!==json.displacementBias&&(material.displacementBias=json.displacementBias),void 0!==json.specularMap&&(material.specularMap=this.getTexture(json.specularMap)),void 0!==json.envMap&&(material.envMap=this.getTexture(json.envMap),material.combine=THREE.MultiplyOperation),json.reflectivity&&(material.reflectivity=json.reflectivity),void 0!==json.lightMap&&(material.lightMap=this.getTexture(json.lightMap)),void 0!==json.lightMapIntensity&&(material.lightMapIntensity=json.lightMapIntensity),void 0!==json.aoMap&&(material.aoMap=this.getTexture(json.aoMap)),void 0!==json.aoMapIntensity&&(material.aoMapIntensity=json.aoMapIntensity),void 0!==json.materials)for(var i=0,l=json.materials.length;l>i;i++)material.materials.push(this.parse(json.materials[i]));return material}},THREE.ObjectLoader=function(manager){this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager,this.texturePath=""},THREE.ObjectLoader.prototype={constructor:THREE.ObjectLoader,load:function(url,onLoad,onProgress,onError){""===this.texturePath&&(this.texturePath=url.substring(0,url.lastIndexOf("/")+1));var scope=this,loader=new THREE.XHRLoader(scope.manager);loader.setCrossOrigin(this.crossOrigin),loader.load(url,function(text){scope.parse(JSON.parse(text),onLoad)},onProgress,onError)},setTexturePath:function(value){this.texturePath=value},setCrossOrigin:function(value){this.crossOrigin=value},parse:function(json,onLoad){var geometries=this.parseGeometries(json.geometries),images=this.parseImages(json.images,function(){void 0!==onLoad&&onLoad(object)}),textures=this.parseTextures(json.textures,images),materials=this.parseMaterials(json.materials,textures),object=this.parseObject(json.object,geometries,materials);return json.animations&&(object.animations=this.parseAnimations(json.animations)),void 0!==json.images&&0!==json.images.length||void 0!==onLoad&&onLoad(object),object},parseGeometries:function(json){var geometries={};if(void 0!==json)for(var geometryLoader=new THREE.JSONLoader,bufferGeometryLoader=new THREE.BufferGeometryLoader,i=0,l=json.length;l>i;i++){var geometry,data=json[i];switch(data.type){case"PlaneGeometry":case"PlaneBufferGeometry":geometry=new THREE[data.type](data.width,data.height,data.widthSegments,data.heightSegments);break;case"BoxGeometry":case"CubeGeometry":geometry=new THREE.BoxGeometry(data.width,data.height,data.depth,data.widthSegments,data.heightSegments,data.depthSegments);break;case"CircleBufferGeometry":geometry=new THREE.CircleBufferGeometry(data.radius,data.segments,data.thetaStart,data.thetaLength);break;case"CircleGeometry":geometry=new THREE.CircleGeometry(data.radius,data.segments,data.thetaStart,data.thetaLength);break;case"CylinderGeometry":geometry=new THREE.CylinderGeometry(data.radiusTop,data.radiusBottom,data.height,data.radialSegments,data.heightSegments,data.openEnded,data.thetaStart,data.thetaLength);break;case"SphereGeometry":geometry=new THREE.SphereGeometry(data.radius,data.widthSegments,data.heightSegments,data.phiStart,data.phiLength,data.thetaStart,data.thetaLength);break;case"SphereBufferGeometry":geometry=new THREE.SphereBufferGeometry(data.radius,data.widthSegments,data.heightSegments,data.phiStart,data.phiLength,data.thetaStart,data.thetaLength);break;case"DodecahedronGeometry":geometry=new THREE.DodecahedronGeometry(data.radius,data.detail);break;case"IcosahedronGeometry":geometry=new THREE.IcosahedronGeometry(data.radius,data.detail);break;case"OctahedronGeometry":geometry=new THREE.OctahedronGeometry(data.radius,data.detail);break;case"TetrahedronGeometry":geometry=new THREE.TetrahedronGeometry(data.radius,data.detail);break;case"RingGeometry":geometry=new THREE.RingGeometry(data.innerRadius,data.outerRadius,data.thetaSegments,data.phiSegments,data.thetaStart,data.thetaLength);break;case"TorusGeometry":geometry=new THREE.TorusGeometry(data.radius,data.tube,data.radialSegments,data.tubularSegments,data.arc);break;case"TorusKnotGeometry":geometry=new THREE.TorusKnotGeometry(data.radius,data.tube,data.radialSegments,data.tubularSegments,data.p,data.q,data.heightScale);break;case"BufferGeometry":geometry=bufferGeometryLoader.parse(data);break;case"Geometry": +geometry=geometryLoader.parse(data.data,this.texturePath).geometry;break;default:console.warn('THREE.ObjectLoader: Unsupported geometry type "'+data.type+'"');continue}geometry.uuid=data.uuid,void 0!==data.name&&(geometry.name=data.name),geometries[data.uuid]=geometry}return geometries},parseMaterials:function(json,textures){var materials={};if(void 0!==json){var loader=new THREE.MaterialLoader;loader.setTextures(textures);for(var i=0,l=json.length;l>i;i++){var material=loader.parse(json[i]);materials[material.uuid]=material}}return materials},parseAnimations:function(json){for(var animations=[],i=0;i0){var manager=new THREE.LoadingManager(onLoad),loader=new THREE.ImageLoader(manager);loader.setCrossOrigin(this.crossOrigin);for(var i=0,l=json.length;l>i;i++){var image=json[i],path=/^(\/\/)|([a-z]+:(\/\/)?)/i.test(image.url)?image.url:scope.texturePath+image.url;images[image.uuid]=loadImage(path)}}return images},parseTextures:function(json,images){function parseConstant(value){return"number"==typeof value?value:(console.warn("THREE.ObjectLoader.parseTexture: Constant should be in numeric form.",value),THREE[value])}var textures={};if(void 0!==json)for(var i=0,l=json.length;l>i;i++){var data=json[i];void 0===data.image&&console.warn('THREE.ObjectLoader: No "image" specified for',data.uuid),void 0===images[data.image]&&console.warn("THREE.ObjectLoader: Undefined image",data.image);var texture=new THREE.Texture(images[data.image]);texture.needsUpdate=!0,texture.uuid=data.uuid,void 0!==data.name&&(texture.name=data.name),void 0!==data.mapping&&(texture.mapping=parseConstant(data.mapping)),void 0!==data.offset&&(texture.offset=new THREE.Vector2(data.offset[0],data.offset[1])),void 0!==data.repeat&&(texture.repeat=new THREE.Vector2(data.repeat[0],data.repeat[1])),void 0!==data.minFilter&&(texture.minFilter=parseConstant(data.minFilter)),void 0!==data.magFilter&&(texture.magFilter=parseConstant(data.magFilter)),void 0!==data.anisotropy&&(texture.anisotropy=data.anisotropy),Array.isArray(data.wrap)&&(texture.wrapS=parseConstant(data.wrap[0]),texture.wrapT=parseConstant(data.wrap[1])),textures[data.uuid]=texture}return textures},parseObject:function(){var matrix=new THREE.Matrix4;return function(data,geometries,materials){function getGeometry(name){return void 0===geometries[name]&&console.warn("THREE.ObjectLoader: Undefined geometry",name),geometries[name]}function getMaterial(name){return void 0!==name?(void 0===materials[name]&&console.warn("THREE.ObjectLoader: Undefined material",name),materials[name]):void 0}var object;switch(data.type){case"Scene":object=new THREE.Scene;break;case"PerspectiveCamera":object=new THREE.PerspectiveCamera(data.fov,data.aspect,data.near,data.far);break;case"OrthographicCamera":object=new THREE.OrthographicCamera(data.left,data.right,data.top,data.bottom,data.near,data.far);break;case"AmbientLight":object=new THREE.AmbientLight(data.color);break;case"DirectionalLight":object=new THREE.DirectionalLight(data.color,data.intensity);break;case"PointLight":object=new THREE.PointLight(data.color,data.intensity,data.distance,data.decay);break;case"SpotLight":object=new THREE.SpotLight(data.color,data.intensity,data.distance,data.angle,data.exponent,data.decay);break;case"HemisphereLight":object=new THREE.HemisphereLight(data.color,data.groundColor,data.intensity);break;case"Mesh":object=new THREE.Mesh(getGeometry(data.geometry),getMaterial(data.material));break;case"LOD":object=new THREE.LOD;break;case"Line":object=new THREE.Line(getGeometry(data.geometry),getMaterial(data.material),data.mode);break;case"PointCloud":case"Points":object=new THREE.Points(getGeometry(data.geometry),getMaterial(data.material));break;case"Sprite":object=new THREE.Sprite(getMaterial(data.material));break;case"Group":object=new THREE.Group;break;default:object=new THREE.Object3D}if(object.uuid=data.uuid,void 0!==data.name&&(object.name=data.name),void 0!==data.matrix?(matrix.fromArray(data.matrix),matrix.decompose(object.position,object.quaternion,object.scale)):(void 0!==data.position&&object.position.fromArray(data.position),void 0!==data.rotation&&object.rotation.fromArray(data.rotation),void 0!==data.scale&&object.scale.fromArray(data.scale)),void 0!==data.castShadow&&(object.castShadow=data.castShadow),void 0!==data.receiveShadow&&(object.receiveShadow=data.receiveShadow),void 0!==data.visible&&(object.visible=data.visible),void 0!==data.userData&&(object.userData=data.userData),void 0!==data.children)for(var child in data.children)object.add(this.parseObject(data.children[child],geometries,materials));if("LOD"===data.type)for(var levels=data.levels,l=0;li;++i)loadTexture(i);else loader.load(url,function(buffer){var texDatas=scope._parser(buffer,!0);if(texDatas.isCubemap)for(var faces=texDatas.mipmaps.length/texDatas.mipmapCount,f=0;faces>f;f++){images[f]={mipmaps:[]};for(var i=0;i0&&(data.alphaTest=this.alphaTest),this.wireframe===!0&&(data.wireframe=this.wireframe),this.wireframeLinewidth>1&&(data.wireframeLinewidth=this.wireframeLinewidth),data},clone:function(){return(new this.constructor).copy(this)},copy:function(source){return this.name=source.name,this.side=source.side,this.opacity=source.opacity,this.transparent=source.transparent,this.blending=source.blending,this.blendSrc=source.blendSrc,this.blendDst=source.blendDst,this.blendEquation=source.blendEquation,this.blendSrcAlpha=source.blendSrcAlpha,this.blendDstAlpha=source.blendDstAlpha,this.blendEquationAlpha=source.blendEquationAlpha,this.depthFunc=source.depthFunc,this.depthTest=source.depthTest,this.depthWrite=source.depthWrite,this.precision=source.precision,this.polygonOffset=source.polygonOffset,this.polygonOffsetFactor=source.polygonOffsetFactor,this.polygonOffsetUnits=source.polygonOffsetUnits,this.alphaTest=source.alphaTest,this.overdraw=source.overdraw,this.visible=source.visible,this},update:function(){this.dispatchEvent({type:"update"})},dispose:function(){this.dispatchEvent({type:"dispose"})},get wrapAround(){console.warn("THREE."+this.type+": .wrapAround has been removed.")},set wrapAround(boolean){console.warn("THREE."+this.type+": .wrapAround has been removed.")},get wrapRGB(){return console.warn("THREE."+this.type+": .wrapRGB has been removed."),new THREE.Color}},THREE.EventDispatcher.prototype.apply(THREE.Material.prototype),THREE.MaterialIdCount=0,THREE.LineBasicMaterial=function(parameters){THREE.Material.call(this),this.type="LineBasicMaterial",this.color=new THREE.Color(16777215),this.linewidth=1,this.linecap="round",this.linejoin="round",this.vertexColors=THREE.NoColors,this.fog=!0,this.setValues(parameters)},THREE.LineBasicMaterial.prototype=Object.create(THREE.Material.prototype),THREE.LineBasicMaterial.prototype.constructor=THREE.LineBasicMaterial,THREE.LineBasicMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.color.copy(source.color),this.linewidth=source.linewidth,this.linecap=source.linecap,this.linejoin=source.linejoin,this.vertexColors=source.vertexColors,this.fog=source.fog,this},THREE.LineDashedMaterial=function(parameters){THREE.Material.call(this),this.type="LineDashedMaterial",this.color=new THREE.Color(16777215),this.linewidth=1,this.scale=1,this.dashSize=3,this.gapSize=1,this.vertexColors=!1,this.fog=!0,this.setValues(parameters)},THREE.LineDashedMaterial.prototype=Object.create(THREE.Material.prototype),THREE.LineDashedMaterial.prototype.constructor=THREE.LineDashedMaterial,THREE.LineDashedMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.color.copy(source.color),this.linewidth=source.linewidth,this.scale=source.scale,this.dashSize=source.dashSize,this.gapSize=source.gapSize,this.vertexColors=source.vertexColors,this.fog=source.fog,this},THREE.MeshBasicMaterial=function(parameters){THREE.Material.call(this),this.type="MeshBasicMaterial",this.color=new THREE.Color(16777215),this.map=null,this.aoMap=null,this.aoMapIntensity=1,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.combine=THREE.MultiplyOperation,this.reflectivity=1,this.refractionRatio=.98,this.fog=!0,this.shading=THREE.SmoothShading,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.vertexColors=THREE.NoColors,this.skinning=!1,this.morphTargets=!1,this.setValues(parameters)},THREE.MeshBasicMaterial.prototype=Object.create(THREE.Material.prototype),THREE.MeshBasicMaterial.prototype.constructor=THREE.MeshBasicMaterial,THREE.MeshBasicMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.color.copy(source.color),this.map=source.map,this.aoMap=source.aoMap,this.aoMapIntensity=source.aoMapIntensity,this.specularMap=source.specularMap,this.alphaMap=source.alphaMap,this.envMap=source.envMap,this.combine=source.combine,this.reflectivity=source.reflectivity,this.refractionRatio=source.refractionRatio,this.fog=source.fog,this.shading=source.shading,this.wireframe=source.wireframe,this.wireframeLinewidth=source.wireframeLinewidth,this.wireframeLinecap=source.wireframeLinecap,this.wireframeLinejoin=source.wireframeLinejoin,this.vertexColors=source.vertexColors,this.skinning=source.skinning,this.morphTargets=source.morphTargets,this},THREE.MeshLambertMaterial=function(parameters){THREE.Material.call(this),this.type="MeshLambertMaterial",this.color=new THREE.Color(16777215),this.emissive=new THREE.Color(0),this.map=null,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.combine=THREE.MultiplyOperation,this.reflectivity=1,this.refractionRatio=.98,this.fog=!0,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.vertexColors=THREE.NoColors,this.skinning=!1,this.morphTargets=!1,this.morphNormals=!1,this.setValues(parameters)},THREE.MeshLambertMaterial.prototype=Object.create(THREE.Material.prototype),THREE.MeshLambertMaterial.prototype.constructor=THREE.MeshLambertMaterial,THREE.MeshLambertMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.color.copy(source.color),this.emissive.copy(source.emissive),this.map=source.map,this.specularMap=source.specularMap,this.alphaMap=source.alphaMap,this.envMap=source.envMap,this.combine=source.combine,this.reflectivity=source.reflectivity,this.refractionRatio=source.refractionRatio,this.fog=source.fog,this.wireframe=source.wireframe,this.wireframeLinewidth=source.wireframeLinewidth,this.wireframeLinecap=source.wireframeLinecap,this.wireframeLinejoin=source.wireframeLinejoin,this.vertexColors=source.vertexColors,this.skinning=source.skinning,this.morphTargets=source.morphTargets,this.morphNormals=source.morphNormals,this},THREE.MeshPhongMaterial=function(parameters){THREE.Material.call(this),this.type="MeshPhongMaterial",this.color=new THREE.Color(16777215),this.emissive=new THREE.Color(0),this.specular=new THREE.Color(1118481),this.shininess=30,this.metal=!1,this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissiveMap=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalScale=new THREE.Vector2(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.combine=THREE.MultiplyOperation,this.reflectivity=1,this.refractionRatio=.98,this.fog=!0,this.shading=THREE.SmoothShading,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.vertexColors=THREE.NoColors,this.skinning=!1,this.morphTargets=!1,this.morphNormals=!1,this.setValues(parameters)},THREE.MeshPhongMaterial.prototype=Object.create(THREE.Material.prototype),THREE.MeshPhongMaterial.prototype.constructor=THREE.MeshPhongMaterial,THREE.MeshPhongMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.color.copy(source.color),this.emissive.copy(source.emissive),this.specular.copy(source.specular),this.shininess=source.shininess,this.metal=source.metal,this.map=source.map,this.lightMap=source.lightMap,this.lightMapIntensity=source.lightMapIntensity,this.aoMap=source.aoMap,this.aoMapIntensity=source.aoMapIntensity,this.emissiveMap=source.emissiveMap,this.bumpMap=source.bumpMap,this.bumpScale=source.bumpScale,this.normalMap=source.normalMap,this.normalScale.copy(source.normalScale),this.displacementMap=source.displacementMap,this.displacementScale=source.displacementScale,this.displacementBias=source.displacementBias,this.specularMap=source.specularMap,this.alphaMap=source.alphaMap,this.envMap=source.envMap,this.combine=source.combine,this.reflectivity=source.reflectivity,this.refractionRatio=source.refractionRatio,this.fog=source.fog,this.shading=source.shading,this.wireframe=source.wireframe,this.wireframeLinewidth=source.wireframeLinewidth,this.wireframeLinecap=source.wireframeLinecap,this.wireframeLinejoin=source.wireframeLinejoin,this.vertexColors=source.vertexColors,this.skinning=source.skinning,this.morphTargets=source.morphTargets,this.morphNormals=source.morphNormals,this},THREE.MeshDepthMaterial=function(parameters){THREE.Material.call(this),this.type="MeshDepthMaterial",this.morphTargets=!1,this.wireframe=!1,this.wireframeLinewidth=1,this.setValues(parameters)},THREE.MeshDepthMaterial.prototype=Object.create(THREE.Material.prototype),THREE.MeshDepthMaterial.prototype.constructor=THREE.MeshDepthMaterial,THREE.MeshDepthMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.wireframe=source.wireframe,this.wireframeLinewidth=source.wireframeLinewidth,this},THREE.MeshNormalMaterial=function(parameters){THREE.Material.call(this,parameters),this.type="MeshNormalMaterial",this.wireframe=!1,this.wireframeLinewidth=1,this.morphTargets=!1,this.setValues(parameters)},THREE.MeshNormalMaterial.prototype=Object.create(THREE.Material.prototype),THREE.MeshNormalMaterial.prototype.constructor=THREE.MeshNormalMaterial,THREE.MeshNormalMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.wireframe=source.wireframe,this.wireframeLinewidth=source.wireframeLinewidth,this},THREE.MultiMaterial=function(materials){this.uuid=THREE.Math.generateUUID(),this.type="MultiMaterial",this.materials=materials instanceof Array?materials:[],this.visible=!0},THREE.MultiMaterial.prototype={constructor:THREE.MultiMaterial,toJSON:function(){for(var output={metadata:{version:4.2,type:"material",generator:"MaterialExporter"},uuid:this.uuid,type:this.type,materials:[]},i=0,l=this.materials.length;l>i;i++)output.materials.push(this.materials[i].toJSON());return output.visible=this.visible,output},clone:function(){for(var material=new this.constructor,i=0;i2048||canvas.height>2048?canvas.toDataURL("image/jpeg",.6):canvas.toDataURL("image/png")}if(void 0!==meta.textures[this.uuid])return meta.textures[this.uuid];var output={metadata:{version:4.4,type:"Texture",generator:"Texture.toJSON"},uuid:this.uuid,name:this.name,mapping:this.mapping,repeat:[this.repeat.x,this.repeat.y],offset:[this.offset.x,this.offset.y],wrap:[this.wrapS,this.wrapT],minFilter:this.minFilter,magFilter:this.magFilter,anisotropy:this.anisotropy};if(void 0!==this.image){var image=this.image;void 0===image.uuid&&(image.uuid=THREE.Math.generateUUID()),void 0===meta.images[image.uuid]&&(meta.images[image.uuid]={uuid:image.uuid,url:getDataURL(image)}),output.image=image.uuid}return meta.textures[this.uuid]=output,output},dispose:function(){this.dispatchEvent({type:"dispose"})},transformUv:function(uv){if(this.mapping===THREE.UVMapping){if(uv.multiply(this.repeat),uv.add(this.offset),uv.x<0||uv.x>1)switch(this.wrapS){case THREE.RepeatWrapping:uv.x=uv.x-Math.floor(uv.x);break;case THREE.ClampToEdgeWrapping:uv.x=uv.x<0?0:1;break;case THREE.MirroredRepeatWrapping:1===Math.abs(Math.floor(uv.x)%2)?uv.x=Math.ceil(uv.x)-uv.x:uv.x=uv.x-Math.floor(uv.x)}if(uv.y<0||uv.y>1)switch(this.wrapT){case THREE.RepeatWrapping:uv.y=uv.y-Math.floor(uv.y);break;case THREE.ClampToEdgeWrapping:uv.y=uv.y<0?0:1;break;case THREE.MirroredRepeatWrapping:1===Math.abs(Math.floor(uv.y)%2)?uv.y=Math.ceil(uv.y)-uv.y:uv.y=uv.y-Math.floor(uv.y)}this.flipY&&(uv.y=1-uv.y)}}},THREE.EventDispatcher.prototype.apply(THREE.Texture.prototype),THREE.TextureIdCount=0,THREE.CanvasTexture=function(canvas,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy){THREE.Texture.call(this,canvas,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy),this.needsUpdate=!0},THREE.CanvasTexture.prototype=Object.create(THREE.Texture.prototype),THREE.CanvasTexture.prototype.constructor=THREE.CanvasTexture,THREE.CubeTexture=function(images,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy){mapping=void 0!==mapping?mapping:THREE.CubeReflectionMapping,THREE.Texture.call(this,images,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy),this.images=images,this.flipY=!1},THREE.CubeTexture.prototype=Object.create(THREE.Texture.prototype),THREE.CubeTexture.prototype.constructor=THREE.CubeTexture,THREE.CubeTexture.prototype.copy=function(source){return THREE.Texture.prototype.copy.call(this,source),this.images=source.images,this},THREE.CompressedTexture=function(mipmaps,width,height,format,type,mapping,wrapS,wrapT,magFilter,minFilter,anisotropy){THREE.Texture.call(this,null,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy),this.image={width:width,height:height},this.mipmaps=mipmaps,this.flipY=!1,this.generateMipmaps=!1},THREE.CompressedTexture.prototype=Object.create(THREE.Texture.prototype),THREE.CompressedTexture.prototype.constructor=THREE.CompressedTexture,THREE.DataTexture=function(data,width,height,format,type,mapping,wrapS,wrapT,magFilter,minFilter,anisotropy){THREE.Texture.call(this,null,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy),this.image={data:data,width:width,height:height +},this.magFilter=void 0!==magFilter?magFilter:THREE.NearestFilter,this.minFilter=void 0!==minFilter?minFilter:THREE.NearestFilter,this.flipY=!1,this.generateMipmaps=!1},THREE.DataTexture.prototype=Object.create(THREE.Texture.prototype),THREE.DataTexture.prototype.constructor=THREE.DataTexture,THREE.VideoTexture=function(video,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy){function update(){requestAnimationFrame(update),video.readyState===video.HAVE_ENOUGH_DATA&&(scope.needsUpdate=!0)}THREE.Texture.call(this,video,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy),this.generateMipmaps=!1;var scope=this;update()},THREE.VideoTexture.prototype=Object.create(THREE.Texture.prototype),THREE.VideoTexture.prototype.constructor=THREE.VideoTexture,THREE.Group=function(){THREE.Object3D.call(this),this.type="Group"},THREE.Group.prototype=Object.create(THREE.Object3D.prototype),THREE.Group.prototype.constructor=THREE.Group,THREE.Points=function(geometry,material){THREE.Object3D.call(this),this.type="Points",this.geometry=void 0!==geometry?geometry:new THREE.Geometry,this.material=void 0!==material?material:new THREE.PointsMaterial({color:16777215*Math.random()})},THREE.Points.prototype=Object.create(THREE.Object3D.prototype),THREE.Points.prototype.constructor=THREE.Points,THREE.Points.prototype.raycast=function(){var inverseMatrix=new THREE.Matrix4,ray=new THREE.Ray;return function(raycaster,intersects){function testPoint(point,index){var rayPointDistanceSq=ray.distanceSqToPoint(point);if(localThresholdSq>rayPointDistanceSq){var intersectPoint=ray.closestPointToPoint(point);intersectPoint.applyMatrix4(object.matrixWorld);var distance=raycaster.ray.origin.distanceTo(intersectPoint);if(distanceraycaster.far)return;intersects.push({distance:distance,distanceToRay:Math.sqrt(rayPointDistanceSq),point:intersectPoint.clone(),index:index,face:null,object:object})}}var object=this,geometry=object.geometry,threshold=raycaster.params.Points.threshold;if(inverseMatrix.getInverse(this.matrixWorld),ray.copy(raycaster.ray).applyMatrix4(inverseMatrix),null===geometry.boundingBox||ray.isIntersectionBox(geometry.boundingBox)!==!1){var localThreshold=threshold/((this.scale.x+this.scale.y+this.scale.z)/3),localThresholdSq=localThreshold*localThreshold,position=new THREE.Vector3;if(geometry instanceof THREE.BufferGeometry){var index=geometry.index,attributes=geometry.attributes,positions=attributes.position.array;if(null!==index)for(var indices=index.array,i=0,il=indices.length;il>i;i++){var a=indices[i];position.fromArray(positions,3*a),testPoint(position,a)}else for(var i=0,l=positions.length/3;l>i;i++)position.fromArray(positions,3*i),testPoint(position,i)}else for(var vertices=geometry.vertices,i=0,l=vertices.length;l>i;i++)testPoint(vertices[i],i)}}}(),THREE.Points.prototype.clone=function(){return new this.constructor(this.geometry,this.material).copy(this)},THREE.PointCloud=function(geometry,material){return console.warn("THREE.PointCloud has been renamed to THREE.Points."),new THREE.Points(geometry,material)},THREE.ParticleSystem=function(geometry,material){return console.warn("THREE.ParticleSystem has been renamed to THREE.Points."),new THREE.Points(geometry,material)},THREE.Line=function(geometry,material,mode){return 1===mode?(console.warn("THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead."),new THREE.LineSegments(geometry,material)):(THREE.Object3D.call(this),this.type="Line",this.geometry=void 0!==geometry?geometry:new THREE.Geometry,void(this.material=void 0!==material?material:new THREE.LineBasicMaterial({color:16777215*Math.random()})))},THREE.Line.prototype=Object.create(THREE.Object3D.prototype),THREE.Line.prototype.constructor=THREE.Line,THREE.Line.prototype.raycast=function(){var inverseMatrix=new THREE.Matrix4,ray=new THREE.Ray,sphere=new THREE.Sphere;return function(raycaster,intersects){var precision=raycaster.linePrecision,precisionSq=precision*precision,geometry=this.geometry;if(null===geometry.boundingSphere&&geometry.computeBoundingSphere(),sphere.copy(geometry.boundingSphere),sphere.applyMatrix4(this.matrixWorld),raycaster.ray.isIntersectionSphere(sphere)!==!1){inverseMatrix.getInverse(this.matrixWorld),ray.copy(raycaster.ray).applyMatrix4(inverseMatrix);var vStart=new THREE.Vector3,vEnd=new THREE.Vector3,interSegment=new THREE.Vector3,interRay=new THREE.Vector3,step=this instanceof THREE.LineSegments?2:1;if(geometry instanceof THREE.BufferGeometry){var index=geometry.index,attributes=geometry.attributes;if(null!==index)for(var indices=index.array,positions=attributes.position.array,i=0,l=indices.length-1;l>i;i+=step){var a=indices[i],b=indices[i+1];vStart.fromArray(positions,3*a),vEnd.fromArray(positions,3*b);var distSq=ray.distanceSqToSegment(vStart,vEnd,interRay,interSegment);if(!(distSq>precisionSq)){interRay.applyMatrix4(this.matrixWorld);var distance=raycaster.ray.origin.distanceTo(interRay);distanceraycaster.far||intersects.push({distance:distance,point:interSegment.clone().applyMatrix4(this.matrixWorld),index:i,face:null,faceIndex:null,object:this})}}else for(var positions=attributes.position.array,i=0,l=positions.length/3-1;l>i;i+=step){vStart.fromArray(positions,3*i),vEnd.fromArray(positions,3*i+3);var distSq=ray.distanceSqToSegment(vStart,vEnd,interRay,interSegment);if(!(distSq>precisionSq)){interRay.applyMatrix4(this.matrixWorld);var distance=raycaster.ray.origin.distanceTo(interRay);distanceraycaster.far||intersects.push({distance:distance,point:interSegment.clone().applyMatrix4(this.matrixWorld),index:i,face:null,faceIndex:null,object:this})}}}else if(geometry instanceof THREE.Geometry)for(var vertices=geometry.vertices,nbVertices=vertices.length,i=0;nbVertices-1>i;i+=step){var distSq=ray.distanceSqToSegment(vertices[i],vertices[i+1],interRay,interSegment);if(!(distSq>precisionSq)){interRay.applyMatrix4(this.matrixWorld);var distance=raycaster.ray.origin.distanceTo(interRay);distanceraycaster.far||intersects.push({distance:distance,point:interSegment.clone().applyMatrix4(this.matrixWorld),index:i,face:null,faceIndex:null,object:this})}}}}}(),THREE.Line.prototype.clone=function(){return new this.constructor(this.geometry,this.material).copy(this)},THREE.LineStrip=0,THREE.LinePieces=1,THREE.LineSegments=function(geometry,material){THREE.Line.call(this,geometry,material),this.type="LineSegments"},THREE.LineSegments.prototype=Object.create(THREE.Line.prototype),THREE.LineSegments.prototype.constructor=THREE.LineSegments,THREE.Mesh=function(geometry,material){THREE.Object3D.call(this),this.type="Mesh",this.geometry=void 0!==geometry?geometry:new THREE.Geometry,this.material=void 0!==material?material:new THREE.MeshBasicMaterial({color:16777215*Math.random()}),this.updateMorphTargets()},THREE.Mesh.prototype=Object.create(THREE.Object3D.prototype),THREE.Mesh.prototype.constructor=THREE.Mesh,THREE.Mesh.prototype.updateMorphTargets=function(){if(void 0!==this.geometry.morphTargets&&this.geometry.morphTargets.length>0){this.morphTargetBase=-1,this.morphTargetInfluences=[],this.morphTargetDictionary={};for(var m=0,ml=this.geometry.morphTargets.length;ml>m;m++)this.morphTargetInfluences.push(0),this.morphTargetDictionary[this.geometry.morphTargets[m].name]=m}},THREE.Mesh.prototype.getMorphTargetIndexByName=function(name){return void 0!==this.morphTargetDictionary[name]?this.morphTargetDictionary[name]:(console.warn("THREE.Mesh.getMorphTargetIndexByName: morph target "+name+" does not exist. Returning 0."),0)},THREE.Mesh.prototype.raycast=function(){function uvIntersection(point,p1,p2,p3,uv1,uv2,uv3){return THREE.Triangle.barycoordFromPoint(point,p1,p2,p3,barycoord),uv1.multiplyScalar(barycoord.x),uv2.multiplyScalar(barycoord.y),uv3.multiplyScalar(barycoord.z),uv1.add(uv2).add(uv3),uv1.clone()}function checkIntersection(object,raycaster,ray,pA,pB,pC,point){var intersect,material=object.material;if(intersect=material.side===THREE.BackSide?ray.intersectTriangle(pC,pB,pA,!0,point):ray.intersectTriangle(pA,pB,pC,material.side!==THREE.DoubleSide,point),null===intersect)return null;intersectionPointWorld.copy(point),intersectionPointWorld.applyMatrix4(object.matrixWorld);var distance=raycaster.ray.origin.distanceTo(intersectionPointWorld);return distanceraycaster.far?null:{distance:distance,point:intersectionPointWorld.clone(),object:object}}function checkBufferGeometryIntersection(object,raycaster,ray,positions,uvs,a,b,c){vA.fromArray(positions,3*a),vB.fromArray(positions,3*b),vC.fromArray(positions,3*c);var intersection=checkIntersection(object,raycaster,ray,vA,vB,vC,intersectionPoint);return intersection&&(uvs&&(uvA.fromArray(uvs,2*a),uvB.fromArray(uvs,2*b),uvC.fromArray(uvs,2*c),intersection.uv=uvIntersection(intersectionPoint,vA,vB,vC,uvA,uvB,uvC)),intersection.face=new THREE.Face3(a,b,c,THREE.Triangle.normal(vA,vB,vC)),intersection.faceIndex=a),intersection}var inverseMatrix=new THREE.Matrix4,ray=new THREE.Ray,sphere=new THREE.Sphere,vA=new THREE.Vector3,vB=new THREE.Vector3,vC=new THREE.Vector3,tempA=new THREE.Vector3,tempB=new THREE.Vector3,tempC=new THREE.Vector3,uvA=new THREE.Vector2,uvB=new THREE.Vector2,uvC=new THREE.Vector2,barycoord=new THREE.Vector3,intersectionPoint=new THREE.Vector3,intersectionPointWorld=new THREE.Vector3;return function(raycaster,intersects){var geometry=this.geometry,material=this.material;if(void 0!==material){null===geometry.boundingSphere&&geometry.computeBoundingSphere();var matrixWorld=this.matrixWorld;if(sphere.copy(geometry.boundingSphere),sphere.applyMatrix4(matrixWorld),raycaster.ray.isIntersectionSphere(sphere)!==!1&&(inverseMatrix.getInverse(matrixWorld),ray.copy(raycaster.ray).applyMatrix4(inverseMatrix),null===geometry.boundingBox||ray.isIntersectionBox(geometry.boundingBox)!==!1)){var uvs,intersection;if(geometry instanceof THREE.BufferGeometry){var a,b,c,index=geometry.index,attributes=geometry.attributes,positions=attributes.position.array;if(void 0!==attributes.uv&&(uvs=attributes.uv.array),null!==index)for(var indices=index.array,i=0,l=indices.length;l>i;i+=3)a=indices[i],b=indices[i+1],c=indices[i+2],intersection=checkBufferGeometryIntersection(this,raycaster,ray,positions,uvs,a,b,c),intersection&&(intersection.faceIndex=Math.floor(i/3),intersects.push(intersection));else for(var i=0,l=positions.length;l>i;i+=9)a=i/3,b=a+1,c=a+2,intersection=checkBufferGeometryIntersection(this,raycaster,ray,positions,uvs,a,b,c),intersection&&(intersection.index=a,intersects.push(intersection))}else if(geometry instanceof THREE.Geometry){var fvA,fvB,fvC,isFaceMaterial=material instanceof THREE.MeshFaceMaterial,materials=isFaceMaterial===!0?material.materials:null,vertices=geometry.vertices,faces=geometry.faces,faceVertexUvs=geometry.faceVertexUvs[0];faceVertexUvs.length>0&&(uvs=faceVertexUvs);for(var f=0,fl=faces.length;fl>f;f++){var face=faces[f],faceMaterial=isFaceMaterial===!0?materials[face.materialIndex]:material;if(void 0!==faceMaterial){if(fvA=vertices[face.a],fvB=vertices[face.b],fvC=vertices[face.c],faceMaterial.morphTargets===!0){var morphTargets=geometry.morphTargets,morphInfluences=this.morphTargetInfluences;vA.set(0,0,0),vB.set(0,0,0),vC.set(0,0,0);for(var t=0,tl=morphTargets.length;tl>t;t++){var influence=morphInfluences[t];if(0!==influence){var targets=morphTargets[t].vertices;vA.addScaledVector(tempA.subVectors(targets[face.a],fvA),influence),vB.addScaledVector(tempB.subVectors(targets[face.b],fvB),influence),vC.addScaledVector(tempC.subVectors(targets[face.c],fvC),influence)}}vA.add(fvA),vB.add(fvB),vC.add(fvC),fvA=vA,fvB=vB,fvC=vC}if(intersection=checkIntersection(this,raycaster,ray,fvA,fvB,fvC,intersectionPoint)){if(uvs){var uvs_f=uvs[f];uvA.copy(uvs_f[0]),uvB.copy(uvs_f[1]),uvC.copy(uvs_f[2]),intersection.uv=uvIntersection(intersectionPoint,fvA,fvB,fvC,uvA,uvB,uvC)}intersection.face=face,intersection.faceIndex=f,intersects.push(intersection)}}}}}}}}(),THREE.Mesh.prototype.clone=function(){return new this.constructor(this.geometry,this.material).copy(this)},THREE.Bone=function(skin){THREE.Object3D.call(this),this.type="Bone",this.skin=skin},THREE.Bone.prototype=Object.create(THREE.Object3D.prototype),THREE.Bone.prototype.constructor=THREE.Bone,THREE.Bone.prototype.copy=function(source){return THREE.Object3D.prototype.copy.call(this,source),this.skin=source.skin,this},THREE.Skeleton=function(bones,boneInverses,useVertexTexture){if(this.useVertexTexture=void 0!==useVertexTexture?useVertexTexture:!0,this.identityMatrix=new THREE.Matrix4,bones=bones||[],this.bones=bones.slice(0),this.useVertexTexture){var size=Math.sqrt(4*this.bones.length);size=THREE.Math.nextPowerOfTwo(Math.ceil(size)),size=Math.max(size,4),this.boneTextureWidth=size,this.boneTextureHeight=size,this.boneMatrices=new Float32Array(this.boneTextureWidth*this.boneTextureHeight*4),this.boneTexture=new THREE.DataTexture(this.boneMatrices,this.boneTextureWidth,this.boneTextureHeight,THREE.RGBAFormat,THREE.FloatType)}else this.boneMatrices=new Float32Array(16*this.bones.length);if(void 0===boneInverses)this.calculateInverses();else if(this.bones.length===boneInverses.length)this.boneInverses=boneInverses.slice(0);else{console.warn("THREE.Skeleton bonInverses is the wrong length."),this.boneInverses=[];for(var b=0,bl=this.bones.length;bl>b;b++)this.boneInverses.push(new THREE.Matrix4)}},THREE.Skeleton.prototype.calculateInverses=function(){this.boneInverses=[];for(var b=0,bl=this.bones.length;bl>b;b++){var inverse=new THREE.Matrix4;this.bones[b]&&inverse.getInverse(this.bones[b].matrixWorld),this.boneInverses.push(inverse)}},THREE.Skeleton.prototype.pose=function(){for(var bone,b=0,bl=this.bones.length;bl>b;b++)bone=this.bones[b],bone&&bone.matrixWorld.getInverse(this.boneInverses[b]);for(var b=0,bl=this.bones.length;bl>b;b++)bone=this.bones[b],bone&&(bone.parent?(bone.matrix.getInverse(bone.parent.matrixWorld),bone.matrix.multiply(bone.matrixWorld)):bone.matrix.copy(bone.matrixWorld),bone.matrix.decompose(bone.position,bone.quaternion,bone.scale))},THREE.Skeleton.prototype.update=function(){var offsetMatrix=new THREE.Matrix4;return function(){for(var b=0,bl=this.bones.length;bl>b;b++){var matrix=this.bones[b]?this.bones[b].matrixWorld:this.identityMatrix;offsetMatrix.multiplyMatrices(matrix,this.boneInverses[b]),offsetMatrix.flattenToArrayOffset(this.boneMatrices,16*b)}this.useVertexTexture&&(this.boneTexture.needsUpdate=!0)}}(),THREE.Skeleton.prototype.clone=function(){return new THREE.Skeleton(this.bones,this.boneInverses,this.useVertexTexture)},THREE.SkinnedMesh=function(geometry,material,useVertexTexture){THREE.Mesh.call(this,geometry,material),this.type="SkinnedMesh",this.bindMode="attached",this.bindMatrix=new THREE.Matrix4,this.bindMatrixInverse=new THREE.Matrix4;var bones=[];if(this.geometry&&void 0!==this.geometry.bones){for(var bone,gbone,b=0,bl=this.geometry.bones.length;bl>b;++b)gbone=this.geometry.bones[b],bone=new THREE.Bone(this),bones.push(bone),bone.name=gbone.name,bone.position.fromArray(gbone.pos),bone.quaternion.fromArray(gbone.rotq),void 0!==gbone.scl&&bone.scale.fromArray(gbone.scl);for(var b=0,bl=this.geometry.bones.length;bl>b;++b)gbone=this.geometry.bones[b],-1!==gbone.parent&&null!==gbone.parent?bones[gbone.parent].add(bones[b]):this.add(bones[b])}this.normalizeSkinWeights(),this.updateMatrixWorld(!0),this.bind(new THREE.Skeleton(bones,void 0,useVertexTexture),this.matrixWorld)},THREE.SkinnedMesh.prototype=Object.create(THREE.Mesh.prototype),THREE.SkinnedMesh.prototype.constructor=THREE.SkinnedMesh,THREE.SkinnedMesh.prototype.bind=function(skeleton,bindMatrix){this.skeleton=skeleton,void 0===bindMatrix&&(this.updateMatrixWorld(!0),this.skeleton.calculateInverses(),bindMatrix=this.matrixWorld),this.bindMatrix.copy(bindMatrix),this.bindMatrixInverse.getInverse(bindMatrix)},THREE.SkinnedMesh.prototype.pose=function(){this.skeleton.pose()},THREE.SkinnedMesh.prototype.normalizeSkinWeights=function(){if(this.geometry instanceof THREE.Geometry)for(var i=0;ii&&!(distance1){v1.setFromMatrixPosition(camera.matrixWorld),v2.setFromMatrixPosition(this.matrixWorld);var distance=v1.distanceTo(v2);levels[0].object.visible=!0;for(var i=1,l=levels.length;l>i&&distance>=levels[i].distance;i++)levels[i-1].object.visible=!1,levels[i].object.visible=!0;for(;l>i;i++)levels[i].object.visible=!1}}}(),THREE.LOD.prototype.copy=function(source){THREE.Object3D.prototype.copy.call(this,source,!1);for(var levels=source.levels,i=0,l=levels.length;l>i;i++){var level=levels[i];this.addLevel(level.object.clone(),level.distance)}return this},THREE.LOD.prototype.toJSON=function(meta){var data=THREE.Object3D.prototype.toJSON.call(this,meta);data.object.levels=[];for(var levels=this.levels,i=0,l=levels.length;l>i;i++){var level=levels[i];data.object.levels.push({object:level.object.uuid,distance:level.distance})}return data},THREE.Sprite=function(){var indices=new Uint16Array([0,1,2,0,2,3]),vertices=new Float32Array([-.5,-.5,0,.5,-.5,0,.5,.5,0,-.5,.5,0]),uvs=new Float32Array([0,0,1,0,1,1,0,1]),geometry=new THREE.BufferGeometry;return geometry.setIndex(new THREE.BufferAttribute(indices,1)),geometry.addAttribute("position",new THREE.BufferAttribute(vertices,3)),geometry.addAttribute("uv",new THREE.BufferAttribute(uvs,2)),function(material){THREE.Object3D.call(this),this.type="Sprite",this.geometry=geometry,this.material=void 0!==material?material:new THREE.SpriteMaterial}}(),THREE.Sprite.prototype=Object.create(THREE.Object3D.prototype),THREE.Sprite.prototype.constructor=THREE.Sprite,THREE.Sprite.prototype.raycast=function(){var matrixPosition=new THREE.Vector3;return function(raycaster,intersects){matrixPosition.setFromMatrixPosition(this.matrixWorld);var distanceSq=raycaster.ray.distanceSqToPoint(matrixPosition),guessSizeSq=this.scale.x*this.scale.y;distanceSq>guessSizeSq||intersects.push({distance:Math.sqrt(distanceSq),point:this.position,face:null,object:this})}}(),THREE.Sprite.prototype.clone=function(){return new this.constructor(this.material).copy(this)},THREE.Particle=THREE.Sprite,THREE.LensFlare=function(texture,size,distance,blending,color){THREE.Object3D.call(this),this.lensFlares=[],this.positionScreen=new THREE.Vector3,this.customUpdateCallback=void 0,void 0!==texture&&this.add(texture,size,distance,blending,color)},THREE.LensFlare.prototype=Object.create(THREE.Object3D.prototype),THREE.LensFlare.prototype.constructor=THREE.LensFlare,THREE.LensFlare.prototype.add=function(texture,size,distance,blending,color,opacity){void 0===size&&(size=-1),void 0===distance&&(distance=0),void 0===opacity&&(opacity=1),void 0===color&&(color=new THREE.Color(16777215)),void 0===blending&&(blending=THREE.NormalBlending),distance=Math.min(distance,Math.max(0,distance)),this.lensFlares.push({texture:texture,size:size,distance:distance,x:0,y:0,z:0,scale:1,rotation:0,opacity:opacity,color:color,blending:blending})},THREE.LensFlare.prototype.updateLensFlares=function(){var f,flare,fl=this.lensFlares.length,vecX=2*-this.positionScreen.x,vecY=2*-this.positionScreen.y;for(f=0;fl>f;f++)flare=this.lensFlares[f],flare.x=this.positionScreen.x+vecX*flare.distance,flare.y=this.positionScreen.y+vecY*flare.distance,flare.wantedRotation=flare.x*Math.PI*.25,flare.rotation+=.25*(flare.wantedRotation-flare.rotation)},THREE.LensFlare.prototype.copy=function(source){THREE.Object3D.prototype.copy.call(this,source),this.positionScreen.copy(source.positionScreen),this.customUpdateCallback=source.customUpdateCallback;for(var i=0,l=source.lensFlares.length;l>i;i++)this.lensFlares.push(source.lensFlares[i]);return this},THREE.Scene=function(){THREE.Object3D.call(this),this.type="Scene",this.fog=null,this.overrideMaterial=null,this.autoUpdate=!0},THREE.Scene.prototype=Object.create(THREE.Object3D.prototype),THREE.Scene.prototype.constructor=THREE.Scene,THREE.Scene.prototype.copy=function(source){return THREE.Object3D.prototype.copy.call(this,source),null!==source.fog&&(this.fog=source.fog.clone()),null!==source.overrideMaterial&&(this.overrideMaterial=source.overrideMaterial.clone()),this.autoUpdate=source.autoUpdate,this.matrixAutoUpdate=source.matrixAutoUpdate,this},THREE.Fog=function(color,near,far){this.name="",this.color=new THREE.Color(color),this.near=void 0!==near?near:1,this.far=void 0!==far?far:1e3},THREE.Fog.prototype.clone=function(){return new THREE.Fog(this.color.getHex(),this.near,this.far)},THREE.FogExp2=function(color,density){this.name="",this.color=new THREE.Color(color),this.density=void 0!==density?density:25e-5},THREE.FogExp2.prototype.clone=function(){return new THREE.FogExp2(this.color.getHex(),this.density)},THREE.ShaderChunk={},THREE.ShaderChunk.alphamap_fragment="#ifdef USE_ALPHAMAP\n\n diffuseColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n",THREE.ShaderChunk.alphamap_pars_fragment="#ifdef USE_ALPHAMAP\n\n uniform sampler2D alphaMap;\n\n#endif\n",THREE.ShaderChunk.alphatest_fragment="#ifdef ALPHATEST\n\n if ( diffuseColor.a < ALPHATEST ) discard;\n\n#endif\n",THREE.ShaderChunk.aomap_fragment="#ifdef USE_AOMAP\n\n totalAmbientLight *= ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\n#endif\n",THREE.ShaderChunk.aomap_pars_fragment="#ifdef USE_AOMAP\n\n uniform sampler2D aoMap;\n uniform float aoMapIntensity;\n\n#endif",THREE.ShaderChunk.begin_vertex="\nvec3 transformed = vec3( position );\n",THREE.ShaderChunk.beginnormal_vertex="\nvec3 objectNormal = vec3( normal );\n",THREE.ShaderChunk.bumpmap_pars_fragment="#ifdef USE_BUMPMAP\n\n uniform sampler2D bumpMap;\n uniform float bumpScale;\n\n\n\n vec2 dHdxy_fwd() {\n\n vec2 dSTdx = dFdx( vUv );\n vec2 dSTdy = dFdy( vUv );\n\n float Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n return vec2( dBx, dBy );\n\n }\n\n vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n vec3 vSigmaX = dFdx( surf_pos );\n vec3 vSigmaY = dFdy( surf_pos );\n vec3 vN = surf_norm;\n vec3 R1 = cross( vSigmaY, vN );\n vec3 R2 = cross( vN, vSigmaX );\n\n float fDet = dot( vSigmaX, R1 );\n\n vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n return normalize( abs( fDet ) * surf_norm - vGrad );\n\n }\n\n#endif\n",THREE.ShaderChunk.color_fragment="#ifdef USE_COLOR\n\n diffuseColor.rgb *= vColor;\n\n#endif",THREE.ShaderChunk.color_pars_fragment="#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif\n",THREE.ShaderChunk.color_pars_vertex="#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif",THREE.ShaderChunk.color_vertex="#ifdef USE_COLOR\n\n vColor.xyz = color.xyz;\n\n#endif",THREE.ShaderChunk.common="#define PI 3.14159\n#define PI2 6.28318\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\n\nvec3 transformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( matrix * vec4( normal, 0.0 ) ).xyz );\n\n}\n\nvec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );\n\n}\n\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n float distance = dot( planeNormal, point - pointOnPlane );\n\n return - distance * planeNormal + point;\n\n}\n\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return sign( dot( point - pointOnPlane, planeNormal ) );\n\n}\n\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n\n}\n\nfloat calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) {\n\n if ( decayExponent > 0.0 ) {\n\n return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\n }\n\n return 1.0;\n\n}\n\nvec3 F_Schlick( in vec3 specularColor, in float dotLH ) {\n\n\n float fresnel = exp2( ( -5.55437 * dotLH - 6.98316 ) * dotLH );\n\n return ( 1.0 - specularColor ) * fresnel + specularColor;\n\n}\n\nfloat G_BlinnPhong_Implicit( /* in float dotNL, in float dotNV */ ) {\n\n\n return 0.25;\n\n}\n\nfloat D_BlinnPhong( in float shininess, in float dotNH ) {\n\n\n return ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n\n}\n\nvec3 BRDF_BlinnPhong( in vec3 specularColor, in float shininess, in vec3 normal, in vec3 lightDir, in vec3 viewDir ) {\n\n vec3 halfDir = normalize( lightDir + viewDir );\n\n float dotNH = saturate( dot( normal, halfDir ) );\n float dotLH = saturate( dot( lightDir, halfDir ) );\n\n vec3 F = F_Schlick( specularColor, dotLH );\n\n float G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );\n\n float D = D_BlinnPhong( shininess, dotNH );\n\n return F * G * D;\n\n}\n\nvec3 inputToLinear( in vec3 a ) {\n\n #ifdef GAMMA_INPUT\n\n return pow( a, vec3( float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n\nvec3 linearToOutput( in vec3 a ) {\n\n #ifdef GAMMA_OUTPUT\n\n return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n",THREE.ShaderChunk.defaultnormal_vertex="#ifdef FLIP_SIDED\n\n objectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;\n",THREE.ShaderChunk.displacementmap_vertex="#ifdef USE_DISPLACEMENTMAP\n\n transformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n\n#endif\n",THREE.ShaderChunk.displacementmap_pars_vertex="#ifdef USE_DISPLACEMENTMAP\n\n uniform sampler2D displacementMap;\n uniform float displacementScale;\n uniform float displacementBias;\n\n#endif\n",THREE.ShaderChunk.emissivemap_fragment="#ifdef USE_EMISSIVEMAP\n\n vec4 emissiveColor = texture2D( emissiveMap, vUv );\n\n emissiveColor.rgb = inputToLinear( emissiveColor.rgb );\n\n totalEmissiveLight *= emissiveColor.rgb;\n\n#endif\n",THREE.ShaderChunk.emissivemap_pars_fragment="#ifdef USE_EMISSIVEMAP\n\n uniform sampler2D emissiveMap;\n\n#endif\n",THREE.ShaderChunk.envmap_fragment="#ifdef USE_ENVMAP\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n #else\n\n vec3 reflectVec = vReflect;\n\n #endif\n\n #ifdef DOUBLE_SIDED\n float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n #else\n float flipNormal = 1.0;\n #endif\n\n #ifdef ENVMAP_TYPE_CUBE\n vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #elif defined( ENVMAP_TYPE_EQUIREC )\n vec2 sampleUV;\n sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n vec4 envColor = texture2D( envMap, sampleUV );\n\n #elif defined( ENVMAP_TYPE_SPHERE )\n vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n #endif\n\n envColor.xyz = inputToLinear( envColor.xyz );\n\n #ifdef ENVMAP_BLENDING_MULTIPLY\n\n outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_MIX )\n\n outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_ADD )\n\n outgoingLight += envColor.xyz * specularStrength * reflectivity;\n\n #endif\n\n#endif\n",THREE.ShaderChunk.envmap_pars_fragment="#ifdef USE_ENVMAP\n\n uniform float reflectivity;\n #ifdef ENVMAP_TYPE_CUBE\n uniform samplerCube envMap;\n #else\n uniform sampler2D envMap;\n #endif\n uniform float flipEnvMap;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n uniform float refractionRatio;\n\n #else\n\n varying vec3 vReflect;\n\n #endif\n\n#endif\n",THREE.ShaderChunk.envmap_pars_vertex="#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n varying vec3 vReflect;\n\n uniform float refractionRatio;\n\n#endif\n",THREE.ShaderChunk.envmap_vertex="#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vReflect = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n#endif\n",THREE.ShaderChunk.fog_fragment="#ifdef USE_FOG\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n float depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n #else\n\n float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n #endif\n\n #ifdef FOG_EXP2\n\n float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\n\n #else\n\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n\n #endif\n \n outgoingLight = mix( outgoingLight, fogColor, fogFactor );\n\n#endif",THREE.ShaderChunk.fog_pars_fragment="#ifdef USE_FOG\n\n uniform vec3 fogColor;\n\n #ifdef FOG_EXP2\n\n uniform float fogDensity;\n\n #else\n\n uniform float fogNear;\n uniform float fogFar;\n #endif\n\n#endif",THREE.ShaderChunk.hemilight_fragment="#if MAX_HEMI_LIGHTS > 0\n\n for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lightDir = hemisphereLightDirection[ i ];\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vec3 lightColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n totalAmbientLight += lightColor;\n\n }\n\n#endif\n\n", +THREE.ShaderChunk.lightmap_fragment="#ifdef USE_LIGHTMAP\n\n totalAmbientLight += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\n#endif\n",THREE.ShaderChunk.lightmap_pars_fragment="#ifdef USE_LIGHTMAP\n\n uniform sampler2D lightMap;\n uniform float lightMapIntensity;\n\n#endif",THREE.ShaderChunk.lights_lambert_pars_vertex="#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n",THREE.ShaderChunk.lights_lambert_vertex="vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vec3( 0.0 );\n\n#endif\n\nvec3 normal = normalize( transformedNormal );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lVector = pointLightPosition[ i ] - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n #endif\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n #endif\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * saturate( - dotProduct );\n\n #endif\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lightDir = hemisphereLightDirection[ i ];\n\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n #ifdef DOUBLE_SIDED\n\n float hemiDiffuseWeightBack = - 0.5 * dotProduct + 0.5;\n\n vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n #endif\n\n }\n\n#endif\n",THREE.ShaderChunk.lights_phong_fragment="vec3 viewDir = normalize( vViewPosition );\n\nvec3 totalDiffuseLight = vec3( 0.0 );\nvec3 totalSpecularLight = vec3( 0.0 );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lightPosition = pointLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * cosineTerm;\n\n }\n\n#endif\n",THREE.ShaderChunk.lights_phong_pars_fragment="uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\nvarying vec3 vViewPosition;\n\n#ifndef FLAT_SHADED\n\n varying vec3 vNormal;\n\n#endif\n",THREE.ShaderChunk.lights_phong_pars_vertex="#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\n#endif\n",THREE.ShaderChunk.lights_phong_vertex="#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n vWorldPosition = worldPosition.xyz;\n\n#endif\n",THREE.ShaderChunk.linear_to_gamma_fragment="\n outgoingLight = linearToOutput( outgoingLight );\n",THREE.ShaderChunk.logdepthbuf_fragment="#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif",THREE.ShaderChunk.logdepthbuf_pars_fragment="#ifdef USE_LOGDEPTHBUF\n\n uniform float logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n#endif\n",THREE.ShaderChunk.logdepthbuf_pars_vertex="#ifdef USE_LOGDEPTHBUF\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n uniform float logDepthBufFC;\n\n#endif",THREE.ShaderChunk.logdepthbuf_vertex="#ifdef USE_LOGDEPTHBUF\n\n gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n vFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n #endif\n\n#endif",THREE.ShaderChunk.map_fragment="#ifdef USE_MAP\n\n vec4 texelColor = texture2D( map, vUv );\n\n texelColor.xyz = inputToLinear( texelColor.xyz );\n\n diffuseColor *= texelColor;\n\n#endif\n",THREE.ShaderChunk.map_pars_fragment="#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif",THREE.ShaderChunk.map_particle_fragment="#ifdef USE_MAP\n\n diffuseColor *= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\n#endif\n",THREE.ShaderChunk.map_particle_pars_fragment="#ifdef USE_MAP\n\n uniform vec4 offsetRepeat;\n uniform sampler2D map;\n\n#endif\n",THREE.ShaderChunk.morphnormal_vertex="#ifdef USE_MORPHNORMALS\n\n objectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n objectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n objectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n objectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n#endif\n",THREE.ShaderChunk.morphtarget_pars_vertex="#ifdef USE_MORPHTARGETS\n\n #ifndef USE_MORPHNORMALS\n\n uniform float morphTargetInfluences[ 8 ];\n\n #else\n\n uniform float morphTargetInfluences[ 4 ];\n\n #endif\n\n#endif",THREE.ShaderChunk.morphtarget_vertex="#ifdef USE_MORPHTARGETS\n\n transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n #ifndef USE_MORPHNORMALS\n\n transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n #endif\n\n#endif\n",THREE.ShaderChunk.normal_phong_fragment="#ifndef FLAT_SHADED\n\n vec3 normal = normalize( vNormal );\n\n #ifdef DOUBLE_SIDED\n\n normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n #endif\n\n#else\n\n vec3 fdx = dFdx( vViewPosition );\n vec3 fdy = dFdy( vViewPosition );\n vec3 normal = normalize( cross( fdx, fdy ) );\n\n#endif\n\n#ifdef USE_NORMALMAP\n\n normal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\n",THREE.ShaderChunk.normalmap_pars_fragment="#ifdef USE_NORMALMAP\n\n uniform sampler2D normalMap;\n uniform vec2 normalScale;\n\n\n vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n vec3 q0 = dFdx( eye_pos.xyz );\n vec3 q1 = dFdy( eye_pos.xyz );\n vec2 st0 = dFdx( vUv.st );\n vec2 st1 = dFdy( vUv.st );\n\n vec3 S = normalize( q0 * st1.t - q1 * st0.t );\n vec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n vec3 N = normalize( surf_norm );\n\n vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n mapN.xy = normalScale * mapN.xy;\n mat3 tsn = mat3( S, T, N );\n return normalize( tsn * mapN );\n\n }\n\n#endif\n",THREE.ShaderChunk.project_vertex="#ifdef USE_SKINNING\n\n vec4 mvPosition = modelViewMatrix * skinned;\n\n#else\n\n vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;\n",THREE.ShaderChunk.shadowmap_fragment="#ifdef USE_SHADOWMAP\n\n for ( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n float texelSizeY = 1.0 / shadowMapSize[ i ].y;\n\n float shadow = 0.0;\n\n#if defined( POINT_LIGHT_SHADOWS )\n\n bool isPointLight = shadowDarkness[ i ] < 0.0;\n\n if ( isPointLight ) {\n\n float realShadowDarkness = abs( shadowDarkness[ i ] );\n\n vec3 lightToPosition = vShadowCoord[ i ].xyz;\n\n #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n vec3 bd3D = normalize( lightToPosition );\n float dp = length( lightToPosition );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D, texelSizeY ) ), shadowBias[ i ], shadow );\n\n\n #if defined( SHADOWMAP_TYPE_PCF )\n const float Dr = 1.25;\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n const float Dr = 2.25;\n #endif\n\n float os = Dr * 2.0 * texelSizeY;\n\n const vec3 Gsd = vec3( - 1, 0, 1 );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zyz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xyz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zyx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xyx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n\n shadow *= realShadowDarkness * ( 1.0 / 21.0 );\n\n #else \n vec3 bd3D = normalize( lightToPosition );\n float dp = length( lightToPosition );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D, texelSizeY ) ), shadowBias[ i ], shadow );\n\n shadow *= realShadowDarkness;\n\n #endif\n\n } else {\n\n#endif \n float texelSizeX = 1.0 / shadowMapSize[ i ].x;\n\n vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n\n bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n bool inFrustum = all( inFrustumVec );\n\n bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n bool frustumTest = all( frustumTestVec );\n\n if ( frustumTest ) {\n\n #if defined( SHADOWMAP_TYPE_PCF )\n\n\n /*\n for ( float y = -1.25; y <= 1.25; y += 1.25 )\n for ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n if ( fDepth < shadowCoord.z )\n shadow += 1.0;\n }\n shadow /= 9.0;\n */\n\n shadowCoord.z += shadowBias[ i ];\n\n const float ShadowDelta = 1.0 / 9.0;\n\n float xPixelOffset = texelSizeX;\n float yPixelOffset = texelSizeY;\n\n float dx0 = - 1.25 * xPixelOffset;\n float dy0 = - 1.25 * yPixelOffset;\n float dx1 = 1.25 * xPixelOffset;\n float dy1 = 1.25 * yPixelOffset;\n\n float fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n shadow *= shadowDarkness[ i ];\n\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n\n shadowCoord.z += shadowBias[ i ];\n\n float xPixelOffset = texelSizeX;\n float yPixelOffset = texelSizeY;\n\n float dx0 = - 1.0 * xPixelOffset;\n float dy0 = - 1.0 * yPixelOffset;\n float dx1 = 1.0 * xPixelOffset;\n float dy1 = 1.0 * yPixelOffset;\n\n mat3 shadowKernel;\n mat3 depthKernel;\n\n depthKernel[ 0 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n depthKernel[ 0 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n depthKernel[ 0 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n depthKernel[ 1 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n depthKernel[ 1 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n depthKernel[ 1 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n depthKernel[ 2 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n depthKernel[ 2 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n depthKernel[ 2 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n vec3 shadowZ = vec3( shadowCoord.z );\n shadowKernel[ 0 ] = vec3( lessThan( depthKernel[ 0 ], shadowZ ) );\n shadowKernel[ 0 ] *= vec3( 0.25 );\n\n shadowKernel[ 1 ] = vec3( lessThan( depthKernel[ 1 ], shadowZ ) );\n shadowKernel[ 1 ] *= vec3( 0.25 );\n\n shadowKernel[ 2 ] = vec3( lessThan( depthKernel[ 2 ], shadowZ ) );\n shadowKernel[ 2 ] *= vec3( 0.25 );\n\n vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[ i ].xy );\n\n shadowKernel[ 0 ] = mix( shadowKernel[ 1 ], shadowKernel[ 0 ], fractionalCoord.x );\n shadowKernel[ 1 ] = mix( shadowKernel[ 2 ], shadowKernel[ 1 ], fractionalCoord.x );\n\n vec4 shadowValues;\n shadowValues.x = mix( shadowKernel[ 0 ][ 1 ], shadowKernel[ 0 ][ 0 ], fractionalCoord.y );\n shadowValues.y = mix( shadowKernel[ 0 ][ 2 ], shadowKernel[ 0 ][ 1 ], fractionalCoord.y );\n shadowValues.z = mix( shadowKernel[ 1 ][ 1 ], shadowKernel[ 1 ][ 0 ], fractionalCoord.y );\n shadowValues.w = mix( shadowKernel[ 1 ][ 2 ], shadowKernel[ 1 ][ 1 ], fractionalCoord.y );\n\n shadow = dot( shadowValues, vec4( 1.0 ) ) * shadowDarkness[ i ];\n\n #else \n shadowCoord.z += shadowBias[ i ];\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n shadow = shadowDarkness[ i ];\n\n #endif\n\n }\n\n#ifdef SHADOWMAP_DEBUG\n\n if ( inFrustum ) {\n\n if ( i == 0 ) {\n\n outgoingLight *= vec3( 1.0, 0.5, 0.0 );\n\n } else if ( i == 1 ) {\n\n outgoingLight *= vec3( 0.0, 1.0, 0.8 );\n\n } else {\n\n outgoingLight *= vec3( 0.0, 0.5, 1.0 );\n\n }\n\n }\n\n#endif\n\n#if defined( POINT_LIGHT_SHADOWS )\n\n }\n\n#endif\n\n shadowMask = shadowMask * vec3( 1.0 - shadow );\n\n }\n\n#endif\n",THREE.ShaderChunk.shadowmap_pars_fragment="#ifdef USE_SHADOWMAP\n\n uniform sampler2D shadowMap[ MAX_SHADOWS ];\n uniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform float shadowBias[ MAX_SHADOWS ];\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n float unpackDepth( const in vec4 rgba_depth ) {\n\n const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n float depth = dot( rgba_depth, bit_shift );\n return depth;\n\n }\n\n #if defined(POINT_LIGHT_SHADOWS)\n\n\n void adjustShadowValue1K( const float testDepth, const vec4 textureData, const float bias, inout float shadowValue ) {\n\n const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n if ( testDepth >= dot( textureData, bitSh ) * 1000.0 + bias )\n shadowValue += 1.0;\n\n }\n\n\n vec2 cubeToUV( vec3 v, float texelSizeY ) {\n\n\n vec3 absV = abs( v );\n\n\n float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n absV *= scaleToCube;\n\n\n v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\n\n\n vec2 planar = v.xy;\n\n float almostATexel = 1.5 * texelSizeY;\n float almostOne = 1.0 - almostATexel;\n\n if ( absV.z >= almostOne ) {\n\n if ( v.z > 0.0 )\n planar.x = 4.0 - v.x;\n\n } else if ( absV.x >= almostOne ) {\n\n float signX = sign( v.x );\n planar.x = v.z * signX + 2.0 * signX;\n\n } else if ( absV.y >= almostOne ) {\n\n float signY = sign( v.y );\n planar.x = v.x + 2.0 * signY + 2.0;\n planar.y = v.z * signY - 2.0;\n\n }\n\n\n return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\n }\n\n #endif\n\n#endif\n",THREE.ShaderChunk.shadowmap_pars_vertex="#ifdef USE_SHADOWMAP\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform mat4 shadowMatrix[ MAX_SHADOWS ];\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n#endif",THREE.ShaderChunk.shadowmap_vertex="#ifdef USE_SHADOWMAP\n\n for ( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n }\n\n#endif",THREE.ShaderChunk.skinbase_vertex="#ifdef USE_SKINNING\n\n mat4 boneMatX = getBoneMatrix( skinIndex.x );\n mat4 boneMatY = getBoneMatrix( skinIndex.y );\n mat4 boneMatZ = getBoneMatrix( skinIndex.z );\n mat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif",THREE.ShaderChunk.skinning_pars_vertex="#ifdef USE_SKINNING\n\n uniform mat4 bindMatrix;\n uniform mat4 bindMatrixInverse;\n\n #ifdef BONE_TEXTURE\n\n uniform sampler2D boneTexture;\n uniform int boneTextureWidth;\n uniform int boneTextureHeight;\n\n mat4 getBoneMatrix( const in float i ) {\n\n float j = i * 4.0;\n float x = mod( j, float( boneTextureWidth ) );\n float y = floor( j / float( boneTextureWidth ) );\n\n float dx = 1.0 / float( boneTextureWidth );\n float dy = 1.0 / float( boneTextureHeight );\n\n y = dy * ( y + 0.5 );\n\n vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n mat4 bone = mat4( v1, v2, v3, v4 );\n\n return bone;\n\n }\n\n #else\n\n uniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n mat4 getBoneMatrix( const in float i ) {\n\n mat4 bone = boneGlobalMatrices[ int(i) ];\n return bone;\n\n }\n\n #endif\n\n#endif\n",THREE.ShaderChunk.skinning_vertex="#ifdef USE_SKINNING\n\n vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\n vec4 skinned = vec4( 0.0 );\n skinned += boneMatX * skinVertex * skinWeight.x;\n skinned += boneMatY * skinVertex * skinWeight.y;\n skinned += boneMatZ * skinVertex * skinWeight.z;\n skinned += boneMatW * skinVertex * skinWeight.w;\n skinned = bindMatrixInverse * skinned;\n\n#endif\n",THREE.ShaderChunk.skinnormal_vertex="#ifdef USE_SKINNING\n\n mat4 skinMatrix = mat4( 0.0 );\n skinMatrix += skinWeight.x * boneMatX;\n skinMatrix += skinWeight.y * boneMatY;\n skinMatrix += skinWeight.z * boneMatZ;\n skinMatrix += skinWeight.w * boneMatW;\n skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\n objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\n#endif\n",THREE.ShaderChunk.specularmap_fragment="float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n vec4 texelSpecular = texture2D( specularMap, vUv );\n specularStrength = texelSpecular.r;\n\n#else\n\n specularStrength = 1.0;\n\n#endif",THREE.ShaderChunk.specularmap_pars_fragment="#ifdef USE_SPECULARMAP\n\n uniform sampler2D specularMap;\n\n#endif",THREE.ShaderChunk.uv2_pars_fragment="#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n varying vec2 vUv2;\n\n#endif",THREE.ShaderChunk.uv2_pars_vertex="#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n attribute vec2 uv2;\n varying vec2 vUv2;\n\n#endif",THREE.ShaderChunk.uv2_vertex="#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n vUv2 = uv2;\n\n#endif",THREE.ShaderChunk.uv_pars_fragment="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n\n#endif",THREE.ShaderChunk.uv_pars_vertex="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n uniform vec4 offsetRepeat;\n\n#endif\n",THREE.ShaderChunk.uv_vertex="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif",THREE.ShaderChunk.worldpos_vertex="#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n #ifdef USE_SKINNING\n\n vec4 worldPosition = modelMatrix * skinned;\n\n #else\n\n vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n\n #endif\n\n#endif\n",THREE.UniformsUtils={merge:function(uniforms){for(var merged={},u=0;u dashSize ) {"," discard;"," }"," vec3 outgoingLight = vec3( 0.0 );"," vec4 diffuseColor = vec4( diffuse, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.color_fragment," outgoingLight = diffuseColor.rgb;",THREE.ShaderChunk.fog_fragment," gl_FragColor = vec4( outgoingLight, diffuseColor.a );","}"].join("\n")},depth:{uniforms:{mNear:{type:"f",value:1},mFar:{type:"f",value:2e3},opacity:{type:"f",value:1}},vertexShader:[THREE.ShaderChunk.common,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.begin_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.project_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform float mNear;","uniform float mFar;","uniform float opacity;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {",THREE.ShaderChunk.logdepthbuf_fragment," #ifdef USE_LOGDEPTHBUF_EXT"," float depth = gl_FragDepthEXT / gl_FragCoord.w;"," #else"," float depth = gl_FragCoord.z / gl_FragCoord.w;"," #endif"," float color = 1.0 - smoothstep( mNear, mFar, depth );"," gl_FragColor = vec4( vec3( color ), opacity );","}"].join("\n")},normal:{uniforms:{opacity:{type:"f",value:1}},vertexShader:["varying vec3 vNormal;",THREE.ShaderChunk.common,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {"," vNormal = normalize( normalMatrix * normal );",THREE.ShaderChunk.begin_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.project_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform float opacity;","varying vec3 vNormal;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {"," gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},cube:{uniforms:{tCube:{type:"t",value:null},tFlip:{type:"f",value:-1}},vertexShader:["varying vec3 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {"," vWorldPosition = transformDirection( position, modelMatrix );"," gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform samplerCube tCube;","uniform float tFlip;","varying vec3 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {"," gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},equirect:{uniforms:{tEquirect:{type:"t",value:null},tFlip:{type:"f",value:-1}},vertexShader:["varying vec3 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {"," vWorldPosition = transformDirection( position, modelMatrix );"," gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform sampler2D tEquirect;","uniform float tFlip;","varying vec3 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {","vec3 direction = normalize( vWorldPosition );","vec2 sampleUV;","sampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );","sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;","gl_FragColor = texture2D( tEquirect, sampleUV );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},depthRGBA:{uniforms:{},vertexShader:[THREE.ShaderChunk.common,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.begin_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.project_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:[THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"vec4 pack_depth( const in float depth ) {"," const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );"," const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );"," vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );"," res -= res.xxyz * bit_mask;"," return res;","}","void main() {",THREE.ShaderChunk.logdepthbuf_fragment," #ifdef USE_LOGDEPTHBUF_EXT"," gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );"," #else"," gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );"," #endif","}"].join("\n")},distanceRGBA:{uniforms:{lightPos:{type:"v3",value:new THREE.Vector3(0,0,0)}},vertexShader:["varying vec4 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.begin_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.project_vertex,THREE.ShaderChunk.worldpos_vertex,"vWorldPosition = worldPosition;","}"].join("\n"),fragmentShader:["uniform vec3 lightPos;","varying vec4 vWorldPosition;",THREE.ShaderChunk.common,"vec4 pack1K ( float depth ) {"," depth /= 1000.0;"," const vec4 bitSh = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );"," const vec4 bitMsk = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );"," vec4 res = fract( depth * bitSh );"," res -= res.xxyz * bitMsk;"," return res; ","}","float unpack1K ( vec4 color ) {"," const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );"," return dot( color, bitSh ) * 1000.0;","}","void main () {"," gl_FragColor = pack1K( length( vWorldPosition.xyz - lightPos.xyz ) );","}"].join("\n")}},THREE.WebGLRenderer=function(parameters){function glClearColor(r,g,b,a){_premultipliedAlpha===!0&&(r*=a,g*=a,b*=a),_gl.clearColor(r,g,b,a)}function setDefaultGLState(){state.init(),_gl.viewport(_viewportX,_viewportY,_viewportWidth,_viewportHeight),glClearColor(_clearColor.r,_clearColor.g,_clearColor.b,_clearAlpha)}function resetGLState(){_currentProgram=null,_currentCamera=null,_currentGeometryProgram="",_currentMaterialId=-1,_lightsNeedUpdate=!0,state.reset()}function onContextLost(event){event.preventDefault(),resetGLState(),setDefaultGLState(),properties.clear()}function onTextureDispose(event){var texture=event.target;texture.removeEventListener("dispose",onTextureDispose),deallocateTexture(texture),_infoMemory.textures--}function onRenderTargetDispose(event){var renderTarget=event.target;renderTarget.removeEventListener("dispose",onRenderTargetDispose),deallocateRenderTarget(renderTarget),_infoMemory.textures--}function onMaterialDispose(event){var material=event.target;material.removeEventListener("dispose",onMaterialDispose),deallocateMaterial(material)}function deallocateTexture(texture){var textureProperties=properties.get(texture);if(texture.image&&textureProperties.__image__webglTextureCube)_gl.deleteTexture(textureProperties.__image__webglTextureCube);else{if(void 0===textureProperties.__webglInit)return;_gl.deleteTexture(textureProperties.__webglTexture)}properties["delete"](texture)}function deallocateRenderTarget(renderTarget){var renderTargetProperties=properties.get(renderTarget),textureProperties=properties.get(renderTarget.texture);if(renderTarget&&void 0!==textureProperties.__webglTexture){if(_gl.deleteTexture(textureProperties.__webglTexture),renderTarget instanceof THREE.WebGLRenderTargetCube)for(var i=0;6>i;i++)_gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i]),_gl.deleteRenderbuffer(renderTargetProperties.__webglRenderbuffer[i]);else _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer),_gl.deleteRenderbuffer(renderTargetProperties.__webglRenderbuffer);properties["delete"](renderTarget.texture),properties["delete"](renderTarget)}}function deallocateMaterial(material){releaseMaterialProgramReference(material),properties["delete"](material)}function releaseMaterialProgramReference(material){var programInfo=properties.get(material).program;material.program=void 0,void 0!==programInfo&&programCache.releaseProgram(programInfo)}function setupVertexAttributes(material,program,geometry,startIndex){var extension;if(geometry instanceof THREE.InstancedBufferGeometry&&(extension=extensions.get("ANGLE_instanced_arrays"),null===extension))return void console.error("THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.");void 0===startIndex&&(startIndex=0),state.initAttributes();var geometryAttributes=geometry.attributes,programAttributes=program.getAttributes(),materialDefaultAttributeValues=material.defaultAttributeValues;for(var name in programAttributes){var programAttribute=programAttributes[name];if(programAttribute>=0){var geometryAttribute=geometryAttributes[name];if(void 0!==geometryAttribute){var size=geometryAttribute.itemSize,buffer=objects.getAttributeBuffer(geometryAttribute);if(geometryAttribute instanceof THREE.InterleavedBufferAttribute){var data=geometryAttribute.data,stride=data.stride,offset=geometryAttribute.offset;data instanceof THREE.InstancedInterleavedBuffer?(state.enableAttributeAndDivisor(programAttribute,data.meshPerAttribute,extension),void 0===geometry.maxInstancedCount&&(geometry.maxInstancedCount=data.meshPerAttribute*data.count)):state.enableAttribute(programAttribute),_gl.bindBuffer(_gl.ARRAY_BUFFER,buffer),_gl.vertexAttribPointer(programAttribute,size,_gl.FLOAT,!1,stride*data.array.BYTES_PER_ELEMENT,(startIndex*stride+offset)*data.array.BYTES_PER_ELEMENT)}else geometryAttribute instanceof THREE.InstancedBufferAttribute?(state.enableAttributeAndDivisor(programAttribute,geometryAttribute.meshPerAttribute,extension),void 0===geometry.maxInstancedCount&&(geometry.maxInstancedCount=geometryAttribute.meshPerAttribute*geometryAttribute.count)):state.enableAttribute(programAttribute),_gl.bindBuffer(_gl.ARRAY_BUFFER,buffer),_gl.vertexAttribPointer(programAttribute,size,_gl.FLOAT,!1,0,startIndex*size*4)}else if(void 0!==materialDefaultAttributeValues){var value=materialDefaultAttributeValues[name];if(void 0!==value)switch(value.length){case 2:_gl.vertexAttrib2fv(programAttribute,value);break;case 3:_gl.vertexAttrib3fv(programAttribute,value);break;case 4:_gl.vertexAttrib4fv(programAttribute,value);break;default:_gl.vertexAttrib1fv(programAttribute,value)}}}}state.disableUnusedAttributes()}function numericalSort(a,b){return b[0]-a[0]}function painterSortStable(a,b){return a.object.renderOrder!==b.object.renderOrder?a.object.renderOrder-b.object.renderOrder:a.material.id!==b.material.id?a.material.id-b.material.id:a.z!==b.z?a.z-b.z:a.id-b.id}function reversePainterSortStable(a,b){return a.object.renderOrder!==b.object.renderOrder?a.object.renderOrder-b.object.renderOrder:a.z!==b.z?b.z-a.z:a.id-b.id}function pushRenderItem(object,geometry,material,z,group){var array,index;material.transparent?(array=transparentObjects,index=++transparentObjectsLastIndex):(array=opaqueObjects,index=++opaqueObjectsLastIndex);var renderItem=array[index];void 0!==renderItem?(renderItem.id=object.id,renderItem.object=object,renderItem.geometry=geometry,renderItem.material=material,renderItem.z=_vector3.z,renderItem.group=group):(renderItem={id:object.id,object:object,geometry:geometry,material:material,z:_vector3.z,group:group},array.push(renderItem))}function projectObject(object,camera){if(object.visible!==!1){if(0!==(object.channels.mask&camera.channels.mask))if(object instanceof THREE.Light)lights.push(object);else if(object instanceof THREE.Sprite)sprites.push(object);else if(object instanceof THREE.LensFlare)lensFlares.push(object);else if(object instanceof THREE.ImmediateRenderObject)_this.sortObjects===!0&&(_vector3.setFromMatrixPosition(object.matrixWorld),_vector3.applyProjection(_projScreenMatrix)),pushRenderItem(object,null,object.material,_vector3.z,null);else if((object instanceof THREE.Mesh||object instanceof THREE.Line||object instanceof THREE.Points)&&(object instanceof THREE.SkinnedMesh&&object.skeleton.update(),object.frustumCulled===!1||_frustum.intersectsObject(object)===!0)){var material=object.material;if(material.visible===!0){_this.sortObjects===!0&&(_vector3.setFromMatrixPosition(object.matrixWorld),_vector3.applyProjection(_projScreenMatrix));var geometry=objects.update(object);if(material instanceof THREE.MeshFaceMaterial)for(var groups=geometry.groups,materials=material.materials,i=0,l=groups.length;l>i;i++){var group=groups[i],groupMaterial=materials[group.materialIndex];groupMaterial.visible===!0&&pushRenderItem(object,geometry,groupMaterial,_vector3.z,group)}else pushRenderItem(object,geometry,material,_vector3.z,null)}}for(var children=object.children,i=0,l=children.length;l>i;i++)projectObject(children[i],camera)}}function renderObjects(renderList,camera,lights,fog,overrideMaterial){for(var i=0,l=renderList.length;l>i;i++){var renderItem=renderList[i],object=renderItem.object,geometry=renderItem.geometry,material=void 0===overrideMaterial?renderItem.material:overrideMaterial,group=renderItem.group;if(object.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse,object.matrixWorld),object.normalMatrix.getNormalMatrix(object.modelViewMatrix),object instanceof THREE.ImmediateRenderObject){setMaterial(material);var program=setProgram(camera,lights,fog,material,object);_currentGeometryProgram="",object.render(function(object){_this.renderBufferImmediate(object,program,material)})}else _this.renderBufferDirect(camera,lights,fog,geometry,material,object,group)}}function initMaterial(material,lights,fog,object){var materialProperties=properties.get(material),parameters=programCache.getParameters(material,lights,fog,object),code=programCache.getProgramCode(material,parameters),program=materialProperties.program,programChange=!0;if(void 0===program)material.addEventListener("dispose",onMaterialDispose);else if(program.code!==code)releaseMaterialProgramReference(material);else{if(void 0!==parameters.shaderID)return;programChange=!1}if(programChange){if(parameters.shaderID){var shader=THREE.ShaderLib[parameters.shaderID];materialProperties.__webglShader={name:material.type,uniforms:THREE.UniformsUtils.clone(shader.uniforms),vertexShader:shader.vertexShader,fragmentShader:shader.fragmentShader}}else materialProperties.__webglShader={name:material.type,uniforms:material.uniforms,vertexShader:material.vertexShader,fragmentShader:material.fragmentShader};material.__webglShader=materialProperties.__webglShader,program=programCache.acquireProgram(material,parameters,code),materialProperties.program=program,material.program=program}var attributes=program.getAttributes();if(material.morphTargets){material.numSupportedMorphTargets=0;for(var i=0;i<_this.maxMorphTargets;i++)attributes["morphTarget"+i]>=0&&material.numSupportedMorphTargets++}if(material.morphNormals)for(material.numSupportedMorphNormals=0,i=0;i<_this.maxMorphNormals;i++)attributes["morphNormal"+i]>=0&&material.numSupportedMorphNormals++;materialProperties.uniformsList=[];var uniformLocations=materialProperties.program.getUniforms();for(var u in materialProperties.__webglShader.uniforms){var location=uniformLocations[u];location&&materialProperties.uniformsList.push([materialProperties.__webglShader.uniforms[u],location])}}function setMaterial(material){setMaterialFaces(material),material.transparent===!0?state.setBlending(material.blending,material.blendEquation,material.blendSrc,material.blendDst,material.blendEquationAlpha,material.blendSrcAlpha,material.blendDstAlpha):state.setBlending(THREE.NoBlending),state.setDepthFunc(material.depthFunc),state.setDepthTest(material.depthTest),state.setDepthWrite(material.depthWrite),state.setColorWrite(material.colorWrite),state.setPolygonOffset(material.polygonOffset,material.polygonOffsetFactor,material.polygonOffsetUnits)}function setMaterialFaces(material){material.side!==THREE.DoubleSide?state.enable(_gl.CULL_FACE):state.disable(_gl.CULL_FACE),state.setFlipSided(material.side===THREE.BackSide)}function setProgram(camera,lights,fog,material,object){_usedTextureUnits=0;var materialProperties=properties.get(material);!material.needsUpdate&&materialProperties.program||(initMaterial(material,lights,fog,object),material.needsUpdate=!1);var refreshProgram=!1,refreshMaterial=!1,refreshLights=!1,program=materialProperties.program,p_uniforms=program.getUniforms(),m_uniforms=materialProperties.__webglShader.uniforms;if(program.id!==_currentProgram&&(_gl.useProgram(program.program),_currentProgram=program.id,refreshProgram=!0,refreshMaterial=!0,refreshLights=!0),material.id!==_currentMaterialId&&(-1===_currentMaterialId&&(refreshLights=!0),_currentMaterialId=material.id,refreshMaterial=!0),(refreshProgram||camera!==_currentCamera)&&(_gl.uniformMatrix4fv(p_uniforms.projectionMatrix,!1,camera.projectionMatrix.elements),capabilities.logarithmicDepthBuffer&&_gl.uniform1f(p_uniforms.logDepthBufFC,2/(Math.log(camera.far+1)/Math.LN2)),camera!==_currentCamera&&(_currentCamera=camera),(material instanceof THREE.ShaderMaterial||material instanceof THREE.MeshPhongMaterial||material.envMap)&&void 0!==p_uniforms.cameraPosition&&(_vector3.setFromMatrixPosition(camera.matrixWorld),_gl.uniform3f(p_uniforms.cameraPosition,_vector3.x,_vector3.y,_vector3.z)),(material instanceof THREE.MeshPhongMaterial||material instanceof THREE.MeshLambertMaterial||material instanceof THREE.MeshBasicMaterial||material instanceof THREE.ShaderMaterial||material.skinning)&&void 0!==p_uniforms.viewMatrix&&_gl.uniformMatrix4fv(p_uniforms.viewMatrix,!1,camera.matrixWorldInverse.elements)),material.skinning)if(object.bindMatrix&&void 0!==p_uniforms.bindMatrix&&_gl.uniformMatrix4fv(p_uniforms.bindMatrix,!1,object.bindMatrix.elements),object.bindMatrixInverse&&void 0!==p_uniforms.bindMatrixInverse&&_gl.uniformMatrix4fv(p_uniforms.bindMatrixInverse,!1,object.bindMatrixInverse.elements),capabilities.floatVertexTextures&&object.skeleton&&object.skeleton.useVertexTexture){if(void 0!==p_uniforms.boneTexture){var textureUnit=getTextureUnit();_gl.uniform1i(p_uniforms.boneTexture,textureUnit),_this.setTexture(object.skeleton.boneTexture,textureUnit)}void 0!==p_uniforms.boneTextureWidth&&_gl.uniform1i(p_uniforms.boneTextureWidth,object.skeleton.boneTextureWidth),void 0!==p_uniforms.boneTextureHeight&&_gl.uniform1i(p_uniforms.boneTextureHeight,object.skeleton.boneTextureHeight)}else object.skeleton&&object.skeleton.boneMatrices&&void 0!==p_uniforms.boneGlobalMatrices&&_gl.uniformMatrix4fv(p_uniforms.boneGlobalMatrices,!1,object.skeleton.boneMatrices);return refreshMaterial&&(fog&&material.fog&&refreshUniformsFog(m_uniforms,fog),(material instanceof THREE.MeshPhongMaterial||material instanceof THREE.MeshLambertMaterial||material.lights)&&(_lightsNeedUpdate&&(refreshLights=!0,setupLights(lights,camera),_lightsNeedUpdate=!1),refreshLights?(refreshUniformsLights(m_uniforms,_lights),markUniformsLightsNeedsUpdate(m_uniforms,!0)):markUniformsLightsNeedsUpdate(m_uniforms,!1)),(material instanceof THREE.MeshBasicMaterial||material instanceof THREE.MeshLambertMaterial||material instanceof THREE.MeshPhongMaterial)&&refreshUniformsCommon(m_uniforms,material),material instanceof THREE.LineBasicMaterial?refreshUniformsLine(m_uniforms,material):material instanceof THREE.LineDashedMaterial?(refreshUniformsLine(m_uniforms,material),refreshUniformsDash(m_uniforms,material)):material instanceof THREE.PointsMaterial?refreshUniformsParticle(m_uniforms,material):material instanceof THREE.MeshPhongMaterial?refreshUniformsPhong(m_uniforms,material):material instanceof THREE.MeshDepthMaterial?(m_uniforms.mNear.value=camera.near,m_uniforms.mFar.value=camera.far,m_uniforms.opacity.value=material.opacity):material instanceof THREE.MeshNormalMaterial&&(m_uniforms.opacity.value=material.opacity),object.receiveShadow&&!material._shadowPass&&refreshUniformsShadow(m_uniforms,lights,camera),loadUniformsGeneric(materialProperties.uniformsList)),loadUniformsMatrices(p_uniforms,object),void 0!==p_uniforms.modelMatrix&&_gl.uniformMatrix4fv(p_uniforms.modelMatrix,!1,object.matrixWorld.elements),program}function refreshUniformsCommon(uniforms,material){uniforms.opacity.value=material.opacity,uniforms.diffuse.value=material.color,material.emissive&&(uniforms.emissive.value=material.emissive),uniforms.map.value=material.map,uniforms.specularMap.value=material.specularMap,uniforms.alphaMap.value=material.alphaMap,material.aoMap&&(uniforms.aoMap.value=material.aoMap,uniforms.aoMapIntensity.value=material.aoMapIntensity);var uvScaleMap;if(material.map?uvScaleMap=material.map:material.specularMap?uvScaleMap=material.specularMap:material.displacementMap?uvScaleMap=material.displacementMap:material.normalMap?uvScaleMap=material.normalMap:material.bumpMap?uvScaleMap=material.bumpMap:material.alphaMap?uvScaleMap=material.alphaMap:material.emissiveMap&&(uvScaleMap=material.emissiveMap),void 0!==uvScaleMap){uvScaleMap instanceof THREE.WebGLRenderTarget&&(uvScaleMap=uvScaleMap.texture);var offset=uvScaleMap.offset,repeat=uvScaleMap.repeat;uniforms.offsetRepeat.value.set(offset.x,offset.y,repeat.x,repeat.y)}uniforms.envMap.value=material.envMap,uniforms.flipEnvMap.value=material.envMap instanceof THREE.WebGLRenderTargetCube?1:-1,uniforms.reflectivity.value=material.reflectivity,uniforms.refractionRatio.value=material.refractionRatio}function refreshUniformsLine(uniforms,material){uniforms.diffuse.value=material.color,uniforms.opacity.value=material.opacity; +}function refreshUniformsDash(uniforms,material){uniforms.dashSize.value=material.dashSize,uniforms.totalSize.value=material.dashSize+material.gapSize,uniforms.scale.value=material.scale}function refreshUniformsParticle(uniforms,material){if(uniforms.psColor.value=material.color,uniforms.opacity.value=material.opacity,uniforms.size.value=material.size,uniforms.scale.value=_canvas.height/2,uniforms.map.value=material.map,null!==material.map){var offset=material.map.offset,repeat=material.map.repeat;uniforms.offsetRepeat.value.set(offset.x,offset.y,repeat.x,repeat.y)}}function refreshUniformsFog(uniforms,fog){uniforms.fogColor.value=fog.color,fog instanceof THREE.Fog?(uniforms.fogNear.value=fog.near,uniforms.fogFar.value=fog.far):fog instanceof THREE.FogExp2&&(uniforms.fogDensity.value=fog.density)}function refreshUniformsPhong(uniforms,material){uniforms.specular.value=material.specular,uniforms.shininess.value=Math.max(material.shininess,1e-4),material.lightMap&&(uniforms.lightMap.value=material.lightMap,uniforms.lightMapIntensity.value=material.lightMapIntensity),material.emissiveMap&&(uniforms.emissiveMap.value=material.emissiveMap),material.bumpMap&&(uniforms.bumpMap.value=material.bumpMap,uniforms.bumpScale.value=material.bumpScale),material.normalMap&&(uniforms.normalMap.value=material.normalMap,uniforms.normalScale.value.copy(material.normalScale)),material.displacementMap&&(uniforms.displacementMap.value=material.displacementMap,uniforms.displacementScale.value=material.displacementScale,uniforms.displacementBias.value=material.displacementBias)}function refreshUniformsLights(uniforms,lights){uniforms.ambientLightColor.value=lights.ambient,uniforms.directionalLightColor.value=lights.directional.colors,uniforms.directionalLightDirection.value=lights.directional.positions,uniforms.pointLightColor.value=lights.point.colors,uniforms.pointLightPosition.value=lights.point.positions,uniforms.pointLightDistance.value=lights.point.distances,uniforms.pointLightDecay.value=lights.point.decays,uniforms.spotLightColor.value=lights.spot.colors,uniforms.spotLightPosition.value=lights.spot.positions,uniforms.spotLightDistance.value=lights.spot.distances,uniforms.spotLightDirection.value=lights.spot.directions,uniforms.spotLightAngleCos.value=lights.spot.anglesCos,uniforms.spotLightExponent.value=lights.spot.exponents,uniforms.spotLightDecay.value=lights.spot.decays,uniforms.hemisphereLightSkyColor.value=lights.hemi.skyColors,uniforms.hemisphereLightGroundColor.value=lights.hemi.groundColors,uniforms.hemisphereLightDirection.value=lights.hemi.positions}function markUniformsLightsNeedsUpdate(uniforms,value){uniforms.ambientLightColor.needsUpdate=value,uniforms.directionalLightColor.needsUpdate=value,uniforms.directionalLightDirection.needsUpdate=value,uniforms.pointLightColor.needsUpdate=value,uniforms.pointLightPosition.needsUpdate=value,uniforms.pointLightDistance.needsUpdate=value,uniforms.pointLightDecay.needsUpdate=value,uniforms.spotLightColor.needsUpdate=value,uniforms.spotLightPosition.needsUpdate=value,uniforms.spotLightDistance.needsUpdate=value,uniforms.spotLightDirection.needsUpdate=value,uniforms.spotLightAngleCos.needsUpdate=value,uniforms.spotLightExponent.needsUpdate=value,uniforms.spotLightDecay.needsUpdate=value,uniforms.hemisphereLightSkyColor.needsUpdate=value,uniforms.hemisphereLightGroundColor.needsUpdate=value,uniforms.hemisphereLightDirection.needsUpdate=value}function refreshUniformsShadow(uniforms,lights,camera){if(uniforms.shadowMatrix)for(var j=0,i=0,il=lights.length;il>i;i++){var light=lights[i];if(light.castShadow===!0&&(light instanceof THREE.PointLight||light instanceof THREE.SpotLight||light instanceof THREE.DirectionalLight)){var shadow=light.shadow;light instanceof THREE.PointLight?(_vector3.setFromMatrixPosition(light.matrixWorld).negate(),shadow.matrix.identity().setPosition(_vector3),uniforms.shadowDarkness.value[j]=-shadow.darkness):uniforms.shadowDarkness.value[j]=shadow.darkness,uniforms.shadowMatrix.value[j]=shadow.matrix,uniforms.shadowMap.value[j]=shadow.map,uniforms.shadowMapSize.value[j]=shadow.mapSize,uniforms.shadowBias.value[j]=shadow.bias,j++}}}function loadUniformsMatrices(uniforms,object){_gl.uniformMatrix4fv(uniforms.modelViewMatrix,!1,object.modelViewMatrix.elements),uniforms.normalMatrix&&_gl.uniformMatrix3fv(uniforms.normalMatrix,!1,object.normalMatrix.elements)}function getTextureUnit(){var textureUnit=_usedTextureUnits;return textureUnit>=capabilities.maxTextures&&console.warn("WebGLRenderer: trying to use "+textureUnit+" texture units while this GPU supports only "+capabilities.maxTextures),_usedTextureUnits+=1,textureUnit}function loadUniformsGeneric(uniforms){for(var texture,textureUnit,j=0,jl=uniforms.length;jl>j;j++){var uniform=uniforms[j][0];if(uniform.needsUpdate!==!1){var type=uniform.type,value=uniform.value,location=uniforms[j][1];switch(type){case"1i":_gl.uniform1i(location,value);break;case"1f":_gl.uniform1f(location,value);break;case"2f":_gl.uniform2f(location,value[0],value[1]);break;case"3f":_gl.uniform3f(location,value[0],value[1],value[2]);break;case"4f":_gl.uniform4f(location,value[0],value[1],value[2],value[3]);break;case"1iv":_gl.uniform1iv(location,value);break;case"3iv":_gl.uniform3iv(location,value);break;case"1fv":_gl.uniform1fv(location,value);break;case"2fv":_gl.uniform2fv(location,value);break;case"3fv":_gl.uniform3fv(location,value);break;case"4fv":_gl.uniform4fv(location,value);break;case"Matrix3fv":_gl.uniformMatrix3fv(location,!1,value);break;case"Matrix4fv":_gl.uniformMatrix4fv(location,!1,value);break;case"i":_gl.uniform1i(location,value);break;case"f":_gl.uniform1f(location,value);break;case"v2":_gl.uniform2f(location,value.x,value.y);break;case"v3":_gl.uniform3f(location,value.x,value.y,value.z);break;case"v4":_gl.uniform4f(location,value.x,value.y,value.z,value.w);break;case"c":_gl.uniform3f(location,value.r,value.g,value.b);break;case"iv1":_gl.uniform1iv(location,value);break;case"iv":_gl.uniform3iv(location,value);break;case"fv1":_gl.uniform1fv(location,value);break;case"fv":_gl.uniform3fv(location,value);break;case"v2v":void 0===uniform._array&&(uniform._array=new Float32Array(2*value.length));for(var i=0,i2=0,il=value.length;il>i;i++,i2+=2)uniform._array[i2+0]=value[i].x,uniform._array[i2+1]=value[i].y;_gl.uniform2fv(location,uniform._array);break;case"v3v":void 0===uniform._array&&(uniform._array=new Float32Array(3*value.length));for(var i=0,i3=0,il=value.length;il>i;i++,i3+=3)uniform._array[i3+0]=value[i].x,uniform._array[i3+1]=value[i].y,uniform._array[i3+2]=value[i].z;_gl.uniform3fv(location,uniform._array);break;case"v4v":void 0===uniform._array&&(uniform._array=new Float32Array(4*value.length));for(var i=0,i4=0,il=value.length;il>i;i++,i4+=4)uniform._array[i4+0]=value[i].x,uniform._array[i4+1]=value[i].y,uniform._array[i4+2]=value[i].z,uniform._array[i4+3]=value[i].w;_gl.uniform4fv(location,uniform._array);break;case"m3":_gl.uniformMatrix3fv(location,!1,value.elements);break;case"m3v":void 0===uniform._array&&(uniform._array=new Float32Array(9*value.length));for(var i=0,il=value.length;il>i;i++)value[i].flattenToArrayOffset(uniform._array,9*i);_gl.uniformMatrix3fv(location,!1,uniform._array);break;case"m4":_gl.uniformMatrix4fv(location,!1,value.elements);break;case"m4v":void 0===uniform._array&&(uniform._array=new Float32Array(16*value.length));for(var i=0,il=value.length;il>i;i++)value[i].flattenToArrayOffset(uniform._array,16*i);_gl.uniformMatrix4fv(location,!1,uniform._array);break;case"t":if(texture=value,textureUnit=getTextureUnit(),_gl.uniform1i(location,textureUnit),!texture)continue;texture instanceof THREE.CubeTexture||Array.isArray(texture.image)&&6===texture.image.length?setCubeTexture(texture,textureUnit):texture instanceof THREE.WebGLRenderTargetCube?setCubeTextureDynamic(texture.texture,textureUnit):texture instanceof THREE.WebGLRenderTarget?_this.setTexture(texture.texture,textureUnit):_this.setTexture(texture,textureUnit);break;case"tv":void 0===uniform._array&&(uniform._array=[]);for(var i=0,il=uniform.value.length;il>i;i++)uniform._array[i]=getTextureUnit();_gl.uniform1iv(location,uniform._array);for(var i=0,il=uniform.value.length;il>i;i++)texture=uniform.value[i],textureUnit=uniform._array[i],texture&&(texture instanceof THREE.CubeTexture||texture.image instanceof Array&&6===texture.image.length?setCubeTexture(texture,textureUnit):texture instanceof THREE.WebGLRenderTarget?_this.setTexture(texture.texture,textureUnit):texture instanceof THREE.WebGLRenderTargetCube?setCubeTextureDynamic(texture.texture,textureUnit):_this.setTexture(texture,textureUnit));break;default:console.warn("THREE.WebGLRenderer: Unknown uniform type: "+type)}}}}function setColorLinear(array,offset,color,intensity){array[offset+0]=color.r*intensity,array[offset+1]=color.g*intensity,array[offset+2]=color.b*intensity}function setupLights(lights,camera){var l,ll,light,color,skyColor,groundColor,intensity,distance,r=0,g=0,b=0,zlights=_lights,viewMatrix=camera.matrixWorldInverse,dirColors=zlights.directional.colors,dirPositions=zlights.directional.positions,pointColors=zlights.point.colors,pointPositions=zlights.point.positions,pointDistances=zlights.point.distances,pointDecays=zlights.point.decays,spotColors=zlights.spot.colors,spotPositions=zlights.spot.positions,spotDistances=zlights.spot.distances,spotDirections=zlights.spot.directions,spotAnglesCos=zlights.spot.anglesCos,spotExponents=zlights.spot.exponents,spotDecays=zlights.spot.decays,hemiSkyColors=zlights.hemi.skyColors,hemiGroundColors=zlights.hemi.groundColors,hemiPositions=zlights.hemi.positions,dirLength=0,pointLength=0,spotLength=0,hemiLength=0,dirCount=0,pointCount=0,spotCount=0,hemiCount=0,dirOffset=0,pointOffset=0,spotOffset=0,hemiOffset=0;for(l=0,ll=lights.length;ll>l;l++)if(light=lights[l],color=light.color,intensity=light.intensity,distance=light.distance,light instanceof THREE.AmbientLight){if(!light.visible)continue;r+=color.r,g+=color.g,b+=color.b}else if(light instanceof THREE.DirectionalLight){if(dirCount+=1,!light.visible)continue;_direction.setFromMatrixPosition(light.matrixWorld),_vector3.setFromMatrixPosition(light.target.matrixWorld),_direction.sub(_vector3),_direction.transformDirection(viewMatrix),dirOffset=3*dirLength,dirPositions[dirOffset+0]=_direction.x,dirPositions[dirOffset+1]=_direction.y,dirPositions[dirOffset+2]=_direction.z,setColorLinear(dirColors,dirOffset,color,intensity),dirLength+=1}else if(light instanceof THREE.PointLight){if(pointCount+=1,!light.visible)continue;pointOffset=3*pointLength,setColorLinear(pointColors,pointOffset,color,intensity),_vector3.setFromMatrixPosition(light.matrixWorld),_vector3.applyMatrix4(viewMatrix),pointPositions[pointOffset+0]=_vector3.x,pointPositions[pointOffset+1]=_vector3.y,pointPositions[pointOffset+2]=_vector3.z,pointDistances[pointLength]=distance,pointDecays[pointLength]=0===light.distance?0:light.decay,pointLength+=1}else if(light instanceof THREE.SpotLight){if(spotCount+=1,!light.visible)continue;spotOffset=3*spotLength,setColorLinear(spotColors,spotOffset,color,intensity),_direction.setFromMatrixPosition(light.matrixWorld),_vector3.copy(_direction).applyMatrix4(viewMatrix),spotPositions[spotOffset+0]=_vector3.x,spotPositions[spotOffset+1]=_vector3.y,spotPositions[spotOffset+2]=_vector3.z,spotDistances[spotLength]=distance,_vector3.setFromMatrixPosition(light.target.matrixWorld),_direction.sub(_vector3),_direction.transformDirection(viewMatrix),spotDirections[spotOffset+0]=_direction.x,spotDirections[spotOffset+1]=_direction.y,spotDirections[spotOffset+2]=_direction.z,spotAnglesCos[spotLength]=Math.cos(light.angle),spotExponents[spotLength]=light.exponent,spotDecays[spotLength]=0===light.distance?0:light.decay,spotLength+=1}else if(light instanceof THREE.HemisphereLight){if(hemiCount+=1,!light.visible)continue;_direction.setFromMatrixPosition(light.matrixWorld),_direction.transformDirection(viewMatrix),hemiOffset=3*hemiLength,hemiPositions[hemiOffset+0]=_direction.x,hemiPositions[hemiOffset+1]=_direction.y,hemiPositions[hemiOffset+2]=_direction.z,skyColor=light.color,groundColor=light.groundColor,setColorLinear(hemiSkyColors,hemiOffset,skyColor,intensity),setColorLinear(hemiGroundColors,hemiOffset,groundColor,intensity),hemiLength+=1}for(l=3*dirLength,ll=Math.max(dirColors.length,3*dirCount);ll>l;l++)dirColors[l]=0;for(l=3*pointLength,ll=Math.max(pointColors.length,3*pointCount);ll>l;l++)pointColors[l]=0;for(l=3*spotLength,ll=Math.max(spotColors.length,3*spotCount);ll>l;l++)spotColors[l]=0;for(l=3*hemiLength,ll=Math.max(hemiSkyColors.length,3*hemiCount);ll>l;l++)hemiSkyColors[l]=0;for(l=3*hemiLength,ll=Math.max(hemiGroundColors.length,3*hemiCount);ll>l;l++)hemiGroundColors[l]=0;zlights.directional.length=dirLength,zlights.point.length=pointLength,zlights.spot.length=spotLength,zlights.hemi.length=hemiLength,zlights.ambient[0]=r,zlights.ambient[1]=g,zlights.ambient[2]=b}function setTextureParameters(textureType,texture,isImagePowerOfTwo){var extension;if(isImagePowerOfTwo?(_gl.texParameteri(textureType,_gl.TEXTURE_WRAP_S,paramThreeToGL(texture.wrapS)),_gl.texParameteri(textureType,_gl.TEXTURE_WRAP_T,paramThreeToGL(texture.wrapT)),_gl.texParameteri(textureType,_gl.TEXTURE_MAG_FILTER,paramThreeToGL(texture.magFilter)),_gl.texParameteri(textureType,_gl.TEXTURE_MIN_FILTER,paramThreeToGL(texture.minFilter))):(_gl.texParameteri(textureType,_gl.TEXTURE_WRAP_S,_gl.CLAMP_TO_EDGE),_gl.texParameteri(textureType,_gl.TEXTURE_WRAP_T,_gl.CLAMP_TO_EDGE),texture.wrapS===THREE.ClampToEdgeWrapping&&texture.wrapT===THREE.ClampToEdgeWrapping||console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.",texture),_gl.texParameteri(textureType,_gl.TEXTURE_MAG_FILTER,filterFallback(texture.magFilter)),_gl.texParameteri(textureType,_gl.TEXTURE_MIN_FILTER,filterFallback(texture.minFilter)),texture.minFilter!==THREE.NearestFilter&&texture.minFilter!==THREE.LinearFilter&&console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.",texture)),extension=extensions.get("EXT_texture_filter_anisotropic")){if(texture.type===THREE.FloatType&&null===extensions.get("OES_texture_float_linear"))return;if(texture.type===THREE.HalfFloatType&&null===extensions.get("OES_texture_half_float_linear"))return;(texture.anisotropy>1||properties.get(texture).__currentAnisotropy)&&(_gl.texParameterf(textureType,extension.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(texture.anisotropy,_this.getMaxAnisotropy())),properties.get(texture).__currentAnisotropy=texture.anisotropy)}}function uploadTexture(textureProperties,texture,slot){void 0===textureProperties.__webglInit&&(textureProperties.__webglInit=!0,texture.addEventListener("dispose",onTextureDispose),textureProperties.__webglTexture=_gl.createTexture(),_infoMemory.textures++),state.activeTexture(_gl.TEXTURE0+slot),state.bindTexture(_gl.TEXTURE_2D,textureProperties.__webglTexture),_gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL,texture.flipY),_gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL,texture.premultiplyAlpha),_gl.pixelStorei(_gl.UNPACK_ALIGNMENT,texture.unpackAlignment),texture.image=clampToMaxSize(texture.image,capabilities.maxTextureSize),textureNeedsPowerOfTwo(texture)&&isPowerOfTwo(texture.image)===!1&&(texture.image=makePowerOfTwo(texture.image));var image=texture.image,isImagePowerOfTwo=isPowerOfTwo(image),glFormat=paramThreeToGL(texture.format),glType=paramThreeToGL(texture.type);setTextureParameters(_gl.TEXTURE_2D,texture,isImagePowerOfTwo);var mipmap,mipmaps=texture.mipmaps;if(texture instanceof THREE.DataTexture)if(mipmaps.length>0&&isImagePowerOfTwo){for(var i=0,il=mipmaps.length;il>i;i++)mipmap=mipmaps[i],state.texImage2D(_gl.TEXTURE_2D,i,glFormat,mipmap.width,mipmap.height,0,glFormat,glType,mipmap.data);texture.generateMipmaps=!1}else state.texImage2D(_gl.TEXTURE_2D,0,glFormat,image.width,image.height,0,glFormat,glType,image.data);else if(texture instanceof THREE.CompressedTexture)for(var i=0,il=mipmaps.length;il>i;i++)mipmap=mipmaps[i],texture.format!==THREE.RGBAFormat&&texture.format!==THREE.RGBFormat?state.getCompressedTextureFormats().indexOf(glFormat)>-1?state.compressedTexImage2D(_gl.TEXTURE_2D,i,glFormat,mipmap.width,mipmap.height,0,mipmap.data):console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()"):state.texImage2D(_gl.TEXTURE_2D,i,glFormat,mipmap.width,mipmap.height,0,glFormat,glType,mipmap.data);else if(mipmaps.length>0&&isImagePowerOfTwo){for(var i=0,il=mipmaps.length;il>i;i++)mipmap=mipmaps[i],state.texImage2D(_gl.TEXTURE_2D,i,glFormat,glFormat,glType,mipmap);texture.generateMipmaps=!1}else state.texImage2D(_gl.TEXTURE_2D,0,glFormat,glFormat,glType,texture.image);texture.generateMipmaps&&isImagePowerOfTwo&&_gl.generateMipmap(_gl.TEXTURE_2D),textureProperties.__version=texture.version,texture.onUpdate&&texture.onUpdate(texture)}function clampToMaxSize(image,maxSize){if(image.width>maxSize||image.height>maxSize){var scale=maxSize/Math.max(image.width,image.height),canvas=document.createElement("canvas");canvas.width=Math.floor(image.width*scale),canvas.height=Math.floor(image.height*scale);var context=canvas.getContext("2d");return context.drawImage(image,0,0,image.width,image.height,0,0,canvas.width,canvas.height),console.warn("THREE.WebGLRenderer: image is too big ("+image.width+"x"+image.height+"). Resized to "+canvas.width+"x"+canvas.height,image),canvas}return image}function isPowerOfTwo(image){return THREE.Math.isPowerOfTwo(image.width)&&THREE.Math.isPowerOfTwo(image.height)}function textureNeedsPowerOfTwo(texture){return texture.wrapS!==THREE.ClampToEdgeWrapping||texture.wrapT!==THREE.ClampToEdgeWrapping?!0:texture.minFilter!==THREE.NearestFilter&&texture.minFilter!==THREE.LinearFilter}function makePowerOfTwo(image){if(image instanceof HTMLImageElement||image instanceof HTMLCanvasElement){var canvas=document.createElement("canvas");canvas.width=THREE.Math.nearestPowerOfTwo(image.width),canvas.height=THREE.Math.nearestPowerOfTwo(image.height);var context=canvas.getContext("2d");return context.drawImage(image,0,0,canvas.width,canvas.height),console.warn("THREE.WebGLRenderer: image is not power of two ("+image.width+"x"+image.height+"). Resized to "+canvas.width+"x"+canvas.height,image),canvas}return image}function setCubeTexture(texture,slot){var textureProperties=properties.get(texture);if(6===texture.image.length)if(texture.version>0&&textureProperties.__version!==texture.version){textureProperties.__image__webglTextureCube||(texture.addEventListener("dispose",onTextureDispose),textureProperties.__image__webglTextureCube=_gl.createTexture(),_infoMemory.textures++),state.activeTexture(_gl.TEXTURE0+slot),state.bindTexture(_gl.TEXTURE_CUBE_MAP,textureProperties.__image__webglTextureCube),_gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL,texture.flipY);for(var isCompressed=texture instanceof THREE.CompressedTexture,isDataTexture=texture.image[0]instanceof THREE.DataTexture,cubeImage=[],i=0;6>i;i++)!_this.autoScaleCubemaps||isCompressed||isDataTexture?cubeImage[i]=isDataTexture?texture.image[i].image:texture.image[i]:cubeImage[i]=clampToMaxSize(texture.image[i],capabilities.maxCubemapSize);var image=cubeImage[0],isImagePowerOfTwo=isPowerOfTwo(image),glFormat=paramThreeToGL(texture.format),glType=paramThreeToGL(texture.type);setTextureParameters(_gl.TEXTURE_CUBE_MAP,texture,isImagePowerOfTwo);for(var i=0;6>i;i++)if(isCompressed)for(var mipmap,mipmaps=cubeImage[i].mipmaps,j=0,jl=mipmaps.length;jl>j;j++)mipmap=mipmaps[j],texture.format!==THREE.RGBAFormat&&texture.format!==THREE.RGBFormat?state.getCompressedTextureFormats().indexOf(glFormat)>-1?state.compressedTexImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,j,glFormat,mipmap.width,mipmap.height,0,mipmap.data):console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setCubeTexture()"):state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,j,glFormat,mipmap.width,mipmap.height,0,glFormat,glType,mipmap.data);else isDataTexture?state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,0,glFormat,cubeImage[i].width,cubeImage[i].height,0,glFormat,glType,cubeImage[i].data):state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,0,glFormat,glFormat,glType,cubeImage[i]);texture.generateMipmaps&&isImagePowerOfTwo&&_gl.generateMipmap(_gl.TEXTURE_CUBE_MAP),textureProperties.__version=texture.version,texture.onUpdate&&texture.onUpdate(texture)}else state.activeTexture(_gl.TEXTURE0+slot),state.bindTexture(_gl.TEXTURE_CUBE_MAP,textureProperties.__image__webglTextureCube)}function setCubeTextureDynamic(texture,slot){state.activeTexture(_gl.TEXTURE0+slot),state.bindTexture(_gl.TEXTURE_CUBE_MAP,properties.get(texture).__webglTexture)}function setupFrameBuffer(framebuffer,renderTarget,textureTarget){_gl.bindFramebuffer(_gl.FRAMEBUFFER,framebuffer),_gl.framebufferTexture2D(_gl.FRAMEBUFFER,_gl.COLOR_ATTACHMENT0,textureTarget,properties.get(renderTarget.texture).__webglTexture,0)}function setupRenderBuffer(renderbuffer,renderTarget){_gl.bindRenderbuffer(_gl.RENDERBUFFER,renderbuffer),renderTarget.depthBuffer&&!renderTarget.stencilBuffer?(_gl.renderbufferStorage(_gl.RENDERBUFFER,_gl.DEPTH_COMPONENT16,renderTarget.width,renderTarget.height),_gl.framebufferRenderbuffer(_gl.FRAMEBUFFER,_gl.DEPTH_ATTACHMENT,_gl.RENDERBUFFER,renderbuffer)):renderTarget.depthBuffer&&renderTarget.stencilBuffer?(_gl.renderbufferStorage(_gl.RENDERBUFFER,_gl.DEPTH_STENCIL,renderTarget.width,renderTarget.height),_gl.framebufferRenderbuffer(_gl.FRAMEBUFFER,_gl.DEPTH_STENCIL_ATTACHMENT,_gl.RENDERBUFFER,renderbuffer)):_gl.renderbufferStorage(_gl.RENDERBUFFER,_gl.RGBA4,renderTarget.width,renderTarget.height)}function updateRenderTargetMipmap(renderTarget){var target=renderTarget instanceof THREE.WebGLRenderTargetCube?_gl.TEXTURE_CUBE_MAP:_gl.TEXTURE_2D,texture=properties.get(renderTarget.texture).__webglTexture;state.bindTexture(target,texture),_gl.generateMipmap(target),state.bindTexture(target,null)}function filterFallback(f){return f===THREE.NearestFilter||f===THREE.NearestMipMapNearestFilter||f===THREE.NearestMipMapLinearFilter?_gl.NEAREST:_gl.LINEAR}function paramThreeToGL(p){var extension;if(p===THREE.RepeatWrapping)return _gl.REPEAT;if(p===THREE.ClampToEdgeWrapping)return _gl.CLAMP_TO_EDGE;if(p===THREE.MirroredRepeatWrapping)return _gl.MIRRORED_REPEAT;if(p===THREE.NearestFilter)return _gl.NEAREST;if(p===THREE.NearestMipMapNearestFilter)return _gl.NEAREST_MIPMAP_NEAREST;if(p===THREE.NearestMipMapLinearFilter)return _gl.NEAREST_MIPMAP_LINEAR;if(p===THREE.LinearFilter)return _gl.LINEAR;if(p===THREE.LinearMipMapNearestFilter)return _gl.LINEAR_MIPMAP_NEAREST;if(p===THREE.LinearMipMapLinearFilter)return _gl.LINEAR_MIPMAP_LINEAR;if(p===THREE.UnsignedByteType)return _gl.UNSIGNED_BYTE;if(p===THREE.UnsignedShort4444Type)return _gl.UNSIGNED_SHORT_4_4_4_4;if(p===THREE.UnsignedShort5551Type)return _gl.UNSIGNED_SHORT_5_5_5_1;if(p===THREE.UnsignedShort565Type)return _gl.UNSIGNED_SHORT_5_6_5;if(p===THREE.ByteType)return _gl.BYTE;if(p===THREE.ShortType)return _gl.SHORT;if(p===THREE.UnsignedShortType)return _gl.UNSIGNED_SHORT;if(p===THREE.IntType)return _gl.INT;if(p===THREE.UnsignedIntType)return _gl.UNSIGNED_INT;if(p===THREE.FloatType)return _gl.FLOAT;if(extension=extensions.get("OES_texture_half_float"),null!==extension&&p===THREE.HalfFloatType)return extension.HALF_FLOAT_OES;if(p===THREE.AlphaFormat)return _gl.ALPHA;if(p===THREE.RGBFormat)return _gl.RGB;if(p===THREE.RGBAFormat)return _gl.RGBA;if(p===THREE.LuminanceFormat)return _gl.LUMINANCE;if(p===THREE.LuminanceAlphaFormat)return _gl.LUMINANCE_ALPHA;if(p===THREE.AddEquation)return _gl.FUNC_ADD;if(p===THREE.SubtractEquation)return _gl.FUNC_SUBTRACT;if(p===THREE.ReverseSubtractEquation)return _gl.FUNC_REVERSE_SUBTRACT;if(p===THREE.ZeroFactor)return _gl.ZERO;if(p===THREE.OneFactor)return _gl.ONE;if(p===THREE.SrcColorFactor)return _gl.SRC_COLOR;if(p===THREE.OneMinusSrcColorFactor)return _gl.ONE_MINUS_SRC_COLOR;if(p===THREE.SrcAlphaFactor)return _gl.SRC_ALPHA;if(p===THREE.OneMinusSrcAlphaFactor)return _gl.ONE_MINUS_SRC_ALPHA;if(p===THREE.DstAlphaFactor)return _gl.DST_ALPHA;if(p===THREE.OneMinusDstAlphaFactor)return _gl.ONE_MINUS_DST_ALPHA;if(p===THREE.DstColorFactor)return _gl.DST_COLOR;if(p===THREE.OneMinusDstColorFactor)return _gl.ONE_MINUS_DST_COLOR;if(p===THREE.SrcAlphaSaturateFactor)return _gl.SRC_ALPHA_SATURATE;if(extension=extensions.get("WEBGL_compressed_texture_s3tc"),null!==extension){if(p===THREE.RGB_S3TC_DXT1_Format)return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;if(p===THREE.RGBA_S3TC_DXT1_Format)return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(p===THREE.RGBA_S3TC_DXT3_Format)return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(p===THREE.RGBA_S3TC_DXT5_Format)return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT}if(extension=extensions.get("WEBGL_compressed_texture_pvrtc"),null!==extension){if(p===THREE.RGB_PVRTC_4BPPV1_Format)return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(p===THREE.RGB_PVRTC_2BPPV1_Format)return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(p===THREE.RGBA_PVRTC_4BPPV1_Format)return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;if(p===THREE.RGBA_PVRTC_2BPPV1_Format)return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}if(extension=extensions.get("EXT_blend_minmax"),null!==extension){if(p===THREE.MinEquation)return extension.MIN_EXT;if(p===THREE.MaxEquation)return extension.MAX_EXT}return 0}console.log("THREE.WebGLRenderer",THREE.REVISION),parameters=parameters||{};var _canvas=void 0!==parameters.canvas?parameters.canvas:document.createElement("canvas"),_context=void 0!==parameters.context?parameters.context:null,_width=_canvas.width,_height=_canvas.height,pixelRatio=1,_alpha=void 0!==parameters.alpha?parameters.alpha:!1,_depth=void 0!==parameters.depth?parameters.depth:!0,_stencil=void 0!==parameters.stencil?parameters.stencil:!0,_antialias=void 0!==parameters.antialias?parameters.antialias:!1,_premultipliedAlpha=void 0!==parameters.premultipliedAlpha?parameters.premultipliedAlpha:!0,_preserveDrawingBuffer=void 0!==parameters.preserveDrawingBuffer?parameters.preserveDrawingBuffer:!1,_clearColor=new THREE.Color(0),_clearAlpha=0,lights=[],opaqueObjects=[],opaqueObjectsLastIndex=-1,transparentObjects=[],transparentObjectsLastIndex=-1,morphInfluences=new Float32Array(8),sprites=[],lensFlares=[];this.domElement=_canvas,this.context=null,this.autoClear=!0,this.autoClearColor=!0,this.autoClearDepth=!0,this.autoClearStencil=!0,this.sortObjects=!0,this.gammaFactor=2,this.gammaInput=!1,this.gammaOutput=!1,this.maxMorphTargets=8,this.maxMorphNormals=4,this.autoScaleCubemaps=!0;var _this=this,_currentProgram=null,_currentFramebuffer=null,_currentMaterialId=-1,_currentGeometryProgram="",_currentCamera=null,_usedTextureUnits=0,_viewportX=0,_viewportY=0,_viewportWidth=_canvas.width,_viewportHeight=_canvas.height,_currentWidth=0,_currentHeight=0,_frustum=new THREE.Frustum,_projScreenMatrix=new THREE.Matrix4,_vector3=new THREE.Vector3,_direction=new THREE.Vector3,_lightsNeedUpdate=!0,_lights={ambient:[0,0,0],directional:{length:0,colors:[],positions:[]},point:{length:0,colors:[],positions:[],distances:[],decays:[]},spot:{length:0,colors:[],positions:[],distances:[],directions:[],anglesCos:[],exponents:[],decays:[]},hemi:{length:0,skyColors:[],groundColors:[],positions:[]}},_infoMemory={geometries:0,textures:0},_infoRender={calls:0,vertices:0,faces:0,points:0};this.info={render:_infoRender,memory:_infoMemory,programs:null};var _gl;try{var attributes={alpha:_alpha,depth:_depth,stencil:_stencil,antialias:_antialias,premultipliedAlpha:_premultipliedAlpha,preserveDrawingBuffer:_preserveDrawingBuffer};if(_gl=_context||_canvas.getContext("webgl",attributes)||_canvas.getContext("experimental-webgl",attributes),null===_gl)throw null!==_canvas.getContext("webgl")?"Error creating WebGL context with your selected attributes.":"Error creating WebGL context.";_canvas.addEventListener("webglcontextlost",onContextLost,!1)}catch(error){console.error("THREE.WebGLRenderer: "+error)}var extensions=new THREE.WebGLExtensions(_gl);extensions.get("OES_texture_float"),extensions.get("OES_texture_float_linear"),extensions.get("OES_texture_half_float"),extensions.get("OES_texture_half_float_linear"),extensions.get("OES_standard_derivatives"),extensions.get("ANGLE_instanced_arrays"),extensions.get("OES_element_index_uint")&&(THREE.BufferGeometry.MaxIndex=4294967296);var capabilities=new THREE.WebGLCapabilities(_gl,extensions,parameters),state=new THREE.WebGLState(_gl,extensions,paramThreeToGL),properties=new THREE.WebGLProperties,objects=new THREE.WebGLObjects(_gl,properties,this.info),programCache=new THREE.WebGLPrograms(this,capabilities);this.info.programs=programCache.programs;var bufferRenderer=new THREE.WebGLBufferRenderer(_gl,extensions,_infoRender),indexedBufferRenderer=new THREE.WebGLIndexedBufferRenderer(_gl,extensions,_infoRender);setDefaultGLState(),this.context=_gl,this.capabilities=capabilities,this.extensions=extensions,this.state=state;var shadowMap=new THREE.WebGLShadowMap(this,lights,objects);this.shadowMap=shadowMap;var spritePlugin=new THREE.SpritePlugin(this,sprites),lensFlarePlugin=new THREE.LensFlarePlugin(this,lensFlares);this.getContext=function(){return _gl},this.getContextAttributes=function(){return _gl.getContextAttributes()},this.forceContextLoss=function(){extensions.get("WEBGL_lose_context").loseContext()},this.getMaxAnisotropy=function(){var value;return function(){if(void 0!==value)return value;var extension=extensions.get("EXT_texture_filter_anisotropic");return value=null!==extension?_gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT):0}}(),this.getPrecision=function(){return capabilities.precision},this.getPixelRatio=function(){return pixelRatio},this.setPixelRatio=function(value){void 0!==value&&(pixelRatio=value)},this.getSize=function(){return{width:_width,height:_height}},this.setSize=function(width,height,updateStyle){_width=width,_height=height,_canvas.width=width*pixelRatio,_canvas.height=height*pixelRatio,updateStyle!==!1&&(_canvas.style.width=width+"px",_canvas.style.height=height+"px"),this.setViewport(0,0,width,height)},this.setViewport=function(x,y,width,height){_viewportX=x*pixelRatio,_viewportY=y*pixelRatio,_viewportWidth=width*pixelRatio,_viewportHeight=height*pixelRatio,_gl.viewport(_viewportX,_viewportY,_viewportWidth,_viewportHeight)},this.getViewport=function(dimensions){dimensions.x=_viewportX/pixelRatio,dimensions.y=_viewportY/pixelRatio,dimensions.z=_viewportWidth/pixelRatio,dimensions.w=_viewportHeight/pixelRatio},this.setScissor=function(x,y,width,height){_gl.scissor(x*pixelRatio,y*pixelRatio,width*pixelRatio,height*pixelRatio)},this.enableScissorTest=function(boolean){state.setScissorTest(boolean)},this.getClearColor=function(){return _clearColor},this.setClearColor=function(color,alpha){_clearColor.set(color),_clearAlpha=void 0!==alpha?alpha:1,glClearColor(_clearColor.r,_clearColor.g,_clearColor.b,_clearAlpha)},this.getClearAlpha=function(){return _clearAlpha},this.setClearAlpha=function(alpha){_clearAlpha=alpha,glClearColor(_clearColor.r,_clearColor.g,_clearColor.b,_clearAlpha)},this.clear=function(color,depth,stencil){var bits=0;(void 0===color||color)&&(bits|=_gl.COLOR_BUFFER_BIT),(void 0===depth||depth)&&(bits|=_gl.DEPTH_BUFFER_BIT),(void 0===stencil||stencil)&&(bits|=_gl.STENCIL_BUFFER_BIT),_gl.clear(bits)},this.clearColor=function(){_gl.clear(_gl.COLOR_BUFFER_BIT)},this.clearDepth=function(){_gl.clear(_gl.DEPTH_BUFFER_BIT)},this.clearStencil=function(){_gl.clear(_gl.STENCIL_BUFFER_BIT)},this.clearTarget=function(renderTarget,color,depth,stencil){this.setRenderTarget(renderTarget),this.clear(color,depth,stencil)},this.resetGLState=resetGLState,this.dispose=function(){_canvas.removeEventListener("webglcontextlost",onContextLost,!1)},this.renderBufferImmediate=function(object,program,material){state.initAttributes();var buffers=properties.get(object);object.hasPositions&&!buffers.position&&(buffers.position=_gl.createBuffer()), +object.hasNormals&&!buffers.normal&&(buffers.normal=_gl.createBuffer()),object.hasUvs&&!buffers.uv&&(buffers.uv=_gl.createBuffer()),object.hasColors&&!buffers.color&&(buffers.color=_gl.createBuffer());var attributes=program.getAttributes();if(object.hasPositions&&(_gl.bindBuffer(_gl.ARRAY_BUFFER,buffers.position),_gl.bufferData(_gl.ARRAY_BUFFER,object.positionArray,_gl.DYNAMIC_DRAW),state.enableAttribute(attributes.position),_gl.vertexAttribPointer(attributes.position,3,_gl.FLOAT,!1,0,0)),object.hasNormals){if(_gl.bindBuffer(_gl.ARRAY_BUFFER,buffers.normal),"MeshPhongMaterial"!==material.type&&material.shading===THREE.FlatShading)for(var i=0,l=3*object.count;l>i;i+=9){var array=object.normalArray,nx=(array[i+0]+array[i+3]+array[i+6])/3,ny=(array[i+1]+array[i+4]+array[i+7])/3,nz=(array[i+2]+array[i+5]+array[i+8])/3;array[i+0]=nx,array[i+1]=ny,array[i+2]=nz,array[i+3]=nx,array[i+4]=ny,array[i+5]=nz,array[i+6]=nx,array[i+7]=ny,array[i+8]=nz}_gl.bufferData(_gl.ARRAY_BUFFER,object.normalArray,_gl.DYNAMIC_DRAW),state.enableAttribute(attributes.normal),_gl.vertexAttribPointer(attributes.normal,3,_gl.FLOAT,!1,0,0)}object.hasUvs&&material.map&&(_gl.bindBuffer(_gl.ARRAY_BUFFER,buffers.uv),_gl.bufferData(_gl.ARRAY_BUFFER,object.uvArray,_gl.DYNAMIC_DRAW),state.enableAttribute(attributes.uv),_gl.vertexAttribPointer(attributes.uv,2,_gl.FLOAT,!1,0,0)),object.hasColors&&material.vertexColors!==THREE.NoColors&&(_gl.bindBuffer(_gl.ARRAY_BUFFER,buffers.color),_gl.bufferData(_gl.ARRAY_BUFFER,object.colorArray,_gl.DYNAMIC_DRAW),state.enableAttribute(attributes.color),_gl.vertexAttribPointer(attributes.color,3,_gl.FLOAT,!1,0,0)),state.disableUnusedAttributes(),_gl.drawArrays(_gl.TRIANGLES,0,object.count),object.count=0},this.renderBufferDirect=function(camera,lights,fog,geometry,material,object,group){setMaterial(material);var program=setProgram(camera,lights,fog,material,object),updateBuffers=!1,geometryProgram=geometry.id+"_"+program.id+"_"+material.wireframe;geometryProgram!==_currentGeometryProgram&&(_currentGeometryProgram=geometryProgram,updateBuffers=!0);var morphTargetInfluences=object.morphTargetInfluences;if(void 0!==morphTargetInfluences){for(var activeInfluences=[],i=0,l=morphTargetInfluences.length;l>i;i++){var influence=morphTargetInfluences[i];activeInfluences.push([influence,i])}activeInfluences.sort(numericalSort),activeInfluences.length>8&&(activeInfluences.length=8);for(var morphAttributes=geometry.morphAttributes,i=0,l=activeInfluences.length;l>i;i++){var influence=activeInfluences[i];if(morphInfluences[i]=influence[0],0!==influence[0]){var index=influence[1];material.morphTargets===!0&&morphAttributes.position&&geometry.addAttribute("morphTarget"+i,morphAttributes.position[index]),material.morphNormals===!0&&morphAttributes.normal&&geometry.addAttribute("morphNormal"+i,morphAttributes.normal[index])}else material.morphTargets===!0&&geometry.removeAttribute("morphTarget"+i),material.morphNormals===!0&&geometry.removeAttribute("morphNormal"+i)}var uniforms=program.getUniforms();null!==uniforms.morphTargetInfluences&&_gl.uniform1fv(uniforms.morphTargetInfluences,morphInfluences),updateBuffers=!0}var index=geometry.index,position=geometry.attributes.position;material.wireframe===!0&&(index=objects.getWireframeAttribute(geometry));var renderer;null!==index?(renderer=indexedBufferRenderer,renderer.setIndex(index)):renderer=bufferRenderer,updateBuffers&&(setupVertexAttributes(material,program,geometry),null!==index&&_gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER,objects.getAttributeBuffer(index)));var dataStart=0,dataCount=1/0;null!==index?dataCount=index.count:void 0!==position&&(dataCount=position.count);var rangeStart=geometry.drawRange.start,rangeCount=geometry.drawRange.count,groupStart=null!==group?group.start:0,groupCount=null!==group?group.count:1/0,drawStart=Math.max(dataStart,rangeStart,groupStart),drawEnd=Math.min(dataStart+dataCount,rangeStart+rangeCount,groupStart+groupCount)-1,drawCount=Math.max(0,drawEnd-drawStart+1);if(object instanceof THREE.Mesh)material.wireframe===!0?(state.setLineWidth(material.wireframeLinewidth*pixelRatio),renderer.setMode(_gl.LINES)):renderer.setMode(_gl.TRIANGLES),geometry instanceof THREE.InstancedBufferGeometry&&geometry.maxInstancedCount>0?renderer.renderInstances(geometry):renderer.render(drawStart,drawCount);else if(object instanceof THREE.Line){var lineWidth=material.linewidth;void 0===lineWidth&&(lineWidth=1),state.setLineWidth(lineWidth*pixelRatio),object instanceof THREE.LineSegments?renderer.setMode(_gl.LINES):renderer.setMode(_gl.LINE_STRIP),renderer.render(drawStart,drawCount)}else object instanceof THREE.Points&&(renderer.setMode(_gl.POINTS),renderer.render(drawStart,drawCount))},this.render=function(scene,camera,renderTarget,forceClear){if(camera instanceof THREE.Camera==!1)return void console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.");var fog=scene.fog;if(_currentGeometryProgram="",_currentMaterialId=-1,_currentCamera=null,_lightsNeedUpdate=!0,scene.autoUpdate===!0&&scene.updateMatrixWorld(),null===camera.parent&&camera.updateMatrixWorld(),camera.matrixWorldInverse.getInverse(camera.matrixWorld),_projScreenMatrix.multiplyMatrices(camera.projectionMatrix,camera.matrixWorldInverse),_frustum.setFromMatrix(_projScreenMatrix),lights.length=0,opaqueObjectsLastIndex=-1,transparentObjectsLastIndex=-1,sprites.length=0,lensFlares.length=0,projectObject(scene,camera),opaqueObjects.length=opaqueObjectsLastIndex+1,transparentObjects.length=transparentObjectsLastIndex+1,_this.sortObjects===!0&&(opaqueObjects.sort(painterSortStable),transparentObjects.sort(reversePainterSortStable)),shadowMap.render(scene),_infoRender.calls=0,_infoRender.vertices=0,_infoRender.faces=0,_infoRender.points=0,this.setRenderTarget(renderTarget),(this.autoClear||forceClear)&&this.clear(this.autoClearColor,this.autoClearDepth,this.autoClearStencil),scene.overrideMaterial){var overrideMaterial=scene.overrideMaterial;renderObjects(opaqueObjects,camera,lights,fog,overrideMaterial),renderObjects(transparentObjects,camera,lights,fog,overrideMaterial)}else state.setBlending(THREE.NoBlending),renderObjects(opaqueObjects,camera,lights,fog),renderObjects(transparentObjects,camera,lights,fog);if(spritePlugin.render(scene,camera),lensFlarePlugin.render(scene,camera,_currentWidth,_currentHeight),renderTarget){var texture=renderTarget.texture,isTargetPowerOfTwo=isPowerOfTwo(renderTarget);texture.generateMipmaps&&isTargetPowerOfTwo&&texture.minFilter!==THREE.NearestFilter&&texture.minFilter!==THREE.LinearFilter&&updateRenderTargetMipmap(renderTarget)}state.setDepthTest(!0),state.setDepthWrite(!0),state.setColorWrite(!0)},this.setFaceCulling=function(cullFace,frontFaceDirection){cullFace===THREE.CullFaceNone?state.disable(_gl.CULL_FACE):(frontFaceDirection===THREE.FrontFaceDirectionCW?_gl.frontFace(_gl.CW):_gl.frontFace(_gl.CCW),cullFace===THREE.CullFaceBack?_gl.cullFace(_gl.BACK):cullFace===THREE.CullFaceFront?_gl.cullFace(_gl.FRONT):_gl.cullFace(_gl.FRONT_AND_BACK),state.enable(_gl.CULL_FACE))},this.setTexture=function(texture,slot){var textureProperties=properties.get(texture);if(texture.version>0&&textureProperties.__version!==texture.version){var image=texture.image;return void 0===image?void console.warn("THREE.WebGLRenderer: Texture marked for update but image is undefined",texture):image.complete===!1?void console.warn("THREE.WebGLRenderer: Texture marked for update but image is incomplete",texture):void uploadTexture(textureProperties,texture,slot)}state.activeTexture(_gl.TEXTURE0+slot),state.bindTexture(_gl.TEXTURE_2D,textureProperties.__webglTexture)},this.setRenderTarget=function(renderTarget){var isCube=renderTarget instanceof THREE.WebGLRenderTargetCube;if(renderTarget&&void 0===properties.get(renderTarget).__webglFramebuffer){var renderTargetProperties=properties.get(renderTarget),textureProperties=properties.get(renderTarget.texture);void 0===renderTarget.depthBuffer&&(renderTarget.depthBuffer=!0),void 0===renderTarget.stencilBuffer&&(renderTarget.stencilBuffer=!0),renderTarget.addEventListener("dispose",onRenderTargetDispose),textureProperties.__webglTexture=_gl.createTexture(),_infoMemory.textures++;var isTargetPowerOfTwo=isPowerOfTwo(renderTarget),glFormat=paramThreeToGL(renderTarget.texture.format),glType=paramThreeToGL(renderTarget.texture.type);if(isCube){renderTargetProperties.__webglFramebuffer=[],renderTargetProperties.__webglRenderbuffer=[],state.bindTexture(_gl.TEXTURE_CUBE_MAP,textureProperties.__webglTexture),setTextureParameters(_gl.TEXTURE_CUBE_MAP,renderTarget.texture,isTargetPowerOfTwo);for(var i=0;6>i;i++)renderTargetProperties.__webglFramebuffer[i]=_gl.createFramebuffer(),renderTargetProperties.__webglRenderbuffer[i]=_gl.createRenderbuffer(),state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,0,glFormat,renderTarget.width,renderTarget.height,0,glFormat,glType,null),setupFrameBuffer(renderTargetProperties.__webglFramebuffer[i],renderTarget,_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i),setupRenderBuffer(renderTargetProperties.__webglRenderbuffer[i],renderTarget);renderTarget.texture.generateMipmaps&&isTargetPowerOfTwo&&_gl.generateMipmap(_gl.TEXTURE_CUBE_MAP)}else renderTargetProperties.__webglFramebuffer=_gl.createFramebuffer(),renderTarget.shareDepthFrom?renderTargetProperties.__webglRenderbuffer=renderTarget.shareDepthFrom.__webglRenderbuffer:renderTargetProperties.__webglRenderbuffer=_gl.createRenderbuffer(),state.bindTexture(_gl.TEXTURE_2D,textureProperties.__webglTexture),setTextureParameters(_gl.TEXTURE_2D,renderTarget.texture,isTargetPowerOfTwo),state.texImage2D(_gl.TEXTURE_2D,0,glFormat,renderTarget.width,renderTarget.height,0,glFormat,glType,null),setupFrameBuffer(renderTargetProperties.__webglFramebuffer,renderTarget,_gl.TEXTURE_2D),renderTarget.shareDepthFrom?renderTarget.depthBuffer&&!renderTarget.stencilBuffer?_gl.framebufferRenderbuffer(_gl.FRAMEBUFFER,_gl.DEPTH_ATTACHMENT,_gl.RENDERBUFFER,renderTargetProperties.__webglRenderbuffer):renderTarget.depthBuffer&&renderTarget.stencilBuffer&&_gl.framebufferRenderbuffer(_gl.FRAMEBUFFER,_gl.DEPTH_STENCIL_ATTACHMENT,_gl.RENDERBUFFER,renderTargetProperties.__webglRenderbuffer):setupRenderBuffer(renderTargetProperties.__webglRenderbuffer,renderTarget),renderTarget.texture.generateMipmaps&&isTargetPowerOfTwo&&_gl.generateMipmap(_gl.TEXTURE_2D);isCube?state.bindTexture(_gl.TEXTURE_CUBE_MAP,null):state.bindTexture(_gl.TEXTURE_2D,null),_gl.bindRenderbuffer(_gl.RENDERBUFFER,null),_gl.bindFramebuffer(_gl.FRAMEBUFFER,null)}var framebuffer,width,height,vx,vy;if(renderTarget){var renderTargetProperties=properties.get(renderTarget);framebuffer=isCube?renderTargetProperties.__webglFramebuffer[renderTarget.activeCubeFace]:renderTargetProperties.__webglFramebuffer,width=renderTarget.width,height=renderTarget.height,vx=0,vy=0}else framebuffer=null,width=_viewportWidth,height=_viewportHeight,vx=_viewportX,vy=_viewportY;if(framebuffer!==_currentFramebuffer&&(_gl.bindFramebuffer(_gl.FRAMEBUFFER,framebuffer),_gl.viewport(vx,vy,width,height),_currentFramebuffer=framebuffer),isCube){var textureProperties=properties.get(renderTarget.texture);_gl.framebufferTexture2D(_gl.FRAMEBUFFER,_gl.COLOR_ATTACHMENT0,_gl.TEXTURE_CUBE_MAP_POSITIVE_X+renderTarget.activeCubeFace,textureProperties.__webglTexture,0)}_currentWidth=width,_currentHeight=height},this.readRenderTargetPixels=function(renderTarget,x,y,width,height,buffer){if(renderTarget instanceof THREE.WebGLRenderTarget==!1)return void console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");var framebuffer=properties.get(renderTarget).__webglFramebuffer;if(framebuffer){var restore=!1;framebuffer!==_currentFramebuffer&&(_gl.bindFramebuffer(_gl.FRAMEBUFFER,framebuffer),restore=!0);try{var texture=renderTarget.texture;if(texture.format!==THREE.RGBAFormat&¶mThreeToGL(texture.format)!==_gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_FORMAT))return void console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.");if(!(texture.type===THREE.UnsignedByteType||paramThreeToGL(texture.type)===_gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_TYPE)||texture.type===THREE.FloatType&&extensions.get("WEBGL_color_buffer_float")||texture.type===THREE.HalfFloatType&&extensions.get("EXT_color_buffer_half_float")))return void console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.");_gl.checkFramebufferStatus(_gl.FRAMEBUFFER)===_gl.FRAMEBUFFER_COMPLETE?_gl.readPixels(x,y,width,height,paramThreeToGL(texture.format),paramThreeToGL(texture.type),buffer):console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.")}finally{restore&&_gl.bindFramebuffer(_gl.FRAMEBUFFER,_currentFramebuffer)}}},this.supportsFloatTextures=function(){return console.warn("THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( 'OES_texture_float' )."),extensions.get("OES_texture_float")},this.supportsHalfFloatTextures=function(){return console.warn("THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( 'OES_texture_half_float' )."),extensions.get("OES_texture_half_float")},this.supportsStandardDerivatives=function(){return console.warn("THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( 'OES_standard_derivatives' )."),extensions.get("OES_standard_derivatives")},this.supportsCompressedTextureS3TC=function(){return console.warn("THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( 'WEBGL_compressed_texture_s3tc' )."),extensions.get("WEBGL_compressed_texture_s3tc")},this.supportsCompressedTexturePVRTC=function(){return console.warn("THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( 'WEBGL_compressed_texture_pvrtc' )."),extensions.get("WEBGL_compressed_texture_pvrtc")},this.supportsBlendMinMax=function(){return console.warn("THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( 'EXT_blend_minmax' )."),extensions.get("EXT_blend_minmax")},this.supportsVertexTextures=function(){return capabilities.vertexTextures},this.supportsInstancedArrays=function(){return console.warn("THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( 'ANGLE_instanced_arrays' )."),extensions.get("ANGLE_instanced_arrays")},this.initMaterial=function(){console.warn("THREE.WebGLRenderer: .initMaterial() has been removed.")},this.addPrePlugin=function(){console.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed.")},this.addPostPlugin=function(){console.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed.")},this.updateShadowMap=function(){console.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed.")},Object.defineProperties(this,{shadowMapEnabled:{get:function(){return shadowMap.enabled},set:function(value){console.warn("THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled."),shadowMap.enabled=value}},shadowMapType:{get:function(){return shadowMap.type},set:function(value){console.warn("THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type."),shadowMap.type=value}},shadowMapCullFace:{get:function(){return shadowMap.cullFace},set:function(value){console.warn("THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace."),shadowMap.cullFace=value}},shadowMapDebug:{get:function(){return shadowMap.debug},set:function(value){console.warn("THREE.WebGLRenderer: .shadowMapDebug is now .shadowMap.debug."),shadowMap.debug=value}}})},THREE.WebGLRenderTarget=function(width,height,options){this.uuid=THREE.Math.generateUUID(),this.width=width,this.height=height,options=options||{},void 0===options.minFilter&&(options.minFilter=THREE.LinearFilter),this.texture=new THREE.Texture(void 0,void 0,options.wrapS,options.wrapT,options.magFilter,options.minFilter,options.format,options.type,options.anisotropy),this.depthBuffer=void 0!==options.depthBuffer?options.depthBuffer:!0,this.stencilBuffer=void 0!==options.stencilBuffer?options.stencilBuffer:!0,this.shareDepthFrom=void 0!==options.shareDepthFrom?options.shareDepthFrom:null},THREE.WebGLRenderTarget.prototype={constructor:THREE.WebGLRenderTarget,get wrapS(){return console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."),this.texture.wrapS},set wrapS(value){console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."),this.texture.wrapS=value},get wrapT(){return console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."),this.texture.wrapT},set wrapT(value){console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."),this.texture.wrapT=value},get magFilter(){return console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."),this.texture.magFilter},set magFilter(value){console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."),this.texture.magFilter=value},get minFilter(){return console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."),this.texture.minFilter},set minFilter(value){console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."),this.texture.minFilter=value},get anisotropy(){return console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."),this.texture.anisotropy},set anisotropy(value){console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."),this.texture.anisotropy=value},get offset(){return console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."),this.texture.offset},set offset(value){console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."),this.texture.offset=value},get repeat(){return console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."),this.texture.repeat},set repeat(value){console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."),this.texture.repeat=value},get format(){return console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."),this.texture.format},set format(value){console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."),this.texture.format=value},get type(){return console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."),this.texture.type},set type(value){console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."),this.texture.type=value},get generateMipmaps(){return console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."),this.texture.generateMipmaps},set generateMipmaps(value){console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."),this.texture.generateMipmaps=value},setSize:function(width,height){this.width===width&&this.height===height||(this.width=width,this.height=height,this.dispose())},clone:function(){return(new this.constructor).copy(this)},copy:function(source){return this.width=source.width,this.height=source.height,this.texture=source.texture.clone(),this.depthBuffer=source.depthBuffer,this.stencilBuffer=source.stencilBuffer,this.shareDepthFrom=source.shareDepthFrom,this},dispose:function(){this.dispatchEvent({type:"dispose"})}},THREE.EventDispatcher.prototype.apply(THREE.WebGLRenderTarget.prototype),THREE.WebGLRenderTargetCube=function(width,height,options){THREE.WebGLRenderTarget.call(this,width,height,options),this.activeCubeFace=0},THREE.WebGLRenderTargetCube.prototype=Object.create(THREE.WebGLRenderTarget.prototype),THREE.WebGLRenderTargetCube.prototype.constructor=THREE.WebGLRenderTargetCube,THREE.WebGLBufferRenderer=function(_gl,extensions,_infoRender){function setMode(value){mode=value}function render(start,count){_gl.drawArrays(mode,start,count),_infoRender.calls++,_infoRender.vertices+=count,mode===_gl.TRIANGLES&&(_infoRender.faces+=count/3)}function renderInstances(geometry){var extension=extensions.get("ANGLE_instanced_arrays");if(null===extension)return void console.error("THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.");var position=geometry.attributes.position;position instanceof THREE.InterleavedBufferAttribute?extension.drawArraysInstancedANGLE(mode,0,position.data.count,geometry.maxInstancedCount):extension.drawArraysInstancedANGLE(mode,0,position.count,geometry.maxInstancedCount)}var mode;this.setMode=setMode,this.render=render,this.renderInstances=renderInstances},THREE.WebGLIndexedBufferRenderer=function(_gl,extensions,_infoRender){function setMode(value){mode=value}function setIndex(index){index.array instanceof Uint32Array&&extensions.get("OES_element_index_uint")?(type=_gl.UNSIGNED_INT,size=4):(type=_gl.UNSIGNED_SHORT,size=2)}function render(start,count){_gl.drawElements(mode,count,type,start*size),_infoRender.calls++,_infoRender.vertices+=count,mode===_gl.TRIANGLES&&(_infoRender.faces+=count/3)}function renderInstances(geometry){var extension=extensions.get("ANGLE_instanced_arrays");if(null===extension)return void console.error("THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.");var index=geometry.index;extension.drawElementsInstancedANGLE(mode,index.array.length,type,0,geometry.maxInstancedCount)}var mode,type,size;this.setMode=setMode,this.setIndex=setIndex,this.render=render,this.renderInstances=renderInstances},THREE.WebGLExtensions=function(gl){var extensions={};this.get=function(name){if(void 0!==extensions[name])return extensions[name];var extension;switch(name){case"EXT_texture_filter_anisotropic":extension=gl.getExtension("EXT_texture_filter_anisotropic")||gl.getExtension("MOZ_EXT_texture_filter_anisotropic")||gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic");break;case"WEBGL_compressed_texture_s3tc":extension=gl.getExtension("WEBGL_compressed_texture_s3tc")||gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc")||gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc");break;case"WEBGL_compressed_texture_pvrtc":extension=gl.getExtension("WEBGL_compressed_texture_pvrtc")||gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc");break;default:extension=gl.getExtension(name)}return null===extension&&console.warn("THREE.WebGLRenderer: "+name+" extension not supported."),extensions[name]=extension,extension}},THREE.WebGLCapabilities=function(gl,extensions,parameters){function getMaxPrecision(precision){if("highp"===precision){if(gl.getShaderPrecisionFormat(gl.VERTEX_SHADER,gl.HIGH_FLOAT).precision>0&&gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER,gl.HIGH_FLOAT).precision>0)return"highp";precision="mediump"}return"mediump"===precision&&gl.getShaderPrecisionFormat(gl.VERTEX_SHADER,gl.MEDIUM_FLOAT).precision>0&&gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER,gl.MEDIUM_FLOAT).precision>0?"mediump":"lowp"}this.getMaxPrecision=getMaxPrecision,this.precision=void 0!==parameters.precision?parameters.precision:"highp",this.logarithmicDepthBuffer=void 0!==parameters.logarithmicDepthBuffer?parameters.logarithmicDepthBuffer:!1,this.maxTextures=gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS),this.maxVertexTextures=gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS),this.maxTextureSize=gl.getParameter(gl.MAX_TEXTURE_SIZE),this.maxCubemapSize=gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE),this.maxAttributes=gl.getParameter(gl.MAX_VERTEX_ATTRIBS),this.maxVertexUniforms=gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS),this.maxVaryings=gl.getParameter(gl.MAX_VARYING_VECTORS),this.maxFragmentUniforms=gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS),this.vertexTextures=this.maxVertexTextures>0,this.floatFragmentTextures=!!extensions.get("OES_texture_float"),this.floatVertexTextures=this.vertexTextures&&this.floatFragmentTextures;var _maxPrecision=getMaxPrecision(this.precision);_maxPrecision!==this.precision&&(console.warn("THREE.WebGLRenderer:",this.precision,"not supported, using",_maxPrecision,"instead."),this.precision=_maxPrecision),this.logarithmicDepthBuffer&&(this.logarithmicDepthBuffer=!!extensions.get("EXT_frag_depth"))},THREE.WebGLGeometries=function(gl,properties,info){function get(object){var geometry=object.geometry;if(void 0!==geometries[geometry.id])return geometries[geometry.id];geometry.addEventListener("dispose",onGeometryDispose);var buffergeometry;return geometry instanceof THREE.BufferGeometry?buffergeometry=geometry:geometry instanceof THREE.Geometry&&(void 0===geometry._bufferGeometry&&(geometry._bufferGeometry=(new THREE.BufferGeometry).setFromObject(object)),buffergeometry=geometry._bufferGeometry),geometries[geometry.id]=buffergeometry,info.memory.geometries++,buffergeometry}function onGeometryDispose(event){var geometry=event.target,buffergeometry=geometries[geometry.id];deleteAttributes(buffergeometry.attributes),geometry.removeEventListener("dispose",onGeometryDispose),delete geometries[geometry.id];var property=properties.get(geometry);property.wireframe&&deleteAttribute(property.wireframe),info.memory.geometries--}function getAttributeBuffer(attribute){return attribute instanceof THREE.InterleavedBufferAttribute?properties.get(attribute.data).__webglBuffer:properties.get(attribute).__webglBuffer}function deleteAttribute(attribute){var buffer=getAttributeBuffer(attribute);void 0!==buffer&&(gl.deleteBuffer(buffer),removeAttributeBuffer(attribute))}function deleteAttributes(attributes){for(var name in attributes)deleteAttribute(attributes[name])}function removeAttributeBuffer(attribute){attribute instanceof THREE.InterleavedBufferAttribute?properties["delete"](attribute.data):properties["delete"](attribute)}var geometries={};this.get=get},THREE.WebGLObjects=function(gl,properties,info){function update(object){var geometry=geometries.get(object);object.geometry instanceof THREE.Geometry&&geometry.updateFromObject(object);var index=geometry.index,attributes=geometry.attributes;null!==index&&updateAttribute(index,gl.ELEMENT_ARRAY_BUFFER);for(var name in attributes)updateAttribute(attributes[name],gl.ARRAY_BUFFER);var morphAttributes=geometry.morphAttributes;for(var name in morphAttributes)for(var array=morphAttributes[name],i=0,l=array.length;l>i;i++)updateAttribute(array[i],gl.ARRAY_BUFFER);return geometry}function updateAttribute(attribute,bufferType){var data=attribute instanceof THREE.InterleavedBufferAttribute?attribute.data:attribute,attributeProperties=properties.get(data);void 0===attributeProperties.__webglBuffer?createBuffer(attributeProperties,data,bufferType):attributeProperties.version!==data.version&&updateBuffer(attributeProperties,data,bufferType)}function createBuffer(attributeProperties,data,bufferType){attributeProperties.__webglBuffer=gl.createBuffer(),gl.bindBuffer(bufferType,attributeProperties.__webglBuffer);var usage=data.dynamic?gl.DYNAMIC_DRAW:gl.STATIC_DRAW;gl.bufferData(bufferType,data.array,usage),attributeProperties.version=data.version}function updateBuffer(attributeProperties,data,bufferType){gl.bindBuffer(bufferType,attributeProperties.__webglBuffer),data.dynamic===!1||-1===data.updateRange.count?gl.bufferSubData(bufferType,0,data.array):0===data.updateRange.count?console.error("THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually."):(gl.bufferSubData(bufferType,data.updateRange.offset*data.array.BYTES_PER_ELEMENT,data.array.subarray(data.updateRange.offset,data.updateRange.offset+data.updateRange.count)),data.updateRange.count=0),attributeProperties.version=data.version}function getAttributeBuffer(attribute){return attribute instanceof THREE.InterleavedBufferAttribute?properties.get(attribute.data).__webglBuffer:properties.get(attribute).__webglBuffer}function getWireframeAttribute(geometry){var property=properties.get(geometry);if(void 0!==property.wireframe)return property.wireframe;var indices=[],index=geometry.index,attributes=geometry.attributes,position=attributes.position;if(null!==index)for(var edges={},array=index.array,i=0,l=array.length;l>i;i+=3){var a=array[i+0],b=array[i+1],c=array[i+2];checkEdge(edges,a,b)&&indices.push(a,b),checkEdge(edges,b,c)&&indices.push(b,c),checkEdge(edges,c,a)&&indices.push(c,a)}else for(var array=attributes.position.array,i=0,l=array.length/3-1;l>i;i+=3){var a=i+0,b=i+1,c=i+2;indices.push(a,b,b,c,c,a)}var TypeArray=position.count>65535?Uint32Array:Uint16Array,attribute=new THREE.BufferAttribute(new TypeArray(indices),1);return updateAttribute(attribute,gl.ELEMENT_ARRAY_BUFFER),property.wireframe=attribute,attribute}function checkEdge(edges,a,b){if(a>b){var tmp=a;a=b,b=tmp}var list=edges[a];return void 0===list?(edges[a]=[b],!0):-1===list.indexOf(b)?(list.push(b),!0):!1}var geometries=new THREE.WebGLGeometries(gl,properties,info);this.getAttributeBuffer=getAttributeBuffer,this.getWireframeAttribute=getWireframeAttribute,this.update=update},THREE.WebGLProgram=function(){function generateDefines(defines){var chunks=[];for(var name in defines){var value=defines[name];value!==!1&&chunks.push("#define "+name+" "+value)}return chunks.join("\n")}function fetchUniformLocations(gl,program,identifiers){for(var uniforms={},n=gl.getProgramParameter(program,gl.ACTIVE_UNIFORMS),i=0;n>i;i++){var info=gl.getActiveUniform(program,i),name=info.name,location=gl.getUniformLocation(program,name),suffixPos=name.lastIndexOf("[0]");-1!==suffixPos&&suffixPos===name.length-3&&(uniforms[name.substr(0,suffixPos)]=location),uniforms[name]=location}return uniforms}function fetchAttributeLocations(gl,program,identifiers){for(var attributes={},n=gl.getProgramParameter(program,gl.ACTIVE_ATTRIBUTES),i=0;n>i;i++){var info=gl.getActiveAttrib(program,i),name=info.name;attributes[name]=gl.getAttribLocation(program,name)}return attributes}function filterEmptyLine(string){return""!==string}var programIdCount=0;return function(renderer,code,material,parameters){var gl=renderer.context,defines=material.defines,vertexShader=material.__webglShader.vertexShader,fragmentShader=material.__webglShader.fragmentShader,shadowMapTypeDefine="SHADOWMAP_TYPE_BASIC";parameters.shadowMapType===THREE.PCFShadowMap?shadowMapTypeDefine="SHADOWMAP_TYPE_PCF":parameters.shadowMapType===THREE.PCFSoftShadowMap&&(shadowMapTypeDefine="SHADOWMAP_TYPE_PCF_SOFT");var envMapTypeDefine="ENVMAP_TYPE_CUBE",envMapModeDefine="ENVMAP_MODE_REFLECTION",envMapBlendingDefine="ENVMAP_BLENDING_MULTIPLY";if(parameters.envMap){switch(material.envMap.mapping){case THREE.CubeReflectionMapping:case THREE.CubeRefractionMapping:envMapTypeDefine="ENVMAP_TYPE_CUBE";break;case THREE.EquirectangularReflectionMapping:case THREE.EquirectangularRefractionMapping:envMapTypeDefine="ENVMAP_TYPE_EQUIREC";break;case THREE.SphericalReflectionMapping:envMapTypeDefine="ENVMAP_TYPE_SPHERE"}switch(material.envMap.mapping){case THREE.CubeRefractionMapping:case THREE.EquirectangularRefractionMapping:envMapModeDefine="ENVMAP_MODE_REFRACTION"}switch(material.combine){case THREE.MultiplyOperation:envMapBlendingDefine="ENVMAP_BLENDING_MULTIPLY";break;case THREE.MixOperation:envMapBlendingDefine="ENVMAP_BLENDING_MIX";break;case THREE.AddOperation:envMapBlendingDefine="ENVMAP_BLENDING_ADD"}}var prefixVertex,prefixFragment,gammaFactorDefine=renderer.gammaFactor>0?renderer.gammaFactor:1,customDefines=generateDefines(defines),program=gl.createProgram();material instanceof THREE.RawShaderMaterial?(prefixVertex="",prefixFragment=""):(prefixVertex=["precision "+parameters.precision+" float;","precision "+parameters.precision+" int;","#define SHADER_NAME "+material.__webglShader.name,customDefines,parameters.supportsVertexTextures?"#define VERTEX_TEXTURES":"",renderer.gammaInput?"#define GAMMA_INPUT":"",renderer.gammaOutput?"#define GAMMA_OUTPUT":"","#define GAMMA_FACTOR "+gammaFactorDefine,"#define MAX_DIR_LIGHTS "+parameters.maxDirLights,"#define MAX_POINT_LIGHTS "+parameters.maxPointLights,"#define MAX_SPOT_LIGHTS "+parameters.maxSpotLights,"#define MAX_HEMI_LIGHTS "+parameters.maxHemiLights,"#define MAX_SHADOWS "+parameters.maxShadows,"#define MAX_BONES "+parameters.maxBones,parameters.map?"#define USE_MAP":"",parameters.envMap?"#define USE_ENVMAP":"",parameters.envMap?"#define "+envMapModeDefine:"",parameters.lightMap?"#define USE_LIGHTMAP":"",parameters.aoMap?"#define USE_AOMAP":"",parameters.emissiveMap?"#define USE_EMISSIVEMAP":"",parameters.bumpMap?"#define USE_BUMPMAP":"",parameters.normalMap?"#define USE_NORMALMAP":"",parameters.displacementMap&¶meters.supportsVertexTextures?"#define USE_DISPLACEMENTMAP":"",parameters.specularMap?"#define USE_SPECULARMAP":"",parameters.alphaMap?"#define USE_ALPHAMAP":"",parameters.vertexColors?"#define USE_COLOR":"",parameters.flatShading?"#define FLAT_SHADED":"",parameters.skinning?"#define USE_SKINNING":"",parameters.useVertexTexture?"#define BONE_TEXTURE":"",parameters.morphTargets?"#define USE_MORPHTARGETS":"",parameters.morphNormals&¶meters.flatShading===!1?"#define USE_MORPHNORMALS":"",parameters.doubleSided?"#define DOUBLE_SIDED":"",parameters.flipSided?"#define FLIP_SIDED":"",parameters.shadowMapEnabled?"#define USE_SHADOWMAP":"",parameters.shadowMapEnabled?"#define "+shadowMapTypeDefine:"",parameters.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",parameters.pointLightShadows>0?"#define POINT_LIGHT_SHADOWS":"",parameters.sizeAttenuation?"#define USE_SIZEATTENUATION":"",parameters.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",parameters.logarithmicDepthBuffer&&renderer.extensions.get("EXT_frag_depth")?"#define USE_LOGDEPTHBUF_EXT":"","uniform mat4 modelMatrix;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform mat4 viewMatrix;","uniform mat3 normalMatrix;","uniform vec3 cameraPosition;","attribute vec3 position;","attribute vec3 normal;","attribute vec2 uv;","#ifdef USE_COLOR"," attribute vec3 color;","#endif","#ifdef USE_MORPHTARGETS"," attribute vec3 morphTarget0;"," attribute vec3 morphTarget1;"," attribute vec3 morphTarget2;"," attribute vec3 morphTarget3;"," #ifdef USE_MORPHNORMALS"," attribute vec3 morphNormal0;"," attribute vec3 morphNormal1;"," attribute vec3 morphNormal2;"," attribute vec3 morphNormal3;"," #else"," attribute vec3 morphTarget4;"," attribute vec3 morphTarget5;"," attribute vec3 morphTarget6;"," attribute vec3 morphTarget7;"," #endif","#endif","#ifdef USE_SKINNING"," attribute vec4 skinIndex;"," attribute vec4 skinWeight;","#endif","\n"].filter(filterEmptyLine).join("\n"), +prefixFragment=[parameters.bumpMap||parameters.normalMap||parameters.flatShading||material.derivatives?"#extension GL_OES_standard_derivatives : enable":"",parameters.logarithmicDepthBuffer&&renderer.extensions.get("EXT_frag_depth")?"#extension GL_EXT_frag_depth : enable":"","precision "+parameters.precision+" float;","precision "+parameters.precision+" int;","#define SHADER_NAME "+material.__webglShader.name,customDefines,"#define MAX_DIR_LIGHTS "+parameters.maxDirLights,"#define MAX_POINT_LIGHTS "+parameters.maxPointLights,"#define MAX_SPOT_LIGHTS "+parameters.maxSpotLights,"#define MAX_HEMI_LIGHTS "+parameters.maxHemiLights,"#define MAX_SHADOWS "+parameters.maxShadows,parameters.alphaTest?"#define ALPHATEST "+parameters.alphaTest:"",renderer.gammaInput?"#define GAMMA_INPUT":"",renderer.gammaOutput?"#define GAMMA_OUTPUT":"","#define GAMMA_FACTOR "+gammaFactorDefine,parameters.useFog&¶meters.fog?"#define USE_FOG":"",parameters.useFog&¶meters.fogExp?"#define FOG_EXP2":"",parameters.map?"#define USE_MAP":"",parameters.envMap?"#define USE_ENVMAP":"",parameters.envMap?"#define "+envMapTypeDefine:"",parameters.envMap?"#define "+envMapModeDefine:"",parameters.envMap?"#define "+envMapBlendingDefine:"",parameters.lightMap?"#define USE_LIGHTMAP":"",parameters.aoMap?"#define USE_AOMAP":"",parameters.emissiveMap?"#define USE_EMISSIVEMAP":"",parameters.bumpMap?"#define USE_BUMPMAP":"",parameters.normalMap?"#define USE_NORMALMAP":"",parameters.specularMap?"#define USE_SPECULARMAP":"",parameters.alphaMap?"#define USE_ALPHAMAP":"",parameters.vertexColors?"#define USE_COLOR":"",parameters.flatShading?"#define FLAT_SHADED":"",parameters.metal?"#define METAL":"",parameters.doubleSided?"#define DOUBLE_SIDED":"",parameters.flipSided?"#define FLIP_SIDED":"",parameters.shadowMapEnabled?"#define USE_SHADOWMAP":"",parameters.shadowMapEnabled?"#define "+shadowMapTypeDefine:"",parameters.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",parameters.pointLightShadows>0?"#define POINT_LIGHT_SHADOWS":"",parameters.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",parameters.logarithmicDepthBuffer&&renderer.extensions.get("EXT_frag_depth")?"#define USE_LOGDEPTHBUF_EXT":"","uniform mat4 viewMatrix;","uniform vec3 cameraPosition;","\n"].filter(filterEmptyLine).join("\n"));var vertexGlsl=prefixVertex+vertexShader,fragmentGlsl=prefixFragment+fragmentShader,glVertexShader=THREE.WebGLShader(gl,gl.VERTEX_SHADER,vertexGlsl),glFragmentShader=THREE.WebGLShader(gl,gl.FRAGMENT_SHADER,fragmentGlsl);gl.attachShader(program,glVertexShader),gl.attachShader(program,glFragmentShader),void 0!==material.index0AttributeName?gl.bindAttribLocation(program,0,material.index0AttributeName):parameters.morphTargets===!0&&gl.bindAttribLocation(program,0,"position"),gl.linkProgram(program);var programLog=gl.getProgramInfoLog(program),vertexLog=gl.getShaderInfoLog(glVertexShader),fragmentLog=gl.getShaderInfoLog(glFragmentShader),runnable=!0,haveDiagnostics=!0;gl.getProgramParameter(program,gl.LINK_STATUS)===!1?(runnable=!1,console.error("THREE.WebGLProgram: shader error: ",gl.getError(),"gl.VALIDATE_STATUS",gl.getProgramParameter(program,gl.VALIDATE_STATUS),"gl.getProgramInfoLog",programLog,vertexLog,fragmentLog)):""!==programLog?console.warn("THREE.WebGLProgram: gl.getProgramInfoLog()",programLog):""!==vertexLog&&""!==fragmentLog||(haveDiagnostics=!1),haveDiagnostics&&(this.diagnostics={runnable:runnable,material:material,programLog:programLog,vertexShader:{log:vertexLog,prefix:prefixVertex},fragmentShader:{log:fragmentLog,prefix:prefixFragment}}),gl.deleteShader(glVertexShader),gl.deleteShader(glFragmentShader);var cachedUniforms;this.getUniforms=function(){return void 0===cachedUniforms&&(cachedUniforms=fetchUniformLocations(gl,program)),cachedUniforms};var cachedAttributes;return this.getAttributes=function(){return void 0===cachedAttributes&&(cachedAttributes=fetchAttributeLocations(gl,program)),cachedAttributes},this.destroy=function(){gl.deleteProgram(program),this.program=void 0},Object.defineProperties(this,{uniforms:{get:function(){return console.warn("THREE.WebGLProgram: .uniforms is now .getUniforms()."),this.getUniforms()}},attributes:{get:function(){return console.warn("THREE.WebGLProgram: .attributes is now .getAttributes()."),this.getAttributes()}}}),this.id=programIdCount++,this.code=code,this.usedTimes=1,this.program=program,this.vertexShader=glVertexShader,this.fragmentShader=glFragmentShader,this}}(),THREE.WebGLPrograms=function(renderer,capabilities){function allocateBones(object){if(capabilities.floatVertexTextures&&object&&object.skeleton&&object.skeleton.useVertexTexture)return 1024;var nVertexUniforms=capabilities.maxVertexUniforms,nVertexMatrices=Math.floor((nVertexUniforms-20)/4),maxBones=nVertexMatrices;return void 0!==object&&object instanceof THREE.SkinnedMesh&&(maxBones=Math.min(object.skeleton.bones.length,maxBones),maxBonesl;l++){var light=lights[l];light.visible!==!1&&(light instanceof THREE.DirectionalLight&&dirLights++,light instanceof THREE.PointLight&&pointLights++,light instanceof THREE.SpotLight&&spotLights++,light instanceof THREE.HemisphereLight&&hemiLights++)}return{directional:dirLights,point:pointLights,spot:spotLights,hemi:hemiLights}}function allocateShadows(lights){for(var maxShadows=0,pointLightShadows=0,l=0,ll=lights.length;ll>l;l++){var light=lights[l];light.castShadow&&((light instanceof THREE.SpotLight||light instanceof THREE.DirectionalLight)&&maxShadows++,light instanceof THREE.PointLight&&(maxShadows++,pointLightShadows++))}return{maxShadows:maxShadows,pointLightShadows:pointLightShadows}}var programs=[],shaderIDs={MeshDepthMaterial:"depth",MeshNormalMaterial:"normal",MeshBasicMaterial:"basic",MeshLambertMaterial:"lambert",MeshPhongMaterial:"phong",LineBasicMaterial:"basic",LineDashedMaterial:"dashed",PointsMaterial:"points"},parameterNames=["precision","supportsVertexTextures","map","envMap","envMapMode","lightMap","aoMap","emissiveMap","bumpMap","normalMap","displacementMap","specularMap","alphaMap","combine","vertexColors","fog","useFog","fogExp","flatShading","sizeAttenuation","logarithmicDepthBuffer","skinning","maxBones","useVertexTexture","morphTargets","morphNormals","maxMorphTargets","maxMorphNormals","maxDirLights","maxPointLights","maxSpotLights","maxHemiLights","maxShadows","shadowMapEnabled","pointLightShadows","shadowMapType","shadowMapDebug","alphaTest","metal","doubleSided","flipSided"];this.getParameters=function(material,lights,fog,object){var shaderID=shaderIDs[material.type],maxLightCount=allocateLights(lights),allocatedShadows=allocateShadows(lights),maxBones=allocateBones(object),precision=renderer.getPrecision();null!==material.precision&&(precision=capabilities.getMaxPrecision(material.precision),precision!==material.precision&&console.warn("THREE.WebGLRenderer.initMaterial:",material.precision,"not supported, using",precision,"instead."));var parameters={shaderID:shaderID,precision:precision,supportsVertexTextures:capabilities.vertexTextures,map:!!material.map,envMap:!!material.envMap,envMapMode:material.envMap&&material.envMap.mapping,lightMap:!!material.lightMap,aoMap:!!material.aoMap,emissiveMap:!!material.emissiveMap,bumpMap:!!material.bumpMap,normalMap:!!material.normalMap,displacementMap:!!material.displacementMap,specularMap:!!material.specularMap,alphaMap:!!material.alphaMap,combine:material.combine,vertexColors:material.vertexColors,fog:fog,useFog:material.fog,fogExp:fog instanceof THREE.FogExp2,flatShading:material.shading===THREE.FlatShading,sizeAttenuation:material.sizeAttenuation,logarithmicDepthBuffer:capabilities.logarithmicDepthBuffer,skinning:material.skinning,maxBones:maxBones,useVertexTexture:capabilities.floatVertexTextures&&object&&object.skeleton&&object.skeleton.useVertexTexture,morphTargets:material.morphTargets,morphNormals:material.morphNormals,maxMorphTargets:renderer.maxMorphTargets,maxMorphNormals:renderer.maxMorphNormals,maxDirLights:maxLightCount.directional,maxPointLights:maxLightCount.point,maxSpotLights:maxLightCount.spot,maxHemiLights:maxLightCount.hemi,maxShadows:allocatedShadows.maxShadows,pointLightShadows:allocatedShadows.pointLightShadows,shadowMapEnabled:renderer.shadowMap.enabled&&object.receiveShadow&&allocatedShadows.maxShadows>0,shadowMapType:renderer.shadowMap.type,shadowMapDebug:renderer.shadowMap.debug,alphaTest:material.alphaTest,metal:material.metal,doubleSided:material.side===THREE.DoubleSide,flipSided:material.side===THREE.BackSide};return parameters},this.getProgramCode=function(material,parameters){var chunks=[];if(parameters.shaderID?chunks.push(parameters.shaderID):(chunks.push(material.fragmentShader),chunks.push(material.vertexShader)),void 0!==material.defines)for(var name in material.defines)chunks.push(name),chunks.push(material.defines[name]);for(var i=0;ip;p++){var programInfo=programs[p];if(programInfo.code===code){program=programInfo,++program.usedTimes;break}}return void 0===program&&(program=new THREE.WebGLProgram(renderer,code,material,parameters),programs.push(program)),program},this.releaseProgram=function(program){if(0===--program.usedTimes){var i=programs.indexOf(program);programs[i]=programs[programs.length-1],programs.pop(),program.destroy()}},this.programs=programs},THREE.WebGLProperties=function(){var properties={};this.get=function(object){var uuid=object.uuid,map=properties[uuid];return void 0===map&&(map={},properties[uuid]=map),map},this["delete"]=function(object){delete properties[object.uuid]},this.clear=function(){properties={}}},THREE.WebGLShader=function(){function addLineNumbers(string){for(var lines=string.split("\n"),i=0;i0&&material.morphTargets,useSkinning=object instanceof THREE.SkinnedMesh&&material.skinning,variantIndex=0;useMorphing&&(variantIndex|=_MorphingFlag),useSkinning&&(variantIndex|=_SkinningFlag),newMaterial=materialVariants[variantIndex]}return newMaterial.visible=material.visible,newMaterial.wireframe=material.wireframe,newMaterial.wireframeLinewidth=material.wireframeLinewidth,isPointLight&&void 0!==newMaterial.uniforms.lightPos&&newMaterial.uniforms.lightPos.value.copy(lightPositionWorld),newMaterial}function projectObject(object,camera){if(object.visible!==!1){if((object instanceof THREE.Mesh||object instanceof THREE.Line||object instanceof THREE.Points)&&object.castShadow&&(object.frustumCulled===!1||_frustum.intersectsObject(object)===!0)){var material=object.material;material.visible===!0&&(object.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse,object.matrixWorld),_renderList.push(object))}for(var children=object.children,i=0,l=children.length;l>i;i++)projectObject(children[i],camera)}}for(var _gl=_renderer.context,_state=_renderer.state,_frustum=new THREE.Frustum,_projScreenMatrix=new THREE.Matrix4,_lookTarget=(new THREE.Vector3,new THREE.Vector3,new THREE.Vector3),_lightPositionWorld=new THREE.Vector3,_renderList=[],_MorphingFlag=1,_SkinningFlag=2,_NumberOfMaterialVariants=(_MorphingFlag|_SkinningFlag)+1,_depthMaterials=new Array(_NumberOfMaterialVariants),_distanceMaterials=new Array(_NumberOfMaterialVariants),cubeDirections=[new THREE.Vector3(1,0,0),new THREE.Vector3(-1,0,0),new THREE.Vector3(0,0,1),new THREE.Vector3(0,0,-1),new THREE.Vector3(0,1,0),new THREE.Vector3(0,-1,0)],cubeUps=[new THREE.Vector3(0,1,0),new THREE.Vector3(0,1,0),new THREE.Vector3(0,1,0),new THREE.Vector3(0,1,0),new THREE.Vector3(0,0,1),new THREE.Vector3(0,0,-1)],cube2DViewPorts=[new THREE.Vector4,new THREE.Vector4,new THREE.Vector4,new THREE.Vector4,new THREE.Vector4,new THREE.Vector4],_vector4=new THREE.Vector4,depthShader=THREE.ShaderLib.depthRGBA,depthUniforms=THREE.UniformsUtils.clone(depthShader.uniforms),distanceShader=THREE.ShaderLib.distanceRGBA,distanceUniforms=THREE.UniformsUtils.clone(distanceShader.uniforms),i=0;i!==_NumberOfMaterialVariants;++i){var useMorphing=0!==(i&_MorphingFlag),useSkinning=0!==(i&_SkinningFlag),depthMaterial=new THREE.ShaderMaterial({uniforms:depthUniforms,vertexShader:depthShader.vertexShader,fragmentShader:depthShader.fragmentShader,morphTargets:useMorphing,skinning:useSkinning});depthMaterial._shadowPass=!0,_depthMaterials[i]=depthMaterial;var distanceMaterial=new THREE.ShaderMaterial({uniforms:distanceUniforms,vertexShader:distanceShader.vertexShader,fragmentShader:distanceShader.fragmentShader,morphTargets:useMorphing,skinning:useSkinning});distanceMaterial._shadowPass=!0,_distanceMaterials[i]=distanceMaterial}var scope=this;this.enabled=!1,this.autoUpdate=!0,this.needsUpdate=!1,this.type=THREE.PCFShadowMap,this.cullFace=THREE.CullFaceFront,this.render=function(scene){var faceCount,isPointLight;if(scope.enabled!==!1&&(scope.autoUpdate!==!1||scope.needsUpdate!==!1)){_gl.clearColor(1,1,1,1),_state.disable(_gl.BLEND),_state.enable(_gl.CULL_FACE),_gl.frontFace(_gl.CCW),_gl.cullFace(scope.cullFace===THREE.CullFaceFront?_gl.FRONT:_gl.BACK),_state.setDepthTest(!0),_renderer.getViewport(_vector4);for(var i=0,il=_lights.length;il>i;i++){var light=_lights[i];if(light.castShadow===!0){var shadow=light.shadow,shadowCamera=shadow.camera,shadowMapSize=shadow.mapSize;if(light instanceof THREE.PointLight){faceCount=6,isPointLight=!0;var vpWidth=shadowMapSize.x/4,vpHeight=shadowMapSize.y/2;cube2DViewPorts[0].set(2*vpWidth,vpHeight,vpWidth,vpHeight),cube2DViewPorts[1].set(0,vpHeight,vpWidth,vpHeight),cube2DViewPorts[2].set(3*vpWidth,vpHeight,vpWidth,vpHeight),cube2DViewPorts[3].set(vpWidth,vpHeight,vpWidth,vpHeight),cube2DViewPorts[4].set(3*vpWidth,0,vpWidth,vpHeight),cube2DViewPorts[5].set(vpWidth,0,vpWidth,vpHeight)}else faceCount=1,isPointLight=!1;if(null===shadow.map){var shadowFilter=THREE.LinearFilter;scope.type===THREE.PCFSoftShadowMap&&(shadowFilter=THREE.NearestFilter);var pars={minFilter:shadowFilter,magFilter:shadowFilter,format:THREE.RGBAFormat};shadow.map=new THREE.WebGLRenderTarget(shadowMapSize.x,shadowMapSize.y,pars),shadow.matrix=new THREE.Matrix4,light instanceof THREE.SpotLight&&(shadowCamera.aspect=shadowMapSize.x/shadowMapSize.y),shadowCamera.updateProjectionMatrix()}var shadowMap=shadow.map,shadowMatrix=shadow.matrix;_lightPositionWorld.setFromMatrixPosition(light.matrixWorld),shadowCamera.position.copy(_lightPositionWorld),_renderer.setRenderTarget(shadowMap),_renderer.clear();for(var face=0;faceCount>face;face++){if(isPointLight){_lookTarget.copy(shadowCamera.position),_lookTarget.add(cubeDirections[face]),shadowCamera.up.copy(cubeUps[face]),shadowCamera.lookAt(_lookTarget);var vpDimensions=cube2DViewPorts[face];_renderer.setViewport(vpDimensions.x,vpDimensions.y,vpDimensions.z,vpDimensions.w)}else _lookTarget.setFromMatrixPosition(light.target.matrixWorld),shadowCamera.lookAt(_lookTarget);shadowCamera.updateMatrixWorld(),shadowCamera.matrixWorldInverse.getInverse(shadowCamera.matrixWorld),shadowMatrix.set(.5,0,0,.5,0,.5,0,.5,0,0,.5,.5,0,0,0,1),shadowMatrix.multiply(shadowCamera.projectionMatrix),shadowMatrix.multiply(shadowCamera.matrixWorldInverse),_projScreenMatrix.multiplyMatrices(shadowCamera.projectionMatrix,shadowCamera.matrixWorldInverse),_frustum.setFromMatrix(_projScreenMatrix),_renderList.length=0,projectObject(scene,shadowCamera);for(var j=0,jl=_renderList.length;jl>j;j++){var object=_renderList[j],geometry=_objects.update(object),material=object.material;if(material instanceof THREE.MeshFaceMaterial)for(var groups=geometry.groups,materials=material.materials,k=0,kl=groups.length;kl>k;k++){var group=groups[k],groupMaterial=materials[group.materialIndex];if(groupMaterial.visible===!0){var depthMaterial=getDepthMaterial(object,groupMaterial,isPointLight,_lightPositionWorld);_renderer.renderBufferDirect(shadowCamera,_lights,null,geometry,depthMaterial,object,group)}}else{var depthMaterial=getDepthMaterial(object,material,isPointLight,_lightPositionWorld);_renderer.renderBufferDirect(shadowCamera,_lights,null,geometry,depthMaterial,object,null)}}}_renderer.resetGLState()}}_renderer.setViewport(_vector4.x,_vector4.y,_vector4.z,_vector4.w);var clearColor=_renderer.getClearColor(),clearAlpha=_renderer.getClearAlpha();_renderer.setClearColor(clearColor,clearAlpha),_state.enable(_gl.BLEND),scope.cullFace===THREE.CullFaceFront&&_gl.cullFace(_gl.BACK),_renderer.resetGLState(),scope.needsUpdate=!1}}},THREE.WebGLState=function(gl,extensions,paramThreeToGL){var _this=this,newAttributes=new Uint8Array(16),enabledAttributes=new Uint8Array(16),attributeDivisors=new Uint8Array(16),capabilities={},compressedTextureFormats=null,currentBlending=null,currentBlendEquation=null,currentBlendSrc=null,currentBlendDst=null,currentBlendEquationAlpha=null,currentBlendSrcAlpha=null,currentBlendDstAlpha=null,currentDepthFunc=null,currentDepthWrite=null,currentColorWrite=null,currentFlipSided=null,currentLineWidth=null,currentPolygonOffsetFactor=null,currentPolygonOffsetUnits=null,maxTextures=gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS),currentTextureSlot=void 0,currentBoundTextures={};this.init=function(){gl.clearColor(0,0,0,1),gl.clearDepth(1),gl.clearStencil(0),this.enable(gl.DEPTH_TEST),gl.depthFunc(gl.LEQUAL),gl.frontFace(gl.CCW),gl.cullFace(gl.BACK),this.enable(gl.CULL_FACE),this.enable(gl.BLEND),gl.blendEquation(gl.FUNC_ADD),gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA)},this.initAttributes=function(){for(var i=0,l=newAttributes.length;l>i;i++)newAttributes[i]=0},this.enableAttribute=function(attribute){if(newAttributes[attribute]=1,0===enabledAttributes[attribute]&&(gl.enableVertexAttribArray(attribute),enabledAttributes[attribute]=1),0!==attributeDivisors[attribute]){var extension=extensions.get("ANGLE_instanced_arrays");extension.vertexAttribDivisorANGLE(attribute,0),attributeDivisors[attribute]=0}},this.enableAttributeAndDivisor=function(attribute,meshPerAttribute,extension){newAttributes[attribute]=1,0===enabledAttributes[attribute]&&(gl.enableVertexAttribArray(attribute),enabledAttributes[attribute]=1),attributeDivisors[attribute]!==meshPerAttribute&&(extension.vertexAttribDivisorANGLE(attribute,meshPerAttribute),attributeDivisors[attribute]=meshPerAttribute)},this.disableUnusedAttributes=function(){for(var i=0,l=enabledAttributes.length;l>i;i++)enabledAttributes[i]!==newAttributes[i]&&(gl.disableVertexAttribArray(i),enabledAttributes[i]=0)},this.enable=function(id){capabilities[id]!==!0&&(gl.enable(id),capabilities[id]=!0)},this.disable=function(id){capabilities[id]!==!1&&(gl.disable(id),capabilities[id]=!1)},this.getCompressedTextureFormats=function(){if(null===compressedTextureFormats&&(compressedTextureFormats=[],extensions.get("WEBGL_compressed_texture_pvrtc")||extensions.get("WEBGL_compressed_texture_s3tc")))for(var formats=gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS),i=0;i0;var shader;shader=hasVertexTexture?{vertexShader:["uniform lowp int renderType;","uniform vec3 screenPosition;","uniform vec2 scale;","uniform float rotation;","uniform sampler2D occlusionMap;","attribute vec2 position;","attribute vec2 uv;","varying vec2 vUV;","varying float vVisibility;","void main() {","vUV = uv;","vec2 pos = position;","if ( renderType == 2 ) {","vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );","visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );","visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );","visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );","visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );","visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );","visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );","visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );","visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );","vVisibility = visibility.r / 9.0;","vVisibility *= 1.0 - visibility.g / 9.0;","vVisibility *= visibility.b / 9.0;","vVisibility *= 1.0 - visibility.a / 9.0;","pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;","pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;","}","gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );","}"].join("\n"),fragmentShader:["uniform lowp int renderType;","uniform sampler2D map;","uniform float opacity;","uniform vec3 color;","varying vec2 vUV;","varying float vVisibility;","void main() {","if ( renderType == 0 ) {","gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );","} else if ( renderType == 1 ) {","gl_FragColor = texture2D( map, vUV );","} else {","vec4 texture = texture2D( map, vUV );","texture.a *= opacity * vVisibility;","gl_FragColor = texture;","gl_FragColor.rgb *= color;","}","}"].join("\n")}:{vertexShader:["uniform lowp int renderType;","uniform vec3 screenPosition;","uniform vec2 scale;","uniform float rotation;","attribute vec2 position;","attribute vec2 uv;","varying vec2 vUV;","void main() {","vUV = uv;","vec2 pos = position;","if ( renderType == 2 ) {","pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;","pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;","}","gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );","}"].join("\n"),fragmentShader:["precision mediump float;","uniform lowp int renderType;","uniform sampler2D map;","uniform sampler2D occlusionMap;","uniform float opacity;","uniform vec3 color;","varying vec2 vUV;","void main() {","if ( renderType == 0 ) {","gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );","} else if ( renderType == 1 ) {","gl_FragColor = texture2D( map, vUV );","} else {","float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;","visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;","visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;","visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;","visibility = ( 1.0 - visibility / 4.0 );","vec4 texture = texture2D( map, vUV );","texture.a *= opacity * visibility;","gl_FragColor = texture;","gl_FragColor.rgb *= color;","}","}"].join("\n")},program=createProgram(shader),attributes={vertex:gl.getAttribLocation(program,"position"),uv:gl.getAttribLocation(program,"uv")},uniforms={renderType:gl.getUniformLocation(program,"renderType"),map:gl.getUniformLocation(program,"map"),occlusionMap:gl.getUniformLocation(program,"occlusionMap"),opacity:gl.getUniformLocation(program,"opacity"),color:gl.getUniformLocation(program,"color"),scale:gl.getUniformLocation(program,"scale"),rotation:gl.getUniformLocation(program,"rotation"),screenPosition:gl.getUniformLocation(program,"screenPosition")}}function createProgram(shader){var program=gl.createProgram(),fragmentShader=gl.createShader(gl.FRAGMENT_SHADER),vertexShader=gl.createShader(gl.VERTEX_SHADER),prefix="precision "+renderer.getPrecision()+" float;\n";return gl.shaderSource(fragmentShader,prefix+shader.fragmentShader),gl.shaderSource(vertexShader,prefix+shader.vertexShader),gl.compileShader(fragmentShader),gl.compileShader(vertexShader),gl.attachShader(program,fragmentShader),gl.attachShader(program,vertexShader),gl.linkProgram(program),program}var vertexBuffer,elementBuffer,program,attributes,uniforms,hasVertexTexture,tempTexture,occlusionTexture,gl=renderer.context,state=renderer.state;this.render=function(scene,camera,viewportWidth,viewportHeight){if(0!==flares.length){var tempPosition=new THREE.Vector3,invAspect=viewportHeight/viewportWidth,halfViewportWidth=.5*viewportWidth,halfViewportHeight=.5*viewportHeight,size=16/viewportHeight,scale=new THREE.Vector2(size*invAspect,size),screenPosition=new THREE.Vector3(1,1,0),screenPositionPixels=new THREE.Vector2(1,1);void 0===program&&init(),gl.useProgram(program),state.initAttributes(),state.enableAttribute(attributes.vertex),state.enableAttribute(attributes.uv),state.disableUnusedAttributes(),gl.uniform1i(uniforms.occlusionMap,0),gl.uniform1i(uniforms.map,1),gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer),gl.vertexAttribPointer(attributes.vertex,2,gl.FLOAT,!1,16,0),gl.vertexAttribPointer(attributes.uv,2,gl.FLOAT,!1,16,8),gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,elementBuffer),state.disable(gl.CULL_FACE),gl.depthMask(!1);for(var i=0,l=flares.length;l>i;i++){size=16/viewportHeight,scale.set(size*invAspect,size);var flare=flares[i];if(tempPosition.set(flare.matrixWorld.elements[12],flare.matrixWorld.elements[13],flare.matrixWorld.elements[14]),tempPosition.applyMatrix4(camera.matrixWorldInverse),tempPosition.applyProjection(camera.projectionMatrix),screenPosition.copy(tempPosition),screenPositionPixels.x=screenPosition.x*halfViewportWidth+halfViewportWidth,screenPositionPixels.y=screenPosition.y*halfViewportHeight+halfViewportHeight, +hasVertexTexture||screenPositionPixels.x>0&&screenPositionPixels.x0&&screenPositionPixels.yj;j++){var sprite=flare.lensFlares[j];sprite.opacity>.001&&sprite.scale>.001&&(screenPosition.x=sprite.x,screenPosition.y=sprite.y,screenPosition.z=sprite.z,size=sprite.size*sprite.scale/viewportHeight,scale.x=size*invAspect,scale.y=size,gl.uniform3f(uniforms.screenPosition,screenPosition.x,screenPosition.y,screenPosition.z),gl.uniform2f(uniforms.scale,scale.x,scale.y),gl.uniform1f(uniforms.rotation,sprite.rotation),gl.uniform1f(uniforms.opacity,sprite.opacity),gl.uniform3f(uniforms.color,sprite.color.r,sprite.color.g,sprite.color.b),state.setBlending(sprite.blending,sprite.blendEquation,sprite.blendSrc,sprite.blendDst),renderer.setTexture(sprite.texture,1),gl.drawElements(gl.TRIANGLES,6,gl.UNSIGNED_SHORT,0))}}}state.enable(gl.CULL_FACE),state.enable(gl.DEPTH_TEST),gl.depthMask(!0),renderer.resetGLState()}}},THREE.SpritePlugin=function(renderer,sprites){function init(){var vertices=new Float32Array([-.5,-.5,0,0,.5,-.5,1,0,.5,.5,1,1,-.5,.5,0,1]),faces=new Uint16Array([0,1,2,0,2,3]);vertexBuffer=gl.createBuffer(),elementBuffer=gl.createBuffer(),gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer),gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW),gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,elementBuffer),gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,faces,gl.STATIC_DRAW),program=createProgram(),attributes={position:gl.getAttribLocation(program,"position"),uv:gl.getAttribLocation(program,"uv")},uniforms={uvOffset:gl.getUniformLocation(program,"uvOffset"),uvScale:gl.getUniformLocation(program,"uvScale"),rotation:gl.getUniformLocation(program,"rotation"),scale:gl.getUniformLocation(program,"scale"),color:gl.getUniformLocation(program,"color"),map:gl.getUniformLocation(program,"map"),opacity:gl.getUniformLocation(program,"opacity"),modelViewMatrix:gl.getUniformLocation(program,"modelViewMatrix"),projectionMatrix:gl.getUniformLocation(program,"projectionMatrix"),fogType:gl.getUniformLocation(program,"fogType"),fogDensity:gl.getUniformLocation(program,"fogDensity"),fogNear:gl.getUniformLocation(program,"fogNear"),fogFar:gl.getUniformLocation(program,"fogFar"),fogColor:gl.getUniformLocation(program,"fogColor"),alphaTest:gl.getUniformLocation(program,"alphaTest")};var canvas=document.createElement("canvas");canvas.width=8,canvas.height=8;var context=canvas.getContext("2d");context.fillStyle="white",context.fillRect(0,0,8,8),texture=new THREE.Texture(canvas),texture.needsUpdate=!0}function createProgram(){var program=gl.createProgram(),vertexShader=gl.createShader(gl.VERTEX_SHADER),fragmentShader=gl.createShader(gl.FRAGMENT_SHADER);return gl.shaderSource(vertexShader,["precision "+renderer.getPrecision()+" float;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform float rotation;","uniform vec2 scale;","uniform vec2 uvOffset;","uniform vec2 uvScale;","attribute vec2 position;","attribute vec2 uv;","varying vec2 vUV;","void main() {","vUV = uvOffset + uv * uvScale;","vec2 alignedPosition = position * scale;","vec2 rotatedPosition;","rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;","rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;","vec4 finalPosition;","finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );","finalPosition.xy += rotatedPosition;","finalPosition = projectionMatrix * finalPosition;","gl_Position = finalPosition;","}"].join("\n")),gl.shaderSource(fragmentShader,["precision "+renderer.getPrecision()+" float;","uniform vec3 color;","uniform sampler2D map;","uniform float opacity;","uniform int fogType;","uniform vec3 fogColor;","uniform float fogDensity;","uniform float fogNear;","uniform float fogFar;","uniform float alphaTest;","varying vec2 vUV;","void main() {","vec4 texture = texture2D( map, vUV );","if ( texture.a < alphaTest ) discard;","gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );","if ( fogType > 0 ) {","float depth = gl_FragCoord.z / gl_FragCoord.w;","float fogFactor = 0.0;","if ( fogType == 1 ) {","fogFactor = smoothstep( fogNear, fogFar, depth );","} else {","const float LOG2 = 1.442695;","fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );","fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );","}","gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );","}","}"].join("\n")),gl.compileShader(vertexShader),gl.compileShader(fragmentShader),gl.attachShader(program,vertexShader),gl.attachShader(program,fragmentShader),gl.linkProgram(program),program}function painterSortStable(a,b){return a.z!==b.z?b.z-a.z:b.id-a.id}var vertexBuffer,elementBuffer,program,attributes,uniforms,texture,gl=renderer.context,state=renderer.state,spritePosition=new THREE.Vector3,spriteRotation=new THREE.Quaternion,spriteScale=new THREE.Vector3;this.render=function(scene,camera){if(0!==sprites.length){void 0===program&&init(),gl.useProgram(program),state.initAttributes(),state.enableAttribute(attributes.position),state.enableAttribute(attributes.uv),state.disableUnusedAttributes(),state.disable(gl.CULL_FACE),state.enable(gl.BLEND),gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer),gl.vertexAttribPointer(attributes.position,2,gl.FLOAT,!1,16,0),gl.vertexAttribPointer(attributes.uv,2,gl.FLOAT,!1,16,8),gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,elementBuffer),gl.uniformMatrix4fv(uniforms.projectionMatrix,!1,camera.projectionMatrix.elements),state.activeTexture(gl.TEXTURE0),gl.uniform1i(uniforms.map,0);var oldFogType=0,sceneFogType=0,fog=scene.fog;fog?(gl.uniform3f(uniforms.fogColor,fog.color.r,fog.color.g,fog.color.b),fog instanceof THREE.Fog?(gl.uniform1f(uniforms.fogNear,fog.near),gl.uniform1f(uniforms.fogFar,fog.far),gl.uniform1i(uniforms.fogType,1),oldFogType=1,sceneFogType=1):fog instanceof THREE.FogExp2&&(gl.uniform1f(uniforms.fogDensity,fog.density),gl.uniform1i(uniforms.fogType,2),oldFogType=2,sceneFogType=2)):(gl.uniform1i(uniforms.fogType,0),oldFogType=0,sceneFogType=0);for(var i=0,l=sprites.length;l>i;i++){var sprite=sprites[i];sprite.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse,sprite.matrixWorld),sprite.z=-sprite.modelViewMatrix.elements[14]}sprites.sort(painterSortStable);for(var scale=[],i=0,l=sprites.length;l>i;i++){var sprite=sprites[i],material=sprite.material;gl.uniform1f(uniforms.alphaTest,material.alphaTest),gl.uniformMatrix4fv(uniforms.modelViewMatrix,!1,sprite.modelViewMatrix.elements),sprite.matrixWorld.decompose(spritePosition,spriteRotation,spriteScale),scale[0]=spriteScale.x,scale[1]=spriteScale.y;var fogType=0;scene.fog&&material.fog&&(fogType=sceneFogType),oldFogType!==fogType&&(gl.uniform1i(uniforms.fogType,fogType),oldFogType=fogType),null!==material.map?(gl.uniform2f(uniforms.uvOffset,material.map.offset.x,material.map.offset.y),gl.uniform2f(uniforms.uvScale,material.map.repeat.x,material.map.repeat.y)):(gl.uniform2f(uniforms.uvOffset,0,0),gl.uniform2f(uniforms.uvScale,1,1)),gl.uniform1f(uniforms.opacity,material.opacity),gl.uniform3f(uniforms.color,material.color.r,material.color.g,material.color.b),gl.uniform1f(uniforms.rotation,material.rotation),gl.uniform2fv(uniforms.scale,scale),state.setBlending(material.blending,material.blendEquation,material.blendSrc,material.blendDst),state.setDepthTest(material.depthTest),state.setDepthWrite(material.depthWrite),material.map&&material.map.image&&material.map.image.width?renderer.setTexture(material.map,0):renderer.setTexture(texture,0),gl.drawElements(gl.TRIANGLES,6,gl.UNSIGNED_SHORT,0)}state.enable(gl.CULL_FACE),renderer.resetGLState()}}},THREE.CurveUtils={tangentQuadraticBezier:function(t,p0,p1,p2){return 2*(1-t)*(p1-p0)+2*t*(p2-p1)},tangentCubicBezier:function(t,p0,p1,p2,p3){return-3*p0*(1-t)*(1-t)+3*p1*(1-t)*(1-t)-6*t*p1*(1-t)+6*t*p2*(1-t)-3*t*t*p2+3*t*t*p3},tangentSpline:function(t,p0,p1,p2,p3){var h00=6*t*t-6*t,h10=3*t*t-4*t+1,h01=-6*t*t+6*t,h11=3*t*t-2*t;return h00+h10+h01+h11},interpolate:function(p0,p1,p2,p3,t){var v0=.5*(p2-p0),v1=.5*(p3-p1),t2=t*t,t3=t*t2;return(2*p1-2*p2+v0+v1)*t3+(-3*p1+3*p2-2*v0-v1)*t2+v0*t+p1}},THREE.GeometryUtils={merge:function(geometry1,geometry2,materialIndexOffset){console.warn("THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.");var matrix;geometry2 instanceof THREE.Mesh&&(geometry2.matrixAutoUpdate&&geometry2.updateMatrix(),matrix=geometry2.matrix,geometry2=geometry2.geometry),geometry1.merge(geometry2,matrix,materialIndexOffset)},center:function(geometry){return console.warn("THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead."),geometry.center()}},THREE.ImageUtils={crossOrigin:void 0,loadTexture:function(url,mapping,onLoad,onError){console.warn("THREE.ImageUtils.loadTexture is being deprecated. Use THREE.TextureLoader() instead.");var loader=new THREE.TextureLoader;loader.setCrossOrigin(this.crossOrigin);var texture=loader.load(url,onLoad,void 0,onError);return mapping&&(texture.mapping=mapping),texture},loadTextureCube:function(urls,mapping,onLoad,onError){console.warn("THREE.ImageUtils.loadTextureCube is being deprecated. Use THREE.CubeTextureLoader() instead.");var loader=new THREE.CubeTextureLoader;loader.setCrossOrigin(this.crossOrigin);var texture=loader.load(urls,onLoad,void 0,onError);return mapping&&(texture.mapping=mapping),texture},loadCompressedTexture:function(){console.error("THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.")},loadCompressedTextureCube:function(){console.error("THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.")}},THREE.SceneUtils={createMultiMaterialObject:function(geometry,materials){for(var group=new THREE.Group,i=0,l=materials.length;l>i;i++)group.add(new THREE.Mesh(geometry,materials[i]));return group},detach:function(child,parent,scene){child.applyMatrix(parent.matrixWorld),parent.remove(child),scene.add(child)},attach:function(child,scene,parent){var matrixWorldInverse=new THREE.Matrix4;matrixWorldInverse.getInverse(parent.matrixWorld),child.applyMatrix(matrixWorldInverse),scene.remove(child),parent.add(child)}},THREE.ShapeUtils={area:function(contour){for(var n=contour.length,a=0,p=n-1,q=0;n>q;p=q++)a+=contour[p].x*contour[q].y-contour[q].x*contour[p].y;return.5*a},triangulate:function(){function snip(contour,u,v,w,n,verts){var p,ax,ay,bx,by,cx,cy,px,py;if(ax=contour[verts[u]].x,ay=contour[verts[u]].y,bx=contour[verts[v]].x,by=contour[verts[v]].y,cx=contour[verts[w]].x,cy=contour[verts[w]].y,Number.EPSILON>(bx-ax)*(cy-ay)-(by-ay)*(cx-ax))return!1;var aX,aY,bX,bY,cX,cY,apx,apy,bpx,bpy,cpx,cpy,cCROSSap,bCROSScp,aCROSSbp;for(aX=cx-bx,aY=cy-by,bX=ax-cx,bY=ay-cy,cX=bx-ax,cY=by-ay,p=0;n>p;p++)if(px=contour[verts[p]].x,py=contour[verts[p]].y,!(px===ax&&py===ay||px===bx&&py===by||px===cx&&py===cy)&&(apx=px-ax,apy=py-ay,bpx=px-bx,bpy=py-by,cpx=px-cx,cpy=py-cy,aCROSSbp=aX*bpy-aY*bpx,cCROSSap=cX*apy-cY*apx,bCROSScp=bX*cpy-bY*cpx,aCROSSbp>=-Number.EPSILON&&bCROSScp>=-Number.EPSILON&&cCROSSap>=-Number.EPSILON))return!1;return!0}return function(contour,indices){var n=contour.length;if(3>n)return null;var u,v,w,result=[],verts=[],vertIndices=[];if(THREE.ShapeUtils.area(contour)>0)for(v=0;n>v;v++)verts[v]=v;else for(v=0;n>v;v++)verts[v]=n-1-v;var nv=n,count=2*nv;for(v=nv-1;nv>2;){if(count--<=0)return console.warn("THREE.ShapeUtils: Unable to triangulate polygon! in triangulate()"),indices?vertIndices:result;if(u=v,u>=nv&&(u=0),v=u+1,v>=nv&&(v=0),w=v+1,w>=nv&&(w=0),snip(contour,u,v,w,nv,verts)){var a,b,c,s,t;for(a=verts[u],b=verts[v],c=verts[w],result.push([contour[a],contour[b],contour[c]]),vertIndices.push([verts[u],verts[v],verts[w]]),s=v,t=v+1;nv>t;s++,t++)verts[s]=verts[t];nv--,count=2*nv}}return indices?vertIndices:result}}(),triangulateShape:function(contour,holes){function point_in_segment_2D_colin(inSegPt1,inSegPt2,inOtherPt){return inSegPt1.x!==inSegPt2.x?inSegPt1.xNumber.EPSILON){var perpSeg2;if(limit>0){if(0>perpSeg1||perpSeg1>limit)return[];if(perpSeg2=seg2dy*seg1seg2dx-seg2dx*seg1seg2dy,0>perpSeg2||perpSeg2>limit)return[]}else{if(perpSeg1>0||limit>perpSeg1)return[];if(perpSeg2=seg2dy*seg1seg2dx-seg2dx*seg1seg2dy,perpSeg2>0||limit>perpSeg2)return[]}if(0===perpSeg2)return!inExcludeAdjacentSegs||0!==perpSeg1&&perpSeg1!==limit?[inSeg1Pt1]:[];if(perpSeg2===limit)return!inExcludeAdjacentSegs||0!==perpSeg1&&perpSeg1!==limit?[inSeg1Pt2]:[];if(0===perpSeg1)return[inSeg2Pt1];if(perpSeg1===limit)return[inSeg2Pt2];var factorSeg1=perpSeg2/limit;return[{x:inSeg1Pt1.x+factorSeg1*seg1dx,y:inSeg1Pt1.y+factorSeg1*seg1dy}]}if(0!==perpSeg1||seg2dy*seg1seg2dx!==seg2dx*seg1seg2dy)return[];var seg1Pt=0===seg1dx&&0===seg1dy,seg2Pt=0===seg2dx&&0===seg2dy;if(seg1Pt&&seg2Pt)return inSeg1Pt1.x!==inSeg2Pt1.x||inSeg1Pt1.y!==inSeg2Pt1.y?[]:[inSeg1Pt1];if(seg1Pt)return point_in_segment_2D_colin(inSeg2Pt1,inSeg2Pt2,inSeg1Pt1)?[inSeg1Pt1]:[];if(seg2Pt)return point_in_segment_2D_colin(inSeg1Pt1,inSeg1Pt2,inSeg2Pt1)?[inSeg2Pt1]:[];var seg1min,seg1max,seg1minVal,seg1maxVal,seg2min,seg2max,seg2minVal,seg2maxVal;return 0!==seg1dx?(inSeg1Pt1.x=seg1minVal?seg2minVal>seg1maxVal?[]:seg1maxVal===seg2minVal?inExcludeAdjacentSegs?[]:[seg2min]:seg2maxVal>=seg1maxVal?[seg2min,seg1max]:[seg2min,seg2max]:seg1minVal>seg2maxVal?[]:seg1minVal===seg2maxVal?inExcludeAdjacentSegs?[]:[seg1min]:seg2maxVal>=seg1maxVal?[seg1min,seg1max]:[seg1min,seg2max]}function isPointInsideAngle(inVertex,inLegFromPt,inLegToPt,inOtherPt){var legFromPtX=inLegFromPt.x-inVertex.x,legFromPtY=inLegFromPt.y-inVertex.y,legToPtX=inLegToPt.x-inVertex.x,legToPtY=inLegToPt.y-inVertex.y,otherPtX=inOtherPt.x-inVertex.x,otherPtY=inOtherPt.y-inVertex.y,from2toAngle=legFromPtX*legToPtY-legFromPtY*legToPtX,from2otherAngle=legFromPtX*otherPtY-legFromPtY*otherPtX;if(Math.abs(from2toAngle)>Number.EPSILON){var other2toAngle=otherPtX*legToPtY-otherPtY*legToPtX;return from2toAngle>0?from2otherAngle>=0&&other2toAngle>=0:from2otherAngle>=0||other2toAngle>=0}return from2otherAngle>0}function removeHoles(contour,holes){function isCutLineInsideAngles(inShapeIdx,inHoleIdx){var lastShapeIdx=shape.length-1,prevShapeIdx=inShapeIdx-1;0>prevShapeIdx&&(prevShapeIdx=lastShapeIdx);var nextShapeIdx=inShapeIdx+1;nextShapeIdx>lastShapeIdx&&(nextShapeIdx=0);var insideAngle=isPointInsideAngle(shape[inShapeIdx],shape[prevShapeIdx],shape[nextShapeIdx],hole[inHoleIdx]);if(!insideAngle)return!1;var lastHoleIdx=hole.length-1,prevHoleIdx=inHoleIdx-1;0>prevHoleIdx&&(prevHoleIdx=lastHoleIdx);var nextHoleIdx=inHoleIdx+1;return nextHoleIdx>lastHoleIdx&&(nextHoleIdx=0),insideAngle=isPointInsideAngle(hole[inHoleIdx],hole[prevHoleIdx],hole[nextHoleIdx],shape[inShapeIdx]),!!insideAngle}function intersectsShapeEdge(inShapePt,inHolePt){var sIdx,nextIdx,intersection;for(sIdx=0;sIdx0)return!0;return!1}function intersectsHoleEdge(inShapePt,inHolePt){var ihIdx,chkHole,hIdx,nextIdx,intersection;for(ihIdx=0;ihIdx0)return!0;return!1}for(var hole,holeIndex,shapeIndex,shapePt,holePt,holeIdx,cutKey,tmpShape1,tmpShape2,tmpHole1,tmpHole2,shape=contour.concat(),indepHoles=[],failedCuts=[],h=0,hl=holes.length;hl>h;h++)indepHoles.push(h);for(var minShapeIndex=0,counter=2*indepHoles.length;indepHoles.length>0;){if(counter--,0>counter){console.log("Infinite Loop! Holes left:"+indepHoles.length+", Probably Hole outside Shape!");break}for(shapeIndex=minShapeIndex;shapeIndex=0)break;failedCuts[cutKey]=!0}if(holeIndex>=0)break}}return shape}for(var i,il,f,face,key,index,allPointsMap={},allpoints=contour.concat(),h=0,hl=holes.length;hl>h;h++)Array.prototype.push.apply(allpoints,holes[h]);for(i=0,il=allpoints.length;il>i;i++)key=allpoints[i].x+":"+allpoints[i].y,void 0!==allPointsMap[key]&&console.warn("THREE.Shape: Duplicate point",key),allPointsMap[key]=i;var shapeWithoutHoles=removeHoles(contour,holes),triangles=THREE.ShapeUtils.triangulate(shapeWithoutHoles,!1);for(i=0,il=triangles.length;il>i;i++)for(face=triangles[i],f=0;3>f;f++)key=face[f].x+":"+face[f].y,index=allPointsMap[key],void 0!==index&&(face[f]=index);return triangles.concat()},isClockWise:function(pts){return THREE.ShapeUtils.area(pts)<0},b2:function(){function b2p0(t,p){var k=1-t;return k*k*p}function b2p1(t,p){return 2*(1-t)*t*p}function b2p2(t,p){return t*t*p}return function(t,p0,p1,p2){return b2p0(t,p0)+b2p1(t,p1)+b2p2(t,p2)}}(),b3:function(){function b3p0(t,p){var k=1-t;return k*k*k*p}function b3p1(t,p){var k=1-t;return 3*k*k*t*p}function b3p2(t,p){var k=1-t;return 3*k*t*t*p}function b3p3(t,p){return t*t*t*p}return function(t,p0,p1,p2,p3){return b3p0(t,p0)+b3p1(t,p1)+b3p2(t,p2)+b3p3(t,p3)}}()},THREE.Audio=function(listener){THREE.Object3D.call(this),this.type="Audio",this.context=listener.context,this.source=this.context.createBufferSource(),this.source.onended=this.onEnded.bind(this),this.gain=this.context.createGain(),this.gain.connect(this.context.destination),this.panner=this.context.createPanner(),this.panner.connect(this.gain),this.autoplay=!1,this.startTime=0,this.playbackRate=1,this.isPlaying=!1},THREE.Audio.prototype=Object.create(THREE.Object3D.prototype),THREE.Audio.prototype.constructor=THREE.Audio,THREE.Audio.prototype.load=function(file){var scope=this,request=new XMLHttpRequest;return request.open("GET",file,!0),request.responseType="arraybuffer",request.onload=function(e){scope.context.decodeAudioData(this.response,function(buffer){scope.source.buffer=buffer,scope.autoplay&&scope.play()})},request.send(),this},THREE.Audio.prototype.play=function(){if(this.isPlaying===!0)return void console.warn("THREE.Audio: Audio is already playing.");var source=this.context.createBufferSource();source.buffer=this.source.buffer,source.loop=this.source.loop,source.onended=this.source.onended,source.start(0,this.startTime),source.playbackRate.value=this.playbackRate,this.isPlaying=!0,this.source=source,this.connect()},THREE.Audio.prototype.pause=function(){this.source.stop(),this.startTime=this.context.currentTime},THREE.Audio.prototype.stop=function(){this.source.stop(),this.startTime=0},THREE.Audio.prototype.connect=function(){void 0!==this.filter?(this.source.connect(this.filter),this.filter.connect(this.panner)):this.source.connect(this.panner)},THREE.Audio.prototype.disconnect=function(){void 0!==this.filter?(this.source.disconnect(this.filter),this.filter.disconnect(this.panner)):this.source.disconnect(this.panner)},THREE.Audio.prototype.setFilter=function(value){this.isPlaying===!0?(this.disconnect(),this.filter=value,this.connect()):this.filter=value},THREE.Audio.prototype.getFilter=function(){return this.filter},THREE.Audio.prototype.setPlaybackRate=function(value){this.playbackRate=value,this.isPlaying===!0&&(this.source.playbackRate.value=this.playbackRate)},THREE.Audio.prototype.getPlaybackRate=function(){return this.playbackRate},THREE.Audio.prototype.onEnded=function(){this.isPlaying=!1},THREE.Audio.prototype.setLoop=function(value){this.source.loop=value},THREE.Audio.prototype.getLoop=function(){return this.source.loop},THREE.Audio.prototype.setRefDistance=function(value){this.panner.refDistance=value},THREE.Audio.prototype.getRefDistance=function(){return this.panner.refDistance},THREE.Audio.prototype.setRolloffFactor=function(value){this.panner.rolloffFactor=value},THREE.Audio.prototype.getRolloffFactor=function(){return this.panner.rolloffFactor},THREE.Audio.prototype.setVolume=function(value){this.gain.gain.value=value},THREE.Audio.prototype.getVolume=function(){return this.gain.gain.value},THREE.Audio.prototype.updateMatrixWorld=function(){var position=new THREE.Vector3;return function(force){THREE.Object3D.prototype.updateMatrixWorld.call(this,force),position.setFromMatrixPosition(this.matrixWorld),this.panner.setPosition(position.x,position.y,position.z)}}(),THREE.AudioListener=function(){THREE.Object3D.call(this),this.type="AudioListener",this.context=new(window.AudioContext||window.webkitAudioContext)},THREE.AudioListener.prototype=Object.create(THREE.Object3D.prototype),THREE.AudioListener.prototype.constructor=THREE.AudioListener,THREE.AudioListener.prototype.updateMatrixWorld=function(){var position=new THREE.Vector3,quaternion=new THREE.Quaternion,scale=new THREE.Vector3,orientation=new THREE.Vector3;return function(force){THREE.Object3D.prototype.updateMatrixWorld.call(this,force);var listener=this.context.listener,up=this.up;this.matrixWorld.decompose(position,quaternion,scale),orientation.set(0,0,-1).applyQuaternion(quaternion),listener.setPosition(position.x,position.y,position.z),listener.setOrientation(orientation.x,orientation.y,orientation.z,up.x,up.y,up.z)}}(),THREE.Curve=function(){},THREE.Curve.prototype={constructor:THREE.Curve,getPoint:function(t){return console.warn("THREE.Curve: Warning, getPoint() not implemented!"),null},getPointAt:function(u){var t=this.getUtoTmapping(u);return this.getPoint(t)},getPoints:function(divisions){divisions||(divisions=5);var d,pts=[];for(d=0;divisions>=d;d++)pts.push(this.getPoint(d/divisions));return pts},getSpacedPoints:function(divisions){divisions||(divisions=5);var d,pts=[];for(d=0;divisions>=d;d++)pts.push(this.getPointAt(d/divisions));return pts},getLength:function(){var lengths=this.getLengths();return lengths[lengths.length-1]},getLengths:function(divisions){if(divisions||(divisions=this.__arcLengthDivisions?this.__arcLengthDivisions:200),this.cacheArcLengths&&this.cacheArcLengths.length===divisions+1&&!this.needsUpdate)return this.cacheArcLengths;this.needsUpdate=!1;var current,p,cache=[],last=this.getPoint(0),sum=0;for(cache.push(0),p=1;divisions>=p;p++)current=this.getPoint(p/divisions),sum+=current.distanceTo(last),cache.push(sum),last=current;return this.cacheArcLengths=cache,cache},updateArcLengths:function(){this.needsUpdate=!0,this.getLengths()},getUtoTmapping:function(u,distance){var targetArcLength,arcLengths=this.getLengths(),i=0,il=arcLengths.length;targetArcLength=distance?distance:u*arcLengths[il-1];for(var comparison,low=0,high=il-1;high>=low;)if(i=Math.floor(low+(high-low)/2),comparison=arcLengths[i]-targetArcLength,0>comparison)low=i+1;else{if(!(comparison>0)){high=i;break}high=i-1}if(i=high,arcLengths[i]===targetArcLength){var t=i/(il-1);return t}var lengthBefore=arcLengths[i],lengthAfter=arcLengths[i+1],segmentLength=lengthAfter-lengthBefore,segmentFraction=(targetArcLength-lengthBefore)/segmentLength,t=(i+segmentFraction)/(il-1);return t},getTangent:function(t){var delta=1e-4,t1=t-delta,t2=t+delta;0>t1&&(t1=0),t2>1&&(t2=1);var pt1=this.getPoint(t1),pt2=this.getPoint(t2),vec=pt2.clone().sub(pt1);return vec.normalize()},getTangentAt:function(u){var t=this.getUtoTmapping(u);return this.getTangent(t)}},THREE.Curve.Utils=THREE.CurveUtils,THREE.Curve.create=function(constructor,getPointFunc){return constructor.prototype=Object.create(THREE.Curve.prototype),constructor.prototype.constructor=constructor,constructor.prototype.getPoint=getPointFunc,constructor},THREE.CurvePath=function(){this.curves=[],this.autoClose=!1},THREE.CurvePath.prototype=Object.create(THREE.Curve.prototype),THREE.CurvePath.prototype.constructor=THREE.CurvePath,THREE.CurvePath.prototype.add=function(curve){this.curves.push(curve)},THREE.CurvePath.prototype.closePath=function(){var startPoint=this.curves[0].getPoint(0),endPoint=this.curves[this.curves.length-1].getPoint(1);startPoint.equals(endPoint)||this.curves.push(new THREE.LineCurve(endPoint,startPoint))},THREE.CurvePath.prototype.getPoint=function(t){for(var d=t*this.getLength(),curveLengths=this.getCurveLengths(),i=0;i=d){var diff=curveLengths[i]-d,curve=this.curves[i],u=1-diff/curve.getLength();return curve.getPointAt(u)}i++}return null},THREE.CurvePath.prototype.getLength=function(){var lens=this.getCurveLengths();return lens[lens.length-1]},THREE.CurvePath.prototype.getCurveLengths=function(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length)return this.cacheLengths;for(var lengths=[],sums=0,i=0,l=this.curves.length;l>i;i++)sums+=this.curves[i].getLength(),lengths.push(sums);return this.cacheLengths=lengths,lengths},THREE.CurvePath.prototype.createPointsGeometry=function(divisions){var pts=this.getPoints(divisions,!0);return this.createGeometry(pts)},THREE.CurvePath.prototype.createSpacedPointsGeometry=function(divisions){var pts=this.getSpacedPoints(divisions,!0);return this.createGeometry(pts)},THREE.CurvePath.prototype.createGeometry=function(points){for(var geometry=new THREE.Geometry,i=0,l=points.length;l>i;i++){var point=points[i];geometry.vertices.push(new THREE.Vector3(point.x,point.y,point.z||0))}return geometry},THREE.Path=function(points){THREE.CurvePath.call(this),this.actions=[],points&&this.fromPoints(points)},THREE.Path.prototype=Object.create(THREE.CurvePath.prototype),THREE.Path.prototype.constructor=THREE.Path,THREE.Path.prototype.fromPoints=function(vectors){this.moveTo(vectors[0].x,vectors[0].y);for(var i=1,l=vectors.length;l>i;i++)this.lineTo(vectors[i].x,vectors[i].y)},THREE.Path.prototype.moveTo=function(x,y){this.actions.push({action:"moveTo",args:[x,y]})},THREE.Path.prototype.lineTo=function(x,y){var lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1],curve=new THREE.LineCurve(new THREE.Vector2(x0,y0),new THREE.Vector2(x,y));this.curves.push(curve),this.actions.push({action:"lineTo",args:[x,y]})},THREE.Path.prototype.quadraticCurveTo=function(aCPx,aCPy,aX,aY){var lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1],curve=new THREE.QuadraticBezierCurve(new THREE.Vector2(x0,y0),new THREE.Vector2(aCPx,aCPy),new THREE.Vector2(aX,aY));this.curves.push(curve),this.actions.push({action:"quadraticCurveTo",args:[aCPx,aCPy,aX,aY]})},THREE.Path.prototype.bezierCurveTo=function(aCP1x,aCP1y,aCP2x,aCP2y,aX,aY){var lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1],curve=new THREE.CubicBezierCurve(new THREE.Vector2(x0,y0),new THREE.Vector2(aCP1x,aCP1y),new THREE.Vector2(aCP2x,aCP2y),new THREE.Vector2(aX,aY));this.curves.push(curve),this.actions.push({action:"bezierCurveTo",args:[aCP1x,aCP1y,aCP2x,aCP2y,aX,aY]})},THREE.Path.prototype.splineThru=function(pts){var args=Array.prototype.slice.call(arguments),lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1],npts=[new THREE.Vector2(x0,y0)];Array.prototype.push.apply(npts,pts);var curve=new THREE.SplineCurve(npts);this.curves.push(curve),this.actions.push({action:"splineThru",args:args})},THREE.Path.prototype.arc=function(aX,aY,aRadius,aStartAngle,aEndAngle,aClockwise){var lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1];this.absarc(aX+x0,aY+y0,aRadius,aStartAngle,aEndAngle,aClockwise)},THREE.Path.prototype.absarc=function(aX,aY,aRadius,aStartAngle,aEndAngle,aClockwise){this.absellipse(aX,aY,aRadius,aRadius,aStartAngle,aEndAngle,aClockwise)},THREE.Path.prototype.ellipse=function(aX,aY,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation){var lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1];this.absellipse(aX+x0,aY+y0,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation)},THREE.Path.prototype.absellipse=function(aX,aY,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation){var args=[aX,aY,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation||0],curve=new THREE.EllipseCurve(aX,aY,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation);this.curves.push(curve);var lastPoint=curve.getPoint(1);args.push(lastPoint.x),args.push(lastPoint.y),this.actions.push({action:"ellipse",args:args})},THREE.Path.prototype.getSpacedPoints=function(divisions,closedPath){divisions||(divisions=40);for(var points=[],i=0;divisions>i;i++)points.push(this.getPoint(i/divisions));return points},THREE.Path.prototype.getPoints=function(divisions,closedPath){divisions=divisions||12;for(var cpx,cpy,cpx2,cpy2,cpx1,cpy1,cpx0,cpy0,laste,tx,ty,b2=THREE.ShapeUtils.b2,b3=THREE.ShapeUtils.b3,points=[],i=0,l=this.actions.length;l>i;i++){var item=this.actions[i],action=item.action,args=item.args;switch(action){case"moveTo":points.push(new THREE.Vector2(args[0],args[1]));break;case"lineTo":points.push(new THREE.Vector2(args[0],args[1])); +break;case"quadraticCurveTo":cpx=args[2],cpy=args[3],cpx1=args[0],cpy1=args[1],points.length>0?(laste=points[points.length-1],cpx0=laste.x,cpy0=laste.y):(laste=this.actions[i-1].args,cpx0=laste[laste.length-2],cpy0=laste[laste.length-1]);for(var j=1;divisions>=j;j++){var t=j/divisions;tx=b2(t,cpx0,cpx1,cpx),ty=b2(t,cpy0,cpy1,cpy),points.push(new THREE.Vector2(tx,ty))}break;case"bezierCurveTo":cpx=args[4],cpy=args[5],cpx1=args[0],cpy1=args[1],cpx2=args[2],cpy2=args[3],points.length>0?(laste=points[points.length-1],cpx0=laste.x,cpy0=laste.y):(laste=this.actions[i-1].args,cpx0=laste[laste.length-2],cpy0=laste[laste.length-1]);for(var j=1;divisions>=j;j++){var t=j/divisions;tx=b3(t,cpx0,cpx1,cpx2,cpx),ty=b3(t,cpy0,cpy1,cpy2,cpy),points.push(new THREE.Vector2(tx,ty))}break;case"splineThru":laste=this.actions[i-1].args;var last=new THREE.Vector2(laste[laste.length-2],laste[laste.length-1]),spts=[last],n=divisions*args[0].length;spts=spts.concat(args[0]);for(var spline=new THREE.SplineCurve(spts),j=1;n>=j;j++)points.push(spline.getPointAt(j/n));break;case"arc":for(var angle,aX=args[0],aY=args[1],aRadius=args[2],aStartAngle=args[3],aEndAngle=args[4],aClockwise=!!args[5],deltaAngle=aEndAngle-aStartAngle,tdivisions=2*divisions,j=1;tdivisions>=j;j++){var t=j/tdivisions;aClockwise||(t=1-t),angle=aStartAngle+t*deltaAngle,tx=aX+aRadius*Math.cos(angle),ty=aY+aRadius*Math.sin(angle),points.push(new THREE.Vector2(tx,ty))}break;case"ellipse":var angle,cos,sin,aX=args[0],aY=args[1],xRadius=args[2],yRadius=args[3],aStartAngle=args[4],aEndAngle=args[5],aClockwise=!!args[6],aRotation=args[7],deltaAngle=aEndAngle-aStartAngle,tdivisions=2*divisions;0!==aRotation&&(cos=Math.cos(aRotation),sin=Math.sin(aRotation));for(var j=1;tdivisions>=j;j++){var t=j/tdivisions;if(aClockwise||(t=1-t),angle=aStartAngle+t*deltaAngle,tx=aX+xRadius*Math.cos(angle),ty=aY+yRadius*Math.sin(angle),0!==aRotation){var x=tx,y=ty;tx=(x-aX)*cos-(y-aY)*sin+aX,ty=(x-aX)*sin+(y-aY)*cos+aY}points.push(new THREE.Vector2(tx,ty))}}}var lastPoint=points[points.length-1];return Math.abs(lastPoint.x-points[0].x)i;i++){var item=inActions[i],args=item.args,action=item.action;"moveTo"===action&&0!==lastPath.actions.length&&(subPaths.push(lastPath),lastPath=new THREE.Path),lastPath[action].apply(lastPath,args)}return 0!==lastPath.actions.length&&subPaths.push(lastPath),subPaths}function toShapesNoHoles(inSubpaths){for(var shapes=[],i=0,l=inSubpaths.length;l>i;i++){var tmpPath=inSubpaths[i],tmpShape=new THREE.Shape;tmpShape.actions=tmpPath.actions,tmpShape.curves=tmpPath.curves,shapes.push(tmpShape)}return shapes}function isPointInsidePolygon(inPt,inPolygon){for(var polyLen=inPolygon.length,inside=!1,p=polyLen-1,q=0;polyLen>q;p=q++){var edgeLowPt=inPolygon[p],edgeHighPt=inPolygon[q],edgeDx=edgeHighPt.x-edgeLowPt.x,edgeDy=edgeHighPt.y-edgeLowPt.y;if(Math.abs(edgeDy)>Number.EPSILON){if(0>edgeDy&&(edgeLowPt=inPolygon[q],edgeDx=-edgeDx,edgeHighPt=inPolygon[p],edgeDy=-edgeDy),inPt.yedgeHighPt.y)continue;if(inPt.y===edgeLowPt.y){if(inPt.x===edgeLowPt.x)return!0}else{var perpEdge=edgeDy*(inPt.x-edgeLowPt.x)-edgeDx*(inPt.y-edgeLowPt.y);if(0===perpEdge)return!0;if(0>perpEdge)continue;inside=!inside}}else{if(inPt.y!==edgeLowPt.y)continue;if(edgeHighPt.x<=inPt.x&&inPt.x<=edgeLowPt.x||edgeLowPt.x<=inPt.x&&inPt.x<=edgeHighPt.x)return!0}}return inside}var isClockWise=THREE.ShapeUtils.isClockWise,subPaths=extractSubpaths(this.actions);if(0===subPaths.length)return[];if(noHoles===!0)return toShapesNoHoles(subPaths);var solid,tmpPath,tmpShape,shapes=[];if(1===subPaths.length)return tmpPath=subPaths[0],tmpShape=new THREE.Shape,tmpShape.actions=tmpPath.actions,tmpShape.curves=tmpPath.curves,shapes.push(tmpShape),shapes;var holesFirst=!isClockWise(subPaths[0].getPoints());holesFirst=isCCW?!holesFirst:holesFirst;var tmpPoints,betterShapeHoles=[],newShapes=[],newShapeHoles=[],mainIdx=0;newShapes[mainIdx]=void 0,newShapeHoles[mainIdx]=[];for(var i=0,l=subPaths.length;l>i;i++)tmpPath=subPaths[i],tmpPoints=tmpPath.getPoints(),solid=isClockWise(tmpPoints),solid=isCCW?!solid:solid,solid?(!holesFirst&&newShapes[mainIdx]&&mainIdx++,newShapes[mainIdx]={s:new THREE.Shape,p:tmpPoints},newShapes[mainIdx].s.actions=tmpPath.actions,newShapes[mainIdx].s.curves=tmpPath.curves,holesFirst&&mainIdx++,newShapeHoles[mainIdx]=[]):newShapeHoles[mainIdx].push({h:tmpPath,p:tmpPoints[0]});if(!newShapes[0])return toShapesNoHoles(subPaths);if(newShapes.length>1){for(var ambiguous=!1,toChange=[],sIdx=0,sLen=newShapes.length;sLen>sIdx;sIdx++)betterShapeHoles[sIdx]=[];for(var sIdx=0,sLen=newShapes.length;sLen>sIdx;sIdx++)for(var sho=newShapeHoles[sIdx],hIdx=0;hIdx0&&(ambiguous||(newShapeHoles=betterShapeHoles))}for(var tmpHoles,i=0,il=newShapes.length;il>i;i++){tmpShape=newShapes[i].s,shapes.push(tmpShape),tmpHoles=newShapeHoles[i];for(var j=0,jl=tmpHoles.length;jl>j;j++)tmpShape.holes.push(tmpHoles[j].h)}return shapes},THREE.Shape=function(){THREE.Path.apply(this,arguments),this.holes=[]},THREE.Shape.prototype=Object.create(THREE.Path.prototype),THREE.Shape.prototype.constructor=THREE.Shape,THREE.Shape.prototype.extrude=function(options){return new THREE.ExtrudeGeometry(this,options)},THREE.Shape.prototype.makeGeometry=function(options){return new THREE.ShapeGeometry(this,options)},THREE.Shape.prototype.getPointsHoles=function(divisions){for(var holesPts=[],i=0,l=this.holes.length;l>i;i++)holesPts[i]=this.holes[i].getPoints(divisions);return holesPts},THREE.Shape.prototype.extractAllPoints=function(divisions){return{shape:this.getPoints(divisions),holes:this.getPointsHoles(divisions)}},THREE.Shape.prototype.extractPoints=function(divisions){return this.extractAllPoints(divisions)},THREE.Shape.Utils=THREE.ShapeUtils,THREE.LineCurve=function(v1,v2){this.v1=v1,this.v2=v2},THREE.LineCurve.prototype=Object.create(THREE.Curve.prototype),THREE.LineCurve.prototype.constructor=THREE.LineCurve,THREE.LineCurve.prototype.getPoint=function(t){var point=this.v2.clone().sub(this.v1);return point.multiplyScalar(t).add(this.v1),point},THREE.LineCurve.prototype.getPointAt=function(u){return this.getPoint(u)},THREE.LineCurve.prototype.getTangent=function(t){var tangent=this.v2.clone().sub(this.v1);return tangent.normalize()},THREE.QuadraticBezierCurve=function(v0,v1,v2){this.v0=v0,this.v1=v1,this.v2=v2},THREE.QuadraticBezierCurve.prototype=Object.create(THREE.Curve.prototype),THREE.QuadraticBezierCurve.prototype.constructor=THREE.QuadraticBezierCurve,THREE.QuadraticBezierCurve.prototype.getPoint=function(t){var b2=THREE.ShapeUtils.b2;return new THREE.Vector2(b2(t,this.v0.x,this.v1.x,this.v2.x),b2(t,this.v0.y,this.v1.y,this.v2.y))},THREE.QuadraticBezierCurve.prototype.getTangent=function(t){var tangentQuadraticBezier=THREE.CurveUtils.tangentQuadraticBezier;return new THREE.Vector2(tangentQuadraticBezier(t,this.v0.x,this.v1.x,this.v2.x),tangentQuadraticBezier(t,this.v0.y,this.v1.y,this.v2.y)).normalize()},THREE.CubicBezierCurve=function(v0,v1,v2,v3){this.v0=v0,this.v1=v1,this.v2=v2,this.v3=v3},THREE.CubicBezierCurve.prototype=Object.create(THREE.Curve.prototype),THREE.CubicBezierCurve.prototype.constructor=THREE.CubicBezierCurve,THREE.CubicBezierCurve.prototype.getPoint=function(t){var b3=THREE.ShapeUtils.b3;return new THREE.Vector2(b3(t,this.v0.x,this.v1.x,this.v2.x,this.v3.x),b3(t,this.v0.y,this.v1.y,this.v2.y,this.v3.y))},THREE.CubicBezierCurve.prototype.getTangent=function(t){var tangentCubicBezier=THREE.CurveUtils.tangentCubicBezier;return new THREE.Vector2(tangentCubicBezier(t,this.v0.x,this.v1.x,this.v2.x,this.v3.x),tangentCubicBezier(t,this.v0.y,this.v1.y,this.v2.y,this.v3.y)).normalize()},THREE.SplineCurve=function(points){this.points=void 0==points?[]:points},THREE.SplineCurve.prototype=Object.create(THREE.Curve.prototype),THREE.SplineCurve.prototype.constructor=THREE.SplineCurve,THREE.SplineCurve.prototype.getPoint=function(t){var points=this.points,point=(points.length-1)*t,intPoint=Math.floor(point),weight=point-intPoint,point0=points[0===intPoint?intPoint:intPoint-1],point1=points[intPoint],point2=points[intPoint>points.length-2?points.length-1:intPoint+1],point3=points[intPoint>points.length-3?points.length-1:intPoint+2],interpolate=THREE.CurveUtils.interpolate;return new THREE.Vector2(interpolate(point0.x,point1.x,point2.x,point3.x,weight),interpolate(point0.y,point1.y,point2.y,point3.y,weight))},THREE.EllipseCurve=function(aX,aY,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation){this.aX=aX,this.aY=aY,this.xRadius=xRadius,this.yRadius=yRadius,this.aStartAngle=aStartAngle,this.aEndAngle=aEndAngle,this.aClockwise=aClockwise,this.aRotation=aRotation||0},THREE.EllipseCurve.prototype=Object.create(THREE.Curve.prototype),THREE.EllipseCurve.prototype.constructor=THREE.EllipseCurve,THREE.EllipseCurve.prototype.getPoint=function(t){var deltaAngle=this.aEndAngle-this.aStartAngle;0>deltaAngle&&(deltaAngle+=2*Math.PI),deltaAngle>2*Math.PI&&(deltaAngle-=2*Math.PI);var angle;angle=this.aClockwise===!0?this.aEndAngle+(1-t)*(2*Math.PI-deltaAngle):this.aStartAngle+t*deltaAngle;var x=this.aX+this.xRadius*Math.cos(angle),y=this.aY+this.yRadius*Math.sin(angle);if(0!==this.aRotation){var cos=Math.cos(this.aRotation),sin=Math.sin(this.aRotation),tx=x,ty=y;x=(tx-this.aX)*cos-(ty-this.aY)*sin+this.aX,y=(tx-this.aX)*sin+(ty-this.aY)*cos+this.aY}return new THREE.Vector2(x,y)},THREE.ArcCurve=function(aX,aY,aRadius,aStartAngle,aEndAngle,aClockwise){THREE.EllipseCurve.call(this,aX,aY,aRadius,aRadius,aStartAngle,aEndAngle,aClockwise)},THREE.ArcCurve.prototype=Object.create(THREE.EllipseCurve.prototype),THREE.ArcCurve.prototype.constructor=THREE.ArcCurve,THREE.LineCurve3=THREE.Curve.create(function(v1,v2){this.v1=v1,this.v2=v2},function(t){var vector=new THREE.Vector3;return vector.subVectors(this.v2,this.v1),vector.multiplyScalar(t),vector.add(this.v1),vector}),THREE.QuadraticBezierCurve3=THREE.Curve.create(function(v0,v1,v2){this.v0=v0,this.v1=v1,this.v2=v2},function(t){var b2=THREE.ShapeUtils.b2;return new THREE.Vector3(b2(t,this.v0.x,this.v1.x,this.v2.x),b2(t,this.v0.y,this.v1.y,this.v2.y),b2(t,this.v0.z,this.v1.z,this.v2.z))}),THREE.CubicBezierCurve3=THREE.Curve.create(function(v0,v1,v2,v3){this.v0=v0,this.v1=v1,this.v2=v2,this.v3=v3},function(t){var b3=THREE.ShapeUtils.b3;return new THREE.Vector3(b3(t,this.v0.x,this.v1.x,this.v2.x,this.v3.x),b3(t,this.v0.y,this.v1.y,this.v2.y,this.v3.y),b3(t,this.v0.z,this.v1.z,this.v2.z,this.v3.z))}),THREE.SplineCurve3=THREE.Curve.create(function(points){console.warn("THREE.SplineCurve3 will be deprecated. Please use THREE.CatmullRomCurve3"),this.points=void 0==points?[]:points},function(t){var points=this.points,point=(points.length-1)*t,intPoint=Math.floor(point),weight=point-intPoint,point0=points[0==intPoint?intPoint:intPoint-1],point1=points[intPoint],point2=points[intPoint>points.length-2?points.length-1:intPoint+1],point3=points[intPoint>points.length-3?points.length-1:intPoint+2],interpolate=THREE.CurveUtils.interpolate;return new THREE.Vector3(interpolate(point0.x,point1.x,point2.x,point3.x,weight),interpolate(point0.y,point1.y,point2.y,point3.y,weight),interpolate(point0.z,point1.z,point2.z,point3.z,weight))}),THREE.CatmullRomCurve3=function(){function CubicPoly(){}var tmp=new THREE.Vector3,px=new CubicPoly,py=new CubicPoly,pz=new CubicPoly;return CubicPoly.prototype.init=function(x0,x1,t0,t1){this.c0=x0,this.c1=t0,this.c2=-3*x0+3*x1-2*t0-t1,this.c3=2*x0-2*x1+t0+t1},CubicPoly.prototype.initNonuniformCatmullRom=function(x0,x1,x2,x3,dt0,dt1,dt2){var t1=(x1-x0)/dt0-(x2-x0)/(dt0+dt1)+(x2-x1)/dt1,t2=(x2-x1)/dt1-(x3-x1)/(dt1+dt2)+(x3-x2)/dt2;t1*=dt1,t2*=dt1,this.init(x1,x2,t1,t2)},CubicPoly.prototype.initCatmullRom=function(x0,x1,x2,x3,tension){this.init(x1,x2,tension*(x2-x0),tension*(x3-x1))},CubicPoly.prototype.calc=function(t){var t2=t*t,t3=t2*t;return this.c0+this.c1*t+this.c2*t2+this.c3*t3},THREE.Curve.create(function(p){this.points=p||[]},function(t){var point,intPoint,weight,l,points=this.points;l=points.length,2>l&&console.log("duh, you need at least 2 points"),point=(l-1)*t,intPoint=Math.floor(point),weight=point-intPoint,0===weight&&intPoint===l-1&&(intPoint=l-2,weight=1);var p0,p1,p2,p3;if(0===intPoint?(tmp.subVectors(points[0],points[1]).add(points[0]),p0=tmp):p0=points[intPoint-1],p1=points[intPoint],p2=points[intPoint+1],l>intPoint+2?p3=points[intPoint+2]:(tmp.subVectors(points[l-1],points[l-2]).add(points[l-2]),p3=tmp),void 0===this.type||"centripetal"===this.type||"chordal"===this.type){var pow="chordal"===this.type?.5:.25,dt0=Math.pow(p0.distanceToSquared(p1),pow),dt1=Math.pow(p1.distanceToSquared(p2),pow),dt2=Math.pow(p2.distanceToSquared(p3),pow);1e-4>dt1&&(dt1=1),1e-4>dt0&&(dt0=dt1),1e-4>dt2&&(dt2=dt1),px.initNonuniformCatmullRom(p0.x,p1.x,p2.x,p3.x,dt0,dt1,dt2),py.initNonuniformCatmullRom(p0.y,p1.y,p2.y,p3.y,dt0,dt1,dt2),pz.initNonuniformCatmullRom(p0.z,p1.z,p2.z,p3.z,dt0,dt1,dt2)}else if("catmullrom"===this.type){var tension=void 0!==this.tension?this.tension:.5;px.initCatmullRom(p0.x,p1.x,p2.x,p3.x,tension),py.initCatmullRom(p0.y,p1.y,p2.y,p3.y,tension),pz.initCatmullRom(p0.z,p1.z,p2.z,p3.z,tension)}var v=new THREE.Vector3(px.calc(weight),py.calc(weight),pz.calc(weight));return v})}(),THREE.ClosedSplineCurve3=THREE.Curve.create(function(points){this.points=void 0==points?[]:points},function(t){var points=this.points,point=(points.length-0)*t,intPoint=Math.floor(point),weight=point-intPoint;intPoint+=intPoint>0?0:(Math.floor(Math.abs(intPoint)/points.length)+1)*points.length;var point0=points[(intPoint-1)%points.length],point1=points[intPoint%points.length],point2=points[(intPoint+1)%points.length],point3=points[(intPoint+2)%points.length],interpolate=THREE.CurveUtils.interpolate;return new THREE.Vector3(interpolate(point0.x,point1.x,point2.x,point3.x,weight),interpolate(point0.y,point1.y,point2.y,point3.y,weight),interpolate(point0.z,point1.z,point2.z,point3.z,weight))}),THREE.BoxGeometry=function(width,height,depth,widthSegments,heightSegments,depthSegments){function buildPlane(u,v,udir,vdir,width,height,depth,materialIndex){var w,ix,iy,gridX=scope.widthSegments,gridY=scope.heightSegments,width_half=width/2,height_half=height/2,offset=scope.vertices.length;"x"===u&&"y"===v||"y"===u&&"x"===v?w="z":"x"===u&&"z"===v||"z"===u&&"x"===v?(w="y",gridY=scope.depthSegments):("z"===u&&"y"===v||"y"===u&&"z"===v)&&(w="x",gridX=scope.depthSegments);var gridX1=gridX+1,gridY1=gridY+1,segment_width=width/gridX,segment_height=height/gridY,normal=new THREE.Vector3;for(normal[w]=depth>0?1:-1,iy=0;gridY1>iy;iy++)for(ix=0;gridX1>ix;ix++){var vector=new THREE.Vector3;vector[u]=(ix*segment_width-width_half)*udir,vector[v]=(iy*segment_height-height_half)*vdir,vector[w]=depth,scope.vertices.push(vector)}for(iy=0;gridY>iy;iy++)for(ix=0;gridX>ix;ix++){var a=ix+gridX1*iy,b=ix+gridX1*(iy+1),c=ix+1+gridX1*(iy+1),d=ix+1+gridX1*iy,uva=new THREE.Vector2(ix/gridX,1-iy/gridY),uvb=new THREE.Vector2(ix/gridX,1-(iy+1)/gridY),uvc=new THREE.Vector2((ix+1)/gridX,1-(iy+1)/gridY),uvd=new THREE.Vector2((ix+1)/gridX,1-iy/gridY),face=new THREE.Face3(a+offset,b+offset,d+offset);face.normal.copy(normal),face.vertexNormals.push(normal.clone(),normal.clone(),normal.clone()),face.materialIndex=materialIndex,scope.faces.push(face),scope.faceVertexUvs[0].push([uva,uvb,uvd]),face=new THREE.Face3(b+offset,c+offset,d+offset),face.normal.copy(normal),face.vertexNormals.push(normal.clone(),normal.clone(),normal.clone()),face.materialIndex=materialIndex,scope.faces.push(face),scope.faceVertexUvs[0].push([uvb.clone(),uvc,uvd.clone()])}}THREE.Geometry.call(this),this.type="BoxGeometry",this.parameters={width:width,height:height,depth:depth,widthSegments:widthSegments,heightSegments:heightSegments,depthSegments:depthSegments},this.widthSegments=widthSegments||1,this.heightSegments=heightSegments||1,this.depthSegments=depthSegments||1;var scope=this,width_half=width/2,height_half=height/2,depth_half=depth/2;buildPlane("z","y",-1,-1,depth,height,width_half,0),buildPlane("z","y",1,-1,depth,height,-width_half,1),buildPlane("x","z",1,1,width,depth,height_half,2),buildPlane("x","z",1,-1,width,depth,-height_half,3),buildPlane("x","y",1,-1,width,height,depth_half,4),buildPlane("x","y",-1,-1,width,height,-depth_half,5),this.mergeVertices()},THREE.BoxGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.BoxGeometry.prototype.constructor=THREE.BoxGeometry,THREE.BoxGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.BoxGeometry(parameters.width,parameters.height,parameters.depth,parameters.widthSegments,parameters.heightSegments,parameters.depthSegments)},THREE.CubeGeometry=THREE.BoxGeometry,THREE.CircleGeometry=function(radius,segments,thetaStart,thetaLength){THREE.Geometry.call(this),this.type="CircleGeometry",this.parameters={radius:radius,segments:segments,thetaStart:thetaStart,thetaLength:thetaLength},this.fromBufferGeometry(new THREE.CircleBufferGeometry(radius,segments,thetaStart,thetaLength))},THREE.CircleGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.CircleGeometry.prototype.constructor=THREE.CircleGeometry,THREE.CircleGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.CircleGeometry(parameters.radius,parameters.segments,parameters.thetaStart,parameters.thetaLength)},THREE.CircleBufferGeometry=function(radius,segments,thetaStart,thetaLength){THREE.BufferGeometry.call(this),this.type="CircleBufferGeometry",this.parameters={radius:radius,segments:segments,thetaStart:thetaStart,thetaLength:thetaLength},radius=radius||50,segments=void 0!==segments?Math.max(3,segments):8,thetaStart=void 0!==thetaStart?thetaStart:0,thetaLength=void 0!==thetaLength?thetaLength:2*Math.PI;var vertices=segments+2,positions=new Float32Array(3*vertices),normals=new Float32Array(3*vertices),uvs=new Float32Array(2*vertices);normals[2]=1,uvs[0]=.5,uvs[1]=.5;for(var s=0,i=3,ii=2;segments>=s;s++,i+=3,ii+=2){var segment=thetaStart+s/segments*thetaLength;positions[i]=radius*Math.cos(segment),positions[i+1]=radius*Math.sin(segment),normals[i+2]=1,uvs[ii]=(positions[i]/radius+1)/2,uvs[ii+1]=(positions[i+1]/radius+1)/2}for(var indices=[],i=1;segments>=i;i++)indices.push(i,i+1,0);this.setIndex(new THREE.BufferAttribute(new Uint16Array(indices),1)),this.addAttribute("position",new THREE.BufferAttribute(positions,3)),this.addAttribute("normal",new THREE.BufferAttribute(normals,3)),this.addAttribute("uv",new THREE.BufferAttribute(uvs,2)),this.boundingSphere=new THREE.Sphere(new THREE.Vector3,radius)},THREE.CircleBufferGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.CircleBufferGeometry.prototype.constructor=THREE.CircleBufferGeometry,THREE.CircleBufferGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.CircleBufferGeometry(parameters.radius,parameters.segments,parameters.thetaStart,parameters.thetaLength)},THREE.CylinderGeometry=function(radiusTop,radiusBottom,height,radialSegments,heightSegments,openEnded,thetaStart,thetaLength){THREE.Geometry.call(this),this.type="CylinderGeometry",this.parameters={radiusTop:radiusTop,radiusBottom:radiusBottom,height:height,radialSegments:radialSegments,heightSegments:heightSegments,openEnded:openEnded,thetaStart:thetaStart,thetaLength:thetaLength},radiusTop=void 0!==radiusTop?radiusTop:20,radiusBottom=void 0!==radiusBottom?radiusBottom:20,height=void 0!==height?height:100,radialSegments=radialSegments||8,heightSegments=heightSegments||1,openEnded=void 0!==openEnded?openEnded:!1,thetaStart=void 0!==thetaStart?thetaStart:0,thetaLength=void 0!==thetaLength?thetaLength:2*Math.PI;var x,y,heightHalf=height/2,vertices=[],uvs=[];for(y=0;heightSegments>=y;y++){var verticesRow=[],uvsRow=[],v=y/heightSegments,radius=v*(radiusBottom-radiusTop)+radiusTop;for(x=0;radialSegments>=x;x++){var u=x/radialSegments,vertex=new THREE.Vector3;vertex.x=radius*Math.sin(u*thetaLength+thetaStart),vertex.y=-v*height+heightHalf,vertex.z=radius*Math.cos(u*thetaLength+thetaStart),this.vertices.push(vertex),verticesRow.push(this.vertices.length-1),uvsRow.push(new THREE.Vector2(u,1-v))}vertices.push(verticesRow),uvs.push(uvsRow)}var na,nb,tanTheta=(radiusBottom-radiusTop)/height;for(x=0;radialSegments>x;x++)for(0!==radiusTop?(na=this.vertices[vertices[0][x]].clone(),nb=this.vertices[vertices[0][x+1]].clone()):(na=this.vertices[vertices[1][x]].clone(),nb=this.vertices[vertices[1][x+1]].clone()),na.setY(Math.sqrt(na.x*na.x+na.z*na.z)*tanTheta).normalize(),nb.setY(Math.sqrt(nb.x*nb.x+nb.z*nb.z)*tanTheta).normalize(),y=0;heightSegments>y;y++){var v1=vertices[y][x],v2=vertices[y+1][x],v3=vertices[y+1][x+1],v4=vertices[y][x+1],n1=na.clone(),n2=na.clone(),n3=nb.clone(),n4=nb.clone(),uv1=uvs[y][x].clone(),uv2=uvs[y+1][x].clone(),uv3=uvs[y+1][x+1].clone(),uv4=uvs[y][x+1].clone();this.faces.push(new THREE.Face3(v1,v2,v4,[n1,n2,n4])),this.faceVertexUvs[0].push([uv1,uv2,uv4]),this.faces.push(new THREE.Face3(v2,v3,v4,[n2.clone(),n3,n4.clone()])),this.faceVertexUvs[0].push([uv2.clone(),uv3,uv4.clone()])}if(openEnded===!1&&radiusTop>0)for(this.vertices.push(new THREE.Vector3(0,heightHalf,0)),x=0;radialSegments>x;x++){var v1=vertices[0][x],v2=vertices[0][x+1],v3=this.vertices.length-1,n1=new THREE.Vector3(0,1,0),n2=new THREE.Vector3(0,1,0),n3=new THREE.Vector3(0,1,0),uv1=uvs[0][x].clone(),uv2=uvs[0][x+1].clone(),uv3=new THREE.Vector2(uv2.x,0);this.faces.push(new THREE.Face3(v1,v2,v3,[n1,n2,n3],void 0,1)),this.faceVertexUvs[0].push([uv1,uv2,uv3])}if(openEnded===!1&&radiusBottom>0)for(this.vertices.push(new THREE.Vector3(0,-heightHalf,0)),x=0;radialSegments>x;x++){var v1=vertices[heightSegments][x+1],v2=vertices[heightSegments][x],v3=this.vertices.length-1,n1=new THREE.Vector3(0,-1,0),n2=new THREE.Vector3(0,-1,0),n3=new THREE.Vector3(0,-1,0),uv1=uvs[heightSegments][x+1].clone(),uv2=uvs[heightSegments][x].clone(),uv3=new THREE.Vector2(uv2.x,1);this.faces.push(new THREE.Face3(v1,v2,v3,[n1,n2,n3],void 0,2)),this.faceVertexUvs[0].push([uv1,uv2,uv3])}this.computeFaceNormals()},THREE.CylinderGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.CylinderGeometry.prototype.constructor=THREE.CylinderGeometry,THREE.CylinderGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.CylinderGeometry(parameters.radiusTop,parameters.radiusBottom,parameters.height,parameters.radialSegments,parameters.heightSegments,parameters.openEnded,parameters.thetaStart,parameters.thetaLength)},THREE.EdgesGeometry=function(geometry,thresholdAngle){function sortFunction(a,b){return a-b}THREE.BufferGeometry.call(this),thresholdAngle=void 0!==thresholdAngle?thresholdAngle:1;var geometry2,thresholdDot=Math.cos(THREE.Math.degToRad(thresholdAngle)),edge=[0,0],hash={},keys=["a","b","c"];geometry instanceof THREE.BufferGeometry?(geometry2=new THREE.Geometry,geometry2.fromBufferGeometry(geometry)):geometry2=geometry.clone(),geometry2.mergeVertices(),geometry2.computeFaceNormals();for(var vertices=geometry2.vertices,faces=geometry2.faces,i=0,l=faces.length;l>i;i++)for(var face=faces[i],j=0;3>j;j++){edge[0]=face[keys[j]],edge[1]=face[keys[(j+1)%3]],edge.sort(sortFunction);var key=edge.toString();void 0===hash[key]?hash[key]={vert1:edge[0],vert2:edge[1],face1:i,face2:void 0}:hash[key].face2=i}var coords=[];for(var key in hash){var h=hash[key];if(void 0===h.face2||faces[h.face1].normal.dot(faces[h.face2].normal)<=thresholdDot){var vertex=vertices[h.vert1];coords.push(vertex.x),coords.push(vertex.y),coords.push(vertex.z),vertex=vertices[h.vert2],coords.push(vertex.x),coords.push(vertex.y),coords.push(vertex.z)}}this.addAttribute("position",new THREE.BufferAttribute(new Float32Array(coords),3))},THREE.EdgesGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.EdgesGeometry.prototype.constructor=THREE.EdgesGeometry,THREE.ExtrudeGeometry=function(shapes,options){return"undefined"==typeof shapes?void(shapes=[]):(THREE.Geometry.call(this),this.type="ExtrudeGeometry",shapes=Array.isArray(shapes)?shapes:[shapes],this.addShapeList(shapes,options),void this.computeFaceNormals())},THREE.ExtrudeGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.ExtrudeGeometry.prototype.constructor=THREE.ExtrudeGeometry,THREE.ExtrudeGeometry.prototype.addShapeList=function(shapes,options){for(var sl=shapes.length,s=0;sl>s;s++){var shape=shapes[s];this.addShape(shape,options)}},THREE.ExtrudeGeometry.prototype.addShape=function(shape,options){function scalePt2(pt,vec,size){return vec||console.error("THREE.ExtrudeGeometry: vec does not exist"),vec.clone().multiplyScalar(size).add(pt)}function getBevelVec(inPt,inPrev,inNext){var v_trans_x,v_trans_y,shrink_by=1,v_prev_x=inPt.x-inPrev.x,v_prev_y=inPt.y-inPrev.y,v_next_x=inNext.x-inPt.x,v_next_y=inNext.y-inPt.y,v_prev_lensq=v_prev_x*v_prev_x+v_prev_y*v_prev_y,collinear0=v_prev_x*v_next_y-v_prev_y*v_next_x;if(Math.abs(collinear0)>Number.EPSILON){var v_prev_len=Math.sqrt(v_prev_lensq),v_next_len=Math.sqrt(v_next_x*v_next_x+v_next_y*v_next_y),ptPrevShift_x=inPrev.x-v_prev_y/v_prev_len,ptPrevShift_y=inPrev.y+v_prev_x/v_prev_len,ptNextShift_x=inNext.x-v_next_y/v_next_len,ptNextShift_y=inNext.y+v_next_x/v_next_len,sf=((ptNextShift_x-ptPrevShift_x)*v_next_y-(ptNextShift_y-ptPrevShift_y)*v_next_x)/(v_prev_x*v_next_y-v_prev_y*v_next_x);v_trans_x=ptPrevShift_x+v_prev_x*sf-inPt.x,v_trans_y=ptPrevShift_y+v_prev_y*sf-inPt.y;var v_trans_lensq=v_trans_x*v_trans_x+v_trans_y*v_trans_y;if(2>=v_trans_lensq)return new THREE.Vector2(v_trans_x,v_trans_y);shrink_by=Math.sqrt(v_trans_lensq/2)}else{var direction_eq=!1;v_prev_x>Number.EPSILON?v_next_x>Number.EPSILON&&(direction_eq=!0):v_prev_x<-Number.EPSILON?v_next_x<-Number.EPSILON&&(direction_eq=!0):Math.sign(v_prev_y)===Math.sign(v_next_y)&&(direction_eq=!0),direction_eq?(v_trans_x=-v_prev_y,v_trans_y=v_prev_x,shrink_by=Math.sqrt(v_prev_lensq)):(v_trans_x=v_prev_x,v_trans_y=v_prev_y,shrink_by=Math.sqrt(v_prev_lensq/2))}return new THREE.Vector2(v_trans_x/shrink_by,v_trans_y/shrink_by)}function buildLidFaces(){if(bevelEnabled){var layer=0,offset=vlen*layer;for(i=0;flen>i;i++)face=faces[i],f3(face[2]+offset,face[1]+offset,face[0]+offset);for(layer=steps+2*bevelSegments,offset=vlen*layer,i=0;flen>i;i++)face=faces[i],f3(face[0]+offset,face[1]+offset,face[2]+offset)}else{for(i=0;flen>i;i++)face=faces[i],f3(face[2],face[1],face[0]);for(i=0;flen>i;i++)face=faces[i],f3(face[0]+vlen*steps,face[1]+vlen*steps,face[2]+vlen*steps)}}function buildSideFaces(){var layeroffset=0;for(sidewalls(contour,layeroffset),layeroffset+=contour.length,h=0,hl=holes.length;hl>h;h++)ahole=holes[h],sidewalls(ahole,layeroffset),layeroffset+=ahole.length}function sidewalls(contour,layeroffset){var j,k;for(i=contour.length;--i>=0;){j=i,k=i-1,0>k&&(k=contour.length-1);var s=0,sl=steps+2*bevelSegments;for(s=0;sl>s;s++){var slen1=vlen*s,slen2=vlen*(s+1),a=layeroffset+j+slen1,b=layeroffset+k+slen1,c=layeroffset+k+slen2,d=layeroffset+j+slen2;f4(a,b,c,d,contour,s,sl,j,k)}}}function v(x,y,z){scope.vertices.push(new THREE.Vector3(x,y,z))}function f3(a,b,c){a+=shapesOffset,b+=shapesOffset,c+=shapesOffset,scope.faces.push(new THREE.Face3(a,b,c,null,null,0));var uvs=uvgen.generateTopUV(scope,a,b,c);scope.faceVertexUvs[0].push(uvs)}function f4(a,b,c,d,wallContour,stepIndex,stepsLength,contourIndex1,contourIndex2){a+=shapesOffset,b+=shapesOffset,c+=shapesOffset,d+=shapesOffset,scope.faces.push(new THREE.Face3(a,b,d,null,null,1)),scope.faces.push(new THREE.Face3(b,c,d,null,null,1));var uvs=uvgen.generateSideWallUV(scope,a,b,c,d);scope.faceVertexUvs[0].push([uvs[0],uvs[1],uvs[3]]),scope.faceVertexUvs[0].push([uvs[1],uvs[2],uvs[3]])}var extrudePts,splineTube,binormal,normal,position2,amount=void 0!==options.amount?options.amount:100,bevelThickness=void 0!==options.bevelThickness?options.bevelThickness:6,bevelSize=void 0!==options.bevelSize?options.bevelSize:bevelThickness-2,bevelSegments=void 0!==options.bevelSegments?options.bevelSegments:3,bevelEnabled=void 0!==options.bevelEnabled?options.bevelEnabled:!0,curveSegments=void 0!==options.curveSegments?options.curveSegments:12,steps=void 0!==options.steps?options.steps:1,extrudePath=options.extrudePath,extrudeByPath=!1,uvgen=void 0!==options.UVGenerator?options.UVGenerator:THREE.ExtrudeGeometry.WorldUVGenerator;extrudePath&&(extrudePts=extrudePath.getSpacedPoints(steps),extrudeByPath=!0,bevelEnabled=!1,splineTube=void 0!==options.frames?options.frames:new THREE.TubeGeometry.FrenetFrames(extrudePath,steps,!1),binormal=new THREE.Vector3,normal=new THREE.Vector3,position2=new THREE.Vector3),bevelEnabled||(bevelSegments=0,bevelThickness=0,bevelSize=0);var ahole,h,hl,scope=this,shapesOffset=this.vertices.length,shapePoints=shape.extractPoints(curveSegments),vertices=shapePoints.shape,holes=shapePoints.holes,reverse=!THREE.ShapeUtils.isClockWise(vertices);if(reverse){for(vertices=vertices.reverse(),h=0,hl=holes.length;hl>h;h++)ahole=holes[h],THREE.ShapeUtils.isClockWise(ahole)&&(holes[h]=ahole.reverse());reverse=!1}var faces=THREE.ShapeUtils.triangulateShape(vertices,holes),contour=vertices;for(h=0,hl=holes.length;hl>h;h++)ahole=holes[h],vertices=vertices.concat(ahole);for(var b,bs,t,z,vert,face,vlen=vertices.length,flen=faces.length,contourMovements=[],i=0,il=contour.length,j=il-1,k=i+1;il>i;i++,j++,k++)j===il&&(j=0),k===il&&(k=0),contourMovements[i]=getBevelVec(contour[i],contour[j],contour[k]);var oneHoleMovements,holesMovements=[],verticesMovements=contourMovements.concat();for(h=0,hl=holes.length;hl>h;h++){for(ahole=holes[h],oneHoleMovements=[],i=0,il=ahole.length,j=il-1,k=i+1;il>i;i++,j++,k++)j===il&&(j=0),k===il&&(k=0),oneHoleMovements[i]=getBevelVec(ahole[i],ahole[j],ahole[k]);holesMovements.push(oneHoleMovements),verticesMovements=verticesMovements.concat(oneHoleMovements)}for(b=0;bevelSegments>b;b++){for(t=b/bevelSegments,z=bevelThickness*(1-t),bs=bevelSize*Math.sin(t*Math.PI/2),i=0,il=contour.length;il>i;i++)vert=scalePt2(contour[i],contourMovements[i],bs),v(vert.x,vert.y,-z);for(h=0,hl=holes.length;hl>h;h++)for(ahole=holes[h],oneHoleMovements=holesMovements[h],i=0,il=ahole.length;il>i;i++)vert=scalePt2(ahole[i],oneHoleMovements[i],bs),v(vert.x,vert.y,-z)}for(bs=bevelSize,i=0;vlen>i;i++)vert=bevelEnabled?scalePt2(vertices[i],verticesMovements[i],bs):vertices[i],extrudeByPath?(normal.copy(splineTube.normals[0]).multiplyScalar(vert.x),binormal.copy(splineTube.binormals[0]).multiplyScalar(vert.y),position2.copy(extrudePts[0]).add(normal).add(binormal),v(position2.x,position2.y,position2.z)):v(vert.x,vert.y,0);var s;for(s=1;steps>=s;s++)for(i=0;vlen>i;i++)vert=bevelEnabled?scalePt2(vertices[i],verticesMovements[i],bs):vertices[i],extrudeByPath?(normal.copy(splineTube.normals[s]).multiplyScalar(vert.x),binormal.copy(splineTube.binormals[s]).multiplyScalar(vert.y),position2.copy(extrudePts[s]).add(normal).add(binormal),v(position2.x,position2.y,position2.z)):v(vert.x,vert.y,amount/steps*s);for(b=bevelSegments-1;b>=0;b--){for(t=b/bevelSegments,z=bevelThickness*(1-t),bs=bevelSize*Math.sin(t*Math.PI/2),i=0,il=contour.length;il>i;i++)vert=scalePt2(contour[i],contourMovements[i],bs),v(vert.x,vert.y,amount+z);for(h=0,hl=holes.length;hl>h;h++)for(ahole=holes[h],oneHoleMovements=holesMovements[h],i=0,il=ahole.length;il>i;i++)vert=scalePt2(ahole[i],oneHoleMovements[i],bs),extrudeByPath?v(vert.x,vert.y+extrudePts[steps-1].y,extrudePts[steps-1].x+z):v(vert.x,vert.y,amount+z)}buildLidFaces(),buildSideFaces()}, +THREE.ExtrudeGeometry.WorldUVGenerator={generateTopUV:function(geometry,indexA,indexB,indexC){var vertices=geometry.vertices,a=vertices[indexA],b=vertices[indexB],c=vertices[indexC];return[new THREE.Vector2(a.x,a.y),new THREE.Vector2(b.x,b.y),new THREE.Vector2(c.x,c.y)]},generateSideWallUV:function(geometry,indexA,indexB,indexC,indexD){var vertices=geometry.vertices,a=vertices[indexA],b=vertices[indexB],c=vertices[indexC],d=vertices[indexD];return Math.abs(a.y-b.y)<.01?[new THREE.Vector2(a.x,1-a.z),new THREE.Vector2(b.x,1-b.z),new THREE.Vector2(c.x,1-c.z),new THREE.Vector2(d.x,1-d.z)]:[new THREE.Vector2(a.y,1-a.z),new THREE.Vector2(b.y,1-b.z),new THREE.Vector2(c.y,1-c.z),new THREE.Vector2(d.y,1-d.z)]}},THREE.ShapeGeometry=function(shapes,options){THREE.Geometry.call(this),this.type="ShapeGeometry",Array.isArray(shapes)===!1&&(shapes=[shapes]),this.addShapeList(shapes,options),this.computeFaceNormals()},THREE.ShapeGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.ShapeGeometry.prototype.constructor=THREE.ShapeGeometry,THREE.ShapeGeometry.prototype.addShapeList=function(shapes,options){for(var i=0,l=shapes.length;l>i;i++)this.addShape(shapes[i],options);return this},THREE.ShapeGeometry.prototype.addShape=function(shape,options){void 0===options&&(options={});var i,l,hole,curveSegments=void 0!==options.curveSegments?options.curveSegments:12,material=options.material,uvgen=void 0===options.UVGenerator?THREE.ExtrudeGeometry.WorldUVGenerator:options.UVGenerator,shapesOffset=this.vertices.length,shapePoints=shape.extractPoints(curveSegments),vertices=shapePoints.shape,holes=shapePoints.holes,reverse=!THREE.ShapeUtils.isClockWise(vertices);if(reverse){for(vertices=vertices.reverse(),i=0,l=holes.length;l>i;i++)hole=holes[i],THREE.ShapeUtils.isClockWise(hole)&&(holes[i]=hole.reverse());reverse=!1}var faces=THREE.ShapeUtils.triangulateShape(vertices,holes);for(i=0,l=holes.length;l>i;i++)hole=holes[i],vertices=vertices.concat(hole);var vert,face,vlen=vertices.length,flen=faces.length;for(i=0;vlen>i;i++)vert=vertices[i],this.vertices.push(new THREE.Vector3(vert.x,vert.y,0));for(i=0;flen>i;i++){face=faces[i];var a=face[0]+shapesOffset,b=face[1]+shapesOffset,c=face[2]+shapesOffset;this.faces.push(new THREE.Face3(a,b,c,null,null,material)),this.faceVertexUvs[0].push(uvgen.generateTopUV(this,a,b,c))}},THREE.LatheGeometry=function(points,segments,phiStart,phiLength){THREE.Geometry.call(this),this.type="LatheGeometry",this.parameters={points:points,segments:segments,phiStart:phiStart,phiLength:phiLength},segments=segments||12,phiStart=phiStart||0,phiLength=phiLength||2*Math.PI;for(var inversePointLength=1/(points.length-1),inverseSegments=1/segments,i=0,il=segments;il>=i;i++)for(var phi=phiStart+i*inverseSegments*phiLength,c=Math.cos(phi),s=Math.sin(phi),j=0,jl=points.length;jl>j;j++){var pt=points[j],vertex=new THREE.Vector3;vertex.x=c*pt.x-s*pt.y,vertex.y=s*pt.x+c*pt.y,vertex.z=pt.z,this.vertices.push(vertex)}for(var np=points.length,i=0,il=segments;il>i;i++)for(var j=0,jl=points.length-1;jl>j;j++){var base=j+np*i,a=base,b=base+np,c=base+1+np,d=base+1,u0=i*inverseSegments,v0=j*inversePointLength,u1=u0+inverseSegments,v1=v0+inversePointLength;this.faces.push(new THREE.Face3(a,b,d)),this.faceVertexUvs[0].push([new THREE.Vector2(u0,v0),new THREE.Vector2(u1,v0),new THREE.Vector2(u0,v1)]),this.faces.push(new THREE.Face3(b,c,d)),this.faceVertexUvs[0].push([new THREE.Vector2(u1,v0),new THREE.Vector2(u1,v1),new THREE.Vector2(u0,v1)])}this.mergeVertices(),this.computeFaceNormals(),this.computeVertexNormals()},THREE.LatheGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.LatheGeometry.prototype.constructor=THREE.LatheGeometry,THREE.PlaneGeometry=function(width,height,widthSegments,heightSegments){THREE.Geometry.call(this),this.type="PlaneGeometry",this.parameters={width:width,height:height,widthSegments:widthSegments,heightSegments:heightSegments},this.fromBufferGeometry(new THREE.PlaneBufferGeometry(width,height,widthSegments,heightSegments))},THREE.PlaneGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.PlaneGeometry.prototype.constructor=THREE.PlaneGeometry,THREE.PlaneGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.PlaneGeometry(parameters.width,parameters.height,parameters.widthSegments,parameters.heightSegments)},THREE.PlaneBufferGeometry=function(width,height,widthSegments,heightSegments){THREE.BufferGeometry.call(this),this.type="PlaneBufferGeometry",this.parameters={width:width,height:height,widthSegments:widthSegments,heightSegments:heightSegments};for(var width_half=width/2,height_half=height/2,gridX=Math.floor(widthSegments)||1,gridY=Math.floor(heightSegments)||1,gridX1=gridX+1,gridY1=gridY+1,segment_width=width/gridX,segment_height=height/gridY,vertices=new Float32Array(gridX1*gridY1*3),normals=new Float32Array(gridX1*gridY1*3),uvs=new Float32Array(gridX1*gridY1*2),offset=0,offset2=0,iy=0;gridY1>iy;iy++)for(var y=iy*segment_height-height_half,ix=0;gridX1>ix;ix++){var x=ix*segment_width-width_half;vertices[offset]=x,vertices[offset+1]=-y,normals[offset+2]=1,uvs[offset2]=ix/gridX,uvs[offset2+1]=1-iy/gridY,offset+=3,offset2+=2}offset=0;for(var indices=new(vertices.length/3>65535?Uint32Array:Uint16Array)(gridX*gridY*6),iy=0;gridY>iy;iy++)for(var ix=0;gridX>ix;ix++){var a=ix+gridX1*iy,b=ix+gridX1*(iy+1),c=ix+1+gridX1*(iy+1),d=ix+1+gridX1*iy;indices[offset]=a,indices[offset+1]=b,indices[offset+2]=d,indices[offset+3]=b,indices[offset+4]=c,indices[offset+5]=d,offset+=6}this.setIndex(new THREE.BufferAttribute(indices,1)),this.addAttribute("position",new THREE.BufferAttribute(vertices,3)),this.addAttribute("normal",new THREE.BufferAttribute(normals,3)),this.addAttribute("uv",new THREE.BufferAttribute(uvs,2))},THREE.PlaneBufferGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.PlaneBufferGeometry.prototype.constructor=THREE.PlaneBufferGeometry,THREE.PlaneBufferGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.PlaneBufferGeometry(parameters.width,parameters.height,parameters.widthSegments,parameters.heightSegments)},THREE.RingGeometry=function(innerRadius,outerRadius,thetaSegments,phiSegments,thetaStart,thetaLength){THREE.Geometry.call(this),this.type="RingGeometry",this.parameters={innerRadius:innerRadius,outerRadius:outerRadius,thetaSegments:thetaSegments,phiSegments:phiSegments,thetaStart:thetaStart,thetaLength:thetaLength},innerRadius=innerRadius||0,outerRadius=outerRadius||50,thetaStart=void 0!==thetaStart?thetaStart:0,thetaLength=void 0!==thetaLength?thetaLength:2*Math.PI,thetaSegments=void 0!==thetaSegments?Math.max(3,thetaSegments):8,phiSegments=void 0!==phiSegments?Math.max(1,phiSegments):8;var i,o,uvs=[],radius=innerRadius,radiusStep=(outerRadius-innerRadius)/phiSegments;for(i=0;phiSegments+1>i;i++){for(o=0;thetaSegments+1>o;o++){var vertex=new THREE.Vector3,segment=thetaStart+o/thetaSegments*thetaLength;vertex.x=radius*Math.cos(segment),vertex.y=radius*Math.sin(segment),this.vertices.push(vertex),uvs.push(new THREE.Vector2((vertex.x/outerRadius+1)/2,(vertex.y/outerRadius+1)/2))}radius+=radiusStep}var n=new THREE.Vector3(0,0,1);for(i=0;phiSegments>i;i++){var thetaSegment=i*(thetaSegments+1);for(o=0;thetaSegments>o;o++){var segment=o+thetaSegment,v1=segment,v2=segment+thetaSegments+1,v3=segment+thetaSegments+2;this.faces.push(new THREE.Face3(v1,v2,v3,[n.clone(),n.clone(),n.clone()])),this.faceVertexUvs[0].push([uvs[v1].clone(),uvs[v2].clone(),uvs[v3].clone()]),v1=segment,v2=segment+thetaSegments+2,v3=segment+1,this.faces.push(new THREE.Face3(v1,v2,v3,[n.clone(),n.clone(),n.clone()])),this.faceVertexUvs[0].push([uvs[v1].clone(),uvs[v2].clone(),uvs[v3].clone()])}}this.computeFaceNormals(),this.boundingSphere=new THREE.Sphere(new THREE.Vector3,radius)},THREE.RingGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.RingGeometry.prototype.constructor=THREE.RingGeometry,THREE.RingGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.RingGeometry(parameters.innerRadius,parameters.outerRadius,parameters.thetaSegments,parameters.phiSegments,parameters.thetaStart,parameters.thetaLength)},THREE.SphereGeometry=function(radius,widthSegments,heightSegments,phiStart,phiLength,thetaStart,thetaLength){THREE.Geometry.call(this),this.type="SphereGeometry",this.parameters={radius:radius,widthSegments:widthSegments,heightSegments:heightSegments,phiStart:phiStart,phiLength:phiLength,thetaStart:thetaStart,thetaLength:thetaLength},this.fromBufferGeometry(new THREE.SphereBufferGeometry(radius,widthSegments,heightSegments,phiStart,phiLength,thetaStart,thetaLength))},THREE.SphereGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.SphereGeometry.prototype.constructor=THREE.SphereGeometry,THREE.SphereGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.SphereGeometry(parameters.radius,parameters.widthSegments,parameters.heightSegments,parameters.phiStart,parameters.phiLength,parameters.thetaStart,parameters.thetaLength)},THREE.SphereBufferGeometry=function(radius,widthSegments,heightSegments,phiStart,phiLength,thetaStart,thetaLength){THREE.BufferGeometry.call(this),this.type="SphereBufferGeometry",this.parameters={radius:radius,widthSegments:widthSegments,heightSegments:heightSegments,phiStart:phiStart,phiLength:phiLength,thetaStart:thetaStart,thetaLength:thetaLength},radius=radius||50,widthSegments=Math.max(3,Math.floor(widthSegments)||8),heightSegments=Math.max(2,Math.floor(heightSegments)||6),phiStart=void 0!==phiStart?phiStart:0,phiLength=void 0!==phiLength?phiLength:2*Math.PI,thetaStart=void 0!==thetaStart?thetaStart:0,thetaLength=void 0!==thetaLength?thetaLength:Math.PI;for(var thetaEnd=thetaStart+thetaLength,vertexCount=(widthSegments+1)*(heightSegments+1),positions=new THREE.BufferAttribute(new Float32Array(3*vertexCount),3),normals=new THREE.BufferAttribute(new Float32Array(3*vertexCount),3),uvs=new THREE.BufferAttribute(new Float32Array(2*vertexCount),2),index=0,vertices=[],normal=new THREE.Vector3,y=0;heightSegments>=y;y++){for(var verticesRow=[],v=y/heightSegments,x=0;widthSegments>=x;x++){var u=x/widthSegments,px=-radius*Math.cos(phiStart+u*phiLength)*Math.sin(thetaStart+v*thetaLength),py=radius*Math.cos(thetaStart+v*thetaLength),pz=radius*Math.sin(phiStart+u*phiLength)*Math.sin(thetaStart+v*thetaLength);normal.set(px,py,pz).normalize(),positions.setXYZ(index,px,py,pz),normals.setXYZ(index,normal.x,normal.y,normal.z),uvs.setXY(index,u,1-v),verticesRow.push(index),index++}vertices.push(verticesRow)}for(var indices=[],y=0;heightSegments>y;y++)for(var x=0;widthSegments>x;x++){var v1=vertices[y][x+1],v2=vertices[y][x],v3=vertices[y+1][x],v4=vertices[y+1][x+1];(0!==y||thetaStart>0)&&indices.push(v1,v2,v4),(y!==heightSegments-1||thetaEnd65535?THREE.Uint32Attribute:THREE.Uint16Attribute)(indices,1)),this.addAttribute("position",positions),this.addAttribute("normal",normals),this.addAttribute("uv",uvs),this.boundingSphere=new THREE.Sphere(new THREE.Vector3,radius)},THREE.SphereBufferGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.SphereBufferGeometry.prototype.constructor=THREE.SphereBufferGeometry,THREE.SphereBufferGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.SphereBufferGeometry(parameters.radius,parameters.widthSegments,parameters.heightSegments,parameters.phiStart,parameters.phiLength,parameters.thetaStart,parameters.thetaLength)},THREE.TorusGeometry=function(radius,tube,radialSegments,tubularSegments,arc){THREE.Geometry.call(this),this.type="TorusGeometry",this.parameters={radius:radius,tube:tube,radialSegments:radialSegments,tubularSegments:tubularSegments,arc:arc},radius=radius||100,tube=tube||40,radialSegments=radialSegments||8,tubularSegments=tubularSegments||6,arc=arc||2*Math.PI;for(var center=new THREE.Vector3,uvs=[],normals=[],j=0;radialSegments>=j;j++)for(var i=0;tubularSegments>=i;i++){var u=i/tubularSegments*arc,v=j/radialSegments*Math.PI*2;center.x=radius*Math.cos(u),center.y=radius*Math.sin(u);var vertex=new THREE.Vector3;vertex.x=(radius+tube*Math.cos(v))*Math.cos(u),vertex.y=(radius+tube*Math.cos(v))*Math.sin(u),vertex.z=tube*Math.sin(v),this.vertices.push(vertex),uvs.push(new THREE.Vector2(i/tubularSegments,j/radialSegments)),normals.push(vertex.clone().sub(center).normalize())}for(var j=1;radialSegments>=j;j++)for(var i=1;tubularSegments>=i;i++){var a=(tubularSegments+1)*j+i-1,b=(tubularSegments+1)*(j-1)+i-1,c=(tubularSegments+1)*(j-1)+i,d=(tubularSegments+1)*j+i,face=new THREE.Face3(a,b,d,[normals[a].clone(),normals[b].clone(),normals[d].clone()]);this.faces.push(face),this.faceVertexUvs[0].push([uvs[a].clone(),uvs[b].clone(),uvs[d].clone()]),face=new THREE.Face3(b,c,d,[normals[b].clone(),normals[c].clone(),normals[d].clone()]),this.faces.push(face),this.faceVertexUvs[0].push([uvs[b].clone(),uvs[c].clone(),uvs[d].clone()])}this.computeFaceNormals()},THREE.TorusGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.TorusGeometry.prototype.constructor=THREE.TorusGeometry,THREE.TorusGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.TorusGeometry(parameters.radius,parameters.tube,parameters.radialSegments,parameters.tubularSegments,parameters.arc)},THREE.TorusKnotGeometry=function(radius,tube,radialSegments,tubularSegments,p,q,heightScale){function getPos(u,in_q,in_p,radius,heightScale){var cu=Math.cos(u),su=Math.sin(u),quOverP=in_q/in_p*u,cs=Math.cos(quOverP),tx=radius*(2+cs)*.5*cu,ty=radius*(2+cs)*su*.5,tz=heightScale*radius*Math.sin(quOverP)*.5;return new THREE.Vector3(tx,ty,tz)}THREE.Geometry.call(this),this.type="TorusKnotGeometry",this.parameters={radius:radius,tube:tube,radialSegments:radialSegments,tubularSegments:tubularSegments,p:p,q:q,heightScale:heightScale},radius=radius||100,tube=tube||40,radialSegments=radialSegments||64,tubularSegments=tubularSegments||8,p=p||2,q=q||3,heightScale=heightScale||1;for(var grid=new Array(radialSegments),tang=new THREE.Vector3,n=new THREE.Vector3,bitan=new THREE.Vector3,i=0;radialSegments>i;++i){grid[i]=new Array(tubularSegments);var u=i/radialSegments*2*p*Math.PI,p1=getPos(u,q,p,radius,heightScale),p2=getPos(u+.01,q,p,radius,heightScale);tang.subVectors(p2,p1),n.addVectors(p2,p1),bitan.crossVectors(tang,n),n.crossVectors(bitan,tang),bitan.normalize(),n.normalize();for(var j=0;tubularSegments>j;++j){var v=j/tubularSegments*2*Math.PI,cx=-tube*Math.cos(v),cy=tube*Math.sin(v),pos=new THREE.Vector3;pos.x=p1.x+cx*n.x+cy*bitan.x,pos.y=p1.y+cx*n.y+cy*bitan.y,pos.z=p1.z+cx*n.z+cy*bitan.z,grid[i][j]=this.vertices.push(pos)-1}}for(var i=0;radialSegments>i;++i)for(var j=0;tubularSegments>j;++j){var ip=(i+1)%radialSegments,jp=(j+1)%tubularSegments,a=grid[i][j],b=grid[ip][j],c=grid[ip][jp],d=grid[i][jp],uva=new THREE.Vector2(i/radialSegments,j/tubularSegments),uvb=new THREE.Vector2((i+1)/radialSegments,j/tubularSegments),uvc=new THREE.Vector2((i+1)/radialSegments,(j+1)/tubularSegments),uvd=new THREE.Vector2(i/radialSegments,(j+1)/tubularSegments);this.faces.push(new THREE.Face3(a,b,d)),this.faceVertexUvs[0].push([uva,uvb,uvd]),this.faces.push(new THREE.Face3(b,c,d)),this.faceVertexUvs[0].push([uvb.clone(),uvc,uvd.clone()])}this.computeFaceNormals(),this.computeVertexNormals()},THREE.TorusKnotGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.TorusKnotGeometry.prototype.constructor=THREE.TorusKnotGeometry,THREE.TorusKnotGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.TorusKnotGeometry(parameters.radius,parameters.tube,parameters.radialSegments,parameters.tubularSegments,parameters.p,parameters.q,parameters.heightScale)},THREE.TubeGeometry=function(path,segments,radius,radialSegments,closed,taper){function vert(x,y,z){return scope.vertices.push(new THREE.Vector3(x,y,z))-1}THREE.Geometry.call(this),this.type="TubeGeometry",this.parameters={path:path,segments:segments,radius:radius,radialSegments:radialSegments,closed:closed,taper:taper},segments=segments||64,radius=radius||1,radialSegments=radialSegments||8,closed=closed||!1,taper=taper||THREE.TubeGeometry.NoTaper;var tangent,normal,binormal,u,v,r,cx,cy,pos,i,j,ip,jp,a,b,c,d,uva,uvb,uvc,uvd,grid=[],scope=this,numpoints=segments+1,pos2=new THREE.Vector3,frames=new THREE.TubeGeometry.FrenetFrames(path,segments,closed),tangents=frames.tangents,normals=frames.normals,binormals=frames.binormals;for(this.tangents=tangents,this.normals=normals,this.binormals=binormals,i=0;numpoints>i;i++)for(grid[i]=[],u=i/(numpoints-1),pos=path.getPointAt(u),tangent=tangents[i],normal=normals[i],binormal=binormals[i],r=radius*taper(u),j=0;radialSegments>j;j++)v=j/radialSegments*2*Math.PI,cx=-r*Math.cos(v),cy=r*Math.sin(v),pos2.copy(pos),pos2.x+=cx*normal.x+cy*binormal.x,pos2.y+=cx*normal.y+cy*binormal.y,pos2.z+=cx*normal.z+cy*binormal.z,grid[i][j]=vert(pos2.x,pos2.y,pos2.z);for(i=0;segments>i;i++)for(j=0;radialSegments>j;j++)ip=closed?(i+1)%segments:i+1,jp=(j+1)%radialSegments,a=grid[i][j],b=grid[ip][j],c=grid[ip][jp],d=grid[i][jp],uva=new THREE.Vector2(i/segments,j/radialSegments),uvb=new THREE.Vector2((i+1)/segments,j/radialSegments),uvc=new THREE.Vector2((i+1)/segments,(j+1)/radialSegments),uvd=new THREE.Vector2(i/segments,(j+1)/radialSegments),this.faces.push(new THREE.Face3(a,b,d)),this.faceVertexUvs[0].push([uva,uvb,uvd]),this.faces.push(new THREE.Face3(b,c,d)),this.faceVertexUvs[0].push([uvb.clone(),uvc,uvd.clone()]);this.computeFaceNormals(),this.computeVertexNormals()},THREE.TubeGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.TubeGeometry.prototype.constructor=THREE.TubeGeometry,THREE.TubeGeometry.prototype.clone=function(){return new this.constructor(this.parameters.path,this.parameters.segments,this.parameters.radius,this.parameters.radialSegments,this.parameters.closed,this.parameters.taper)},THREE.TubeGeometry.NoTaper=function(u){return 1},THREE.TubeGeometry.SinusoidalTaper=function(u){return Math.sin(Math.PI*u)},THREE.TubeGeometry.FrenetFrames=function(path,segments,closed){function initialNormal3(){normals[0]=new THREE.Vector3,binormals[0]=new THREE.Vector3,smallest=Number.MAX_VALUE,tx=Math.abs(tangents[0].x),ty=Math.abs(tangents[0].y),tz=Math.abs(tangents[0].z),smallest>=tx&&(smallest=tx,normal.set(1,0,0)),smallest>=ty&&(smallest=ty,normal.set(0,1,0)),smallest>=tz&&normal.set(0,0,1),vec.crossVectors(tangents[0],normal).normalize(),normals[0].crossVectors(tangents[0],vec),binormals[0].crossVectors(tangents[0],normals[0])}var theta,smallest,tx,ty,tz,i,u,normal=new THREE.Vector3,tangents=[],normals=[],binormals=[],vec=new THREE.Vector3,mat=new THREE.Matrix4,numpoints=segments+1;for(this.tangents=tangents,this.normals=normals,this.binormals=binormals,i=0;numpoints>i;i++)u=i/(numpoints-1),tangents[i]=path.getTangentAt(u),tangents[i].normalize();for(initialNormal3(),i=1;numpoints>i;i++)normals[i]=normals[i-1].clone(),binormals[i]=binormals[i-1].clone(),vec.crossVectors(tangents[i-1],tangents[i]),vec.length()>Number.EPSILON&&(vec.normalize(),theta=Math.acos(THREE.Math.clamp(tangents[i-1].dot(tangents[i]),-1,1)),normals[i].applyMatrix4(mat.makeRotationAxis(vec,theta))),binormals[i].crossVectors(tangents[i],normals[i]);if(closed)for(theta=Math.acos(THREE.Math.clamp(normals[0].dot(normals[numpoints-1]),-1,1)),theta/=numpoints-1,tangents[0].dot(vec.crossVectors(normals[0],normals[numpoints-1]))>0&&(theta=-theta),i=1;numpoints>i;i++)normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i],theta*i)),binormals[i].crossVectors(tangents[i],normals[i])},THREE.PolyhedronGeometry=function(vertices,indices,radius,detail){function prepare(vector){var vertex=vector.normalize().clone();vertex.index=that.vertices.push(vertex)-1;var u=azimuth(vector)/2/Math.PI+.5,v=inclination(vector)/Math.PI+.5;return vertex.uv=new THREE.Vector2(u,1-v),vertex}function make(v1,v2,v3,materialIndex){var face=new THREE.Face3(v1.index,v2.index,v3.index,[v1.clone(),v2.clone(),v3.clone()],void 0,materialIndex);that.faces.push(face),centroid.copy(v1).add(v2).add(v3).divideScalar(3);var azi=azimuth(centroid);that.faceVertexUvs[0].push([correctUV(v1.uv,v1,azi),correctUV(v2.uv,v2,azi),correctUV(v3.uv,v3,azi)])}function subdivide(face,detail){for(var cols=Math.pow(2,detail),a=prepare(that.vertices[face.a]),b=prepare(that.vertices[face.b]),c=prepare(that.vertices[face.c]),v=[],materialIndex=face.materialIndex,i=0;cols>=i;i++){v[i]=[];for(var aj=prepare(a.clone().lerp(c,i/cols)),bj=prepare(b.clone().lerp(c,i/cols)),rows=cols-i,j=0;rows>=j;j++)0===j&&i===cols?v[i][j]=aj:v[i][j]=prepare(aj.clone().lerp(bj,j/rows))}for(var i=0;cols>i;i++)for(var j=0;2*(cols-i)-1>j;j++){var k=Math.floor(j/2);j%2===0?make(v[i][k+1],v[i+1][k],v[i][k],materialIndex):make(v[i][k+1],v[i+1][k+1],v[i+1][k],materialIndex)}}function azimuth(vector){return Math.atan2(vector.z,-vector.x)}function inclination(vector){return Math.atan2(-vector.y,Math.sqrt(vector.x*vector.x+vector.z*vector.z))}function correctUV(uv,vector,azimuth){return 0>azimuth&&1===uv.x&&(uv=new THREE.Vector2(uv.x-1,uv.y)),0===vector.x&&0===vector.z&&(uv=new THREE.Vector2(azimuth/2/Math.PI+.5,uv.y)),uv.clone()}THREE.Geometry.call(this),this.type="PolyhedronGeometry",this.parameters={vertices:vertices,indices:indices,radius:radius,detail:detail},radius=radius||1,detail=detail||0;for(var that=this,i=0,l=vertices.length;l>i;i+=3)prepare(new THREE.Vector3(vertices[i],vertices[i+1],vertices[i+2]));for(var p=this.vertices,faces=[],i=0,j=0,l=indices.length;l>i;i+=3,j++){var v1=p[indices[i]],v2=p[indices[i+1]],v3=p[indices[i+2]];faces[j]=new THREE.Face3(v1.index,v2.index,v3.index,[v1.clone(),v2.clone(),v3.clone()],void 0,j)}for(var centroid=new THREE.Vector3,i=0,l=faces.length;l>i;i++)subdivide(faces[i],detail);for(var i=0,l=this.faceVertexUvs[0].length;l>i;i++){var uvs=this.faceVertexUvs[0][i],x0=uvs[0].x,x1=uvs[1].x,x2=uvs[2].x,max=Math.max(x0,x1,x2),min=Math.min(x0,x1,x2);max>.9&&.1>min&&(.2>x0&&(uvs[0].x+=1),.2>x1&&(uvs[1].x+=1),.2>x2&&(uvs[2].x+=1))}for(var i=0,l=this.vertices.length;l>i;i++)this.vertices[i].multiplyScalar(radius);this.mergeVertices(),this.computeFaceNormals(),this.boundingSphere=new THREE.Sphere(new THREE.Vector3,radius)},THREE.PolyhedronGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.PolyhedronGeometry.prototype.constructor=THREE.PolyhedronGeometry,THREE.PolyhedronGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.PolyhedronGeometry(parameters.vertices,parameters.indices,parameters.radius,parameters.detail)},THREE.DodecahedronGeometry=function(radius,detail){var t=(1+Math.sqrt(5))/2,r=1/t,vertices=[-1,-1,-1,-1,-1,1,-1,1,-1,-1,1,1,1,-1,-1,1,-1,1,1,1,-1,1,1,1,0,-r,-t,0,-r,t,0,r,-t,0,r,t,-r,-t,0,-r,t,0,r,-t,0,r,t,0,-t,0,-r,t,0,-r,-t,0,r,t,0,r],indices=[3,11,7,3,7,15,3,15,13,7,19,17,7,17,6,7,6,15,17,4,8,17,8,10,17,10,6,8,0,16,8,16,2,8,2,10,0,12,1,0,1,18,0,18,16,6,10,2,6,2,13,6,13,15,2,16,18,2,18,3,2,3,13,18,1,9,18,9,11,18,11,3,4,14,12,4,12,0,4,0,8,11,9,5,11,5,19,11,19,7,19,5,14,19,14,4,19,4,17,1,12,14,1,14,5,1,5,9];THREE.PolyhedronGeometry.call(this,vertices,indices,radius,detail),this.type="DodecahedronGeometry",this.parameters={radius:radius,detail:detail}},THREE.DodecahedronGeometry.prototype=Object.create(THREE.PolyhedronGeometry.prototype),THREE.DodecahedronGeometry.prototype.constructor=THREE.DodecahedronGeometry,THREE.DodecahedronGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.DodecahedronGeometry(parameters.radius,parameters.detail)},THREE.IcosahedronGeometry=function(radius,detail){var t=(1+Math.sqrt(5))/2,vertices=[-1,t,0,1,t,0,-1,-t,0,1,-t,0,0,-1,t,0,1,t,0,-1,-t,0,1,-t,t,0,-1,t,0,1,-t,0,-1,-t,0,1],indices=[0,11,5,0,5,1,0,1,7,0,7,10,0,10,11,1,5,9,5,11,4,11,10,2,10,7,6,7,1,8,3,9,4,3,4,2,3,2,6,3,6,8,3,8,9,4,9,5,2,4,11,6,2,10,8,6,7,9,8,1];THREE.PolyhedronGeometry.call(this,vertices,indices,radius,detail),this.type="IcosahedronGeometry",this.parameters={radius:radius,detail:detail}},THREE.IcosahedronGeometry.prototype=Object.create(THREE.PolyhedronGeometry.prototype),THREE.IcosahedronGeometry.prototype.constructor=THREE.IcosahedronGeometry,THREE.IcosahedronGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.IcosahedronGeometry(parameters.radius,parameters.detail)},THREE.OctahedronGeometry=function(radius,detail){var vertices=[1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1],indices=[0,2,4,0,4,3,0,3,5,0,5,2,1,2,5,1,5,3,1,3,4,1,4,2];THREE.PolyhedronGeometry.call(this,vertices,indices,radius,detail),this.type="OctahedronGeometry",this.parameters={radius:radius,detail:detail}},THREE.OctahedronGeometry.prototype=Object.create(THREE.PolyhedronGeometry.prototype),THREE.OctahedronGeometry.prototype.constructor=THREE.OctahedronGeometry,THREE.OctahedronGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.OctahedronGeometry(parameters.radius,parameters.detail)},THREE.TetrahedronGeometry=function(radius,detail){var vertices=[1,1,1,-1,-1,1,-1,1,-1,1,-1,-1],indices=[2,1,0,0,3,2,1,3,0,2,3,1];THREE.PolyhedronGeometry.call(this,vertices,indices,radius,detail),this.type="TetrahedronGeometry",this.parameters={radius:radius,detail:detail}},THREE.TetrahedronGeometry.prototype=Object.create(THREE.PolyhedronGeometry.prototype),THREE.TetrahedronGeometry.prototype.constructor=THREE.TetrahedronGeometry,THREE.TetrahedronGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.TetrahedronGeometry(parameters.radius,parameters.detail)},THREE.ParametricGeometry=function(func,slices,stacks){THREE.Geometry.call(this),this.type="ParametricGeometry",this.parameters={func:func,slices:slices,stacks:stacks};var i,j,p,u,v,verts=this.vertices,faces=this.faces,uvs=this.faceVertexUvs[0],sliceCount=slices+1;for(i=0;stacks>=i;i++)for(v=i/stacks,j=0;slices>=j;j++)u=j/slices,p=func(u,v),verts.push(p);var a,b,c,d,uva,uvb,uvc,uvd;for(i=0;stacks>i;i++)for(j=0;slices>j;j++)a=i*sliceCount+j,b=i*sliceCount+j+1,c=(i+1)*sliceCount+j+1,d=(i+1)*sliceCount+j,uva=new THREE.Vector2(j/slices,i/stacks),uvb=new THREE.Vector2((j+1)/slices,i/stacks),uvc=new THREE.Vector2((j+1)/slices,(i+1)/stacks),uvd=new THREE.Vector2(j/slices,(i+1)/stacks),faces.push(new THREE.Face3(a,b,d)),uvs.push([uva,uvb,uvd]),faces.push(new THREE.Face3(b,c,d)),uvs.push([uvb.clone(),uvc,uvd.clone()]);this.computeFaceNormals(),this.computeVertexNormals()},THREE.ParametricGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.ParametricGeometry.prototype.constructor=THREE.ParametricGeometry,THREE.WireframeGeometry=function(geometry){function sortFunction(a,b){return a-b}THREE.BufferGeometry.call(this);var edge=[0,0],hash={},keys=["a","b","c"];if(geometry instanceof THREE.Geometry){for(var vertices=geometry.vertices,faces=geometry.faces,numEdges=0,edges=new Uint32Array(6*faces.length),i=0,l=faces.length;l>i;i++)for(var face=faces[i],j=0;3>j;j++){edge[0]=face[keys[j]],edge[1]=face[keys[(j+1)%3]],edge.sort(sortFunction);var key=edge.toString();void 0===hash[key]&&(edges[2*numEdges]=edge[0],edges[2*numEdges+1]=edge[1],hash[key]=!0,numEdges++)}for(var coords=new Float32Array(2*numEdges*3),i=0,l=numEdges;l>i;i++)for(var j=0;2>j;j++){var vertex=vertices[edges[2*i+j]],index=6*i+3*j;coords[index+0]=vertex.x,coords[index+1]=vertex.y,coords[index+2]=vertex.z}this.addAttribute("position",new THREE.BufferAttribute(coords,3))}else if(geometry instanceof THREE.BufferGeometry)if(null!==geometry.index){var indices=geometry.index.array,vertices=geometry.attributes.position,drawcalls=geometry.drawcalls,numEdges=0;0===drawcalls.length&&geometry.addGroup(0,indices.length);for(var edges=new Uint32Array(2*indices.length),o=0,ol=drawcalls.length;ol>o;++o)for(var drawcall=drawcalls[o],start=drawcall.start,count=drawcall.count,i=start,il=start+count;il>i;i+=3)for(var j=0;3>j;j++){edge[0]=indices[i+j],edge[1]=indices[i+(j+1)%3],edge.sort(sortFunction);var key=edge.toString();void 0===hash[key]&&(edges[2*numEdges]=edge[0],edges[2*numEdges+1]=edge[1],hash[key]=!0,numEdges++)}for(var coords=new Float32Array(2*numEdges*3),i=0,l=numEdges;l>i;i++)for(var j=0;2>j;j++){var index=6*i+3*j,index2=edges[2*i+j];coords[index+0]=vertices.getX(index2),coords[index+1]=vertices.getY(index2),coords[index+2]=vertices.getZ(index2)}this.addAttribute("position",new THREE.BufferAttribute(coords,3))}else{for(var vertices=geometry.attributes.position.array,numEdges=vertices.length/3,numTris=numEdges/3,coords=new Float32Array(2*numEdges*3),i=0,l=numTris;l>i;i++)for(var j=0;3>j;j++){var index=18*i+6*j,index1=9*i+3*j;coords[index+0]=vertices[index1],coords[index+1]=vertices[index1+1],coords[index+2]=vertices[index1+2];var index2=9*i+3*((j+1)%3);coords[index+3]=vertices[index2],coords[index+4]=vertices[index2+1],coords[index+5]=vertices[index2+2]}this.addAttribute("position",new THREE.BufferAttribute(coords,3))}},THREE.WireframeGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.WireframeGeometry.prototype.constructor=THREE.WireframeGeometry,THREE.AxisHelper=function(size){size=size||1;var vertices=new Float32Array([0,0,0,size,0,0,0,0,0,0,size,0,0,0,0,0,0,size]),colors=new Float32Array([1,0,0,1,.6,0,0,1,0,.6,1,0,0,0,1,0,.6,1]),geometry=new THREE.BufferGeometry;geometry.addAttribute("position",new THREE.BufferAttribute(vertices,3)),geometry.addAttribute("color",new THREE.BufferAttribute(colors,3));var material=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});THREE.LineSegments.call(this,geometry,material)},THREE.AxisHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.AxisHelper.prototype.constructor=THREE.AxisHelper,THREE.ArrowHelper=function(){var lineGeometry=new THREE.Geometry;lineGeometry.vertices.push(new THREE.Vector3(0,0,0),new THREE.Vector3(0,1,0));var coneGeometry=new THREE.CylinderGeometry(0,.5,1,5,1);return coneGeometry.translate(0,-.5,0),function(dir,origin,length,color,headLength,headWidth){THREE.Object3D.call(this),void 0===color&&(color=16776960),void 0===length&&(length=1),void 0===headLength&&(headLength=.2*length),void 0===headWidth&&(headWidth=.2*headLength),this.position.copy(origin),length>headLength&&(this.line=new THREE.Line(lineGeometry,new THREE.LineBasicMaterial({color:color})),this.line.matrixAutoUpdate=!1,this.add(this.line)),this.cone=new THREE.Mesh(coneGeometry,new THREE.MeshBasicMaterial({color:color})),this.cone.matrixAutoUpdate=!1,this.add(this.cone),this.setDirection(dir),this.setLength(length,headLength,headWidth)}}(),THREE.ArrowHelper.prototype=Object.create(THREE.Object3D.prototype),THREE.ArrowHelper.prototype.constructor=THREE.ArrowHelper,THREE.ArrowHelper.prototype.setDirection=function(){var radians,axis=new THREE.Vector3;return function(dir){dir.y>.99999?this.quaternion.set(0,0,0,1):dir.y<-.99999?this.quaternion.set(1,0,0,0):(axis.set(dir.z,0,-dir.x).normalize(),radians=Math.acos(dir.y),this.quaternion.setFromAxisAngle(axis,radians))}}(),THREE.ArrowHelper.prototype.setLength=function(length,headLength,headWidth){void 0===headLength&&(headLength=.2*length),void 0===headWidth&&(headWidth=.2*headLength),length>headLength&&(this.line.scale.set(1,length-headLength,1),this.line.updateMatrix()),this.cone.scale.set(headWidth,headLength,headWidth),this.cone.position.y=length,this.cone.updateMatrix()},THREE.ArrowHelper.prototype.setColor=function(color){void 0!==this.line&&this.line.material.color.set(color),this.cone.material.color.set(color)},THREE.BoxHelper=function(object){var indices=new Uint16Array([0,1,1,2,2,3,3,0,4,5,5,6,6,7,7,4,0,4,1,5,2,6,3,7]),positions=new Float32Array(24),geometry=new THREE.BufferGeometry;geometry.setIndex(new THREE.BufferAttribute(indices,1)),geometry.addAttribute("position",new THREE.BufferAttribute(positions,3)),THREE.LineSegments.call(this,geometry,new THREE.LineBasicMaterial({color:16776960})),void 0!==object&&this.update(object)},THREE.BoxHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.BoxHelper.prototype.constructor=THREE.BoxHelper, +THREE.BoxHelper.prototype.update=function(){var box=new THREE.Box3;return function(object){if(box.setFromObject(object),!box.empty()){var min=box.min,max=box.max,position=this.geometry.attributes.position,array=position.array;array[0]=max.x,array[1]=max.y,array[2]=max.z,array[3]=min.x,array[4]=max.y,array[5]=max.z,array[6]=min.x,array[7]=min.y,array[8]=max.z,array[9]=max.x,array[10]=min.y,array[11]=max.z,array[12]=max.x,array[13]=max.y,array[14]=min.z,array[15]=min.x,array[16]=max.y,array[17]=min.z,array[18]=min.x,array[19]=min.y,array[20]=min.z,array[21]=max.x,array[22]=min.y,array[23]=min.z,position.needsUpdate=!0,this.geometry.computeBoundingSphere()}}}(),THREE.BoundingBoxHelper=function(object,hex){var color=void 0!==hex?hex:8947848;this.object=object,this.box=new THREE.Box3,THREE.Mesh.call(this,new THREE.BoxGeometry(1,1,1),new THREE.MeshBasicMaterial({color:color,wireframe:!0}))},THREE.BoundingBoxHelper.prototype=Object.create(THREE.Mesh.prototype),THREE.BoundingBoxHelper.prototype.constructor=THREE.BoundingBoxHelper,THREE.BoundingBoxHelper.prototype.update=function(){this.box.setFromObject(this.object),this.box.size(this.scale),this.box.center(this.position)},THREE.CameraHelper=function(camera){function addLine(a,b,hex){addPoint(a,hex),addPoint(b,hex)}function addPoint(id,hex){geometry.vertices.push(new THREE.Vector3),geometry.colors.push(new THREE.Color(hex)),void 0===pointMap[id]&&(pointMap[id]=[]),pointMap[id].push(geometry.vertices.length-1)}var geometry=new THREE.Geometry,material=new THREE.LineBasicMaterial({color:16777215,vertexColors:THREE.FaceColors}),pointMap={},hexFrustum=16755200,hexCone=16711680,hexUp=43775,hexTarget=16777215,hexCross=3355443;addLine("n1","n2",hexFrustum),addLine("n2","n4",hexFrustum),addLine("n4","n3",hexFrustum),addLine("n3","n1",hexFrustum),addLine("f1","f2",hexFrustum),addLine("f2","f4",hexFrustum),addLine("f4","f3",hexFrustum),addLine("f3","f1",hexFrustum),addLine("n1","f1",hexFrustum),addLine("n2","f2",hexFrustum),addLine("n3","f3",hexFrustum),addLine("n4","f4",hexFrustum),addLine("p","n1",hexCone),addLine("p","n2",hexCone),addLine("p","n3",hexCone),addLine("p","n4",hexCone),addLine("u1","u2",hexUp),addLine("u2","u3",hexUp),addLine("u3","u1",hexUp),addLine("c","t",hexTarget),addLine("p","c",hexCross),addLine("cn1","cn2",hexCross),addLine("cn3","cn4",hexCross),addLine("cf1","cf2",hexCross),addLine("cf3","cf4",hexCross),THREE.LineSegments.call(this,geometry,material),this.camera=camera,this.camera.updateProjectionMatrix(),this.matrix=camera.matrixWorld,this.matrixAutoUpdate=!1,this.pointMap=pointMap,this.update()},THREE.CameraHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.CameraHelper.prototype.constructor=THREE.CameraHelper,THREE.CameraHelper.prototype.update=function(){function setPoint(point,x,y,z){vector.set(x,y,z).unproject(camera);var points=pointMap[point];if(void 0!==points)for(var i=0,il=points.length;il>i;i++)geometry.vertices[points[i]].copy(vector)}var geometry,pointMap,vector=new THREE.Vector3,camera=new THREE.Camera;return function(){geometry=this.geometry,pointMap=this.pointMap;var w=1,h=1;camera.projectionMatrix.copy(this.camera.projectionMatrix),setPoint("c",0,0,-1),setPoint("t",0,0,1),setPoint("n1",-w,-h,-1),setPoint("n2",w,-h,-1),setPoint("n3",-w,h,-1),setPoint("n4",w,h,-1),setPoint("f1",-w,-h,1),setPoint("f2",w,-h,1),setPoint("f3",-w,h,1),setPoint("f4",w,h,1),setPoint("u1",.7*w,1.1*h,-1),setPoint("u2",.7*-w,1.1*h,-1),setPoint("u3",0,2*h,-1),setPoint("cf1",-w,0,1),setPoint("cf2",w,0,1),setPoint("cf3",0,-h,1),setPoint("cf4",0,h,1),setPoint("cn1",-w,0,-1),setPoint("cn2",w,0,-1),setPoint("cn3",0,-h,-1),setPoint("cn4",0,h,-1),geometry.verticesNeedUpdate=!0}}(),THREE.DirectionalLightHelper=function(light,size){THREE.Object3D.call(this),this.light=light,this.light.updateMatrixWorld(),this.matrix=light.matrixWorld,this.matrixAutoUpdate=!1,size=size||1;var geometry=new THREE.Geometry;geometry.vertices.push(new THREE.Vector3(-size,size,0),new THREE.Vector3(size,size,0),new THREE.Vector3(size,-size,0),new THREE.Vector3(-size,-size,0),new THREE.Vector3(-size,size,0));var material=new THREE.LineBasicMaterial({fog:!1});material.color.copy(this.light.color).multiplyScalar(this.light.intensity),this.lightPlane=new THREE.Line(geometry,material),this.add(this.lightPlane),geometry=new THREE.Geometry,geometry.vertices.push(new THREE.Vector3,new THREE.Vector3),material=new THREE.LineBasicMaterial({fog:!1}),material.color.copy(this.light.color).multiplyScalar(this.light.intensity),this.targetLine=new THREE.Line(geometry,material),this.add(this.targetLine),this.update()},THREE.DirectionalLightHelper.prototype=Object.create(THREE.Object3D.prototype),THREE.DirectionalLightHelper.prototype.constructor=THREE.DirectionalLightHelper,THREE.DirectionalLightHelper.prototype.dispose=function(){this.lightPlane.geometry.dispose(),this.lightPlane.material.dispose(),this.targetLine.geometry.dispose(),this.targetLine.material.dispose()},THREE.DirectionalLightHelper.prototype.update=function(){var v1=new THREE.Vector3,v2=new THREE.Vector3,v3=new THREE.Vector3;return function(){v1.setFromMatrixPosition(this.light.matrixWorld),v2.setFromMatrixPosition(this.light.target.matrixWorld),v3.subVectors(v2,v1),this.lightPlane.lookAt(v3),this.lightPlane.material.color.copy(this.light.color).multiplyScalar(this.light.intensity),this.targetLine.geometry.vertices[1].copy(v3),this.targetLine.geometry.verticesNeedUpdate=!0,this.targetLine.material.color.copy(this.lightPlane.material.color)}}(),THREE.EdgesHelper=function(object,hex,thresholdAngle){var color=void 0!==hex?hex:16777215;THREE.LineSegments.call(this,new THREE.EdgesGeometry(object.geometry,thresholdAngle),new THREE.LineBasicMaterial({color:color})),this.matrix=object.matrixWorld,this.matrixAutoUpdate=!1},THREE.EdgesHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.EdgesHelper.prototype.constructor=THREE.EdgesHelper,THREE.FaceNormalsHelper=function(object,size,hex,linewidth){this.object=object,this.size=void 0!==size?size:1;var color=void 0!==hex?hex:16776960,width=void 0!==linewidth?linewidth:1,nNormals=0,objGeometry=this.object.geometry;objGeometry instanceof THREE.Geometry?nNormals=objGeometry.faces.length:console.warn("THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead.");var geometry=new THREE.BufferGeometry,positions=new THREE.Float32Attribute(2*nNormals*3,3);geometry.addAttribute("position",positions),THREE.LineSegments.call(this,geometry,new THREE.LineBasicMaterial({color:color,linewidth:width})),this.matrixAutoUpdate=!1,this.update()},THREE.FaceNormalsHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.FaceNormalsHelper.prototype.constructor=THREE.FaceNormalsHelper,THREE.FaceNormalsHelper.prototype.update=function(){var v1=new THREE.Vector3,v2=new THREE.Vector3,normalMatrix=new THREE.Matrix3;return function(){this.object.updateMatrixWorld(!0),normalMatrix.getNormalMatrix(this.object.matrixWorld);for(var matrixWorld=this.object.matrixWorld,position=this.geometry.attributes.position,objGeometry=this.object.geometry,vertices=objGeometry.vertices,faces=objGeometry.faces,idx=0,i=0,l=faces.length;l>i;i++){var face=faces[i],normal=face.normal;v1.copy(vertices[face.a]).add(vertices[face.b]).add(vertices[face.c]).divideScalar(3).applyMatrix4(matrixWorld),v2.copy(normal).applyMatrix3(normalMatrix).normalize().multiplyScalar(this.size).add(v1),position.setXYZ(idx,v1.x,v1.y,v1.z),idx+=1,position.setXYZ(idx,v2.x,v2.y,v2.z),idx+=1}return position.needsUpdate=!0,this}}(),THREE.GridHelper=function(size,step){var geometry=new THREE.Geometry,material=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});this.color1=new THREE.Color(4473924),this.color2=new THREE.Color(8947848);for(var i=-size;size>=i;i+=step){geometry.vertices.push(new THREE.Vector3(-size,0,i),new THREE.Vector3(size,0,i),new THREE.Vector3(i,0,-size),new THREE.Vector3(i,0,size));var color=0===i?this.color1:this.color2;geometry.colors.push(color,color,color,color)}THREE.LineSegments.call(this,geometry,material)},THREE.GridHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.GridHelper.prototype.constructor=THREE.GridHelper,THREE.GridHelper.prototype.setColors=function(colorCenterLine,colorGrid){this.color1.set(colorCenterLine),this.color2.set(colorGrid),this.geometry.colorsNeedUpdate=!0},THREE.HemisphereLightHelper=function(light,sphereSize){THREE.Object3D.call(this),this.light=light,this.light.updateMatrixWorld(),this.matrix=light.matrixWorld,this.matrixAutoUpdate=!1,this.colors=[new THREE.Color,new THREE.Color];var geometry=new THREE.SphereGeometry(sphereSize,4,2);geometry.rotateX(-Math.PI/2);for(var i=0,il=8;il>i;i++)geometry.faces[i].color=this.colors[4>i?0:1];var material=new THREE.MeshBasicMaterial({vertexColors:THREE.FaceColors,wireframe:!0});this.lightSphere=new THREE.Mesh(geometry,material),this.add(this.lightSphere),this.update()},THREE.HemisphereLightHelper.prototype=Object.create(THREE.Object3D.prototype),THREE.HemisphereLightHelper.prototype.constructor=THREE.HemisphereLightHelper,THREE.HemisphereLightHelper.prototype.dispose=function(){this.lightSphere.geometry.dispose(),this.lightSphere.material.dispose()},THREE.HemisphereLightHelper.prototype.update=function(){var vector=new THREE.Vector3;return function(){this.colors[0].copy(this.light.color).multiplyScalar(this.light.intensity),this.colors[1].copy(this.light.groundColor).multiplyScalar(this.light.intensity),this.lightSphere.lookAt(vector.setFromMatrixPosition(this.light.matrixWorld).negate()),this.lightSphere.geometry.colorsNeedUpdate=!0}}(),THREE.PointLightHelper=function(light,sphereSize){this.light=light,this.light.updateMatrixWorld();var geometry=new THREE.SphereGeometry(sphereSize,4,2),material=new THREE.MeshBasicMaterial({wireframe:!0,fog:!1});material.color.copy(this.light.color).multiplyScalar(this.light.intensity),THREE.Mesh.call(this,geometry,material),this.matrix=this.light.matrixWorld,this.matrixAutoUpdate=!1},THREE.PointLightHelper.prototype=Object.create(THREE.Mesh.prototype),THREE.PointLightHelper.prototype.constructor=THREE.PointLightHelper,THREE.PointLightHelper.prototype.dispose=function(){this.geometry.dispose(),this.material.dispose()},THREE.PointLightHelper.prototype.update=function(){this.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)},THREE.SkeletonHelper=function(object){this.bones=this.getBoneList(object);for(var geometry=new THREE.Geometry,i=0;ii;i++)for(var face=faces[i],j=0,jl=face.vertexNormals.length;jl>j;j++){var vertex=vertices[face[keys[j]]],normal=face.vertexNormals[j];v1.copy(vertex).applyMatrix4(matrixWorld),v2.copy(normal).applyMatrix3(normalMatrix).normalize().multiplyScalar(this.size).add(v1),position.setXYZ(idx,v1.x,v1.y,v1.z),idx+=1,position.setXYZ(idx,v2.x,v2.y,v2.z),idx+=1}else if(objGeometry instanceof THREE.BufferGeometry)for(var objPos=objGeometry.attributes.position,objNorm=objGeometry.attributes.normal,idx=0,j=0,jl=objPos.count;jl>j;j++)v1.set(objPos.getX(j),objPos.getY(j),objPos.getZ(j)).applyMatrix4(matrixWorld),v2.set(objNorm.getX(j),objNorm.getY(j),objNorm.getZ(j)),v2.applyMatrix3(normalMatrix).normalize().multiplyScalar(this.size).add(v1),position.setXYZ(idx,v1.x,v1.y,v1.z),idx+=1,position.setXYZ(idx,v2.x,v2.y,v2.z),idx+=1;return position.needsUpdate=!0,this}}(),THREE.WireframeHelper=function(object,hex){var color=void 0!==hex?hex:16777215;THREE.LineSegments.call(this,new THREE.WireframeGeometry(object.geometry),new THREE.LineBasicMaterial({color:color})),this.matrix=object.matrixWorld,this.matrixAutoUpdate=!1},THREE.WireframeHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.WireframeHelper.prototype.constructor=THREE.WireframeHelper,THREE.ImmediateRenderObject=function(material){THREE.Object3D.call(this),this.material=material,this.render=function(renderCallback){}},THREE.ImmediateRenderObject.prototype=Object.create(THREE.Object3D.prototype),THREE.ImmediateRenderObject.prototype.constructor=THREE.ImmediateRenderObject,THREE.MorphBlendMesh=function(geometry,material){THREE.Mesh.call(this,geometry,material),this.animationsMap={},this.animationsList=[];var numFrames=this.geometry.morphTargets.length,name="__default",startFrame=0,endFrame=numFrames-1,fps=numFrames/1;this.createAnimation(name,startFrame,endFrame,fps),this.setAnimationWeight(name,1)},THREE.MorphBlendMesh.prototype=Object.create(THREE.Mesh.prototype),THREE.MorphBlendMesh.prototype.constructor=THREE.MorphBlendMesh,THREE.MorphBlendMesh.prototype.createAnimation=function(name,start,end,fps){var animation={start:start,end:end,length:end-start+1,fps:fps,duration:(end-start)/fps,lastFrame:0,currentFrame:0,active:!1,time:0,direction:1,weight:1,directionBackwards:!1,mirroredLoop:!1};this.animationsMap[name]=animation,this.animationsList.push(animation)},THREE.MorphBlendMesh.prototype.autoCreateAnimations=function(fps){for(var firstAnimation,pattern=/([a-z]+)_?(\d+)/,frameRanges={},geometry=this.geometry,i=0,il=geometry.morphTargets.length;il>i;i++){var morph=geometry.morphTargets[i],chunks=morph.name.match(pattern);if(chunks&&chunks.length>1){var name=chunks[1];frameRanges[name]||(frameRanges[name]={start:1/0,end:-(1/0)});var range=frameRanges[name];irange.end&&(range.end=i),firstAnimation||(firstAnimation=name)}}for(var name in frameRanges){var range=frameRanges[name];this.createAnimation(name,range.start,range.end,fps)}this.firstAnimation=firstAnimation},THREE.MorphBlendMesh.prototype.setAnimationDirectionForward=function(name){var animation=this.animationsMap[name];animation&&(animation.direction=1,animation.directionBackwards=!1)},THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward=function(name){var animation=this.animationsMap[name];animation&&(animation.direction=-1,animation.directionBackwards=!0)},THREE.MorphBlendMesh.prototype.setAnimationFPS=function(name,fps){var animation=this.animationsMap[name];animation&&(animation.fps=fps,animation.duration=(animation.end-animation.start)/animation.fps)},THREE.MorphBlendMesh.prototype.setAnimationDuration=function(name,duration){var animation=this.animationsMap[name];animation&&(animation.duration=duration,animation.fps=(animation.end-animation.start)/animation.duration)},THREE.MorphBlendMesh.prototype.setAnimationWeight=function(name,weight){var animation=this.animationsMap[name];animation&&(animation.weight=weight)},THREE.MorphBlendMesh.prototype.setAnimationTime=function(name,time){var animation=this.animationsMap[name];animation&&(animation.time=time)},THREE.MorphBlendMesh.prototype.getAnimationTime=function(name){var time=0,animation=this.animationsMap[name];return animation&&(time=animation.time),time},THREE.MorphBlendMesh.prototype.getAnimationDuration=function(name){var duration=-1,animation=this.animationsMap[name];return animation&&(duration=animation.duration),duration},THREE.MorphBlendMesh.prototype.playAnimation=function(name){var animation=this.animationsMap[name];animation?(animation.time=0,animation.active=!0):console.warn("THREE.MorphBlendMesh: animation["+name+"] undefined in .playAnimation()")},THREE.MorphBlendMesh.prototype.stopAnimation=function(name){var animation=this.animationsMap[name];animation&&(animation.active=!1)},THREE.MorphBlendMesh.prototype.update=function(delta){for(var i=0,il=this.animationsList.length;il>i;i++){var animation=this.animationsList[i];if(animation.active){var frameTime=animation.duration/animation.length;animation.time+=animation.direction*delta,animation.mirroredLoop?(animation.time>animation.duration||animation.time<0)&&(animation.direction*=-1,animation.time>animation.duration&&(animation.time=animation.duration,animation.directionBackwards=!0),animation.time<0&&(animation.time=0,animation.directionBackwards=!1)):(animation.time=animation.time%animation.duration,animation.time<0&&(animation.time+=animation.duration));var keyframe=animation.start+THREE.Math.clamp(Math.floor(animation.time/frameTime),0,animation.length-1),weight=animation.weight;keyframe!==animation.currentFrame&&(this.morphTargetInfluences[animation.lastFrame]=0,this.morphTargetInfluences[animation.currentFrame]=1*weight,this.morphTargetInfluences[keyframe]=0,animation.lastFrame=animation.currentFrame,animation.currentFrame=keyframe);var mix=animation.time%frameTime/frameTime;animation.directionBackwards&&(mix=1-mix),animation.currentFrame!==animation.lastFrame?(this.morphTargetInfluences[animation.currentFrame]=mix*weight,this.morphTargetInfluences[animation.lastFrame]=(1-mix)*weight):this.morphTargetInfluences[animation.currentFrame]=weight}}},"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=THREE),exports.THREE=THREE):this.THREE=THREE},{}],6:[function(_dereq_,module,exports){!function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof _dereq_&&_dereq_;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof _dereq_&&_dereq_,o=0;o 0.0 && abs(vUV.x - 0.5) < .001) {","} else if (a.x < 0.0 || a.x > 1.0 || a.y < 0.0 || a.y > 1.0) {","gl_FragColor = backgroundColor;","} else {","gl_FragColor = texture2D(texture, vec2(a.x * 0.5 + (vUV.x < 0.5 ? 0.0 : 0.5), a.y));","}","}"].join("\n")};module.exports=BarrelDistortionFragment},{}],6:[function(_dereq_,module,exports){function Distortion(coefficients){this.coefficients=coefficients}Distortion.prototype.distortInverse=function(radius){for(var r0=radius/.9,r1=.9*radius,dr0=radius-this.distort(r0);Math.abs(r1-r0)>1e-4;){var dr1=radius-this.distort(r1),r2=r1-dr1*((r1-r0)/(dr1-dr0));r0=r1,r1=r2,dr0=dr1}return r1},Distortion.prototype.distort=function(radius){return radius*this.distortionFactor_(radius)},Distortion.prototype.distortionFactor_=function(radius){for(var result=1,rFactor=1,rSquared=radius*radius,i=0;i=200&&xhr.status<=299?(console.log("Successfully loaded online DPDB."),obj.dpdb=JSON.parse(xhr.response),obj.recalculateDeviceParams_()):console.error("Error loading online DPDB!")}),xhr.send()}}function DeviceParams(params){this.xdpi=params.xdpi,this.ydpi=params.ydpi,this.bevelMm=params.bevelMm}var DPDB_CACHE=_dereq_("./dpdb-cache.js"),Util=_dereq_("./util.js"),ONLINE_DPDB_URL="https://storage.googleapis.com/cardboard-dpdb/dpdb.json";Dpdb.prototype.getDeviceParams=function(){return this.deviceParams},Dpdb.prototype.recalculateDeviceParams_=function(){console.log("Recalculating device params.");var newDeviceParams=this.calcDeviceParams_();console.log("New device parameters:"),console.log(newDeviceParams),newDeviceParams?(this.deviceParams=newDeviceParams,this.onDeviceParamsUpdated&&this.onDeviceParamsUpdated(this.deviceParams)):console.warn("Failed to recalculate device parameters.")},Dpdb.prototype.calcDeviceParams_=function(){var db=this.dpdb;if(!db)return console.error("DPDB not available."),null;if(1!=db.format)return console.error("DPDB has unexpected format version."),null;if(!db.devices||!db.devices.length)return console.error("DPDB does not have a devices section."),null;var userAgent=navigator.userAgent||navigator.vendor||window.opera,width=Util.getScreenWidth(),height=Util.getScreenHeight();if(console.log("User agent: "+userAgent),console.log("Pixel width: "+width),console.log("Pixel height: "+height),!db.devices)return console.error("DPDB has no devices section."),null;for(var i=0;i %s",this.mode,mode),this.mode=mode,this.button.setMode(mode,this.isVRCompatible),this.mode==Modes.VR&&Util.isLandscapeMode()&&Util.isMobile()?this.rotateInstructions.showTemporarily(3e3):this.updateRotateInstructions_(),this.viewerSelector.hide(),this.emit("modechange",mode,oldMode),this.isTouchPannerEnabled&&(this.mode==Modes.VR?WebVRConfig.TOUCH_PANNER_DISABLED=!0:WebVRConfig.TOUCH_PANNER_DISABLED=!1),void(this.mode==Modes.VR&&this.setHMDVRDeviceParams_(this.getViewer())))},WebVRManager.prototype.onFSClick_=function(){switch(this.mode){case Modes.NORMAL:if(Util.isIOS()&&Util.isIFrame()){var url=window.location.href;return url=Util.appendQueryParameter(url,"no_fullscreen","true"),url=Util.appendQueryParameter(url,"start_mode",Modes.MAGIC_WINDOW),void(top.location.href=url)}this.normalToMagicWindow_(),this.setMode_(Modes.MAGIC_WINDOW);break;case Modes.MAGIC_WINDOW:this.isFullscreenDisabled?window.history.back():(this.anyModeToNormal_(),this.setMode_(Modes.NORMAL))}},WebVRManager.prototype.onVRClick_=function(){if(this.mode==Modes.NORMAL&&Util.isIOS()&&Util.isIFrame()){var url=window.location.href;return url=Util.appendQueryParameter(url,"no_fullscreen","true"),url=Util.appendQueryParameter(url,"start_mode",Modes.VR),void(top.location.href=url)}this.anyModeToVR_(),this.setMode_(Modes.VR)},WebVRManager.prototype.onBackClick_=function(){this.isFullscreenDisabled?window.history.back():(this.anyModeToNormal_(),this.setMode_(Modes.NORMAL))},WebVRManager.prototype.onSettingsClick_=function(){this.viewerSelector.show()},WebVRManager.prototype.normalToMagicWindow_=function(){this.requestFullscreen_(),this.wakelock.request()},WebVRManager.prototype.anyModeToVR_=function(){this.requestFullscreen_(),this.wakelock.request(),this.distorter.patch()},WebVRManager.prototype.vrToMagicWindow_=function(){this.distorter.unpatch(),this.resize_()},WebVRManager.prototype.anyModeToNormal_=function(){this.exitFullscreen_(),this.releasePointerLock_(),this.wakelock.release(),this.distorter.unpatch(),this.resize_()},WebVRManager.prototype.resizeIfNeeded_=function(camera){var size=this.renderer.getSize();size.width==window.innerWidth&&size.height==window.innerHeight||this.resize_()},WebVRManager.prototype.resize_=function(){this.effect.setSize(window.innerWidth,window.innerHeight),this.camera&&(this.camera.aspect=window.innerWidth/window.innerHeight,this.camera.updateProjectionMatrix())},WebVRManager.prototype.onOrientationChange_=function(e){this.updateRotateInstructions_(),this.viewerSelector.hide()},WebVRManager.prototype.updateRotateInstructions_=function(){this.rotateInstructions.disableShowTemporarily(),this.mode==Modes.VR&&!Util.isLandscapeMode()&&Util.isMobile()?this.rotateInstructions.show():this.rotateInstructions.hide()},WebVRManager.prototype.onFullscreenChange_=function(e){null!==document.webkitFullscreenElement&&null!==document.mozFullScreenElement||(this.anyModeToNormal_(),this.setMode_(Modes.NORMAL))},WebVRManager.prototype.requestPointerLock_=function(){var canvas=this.renderer.domElement;canvas.requestPointerLock=canvas.requestPointerLock||canvas.mozRequestPointerLock||canvas.webkitRequestPointerLock,canvas.requestPointerLock&&canvas.requestPointerLock()},WebVRManager.prototype.releasePointerLock_=function(){document.exitPointerLock=document.exitPointerLock||document.mozExitPointerLock||document.webkitExitPointerLock,document.exitPointerLock&&document.exitPointerLock()},WebVRManager.prototype.requestOrientationLock_=function(){screen.orientation&&Util.isMobile()&&screen.orientation.lock("landscape")},WebVRManager.prototype.releaseOrientationLock_=function(){screen.orientation&&screen.orientation.unlock()},WebVRManager.prototype.requestFullscreen_=function(){var canvas=document.body;canvas.requestFullscreen?canvas.requestFullscreen():canvas.mozRequestFullScreen?canvas.mozRequestFullScreen({vrDisplay:this.hmd}):canvas.webkitRequestFullscreen?canvas.webkitRequestFullscreen({vrDisplay:this.hmd}):canvas.msRequestFullscreen&&canvas.msRequestFullscreen({vrDisplay:this.hmd})},WebVRManager.prototype.exitFullscreen_=function(){document.exitFullscreen?document.exitFullscreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen?document.webkitExitFullscreen():document.msExitFullscreen&&document.msExitFullscreen()},WebVRManager.prototype.onViewerChanged_=function(viewer){this.deviceInfo.setViewer(viewer),this.distorter.updateDeviceInfo(this.deviceInfo),this.setHMDVRDeviceParams_(viewer),this.emit("viewerchange",viewer)},WebVRManager.prototype.setHMDVRDeviceParams_=function(viewer){this.getDeviceByType_(HMDVRDevice).then(function(hmd){hmd&&(hmd.setFieldOfView&&hmd.setFieldOfView(this.deviceInfo.getFieldOfViewLeftEye(this.isUndistorted),this.deviceInfo.getFieldOfViewRightEye(this.isUndistorted)),hmd.setInterpupillaryDistance&&hmd.setInterpupillaryDistance(viewer.interLensDistance))}.bind(this))},WebVRManager.prototype.onDeviceParamsUpdated_=function(newParams){console.log("DPDB reported that device params were updated."),this.deviceInfo.updateDeviceParams(newParams),this.distorter.updateDeviceInfo(this.deviceInfo)},module.exports=WebVRManager},{"./button-manager.js":2,"./cardboard-distorter.js":3,"./device-info.js":4,"./dpdb.js":8,"./emitter.js":9,"./modes.js":11,"./rotate-instructions.js":12,"./util.js":13,"./viewer-selector.js":14,"./wakelock.js":15}]},{},[10])},{}],7:[function(_dereq_,module,exports){!function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof _dereq_&&_dereq_;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof _dereq_&&_dereq_,o=0;oUtil.MAX_TIMESTEP?(console.warn("Invalid timestamps detected. Time step between successive gyroscope sensor samples is very small or not monotonic"),void(this.previousTimestampS=timestampS)):(this.accelerometer.set(-accGravity.x,-accGravity.y,-accGravity.z), +this.gyroscope.set(rotRate.alpha,rotRate.beta,rotRate.gamma),(this.isIOS||this.isFirefoxAndroid)&&this.gyroscope.multiplyScalar(Math.PI/180),this.filter.addAccelMeasurement(this.accelerometer,timestampS),this.filter.addGyroMeasurement(this.gyroscope,timestampS),void(this.previousTimestampS=timestampS))},FusionPositionSensorVRDevice.prototype.onScreenOrientationChange_=function(screenOrientation){this.setScreenTransform_()},FusionPositionSensorVRDevice.prototype.setScreenTransform_=function(){switch(this.worldToScreenQ.set(0,0,0,1),window.orientation){case 0:break;case 90:this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0,0,1),-Math.PI/2);break;case-90:this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0,0,1),Math.PI/2);break;case 180:}},module.exports=FusionPositionSensorVRDevice},{"./base.js":1,"./complementary-filter.js":3,"./pose-predictor.js":7,"./three-math.js":9,"./touch-panner.js":10,"./util.js":11}],5:[function(_dereq_,module,exports){var WebVRPolyfill=_dereq_("./webvr-polyfill.js");window.WebVRConfig=window.WebVRConfig||{},new WebVRPolyfill},{"./webvr-polyfill.js":12}],6:[function(_dereq_,module,exports){function MouseKeyboardPositionSensorVRDevice(){this.deviceId="webvr-polyfill:mouse-keyboard",this.deviceName="VR Position Device (webvr-polyfill:mouse-keyboard)",window.addEventListener("keydown",this.onKeyDown_.bind(this)),window.addEventListener("mousemove",this.onMouseMove_.bind(this)),window.addEventListener("mousedown",this.onMouseDown_.bind(this)),window.addEventListener("mouseup",this.onMouseUp_.bind(this)),this.phi=0,this.theta=0,this.targetAngle=null,this.euler=new THREE.Euler,this.orientation=new THREE.Quaternion,this.rotateStart=new THREE.Vector2,this.rotateEnd=new THREE.Vector2,this.rotateDelta=new THREE.Vector2}var PositionSensorVRDevice=_dereq_("./base.js").PositionSensorVRDevice,THREE=_dereq_("./three-math.js"),Util=_dereq_("./util.js"),KEY_SPEED=.15,KEY_ANIMATION_DURATION=80,MOUSE_SPEED_X=.5,MOUSE_SPEED_Y=.3;MouseKeyboardPositionSensorVRDevice.prototype=new PositionSensorVRDevice,MouseKeyboardPositionSensorVRDevice.prototype.getState=function(){return this.euler.set(this.phi,this.theta,0,"YXZ"),this.orientation.setFromEuler(this.euler),{hasOrientation:!0,orientation:this.orientation,hasPosition:!1,position:null}},MouseKeyboardPositionSensorVRDevice.prototype.onKeyDown_=function(e){38==e.keyCode?this.animatePhi_(this.phi+KEY_SPEED):39==e.keyCode?this.animateTheta_(this.theta-KEY_SPEED):40==e.keyCode?this.animatePhi_(this.phi-KEY_SPEED):37==e.keyCode&&this.animateTheta_(this.theta+KEY_SPEED)},MouseKeyboardPositionSensorVRDevice.prototype.animateTheta_=function(targetAngle){this.animateKeyTransitions_("theta",targetAngle)},MouseKeyboardPositionSensorVRDevice.prototype.animatePhi_=function(targetAngle){targetAngle=Util.clamp(targetAngle,-Math.PI/2,Math.PI/2),this.animateKeyTransitions_("phi",targetAngle)},MouseKeyboardPositionSensorVRDevice.prototype.animateKeyTransitions_=function(angleName,targetAngle){this.angleAnimation&&clearInterval(this.angleAnimation);var startAngle=this[angleName],startTime=new Date;this.angleAnimation=setInterval(function(){var elapsed=new Date-startTime;if(elapsed>=KEY_ANIMATION_DURATION)return this[angleName]=targetAngle,void clearInterval(this.angleAnimation);var percent=elapsed/KEY_ANIMATION_DURATION;this[angleName]=startAngle+(targetAngle-startAngle)*percent}.bind(this),1e3/60)},MouseKeyboardPositionSensorVRDevice.prototype.onMouseDown_=function(e){this.rotateStart.set(e.clientX,e.clientY),this.isDragging=!0},MouseKeyboardPositionSensorVRDevice.prototype.onMouseMove_=function(e){if(this.isDragging||this.isPointerLocked_()){if(this.isPointerLocked_()){var movementX=e.movementX||e.mozMovementX||0,movementY=e.movementY||e.mozMovementY||0;this.rotateEnd.set(this.rotateStart.x-movementX,this.rotateStart.y-movementY)}else this.rotateEnd.set(e.clientX,e.clientY);this.rotateDelta.subVectors(this.rotateEnd,this.rotateStart),this.rotateStart.copy(this.rotateEnd);var element=document.body;this.phi+=2*Math.PI*this.rotateDelta.y/element.clientHeight*MOUSE_SPEED_Y,this.theta+=2*Math.PI*this.rotateDelta.x/element.clientWidth*MOUSE_SPEED_X,this.phi=Util.clamp(this.phi,-Math.PI/2,Math.PI/2)}},MouseKeyboardPositionSensorVRDevice.prototype.onMouseUp_=function(e){this.isDragging=!1},MouseKeyboardPositionSensorVRDevice.prototype.isPointerLocked_=function(){var el=document.pointerLockElement||document.mozPointerLockElement||document.webkitPointerLockElement;return void 0!==el},MouseKeyboardPositionSensorVRDevice.prototype.resetSensor=function(){console.error("Not implemented yet.")},module.exports=MouseKeyboardPositionSensorVRDevice},{"./base.js":1,"./three-math.js":9,"./util.js":11}],7:[function(_dereq_,module,exports){function PosePredictor(predictionTimeS){this.predictionTimeS=predictionTimeS,this.previousQ=new THREE.Quaternion,this.previousTimestampS=null,this.deltaQ=new THREE.Quaternion,this.outQ=new THREE.Quaternion}var THREE=_dereq_("./three-math.js"),DEBUG=!1;PosePredictor.prototype.getPrediction=function(currentQ,gyro,timestampS){if(!this.previousTimestampS)return this.previousQ.copy(currentQ),this.previousTimestampS=timestampS,currentQ;var axis=new THREE.Vector3;axis.copy(gyro),axis.normalize();var angularSpeed=gyro.length();if(angularSpeed0?(s=.5/Math.sqrt(trace+1),this._w=.25/s,this._x=(m32-m23)*s,this._y=(m13-m31)*s,this._z=(m21-m12)*s):m11>m22&&m11>m33?(s=2*Math.sqrt(1+m11-m22-m33),this._w=(m32-m23)/s,this._x=.25*s,this._y=(m12+m21)/s,this._z=(m13+m31)/s):m22>m33?(s=2*Math.sqrt(1+m22-m11-m33),this._w=(m13-m31)/s,this._x=(m12+m21)/s,this._y=.25*s,this._z=(m23+m32)/s):(s=2*Math.sqrt(1+m33-m11-m22),this._w=(m21-m12)/s,this._x=(m13+m31)/s,this._y=(m23+m32)/s,this._z=.25*s),this.onChangeCallback(),this},setFromUnitVectors:function(){var v1,r,EPS=1e-6;return function(vFrom,vTo){return void 0===v1&&(v1=new THREE.Vector3),r=vFrom.dot(vTo)+1,EPS>r?(r=0,Math.abs(vFrom.x)>Math.abs(vFrom.z)?v1.set(-vFrom.y,vFrom.x,0):v1.set(0,-vFrom.z,vFrom.y)):v1.crossVectors(vFrom,vTo),this._x=v1.x,this._y=v1.y,this._z=v1.z,this._w=r,this.normalize(),this}}(),inverse:function(){return this.conjugate().normalize(),this},conjugate:function(){return this._x*=-1,this._y*=-1,this._z*=-1,this.onChangeCallback(),this},dot:function(v){return this._x*v._x+this._y*v._y+this._z*v._z+this._w*v._w},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var l=this.length();return 0===l?(this._x=0,this._y=0,this._z=0,this._w=1):(l=1/l,this._x=this._x*l,this._y=this._y*l,this._z=this._z*l,this._w=this._w*l),this.onChangeCallback(),this},multiply:function(q,p){return void 0!==p?(console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(q,p)):this.multiplyQuaternions(this,q)},multiplyQuaternions:function(a,b){var qax=a._x,qay=a._y,qaz=a._z,qaw=a._w,qbx=b._x,qby=b._y,qbz=b._z,qbw=b._w;return this._x=qax*qbw+qaw*qbx+qay*qbz-qaz*qby,this._y=qay*qbw+qaw*qby+qaz*qbx-qax*qbz,this._z=qaz*qbw+qaw*qbz+qax*qby-qay*qbx,this._w=qaw*qbw-qax*qbx-qay*qby-qaz*qbz,this.onChangeCallback(),this},multiplyVector3:function(vector){return console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead."),vector.applyQuaternion(this)},slerp:function(qb,t){if(0===t)return this;if(1===t)return this.copy(qb);var x=this._x,y=this._y,z=this._z,w=this._w,cosHalfTheta=w*qb._w+x*qb._x+y*qb._y+z*qb._z;if(0>cosHalfTheta?(this._w=-qb._w,this._x=-qb._x,this._y=-qb._y,this._z=-qb._z,cosHalfTheta=-cosHalfTheta):this.copy(qb),cosHalfTheta>=1)return this._w=w,this._x=x,this._y=y,this._z=z,this;var halfTheta=Math.acos(cosHalfTheta),sinHalfTheta=Math.sqrt(1-cosHalfTheta*cosHalfTheta);if(Math.abs(sinHalfTheta)<.001)return this._w=.5*(w+this._w),this._x=.5*(x+this._x),this._y=.5*(y+this._y),this._z=.5*(z+this._z),this;var ratioA=Math.sin((1-t)*halfTheta)/sinHalfTheta,ratioB=Math.sin(t*halfTheta)/sinHalfTheta;return this._w=w*ratioA+this._w*ratioB,this._x=x*ratioA+this._x*ratioB,this._y=y*ratioA+this._y*ratioB,this._z=z*ratioA+this._z*ratioB,this.onChangeCallback(),this},equals:function(quaternion){return quaternion._x===this._x&&quaternion._y===this._y&&quaternion._z===this._z&&quaternion._w===this._w},fromArray:function(array,offset){return void 0===offset&&(offset=0),this._x=array[offset],this._y=array[offset+1],this._z=array[offset+2],this._w=array[offset+3],this.onChangeCallback(),this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this._x,array[offset+1]=this._y,array[offset+2]=this._z,array[offset+3]=this._w,array},onChange:function(callback){return this.onChangeCallback=callback,this},onChangeCallback:function(){},clone:function(){return new THREE.Quaternion(this._x,this._y,this._z,this._w)}},THREE.Quaternion.slerp=function(qa,qb,qm,t){return qm.copy(qa).slerp(qb,t)},THREE.Vector2=function(x,y){this.x=x||0,this.y=y||0},THREE.Vector2.prototype={constructor:THREE.Vector2,set:function(x,y){return this.x=x,this.y=y,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+index)}},copy:function(v){return this.x=v.x,this.y=v.y,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this)},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this},addScalar:function(s){return this.x+=s,this.y+=s,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this)},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this},multiply:function(v){return this.x*=v.x,this.y*=v.y,this},multiplyScalar:function(s){return this.x*=s,this.y*=s,this},divide:function(v){return this.x/=v.x,this.y/=v.y,this},divideScalar:function(scalar){if(0!==scalar){var invScalar=1/scalar;this.x*=invScalar,this.y*=invScalar}else this.x=0,this.y=0;return this},min:function(v){return this.x>v.x&&(this.x=v.x),this.y>v.y&&(this.y=v.y),this},max:function(v){return this.xmax.x&&(this.x=max.x),this.ymax.y&&(this.y=max.y),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector2,max=new THREE.Vector2),min.set(minVal,minVal),max.set(maxVal,maxVal),this.clamp(min,max)}}(),floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this},negate:function(){return this.x=-this.x,this.y=-this.y,this},dot:function(v){return this.x*v.x+this.y*v.y},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},normalize:function(){return this.divideScalar(this.length())},distanceTo:function(v){return Math.sqrt(this.distanceToSquared(v))},distanceToSquared:function(v){var dx=this.x-v.x,dy=this.y-v.y;return dx*dx+dy*dy},setLength:function(l){var oldLength=this.length();return 0!==oldLength&&l!==oldLength&&this.multiplyScalar(l/oldLength),this},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this},equals:function(v){return v.x===this.x&&v.y===this.y},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this},clone:function(){return new THREE.Vector2(this.x,this.y)}},THREE.Vector3=function(x,y,z){this.x=x||0,this.y=y||0,this.z=z||0},THREE.Vector3.prototype={constructor:THREE.Vector3,set:function(x,y,z){return this.x=x,this.y=y,this.z=z,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setZ:function(z){return this.z=z,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;case 2:this.z=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("index is out of range: "+index)}},copy:function(v){return this.x=v.x,this.y=v.y,this.z=v.z,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this.z+=v.z,this)},addScalar:function(s){return this.x+=s,this.y+=s,this.z+=s,this},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this.z=a.z+b.z,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this.z-=v.z,this)},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this.z=a.z-b.z,this},multiply:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(v,w)):(this.x*=v.x,this.y*=v.y,this.z*=v.z,this)},multiplyScalar:function(scalar){return this.x*=scalar,this.y*=scalar,this.z*=scalar,this},multiplyVectors:function(a,b){return this.x=a.x*b.x,this.y=a.y*b.y,this.z=a.z*b.z,this},applyEuler:function(){var quaternion;return function(euler){return euler instanceof THREE.Euler==!1&&console.error("THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order."),void 0===quaternion&&(quaternion=new THREE.Quaternion),this.applyQuaternion(quaternion.setFromEuler(euler)),this}}(),applyAxisAngle:function(){var quaternion;return function(axis,angle){return void 0===quaternion&&(quaternion=new THREE.Quaternion),this.applyQuaternion(quaternion.setFromAxisAngle(axis,angle)),this}}(),applyMatrix3:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[3]*y+e[6]*z,this.y=e[1]*x+e[4]*y+e[7]*z,this.z=e[2]*x+e[5]*y+e[8]*z,this},applyMatrix4:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z+e[12],this.y=e[1]*x+e[5]*y+e[9]*z+e[13],this.z=e[2]*x+e[6]*y+e[10]*z+e[14],this},applyProjection:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements,d=1/(e[3]*x+e[7]*y+e[11]*z+e[15]);return this.x=(e[0]*x+e[4]*y+e[8]*z+e[12])*d,this.y=(e[1]*x+e[5]*y+e[9]*z+e[13])*d,this.z=(e[2]*x+e[6]*y+e[10]*z+e[14])*d,this},applyQuaternion:function(q){var x=this.x,y=this.y,z=this.z,qx=q.x,qy=q.y,qz=q.z,qw=q.w,ix=qw*x+qy*z-qz*y,iy=qw*y+qz*x-qx*z,iz=qw*z+qx*y-qy*x,iw=-qx*x-qy*y-qz*z;return this.x=ix*qw+iw*-qx+iy*-qz-iz*-qy,this.y=iy*qw+iw*-qy+iz*-qx-ix*-qz,this.z=iz*qw+iw*-qz+ix*-qy-iy*-qx,this},project:function(){var matrix;return function(camera){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.multiplyMatrices(camera.projectionMatrix,matrix.getInverse(camera.matrixWorld)),this.applyProjection(matrix)}}(),unproject:function(){var matrix;return function(camera){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.multiplyMatrices(camera.matrixWorld,matrix.getInverse(camera.projectionMatrix)),this.applyProjection(matrix)}}(),transformDirection:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z,this.y=e[1]*x+e[5]*y+e[9]*z,this.z=e[2]*x+e[6]*y+e[10]*z,this.normalize(),this},divide:function(v){return this.x/=v.x,this.y/=v.y,this.z/=v.z,this},divideScalar:function(scalar){if(0!==scalar){var invScalar=1/scalar;this.x*=invScalar,this.y*=invScalar,this.z*=invScalar}else this.x=0,this.y=0,this.z=0;return this},min:function(v){return this.x>v.x&&(this.x=v.x),this.y>v.y&&(this.y=v.y),this.z>v.z&&(this.z=v.z),this},max:function(v){return this.xmax.x&&(this.x=max.x),this.ymax.y&&(this.y=max.y),this.zmax.z&&(this.z=max.z),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector3,max=new THREE.Vector3),min.set(minVal,minVal,minVal),max.set(maxVal,maxVal,maxVal),this.clamp(min,max)}}(),floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this},negate:function(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this},dot:function(v){return this.x*v.x+this.y*v.y+this.z*v.z},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length())},setLength:function(l){var oldLength=this.length();return 0!==oldLength&&l!==oldLength&&this.multiplyScalar(l/oldLength),this},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this.z+=(v.z-this.z)*alpha,this},cross:function(v,w){if(void 0!==w)return console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(v,w);var x=this.x,y=this.y,z=this.z;return this.x=y*v.z-z*v.y,this.y=z*v.x-x*v.z,this.z=x*v.y-y*v.x,this},crossVectors:function(a,b){var ax=a.x,ay=a.y,az=a.z,bx=b.x,by=b.y,bz=b.z;return this.x=ay*bz-az*by,this.y=az*bx-ax*bz,this.z=ax*by-ay*bx,this},projectOnVector:function(){var v1,dot;return function(vector){return void 0===v1&&(v1=new THREE.Vector3),v1.copy(vector).normalize(),dot=this.dot(v1),this.copy(v1).multiplyScalar(dot)}}(),projectOnPlane:function(){var v1;return function(planeNormal){return void 0===v1&&(v1=new THREE.Vector3),v1.copy(this).projectOnVector(planeNormal),this.sub(v1)}}(),reflect:function(){var v1;return function(normal){return void 0===v1&&(v1=new THREE.Vector3),this.sub(v1.copy(normal).multiplyScalar(2*this.dot(normal)))}}(),angleTo:function(v){var theta=this.dot(v)/(this.length()*v.length());return Math.acos(THREE.Math.clamp(theta,-1,1))},distanceTo:function(v){return Math.sqrt(this.distanceToSquared(v))},distanceToSquared:function(v){var dx=this.x-v.x,dy=this.y-v.y,dz=this.z-v.z;return dx*dx+dy*dy+dz*dz},setEulerFromRotationMatrix:function(m,order){console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.")},setEulerFromQuaternion:function(q,order){console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.")},getPositionFromMatrix:function(m){return console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()."),this.setFromMatrixPosition(m)},getScaleFromMatrix:function(m){return console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()."),this.setFromMatrixScale(m)},getColumnFromMatrix:function(index,matrix){return console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()."),this.setFromMatrixColumn(index,matrix)},setFromMatrixPosition:function(m){return this.x=m.elements[12],this.y=m.elements[13],this.z=m.elements[14],this},setFromMatrixScale:function(m){var sx=this.set(m.elements[0],m.elements[1],m.elements[2]).length(),sy=this.set(m.elements[4],m.elements[5],m.elements[6]).length(),sz=this.set(m.elements[8],m.elements[9],m.elements[10]).length();return this.x=sx,this.y=sy,this.z=sz,this},setFromMatrixColumn:function(index,matrix){var offset=4*index,me=matrix.elements;return this.x=me[offset],this.y=me[offset+1],this.z=me[offset+2],this},equals:function(v){return v.x===this.x&&v.y===this.y&&v.z===this.z},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this.z=array[offset+2],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array[offset+2]=this.z,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this.z=attribute.array[index+2],this},clone:function(){return new THREE.Vector3(this.x,this.y,this.z)}},THREE.Euler=function(x,y,z,order){this._x=x||0,this._y=y||0,this._z=z||0,this._order=order||THREE.Euler.DefaultOrder},THREE.Euler.RotationOrders=["XYZ","YZX","ZXY","XZY","YXZ","ZYX"],THREE.Euler.DefaultOrder="XYZ",THREE.Euler.prototype={constructor:THREE.Euler,_x:0,_y:0,_z:0,_order:THREE.Euler.DefaultOrder,get x(){return this._x},set x(value){this._x=value,this.onChangeCallback()},get y(){return this._y},set y(value){this._y=value,this.onChangeCallback()},get z(){return this._z},set z(value){this._z=value,this.onChangeCallback()},get order(){return this._order},set order(value){this._order=value,this.onChangeCallback()},set:function(x,y,z,order){return this._x=x,this._y=y,this._z=z,this._order=order||this._order,this.onChangeCallback(),this},copy:function(euler){return this._x=euler._x,this._y=euler._y,this._z=euler._z,this._order=euler._order,this.onChangeCallback(),this},setFromRotationMatrix:function(m,order,update){var clamp=THREE.Math.clamp,te=m.elements,m11=te[0],m12=te[4],m13=te[8],m21=te[1],m22=te[5],m23=te[9],m31=te[2],m32=te[6],m33=te[10];return order=order||this._order,"XYZ"===order?(this._y=Math.asin(clamp(m13,-1,1)),Math.abs(m13)<.99999?(this._x=Math.atan2(-m23,m33),this._z=Math.atan2(-m12,m11)):(this._x=Math.atan2(m32,m22),this._z=0)):"YXZ"===order?(this._x=Math.asin(-clamp(m23,-1,1)),Math.abs(m23)<.99999?(this._y=Math.atan2(m13,m33),this._z=Math.atan2(m21,m22)):(this._y=Math.atan2(-m31,m11),this._z=0)):"ZXY"===order?(this._x=Math.asin(clamp(m32,-1,1)),Math.abs(m32)<.99999?(this._y=Math.atan2(-m31,m33),this._z=Math.atan2(-m12,m22)):(this._y=0,this._z=Math.atan2(m21,m11))):"ZYX"===order?(this._y=Math.asin(-clamp(m31,-1,1)),Math.abs(m31)<.99999?(this._x=Math.atan2(m32,m33),this._z=Math.atan2(m21,m11)):(this._x=0,this._z=Math.atan2(-m12,m22))):"YZX"===order?(this._z=Math.asin(clamp(m21,-1,1)),Math.abs(m21)<.99999?(this._x=Math.atan2(-m23,m22),this._y=Math.atan2(-m31,m11)):(this._x=0,this._y=Math.atan2(m13,m33))):"XZY"===order?(this._z=Math.asin(-clamp(m12,-1,1)),Math.abs(m12)<.99999?(this._x=Math.atan2(m32,m22),this._y=Math.atan2(m13,m11)):(this._x=Math.atan2(-m23,m33),this._y=0)):console.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+order),this._order=order,update!==!1&&this.onChangeCallback(),this},setFromQuaternion:function(){var matrix;return function(q,order,update){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.makeRotationFromQuaternion(q),this.setFromRotationMatrix(matrix,order,update),this}}(),setFromVector3:function(v,order){return this.set(v.x,v.y,v.z,order||this._order)},reorder:function(){var q=new THREE.Quaternion;return function(newOrder){q.setFromEuler(this),this.setFromQuaternion(q,newOrder)}}(),equals:function(euler){return euler._x===this._x&&euler._y===this._y&&euler._z===this._z&&euler._order===this._order},fromArray:function(array){return this._x=array[0],this._y=array[1],this._z=array[2],void 0!==array[3]&&(this._order=array[3]),this.onChangeCallback(),this},toArray:function(){return[this._x,this._y,this._z,this._order]},toVector3:function(optionalResult){return optionalResult?optionalResult.set(this._x,this._y,this._z):new THREE.Vector3(this._x,this._y,this._z)},onChange:function(callback){return this.onChangeCallback=callback,this},onChangeCallback:function(){},clone:function(){return new THREE.Euler(this._x,this._y,this._z,this._order)}},THREE.Math={generateUUID:function(){var r,chars="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),uuid=new Array(36),rnd=0;return function(){for(var i=0;36>i;i++)8==i||13==i||18==i||23==i?uuid[i]="-":14==i?uuid[i]="4":(2>=rnd&&(rnd=33554432+16777216*Math.random()|0),r=15&rnd,rnd>>=4,uuid[i]=chars[19==i?3&r|8:r]);return uuid.join("")}}(),clamp:function(x,a,b){return a>x?a:x>b?b:x},clampBottom:function(x,a){return a>x?a:x},mapLinear:function(x,a1,a2,b1,b2){return b1+(x-a1)*(b2-b1)/(a2-a1)},smoothstep:function(x,min,max){return min>=x?0:x>=max?1:(x=(x-min)/(max-min),x*x*(3-2*x))},smootherstep:function(x,min,max){return min>=x?0:x>=max?1:(x=(x-min)/(max-min),x*x*x*(x*(6*x-15)+10))},random16:function(){return(65280*Math.random()+255*Math.random())/65535},randInt:function(low,high){return Math.floor(this.randFloat(low,high))},randFloat:function(low,high){return low+Math.random()*(high-low)},randFloatSpread:function(range){return range*(.5-Math.random())},degToRad:function(){var degreeToRadiansFactor=Math.PI/180;return function(degrees){return degrees*degreeToRadiansFactor}}(),radToDeg:function(){var radianToDegreesFactor=180/Math.PI;return function(radians){return radians*radianToDegreesFactor}}(),isPowerOfTwo:function(value){return 0===(value&value-1)&&0!==value},nextPowerOfTwo:function(value){return value--,value|=value>>1,value|=value>>2,value|=value>>4,value|=value>>8,value|=value>>16,value++,value}}),module.exports=THREE},{}],10:[function(_dereq_,module,exports){function TouchPanner(){window.addEventListener("touchstart",this.onTouchStart_.bind(this)),window.addEventListener("touchmove",this.onTouchMove_.bind(this)),window.addEventListener("touchend",this.onTouchEnd_.bind(this)),this.isTouching=!1,this.rotateStart=new THREE.Vector2,this.rotateEnd=new THREE.Vector2,this.rotateDelta=new THREE.Vector2,this.theta=0,this.orientation=new THREE.Quaternion}var THREE=_dereq_("./three-math.js"),Util=_dereq_("./util.js"),ROTATE_SPEED=.5;TouchPanner.prototype.getOrientation=function(){return this.orientation.setFromEuler(new THREE.Euler(0,0,this.theta)),this.orientation},TouchPanner.prototype.resetSensor=function(){this.theta=0},TouchPanner.prototype.onTouchStart_=function(e){1==e.touches.length&&(this.rotateStart.set(e.touches[0].pageX,e.touches[0].pageY),this.isTouching=!0)},TouchPanner.prototype.onTouchMove_=function(e){if(this.isTouching){this.rotateEnd.set(e.touches[0].pageX,e.touches[0].pageY),this.rotateDelta.subVectors(this.rotateEnd,this.rotateStart),this.rotateStart.copy(this.rotateEnd),Util.isIOS()&&(this.rotateDelta.x*=-1);var element=document.body;this.theta+=2*Math.PI*this.rotateDelta.x/element.clientWidth*ROTATE_SPEED}},TouchPanner.prototype.onTouchEnd_=function(e){this.isTouching=!1},module.exports=TouchPanner},{"./three-math.js":9,"./util.js":11}],11:[function(_dereq_,module,exports){var Util=window.Util||{};Util.MIN_TIMESTEP=.001,Util.MAX_TIMESTEP=1,Util.clamp=function(value,min,max){return Math.min(Math.max(min,value),max)},Util.isIOS=function(){return/iPad|iPhone|iPod/.test(navigator.platform)},Util.isFirefoxAndroid=function(){return-1!==navigator.userAgent.indexOf("Firefox")&&-1!==navigator.userAgent.indexOf("Android")},Util.isTimestampDeltaValid=function(timestampDeltaS){return isNaN(timestampDeltaS)?!1:timestampDeltaS<=Util.MIN_TIMESTEP?!1:!(timestampDeltaS>Util.MAX_TIMESTEP)},module.exports=Util},{}],12:[function(_dereq_,module,exports){function WebVRPolyfill(){this.devices=[],this.isWebVRAvailable()||this.enablePolyfill()}var CardboardHMDVRDevice=_dereq_("./cardboard-hmd-vr-device.js"),FusionPositionSensorVRDevice=_dereq_("./fusion-position-sensor-vr-device.js"),MouseKeyboardPositionSensorVRDevice=_dereq_("./mouse-keyboard-position-sensor-vr-device.js"),HMDVRDevice=_dereq_("./base.js").HMDVRDevice,PositionSensorVRDevice=_dereq_("./base.js").PositionSensorVRDevice; +WebVRPolyfill.prototype.isWebVRAvailable=function(){return"getVRDevices"in navigator||"mozGetVRDevices"in navigator},WebVRPolyfill.prototype.enablePolyfill=function(){this.isCardboardCompatible()&&this.devices.push(new CardboardHMDVRDevice),this.isMobile()?this.devices.push(new FusionPositionSensorVRDevice):WebVRConfig.MOUSE_KEYBOARD_CONTROLS_DISABLED||this.devices.push(new MouseKeyboardPositionSensorVRDevice),navigator.getVRDevices=this.getVRDevices.bind(this),window.HMDVRDevice=HMDVRDevice,window.PositionSensorVRDevice=PositionSensorVRDevice},WebVRPolyfill.prototype.getVRDevices=function(){var devices=this.devices;return new Promise(function(resolve,reject){try{resolve(devices)}catch(e){reject(e)}})},WebVRPolyfill.prototype.isMobile=function(){return/Android/i.test(navigator.userAgent)||/iPhone|iPad|iPod/i.test(navigator.userAgent)},WebVRPolyfill.prototype.isCardboardCompatible=function(){return this.isMobile()||WebVRConfig.FORCE_ENABLE_VR},module.exports=WebVRPolyfill},{"./base.js":1,"./cardboard-hmd-vr-device.js":2,"./fusion-position-sensor-vr-device.js":4,"./mouse-keyboard-position-sensor-vr-device.js":6}]},{},[5])},{}],8:[function(_dereq_,module,exports){function Analytics(){!function(i,s,o,g,r,a,m){i.GoogleAnalyticsObject=r,i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date,a=s.createElement(o),m=s.getElementsByTagName(o)[0],a.async=1,a.src=g,m.parentNode.insertBefore(a,m)}(window,document,"script","//www.google-analytics.com/analytics.js","ga"),ga("create","UA-35315454-8","auto"),ga("send","pageview"),this.lastModeChangeTime=window.performance.now(),this.lastModeLabel=WebVRManager.Modes.UNKNOWN}var MODE_LABELS={0:"UNKNOWN",1:"NORMAL",2:"MAGIC_WINDOW",3:"VR"};Analytics.prototype.logModeChanged=function(mode){var modeLabel=MODE_LABELS[mode],lastModeLabel=MODE_LABELS[this.lastMode];console.log("Analytics: going from mode %s to %s",lastModeLabel,modeLabel),ga("send","screenview",{appName:"EmbedVR",screenName:modeLabel});var now=window.performance.now(),msSinceLastModeChange=Math.round(now-this.lastModeChangeTime);ga("send","timing","Time spent in mode",lastModeLabel,msSinceLastModeChange),this.lastModeChangeTime=now,this.lastMode=mode},window.analytics=new Analytics},{}],9:[function(_dereq_,module,exports){function DeviceMotionReceiver(){window.addEventListener("message",this.onMessage_.bind(this),!1)}DeviceMotionReceiver.prototype.onMessage_=function(event){var message=event.data;return"DeviceMotion"!==message.type?void console.warn("Got unknown message of type %s from %s",message.type,message.origin):(console.log("onMessage_",event),void this.synthesizeDeviceMotionEvent_(message.deviceMotionEvent))},DeviceMotionReceiver.prototype.synthesizeDeviceMotionEvent_=function(eventData){var type="devicemotion-iframe",canBubble=!1,cancelable=!1,dme=document.createEvent("DeviceMotionEvent");dme.initDeviceMotionEvent(type,canBubble,cancelable,eventData.acceleration,eventData.accelerationIncludingGravity,eventData.rotationRate,eventData.interval),window.dispatchEvent(dme)},module.exports=DeviceMotionReceiver},{}],10:[function(_dereq_,module,exports){function Emitter(){this.initEmitter()}Emitter.prototype.initEmitter=function(){this.callbacks={}},Emitter.prototype.emit=function(eventName){var callbacks=this.callbacks[eventName];if(!callbacks)return void console.log("No valid callback specified.");var args=[].slice.call(arguments);args.shift();for(var i=0;ij;j++)uvs[i][j].x*=p.scaleX,uvs[i][j].x+=p.offsetX,uvs[i][j].y*=p.scaleY,uvs[i][j].y+=p.offsetY;var material=new THREE.MeshBasicMaterial({map:texture});this.distorter.setMap(texture);var out=new THREE.Mesh(geometry,material);return out.renderOrder=-1,out},PhotosphereRenderer.prototype.createScene_=function(opt_params){var scene=new THREE.Scene;scene.add(new THREE.PointLight(16777215));var photoGroup=new THREE.Object3D;return photoGroup.name="photo",scene.add(photoGroup),scene},PhotosphereRenderer.prototype.updateMaterial_=function(material_FOO){for(var i=0;i1)for(var i=1;it;t+=2){var e=X[t],n=X[t+1];e(n),X[t]=void 0,X[t+1]=void 0}G=0}function f(){try{var t=_dereq_,e=t("vertx");return U=e.runOnLoop||e.runOnContext,i()}catch(n){return c()}}function l(t,e){var n=this,r=n._state;if(r===et&&!t||r===nt&&!e)return this;var o=new this.constructor(p),i=n._result;if(r){var s=arguments[r-1];H(function(){C(r,o,s,i)})}else j(n,o,t,e);return o}function h(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var n=new e(p);return g(n,t),n}function p(){}function _(){return new TypeError("You cannot resolve a promise with itself")}function v(){return new TypeError("A promises callback cannot return that same promise.")}function d(t){try{return t.then}catch(e){return rt.error=e,rt}}function y(t,e,n,r){try{t.call(e,n,r)}catch(o){return o}}function m(t,e,n){H(function(t){var r=!1,o=y(n,e,function(n){r||(r=!0,e!==n?g(t,n):E(t,n))},function(e){r||(r=!0,S(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&o&&(r=!0,S(t,o))},t)}function w(t,e){e._state===et?E(t,e._result):e._state===nt?S(t,e._result):j(e,void 0,function(e){g(t,e)},function(e){S(t,e)})}function b(t,n,r){n.constructor===t.constructor&&r===Z&&constructor.resolve===$?w(t,n):r===rt?S(t,rt.error):void 0===r?E(t,n):e(r)?m(t,n,r):E(t,n)}function g(e,n){e===n?S(e,_()):t(n)?b(e,n,d(n)):E(e,n)}function A(t){t._onerror&&t._onerror(t._result),T(t)}function E(t,e){t._state===tt&&(t._result=e,t._state=et,0!==t._subscribers.length&&H(T,t))}function S(t,e){t._state===tt&&(t._state=nt,t._result=e,H(A,t))}function j(t,e,n,r){var o=t._subscribers,i=o.length;t._onerror=null,o[i]=e,o[i+et]=n,o[i+nt]=r,0===i&&t._state&&H(T,t)}function T(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r,o,i=t._result,s=0;ss;s++)j(r.resolve(t[s]),void 0,e,n);return o}function Y(t){var e=this,n=new e(p);return S(n,t),n}function q(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function F(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function D(t){this._id=ct++,this._state=void 0,this._result=void 0,this._subscribers=[],p!==t&&("function"!=typeof t&&q(),this instanceof D?M(this,t):F())}function K(t,e){this._instanceConstructor=t,this.promise=new t(p),Array.isArray(e)?(this._input=e,this.length=e.length,this._remaining=e.length,this._result=new Array(this.length),0===this.length?E(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&E(this.promise,this._result))):S(this.promise,this._validationError())}function L(){var t;if("undefined"!=typeof global)t=global;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;(!n||"[object Promise]"!==Object.prototype.toString.call(n.resolve())||n.cast)&&(t.Promise=at)}var N;N=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var U,W,z,B=N,G=0,H=function(t,e){X[G]=t,X[G+1]=e,G+=2,2===G&&(W?W(a):z())},I="undefined"!=typeof window?window:void 0,J=I||{},Q=J.MutationObserver||J.WebKitMutationObserver,R="undefined"!=typeof process&&"[object process]"==={}.toString.call(process),V="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,X=new Array(1e3);z=R?o():Q?s():V?u():void 0===I&&"function"==typeof _dereq_?f():c();var Z=l,$=h,tt=void 0,et=1,nt=2,rt=new P,ot=new P,it=O,st=k,ut=Y,ct=0,at=D;D.all=it,D.race=st,D.resolve=$,D.reject=ut,D._setScheduler=n,D._setAsap=r,D._asap=H,D.prototype={constructor:D,then:Z,"catch":function(t){return this.then(null,t)}};var ft=K;K.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},K.prototype._enumerate=function(){for(var t=this.length,e=this._input,n=0;this._state===tt&&t>n;n++)this._eachEntry(e[n],n)},K.prototype._eachEntry=function(t,e){var n=this._instanceConstructor,r=n.resolve;if(r===$){var o=d(t);if(o===Z&&t._state!==tt)this._settledAt(t._state,e,t._result);else if("function"!=typeof o)this._remaining--,this._result[e]=t;else if(n===at){var i=new n(p);b(i,t,o),this._willSettleAt(i,e)}else this._willSettleAt(new n(function(e){e(t)}),e)}else this._willSettleAt(r(t),e)},K.prototype._settledAt=function(t,e,n){var r=this.promise;r._state===tt&&(this._remaining--,t===nt?S(r,n):this._result[e]=n),0===this._remaining&&E(r,this._result)},K.prototype._willSettleAt=function(t,e){var n=this;j(t,void 0,function(t){n._settledAt(et,e,t)},function(t){n._settledAt(nt,e,t)})};var lt=L,ht={Promise:at,polyfill:lt};"function"==typeof define&&define.amd?define(function(){return ht}):"undefined"!=typeof module&&module.exports?module.exports=ht:"undefined"!=typeof this&&(this.ES6Promise=ht),lt()}).call(this); +}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"_process":18}],2:[function(_dereq_,module,exports){ +// stats.js - http://github.com/mrdoob/stats.js +var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px"; +i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div"); +k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display= +"block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:12,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height= +a+"px",m=b,r=0);return b},update:function(){l=this.end()}}};"object"===typeof module&&(module.exports=Stats); + +},{}],3:[function(_dereq_,module,exports){ +/** + * @author dmarcos / https://github.com/dmarcos + * @author mrdoob / http://mrdoob.com + */ + +var VRControls = function ( object, onError ) { + + var scope = this; + + var vrInputs = []; + + function filterInvalidDevices( devices ) { + + // Exclude Cardboard position sensor if Oculus exists. + + var oculusDevices = devices.filter( function ( device ) { + + return device.deviceName.toLowerCase().indexOf( 'oculus' ) !== - 1; + + } ); + + if ( oculusDevices.length >= 1 ) { + + return devices.filter( function ( device ) { + + return device.deviceName.toLowerCase().indexOf( 'cardboard' ) === - 1; + + } ); + + } else { + + return devices; + + } + + } + + function gotVRDevices( devices ) { + + devices = filterInvalidDevices( devices ); + + for ( var i = 0; i < devices.length; i ++ ) { + + if ( devices[ i ] instanceof PositionSensorVRDevice ) { + + vrInputs.push( devices[ i ] ); + + } + + } + + if ( onError ) onError( 'HMD not available' ); + + } + + if ( navigator.getVRDevices ) { + + navigator.getVRDevices().then( gotVRDevices ); + + } + + // the Rift SDK returns the position in meters + // this scale factor allows the user to define how meters + // are converted to scene units. + + this.scale = 1; + + this.update = function () { + + for ( var i = 0; i < vrInputs.length; i ++ ) { + + var vrInput = vrInputs[ i ]; + + var state = vrInput.getState(); + + if ( state.orientation !== null ) { + + object.quaternion.copy( state.orientation ); + + } + + if ( state.position !== null ) { + + object.position.copy( state.position ).multiplyScalar( scope.scale ); + + } + + } + + }; + + this.resetSensor = function () { + + for ( var i = 0; i < vrInputs.length; i ++ ) { + + var vrInput = vrInputs[ i ]; + + if ( vrInput.resetSensor !== undefined ) { + + vrInput.resetSensor(); + + } else if ( vrInput.zeroSensor !== undefined ) { + + vrInput.zeroSensor(); + + } + + } + + }; + + this.zeroSensor = function () { + + console.warn( 'THREE.VRControls: .zeroSensor() is now .resetSensor().' ); + this.resetSensor(); + + }; + + this.dispose = function () { + + vrInputs = []; + + }; + +}; + +try { + module.exports = VRControls; +} catch (e) { + THREE.VRControls = VRControls; +} + +},{}],4:[function(_dereq_,module,exports){ +/** + * @author dmarcos / https://github.com/dmarcos + * @author mrdoob / http://mrdoob.com + * + * WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html + * + * Firefox: http://mozvr.com/downloads/ + * Chromium: https://drive.google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ&usp=sharing#list + * + */ + +var VREffect = function ( renderer, onError ) { + + var vrHMD; + var eyeTranslationL, eyeFOVL, rectL; + var eyeTranslationR, eyeFOVR, rectR; + + function gotVRDevices( devices ) { + + for ( var i = 0; i < devices.length; i ++ ) { + + if ( devices[ i ] instanceof HMDVRDevice ) { + + vrHMD = devices[ i ]; + + break; // We keep the first we encounter + + } + + } + + if ( vrHMD === undefined ) { + + if ( onError ) onError( 'HMD not available' ); + + } + + } + + if ( navigator.getVRDevices ) { + + navigator.getVRDevices().then( gotVRDevices ); + + } + + // + + this.scale = 1; + + this.setSize = function( width, height ) { + + renderer.setSize( width, height ); + + }; + + // fullscreen + + var isFullscreen = false; + + var canvas = renderer.domElement; + var fullscreenchange = canvas.mozRequestFullScreen ? 'mozfullscreenchange' : 'webkitfullscreenchange'; + + document.addEventListener( fullscreenchange, function ( event ) { + + isFullscreen = document.mozFullScreenElement || document.webkitFullscreenElement; + + }, false ); + + this.setFullScreen = function ( boolean ) { + + if ( vrHMD === undefined ) return; + if ( isFullscreen === boolean ) return; + + if ( canvas.mozRequestFullScreen ) { + + canvas.mozRequestFullScreen( { vrDisplay: vrHMD } ); + + } else if ( canvas.webkitRequestFullscreen ) { + + canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } ); + + } + + }; + + // render + + var cameraL = new THREE.PerspectiveCamera(); + var cameraR = new THREE.PerspectiveCamera(); + + this.render = function ( scene, camera ) { + + if ( vrHMD ) { + + var eyeParamsL = vrHMD.getEyeParameters( 'left' ); + var eyeParamsR = vrHMD.getEyeParameters( 'right' ); + + eyeTranslationL = eyeParamsL.eyeTranslation; + eyeTranslationR = eyeParamsR.eyeTranslation; + eyeFOVL = eyeParamsL.recommendedFieldOfView; + eyeFOVR = eyeParamsR.recommendedFieldOfView; + rectL = eyeParamsL.renderRect; + rectR = eyeParamsR.renderRect; + + var sceneL, sceneR; + + if ( Array.isArray( scene ) ) { + + sceneL = scene[ 0 ]; + sceneR = scene[ 1 ]; + + } else { + + sceneL = scene; + sceneR = scene; + + } + + var size = renderer.getSize(); + size.width /= 2; + + renderer.enableScissorTest( true ); + renderer.clear(); + + if ( camera.parent === null ) camera.updateMatrixWorld(); + + cameraL.projectionMatrix = fovToProjection( eyeFOVL, true, camera.near, camera.far ); + cameraR.projectionMatrix = fovToProjection( eyeFOVR, true, camera.near, camera.far ); + + camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale ); + camera.matrixWorld.decompose( cameraR.position, cameraR.quaternion, cameraR.scale ); + + cameraL.translateX( eyeTranslationL.x * this.scale ); + cameraR.translateX( eyeTranslationR.x * this.scale ); + + // render left eye + if ( rectL ) { + + renderer.setViewport( rectL.x, rectL.y, rectL.width, rectL.height ); + renderer.setScissor( rectL.x, rectL.y, rectL.width, rectL.height ); + + } else { + + renderer.setViewport( 0, 0, size.width, size.height ); + renderer.setScissor( 0, 0, size.width, size.height ); + + } + renderer.render( sceneL, cameraL ); + + // render right eye + if ( rectR ) { + + renderer.setViewport( rectR.x, rectR.y, rectR.width, rectR.height ); + renderer.setScissor( rectR.x, rectR.y, rectR.width, rectR.height ); + + } else { + + renderer.setViewport( size.width, 0, size.width, size.height ); + renderer.setScissor( size.width, 0, size.width, size.height ); + + } + renderer.render( sceneR, cameraR ); + + renderer.enableScissorTest( false ); + + return; + + } + + // Regular render mode if not HMD + + if ( Array.isArray( scene ) ) scene = scene[ 0 ]; + + renderer.render( scene, camera ); + + }; + + // + + function fovToNDCScaleOffset( fov ) { + + var pxscale = 2.0 / ( fov.leftTan + fov.rightTan ); + var pxoffset = ( fov.leftTan - fov.rightTan ) * pxscale * 0.5; + var pyscale = 2.0 / ( fov.upTan + fov.downTan ); + var pyoffset = ( fov.upTan - fov.downTan ) * pyscale * 0.5; + return { scale: [ pxscale, pyscale ], offset: [ pxoffset, pyoffset ] }; + + } + + function fovPortToProjection( fov, rightHanded, zNear, zFar ) { + + rightHanded = rightHanded === undefined ? true : rightHanded; + zNear = zNear === undefined ? 0.01 : zNear; + zFar = zFar === undefined ? 10000.0 : zFar; + + var handednessScale = rightHanded ? - 1.0 : 1.0; + + // start with an identity matrix + var mobj = new THREE.Matrix4(); + var m = mobj.elements; + + // and with scale/offset info for normalized device coords + var scaleAndOffset = fovToNDCScaleOffset( fov ); + + // X result, map clip edges to [-w,+w] + m[ 0 * 4 + 0 ] = scaleAndOffset.scale[ 0 ]; + m[ 0 * 4 + 1 ] = 0.0; + m[ 0 * 4 + 2 ] = scaleAndOffset.offset[ 0 ] * handednessScale; + m[ 0 * 4 + 3 ] = 0.0; + + // Y result, map clip edges to [-w,+w] + // Y offset is negated because this proj matrix transforms from world coords with Y=up, + // but the NDC scaling has Y=down (thanks D3D?) + m[ 1 * 4 + 0 ] = 0.0; + m[ 1 * 4 + 1 ] = scaleAndOffset.scale[ 1 ]; + m[ 1 * 4 + 2 ] = - scaleAndOffset.offset[ 1 ] * handednessScale; + m[ 1 * 4 + 3 ] = 0.0; + + // Z result (up to the app) + m[ 2 * 4 + 0 ] = 0.0; + m[ 2 * 4 + 1 ] = 0.0; + m[ 2 * 4 + 2 ] = zFar / ( zNear - zFar ) * - handednessScale; + m[ 2 * 4 + 3 ] = ( zFar * zNear ) / ( zNear - zFar ); + + // W result (= Z in) + m[ 3 * 4 + 0 ] = 0.0; + m[ 3 * 4 + 1 ] = 0.0; + m[ 3 * 4 + 2 ] = handednessScale; + m[ 3 * 4 + 3 ] = 0.0; + + mobj.transpose(); + + return mobj; + + } + + function fovToProjection( fov, rightHanded, zNear, zFar ) { + + var DEG2RAD = Math.PI / 180.0; + + var fovPort = { + upTan: Math.tan( fov.upDegrees * DEG2RAD ), + downTan: Math.tan( fov.downDegrees * DEG2RAD ), + leftTan: Math.tan( fov.leftDegrees * DEG2RAD ), + rightTan: Math.tan( fov.rightDegrees * DEG2RAD ) + }; + + return fovPortToProjection( fovPort, rightHanded, zNear, zFar ); + + } + +}; + +try { + module.exports = VREffect; +} catch (e) { + THREE.VREffect = VREffect; +} + +},{}],5:[function(_dereq_,module,exports){ +var self = self || {};// File:src/Three.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +var THREE = { REVISION: '73' }; + +// + +if ( typeof define === 'function' && define.amd ) { + + define( 'three', THREE ); + +} else if ( 'undefined' !== typeof exports && 'undefined' !== typeof module ) { + + module.exports = THREE; + +} + + +// polyfills + +if ( self.requestAnimationFrame === undefined || self.cancelAnimationFrame === undefined ) { + + // Missing in Android stock browser. + + ( function () { + + var lastTime = 0; + var vendors = [ 'ms', 'moz', 'webkit', 'o' ]; + + for ( var x = 0; x < vendors.length && ! self.requestAnimationFrame; ++ x ) { + + self.requestAnimationFrame = self[ vendors[ x ] + 'RequestAnimationFrame' ]; + self.cancelAnimationFrame = self[ vendors[ x ] + 'CancelAnimationFrame' ] || self[ vendors[ x ] + 'CancelRequestAnimationFrame' ]; + + } + + if ( self.requestAnimationFrame === undefined && self.setTimeout !== undefined ) { + + self.requestAnimationFrame = function ( callback ) { + + var currTime = Date.now(), timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) ); + var id = self.setTimeout( function () { + + callback( currTime + timeToCall ); + + }, timeToCall ); + lastTime = currTime + timeToCall; + return id; + + }; + + } + + if ( self.cancelAnimationFrame === undefined && self.clearTimeout !== undefined ) { + + self.cancelAnimationFrame = function ( id ) { + + self.clearTimeout( id ); + + }; + + } + + } )(); + +} + +// + +if ( self.performance === undefined ) { + + self.performance = {}; + +} + +if ( self.performance.now === undefined ) { + + ( function () { + + var start = Date.now(); + + self.performance.now = function () { + + return Date.now() - start; + + } + + } )(); + +} + +// + +if ( Number.EPSILON === undefined ) { + + Number.EPSILON = Math.pow( 2, -52 ); + +} + +// + +if ( Math.sign === undefined ) { + + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign + + Math.sign = function ( x ) { + + return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x; + + }; + +} + +if ( Function.prototype.name === undefined && Object.defineProperty !== undefined ) { + + // Missing in IE9-11. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name + + Object.defineProperty( Function.prototype, 'name', { + + get: function () { + + return this.toString().match( /^\s*function\s*(\S*)\s*\(/ )[ 1 ]; + + } + + } ); + +} + +// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.button + +THREE.MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 }; + +// GL STATE CONSTANTS + +THREE.CullFaceNone = 0; +THREE.CullFaceBack = 1; +THREE.CullFaceFront = 2; +THREE.CullFaceFrontBack = 3; + +THREE.FrontFaceDirectionCW = 0; +THREE.FrontFaceDirectionCCW = 1; + +// SHADOWING TYPES + +THREE.BasicShadowMap = 0; +THREE.PCFShadowMap = 1; +THREE.PCFSoftShadowMap = 2; + +// MATERIAL CONSTANTS + +// side + +THREE.FrontSide = 0; +THREE.BackSide = 1; +THREE.DoubleSide = 2; + +// shading + +THREE.FlatShading = 1; +THREE.SmoothShading = 2; + +// colors + +THREE.NoColors = 0; +THREE.FaceColors = 1; +THREE.VertexColors = 2; + +// blending modes + +THREE.NoBlending = 0; +THREE.NormalBlending = 1; +THREE.AdditiveBlending = 2; +THREE.SubtractiveBlending = 3; +THREE.MultiplyBlending = 4; +THREE.CustomBlending = 5; + +// custom blending equations +// (numbers start from 100 not to clash with other +// mappings to OpenGL constants defined in Texture.js) + +THREE.AddEquation = 100; +THREE.SubtractEquation = 101; +THREE.ReverseSubtractEquation = 102; +THREE.MinEquation = 103; +THREE.MaxEquation = 104; + +// custom blending destination factors + +THREE.ZeroFactor = 200; +THREE.OneFactor = 201; +THREE.SrcColorFactor = 202; +THREE.OneMinusSrcColorFactor = 203; +THREE.SrcAlphaFactor = 204; +THREE.OneMinusSrcAlphaFactor = 205; +THREE.DstAlphaFactor = 206; +THREE.OneMinusDstAlphaFactor = 207; + +// custom blending source factors + +//THREE.ZeroFactor = 200; +//THREE.OneFactor = 201; +//THREE.SrcAlphaFactor = 204; +//THREE.OneMinusSrcAlphaFactor = 205; +//THREE.DstAlphaFactor = 206; +//THREE.OneMinusDstAlphaFactor = 207; +THREE.DstColorFactor = 208; +THREE.OneMinusDstColorFactor = 209; +THREE.SrcAlphaSaturateFactor = 210; + +// depth modes + +THREE.NeverDepth = 0; +THREE.AlwaysDepth = 1; +THREE.LessDepth = 2; +THREE.LessEqualDepth = 3; +THREE.EqualDepth = 4; +THREE.GreaterEqualDepth = 5; +THREE.GreaterDepth = 6; +THREE.NotEqualDepth = 7; + + +// TEXTURE CONSTANTS + +THREE.MultiplyOperation = 0; +THREE.MixOperation = 1; +THREE.AddOperation = 2; + +// Mapping modes + +THREE.UVMapping = 300; + +THREE.CubeReflectionMapping = 301; +THREE.CubeRefractionMapping = 302; + +THREE.EquirectangularReflectionMapping = 303; +THREE.EquirectangularRefractionMapping = 304; + +THREE.SphericalReflectionMapping = 305; + +// Wrapping modes + +THREE.RepeatWrapping = 1000; +THREE.ClampToEdgeWrapping = 1001; +THREE.MirroredRepeatWrapping = 1002; + +// Filters + +THREE.NearestFilter = 1003; +THREE.NearestMipMapNearestFilter = 1004; +THREE.NearestMipMapLinearFilter = 1005; +THREE.LinearFilter = 1006; +THREE.LinearMipMapNearestFilter = 1007; +THREE.LinearMipMapLinearFilter = 1008; + +// Data types + +THREE.UnsignedByteType = 1009; +THREE.ByteType = 1010; +THREE.ShortType = 1011; +THREE.UnsignedShortType = 1012; +THREE.IntType = 1013; +THREE.UnsignedIntType = 1014; +THREE.FloatType = 1015; +THREE.HalfFloatType = 1025; + +// Pixel types + +//THREE.UnsignedByteType = 1009; +THREE.UnsignedShort4444Type = 1016; +THREE.UnsignedShort5551Type = 1017; +THREE.UnsignedShort565Type = 1018; + +// Pixel formats + +THREE.AlphaFormat = 1019; +THREE.RGBFormat = 1020; +THREE.RGBAFormat = 1021; +THREE.LuminanceFormat = 1022; +THREE.LuminanceAlphaFormat = 1023; +// THREE.RGBEFormat handled as THREE.RGBAFormat in shaders +THREE.RGBEFormat = THREE.RGBAFormat; //1024; + +// DDS / ST3C Compressed texture formats + +THREE.RGB_S3TC_DXT1_Format = 2001; +THREE.RGBA_S3TC_DXT1_Format = 2002; +THREE.RGBA_S3TC_DXT3_Format = 2003; +THREE.RGBA_S3TC_DXT5_Format = 2004; + + +// PVRTC compressed texture formats + +THREE.RGB_PVRTC_4BPPV1_Format = 2100; +THREE.RGB_PVRTC_2BPPV1_Format = 2101; +THREE.RGBA_PVRTC_4BPPV1_Format = 2102; +THREE.RGBA_PVRTC_2BPPV1_Format = 2103; + +// Loop styles for AnimationAction + +THREE.LoopOnce = 2200; +THREE.LoopRepeat = 2201; +THREE.LoopPingPong = 2202; + +// DEPRECATED + +THREE.Projector = function () { + + console.error( 'THREE.Projector has been moved to /examples/js/renderers/Projector.js.' ); + + this.projectVector = function ( vector, camera ) { + + console.warn( 'THREE.Projector: .projectVector() is now vector.project().' ); + vector.project( camera ); + + }; + + this.unprojectVector = function ( vector, camera ) { + + console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' ); + vector.unproject( camera ); + + }; + + this.pickingRay = function ( vector, camera ) { + + console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' ); + + }; + +}; + +THREE.CanvasRenderer = function () { + + console.error( 'THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js' ); + + this.domElement = document.createElement( 'canvas' ); + this.clear = function () {}; + this.render = function () {}; + this.setClearColor = function () {}; + this.setSize = function () {}; + +}; + +// File:src/math/Color.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Color = function ( color ) { + + if ( arguments.length === 3 ) { + + return this.fromArray( arguments ); + + } + + return this.set( color ); + +}; + +THREE.Color.prototype = { + + constructor: THREE.Color, + + r: 1, g: 1, b: 1, + + set: function ( value ) { + + if ( value instanceof THREE.Color ) { + + this.copy( value ); + + } else if ( typeof value === 'number' ) { + + this.setHex( value ); + + } else if ( typeof value === 'string' ) { + + this.setStyle( value ); + + } + + return this; + + }, + + setHex: function ( hex ) { + + hex = Math.floor( hex ); + + this.r = ( hex >> 16 & 255 ) / 255; + this.g = ( hex >> 8 & 255 ) / 255; + this.b = ( hex & 255 ) / 255; + + return this; + + }, + + setRGB: function ( r, g, b ) { + + this.r = r; + this.g = g; + this.b = b; + + return this; + + }, + + setHSL: function () { + + function hue2rgb( p, q, t ) { + + if ( t < 0 ) t += 1; + if ( t > 1 ) t -= 1; + if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; + if ( t < 1 / 2 ) return q; + if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); + return p; + + } + + return function ( h, s, l ) { + + // h,s,l ranges are in 0.0 - 1.0 + h = THREE.Math.euclideanModulo( h, 1 ); + s = THREE.Math.clamp( s, 0, 1 ); + l = THREE.Math.clamp( l, 0, 1 ); + + if ( s === 0 ) { + + this.r = this.g = this.b = l; + + } else { + + var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); + var q = ( 2 * l ) - p; + + this.r = hue2rgb( q, p, h + 1 / 3 ); + this.g = hue2rgb( q, p, h ); + this.b = hue2rgb( q, p, h - 1 / 3 ); + + } + + return this; + + }; + + }(), + + setStyle: function ( style ) { + + function handleAlpha( string ) { + + if ( string === undefined ) return; + + if ( parseFloat( string ) < 1 ) { + + console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); + + } + + } + + + var m; + + if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) { + + // rgb / hsl + + var color; + var name = m[ 1 ]; + var components = m[ 2 ]; + + switch ( name ) { + + case 'rgb': + case 'rgba': + + if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { + + // rgb(255,0,0) rgba(255,0,0,0.5) + this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; + this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; + this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; + + handleAlpha( color[ 5 ] ); + + return this; + + } + + if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { + + // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) + this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; + this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; + this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; + + handleAlpha( color[ 5 ] ); + + return this; + + } + + break; + + case 'hsl': + case 'hsla': + + if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { + + // hsl(120,50%,50%) hsla(120,50%,50%,0.5) + var h = parseFloat( color[ 1 ] ) / 360; + var s = parseInt( color[ 2 ], 10 ) / 100; + var l = parseInt( color[ 3 ], 10 ) / 100; + + handleAlpha( color[ 5 ] ); + + return this.setHSL( h, s, l ); + + } + + break; + + } + + } else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) { + + // hex color + + var hex = m[ 1 ]; + var size = hex.length; + + if ( size === 3 ) { + + // #ff0 + this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255; + this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255; + this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255; + + return this; + + } else if ( size === 6 ) { + + // #ff0000 + this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255; + this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255; + this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255; + + return this; + + } + + } + + if ( style && style.length > 0 ) { + + // color keywords + var hex = THREE.ColorKeywords[ style ]; + + if ( hex !== undefined ) { + + // red + this.setHex( hex ); + + } else { + + // unknown color + console.warn( 'THREE.Color: Unknown color ' + style ); + + } + + } + + return this; + + }, + + clone: function () { + + return new this.constructor( this.r, this.g, this.b ); + + }, + + copy: function ( color ) { + + this.r = color.r; + this.g = color.g; + this.b = color.b; + + return this; + + }, + + copyGammaToLinear: function ( color, gammaFactor ) { + + if ( gammaFactor === undefined ) gammaFactor = 2.0; + + this.r = Math.pow( color.r, gammaFactor ); + this.g = Math.pow( color.g, gammaFactor ); + this.b = Math.pow( color.b, gammaFactor ); + + return this; + + }, + + copyLinearToGamma: function ( color, gammaFactor ) { + + if ( gammaFactor === undefined ) gammaFactor = 2.0; + + var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0; + + this.r = Math.pow( color.r, safeInverse ); + this.g = Math.pow( color.g, safeInverse ); + this.b = Math.pow( color.b, safeInverse ); + + return this; + + }, + + convertGammaToLinear: function () { + + var r = this.r, g = this.g, b = this.b; + + this.r = r * r; + this.g = g * g; + this.b = b * b; + + return this; + + }, + + convertLinearToGamma: function () { + + this.r = Math.sqrt( this.r ); + this.g = Math.sqrt( this.g ); + this.b = Math.sqrt( this.b ); + + return this; + + }, + + getHex: function () { + + return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; + + }, + + getHexString: function () { + + return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 ); + + }, + + getHSL: function ( optionalTarget ) { + + // h,s,l ranges are in 0.0 - 1.0 + + var hsl = optionalTarget || { h: 0, s: 0, l: 0 }; + + var r = this.r, g = this.g, b = this.b; + + var max = Math.max( r, g, b ); + var min = Math.min( r, g, b ); + + var hue, saturation; + var lightness = ( min + max ) / 2.0; + + if ( min === max ) { + + hue = 0; + saturation = 0; + + } else { + + var delta = max - min; + + saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); + + switch ( max ) { + + case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; + case g: hue = ( b - r ) / delta + 2; break; + case b: hue = ( r - g ) / delta + 4; break; + + } + + hue /= 6; + + } + + hsl.h = hue; + hsl.s = saturation; + hsl.l = lightness; + + return hsl; + + }, + + getStyle: function () { + + return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')'; + + }, + + offsetHSL: function ( h, s, l ) { + + var hsl = this.getHSL(); + + hsl.h += h; hsl.s += s; hsl.l += l; + + this.setHSL( hsl.h, hsl.s, hsl.l ); + + return this; + + }, + + add: function ( color ) { + + this.r += color.r; + this.g += color.g; + this.b += color.b; + + return this; + + }, + + addColors: function ( color1, color2 ) { + + this.r = color1.r + color2.r; + this.g = color1.g + color2.g; + this.b = color1.b + color2.b; + + return this; + + }, + + addScalar: function ( s ) { + + this.r += s; + this.g += s; + this.b += s; + + return this; + + }, + + multiply: function ( color ) { + + this.r *= color.r; + this.g *= color.g; + this.b *= color.b; + + return this; + + }, + + multiplyScalar: function ( s ) { + + this.r *= s; + this.g *= s; + this.b *= s; + + return this; + + }, + + lerp: function ( color, alpha ) { + + this.r += ( color.r - this.r ) * alpha; + this.g += ( color.g - this.g ) * alpha; + this.b += ( color.b - this.b ) * alpha; + + return this; + + }, + + equals: function ( c ) { + + return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.r = array[ offset ]; + this.g = array[ offset + 1 ]; + this.b = array[ offset + 2 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.r; + array[ offset + 1 ] = this.g; + array[ offset + 2 ] = this.b; + + return array; + + } + +}; + +THREE.ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, +'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, +'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, +'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B, +'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B, +'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F, +'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3, +'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222, +'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700, +'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4, +'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00, +'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3, +'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA, +'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32, +'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3, +'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC, +'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD, +'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6, +'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9, +'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F, +'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE, +'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA, +'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0, +'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 }; + +// File:src/math/Quaternion.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://clara.io + */ + +THREE.Quaternion = function ( x, y, z, w ) { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._w = ( w !== undefined ) ? w : 1; + +}; + +THREE.Quaternion.prototype = { + + constructor: THREE.Quaternion, + + get x () { + + return this._x; + + }, + + set x ( value ) { + + this._x = value; + this.onChangeCallback(); + + }, + + get y () { + + return this._y; + + }, + + set y ( value ) { + + this._y = value; + this.onChangeCallback(); + + }, + + get z () { + + return this._z; + + }, + + set z ( value ) { + + this._z = value; + this.onChangeCallback(); + + }, + + get w () { + + return this._w; + + }, + + set w ( value ) { + + this._w = value; + this.onChangeCallback(); + + }, + + set: function ( x, y, z, w ) { + + this._x = x; + this._y = y; + this._z = z; + this._w = w; + + this.onChangeCallback(); + + return this; + + }, + + clone: function () { + + return new this.constructor( this._x, this._y, this._z, this._w ); + + }, + + copy: function ( quaternion ) { + + this._x = quaternion.x; + this._y = quaternion.y; + this._z = quaternion.z; + this._w = quaternion.w; + + this.onChangeCallback(); + + return this; + + }, + + setFromEuler: function ( euler, update ) { + + if ( euler instanceof THREE.Euler === false ) { + + throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + + } + + // http://www.mathworks.com/matlabcentral/fileexchange/ + // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ + // content/SpinCalc.m + + var c1 = Math.cos( euler._x / 2 ); + var c2 = Math.cos( euler._y / 2 ); + var c3 = Math.cos( euler._z / 2 ); + var s1 = Math.sin( euler._x / 2 ); + var s2 = Math.sin( euler._y / 2 ); + var s3 = Math.sin( euler._z / 2 ); + + var order = euler.order; + + if ( order === 'XYZ' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( order === 'YXZ' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } else if ( order === 'ZXY' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( order === 'ZYX' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } else if ( order === 'YZX' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( order === 'XZY' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } + + if ( update !== false ) this.onChangeCallback(); + + return this; + + }, + + setFromAxisAngle: function ( axis, angle ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm + + // assumes axis is normalized + + var halfAngle = angle / 2, s = Math.sin( halfAngle ); + + this._x = axis.x * s; + this._y = axis.y * s; + this._z = axis.z * s; + this._w = Math.cos( halfAngle ); + + this.onChangeCallback(); + + return this; + + }, + + setFromRotationMatrix: function ( m ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements, + + m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], + m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], + m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], + + trace = m11 + m22 + m33, + s; + + if ( trace > 0 ) { + + s = 0.5 / Math.sqrt( trace + 1.0 ); + + this._w = 0.25 / s; + this._x = ( m32 - m23 ) * s; + this._y = ( m13 - m31 ) * s; + this._z = ( m21 - m12 ) * s; + + } else if ( m11 > m22 && m11 > m33 ) { + + s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); + + this._w = ( m32 - m23 ) / s; + this._x = 0.25 * s; + this._y = ( m12 + m21 ) / s; + this._z = ( m13 + m31 ) / s; + + } else if ( m22 > m33 ) { + + s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); + + this._w = ( m13 - m31 ) / s; + this._x = ( m12 + m21 ) / s; + this._y = 0.25 * s; + this._z = ( m23 + m32 ) / s; + + } else { + + s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); + + this._w = ( m21 - m12 ) / s; + this._x = ( m13 + m31 ) / s; + this._y = ( m23 + m32 ) / s; + this._z = 0.25 * s; + + } + + this.onChangeCallback(); + + return this; + + }, + + setFromUnitVectors: function () { + + // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final + + // assumes direction vectors vFrom and vTo are normalized + + var v1, r; + + var EPS = 0.000001; + + return function ( vFrom, vTo ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + r = vFrom.dot( vTo ) + 1; + + if ( r < EPS ) { + + r = 0; + + if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { + + v1.set( - vFrom.y, vFrom.x, 0 ); + + } else { + + v1.set( 0, - vFrom.z, vFrom.y ); + + } + + } else { + + v1.crossVectors( vFrom, vTo ); + + } + + this._x = v1.x; + this._y = v1.y; + this._z = v1.z; + this._w = r; + + this.normalize(); + + return this; + + } + + }(), + + inverse: function () { + + this.conjugate().normalize(); + + return this; + + }, + + conjugate: function () { + + this._x *= - 1; + this._y *= - 1; + this._z *= - 1; + + this.onChangeCallback(); + + return this; + + }, + + dot: function ( v ) { + + return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; + + }, + + lengthSq: function () { + + return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; + + }, + + length: function () { + + return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); + + }, + + normalize: function () { + + var l = this.length(); + + if ( l === 0 ) { + + this._x = 0; + this._y = 0; + this._z = 0; + this._w = 1; + + } else { + + l = 1 / l; + + this._x = this._x * l; + this._y = this._y * l; + this._z = this._z * l; + this._w = this._w * l; + + } + + this.onChangeCallback(); + + return this; + + }, + + multiply: function ( q, p ) { + + if ( p !== undefined ) { + + console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); + return this.multiplyQuaternions( q, p ); + + } + + return this.multiplyQuaternions( this, q ); + + }, + + multiplyQuaternions: function ( a, b ) { + + // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm + + var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; + var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; + + this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; + this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; + this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; + this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; + + this.onChangeCallback(); + + return this; + + }, + + multiplyVector3: function ( vector ) { + + console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); + return vector.applyQuaternion( this ); + + }, + + slerp: function ( qb, t ) { + + if ( t === 0 ) return this; + if ( t === 1 ) return this.copy( qb ); + + var x = this._x, y = this._y, z = this._z, w = this._w; + + // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + + var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; + + if ( cosHalfTheta < 0 ) { + + this._w = - qb._w; + this._x = - qb._x; + this._y = - qb._y; + this._z = - qb._z; + + cosHalfTheta = - cosHalfTheta; + + } else { + + this.copy( qb ); + + } + + if ( cosHalfTheta >= 1.0 ) { + + this._w = w; + this._x = x; + this._y = y; + this._z = z; + + return this; + + } + + var halfTheta = Math.acos( cosHalfTheta ); + var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); + + if ( Math.abs( sinHalfTheta ) < 0.001 ) { + + this._w = 0.5 * ( w + this._w ); + this._x = 0.5 * ( x + this._x ); + this._y = 0.5 * ( y + this._y ); + this._z = 0.5 * ( z + this._z ); + + return this; + + } + + var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, + ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; + + this._w = ( w * ratioA + this._w * ratioB ); + this._x = ( x * ratioA + this._x * ratioB ); + this._y = ( y * ratioA + this._y * ratioB ); + this._z = ( z * ratioA + this._z * ratioB ); + + this.onChangeCallback(); + + return this; + + }, + + equals: function ( quaternion ) { + + return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this._x = array[ offset ]; + this._y = array[ offset + 1 ]; + this._z = array[ offset + 2 ]; + this._w = array[ offset + 3 ]; + + this.onChangeCallback(); + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this._x; + array[ offset + 1 ] = this._y; + array[ offset + 2 ] = this._z; + array[ offset + 3 ] = this._w; + + return array; + + }, + + onChange: function ( callback ) { + + this.onChangeCallback = callback; + + return this; + + }, + + onChangeCallback: function () {} + +}; + +THREE.Quaternion.slerp = function ( qa, qb, qm, t ) { + + return qm.copy( qa ).slerp( qb, t ); + +}; + +// File:src/math/Vector2.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author philogb / http://blog.thejit.org/ + * @author egraether / http://egraether.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.Vector2 = function ( x, y ) { + + this.x = x || 0; + this.y = y || 0; + +}; + +THREE.Vector2.prototype = { + + constructor: THREE.Vector2, + + get width() { return this.x }, + set width( value ) { this.x = value }, + + get height() { return this.y }, + set height( value ) { this.y = value }, + + // + + set: function ( x, y ) { + + this.x = x; + this.y = y; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + clone: function () { + + return new this.constructor( this.x, this.y ); + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + + return this; + + }, + + addScaledVector: function ( v, s ) { + + this.x += v.x * s; + this.y += v.y * s; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + + return this; + + }, + + subScalar: function ( s ) { + + this.x -= s; + this.y -= s; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + + return this; + + }, + + multiply: function ( v ) { + + this.x *= v.x; + this.y *= v.y; + + return this; + + }, + + multiplyScalar: function ( scalar ) { + + if ( isFinite( scalar ) ) { + this.x *= scalar; + this.y *= scalar; + } else { + this.x = 0; + this.y = 0; + } + + return this; + + }, + + divide: function ( v ) { + + this.x /= v.x; + this.y /= v.y; + + return this; + + }, + + divideScalar: function ( scalar ) { + + return this.multiplyScalar( 1 / scalar ); + + }, + + min: function ( v ) { + + this.x = Math.min( this.x, v.x ); + this.y = Math.min( this.y, v.y ); + + return this; + + }, + + max: function ( v ) { + + this.x = Math.max( this.x, v.x ); + this.y = Math.max( this.y, v.y ); + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + this.x = Math.max( min.x, Math.min( max.x, this.x ) ); + this.y = Math.max( min.y, Math.min( max.y, this.y ) ); + + return this; + + }, + + clampScalar: function () { + + var min, max; + + return function clampScalar( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector2(); + max = new THREE.Vector2(); + + } + + min.set( minVal, minVal ); + max.set( maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + }(), + + clampLength: function ( min, max ) { + + var length = this.length(); + + this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length ); + + return this; + + }, + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y ); + + }, + + lengthManhattan: function() { + + return Math.abs( this.x ) + Math.abs( this.y ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + distanceTo: function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared: function ( v ) { + + var dx = this.x - v.x, dy = this.y - v.y; + return dx * dx + dy * dy; + + }, + + setLength: function ( length ) { + + return this.multiplyScalar( length / this.length() ); + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + + return this; + + }, + + lerpVectors: function ( v1, v2, alpha ) { + + this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + + return array; + + }, + + fromAttribute: function ( attribute, index, offset ) { + + if ( offset === undefined ) offset = 0; + + index = index * attribute.itemSize + offset; + + this.x = attribute.array[ index ]; + this.y = attribute.array[ index + 1 ]; + + return this; + + }, + + rotateAround: function ( center, angle ) { + + var c = Math.cos( angle ), s = Math.sin( angle ); + + var x = this.x - center.x; + var y = this.y - center.y; + + this.x = x * c - y * s + center.x; + this.y = x * s + y * c + center.y; + + return this; + + } + +}; + +// File:src/math/Vector3.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author *kile / http://kile.stravaganza.org/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Vector3 = function ( x, y, z ) { + + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + +}; + +THREE.Vector3.prototype = { + + constructor: THREE.Vector3, + + set: function ( x, y, z ) { + + this.x = x; + this.y = y; + this.z = z; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setZ: function ( z ) { + + this.z = z; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + case 2: this.z = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + case 2: return this.z; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + clone: function () { + + return new this.constructor( this.x, this.y, this.z ); + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + this.z = v.z; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + this.z += v.z; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + this.z += s; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + + return this; + + }, + + addScaledVector: function ( v, s ) { + + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + + return this; + + }, + + subScalar: function ( s ) { + + this.x -= s; + this.y -= s; + this.z -= s; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + + return this; + + }, + + multiply: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); + return this.multiplyVectors( v, w ); + + } + + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; + + return this; + + }, + + multiplyScalar: function ( scalar ) { + + if ( isFinite( scalar ) ) { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + } else { + this.x = 0; + this.y = 0; + this.z = 0; + } + + return this; + + }, + + multiplyVectors: function ( a, b ) { + + this.x = a.x * b.x; + this.y = a.y * b.y; + this.z = a.z * b.z; + + return this; + + }, + + applyEuler: function () { + + var quaternion; + + return function applyEuler( euler ) { + + if ( euler instanceof THREE.Euler === false ) { + + console.error( 'THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + + } + + if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); + + this.applyQuaternion( quaternion.setFromEuler( euler ) ); + + return this; + + }; + + }(), + + applyAxisAngle: function () { + + var quaternion; + + return function applyAxisAngle( axis, angle ) { + + if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); + + this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); + + return this; + + }; + + }(), + + applyMatrix3: function ( m ) { + + var x = this.x; + var y = this.y; + var z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; + this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; + this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; + + return this; + + }, + + applyMatrix4: function ( m ) { + + // input: THREE.Matrix4 affine matrix + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ]; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ]; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ]; + + return this; + + }, + + applyProjection: function ( m ) { + + // input: THREE.Matrix4 projection matrix + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide + + this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d; + this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d; + this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d; + + return this; + + }, + + applyQuaternion: function ( q ) { + + var x = this.x; + var y = this.y; + var z = this.z; + + var qx = q.x; + var qy = q.y; + var qz = q.z; + var qw = q.w; + + // calculate quat * vector + + var ix = qw * x + qy * z - qz * y; + var iy = qw * y + qz * x - qx * z; + var iz = qw * z + qx * y - qy * x; + var iw = - qx * x - qy * y - qz * z; + + // calculate result * inverse quat + + this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; + this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; + this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; + + return this; + + }, + + project: function () { + + var matrix; + + return function project( camera ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + + matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) ); + return this.applyProjection( matrix ); + + }; + + }(), + + unproject: function () { + + var matrix; + + return function unproject( camera ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + + matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) ); + return this.applyProjection( matrix ); + + }; + + }(), + + transformDirection: function ( m ) { + + // input: THREE.Matrix4 affine matrix + // vector interpreted as a direction + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; + + this.normalize(); + + return this; + + }, + + divide: function ( v ) { + + this.x /= v.x; + this.y /= v.y; + this.z /= v.z; + + return this; + + }, + + divideScalar: function ( scalar ) { + + return this.multiplyScalar( 1 / scalar ); + + }, + + min: function ( v ) { + + this.x = Math.min( this.x, v.x ); + this.y = Math.min( this.y, v.y ); + this.z = Math.min( this.z, v.z ); + + return this; + + }, + + max: function ( v ) { + + this.x = Math.max( this.x, v.x ); + this.y = Math.max( this.y, v.y ); + this.z = Math.max( this.z, v.z ); + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + this.x = Math.max( min.x, Math.min( max.x, this.x ) ); + this.y = Math.max( min.y, Math.min( max.y, this.y ) ); + this.z = Math.max( min.z, Math.min( max.z, this.z ) ); + + return this; + + }, + + clampScalar: function () { + + var min, max; + + return function clampScalar( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector3(); + max = new THREE.Vector3(); + + } + + min.set( minVal, minVal, minVal ); + max.set( maxVal, maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + }(), + + clampLength: function ( min, max ) { + + var length = this.length(); + + this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length ); + + return this; + + }, + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y + this.z * v.z; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y + this.z * this.z; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); + + }, + + lengthManhattan: function () { + + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + setLength: function ( length ) { + + return this.multiplyScalar( length / this.length() ); + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; + + return this; + + }, + + lerpVectors: function ( v1, v2, alpha ) { + + this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); + + return this; + + }, + + cross: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); + return this.crossVectors( v, w ); + + } + + var x = this.x, y = this.y, z = this.z; + + this.x = y * v.z - z * v.y; + this.y = z * v.x - x * v.z; + this.z = x * v.y - y * v.x; + + return this; + + }, + + crossVectors: function ( a, b ) { + + var ax = a.x, ay = a.y, az = a.z; + var bx = b.x, by = b.y, bz = b.z; + + this.x = ay * bz - az * by; + this.y = az * bx - ax * bz; + this.z = ax * by - ay * bx; + + return this; + + }, + + projectOnVector: function () { + + var v1, dot; + + return function projectOnVector( vector ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + v1.copy( vector ).normalize(); + + dot = this.dot( v1 ); + + return this.copy( v1 ).multiplyScalar( dot ); + + }; + + }(), + + projectOnPlane: function () { + + var v1; + + return function projectOnPlane( planeNormal ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + v1.copy( this ).projectOnVector( planeNormal ); + + return this.sub( v1 ); + + } + + }(), + + reflect: function () { + + // reflect incident vector off plane orthogonal to normal + // normal is assumed to have unit length + + var v1; + + return function reflect( normal ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); + + } + + }(), + + angleTo: function ( v ) { + + var theta = this.dot( v ) / ( this.length() * v.length() ); + + // clamp, to handle numerical problems + + return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) ); + + }, + + distanceTo: function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared: function ( v ) { + + var dx = this.x - v.x; + var dy = this.y - v.y; + var dz = this.z - v.z; + + return dx * dx + dy * dy + dz * dz; + + }, + + setEulerFromRotationMatrix: function ( m, order ) { + + console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); + + }, + + setEulerFromQuaternion: function ( q, order ) { + + console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); + + }, + + getPositionFromMatrix: function ( m ) { + + console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); + + return this.setFromMatrixPosition( m ); + + }, + + getScaleFromMatrix: function ( m ) { + + console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); + + return this.setFromMatrixScale( m ); + + }, + + getColumnFromMatrix: function ( index, matrix ) { + + console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); + + return this.setFromMatrixColumn( index, matrix ); + + }, + + setFromMatrixPosition: function ( m ) { + + this.x = m.elements[ 12 ]; + this.y = m.elements[ 13 ]; + this.z = m.elements[ 14 ]; + + return this; + + }, + + setFromMatrixScale: function ( m ) { + + var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length(); + var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length(); + var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length(); + + this.x = sx; + this.y = sy; + this.z = sz; + + return this; + + }, + + setFromMatrixColumn: function ( index, matrix ) { + + var offset = index * 4; + + var me = matrix.elements; + + this.x = me[ offset ]; + this.y = me[ offset + 1 ]; + this.z = me[ offset + 2 ]; + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; + + return array; + + }, + + fromAttribute: function ( attribute, index, offset ) { + + if ( offset === undefined ) offset = 0; + + index = index * attribute.itemSize + offset; + + this.x = attribute.array[ index ]; + this.y = attribute.array[ index + 1 ]; + this.z = attribute.array[ index + 2 ]; + + return this; + + } + +}; + +// File:src/math/Vector4.js + +/** + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Vector4 = function ( x, y, z, w ) { + + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + this.w = ( w !== undefined ) ? w : 1; + +}; + +THREE.Vector4.prototype = { + + constructor: THREE.Vector4, + + set: function ( x, y, z, w ) { + + this.x = x; + this.y = y; + this.z = z; + this.w = w; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setZ: function ( z ) { + + this.z = z; + + return this; + + }, + + setW: function ( w ) { + + this.w = w; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + case 2: this.z = value; break; + case 3: this.w = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + case 2: return this.z; + case 3: return this.w; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + clone: function () { + + return new this.constructor( this.x, this.y, this.z, this.w ); + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + this.z = v.z; + this.w = ( v.w !== undefined ) ? v.w : 1; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + this.z += v.z; + this.w += v.w; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + this.z += s; + this.w += s; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + this.w = a.w + b.w; + + return this; + + }, + + addScaledVector: function ( v, s ) { + + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; + this.w += v.w * s; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + this.w -= v.w; + + return this; + + }, + + subScalar: function ( s ) { + + this.x -= s; + this.y -= s; + this.z -= s; + this.w -= s; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + this.w = a.w - b.w; + + return this; + + }, + + multiplyScalar: function ( scalar ) { + + if ( isFinite( scalar ) ) { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + this.w *= scalar; + } else { + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 0; + } + + return this; + + }, + + applyMatrix4: function ( m ) { + + var x = this.x; + var y = this.y; + var z = this.z; + var w = this.w; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; + this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; + + return this; + + }, + + divideScalar: function ( scalar ) { + + return this.multiplyScalar( 1 / scalar ); + + }, + + setAxisAngleFromQuaternion: function ( q ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm + + // q is assumed to be normalized + + this.w = 2 * Math.acos( q.w ); + + var s = Math.sqrt( 1 - q.w * q.w ); + + if ( s < 0.0001 ) { + + this.x = 1; + this.y = 0; + this.z = 0; + + } else { + + this.x = q.x / s; + this.y = q.y / s; + this.z = q.z / s; + + } + + return this; + + }, + + setAxisAngleFromRotationMatrix: function ( m ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var angle, x, y, z, // variables for result + epsilon = 0.01, // margin to allow for rounding errors + epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees + + te = m.elements, + + m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], + m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], + m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; + + if ( ( Math.abs( m12 - m21 ) < epsilon ) + && ( Math.abs( m13 - m31 ) < epsilon ) + && ( Math.abs( m23 - m32 ) < epsilon ) ) { + + // singularity found + // first check for identity matrix which must have +1 for all terms + // in leading diagonal and zero in other terms + + if ( ( Math.abs( m12 + m21 ) < epsilon2 ) + && ( Math.abs( m13 + m31 ) < epsilon2 ) + && ( Math.abs( m23 + m32 ) < epsilon2 ) + && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { + + // this singularity is identity matrix so angle = 0 + + this.set( 1, 0, 0, 0 ); + + return this; // zero angle, arbitrary axis + + } + + // otherwise this singularity is angle = 180 + + angle = Math.PI; + + var xx = ( m11 + 1 ) / 2; + var yy = ( m22 + 1 ) / 2; + var zz = ( m33 + 1 ) / 2; + var xy = ( m12 + m21 ) / 4; + var xz = ( m13 + m31 ) / 4; + var yz = ( m23 + m32 ) / 4; + + if ( ( xx > yy ) && ( xx > zz ) ) { + + // m11 is the largest diagonal term + + if ( xx < epsilon ) { + + x = 0; + y = 0.707106781; + z = 0.707106781; + + } else { + + x = Math.sqrt( xx ); + y = xy / x; + z = xz / x; + + } + + } else if ( yy > zz ) { + + // m22 is the largest diagonal term + + if ( yy < epsilon ) { + + x = 0.707106781; + y = 0; + z = 0.707106781; + + } else { + + y = Math.sqrt( yy ); + x = xy / y; + z = yz / y; + + } + + } else { + + // m33 is the largest diagonal term so base result on this + + if ( zz < epsilon ) { + + x = 0.707106781; + y = 0.707106781; + z = 0; + + } else { + + z = Math.sqrt( zz ); + x = xz / z; + y = yz / z; + + } + + } + + this.set( x, y, z, angle ); + + return this; // return 180 deg rotation + + } + + // as we have reached here there are no singularities so we can handle normally + + var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + + ( m13 - m31 ) * ( m13 - m31 ) + + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize + + if ( Math.abs( s ) < 0.001 ) s = 1; + + // prevent divide by zero, should not happen if matrix is orthogonal and should be + // caught by singularity test above, but I've left it in just in case + + this.x = ( m32 - m23 ) / s; + this.y = ( m13 - m31 ) / s; + this.z = ( m21 - m12 ) / s; + this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); + + return this; + + }, + + min: function ( v ) { + + this.x = Math.min( this.x, v.x ); + this.y = Math.min( this.y, v.y ); + this.z = Math.min( this.z, v.z ); + this.w = Math.min( this.w, v.w ); + + return this; + + }, + + max: function ( v ) { + + this.x = Math.max( this.x, v.x ); + this.y = Math.max( this.y, v.y ); + this.z = Math.max( this.z, v.z ); + this.w = Math.max( this.w, v.w ); + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + this.x = Math.max( min.x, Math.min( max.x, this.x ) ); + this.y = Math.max( min.y, Math.min( max.y, this.y ) ); + this.z = Math.max( min.z, Math.min( max.z, this.z ) ); + this.w = Math.max( min.w, Math.min( max.w, this.w ) ); + + return this; + + }, + + clampScalar: function () { + + var min, max; + + return function clampScalar( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector4(); + max = new THREE.Vector4(); + + } + + min.set( minVal, minVal, minVal, minVal ); + max.set( maxVal, maxVal, maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + }(), + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); + this.w = Math.floor( this.w ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); + this.w = Math.ceil( this.w ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); + this.w = Math.round( this.w ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); + this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; + this.w = - this.w; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); + + }, + + lengthManhattan: function () { + + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + setLength: function ( length ) { + + return this.multiplyScalar( length / this.length() ); + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; + this.w += ( v.w - this.w ) * alpha; + + return this; + + }, + + lerpVectors: function ( v1, v2, alpha ) { + + this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; + this.w = array[ offset + 3 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; + array[ offset + 3 ] = this.w; + + return array; + + }, + + fromAttribute: function ( attribute, index, offset ) { + + if ( offset === undefined ) offset = 0; + + index = index * attribute.itemSize + offset; + + this.x = attribute.array[ index ]; + this.y = attribute.array[ index + 1 ]; + this.z = attribute.array[ index + 2 ]; + this.w = attribute.array[ index + 3 ]; + + return this; + + } + +}; + +// File:src/math/Euler.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://clara.io + */ + +THREE.Euler = function ( x, y, z, order ) { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._order = order || THREE.Euler.DefaultOrder; + +}; + +THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; + +THREE.Euler.DefaultOrder = 'XYZ'; + +THREE.Euler.prototype = { + + constructor: THREE.Euler, + + get x () { + + return this._x; + + }, + + set x ( value ) { + + this._x = value; + this.onChangeCallback(); + + }, + + get y () { + + return this._y; + + }, + + set y ( value ) { + + this._y = value; + this.onChangeCallback(); + + }, + + get z () { + + return this._z; + + }, + + set z ( value ) { + + this._z = value; + this.onChangeCallback(); + + }, + + get order () { + + return this._order; + + }, + + set order ( value ) { + + this._order = value; + this.onChangeCallback(); + + }, + + set: function ( x, y, z, order ) { + + this._x = x; + this._y = y; + this._z = z; + this._order = order || this._order; + + this.onChangeCallback(); + + return this; + + }, + + clone: function () { + + return new this.constructor( this._x, this._y, this._z, this._order); + + }, + + copy: function ( euler ) { + + this._x = euler._x; + this._y = euler._y; + this._z = euler._z; + this._order = euler._order; + + this.onChangeCallback(); + + return this; + + }, + + setFromRotationMatrix: function ( m, order, update ) { + + var clamp = THREE.Math.clamp; + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements; + var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; + var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; + var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; + + order = order || this._order; + + if ( order === 'XYZ' ) { + + this._y = Math.asin( clamp( m13, - 1, 1 ) ); + + if ( Math.abs( m13 ) < 0.99999 ) { + + this._x = Math.atan2( - m23, m33 ); + this._z = Math.atan2( - m12, m11 ); + + } else { + + this._x = Math.atan2( m32, m22 ); + this._z = 0; + + } + + } else if ( order === 'YXZ' ) { + + this._x = Math.asin( - clamp( m23, - 1, 1 ) ); + + if ( Math.abs( m23 ) < 0.99999 ) { + + this._y = Math.atan2( m13, m33 ); + this._z = Math.atan2( m21, m22 ); + + } else { + + this._y = Math.atan2( - m31, m11 ); + this._z = 0; + + } + + } else if ( order === 'ZXY' ) { + + this._x = Math.asin( clamp( m32, - 1, 1 ) ); + + if ( Math.abs( m32 ) < 0.99999 ) { + + this._y = Math.atan2( - m31, m33 ); + this._z = Math.atan2( - m12, m22 ); + + } else { + + this._y = 0; + this._z = Math.atan2( m21, m11 ); + + } + + } else if ( order === 'ZYX' ) { + + this._y = Math.asin( - clamp( m31, - 1, 1 ) ); + + if ( Math.abs( m31 ) < 0.99999 ) { + + this._x = Math.atan2( m32, m33 ); + this._z = Math.atan2( m21, m11 ); + + } else { + + this._x = 0; + this._z = Math.atan2( - m12, m22 ); + + } + + } else if ( order === 'YZX' ) { + + this._z = Math.asin( clamp( m21, - 1, 1 ) ); + + if ( Math.abs( m21 ) < 0.99999 ) { + + this._x = Math.atan2( - m23, m22 ); + this._y = Math.atan2( - m31, m11 ); + + } else { + + this._x = 0; + this._y = Math.atan2( m13, m33 ); + + } + + } else if ( order === 'XZY' ) { + + this._z = Math.asin( - clamp( m12, - 1, 1 ) ); + + if ( Math.abs( m12 ) < 0.99999 ) { + + this._x = Math.atan2( m32, m22 ); + this._y = Math.atan2( m13, m11 ); + + } else { + + this._x = Math.atan2( - m23, m33 ); + this._y = 0; + + } + + } else { + + console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order ) + + } + + this._order = order; + + if ( update !== false ) this.onChangeCallback(); + + return this; + + }, + + setFromQuaternion: function () { + + var matrix; + + return function ( q, order, update ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + matrix.makeRotationFromQuaternion( q ); + this.setFromRotationMatrix( matrix, order, update ); + + return this; + + }; + + }(), + + setFromVector3: function ( v, order ) { + + return this.set( v.x, v.y, v.z, order || this._order ); + + }, + + reorder: function () { + + // WARNING: this discards revolution information -bhouston + + var q = new THREE.Quaternion(); + + return function ( newOrder ) { + + q.setFromEuler( this ); + this.setFromQuaternion( q, newOrder ); + + }; + + }(), + + equals: function ( euler ) { + + return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); + + }, + + fromArray: function ( array ) { + + this._x = array[ 0 ]; + this._y = array[ 1 ]; + this._z = array[ 2 ]; + if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; + + this.onChangeCallback(); + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this._x; + array[ offset + 1 ] = this._y; + array[ offset + 2 ] = this._z; + array[ offset + 3 ] = this._order; + + return array; + + }, + + toVector3: function ( optionalResult ) { + + if ( optionalResult ) { + + return optionalResult.set( this._x, this._y, this._z ); + + } else { + + return new THREE.Vector3( this._x, this._y, this._z ); + + } + + }, + + onChange: function ( callback ) { + + this.onChangeCallback = callback; + + return this; + + }, + + onChangeCallback: function () {} + +}; + +// File:src/math/Line3.js + +/** + * @author bhouston / http://clara.io + */ + +THREE.Line3 = function ( start, end ) { + + this.start = ( start !== undefined ) ? start : new THREE.Vector3(); + this.end = ( end !== undefined ) ? end : new THREE.Vector3(); + +}; + +THREE.Line3.prototype = { + + constructor: THREE.Line3, + + set: function ( start, end ) { + + this.start.copy( start ); + this.end.copy( end ); + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( line ) { + + this.start.copy( line.start ); + this.end.copy( line.end ); + + return this; + + }, + + center: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); + + }, + + delta: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.subVectors( this.end, this.start ); + + }, + + distanceSq: function () { + + return this.start.distanceToSquared( this.end ); + + }, + + distance: function () { + + return this.start.distanceTo( this.end ); + + }, + + at: function ( t, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + return this.delta( result ).multiplyScalar( t ).add( this.start ); + + }, + + closestPointToPointParameter: function () { + + var startP = new THREE.Vector3(); + var startEnd = new THREE.Vector3(); + + return function ( point, clampToLine ) { + + startP.subVectors( point, this.start ); + startEnd.subVectors( this.end, this.start ); + + var startEnd2 = startEnd.dot( startEnd ); + var startEnd_startP = startEnd.dot( startP ); + + var t = startEnd_startP / startEnd2; + + if ( clampToLine ) { + + t = THREE.Math.clamp( t, 0, 1 ); + + } + + return t; + + }; + + }(), + + closestPointToPoint: function ( point, clampToLine, optionalTarget ) { + + var t = this.closestPointToPointParameter( point, clampToLine ); + + var result = optionalTarget || new THREE.Vector3(); + + return this.delta( result ).multiplyScalar( t ).add( this.start ); + + }, + + applyMatrix4: function ( matrix ) { + + this.start.applyMatrix4( matrix ); + this.end.applyMatrix4( matrix ); + + return this; + + }, + + equals: function ( line ) { + + return line.start.equals( this.start ) && line.end.equals( this.end ); + + } + +}; + +// File:src/math/Box2.js + +/** + * @author bhouston / http://clara.io + */ + +THREE.Box2 = function ( min, max ) { + + this.min = ( min !== undefined ) ? min : new THREE.Vector2( Infinity, Infinity ); + this.max = ( max !== undefined ) ? max : new THREE.Vector2( - Infinity, - Infinity ); + +}; + +THREE.Box2.prototype = { + + constructor: THREE.Box2, + + set: function ( min, max ) { + + this.min.copy( min ); + this.max.copy( max ); + + return this; + + }, + + setFromPoints: function ( points ) { + + this.makeEmpty(); + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + this.expandByPoint( points[ i ] ) + + } + + return this; + + }, + + setFromCenterAndSize: function () { + + var v1 = new THREE.Vector2(); + + return function ( center, size ) { + + var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); + this.min.copy( center ).sub( halfSize ); + this.max.copy( center ).add( halfSize ); + + return this; + + }; + + }(), + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( box ) { + + this.min.copy( box.min ); + this.max.copy( box.max ); + + return this; + + }, + + makeEmpty: function () { + + this.min.x = this.min.y = Infinity; + this.max.x = this.max.y = - Infinity; + + return this; + + }, + + empty: function () { + + // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + + return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); + + }, + + center: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector2(); + return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); + + }, + + size: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector2(); + return result.subVectors( this.max, this.min ); + + }, + + expandByPoint: function ( point ) { + + this.min.min( point ); + this.max.max( point ); + + return this; + + }, + + expandByVector: function ( vector ) { + + this.min.sub( vector ); + this.max.add( vector ); + + return this; + + }, + + expandByScalar: function ( scalar ) { + + this.min.addScalar( - scalar ); + this.max.addScalar( scalar ); + + return this; + + }, + + containsPoint: function ( point ) { + + if ( point.x < this.min.x || point.x > this.max.x || + point.y < this.min.y || point.y > this.max.y ) { + + return false; + + } + + return true; + + }, + + containsBox: function ( box ) { + + if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) && + ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) ) { + + return true; + + } + + return false; + + }, + + getParameter: function ( point, optionalTarget ) { + + // This can potentially have a divide by zero if the box + // has a size dimension of 0. + + var result = optionalTarget || new THREE.Vector2(); + + return result.set( + ( point.x - this.min.x ) / ( this.max.x - this.min.x ), + ( point.y - this.min.y ) / ( this.max.y - this.min.y ) + ); + + }, + + isIntersectionBox: function ( box ) { + + // using 6 splitting planes to rule out intersections. + + if ( box.max.x < this.min.x || box.min.x > this.max.x || + box.max.y < this.min.y || box.min.y > this.max.y ) { + + return false; + + } + + return true; + + }, + + clampPoint: function ( point, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector2(); + return result.copy( point ).clamp( this.min, this.max ); + + }, + + distanceToPoint: function () { + + var v1 = new THREE.Vector2(); + + return function ( point ) { + + var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); + return clampedPoint.sub( point ).length(); + + }; + + }(), + + intersect: function ( box ) { + + this.min.max( box.min ); + this.max.min( box.max ); + + return this; + + }, + + union: function ( box ) { + + this.min.min( box.min ); + this.max.max( box.max ); + + return this; + + }, + + translate: function ( offset ) { + + this.min.add( offset ); + this.max.add( offset ); + + return this; + + }, + + equals: function ( box ) { + + return box.min.equals( this.min ) && box.max.equals( this.max ); + + } + +}; + +// File:src/math/Box3.js + +/** + * @author bhouston / http://clara.io + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Box3 = function ( min, max ) { + + this.min = ( min !== undefined ) ? min : new THREE.Vector3( Infinity, Infinity, Infinity ); + this.max = ( max !== undefined ) ? max : new THREE.Vector3( - Infinity, - Infinity, - Infinity ); + +}; + +THREE.Box3.prototype = { + + constructor: THREE.Box3, + + set: function ( min, max ) { + + this.min.copy( min ); + this.max.copy( max ); + + return this; + + }, + + setFromPoints: function ( points ) { + + this.makeEmpty(); + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + this.expandByPoint( points[ i ] ); + + } + + return this; + + }, + + setFromCenterAndSize: function () { + + var v1 = new THREE.Vector3(); + + return function ( center, size ) { + + var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); + + this.min.copy( center ).sub( halfSize ); + this.max.copy( center ).add( halfSize ); + + return this; + + }; + + }(), + + setFromObject: function () { + + // Computes the world-axis-aligned bounding box of an object (including its children), + // accounting for both the object's, and children's, world transforms + + var v1 = new THREE.Vector3(); + + return function ( object ) { + + var scope = this; + + object.updateMatrixWorld( true ); + + this.makeEmpty(); + + object.traverse( function ( node ) { + + var geometry = node.geometry; + + if ( geometry !== undefined ) { + + if ( geometry instanceof THREE.Geometry ) { + + var vertices = geometry.vertices; + + for ( var i = 0, il = vertices.length; i < il; i ++ ) { + + v1.copy( vertices[ i ] ); + + v1.applyMatrix4( node.matrixWorld ); + + scope.expandByPoint( v1 ); + + } + + } else if ( geometry instanceof THREE.BufferGeometry && geometry.attributes[ 'position' ] !== undefined ) { + + var positions = geometry.attributes[ 'position' ].array; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + v1.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + + v1.applyMatrix4( node.matrixWorld ); + + scope.expandByPoint( v1 ); + + } + + } + + } + + } ); + + return this; + + }; + + }(), + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( box ) { + + this.min.copy( box.min ); + this.max.copy( box.max ); + + return this; + + }, + + makeEmpty: function () { + + this.min.x = this.min.y = this.min.z = Infinity; + this.max.x = this.max.y = this.max.z = - Infinity; + + return this; + + }, + + empty: function () { + + // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + + return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); + + }, + + center: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); + + }, + + size: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.subVectors( this.max, this.min ); + + }, + + expandByPoint: function ( point ) { + + this.min.min( point ); + this.max.max( point ); + + return this; + + }, + + expandByVector: function ( vector ) { + + this.min.sub( vector ); + this.max.add( vector ); + + return this; + + }, + + expandByScalar: function ( scalar ) { + + this.min.addScalar( - scalar ); + this.max.addScalar( scalar ); + + return this; + + }, + + containsPoint: function ( point ) { + + if ( point.x < this.min.x || point.x > this.max.x || + point.y < this.min.y || point.y > this.max.y || + point.z < this.min.z || point.z > this.max.z ) { + + return false; + + } + + return true; + + }, + + containsBox: function ( box ) { + + if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) && + ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) && + ( this.min.z <= box.min.z ) && ( box.max.z <= this.max.z ) ) { + + return true; + + } + + return false; + + }, + + getParameter: function ( point, optionalTarget ) { + + // This can potentially have a divide by zero if the box + // has a size dimension of 0. + + var result = optionalTarget || new THREE.Vector3(); + + return result.set( + ( point.x - this.min.x ) / ( this.max.x - this.min.x ), + ( point.y - this.min.y ) / ( this.max.y - this.min.y ), + ( point.z - this.min.z ) / ( this.max.z - this.min.z ) + ); + + }, + + isIntersectionBox: function ( box ) { + + // using 6 splitting planes to rule out intersections. + + if ( box.max.x < this.min.x || box.min.x > this.max.x || + box.max.y < this.min.y || box.min.y > this.max.y || + box.max.z < this.min.z || box.min.z > this.max.z ) { + + return false; + + } + + return true; + + }, + + clampPoint: function ( point, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.copy( point ).clamp( this.min, this.max ); + + }, + + distanceToPoint: function () { + + var v1 = new THREE.Vector3(); + + return function ( point ) { + + var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); + return clampedPoint.sub( point ).length(); + + }; + + }(), + + getBoundingSphere: function () { + + var v1 = new THREE.Vector3(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Sphere(); + + result.center = this.center(); + result.radius = this.size( v1 ).length() * 0.5; + + return result; + + }; + + }(), + + intersect: function ( box ) { + + this.min.max( box.min ); + this.max.min( box.max ); + + return this; + + }, + + union: function ( box ) { + + this.min.min( box.min ); + this.max.max( box.max ); + + return this; + + }, + + applyMatrix4: function () { + + var points = [ + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3() + ]; + + return function ( matrix ) { + + // NOTE: I am using a binary pattern to specify all 2^3 combinations below + points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 + points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 + points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 + points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 + points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 + points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 + points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 + points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 + + this.makeEmpty(); + this.setFromPoints( points ); + + return this; + + }; + + }(), + + translate: function ( offset ) { + + this.min.add( offset ); + this.max.add( offset ); + + return this; + + }, + + equals: function ( box ) { + + return box.min.equals( this.min ) && box.max.equals( this.max ); + + } + +}; + +// File:src/math/Matrix3.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://clara.io + */ + +THREE.Matrix3 = function () { + + this.elements = new Float32Array( [ + + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + + ] ); + + if ( arguments.length > 0 ) { + + console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); + + } + +}; + +THREE.Matrix3.prototype = { + + constructor: THREE.Matrix3, + + set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { + + var te = this.elements; + + te[ 0 ] = n11; te[ 3 ] = n12; te[ 6 ] = n13; + te[ 1 ] = n21; te[ 4 ] = n22; te[ 7 ] = n23; + te[ 2 ] = n31; te[ 5 ] = n32; te[ 8 ] = n33; + + return this; + + }, + + identity: function () { + + this.set( + + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + + ); + + return this; + + }, + + clone: function () { + + return new this.constructor().fromArray( this.elements ); + + }, + + copy: function ( m ) { + + var me = m.elements; + + this.set( + + me[ 0 ], me[ 3 ], me[ 6 ], + me[ 1 ], me[ 4 ], me[ 7 ], + me[ 2 ], me[ 5 ], me[ 8 ] + + ); + + return this; + + }, + + multiplyVector3: function ( vector ) { + + console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); + return vector.applyMatrix3( this ); + + }, + + multiplyVector3Array: function ( a ) { + + console.warn( 'THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); + return this.applyToVector3Array( a ); + + }, + + applyToVector3Array: function () { + + var v1; + + return function ( array, offset, length ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + if ( offset === undefined ) offset = 0; + if ( length === undefined ) length = array.length; + + for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) { + + v1.fromArray( array, j ); + v1.applyMatrix3( this ); + v1.toArray( array, j ); + + } + + return array; + + }; + + }(), + + applyToBuffer: function () { + + var v1; + + return function applyToBuffer( buffer, offset, length ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + if ( offset === undefined ) offset = 0; + if ( length === undefined ) length = buffer.length / buffer.itemSize; + + for ( var i = 0, j = offset; i < length; i ++, j ++ ) { + + v1.x = buffer.getX( j ); + v1.y = buffer.getY( j ); + v1.z = buffer.getZ( j ); + + v1.applyMatrix3( this ); + + buffer.setXYZ( v1.x, v1.y, v1.z ); + + } + + return buffer; + + }; + + }(), + + multiplyScalar: function ( s ) { + + var te = this.elements; + + te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; + te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; + te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; + + return this; + + }, + + determinant: function () { + + var te = this.elements; + + var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], + d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], + g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; + + return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; + + }, + + getInverse: function ( matrix, throwOnInvertible ) { + + // input: THREE.Matrix4 + // ( based on http://code.google.com/p/webgl-mjs/ ) + + var me = matrix.elements; + var te = this.elements; + + te[ 0 ] = me[ 10 ] * me[ 5 ] - me[ 6 ] * me[ 9 ]; + te[ 1 ] = - me[ 10 ] * me[ 1 ] + me[ 2 ] * me[ 9 ]; + te[ 2 ] = me[ 6 ] * me[ 1 ] - me[ 2 ] * me[ 5 ]; + te[ 3 ] = - me[ 10 ] * me[ 4 ] + me[ 6 ] * me[ 8 ]; + te[ 4 ] = me[ 10 ] * me[ 0 ] - me[ 2 ] * me[ 8 ]; + te[ 5 ] = - me[ 6 ] * me[ 0 ] + me[ 2 ] * me[ 4 ]; + te[ 6 ] = me[ 9 ] * me[ 4 ] - me[ 5 ] * me[ 8 ]; + te[ 7 ] = - me[ 9 ] * me[ 0 ] + me[ 1 ] * me[ 8 ]; + te[ 8 ] = me[ 5 ] * me[ 0 ] - me[ 1 ] * me[ 4 ]; + + var det = me[ 0 ] * te[ 0 ] + me[ 1 ] * te[ 3 ] + me[ 2 ] * te[ 6 ]; + + // no inverse + + if ( det === 0 ) { + + var msg = "Matrix3.getInverse(): can't invert matrix, determinant is 0"; + + if ( throwOnInvertible || false ) { + + throw new Error( msg ); + + } else { + + console.warn( msg ); + + } + + this.identity(); + + return this; + + } + + this.multiplyScalar( 1.0 / det ); + + return this; + + }, + + transpose: function () { + + var tmp, m = this.elements; + + tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; + tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; + tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; + + return this; + + }, + + flattenToArrayOffset: function ( array, offset ) { + + var te = this.elements; + + array[ offset ] = te[ 0 ]; + array[ offset + 1 ] = te[ 1 ]; + array[ offset + 2 ] = te[ 2 ]; + + array[ offset + 3 ] = te[ 3 ]; + array[ offset + 4 ] = te[ 4 ]; + array[ offset + 5 ] = te[ 5 ]; + + array[ offset + 6 ] = te[ 6 ]; + array[ offset + 7 ] = te[ 7 ]; + array[ offset + 8 ] = te[ 8 ]; + + return array; + + }, + + getNormalMatrix: function ( m ) { + + // input: THREE.Matrix4 + + this.getInverse( m ).transpose(); + + return this; + + }, + + transposeIntoArray: function ( r ) { + + var m = this.elements; + + r[ 0 ] = m[ 0 ]; + r[ 1 ] = m[ 3 ]; + r[ 2 ] = m[ 6 ]; + r[ 3 ] = m[ 1 ]; + r[ 4 ] = m[ 4 ]; + r[ 5 ] = m[ 7 ]; + r[ 6 ] = m[ 2 ]; + r[ 7 ] = m[ 5 ]; + r[ 8 ] = m[ 8 ]; + + return this; + + }, + + fromArray: function ( array ) { + + this.elements.set( array ); + + return this; + + }, + + toArray: function () { + + var te = this.elements; + + return [ + te[ 0 ], te[ 1 ], te[ 2 ], + te[ 3 ], te[ 4 ], te[ 5 ], + te[ 6 ], te[ 7 ], te[ 8 ] + ]; + + } + +}; + +// File:src/math/Matrix4.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author philogb / http://blog.thejit.org/ + * @author jordi_ros / http://plattsoft.com + * @author D1plo1d / http://github.com/D1plo1d + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author timknip / http://www.floorplanner.com/ + * @author bhouston / http://clara.io + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Matrix4 = function () { + + this.elements = new Float32Array( [ + + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ] ); + + if ( arguments.length > 0 ) { + + console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); + + } + +}; + +THREE.Matrix4.prototype = { + + constructor: THREE.Matrix4, + + set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { + + var te = this.elements; + + te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; + te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; + te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; + te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; + + return this; + + }, + + identity: function () { + + this.set( + + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + clone: function () { + + return new THREE.Matrix4().fromArray( this.elements ); + + }, + + copy: function ( m ) { + + this.elements.set( m.elements ); + + return this; + + }, + + extractPosition: function ( m ) { + + console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); + return this.copyPosition( m ); + + }, + + copyPosition: function ( m ) { + + var te = this.elements; + var me = m.elements; + + te[ 12 ] = me[ 12 ]; + te[ 13 ] = me[ 13 ]; + te[ 14 ] = me[ 14 ]; + + return this; + + }, + + extractBasis: function ( xAxis, yAxis, zAxis ) { + + var te = this.elements; + + xAxis.set( te[ 0 ], te[ 1 ], te[ 2 ] ); + yAxis.set( te[ 4 ], te[ 5 ], te[ 6 ] ); + zAxis.set( te[ 8 ], te[ 9 ], te[ 10 ] ); + + return this; + + }, + + makeBasis: function ( xAxis, yAxis, zAxis ) { + + this.set( + xAxis.x, yAxis.x, zAxis.x, 0, + xAxis.y, yAxis.y, zAxis.y, 0, + xAxis.z, yAxis.z, zAxis.z, 0, + 0, 0, 0, 1 + ); + + return this; + + }, + + extractRotation: function () { + + var v1; + + return function ( m ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + var te = this.elements; + var me = m.elements; + + var scaleX = 1 / v1.set( me[ 0 ], me[ 1 ], me[ 2 ] ).length(); + var scaleY = 1 / v1.set( me[ 4 ], me[ 5 ], me[ 6 ] ).length(); + var scaleZ = 1 / v1.set( me[ 8 ], me[ 9 ], me[ 10 ] ).length(); + + te[ 0 ] = me[ 0 ] * scaleX; + te[ 1 ] = me[ 1 ] * scaleX; + te[ 2 ] = me[ 2 ] * scaleX; + + te[ 4 ] = me[ 4 ] * scaleY; + te[ 5 ] = me[ 5 ] * scaleY; + te[ 6 ] = me[ 6 ] * scaleY; + + te[ 8 ] = me[ 8 ] * scaleZ; + te[ 9 ] = me[ 9 ] * scaleZ; + te[ 10 ] = me[ 10 ] * scaleZ; + + return this; + + }; + + }(), + + makeRotationFromEuler: function ( euler ) { + + if ( euler instanceof THREE.Euler === false ) { + + console.error( 'THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + + } + + var te = this.elements; + + var x = euler.x, y = euler.y, z = euler.z; + var a = Math.cos( x ), b = Math.sin( x ); + var c = Math.cos( y ), d = Math.sin( y ); + var e = Math.cos( z ), f = Math.sin( z ); + + if ( euler.order === 'XYZ' ) { + + var ae = a * e, af = a * f, be = b * e, bf = b * f; + + te[ 0 ] = c * e; + te[ 4 ] = - c * f; + te[ 8 ] = d; + + te[ 1 ] = af + be * d; + te[ 5 ] = ae - bf * d; + te[ 9 ] = - b * c; + + te[ 2 ] = bf - ae * d; + te[ 6 ] = be + af * d; + te[ 10 ] = a * c; + + } else if ( euler.order === 'YXZ' ) { + + var ce = c * e, cf = c * f, de = d * e, df = d * f; + + te[ 0 ] = ce + df * b; + te[ 4 ] = de * b - cf; + te[ 8 ] = a * d; + + te[ 1 ] = a * f; + te[ 5 ] = a * e; + te[ 9 ] = - b; + + te[ 2 ] = cf * b - de; + te[ 6 ] = df + ce * b; + te[ 10 ] = a * c; + + } else if ( euler.order === 'ZXY' ) { + + var ce = c * e, cf = c * f, de = d * e, df = d * f; + + te[ 0 ] = ce - df * b; + te[ 4 ] = - a * f; + te[ 8 ] = de + cf * b; + + te[ 1 ] = cf + de * b; + te[ 5 ] = a * e; + te[ 9 ] = df - ce * b; + + te[ 2 ] = - a * d; + te[ 6 ] = b; + te[ 10 ] = a * c; + + } else if ( euler.order === 'ZYX' ) { + + var ae = a * e, af = a * f, be = b * e, bf = b * f; + + te[ 0 ] = c * e; + te[ 4 ] = be * d - af; + te[ 8 ] = ae * d + bf; + + te[ 1 ] = c * f; + te[ 5 ] = bf * d + ae; + te[ 9 ] = af * d - be; + + te[ 2 ] = - d; + te[ 6 ] = b * c; + te[ 10 ] = a * c; + + } else if ( euler.order === 'YZX' ) { + + var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + + te[ 0 ] = c * e; + te[ 4 ] = bd - ac * f; + te[ 8 ] = bc * f + ad; + + te[ 1 ] = f; + te[ 5 ] = a * e; + te[ 9 ] = - b * e; + + te[ 2 ] = - d * e; + te[ 6 ] = ad * f + bc; + te[ 10 ] = ac - bd * f; + + } else if ( euler.order === 'XZY' ) { + + var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + + te[ 0 ] = c * e; + te[ 4 ] = - f; + te[ 8 ] = d * e; + + te[ 1 ] = ac * f + bd; + te[ 5 ] = a * e; + te[ 9 ] = ad * f - bc; + + te[ 2 ] = bc * f - ad; + te[ 6 ] = b * e; + te[ 10 ] = bd * f + ac; + + } + + // last column + te[ 3 ] = 0; + te[ 7 ] = 0; + te[ 11 ] = 0; + + // bottom row + te[ 12 ] = 0; + te[ 13 ] = 0; + te[ 14 ] = 0; + te[ 15 ] = 1; + + return this; + + }, + + setRotationFromQuaternion: function ( q ) { + + console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); + + return this.makeRotationFromQuaternion( q ); + + }, + + makeRotationFromQuaternion: function ( q ) { + + var te = this.elements; + + var x = q.x, y = q.y, z = q.z, w = q.w; + var x2 = x + x, y2 = y + y, z2 = z + z; + var xx = x * x2, xy = x * y2, xz = x * z2; + var yy = y * y2, yz = y * z2, zz = z * z2; + var wx = w * x2, wy = w * y2, wz = w * z2; + + te[ 0 ] = 1 - ( yy + zz ); + te[ 4 ] = xy - wz; + te[ 8 ] = xz + wy; + + te[ 1 ] = xy + wz; + te[ 5 ] = 1 - ( xx + zz ); + te[ 9 ] = yz - wx; + + te[ 2 ] = xz - wy; + te[ 6 ] = yz + wx; + te[ 10 ] = 1 - ( xx + yy ); + + // last column + te[ 3 ] = 0; + te[ 7 ] = 0; + te[ 11 ] = 0; + + // bottom row + te[ 12 ] = 0; + te[ 13 ] = 0; + te[ 14 ] = 0; + te[ 15 ] = 1; + + return this; + + }, + + lookAt: function () { + + var x, y, z; + + return function ( eye, target, up ) { + + if ( x === undefined ) x = new THREE.Vector3(); + if ( y === undefined ) y = new THREE.Vector3(); + if ( z === undefined ) z = new THREE.Vector3(); + + var te = this.elements; + + z.subVectors( eye, target ).normalize(); + + if ( z.lengthSq() === 0 ) { + + z.z = 1; + + } + + x.crossVectors( up, z ).normalize(); + + if ( x.lengthSq() === 0 ) { + + z.x += 0.0001; + x.crossVectors( up, z ).normalize(); + + } + + y.crossVectors( z, x ); + + + te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x; + te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y; + te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z; + + return this; + + }; + + }(), + + multiply: function ( m, n ) { + + if ( n !== undefined ) { + + console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); + return this.multiplyMatrices( m, n ); + + } + + return this.multiplyMatrices( this, m ); + + }, + + multiplyMatrices: function ( a, b ) { + + var ae = a.elements; + var be = b.elements; + var te = this.elements; + + var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; + var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; + var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; + var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; + + var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; + var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; + var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; + var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; + + te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; + te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; + te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; + te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; + + te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; + te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; + te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; + te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; + + te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; + te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; + te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; + te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; + + te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; + te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; + te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; + te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; + + return this; + + }, + + multiplyToArray: function ( a, b, r ) { + + var te = this.elements; + + this.multiplyMatrices( a, b ); + + r[ 0 ] = te[ 0 ]; r[ 1 ] = te[ 1 ]; r[ 2 ] = te[ 2 ]; r[ 3 ] = te[ 3 ]; + r[ 4 ] = te[ 4 ]; r[ 5 ] = te[ 5 ]; r[ 6 ] = te[ 6 ]; r[ 7 ] = te[ 7 ]; + r[ 8 ] = te[ 8 ]; r[ 9 ] = te[ 9 ]; r[ 10 ] = te[ 10 ]; r[ 11 ] = te[ 11 ]; + r[ 12 ] = te[ 12 ]; r[ 13 ] = te[ 13 ]; r[ 14 ] = te[ 14 ]; r[ 15 ] = te[ 15 ]; + + return this; + + }, + + multiplyScalar: function ( s ) { + + var te = this.elements; + + te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; + te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; + te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; + te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; + + return this; + + }, + + multiplyVector3: function ( vector ) { + + console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.' ); + return vector.applyProjection( this ); + + }, + + multiplyVector4: function ( vector ) { + + console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); + + }, + + multiplyVector3Array: function ( a ) { + + console.warn( 'THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); + return this.applyToVector3Array( a ); + + }, + + applyToVector3Array: function () { + + var v1; + + return function ( array, offset, length ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + if ( offset === undefined ) offset = 0; + if ( length === undefined ) length = array.length; + + for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) { + + v1.fromArray( array, j ); + v1.applyMatrix4( this ); + v1.toArray( array, j ); + + } + + return array; + + }; + + }(), + + applyToBuffer: function () { + + var v1; + + return function applyToBuffer( buffer, offset, length ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + if ( offset === undefined ) offset = 0; + if ( length === undefined ) length = buffer.length / buffer.itemSize; + + for ( var i = 0, j = offset; i < length; i ++, j ++ ) { + + v1.x = buffer.getX( j ); + v1.y = buffer.getY( j ); + v1.z = buffer.getZ( j ); + + v1.applyMatrix4( this ); + + buffer.setXYZ( v1.x, v1.y, v1.z ); + + } + + return buffer; + + }; + + }(), + + rotateAxis: function ( v ) { + + console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); + + v.transformDirection( this ); + + }, + + crossVector: function ( vector ) { + + console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); + + }, + + determinant: function () { + + var te = this.elements; + + var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; + var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; + var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; + var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; + + //TODO: make this more efficient + //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) + + return ( + n41 * ( + + n14 * n23 * n32 + - n13 * n24 * n32 + - n14 * n22 * n33 + + n12 * n24 * n33 + + n13 * n22 * n34 + - n12 * n23 * n34 + ) + + n42 * ( + + n11 * n23 * n34 + - n11 * n24 * n33 + + n14 * n21 * n33 + - n13 * n21 * n34 + + n13 * n24 * n31 + - n14 * n23 * n31 + ) + + n43 * ( + + n11 * n24 * n32 + - n11 * n22 * n34 + - n14 * n21 * n32 + + n12 * n21 * n34 + + n14 * n22 * n31 + - n12 * n24 * n31 + ) + + n44 * ( + - n13 * n22 * n31 + - n11 * n23 * n32 + + n11 * n22 * n33 + + n13 * n21 * n32 + - n12 * n21 * n33 + + n12 * n23 * n31 + ) + + ); + + }, + + transpose: function () { + + var te = this.elements; + var tmp; + + tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; + tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; + tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; + + tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; + tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; + tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; + + return this; + + }, + + flattenToArrayOffset: function ( array, offset ) { + + var te = this.elements; + + array[ offset ] = te[ 0 ]; + array[ offset + 1 ] = te[ 1 ]; + array[ offset + 2 ] = te[ 2 ]; + array[ offset + 3 ] = te[ 3 ]; + + array[ offset + 4 ] = te[ 4 ]; + array[ offset + 5 ] = te[ 5 ]; + array[ offset + 6 ] = te[ 6 ]; + array[ offset + 7 ] = te[ 7 ]; + + array[ offset + 8 ] = te[ 8 ]; + array[ offset + 9 ] = te[ 9 ]; + array[ offset + 10 ] = te[ 10 ]; + array[ offset + 11 ] = te[ 11 ]; + + array[ offset + 12 ] = te[ 12 ]; + array[ offset + 13 ] = te[ 13 ]; + array[ offset + 14 ] = te[ 14 ]; + array[ offset + 15 ] = te[ 15 ]; + + return array; + + }, + + getPosition: function () { + + var v1; + + return function () { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); + + var te = this.elements; + return v1.set( te[ 12 ], te[ 13 ], te[ 14 ] ); + + }; + + }(), + + setPosition: function ( v ) { + + var te = this.elements; + + te[ 12 ] = v.x; + te[ 13 ] = v.y; + te[ 14 ] = v.z; + + return this; + + }, + + getInverse: function ( m, throwOnInvertible ) { + + // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm + var te = this.elements; + var me = m.elements; + + var n11 = me[ 0 ], n12 = me[ 4 ], n13 = me[ 8 ], n14 = me[ 12 ]; + var n21 = me[ 1 ], n22 = me[ 5 ], n23 = me[ 9 ], n24 = me[ 13 ]; + var n31 = me[ 2 ], n32 = me[ 6 ], n33 = me[ 10 ], n34 = me[ 14 ]; + var n41 = me[ 3 ], n42 = me[ 7 ], n43 = me[ 11 ], n44 = me[ 15 ]; + + te[ 0 ] = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44; + te[ 4 ] = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44; + te[ 8 ] = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44; + te[ 12 ] = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; + te[ 1 ] = n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44; + te[ 5 ] = n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44; + te[ 9 ] = n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44; + te[ 13 ] = n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34; + te[ 2 ] = n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44; + te[ 6 ] = n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44; + te[ 10 ] = n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44; + te[ 14 ] = n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34; + te[ 3 ] = n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43; + te[ 7 ] = n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43; + te[ 11 ] = n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43; + te[ 15 ] = n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33; + + var det = n11 * te[ 0 ] + n21 * te[ 4 ] + n31 * te[ 8 ] + n41 * te[ 12 ]; + + if ( det === 0 ) { + + var msg = "THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0"; + + if ( throwOnInvertible || false ) { + + throw new Error( msg ); + + } else { + + console.warn( msg ); + + } + + this.identity(); + + return this; + + } + + this.multiplyScalar( 1 / det ); + + return this; + + }, + + translate: function ( v ) { + + console.error( 'THREE.Matrix4: .translate() has been removed.' ); + + }, + + rotateX: function ( angle ) { + + console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); + + }, + + rotateY: function ( angle ) { + + console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); + + }, + + rotateZ: function ( angle ) { + + console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); + + }, + + rotateByAxis: function ( axis, angle ) { + + console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); + + }, + + scale: function ( v ) { + + var te = this.elements; + var x = v.x, y = v.y, z = v.z; + + te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; + te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; + te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; + te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; + + return this; + + }, + + getMaxScaleOnAxis: function () { + + var te = this.elements; + + var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; + var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; + var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; + + return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); + + }, + + makeTranslation: function ( x, y, z ) { + + this.set( + + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationX: function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + 1, 0, 0, 0, + 0, c, - s, 0, + 0, s, c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationY: function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + c, 0, s, 0, + 0, 1, 0, 0, + - s, 0, c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationZ: function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + c, - s, 0, 0, + s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationAxis: function ( axis, angle ) { + + // Based on http://www.gamedev.net/reference/articles/article1199.asp + + var c = Math.cos( angle ); + var s = Math.sin( angle ); + var t = 1 - c; + var x = axis.x, y = axis.y, z = axis.z; + var tx = t * x, ty = t * y; + + this.set( + + tx * x + c, tx * y - s * z, tx * z + s * y, 0, + tx * y + s * z, ty * y + c, ty * z - s * x, 0, + tx * z - s * y, ty * z + s * x, t * z * z + c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeScale: function ( x, y, z ) { + + this.set( + + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + compose: function ( position, quaternion, scale ) { + + this.makeRotationFromQuaternion( quaternion ); + this.scale( scale ); + this.setPosition( position ); + + return this; + + }, + + decompose: function () { + + var vector, matrix; + + return function ( position, quaternion, scale ) { + + if ( vector === undefined ) vector = new THREE.Vector3(); + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + + var te = this.elements; + + var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); + var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); + var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); + + // if determine is negative, we need to invert one scale + var det = this.determinant(); + if ( det < 0 ) { + + sx = - sx; + + } + + position.x = te[ 12 ]; + position.y = te[ 13 ]; + position.z = te[ 14 ]; + + // scale the rotation part + + matrix.elements.set( this.elements ); // at this point matrix is incomplete so we can't use .copy() + + var invSX = 1 / sx; + var invSY = 1 / sy; + var invSZ = 1 / sz; + + matrix.elements[ 0 ] *= invSX; + matrix.elements[ 1 ] *= invSX; + matrix.elements[ 2 ] *= invSX; + + matrix.elements[ 4 ] *= invSY; + matrix.elements[ 5 ] *= invSY; + matrix.elements[ 6 ] *= invSY; + + matrix.elements[ 8 ] *= invSZ; + matrix.elements[ 9 ] *= invSZ; + matrix.elements[ 10 ] *= invSZ; + + quaternion.setFromRotationMatrix( matrix ); + + scale.x = sx; + scale.y = sy; + scale.z = sz; + + return this; + + }; + + }(), + + makeFrustum: function ( left, right, bottom, top, near, far ) { + + var te = this.elements; + var x = 2 * near / ( right - left ); + var y = 2 * near / ( top - bottom ); + + var a = ( right + left ) / ( right - left ); + var b = ( top + bottom ) / ( top - bottom ); + var c = - ( far + near ) / ( far - near ); + var d = - 2 * far * near / ( far - near ); + + te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; + te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; + te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; + te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; + + return this; + + }, + + makePerspective: function ( fov, aspect, near, far ) { + + var ymax = near * Math.tan( THREE.Math.degToRad( fov * 0.5 ) ); + var ymin = - ymax; + var xmin = ymin * aspect; + var xmax = ymax * aspect; + + return this.makeFrustum( xmin, xmax, ymin, ymax, near, far ); + + }, + + makeOrthographic: function ( left, right, top, bottom, near, far ) { + + var te = this.elements; + var w = right - left; + var h = top - bottom; + var p = far - near; + + var x = ( right + left ) / w; + var y = ( top + bottom ) / h; + var z = ( far + near ) / p; + + te[ 0 ] = 2 / w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; + te[ 1 ] = 0; te[ 5 ] = 2 / h; te[ 9 ] = 0; te[ 13 ] = - y; + te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 / p; te[ 14 ] = - z; + te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; + + return this; + + }, + + equals: function ( matrix ) { + + var te = this.elements; + var me = matrix.elements; + + for ( var i = 0; i < 16; i ++ ) { + + if ( te[ i ] !== me[ i ] ) return false; + + } + + return true; + + }, + + fromArray: function ( array ) { + + this.elements.set( array ); + + return this; + + }, + + toArray: function () { + + var te = this.elements; + + return [ + te[ 0 ], te[ 1 ], te[ 2 ], te[ 3 ], + te[ 4 ], te[ 5 ], te[ 6 ], te[ 7 ], + te[ 8 ], te[ 9 ], te[ 10 ], te[ 11 ], + te[ 12 ], te[ 13 ], te[ 14 ], te[ 15 ] + ]; + + } + +}; + +// File:src/math/Ray.js + +/** + * @author bhouston / http://clara.io + */ + +THREE.Ray = function ( origin, direction ) { + + this.origin = ( origin !== undefined ) ? origin : new THREE.Vector3(); + this.direction = ( direction !== undefined ) ? direction : new THREE.Vector3(); + +}; + +THREE.Ray.prototype = { + + constructor: THREE.Ray, + + set: function ( origin, direction ) { + + this.origin.copy( origin ); + this.direction.copy( direction ); + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( ray ) { + + this.origin.copy( ray.origin ); + this.direction.copy( ray.direction ); + + return this; + + }, + + at: function ( t, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + return result.copy( this.direction ).multiplyScalar( t ).add( this.origin ); + + }, + + recast: function () { + + var v1 = new THREE.Vector3(); + + return function ( t ) { + + this.origin.copy( this.at( t, v1 ) ); + + return this; + + }; + + }(), + + closestPointToPoint: function ( point, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + result.subVectors( point, this.origin ); + var directionDistance = result.dot( this.direction ); + + if ( directionDistance < 0 ) { + + return result.copy( this.origin ); + + } + + return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + + }, + + distanceToPoint: function ( point ) { + + return Math.sqrt( this.distanceSqToPoint( point ) ); + + }, + + distanceSqToPoint: function () { + + var v1 = new THREE.Vector3(); + + return function ( point ) { + + var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction ); + + // point behind the ray + + if ( directionDistance < 0 ) { + + return this.origin.distanceToSquared( point ); + + } + + v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + + return v1.distanceToSquared( point ); + + }; + + }(), + + distanceSqToSegment: function () { + + var segCenter = new THREE.Vector3(); + var segDir = new THREE.Vector3(); + var diff = new THREE.Vector3(); + + return function ( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { + + // from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp + // It returns the min distance between the ray and the segment + // defined by v0 and v1 + // It can also set two optional targets : + // - The closest point on the ray + // - The closest point on the segment + + segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); + segDir.copy( v1 ).sub( v0 ).normalize(); + diff.copy( this.origin ).sub( segCenter ); + + var segExtent = v0.distanceTo( v1 ) * 0.5; + var a01 = - this.direction.dot( segDir ); + var b0 = diff.dot( this.direction ); + var b1 = - diff.dot( segDir ); + var c = diff.lengthSq(); + var det = Math.abs( 1 - a01 * a01 ); + var s0, s1, sqrDist, extDet; + + if ( det > 0 ) { + + // The ray and segment are not parallel. + + s0 = a01 * b1 - b0; + s1 = a01 * b0 - b1; + extDet = segExtent * det; + + if ( s0 >= 0 ) { + + if ( s1 >= - extDet ) { + + if ( s1 <= extDet ) { + + // region 0 + // Minimum at interior points of ray and segment. + + var invDet = 1 / det; + s0 *= invDet; + s1 *= invDet; + sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; + + } else { + + // region 1 + + s1 = segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + } else { + + // region 5 + + s1 = - segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + } else { + + if ( s1 <= - extDet ) { + + // region 4 + + s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } else if ( s1 <= extDet ) { + + // region 3 + + s0 = 0; + s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = s1 * ( s1 + 2 * b1 ) + c; + + } else { + + // region 2 + + s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + } + + } else { + + // Ray and segment are parallel. + + s1 = ( a01 > 0 ) ? - segExtent : segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + if ( optionalPointOnRay ) { + + optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); + + } + + if ( optionalPointOnSegment ) { + + optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter ); + + } + + return sqrDist; + + }; + + }(), + + + isIntersectionSphere: function ( sphere ) { + + return this.distanceToPoint( sphere.center ) <= sphere.radius; + + }, + + intersectSphere: function () { + + // from http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-sphere-intersection/ + + var v1 = new THREE.Vector3(); + + return function ( sphere, optionalTarget ) { + + v1.subVectors( sphere.center, this.origin ); + + var tca = v1.dot( this.direction ); + + var d2 = v1.dot( v1 ) - tca * tca; + + var radius2 = sphere.radius * sphere.radius; + + if ( d2 > radius2 ) return null; + + var thc = Math.sqrt( radius2 - d2 ); + + // t0 = first intersect point - entrance on front of sphere + var t0 = tca - thc; + + // t1 = second intersect point - exit point on back of sphere + var t1 = tca + thc; + + // test to see if both t0 and t1 are behind the ray - if so, return null + if ( t0 < 0 && t1 < 0 ) return null; + + // test to see if t0 is behind the ray: + // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, + // in order to always return an intersect point that is in front of the ray. + if ( t0 < 0 ) return this.at( t1, optionalTarget ); + + // else t0 is in front of the ray, so return the first collision point scaled by t0 + return this.at( t0, optionalTarget ); + + } + + }(), + + isIntersectionPlane: function ( plane ) { + + // check if the ray lies on the plane first + + var distToPoint = plane.distanceToPoint( this.origin ); + + if ( distToPoint === 0 ) { + + return true; + + } + + var denominator = plane.normal.dot( this.direction ); + + if ( denominator * distToPoint < 0 ) { + + return true; + + } + + // ray origin is behind the plane (and is pointing behind it) + + return false; + + }, + + distanceToPlane: function ( plane ) { + + var denominator = plane.normal.dot( this.direction ); + if ( denominator === 0 ) { + + // line is coplanar, return origin + if ( plane.distanceToPoint( this.origin ) === 0 ) { + + return 0; + + } + + // Null is preferable to undefined since undefined means.... it is undefined + + return null; + + } + + var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; + + // Return if the ray never intersects the plane + + return t >= 0 ? t : null; + + }, + + intersectPlane: function ( plane, optionalTarget ) { + + var t = this.distanceToPlane( plane ); + + if ( t === null ) { + + return null; + + } + + return this.at( t, optionalTarget ); + + }, + + isIntersectionBox: function () { + + var v = new THREE.Vector3(); + + return function ( box ) { + + return this.intersectBox( box, v ) !== null; + + }; + + }(), + + intersectBox: function ( box, optionalTarget ) { + + // http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/ + + var tmin, tmax, tymin, tymax, tzmin, tzmax; + + var invdirx = 1 / this.direction.x, + invdiry = 1 / this.direction.y, + invdirz = 1 / this.direction.z; + + var origin = this.origin; + + if ( invdirx >= 0 ) { + + tmin = ( box.min.x - origin.x ) * invdirx; + tmax = ( box.max.x - origin.x ) * invdirx; + + } else { + + tmin = ( box.max.x - origin.x ) * invdirx; + tmax = ( box.min.x - origin.x ) * invdirx; + + } + + if ( invdiry >= 0 ) { + + tymin = ( box.min.y - origin.y ) * invdiry; + tymax = ( box.max.y - origin.y ) * invdiry; + + } else { + + tymin = ( box.max.y - origin.y ) * invdiry; + tymax = ( box.min.y - origin.y ) * invdiry; + + } + + if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; + + // These lines also handle the case where tmin or tmax is NaN + // (result of 0 * Infinity). x !== x returns true if x is NaN + + if ( tymin > tmin || tmin !== tmin ) tmin = tymin; + + if ( tymax < tmax || tmax !== tmax ) tmax = tymax; + + if ( invdirz >= 0 ) { + + tzmin = ( box.min.z - origin.z ) * invdirz; + tzmax = ( box.max.z - origin.z ) * invdirz; + + } else { + + tzmin = ( box.max.z - origin.z ) * invdirz; + tzmax = ( box.min.z - origin.z ) * invdirz; + + } + + if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; + + if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; + + if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; + + //return point closest to the ray (positive side) + + if ( tmax < 0 ) return null; + + return this.at( tmin >= 0 ? tmin : tmax, optionalTarget ); + + }, + + intersectTriangle: function () { + + // Compute the offset origin, edges, and normal. + var diff = new THREE.Vector3(); + var edge1 = new THREE.Vector3(); + var edge2 = new THREE.Vector3(); + var normal = new THREE.Vector3(); + + return function ( a, b, c, backfaceCulling, optionalTarget ) { + + // from http://www.geometrictools.com/LibMathematics/Intersection/Wm5IntrRay3Triangle3.cpp + + edge1.subVectors( b, a ); + edge2.subVectors( c, a ); + normal.crossVectors( edge1, edge2 ); + + // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, + // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by + // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) + // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) + // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) + var DdN = this.direction.dot( normal ); + var sign; + + if ( DdN > 0 ) { + + if ( backfaceCulling ) return null; + sign = 1; + + } else if ( DdN < 0 ) { + + sign = - 1; + DdN = - DdN; + + } else { + + return null; + + } + + diff.subVectors( this.origin, a ); + var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) ); + + // b1 < 0, no intersection + if ( DdQxE2 < 0 ) { + + return null; + + } + + var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) ); + + // b2 < 0, no intersection + if ( DdE1xQ < 0 ) { + + return null; + + } + + // b1+b2 > 1, no intersection + if ( DdQxE2 + DdE1xQ > DdN ) { + + return null; + + } + + // Line intersects triangle, check if ray does. + var QdN = - sign * diff.dot( normal ); + + // t < 0, no intersection + if ( QdN < 0 ) { + + return null; + + } + + // Ray intersects triangle. + return this.at( QdN / DdN, optionalTarget ); + + }; + + }(), + + applyMatrix4: function ( matrix4 ) { + + this.direction.add( this.origin ).applyMatrix4( matrix4 ); + this.origin.applyMatrix4( matrix4 ); + this.direction.sub( this.origin ); + this.direction.normalize(); + + return this; + + }, + + equals: function ( ray ) { + + return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); + + } + +}; + +// File:src/math/Sphere.js + +/** + * @author bhouston / http://clara.io + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Sphere = function ( center, radius ) { + + this.center = ( center !== undefined ) ? center : new THREE.Vector3(); + this.radius = ( radius !== undefined ) ? radius : 0; + +}; + +THREE.Sphere.prototype = { + + constructor: THREE.Sphere, + + set: function ( center, radius ) { + + this.center.copy( center ); + this.radius = radius; + + return this; + + }, + + setFromPoints: function () { + + var box = new THREE.Box3(); + + return function ( points, optionalCenter ) { + + var center = this.center; + + if ( optionalCenter !== undefined ) { + + center.copy( optionalCenter ); + + } else { + + box.setFromPoints( points ).center( center ); + + } + + var maxRadiusSq = 0; + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); + + } + + this.radius = Math.sqrt( maxRadiusSq ); + + return this; + + }; + + }(), + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( sphere ) { + + this.center.copy( sphere.center ); + this.radius = sphere.radius; + + return this; + + }, + + empty: function () { + + return ( this.radius <= 0 ); + + }, + + containsPoint: function ( point ) { + + return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); + + }, + + distanceToPoint: function ( point ) { + + return ( point.distanceTo( this.center ) - this.radius ); + + }, + + intersectsSphere: function ( sphere ) { + + var radiusSum = this.radius + sphere.radius; + + return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); + + }, + + clampPoint: function ( point, optionalTarget ) { + + var deltaLengthSq = this.center.distanceToSquared( point ); + + var result = optionalTarget || new THREE.Vector3(); + result.copy( point ); + + if ( deltaLengthSq > ( this.radius * this.radius ) ) { + + result.sub( this.center ).normalize(); + result.multiplyScalar( this.radius ).add( this.center ); + + } + + return result; + + }, + + getBoundingBox: function ( optionalTarget ) { + + var box = optionalTarget || new THREE.Box3(); + + box.set( this.center, this.center ); + box.expandByScalar( this.radius ); + + return box; + + }, + + applyMatrix4: function ( matrix ) { + + this.center.applyMatrix4( matrix ); + this.radius = this.radius * matrix.getMaxScaleOnAxis(); + + return this; + + }, + + translate: function ( offset ) { + + this.center.add( offset ); + + return this; + + }, + + equals: function ( sphere ) { + + return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); + + } + +}; + +// File:src/math/Frustum.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author bhouston / http://clara.io + */ + +THREE.Frustum = function ( p0, p1, p2, p3, p4, p5 ) { + + this.planes = [ + + ( p0 !== undefined ) ? p0 : new THREE.Plane(), + ( p1 !== undefined ) ? p1 : new THREE.Plane(), + ( p2 !== undefined ) ? p2 : new THREE.Plane(), + ( p3 !== undefined ) ? p3 : new THREE.Plane(), + ( p4 !== undefined ) ? p4 : new THREE.Plane(), + ( p5 !== undefined ) ? p5 : new THREE.Plane() + + ]; + +}; + +THREE.Frustum.prototype = { + + constructor: THREE.Frustum, + + set: function ( p0, p1, p2, p3, p4, p5 ) { + + var planes = this.planes; + + planes[ 0 ].copy( p0 ); + planes[ 1 ].copy( p1 ); + planes[ 2 ].copy( p2 ); + planes[ 3 ].copy( p3 ); + planes[ 4 ].copy( p4 ); + planes[ 5 ].copy( p5 ); + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( frustum ) { + + var planes = this.planes; + + for ( var i = 0; i < 6; i ++ ) { + + planes[ i ].copy( frustum.planes[ i ] ); + + } + + return this; + + }, + + setFromMatrix: function ( m ) { + + var planes = this.planes; + var me = m.elements; + var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; + var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; + var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; + var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; + + planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); + planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); + planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); + planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); + planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); + planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); + + return this; + + }, + + intersectsObject: function () { + + var sphere = new THREE.Sphere(); + + return function ( object ) { + + var geometry = object.geometry; + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( object.matrixWorld ); + + return this.intersectsSphere( sphere ); + + }; + + }(), + + intersectsSphere: function ( sphere ) { + + var planes = this.planes; + var center = sphere.center; + var negRadius = - sphere.radius; + + for ( var i = 0; i < 6; i ++ ) { + + var distance = planes[ i ].distanceToPoint( center ); + + if ( distance < negRadius ) { + + return false; + + } + + } + + return true; + + }, + + intersectsBox: function () { + + var p1 = new THREE.Vector3(), + p2 = new THREE.Vector3(); + + return function ( box ) { + + var planes = this.planes; + + for ( var i = 0; i < 6 ; i ++ ) { + + var plane = planes[ i ]; + + p1.x = plane.normal.x > 0 ? box.min.x : box.max.x; + p2.x = plane.normal.x > 0 ? box.max.x : box.min.x; + p1.y = plane.normal.y > 0 ? box.min.y : box.max.y; + p2.y = plane.normal.y > 0 ? box.max.y : box.min.y; + p1.z = plane.normal.z > 0 ? box.min.z : box.max.z; + p2.z = plane.normal.z > 0 ? box.max.z : box.min.z; + + var d1 = plane.distanceToPoint( p1 ); + var d2 = plane.distanceToPoint( p2 ); + + // if both outside plane, no intersection + + if ( d1 < 0 && d2 < 0 ) { + + return false; + + } + + } + + return true; + + }; + + }(), + + + containsPoint: function ( point ) { + + var planes = this.planes; + + for ( var i = 0; i < 6; i ++ ) { + + if ( planes[ i ].distanceToPoint( point ) < 0 ) { + + return false; + + } + + } + + return true; + + } + +}; + +// File:src/math/Plane.js + +/** + * @author bhouston / http://clara.io + */ + +THREE.Plane = function ( normal, constant ) { + + this.normal = ( normal !== undefined ) ? normal : new THREE.Vector3( 1, 0, 0 ); + this.constant = ( constant !== undefined ) ? constant : 0; + +}; + +THREE.Plane.prototype = { + + constructor: THREE.Plane, + + set: function ( normal, constant ) { + + this.normal.copy( normal ); + this.constant = constant; + + return this; + + }, + + setComponents: function ( x, y, z, w ) { + + this.normal.set( x, y, z ); + this.constant = w; + + return this; + + }, + + setFromNormalAndCoplanarPoint: function ( normal, point ) { + + this.normal.copy( normal ); + this.constant = - point.dot( this.normal ); // must be this.normal, not normal, as this.normal is normalized + + return this; + + }, + + setFromCoplanarPoints: function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + + return function ( a, b, c ) { + + var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize(); + + // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? + + this.setFromNormalAndCoplanarPoint( normal, a ); + + return this; + + }; + + }(), + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( plane ) { + + this.normal.copy( plane.normal ); + this.constant = plane.constant; + + return this; + + }, + + normalize: function () { + + // Note: will lead to a divide by zero if the plane is invalid. + + var inverseNormalLength = 1.0 / this.normal.length(); + this.normal.multiplyScalar( inverseNormalLength ); + this.constant *= inverseNormalLength; + + return this; + + }, + + negate: function () { + + this.constant *= - 1; + this.normal.negate(); + + return this; + + }, + + distanceToPoint: function ( point ) { + + return this.normal.dot( point ) + this.constant; + + }, + + distanceToSphere: function ( sphere ) { + + return this.distanceToPoint( sphere.center ) - sphere.radius; + + }, + + projectPoint: function ( point, optionalTarget ) { + + return this.orthoPoint( point, optionalTarget ).sub( point ).negate(); + + }, + + orthoPoint: function ( point, optionalTarget ) { + + var perpendicularMagnitude = this.distanceToPoint( point ); + + var result = optionalTarget || new THREE.Vector3(); + return result.copy( this.normal ).multiplyScalar( perpendicularMagnitude ); + + }, + + isIntersectionLine: function ( line ) { + + // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. + + var startSign = this.distanceToPoint( line.start ); + var endSign = this.distanceToPoint( line.end ); + + return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); + + }, + + intersectLine: function () { + + var v1 = new THREE.Vector3(); + + return function ( line, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + var direction = line.delta( v1 ); + + var denominator = this.normal.dot( direction ); + + if ( denominator === 0 ) { + + // line is coplanar, return origin + if ( this.distanceToPoint( line.start ) === 0 ) { + + return result.copy( line.start ); + + } + + // Unsure if this is the correct method to handle this case. + return undefined; + + } + + var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; + + if ( t < 0 || t > 1 ) { + + return undefined; + + } + + return result.copy( direction ).multiplyScalar( t ).add( line.start ); + + }; + + }(), + + + coplanarPoint: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.copy( this.normal ).multiplyScalar( - this.constant ); + + }, + + applyMatrix4: function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var m1 = new THREE.Matrix3(); + + return function ( matrix, optionalNormalMatrix ) { + + // compute new normal based on theory here: + // http://www.songho.ca/opengl/gl_normaltransform.html + var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix ); + var newNormal = v1.copy( this.normal ).applyMatrix3( normalMatrix ); + + var newCoplanarPoint = this.coplanarPoint( v2 ); + newCoplanarPoint.applyMatrix4( matrix ); + + this.setFromNormalAndCoplanarPoint( newNormal, newCoplanarPoint ); + + return this; + + }; + + }(), + + translate: function ( offset ) { + + this.constant = this.constant - offset.dot( this.normal ); + + return this; + + }, + + equals: function ( plane ) { + + return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); + + } + +}; + +// File:src/math/Math.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Math = { + + generateUUID: function () { + + // http://www.broofa.com/Tools/Math.uuid.htm + + var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' ); + var uuid = new Array( 36 ); + var rnd = 0, r; + + return function () { + + for ( var i = 0; i < 36; i ++ ) { + + if ( i === 8 || i === 13 || i === 18 || i === 23 ) { + + uuid[ i ] = '-'; + + } else if ( i === 14 ) { + + uuid[ i ] = '4'; + + } else { + + if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0; + r = rnd & 0xf; + rnd = rnd >> 4; + uuid[ i ] = chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ]; + + } + + } + + return uuid.join( '' ); + + }; + + }(), + + clamp: function ( value, min, max ) { + + return Math.max( min, Math.min( max, value ) ); + + }, + + // compute euclidian modulo of m % n + // https://en.wikipedia.org/wiki/Modulo_operation + + euclideanModulo: function ( n, m ) { + + return ( ( n % m ) + m ) % m; + + }, + + // Linear mapping from range to range + + mapLinear: function ( x, a1, a2, b1, b2 ) { + + return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); + + }, + + // http://en.wikipedia.org/wiki/Smoothstep + + smoothstep: function ( x, min, max ) { + + if ( x <= min ) return 0; + if ( x >= max ) return 1; + + x = ( x - min ) / ( max - min ); + + return x * x * ( 3 - 2 * x ); + + }, + + smootherstep: function ( x, min, max ) { + + if ( x <= min ) return 0; + if ( x >= max ) return 1; + + x = ( x - min ) / ( max - min ); + + return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); + + }, + + // Random float from <0, 1> with 16 bits of randomness + // (standard Math.random() creates repetitive patterns when applied over larger space) + + random16: function () { + + return ( 65280 * Math.random() + 255 * Math.random() ) / 65535; + + }, + + // Random integer from interval + + randInt: function ( low, high ) { + + return low + Math.floor( Math.random() * ( high - low + 1 ) ); + + }, + + // Random float from interval + + randFloat: function ( low, high ) { + + return low + Math.random() * ( high - low ); + + }, + + // Random float from <-range/2, range/2> interval + + randFloatSpread: function ( range ) { + + return range * ( 0.5 - Math.random() ); + + }, + + degToRad: function () { + + var degreeToRadiansFactor = Math.PI / 180; + + return function ( degrees ) { + + return degrees * degreeToRadiansFactor; + + }; + + }(), + + radToDeg: function () { + + var radianToDegreesFactor = 180 / Math.PI; + + return function ( radians ) { + + return radians * radianToDegreesFactor; + + }; + + }(), + + isPowerOfTwo: function ( value ) { + + return ( value & ( value - 1 ) ) === 0 && value !== 0; + + }, + + nearestPowerOfTwo: function ( value ) { + + return Math.pow( 2, Math.round( Math.log( value ) / Math.LN2 ) ); + + }, + + nextPowerOfTwo: function ( value ) { + + value --; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value ++; + + return value; + + } + +}; + +// File:src/math/Spline.js + +/** + * Spline from Tween.js, slightly optimized (and trashed) + * http://sole.github.com/tween.js/examples/05_spline.html + * + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Spline = function ( points ) { + + this.points = points; + + var c = [], v3 = { x: 0, y: 0, z: 0 }, + point, intPoint, weight, w2, w3, + pa, pb, pc, pd; + + this.initFromArray = function ( a ) { + + this.points = []; + + for ( var i = 0; i < a.length; i ++ ) { + + this.points[ i ] = { x: a[ i ][ 0 ], y: a[ i ][ 1 ], z: a[ i ][ 2 ] }; + + } + + }; + + this.getPoint = function ( k ) { + + point = ( this.points.length - 1 ) * k; + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > this.points.length - 2 ? this.points.length - 1 : intPoint + 1; + c[ 3 ] = intPoint > this.points.length - 3 ? this.points.length - 1 : intPoint + 2; + + pa = this.points[ c[ 0 ] ]; + pb = this.points[ c[ 1 ] ]; + pc = this.points[ c[ 2 ] ]; + pd = this.points[ c[ 3 ] ]; + + w2 = weight * weight; + w3 = weight * w2; + + v3.x = interpolate( pa.x, pb.x, pc.x, pd.x, weight, w2, w3 ); + v3.y = interpolate( pa.y, pb.y, pc.y, pd.y, weight, w2, w3 ); + v3.z = interpolate( pa.z, pb.z, pc.z, pd.z, weight, w2, w3 ); + + return v3; + + }; + + this.getControlPointsArray = function () { + + var i, p, l = this.points.length, + coords = []; + + for ( i = 0; i < l; i ++ ) { + + p = this.points[ i ]; + coords[ i ] = [ p.x, p.y, p.z ]; + + } + + return coords; + + }; + + // approximate length by summing linear segments + + this.getLength = function ( nSubDivisions ) { + + var i, index, nSamples, position, + point = 0, intPoint = 0, oldIntPoint = 0, + oldPosition = new THREE.Vector3(), + tmpVec = new THREE.Vector3(), + chunkLengths = [], + totalLength = 0; + + // first point has 0 length + + chunkLengths[ 0 ] = 0; + + if ( ! nSubDivisions ) nSubDivisions = 100; + + nSamples = this.points.length * nSubDivisions; + + oldPosition.copy( this.points[ 0 ] ); + + for ( i = 1; i < nSamples; i ++ ) { + + index = i / nSamples; + + position = this.getPoint( index ); + tmpVec.copy( position ); + + totalLength += tmpVec.distanceTo( oldPosition ); + + oldPosition.copy( position ); + + point = ( this.points.length - 1 ) * index; + intPoint = Math.floor( point ); + + if ( intPoint !== oldIntPoint ) { + + chunkLengths[ intPoint ] = totalLength; + oldIntPoint = intPoint; + + } + + } + + // last point ends with total length + + chunkLengths[ chunkLengths.length ] = totalLength; + + return { chunks: chunkLengths, total: totalLength }; + + }; + + this.reparametrizeByArcLength = function ( samplingCoef ) { + + var i, j, + index, indexCurrent, indexNext, + realDistance, + sampling, position, + newpoints = [], + tmpVec = new THREE.Vector3(), + sl = this.getLength(); + + newpoints.push( tmpVec.copy( this.points[ 0 ] ).clone() ); + + for ( i = 1; i < this.points.length; i ++ ) { + + //tmpVec.copy( this.points[ i - 1 ] ); + //linearDistance = tmpVec.distanceTo( this.points[ i ] ); + + realDistance = sl.chunks[ i ] - sl.chunks[ i - 1 ]; + + sampling = Math.ceil( samplingCoef * realDistance / sl.total ); + + indexCurrent = ( i - 1 ) / ( this.points.length - 1 ); + indexNext = i / ( this.points.length - 1 ); + + for ( j = 1; j < sampling - 1; j ++ ) { + + index = indexCurrent + j * ( 1 / sampling ) * ( indexNext - indexCurrent ); + + position = this.getPoint( index ); + newpoints.push( tmpVec.copy( position ).clone() ); + + } + + newpoints.push( tmpVec.copy( this.points[ i ] ).clone() ); + + } + + this.points = newpoints; + + }; + + // Catmull-Rom + + function interpolate( p0, p1, p2, p3, t, t2, t3 ) { + + var v0 = ( p2 - p0 ) * 0.5, + v1 = ( p3 - p1 ) * 0.5; + + return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + } + +}; + +// File:src/math/Triangle.js + +/** + * @author bhouston / http://clara.io + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Triangle = function ( a, b, c ) { + + this.a = ( a !== undefined ) ? a : new THREE.Vector3(); + this.b = ( b !== undefined ) ? b : new THREE.Vector3(); + this.c = ( c !== undefined ) ? c : new THREE.Vector3(); + +}; + +THREE.Triangle.normal = function () { + + var v0 = new THREE.Vector3(); + + return function ( a, b, c, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + result.subVectors( c, b ); + v0.subVectors( a, b ); + result.cross( v0 ); + + var resultLengthSq = result.lengthSq(); + if ( resultLengthSq > 0 ) { + + return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) ); + + } + + return result.set( 0, 0, 0 ); + + }; + +}(); + +// static/instance method to calculate barycentric coordinates +// based on: http://www.blackpawn.com/texts/pointinpoly/default.html +THREE.Triangle.barycoordFromPoint = function () { + + var v0 = new THREE.Vector3(); + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + + return function ( point, a, b, c, optionalTarget ) { + + v0.subVectors( c, a ); + v1.subVectors( b, a ); + v2.subVectors( point, a ); + + var dot00 = v0.dot( v0 ); + var dot01 = v0.dot( v1 ); + var dot02 = v0.dot( v2 ); + var dot11 = v1.dot( v1 ); + var dot12 = v1.dot( v2 ); + + var denom = ( dot00 * dot11 - dot01 * dot01 ); + + var result = optionalTarget || new THREE.Vector3(); + + // collinear or singular triangle + if ( denom === 0 ) { + + // arbitrary location outside of triangle? + // not sure if this is the best idea, maybe should be returning undefined + return result.set( - 2, - 1, - 1 ); + + } + + var invDenom = 1 / denom; + var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; + var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; + + // barycentric coordinates must always sum to 1 + return result.set( 1 - u - v, v, u ); + + }; + +}(); + +THREE.Triangle.containsPoint = function () { + + var v1 = new THREE.Vector3(); + + return function ( point, a, b, c ) { + + var result = THREE.Triangle.barycoordFromPoint( point, a, b, c, v1 ); + + return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 ); + + }; + +}(); + +THREE.Triangle.prototype = { + + constructor: THREE.Triangle, + + set: function ( a, b, c ) { + + this.a.copy( a ); + this.b.copy( b ); + this.c.copy( c ); + + return this; + + }, + + setFromPointsAndIndices: function ( points, i0, i1, i2 ) { + + this.a.copy( points[ i0 ] ); + this.b.copy( points[ i1 ] ); + this.c.copy( points[ i2 ] ); + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( triangle ) { + + this.a.copy( triangle.a ); + this.b.copy( triangle.b ); + this.c.copy( triangle.c ); + + return this; + + }, + + area: function () { + + var v0 = new THREE.Vector3(); + var v1 = new THREE.Vector3(); + + return function () { + + v0.subVectors( this.c, this.b ); + v1.subVectors( this.a, this.b ); + + return v0.cross( v1 ).length() * 0.5; + + }; + + }(), + + midpoint: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); + + }, + + normal: function ( optionalTarget ) { + + return THREE.Triangle.normal( this.a, this.b, this.c, optionalTarget ); + + }, + + plane: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Plane(); + + return result.setFromCoplanarPoints( this.a, this.b, this.c ); + + }, + + barycoordFromPoint: function ( point, optionalTarget ) { + + return THREE.Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget ); + + }, + + containsPoint: function ( point ) { + + return THREE.Triangle.containsPoint( point, this.a, this.b, this.c ); + + }, + + equals: function ( triangle ) { + + return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); + + } + +}; + +// File:src/core/Channels.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Channels = function () { + + this.mask = 1; + +}; + +THREE.Channels.prototype = { + + constructor: THREE.Channels, + + set: function ( channel ) { + + this.mask = 1 << channel; + + }, + + enable: function ( channel ) { + + this.mask |= 1 << channel; + + }, + + toggle: function ( channel ) { + + this.mask ^= 1 << channel; + + }, + + disable: function ( channel ) { + + this.mask &= ~ ( 1 << channel ); + + } + +}; + +// File:src/core/Clock.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Clock = function ( autoStart ) { + + this.autoStart = ( autoStart !== undefined ) ? autoStart : true; + + this.startTime = 0; + this.oldTime = 0; + this.elapsedTime = 0; + + this.running = false; + +}; + +THREE.Clock.prototype = { + + constructor: THREE.Clock, + + start: function () { + + this.startTime = self.performance.now(); + + this.oldTime = this.startTime; + this.running = true; + + }, + + stop: function () { + + this.getElapsedTime(); + this.running = false; + + }, + + getElapsedTime: function () { + + this.getDelta(); + return this.elapsedTime; + + }, + + getDelta: function () { + + var diff = 0; + + if ( this.autoStart && ! this.running ) { + + this.start(); + + } + + if ( this.running ) { + + var newTime = self.performance.now(); + + diff = 0.001 * ( newTime - this.oldTime ); + this.oldTime = newTime; + + this.elapsedTime += diff; + + } + + return diff; + + } + +}; + +// File:src/core/EventDispatcher.js + +/** + * https://github.com/mrdoob/eventdispatcher.js/ + */ + +THREE.EventDispatcher = function () {}; + +THREE.EventDispatcher.prototype = { + + constructor: THREE.EventDispatcher, + + apply: function ( object ) { + + object.addEventListener = THREE.EventDispatcher.prototype.addEventListener; + object.hasEventListener = THREE.EventDispatcher.prototype.hasEventListener; + object.removeEventListener = THREE.EventDispatcher.prototype.removeEventListener; + object.dispatchEvent = THREE.EventDispatcher.prototype.dispatchEvent; + + }, + + addEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) this._listeners = {}; + + var listeners = this._listeners; + + if ( listeners[ type ] === undefined ) { + + listeners[ type ] = []; + + } + + if ( listeners[ type ].indexOf( listener ) === - 1 ) { + + listeners[ type ].push( listener ); + + } + + }, + + hasEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) return false; + + var listeners = this._listeners; + + if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) { + + return true; + + } + + return false; + + }, + + removeEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) return; + + var listeners = this._listeners; + var listenerArray = listeners[ type ]; + + if ( listenerArray !== undefined ) { + + var index = listenerArray.indexOf( listener ); + + if ( index !== - 1 ) { + + listenerArray.splice( index, 1 ); + + } + + } + + }, + + dispatchEvent: function ( event ) { + + if ( this._listeners === undefined ) return; + + var listeners = this._listeners; + var listenerArray = listeners[ event.type ]; + + if ( listenerArray !== undefined ) { + + event.target = this; + + var array = []; + var length = listenerArray.length; + + for ( var i = 0; i < length; i ++ ) { + + array[ i ] = listenerArray[ i ]; + + } + + for ( var i = 0; i < length; i ++ ) { + + array[ i ].call( this, event ); + + } + + } + + } + +}; + +// File:src/core/Raycaster.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author bhouston / http://clara.io/ + * @author stephomi / http://stephaneginier.com/ + */ + +( function ( THREE ) { + + THREE.Raycaster = function ( origin, direction, near, far ) { + + this.ray = new THREE.Ray( origin, direction ); + // direction is assumed to be normalized (for accurate distance calculations) + + this.near = near || 0; + this.far = far || Infinity; + + this.params = { + Mesh: {}, + Line: {}, + LOD: {}, + Points: { threshold: 1 }, + Sprite: {} + }; + + Object.defineProperties( this.params, { + PointCloud: { + get: function () { + console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' ); + return this.Points; + } + } + } ); + + }; + + function descSort( a, b ) { + + return a.distance - b.distance; + + } + + function intersectObject( object, raycaster, intersects, recursive ) { + + if ( object.visible === false ) return; + + object.raycast( raycaster, intersects ); + + if ( recursive === true ) { + + var children = object.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + intersectObject( children[ i ], raycaster, intersects, true ); + + } + + } + + } + + // + + THREE.Raycaster.prototype = { + + constructor: THREE.Raycaster, + + linePrecision: 1, + + set: function ( origin, direction ) { + + // direction is assumed to be normalized (for accurate distance calculations) + + this.ray.set( origin, direction ); + + }, + + setFromCamera: function ( coords, camera ) { + + if ( camera instanceof THREE.PerspectiveCamera ) { + + this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); + this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); + + } else if ( camera instanceof THREE.OrthographicCamera ) { + + this.ray.origin.set( coords.x, coords.y, - 1 ).unproject( camera ); + this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); + + } else { + + console.error( 'THREE.Raycaster: Unsupported camera type.' ); + + } + + }, + + intersectObject: function ( object, recursive ) { + + var intersects = []; + + intersectObject( object, this, intersects, recursive ); + + intersects.sort( descSort ); + + return intersects; + + }, + + intersectObjects: function ( objects, recursive ) { + + var intersects = []; + + if ( Array.isArray( objects ) === false ) { + + console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' ); + return intersects; + + } + + for ( var i = 0, l = objects.length; i < l; i ++ ) { + + intersectObject( objects[ i ], this, intersects, recursive ); + + } + + intersects.sort( descSort ); + + return intersects; + + } + + }; + +}( THREE ) ); + +// File:src/core/Object3D.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author elephantatwork / www.elephantatwork.ch + */ + +THREE.Object3D = function () { + + Object.defineProperty( this, 'id', { value: THREE.Object3DIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.type = 'Object3D'; + + this.parent = null; + this.channels = new THREE.Channels(); + this.children = []; + + this.up = THREE.Object3D.DefaultUp.clone(); + + var position = new THREE.Vector3(); + var rotation = new THREE.Euler(); + var quaternion = new THREE.Quaternion(); + var scale = new THREE.Vector3( 1, 1, 1 ); + + function onRotationChange() { + + quaternion.setFromEuler( rotation, false ); + + } + + function onQuaternionChange() { + + rotation.setFromQuaternion( quaternion, undefined, false ); + + } + + rotation.onChange( onRotationChange ); + quaternion.onChange( onQuaternionChange ); + + Object.defineProperties( this, { + position: { + enumerable: true, + value: position + }, + rotation: { + enumerable: true, + value: rotation + }, + quaternion: { + enumerable: true, + value: quaternion + }, + scale: { + enumerable: true, + value: scale + }, + modelViewMatrix: { + value: new THREE.Matrix4() + }, + normalMatrix: { + value: new THREE.Matrix3() + } + } ); + + this.rotationAutoUpdate = true; + + this.matrix = new THREE.Matrix4(); + this.matrixWorld = new THREE.Matrix4(); + + this.matrixAutoUpdate = THREE.Object3D.DefaultMatrixAutoUpdate; + this.matrixWorldNeedsUpdate = false; + + this.visible = true; + + this.castShadow = false; + this.receiveShadow = false; + + this.frustumCulled = true; + this.renderOrder = 0; + + this.userData = {}; + +}; + +THREE.Object3D.DefaultUp = new THREE.Vector3( 0, 1, 0 ); +THREE.Object3D.DefaultMatrixAutoUpdate = true; + +THREE.Object3D.prototype = { + + constructor: THREE.Object3D, + + get eulerOrder () { + + console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); + + return this.rotation.order; + + }, + + set eulerOrder ( value ) { + + console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); + + this.rotation.order = value; + + }, + + get useQuaternion () { + + console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + + }, + + set useQuaternion ( value ) { + + console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + + }, + + set renderDepth ( value ) { + + console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); + + }, + + // + + applyMatrix: function ( matrix ) { + + this.matrix.multiplyMatrices( matrix, this.matrix ); + + this.matrix.decompose( this.position, this.quaternion, this.scale ); + + }, + + setRotationFromAxisAngle: function ( axis, angle ) { + + // assumes axis is normalized + + this.quaternion.setFromAxisAngle( axis, angle ); + + }, + + setRotationFromEuler: function ( euler ) { + + this.quaternion.setFromEuler( euler, true ); + + }, + + setRotationFromMatrix: function ( m ) { + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + this.quaternion.setFromRotationMatrix( m ); + + }, + + setRotationFromQuaternion: function ( q ) { + + // assumes q is normalized + + this.quaternion.copy( q ); + + }, + + rotateOnAxis: function () { + + // rotate object on axis in object space + // axis is assumed to be normalized + + var q1 = new THREE.Quaternion(); + + return function ( axis, angle ) { + + q1.setFromAxisAngle( axis, angle ); + + this.quaternion.multiply( q1 ); + + return this; + + }; + + }(), + + rotateX: function () { + + var v1 = new THREE.Vector3( 1, 0, 0 ); + + return function ( angle ) { + + return this.rotateOnAxis( v1, angle ); + + }; + + }(), + + rotateY: function () { + + var v1 = new THREE.Vector3( 0, 1, 0 ); + + return function ( angle ) { + + return this.rotateOnAxis( v1, angle ); + + }; + + }(), + + rotateZ: function () { + + var v1 = new THREE.Vector3( 0, 0, 1 ); + + return function ( angle ) { + + return this.rotateOnAxis( v1, angle ); + + }; + + }(), + + translateOnAxis: function () { + + // translate object by distance along axis in object space + // axis is assumed to be normalized + + var v1 = new THREE.Vector3(); + + return function ( axis, distance ) { + + v1.copy( axis ).applyQuaternion( this.quaternion ); + + this.position.add( v1.multiplyScalar( distance ) ); + + return this; + + }; + + }(), + + translate: function ( distance, axis ) { + + console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); + return this.translateOnAxis( axis, distance ); + + }, + + translateX: function () { + + var v1 = new THREE.Vector3( 1, 0, 0 ); + + return function ( distance ) { + + return this.translateOnAxis( v1, distance ); + + }; + + }(), + + translateY: function () { + + var v1 = new THREE.Vector3( 0, 1, 0 ); + + return function ( distance ) { + + return this.translateOnAxis( v1, distance ); + + }; + + }(), + + translateZ: function () { + + var v1 = new THREE.Vector3( 0, 0, 1 ); + + return function ( distance ) { + + return this.translateOnAxis( v1, distance ); + + }; + + }(), + + localToWorld: function ( vector ) { + + return vector.applyMatrix4( this.matrixWorld ); + + }, + + worldToLocal: function () { + + var m1 = new THREE.Matrix4(); + + return function ( vector ) { + + return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) ); + + }; + + }(), + + lookAt: function () { + + // This routine does not support objects with rotated and/or translated parent(s) + + var m1 = new THREE.Matrix4(); + + return function ( vector ) { + + m1.lookAt( vector, this.position, this.up ); + + this.quaternion.setFromRotationMatrix( m1 ); + + }; + + }(), + + add: function ( object ) { + + if ( arguments.length > 1 ) { + + for ( var i = 0; i < arguments.length; i ++ ) { + + this.add( arguments[ i ] ); + + } + + return this; + + } + + if ( object === this ) { + + console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object ); + return this; + + } + + if ( object instanceof THREE.Object3D ) { + + if ( object.parent !== null ) { + + object.parent.remove( object ); + + } + + object.parent = this; + object.dispatchEvent( { type: 'added' } ); + + this.children.push( object ); + + } else { + + console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); + + } + + return this; + + }, + + remove: function ( object ) { + + if ( arguments.length > 1 ) { + + for ( var i = 0; i < arguments.length; i ++ ) { + + this.remove( arguments[ i ] ); + + } + + } + + var index = this.children.indexOf( object ); + + if ( index !== - 1 ) { + + object.parent = null; + + object.dispatchEvent( { type: 'removed' } ); + + this.children.splice( index, 1 ); + + } + + }, + + getChildByName: function ( name ) { + + console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); + return this.getObjectByName( name ); + + }, + + getObjectById: function ( id ) { + + return this.getObjectByProperty( 'id', id ); + + }, + + getObjectByName: function ( name ) { + + return this.getObjectByProperty( 'name', name ); + + }, + + getObjectByProperty: function ( name, value ) { + + if ( this[ name ] === value ) return this; + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + var child = this.children[ i ]; + var object = child.getObjectByProperty( name, value ); + + if ( object !== undefined ) { + + return object; + + } + + } + + return undefined; + + }, + + getWorldPosition: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + this.updateMatrixWorld( true ); + + return result.setFromMatrixPosition( this.matrixWorld ); + + }, + + getWorldQuaternion: function () { + + var position = new THREE.Vector3(); + var scale = new THREE.Vector3(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Quaternion(); + + this.updateMatrixWorld( true ); + + this.matrixWorld.decompose( position, result, scale ); + + return result; + + }; + + }(), + + getWorldRotation: function () { + + var quaternion = new THREE.Quaternion(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Euler(); + + this.getWorldQuaternion( quaternion ); + + return result.setFromQuaternion( quaternion, this.rotation.order, false ); + + }; + + }(), + + getWorldScale: function () { + + var position = new THREE.Vector3(); + var quaternion = new THREE.Quaternion(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + this.updateMatrixWorld( true ); + + this.matrixWorld.decompose( position, quaternion, result ); + + return result; + + }; + + }(), + + getWorldDirection: function () { + + var quaternion = new THREE.Quaternion(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + this.getWorldQuaternion( quaternion ); + + return result.set( 0, 0, 1 ).applyQuaternion( quaternion ); + + }; + + }(), + + raycast: function () {}, + + traverse: function ( callback ) { + + callback( this ); + + var children = this.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + children[ i ].traverse( callback ); + + } + + }, + + traverseVisible: function ( callback ) { + + if ( this.visible === false ) return; + + callback( this ); + + var children = this.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + children[ i ].traverseVisible( callback ); + + } + + }, + + traverseAncestors: function ( callback ) { + + var parent = this.parent; + + if ( parent !== null ) { + + callback( parent ); + + parent.traverseAncestors( callback ); + + } + + }, + + updateMatrix: function () { + + this.matrix.compose( this.position, this.quaternion, this.scale ); + + this.matrixWorldNeedsUpdate = true; + + }, + + updateMatrixWorld: function ( force ) { + + if ( this.matrixAutoUpdate === true ) this.updateMatrix(); + + if ( this.matrixWorldNeedsUpdate === true || force === true ) { + + if ( this.parent === null ) { + + this.matrixWorld.copy( this.matrix ); + + } else { + + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + + } + + this.matrixWorldNeedsUpdate = false; + + force = true; + + } + + // update children + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].updateMatrixWorld( force ); + + } + + }, + + toJSON: function ( meta ) { + + var isRootObject = ( meta === undefined ); + + var output = {}; + + // meta is a hash used to collect geometries, materials. + // not providing it implies that this is the root object + // being serialized. + if ( isRootObject ) { + + // initialize meta obj + meta = { + geometries: {}, + materials: {}, + textures: {}, + images: {} + }; + + output.metadata = { + version: 4.4, + type: 'Object', + generator: 'Object3D.toJSON' + }; + + } + + // standard Object3D serialization + + var object = {}; + + object.uuid = this.uuid; + object.type = this.type; + + if ( this.name !== '' ) object.name = this.name; + if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData; + if ( this.castShadow === true ) object.castShadow = true; + if ( this.receiveShadow === true ) object.receiveShadow = true; + if ( this.visible === false ) object.visible = false; + + object.matrix = this.matrix.toArray(); + + // + + if ( this.geometry !== undefined ) { + + if ( meta.geometries[ this.geometry.uuid ] === undefined ) { + + meta.geometries[ this.geometry.uuid ] = this.geometry.toJSON( meta ); + + } + + object.geometry = this.geometry.uuid; + + } + + if ( this.material !== undefined ) { + + if ( meta.materials[ this.material.uuid ] === undefined ) { + + meta.materials[ this.material.uuid ] = this.material.toJSON( meta ); + + } + + object.material = this.material.uuid; + + } + + // + + if ( this.children.length > 0 ) { + + object.children = []; + + for ( var i = 0; i < this.children.length; i ++ ) { + + object.children.push( this.children[ i ].toJSON( meta ).object ); + + } + + } + + if ( isRootObject ) { + + var geometries = extractFromCache( meta.geometries ); + var materials = extractFromCache( meta.materials ); + var textures = extractFromCache( meta.textures ); + var images = extractFromCache( meta.images ); + + if ( geometries.length > 0 ) output.geometries = geometries; + if ( materials.length > 0 ) output.materials = materials; + if ( textures.length > 0 ) output.textures = textures; + if ( images.length > 0 ) output.images = images; + + } + + output.object = object; + + return output; + + // extract data from the cache hash + // remove metadata on each item + // and return as array + function extractFromCache ( cache ) { + + var values = []; + for ( var key in cache ) { + + var data = cache[ key ]; + delete data.metadata; + values.push( data ); + + } + return values; + + } + + }, + + clone: function ( recursive ) { + + return new this.constructor().copy( this, recursive ); + + }, + + copy: function ( source, recursive ) { + + if ( recursive === undefined ) recursive = true; + + this.name = source.name; + + this.up.copy( source.up ); + + this.position.copy( source.position ); + this.quaternion.copy( source.quaternion ); + this.scale.copy( source.scale ); + + this.rotationAutoUpdate = source.rotationAutoUpdate; + + this.matrix.copy( source.matrix ); + this.matrixWorld.copy( source.matrixWorld ); + + this.matrixAutoUpdate = source.matrixAutoUpdate; + this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; + + this.visible = source.visible; + + this.castShadow = source.castShadow; + this.receiveShadow = source.receiveShadow; + + this.frustumCulled = source.frustumCulled; + this.renderOrder = source.renderOrder; + + this.userData = JSON.parse( JSON.stringify( source.userData ) ); + + if ( recursive === true ) { + + for ( var i = 0; i < source.children.length; i ++ ) { + + var child = source.children[ i ]; + this.add( child.clone() ); + + } + + } + + return this; + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Object3D.prototype ); + +THREE.Object3DIdCount = 0; + +// File:src/core/Face3.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Face3 = function ( a, b, c, normal, color, materialIndex ) { + + this.a = a; + this.b = b; + this.c = c; + + this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3(); + this.vertexNormals = Array.isArray( normal ) ? normal : []; + + this.color = color instanceof THREE.Color ? color : new THREE.Color(); + this.vertexColors = Array.isArray( color ) ? color : []; + + this.materialIndex = materialIndex !== undefined ? materialIndex : 0; + +}; + +THREE.Face3.prototype = { + + constructor: THREE.Face3, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( source ) { + + this.a = source.a; + this.b = source.b; + this.c = source.c; + + this.normal.copy( source.normal ); + this.color.copy( source.color ); + + this.materialIndex = source.materialIndex; + + for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) { + + this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); + + } + + for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) { + + this.vertexColors[ i ] = source.vertexColors[ i ].clone(); + + } + + return this; + + } + +}; + +// File:src/core/Face4.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) { + + console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' ); + return new THREE.Face3( a, b, c, normal, color, materialIndex ); + +}; + +// File:src/core/BufferAttribute.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BufferAttribute = function ( array, itemSize ) { + + this.uuid = THREE.Math.generateUUID(); + + this.array = array; + this.itemSize = itemSize; + + this.dynamic = false; + this.updateRange = { offset: 0, count: - 1 }; + + this.version = 0; + +}; + +THREE.BufferAttribute.prototype = { + + constructor: THREE.BufferAttribute, + + get length() { + + console.warn( 'THREE.BufferAttribute: .length has been deprecated. Please use .count.' ); + return this.array.length; + + }, + + get count() { + + return this.array.length / this.itemSize; + + }, + + set needsUpdate( value ) { + + if ( value === true ) this.version ++; + + }, + + setDynamic: function ( value ) { + + this.dynamic = value; + + return this; + + }, + + copy: function ( source ) { + + this.array = new source.array.constructor( source.array ); + this.itemSize = source.itemSize; + + this.dynamic = source.dynamic; + + return this; + + }, + + copyAt: function ( index1, attribute, index2 ) { + + index1 *= this.itemSize; + index2 *= attribute.itemSize; + + for ( var i = 0, l = this.itemSize; i < l; i ++ ) { + + this.array[ index1 + i ] = attribute.array[ index2 + i ]; + + } + + return this; + + }, + + copyArray: function ( array ) { + + this.array.set( array ); + + return this; + + }, + + copyColorsArray: function ( colors ) { + + var array = this.array, offset = 0; + + for ( var i = 0, l = colors.length; i < l; i ++ ) { + + var color = colors[ i ]; + + if ( color === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); + color = new THREE.Color(); + + } + + array[ offset ++ ] = color.r; + array[ offset ++ ] = color.g; + array[ offset ++ ] = color.b; + + } + + return this; + + }, + + copyIndicesArray: function ( indices ) { + + var array = this.array, offset = 0; + + for ( var i = 0, l = indices.length; i < l; i ++ ) { + + var index = indices[ i ]; + + array[ offset ++ ] = index.a; + array[ offset ++ ] = index.b; + array[ offset ++ ] = index.c; + + } + + return this; + + }, + + copyVector2sArray: function ( vectors ) { + + var array = this.array, offset = 0; + + for ( var i = 0, l = vectors.length; i < l; i ++ ) { + + var vector = vectors[ i ]; + + if ( vector === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); + vector = new THREE.Vector2(); + + } + + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; + + } + + return this; + + }, + + copyVector3sArray: function ( vectors ) { + + var array = this.array, offset = 0; + + for ( var i = 0, l = vectors.length; i < l; i ++ ) { + + var vector = vectors[ i ]; + + if ( vector === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); + vector = new THREE.Vector3(); + + } + + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; + array[ offset ++ ] = vector.z; + + } + + return this; + + }, + + copyVector4sArray: function ( vectors ) { + + var array = this.array, offset = 0; + + for ( var i = 0, l = vectors.length; i < l; i ++ ) { + + var vector = vectors[ i ]; + + if ( vector === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); + vector = new THREE.Vector4(); + + } + + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; + array[ offset ++ ] = vector.z; + array[ offset ++ ] = vector.w; + + } + + return this; + + }, + + set: function ( value, offset ) { + + if ( offset === undefined ) offset = 0; + + this.array.set( value, offset ); + + return this; + + }, + + getX: function ( index ) { + + return this.array[ index * this.itemSize ]; + + }, + + setX: function ( index, x ) { + + this.array[ index * this.itemSize ] = x; + + return this; + + }, + + getY: function ( index ) { + + return this.array[ index * this.itemSize + 1 ]; + + }, + + setY: function ( index, y ) { + + this.array[ index * this.itemSize + 1 ] = y; + + return this; + + }, + + getZ: function ( index ) { + + return this.array[ index * this.itemSize + 2 ]; + + }, + + setZ: function ( index, z ) { + + this.array[ index * this.itemSize + 2 ] = z; + + return this; + + }, + + getW: function ( index ) { + + return this.array[ index * this.itemSize + 3 ]; + + }, + + setW: function ( index, w ) { + + this.array[ index * this.itemSize + 3 ] = w; + + return this; + + }, + + setXY: function ( index, x, y ) { + + index *= this.itemSize; + + this.array[ index + 0 ] = x; + this.array[ index + 1 ] = y; + + return this; + + }, + + setXYZ: function ( index, x, y, z ) { + + index *= this.itemSize; + + this.array[ index + 0 ] = x; + this.array[ index + 1 ] = y; + this.array[ index + 2 ] = z; + + return this; + + }, + + setXYZW: function ( index, x, y, z, w ) { + + index *= this.itemSize; + + this.array[ index + 0 ] = x; + this.array[ index + 1 ] = y; + this.array[ index + 2 ] = z; + this.array[ index + 3 ] = w; + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + } + +}; + +// + +THREE.Int8Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Int8Array( array ), itemSize ); + +}; + +THREE.Uint8Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Uint8Array( array ), itemSize ); + +}; + +THREE.Uint8ClampedAttribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Uint8ClampedArray( array ), itemSize ); + +}; + +THREE.Int16Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Int16Array( array ), itemSize ); + +}; + +THREE.Uint16Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Uint16Array( array ), itemSize ); + +}; + +THREE.Int32Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Int32Array( array ), itemSize ); + +}; + +THREE.Uint32Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Uint32Array( array ), itemSize ); + +}; + +THREE.Float32Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Float32Array( array ), itemSize ); + +}; + +THREE.Float64Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Float64Array( array ), itemSize ); + +}; + + +// Deprecated + +THREE.DynamicBufferAttribute = function ( array, itemSize ) { + + console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' ); + return new THREE.BufferAttribute( array, itemSize ).setDynamic( true ); + +}; + +// File:src/core/InstancedBufferAttribute.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.InstancedBufferAttribute = function ( array, itemSize, meshPerAttribute ) { + + THREE.BufferAttribute.call( this, array, itemSize ); + + this.meshPerAttribute = meshPerAttribute || 1; + +}; + +THREE.InstancedBufferAttribute.prototype = Object.create( THREE.BufferAttribute.prototype ); +THREE.InstancedBufferAttribute.prototype.constructor = THREE.InstancedBufferAttribute; + +THREE.InstancedBufferAttribute.prototype.copy = function ( source ) { + + THREE.BufferAttribute.prototype.copy.call( this, source ); + + this.meshPerAttribute = source.meshPerAttribute; + + return this; + +}; + +// File:src/core/InterleavedBuffer.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.InterleavedBuffer = function ( array, stride ) { + + this.uuid = THREE.Math.generateUUID(); + + this.array = array; + this.stride = stride; + + this.dynamic = false; + this.updateRange = { offset: 0, count: - 1 }; + + this.version = 0; + +}; + +THREE.InterleavedBuffer.prototype = { + + constructor: THREE.InterleavedBuffer, + + get length () { + + return this.array.length; + + }, + + get count () { + + return this.array.length / this.stride; + + }, + + set needsUpdate( value ) { + + if ( value === true ) this.version ++; + + }, + + setDynamic: function ( value ) { + + this.dynamic = value; + + return this; + + }, + + copy: function ( source ) { + + this.array = new source.array.constructor( source.array ); + this.stride = source.stride; + this.dynamic = source.dynamic; + + }, + + copyAt: function ( index1, attribute, index2 ) { + + index1 *= this.stride; + index2 *= attribute.stride; + + for ( var i = 0, l = this.stride; i < l; i ++ ) { + + this.array[ index1 + i ] = attribute.array[ index2 + i ]; + + } + + return this; + + }, + + set: function ( value, offset ) { + + if ( offset === undefined ) offset = 0; + + this.array.set( value, offset ); + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + } + +}; + +// File:src/core/InstancedInterleavedBuffer.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.InstancedInterleavedBuffer = function ( array, stride, meshPerAttribute ) { + + THREE.InterleavedBuffer.call( this, array, stride ); + + this.meshPerAttribute = meshPerAttribute || 1; + +}; + +THREE.InstancedInterleavedBuffer.prototype = Object.create( THREE.InterleavedBuffer.prototype ); +THREE.InstancedInterleavedBuffer.prototype.constructor = THREE.InstancedInterleavedBuffer; + +THREE.InstancedInterleavedBuffer.prototype.copy = function ( source ) { + + THREE.InterleavedBuffer.prototype.copy.call( this, source ); + + this.meshPerAttribute = source.meshPerAttribute; + + return this; + +}; + +// File:src/core/InterleavedBufferAttribute.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.InterleavedBufferAttribute = function ( interleavedBuffer, itemSize, offset ) { + + this.uuid = THREE.Math.generateUUID(); + + this.data = interleavedBuffer; + this.itemSize = itemSize; + this.offset = offset; + +}; + + +THREE.InterleavedBufferAttribute.prototype = { + + constructor: THREE.InterleavedBufferAttribute, + + get length() { + + console.warn( 'THREE.BufferAttribute: .length has been deprecated. Please use .count.' ); + return this.array.length; + + }, + + get count() { + + return this.data.array.length / this.data.stride; + + }, + + setX: function ( index, x ) { + + this.data.array[ index * this.data.stride + this.offset ] = x; + + return this; + + }, + + setY: function ( index, y ) { + + this.data.array[ index * this.data.stride + this.offset + 1 ] = y; + + return this; + + }, + + setZ: function ( index, z ) { + + this.data.array[ index * this.data.stride + this.offset + 2 ] = z; + + return this; + + }, + + setW: function ( index, w ) { + + this.data.array[ index * this.data.stride + this.offset + 3 ] = w; + + return this; + + }, + + getX: function ( index ) { + + return this.data.array[ index * this.data.stride + this.offset ]; + + }, + + getY: function ( index ) { + + return this.data.array[ index * this.data.stride + this.offset + 1 ]; + + }, + + getZ: function ( index ) { + + return this.data.array[ index * this.data.stride + this.offset + 2 ]; + + }, + + getW: function ( index ) { + + return this.data.array[ index * this.data.stride + this.offset + 3 ]; + + }, + + setXY: function ( index, x, y ) { + + index = index * this.data.stride + this.offset; + + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + + return this; + + }, + + setXYZ: function ( index, x, y, z ) { + + index = index * this.data.stride + this.offset; + + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + this.data.array[ index + 2 ] = z; + + return this; + + }, + + setXYZW: function ( index, x, y, z, w ) { + + index = index * this.data.stride + this.offset; + + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + this.data.array[ index + 2 ] = z; + this.data.array[ index + 3 ] = w; + + return this; + + } + +}; + +// File:src/core/Geometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author kile / http://kile.stravaganza.org/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author bhouston / http://clara.io + */ + +THREE.Geometry = function () { + + Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.type = 'Geometry'; + + this.vertices = []; + this.colors = []; + this.faces = []; + this.faceVertexUvs = [ [] ]; + + this.morphTargets = []; + this.morphNormals = []; + + this.skinWeights = []; + this.skinIndices = []; + + this.lineDistances = []; + + this.boundingBox = null; + this.boundingSphere = null; + + // update flags + + this.verticesNeedUpdate = false; + this.elementsNeedUpdate = false; + this.uvsNeedUpdate = false; + this.normalsNeedUpdate = false; + this.colorsNeedUpdate = false; + this.lineDistancesNeedUpdate = false; + this.groupsNeedUpdate = false; + +}; + +THREE.Geometry.prototype = { + + constructor: THREE.Geometry, + + applyMatrix: function ( matrix ) { + + var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + + for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { + + var vertex = this.vertices[ i ]; + vertex.applyMatrix4( matrix ); + + } + + for ( var i = 0, il = this.faces.length; i < il; i ++ ) { + + var face = this.faces[ i ]; + face.normal.applyMatrix3( normalMatrix ).normalize(); + + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + + face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); + + } + + } + + if ( this.boundingBox !== null ) { + + this.computeBoundingBox(); + + } + + if ( this.boundingSphere !== null ) { + + this.computeBoundingSphere(); + + } + + this.verticesNeedUpdate = true; + this.normalsNeedUpdate = true; + + }, + + rotateX: function () { + + // rotate geometry around world x-axis + + var m1; + + return function rotateX( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationX( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + rotateY: function () { + + // rotate geometry around world y-axis + + var m1; + + return function rotateY( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationY( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + rotateZ: function () { + + // rotate geometry around world z-axis + + var m1; + + return function rotateZ( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationZ( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + translate: function () { + + // translate geometry + + var m1; + + return function translate( x, y, z ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeTranslation( x, y, z ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + scale: function () { + + // scale geometry + + var m1; + + return function scale( x, y, z ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeScale( x, y, z ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + lookAt: function () { + + var obj; + + return function lookAt( vector ) { + + if ( obj === undefined ) obj = new THREE.Object3D(); + + obj.lookAt( vector ); + + obj.updateMatrix(); + + this.applyMatrix( obj.matrix ); + + }; + + }(), + + fromBufferGeometry: function ( geometry ) { + + var scope = this; + + var indices = geometry.index !== null ? geometry.index.array : undefined; + var attributes = geometry.attributes; + + var vertices = attributes.position.array; + var normals = attributes.normal !== undefined ? attributes.normal.array : undefined; + var colors = attributes.color !== undefined ? attributes.color.array : undefined; + var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined; + var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined; + + if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = []; + + var tempNormals = []; + var tempUVs = []; + var tempUVs2 = []; + + for ( var i = 0, j = 0, k = 0; i < vertices.length; i += 3, j += 2, k += 4 ) { + + scope.vertices.push( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) ); + + if ( normals !== undefined ) { + + tempNormals.push( new THREE.Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) ); + + } + + if ( colors !== undefined ) { + + scope.colors.push( new THREE.Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) ); + + } + + if ( uvs !== undefined ) { + + tempUVs.push( new THREE.Vector2( uvs[ j ], uvs[ j + 1 ] ) ); + + } + + if ( uvs2 !== undefined ) { + + tempUVs2.push( new THREE.Vector2( uvs2[ j ], uvs2[ j + 1 ] ) ); + + } + + } + + function addFace( a, b, c ) { + + var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : []; + var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : []; + + var face = new THREE.Face3( a, b, c, vertexNormals, vertexColors ); + + scope.faces.push( face ); + + if ( uvs !== undefined ) { + + scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] ); + + } + + if ( uvs2 !== undefined ) { + + scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] ); + + } + + }; + + if ( indices !== undefined ) { + + var groups = geometry.groups; + + if ( groups.length > 0 ) { + + for ( var i = 0; i < groups.length; i ++ ) { + + var group = groups[ i ]; + + var start = group.start; + var count = group.count; + + for ( var j = start, jl = start + count; j < jl; j += 3 ) { + + addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ] ); + + } + + } + + } else { + + for ( var i = 0; i < indices.length; i += 3 ) { + + addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); + + } + + } + + } else { + + for ( var i = 0; i < vertices.length / 3; i += 3 ) { + + addFace( i, i + 1, i + 2 ); + + } + + } + + this.computeFaceNormals(); + + if ( geometry.boundingBox !== null ) { + + this.boundingBox = geometry.boundingBox.clone(); + + } + + if ( geometry.boundingSphere !== null ) { + + this.boundingSphere = geometry.boundingSphere.clone(); + + } + + return this; + + }, + + center: function () { + + this.computeBoundingBox(); + + var offset = this.boundingBox.center().negate(); + + this.translate( offset.x, offset.y, offset.z ); + + return offset; + + }, + + normalize: function () { + + this.computeBoundingSphere(); + + var center = this.boundingSphere.center; + var radius = this.boundingSphere.radius; + + var s = radius === 0 ? 1 : 1.0 / radius; + + var matrix = new THREE.Matrix4(); + matrix.set( + s, 0, 0, - s * center.x, + 0, s, 0, - s * center.y, + 0, 0, s, - s * center.z, + 0, 0, 0, 1 + ); + + this.applyMatrix( matrix ); + + return this; + + }, + + computeFaceNormals: function () { + + var cb = new THREE.Vector3(), ab = new THREE.Vector3(); + + for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { + + var face = this.faces[ f ]; + + var vA = this.vertices[ face.a ]; + var vB = this.vertices[ face.b ]; + var vC = this.vertices[ face.c ]; + + cb.subVectors( vC, vB ); + ab.subVectors( vA, vB ); + cb.cross( ab ); + + cb.normalize(); + + face.normal.copy( cb ); + + } + + }, + + computeVertexNormals: function ( areaWeighted ) { + + var v, vl, f, fl, face, vertices; + + vertices = new Array( this.vertices.length ); + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + vertices[ v ] = new THREE.Vector3(); + + } + + if ( areaWeighted ) { + + // vertex normals weighted by triangle areas + // http://www.iquilezles.org/www/articles/normals/normals.htm + + var vA, vB, vC; + var cb = new THREE.Vector3(), ab = new THREE.Vector3(); + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + vA = this.vertices[ face.a ]; + vB = this.vertices[ face.b ]; + vC = this.vertices[ face.c ]; + + cb.subVectors( vC, vB ); + ab.subVectors( vA, vB ); + cb.cross( ab ); + + vertices[ face.a ].add( cb ); + vertices[ face.b ].add( cb ); + vertices[ face.c ].add( cb ); + + } + + } else { + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + vertices[ face.a ].add( face.normal ); + vertices[ face.b ].add( face.normal ); + vertices[ face.c ].add( face.normal ); + + } + + } + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + vertices[ v ].normalize(); + + } + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + var vertexNormals = face.vertexNormals; + + if ( vertexNormals.length === 3 ) { + + vertexNormals[ 0 ].copy( vertices[ face.a ] ); + vertexNormals[ 1 ].copy( vertices[ face.b ] ); + vertexNormals[ 2 ].copy( vertices[ face.c ] ); + + } else { + + vertexNormals[ 0 ] = vertices[ face.a ].clone(); + vertexNormals[ 1 ] = vertices[ face.b ].clone(); + vertexNormals[ 2 ] = vertices[ face.c ].clone(); + + } + + } + + }, + + computeMorphNormals: function () { + + var i, il, f, fl, face; + + // save original normals + // - create temp variables on first access + // otherwise just copy (for faster repeated calls) + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + if ( ! face.__originalFaceNormal ) { + + face.__originalFaceNormal = face.normal.clone(); + + } else { + + face.__originalFaceNormal.copy( face.normal ); + + } + + if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; + + for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { + + if ( ! face.__originalVertexNormals[ i ] ) { + + face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); + + } else { + + face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); + + } + + } + + } + + // use temp geometry to compute face and vertex normals for each morph + + var tmpGeo = new THREE.Geometry(); + tmpGeo.faces = this.faces; + + for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { + + // create on first access + + if ( ! this.morphNormals[ i ] ) { + + this.morphNormals[ i ] = {}; + this.morphNormals[ i ].faceNormals = []; + this.morphNormals[ i ].vertexNormals = []; + + var dstNormalsFace = this.morphNormals[ i ].faceNormals; + var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; + + var faceNormal, vertexNormals; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + faceNormal = new THREE.Vector3(); + vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() }; + + dstNormalsFace.push( faceNormal ); + dstNormalsVertex.push( vertexNormals ); + + } + + } + + var morphNormals = this.morphNormals[ i ]; + + // set vertices to morph target + + tmpGeo.vertices = this.morphTargets[ i ].vertices; + + // compute morph normals + + tmpGeo.computeFaceNormals(); + tmpGeo.computeVertexNormals(); + + // store morph normals + + var faceNormal, vertexNormals; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + faceNormal = morphNormals.faceNormals[ f ]; + vertexNormals = morphNormals.vertexNormals[ f ]; + + faceNormal.copy( face.normal ); + + vertexNormals.a.copy( face.vertexNormals[ 0 ] ); + vertexNormals.b.copy( face.vertexNormals[ 1 ] ); + vertexNormals.c.copy( face.vertexNormals[ 2 ] ); + + } + + } + + // restore original normals + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + face.normal = face.__originalFaceNormal; + face.vertexNormals = face.__originalVertexNormals; + + } + + }, + + computeTangents: function () { + + console.warn( 'THREE.Geometry: .computeTangents() has been removed.' ); + + }, + + computeLineDistances: function () { + + var d = 0; + var vertices = this.vertices; + + for ( var i = 0, il = vertices.length; i < il; i ++ ) { + + if ( i > 0 ) { + + d += vertices[ i ].distanceTo( vertices[ i - 1 ] ); + + } + + this.lineDistances[ i ] = d; + + } + + }, + + computeBoundingBox: function () { + + if ( this.boundingBox === null ) { + + this.boundingBox = new THREE.Box3(); + + } + + this.boundingBox.setFromPoints( this.vertices ); + + }, + + computeBoundingSphere: function () { + + if ( this.boundingSphere === null ) { + + this.boundingSphere = new THREE.Sphere(); + + } + + this.boundingSphere.setFromPoints( this.vertices ); + + }, + + merge: function ( geometry, matrix, materialIndexOffset ) { + + if ( geometry instanceof THREE.Geometry === false ) { + + console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); + return; + + } + + var normalMatrix, + vertexOffset = this.vertices.length, + vertices1 = this.vertices, + vertices2 = geometry.vertices, + faces1 = this.faces, + faces2 = geometry.faces, + uvs1 = this.faceVertexUvs[ 0 ], + uvs2 = geometry.faceVertexUvs[ 0 ]; + + if ( materialIndexOffset === undefined ) materialIndexOffset = 0; + + if ( matrix !== undefined ) { + + normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + + } + + // vertices + + for ( var i = 0, il = vertices2.length; i < il; i ++ ) { + + var vertex = vertices2[ i ]; + + var vertexCopy = vertex.clone(); + + if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); + + vertices1.push( vertexCopy ); + + } + + // faces + + for ( i = 0, il = faces2.length; i < il; i ++ ) { + + var face = faces2[ i ], faceCopy, normal, color, + faceVertexNormals = face.vertexNormals, + faceVertexColors = face.vertexColors; + + faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); + faceCopy.normal.copy( face.normal ); + + if ( normalMatrix !== undefined ) { + + faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); + + } + + for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { + + normal = faceVertexNormals[ j ].clone(); + + if ( normalMatrix !== undefined ) { + + normal.applyMatrix3( normalMatrix ).normalize(); + + } + + faceCopy.vertexNormals.push( normal ); + + } + + faceCopy.color.copy( face.color ); + + for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { + + color = faceVertexColors[ j ]; + faceCopy.vertexColors.push( color.clone() ); + + } + + faceCopy.materialIndex = face.materialIndex + materialIndexOffset; + + faces1.push( faceCopy ); + + } + + // uvs + + for ( i = 0, il = uvs2.length; i < il; i ++ ) { + + var uv = uvs2[ i ], uvCopy = []; + + if ( uv === undefined ) { + + continue; + + } + + for ( var j = 0, jl = uv.length; j < jl; j ++ ) { + + uvCopy.push( uv[ j ].clone() ); + + } + + uvs1.push( uvCopy ); + + } + + }, + + mergeMesh: function ( mesh ) { + + if ( mesh instanceof THREE.Mesh === false ) { + + console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh ); + return; + + } + + mesh.matrixAutoUpdate && mesh.updateMatrix(); + + this.merge( mesh.geometry, mesh.matrix ); + + }, + + /* + * Checks for duplicate vertices with hashmap. + * Duplicated vertices are removed + * and faces' vertices are updated. + */ + + mergeVertices: function () { + + var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) + var unique = [], changes = []; + + var v, key; + var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 + var precision = Math.pow( 10, precisionPoints ); + var i, il, face; + var indices, j, jl; + + for ( i = 0, il = this.vertices.length; i < il; i ++ ) { + + v = this.vertices[ i ]; + key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); + + if ( verticesMap[ key ] === undefined ) { + + verticesMap[ key ] = i; + unique.push( this.vertices[ i ] ); + changes[ i ] = unique.length - 1; + + } else { + + //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); + changes[ i ] = changes[ verticesMap[ key ] ]; + + } + + } + + + // if faces are completely degenerate after merging vertices, we + // have to remove them from the geometry. + var faceIndicesToRemove = []; + + for ( i = 0, il = this.faces.length; i < il; i ++ ) { + + face = this.faces[ i ]; + + face.a = changes[ face.a ]; + face.b = changes[ face.b ]; + face.c = changes[ face.c ]; + + indices = [ face.a, face.b, face.c ]; + + var dupIndex = - 1; + + // if any duplicate vertices are found in a Face3 + // we have to remove the face as nothing can be saved + for ( var n = 0; n < 3; n ++ ) { + + if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { + + dupIndex = n; + faceIndicesToRemove.push( i ); + break; + + } + + } + + } + + for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { + + var idx = faceIndicesToRemove[ i ]; + + this.faces.splice( idx, 1 ); + + for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { + + this.faceVertexUvs[ j ].splice( idx, 1 ); + + } + + } + + // Use unique set of vertices + + var diff = this.vertices.length - unique.length; + this.vertices = unique; + return diff; + + }, + + sortFacesByMaterialIndex: function () { + + var faces = this.faces; + var length = faces.length; + + // tag faces + + for ( var i = 0; i < length; i ++ ) { + + faces[ i ]._id = i; + + } + + // sort faces + + function materialIndexSort( a, b ) { + + return a.materialIndex - b.materialIndex; + + } + + faces.sort( materialIndexSort ); + + // sort uvs + + var uvs1 = this.faceVertexUvs[ 0 ]; + var uvs2 = this.faceVertexUvs[ 1 ]; + + var newUvs1, newUvs2; + + if ( uvs1 && uvs1.length === length ) newUvs1 = []; + if ( uvs2 && uvs2.length === length ) newUvs2 = []; + + for ( var i = 0; i < length; i ++ ) { + + var id = faces[ i ]._id; + + if ( newUvs1 ) newUvs1.push( uvs1[ id ] ); + if ( newUvs2 ) newUvs2.push( uvs2[ id ] ); + + } + + if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1; + if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2; + + }, + + toJSON: function () { + + var data = { + metadata: { + version: 4.4, + type: 'Geometry', + generator: 'Geometry.toJSON' + } + }; + + // standard Geometry serialization + + data.uuid = this.uuid; + data.type = this.type; + if ( this.name !== '' ) data.name = this.name; + + if ( this.parameters !== undefined ) { + + var parameters = this.parameters; + + for ( var key in parameters ) { + + if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; + + } + + return data; + + } + + var vertices = []; + + for ( var i = 0; i < this.vertices.length; i ++ ) { + + var vertex = this.vertices[ i ]; + vertices.push( vertex.x, vertex.y, vertex.z ); + + } + + var faces = []; + var normals = []; + var normalsHash = {}; + var colors = []; + var colorsHash = {}; + var uvs = []; + var uvsHash = {}; + + for ( var i = 0; i < this.faces.length; i ++ ) { + + var face = this.faces[ i ]; + + var hasMaterial = false; // face.materialIndex !== undefined; + var hasFaceUv = false; // deprecated + var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; + var hasFaceNormal = face.normal.length() > 0; + var hasFaceVertexNormal = face.vertexNormals.length > 0; + var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; + var hasFaceVertexColor = face.vertexColors.length > 0; + + var faceType = 0; + + faceType = setBit( faceType, 0, 0 ); + faceType = setBit( faceType, 1, hasMaterial ); + faceType = setBit( faceType, 2, hasFaceUv ); + faceType = setBit( faceType, 3, hasFaceVertexUv ); + faceType = setBit( faceType, 4, hasFaceNormal ); + faceType = setBit( faceType, 5, hasFaceVertexNormal ); + faceType = setBit( faceType, 6, hasFaceColor ); + faceType = setBit( faceType, 7, hasFaceVertexColor ); + + faces.push( faceType ); + faces.push( face.a, face.b, face.c ); + + if ( hasFaceVertexUv ) { + + var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; + + faces.push( + getUvIndex( faceVertexUvs[ 0 ] ), + getUvIndex( faceVertexUvs[ 1 ] ), + getUvIndex( faceVertexUvs[ 2 ] ) + ); + + } + + if ( hasFaceNormal ) { + + faces.push( getNormalIndex( face.normal ) ); + + } + + if ( hasFaceVertexNormal ) { + + var vertexNormals = face.vertexNormals; + + faces.push( + getNormalIndex( vertexNormals[ 0 ] ), + getNormalIndex( vertexNormals[ 1 ] ), + getNormalIndex( vertexNormals[ 2 ] ) + ); + + } + + if ( hasFaceColor ) { + + faces.push( getColorIndex( face.color ) ); + + } + + if ( hasFaceVertexColor ) { + + var vertexColors = face.vertexColors; + + faces.push( + getColorIndex( vertexColors[ 0 ] ), + getColorIndex( vertexColors[ 1 ] ), + getColorIndex( vertexColors[ 2 ] ) + ); + + } + + } + + function setBit( value, position, enabled ) { + + return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); + + } + + function getNormalIndex( normal ) { + + var hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); + + if ( normalsHash[ hash ] !== undefined ) { + + return normalsHash[ hash ]; + + } + + normalsHash[ hash ] = normals.length / 3; + normals.push( normal.x, normal.y, normal.z ); + + return normalsHash[ hash ]; + + } + + function getColorIndex( color ) { + + var hash = color.r.toString() + color.g.toString() + color.b.toString(); + + if ( colorsHash[ hash ] !== undefined ) { + + return colorsHash[ hash ]; + + } + + colorsHash[ hash ] = colors.length; + colors.push( color.getHex() ); + + return colorsHash[ hash ]; + + } + + function getUvIndex( uv ) { + + var hash = uv.x.toString() + uv.y.toString(); + + if ( uvsHash[ hash ] !== undefined ) { + + return uvsHash[ hash ]; + + } + + uvsHash[ hash ] = uvs.length / 2; + uvs.push( uv.x, uv.y ); + + return uvsHash[ hash ]; + + } + + data.data = {}; + + data.data.vertices = vertices; + data.data.normals = normals; + if ( colors.length > 0 ) data.data.colors = colors; + if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility + data.data.faces = faces; + + return data; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( source ) { + + this.vertices = []; + this.faces = []; + this.faceVertexUvs = [ [] ]; + + var vertices = source.vertices; + + for ( var i = 0, il = vertices.length; i < il; i ++ ) { + + this.vertices.push( vertices[ i ].clone() ); + + } + + var faces = source.faces; + + for ( var i = 0, il = faces.length; i < il; i ++ ) { + + this.faces.push( faces[ i ].clone() ); + + } + + for ( var i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { + + var faceVertexUvs = source.faceVertexUvs[ i ]; + + if ( this.faceVertexUvs[ i ] === undefined ) { + + this.faceVertexUvs[ i ] = []; + + } + + for ( var j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { + + var uvs = faceVertexUvs[ j ], uvsCopy = []; + + for ( var k = 0, kl = uvs.length; k < kl; k ++ ) { + + var uv = uvs[ k ]; + + uvsCopy.push( uv.clone() ); + + } + + this.faceVertexUvs[ i ].push( uvsCopy ); + + } + + } + + return this; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Geometry.prototype ); + +THREE.GeometryIdCount = 0; + +// File:src/core/DirectGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.DirectGeometry = function () { + + Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.type = 'DirectGeometry'; + + this.indices = []; + this.vertices = []; + this.normals = []; + this.colors = []; + this.uvs = []; + this.uvs2 = []; + + this.groups = []; + + this.morphTargets = {}; + + this.skinWeights = []; + this.skinIndices = []; + + // this.lineDistances = []; + + this.boundingBox = null; + this.boundingSphere = null; + + // update flags + + this.verticesNeedUpdate = false; + this.normalsNeedUpdate = false; + this.colorsNeedUpdate = false; + this.uvsNeedUpdate = false; + this.groupsNeedUpdate = false; + +}; + +THREE.DirectGeometry.prototype = { + + constructor: THREE.DirectGeometry, + + computeBoundingBox: THREE.Geometry.prototype.computeBoundingBox, + computeBoundingSphere: THREE.Geometry.prototype.computeBoundingSphere, + + computeFaceNormals: function () { + + console.warn( 'THREE.DirectGeometry: computeFaceNormals() is not a method of this type of geometry.' ); + + }, + + computeVertexNormals: function () { + + console.warn( 'THREE.DirectGeometry: computeVertexNormals() is not a method of this type of geometry.' ); + + }, + + computeGroups: function ( geometry ) { + + var group; + var groups = []; + var materialIndex; + + var faces = geometry.faces; + + for ( var i = 0; i < faces.length; i ++ ) { + + var face = faces[ i ]; + + // materials + + if ( face.materialIndex !== materialIndex ) { + + materialIndex = face.materialIndex; + + if ( group !== undefined ) { + + group.count = ( i * 3 ) - group.start; + groups.push( group ); + + } + + group = { + start: i * 3, + materialIndex: materialIndex + }; + + } + + } + + if ( group !== undefined ) { + + group.count = ( i * 3 ) - group.start; + groups.push( group ); + + } + + this.groups = groups; + + }, + + fromGeometry: function ( geometry ) { + + var faces = geometry.faces; + var vertices = geometry.vertices; + var faceVertexUvs = geometry.faceVertexUvs; + + var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; + var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; + + // morphs + + var morphTargets = geometry.morphTargets; + var morphTargetsLength = morphTargets.length; + + if ( morphTargetsLength > 0 ) { + + var morphTargetsPosition = []; + + for ( var i = 0; i < morphTargetsLength; i ++ ) { + + morphTargetsPosition[ i ] = []; + + } + + this.morphTargets.position = morphTargetsPosition; + + } + + var morphNormals = geometry.morphNormals; + var morphNormalsLength = morphNormals.length; + + if ( morphNormalsLength > 0 ) { + + var morphTargetsNormal = []; + + for ( var i = 0; i < morphNormalsLength; i ++ ) { + + morphTargetsNormal[ i ] = []; + + } + + this.morphTargets.normal = morphTargetsNormal; + + } + + // skins + + var skinIndices = geometry.skinIndices; + var skinWeights = geometry.skinWeights; + + var hasSkinIndices = skinIndices.length === vertices.length; + var hasSkinWeights = skinWeights.length === vertices.length; + + // + + for ( var i = 0; i < faces.length; i ++ ) { + + var face = faces[ i ]; + + this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); + + var vertexNormals = face.vertexNormals; + + if ( vertexNormals.length === 3 ) { + + this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); + + } else { + + var normal = face.normal; + + this.normals.push( normal, normal, normal ); + + } + + var vertexColors = face.vertexColors; + + if ( vertexColors.length === 3 ) { + + this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); + + } else { + + var color = face.color; + + this.colors.push( color, color, color ); + + } + + if ( hasFaceVertexUv === true ) { + + var vertexUvs = faceVertexUvs[ 0 ][ i ]; + + if ( vertexUvs !== undefined ) { + + this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); + + } else { + + console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i ); + + this.uvs.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ); + + } + + } + + if ( hasFaceVertexUv2 === true ) { + + var vertexUvs = faceVertexUvs[ 1 ][ i ]; + + if ( vertexUvs !== undefined ) { + + this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); + + } else { + + console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i ); + + this.uvs2.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ); + + } + + } + + // morphs + + for ( var j = 0; j < morphTargetsLength; j ++ ) { + + var morphTarget = morphTargets[ j ].vertices; + + morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); + + } + + for ( var j = 0; j < morphNormalsLength; j ++ ) { + + var morphNormal = morphNormals[ j ].vertexNormals[ i ]; + + morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c ); + + } + + // skins + + if ( hasSkinIndices ) { + + this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); + + } + + if ( hasSkinWeights ) { + + this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); + + } + + } + + this.computeGroups( geometry ); + + this.verticesNeedUpdate = geometry.verticesNeedUpdate; + this.normalsNeedUpdate = geometry.normalsNeedUpdate; + this.colorsNeedUpdate = geometry.colorsNeedUpdate; + this.uvsNeedUpdate = geometry.uvsNeedUpdate; + this.groupsNeedUpdate = geometry.groupsNeedUpdate; + + return this; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.DirectGeometry.prototype ); + +// File:src/core/BufferGeometry.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BufferGeometry = function () { + + Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.type = 'BufferGeometry'; + + this.index = null; + this.attributes = {}; + + this.morphAttributes = {}; + + this.groups = []; + + this.boundingBox = null; + this.boundingSphere = null; + + this.drawRange = { start: 0, count: Infinity }; + +}; + +THREE.BufferGeometry.prototype = { + + constructor: THREE.BufferGeometry, + + addIndex: function ( index ) { + + console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); + this.setIndex( index ); + + }, + + getIndex: function () { + + return this.index; + + }, + + setIndex: function ( index ) { + + this.index = index; + + }, + + addAttribute: function ( name, attribute ) { + + if ( attribute instanceof THREE.BufferAttribute === false && attribute instanceof THREE.InterleavedBufferAttribute === false ) { + + console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); + + this.addAttribute( name, new THREE.BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); + + return; + + } + + if ( name === 'index' ) { + + console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); + this.setIndex( attribute ); + + return; + + } + + this.attributes[ name ] = attribute; + + }, + + getAttribute: function ( name ) { + + return this.attributes[ name ]; + + }, + + removeAttribute: function ( name ) { + + delete this.attributes[ name ]; + + }, + + get drawcalls() { + + console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' ); + return this.groups; + + }, + + get offsets() { + + console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' ); + return this.groups; + + }, + + addDrawCall: function ( start, count, indexOffset ) { + + if ( indexOffset !== undefined ) { + + console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); + + } + + console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); + this.addGroup( start, count ); + + }, + + clearDrawCalls: function () { + + console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); + this.clearGroups(); + + }, + + addGroup: function ( start, count, materialIndex ) { + + this.groups.push( { + + start: start, + count: count, + materialIndex: materialIndex !== undefined ? materialIndex : 0 + + } ); + + }, + + clearGroups: function () { + + this.groups = []; + + }, + + setDrawRange: function ( start, count ) { + + this.drawRange.start = start; + this.drawRange.count = count; + + }, + + applyMatrix: function ( matrix ) { + + var position = this.attributes.position; + + if ( position !== undefined ) { + + matrix.applyToVector3Array( position.array ); + position.needsUpdate = true; + + } + + var normal = this.attributes.normal; + + if ( normal !== undefined ) { + + var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + + normalMatrix.applyToVector3Array( normal.array ); + normal.needsUpdate = true; + + } + + if ( this.boundingBox !== null ) { + + this.computeBoundingBox(); + + } + + if ( this.boundingSphere !== null ) { + + this.computeBoundingSphere(); + + } + + }, + + rotateX: function () { + + // rotate geometry around world x-axis + + var m1; + + return function rotateX( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationX( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + rotateY: function () { + + // rotate geometry around world y-axis + + var m1; + + return function rotateY( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationY( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + rotateZ: function () { + + // rotate geometry around world z-axis + + var m1; + + return function rotateZ( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationZ( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + translate: function () { + + // translate geometry + + var m1; + + return function translate( x, y, z ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeTranslation( x, y, z ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + scale: function () { + + // scale geometry + + var m1; + + return function scale( x, y, z ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeScale( x, y, z ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + lookAt: function () { + + var obj; + + return function lookAt( vector ) { + + if ( obj === undefined ) obj = new THREE.Object3D(); + + obj.lookAt( vector ); + + obj.updateMatrix(); + + this.applyMatrix( obj.matrix ); + + }; + + }(), + + center: function () { + + this.computeBoundingBox(); + + var offset = this.boundingBox.center().negate(); + + this.translate( offset.x, offset.y, offset.z ); + + return offset; + + }, + + setFromObject: function ( object ) { + + // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this ); + + var geometry = object.geometry; + + if ( object instanceof THREE.Points || object instanceof THREE.Line ) { + + var positions = new THREE.Float32Attribute( geometry.vertices.length * 3, 3 ); + var colors = new THREE.Float32Attribute( geometry.colors.length * 3, 3 ); + + this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) ); + this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) ); + + if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { + + var lineDistances = new THREE.Float32Attribute( geometry.lineDistances.length, 1 ); + + this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) ); + + } + + if ( geometry.boundingSphere !== null ) { + + this.boundingSphere = geometry.boundingSphere.clone(); + + } + + if ( geometry.boundingBox !== null ) { + + this.boundingBox = geometry.boundingBox.clone(); + + } + + } else if ( object instanceof THREE.Mesh ) { + + if ( geometry instanceof THREE.Geometry ) { + + this.fromGeometry( geometry ); + + } + + } + + return this; + + }, + + updateFromObject: function ( object ) { + + var geometry = object.geometry; + + if ( object instanceof THREE.Mesh ) { + + var direct = geometry.__directGeometry; + + if ( direct === undefined ) { + + return this.fromGeometry( geometry ); + + } + + direct.verticesNeedUpdate = geometry.verticesNeedUpdate; + direct.normalsNeedUpdate = geometry.normalsNeedUpdate; + direct.colorsNeedUpdate = geometry.colorsNeedUpdate; + direct.uvsNeedUpdate = geometry.uvsNeedUpdate; + direct.groupsNeedUpdate = geometry.groupsNeedUpdate; + + geometry.verticesNeedUpdate = false; + geometry.normalsNeedUpdate = false; + geometry.colorsNeedUpdate = false; + geometry.uvsNeedUpdate = false; + geometry.groupsNeedUpdate = false; + + geometry = direct; + + } + + if ( geometry.verticesNeedUpdate === true ) { + + var attribute = this.attributes.position; + + if ( attribute !== undefined ) { + + attribute.copyVector3sArray( geometry.vertices ); + attribute.needsUpdate = true; + + } + + geometry.verticesNeedUpdate = false; + + } + + if ( geometry.normalsNeedUpdate === true ) { + + var attribute = this.attributes.normal; + + if ( attribute !== undefined ) { + + attribute.copyVector3sArray( geometry.normals ); + attribute.needsUpdate = true; + + } + + geometry.normalsNeedUpdate = false; + + } + + if ( geometry.colorsNeedUpdate === true ) { + + var attribute = this.attributes.color; + + if ( attribute !== undefined ) { + + attribute.copyColorsArray( geometry.colors ); + attribute.needsUpdate = true; + + } + + geometry.colorsNeedUpdate = false; + + } + + if ( geometry.uvsNeedUpdate ) { + + var attribute = this.attributes.uv; + + if ( attribute !== undefined ) { + + attribute.copyVector2sArray( geometry.uvs ); + attribute.needsUpdate = true; + + } + + geometry.uvsNeedUpdate = false; + + } + + if ( geometry.lineDistancesNeedUpdate ) { + + var attribute = this.attributes.lineDistance; + + if ( attribute !== undefined ) { + + attribute.copyArray( geometry.lineDistances ); + attribute.needsUpdate = true; + + } + + geometry.lineDistancesNeedUpdate = false; + + } + + if ( geometry.groupsNeedUpdate ) { + + geometry.computeGroups( object.geometry ); + this.groups = geometry.groups; + + geometry.groupsNeedUpdate = false; + + } + + return this; + + }, + + fromGeometry: function ( geometry ) { + + geometry.__directGeometry = new THREE.DirectGeometry().fromGeometry( geometry ); + + return this.fromDirectGeometry( geometry.__directGeometry ); + + }, + + fromDirectGeometry: function ( geometry ) { + + var positions = new Float32Array( geometry.vertices.length * 3 ); + this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); + + if ( geometry.normals.length > 0 ) { + + var normals = new Float32Array( geometry.normals.length * 3 ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); + + } + + if ( geometry.colors.length > 0 ) { + + var colors = new Float32Array( geometry.colors.length * 3 ); + this.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); + + } + + if ( geometry.uvs.length > 0 ) { + + var uvs = new Float32Array( geometry.uvs.length * 2 ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); + + } + + if ( geometry.uvs2.length > 0 ) { + + var uvs2 = new Float32Array( geometry.uvs2.length * 2 ); + this.addAttribute( 'uv2', new THREE.BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); + + } + + if ( geometry.indices.length > 0 ) { + + var TypeArray = geometry.vertices.length > 65535 ? Uint32Array : Uint16Array; + var indices = new TypeArray( geometry.indices.length * 3 ); + this.setIndex( new THREE.BufferAttribute( indices, 1 ).copyIndicesArray( geometry.indices ) ); + + } + + // groups + + this.groups = geometry.groups; + + // morphs + + for ( var name in geometry.morphTargets ) { + + var array = []; + var morphTargets = geometry.morphTargets[ name ]; + + for ( var i = 0, l = morphTargets.length; i < l; i ++ ) { + + var morphTarget = morphTargets[ i ]; + + var attribute = new THREE.Float32Attribute( morphTarget.length * 3, 3 ); + + array.push( attribute.copyVector3sArray( morphTarget ) ); + + } + + this.morphAttributes[ name ] = array; + + } + + // skinning + + if ( geometry.skinIndices.length > 0 ) { + + var skinIndices = new THREE.Float32Attribute( geometry.skinIndices.length * 4, 4 ); + this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) ); + + } + + if ( geometry.skinWeights.length > 0 ) { + + var skinWeights = new THREE.Float32Attribute( geometry.skinWeights.length * 4, 4 ); + this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) ); + + } + + // + + if ( geometry.boundingSphere !== null ) { + + this.boundingSphere = geometry.boundingSphere.clone(); + + } + + if ( geometry.boundingBox !== null ) { + + this.boundingBox = geometry.boundingBox.clone(); + + } + + return this; + + }, + + computeBoundingBox: function () { + + var vector = new THREE.Vector3(); + + return function () { + + if ( this.boundingBox === null ) { + + this.boundingBox = new THREE.Box3(); + + } + + var positions = this.attributes.position.array; + + if ( positions ) { + + var bb = this.boundingBox; + bb.makeEmpty(); + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + vector.fromArray( positions, i ); + bb.expandByPoint( vector ); + + } + + } + + if ( positions === undefined || positions.length === 0 ) { + + this.boundingBox.min.set( 0, 0, 0 ); + this.boundingBox.max.set( 0, 0, 0 ); + + } + + if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { + + console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); + + } + + }; + + }(), + + computeBoundingSphere: function () { + + var box = new THREE.Box3(); + var vector = new THREE.Vector3(); + + return function () { + + if ( this.boundingSphere === null ) { + + this.boundingSphere = new THREE.Sphere(); + + } + + var positions = this.attributes.position.array; + + if ( positions ) { + + box.makeEmpty(); + + var center = this.boundingSphere.center; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + vector.fromArray( positions, i ); + box.expandByPoint( vector ); + + } + + box.center( center ); + + // hoping to find a boundingSphere with a radius smaller than the + // boundingSphere of the boundingBox: sqrt(3) smaller in the best case + + var maxRadiusSq = 0; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + vector.fromArray( positions, i ); + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); + + } + + this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); + + if ( isNaN( this.boundingSphere.radius ) ) { + + console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); + + } + + } + + }; + + }(), + + computeFaceNormals: function () { + + // backwards compatibility + + }, + + computeVertexNormals: function () { + + var index = this.index; + var attributes = this.attributes; + var groups = this.groups; + + if ( attributes.position ) { + + var positions = attributes.position.array; + + if ( attributes.normal === undefined ) { + + this.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( positions.length ), 3 ) ); + + } else { + + // reset existing normals to zero + + var normals = attributes.normal.array; + + for ( var i = 0, il = normals.length; i < il; i ++ ) { + + normals[ i ] = 0; + + } + + } + + var normals = attributes.normal.array; + + var vA, vB, vC, + + pA = new THREE.Vector3(), + pB = new THREE.Vector3(), + pC = new THREE.Vector3(), + + cb = new THREE.Vector3(), + ab = new THREE.Vector3(); + + // indexed elements + + if ( index ) { + + var indices = index.array; + + if ( groups.length === 0 ) { + + this.addGroup( 0, indices.length ); + + } + + for ( var j = 0, jl = groups.length; j < jl; ++ j ) { + + var group = groups[ j ]; + + var start = group.start; + var count = group.count; + + for ( var i = start, il = start + count; i < il; i += 3 ) { + + vA = indices[ i + 0 ] * 3; + vB = indices[ i + 1 ] * 3; + vC = indices[ i + 2 ] * 3; + + pA.fromArray( positions, vA ); + pB.fromArray( positions, vB ); + pC.fromArray( positions, vC ); + + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); + + normals[ vA ] += cb.x; + normals[ vA + 1 ] += cb.y; + normals[ vA + 2 ] += cb.z; + + normals[ vB ] += cb.x; + normals[ vB + 1 ] += cb.y; + normals[ vB + 2 ] += cb.z; + + normals[ vC ] += cb.x; + normals[ vC + 1 ] += cb.y; + normals[ vC + 2 ] += cb.z; + + } + + } + + } else { + + // non-indexed elements (unconnected triangle soup) + + for ( var i = 0, il = positions.length; i < il; i += 9 ) { + + pA.fromArray( positions, i ); + pB.fromArray( positions, i + 3 ); + pC.fromArray( positions, i + 6 ); + + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); + + normals[ i ] = cb.x; + normals[ i + 1 ] = cb.y; + normals[ i + 2 ] = cb.z; + + normals[ i + 3 ] = cb.x; + normals[ i + 4 ] = cb.y; + normals[ i + 5 ] = cb.z; + + normals[ i + 6 ] = cb.x; + normals[ i + 7 ] = cb.y; + normals[ i + 8 ] = cb.z; + + } + + } + + this.normalizeNormals(); + + attributes.normal.needsUpdate = true; + + } + + }, + + computeTangents: function () { + + console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' ); + + }, + + computeOffsets: function ( size ) { + + console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.') + + }, + + merge: function ( geometry, offset ) { + + if ( geometry instanceof THREE.BufferGeometry === false ) { + + console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry ); + return; + + } + + if ( offset === undefined ) offset = 0; + + var attributes = this.attributes; + + for ( var key in attributes ) { + + if ( geometry.attributes[ key ] === undefined ) continue; + + var attribute1 = attributes[ key ]; + var attributeArray1 = attribute1.array; + + var attribute2 = geometry.attributes[ key ]; + var attributeArray2 = attribute2.array; + + var attributeSize = attribute2.itemSize; + + for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) { + + attributeArray1[ j ] = attributeArray2[ i ]; + + } + + } + + return this; + + }, + + normalizeNormals: function () { + + var normals = this.attributes.normal.array; + + var x, y, z, n; + + for ( var i = 0, il = normals.length; i < il; i += 3 ) { + + x = normals[ i ]; + y = normals[ i + 1 ]; + z = normals[ i + 2 ]; + + n = 1.0 / Math.sqrt( x * x + y * y + z * z ); + + normals[ i ] *= n; + normals[ i + 1 ] *= n; + normals[ i + 2 ] *= n; + + } + + }, + + toJSON: function () { + + var data = { + metadata: { + version: 4.4, + type: 'BufferGeometry', + generator: 'BufferGeometry.toJSON' + } + }; + + // standard BufferGeometry serialization + + data.uuid = this.uuid; + data.type = this.type; + if ( this.name !== '' ) data.name = this.name; + + if ( this.parameters !== undefined ) { + + var parameters = this.parameters; + + for ( var key in parameters ) { + + if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; + + } + + return data; + + } + + data.data = { attributes: {} }; + + var index = this.index; + + if ( index !== null ) { + + var array = Array.prototype.slice.call( index.array ); + + data.data.index = { + type: index.array.constructor.name, + array: array + }; + + } + + var attributes = this.attributes; + + for ( var key in attributes ) { + + var attribute = attributes[ key ]; + + var array = Array.prototype.slice.call( attribute.array ); + + data.data.attributes[ key ] = { + itemSize: attribute.itemSize, + type: attribute.array.constructor.name, + array: array + }; + + } + + var groups = this.groups; + + if ( groups.length > 0 ) { + + data.data.groups = JSON.parse( JSON.stringify( groups ) ); + + } + + var boundingSphere = this.boundingSphere; + + if ( boundingSphere !== null ) { + + data.data.boundingSphere = { + center: boundingSphere.center.toArray(), + radius: boundingSphere.radius + }; + + } + + return data; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( source ) { + + var index = source.index; + + if ( index !== null ) { + + this.setIndex( index.clone() ); + + } + + var attributes = source.attributes; + + for ( var name in attributes ) { + + var attribute = attributes[ name ]; + this.addAttribute( name, attribute.clone() ); + + } + + var groups = source.groups; + + for ( var i = 0, l = groups.length; i < l; i ++ ) { + + var group = groups[ i ]; + this.addGroup( group.start, group.count ); + + } + + return this; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype ); + +THREE.BufferGeometry.MaxIndex = 65535; + +// File:src/core/InstancedBufferGeometry.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.InstancedBufferGeometry = function () { + + THREE.BufferGeometry.call( this ); + + this.type = 'InstancedBufferGeometry'; + this.maxInstancedCount = undefined; + +}; + +THREE.InstancedBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.InstancedBufferGeometry.prototype.constructor = THREE.InstancedBufferGeometry; + +THREE.InstancedBufferGeometry.prototype.addGroup = function ( start, count, instances ) { + + this.groups.push( { + + start: start, + count: count, + instances: instances + + } ); + +}; + +THREE.InstancedBufferGeometry.prototype.copy = function ( source ) { + + var index = source.index; + + if ( index !== null ) { + + this.setIndex( index.clone() ); + + } + + var attributes = source.attributes; + + for ( var name in attributes ) { + + var attribute = attributes[ name ]; + this.addAttribute( name, attribute.clone() ); + + } + + var groups = source.groups; + + for ( var i = 0, l = groups.length; i < l; i ++ ) { + + var group = groups[ i ]; + this.addGroup( group.start, group.count, group.instances ); + + } + + return this; + +}; + +THREE.EventDispatcher.prototype.apply( THREE.InstancedBufferGeometry.prototype ); + +// File:src/animation/AnimationAction.js + +/** + * + * A clip that has been explicitly scheduled. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.AnimationAction = function ( clip, startTime, timeScale, weight, loop ) { + + if ( clip === undefined ) throw new Error( 'clip is null' ); + this.clip = clip; + this.localRoot = null; + this.startTime = startTime || 0; + this.timeScale = timeScale || 1; + this.weight = weight || 1; + this.loop = loop || THREE.LoopRepeat; + this.loopCount = 0; + this.enabled = true; // allow for easy disabling of the action. + + this.actionTime = - this.startTime; + this.clipTime = 0; + + this.propertyBindings = []; +}; + +/* +THREE.LoopOnce = 2200; +THREE.LoopRepeat = 2201; +THREE.LoopPingPing = 2202; +*/ + +THREE.AnimationAction.prototype = { + + constructor: THREE.AnimationAction, + + setLocalRoot: function( localRoot ) { + + this.localRoot = localRoot; + + return this; + + }, + + updateTime: function( clipDeltaTime ) { + + var previousClipTime = this.clipTime; + var previousLoopCount = this.loopCount; + var previousActionTime = this.actionTime; + + var duration = this.clip.duration; + + this.actionTime = this.actionTime + clipDeltaTime; + + if ( this.loop === THREE.LoopOnce ) { + + this.loopCount = 0; + this.clipTime = Math.min( Math.max( this.actionTime, 0 ), duration ); + + // if time is changed since last time, see if we have hit a start/end limit + if ( this.clipTime !== previousClipTime ) { + + if ( this.clipTime === duration ) { + + this.mixer.dispatchEvent( { type: 'finished', action: this, direction: 1 } ); + + } else if ( this.clipTime === 0 ) { + + this.mixer.dispatchEvent( { type: 'finished', action: this, direction: -1 } ); + + } + + } + + + return this.clipTime; + + } + + this.loopCount = Math.floor( this.actionTime / duration ); + + var newClipTime = this.actionTime - this.loopCount * duration; + newClipTime = newClipTime % duration; + + // if we are ping pong looping, ensure that we go backwards when appropriate + if ( this.loop == THREE.LoopPingPong ) { + + if ( Math.abs( this.loopCount % 2 ) === 1 ) { + + newClipTime = duration - newClipTime; + + } + + } + + this.clipTime = newClipTime; + + if ( this.loopCount !== previousLoopCount ) { + + this.mixer.dispatchEvent( { type: 'loop', action: this, loopDelta: ( this.loopCount - this.loopCount ) } ); + + } + + return this.clipTime; + + }, + + syncWith: function( action ) { + + this.actionTime = action.actionTime; + this.timeScale = action.timeScale; + + return this; + }, + + warpToDuration: function( duration ) { + + this.timeScale = this.clip.duration / duration; + + return this; + }, + + init: function( time ) { + + this.clipTime = time - this.startTime; + + return this; + + }, + + update: function( clipDeltaTime ) { + + this.updateTime( clipDeltaTime ); + + var clipResults = this.clip.getAt( this.clipTime ); + + return clipResults; + + }, + + getTimeScaleAt: function( time ) { + + if ( this.timeScale.getAt ) { + // pass in time, not clip time, allows for fadein/fadeout across multiple loops of the clip + return this.timeScale.getAt( time ); + + } + + return this.timeScale; + + }, + + getWeightAt: function( time ) { + + if ( this.weight.getAt ) { + // pass in time, not clip time, allows for fadein/fadeout across multiple loops of the clip + return this.weight.getAt( time ); + + } + + return this.weight; + + } + +}; + +// File:src/animation/AnimationClip.js + +/** + * + * Reusable set of Tracks that represent an animation. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.AnimationClip = function ( name, duration, tracks ) { + + this.name = name; + this.tracks = tracks; + this.duration = ( duration !== undefined ) ? duration : -1; + + // this means it should figure out its duration by scanning the tracks + if ( this.duration < 0 ) { + for ( var i = 0; i < this.tracks.length; i ++ ) { + var track = this.tracks[i]; + this.duration = Math.max( track.keys[ track.keys.length - 1 ].time ); + } + } + + // maybe only do these on demand, as doing them here could potentially slow down loading + // but leaving these here during development as this ensures a lot of testing of these functions + this.trim(); + this.optimize(); + + this.results = []; + +}; + +THREE.AnimationClip.prototype = { + + constructor: THREE.AnimationClip, + + getAt: function( clipTime ) { + + clipTime = Math.max( 0, Math.min( clipTime, this.duration ) ); + + for ( var i = 0; i < this.tracks.length; i ++ ) { + + var track = this.tracks[ i ]; + + this.results[ i ] = track.getAt( clipTime ); + + } + + return this.results; + }, + + trim: function() { + + for ( var i = 0; i < this.tracks.length; i ++ ) { + + this.tracks[ i ].trim( 0, this.duration ); + + } + + return this; + + }, + + optimize: function() { + + for ( var i = 0; i < this.tracks.length; i ++ ) { + + this.tracks[ i ].optimize(); + + } + + return this; + + } + +}; + + +THREE.AnimationClip.CreateFromMorphTargetSequence = function( name, morphTargetSequence, fps ) { + + + var numMorphTargets = morphTargetSequence.length; + var tracks = []; + + for ( var i = 0; i < numMorphTargets; i ++ ) { + + var keys = []; + + keys.push( { time: ( i + numMorphTargets - 1 ) % numMorphTargets, value: 0 } ); + keys.push( { time: i, value: 1 } ); + keys.push( { time: ( i + 1 ) % numMorphTargets, value: 0 } ); + + keys.sort( THREE.KeyframeTrack.keyComparer ); + + // if there is a key at the first frame, duplicate it as the last frame as well for perfect loop. + if ( keys[0].time === 0 ) { + keys.push( { + time: numMorphTargets, + value: keys[0].value + }); + } + + tracks.push( new THREE.NumberKeyframeTrack( '.morphTargetInfluences[' + morphTargetSequence[i].name + ']', keys ).scale( 1.0 / fps ) ); + } + + return new THREE.AnimationClip( name, -1, tracks ); + +}; + +THREE.AnimationClip.findByName = function( clipArray, name ) { + + for ( var i = 0; i < clipArray.length; i ++ ) { + + if ( clipArray[i].name === name ) { + + return clipArray[i]; + + } + } + + return null; + +}; + +THREE.AnimationClip.CreateClipsFromMorphTargetSequences = function( morphTargets, fps ) { + + var animationToMorphTargets = {}; + + // tested with https://regex101.com/ on trick sequences such flamingo_flyA_003, flamingo_run1_003, crdeath0059 + var pattern = /^([\w-]*?)([\d]+)$/; + + // sort morph target names into animation groups based patterns like Walk_001, Walk_002, Run_001, Run_002 + for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { + + var morphTarget = morphTargets[ i ]; + var parts = morphTarget.name.match( pattern ); + + if ( parts && parts.length > 1 ) { + + var name = parts[ 1 ]; + + var animationMorphTargets = animationToMorphTargets[ name ]; + if ( ! animationMorphTargets ) { + animationToMorphTargets[ name ] = animationMorphTargets = []; + } + + animationMorphTargets.push( morphTarget ); + + } + + } + + var clips = []; + + for ( var name in animationToMorphTargets ) { + + clips.push( THREE.AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps ) ); + } + + return clips; + +}; + +// parse the standard JSON format for clips +THREE.AnimationClip.parse = function( json ) { + + var tracks = []; + + for ( var i = 0; i < json.tracks.length; i ++ ) { + + tracks.push( THREE.KeyframeTrack.parse( json.tracks[i] ).scale( 1.0 / json.fps ) ); + + } + + return new THREE.AnimationClip( json.name, json.duration, tracks ); + +}; + + +// parse the animation.hierarchy format +THREE.AnimationClip.parseAnimation = function( animation, bones, nodeName ) { + + if ( ! animation ) { + console.error( " no animation in JSONLoader data" ); + return null; + } + + var convertTrack = function( trackName, animationKeys, propertyName, trackType, animationKeyToValueFunc ) { + + var keys = []; + + for ( var k = 0; k < animationKeys.length; k ++ ) { + + var animationKey = animationKeys[k]; + + if ( animationKey[propertyName] !== undefined ) { + + keys.push( { time: animationKey.time, value: animationKeyToValueFunc( animationKey ) } ); + } + + } + + // only return track if there are actually keys. + if ( keys.length > 0 ) { + + return new trackType( trackName, keys ); + + } + + return null; + + }; + + var tracks = []; + + var clipName = animation.name || 'default'; + var duration = animation.length || -1; // automatic length determination in AnimationClip. + var fps = animation.fps || 30; + + var hierarchyTracks = animation.hierarchy || []; + + for ( var h = 0; h < hierarchyTracks.length; h ++ ) { + + var animationKeys = hierarchyTracks[ h ].keys; + + // skip empty tracks + if ( ! animationKeys || animationKeys.length == 0 ) { + continue; + } + + // process morph targets in a way exactly compatible with AnimationHandler.init( animation ) + if ( animationKeys[0].morphTargets ) { + + // figure out all morph targets used in this track + var morphTargetNames = {}; + for ( var k = 0; k < animationKeys.length; k ++ ) { + + if ( animationKeys[k].morphTargets ) { + for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) { + + morphTargetNames[ animationKeys[k].morphTargets[m] ] = -1; + } + } + + } + + // create a track for each morph target with all zero morphTargetInfluences except for the keys in which the morphTarget is named. + for ( var morphTargetName in morphTargetNames ) { + + var keys = []; + + for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) { + + var animationKey = animationKeys[k]; + + keys.push( { + time: animationKey.time, + value: (( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ) + }); + + } + + tracks.push( new THREE.NumberKeyframeTrack( nodeName + '.morphTargetInfluence[' + morphTargetName + ']', keys ) ); + + } + + duration = morphTargetNames.length * ( fps || 1.0 ); + + } else { + + var boneName = nodeName + '.bones[' + bones[ h ].name + ']'; + + // track contains positions... + var positionTrack = convertTrack( boneName + '.position', animationKeys, 'pos', THREE.VectorKeyframeTrack, function( animationKey ) { + return new THREE.Vector3().fromArray( animationKey.pos ) + } ); + + if ( positionTrack ) tracks.push( positionTrack ); + + // track contains quaternions... + var quaternionTrack = convertTrack( boneName + '.quaternion', animationKeys, 'rot', THREE.QuaternionKeyframeTrack, function( animationKey ) { + if ( animationKey.rot.slerp ) { + return animationKey.rot.clone(); + } else { + return new THREE.Quaternion().fromArray( animationKey.rot ); + } + } ); + + if ( quaternionTrack ) tracks.push( quaternionTrack ); + + // track contains quaternions... + var scaleTrack = convertTrack( boneName + '.scale', animationKeys, 'scl', THREE.VectorKeyframeTrack, function( animationKey ) { + return new THREE.Vector3().fromArray( animationKey.scl ) + } ); + + if ( scaleTrack ) tracks.push( scaleTrack ); + + } + } + + if ( tracks.length === 0 ) { + + return null; + + } + + var clip = new THREE.AnimationClip( clipName, duration, tracks ); + + return clip; + +}; + +// File:src/animation/AnimationMixer.js + +/** + * + * Mixes together the AnimationClips scheduled by AnimationActions and applies them to the root and subtree + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.AnimationMixer = function( root ) { + + this.root = root; + this.time = 0; + this.timeScale = 1.0; + this.actions = []; + this.propertyBindingMap = {}; + +}; + +THREE.AnimationMixer.prototype = { + + constructor: THREE.AnimationMixer, + + addAction: function( action ) { + + // TODO: check for duplicate action names? Or provide each action with a UUID? + + this.actions.push( action ); + action.init( this.time ); + action.mixer = this; + + var tracks = action.clip.tracks; + + var root = action.localRoot || this.root; + + for ( var i = 0; i < tracks.length; i ++ ) { + + var track = tracks[ i ]; + + var propertyBindingKey = root.uuid + '-' + track.name; + var propertyBinding = this.propertyBindingMap[ propertyBindingKey ]; + + if ( propertyBinding === undefined ) { + + propertyBinding = new THREE.PropertyBinding( root, track.name ); + this.propertyBindingMap[ propertyBindingKey ] = propertyBinding; + + } + + // push in the same order as the tracks. + action.propertyBindings.push( propertyBinding ); + + // track usages of shared property bindings, because if we leave too many around, the mixer can get slow + propertyBinding.referenceCount += 1; + + } + + }, + + removeAllActions: function() { + + for ( var i = 0; i < this.actions.length; i ++ ) { + + this.actions[i].mixer = null; + + } + + // unbind all property bindings + for ( var properyBindingKey in this.propertyBindingMap ) { + + this.propertyBindingMap[ properyBindingKey ].unbind(); + + } + + this.actions = []; + this.propertyBindingMap = {}; + + return this; + + }, + + removeAction: function( action ) { + + var index = this.actions.indexOf( action ); + + if ( index !== - 1 ) { + + this.actions.splice( index, 1 ); + action.mixer = null; + + } + + + // remove unused property bindings because if we leave them around the mixer can get slow + var root = action.localRoot || this.root; + var tracks = action.clip.tracks; + + for ( var i = 0; i < tracks.length; i ++ ) { + + var track = tracks[ i ]; + + var propertyBindingKey = root.uuid + '-' + track.name; + var propertyBinding = this.propertyBindingMap[ propertyBindingKey ]; + + propertyBinding.referenceCount -= 1; + + if ( propertyBinding.referenceCount <= 0 ) { + + propertyBinding.unbind(); + + delete this.propertyBindingMap[ propertyBindingKey ]; + + } + } + + return this; + + }, + + // can be optimized if needed + findActionByName: function( name ) { + + for ( var i = 0; i < this.actions.length; i ++ ) { + + if ( this.actions[i].name === name ) return this.actions[i]; + + } + + return null; + + }, + + play: function( action, optionalFadeInDuration ) { + + action.startTime = this.time; + this.addAction( action ); + + return this; + + }, + + fadeOut: function( action, duration ) { + + var keys = []; + + keys.push( { time: this.time, value: 1 } ); + keys.push( { time: this.time + duration, value: 0 } ); + + action.weight = new THREE.NumberKeyframeTrack( "weight", keys ); + + return this; + + }, + + fadeIn: function( action, duration ) { + + var keys = []; + + keys.push( { time: this.time, value: 0 } ); + keys.push( { time: this.time + duration, value: 1 } ); + + action.weight = new THREE.NumberKeyframeTrack( "weight", keys ); + + return this; + + }, + + warp: function( action, startTimeScale, endTimeScale, duration ) { + + var keys = []; + + keys.push( { time: this.time, value: startTimeScale } ); + keys.push( { time: this.time + duration, value: endTimeScale } ); + + action.timeScale = new THREE.NumberKeyframeTrack( "timeScale", keys ); + + return this; + + }, + + crossFade: function( fadeOutAction, fadeInAction, duration, warp ) { + + this.fadeOut( fadeOutAction, duration ); + this.fadeIn( fadeInAction, duration ); + + if ( warp ) { + + var startEndRatio = fadeOutAction.clip.duration / fadeInAction.clip.duration; + var endStartRatio = 1.0 / startEndRatio; + + this.warp( fadeOutAction, 1.0, startEndRatio, duration ); + this.warp( fadeInAction, endStartRatio, 1.0, duration ); + + } + + return this; + + }, + + update: function( deltaTime ) { + + var mixerDeltaTime = deltaTime * this.timeScale; + this.time += mixerDeltaTime; + + for ( var i = 0; i < this.actions.length; i ++ ) { + + var action = this.actions[i]; + + var weight = action.getWeightAt( this.time ); + + var actionTimeScale = action.getTimeScaleAt( this.time ); + var actionDeltaTime = mixerDeltaTime * actionTimeScale; + + var actionResults = action.update( actionDeltaTime ); + + if ( action.weight <= 0 || ! action.enabled ) continue; + + for ( var j = 0; j < actionResults.length; j ++ ) { + + var name = action.clip.tracks[j].name; + + action.propertyBindings[ j ].accumulate( actionResults[j], weight ); + + } + + } + + // apply to nodes + for ( var propertyBindingKey in this.propertyBindingMap ) { + + this.propertyBindingMap[ propertyBindingKey ].apply(); + + } + + return this; + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.AnimationMixer.prototype ); + +// File:src/animation/AnimationUtils.js + +/** + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.AnimationUtils = { + + getEqualsFunc: function( exemplarValue ) { + + if ( exemplarValue.equals ) { + return function equals_object( a, b ) { + return a.equals( b ); + } + } + + return function equals_primitive( a, b ) { + return ( a === b ); + }; + + }, + + clone: function( exemplarValue ) { + + var typeName = typeof exemplarValue; + if ( typeName === "object" ) { + if ( exemplarValue.clone ) { + return exemplarValue.clone(); + } + console.error( "can not figure out how to copy exemplarValue", exemplarValue ); + } + + return exemplarValue; + + }, + + lerp: function( a, b, alpha, interTrack ) { + + var lerpFunc = THREE.AnimationUtils.getLerpFunc( a, interTrack ); + + return lerpFunc( a, b, alpha ); + + }, + + lerp_object: function( a, b, alpha ) { + return a.lerp( b, alpha ); + }, + + slerp_object: function( a, b, alpha ) { + return a.slerp( b, alpha ); + }, + + lerp_number: function( a, b, alpha ) { + return a * ( 1 - alpha ) + b * alpha; + }, + + lerp_boolean: function( a, b, alpha ) { + return ( alpha < 0.5 ) ? a : b; + }, + + lerp_boolean_immediate: function( a, b, alpha ) { + return a; + }, + + lerp_string: function( a, b, alpha ) { + return ( alpha < 0.5 ) ? a : b; + }, + + lerp_string_immediate: function( a, b, alpha ) { + return a; + }, + + // NOTE: this is an accumulator function that modifies the first argument (e.g. a). This is to minimize memory alocations. + getLerpFunc: function( exemplarValue, interTrack ) { + + if ( exemplarValue === undefined || exemplarValue === null ) throw new Error( "examplarValue is null" ); + + var typeName = typeof exemplarValue; + + switch( typeName ) { + + case "object": + if ( exemplarValue.lerp ) { + return THREE.AnimationUtils.lerp_object; + } + + if ( exemplarValue.slerp ) { + return THREE.AnimationUtils.slerp_object; + } + break; + + case "number": + return THREE.AnimationUtils.lerp_number; + + case "boolean": + if ( interTrack ) { + return THREE.AnimationUtils.lerp_boolean; + } else { + return THREE.AnimationUtils.lerp_boolean_immediate; + } + + case "string": + if ( interTrack ) { + return THREE.AnimationUtils.lerp_string; + } else { + return THREE.AnimationUtils.lerp_string_immediate; + } + + } + + } + +}; + +// File:src/animation/KeyframeTrack.js + +/** + * + * A Track that returns a keyframe interpolated value, currently linearly interpolated + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.KeyframeTrack = function ( name, keys ) { + + if ( name === undefined ) throw new Error( "track name is undefined" ); + if ( keys === undefined || keys.length === 0 ) throw new Error( "no keys in track named " + name ); + + this.name = name; + this.keys = keys; // time in seconds, value as value + + // the index of the last result, used as a starting point for local search. + this.lastIndex = 0; + + this.validate(); + this.optimize(); + +}; + +THREE.KeyframeTrack.prototype = { + + constructor: THREE.KeyframeTrack, + + getAt: function( time ) { + + + // this can not go higher than this.keys.length. + while( ( this.lastIndex < this.keys.length ) && ( time >= this.keys[this.lastIndex].time ) ) { + this.lastIndex ++; + }; + + // this can not go lower than 0. + while( ( this.lastIndex > 0 ) && ( time < this.keys[this.lastIndex - 1].time ) ) { + this.lastIndex --; + } + + if ( this.lastIndex >= this.keys.length ) { + + this.setResult( this.keys[ this.keys.length - 1 ].value ); + + return this.result; + + } + + if ( this.lastIndex === 0 ) { + + this.setResult( this.keys[ 0 ].value ); + + return this.result; + + } + + var prevKey = this.keys[ this.lastIndex - 1 ]; + this.setResult( prevKey.value ); + + // if true, means that prev/current keys are identical, thus no interpolation required. + if ( prevKey.constantToNext ) { + + return this.result; + + } + + // linear interpolation to start with + var currentKey = this.keys[ this.lastIndex ]; + var alpha = ( time - prevKey.time ) / ( currentKey.time - prevKey.time ); + this.result = this.lerpValues( this.result, currentKey.value, alpha ); + + return this.result; + + }, + + // move all keyframes either forwards or backwards in time + shift: function( timeOffset ) { + + if ( timeOffset !== 0.0 ) { + + for ( var i = 0; i < this.keys.length; i ++ ) { + this.keys[i].time += timeOffset; + } + + } + + return this; + + }, + + // scale all keyframe times by a factor (useful for frame <-> seconds conversions) + scale: function( timeScale ) { + + if ( timeScale !== 1.0 ) { + + for ( var i = 0; i < this.keys.length; i ++ ) { + this.keys[i].time *= timeScale; + } + + } + + return this; + + }, + + // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. + // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values + trim: function( startTime, endTime ) { + + var firstKeysToRemove = 0; + for ( var i = 1; i < this.keys.length; i ++ ) { + if ( this.keys[i] <= startTime ) { + firstKeysToRemove ++; + } + } + + var lastKeysToRemove = 0; + for ( var i = this.keys.length - 2; i > 0; i ++ ) { + if ( this.keys[i] >= endTime ) { + lastKeysToRemove ++; + } else { + break; + } + } + + // remove last keys first because it doesn't affect the position of the first keys (the otherway around doesn't work as easily) + if ( ( firstKeysToRemove + lastKeysToRemove ) > 0 ) { + this.keys = this.keys.splice( firstKeysToRemove, this.keys.length - lastKeysToRemove - firstKeysToRemove );; + } + + return this; + + }, + + /* NOTE: This is commented out because we really shouldn't have to handle unsorted key lists + Tracks with out of order keys should be considered to be invalid. - bhouston + sort: function() { + + this.keys.sort( THREE.KeyframeTrack.keyComparer ); + + return this; + + },*/ + + // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable + // One could eventually ensure that all key.values in a track are all of the same type (otherwise interpolation makes no sense.) + validate: function() { + + var prevKey = null; + + if ( this.keys.length === 0 ) { + console.error( " track is empty, no keys", this ); + return; + } + + for ( var i = 0; i < this.keys.length; i ++ ) { + + var currKey = this.keys[i]; + + if ( ! currKey ) { + console.error( " key is null in track", this, i ); + return; + } + + if ( ( typeof currKey.time ) !== 'number' || isNaN( currKey.time ) ) { + console.error( " key.time is not a valid number", this, i, currKey ); + return; + } + + if ( currKey.value === undefined || currKey.value === null) { + console.error( " key.value is null in track", this, i, currKey ); + return; + } + + if ( prevKey && prevKey.time > currKey.time ) { + console.error( " key.time is less than previous key time, out of order keys", this, i, currKey, prevKey ); + return; + } + + prevKey = currKey; + + } + + return this; + + }, + + // currently only removes equivalent sequential keys (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0), which are common in morph target animations + optimize: function() { + + var newKeys = []; + var prevKey = this.keys[0]; + newKeys.push( prevKey ); + + var equalsFunc = THREE.AnimationUtils.getEqualsFunc( prevKey.value ); + + for ( var i = 1; i < this.keys.length - 1; i ++ ) { + var currKey = this.keys[i]; + var nextKey = this.keys[i+1]; + + // if prevKey & currKey are the same time, remove currKey. If you want immediate adjacent keys, use an epsilon offset + // it is not possible to have two keys at the same time as we sort them. The sort is not stable on keys with the same time. + if ( ( prevKey.time === currKey.time ) ) { + + continue; + + } + + // remove completely unnecessary keyframes that are the same as their prev and next keys + if ( this.compareValues( prevKey.value, currKey.value ) && this.compareValues( currKey.value, nextKey.value ) ) { + + continue; + + } + + // determine if interpolation is required + prevKey.constantToNext = this.compareValues( prevKey.value, currKey.value ); + + newKeys.push( currKey ); + prevKey = currKey; + } + newKeys.push( this.keys[ this.keys.length - 1 ] ); + + this.keys = newKeys; + + return this; + + } + +}; + +THREE.KeyframeTrack.keyComparer = function keyComparator(key0, key1) { + return key0.time - key1.time; +}; + +THREE.KeyframeTrack.parse = function( json ) { + + if ( json.type === undefined ) throw new Error( "track type undefined, can not parse" ); + + var trackType = THREE.KeyframeTrack.GetTrackTypeForTypeName( json.type ); + + return trackType.parse( json ); + +}; + +THREE.KeyframeTrack.GetTrackTypeForTypeName = function( typeName ) { + switch( typeName.toLowerCase() ) { + case "vector": + case "vector2": + case "vector3": + case "vector4": + return THREE.VectorKeyframeTrack; + + case "quaternion": + return THREE.QuaternionKeyframeTrack; + + case "integer": + case "scalar": + case "double": + case "float": + case "number": + return THREE.NumberKeyframeTrack; + + case "bool": + case "boolean": + return THREE.BooleanKeyframeTrack; + + case "string": + return THREE.StringKeyframeTrack; + }; + + throw new Error( "Unsupported typeName: " + typeName ); +}; + +// File:src/animation/PropertyBinding.js + +/** + * + * A track bound to a real value in the scene graph. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.PropertyBinding = function ( rootNode, trackName ) { + + this.rootNode = rootNode; + this.trackName = trackName; + this.referenceCount = 0; + this.originalValue = null; // the value of the property before it was controlled by this binding + + var parseResults = THREE.PropertyBinding.parseTrackName( trackName ); + + this.directoryName = parseResults.directoryName; + this.nodeName = parseResults.nodeName; + this.objectName = parseResults.objectName; + this.objectIndex = parseResults.objectIndex; + this.propertyName = parseResults.propertyName; + this.propertyIndex = parseResults.propertyIndex; + + this.node = THREE.PropertyBinding.findNode( rootNode, this.nodeName ) || rootNode; + + this.cumulativeValue = null; + this.cumulativeWeight = 0; +}; + +THREE.PropertyBinding.prototype = { + + constructor: THREE.PropertyBinding, + + reset: function() { + + this.cumulativeValue = null; + this.cumulativeWeight = 0; + + }, + + accumulate: function( value, weight ) { + + if ( ! this.isBound ) this.bind(); + + if ( this.cumulativeWeight === 0 ) { + + if ( weight > 0 ) { + + if ( this.cumulativeValue === null ) { + this.cumulativeValue = THREE.AnimationUtils.clone( value ); + } + this.cumulativeWeight = weight; + + } + + } else { + + var lerpAlpha = weight / ( this.cumulativeWeight + weight ); + this.cumulativeValue = this.lerpValue( this.cumulativeValue, value, lerpAlpha ); + this.cumulativeWeight += weight; + + } + + }, + + unbind: function() { + + if ( ! this.isBound ) return; + + this.setValue( this.originalValue ); + + this.setValue = null; + this.getValue = null; + this.lerpValue = null; + this.equalsValue = null; + this.triggerDirty = null; + this.isBound = false; + + }, + + // bind to the real property in the scene graph, remember original value, memorize various accessors for speed/inefficiency + bind: function() { + + if ( this.isBound ) return; + + var targetObject = this.node; + + // ensure there is a value node + if ( ! targetObject ) { + console.error( " trying to update node for track: " + this.trackName + " but it wasn't found." ); + return; + } + + if ( this.objectName ) { + // special case were we need to reach deeper into the hierarchy to get the face materials.... + if ( this.objectName === "materials" ) { + if ( ! targetObject.material ) { + console.error( ' can not bind to material as node does not have a material', this ); + return; + } + if ( ! targetObject.material.materials ) { + console.error( ' can not bind to material.materials as node.material does not have a materials array', this ); + return; + } + targetObject = targetObject.material.materials; + } else if ( this.objectName === "bones" ) { + if ( ! targetObject.skeleton ) { + console.error( ' can not bind to bones as node does not have a skeleton', this ); + return; + } + // potential future optimization: skip this if propertyIndex is already an integer, and convert the integer string to a true integer. + + targetObject = targetObject.skeleton.bones; + + // support resolving morphTarget names into indices. + for ( var i = 0; i < targetObject.length; i ++ ) { + if ( targetObject[i].name === this.objectIndex ) { + this.objectIndex = i; + break; + } + } + } else { + + if ( targetObject[ this.objectName ] === undefined ) { + console.error( ' can not bind to objectName of node, undefined', this ); + return; + } + targetObject = targetObject[ this.objectName ]; + } + + if ( this.objectIndex !== undefined ) { + if ( targetObject[ this.objectIndex ] === undefined ) { + console.error( " trying to bind to objectIndex of objectName, but is undefined:", this, targetObject ); + return; + } + + targetObject = targetObject[ this.objectIndex ]; + } + + } + + // special case mappings + var nodeProperty = targetObject[ this.propertyName ]; + if ( ! nodeProperty ) { + console.error( " trying to update property for track: " + this.nodeName + '.' + this.propertyName + " but it wasn't found.", targetObject ); + return; + } + + // access a sub element of the property array (only primitives are supported right now) + if ( this.propertyIndex !== undefined ) { + + if ( this.propertyName === "morphTargetInfluences" ) { + // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. + + // support resolving morphTarget names into indices. + if ( ! targetObject.geometry ) { + console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry', this ); + } + if ( ! targetObject.geometry.morphTargets ) { + console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry.morphTargets', this ); + } + + for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) { + if ( targetObject.geometry.morphTargets[i].name === this.propertyIndex ) { + this.propertyIndex = i; + break; + } + } + } + + this.setValue = function setValue_propertyIndexed( value ) { + if ( ! this.equalsValue( nodeProperty[ this.propertyIndex ], value ) ) { + nodeProperty[ this.propertyIndex ] = value; + return true; + } + return false; + }; + + this.getValue = function getValue_propertyIndexed() { + return nodeProperty[ this.propertyIndex ]; + }; + + } + // must use copy for Object3D.Euler/Quaternion + else if ( nodeProperty.copy ) { + + this.setValue = function setValue_propertyObject( value ) { + if ( ! this.equalsValue( nodeProperty, value ) ) { + nodeProperty.copy( value ); + return true; + } + return false; + } + + this.getValue = function getValue_propertyObject() { + return nodeProperty; + }; + + } + // otherwise just set the property directly on the node (do not use nodeProperty as it may not be a reference object) + else { + + this.setValue = function setValue_property( value ) { + if ( ! this.equalsValue( targetObject[ this.propertyName ], value ) ) { + targetObject[ this.propertyName ] = value; + return true; + } + return false; + } + + this.getValue = function getValue_property() { + return targetObject[ this.propertyName ]; + }; + + } + + // trigger node dirty + if ( targetObject.needsUpdate !== undefined ) { // material + + this.triggerDirty = function triggerDirty_needsUpdate() { + this.node.needsUpdate = true; + } + + } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform + + this.triggerDirty = function triggerDirty_matrixWorldNeedsUpdate() { + targetObject.matrixWorldNeedsUpdate = true; + } + + } + + this.originalValue = this.getValue(); + + this.equalsValue = THREE.AnimationUtils.getEqualsFunc( this.originalValue ); + this.lerpValue = THREE.AnimationUtils.getLerpFunc( this.originalValue, true ); + + this.isBound = true; + + }, + + apply: function() { + + // for speed capture the setter pattern as a closure (sort of a memoization pattern: https://en.wikipedia.org/wiki/Memoization) + if ( ! this.isBound ) this.bind(); + + // early exit if there is nothing to apply. + if ( this.cumulativeWeight > 0 ) { + + // blend with original value + if ( this.cumulativeWeight < 1 ) { + + var remainingWeight = 1 - this.cumulativeWeight; + var lerpAlpha = remainingWeight / ( this.cumulativeWeight + remainingWeight ); + this.cumulativeValue = this.lerpValue( this.cumulativeValue, this.originalValue, lerpAlpha ); + + } + + var valueChanged = this.setValue( this.cumulativeValue ); + + if ( valueChanged && this.triggerDirty ) { + this.triggerDirty(); + } + + // reset accumulator + this.cumulativeValue = null; + this.cumulativeWeight = 0; + + } + } + +}; + + +THREE.PropertyBinding.parseTrackName = function( trackName ) { + + // matches strings in the form of: + // nodeName.property + // nodeName.property[accessor] + // nodeName.material.property[accessor] + // uuid.property[accessor] + // uuid.objectName[objectIndex].propertyName[propertyIndex] + // parentName/nodeName.property + // parentName/parentName/nodeName.property[index] + // .bone[Armature.DEF_cog].position + // created and tested via https://regex101.com/#javascript + + var re = /^(([\w]+\/)*)([\w-\d]+)?(\.([\w]+)(\[([\w\d\[\]\_. ]+)\])?)?(\.([\w.]+)(\[([\w\d\[\]\_. ]+)\])?)$/; + var matches = re.exec(trackName); + + if ( ! matches ) { + throw new Error( "cannot parse trackName at all: " + trackName ); + } + + if (matches.index === re.lastIndex) { + re.lastIndex++; + } + + var results = { + directoryName: matches[1], + nodeName: matches[3], // allowed to be null, specified root node. + objectName: matches[5], + objectIndex: matches[7], + propertyName: matches[9], + propertyIndex: matches[11] // allowed to be null, specifies that the whole property is set. + }; + + if ( results.propertyName === null || results.propertyName.length === 0 ) { + throw new Error( "can not parse propertyName from trackName: " + trackName ); + } + + return results; + +}; + +THREE.PropertyBinding.findNode = function( root, nodeName ) { + + function searchSkeleton( skeleton ) { + + for ( var i = 0; i < skeleton.bones.length; i ++ ) { + + var bone = skeleton.bones[i]; + + if ( bone.name === nodeName ) { + + return bone; + + } + } + + return null; + + } + + function searchNodeSubtree( children ) { + + for ( var i = 0; i < children.length; i ++ ) { + + var childNode = children[i]; + + if ( childNode.name === nodeName || childNode.uuid === nodeName ) { + + return childNode; + + } + + var result = searchNodeSubtree( childNode.children ); + + if ( result ) return result; + + } + + return null; + + } + + // + + if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) { + + return root; + + } + + // search into skeleton bones. + if ( root.skeleton ) { + + var bone = searchSkeleton( root.skeleton ); + + if ( bone ) { + + return bone; + + } + } + + // search into node subtree. + if ( root.children ) { + + var subTreeNode = searchNodeSubtree( root.children ); + + if ( subTreeNode ) { + + return subTreeNode; + + } + + } + + return null; +} + +// File:src/animation/tracks/VectorKeyframeTrack.js + +/** + * + * A Track that interpolates Vectors + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.VectorKeyframeTrack = function ( name, keys ) { + + THREE.KeyframeTrack.call( this, name, keys ); + + // local cache of value type to avoid allocations during runtime. + this.result = this.keys[0].value.clone(); + +}; + +THREE.VectorKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype ); + +THREE.VectorKeyframeTrack.prototype.constructor = THREE.VectorKeyframeTrack; + +THREE.VectorKeyframeTrack.prototype.setResult = function( value ) { + + this.result.copy( value ); + +}; + +// memoization of the lerp function for speed. +// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis. +THREE.VectorKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) { + + return value0.lerp( value1, alpha ); + +}; + +THREE.VectorKeyframeTrack.prototype.compareValues = function( value0, value1 ) { + + return value0.equals( value1 ); + +}; + +THREE.VectorKeyframeTrack.prototype.clone = function() { + + var clonedKeys = []; + + for ( var i = 0; i < this.keys.length; i ++ ) { + + var key = this.keys[i]; + clonedKeys.push( { + time: key.time, + value: key.value.clone() + } ); + } + + return new THREE.VectorKeyframeTrack( this.name, clonedKeys ); + +}; + +THREE.VectorKeyframeTrack.parse = function( json ) { + + var elementCount = json.keys[0].value.length; + var valueType = THREE[ 'Vector' + elementCount ]; + + var keys = []; + + for ( var i = 0; i < json.keys.length; i ++ ) { + var jsonKey = json.keys[i]; + keys.push( { + value: new valueType().fromArray( jsonKey.value ), + time: jsonKey.time + } ); + } + + return new THREE.VectorKeyframeTrack( json.name, keys ); + +}; + +// File:src/animation/tracks/QuaternionKeyframeTrack.js + +/** + * + * A Track that interpolates Quaternion + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.QuaternionKeyframeTrack = function ( name, keys ) { + + THREE.KeyframeTrack.call( this, name, keys ); + + // local cache of value type to avoid allocations during runtime. + this.result = this.keys[0].value.clone(); + +}; + +THREE.QuaternionKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype ); + +THREE.QuaternionKeyframeTrack.prototype.constructor = THREE.QuaternionKeyframeTrack; + +THREE.QuaternionKeyframeTrack.prototype.setResult = function( value ) { + + this.result.copy( value ); + +}; + +// memoization of the lerp function for speed. +// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis. +THREE.QuaternionKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) { + + return value0.slerp( value1, alpha ); + +}; + +THREE.QuaternionKeyframeTrack.prototype.compareValues = function( value0, value1 ) { + + return value0.equals( value1 ); + +}; + +THREE.QuaternionKeyframeTrack.prototype.multiply = function( quat ) { + + for ( var i = 0; i < this.keys.length; i ++ ) { + + this.keys[i].value.multiply( quat ); + + } + + return this; + +}; + +THREE.QuaternionKeyframeTrack.prototype.clone = function() { + + var clonedKeys = []; + + for ( var i = 0; i < this.keys.length; i ++ ) { + + var key = this.keys[i]; + clonedKeys.push( { + time: key.time, + value: key.value.clone() + } ); + } + + return new THREE.QuaternionKeyframeTrack( this.name, clonedKeys ); + +}; + +THREE.QuaternionKeyframeTrack.parse = function( json ) { + + var keys = []; + + for ( var i = 0; i < json.keys.length; i ++ ) { + var jsonKey = json.keys[i]; + keys.push( { + value: new THREE.Quaternion().fromArray( jsonKey.value ), + time: jsonKey.time + } ); + } + + return new THREE.QuaternionKeyframeTrack( json.name, keys ); + +}; + +// File:src/animation/tracks/StringKeyframeTrack.js + +/** + * + * A Track that interpolates Strings + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.StringKeyframeTrack = function ( name, keys ) { + + THREE.KeyframeTrack.call( this, name, keys ); + + // local cache of value type to avoid allocations during runtime. + this.result = this.keys[0].value; + +}; + +THREE.StringKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype ); + +THREE.StringKeyframeTrack.prototype.constructor = THREE.StringKeyframeTrack; + +THREE.StringKeyframeTrack.prototype.setResult = function( value ) { + + this.result = value; + +}; + +// memoization of the lerp function for speed. +// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis. +THREE.StringKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) { + + return ( alpha < 1.0 ) ? value0 : value1; + +}; + +THREE.StringKeyframeTrack.prototype.compareValues = function( value0, value1 ) { + + return ( value0 === value1 ); + +}; + +THREE.StringKeyframeTrack.prototype.clone = function() { + + var clonedKeys = []; + + for ( var i = 0; i < this.keys.length; i ++ ) { + + var key = this.keys[i]; + clonedKeys.push( { + time: key.time, + value: key.value + } ); + } + + return new THREE.StringKeyframeTrack( this.name, clonedKeys ); + +}; + +THREE.StringKeyframeTrack.parse = function( json ) { + + return new THREE.StringKeyframeTrack( json.name, json.keys ); + +}; + +// File:src/animation/tracks/BooleanKeyframeTrack.js + +/** + * + * A Track that interpolates Boolean + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.BooleanKeyframeTrack = function ( name, keys ) { + + THREE.KeyframeTrack.call( this, name, keys ); + + // local cache of value type to avoid allocations during runtime. + this.result = this.keys[0].value; + +}; + +THREE.BooleanKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype ); + +THREE.BooleanKeyframeTrack.prototype.constructor = THREE.BooleanKeyframeTrack; + +THREE.BooleanKeyframeTrack.prototype.setResult = function( value ) { + + this.result = value; + +}; + +// memoization of the lerp function for speed. +// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis. +THREE.BooleanKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) { + + return ( alpha < 1.0 ) ? value0 : value1; + +}; + +THREE.BooleanKeyframeTrack.prototype.compareValues = function( value0, value1 ) { + + return ( value0 === value1 ); + +}; + +THREE.BooleanKeyframeTrack.prototype.clone = function() { + + var clonedKeys = []; + + for ( var i = 0; i < this.keys.length; i ++ ) { + + var key = this.keys[i]; + clonedKeys.push( { + time: key.time, + value: key.value + } ); + } + + return new THREE.BooleanKeyframeTrack( this.name, clonedKeys ); + +}; + +THREE.BooleanKeyframeTrack.parse = function( json ) { + + return new THREE.BooleanKeyframeTrack( json.name, json.keys ); + +}; + +// File:src/animation/tracks/NumberKeyframeTrack.js + +/** + * + * A Track that interpolates Numbers + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.NumberKeyframeTrack = function ( name, keys ) { + + THREE.KeyframeTrack.call( this, name, keys ); + + // local cache of value type to avoid allocations during runtime. + this.result = this.keys[0].value; + +}; + +THREE.NumberKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype ); + +THREE.NumberKeyframeTrack.prototype.constructor = THREE.NumberKeyframeTrack; + +THREE.NumberKeyframeTrack.prototype.setResult = function( value ) { + + this.result = value; + +}; + +// memoization of the lerp function for speed. +// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis. +THREE.NumberKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) { + + return value0 * ( 1 - alpha ) + value1 * alpha; + +}; + +THREE.NumberKeyframeTrack.prototype.compareValues = function( value0, value1 ) { + + return ( value0 === value1 ); + +}; + +THREE.NumberKeyframeTrack.prototype.clone = function() { + + var clonedKeys = []; + + for ( var i = 0; i < this.keys.length; i ++ ) { + + var key = this.keys[i]; + clonedKeys.push( { + time: key.time, + value: key.value + } ); + } + + return new THREE.NumberKeyframeTrack( this.name, clonedKeys ); + +}; + +THREE.NumberKeyframeTrack.parse = function( json ) { + + return new THREE.NumberKeyframeTrack( json.name, json.keys ); + +}; + +// File:src/cameras/Camera.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.Camera = function () { + + THREE.Object3D.call( this ); + + this.type = 'Camera'; + + this.matrixWorldInverse = new THREE.Matrix4(); + this.projectionMatrix = new THREE.Matrix4(); + +}; + +THREE.Camera.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Camera.prototype.constructor = THREE.Camera; + +THREE.Camera.prototype.getWorldDirection = function () { + + var quaternion = new THREE.Quaternion(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + this.getWorldQuaternion( quaternion ); + + return result.set( 0, 0, - 1 ).applyQuaternion( quaternion ); + + }; + +}(); + +THREE.Camera.prototype.lookAt = function () { + + // This routine does not support cameras with rotated and/or translated parent(s) + + var m1 = new THREE.Matrix4(); + + return function ( vector ) { + + m1.lookAt( this.position, vector, this.up ); + + this.quaternion.setFromRotationMatrix( m1 ); + + }; + +}(); + +THREE.Camera.prototype.clone = function () { + + return new this.constructor().copy( this ); + +}; + +THREE.Camera.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source ); + + this.matrixWorldInverse.copy( source.matrixWorldInverse ); + this.projectionMatrix.copy( source.projectionMatrix ); + + return this; + +}; + +// File:src/cameras/CubeCamera.js + +/** + * Camera for rendering cube maps + * - renders scene into axis-aligned cube + * + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.CubeCamera = function ( near, far, cubeResolution ) { + + THREE.Object3D.call( this ); + + this.type = 'CubeCamera'; + + var fov = 90, aspect = 1; + + var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPX.up.set( 0, - 1, 0 ); + cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) ); + this.add( cameraPX ); + + var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNX.up.set( 0, - 1, 0 ); + cameraNX.lookAt( new THREE.Vector3( - 1, 0, 0 ) ); + this.add( cameraNX ); + + var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPY.up.set( 0, 0, 1 ); + cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) ); + this.add( cameraPY ); + + var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNY.up.set( 0, 0, - 1 ); + cameraNY.lookAt( new THREE.Vector3( 0, - 1, 0 ) ); + this.add( cameraNY ); + + var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPZ.up.set( 0, - 1, 0 ); + cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) ); + this.add( cameraPZ ); + + var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNZ.up.set( 0, - 1, 0 ); + cameraNZ.lookAt( new THREE.Vector3( 0, 0, - 1 ) ); + this.add( cameraNZ ); + + this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } ); + + this.updateCubeMap = function ( renderer, scene ) { + + if ( this.parent === null ) this.updateMatrixWorld(); + + var renderTarget = this.renderTarget; + var generateMipmaps = renderTarget.texture.generateMipmaps; + + renderTarget.texture.generateMipmaps = false; + + renderTarget.activeCubeFace = 0; + renderer.render( scene, cameraPX, renderTarget ); + + renderTarget.activeCubeFace = 1; + renderer.render( scene, cameraNX, renderTarget ); + + renderTarget.activeCubeFace = 2; + renderer.render( scene, cameraPY, renderTarget ); + + renderTarget.activeCubeFace = 3; + renderer.render( scene, cameraNY, renderTarget ); + + renderTarget.activeCubeFace = 4; + renderer.render( scene, cameraPZ, renderTarget ); + + renderTarget.texture.generateMipmaps = generateMipmaps; + + renderTarget.activeCubeFace = 5; + renderer.render( scene, cameraNZ, renderTarget ); + + renderer.setRenderTarget( null ); + + }; + +}; + +THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype ); +THREE.CubeCamera.prototype.constructor = THREE.CubeCamera; + +// File:src/cameras/OrthographicCamera.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) { + + THREE.Camera.call( this ); + + this.type = 'OrthographicCamera'; + + this.zoom = 1; + + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; + + this.near = ( near !== undefined ) ? near : 0.1; + this.far = ( far !== undefined ) ? far : 2000; + + this.updateProjectionMatrix(); + +}; + +THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype ); +THREE.OrthographicCamera.prototype.constructor = THREE.OrthographicCamera; + +THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () { + + var dx = ( this.right - this.left ) / ( 2 * this.zoom ); + var dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); + var cx = ( this.right + this.left ) / 2; + var cy = ( this.top + this.bottom ) / 2; + + this.projectionMatrix.makeOrthographic( cx - dx, cx + dx, cy + dy, cy - dy, this.near, this.far ); + +}; + +THREE.OrthographicCamera.prototype.copy = function ( source ) { + + THREE.Camera.prototype.copy.call( this, source ); + + this.left = source.left; + this.right = source.right; + this.top = source.top; + this.bottom = source.bottom; + this.near = source.near; + this.far = source.far; + + this.zoom = source.zoom; + + return this; + +}; + +THREE.OrthographicCamera.prototype.toJSON = function ( meta ) { + + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + + data.object.zoom = this.zoom; + data.object.left = this.left; + data.object.right = this.right; + data.object.top = this.top; + data.object.bottom = this.bottom; + data.object.near = this.near; + data.object.far = this.far; + + return data; + +}; + +// File:src/cameras/PerspectiveCamera.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author greggman / http://games.greggman.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.PerspectiveCamera = function ( fov, aspect, near, far ) { + + THREE.Camera.call( this ); + + this.type = 'PerspectiveCamera'; + + this.zoom = 1; + + this.fov = fov !== undefined ? fov : 50; + this.aspect = aspect !== undefined ? aspect : 1; + this.near = near !== undefined ? near : 0.1; + this.far = far !== undefined ? far : 2000; + + this.updateProjectionMatrix(); + +}; + +THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype ); +THREE.PerspectiveCamera.prototype.constructor = THREE.PerspectiveCamera; + + +/** + * Uses Focal Length (in mm) to estimate and set FOV + * 35mm (full-frame) camera is used if frame size is not specified; + * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html + */ + +THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) { + + if ( frameHeight === undefined ) frameHeight = 24; + + this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) ); + this.updateProjectionMatrix(); + +}; + + +/** + * Sets an offset in a larger frustum. This is useful for multi-window or + * multi-monitor/multi-machine setups. + * + * For example, if you have 3x2 monitors and each monitor is 1920x1080 and + * the monitors are in grid like this + * + * +---+---+---+ + * | A | B | C | + * +---+---+---+ + * | D | E | F | + * +---+---+---+ + * + * then for each monitor you would call it like this + * + * var w = 1920; + * var h = 1080; + * var fullWidth = w * 3; + * var fullHeight = h * 2; + * + * --A-- + * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); + * --B-- + * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); + * --C-- + * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); + * --D-- + * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); + * --E-- + * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); + * --F-- + * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); + * + * Note there is no reason monitors have to be the same size or in a grid. + */ + +THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) { + + this.fullWidth = fullWidth; + this.fullHeight = fullHeight; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + + this.updateProjectionMatrix(); + +}; + + +THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () { + + var fov = THREE.Math.radToDeg( 2 * Math.atan( Math.tan( THREE.Math.degToRad( this.fov ) * 0.5 ) / this.zoom ) ); + + if ( this.fullWidth ) { + + var aspect = this.fullWidth / this.fullHeight; + var top = Math.tan( THREE.Math.degToRad( fov * 0.5 ) ) * this.near; + var bottom = - top; + var left = aspect * bottom; + var right = aspect * top; + var width = Math.abs( right - left ); + var height = Math.abs( top - bottom ); + + this.projectionMatrix.makeFrustum( + left + this.x * width / this.fullWidth, + left + ( this.x + this.width ) * width / this.fullWidth, + top - ( this.y + this.height ) * height / this.fullHeight, + top - this.y * height / this.fullHeight, + this.near, + this.far + ); + + } else { + + this.projectionMatrix.makePerspective( fov, this.aspect, this.near, this.far ); + + } + +}; + +THREE.PerspectiveCamera.prototype.copy = function ( source ) { + + THREE.Camera.prototype.copy.call( this, source ); + + this.fov = source.fov; + this.aspect = source.aspect; + this.near = source.near; + this.far = source.far; + + this.zoom = source.zoom; + + return this; + +}; + +THREE.PerspectiveCamera.prototype.toJSON = function ( meta ) { + + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + + data.object.zoom = this.zoom; + data.object.fov = this.fov; + data.object.aspect = this.aspect; + data.object.near = this.near; + data.object.far = this.far; + + return data; + +}; + +// File:src/lights/Light.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Light = function ( color ) { + + THREE.Object3D.call( this ); + + this.type = 'Light'; + + this.color = new THREE.Color( color ); + + this.receiveShadow = undefined; + +}; + +THREE.Light.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Light.prototype.constructor = THREE.Light; + +Object.defineProperties( THREE.Light.prototype, { + onlyShadow: { + set: function ( value ) { + console.warn( 'THREE.Light: .onlyShadow has been removed.' ); + } + }, + shadowCameraFov: { + set: function ( value ) { + this.shadow.camera.fov = value; + } + }, + shadowCameraLeft: { + set: function ( value ) { + this.shadow.camera.left = value; + } + }, + shadowCameraRight: { + set: function ( value ) { + this.shadow.camera.right = value; + } + }, + shadowCameraTop: { + set: function ( value ) { + this.shadow.camera.top = value; + } + }, + shadowCameraBottom: { + set: function ( value ) { + this.shadow.camera.bottom = value; + } + }, + shadowCameraNear: { + set: function ( value ) { + this.shadow.camera.near = value; + } + }, + shadowCameraFar: { + set: function ( value ) { + this.shadow.camera.far = value; + } + }, + shadowCameraVisible: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow ) instead.' ); + } + }, + shadowBias: { + set: function ( value ) { + this.shadow.bias = value; + } + }, + shadowDarkness: { + set: function ( value ) { + this.shadow.darkness = value; + } + }, + shadowMapWidth: { + set: function ( value ) { + this.shadow.mapSize.width = value; + } + }, + shadowMapHeight: { + set: function ( value ) { + this.shadow.mapSize.height = value; + } + } +} ); + +THREE.Light.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + + return this; + +}; + +THREE.Light.prototype.toJSON = function ( meta ) { + + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + + data.object.color = this.color.getHex(); + if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); + + if ( this.intensity !== undefined ) data.object.intensity = this.intensity; + if ( this.distance !== undefined ) data.object.distance = this.distance; + if ( this.angle !== undefined ) data.object.angle = this.angle; + if ( this.decay !== undefined ) data.object.decay = this.decay; + if ( this.exponent !== undefined ) data.object.exponent = this.exponent; + + return data; + +}; + +// File:src/lights/LightShadow.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LightShadow = function ( camera ) { + + this.camera = camera; + + this.bias = 0; + this.darkness = 1; + + this.mapSize = new THREE.Vector2( 512, 512 ); + + this.map = null; + this.matrix = null; + +}; + +THREE.LightShadow.prototype = { + + constructor: THREE.LightShadow, + + copy: function ( source ) { + + this.camera = source.camera.clone(); + + this.bias = source.bias; + this.darkness = source.darkness; + + this.mapSize.copy( source.mapSize ); + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + } + +}; + +// File:src/lights/AmbientLight.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AmbientLight = function ( color ) { + + THREE.Light.call( this, color ); + + this.type = 'AmbientLight'; + + this.castShadow = undefined; + +}; + +THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype ); +THREE.AmbientLight.prototype.constructor = THREE.AmbientLight; + +// File:src/lights/DirectionalLight.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.DirectionalLight = function ( color, intensity ) { + + THREE.Light.call( this, color ); + + this.type = 'DirectionalLight'; + + this.position.set( 0, 1, 0 ); + this.updateMatrix(); + + this.target = new THREE.Object3D(); + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + + this.shadow = new THREE.LightShadow( new THREE.OrthographicCamera( - 500, 500, 500, - 500, 50, 5000 ) ); + +}; + +THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype ); +THREE.DirectionalLight.prototype.constructor = THREE.DirectionalLight; + +THREE.DirectionalLight.prototype.copy = function ( source ) { + + THREE.Light.prototype.copy.call( this, source ); + + this.intensity = source.intensity; + this.target = source.target.clone(); + + this.shadow = source.shadow.clone(); + + return this; + +}; + +// File:src/lights/HemisphereLight.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) { + + THREE.Light.call( this, skyColor ); + + this.type = 'HemisphereLight'; + + this.castShadow = undefined; + + this.position.set( 0, 1, 0 ); + this.updateMatrix(); + + this.groundColor = new THREE.Color( groundColor ); + this.intensity = ( intensity !== undefined ) ? intensity : 1; + +}; + +THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype ); +THREE.HemisphereLight.prototype.constructor = THREE.HemisphereLight; + +THREE.HemisphereLight.prototype.copy = function ( source ) { + + THREE.Light.prototype.copy.call( this, source ); + + this.groundColor.copy( source.groundColor ); + this.intensity = source.intensity; + + return this; + +}; + +// File:src/lights/PointLight.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + + +THREE.PointLight = function ( color, intensity, distance, decay ) { + + THREE.Light.call( this, color ); + + this.type = 'PointLight'; + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + this.distance = ( distance !== undefined ) ? distance : 0; + this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. + + this.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 90, 1, 1, 500 ) ); + +}; + +THREE.PointLight.prototype = Object.create( THREE.Light.prototype ); +THREE.PointLight.prototype.constructor = THREE.PointLight; + +THREE.PointLight.prototype.copy = function ( source ) { + + THREE.Light.prototype.copy.call( this, source ); + + this.intensity = source.intensity; + this.distance = source.distance; + this.decay = source.decay; + + this.shadow = source.shadow.clone(); + + return this; + +}; + +// File:src/lights/SpotLight.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SpotLight = function ( color, intensity, distance, angle, exponent, decay ) { + + THREE.Light.call( this, color ); + + this.type = 'SpotLight'; + + this.position.set( 0, 1, 0 ); + this.updateMatrix(); + + this.target = new THREE.Object3D(); + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + this.distance = ( distance !== undefined ) ? distance : 0; + this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; + this.exponent = ( exponent !== undefined ) ? exponent : 10; + this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. + + this.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 50, 1, 50, 5000 ) ); + +}; + +THREE.SpotLight.prototype = Object.create( THREE.Light.prototype ); +THREE.SpotLight.prototype.constructor = THREE.SpotLight; + +THREE.SpotLight.prototype.copy = function ( source ) { + + THREE.Light.prototype.copy.call( this, source ); + + this.intensity = source.intensity; + this.distance = source.distance; + this.angle = source.angle; + this.exponent = source.exponent; + this.decay = source.decay; + + this.target = source.target.clone(); + + this.shadow = source.shadow.clone(); + + return this; + +}; + +// File:src/loaders/Cache.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Cache = { + + enabled: false, + + files: {}, + + add: function ( key, file ) { + + if ( this.enabled === false ) return; + + // console.log( 'THREE.Cache', 'Adding key:', key ); + + this.files[ key ] = file; + + }, + + get: function ( key ) { + + if ( this.enabled === false ) return; + + // console.log( 'THREE.Cache', 'Checking key:', key ); + + return this.files[ key ]; + + }, + + remove: function ( key ) { + + delete this.files[ key ]; + + }, + + clear: function () { + + this.files = {}; + + } + +}; + +// File:src/loaders/Loader.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Loader = function () { + + this.onLoadStart = function () {}; + this.onLoadProgress = function () {}; + this.onLoadComplete = function () {}; + +}; + +THREE.Loader.prototype = { + + constructor: THREE.Loader, + + crossOrigin: undefined, + + extractUrlBase: function ( url ) { + + var parts = url.split( '/' ); + + if ( parts.length === 1 ) return './'; + + parts.pop(); + + return parts.join( '/' ) + '/'; + + }, + + initMaterials: function ( materials, texturePath, crossOrigin ) { + + var array = []; + + for ( var i = 0; i < materials.length; ++ i ) { + + array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); + + } + + return array; + + }, + + createMaterial: ( function () { + + var color, textureLoader, materialLoader; + + return function ( m, texturePath, crossOrigin ) { + + if ( color === undefined ) color = new THREE.Color(); + if ( textureLoader === undefined ) textureLoader = new THREE.TextureLoader(); + if ( materialLoader === undefined ) materialLoader = new THREE.MaterialLoader(); + + // convert from old material format + + var textures = {}; + + function loadTexture( path, repeat, offset, wrap, anisotropy ) { + + var fullPath = texturePath + path; + var loader = THREE.Loader.Handlers.get( fullPath ); + + var texture; + + if ( loader !== null ) { + + texture = loader.load( fullPath ); + + } else { + + textureLoader.setCrossOrigin( crossOrigin ); + texture = textureLoader.load( fullPath ); + + } + + if ( repeat !== undefined ) { + + texture.repeat.fromArray( repeat ); + + if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping; + if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping; + + } + + if ( offset !== undefined ) { + + texture.offset.fromArray( offset ); + + } + + if ( wrap !== undefined ) { + + if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = THREE.RepeatWrapping; + if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = THREE.MirroredRepeatWrapping; + + if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = THREE.RepeatWrapping; + if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = THREE.MirroredRepeatWrapping; + + } + + if ( anisotropy !== undefined ) { + + texture.anisotropy = anisotropy; + + } + + var uuid = THREE.Math.generateUUID(); + + textures[ uuid ] = texture; + + return uuid; + + } + + // + + var json = { + uuid: THREE.Math.generateUUID(), + type: 'MeshLambertMaterial' + }; + + for ( var name in m ) { + + var value = m[ name ]; + + switch ( name ) { + case 'DbgColor': + json.color = value; + break; + case 'DbgIndex': + case 'opticalDensity': + case 'illumination': + // These were never supported + break; + case 'DbgName': + json.name = value; + break; + case 'blending': + json.blending = THREE[ value ]; + break; + case 'colorDiffuse': + json.color = color.fromArray( value ).getHex(); + break; + case 'colorSpecular': + json.specular = color.fromArray( value ).getHex(); + break; + case 'colorEmissive': + json.emissive = color.fromArray( value ).getHex(); + break; + case 'specularCoef': + json.shininess = value; + break; + case 'shading': + if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial'; + if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial'; + break; + case 'mapDiffuse': + json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); + break; + case 'mapDiffuseRepeat': + case 'mapDiffuseOffset': + case 'mapDiffuseWrap': + case 'mapDiffuseAnisotropy': + break; + case 'mapLight': + json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); + break; + case 'mapLightRepeat': + case 'mapLightOffset': + case 'mapLightWrap': + case 'mapLightAnisotropy': + break; + case 'mapAO': + json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy ); + break; + case 'mapAORepeat': + case 'mapAOOffset': + case 'mapAOWrap': + case 'mapAOAnisotropy': + break; + case 'mapBump': + json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); + break; + case 'mapBumpScale': + json.bumpScale = value; + break; + case 'mapBumpRepeat': + case 'mapBumpOffset': + case 'mapBumpWrap': + case 'mapBumpAnisotropy': + break; + case 'mapNormal': + json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); + break; + case 'mapNormalFactor': + json.normalScale = [ value, value ]; + break; + case 'mapNormalRepeat': + case 'mapNormalOffset': + case 'mapNormalWrap': + case 'mapNormalAnisotropy': + break; + case 'mapSpecular': + json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); + break; + case 'mapSpecularRepeat': + case 'mapSpecularOffset': + case 'mapSpecularWrap': + case 'mapSpecularAnisotropy': + break; + case 'mapAlpha': + json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); + break; + case 'mapAlphaRepeat': + case 'mapAlphaOffset': + case 'mapAlphaWrap': + case 'mapAlphaAnisotropy': + break; + case 'flipSided': + json.side = THREE.BackSide; + break; + case 'doubleSided': + json.side = THREE.DoubleSide; + break; + case 'transparency': + console.warn( 'THREE.Loader: transparency has been renamed to opacity' ); + json.opacity = value; + break; + case 'opacity': + case 'transparent': + case 'depthTest': + case 'depthWrite': + case 'transparent': + case 'visible': + case 'wireframe': + json[ name ] = value; + break; + case 'vertexColors': + if ( value === true ) json.vertexColors = THREE.VertexColors; + if ( value === 'face' ) json.vertexColors = THREE.FaceColors; + break; + default: + console.error( 'Loader.createMaterial: Unsupported', name, value ); + break; + } + + } + + if ( json.type !== 'MeshPhongMaterial' ) delete json.specular; + if ( json.opacity < 1 ) json.transparent = true; + + materialLoader.setTextures( textures ); + + return materialLoader.parse( json ); + + }; + + } )() + +}; + +THREE.Loader.Handlers = { + + handlers: [], + + add: function ( regex, loader ) { + + this.handlers.push( regex, loader ); + + }, + + get: function ( file ) { + + var handlers = this.handlers; + + for ( var i = 0, l = handlers.length; i < l; i += 2 ) { + + var regex = handlers[ i ]; + var loader = handlers[ i + 1 ]; + + if ( regex.test( file ) ) { + + return loader; + + } + + } + + return null; + + } + +}; + +// File:src/loaders/XHRLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.XHRLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.XHRLoader.prototype = { + + constructor: THREE.XHRLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var cached = THREE.Cache.get( url ); + + if ( cached !== undefined ) { + + if ( onLoad ) { + + setTimeout( function () { + + onLoad( cached ); + + }, 0 ); + + } + + return cached; + + } + + var request = new XMLHttpRequest(); + request.open( 'GET', url, true ); + + request.addEventListener( 'load', function ( event ) { + + var response = event.target.response; + + THREE.Cache.add( url, response ); + + if ( onLoad ) onLoad( response ); + + scope.manager.itemEnd( url ); + + }, false ); + + if ( onProgress !== undefined ) { + + request.addEventListener( 'progress', function ( event ) { + + onProgress( event ); + + }, false ); + + } + + request.addEventListener( 'error', function ( event ) { + + if ( onError ) onError( event ); + + scope.manager.itemError( url ); + + }, false ); + + if ( this.crossOrigin !== undefined ) request.crossOrigin = this.crossOrigin; + if ( this.responseType !== undefined ) request.responseType = this.responseType; + if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; + + request.send( null ); + + scope.manager.itemStart( url ); + + return request; + + }, + + setResponseType: function ( value ) { + + this.responseType = value; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setWithCredentials: function ( value ) { + + this.withCredentials = value; + + } + +}; + +// File:src/loaders/ImageLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.ImageLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.ImageLoader.prototype = { + + constructor: THREE.ImageLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var cached = THREE.Cache.get( url ); + + if ( cached !== undefined ) { + + scope.manager.itemStart( url ); + + if ( onLoad ) { + + setTimeout( function () { + + onLoad( cached ); + + scope.manager.itemEnd( url ); + + }, 0 ); + + } else { + + scope.manager.itemEnd( url ); + + } + + return cached; + + } + + var image = document.createElement( 'img' ); + + image.addEventListener( 'load', function ( event ) { + + THREE.Cache.add( url, this ); + + if ( onLoad ) onLoad( this ); + + scope.manager.itemEnd( url ); + + }, false ); + + if ( onProgress !== undefined ) { + + image.addEventListener( 'progress', function ( event ) { + + onProgress( event ); + + }, false ); + + } + + image.addEventListener( 'error', function ( event ) { + + if ( onError ) onError( event ); + + scope.manager.itemError( url ); + + }, false ); + + if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; + + scope.manager.itemStart( url ); + + image.src = url; + + return image; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; + +// File:src/loaders/JSONLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.JSONLoader = function ( manager ) { + + if ( typeof manager === 'boolean' ) { + + console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' ); + manager = undefined; + + } + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + this.withCredentials = false; + +}; + +THREE.JSONLoader.prototype = { + + constructor: THREE.JSONLoader, + + // Deprecated + + get statusDomElement () { + + if ( this._statusDomElement === undefined ) { + + this._statusDomElement = document.createElement( 'div' ); + + } + + console.warn( 'THREE.JSONLoader: .statusDomElement has been removed.' ); + return this._statusDomElement; + + }, + + load: function( url, onLoad, onProgress, onError ) { + + var scope = this; + + var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : THREE.Loader.prototype.extractUrlBase( url ); + + var loader = new THREE.XHRLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setWithCredentials( this.withCredentials ); + loader.load( url, function ( text ) { + + var json = JSON.parse( text ); + var metadata = json.metadata; + + if ( metadata !== undefined ) { + + if ( metadata.type === 'object' ) { + + console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' ); + return; + + } + + if ( metadata.type === 'scene' ) { + + console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' ); + return; + + } + + } + + var object = scope.parse( json, texturePath ); + onLoad( object.geometry, object.materials ); + + } ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setTexturePath: function ( value ) { + + this.texturePath = value; + + }, + + parse: function ( json, texturePath ) { + + var geometry = new THREE.Geometry(), + scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; + + parseModel( scale ); + + parseSkin(); + parseMorphing( scale ); + parseAnimations(); + + geometry.computeFaceNormals(); + geometry.computeBoundingSphere(); + + function parseModel( scale ) { + + function isBitSet( value, position ) { + + return value & ( 1 << position ); + + } + + var i, j, fi, + + offset, zLength, + + colorIndex, normalIndex, uvIndex, materialIndex, + + type, + isQuad, + hasMaterial, + hasFaceVertexUv, + hasFaceNormal, hasFaceVertexNormal, + hasFaceColor, hasFaceVertexColor, + + vertex, face, faceA, faceB, hex, normal, + + uvLayer, uv, u, v, + + faces = json.faces, + vertices = json.vertices, + normals = json.normals, + colors = json.colors, + + nUvLayers = 0; + + if ( json.uvs !== undefined ) { + + // disregard empty arrays + + for ( i = 0; i < json.uvs.length; i ++ ) { + + if ( json.uvs[ i ].length ) nUvLayers ++; + + } + + for ( i = 0; i < nUvLayers; i ++ ) { + + geometry.faceVertexUvs[ i ] = []; + + } + + } + + offset = 0; + zLength = vertices.length; + + while ( offset < zLength ) { + + vertex = new THREE.Vector3(); + + vertex.x = vertices[ offset ++ ] * scale; + vertex.y = vertices[ offset ++ ] * scale; + vertex.z = vertices[ offset ++ ] * scale; + + geometry.vertices.push( vertex ); + + } + + offset = 0; + zLength = faces.length; + + while ( offset < zLength ) { + + type = faces[ offset ++ ]; + + + isQuad = isBitSet( type, 0 ); + hasMaterial = isBitSet( type, 1 ); + hasFaceVertexUv = isBitSet( type, 3 ); + hasFaceNormal = isBitSet( type, 4 ); + hasFaceVertexNormal = isBitSet( type, 5 ); + hasFaceColor = isBitSet( type, 6 ); + hasFaceVertexColor = isBitSet( type, 7 ); + + // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); + + if ( isQuad ) { + + faceA = new THREE.Face3(); + faceA.a = faces[ offset ]; + faceA.b = faces[ offset + 1 ]; + faceA.c = faces[ offset + 3 ]; + + faceB = new THREE.Face3(); + faceB.a = faces[ offset + 1 ]; + faceB.b = faces[ offset + 2 ]; + faceB.c = faces[ offset + 3 ]; + + offset += 4; + + if ( hasMaterial ) { + + materialIndex = faces[ offset ++ ]; + faceA.materialIndex = materialIndex; + faceB.materialIndex = materialIndex; + + } + + // to get face <=> uv index correspondence + + fi = geometry.faces.length; + + if ( hasFaceVertexUv ) { + + for ( i = 0; i < nUvLayers; i ++ ) { + + uvLayer = json.uvs[ i ]; + + geometry.faceVertexUvs[ i ][ fi ] = []; + geometry.faceVertexUvs[ i ][ fi + 1 ] = []; + + for ( j = 0; j < 4; j ++ ) { + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + uv = new THREE.Vector2( u, v ); + + if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); + if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); + + } + + } + + } + + if ( hasFaceNormal ) { + + normalIndex = faces[ offset ++ ] * 3; + + faceA.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + faceB.normal.copy( faceA.normal ); + + } + + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < 4; i ++ ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + + if ( i !== 2 ) faceA.vertexNormals.push( normal ); + if ( i !== 0 ) faceB.vertexNormals.push( normal ); + + } + + } + + + if ( hasFaceColor ) { + + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; + + faceA.color.setHex( hex ); + faceB.color.setHex( hex ); + + } + + + if ( hasFaceVertexColor ) { + + for ( i = 0; i < 4; i ++ ) { + + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; + + if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) ); + if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) ); + + } + + } + + geometry.faces.push( faceA ); + geometry.faces.push( faceB ); + + } else { + + face = new THREE.Face3(); + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; + + if ( hasMaterial ) { + + materialIndex = faces[ offset ++ ]; + face.materialIndex = materialIndex; + + } + + // to get face <=> uv index correspondence + + fi = geometry.faces.length; + + if ( hasFaceVertexUv ) { + + for ( i = 0; i < nUvLayers; i ++ ) { + + uvLayer = json.uvs[ i ]; + + geometry.faceVertexUvs[ i ][ fi ] = []; + + for ( j = 0; j < 3; j ++ ) { + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + uv = new THREE.Vector2( u, v ); + + geometry.faceVertexUvs[ i ][ fi ].push( uv ); + + } + + } + + } + + if ( hasFaceNormal ) { + + normalIndex = faces[ offset ++ ] * 3; + + face.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + } + + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < 3; i ++ ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + face.vertexNormals.push( normal ); + + } + + } + + + if ( hasFaceColor ) { + + colorIndex = faces[ offset ++ ]; + face.color.setHex( colors[ colorIndex ] ); + + } + + + if ( hasFaceVertexColor ) { + + for ( i = 0; i < 3; i ++ ) { + + colorIndex = faces[ offset ++ ]; + face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) ); + + } + + } + + geometry.faces.push( face ); + + } + + } + + }; + + function parseSkin() { + + var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; + + if ( json.skinWeights ) { + + for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { + + var x = json.skinWeights[ i ]; + var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; + var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; + var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; + + geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) ); + + } + + } + + if ( json.skinIndices ) { + + for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { + + var a = json.skinIndices[ i ]; + var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; + var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; + var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; + + geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) ); + + } + + } + + geometry.bones = json.bones; + + if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { + + console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + + geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); + + } + + }; + + function parseMorphing( scale ) { + + if ( json.morphTargets !== undefined ) { + + for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) { + + geometry.morphTargets[ i ] = {}; + geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; + geometry.morphTargets[ i ].vertices = []; + + var dstVertices = geometry.morphTargets[ i ].vertices; + var srcVertices = json.morphTargets[ i ].vertices; + + for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) { + + var vertex = new THREE.Vector3(); + vertex.x = srcVertices[ v ] * scale; + vertex.y = srcVertices[ v + 1 ] * scale; + vertex.z = srcVertices[ v + 2 ] * scale; + + dstVertices.push( vertex ); + + } + + } + + } + + if ( json.morphColors !== undefined && json.morphColors.length > 0 ) { + + console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' ); + + var faces = geometry.faces; + var morphColors = json.morphColors[ 0 ].colors; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + faces[ i ].color.fromArray( morphColors, i * 3 ); + + } + + } + + } + + function parseAnimations() { + + var outputAnimations = []; + + // parse old style Bone/Hierarchy animations + var animations = []; + if ( json.animation !== undefined ) { + animations.push( json.animation ); + } + if ( json.animations !== undefined ) { + if ( json.animations.length ) { + animations = animations.concat( json.animations ); + } else { + animations.push( json.animations ); + } + } + + for ( var i = 0; i < animations.length; i ++ ) { + + var clip = THREE.AnimationClip.parseAnimation( animations[i], geometry.bones ); + if ( clip ) outputAnimations.push( clip ); + + } + + // parse implicit morph animations + if ( geometry.morphTargets ) { + + // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary. + var morphAnimationClips = THREE.AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ); + outputAnimations = outputAnimations.concat( morphAnimationClips ); + + } + + if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations; + + }; + + if ( json.materials === undefined || json.materials.length === 0 ) { + + return { geometry: geometry }; + + } else { + + var materials = THREE.Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); + + return { geometry: geometry, materials: materials }; + + } + + } + +}; + +// File:src/loaders/LoadingManager.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LoadingManager = function ( onLoad, onProgress, onError ) { + + var scope = this; + + var isLoading = false, itemsLoaded = 0, itemsTotal = 0; + + this.onStart = undefined; + this.onLoad = onLoad; + this.onProgress = onProgress; + this.onError = onError; + + this.itemStart = function ( url ) { + + itemsTotal ++; + + if ( isLoading === false ) { + + if ( scope.onStart !== undefined ) { + + scope.onStart( url, itemsLoaded, itemsTotal ); + + } + + } + + isLoading = true; + + }; + + this.itemEnd = function ( url ) { + + itemsLoaded ++; + + if ( scope.onProgress !== undefined ) { + + scope.onProgress( url, itemsLoaded, itemsTotal ); + + } + + if ( itemsLoaded === itemsTotal ) { + + isLoading = false; + + if ( scope.onLoad !== undefined ) { + + scope.onLoad(); + + } + + } + + }; + + this.itemError = function ( url ) { + + if ( scope.onError !== undefined ) { + + scope.onError( url ); + + } + + }; + +}; + +THREE.DefaultLoadingManager = new THREE.LoadingManager(); + +// File:src/loaders/BufferGeometryLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BufferGeometryLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.BufferGeometryLoader.prototype = { + + constructor: THREE.BufferGeometryLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( json ) { + + var geometry = new THREE.BufferGeometry(); + + var index = json.data.index; + + if ( index !== undefined ) { + + var typedArray = new self[ index.type ]( index.array ); + geometry.setIndex( new THREE.BufferAttribute( typedArray, 1 ) ); + + } + + var attributes = json.data.attributes; + + for ( var key in attributes ) { + + var attribute = attributes[ key ]; + var typedArray = new self[ attribute.type ]( attribute.array ); + + geometry.addAttribute( key, new THREE.BufferAttribute( typedArray, attribute.itemSize ) ); + + } + + var groups = json.data.groups || json.data.drawcalls || json.data.offsets; + + if ( groups !== undefined ) { + + for ( var i = 0, n = groups.length; i !== n; ++ i ) { + + var group = groups[ i ]; + + geometry.addGroup( group.start, group.count ); + + } + + } + + var boundingSphere = json.data.boundingSphere; + + if ( boundingSphere !== undefined ) { + + var center = new THREE.Vector3(); + + if ( boundingSphere.center !== undefined ) { + + center.fromArray( boundingSphere.center ); + + } + + geometry.boundingSphere = new THREE.Sphere( center, boundingSphere.radius ); + + } + + return geometry; + + } + +}; + +// File:src/loaders/MaterialLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.MaterialLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + this.textures = {}; + +}; + +THREE.MaterialLoader.prototype = { + + constructor: THREE.MaterialLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setTextures: function ( value ) { + + this.textures = value; + + }, + + getTexture: function ( name ) { + + var textures = this.textures; + + if ( textures[ name ] === undefined ) { + + console.warn( 'THREE.MaterialLoader: Undefined texture', name ); + + } + + return textures[ name ]; + + }, + + parse: function ( json ) { + + var material = new THREE[ json.type ]; + material.uuid = json.uuid; + + if ( json.name !== undefined ) material.name = json.name; + if ( json.color !== undefined ) material.color.setHex( json.color ); + if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); + if ( json.specular !== undefined ) material.specular.setHex( json.specular ); + if ( json.shininess !== undefined ) material.shininess = json.shininess; + if ( json.uniforms !== undefined ) material.uniforms = json.uniforms; + if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; + if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; + if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; + if ( json.shading !== undefined ) material.shading = json.shading; + if ( json.blending !== undefined ) material.blending = json.blending; + if ( json.side !== undefined ) material.side = json.side; + if ( json.opacity !== undefined ) material.opacity = json.opacity; + if ( json.transparent !== undefined ) material.transparent = json.transparent; + if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; + if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; + if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; + if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; + if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; + + // for PointsMaterial + if ( json.size !== undefined ) material.size = json.size; + if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; + + // maps + + if ( json.map !== undefined ) material.map = this.getTexture( json.map ); + + if ( json.alphaMap !== undefined ) { + + material.alphaMap = this.getTexture( json.alphaMap ); + material.transparent = true; + + } + + if ( json.bumpMap !== undefined ) material.bumpMap = this.getTexture( json.bumpMap ); + if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; + + if ( json.normalMap !== undefined ) material.normalMap = this.getTexture( json.normalMap ); + if ( json.normalScale ) material.normalScale = new THREE.Vector2( json.normalScale, json.normalScale ); + + if ( json.displacementMap !== undefined ) material.displacementMap = this.getTexture( json.displacementMap ); + if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; + if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; + + if ( json.specularMap !== undefined ) material.specularMap = this.getTexture( json.specularMap ); + + if ( json.envMap !== undefined ) { + + material.envMap = this.getTexture( json.envMap ); + material.combine = THREE.MultiplyOperation; + + } + + if ( json.reflectivity ) material.reflectivity = json.reflectivity; + + if ( json.lightMap !== undefined ) material.lightMap = this.getTexture( json.lightMap ); + if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; + + if ( json.aoMap !== undefined ) material.aoMap = this.getTexture( json.aoMap ); + if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; + + // MeshFaceMaterial + + if ( json.materials !== undefined ) { + + for ( var i = 0, l = json.materials.length; i < l; i ++ ) { + + material.materials.push( this.parse( json.materials[ i ] ) ); + + } + + } + + return material; + + } + +}; + +// File:src/loaders/ObjectLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.ObjectLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + this.texturePath = ''; + +}; + +THREE.ObjectLoader.prototype = { + + constructor: THREE.ObjectLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + if ( this.texturePath === '' ) { + + this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 ); + + } + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + scope.parse( JSON.parse( text ), onLoad ); + + }, onProgress, onError ); + + }, + + setTexturePath: function ( value ) { + + this.texturePath = value; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( json, onLoad ) { + + var geometries = this.parseGeometries( json.geometries ); + + var images = this.parseImages( json.images, function () { + + if ( onLoad !== undefined ) onLoad( object ); + + } ); + + var textures = this.parseTextures( json.textures, images ); + var materials = this.parseMaterials( json.materials, textures ); + + var object = this.parseObject( json.object, geometries, materials ); + + if ( json.animations ) { + + object.animations = this.parseAnimations( json.animations ); + + } + + if ( json.images === undefined || json.images.length === 0 ) { + + if ( onLoad !== undefined ) onLoad( object ); + + } + + return object; + + }, + + parseGeometries: function ( json ) { + + var geometries = {}; + + if ( json !== undefined ) { + + var geometryLoader = new THREE.JSONLoader(); + var bufferGeometryLoader = new THREE.BufferGeometryLoader(); + + for ( var i = 0, l = json.length; i < l; i ++ ) { + + var geometry; + var data = json[ i ]; + + switch ( data.type ) { + + case 'PlaneGeometry': + case 'PlaneBufferGeometry': + + geometry = new THREE[ data.type ]( + data.width, + data.height, + data.widthSegments, + data.heightSegments + ); + + break; + + case 'BoxGeometry': + case 'CubeGeometry': // backwards compatible + + geometry = new THREE.BoxGeometry( + data.width, + data.height, + data.depth, + data.widthSegments, + data.heightSegments, + data.depthSegments + ); + + break; + + case 'CircleBufferGeometry': + + geometry = new THREE.CircleBufferGeometry( + data.radius, + data.segments, + data.thetaStart, + data.thetaLength + ); + + break; + + case 'CircleGeometry': + + geometry = new THREE.CircleGeometry( + data.radius, + data.segments, + data.thetaStart, + data.thetaLength + ); + + break; + + case 'CylinderGeometry': + + geometry = new THREE.CylinderGeometry( + data.radiusTop, + data.radiusBottom, + data.height, + data.radialSegments, + data.heightSegments, + data.openEnded, + data.thetaStart, + data.thetaLength + ); + + break; + + case 'SphereGeometry': + + geometry = new THREE.SphereGeometry( + data.radius, + data.widthSegments, + data.heightSegments, + data.phiStart, + data.phiLength, + data.thetaStart, + data.thetaLength + ); + + break; + + case 'SphereBufferGeometry': + + geometry = new THREE.SphereBufferGeometry( + data.radius, + data.widthSegments, + data.heightSegments, + data.phiStart, + data.phiLength, + data.thetaStart, + data.thetaLength + ); + + break; + + case 'DodecahedronGeometry': + + geometry = new THREE.DodecahedronGeometry( + data.radius, + data.detail + ); + + break; + + case 'IcosahedronGeometry': + + geometry = new THREE.IcosahedronGeometry( + data.radius, + data.detail + ); + + break; + + case 'OctahedronGeometry': + + geometry = new THREE.OctahedronGeometry( + data.radius, + data.detail + ); + + break; + + case 'TetrahedronGeometry': + + geometry = new THREE.TetrahedronGeometry( + data.radius, + data.detail + ); + + break; + + case 'RingGeometry': + + geometry = new THREE.RingGeometry( + data.innerRadius, + data.outerRadius, + data.thetaSegments, + data.phiSegments, + data.thetaStart, + data.thetaLength + ); + + break; + + case 'TorusGeometry': + + geometry = new THREE.TorusGeometry( + data.radius, + data.tube, + data.radialSegments, + data.tubularSegments, + data.arc + ); + + break; + + case 'TorusKnotGeometry': + + geometry = new THREE.TorusKnotGeometry( + data.radius, + data.tube, + data.radialSegments, + data.tubularSegments, + data.p, + data.q, + data.heightScale + ); + + break; + + case 'BufferGeometry': + + geometry = bufferGeometryLoader.parse( data ); + + break; + + case 'Geometry': + + geometry = geometryLoader.parse( data.data, this.texturePath ).geometry; + + break; + + default: + + console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); + + continue; + + } + + geometry.uuid = data.uuid; + + if ( data.name !== undefined ) geometry.name = data.name; + + geometries[ data.uuid ] = geometry; + + } + + } + + return geometries; + + }, + + parseMaterials: function ( json, textures ) { + + var materials = {}; + + if ( json !== undefined ) { + + var loader = new THREE.MaterialLoader(); + loader.setTextures( textures ); + + for ( var i = 0, l = json.length; i < l; i ++ ) { + + var material = loader.parse( json[ i ] ); + materials[ material.uuid ] = material; + + } + + } + + return materials; + + }, + + parseAnimations: function ( json ) { + + var animations = []; + + for ( var i = 0; i < json.length; i ++ ) { + + var clip = THREE.AnimationClip.parse( json[i] ); + + animations.push( clip ); + + } + + return animations; + + }, + + parseImages: function ( json, onLoad ) { + + var scope = this; + var images = {}; + + function loadImage( url ) { + + scope.manager.itemStart( url ); + + return loader.load( url, function () { + + scope.manager.itemEnd( url ); + + } ); + + } + + if ( json !== undefined && json.length > 0 ) { + + var manager = new THREE.LoadingManager( onLoad ); + + var loader = new THREE.ImageLoader( manager ); + loader.setCrossOrigin( this.crossOrigin ); + + for ( var i = 0, l = json.length; i < l; i ++ ) { + + var image = json[ i ]; + var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url; + + images[ image.uuid ] = loadImage( path ); + + } + + } + + return images; + + }, + + parseTextures: function ( json, images ) { + + function parseConstant( value ) { + + if ( typeof( value ) === 'number' ) return value; + + console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); + + return THREE[ value ]; + + } + + var textures = {}; + + if ( json !== undefined ) { + + for ( var i = 0, l = json.length; i < l; i ++ ) { + + var data = json[ i ]; + + if ( data.image === undefined ) { + + console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); + + } + + if ( images[ data.image ] === undefined ) { + + console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); + + } + + var texture = new THREE.Texture( images[ data.image ] ); + texture.needsUpdate = true; + + texture.uuid = data.uuid; + + if ( data.name !== undefined ) texture.name = data.name; + if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping ); + if ( data.offset !== undefined ) texture.offset = new THREE.Vector2( data.offset[ 0 ], data.offset[ 1 ] ); + if ( data.repeat !== undefined ) texture.repeat = new THREE.Vector2( data.repeat[ 0 ], data.repeat[ 1 ] ); + if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter ); + if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter ); + if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; + if ( Array.isArray( data.wrap ) ) { + + texture.wrapS = parseConstant( data.wrap[ 0 ] ); + texture.wrapT = parseConstant( data.wrap[ 1 ] ); + + } + + textures[ data.uuid ] = texture; + + } + + } + + return textures; + + }, + + parseObject: function () { + + var matrix = new THREE.Matrix4(); + + return function ( data, geometries, materials ) { + + var object; + + function getGeometry( name ) { + + if ( geometries[ name ] === undefined ) { + + console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); + + } + + return geometries[ name ]; + + } + + function getMaterial( name ) { + + if ( name === undefined ) return undefined; + + if ( materials[ name ] === undefined ) { + + console.warn( 'THREE.ObjectLoader: Undefined material', name ); + + } + + return materials[ name ]; + + } + + switch ( data.type ) { + + case 'Scene': + + object = new THREE.Scene(); + + break; + + case 'PerspectiveCamera': + + object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); + + break; + + case 'OrthographicCamera': + + object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); + + break; + + case 'AmbientLight': + + object = new THREE.AmbientLight( data.color ); + + break; + + case 'DirectionalLight': + + object = new THREE.DirectionalLight( data.color, data.intensity ); + + break; + + case 'PointLight': + + object = new THREE.PointLight( data.color, data.intensity, data.distance, data.decay ); + + break; + + case 'SpotLight': + + object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent, data.decay ); + + break; + + case 'HemisphereLight': + + object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity ); + + break; + + case 'Mesh': + + object = new THREE.Mesh( getGeometry( data.geometry ), getMaterial( data.material ) ); + + break; + + case 'LOD': + + object = new THREE.LOD(); + + break; + + case 'Line': + + object = new THREE.Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); + + break; + + case 'PointCloud': + case 'Points': + + object = new THREE.Points( getGeometry( data.geometry ), getMaterial( data.material ) ); + + break; + + case 'Sprite': + + object = new THREE.Sprite( getMaterial( data.material ) ); + + break; + + case 'Group': + + object = new THREE.Group(); + + break; + + default: + + object = new THREE.Object3D(); + + } + + object.uuid = data.uuid; + + if ( data.name !== undefined ) object.name = data.name; + if ( data.matrix !== undefined ) { + + matrix.fromArray( data.matrix ); + matrix.decompose( object.position, object.quaternion, object.scale ); + + } else { + + if ( data.position !== undefined ) object.position.fromArray( data.position ); + if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); + if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); + + } + + if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; + if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; + + if ( data.visible !== undefined ) object.visible = data.visible; + if ( data.userData !== undefined ) object.userData = data.userData; + + if ( data.children !== undefined ) { + + for ( var child in data.children ) { + + object.add( this.parseObject( data.children[ child ], geometries, materials ) ); + + } + + } + + if ( data.type === 'LOD' ) { + + var levels = data.levels; + + for ( var l = 0; l < levels.length; l ++ ) { + + var level = levels[ l ]; + var child = object.getObjectByProperty( 'uuid', level.object ); + + if ( child !== undefined ) { + + object.addLevel( child, level.distance ); + + } + + } + + } + + return object; + + } + + }() + +}; + +// File:src/loaders/TextureLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.TextureLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.TextureLoader.prototype = { + + constructor: THREE.TextureLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var texture = new THREE.Texture(); + + var loader = new THREE.ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( image ) { + + texture.image = image; + texture.needsUpdate = true; + + if ( onLoad !== undefined ) { + + onLoad( texture ); + + } + + }, onProgress, onError ); + + return texture; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; + +// File:src/loaders/CubeTextureLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CubeTextureLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.CubeTextureLoader.prototype = { + + constructor: THREE.CubeTextureLoader, + + load: function ( urls, onLoad, onProgress, onError ) { + + var texture = new THREE.CubeTexture( [] ); + + var loader = new THREE.ImageLoader(); + loader.setCrossOrigin( this.crossOrigin ); + + var loaded = 0; + + function loadTexture( i ) { + + loader.load( urls[ i ], function ( image ) { + + texture.images[ i ] = image; + + loaded ++; + + if ( loaded === 6 ) { + + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + } + + }, undefined, onError ); + + } + + for ( var i = 0; i < urls.length; ++ i ) { + + loadTexture( i ); + + } + + return texture; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; + +// File:src/loaders/BinaryTextureLoader.js + +/** + * @author Nikos M. / https://github.com/foo123/ + * + * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) + */ + +THREE.DataTextureLoader = THREE.BinaryTextureLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + // override in sub classes + this._parser = null; + +}; + +THREE.BinaryTextureLoader.prototype = { + + constructor: THREE.BinaryTextureLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var texture = new THREE.DataTexture(); + + var loader = new THREE.XHRLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setResponseType( 'arraybuffer' ); + + loader.load( url, function ( buffer ) { + + var texData = scope._parser( buffer ); + + if ( ! texData ) return; + + if ( undefined !== texData.image ) { + + texture.image = texData.image; + + } else if ( undefined !== texData.data ) { + + texture.image.width = texData.width; + texture.image.height = texData.height; + texture.image.data = texData.data; + + } + + texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : THREE.ClampToEdgeWrapping; + texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : THREE.ClampToEdgeWrapping; + + texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : THREE.LinearFilter; + texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : THREE.LinearMipMapLinearFilter; + + texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1; + + if ( undefined !== texData.format ) { + + texture.format = texData.format; + + } + if ( undefined !== texData.type ) { + + texture.type = texData.type; + + } + + if ( undefined !== texData.mipmaps ) { + + texture.mipmaps = texData.mipmaps; + + } + + if ( 1 === texData.mipmapCount ) { + + texture.minFilter = THREE.LinearFilter; + + } + + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture, texData ); + + }, onProgress, onError ); + + + return texture; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; + +// File:src/loaders/CompressedTextureLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + * + * Abstract Base class to block based textures loader (dds, pvr, ...) + */ + +THREE.CompressedTextureLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + // override in sub classes + this._parser = null; + +}; + + +THREE.CompressedTextureLoader.prototype = { + + constructor: THREE.CompressedTextureLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var images = []; + + var texture = new THREE.CompressedTexture(); + texture.image = images; + + var loader = new THREE.XHRLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setResponseType( 'arraybuffer' ); + + if ( Array.isArray( url ) ) { + + var loaded = 0; + + var loadTexture = function ( i ) { + + loader.load( url[ i ], function ( buffer ) { + + var texDatas = scope._parser( buffer, true ); + + images[ i ] = { + width: texDatas.width, + height: texDatas.height, + format: texDatas.format, + mipmaps: texDatas.mipmaps + }; + + loaded += 1; + + if ( loaded === 6 ) { + + if ( texDatas.mipmapCount === 1 ) + texture.minFilter = THREE.LinearFilter; + + texture.format = texDatas.format; + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + } + + }, onProgress, onError ); + + }; + + for ( var i = 0, il = url.length; i < il; ++ i ) { + + loadTexture( i ); + + } + + } else { + + // compressed cubemap texture stored in a single DDS file + + loader.load( url, function ( buffer ) { + + var texDatas = scope._parser( buffer, true ); + + if ( texDatas.isCubemap ) { + + var faces = texDatas.mipmaps.length / texDatas.mipmapCount; + + for ( var f = 0; f < faces; f ++ ) { + + images[ f ] = { mipmaps : [] }; + + for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { + + images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); + images[ f ].format = texDatas.format; + images[ f ].width = texDatas.width; + images[ f ].height = texDatas.height; + + } + + } + + } else { + + texture.image.width = texDatas.width; + texture.image.height = texDatas.height; + texture.mipmaps = texDatas.mipmaps; + + } + + if ( texDatas.mipmapCount === 1 ) { + + texture.minFilter = THREE.LinearFilter; + + } + + texture.format = texDatas.format; + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + }, onProgress, onError ); + + } + + return texture; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; + +// File:src/materials/Material.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Material = function () { + + Object.defineProperty( this, 'id', { value: THREE.MaterialIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.type = 'Material'; + + this.side = THREE.FrontSide; + + this.opacity = 1; + this.transparent = false; + + this.blending = THREE.NormalBlending; + + this.blendSrc = THREE.SrcAlphaFactor; + this.blendDst = THREE.OneMinusSrcAlphaFactor; + this.blendEquation = THREE.AddEquation; + this.blendSrcAlpha = null; + this.blendDstAlpha = null; + this.blendEquationAlpha = null; + + this.depthFunc = THREE.LessEqualDepth; + this.depthTest = true; + this.depthWrite = true; + + this.colorWrite = true; + + this.precision = null; // override the renderer's default precision for this material + + this.polygonOffset = false; + this.polygonOffsetFactor = 0; + this.polygonOffsetUnits = 0; + + this.alphaTest = 0; + + this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer + + this.visible = true; + + this._needsUpdate = true; + +}; + +THREE.Material.prototype = { + + constructor: THREE.Material, + + get needsUpdate () { + + return this._needsUpdate; + + }, + + set needsUpdate ( value ) { + + if ( value === true ) this.update(); + + this._needsUpdate = value; + + }, + + setValues: function ( values ) { + + if ( values === undefined ) return; + + for ( var key in values ) { + + var newValue = values[ key ]; + + if ( newValue === undefined ) { + + console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); + continue; + + } + + var currentValue = this[ key ]; + + if ( currentValue === undefined ) { + + console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." ); + continue; + + } + + if ( currentValue instanceof THREE.Color ) { + + currentValue.set( newValue ); + + } else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) { + + currentValue.copy( newValue ); + + } else if ( key === 'overdraw' ) { + + // ensure overdraw is backwards-compatible with legacy boolean type + this[ key ] = Number( newValue ); + + } else { + + this[ key ] = newValue; + + } + + } + + }, + + toJSON: function ( meta ) { + + var data = { + metadata: { + version: 4.4, + type: 'Material', + generator: 'Material.toJSON' + } + }; + + // standard Material serialization + data.uuid = this.uuid; + data.type = this.type; + if ( this.name !== '' ) data.name = this.name; + + if ( this.color instanceof THREE.Color ) data.color = this.color.getHex(); + if ( this.emissive instanceof THREE.Color ) data.emissive = this.emissive.getHex(); + if ( this.specular instanceof THREE.Color ) data.specular = this.specular.getHex(); + if ( this.shininess !== undefined ) data.shininess = this.shininess; + + if ( this.map instanceof THREE.Texture ) data.map = this.map.toJSON( meta ).uuid; + if ( this.alphaMap instanceof THREE.Texture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; + if ( this.lightMap instanceof THREE.Texture ) data.lightMap = this.lightMap.toJSON( meta ).uuid; + if ( this.bumpMap instanceof THREE.Texture ) { + + data.bumpMap = this.bumpMap.toJSON( meta ).uuid; + data.bumpScale = this.bumpScale; + + } + if ( this.normalMap instanceof THREE.Texture ) { + + data.normalMap = this.normalMap.toJSON( meta ).uuid; + data.normalScale = this.normalScale; // Removed for now, causes issue in editor ui.js + + } + if ( this.displacementMap instanceof THREE.Texture ) { + + data.displacementMap = this.displacementMap.toJSON( meta ).uuid; + data.displacementScale = this.displacementScale; + data.displacementBias = this.displacementBias; + + } + if ( this.specularMap instanceof THREE.Texture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; + if ( this.envMap instanceof THREE.Texture ) { + + data.envMap = this.envMap.toJSON( meta ).uuid; + data.reflectivity = this.reflectivity; // Scale behind envMap + + } + + if ( this.size !== undefined ) data.size = this.size; + if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; + + if ( this.vertexColors !== undefined && this.vertexColors !== THREE.NoColors ) data.vertexColors = this.vertexColors; + if ( this.shading !== undefined && this.shading !== THREE.SmoothShading ) data.shading = this.shading; + if ( this.blending !== undefined && this.blending !== THREE.NormalBlending ) data.blending = this.blending; + if ( this.side !== undefined && this.side !== THREE.FrontSide ) data.side = this.side; + + if ( this.opacity < 1 ) data.opacity = this.opacity; + if ( this.transparent === true ) data.transparent = this.transparent; + if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; + if ( this.wireframe === true ) data.wireframe = this.wireframe; + if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; + + return data; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( source ) { + + this.name = source.name; + + this.side = source.side; + + this.opacity = source.opacity; + this.transparent = source.transparent; + + this.blending = source.blending; + + this.blendSrc = source.blendSrc; + this.blendDst = source.blendDst; + this.blendEquation = source.blendEquation; + this.blendSrcAlpha = source.blendSrcAlpha; + this.blendDstAlpha = source.blendDstAlpha; + this.blendEquationAlpha = source.blendEquationAlpha; + + this.depthFunc = source.depthFunc; + this.depthTest = source.depthTest; + this.depthWrite = source.depthWrite; + + this.precision = source.precision; + + this.polygonOffset = source.polygonOffset; + this.polygonOffsetFactor = source.polygonOffsetFactor; + this.polygonOffsetUnits = source.polygonOffsetUnits; + + this.alphaTest = source.alphaTest; + + this.overdraw = source.overdraw; + + this.visible = source.visible; + + return this; + + }, + + update: function () { + + this.dispatchEvent( { type: 'update' } ); + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + }, + + // Deprecated + + get wrapAround () { + + console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' ); + + }, + + set wrapAround ( boolean ) { + + console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' ); + + }, + + get wrapRGB () { + + console.warn( 'THREE.' + this.type + ': .wrapRGB has been removed.' ); + return new THREE.Color(); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Material.prototype ); + +THREE.MaterialIdCount = 0; + +// File:src/materials/LineBasicMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * linewidth: , + * linecap: "round", + * linejoin: "round", + * + * vertexColors: + * + * fog: + * } + */ + +THREE.LineBasicMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'LineBasicMaterial'; + + this.color = new THREE.Color( 0xffffff ); + + this.linewidth = 1; + this.linecap = 'round'; + this.linejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.fog = true; + + this.setValues( parameters ); + +}; + +THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.LineBasicMaterial.prototype.constructor = THREE.LineBasicMaterial; + +THREE.LineBasicMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + + this.linewidth = source.linewidth; + this.linecap = source.linecap; + this.linejoin = source.linejoin; + + this.vertexColors = source.vertexColors; + + this.fog = source.fog; + + return this; + +}; + +// File:src/materials/LineDashedMaterial.js + +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * linewidth: , + * + * scale: , + * dashSize: , + * gapSize: , + * + * vertexColors: + * + * fog: + * } + */ + +THREE.LineDashedMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'LineDashedMaterial'; + + this.color = new THREE.Color( 0xffffff ); + + this.linewidth = 1; + + this.scale = 1; + this.dashSize = 3; + this.gapSize = 1; + + this.vertexColors = false; + + this.fog = true; + + this.setValues( parameters ); + +}; + +THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.LineDashedMaterial.prototype.constructor = THREE.LineDashedMaterial; + +THREE.LineDashedMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + + this.linewidth = source.linewidth; + + this.scale = source.scale; + this.dashSize = source.dashSize; + this.gapSize = source.gapSize; + + this.vertexColors = source.vertexColors; + + this.fog = source.fog; + + return this; + +}; + +// File:src/materials/MeshBasicMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * + * fog: + * } + */ + +THREE.MeshBasicMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'MeshBasicMaterial'; + + this.color = new THREE.Color( 0xffffff ); // emissive + + this.map = null; + + this.aoMap = null; + this.aoMapIntensity = 1.0; + + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.fog = true; + + this.shading = THREE.SmoothShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + + this.setValues( parameters ); + +}; + +THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshBasicMaterial.prototype.constructor = THREE.MeshBasicMaterial; + +THREE.MeshBasicMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + + this.map = source.map; + + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + + this.specularMap = source.specularMap; + + this.alphaMap = source.alphaMap; + + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; + + this.fog = source.fog; + + this.shading = source.shading; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + + this.vertexColors = source.vertexColors; + + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + + return this; + +}; + +// File:src/materials/MeshLambertMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * emissive: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } + */ + +THREE.MeshLambertMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'MeshLambertMaterial'; + + this.color = new THREE.Color( 0xffffff ); // diffuse + this.emissive = new THREE.Color( 0x000000 ); + + this.map = null; + + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.fog = true; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; + + this.setValues( parameters ); + +}; + +THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshLambertMaterial.prototype.constructor = THREE.MeshLambertMaterial; + +THREE.MeshLambertMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + this.emissive.copy( source.emissive ); + + this.map = source.map; + + this.specularMap = source.specularMap; + + this.alphaMap = source.alphaMap; + + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; + + this.fog = source.fog; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + + this.vertexColors = source.vertexColors; + + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; + + return this; + +}; + +// File:src/materials/MeshPhongMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * emissive: , + * specular: , + * shininess: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissiveMap: new THREE.Texture( ), + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalScale: , + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } + */ + +THREE.MeshPhongMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'MeshPhongMaterial'; + + this.color = new THREE.Color( 0xffffff ); // diffuse + this.emissive = new THREE.Color( 0x000000 ); + this.specular = new THREE.Color( 0x111111 ); + this.shininess = 30; + + this.metal = false; + + this.map = null; + + this.lightMap = null; + this.lightMapIntensity = 1.0; + + this.aoMap = null; + this.aoMapIntensity = 1.0; + + this.emissiveMap = null; + + this.bumpMap = null; + this.bumpScale = 1; + + this.normalMap = null; + this.normalScale = new THREE.Vector2( 1, 1 ); + + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; + + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.fog = true; + + this.shading = THREE.SmoothShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; + + this.setValues( parameters ); + +}; + +THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshPhongMaterial.prototype.constructor = THREE.MeshPhongMaterial; + +THREE.MeshPhongMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + this.emissive.copy( source.emissive ); + this.specular.copy( source.specular ); + this.shininess = source.shininess; + + this.metal = source.metal; + + this.map = source.map; + + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; + + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + + this.emissiveMap = source.emissiveMap; + + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; + + this.normalMap = source.normalMap; + this.normalScale.copy( source.normalScale ); + + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + + this.specularMap = source.specularMap; + + this.alphaMap = source.alphaMap; + + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; + + this.fog = source.fog; + + this.shading = source.shading; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + + this.vertexColors = source.vertexColors; + + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; + + return this; + +}; + +// File:src/materials/MeshDepthMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * opacity: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: + * } + */ + +THREE.MeshDepthMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'MeshDepthMaterial'; + + this.morphTargets = false; + this.wireframe = false; + this.wireframeLinewidth = 1; + + this.setValues( parameters ); + +}; + +THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshDepthMaterial.prototype.constructor = THREE.MeshDepthMaterial; + +THREE.MeshDepthMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + + return this; + +}; + +// File:src/materials/MeshNormalMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * + * parameters = { + * opacity: , + * + * shading: THREE.FlatShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: + * } + */ + +THREE.MeshNormalMaterial = function ( parameters ) { + + THREE.Material.call( this, parameters ); + + this.type = 'MeshNormalMaterial'; + + this.wireframe = false; + this.wireframeLinewidth = 1; + + this.morphTargets = false; + + this.setValues( parameters ); + +}; + +THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshNormalMaterial.prototype.constructor = THREE.MeshNormalMaterial; + +THREE.MeshNormalMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + + return this; + +}; + +// File:src/materials/MultiMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.MultiMaterial = function ( materials ) { + + this.uuid = THREE.Math.generateUUID(); + + this.type = 'MultiMaterial'; + + this.materials = materials instanceof Array ? materials : []; + + this.visible = true; + +}; + +THREE.MultiMaterial.prototype = { + + constructor: THREE.MultiMaterial, + + toJSON: function () { + + var output = { + metadata: { + version: 4.2, + type: 'material', + generator: 'MaterialExporter' + }, + uuid: this.uuid, + type: this.type, + materials: [] + }; + + for ( var i = 0, l = this.materials.length; i < l; i ++ ) { + + output.materials.push( this.materials[ i ].toJSON() ); + + } + + output.visible = this.visible; + + return output; + + }, + + clone: function () { + + var material = new this.constructor(); + + for ( var i = 0; i < this.materials.length; i ++ ) { + + material.materials.push( this.materials[ i ].clone() ); + + } + + material.visible = this.visible; + + return material; + + } + +}; + +// backwards compatibility + +THREE.MeshFaceMaterial = THREE.MultiMaterial; + +// File:src/materials/PointsMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * size: , + * sizeAttenuation: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * vertexColors: , + * + * fog: + * } + */ + +THREE.PointsMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'PointsMaterial'; + + this.color = new THREE.Color( 0xffffff ); + + this.map = null; + + this.size = 1; + this.sizeAttenuation = true; + + this.vertexColors = THREE.NoColors; + + this.fog = true; + + this.setValues( parameters ); + +}; + +THREE.PointsMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.PointsMaterial.prototype.constructor = THREE.PointsMaterial; + +THREE.PointsMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + + this.map = source.map; + + this.size = source.size; + this.sizeAttenuation = source.sizeAttenuation; + + this.vertexColors = source.vertexColors; + + this.fog = source.fog; + + return this; + +}; + +// backwards compatibility + +THREE.PointCloudMaterial = function ( parameters ) { + + console.warn( 'THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.' ); + return new THREE.PointsMaterial( parameters ); + +}; + +THREE.ParticleBasicMaterial = function ( parameters ) { + + console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.' ); + return new THREE.PointsMaterial( parameters ); + +}; + +THREE.ParticleSystemMaterial = function ( parameters ) { + + console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.' ); + return new THREE.PointsMaterial( parameters ); + +}; + +// File:src/materials/ShaderMaterial.js + +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * defines: { "label" : "value" }, + * uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } }, + * + * fragmentShader: , + * vertexShader: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * lights: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } + */ + +THREE.ShaderMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'ShaderMaterial'; + + this.defines = {}; + this.uniforms = {}; + + this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}'; + this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}'; + + this.shading = THREE.SmoothShading; + + this.linewidth = 1; + + this.wireframe = false; + this.wireframeLinewidth = 1; + + this.fog = false; // set to use scene fog + + this.lights = false; // set to use scene lights + + this.vertexColors = THREE.NoColors; // set to use "color" attribute stream + + this.skinning = false; // set to use skinning attribute streams + + this.morphTargets = false; // set to use morph targets + this.morphNormals = false; // set to use morph normals + + this.derivatives = false; // set to use derivatives + + // When rendered geometry doesn't include these attributes but the material does, + // use these default values in WebGL. This avoids errors when buffer data is missing. + this.defaultAttributeValues = { + 'color': [ 1, 1, 1 ], + 'uv': [ 0, 0 ], + 'uv2': [ 0, 0 ] + }; + + this.index0AttributeName = undefined; + + if ( parameters !== undefined ) { + + if ( parameters.attributes !== undefined ) { + + console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); + + } + + this.setValues( parameters ); + + } + +}; + +THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.ShaderMaterial.prototype.constructor = THREE.ShaderMaterial; + +THREE.ShaderMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.fragmentShader = source.fragmentShader; + this.vertexShader = source.vertexShader; + + this.uniforms = THREE.UniformsUtils.clone( source.uniforms ); + + this.attributes = source.attributes; + this.defines = source.defines; + + this.shading = source.shading; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + + this.fog = source.fog; + + this.lights = source.lights; + + this.vertexColors = source.vertexColors; + + this.skinning = source.skinning; + + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; + + this.derivatives = source.derivatives; + + return this; + +}; + +THREE.ShaderMaterial.prototype.toJSON = function ( meta ) { + + var data = THREE.Material.prototype.toJSON.call( this, meta ); + + data.uniforms = this.uniforms; + data.attributes = this.attributes; + data.vertexShader = this.vertexShader; + data.fragmentShader = this.fragmentShader; + + return data; + +}; + +// File:src/materials/RawShaderMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RawShaderMaterial = function ( parameters ) { + + THREE.ShaderMaterial.call( this, parameters ); + + this.type = 'RawShaderMaterial'; + +}; + +THREE.RawShaderMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype ); +THREE.RawShaderMaterial.prototype.constructor = THREE.RawShaderMaterial; +// File:src/materials/SpriteMaterial.js + +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * uvOffset: new THREE.Vector2(), + * uvScale: new THREE.Vector2(), + * + * fog: + * } + */ + +THREE.SpriteMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'SpriteMaterial'; + + this.color = new THREE.Color( 0xffffff ); + this.map = null; + + this.rotation = 0; + + this.fog = false; + + // set parameters + + this.setValues( parameters ); + +}; + +THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.SpriteMaterial.prototype.constructor = THREE.SpriteMaterial; + +THREE.SpriteMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + this.map = source.map; + + this.rotation = source.rotation; + + this.fog = source.fog; + + return this; + +}; + +// File:src/textures/Texture.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ + +THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + Object.defineProperty( this, 'id', { value: THREE.TextureIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.sourceFile = ''; + + this.image = image !== undefined ? image : THREE.Texture.DEFAULT_IMAGE; + this.mipmaps = []; + + this.mapping = mapping !== undefined ? mapping : THREE.Texture.DEFAULT_MAPPING; + + this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping; + + this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter; + this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter; + + this.anisotropy = anisotropy !== undefined ? anisotropy : 1; + + this.format = format !== undefined ? format : THREE.RGBAFormat; + this.type = type !== undefined ? type : THREE.UnsignedByteType; + + this.offset = new THREE.Vector2( 0, 0 ); + this.repeat = new THREE.Vector2( 1, 1 ); + + this.generateMipmaps = true; + this.premultiplyAlpha = false; + this.flipY = true; + this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) + + this.version = 0; + this.onUpdate = null; + +}; + +THREE.Texture.DEFAULT_IMAGE = undefined; +THREE.Texture.DEFAULT_MAPPING = THREE.UVMapping; + +THREE.Texture.prototype = { + + constructor: THREE.Texture, + + set needsUpdate ( value ) { + + if ( value === true ) this.version ++; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( source ) { + + this.image = source.image; + this.mipmaps = source.mipmaps.slice( 0 ); + + this.mapping = source.mapping; + + this.wrapS = source.wrapS; + this.wrapT = source.wrapT; + + this.magFilter = source.magFilter; + this.minFilter = source.minFilter; + + this.anisotropy = source.anisotropy; + + this.format = source.format; + this.type = source.type; + + this.offset.copy( source.offset ); + this.repeat.copy( source.repeat ); + + this.generateMipmaps = source.generateMipmaps; + this.premultiplyAlpha = source.premultiplyAlpha; + this.flipY = source.flipY; + this.unpackAlignment = source.unpackAlignment; + + return this; + + }, + + toJSON: function ( meta ) { + + if ( meta.textures[ this.uuid ] !== undefined ) { + + return meta.textures[ this.uuid ]; + + } + + function getDataURL( image ) { + + var canvas; + + if ( image.toDataURL !== undefined ) { + + canvas = image; + + } else { + + canvas = document.createElement( 'canvas' ); + canvas.width = image.width; + canvas.height = image.height; + + canvas.getContext( '2d' ).drawImage( image, 0, 0, image.width, image.height ); + + } + + if ( canvas.width > 2048 || canvas.height > 2048 ) { + + return canvas.toDataURL( 'image/jpeg', 0.6 ); + + } else { + + return canvas.toDataURL( 'image/png' ); + + } + + } + + var output = { + metadata: { + version: 4.4, + type: 'Texture', + generator: 'Texture.toJSON' + }, + + uuid: this.uuid, + name: this.name, + + mapping: this.mapping, + + repeat: [ this.repeat.x, this.repeat.y ], + offset: [ this.offset.x, this.offset.y ], + wrap: [ this.wrapS, this.wrapT ], + + minFilter: this.minFilter, + magFilter: this.magFilter, + anisotropy: this.anisotropy + }; + + if ( this.image !== undefined ) { + + // TODO: Move to THREE.Image + + var image = this.image; + + if ( image.uuid === undefined ) { + + image.uuid = THREE.Math.generateUUID(); // UGH + + } + + if ( meta.images[ image.uuid ] === undefined ) { + + meta.images[ image.uuid ] = { + uuid: image.uuid, + url: getDataURL( image ) + }; + + } + + output.image = image.uuid; + + } + + meta.textures[ this.uuid ] = output; + + return output; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + }, + + transformUv: function ( uv ) { + + if ( this.mapping !== THREE.UVMapping ) return; + + uv.multiply( this.repeat ); + uv.add( this.offset ); + + if ( uv.x < 0 || uv.x > 1 ) { + + switch ( this.wrapS ) { + + case THREE.RepeatWrapping: + + uv.x = uv.x - Math.floor( uv.x ); + break; + + case THREE.ClampToEdgeWrapping: + + uv.x = uv.x < 0 ? 0 : 1; + break; + + case THREE.MirroredRepeatWrapping: + + if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { + + uv.x = Math.ceil( uv.x ) - uv.x; + + } else { + + uv.x = uv.x - Math.floor( uv.x ); + + } + break; + + } + + } + + if ( uv.y < 0 || uv.y > 1 ) { + + switch ( this.wrapT ) { + + case THREE.RepeatWrapping: + + uv.y = uv.y - Math.floor( uv.y ); + break; + + case THREE.ClampToEdgeWrapping: + + uv.y = uv.y < 0 ? 0 : 1; + break; + + case THREE.MirroredRepeatWrapping: + + if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { + + uv.y = Math.ceil( uv.y ) - uv.y; + + } else { + + uv.y = uv.y - Math.floor( uv.y ); + + } + break; + + } + + } + + if ( this.flipY ) { + + uv.y = 1 - uv.y; + + } + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype ); + +THREE.TextureIdCount = 0; + +// File:src/textures/CanvasTexture.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CanvasTexture = function ( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + THREE.Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.needsUpdate = true; + +}; + +THREE.CanvasTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.CanvasTexture.prototype.constructor = THREE.CanvasTexture; + +// File:src/textures/CubeTexture.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CubeTexture = function ( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + mapping = mapping !== undefined ? mapping : THREE.CubeReflectionMapping; + + THREE.Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.images = images; + this.flipY = false; + +}; + +THREE.CubeTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.CubeTexture.prototype.constructor = THREE.CubeTexture; + +THREE.CubeTexture.prototype.copy = function ( source ) { + + THREE.Texture.prototype.copy.call( this, source ); + + this.images = source.images; + + return this; + +}; +// File:src/textures/CompressedTexture.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { + + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.image = { width: width, height: height }; + this.mipmaps = mipmaps; + + // no flipping for cube textures + // (also flipping doesn't work for compressed textures ) + + this.flipY = false; + + // can't generate mipmaps for compressed textures + // mips must be embedded in DDS files + + this.generateMipmaps = false; + +}; + +THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.CompressedTexture.prototype.constructor = THREE.CompressedTexture; + +// File:src/textures/DataTexture.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { + + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.image = { data: data, width: width, height: height }; + + this.magFilter = magFilter !== undefined ? magFilter : THREE.NearestFilter; + this.minFilter = minFilter !== undefined ? minFilter : THREE.NearestFilter; + + this.flipY = false; + this.generateMipmaps = false; + +}; + +THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.DataTexture.prototype.constructor = THREE.DataTexture; + +// File:src/textures/VideoTexture.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.VideoTexture = function ( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + THREE.Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.generateMipmaps = false; + + var scope = this; + + function update() { + + requestAnimationFrame( update ); + + if ( video.readyState === video.HAVE_ENOUGH_DATA ) { + + scope.needsUpdate = true; + + } + + } + + update(); + +}; + +THREE.VideoTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.VideoTexture.prototype.constructor = THREE.VideoTexture; + +// File:src/objects/Group.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Group = function () { + + THREE.Object3D.call( this ); + + this.type = 'Group'; + +}; + +THREE.Group.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Group.prototype.constructor = THREE.Group; +// File:src/objects/Points.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Points = function ( geometry, material ) { + + THREE.Object3D.call( this ); + + this.type = 'Points'; + + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.PointsMaterial( { color: Math.random() * 0xffffff } ); + +}; + +THREE.Points.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Points.prototype.constructor = THREE.Points; + +THREE.Points.prototype.raycast = ( function () { + + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + + return function raycast( raycaster, intersects ) { + + var object = this; + var geometry = object.geometry; + var threshold = raycaster.params.Points.threshold; + + inverseMatrix.getInverse( this.matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + + if ( geometry.boundingBox !== null ) { + + if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) { + + return; + + } + + } + + var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + var localThresholdSq = localThreshold * localThreshold; + var position = new THREE.Vector3(); + + function testPoint( point, index ) { + + var rayPointDistanceSq = ray.distanceSqToPoint( point ); + + if ( rayPointDistanceSq < localThresholdSq ) { + + var intersectPoint = ray.closestPointToPoint( point ); + intersectPoint.applyMatrix4( object.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( intersectPoint ); + + if ( distance < raycaster.near || distance > raycaster.far ) return; + + intersects.push( { + + distance: distance, + distanceToRay: Math.sqrt( rayPointDistanceSq ), + point: intersectPoint.clone(), + index: index, + face: null, + object: object + + } ); + + } + + } + + if ( geometry instanceof THREE.BufferGeometry ) { + + var index = geometry.index; + var attributes = geometry.attributes; + var positions = attributes.position.array; + + if ( index !== null ) { + + var indices = index.array; + + for ( var i = 0, il = indices.length; i < il; i ++ ) { + + var a = indices[ i ]; + + position.fromArray( positions, a * 3 ); + + testPoint( position, a ); + + } + + } else { + + for ( var i = 0, l = positions.length / 3; i < l; i ++ ) { + + position.fromArray( positions, i * 3 ); + + testPoint( position, i ); + + } + + } + + } else { + + var vertices = geometry.vertices; + + for ( var i = 0, l = vertices.length; i < l; i ++ ) { + + testPoint( vertices[ i ], i ); + + } + + } + + }; + +}() ); + +THREE.Points.prototype.clone = function () { + + return new this.constructor( this.geometry, this.material ).copy( this ); + +}; + +// Backwards compatibility + +THREE.PointCloud = function ( geometry, material ) { + + console.warn( 'THREE.PointCloud has been renamed to THREE.Points.' ); + return new THREE.Points( geometry, material ); + +}; + +THREE.ParticleSystem = function ( geometry, material ) { + + console.warn( 'THREE.ParticleSystem has been renamed to THREE.Points.' ); + return new THREE.Points( geometry, material ); + +}; + +// File:src/objects/Line.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Line = function ( geometry, material, mode ) { + + if ( mode === 1 ) { + + console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' ); + return new THREE.LineSegments( geometry, material ); + + } + + THREE.Object3D.call( this ); + + this.type = 'Line'; + + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } ); + +}; + +THREE.Line.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Line.prototype.constructor = THREE.Line; + +THREE.Line.prototype.raycast = ( function () { + + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + var sphere = new THREE.Sphere(); + + return function raycast( raycaster, intersects ) { + + var precision = raycaster.linePrecision; + var precisionSq = precision * precision; + + var geometry = this.geometry; + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + // Checking boundingSphere distance to ray + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( this.matrixWorld ); + + if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { + + return; + + } + + inverseMatrix.getInverse( this.matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + + var vStart = new THREE.Vector3(); + var vEnd = new THREE.Vector3(); + var interSegment = new THREE.Vector3(); + var interRay = new THREE.Vector3(); + var step = this instanceof THREE.LineSegments ? 2 : 1; + + if ( geometry instanceof THREE.BufferGeometry ) { + + var index = geometry.index; + var attributes = geometry.attributes; + + if ( index !== null ) { + + var indices = index.array; + var positions = attributes.position.array; + + for ( var i = 0, l = indices.length - 1; i < l; i += step ) { + + var a = indices[ i ]; + var b = indices[ i + 1 ]; + + vStart.fromArray( positions, a * 3 ); + vEnd.fromArray( positions, b * 3 ); + + var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + + if ( distSq > precisionSq ) continue; + + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + + var distance = raycaster.ray.origin.distanceTo( interRay ); + + if ( distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this + + } ); + + } + + } else { + + var positions = attributes.position.array; + + for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) { + + vStart.fromArray( positions, 3 * i ); + vEnd.fromArray( positions, 3 * i + 3 ); + + var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + + if ( distSq > precisionSq ) continue; + + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + + var distance = raycaster.ray.origin.distanceTo( interRay ); + + if ( distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this + + } ); + + } + + } + + } else if ( geometry instanceof THREE.Geometry ) { + + var vertices = geometry.vertices; + var nbVertices = vertices.length; + + for ( var i = 0; i < nbVertices - 1; i += step ) { + + var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); + + if ( distSq > precisionSq ) continue; + + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + + var distance = raycaster.ray.origin.distanceTo( interRay ); + + if ( distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this + + } ); + + } + + } + + }; + +}() ); + +THREE.Line.prototype.clone = function () { + + return new this.constructor( this.geometry, this.material ).copy( this ); + +}; + +// DEPRECATED + +THREE.LineStrip = 0; +THREE.LinePieces = 1; + +// File:src/objects/LineSegments.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LineSegments = function ( geometry, material ) { + + THREE.Line.call( this, geometry, material ); + + this.type = 'LineSegments'; + +}; + +THREE.LineSegments.prototype = Object.create( THREE.Line.prototype ); +THREE.LineSegments.prototype.constructor = THREE.LineSegments; + +// File:src/objects/Mesh.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author jonobr1 / http://jonobr1.com/ + */ + +THREE.Mesh = function ( geometry, material ) { + + THREE.Object3D.call( this ); + + this.type = 'Mesh'; + + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } ); + + this.updateMorphTargets(); + +}; + +THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Mesh.prototype.constructor = THREE.Mesh; + +THREE.Mesh.prototype.updateMorphTargets = function () { + + if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) { + + this.morphTargetBase = - 1; + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; + + for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) { + + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m; + + } + + } + +}; + +THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) { + + if ( this.morphTargetDictionary[ name ] !== undefined ) { + + return this.morphTargetDictionary[ name ]; + + } + + console.warn( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' ); + + return 0; + +}; + + +THREE.Mesh.prototype.raycast = ( function () { + + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + var sphere = new THREE.Sphere(); + + var vA = new THREE.Vector3(); + var vB = new THREE.Vector3(); + var vC = new THREE.Vector3(); + + var tempA = new THREE.Vector3(); + var tempB = new THREE.Vector3(); + var tempC = new THREE.Vector3(); + + var uvA = new THREE.Vector2(); + var uvB = new THREE.Vector2(); + var uvC = new THREE.Vector2(); + + var barycoord = new THREE.Vector3(); + + var intersectionPoint = new THREE.Vector3(); + var intersectionPointWorld = new THREE.Vector3(); + + function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) { + + THREE.Triangle.barycoordFromPoint( point, p1, p2, p3, barycoord ); + + uv1.multiplyScalar( barycoord.x ); + uv2.multiplyScalar( barycoord.y ); + uv3.multiplyScalar( barycoord.z ); + + uv1.add( uv2 ).add( uv3 ); + + return uv1.clone(); + + } + + function checkIntersection( object, raycaster, ray, pA, pB, pC, point ){ + + var intersect; + var material = object.material; + + if ( material.side === THREE.BackSide ) { + + intersect = ray.intersectTriangle( pC, pB, pA, true, point ); + + } else { + + intersect = ray.intersectTriangle( pA, pB, pC, material.side !== THREE.DoubleSide, point ); + + } + + if ( intersect === null ) return null; + + intersectionPointWorld.copy( point ); + intersectionPointWorld.applyMatrix4( object.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); + + if ( distance < raycaster.near || distance > raycaster.far ) return null; + + return { + distance: distance, + point: intersectionPointWorld.clone(), + object: object + }; + + } + + function checkBufferGeometryIntersection( object, raycaster, ray, positions, uvs, a, b, c ) { + + vA.fromArray( positions, a * 3 ); + vB.fromArray( positions, b * 3 ); + vC.fromArray( positions, c * 3 ); + + var intersection = checkIntersection( object, raycaster, ray, vA, vB, vC, intersectionPoint ); + + if ( intersection ) { + + if ( uvs ) { + + uvA.fromArray( uvs, a * 2 ); + uvB.fromArray( uvs, b * 2 ); + uvC.fromArray( uvs, c * 2 ); + + intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC ); + + } + + intersection.face = new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ); + intersection.faceIndex = a; + + } + + return intersection; + + } + + return function raycast( raycaster, intersects ) { + + var geometry = this.geometry; + var material = this.material; + + if ( material === undefined ) return; + + // Checking boundingSphere distance to ray + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + var matrixWorld = this.matrixWorld; + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( matrixWorld ); + + if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) return; + + // Check boundingBox before continuing + + inverseMatrix.getInverse( matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + + if ( geometry.boundingBox !== null ) { + + if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) return; + + } + + var uvs, intersection; + + if ( geometry instanceof THREE.BufferGeometry ) { + + var a, b, c; + var index = geometry.index; + var attributes = geometry.attributes; + var positions = attributes.position.array; + + if ( attributes.uv !== undefined ){ + + uvs = attributes.uv.array; + + } + + if ( index !== null ) { + + var indices = index.array; + + for ( var i = 0, l = indices.length; i < l; i += 3 ) { + + a = indices[ i ]; + b = indices[ i + 1 ]; + c = indices[ i + 2 ]; + + intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c ); + + if ( intersection ) { + + intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indices buffer semantics + intersects.push( intersection ); + + } + + } + + } else { + + + for ( var i = 0, l = positions.length; i < l; i += 9 ) { + + a = i / 3; + b = a + 1; + c = a + 2; + + intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c ); + + if ( intersection ) { + + intersection.index = a; // triangle number in positions buffer semantics + intersects.push( intersection ); + + } + + } + + } + + } else if ( geometry instanceof THREE.Geometry ) { + + var fvA, fvB, fvC; + var isFaceMaterial = material instanceof THREE.MeshFaceMaterial; + var materials = isFaceMaterial === true ? material.materials : null; + + var vertices = geometry.vertices; + var faces = geometry.faces; + var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; + if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs; + + for ( var f = 0, fl = faces.length; f < fl; f ++ ) { + + var face = faces[ f ]; + var faceMaterial = isFaceMaterial === true ? materials[ face.materialIndex ] : material; + + if ( faceMaterial === undefined ) continue; + + fvA = vertices[ face.a ]; + fvB = vertices[ face.b ]; + fvC = vertices[ face.c ]; + + if ( faceMaterial.morphTargets === true ) { + + var morphTargets = geometry.morphTargets; + var morphInfluences = this.morphTargetInfluences; + + vA.set( 0, 0, 0 ); + vB.set( 0, 0, 0 ); + vC.set( 0, 0, 0 ); + + for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { + + var influence = morphInfluences[ t ]; + + if ( influence === 0 ) continue; + + var targets = morphTargets[ t ].vertices; + + vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence ); + vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence ); + vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence ); + + } + + vA.add( fvA ); + vB.add( fvB ); + vC.add( fvC ); + + fvA = vA; + fvB = vB; + fvC = vC; + + } + + intersection = checkIntersection( this, raycaster, ray, fvA, fvB, fvC, intersectionPoint ); + + if ( intersection ) { + + if ( uvs ) { + + var uvs_f = uvs[ f ]; + uvA.copy( uvs_f[ 0 ] ); + uvB.copy( uvs_f[ 1 ] ); + uvC.copy( uvs_f[ 2 ] ); + + intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC ); + + } + + intersection.face = face; + intersection.faceIndex = f; + intersects.push( intersection ); + + } + + } + + } + + }; + +}() ); + +THREE.Mesh.prototype.clone = function () { + + return new this.constructor( this.geometry, this.material ).copy( this ); + +}; + +// File:src/objects/Bone.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author ikerr / http://verold.com + */ + +THREE.Bone = function ( skin ) { + + THREE.Object3D.call( this ); + + this.type = 'Bone'; + + this.skin = skin; + +}; + +THREE.Bone.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Bone.prototype.constructor = THREE.Bone; + +THREE.Bone.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source ); + + this.skin = source.skin; + + return this; + +}; + +// File:src/objects/Skeleton.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author michael guerrero / http://realitymeltdown.com + * @author ikerr / http://verold.com + */ + +THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) { + + this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true; + + this.identityMatrix = new THREE.Matrix4(); + + // copy the bone array + + bones = bones || []; + + this.bones = bones.slice( 0 ); + + // create a bone texture or an array of floats + + if ( this.useVertexTexture ) { + + // layout (1 matrix = 4 pixels) + // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) + // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) + // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) + // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) + // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) + + + var size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix + size = THREE.Math.nextPowerOfTwo( Math.ceil( size ) ); + size = Math.max( size, 4 ); + + this.boneTextureWidth = size; + this.boneTextureHeight = size; + + this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel + this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType ); + + } else { + + this.boneMatrices = new Float32Array( 16 * this.bones.length ); + + } + + // use the supplied bone inverses or calculate the inverses + + if ( boneInverses === undefined ) { + + this.calculateInverses(); + + } else { + + if ( this.bones.length === boneInverses.length ) { + + this.boneInverses = boneInverses.slice( 0 ); + + } else { + + console.warn( 'THREE.Skeleton bonInverses is the wrong length.' ); + + this.boneInverses = []; + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + this.boneInverses.push( new THREE.Matrix4() ); + + } + + } + + } + +}; + +THREE.Skeleton.prototype.calculateInverses = function () { + + this.boneInverses = []; + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + var inverse = new THREE.Matrix4(); + + if ( this.bones[ b ] ) { + + inverse.getInverse( this.bones[ b ].matrixWorld ); + + } + + this.boneInverses.push( inverse ); + + } + +}; + +THREE.Skeleton.prototype.pose = function () { + + var bone; + + // recover the bind-time world matrices + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + bone = this.bones[ b ]; + + if ( bone ) { + + bone.matrixWorld.getInverse( this.boneInverses[ b ] ); + + } + + } + + // compute the local matrices, positions, rotations and scales + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + bone = this.bones[ b ]; + + if ( bone ) { + + if ( bone.parent ) { + + bone.matrix.getInverse( bone.parent.matrixWorld ); + bone.matrix.multiply( bone.matrixWorld ); + + } else { + + bone.matrix.copy( bone.matrixWorld ); + + } + + bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); + + } + + } + +}; + +THREE.Skeleton.prototype.update = ( function () { + + var offsetMatrix = new THREE.Matrix4(); + + return function update() { + + // flatten bone matrices to array + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + // compute the offset between the current and the original transform + + var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix; + + offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] ); + offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 ); + + } + + if ( this.useVertexTexture ) { + + this.boneTexture.needsUpdate = true; + + } + + }; + +} )(); + +THREE.Skeleton.prototype.clone = function () { + + return new THREE.Skeleton( this.bones, this.boneInverses, this.useVertexTexture ); + +}; + +// File:src/objects/SkinnedMesh.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author ikerr / http://verold.com + */ + +THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) { + + THREE.Mesh.call( this, geometry, material ); + + this.type = 'SkinnedMesh'; + + this.bindMode = "attached"; + this.bindMatrix = new THREE.Matrix4(); + this.bindMatrixInverse = new THREE.Matrix4(); + + // init bones + + // TODO: remove bone creation as there is no reason (other than + // convenience) for THREE.SkinnedMesh to do this. + + var bones = []; + + if ( this.geometry && this.geometry.bones !== undefined ) { + + var bone, gbone; + + for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { + + gbone = this.geometry.bones[ b ]; + + bone = new THREE.Bone( this ); + bones.push( bone ); + + bone.name = gbone.name; + bone.position.fromArray( gbone.pos ); + bone.quaternion.fromArray( gbone.rotq ); + if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); + + } + + for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { + + gbone = this.geometry.bones[ b ]; + + if ( gbone.parent !== - 1 && gbone.parent !== null) { + + bones[ gbone.parent ].add( bones[ b ] ); + + } else { + + this.add( bones[ b ] ); + + } + + } + + } + + this.normalizeSkinWeights(); + + this.updateMatrixWorld( true ); + this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ), this.matrixWorld ); + +}; + + +THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype ); +THREE.SkinnedMesh.prototype.constructor = THREE.SkinnedMesh; + +THREE.SkinnedMesh.prototype.bind = function( skeleton, bindMatrix ) { + + this.skeleton = skeleton; + + if ( bindMatrix === undefined ) { + + this.updateMatrixWorld( true ); + + this.skeleton.calculateInverses(); + + bindMatrix = this.matrixWorld; + + } + + this.bindMatrix.copy( bindMatrix ); + this.bindMatrixInverse.getInverse( bindMatrix ); + +}; + +THREE.SkinnedMesh.prototype.pose = function () { + + this.skeleton.pose(); + +}; + +THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () { + + if ( this.geometry instanceof THREE.Geometry ) { + + for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) { + + var sw = this.geometry.skinWeights[ i ]; + + var scale = 1.0 / sw.lengthManhattan(); + + if ( scale !== Infinity ) { + + sw.multiplyScalar( scale ); + + } else { + + sw.set( 1 ); // this will be normalized by the shader anyway + + } + + } + + } else { + + // skinning weights assumed to be normalized for THREE.BufferGeometry + + } + +}; + +THREE.SkinnedMesh.prototype.updateMatrixWorld = function( force ) { + + THREE.Mesh.prototype.updateMatrixWorld.call( this, true ); + + if ( this.bindMode === "attached" ) { + + this.bindMatrixInverse.getInverse( this.matrixWorld ); + + } else if ( this.bindMode === "detached" ) { + + this.bindMatrixInverse.getInverse( this.bindMatrix ); + + } else { + + console.warn( 'THREE.SkinnedMesh unrecognized bindMode: ' + this.bindMode ); + + } + +}; + +THREE.SkinnedMesh.prototype.clone = function() { + + return new this.constructor( this.geometry, this.material, this.useVertexTexture ).copy( this ); + +}; + +// File:src/objects/LOD.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LOD = function () { + + THREE.Object3D.call( this ); + + this.type = 'LOD'; + + Object.defineProperties( this, { + levels: { + enumerable: true, + value: [] + }, + objects: { + get: function () { + + console.warn( 'THREE.LOD: .objects has been renamed to .levels.' ); + return this.levels; + + } + } + } ); + +}; + + +THREE.LOD.prototype = Object.create( THREE.Object3D.prototype ); +THREE.LOD.prototype.constructor = THREE.LOD; + +THREE.LOD.prototype.addLevel = function ( object, distance ) { + + if ( distance === undefined ) distance = 0; + + distance = Math.abs( distance ); + + var levels = this.levels; + + for ( var l = 0; l < levels.length; l ++ ) { + + if ( distance < levels[ l ].distance ) { + + break; + + } + + } + + levels.splice( l, 0, { distance: distance, object: object } ); + + this.add( object ); + +}; + +THREE.LOD.prototype.getObjectForDistance = function ( distance ) { + + var levels = this.levels; + + for ( var i = 1, l = levels.length; i < l; i ++ ) { + + if ( distance < levels[ i ].distance ) { + + break; + + } + + } + + return levels[ i - 1 ].object; + +}; + +THREE.LOD.prototype.raycast = ( function () { + + var matrixPosition = new THREE.Vector3(); + + return function raycast( raycaster, intersects ) { + + matrixPosition.setFromMatrixPosition( this.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( matrixPosition ); + + this.getObjectForDistance( distance ).raycast( raycaster, intersects ); + + }; + +}() ); + +THREE.LOD.prototype.update = function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + + return function update( camera ) { + + var levels = this.levels; + + if ( levels.length > 1 ) { + + v1.setFromMatrixPosition( camera.matrixWorld ); + v2.setFromMatrixPosition( this.matrixWorld ); + + var distance = v1.distanceTo( v2 ); + + levels[ 0 ].object.visible = true; + + for ( var i = 1, l = levels.length; i < l; i ++ ) { + + if ( distance >= levels[ i ].distance ) { + + levels[ i - 1 ].object.visible = false; + levels[ i ].object.visible = true; + + } else { + + break; + + } + + } + + for ( ; i < l; i ++ ) { + + levels[ i ].object.visible = false; + + } + + } + + }; + +}(); + +THREE.LOD.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source, false ); + + var levels = source.levels; + + for ( var i = 0, l = levels.length; i < l; i ++ ) { + + var level = levels[ i ]; + + this.addLevel( level.object.clone(), level.distance ); + + } + + return this; + +}; + +THREE.LOD.prototype.toJSON = function ( meta ) { + + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + + data.object.levels = []; + + var levels = this.levels; + + for ( var i = 0, l = levels.length; i < l; i ++ ) { + + var level = levels[ i ]; + + data.object.levels.push( { + object: level.object.uuid, + distance: level.distance + } ); + + } + + return data; + +}; + +// File:src/objects/Sprite.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Sprite = ( function () { + + var indices = new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] ); + var vertices = new Float32Array( [ - 0.5, - 0.5, 0, 0.5, - 0.5, 0, 0.5, 0.5, 0, - 0.5, 0.5, 0 ] ); + var uvs = new Float32Array( [ 0, 0, 1, 0, 1, 1, 0, 1 ] ); + + var geometry = new THREE.BufferGeometry(); + geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) ); + geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + + return function Sprite( material ) { + + THREE.Object3D.call( this ); + + this.type = 'Sprite'; + + this.geometry = geometry; + this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial(); + + }; + +} )(); + +THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Sprite.prototype.constructor = THREE.Sprite; + +THREE.Sprite.prototype.raycast = ( function () { + + var matrixPosition = new THREE.Vector3(); + + return function raycast( raycaster, intersects ) { + + matrixPosition.setFromMatrixPosition( this.matrixWorld ); + + var distanceSq = raycaster.ray.distanceSqToPoint( matrixPosition ); + var guessSizeSq = this.scale.x * this.scale.y; + + if ( distanceSq > guessSizeSq ) { + + return; + + } + + intersects.push( { + + distance: Math.sqrt( distanceSq ), + point: this.position, + face: null, + object: this + + } ); + + }; + +}() ); + +THREE.Sprite.prototype.clone = function () { + + return new this.constructor( this.material ).copy( this ); + +}; + +// Backwards compatibility + +THREE.Particle = THREE.Sprite; + +// File:src/objects/LensFlare.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.LensFlare = function ( texture, size, distance, blending, color ) { + + THREE.Object3D.call( this ); + + this.lensFlares = []; + + this.positionScreen = new THREE.Vector3(); + this.customUpdateCallback = undefined; + + if ( texture !== undefined ) { + + this.add( texture, size, distance, blending, color ); + + } + +}; + +THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype ); +THREE.LensFlare.prototype.constructor = THREE.LensFlare; + + +/* + * Add: adds another flare + */ + +THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) { + + if ( size === undefined ) size = - 1; + if ( distance === undefined ) distance = 0; + if ( opacity === undefined ) opacity = 1; + if ( color === undefined ) color = new THREE.Color( 0xffffff ); + if ( blending === undefined ) blending = THREE.NormalBlending; + + distance = Math.min( distance, Math.max( 0, distance ) ); + + this.lensFlares.push( { + texture: texture, // THREE.Texture + size: size, // size in pixels (-1 = use texture.width) + distance: distance, // distance (0-1) from light source (0=at light source) + x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is in front z = 1 is back + scale: 1, // scale + rotation: 0, // rotation + opacity: opacity, // opacity + color: color, // color + blending: blending // blending + } ); + +}; + +/* + * Update lens flares update positions on all flares based on the screen position + * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way. + */ + +THREE.LensFlare.prototype.updateLensFlares = function () { + + var f, fl = this.lensFlares.length; + var flare; + var vecX = - this.positionScreen.x * 2; + var vecY = - this.positionScreen.y * 2; + + for ( f = 0; f < fl; f ++ ) { + + flare = this.lensFlares[ f ]; + + flare.x = this.positionScreen.x + vecX * flare.distance; + flare.y = this.positionScreen.y + vecY * flare.distance; + + flare.wantedRotation = flare.x * Math.PI * 0.25; + flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; + + } + +}; + +THREE.LensFlare.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source ); + + this.positionScreen.copy( source.positionScreen ); + this.customUpdateCallback = source.customUpdateCallback; + + for ( var i = 0, l = source.lensFlares.length; i < l; i ++ ) { + + this.lensFlares.push( source.lensFlares[ i ] ); + + } + + return this; + +}; + +// File:src/scenes/Scene.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Scene = function () { + + THREE.Object3D.call( this ); + + this.type = 'Scene'; + + this.fog = null; + this.overrideMaterial = null; + + this.autoUpdate = true; // checked by the renderer + +}; + +THREE.Scene.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Scene.prototype.constructor = THREE.Scene; + +THREE.Scene.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source ); + + if ( source.fog !== null ) this.fog = source.fog.clone(); + if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); + + this.autoUpdate = source.autoUpdate; + this.matrixAutoUpdate = source.matrixAutoUpdate; + + return this; + +}; + +// File:src/scenes/Fog.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Fog = function ( color, near, far ) { + + this.name = ''; + + this.color = new THREE.Color( color ); + + this.near = ( near !== undefined ) ? near : 1; + this.far = ( far !== undefined ) ? far : 1000; + +}; + +THREE.Fog.prototype.clone = function () { + + return new THREE.Fog( this.color.getHex(), this.near, this.far ); + +}; + +// File:src/scenes/FogExp2.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.FogExp2 = function ( color, density ) { + + this.name = ''; + + this.color = new THREE.Color( color ); + this.density = ( density !== undefined ) ? density : 0.00025; + +}; + +THREE.FogExp2.prototype.clone = function () { + + return new THREE.FogExp2( this.color.getHex(), this.density ); + +}; + +// File:src/renderers/shaders/ShaderChunk.js + +THREE.ShaderChunk = {}; + +// File:src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl + +THREE.ShaderChunk[ 'alphamap_fragment'] = "#ifdef USE_ALPHAMAP\n\n diffuseColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl + +THREE.ShaderChunk[ 'alphamap_pars_fragment'] = "#ifdef USE_ALPHAMAP\n\n uniform sampler2D alphaMap;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl + +THREE.ShaderChunk[ 'alphatest_fragment'] = "#ifdef ALPHATEST\n\n if ( diffuseColor.a < ALPHATEST ) discard;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/aomap_fragment.glsl + +THREE.ShaderChunk[ 'aomap_fragment'] = "#ifdef USE_AOMAP\n\n totalAmbientLight *= ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/aomap_pars_fragment.glsl + +THREE.ShaderChunk[ 'aomap_pars_fragment'] = "#ifdef USE_AOMAP\n\n uniform sampler2D aoMap;\n uniform float aoMapIntensity;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/begin_vertex.glsl + +THREE.ShaderChunk[ 'begin_vertex'] = "\nvec3 transformed = vec3( position );\n"; + +// File:src/renderers/shaders/ShaderChunk/beginnormal_vertex.glsl + +THREE.ShaderChunk[ 'beginnormal_vertex'] = "\nvec3 objectNormal = vec3( normal );\n"; + +// File:src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'bumpmap_pars_fragment'] = "#ifdef USE_BUMPMAP\n\n uniform sampler2D bumpMap;\n uniform float bumpScale;\n\n\n\n vec2 dHdxy_fwd() {\n\n vec2 dSTdx = dFdx( vUv );\n vec2 dSTdy = dFdy( vUv );\n\n float Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n return vec2( dBx, dBy );\n\n }\n\n vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n vec3 vSigmaX = dFdx( surf_pos );\n vec3 vSigmaY = dFdy( surf_pos );\n vec3 vN = surf_norm;\n vec3 R1 = cross( vSigmaY, vN );\n vec3 R2 = cross( vN, vSigmaX );\n\n float fDet = dot( vSigmaX, R1 );\n\n vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n return normalize( abs( fDet ) * surf_norm - vGrad );\n\n }\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/color_fragment.glsl + +THREE.ShaderChunk[ 'color_fragment'] = "#ifdef USE_COLOR\n\n diffuseColor.rgb *= vColor;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl + +THREE.ShaderChunk[ 'color_pars_fragment'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl + +THREE.ShaderChunk[ 'color_pars_vertex'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/color_vertex.glsl + +THREE.ShaderChunk[ 'color_vertex'] = "#ifdef USE_COLOR\n\n vColor.xyz = color.xyz;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/common.glsl + +THREE.ShaderChunk[ 'common'] = "#define PI 3.14159\n#define PI2 6.28318\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\n\nvec3 transformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( matrix * vec4( normal, 0.0 ) ).xyz );\n\n}\n\nvec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );\n\n}\n\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n float distance = dot( planeNormal, point - pointOnPlane );\n\n return - distance * planeNormal + point;\n\n}\n\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return sign( dot( point - pointOnPlane, planeNormal ) );\n\n}\n\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n\n}\n\nfloat calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) {\n\n if ( decayExponent > 0.0 ) {\n\n return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\n }\n\n return 1.0;\n\n}\n\nvec3 F_Schlick( in vec3 specularColor, in float dotLH ) {\n\n\n float fresnel = exp2( ( -5.55437 * dotLH - 6.98316 ) * dotLH );\n\n return ( 1.0 - specularColor ) * fresnel + specularColor;\n\n}\n\nfloat G_BlinnPhong_Implicit( /* in float dotNL, in float dotNV */ ) {\n\n\n return 0.25;\n\n}\n\nfloat D_BlinnPhong( in float shininess, in float dotNH ) {\n\n\n return ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n\n}\n\nvec3 BRDF_BlinnPhong( in vec3 specularColor, in float shininess, in vec3 normal, in vec3 lightDir, in vec3 viewDir ) {\n\n vec3 halfDir = normalize( lightDir + viewDir );\n\n float dotNH = saturate( dot( normal, halfDir ) );\n float dotLH = saturate( dot( lightDir, halfDir ) );\n\n vec3 F = F_Schlick( specularColor, dotLH );\n\n float G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );\n\n float D = D_BlinnPhong( shininess, dotNH );\n\n return F * G * D;\n\n}\n\nvec3 inputToLinear( in vec3 a ) {\n\n #ifdef GAMMA_INPUT\n\n return pow( a, vec3( float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n\nvec3 linearToOutput( in vec3 a ) {\n\n #ifdef GAMMA_OUTPUT\n\n return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n"; + +// File:src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl + +THREE.ShaderChunk[ 'defaultnormal_vertex'] = "#ifdef FLIP_SIDED\n\n objectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;\n"; + +// File:src/renderers/shaders/ShaderChunk/displacementmap_vertex.glsl + +THREE.ShaderChunk[ 'displacementmap_vertex'] = "#ifdef USE_DISPLACEMENTMAP\n\n transformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/displacementmap_pars_vertex.glsl + +THREE.ShaderChunk[ 'displacementmap_pars_vertex'] = "#ifdef USE_DISPLACEMENTMAP\n\n uniform sampler2D displacementMap;\n uniform float displacementScale;\n uniform float displacementBias;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl + +THREE.ShaderChunk[ 'emissivemap_fragment'] = "#ifdef USE_EMISSIVEMAP\n\n vec4 emissiveColor = texture2D( emissiveMap, vUv );\n\n emissiveColor.rgb = inputToLinear( emissiveColor.rgb );\n\n totalEmissiveLight *= emissiveColor.rgb;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/emissivemap_pars_fragment.glsl + +THREE.ShaderChunk[ 'emissivemap_pars_fragment'] = "#ifdef USE_EMISSIVEMAP\n\n uniform sampler2D emissiveMap;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl + +THREE.ShaderChunk[ 'envmap_fragment'] = "#ifdef USE_ENVMAP\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n #else\n\n vec3 reflectVec = vReflect;\n\n #endif\n\n #ifdef DOUBLE_SIDED\n float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n #else\n float flipNormal = 1.0;\n #endif\n\n #ifdef ENVMAP_TYPE_CUBE\n vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #elif defined( ENVMAP_TYPE_EQUIREC )\n vec2 sampleUV;\n sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n vec4 envColor = texture2D( envMap, sampleUV );\n\n #elif defined( ENVMAP_TYPE_SPHERE )\n vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n #endif\n\n envColor.xyz = inputToLinear( envColor.xyz );\n\n #ifdef ENVMAP_BLENDING_MULTIPLY\n\n outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_MIX )\n\n outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_ADD )\n\n outgoingLight += envColor.xyz * specularStrength * reflectivity;\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'envmap_pars_fragment'] = "#ifdef USE_ENVMAP\n\n uniform float reflectivity;\n #ifdef ENVMAP_TYPE_CUBE\n uniform samplerCube envMap;\n #else\n uniform sampler2D envMap;\n #endif\n uniform float flipEnvMap;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n uniform float refractionRatio;\n\n #else\n\n varying vec3 vReflect;\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl + +THREE.ShaderChunk[ 'envmap_pars_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n varying vec3 vReflect;\n\n uniform float refractionRatio;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl + +THREE.ShaderChunk[ 'envmap_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vReflect = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/fog_fragment.glsl + +THREE.ShaderChunk[ 'fog_fragment'] = "#ifdef USE_FOG\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n float depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n #else\n\n float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n #endif\n\n #ifdef FOG_EXP2\n\n float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\n\n #else\n\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n\n #endif\n \n outgoingLight = mix( outgoingLight, fogColor, fogFactor );\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl + +THREE.ShaderChunk[ 'fog_pars_fragment'] = "#ifdef USE_FOG\n\n uniform vec3 fogColor;\n\n #ifdef FOG_EXP2\n\n uniform float fogDensity;\n\n #else\n\n uniform float fogNear;\n uniform float fogFar;\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/hemilight_fragment.glsl + +THREE.ShaderChunk[ 'hemilight_fragment'] = "#if MAX_HEMI_LIGHTS > 0\n\n for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lightDir = hemisphereLightDirection[ i ];\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vec3 lightColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n totalAmbientLight += lightColor;\n\n }\n\n#endif\n\n"; + +// File:src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl + +THREE.ShaderChunk[ 'lightmap_fragment'] = "#ifdef USE_LIGHTMAP\n\n totalAmbientLight += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'lightmap_pars_fragment'] = "#ifdef USE_LIGHTMAP\n\n uniform sampler2D lightMap;\n uniform float lightMapIntensity;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl + +THREE.ShaderChunk[ 'lights_lambert_pars_vertex'] = "#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl + +THREE.ShaderChunk[ 'lights_lambert_vertex'] = "vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vec3( 0.0 );\n\n#endif\n\nvec3 normal = normalize( transformedNormal );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lVector = pointLightPosition[ i ] - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n #endif\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n #endif\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * saturate( - dotProduct );\n\n #endif\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lightDir = hemisphereLightDirection[ i ];\n\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n #ifdef DOUBLE_SIDED\n\n float hemiDiffuseWeightBack = - 0.5 * dotProduct + 0.5;\n\n vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n #endif\n\n }\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl + +THREE.ShaderChunk[ 'lights_phong_fragment'] = "vec3 viewDir = normalize( vViewPosition );\n\nvec3 totalDiffuseLight = vec3( 0.0 );\nvec3 totalSpecularLight = vec3( 0.0 );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lightPosition = pointLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * cosineTerm;\n\n }\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl + +THREE.ShaderChunk[ 'lights_phong_pars_fragment'] = "uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\nvarying vec3 vViewPosition;\n\n#ifndef FLAT_SHADED\n\n varying vec3 vNormal;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl + +THREE.ShaderChunk[ 'lights_phong_pars_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_vertex.glsl + +THREE.ShaderChunk[ 'lights_phong_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n vWorldPosition = worldPosition.xyz;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl + +THREE.ShaderChunk[ 'linear_to_gamma_fragment'] = "\n outgoingLight = linearToOutput( outgoingLight );\n"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl + +THREE.ShaderChunk[ 'logdepthbuf_fragment'] = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl + +THREE.ShaderChunk[ 'logdepthbuf_pars_fragment'] = "#ifdef USE_LOGDEPTHBUF\n\n uniform float logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl + +THREE.ShaderChunk[ 'logdepthbuf_pars_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n uniform float logDepthBufFC;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl + +THREE.ShaderChunk[ 'logdepthbuf_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n vFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/map_fragment.glsl + +THREE.ShaderChunk[ 'map_fragment'] = "#ifdef USE_MAP\n\n vec4 texelColor = texture2D( map, vUv );\n\n texelColor.xyz = inputToLinear( texelColor.xyz );\n\n diffuseColor *= texelColor;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl + +THREE.ShaderChunk[ 'map_pars_fragment'] = "#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl + +THREE.ShaderChunk[ 'map_particle_fragment'] = "#ifdef USE_MAP\n\n diffuseColor *= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl + +THREE.ShaderChunk[ 'map_particle_pars_fragment'] = "#ifdef USE_MAP\n\n uniform vec4 offsetRepeat;\n uniform sampler2D map;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl + +THREE.ShaderChunk[ 'morphnormal_vertex'] = "#ifdef USE_MORPHNORMALS\n\n objectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n objectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n objectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n objectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl + +THREE.ShaderChunk[ 'morphtarget_pars_vertex'] = "#ifdef USE_MORPHTARGETS\n\n #ifndef USE_MORPHNORMALS\n\n uniform float morphTargetInfluences[ 8 ];\n\n #else\n\n uniform float morphTargetInfluences[ 4 ];\n\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl + +THREE.ShaderChunk[ 'morphtarget_vertex'] = "#ifdef USE_MORPHTARGETS\n\n transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n #ifndef USE_MORPHNORMALS\n\n transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/normal_phong_fragment.glsl + +THREE.ShaderChunk[ 'normal_phong_fragment'] = "#ifndef FLAT_SHADED\n\n vec3 normal = normalize( vNormal );\n\n #ifdef DOUBLE_SIDED\n\n normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n #endif\n\n#else\n\n vec3 fdx = dFdx( vViewPosition );\n vec3 fdy = dFdy( vViewPosition );\n vec3 normal = normalize( cross( fdx, fdy ) );\n\n#endif\n\n#ifdef USE_NORMALMAP\n\n normal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\n"; + +// File:src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'normalmap_pars_fragment'] = "#ifdef USE_NORMALMAP\n\n uniform sampler2D normalMap;\n uniform vec2 normalScale;\n\n\n vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n vec3 q0 = dFdx( eye_pos.xyz );\n vec3 q1 = dFdy( eye_pos.xyz );\n vec2 st0 = dFdx( vUv.st );\n vec2 st1 = dFdy( vUv.st );\n\n vec3 S = normalize( q0 * st1.t - q1 * st0.t );\n vec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n vec3 N = normalize( surf_norm );\n\n vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n mapN.xy = normalScale * mapN.xy;\n mat3 tsn = mat3( S, T, N );\n return normalize( tsn * mapN );\n\n }\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/project_vertex.glsl + +THREE.ShaderChunk[ 'project_vertex'] = "#ifdef USE_SKINNING\n\n vec4 mvPosition = modelViewMatrix * skinned;\n\n#else\n\n vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;\n"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl + +THREE.ShaderChunk[ 'shadowmap_fragment'] = "#ifdef USE_SHADOWMAP\n\n for ( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n float texelSizeY = 1.0 / shadowMapSize[ i ].y;\n\n float shadow = 0.0;\n\n#if defined( POINT_LIGHT_SHADOWS )\n\n bool isPointLight = shadowDarkness[ i ] < 0.0;\n\n if ( isPointLight ) {\n\n float realShadowDarkness = abs( shadowDarkness[ i ] );\n\n vec3 lightToPosition = vShadowCoord[ i ].xyz;\n\n #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n vec3 bd3D = normalize( lightToPosition );\n float dp = length( lightToPosition );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D, texelSizeY ) ), shadowBias[ i ], shadow );\n\n\n #if defined( SHADOWMAP_TYPE_PCF )\n const float Dr = 1.25;\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n const float Dr = 2.25;\n #endif\n\n float os = Dr * 2.0 * texelSizeY;\n\n const vec3 Gsd = vec3( - 1, 0, 1 );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zyz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xyz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zyx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xyx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n\n shadow *= realShadowDarkness * ( 1.0 / 21.0 );\n\n #else \n vec3 bd3D = normalize( lightToPosition );\n float dp = length( lightToPosition );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D, texelSizeY ) ), shadowBias[ i ], shadow );\n\n shadow *= realShadowDarkness;\n\n #endif\n\n } else {\n\n#endif \n float texelSizeX = 1.0 / shadowMapSize[ i ].x;\n\n vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n\n bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n bool inFrustum = all( inFrustumVec );\n\n bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n bool frustumTest = all( frustumTestVec );\n\n if ( frustumTest ) {\n\n #if defined( SHADOWMAP_TYPE_PCF )\n\n\n /*\n for ( float y = -1.25; y <= 1.25; y += 1.25 )\n for ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n if ( fDepth < shadowCoord.z )\n shadow += 1.0;\n }\n shadow /= 9.0;\n */\n\n shadowCoord.z += shadowBias[ i ];\n\n const float ShadowDelta = 1.0 / 9.0;\n\n float xPixelOffset = texelSizeX;\n float yPixelOffset = texelSizeY;\n\n float dx0 = - 1.25 * xPixelOffset;\n float dy0 = - 1.25 * yPixelOffset;\n float dx1 = 1.25 * xPixelOffset;\n float dy1 = 1.25 * yPixelOffset;\n\n float fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n shadow *= shadowDarkness[ i ];\n\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n\n shadowCoord.z += shadowBias[ i ];\n\n float xPixelOffset = texelSizeX;\n float yPixelOffset = texelSizeY;\n\n float dx0 = - 1.0 * xPixelOffset;\n float dy0 = - 1.0 * yPixelOffset;\n float dx1 = 1.0 * xPixelOffset;\n float dy1 = 1.0 * yPixelOffset;\n\n mat3 shadowKernel;\n mat3 depthKernel;\n\n depthKernel[ 0 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n depthKernel[ 0 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n depthKernel[ 0 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n depthKernel[ 1 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n depthKernel[ 1 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n depthKernel[ 1 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n depthKernel[ 2 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n depthKernel[ 2 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n depthKernel[ 2 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n vec3 shadowZ = vec3( shadowCoord.z );\n shadowKernel[ 0 ] = vec3( lessThan( depthKernel[ 0 ], shadowZ ) );\n shadowKernel[ 0 ] *= vec3( 0.25 );\n\n shadowKernel[ 1 ] = vec3( lessThan( depthKernel[ 1 ], shadowZ ) );\n shadowKernel[ 1 ] *= vec3( 0.25 );\n\n shadowKernel[ 2 ] = vec3( lessThan( depthKernel[ 2 ], shadowZ ) );\n shadowKernel[ 2 ] *= vec3( 0.25 );\n\n vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[ i ].xy );\n\n shadowKernel[ 0 ] = mix( shadowKernel[ 1 ], shadowKernel[ 0 ], fractionalCoord.x );\n shadowKernel[ 1 ] = mix( shadowKernel[ 2 ], shadowKernel[ 1 ], fractionalCoord.x );\n\n vec4 shadowValues;\n shadowValues.x = mix( shadowKernel[ 0 ][ 1 ], shadowKernel[ 0 ][ 0 ], fractionalCoord.y );\n shadowValues.y = mix( shadowKernel[ 0 ][ 2 ], shadowKernel[ 0 ][ 1 ], fractionalCoord.y );\n shadowValues.z = mix( shadowKernel[ 1 ][ 1 ], shadowKernel[ 1 ][ 0 ], fractionalCoord.y );\n shadowValues.w = mix( shadowKernel[ 1 ][ 2 ], shadowKernel[ 1 ][ 1 ], fractionalCoord.y );\n\n shadow = dot( shadowValues, vec4( 1.0 ) ) * shadowDarkness[ i ];\n\n #else \n shadowCoord.z += shadowBias[ i ];\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n shadow = shadowDarkness[ i ];\n\n #endif\n\n }\n\n#ifdef SHADOWMAP_DEBUG\n\n if ( inFrustum ) {\n\n if ( i == 0 ) {\n\n outgoingLight *= vec3( 1.0, 0.5, 0.0 );\n\n } else if ( i == 1 ) {\n\n outgoingLight *= vec3( 0.0, 1.0, 0.8 );\n\n } else {\n\n outgoingLight *= vec3( 0.0, 0.5, 1.0 );\n\n }\n\n }\n\n#endif\n\n#if defined( POINT_LIGHT_SHADOWS )\n\n }\n\n#endif\n\n shadowMask = shadowMask * vec3( 1.0 - shadow );\n\n }\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'shadowmap_pars_fragment'] = "#ifdef USE_SHADOWMAP\n\n uniform sampler2D shadowMap[ MAX_SHADOWS ];\n uniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform float shadowBias[ MAX_SHADOWS ];\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n float unpackDepth( const in vec4 rgba_depth ) {\n\n const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n float depth = dot( rgba_depth, bit_shift );\n return depth;\n\n }\n\n #if defined(POINT_LIGHT_SHADOWS)\n\n\n void adjustShadowValue1K( const float testDepth, const vec4 textureData, const float bias, inout float shadowValue ) {\n\n const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n if ( testDepth >= dot( textureData, bitSh ) * 1000.0 + bias )\n shadowValue += 1.0;\n\n }\n\n\n vec2 cubeToUV( vec3 v, float texelSizeY ) {\n\n\n vec3 absV = abs( v );\n\n\n float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n absV *= scaleToCube;\n\n\n v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\n\n\n vec2 planar = v.xy;\n\n float almostATexel = 1.5 * texelSizeY;\n float almostOne = 1.0 - almostATexel;\n\n if ( absV.z >= almostOne ) {\n\n if ( v.z > 0.0 )\n planar.x = 4.0 - v.x;\n\n } else if ( absV.x >= almostOne ) {\n\n float signX = sign( v.x );\n planar.x = v.z * signX + 2.0 * signX;\n\n } else if ( absV.y >= almostOne ) {\n\n float signY = sign( v.y );\n planar.x = v.x + 2.0 * signY + 2.0;\n planar.y = v.z * signY - 2.0;\n\n }\n\n\n return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\n }\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl + +THREE.ShaderChunk[ 'shadowmap_pars_vertex'] = "#ifdef USE_SHADOWMAP\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform mat4 shadowMatrix[ MAX_SHADOWS ];\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl + +THREE.ShaderChunk[ 'shadowmap_vertex'] = "#ifdef USE_SHADOWMAP\n\n for ( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n }\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl + +THREE.ShaderChunk[ 'skinbase_vertex'] = "#ifdef USE_SKINNING\n\n mat4 boneMatX = getBoneMatrix( skinIndex.x );\n mat4 boneMatY = getBoneMatrix( skinIndex.y );\n mat4 boneMatZ = getBoneMatrix( skinIndex.z );\n mat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl + +THREE.ShaderChunk[ 'skinning_pars_vertex'] = "#ifdef USE_SKINNING\n\n uniform mat4 bindMatrix;\n uniform mat4 bindMatrixInverse;\n\n #ifdef BONE_TEXTURE\n\n uniform sampler2D boneTexture;\n uniform int boneTextureWidth;\n uniform int boneTextureHeight;\n\n mat4 getBoneMatrix( const in float i ) {\n\n float j = i * 4.0;\n float x = mod( j, float( boneTextureWidth ) );\n float y = floor( j / float( boneTextureWidth ) );\n\n float dx = 1.0 / float( boneTextureWidth );\n float dy = 1.0 / float( boneTextureHeight );\n\n y = dy * ( y + 0.5 );\n\n vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n mat4 bone = mat4( v1, v2, v3, v4 );\n\n return bone;\n\n }\n\n #else\n\n uniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n mat4 getBoneMatrix( const in float i ) {\n\n mat4 bone = boneGlobalMatrices[ int(i) ];\n return bone;\n\n }\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/skinning_vertex.glsl + +THREE.ShaderChunk[ 'skinning_vertex'] = "#ifdef USE_SKINNING\n\n vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\n vec4 skinned = vec4( 0.0 );\n skinned += boneMatX * skinVertex * skinWeight.x;\n skinned += boneMatY * skinVertex * skinWeight.y;\n skinned += boneMatZ * skinVertex * skinWeight.z;\n skinned += boneMatW * skinVertex * skinWeight.w;\n skinned = bindMatrixInverse * skinned;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl + +THREE.ShaderChunk[ 'skinnormal_vertex'] = "#ifdef USE_SKINNING\n\n mat4 skinMatrix = mat4( 0.0 );\n skinMatrix += skinWeight.x * boneMatX;\n skinMatrix += skinWeight.y * boneMatY;\n skinMatrix += skinWeight.z * boneMatZ;\n skinMatrix += skinWeight.w * boneMatW;\n skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\n objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl + +THREE.ShaderChunk[ 'specularmap_fragment'] = "float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n vec4 texelSpecular = texture2D( specularMap, vUv );\n specularStrength = texelSpecular.r;\n\n#else\n\n specularStrength = 1.0;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'specularmap_pars_fragment'] = "#ifdef USE_SPECULARMAP\n\n uniform sampler2D specularMap;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/uv2_pars_fragment.glsl + +THREE.ShaderChunk[ 'uv2_pars_fragment'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n varying vec2 vUv2;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/uv2_pars_vertex.glsl + +THREE.ShaderChunk[ 'uv2_pars_vertex'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n attribute vec2 uv2;\n varying vec2 vUv2;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/uv2_vertex.glsl + +THREE.ShaderChunk[ 'uv2_vertex'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n vUv2 = uv2;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl + +THREE.ShaderChunk[ 'uv_pars_fragment'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl + +THREE.ShaderChunk[ 'uv_pars_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n uniform vec4 offsetRepeat;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/uv_vertex.glsl + +THREE.ShaderChunk[ 'uv_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl + +THREE.ShaderChunk[ 'worldpos_vertex'] = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n #ifdef USE_SKINNING\n\n vec4 worldPosition = modelMatrix * skinned;\n\n #else\n\n vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/UniformsUtils.js + +/** + * Uniform Utilities + */ + +THREE.UniformsUtils = { + + merge: function ( uniforms ) { + + var merged = {}; + + for ( var u = 0; u < uniforms.length; u ++ ) { + + var tmp = this.clone( uniforms[ u ] ); + + for ( var p in tmp ) { + + merged[ p ] = tmp[ p ]; + + } + + } + + return merged; + + }, + + clone: function ( uniforms_src ) { + + var uniforms_dst = {}; + + for ( var u in uniforms_src ) { + + uniforms_dst[ u ] = {}; + + for ( var p in uniforms_src[ u ] ) { + + var parameter_src = uniforms_src[ u ][ p ]; + + if ( parameter_src instanceof THREE.Color || + parameter_src instanceof THREE.Vector2 || + parameter_src instanceof THREE.Vector3 || + parameter_src instanceof THREE.Vector4 || + parameter_src instanceof THREE.Matrix3 || + parameter_src instanceof THREE.Matrix4 || + parameter_src instanceof THREE.Texture ) { + + uniforms_dst[ u ][ p ] = parameter_src.clone(); + + } else if ( Array.isArray( parameter_src ) ) { + + uniforms_dst[ u ][ p ] = parameter_src.slice(); + + } else { + + uniforms_dst[ u ][ p ] = parameter_src; + + } + + } + + } + + return uniforms_dst; + + } + +}; + +// File:src/renderers/shaders/UniformsLib.js + +/** + * Uniforms library for shared webgl shaders + */ + +THREE.UniformsLib = { + + common: { + + "diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity" : { type: "f", value: 1.0 }, + + "map" : { type: "t", value: null }, + "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, + + "specularMap" : { type: "t", value: null }, + "alphaMap" : { type: "t", value: null }, + + "envMap" : { type: "t", value: null }, + "flipEnvMap" : { type: "f", value: - 1 }, + "reflectivity" : { type: "f", value: 1.0 }, + "refractionRatio" : { type: "f", value: 0.98 } + + }, + + aomap: { + + "aoMap" : { type: "t", value: null }, + "aoMapIntensity" : { type: "f", value: 1 }, + + }, + + lightmap: { + + "lightMap" : { type: "t", value: null }, + "lightMapIntensity" : { type: "f", value: 1 }, + + }, + + emissivemap: { + + "emissiveMap" : { type: "t", value: null }, + + }, + + bumpmap: { + + "bumpMap" : { type: "t", value: null }, + "bumpScale" : { type: "f", value: 1 } + + }, + + normalmap: { + + "normalMap" : { type: "t", value: null }, + "normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) } + + }, + + displacementmap: { + + "displacementMap" : { type: "t", value: null }, + "displacementScale" : { type: "f", value: 1 }, + "displacementBias" : { type: "f", value: 0 } + + }, + + fog : { + + "fogDensity" : { type: "f", value: 0.00025 }, + "fogNear" : { type: "f", value: 1 }, + "fogFar" : { type: "f", value: 2000 }, + "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } + + }, + + lights: { + + "ambientLightColor" : { type: "fv", value: [] }, + + "directionalLightDirection" : { type: "fv", value: [] }, + "directionalLightColor" : { type: "fv", value: [] }, + + "hemisphereLightDirection" : { type: "fv", value: [] }, + "hemisphereLightSkyColor" : { type: "fv", value: [] }, + "hemisphereLightGroundColor" : { type: "fv", value: [] }, + + "pointLightColor" : { type: "fv", value: [] }, + "pointLightPosition" : { type: "fv", value: [] }, + "pointLightDistance" : { type: "fv1", value: [] }, + "pointLightDecay" : { type: "fv1", value: [] }, + + "spotLightColor" : { type: "fv", value: [] }, + "spotLightPosition" : { type: "fv", value: [] }, + "spotLightDirection" : { type: "fv", value: [] }, + "spotLightDistance" : { type: "fv1", value: [] }, + "spotLightAngleCos" : { type: "fv1", value: [] }, + "spotLightExponent" : { type: "fv1", value: [] }, + "spotLightDecay" : { type: "fv1", value: [] } + + }, + + points: { + + "psColor" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity" : { type: "f", value: 1.0 }, + "size" : { type: "f", value: 1.0 }, + "scale" : { type: "f", value: 1.0 }, + "map" : { type: "t", value: null }, + "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, + + "fogDensity" : { type: "f", value: 0.00025 }, + "fogNear" : { type: "f", value: 1 }, + "fogFar" : { type: "f", value: 2000 }, + "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } + + }, + + shadowmap: { + + "shadowMap": { type: "tv", value: [] }, + "shadowMapSize": { type: "v2v", value: [] }, + + "shadowBias" : { type: "fv1", value: [] }, + "shadowDarkness": { type: "fv1", value: [] }, + + "shadowMatrix" : { type: "m4v", value: [] } + + } + +}; + +// File:src/renderers/shaders/ShaderLib.js + +/** + * Webgl Shader Library for three.js + * + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + */ + + +THREE.ShaderLib = { + + 'basic': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "aomap" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "shadowmap" ] + + ] ), + + vertexShader: [ + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "uv_pars_vertex" ], + THREE.ShaderChunk[ "uv2_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "uv_vertex" ], + THREE.ShaderChunk[ "uv2_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + + " #ifdef USE_ENVMAP", + + THREE.ShaderChunk[ "beginnormal_vertex" ], + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], + + " #endif", + + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform float opacity;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "uv_pars_fragment" ], + THREE.ShaderChunk[ "uv2_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "aomap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vec4( diffuse, opacity );", + " vec3 totalAmbientLight = vec3( 1.0 );", // hardwired + " vec3 shadowMask = vec3( 1.0 );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + THREE.ShaderChunk[ "aomap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + " outgoingLight = diffuseColor.rgb * totalAmbientLight * shadowMask;", + + THREE.ShaderChunk[ "envmap_fragment" ], + + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + + THREE.ShaderChunk[ "fog_fragment" ], + + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + + "}" + + ].join( "\n" ) + + }, + + 'lambert': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], + + { + "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) } + } + + ] ), + + vertexShader: [ + + "#define LAMBERT", + + "varying vec3 vLightFront;", + + "#ifdef DOUBLE_SIDED", + + " varying vec3 vLightBack;", + + "#endif", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "uv_pars_vertex" ], + THREE.ShaderChunk[ "uv2_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "lights_lambert_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "uv_vertex" ], + THREE.ShaderChunk[ "uv2_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + + THREE.ShaderChunk[ "beginnormal_vertex" ], + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], + + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "lights_lambert_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform vec3 emissive;", + "uniform float opacity;", + + "uniform vec3 ambientLightColor;", + + "varying vec3 vLightFront;", + + "#ifdef DOUBLE_SIDED", + + " varying vec3 vLightBack;", + + "#endif", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "uv_pars_fragment" ], + THREE.ShaderChunk[ "uv2_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " vec3 outgoingLight = vec3( 0.0 );", // outgoing light does not have an alpha, the surface does + " vec4 diffuseColor = vec4( diffuse, opacity );", + " vec3 totalAmbientLight = ambientLightColor;", + " vec3 shadowMask = vec3( 1.0 );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + " #ifdef DOUBLE_SIDED", + + " if ( gl_FrontFacing )", + " outgoingLight += diffuseColor.rgb * ( vLightFront * shadowMask + totalAmbientLight ) + emissive;", + " else", + " outgoingLight += diffuseColor.rgb * ( vLightBack * shadowMask + totalAmbientLight ) + emissive;", + + " #else", + + " outgoingLight += diffuseColor.rgb * ( vLightFront * shadowMask + totalAmbientLight ) + emissive;", + + " #endif", + + THREE.ShaderChunk[ "envmap_fragment" ], + + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + + THREE.ShaderChunk[ "fog_fragment" ], + + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + + "}" + + ].join( "\n" ) + + }, + + 'phong': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "aomap" ], + THREE.UniformsLib[ "lightmap" ], + THREE.UniformsLib[ "emissivemap" ], + THREE.UniformsLib[ "bumpmap" ], + THREE.UniformsLib[ "normalmap" ], + THREE.UniformsLib[ "displacementmap" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], + + { + "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, + "specular" : { type: "c", value: new THREE.Color( 0x111111 ) }, + "shininess": { type: "f", value: 30 } + } + + ] ), + + vertexShader: [ + + "#define PHONG", + + "varying vec3 vViewPosition;", + + "#ifndef FLAT_SHADED", + + " varying vec3 vNormal;", + + "#endif", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "uv_pars_vertex" ], + THREE.ShaderChunk[ "uv2_pars_vertex" ], + THREE.ShaderChunk[ "displacementmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "lights_phong_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "uv_vertex" ], + THREE.ShaderChunk[ "uv2_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + + THREE.ShaderChunk[ "beginnormal_vertex" ], + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], + + "#ifndef FLAT_SHADED", // Normal computed with derivatives when FLAT_SHADED + + " vNormal = normalize( transformedNormal );", + + "#endif", + + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "displacementmap_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + " vViewPosition = - mvPosition.xyz;", + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "lights_phong_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "#define PHONG", + + "uniform vec3 diffuse;", + "uniform vec3 emissive;", + "uniform vec3 specular;", + "uniform float shininess;", + "uniform float opacity;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "uv_pars_fragment" ], + THREE.ShaderChunk[ "uv2_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "aomap_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "emissivemap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "lights_phong_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "bumpmap_pars_fragment" ], + THREE.ShaderChunk[ "normalmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vec4( diffuse, opacity );", + " vec3 totalAmbientLight = ambientLightColor;", + " vec3 totalEmissiveLight = emissive;", + " vec3 shadowMask = vec3( 1.0 );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + THREE.ShaderChunk[ "normal_phong_fragment" ], + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "hemilight_fragment" ], + THREE.ShaderChunk[ "aomap_fragment" ], + THREE.ShaderChunk[ "emissivemap_fragment" ], + + THREE.ShaderChunk[ "lights_phong_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + "totalDiffuseLight *= shadowMask;", + "totalSpecularLight *= shadowMask;", + + "#ifdef METAL", + + " outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) * specular + totalSpecularLight + totalEmissiveLight;", + + "#else", + + " outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) + totalSpecularLight + totalEmissiveLight;", + + "#endif", + + THREE.ShaderChunk[ "envmap_fragment" ], + + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + + THREE.ShaderChunk[ "fog_fragment" ], + + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + + "}" + + ].join( "\n" ) + + }, + + 'points': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "points" ], + THREE.UniformsLib[ "shadowmap" ] + + ] ), + + vertexShader: [ + + "uniform float size;", + "uniform float scale;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "color_vertex" ], + + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + + " #ifdef USE_SIZEATTENUATION", + " gl_PointSize = size * ( scale / length( mvPosition.xyz ) );", + " #else", + " gl_PointSize = size;", + " #endif", + + " gl_Position = projectionMatrix * mvPosition;", + + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 psColor;", + "uniform float opacity;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_particle_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vec4( psColor, opacity );", + " vec3 shadowMask = vec3( 1.0 );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_particle_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + " outgoingLight = diffuseColor.rgb * shadowMask;", + + THREE.ShaderChunk[ "fog_fragment" ], + + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + + "}" + + ].join( "\n" ) + + }, + + 'dashed': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + + { + "scale" : { type: "f", value: 1 }, + "dashSize" : { type: "f", value: 1 }, + "totalSize": { type: "f", value: 2 } + } + + ] ), + + vertexShader: [ + + "uniform float scale;", + "attribute float lineDistance;", + + "varying float vLineDistance;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "color_vertex" ], + + " vLineDistance = scale * lineDistance;", + + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + " gl_Position = projectionMatrix * mvPosition;", + + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform float opacity;", + + "uniform float dashSize;", + "uniform float totalSize;", + + "varying float vLineDistance;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " if ( mod( vLineDistance, totalSize ) > dashSize ) {", + + " discard;", + + " }", + + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vec4( diffuse, opacity );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + + " outgoingLight = diffuseColor.rgb;", // simple shader + + THREE.ShaderChunk[ "fog_fragment" ], + + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + + "}" + + ].join( "\n" ) + + }, + + 'depth': { + + uniforms: { + + "mNear": { type: "f", value: 1.0 }, + "mFar" : { type: "f", value: 2000.0 }, + "opacity" : { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform float mNear;", + "uniform float mFar;", + "uniform float opacity;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + " #ifdef USE_LOGDEPTHBUF_EXT", + + " float depth = gl_FragDepthEXT / gl_FragCoord.w;", + + " #else", + + " float depth = gl_FragCoord.z / gl_FragCoord.w;", + + " #endif", + + " float color = 1.0 - smoothstep( mNear, mFar, depth );", + " gl_FragColor = vec4( vec3( color ), opacity );", + + "}" + + ].join( "\n" ) + + }, + + 'normal': { + + uniforms: { + + "opacity" : { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + "varying vec3 vNormal;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + " vNormal = normalize( normalMatrix * normal );", + + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform float opacity;", + "varying vec3 vNormal;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + "}" + + ].join( "\n" ) + + }, + + /* ------------------------------------------------------------------------- + // Cube map shader + ------------------------------------------------------------------------- */ + + 'cube': { + + uniforms: { "tCube": { type: "t", value: null }, + "tFlip": { type: "f", value: - 1 } }, + + vertexShader: [ + + "varying vec3 vWorldPosition;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + " vWorldPosition = transformDirection( position, modelMatrix );", + + " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform samplerCube tCube;", + "uniform float tFlip;", + + "varying vec3 vWorldPosition;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + "}" + + ].join( "\n" ) + + }, + + /* ------------------------------------------------------------------------- + // Cube map shader + ------------------------------------------------------------------------- */ + + 'equirect': { + + uniforms: { "tEquirect": { type: "t", value: null }, + "tFlip": { type: "f", value: - 1 } }, + + vertexShader: [ + + "varying vec3 vWorldPosition;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + " vWorldPosition = transformDirection( position, modelMatrix );", + + " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tEquirect;", + "uniform float tFlip;", + + "varying vec3 vWorldPosition;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + // " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", + "vec3 direction = normalize( vWorldPosition );", + "vec2 sampleUV;", + "sampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );", + "sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;", + "gl_FragColor = texture2D( tEquirect, sampleUV );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + "}" + + ].join( "\n" ) + + }, + + /* Depth encoding into RGBA texture + * + * based on SpiderGL shadow map example + * http://spidergl.org/example.php?id=6 + * + * originally from + * http://www.gamedev.net/topic/442138-packing-a-float-into-a-a8r8g8b8-texture-shader/page__whichpage__1%25EF%25BF%25BD + * + * see also + * http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ + */ + + 'depthRGBA': { + + uniforms: {}, + + vertexShader: [ + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "skinbase_vertex" ], + + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "vec4 pack_depth( const in float depth ) {", + + " const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );", + " const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );", + " vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );", // " vec4 res = fract( depth * bit_shift );", + " res -= res.xxyz * bit_mask;", + " return res;", + + "}", + + "void main() {", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + " #ifdef USE_LOGDEPTHBUF_EXT", + + " gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );", + + " #else", + + " gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );", + + " #endif", + + //"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z / gl_FragCoord.w );", + //"float z = ( ( gl_FragCoord.z / gl_FragCoord.w ) - 3.0 ) / ( 4000.0 - 3.0 );", + //"gl_FragData[ 0 ] = pack_depth( z );", + //"gl_FragData[ 0 ] = vec4( z, z, z, 1.0 );", + + "}" + + ].join( "\n" ) + + }, + + + 'distanceRGBA': { + + uniforms: { + + "lightPos": { type: "v3", value: new THREE.Vector3( 0, 0, 0 ) } + + }, + + vertexShader: [ + + "varying vec4 vWorldPosition;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "worldpos_vertex" ], + + "vWorldPosition = worldPosition;", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 lightPos;", + "varying vec4 vWorldPosition;", + + THREE.ShaderChunk[ "common" ], + + "vec4 pack1K ( float depth ) {", + + " depth /= 1000.0;", + " const vec4 bitSh = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );", + " const vec4 bitMsk = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );", + " vec4 res = fract( depth * bitSh );", + " res -= res.xxyz * bitMsk;", + " return res; ", + + "}", + + "float unpack1K ( vec4 color ) {", + + " const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );", + " return dot( color, bitSh ) * 1000.0;", + + "}", + + "void main () {", + + " gl_FragColor = pack1K( length( vWorldPosition.xyz - lightPos.xyz ) );", + + "}" + + ].join( "\n" ) + + } + +}; + +// File:src/renderers/WebGLRenderer.js + +/** + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ + +THREE.WebGLRenderer = function ( parameters ) { + + console.log( 'THREE.WebGLRenderer', THREE.REVISION ); + + parameters = parameters || {}; + + var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ), + _context = parameters.context !== undefined ? parameters.context : null, + + _width = _canvas.width, + _height = _canvas.height, + + pixelRatio = 1, + + _alpha = parameters.alpha !== undefined ? parameters.alpha : false, + _depth = parameters.depth !== undefined ? parameters.depth : true, + _stencil = parameters.stencil !== undefined ? parameters.stencil : true, + _antialias = parameters.antialias !== undefined ? parameters.antialias : false, + _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, + _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, + + _clearColor = new THREE.Color( 0x000000 ), + _clearAlpha = 0; + + var lights = []; + + var opaqueObjects = []; + var opaqueObjectsLastIndex = - 1; + var transparentObjects = []; + var transparentObjectsLastIndex = - 1; + + var morphInfluences = new Float32Array( 8 ); + + + var sprites = []; + var lensFlares = []; + + // public properties + + this.domElement = _canvas; + this.context = null; + + // clearing + + this.autoClear = true; + this.autoClearColor = true; + this.autoClearDepth = true; + this.autoClearStencil = true; + + // scene graph + + this.sortObjects = true; + + // physically based shading + + this.gammaFactor = 2.0; // for backwards compatibility + this.gammaInput = false; + this.gammaOutput = false; + + // morphs + + this.maxMorphTargets = 8; + this.maxMorphNormals = 4; + + // flags + + this.autoScaleCubemaps = true; + + // internal properties + + var _this = this, + + // internal state cache + + _currentProgram = null, + _currentFramebuffer = null, + _currentMaterialId = - 1, + _currentGeometryProgram = '', + _currentCamera = null, + + _usedTextureUnits = 0, + + _viewportX = 0, + _viewportY = 0, + _viewportWidth = _canvas.width, + _viewportHeight = _canvas.height, + _currentWidth = 0, + _currentHeight = 0, + + // frustum + + _frustum = new THREE.Frustum(), + + // camera matrices cache + + _projScreenMatrix = new THREE.Matrix4(), + + _vector3 = new THREE.Vector3(), + + // light arrays cache + + _direction = new THREE.Vector3(), + + _lightsNeedUpdate = true, + + _lights = { + + ambient: [ 0, 0, 0 ], + directional: { length: 0, colors: [], positions: [] }, + point: { length: 0, colors: [], positions: [], distances: [], decays: [] }, + spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [], decays: [] }, + hemi: { length: 0, skyColors: [], groundColors: [], positions: [] } + + }, + + // info + + _infoMemory = { + + geometries: 0, + textures: 0 + + }, + + _infoRender = { + + calls: 0, + vertices: 0, + faces: 0, + points: 0 + + }; + + this.info = { + + render: _infoRender, + memory: _infoMemory, + programs: null + + }; + + + // initialize + + var _gl; + + try { + + var attributes = { + alpha: _alpha, + depth: _depth, + stencil: _stencil, + antialias: _antialias, + premultipliedAlpha: _premultipliedAlpha, + preserveDrawingBuffer: _preserveDrawingBuffer + }; + + _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); + + if ( _gl === null ) { + + if ( _canvas.getContext( 'webgl' ) !== null ) { + + throw 'Error creating WebGL context with your selected attributes.'; + + } else { + + throw 'Error creating WebGL context.'; + + } + + } + + _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); + + } catch ( error ) { + + console.error( 'THREE.WebGLRenderer: ' + error ); + + } + + var extensions = new THREE.WebGLExtensions( _gl ); + + extensions.get( 'OES_texture_float' ); + extensions.get( 'OES_texture_float_linear' ); + extensions.get( 'OES_texture_half_float' ); + extensions.get( 'OES_texture_half_float_linear' ); + extensions.get( 'OES_standard_derivatives' ); + extensions.get( 'ANGLE_instanced_arrays' ); + + if ( extensions.get( 'OES_element_index_uint' ) ) { + + THREE.BufferGeometry.MaxIndex = 4294967296; + + } + + var capabilities = new THREE.WebGLCapabilities( _gl, extensions, parameters ); + + var state = new THREE.WebGLState( _gl, extensions, paramThreeToGL ); + var properties = new THREE.WebGLProperties(); + var objects = new THREE.WebGLObjects( _gl, properties, this.info ); + var programCache = new THREE.WebGLPrograms( this, capabilities ); + + this.info.programs = programCache.programs; + + var bufferRenderer = new THREE.WebGLBufferRenderer( _gl, extensions, _infoRender ); + var indexedBufferRenderer = new THREE.WebGLIndexedBufferRenderer( _gl, extensions, _infoRender ); + + // + + function glClearColor( r, g, b, a ) { + + if ( _premultipliedAlpha === true ) { + + r *= a; g *= a; b *= a; + + } + + _gl.clearColor( r, g, b, a ); + + } + + function setDefaultGLState() { + + state.init(); + + _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + + glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + } + + function resetGLState() { + + _currentProgram = null; + _currentCamera = null; + + _currentGeometryProgram = ''; + _currentMaterialId = - 1; + + _lightsNeedUpdate = true; + + state.reset(); + + } + + setDefaultGLState(); + + this.context = _gl; + this.capabilities = capabilities; + this.extensions = extensions; + this.state = state; + + // shadow map + + var shadowMap = new THREE.WebGLShadowMap( this, lights, objects ); + + this.shadowMap = shadowMap; + + + // Plugins + + var spritePlugin = new THREE.SpritePlugin( this, sprites ); + var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares ); + + // API + + this.getContext = function () { + + return _gl; + + }; + + this.getContextAttributes = function () { + + return _gl.getContextAttributes(); + + }; + + this.forceContextLoss = function () { + + extensions.get( 'WEBGL_lose_context' ).loseContext(); + + }; + + this.getMaxAnisotropy = ( function () { + + var value; + + return function getMaxAnisotropy() { + + if ( value !== undefined ) return value; + + var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); + + if ( extension !== null ) { + + value = _gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); + + } else { + + value = 0; + + } + + return value; + + } + + } )(); + + this.getPrecision = function () { + + return capabilities.precision; + + }; + + this.getPixelRatio = function () { + + return pixelRatio; + + }; + + this.setPixelRatio = function ( value ) { + + if ( value !== undefined ) pixelRatio = value; + + }; + + this.getSize = function () { + + return { + width: _width, + height: _height + }; + + }; + + this.setSize = function ( width, height, updateStyle ) { + + _width = width; + _height = height; + + _canvas.width = width * pixelRatio; + _canvas.height = height * pixelRatio; + + if ( updateStyle !== false ) { + + _canvas.style.width = width + 'px'; + _canvas.style.height = height + 'px'; + + } + + this.setViewport( 0, 0, width, height ); + + }; + + this.setViewport = function ( x, y, width, height ) { + + _viewportX = x * pixelRatio; + _viewportY = y * pixelRatio; + + _viewportWidth = width * pixelRatio; + _viewportHeight = height * pixelRatio; + + _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + + }; + + this.getViewport = function ( dimensions ) { + + dimensions.x = _viewportX / pixelRatio; + dimensions.y = _viewportY / pixelRatio; + + dimensions.z = _viewportWidth / pixelRatio; + dimensions.w = _viewportHeight / pixelRatio; + + }; + + this.setScissor = function ( x, y, width, height ) { + + _gl.scissor( + x * pixelRatio, + y * pixelRatio, + width * pixelRatio, + height * pixelRatio + ); + + }; + + this.enableScissorTest = function ( boolean ) { + + state.setScissorTest( boolean ); + + }; + + // Clearing + + this.getClearColor = function () { + + return _clearColor; + + }; + + this.setClearColor = function ( color, alpha ) { + + _clearColor.set( color ); + + _clearAlpha = alpha !== undefined ? alpha : 1; + + glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + }; + + this.getClearAlpha = function () { + + return _clearAlpha; + + }; + + this.setClearAlpha = function ( alpha ) { + + _clearAlpha = alpha; + + glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + }; + + this.clear = function ( color, depth, stencil ) { + + var bits = 0; + + if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; + if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; + if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; + + _gl.clear( bits ); + + }; + + this.clearColor = function () { + + _gl.clear( _gl.COLOR_BUFFER_BIT ); + + }; + + this.clearDepth = function () { + + _gl.clear( _gl.DEPTH_BUFFER_BIT ); + + }; + + this.clearStencil = function () { + + _gl.clear( _gl.STENCIL_BUFFER_BIT ); + + }; + + this.clearTarget = function ( renderTarget, color, depth, stencil ) { + + this.setRenderTarget( renderTarget ); + this.clear( color, depth, stencil ); + + }; + + // Reset + + this.resetGLState = resetGLState; + + this.dispose = function() { + + _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); + + }; + + // Events + + function onContextLost( event ) { + + event.preventDefault(); + + resetGLState(); + setDefaultGLState(); + + properties.clear(); + + }; + + function onTextureDispose( event ) { + + var texture = event.target; + + texture.removeEventListener( 'dispose', onTextureDispose ); + + deallocateTexture( texture ); + + _infoMemory.textures --; + + + } + + function onRenderTargetDispose( event ) { + + var renderTarget = event.target; + + renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); + + deallocateRenderTarget( renderTarget ); + + _infoMemory.textures --; + + } + + function onMaterialDispose( event ) { + + var material = event.target; + + material.removeEventListener( 'dispose', onMaterialDispose ); + + deallocateMaterial( material ); + + } + + // Buffer deallocation + + function deallocateTexture( texture ) { + + var textureProperties = properties.get( texture ); + + if ( texture.image && textureProperties.__image__webglTextureCube ) { + + // cube texture + + _gl.deleteTexture( textureProperties.__image__webglTextureCube ); + + } else { + + // 2D texture + + if ( textureProperties.__webglInit === undefined ) return; + + _gl.deleteTexture( textureProperties.__webglTexture ); + + } + + // remove all webgl properties + properties.delete( texture ); + + } + + function deallocateRenderTarget( renderTarget ) { + + var renderTargetProperties = properties.get( renderTarget ); + var textureProperties = properties.get( renderTarget.texture ); + + if ( ! renderTarget || textureProperties.__webglTexture === undefined ) return; + + _gl.deleteTexture( textureProperties.__webglTexture ); + + if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { + + for ( var i = 0; i < 6; i ++ ) { + + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); + _gl.deleteRenderbuffer( renderTargetProperties.__webglRenderbuffer[ i ] ); + + } + + } else { + + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); + _gl.deleteRenderbuffer( renderTargetProperties.__webglRenderbuffer ); + + } + + properties.delete( renderTarget.texture ); + properties.delete( renderTarget ); + + } + + function deallocateMaterial( material ) { + + releaseMaterialProgramReference( material ); + + properties.delete( material ); + + } + + + function releaseMaterialProgramReference( material ) { + + var programInfo = properties.get( material ).program; + + material.program = undefined; + + if ( programInfo !== undefined ) { + + programCache.releaseProgram( programInfo ); + + } + + } + + // Buffer rendering + + this.renderBufferImmediate = function ( object, program, material ) { + + state.initAttributes(); + + var buffers = properties.get( object ); + + if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); + if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); + if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); + if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); + + var attributes = program.getAttributes(); + + if ( object.hasPositions ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); + + state.enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal ); + + if ( material.type !== 'MeshPhongMaterial' && material.shading === THREE.FlatShading ) { + + for ( var i = 0, l = object.count * 3; i < l; i += 9 ) { + + var array = object.normalArray; + + var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3; + var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3; + var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3; + + array[ i + 0 ] = nx; + array[ i + 1 ] = ny; + array[ i + 2 ] = nz; + + array[ i + 3 ] = nx; + array[ i + 4 ] = ny; + array[ i + 5 ] = nz; + + array[ i + 6 ] = nx; + array[ i + 7 ] = ny; + array[ i + 8 ] = nz; + + } + + } + + _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); + + state.enableAttribute( attributes.normal ); + + _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasUvs && material.map ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); + + state.enableAttribute( attributes.uv ); + + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasColors && material.vertexColors !== THREE.NoColors ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); + + state.enableAttribute( attributes.color ); + + _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); + + } + + state.disableUnusedAttributes(); + + _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); + + object.count = 0; + + }; + + this.renderBufferDirect = function ( camera, lights, fog, geometry, material, object, group ) { + + setMaterial( material ); + + var program = setProgram( camera, lights, fog, material, object ); + + var updateBuffers = false; + var geometryProgram = geometry.id + '_' + program.id + '_' + material.wireframe; + + if ( geometryProgram !== _currentGeometryProgram ) { + + _currentGeometryProgram = geometryProgram; + updateBuffers = true; + + } + + // morph targets + + var morphTargetInfluences = object.morphTargetInfluences; + + if ( morphTargetInfluences !== undefined ) { + + var activeInfluences = []; + + for ( var i = 0, l = morphTargetInfluences.length; i < l; i ++ ) { + + var influence = morphTargetInfluences[ i ]; + activeInfluences.push( [ influence, i ] ); + + } + + activeInfluences.sort( numericalSort ); + + if ( activeInfluences.length > 8 ) { + + activeInfluences.length = 8; + + } + + var morphAttributes = geometry.morphAttributes; + + for ( var i = 0, l = activeInfluences.length; i < l; i ++ ) { + + var influence = activeInfluences[ i ]; + morphInfluences[ i ] = influence[ 0 ]; + + if ( influence[ 0 ] !== 0 ) { + + var index = influence[ 1 ]; + + if ( material.morphTargets === true && morphAttributes.position ) geometry.addAttribute( 'morphTarget' + i, morphAttributes.position[ index ] ); + if ( material.morphNormals === true && morphAttributes.normal ) geometry.addAttribute( 'morphNormal' + i, morphAttributes.normal[ index ] ); + + } else { + + if ( material.morphTargets === true ) geometry.removeAttribute( 'morphTarget' + i ); + if ( material.morphNormals === true ) geometry.removeAttribute( 'morphNormal' + i ); + + } + + } + + var uniforms = program.getUniforms(); + + if ( uniforms.morphTargetInfluences !== null ) { + + _gl.uniform1fv( uniforms.morphTargetInfluences, morphInfluences ); + + } + + updateBuffers = true; + + } + + // + + var index = geometry.index; + var position = geometry.attributes.position; + + if ( material.wireframe === true ) { + + index = objects.getWireframeAttribute( geometry ); + + } + + var renderer; + + if ( index !== null ) { + + renderer = indexedBufferRenderer; + renderer.setIndex( index ); + + } else { + + renderer = bufferRenderer; + + } + + if ( updateBuffers ) { + + setupVertexAttributes( material, program, geometry ); + + if ( index !== null ) { + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, objects.getAttributeBuffer( index ) ); + + } + + } + + // + + var dataStart = 0; + var dataCount = Infinity; + + if ( index !== null ) { + + dataCount = index.count + + } else if ( position !== undefined ) { + + dataCount = position.count; + + } + + var rangeStart = geometry.drawRange.start; + var rangeCount = geometry.drawRange.count; + + var groupStart = group !== null ? group.start : 0; + var groupCount = group !== null ? group.count : Infinity; + + var drawStart = Math.max( dataStart, rangeStart, groupStart ); + var drawEnd = Math.min( dataStart + dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; + + var drawCount = Math.max( 0, drawEnd - drawStart + 1 ); + + // + + if ( object instanceof THREE.Mesh ) { + + if ( material.wireframe === true ) { + + state.setLineWidth( material.wireframeLinewidth * pixelRatio ); + renderer.setMode( _gl.LINES ); + + } else { + + renderer.setMode( _gl.TRIANGLES ); + + } + + if ( geometry instanceof THREE.InstancedBufferGeometry && geometry.maxInstancedCount > 0 ) { + + renderer.renderInstances( geometry ); + + } else { + + renderer.render( drawStart, drawCount ); + + } + + } else if ( object instanceof THREE.Line ) { + + var lineWidth = material.linewidth; + + if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material + + state.setLineWidth( lineWidth * pixelRatio ); + + if ( object instanceof THREE.LineSegments ) { + + renderer.setMode( _gl.LINES ); + + } else { + + renderer.setMode( _gl.LINE_STRIP ); + + } + + renderer.render( drawStart, drawCount ); + + } else if ( object instanceof THREE.Points ) { + + renderer.setMode( _gl.POINTS ); + renderer.render( drawStart, drawCount ); + + } + + }; + + function setupVertexAttributes( material, program, geometry, startIndex ) { + + var extension; + + if ( geometry instanceof THREE.InstancedBufferGeometry ) { + + extension = extensions.get( 'ANGLE_instanced_arrays' ); + + if ( extension === null ) { + + console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; + + } + + } + + if ( startIndex === undefined ) startIndex = 0; + + state.initAttributes(); + + var geometryAttributes = geometry.attributes; + + var programAttributes = program.getAttributes(); + + var materialDefaultAttributeValues = material.defaultAttributeValues; + + for ( var name in programAttributes ) { + + var programAttribute = programAttributes[ name ]; + + if ( programAttribute >= 0 ) { + + var geometryAttribute = geometryAttributes[ name ]; + + if ( geometryAttribute !== undefined ) { + + var size = geometryAttribute.itemSize; + var buffer = objects.getAttributeBuffer( geometryAttribute ); + + if ( geometryAttribute instanceof THREE.InterleavedBufferAttribute ) { + + var data = geometryAttribute.data; + var stride = data.stride; + var offset = geometryAttribute.offset; + + if ( data instanceof THREE.InstancedInterleavedBuffer ) { + + state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute, extension ); + + if ( geometry.maxInstancedCount === undefined ) { + + geometry.maxInstancedCount = data.meshPerAttribute * data.count; + + } + + } else { + + state.enableAttribute( programAttribute ); + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); + _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, stride * data.array.BYTES_PER_ELEMENT, ( startIndex * stride + offset ) * data.array.BYTES_PER_ELEMENT ); + + } else { + + if ( geometryAttribute instanceof THREE.InstancedBufferAttribute ) { + + state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute, extension ); + + if ( geometry.maxInstancedCount === undefined ) { + + geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; + + } + + } else { + + state.enableAttribute( programAttribute ); + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); + _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32 + + } + + } else if ( materialDefaultAttributeValues !== undefined ) { + + var value = materialDefaultAttributeValues[ name ]; + + if ( value !== undefined ) { + + switch ( value.length ) { + + case 2: + _gl.vertexAttrib2fv( programAttribute, value ); + break; + + case 3: + _gl.vertexAttrib3fv( programAttribute, value ); + break; + + case 4: + _gl.vertexAttrib4fv( programAttribute, value ); + break; + + default: + _gl.vertexAttrib1fv( programAttribute, value ); + + } + + } + + } + + } + + } + + state.disableUnusedAttributes(); + + } + + // Sorting + + function numericalSort ( a, b ) { + + return b[ 0 ] - a[ 0 ]; + + } + + function painterSortStable ( a, b ) { + + if ( a.object.renderOrder !== b.object.renderOrder ) { + + return a.object.renderOrder - b.object.renderOrder; + + } else if ( a.material.id !== b.material.id ) { + + return a.material.id - b.material.id; + + } else if ( a.z !== b.z ) { + + return a.z - b.z; + + } else { + + return a.id - b.id; + + } + + } + + function reversePainterSortStable ( a, b ) { + + if ( a.object.renderOrder !== b.object.renderOrder ) { + + return a.object.renderOrder - b.object.renderOrder; + + } if ( a.z !== b.z ) { + + return b.z - a.z; + + } else { + + return a.id - b.id; + + } + + } + + // Rendering + + this.render = function ( scene, camera, renderTarget, forceClear ) { + + if ( camera instanceof THREE.Camera === false ) { + + console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); + return; + + } + + var fog = scene.fog; + + // reset caching for this frame + + _currentGeometryProgram = ''; + _currentMaterialId = - 1; + _currentCamera = null; + _lightsNeedUpdate = true; + + // update scene graph + + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + + // update camera matrices and frustum + + if ( camera.parent === null ) camera.updateMatrixWorld(); + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + lights.length = 0; + + opaqueObjectsLastIndex = - 1; + transparentObjectsLastIndex = - 1; + + sprites.length = 0; + lensFlares.length = 0; + + projectObject( scene, camera ); + + opaqueObjects.length = opaqueObjectsLastIndex + 1; + transparentObjects.length = transparentObjectsLastIndex + 1; + + if ( _this.sortObjects === true ) { + + opaqueObjects.sort( painterSortStable ); + transparentObjects.sort( reversePainterSortStable ); + + } + + // + + shadowMap.render( scene ); + + // + + _infoRender.calls = 0; + _infoRender.vertices = 0; + _infoRender.faces = 0; + _infoRender.points = 0; + + this.setRenderTarget( renderTarget ); + + if ( this.autoClear || forceClear ) { + + this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil ); + + } + + // + + if ( scene.overrideMaterial ) { + + var overrideMaterial = scene.overrideMaterial; + + renderObjects( opaqueObjects, camera, lights, fog, overrideMaterial ); + renderObjects( transparentObjects, camera, lights, fog, overrideMaterial ); + + } else { + + // opaque pass (front-to-back order) + + state.setBlending( THREE.NoBlending ); + renderObjects( opaqueObjects, camera, lights, fog ); + + // transparent pass (back-to-front order) + + renderObjects( transparentObjects, camera, lights, fog ); + + } + + // custom render plugins (post pass) + + spritePlugin.render( scene, camera ); + lensFlarePlugin.render( scene, camera, _currentWidth, _currentHeight ); + + // Generate mipmap if we're using any kind of mipmap filtering + + if ( renderTarget ) { + + var texture = renderTarget.texture; + var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ); + if ( texture.generateMipmaps && isTargetPowerOfTwo && texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) { + + updateRenderTargetMipmap( renderTarget ); + + } + + } + + // Ensure depth buffer writing is enabled so it can be cleared on next render + + state.setDepthTest( true ); + state.setDepthWrite( true ); + state.setColorWrite( true ); + + // _gl.finish(); + + }; + + function pushRenderItem( object, geometry, material, z, group ) { + + var array, index; + + // allocate the next position in the appropriate array + + if ( material.transparent ) { + + array = transparentObjects; + index = ++ transparentObjectsLastIndex; + + } else { + + array = opaqueObjects; + index = ++ opaqueObjectsLastIndex; + + } + + // recycle existing render item or grow the array + + var renderItem = array[ index ]; + + if ( renderItem !== undefined ) { + + renderItem.id = object.id; + renderItem.object = object; + renderItem.geometry = geometry; + renderItem.material = material; + renderItem.z = _vector3.z; + renderItem.group = group; + + } else { + + renderItem = { + id: object.id, + object: object, + geometry: geometry, + material: material, + z: _vector3.z, + group: group + }; + + // assert( index === array.length ); + array.push( renderItem ); + + } + + } + + function projectObject( object, camera ) { + + if ( object.visible === false ) return; + + if ( ( object.channels.mask & camera.channels.mask ) !== 0 ) { + + if ( object instanceof THREE.Light ) { + + lights.push( object ); + + } else if ( object instanceof THREE.Sprite ) { + + sprites.push( object ); + + } else if ( object instanceof THREE.LensFlare ) { + + lensFlares.push( object ); + + } else if ( object instanceof THREE.ImmediateRenderObject ) { + + if ( _this.sortObjects === true ) { + + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyProjection( _projScreenMatrix ); + + } + + pushRenderItem( object, null, object.material, _vector3.z, null ); + + } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) { + + if ( object instanceof THREE.SkinnedMesh ) { + + object.skeleton.update(); + + } + + if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) { + + var material = object.material; + + if ( material.visible === true ) { + + if ( _this.sortObjects === true ) { + + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyProjection( _projScreenMatrix ); + + } + + var geometry = objects.update( object ); + + if ( material instanceof THREE.MeshFaceMaterial ) { + + var groups = geometry.groups; + var materials = material.materials; + + for ( var i = 0, l = groups.length; i < l; i ++ ) { + + var group = groups[ i ]; + var groupMaterial = materials[ group.materialIndex ]; + + if ( groupMaterial.visible === true ) { + + pushRenderItem( object, geometry, groupMaterial, _vector3.z, group ); + + } + + } + + } else { + + pushRenderItem( object, geometry, material, _vector3.z, null ); + + } + + } + + } + + } + + } + + var children = object.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + projectObject( children[ i ], camera ); + + } + + } + + function renderObjects( renderList, camera, lights, fog, overrideMaterial ) { + + for ( var i = 0, l = renderList.length; i < l; i ++ ) { + + var renderItem = renderList[ i ]; + + var object = renderItem.object; + var geometry = renderItem.geometry; + var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial; + var group = renderItem.group; + + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); + + if ( object instanceof THREE.ImmediateRenderObject ) { + + setMaterial( material ); + + var program = setProgram( camera, lights, fog, material, object ); + + _currentGeometryProgram = ''; + + object.render( function ( object ) { + + _this.renderBufferImmediate( object, program, material ); + + } ); + + } else { + + _this.renderBufferDirect( camera, lights, fog, geometry, material, object, group ); + + } + + } + + } + + function initMaterial( material, lights, fog, object ) { + + var materialProperties = properties.get( material ); + + var parameters = programCache.getParameters( material, lights, fog, object ); + var code = programCache.getProgramCode( material, parameters ); + + var program = materialProperties.program; + var programChange = true; + + if ( program === undefined ) { + + // new material + material.addEventListener( 'dispose', onMaterialDispose ); + + } else if ( program.code !== code ) { + + // changed glsl or parameters + releaseMaterialProgramReference( material ); + + } else if ( parameters.shaderID !== undefined ) { + + // same glsl and uniform list + return; + + } else { + + // only rebuild uniform list + programChange = false; + + } + + if ( programChange ) { + + if ( parameters.shaderID ) { + + var shader = THREE.ShaderLib[ parameters.shaderID ]; + + materialProperties.__webglShader = { + name: material.type, + uniforms: THREE.UniformsUtils.clone( shader.uniforms ), + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader + }; + + } else { + + materialProperties.__webglShader = { + name: material.type, + uniforms: material.uniforms, + vertexShader: material.vertexShader, + fragmentShader: material.fragmentShader + }; + + } + + material.__webglShader = materialProperties.__webglShader; + + program = programCache.acquireProgram( material, parameters, code ); + + materialProperties.program = program; + material.program = program; + + } + + var attributes = program.getAttributes(); + + if ( material.morphTargets ) { + + material.numSupportedMorphTargets = 0; + + for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { + + if ( attributes[ 'morphTarget' + i ] >= 0 ) { + + material.numSupportedMorphTargets ++; + + } + + } + + } + + if ( material.morphNormals ) { + + material.numSupportedMorphNormals = 0; + + for ( i = 0; i < _this.maxMorphNormals; i ++ ) { + + if ( attributes[ 'morphNormal' + i ] >= 0 ) { + + material.numSupportedMorphNormals ++; + + } + + } + + } + + materialProperties.uniformsList = []; + + var uniformLocations = materialProperties.program.getUniforms(); + + for ( var u in materialProperties.__webglShader.uniforms ) { + + var location = uniformLocations[ u ]; + + if ( location ) { + + materialProperties.uniformsList.push( [ materialProperties.__webglShader.uniforms[ u ], location ] ); + + } + + } + + } + + function setMaterial( material ) { + + setMaterialFaces( material ); + + if ( material.transparent === true ) { + + state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha ); + + } else { + + state.setBlending( THREE.NoBlending ); + + } + + state.setDepthFunc( material.depthFunc ); + state.setDepthTest( material.depthTest ); + state.setDepthWrite( material.depthWrite ); + state.setColorWrite( material.colorWrite ); + state.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + } + + function setMaterialFaces( material ) { + + material.side !== THREE.DoubleSide ? state.enable( _gl.CULL_FACE ) : state.disable( _gl.CULL_FACE ); + state.setFlipSided( material.side === THREE.BackSide ); + + } + + function setProgram( camera, lights, fog, material, object ) { + + _usedTextureUnits = 0; + + var materialProperties = properties.get( material ); + + if ( material.needsUpdate || ! materialProperties.program ) { + + initMaterial( material, lights, fog, object ); + material.needsUpdate = false; + + } + + var refreshProgram = false; + var refreshMaterial = false; + var refreshLights = false; + + var program = materialProperties.program, + p_uniforms = program.getUniforms(), + m_uniforms = materialProperties.__webglShader.uniforms; + + if ( program.id !== _currentProgram ) { + + _gl.useProgram( program.program ); + _currentProgram = program.id; + + refreshProgram = true; + refreshMaterial = true; + refreshLights = true; + + } + + if ( material.id !== _currentMaterialId ) { + + if ( _currentMaterialId === - 1 ) refreshLights = true; + _currentMaterialId = material.id; + + refreshMaterial = true; + + } + + if ( refreshProgram || camera !== _currentCamera ) { + + _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + + if ( capabilities.logarithmicDepthBuffer ) { + + _gl.uniform1f( p_uniforms.logDepthBufFC, 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); + + } + + + if ( camera !== _currentCamera ) _currentCamera = camera; + + // load material specific uniforms + // (shader material also gets them for the sake of genericity) + + if ( material instanceof THREE.ShaderMaterial || + material instanceof THREE.MeshPhongMaterial || + material.envMap ) { + + if ( p_uniforms.cameraPosition !== undefined ) { + + _vector3.setFromMatrixPosition( camera.matrixWorld ); + _gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z ); + + } + + } + + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.ShaderMaterial || + material.skinning ) { + + if ( p_uniforms.viewMatrix !== undefined ) { + + _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements ); + + } + + } + + } + + // skinning uniforms must be set even if material didn't change + // auto-setting of texture unit for bone texture must go before other textures + // not sure why, but otherwise weird things happen + + if ( material.skinning ) { + + if ( object.bindMatrix && p_uniforms.bindMatrix !== undefined ) { + + _gl.uniformMatrix4fv( p_uniforms.bindMatrix, false, object.bindMatrix.elements ); + + } + + if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== undefined ) { + + _gl.uniformMatrix4fv( p_uniforms.bindMatrixInverse, false, object.bindMatrixInverse.elements ); + + } + + if ( capabilities.floatVertexTextures && object.skeleton && object.skeleton.useVertexTexture ) { + + if ( p_uniforms.boneTexture !== undefined ) { + + var textureUnit = getTextureUnit(); + + _gl.uniform1i( p_uniforms.boneTexture, textureUnit ); + _this.setTexture( object.skeleton.boneTexture, textureUnit ); + + } + + if ( p_uniforms.boneTextureWidth !== undefined ) { + + _gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth ); + + } + + if ( p_uniforms.boneTextureHeight !== undefined ) { + + _gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight ); + + } + + } else if ( object.skeleton && object.skeleton.boneMatrices ) { + + if ( p_uniforms.boneGlobalMatrices !== undefined ) { + + _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices ); + + } + + } + + } + + if ( refreshMaterial ) { + + // refresh uniforms common to several materials + + if ( fog && material.fog ) { + + refreshUniformsFog( m_uniforms, fog ); + + } + + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material.lights ) { + + if ( _lightsNeedUpdate ) { + + refreshLights = true; + setupLights( lights, camera ); + _lightsNeedUpdate = false; + + } + + if ( refreshLights ) { + + refreshUniformsLights( m_uniforms, _lights ); + markUniformsLightsNeedsUpdate( m_uniforms, true ); + + } else { + + markUniformsLightsNeedsUpdate( m_uniforms, false ); + + } + + } + + if ( material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshPhongMaterial ) { + + refreshUniformsCommon( m_uniforms, material ); + + } + + // refresh single material specific uniforms + + if ( material instanceof THREE.LineBasicMaterial ) { + + refreshUniformsLine( m_uniforms, material ); + + } else if ( material instanceof THREE.LineDashedMaterial ) { + + refreshUniformsLine( m_uniforms, material ); + refreshUniformsDash( m_uniforms, material ); + + } else if ( material instanceof THREE.PointsMaterial ) { + + refreshUniformsParticle( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshPhongMaterial ) { + + refreshUniformsPhong( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshDepthMaterial ) { + + m_uniforms.mNear.value = camera.near; + m_uniforms.mFar.value = camera.far; + m_uniforms.opacity.value = material.opacity; + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + m_uniforms.opacity.value = material.opacity; + + } + + if ( object.receiveShadow && ! material._shadowPass ) { + + refreshUniformsShadow( m_uniforms, lights, camera ); + + } + + // load common uniforms + + loadUniformsGeneric( materialProperties.uniformsList ); + + } + + loadUniformsMatrices( p_uniforms, object ); + + if ( p_uniforms.modelMatrix !== undefined ) { + + _gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements ); + + } + + return program; + + } + + // Uniforms (refresh uniforms objects) + + function refreshUniformsCommon ( uniforms, material ) { + + uniforms.opacity.value = material.opacity; + + uniforms.diffuse.value = material.color; + + if ( material.emissive ) { + + uniforms.emissive.value = material.emissive; + + } + + uniforms.map.value = material.map; + uniforms.specularMap.value = material.specularMap; + uniforms.alphaMap.value = material.alphaMap; + + if ( material.aoMap ) { + + uniforms.aoMap.value = material.aoMap; + uniforms.aoMapIntensity.value = material.aoMapIntensity; + + } + + // uv repeat and offset setting priorities + // 1. color map + // 2. specular map + // 3. normal map + // 4. bump map + // 5. alpha map + // 6. emissive map + + var uvScaleMap; + + if ( material.map ) { + + uvScaleMap = material.map; + + } else if ( material.specularMap ) { + + uvScaleMap = material.specularMap; + + } else if ( material.displacementMap ) { + + uvScaleMap = material.displacementMap; + + } else if ( material.normalMap ) { + + uvScaleMap = material.normalMap; + + } else if ( material.bumpMap ) { + + uvScaleMap = material.bumpMap; + + } else if ( material.alphaMap ) { + + uvScaleMap = material.alphaMap; + + } else if ( material.emissiveMap ) { + + uvScaleMap = material.emissiveMap; + + } + + if ( uvScaleMap !== undefined ) { + + if ( uvScaleMap instanceof THREE.WebGLRenderTarget ) uvScaleMap = uvScaleMap.texture; + var offset = uvScaleMap.offset; + var repeat = uvScaleMap.repeat; + + uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); + + } + + uniforms.envMap.value = material.envMap; + uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : - 1; + + uniforms.reflectivity.value = material.reflectivity; + uniforms.refractionRatio.value = material.refractionRatio; + + } + + function refreshUniformsLine ( uniforms, material ) { + + uniforms.diffuse.value = material.color; + uniforms.opacity.value = material.opacity; + + } + + function refreshUniformsDash ( uniforms, material ) { + + uniforms.dashSize.value = material.dashSize; + uniforms.totalSize.value = material.dashSize + material.gapSize; + uniforms.scale.value = material.scale; + + } + + function refreshUniformsParticle ( uniforms, material ) { + + uniforms.psColor.value = material.color; + uniforms.opacity.value = material.opacity; + uniforms.size.value = material.size; + uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this. + + uniforms.map.value = material.map; + + if ( material.map !== null ) { + + var offset = material.map.offset; + var repeat = material.map.repeat; + + uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); + + } + + } + + function refreshUniformsFog ( uniforms, fog ) { + + uniforms.fogColor.value = fog.color; + + if ( fog instanceof THREE.Fog ) { + + uniforms.fogNear.value = fog.near; + uniforms.fogFar.value = fog.far; + + } else if ( fog instanceof THREE.FogExp2 ) { + + uniforms.fogDensity.value = fog.density; + + } + + } + + function refreshUniformsPhong ( uniforms, material ) { + + uniforms.specular.value = material.specular; + uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) + + if ( material.lightMap ) { + + uniforms.lightMap.value = material.lightMap; + uniforms.lightMapIntensity.value = material.lightMapIntensity; + + } + + if ( material.emissiveMap ) { + + uniforms.emissiveMap.value = material.emissiveMap; + + } + + if ( material.bumpMap ) { + + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + + } + + if ( material.normalMap ) { + + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); + + } + + if ( material.displacementMap ) { + + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; + + } + + } + + function refreshUniformsLights ( uniforms, lights ) { + + uniforms.ambientLightColor.value = lights.ambient; + + uniforms.directionalLightColor.value = lights.directional.colors; + uniforms.directionalLightDirection.value = lights.directional.positions; + + uniforms.pointLightColor.value = lights.point.colors; + uniforms.pointLightPosition.value = lights.point.positions; + uniforms.pointLightDistance.value = lights.point.distances; + uniforms.pointLightDecay.value = lights.point.decays; + + uniforms.spotLightColor.value = lights.spot.colors; + uniforms.spotLightPosition.value = lights.spot.positions; + uniforms.spotLightDistance.value = lights.spot.distances; + uniforms.spotLightDirection.value = lights.spot.directions; + uniforms.spotLightAngleCos.value = lights.spot.anglesCos; + uniforms.spotLightExponent.value = lights.spot.exponents; + uniforms.spotLightDecay.value = lights.spot.decays; + + uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors; + uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors; + uniforms.hemisphereLightDirection.value = lights.hemi.positions; + + } + + // If uniforms are marked as clean, they don't need to be loaded to the GPU. + + function markUniformsLightsNeedsUpdate ( uniforms, value ) { + + uniforms.ambientLightColor.needsUpdate = value; + + uniforms.directionalLightColor.needsUpdate = value; + uniforms.directionalLightDirection.needsUpdate = value; + + uniforms.pointLightColor.needsUpdate = value; + uniforms.pointLightPosition.needsUpdate = value; + uniforms.pointLightDistance.needsUpdate = value; + uniforms.pointLightDecay.needsUpdate = value; + + uniforms.spotLightColor.needsUpdate = value; + uniforms.spotLightPosition.needsUpdate = value; + uniforms.spotLightDistance.needsUpdate = value; + uniforms.spotLightDirection.needsUpdate = value; + uniforms.spotLightAngleCos.needsUpdate = value; + uniforms.spotLightExponent.needsUpdate = value; + uniforms.spotLightDecay.needsUpdate = value; + + uniforms.hemisphereLightSkyColor.needsUpdate = value; + uniforms.hemisphereLightGroundColor.needsUpdate = value; + uniforms.hemisphereLightDirection.needsUpdate = value; + + } + + function refreshUniformsShadow ( uniforms, lights, camera ) { + + if ( uniforms.shadowMatrix ) { + + var j = 0; + + for ( var i = 0, il = lights.length; i < il; i ++ ) { + + var light = lights[ i ]; + + if ( light.castShadow === true ) { + + if ( light instanceof THREE.PointLight || light instanceof THREE.SpotLight || light instanceof THREE.DirectionalLight ) { + + var shadow = light.shadow; + + if ( light instanceof THREE.PointLight ) { + + // for point lights we set the shadow matrix to be a translation-only matrix + // equal to inverse of the light's position + _vector3.setFromMatrixPosition( light.matrixWorld ).negate(); + shadow.matrix.identity().setPosition( _vector3 ); + + // for point lights we set the sign of the shadowDarkness uniform to be negative + uniforms.shadowDarkness.value[ j ] = - shadow.darkness; + + } else { + + uniforms.shadowDarkness.value[ j ] = shadow.darkness; + + } + + uniforms.shadowMatrix.value[ j ] = shadow.matrix; + uniforms.shadowMap.value[ j ] = shadow.map; + uniforms.shadowMapSize.value[ j ] = shadow.mapSize; + uniforms.shadowBias.value[ j ] = shadow.bias; + + j ++; + + } + + } + + } + + } + + } + + // Uniforms (load to GPU) + + function loadUniformsMatrices ( uniforms, object ) { + + _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object.modelViewMatrix.elements ); + + if ( uniforms.normalMatrix ) { + + _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object.normalMatrix.elements ); + + } + + } + + function getTextureUnit() { + + var textureUnit = _usedTextureUnits; + + if ( textureUnit >= capabilities.maxTextures ) { + + console.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures ); + + } + + _usedTextureUnits += 1; + + return textureUnit; + + } + + function loadUniformsGeneric ( uniforms ) { + + var texture, textureUnit; + + for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) { + + var uniform = uniforms[ j ][ 0 ]; + + // needsUpdate property is not added to all uniforms. + if ( uniform.needsUpdate === false ) continue; + + var type = uniform.type; + var value = uniform.value; + var location = uniforms[ j ][ 1 ]; + + switch ( type ) { + + case '1i': + _gl.uniform1i( location, value ); + break; + + case '1f': + _gl.uniform1f( location, value ); + break; + + case '2f': + _gl.uniform2f( location, value[ 0 ], value[ 1 ] ); + break; + + case '3f': + _gl.uniform3f( location, value[ 0 ], value[ 1 ], value[ 2 ] ); + break; + + case '4f': + _gl.uniform4f( location, value[ 0 ], value[ 1 ], value[ 2 ], value[ 3 ] ); + break; + + case '1iv': + _gl.uniform1iv( location, value ); + break; + + case '3iv': + _gl.uniform3iv( location, value ); + break; + + case '1fv': + _gl.uniform1fv( location, value ); + break; + + case '2fv': + _gl.uniform2fv( location, value ); + break; + + case '3fv': + _gl.uniform3fv( location, value ); + break; + + case '4fv': + _gl.uniform4fv( location, value ); + break; + + case 'Matrix3fv': + _gl.uniformMatrix3fv( location, false, value ); + break; + + case 'Matrix4fv': + _gl.uniformMatrix4fv( location, false, value ); + break; + + // + + case 'i': + + // single integer + _gl.uniform1i( location, value ); + + break; + + case 'f': + + // single float + _gl.uniform1f( location, value ); + + break; + + case 'v2': + + // single THREE.Vector2 + _gl.uniform2f( location, value.x, value.y ); + + break; + + case 'v3': + + // single THREE.Vector3 + _gl.uniform3f( location, value.x, value.y, value.z ); + + break; + + case 'v4': + + // single THREE.Vector4 + _gl.uniform4f( location, value.x, value.y, value.z, value.w ); + + break; + + case 'c': + + // single THREE.Color + _gl.uniform3f( location, value.r, value.g, value.b ); + + break; + + case 'iv1': + + // flat array of integers (JS or typed array) + _gl.uniform1iv( location, value ); + + break; + + case 'iv': + + // flat array of integers with 3 x N size (JS or typed array) + _gl.uniform3iv( location, value ); + + break; + + case 'fv1': + + // flat array of floats (JS or typed array) + _gl.uniform1fv( location, value ); + + break; + + case 'fv': + + // flat array of floats with 3 x N size (JS or typed array) + _gl.uniform3fv( location, value ); + + break; + + case 'v2v': + + // array of THREE.Vector2 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 2 * value.length ); + + } + + for ( var i = 0, i2 = 0, il = value.length; i < il; i ++, i2 += 2 ) { + + uniform._array[ i2 + 0 ] = value[ i ].x; + uniform._array[ i2 + 1 ] = value[ i ].y; + + } + + _gl.uniform2fv( location, uniform._array ); + + break; + + case 'v3v': + + // array of THREE.Vector3 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 3 * value.length ); + + } + + for ( var i = 0, i3 = 0, il = value.length; i < il; i ++, i3 += 3 ) { + + uniform._array[ i3 + 0 ] = value[ i ].x; + uniform._array[ i3 + 1 ] = value[ i ].y; + uniform._array[ i3 + 2 ] = value[ i ].z; + + } + + _gl.uniform3fv( location, uniform._array ); + + break; + + case 'v4v': + + // array of THREE.Vector4 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 4 * value.length ); + + } + + for ( var i = 0, i4 = 0, il = value.length; i < il; i ++, i4 += 4 ) { + + uniform._array[ i4 + 0 ] = value[ i ].x; + uniform._array[ i4 + 1 ] = value[ i ].y; + uniform._array[ i4 + 2 ] = value[ i ].z; + uniform._array[ i4 + 3 ] = value[ i ].w; + + } + + _gl.uniform4fv( location, uniform._array ); + + break; + + case 'm3': + + // single THREE.Matrix3 + _gl.uniformMatrix3fv( location, false, value.elements ); + + break; + + case 'm3v': + + // array of THREE.Matrix3 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 9 * value.length ); + + } + + for ( var i = 0, il = value.length; i < il; i ++ ) { + + value[ i ].flattenToArrayOffset( uniform._array, i * 9 ); + + } + + _gl.uniformMatrix3fv( location, false, uniform._array ); + + break; + + case 'm4': + + // single THREE.Matrix4 + _gl.uniformMatrix4fv( location, false, value.elements ); + + break; + + case 'm4v': + + // array of THREE.Matrix4 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 16 * value.length ); + + } + + for ( var i = 0, il = value.length; i < il; i ++ ) { + + value[ i ].flattenToArrayOffset( uniform._array, i * 16 ); + + } + + _gl.uniformMatrix4fv( location, false, uniform._array ); + + break; + + case 't': + + // single THREE.Texture (2d or cube) + + texture = value; + textureUnit = getTextureUnit(); + + _gl.uniform1i( location, textureUnit ); + + if ( ! texture ) continue; + + if ( texture instanceof THREE.CubeTexture || + ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { + + // CompressedTexture can have Array in image :/ + + setCubeTexture( texture, textureUnit ); + + } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { + + setCubeTextureDynamic( texture.texture, textureUnit ); + + } else if ( texture instanceof THREE.WebGLRenderTarget ) { + + _this.setTexture( texture.texture, textureUnit ); + + } else { + + _this.setTexture( texture, textureUnit ); + + } + + break; + + case 'tv': + + // array of THREE.Texture (2d or cube) + + if ( uniform._array === undefined ) { + + uniform._array = []; + + } + + for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { + + uniform._array[ i ] = getTextureUnit(); + + } + + _gl.uniform1iv( location, uniform._array ); + + for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { + + texture = uniform.value[ i ]; + textureUnit = uniform._array[ i ]; + + if ( ! texture ) continue; + + if ( texture instanceof THREE.CubeTexture || + ( texture.image instanceof Array && texture.image.length === 6 ) ) { + + // CompressedTexture can have Array in image :/ + + setCubeTexture( texture, textureUnit ); + + } else if ( texture instanceof THREE.WebGLRenderTarget ) { + + _this.setTexture( texture.texture, textureUnit ); + + } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { + + setCubeTextureDynamic( texture.texture, textureUnit ); + + } else { + + _this.setTexture( texture, textureUnit ); + + } + + } + + break; + + default: + + console.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type ); + + } + + } + + } + + function setColorLinear( array, offset, color, intensity ) { + + array[ offset + 0 ] = color.r * intensity; + array[ offset + 1 ] = color.g * intensity; + array[ offset + 2 ] = color.b * intensity; + + } + + function setupLights ( lights, camera ) { + + var l, ll, light, + r = 0, g = 0, b = 0, + color, skyColor, groundColor, + intensity, + distance, + + zlights = _lights, + + viewMatrix = camera.matrixWorldInverse, + + dirColors = zlights.directional.colors, + dirPositions = zlights.directional.positions, + + pointColors = zlights.point.colors, + pointPositions = zlights.point.positions, + pointDistances = zlights.point.distances, + pointDecays = zlights.point.decays, + + spotColors = zlights.spot.colors, + spotPositions = zlights.spot.positions, + spotDistances = zlights.spot.distances, + spotDirections = zlights.spot.directions, + spotAnglesCos = zlights.spot.anglesCos, + spotExponents = zlights.spot.exponents, + spotDecays = zlights.spot.decays, + + hemiSkyColors = zlights.hemi.skyColors, + hemiGroundColors = zlights.hemi.groundColors, + hemiPositions = zlights.hemi.positions, + + dirLength = 0, + pointLength = 0, + spotLength = 0, + hemiLength = 0, + + dirCount = 0, + pointCount = 0, + spotCount = 0, + hemiCount = 0, + + dirOffset = 0, + pointOffset = 0, + spotOffset = 0, + hemiOffset = 0; + + for ( l = 0, ll = lights.length; l < ll; l ++ ) { + + light = lights[ l ]; + + color = light.color; + intensity = light.intensity; + distance = light.distance; + + if ( light instanceof THREE.AmbientLight ) { + + if ( ! light.visible ) continue; + + r += color.r; + g += color.g; + b += color.b; + + } else if ( light instanceof THREE.DirectionalLight ) { + + dirCount += 1; + + if ( ! light.visible ) continue; + + _direction.setFromMatrixPosition( light.matrixWorld ); + _vector3.setFromMatrixPosition( light.target.matrixWorld ); + _direction.sub( _vector3 ); + _direction.transformDirection( viewMatrix ); + + dirOffset = dirLength * 3; + + dirPositions[ dirOffset + 0 ] = _direction.x; + dirPositions[ dirOffset + 1 ] = _direction.y; + dirPositions[ dirOffset + 2 ] = _direction.z; + + setColorLinear( dirColors, dirOffset, color, intensity ); + + dirLength += 1; + + } else if ( light instanceof THREE.PointLight ) { + + pointCount += 1; + + if ( ! light.visible ) continue; + + pointOffset = pointLength * 3; + + setColorLinear( pointColors, pointOffset, color, intensity ); + + _vector3.setFromMatrixPosition( light.matrixWorld ); + _vector3.applyMatrix4( viewMatrix ); + + pointPositions[ pointOffset + 0 ] = _vector3.x; + pointPositions[ pointOffset + 1 ] = _vector3.y; + pointPositions[ pointOffset + 2 ] = _vector3.z; + + // distance is 0 if decay is 0, because there is no attenuation at all. + pointDistances[ pointLength ] = distance; + pointDecays[ pointLength ] = ( light.distance === 0 ) ? 0.0 : light.decay; + + pointLength += 1; + + } else if ( light instanceof THREE.SpotLight ) { + + spotCount += 1; + + if ( ! light.visible ) continue; + + spotOffset = spotLength * 3; + + setColorLinear( spotColors, spotOffset, color, intensity ); + + _direction.setFromMatrixPosition( light.matrixWorld ); + _vector3.copy( _direction ).applyMatrix4( viewMatrix ); + + spotPositions[ spotOffset + 0 ] = _vector3.x; + spotPositions[ spotOffset + 1 ] = _vector3.y; + spotPositions[ spotOffset + 2 ] = _vector3.z; + + spotDistances[ spotLength ] = distance; + + _vector3.setFromMatrixPosition( light.target.matrixWorld ); + _direction.sub( _vector3 ); + _direction.transformDirection( viewMatrix ); + + spotDirections[ spotOffset + 0 ] = _direction.x; + spotDirections[ spotOffset + 1 ] = _direction.y; + spotDirections[ spotOffset + 2 ] = _direction.z; + + spotAnglesCos[ spotLength ] = Math.cos( light.angle ); + spotExponents[ spotLength ] = light.exponent; + spotDecays[ spotLength ] = ( light.distance === 0 ) ? 0.0 : light.decay; + + spotLength += 1; + + } else if ( light instanceof THREE.HemisphereLight ) { + + hemiCount += 1; + + if ( ! light.visible ) continue; + + _direction.setFromMatrixPosition( light.matrixWorld ); + _direction.transformDirection( viewMatrix ); + + hemiOffset = hemiLength * 3; + + hemiPositions[ hemiOffset + 0 ] = _direction.x; + hemiPositions[ hemiOffset + 1 ] = _direction.y; + hemiPositions[ hemiOffset + 2 ] = _direction.z; + + skyColor = light.color; + groundColor = light.groundColor; + + setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity ); + setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity ); + + hemiLength += 1; + + } + + } + + // null eventual remains from removed lights + // (this is to avoid if in shader) + + for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0; + for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0; + for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0; + for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0; + for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0; + + zlights.directional.length = dirLength; + zlights.point.length = pointLength; + zlights.spot.length = spotLength; + zlights.hemi.length = hemiLength; + + zlights.ambient[ 0 ] = r; + zlights.ambient[ 1 ] = g; + zlights.ambient[ 2 ] = b; + + } + + // GL state setting + + this.setFaceCulling = function ( cullFace, frontFaceDirection ) { + + if ( cullFace === THREE.CullFaceNone ) { + + state.disable( _gl.CULL_FACE ); + + } else { + + if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) { + + _gl.frontFace( _gl.CW ); + + } else { + + _gl.frontFace( _gl.CCW ); + + } + + if ( cullFace === THREE.CullFaceBack ) { + + _gl.cullFace( _gl.BACK ); + + } else if ( cullFace === THREE.CullFaceFront ) { + + _gl.cullFace( _gl.FRONT ); + + } else { + + _gl.cullFace( _gl.FRONT_AND_BACK ); + + } + + state.enable( _gl.CULL_FACE ); + + } + + }; + + // Textures + + function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) { + + var extension; + + if ( isImagePowerOfTwo ) { + + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) ); + + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) ); + + } else { + + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + + if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) { + + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.', texture ); + + } + + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); + + if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) { + + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.', texture ); + + } + + } + + extension = extensions.get( 'EXT_texture_filter_anisotropic' ); + + if ( extension ) { + + if ( texture.type === THREE.FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return; + if ( texture.type === THREE.HalfFloatType && extensions.get( 'OES_texture_half_float_linear' ) === null ) return; + + if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { + + _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _this.getMaxAnisotropy() ) ); + properties.get( texture ).__currentAnisotropy = texture.anisotropy; + + } + + } + + } + + function uploadTexture( textureProperties, texture, slot ) { + + if ( textureProperties.__webglInit === undefined ) { + + textureProperties.__webglInit = true; + + texture.addEventListener( 'dispose', onTextureDispose ); + + textureProperties.__webglTexture = _gl.createTexture(); + + _infoMemory.textures ++; + + } + + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); + + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); + _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); + + texture.image = clampToMaxSize( texture.image, capabilities.maxTextureSize ); + + if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( texture.image ) === false ) { + + texture.image = makePowerOfTwo( texture.image ); + + } + + var image = texture.image, + isImagePowerOfTwo = isPowerOfTwo( image ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); + + setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo ); + + var mipmap, mipmaps = texture.mipmaps; + + if ( texture instanceof THREE.DataTexture ) { + + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels + + if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + + } + + texture.generateMipmaps = false; + + } else { + + state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); + + } + + } else if ( texture instanceof THREE.CompressedTexture ) { + + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + + if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { + + if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { + + state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + + } else { + + console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); + + } + + } else { + + state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + + } + + } + + } else { + + // regular Texture (image, video, canvas) + + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels + + if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); + + } + + texture.generateMipmaps = false; + + } else { + + state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image ); + + } + + } + + if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + + textureProperties.__version = texture.version; + + if ( texture.onUpdate ) texture.onUpdate( texture ); + + } + + this.setTexture = function ( texture, slot ) { + + var textureProperties = properties.get( texture ); + + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + + var image = texture.image; + + if ( image === undefined ) { + + console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined', texture ); + return; + + } + + if ( image.complete === false ) { + + console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete', texture ); + return; + + } + + uploadTexture( textureProperties, texture, slot ); + + return; + + } + + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); + + }; + + function clampToMaxSize ( image, maxSize ) { + + if ( image.width > maxSize || image.height > maxSize ) { + + // Warning: Scaling through the canvas will only work with images that use + // premultiplied alpha. + + var scale = maxSize / Math.max( image.width, image.height ); + + var canvas = document.createElement( 'canvas' ); + canvas.width = Math.floor( image.width * scale ); + canvas.height = Math.floor( image.height * scale ); + + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); + + console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image ); + + return canvas; + + } + + return image; + + } + + function isPowerOfTwo( image ) { + + return THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ); + + } + + function textureNeedsPowerOfTwo( texture ) { + + if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) return true; + if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) return true; + + return false; + + } + + function makePowerOfTwo( image ) { + + if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement ) { + + var canvas = document.createElement( 'canvas' ); + canvas.width = THREE.Math.nearestPowerOfTwo( image.width ); + canvas.height = THREE.Math.nearestPowerOfTwo( image.height ); + + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, canvas.width, canvas.height ); + + console.warn( 'THREE.WebGLRenderer: image is not power of two (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image ); + + return canvas; + + } + + return image; + + } + + function setCubeTexture ( texture, slot ) { + + var textureProperties = properties.get( texture ); + + if ( texture.image.length === 6 ) { + + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + + if ( ! textureProperties.__image__webglTextureCube ) { + + texture.addEventListener( 'dispose', onTextureDispose ); + + textureProperties.__image__webglTextureCube = _gl.createTexture(); + + _infoMemory.textures ++; + + } + + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); + + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + + var isCompressed = texture instanceof THREE.CompressedTexture; + var isDataTexture = texture.image[ 0 ] instanceof THREE.DataTexture; + + var cubeImage = []; + + for ( var i = 0; i < 6; i ++ ) { + + if ( _this.autoScaleCubemaps && ! isCompressed && ! isDataTexture ) { + + cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize ); + + } else { + + cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; + + } + + } + + var image = cubeImage[ 0 ], + isImagePowerOfTwo = isPowerOfTwo( image ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); + + setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo ); + + for ( var i = 0; i < 6; i ++ ) { + + if ( ! isCompressed ) { + + if ( isDataTexture ) { + + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); + + } else { + + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); + + } + + } else { + + var mipmap, mipmaps = cubeImage[ i ].mipmaps; + + for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { + + mipmap = mipmaps[ j ]; + + if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { + + if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { + + state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + + } else { + + console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setCubeTexture()" ); + + } + + } else { + + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + + } + + } + + } + + } + + if ( texture.generateMipmaps && isImagePowerOfTwo ) { + + _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + + } + + textureProperties.__version = texture.version; + + if ( texture.onUpdate ) texture.onUpdate( texture ); + + } else { + + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); + + } + + } + + } + + function setCubeTextureDynamic ( texture, slot ) { + + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture ); + + } + + // Render targets + + function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); + + } + + function setupRenderBuffer ( renderbuffer, renderTarget ) { + + _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); + + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + + /* For some reason this is not working. Defaulting to RGBA4. + } else if ( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + */ + + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + + } else { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); + + } + + } + + this.setRenderTarget = function ( renderTarget ) { + + var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); + + if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { + + var renderTargetProperties = properties.get( renderTarget ); + var textureProperties = properties.get( renderTarget.texture ); + + if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true; + if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true; + + renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); + + textureProperties.__webglTexture = _gl.createTexture(); + + _infoMemory.textures ++; + + // Setup texture, create render and frame buffers + + var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ), + glFormat = paramThreeToGL( renderTarget.texture.format ), + glType = paramThreeToGL( renderTarget.texture.type ); + + if ( isCube ) { + + renderTargetProperties.__webglFramebuffer = []; + renderTargetProperties.__webglRenderbuffer = []; + + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture ); + + setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo ); + + for ( var i = 0; i < 6; i ++ ) { + + renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); + renderTargetProperties.__webglRenderbuffer[ i ] = _gl.createRenderbuffer(); + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + + setupFrameBuffer( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); + setupRenderBuffer( renderTargetProperties.__webglRenderbuffer[ i ], renderTarget ); + + } + + if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + + } else { + + renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); + + if ( renderTarget.shareDepthFrom ) { + + renderTargetProperties.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer; + + } else { + + renderTargetProperties.__webglRenderbuffer = _gl.createRenderbuffer(); + + } + + state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); + setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo ); + + state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + + setupFrameBuffer( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D ); + + if ( renderTarget.shareDepthFrom ) { + + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTargetProperties.__webglRenderbuffer ); + + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTargetProperties.__webglRenderbuffer ); + + } + + } else { + + setupRenderBuffer( renderTargetProperties.__webglRenderbuffer, renderTarget ); + + } + + if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + + } + + // Release everything + + if ( isCube ) { + + state.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + + } else { + + state.bindTexture( _gl.TEXTURE_2D, null ); + + } + + _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); + + } + + var framebuffer, width, height, vx, vy; + + if ( renderTarget ) { + + var renderTargetProperties = properties.get( renderTarget ); + + if ( isCube ) { + + framebuffer = renderTargetProperties.__webglFramebuffer[ renderTarget.activeCubeFace ]; + + } else { + + framebuffer = renderTargetProperties.__webglFramebuffer; + + } + + width = renderTarget.width; + height = renderTarget.height; + + vx = 0; + vy = 0; + + } else { + + framebuffer = null; + + width = _viewportWidth; + height = _viewportHeight; + + vx = _viewportX; + vy = _viewportY; + + } + + if ( framebuffer !== _currentFramebuffer ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.viewport( vx, vy, width, height ); + + _currentFramebuffer = framebuffer; + + } + + if ( isCube ) { + + var textureProperties = properties.get( renderTarget.texture ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, 0 ); + + } + + _currentWidth = width; + _currentHeight = height; + + }; + + this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) { + + if ( renderTarget instanceof THREE.WebGLRenderTarget === false ) { + + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); + return; + + } + + var framebuffer = properties.get( renderTarget ).__webglFramebuffer; + + if ( framebuffer ) { + + var restore = false; + + if ( framebuffer !== _currentFramebuffer ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + + restore = true; + + } + + try { + + var texture = renderTarget.texture; + + if ( texture.format !== THREE.RGBAFormat + && paramThreeToGL( texture.format ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { + + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); + return; + + } + + if ( texture.type !== THREE.UnsignedByteType + && paramThreeToGL( texture.type ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) + && ! ( texture.type === THREE.FloatType && extensions.get( 'WEBGL_color_buffer_float' ) ) + && ! ( texture.type === THREE.HalfFloatType && extensions.get( 'EXT_color_buffer_half_float' ) ) ) { + + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); + return; + + } + + if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) { + + _gl.readPixels( x, y, width, height, paramThreeToGL( texture.format ), paramThreeToGL( texture.type ), buffer ); + + } else { + + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); + + } + + } finally { + + if ( restore ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer ); + + } + + } + + } + + }; + + function updateRenderTargetMipmap( renderTarget ) { + + var target = renderTarget instanceof THREE.WebGLRenderTargetCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; + var texture = properties.get( renderTarget.texture ).__webglTexture; + + state.bindTexture( target, texture ); + _gl.generateMipmap( target ); + state.bindTexture( target, null ); + + } + + // Fallback filters for non-power-of-2 textures + + function filterFallback ( f ) { + + if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) { + + return _gl.NEAREST; + + } + + return _gl.LINEAR; + + } + + // Map three.js constants to WebGL constants + + function paramThreeToGL ( p ) { + + var extension; + + if ( p === THREE.RepeatWrapping ) return _gl.REPEAT; + if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE; + if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT; + + if ( p === THREE.NearestFilter ) return _gl.NEAREST; + if ( p === THREE.NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST; + if ( p === THREE.NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR; + + if ( p === THREE.LinearFilter ) return _gl.LINEAR; + if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST; + if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR; + + if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE; + if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4; + if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1; + if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5; + + if ( p === THREE.ByteType ) return _gl.BYTE; + if ( p === THREE.ShortType ) return _gl.SHORT; + if ( p === THREE.UnsignedShortType ) return _gl.UNSIGNED_SHORT; + if ( p === THREE.IntType ) return _gl.INT; + if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT; + if ( p === THREE.FloatType ) return _gl.FLOAT; + + extension = extensions.get( 'OES_texture_half_float' ); + + if ( extension !== null ) { + + if ( p === THREE.HalfFloatType ) return extension.HALF_FLOAT_OES; + + } + + if ( p === THREE.AlphaFormat ) return _gl.ALPHA; + if ( p === THREE.RGBFormat ) return _gl.RGB; + if ( p === THREE.RGBAFormat ) return _gl.RGBA; + if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE; + if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA; + + if ( p === THREE.AddEquation ) return _gl.FUNC_ADD; + if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT; + if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT; + + if ( p === THREE.ZeroFactor ) return _gl.ZERO; + if ( p === THREE.OneFactor ) return _gl.ONE; + if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR; + if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR; + if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA; + if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA; + if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA; + if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA; + + if ( p === THREE.DstColorFactor ) return _gl.DST_COLOR; + if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR; + if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE; + + extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); + + if ( extension !== null ) { + + if ( p === THREE.RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; + if ( p === THREE.RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; + if ( p === THREE.RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; + if ( p === THREE.RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; + + } + + extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + + if ( extension !== null ) { + + if ( p === THREE.RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + if ( p === THREE.RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + if ( p === THREE.RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + if ( p === THREE.RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + + } + + extension = extensions.get( 'EXT_blend_minmax' ); + + if ( extension !== null ) { + + if ( p === THREE.MinEquation ) return extension.MIN_EXT; + if ( p === THREE.MaxEquation ) return extension.MAX_EXT; + + } + + return 0; + + } + + // DEPRECATED + + this.supportsFloatTextures = function () { + + console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); + return extensions.get( 'OES_texture_float' ); + + }; + + this.supportsHalfFloatTextures = function () { + + console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); + return extensions.get( 'OES_texture_half_float' ); + + }; + + this.supportsStandardDerivatives = function () { + + console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); + return extensions.get( 'OES_standard_derivatives' ); + + }; + + this.supportsCompressedTextureS3TC = function () { + + console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); + return extensions.get( 'WEBGL_compressed_texture_s3tc' ); + + }; + + this.supportsCompressedTexturePVRTC = function () { + + console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); + return extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + + }; + + this.supportsBlendMinMax = function () { + + console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); + return extensions.get( 'EXT_blend_minmax' ); + + }; + + this.supportsVertexTextures = function () { + + return capabilities.vertexTextures; + + }; + + this.supportsInstancedArrays = function () { + + console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); + return extensions.get( 'ANGLE_instanced_arrays' ); + + }; + + // + + this.initMaterial = function () { + + console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); + + }; + + this.addPrePlugin = function () { + + console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); + + }; + + this.addPostPlugin = function () { + + console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); + + }; + + this.updateShadowMap = function () { + + console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); + + }; + + Object.defineProperties( this, { + shadowMapEnabled: { + get: function () { + + return shadowMap.enabled; + + }, + set: function ( value ) { + + console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); + shadowMap.enabled = value; + + } + }, + shadowMapType: { + get: function () { + + return shadowMap.type; + + }, + set: function ( value ) { + + console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); + shadowMap.type = value; + + } + }, + shadowMapCullFace: { + get: function () { + + return shadowMap.cullFace; + + }, + set: function ( value ) { + + console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace.' ); + shadowMap.cullFace = value; + + } + }, + shadowMapDebug: { + get: function () { + + return shadowMap.debug; + + }, + set: function ( value ) { + + console.warn( 'THREE.WebGLRenderer: .shadowMapDebug is now .shadowMap.debug.' ); + shadowMap.debug = value; + + } + } + } ); + +}; + +// File:src/renderers/WebGLRenderTarget.js + +/** + * @author szimek / https://github.com/szimek/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.WebGLRenderTarget = function ( width, height, options ) { + + this.uuid = THREE.Math.generateUUID(); + + this.width = width; + this.height = height; + + options = options || {}; + + if ( options.minFilter === undefined ) options.minFilter = THREE.LinearFilter; + + this.texture = new THREE.Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy ); + + this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; + this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; + + this.shareDepthFrom = options.shareDepthFrom !== undefined ? options.shareDepthFrom : null; + +}; + +THREE.WebGLRenderTarget.prototype = { + + constructor: THREE.WebGLRenderTarget, + + get wrapS() { + + console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); + + return this.texture.wrapS; + + }, + + set wrapS( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); + + this.texture.wrapS = value; + + }, + + get wrapT() { + + console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); + + return this.texture.wrapT; + + }, + + set wrapT( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); + + this.texture.wrapT = value; + + }, + + get magFilter() { + + console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); + + return this.texture.magFilter; + + }, + + set magFilter( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); + + this.texture.magFilter = value; + + }, + + get minFilter() { + + console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); + + return this.texture.minFilter; + + }, + + set minFilter( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); + + this.texture.minFilter = value; + + }, + + get anisotropy() { + + console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); + + return this.texture.anisotropy; + + }, + + set anisotropy( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); + + this.texture.anisotropy = value; + + }, + + get offset() { + + console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); + + return this.texture.offset; + + }, + + set offset( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); + + this.texture.offset = value; + + }, + + get repeat() { + + console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); + + return this.texture.repeat; + + }, + + set repeat( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); + + this.texture.repeat = value; + + }, + + get format() { + + console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); + + return this.texture.format; + + }, + + set format( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); + + this.texture.format = value; + + }, + + get type() { + + console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); + + return this.texture.type; + + }, + + set type( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); + + this.texture.type = value; + + }, + + get generateMipmaps() { + + console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); + + return this.texture.generateMipmaps; + + }, + + set generateMipmaps( value ) { + + console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); + + this.texture.generateMipmaps = value; + + }, + + // + + setSize: function ( width, height ) { + + if ( this.width !== width || this.height !== height ) { + + this.width = width; + this.height = height; + + this.dispose(); + + } + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( source ) { + + this.width = source.width; + this.height = source.height; + + this.texture = source.texture.clone(); + + this.depthBuffer = source.depthBuffer; + this.stencilBuffer = source.stencilBuffer; + + this.shareDepthFrom = source.shareDepthFrom; + + return this; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.WebGLRenderTarget.prototype ); + +// File:src/renderers/WebGLRenderTargetCube.js + +/** + * @author alteredq / http://alteredqualia.com + */ + +THREE.WebGLRenderTargetCube = function ( width, height, options ) { + + THREE.WebGLRenderTarget.call( this, width, height, options ); + + this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 + +}; + +THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype ); +THREE.WebGLRenderTargetCube.prototype.constructor = THREE.WebGLRenderTargetCube; + +// File:src/renderers/webgl/WebGLBufferRenderer.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLBufferRenderer = function ( _gl, extensions, _infoRender ) { + + var mode; + + function setMode( value ) { + + mode = value; + + } + + function render( start, count ) { + + _gl.drawArrays( mode, start, count ); + + _infoRender.calls ++; + _infoRender.vertices += count; + if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3; + + } + + function renderInstances( geometry ) { + + var extension = extensions.get( 'ANGLE_instanced_arrays' ); + + if ( extension === null ) { + + console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; + + } + + var position = geometry.attributes.position; + + if ( position instanceof THREE.InterleavedBufferAttribute ) { + + extension.drawArraysInstancedANGLE( mode, 0, position.data.count, geometry.maxInstancedCount ); + + } else { + + extension.drawArraysInstancedANGLE( mode, 0, position.count, geometry.maxInstancedCount ); + + } + + } + + this.setMode = setMode; + this.render = render; + this.renderInstances = renderInstances; + +}; + +// File:src/renderers/webgl/WebGLIndexedBufferRenderer.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLIndexedBufferRenderer = function ( _gl, extensions, _infoRender ) { + + var mode; + + function setMode( value ) { + + mode = value; + + } + + var type, size; + + function setIndex( index ) { + + if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { + + type = _gl.UNSIGNED_INT; + size = 4; + + } else { + + type = _gl.UNSIGNED_SHORT; + size = 2; + + } + + } + + function render( start, count ) { + + _gl.drawElements( mode, count, type, start * size ); + + _infoRender.calls ++; + _infoRender.vertices += count; + if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3; + + } + + function renderInstances( geometry ) { + + var extension = extensions.get( 'ANGLE_instanced_arrays' ); + + if ( extension === null ) { + + console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; + + } + + var index = geometry.index; + + extension.drawElementsInstancedANGLE( mode, index.array.length, type, 0, geometry.maxInstancedCount ); + + } + + this.setMode = setMode; + this.setIndex = setIndex; + this.render = render; + this.renderInstances = renderInstances; + +}; + +// File:src/renderers/webgl/WebGLExtensions.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLExtensions = function ( gl ) { + + var extensions = {}; + + this.get = function ( name ) { + + if ( extensions[ name ] !== undefined ) { + + return extensions[ name ]; + + } + + var extension; + + switch ( name ) { + + case 'EXT_texture_filter_anisotropic': + extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); + break; + + case 'WEBGL_compressed_texture_s3tc': + extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); + break; + + case 'WEBGL_compressed_texture_pvrtc': + extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); + break; + + default: + extension = gl.getExtension( name ); + + } + + if ( extension === null ) { + + console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); + + } + + extensions[ name ] = extension; + + return extension; + + }; + +}; + +// File:src/renderers/webgl/WebGLCapabilities.js + +THREE.WebGLCapabilities = function ( gl, extensions, parameters ) { + + function getMaxPrecision( precision ) { + + if ( precision === 'highp' ) { + + if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && + gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { + + return 'highp'; + + } + + precision = 'mediump'; + + } + + if ( precision === 'mediump' ) { + + if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && + gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { + + return 'mediump'; + + } + + } + + return 'lowp'; + + } + + this.getMaxPrecision = getMaxPrecision; + + this.precision = parameters.precision !== undefined ? parameters.precision : 'highp', + this.logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false; + + this.maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); + this.maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); + this.maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE ); + this.maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ); + + this.maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); + this.maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS ); + this.maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); + this.maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); + + this.vertexTextures = this.maxVertexTextures > 0; + this.floatFragmentTextures = !! extensions.get( 'OES_texture_float' ); + this.floatVertexTextures = this.vertexTextures && this.floatFragmentTextures; + + var _maxPrecision = getMaxPrecision( this.precision ); + + if ( _maxPrecision !== this.precision ) { + + console.warn( 'THREE.WebGLRenderer:', this.precision, 'not supported, using', _maxPrecision, 'instead.' ); + this.precision = _maxPrecision; + + } + + if ( this.logarithmicDepthBuffer ) { + + this.logarithmicDepthBuffer = !! extensions.get( 'EXT_frag_depth' ); + + } + +}; + +// File:src/renderers/webgl/WebGLGeometries.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLGeometries = function ( gl, properties, info ) { + + var geometries = {}; + + function get( object ) { + + var geometry = object.geometry; + + if ( geometries[ geometry.id ] !== undefined ) { + + return geometries[ geometry.id ]; + + } + + geometry.addEventListener( 'dispose', onGeometryDispose ); + + var buffergeometry; + + if ( geometry instanceof THREE.BufferGeometry ) { + + buffergeometry = geometry; + + } else if ( geometry instanceof THREE.Geometry ) { + + if ( geometry._bufferGeometry === undefined ) { + + geometry._bufferGeometry = new THREE.BufferGeometry().setFromObject( object ); + + } + + buffergeometry = geometry._bufferGeometry; + + } + + geometries[ geometry.id ] = buffergeometry; + + info.memory.geometries ++; + + return buffergeometry; + + } + + function onGeometryDispose( event ) { + + var geometry = event.target; + var buffergeometry = geometries[ geometry.id ]; + + deleteAttributes( buffergeometry.attributes ); + + geometry.removeEventListener( 'dispose', onGeometryDispose ); + + delete geometries[ geometry.id ]; + + var property = properties.get( geometry ); + if ( property.wireframe ) deleteAttribute( property.wireframe ); + + info.memory.geometries --; + + } + + function getAttributeBuffer( attribute ) { + + if ( attribute instanceof THREE.InterleavedBufferAttribute ) { + + return properties.get( attribute.data ).__webglBuffer; + + } + + return properties.get( attribute ).__webglBuffer; + + } + + function deleteAttribute( attribute ) { + + var buffer = getAttributeBuffer( attribute ); + + if ( buffer !== undefined ) { + + gl.deleteBuffer( buffer ); + removeAttributeBuffer( attribute ); + + } + + } + + function deleteAttributes( attributes ) { + + for ( var name in attributes ) { + + deleteAttribute( attributes[ name ] ); + + } + + } + + function removeAttributeBuffer( attribute ) { + + if ( attribute instanceof THREE.InterleavedBufferAttribute ) { + + properties.delete( attribute.data ); + + } else { + + properties.delete( attribute ); + + } + + } + + this.get = get; + +}; + +// File:src/renderers/webgl/WebGLObjects.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLObjects = function ( gl, properties, info ) { + + var geometries = new THREE.WebGLGeometries( gl, properties, info ); + + // + + function update( object ) { + + // TODO: Avoid updating twice (when using shadowMap). Maybe add frame counter. + + var geometry = geometries.get( object ); + + if ( object.geometry instanceof THREE.Geometry ) { + + geometry.updateFromObject( object ); + + } + + var index = geometry.index; + var attributes = geometry.attributes; + + if ( index !== null ) { + + updateAttribute( index, gl.ELEMENT_ARRAY_BUFFER ); + + } + + for ( var name in attributes ) { + + updateAttribute( attributes[ name ], gl.ARRAY_BUFFER ); + + } + + // morph targets + + var morphAttributes = geometry.morphAttributes; + + for ( var name in morphAttributes ) { + + var array = morphAttributes[ name ]; + + for ( var i = 0, l = array.length; i < l; i ++ ) { + + updateAttribute( array[ i ], gl.ARRAY_BUFFER ); + + } + + } + + return geometry; + + } + + function updateAttribute( attribute, bufferType ) { + + var data = ( attribute instanceof THREE.InterleavedBufferAttribute ) ? attribute.data : attribute; + + var attributeProperties = properties.get( data ); + + if ( attributeProperties.__webglBuffer === undefined ) { + + createBuffer( attributeProperties, data, bufferType ); + + } else if ( attributeProperties.version !== data.version ) { + + updateBuffer( attributeProperties, data, bufferType ); + + } + + } + + function createBuffer( attributeProperties, data, bufferType ) { + + attributeProperties.__webglBuffer = gl.createBuffer(); + gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); + + var usage = data.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW; + + gl.bufferData( bufferType, data.array, usage ); + + attributeProperties.version = data.version; + + } + + function updateBuffer( attributeProperties, data, bufferType ) { + + gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); + + if ( data.dynamic === false || data.updateRange.count === - 1 ) { + + // Not using update ranges + + gl.bufferSubData( bufferType, 0, data.array ); + + } else if ( data.updateRange.count === 0 ) { + + console.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' ); + + } else { + + gl.bufferSubData( bufferType, data.updateRange.offset * data.array.BYTES_PER_ELEMENT, + data.array.subarray( data.updateRange.offset, data.updateRange.offset + data.updateRange.count ) ); + + data.updateRange.count = 0; // reset range + + } + + attributeProperties.version = data.version; + + } + + function getAttributeBuffer( attribute ) { + + if ( attribute instanceof THREE.InterleavedBufferAttribute ) { + + return properties.get( attribute.data ).__webglBuffer; + + } + + return properties.get( attribute ).__webglBuffer; + + } + + function getWireframeAttribute( geometry ) { + + var property = properties.get( geometry ); + + if ( property.wireframe !== undefined ) { + + return property.wireframe; + + } + + var indices = []; + + var index = geometry.index; + var attributes = geometry.attributes; + var position = attributes.position; + + // console.time( 'wireframe' ); + + if ( index !== null ) { + + var edges = {}; + var array = index.array; + + for ( var i = 0, l = array.length; i < l; i += 3 ) { + + var a = array[ i + 0 ]; + var b = array[ i + 1 ]; + var c = array[ i + 2 ]; + + if ( checkEdge( edges, a, b ) ) indices.push( a, b ); + if ( checkEdge( edges, b, c ) ) indices.push( b, c ); + if ( checkEdge( edges, c, a ) ) indices.push( c, a ); + + } + + } else { + + var array = attributes.position.array; + + for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { + + var a = i + 0; + var b = i + 1; + var c = i + 2; + + indices.push( a, b, b, c, c, a ); + + } + + } + + // console.timeEnd( 'wireframe' ); + + var TypeArray = position.count > 65535 ? Uint32Array : Uint16Array; + var attribute = new THREE.BufferAttribute( new TypeArray( indices ), 1 ); + + updateAttribute( attribute, gl.ELEMENT_ARRAY_BUFFER ); + + property.wireframe = attribute; + + return attribute; + + } + + function checkEdge( edges, a, b ) { + + if ( a > b ) { + + var tmp = a; + a = b; + b = tmp; + + } + + var list = edges[ a ]; + + if ( list === undefined ) { + + edges[ a ] = [ b ]; + return true; + + } else if ( list.indexOf( b ) === -1 ) { + + list.push( b ); + return true; + + } + + return false; + + } + + this.getAttributeBuffer = getAttributeBuffer; + this.getWireframeAttribute = getWireframeAttribute; + + this.update = update; + +}; + +// File:src/renderers/webgl/WebGLProgram.js + +THREE.WebGLProgram = ( function () { + + var programIdCount = 0; + + function generateDefines( defines ) { + + var chunks = []; + + for ( var name in defines ) { + + var value = defines[ name ]; + + if ( value === false ) continue; + + chunks.push( '#define ' + name + ' ' + value ); + + } + + return chunks.join( '\n' ); + + } + + function fetchUniformLocations( gl, program, identifiers ) { + + var uniforms = {}; + + var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); + + for ( var i = 0; i < n; i ++ ) { + + var info = gl.getActiveUniform( program, i ); + var name = info.name; + var location = gl.getUniformLocation( program, name ); + + // console.log("THREE.WebGLProgram: ACTIVE UNIFORM:", name); + + var suffixPos = name.lastIndexOf( '[0]' ); + if ( suffixPos !== - 1 && suffixPos === name.length - 3 ) { + + uniforms[ name.substr( 0, suffixPos ) ] = location; + + } + + uniforms[ name ] = location; + + } + + return uniforms; + + } + + function fetchAttributeLocations( gl, program, identifiers ) { + + var attributes = {}; + + var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); + + for ( var i = 0; i < n; i ++ ) { + + var info = gl.getActiveAttrib( program, i ); + var name = info.name; + + // console.log("THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); + + attributes[ name ] = gl.getAttribLocation( program, name ); + + } + + return attributes; + + } + + function filterEmptyLine( string ) { + + return string !== ''; + + } + + return function WebGLProgram( renderer, code, material, parameters ) { + + var gl = renderer.context; + + var defines = material.defines; + + var vertexShader = material.__webglShader.vertexShader; + var fragmentShader = material.__webglShader.fragmentShader; + + var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; + + if ( parameters.shadowMapType === THREE.PCFShadowMap ) { + + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; + + } else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) { + + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; + + } + + var envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + var envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; + var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; + + if ( parameters.envMap ) { + + switch ( material.envMap.mapping ) { + + case THREE.CubeReflectionMapping: + case THREE.CubeRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + break; + + case THREE.EquirectangularReflectionMapping: + case THREE.EquirectangularRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC'; + break; + + case THREE.SphericalReflectionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_SPHERE'; + break; + + } + + switch ( material.envMap.mapping ) { + + case THREE.CubeRefractionMapping: + case THREE.EquirectangularRefractionMapping: + envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; + break; + + } + + switch ( material.combine ) { + + case THREE.MultiplyOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; + break; + + case THREE.MixOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; + break; + + case THREE.AddOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; + break; + + } + + } + + var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; + + // console.log( 'building new program ' ); + + // + + var customDefines = generateDefines( defines ); + + // + + var program = gl.createProgram(); + + var prefixVertex, prefixFragment; + + if ( material instanceof THREE.RawShaderMaterial ) { + + prefixVertex = ''; + prefixFragment = ''; + + } else { + + prefixVertex = [ + + 'precision ' + parameters.precision + ' float;', + 'precision ' + parameters.precision + ' int;', + + '#define SHADER_NAME ' + material.__webglShader.name, + + customDefines, + + parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', + + renderer.gammaInput ? '#define GAMMA_INPUT' : '', + renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '', + '#define GAMMA_FACTOR ' + gammaFactorDefine, + + '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights, + '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights, + '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights, + '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights, + + '#define MAX_SHADOWS ' + parameters.maxShadows, + + '#define MAX_BONES ' + parameters.maxBones, + + parameters.map ? '#define USE_MAP' : '', + parameters.envMap ? '#define USE_ENVMAP' : '', + parameters.envMap ? '#define ' + envMapModeDefine : '', + parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.aoMap ? '#define USE_AOMAP' : '', + parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', + parameters.bumpMap ? '#define USE_BUMPMAP' : '', + parameters.normalMap ? '#define USE_NORMALMAP' : '', + parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', + parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + parameters.vertexColors ? '#define USE_COLOR' : '', + + parameters.flatShading ? '#define FLAT_SHADED' : '', + + parameters.skinning ? '#define USE_SKINNING' : '', + parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', + + parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', + parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', + + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', + parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '', + parameters.pointLightShadows > 0 ? '#define POINT_LIGHT_SHADOWS' : '', + + parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', + + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', + + + 'uniform mat4 modelMatrix;', + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform mat4 viewMatrix;', + 'uniform mat3 normalMatrix;', + 'uniform vec3 cameraPosition;', + + 'attribute vec3 position;', + 'attribute vec3 normal;', + 'attribute vec2 uv;', + + '#ifdef USE_COLOR', + + ' attribute vec3 color;', + + '#endif', + + '#ifdef USE_MORPHTARGETS', + + ' attribute vec3 morphTarget0;', + ' attribute vec3 morphTarget1;', + ' attribute vec3 morphTarget2;', + ' attribute vec3 morphTarget3;', + + ' #ifdef USE_MORPHNORMALS', + + ' attribute vec3 morphNormal0;', + ' attribute vec3 morphNormal1;', + ' attribute vec3 morphNormal2;', + ' attribute vec3 morphNormal3;', + + ' #else', + + ' attribute vec3 morphTarget4;', + ' attribute vec3 morphTarget5;', + ' attribute vec3 morphTarget6;', + ' attribute vec3 morphTarget7;', + + ' #endif', + + '#endif', + + '#ifdef USE_SKINNING', + + ' attribute vec4 skinIndex;', + ' attribute vec4 skinWeight;', + + '#endif', + + '\n' + + ].filter( filterEmptyLine ).join( '\n' ); + + prefixFragment = [ + + parameters.bumpMap || parameters.normalMap || parameters.flatShading || material.derivatives ? '#extension GL_OES_standard_derivatives : enable' : '', + parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '', + + 'precision ' + parameters.precision + ' float;', + 'precision ' + parameters.precision + ' int;', + + '#define SHADER_NAME ' + material.__webglShader.name, + + customDefines, + + '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights, + '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights, + '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights, + '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights, + + '#define MAX_SHADOWS ' + parameters.maxShadows, + + parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '', + + renderer.gammaInput ? '#define GAMMA_INPUT' : '', + renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '', + '#define GAMMA_FACTOR ' + gammaFactorDefine, + + ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', + ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '', + + parameters.map ? '#define USE_MAP' : '', + parameters.envMap ? '#define USE_ENVMAP' : '', + parameters.envMap ? '#define ' + envMapTypeDefine : '', + parameters.envMap ? '#define ' + envMapModeDefine : '', + parameters.envMap ? '#define ' + envMapBlendingDefine : '', + parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.aoMap ? '#define USE_AOMAP' : '', + parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', + parameters.bumpMap ? '#define USE_BUMPMAP' : '', + parameters.normalMap ? '#define USE_NORMALMAP' : '', + parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + parameters.vertexColors ? '#define USE_COLOR' : '', + + parameters.flatShading ? '#define FLAT_SHADED' : '', + + parameters.metal ? '#define METAL' : '', + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', + + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', + parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '', + parameters.pointLightShadows > 0 ? '#define POINT_LIGHT_SHADOWS' : '', + + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', + + 'uniform mat4 viewMatrix;', + 'uniform vec3 cameraPosition;', + + '\n' + + ].filter( filterEmptyLine ).join( '\n' ); + + } + + var vertexGlsl = prefixVertex + vertexShader; + var fragmentGlsl = prefixFragment + fragmentShader; + + var glVertexShader = THREE.WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); + var glFragmentShader = THREE.WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); + + gl.attachShader( program, glVertexShader ); + gl.attachShader( program, glFragmentShader ); + + // Force a particular attribute to index 0. + + if ( material.index0AttributeName !== undefined ) { + + gl.bindAttribLocation( program, 0, material.index0AttributeName ); + + } else if ( parameters.morphTargets === true ) { + + // programs with morphTargets displace position out of attribute 0 + gl.bindAttribLocation( program, 0, 'position' ); + + } + + gl.linkProgram( program ); + + var programLog = gl.getProgramInfoLog( program ); + var vertexLog = gl.getShaderInfoLog( glVertexShader ); + var fragmentLog = gl.getShaderInfoLog( glFragmentShader ); + + var runnable = true; + var haveDiagnostics = true; + + if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { + + runnable = false; + + console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog ); + + } else if ( programLog !== '' ) { + + console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); + + } else if ( vertexLog === '' || fragmentLog === '' ) { + + haveDiagnostics = false; + + } + + if ( haveDiagnostics ) { + + this.diagnostics = { + + runnable: runnable, + material: material, + + programLog: programLog, + + vertexShader: { + + log: vertexLog, + prefix: prefixVertex + + }, + + fragmentShader: { + + log: fragmentLog, + prefix: prefixFragment + + } + + }; + + } + + // clean up + + gl.deleteShader( glVertexShader ); + gl.deleteShader( glFragmentShader ); + + // set up caching for uniform locations + + var cachedUniforms; + + this.getUniforms = function() { + + if ( cachedUniforms === undefined ) { + + cachedUniforms = fetchUniformLocations( gl, program ); + + } + + return cachedUniforms; + + }; + + // set up caching for attribute locations + + var cachedAttributes; + + this.getAttributes = function() { + + if ( cachedAttributes === undefined ) { + + cachedAttributes = fetchAttributeLocations( gl, program ); + + } + + return cachedAttributes; + + }; + + // free resource + + this.destroy = function() { + + gl.deleteProgram( program ); + this.program = undefined; + + }; + + // DEPRECATED + + Object.defineProperties( this, { + + uniforms: { + get: function() { + + console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' ); + return this.getUniforms(); + + } + }, + + attributes: { + get: function() { + + console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' ); + return this.getAttributes(); + + } + } + + } ); + + + // + + this.id = programIdCount ++; + this.code = code; + this.usedTimes = 1; + this.program = program; + this.vertexShader = glVertexShader; + this.fragmentShader = glFragmentShader; + + return this; + + }; + +} )(); + +// File:src/renderers/webgl/WebGLPrograms.js + +THREE.WebGLPrograms = function ( renderer, capabilities ) { + + var programs = []; + + var shaderIDs = { + MeshDepthMaterial: 'depth', + MeshNormalMaterial: 'normal', + MeshBasicMaterial: 'basic', + MeshLambertMaterial: 'lambert', + MeshPhongMaterial: 'phong', + LineBasicMaterial: 'basic', + LineDashedMaterial: 'dashed', + PointsMaterial: 'points' + }; + + var parameterNames = [ + "precision", "supportsVertexTextures", "map", "envMap", "envMapMode", + "lightMap", "aoMap", "emissiveMap", "bumpMap", "normalMap", "displacementMap", "specularMap", + "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp", + "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", + "maxBones", "useVertexTexture", "morphTargets", "morphNormals", + "maxMorphTargets", "maxMorphNormals", "maxDirLights", "maxPointLights", + "maxSpotLights", "maxHemiLights", "maxShadows", "shadowMapEnabled", "pointLightShadows", + "shadowMapType", "shadowMapDebug", "alphaTest", "metal", "doubleSided", + "flipSided" + ]; + + + function allocateBones ( object ) { + + if ( capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture ) { + + return 1024; + + } else { + + // default for when object is not specified + // ( for example when prebuilding shader to be used with multiple objects ) + // + // - leave some extra space for other uniforms + // - limit here is ANGLE's 254 max uniform vectors + // (up to 54 should be safe) + + var nVertexUniforms = capabilities.maxVertexUniforms; + var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); + + var maxBones = nVertexMatrices; + + if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { + + maxBones = Math.min( object.skeleton.bones.length, maxBones ); + + if ( maxBones < object.skeleton.bones.length ) { + + console.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' ); + + } + + } + + return maxBones; + + } + + } + + function allocateLights( lights ) { + + var dirLights = 0; + var pointLights = 0; + var spotLights = 0; + var hemiLights = 0; + + for ( var l = 0, ll = lights.length; l < ll; l ++ ) { + + var light = lights[ l ]; + + if ( light.visible === false ) continue; + + if ( light instanceof THREE.DirectionalLight ) dirLights ++; + if ( light instanceof THREE.PointLight ) pointLights ++; + if ( light instanceof THREE.SpotLight ) spotLights ++; + if ( light instanceof THREE.HemisphereLight ) hemiLights ++; + + } + + return { 'directional': dirLights, 'point': pointLights, 'spot': spotLights, 'hemi': hemiLights }; + + } + + function allocateShadows( lights ) { + + var maxShadows = 0; + var pointLightShadows = 0; + + for ( var l = 0, ll = lights.length; l < ll; l ++ ) { + + var light = lights[ l ]; + + if ( ! light.castShadow ) continue; + + if ( light instanceof THREE.SpotLight || light instanceof THREE.DirectionalLight ) maxShadows ++; + if ( light instanceof THREE.PointLight ) { + + maxShadows ++; + pointLightShadows ++; + + } + + } + + return { 'maxShadows': maxShadows, 'pointLightShadows': pointLightShadows }; + + } + + this.getParameters = function ( material, lights, fog, object ) { + + var shaderID = shaderIDs[ material.type ]; + // heuristics to create shader parameters according to lights in the scene + // (not to blow over maxLights budget) + + var maxLightCount = allocateLights( lights ); + var allocatedShadows = allocateShadows( lights ); + var maxBones = allocateBones( object ); + var precision = renderer.getPrecision(); + + if ( material.precision !== null ) { + + precision = capabilities.getMaxPrecision( material.precision ); + + if ( precision !== material.precision ) { + + console.warn( 'THREE.WebGLRenderer.initMaterial:', material.precision, 'not supported, using', precision, 'instead.' ); + + } + + } + + var parameters = { + + shaderID: shaderID, + + precision: precision, + supportsVertexTextures: capabilities.vertexTextures, + + map: !! material.map, + envMap: !! material.envMap, + envMapMode: material.envMap && material.envMap.mapping, + lightMap: !! material.lightMap, + aoMap: !! material.aoMap, + emissiveMap: !! material.emissiveMap, + bumpMap: !! material.bumpMap, + normalMap: !! material.normalMap, + displacementMap: !! material.displacementMap, + specularMap: !! material.specularMap, + alphaMap: !! material.alphaMap, + + combine: material.combine, + + vertexColors: material.vertexColors, + + fog: fog, + useFog: material.fog, + fogExp: fog instanceof THREE.FogExp2, + + flatShading: material.shading === THREE.FlatShading, + + sizeAttenuation: material.sizeAttenuation, + logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer, + + skinning: material.skinning, + maxBones: maxBones, + useVertexTexture: capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture, + + morphTargets: material.morphTargets, + morphNormals: material.morphNormals, + maxMorphTargets: renderer.maxMorphTargets, + maxMorphNormals: renderer.maxMorphNormals, + + maxDirLights: maxLightCount.directional, + maxPointLights: maxLightCount.point, + maxSpotLights: maxLightCount.spot, + maxHemiLights: maxLightCount.hemi, + + maxShadows: allocatedShadows.maxShadows, + pointLightShadows: allocatedShadows.pointLightShadows, + shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && allocatedShadows.maxShadows > 0, + shadowMapType: renderer.shadowMap.type, + shadowMapDebug: renderer.shadowMap.debug, + + alphaTest: material.alphaTest, + metal: material.metal, + doubleSided: material.side === THREE.DoubleSide, + flipSided: material.side === THREE.BackSide + + }; + + return parameters; + + }; + + this.getProgramCode = function ( material, parameters ) { + + var chunks = []; + + if ( parameters.shaderID ) { + + chunks.push( parameters.shaderID ); + + } else { + + chunks.push( material.fragmentShader ); + chunks.push( material.vertexShader ); + + } + + if ( material.defines !== undefined ) { + + for ( var name in material.defines ) { + + chunks.push( name ); + chunks.push( material.defines[ name ] ); + + } + + } + + for ( var i = 0; i < parameterNames.length; i ++ ) { + + var parameterName = parameterNames[ i ]; + chunks.push( parameterName ); + chunks.push( parameters[ parameterName ] ); + + } + + return chunks.join(); + + }; + + this.acquireProgram = function ( material, parameters, code ) { + + var program; + + // Check if code has been already compiled + for ( var p = 0, pl = programs.length; p < pl; p ++ ) { + + var programInfo = programs[ p ]; + + if ( programInfo.code === code ) { + + program = programInfo; + ++ program.usedTimes; + + break; + + } + + } + + if ( program === undefined ) { + + program = new THREE.WebGLProgram( renderer, code, material, parameters ); + programs.push( program ); + + } + + return program; + + }; + + this.releaseProgram = function( program ) { + + if ( -- program.usedTimes === 0 ) { + + // Remove from unordered set + var i = programs.indexOf( program ); + programs[ i ] = programs[ programs.length - 1 ]; + programs.pop(); + + // Free WebGL resources + program.destroy(); + + } + + }; + + // Exposed for resource monitoring & error feedback via renderer.info: + this.programs = programs; + +}; + +// File:src/renderers/webgl/WebGLProperties.js + +/** +* @author fordacious / fordacious.github.io +*/ + +THREE.WebGLProperties = function () { + + var properties = {}; + + this.get = function ( object ) { + + var uuid = object.uuid; + var map = properties[ uuid ]; + + if ( map === undefined ) { + + map = {}; + properties[ uuid ] = map; + + } + + return map; + + }; + + this.delete = function ( object ) { + + delete properties[ object.uuid ]; + + }; + + this.clear = function () { + + properties = {}; + + }; + +}; + +// File:src/renderers/webgl/WebGLShader.js + +THREE.WebGLShader = ( function () { + + function addLineNumbers( string ) { + + var lines = string.split( '\n' ); + + for ( var i = 0; i < lines.length; i ++ ) { + + lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; + + } + + return lines.join( '\n' ); + + } + + return function WebGLShader( gl, type, string ) { + + var shader = gl.createShader( type ); + + gl.shaderSource( shader, string ); + gl.compileShader( shader ); + + if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { + + console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); + + } + + if ( gl.getShaderInfoLog( shader ) !== '' ) { + + console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) ); + + } + + // --enable-privileged-webgl-extension + // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); + + return shader; + + }; + +} )(); + +// File:src/renderers/webgl/WebGLShadowMap.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { + + var _gl = _renderer.context, + _state = _renderer.state, + _frustum = new THREE.Frustum(), + _projScreenMatrix = new THREE.Matrix4(), + + _min = new THREE.Vector3(), + _max = new THREE.Vector3(), + + _lookTarget = new THREE.Vector3(), + _lightPositionWorld = new THREE.Vector3(), + + _renderList = [], + + _MorphingFlag = 1, + _SkinningFlag = 2, + + _NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1, + + _depthMaterials = new Array( _NumberOfMaterialVariants ), + _distanceMaterials = new Array( _NumberOfMaterialVariants ); + + var cubeDirections = [ + new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( - 1, 0, 0 ), new THREE.Vector3( 0, 0, 1 ), + new THREE.Vector3( 0, 0, - 1 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, - 1, 0 ) + ]; + + var cubeUps = [ + new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ), + new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 0, 1 ), new THREE.Vector3( 0, 0, - 1 ) + ]; + + var cube2DViewPorts = [ + new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4(), + new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4() + ]; + + var _vector4 = new THREE.Vector4(); + + // init + + var depthShader = THREE.ShaderLib[ "depthRGBA" ]; + var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); + + var distanceShader = THREE.ShaderLib[ "distanceRGBA" ]; + var distanceUniforms = THREE.UniformsUtils.clone( distanceShader.uniforms ); + + for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) { + + var useMorphing = ( i & _MorphingFlag ) !== 0; + var useSkinning = ( i & _SkinningFlag ) !== 0; + + var depthMaterial = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader, + morphTargets: useMorphing, + skinning: useSkinning + } ); + + depthMaterial._shadowPass = true; + + _depthMaterials[ i ] = depthMaterial; + + var distanceMaterial = new THREE.ShaderMaterial( { + uniforms: distanceUniforms, + vertexShader: distanceShader.vertexShader, + fragmentShader: distanceShader.fragmentShader, + morphTargets: useMorphing, + skinning: useSkinning + } ); + + distanceMaterial._shadowPass = true; + + _distanceMaterials[ i ] = distanceMaterial; + + } + + // + + var scope = this; + + this.enabled = false; + + this.autoUpdate = true; + this.needsUpdate = false; + + this.type = THREE.PCFShadowMap; + this.cullFace = THREE.CullFaceFront; + + this.render = function ( scene ) { + + var faceCount, isPointLight; + + if ( scope.enabled === false ) return; + if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; + + // Set GL state for depth map. + _gl.clearColor( 1, 1, 1, 1 ); + _state.disable( _gl.BLEND ); + _state.enable( _gl.CULL_FACE ); + _gl.frontFace( _gl.CCW ); + _gl.cullFace( scope.cullFace === THREE.CullFaceFront ? _gl.FRONT : _gl.BACK ); + _state.setDepthTest( true ); + + // save the existing viewport so it can be restored later + _renderer.getViewport( _vector4 ); + + // render depth map + + for ( var i = 0, il = _lights.length; i < il; i ++ ) { + + var light = _lights[ i ]; + + if ( light.castShadow === true ) { + + var shadow = light.shadow; + var shadowCamera = shadow.camera; + var shadowMapSize = shadow.mapSize; + + if ( light instanceof THREE.PointLight ) { + + faceCount = 6; + isPointLight = true; + + var vpWidth = shadowMapSize.x / 4.0; + var vpHeight = shadowMapSize.y / 2.0; + + // These viewports map a cube-map onto a 2D texture with the + // following orientation: + // + // xzXZ + // y Y + // + // X - Positive x direction + // x - Negative x direction + // Y - Positive y direction + // y - Negative y direction + // Z - Positive z direction + // z - Negative z direction + + // positive X + cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight ); + // negative X + cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight ); + // positive Z + cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight ); + // negative Z + cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight ); + // positive Y + cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight ); + // negative Y + cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight ); + + } else { + + faceCount = 1; + isPointLight = false; + + } + + if ( shadow.map === null ) { + + var shadowFilter = THREE.LinearFilter; + + if ( scope.type === THREE.PCFSoftShadowMap ) { + + shadowFilter = THREE.NearestFilter; + + } + + var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat }; + + shadow.map = new THREE.WebGLRenderTarget( shadowMapSize.x, shadowMapSize.y, pars ); + shadow.matrix = new THREE.Matrix4(); + + // + + if ( light instanceof THREE.SpotLight ) { + + shadowCamera.aspect = shadowMapSize.x / shadowMapSize.y; + + } + + shadowCamera.updateProjectionMatrix(); + + } + + var shadowMap = shadow.map; + var shadowMatrix = shadow.matrix; + + _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); + shadowCamera.position.copy( _lightPositionWorld ); + + _renderer.setRenderTarget( shadowMap ); + _renderer.clear(); + + // render shadow map for each cube face (if omni-directional) or + // run a single pass if not + + for ( var face = 0; face < faceCount; face ++ ) { + + if ( isPointLight ) { + + _lookTarget.copy( shadowCamera.position ); + _lookTarget.add( cubeDirections[ face ] ); + shadowCamera.up.copy( cubeUps[ face ] ); + shadowCamera.lookAt( _lookTarget ); + var vpDimensions = cube2DViewPorts[ face ]; + _renderer.setViewport( vpDimensions.x, vpDimensions.y, vpDimensions.z, vpDimensions.w ); + + } else { + + _lookTarget.setFromMatrixPosition( light.target.matrixWorld ); + shadowCamera.lookAt( _lookTarget ); + + } + + shadowCamera.updateMatrixWorld(); + shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); + + // compute shadow matrix + + shadowMatrix.set( + 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0 + ); + + shadowMatrix.multiply( shadowCamera.projectionMatrix ); + shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); + + // update camera matrices and frustum + + _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + // set object matrices & frustum culling + + _renderList.length = 0; + + projectObject( scene, shadowCamera ); + + // render shadow map + // render regular objects + + for ( var j = 0, jl = _renderList.length; j < jl; j ++ ) { + + var object = _renderList[ j ]; + var geometry = _objects.update( object ); + var material = object.material; + + if ( material instanceof THREE.MeshFaceMaterial ) { + + var groups = geometry.groups; + var materials = material.materials; + + for ( var k = 0, kl = groups.length; k < kl; k ++ ) { + + var group = groups[ k ]; + var groupMaterial = materials[ group.materialIndex ]; + + if ( groupMaterial.visible === true ) { + + var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld ); + _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial, object, group ); + + } + + } + + } else { + + var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld ); + _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial, object, null ); + + } + + } + + } + + // We must call _renderer.resetGLState() at the end of each iteration of + // the light loop in order to force material updates for each light. + _renderer.resetGLState(); + + } + + } + + _renderer.setViewport( _vector4.x, _vector4.y, _vector4.z, _vector4.w ); + + // Restore GL state. + var clearColor = _renderer.getClearColor(), + clearAlpha = _renderer.getClearAlpha(); + _renderer.setClearColor( clearColor, clearAlpha ); + _state.enable( _gl.BLEND ); + + if ( scope.cullFace === THREE.CullFaceFront ) { + + _gl.cullFace( _gl.BACK ); + + } + + _renderer.resetGLState(); + + scope.needsUpdate = false; + + }; + + function getDepthMaterial( object, material, isPointLight, lightPositionWorld ) { + + var geometry = object.geometry; + + var newMaterial = null; + + var materialVariants = _depthMaterials; + var customMaterial = object.customDepthMaterial; + + if ( isPointLight ) { + + materialVariants = _distanceMaterials; + customMaterial = object.customDistanceMaterial; + + } + + if ( ! customMaterial ) { + + var useMorphing = geometry.morphTargets !== undefined && + geometry.morphTargets.length > 0 && material.morphTargets; + + var useSkinning = object instanceof THREE.SkinnedMesh && material.skinning; + + var variantIndex = 0; + + if ( useMorphing ) variantIndex |= _MorphingFlag; + if ( useSkinning ) variantIndex |= _SkinningFlag; + + newMaterial = materialVariants[ variantIndex ]; + + } else { + + newMaterial = customMaterial; + + } + + newMaterial.visible = material.visible; + newMaterial.wireframe = material.wireframe; + newMaterial.wireframeLinewidth = material.wireframeLinewidth; + + if ( isPointLight && newMaterial.uniforms.lightPos !== undefined ) { + + newMaterial.uniforms.lightPos.value.copy( lightPositionWorld ); + + } + + return newMaterial; + + } + + function projectObject( object, camera ) { + + if ( object.visible === false ) return; + + if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) { + + if ( object.castShadow && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) { + + var material = object.material; + + if ( material.visible === true ) { + + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + _renderList.push( object ); + + } + + } + + } + + var children = object.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + projectObject( children[ i ], camera ); + + } + + } + +}; + +// File:src/renderers/webgl/WebGLState.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLState = function ( gl, extensions, paramThreeToGL ) { + + var _this = this; + + var newAttributes = new Uint8Array( 16 ); + var enabledAttributes = new Uint8Array( 16 ); + var attributeDivisors = new Uint8Array( 16 ); + + var capabilities = {}; + + var compressedTextureFormats = null; + + var currentBlending = null; + var currentBlendEquation = null; + var currentBlendSrc = null; + var currentBlendDst = null; + var currentBlendEquationAlpha = null; + var currentBlendSrcAlpha = null; + var currentBlendDstAlpha = null; + + var currentDepthFunc = null; + var currentDepthWrite = null; + + var currentColorWrite = null; + + var currentFlipSided = null; + + var currentLineWidth = null; + + var currentPolygonOffsetFactor = null; + var currentPolygonOffsetUnits = null; + + var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); + + var currentTextureSlot = undefined; + var currentBoundTextures = {}; + + this.init = function () { + + gl.clearColor( 0, 0, 0, 1 ); + gl.clearDepth( 1 ); + gl.clearStencil( 0 ); + + this.enable( gl.DEPTH_TEST ); + gl.depthFunc( gl.LEQUAL ); + + gl.frontFace( gl.CCW ); + gl.cullFace( gl.BACK ); + this.enable( gl.CULL_FACE ); + + this.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); + + }; + + this.initAttributes = function () { + + for ( var i = 0, l = newAttributes.length; i < l; i ++ ) { + + newAttributes[ i ] = 0; + + } + + }; + + this.enableAttribute = function ( attribute ) { + + newAttributes[ attribute ] = 1; + + if ( enabledAttributes[ attribute ] === 0 ) { + + gl.enableVertexAttribArray( attribute ); + enabledAttributes[ attribute ] = 1; + + } + + if ( attributeDivisors[ attribute ] !== 0 ) { + + var extension = extensions.get( 'ANGLE_instanced_arrays' ); + + extension.vertexAttribDivisorANGLE( attribute, 0 ); + attributeDivisors[ attribute ] = 0; + + } + + }; + + this.enableAttributeAndDivisor = function ( attribute, meshPerAttribute, extension ) { + + newAttributes[ attribute ] = 1; + + if ( enabledAttributes[ attribute ] === 0 ) { + + gl.enableVertexAttribArray( attribute ); + enabledAttributes[ attribute ] = 1; + + } + + if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { + + extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute ); + attributeDivisors[ attribute ] = meshPerAttribute; + + } + + }; + + this.disableUnusedAttributes = function () { + + for ( var i = 0, l = enabledAttributes.length; i < l; i ++ ) { + + if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { + + gl.disableVertexAttribArray( i ); + enabledAttributes[ i ] = 0; + + } + + } + + }; + + this.enable = function ( id ) { + + if ( capabilities[ id ] !== true ) { + + gl.enable( id ); + capabilities[ id ] = true; + + } + + }; + + this.disable = function ( id ) { + + if ( capabilities[ id ] !== false ) { + + gl.disable( id ); + capabilities[ id ] = false; + + } + + }; + + this.getCompressedTextureFormats = function () { + + if ( compressedTextureFormats === null ) { + + compressedTextureFormats = []; + + if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || + extensions.get( 'WEBGL_compressed_texture_s3tc' ) ) { + + var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS ); + + for ( var i = 0; i < formats.length; i ++ ) { + + compressedTextureFormats.push( formats[ i ] ); + + } + + } + + } + + return compressedTextureFormats; + + }; + + this.setBlending = function ( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha ) { + + if ( blending !== currentBlending ) { + + if ( blending === THREE.NoBlending ) { + + this.disable( gl.BLEND ); + + } else if ( blending === THREE.AdditiveBlending ) { + + this.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); + + } else if ( blending === THREE.SubtractiveBlending ) { + + // TODO: Find blendFuncSeparate() combination + + this.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR ); + + } else if ( blending === THREE.MultiplyBlending ) { + + // TODO: Find blendFuncSeparate() combination + + this.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); + + } else if ( blending === THREE.CustomBlending ) { + + this.enable( gl.BLEND ); + + } else { + + this.enable( gl.BLEND ); + gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); + gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); + + } + + currentBlending = blending; + + } + + if ( blending === THREE.CustomBlending ) { + + blendEquationAlpha = blendEquationAlpha || blendEquation; + blendSrcAlpha = blendSrcAlpha || blendSrc; + blendDstAlpha = blendDstAlpha || blendDst; + + if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { + + gl.blendEquationSeparate( paramThreeToGL( blendEquation ), paramThreeToGL( blendEquationAlpha ) ); + + currentBlendEquation = blendEquation; + currentBlendEquationAlpha = blendEquationAlpha; + + } + + if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { + + gl.blendFuncSeparate( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ), paramThreeToGL( blendSrcAlpha ), paramThreeToGL( blendDstAlpha ) ); + + currentBlendSrc = blendSrc; + currentBlendDst = blendDst; + currentBlendSrcAlpha = blendSrcAlpha; + currentBlendDstAlpha = blendDstAlpha; + + } + + } else { + + currentBlendEquation = null; + currentBlendSrc = null; + currentBlendDst = null; + currentBlendEquationAlpha = null; + currentBlendSrcAlpha = null; + currentBlendDstAlpha = null; + + } + + }; + + this.setDepthFunc = function ( depthFunc ) { + + if ( currentDepthFunc !== depthFunc ) { + + if ( depthFunc ) { + + switch ( depthFunc ) { + + case THREE.NeverDepth: + + gl.depthFunc( gl.NEVER ); + break; + + case THREE.AlwaysDepth: + + gl.depthFunc( gl.ALWAYS ); + break; + + case THREE.LessDepth: + + gl.depthFunc( gl.LESS ); + break; + + case THREE.LessEqualDepth: + + gl.depthFunc( gl.LEQUAL ); + break; + + case THREE.EqualDepth: + + gl.depthFunc( gl.EQUAL ); + break; + + case THREE.GreaterEqualDepth: + + gl.depthFunc( gl.GEQUAL ); + break; + + case THREE.GreaterDepth: + + gl.depthFunc( gl.GREATER ); + break; + + case THREE.NotEqualDepth: + + gl.depthFunc( gl.NOTEQUAL ); + break; + + default: + + gl.depthFunc( gl.LEQUAL ); + + } + + } else { + + gl.depthFunc( gl.LEQUAL ); + + } + + currentDepthFunc = depthFunc; + + } + + }; + + this.setDepthTest = function ( depthTest ) { + + if ( depthTest ) { + + this.enable( gl.DEPTH_TEST ); + + } else { + + this.disable( gl.DEPTH_TEST ); + + } + + }; + + this.setDepthWrite = function ( depthWrite ) { + + if ( currentDepthWrite !== depthWrite ) { + + gl.depthMask( depthWrite ); + currentDepthWrite = depthWrite; + + } + + }; + + this.setColorWrite = function ( colorWrite ) { + + if ( currentColorWrite !== colorWrite ) { + + gl.colorMask( colorWrite, colorWrite, colorWrite, colorWrite ); + currentColorWrite = colorWrite; + + } + + }; + + this.setFlipSided = function ( flipSided ) { + + if ( currentFlipSided !== flipSided ) { + + if ( flipSided ) { + + gl.frontFace( gl.CW ); + + } else { + + gl.frontFace( gl.CCW ); + + } + + currentFlipSided = flipSided; + + } + + }; + + this.setLineWidth = function ( width ) { + + if ( width !== currentLineWidth ) { + + gl.lineWidth( width ); + + currentLineWidth = width; + + } + + }; + + this.setPolygonOffset = function ( polygonOffset, factor, units ) { + + if ( polygonOffset ) { + + this.enable( gl.POLYGON_OFFSET_FILL ); + + } else { + + this.disable( gl.POLYGON_OFFSET_FILL ); + + } + + if ( polygonOffset && ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) ) { + + gl.polygonOffset( factor, units ); + + currentPolygonOffsetFactor = factor; + currentPolygonOffsetUnits = units; + + } + + }; + + this.setScissorTest = function ( scissorTest ) { + + if ( scissorTest ) { + + this.enable( gl.SCISSOR_TEST ); + + } else { + + this.disable( gl.SCISSOR_TEST ); + + } + + }; + + // texture + + this.activeTexture = function ( webglSlot ) { + + if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; + + if ( currentTextureSlot !== webglSlot ) { + + gl.activeTexture( webglSlot ); + currentTextureSlot = webglSlot; + + } + + } + + this.bindTexture = function ( webglType, webglTexture ) { + + if ( currentTextureSlot === undefined ) { + + _this.activeTexture(); + + } + + var boundTexture = currentBoundTextures[ currentTextureSlot ]; + + if ( boundTexture === undefined ) { + + boundTexture = { type: undefined, texture: undefined }; + currentBoundTextures[ currentTextureSlot ] = boundTexture; + + } + + if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { + + gl.bindTexture( webglType, webglTexture ); + + boundTexture.type = webglType; + boundTexture.texture = webglTexture; + + } + + }; + + this.compressedTexImage2D = function () { + + try { + + gl.compressedTexImage2D.apply( gl, arguments ); + + } catch ( error ) { + + console.error( error ); + + } + + }; + + this.texImage2D = function () { + + try { + + gl.texImage2D.apply( gl, arguments ); + + } catch ( error ) { + + console.error( error ); + + } + + }; + + // + + this.reset = function () { + + for ( var i = 0; i < enabledAttributes.length; i ++ ) { + + if ( enabledAttributes[ i ] === 1 ) { + + gl.disableVertexAttribArray( i ); + enabledAttributes[ i ] = 0; + + } + + } + + capabilities = {}; + + compressedTextureFormats = null; + + currentBlending = null; + + currentDepthWrite = null; + currentColorWrite = null; + + currentFlipSided = null; + + }; + +}; + +// File:src/renderers/webgl/plugins/LensFlarePlugin.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.LensFlarePlugin = function ( renderer, flares ) { + + var gl = renderer.context; + var state = renderer.state; + + var vertexBuffer, elementBuffer; + var program, attributes, uniforms; + var hasVertexTexture; + + var tempTexture, occlusionTexture; + + function init() { + + var vertices = new Float32Array( [ + - 1, - 1, 0, 0, + 1, - 1, 1, 0, + 1, 1, 1, 1, + - 1, 1, 0, 1 + ] ); + + var faces = new Uint16Array( [ + 0, 1, 2, + 0, 2, 3 + ] ); + + // buffers + + vertexBuffer = gl.createBuffer(); + elementBuffer = gl.createBuffer(); + + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); + + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); + + // textures + + tempTexture = gl.createTexture(); + occlusionTexture = gl.createTexture(); + + state.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); + + state.bindTexture( gl.TEXTURE_2D, occlusionTexture ); + gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); + + hasVertexTexture = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 0; + + var shader; + + if ( hasVertexTexture ) { + + shader = { + + vertexShader: [ + + "uniform lowp int renderType;", + + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", + + "uniform sampler2D occlusionMap;", + + "attribute vec2 position;", + "attribute vec2 uv;", + + "varying vec2 vUV;", + "varying float vVisibility;", + + "void main() {", + + "vUV = uv;", + + "vec2 pos = position;", + + "if ( renderType == 2 ) {", + + "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );", + + "vVisibility = visibility.r / 9.0;", + "vVisibility *= 1.0 - visibility.g / 9.0;", + "vVisibility *= visibility.b / 9.0;", + "vVisibility *= 1.0 - visibility.a / 9.0;", + + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + + "}", + + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform lowp int renderType;", + + "uniform sampler2D map;", + "uniform float opacity;", + "uniform vec3 color;", + + "varying vec2 vUV;", + "varying float vVisibility;", + + "void main() {", + + // pink square + + "if ( renderType == 0 ) {", + + "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", + + // restore + + "} else if ( renderType == 1 ) {", + + "gl_FragColor = texture2D( map, vUV );", + + // flare + + "} else {", + + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * vVisibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", + + "}", + + "}" + + ].join( "\n" ) + + }; + + } else { + + shader = { + + vertexShader: [ + + "uniform lowp int renderType;", + + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", + + "attribute vec2 position;", + "attribute vec2 uv;", + + "varying vec2 vUV;", + + "void main() {", + + "vUV = uv;", + + "vec2 pos = position;", + + "if ( renderType == 2 ) {", + + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + + "}", + + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "precision mediump float;", + + "uniform lowp int renderType;", + + "uniform sampler2D map;", + "uniform sampler2D occlusionMap;", + "uniform float opacity;", + "uniform vec3 color;", + + "varying vec2 vUV;", + + "void main() {", + + // pink square + + "if ( renderType == 0 ) {", + + "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );", + + // restore + + "} else if ( renderType == 1 ) {", + + "gl_FragColor = texture2D( map, vUV );", + + // flare + + "} else {", + + "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;", + "visibility = ( 1.0 - visibility / 4.0 );", + + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * visibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", + + "}", + + "}" + + ].join( "\n" ) + + }; + + } + + program = createProgram( shader ); + + attributes = { + vertex: gl.getAttribLocation ( program, "position" ), + uv: gl.getAttribLocation ( program, "uv" ) + }; + + uniforms = { + renderType: gl.getUniformLocation( program, "renderType" ), + map: gl.getUniformLocation( program, "map" ), + occlusionMap: gl.getUniformLocation( program, "occlusionMap" ), + opacity: gl.getUniformLocation( program, "opacity" ), + color: gl.getUniformLocation( program, "color" ), + scale: gl.getUniformLocation( program, "scale" ), + rotation: gl.getUniformLocation( program, "rotation" ), + screenPosition: gl.getUniformLocation( program, "screenPosition" ) + }; + + } + + /* + * Render lens flares + * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, + * reads these back and calculates occlusion. + */ + + this.render = function ( scene, camera, viewportWidth, viewportHeight ) { + + if ( flares.length === 0 ) return; + + var tempPosition = new THREE.Vector3(); + + var invAspect = viewportHeight / viewportWidth, + halfViewportWidth = viewportWidth * 0.5, + halfViewportHeight = viewportHeight * 0.5; + + var size = 16 / viewportHeight, + scale = new THREE.Vector2( size * invAspect, size ); + + var screenPosition = new THREE.Vector3( 1, 1, 0 ), + screenPositionPixels = new THREE.Vector2( 1, 1 ); + + if ( program === undefined ) { + + init(); + + } + + gl.useProgram( program ); + + state.initAttributes(); + state.enableAttribute( attributes.vertex ); + state.enableAttribute( attributes.uv ); + state.disableUnusedAttributes(); + + // loop through all lens flares to update their occlusion and positions + // setup gl and common used attribs/uniforms + + gl.uniform1i( uniforms.occlusionMap, 0 ); + gl.uniform1i( uniforms.map, 1 ); + + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.vertexAttribPointer( attributes.vertex, 2, gl.FLOAT, false, 2 * 8, 0 ); + gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); + + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + + state.disable( gl.CULL_FACE ); + gl.depthMask( false ); + + for ( var i = 0, l = flares.length; i < l; i ++ ) { + + size = 16 / viewportHeight; + scale.set( size * invAspect, size ); + + // calc object screen position + + var flare = flares[ i ]; + + tempPosition.set( flare.matrixWorld.elements[ 12 ], flare.matrixWorld.elements[ 13 ], flare.matrixWorld.elements[ 14 ] ); + + tempPosition.applyMatrix4( camera.matrixWorldInverse ); + tempPosition.applyProjection( camera.projectionMatrix ); + + // setup arrays for gl programs + + screenPosition.copy( tempPosition ); + + screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth; + screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight; + + // screen cull + + if ( hasVertexTexture || ( + screenPositionPixels.x > 0 && + screenPositionPixels.x < viewportWidth && + screenPositionPixels.y > 0 && + screenPositionPixels.y < viewportHeight ) ) { + + // save current RGB to temp texture + + state.activeTexture( gl.TEXTURE0 ); + state.bindTexture( gl.TEXTURE_2D, null ); + state.activeTexture( gl.TEXTURE1 ); + state.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + + + // render pink quad + + gl.uniform1i( uniforms.renderType, 0 ); + gl.uniform2f( uniforms.scale, scale.x, scale.y ); + gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + + state.disable( gl.BLEND ); + state.enable( gl.DEPTH_TEST ); + + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + + // copy result to occlusionMap + + state.activeTexture( gl.TEXTURE0 ); + state.bindTexture( gl.TEXTURE_2D, occlusionTexture ); + gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + + + // restore graphics + + gl.uniform1i( uniforms.renderType, 1 ); + state.disable( gl.DEPTH_TEST ); + + state.activeTexture( gl.TEXTURE1 ); + state.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + + // update object positions + + flare.positionScreen.copy( screenPosition ); + + if ( flare.customUpdateCallback ) { + + flare.customUpdateCallback( flare ); + + } else { + + flare.updateLensFlares(); + + } + + // render flares + + gl.uniform1i( uniforms.renderType, 2 ); + state.enable( gl.BLEND ); + + for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { + + var sprite = flare.lensFlares[ j ]; + + if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { + + screenPosition.x = sprite.x; + screenPosition.y = sprite.y; + screenPosition.z = sprite.z; + + size = sprite.size * sprite.scale / viewportHeight; + + scale.x = size * invAspect; + scale.y = size; + + gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + gl.uniform2f( uniforms.scale, scale.x, scale.y ); + gl.uniform1f( uniforms.rotation, sprite.rotation ); + + gl.uniform1f( uniforms.opacity, sprite.opacity ); + gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); + + state.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); + renderer.setTexture( sprite.texture, 1 ); + + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + } + + } + + } + + } + + // restore gl + + state.enable( gl.CULL_FACE ); + state.enable( gl.DEPTH_TEST ); + gl.depthMask( true ); + + renderer.resetGLState(); + + }; + + function createProgram ( shader ) { + + var program = gl.createProgram(); + + var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); + var vertexShader = gl.createShader( gl.VERTEX_SHADER ); + + var prefix = "precision " + renderer.getPrecision() + " float;\n"; + + gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); + gl.shaderSource( vertexShader, prefix + shader.vertexShader ); + + gl.compileShader( fragmentShader ); + gl.compileShader( vertexShader ); + + gl.attachShader( program, fragmentShader ); + gl.attachShader( program, vertexShader ); + + gl.linkProgram( program ); + + return program; + + } + +}; + +// File:src/renderers/webgl/plugins/SpritePlugin.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SpritePlugin = function ( renderer, sprites ) { + + var gl = renderer.context; + var state = renderer.state; + + var vertexBuffer, elementBuffer; + var program, attributes, uniforms; + + var texture; + + // decompose matrixWorld + + var spritePosition = new THREE.Vector3(); + var spriteRotation = new THREE.Quaternion(); + var spriteScale = new THREE.Vector3(); + + function init() { + + var vertices = new Float32Array( [ + - 0.5, - 0.5, 0, 0, + 0.5, - 0.5, 1, 0, + 0.5, 0.5, 1, 1, + - 0.5, 0.5, 0, 1 + ] ); + + var faces = new Uint16Array( [ + 0, 1, 2, + 0, 2, 3 + ] ); + + vertexBuffer = gl.createBuffer(); + elementBuffer = gl.createBuffer(); + + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); + + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); + + program = createProgram(); + + attributes = { + position: gl.getAttribLocation ( program, 'position' ), + uv: gl.getAttribLocation ( program, 'uv' ) + }; + + uniforms = { + uvOffset: gl.getUniformLocation( program, 'uvOffset' ), + uvScale: gl.getUniformLocation( program, 'uvScale' ), + + rotation: gl.getUniformLocation( program, 'rotation' ), + scale: gl.getUniformLocation( program, 'scale' ), + + color: gl.getUniformLocation( program, 'color' ), + map: gl.getUniformLocation( program, 'map' ), + opacity: gl.getUniformLocation( program, 'opacity' ), + + modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ), + projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ), + + fogType: gl.getUniformLocation( program, 'fogType' ), + fogDensity: gl.getUniformLocation( program, 'fogDensity' ), + fogNear: gl.getUniformLocation( program, 'fogNear' ), + fogFar: gl.getUniformLocation( program, 'fogFar' ), + fogColor: gl.getUniformLocation( program, 'fogColor' ), + + alphaTest: gl.getUniformLocation( program, 'alphaTest' ) + }; + + var canvas = document.createElement( 'canvas' ); + canvas.width = 8; + canvas.height = 8; + + var context = canvas.getContext( '2d' ); + context.fillStyle = 'white'; + context.fillRect( 0, 0, 8, 8 ); + + texture = new THREE.Texture( canvas ); + texture.needsUpdate = true; + + } + + this.render = function ( scene, camera ) { + + if ( sprites.length === 0 ) return; + + // setup gl + + if ( program === undefined ) { + + init(); + + } + + gl.useProgram( program ); + + state.initAttributes(); + state.enableAttribute( attributes.position ); + state.enableAttribute( attributes.uv ); + state.disableUnusedAttributes(); + + state.disable( gl.CULL_FACE ); + state.enable( gl.BLEND ); + + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 ); + gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); + + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + + gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + + state.activeTexture( gl.TEXTURE0 ); + gl.uniform1i( uniforms.map, 0 ); + + var oldFogType = 0; + var sceneFogType = 0; + var fog = scene.fog; + + if ( fog ) { + + gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); + + if ( fog instanceof THREE.Fog ) { + + gl.uniform1f( uniforms.fogNear, fog.near ); + gl.uniform1f( uniforms.fogFar, fog.far ); + + gl.uniform1i( uniforms.fogType, 1 ); + oldFogType = 1; + sceneFogType = 1; + + } else if ( fog instanceof THREE.FogExp2 ) { + + gl.uniform1f( uniforms.fogDensity, fog.density ); + + gl.uniform1i( uniforms.fogType, 2 ); + oldFogType = 2; + sceneFogType = 2; + + } + + } else { + + gl.uniform1i( uniforms.fogType, 0 ); + oldFogType = 0; + sceneFogType = 0; + + } + + + // update positions and sort + + for ( var i = 0, l = sprites.length; i < l; i ++ ) { + + var sprite = sprites[ i ]; + + sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); + sprite.z = - sprite.modelViewMatrix.elements[ 14 ]; + + } + + sprites.sort( painterSortStable ); + + // render all sprites + + var scale = []; + + for ( var i = 0, l = sprites.length; i < l; i ++ ) { + + var sprite = sprites[ i ]; + var material = sprite.material; + + gl.uniform1f( uniforms.alphaTest, material.alphaTest ); + gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements ); + + sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale ); + + scale[ 0 ] = spriteScale.x; + scale[ 1 ] = spriteScale.y; + + var fogType = 0; + + if ( scene.fog && material.fog ) { + + fogType = sceneFogType; + + } + + if ( oldFogType !== fogType ) { + + gl.uniform1i( uniforms.fogType, fogType ); + oldFogType = fogType; + + } + + if ( material.map !== null ) { + + gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); + gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); + + } else { + + gl.uniform2f( uniforms.uvOffset, 0, 0 ); + gl.uniform2f( uniforms.uvScale, 1, 1 ); + + } + + gl.uniform1f( uniforms.opacity, material.opacity ); + gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); + + gl.uniform1f( uniforms.rotation, material.rotation ); + gl.uniform2fv( uniforms.scale, scale ); + + state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + state.setDepthTest( material.depthTest ); + state.setDepthWrite( material.depthWrite ); + + if ( material.map && material.map.image && material.map.image.width ) { + + renderer.setTexture( material.map, 0 ); + + } else { + + renderer.setTexture( texture, 0 ); + + } + + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + } + + // restore gl + + state.enable( gl.CULL_FACE ); + + renderer.resetGLState(); + + }; + + function createProgram () { + + var program = gl.createProgram(); + + var vertexShader = gl.createShader( gl.VERTEX_SHADER ); + var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); + + gl.shaderSource( vertexShader, [ + + 'precision ' + renderer.getPrecision() + ' float;', + + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform float rotation;', + 'uniform vec2 scale;', + 'uniform vec2 uvOffset;', + 'uniform vec2 uvScale;', + + 'attribute vec2 position;', + 'attribute vec2 uv;', + + 'varying vec2 vUV;', + + 'void main() {', + + 'vUV = uvOffset + uv * uvScale;', + + 'vec2 alignedPosition = position * scale;', + + 'vec2 rotatedPosition;', + 'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;', + 'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;', + + 'vec4 finalPosition;', + + 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );', + 'finalPosition.xy += rotatedPosition;', + 'finalPosition = projectionMatrix * finalPosition;', + + 'gl_Position = finalPosition;', + + '}' + + ].join( '\n' ) ); + + gl.shaderSource( fragmentShader, [ + + 'precision ' + renderer.getPrecision() + ' float;', + + 'uniform vec3 color;', + 'uniform sampler2D map;', + 'uniform float opacity;', + + 'uniform int fogType;', + 'uniform vec3 fogColor;', + 'uniform float fogDensity;', + 'uniform float fogNear;', + 'uniform float fogFar;', + 'uniform float alphaTest;', + + 'varying vec2 vUV;', + + 'void main() {', + + 'vec4 texture = texture2D( map, vUV );', + + 'if ( texture.a < alphaTest ) discard;', + + 'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );', + + 'if ( fogType > 0 ) {', + + 'float depth = gl_FragCoord.z / gl_FragCoord.w;', + 'float fogFactor = 0.0;', + + 'if ( fogType == 1 ) {', + + 'fogFactor = smoothstep( fogNear, fogFar, depth );', + + '} else {', + + 'const float LOG2 = 1.442695;', + 'fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );', + 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );', + + '}', + + 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );', + + '}', + + '}' + + ].join( '\n' ) ); + + gl.compileShader( vertexShader ); + gl.compileShader( fragmentShader ); + + gl.attachShader( program, vertexShader ); + gl.attachShader( program, fragmentShader ); + + gl.linkProgram( program ); + + return program; + + } + + function painterSortStable ( a, b ) { + + if ( a.z !== b.z ) { + + return b.z - a.z; + + } else { + + return b.id - a.id; + + } + + } + +}; + +// File:src/extras/CurveUtils.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.CurveUtils = { + + tangentQuadraticBezier: function ( t, p0, p1, p2 ) { + + return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 ); + + }, + + // Puay Bing, thanks for helping with this derivative! + + tangentCubicBezier: function ( t, p0, p1, p2, p3 ) { + + return - 3 * p0 * ( 1 - t ) * ( 1 - t ) + + 3 * p1 * ( 1 - t ) * ( 1 - t ) - 6 * t * p1 * ( 1 - t ) + + 6 * t * p2 * ( 1 - t ) - 3 * t * t * p2 + + 3 * t * t * p3; + + }, + + tangentSpline: function ( t, p0, p1, p2, p3 ) { + + // To check if my formulas are correct + + var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1 + var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t + var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2 + var h11 = 3 * t * t - 2 * t; // t3 − t2 + + return h00 + h10 + h01 + h11; + + }, + + // Catmull-Rom + + interpolate: function( p0, p1, p2, p3, t ) { + + var v0 = ( p2 - p0 ) * 0.5; + var v1 = ( p3 - p1 ) * 0.5; + var t2 = t * t; + var t3 = t * t2; + return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + } + +}; + +// File:src/extras/GeometryUtils.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.GeometryUtils = { + + merge: function ( geometry1, geometry2, materialIndexOffset ) { + + console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' ); + + var matrix; + + if ( geometry2 instanceof THREE.Mesh ) { + + geometry2.matrixAutoUpdate && geometry2.updateMatrix(); + + matrix = geometry2.matrix; + geometry2 = geometry2.geometry; + + } + + geometry1.merge( geometry2, matrix, materialIndexOffset ); + + }, + + center: function ( geometry ) { + + console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' ); + return geometry.center(); + + } + +}; + +// File:src/extras/ImageUtils.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author Daosheng Mu / https://github.com/DaoshengMu/ + */ + +THREE.ImageUtils = { + + crossOrigin: undefined, + + loadTexture: function ( url, mapping, onLoad, onError ) { + + console.warn( 'THREE.ImageUtils.loadTexture is being deprecated. Use THREE.TextureLoader() instead.' ); + + var loader = new THREE.TextureLoader(); + loader.setCrossOrigin( this.crossOrigin ); + + var texture = loader.load( url, onLoad, undefined, onError ); + + if ( mapping ) texture.mapping = mapping; + + return texture; + + }, + + loadTextureCube: function ( urls, mapping, onLoad, onError ) { + + console.warn( 'THREE.ImageUtils.loadTextureCube is being deprecated. Use THREE.CubeTextureLoader() instead.' ); + + var loader = new THREE.CubeTextureLoader(); + loader.setCrossOrigin( this.crossOrigin ); + + var texture = loader.load( urls, onLoad, undefined, onError ); + + if ( mapping ) texture.mapping = mapping; + + return texture; + + }, + + loadCompressedTexture: function () { + + console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ) + + }, + + loadCompressedTextureCube: function () { + + console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ) + + } + +}; + +// File:src/extras/SceneUtils.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SceneUtils = { + + createMultiMaterialObject: function ( geometry, materials ) { + + var group = new THREE.Group(); + + for ( var i = 0, l = materials.length; i < l; i ++ ) { + + group.add( new THREE.Mesh( geometry, materials[ i ] ) ); + + } + + return group; + + }, + + detach: function ( child, parent, scene ) { + + child.applyMatrix( parent.matrixWorld ); + parent.remove( child ); + scene.add( child ); + + }, + + attach: function ( child, scene, parent ) { + + var matrixWorldInverse = new THREE.Matrix4(); + matrixWorldInverse.getInverse( parent.matrixWorld ); + child.applyMatrix( matrixWorldInverse ); + + scene.remove( child ); + parent.add( child ); + + } + +}; + +// File:src/extras/ShapeUtils.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.ShapeUtils = { + + // calculate area of the contour polygon + + area: function ( contour ) { + + var n = contour.length; + var a = 0.0; + + for ( var p = n - 1, q = 0; q < n; p = q ++ ) { + + a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; + + } + + return a * 0.5; + + }, + + triangulate: ( function () { + + /** + * This code is a quick port of code written in C++ which was submitted to + * flipcode.com by John W. Ratcliff // July 22, 2000 + * See original code and more information here: + * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml + * + * ported to actionscript by Zevan Rosser + * www.actionsnippet.com + * + * ported to javascript by Joshua Koo + * http://www.lab4games.net/zz85/blog + * + */ + + function snip( contour, u, v, w, n, verts ) { + + var p; + var ax, ay, bx, by; + var cx, cy, px, py; + + ax = contour[ verts[ u ] ].x; + ay = contour[ verts[ u ] ].y; + + bx = contour[ verts[ v ] ].x; + by = contour[ verts[ v ] ].y; + + cx = contour[ verts[ w ] ].x; + cy = contour[ verts[ w ] ].y; + + if ( Number.EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false; + + var aX, aY, bX, bY, cX, cY; + var apx, apy, bpx, bpy, cpx, cpy; + var cCROSSap, bCROSScp, aCROSSbp; + + aX = cx - bx; aY = cy - by; + bX = ax - cx; bY = ay - cy; + cX = bx - ax; cY = by - ay; + + for ( p = 0; p < n; p ++ ) { + + px = contour[ verts[ p ] ].x; + py = contour[ verts[ p ] ].y; + + if ( ( ( px === ax ) && ( py === ay ) ) || + ( ( px === bx ) && ( py === by ) ) || + ( ( px === cx ) && ( py === cy ) ) ) continue; + + apx = px - ax; apy = py - ay; + bpx = px - bx; bpy = py - by; + cpx = px - cx; cpy = py - cy; + + // see if p is inside triangle abc + + aCROSSbp = aX * bpy - aY * bpx; + cCROSSap = cX * apy - cY * apx; + bCROSScp = bX * cpy - bY * cpx; + + if ( ( aCROSSbp >= - Number.EPSILON ) && ( bCROSScp >= - Number.EPSILON ) && ( cCROSSap >= - Number.EPSILON ) ) return false; + + } + + return true; + + } + + // takes in an contour array and returns + + return function ( contour, indices ) { + + var n = contour.length; + + if ( n < 3 ) return null; + + var result = [], + verts = [], + vertIndices = []; + + /* we want a counter-clockwise polygon in verts */ + + var u, v, w; + + if ( THREE.ShapeUtils.area( contour ) > 0.0 ) { + + for ( v = 0; v < n; v ++ ) verts[ v ] = v; + + } else { + + for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v; + + } + + var nv = n; + + /* remove nv - 2 vertices, creating 1 triangle every time */ + + var count = 2 * nv; /* error detection */ + + for ( v = nv - 1; nv > 2; ) { + + /* if we loop, it is probably a non-simple polygon */ + + if ( ( count -- ) <= 0 ) { + + //** Triangulate: ERROR - probable bad polygon! + + //throw ( "Warning, unable to triangulate polygon!" ); + //return null; + // Sometimes warning is fine, especially polygons are triangulated in reverse. + console.warn( 'THREE.ShapeUtils: Unable to triangulate polygon! in triangulate()' ); + + if ( indices ) return vertIndices; + return result; + + } + + /* three consecutive vertices in current polygon, */ + + u = v; if ( nv <= u ) u = 0; /* previous */ + v = u + 1; if ( nv <= v ) v = 0; /* new v */ + w = v + 1; if ( nv <= w ) w = 0; /* next */ + + if ( snip( contour, u, v, w, nv, verts ) ) { + + var a, b, c, s, t; + + /* true names of the vertices */ + + a = verts[ u ]; + b = verts[ v ]; + c = verts[ w ]; + + /* output Triangle */ + + result.push( [ contour[ a ], + contour[ b ], + contour[ c ] ] ); + + + vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); + + /* remove v from the remaining polygon */ + + for ( s = v, t = v + 1; t < nv; s ++, t ++ ) { + + verts[ s ] = verts[ t ]; + + } + + nv --; + + /* reset error detection counter */ + + count = 2 * nv; + + } + + } + + if ( indices ) return vertIndices; + return result; + + } + + } )(), + + triangulateShape: function ( contour, holes ) { + + function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) { + + // inOtherPt needs to be collinear to the inSegment + if ( inSegPt1.x !== inSegPt2.x ) { + + if ( inSegPt1.x < inSegPt2.x ) { + + return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) ); + + } else { + + return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) ); + + } + + } else { + + if ( inSegPt1.y < inSegPt2.y ) { + + return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) ); + + } else { + + return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) ); + + } + + } + + } + + function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) { + + var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y; + var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y; + + var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x; + var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y; + + var limit = seg1dy * seg2dx - seg1dx * seg2dy; + var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy; + + if ( Math.abs( limit ) > Number.EPSILON ) { + + // not parallel + + var perpSeg2; + if ( limit > 0 ) { + + if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return []; + perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; + if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return []; + + } else { + + if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return []; + perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; + if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return []; + + } + + // i.e. to reduce rounding errors + // intersection at endpoint of segment#1? + if ( perpSeg2 === 0 ) { + + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; + return [ inSeg1Pt1 ]; + + } + if ( perpSeg2 === limit ) { + + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; + return [ inSeg1Pt2 ]; + + } + // intersection at endpoint of segment#2? + if ( perpSeg1 === 0 ) return [ inSeg2Pt1 ]; + if ( perpSeg1 === limit ) return [ inSeg2Pt2 ]; + + // return real intersection point + var factorSeg1 = perpSeg2 / limit; + return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx, + y: inSeg1Pt1.y + factorSeg1 * seg1dy } ]; + + } else { + + // parallel or collinear + if ( ( perpSeg1 !== 0 ) || + ( seg2dy * seg1seg2dx !== seg2dx * seg1seg2dy ) ) return []; + + // they are collinear or degenerate + var seg1Pt = ( ( seg1dx === 0 ) && ( seg1dy === 0 ) ); // segment1 is just a point? + var seg2Pt = ( ( seg2dx === 0 ) && ( seg2dy === 0 ) ); // segment2 is just a point? + // both segments are points + if ( seg1Pt && seg2Pt ) { + + if ( ( inSeg1Pt1.x !== inSeg2Pt1.x ) || + ( inSeg1Pt1.y !== inSeg2Pt1.y ) ) return []; // they are distinct points + return [ inSeg1Pt1 ]; // they are the same point + + } + // segment#1 is a single point + if ( seg1Pt ) { + + if ( ! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2 + return [ inSeg1Pt1 ]; + + } + // segment#2 is a single point + if ( seg2Pt ) { + + if ( ! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1 + return [ inSeg2Pt1 ]; + + } + + // they are collinear segments, which might overlap + var seg1min, seg1max, seg1minVal, seg1maxVal; + var seg2min, seg2max, seg2minVal, seg2maxVal; + if ( seg1dx !== 0 ) { + + // the segments are NOT on a vertical line + if ( inSeg1Pt1.x < inSeg1Pt2.x ) { + + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x; + + } else { + + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x; + + } + if ( inSeg2Pt1.x < inSeg2Pt2.x ) { + + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x; + + } else { + + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x; + + } + + } else { + + // the segments are on a vertical line + if ( inSeg1Pt1.y < inSeg1Pt2.y ) { + + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y; + + } else { + + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y; + + } + if ( inSeg2Pt1.y < inSeg2Pt2.y ) { + + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y; + + } else { + + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y; + + } + + } + if ( seg1minVal <= seg2minVal ) { + + if ( seg1maxVal < seg2minVal ) return []; + if ( seg1maxVal === seg2minVal ) { + + if ( inExcludeAdjacentSegs ) return []; + return [ seg2min ]; + + } + if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ]; + return [ seg2min, seg2max ]; + + } else { + + if ( seg1minVal > seg2maxVal ) return []; + if ( seg1minVal === seg2maxVal ) { + + if ( inExcludeAdjacentSegs ) return []; + return [ seg1min ]; + + } + if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ]; + return [ seg1min, seg2max ]; + + } + + } + + } + + function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) { + + // The order of legs is important + + // translation of all points, so that Vertex is at (0,0) + var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y; + var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y; + var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y; + + // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg. + var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX; + var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX; + + if ( Math.abs( from2toAngle ) > Number.EPSILON ) { + + // angle != 180 deg. + + var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX; + // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle ); + + if ( from2toAngle > 0 ) { + + // main angle < 180 deg. + return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) ); + + } else { + + // main angle > 180 deg. + return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) ); + + } + + } else { + + // angle == 180 deg. + // console.log( "from2to: 180 deg., from2other: " + from2otherAngle ); + return ( from2otherAngle > 0 ); + + } + + } + + + function removeHoles( contour, holes ) { + + var shape = contour.concat(); // work on this shape + var hole; + + function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) { + + // Check if hole point lies within angle around shape point + var lastShapeIdx = shape.length - 1; + + var prevShapeIdx = inShapeIdx - 1; + if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx; + + var nextShapeIdx = inShapeIdx + 1; + if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0; + + var insideAngle = isPointInsideAngle( shape[ inShapeIdx ], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[ inHoleIdx ] ); + if ( ! insideAngle ) { + + // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y ); + return false; + + } + + // Check if shape point lies within angle around hole point + var lastHoleIdx = hole.length - 1; + + var prevHoleIdx = inHoleIdx - 1; + if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx; + + var nextHoleIdx = inHoleIdx + 1; + if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0; + + insideAngle = isPointInsideAngle( hole[ inHoleIdx ], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[ inShapeIdx ] ); + if ( ! insideAngle ) { + + // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y ); + return false; + + } + + return true; + + } + + function intersectsShapeEdge( inShapePt, inHolePt ) { + + // checks for intersections with shape edges + var sIdx, nextIdx, intersection; + for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) { + + nextIdx = sIdx + 1; nextIdx %= shape.length; + intersection = intersect_segments_2D( inShapePt, inHolePt, shape[ sIdx ], shape[ nextIdx ], true ); + if ( intersection.length > 0 ) return true; + + } + + return false; + + } + + var indepHoles = []; + + function intersectsHoleEdge( inShapePt, inHolePt ) { + + // checks for intersections with hole edges + var ihIdx, chkHole, + hIdx, nextIdx, intersection; + for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) { + + chkHole = holes[ indepHoles[ ihIdx ]]; + for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) { + + nextIdx = hIdx + 1; nextIdx %= chkHole.length; + intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[ hIdx ], chkHole[ nextIdx ], true ); + if ( intersection.length > 0 ) return true; + + } + + } + return false; + + } + + var holeIndex, shapeIndex, + shapePt, holePt, + holeIdx, cutKey, failedCuts = [], + tmpShape1, tmpShape2, + tmpHole1, tmpHole2; + + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + + indepHoles.push( h ); + + } + + var minShapeIndex = 0; + var counter = indepHoles.length * 2; + while ( indepHoles.length > 0 ) { + + counter --; + if ( counter < 0 ) { + + console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" ); + break; + + } + + // search for shape-vertex and hole-vertex, + // which can be connected without intersections + for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) { + + shapePt = shape[ shapeIndex ]; + holeIndex = - 1; + + // search for hole which can be reached without intersections + for ( var h = 0; h < indepHoles.length; h ++ ) { + + holeIdx = indepHoles[ h ]; + + // prevent multiple checks + cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx; + if ( failedCuts[ cutKey ] !== undefined ) continue; + + hole = holes[ holeIdx ]; + for ( var h2 = 0; h2 < hole.length; h2 ++ ) { + + holePt = hole[ h2 ]; + if ( ! isCutLineInsideAngles( shapeIndex, h2 ) ) continue; + if ( intersectsShapeEdge( shapePt, holePt ) ) continue; + if ( intersectsHoleEdge( shapePt, holePt ) ) continue; + + holeIndex = h2; + indepHoles.splice( h, 1 ); + + tmpShape1 = shape.slice( 0, shapeIndex + 1 ); + tmpShape2 = shape.slice( shapeIndex ); + tmpHole1 = hole.slice( holeIndex ); + tmpHole2 = hole.slice( 0, holeIndex + 1 ); + + shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); + + minShapeIndex = shapeIndex; + + // Debug only, to show the selected cuts + // glob_CutLines.push( [ shapePt, holePt ] ); + + break; + + } + if ( holeIndex >= 0 ) break; // hole-vertex found + + failedCuts[ cutKey ] = true; // remember failure + + } + if ( holeIndex >= 0 ) break; // hole-vertex found + + } + + } + + return shape; /* shape with no holes */ + + } + + + var i, il, f, face, + key, index, + allPointsMap = {}; + + // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first. + + var allpoints = contour.concat(); + + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + + Array.prototype.push.apply( allpoints, holes[ h ] ); + + } + + //console.log( "allpoints",allpoints, allpoints.length ); + + // prepare all points map + + for ( i = 0, il = allpoints.length; i < il; i ++ ) { + + key = allpoints[ i ].x + ":" + allpoints[ i ].y; + + if ( allPointsMap[ key ] !== undefined ) { + + console.warn( "THREE.Shape: Duplicate point", key ); + + } + + allPointsMap[ key ] = i; + + } + + // remove holes by cutting paths to holes and adding them to the shape + var shapeWithoutHoles = removeHoles( contour, holes ); + + var triangles = THREE.ShapeUtils.triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape + //console.log( "triangles",triangles, triangles.length ); + + // check all face vertices against all points map + + for ( i = 0, il = triangles.length; i < il; i ++ ) { + + face = triangles[ i ]; + + for ( f = 0; f < 3; f ++ ) { + + key = face[ f ].x + ":" + face[ f ].y; + + index = allPointsMap[ key ]; + + if ( index !== undefined ) { + + face[ f ] = index; + + } + + } + + } + + return triangles.concat(); + + }, + + isClockWise: function ( pts ) { + + return THREE.ShapeUtils.area( pts ) < 0; + + }, + + // Bezier Curves formulas obtained from + // http://en.wikipedia.org/wiki/B%C3%A9zier_curve + + // Quad Bezier Functions + + b2: ( function () { + + function b2p0( t, p ) { + + var k = 1 - t; + return k * k * p; + + } + + function b2p1( t, p ) { + + return 2 * ( 1 - t ) * t * p; + + } + + function b2p2( t, p ) { + + return t * t * p; + + } + + return function ( t, p0, p1, p2 ) { + + return b2p0( t, p0 ) + b2p1( t, p1 ) + b2p2( t, p2 ); + + }; + + } )(), + + // Cubic Bezier Functions + + b3: ( function () { + + function b3p0( t, p ) { + + var k = 1 - t; + return k * k * k * p; + + } + + function b3p1( t, p ) { + + var k = 1 - t; + return 3 * k * k * t * p; + + } + + function b3p2( t, p ) { + + var k = 1 - t; + return 3 * k * t * t * p; + + } + + function b3p3( t, p ) { + + return t * t * t * p; + + } + + return function ( t, p0, p1, p2, p3 ) { + + return b3p0( t, p0 ) + b3p1( t, p1 ) + b3p2( t, p2 ) + b3p3( t, p3 ); + + }; + + } )() + +}; + +// File:src/extras/audio/Audio.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Audio = function ( listener ) { + + THREE.Object3D.call( this ); + + this.type = 'Audio'; + + this.context = listener.context; + this.source = this.context.createBufferSource(); + this.source.onended = this.onEnded.bind( this ); + + this.gain = this.context.createGain(); + this.gain.connect( this.context.destination ); + + this.panner = this.context.createPanner(); + this.panner.connect( this.gain ); + + this.autoplay = false; + + this.startTime = 0; + this.playbackRate = 1; + this.isPlaying = false; + +}; + +THREE.Audio.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Audio.prototype.constructor = THREE.Audio; + +THREE.Audio.prototype.load = function ( file ) { + + var scope = this; + + var request = new XMLHttpRequest(); + request.open( 'GET', file, true ); + request.responseType = 'arraybuffer'; + request.onload = function ( e ) { + + scope.context.decodeAudioData( this.response, function ( buffer ) { + + scope.source.buffer = buffer; + + if ( scope.autoplay ) scope.play(); + + } ); + + }; + request.send(); + + return this; + +}; + +THREE.Audio.prototype.play = function () { + + if ( this.isPlaying === true ) { + + console.warn( 'THREE.Audio: Audio is already playing.' ); + return; + + } + + var source = this.context.createBufferSource(); + + source.buffer = this.source.buffer; + source.loop = this.source.loop; + source.onended = this.source.onended; + source.start( 0, this.startTime ); + source.playbackRate.value = this.playbackRate; + + this.isPlaying = true; + + this.source = source; + + this.connect(); + +}; + +THREE.Audio.prototype.pause = function () { + + this.source.stop(); + this.startTime = this.context.currentTime; + +}; + +THREE.Audio.prototype.stop = function () { + + this.source.stop(); + this.startTime = 0; + +}; + +THREE.Audio.prototype.connect = function () { + + if ( this.filter !== undefined ) { + + this.source.connect( this.filter ); + this.filter.connect( this.panner ); + + } else { + + this.source.connect( this.panner ); + + } + +}; + +THREE.Audio.prototype.disconnect = function () { + + if ( this.filter !== undefined ) { + + this.source.disconnect( this.filter ); + this.filter.disconnect( this.panner ); + + } else { + + this.source.disconnect( this.panner ); + + } + +}; + +THREE.Audio.prototype.setFilter = function ( value ) { + + if ( this.isPlaying === true ) { + + this.disconnect(); + this.filter = value; + this.connect(); + + } else { + + this.filter = value; + + } + +}; + +THREE.Audio.prototype.getFilter = function () { + + return this.filter; + +}; + +THREE.Audio.prototype.setPlaybackRate = function ( value ) { + + this.playbackRate = value; + + if ( this.isPlaying === true ) { + + this.source.playbackRate.value = this.playbackRate; + + } + +}; + +THREE.Audio.prototype.getPlaybackRate = function () { + + return this.playbackRate; + +}; + +THREE.Audio.prototype.onEnded = function() { + + this.isPlaying = false; + +}; + +THREE.Audio.prototype.setLoop = function ( value ) { + + this.source.loop = value; + +}; + +THREE.Audio.prototype.getLoop = function () { + + return this.source.loop; + +}; + +THREE.Audio.prototype.setRefDistance = function ( value ) { + + this.panner.refDistance = value; + +}; + +THREE.Audio.prototype.getRefDistance = function () { + + return this.panner.refDistance; + +}; + +THREE.Audio.prototype.setRolloffFactor = function ( value ) { + + this.panner.rolloffFactor = value; + +}; + +THREE.Audio.prototype.getRolloffFactor = function () { + + return this.panner.rolloffFactor; + +}; + +THREE.Audio.prototype.setVolume = function ( value ) { + + this.gain.gain.value = value; + +}; + +THREE.Audio.prototype.getVolume = function () { + + return this.gain.gain.value; + +}; + +THREE.Audio.prototype.updateMatrixWorld = ( function () { + + var position = new THREE.Vector3(); + + return function updateMatrixWorld( force ) { + + THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); + + position.setFromMatrixPosition( this.matrixWorld ); + + this.panner.setPosition( position.x, position.y, position.z ); + + }; + +} )(); + +// File:src/extras/audio/AudioListener.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AudioListener = function () { + + THREE.Object3D.call( this ); + + this.type = 'AudioListener'; + + this.context = new ( window.AudioContext || window.webkitAudioContext )(); + +}; + +THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype ); +THREE.AudioListener.prototype.constructor = THREE.AudioListener; + +THREE.AudioListener.prototype.updateMatrixWorld = ( function () { + + var position = new THREE.Vector3(); + var quaternion = new THREE.Quaternion(); + var scale = new THREE.Vector3(); + + var orientation = new THREE.Vector3(); + + return function updateMatrixWorld( force ) { + + THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); + + var listener = this.context.listener; + var up = this.up; + + this.matrixWorld.decompose( position, quaternion, scale ); + + orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion ); + + listener.setPosition( position.x, position.y, position.z ); + listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z ); + + }; + +} )(); + +// File:src/extras/core/Curve.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Extensible curve object + * + * Some common of Curve methods + * .getPoint(t), getTangent(t) + * .getPointAt(u), getTagentAt(u) + * .getPoints(), .getSpacedPoints() + * .getLength() + * .updateArcLengths() + * + * This following classes subclasses THREE.Curve: + * + * -- 2d classes -- + * THREE.LineCurve + * THREE.QuadraticBezierCurve + * THREE.CubicBezierCurve + * THREE.SplineCurve + * THREE.ArcCurve + * THREE.EllipseCurve + * + * -- 3d classes -- + * THREE.LineCurve3 + * THREE.QuadraticBezierCurve3 + * THREE.CubicBezierCurve3 + * THREE.SplineCurve3 + * THREE.ClosedSplineCurve3 + * + * A series of curves can be represented as a THREE.CurvePath + * + **/ + +/************************************************************** + * Abstract Curve base class + **************************************************************/ + +THREE.Curve = function () { + +}; + +THREE.Curve.prototype = { + + constructor: THREE.Curve, + + // Virtual base class method to overwrite and implement in subclasses + // - t [0 .. 1] + + getPoint: function ( t ) { + + console.warn( "THREE.Curve: Warning, getPoint() not implemented!" ); + return null; + + }, + + // Get point at relative position in curve according to arc length + // - u [0 .. 1] + + getPointAt: function ( u ) { + + var t = this.getUtoTmapping( u ); + return this.getPoint( t ); + + }, + + // Get sequence of points using getPoint( t ) + + getPoints: function ( divisions ) { + + if ( ! divisions ) divisions = 5; + + var d, pts = []; + + for ( d = 0; d <= divisions; d ++ ) { + + pts.push( this.getPoint( d / divisions ) ); + + } + + return pts; + + }, + + // Get sequence of points using getPointAt( u ) + + getSpacedPoints: function ( divisions ) { + + if ( ! divisions ) divisions = 5; + + var d, pts = []; + + for ( d = 0; d <= divisions; d ++ ) { + + pts.push( this.getPointAt( d / divisions ) ); + + } + + return pts; + + }, + + // Get total curve arc length + + getLength: function () { + + var lengths = this.getLengths(); + return lengths[ lengths.length - 1 ]; + + }, + + // Get list of cumulative segment lengths + + getLengths: function ( divisions ) { + + if ( ! divisions ) divisions = ( this.__arcLengthDivisions ) ? ( this.__arcLengthDivisions ) : 200; + + if ( this.cacheArcLengths + && ( this.cacheArcLengths.length === divisions + 1 ) + && ! this.needsUpdate ) { + + //console.log( "cached", this.cacheArcLengths ); + return this.cacheArcLengths; + + } + + this.needsUpdate = false; + + var cache = []; + var current, last = this.getPoint( 0 ); + var p, sum = 0; + + cache.push( 0 ); + + for ( p = 1; p <= divisions; p ++ ) { + + current = this.getPoint ( p / divisions ); + sum += current.distanceTo( last ); + cache.push( sum ); + last = current; + + } + + this.cacheArcLengths = cache; + + return cache; // { sums: cache, sum:sum }; Sum is in the last element. + + }, + + updateArcLengths: function() { + + this.needsUpdate = true; + this.getLengths(); + + }, + + // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant + + getUtoTmapping: function ( u, distance ) { + + var arcLengths = this.getLengths(); + + var i = 0, il = arcLengths.length; + + var targetArcLength; // The targeted u distance value to get + + if ( distance ) { + + targetArcLength = distance; + + } else { + + targetArcLength = u * arcLengths[ il - 1 ]; + + } + + //var time = Date.now(); + + // binary search for the index with largest value smaller than target u distance + + var low = 0, high = il - 1, comparison; + + while ( low <= high ) { + + i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats + + comparison = arcLengths[ i ] - targetArcLength; + + if ( comparison < 0 ) { + + low = i + 1; + + } else if ( comparison > 0 ) { + + high = i - 1; + + } else { + + high = i; + break; + + // DONE + + } + + } + + i = high; + + //console.log('b' , i, low, high, Date.now()- time); + + if ( arcLengths[ i ] === targetArcLength ) { + + var t = i / ( il - 1 ); + return t; + + } + + // we could get finer grain at lengths, or use simple interpolation between two points + + var lengthBefore = arcLengths[ i ]; + var lengthAfter = arcLengths[ i + 1 ]; + + var segmentLength = lengthAfter - lengthBefore; + + // determine where we are between the 'before' and 'after' points + + var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + + // add that fractional amount to t + + var t = ( i + segmentFraction ) / ( il - 1 ); + + return t; + + }, + + // Returns a unit vector tangent at t + // In case any sub curve does not implement its tangent derivation, + // 2 points a small delta apart will be used to find its gradient + // which seems to give a reasonable approximation + + getTangent: function( t ) { + + var delta = 0.0001; + var t1 = t - delta; + var t2 = t + delta; + + // Capping in case of danger + + if ( t1 < 0 ) t1 = 0; + if ( t2 > 1 ) t2 = 1; + + var pt1 = this.getPoint( t1 ); + var pt2 = this.getPoint( t2 ); + + var vec = pt2.clone().sub( pt1 ); + return vec.normalize(); + + }, + + getTangentAt: function ( u ) { + + var t = this.getUtoTmapping( u ); + return this.getTangent( t ); + + } + +} + +THREE.Curve.Utils = THREE.CurveUtils; // backwards compatibility + +// TODO: Transformation for Curves? + +/************************************************************** + * 3D Curves + **************************************************************/ + +// A Factory method for creating new curve subclasses + +THREE.Curve.create = function ( constructor, getPointFunc ) { + + constructor.prototype = Object.create( THREE.Curve.prototype ); + constructor.prototype.constructor = constructor; + constructor.prototype.getPoint = getPointFunc; + + return constructor; + +}; + +// File:src/extras/core/CurvePath.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + **/ + +/************************************************************** + * Curved Path - a curve path is simply a array of connected + * curves, but retains the api of a curve + **************************************************************/ + +THREE.CurvePath = function () { + + this.curves = []; + + this.autoClose = false; // Automatically closes the path + +}; + +THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype ); +THREE.CurvePath.prototype.constructor = THREE.CurvePath; + +THREE.CurvePath.prototype.add = function ( curve ) { + + this.curves.push( curve ); + +}; + +/* +THREE.CurvePath.prototype.checkConnection = function() { + // TODO + // If the ending of curve is not connected to the starting + // or the next curve, then, this is not a real path +}; +*/ + +THREE.CurvePath.prototype.closePath = function() { + + // TODO Test + // and verify for vector3 (needs to implement equals) + // Add a line curve if start and end of lines are not connected + var startPoint = this.curves[ 0 ].getPoint( 0 ); + var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); + + if ( ! startPoint.equals( endPoint ) ) { + + this.curves.push( new THREE.LineCurve( endPoint, startPoint ) ); + + } + +}; + +// To get accurate point with reference to +// entire path distance at time t, +// following has to be done: + +// 1. Length of each sub path have to be known +// 2. Locate and identify type of curve +// 3. Get t for the curve +// 4. Return curve.getPointAt(t') + +THREE.CurvePath.prototype.getPoint = function( t ) { + + var d = t * this.getLength(); + var curveLengths = this.getCurveLengths(); + var i = 0; + + // To think about boundaries points. + + while ( i < curveLengths.length ) { + + if ( curveLengths[ i ] >= d ) { + + var diff = curveLengths[ i ] - d; + var curve = this.curves[ i ]; + + var u = 1 - diff / curve.getLength(); + + return curve.getPointAt( u ); + + } + + i ++; + + } + + return null; + + // loop where sum != 0, sum > d , sum+1 0 ) { + + laste = points[ points.length - 1 ]; + + cpx0 = laste.x; + cpy0 = laste.y; + + } else { + + laste = this.actions[ i - 1 ].args; + + cpx0 = laste[ laste.length - 2 ]; + cpy0 = laste[ laste.length - 1 ]; + + } + + for ( var j = 1; j <= divisions; j ++ ) { + + var t = j / divisions; + + tx = b2( t, cpx0, cpx1, cpx ); + ty = b2( t, cpy0, cpy1, cpy ); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + break; + + case 'bezierCurveTo': + + cpx = args[ 4 ]; + cpy = args[ 5 ]; + + cpx1 = args[ 0 ]; + cpy1 = args[ 1 ]; + + cpx2 = args[ 2 ]; + cpy2 = args[ 3 ]; + + if ( points.length > 0 ) { + + laste = points[ points.length - 1 ]; + + cpx0 = laste.x; + cpy0 = laste.y; + + } else { + + laste = this.actions[ i - 1 ].args; + + cpx0 = laste[ laste.length - 2 ]; + cpy0 = laste[ laste.length - 1 ]; + + } + + + for ( var j = 1; j <= divisions; j ++ ) { + + var t = j / divisions; + + tx = b3( t, cpx0, cpx1, cpx2, cpx ); + ty = b3( t, cpy0, cpy1, cpy2, cpy ); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + break; + + case 'splineThru': + + laste = this.actions[ i - 1 ].args; + + var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] ); + var spts = [ last ]; + + var n = divisions * args[ 0 ].length; + + spts = spts.concat( args[ 0 ] ); + + var spline = new THREE.SplineCurve( spts ); + + for ( var j = 1; j <= n; j ++ ) { + + points.push( spline.getPointAt( j / n ) ); + + } + + break; + + case 'arc': + + var aX = args[ 0 ], aY = args[ 1 ], + aRadius = args[ 2 ], + aStartAngle = args[ 3 ], aEndAngle = args[ 4 ], + aClockwise = !! args[ 5 ]; + + var deltaAngle = aEndAngle - aStartAngle; + var angle; + var tdivisions = divisions * 2; + + for ( var j = 1; j <= tdivisions; j ++ ) { + + var t = j / tdivisions; + + if ( ! aClockwise ) { + + t = 1 - t; + + } + + angle = aStartAngle + t * deltaAngle; + + tx = aX + aRadius * Math.cos( angle ); + ty = aY + aRadius * Math.sin( angle ); + + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + //console.log(points); + + break; + + case 'ellipse': + + var aX = args[ 0 ], aY = args[ 1 ], + xRadius = args[ 2 ], + yRadius = args[ 3 ], + aStartAngle = args[ 4 ], aEndAngle = args[ 5 ], + aClockwise = !! args[ 6 ], + aRotation = args[ 7 ]; + + + var deltaAngle = aEndAngle - aStartAngle; + var angle; + var tdivisions = divisions * 2; + + var cos, sin; + if ( aRotation !== 0 ) { + + cos = Math.cos( aRotation ); + sin = Math.sin( aRotation ); + + } + + for ( var j = 1; j <= tdivisions; j ++ ) { + + var t = j / tdivisions; + + if ( ! aClockwise ) { + + t = 1 - t; + + } + + angle = aStartAngle + t * deltaAngle; + + tx = aX + xRadius * Math.cos( angle ); + ty = aY + yRadius * Math.sin( angle ); + + if ( aRotation !== 0 ) { + + var x = tx, y = ty; + + // Rotate the point about the center of the ellipse. + tx = ( x - aX ) * cos - ( y - aY ) * sin + aX; + ty = ( x - aX ) * sin + ( y - aY ) * cos + aY; + + } + + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + //console.log(points); + + break; + + } // end switch + + } + + + + // Normalize to remove the closing point by default. + var lastPoint = points[ points.length - 1 ]; + if ( Math.abs( lastPoint.x - points[ 0 ].x ) < Number.EPSILON && + Math.abs( lastPoint.y - points[ 0 ].y ) < Number.EPSILON ) + points.splice( points.length - 1, 1 ); + if ( closedPath ) { + + points.push( points[ 0 ] ); + + } + + return points; + +}; + +// +// Breaks path into shapes +// +// Assumptions (if parameter isCCW==true the opposite holds): +// - solid shapes are defined clockwise (CW) +// - holes are defined counterclockwise (CCW) +// +// If parameter noHoles==true: +// - all subPaths are regarded as solid shapes +// - definition order CW/CCW has no relevance +// + +THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { + + function extractSubpaths( inActions ) { + + var subPaths = [], lastPath = new THREE.Path(); + + for ( var i = 0, l = inActions.length; i < l; i ++ ) { + + var item = inActions[ i ]; + + var args = item.args; + var action = item.action; + + if ( action === 'moveTo' ) { + + if ( lastPath.actions.length !== 0 ) { + + subPaths.push( lastPath ); + lastPath = new THREE.Path(); + + } + + } + + lastPath[ action ].apply( lastPath, args ); + + } + + if ( lastPath.actions.length !== 0 ) { + + subPaths.push( lastPath ); + + } + + // console.log(subPaths); + + return subPaths; + + } + + function toShapesNoHoles( inSubpaths ) { + + var shapes = []; + + for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) { + + var tmpPath = inSubpaths[ i ]; + + var tmpShape = new THREE.Shape(); + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; + + shapes.push( tmpShape ); + + } + + //console.log("shape", shapes); + + return shapes; + + } + + function isPointInsidePolygon( inPt, inPolygon ) { + + var polyLen = inPolygon.length; + + // inPt on polygon contour => immediate success or + // toggling of inside/outside at every single! intersection point of an edge + // with the horizontal line through inPt, left of inPt + // not counting lowerY endpoints of edges and whole edges on that line + var inside = false; + for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { + + var edgeLowPt = inPolygon[ p ]; + var edgeHighPt = inPolygon[ q ]; + + var edgeDx = edgeHighPt.x - edgeLowPt.x; + var edgeDy = edgeHighPt.y - edgeLowPt.y; + + if ( Math.abs( edgeDy ) > Number.EPSILON ) { + + // not parallel + if ( edgeDy < 0 ) { + + edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; + edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; + + } + if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; + + if ( inPt.y === edgeLowPt.y ) { + + if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? + // continue; // no intersection or edgeLowPt => doesn't count !!! + + } else { + + var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); + if ( perpEdge === 0 ) return true; // inPt is on contour ? + if ( perpEdge < 0 ) continue; + inside = ! inside; // true intersection left of inPt + + } + + } else { + + // parallel or collinear + if ( inPt.y !== edgeLowPt.y ) continue; // parallel + // edge lies on the same horizontal line as inPt + if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || + ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! + // continue; + + } + + } + + return inside; + + } + + var isClockWise = THREE.ShapeUtils.isClockWise; + + var subPaths = extractSubpaths( this.actions ); + if ( subPaths.length === 0 ) return []; + + if ( noHoles === true ) return toShapesNoHoles( subPaths ); + + + var solid, tmpPath, tmpShape, shapes = []; + + if ( subPaths.length === 1 ) { + + tmpPath = subPaths[ 0 ]; + tmpShape = new THREE.Shape(); + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; + shapes.push( tmpShape ); + return shapes; + + } + + var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); + holesFirst = isCCW ? ! holesFirst : holesFirst; + + // console.log("Holes first", holesFirst); + + var betterShapeHoles = []; + var newShapes = []; + var newShapeHoles = []; + var mainIdx = 0; + var tmpPoints; + + newShapes[ mainIdx ] = undefined; + newShapeHoles[ mainIdx ] = []; + + for ( var i = 0, l = subPaths.length; i < l; i ++ ) { + + tmpPath = subPaths[ i ]; + tmpPoints = tmpPath.getPoints(); + solid = isClockWise( tmpPoints ); + solid = isCCW ? ! solid : solid; + + if ( solid ) { + + if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; + + newShapes[ mainIdx ] = { s: new THREE.Shape(), p: tmpPoints }; + newShapes[ mainIdx ].s.actions = tmpPath.actions; + newShapes[ mainIdx ].s.curves = tmpPath.curves; + + if ( holesFirst ) mainIdx ++; + newShapeHoles[ mainIdx ] = []; + + //console.log('cw', i); + + } else { + + newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); + + //console.log('ccw', i); + + } + + } + + // only Holes? -> probably all Shapes with wrong orientation + if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); + + + if ( newShapes.length > 1 ) { + + var ambiguous = false; + var toChange = []; + + for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + + betterShapeHoles[ sIdx ] = []; + + } + + for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + + var sho = newShapeHoles[ sIdx ]; + + for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) { + + var ho = sho[ hIdx ]; + var hole_unassigned = true; + + for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { + + if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { + + if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); + if ( hole_unassigned ) { + + hole_unassigned = false; + betterShapeHoles[ s2Idx ].push( ho ); + + } else { + + ambiguous = true; + + } + + } + + } + if ( hole_unassigned ) { + + betterShapeHoles[ sIdx ].push( ho ); + + } + + } + + } + // console.log("ambiguous: ", ambiguous); + if ( toChange.length > 0 ) { + + // console.log("to change: ", toChange); + if ( ! ambiguous ) newShapeHoles = betterShapeHoles; + + } + + } + + var tmpHoles; + + for ( var i = 0, il = newShapes.length; i < il; i ++ ) { + + tmpShape = newShapes[ i ].s; + shapes.push( tmpShape ); + tmpHoles = newShapeHoles[ i ]; + + for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) { + + tmpShape.holes.push( tmpHoles[ j ].h ); + + } + + } + + //console.log("shape", shapes); + + return shapes; + +}; + +// File:src/extras/core/Shape.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Defines a 2d shape plane using paths. + **/ + +// STEP 1 Create a path. +// STEP 2 Turn path into shape. +// STEP 3 ExtrudeGeometry takes in Shape/Shapes +// STEP 3a - Extract points from each shape, turn to vertices +// STEP 3b - Triangulate each shape, add faces. + +THREE.Shape = function () { + + THREE.Path.apply( this, arguments ); + + this.holes = []; + +}; + +THREE.Shape.prototype = Object.create( THREE.Path.prototype ); +THREE.Shape.prototype.constructor = THREE.Shape; + +// Convenience method to return ExtrudeGeometry + +THREE.Shape.prototype.extrude = function ( options ) { + + return new THREE.ExtrudeGeometry( this, options ); + +}; + +// Convenience method to return ShapeGeometry + +THREE.Shape.prototype.makeGeometry = function ( options ) { + + return new THREE.ShapeGeometry( this, options ); + +}; + +// Get points of holes + +THREE.Shape.prototype.getPointsHoles = function ( divisions ) { + + var holesPts = []; + + for ( var i = 0, l = this.holes.length; i < l; i ++ ) { + + holesPts[ i ] = this.holes[ i ].getPoints( divisions ); + + } + + return holesPts; + +}; + + +// Get points of shape and holes (keypoints based on segments parameter) + +THREE.Shape.prototype.extractAllPoints = function ( divisions ) { + + return { + + shape: this.getPoints( divisions ), + holes: this.getPointsHoles( divisions ) + + }; + +}; + +THREE.Shape.prototype.extractPoints = function ( divisions ) { + + return this.extractAllPoints( divisions ); + +}; + +THREE.Shape.Utils = THREE.ShapeUtils; // backwards compatibility + +// File:src/extras/curves/LineCurve.js + +/************************************************************** + * Line + **************************************************************/ + +THREE.LineCurve = function ( v1, v2 ) { + + this.v1 = v1; + this.v2 = v2; + +}; + +THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.LineCurve.prototype.constructor = THREE.LineCurve; + +THREE.LineCurve.prototype.getPoint = function ( t ) { + + var point = this.v2.clone().sub( this.v1 ); + point.multiplyScalar( t ).add( this.v1 ); + + return point; + +}; + +// Line curve is linear, so we can overwrite default getPointAt + +THREE.LineCurve.prototype.getPointAt = function ( u ) { + + return this.getPoint( u ); + +}; + +THREE.LineCurve.prototype.getTangent = function( t ) { + + var tangent = this.v2.clone().sub( this.v1 ); + + return tangent.normalize(); + +}; + +// File:src/extras/curves/QuadraticBezierCurve.js + +/************************************************************** + * Quadratic Bezier curve + **************************************************************/ + + +THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + +}; + +THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.QuadraticBezierCurve.prototype.constructor = THREE.QuadraticBezierCurve; + + +THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) { + + var b2 = THREE.ShapeUtils.b2; + + return new THREE.Vector2( + b2( t, this.v0.x, this.v1.x, this.v2.x ), + b2( t, this.v0.y, this.v1.y, this.v2.y ) + ); + +}; + + +THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) { + + var tangentQuadraticBezier = THREE.CurveUtils.tangentQuadraticBezier; + + return new THREE.Vector2( + tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x ), + tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y ) + ).normalize(); + +}; + +// File:src/extras/curves/CubicBezierCurve.js + +/************************************************************** + * Cubic Bezier curve + **************************************************************/ + +THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + +}; + +THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.CubicBezierCurve.prototype.constructor = THREE.CubicBezierCurve; + +THREE.CubicBezierCurve.prototype.getPoint = function ( t ) { + + var b3 = THREE.ShapeUtils.b3; + + return new THREE.Vector2( + b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), + b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ) + ); + +}; + +THREE.CubicBezierCurve.prototype.getTangent = function( t ) { + + var tangentCubicBezier = THREE.CurveUtils.tangentCubicBezier; + + return new THREE.Vector2( + tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), + tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ) + ).normalize(); + +}; + +// File:src/extras/curves/SplineCurve.js + +/************************************************************** + * Spline curve + **************************************************************/ + +THREE.SplineCurve = function ( points /* array of Vector2 */ ) { + + this.points = ( points == undefined ) ? [] : points; + +}; + +THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.SplineCurve.prototype.constructor = THREE.SplineCurve; + +THREE.SplineCurve.prototype.getPoint = function ( t ) { + + var points = this.points; + var point = ( points.length - 1 ) * t; + + var intPoint = Math.floor( point ); + var weight = point - intPoint; + + var point0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; + var point1 = points[ intPoint ]; + var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; + var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; + + var interpolate = THREE.CurveUtils.interpolate; + + return new THREE.Vector2( + interpolate( point0.x, point1.x, point2.x, point3.x, weight ), + interpolate( point0.y, point1.y, point2.y, point3.y, weight ) + ); + +}; + +// File:src/extras/curves/EllipseCurve.js + +/************************************************************** + * Ellipse curve + **************************************************************/ + +THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + + this.aX = aX; + this.aY = aY; + + this.xRadius = xRadius; + this.yRadius = yRadius; + + this.aStartAngle = aStartAngle; + this.aEndAngle = aEndAngle; + + this.aClockwise = aClockwise; + + this.aRotation = aRotation || 0; + +}; + +THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.EllipseCurve.prototype.constructor = THREE.EllipseCurve; + +THREE.EllipseCurve.prototype.getPoint = function ( t ) { + + var deltaAngle = this.aEndAngle - this.aStartAngle; + + if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2; + if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2; + + var angle; + + if ( this.aClockwise === true ) { + + angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle ); + + } else { + + angle = this.aStartAngle + t * deltaAngle; + + } + + var x = this.aX + this.xRadius * Math.cos( angle ); + var y = this.aY + this.yRadius * Math.sin( angle ); + + if ( this.aRotation !== 0 ) { + + var cos = Math.cos( this.aRotation ); + var sin = Math.sin( this.aRotation ); + + var tx = x, ty = y; + + // Rotate the point about the center of the ellipse. + x = ( tx - this.aX ) * cos - ( ty - this.aY ) * sin + this.aX; + y = ( tx - this.aX ) * sin + ( ty - this.aY ) * cos + this.aY; + + } + + return new THREE.Vector2( x, y ); + +}; + +// File:src/extras/curves/ArcCurve.js + +/************************************************************** + * Arc curve + **************************************************************/ + +THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + + THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + +}; + +THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype ); +THREE.ArcCurve.prototype.constructor = THREE.ArcCurve; + +// File:src/extras/curves/LineCurve3.js + +/************************************************************** + * Line3D + **************************************************************/ + +THREE.LineCurve3 = THREE.Curve.create( + + function ( v1, v2 ) { + + this.v1 = v1; + this.v2 = v2; + + }, + + function ( t ) { + + var vector = new THREE.Vector3(); + + vector.subVectors( this.v2, this.v1 ); // diff + vector.multiplyScalar( t ); + vector.add( this.v1 ); + + return vector; + + } + +); + +// File:src/extras/curves/QuadraticBezierCurve3.js + +/************************************************************** + * Quadratic Bezier 3D curve + **************************************************************/ + +THREE.QuadraticBezierCurve3 = THREE.Curve.create( + + function ( v0, v1, v2 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + + }, + + function ( t ) { + + var b2 = THREE.ShapeUtils.b2; + + return new THREE.Vector3( + b2( t, this.v0.x, this.v1.x, this.v2.x ), + b2( t, this.v0.y, this.v1.y, this.v2.y ), + b2( t, this.v0.z, this.v1.z, this.v2.z ) + ); + + } + +); + +// File:src/extras/curves/CubicBezierCurve3.js + +/************************************************************** + * Cubic Bezier 3D curve + **************************************************************/ + +THREE.CubicBezierCurve3 = THREE.Curve.create( + + function ( v0, v1, v2, v3 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + + }, + + function ( t ) { + + var b3 = THREE.ShapeUtils.b3; + + return new THREE.Vector3( + b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), + b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ), + b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z ) + ); + + } + +); + +// File:src/extras/curves/SplineCurve3.js + +/************************************************************** + * Spline 3D curve + **************************************************************/ + + +THREE.SplineCurve3 = THREE.Curve.create( + + function ( points /* array of Vector3 */ ) { + + console.warn( 'THREE.SplineCurve3 will be deprecated. Please use THREE.CatmullRomCurve3' ); + this.points = ( points == undefined ) ? [] : points; + + }, + + function ( t ) { + + var points = this.points; + var point = ( points.length - 1 ) * t; + + var intPoint = Math.floor( point ); + var weight = point - intPoint; + + var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ]; + var point1 = points[ intPoint ]; + var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; + var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; + + var interpolate = THREE.CurveUtils.interpolate; + + return new THREE.Vector3( + interpolate( point0.x, point1.x, point2.x, point3.x, weight ), + interpolate( point0.y, point1.y, point2.y, point3.y, weight ), + interpolate( point0.z, point1.z, point2.z, point3.z, weight ) + ); + + } + +); + +// File:src/extras/curves/CatmullRomCurve3.js + +/** + * @author zz85 https://github.com/zz85 + * + * Centripetal CatmullRom Curve - which is useful for avoiding + * cusps and self-intersections in non-uniform catmull rom curves. + * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf + * + * curve.type accepts centripetal(default), chordal and catmullrom + * curve.tension is used for catmullrom which defaults to 0.5 + */ + +THREE.CatmullRomCurve3 = ( function() { + + var + tmp = new THREE.Vector3(), + px = new CubicPoly(), + py = new CubicPoly(), + pz = new CubicPoly(); + + /* + Based on an optimized c++ solution in + - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ + - http://ideone.com/NoEbVM + + This CubicPoly class could be used for reusing some variables and calculations, + but for three.js curve use, it could be possible inlined and flatten into a single function call + which can be placed in CurveUtils. + */ + + function CubicPoly() { + + } + + /* + * Compute coefficients for a cubic polynomial + * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 + * such that + * p(0) = x0, p(1) = x1 + * and + * p'(0) = t0, p'(1) = t1. + */ + CubicPoly.prototype.init = function( x0, x1, t0, t1 ) { + + this.c0 = x0; + this.c1 = t0; + this.c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; + this.c3 = 2 * x0 - 2 * x1 + t0 + t1; + + }; + + CubicPoly.prototype.initNonuniformCatmullRom = function( x0, x1, x2, x3, dt0, dt1, dt2 ) { + + // compute tangents when parameterized in [t1,t2] + var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; + var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; + + // rescale tangents for parametrization in [0,1] + t1 *= dt1; + t2 *= dt1; + + // initCubicPoly + this.init( x1, x2, t1, t2 ); + + }; + + // standard Catmull-Rom spline: interpolate between x1 and x2 with previous/following points x1/x4 + CubicPoly.prototype.initCatmullRom = function( x0, x1, x2, x3, tension ) { + + this.init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); + + }; + + CubicPoly.prototype.calc = function( t ) { + + var t2 = t * t; + var t3 = t2 * t; + return this.c0 + this.c1 * t + this.c2 * t2 + this.c3 * t3; + + }; + + // Subclass Three.js curve + return THREE.Curve.create( + + function ( p /* array of Vector3 */ ) { + + this.points = p || []; + + }, + + function ( t ) { + + var points = this.points, + point, intPoint, weight, l; + + l = points.length; + + if ( l < 2 ) console.log( 'duh, you need at least 2 points' ); + + point = ( l - 1 ) * t; + intPoint = Math.floor( point ); + weight = point - intPoint; + + if ( weight === 0 && intPoint === l - 1 ) { + + intPoint = l - 2; + weight = 1; + + } + + var p0, p1, p2, p3; + + if ( intPoint === 0 ) { + + // extrapolate first point + tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); + p0 = tmp; + + } else { + + p0 = points[ intPoint - 1 ]; + + } + + p1 = points[ intPoint ]; + p2 = points[ intPoint + 1 ]; + + if ( intPoint + 2 < l ) { + + p3 = points[ intPoint + 2 ] + + } else { + + // extrapolate last point + tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 2 ] ); + p3 = tmp; + + } + + if ( this.type === undefined || this.type === 'centripetal' || this.type === 'chordal' ) { + + // init Centripetal / Chordal Catmull-Rom + var pow = this.type === 'chordal' ? 0.5 : 0.25; + var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); + var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); + var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); + + // safety check for repeated points + if ( dt1 < 1e-4 ) dt1 = 1.0; + if ( dt0 < 1e-4 ) dt0 = dt1; + if ( dt2 < 1e-4 ) dt2 = dt1; + + px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); + py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); + pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); + + } else if ( this.type === 'catmullrom' ) { + + var tension = this.tension !== undefined ? this.tension : 0.5; + px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, tension ); + py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, tension ); + pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, tension ); + + } + + var v = new THREE.Vector3( + px.calc( weight ), + py.calc( weight ), + pz.calc( weight ) + ); + + return v; + + } + + ); + +} )(); + +// File:src/extras/curves/ClosedSplineCurve3.js + +/************************************************************** + * Closed Spline 3D curve + **************************************************************/ + + +THREE.ClosedSplineCurve3 = THREE.Curve.create( + + function ( points /* array of Vector3 */ ) { + + this.points = ( points == undefined ) ? [] : points; + + }, + + function ( t ) { + + var points = this.points; + var point = ( points.length - 0 ) * t; // This needs to be from 0-length +1 + + var intPoint = Math.floor( point ); + var weight = point - intPoint; + + intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; + + var point0 = points[ ( intPoint - 1 ) % points.length ]; + var point1 = points[ ( intPoint ) % points.length ]; + var point2 = points[ ( intPoint + 1 ) % points.length ]; + var point3 = points[ ( intPoint + 2 ) % points.length ]; + + var interpolate = THREE.CurveUtils.interpolate; + + return new THREE.Vector3( + interpolate( point0.x, point1.x, point2.x, point3.x, weight ), + interpolate( point0.y, point1.y, point2.y, point3.y, weight ), + interpolate( point0.z, point1.z, point2.z, point3.z, weight ) + ); + + } + +); + +// File:src/extras/geometries/BoxGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as + */ + +THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) { + + THREE.Geometry.call( this ); + + this.type = 'BoxGeometry'; + + this.parameters = { + width: width, + height: height, + depth: depth, + widthSegments: widthSegments, + heightSegments: heightSegments, + depthSegments: depthSegments + }; + + this.widthSegments = widthSegments || 1; + this.heightSegments = heightSegments || 1; + this.depthSegments = depthSegments || 1; + + var scope = this; + + var width_half = width / 2; + var height_half = height / 2; + var depth_half = depth / 2; + + buildPlane( 'z', 'y', - 1, - 1, depth, height, width_half, 0 ); // px + buildPlane( 'z', 'y', 1, - 1, depth, height, - width_half, 1 ); // nx + buildPlane( 'x', 'z', 1, 1, width, depth, height_half, 2 ); // py + buildPlane( 'x', 'z', 1, - 1, width, depth, - height_half, 3 ); // ny + buildPlane( 'x', 'y', 1, - 1, width, height, depth_half, 4 ); // pz + buildPlane( 'x', 'y', - 1, - 1, width, height, - depth_half, 5 ); // nz + + function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) { + + var w, ix, iy, + gridX = scope.widthSegments, + gridY = scope.heightSegments, + width_half = width / 2, + height_half = height / 2, + offset = scope.vertices.length; + + if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) { + + w = 'z'; + + } else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) { + + w = 'y'; + gridY = scope.depthSegments; + + } else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) { + + w = 'x'; + gridX = scope.depthSegments; + + } + + var gridX1 = gridX + 1, + gridY1 = gridY + 1, + segment_width = width / gridX, + segment_height = height / gridY, + normal = new THREE.Vector3(); + + normal[ w ] = depth > 0 ? 1 : - 1; + + for ( iy = 0; iy < gridY1; iy ++ ) { + + for ( ix = 0; ix < gridX1; ix ++ ) { + + var vector = new THREE.Vector3(); + vector[ u ] = ( ix * segment_width - width_half ) * udir; + vector[ v ] = ( iy * segment_height - height_half ) * vdir; + vector[ w ] = depth; + + scope.vertices.push( vector ); + + } + + } + + for ( iy = 0; iy < gridY; iy ++ ) { + + for ( ix = 0; ix < gridX; ix ++ ) { + + var a = ix + gridX1 * iy; + var b = ix + gridX1 * ( iy + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); + var d = ( ix + 1 ) + gridX1 * iy; + + var uva = new THREE.Vector2( ix / gridX, 1 - iy / gridY ); + var uvb = new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY ); + var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iy + 1 ) / gridY ); + var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY ); + + var face = new THREE.Face3( a + offset, b + offset, d + offset ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + face.materialIndex = materialIndex; + + scope.faces.push( face ); + scope.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + face = new THREE.Face3( b + offset, c + offset, d + offset ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + face.materialIndex = materialIndex; + + scope.faces.push( face ); + scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + + } + + } + + this.mergeVertices(); + +}; + +THREE.BoxGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.BoxGeometry.prototype.constructor = THREE.BoxGeometry; + +THREE.BoxGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.BoxGeometry( + parameters.width, + parameters.height, + parameters.depth, + parameters.widthSegments, + parameters.heightSegments, + parameters.depthSegments + ); + +}; + +THREE.CubeGeometry = THREE.BoxGeometry; // backwards compatibility + +// File:src/extras/geometries/CircleGeometry.js + +/** + * @author hughes + */ + +THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + this.type = 'CircleGeometry'; + + this.parameters = { + radius: radius, + segments: segments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + this.fromBufferGeometry( new THREE.CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) ); + +}; + +THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.CircleGeometry.prototype.constructor = THREE.CircleGeometry; + +THREE.CircleGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.CircleGeometry( + parameters.radius, + parameters.segments, + parameters.thetaStart, + parameters.thetaLength + ); + +}; + +// File:src/extras/geometries/CircleBufferGeometry.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.CircleBufferGeometry = function ( radius, segments, thetaStart, thetaLength ) { + + THREE.BufferGeometry.call( this ); + + this.type = 'CircleBufferGeometry'; + + this.parameters = { + radius: radius, + segments: segments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + radius = radius || 50; + segments = segments !== undefined ? Math.max( 3, segments ) : 8; + + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + + var vertices = segments + 2; + + var positions = new Float32Array( vertices * 3 ); + var normals = new Float32Array( vertices * 3 ); + var uvs = new Float32Array( vertices * 2 ); + + // center data is already zero, but need to set a few extras + normals[ 2 ] = 1.0; + uvs[ 0 ] = 0.5; + uvs[ 1 ] = 0.5; + + for ( var s = 0, i = 3, ii = 2 ; s <= segments; s ++, i += 3, ii += 2 ) { + + var segment = thetaStart + s / segments * thetaLength; + + positions[ i ] = radius * Math.cos( segment ); + positions[ i + 1 ] = radius * Math.sin( segment ); + + normals[ i + 2 ] = 1; // normal z + + uvs[ ii ] = ( positions[ i ] / radius + 1 ) / 2; + uvs[ ii + 1 ] = ( positions[ i + 1 ] / radius + 1 ) / 2; + + } + + var indices = []; + + for ( var i = 1; i <= segments; i ++ ) { + + indices.push( i, i + 1, 0 ); + + } + + this.setIndex( new THREE.BufferAttribute( new Uint16Array( indices ), 1 ) ); + this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + +}; + +THREE.CircleBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.CircleBufferGeometry.prototype.constructor = THREE.CircleBufferGeometry; + +THREE.CircleBufferGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.CircleBufferGeometry( + parameters.radius, + parameters.segments, + parameters.thetaStart, + parameters.thetaLength + ); + +}; + +// File:src/extras/geometries/CylinderGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + this.type = 'CylinderGeometry'; + + this.parameters = { + radiusTop: radiusTop, + radiusBottom: radiusBottom, + height: height, + radialSegments: radialSegments, + heightSegments: heightSegments, + openEnded: openEnded, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + radiusTop = radiusTop !== undefined ? radiusTop : 20; + radiusBottom = radiusBottom !== undefined ? radiusBottom : 20; + height = height !== undefined ? height : 100; + + radialSegments = radialSegments || 8; + heightSegments = heightSegments || 1; + + openEnded = openEnded !== undefined ? openEnded : false; + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : 2 * Math.PI; + + var heightHalf = height / 2; + + var x, y, vertices = [], uvs = []; + + for ( y = 0; y <= heightSegments; y ++ ) { + + var verticesRow = []; + var uvsRow = []; + + var v = y / heightSegments; + var radius = v * ( radiusBottom - radiusTop ) + radiusTop; + + for ( x = 0; x <= radialSegments; x ++ ) { + + var u = x / radialSegments; + + var vertex = new THREE.Vector3(); + vertex.x = radius * Math.sin( u * thetaLength + thetaStart ); + vertex.y = - v * height + heightHalf; + vertex.z = radius * Math.cos( u * thetaLength + thetaStart ); + + this.vertices.push( vertex ); + + verticesRow.push( this.vertices.length - 1 ); + uvsRow.push( new THREE.Vector2( u, 1 - v ) ); + + } + + vertices.push( verticesRow ); + uvs.push( uvsRow ); + + } + + var tanTheta = ( radiusBottom - radiusTop ) / height; + var na, nb; + + for ( x = 0; x < radialSegments; x ++ ) { + + if ( radiusTop !== 0 ) { + + na = this.vertices[ vertices[ 0 ][ x ] ].clone(); + nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone(); + + } else { + + na = this.vertices[ vertices[ 1 ][ x ] ].clone(); + nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone(); + + } + + na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize(); + nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize(); + + for ( y = 0; y < heightSegments; y ++ ) { + + var v1 = vertices[ y ][ x ]; + var v2 = vertices[ y + 1 ][ x ]; + var v3 = vertices[ y + 1 ][ x + 1 ]; + var v4 = vertices[ y ][ x + 1 ]; + + var n1 = na.clone(); + var n2 = na.clone(); + var n3 = nb.clone(); + var n4 = nb.clone(); + + var uv1 = uvs[ y ][ x ].clone(); + var uv2 = uvs[ y + 1 ][ x ].clone(); + var uv3 = uvs[ y + 1 ][ x + 1 ].clone(); + var uv4 = uvs[ y ][ x + 1 ].clone(); + + this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] ); + + this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] ); + + } + + } + + // top cap + + if ( openEnded === false && radiusTop > 0 ) { + + this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) ); + + for ( x = 0; x < radialSegments; x ++ ) { + + var v1 = vertices[ 0 ][ x ]; + var v2 = vertices[ 0 ][ x + 1 ]; + var v3 = this.vertices.length - 1; + + var n1 = new THREE.Vector3( 0, 1, 0 ); + var n2 = new THREE.Vector3( 0, 1, 0 ); + var n3 = new THREE.Vector3( 0, 1, 0 ); + + var uv1 = uvs[ 0 ][ x ].clone(); + var uv2 = uvs[ 0 ][ x + 1 ].clone(); + var uv3 = new THREE.Vector2( uv2.x, 0 ); + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ], undefined, 1 ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + + } + + } + + // bottom cap + + if ( openEnded === false && radiusBottom > 0 ) { + + this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) ); + + for ( x = 0; x < radialSegments; x ++ ) { + + var v1 = vertices[ heightSegments ][ x + 1 ]; + var v2 = vertices[ heightSegments ][ x ]; + var v3 = this.vertices.length - 1; + + var n1 = new THREE.Vector3( 0, - 1, 0 ); + var n2 = new THREE.Vector3( 0, - 1, 0 ); + var n3 = new THREE.Vector3( 0, - 1, 0 ); + + var uv1 = uvs[ heightSegments ][ x + 1 ].clone(); + var uv2 = uvs[ heightSegments ][ x ].clone(); + var uv3 = new THREE.Vector2( uv2.x, 1 ); + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ], undefined, 2 ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + + } + + } + + this.computeFaceNormals(); + +}; + +THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.CylinderGeometry.prototype.constructor = THREE.CylinderGeometry; + +THREE.CylinderGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.CylinderGeometry( + parameters.radiusTop, + parameters.radiusBottom, + parameters.height, + parameters.radialSegments, + parameters.heightSegments, + parameters.openEnded, + parameters.thetaStart, + parameters.thetaLength + ); + +}; + +// File:src/extras/geometries/EdgesGeometry.js + +/** + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.EdgesGeometry = function ( geometry, thresholdAngle ) { + + THREE.BufferGeometry.call( this ); + + thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; + + var thresholdDot = Math.cos( THREE.Math.degToRad( thresholdAngle ) ); + + var edge = [ 0, 0 ], hash = {}; + + function sortFunction( a, b ) { + + return a - b; + + } + + var keys = [ 'a', 'b', 'c' ]; + + var geometry2; + + if ( geometry instanceof THREE.BufferGeometry ) { + + geometry2 = new THREE.Geometry(); + geometry2.fromBufferGeometry( geometry ); + + } else { + + geometry2 = geometry.clone(); + + } + + geometry2.mergeVertices(); + geometry2.computeFaceNormals(); + + var vertices = geometry2.vertices; + var faces = geometry2.faces; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0; j < 3; j ++ ) { + + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); + + var key = edge.toString(); + + if ( hash[ key ] === undefined ) { + + hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined }; + + } else { + + hash[ key ].face2 = i; + + } + + } + + } + + var coords = []; + + for ( var key in hash ) { + + var h = hash[ key ]; + + if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) <= thresholdDot ) { + + var vertex = vertices[ h.vert1 ]; + coords.push( vertex.x ); + coords.push( vertex.y ); + coords.push( vertex.z ); + + vertex = vertices[ h.vert2 ]; + coords.push( vertex.x ); + coords.push( vertex.y ); + coords.push( vertex.z ); + + } + + } + + this.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( coords ), 3 ) ); + +}; + +THREE.EdgesGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.EdgesGeometry.prototype.constructor = THREE.EdgesGeometry; + +// File:src/extras/geometries/ExtrudeGeometry.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + * Creates extruded geometry from a path shape. + * + * parameters = { + * + * curveSegments: , // number of points on the curves + * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too + * amount: , // Depth to extrude the shape + * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into the original shape bevel goes + * bevelSize: , // how far from shape outline is bevel + * bevelSegments: , // number of bevel layers + * + * extrudePath: // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined) + * frames: // containing arrays of tangents, normals, binormals + * + * uvGenerator: // object that provides UV generator functions + * + * } + **/ + +THREE.ExtrudeGeometry = function ( shapes, options ) { + + if ( typeof( shapes ) === "undefined" ) { + + shapes = []; + return; + + } + + THREE.Geometry.call( this ); + + this.type = 'ExtrudeGeometry'; + + shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; + + this.addShapeList( shapes, options ); + + this.computeFaceNormals(); + + // can't really use automatic vertex normals + // as then front and back sides get smoothed too + // should do separate smoothing just for sides + + //this.computeVertexNormals(); + + //console.log( "took", ( Date.now() - startTime ) ); + +}; + +THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ExtrudeGeometry.prototype.constructor = THREE.ExtrudeGeometry; + +THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) { + + var sl = shapes.length; + + for ( var s = 0; s < sl; s ++ ) { + + var shape = shapes[ s ]; + this.addShape( shape, options ); + + } + +}; + +THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { + + var amount = options.amount !== undefined ? options.amount : 100; + + var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10 + var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8 + var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; + + var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false + + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + + var steps = options.steps !== undefined ? options.steps : 1; + + var extrudePath = options.extrudePath; + var extrudePts, extrudeByPath = false; + + // Use default WorldUVGenerator if no UV generators are specified. + var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator; + + var splineTube, binormal, normal, position2; + if ( extrudePath ) { + + extrudePts = extrudePath.getSpacedPoints( steps ); + + extrudeByPath = true; + bevelEnabled = false; // bevels not supported for path extrusion + + // SETUP TNB variables + + // Reuse TNB from TubeGeomtry for now. + // TODO1 - have a .isClosed in spline? + + splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames( extrudePath, steps, false ); + + // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); + + binormal = new THREE.Vector3(); + normal = new THREE.Vector3(); + position2 = new THREE.Vector3(); + + } + + // Safeguards if bevels are not enabled + + if ( ! bevelEnabled ) { + + bevelSegments = 0; + bevelThickness = 0; + bevelSize = 0; + + } + + // Variables initialization + + var ahole, h, hl; // looping of holes + var scope = this; + + var shapesOffset = this.vertices.length; + + var shapePoints = shape.extractPoints( curveSegments ); + + var vertices = shapePoints.shape; + var holes = shapePoints.holes; + + var reverse = ! THREE.ShapeUtils.isClockWise( vertices ); + + if ( reverse ) { + + vertices = vertices.reverse(); + + // Maybe we should also check if holes are in the opposite direction, just to be safe ... + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + if ( THREE.ShapeUtils.isClockWise( ahole ) ) { + + holes[ h ] = ahole.reverse(); + + } + + } + + reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! + + } + + + var faces = THREE.ShapeUtils.triangulateShape( vertices, holes ); + + /* Vertices */ + + var contour = vertices; // vertices has all points but contour has only points of circumference + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + vertices = vertices.concat( ahole ); + + } + + + function scalePt2 ( pt, vec, size ) { + + if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); + + return vec.clone().multiplyScalar( size ).add( pt ); + + } + + var b, bs, t, z, + vert, vlen = vertices.length, + face, flen = faces.length; + + + // Find directions for point movement + + + function getBevelVec( inPt, inPrev, inNext ) { + + // computes for inPt the corresponding point inPt' on a new contour + // shifted by 1 unit (length of normalized vector) to the left + // if we walk along contour clockwise, this new contour is outside the old one + // + // inPt' is the intersection of the two lines parallel to the two + // adjacent edges of inPt at a distance of 1 unit on the left side. + + var v_trans_x, v_trans_y, shrink_by = 1; // resulting translation vector for inPt + + // good reading for geometry algorithms (here: line-line intersection) + // http://geomalgorithms.com/a05-_intersect-1.html + + var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; + var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; + + var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); + + // check for collinear edges + var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + + if ( Math.abs( collinear0 ) > Number.EPSILON ) { + + // not collinear + + // length of vectors for normalizing + + var v_prev_len = Math.sqrt( v_prev_lensq ); + var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); + + // shift adjacent points by unit vectors to the left + + var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); + var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); + + var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); + var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); + + // scaling factor for v_prev to intersection point + + var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - + ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / + ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + + // vector from inPt to intersection point + + v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); + v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); + + // Don't normalize!, otherwise sharp corners become ugly + // but prevent crazy spikes + var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); + if ( v_trans_lensq <= 2 ) { + + return new THREE.Vector2( v_trans_x, v_trans_y ); + + } else { + + shrink_by = Math.sqrt( v_trans_lensq / 2 ); + + } + + } else { + + // handle special case of collinear edges + + var direction_eq = false; // assumes: opposite + if ( v_prev_x > Number.EPSILON ) { + + if ( v_next_x > Number.EPSILON ) { + + direction_eq = true; + + } + + } else { + + if ( v_prev_x < - Number.EPSILON ) { + + if ( v_next_x < - Number.EPSILON ) { + + direction_eq = true; + + } + + } else { + + if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { + + direction_eq = true; + + } + + } + + } + + if ( direction_eq ) { + + // console.log("Warning: lines are a straight sequence"); + v_trans_x = - v_prev_y; + v_trans_y = v_prev_x; + shrink_by = Math.sqrt( v_prev_lensq ); + + } else { + + // console.log("Warning: lines are a straight spike"); + v_trans_x = v_prev_x; + v_trans_y = v_prev_y; + shrink_by = Math.sqrt( v_prev_lensq / 2 ); + + } + + } + + return new THREE.Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); + + } + + + var contourMovements = []; + + for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + + if ( j === il ) j = 0; + if ( k === il ) k = 0; + + // (j)---(i)---(k) + // console.log('i,j,k', i, j , k) + + contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); + + } + + var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + oneHoleMovements = []; + + for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + + if ( j === il ) j = 0; + if ( k === il ) k = 0; + + // (j)---(i)---(k) + oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); + + } + + holesMovements.push( oneHoleMovements ); + verticesMovements = verticesMovements.concat( oneHoleMovements ); + + } + + + // Loop bevelSegments, 1 for the front, 1 for the back + + for ( b = 0; b < bevelSegments; b ++ ) { + + //for ( b = bevelSegments; b > 0; b -- ) { + + t = b / bevelSegments; + z = bevelThickness * ( 1 - t ); + + //z = bevelThickness * t; + bs = bevelSize * ( Math.sin ( t * Math.PI / 2 ) ); // curved + //bs = bevelSize * t; // linear + + // contract shape + + for ( i = 0, il = contour.length; i < il; i ++ ) { + + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + + v( vert.x, vert.y, - z ); + + } + + // expand holes + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; + + for ( i = 0, il = ahole.length; i < il; i ++ ) { + + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + + v( vert.x, vert.y, - z ); + + } + + } + + } + + bs = bevelSize; + + // Back facing vertices + + for ( i = 0; i < vlen; i ++ ) { + + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + + if ( ! extrudeByPath ) { + + v( vert.x, vert.y, 0 ); + + } else { + + // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); + + normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); + + position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); + + v( position2.x, position2.y, position2.z ); + + } + + } + + // Add stepped vertices... + // Including front facing vertices + + var s; + + for ( s = 1; s <= steps; s ++ ) { + + for ( i = 0; i < vlen; i ++ ) { + + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + + if ( ! extrudeByPath ) { + + v( vert.x, vert.y, amount / steps * s ); + + } else { + + // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); + + normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); + + position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); + + v( position2.x, position2.y, position2.z ); + + } + + } + + } + + + // Add bevel segments planes + + //for ( b = 1; b <= bevelSegments; b ++ ) { + for ( b = bevelSegments - 1; b >= 0; b -- ) { + + t = b / bevelSegments; + z = bevelThickness * ( 1 - t ); + //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) ); + bs = bevelSize * Math.sin ( t * Math.PI / 2 ); + + // contract shape + + for ( i = 0, il = contour.length; i < il; i ++ ) { + + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + v( vert.x, vert.y, amount + z ); + + } + + // expand holes + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; + + for ( i = 0, il = ahole.length; i < il; i ++ ) { + + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + + if ( ! extrudeByPath ) { + + v( vert.x, vert.y, amount + z ); + + } else { + + v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); + + } + + } + + } + + } + + /* Faces */ + + // Top and bottom faces + + buildLidFaces(); + + // Sides faces + + buildSideFaces(); + + + ///// Internal functions + + function buildLidFaces() { + + if ( bevelEnabled ) { + + var layer = 0; // steps + 1 + var offset = vlen * layer; + + // Bottom faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); + + } + + layer = steps + bevelSegments * 2; + offset = vlen * layer; + + // Top faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); + + } + + } else { + + // Bottom faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 2 ], face[ 1 ], face[ 0 ] ); + + } + + // Top faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); + + } + + } + + } + + // Create faces for the z-sides of the shape + + function buildSideFaces() { + + var layeroffset = 0; + sidewalls( contour, layeroffset ); + layeroffset += contour.length; + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + sidewalls( ahole, layeroffset ); + + //, true + layeroffset += ahole.length; + + } + + } + + function sidewalls( contour, layeroffset ) { + + var j, k; + i = contour.length; + + while ( -- i >= 0 ) { + + j = i; + k = i - 1; + if ( k < 0 ) k = contour.length - 1; + + //console.log('b', i,j, i-1, k,vertices.length); + + var s = 0, sl = steps + bevelSegments * 2; + + for ( s = 0; s < sl; s ++ ) { + + var slen1 = vlen * s; + var slen2 = vlen * ( s + 1 ); + + var a = layeroffset + j + slen1, + b = layeroffset + k + slen1, + c = layeroffset + k + slen2, + d = layeroffset + j + slen2; + + f4( a, b, c, d, contour, s, sl, j, k ); + + } + + } + + } + + + function v( x, y, z ) { + + scope.vertices.push( new THREE.Vector3( x, y, z ) ); + + } + + function f3( a, b, c ) { + + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; + + scope.faces.push( new THREE.Face3( a, b, c, null, null, 0 ) ); + + var uvs = uvgen.generateTopUV( scope, a, b, c ); + + scope.faceVertexUvs[ 0 ].push( uvs ); + + } + + function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) { + + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; + d += shapesOffset; + + scope.faces.push( new THREE.Face3( a, b, d, null, null, 1 ) ); + scope.faces.push( new THREE.Face3( b, c, d, null, null, 1 ) ); + + var uvs = uvgen.generateSideWallUV( scope, a, b, c, d ); + + scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] ); + scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] ); + + } + +}; + +THREE.ExtrudeGeometry.WorldUVGenerator = { + + generateTopUV: function ( geometry, indexA, indexB, indexC ) { + + var vertices = geometry.vertices; + + var a = vertices[ indexA ]; + var b = vertices[ indexB ]; + var c = vertices[ indexC ]; + + return [ + new THREE.Vector2( a.x, a.y ), + new THREE.Vector2( b.x, b.y ), + new THREE.Vector2( c.x, c.y ) + ]; + + }, + + generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) { + + var vertices = geometry.vertices; + + var a = vertices[ indexA ]; + var b = vertices[ indexB ]; + var c = vertices[ indexC ]; + var d = vertices[ indexD ]; + + if ( Math.abs( a.y - b.y ) < 0.01 ) { + + return [ + new THREE.Vector2( a.x, 1 - a.z ), + new THREE.Vector2( b.x, 1 - b.z ), + new THREE.Vector2( c.x, 1 - c.z ), + new THREE.Vector2( d.x, 1 - d.z ) + ]; + + } else { + + return [ + new THREE.Vector2( a.y, 1 - a.z ), + new THREE.Vector2( b.y, 1 - b.z ), + new THREE.Vector2( c.y, 1 - c.z ), + new THREE.Vector2( d.y, 1 - d.z ) + ]; + + } + + } +}; + +// File:src/extras/geometries/ShapeGeometry.js + +/** + * @author jonobr1 / http://jonobr1.com + * + * Creates a one-sided polygonal geometry from a path shape. Similar to + * ExtrudeGeometry. + * + * parameters = { + * + * curveSegments: , // number of points on the curves. NOT USED AT THE MOMENT. + * + * material: // material index for front and back faces + * uvGenerator: // object that provides UV generator functions + * + * } + **/ + +THREE.ShapeGeometry = function ( shapes, options ) { + + THREE.Geometry.call( this ); + + this.type = 'ShapeGeometry'; + + if ( Array.isArray( shapes ) === false ) shapes = [ shapes ]; + + this.addShapeList( shapes, options ); + + this.computeFaceNormals(); + +}; + +THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ShapeGeometry.prototype.constructor = THREE.ShapeGeometry; + +/** + * Add an array of shapes to THREE.ShapeGeometry. + */ +THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) { + + for ( var i = 0, l = shapes.length; i < l; i ++ ) { + + this.addShape( shapes[ i ], options ); + + } + + return this; + +}; + +/** + * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry. + */ +THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { + + if ( options === undefined ) options = {}; + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + + var material = options.material; + var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator; + + // + + var i, l, hole; + + var shapesOffset = this.vertices.length; + var shapePoints = shape.extractPoints( curveSegments ); + + var vertices = shapePoints.shape; + var holes = shapePoints.holes; + + var reverse = ! THREE.ShapeUtils.isClockWise( vertices ); + + if ( reverse ) { + + vertices = vertices.reverse(); + + // Maybe we should also check if holes are in the opposite direction, just to be safe... + + for ( i = 0, l = holes.length; i < l; i ++ ) { + + hole = holes[ i ]; + + if ( THREE.ShapeUtils.isClockWise( hole ) ) { + + holes[ i ] = hole.reverse(); + + } + + } + + reverse = false; + + } + + var faces = THREE.ShapeUtils.triangulateShape( vertices, holes ); + + // Vertices + + for ( i = 0, l = holes.length; i < l; i ++ ) { + + hole = holes[ i ]; + vertices = vertices.concat( hole ); + + } + + // + + var vert, vlen = vertices.length; + var face, flen = faces.length; + + for ( i = 0; i < vlen; i ++ ) { + + vert = vertices[ i ]; + + this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) ); + + } + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + + var a = face[ 0 ] + shapesOffset; + var b = face[ 1 ] + shapesOffset; + var c = face[ 2 ] + shapesOffset; + + this.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); + this.faceVertexUvs[ 0 ].push( uvgen.generateTopUV( this, a, b, c ) ); + + } + +}; + +// File:src/extras/geometries/LatheGeometry.js + +/** + * @author astrodud / http://astrodud.isgreat.org/ + * @author zz85 / https://github.com/zz85 + * @author bhouston / http://clara.io + */ + +// points - to create a closed torus, one must use a set of points +// like so: [ a, b, c, d, a ], see first is the same as last. +// segments - the number of circumference segments to create +// phiStart - the starting radian +// phiLength - the radian (0 to 2*PI) range of the lathed section +// 2*pi is a closed lathe, less than 2PI is a portion. + +THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) { + + THREE.Geometry.call( this ); + + this.type = 'LatheGeometry'; + + this.parameters = { + points: points, + segments: segments, + phiStart: phiStart, + phiLength: phiLength + }; + + segments = segments || 12; + phiStart = phiStart || 0; + phiLength = phiLength || 2 * Math.PI; + + var inversePointLength = 1.0 / ( points.length - 1 ); + var inverseSegments = 1.0 / segments; + + for ( var i = 0, il = segments; i <= il; i ++ ) { + + var phi = phiStart + i * inverseSegments * phiLength; + + var c = Math.cos( phi ), + s = Math.sin( phi ); + + for ( var j = 0, jl = points.length; j < jl; j ++ ) { + + var pt = points[ j ]; + + var vertex = new THREE.Vector3(); + + vertex.x = c * pt.x - s * pt.y; + vertex.y = s * pt.x + c * pt.y; + vertex.z = pt.z; + + this.vertices.push( vertex ); + + } + + } + + var np = points.length; + + for ( var i = 0, il = segments; i < il; i ++ ) { + + for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) { + + var base = j + np * i; + var a = base; + var b = base + np; + var c = base + 1 + np; + var d = base + 1; + + var u0 = i * inverseSegments; + var v0 = j * inversePointLength; + var u1 = u0 + inverseSegments; + var v1 = v0 + inversePointLength; + + this.faces.push( new THREE.Face3( a, b, d ) ); + + this.faceVertexUvs[ 0 ].push( [ + + new THREE.Vector2( u0, v0 ), + new THREE.Vector2( u1, v0 ), + new THREE.Vector2( u0, v1 ) + + ] ); + + this.faces.push( new THREE.Face3( b, c, d ) ); + + this.faceVertexUvs[ 0 ].push( [ + + new THREE.Vector2( u1, v0 ), + new THREE.Vector2( u1, v1 ), + new THREE.Vector2( u0, v1 ) + + ] ); + + + } + + } + + this.mergeVertices(); + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.LatheGeometry.prototype.constructor = THREE.LatheGeometry; + +// File:src/extras/geometries/PlaneGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as + */ + +THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) { + + THREE.Geometry.call( this ); + + this.type = 'PlaneGeometry'; + + this.parameters = { + width: width, + height: height, + widthSegments: widthSegments, + heightSegments: heightSegments + }; + + this.fromBufferGeometry( new THREE.PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); + +}; + +THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.PlaneGeometry.prototype.constructor = THREE.PlaneGeometry; + +THREE.PlaneGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.PlaneGeometry( + parameters.width, + parameters.height, + parameters.widthSegments, + parameters.heightSegments + ); + +}; + +// File:src/extras/geometries/PlaneBufferGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as + */ + +THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegments ) { + + THREE.BufferGeometry.call( this ); + + this.type = 'PlaneBufferGeometry'; + + this.parameters = { + width: width, + height: height, + widthSegments: widthSegments, + heightSegments: heightSegments + }; + + var width_half = width / 2; + var height_half = height / 2; + + var gridX = Math.floor( widthSegments ) || 1; + var gridY = Math.floor( heightSegments ) || 1; + + var gridX1 = gridX + 1; + var gridY1 = gridY + 1; + + var segment_width = width / gridX; + var segment_height = height / gridY; + + var vertices = new Float32Array( gridX1 * gridY1 * 3 ); + var normals = new Float32Array( gridX1 * gridY1 * 3 ); + var uvs = new Float32Array( gridX1 * gridY1 * 2 ); + + var offset = 0; + var offset2 = 0; + + for ( var iy = 0; iy < gridY1; iy ++ ) { + + var y = iy * segment_height - height_half; + + for ( var ix = 0; ix < gridX1; ix ++ ) { + + var x = ix * segment_width - width_half; + + vertices[ offset ] = x; + vertices[ offset + 1 ] = - y; + + normals[ offset + 2 ] = 1; + + uvs[ offset2 ] = ix / gridX; + uvs[ offset2 + 1 ] = 1 - ( iy / gridY ); + + offset += 3; + offset2 += 2; + + } + + } + + offset = 0; + + var indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 ); + + for ( var iy = 0; iy < gridY; iy ++ ) { + + for ( var ix = 0; ix < gridX; ix ++ ) { + + var a = ix + gridX1 * iy; + var b = ix + gridX1 * ( iy + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); + var d = ( ix + 1 ) + gridX1 * iy; + + indices[ offset ] = a; + indices[ offset + 1 ] = b; + indices[ offset + 2 ] = d; + + indices[ offset + 3 ] = b; + indices[ offset + 4 ] = c; + indices[ offset + 5 ] = d; + + offset += 6; + + } + + } + + this.setIndex( new THREE.BufferAttribute( indices, 1 ) ); + this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + +}; + +THREE.PlaneBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.PlaneBufferGeometry.prototype.constructor = THREE.PlaneBufferGeometry; + +THREE.PlaneBufferGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.PlaneBufferGeometry( + parameters.width, + parameters.height, + parameters.widthSegments, + parameters.heightSegments + ); + +}; + +// File:src/extras/geometries/RingGeometry.js + +/** + * @author Kaleb Murphy + */ + +THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + this.type = 'RingGeometry'; + + this.parameters = { + innerRadius: innerRadius, + outerRadius: outerRadius, + thetaSegments: thetaSegments, + phiSegments: phiSegments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + innerRadius = innerRadius || 0; + outerRadius = outerRadius || 50; + + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + + thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; + phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 8; + + var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); + + for ( i = 0; i < phiSegments + 1; i ++ ) { + + // concentric circles inside ring + + for ( o = 0; o < thetaSegments + 1; o ++ ) { + + // number of segments per circle + + var vertex = new THREE.Vector3(); + var segment = thetaStart + o / thetaSegments * thetaLength; + vertex.x = radius * Math.cos( segment ); + vertex.y = radius * Math.sin( segment ); + + this.vertices.push( vertex ); + uvs.push( new THREE.Vector2( ( vertex.x / outerRadius + 1 ) / 2, ( vertex.y / outerRadius + 1 ) / 2 ) ); + + } + + radius += radiusStep; + + } + + var n = new THREE.Vector3( 0, 0, 1 ); + + for ( i = 0; i < phiSegments; i ++ ) { + + // concentric circles inside ring + + var thetaSegment = i * ( thetaSegments + 1 ); + + for ( o = 0; o < thetaSegments ; o ++ ) { + + // number of segments per circle + + var segment = o + thetaSegment; + + var v1 = segment; + var v2 = segment + thetaSegments + 1; + var v3 = segment + thetaSegments + 2; + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ] ); + + v1 = segment; + v2 = segment + thetaSegments + 2; + v3 = segment + 1; + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ] ); + + } + + } + + this.computeFaceNormals(); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + +}; + +THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.RingGeometry.prototype.constructor = THREE.RingGeometry; + +THREE.RingGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.RingGeometry( + parameters.innerRadius, + parameters.outerRadius, + parameters.thetaSegments, + parameters.phiSegments, + parameters.thetaStart, + parameters.thetaLength + ); + +}; + +// File:src/extras/geometries/SphereGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + this.type = 'SphereGeometry'; + + this.parameters = { + radius: radius, + widthSegments: widthSegments, + heightSegments: heightSegments, + phiStart: phiStart, + phiLength: phiLength, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + this.fromBufferGeometry( new THREE.SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) ); + +}; + +THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.SphereGeometry.prototype.constructor = THREE.SphereGeometry; + +THREE.SphereGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.SphereGeometry( + parameters.radius, + parameters.widthSegments, + parameters.heightSegments, + parameters.phiStart, + parameters.phiLength, + parameters.thetaStart, + parameters.thetaLength + ); + +}; + +// File:src/extras/geometries/SphereBufferGeometry.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + * based on THREE.SphereGeometry + */ + +THREE.SphereBufferGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { + + THREE.BufferGeometry.call( this ); + + this.type = 'SphereBufferGeometry'; + + this.parameters = { + radius: radius, + widthSegments: widthSegments, + heightSegments: heightSegments, + phiStart: phiStart, + phiLength: phiLength, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + radius = radius || 50; + + widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); + heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); + + phiStart = phiStart !== undefined ? phiStart : 0; + phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; + + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; + + var thetaEnd = thetaStart + thetaLength; + + var vertexCount = ( ( widthSegments + 1 ) * ( heightSegments + 1 ) ); + + var positions = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var normals = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var uvs = new THREE.BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); + + var index = 0, vertices = [], normal = new THREE.Vector3(); + + for ( var y = 0; y <= heightSegments; y ++ ) { + + var verticesRow = []; + + var v = y / heightSegments; + + for ( var x = 0; x <= widthSegments; x ++ ) { + + var u = x / widthSegments; + + var px = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + var py = radius * Math.cos( thetaStart + v * thetaLength ); + var pz = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + + normal.set( px, py, pz ).normalize(); + + positions.setXYZ( index, px, py, pz ); + normals.setXYZ( index, normal.x, normal.y, normal.z ); + uvs.setXY( index, u, 1 - v ); + + verticesRow.push( index ); + + index ++; + + } + + vertices.push( verticesRow ); + + } + + var indices = []; + + for ( var y = 0; y < heightSegments; y ++ ) { + + for ( var x = 0; x < widthSegments; x ++ ) { + + var v1 = vertices[ y ][ x + 1 ]; + var v2 = vertices[ y ][ x ]; + var v3 = vertices[ y + 1 ][ x ]; + var v4 = vertices[ y + 1 ][ x + 1 ]; + + if ( y !== 0 || thetaStart > 0 ) indices.push( v1, v2, v4 ); + if ( y !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( v2, v3, v4 ); + + } + + } + + this.setIndex( new ( positions.count > 65535 ? THREE.Uint32Attribute : THREE.Uint16Attribute )( indices, 1 ) ); + this.addAttribute( 'position', positions ); + this.addAttribute( 'normal', normals ); + this.addAttribute( 'uv', uvs ); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + +}; + +THREE.SphereBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.SphereBufferGeometry.prototype.constructor = THREE.SphereBufferGeometry; + +THREE.SphereBufferGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.SphereBufferGeometry( + parameters.radius, + parameters.widthSegments, + parameters.heightSegments, + parameters.phiStart, + parameters.phiLength, + parameters.thetaStart, + parameters.thetaLength + ); + +}; + +// File:src/extras/geometries/TorusGeometry.js + +/** + * @author oosmoxiecode + * @author mrdoob / http://mrdoob.com/ + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888 + */ + +THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) { + + THREE.Geometry.call( this ); + + this.type = 'TorusGeometry'; + + this.parameters = { + radius: radius, + tube: tube, + radialSegments: radialSegments, + tubularSegments: tubularSegments, + arc: arc + }; + + radius = radius || 100; + tube = tube || 40; + radialSegments = radialSegments || 8; + tubularSegments = tubularSegments || 6; + arc = arc || Math.PI * 2; + + var center = new THREE.Vector3(), uvs = [], normals = []; + + for ( var j = 0; j <= radialSegments; j ++ ) { + + for ( var i = 0; i <= tubularSegments; i ++ ) { + + var u = i / tubularSegments * arc; + var v = j / radialSegments * Math.PI * 2; + + center.x = radius * Math.cos( u ); + center.y = radius * Math.sin( u ); + + var vertex = new THREE.Vector3(); + vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); + vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); + vertex.z = tube * Math.sin( v ); + + this.vertices.push( vertex ); + + uvs.push( new THREE.Vector2( i / tubularSegments, j / radialSegments ) ); + normals.push( vertex.clone().sub( center ).normalize() ); + + } + + } + + for ( var j = 1; j <= radialSegments; j ++ ) { + + for ( var i = 1; i <= tubularSegments; i ++ ) { + + var a = ( tubularSegments + 1 ) * j + i - 1; + var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; + var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; + var d = ( tubularSegments + 1 ) * j + i; + + var face = new THREE.Face3( a, b, d, [ normals[ a ].clone(), normals[ b ].clone(), normals[ d ].clone() ] ); + this.faces.push( face ); + this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ d ].clone() ] ); + + face = new THREE.Face3( b, c, d, [ normals[ b ].clone(), normals[ c ].clone(), normals[ d ].clone() ] ); + this.faces.push( face ); + this.faceVertexUvs[ 0 ].push( [ uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] ); + + } + + } + + this.computeFaceNormals(); + +}; + +THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.TorusGeometry.prototype.constructor = THREE.TorusGeometry; + +THREE.TorusGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.TorusGeometry( + parameters.radius, + parameters.tube, + parameters.radialSegments, + parameters.tubularSegments, + parameters.arc + ); + +}; + +// File:src/extras/geometries/TorusKnotGeometry.js + +/** + * @author oosmoxiecode + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473 + */ + +THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) { + + THREE.Geometry.call( this ); + + this.type = 'TorusKnotGeometry'; + + this.parameters = { + radius: radius, + tube: tube, + radialSegments: radialSegments, + tubularSegments: tubularSegments, + p: p, + q: q, + heightScale: heightScale + }; + + radius = radius || 100; + tube = tube || 40; + radialSegments = radialSegments || 64; + tubularSegments = tubularSegments || 8; + p = p || 2; + q = q || 3; + heightScale = heightScale || 1; + + var grid = new Array( radialSegments ); + var tang = new THREE.Vector3(); + var n = new THREE.Vector3(); + var bitan = new THREE.Vector3(); + + for ( var i = 0; i < radialSegments; ++ i ) { + + grid[ i ] = new Array( tubularSegments ); + var u = i / radialSegments * 2 * p * Math.PI; + var p1 = getPos( u, q, p, radius, heightScale ); + var p2 = getPos( u + 0.01, q, p, radius, heightScale ); + tang.subVectors( p2, p1 ); + n.addVectors( p2, p1 ); + + bitan.crossVectors( tang, n ); + n.crossVectors( bitan, tang ); + bitan.normalize(); + n.normalize(); + + for ( var j = 0; j < tubularSegments; ++ j ) { + + var v = j / tubularSegments * 2 * Math.PI; + var cx = - tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + var cy = tube * Math.sin( v ); + + var pos = new THREE.Vector3(); + pos.x = p1.x + cx * n.x + cy * bitan.x; + pos.y = p1.y + cx * n.y + cy * bitan.y; + pos.z = p1.z + cx * n.z + cy * bitan.z; + + grid[ i ][ j ] = this.vertices.push( pos ) - 1; + + } + + } + + for ( var i = 0; i < radialSegments; ++ i ) { + + for ( var j = 0; j < tubularSegments; ++ j ) { + + var ip = ( i + 1 ) % radialSegments; + var jp = ( j + 1 ) % tubularSegments; + + var a = grid[ i ][ j ]; + var b = grid[ ip ][ j ]; + var c = grid[ ip ][ jp ]; + var d = grid[ i ][ jp ]; + + var uva = new THREE.Vector2( i / radialSegments, j / tubularSegments ); + var uvb = new THREE.Vector2( ( i + 1 ) / radialSegments, j / tubularSegments ); + var uvc = new THREE.Vector2( ( i + 1 ) / radialSegments, ( j + 1 ) / tubularSegments ); + var uvd = new THREE.Vector2( i / radialSegments, ( j + 1 ) / tubularSegments ); + + this.faces.push( new THREE.Face3( a, b, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + this.faces.push( new THREE.Face3( b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + + } + + this.computeFaceNormals(); + this.computeVertexNormals(); + + function getPos( u, in_q, in_p, radius, heightScale ) { + + var cu = Math.cos( u ); + var su = Math.sin( u ); + var quOverP = in_q / in_p * u; + var cs = Math.cos( quOverP ); + + var tx = radius * ( 2 + cs ) * 0.5 * cu; + var ty = radius * ( 2 + cs ) * su * 0.5; + var tz = heightScale * radius * Math.sin( quOverP ) * 0.5; + + return new THREE.Vector3( tx, ty, tz ); + + } + +}; + +THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.TorusKnotGeometry.prototype.constructor = THREE.TorusKnotGeometry; + +THREE.TorusKnotGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.TorusKnotGeometry( + parameters.radius, + parameters.tube, + parameters.radialSegments, + parameters.tubularSegments, + parameters.p, + parameters.q, + parameters.heightScale + ); + +}; + +// File:src/extras/geometries/TubeGeometry.js + +/** + * @author WestLangley / https://github.com/WestLangley + * @author zz85 / https://github.com/zz85 + * @author miningold / https://github.com/miningold + * @author jonobr1 / https://github.com/jonobr1 + * + * Modified from the TorusKnotGeometry by @oosmoxiecode + * + * Creates a tube which extrudes along a 3d spline + * + * Uses parallel transport frames as described in + * http://www.cs.indiana.edu/pub/techreports/TR425.pdf + */ + +THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed, taper ) { + + THREE.Geometry.call( this ); + + this.type = 'TubeGeometry'; + + this.parameters = { + path: path, + segments: segments, + radius: radius, + radialSegments: radialSegments, + closed: closed, + taper: taper + }; + + segments = segments || 64; + radius = radius || 1; + radialSegments = radialSegments || 8; + closed = closed || false; + taper = taper || THREE.TubeGeometry.NoTaper; + + var grid = []; + + var scope = this, + + tangent, + normal, + binormal, + + numpoints = segments + 1, + + u, v, r, + + cx, cy, + pos, pos2 = new THREE.Vector3(), + i, j, + ip, jp, + a, b, c, d, + uva, uvb, uvc, uvd; + + var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ), + tangents = frames.tangents, + normals = frames.normals, + binormals = frames.binormals; + + // proxy internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; + + function vert( x, y, z ) { + + return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1; + + } + + // construct the grid + + for ( i = 0; i < numpoints; i ++ ) { + + grid[ i ] = []; + + u = i / ( numpoints - 1 ); + + pos = path.getPointAt( u ); + + tangent = tangents[ i ]; + normal = normals[ i ]; + binormal = binormals[ i ]; + + r = radius * taper( u ); + + for ( j = 0; j < radialSegments; j ++ ) { + + v = j / radialSegments * 2 * Math.PI; + + cx = - r * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + cy = r * Math.sin( v ); + + pos2.copy( pos ); + pos2.x += cx * normal.x + cy * binormal.x; + pos2.y += cx * normal.y + cy * binormal.y; + pos2.z += cx * normal.z + cy * binormal.z; + + grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z ); + + } + + } + + + // construct the mesh + + for ( i = 0; i < segments; i ++ ) { + + for ( j = 0; j < radialSegments; j ++ ) { + + ip = ( closed ) ? ( i + 1 ) % segments : i + 1; + jp = ( j + 1 ) % radialSegments; + + a = grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! *** + b = grid[ ip ][ j ]; + c = grid[ ip ][ jp ]; + d = grid[ i ][ jp ]; + + uva = new THREE.Vector2( i / segments, j / radialSegments ); + uvb = new THREE.Vector2( ( i + 1 ) / segments, j / radialSegments ); + uvc = new THREE.Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments ); + uvd = new THREE.Vector2( i / segments, ( j + 1 ) / radialSegments ); + + this.faces.push( new THREE.Face3( a, b, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + this.faces.push( new THREE.Face3( b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + + } + + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.TubeGeometry.prototype.constructor = THREE.TubeGeometry; +THREE.TubeGeometry.prototype.clone = function() { + + return new this.constructor( this.parameters.path, + this.parameters.segments, this.parameters.radius, this.parameters.radialSegments, + this.parameters.closed, this.parameters.taper + ); + +}; + +THREE.TubeGeometry.NoTaper = function ( u ) { + + return 1; + +}; + +THREE.TubeGeometry.SinusoidalTaper = function ( u ) { + + return Math.sin( Math.PI * u ); + +}; + +// For computing of Frenet frames, exposing the tangents, normals and binormals the spline +THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) { + + var normal = new THREE.Vector3(), + + tangents = [], + normals = [], + binormals = [], + + vec = new THREE.Vector3(), + mat = new THREE.Matrix4(), + + numpoints = segments + 1, + theta, + smallest, + + tx, ty, tz, + i, u; + + + // expose internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; + + // compute the tangent vectors for each segment on the path + + for ( i = 0; i < numpoints; i ++ ) { + + u = i / ( numpoints - 1 ); + + tangents[ i ] = path.getTangentAt( u ); + tangents[ i ].normalize(); + + } + + initialNormal3(); + + /* + function initialNormal1(lastBinormal) { + // fixed start binormal. Has dangers of 0 vectors + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 ); + normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize(); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + } + + function initialNormal2() { + + // This uses the Frenet-Serret formula for deriving binormal + var t2 = path.getTangentAt( epsilon ); + + normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize(); + binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] ); + + normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + + } + */ + + function initialNormal3() { + + // select an initial normal vector perpendicular to the first tangent vector, + // and in the direction of the smallest tangent xyz component + + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + smallest = Number.MAX_VALUE; + tx = Math.abs( tangents[ 0 ].x ); + ty = Math.abs( tangents[ 0 ].y ); + tz = Math.abs( tangents[ 0 ].z ); + + if ( tx <= smallest ) { + + smallest = tx; + normal.set( 1, 0, 0 ); + + } + + if ( ty <= smallest ) { + + smallest = ty; + normal.set( 0, 1, 0 ); + + } + + if ( tz <= smallest ) { + + normal.set( 0, 0, 1 ); + + } + + vec.crossVectors( tangents[ 0 ], normal ).normalize(); + + normals[ 0 ].crossVectors( tangents[ 0 ], vec ); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); + + } + + + // compute the slowly-varying normal and binormal vectors for each segment on the path + + for ( i = 1; i < numpoints; i ++ ) { + + normals[ i ] = normals[ i - 1 ].clone(); + + binormals[ i ] = binormals[ i - 1 ].clone(); + + vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); + + if ( vec.length() > Number.EPSILON ) { + + vec.normalize(); + + theta = Math.acos( THREE.Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors + + normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); + + } + + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + + } + + + // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same + + if ( closed ) { + + theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints - 1 ] ), - 1, 1 ) ); + theta /= ( numpoints - 1 ); + + if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints - 1 ] ) ) > 0 ) { + + theta = - theta; + + } + + for ( i = 1; i < numpoints; i ++ ) { + + // twist a little... + normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + + } + + } + +}; + +// File:src/extras/geometries/PolyhedronGeometry.js + +/** + * @author clockworkgeek / https://github.com/clockworkgeek + * @author timothypratley / https://github.com/timothypratley + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) { + + THREE.Geometry.call( this ); + + this.type = 'PolyhedronGeometry'; + + this.parameters = { + vertices: vertices, + indices: indices, + radius: radius, + detail: detail + }; + + radius = radius || 1; + detail = detail || 0; + + var that = this; + + for ( var i = 0, l = vertices.length; i < l; i += 3 ) { + + prepare( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) ); + + } + + var p = this.vertices; + + var faces = []; + + for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) { + + var v1 = p[ indices[ i ] ]; + var v2 = p[ indices[ i + 1 ] ]; + var v3 = p[ indices[ i + 2 ] ]; + + faces[ j ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ], undefined, j ); + + } + + var centroid = new THREE.Vector3(); + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + subdivide( faces[ i ], detail ); + + } + + + // Handle case when face straddles the seam + + for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) { + + var uvs = this.faceVertexUvs[ 0 ][ i ]; + + var x0 = uvs[ 0 ].x; + var x1 = uvs[ 1 ].x; + var x2 = uvs[ 2 ].x; + + var max = Math.max( x0, x1, x2 ); + var min = Math.min( x0, x1, x2 ); + + if ( max > 0.9 && min < 0.1 ) { + + // 0.9 is somewhat arbitrary + + if ( x0 < 0.2 ) uvs[ 0 ].x += 1; + if ( x1 < 0.2 ) uvs[ 1 ].x += 1; + if ( x2 < 0.2 ) uvs[ 2 ].x += 1; + + } + + } + + + // Apply radius + + for ( var i = 0, l = this.vertices.length; i < l; i ++ ) { + + this.vertices[ i ].multiplyScalar( radius ); + + } + + + // Merge vertices + + this.mergeVertices(); + + this.computeFaceNormals(); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + + + // Project vector onto sphere's surface + + function prepare( vector ) { + + var vertex = vector.normalize().clone(); + vertex.index = that.vertices.push( vertex ) - 1; + + // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle. + + var u = azimuth( vector ) / 2 / Math.PI + 0.5; + var v = inclination( vector ) / Math.PI + 0.5; + vertex.uv = new THREE.Vector2( u, 1 - v ); + + return vertex; + + } + + + // Approximate a curved face with recursively sub-divided triangles. + + function make( v1, v2, v3, materialIndex ) { + + var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ], undefined, materialIndex ); + that.faces.push( face ); + + centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 ); + + var azi = azimuth( centroid ); + + that.faceVertexUvs[ 0 ].push( [ + correctUV( v1.uv, v1, azi ), + correctUV( v2.uv, v2, azi ), + correctUV( v3.uv, v3, azi ) + ] ); + + } + + + // Analytically subdivide a face to the required detail level. + + function subdivide( face, detail ) { + + var cols = Math.pow( 2, detail ); + var a = prepare( that.vertices[ face.a ] ); + var b = prepare( that.vertices[ face.b ] ); + var c = prepare( that.vertices[ face.c ] ); + var v = []; + + var materialIndex = face.materialIndex; + + // Construct all of the vertices for this subdivision. + + for ( var i = 0 ; i <= cols; i ++ ) { + + v[ i ] = []; + + var aj = prepare( a.clone().lerp( c, i / cols ) ); + var bj = prepare( b.clone().lerp( c, i / cols ) ); + var rows = cols - i; + + for ( var j = 0; j <= rows; j ++ ) { + + if ( j === 0 && i === cols ) { + + v[ i ][ j ] = aj; + + } else { + + v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) ); + + } + + } + + } + + // Construct all of the faces. + + for ( var i = 0; i < cols ; i ++ ) { + + for ( var j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { + + var k = Math.floor( j / 2 ); + + if ( j % 2 === 0 ) { + + make( + v[ i ][ k + 1 ], + v[ i + 1 ][ k ], + v[ i ][ k ], + materialIndex + ); + + } else { + + make( + v[ i ][ k + 1 ], + v[ i + 1 ][ k + 1 ], + v[ i + 1 ][ k ], + materialIndex + ); + + } + + } + + } + + } + + + // Angle around the Y axis, counter-clockwise when looking from above. + + function azimuth( vector ) { + + return Math.atan2( vector.z, - vector.x ); + + } + + + // Angle above the XZ plane. + + function inclination( vector ) { + + return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); + + } + + + // Texture fixing helper. Spheres have some odd behaviours. + + function correctUV( uv, vector, azimuth ) { + + if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new THREE.Vector2( uv.x - 1, uv.y ); + if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new THREE.Vector2( azimuth / 2 / Math.PI + 0.5, uv.y ); + return uv.clone(); + + } + + +}; + +THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.PolyhedronGeometry.prototype.constructor = THREE.PolyhedronGeometry; + +THREE.PolyhedronGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.PolyhedronGeometry( + parameters.vertices, + parameters.indices, + parameters.radius, + parameters.detail + ); + +}; + +// File:src/extras/geometries/DodecahedronGeometry.js + +/** + * @author Abe Pazos / https://hamoid.com + */ + +THREE.DodecahedronGeometry = function ( radius, detail ) { + + var t = ( 1 + Math.sqrt( 5 ) ) / 2; + var r = 1 / t; + + var vertices = [ + + // (±1, ±1, ±1) + - 1, - 1, - 1, - 1, - 1, 1, + - 1, 1, - 1, - 1, 1, 1, + 1, - 1, - 1, 1, - 1, 1, + 1, 1, - 1, 1, 1, 1, + + // (0, ±1/φ, ±φ) + 0, - r, - t, 0, - r, t, + 0, r, - t, 0, r, t, + + // (±1/φ, ±φ, 0) + - r, - t, 0, - r, t, 0, + r, - t, 0, r, t, 0, + + // (±φ, 0, ±1/φ) + - t, 0, - r, t, 0, - r, + - t, 0, r, t, 0, r + ]; + + var indices = [ + 3, 11, 7, 3, 7, 15, 3, 15, 13, + 7, 19, 17, 7, 17, 6, 7, 6, 15, + 17, 4, 8, 17, 8, 10, 17, 10, 6, + 8, 0, 16, 8, 16, 2, 8, 2, 10, + 0, 12, 1, 0, 1, 18, 0, 18, 16, + 6, 10, 2, 6, 2, 13, 6, 13, 15, + 2, 16, 18, 2, 18, 3, 2, 3, 13, + 18, 1, 9, 18, 9, 11, 18, 11, 3, + 4, 14, 12, 4, 12, 0, 4, 0, 8, + 11, 9, 5, 11, 5, 19, 11, 19, 7, + 19, 5, 14, 19, 14, 4, 19, 4, 17, + 1, 12, 14, 1, 14, 5, 1, 5, 9 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + + this.type = 'DodecahedronGeometry'; + + this.parameters = { + radius: radius, + detail: detail + }; + +}; + +THREE.DodecahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype ); +THREE.DodecahedronGeometry.prototype.constructor = THREE.DodecahedronGeometry; + +THREE.DodecahedronGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.DodecahedronGeometry( + parameters.radius, + parameters.detail + ); + +}; + +// File:src/extras/geometries/IcosahedronGeometry.js + +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.IcosahedronGeometry = function ( radius, detail ) { + + var t = ( 1 + Math.sqrt( 5 ) ) / 2; + + var vertices = [ + - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, + 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, + t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 + ]; + + var indices = [ + 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, + 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, + 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, + 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + + this.type = 'IcosahedronGeometry'; + + this.parameters = { + radius: radius, + detail: detail + }; + +}; + +THREE.IcosahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype ); +THREE.IcosahedronGeometry.prototype.constructor = THREE.IcosahedronGeometry; + +THREE.IcosahedronGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.IcosahedronGeometry( + parameters.radius, + parameters.detail + ); + +}; + +// File:src/extras/geometries/OctahedronGeometry.js + +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.OctahedronGeometry = function ( radius, detail ) { + + var vertices = [ + 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1 + ]; + + var indices = [ + 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + + this.type = 'OctahedronGeometry'; + + this.parameters = { + radius: radius, + detail: detail + }; + +}; + +THREE.OctahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype ); +THREE.OctahedronGeometry.prototype.constructor = THREE.OctahedronGeometry; + +THREE.OctahedronGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.OctahedronGeometry( + parameters.radius, + parameters.detail + ); + +}; + +// File:src/extras/geometries/TetrahedronGeometry.js + +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.TetrahedronGeometry = function ( radius, detail ) { + + var vertices = [ + 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 + ]; + + var indices = [ + 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + + this.type = 'TetrahedronGeometry'; + + this.parameters = { + radius: radius, + detail: detail + }; + +}; + +THREE.TetrahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype ); +THREE.TetrahedronGeometry.prototype.constructor = THREE.TetrahedronGeometry; + +THREE.TetrahedronGeometry.prototype.clone = function () { + + var parameters = this.parameters; + + return new THREE.TetrahedronGeometry( + parameters.radius, + parameters.detail + ); + +}; + +// File:src/extras/geometries/ParametricGeometry.js + +/** + * @author zz85 / https://github.com/zz85 + * Parametric Surfaces Geometry + * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 + * + * new THREE.ParametricGeometry( parametricFunction, uSegments, ySegements ); + * + */ + +THREE.ParametricGeometry = function ( func, slices, stacks ) { + + THREE.Geometry.call( this ); + + this.type = 'ParametricGeometry'; + + this.parameters = { + func: func, + slices: slices, + stacks: stacks + }; + + var verts = this.vertices; + var faces = this.faces; + var uvs = this.faceVertexUvs[ 0 ]; + + var i, j, p; + var u, v; + + var sliceCount = slices + 1; + + for ( i = 0; i <= stacks; i ++ ) { + + v = i / stacks; + + for ( j = 0; j <= slices; j ++ ) { + + u = j / slices; + + p = func( u, v ); + verts.push( p ); + + } + + } + + var a, b, c, d; + var uva, uvb, uvc, uvd; + + for ( i = 0; i < stacks; i ++ ) { + + for ( j = 0; j < slices; j ++ ) { + + a = i * sliceCount + j; + b = i * sliceCount + j + 1; + c = ( i + 1 ) * sliceCount + j + 1; + d = ( i + 1 ) * sliceCount + j; + + uva = new THREE.Vector2( j / slices, i / stacks ); + uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks ); + uvc = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks ); + uvd = new THREE.Vector2( j / slices, ( i + 1 ) / stacks ); + + faces.push( new THREE.Face3( a, b, d ) ); + uvs.push( [ uva, uvb, uvd ] ); + + faces.push( new THREE.Face3( b, c, d ) ); + uvs.push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + + } + + // console.log(this); + + // magic bullet + // var diff = this.mergeVertices(); + // console.log('removed ', diff, ' vertices by merging'); + + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ParametricGeometry.prototype.constructor = THREE.ParametricGeometry; + +// File:src/extras/geometries/WireframeGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.WireframeGeometry = function ( geometry ) { + + THREE.BufferGeometry.call( this ); + + var edge = [ 0, 0 ], hash = {}; + + function sortFunction( a, b ) { + + return a - b; + + } + + var keys = [ 'a', 'b', 'c' ]; + + if ( geometry instanceof THREE.Geometry ) { + + var vertices = geometry.vertices; + var faces = geometry.faces; + var numEdges = 0; + + // allocate maximal size + var edges = new Uint32Array( 6 * faces.length ); + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0; j < 3; j ++ ) { + + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); + + var key = edge.toString(); + + if ( hash[ key ] === undefined ) { + + edges[ 2 * numEdges ] = edge[ 0 ]; + edges[ 2 * numEdges + 1 ] = edge[ 1 ]; + hash[ key ] = true; + numEdges ++; + + } + + } + + } + + var coords = new Float32Array( numEdges * 2 * 3 ); + + for ( var i = 0, l = numEdges; i < l; i ++ ) { + + for ( var j = 0; j < 2; j ++ ) { + + var vertex = vertices[ edges [ 2 * i + j ] ]; + + var index = 6 * i + 3 * j; + coords[ index + 0 ] = vertex.x; + coords[ index + 1 ] = vertex.y; + coords[ index + 2 ] = vertex.z; + + } + + } + + this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + + } else if ( geometry instanceof THREE.BufferGeometry ) { + + if ( geometry.index !== null ) { + + // Indexed BufferGeometry + + var indices = geometry.index.array; + var vertices = geometry.attributes.position; + var drawcalls = geometry.drawcalls; + var numEdges = 0; + + if ( drawcalls.length === 0 ) { + + geometry.addGroup( 0, indices.length ); + + } + + // allocate maximal size + var edges = new Uint32Array( 2 * indices.length ); + + for ( var o = 0, ol = drawcalls.length; o < ol; ++ o ) { + + var drawcall = drawcalls[ o ]; + + var start = drawcall.start; + var count = drawcall.count; + + for ( var i = start, il = start + count; i < il; i += 3 ) { + + for ( var j = 0; j < 3; j ++ ) { + + edge[ 0 ] = indices[ i + j ]; + edge[ 1 ] = indices[ i + ( j + 1 ) % 3 ]; + edge.sort( sortFunction ); + + var key = edge.toString(); + + if ( hash[ key ] === undefined ) { + + edges[ 2 * numEdges ] = edge[ 0 ]; + edges[ 2 * numEdges + 1 ] = edge[ 1 ]; + hash[ key ] = true; + numEdges ++; + + } + + } + + } + + } + + var coords = new Float32Array( numEdges * 2 * 3 ); + + for ( var i = 0, l = numEdges; i < l; i ++ ) { + + for ( var j = 0; j < 2; j ++ ) { + + var index = 6 * i + 3 * j; + var index2 = edges[ 2 * i + j ]; + + coords[ index + 0 ] = vertices.getX( index2 ); + coords[ index + 1 ] = vertices.getY( index2 ); + coords[ index + 2 ] = vertices.getZ( index2 ); + + } + + } + + this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + + } else { + + // non-indexed BufferGeometry + + var vertices = geometry.attributes.position.array; + var numEdges = vertices.length / 3; + var numTris = numEdges / 3; + + var coords = new Float32Array( numEdges * 2 * 3 ); + + for ( var i = 0, l = numTris; i < l; i ++ ) { + + for ( var j = 0; j < 3; j ++ ) { + + var index = 18 * i + 6 * j; + + var index1 = 9 * i + 3 * j; + coords[ index + 0 ] = vertices[ index1 ]; + coords[ index + 1 ] = vertices[ index1 + 1 ]; + coords[ index + 2 ] = vertices[ index1 + 2 ]; + + var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 ); + coords[ index + 3 ] = vertices[ index2 ]; + coords[ index + 4 ] = vertices[ index2 + 1 ]; + coords[ index + 5 ] = vertices[ index2 + 2 ]; + + } + + } + + this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + + } + + } + +}; + +THREE.WireframeGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.WireframeGeometry.prototype.constructor = THREE.WireframeGeometry; + +// File:src/extras/helpers/AxisHelper.js + +/** + * @author sroucheray / http://sroucheray.org/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AxisHelper = function ( size ) { + + size = size || 1; + + var vertices = new Float32Array( [ + 0, 0, 0, size, 0, 0, + 0, 0, 0, 0, size, 0, + 0, 0, 0, 0, 0, size + ] ); + + var colors = new Float32Array( [ + 1, 0, 0, 1, 0.6, 0, + 0, 1, 0, 0.6, 1, 0, + 0, 0, 1, 0, 0.6, 1 + ] ); + + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); + + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); + + THREE.LineSegments.call( this, geometry, material ); + +}; + +THREE.AxisHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.AxisHelper.prototype.constructor = THREE.AxisHelper; + +// File:src/extras/helpers/ArrowHelper.js + +/** + * @author WestLangley / http://github.com/WestLangley + * @author zz85 / http://github.com/zz85 + * @author bhouston / http://clara.io + * + * Creates an arrow for visualizing directions + * + * Parameters: + * dir - Vector3 + * origin - Vector3 + * length - Number + * color - color in hex value + * headLength - Number + * headWidth - Number + */ + +THREE.ArrowHelper = ( function () { + + var lineGeometry = new THREE.Geometry(); + lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) ); + + var coneGeometry = new THREE.CylinderGeometry( 0, 0.5, 1, 5, 1 ); + coneGeometry.translate( 0, - 0.5, 0 ); + + return function ArrowHelper( dir, origin, length, color, headLength, headWidth ) { + + // dir is assumed to be normalized + + THREE.Object3D.call( this ); + + if ( color === undefined ) color = 0xffff00; + if ( length === undefined ) length = 1; + if ( headLength === undefined ) headLength = 0.2 * length; + if ( headWidth === undefined ) headWidth = 0.2 * headLength; + + this.position.copy( origin ); + + if ( headLength < length ) { + this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) ); + this.line.matrixAutoUpdate = false; + this.add( this.line ); + } + + this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) ); + this.cone.matrixAutoUpdate = false; + this.add( this.cone ); + + this.setDirection( dir ); + this.setLength( length, headLength, headWidth ); + + } + +}() ); + +THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype ); +THREE.ArrowHelper.prototype.constructor = THREE.ArrowHelper; + +THREE.ArrowHelper.prototype.setDirection = ( function () { + + var axis = new THREE.Vector3(); + var radians; + + return function setDirection( dir ) { + + // dir is assumed to be normalized + + if ( dir.y > 0.99999 ) { + + this.quaternion.set( 0, 0, 0, 1 ); + + } else if ( dir.y < - 0.99999 ) { + + this.quaternion.set( 1, 0, 0, 0 ); + + } else { + + axis.set( dir.z, 0, - dir.x ).normalize(); + + radians = Math.acos( dir.y ); + + this.quaternion.setFromAxisAngle( axis, radians ); + + } + + }; + +}() ); + +THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) { + + if ( headLength === undefined ) headLength = 0.2 * length; + if ( headWidth === undefined ) headWidth = 0.2 * headLength; + + if ( headLength < length ){ + this.line.scale.set( 1, length - headLength, 1 ); + this.line.updateMatrix(); + } + + this.cone.scale.set( headWidth, headLength, headWidth ); + this.cone.position.y = length; + this.cone.updateMatrix(); + +}; + +THREE.ArrowHelper.prototype.setColor = function ( color ) { + + if ( this.line !== undefined ) this.line.material.color.set( color ); + this.cone.material.color.set( color ); + +}; + +// File:src/extras/helpers/BoxHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BoxHelper = function ( object ) { + + var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); + var positions = new Float32Array( 8 * 3 ); + + var geometry = new THREE.BufferGeometry(); + geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) ); + geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + + THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ) ); + + if ( object !== undefined ) { + + this.update( object ); + + } + +}; + +THREE.BoxHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.BoxHelper.prototype.constructor = THREE.BoxHelper; + +THREE.BoxHelper.prototype.update = ( function () { + + var box = new THREE.Box3(); + + return function ( object ) { + + box.setFromObject( object ); + + if ( box.empty() ) return; + + var min = box.min; + var max = box.max; + + /* + 5____4 + 1/___0/| + | 6__|_7 + 2/___3/ + + 0: max.x, max.y, max.z + 1: min.x, max.y, max.z + 2: min.x, min.y, max.z + 3: max.x, min.y, max.z + 4: max.x, max.y, min.z + 5: min.x, max.y, min.z + 6: min.x, min.y, min.z + 7: max.x, min.y, min.z + */ + + var position = this.geometry.attributes.position; + var array = position.array; + + array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z; + array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z; + array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z; + array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z; + array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z; + array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z; + array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z; + array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z; + + position.needsUpdate = true; + + this.geometry.computeBoundingSphere(); + + } + +} )(); + +// File:src/extras/helpers/BoundingBoxHelper.js + +/** + * @author WestLangley / http://github.com/WestLangley + */ + +// a helper to show the world-axis-aligned bounding box for an object + +THREE.BoundingBoxHelper = function ( object, hex ) { + + var color = ( hex !== undefined ) ? hex : 0x888888; + + this.object = object; + + this.box = new THREE.Box3(); + + THREE.Mesh.call( this, new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) ); + +}; + +THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype ); +THREE.BoundingBoxHelper.prototype.constructor = THREE.BoundingBoxHelper; + +THREE.BoundingBoxHelper.prototype.update = function () { + + this.box.setFromObject( this.object ); + + this.box.size( this.scale ); + + this.box.center( this.position ); + +}; + +// File:src/extras/helpers/CameraHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * + * - shows frustum, line of sight and up of the camera + * - suitable for fast updates + * - based on frustum visualization in lightgl.js shadowmap example + * http://evanw.github.com/lightgl.js/tests/shadowmap.html + */ + +THREE.CameraHelper = function ( camera ) { + + var geometry = new THREE.Geometry(); + var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } ); + + var pointMap = {}; + + // colors + + var hexFrustum = 0xffaa00; + var hexCone = 0xff0000; + var hexUp = 0x00aaff; + var hexTarget = 0xffffff; + var hexCross = 0x333333; + + // near + + addLine( "n1", "n2", hexFrustum ); + addLine( "n2", "n4", hexFrustum ); + addLine( "n4", "n3", hexFrustum ); + addLine( "n3", "n1", hexFrustum ); + + // far + + addLine( "f1", "f2", hexFrustum ); + addLine( "f2", "f4", hexFrustum ); + addLine( "f4", "f3", hexFrustum ); + addLine( "f3", "f1", hexFrustum ); + + // sides + + addLine( "n1", "f1", hexFrustum ); + addLine( "n2", "f2", hexFrustum ); + addLine( "n3", "f3", hexFrustum ); + addLine( "n4", "f4", hexFrustum ); + + // cone + + addLine( "p", "n1", hexCone ); + addLine( "p", "n2", hexCone ); + addLine( "p", "n3", hexCone ); + addLine( "p", "n4", hexCone ); + + // up + + addLine( "u1", "u2", hexUp ); + addLine( "u2", "u3", hexUp ); + addLine( "u3", "u1", hexUp ); + + // target + + addLine( "c", "t", hexTarget ); + addLine( "p", "c", hexCross ); + + // cross + + addLine( "cn1", "cn2", hexCross ); + addLine( "cn3", "cn4", hexCross ); + + addLine( "cf1", "cf2", hexCross ); + addLine( "cf3", "cf4", hexCross ); + + function addLine( a, b, hex ) { + + addPoint( a, hex ); + addPoint( b, hex ); + + } + + function addPoint( id, hex ) { + + geometry.vertices.push( new THREE.Vector3() ); + geometry.colors.push( new THREE.Color( hex ) ); + + if ( pointMap[ id ] === undefined ) { + + pointMap[ id ] = []; + + } + + pointMap[ id ].push( geometry.vertices.length - 1 ); + + } + + THREE.LineSegments.call( this, geometry, material ); + + this.camera = camera; + this.camera.updateProjectionMatrix(); + + this.matrix = camera.matrixWorld; + this.matrixAutoUpdate = false; + + this.pointMap = pointMap; + + this.update(); + +}; + +THREE.CameraHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.CameraHelper.prototype.constructor = THREE.CameraHelper; + +THREE.CameraHelper.prototype.update = function () { + + var geometry, pointMap; + + var vector = new THREE.Vector3(); + var camera = new THREE.Camera(); + + function setPoint( point, x, y, z ) { + + vector.set( x, y, z ).unproject( camera ); + + var points = pointMap[ point ]; + + if ( points !== undefined ) { + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + geometry.vertices[ points[ i ] ].copy( vector ); + + } + + } + + } + + return function () { + + geometry = this.geometry; + pointMap = this.pointMap; + + var w = 1, h = 1; + + // we need just camera projection matrix + // world matrix must be identity + + camera.projectionMatrix.copy( this.camera.projectionMatrix ); + + // center / target + + setPoint( "c", 0, 0, - 1 ); + setPoint( "t", 0, 0, 1 ); + + // near + + setPoint( "n1", - w, - h, - 1 ); + setPoint( "n2", w, - h, - 1 ); + setPoint( "n3", - w, h, - 1 ); + setPoint( "n4", w, h, - 1 ); + + // far + + setPoint( "f1", - w, - h, 1 ); + setPoint( "f2", w, - h, 1 ); + setPoint( "f3", - w, h, 1 ); + setPoint( "f4", w, h, 1 ); + + // up + + setPoint( "u1", w * 0.7, h * 1.1, - 1 ); + setPoint( "u2", - w * 0.7, h * 1.1, - 1 ); + setPoint( "u3", 0, h * 2, - 1 ); + + // cross + + setPoint( "cf1", - w, 0, 1 ); + setPoint( "cf2", w, 0, 1 ); + setPoint( "cf3", 0, - h, 1 ); + setPoint( "cf4", 0, h, 1 ); + + setPoint( "cn1", - w, 0, - 1 ); + setPoint( "cn2", w, 0, - 1 ); + setPoint( "cn3", 0, - h, - 1 ); + setPoint( "cn4", 0, h, - 1 ); + + geometry.verticesNeedUpdate = true; + + }; + +}(); + +// File:src/extras/helpers/DirectionalLightHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.DirectionalLightHelper = function ( light, size ) { + + THREE.Object3D.call( this ); + + this.light = light; + this.light.updateMatrixWorld(); + + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; + + size = size || 1; + + var geometry = new THREE.Geometry(); + geometry.vertices.push( + new THREE.Vector3( - size, size, 0 ), + new THREE.Vector3( size, size, 0 ), + new THREE.Vector3( size, - size, 0 ), + new THREE.Vector3( - size, - size, 0 ), + new THREE.Vector3( - size, size, 0 ) + ); + + var material = new THREE.LineBasicMaterial( { fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + this.lightPlane = new THREE.Line( geometry, material ); + this.add( this.lightPlane ); + + geometry = new THREE.Geometry(); + geometry.vertices.push( + new THREE.Vector3(), + new THREE.Vector3() + ); + + material = new THREE.LineBasicMaterial( { fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + this.targetLine = new THREE.Line( geometry, material ); + this.add( this.targetLine ); + + this.update(); + +}; + +THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype ); +THREE.DirectionalLightHelper.prototype.constructor = THREE.DirectionalLightHelper; + +THREE.DirectionalLightHelper.prototype.dispose = function () { + + this.lightPlane.geometry.dispose(); + this.lightPlane.material.dispose(); + this.targetLine.geometry.dispose(); + this.targetLine.material.dispose(); + +}; + +THREE.DirectionalLightHelper.prototype.update = function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var v3 = new THREE.Vector3(); + + return function () { + + v1.setFromMatrixPosition( this.light.matrixWorld ); + v2.setFromMatrixPosition( this.light.target.matrixWorld ); + v3.subVectors( v2, v1 ); + + this.lightPlane.lookAt( v3 ); + this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + this.targetLine.geometry.vertices[ 1 ].copy( v3 ); + this.targetLine.geometry.verticesNeedUpdate = true; + this.targetLine.material.color.copy( this.lightPlane.material.color ); + + }; + +}(); + +// File:src/extras/helpers/EdgesHelper.js + +/** + * @author WestLangley / http://github.com/WestLangley + * @param object THREE.Mesh whose geometry will be used + * @param hex line color + * @param thresholdAngle the minimum angle (in degrees), + * between the face normals of adjacent faces, + * that is required to render an edge. A value of 10 means + * an edge is only rendered if the angle is at least 10 degrees. + */ + +THREE.EdgesHelper = function ( object, hex, thresholdAngle ) { + + var color = ( hex !== undefined ) ? hex : 0xffffff; + + THREE.LineSegments.call( this, new THREE.EdgesGeometry( object.geometry, thresholdAngle ), new THREE.LineBasicMaterial( { color: color } ) ); + + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; + +}; + +THREE.EdgesHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.EdgesHelper.prototype.constructor = THREE.EdgesHelper; + +// File:src/extras/helpers/FaceNormalsHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) { + + // FaceNormalsHelper only supports THREE.Geometry + + this.object = object; + + this.size = ( size !== undefined ) ? size : 1; + + var color = ( hex !== undefined ) ? hex : 0xffff00; + + var width = ( linewidth !== undefined ) ? linewidth : 1; + + // + + var nNormals = 0; + + var objGeometry = this.object.geometry; + + if ( objGeometry instanceof THREE.Geometry ) { + + nNormals = objGeometry.faces.length; + + } else { + + console.warn( 'THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead.' ); + + } + + // + + var geometry = new THREE.BufferGeometry(); + + var positions = new THREE.Float32Attribute( nNormals * 2 * 3, 3 ); + + geometry.addAttribute( 'position', positions ); + + THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ) ); + + // + + this.matrixAutoUpdate = false; + this.update(); + +}; + +THREE.FaceNormalsHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.FaceNormalsHelper.prototype.constructor = THREE.FaceNormalsHelper; + +THREE.FaceNormalsHelper.prototype.update = ( function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var normalMatrix = new THREE.Matrix3(); + + return function update() { + + this.object.updateMatrixWorld( true ); + + normalMatrix.getNormalMatrix( this.object.matrixWorld ); + + var matrixWorld = this.object.matrixWorld; + + var position = this.geometry.attributes.position; + + // + + var objGeometry = this.object.geometry; + + var vertices = objGeometry.vertices; + + var faces = objGeometry.faces; + + var idx = 0; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + var normal = face.normal; + + v1.copy( vertices[ face.a ] ) + .add( vertices[ face.b ] ) + .add( vertices[ face.c ] ) + .divideScalar( 3 ) + .applyMatrix4( matrixWorld ); + + v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); + + position.setXYZ( idx, v1.x, v1.y, v1.z ); + + idx = idx + 1; + + position.setXYZ( idx, v2.x, v2.y, v2.z ); + + idx = idx + 1; + + } + + position.needsUpdate = true; + + return this; + + } + +}() ); + +// File:src/extras/helpers/GridHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.GridHelper = function ( size, step ) { + + var geometry = new THREE.Geometry(); + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); + + this.color1 = new THREE.Color( 0x444444 ); + this.color2 = new THREE.Color( 0x888888 ); + + for ( var i = - size; i <= size; i += step ) { + + geometry.vertices.push( + new THREE.Vector3( - size, 0, i ), new THREE.Vector3( size, 0, i ), + new THREE.Vector3( i, 0, - size ), new THREE.Vector3( i, 0, size ) + ); + + var color = i === 0 ? this.color1 : this.color2; + + geometry.colors.push( color, color, color, color ); + + } + + THREE.LineSegments.call( this, geometry, material ); + +}; + +THREE.GridHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.GridHelper.prototype.constructor = THREE.GridHelper; + +THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) { + + this.color1.set( colorCenterLine ); + this.color2.set( colorGrid ); + + this.geometry.colorsNeedUpdate = true; + +}; + +// File:src/extras/helpers/HemisphereLightHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.HemisphereLightHelper = function ( light, sphereSize ) { + + THREE.Object3D.call( this ); + + this.light = light; + this.light.updateMatrixWorld(); + + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; + + this.colors = [ new THREE.Color(), new THREE.Color() ]; + + var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); + geometry.rotateX( - Math.PI / 2 ); + + for ( var i = 0, il = 8; i < il; i ++ ) { + + geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ]; + + } + + var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } ); + + this.lightSphere = new THREE.Mesh( geometry, material ); + this.add( this.lightSphere ); + + this.update(); + +}; + +THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype ); +THREE.HemisphereLightHelper.prototype.constructor = THREE.HemisphereLightHelper; + +THREE.HemisphereLightHelper.prototype.dispose = function () { + + this.lightSphere.geometry.dispose(); + this.lightSphere.material.dispose(); + +}; + +THREE.HemisphereLightHelper.prototype.update = function () { + + var vector = new THREE.Vector3(); + + return function () { + + this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity ); + this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity ); + + this.lightSphere.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() ); + this.lightSphere.geometry.colorsNeedUpdate = true; + + } + +}(); + +// File:src/extras/helpers/PointLightHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.PointLightHelper = function ( light, sphereSize ) { + + this.light = light; + this.light.updateMatrixWorld(); + + var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); + var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + THREE.Mesh.call( this, geometry, material ); + + this.matrix = this.light.matrixWorld; + this.matrixAutoUpdate = false; + + /* + var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); + var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); + + this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); + this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); + + var d = light.distance; + + if ( d === 0.0 ) { + + this.lightDistance.visible = false; + + } else { + + this.lightDistance.scale.set( d, d, d ); + + } + + this.add( this.lightDistance ); + */ + +}; + +THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype ); +THREE.PointLightHelper.prototype.constructor = THREE.PointLightHelper; + +THREE.PointLightHelper.prototype.dispose = function () { + + this.geometry.dispose(); + this.material.dispose(); + +}; + +THREE.PointLightHelper.prototype.update = function () { + + this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + /* + var d = this.light.distance; + + if ( d === 0.0 ) { + + this.lightDistance.visible = false; + + } else { + + this.lightDistance.visible = true; + this.lightDistance.scale.set( d, d, d ); + + } + */ + +}; + +// File:src/extras/helpers/SkeletonHelper.js + +/** + * @author Sean Griffin / http://twitter.com/sgrif + * @author Michael Guerrero / http://realitymeltdown.com + * @author mrdoob / http://mrdoob.com/ + * @author ikerr / http://verold.com + */ + +THREE.SkeletonHelper = function ( object ) { + + this.bones = this.getBoneList( object ); + + var geometry = new THREE.Geometry(); + + for ( var i = 0; i < this.bones.length; i ++ ) { + + var bone = this.bones[ i ]; + + if ( bone.parent instanceof THREE.Bone ) { + + geometry.vertices.push( new THREE.Vector3() ); + geometry.vertices.push( new THREE.Vector3() ); + geometry.colors.push( new THREE.Color( 0, 0, 1 ) ); + geometry.colors.push( new THREE.Color( 0, 1, 0 ) ); + + } + + } + + geometry.dynamic = true; + + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } ); + + THREE.LineSegments.call( this, geometry, material ); + + this.root = object; + + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; + + this.update(); + +}; + + +THREE.SkeletonHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.SkeletonHelper.prototype.constructor = THREE.SkeletonHelper; + +THREE.SkeletonHelper.prototype.getBoneList = function( object ) { + + var boneList = []; + + if ( object instanceof THREE.Bone ) { + + boneList.push( object ); + + } + + for ( var i = 0; i < object.children.length; i ++ ) { + + boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) ); + + } + + return boneList; + +}; + +THREE.SkeletonHelper.prototype.update = function () { + + var geometry = this.geometry; + + var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld ); + + var boneMatrix = new THREE.Matrix4(); + + var j = 0; + + for ( var i = 0; i < this.bones.length; i ++ ) { + + var bone = this.bones[ i ]; + + if ( bone.parent instanceof THREE.Bone ) { + + boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld ); + geometry.vertices[ j ].setFromMatrixPosition( boneMatrix ); + + boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld ); + geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix ); + + j += 2; + + } + + } + + geometry.verticesNeedUpdate = true; + + geometry.computeBoundingSphere(); + +}; + +// File:src/extras/helpers/SpotLightHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.SpotLightHelper = function ( light ) { + + THREE.Object3D.call( this ); + + this.light = light; + this.light.updateMatrixWorld(); + + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; + + var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true ); + + geometry.translate( 0, - 0.5, 0 ); + geometry.rotateX( - Math.PI / 2 ); + + var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); + + this.cone = new THREE.Mesh( geometry, material ); + this.add( this.cone ); + + this.update(); + +}; + +THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype ); +THREE.SpotLightHelper.prototype.constructor = THREE.SpotLightHelper; + +THREE.SpotLightHelper.prototype.dispose = function () { + + this.cone.geometry.dispose(); + this.cone.material.dispose(); + +}; + +THREE.SpotLightHelper.prototype.update = function () { + + var vector = new THREE.Vector3(); + var vector2 = new THREE.Vector3(); + + return function () { + + var coneLength = this.light.distance ? this.light.distance : 10000; + var coneWidth = coneLength * Math.tan( this.light.angle ); + + this.cone.scale.set( coneWidth, coneWidth, coneLength ); + + vector.setFromMatrixPosition( this.light.matrixWorld ); + vector2.setFromMatrixPosition( this.light.target.matrixWorld ); + + this.cone.lookAt( vector2.sub( vector ) ); + + this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + }; + +}(); + +// File:src/extras/helpers/VertexNormalsHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) { + + this.object = object; + + this.size = ( size !== undefined ) ? size : 1; + + var color = ( hex !== undefined ) ? hex : 0xff0000; + + var width = ( linewidth !== undefined ) ? linewidth : 1; + + // + + var nNormals = 0; + + var objGeometry = this.object.geometry; + + if ( objGeometry instanceof THREE.Geometry ) { + + nNormals = objGeometry.faces.length * 3; + + } else if ( objGeometry instanceof THREE.BufferGeometry ) { + + nNormals = objGeometry.attributes.normal.count + + } + + // + + var geometry = new THREE.BufferGeometry(); + + var positions = new THREE.Float32Attribute( nNormals * 2 * 3, 3 ); + + geometry.addAttribute( 'position', positions ); + + THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ) ); + + // + + this.matrixAutoUpdate = false; + + this.update(); + +}; + +THREE.VertexNormalsHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.VertexNormalsHelper.prototype.constructor = THREE.VertexNormalsHelper; + +THREE.VertexNormalsHelper.prototype.update = ( function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var normalMatrix = new THREE.Matrix3(); + + return function update() { + + var keys = [ 'a', 'b', 'c' ]; + + this.object.updateMatrixWorld( true ); + + normalMatrix.getNormalMatrix( this.object.matrixWorld ); + + var matrixWorld = this.object.matrixWorld; + + var position = this.geometry.attributes.position; + + // + + var objGeometry = this.object.geometry; + + if ( objGeometry instanceof THREE.Geometry ) { + + var vertices = objGeometry.vertices; + + var faces = objGeometry.faces; + + var idx = 0; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + + var vertex = vertices[ face[ keys[ j ] ] ]; + + var normal = face.vertexNormals[ j ]; + + v1.copy( vertex ).applyMatrix4( matrixWorld ); + + v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); + + position.setXYZ( idx, v1.x, v1.y, v1.z ); + + idx = idx + 1; + + position.setXYZ( idx, v2.x, v2.y, v2.z ); + + idx = idx + 1; + + } + + } + + } else if ( objGeometry instanceof THREE.BufferGeometry ) { + + var objPos = objGeometry.attributes.position; + + var objNorm = objGeometry.attributes.normal; + + var idx = 0; + + // for simplicity, ignore index and drawcalls, and render every normal + + for ( var j = 0, jl = objPos.count; j < jl; j ++ ) { + + v1.set( objPos.getX( j ), objPos.getY( j ), objPos.getZ( j ) ).applyMatrix4( matrixWorld ); + + v2.set( objNorm.getX( j ), objNorm.getY( j ), objNorm.getZ( j ) ); + + v2.applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); + + position.setXYZ( idx, v1.x, v1.y, v1.z ); + + idx = idx + 1; + + position.setXYZ( idx, v2.x, v2.y, v2.z ); + + idx = idx + 1; + + } + + } + + position.needsUpdate = true; + + return this; + + } + +}() ); + +// File:src/extras/helpers/WireframeHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.WireframeHelper = function ( object, hex ) { + + var color = ( hex !== undefined ) ? hex : 0xffffff; + + THREE.LineSegments.call( this, new THREE.WireframeGeometry( object.geometry ), new THREE.LineBasicMaterial( { color: color } ) ); + + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; + +}; + +THREE.WireframeHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.WireframeHelper.prototype.constructor = THREE.WireframeHelper; + +// File:src/extras/objects/ImmediateRenderObject.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ImmediateRenderObject = function ( material ) { + + THREE.Object3D.call( this ); + + this.material = material; + this.render = function ( renderCallback ) {}; + +}; + +THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype ); +THREE.ImmediateRenderObject.prototype.constructor = THREE.ImmediateRenderObject; + +// File:src/extras/objects/MorphBlendMesh.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.MorphBlendMesh = function( geometry, material ) { + + THREE.Mesh.call( this, geometry, material ); + + this.animationsMap = {}; + this.animationsList = []; + + // prepare default animation + // (all frames played together in 1 second) + + var numFrames = this.geometry.morphTargets.length; + + var name = "__default"; + + var startFrame = 0; + var endFrame = numFrames - 1; + + var fps = numFrames / 1; + + this.createAnimation( name, startFrame, endFrame, fps ); + this.setAnimationWeight( name, 1 ); + +}; + +THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype ); +THREE.MorphBlendMesh.prototype.constructor = THREE.MorphBlendMesh; + +THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) { + + var animation = { + + start: start, + end: end, + + length: end - start + 1, + + fps: fps, + duration: ( end - start ) / fps, + + lastFrame: 0, + currentFrame: 0, + + active: false, + + time: 0, + direction: 1, + weight: 1, + + directionBackwards: false, + mirroredLoop: false + + }; + + this.animationsMap[ name ] = animation; + this.animationsList.push( animation ); + +}; + +THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) { + + var pattern = /([a-z]+)_?(\d+)/; + + var firstAnimation, frameRanges = {}; + + var geometry = this.geometry; + + for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { + + var morph = geometry.morphTargets[ i ]; + var chunks = morph.name.match( pattern ); + + if ( chunks && chunks.length > 1 ) { + + var name = chunks[ 1 ]; + + if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity }; + + var range = frameRanges[ name ]; + + if ( i < range.start ) range.start = i; + if ( i > range.end ) range.end = i; + + if ( ! firstAnimation ) firstAnimation = name; + + } + + } + + for ( var name in frameRanges ) { + + var range = frameRanges[ name ]; + this.createAnimation( name, range.start, range.end, fps ); + + } + + this.firstAnimation = firstAnimation; + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.direction = 1; + animation.directionBackwards = false; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.direction = - 1; + animation.directionBackwards = true; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.fps = fps; + animation.duration = ( animation.end - animation.start ) / animation.fps; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.duration = duration; + animation.fps = ( animation.end - animation.start ) / animation.duration; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.weight = weight; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.time = time; + + } + +}; + +THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) { + + var time = 0; + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + time = animation.time; + + } + + return time; + +}; + +THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) { + + var duration = - 1; + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + duration = animation.duration; + + } + + return duration; + +}; + +THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.time = 0; + animation.active = true; + + } else { + + console.warn( "THREE.MorphBlendMesh: animation[" + name + "] undefined in .playAnimation()" ); + + } + +}; + +THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.active = false; + + } + +}; + +THREE.MorphBlendMesh.prototype.update = function ( delta ) { + + for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) { + + var animation = this.animationsList[ i ]; + + if ( ! animation.active ) continue; + + var frameTime = animation.duration / animation.length; + + animation.time += animation.direction * delta; + + if ( animation.mirroredLoop ) { + + if ( animation.time > animation.duration || animation.time < 0 ) { + + animation.direction *= - 1; + + if ( animation.time > animation.duration ) { + + animation.time = animation.duration; + animation.directionBackwards = true; + + } + + if ( animation.time < 0 ) { + + animation.time = 0; + animation.directionBackwards = false; + + } + + } + + } else { + + animation.time = animation.time % animation.duration; + + if ( animation.time < 0 ) animation.time += animation.duration; + + } + + var keyframe = animation.start + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 ); + var weight = animation.weight; + + if ( keyframe !== animation.currentFrame ) { + + this.morphTargetInfluences[ animation.lastFrame ] = 0; + this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight; + + this.morphTargetInfluences[ keyframe ] = 0; + + animation.lastFrame = animation.currentFrame; + animation.currentFrame = keyframe; + + } + + var mix = ( animation.time % frameTime ) / frameTime; + + if ( animation.directionBackwards ) mix = 1 - mix; + + if ( animation.currentFrame !== animation.lastFrame ) { + + this.morphTargetInfluences[ animation.currentFrame ] = mix * weight; + this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight; + + } else { + + this.morphTargetInfluences[ animation.currentFrame ] = weight; + + } + + } + +}; + + +// Export the THREE object for **Node.js**, with +// backwards-compatibility for the old `require()` API. If we're in +// the browser, add `_` as a global object via a string identifier, +// for Closure Compiler "advanced" mode. +if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = THREE; + } + exports.THREE = THREE; +} else { + this['THREE'] = THREE; +} + +},{}],6:[function(_dereq_,module,exports){ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof _dereq_=="function"&&_dereq_;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof _dereq_=="function"&&_dereq_;for(var o=0;o 0.0 && abs(vUV.x - 0.5) < .001) {', + // Don't render the divider, since it's rendered in HTML. + //'gl_FragColor = dividerColor;', + '} else if (a.x < 0.0 || a.x > 1.0 || a.y < 0.0 || a.y > 1.0) {', + 'gl_FragColor = backgroundColor;', + '} else {', + 'gl_FragColor = texture2D(texture, vec2(a.x * 0.5 + (vUV.x < 0.5 ? 0.0 : 0.5), a.y));', + '}', + '}' + + ].join('\n') +}; + +module.exports = BarrelDistortionFragment; + +},{}],6:[function(_dereq_,module,exports){ +/** + * TODO(smus): Implement coefficient inversion. + */ +function Distortion(coefficients) { + this.coefficients = coefficients; +} + +/** + * Calculates the inverse distortion for a radius. + *

+ * Allows to compute the original undistorted radius from a distorted one. + * See also getApproximateInverseDistortion() for a faster but potentially + * less accurate method. + * + * @param {Number} radius Distorted radius from the lens center in tan-angle units. + * @return {Number} The undistorted radius in tan-angle units. + */ +Distortion.prototype.distortInverse = function(radius) { + // Secant method. + var r0 = radius / 0.9; + var r1 = radius * 0.9; + var dr0 = radius - this.distort(r0); + while (Math.abs(r1 - r0) > 0.0001 /** 0.1mm */) { + var dr1 = radius - this.distort(r1); + var r2 = r1 - dr1 * ((r1 - r0) / (dr1 - dr0)); + r0 = r1; + r1 = r2; + dr0 = dr1; + } + return r1; +} + + +/** + * Distorts a radius by its distortion factor from the center of the lenses. + * + * @param {Number} radius Radius from the lens center in tan-angle units. + * @return {Number} The distorted radius in tan-angle units. + */ +Distortion.prototype.distort = function(radius) { + return radius * this.distortionFactor_(radius); +} + +/** + * Returns the distortion factor of a point. + * + * @param {Number} radius Radius of the point from the lens center in tan-angle units. + * @return {Number} The distortion factor. Multiply by this factor to distort points. + */ +Distortion.prototype.distortionFactor_ = function(radius) { + var result = 1.0; + var rFactor = 1.0; + var rSquared = radius * radius; + + for (var i = 0; i < this.coefficients.length; i++) { + var ki = this.coefficients[i]; + rFactor *= rSquared; + result += ki * rFactor; + } + + return result; +} + +module.exports = Distortion; + +},{}],7:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * DPDB cache. + */ +var DPDB_CACHE = { + "format": 1, + "last_updated": "2016-01-26T23:11:18Z", + "devices": [ + { + "type": "android", + "rules": [ + { "mdmh": "asus/*/Nexus 7/*" }, + { "ua": "Nexus 7" } + ], + "dpi": [ 320.8, 323.0 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "asus/*/ASUS_Z00AD/*" }, + { "ua": "ASUS_Z00AD" } + ], + "dpi": [ 403.0, 404.6 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "HTC/*/HTC6435LVW/*" }, + { "ua": "HTC6435LVW" } + ], + "dpi": [ 449.7, 443.3 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "HTC/*/HTC One XL/*" }, + { "ua": "HTC One XL" } + ], + "dpi": [ 315.3, 314.6 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "htc/*/Nexus 9/*" }, + { "ua": "Nexus 9" } + ], + "dpi": 289.0, + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "HTC/*/HTC One M9/*" }, + { "ua": "HTC One M9" } + ], + "dpi": [ 442.5, 443.3 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "HTC/*/HTC One_M8/*" }, + { "ua": "HTC One_M8" } + ], + "dpi": [ 449.7, 447.4 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "HTC/*/HTC One/*" }, + { "ua": "HTC One" } + ], + "dpi": 472.8, + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Huawei/*/Nexus 6P/*" }, + { "ua": "Nexus 6P" } + ], + "dpi": [ 515.1, 518.0 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/Nexus 5X/*" }, + { "ua": "Nexus 5X" } + ], + "dpi": [ 422.0, 419.9 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/LGMS345/*" }, + { "ua": "LGMS345" } + ], + "dpi": [ 221.7, 219.1 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/LG-D800/*" }, + { "ua": "LG-D800" } + ], + "dpi": [ 422.0, 424.1 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/LG-D850/*" }, + { "ua": "LG-D850" } + ], + "dpi": [ 537.9, 541.9 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/VS985 4G/*" }, + { "ua": "VS985 4G" } + ], + "dpi": [ 537.9, 535.6 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/Nexus 5/*" }, + { "ua": "Nexus 5 B" } + ], + "dpi": [ 442.4, 444.8 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/Nexus 4/*" }, + { "ua": "Nexus 4" } + ], + "dpi": [ 319.8, 318.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/LG-P769/*" }, + { "ua": "LG-P769" } + ], + "dpi": [ 240.6, 247.5 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/LGMS323/*" }, + { "ua": "LGMS323" } + ], + "dpi": [ 206.6, 204.6 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "LGE/*/LGLS996/*" }, + { "ua": "LGLS996" } + ], + "dpi": [ 403.4, 401.5 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Micromax/*/4560MMX/*" }, + { "ua": "4560MMX" } + ], + "dpi": [ 240.0, 219.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Micromax/*/A250/*" }, + { "ua": "Micromax A250" } + ], + "dpi": [ 480.0, 446.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Micromax/*/Micromax AQ4501/*" }, + { "ua": "Micromax AQ4501" } + ], + "dpi": 240.0, + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/DROID RAZR/*" }, + { "ua": "DROID RAZR" } + ], + "dpi": [ 368.1, 256.7 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT830C/*" }, + { "ua": "XT830C" } + ], + "dpi": [ 254.0, 255.9 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1021/*" }, + { "ua": "XT1021" } + ], + "dpi": [ 254.0, 256.7 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1023/*" }, + { "ua": "XT1023" } + ], + "dpi": [ 254.0, 256.7 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1028/*" }, + { "ua": "XT1028" } + ], + "dpi": [ 326.6, 327.6 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1034/*" }, + { "ua": "XT1034" } + ], + "dpi": [ 326.6, 328.4 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1053/*" }, + { "ua": "XT1053" } + ], + "dpi": [ 315.3, 316.1 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1562/*" }, + { "ua": "XT1562" } + ], + "dpi": [ 403.4, 402.7 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/Nexus 6/*" }, + { "ua": "Nexus 6 B" } + ], + "dpi": [ 494.3, 489.7 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1063/*" }, + { "ua": "XT1063" } + ], + "dpi": [ 295.0, 296.6 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1064/*" }, + { "ua": "XT1064" } + ], + "dpi": [ 295.0, 295.6 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1092/*" }, + { "ua": "XT1092" } + ], + "dpi": [ 422.0, 424.1 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "motorola/*/XT1095/*" }, + { "ua": "XT1095" } + ], + "dpi": [ 422.0, 423.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "OnePlus/*/A0001/*" }, + { "ua": "A0001" } + ], + "dpi": [ 403.4, 401.0 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "OnePlus/*/ONE E1005/*" }, + { "ua": "ONE E1005" } + ], + "dpi": [ 442.4, 441.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "OnePlus/*/ONE A2005/*" }, + { "ua": "ONE A2005" } + ], + "dpi": [ 391.9, 405.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "OPPO/*/X909/*" }, + { "ua": "X909" } + ], + "dpi": [ 442.4, 444.1 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/GT-I9082/*" }, + { "ua": "GT-I9082" } + ], + "dpi": [ 184.7, 185.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G360P/*" }, + { "ua": "SM-G360P" } + ], + "dpi": [ 196.7, 205.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/Nexus S/*" }, + { "ua": "Nexus S" } + ], + "dpi": [ 234.5, 229.8 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/GT-I9300/*" }, + { "ua": "GT-I9300" } + ], + "dpi": [ 304.8, 303.9 ], + "bw": 5, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-T230NU/*" }, + { "ua": "SM-T230NU" } + ], + "dpi": 216.0, + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SGH-T399/*" }, + { "ua": "SGH-T399" } + ], + "dpi": [ 217.7, 231.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-N9005/*" }, + { "ua": "SM-N9005" } + ], + "dpi": [ 386.4, 387.0 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SAMSUNG-SM-N900A/*" }, + { "ua": "SAMSUNG-SM-N900A" } + ], + "dpi": [ 386.4, 387.7 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/GT-I9500/*" }, + { "ua": "GT-I9500" } + ], + "dpi": [ 442.5, 443.3 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/GT-I9505/*" }, + { "ua": "GT-I9505" } + ], + "dpi": 439.4, + "bw": 4, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G900F/*" }, + { "ua": "SM-G900F" } + ], + "dpi": [ 415.6, 431.6 ], + "bw": 5, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G900M/*" }, + { "ua": "SM-G900M" } + ], + "dpi": [ 415.6, 431.6 ], + "bw": 5, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G800F/*" }, + { "ua": "SM-G800F" } + ], + "dpi": 326.8, + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G906S/*" }, + { "ua": "SM-G906S" } + ], + "dpi": [ 562.7, 572.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/GT-I9300/*" }, + { "ua": "GT-I9300" } + ], + "dpi": [ 306.7, 304.8 ], + "bw": 5, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-T535/*" }, + { "ua": "SM-T535" } + ], + "dpi": [ 142.6, 136.4 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-N920C/*" }, + { "ua": "SM-N920C" } + ], + "dpi": [ 515.1, 518.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/GT-I9300I/*" }, + { "ua": "GT-I9300I" } + ], + "dpi": [ 304.8, 305.8 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/GT-I9195/*" }, + { "ua": "GT-I9195" } + ], + "dpi": [ 249.4, 256.7 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SPH-L520/*" }, + { "ua": "SPH-L520" } + ], + "dpi": [ 249.4, 255.9 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SAMSUNG-SGH-I717/*" }, + { "ua": "SAMSUNG-SGH-I717" } + ], + "dpi": 285.8, + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SPH-D710/*" }, + { "ua": "SPH-D710" } + ], + "dpi": [ 217.7, 204.2 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/GT-N7100/*" }, + { "ua": "GT-N7100" } + ], + "dpi": 265.1, + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SCH-I605/*" }, + { "ua": "SCH-I605" } + ], + "dpi": 265.1, + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/Galaxy Nexus/*" }, + { "ua": "Galaxy Nexus" } + ], + "dpi": [ 315.3, 314.2 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-N910H/*" }, + { "ua": "SM-N910H" } + ], + "dpi": [ 515.1, 518.0 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-N910C/*" }, + { "ua": "SM-N910C" } + ], + "dpi": [ 515.2, 520.2 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G130M/*" }, + { "ua": "SM-G130M" } + ], + "dpi": [ 165.9, 164.8 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G928I/*" }, + { "ua": "SM-G928I" } + ], + "dpi": [ 515.1, 518.4 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G920F/*" }, + { "ua": "SM-G920F" } + ], + "dpi": 580.6, + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G920P/*" }, + { "ua": "SM-G920P" } + ], + "dpi": [ 522.5, 577.0 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G925F/*" }, + { "ua": "SM-G925F" } + ], + "dpi": 580.6, + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "samsung/*/SM-G925V/*" }, + { "ua": "SM-G925V" } + ], + "dpi": [ 522.5, 576.6 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Sony/*/C6903/*" }, + { "ua": "C6903" } + ], + "dpi": [ 442.5, 443.3 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Sony/*/D6653/*" }, + { "ua": "D6653" } + ], + "dpi": [ 428.6, 427.6 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Sony/*/E6653/*" }, + { "ua": "E6653" } + ], + "dpi": [ 428.6, 425.7 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Sony/*/E6853/*" }, + { "ua": "E6853" } + ], + "dpi": [ 403.4, 401.9 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "Sony/*/SGP321/*" }, + { "ua": "SGP321" } + ], + "dpi": [ 224.7, 224.1 ], + "bw": 3, + "ac": 500 + }, + { + "type": "android", + "rules": [ + { "mdmh": "TCT/*/ALCATEL ONE TOUCH Fierce/*" }, + { "ua": "ALCATEL ONE TOUCH Fierce" } + ], + "dpi": [ 240.0, 247.5 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "THL/*/thl 5000/*" }, + { "ua": "thl 5000" } + ], + "dpi": [ 480.0, 443.3 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "android", + "rules": [ + { "mdmh": "ZTE/*/ZTE Blade L2/*" }, + { "ua": "ZTE Blade L2" } + ], + "dpi": 240.0, + "bw": 3, + "ac": 500 + }, + { + "type": "ios", + "rules": [ { "res": [ 640, 960 ] } ], + "dpi": [ 325.1, 328.4 ], + "bw": 4, + "ac": 1000 + }, + { + "type": "ios", + "rules": [ { "res": [ 640, 1136 ] } ], + "dpi": [ 317.1, 320.2 ], + "bw": 3, + "ac": 1000 + }, + { + "type": "ios", + "rules": [ { "res": [ 750, 1334 ] } ], + "dpi": 326.4, + "bw": 4, + "ac": 1000 + }, + { + "type": "ios", + "rules": [ { "res": [ 1242, 2208 ] } ], + "dpi": [ 453.6, 458.4 ], + "bw": 4, + "ac": 1000 + }, + { + "type": "ios", + "rules": [ { "res": [ 1125, 2001 ] } ], + "dpi": [ 410.9, 415.4 ], + "bw": 4, + "ac": 1000 + } +]}; + +module.exports = DPDB_CACHE; + + +},{}],8:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Offline cache of the DPDB, to be used until we load the online one (and +// as a fallback in case we can't load the online one). +var DPDB_CACHE = _dereq_('./dpdb-cache.js'); +var Util = _dereq_('./util.js'); + +// Online DPDB URL. +var ONLINE_DPDB_URL = 'https://storage.googleapis.com/cardboard-dpdb/dpdb.json'; + +/** + * Calculates device parameters based on the DPDB (Device Parameter Database). + * Initially, uses the cached DPDB values. + * + * If fetchOnline == true, then this object tries to fetch the online version + * of the DPDB and updates the device info if a better match is found. + * Calls the onDeviceParamsUpdated callback when there is an update to the + * device information. + */ +function Dpdb(fetchOnline, onDeviceParamsUpdated) { + // Start with the offline DPDB cache while we are loading the real one. + this.dpdb = DPDB_CACHE; + + // Calculate device params based on the offline version of the DPDB. + this.recalculateDeviceParams_(); + + // XHR to fetch online DPDB file, if requested. + if (fetchOnline) { + // Set the callback. + this.onDeviceParamsUpdated = onDeviceParamsUpdated; + + console.log('Fetching DPDB...'); + var xhr = new XMLHttpRequest(); + var obj = this; + xhr.open('GET', ONLINE_DPDB_URL, true); + xhr.addEventListener('load', function() { + obj.loading = false; + if (xhr.status >= 200 && xhr.status <= 299) { + // Success. + console.log('Successfully loaded online DPDB.'); + obj.dpdb = JSON.parse(xhr.response); + obj.recalculateDeviceParams_(); + } else { + // Error loading the DPDB. + console.error('Error loading online DPDB!'); + } + }); + xhr.send(); + } +} + +// Returns the current device parameters. +Dpdb.prototype.getDeviceParams = function() { + return this.deviceParams; +}; + +// Recalculates this device's parameters based on the DPDB. +Dpdb.prototype.recalculateDeviceParams_ = function() { + console.log('Recalculating device params.'); + var newDeviceParams = this.calcDeviceParams_(); + console.log('New device parameters:'); + console.log(newDeviceParams); + if (newDeviceParams) { + this.deviceParams = newDeviceParams; + // Invoke callback, if it is set. + if (this.onDeviceParamsUpdated) { + this.onDeviceParamsUpdated(this.deviceParams); + } + } else { + console.warn('Failed to recalculate device parameters.'); + } +}; + +// Returns a DeviceParams object that represents the best guess as to this +// device's parameters. Can return null if the device does not match any +// known devices. +Dpdb.prototype.calcDeviceParams_ = function() { + var db = this.dpdb; // shorthand + if (!db) { + console.error('DPDB not available.'); + return null; + } + if (db.format != 1) { + console.error('DPDB has unexpected format version.'); + return null; + } + if (!db.devices || !db.devices.length) { + console.error('DPDB does not have a devices section.'); + return null; + } + + // Get the actual user agent and screen dimensions in pixels. + var userAgent = navigator.userAgent || navigator.vendor || window.opera; + var width = Util.getScreenWidth(); + var height = Util.getScreenHeight(); + console.log('User agent: ' + userAgent); + console.log('Pixel width: ' + width); + console.log('Pixel height: ' + height); + + if (!db.devices) { + console.error('DPDB has no devices section.'); + return null; + } + + for (var i = 0; i < db.devices.length; i++) { + var device = db.devices[i]; + if (!device.rules) { + console.warn('Device[' + i + '] has no rules section.'); + continue; + } + + if (device.type != 'ios' && device.type != 'android') { + console.warn('Device[' + i + '] has invalid type.'); + continue; + } + + // See if this device is of the appropriate type. + if (Util.isIOS() != (device.type == 'ios')) continue; + + // See if this device matches any of the rules: + var matched = false; + for (var j = 0; j < device.rules.length; j++) { + var rule = device.rules[j]; + if (this.matchRule_(rule, userAgent, width, height)) { + console.log('Rule matched:'); + console.log(rule); + matched = true; + break; + } + } + if (!matched) continue; + + // device.dpi might be an array of [ xdpi, ydpi] or just a scalar. + var xdpi = device.dpi[0] || device.dpi; + var ydpi = device.dpi[1] || device.dpi; + + return new DeviceParams({ xdpi: xdpi, ydpi: ydpi, bevelMm: device.bw }); + } + + console.warn('No DPDB device match.'); + return null; +}; + +Dpdb.prototype.matchRule_ = function(rule, ua, screenWidth, screenHeight) { + // We can only match 'ua' and 'res' rules, not other types like 'mdmh' + // (which are meant for native platforms). + if (!rule.ua && !rule.res) return false; + + // If our user agent string doesn't contain the indicated user agent string, + // the match fails. + if (rule.ua && ua.indexOf(rule.ua) < 0) return false; + + // If the rule specifies screen dimensions that don't correspond to ours, + // the match fails. + if (rule.res) { + if (!rule.res[0] || !rule.res[1]) return false; + var resX = rule.res[0]; + var resY = rule.res[1]; + // Compare min and max so as to make the order not matter, i.e., it should + // be true that 640x480 == 480x640. + if (Math.min(screenWidth, screenHeight) != Math.min(resX, resY) || + (Math.max(screenWidth, screenHeight) != Math.max(resX, resY))) { + return false; + } + } + + return true; +} + +function DeviceParams(params) { + this.xdpi = params.xdpi; + this.ydpi = params.ydpi; + this.bevelMm = params.bevelMm; +} + +module.exports = Dpdb; + +},{"./dpdb-cache.js":7,"./util.js":13}],9:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function Emitter() { + this.callbacks = {}; +} + +Emitter.prototype.emit = function(eventName) { + var callbacks = this.callbacks[eventName]; + if (!callbacks) { + //console.log('No valid callback specified.'); + return; + } + var args = [].slice.call(arguments); + // Eliminate the first param (the callback). + args.shift(); + for (var i = 0; i < callbacks.length; i++) { + callbacks[i].apply(this, args); + } +}; + +Emitter.prototype.on = function(eventName, callback) { + if (eventName in this.callbacks) { + this.callbacks[eventName].push(callback); + } else { + this.callbacks[eventName] = [callback]; + } +}; + +module.exports = Emitter; + +},{}],10:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var WebVRManager = _dereq_('./webvr-manager.js'); + +window.WebVRConfig = window.WebVRConfig || {}; +window.WebVRManager = WebVRManager; + +},{"./webvr-manager.js":16}],11:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var Modes = { + UNKNOWN: 0, + // Not fullscreen, just tracking. + NORMAL: 1, + // Magic window immersive mode. + MAGIC_WINDOW: 2, + // Full screen split screen VR mode. + VR: 3, +}; + +module.exports = Modes; + +},{}],12:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var Util = _dereq_('./util.js'); + +function RotateInstructions() { + this.loadIcon_(); + + var overlay = document.createElement('div'); + var s = overlay.style; + s.position = 'fixed'; + s.top = 0; + s.right = 0; + s.bottom = 0; + s.left = 0; + s.backgroundColor = 'gray'; + s.fontFamily = 'sans-serif'; + + var img = document.createElement('img'); + img.src = this.icon; + var s = img.style; + s.marginLeft = '25%'; + s.marginTop = '25%'; + s.width = '50%'; + overlay.appendChild(img); + + var text = document.createElement('div'); + var s = text.style; + s.textAlign = 'center'; + s.fontSize = '16px'; + s.lineHeight = '24px'; + s.margin = '24px 25%'; + s.width = '50%'; + text.innerHTML = 'Place your phone into your Cardboard viewer.'; + overlay.appendChild(text); + + var snackbar = document.createElement('div'); + var s = snackbar.style; + s.backgroundColor = '#CFD8DC'; + s.position = 'fixed'; + s.bottom = 0; + s.width = '100%'; + s.height = '48px'; + s.padding = '14px 24px'; + s.boxSizing = 'border-box'; + s.color = '#656A6B'; + overlay.appendChild(snackbar); + + var snackbarText = document.createElement('div'); + snackbarText.style.float = 'left'; + snackbarText.innerHTML = 'No Cardboard viewer?'; + + var snackbarButton = document.createElement('a'); + snackbarButton.href = 'https://www.google.com/get/cardboard/get-cardboard/'; + snackbarButton.innerHTML = 'get one'; + snackbarButton.target = '_blank'; + var s = snackbarButton.style; + s.float = 'right'; + s.fontWeight = 600; + s.textTransform = 'uppercase'; + s.borderLeft = '1px solid gray'; + s.paddingLeft = '24px'; + s.textDecoration = 'none'; + s.color = '#656A6B'; + + snackbar.appendChild(snackbarText); + snackbar.appendChild(snackbarButton); + + this.overlay = overlay; + this.text = text; + document.body.appendChild(overlay); + + this.hide(); +} + +RotateInstructions.prototype.show = function() { + this.overlay.style.display = 'block'; + + var img = this.overlay.querySelector('img'); + var s = img.style; + + if (Util.isLandscapeMode()) { + s.width = '20%'; + s.marginLeft = '40%'; + s.marginTop = '3%'; + } else { + s.width = '50%'; + s.marginLeft = '25%'; + s.marginTop = '25%'; + } +}; + +RotateInstructions.prototype.hide = function() { + this.overlay.style.display = 'none'; +}; + +RotateInstructions.prototype.showTemporarily = function(ms) { + this.show(); + this.timer = setTimeout(this.hide.bind(this), ms); +}; + +RotateInstructions.prototype.disableShowTemporarily = function() { + clearTimeout(this.timer); +}; + +RotateInstructions.prototype.loadIcon_ = function() { + // Encoded asset_src/rotate-instructions.svg + this.icon = Util.base64('image/svg+xml', 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjE5OHB4IiBoZWlnaHQ9IjI0MHB4IiB2aWV3Qm94PSIwIDAgMTk4IDI0MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxuczpza2V0Y2g9Imh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaC9ucyI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDMuMy4zICgxMjA4MSkgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+dHJhbnNpdGlvbjwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPjwvZGVmcz4KICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHNrZXRjaDp0eXBlPSJNU1BhZ2UiPgogICAgICAgIDxnIGlkPSJ0cmFuc2l0aW9uIiBza2V0Y2g6dHlwZT0iTVNBcnRib2FyZEdyb3VwIj4KICAgICAgICAgICAgPGcgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTQtKy1JbXBvcnRlZC1MYXllcnMtQ29weS0rLUltcG9ydGVkLUxheWVycy1Db3B5LTItQ29weSIgc2tldGNoOnR5cGU9Ik1TTGF5ZXJHcm91cCI+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHktNCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDEwNy4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjYyNSwyLjUyNyBDMTQ5LjYyNSwyLjUyNyAxNTUuODA1LDYuMDk2IDE1Ni4zNjIsNi40MTggTDE1Ni4zNjIsNy4zMDQgQzE1Ni4zNjIsNy40ODEgMTU2LjM3NSw3LjY2NCAxNTYuNCw3Ljg1MyBDMTU2LjQxLDcuOTM0IDE1Ni40Miw4LjAxNSAxNTYuNDI3LDguMDk1IEMxNTYuNTY3LDkuNTEgMTU3LjQwMSwxMS4wOTMgMTU4LjUzMiwxMi4wOTQgTDE2NC4yNTIsMTcuMTU2IEwxNjQuMzMzLDE3LjA2NiBDMTY0LjMzMywxNy4wNjYgMTY4LjcxNSwxNC41MzYgMTY5LjU2OCwxNC4wNDIgQzE3MS4wMjUsMTQuODgzIDE5NS41MzgsMjkuMDM1IDE5NS41MzgsMjkuMDM1IEwxOTUuNTM4LDgzLjAzNiBDMTk1LjUzOCw4My44MDcgMTk1LjE1Miw4NC4yNTMgMTk0LjU5LDg0LjI1MyBDMTk0LjM1Nyw4NC4yNTMgMTk0LjA5NSw4NC4xNzcgMTkzLjgxOCw4NC4wMTcgTDE2OS44NTEsNzAuMTc5IEwxNjkuODM3LDcwLjIwMyBMMTQyLjUxNSw4NS45NzggTDE0MS42NjUsODQuNjU1IEMxMzYuOTM0LDgzLjEyNiAxMzEuOTE3LDgxLjkxNSAxMjYuNzE0LDgxLjA0NSBDMTI2LjcwOSw4MS4wNiAxMjYuNzA3LDgxLjA2OSAxMjYuNzA3LDgxLjA2OSBMMTIxLjY0LDk4LjAzIEwxMTMuNzQ5LDEwMi41ODYgTDExMy43MTIsMTAyLjUyMyBMMTEzLjcxMiwxMzAuMTEzIEMxMTMuNzEyLDEzMC44ODUgMTEzLjMyNiwxMzEuMzMgMTEyLjc2NCwxMzEuMzMgQzExMi41MzIsMTMxLjMzIDExMi4yNjksMTMxLjI1NCAxMTEuOTkyLDEzMS4wOTQgTDY5LjUxOSwxMDYuNTcyIEM2OC41NjksMTA2LjAyMyA2Ny43OTksMTA0LjY5NSA2Ny43OTksMTAzLjYwNSBMNjcuNzk5LDEwMi41NyBMNjcuNzc4LDEwMi42MTcgQzY3LjI3LDEwMi4zOTMgNjYuNjQ4LDEwMi4yNDkgNjUuOTYyLDEwMi4yMTggQzY1Ljg3NSwxMDIuMjE0IDY1Ljc4OCwxMDIuMjEyIDY1LjcwMSwxMDIuMjEyIEM2NS42MDYsMTAyLjIxMiA2NS41MTEsMTAyLjIxNSA2NS40MTYsMTAyLjIxOSBDNjUuMTk1LDEwMi4yMjkgNjQuOTc0LDEwMi4yMzUgNjQuNzU0LDEwMi4yMzUgQzY0LjMzMSwxMDIuMjM1IDYzLjkxMSwxMDIuMjE2IDYzLjQ5OCwxMDIuMTc4IEM2MS44NDMsMTAyLjAyNSA2MC4yOTgsMTAxLjU3OCA1OS4wOTQsMTAwLjg4MiBMMTIuNTE4LDczLjk5MiBMMTIuNTIzLDc0LjAwNCBMMi4yNDUsNTUuMjU0IEMxLjI0NCw1My40MjcgMi4wMDQsNTEuMDM4IDMuOTQzLDQ5LjkxOCBMNTkuOTU0LDE3LjU3MyBDNjAuNjI2LDE3LjE4NSA2MS4zNSwxNy4wMDEgNjIuMDUzLDE3LjAwMSBDNjMuMzc5LDE3LjAwMSA2NC42MjUsMTcuNjYgNjUuMjgsMTguODU0IEw2NS4yODUsMTguODUxIEw2NS41MTIsMTkuMjY0IEw2NS41MDYsMTkuMjY4IEM2NS45MDksMjAuMDAzIDY2LjQwNSwyMC42OCA2Ni45ODMsMjEuMjg2IEw2Ny4yNiwyMS41NTYgQzY5LjE3NCwyMy40MDYgNzEuNzI4LDI0LjM1NyA3NC4zNzMsMjQuMzU3IEM3Ni4zMjIsMjQuMzU3IDc4LjMyMSwyMy44NCA4MC4xNDgsMjIuNzg1IEM4MC4xNjEsMjIuNzg1IDg3LjQ2NywxOC41NjYgODcuNDY3LDE4LjU2NiBDODguMTM5LDE4LjE3OCA4OC44NjMsMTcuOTk0IDg5LjU2NiwxNy45OTQgQzkwLjg5MiwxNy45OTQgOTIuMTM4LDE4LjY1MiA5Mi43OTIsMTkuODQ3IEw5Ni4wNDIsMjUuNzc1IEw5Ni4wNjQsMjUuNzU3IEwxMDIuODQ5LDI5LjY3NCBMMTAyLjc0NCwyOS40OTIgTDE0OS42MjUsMi41MjcgTTE0OS42MjUsMC44OTIgQzE0OS4zNDMsMC44OTIgMTQ5LjA2MiwwLjk2NSAxNDguODEsMS4xMSBMMTAyLjY0MSwyNy42NjYgTDk3LjIzMSwyNC41NDIgTDk0LjIyNiwxOS4wNjEgQzkzLjMxMywxNy4zOTQgOTEuNTI3LDE2LjM1OSA4OS41NjYsMTYuMzU4IEM4OC41NTUsMTYuMzU4IDg3LjU0NiwxNi42MzIgODYuNjQ5LDE3LjE1IEM4My44NzgsMTguNzUgNzkuNjg3LDIxLjE2OSA3OS4zNzQsMjEuMzQ1IEM3OS4zNTksMjEuMzUzIDc5LjM0NSwyMS4zNjEgNzkuMzMsMjEuMzY5IEM3Ny43OTgsMjIuMjU0IDc2LjA4NCwyMi43MjIgNzQuMzczLDIyLjcyMiBDNzIuMDgxLDIyLjcyMiA2OS45NTksMjEuODkgNjguMzk3LDIwLjM4IEw2OC4xNDUsMjAuMTM1IEM2Ny43MDYsMTkuNjcyIDY3LjMyMywxOS4xNTYgNjcuMDA2LDE4LjYwMSBDNjYuOTg4LDE4LjU1OSA2Ni45NjgsMTguNTE5IDY2Ljk0NiwxOC40NzkgTDY2LjcxOSwxOC4wNjUgQzY2LjY5LDE4LjAxMiA2Ni42NTgsMTcuOTYgNjYuNjI0LDE3LjkxMSBDNjUuNjg2LDE2LjMzNyA2My45NTEsMTUuMzY2IDYyLjA1MywxNS4zNjYgQzYxLjA0MiwxNS4zNjYgNjAuMDMzLDE1LjY0IDU5LjEzNiwxNi4xNTggTDMuMTI1LDQ4LjUwMiBDMC40MjYsNTAuMDYxIC0wLjYxMyw1My40NDIgMC44MTEsNTYuMDQgTDExLjA4OSw3NC43OSBDMTEuMjY2LDc1LjExMyAxMS41MzcsNzUuMzUzIDExLjg1LDc1LjQ5NCBMNTguMjc2LDEwMi4yOTggQzU5LjY3OSwxMDMuMTA4IDYxLjQzMywxMDMuNjMgNjMuMzQ4LDEwMy44MDYgQzYzLjgxMiwxMDMuODQ4IDY0LjI4NSwxMDMuODcgNjQuNzU0LDEwMy44NyBDNjUsMTAzLjg3IDY1LjI0OSwxMDMuODY0IDY1LjQ5NCwxMDMuODUyIEM2NS41NjMsMTAzLjg0OSA2NS42MzIsMTAzLjg0NyA2NS43MDEsMTAzLjg0NyBDNjUuNzY0LDEwMy44NDcgNjUuODI4LDEwMy44NDkgNjUuODksMTAzLjg1MiBDNjUuOTg2LDEwMy44NTYgNjYuMDgsMTAzLjg2MyA2Ni4xNzMsMTAzLjg3NCBDNjYuMjgyLDEwNS40NjcgNjcuMzMyLDEwNy4xOTcgNjguNzAyLDEwNy45ODggTDExMS4xNzQsMTMyLjUxIEMxMTEuNjk4LDEzMi44MTIgMTEyLjIzMiwxMzIuOTY1IDExMi43NjQsMTMyLjk2NSBDMTE0LjI2MSwxMzIuOTY1IDExNS4zNDcsMTMxLjc2NSAxMTUuMzQ3LDEzMC4xMTMgTDExNS4zNDcsMTAzLjU1MSBMMTIyLjQ1OCw5OS40NDYgQzEyMi44MTksOTkuMjM3IDEyMy4wODcsOTguODk4IDEyMy4yMDcsOTguNDk4IEwxMjcuODY1LDgyLjkwNSBDMTMyLjI3OSw4My43MDIgMTM2LjU1Nyw4NC43NTMgMTQwLjYwNyw4Ni4wMzMgTDE0MS4xNCw4Ni44NjIgQzE0MS40NTEsODcuMzQ2IDE0MS45NzcsODcuNjEzIDE0Mi41MTYsODcuNjEzIEMxNDIuNzk0LDg3LjYxMyAxNDMuMDc2LDg3LjU0MiAxNDMuMzMzLDg3LjM5MyBMMTY5Ljg2NSw3Mi4wNzYgTDE5Myw4NS40MzMgQzE5My41MjMsODUuNzM1IDE5NC4wNTgsODUuODg4IDE5NC41OSw4NS44ODggQzE5Ni4wODcsODUuODg4IDE5Ny4xNzMsODQuNjg5IDE5Ny4xNzMsODMuMDM2IEwxOTcuMTczLDI5LjAzNSBDMTk3LjE3MywyOC40NTEgMTk2Ljg2MSwyNy45MTEgMTk2LjM1NSwyNy42MTkgQzE5Ni4zNTUsMjcuNjE5IDE3MS44NDMsMTMuNDY3IDE3MC4zODUsMTIuNjI2IEMxNzAuMTMyLDEyLjQ4IDE2OS44NSwxMi40MDcgMTY5LjU2OCwxMi40MDcgQzE2OS4yODUsMTIuNDA3IDE2OS4wMDIsMTIuNDgxIDE2OC43NDksMTIuNjI3IEMxNjguMTQzLDEyLjk3OCAxNjUuNzU2LDE0LjM1NyAxNjQuNDI0LDE1LjEyNSBMMTU5LjYxNSwxMC44NyBDMTU4Ljc5NiwxMC4xNDUgMTU4LjE1NCw4LjkzNyAxNTguMDU0LDcuOTM0IEMxNTguMDQ1LDcuODM3IDE1OC4wMzQsNy43MzkgMTU4LjAyMSw3LjY0IEMxNTguMDA1LDcuNTIzIDE1Ny45OTgsNy40MSAxNTcuOTk4LDcuMzA0IEwxNTcuOTk4LDYuNDE4IEMxNTcuOTk4LDUuODM0IDE1Ny42ODYsNS4yOTUgMTU3LjE4MSw1LjAwMiBDMTU2LjYyNCw0LjY4IDE1MC40NDIsMS4xMTEgMTUwLjQ0MiwxLjExMSBDMTUwLjE4OSwwLjk2NSAxNDkuOTA3LDAuODkyIDE0OS42MjUsMC44OTIiIGlkPSJGaWxsLTEiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTYuMDI3LDI1LjYzNiBMMTQyLjYwMyw1Mi41MjcgQzE0My44MDcsNTMuMjIyIDE0NC41ODIsNTQuMTE0IDE0NC44NDUsNTUuMDY4IEwxNDQuODM1LDU1LjA3NSBMNjMuNDYxLDEwMi4wNTcgTDYzLjQ2LDEwMi4wNTcgQzYxLjgwNiwxMDEuOTA1IDYwLjI2MSwxMDEuNDU3IDU5LjA1NywxMDAuNzYyIEwxMi40ODEsNzMuODcxIEw5Ni4wMjcsMjUuNjM2IiBpZD0iRmlsbC0yIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYzLjQ2MSwxMDIuMTc0IEM2My40NTMsMTAyLjE3NCA2My40NDYsMTAyLjE3NCA2My40MzksMTAyLjE3MiBDNjEuNzQ2LDEwMi4wMTYgNjAuMjExLDEwMS41NjMgNTguOTk4LDEwMC44NjMgTDEyLjQyMiw3My45NzMgQzEyLjM4Niw3My45NTIgMTIuMzY0LDczLjkxNCAxMi4zNjQsNzMuODcxIEMxMi4zNjQsNzMuODMgMTIuMzg2LDczLjc5MSAxMi40MjIsNzMuNzcgTDk1Ljk2OCwyNS41MzUgQzk2LjAwNCwyNS41MTQgOTYuMDQ5LDI1LjUxNCA5Ni4wODUsMjUuNTM1IEwxNDIuNjYxLDUyLjQyNiBDMTQzLjg4OCw1My4xMzQgMTQ0LjY4Miw1NC4wMzggMTQ0Ljk1Nyw1NS4wMzcgQzE0NC45Nyw1NS4wODMgMTQ0Ljk1Myw1NS4xMzMgMTQ0LjkxNSw1NS4xNjEgQzE0NC45MTEsNTUuMTY1IDE0NC44OTgsNTUuMTc0IDE0NC44OTQsNTUuMTc3IEw2My41MTksMTAyLjE1OCBDNjMuNTAxLDEwMi4xNjkgNjMuNDgxLDEwMi4xNzQgNjMuNDYxLDEwMi4xNzQgTDYzLjQ2MSwxMDIuMTc0IFogTTEyLjcxNCw3My44NzEgTDU5LjExNSwxMDAuNjYxIEM2MC4yOTMsMTAxLjM0MSA2MS43ODYsMTAxLjc4MiA2My40MzUsMTAxLjkzNyBMMTQ0LjcwNyw1NS4wMTUgQzE0NC40MjgsNTQuMTA4IDE0My42ODIsNTMuMjg1IDE0Mi41NDQsNTIuNjI4IEw5Ni4wMjcsMjUuNzcxIEwxMi43MTQsNzMuODcxIEwxMi43MTQsNzMuODcxIFoiIGlkPSJGaWxsLTMiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ4LjMyNyw1OC40NzEgQzE0OC4xNDUsNTguNDggMTQ3Ljk2Miw1OC40OCAxNDcuNzgxLDU4LjQ3MiBDMTQ1Ljg4Nyw1OC4zODkgMTQ0LjQ3OSw1Ny40MzQgMTQ0LjYzNiw1Ni4zNCBDMTQ0LjY4OSw1NS45NjcgMTQ0LjY2NCw1NS41OTcgMTQ0LjU2NCw1NS4yMzUgTDYzLjQ2MSwxMDIuMDU3IEM2NC4wODksMTAyLjExNSA2NC43MzMsMTAyLjEzIDY1LjM3OSwxMDIuMDk5IEM2NS41NjEsMTAyLjA5IDY1Ljc0MywxMDIuMDkgNjUuOTI1LDEwMi4wOTggQzY3LjgxOSwxMDIuMTgxIDY5LjIyNywxMDMuMTM2IDY5LjA3LDEwNC4yMyBMMTQ4LjMyNyw1OC40NzEiIGlkPSJGaWxsLTQiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNjkuMDcsMTA0LjM0NyBDNjkuMDQ4LDEwNC4zNDcgNjkuMDI1LDEwNC4zNCA2OS4wMDUsMTA0LjMyNyBDNjguOTY4LDEwNC4zMDEgNjguOTQ4LDEwNC4yNTcgNjguOTU1LDEwNC4yMTMgQzY5LDEwMy44OTYgNjguODk4LDEwMy41NzYgNjguNjU4LDEwMy4yODggQzY4LjE1MywxMDIuNjc4IDY3LjEwMywxMDIuMjY2IDY1LjkyLDEwMi4yMTQgQzY1Ljc0MiwxMDIuMjA2IDY1LjU2MywxMDIuMjA3IDY1LjM4NSwxMDIuMjE1IEM2NC43NDIsMTAyLjI0NiA2NC4wODcsMTAyLjIzMiA2My40NSwxMDIuMTc0IEM2My4zOTksMTAyLjE2OSA2My4zNTgsMTAyLjEzMiA2My4zNDcsMTAyLjA4MiBDNjMuMzM2LDEwMi4wMzMgNjMuMzU4LDEwMS45ODEgNjMuNDAyLDEwMS45NTYgTDE0NC41MDYsNTUuMTM0IEMxNDQuNTM3LDU1LjExNiAxNDQuNTc1LDU1LjExMyAxNDQuNjA5LDU1LjEyNyBDMTQ0LjY0Miw1NS4xNDEgMTQ0LjY2OCw1NS4xNyAxNDQuNjc3LDU1LjIwNCBDMTQ0Ljc4MSw1NS41ODUgMTQ0LjgwNiw1NS45NzIgMTQ0Ljc1MSw1Ni4zNTcgQzE0NC43MDYsNTYuNjczIDE0NC44MDgsNTYuOTk0IDE0NS4wNDcsNTcuMjgyIEMxNDUuNTUzLDU3Ljg5MiAxNDYuNjAyLDU4LjMwMyAxNDcuNzg2LDU4LjM1NSBDMTQ3Ljk2NCw1OC4zNjMgMTQ4LjE0Myw1OC4zNjMgMTQ4LjMyMSw1OC4zNTQgQzE0OC4zNzcsNTguMzUyIDE0OC40MjQsNTguMzg3IDE0OC40MzksNTguNDM4IEMxNDguNDU0LDU4LjQ5IDE0OC40MzIsNTguNTQ1IDE0OC4zODUsNTguNTcyIEw2OS4xMjksMTA0LjMzMSBDNjkuMTExLDEwNC4zNDIgNjkuMDksMTA0LjM0NyA2OS4wNywxMDQuMzQ3IEw2OS4wNywxMDQuMzQ3IFogTTY1LjY2NSwxMDEuOTc1IEM2NS43NTQsMTAxLjk3NSA2NS44NDIsMTAxLjk3NyA2NS45MywxMDEuOTgxIEM2Ny4xOTYsMTAyLjAzNyA2OC4yODMsMTAyLjQ2OSA2OC44MzgsMTAzLjEzOSBDNjkuMDY1LDEwMy40MTMgNjkuMTg4LDEwMy43MTQgNjkuMTk4LDEwNC4wMjEgTDE0Ny44ODMsNTguNTkyIEMxNDcuODQ3LDU4LjU5MiAxNDcuODExLDU4LjU5MSAxNDcuNzc2LDU4LjU4OSBDMTQ2LjUwOSw1OC41MzMgMTQ1LjQyMiw1OC4xIDE0NC44NjcsNTcuNDMxIEMxNDQuNTg1LDU3LjA5MSAxNDQuNDY1LDU2LjcwNyAxNDQuNTIsNTYuMzI0IEMxNDQuNTYzLDU2LjAyMSAxNDQuNTUyLDU1LjcxNiAxNDQuNDg4LDU1LjQxNCBMNjMuODQ2LDEwMS45NyBDNjQuMzUzLDEwMi4wMDIgNjQuODY3LDEwMi4wMDYgNjUuMzc0LDEwMS45ODIgQzY1LjQ3MSwxMDEuOTc3IDY1LjU2OCwxMDEuOTc1IDY1LjY2NSwxMDEuOTc1IEw2NS42NjUsMTAxLjk3NSBaIiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTIuMjA4LDU1LjEzNCBDMS4yMDcsNTMuMzA3IDEuOTY3LDUwLjkxNyAzLjkwNiw0OS43OTcgTDU5LjkxNywxNy40NTMgQzYxLjg1NiwxNi4zMzMgNjQuMjQxLDE2LjkwNyA2NS4yNDMsMTguNzM0IEw2NS40NzUsMTkuMTQ0IEM2NS44NzIsMTkuODgyIDY2LjM2OCwyMC41NiA2Ni45NDUsMjEuMTY1IEw2Ny4yMjMsMjEuNDM1IEM3MC41NDgsMjQuNjQ5IDc1LjgwNiwyNS4xNTEgODAuMTExLDIyLjY2NSBMODcuNDMsMTguNDQ1IEM4OS4zNywxNy4zMjYgOTEuNzU0LDE3Ljg5OSA5Mi43NTUsMTkuNzI3IEw5Ni4wMDUsMjUuNjU1IEwxMi40ODYsNzMuODg0IEwyLjIwOCw1NS4xMzQgWiIgaWQ9IkZpbGwtNiIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMi40ODYsNzQuMDAxIEMxMi40NzYsNzQuMDAxIDEyLjQ2NSw3My45OTkgMTIuNDU1LDczLjk5NiBDMTIuNDI0LDczLjk4OCAxMi4zOTksNzMuOTY3IDEyLjM4NCw3My45NCBMMi4xMDYsNTUuMTkgQzEuMDc1LDUzLjMxIDEuODU3LDUwLjg0NSAzLjg0OCw0OS42OTYgTDU5Ljg1OCwxNy4zNTIgQzYwLjUyNSwxNi45NjcgNjEuMjcxLDE2Ljc2NCA2Mi4wMTYsMTYuNzY0IEM2My40MzEsMTYuNzY0IDY0LjY2NiwxNy40NjYgNjUuMzI3LDE4LjY0NiBDNjUuMzM3LDE4LjY1NCA2NS4zNDUsMTguNjYzIDY1LjM1MSwxOC42NzQgTDY1LjU3OCwxOS4wODggQzY1LjU4NCwxOS4xIDY1LjU4OSwxOS4xMTIgNjUuNTkxLDE5LjEyNiBDNjUuOTg1LDE5LjgzOCA2Ni40NjksMjAuNDk3IDY3LjAzLDIxLjA4NSBMNjcuMzA1LDIxLjM1MSBDNjkuMTUxLDIzLjEzNyA3MS42NDksMjQuMTIgNzQuMzM2LDI0LjEyIEM3Ni4zMTMsMjQuMTIgNzguMjksMjMuNTgyIDgwLjA1MywyMi41NjMgQzgwLjA2NCwyMi41NTcgODAuMDc2LDIyLjU1MyA4MC4wODgsMjIuNTUgTDg3LjM3MiwxOC4zNDQgQzg4LjAzOCwxNy45NTkgODguNzg0LDE3Ljc1NiA4OS41MjksMTcuNzU2IEM5MC45NTYsMTcuNzU2IDkyLjIwMSwxOC40NzIgOTIuODU4LDE5LjY3IEw5Ni4xMDcsMjUuNTk5IEM5Ni4xMzgsMjUuNjU0IDk2LjExOCwyNS43MjQgOTYuMDYzLDI1Ljc1NiBMMTIuNTQ1LDczLjk4NSBDMTIuNTI2LDczLjk5NiAxMi41MDYsNzQuMDAxIDEyLjQ4Niw3NC4wMDEgTDEyLjQ4Niw3NC4wMDEgWiBNNjIuMDE2LDE2Ljk5NyBDNjEuMzEyLDE2Ljk5NyA2MC42MDYsMTcuMTkgNTkuOTc1LDE3LjU1NCBMMy45NjUsNDkuODk5IEMyLjA4Myw1MC45ODUgMS4zNDEsNTMuMzA4IDIuMzEsNTUuMDc4IEwxMi41MzEsNzMuNzIzIEw5NS44NDgsMjUuNjExIEw5Mi42NTMsMTkuNzgyIEM5Mi4wMzgsMTguNjYgOTAuODcsMTcuOTkgODkuNTI5LDE3Ljk5IEM4OC44MjUsMTcuOTkgODguMTE5LDE4LjE4MiA4Ny40ODksMTguNTQ3IEw4MC4xNzIsMjIuNzcyIEM4MC4xNjEsMjIuNzc4IDgwLjE0OSwyMi43ODIgODAuMTM3LDIyLjc4NSBDNzguMzQ2LDIzLjgxMSA3Ni4zNDEsMjQuMzU0IDc0LjMzNiwyNC4zNTQgQzcxLjU4OCwyNC4zNTQgNjkuMDMzLDIzLjM0NyA2Ny4xNDIsMjEuNTE5IEw2Ni44NjQsMjEuMjQ5IEM2Ni4yNzcsMjAuNjM0IDY1Ljc3NCwxOS45NDcgNjUuMzY3LDE5LjIwMyBDNjUuMzYsMTkuMTkyIDY1LjM1NiwxOS4xNzkgNjUuMzU0LDE5LjE2NiBMNjUuMTYzLDE4LjgxOSBDNjUuMTU0LDE4LjgxMSA2NS4xNDYsMTguODAxIDY1LjE0LDE4Ljc5IEM2NC41MjUsMTcuNjY3IDYzLjM1NywxNi45OTcgNjIuMDE2LDE2Ljk5NyBMNjIuMDE2LDE2Ljk5NyBaIiBpZD0iRmlsbC03IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTQyLjQzNCw0OC44MDggTDQyLjQzNCw0OC44MDggQzM5LjkyNCw0OC44MDcgMzcuNzM3LDQ3LjU1IDM2LjU4Miw0NS40NDMgQzM0Ljc3MSw0Mi4xMzkgMzYuMTQ0LDM3LjgwOSAzOS42NDEsMzUuNzg5IEw1MS45MzIsMjguNjkxIEM1My4xMDMsMjguMDE1IDU0LjQxMywyNy42NTggNTUuNzIxLDI3LjY1OCBDNTguMjMxLDI3LjY1OCA2MC40MTgsMjguOTE2IDYxLjU3MywzMS4wMjMgQzYzLjM4NCwzNC4zMjcgNjIuMDEyLDM4LjY1NyA1OC41MTQsNDAuNjc3IEw0Ni4yMjMsNDcuNzc1IEM0NS4wNTMsNDguNDUgNDMuNzQyLDQ4LjgwOCA0Mi40MzQsNDguODA4IEw0Mi40MzQsNDguODA4IFogTTU1LjcyMSwyOC4xMjUgQzU0LjQ5NSwyOC4xMjUgNTMuMjY1LDI4LjQ2MSA1Mi4xNjYsMjkuMDk2IEwzOS44NzUsMzYuMTk0IEMzNi41OTYsMzguMDg3IDM1LjMwMiw0Mi4xMzYgMzYuOTkyLDQ1LjIxOCBDMzguMDYzLDQ3LjE3MyA0MC4wOTgsNDguMzQgNDIuNDM0LDQ4LjM0IEM0My42NjEsNDguMzQgNDQuODksNDguMDA1IDQ1Ljk5LDQ3LjM3IEw1OC4yODEsNDAuMjcyIEM2MS41NiwzOC4zNzkgNjIuODUzLDM0LjMzIDYxLjE2NCwzMS4yNDggQzYwLjA5MiwyOS4yOTMgNTguMDU4LDI4LjEyNSA1NS43MjEsMjguMTI1IEw1NS43MjEsMjguMTI1IFoiIGlkPSJGaWxsLTgiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjU4OCwyLjQwNyBDMTQ5LjU4OCwyLjQwNyAxNTUuNzY4LDUuOTc1IDE1Ni4zMjUsNi4yOTcgTDE1Ni4zMjUsNy4xODQgQzE1Ni4zMjUsNy4zNiAxNTYuMzM4LDcuNTQ0IDE1Ni4zNjIsNy43MzMgQzE1Ni4zNzMsNy44MTQgMTU2LjM4Miw3Ljg5NCAxNTYuMzksNy45NzUgQzE1Ni41Myw5LjM5IDE1Ny4zNjMsMTAuOTczIDE1OC40OTUsMTEuOTc0IEwxNjUuODkxLDE4LjUxOSBDMTY2LjA2OCwxOC42NzUgMTY2LjI0OSwxOC44MTQgMTY2LjQzMiwxOC45MzQgQzE2OC4wMTEsMTkuOTc0IDE2OS4zODIsMTkuNCAxNjkuNDk0LDE3LjY1MiBDMTY5LjU0MywxNi44NjggMTY5LjU1MSwxNi4wNTcgMTY5LjUxNywxNS4yMjMgTDE2OS41MTQsMTUuMDYzIEwxNjkuNTE0LDEzLjkxMiBDMTcwLjc4LDE0LjY0MiAxOTUuNTAxLDI4LjkxNSAxOTUuNTAxLDI4LjkxNSBMMTk1LjUwMSw4Mi45MTUgQzE5NS41MDEsODQuMDA1IDE5NC43MzEsODQuNDQ1IDE5My43ODEsODMuODk3IEwxNTEuMzA4LDU5LjM3NCBDMTUwLjM1OCw1OC44MjYgMTQ5LjU4OCw1Ny40OTcgMTQ5LjU4OCw1Ni40MDggTDE0OS41ODgsMjIuMzc1IiBpZD0iRmlsbC05IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE5NC41NTMsODQuMjUgQzE5NC4yOTYsODQuMjUgMTk0LjAxMyw4NC4xNjUgMTkzLjcyMiw4My45OTcgTDE1MS4yNSw1OS40NzYgQzE1MC4yNjksNTguOTA5IDE0OS40NzEsNTcuNTMzIDE0OS40NzEsNTYuNDA4IEwxNDkuNDcxLDIyLjM3NSBMMTQ5LjcwNSwyMi4zNzUgTDE0OS43MDUsNTYuNDA4IEMxNDkuNzA1LDU3LjQ1OSAxNTAuNDUsNTguNzQ0IDE1MS4zNjYsNTkuMjc0IEwxOTMuODM5LDgzLjc5NSBDMTk0LjI2Myw4NC4wNCAxOTQuNjU1LDg0LjA4MyAxOTQuOTQyLDgzLjkxNyBDMTk1LjIyNyw4My43NTMgMTk1LjM4NCw4My4zOTcgMTk1LjM4NCw4Mi45MTUgTDE5NS4zODQsMjguOTgyIEMxOTQuMTAyLDI4LjI0MiAxNzIuMTA0LDE1LjU0MiAxNjkuNjMxLDE0LjExNCBMMTY5LjYzNCwxNS4yMiBDMTY5LjY2OCwxNi4wNTIgMTY5LjY2LDE2Ljg3NCAxNjkuNjEsMTcuNjU5IEMxNjkuNTU2LDE4LjUwMyAxNjkuMjE0LDE5LjEyMyAxNjguNjQ3LDE5LjQwNSBDMTY4LjAyOCwxOS43MTQgMTY3LjE5NywxOS41NzggMTY2LjM2NywxOS4wMzIgQzE2Ni4xODEsMTguOTA5IDE2NS45OTUsMTguNzY2IDE2NS44MTQsMTguNjA2IEwxNTguNDE3LDEyLjA2MiBDMTU3LjI1OSwxMS4wMzYgMTU2LjQxOCw5LjQzNyAxNTYuMjc0LDcuOTg2IEMxNTYuMjY2LDcuOTA3IDE1Ni4yNTcsNy44MjcgMTU2LjI0Nyw3Ljc0OCBDMTU2LjIyMSw3LjU1NSAxNTYuMjA5LDcuMzY1IDE1Ni4yMDksNy4xODQgTDE1Ni4yMDksNi4zNjQgQzE1NS4zNzUsNS44ODMgMTQ5LjUyOSwyLjUwOCAxNDkuNTI5LDIuNTA4IEwxNDkuNjQ2LDIuMzA2IEMxNDkuNjQ2LDIuMzA2IDE1NS44MjcsNS44NzQgMTU2LjM4NCw2LjE5NiBMMTU2LjQ0Miw2LjIzIEwxNTYuNDQyLDcuMTg0IEMxNTYuNDQyLDcuMzU1IDE1Ni40NTQsNy41MzUgMTU2LjQ3OCw3LjcxNyBDMTU2LjQ4OSw3LjggMTU2LjQ5OSw3Ljg4MiAxNTYuNTA3LDcuOTYzIEMxNTYuNjQ1LDkuMzU4IDE1Ny40NTUsMTAuODk4IDE1OC41NzIsMTEuODg2IEwxNjUuOTY5LDE4LjQzMSBDMTY2LjE0MiwxOC41ODQgMTY2LjMxOSwxOC43MiAxNjYuNDk2LDE4LjgzNyBDMTY3LjI1NCwxOS4zMzYgMTY4LDE5LjQ2NyAxNjguNTQzLDE5LjE5NiBDMTY5LjAzMywxOC45NTMgMTY5LjMyOSwxOC40MDEgMTY5LjM3NywxNy42NDUgQzE2OS40MjcsMTYuODY3IDE2OS40MzQsMTYuMDU0IDE2OS40MDEsMTUuMjI4IEwxNjkuMzk3LDE1LjA2NSBMMTY5LjM5NywxMy43MSBMMTY5LjU3MiwxMy44MSBDMTcwLjgzOSwxNC41NDEgMTk1LjU1OSwyOC44MTQgMTk1LjU1OSwyOC44MTQgTDE5NS42MTgsMjguODQ3IEwxOTUuNjE4LDgyLjkxNSBDMTk1LjYxOCw4My40ODQgMTk1LjQyLDgzLjkxMSAxOTUuMDU5LDg0LjExOSBDMTk0LjkwOCw4NC4yMDYgMTk0LjczNyw4NC4yNSAxOTQuNTUzLDg0LjI1IiBpZD0iRmlsbC0xMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDUuNjg1LDU2LjE2MSBMMTY5LjgsNzAuMDgzIEwxNDMuODIyLDg1LjA4MSBMMTQyLjM2LDg0Ljc3NCBDMTM1LjgyNiw4Mi42MDQgMTI4LjczMiw4MS4wNDYgMTIxLjM0MSw4MC4xNTggQzExNi45NzYsNzkuNjM0IDExMi42NzgsODEuMjU0IDExMS43NDMsODMuNzc4IEMxMTEuNTA2LDg0LjQxNCAxMTEuNTAzLDg1LjA3MSAxMTEuNzMyLDg1LjcwNiBDMTEzLjI3LDg5Ljk3MyAxMTUuOTY4LDk0LjA2OSAxMTkuNzI3LDk3Ljg0MSBMMTIwLjI1OSw5OC42ODYgQzEyMC4yNiw5OC42ODUgOTQuMjgyLDExMy42ODMgOTQuMjgyLDExMy42ODMgTDcwLjE2Nyw5OS43NjEgTDE0NS42ODUsNTYuMTYxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik05NC4yODIsMTEzLjgxOCBMOTQuMjIzLDExMy43ODUgTDY5LjkzMyw5OS43NjEgTDcwLjEwOCw5OS42NiBMMTQ1LjY4NSw1Ni4wMjYgTDE0NS43NDMsNTYuMDU5IEwxNzAuMDMzLDcwLjA4MyBMMTQzLjg0Miw4NS4yMDUgTDE0My43OTcsODUuMTk1IEMxNDMuNzcyLDg1LjE5IDE0Mi4zMzYsODQuODg4IDE0Mi4zMzYsODQuODg4IEMxMzUuNzg3LDgyLjcxNCAxMjguNzIzLDgxLjE2MyAxMjEuMzI3LDgwLjI3NCBDMTIwLjc4OCw4MC4yMDkgMTIwLjIzNiw4MC4xNzcgMTE5LjY4OSw4MC4xNzcgQzExNS45MzEsODAuMTc3IDExMi42MzUsODEuNzA4IDExMS44NTIsODMuODE5IEMxMTEuNjI0LDg0LjQzMiAxMTEuNjIxLDg1LjA1MyAxMTEuODQyLDg1LjY2NyBDMTEzLjM3Nyw4OS45MjUgMTE2LjA1OCw5My45OTMgMTE5LjgxLDk3Ljc1OCBMMTE5LjgyNiw5Ny43NzkgTDEyMC4zNTIsOTguNjE0IEMxMjAuMzU0LDk4LjYxNyAxMjAuMzU2LDk4LjYyIDEyMC4zNTgsOTguNjI0IEwxMjAuNDIyLDk4LjcyNiBMMTIwLjMxNyw5OC43ODcgQzEyMC4yNjQsOTguODE4IDk0LjU5OSwxMTMuNjM1IDk0LjM0LDExMy43ODUgTDk0LjI4MiwxMTMuODE4IEw5NC4yODIsMTEzLjgxOCBaIE03MC40MDEsOTkuNzYxIEw5NC4yODIsMTEzLjU0OSBMMTE5LjA4NCw5OS4yMjkgQzExOS42Myw5OC45MTQgMTE5LjkzLDk4Ljc0IDEyMC4xMDEsOTguNjU0IEwxMTkuNjM1LDk3LjkxNCBDMTE1Ljg2NCw5NC4xMjcgMTEzLjE2OCw5MC4wMzMgMTExLjYyMiw4NS43NDYgQzExMS4zODIsODUuMDc5IDExMS4zODYsODQuNDA0IDExMS42MzMsODMuNzM4IEMxMTIuNDQ4LDgxLjUzOSAxMTUuODM2LDc5Ljk0MyAxMTkuNjg5LDc5Ljk0MyBDMTIwLjI0Niw3OS45NDMgMTIwLjgwNiw3OS45NzYgMTIxLjM1NSw4MC4wNDIgQzEyOC43NjcsODAuOTMzIDEzNS44NDYsODIuNDg3IDE0Mi4zOTYsODQuNjYzIEMxNDMuMjMyLDg0LjgzOCAxNDMuNjExLDg0LjkxNyAxNDMuNzg2LDg0Ljk2NyBMMTY5LjU2Niw3MC4wODMgTDE0NS42ODUsNTYuMjk1IEw3MC40MDEsOTkuNzYxIEw3MC40MDEsOTkuNzYxIFoiIGlkPSJGaWxsLTEyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2Ny4yMywxOC45NzkgTDE2Ny4yMyw2OS44NSBMMTM5LjkwOSw4NS42MjMgTDEzMy40NDgsNzEuNDU2IEMxMzIuNTM4LDY5LjQ2IDEzMC4wMiw2OS43MTggMTI3LjgyNCw3Mi4wMyBDMTI2Ljc2OSw3My4xNCAxMjUuOTMxLDc0LjU4NSAxMjUuNDk0LDc2LjA0OCBMMTE5LjAzNCw5Ny42NzYgTDkxLjcxMiwxMTMuNDUgTDkxLjcxMiw2Mi41NzkgTDE2Ny4yMywxOC45NzkiIGlkPSJGaWxsLTEzIiBmaWxsPSIjRkZGRkZGIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTkxLjcxMiwxMTMuNTY3IEM5MS42OTIsMTEzLjU2NyA5MS42NzIsMTEzLjU2MSA5MS42NTMsMTEzLjU1MSBDOTEuNjE4LDExMy41MyA5MS41OTUsMTEzLjQ5MiA5MS41OTUsMTEzLjQ1IEw5MS41OTUsNjIuNTc5IEM5MS41OTUsNjIuNTM3IDkxLjYxOCw2Mi40OTkgOTEuNjUzLDYyLjQ3OCBMMTY3LjE3MiwxOC44NzggQzE2Ny4yMDgsMTguODU3IDE2Ny4yNTIsMTguODU3IDE2Ny4yODgsMTguODc4IEMxNjcuMzI0LDE4Ljg5OSAxNjcuMzQ3LDE4LjkzNyAxNjcuMzQ3LDE4Ljk3OSBMMTY3LjM0Nyw2OS44NSBDMTY3LjM0Nyw2OS44OTEgMTY3LjMyNCw2OS45MyAxNjcuMjg4LDY5Ljk1IEwxMzkuOTY3LDg1LjcyNSBDMTM5LjkzOSw4NS43NDEgMTM5LjkwNSw4NS43NDUgMTM5Ljg3Myw4NS43MzUgQzEzOS44NDIsODUuNzI1IDEzOS44MTYsODUuNzAyIDEzOS44MDIsODUuNjcyIEwxMzMuMzQyLDcxLjUwNCBDMTMyLjk2Nyw3MC42ODIgMTMyLjI4LDcwLjIyOSAxMzEuNDA4LDcwLjIyOSBDMTMwLjMxOSw3MC4yMjkgMTI5LjA0NCw3MC45MTUgMTI3LjkwOCw3Mi4xMSBDMTI2Ljg3NCw3My4yIDEyNi4wMzQsNzQuNjQ3IDEyNS42MDYsNzYuMDgyIEwxMTkuMTQ2LDk3LjcwOSBDMTE5LjEzNyw5Ny43MzggMTE5LjExOCw5Ny43NjIgMTE5LjA5Miw5Ny43NzcgTDkxLjc3LDExMy41NTEgQzkxLjc1MiwxMTMuNTYxIDkxLjczMiwxMTMuNTY3IDkxLjcxMiwxMTMuNTY3IEw5MS43MTIsMTEzLjU2NyBaIE05MS44MjksNjIuNjQ3IEw5MS44MjksMTEzLjI0OCBMMTE4LjkzNSw5Ny41OTggTDEyNS4zODIsNzYuMDE1IEMxMjUuODI3LDc0LjUyNSAxMjYuNjY0LDczLjA4MSAxMjcuNzM5LDcxLjk1IEMxMjguOTE5LDcwLjcwOCAxMzAuMjU2LDY5Ljk5NiAxMzEuNDA4LDY5Ljk5NiBDMTMyLjM3Nyw2OS45OTYgMTMzLjEzOSw3MC40OTcgMTMzLjU1NCw3MS40MDcgTDEzOS45NjEsODUuNDU4IEwxNjcuMTEzLDY5Ljc4MiBMMTY3LjExMywxOS4xODEgTDkxLjgyOSw2Mi42NDcgTDkxLjgyOSw2Mi42NDcgWiIgaWQ9IkZpbGwtMTQiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTY4LjU0MywxOS4yMTMgTDE2OC41NDMsNzAuMDgzIEwxNDEuMjIxLDg1Ljg1NyBMMTM0Ljc2MSw3MS42ODkgQzEzMy44NTEsNjkuNjk0IDEzMS4zMzMsNjkuOTUxIDEyOS4xMzcsNzIuMjYzIEMxMjguMDgyLDczLjM3NCAxMjcuMjQ0LDc0LjgxOSAxMjYuODA3LDc2LjI4MiBMMTIwLjM0Niw5Ny45MDkgTDkzLjAyNSwxMTMuNjgzIEw5My4wMjUsNjIuODEzIEwxNjguNTQzLDE5LjIxMyIgaWQ9IkZpbGwtMTUiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTMuMDI1LDExMy44IEM5My4wMDUsMTEzLjggOTIuOTg0LDExMy43OTUgOTIuOTY2LDExMy43ODUgQzkyLjkzMSwxMTMuNzY0IDkyLjkwOCwxMTMuNzI1IDkyLjkwOCwxMTMuNjg0IEw5Mi45MDgsNjIuODEzIEM5Mi45MDgsNjIuNzcxIDkyLjkzMSw2Mi43MzMgOTIuOTY2LDYyLjcxMiBMMTY4LjQ4NCwxOS4xMTIgQzE2OC41MiwxOS4wOSAxNjguNTY1LDE5LjA5IDE2OC42MDEsMTkuMTEyIEMxNjguNjM3LDE5LjEzMiAxNjguNjYsMTkuMTcxIDE2OC42NiwxOS4yMTIgTDE2OC42Niw3MC4wODMgQzE2OC42Niw3MC4xMjUgMTY4LjYzNyw3MC4xNjQgMTY4LjYwMSw3MC4xODQgTDE0MS4yOCw4NS45NTggQzE0MS4yNTEsODUuOTc1IDE0MS4yMTcsODUuOTc5IDE0MS4xODYsODUuOTY4IEMxNDEuMTU0LDg1Ljk1OCAxNDEuMTI5LDg1LjkzNiAxNDEuMTE1LDg1LjkwNiBMMTM0LjY1NSw3MS43MzggQzEzNC4yOCw3MC45MTUgMTMzLjU5Myw3MC40NjMgMTMyLjcyLDcwLjQ2MyBDMTMxLjYzMiw3MC40NjMgMTMwLjM1Nyw3MS4xNDggMTI5LjIyMSw3Mi4zNDQgQzEyOC4xODYsNzMuNDMzIDEyNy4zNDcsNzQuODgxIDEyNi45MTksNzYuMzE1IEwxMjAuNDU4LDk3Ljk0MyBDMTIwLjQ1LDk3Ljk3MiAxMjAuNDMxLDk3Ljk5NiAxMjAuNDA1LDk4LjAxIEw5My4wODMsMTEzLjc4NSBDOTMuMDY1LDExMy43OTUgOTMuMDQ1LDExMy44IDkzLjAyNSwxMTMuOCBMOTMuMDI1LDExMy44IFogTTkzLjE0Miw2Mi44ODEgTDkzLjE0MiwxMTMuNDgxIEwxMjAuMjQ4LDk3LjgzMiBMMTI2LjY5NSw3Ni4yNDggQzEyNy4xNCw3NC43NTggMTI3Ljk3Nyw3My4zMTUgMTI5LjA1Miw3Mi4xODMgQzEzMC4yMzEsNzAuOTQyIDEzMS41NjgsNzAuMjI5IDEzMi43Miw3MC4yMjkgQzEzMy42ODksNzAuMjI5IDEzNC40NTIsNzAuNzMxIDEzNC44NjcsNzEuNjQxIEwxNDEuMjc0LDg1LjY5MiBMMTY4LjQyNiw3MC4wMTYgTDE2OC40MjYsMTkuNDE1IEw5My4xNDIsNjIuODgxIEw5My4xNDIsNjIuODgxIFoiIGlkPSJGaWxsLTE2IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS44LDcwLjA4MyBMMTQyLjQ3OCw4NS44NTcgTDEzNi4wMTgsNzEuNjg5IEMxMzUuMTA4LDY5LjY5NCAxMzIuNTksNjkuOTUxIDEzMC4zOTMsNzIuMjYzIEMxMjkuMzM5LDczLjM3NCAxMjguNSw3NC44MTkgMTI4LjA2NCw3Ni4yODIgTDEyMS42MDMsOTcuOTA5IEw5NC4yODIsMTEzLjY4MyBMOTQuMjgyLDYyLjgxMyBMMTY5LjgsMTkuMjEzIEwxNjkuOCw3MC4wODMgWiIgaWQ9IkZpbGwtMTciIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTQuMjgyLDExMy45MTcgQzk0LjI0MSwxMTMuOTE3IDk0LjIwMSwxMTMuOTA3IDk0LjE2NSwxMTMuODg2IEM5NC4wOTMsMTEzLjg0NSA5NC4wNDgsMTEzLjc2NyA5NC4wNDgsMTEzLjY4NCBMOTQuMDQ4LDYyLjgxMyBDOTQuMDQ4LDYyLjczIDk0LjA5Myw2Mi42NTIgOTQuMTY1LDYyLjYxMSBMMTY5LjY4MywxOS4wMSBDMTY5Ljc1NSwxOC45NjkgMTY5Ljg0NCwxOC45NjkgMTY5LjkxNywxOS4wMSBDMTY5Ljk4OSwxOS4wNTIgMTcwLjAzMywxOS4xMjkgMTcwLjAzMywxOS4yMTIgTDE3MC4wMzMsNzAuMDgzIEMxNzAuMDMzLDcwLjE2NiAxNjkuOTg5LDcwLjI0NCAxNjkuOTE3LDcwLjI4NSBMMTQyLjU5NSw4Ni4wNiBDMTQyLjUzOCw4Ni4wOTIgMTQyLjQ2OSw4Ni4xIDE0Mi40MDcsODYuMDggQzE0Mi4zNDQsODYuMDYgMTQyLjI5Myw4Ni4wMTQgMTQyLjI2Niw4NS45NTQgTDEzNS44MDUsNzEuNzg2IEMxMzUuNDQ1LDcwLjk5NyAxMzQuODEzLDcwLjU4IDEzMy45NzcsNzAuNTggQzEzMi45MjEsNzAuNTggMTMxLjY3Niw3MS4yNTIgMTMwLjU2Miw3Mi40MjQgQzEyOS41NCw3My41MDEgMTI4LjcxMSw3NC45MzEgMTI4LjI4Nyw3Ni4zNDggTDEyMS44MjcsOTcuOTc2IEMxMjEuODEsOTguMDM0IDEyMS43NzEsOTguMDgyIDEyMS43Miw5OC4xMTIgTDk0LjM5OCwxMTMuODg2IEM5NC4zNjIsMTEzLjkwNyA5NC4zMjIsMTEzLjkxNyA5NC4yODIsMTEzLjkxNyBMOTQuMjgyLDExMy45MTcgWiBNOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDExMy4yNzkgTDEyMS40MDYsOTcuNzU0IEwxMjcuODQsNzYuMjE1IEMxMjguMjksNzQuNzA4IDEyOS4xMzcsNzMuMjQ3IDEzMC4yMjQsNzIuMTAzIEMxMzEuNDI1LDcwLjgzOCAxMzIuNzkzLDcwLjExMiAxMzMuOTc3LDcwLjExMiBDMTM0Ljk5NSw3MC4xMTIgMTM1Ljc5NSw3MC42MzggMTM2LjIzLDcxLjU5MiBMMTQyLjU4NCw4NS41MjYgTDE2OS41NjYsNjkuOTQ4IEwxNjkuNTY2LDE5LjYxNyBMOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDYyLjk0OCBaIiBpZD0iRmlsbC0xOCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMDkuODk0LDkyLjk0MyBMMTA5Ljg5NCw5Mi45NDMgQzEwOC4xMiw5Mi45NDMgMTA2LjY1Myw5Mi4yMTggMTA1LjY1LDkwLjgyMyBDMTA1LjU4Myw5MC43MzEgMTA1LjU5Myw5MC42MSAxMDUuNjczLDkwLjUyOSBDMTA1Ljc1Myw5MC40NDggMTA1Ljg4LDkwLjQ0IDEwNS45NzQsOTAuNTA2IEMxMDYuNzU0LDkxLjA1MyAxMDcuNjc5LDkxLjMzMyAxMDguNzI0LDkxLjMzMyBDMTEwLjA0Nyw5MS4zMzMgMTExLjQ3OCw5MC44OTQgMTEyLjk4LDkwLjAyNyBDMTE4LjI5MSw4Ni45NiAxMjIuNjExLDc5LjUwOSAxMjIuNjExLDczLjQxNiBDMTIyLjYxMSw3MS40ODkgMTIyLjE2OSw2OS44NTYgMTIxLjMzMyw2OC42OTIgQzEyMS4yNjYsNjguNiAxMjEuMjc2LDY4LjQ3MyAxMjEuMzU2LDY4LjM5MiBDMTIxLjQzNiw2OC4zMTEgMTIxLjU2Myw2OC4yOTkgMTIxLjY1Niw2OC4zNjUgQzEyMy4zMjcsNjkuNTM3IDEyNC4yNDcsNzEuNzQ2IDEyNC4yNDcsNzQuNTg0IEMxMjQuMjQ3LDgwLjgyNiAxMTkuODIxLDg4LjQ0NyAxMTQuMzgyLDkxLjU4NyBDMTEyLjgwOCw5Mi40OTUgMTExLjI5OCw5Mi45NDMgMTA5Ljg5NCw5Mi45NDMgTDEwOS44OTQsOTIuOTQzIFogTTEwNi45MjUsOTEuNDAxIEMxMDcuNzM4LDkyLjA1MiAxMDguNzQ1LDkyLjI3OCAxMDkuODkzLDkyLjI3OCBMMTA5Ljg5NCw5Mi4yNzggQzExMS4yMTUsOTIuMjc4IDExMi42NDcsOTEuOTUxIDExNC4xNDgsOTEuMDg0IEMxMTkuNDU5LDg4LjAxNyAxMjMuNzgsODAuNjIxIDEyMy43OCw3NC41MjggQzEyMy43OCw3Mi41NDkgMTIzLjMxNyw3MC45MjkgMTIyLjQ1NCw2OS43NjcgQzEyMi44NjUsNzAuODAyIDEyMy4wNzksNzIuMDQyIDEyMy4wNzksNzMuNDAyIEMxMjMuMDc5LDc5LjY0NSAxMTguNjUzLDg3LjI4NSAxMTMuMjE0LDkwLjQyNSBDMTExLjY0LDkxLjMzNCAxMTAuMTMsOTEuNzQyIDEwOC43MjQsOTEuNzQyIEMxMDguMDgzLDkxLjc0MiAxMDcuNDgxLDkxLjU5MyAxMDYuOTI1LDkxLjQwMSBMMTA2LjkyNSw5MS40MDEgWiIgaWQ9IkZpbGwtMTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjA5Nyw5MC4yMyBDMTE4LjQ4MSw4Ny4xMjIgMTIyLjg0NSw3OS41OTQgMTIyLjg0NSw3My40MTYgQzEyMi44NDUsNzEuMzY1IDEyMi4zNjIsNjkuNzI0IDEyMS41MjIsNjguNTU2IEMxMTkuNzM4LDY3LjMwNCAxMTcuMTQ4LDY3LjM2MiAxMTQuMjY1LDY5LjAyNiBDMTA4Ljg4MSw3Mi4xMzQgMTA0LjUxNyw3OS42NjIgMTA0LjUxNyw4NS44NCBDMTA0LjUxNyw4Ny44OTEgMTA1LDg5LjUzMiAxMDUuODQsOTAuNyBDMTA3LjYyNCw5MS45NTIgMTEwLjIxNCw5MS44OTQgMTEzLjA5Nyw5MC4yMyIgaWQ9IkZpbGwtMjAiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTA4LjcyNCw5MS42MTQgTDEwOC43MjQsOTEuNjE0IEMxMDcuNTgyLDkxLjYxNCAxMDYuNTY2LDkxLjQwMSAxMDUuNzA1LDkwLjc5NyBDMTA1LjY4NCw5MC43ODMgMTA1LjY2NSw5MC44MTEgMTA1LjY1LDkwLjc5IEMxMDQuNzU2LDg5LjU0NiAxMDQuMjgzLDg3Ljg0MiAxMDQuMjgzLDg1LjgxNyBDMTA0LjI4Myw3OS41NzUgMTA4LjcwOSw3MS45NTMgMTE0LjE0OCw2OC44MTIgQzExNS43MjIsNjcuOTA0IDExNy4yMzIsNjcuNDQ5IDExOC42MzgsNjcuNDQ5IEMxMTkuNzgsNjcuNDQ5IDEyMC43OTYsNjcuNzU4IDEyMS42NTYsNjguMzYyIEMxMjEuNjc4LDY4LjM3NyAxMjEuNjk3LDY4LjM5NyAxMjEuNzEyLDY4LjQxOCBDMTIyLjYwNiw2OS42NjIgMTIzLjA3OSw3MS4zOSAxMjMuMDc5LDczLjQxNSBDMTIzLjA3OSw3OS42NTggMTE4LjY1Myw4Ny4xOTggMTEzLjIxNCw5MC4zMzggQzExMS42NCw5MS4yNDcgMTEwLjEzLDkxLjYxNCAxMDguNzI0LDkxLjYxNCBMMTA4LjcyNCw5MS42MTQgWiBNMTA2LjAwNiw5MC41MDUgQzEwNi43OCw5MS4wMzcgMTA3LjY5NCw5MS4yODEgMTA4LjcyNCw5MS4yODEgQzExMC4wNDcsOTEuMjgxIDExMS40NzgsOTAuODY4IDExMi45OCw5MC4wMDEgQzExOC4yOTEsODYuOTM1IDEyMi42MTEsNzkuNDk2IDEyMi42MTEsNzMuNDAzIEMxMjIuNjExLDcxLjQ5NCAxMjIuMTc3LDY5Ljg4IDEyMS4zNTYsNjguNzE4IEMxMjAuNTgyLDY4LjE4NSAxMTkuNjY4LDY3LjkxOSAxMTguNjM4LDY3LjkxOSBDMTE3LjMxNSw2Ny45MTkgMTE1Ljg4Myw2OC4zNiAxMTQuMzgyLDY5LjIyNyBDMTA5LjA3MSw3Mi4yOTMgMTA0Ljc1MSw3OS43MzMgMTA0Ljc1MSw4NS44MjYgQzEwNC43NTEsODcuNzM1IDEwNS4xODUsODkuMzQzIDEwNi4wMDYsOTAuNTA1IEwxMDYuMDA2LDkwLjUwNSBaIiBpZD0iRmlsbC0yMSIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDkuMzE4LDcuMjYyIEwxMzkuMzM0LDE2LjE0IEwxNTUuMjI3LDI3LjE3MSBMMTYwLjgxNiwyMS4wNTkgTDE0OS4zMTgsNy4yNjIiIGlkPSJGaWxsLTIyIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS42NzYsMTMuODQgTDE1OS45MjgsMTkuNDY3IEMxNTYuMjg2LDIxLjU3IDE1MC40LDIxLjU4IDE0Ni43ODEsMTkuNDkxIEMxNDMuMTYxLDE3LjQwMiAxNDMuMTgsMTQuMDAzIDE0Ni44MjIsMTEuOSBMMTU2LjMxNyw2LjI5MiBMMTQ5LjU4OCwyLjQwNyBMNjcuNzUyLDQ5LjQ3OCBMMTEzLjY3NSw3NS45OTIgTDExNi43NTYsNzQuMjEzIEMxMTcuMzg3LDczLjg0OCAxMTcuNjI1LDczLjMxNSAxMTcuMzc0LDcyLjgyMyBDMTE1LjAxNyw2OC4xOTEgMTE0Ljc4MSw2My4yNzcgMTE2LjY5MSw1OC41NjEgQzEyMi4zMjksNDQuNjQxIDE0MS4yLDMzLjc0NiAxNjUuMzA5LDMwLjQ5MSBDMTczLjQ3OCwyOS4zODggMTgxLjk4OSwyOS41MjQgMTkwLjAxMywzMC44ODUgQzE5MC44NjUsMzEuMDMgMTkxLjc4OSwzMC44OTMgMTkyLjQyLDMwLjUyOCBMMTk1LjUwMSwyOC43NSBMMTY5LjY3NiwxMy44NCIgaWQ9IkZpbGwtMjMiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3Ni40NTkgQzExMy41OTQsNzYuNDU5IDExMy41MTQsNzYuNDM4IDExMy40NDIsNzYuMzk3IEw2Ny41MTgsNDkuODgyIEM2Ny4zNzQsNDkuNzk5IDY3LjI4NCw0OS42NDUgNjcuMjg1LDQ5LjQ3OCBDNjcuMjg1LDQ5LjMxMSA2Ny4zNzQsNDkuMTU3IDY3LjUxOSw0OS4wNzMgTDE0OS4zNTUsMi4wMDIgQzE0OS40OTksMS45MTkgMTQ5LjY3NywxLjkxOSAxNDkuODIxLDIuMDAyIEwxNTYuNTUsNS44ODcgQzE1Ni43NzQsNi4wMTcgMTU2Ljg1LDYuMzAyIDE1Ni43MjIsNi41MjYgQzE1Ni41OTIsNi43NDkgMTU2LjMwNyw2LjgyNiAxNTYuMDgzLDYuNjk2IEwxNDkuNTg3LDIuOTQ2IEw2OC42ODcsNDkuNDc5IEwxMTMuNjc1LDc1LjQ1MiBMMTE2LjUyMyw3My44MDggQzExNi43MTUsNzMuNjk3IDExNy4xNDMsNzMuMzk5IDExNi45NTgsNzMuMDM1IEMxMTQuNTQyLDY4LjI4NyAxMTQuMyw2My4yMjEgMTE2LjI1OCw1OC4zODUgQzExOS4wNjQsNTEuNDU4IDEyNS4xNDMsNDUuMTQzIDEzMy44NCw0MC4xMjIgQzE0Mi40OTcsMzUuMTI0IDE1My4zNTgsMzEuNjMzIDE2NS4yNDcsMzAuMDI4IEMxNzMuNDQ1LDI4LjkyMSAxODIuMDM3LDI5LjA1OCAxOTAuMDkxLDMwLjQyNSBDMTkwLjgzLDMwLjU1IDE5MS42NTIsMzAuNDMyIDE5Mi4xODYsMzAuMTI0IEwxOTQuNTY3LDI4Ljc1IEwxNjkuNDQyLDE0LjI0NCBDMTY5LjIxOSwxNC4xMTUgMTY5LjE0MiwxMy44MjkgMTY5LjI3MSwxMy42MDYgQzE2OS40LDEzLjM4MiAxNjkuNjg1LDEzLjMwNiAxNjkuOTA5LDEzLjQzNSBMMTk1LjczNCwyOC4zNDUgQzE5NS44NzksMjguNDI4IDE5NS45NjgsMjguNTgzIDE5NS45NjgsMjguNzUgQzE5NS45NjgsMjguOTE2IDE5NS44NzksMjkuMDcxIDE5NS43MzQsMjkuMTU0IEwxOTIuNjUzLDMwLjkzMyBDMTkxLjkzMiwzMS4zNSAxOTAuODksMzEuNTA4IDE4OS45MzUsMzEuMzQ2IEMxODEuOTcyLDI5Ljk5NSAxNzMuNDc4LDI5Ljg2IDE2NS4zNzIsMzAuOTU0IEMxNTMuNjAyLDMyLjU0MyAxNDIuODYsMzUuOTkzIDEzNC4zMDcsNDAuOTMxIEMxMjUuNzkzLDQ1Ljg0NyAxMTkuODUxLDUyLjAwNCAxMTcuMTI0LDU4LjczNiBDMTE1LjI3LDYzLjMxNCAxMTUuNTAxLDY4LjExMiAxMTcuNzksNzIuNjExIEMxMTguMTYsNzMuMzM2IDExNy44NDUsNzQuMTI0IDExNi45OSw3NC42MTcgTDExMy45MDksNzYuMzk3IEMxMTMuODM2LDc2LjQzOCAxMTMuNzU2LDc2LjQ1OSAxMTMuNjc1LDc2LjQ1OSIgaWQ9IkZpbGwtMjQiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUzLjMxNiwyMS4yNzkgQzE1MC45MDMsMjEuMjc5IDE0OC40OTUsMjAuNzUxIDE0Ni42NjQsMTkuNjkzIEMxNDQuODQ2LDE4LjY0NCAxNDMuODQ0LDE3LjIzMiAxNDMuODQ0LDE1LjcxOCBDMTQzLjg0NCwxNC4xOTEgMTQ0Ljg2LDEyLjc2MyAxNDYuNzA1LDExLjY5OCBMMTU2LjE5OCw2LjA5MSBDMTU2LjMwOSw2LjAyNSAxNTYuNDUyLDYuMDYyIDE1Ni41MTgsNi4xNzMgQzE1Ni41ODMsNi4yODQgMTU2LjU0Nyw2LjQyNyAxNTYuNDM2LDYuNDkzIEwxNDYuOTQsMTIuMTAyIEMxNDUuMjQ0LDEzLjA4MSAxNDQuMzEyLDE0LjM2NSAxNDQuMzEyLDE1LjcxOCBDMTQ0LjMxMiwxNy4wNTggMTQ1LjIzLDE4LjMyNiAxNDYuODk3LDE5LjI4OSBDMTUwLjQ0NiwyMS4zMzggMTU2LjI0LDIxLjMyNyAxNTkuODExLDE5LjI2NSBMMTY5LjU1OSwxMy42MzcgQzE2OS42NywxMy41NzMgMTY5LjgxMywxMy42MTEgMTY5Ljg3OCwxMy43MjMgQzE2OS45NDMsMTMuODM0IDE2OS45MDQsMTMuOTc3IDE2OS43OTMsMTQuMDQyIEwxNjAuMDQ1LDE5LjY3IEMxNTguMTg3LDIwLjc0MiAxNTUuNzQ5LDIxLjI3OSAxNTMuMzE2LDIxLjI3OSIgaWQ9IkZpbGwtMjUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3NS45OTIgTDY3Ljc2Miw0OS40ODQiIGlkPSJGaWxsLTI2IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMy42NzUsNzYuMzQyIEMxMTMuNjE1LDc2LjM0MiAxMTMuNTU1LDc2LjMyNyAxMTMuNSw3Ni4yOTUgTDY3LjU4Nyw0OS43ODcgQzY3LjQxOSw0OS42OSA2Ny4zNjIsNDkuNDc2IDY3LjQ1OSw0OS4zMDkgQzY3LjU1Niw0OS4xNDEgNjcuNzcsNDkuMDgzIDY3LjkzNyw0OS4xOCBMMTEzLjg1LDc1LjY4OCBDMTE0LjAxOCw3NS43ODUgMTE0LjA3NSw3NiAxMTMuOTc4LDc2LjE2NyBDMTEzLjkxNCw3Ni4yNzkgMTEzLjc5Niw3Ni4zNDIgMTEzLjY3NSw3Ni4zNDIiIGlkPSJGaWxsLTI3IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY3Ljc2Miw0OS40ODQgTDY3Ljc2MiwxMDMuNDg1IEM2Ny43NjIsMTA0LjU3NSA2OC41MzIsMTA1LjkwMyA2OS40ODIsMTA2LjQ1MiBMMTExLjk1NSwxMzAuOTczIEMxMTIuOTA1LDEzMS41MjIgMTEzLjY3NSwxMzEuMDgzIDExMy42NzUsMTI5Ljk5MyBMMTEzLjY3NSw3NS45OTIiIGlkPSJGaWxsLTI4IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMi43MjcsMTMxLjU2MSBDMTEyLjQzLDEzMS41NjEgMTEyLjEwNywxMzEuNDY2IDExMS43OCwxMzEuMjc2IEw2OS4zMDcsMTA2Ljc1NSBDNjguMjQ0LDEwNi4xNDIgNjcuNDEyLDEwNC43MDUgNjcuNDEyLDEwMy40ODUgTDY3LjQxMiw0OS40ODQgQzY3LjQxMiw0OS4yOSA2Ny41NjksNDkuMTM0IDY3Ljc2Miw0OS4xMzQgQzY3Ljk1Niw0OS4xMzQgNjguMTEzLDQ5LjI5IDY4LjExMyw0OS40ODQgTDY4LjExMywxMDMuNDg1IEM2OC4xMTMsMTA0LjQ0NSA2OC44MiwxMDUuNjY1IDY5LjY1NywxMDYuMTQ4IEwxMTIuMTMsMTMwLjY3IEMxMTIuNDc0LDEzMC44NjggMTEyLjc5MSwxMzAuOTEzIDExMywxMzAuNzkyIEMxMTMuMjA2LDEzMC42NzMgMTEzLjMyNSwxMzAuMzgxIDExMy4zMjUsMTI5Ljk5MyBMMTEzLjMyNSw3NS45OTIgQzExMy4zMjUsNzUuNzk4IDExMy40ODIsNzUuNjQxIDExMy42NzUsNzUuNjQxIEMxMTMuODY5LDc1LjY0MSAxMTQuMDI1LDc1Ljc5OCAxMTQuMDI1LDc1Ljk5MiBMMTE0LjAyNSwxMjkuOTkzIEMxMTQuMDI1LDEzMC42NDggMTEzLjc4NiwxMzEuMTQ3IDExMy4zNSwxMzEuMzk5IEMxMTMuMTYyLDEzMS41MDcgMTEyLjk1MiwxMzEuNTYxIDExMi43MjcsMTMxLjU2MSIgaWQ9IkZpbGwtMjkiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEyLjg2LDQwLjUxMiBDMTEyLjg2LDQwLjUxMiAxMTIuODYsNDAuNTEyIDExMi44NTksNDAuNTEyIEMxMTAuNTQxLDQwLjUxMiAxMDguMzYsMzkuOTkgMTA2LjcxNywzOS4wNDEgQzEwNS4wMTIsMzguMDU3IDEwNC4wNzQsMzYuNzI2IDEwNC4wNzQsMzUuMjkyIEMxMDQuMDc0LDMzLjg0NyAxMDUuMDI2LDMyLjUwMSAxMDYuNzU0LDMxLjUwNCBMMTE4Ljc5NSwyNC41NTEgQzEyMC40NjMsMjMuNTg5IDEyMi42NjksMjMuMDU4IDEyNS4wMDcsMjMuMDU4IEMxMjcuMzI1LDIzLjA1OCAxMjkuNTA2LDIzLjU4MSAxMzEuMTUsMjQuNTMgQzEzMi44NTQsMjUuNTE0IDEzMy43OTMsMjYuODQ1IDEzMy43OTMsMjguMjc4IEMxMzMuNzkzLDI5LjcyNCAxMzIuODQxLDMxLjA2OSAxMzEuMTEzLDMyLjA2NyBMMTE5LjA3MSwzOS4wMTkgQzExNy40MDMsMzkuOTgyIDExNS4xOTcsNDAuNTEyIDExMi44Niw0MC41MTIgTDExMi44Niw0MC41MTIgWiBNMTI1LjAwNywyMy43NTkgQzEyMi43OSwyMy43NTkgMTIwLjcwOSwyNC4yNTYgMTE5LjE0NiwyNS4xNTggTDEwNy4xMDQsMzIuMTEgQzEwNS42MDIsMzIuOTc4IDEwNC43NzQsMzQuMTA4IDEwNC43NzQsMzUuMjkyIEMxMDQuNzc0LDM2LjQ2NSAxMDUuNTg5LDM3LjU4MSAxMDcuMDY3LDM4LjQzNCBDMTA4LjYwNSwzOS4zMjMgMTEwLjY2MywzOS44MTIgMTEyLjg1OSwzOS44MTIgTDExMi44NiwzOS44MTIgQzExNS4wNzYsMzkuODEyIDExNy4xNTgsMzkuMzE1IDExOC43MjEsMzguNDEzIEwxMzAuNzYyLDMxLjQ2IEMxMzIuMjY0LDMwLjU5MyAxMzMuMDkyLDI5LjQ2MyAxMzMuMDkyLDI4LjI3OCBDMTMzLjA5MiwyNy4xMDYgMTMyLjI3OCwyNS45OSAxMzAuOCwyNS4xMzYgQzEyOS4yNjEsMjQuMjQ4IDEyNy4yMDQsMjMuNzU5IDEyNS4wMDcsMjMuNzU5IEwxMjUuMDA3LDIzLjc1OSBaIiBpZD0iRmlsbC0zMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNjUuNjMsMTYuMjE5IEwxNTkuODk2LDE5LjUzIEMxNTYuNzI5LDIxLjM1OCAxNTEuNjEsMjEuMzY3IDE0OC40NjMsMTkuNTUgQzE0NS4zMTYsMTcuNzMzIDE0NS4zMzIsMTQuNzc4IDE0OC40OTksMTIuOTQ5IEwxNTQuMjMzLDkuNjM5IEwxNjUuNjMsMTYuMjE5IiBpZD0iRmlsbC0zMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNTQuMjMzLDEwLjQ0OCBMMTY0LjIyOCwxNi4yMTkgTDE1OS41NDYsMTguOTIzIEMxNTguMTEyLDE5Ljc1IDE1Ni4xOTQsMjAuMjA2IDE1NC4xNDcsMjAuMjA2IEMxNTIuMTE4LDIwLjIwNiAxNTAuMjI0LDE5Ljc1NyAxNDguODE0LDE4Ljk0MyBDMTQ3LjUyNCwxOC4xOTkgMTQ2LjgxNCwxNy4yNDkgMTQ2LjgxNCwxNi4yNjkgQzE0Ni44MTQsMTUuMjc4IDE0Ny41MzcsMTQuMzE0IDE0OC44NSwxMy41NTYgTDE1NC4yMzMsMTAuNDQ4IE0xNTQuMjMzLDkuNjM5IEwxNDguNDk5LDEyLjk0OSBDMTQ1LjMzMiwxNC43NzggMTQ1LjMxNiwxNy43MzMgMTQ4LjQ2MywxOS41NSBDMTUwLjAzMSwyMC40NTUgMTUyLjA4NiwyMC45MDcgMTU0LjE0NywyMC45MDcgQzE1Ni4yMjQsMjAuOTA3IDE1OC4zMDYsMjAuNDQ3IDE1OS44OTYsMTkuNTMgTDE2NS42MywxNi4yMTkgTDE1NC4yMzMsOS42MzkiIGlkPSJGaWxsLTMyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NS40NDUsNzIuNjY3IEwxNDUuNDQ1LDcyLjY2NyBDMTQzLjY3Miw3Mi42NjcgMTQyLjIwNCw3MS44MTcgMTQxLjIwMiw3MC40MjIgQzE0MS4xMzUsNzAuMzMgMTQxLjE0NSw3MC4xNDcgMTQxLjIyNSw3MC4wNjYgQzE0MS4zMDUsNjkuOTg1IDE0MS40MzIsNjkuOTQ2IDE0MS41MjUsNzAuMDExIEMxNDIuMzA2LDcwLjU1OSAxNDMuMjMxLDcwLjgyMyAxNDQuMjc2LDcwLjgyMiBDMTQ1LjU5OCw3MC44MjIgMTQ3LjAzLDcwLjM3NiAxNDguNTMyLDY5LjUwOSBDMTUzLjg0Miw2Ni40NDMgMTU4LjE2Myw1OC45ODcgMTU4LjE2Myw1Mi44OTQgQzE1OC4xNjMsNTAuOTY3IDE1Ny43MjEsNDkuMzMyIDE1Ni44ODQsNDguMTY4IEMxNTYuODE4LDQ4LjA3NiAxNTYuODI4LDQ3Ljk0OCAxNTYuOTA4LDQ3Ljg2NyBDMTU2Ljk4OCw0Ny43ODYgMTU3LjExNCw0Ny43NzQgMTU3LjIwOCw0Ny44NCBDMTU4Ljg3OCw0OS4wMTIgMTU5Ljc5OCw1MS4yMiAxNTkuNzk4LDU0LjA1OSBDMTU5Ljc5OCw2MC4zMDEgMTU1LjM3Myw2OC4wNDYgMTQ5LjkzMyw3MS4xODYgQzE0OC4zNiw3Mi4wOTQgMTQ2Ljg1LDcyLjY2NyAxNDUuNDQ1LDcyLjY2NyBMMTQ1LjQ0NSw3Mi42NjcgWiBNMTQyLjQ3Niw3MSBDMTQzLjI5LDcxLjY1MSAxNDQuMjk2LDcyLjAwMiAxNDUuNDQ1LDcyLjAwMiBDMTQ2Ljc2Nyw3Mi4wMDIgMTQ4LjE5OCw3MS41NSAxNDkuNyw3MC42ODIgQzE1NS4wMSw2Ny42MTcgMTU5LjMzMSw2MC4xNTkgMTU5LjMzMSw1NC4wNjUgQzE1OS4zMzEsNTIuMDg1IDE1OC44NjgsNTAuNDM1IDE1OC4wMDYsNDkuMjcyIEMxNTguNDE3LDUwLjMwNyAxNTguNjMsNTEuNTMyIDE1OC42Myw1Mi44OTIgQzE1OC42Myw1OS4xMzQgMTU0LjIwNSw2Ni43NjcgMTQ4Ljc2NSw2OS45MDcgQzE0Ny4xOTIsNzAuODE2IDE0NS42ODEsNzEuMjgzIDE0NC4yNzYsNzEuMjgzIEMxNDMuNjM0LDcxLjI4MyAxNDMuMDMzLDcxLjE5MiAxNDIuNDc2LDcxIEwxNDIuNDc2LDcxIFoiIGlkPSJGaWxsLTMzIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0OC42NDgsNjkuNzA0IEMxNTQuMDMyLDY2LjU5NiAxNTguMzk2LDU5LjA2OCAxNTguMzk2LDUyLjg5MSBDMTU4LjM5Niw1MC44MzkgMTU3LjkxMyw0OS4xOTggMTU3LjA3NCw0OC4wMyBDMTU1LjI4OSw0Ni43NzggMTUyLjY5OSw0Ni44MzYgMTQ5LjgxNiw0OC41MDEgQzE0NC40MzMsNTEuNjA5IDE0MC4wNjgsNTkuMTM3IDE0MC4wNjgsNjUuMzE0IEMxNDAuMDY4LDY3LjM2NSAxNDAuNTUyLDY5LjAwNiAxNDEuMzkxLDcwLjE3NCBDMTQzLjE3Niw3MS40MjcgMTQ1Ljc2NSw3MS4zNjkgMTQ4LjY0OCw2OS43MDQiIGlkPSJGaWxsLTM0IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NC4yNzYsNzEuMjc2IEwxNDQuMjc2LDcxLjI3NiBDMTQzLjEzMyw3MS4yNzYgMTQyLjExOCw3MC45NjkgMTQxLjI1Nyw3MC4zNjUgQzE0MS4yMzYsNzAuMzUxIDE0MS4yMTcsNzAuMzMyIDE0MS4yMDIsNzAuMzExIEMxNDAuMzA3LDY5LjA2NyAxMzkuODM1LDY3LjMzOSAxMzkuODM1LDY1LjMxNCBDMTM5LjgzNSw1OS4wNzMgMTQ0LjI2LDUxLjQzOSAxNDkuNyw0OC4yOTggQzE1MS4yNzMsNDcuMzkgMTUyLjc4NCw0Ni45MjkgMTU0LjE4OSw0Ni45MjkgQzE1NS4zMzIsNDYuOTI5IDE1Ni4zNDcsNDcuMjM2IDE1Ny4yMDgsNDcuODM5IEMxNTcuMjI5LDQ3Ljg1NCAxNTcuMjQ4LDQ3Ljg3MyAxNTcuMjYzLDQ3Ljg5NCBDMTU4LjE1Nyw0OS4xMzggMTU4LjYzLDUwLjg2NSAxNTguNjMsNTIuODkxIEMxNTguNjMsNTkuMTMyIDE1NC4yMDUsNjYuNzY2IDE0OC43NjUsNjkuOTA3IEMxNDcuMTkyLDcwLjgxNSAxNDUuNjgxLDcxLjI3NiAxNDQuMjc2LDcxLjI3NiBMMTQ0LjI3Niw3MS4yNzYgWiBNMTQxLjU1OCw3MC4xMDQgQzE0Mi4zMzEsNzAuNjM3IDE0My4yNDUsNzEuMDA1IDE0NC4yNzYsNzEuMDA1IEMxNDUuNTk4LDcxLjAwNSAxNDcuMDMsNzAuNDY3IDE0OC41MzIsNjkuNiBDMTUzLjg0Miw2Ni41MzQgMTU4LjE2Myw1OS4wMzMgMTU4LjE2Myw1Mi45MzkgQzE1OC4xNjMsNTEuMDMxIDE1Ny43MjksNDkuMzg1IDE1Ni45MDcsNDguMjIzIEMxNTYuMTMzLDQ3LjY5MSAxNTUuMjE5LDQ3LjQwOSAxNTQuMTg5LDQ3LjQwOSBDMTUyLjg2Nyw0Ny40MDkgMTUxLjQzNSw0Ny44NDIgMTQ5LjkzMyw0OC43MDkgQzE0NC42MjMsNTEuNzc1IDE0MC4zMDIsNTkuMjczIDE0MC4zMDIsNjUuMzY2IEMxNDAuMzAyLDY3LjI3NiAxNDAuNzM2LDY4Ljk0MiAxNDEuNTU4LDcwLjEwNCBMMTQxLjU1OCw3MC4xMDQgWiIgaWQ9IkZpbGwtMzUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUwLjcyLDY1LjM2MSBMMTUwLjM1Nyw2NS4wNjYgQzE1MS4xNDcsNjQuMDkyIDE1MS44NjksNjMuMDQgMTUyLjUwNSw2MS45MzggQzE1My4zMTMsNjAuNTM5IDE1My45NzgsNTkuMDY3IDE1NC40ODIsNTcuNTYzIEwxNTQuOTI1LDU3LjcxMiBDMTU0LjQxMiw1OS4yNDUgMTUzLjczMyw2MC43NDUgMTUyLjkxLDYyLjE3MiBDMTUyLjI2Miw2My4yOTUgMTUxLjUyNSw2NC4zNjggMTUwLjcyLDY1LjM2MSIgaWQ9IkZpbGwtMzYiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE1LjkxNyw4NC41MTQgTDExNS41NTQsODQuMjIgQzExNi4zNDQsODMuMjQ1IDExNy4wNjYsODIuMTk0IDExNy43MDIsODEuMDkyIEMxMTguNTEsNzkuNjkyIDExOS4xNzUsNzguMjIgMTE5LjY3OCw3Ni43MTcgTDEyMC4xMjEsNzYuODY1IEMxMTkuNjA4LDc4LjM5OCAxMTguOTMsNzkuODk5IDExOC4xMDYsODEuMzI2IEMxMTcuNDU4LDgyLjQ0OCAxMTYuNzIyLDgzLjUyMSAxMTUuOTE3LDg0LjUxNCIgaWQ9IkZpbGwtMzciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE0LDEzMC40NzYgTDExNCwxMzAuMDA4IEwxMTQsNzYuMDUyIEwxMTQsNzUuNTg0IEwxMTQsNzYuMDUyIEwxMTQsMTMwLjAwOCBMMTE0LDEzMC40NzYiIGlkPSJGaWxsLTM4IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYyLjAwMDAwMCwgMC4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTkuODIyLDM3LjQ3NCBDMTkuODM5LDM3LjMzOSAxOS43NDcsMzcuMTk0IDE5LjU1NSwzNy4wODIgQzE5LjIyOCwzNi44OTQgMTguNzI5LDM2Ljg3MiAxOC40NDYsMzcuMDM3IEwxMi40MzQsNDAuNTA4IEMxMi4zMDMsNDAuNTg0IDEyLjI0LDQwLjY4NiAxMi4yNDMsNDAuNzkzIEMxMi4yNDUsNDAuOTI1IDEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQxLjM3MSBMMTIuMjQ1LDQxLjQxNCBMMTIuMjM4LDQxLjU0MiBDOC4xNDgsNDMuODg3IDUuNjQ3LDQ1LjMyMSA1LjY0Nyw0NS4zMjEgQzUuNjQ2LDQ1LjMyMSAzLjU3LDQ2LjM2NyAyLjg2LDUwLjUxMyBDMi44Niw1MC41MTMgMS45NDgsNTcuNDc0IDEuOTYyLDcwLjI1OCBDMS45NzcsODIuODI4IDIuNTY4LDg3LjMyOCAzLjEyOSw5MS42MDkgQzMuMzQ5LDkzLjI5MyA2LjEzLDkzLjczNCA2LjEzLDkzLjczNCBDNi40NjEsOTMuNzc0IDYuODI4LDkzLjcwNyA3LjIxLDkzLjQ4NiBMODIuNDgzLDQ5LjkzNSBDODQuMjkxLDQ4Ljg2NiA4NS4xNSw0Ni4yMTYgODUuNTM5LDQzLjY1MSBDODYuNzUyLDM1LjY2MSA4Ny4yMTQsMTAuNjczIDg1LjI2NCwzLjc3MyBDODUuMDY4LDMuMDggODQuNzU0LDIuNjkgODQuMzk2LDIuNDkxIEw4Mi4zMSwxLjcwMSBDODEuNTgzLDEuNzI5IDgwLjg5NCwyLjE2OCA4MC43NzYsMi4yMzYgQzgwLjYzNiwyLjMxNyA0MS44MDcsMjQuNTg1IDIwLjAzMiwzNy4wNzIgTDE5LjgyMiwzNy40NzQiIGlkPSJGaWxsLTEiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNODIuMzExLDEuNzAxIEw4NC4zOTYsMi40OTEgQzg0Ljc1NCwyLjY5IDg1LjA2OCwzLjA4IDg1LjI2NCwzLjc3MyBDODcuMjEzLDEwLjY3MyA4Ni43NTEsMzUuNjYgODUuNTM5LDQzLjY1MSBDODUuMTQ5LDQ2LjIxNiA4NC4yOSw0OC44NjYgODIuNDgzLDQ5LjkzNSBMNy4yMSw5My40ODYgQzYuODk3LDkzLjY2NyA2LjU5NSw5My43NDQgNi4zMTQsOTMuNzQ0IEw2LjEzMSw5My43MzMgQzYuMTMxLDkzLjczNCAzLjM0OSw5My4yOTMgMy4xMjgsOTEuNjA5IEMyLjU2OCw4Ny4zMjcgMS45NzcsODIuODI4IDEuOTYzLDcwLjI1OCBDMS45NDgsNTcuNDc0IDIuODYsNTAuNTEzIDIuODYsNTAuNTEzIEMzLjU3LDQ2LjM2NyA1LjY0Nyw0NS4zMjEgNS42NDcsNDUuMzIxIEM1LjY0Nyw0NS4zMjEgOC4xNDgsNDMuODg3IDEyLjIzOCw0MS41NDIgTDEyLjI0NSw0MS40MTQgTDEyLjI0NSw0MS4zNzEgQzEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQwLjkyNSAxMi4yNDMsNDAuNzkzIEMxMi4yNCw0MC42ODYgMTIuMzAyLDQwLjU4MyAxMi40MzQsNDAuNTA4IEwxOC40NDYsMzcuMDM2IEMxOC41NzQsMzYuOTYyIDE4Ljc0NiwzNi45MjYgMTguOTI3LDM2LjkyNiBDMTkuMTQ1LDM2LjkyNiAxOS4zNzYsMzYuOTc5IDE5LjU1NCwzNy4wODIgQzE5Ljc0NywzNy4xOTQgMTkuODM5LDM3LjM0IDE5LjgyMiwzNy40NzQgTDIwLjAzMywzNy4wNzIgQzQxLjgwNiwyNC41ODUgODAuNjM2LDIuMzE4IDgwLjc3NywyLjIzNiBDODAuODk0LDIuMTY4IDgxLjU4MywxLjcyOSA4Mi4zMTEsMS43MDEgTTgyLjMxMSwwLjcwNCBMODIuMjcyLDAuNzA1IEM4MS42NTQsMC43MjggODAuOTg5LDAuOTQ5IDgwLjI5OCwxLjM2MSBMODAuMjc3LDEuMzczIEM4MC4xMjksMS40NTggNTkuNzY4LDEzLjEzNSAxOS43NTgsMzYuMDc5IEMxOS41LDM1Ljk4MSAxOS4yMTQsMzUuOTI5IDE4LjkyNywzNS45MjkgQzE4LjU2MiwzNS45MjkgMTguMjIzLDM2LjAxMyAxNy45NDcsMzYuMTczIEwxMS45MzUsMzkuNjQ0IEMxMS40OTMsMzkuODk5IDExLjIzNiw0MC4zMzQgMTEuMjQ2LDQwLjgxIEwxMS4yNDcsNDAuOTYgTDUuMTY3LDQ0LjQ0NyBDNC43OTQsNDQuNjQ2IDIuNjI1LDQ1Ljk3OCAxLjg3Nyw1MC4zNDUgTDEuODcxLDUwLjM4NCBDMS44NjIsNTAuNDU0IDAuOTUxLDU3LjU1NyAwLjk2NSw3MC4yNTkgQzAuOTc5LDgyLjg3OSAxLjU2OCw4Ny4zNzUgMi4xMzcsOTEuNzI0IEwyLjEzOSw5MS43MzkgQzIuNDQ3LDk0LjA5NCA1LjYxNCw5NC42NjIgNS45NzUsOTQuNzE5IEw2LjAwOSw5NC43MjMgQzYuMTEsOTQuNzM2IDYuMjEzLDk0Ljc0MiA2LjMxNCw5NC43NDIgQzYuNzksOTQuNzQyIDcuMjYsOTQuNjEgNy43MSw5NC4zNSBMODIuOTgzLDUwLjc5OCBDODQuNzk0LDQ5LjcyNyA4NS45ODIsNDcuMzc1IDg2LjUyNSw0My44MDEgQzg3LjcxMSwzNS45ODcgODguMjU5LDEwLjcwNSA4Ni4yMjQsMy41MDIgQzg1Ljk3MSwyLjYwOSA4NS41MiwxLjk3NSA4NC44ODEsMS42MiBMODQuNzQ5LDEuNTU4IEw4Mi42NjQsMC43NjkgQzgyLjU1MSwwLjcyNSA4Mi40MzEsMC43MDQgODIuMzExLDAuNzA0IiBpZD0iRmlsbC0yIiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY2LjI2NywxMS41NjUgTDY3Ljc2MiwxMS45OTkgTDExLjQyMyw0NC4zMjUiIGlkPSJGaWxsLTMiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMjAyLDkwLjU0NSBDMTIuMDI5LDkwLjU0NSAxMS44NjIsOTAuNDU1IDExLjc2OSw5MC4yOTUgQzExLjYzMiw5MC4wNTcgMTEuNzEzLDg5Ljc1MiAxMS45NTIsODkuNjE0IEwzMC4zODksNzguOTY5IEMzMC42MjgsNzguODMxIDMwLjkzMyw3OC45MTMgMzEuMDcxLDc5LjE1MiBDMzEuMjA4LDc5LjM5IDMxLjEyNyw3OS42OTYgMzAuODg4LDc5LjgzMyBMMTIuNDUxLDkwLjQ3OCBMMTIuMjAyLDkwLjU0NSIgaWQ9IkZpbGwtNCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMy43NjQsNDIuNjU0IEwxMy42NTYsNDIuNTkyIEwxMy43MDIsNDIuNDIxIEwxOC44MzcsMzkuNDU3IEwxOS4wMDcsMzkuNTAyIEwxOC45NjIsMzkuNjczIEwxMy44MjcsNDIuNjM3IEwxMy43NjQsNDIuNjU0IiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTguNTIsOTAuMzc1IEw4LjUyLDQ2LjQyMSBMOC41ODMsNDYuMzg1IEw3NS44NCw3LjU1NCBMNzUuODQsNTEuNTA4IEw3NS43NzgsNTEuNTQ0IEw4LjUyLDkwLjM3NSBMOC41Miw5MC4zNzUgWiBNOC43Nyw0Ni41NjQgTDguNzcsODkuOTQ0IEw3NS41OTEsNTEuMzY1IEw3NS41OTEsNy45ODUgTDguNzcsNDYuNTY0IEw4Ljc3LDQ2LjU2NCBaIiBpZD0iRmlsbC02IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTI0Ljk4Niw4My4xODIgQzI0Ljc1Niw4My4zMzEgMjQuMzc0LDgzLjU2NiAyNC4xMzcsODMuNzA1IEwxMi42MzIsOTAuNDA2IEMxMi4zOTUsOTAuNTQ1IDEyLjQyNiw5MC42NTggMTIuNyw5MC42NTggTDEzLjI2NSw5MC42NTggQzEzLjU0LDkwLjY1OCAxMy45NTgsOTAuNTQ1IDE0LjE5NSw5MC40MDYgTDI1LjcsODMuNzA1IEMyNS45MzcsODMuNTY2IDI2LjEyOCw4My40NTIgMjYuMTI1LDgzLjQ0OSBDMjYuMTIyLDgzLjQ0NyAyNi4xMTksODMuMjIgMjYuMTE5LDgyLjk0NiBDMjYuMTE5LDgyLjY3MiAyNS45MzEsODIuNTY5IDI1LjcwMSw4Mi43MTkgTDI0Ljk4Niw4My4xODIiIGlkPSJGaWxsLTciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTMuMjY2LDkwLjc4MiBMMTIuNyw5MC43ODIgQzEyLjUsOTAuNzgyIDEyLjM4NCw5MC43MjYgMTIuMzU0LDkwLjYxNiBDMTIuMzI0LDkwLjUwNiAxMi4zOTcsOTAuMzk5IDEyLjU2OSw5MC4yOTkgTDI0LjA3NCw4My41OTcgQzI0LjMxLDgzLjQ1OSAyNC42ODksODMuMjI2IDI0LjkxOCw4My4wNzggTDI1LjYzMyw4Mi42MTQgQzI1LjcyMyw4Mi41NTUgMjUuODEzLDgyLjUyNSAyNS44OTksODIuNTI1IEMyNi4wNzEsODIuNTI1IDI2LjI0NCw4Mi42NTUgMjYuMjQ0LDgyLjk0NiBDMjYuMjQ0LDgzLjE2IDI2LjI0NSw4My4zMDkgMjYuMjQ3LDgzLjM4MyBMMjYuMjUzLDgzLjM4NyBMMjYuMjQ5LDgzLjQ1NiBDMjYuMjQ2LDgzLjUzMSAyNi4yNDYsODMuNTMxIDI1Ljc2Myw4My44MTIgTDE0LjI1OCw5MC41MTQgQzE0LDkwLjY2NSAxMy41NjQsOTAuNzgyIDEzLjI2Niw5MC43ODIgTDEzLjI2Niw5MC43ODIgWiBNMTIuNjY2LDkwLjUzMiBMMTIuNyw5MC41MzMgTDEzLjI2Niw5MC41MzMgQzEzLjUxOCw5MC41MzMgMTMuOTE1LDkwLjQyNSAxNC4xMzIsOTAuMjk5IEwyNS42MzcsODMuNTk3IEMyNS44MDUsODMuNDk5IDI1LjkzMSw4My40MjQgMjUuOTk4LDgzLjM4MyBDMjUuOTk0LDgzLjI5OSAyNS45OTQsODMuMTY1IDI1Ljk5NCw4Mi45NDYgTDI1Ljg5OSw4Mi43NzUgTDI1Ljc2OCw4Mi44MjQgTDI1LjA1NCw4My4yODcgQzI0LjgyMiw4My40MzcgMjQuNDM4LDgzLjY3MyAyNC4yLDgzLjgxMiBMMTIuNjk1LDkwLjUxNCBMMTIuNjY2LDkwLjUzMiBMMTIuNjY2LDkwLjUzMiBaIiBpZD0iRmlsbC04IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEzLjI2Niw4OS44NzEgTDEyLjcsODkuODcxIEMxMi41LDg5Ljg3MSAxMi4zODQsODkuODE1IDEyLjM1NCw4OS43MDUgQzEyLjMyNCw4OS41OTUgMTIuMzk3LDg5LjQ4OCAxMi41NjksODkuMzg4IEwyNC4wNzQsODIuNjg2IEMyNC4zMzIsODIuNTM1IDI0Ljc2OCw4Mi40MTggMjUuMDY3LDgyLjQxOCBMMjUuNjMyLDgyLjQxOCBDMjUuODMyLDgyLjQxOCAyNS45NDgsODIuNDc0IDI1Ljk3OCw4Mi41ODQgQzI2LjAwOCw4Mi42OTQgMjUuOTM1LDgyLjgwMSAyNS43NjMsODIuOTAxIEwxNC4yNTgsODkuNjAzIEMxNCw4OS43NTQgMTMuNTY0LDg5Ljg3MSAxMy4yNjYsODkuODcxIEwxMy4yNjYsODkuODcxIFogTTEyLjY2Niw4OS42MjEgTDEyLjcsODkuNjIyIEwxMy4yNjYsODkuNjIyIEMxMy41MTgsODkuNjIyIDEzLjkxNSw4OS41MTUgMTQuMTMyLDg5LjM4OCBMMjUuNjM3LDgyLjY4NiBMMjUuNjY3LDgyLjY2OCBMMjUuNjMyLDgyLjY2NyBMMjUuMDY3LDgyLjY2NyBDMjQuODE1LDgyLjY2NyAyNC40MTgsODIuNzc1IDI0LjIsODIuOTAxIEwxMi42OTUsODkuNjAzIEwxMi42NjYsODkuNjIxIEwxMi42NjYsODkuNjIxIFoiIGlkPSJGaWxsLTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMzcsOTAuODAxIEwxMi4zNyw4OS41NTQgTDEyLjM3LDkwLjgwMSIgaWQ9IkZpbGwtMTAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNi4xMyw5My45MDEgQzUuMzc5LDkzLjgwOCA0LjgxNiw5My4xNjQgNC42OTEsOTIuNTI1IEMzLjg2LDg4LjI4NyAzLjU0LDgzLjc0MyAzLjUyNiw3MS4xNzMgQzMuNTExLDU4LjM4OSA0LjQyMyw1MS40MjggNC40MjMsNTEuNDI4IEM1LjEzNCw0Ny4yODIgNy4yMSw0Ni4yMzYgNy4yMSw0Ni4yMzYgQzcuMjEsNDYuMjM2IDgxLjY2NywzLjI1IDgyLjA2OSwzLjAxNyBDODIuMjkyLDIuODg4IDg0LjU1NiwxLjQzMyA4NS4yNjQsMy45NCBDODcuMjE0LDEwLjg0IDg2Ljc1MiwzNS44MjcgODUuNTM5LDQzLjgxOCBDODUuMTUsNDYuMzgzIDg0LjI5MSw0OS4wMzMgODIuNDgzLDUwLjEwMSBMNy4yMSw5My42NTMgQzYuODI4LDkzLjg3NCA2LjQ2MSw5My45NDEgNi4xMyw5My45MDEgQzYuMTMsOTMuOTAxIDMuMzQ5LDkzLjQ2IDMuMTI5LDkxLjc3NiBDMi41NjgsODcuNDk1IDEuOTc3LDgyLjk5NSAxLjk2Miw3MC40MjUgQzEuOTQ4LDU3LjY0MSAyLjg2LDUwLjY4IDIuODYsNTAuNjggQzMuNTcsNDYuNTM0IDUuNjQ3LDQ1LjQ4OSA1LjY0Nyw0NS40ODkgQzUuNjQ2LDQ1LjQ4OSA4LjA2NSw0NC4wOTIgMTIuMjQ1LDQxLjY3OSBMMTMuMTE2LDQxLjU2IEwxOS43MTUsMzcuNzMgTDE5Ljc2MSwzNy4yNjkgTDYuMTMsOTMuOTAxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjMxNyw5NC4xNjEgTDYuMTAyLDk0LjE0OCBMNi4xMDEsOTQuMTQ4IEw1Ljg1Nyw5NC4xMDEgQzUuMTM4LDkzLjk0NSAzLjA4NSw5My4zNjUgMi44ODEsOTEuODA5IEMyLjMxMyw4Ny40NjkgMS43MjcsODIuOTk2IDEuNzEzLDcwLjQyNSBDMS42OTksNTcuNzcxIDIuNjA0LDUwLjcxOCAyLjYxMyw1MC42NDggQzMuMzM4LDQ2LjQxNyA1LjQ0NSw0NS4zMSA1LjUzNSw0NS4yNjYgTDEyLjE2Myw0MS40MzkgTDEzLjAzMyw0MS4zMiBMMTkuNDc5LDM3LjU3OCBMMTkuNTEzLDM3LjI0NCBDMTkuNTI2LDM3LjEwNyAxOS42NDcsMzcuMDA4IDE5Ljc4NiwzNy4wMjEgQzE5LjkyMiwzNy4wMzQgMjAuMDIzLDM3LjE1NiAyMC4wMDksMzcuMjkzIEwxOS45NSwzNy44ODIgTDEzLjE5OCw0MS44MDEgTDEyLjMyOCw0MS45MTkgTDUuNzcyLDQ1LjcwNCBDNS43NDEsNDUuNzIgMy43ODIsNDYuNzcyIDMuMTA2LDUwLjcyMiBDMy4wOTksNTAuNzgyIDIuMTk4LDU3LjgwOCAyLjIxMiw3MC40MjQgQzIuMjI2LDgyLjk2MyAyLjgwOSw4Ny40MiAzLjM3Myw5MS43MjkgQzMuNDY0LDkyLjQyIDQuMDYyLDkyLjg4MyA0LjY4Miw5My4xODEgQzQuNTY2LDkyLjk4NCA0LjQ4Niw5Mi43NzYgNC40NDYsOTIuNTcyIEMzLjY2NSw4OC41ODggMy4yOTEsODQuMzcgMy4yNzYsNzEuMTczIEMzLjI2Miw1OC41MiA0LjE2Nyw1MS40NjYgNC4xNzYsNTEuMzk2IEM0LjkwMSw0Ny4xNjUgNy4wMDgsNDYuMDU5IDcuMDk4LDQ2LjAxNCBDNy4wOTQsNDYuMDE1IDgxLjU0MiwzLjAzNCA4MS45NDQsMi44MDIgTDgxLjk3MiwyLjc4NSBDODIuODc2LDIuMjQ3IDgzLjY5MiwyLjA5NyA4NC4zMzIsMi4zNTIgQzg0Ljg4NywyLjU3MyA4NS4yODEsMy4wODUgODUuNTA0LDMuODcyIEM4Ny41MTgsMTEgODYuOTY0LDM2LjA5MSA4NS43ODUsNDMuODU1IEM4NS4yNzgsNDcuMTk2IDg0LjIxLDQ5LjM3IDgyLjYxLDUwLjMxNyBMNy4zMzUsOTMuODY5IEM2Ljk5OSw5NC4wNjMgNi42NTgsOTQuMTYxIDYuMzE3LDk0LjE2MSBMNi4zMTcsOTQuMTYxIFogTTYuMTcsOTMuNjU0IEM2LjQ2Myw5My42OSA2Ljc3NCw5My42MTcgNy4wODUsOTMuNDM3IEw4Mi4zNTgsNDkuODg2IEM4NC4xODEsNDguODA4IDg0Ljk2LDQ1Ljk3MSA4NS4yOTIsNDMuNzggQzg2LjQ2NiwzNi4wNDkgODcuMDIzLDExLjA4NSA4NS4wMjQsNC4wMDggQzg0Ljg0NiwzLjM3NyA4NC41NTEsMi45NzYgODQuMTQ4LDIuODE2IEM4My42NjQsMi42MjMgODIuOTgyLDIuNzY0IDgyLjIyNywzLjIxMyBMODIuMTkzLDMuMjM0IEM4MS43OTEsMy40NjYgNy4zMzUsNDYuNDUyIDcuMzM1LDQ2LjQ1MiBDNy4zMDQsNDYuNDY5IDUuMzQ2LDQ3LjUyMSA0LjY2OSw1MS40NzEgQzQuNjYyLDUxLjUzIDMuNzYxLDU4LjU1NiAzLjc3NSw3MS4xNzMgQzMuNzksODQuMzI4IDQuMTYxLDg4LjUyNCA0LjkzNiw5Mi40NzYgQzUuMDI2LDkyLjkzNyA1LjQxMiw5My40NTkgNS45NzMsOTMuNjE1IEM2LjA4Nyw5My42NCA2LjE1OCw5My42NTIgNi4xNjksOTMuNjU0IEw2LjE3LDkzLjY1NCBMNi4xNyw5My42NTQgWiIgaWQ9IkZpbGwtMTIiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4zMTcsNjguOTgyIEM3LjgwNiw2OC43MDEgOC4yMDIsNjguOTI2IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNi44MjksNzEuMjk0IDYuNDMzLDcxLjA2OSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIiBpZD0iRmlsbC0xMyIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjkyLDcxLjEzMyBDNi42MzEsNzEuMTMzIDYuNDMzLDcwLjkwNSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIEM3LjQ2LDY4LjkgNy41OTUsNjguODYxIDcuNzE0LDY4Ljg2MSBDOC4wMDMsNjguODYxIDguMjAyLDY5LjA5IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNy4xNzQsNzEuMDk0IDcuMDM5LDcxLjEzMyA2LjkyLDcxLjEzMyBNNy43MTQsNjguNjc0IEM3LjU1Nyw2OC42NzQgNy4zOTIsNjguNzIzIDcuMjI0LDY4LjgyMSBDNi42NzYsNjkuMTM4IDYuMjQ2LDY5Ljg3OSA2LjI0Niw3MC41MDggQzYuMjQ2LDcwLjk5NCA2LjUxNyw3MS4zMiA2LjkyLDcxLjMyIEM3LjA3OCw3MS4zMiA3LjI0Myw3MS4yNzEgNy40MTEsNzEuMTc0IEM3Ljk1OSw3MC44NTcgOC4zODksNzAuMTE3IDguMzg5LDY5LjQ4NyBDOC4zODksNjkuMDAxIDguMTE3LDY4LjY3NCA3LjcxNCw2OC42NzQiIGlkPSJGaWxsLTE0IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYuOTIsNzAuOTQ3IEM2LjY0OSw3MC45NDcgNi42MjEsNzAuNjQgNi42MjEsNzAuNTA4IEM2LjYyMSw3MC4wMTcgNi45ODIsNjkuMzkyIDcuNDExLDY5LjE0NSBDNy41MjEsNjkuMDgyIDcuNjI1LDY5LjA0OSA3LjcxNCw2OS4wNDkgQzcuOTg2LDY5LjA0OSA4LjAxNSw2OS4zNTUgOC4wMTUsNjkuNDg3IEM4LjAxNSw2OS45NzggNy42NTIsNzAuNjAzIDcuMjI0LDcwLjg1MSBDNy4xMTUsNzAuOTE0IDcuMDEsNzAuOTQ3IDYuOTIsNzAuOTQ3IE03LjcxNCw2OC44NjEgQzcuNTk1LDY4Ljg2MSA3LjQ2LDY4LjkgNy4zMTcsNjguOTgyIEM2LjgyOSw2OS4yNjUgNi40MzMsNjkuOTQ4IDYuNDMzLDcwLjUwOCBDNi40MzMsNzAuOTA1IDYuNjMxLDcxLjEzMyA2LjkyLDcxLjEzMyBDNy4wMzksNzEuMTMzIDcuMTc0LDcxLjA5NCA3LjMxNyw3MS4wMTIgQzcuODA2LDcwLjczIDguMjAyLDcwLjA0NyA4LjIwMiw2OS40ODcgQzguMjAyLDY5LjA5IDguMDAzLDY4Ljg2MSA3LjcxNCw2OC44NjEiIGlkPSJGaWxsLTE1IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTcuNDQ0LDg1LjM1IEM3LjcwOCw4NS4xOTggNy45MjEsODUuMzE5IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuOTI1IDcuNzA4LDg2LjI5MiA3LjQ0NCw4Ni40NDQgQzcuMTgxLDg2LjU5NyA2Ljk2Nyw4Ni40NzUgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IiBpZD0iRmlsbC0xNiIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik03LjIzLDg2LjUxIEM3LjA3NCw4Ni41MSA2Ljk2Nyw4Ni4zODcgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IEM3LjUyMSw4NS4zMDUgNy41OTQsODUuMjg0IDcuNjU4LDg1LjI4NCBDNy44MTQsODUuMjg0IDcuOTIxLDg1LjQwOCA3LjkyMSw4NS42MjIgQzcuOTIxLDg1LjkyNSA3LjcwOCw4Ni4yOTIgNy40NDQsODYuNDQ0IEM3LjM2Nyw4Ni40ODkgNy4yOTQsODYuNTEgNy4yMyw4Ni41MSBNNy42NTgsODUuMDk4IEM3LjU1OCw4NS4wOTggNy40NTUsODUuMTI3IDcuMzUxLDg1LjE4OCBDNy4wMzEsODUuMzczIDYuNzgxLDg1LjgwNiA2Ljc4MSw4Ni4xNzMgQzYuNzgxLDg2LjQ4MiA2Ljk2Niw4Ni42OTcgNy4yMyw4Ni42OTcgQzcuMzMsODYuNjk3IDcuNDMzLDg2LjY2NiA3LjUzOCw4Ni42MDcgQzcuODU4LDg2LjQyMiA4LjEwOCw4NS45ODkgOC4xMDgsODUuNjIyIEM4LjEwOCw4NS4zMTMgNy45MjMsODUuMDk4IDcuNjU4LDg1LjA5OCIgaWQ9IkZpbGwtMTciIGZpbGw9IiM4MDk3QTIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4yMyw4Ni4zMjIgTDcuMTU0LDg2LjE3MyBDNy4xNTQsODUuOTM4IDcuMzMzLDg1LjYyOSA3LjUzOCw4NS41MTIgTDcuNjU4LDg1LjQ3MSBMNy43MzQsODUuNjIyIEM3LjczNCw4NS44NTYgNy41NTUsODYuMTY0IDcuMzUxLDg2LjI4MiBMNy4yMyw4Ni4zMjIgTTcuNjU4LDg1LjI4NCBDNy41OTQsODUuMjg0IDcuNTIxLDg1LjMwNSA3LjQ0NCw4NS4zNSBDNy4xODEsODUuNTAyIDYuOTY3LDg1Ljg3MSA2Ljk2Nyw4Ni4xNzMgQzYuOTY3LDg2LjM4NyA3LjA3NCw4Ni41MSA3LjIzLDg2LjUxIEM3LjI5NCw4Ni41MSA3LjM2Nyw4Ni40ODkgNy40NDQsODYuNDQ0IEM3LjcwOCw4Ni4yOTIgNy45MjEsODUuOTI1IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuNDA4IDcuODE0LDg1LjI4NCA3LjY1OCw4NS4yODQiIGlkPSJGaWxsLTE4IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTc3LjI3OCw3Ljc2OSBMNzcuMjc4LDUxLjQzNiBMMTAuMjA4LDkwLjE2IEwxMC4yMDgsNDYuNDkzIEw3Ny4yNzgsNy43NjkiIGlkPSJGaWxsLTE5IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEwLjA4Myw5MC4zNzUgTDEwLjA4Myw0Ni40MjEgTDEwLjE0Niw0Ni4zODUgTDc3LjQwMyw3LjU1NCBMNzcuNDAzLDUxLjUwOCBMNzcuMzQxLDUxLjU0NCBMMTAuMDgzLDkwLjM3NSBMMTAuMDgzLDkwLjM3NSBaIE0xMC4zMzMsNDYuNTY0IEwxMC4zMzMsODkuOTQ0IEw3Ny4xNTQsNTEuMzY1IEw3Ny4xNTQsNy45ODUgTDEwLjMzMyw0Ni41NjQgTDEwLjMzMyw0Ni41NjQgWiIgaWQ9IkZpbGwtMjAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMjUuNzM3LDg4LjY0NyBMMTE4LjA5OCw5MS45ODEgTDExOC4wOTgsODQgTDEwNi42MzksODguNzEzIEwxMDYuNjM5LDk2Ljk4MiBMOTksMTAwLjMxNSBMMTEyLjM2OSwxMDMuOTYxIEwxMjUuNzM3LDg4LjY0NyIgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTIiIGZpbGw9IiM0NTVBNjQiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+'); +}; + +module.exports = RotateInstructions; + +},{"./util.js":13}],13:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var Util = {}; + +Util.base64 = function(mimeType, base64) { + return 'data:' + mimeType + ';base64,' + base64; +}; + +Util.isMobile = function() { + var check = false; + (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); + return check; +}; + +Util.isFirefox = function() { + return /firefox/i.test(navigator.userAgent); +}; + +Util.isIOS = function() { + return /(iPad|iPhone|iPod)/g.test(navigator.userAgent); +}; + +Util.isIFrame = function() { + try { + return window.self !== window.top; + } catch (e) { + return true; + } +}; + +Util.appendQueryParameter = function(url, key, value) { + // Determine delimiter based on if the URL already GET parameters in it. + var delimiter = (url.indexOf('?') < 0 ? '?' : '&'); + url += delimiter + key + '=' + value; + return url; +}; + +// From http://goo.gl/4WX3tg +Util.getQueryParameter = function(name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), + results = regex.exec(location.search); + return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); +}; + +Util.isLandscapeMode = function() { + return (window.orientation == 90 || window.orientation == -90); +}; + +Util.getScreenWidth = function() { + return Math.max(window.screen.width, window.screen.height) * + window.devicePixelRatio; +}; + +Util.getScreenHeight = function() { + return Math.min(window.screen.width, window.screen.height) * + window.devicePixelRatio; +}; + +/** + * Utility to convert the projection matrix to a vector accepted by the shader. + * + * @param {Object} opt_params A rectangle to scale this vector by. + */ +Util.projectionMatrixToVector_ = function(matrix, opt_params) { + var params = opt_params || {}; + var xScale = params.xScale || 1; + var yScale = params.yScale || 1; + var xTrans = params.xTrans || 0; + var yTrans = params.yTrans || 0; + + var elements = matrix.elements; + var vec = new THREE.Vector4(); + vec.set(elements[4*0 + 0] * xScale, + elements[4*1 + 1] * yScale, + elements[4*2 + 0] - 1 - xTrans, + elements[4*2 + 1] - 1 - yTrans).divideScalar(2); + return vec; +}; + +Util.leftProjectionVectorToRight_ = function(left) { + //projectionLeft + vec4(0.0, 0.0, 1.0, 0.0)) * vec4(1.0, 1.0, -1.0, 1.0); + var out = new THREE.Vector4(0, 0, 1, 0); + out.add(left); // out = left + (0, 0, 1, 0). + out.z *= -1; // Flip z. + + return out; +}; + +module.exports = Util; + +},{}],14:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var Emitter = _dereq_('./emitter.js'); +var Util = _dereq_('./util.js'); + +var DEFAULT_VIEWER = 'CardboardV1'; +var VIEWER_KEY = 'WEBVR_CARDBOARD_VIEWER'; + +/** + * Creates a viewer selector with the options specified. Supports being shown + * and hidden. Generates events when viewer parameters change. Also supports + * saving the currently selected index in localStorage. + * + * @param {Object} options Option labels for all valid selections {name: index}. + */ +function ViewerSelector(options) { + // Try to load the selected key from local storage. If none exists, use the + // default key. + try { + this.selectedKey = localStorage.getItem(VIEWER_KEY) || DEFAULT_VIEWER; + } catch(error) { + console.error('Failed to load viewer profile: %s', error); + } + this.dialog = this.createDialog_(options); + this.options = options; + document.body.appendChild(this.dialog); +} +ViewerSelector.prototype = new Emitter(); + +ViewerSelector.prototype.show = function() { + //console.log('ViewerSelector.show'); + + // Ensure the currently selected item is checked. + var selected = this.dialog.querySelector('#' + this.selectedKey); + selected.checked = true; + + // Show the UI. + this.dialog.style.display = 'block'; +}; + +ViewerSelector.prototype.hide = function() { + //console.log('ViewerSelector.hide'); + this.dialog.style.display = 'none'; +}; + +ViewerSelector.prototype.getSelectedKey_ = function() { + var input = this.dialog.querySelector('input[name=field]:checked'); + if (input) { + return input.id; + } + return null; +}; + +ViewerSelector.prototype.onSave_ = function() { + this.selectedKey = this.getSelectedKey_(); + if (!this.selectedKey || !this.options[this.selectedKey]) { + console.error('ViewerSelector.onSave_: this should never happen!'); + return; + } + + this.emit('change', this.options[this.selectedKey]); + + // Attempt to save the viewer profile, but fails in private mode. + try { + localStorage.setItem(VIEWER_KEY, this.selectedKey); + } catch(error) { + console.error('Failed to save viewer profile: %s', error); + } + this.hide(); +}; + +/** + * Creates the dialog. + */ +ViewerSelector.prototype.createDialog_ = function(options) { + var container = document.createElement('div'); + container.style.display = 'none'; + // Create an overlay that dims the background, and which goes away when you + // tap it. + var overlay = document.createElement('div'); + var s = overlay.style; + s.position = 'fixed'; + s.left = 0; + s.top = 0; + s.width = '100%'; + s.height = '100%'; + s.background = 'rgba(0, 0, 0, 0.3)'; + overlay.addEventListener('click', this.hide.bind(this)); + + var width = 280; + var dialog = document.createElement('div'); + var s = dialog.style; + s.boxSizing = 'border-box'; + s.position = 'fixed'; + s.top = '24px'; + s.left = '50%'; + s.marginLeft = (-width/2) + 'px'; + s.width = width + 'px'; + s.padding = '24px'; + s.overflow = 'hidden'; + s.background = '#fafafa'; + s.fontFamily = "'Roboto', sans-serif"; + s.boxShadow = '0px 5px 20px #666'; + + dialog.appendChild(this.createH1_('Select your viewer')); + for (var id in options) { + dialog.appendChild(this.createChoice_(id, options[id].label)); + } + dialog.appendChild(this.createButton_('Save', this.onSave_.bind(this))); + + container.appendChild(overlay); + container.appendChild(dialog); + + return container; +}; + +ViewerSelector.prototype.createH1_ = function(name) { + var h1 = document.createElement('h1'); + var s = h1.style; + s.color = 'black'; + s.fontSize = '20px'; + s.fontWeight = 'bold'; + s.marginTop = 0; + s.marginBottom = '24px'; + h1.innerHTML = name; + return h1; +}; + +ViewerSelector.prototype.createChoice_ = function(id, name) { + /* +

+ + +
+ */ + var div = document.createElement('div'); + div.style.marginTop = '8px'; + div.style.color = 'black'; + + var input = document.createElement('input'); + input.style.fontSize = '30px'; + input.setAttribute('id', id); + input.setAttribute('type', 'radio'); + input.setAttribute('value', id); + input.setAttribute('name', 'field'); + + var label = document.createElement('label'); + label.style.marginLeft = '4px'; + label.setAttribute('for', id); + label.innerHTML = name; + + div.appendChild(input); + div.appendChild(label); + + return div; +}; + +ViewerSelector.prototype.createButton_ = function(label, onclick) { + var button = document.createElement('button'); + button.innerHTML = label; + var s = button.style; + s.float = 'right'; + s.textTransform = 'uppercase'; + s.color = '#1094f7'; + s.fontSize = '14px'; + s.letterSpacing = 0; + s.border = 0; + s.background = 'none'; + s.marginTop = '16px'; + + button.addEventListener('click', onclick); + + return button; +}; + +module.exports = ViewerSelector; + +},{"./emitter.js":9,"./util.js":13}],15:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var Util = _dereq_('./util.js'); + +/** + * Android and iOS compatible wakelock implementation. + * + * Refactored thanks to dkovalev@. + */ +function AndroidWakeLock() { + var video = document.createElement('video'); + + video.addEventListener('ended', function() { + video.play(); + }); + + this.request = function() { + if (video.paused) { + // Base64 version of videos_src/no-sleep-120s.mp4. + video.src = Util.base64('video/mp4', 'AAAAGGZ0eXBpc29tAAAAAG1wNDFhdmMxAAAIA21vb3YAAABsbXZoZAAAAADSa9v60mvb+gABX5AAlw/gAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAdkdHJhawAAAFx0a2hkAAAAAdJr2/rSa9v6AAAAAQAAAAAAlw/gAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAQAAAAHAAAAAAAJGVkdHMAAAAcZWxzdAAAAAAAAAABAJcP4AAAAAAAAQAAAAAG3G1kaWEAAAAgbWRoZAAAAADSa9v60mvb+gAPQkAGjneAFccAAAAAAC1oZGxyAAAAAAAAAAB2aWRlAAAAAAAAAAAAAAAAVmlkZW9IYW5kbGVyAAAABodtaW5mAAAAFHZtaGQAAAABAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAAZHc3RibAAAAJdzdHNkAAAAAAAAAAEAAACHYXZjMQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAMABwASAAAAEgAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABj//wAAADFhdmNDAWQAC//hABlnZAALrNlfllw4QAAAAwBAAAADAKPFCmWAAQAFaOvssiwAAAAYc3R0cwAAAAAAAAABAAAAbgAPQkAAAAAUc3RzcwAAAAAAAAABAAAAAQAAA4BjdHRzAAAAAAAAAG4AAAABAD0JAAAAAAEAehIAAAAAAQA9CQAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEATEtAAAAAAQAehIAAAAABAAAAAAAAAAEAD0JAAAAAAQBMS0AAAAABAB6EgAAAAAEAAAAAAAAAAQAPQkAAAAABAExLQAAAAAEAHoSAAAAAAQAAAAAAAAABAA9CQAAAAAEALcbAAAAAHHN0c2MAAAAAAAAAAQAAAAEAAABuAAAAAQAAAcxzdHN6AAAAAAAAAAAAAABuAAADCQAAABgAAAAOAAAADgAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABIAAAAOAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAABMAAAAUc3RjbwAAAAAAAAABAAAIKwAAACt1ZHRhAAAAI6llbmMAFwAAdmxjIDIuMi4xIHN0cmVhbSBvdXRwdXQAAAAId2lkZQAACRRtZGF0AAACrgX//6vcRem95tlIt5Ys2CDZI+7veDI2NCAtIGNvcmUgMTQyIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTMgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MzoweDEzIG1lPWhleCBzdWJtZT03IHBzeT0xIHBzeV9yZD0xLjAwOjAuMDAgbWl4ZWRfcmVmPTEgbWVfcmFuZ2U9MTYgY2hyb21hX21lPTEgdHJlbGxpcz0xIDh4OGRjdD0xIGNxbT0wIGRlYWR6b25lPTIxLDExIGZhc3RfcHNraXA9MSBjaHJvbWFfcXBfb2Zmc2V0PS0yIHRocmVhZHM9MTIgbG9va2FoZWFkX3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRlcmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9MSBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRiPTEgb3Blbl9nb3A9MCB3ZWlnaHRwPTIga2V5aW50PTI1MCBrZXlpbnRfbWluPTEgc2NlbmVjdXQ9NDAgaW50cmFfcmVmcmVzaD0wIHJjX2xvb2thaGVhZD00MCByYz1hYnIgbWJ0cmVlPTEgYml0cmF0ZT0xMDAgcmF0ZXRvbD0xLjAgcWNvbXA9MC42MCBxcG1pbj0xMCBxcG1heD01MSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAU2WIhAAQ/8ltlOe+cTZuGkKg+aRtuivcDZ0pBsfsEi9p/i1yU9DxS2lq4dXTinViF1URBKXgnzKBd/Uh1bkhHtMrwrRcOJslD01UB+fyaL6ef+DBAAAAFEGaJGxBD5B+v+a+4QqF3MgBXz9MAAAACkGeQniH/+94r6EAAAAKAZ5hdEN/8QytwAAAAAgBnmNqQ3/EgQAAAA5BmmhJqEFomUwIIf/+4QAAAApBnoZFESw//76BAAAACAGepXRDf8SBAAAACAGep2pDf8SAAAAADkGarEmoQWyZTAgh//7gAAAACkGeykUVLD//voEAAAAIAZ7pdEN/xIAAAAAIAZ7rakN/xIAAAAAOQZrwSahBbJlMCCH//uEAAAAKQZ8ORRUsP/++gQAAAAgBny10Q3/EgQAAAAgBny9qQ3/EgAAAAA5BmzRJqEFsmUwIIf/+4AAAAApBn1JFFSw//76BAAAACAGfcXRDf8SAAAAACAGfc2pDf8SAAAAADkGbeEmoQWyZTAgh//7hAAAACkGflkUVLD//voAAAAAIAZ+1dEN/xIEAAAAIAZ+3akN/xIEAAAAOQZu8SahBbJlMCCH//uAAAAAKQZ/aRRUsP/++gQAAAAgBn/l0Q3/EgAAAAAgBn/tqQ3/EgQAAAA5Bm+BJqEFsmUwIIf/+4QAAAApBnh5FFSw//76AAAAACAGePXRDf8SAAAAACAGeP2pDf8SBAAAADkGaJEmoQWyZTAgh//7gAAAACkGeQkUVLD//voEAAAAIAZ5hdEN/xIAAAAAIAZ5jakN/xIEAAAAOQZpoSahBbJlMCCH//uEAAAAKQZ6GRRUsP/++gQAAAAgBnqV0Q3/EgQAAAAgBnqdqQ3/EgAAAAA5BmqxJqEFsmUwIIf/+4AAAAApBnspFFSw//76BAAAACAGe6XRDf8SAAAAACAGe62pDf8SAAAAADkGa8EmoQWyZTAgh//7hAAAACkGfDkUVLD//voEAAAAIAZ8tdEN/xIEAAAAIAZ8vakN/xIAAAAAOQZs0SahBbJlMCCH//uAAAAAKQZ9SRRUsP/++gQAAAAgBn3F0Q3/EgAAAAAgBn3NqQ3/EgAAAAA5Bm3hJqEFsmUwIIf/+4QAAAApBn5ZFFSw//76AAAAACAGftXRDf8SBAAAACAGft2pDf8SBAAAADkGbvEmoQWyZTAgh//7gAAAACkGf2kUVLD//voEAAAAIAZ/5dEN/xIAAAAAIAZ/7akN/xIEAAAAOQZvgSahBbJlMCCH//uEAAAAKQZ4eRRUsP/++gAAAAAgBnj10Q3/EgAAAAAgBnj9qQ3/EgQAAAA5BmiRJqEFsmUwIIf/+4AAAAApBnkJFFSw//76BAAAACAGeYXRDf8SAAAAACAGeY2pDf8SBAAAADkGaaEmoQWyZTAgh//7hAAAACkGehkUVLD//voEAAAAIAZ6ldEN/xIEAAAAIAZ6nakN/xIAAAAAOQZqsSahBbJlMCCH//uAAAAAKQZ7KRRUsP/++gQAAAAgBnul0Q3/EgAAAAAgBnutqQ3/EgAAAAA5BmvBJqEFsmUwIIf/+4QAAAApBnw5FFSw//76BAAAACAGfLXRDf8SBAAAACAGfL2pDf8SAAAAADkGbNEmoQWyZTAgh//7gAAAACkGfUkUVLD//voEAAAAIAZ9xdEN/xIAAAAAIAZ9zakN/xIAAAAAOQZt4SahBbJlMCCH//uEAAAAKQZ+WRRUsP/++gAAAAAgBn7V0Q3/EgQAAAAgBn7dqQ3/EgQAAAA5Bm7xJqEFsmUwIIf/+4AAAAApBn9pFFSw//76BAAAACAGf+XRDf8SAAAAACAGf+2pDf8SBAAAADkGb4EmoQWyZTAgh//7hAAAACkGeHkUVLD//voAAAAAIAZ49dEN/xIAAAAAIAZ4/akN/xIEAAAAOQZokSahBbJlMCCH//uAAAAAKQZ5CRRUsP/++gQAAAAgBnmF0Q3/EgAAAAAgBnmNqQ3/EgQAAAA5BmmhJqEFsmUwIIf/+4QAAAApBnoZFFSw//76BAAAACAGepXRDf8SBAAAACAGep2pDf8SAAAAADkGarEmoQWyZTAgh//7gAAAACkGeykUVLD//voEAAAAIAZ7pdEN/xIAAAAAIAZ7rakN/xIAAAAAPQZruSahBbJlMFEw3//7B'); + video.play(); + } + }; + + this.release = function() { + video.pause(); + video.src = ''; + }; +} + +function iOSWakeLock() { + var timer = null; + + this.request = function() { + if (!timer) { + timer = setInterval(function() { + window.location = window.location; + setTimeout(window.stop, 0); + }, 30000); + } + } + + this.release = function() { + if (timer) { + clearInterval(timer); + timer = null; + } + } +} + + +function getWakeLock() { + var userAgent = navigator.userAgent || navigator.vendor || window.opera; + if (userAgent.match(/iPhone/i) || userAgent.match(/iPod/i)) { + return iOSWakeLock; + } else { + return AndroidWakeLock; + } +} + +module.exports = getWakeLock(); + +},{"./util.js":13}],16:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var ButtonManager = _dereq_('./button-manager.js'); +var CardboardDistorter = _dereq_('./cardboard-distorter.js'); +var DeviceInfo = _dereq_('./device-info.js'); +var Dpdb = _dereq_('./dpdb.js'); +var Emitter = _dereq_('./emitter.js'); +var Modes = _dereq_('./modes.js'); +var RotateInstructions = _dereq_('./rotate-instructions.js'); +var Util = _dereq_('./util.js'); +var ViewerSelector = _dereq_('./viewer-selector.js'); +var Wakelock = _dereq_('./wakelock.js'); + +/** + * Helper for getting in and out of VR mode. + * Here we assume VR mode == full screen mode. + * + * 1. Detects whether or not VR mode is possible by feature detecting for + * WebVR (or polyfill). + * + * 2. If WebVR is available, shows a button that lets you enter VR mode. + * + * 3. Provides Cardboard-style distortion if the webvr-polyfill is being used. + * + * 4. Provides best practices while in VR mode. + * - Full screen + * - Wake lock + * - Orientation lock (mobile only) + */ +function WebVRManager(renderer, effect, params) { + this.params = params || {}; + + this.mode = Modes.UNKNOWN; + + // Set option to hide the button. + this.hideButton = this.params.hideButton || false; + // Whether or not the FOV should be distorted or un-distorted. By default, it + // should be distorted, but in the case of vertex shader based distortion, + // ensure that we use undistorted parameters. + this.isUndistorted = !!this.params.isUndistorted; + + // Save the THREE.js renderer and effect for later. + this.renderer = renderer; + this.effect = effect; + this.button = new ButtonManager(); + this.rotateInstructions = new RotateInstructions(); + this.viewerSelector = new ViewerSelector(DeviceInfo.Viewers); + + // Load the DPDB. + var shouldFetch = !WebVRConfig.NO_DPDB_FETCH; + this.dpdb = new Dpdb(shouldFetch, this.onDeviceParamsUpdated_.bind(this)); + + // Create device info and set the correct default viewer. + this.deviceInfo = new DeviceInfo(this.dpdb.getDeviceParams()); + this.deviceInfo.viewer = DeviceInfo.Viewers[this.viewerSelector.selectedKey]; + console.log('Using the %s viewer.', this.getViewer().label); + + this.distorter = new CardboardDistorter(renderer); + this.distorter.updateDeviceInfo(this.deviceInfo); + + this.isVRCompatible = false; + this.isFullscreenDisabled = !!Util.getQueryParameter('no_fullscreen'); + this.startMode = Modes.NORMAL; + var startModeParam = parseInt(Util.getQueryParameter('start_mode')); + if (!isNaN(startModeParam)) { + this.startMode = startModeParam; + } + + // Set the correct viewer profile, but only if this is Cardboard. + if (Util.isMobile()) { + this.onViewerChanged_(this.getViewer()); + } + // Listen for changes to the viewer. + this.viewerSelector.on('change', this.onViewerChanged_.bind(this)); + + if (this.hideButton) { + this.button.setVisibility(false); + } + + // Check if the browser is compatible with WebVR. + this.getDeviceByType_(HMDVRDevice).then(function(hmd) { + // Activate either VR or Immersive mode. + if (WebVRConfig.FORCE_DISTORTION) { + this.distorter.setActive(true); + this.isVRCompatible = true; + } else if (hmd) { + this.isVRCompatible = true; + // Only enable distortion if we are dealing using the polyfill, we have a + // perfect device match, and it's not prevented via configuration. + if (hmd.deviceName.indexOf('webvr-polyfill') == 0 && this.deviceInfo.getDevice() && + !WebVRConfig.PREVENT_DISTORTION) { + this.distorter.setActive(true); + } + this.hmd = hmd; + } + // Set the right mode. + switch (this.startMode) { + case Modes.MAGIC_WINDOW: + this.normalToMagicWindow_(); + this.setMode_(Modes.MAGIC_WINDOW); + break; + case Modes.VR: + this.anyModeToVR_(); + this.setMode_(Modes.VR); + break; + default: + this.setMode_(Modes.NORMAL); + } + this.button.on('fs', this.onFSClick_.bind(this)); + this.button.on('vr', this.onVRClick_.bind(this)); + this.button.on('back', this.onBackClick_.bind(this)); + this.button.on('settings', this.onSettingsClick_.bind(this)); + this.emit('initialized'); + }.bind(this)); + + // Save the input device for later sending timing data. + this.getDeviceByType_(PositionSensorVRDevice).then(function(input) { + this.input = input; + }.bind(this)); + + // Whenever we enter fullscreen, we are entering VR or immersive mode. + document.addEventListener('webkitfullscreenchange', + this.onFullscreenChange_.bind(this)); + document.addEventListener('mozfullscreenchange', + this.onFullscreenChange_.bind(this)); + document.addEventListener('MSFullscreenChange', + this.onFullscreenChange_.bind(this)); + window.addEventListener('orientationchange', + this.onOrientationChange_.bind(this)); + + // Create the necessary elements for wake lock to work. + this.wakelock = new Wakelock(); + + // Save whether or not we want the touch panner to be enabled or disabled by + // default. + this.isTouchPannerEnabled = !WebVRConfig.TOUCH_PANNER_DISABLED; + +} + +WebVRManager.prototype = new Emitter(); + +// Expose these values externally. +WebVRManager.Modes = Modes; + +/** + * Promise returns true if there is at least one HMD device available. + */ +WebVRManager.prototype.getDeviceByType_ = function(type) { + return new Promise(function(resolve, reject) { + navigator.getVRDevices().then(function(devices) { + // Promise succeeds, but check if there are any devices actually. + for (var i = 0; i < devices.length; i++) { + if (devices[i] instanceof type) { + resolve(devices[i]); + break; + } + } + resolve(null); + }, function() { + // No devices are found. + resolve(null); + }); + }); +}; + +WebVRManager.prototype.isVRMode = function() { + return this.mode == Modes.VR; +}; + +WebVRManager.prototype.getViewer = function() { + return this.deviceInfo.viewer; +}; + +WebVRManager.prototype.getDevice = function() { + return this.deviceInfo.device; +}; + +WebVRManager.prototype.getDeviceInfo = function() { + return this.deviceInfo; +}; + +WebVRManager.prototype.render = function(scene, camera, timestamp) { + this.camera = camera; + + this.resizeIfNeeded_(camera); + + if (this.isVRMode()) { + this.distorter.preRender(); + this.effect.render(scene, camera); + this.distorter.postRender(); + } else { + // Scene may be an array of two scenes, one for each eye. + if (scene instanceof Array) { + this.renderer.render(scene[0], camera); + } else { + this.renderer.render(scene, camera); + } + } +}; + + +WebVRManager.prototype.setMode_ = function(mode) { + var oldMode = this.mode; + if (mode == this.mode) { + console.error('Not changing modes, already in %s', mode); + return; + } + console.log('Mode change: %s => %s', this.mode, mode); + this.mode = mode; + this.button.setMode(mode, this.isVRCompatible); + + if (this.mode == Modes.VR && Util.isLandscapeMode() && Util.isMobile()) { + // In landscape mode, temporarily show the "put into Cardboard" + // interstitial. Otherwise, do the default thing. + this.rotateInstructions.showTemporarily(3000); + } else { + this.updateRotateInstructions_(); + } + + // Also hide the viewer selector. + this.viewerSelector.hide(); + + // Emit an event indicating the mode changed. + this.emit('modechange', mode, oldMode); + + // Note: This is a nasty hack since we need to communicate to the polyfill + // that touch panning is disabled, and the only way to do this currently is + // via WebVRConfig. + // TODO: Maybe move touch panning to the boilerplate to eliminate the hack. + // + // If we are in VR mode, always disable touch panning. + if (this.isTouchPannerEnabled) { + if (this.mode == Modes.VR) { + WebVRConfig.TOUCH_PANNER_DISABLED = true; + } else { + WebVRConfig.TOUCH_PANNER_DISABLED = false; + } + } + + if (this.mode == Modes.VR) { + // In VR mode, set the HMDVRDevice parameters. + this.setHMDVRDeviceParams_(this.getViewer()); + } +}; + +/** + * Main button was clicked. + */ +WebVRManager.prototype.onFSClick_ = function() { + switch (this.mode) { + case Modes.NORMAL: + // TODO: Remove this hack when iOS has fullscreen mode. + // If this is an iframe on iOS, break out and open in no_fullscreen mode. + if (Util.isIOS() && Util.isIFrame()) { + var url = window.location.href; + url = Util.appendQueryParameter(url, 'no_fullscreen', 'true'); + url = Util.appendQueryParameter(url, 'start_mode', Modes.MAGIC_WINDOW); + top.location.href = url; + return; + } + this.normalToMagicWindow_(); + this.setMode_(Modes.MAGIC_WINDOW); + break; + case Modes.MAGIC_WINDOW: + if (this.isFullscreenDisabled) { + window.history.back(); + } else { + this.anyModeToNormal_(); + this.setMode_(Modes.NORMAL); + } + break; + } +}; + +/** + * The VR button was clicked. + */ +WebVRManager.prototype.onVRClick_ = function() { + // TODO: Remove this hack when iOS has fullscreen mode. + // If this is an iframe on iOS, break out and open in no_fullscreen mode. + if (this.mode == Modes.NORMAL && Util.isIOS() && Util.isIFrame()) { + var url = window.location.href; + url = Util.appendQueryParameter(url, 'no_fullscreen', 'true'); + url = Util.appendQueryParameter(url, 'start_mode', Modes.VR); + top.location.href = url; + return; + } + this.anyModeToVR_(); + this.setMode_(Modes.VR); +}; + +/** + * Back button was clicked. + */ +WebVRManager.prototype.onBackClick_ = function() { + if (this.isFullscreenDisabled) { + window.history.back(); + } else { + this.anyModeToNormal_(); + this.setMode_(Modes.NORMAL); + } +}; + +WebVRManager.prototype.onSettingsClick_ = function() { + // Show the viewer selection dialog. + this.viewerSelector.show(); +}; + +/** + * + * Methods to go between modes. + * + */ +WebVRManager.prototype.normalToMagicWindow_ = function() { + // TODO: Re-enable pointer lock after debugging. + //this.requestPointerLock_(); + this.requestFullscreen_(); + this.wakelock.request(); +}; + +WebVRManager.prototype.anyModeToVR_ = function() { + // Don't do orientation locking for consistency. + //this.requestOrientationLock_(); + this.requestFullscreen_(); + //this.effect.setFullScreen(true); + this.wakelock.request(); + this.distorter.patch(); +}; + +WebVRManager.prototype.vrToMagicWindow_ = function() { + //this.releaseOrientationLock_(); + this.distorter.unpatch(); + + // Android bug: when returning from VR, resize the effect. + this.resize_(); +} + +WebVRManager.prototype.anyModeToNormal_ = function() { + //this.effect.setFullScreen(false); + this.exitFullscreen_(); + //this.releaseOrientationLock_(); + this.releasePointerLock_(); + this.wakelock.release(); + this.distorter.unpatch(); + + // Android bug: when returning from VR, resize the effect. + this.resize_(); +}; + +WebVRManager.prototype.resizeIfNeeded_ = function(camera) { + // Only resize the canvas if it needs to be resized. + var size = this.renderer.getSize(); + if (size.width != window.innerWidth || size.height != window.innerHeight) { + this.resize_(); + } +}; + +WebVRManager.prototype.resize_ = function() { + this.effect.setSize(window.innerWidth, window.innerHeight); + if (this.camera) { + this.camera.aspect = window.innerWidth / window.innerHeight; + this.camera.updateProjectionMatrix(); + } +}; + +WebVRManager.prototype.onOrientationChange_ = function(e) { + this.updateRotateInstructions_(); + // Also hide the viewer selector. + this.viewerSelector.hide(); +}; + +WebVRManager.prototype.updateRotateInstructions_ = function() { + this.rotateInstructions.disableShowTemporarily(); + // In portrait VR mode, tell the user to rotate to landscape. + if (this.mode == Modes.VR && !Util.isLandscapeMode() && Util.isMobile()) { + this.rotateInstructions.show(); + } else { + this.rotateInstructions.hide(); + } +}; + +WebVRManager.prototype.onFullscreenChange_ = function(e) { + // If we leave full-screen, go back to normal mode. + if (document.webkitFullscreenElement === null || + document.mozFullScreenElement === null) { + this.anyModeToNormal_(); + this.setMode_(Modes.NORMAL); + } +}; + +WebVRManager.prototype.requestPointerLock_ = function() { + var canvas = this.renderer.domElement; + canvas.requestPointerLock = canvas.requestPointerLock || + canvas.mozRequestPointerLock || + canvas.webkitRequestPointerLock; + + if (canvas.requestPointerLock) { + canvas.requestPointerLock(); + } +}; + +WebVRManager.prototype.releasePointerLock_ = function() { + document.exitPointerLock = document.exitPointerLock || + document.mozExitPointerLock || + document.webkitExitPointerLock; + + if (document.exitPointerLock) { + document.exitPointerLock(); + } +}; + +WebVRManager.prototype.requestOrientationLock_ = function() { + if (screen.orientation && Util.isMobile()) { + screen.orientation.lock('landscape'); + } +}; + +WebVRManager.prototype.releaseOrientationLock_ = function() { + if (screen.orientation) { + screen.orientation.unlock(); + } +}; + +WebVRManager.prototype.requestFullscreen_ = function() { + var canvas = document.body; + //var canvas = this.renderer.domElement; + if (canvas.requestFullscreen) { + canvas.requestFullscreen(); + } else if (canvas.mozRequestFullScreen) { + canvas.mozRequestFullScreen({vrDisplay: this.hmd}); + } else if (canvas.webkitRequestFullscreen) { + canvas.webkitRequestFullscreen({vrDisplay: this.hmd}); + } else if (canvas.msRequestFullscreen) { + canvas.msRequestFullscreen({vrDisplay: this.hmd}); + } +}; + +WebVRManager.prototype.exitFullscreen_ = function() { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } +}; + +WebVRManager.prototype.onViewerChanged_ = function(viewer) { + this.deviceInfo.setViewer(viewer); + + // Update the distortion appropriately. + this.distorter.updateDeviceInfo(this.deviceInfo); + + // And update the HMDVRDevice parameters. + this.setHMDVRDeviceParams_(viewer); + + // Notify anyone interested in this event. + this.emit('viewerchange', viewer); +}; + +/** + * Sets parameters on CardboardHMDVRDevice. These changes are ultimately handled + * by VREffect. + */ +WebVRManager.prototype.setHMDVRDeviceParams_ = function(viewer) { + this.getDeviceByType_(HMDVRDevice).then(function(hmd) { + if (!hmd) { + return; + } + + // If we can set fields of view, do that now. + if (hmd.setFieldOfView) { + // Calculate the optimal field of view for each eye. + hmd.setFieldOfView(this.deviceInfo.getFieldOfViewLeftEye(this.isUndistorted), + this.deviceInfo.getFieldOfViewRightEye(this.isUndistorted)); + } + + // Note: setInterpupillaryDistance is not part of the WebVR standard. + if (hmd.setInterpupillaryDistance) { + hmd.setInterpupillaryDistance(viewer.interLensDistance); + } + }.bind(this)); +}; + +WebVRManager.prototype.onDeviceParamsUpdated_ = function(newParams) { + console.log('DPDB reported that device params were updated.'); + this.deviceInfo.updateDeviceParams(newParams); + this.distorter.updateDeviceInfo(this.deviceInfo); +}; + +module.exports = WebVRManager; + +},{"./button-manager.js":2,"./cardboard-distorter.js":3,"./device-info.js":4,"./dpdb.js":8,"./emitter.js":9,"./modes.js":11,"./rotate-instructions.js":12,"./util.js":13,"./viewer-selector.js":14,"./wakelock.js":15}]},{},[10]); + +},{}],7:[function(_dereq_,module,exports){ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof _dereq_=="function"&&_dereq_;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof _dereq_=="function"&&_dereq_;for(var o=0;o Util.MAX_TIMESTEP) { + console.warn('Invalid timestamps detected. Time step between successive ' + + 'gyroscope sensor samples is very small or not monotonic'); + this.previousTimestampS = timestampS; + return; + } + this.accelerometer.set(-accGravity.x, -accGravity.y, -accGravity.z); + this.gyroscope.set(rotRate.alpha, rotRate.beta, rotRate.gamma); + + // With iOS and Firefox Android, rotationRate is reported in degrees, + // so we first convert to radians. + if (this.isIOS || this.isFirefoxAndroid) { + this.gyroscope.multiplyScalar(Math.PI / 180); + } + + this.filter.addAccelMeasurement(this.accelerometer, timestampS); + this.filter.addGyroMeasurement(this.gyroscope, timestampS); + + this.previousTimestampS = timestampS; +}; + +FusionPositionSensorVRDevice.prototype.onScreenOrientationChange_ = + function(screenOrientation) { + this.setScreenTransform_(); +}; + +FusionPositionSensorVRDevice.prototype.setScreenTransform_ = function() { + this.worldToScreenQ.set(0, 0, 0, 1); + switch (window.orientation) { + case 0: + break; + case 90: + this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0, 0, 1), -Math.PI/2); + break; + case -90: + this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI/2); + break; + case 180: + // TODO. + break; + } +}; + + +module.exports = FusionPositionSensorVRDevice; + +},{"./base.js":1,"./complementary-filter.js":3,"./pose-predictor.js":7,"./three-math.js":9,"./touch-panner.js":10,"./util.js":11}],5:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var WebVRPolyfill = _dereq_('./webvr-polyfill.js'); + +// Initialize a WebVRConfig just in case. +window.WebVRConfig = window.WebVRConfig || {}; +new WebVRPolyfill(); + +},{"./webvr-polyfill.js":12}],6:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var PositionSensorVRDevice = _dereq_('./base.js').PositionSensorVRDevice; +var THREE = _dereq_('./three-math.js'); +var Util = _dereq_('./util.js'); + +// How much to rotate per key stroke. +var KEY_SPEED = 0.15; +var KEY_ANIMATION_DURATION = 80; + +// How much to rotate for mouse events. +var MOUSE_SPEED_X = 0.5; +var MOUSE_SPEED_Y = 0.3; + +/** + * A virtual position sensor, implemented using keyboard and + * mouse APIs. This is designed as for desktops/laptops where no Device* + * events work. + */ +function MouseKeyboardPositionSensorVRDevice() { + this.deviceId = 'webvr-polyfill:mouse-keyboard'; + this.deviceName = 'VR Position Device (webvr-polyfill:mouse-keyboard)'; + + // Attach to mouse and keyboard events. + window.addEventListener('keydown', this.onKeyDown_.bind(this)); + window.addEventListener('mousemove', this.onMouseMove_.bind(this)); + window.addEventListener('mousedown', this.onMouseDown_.bind(this)); + window.addEventListener('mouseup', this.onMouseUp_.bind(this)); + + this.phi = 0; + this.theta = 0; + + // Variables for keyboard-based rotation animation. + this.targetAngle = null; + + // State variables for calculations. + this.euler = new THREE.Euler(); + this.orientation = new THREE.Quaternion(); + + // Variables for mouse-based rotation. + this.rotateStart = new THREE.Vector2(); + this.rotateEnd = new THREE.Vector2(); + this.rotateDelta = new THREE.Vector2(); +} +MouseKeyboardPositionSensorVRDevice.prototype = new PositionSensorVRDevice(); + +/** + * Returns {orientation: {x,y,z,w}, position: null}. + * Position is not supported for parity with other PositionSensors. + */ +MouseKeyboardPositionSensorVRDevice.prototype.getState = function() { + this.euler.set(this.phi, this.theta, 0, 'YXZ'); + this.orientation.setFromEuler(this.euler); + + return { + hasOrientation: true, + orientation: this.orientation, + hasPosition: false, + position: null + } +}; + +MouseKeyboardPositionSensorVRDevice.prototype.onKeyDown_ = function(e) { + // Track WASD and arrow keys. + if (e.keyCode == 38) { // Up key. + this.animatePhi_(this.phi + KEY_SPEED); + } else if (e.keyCode == 39) { // Right key. + this.animateTheta_(this.theta - KEY_SPEED); + } else if (e.keyCode == 40) { // Down key. + this.animatePhi_(this.phi - KEY_SPEED); + } else if (e.keyCode == 37) { // Left key. + this.animateTheta_(this.theta + KEY_SPEED); + } +}; + +MouseKeyboardPositionSensorVRDevice.prototype.animateTheta_ = function(targetAngle) { + this.animateKeyTransitions_('theta', targetAngle); +}; + +MouseKeyboardPositionSensorVRDevice.prototype.animatePhi_ = function(targetAngle) { + // Prevent looking too far up or down. + targetAngle = Util.clamp(targetAngle, -Math.PI/2, Math.PI/2); + this.animateKeyTransitions_('phi', targetAngle); +}; + +/** + * Start an animation to transition an angle from one value to another. + */ +MouseKeyboardPositionSensorVRDevice.prototype.animateKeyTransitions_ = function(angleName, targetAngle) { + // If an animation is currently running, cancel it. + if (this.angleAnimation) { + clearInterval(this.angleAnimation); + } + var startAngle = this[angleName]; + var startTime = new Date(); + // Set up an interval timer to perform the animation. + this.angleAnimation = setInterval(function() { + // Once we're finished the animation, we're done. + var elapsed = new Date() - startTime; + if (elapsed >= KEY_ANIMATION_DURATION) { + this[angleName] = targetAngle; + clearInterval(this.angleAnimation); + return; + } + // Linearly interpolate the angle some amount. + var percent = elapsed / KEY_ANIMATION_DURATION; + this[angleName] = startAngle + (targetAngle - startAngle) * percent; + }.bind(this), 1000/60); +}; + +MouseKeyboardPositionSensorVRDevice.prototype.onMouseDown_ = function(e) { + this.rotateStart.set(e.clientX, e.clientY); + this.isDragging = true; +}; + +// Very similar to https://gist.github.com/mrflix/8351020 +MouseKeyboardPositionSensorVRDevice.prototype.onMouseMove_ = function(e) { + if (!this.isDragging && !this.isPointerLocked_()) { + return; + } + // Support pointer lock API. + if (this.isPointerLocked_()) { + var movementX = e.movementX || e.mozMovementX || 0; + var movementY = e.movementY || e.mozMovementY || 0; + this.rotateEnd.set(this.rotateStart.x - movementX, this.rotateStart.y - movementY); + } else { + this.rotateEnd.set(e.clientX, e.clientY); + } + // Calculate how much we moved in mouse space. + this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart); + this.rotateStart.copy(this.rotateEnd); + + // Keep track of the cumulative euler angles. + var element = document.body; + this.phi += 2 * Math.PI * this.rotateDelta.y / element.clientHeight * MOUSE_SPEED_Y; + this.theta += 2 * Math.PI * this.rotateDelta.x / element.clientWidth * MOUSE_SPEED_X; + + // Prevent looking too far up or down. + this.phi = Util.clamp(this.phi, -Math.PI/2, Math.PI/2); +}; + +MouseKeyboardPositionSensorVRDevice.prototype.onMouseUp_ = function(e) { + this.isDragging = false; +}; + +MouseKeyboardPositionSensorVRDevice.prototype.isPointerLocked_ = function() { + var el = document.pointerLockElement || document.mozPointerLockElement || + document.webkitPointerLockElement; + return el !== undefined; +}; + +MouseKeyboardPositionSensorVRDevice.prototype.resetSensor = function() { + console.error('Not implemented yet.'); +}; + +module.exports = MouseKeyboardPositionSensorVRDevice; + +},{"./base.js":1,"./three-math.js":9,"./util.js":11}],7:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var THREE = _dereq_('./three-math.js'); + +var DEBUG = false; + +/** + * Given an orientation and the gyroscope data, predicts the future orientation + * of the head. This makes rendering appear faster. + * + * Also see: http://msl.cs.uiuc.edu/~lavalle/papers/LavYerKatAnt14.pdf + * + * @param {Number} predictionTimeS time from head movement to the appearance of + * the corresponding image. + */ +function PosePredictor(predictionTimeS) { + this.predictionTimeS = predictionTimeS; + + // The quaternion corresponding to the previous state. + this.previousQ = new THREE.Quaternion(); + // Previous time a prediction occurred. + this.previousTimestampS = null; + + // The delta quaternion that adjusts the current pose. + this.deltaQ = new THREE.Quaternion(); + // The output quaternion. + this.outQ = new THREE.Quaternion(); +} + +PosePredictor.prototype.getPrediction = function(currentQ, gyro, timestampS) { + if (!this.previousTimestampS) { + this.previousQ.copy(currentQ); + this.previousTimestampS = timestampS; + return currentQ; + } + + // Calculate axis and angle based on gyroscope rotation rate data. + var axis = new THREE.Vector3(); + axis.copy(gyro); + axis.normalize(); + + var angularSpeed = gyro.length(); + + // If we're rotating slowly, don't do prediction. + if (angularSpeed < THREE.Math.degToRad(20)) { + if (DEBUG) { + console.log('Moving slowly, at %s deg/s: no prediction', + THREE.Math.radToDeg(angularSpeed).toFixed(1)); + } + this.outQ.copy(currentQ); + this.previousQ.copy(currentQ); + return this.outQ; + } + + // Get the predicted angle based on the time delta and latency. + var deltaT = timestampS - this.previousTimestampS; + var predictAngle = angularSpeed * this.predictionTimeS; + + this.deltaQ.setFromAxisAngle(axis, predictAngle); + this.outQ.copy(this.previousQ); + this.outQ.multiply(this.deltaQ); + + this.previousQ.copy(currentQ); + + return this.outQ; +}; + + +module.exports = PosePredictor; + +},{"./three-math.js":9}],8:[function(_dereq_,module,exports){ +function SensorSample(sample, timestampS) { + this.set(sample, timestampS); +}; + +SensorSample.prototype.set = function(sample, timestampS) { + this.sample = sample; + this.timestampS = timestampS; +}; + +SensorSample.prototype.copy = function(sensorSample) { + this.set(sensorSample.sample, sensorSample.timestampS); +}; + +module.exports = SensorSample; + +},{}],9:[function(_dereq_,module,exports){ +/* + * A subset of THREE.js, providing mostly quaternion and euler-related + * operations, manually lifted from + * https://github.com/mrdoob/three.js/tree/master/src/math, as of 9c30286b38df039fca389989ff06ea1c15d6bad1 + */ + +// Only use if the real THREE is not provided. +var THREE = window.THREE || {}; + +// If some piece of THREE is missing, fill it in here. +if (!THREE.Quaternion || !THREE.Vector3 || !THREE.Vector2 || !THREE.Euler || !THREE.Math) { +console.log('No THREE.js found.'); + + +/*** START Quaternion ***/ + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://exocortex.com + */ + +THREE.Quaternion = function ( x, y, z, w ) { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._w = ( w !== undefined ) ? w : 1; + +}; + +THREE.Quaternion.prototype = { + + constructor: THREE.Quaternion, + + _x: 0,_y: 0, _z: 0, _w: 0, + + get x () { + + return this._x; + + }, + + set x ( value ) { + + this._x = value; + this.onChangeCallback(); + + }, + + get y () { + + return this._y; + + }, + + set y ( value ) { + + this._y = value; + this.onChangeCallback(); + + }, + + get z () { + + return this._z; + + }, + + set z ( value ) { + + this._z = value; + this.onChangeCallback(); + + }, + + get w () { + + return this._w; + + }, + + set w ( value ) { + + this._w = value; + this.onChangeCallback(); + + }, + + set: function ( x, y, z, w ) { + + this._x = x; + this._y = y; + this._z = z; + this._w = w; + + this.onChangeCallback(); + + return this; + + }, + + copy: function ( quaternion ) { + + this._x = quaternion.x; + this._y = quaternion.y; + this._z = quaternion.z; + this._w = quaternion.w; + + this.onChangeCallback(); + + return this; + + }, + + setFromEuler: function ( euler, update ) { + + if ( euler instanceof THREE.Euler === false ) { + + throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + } + + // http://www.mathworks.com/matlabcentral/fileexchange/ + // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ + // content/SpinCalc.m + + var c1 = Math.cos( euler._x / 2 ); + var c2 = Math.cos( euler._y / 2 ); + var c3 = Math.cos( euler._z / 2 ); + var s1 = Math.sin( euler._x / 2 ); + var s2 = Math.sin( euler._y / 2 ); + var s3 = Math.sin( euler._z / 2 ); + + if ( euler.order === 'XYZ' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( euler.order === 'YXZ' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } else if ( euler.order === 'ZXY' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( euler.order === 'ZYX' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } else if ( euler.order === 'YZX' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( euler.order === 'XZY' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } + + if ( update !== false ) this.onChangeCallback(); + + return this; + + }, + + setFromAxisAngle: function ( axis, angle ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm + + // assumes axis is normalized + + var halfAngle = angle / 2, s = Math.sin( halfAngle ); + + this._x = axis.x * s; + this._y = axis.y * s; + this._z = axis.z * s; + this._w = Math.cos( halfAngle ); + + this.onChangeCallback(); + + return this; + + }, + + setFromRotationMatrix: function ( m ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements, + + m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], + m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], + m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], + + trace = m11 + m22 + m33, + s; + + if ( trace > 0 ) { + + s = 0.5 / Math.sqrt( trace + 1.0 ); + + this._w = 0.25 / s; + this._x = ( m32 - m23 ) * s; + this._y = ( m13 - m31 ) * s; + this._z = ( m21 - m12 ) * s; + + } else if ( m11 > m22 && m11 > m33 ) { + + s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); + + this._w = ( m32 - m23 ) / s; + this._x = 0.25 * s; + this._y = ( m12 + m21 ) / s; + this._z = ( m13 + m31 ) / s; + + } else if ( m22 > m33 ) { + + s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); + + this._w = ( m13 - m31 ) / s; + this._x = ( m12 + m21 ) / s; + this._y = 0.25 * s; + this._z = ( m23 + m32 ) / s; + + } else { + + s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); + + this._w = ( m21 - m12 ) / s; + this._x = ( m13 + m31 ) / s; + this._y = ( m23 + m32 ) / s; + this._z = 0.25 * s; + + } + + this.onChangeCallback(); + + return this; + + }, + + setFromUnitVectors: function () { + + // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final + + // assumes direction vectors vFrom and vTo are normalized + + var v1, r; + + var EPS = 0.000001; + + return function ( vFrom, vTo ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + r = vFrom.dot( vTo ) + 1; + + if ( r < EPS ) { + + r = 0; + + if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { + + v1.set( - vFrom.y, vFrom.x, 0 ); + + } else { + + v1.set( 0, - vFrom.z, vFrom.y ); + + } + + } else { + + v1.crossVectors( vFrom, vTo ); + + } + + this._x = v1.x; + this._y = v1.y; + this._z = v1.z; + this._w = r; + + this.normalize(); + + return this; + + } + + }(), + + inverse: function () { + + this.conjugate().normalize(); + + return this; + + }, + + conjugate: function () { + + this._x *= - 1; + this._y *= - 1; + this._z *= - 1; + + this.onChangeCallback(); + + return this; + + }, + + dot: function ( v ) { + + return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; + + }, + + lengthSq: function () { + + return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; + + }, + + length: function () { + + return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); + + }, + + normalize: function () { + + var l = this.length(); + + if ( l === 0 ) { + + this._x = 0; + this._y = 0; + this._z = 0; + this._w = 1; + + } else { + + l = 1 / l; + + this._x = this._x * l; + this._y = this._y * l; + this._z = this._z * l; + this._w = this._w * l; + + } + + this.onChangeCallback(); + + return this; + + }, + + multiply: function ( q, p ) { + + if ( p !== undefined ) { + + console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); + return this.multiplyQuaternions( q, p ); + + } + + return this.multiplyQuaternions( this, q ); + + }, + + multiplyQuaternions: function ( a, b ) { + + // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm + + var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; + var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; + + this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; + this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; + this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; + this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; + + this.onChangeCallback(); + + return this; + + }, + + multiplyVector3: function ( vector ) { + + console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); + return vector.applyQuaternion( this ); + + }, + + slerp: function ( qb, t ) { + + if ( t === 0 ) return this; + if ( t === 1 ) return this.copy( qb ); + + var x = this._x, y = this._y, z = this._z, w = this._w; + + // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + + var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; + + if ( cosHalfTheta < 0 ) { + + this._w = - qb._w; + this._x = - qb._x; + this._y = - qb._y; + this._z = - qb._z; + + cosHalfTheta = - cosHalfTheta; + + } else { + + this.copy( qb ); + + } + + if ( cosHalfTheta >= 1.0 ) { + + this._w = w; + this._x = x; + this._y = y; + this._z = z; + + return this; + + } + + var halfTheta = Math.acos( cosHalfTheta ); + var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); + + if ( Math.abs( sinHalfTheta ) < 0.001 ) { + + this._w = 0.5 * ( w + this._w ); + this._x = 0.5 * ( x + this._x ); + this._y = 0.5 * ( y + this._y ); + this._z = 0.5 * ( z + this._z ); + + return this; + + } + + var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, + ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; + + this._w = ( w * ratioA + this._w * ratioB ); + this._x = ( x * ratioA + this._x * ratioB ); + this._y = ( y * ratioA + this._y * ratioB ); + this._z = ( z * ratioA + this._z * ratioB ); + + this.onChangeCallback(); + + return this; + + }, + + equals: function ( quaternion ) { + + return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this._x = array[ offset ]; + this._y = array[ offset + 1 ]; + this._z = array[ offset + 2 ]; + this._w = array[ offset + 3 ]; + + this.onChangeCallback(); + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this._x; + array[ offset + 1 ] = this._y; + array[ offset + 2 ] = this._z; + array[ offset + 3 ] = this._w; + + return array; + + }, + + onChange: function ( callback ) { + + this.onChangeCallback = callback; + + return this; + + }, + + onChangeCallback: function () {}, + + clone: function () { + + return new THREE.Quaternion( this._x, this._y, this._z, this._w ); + + } + +}; + +THREE.Quaternion.slerp = function ( qa, qb, qm, t ) { + + return qm.copy( qa ).slerp( qb, t ); + +} + +/*** END Quaternion ***/ +/*** START Vector2 ***/ +/** + * @author mrdoob / http://mrdoob.com/ + * @author philogb / http://blog.thejit.org/ + * @author egraether / http://egraether.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.Vector2 = function ( x, y ) { + + this.x = x || 0; + this.y = y || 0; + +}; + +THREE.Vector2.prototype = { + + constructor: THREE.Vector2, + + set: function ( x, y ) { + + this.x = x; + this.y = y; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + + return this; + + }, + + multiply: function ( v ) { + + this.x *= v.x; + this.y *= v.y; + + return this; + + }, + + multiplyScalar: function ( s ) { + + this.x *= s; + this.y *= s; + + return this; + + }, + + divide: function ( v ) { + + this.x /= v.x; + this.y /= v.y; + + return this; + + }, + + divideScalar: function ( scalar ) { + + if ( scalar !== 0 ) { + + var invScalar = 1 / scalar; + + this.x *= invScalar; + this.y *= invScalar; + + } else { + + this.x = 0; + this.y = 0; + + } + + return this; + + }, + + min: function ( v ) { + + if ( this.x > v.x ) { + + this.x = v.x; + + } + + if ( this.y > v.y ) { + + this.y = v.y; + + } + + return this; + + }, + + max: function ( v ) { + + if ( this.x < v.x ) { + + this.x = v.x; + + } + + if ( this.y < v.y ) { + + this.y = v.y; + + } + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + if ( this.x < min.x ) { + + this.x = min.x; + + } else if ( this.x > max.x ) { + + this.x = max.x; + + } + + if ( this.y < min.y ) { + + this.y = min.y; + + } else if ( this.y > max.y ) { + + this.y = max.y; + + } + + return this; + }, + + clampScalar: ( function () { + + var min, max; + + return function ( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector2(); + max = new THREE.Vector2(); + + } + + min.set( minVal, minVal ); + max.set( maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + } )(), + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + distanceTo: function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared: function ( v ) { + + var dx = this.x - v.x, dy = this.y - v.y; + return dx * dx + dy * dy; + + }, + + setLength: function ( l ) { + + var oldLength = this.length(); + + if ( oldLength !== 0 && l !== oldLength ) { + + this.multiplyScalar( l / oldLength ); + } + + return this; + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + + return array; + + }, + + fromAttribute: function ( attribute, index, offset ) { + + if ( offset === undefined ) offset = 0; + + index = index * attribute.itemSize + offset; + + this.x = attribute.array[ index ]; + this.y = attribute.array[ index + 1 ]; + + return this; + + }, + + clone: function () { + + return new THREE.Vector2( this.x, this.y ); + + } + +}; +/*** END Vector2 ***/ +/*** START Vector3 ***/ + +/** + * @author mrdoob / http://mrdoob.com/ + * @author *kile / http://kile.stravaganza.org/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Vector3 = function ( x, y, z ) { + + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + +}; + +THREE.Vector3.prototype = { + + constructor: THREE.Vector3, + + set: function ( x, y, z ) { + + this.x = x; + this.y = y; + this.z = z; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setZ: function ( z ) { + + this.z = z; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + case 2: this.z = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + case 2: return this.z; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + this.z = v.z; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + this.z += v.z; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + this.z += s; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + + return this; + + }, + + multiply: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); + return this.multiplyVectors( v, w ); + + } + + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; + + return this; + + }, + + multiplyScalar: function ( scalar ) { + + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + + return this; + + }, + + multiplyVectors: function ( a, b ) { + + this.x = a.x * b.x; + this.y = a.y * b.y; + this.z = a.z * b.z; + + return this; + + }, + + applyEuler: function () { + + var quaternion; + + return function ( euler ) { + + if ( euler instanceof THREE.Euler === false ) { + + console.error( 'THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + + } + + if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); + + this.applyQuaternion( quaternion.setFromEuler( euler ) ); + + return this; + + }; + + }(), + + applyAxisAngle: function () { + + var quaternion; + + return function ( axis, angle ) { + + if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); + + this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); + + return this; + + }; + + }(), + + applyMatrix3: function ( m ) { + + var x = this.x; + var y = this.y; + var z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; + this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; + this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; + + return this; + + }, + + applyMatrix4: function ( m ) { + + // input: THREE.Matrix4 affine matrix + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ]; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ]; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ]; + + return this; + + }, + + applyProjection: function ( m ) { + + // input: THREE.Matrix4 projection matrix + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide + + this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d; + this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d; + this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d; + + return this; + + }, + + applyQuaternion: function ( q ) { + + var x = this.x; + var y = this.y; + var z = this.z; + + var qx = q.x; + var qy = q.y; + var qz = q.z; + var qw = q.w; + + // calculate quat * vector + + var ix = qw * x + qy * z - qz * y; + var iy = qw * y + qz * x - qx * z; + var iz = qw * z + qx * y - qy * x; + var iw = - qx * x - qy * y - qz * z; + + // calculate result * inverse quat + + this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; + this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; + this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; + + return this; + + }, + + project: function () { + + var matrix; + + return function ( camera ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + + matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) ); + return this.applyProjection( matrix ); + + }; + + }(), + + unproject: function () { + + var matrix; + + return function ( camera ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + + matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) ); + return this.applyProjection( matrix ); + + }; + + }(), + + transformDirection: function ( m ) { + + // input: THREE.Matrix4 affine matrix + // vector interpreted as a direction + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; + + this.normalize(); + + return this; + + }, + + divide: function ( v ) { + + this.x /= v.x; + this.y /= v.y; + this.z /= v.z; + + return this; + + }, + + divideScalar: function ( scalar ) { + + if ( scalar !== 0 ) { + + var invScalar = 1 / scalar; + + this.x *= invScalar; + this.y *= invScalar; + this.z *= invScalar; + + } else { + + this.x = 0; + this.y = 0; + this.z = 0; + + } + + return this; + + }, + + min: function ( v ) { + + if ( this.x > v.x ) { + + this.x = v.x; + + } + + if ( this.y > v.y ) { + + this.y = v.y; + + } + + if ( this.z > v.z ) { + + this.z = v.z; + + } + + return this; + + }, + + max: function ( v ) { + + if ( this.x < v.x ) { + + this.x = v.x; + + } + + if ( this.y < v.y ) { + + this.y = v.y; + + } + + if ( this.z < v.z ) { + + this.z = v.z; + + } + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + if ( this.x < min.x ) { + + this.x = min.x; + + } else if ( this.x > max.x ) { + + this.x = max.x; + + } + + if ( this.y < min.y ) { + + this.y = min.y; + + } else if ( this.y > max.y ) { + + this.y = max.y; + + } + + if ( this.z < min.z ) { + + this.z = min.z; + + } else if ( this.z > max.z ) { + + this.z = max.z; + + } + + return this; + + }, + + clampScalar: ( function () { + + var min, max; + + return function ( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector3(); + max = new THREE.Vector3(); + + } + + min.set( minVal, minVal, minVal ); + max.set( maxVal, maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + } )(), + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y + this.z * v.z; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y + this.z * this.z; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); + + }, + + lengthManhattan: function () { + + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + setLength: function ( l ) { + + var oldLength = this.length(); + + if ( oldLength !== 0 && l !== oldLength ) { + + this.multiplyScalar( l / oldLength ); + } + + return this; + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; + + return this; + + }, + + cross: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); + return this.crossVectors( v, w ); + + } + + var x = this.x, y = this.y, z = this.z; + + this.x = y * v.z - z * v.y; + this.y = z * v.x - x * v.z; + this.z = x * v.y - y * v.x; + + return this; + + }, + + crossVectors: function ( a, b ) { + + var ax = a.x, ay = a.y, az = a.z; + var bx = b.x, by = b.y, bz = b.z; + + this.x = ay * bz - az * by; + this.y = az * bx - ax * bz; + this.z = ax * by - ay * bx; + + return this; + + }, + + projectOnVector: function () { + + var v1, dot; + + return function ( vector ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + v1.copy( vector ).normalize(); + + dot = this.dot( v1 ); + + return this.copy( v1 ).multiplyScalar( dot ); + + }; + + }(), + + projectOnPlane: function () { + + var v1; + + return function ( planeNormal ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + v1.copy( this ).projectOnVector( planeNormal ); + + return this.sub( v1 ); + + } + + }(), + + reflect: function () { + + // reflect incident vector off plane orthogonal to normal + // normal is assumed to have unit length + + var v1; + + return function ( normal ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); + + } + + }(), + + angleTo: function ( v ) { + + var theta = this.dot( v ) / ( this.length() * v.length() ); + + // clamp, to handle numerical problems + + return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) ); + + }, + + distanceTo: function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared: function ( v ) { + + var dx = this.x - v.x; + var dy = this.y - v.y; + var dz = this.z - v.z; + + return dx * dx + dy * dy + dz * dz; + + }, + + setEulerFromRotationMatrix: function ( m, order ) { + + console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); + + }, + + setEulerFromQuaternion: function ( q, order ) { + + console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); + + }, + + getPositionFromMatrix: function ( m ) { + + console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); + + return this.setFromMatrixPosition( m ); + + }, + + getScaleFromMatrix: function ( m ) { + + console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); + + return this.setFromMatrixScale( m ); + }, + + getColumnFromMatrix: function ( index, matrix ) { + + console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); + + return this.setFromMatrixColumn( index, matrix ); + + }, + + setFromMatrixPosition: function ( m ) { + + this.x = m.elements[ 12 ]; + this.y = m.elements[ 13 ]; + this.z = m.elements[ 14 ]; + + return this; + + }, + + setFromMatrixScale: function ( m ) { + + var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length(); + var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length(); + var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length(); + + this.x = sx; + this.y = sy; + this.z = sz; + + return this; + }, + + setFromMatrixColumn: function ( index, matrix ) { + + var offset = index * 4; + + var me = matrix.elements; + + this.x = me[ offset ]; + this.y = me[ offset + 1 ]; + this.z = me[ offset + 2 ]; + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; + + return array; + + }, + + fromAttribute: function ( attribute, index, offset ) { + + if ( offset === undefined ) offset = 0; + + index = index * attribute.itemSize + offset; + + this.x = attribute.array[ index ]; + this.y = attribute.array[ index + 1 ]; + this.z = attribute.array[ index + 2 ]; + + return this; + + }, + + clone: function () { + + return new THREE.Vector3( this.x, this.y, this.z ); + + } + +}; +/*** END Vector3 ***/ +/*** START Euler ***/ +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://exocortex.com + */ + +THREE.Euler = function ( x, y, z, order ) { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._order = order || THREE.Euler.DefaultOrder; + +}; + +THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; + +THREE.Euler.DefaultOrder = 'XYZ'; + +THREE.Euler.prototype = { + + constructor: THREE.Euler, + + _x: 0, _y: 0, _z: 0, _order: THREE.Euler.DefaultOrder, + + get x () { + + return this._x; + + }, + + set x ( value ) { + + this._x = value; + this.onChangeCallback(); + + }, + + get y () { + + return this._y; + + }, + + set y ( value ) { + + this._y = value; + this.onChangeCallback(); + + }, + + get z () { + + return this._z; + + }, + + set z ( value ) { + + this._z = value; + this.onChangeCallback(); + + }, + + get order () { + + return this._order; + + }, + + set order ( value ) { + + this._order = value; + this.onChangeCallback(); + + }, + + set: function ( x, y, z, order ) { + + this._x = x; + this._y = y; + this._z = z; + this._order = order || this._order; + + this.onChangeCallback(); + + return this; + + }, + + copy: function ( euler ) { + + this._x = euler._x; + this._y = euler._y; + this._z = euler._z; + this._order = euler._order; + + this.onChangeCallback(); + + return this; + + }, + + setFromRotationMatrix: function ( m, order, update ) { + + var clamp = THREE.Math.clamp; + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements; + var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; + var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; + var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; + + order = order || this._order; + + if ( order === 'XYZ' ) { + + this._y = Math.asin( clamp( m13, - 1, 1 ) ); + + if ( Math.abs( m13 ) < 0.99999 ) { + + this._x = Math.atan2( - m23, m33 ); + this._z = Math.atan2( - m12, m11 ); + + } else { + + this._x = Math.atan2( m32, m22 ); + this._z = 0; + + } + + } else if ( order === 'YXZ' ) { + + this._x = Math.asin( - clamp( m23, - 1, 1 ) ); + + if ( Math.abs( m23 ) < 0.99999 ) { + + this._y = Math.atan2( m13, m33 ); + this._z = Math.atan2( m21, m22 ); + + } else { + + this._y = Math.atan2( - m31, m11 ); + this._z = 0; + + } + + } else if ( order === 'ZXY' ) { + + this._x = Math.asin( clamp( m32, - 1, 1 ) ); + + if ( Math.abs( m32 ) < 0.99999 ) { + + this._y = Math.atan2( - m31, m33 ); + this._z = Math.atan2( - m12, m22 ); + + } else { + + this._y = 0; + this._z = Math.atan2( m21, m11 ); + + } + + } else if ( order === 'ZYX' ) { + + this._y = Math.asin( - clamp( m31, - 1, 1 ) ); + + if ( Math.abs( m31 ) < 0.99999 ) { + + this._x = Math.atan2( m32, m33 ); + this._z = Math.atan2( m21, m11 ); + + } else { + + this._x = 0; + this._z = Math.atan2( - m12, m22 ); + + } + + } else if ( order === 'YZX' ) { + + this._z = Math.asin( clamp( m21, - 1, 1 ) ); + + if ( Math.abs( m21 ) < 0.99999 ) { + + this._x = Math.atan2( - m23, m22 ); + this._y = Math.atan2( - m31, m11 ); + + } else { + + this._x = 0; + this._y = Math.atan2( m13, m33 ); + + } + + } else if ( order === 'XZY' ) { + + this._z = Math.asin( - clamp( m12, - 1, 1 ) ); + + if ( Math.abs( m12 ) < 0.99999 ) { + + this._x = Math.atan2( m32, m22 ); + this._y = Math.atan2( m13, m11 ); + + } else { + + this._x = Math.atan2( - m23, m33 ); + this._y = 0; + + } + + } else { + + console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order ) + + } + + this._order = order; + + if ( update !== false ) this.onChangeCallback(); + + return this; + + }, + + setFromQuaternion: function () { + + var matrix; + + return function ( q, order, update ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + matrix.makeRotationFromQuaternion( q ); + this.setFromRotationMatrix( matrix, order, update ); + + return this; + + }; + + }(), + + setFromVector3: function ( v, order ) { + + return this.set( v.x, v.y, v.z, order || this._order ); + + }, + + reorder: function () { + + // WARNING: this discards revolution information -bhouston + + var q = new THREE.Quaternion(); + + return function ( newOrder ) { + + q.setFromEuler( this ); + this.setFromQuaternion( q, newOrder ); + + }; + + }(), + + equals: function ( euler ) { + + return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); + + }, + + fromArray: function ( array ) { + + this._x = array[ 0 ]; + this._y = array[ 1 ]; + this._z = array[ 2 ]; + if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; + + this.onChangeCallback(); + + return this; + + }, + + toArray: function () { + + return [ this._x, this._y, this._z, this._order ]; + + }, + + toVector3: function ( optionalResult ) { + + if ( optionalResult ) { + + return optionalResult.set( this._x, this._y, this._z ); + + } else { + + return new THREE.Vector3( this._x, this._y, this._z ); + + } + + }, + + onChange: function ( callback ) { + + this.onChangeCallback = callback; + + return this; + + }, + + onChangeCallback: function () {}, + + clone: function () { + + return new THREE.Euler( this._x, this._y, this._z, this._order ); + + } + +}; +/*** END Euler ***/ +/*** START Math ***/ +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Math = { + + generateUUID: function () { + + // http://www.broofa.com/Tools/Math.uuid.htm + + var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' ); + var uuid = new Array( 36 ); + var rnd = 0, r; + + return function () { + + for ( var i = 0; i < 36; i ++ ) { + + if ( i == 8 || i == 13 || i == 18 || i == 23 ) { + + uuid[ i ] = '-'; + + } else if ( i == 14 ) { + + uuid[ i ] = '4'; + + } else { + + if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0; + r = rnd & 0xf; + rnd = rnd >> 4; + uuid[ i ] = chars[ ( i == 19 ) ? ( r & 0x3 ) | 0x8 : r ]; + + } + } + + return uuid.join( '' ); + + }; + + }(), + + // Clamp value to range + + clamp: function ( x, a, b ) { + + return ( x < a ) ? a : ( ( x > b ) ? b : x ); + + }, + + // Clamp value to range to range + + mapLinear: function ( x, a1, a2, b1, b2 ) { + + return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); + + }, + + // http://en.wikipedia.org/wiki/Smoothstep + + smoothstep: function ( x, min, max ) { + + if ( x <= min ) return 0; + if ( x >= max ) return 1; + + x = ( x - min ) / ( max - min ); + + return x * x * ( 3 - 2 * x ); + + }, + + smootherstep: function ( x, min, max ) { + + if ( x <= min ) return 0; + if ( x >= max ) return 1; + + x = ( x - min ) / ( max - min ); + + return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); + + }, + + // Random float from <0, 1> with 16 bits of randomness + // (standard Math.random() creates repetitive patterns when applied over larger space) + + random16: function () { + + return ( 65280 * Math.random() + 255 * Math.random() ) / 65535; + + }, + + // Random integer from interval + + randInt: function ( low, high ) { + + return Math.floor( this.randFloat( low, high ) ); + + }, + + // Random float from interval + + randFloat: function ( low, high ) { + + return low + Math.random() * ( high - low ); + + }, + + // Random float from <-range/2, range/2> interval + + randFloatSpread: function ( range ) { + + return range * ( 0.5 - Math.random() ); + + }, + + degToRad: function () { + + var degreeToRadiansFactor = Math.PI / 180; + + return function ( degrees ) { + + return degrees * degreeToRadiansFactor; + + }; + + }(), + + radToDeg: function () { + + var radianToDegreesFactor = 180 / Math.PI; + + return function ( radians ) { + + return radians * radianToDegreesFactor; + + }; + + }(), + + isPowerOfTwo: function ( value ) { + + return ( value & ( value - 1 ) ) === 0 && value !== 0; + + }, + + nextPowerOfTwo: function ( value ) { + + value --; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value ++; + + return value; + } + +}; + +/*** END Math ***/ + +} + +module.exports = THREE; + +},{}],10:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var THREE = _dereq_('./three-math.js'); +var Util = _dereq_('./util.js'); + +var ROTATE_SPEED = 0.5; +/** + * Provides a quaternion responsible for pre-panning the scene before further + * transformations due to device sensors. + */ +function TouchPanner() { + window.addEventListener('touchstart', this.onTouchStart_.bind(this)); + window.addEventListener('touchmove', this.onTouchMove_.bind(this)); + window.addEventListener('touchend', this.onTouchEnd_.bind(this)); + + this.isTouching = false; + this.rotateStart = new THREE.Vector2(); + this.rotateEnd = new THREE.Vector2(); + this.rotateDelta = new THREE.Vector2(); + + this.theta = 0; + this.orientation = new THREE.Quaternion(); +} + +TouchPanner.prototype.getOrientation = function() { + this.orientation.setFromEuler(new THREE.Euler(0, 0, this.theta)); + return this.orientation; +}; + +TouchPanner.prototype.resetSensor = function() { + this.theta = 0; +}; + +TouchPanner.prototype.onTouchStart_ = function(e) { + // Only respond if there is exactly one touch. + if (e.touches.length != 1) { + return; + } + this.rotateStart.set(e.touches[0].pageX, e.touches[0].pageY); + this.isTouching = true; +}; + +TouchPanner.prototype.onTouchMove_ = function(e) { + if (!this.isTouching) { + return; + } + this.rotateEnd.set(e.touches[0].pageX, e.touches[0].pageY); + this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart); + this.rotateStart.copy(this.rotateEnd); + + // On iOS, direction is inverted. + if (Util.isIOS()) { + this.rotateDelta.x *= -1; + } + + var element = document.body; + this.theta += 2 * Math.PI * this.rotateDelta.x / element.clientWidth * ROTATE_SPEED; +}; + +TouchPanner.prototype.onTouchEnd_ = function(e) { + this.isTouching = false; +}; + +module.exports = TouchPanner; + +},{"./three-math.js":9,"./util.js":11}],11:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var Util = window.Util || {}; + +Util.MIN_TIMESTEP = 0.001; +Util.MAX_TIMESTEP = 1; + +Util.clamp = function(value, min, max) { + return Math.min(Math.max(min, value), max); +}; + +Util.isIOS = function() { + return /iPad|iPhone|iPod/.test(navigator.platform); +}; + +Util.isFirefoxAndroid = function() { + return navigator.userAgent.indexOf('Firefox') !== -1 && navigator.userAgent.indexOf('Android') !== -1; +} + +// Helper method to validate the time steps of sensor timestamps. +Util.isTimestampDeltaValid = function(timestampDeltaS) { + if (isNaN(timestampDeltaS)) { + return false; + } + if (timestampDeltaS <= Util.MIN_TIMESTEP) { + return false; + } + if (timestampDeltaS > Util.MAX_TIMESTEP) { + return false; + } + return true; +} + +module.exports = Util; + +},{}],12:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var CardboardHMDVRDevice = _dereq_('./cardboard-hmd-vr-device.js'); +//var OrientationPositionSensorVRDevice = require('./orientation-position-sensor-vr-device.js'); +var FusionPositionSensorVRDevice = _dereq_('./fusion-position-sensor-vr-device.js'); +var MouseKeyboardPositionSensorVRDevice = _dereq_('./mouse-keyboard-position-sensor-vr-device.js'); +// Uncomment to add positional tracking via webcam. +//var WebcamPositionSensorVRDevice = require('./webcam-position-sensor-vr-device.js'); +var HMDVRDevice = _dereq_('./base.js').HMDVRDevice; +var PositionSensorVRDevice = _dereq_('./base.js').PositionSensorVRDevice; + +function WebVRPolyfill() { + this.devices = []; + + if (!this.isWebVRAvailable()) { + this.enablePolyfill(); + } +} + +WebVRPolyfill.prototype.isWebVRAvailable = function() { + return ('getVRDevices' in navigator) || ('mozGetVRDevices' in navigator); +}; + + +WebVRPolyfill.prototype.enablePolyfill = function() { + // Initialize our virtual VR devices. + if (this.isCardboardCompatible()) { + this.devices.push(new CardboardHMDVRDevice()); + } + + // Polyfill using the right position sensor. + if (this.isMobile()) { + //this.devices.push(new OrientationPositionSensorVRDevice()); + this.devices.push(new FusionPositionSensorVRDevice()); + } else { + if (!WebVRConfig.MOUSE_KEYBOARD_CONTROLS_DISABLED) { + this.devices.push(new MouseKeyboardPositionSensorVRDevice()); + } + // Uncomment to add positional tracking via webcam. + //this.devices.push(new WebcamPositionSensorVRDevice()); + } + + // Provide navigator.getVRDevices. + navigator.getVRDevices = this.getVRDevices.bind(this); + + // Provide the CardboardHMDVRDevice and PositionSensorVRDevice objects. + window.HMDVRDevice = HMDVRDevice; + window.PositionSensorVRDevice = PositionSensorVRDevice; +}; + +WebVRPolyfill.prototype.getVRDevices = function() { + var devices = this.devices; + return new Promise(function(resolve, reject) { + try { + resolve(devices); + } catch (e) { + reject(e); + } + }); +}; + +/** + * Determine if a device is mobile. + */ +WebVRPolyfill.prototype.isMobile = function() { + return /Android/i.test(navigator.userAgent) || + /iPhone|iPad|iPod/i.test(navigator.userAgent); +}; + +WebVRPolyfill.prototype.isCardboardCompatible = function() { + // For now, support all iOS and Android devices. + // Also enable the WebVRConfig.FORCE_VR flag for debugging. + return this.isMobile() || WebVRConfig.FORCE_ENABLE_VR; +}; + +module.exports = WebVRPolyfill; + +},{"./base.js":1,"./cardboard-hmd-vr-device.js":2,"./fusion-position-sensor-vr-device.js":4,"./mouse-keyboard-position-sensor-vr-device.js":6}]},{},[5]); + +},{}],8:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * Sits in an embedded iframe, receiving DeviceMotion messages from a containing + * iFrame. These messages are converted into synthetic DeviceMotion events. + * + * This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=150072. + */ +function DeviceMotionReceiver() { + window.addEventListener('message', this.onMessage_.bind(this), false); +} + +DeviceMotionReceiver.prototype.onMessage_ = function(event) { + var message = event.data; + if (message.type !== 'DeviceMotion') { + console.warn('Got unknown message of type %s from %s', message.type, message.origin); + return; + } + + console.log('onMessage_', event); + + // Synthesize a DeviceMotion event. + this.synthesizeDeviceMotionEvent_(message.deviceMotionEvent); +}; + +DeviceMotionReceiver.prototype.synthesizeDeviceMotionEvent_ = function(eventData) { + var type = 'devicemotion-iframe'; + var canBubble = false; + var cancelable = false; + + var dme = document.createEvent('DeviceMotionEvent'); + dme.initDeviceMotionEvent(type, canBubble, cancelable, + eventData.acceleration, + eventData.accelerationIncludingGravity, + eventData.rotationRate, + eventData.interval); + + window.dispatchEvent(dme); +}; + +module.exports = DeviceMotionReceiver; + +},{}],9:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +function Emitter() { + this.initEmitter(); +} + +Emitter.prototype.initEmitter = function() { + this.callbacks = {}; +}; + +Emitter.prototype.emit = function(eventName) { + var callbacks = this.callbacks[eventName]; + if (!callbacks) { + console.log('No valid callback specified.'); + return; + } + var args = [].slice.call(arguments) + // Eliminate the first param (the callback). + args.shift(); + for (var i = 0; i < callbacks.length; i++) { + callbacks[i].apply(this, args); + } +}; + +Emitter.prototype.on = function(eventName, callback) { + if (eventName in this.callbacks) { + var cbs = this.callbacks[eventName] + if (cbs.indexOf(callback) == -1) { + cbs.push(callback); + } + } else { + this.callbacks[eventName] = [callback]; + } +}; + +Emitter.prototype.removeListener = function(eventName, callback) { + if (!(eventName in this.callbacks)) { + return; + } + var cbs = this.callbacks[eventName]; + var ind = cbs.indexOf(callback); + if (ind == -1) { + console.warn('No matching callback found'); + return; + } + cbs.splice(ind, 1); +}; + +module.exports = Emitter; + +},{}],10:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var Eyes = { + LEFT: 0, + RIGHT: 1 +}; + +module.exports = Eyes; + +},{}],11:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Shows a 2D loading indicator while various pieces of EmbedVR load. + */ +function LoadingIndicator() { + this.el = this.build_(); + document.body.appendChild(this.el); + this.show(); +} + +LoadingIndicator.prototype.build_ = function() { + var overlay = document.createElement('div'); + var s = overlay.style; + s.position = 'fixed'; + s.top = 0; + s.left = 0; + s.width = '100%'; + s.height = '100%'; + s.background = '#eee'; + var img = document.createElement('img'); + img.src = 'images/loading.gif'; + var s = img.style; + s.position = 'absolute'; + s.top = '50%'; + s.left = '50%'; + s.transform = 'translate(-50%, -50%)'; + + overlay.appendChild(img); + return overlay; +}; + +LoadingIndicator.prototype.hide = function() { + this.el.style.display = 'none'; +}; + +LoadingIndicator.prototype.show = function() { + this.el.style.display = 'block'; +}; + +module.exports = LoadingIndicator; + +},{}],12:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Disable distortion provided by the boilerplate because we are doing +// vertex-based distortion. +WebVRConfig = window.WebVRConfig || {} +WebVRConfig.PREVENT_DISTORTION = true; + +// Initialize the loading indicator as quickly as possible to give the user +// immediate feedback. +var LoadingIndicator = _dereq_('./loading-indicator'); +var loadIndicator = new LoadingIndicator(); + +// Include relevant polyfills. +_dereq_('../node_modules/webvr-polyfill/build/webvr-polyfill'); +var ES6Promise = _dereq_('../node_modules/es6-promise/dist/es6-promise.min'); +// Polyfill ES6 promises for IE. +ES6Promise.polyfill(); + +var PhotosphereRenderer = _dereq_('./photosphere-renderer'); +var SceneLoader = _dereq_('./scene-loader'); +var Stats = _dereq_('../node_modules/stats-js/build/stats.min'); +var Util = _dereq_('./util'); + +// Include the DeviceMotionReceiver for the iOS cross domain iframe workaround. +// This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=150072. +var DeviceMotionReceiver = _dereq_('./device-motion-receiver'); +var dmr = new DeviceMotionReceiver(); + + +window.addEventListener('load', init); + +var stats = new Stats(); + +var loader = new SceneLoader(); +loader.on('error', onSceneError); +loader.on('load', onSceneLoad); + +var renderer = new PhotosphereRenderer(); +renderer.on('error', onRenderError); + +var videoElement = null; +// TODO: Make this not global. +// Currently global in order to allow callbacks. +var loadedScene = null; + +function init() { + if (!Util.isWebGLEnabled()) { + showError('WebGL not supported.'); + return; + } + // Load the scene. + loader.loadScene(); + + if (Util.getQueryParameter('debug')) { + showStats(); + } +} + +function loadImage(src, params) { + renderer.on('load', onRenderLoad); + renderer.setPhotosphere(src, params); +} + +function onSceneLoad(scene) { + if (!scene || !scene.isComplete()) { + showError('Scene failed to load'); + return; + } + + loadedScene = scene; + + var params = { + isStereo: scene.isStereo, + } + renderer.setDefaultLookDirection(scene.yaw || 0); + + if (scene.preview) { + var onPreviewLoad = function() { + loadIndicator.hide(); + renderer.removeListener('load', onPreviewLoad); + renderer.setPhotosphere(scene.image, params); + } + renderer.removeListener('load', onRenderLoad); + renderer.on('load', onPreviewLoad); + renderer.setPhotosphere(scene.preview, params); + } else if (scene.video) { + if (Util.isIOS() || Util.isIE11()) { + // On iOS and IE 11, if an 'image' param is provided, load it instead of + // showing an error. + // + // TODO(smus): Once video textures are supported, remove this fallback. + if (scene.image) { + loadImage(scene.image, params); + } else { + showError('Video is not supported on this platform (iOS or IE11).'); + } + } else { + // Load the video element. + videoElement = document.createElement('video'); + videoElement.src = scene.video; + videoElement.loop = true; + videoElement.setAttribute('crossorigin', 'anonymous'); + videoElement.addEventListener('canplaythrough', onVideoLoad); + videoElement.addEventListener('error', onVideoError); + } + } else if (scene.image) { + // Otherwise, just render the photosphere. + loadImage(scene.image, params); + } + + console.log('Loaded scene', scene); +} + +function onVideoLoad() { + // Render the stereo video. + var params = { + isStereo: loadedScene.isStereo, + } + renderer.set360Video(videoElement, params); + + // On mobile, tell the user they need to tap to start. Otherwise, autoplay. + if (!Util.isMobile()) { + // Hide loading indicator. + loadIndicator.hide(); + // Autoplay the video on desktop. + videoElement.play(); + } else { + // Tell user to tap to start. + showError('Tap to start video', 'Play'); + document.body.addEventListener('touchend', onVideoTap); + } + + // Prevent onVideoLoad from firing multiple times. + videoElement.removeEventListener('canplaythrough', onVideoLoad); +} + +function onVideoTap() { + hideError(); + videoElement.play(); + + // Prevent multiple play() calls on the video element. + document.body.removeEventListener('touchend', onVideoTap); +} + +function onRenderLoad() { + // Hide loading indicator. + loadIndicator.hide(); +} + +function onSceneError(message) { + showError('Loader: ' + message); +} + +function onRenderError(message) { + showError('Render: ' + message); +} + +function onVideoError(e) { + showError('Video load error'); + console.log(e); +} + +function showError(message, opt_title) { + // Hide loading indicator. + loadIndicator.hide(); + + var error = document.querySelector('#error'); + error.classList.add('visible'); + error.querySelector('.message').innerHTML = message; + + var title = (opt_title !== undefined ? opt_title : 'Error'); + error.querySelector('.title').innerHTML = title; +} + +function hideError() { + var error = document.querySelector('#error'); + error.classList.remove('visible'); +} + +function showStats() { + stats.setMode(0); // 0: fps, 1: ms + + // Align bottom-left. + stats.domElement.style.position = 'absolute'; + stats.domElement.style.left = '0px'; + stats.domElement.style.bottom = '0px'; + document.body.appendChild(stats.domElement); +} + +function loop(time) { + stats.begin(); + renderer.render(time); + stats.end(); + requestAnimationFrame(loop); +} +requestAnimationFrame(loop); + +},{"../node_modules/es6-promise/dist/es6-promise.min":1,"../node_modules/stats-js/build/stats.min":2,"../node_modules/webvr-polyfill/build/webvr-polyfill":7,"./device-motion-receiver":8,"./loading-indicator":11,"./photosphere-renderer":13,"./scene-loader":15,"./util":16}],13:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var Emitter = _dereq_('./emitter'); +var Eyes = _dereq_('./eyes'); +var THREE = _dereq_('../node_modules/three/three'); +THREE.VRControls = _dereq_('../node_modules/three/examples/js/controls/VRControls'); +THREE.VREffect = _dereq_('../node_modules/three/examples/js/effects/VREffect'); +var Util = _dereq_('./util'); +var VertexDistorter = _dereq_('./vertex-distorter'); +_dereq_('../node_modules/webvr-boilerplate/build/webvr-manager'); + +function PhotosphereRenderer() { + this.init(); +} +PhotosphereRenderer.prototype = new Emitter(); + +PhotosphereRenderer.prototype.init = function() { + var container = document.querySelector('body'); + var camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.1, 100); + var cameraDummy = new THREE.Object3D(); + cameraDummy.add(camera); + + // Antialiasing temporarily disabled to improve performance. + var renderer = new THREE.WebGLRenderer({antialias: false}); + renderer.setClearColor(0x000000, 0); + renderer.setSize(window.innerWidth, window.innerHeight); + + // Round down fractional DPR values for better performance. + renderer.setPixelRatio(Math.floor(window.devicePixelRatio)); + container.appendChild(renderer.domElement); + + var controls = new THREE.VRControls(camera); + var effect = new THREE.VREffect(renderer); + effect.setSize(window.innerWidth, window.innerHeight); + + this.camera = camera; + this.renderer = renderer; + this.effect = effect; + this.controls = controls; + this.manager = new WebVRManager(renderer, effect, {isUndistorted: true}); + + this.initScenes_(); + + // The vertex distorter. + this.distorter = new VertexDistorter(this.manager.getDeviceInfo()); + + this.manager.on('modechange', this.onModeChange_.bind(this)); + this.manager.on('viewerchange', this.onViewerChange_.bind(this)); + + // Watch the resize event. + window.addEventListener('resize', this.onResize_.bind(this)); + + var that = this; + navigator.getVRDevices().then(function(devices) { + devices.forEach(function(device) { + if (device instanceof HMDVRDevice) { + that.hmd = device; + } + }); + }); +}; + +PhotosphereRenderer.prototype.render = function(timestamp) { + this.controls.update(); + this.manager.render(this.scenes, this.camera, timestamp); +}; + +PhotosphereRenderer.prototype.setDefaultLookDirection = function(phi) { + // Rotate the camera parent to take into account the scene's rotation. + this.camera.parent.rotation.y = phi; +}; + +/** + * Sets the photosphere based on the image in the source. Supports stereo and + * mono photospheres. + * + * Emits 'load' and 'error' events. + */ +PhotosphereRenderer.prototype.setPhotosphere = function(src, opt_params) { + var params = opt_params || {}; + + this.isStereo = !!params.isStereo; + this.src = src; + + // Load texture. + var loader = new THREE.TextureLoader(); + loader.crossOrigin = 'anonymous'; + loader.load(src, this.onTextureLoaded_.bind(this), null, + this.onTextureError_.bind(this)); +}; + +PhotosphereRenderer.prototype.set360Video = function(videoElement, opt_params) { + var params = opt_params || {}; + + this.isStereo = !!params.isStereo; + + // Load the video texture. + var videoTexture = new THREE.VideoTexture(videoElement); + videoTexture.minFilter = THREE.LinearFilter; + videoTexture.magFilter = THREE.LinearFilter; + videoTexture.format = THREE.RGBFormat; + videoTexture.generateMipmaps = false; + videoTexture.needsUpdate = true; + + this.onTextureLoaded_(videoTexture); +}; + +PhotosphereRenderer.prototype.initScenes_ = function() { + this.sceneLeft = this.createScene_(); + this.sceneRight = this.createScene_(); + this.sceneLeft.add(this.camera.parent); + + this.scenes = [this.sceneLeft, this.sceneRight]; + this.eyes = [Eyes.LEFT, Eyes.RIGHT]; +}; + +PhotosphereRenderer.prototype.onTextureLoaded_ = function(texture) { + var sphereLeft; + var sphereRight; + if (this.isStereo) { + sphereLeft = this.createPhotosphere_(texture, {offsetY: 0.5, scaleY: 0.5}); + sphereRight = this.createPhotosphere_(texture, {offsetY: 0, scaleY: 0.5}); + } else { + sphereLeft = this.createPhotosphere_(texture); + sphereRight = this.createPhotosphere_(texture); + } + + this.sceneLeft.getObjectByName('photo').children = [sphereLeft]; + this.sceneRight.getObjectByName('photo').children = [sphereRight]; + + this.emit('load'); +}; + +PhotosphereRenderer.prototype.onTextureError_ = function(error) { + this.emit('error', 'Unable to load texture from ' + this.src); +}; + + +PhotosphereRenderer.prototype.createPhotosphere_ = function(texture, opt_params) { + var p = opt_params || {}; + p.scaleX = p.scaleX || 1; + p.scaleY = p.scaleY || 1; + p.offsetX = p.offsetX || 0; + p.offsetY = p.offsetY || 0; + p.phiStart = p.phiStart || 0; + p.phiLength = p.phiLength || Math.PI * 2; + p.thetaStart = p.thetaStart || 0; + p.thetaLength = p.thetaLength || Math.PI; + + var geometry = new THREE.SphereGeometry(1, 48, 48, + p.phiStart, p.phiLength, p.thetaStart, p.thetaLength); + geometry.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1)); + var uvs = geometry.faceVertexUvs[0]; + for (var i = 0; i < uvs.length; i ++) { + for (var j = 0; j < 3; j ++) { + uvs[i][j].x *= p.scaleX; + uvs[i][j].x += p.offsetX; + uvs[i][j].y *= p.scaleY; + uvs[i][j].y += p.offsetY; + } + } + + var material = new THREE.MeshBasicMaterial({ map: texture }); + this.distorter.setMap(texture); + var out = new THREE.Mesh(geometry, material); + out.renderOrder = -1; + return out; +}; + +PhotosphereRenderer.prototype.createScene_ = function(opt_params) { + var scene = new THREE.Scene(); + // Add a light. + scene.add(new THREE.PointLight(0xFFFFFF)); + + // Add a group for the photosphere. + var photoGroup = new THREE.Object3D(); + photoGroup.name = 'photo'; + scene.add(photoGroup); + + return scene; +}; + +PhotosphereRenderer.prototype.updateMaterial_ = function(material_FOO) { + for (var i = 0; i < this.scenes.length; i++) { + var eye = this.eyes[i]; + var material = this.distorter.getShaderMaterial(eye); + var scene = this.scenes[i]; + var children = scene.getObjectByName('photo').children; + for (var j = 0; j < children.length; j++) { + var child = children[j]; + child.material = material; + child.material.needsUpdate = true; + } + } +}; + +PhotosphereRenderer.prototype.updateRenderRect_ = function() { + if (this.hmd && this.hmd.setRenderRect) { + var deviceInfo = this.manager.getDeviceInfo(); + var leftRect = deviceInfo.getUndistortedViewportLeftEye(); + var dpr = window.devicePixelRatio; + leftRect.x /= dpr; + leftRect.y /= dpr; + leftRect.width /= dpr; + leftRect.height /= dpr; + + var rightRect = Util.clone(leftRect); + rightRect.x = (window.innerWidth - leftRect.x) - leftRect.width; + + this.hmd.setRenderRect(leftRect, rightRect); + } +}; + +PhotosphereRenderer.prototype.onModeChange_ = function(newMode, oldMode) { + console.log('onModeChange_', newMode); + + var coefficients; + if (newMode == WebVRManager.Modes.VR) { + // Entering VR mode. + this.distorter.setEnabled(true); + this.updateMaterial_(); + } else if (oldMode == WebVRManager.Modes.VR) { + // Leaving VR mode. + this.distorter.setEnabled(false); + this.updateMaterial_(); + } + + if (window.analytics) { + analytics.logModeChanged(newMode); + } +}; + +PhotosphereRenderer.prototype.onViewerChange_ = function(newViewer) { + console.log('onViewerChange_', newViewer); + + // Reset the photosphere with new coefficients. + this.updateMaterial_(); + this.updateRenderRect_(); +}; + +PhotosphereRenderer.prototype.onResize_ = function() { + this.updateRenderRect_(); +}; + +module.exports = PhotosphereRenderer; + +},{"../node_modules/three/examples/js/controls/VRControls":3,"../node_modules/three/examples/js/effects/VREffect":4,"../node_modules/three/three":5,"../node_modules/webvr-boilerplate/build/webvr-manager":6,"./emitter":9,"./eyes":10,"./util":16,"./vertex-distorter":17}],14:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Contains all information about a given scene, including the photosphere asset, + * background music. + */ +function SceneInfo(opt_params) { + var params = opt_params || {}; + this.id = params.id; + this.title = params.title; + this.image = params.image; + this.preview = params.preview; + this.isStereo = !!params.isStereo; + this.audio = params.audio; + this.video = params.video; + this.yaw = params.yaw || 0; + this.isYawOnly = params.isYawOnly; +} + +SceneInfo.prototype.isComplete = function() { + return !!this.image || !!this.video; +}; + +SceneInfo.prototype.toObject = function() { + return { + id: this.id || null, + title: this.title || null, + image: this.image, + preview: this.preview, + isStereo: this.isStereo, + audio: this.audio, + yaw: this.yaw || null, + video: this.video || null, + }; +}; + +SceneInfo.prototype.isImageDataURI = function() { + return this.image.indexOf('data:') == 0; +}; + + +module.exports = SceneInfo; + +},{}],15:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var Emitter = _dereq_('./emitter'); +var SceneInfo = _dereq_('./scene-info'); + +var Query = { + JSON_URL: 'url', + VIDEO_URL: 'video', + OBJECT_URL: 'object', + IMAGE_URL: 'image', + PREVIEW_URL: 'preview', + IS_STEREO: 'is_stereo', + AUDIO_URL: 'audio', + START_YAW: 'start_yaw', + IS_YAW_ONLY: 'is_yaw_only', +}; + +function SceneLoader() { +} +SceneLoader.prototype = new Emitter(); + +/** + * Loads a scene from a JSON file. + */ +SceneLoader.prototype.loadFromJson_ = function(url, callback) { + // XHR to fetch the JSON. + var xhr = new XMLHttpRequest(); + xhr.open('GET', url); + var that = this; + xhr.onload = function(e) { + try { + var jsonObj = JSON.parse(this.response); + } catch (e) { + that.emit('error', 'Invalid JSON at ' + url + '.'); + return; + } + var labelObjects = {}; + var labels = jsonObj.labels || []; + // Go through the labels in the data and objectify them. + for (var i = 0; i < labels.length; i++) { + var label = new Label(labels[i]); + labelObjects[label.id] = label; + } + jsonObj.labels = labelObjects; + var scene = new SceneInfo(jsonObj); + that.emit('load', scene); + }; + xhr.send(); +}; + +/** + * Parse out GET parameters from the URL string and load the scene. + */ +SceneLoader.prototype.loadFromGetParams_ = function() { + var params = { + image: Util.getQueryParameter(Query.IMAGE_URL), + video: Util.getQueryParameter(Query.VIDEO_URL), + object: Util.getQueryParameter(Query.OBJECT_URL), + preview: Util.getQueryParameter(Query.PREVIEW_URL), + isStereo: this.parseBoolean_(Util.getQueryParameter(Query.IS_STEREO)), + audio: Util.getQueryParameter(Query.AUDIO_URL), + isYawOnly: this.parseBoolean_(Util.getQueryParameter(Query.IS_YAW_ONLY)), + yaw: THREE.Math.degToRad(Util.getQueryParameter(Query.START_YAW)), + }; + + var count = 0; + count += (params[Query.IMAGE_URL] ? 1 : 0); + count += (params[Query.VIDEO_URL] ? 1 : 0); + count += (params[Query.OBJECT_URL] ? 1 : 0); + // Validate this. + if (count == 0) { + this.emit('error', 'Either "image", "video", or "object" GET parameter is required.'); + return; + } + + var scene = new SceneInfo(params); + this.emit('load', scene); +}; + +SceneLoader.prototype.parseBoolean_ = function(value) { + if (value == 'false') { + return false; + } + return !!value; +}; + +SceneLoader.prototype.loadScene = function(callback) { + // If there's a url param specified, try loading from JSON. + var url = Util.getQueryParameter('url'); + var image = Util.getQueryParameter('image'); + var video = Util.getQueryParameter('video'); + var object = Util.getQueryParameter('object'); + if (url) { + this.loadFromJson_(url); + } else if (image || video || object) { + // Otherwise, try loading from URL parameters. + this.loadFromGetParams_(); + } else { + // If it fails, throw an exception. + this.emit('error', 'Unable to load scene. Required parameter missing.'); + } +}; + +module.exports = SceneLoader; + +},{"./emitter":9,"./scene-info":14}],16:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +Util = window.Util || {}; + +Util.isDataURI = function(src) { + return src && src.indexOf('data:') == 0; +}; + +Util.generateUUID = function() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); +}; + +Util.isMobile = function() { + var check = false; + (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); + return check; +}; + +Util.isIOS = function() { + return /(iPad|iPhone|iPod)/g.test(navigator.userAgent); +}; + +Util.cloneObject = function(obj) { + var out = {}; + for (key in obj) { + out[key] = obj[key]; + } + return out; +}; + +Util.hashCode = function(s) { + return s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0); +}; + +Util.loadTrackSrc = function(context, src, callback, opt_progressCallback) { + var request = new XMLHttpRequest(); + request.open('GET', src, true); + request.responseType = 'arraybuffer'; + + // Decode asynchronously. + request.onload = function() { + context.decodeAudioData(request.response, function(buffer) { + callback(buffer); + }, function(e) { + console.error(e); + }); + }; + if (opt_progressCallback) { + request.onprogress = function(e) { + var percent = e.loaded / e.total; + opt_progressCallback(percent); + }; + } + request.send(); +}; + +Util.isPow2 = function(n) { + return (n & (n - 1)) == 0; +}; + +Util.capitalize = function(s) { + return s.charAt(0).toUpperCase() + s.slice(1); +}; + +Util.isIFrame = function() { + try { + return window.self !== window.top; + } catch (e) { + return true; + } +}; + +// From http://goo.gl/4WX3tg +Util.getQueryParameter = function(name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), + results = regex.exec(location.search); + return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); +}; + + +// From http://stackoverflow.com/questions/11871077/proper-way-to-detect-webgl-support. +Util.isWebGLEnabled = function() { + var canvas = document.createElement('canvas'); + try { gl = canvas.getContext("webgl"); } + catch (x) { gl = null; } + + if (gl == null) { + try { gl = canvas.getContext("experimental-webgl"); experimental = true; } + catch (x) { gl = null; } + } + return !!gl; +}; + +Util.clone = function(obj) { + return JSON.parse(JSON.stringify(obj)); +}; + +// From http://stackoverflow.com/questions/10140604/fastest-hypotenuse-in-javascript +Util.hypot = Math.hypot || function(x, y) { + return Math.sqrt(x*x + y*y); +}; + +// From http://stackoverflow.com/a/17447718/693934 +Util.isIE11 = function() { + return navigator.userAgent.match(/Trident/); +}; + +module.exports = Util; + +},{}],17:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +THREE = _dereq_('../node_modules/three/three'); +var Eyes = _dereq_('./eyes'); +var Util = _dereq_('./util'); + +var DEFAULT_FOV = 40; +var NO_DISTORTION = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; +/** + * Responsible for distortion correction by pre-distorting the vertex geometry + * so that a second shader pass is not needed. This is intended to greatly + * optimize the rendering process. + */ +function VertexDistorter(deviceInfo) { + this.texture = null; + this.isEnabled = false; + + this.deviceInfo = deviceInfo; +} + +VertexDistorter.prototype.setEnabled = function(isEnabled) { + this.isEnabled = isEnabled; +} + +/** + * Sets the texture that is used to render this photosphere. + */ +VertexDistorter.prototype.setMap = function(texture) { + this.texture = texture; +}; + +VertexDistorter.prototype.getVertexShader_ = function() { + return [ + '#ifdef GL_ES', + 'precision highp float;', + '#endif', + + 'varying vec2 vUV;', + + this.getDistortionInclude_(), + + 'void main() {', + // Pass through texture coordinates to the fragment shader. + 'vUV = uv;', + + // Here, we want to ensure that we are using an undistorted projection + // matrix. By setting isUndistorted: true in the WebVRManager, we + // guarantee this. + 'vec4 pos = projectionMatrix * modelViewMatrix * vec4(position, 1.0);', + + 'gl_Position = Distort(pos);', + '}' + ].join('\n'); +}; + +VertexDistorter.prototype.getNoopVertexShader_ = function() { + return [ + '#ifdef GL_ES', + 'precision highp float;', + '#endif', + + 'varying vec2 vUV;', + + 'void main() {', + 'gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);', + 'vUV = uv;', + '}' + ].join('\n'); +}; + +VertexDistorter.prototype.getFragmentShader_ = function() { + return [ + '#ifdef GL_ES', + 'precision highp float;', + '#endif', + + 'varying vec2 vUV;', + 'uniform sampler2D texture;', + + 'void main() {', + 'gl_FragColor = texture2D(texture, vUV);', + '}' + ].join('\n'); +}; + +VertexDistorter.prototype.getNoopDistortionInclude_ = function() { + return [ + 'vec4 Distort(vec4 point) {', + 'return point;', + '}' + ].join('\n'); +}; + +VertexDistorter.prototype.getDistortionInclude_ = function() { + return [ + 'uniform float uDistortionCoefficients[12];', + 'uniform float uDistortionMaxFovSquared;', + 'uniform vec2 uDistortionFovOffset;', + 'uniform vec2 uDistortionFovScale;', + + // Returns a scalar to distort a point; computed in reverse via the polynomial approximation: + // r' = 1 + Σ_i (uDistortionCoefficients[i] rSquared^(i+1)) i=[0..11] + // where rSquared is the squared radius of an undistorted point in tan-angle space. + // See {@link Distortion} for more information. + 'float DistortionFactor(float rSquared) {', + 'float ret = 0.0;', + 'rSquared = min(uDistortionMaxFovSquared, rSquared);', + 'ret = rSquared * (ret + uDistortionCoefficients[11]);', + 'ret = rSquared * (ret + uDistortionCoefficients[10]);', + 'ret = rSquared * (ret + uDistortionCoefficients[9]);', + 'ret = rSquared * (ret + uDistortionCoefficients[8]);', + 'ret = rSquared * (ret + uDistortionCoefficients[7]);', + 'ret = rSquared * (ret + uDistortionCoefficients[6]);', + 'ret = rSquared * (ret + uDistortionCoefficients[5]);', + 'ret = rSquared * (ret + uDistortionCoefficients[4]);', + 'ret = rSquared * (ret + uDistortionCoefficients[3]);', + 'ret = rSquared * (ret + uDistortionCoefficients[2]);', + 'ret = rSquared * (ret + uDistortionCoefficients[1]);', + 'ret = rSquared * (ret + uDistortionCoefficients[0]);', + 'return ret + 1.0;', + '}', + + // Given a point in clip space, distort the point according to the coefficients stored in + // uDistortionCoefficients and the field of view (FOV) specified in uDistortionFovOffset and + // uDistortionFovScale. + // Returns the distorted point in clip space, with its Z untouched. + 'vec4 Distort(vec4 point) {', + // Put point into normalized device coordinates (NDC), [(-1, -1, -1) to (1, 1, 1)]. + 'vec3 pointNdc = point.xyz / point.w;', + // Throw away the Z coordinate and map the point to the unit square, [(0, 0) to (1, 1)]. + 'vec2 pointUnitSquare = (pointNdc.xy + vec2(1.0)) / 2.0;', + // Map the point into FOV tan-angle space. + 'vec2 pointTanAngle = pointUnitSquare * uDistortionFovScale - uDistortionFovOffset;', + 'float radiusSquared = dot(pointTanAngle, pointTanAngle);', + 'float distortionFactor = DistortionFactor(radiusSquared);', + //'float distortionFactor = 2.0;', + 'vec2 distortedPointTanAngle = pointTanAngle * distortionFactor;', + // Reverse the mappings above to bring the distorted point back into NDC space. + 'vec2 distortedPointUnitSquare = (distortedPointTanAngle + uDistortionFovOffset)', + '/ uDistortionFovScale;', + 'vec3 distortedPointNdc = vec3(distortedPointUnitSquare * 2.0 - vec2(1.0), pointNdc.z);', + // Convert the point into clip space before returning in case any operations are done after. + 'return vec4(distortedPointNdc, 1.0) * point.w;', + '}', + ].join('\n'); +}; + +VertexDistorter.prototype.getDistortionMaxFovSquared_ = function() { + var fov = this.getFov_(); + var maxFov = Util.hypot( + Math.tan(THREE.Math.degToRad(Math.max(fov.leftDegrees, fov.rightDegrees))), + Math.tan(THREE.Math.degToRad(Math.max(fov.downDegrees, fov.upDegrees)))); + return maxFov * maxFov; +}; + +VertexDistorter.prototype.getDistortionCoefficients_ = function() { + var viewer = this.deviceInfo.viewer; + return this.isEnabled ? viewer.inverseCoefficients : NO_DISTORTION; +}; + +VertexDistorter.prototype.getDistortionFovOffset_ = function(eye) { + var fov = this.getFov_(eye); + /* + var sideDegrees = Math.min(fov.leftDegrees, fov.rightDegrees); + var left = Math.tan(THREE.Math.degToRad(sideDegrees)); + */ + var left = Math.tan(THREE.Math.degToRad(fov.leftDegrees)); + var down = Math.tan(THREE.Math.degToRad(fov.downDegrees)); + return new THREE.Vector2(left, down); +}; + +VertexDistorter.prototype.getDistortionFovScale_ = function() { + var fov = this.getFov_(); + var left = Math.tan(THREE.Math.degToRad(fov.leftDegrees)); + var right = Math.tan(THREE.Math.degToRad(fov.rightDegrees)); + var up = Math.tan(THREE.Math.degToRad(fov.upDegrees)); + var down = Math.tan(THREE.Math.degToRad(fov.downDegrees)); + return new THREE.Vector2(left + right, up + down); +}; + + +VertexDistorter.prototype.getUniforms_ = function(eye) { + return { + texture: { + type: 't', + value: this.texture + }, + uDistortionCoefficients: { + type: 'fv1', + value: this.getDistortionCoefficients_() + }, + uDistortionMaxFovSquared: { + type: 'f', + value: this.getDistortionMaxFovSquared_() + }, + uDistortionFovOffset: { + type: 'v2', + value: this.getDistortionFovOffset_(eye) + }, + uDistortionFovScale: { + type: 'v2', + value: this.getDistortionFovScale_() + } + }; +}; + +VertexDistorter.prototype.getShaderMaterial = function(eye) { + var uniforms = this.getUniforms_(eye); + return new THREE.ShaderMaterial({ + uniforms: uniforms, + vertexShader: this.getVertexShader_(), + fragmentShader: this.getFragmentShader_() + }); +}; + +VertexDistorter.prototype.getFov_ = function(opt_eye) { + var eye = opt_eye || Eyes.LEFT; + + if (eye == Eyes.LEFT) { + return this.deviceInfo.getFieldOfViewLeftEye(true); + } + if (eye == Eyes.RIGHT) { + return this.deviceInfo.getFieldOfViewRightEye(true); + } + return null; +}; + +module.exports = VertexDistorter; + +},{"../node_modules/three/three":5,"./eyes":10,"./util":16}],18:[function(_dereq_,module,exports){ +// shim for using process in browser + +var process = module.exports = {}; +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = setTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + clearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + setTimeout(drainQueue, 0); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}]},{},[12]); diff --git a/build/vrview.min.js b/build/vrview.min.js new file mode 100644 index 00000000..efea002d --- /dev/null +++ b/build/vrview.min.js @@ -0,0 +1,22 @@ +!function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;ot;t+=2){var e=X[t],n=X[t+1];e(n),X[t]=void 0,X[t+1]=void 0}G=0}function f(){try{var t=_dereq_,e=t("vertx");return U=e.runOnLoop||e.runOnContext,i()}catch(n){return c()}}function l(t,e){var n=this,r=n._state;if(r===et&&!t||r===nt&&!e)return this;var o=new this.constructor(p),i=n._result;if(r){var s=arguments[r-1];H(function(){C(r,o,s,i)})}else j(n,o,t,e);return o}function h(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var n=new e(p);return g(n,t),n}function p(){}function _(){return new TypeError("You cannot resolve a promise with itself")}function v(){return new TypeError("A promises callback cannot return that same promise.")}function d(t){try{return t.then}catch(e){return rt.error=e,rt}}function y(t,e,n,r){try{t.call(e,n,r)}catch(o){return o}}function m(t,e,n){H(function(t){var r=!1,o=y(n,e,function(n){r||(r=!0,e!==n?g(t,n):E(t,n))},function(e){r||(r=!0,S(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&o&&(r=!0,S(t,o))},t)}function w(t,e){e._state===et?E(t,e._result):e._state===nt?S(t,e._result):j(e,void 0,function(e){g(t,e)},function(e){S(t,e)})}function b(t,n,r){n.constructor===t.constructor&&r===Z&&constructor.resolve===$?w(t,n):r===rt?S(t,rt.error):void 0===r?E(t,n):e(r)?m(t,n,r):E(t,n)}function g(e,n){e===n?S(e,_()):t(n)?b(e,n,d(n)):E(e,n)}function A(t){t._onerror&&t._onerror(t._result),T(t)}function E(t,e){t._state===tt&&(t._result=e,t._state=et,0!==t._subscribers.length&&H(T,t))}function S(t,e){t._state===tt&&(t._state=nt,t._result=e,H(A,t))}function j(t,e,n,r){var o=t._subscribers,i=o.length;t._onerror=null,o[i]=e,o[i+et]=n,o[i+nt]=r,0===i&&t._state&&H(T,t)}function T(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r,o,i=t._result,s=0;ss;s++)j(r.resolve(t[s]),void 0,e,n);return o}function Y(t){var e=this,n=new e(p);return S(n,t),n}function q(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function F(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function D(t){this._id=ct++,this._state=void 0,this._result=void 0,this._subscribers=[],p!==t&&("function"!=typeof t&&q(),this instanceof D?M(this,t):F())}function K(t,e){this._instanceConstructor=t,this.promise=new t(p),Array.isArray(e)?(this._input=e,this.length=e.length,this._remaining=e.length,this._result=new Array(this.length),0===this.length?E(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&E(this.promise,this._result))):S(this.promise,this._validationError())}function L(){var t;if("undefined"!=typeof global)t=global;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;(!n||"[object Promise]"!==Object.prototype.toString.call(n.resolve())||n.cast)&&(t.Promise=at)}var N;N=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var U,W,z,B=N,G=0,H=function(t,e){X[G]=t,X[G+1]=e,G+=2,2===G&&(W?W(a):z())},I="undefined"!=typeof window?window:void 0,J=I||{},Q=J.MutationObserver||J.WebKitMutationObserver,R="undefined"!=typeof process&&"[object process]"==={}.toString.call(process),V="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,X=new Array(1e3);z=R?o():Q?s():V?u():void 0===I&&"function"==typeof _dereq_?f():c();var Z=l,$=h,tt=void 0,et=1,nt=2,rt=new P,ot=new P,it=O,st=k,ut=Y,ct=0,at=D;D.all=it,D.race=st,D.resolve=$,D.reject=ut,D._setScheduler=n,D._setAsap=r,D._asap=H,D.prototype={constructor:D,then:Z,"catch":function(t){return this.then(null,t)}};var ft=K;K.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},K.prototype._enumerate=function(){for(var t=this.length,e=this._input,n=0;this._state===tt&&t>n;n++)this._eachEntry(e[n],n)},K.prototype._eachEntry=function(t,e){var n=this._instanceConstructor,r=n.resolve;if(r===$){var o=d(t);if(o===Z&&t._state!==tt)this._settledAt(t._state,e,t._result);else if("function"!=typeof o)this._remaining--,this._result[e]=t;else if(n===at){var i=new n(p);b(i,t,o),this._willSettleAt(i,e)}else this._willSettleAt(new n(function(e){e(t)}),e)}else this._willSettleAt(r(t),e)},K.prototype._settledAt=function(t,e,n){var r=this.promise;r._state===tt&&(this._remaining--,t===nt?S(r,n):this._result[e]=n),0===this._remaining&&E(r,this._result)},K.prototype._willSettleAt=function(t,e){var n=this;j(t,void 0,function(t){n._settledAt(et,e,t)},function(t){n._settledAt(nt,e,t)})};var lt=L,ht={Promise:at,polyfill:lt};"function"==typeof define&&define.amd?define(function(){return ht}):"undefined"!=typeof module&&module.exports?module.exports=ht:"undefined"!=typeof this&&(this.ES6Promise=ht),lt()}).call(this)}).call(this,_dereq_("_process"),"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{_process:18}],2:[function(_dereq_,module,exports){var Stats=function(){var l=Date.now(),m=l,g=0,n=1/0,o=0,h=0,p=1/0,q=0,r=0,s=0,f=document.createElement("div");f.id="stats",f.addEventListener("mousedown",function(b){b.preventDefault(),t(++s%2)},!1),f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps",a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002",f.appendChild(a);var i=document.createElement("div");i.id="fpsText",i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px",i.innerHTML="FPS",a.appendChild(i);var c=document.createElement("div");for(c.id="fpsGraph",c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff",a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113",c.appendChild(j)}var d=document.createElement("div");d.id="ms",d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none",f.appendChild(d);var k=document.createElement("div");k.id="msText",k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px",k.innerHTML="MS",d.appendChild(k);var e=document.createElement("div");for(e.id="msGraph",e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0",d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){switch(s=b){case 0:a.style.display="block",d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:12,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l,n=Math.min(n,g),o=Math.max(o,g),k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));return e.appendChild(e.firstChild).style.height=a+"px",r++,b>m+1e3&&(h=Math.round(1e3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height=a+"px",m=b,r=0),b},update:function(){l=this.end()}}};"object"==typeof module&&(module.exports=Stats)},{}],3:[function(_dereq_,module,exports){var VRControls=function(object,onError){function filterInvalidDevices(devices){var oculusDevices=devices.filter(function(device){return-1!==device.deviceName.toLowerCase().indexOf("oculus")});return oculusDevices.length>=1?devices.filter(function(device){return-1===device.deviceName.toLowerCase().indexOf("cardboard")}):devices}function gotVRDevices(devices){devices=filterInvalidDevices(devices);for(var i=0;ix?-1:x>0?1:+x}),void 0===Function.prototype.name&&void 0!==Object.defineProperty&&Object.defineProperty(Function.prototype,"name",{get:function(){return this.toString().match(/^\s*function\s*(\S*)\s*\(/)[1]}}),THREE.MOUSE={LEFT:0,MIDDLE:1,RIGHT:2},THREE.CullFaceNone=0,THREE.CullFaceBack=1,THREE.CullFaceFront=2,THREE.CullFaceFrontBack=3,THREE.FrontFaceDirectionCW=0,THREE.FrontFaceDirectionCCW=1,THREE.BasicShadowMap=0,THREE.PCFShadowMap=1,THREE.PCFSoftShadowMap=2,THREE.FrontSide=0,THREE.BackSide=1,THREE.DoubleSide=2,THREE.FlatShading=1,THREE.SmoothShading=2,THREE.NoColors=0,THREE.FaceColors=1,THREE.VertexColors=2,THREE.NoBlending=0,THREE.NormalBlending=1,THREE.AdditiveBlending=2,THREE.SubtractiveBlending=3,THREE.MultiplyBlending=4,THREE.CustomBlending=5,THREE.AddEquation=100,THREE.SubtractEquation=101,THREE.ReverseSubtractEquation=102,THREE.MinEquation=103,THREE.MaxEquation=104,THREE.ZeroFactor=200,THREE.OneFactor=201,THREE.SrcColorFactor=202,THREE.OneMinusSrcColorFactor=203,THREE.SrcAlphaFactor=204,THREE.OneMinusSrcAlphaFactor=205,THREE.DstAlphaFactor=206,THREE.OneMinusDstAlphaFactor=207,THREE.DstColorFactor=208,THREE.OneMinusDstColorFactor=209,THREE.SrcAlphaSaturateFactor=210,THREE.NeverDepth=0,THREE.AlwaysDepth=1,THREE.LessDepth=2,THREE.LessEqualDepth=3,THREE.EqualDepth=4,THREE.GreaterEqualDepth=5,THREE.GreaterDepth=6,THREE.NotEqualDepth=7,THREE.MultiplyOperation=0,THREE.MixOperation=1,THREE.AddOperation=2,THREE.UVMapping=300,THREE.CubeReflectionMapping=301,THREE.CubeRefractionMapping=302,THREE.EquirectangularReflectionMapping=303,THREE.EquirectangularRefractionMapping=304,THREE.SphericalReflectionMapping=305,THREE.RepeatWrapping=1e3,THREE.ClampToEdgeWrapping=1001,THREE.MirroredRepeatWrapping=1002,THREE.NearestFilter=1003,THREE.NearestMipMapNearestFilter=1004,THREE.NearestMipMapLinearFilter=1005,THREE.LinearFilter=1006,THREE.LinearMipMapNearestFilter=1007,THREE.LinearMipMapLinearFilter=1008,THREE.UnsignedByteType=1009,THREE.ByteType=1010,THREE.ShortType=1011,THREE.UnsignedShortType=1012,THREE.IntType=1013,THREE.UnsignedIntType=1014,THREE.FloatType=1015,THREE.HalfFloatType=1025,THREE.UnsignedShort4444Type=1016,THREE.UnsignedShort5551Type=1017,THREE.UnsignedShort565Type=1018,THREE.AlphaFormat=1019,THREE.RGBFormat=1020,THREE.RGBAFormat=1021,THREE.LuminanceFormat=1022,THREE.LuminanceAlphaFormat=1023,THREE.RGBEFormat=THREE.RGBAFormat,THREE.RGB_S3TC_DXT1_Format=2001,THREE.RGBA_S3TC_DXT1_Format=2002,THREE.RGBA_S3TC_DXT3_Format=2003,THREE.RGBA_S3TC_DXT5_Format=2004,THREE.RGB_PVRTC_4BPPV1_Format=2100,THREE.RGB_PVRTC_2BPPV1_Format=2101,THREE.RGBA_PVRTC_4BPPV1_Format=2102,THREE.RGBA_PVRTC_2BPPV1_Format=2103,THREE.LoopOnce=2200,THREE.LoopRepeat=2201,THREE.LoopPingPong=2202,THREE.Projector=function(){console.error("THREE.Projector has been moved to /examples/js/renderers/Projector.js."),this.projectVector=function(vector,camera){console.warn("THREE.Projector: .projectVector() is now vector.project()."),vector.project(camera)},this.unprojectVector=function(vector,camera){console.warn("THREE.Projector: .unprojectVector() is now vector.unproject()."),vector.unproject(camera)},this.pickingRay=function(vector,camera){console.error("THREE.Projector: .pickingRay() is now raycaster.setFromCamera().")}},THREE.CanvasRenderer=function(){console.error("THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js"),this.domElement=document.createElement("canvas"),this.clear=function(){},this.render=function(){},this.setClearColor=function(){},this.setSize=function(){}},THREE.Color=function(color){return 3===arguments.length?this.fromArray(arguments):this.set(color)},THREE.Color.prototype={constructor:THREE.Color,r:1,g:1,b:1,set:function(value){return value instanceof THREE.Color?this.copy(value):"number"==typeof value?this.setHex(value):"string"==typeof value&&this.setStyle(value),this},setHex:function(hex){return hex=Math.floor(hex),this.r=(hex>>16&255)/255,this.g=(hex>>8&255)/255,this.b=(255&hex)/255,this},setRGB:function(r,g,b){return this.r=r,this.g=g,this.b=b,this},setHSL:function(){function hue2rgb(p,q,t){return 0>t&&(t+=1),t>1&&(t-=1),1/6>t?p+6*(q-p)*t:.5>t?q:2/3>t?p+6*(q-p)*(2/3-t):p}return function(h,s,l){if(h=THREE.Math.euclideanModulo(h,1),s=THREE.Math.clamp(s,0,1),l=THREE.Math.clamp(l,0,1),0===s)this.r=this.g=this.b=l;else{var p=.5>=l?l*(1+s):l+s-l*s,q=2*l-p;this.r=hue2rgb(q,p,h+1/3),this.g=hue2rgb(q,p,h),this.b=hue2rgb(q,p,h-1/3)}return this}}(),setStyle:function(style){function handleAlpha(string){void 0!==string&&parseFloat(string)<1&&console.warn("THREE.Color: Alpha component of "+style+" will be ignored.")}var m;if(m=/^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec(style)){var color,name=m[1],components=m[2];switch(name){case"rgb":case"rgba":if(color=/^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(components))return this.r=Math.min(255,parseInt(color[1],10))/255,this.g=Math.min(255,parseInt(color[2],10))/255,this.b=Math.min(255,parseInt(color[3],10))/255,handleAlpha(color[5]),this;if(color=/^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(components))return this.r=Math.min(100,parseInt(color[1],10))/100,this.g=Math.min(100,parseInt(color[2],10))/100,this.b=Math.min(100,parseInt(color[3],10))/100,handleAlpha(color[5]),this;break;case"hsl":case"hsla":if(color=/^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(components)){var h=parseFloat(color[1])/360,s=parseInt(color[2],10)/100,l=parseInt(color[3],10)/100;return handleAlpha(color[5]),this.setHSL(h,s,l)}}}else if(m=/^\#([A-Fa-f0-9]+)$/.exec(style)){var hex=m[1],size=hex.length;if(3===size)return this.r=parseInt(hex.charAt(0)+hex.charAt(0),16)/255,this.g=parseInt(hex.charAt(1)+hex.charAt(1),16)/255,this.b=parseInt(hex.charAt(2)+hex.charAt(2),16)/255,this;if(6===size)return this.r=parseInt(hex.charAt(0)+hex.charAt(1),16)/255,this.g=parseInt(hex.charAt(2)+hex.charAt(3),16)/255,this.b=parseInt(hex.charAt(4)+hex.charAt(5),16)/255,this}if(style&&style.length>0){var hex=THREE.ColorKeywords[style];void 0!==hex?this.setHex(hex):console.warn("THREE.Color: Unknown color "+style)}return this},clone:function(){return new this.constructor(this.r,this.g,this.b)},copy:function(color){return this.r=color.r,this.g=color.g,this.b=color.b,this},copyGammaToLinear:function(color,gammaFactor){return void 0===gammaFactor&&(gammaFactor=2),this.r=Math.pow(color.r,gammaFactor),this.g=Math.pow(color.g,gammaFactor),this.b=Math.pow(color.b,gammaFactor),this},copyLinearToGamma:function(color,gammaFactor){void 0===gammaFactor&&(gammaFactor=2);var safeInverse=gammaFactor>0?1/gammaFactor:1;return this.r=Math.pow(color.r,safeInverse),this.g=Math.pow(color.g,safeInverse),this.b=Math.pow(color.b,safeInverse),this},convertGammaToLinear:function(){var r=this.r,g=this.g,b=this.b;return this.r=r*r,this.g=g*g,this.b=b*b,this},convertLinearToGamma:function(){return this.r=Math.sqrt(this.r),this.g=Math.sqrt(this.g),this.b=Math.sqrt(this.b),this},getHex:function(){return 255*this.r<<16^255*this.g<<8^255*this.b<<0},getHexString:function(){return("000000"+this.getHex().toString(16)).slice(-6)},getHSL:function(optionalTarget){var hue,saturation,hsl=optionalTarget||{h:0,s:0,l:0},r=this.r,g=this.g,b=this.b,max=Math.max(r,g,b),min=Math.min(r,g,b),lightness=(min+max)/2;if(min===max)hue=0,saturation=0;else{var delta=max-min;switch(saturation=.5>=lightness?delta/(max+min):delta/(2-max-min),max){case r:hue=(g-b)/delta+(b>g?6:0);break;case g:hue=(b-r)/delta+2;break;case b:hue=(r-g)/delta+4}hue/=6}return hsl.h=hue,hsl.s=saturation,hsl.l=lightness,hsl},getStyle:function(){return"rgb("+(255*this.r|0)+","+(255*this.g|0)+","+(255*this.b|0)+")"},offsetHSL:function(h,s,l){var hsl=this.getHSL();return hsl.h+=h,hsl.s+=s,hsl.l+=l,this.setHSL(hsl.h,hsl.s,hsl.l),this},add:function(color){return this.r+=color.r,this.g+=color.g,this.b+=color.b,this},addColors:function(color1,color2){return this.r=color1.r+color2.r,this.g=color1.g+color2.g,this.b=color1.b+color2.b,this},addScalar:function(s){return this.r+=s,this.g+=s,this.b+=s,this},multiply:function(color){return this.r*=color.r,this.g*=color.g,this.b*=color.b,this},multiplyScalar:function(s){return this.r*=s,this.g*=s,this.b*=s,this},lerp:function(color,alpha){return this.r+=(color.r-this.r)*alpha,this.g+=(color.g-this.g)*alpha,this.b+=(color.b-this.b)*alpha,this},equals:function(c){return c.r===this.r&&c.g===this.g&&c.b===this.b},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.r=array[offset],this.g=array[offset+1],this.b=array[offset+2],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.r,array[offset+1]=this.g,array[offset+2]=this.b,array}},THREE.ColorKeywords={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},THREE.Quaternion=function(x,y,z,w){this._x=x||0,this._y=y||0,this._z=z||0,this._w=void 0!==w?w:1},THREE.Quaternion.prototype={constructor:THREE.Quaternion,get x(){return this._x},set x(value){this._x=value,this.onChangeCallback()},get y(){return this._y},set y(value){this._y=value,this.onChangeCallback()},get z(){return this._z},set z(value){this._z=value,this.onChangeCallback()},get w(){return this._w},set w(value){this._w=value,this.onChangeCallback()},set:function(x,y,z,w){return this._x=x,this._y=y,this._z=z,this._w=w,this.onChangeCallback(),this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._w)},copy:function(quaternion){return this._x=quaternion.x,this._y=quaternion.y,this._z=quaternion.z,this._w=quaternion.w,this.onChangeCallback(),this},setFromEuler:function(euler,update){if(euler instanceof THREE.Euler==!1)throw new Error("THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var c1=Math.cos(euler._x/2),c2=Math.cos(euler._y/2),c3=Math.cos(euler._z/2),s1=Math.sin(euler._x/2),s2=Math.sin(euler._y/2),s3=Math.sin(euler._z/2),order=euler.order;return"XYZ"===order?(this._x=s1*c2*c3+c1*s2*s3,this._y=c1*s2*c3-s1*c2*s3,this._z=c1*c2*s3+s1*s2*c3,this._w=c1*c2*c3-s1*s2*s3):"YXZ"===order?(this._x=s1*c2*c3+c1*s2*s3,this._y=c1*s2*c3-s1*c2*s3,this._z=c1*c2*s3-s1*s2*c3,this._w=c1*c2*c3+s1*s2*s3):"ZXY"===order?(this._x=s1*c2*c3-c1*s2*s3,this._y=c1*s2*c3+s1*c2*s3,this._z=c1*c2*s3+s1*s2*c3,this._w=c1*c2*c3-s1*s2*s3):"ZYX"===order?(this._x=s1*c2*c3-c1*s2*s3,this._y=c1*s2*c3+s1*c2*s3,this._z=c1*c2*s3-s1*s2*c3,this._w=c1*c2*c3+s1*s2*s3):"YZX"===order?(this._x=s1*c2*c3+c1*s2*s3,this._y=c1*s2*c3+s1*c2*s3,this._z=c1*c2*s3-s1*s2*c3,this._w=c1*c2*c3-s1*s2*s3):"XZY"===order&&(this._x=s1*c2*c3-c1*s2*s3,this._y=c1*s2*c3-s1*c2*s3,this._z=c1*c2*s3+s1*s2*c3,this._w=c1*c2*c3+s1*s2*s3),update!==!1&&this.onChangeCallback(),this},setFromAxisAngle:function(axis,angle){var halfAngle=angle/2,s=Math.sin(halfAngle);return this._x=axis.x*s,this._y=axis.y*s,this._z=axis.z*s,this._w=Math.cos(halfAngle),this.onChangeCallback(),this},setFromRotationMatrix:function(m){var s,te=m.elements,m11=te[0],m12=te[4],m13=te[8],m21=te[1],m22=te[5],m23=te[9],m31=te[2],m32=te[6],m33=te[10],trace=m11+m22+m33;return trace>0?(s=.5/Math.sqrt(trace+1),this._w=.25/s,this._x=(m32-m23)*s,this._y=(m13-m31)*s,this._z=(m21-m12)*s):m11>m22&&m11>m33?(s=2*Math.sqrt(1+m11-m22-m33),this._w=(m32-m23)/s,this._x=.25*s,this._y=(m12+m21)/s,this._z=(m13+m31)/s):m22>m33?(s=2*Math.sqrt(1+m22-m11-m33),this._w=(m13-m31)/s,this._x=(m12+m21)/s,this._y=.25*s,this._z=(m23+m32)/s):(s=2*Math.sqrt(1+m33-m11-m22),this._w=(m21-m12)/s,this._x=(m13+m31)/s,this._y=(m23+m32)/s,this._z=.25*s),this.onChangeCallback(),this},setFromUnitVectors:function(){var v1,r,EPS=1e-6;return function(vFrom,vTo){return void 0===v1&&(v1=new THREE.Vector3),r=vFrom.dot(vTo)+1,EPS>r?(r=0,Math.abs(vFrom.x)>Math.abs(vFrom.z)?v1.set(-vFrom.y,vFrom.x,0):v1.set(0,-vFrom.z,vFrom.y)):v1.crossVectors(vFrom,vTo),this._x=v1.x,this._y=v1.y,this._z=v1.z,this._w=r,this.normalize(),this}}(),inverse:function(){return this.conjugate().normalize(),this},conjugate:function(){return this._x*=-1,this._y*=-1,this._z*=-1,this.onChangeCallback(),this},dot:function(v){return this._x*v._x+this._y*v._y+this._z*v._z+this._w*v._w},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var l=this.length();return 0===l?(this._x=0,this._y=0,this._z=0,this._w=1):(l=1/l,this._x=this._x*l,this._y=this._y*l,this._z=this._z*l,this._w=this._w*l),this.onChangeCallback(),this},multiply:function(q,p){return void 0!==p?(console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(q,p)):this.multiplyQuaternions(this,q)},multiplyQuaternions:function(a,b){var qax=a._x,qay=a._y,qaz=a._z,qaw=a._w,qbx=b._x,qby=b._y,qbz=b._z,qbw=b._w;return this._x=qax*qbw+qaw*qbx+qay*qbz-qaz*qby,this._y=qay*qbw+qaw*qby+qaz*qbx-qax*qbz,this._z=qaz*qbw+qaw*qbz+qax*qby-qay*qbx,this._w=qaw*qbw-qax*qbx-qay*qby-qaz*qbz,this.onChangeCallback(),this},multiplyVector3:function(vector){return console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead."),vector.applyQuaternion(this)},slerp:function(qb,t){if(0===t)return this;if(1===t)return this.copy(qb);var x=this._x,y=this._y,z=this._z,w=this._w,cosHalfTheta=w*qb._w+x*qb._x+y*qb._y+z*qb._z;if(0>cosHalfTheta?(this._w=-qb._w,this._x=-qb._x,this._y=-qb._y,this._z=-qb._z,cosHalfTheta=-cosHalfTheta):this.copy(qb),cosHalfTheta>=1)return this._w=w,this._x=x,this._y=y,this._z=z,this;var halfTheta=Math.acos(cosHalfTheta),sinHalfTheta=Math.sqrt(1-cosHalfTheta*cosHalfTheta); +if(Math.abs(sinHalfTheta)<.001)return this._w=.5*(w+this._w),this._x=.5*(x+this._x),this._y=.5*(y+this._y),this._z=.5*(z+this._z),this;var ratioA=Math.sin((1-t)*halfTheta)/sinHalfTheta,ratioB=Math.sin(t*halfTheta)/sinHalfTheta;return this._w=w*ratioA+this._w*ratioB,this._x=x*ratioA+this._x*ratioB,this._y=y*ratioA+this._y*ratioB,this._z=z*ratioA+this._z*ratioB,this.onChangeCallback(),this},equals:function(quaternion){return quaternion._x===this._x&&quaternion._y===this._y&&quaternion._z===this._z&&quaternion._w===this._w},fromArray:function(array,offset){return void 0===offset&&(offset=0),this._x=array[offset],this._y=array[offset+1],this._z=array[offset+2],this._w=array[offset+3],this.onChangeCallback(),this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this._x,array[offset+1]=this._y,array[offset+2]=this._z,array[offset+3]=this._w,array},onChange:function(callback){return this.onChangeCallback=callback,this},onChangeCallback:function(){}},THREE.Quaternion.slerp=function(qa,qb,qm,t){return qm.copy(qa).slerp(qb,t)},THREE.Vector2=function(x,y){this.x=x||0,this.y=y||0},THREE.Vector2.prototype={constructor:THREE.Vector2,get width(){return this.x},set width(value){this.x=value},get height(){return this.y},set height(value){this.y=value},set:function(x,y){return this.x=x,this.y=y,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+index)}},clone:function(){return new this.constructor(this.x,this.y)},copy:function(v){return this.x=v.x,this.y=v.y,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this)},addScalar:function(s){return this.x+=s,this.y+=s,this},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this},addScaledVector:function(v,s){return this.x+=v.x*s,this.y+=v.y*s,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this)},subScalar:function(s){return this.x-=s,this.y-=s,this},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this},multiply:function(v){return this.x*=v.x,this.y*=v.y,this},multiplyScalar:function(scalar){return isFinite(scalar)?(this.x*=scalar,this.y*=scalar):(this.x=0,this.y=0),this},divide:function(v){return this.x/=v.x,this.y/=v.y,this},divideScalar:function(scalar){return this.multiplyScalar(1/scalar)},min:function(v){return this.x=Math.min(this.x,v.x),this.y=Math.min(this.y,v.y),this},max:function(v){return this.x=Math.max(this.x,v.x),this.y=Math.max(this.y,v.y),this},clamp:function(min,max){return this.x=Math.max(min.x,Math.min(max.x,this.x)),this.y=Math.max(min.y,Math.min(max.y,this.y)),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector2,max=new THREE.Vector2),min.set(minVal,minVal),max.set(maxVal,maxVal),this.clamp(min,max)}}(),clampLength:function(min,max){var length=this.length();return this.multiplyScalar(Math.max(min,Math.min(max,length))/length),this},floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this},negate:function(){return this.x=-this.x,this.y=-this.y,this},dot:function(v){return this.x*v.x+this.y*v.y},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)},normalize:function(){return this.divideScalar(this.length())},distanceTo:function(v){return Math.sqrt(this.distanceToSquared(v))},distanceToSquared:function(v){var dx=this.x-v.x,dy=this.y-v.y;return dx*dx+dy*dy},setLength:function(length){return this.multiplyScalar(length/this.length())},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this},lerpVectors:function(v1,v2,alpha){return this.subVectors(v2,v1).multiplyScalar(alpha).add(v1),this},equals:function(v){return v.x===this.x&&v.y===this.y},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this},rotateAround:function(center,angle){var c=Math.cos(angle),s=Math.sin(angle),x=this.x-center.x,y=this.y-center.y;return this.x=x*c-y*s+center.x,this.y=x*s+y*c+center.y,this}},THREE.Vector3=function(x,y,z){this.x=x||0,this.y=y||0,this.z=z||0},THREE.Vector3.prototype={constructor:THREE.Vector3,set:function(x,y,z){return this.x=x,this.y=y,this.z=z,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setZ:function(z){return this.z=z,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;case 2:this.z=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("index is out of range: "+index)}},clone:function(){return new this.constructor(this.x,this.y,this.z)},copy:function(v){return this.x=v.x,this.y=v.y,this.z=v.z,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this.z+=v.z,this)},addScalar:function(s){return this.x+=s,this.y+=s,this.z+=s,this},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this.z=a.z+b.z,this},addScaledVector:function(v,s){return this.x+=v.x*s,this.y+=v.y*s,this.z+=v.z*s,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this.z-=v.z,this)},subScalar:function(s){return this.x-=s,this.y-=s,this.z-=s,this},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this.z=a.z-b.z,this},multiply:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(v,w)):(this.x*=v.x,this.y*=v.y,this.z*=v.z,this)},multiplyScalar:function(scalar){return isFinite(scalar)?(this.x*=scalar,this.y*=scalar,this.z*=scalar):(this.x=0,this.y=0,this.z=0),this},multiplyVectors:function(a,b){return this.x=a.x*b.x,this.y=a.y*b.y,this.z=a.z*b.z,this},applyEuler:function(){var quaternion;return function(euler){return euler instanceof THREE.Euler==!1&&console.error("THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order."),void 0===quaternion&&(quaternion=new THREE.Quaternion),this.applyQuaternion(quaternion.setFromEuler(euler)),this}}(),applyAxisAngle:function(){var quaternion;return function(axis,angle){return void 0===quaternion&&(quaternion=new THREE.Quaternion),this.applyQuaternion(quaternion.setFromAxisAngle(axis,angle)),this}}(),applyMatrix3:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[3]*y+e[6]*z,this.y=e[1]*x+e[4]*y+e[7]*z,this.z=e[2]*x+e[5]*y+e[8]*z,this},applyMatrix4:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z+e[12],this.y=e[1]*x+e[5]*y+e[9]*z+e[13],this.z=e[2]*x+e[6]*y+e[10]*z+e[14],this},applyProjection:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements,d=1/(e[3]*x+e[7]*y+e[11]*z+e[15]);return this.x=(e[0]*x+e[4]*y+e[8]*z+e[12])*d,this.y=(e[1]*x+e[5]*y+e[9]*z+e[13])*d,this.z=(e[2]*x+e[6]*y+e[10]*z+e[14])*d,this},applyQuaternion:function(q){var x=this.x,y=this.y,z=this.z,qx=q.x,qy=q.y,qz=q.z,qw=q.w,ix=qw*x+qy*z-qz*y,iy=qw*y+qz*x-qx*z,iz=qw*z+qx*y-qy*x,iw=-qx*x-qy*y-qz*z;return this.x=ix*qw+iw*-qx+iy*-qz-iz*-qy,this.y=iy*qw+iw*-qy+iz*-qx-ix*-qz,this.z=iz*qw+iw*-qz+ix*-qy-iy*-qx,this},project:function(){var matrix;return function(camera){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.multiplyMatrices(camera.projectionMatrix,matrix.getInverse(camera.matrixWorld)),this.applyProjection(matrix)}}(),unproject:function(){var matrix;return function(camera){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.multiplyMatrices(camera.matrixWorld,matrix.getInverse(camera.projectionMatrix)),this.applyProjection(matrix)}}(),transformDirection:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z,this.y=e[1]*x+e[5]*y+e[9]*z,this.z=e[2]*x+e[6]*y+e[10]*z,this.normalize(),this},divide:function(v){return this.x/=v.x,this.y/=v.y,this.z/=v.z,this},divideScalar:function(scalar){return this.multiplyScalar(1/scalar)},min:function(v){return this.x=Math.min(this.x,v.x),this.y=Math.min(this.y,v.y),this.z=Math.min(this.z,v.z),this},max:function(v){return this.x=Math.max(this.x,v.x),this.y=Math.max(this.y,v.y),this.z=Math.max(this.z,v.z),this},clamp:function(min,max){return this.x=Math.max(min.x,Math.min(max.x,this.x)),this.y=Math.max(min.y,Math.min(max.y,this.y)),this.z=Math.max(min.z,Math.min(max.z,this.z)),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector3,max=new THREE.Vector3),min.set(minVal,minVal,minVal),max.set(maxVal,maxVal,maxVal),this.clamp(min,max)}}(),clampLength:function(min,max){var length=this.length();return this.multiplyScalar(Math.max(min,Math.min(max,length))/length),this},floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this},negate:function(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this},dot:function(v){return this.x*v.x+this.y*v.y+this.z*v.z},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length())},setLength:function(length){return this.multiplyScalar(length/this.length())},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this.z+=(v.z-this.z)*alpha,this},lerpVectors:function(v1,v2,alpha){return this.subVectors(v2,v1).multiplyScalar(alpha).add(v1),this},cross:function(v,w){if(void 0!==w)return console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(v,w);var x=this.x,y=this.y,z=this.z;return this.x=y*v.z-z*v.y,this.y=z*v.x-x*v.z,this.z=x*v.y-y*v.x,this},crossVectors:function(a,b){var ax=a.x,ay=a.y,az=a.z,bx=b.x,by=b.y,bz=b.z;return this.x=ay*bz-az*by,this.y=az*bx-ax*bz,this.z=ax*by-ay*bx,this},projectOnVector:function(){var v1,dot;return function(vector){return void 0===v1&&(v1=new THREE.Vector3),v1.copy(vector).normalize(),dot=this.dot(v1),this.copy(v1).multiplyScalar(dot)}}(),projectOnPlane:function(){var v1;return function(planeNormal){return void 0===v1&&(v1=new THREE.Vector3),v1.copy(this).projectOnVector(planeNormal),this.sub(v1)}}(),reflect:function(){var v1;return function(normal){return void 0===v1&&(v1=new THREE.Vector3),this.sub(v1.copy(normal).multiplyScalar(2*this.dot(normal)))}}(),angleTo:function(v){var theta=this.dot(v)/(this.length()*v.length());return Math.acos(THREE.Math.clamp(theta,-1,1))},distanceTo:function(v){return Math.sqrt(this.distanceToSquared(v))},distanceToSquared:function(v){var dx=this.x-v.x,dy=this.y-v.y,dz=this.z-v.z;return dx*dx+dy*dy+dz*dz},setEulerFromRotationMatrix:function(m,order){console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.")},setEulerFromQuaternion:function(q,order){console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.")},getPositionFromMatrix:function(m){return console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()."),this.setFromMatrixPosition(m)},getScaleFromMatrix:function(m){return console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()."),this.setFromMatrixScale(m)},getColumnFromMatrix:function(index,matrix){return console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()."),this.setFromMatrixColumn(index,matrix)},setFromMatrixPosition:function(m){return this.x=m.elements[12],this.y=m.elements[13],this.z=m.elements[14],this},setFromMatrixScale:function(m){var sx=this.set(m.elements[0],m.elements[1],m.elements[2]).length(),sy=this.set(m.elements[4],m.elements[5],m.elements[6]).length(),sz=this.set(m.elements[8],m.elements[9],m.elements[10]).length();return this.x=sx,this.y=sy,this.z=sz,this},setFromMatrixColumn:function(index,matrix){var offset=4*index,me=matrix.elements;return this.x=me[offset],this.y=me[offset+1],this.z=me[offset+2],this},equals:function(v){return v.x===this.x&&v.y===this.y&&v.z===this.z},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this.z=array[offset+2],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array[offset+2]=this.z,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this.z=attribute.array[index+2],this}},THREE.Vector4=function(x,y,z,w){this.x=x||0,this.y=y||0,this.z=z||0,this.w=void 0!==w?w:1},THREE.Vector4.prototype={constructor:THREE.Vector4,set:function(x,y,z,w){return this.x=x,this.y=y,this.z=z,this.w=w,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setZ:function(z){return this.z=z,this},setW:function(w){return this.w=w,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;case 2:this.z=value;break;case 3:this.w=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("index is out of range: "+index)}},clone:function(){return new this.constructor(this.x,this.y,this.z,this.w)},copy:function(v){return this.x=v.x,this.y=v.y,this.z=v.z,this.w=void 0!==v.w?v.w:1,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this.z+=v.z,this.w+=v.w,this)},addScalar:function(s){return this.x+=s,this.y+=s,this.z+=s,this.w+=s,this},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this.z=a.z+b.z,this.w=a.w+b.w,this},addScaledVector:function(v,s){return this.x+=v.x*s,this.y+=v.y*s,this.z+=v.z*s,this.w+=v.w*s,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this.z-=v.z,this.w-=v.w,this)},subScalar:function(s){return this.x-=s,this.y-=s,this.z-=s,this.w-=s,this},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this.z=a.z-b.z,this.w=a.w-b.w,this},multiplyScalar:function(scalar){return isFinite(scalar)?(this.x*=scalar,this.y*=scalar,this.z*=scalar,this.w*=scalar):(this.x=0,this.y=0,this.z=0,this.w=0),this},applyMatrix4:function(m){var x=this.x,y=this.y,z=this.z,w=this.w,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z+e[12]*w,this.y=e[1]*x+e[5]*y+e[9]*z+e[13]*w,this.z=e[2]*x+e[6]*y+e[10]*z+e[14]*w,this.w=e[3]*x+e[7]*y+e[11]*z+e[15]*w,this},divideScalar:function(scalar){return this.multiplyScalar(1/scalar)},setAxisAngleFromQuaternion:function(q){this.w=2*Math.acos(q.w);var s=Math.sqrt(1-q.w*q.w);return 1e-4>s?(this.x=1,this.y=0,this.z=0):(this.x=q.x/s,this.y=q.y/s,this.z=q.z/s),this},setAxisAngleFromRotationMatrix:function(m){var angle,x,y,z,epsilon=.01,epsilon2=.1,te=m.elements,m11=te[0],m12=te[4],m13=te[8],m21=te[1],m22=te[5],m23=te[9],m31=te[2],m32=te[6],m33=te[10];if(Math.abs(m12-m21)yy&&xx>zz?epsilon>xx?(x=0,y=.707106781,z=.707106781):(x=Math.sqrt(xx),y=xy/x,z=xz/x):yy>zz?epsilon>yy?(x=.707106781,y=0,z=.707106781):(y=Math.sqrt(yy),x=xy/y,z=yz/y):epsilon>zz?(x=.707106781,y=.707106781,z=0):(z=Math.sqrt(zz),x=xz/z,y=yz/z),this.set(x,y,z,angle),this}var s=Math.sqrt((m32-m23)*(m32-m23)+(m13-m31)*(m13-m31)+(m21-m12)*(m21-m12));return Math.abs(s)<.001&&(s=1),this.x=(m32-m23)/s,this.y=(m13-m31)/s,this.z=(m21-m12)/s,this.w=Math.acos((m11+m22+m33-1)/2),this},min:function(v){return this.x=Math.min(this.x,v.x),this.y=Math.min(this.y,v.y),this.z=Math.min(this.z,v.z),this.w=Math.min(this.w,v.w),this},max:function(v){return this.x=Math.max(this.x,v.x),this.y=Math.max(this.y,v.y),this.z=Math.max(this.z,v.z),this.w=Math.max(this.w,v.w),this},clamp:function(min,max){return this.x=Math.max(min.x,Math.min(max.x,this.x)),this.y=Math.max(min.y,Math.min(max.y,this.y)),this.z=Math.max(min.z,Math.min(max.z,this.z)),this.w=Math.max(min.w,Math.min(max.w,this.w)),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector4,max=new THREE.Vector4),min.set(minVal,minVal,minVal,minVal),max.set(maxVal,maxVal,maxVal,maxVal),this.clamp(min,max)}}(),floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this.w=Math.floor(this.w),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this.w=Math.ceil(this.w),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this.w=Math.round(this.w),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this.w=this.w<0?Math.ceil(this.w):Math.floor(this.w),this},negate:function(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this.w=-this.w,this},dot:function(v){return this.x*v.x+this.y*v.y+this.z*v.z+this.w*v.w},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)},normalize:function(){return this.divideScalar(this.length())},setLength:function(length){return this.multiplyScalar(length/this.length())},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this.z+=(v.z-this.z)*alpha,this.w+=(v.w-this.w)*alpha,this},lerpVectors:function(v1,v2,alpha){return this.subVectors(v2,v1).multiplyScalar(alpha).add(v1),this},equals:function(v){return v.x===this.x&&v.y===this.y&&v.z===this.z&&v.w===this.w},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this.z=array[offset+2],this.w=array[offset+3],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array[offset+2]=this.z,array[offset+3]=this.w,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this.z=attribute.array[index+2],this.w=attribute.array[index+3],this}},THREE.Euler=function(x,y,z,order){this._x=x||0,this._y=y||0,this._z=z||0,this._order=order||THREE.Euler.DefaultOrder},THREE.Euler.RotationOrders=["XYZ","YZX","ZXY","XZY","YXZ","ZYX"],THREE.Euler.DefaultOrder="XYZ",THREE.Euler.prototype={constructor:THREE.Euler,get x(){return this._x},set x(value){this._x=value,this.onChangeCallback()},get y(){return this._y},set y(value){this._y=value,this.onChangeCallback()},get z(){return this._z},set z(value){this._z=value,this.onChangeCallback()},get order(){return this._order},set order(value){this._order=value,this.onChangeCallback()},set:function(x,y,z,order){return this._x=x,this._y=y,this._z=z,this._order=order||this._order,this.onChangeCallback(),this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._order)},copy:function(euler){return this._x=euler._x,this._y=euler._y,this._z=euler._z,this._order=euler._order,this.onChangeCallback(),this},setFromRotationMatrix:function(m,order,update){var clamp=THREE.Math.clamp,te=m.elements,m11=te[0],m12=te[4],m13=te[8],m21=te[1],m22=te[5],m23=te[9],m31=te[2],m32=te[6],m33=te[10];return order=order||this._order,"XYZ"===order?(this._y=Math.asin(clamp(m13,-1,1)),Math.abs(m13)<.99999?(this._x=Math.atan2(-m23,m33),this._z=Math.atan2(-m12,m11)):(this._x=Math.atan2(m32,m22),this._z=0)):"YXZ"===order?(this._x=Math.asin(-clamp(m23,-1,1)),Math.abs(m23)<.99999?(this._y=Math.atan2(m13,m33),this._z=Math.atan2(m21,m22)):(this._y=Math.atan2(-m31,m11),this._z=0)):"ZXY"===order?(this._x=Math.asin(clamp(m32,-1,1)),Math.abs(m32)<.99999?(this._y=Math.atan2(-m31,m33),this._z=Math.atan2(-m12,m22)):(this._y=0,this._z=Math.atan2(m21,m11))):"ZYX"===order?(this._y=Math.asin(-clamp(m31,-1,1)),Math.abs(m31)<.99999?(this._x=Math.atan2(m32,m33),this._z=Math.atan2(m21,m11)):(this._x=0,this._z=Math.atan2(-m12,m22))):"YZX"===order?(this._z=Math.asin(clamp(m21,-1,1)),Math.abs(m21)<.99999?(this._x=Math.atan2(-m23,m22),this._y=Math.atan2(-m31,m11)):(this._x=0,this._y=Math.atan2(m13,m33))):"XZY"===order?(this._z=Math.asin(-clamp(m12,-1,1)),Math.abs(m12)<.99999?(this._x=Math.atan2(m32,m22),this._y=Math.atan2(m13,m11)):(this._x=Math.atan2(-m23,m33),this._y=0)):console.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+order),this._order=order,update!==!1&&this.onChangeCallback(),this},setFromQuaternion:function(){var matrix;return function(q,order,update){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.makeRotationFromQuaternion(q),this.setFromRotationMatrix(matrix,order,update),this}}(),setFromVector3:function(v,order){return this.set(v.x,v.y,v.z,order||this._order)},reorder:function(){var q=new THREE.Quaternion;return function(newOrder){q.setFromEuler(this),this.setFromQuaternion(q,newOrder)}}(),equals:function(euler){return euler._x===this._x&&euler._y===this._y&&euler._z===this._z&&euler._order===this._order},fromArray:function(array){return this._x=array[0],this._y=array[1],this._z=array[2],void 0!==array[3]&&(this._order=array[3]),this.onChangeCallback(),this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this._x,array[offset+1]=this._y,array[offset+2]=this._z,array[offset+3]=this._order,array},toVector3:function(optionalResult){return optionalResult?optionalResult.set(this._x,this._y,this._z):new THREE.Vector3(this._x,this._y,this._z)},onChange:function(callback){return this.onChangeCallback=callback,this},onChangeCallback:function(){}},THREE.Line3=function(start,end){this.start=void 0!==start?start:new THREE.Vector3,this.end=void 0!==end?end:new THREE.Vector3},THREE.Line3.prototype={constructor:THREE.Line3,set:function(start,end){return this.start.copy(start),this.end.copy(end),this},clone:function(){return(new this.constructor).copy(this)},copy:function(line){return this.start.copy(line.start),this.end.copy(line.end),this},center:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.addVectors(this.start,this.end).multiplyScalar(.5)},delta:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.subVectors(this.end,this.start)},distanceSq:function(){return this.start.distanceToSquared(this.end)},distance:function(){return this.start.distanceTo(this.end)},at:function(t,optionalTarget){var result=optionalTarget||new THREE.Vector3;return this.delta(result).multiplyScalar(t).add(this.start)},closestPointToPointParameter:function(){var startP=new THREE.Vector3,startEnd=new THREE.Vector3;return function(point,clampToLine){startP.subVectors(point,this.start),startEnd.subVectors(this.end,this.start);var startEnd2=startEnd.dot(startEnd),startEnd_startP=startEnd.dot(startP),t=startEnd_startP/startEnd2;return clampToLine&&(t=THREE.Math.clamp(t,0,1)),t}}(),closestPointToPoint:function(point,clampToLine,optionalTarget){var t=this.closestPointToPointParameter(point,clampToLine),result=optionalTarget||new THREE.Vector3;return this.delta(result).multiplyScalar(t).add(this.start)},applyMatrix4:function(matrix){return this.start.applyMatrix4(matrix),this.end.applyMatrix4(matrix),this},equals:function(line){return line.start.equals(this.start)&&line.end.equals(this.end)}},THREE.Box2=function(min,max){this.min=void 0!==min?min:new THREE.Vector2(1/0,1/0),this.max=void 0!==max?max:new THREE.Vector2(-(1/0),-(1/0))},THREE.Box2.prototype={constructor:THREE.Box2,set:function(min,max){return this.min.copy(min),this.max.copy(max),this},setFromPoints:function(points){this.makeEmpty();for(var i=0,il=points.length;il>i;i++)this.expandByPoint(points[i]);return this},setFromCenterAndSize:function(){var v1=new THREE.Vector2;return function(center,size){var halfSize=v1.copy(size).multiplyScalar(.5);return this.min.copy(center).sub(halfSize),this.max.copy(center).add(halfSize),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(box){return this.min.copy(box.min),this.max.copy(box.max),this},makeEmpty:function(){return this.min.x=this.min.y=1/0,this.max.x=this.max.y=-(1/0),this},empty:function(){return this.max.xthis.max.x||point.ythis.max.y)},containsBox:function(box){return this.min.x<=box.min.x&&box.max.x<=this.max.x&&this.min.y<=box.min.y&&box.max.y<=this.max.y},getParameter:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector2;return result.set((point.x-this.min.x)/(this.max.x-this.min.x),(point.y-this.min.y)/(this.max.y-this.min.y))},isIntersectionBox:function(box){return!(box.max.xthis.max.x||box.max.ythis.max.y)},clampPoint:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector2;return result.copy(point).clamp(this.min,this.max)},distanceToPoint:function(){var v1=new THREE.Vector2;return function(point){var clampedPoint=v1.copy(point).clamp(this.min,this.max);return clampedPoint.sub(point).length()}}(),intersect:function(box){return this.min.max(box.min),this.max.min(box.max),this},union:function(box){return this.min.min(box.min),this.max.max(box.max),this},translate:function(offset){return this.min.add(offset),this.max.add(offset),this},equals:function(box){return box.min.equals(this.min)&&box.max.equals(this.max)}},THREE.Box3=function(min,max){this.min=void 0!==min?min:new THREE.Vector3(1/0,1/0,1/0),this.max=void 0!==max?max:new THREE.Vector3(-(1/0),-(1/0),-(1/0))},THREE.Box3.prototype={constructor:THREE.Box3,set:function(min,max){return this.min.copy(min),this.max.copy(max),this},setFromPoints:function(points){this.makeEmpty();for(var i=0,il=points.length;il>i;i++)this.expandByPoint(points[i]);return this},setFromCenterAndSize:function(){var v1=new THREE.Vector3;return function(center,size){var halfSize=v1.copy(size).multiplyScalar(.5);return this.min.copy(center).sub(halfSize),this.max.copy(center).add(halfSize),this}}(),setFromObject:function(){var v1=new THREE.Vector3;return function(object){var scope=this;return object.updateMatrixWorld(!0),this.makeEmpty(),object.traverse(function(node){var geometry=node.geometry;if(void 0!==geometry)if(geometry instanceof THREE.Geometry)for(var vertices=geometry.vertices,i=0,il=vertices.length;il>i;i++)v1.copy(vertices[i]),v1.applyMatrix4(node.matrixWorld),scope.expandByPoint(v1);else if(geometry instanceof THREE.BufferGeometry&&void 0!==geometry.attributes.position)for(var positions=geometry.attributes.position.array,i=0,il=positions.length;il>i;i+=3)v1.set(positions[i],positions[i+1],positions[i+2]),v1.applyMatrix4(node.matrixWorld),scope.expandByPoint(v1)}),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(box){return this.min.copy(box.min),this.max.copy(box.max),this},makeEmpty:function(){return this.min.x=this.min.y=this.min.z=1/0,this.max.x=this.max.y=this.max.z=-(1/0),this},empty:function(){return this.max.xthis.max.x||point.ythis.max.y||point.zthis.max.z)},containsBox:function(box){return this.min.x<=box.min.x&&box.max.x<=this.max.x&&this.min.y<=box.min.y&&box.max.y<=this.max.y&&this.min.z<=box.min.z&&box.max.z<=this.max.z},getParameter:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.set((point.x-this.min.x)/(this.max.x-this.min.x),(point.y-this.min.y)/(this.max.y-this.min.y),(point.z-this.min.z)/(this.max.z-this.min.z))},isIntersectionBox:function(box){return!(box.max.xthis.max.x||box.max.ythis.max.y||box.max.zthis.max.z)},clampPoint:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.copy(point).clamp(this.min,this.max)},distanceToPoint:function(){var v1=new THREE.Vector3;return function(point){ +var clampedPoint=v1.copy(point).clamp(this.min,this.max);return clampedPoint.sub(point).length()}}(),getBoundingSphere:function(){var v1=new THREE.Vector3;return function(optionalTarget){var result=optionalTarget||new THREE.Sphere;return result.center=this.center(),result.radius=.5*this.size(v1).length(),result}}(),intersect:function(box){return this.min.max(box.min),this.max.min(box.max),this},union:function(box){return this.min.min(box.min),this.max.max(box.max),this},applyMatrix4:function(){var points=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3];return function(matrix){return points[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(matrix),points[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(matrix),points[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(matrix),points[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(matrix),points[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(matrix),points[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(matrix),points[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(matrix),points[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(matrix),this.makeEmpty(),this.setFromPoints(points),this}}(),translate:function(offset){return this.min.add(offset),this.max.add(offset),this},equals:function(box){return box.min.equals(this.min)&&box.max.equals(this.max)}},THREE.Matrix3=function(){this.elements=new Float32Array([1,0,0,0,1,0,0,0,1]),arguments.length>0&&console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.")},THREE.Matrix3.prototype={constructor:THREE.Matrix3,set:function(n11,n12,n13,n21,n22,n23,n31,n32,n33){var te=this.elements;return te[0]=n11,te[3]=n12,te[6]=n13,te[1]=n21,te[4]=n22,te[7]=n23,te[2]=n31,te[5]=n32,te[8]=n33,this},identity:function(){return this.set(1,0,0,0,1,0,0,0,1),this},clone:function(){return(new this.constructor).fromArray(this.elements)},copy:function(m){var me=m.elements;return this.set(me[0],me[3],me[6],me[1],me[4],me[7],me[2],me[5],me[8]),this},multiplyVector3:function(vector){return console.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead."),vector.applyMatrix3(this)},multiplyVector3Array:function(a){return console.warn("THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead."),this.applyToVector3Array(a)},applyToVector3Array:function(){var v1;return function(array,offset,length){void 0===v1&&(v1=new THREE.Vector3),void 0===offset&&(offset=0),void 0===length&&(length=array.length);for(var i=0,j=offset;length>i;i+=3,j+=3)v1.fromArray(array,j),v1.applyMatrix3(this),v1.toArray(array,j);return array}}(),applyToBuffer:function(){var v1;return function(buffer,offset,length){void 0===v1&&(v1=new THREE.Vector3),void 0===offset&&(offset=0),void 0===length&&(length=buffer.length/buffer.itemSize);for(var i=0,j=offset;length>i;i++,j++)v1.x=buffer.getX(j),v1.y=buffer.getY(j),v1.z=buffer.getZ(j),v1.applyMatrix3(this),buffer.setXYZ(v1.x,v1.y,v1.z);return buffer}}(),multiplyScalar:function(s){var te=this.elements;return te[0]*=s,te[3]*=s,te[6]*=s,te[1]*=s,te[4]*=s,te[7]*=s,te[2]*=s,te[5]*=s,te[8]*=s,this},determinant:function(){var te=this.elements,a=te[0],b=te[1],c=te[2],d=te[3],e=te[4],f=te[5],g=te[6],h=te[7],i=te[8];return a*e*i-a*f*h-b*d*i+b*f*g+c*d*h-c*e*g},getInverse:function(matrix,throwOnInvertible){var me=matrix.elements,te=this.elements;te[0]=me[10]*me[5]-me[6]*me[9],te[1]=-me[10]*me[1]+me[2]*me[9],te[2]=me[6]*me[1]-me[2]*me[5],te[3]=-me[10]*me[4]+me[6]*me[8],te[4]=me[10]*me[0]-me[2]*me[8],te[5]=-me[6]*me[0]+me[2]*me[4],te[6]=me[9]*me[4]-me[5]*me[8],te[7]=-me[9]*me[0]+me[1]*me[8],te[8]=me[5]*me[0]-me[1]*me[4];var det=me[0]*te[0]+me[1]*te[3]+me[2]*te[6];if(0===det){var msg="Matrix3.getInverse(): can't invert matrix, determinant is 0";if(throwOnInvertible)throw new Error(msg);return console.warn(msg),this.identity(),this}return this.multiplyScalar(1/det),this},transpose:function(){var tmp,m=this.elements;return tmp=m[1],m[1]=m[3],m[3]=tmp,tmp=m[2],m[2]=m[6],m[6]=tmp,tmp=m[5],m[5]=m[7],m[7]=tmp,this},flattenToArrayOffset:function(array,offset){var te=this.elements;return array[offset]=te[0],array[offset+1]=te[1],array[offset+2]=te[2],array[offset+3]=te[3],array[offset+4]=te[4],array[offset+5]=te[5],array[offset+6]=te[6],array[offset+7]=te[7],array[offset+8]=te[8],array},getNormalMatrix:function(m){return this.getInverse(m).transpose(),this},transposeIntoArray:function(r){var m=this.elements;return r[0]=m[0],r[1]=m[3],r[2]=m[6],r[3]=m[1],r[4]=m[4],r[5]=m[7],r[6]=m[2],r[7]=m[5],r[8]=m[8],this},fromArray:function(array){return this.elements.set(array),this},toArray:function(){var te=this.elements;return[te[0],te[1],te[2],te[3],te[4],te[5],te[6],te[7],te[8]]}},THREE.Matrix4=function(){this.elements=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),arguments.length>0&&console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.")},THREE.Matrix4.prototype={constructor:THREE.Matrix4,set:function(n11,n12,n13,n14,n21,n22,n23,n24,n31,n32,n33,n34,n41,n42,n43,n44){var te=this.elements;return te[0]=n11,te[4]=n12,te[8]=n13,te[12]=n14,te[1]=n21,te[5]=n22,te[9]=n23,te[13]=n24,te[2]=n31,te[6]=n32,te[10]=n33,te[14]=n34,te[3]=n41,te[7]=n42,te[11]=n43,te[15]=n44,this},identity:function(){return this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),this},clone:function(){return(new THREE.Matrix4).fromArray(this.elements)},copy:function(m){return this.elements.set(m.elements),this},extractPosition:function(m){return console.warn("THREE.Matrix4: .extractPosition() has been renamed to .copyPosition()."),this.copyPosition(m)},copyPosition:function(m){var te=this.elements,me=m.elements;return te[12]=me[12],te[13]=me[13],te[14]=me[14],this},extractBasis:function(xAxis,yAxis,zAxis){var te=this.elements;return xAxis.set(te[0],te[1],te[2]),yAxis.set(te[4],te[5],te[6]),zAxis.set(te[8],te[9],te[10]),this},makeBasis:function(xAxis,yAxis,zAxis){return this.set(xAxis.x,yAxis.x,zAxis.x,0,xAxis.y,yAxis.y,zAxis.y,0,xAxis.z,yAxis.z,zAxis.z,0,0,0,0,1),this},extractRotation:function(){var v1;return function(m){void 0===v1&&(v1=new THREE.Vector3);var te=this.elements,me=m.elements,scaleX=1/v1.set(me[0],me[1],me[2]).length(),scaleY=1/v1.set(me[4],me[5],me[6]).length(),scaleZ=1/v1.set(me[8],me[9],me[10]).length();return te[0]=me[0]*scaleX,te[1]=me[1]*scaleX,te[2]=me[2]*scaleX,te[4]=me[4]*scaleY,te[5]=me[5]*scaleY,te[6]=me[6]*scaleY,te[8]=me[8]*scaleZ,te[9]=me[9]*scaleZ,te[10]=me[10]*scaleZ,this}}(),makeRotationFromEuler:function(euler){euler instanceof THREE.Euler==!1&&console.error("THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var te=this.elements,x=euler.x,y=euler.y,z=euler.z,a=Math.cos(x),b=Math.sin(x),c=Math.cos(y),d=Math.sin(y),e=Math.cos(z),f=Math.sin(z);if("XYZ"===euler.order){var ae=a*e,af=a*f,be=b*e,bf=b*f;te[0]=c*e,te[4]=-c*f,te[8]=d,te[1]=af+be*d,te[5]=ae-bf*d,te[9]=-b*c,te[2]=bf-ae*d,te[6]=be+af*d,te[10]=a*c}else if("YXZ"===euler.order){var ce=c*e,cf=c*f,de=d*e,df=d*f;te[0]=ce+df*b,te[4]=de*b-cf,te[8]=a*d,te[1]=a*f,te[5]=a*e,te[9]=-b,te[2]=cf*b-de,te[6]=df+ce*b,te[10]=a*c}else if("ZXY"===euler.order){var ce=c*e,cf=c*f,de=d*e,df=d*f;te[0]=ce-df*b,te[4]=-a*f,te[8]=de+cf*b,te[1]=cf+de*b,te[5]=a*e,te[9]=df-ce*b,te[2]=-a*d,te[6]=b,te[10]=a*c}else if("ZYX"===euler.order){var ae=a*e,af=a*f,be=b*e,bf=b*f;te[0]=c*e,te[4]=be*d-af,te[8]=ae*d+bf,te[1]=c*f,te[5]=bf*d+ae,te[9]=af*d-be,te[2]=-d,te[6]=b*c,te[10]=a*c}else if("YZX"===euler.order){var ac=a*c,ad=a*d,bc=b*c,bd=b*d;te[0]=c*e,te[4]=bd-ac*f,te[8]=bc*f+ad,te[1]=f,te[5]=a*e,te[9]=-b*e,te[2]=-d*e,te[6]=ad*f+bc,te[10]=ac-bd*f}else if("XZY"===euler.order){var ac=a*c,ad=a*d,bc=b*c,bd=b*d;te[0]=c*e,te[4]=-f,te[8]=d*e,te[1]=ac*f+bd,te[5]=a*e,te[9]=ad*f-bc,te[2]=bc*f-ad,te[6]=b*e,te[10]=bd*f+ac}return te[3]=0,te[7]=0,te[11]=0,te[12]=0,te[13]=0,te[14]=0,te[15]=1,this},setRotationFromQuaternion:function(q){return console.warn("THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion()."),this.makeRotationFromQuaternion(q)},makeRotationFromQuaternion:function(q){var te=this.elements,x=q.x,y=q.y,z=q.z,w=q.w,x2=x+x,y2=y+y,z2=z+z,xx=x*x2,xy=x*y2,xz=x*z2,yy=y*y2,yz=y*z2,zz=z*z2,wx=w*x2,wy=w*y2,wz=w*z2;return te[0]=1-(yy+zz),te[4]=xy-wz,te[8]=xz+wy,te[1]=xy+wz,te[5]=1-(xx+zz),te[9]=yz-wx,te[2]=xz-wy,te[6]=yz+wx,te[10]=1-(xx+yy),te[3]=0,te[7]=0,te[11]=0,te[12]=0,te[13]=0,te[14]=0,te[15]=1,this},lookAt:function(){var x,y,z;return function(eye,target,up){void 0===x&&(x=new THREE.Vector3),void 0===y&&(y=new THREE.Vector3),void 0===z&&(z=new THREE.Vector3);var te=this.elements;return z.subVectors(eye,target).normalize(),0===z.lengthSq()&&(z.z=1),x.crossVectors(up,z).normalize(),0===x.lengthSq()&&(z.x+=1e-4,x.crossVectors(up,z).normalize()),y.crossVectors(z,x),te[0]=x.x,te[4]=y.x,te[8]=z.x,te[1]=x.y,te[5]=y.y,te[9]=z.y,te[2]=x.z,te[6]=y.z,te[10]=z.z,this}}(),multiply:function(m,n){return void 0!==n?(console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."),this.multiplyMatrices(m,n)):this.multiplyMatrices(this,m)},multiplyMatrices:function(a,b){var ae=a.elements,be=b.elements,te=this.elements,a11=ae[0],a12=ae[4],a13=ae[8],a14=ae[12],a21=ae[1],a22=ae[5],a23=ae[9],a24=ae[13],a31=ae[2],a32=ae[6],a33=ae[10],a34=ae[14],a41=ae[3],a42=ae[7],a43=ae[11],a44=ae[15],b11=be[0],b12=be[4],b13=be[8],b14=be[12],b21=be[1],b22=be[5],b23=be[9],b24=be[13],b31=be[2],b32=be[6],b33=be[10],b34=be[14],b41=be[3],b42=be[7],b43=be[11],b44=be[15];return te[0]=a11*b11+a12*b21+a13*b31+a14*b41,te[4]=a11*b12+a12*b22+a13*b32+a14*b42,te[8]=a11*b13+a12*b23+a13*b33+a14*b43,te[12]=a11*b14+a12*b24+a13*b34+a14*b44,te[1]=a21*b11+a22*b21+a23*b31+a24*b41,te[5]=a21*b12+a22*b22+a23*b32+a24*b42,te[9]=a21*b13+a22*b23+a23*b33+a24*b43,te[13]=a21*b14+a22*b24+a23*b34+a24*b44,te[2]=a31*b11+a32*b21+a33*b31+a34*b41,te[6]=a31*b12+a32*b22+a33*b32+a34*b42,te[10]=a31*b13+a32*b23+a33*b33+a34*b43,te[14]=a31*b14+a32*b24+a33*b34+a34*b44,te[3]=a41*b11+a42*b21+a43*b31+a44*b41,te[7]=a41*b12+a42*b22+a43*b32+a44*b42,te[11]=a41*b13+a42*b23+a43*b33+a44*b43,te[15]=a41*b14+a42*b24+a43*b34+a44*b44,this},multiplyToArray:function(a,b,r){var te=this.elements;return this.multiplyMatrices(a,b),r[0]=te[0],r[1]=te[1],r[2]=te[2],r[3]=te[3],r[4]=te[4],r[5]=te[5],r[6]=te[6],r[7]=te[7],r[8]=te[8],r[9]=te[9],r[10]=te[10],r[11]=te[11],r[12]=te[12],r[13]=te[13],r[14]=te[14],r[15]=te[15],this},multiplyScalar:function(s){var te=this.elements;return te[0]*=s,te[4]*=s,te[8]*=s,te[12]*=s,te[1]*=s,te[5]*=s,te[9]*=s,te[13]*=s,te[2]*=s,te[6]*=s,te[10]*=s,te[14]*=s,te[3]*=s,te[7]*=s,te[11]*=s,te[15]*=s,this},multiplyVector3:function(vector){return console.warn("THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead."),vector.applyProjection(this)},multiplyVector4:function(vector){return console.warn("THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead."),vector.applyMatrix4(this)},multiplyVector3Array:function(a){return console.warn("THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead."),this.applyToVector3Array(a)},applyToVector3Array:function(){var v1;return function(array,offset,length){void 0===v1&&(v1=new THREE.Vector3),void 0===offset&&(offset=0),void 0===length&&(length=array.length);for(var i=0,j=offset;length>i;i+=3,j+=3)v1.fromArray(array,j),v1.applyMatrix4(this),v1.toArray(array,j);return array}}(),applyToBuffer:function(){var v1;return function(buffer,offset,length){void 0===v1&&(v1=new THREE.Vector3),void 0===offset&&(offset=0),void 0===length&&(length=buffer.length/buffer.itemSize);for(var i=0,j=offset;length>i;i++,j++)v1.x=buffer.getX(j),v1.y=buffer.getY(j),v1.z=buffer.getZ(j),v1.applyMatrix4(this),buffer.setXYZ(v1.x,v1.y,v1.z);return buffer}}(),rotateAxis:function(v){console.warn("THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead."),v.transformDirection(this)},crossVector:function(vector){return console.warn("THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead."),vector.applyMatrix4(this)},determinant:function(){var te=this.elements,n11=te[0],n12=te[4],n13=te[8],n14=te[12],n21=te[1],n22=te[5],n23=te[9],n24=te[13],n31=te[2],n32=te[6],n33=te[10],n34=te[14],n41=te[3],n42=te[7],n43=te[11],n44=te[15];return n41*(+n14*n23*n32-n13*n24*n32-n14*n22*n33+n12*n24*n33+n13*n22*n34-n12*n23*n34)+n42*(+n11*n23*n34-n11*n24*n33+n14*n21*n33-n13*n21*n34+n13*n24*n31-n14*n23*n31)+n43*(+n11*n24*n32-n11*n22*n34-n14*n21*n32+n12*n21*n34+n14*n22*n31-n12*n24*n31)+n44*(-n13*n22*n31-n11*n23*n32+n11*n22*n33+n13*n21*n32-n12*n21*n33+n12*n23*n31)},transpose:function(){var tmp,te=this.elements;return tmp=te[1],te[1]=te[4],te[4]=tmp,tmp=te[2],te[2]=te[8],te[8]=tmp,tmp=te[6],te[6]=te[9],te[9]=tmp,tmp=te[3],te[3]=te[12],te[12]=tmp,tmp=te[7],te[7]=te[13],te[13]=tmp,tmp=te[11],te[11]=te[14],te[14]=tmp,this},flattenToArrayOffset:function(array,offset){var te=this.elements;return array[offset]=te[0],array[offset+1]=te[1],array[offset+2]=te[2],array[offset+3]=te[3],array[offset+4]=te[4],array[offset+5]=te[5],array[offset+6]=te[6],array[offset+7]=te[7],array[offset+8]=te[8],array[offset+9]=te[9],array[offset+10]=te[10],array[offset+11]=te[11],array[offset+12]=te[12],array[offset+13]=te[13],array[offset+14]=te[14],array[offset+15]=te[15],array},getPosition:function(){var v1;return function(){void 0===v1&&(v1=new THREE.Vector3),console.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.");var te=this.elements;return v1.set(te[12],te[13],te[14])}}(),setPosition:function(v){var te=this.elements;return te[12]=v.x,te[13]=v.y,te[14]=v.z,this},getInverse:function(m,throwOnInvertible){var te=this.elements,me=m.elements,n11=me[0],n12=me[4],n13=me[8],n14=me[12],n21=me[1],n22=me[5],n23=me[9],n24=me[13],n31=me[2],n32=me[6],n33=me[10],n34=me[14],n41=me[3],n42=me[7],n43=me[11],n44=me[15];te[0]=n23*n34*n42-n24*n33*n42+n24*n32*n43-n22*n34*n43-n23*n32*n44+n22*n33*n44,te[4]=n14*n33*n42-n13*n34*n42-n14*n32*n43+n12*n34*n43+n13*n32*n44-n12*n33*n44,te[8]=n13*n24*n42-n14*n23*n42+n14*n22*n43-n12*n24*n43-n13*n22*n44+n12*n23*n44,te[12]=n14*n23*n32-n13*n24*n32-n14*n22*n33+n12*n24*n33+n13*n22*n34-n12*n23*n34,te[1]=n24*n33*n41-n23*n34*n41-n24*n31*n43+n21*n34*n43+n23*n31*n44-n21*n33*n44,te[5]=n13*n34*n41-n14*n33*n41+n14*n31*n43-n11*n34*n43-n13*n31*n44+n11*n33*n44,te[9]=n14*n23*n41-n13*n24*n41-n14*n21*n43+n11*n24*n43+n13*n21*n44-n11*n23*n44,te[13]=n13*n24*n31-n14*n23*n31+n14*n21*n33-n11*n24*n33-n13*n21*n34+n11*n23*n34,te[2]=n22*n34*n41-n24*n32*n41+n24*n31*n42-n21*n34*n42-n22*n31*n44+n21*n32*n44,te[6]=n14*n32*n41-n12*n34*n41-n14*n31*n42+n11*n34*n42+n12*n31*n44-n11*n32*n44,te[10]=n12*n24*n41-n14*n22*n41+n14*n21*n42-n11*n24*n42-n12*n21*n44+n11*n22*n44,te[14]=n14*n22*n31-n12*n24*n31-n14*n21*n32+n11*n24*n32+n12*n21*n34-n11*n22*n34,te[3]=n23*n32*n41-n22*n33*n41-n23*n31*n42+n21*n33*n42+n22*n31*n43-n21*n32*n43,te[7]=n12*n33*n41-n13*n32*n41+n13*n31*n42-n11*n33*n42-n12*n31*n43+n11*n32*n43,te[11]=n13*n22*n41-n12*n23*n41-n13*n21*n42+n11*n23*n42+n12*n21*n43-n11*n22*n43,te[15]=n12*n23*n31-n13*n22*n31+n13*n21*n32-n11*n23*n32-n12*n21*n33+n11*n22*n33;var det=n11*te[0]+n21*te[4]+n31*te[8]+n41*te[12];if(0===det){var msg="THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0";if(throwOnInvertible)throw new Error(msg);return console.warn(msg),this.identity(),this}return this.multiplyScalar(1/det),this},translate:function(v){console.error("THREE.Matrix4: .translate() has been removed.")},rotateX:function(angle){console.error("THREE.Matrix4: .rotateX() has been removed.")},rotateY:function(angle){console.error("THREE.Matrix4: .rotateY() has been removed.")},rotateZ:function(angle){console.error("THREE.Matrix4: .rotateZ() has been removed.")},rotateByAxis:function(axis,angle){console.error("THREE.Matrix4: .rotateByAxis() has been removed.")},scale:function(v){var te=this.elements,x=v.x,y=v.y,z=v.z;return te[0]*=x,te[4]*=y,te[8]*=z,te[1]*=x,te[5]*=y,te[9]*=z,te[2]*=x,te[6]*=y,te[10]*=z,te[3]*=x,te[7]*=y,te[11]*=z,this},getMaxScaleOnAxis:function(){var te=this.elements,scaleXSq=te[0]*te[0]+te[1]*te[1]+te[2]*te[2],scaleYSq=te[4]*te[4]+te[5]*te[5]+te[6]*te[6],scaleZSq=te[8]*te[8]+te[9]*te[9]+te[10]*te[10];return Math.sqrt(Math.max(scaleXSq,scaleYSq,scaleZSq))},makeTranslation:function(x,y,z){return this.set(1,0,0,x,0,1,0,y,0,0,1,z,0,0,0,1),this},makeRotationX:function(theta){var c=Math.cos(theta),s=Math.sin(theta);return this.set(1,0,0,0,0,c,-s,0,0,s,c,0,0,0,0,1),this},makeRotationY:function(theta){var c=Math.cos(theta),s=Math.sin(theta);return this.set(c,0,s,0,0,1,0,0,-s,0,c,0,0,0,0,1),this},makeRotationZ:function(theta){var c=Math.cos(theta),s=Math.sin(theta);return this.set(c,-s,0,0,s,c,0,0,0,0,1,0,0,0,0,1),this},makeRotationAxis:function(axis,angle){var c=Math.cos(angle),s=Math.sin(angle),t=1-c,x=axis.x,y=axis.y,z=axis.z,tx=t*x,ty=t*y;return this.set(tx*x+c,tx*y-s*z,tx*z+s*y,0,tx*y+s*z,ty*y+c,ty*z-s*x,0,tx*z-s*y,ty*z+s*x,t*z*z+c,0,0,0,0,1),this},makeScale:function(x,y,z){return this.set(x,0,0,0,0,y,0,0,0,0,z,0,0,0,0,1),this},compose:function(position,quaternion,scale){return this.makeRotationFromQuaternion(quaternion),this.scale(scale),this.setPosition(position),this},decompose:function(){var vector,matrix;return function(position,quaternion,scale){void 0===vector&&(vector=new THREE.Vector3),void 0===matrix&&(matrix=new THREE.Matrix4);var te=this.elements,sx=vector.set(te[0],te[1],te[2]).length(),sy=vector.set(te[4],te[5],te[6]).length(),sz=vector.set(te[8],te[9],te[10]).length(),det=this.determinant();0>det&&(sx=-sx),position.x=te[12],position.y=te[13],position.z=te[14],matrix.elements.set(this.elements);var invSX=1/sx,invSY=1/sy,invSZ=1/sz;return matrix.elements[0]*=invSX,matrix.elements[1]*=invSX,matrix.elements[2]*=invSX,matrix.elements[4]*=invSY,matrix.elements[5]*=invSY,matrix.elements[6]*=invSY,matrix.elements[8]*=invSZ,matrix.elements[9]*=invSZ,matrix.elements[10]*=invSZ,quaternion.setFromRotationMatrix(matrix),scale.x=sx,scale.y=sy,scale.z=sz,this}}(),makeFrustum:function(left,right,bottom,top,near,far){var te=this.elements,x=2*near/(right-left),y=2*near/(top-bottom),a=(right+left)/(right-left),b=(top+bottom)/(top-bottom),c=-(far+near)/(far-near),d=-2*far*near/(far-near);return te[0]=x,te[4]=0,te[8]=a,te[12]=0,te[1]=0,te[5]=y,te[9]=b,te[13]=0,te[2]=0,te[6]=0,te[10]=c,te[14]=d,te[3]=0,te[7]=0,te[11]=-1,te[15]=0,this},makePerspective:function(fov,aspect,near,far){var ymax=near*Math.tan(THREE.Math.degToRad(.5*fov)),ymin=-ymax,xmin=ymin*aspect,xmax=ymax*aspect;return this.makeFrustum(xmin,xmax,ymin,ymax,near,far)},makeOrthographic:function(left,right,top,bottom,near,far){var te=this.elements,w=right-left,h=top-bottom,p=far-near,x=(right+left)/w,y=(top+bottom)/h,z=(far+near)/p;return te[0]=2/w,te[4]=0,te[8]=0,te[12]=-x,te[1]=0,te[5]=2/h,te[9]=0,te[13]=-y,te[2]=0,te[6]=0,te[10]=-2/p,te[14]=-z,te[3]=0,te[7]=0,te[11]=0,te[15]=1,this},equals:function(matrix){for(var te=this.elements,me=matrix.elements,i=0;16>i;i++)if(te[i]!==me[i])return!1;return!0},fromArray:function(array){return this.elements.set(array),this},toArray:function(){var te=this.elements;return[te[0],te[1],te[2],te[3],te[4],te[5],te[6],te[7],te[8],te[9],te[10],te[11],te[12],te[13],te[14],te[15]]}},THREE.Ray=function(origin,direction){this.origin=void 0!==origin?origin:new THREE.Vector3,this.direction=void 0!==direction?direction:new THREE.Vector3},THREE.Ray.prototype={constructor:THREE.Ray,set:function(origin,direction){return this.origin.copy(origin),this.direction.copy(direction),this},clone:function(){return(new this.constructor).copy(this)},copy:function(ray){return this.origin.copy(ray.origin),this.direction.copy(ray.direction),this},at:function(t,optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.copy(this.direction).multiplyScalar(t).add(this.origin)},recast:function(){var v1=new THREE.Vector3;return function(t){return this.origin.copy(this.at(t,v1)),this}}(),closestPointToPoint:function(point,optionalTarget){var result=optionalTarget||new THREE.Vector3;result.subVectors(point,this.origin);var directionDistance=result.dot(this.direction);return 0>directionDistance?result.copy(this.origin):result.copy(this.direction).multiplyScalar(directionDistance).add(this.origin)},distanceToPoint:function(point){return Math.sqrt(this.distanceSqToPoint(point))},distanceSqToPoint:function(){var v1=new THREE.Vector3;return function(point){var directionDistance=v1.subVectors(point,this.origin).dot(this.direction);return 0>directionDistance?this.origin.distanceToSquared(point):(v1.copy(this.direction).multiplyScalar(directionDistance).add(this.origin),v1.distanceToSquared(point))}}(),distanceSqToSegment:function(){var segCenter=new THREE.Vector3,segDir=new THREE.Vector3,diff=new THREE.Vector3;return function(v0,v1,optionalPointOnRay,optionalPointOnSegment){segCenter.copy(v0).add(v1).multiplyScalar(.5),segDir.copy(v1).sub(v0).normalize(),diff.copy(this.origin).sub(segCenter);var s0,s1,sqrDist,extDet,segExtent=.5*v0.distanceTo(v1),a01=-this.direction.dot(segDir),b0=diff.dot(this.direction),b1=-diff.dot(segDir),c=diff.lengthSq(),det=Math.abs(1-a01*a01);if(det>0)if(s0=a01*b1-b0,s1=a01*b0-b1,extDet=segExtent*det,s0>=0)if(s1>=-extDet)if(extDet>=s1){var invDet=1/det;s0*=invDet,s1*=invDet,sqrDist=s0*(s0+a01*s1+2*b0)+s1*(a01*s0+s1+2*b1)+c}else s1=segExtent,s0=Math.max(0,-(a01*s1+b0)),sqrDist=-s0*s0+s1*(s1+2*b1)+c;else s1=-segExtent,s0=Math.max(0,-(a01*s1+b0)),sqrDist=-s0*s0+s1*(s1+2*b1)+c;else-extDet>=s1?(s0=Math.max(0,-(-a01*segExtent+b0)),s1=s0>0?-segExtent:Math.min(Math.max(-segExtent,-b1),segExtent),sqrDist=-s0*s0+s1*(s1+2*b1)+c):extDet>=s1?(s0=0,s1=Math.min(Math.max(-segExtent,-b1),segExtent),sqrDist=s1*(s1+2*b1)+c):(s0=Math.max(0,-(a01*segExtent+b0)),s1=s0>0?segExtent:Math.min(Math.max(-segExtent,-b1),segExtent),sqrDist=-s0*s0+s1*(s1+2*b1)+c);else s1=a01>0?-segExtent:segExtent,s0=Math.max(0,-(a01*s1+b0)),sqrDist=-s0*s0+s1*(s1+2*b1)+c;return optionalPointOnRay&&optionalPointOnRay.copy(this.direction).multiplyScalar(s0).add(this.origin),optionalPointOnSegment&&optionalPointOnSegment.copy(segDir).multiplyScalar(s1).add(segCenter),sqrDist}}(),isIntersectionSphere:function(sphere){return this.distanceToPoint(sphere.center)<=sphere.radius},intersectSphere:function(){var v1=new THREE.Vector3;return function(sphere,optionalTarget){v1.subVectors(sphere.center,this.origin);var tca=v1.dot(this.direction),d2=v1.dot(v1)-tca*tca,radius2=sphere.radius*sphere.radius;if(d2>radius2)return null;var thc=Math.sqrt(radius2-d2),t0=tca-thc,t1=tca+thc;return 0>t0&&0>t1?null:0>t0?this.at(t1,optionalTarget):this.at(t0,optionalTarget)}}(),isIntersectionPlane:function(plane){var distToPoint=plane.distanceToPoint(this.origin);if(0===distToPoint)return!0;var denominator=plane.normal.dot(this.direction);return 0>denominator*distToPoint},distanceToPlane:function(plane){var denominator=plane.normal.dot(this.direction);if(0===denominator)return 0===plane.distanceToPoint(this.origin)?0:null;var t=-(this.origin.dot(plane.normal)+plane.constant)/denominator;return t>=0?t:null},intersectPlane:function(plane,optionalTarget){var t=this.distanceToPlane(plane);return null===t?null:this.at(t,optionalTarget)},isIntersectionBox:function(){var v=new THREE.Vector3;return function(box){return null!==this.intersectBox(box,v)}}(),intersectBox:function(box,optionalTarget){var tmin,tmax,tymin,tymax,tzmin,tzmax,invdirx=1/this.direction.x,invdiry=1/this.direction.y,invdirz=1/this.direction.z,origin=this.origin;return invdirx>=0?(tmin=(box.min.x-origin.x)*invdirx,tmax=(box.max.x-origin.x)*invdirx):(tmin=(box.max.x-origin.x)*invdirx,tmax=(box.min.x-origin.x)*invdirx),invdiry>=0?(tymin=(box.min.y-origin.y)*invdiry,tymax=(box.max.y-origin.y)*invdiry):(tymin=(box.max.y-origin.y)*invdiry,tymax=(box.min.y-origin.y)*invdiry),tmin>tymax||tymin>tmax?null:((tymin>tmin||tmin!==tmin)&&(tmin=tymin),(tmax>tymax||tmax!==tmax)&&(tmax=tymax),invdirz>=0?(tzmin=(box.min.z-origin.z)*invdirz,tzmax=(box.max.z-origin.z)*invdirz):(tzmin=(box.max.z-origin.z)*invdirz,tzmax=(box.min.z-origin.z)*invdirz),tmin>tzmax||tzmin>tmax?null:((tzmin>tmin||tmin!==tmin)&&(tmin=tzmin),(tmax>tzmax||tmax!==tmax)&&(tmax=tzmax),0>tmax?null:this.at(tmin>=0?tmin:tmax,optionalTarget)))},intersectTriangle:function(){var diff=new THREE.Vector3,edge1=new THREE.Vector3,edge2=new THREE.Vector3,normal=new THREE.Vector3;return function(a,b,c,backfaceCulling,optionalTarget){edge1.subVectors(b,a),edge2.subVectors(c,a),normal.crossVectors(edge1,edge2);var sign,DdN=this.direction.dot(normal);if(DdN>0){if(backfaceCulling)return null;sign=1}else{if(!(0>DdN))return null;sign=-1,DdN=-DdN}diff.subVectors(this.origin,a);var DdQxE2=sign*this.direction.dot(edge2.crossVectors(diff,edge2));if(0>DdQxE2)return null;var DdE1xQ=sign*this.direction.dot(edge1.cross(diff));if(0>DdE1xQ)return null;if(DdQxE2+DdE1xQ>DdN)return null;var QdN=-sign*diff.dot(normal);return 0>QdN?null:this.at(QdN/DdN,optionalTarget)}}(),applyMatrix4:function(matrix4){return this.direction.add(this.origin).applyMatrix4(matrix4),this.origin.applyMatrix4(matrix4),this.direction.sub(this.origin),this.direction.normalize(),this},equals:function(ray){return ray.origin.equals(this.origin)&&ray.direction.equals(this.direction)}},THREE.Sphere=function(center,radius){this.center=void 0!==center?center:new THREE.Vector3,this.radius=void 0!==radius?radius:0},THREE.Sphere.prototype={constructor:THREE.Sphere,set:function(center,radius){return this.center.copy(center),this.radius=radius,this},setFromPoints:function(){var box=new THREE.Box3;return function(points,optionalCenter){var center=this.center;void 0!==optionalCenter?center.copy(optionalCenter):box.setFromPoints(points).center(center);for(var maxRadiusSq=0,i=0,il=points.length;il>i;i++)maxRadiusSq=Math.max(maxRadiusSq,center.distanceToSquared(points[i]));return this.radius=Math.sqrt(maxRadiusSq),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(sphere){return this.center.copy(sphere.center),this.radius=sphere.radius,this},empty:function(){return this.radius<=0},containsPoint:function(point){return point.distanceToSquared(this.center)<=this.radius*this.radius},distanceToPoint:function(point){return point.distanceTo(this.center)-this.radius},intersectsSphere:function(sphere){var radiusSum=this.radius+sphere.radius;return sphere.center.distanceToSquared(this.center)<=radiusSum*radiusSum},clampPoint:function(point,optionalTarget){var deltaLengthSq=this.center.distanceToSquared(point),result=optionalTarget||new THREE.Vector3;return result.copy(point),deltaLengthSq>this.radius*this.radius&&(result.sub(this.center).normalize(),result.multiplyScalar(this.radius).add(this.center)),result},getBoundingBox:function(optionalTarget){var box=optionalTarget||new THREE.Box3;return box.set(this.center,this.center),box.expandByScalar(this.radius),box},applyMatrix4:function(matrix){return this.center.applyMatrix4(matrix),this.radius=this.radius*matrix.getMaxScaleOnAxis(),this},translate:function(offset){return this.center.add(offset),this},equals:function(sphere){return sphere.center.equals(this.center)&&sphere.radius===this.radius}},THREE.Frustum=function(p0,p1,p2,p3,p4,p5){this.planes=[void 0!==p0?p0:new THREE.Plane,void 0!==p1?p1:new THREE.Plane,void 0!==p2?p2:new THREE.Plane,void 0!==p3?p3:new THREE.Plane,void 0!==p4?p4:new THREE.Plane,void 0!==p5?p5:new THREE.Plane]},THREE.Frustum.prototype={constructor:THREE.Frustum,set:function(p0,p1,p2,p3,p4,p5){var planes=this.planes;return planes[0].copy(p0),planes[1].copy(p1),planes[2].copy(p2),planes[3].copy(p3),planes[4].copy(p4),planes[5].copy(p5),this},clone:function(){return(new this.constructor).copy(this)},copy:function(frustum){for(var planes=this.planes,i=0;6>i;i++)planes[i].copy(frustum.planes[i]);return this},setFromMatrix:function(m){var planes=this.planes,me=m.elements,me0=me[0],me1=me[1],me2=me[2],me3=me[3],me4=me[4],me5=me[5],me6=me[6],me7=me[7],me8=me[8],me9=me[9],me10=me[10],me11=me[11],me12=me[12],me13=me[13],me14=me[14],me15=me[15];return planes[0].setComponents(me3-me0,me7-me4,me11-me8,me15-me12).normalize(),planes[1].setComponents(me3+me0,me7+me4,me11+me8,me15+me12).normalize(),planes[2].setComponents(me3+me1,me7+me5,me11+me9,me15+me13).normalize(),planes[3].setComponents(me3-me1,me7-me5,me11-me9,me15-me13).normalize(),planes[4].setComponents(me3-me2,me7-me6,me11-me10,me15-me14).normalize(),planes[5].setComponents(me3+me2,me7+me6,me11+me10,me15+me14).normalize(),this},intersectsObject:function(){var sphere=new THREE.Sphere;return function(object){var geometry=object.geometry;return null===geometry.boundingSphere&&geometry.computeBoundingSphere(),sphere.copy(geometry.boundingSphere),sphere.applyMatrix4(object.matrixWorld),this.intersectsSphere(sphere)}}(),intersectsSphere:function(sphere){for(var planes=this.planes,center=sphere.center,negRadius=-sphere.radius,i=0;6>i;i++){var distance=planes[i].distanceToPoint(center);if(negRadius>distance)return!1}return!0},intersectsBox:function(){var p1=new THREE.Vector3,p2=new THREE.Vector3;return function(box){for(var planes=this.planes,i=0;6>i;i++){var plane=planes[i];p1.x=plane.normal.x>0?box.min.x:box.max.x,p2.x=plane.normal.x>0?box.max.x:box.min.x,p1.y=plane.normal.y>0?box.min.y:box.max.y,p2.y=plane.normal.y>0?box.max.y:box.min.y,p1.z=plane.normal.z>0?box.min.z:box.max.z,p2.z=plane.normal.z>0?box.max.z:box.min.z;var d1=plane.distanceToPoint(p1),d2=plane.distanceToPoint(p2);if(0>d1&&0>d2)return!1}return!0}}(),containsPoint:function(point){for(var planes=this.planes,i=0;6>i;i++)if(planes[i].distanceToPoint(point)<0)return!1;return!0}},THREE.Plane=function(normal,constant){this.normal=void 0!==normal?normal:new THREE.Vector3(1,0,0),this.constant=void 0!==constant?constant:0},THREE.Plane.prototype={constructor:THREE.Plane,set:function(normal,constant){return this.normal.copy(normal),this.constant=constant,this},setComponents:function(x,y,z,w){return this.normal.set(x,y,z),this.constant=w,this},setFromNormalAndCoplanarPoint:function(normal,point){return this.normal.copy(normal),this.constant=-point.dot(this.normal),this},setFromCoplanarPoints:function(){var v1=new THREE.Vector3,v2=new THREE.Vector3;return function(a,b,c){var normal=v1.subVectors(c,b).cross(v2.subVectors(a,b)).normalize();return this.setFromNormalAndCoplanarPoint(normal,a),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(plane){return this.normal.copy(plane.normal),this.constant=plane.constant,this},normalize:function(){var inverseNormalLength=1/this.normal.length();return this.normal.multiplyScalar(inverseNormalLength),this.constant*=inverseNormalLength,this},negate:function(){return this.constant*=-1,this.normal.negate(),this},distanceToPoint:function(point){return this.normal.dot(point)+this.constant},distanceToSphere:function(sphere){return this.distanceToPoint(sphere.center)-sphere.radius},projectPoint:function(point,optionalTarget){return this.orthoPoint(point,optionalTarget).sub(point).negate()},orthoPoint:function(point,optionalTarget){var perpendicularMagnitude=this.distanceToPoint(point),result=optionalTarget||new THREE.Vector3;return result.copy(this.normal).multiplyScalar(perpendicularMagnitude)},isIntersectionLine:function(line){var startSign=this.distanceToPoint(line.start),endSign=this.distanceToPoint(line.end);return 0>startSign&&endSign>0||0>endSign&&startSign>0},intersectLine:function(){var v1=new THREE.Vector3;return function(line,optionalTarget){var result=optionalTarget||new THREE.Vector3,direction=line.delta(v1),denominator=this.normal.dot(direction);if(0!==denominator){var t=-(line.start.dot(this.normal)+this.constant)/denominator;if(!(0>t||t>1))return result.copy(direction).multiplyScalar(t).add(line.start); +}else if(0===this.distanceToPoint(line.start))return result.copy(line.start)}}(),coplanarPoint:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.copy(this.normal).multiplyScalar(-this.constant)},applyMatrix4:function(){var v1=new THREE.Vector3,v2=new THREE.Vector3,m1=new THREE.Matrix3;return function(matrix,optionalNormalMatrix){var normalMatrix=optionalNormalMatrix||m1.getNormalMatrix(matrix),newNormal=v1.copy(this.normal).applyMatrix3(normalMatrix),newCoplanarPoint=this.coplanarPoint(v2);return newCoplanarPoint.applyMatrix4(matrix),this.setFromNormalAndCoplanarPoint(newNormal,newCoplanarPoint),this}}(),translate:function(offset){return this.constant=this.constant-offset.dot(this.normal),this},equals:function(plane){return plane.normal.equals(this.normal)&&plane.constant===this.constant}},THREE.Math={generateUUID:function(){var r,chars="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),uuid=new Array(36),rnd=0;return function(){for(var i=0;36>i;i++)8===i||13===i||18===i||23===i?uuid[i]="-":14===i?uuid[i]="4":(2>=rnd&&(rnd=33554432+16777216*Math.random()|0),r=15&rnd,rnd>>=4,uuid[i]=chars[19===i?3&r|8:r]);return uuid.join("")}}(),clamp:function(value,min,max){return Math.max(min,Math.min(max,value))},euclideanModulo:function(n,m){return(n%m+m)%m},mapLinear:function(x,a1,a2,b1,b2){return b1+(x-a1)*(b2-b1)/(a2-a1)},smoothstep:function(x,min,max){return min>=x?0:x>=max?1:(x=(x-min)/(max-min),x*x*(3-2*x))},smootherstep:function(x,min,max){return min>=x?0:x>=max?1:(x=(x-min)/(max-min),x*x*x*(x*(6*x-15)+10))},random16:function(){return(65280*Math.random()+255*Math.random())/65535},randInt:function(low,high){return low+Math.floor(Math.random()*(high-low+1))},randFloat:function(low,high){return low+Math.random()*(high-low)},randFloatSpread:function(range){return range*(.5-Math.random())},degToRad:function(){var degreeToRadiansFactor=Math.PI/180;return function(degrees){return degrees*degreeToRadiansFactor}}(),radToDeg:function(){var radianToDegreesFactor=180/Math.PI;return function(radians){return radians*radianToDegreesFactor}}(),isPowerOfTwo:function(value){return 0===(value&value-1)&&0!==value},nearestPowerOfTwo:function(value){return Math.pow(2,Math.round(Math.log(value)/Math.LN2))},nextPowerOfTwo:function(value){return value--,value|=value>>1,value|=value>>2,value|=value>>4,value|=value>>8,value|=value>>16,value++,value}},THREE.Spline=function(points){function interpolate(p0,p1,p2,p3,t,t2,t3){var v0=.5*(p2-p0),v1=.5*(p3-p1);return(2*(p1-p2)+v0+v1)*t3+(-3*(p1-p2)-2*v0-v1)*t2+v0*t+p1}this.points=points;var point,intPoint,weight,w2,w3,pa,pb,pc,pd,c=[],v3={x:0,y:0,z:0};this.initFromArray=function(a){this.points=[];for(var i=0;ithis.points.length-2?this.points.length-1:intPoint+1,c[3]=intPoint>this.points.length-3?this.points.length-1:intPoint+2,pa=this.points[c[0]],pb=this.points[c[1]],pc=this.points[c[2]],pd=this.points[c[3]],w2=weight*weight,w3=weight*w2,v3.x=interpolate(pa.x,pb.x,pc.x,pd.x,weight,w2,w3),v3.y=interpolate(pa.y,pb.y,pc.y,pd.y,weight,w2,w3),v3.z=interpolate(pa.z,pb.z,pc.z,pd.z,weight,w2,w3),v3},this.getControlPointsArray=function(){var i,p,l=this.points.length,coords=[];for(i=0;l>i;i++)p=this.points[i],coords[i]=[p.x,p.y,p.z];return coords},this.getLength=function(nSubDivisions){var i,index,nSamples,position,point=0,intPoint=0,oldIntPoint=0,oldPosition=new THREE.Vector3,tmpVec=new THREE.Vector3,chunkLengths=[],totalLength=0;for(chunkLengths[0]=0,nSubDivisions||(nSubDivisions=100),nSamples=this.points.length*nSubDivisions,oldPosition.copy(this.points[0]),i=1;nSamples>i;i++)index=i/nSamples,position=this.getPoint(index),tmpVec.copy(position),totalLength+=tmpVec.distanceTo(oldPosition),oldPosition.copy(position),point=(this.points.length-1)*index,intPoint=Math.floor(point),intPoint!==oldIntPoint&&(chunkLengths[intPoint]=totalLength,oldIntPoint=intPoint);return chunkLengths[chunkLengths.length]=totalLength,{chunks:chunkLengths,total:totalLength}},this.reparametrizeByArcLength=function(samplingCoef){var i,j,index,indexCurrent,indexNext,realDistance,sampling,position,newpoints=[],tmpVec=new THREE.Vector3,sl=this.getLength();for(newpoints.push(tmpVec.copy(this.points[0]).clone()),i=1;ij;j++)index=indexCurrent+j*(1/sampling)*(indexNext-indexCurrent),position=this.getPoint(index),newpoints.push(tmpVec.copy(position).clone());newpoints.push(tmpVec.copy(this.points[i]).clone())}this.points=newpoints}},THREE.Triangle=function(a,b,c){this.a=void 0!==a?a:new THREE.Vector3,this.b=void 0!==b?b:new THREE.Vector3,this.c=void 0!==c?c:new THREE.Vector3},THREE.Triangle.normal=function(){var v0=new THREE.Vector3;return function(a,b,c,optionalTarget){var result=optionalTarget||new THREE.Vector3;result.subVectors(c,b),v0.subVectors(a,b),result.cross(v0);var resultLengthSq=result.lengthSq();return resultLengthSq>0?result.multiplyScalar(1/Math.sqrt(resultLengthSq)):result.set(0,0,0)}}(),THREE.Triangle.barycoordFromPoint=function(){var v0=new THREE.Vector3,v1=new THREE.Vector3,v2=new THREE.Vector3;return function(point,a,b,c,optionalTarget){v0.subVectors(c,a),v1.subVectors(b,a),v2.subVectors(point,a);var dot00=v0.dot(v0),dot01=v0.dot(v1),dot02=v0.dot(v2),dot11=v1.dot(v1),dot12=v1.dot(v2),denom=dot00*dot11-dot01*dot01,result=optionalTarget||new THREE.Vector3;if(0===denom)return result.set(-2,-1,-1);var invDenom=1/denom,u=(dot11*dot02-dot01*dot12)*invDenom,v=(dot00*dot12-dot01*dot02)*invDenom;return result.set(1-u-v,v,u)}}(),THREE.Triangle.containsPoint=function(){var v1=new THREE.Vector3;return function(point,a,b,c){var result=THREE.Triangle.barycoordFromPoint(point,a,b,c,v1);return result.x>=0&&result.y>=0&&result.x+result.y<=1}}(),THREE.Triangle.prototype={constructor:THREE.Triangle,set:function(a,b,c){return this.a.copy(a),this.b.copy(b),this.c.copy(c),this},setFromPointsAndIndices:function(points,i0,i1,i2){return this.a.copy(points[i0]),this.b.copy(points[i1]),this.c.copy(points[i2]),this},clone:function(){return(new this.constructor).copy(this)},copy:function(triangle){return this.a.copy(triangle.a),this.b.copy(triangle.b),this.c.copy(triangle.c),this},area:function(){var v0=new THREE.Vector3,v1=new THREE.Vector3;return function(){return v0.subVectors(this.c,this.b),v1.subVectors(this.a,this.b),.5*v0.cross(v1).length()}}(),midpoint:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return result.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},normal:function(optionalTarget){return THREE.Triangle.normal(this.a,this.b,this.c,optionalTarget)},plane:function(optionalTarget){var result=optionalTarget||new THREE.Plane;return result.setFromCoplanarPoints(this.a,this.b,this.c)},barycoordFromPoint:function(point,optionalTarget){return THREE.Triangle.barycoordFromPoint(point,this.a,this.b,this.c,optionalTarget)},containsPoint:function(point){return THREE.Triangle.containsPoint(point,this.a,this.b,this.c)},equals:function(triangle){return triangle.a.equals(this.a)&&triangle.b.equals(this.b)&&triangle.c.equals(this.c)}},THREE.Channels=function(){this.mask=1},THREE.Channels.prototype={constructor:THREE.Channels,set:function(channel){this.mask=1<i;i++)array[i]=listenerArray[i];for(var i=0;length>i;i++)array[i].call(this,event)}}}},function(THREE){function descSort(a,b){return a.distance-b.distance}function intersectObject(object,raycaster,intersects,recursive){if(object.visible!==!1&&(object.raycast(raycaster,intersects),recursive===!0))for(var children=object.children,i=0,l=children.length;l>i;i++)intersectObject(children[i],raycaster,intersects,!0)}THREE.Raycaster=function(origin,direction,near,far){this.ray=new THREE.Ray(origin,direction),this.near=near||0,this.far=far||1/0,this.params={Mesh:{},Line:{},LOD:{},Points:{threshold:1},Sprite:{}},Object.defineProperties(this.params,{PointCloud:{get:function(){return console.warn("THREE.Raycaster: params.PointCloud has been renamed to params.Points."),this.Points}}})},THREE.Raycaster.prototype={constructor:THREE.Raycaster,linePrecision:1,set:function(origin,direction){this.ray.set(origin,direction)},setFromCamera:function(coords,camera){camera instanceof THREE.PerspectiveCamera?(this.ray.origin.setFromMatrixPosition(camera.matrixWorld),this.ray.direction.set(coords.x,coords.y,.5).unproject(camera).sub(this.ray.origin).normalize()):camera instanceof THREE.OrthographicCamera?(this.ray.origin.set(coords.x,coords.y,-1).unproject(camera),this.ray.direction.set(0,0,-1).transformDirection(camera.matrixWorld)):console.error("THREE.Raycaster: Unsupported camera type.")},intersectObject:function(object,recursive){var intersects=[];return intersectObject(object,this,intersects,recursive),intersects.sort(descSort),intersects},intersectObjects:function(objects,recursive){var intersects=[];if(Array.isArray(objects)===!1)return console.warn("THREE.Raycaster.intersectObjects: objects is not an Array."),intersects;for(var i=0,l=objects.length;l>i;i++)intersectObject(objects[i],this,intersects,recursive);return intersects.sort(descSort),intersects}}}(THREE),THREE.Object3D=function(){function onRotationChange(){quaternion.setFromEuler(rotation,!1)}function onQuaternionChange(){rotation.setFromQuaternion(quaternion,void 0,!1)}Object.defineProperty(this,"id",{value:THREE.Object3DIdCount++}),this.uuid=THREE.Math.generateUUID(),this.name="",this.type="Object3D",this.parent=null,this.channels=new THREE.Channels,this.children=[],this.up=THREE.Object3D.DefaultUp.clone();var position=new THREE.Vector3,rotation=new THREE.Euler,quaternion=new THREE.Quaternion,scale=new THREE.Vector3(1,1,1);rotation.onChange(onRotationChange),quaternion.onChange(onQuaternionChange),Object.defineProperties(this,{position:{enumerable:!0,value:position},rotation:{enumerable:!0,value:rotation},quaternion:{enumerable:!0,value:quaternion},scale:{enumerable:!0,value:scale},modelViewMatrix:{value:new THREE.Matrix4},normalMatrix:{value:new THREE.Matrix3}}),this.rotationAutoUpdate=!0,this.matrix=new THREE.Matrix4,this.matrixWorld=new THREE.Matrix4,this.matrixAutoUpdate=THREE.Object3D.DefaultMatrixAutoUpdate,this.matrixWorldNeedsUpdate=!1,this.visible=!0,this.castShadow=!1,this.receiveShadow=!1,this.frustumCulled=!0,this.renderOrder=0,this.userData={}},THREE.Object3D.DefaultUp=new THREE.Vector3(0,1,0),THREE.Object3D.DefaultMatrixAutoUpdate=!0,THREE.Object3D.prototype={constructor:THREE.Object3D,get eulerOrder(){return console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."),this.rotation.order},set eulerOrder(value){console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."),this.rotation.order=value},get useQuaternion(){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},set useQuaternion(value){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},set renderDepth(value){console.warn("THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.")},applyMatrix:function(matrix){this.matrix.multiplyMatrices(matrix,this.matrix),this.matrix.decompose(this.position,this.quaternion,this.scale)},setRotationFromAxisAngle:function(axis,angle){this.quaternion.setFromAxisAngle(axis,angle)},setRotationFromEuler:function(euler){this.quaternion.setFromEuler(euler,!0)},setRotationFromMatrix:function(m){this.quaternion.setFromRotationMatrix(m)},setRotationFromQuaternion:function(q){this.quaternion.copy(q)},rotateOnAxis:function(){var q1=new THREE.Quaternion;return function(axis,angle){return q1.setFromAxisAngle(axis,angle),this.quaternion.multiply(q1),this}}(),rotateX:function(){var v1=new THREE.Vector3(1,0,0);return function(angle){return this.rotateOnAxis(v1,angle)}}(),rotateY:function(){var v1=new THREE.Vector3(0,1,0);return function(angle){return this.rotateOnAxis(v1,angle)}}(),rotateZ:function(){var v1=new THREE.Vector3(0,0,1);return function(angle){return this.rotateOnAxis(v1,angle)}}(),translateOnAxis:function(){var v1=new THREE.Vector3;return function(axis,distance){return v1.copy(axis).applyQuaternion(this.quaternion),this.position.add(v1.multiplyScalar(distance)),this}}(),translate:function(distance,axis){return console.warn("THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead."),this.translateOnAxis(axis,distance)},translateX:function(){var v1=new THREE.Vector3(1,0,0);return function(distance){return this.translateOnAxis(v1,distance)}}(),translateY:function(){var v1=new THREE.Vector3(0,1,0);return function(distance){return this.translateOnAxis(v1,distance)}}(),translateZ:function(){var v1=new THREE.Vector3(0,0,1);return function(distance){return this.translateOnAxis(v1,distance)}}(),localToWorld:function(vector){return vector.applyMatrix4(this.matrixWorld)},worldToLocal:function(){var m1=new THREE.Matrix4;return function(vector){return vector.applyMatrix4(m1.getInverse(this.matrixWorld))}}(),lookAt:function(){var m1=new THREE.Matrix4;return function(vector){m1.lookAt(vector,this.position,this.up),this.quaternion.setFromRotationMatrix(m1)}}(),add:function(object){if(arguments.length>1){for(var i=0;i1)for(var i=0;ii;i++){var child=this.children[i],object=child.getObjectByProperty(name,value);if(void 0!==object)return object}},getWorldPosition:function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return this.updateMatrixWorld(!0),result.setFromMatrixPosition(this.matrixWorld)},getWorldQuaternion:function(){var position=new THREE.Vector3,scale=new THREE.Vector3;return function(optionalTarget){var result=optionalTarget||new THREE.Quaternion;return this.updateMatrixWorld(!0),this.matrixWorld.decompose(position,result,scale),result}}(),getWorldRotation:function(){var quaternion=new THREE.Quaternion;return function(optionalTarget){var result=optionalTarget||new THREE.Euler;return this.getWorldQuaternion(quaternion),result.setFromQuaternion(quaternion,this.rotation.order,!1)}}(),getWorldScale:function(){var position=new THREE.Vector3,quaternion=new THREE.Quaternion;return function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return this.updateMatrixWorld(!0),this.matrixWorld.decompose(position,quaternion,result),result}}(),getWorldDirection:function(){var quaternion=new THREE.Quaternion;return function(optionalTarget){var result=optionalTarget||new THREE.Vector3;return this.getWorldQuaternion(quaternion),result.set(0,0,1).applyQuaternion(quaternion)}}(),raycast:function(){},traverse:function(callback){callback(this);for(var children=this.children,i=0,l=children.length;l>i;i++)children[i].traverse(callback)},traverseVisible:function(callback){if(this.visible!==!1){callback(this);for(var children=this.children,i=0,l=children.length;l>i;i++)children[i].traverseVisible(callback)}},traverseAncestors:function(callback){var parent=this.parent;null!==parent&&(callback(parent),parent.traverseAncestors(callback))},updateMatrix:function(){this.matrix.compose(this.position,this.quaternion,this.scale),this.matrixWorldNeedsUpdate=!0},updateMatrixWorld:function(force){this.matrixAutoUpdate===!0&&this.updateMatrix(),this.matrixWorldNeedsUpdate!==!0&&force!==!0||(null===this.parent?this.matrixWorld.copy(this.matrix):this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix),this.matrixWorldNeedsUpdate=!1,force=!0);for(var i=0,l=this.children.length;l>i;i++)this.children[i].updateMatrixWorld(force)},toJSON:function(meta){function extractFromCache(cache){var values=[];for(var key in cache){var data=cache[key];delete data.metadata,values.push(data)}return values}var isRootObject=void 0===meta,output={};isRootObject&&(meta={geometries:{},materials:{},textures:{},images:{}},output.metadata={version:4.4,type:"Object",generator:"Object3D.toJSON"});var object={};if(object.uuid=this.uuid,object.type=this.type,""!==this.name&&(object.name=this.name),"{}"!==JSON.stringify(this.userData)&&(object.userData=this.userData),this.castShadow===!0&&(object.castShadow=!0),this.receiveShadow===!0&&(object.receiveShadow=!0),this.visible===!1&&(object.visible=!1),object.matrix=this.matrix.toArray(),void 0!==this.geometry&&(void 0===meta.geometries[this.geometry.uuid]&&(meta.geometries[this.geometry.uuid]=this.geometry.toJSON(meta)),object.geometry=this.geometry.uuid),void 0!==this.material&&(void 0===meta.materials[this.material.uuid]&&(meta.materials[this.material.uuid]=this.material.toJSON(meta)),object.material=this.material.uuid),this.children.length>0){object.children=[];for(var i=0;i0&&(output.geometries=geometries),materials.length>0&&(output.materials=materials),textures.length>0&&(output.textures=textures),images.length>0&&(output.images=images)}return output.object=object,output},clone:function(recursive){return(new this.constructor).copy(this,recursive)},copy:function(source,recursive){if(void 0===recursive&&(recursive=!0),this.name=source.name,this.up.copy(source.up),this.position.copy(source.position),this.quaternion.copy(source.quaternion),this.scale.copy(source.scale),this.rotationAutoUpdate=source.rotationAutoUpdate,this.matrix.copy(source.matrix),this.matrixWorld.copy(source.matrixWorld),this.matrixAutoUpdate=source.matrixAutoUpdate,this.matrixWorldNeedsUpdate=source.matrixWorldNeedsUpdate,this.visible=source.visible,this.castShadow=source.castShadow,this.receiveShadow=source.receiveShadow,this.frustumCulled=source.frustumCulled,this.renderOrder=source.renderOrder,this.userData=JSON.parse(JSON.stringify(source.userData)),recursive===!0)for(var i=0;ii;i++)this.vertexNormals[i]=source.vertexNormals[i].clone();for(var i=0,il=source.vertexColors.length;il>i;i++)this.vertexColors[i]=source.vertexColors[i].clone();return this}},THREE.Face4=function(a,b,c,d,normal,color,materialIndex){return console.warn("THREE.Face4 has been removed. A THREE.Face3 will be created instead."),new THREE.Face3(a,b,c,normal,color,materialIndex)},THREE.BufferAttribute=function(array,itemSize){this.uuid=THREE.Math.generateUUID(),this.array=array,this.itemSize=itemSize,this.dynamic=!1,this.updateRange={offset:0,count:-1},this.version=0},THREE.BufferAttribute.prototype={constructor:THREE.BufferAttribute,get length(){return console.warn("THREE.BufferAttribute: .length has been deprecated. Please use .count."),this.array.length},get count(){return this.array.length/this.itemSize},set needsUpdate(value){value===!0&&this.version++},setDynamic:function(value){return this.dynamic=value,this},copy:function(source){return this.array=new source.array.constructor(source.array),this.itemSize=source.itemSize,this.dynamic=source.dynamic,this},copyAt:function(index1,attribute,index2){index1*=this.itemSize,index2*=attribute.itemSize;for(var i=0,l=this.itemSize;l>i;i++)this.array[index1+i]=attribute.array[index2+i];return this},copyArray:function(array){return this.array.set(array),this},copyColorsArray:function(colors){for(var array=this.array,offset=0,i=0,l=colors.length;l>i;i++){var color=colors[i];void 0===color&&(console.warn("THREE.BufferAttribute.copyColorsArray(): color is undefined",i),color=new THREE.Color),array[offset++]=color.r,array[offset++]=color.g,array[offset++]=color.b}return this},copyIndicesArray:function(indices){for(var array=this.array,offset=0,i=0,l=indices.length;l>i;i++){var index=indices[i];array[offset++]=index.a,array[offset++]=index.b,array[offset++]=index.c}return this},copyVector2sArray:function(vectors){for(var array=this.array,offset=0,i=0,l=vectors.length;l>i;i++){var vector=vectors[i];void 0===vector&&(console.warn("THREE.BufferAttribute.copyVector2sArray(): vector is undefined",i),vector=new THREE.Vector2),array[offset++]=vector.x,array[offset++]=vector.y}return this},copyVector3sArray:function(vectors){for(var array=this.array,offset=0,i=0,l=vectors.length;l>i;i++){var vector=vectors[i];void 0===vector&&(console.warn("THREE.BufferAttribute.copyVector3sArray(): vector is undefined",i),vector=new THREE.Vector3),array[offset++]=vector.x,array[offset++]=vector.y,array[offset++]=vector.z}return this},copyVector4sArray:function(vectors){for(var array=this.array,offset=0,i=0,l=vectors.length;l>i;i++){var vector=vectors[i];void 0===vector&&(console.warn("THREE.BufferAttribute.copyVector4sArray(): vector is undefined",i),vector=new THREE.Vector4),array[offset++]=vector.x,array[offset++]=vector.y,array[offset++]=vector.z,array[offset++]=vector.w}return this},set:function(value,offset){return void 0===offset&&(offset=0),this.array.set(value,offset),this},getX:function(index){return this.array[index*this.itemSize]},setX:function(index,x){return this.array[index*this.itemSize]=x,this},getY:function(index){return this.array[index*this.itemSize+1]},setY:function(index,y){return this.array[index*this.itemSize+1]=y,this},getZ:function(index){return this.array[index*this.itemSize+2]},setZ:function(index,z){return this.array[index*this.itemSize+2]=z,this},getW:function(index){return this.array[index*this.itemSize+3]},setW:function(index,w){return this.array[index*this.itemSize+3]=w,this},setXY:function(index,x,y){return index*=this.itemSize,this.array[index+0]=x,this.array[index+1]=y,this},setXYZ:function(index,x,y,z){return index*=this.itemSize,this.array[index+0]=x,this.array[index+1]=y,this.array[index+2]=z,this},setXYZW:function(index,x,y,z,w){return index*=this.itemSize,this.array[index+0]=x,this.array[index+1]=y,this.array[index+2]=z,this.array[index+3]=w,this},clone:function(){return(new this.constructor).copy(this)}},THREE.Int8Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Int8Array(array),itemSize)},THREE.Uint8Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Uint8Array(array),itemSize)},THREE.Uint8ClampedAttribute=function(array,itemSize){return new THREE.BufferAttribute(new Uint8ClampedArray(array),itemSize)},THREE.Int16Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Int16Array(array),itemSize)},THREE.Uint16Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Uint16Array(array),itemSize)},THREE.Int32Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Int32Array(array),itemSize)},THREE.Uint32Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Uint32Array(array),itemSize)},THREE.Float32Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Float32Array(array),itemSize)},THREE.Float64Attribute=function(array,itemSize){return new THREE.BufferAttribute(new Float64Array(array),itemSize)},THREE.DynamicBufferAttribute=function(array,itemSize){return console.warn("THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead."),new THREE.BufferAttribute(array,itemSize).setDynamic(!0)},THREE.InstancedBufferAttribute=function(array,itemSize,meshPerAttribute){THREE.BufferAttribute.call(this,array,itemSize),this.meshPerAttribute=meshPerAttribute||1},THREE.InstancedBufferAttribute.prototype=Object.create(THREE.BufferAttribute.prototype),THREE.InstancedBufferAttribute.prototype.constructor=THREE.InstancedBufferAttribute,THREE.InstancedBufferAttribute.prototype.copy=function(source){return THREE.BufferAttribute.prototype.copy.call(this,source),this.meshPerAttribute=source.meshPerAttribute,this},THREE.InterleavedBuffer=function(array,stride){this.uuid=THREE.Math.generateUUID(),this.array=array,this.stride=stride,this.dynamic=!1,this.updateRange={offset:0,count:-1},this.version=0},THREE.InterleavedBuffer.prototype={constructor:THREE.InterleavedBuffer,get length(){return this.array.length},get count(){return this.array.length/this.stride},set needsUpdate(value){value===!0&&this.version++},setDynamic:function(value){return this.dynamic=value,this},copy:function(source){this.array=new source.array.constructor(source.array),this.stride=source.stride,this.dynamic=source.dynamic},copyAt:function(index1,attribute,index2){index1*=this.stride,index2*=attribute.stride;for(var i=0,l=this.stride;l>i;i++)this.array[index1+i]=attribute.array[index2+i];return this},set:function(value,offset){return void 0===offset&&(offset=0),this.array.set(value,offset),this},clone:function(){return(new this.constructor).copy(this)}},THREE.InstancedInterleavedBuffer=function(array,stride,meshPerAttribute){THREE.InterleavedBuffer.call(this,array,stride),this.meshPerAttribute=meshPerAttribute||1},THREE.InstancedInterleavedBuffer.prototype=Object.create(THREE.InterleavedBuffer.prototype),THREE.InstancedInterleavedBuffer.prototype.constructor=THREE.InstancedInterleavedBuffer,THREE.InstancedInterleavedBuffer.prototype.copy=function(source){return THREE.InterleavedBuffer.prototype.copy.call(this,source),this.meshPerAttribute=source.meshPerAttribute,this},THREE.InterleavedBufferAttribute=function(interleavedBuffer,itemSize,offset){this.uuid=THREE.Math.generateUUID(),this.data=interleavedBuffer,this.itemSize=itemSize,this.offset=offset},THREE.InterleavedBufferAttribute.prototype={constructor:THREE.InterleavedBufferAttribute,get length(){return console.warn("THREE.BufferAttribute: .length has been deprecated. Please use .count."),this.array.length},get count(){return this.data.array.length/this.data.stride},setX:function(index,x){return this.data.array[index*this.data.stride+this.offset]=x,this},setY:function(index,y){return this.data.array[index*this.data.stride+this.offset+1]=y,this},setZ:function(index,z){return this.data.array[index*this.data.stride+this.offset+2]=z,this},setW:function(index,w){return this.data.array[index*this.data.stride+this.offset+3]=w,this},getX:function(index){return this.data.array[index*this.data.stride+this.offset]},getY:function(index){return this.data.array[index*this.data.stride+this.offset+1]},getZ:function(index){return this.data.array[index*this.data.stride+this.offset+2]},getW:function(index){return this.data.array[index*this.data.stride+this.offset+3]},setXY:function(index,x,y){return index=index*this.data.stride+this.offset,this.data.array[index+0]=x,this.data.array[index+1]=y,this},setXYZ:function(index,x,y,z){return index=index*this.data.stride+this.offset,this.data.array[index+0]=x,this.data.array[index+1]=y,this.data.array[index+2]=z,this},setXYZW:function(index,x,y,z,w){return index=index*this.data.stride+this.offset,this.data.array[index+0]=x,this.data.array[index+1]=y,this.data.array[index+2]=z,this.data.array[index+3]=w,this}},THREE.Geometry=function(){Object.defineProperty(this,"id",{value:THREE.GeometryIdCount++}),this.uuid=THREE.Math.generateUUID(),this.name="",this.type="Geometry",this.vertices=[],this.colors=[],this.faces=[],this.faceVertexUvs=[[]],this.morphTargets=[],this.morphNormals=[],this.skinWeights=[],this.skinIndices=[],this.lineDistances=[],this.boundingBox=null,this.boundingSphere=null,this.verticesNeedUpdate=!1,this.elementsNeedUpdate=!1,this.uvsNeedUpdate=!1,this.normalsNeedUpdate=!1,this.colorsNeedUpdate=!1,this.lineDistancesNeedUpdate=!1,this.groupsNeedUpdate=!1},THREE.Geometry.prototype={constructor:THREE.Geometry,applyMatrix:function(matrix){for(var normalMatrix=(new THREE.Matrix3).getNormalMatrix(matrix),i=0,il=this.vertices.length;il>i;i++){var vertex=this.vertices[i];vertex.applyMatrix4(matrix)}for(var i=0,il=this.faces.length;il>i;i++){var face=this.faces[i];face.normal.applyMatrix3(normalMatrix).normalize();for(var j=0,jl=face.vertexNormals.length;jl>j;j++)face.vertexNormals[j].applyMatrix3(normalMatrix).normalize(); +}null!==this.boundingBox&&this.computeBoundingBox(),null!==this.boundingSphere&&this.computeBoundingSphere(),this.verticesNeedUpdate=!0,this.normalsNeedUpdate=!0},rotateX:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationX(angle),this.applyMatrix(m1),this}}(),rotateY:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationY(angle),this.applyMatrix(m1),this}}(),rotateZ:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationZ(angle),this.applyMatrix(m1),this}}(),translate:function(){var m1;return function(x,y,z){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeTranslation(x,y,z),this.applyMatrix(m1),this}}(),scale:function(){var m1;return function(x,y,z){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeScale(x,y,z),this.applyMatrix(m1),this}}(),lookAt:function(){var obj;return function(vector){void 0===obj&&(obj=new THREE.Object3D),obj.lookAt(vector),obj.updateMatrix(),this.applyMatrix(obj.matrix)}}(),fromBufferGeometry:function(geometry){function addFace(a,b,c){var vertexNormals=void 0!==normals?[tempNormals[a].clone(),tempNormals[b].clone(),tempNormals[c].clone()]:[],vertexColors=void 0!==colors?[scope.colors[a].clone(),scope.colors[b].clone(),scope.colors[c].clone()]:[],face=new THREE.Face3(a,b,c,vertexNormals,vertexColors);scope.faces.push(face),void 0!==uvs&&scope.faceVertexUvs[0].push([tempUVs[a].clone(),tempUVs[b].clone(),tempUVs[c].clone()]),void 0!==uvs2&&scope.faceVertexUvs[1].push([tempUVs2[a].clone(),tempUVs2[b].clone(),tempUVs2[c].clone()])}var scope=this,indices=null!==geometry.index?geometry.index.array:void 0,attributes=geometry.attributes,vertices=attributes.position.array,normals=void 0!==attributes.normal?attributes.normal.array:void 0,colors=void 0!==attributes.color?attributes.color.array:void 0,uvs=void 0!==attributes.uv?attributes.uv.array:void 0,uvs2=void 0!==attributes.uv2?attributes.uv2.array:void 0;void 0!==uvs2&&(this.faceVertexUvs[1]=[]);for(var tempNormals=[],tempUVs=[],tempUVs2=[],i=0,j=0,k=0;i0)for(var i=0;ij;j+=3)addFace(indices[j],indices[j+1],indices[j+2]);else for(var i=0;if;f++){var face=this.faces[f],vA=this.vertices[face.a],vB=this.vertices[face.b],vC=this.vertices[face.c];cb.subVectors(vC,vB),ab.subVectors(vA,vB),cb.cross(ab),cb.normalize(),face.normal.copy(cb)}},computeVertexNormals:function(areaWeighted){var v,vl,f,fl,face,vertices;for(vertices=new Array(this.vertices.length),v=0,vl=this.vertices.length;vl>v;v++)vertices[v]=new THREE.Vector3;if(areaWeighted){var vA,vB,vC,cb=new THREE.Vector3,ab=new THREE.Vector3;for(f=0,fl=this.faces.length;fl>f;f++)face=this.faces[f],vA=this.vertices[face.a],vB=this.vertices[face.b],vC=this.vertices[face.c],cb.subVectors(vC,vB),ab.subVectors(vA,vB),cb.cross(ab),vertices[face.a].add(cb),vertices[face.b].add(cb),vertices[face.c].add(cb)}else for(f=0,fl=this.faces.length;fl>f;f++)face=this.faces[f],vertices[face.a].add(face.normal),vertices[face.b].add(face.normal),vertices[face.c].add(face.normal);for(v=0,vl=this.vertices.length;vl>v;v++)vertices[v].normalize();for(f=0,fl=this.faces.length;fl>f;f++){face=this.faces[f];var vertexNormals=face.vertexNormals;3===vertexNormals.length?(vertexNormals[0].copy(vertices[face.a]),vertexNormals[1].copy(vertices[face.b]),vertexNormals[2].copy(vertices[face.c])):(vertexNormals[0]=vertices[face.a].clone(),vertexNormals[1]=vertices[face.b].clone(),vertexNormals[2]=vertices[face.c].clone())}},computeMorphNormals:function(){var i,il,f,fl,face;for(f=0,fl=this.faces.length;fl>f;f++)for(face=this.faces[f],face.__originalFaceNormal?face.__originalFaceNormal.copy(face.normal):face.__originalFaceNormal=face.normal.clone(),face.__originalVertexNormals||(face.__originalVertexNormals=[]),i=0,il=face.vertexNormals.length;il>i;i++)face.__originalVertexNormals[i]?face.__originalVertexNormals[i].copy(face.vertexNormals[i]):face.__originalVertexNormals[i]=face.vertexNormals[i].clone();var tmpGeo=new THREE.Geometry;for(tmpGeo.faces=this.faces,i=0,il=this.morphTargets.length;il>i;i++){if(!this.morphNormals[i]){this.morphNormals[i]={},this.morphNormals[i].faceNormals=[],this.morphNormals[i].vertexNormals=[];var faceNormal,vertexNormals,dstNormalsFace=this.morphNormals[i].faceNormals,dstNormalsVertex=this.morphNormals[i].vertexNormals;for(f=0,fl=this.faces.length;fl>f;f++)faceNormal=new THREE.Vector3,vertexNormals={a:new THREE.Vector3,b:new THREE.Vector3,c:new THREE.Vector3},dstNormalsFace.push(faceNormal),dstNormalsVertex.push(vertexNormals)}var morphNormals=this.morphNormals[i];tmpGeo.vertices=this.morphTargets[i].vertices,tmpGeo.computeFaceNormals(),tmpGeo.computeVertexNormals();var faceNormal,vertexNormals;for(f=0,fl=this.faces.length;fl>f;f++)face=this.faces[f],faceNormal=morphNormals.faceNormals[f],vertexNormals=morphNormals.vertexNormals[f],faceNormal.copy(face.normal),vertexNormals.a.copy(face.vertexNormals[0]),vertexNormals.b.copy(face.vertexNormals[1]),vertexNormals.c.copy(face.vertexNormals[2])}for(f=0,fl=this.faces.length;fl>f;f++)face=this.faces[f],face.normal=face.__originalFaceNormal,face.vertexNormals=face.__originalVertexNormals},computeTangents:function(){console.warn("THREE.Geometry: .computeTangents() has been removed.")},computeLineDistances:function(){for(var d=0,vertices=this.vertices,i=0,il=vertices.length;il>i;i++)i>0&&(d+=vertices[i].distanceTo(vertices[i-1])),this.lineDistances[i]=d},computeBoundingBox:function(){null===this.boundingBox&&(this.boundingBox=new THREE.Box3),this.boundingBox.setFromPoints(this.vertices)},computeBoundingSphere:function(){null===this.boundingSphere&&(this.boundingSphere=new THREE.Sphere),this.boundingSphere.setFromPoints(this.vertices)},merge:function(geometry,matrix,materialIndexOffset){if(geometry instanceof THREE.Geometry==!1)return void console.error("THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.",geometry);var normalMatrix,vertexOffset=this.vertices.length,vertices1=this.vertices,vertices2=geometry.vertices,faces1=this.faces,faces2=geometry.faces,uvs1=this.faceVertexUvs[0],uvs2=geometry.faceVertexUvs[0];void 0===materialIndexOffset&&(materialIndexOffset=0),void 0!==matrix&&(normalMatrix=(new THREE.Matrix3).getNormalMatrix(matrix));for(var i=0,il=vertices2.length;il>i;i++){var vertex=vertices2[i],vertexCopy=vertex.clone();void 0!==matrix&&vertexCopy.applyMatrix4(matrix),vertices1.push(vertexCopy)}for(i=0,il=faces2.length;il>i;i++){var faceCopy,normal,color,face=faces2[i],faceVertexNormals=face.vertexNormals,faceVertexColors=face.vertexColors;faceCopy=new THREE.Face3(face.a+vertexOffset,face.b+vertexOffset,face.c+vertexOffset),faceCopy.normal.copy(face.normal),void 0!==normalMatrix&&faceCopy.normal.applyMatrix3(normalMatrix).normalize();for(var j=0,jl=faceVertexNormals.length;jl>j;j++)normal=faceVertexNormals[j].clone(),void 0!==normalMatrix&&normal.applyMatrix3(normalMatrix).normalize(),faceCopy.vertexNormals.push(normal);faceCopy.color.copy(face.color);for(var j=0,jl=faceVertexColors.length;jl>j;j++)color=faceVertexColors[j],faceCopy.vertexColors.push(color.clone());faceCopy.materialIndex=face.materialIndex+materialIndexOffset,faces1.push(faceCopy)}for(i=0,il=uvs2.length;il>i;i++){var uv=uvs2[i],uvCopy=[];if(void 0!==uv){for(var j=0,jl=uv.length;jl>j;j++)uvCopy.push(uv[j].clone());uvs1.push(uvCopy)}}},mergeMesh:function(mesh){return mesh instanceof THREE.Mesh==!1?void console.error("THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.",mesh):(mesh.matrixAutoUpdate&&mesh.updateMatrix(),void this.merge(mesh.geometry,mesh.matrix))},mergeVertices:function(){var v,key,i,il,face,indices,j,jl,verticesMap={},unique=[],changes=[],precisionPoints=4,precision=Math.pow(10,precisionPoints);for(i=0,il=this.vertices.length;il>i;i++)v=this.vertices[i],key=Math.round(v.x*precision)+"_"+Math.round(v.y*precision)+"_"+Math.round(v.z*precision),void 0===verticesMap[key]?(verticesMap[key]=i,unique.push(this.vertices[i]),changes[i]=unique.length-1):changes[i]=changes[verticesMap[key]];var faceIndicesToRemove=[];for(i=0,il=this.faces.length;il>i;i++){face=this.faces[i],face.a=changes[face.a],face.b=changes[face.b],face.c=changes[face.c],indices=[face.a,face.b,face.c];for(var dupIndex=-1,n=0;3>n;n++)if(indices[n]===indices[(n+1)%3]){dupIndex=n,faceIndicesToRemove.push(i);break}}for(i=faceIndicesToRemove.length-1;i>=0;i--){var idx=faceIndicesToRemove[i];for(this.faces.splice(idx,1),j=0,jl=this.faceVertexUvs.length;jl>j;j++)this.faceVertexUvs[j].splice(idx,1)}var diff=this.vertices.length-unique.length;return this.vertices=unique,diff},sortFacesByMaterialIndex:function(){function materialIndexSort(a,b){return a.materialIndex-b.materialIndex}for(var faces=this.faces,length=faces.length,i=0;length>i;i++)faces[i]._id=i;faces.sort(materialIndexSort);var newUvs1,newUvs2,uvs1=this.faceVertexUvs[0],uvs2=this.faceVertexUvs[1];uvs1&&uvs1.length===length&&(newUvs1=[]),uvs2&&uvs2.length===length&&(newUvs2=[]);for(var i=0;length>i;i++){var id=faces[i]._id;newUvs1&&newUvs1.push(uvs1[id]),newUvs2&&newUvs2.push(uvs2[id])}newUvs1&&(this.faceVertexUvs[0]=newUvs1),newUvs2&&(this.faceVertexUvs[1]=newUvs2)},toJSON:function(){function setBit(value,position,enabled){return enabled?value|1<0,hasFaceVertexNormal=face.vertexNormals.length>0,hasFaceColor=1!==face.color.r||1!==face.color.g||1!==face.color.b,hasFaceVertexColor=face.vertexColors.length>0,faceType=0;if(faceType=setBit(faceType,0,0),faceType=setBit(faceType,1,hasMaterial),faceType=setBit(faceType,2,hasFaceUv),faceType=setBit(faceType,3,hasFaceVertexUv),faceType=setBit(faceType,4,hasFaceNormal),faceType=setBit(faceType,5,hasFaceVertexNormal),faceType=setBit(faceType,6,hasFaceColor),faceType=setBit(faceType,7,hasFaceVertexColor),faces.push(faceType),faces.push(face.a,face.b,face.c),hasFaceVertexUv){var faceVertexUvs=this.faceVertexUvs[0][i];faces.push(getUvIndex(faceVertexUvs[0]),getUvIndex(faceVertexUvs[1]),getUvIndex(faceVertexUvs[2]))}if(hasFaceNormal&&faces.push(getNormalIndex(face.normal)),hasFaceVertexNormal){var vertexNormals=face.vertexNormals;faces.push(getNormalIndex(vertexNormals[0]),getNormalIndex(vertexNormals[1]),getNormalIndex(vertexNormals[2]))}if(hasFaceColor&&faces.push(getColorIndex(face.color)),hasFaceVertexColor){var vertexColors=face.vertexColors;faces.push(getColorIndex(vertexColors[0]),getColorIndex(vertexColors[1]),getColorIndex(vertexColors[2]))}}return data.data={},data.data.vertices=vertices,data.data.normals=normals,colors.length>0&&(data.data.colors=colors),uvs.length>0&&(data.data.uvs=[uvs]),data.data.faces=faces,data},clone:function(){return(new this.constructor).copy(this)},copy:function(source){this.vertices=[],this.faces=[],this.faceVertexUvs=[[]];for(var vertices=source.vertices,i=0,il=vertices.length;il>i;i++)this.vertices.push(vertices[i].clone());for(var faces=source.faces,i=0,il=faces.length;il>i;i++)this.faces.push(faces[i].clone());for(var i=0,il=source.faceVertexUvs.length;il>i;i++){var faceVertexUvs=source.faceVertexUvs[i];void 0===this.faceVertexUvs[i]&&(this.faceVertexUvs[i]=[]);for(var j=0,jl=faceVertexUvs.length;jl>j;j++){for(var uvs=faceVertexUvs[j],uvsCopy=[],k=0,kl=uvs.length;kl>k;k++){var uv=uvs[k];uvsCopy.push(uv.clone())}this.faceVertexUvs[i].push(uvsCopy)}}return this},dispose:function(){this.dispatchEvent({type:"dispose"})}},THREE.EventDispatcher.prototype.apply(THREE.Geometry.prototype),THREE.GeometryIdCount=0,THREE.DirectGeometry=function(){Object.defineProperty(this,"id",{value:THREE.GeometryIdCount++}),this.uuid=THREE.Math.generateUUID(),this.name="",this.type="DirectGeometry",this.indices=[],this.vertices=[],this.normals=[],this.colors=[],this.uvs=[],this.uvs2=[],this.groups=[],this.morphTargets={},this.skinWeights=[],this.skinIndices=[],this.boundingBox=null,this.boundingSphere=null,this.verticesNeedUpdate=!1,this.normalsNeedUpdate=!1,this.colorsNeedUpdate=!1,this.uvsNeedUpdate=!1,this.groupsNeedUpdate=!1},THREE.DirectGeometry.prototype={constructor:THREE.DirectGeometry,computeBoundingBox:THREE.Geometry.prototype.computeBoundingBox,computeBoundingSphere:THREE.Geometry.prototype.computeBoundingSphere,computeFaceNormals:function(){console.warn("THREE.DirectGeometry: computeFaceNormals() is not a method of this type of geometry.")},computeVertexNormals:function(){console.warn("THREE.DirectGeometry: computeVertexNormals() is not a method of this type of geometry.")},computeGroups:function(geometry){for(var group,materialIndex,groups=[],faces=geometry.faces,i=0;i0,hasFaceVertexUv2=faceVertexUvs[1]&&faceVertexUvs[1].length>0,morphTargets=geometry.morphTargets,morphTargetsLength=morphTargets.length;if(morphTargetsLength>0){for(var morphTargetsPosition=[],i=0;morphTargetsLength>i;i++)morphTargetsPosition[i]=[];this.morphTargets.position=morphTargetsPosition}var morphNormals=geometry.morphNormals,morphNormalsLength=morphNormals.length;if(morphNormalsLength>0){for(var morphTargetsNormal=[],i=0;morphNormalsLength>i;i++)morphTargetsNormal[i]=[];this.morphTargets.normal=morphTargetsNormal}for(var skinIndices=geometry.skinIndices,skinWeights=geometry.skinWeights,hasSkinIndices=skinIndices.length===vertices.length,hasSkinWeights=skinWeights.length===vertices.length,i=0;ij;j++){var morphTarget=morphTargets[j].vertices;morphTargetsPosition[j].push(morphTarget[face.a],morphTarget[face.b],morphTarget[face.c])}for(var j=0;morphNormalsLength>j;j++){var morphNormal=morphNormals[j].vertexNormals[i];morphTargetsNormal[j].push(morphNormal.a,morphNormal.b,morphNormal.c)}hasSkinIndices&&this.skinIndices.push(skinIndices[face.a],skinIndices[face.b],skinIndices[face.c]),hasSkinWeights&&this.skinWeights.push(skinWeights[face.a],skinWeights[face.b],skinWeights[face.c])}return this.computeGroups(geometry),this.verticesNeedUpdate=geometry.verticesNeedUpdate,this.normalsNeedUpdate=geometry.normalsNeedUpdate,this.colorsNeedUpdate=geometry.colorsNeedUpdate,this.uvsNeedUpdate=geometry.uvsNeedUpdate,this.groupsNeedUpdate=geometry.groupsNeedUpdate,this},dispose:function(){this.dispatchEvent({type:"dispose"})}},THREE.EventDispatcher.prototype.apply(THREE.DirectGeometry.prototype),THREE.BufferGeometry=function(){Object.defineProperty(this,"id",{value:THREE.GeometryIdCount++}),this.uuid=THREE.Math.generateUUID(),this.name="",this.type="BufferGeometry",this.index=null,this.attributes={},this.morphAttributes={},this.groups=[],this.boundingBox=null,this.boundingSphere=null,this.drawRange={start:0,count:1/0}},THREE.BufferGeometry.prototype={constructor:THREE.BufferGeometry,addIndex:function(index){console.warn("THREE.BufferGeometry: .addIndex() has been renamed to .setIndex()."),this.setIndex(index)},getIndex:function(){return this.index},setIndex:function(index){this.index=index},addAttribute:function(name,attribute){return attribute instanceof THREE.BufferAttribute==!1&&attribute instanceof THREE.InterleavedBufferAttribute==!1?(console.warn("THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )."),void this.addAttribute(name,new THREE.BufferAttribute(arguments[1],arguments[2]))):"index"===name?(console.warn("THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute."),void this.setIndex(attribute)):void(this.attributes[name]=attribute)},getAttribute:function(name){return this.attributes[name]},removeAttribute:function(name){delete this.attributes[name]},get drawcalls(){return console.error("THREE.BufferGeometry: .drawcalls has been renamed to .groups."),this.groups},get offsets(){return console.warn("THREE.BufferGeometry: .offsets has been renamed to .groups."),this.groups},addDrawCall:function(start,count,indexOffset){void 0!==indexOffset&&console.warn("THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset."),console.warn("THREE.BufferGeometry: .addDrawCall() is now .addGroup()."),this.addGroup(start,count)},clearDrawCalls:function(){console.warn("THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups()."),this.clearGroups()},addGroup:function(start,count,materialIndex){this.groups.push({start:start,count:count,materialIndex:void 0!==materialIndex?materialIndex:0})},clearGroups:function(){this.groups=[]},setDrawRange:function(start,count){this.drawRange.start=start,this.drawRange.count=count},applyMatrix:function(matrix){var position=this.attributes.position;void 0!==position&&(matrix.applyToVector3Array(position.array),position.needsUpdate=!0);var normal=this.attributes.normal;if(void 0!==normal){var normalMatrix=(new THREE.Matrix3).getNormalMatrix(matrix);normalMatrix.applyToVector3Array(normal.array),normal.needsUpdate=!0}null!==this.boundingBox&&this.computeBoundingBox(),null!==this.boundingSphere&&this.computeBoundingSphere()},rotateX:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationX(angle),this.applyMatrix(m1),this}}(),rotateY:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationY(angle),this.applyMatrix(m1),this}}(),rotateZ:function(){var m1;return function(angle){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeRotationZ(angle),this.applyMatrix(m1),this}}(),translate:function(){var m1;return function(x,y,z){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeTranslation(x,y,z),this.applyMatrix(m1),this}}(),scale:function(){var m1;return function(x,y,z){return void 0===m1&&(m1=new THREE.Matrix4),m1.makeScale(x,y,z),this.applyMatrix(m1),this}}(),lookAt:function(){var obj;return function(vector){void 0===obj&&(obj=new THREE.Object3D),obj.lookAt(vector),obj.updateMatrix(),this.applyMatrix(obj.matrix)}}(),center:function(){this.computeBoundingBox();var offset=this.boundingBox.center().negate();return this.translate(offset.x,offset.y,offset.z),offset},setFromObject:function(object){var geometry=object.geometry;if(object instanceof THREE.Points||object instanceof THREE.Line){var positions=new THREE.Float32Attribute(3*geometry.vertices.length,3),colors=new THREE.Float32Attribute(3*geometry.colors.length,3);if(this.addAttribute("position",positions.copyVector3sArray(geometry.vertices)),this.addAttribute("color",colors.copyColorsArray(geometry.colors)),geometry.lineDistances&&geometry.lineDistances.length===geometry.vertices.length){var lineDistances=new THREE.Float32Attribute(geometry.lineDistances.length,1);this.addAttribute("lineDistance",lineDistances.copyArray(geometry.lineDistances))}null!==geometry.boundingSphere&&(this.boundingSphere=geometry.boundingSphere.clone()),null!==geometry.boundingBox&&(this.boundingBox=geometry.boundingBox.clone())}else object instanceof THREE.Mesh&&geometry instanceof THREE.Geometry&&this.fromGeometry(geometry);return this},updateFromObject:function(object){var geometry=object.geometry;if(object instanceof THREE.Mesh){var direct=geometry.__directGeometry;if(void 0===direct)return this.fromGeometry(geometry);direct.verticesNeedUpdate=geometry.verticesNeedUpdate,direct.normalsNeedUpdate=geometry.normalsNeedUpdate,direct.colorsNeedUpdate=geometry.colorsNeedUpdate,direct.uvsNeedUpdate=geometry.uvsNeedUpdate,direct.groupsNeedUpdate=geometry.groupsNeedUpdate,geometry.verticesNeedUpdate=!1,geometry.normalsNeedUpdate=!1,geometry.colorsNeedUpdate=!1,geometry.uvsNeedUpdate=!1,geometry.groupsNeedUpdate=!1,geometry=direct}if(geometry.verticesNeedUpdate===!0){var attribute=this.attributes.position;void 0!==attribute&&(attribute.copyVector3sArray(geometry.vertices),attribute.needsUpdate=!0),geometry.verticesNeedUpdate=!1}if(geometry.normalsNeedUpdate===!0){var attribute=this.attributes.normal;void 0!==attribute&&(attribute.copyVector3sArray(geometry.normals),attribute.needsUpdate=!0),geometry.normalsNeedUpdate=!1}if(geometry.colorsNeedUpdate===!0){var attribute=this.attributes.color;void 0!==attribute&&(attribute.copyColorsArray(geometry.colors),attribute.needsUpdate=!0),geometry.colorsNeedUpdate=!1}if(geometry.uvsNeedUpdate){var attribute=this.attributes.uv;void 0!==attribute&&(attribute.copyVector2sArray(geometry.uvs),attribute.needsUpdate=!0),geometry.uvsNeedUpdate=!1}if(geometry.lineDistancesNeedUpdate){var attribute=this.attributes.lineDistance;void 0!==attribute&&(attribute.copyArray(geometry.lineDistances),attribute.needsUpdate=!0),geometry.lineDistancesNeedUpdate=!1}return geometry.groupsNeedUpdate&&(geometry.computeGroups(object.geometry),this.groups=geometry.groups,geometry.groupsNeedUpdate=!1),this},fromGeometry:function(geometry){return geometry.__directGeometry=(new THREE.DirectGeometry).fromGeometry(geometry),this.fromDirectGeometry(geometry.__directGeometry)},fromDirectGeometry:function(geometry){var positions=new Float32Array(3*geometry.vertices.length);if(this.addAttribute("position",new THREE.BufferAttribute(positions,3).copyVector3sArray(geometry.vertices)),geometry.normals.length>0){var normals=new Float32Array(3*geometry.normals.length);this.addAttribute("normal",new THREE.BufferAttribute(normals,3).copyVector3sArray(geometry.normals))}if(geometry.colors.length>0){var colors=new Float32Array(3*geometry.colors.length);this.addAttribute("color",new THREE.BufferAttribute(colors,3).copyColorsArray(geometry.colors))}if(geometry.uvs.length>0){var uvs=new Float32Array(2*geometry.uvs.length);this.addAttribute("uv",new THREE.BufferAttribute(uvs,2).copyVector2sArray(geometry.uvs))}if(geometry.uvs2.length>0){var uvs2=new Float32Array(2*geometry.uvs2.length);this.addAttribute("uv2",new THREE.BufferAttribute(uvs2,2).copyVector2sArray(geometry.uvs2))}if(geometry.indices.length>0){var TypeArray=geometry.vertices.length>65535?Uint32Array:Uint16Array,indices=new TypeArray(3*geometry.indices.length);this.setIndex(new THREE.BufferAttribute(indices,1).copyIndicesArray(geometry.indices))}this.groups=geometry.groups;for(var name in geometry.morphTargets){for(var array=[],morphTargets=geometry.morphTargets[name],i=0,l=morphTargets.length;l>i;i++){var morphTarget=morphTargets[i],attribute=new THREE.Float32Attribute(3*morphTarget.length,3);array.push(attribute.copyVector3sArray(morphTarget))}this.morphAttributes[name]=array}if(geometry.skinIndices.length>0){var skinIndices=new THREE.Float32Attribute(4*geometry.skinIndices.length,4);this.addAttribute("skinIndex",skinIndices.copyVector4sArray(geometry.skinIndices))}if(geometry.skinWeights.length>0){var skinWeights=new THREE.Float32Attribute(4*geometry.skinWeights.length,4);this.addAttribute("skinWeight",skinWeights.copyVector4sArray(geometry.skinWeights))}return null!==geometry.boundingSphere&&(this.boundingSphere=geometry.boundingSphere.clone()),null!==geometry.boundingBox&&(this.boundingBox=geometry.boundingBox.clone()),this},computeBoundingBox:function(){var vector=new THREE.Vector3;return function(){null===this.boundingBox&&(this.boundingBox=new THREE.Box3);var positions=this.attributes.position.array;if(positions){var bb=this.boundingBox;bb.makeEmpty();for(var i=0,il=positions.length;il>i;i+=3)vector.fromArray(positions,i),bb.expandByPoint(vector)}void 0!==positions&&0!==positions.length||(this.boundingBox.min.set(0,0,0),this.boundingBox.max.set(0,0,0)),(isNaN(this.boundingBox.min.x)||isNaN(this.boundingBox.min.y)||isNaN(this.boundingBox.min.z))&&console.error('THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.',this)}}(),computeBoundingSphere:function(){var box=new THREE.Box3,vector=new THREE.Vector3;return function(){null===this.boundingSphere&&(this.boundingSphere=new THREE.Sphere);var positions=this.attributes.position.array;if(positions){box.makeEmpty();for(var center=this.boundingSphere.center,i=0,il=positions.length;il>i;i+=3)vector.fromArray(positions,i),box.expandByPoint(vector);box.center(center);for(var maxRadiusSq=0,i=0,il=positions.length;il>i;i+=3)vector.fromArray(positions,i),maxRadiusSq=Math.max(maxRadiusSq,center.distanceToSquared(vector));this.boundingSphere.radius=Math.sqrt(maxRadiusSq),isNaN(this.boundingSphere.radius)&&console.error('THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.',this)}}}(),computeFaceNormals:function(){},computeVertexNormals:function(){var index=this.index,attributes=this.attributes,groups=this.groups;if(attributes.position){var positions=attributes.position.array;if(void 0===attributes.normal)this.addAttribute("normal",new THREE.BufferAttribute(new Float32Array(positions.length),3));else for(var normals=attributes.normal.array,i=0,il=normals.length;il>i;i++)normals[i]=0;var vA,vB,vC,normals=attributes.normal.array,pA=new THREE.Vector3,pB=new THREE.Vector3,pC=new THREE.Vector3,cb=new THREE.Vector3,ab=new THREE.Vector3;if(index){var indices=index.array;0===groups.length&&this.addGroup(0,indices.length);for(var j=0,jl=groups.length;jl>j;++j)for(var group=groups[j],start=group.start,count=group.count,i=start,il=start+count;il>i;i+=3)vA=3*indices[i+0],vB=3*indices[i+1],vC=3*indices[i+2],pA.fromArray(positions,vA),pB.fromArray(positions,vB),pC.fromArray(positions,vC),cb.subVectors(pC,pB),ab.subVectors(pA,pB),cb.cross(ab),normals[vA]+=cb.x,normals[vA+1]+=cb.y,normals[vA+2]+=cb.z,normals[vB]+=cb.x,normals[vB+1]+=cb.y,normals[vB+2]+=cb.z,normals[vC]+=cb.x,normals[vC+1]+=cb.y,normals[vC+2]+=cb.z}else for(var i=0,il=positions.length;il>i;i+=9)pA.fromArray(positions,i),pB.fromArray(positions,i+3),pC.fromArray(positions,i+6),cb.subVectors(pC,pB),ab.subVectors(pA,pB),cb.cross(ab),normals[i]=cb.x,normals[i+1]=cb.y,normals[i+2]=cb.z,normals[i+3]=cb.x,normals[i+4]=cb.y,normals[i+5]=cb.z,normals[i+6]=cb.x,normals[i+7]=cb.y,normals[i+8]=cb.z;this.normalizeNormals(),attributes.normal.needsUpdate=!0}},computeTangents:function(){console.warn("THREE.BufferGeometry: .computeTangents() has been removed.")},computeOffsets:function(size){console.warn("THREE.BufferGeometry: .computeOffsets() has been removed.")},merge:function(geometry,offset){if(geometry instanceof THREE.BufferGeometry==!1)return void console.error("THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.",geometry);void 0===offset&&(offset=0);var attributes=this.attributes;for(var key in attributes)if(void 0!==geometry.attributes[key])for(var attribute1=attributes[key],attributeArray1=attribute1.array,attribute2=geometry.attributes[key],attributeArray2=attribute2.array,attributeSize=attribute2.itemSize,i=0,j=attributeSize*offset;ii;i+=3)x=normals[i],y=normals[i+1],z=normals[i+2],n=1/Math.sqrt(x*x+y*y+z*z),normals[i]*=n,normals[i+1]*=n,normals[i+2]*=n},toJSON:function(){var data={metadata:{version:4.4,type:"BufferGeometry",generator:"BufferGeometry.toJSON"}};if(data.uuid=this.uuid,data.type=this.type,""!==this.name&&(data.name=this.name),void 0!==this.parameters){var parameters=this.parameters;for(var key in parameters)void 0!==parameters[key]&&(data[key]=parameters[key]);return data}data.data={attributes:{}};var index=this.index;if(null!==index){var array=Array.prototype.slice.call(index.array);data.data.index={type:index.array.constructor.name,array:array}}var attributes=this.attributes;for(var key in attributes){var attribute=attributes[key],array=Array.prototype.slice.call(attribute.array);data.data.attributes[key]={itemSize:attribute.itemSize,type:attribute.array.constructor.name,array:array}}var groups=this.groups;groups.length>0&&(data.data.groups=JSON.parse(JSON.stringify(groups)));var boundingSphere=this.boundingSphere;return null!==boundingSphere&&(data.data.boundingSphere={center:boundingSphere.center.toArray(),radius:boundingSphere.radius}),data},clone:function(){return(new this.constructor).copy(this); +},copy:function(source){var index=source.index;null!==index&&this.setIndex(index.clone());var attributes=source.attributes;for(var name in attributes){var attribute=attributes[name];this.addAttribute(name,attribute.clone())}for(var groups=source.groups,i=0,l=groups.length;l>i;i++){var group=groups[i];this.addGroup(group.start,group.count)}return this},dispose:function(){this.dispatchEvent({type:"dispose"})}},THREE.EventDispatcher.prototype.apply(THREE.BufferGeometry.prototype),THREE.BufferGeometry.MaxIndex=65535,THREE.InstancedBufferGeometry=function(){THREE.BufferGeometry.call(this),this.type="InstancedBufferGeometry",this.maxInstancedCount=void 0},THREE.InstancedBufferGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.InstancedBufferGeometry.prototype.constructor=THREE.InstancedBufferGeometry,THREE.InstancedBufferGeometry.prototype.addGroup=function(start,count,instances){this.groups.push({start:start,count:count,instances:instances})},THREE.InstancedBufferGeometry.prototype.copy=function(source){var index=source.index;null!==index&&this.setIndex(index.clone());var attributes=source.attributes;for(var name in attributes){var attribute=attributes[name];this.addAttribute(name,attribute.clone())}for(var groups=source.groups,i=0,l=groups.length;l>i;i++){var group=groups[i];this.addGroup(group.start,group.count,group.instances)}return this},THREE.EventDispatcher.prototype.apply(THREE.InstancedBufferGeometry.prototype),THREE.AnimationAction=function(clip,startTime,timeScale,weight,loop){if(void 0===clip)throw new Error("clip is null");this.clip=clip,this.localRoot=null,this.startTime=startTime||0,this.timeScale=timeScale||1,this.weight=weight||1,this.loop=loop||THREE.LoopRepeat,this.loopCount=0,this.enabled=!0,this.actionTime=-this.startTime,this.clipTime=0,this.propertyBindings=[]},THREE.AnimationAction.prototype={constructor:THREE.AnimationAction,setLocalRoot:function(localRoot){return this.localRoot=localRoot,this},updateTime:function(clipDeltaTime){var previousClipTime=this.clipTime,previousLoopCount=this.loopCount,duration=(this.actionTime,this.clip.duration);if(this.actionTime=this.actionTime+clipDeltaTime,this.loop===THREE.LoopOnce)return this.loopCount=0,this.clipTime=Math.min(Math.max(this.actionTime,0),duration),this.clipTime!==previousClipTime&&(this.clipTime===duration?this.mixer.dispatchEvent({type:"finished",action:this,direction:1}):0===this.clipTime&&this.mixer.dispatchEvent({type:"finished",action:this,direction:-1})),this.clipTime;this.loopCount=Math.floor(this.actionTime/duration);var newClipTime=this.actionTime-this.loopCount*duration;return newClipTime%=duration,this.loop==THREE.LoopPingPong&&1===Math.abs(this.loopCount%2)&&(newClipTime=duration-newClipTime),this.clipTime=newClipTime,this.loopCount!==previousLoopCount&&this.mixer.dispatchEvent({type:"loop",action:this,loopDelta:this.loopCount-this.loopCount}),this.clipTime},syncWith:function(action){return this.actionTime=action.actionTime,this.timeScale=action.timeScale,this},warpToDuration:function(duration){return this.timeScale=this.clip.duration/duration,this},init:function(time){return this.clipTime=time-this.startTime,this},update:function(clipDeltaTime){this.updateTime(clipDeltaTime);var clipResults=this.clip.getAt(this.clipTime);return clipResults},getTimeScaleAt:function(time){return this.timeScale.getAt?this.timeScale.getAt(time):this.timeScale},getWeightAt:function(time){return this.weight.getAt?this.weight.getAt(time):this.weight}},THREE.AnimationClip=function(name,duration,tracks){if(this.name=name,this.tracks=tracks,this.duration=void 0!==duration?duration:-1,this.duration<0)for(var i=0;ii;i++){var keys=[];keys.push({time:(i+numMorphTargets-1)%numMorphTargets,value:0}),keys.push({time:i,value:1}),keys.push({time:(i+1)%numMorphTargets,value:0}),keys.sort(THREE.KeyframeTrack.keyComparer),0===keys[0].time&&keys.push({time:numMorphTargets,value:keys[0].value}),tracks.push(new THREE.NumberKeyframeTrack(".morphTargetInfluences["+morphTargetSequence[i].name+"]",keys).scale(1/fps))}return new THREE.AnimationClip(name,-1,tracks)},THREE.AnimationClip.findByName=function(clipArray,name){for(var i=0;ii;i++){var morphTarget=morphTargets[i],parts=morphTarget.name.match(pattern);if(parts&&parts.length>1){var name=parts[1],animationMorphTargets=animationToMorphTargets[name];animationMorphTargets||(animationToMorphTargets[name]=animationMorphTargets=[]),animationMorphTargets.push(morphTarget)}}var clips=[];for(var name in animationToMorphTargets)clips.push(THREE.AnimationClip.CreateFromMorphTargetSequence(name,animationToMorphTargets[name],fps));return clips},THREE.AnimationClip.parse=function(json){for(var tracks=[],i=0;i0?new trackType(trackName,keys):null},tracks=[],clipName=animation.name||"default",duration=animation.length||-1,fps=animation.fps||30,hierarchyTracks=animation.hierarchy||[],h=0;halpha?a:b},lerp_boolean_immediate:function(a,b,alpha){return a},lerp_string:function(a,b,alpha){return.5>alpha?a:b},lerp_string_immediate:function(a,b,alpha){return a},getLerpFunc:function(exemplarValue,interTrack){if(void 0===exemplarValue||null===exemplarValue)throw new Error("examplarValue is null");var typeName=typeof exemplarValue;switch(typeName){case"object":if(exemplarValue.lerp)return THREE.AnimationUtils.lerp_object;if(exemplarValue.slerp)return THREE.AnimationUtils.slerp_object;break;case"number":return THREE.AnimationUtils.lerp_number;case"boolean":return interTrack?THREE.AnimationUtils.lerp_boolean:THREE.AnimationUtils.lerp_boolean_immediate;case"string":return interTrack?THREE.AnimationUtils.lerp_string:THREE.AnimationUtils.lerp_string_immediate}}},THREE.KeyframeTrack=function(name,keys){if(void 0===name)throw new Error("track name is undefined");if(void 0===keys||0===keys.length)throw new Error("no keys in track named "+name);this.name=name,this.keys=keys,this.lastIndex=0,this.validate(),this.optimize()},THREE.KeyframeTrack.prototype={constructor:THREE.KeyframeTrack,getAt:function(time){for(;this.lastIndex=this.keys[this.lastIndex].time;)this.lastIndex++;for(;this.lastIndex>0&&time=this.keys.length)return this.setResult(this.keys[this.keys.length-1].value),this.result;if(0===this.lastIndex)return this.setResult(this.keys[0].value),this.result;var prevKey=this.keys[this.lastIndex-1];if(this.setResult(prevKey.value),prevKey.constantToNext)return this.result;var currentKey=this.keys[this.lastIndex],alpha=(time-prevKey.time)/(currentKey.time-prevKey.time);return this.result=this.lerpValues(this.result,currentKey.value,alpha),this.result},shift:function(timeOffset){if(0!==timeOffset)for(var i=0;i0&&this.keys[i]>=endTime;i++)lastKeysToRemove++;return firstKeysToRemove+lastKeysToRemove>0&&(this.keys=this.keys.splice(firstKeysToRemove,this.keys.length-lastKeysToRemove-firstKeysToRemove)),this},validate:function(){var prevKey=null;if(0===this.keys.length)return void console.error(" track is empty, no keys",this);for(var i=0;icurrKey.time)return void console.error(" key.time is less than previous key time, out of order keys",this,i,currKey,prevKey);prevKey=currKey}return this},optimize:function(){var newKeys=[],prevKey=this.keys[0];newKeys.push(prevKey);for(var i=(THREE.AnimationUtils.getEqualsFunc(prevKey.value),1);i0&&(null===this.cumulativeValue&&(this.cumulativeValue=THREE.AnimationUtils.clone(value)),this.cumulativeWeight=weight);else{var lerpAlpha=weight/(this.cumulativeWeight+weight);this.cumulativeValue=this.lerpValue(this.cumulativeValue,value,lerpAlpha),this.cumulativeWeight+=weight}},unbind:function(){this.isBound&&(this.setValue(this.originalValue),this.setValue=null,this.getValue=null,this.lerpValue=null,this.equalsValue=null,this.triggerDirty=null,this.isBound=!1)},bind:function(){if(!this.isBound){var targetObject=this.node;if(!targetObject)return void console.error(" trying to update node for track: "+this.trackName+" but it wasn't found.");if(this.objectName){if("materials"===this.objectName){if(!targetObject.material)return void console.error(" can not bind to material as node does not have a material",this);if(!targetObject.material.materials)return void console.error(" can not bind to material.materials as node.material does not have a materials array",this);targetObject=targetObject.material.materials}else if("bones"===this.objectName){if(!targetObject.skeleton)return void console.error(" can not bind to bones as node does not have a skeleton",this);targetObject=targetObject.skeleton.bones;for(var i=0;i0){if(this.cumulativeWeight<1){var remainingWeight=1-this.cumulativeWeight,lerpAlpha=remainingWeight/(this.cumulativeWeight+remainingWeight);this.cumulativeValue=this.lerpValue(this.cumulativeValue,this.originalValue,lerpAlpha)}var valueChanged=this.setValue(this.cumulativeValue);valueChanged&&this.triggerDirty&&this.triggerDirty(),this.cumulativeValue=null,this.cumulativeWeight=0}}},THREE.PropertyBinding.parseTrackName=function(trackName){var re=/^(([\w]+\/)*)([\w-\d]+)?(\.([\w]+)(\[([\w\d\[\]\_. ]+)\])?)?(\.([\w.]+)(\[([\w\d\[\]\_. ]+)\])?)$/,matches=re.exec(trackName);if(!matches)throw new Error("cannot parse trackName at all: "+trackName);matches.index===re.lastIndex&&re.lastIndex++;var results={directoryName:matches[1],nodeName:matches[3],objectName:matches[5],objectIndex:matches[7],propertyName:matches[9],propertyIndex:matches[11]};if(null===results.propertyName||0===results.propertyName.length)throw new Error("can not parse propertyName from trackName: "+trackName);return results},THREE.PropertyBinding.findNode=function(root,nodeName){function searchSkeleton(skeleton){for(var i=0;ialpha?value0:value1},THREE.StringKeyframeTrack.prototype.compareValues=function(value0,value1){return value0===value1},THREE.StringKeyframeTrack.prototype.clone=function(){for(var clonedKeys=[],i=0;ialpha?value0:value1},THREE.BooleanKeyframeTrack.prototype.compareValues=function(value0,value1){return value0===value1},THREE.BooleanKeyframeTrack.prototype.clone=function(){for(var clonedKeys=[],i=0;ii;i+=2){var regex=handlers[i],loader=handlers[i+1];if(regex.test(file))return loader}return null}},THREE.XHRLoader=function(manager){this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager},THREE.XHRLoader.prototype={constructor:THREE.XHRLoader,load:function(url,onLoad,onProgress,onError){var scope=this,cached=THREE.Cache.get(url);if(void 0!==cached)return onLoad&&setTimeout(function(){onLoad(cached)},0),cached;var request=new XMLHttpRequest;return request.open("GET",url,!0),request.addEventListener("load",function(event){var response=event.target.response;THREE.Cache.add(url,response),onLoad&&onLoad(response),scope.manager.itemEnd(url)},!1),void 0!==onProgress&&request.addEventListener("progress",function(event){onProgress(event)},!1),request.addEventListener("error",function(event){onError&&onError(event),scope.manager.itemError(url)},!1),void 0!==this.crossOrigin&&(request.crossOrigin=this.crossOrigin),void 0!==this.responseType&&(request.responseType=this.responseType),void 0!==this.withCredentials&&(request.withCredentials=this.withCredentials),request.send(null),scope.manager.itemStart(url),request},setResponseType:function(value){this.responseType=value},setCrossOrigin:function(value){this.crossOrigin=value},setWithCredentials:function(value){this.withCredentials=value}},THREE.ImageLoader=function(manager){this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager},THREE.ImageLoader.prototype={constructor:THREE.ImageLoader,load:function(url,onLoad,onProgress,onError){var scope=this,cached=THREE.Cache.get(url);if(void 0!==cached)return scope.manager.itemStart(url),onLoad?setTimeout(function(){onLoad(cached),scope.manager.itemEnd(url)},0):scope.manager.itemEnd(url),cached;var image=document.createElement("img");return image.addEventListener("load",function(event){THREE.Cache.add(url,this),onLoad&&onLoad(this),scope.manager.itemEnd(url)},!1),void 0!==onProgress&&image.addEventListener("progress",function(event){onProgress(event)},!1),image.addEventListener("error",function(event){onError&&onError(event),scope.manager.itemError(url)},!1),void 0!==this.crossOrigin&&(image.crossOrigin=this.crossOrigin),scope.manager.itemStart(url),image.src=url,image},setCrossOrigin:function(value){this.crossOrigin=value}},THREE.JSONLoader=function(manager){"boolean"==typeof manager&&(console.warn("THREE.JSONLoader: showStatus parameter has been removed from constructor."),manager=void 0),this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager,this.withCredentials=!1},THREE.JSONLoader.prototype={constructor:THREE.JSONLoader,get statusDomElement(){return void 0===this._statusDomElement&&(this._statusDomElement=document.createElement("div")),console.warn("THREE.JSONLoader: .statusDomElement has been removed."),this._statusDomElement},load:function(url,onLoad,onProgress,onError){var scope=this,texturePath=this.texturePath&&"string"==typeof this.texturePath?this.texturePath:THREE.Loader.prototype.extractUrlBase(url),loader=new THREE.XHRLoader(this.manager);loader.setCrossOrigin(this.crossOrigin),loader.setWithCredentials(this.withCredentials),loader.load(url,function(text){var json=JSON.parse(text),metadata=json.metadata;if(void 0!==metadata){if("object"===metadata.type)return void console.error("THREE.JSONLoader: "+url+" should be loaded with THREE.ObjectLoader instead.");if("scene"===metadata.type)return void console.error("THREE.JSONLoader: "+url+" should be loaded with THREE.SceneLoader instead.")}var object=scope.parse(json,texturePath);onLoad(object.geometry,object.materials)})},setCrossOrigin:function(value){this.crossOrigin=value},setTexturePath:function(value){this.texturePath=value},parse:function(json,texturePath){function parseModel(scale){function isBitSet(value,position){return value&1<i;i++)geometry.faceVertexUvs[i]=[]}for(offset=0,zLength=vertices.length;zLength>offset;)vertex=new THREE.Vector3,vertex.x=vertices[offset++]*scale,vertex.y=vertices[offset++]*scale,vertex.z=vertices[offset++]*scale,geometry.vertices.push(vertex);for(offset=0,zLength=faces.length;zLength>offset;)if(type=faces[offset++],isQuad=isBitSet(type,0),hasMaterial=isBitSet(type,1),hasFaceVertexUv=isBitSet(type,3),hasFaceNormal=isBitSet(type,4),hasFaceVertexNormal=isBitSet(type,5),hasFaceColor=isBitSet(type,6),hasFaceVertexColor=isBitSet(type,7),isQuad){if(faceA=new THREE.Face3,faceA.a=faces[offset],faceA.b=faces[offset+1],faceA.c=faces[offset+3],faceB=new THREE.Face3,faceB.a=faces[offset+1],faceB.b=faces[offset+2],faceB.c=faces[offset+3],offset+=4,hasMaterial&&(materialIndex=faces[offset++],faceA.materialIndex=materialIndex,faceB.materialIndex=materialIndex),fi=geometry.faces.length,hasFaceVertexUv)for(i=0;nUvLayers>i;i++)for(uvLayer=json.uvs[i],geometry.faceVertexUvs[i][fi]=[],geometry.faceVertexUvs[i][fi+1]=[],j=0;4>j;j++)uvIndex=faces[offset++],u=uvLayer[2*uvIndex],v=uvLayer[2*uvIndex+1],uv=new THREE.Vector2(u,v),2!==j&&geometry.faceVertexUvs[i][fi].push(uv),0!==j&&geometry.faceVertexUvs[i][fi+1].push(uv);if(hasFaceNormal&&(normalIndex=3*faces[offset++],faceA.normal.set(normals[normalIndex++],normals[normalIndex++],normals[normalIndex]),faceB.normal.copy(faceA.normal)),hasFaceVertexNormal)for(i=0;4>i;i++)normalIndex=3*faces[offset++],normal=new THREE.Vector3(normals[normalIndex++],normals[normalIndex++],normals[normalIndex]),2!==i&&faceA.vertexNormals.push(normal),0!==i&&faceB.vertexNormals.push(normal);if(hasFaceColor&&(colorIndex=faces[offset++],hex=colors[colorIndex],faceA.color.setHex(hex),faceB.color.setHex(hex)),hasFaceVertexColor)for(i=0;4>i;i++)colorIndex=faces[offset++],hex=colors[colorIndex],2!==i&&faceA.vertexColors.push(new THREE.Color(hex)),0!==i&&faceB.vertexColors.push(new THREE.Color(hex));geometry.faces.push(faceA),geometry.faces.push(faceB)}else{if(face=new THREE.Face3,face.a=faces[offset++],face.b=faces[offset++],face.c=faces[offset++],hasMaterial&&(materialIndex=faces[offset++],face.materialIndex=materialIndex),fi=geometry.faces.length,hasFaceVertexUv)for(i=0;nUvLayers>i;i++)for(uvLayer=json.uvs[i],geometry.faceVertexUvs[i][fi]=[],j=0;3>j;j++)uvIndex=faces[offset++],u=uvLayer[2*uvIndex],v=uvLayer[2*uvIndex+1],uv=new THREE.Vector2(u,v),geometry.faceVertexUvs[i][fi].push(uv);if(hasFaceNormal&&(normalIndex=3*faces[offset++],face.normal.set(normals[normalIndex++],normals[normalIndex++],normals[normalIndex])),hasFaceVertexNormal)for(i=0;3>i;i++)normalIndex=3*faces[offset++],normal=new THREE.Vector3(normals[normalIndex++],normals[normalIndex++],normals[normalIndex]),face.vertexNormals.push(normal);if(hasFaceColor&&(colorIndex=faces[offset++],face.color.setHex(colors[colorIndex])),hasFaceVertexColor)for(i=0;3>i;i++)colorIndex=faces[offset++],face.vertexColors.push(new THREE.Color(colors[colorIndex]));geometry.faces.push(face)}}function parseSkin(){var influencesPerVertex=void 0!==json.influencesPerVertex?json.influencesPerVertex:2;if(json.skinWeights)for(var i=0,l=json.skinWeights.length;l>i;i+=influencesPerVertex){var x=json.skinWeights[i],y=influencesPerVertex>1?json.skinWeights[i+1]:0,z=influencesPerVertex>2?json.skinWeights[i+2]:0,w=influencesPerVertex>3?json.skinWeights[i+3]:0;geometry.skinWeights.push(new THREE.Vector4(x,y,z,w))}if(json.skinIndices)for(var i=0,l=json.skinIndices.length;l>i;i+=influencesPerVertex){var a=json.skinIndices[i],b=influencesPerVertex>1?json.skinIndices[i+1]:0,c=influencesPerVertex>2?json.skinIndices[i+2]:0,d=influencesPerVertex>3?json.skinIndices[i+3]:0;geometry.skinIndices.push(new THREE.Vector4(a,b,c,d))}geometry.bones=json.bones,geometry.bones&&geometry.bones.length>0&&(geometry.skinWeights.length!==geometry.skinIndices.length||geometry.skinIndices.length!==geometry.vertices.length)&&console.warn("When skinning, number of vertices ("+geometry.vertices.length+"), skinIndices ("+geometry.skinIndices.length+"), and skinWeights ("+geometry.skinWeights.length+") should match.")}function parseMorphing(scale){if(void 0!==json.morphTargets)for(var i=0,l=json.morphTargets.length;l>i;i++){geometry.morphTargets[i]={},geometry.morphTargets[i].name=json.morphTargets[i].name,geometry.morphTargets[i].vertices=[];for(var dstVertices=geometry.morphTargets[i].vertices,srcVertices=json.morphTargets[i].vertices,v=0,vl=srcVertices.length;vl>v;v+=3){var vertex=new THREE.Vector3;vertex.x=srcVertices[v]*scale,vertex.y=srcVertices[v+1]*scale,vertex.z=srcVertices[v+2]*scale,dstVertices.push(vertex)}}if(void 0!==json.morphColors&&json.morphColors.length>0){console.warn('THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.');for(var faces=geometry.faces,morphColors=json.morphColors[0].colors,i=0,l=faces.length;l>i;i++)faces[i].color.fromArray(morphColors,3*i)}}function parseAnimations(){var outputAnimations=[],animations=[];void 0!==json.animation&&animations.push(json.animation),void 0!==json.animations&&(json.animations.length?animations=animations.concat(json.animations):animations.push(json.animations));for(var i=0;i0&&(geometry.animations=outputAnimations)}var geometry=new THREE.Geometry,scale=void 0!==json.scale?1/json.scale:1;if(parseModel(scale),parseSkin(),parseMorphing(scale),parseAnimations(),geometry.computeFaceNormals(),geometry.computeBoundingSphere(),void 0===json.materials||0===json.materials.length)return{geometry:geometry};var materials=THREE.Loader.prototype.initMaterials(json.materials,texturePath,this.crossOrigin);return{geometry:geometry,materials:materials}}},THREE.LoadingManager=function(onLoad,onProgress,onError){var scope=this,isLoading=!1,itemsLoaded=0,itemsTotal=0;this.onStart=void 0,this.onLoad=onLoad,this.onProgress=onProgress,this.onError=onError,this.itemStart=function(url){itemsTotal++,isLoading===!1&&void 0!==scope.onStart&&scope.onStart(url,itemsLoaded,itemsTotal),isLoading=!0},this.itemEnd=function(url){itemsLoaded++,void 0!==scope.onProgress&&scope.onProgress(url,itemsLoaded,itemsTotal),itemsLoaded===itemsTotal&&(isLoading=!1,void 0!==scope.onLoad&&scope.onLoad())},this.itemError=function(url){void 0!==scope.onError&&scope.onError(url)}},THREE.DefaultLoadingManager=new THREE.LoadingManager,THREE.BufferGeometryLoader=function(manager){this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager},THREE.BufferGeometryLoader.prototype={constructor:THREE.BufferGeometryLoader,load:function(url,onLoad,onProgress,onError){var scope=this,loader=new THREE.XHRLoader(scope.manager);loader.setCrossOrigin(this.crossOrigin),loader.load(url,function(text){onLoad(scope.parse(JSON.parse(text)))},onProgress,onError)},setCrossOrigin:function(value){this.crossOrigin=value},parse:function(json){var geometry=new THREE.BufferGeometry,index=json.data.index;if(void 0!==index){var typedArray=new self[index.type](index.array);geometry.setIndex(new THREE.BufferAttribute(typedArray,1))}var attributes=json.data.attributes;for(var key in attributes){var attribute=attributes[key],typedArray=new self[attribute.type](attribute.array);geometry.addAttribute(key,new THREE.BufferAttribute(typedArray,attribute.itemSize))}var groups=json.data.groups||json.data.drawcalls||json.data.offsets;if(void 0!==groups)for(var i=0,n=groups.length;i!==n;++i){var group=groups[i];geometry.addGroup(group.start,group.count)}var boundingSphere=json.data.boundingSphere;if(void 0!==boundingSphere){var center=new THREE.Vector3;void 0!==boundingSphere.center&¢er.fromArray(boundingSphere.center),geometry.boundingSphere=new THREE.Sphere(center,boundingSphere.radius)}return geometry}},THREE.MaterialLoader=function(manager){this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager,this.textures={}},THREE.MaterialLoader.prototype={constructor:THREE.MaterialLoader,load:function(url,onLoad,onProgress,onError){var scope=this,loader=new THREE.XHRLoader(scope.manager);loader.setCrossOrigin(this.crossOrigin),loader.load(url,function(text){onLoad(scope.parse(JSON.parse(text)))},onProgress,onError)},setCrossOrigin:function(value){this.crossOrigin=value},setTextures:function(value){this.textures=value},getTexture:function(name){var textures=this.textures;return void 0===textures[name]&&console.warn("THREE.MaterialLoader: Undefined texture",name),textures[name]},parse:function(json){var material=new THREE[json.type];if(material.uuid=json.uuid,void 0!==json.name&&(material.name=json.name),void 0!==json.color&&material.color.setHex(json.color),void 0!==json.emissive&&material.emissive.setHex(json.emissive),void 0!==json.specular&&material.specular.setHex(json.specular),void 0!==json.shininess&&(material.shininess=json.shininess),void 0!==json.uniforms&&(material.uniforms=json.uniforms),void 0!==json.vertexShader&&(material.vertexShader=json.vertexShader),void 0!==json.fragmentShader&&(material.fragmentShader=json.fragmentShader),void 0!==json.vertexColors&&(material.vertexColors=json.vertexColors),void 0!==json.shading&&(material.shading=json.shading),void 0!==json.blending&&(material.blending=json.blending),void 0!==json.side&&(material.side=json.side),void 0!==json.opacity&&(material.opacity=json.opacity),void 0!==json.transparent&&(material.transparent=json.transparent),void 0!==json.alphaTest&&(material.alphaTest=json.alphaTest),void 0!==json.depthTest&&(material.depthTest=json.depthTest),void 0!==json.depthWrite&&(material.depthWrite=json.depthWrite),void 0!==json.wireframe&&(material.wireframe=json.wireframe),void 0!==json.wireframeLinewidth&&(material.wireframeLinewidth=json.wireframeLinewidth),void 0!==json.size&&(material.size=json.size),void 0!==json.sizeAttenuation&&(material.sizeAttenuation=json.sizeAttenuation),void 0!==json.map&&(material.map=this.getTexture(json.map)),void 0!==json.alphaMap&&(material.alphaMap=this.getTexture(json.alphaMap),material.transparent=!0),void 0!==json.bumpMap&&(material.bumpMap=this.getTexture(json.bumpMap)),void 0!==json.bumpScale&&(material.bumpScale=json.bumpScale),void 0!==json.normalMap&&(material.normalMap=this.getTexture(json.normalMap)),json.normalScale&&(material.normalScale=new THREE.Vector2(json.normalScale,json.normalScale)),void 0!==json.displacementMap&&(material.displacementMap=this.getTexture(json.displacementMap)),void 0!==json.displacementScale&&(material.displacementScale=json.displacementScale),void 0!==json.displacementBias&&(material.displacementBias=json.displacementBias),void 0!==json.specularMap&&(material.specularMap=this.getTexture(json.specularMap)),void 0!==json.envMap&&(material.envMap=this.getTexture(json.envMap),material.combine=THREE.MultiplyOperation),json.reflectivity&&(material.reflectivity=json.reflectivity),void 0!==json.lightMap&&(material.lightMap=this.getTexture(json.lightMap)),void 0!==json.lightMapIntensity&&(material.lightMapIntensity=json.lightMapIntensity),void 0!==json.aoMap&&(material.aoMap=this.getTexture(json.aoMap)),void 0!==json.aoMapIntensity&&(material.aoMapIntensity=json.aoMapIntensity),void 0!==json.materials)for(var i=0,l=json.materials.length;l>i;i++)material.materials.push(this.parse(json.materials[i]));return material}},THREE.ObjectLoader=function(manager){this.manager=void 0!==manager?manager:THREE.DefaultLoadingManager,this.texturePath=""},THREE.ObjectLoader.prototype={constructor:THREE.ObjectLoader,load:function(url,onLoad,onProgress,onError){""===this.texturePath&&(this.texturePath=url.substring(0,url.lastIndexOf("/")+1));var scope=this,loader=new THREE.XHRLoader(scope.manager);loader.setCrossOrigin(this.crossOrigin),loader.load(url,function(text){scope.parse(JSON.parse(text),onLoad)},onProgress,onError)},setTexturePath:function(value){this.texturePath=value},setCrossOrigin:function(value){this.crossOrigin=value},parse:function(json,onLoad){var geometries=this.parseGeometries(json.geometries),images=this.parseImages(json.images,function(){void 0!==onLoad&&onLoad(object)}),textures=this.parseTextures(json.textures,images),materials=this.parseMaterials(json.materials,textures),object=this.parseObject(json.object,geometries,materials);return json.animations&&(object.animations=this.parseAnimations(json.animations)),void 0!==json.images&&0!==json.images.length||void 0!==onLoad&&onLoad(object),object},parseGeometries:function(json){var geometries={};if(void 0!==json)for(var geometryLoader=new THREE.JSONLoader,bufferGeometryLoader=new THREE.BufferGeometryLoader,i=0,l=json.length;l>i;i++){var geometry,data=json[i];switch(data.type){case"PlaneGeometry":case"PlaneBufferGeometry":geometry=new THREE[data.type](data.width,data.height,data.widthSegments,data.heightSegments);break;case"BoxGeometry":case"CubeGeometry":geometry=new THREE.BoxGeometry(data.width,data.height,data.depth,data.widthSegments,data.heightSegments,data.depthSegments);break;case"CircleBufferGeometry":geometry=new THREE.CircleBufferGeometry(data.radius,data.segments,data.thetaStart,data.thetaLength);break;case"CircleGeometry":geometry=new THREE.CircleGeometry(data.radius,data.segments,data.thetaStart,data.thetaLength);break;case"CylinderGeometry":geometry=new THREE.CylinderGeometry(data.radiusTop,data.radiusBottom,data.height,data.radialSegments,data.heightSegments,data.openEnded,data.thetaStart,data.thetaLength);break;case"SphereGeometry":geometry=new THREE.SphereGeometry(data.radius,data.widthSegments,data.heightSegments,data.phiStart,data.phiLength,data.thetaStart,data.thetaLength);break;case"SphereBufferGeometry":geometry=new THREE.SphereBufferGeometry(data.radius,data.widthSegments,data.heightSegments,data.phiStart,data.phiLength,data.thetaStart,data.thetaLength);break;case"DodecahedronGeometry":geometry=new THREE.DodecahedronGeometry(data.radius,data.detail);break;case"IcosahedronGeometry":geometry=new THREE.IcosahedronGeometry(data.radius,data.detail);break;case"OctahedronGeometry":geometry=new THREE.OctahedronGeometry(data.radius,data.detail);break;case"TetrahedronGeometry":geometry=new THREE.TetrahedronGeometry(data.radius,data.detail);break;case"RingGeometry":geometry=new THREE.RingGeometry(data.innerRadius,data.outerRadius,data.thetaSegments,data.phiSegments,data.thetaStart,data.thetaLength);break;case"TorusGeometry":geometry=new THREE.TorusGeometry(data.radius,data.tube,data.radialSegments,data.tubularSegments,data.arc);break;case"TorusKnotGeometry":geometry=new THREE.TorusKnotGeometry(data.radius,data.tube,data.radialSegments,data.tubularSegments,data.p,data.q,data.heightScale);break;case"BufferGeometry":geometry=bufferGeometryLoader.parse(data);break;case"Geometry": +geometry=geometryLoader.parse(data.data,this.texturePath).geometry;break;default:console.warn('THREE.ObjectLoader: Unsupported geometry type "'+data.type+'"');continue}geometry.uuid=data.uuid,void 0!==data.name&&(geometry.name=data.name),geometries[data.uuid]=geometry}return geometries},parseMaterials:function(json,textures){var materials={};if(void 0!==json){var loader=new THREE.MaterialLoader;loader.setTextures(textures);for(var i=0,l=json.length;l>i;i++){var material=loader.parse(json[i]);materials[material.uuid]=material}}return materials},parseAnimations:function(json){for(var animations=[],i=0;i0){var manager=new THREE.LoadingManager(onLoad),loader=new THREE.ImageLoader(manager);loader.setCrossOrigin(this.crossOrigin);for(var i=0,l=json.length;l>i;i++){var image=json[i],path=/^(\/\/)|([a-z]+:(\/\/)?)/i.test(image.url)?image.url:scope.texturePath+image.url;images[image.uuid]=loadImage(path)}}return images},parseTextures:function(json,images){function parseConstant(value){return"number"==typeof value?value:(console.warn("THREE.ObjectLoader.parseTexture: Constant should be in numeric form.",value),THREE[value])}var textures={};if(void 0!==json)for(var i=0,l=json.length;l>i;i++){var data=json[i];void 0===data.image&&console.warn('THREE.ObjectLoader: No "image" specified for',data.uuid),void 0===images[data.image]&&console.warn("THREE.ObjectLoader: Undefined image",data.image);var texture=new THREE.Texture(images[data.image]);texture.needsUpdate=!0,texture.uuid=data.uuid,void 0!==data.name&&(texture.name=data.name),void 0!==data.mapping&&(texture.mapping=parseConstant(data.mapping)),void 0!==data.offset&&(texture.offset=new THREE.Vector2(data.offset[0],data.offset[1])),void 0!==data.repeat&&(texture.repeat=new THREE.Vector2(data.repeat[0],data.repeat[1])),void 0!==data.minFilter&&(texture.minFilter=parseConstant(data.minFilter)),void 0!==data.magFilter&&(texture.magFilter=parseConstant(data.magFilter)),void 0!==data.anisotropy&&(texture.anisotropy=data.anisotropy),Array.isArray(data.wrap)&&(texture.wrapS=parseConstant(data.wrap[0]),texture.wrapT=parseConstant(data.wrap[1])),textures[data.uuid]=texture}return textures},parseObject:function(){var matrix=new THREE.Matrix4;return function(data,geometries,materials){function getGeometry(name){return void 0===geometries[name]&&console.warn("THREE.ObjectLoader: Undefined geometry",name),geometries[name]}function getMaterial(name){return void 0!==name?(void 0===materials[name]&&console.warn("THREE.ObjectLoader: Undefined material",name),materials[name]):void 0}var object;switch(data.type){case"Scene":object=new THREE.Scene;break;case"PerspectiveCamera":object=new THREE.PerspectiveCamera(data.fov,data.aspect,data.near,data.far);break;case"OrthographicCamera":object=new THREE.OrthographicCamera(data.left,data.right,data.top,data.bottom,data.near,data.far);break;case"AmbientLight":object=new THREE.AmbientLight(data.color);break;case"DirectionalLight":object=new THREE.DirectionalLight(data.color,data.intensity);break;case"PointLight":object=new THREE.PointLight(data.color,data.intensity,data.distance,data.decay);break;case"SpotLight":object=new THREE.SpotLight(data.color,data.intensity,data.distance,data.angle,data.exponent,data.decay);break;case"HemisphereLight":object=new THREE.HemisphereLight(data.color,data.groundColor,data.intensity);break;case"Mesh":object=new THREE.Mesh(getGeometry(data.geometry),getMaterial(data.material));break;case"LOD":object=new THREE.LOD;break;case"Line":object=new THREE.Line(getGeometry(data.geometry),getMaterial(data.material),data.mode);break;case"PointCloud":case"Points":object=new THREE.Points(getGeometry(data.geometry),getMaterial(data.material));break;case"Sprite":object=new THREE.Sprite(getMaterial(data.material));break;case"Group":object=new THREE.Group;break;default:object=new THREE.Object3D}if(object.uuid=data.uuid,void 0!==data.name&&(object.name=data.name),void 0!==data.matrix?(matrix.fromArray(data.matrix),matrix.decompose(object.position,object.quaternion,object.scale)):(void 0!==data.position&&object.position.fromArray(data.position),void 0!==data.rotation&&object.rotation.fromArray(data.rotation),void 0!==data.scale&&object.scale.fromArray(data.scale)),void 0!==data.castShadow&&(object.castShadow=data.castShadow),void 0!==data.receiveShadow&&(object.receiveShadow=data.receiveShadow),void 0!==data.visible&&(object.visible=data.visible),void 0!==data.userData&&(object.userData=data.userData),void 0!==data.children)for(var child in data.children)object.add(this.parseObject(data.children[child],geometries,materials));if("LOD"===data.type)for(var levels=data.levels,l=0;li;++i)loadTexture(i);else loader.load(url,function(buffer){var texDatas=scope._parser(buffer,!0);if(texDatas.isCubemap)for(var faces=texDatas.mipmaps.length/texDatas.mipmapCount,f=0;faces>f;f++){images[f]={mipmaps:[]};for(var i=0;i0&&(data.alphaTest=this.alphaTest),this.wireframe===!0&&(data.wireframe=this.wireframe),this.wireframeLinewidth>1&&(data.wireframeLinewidth=this.wireframeLinewidth),data},clone:function(){return(new this.constructor).copy(this)},copy:function(source){return this.name=source.name,this.side=source.side,this.opacity=source.opacity,this.transparent=source.transparent,this.blending=source.blending,this.blendSrc=source.blendSrc,this.blendDst=source.blendDst,this.blendEquation=source.blendEquation,this.blendSrcAlpha=source.blendSrcAlpha,this.blendDstAlpha=source.blendDstAlpha,this.blendEquationAlpha=source.blendEquationAlpha,this.depthFunc=source.depthFunc,this.depthTest=source.depthTest,this.depthWrite=source.depthWrite,this.precision=source.precision,this.polygonOffset=source.polygonOffset,this.polygonOffsetFactor=source.polygonOffsetFactor,this.polygonOffsetUnits=source.polygonOffsetUnits,this.alphaTest=source.alphaTest,this.overdraw=source.overdraw,this.visible=source.visible,this},update:function(){this.dispatchEvent({type:"update"})},dispose:function(){this.dispatchEvent({type:"dispose"})},get wrapAround(){console.warn("THREE."+this.type+": .wrapAround has been removed.")},set wrapAround(boolean){console.warn("THREE."+this.type+": .wrapAround has been removed.")},get wrapRGB(){return console.warn("THREE."+this.type+": .wrapRGB has been removed."),new THREE.Color}},THREE.EventDispatcher.prototype.apply(THREE.Material.prototype),THREE.MaterialIdCount=0,THREE.LineBasicMaterial=function(parameters){THREE.Material.call(this),this.type="LineBasicMaterial",this.color=new THREE.Color(16777215),this.linewidth=1,this.linecap="round",this.linejoin="round",this.vertexColors=THREE.NoColors,this.fog=!0,this.setValues(parameters)},THREE.LineBasicMaterial.prototype=Object.create(THREE.Material.prototype),THREE.LineBasicMaterial.prototype.constructor=THREE.LineBasicMaterial,THREE.LineBasicMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.color.copy(source.color),this.linewidth=source.linewidth,this.linecap=source.linecap,this.linejoin=source.linejoin,this.vertexColors=source.vertexColors,this.fog=source.fog,this},THREE.LineDashedMaterial=function(parameters){THREE.Material.call(this),this.type="LineDashedMaterial",this.color=new THREE.Color(16777215),this.linewidth=1,this.scale=1,this.dashSize=3,this.gapSize=1,this.vertexColors=!1,this.fog=!0,this.setValues(parameters)},THREE.LineDashedMaterial.prototype=Object.create(THREE.Material.prototype),THREE.LineDashedMaterial.prototype.constructor=THREE.LineDashedMaterial,THREE.LineDashedMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.color.copy(source.color),this.linewidth=source.linewidth,this.scale=source.scale,this.dashSize=source.dashSize,this.gapSize=source.gapSize,this.vertexColors=source.vertexColors,this.fog=source.fog,this},THREE.MeshBasicMaterial=function(parameters){THREE.Material.call(this),this.type="MeshBasicMaterial",this.color=new THREE.Color(16777215),this.map=null,this.aoMap=null,this.aoMapIntensity=1,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.combine=THREE.MultiplyOperation,this.reflectivity=1,this.refractionRatio=.98,this.fog=!0,this.shading=THREE.SmoothShading,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.vertexColors=THREE.NoColors,this.skinning=!1,this.morphTargets=!1,this.setValues(parameters)},THREE.MeshBasicMaterial.prototype=Object.create(THREE.Material.prototype),THREE.MeshBasicMaterial.prototype.constructor=THREE.MeshBasicMaterial,THREE.MeshBasicMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.color.copy(source.color),this.map=source.map,this.aoMap=source.aoMap,this.aoMapIntensity=source.aoMapIntensity,this.specularMap=source.specularMap,this.alphaMap=source.alphaMap,this.envMap=source.envMap,this.combine=source.combine,this.reflectivity=source.reflectivity,this.refractionRatio=source.refractionRatio,this.fog=source.fog,this.shading=source.shading,this.wireframe=source.wireframe,this.wireframeLinewidth=source.wireframeLinewidth,this.wireframeLinecap=source.wireframeLinecap,this.wireframeLinejoin=source.wireframeLinejoin,this.vertexColors=source.vertexColors,this.skinning=source.skinning,this.morphTargets=source.morphTargets,this},THREE.MeshLambertMaterial=function(parameters){THREE.Material.call(this),this.type="MeshLambertMaterial",this.color=new THREE.Color(16777215),this.emissive=new THREE.Color(0),this.map=null,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.combine=THREE.MultiplyOperation,this.reflectivity=1,this.refractionRatio=.98,this.fog=!0,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.vertexColors=THREE.NoColors,this.skinning=!1,this.morphTargets=!1,this.morphNormals=!1,this.setValues(parameters)},THREE.MeshLambertMaterial.prototype=Object.create(THREE.Material.prototype),THREE.MeshLambertMaterial.prototype.constructor=THREE.MeshLambertMaterial,THREE.MeshLambertMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.color.copy(source.color),this.emissive.copy(source.emissive),this.map=source.map,this.specularMap=source.specularMap,this.alphaMap=source.alphaMap,this.envMap=source.envMap,this.combine=source.combine,this.reflectivity=source.reflectivity,this.refractionRatio=source.refractionRatio,this.fog=source.fog,this.wireframe=source.wireframe,this.wireframeLinewidth=source.wireframeLinewidth,this.wireframeLinecap=source.wireframeLinecap,this.wireframeLinejoin=source.wireframeLinejoin,this.vertexColors=source.vertexColors,this.skinning=source.skinning,this.morphTargets=source.morphTargets,this.morphNormals=source.morphNormals,this},THREE.MeshPhongMaterial=function(parameters){THREE.Material.call(this),this.type="MeshPhongMaterial",this.color=new THREE.Color(16777215),this.emissive=new THREE.Color(0),this.specular=new THREE.Color(1118481),this.shininess=30,this.metal=!1,this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissiveMap=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalScale=new THREE.Vector2(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.combine=THREE.MultiplyOperation,this.reflectivity=1,this.refractionRatio=.98,this.fog=!0,this.shading=THREE.SmoothShading,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.vertexColors=THREE.NoColors,this.skinning=!1,this.morphTargets=!1,this.morphNormals=!1,this.setValues(parameters)},THREE.MeshPhongMaterial.prototype=Object.create(THREE.Material.prototype),THREE.MeshPhongMaterial.prototype.constructor=THREE.MeshPhongMaterial,THREE.MeshPhongMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.color.copy(source.color),this.emissive.copy(source.emissive),this.specular.copy(source.specular),this.shininess=source.shininess,this.metal=source.metal,this.map=source.map,this.lightMap=source.lightMap,this.lightMapIntensity=source.lightMapIntensity,this.aoMap=source.aoMap,this.aoMapIntensity=source.aoMapIntensity,this.emissiveMap=source.emissiveMap,this.bumpMap=source.bumpMap,this.bumpScale=source.bumpScale,this.normalMap=source.normalMap,this.normalScale.copy(source.normalScale),this.displacementMap=source.displacementMap,this.displacementScale=source.displacementScale,this.displacementBias=source.displacementBias,this.specularMap=source.specularMap,this.alphaMap=source.alphaMap,this.envMap=source.envMap,this.combine=source.combine,this.reflectivity=source.reflectivity,this.refractionRatio=source.refractionRatio,this.fog=source.fog,this.shading=source.shading,this.wireframe=source.wireframe,this.wireframeLinewidth=source.wireframeLinewidth,this.wireframeLinecap=source.wireframeLinecap,this.wireframeLinejoin=source.wireframeLinejoin,this.vertexColors=source.vertexColors,this.skinning=source.skinning,this.morphTargets=source.morphTargets,this.morphNormals=source.morphNormals,this},THREE.MeshDepthMaterial=function(parameters){THREE.Material.call(this),this.type="MeshDepthMaterial",this.morphTargets=!1,this.wireframe=!1,this.wireframeLinewidth=1,this.setValues(parameters)},THREE.MeshDepthMaterial.prototype=Object.create(THREE.Material.prototype),THREE.MeshDepthMaterial.prototype.constructor=THREE.MeshDepthMaterial,THREE.MeshDepthMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.wireframe=source.wireframe,this.wireframeLinewidth=source.wireframeLinewidth,this},THREE.MeshNormalMaterial=function(parameters){THREE.Material.call(this,parameters),this.type="MeshNormalMaterial",this.wireframe=!1,this.wireframeLinewidth=1,this.morphTargets=!1,this.setValues(parameters)},THREE.MeshNormalMaterial.prototype=Object.create(THREE.Material.prototype),THREE.MeshNormalMaterial.prototype.constructor=THREE.MeshNormalMaterial,THREE.MeshNormalMaterial.prototype.copy=function(source){return THREE.Material.prototype.copy.call(this,source),this.wireframe=source.wireframe,this.wireframeLinewidth=source.wireframeLinewidth,this},THREE.MultiMaterial=function(materials){this.uuid=THREE.Math.generateUUID(),this.type="MultiMaterial",this.materials=materials instanceof Array?materials:[],this.visible=!0},THREE.MultiMaterial.prototype={constructor:THREE.MultiMaterial,toJSON:function(){for(var output={metadata:{version:4.2,type:"material",generator:"MaterialExporter"},uuid:this.uuid,type:this.type,materials:[]},i=0,l=this.materials.length;l>i;i++)output.materials.push(this.materials[i].toJSON());return output.visible=this.visible,output},clone:function(){for(var material=new this.constructor,i=0;i2048||canvas.height>2048?canvas.toDataURL("image/jpeg",.6):canvas.toDataURL("image/png")}if(void 0!==meta.textures[this.uuid])return meta.textures[this.uuid];var output={metadata:{version:4.4,type:"Texture",generator:"Texture.toJSON"},uuid:this.uuid,name:this.name,mapping:this.mapping,repeat:[this.repeat.x,this.repeat.y],offset:[this.offset.x,this.offset.y],wrap:[this.wrapS,this.wrapT],minFilter:this.minFilter,magFilter:this.magFilter,anisotropy:this.anisotropy};if(void 0!==this.image){var image=this.image;void 0===image.uuid&&(image.uuid=THREE.Math.generateUUID()),void 0===meta.images[image.uuid]&&(meta.images[image.uuid]={uuid:image.uuid,url:getDataURL(image)}),output.image=image.uuid}return meta.textures[this.uuid]=output,output},dispose:function(){this.dispatchEvent({type:"dispose"})},transformUv:function(uv){if(this.mapping===THREE.UVMapping){if(uv.multiply(this.repeat),uv.add(this.offset),uv.x<0||uv.x>1)switch(this.wrapS){case THREE.RepeatWrapping:uv.x=uv.x-Math.floor(uv.x);break;case THREE.ClampToEdgeWrapping:uv.x=uv.x<0?0:1;break;case THREE.MirroredRepeatWrapping:1===Math.abs(Math.floor(uv.x)%2)?uv.x=Math.ceil(uv.x)-uv.x:uv.x=uv.x-Math.floor(uv.x)}if(uv.y<0||uv.y>1)switch(this.wrapT){case THREE.RepeatWrapping:uv.y=uv.y-Math.floor(uv.y);break;case THREE.ClampToEdgeWrapping:uv.y=uv.y<0?0:1;break;case THREE.MirroredRepeatWrapping:1===Math.abs(Math.floor(uv.y)%2)?uv.y=Math.ceil(uv.y)-uv.y:uv.y=uv.y-Math.floor(uv.y)}this.flipY&&(uv.y=1-uv.y)}}},THREE.EventDispatcher.prototype.apply(THREE.Texture.prototype),THREE.TextureIdCount=0,THREE.CanvasTexture=function(canvas,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy){THREE.Texture.call(this,canvas,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy),this.needsUpdate=!0},THREE.CanvasTexture.prototype=Object.create(THREE.Texture.prototype),THREE.CanvasTexture.prototype.constructor=THREE.CanvasTexture,THREE.CubeTexture=function(images,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy){mapping=void 0!==mapping?mapping:THREE.CubeReflectionMapping,THREE.Texture.call(this,images,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy),this.images=images,this.flipY=!1},THREE.CubeTexture.prototype=Object.create(THREE.Texture.prototype),THREE.CubeTexture.prototype.constructor=THREE.CubeTexture,THREE.CubeTexture.prototype.copy=function(source){return THREE.Texture.prototype.copy.call(this,source),this.images=source.images,this},THREE.CompressedTexture=function(mipmaps,width,height,format,type,mapping,wrapS,wrapT,magFilter,minFilter,anisotropy){THREE.Texture.call(this,null,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy),this.image={width:width,height:height},this.mipmaps=mipmaps,this.flipY=!1,this.generateMipmaps=!1},THREE.CompressedTexture.prototype=Object.create(THREE.Texture.prototype),THREE.CompressedTexture.prototype.constructor=THREE.CompressedTexture,THREE.DataTexture=function(data,width,height,format,type,mapping,wrapS,wrapT,magFilter,minFilter,anisotropy){THREE.Texture.call(this,null,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy),this.image={data:data,width:width,height:height +},this.magFilter=void 0!==magFilter?magFilter:THREE.NearestFilter,this.minFilter=void 0!==minFilter?minFilter:THREE.NearestFilter,this.flipY=!1,this.generateMipmaps=!1},THREE.DataTexture.prototype=Object.create(THREE.Texture.prototype),THREE.DataTexture.prototype.constructor=THREE.DataTexture,THREE.VideoTexture=function(video,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy){function update(){requestAnimationFrame(update),video.readyState===video.HAVE_ENOUGH_DATA&&(scope.needsUpdate=!0)}THREE.Texture.call(this,video,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy),this.generateMipmaps=!1;var scope=this;update()},THREE.VideoTexture.prototype=Object.create(THREE.Texture.prototype),THREE.VideoTexture.prototype.constructor=THREE.VideoTexture,THREE.Group=function(){THREE.Object3D.call(this),this.type="Group"},THREE.Group.prototype=Object.create(THREE.Object3D.prototype),THREE.Group.prototype.constructor=THREE.Group,THREE.Points=function(geometry,material){THREE.Object3D.call(this),this.type="Points",this.geometry=void 0!==geometry?geometry:new THREE.Geometry,this.material=void 0!==material?material:new THREE.PointsMaterial({color:16777215*Math.random()})},THREE.Points.prototype=Object.create(THREE.Object3D.prototype),THREE.Points.prototype.constructor=THREE.Points,THREE.Points.prototype.raycast=function(){var inverseMatrix=new THREE.Matrix4,ray=new THREE.Ray;return function(raycaster,intersects){function testPoint(point,index){var rayPointDistanceSq=ray.distanceSqToPoint(point);if(localThresholdSq>rayPointDistanceSq){var intersectPoint=ray.closestPointToPoint(point);intersectPoint.applyMatrix4(object.matrixWorld);var distance=raycaster.ray.origin.distanceTo(intersectPoint);if(distanceraycaster.far)return;intersects.push({distance:distance,distanceToRay:Math.sqrt(rayPointDistanceSq),point:intersectPoint.clone(),index:index,face:null,object:object})}}var object=this,geometry=object.geometry,threshold=raycaster.params.Points.threshold;if(inverseMatrix.getInverse(this.matrixWorld),ray.copy(raycaster.ray).applyMatrix4(inverseMatrix),null===geometry.boundingBox||ray.isIntersectionBox(geometry.boundingBox)!==!1){var localThreshold=threshold/((this.scale.x+this.scale.y+this.scale.z)/3),localThresholdSq=localThreshold*localThreshold,position=new THREE.Vector3;if(geometry instanceof THREE.BufferGeometry){var index=geometry.index,attributes=geometry.attributes,positions=attributes.position.array;if(null!==index)for(var indices=index.array,i=0,il=indices.length;il>i;i++){var a=indices[i];position.fromArray(positions,3*a),testPoint(position,a)}else for(var i=0,l=positions.length/3;l>i;i++)position.fromArray(positions,3*i),testPoint(position,i)}else for(var vertices=geometry.vertices,i=0,l=vertices.length;l>i;i++)testPoint(vertices[i],i)}}}(),THREE.Points.prototype.clone=function(){return new this.constructor(this.geometry,this.material).copy(this)},THREE.PointCloud=function(geometry,material){return console.warn("THREE.PointCloud has been renamed to THREE.Points."),new THREE.Points(geometry,material)},THREE.ParticleSystem=function(geometry,material){return console.warn("THREE.ParticleSystem has been renamed to THREE.Points."),new THREE.Points(geometry,material)},THREE.Line=function(geometry,material,mode){return 1===mode?(console.warn("THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead."),new THREE.LineSegments(geometry,material)):(THREE.Object3D.call(this),this.type="Line",this.geometry=void 0!==geometry?geometry:new THREE.Geometry,void(this.material=void 0!==material?material:new THREE.LineBasicMaterial({color:16777215*Math.random()})))},THREE.Line.prototype=Object.create(THREE.Object3D.prototype),THREE.Line.prototype.constructor=THREE.Line,THREE.Line.prototype.raycast=function(){var inverseMatrix=new THREE.Matrix4,ray=new THREE.Ray,sphere=new THREE.Sphere;return function(raycaster,intersects){var precision=raycaster.linePrecision,precisionSq=precision*precision,geometry=this.geometry;if(null===geometry.boundingSphere&&geometry.computeBoundingSphere(),sphere.copy(geometry.boundingSphere),sphere.applyMatrix4(this.matrixWorld),raycaster.ray.isIntersectionSphere(sphere)!==!1){inverseMatrix.getInverse(this.matrixWorld),ray.copy(raycaster.ray).applyMatrix4(inverseMatrix);var vStart=new THREE.Vector3,vEnd=new THREE.Vector3,interSegment=new THREE.Vector3,interRay=new THREE.Vector3,step=this instanceof THREE.LineSegments?2:1;if(geometry instanceof THREE.BufferGeometry){var index=geometry.index,attributes=geometry.attributes;if(null!==index)for(var indices=index.array,positions=attributes.position.array,i=0,l=indices.length-1;l>i;i+=step){var a=indices[i],b=indices[i+1];vStart.fromArray(positions,3*a),vEnd.fromArray(positions,3*b);var distSq=ray.distanceSqToSegment(vStart,vEnd,interRay,interSegment);if(!(distSq>precisionSq)){interRay.applyMatrix4(this.matrixWorld);var distance=raycaster.ray.origin.distanceTo(interRay);distanceraycaster.far||intersects.push({distance:distance,point:interSegment.clone().applyMatrix4(this.matrixWorld),index:i,face:null,faceIndex:null,object:this})}}else for(var positions=attributes.position.array,i=0,l=positions.length/3-1;l>i;i+=step){vStart.fromArray(positions,3*i),vEnd.fromArray(positions,3*i+3);var distSq=ray.distanceSqToSegment(vStart,vEnd,interRay,interSegment);if(!(distSq>precisionSq)){interRay.applyMatrix4(this.matrixWorld);var distance=raycaster.ray.origin.distanceTo(interRay);distanceraycaster.far||intersects.push({distance:distance,point:interSegment.clone().applyMatrix4(this.matrixWorld),index:i,face:null,faceIndex:null,object:this})}}}else if(geometry instanceof THREE.Geometry)for(var vertices=geometry.vertices,nbVertices=vertices.length,i=0;nbVertices-1>i;i+=step){var distSq=ray.distanceSqToSegment(vertices[i],vertices[i+1],interRay,interSegment);if(!(distSq>precisionSq)){interRay.applyMatrix4(this.matrixWorld);var distance=raycaster.ray.origin.distanceTo(interRay);distanceraycaster.far||intersects.push({distance:distance,point:interSegment.clone().applyMatrix4(this.matrixWorld),index:i,face:null,faceIndex:null,object:this})}}}}}(),THREE.Line.prototype.clone=function(){return new this.constructor(this.geometry,this.material).copy(this)},THREE.LineStrip=0,THREE.LinePieces=1,THREE.LineSegments=function(geometry,material){THREE.Line.call(this,geometry,material),this.type="LineSegments"},THREE.LineSegments.prototype=Object.create(THREE.Line.prototype),THREE.LineSegments.prototype.constructor=THREE.LineSegments,THREE.Mesh=function(geometry,material){THREE.Object3D.call(this),this.type="Mesh",this.geometry=void 0!==geometry?geometry:new THREE.Geometry,this.material=void 0!==material?material:new THREE.MeshBasicMaterial({color:16777215*Math.random()}),this.updateMorphTargets()},THREE.Mesh.prototype=Object.create(THREE.Object3D.prototype),THREE.Mesh.prototype.constructor=THREE.Mesh,THREE.Mesh.prototype.updateMorphTargets=function(){if(void 0!==this.geometry.morphTargets&&this.geometry.morphTargets.length>0){this.morphTargetBase=-1,this.morphTargetInfluences=[],this.morphTargetDictionary={};for(var m=0,ml=this.geometry.morphTargets.length;ml>m;m++)this.morphTargetInfluences.push(0),this.morphTargetDictionary[this.geometry.morphTargets[m].name]=m}},THREE.Mesh.prototype.getMorphTargetIndexByName=function(name){return void 0!==this.morphTargetDictionary[name]?this.morphTargetDictionary[name]:(console.warn("THREE.Mesh.getMorphTargetIndexByName: morph target "+name+" does not exist. Returning 0."),0)},THREE.Mesh.prototype.raycast=function(){function uvIntersection(point,p1,p2,p3,uv1,uv2,uv3){return THREE.Triangle.barycoordFromPoint(point,p1,p2,p3,barycoord),uv1.multiplyScalar(barycoord.x),uv2.multiplyScalar(barycoord.y),uv3.multiplyScalar(barycoord.z),uv1.add(uv2).add(uv3),uv1.clone()}function checkIntersection(object,raycaster,ray,pA,pB,pC,point){var intersect,material=object.material;if(intersect=material.side===THREE.BackSide?ray.intersectTriangle(pC,pB,pA,!0,point):ray.intersectTriangle(pA,pB,pC,material.side!==THREE.DoubleSide,point),null===intersect)return null;intersectionPointWorld.copy(point),intersectionPointWorld.applyMatrix4(object.matrixWorld);var distance=raycaster.ray.origin.distanceTo(intersectionPointWorld);return distanceraycaster.far?null:{distance:distance,point:intersectionPointWorld.clone(),object:object}}function checkBufferGeometryIntersection(object,raycaster,ray,positions,uvs,a,b,c){vA.fromArray(positions,3*a),vB.fromArray(positions,3*b),vC.fromArray(positions,3*c);var intersection=checkIntersection(object,raycaster,ray,vA,vB,vC,intersectionPoint);return intersection&&(uvs&&(uvA.fromArray(uvs,2*a),uvB.fromArray(uvs,2*b),uvC.fromArray(uvs,2*c),intersection.uv=uvIntersection(intersectionPoint,vA,vB,vC,uvA,uvB,uvC)),intersection.face=new THREE.Face3(a,b,c,THREE.Triangle.normal(vA,vB,vC)),intersection.faceIndex=a),intersection}var inverseMatrix=new THREE.Matrix4,ray=new THREE.Ray,sphere=new THREE.Sphere,vA=new THREE.Vector3,vB=new THREE.Vector3,vC=new THREE.Vector3,tempA=new THREE.Vector3,tempB=new THREE.Vector3,tempC=new THREE.Vector3,uvA=new THREE.Vector2,uvB=new THREE.Vector2,uvC=new THREE.Vector2,barycoord=new THREE.Vector3,intersectionPoint=new THREE.Vector3,intersectionPointWorld=new THREE.Vector3;return function(raycaster,intersects){var geometry=this.geometry,material=this.material;if(void 0!==material){null===geometry.boundingSphere&&geometry.computeBoundingSphere();var matrixWorld=this.matrixWorld;if(sphere.copy(geometry.boundingSphere),sphere.applyMatrix4(matrixWorld),raycaster.ray.isIntersectionSphere(sphere)!==!1&&(inverseMatrix.getInverse(matrixWorld),ray.copy(raycaster.ray).applyMatrix4(inverseMatrix),null===geometry.boundingBox||ray.isIntersectionBox(geometry.boundingBox)!==!1)){var uvs,intersection;if(geometry instanceof THREE.BufferGeometry){var a,b,c,index=geometry.index,attributes=geometry.attributes,positions=attributes.position.array;if(void 0!==attributes.uv&&(uvs=attributes.uv.array),null!==index)for(var indices=index.array,i=0,l=indices.length;l>i;i+=3)a=indices[i],b=indices[i+1],c=indices[i+2],intersection=checkBufferGeometryIntersection(this,raycaster,ray,positions,uvs,a,b,c),intersection&&(intersection.faceIndex=Math.floor(i/3),intersects.push(intersection));else for(var i=0,l=positions.length;l>i;i+=9)a=i/3,b=a+1,c=a+2,intersection=checkBufferGeometryIntersection(this,raycaster,ray,positions,uvs,a,b,c),intersection&&(intersection.index=a,intersects.push(intersection))}else if(geometry instanceof THREE.Geometry){var fvA,fvB,fvC,isFaceMaterial=material instanceof THREE.MeshFaceMaterial,materials=isFaceMaterial===!0?material.materials:null,vertices=geometry.vertices,faces=geometry.faces,faceVertexUvs=geometry.faceVertexUvs[0];faceVertexUvs.length>0&&(uvs=faceVertexUvs);for(var f=0,fl=faces.length;fl>f;f++){var face=faces[f],faceMaterial=isFaceMaterial===!0?materials[face.materialIndex]:material;if(void 0!==faceMaterial){if(fvA=vertices[face.a],fvB=vertices[face.b],fvC=vertices[face.c],faceMaterial.morphTargets===!0){var morphTargets=geometry.morphTargets,morphInfluences=this.morphTargetInfluences;vA.set(0,0,0),vB.set(0,0,0),vC.set(0,0,0);for(var t=0,tl=morphTargets.length;tl>t;t++){var influence=morphInfluences[t];if(0!==influence){var targets=morphTargets[t].vertices;vA.addScaledVector(tempA.subVectors(targets[face.a],fvA),influence),vB.addScaledVector(tempB.subVectors(targets[face.b],fvB),influence),vC.addScaledVector(tempC.subVectors(targets[face.c],fvC),influence)}}vA.add(fvA),vB.add(fvB),vC.add(fvC),fvA=vA,fvB=vB,fvC=vC}if(intersection=checkIntersection(this,raycaster,ray,fvA,fvB,fvC,intersectionPoint)){if(uvs){var uvs_f=uvs[f];uvA.copy(uvs_f[0]),uvB.copy(uvs_f[1]),uvC.copy(uvs_f[2]),intersection.uv=uvIntersection(intersectionPoint,fvA,fvB,fvC,uvA,uvB,uvC)}intersection.face=face,intersection.faceIndex=f,intersects.push(intersection)}}}}}}}}(),THREE.Mesh.prototype.clone=function(){return new this.constructor(this.geometry,this.material).copy(this)},THREE.Bone=function(skin){THREE.Object3D.call(this),this.type="Bone",this.skin=skin},THREE.Bone.prototype=Object.create(THREE.Object3D.prototype),THREE.Bone.prototype.constructor=THREE.Bone,THREE.Bone.prototype.copy=function(source){return THREE.Object3D.prototype.copy.call(this,source),this.skin=source.skin,this},THREE.Skeleton=function(bones,boneInverses,useVertexTexture){if(this.useVertexTexture=void 0!==useVertexTexture?useVertexTexture:!0,this.identityMatrix=new THREE.Matrix4,bones=bones||[],this.bones=bones.slice(0),this.useVertexTexture){var size=Math.sqrt(4*this.bones.length);size=THREE.Math.nextPowerOfTwo(Math.ceil(size)),size=Math.max(size,4),this.boneTextureWidth=size,this.boneTextureHeight=size,this.boneMatrices=new Float32Array(this.boneTextureWidth*this.boneTextureHeight*4),this.boneTexture=new THREE.DataTexture(this.boneMatrices,this.boneTextureWidth,this.boneTextureHeight,THREE.RGBAFormat,THREE.FloatType)}else this.boneMatrices=new Float32Array(16*this.bones.length);if(void 0===boneInverses)this.calculateInverses();else if(this.bones.length===boneInverses.length)this.boneInverses=boneInverses.slice(0);else{console.warn("THREE.Skeleton bonInverses is the wrong length."),this.boneInverses=[];for(var b=0,bl=this.bones.length;bl>b;b++)this.boneInverses.push(new THREE.Matrix4)}},THREE.Skeleton.prototype.calculateInverses=function(){this.boneInverses=[];for(var b=0,bl=this.bones.length;bl>b;b++){var inverse=new THREE.Matrix4;this.bones[b]&&inverse.getInverse(this.bones[b].matrixWorld),this.boneInverses.push(inverse)}},THREE.Skeleton.prototype.pose=function(){for(var bone,b=0,bl=this.bones.length;bl>b;b++)bone=this.bones[b],bone&&bone.matrixWorld.getInverse(this.boneInverses[b]);for(var b=0,bl=this.bones.length;bl>b;b++)bone=this.bones[b],bone&&(bone.parent?(bone.matrix.getInverse(bone.parent.matrixWorld),bone.matrix.multiply(bone.matrixWorld)):bone.matrix.copy(bone.matrixWorld),bone.matrix.decompose(bone.position,bone.quaternion,bone.scale))},THREE.Skeleton.prototype.update=function(){var offsetMatrix=new THREE.Matrix4;return function(){for(var b=0,bl=this.bones.length;bl>b;b++){var matrix=this.bones[b]?this.bones[b].matrixWorld:this.identityMatrix;offsetMatrix.multiplyMatrices(matrix,this.boneInverses[b]),offsetMatrix.flattenToArrayOffset(this.boneMatrices,16*b)}this.useVertexTexture&&(this.boneTexture.needsUpdate=!0)}}(),THREE.Skeleton.prototype.clone=function(){return new THREE.Skeleton(this.bones,this.boneInverses,this.useVertexTexture)},THREE.SkinnedMesh=function(geometry,material,useVertexTexture){THREE.Mesh.call(this,geometry,material),this.type="SkinnedMesh",this.bindMode="attached",this.bindMatrix=new THREE.Matrix4,this.bindMatrixInverse=new THREE.Matrix4;var bones=[];if(this.geometry&&void 0!==this.geometry.bones){for(var bone,gbone,b=0,bl=this.geometry.bones.length;bl>b;++b)gbone=this.geometry.bones[b],bone=new THREE.Bone(this),bones.push(bone),bone.name=gbone.name,bone.position.fromArray(gbone.pos),bone.quaternion.fromArray(gbone.rotq),void 0!==gbone.scl&&bone.scale.fromArray(gbone.scl);for(var b=0,bl=this.geometry.bones.length;bl>b;++b)gbone=this.geometry.bones[b],-1!==gbone.parent&&null!==gbone.parent?bones[gbone.parent].add(bones[b]):this.add(bones[b])}this.normalizeSkinWeights(),this.updateMatrixWorld(!0),this.bind(new THREE.Skeleton(bones,void 0,useVertexTexture),this.matrixWorld)},THREE.SkinnedMesh.prototype=Object.create(THREE.Mesh.prototype),THREE.SkinnedMesh.prototype.constructor=THREE.SkinnedMesh,THREE.SkinnedMesh.prototype.bind=function(skeleton,bindMatrix){this.skeleton=skeleton,void 0===bindMatrix&&(this.updateMatrixWorld(!0),this.skeleton.calculateInverses(),bindMatrix=this.matrixWorld),this.bindMatrix.copy(bindMatrix),this.bindMatrixInverse.getInverse(bindMatrix)},THREE.SkinnedMesh.prototype.pose=function(){this.skeleton.pose()},THREE.SkinnedMesh.prototype.normalizeSkinWeights=function(){if(this.geometry instanceof THREE.Geometry)for(var i=0;ii&&!(distance1){v1.setFromMatrixPosition(camera.matrixWorld),v2.setFromMatrixPosition(this.matrixWorld);var distance=v1.distanceTo(v2);levels[0].object.visible=!0;for(var i=1,l=levels.length;l>i&&distance>=levels[i].distance;i++)levels[i-1].object.visible=!1,levels[i].object.visible=!0;for(;l>i;i++)levels[i].object.visible=!1}}}(),THREE.LOD.prototype.copy=function(source){THREE.Object3D.prototype.copy.call(this,source,!1);for(var levels=source.levels,i=0,l=levels.length;l>i;i++){var level=levels[i];this.addLevel(level.object.clone(),level.distance)}return this},THREE.LOD.prototype.toJSON=function(meta){var data=THREE.Object3D.prototype.toJSON.call(this,meta);data.object.levels=[];for(var levels=this.levels,i=0,l=levels.length;l>i;i++){var level=levels[i];data.object.levels.push({object:level.object.uuid,distance:level.distance})}return data},THREE.Sprite=function(){var indices=new Uint16Array([0,1,2,0,2,3]),vertices=new Float32Array([-.5,-.5,0,.5,-.5,0,.5,.5,0,-.5,.5,0]),uvs=new Float32Array([0,0,1,0,1,1,0,1]),geometry=new THREE.BufferGeometry;return geometry.setIndex(new THREE.BufferAttribute(indices,1)),geometry.addAttribute("position",new THREE.BufferAttribute(vertices,3)),geometry.addAttribute("uv",new THREE.BufferAttribute(uvs,2)),function(material){THREE.Object3D.call(this),this.type="Sprite",this.geometry=geometry,this.material=void 0!==material?material:new THREE.SpriteMaterial}}(),THREE.Sprite.prototype=Object.create(THREE.Object3D.prototype),THREE.Sprite.prototype.constructor=THREE.Sprite,THREE.Sprite.prototype.raycast=function(){var matrixPosition=new THREE.Vector3;return function(raycaster,intersects){matrixPosition.setFromMatrixPosition(this.matrixWorld);var distanceSq=raycaster.ray.distanceSqToPoint(matrixPosition),guessSizeSq=this.scale.x*this.scale.y;distanceSq>guessSizeSq||intersects.push({distance:Math.sqrt(distanceSq),point:this.position,face:null,object:this})}}(),THREE.Sprite.prototype.clone=function(){return new this.constructor(this.material).copy(this)},THREE.Particle=THREE.Sprite,THREE.LensFlare=function(texture,size,distance,blending,color){THREE.Object3D.call(this),this.lensFlares=[],this.positionScreen=new THREE.Vector3,this.customUpdateCallback=void 0,void 0!==texture&&this.add(texture,size,distance,blending,color)},THREE.LensFlare.prototype=Object.create(THREE.Object3D.prototype),THREE.LensFlare.prototype.constructor=THREE.LensFlare,THREE.LensFlare.prototype.add=function(texture,size,distance,blending,color,opacity){void 0===size&&(size=-1),void 0===distance&&(distance=0),void 0===opacity&&(opacity=1),void 0===color&&(color=new THREE.Color(16777215)),void 0===blending&&(blending=THREE.NormalBlending),distance=Math.min(distance,Math.max(0,distance)),this.lensFlares.push({texture:texture,size:size,distance:distance,x:0,y:0,z:0,scale:1,rotation:0,opacity:opacity,color:color,blending:blending})},THREE.LensFlare.prototype.updateLensFlares=function(){var f,flare,fl=this.lensFlares.length,vecX=2*-this.positionScreen.x,vecY=2*-this.positionScreen.y;for(f=0;fl>f;f++)flare=this.lensFlares[f],flare.x=this.positionScreen.x+vecX*flare.distance,flare.y=this.positionScreen.y+vecY*flare.distance,flare.wantedRotation=flare.x*Math.PI*.25,flare.rotation+=.25*(flare.wantedRotation-flare.rotation)},THREE.LensFlare.prototype.copy=function(source){THREE.Object3D.prototype.copy.call(this,source),this.positionScreen.copy(source.positionScreen),this.customUpdateCallback=source.customUpdateCallback;for(var i=0,l=source.lensFlares.length;l>i;i++)this.lensFlares.push(source.lensFlares[i]);return this},THREE.Scene=function(){THREE.Object3D.call(this),this.type="Scene",this.fog=null,this.overrideMaterial=null,this.autoUpdate=!0},THREE.Scene.prototype=Object.create(THREE.Object3D.prototype),THREE.Scene.prototype.constructor=THREE.Scene,THREE.Scene.prototype.copy=function(source){return THREE.Object3D.prototype.copy.call(this,source),null!==source.fog&&(this.fog=source.fog.clone()),null!==source.overrideMaterial&&(this.overrideMaterial=source.overrideMaterial.clone()),this.autoUpdate=source.autoUpdate,this.matrixAutoUpdate=source.matrixAutoUpdate,this},THREE.Fog=function(color,near,far){this.name="",this.color=new THREE.Color(color),this.near=void 0!==near?near:1,this.far=void 0!==far?far:1e3},THREE.Fog.prototype.clone=function(){return new THREE.Fog(this.color.getHex(),this.near,this.far)},THREE.FogExp2=function(color,density){this.name="",this.color=new THREE.Color(color),this.density=void 0!==density?density:25e-5},THREE.FogExp2.prototype.clone=function(){return new THREE.FogExp2(this.color.getHex(),this.density)},THREE.ShaderChunk={},THREE.ShaderChunk.alphamap_fragment="#ifdef USE_ALPHAMAP\n\n diffuseColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n",THREE.ShaderChunk.alphamap_pars_fragment="#ifdef USE_ALPHAMAP\n\n uniform sampler2D alphaMap;\n\n#endif\n",THREE.ShaderChunk.alphatest_fragment="#ifdef ALPHATEST\n\n if ( diffuseColor.a < ALPHATEST ) discard;\n\n#endif\n",THREE.ShaderChunk.aomap_fragment="#ifdef USE_AOMAP\n\n totalAmbientLight *= ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\n#endif\n",THREE.ShaderChunk.aomap_pars_fragment="#ifdef USE_AOMAP\n\n uniform sampler2D aoMap;\n uniform float aoMapIntensity;\n\n#endif",THREE.ShaderChunk.begin_vertex="\nvec3 transformed = vec3( position );\n",THREE.ShaderChunk.beginnormal_vertex="\nvec3 objectNormal = vec3( normal );\n",THREE.ShaderChunk.bumpmap_pars_fragment="#ifdef USE_BUMPMAP\n\n uniform sampler2D bumpMap;\n uniform float bumpScale;\n\n\n\n vec2 dHdxy_fwd() {\n\n vec2 dSTdx = dFdx( vUv );\n vec2 dSTdy = dFdy( vUv );\n\n float Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n return vec2( dBx, dBy );\n\n }\n\n vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n vec3 vSigmaX = dFdx( surf_pos );\n vec3 vSigmaY = dFdy( surf_pos );\n vec3 vN = surf_norm;\n vec3 R1 = cross( vSigmaY, vN );\n vec3 R2 = cross( vN, vSigmaX );\n\n float fDet = dot( vSigmaX, R1 );\n\n vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n return normalize( abs( fDet ) * surf_norm - vGrad );\n\n }\n\n#endif\n",THREE.ShaderChunk.color_fragment="#ifdef USE_COLOR\n\n diffuseColor.rgb *= vColor;\n\n#endif",THREE.ShaderChunk.color_pars_fragment="#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif\n",THREE.ShaderChunk.color_pars_vertex="#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif",THREE.ShaderChunk.color_vertex="#ifdef USE_COLOR\n\n vColor.xyz = color.xyz;\n\n#endif",THREE.ShaderChunk.common="#define PI 3.14159\n#define PI2 6.28318\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\n\nvec3 transformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( matrix * vec4( normal, 0.0 ) ).xyz );\n\n}\n\nvec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );\n\n}\n\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n float distance = dot( planeNormal, point - pointOnPlane );\n\n return - distance * planeNormal + point;\n\n}\n\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return sign( dot( point - pointOnPlane, planeNormal ) );\n\n}\n\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n\n}\n\nfloat calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) {\n\n if ( decayExponent > 0.0 ) {\n\n return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\n }\n\n return 1.0;\n\n}\n\nvec3 F_Schlick( in vec3 specularColor, in float dotLH ) {\n\n\n float fresnel = exp2( ( -5.55437 * dotLH - 6.98316 ) * dotLH );\n\n return ( 1.0 - specularColor ) * fresnel + specularColor;\n\n}\n\nfloat G_BlinnPhong_Implicit( /* in float dotNL, in float dotNV */ ) {\n\n\n return 0.25;\n\n}\n\nfloat D_BlinnPhong( in float shininess, in float dotNH ) {\n\n\n return ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n\n}\n\nvec3 BRDF_BlinnPhong( in vec3 specularColor, in float shininess, in vec3 normal, in vec3 lightDir, in vec3 viewDir ) {\n\n vec3 halfDir = normalize( lightDir + viewDir );\n\n float dotNH = saturate( dot( normal, halfDir ) );\n float dotLH = saturate( dot( lightDir, halfDir ) );\n\n vec3 F = F_Schlick( specularColor, dotLH );\n\n float G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );\n\n float D = D_BlinnPhong( shininess, dotNH );\n\n return F * G * D;\n\n}\n\nvec3 inputToLinear( in vec3 a ) {\n\n #ifdef GAMMA_INPUT\n\n return pow( a, vec3( float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n\nvec3 linearToOutput( in vec3 a ) {\n\n #ifdef GAMMA_OUTPUT\n\n return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n",THREE.ShaderChunk.defaultnormal_vertex="#ifdef FLIP_SIDED\n\n objectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;\n",THREE.ShaderChunk.displacementmap_vertex="#ifdef USE_DISPLACEMENTMAP\n\n transformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n\n#endif\n",THREE.ShaderChunk.displacementmap_pars_vertex="#ifdef USE_DISPLACEMENTMAP\n\n uniform sampler2D displacementMap;\n uniform float displacementScale;\n uniform float displacementBias;\n\n#endif\n",THREE.ShaderChunk.emissivemap_fragment="#ifdef USE_EMISSIVEMAP\n\n vec4 emissiveColor = texture2D( emissiveMap, vUv );\n\n emissiveColor.rgb = inputToLinear( emissiveColor.rgb );\n\n totalEmissiveLight *= emissiveColor.rgb;\n\n#endif\n",THREE.ShaderChunk.emissivemap_pars_fragment="#ifdef USE_EMISSIVEMAP\n\n uniform sampler2D emissiveMap;\n\n#endif\n",THREE.ShaderChunk.envmap_fragment="#ifdef USE_ENVMAP\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n #else\n\n vec3 reflectVec = vReflect;\n\n #endif\n\n #ifdef DOUBLE_SIDED\n float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n #else\n float flipNormal = 1.0;\n #endif\n\n #ifdef ENVMAP_TYPE_CUBE\n vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #elif defined( ENVMAP_TYPE_EQUIREC )\n vec2 sampleUV;\n sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n vec4 envColor = texture2D( envMap, sampleUV );\n\n #elif defined( ENVMAP_TYPE_SPHERE )\n vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n #endif\n\n envColor.xyz = inputToLinear( envColor.xyz );\n\n #ifdef ENVMAP_BLENDING_MULTIPLY\n\n outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_MIX )\n\n outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_ADD )\n\n outgoingLight += envColor.xyz * specularStrength * reflectivity;\n\n #endif\n\n#endif\n",THREE.ShaderChunk.envmap_pars_fragment="#ifdef USE_ENVMAP\n\n uniform float reflectivity;\n #ifdef ENVMAP_TYPE_CUBE\n uniform samplerCube envMap;\n #else\n uniform sampler2D envMap;\n #endif\n uniform float flipEnvMap;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n uniform float refractionRatio;\n\n #else\n\n varying vec3 vReflect;\n\n #endif\n\n#endif\n",THREE.ShaderChunk.envmap_pars_vertex="#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n varying vec3 vReflect;\n\n uniform float refractionRatio;\n\n#endif\n",THREE.ShaderChunk.envmap_vertex="#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vReflect = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n#endif\n",THREE.ShaderChunk.fog_fragment="#ifdef USE_FOG\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n float depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n #else\n\n float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n #endif\n\n #ifdef FOG_EXP2\n\n float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\n\n #else\n\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n\n #endif\n \n outgoingLight = mix( outgoingLight, fogColor, fogFactor );\n\n#endif",THREE.ShaderChunk.fog_pars_fragment="#ifdef USE_FOG\n\n uniform vec3 fogColor;\n\n #ifdef FOG_EXP2\n\n uniform float fogDensity;\n\n #else\n\n uniform float fogNear;\n uniform float fogFar;\n #endif\n\n#endif",THREE.ShaderChunk.hemilight_fragment="#if MAX_HEMI_LIGHTS > 0\n\n for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lightDir = hemisphereLightDirection[ i ];\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vec3 lightColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n totalAmbientLight += lightColor;\n\n }\n\n#endif\n\n", +THREE.ShaderChunk.lightmap_fragment="#ifdef USE_LIGHTMAP\n\n totalAmbientLight += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\n#endif\n",THREE.ShaderChunk.lightmap_pars_fragment="#ifdef USE_LIGHTMAP\n\n uniform sampler2D lightMap;\n uniform float lightMapIntensity;\n\n#endif",THREE.ShaderChunk.lights_lambert_pars_vertex="#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n",THREE.ShaderChunk.lights_lambert_vertex="vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vec3( 0.0 );\n\n#endif\n\nvec3 normal = normalize( transformedNormal );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lVector = pointLightPosition[ i ] - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n #endif\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n #endif\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * saturate( - dotProduct );\n\n #endif\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lightDir = hemisphereLightDirection[ i ];\n\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n #ifdef DOUBLE_SIDED\n\n float hemiDiffuseWeightBack = - 0.5 * dotProduct + 0.5;\n\n vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n #endif\n\n }\n\n#endif\n",THREE.ShaderChunk.lights_phong_fragment="vec3 viewDir = normalize( vViewPosition );\n\nvec3 totalDiffuseLight = vec3( 0.0 );\nvec3 totalSpecularLight = vec3( 0.0 );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lightPosition = pointLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * cosineTerm;\n\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * cosineTerm;\n\n }\n\n#endif\n",THREE.ShaderChunk.lights_phong_pars_fragment="uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\nvarying vec3 vViewPosition;\n\n#ifndef FLAT_SHADED\n\n varying vec3 vNormal;\n\n#endif\n",THREE.ShaderChunk.lights_phong_pars_vertex="#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\n#endif\n",THREE.ShaderChunk.lights_phong_vertex="#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n vWorldPosition = worldPosition.xyz;\n\n#endif\n",THREE.ShaderChunk.linear_to_gamma_fragment="\n outgoingLight = linearToOutput( outgoingLight );\n",THREE.ShaderChunk.logdepthbuf_fragment="#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif",THREE.ShaderChunk.logdepthbuf_pars_fragment="#ifdef USE_LOGDEPTHBUF\n\n uniform float logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n#endif\n",THREE.ShaderChunk.logdepthbuf_pars_vertex="#ifdef USE_LOGDEPTHBUF\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n uniform float logDepthBufFC;\n\n#endif",THREE.ShaderChunk.logdepthbuf_vertex="#ifdef USE_LOGDEPTHBUF\n\n gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n vFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n #endif\n\n#endif",THREE.ShaderChunk.map_fragment="#ifdef USE_MAP\n\n vec4 texelColor = texture2D( map, vUv );\n\n texelColor.xyz = inputToLinear( texelColor.xyz );\n\n diffuseColor *= texelColor;\n\n#endif\n",THREE.ShaderChunk.map_pars_fragment="#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif",THREE.ShaderChunk.map_particle_fragment="#ifdef USE_MAP\n\n diffuseColor *= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\n#endif\n",THREE.ShaderChunk.map_particle_pars_fragment="#ifdef USE_MAP\n\n uniform vec4 offsetRepeat;\n uniform sampler2D map;\n\n#endif\n",THREE.ShaderChunk.morphnormal_vertex="#ifdef USE_MORPHNORMALS\n\n objectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n objectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n objectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n objectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n#endif\n",THREE.ShaderChunk.morphtarget_pars_vertex="#ifdef USE_MORPHTARGETS\n\n #ifndef USE_MORPHNORMALS\n\n uniform float morphTargetInfluences[ 8 ];\n\n #else\n\n uniform float morphTargetInfluences[ 4 ];\n\n #endif\n\n#endif",THREE.ShaderChunk.morphtarget_vertex="#ifdef USE_MORPHTARGETS\n\n transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n #ifndef USE_MORPHNORMALS\n\n transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n #endif\n\n#endif\n",THREE.ShaderChunk.normal_phong_fragment="#ifndef FLAT_SHADED\n\n vec3 normal = normalize( vNormal );\n\n #ifdef DOUBLE_SIDED\n\n normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n #endif\n\n#else\n\n vec3 fdx = dFdx( vViewPosition );\n vec3 fdy = dFdy( vViewPosition );\n vec3 normal = normalize( cross( fdx, fdy ) );\n\n#endif\n\n#ifdef USE_NORMALMAP\n\n normal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\n",THREE.ShaderChunk.normalmap_pars_fragment="#ifdef USE_NORMALMAP\n\n uniform sampler2D normalMap;\n uniform vec2 normalScale;\n\n\n vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n vec3 q0 = dFdx( eye_pos.xyz );\n vec3 q1 = dFdy( eye_pos.xyz );\n vec2 st0 = dFdx( vUv.st );\n vec2 st1 = dFdy( vUv.st );\n\n vec3 S = normalize( q0 * st1.t - q1 * st0.t );\n vec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n vec3 N = normalize( surf_norm );\n\n vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n mapN.xy = normalScale * mapN.xy;\n mat3 tsn = mat3( S, T, N );\n return normalize( tsn * mapN );\n\n }\n\n#endif\n",THREE.ShaderChunk.project_vertex="#ifdef USE_SKINNING\n\n vec4 mvPosition = modelViewMatrix * skinned;\n\n#else\n\n vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;\n",THREE.ShaderChunk.shadowmap_fragment="#ifdef USE_SHADOWMAP\n\n for ( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n float texelSizeY = 1.0 / shadowMapSize[ i ].y;\n\n float shadow = 0.0;\n\n#if defined( POINT_LIGHT_SHADOWS )\n\n bool isPointLight = shadowDarkness[ i ] < 0.0;\n\n if ( isPointLight ) {\n\n float realShadowDarkness = abs( shadowDarkness[ i ] );\n\n vec3 lightToPosition = vShadowCoord[ i ].xyz;\n\n #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n vec3 bd3D = normalize( lightToPosition );\n float dp = length( lightToPosition );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D, texelSizeY ) ), shadowBias[ i ], shadow );\n\n\n #if defined( SHADOWMAP_TYPE_PCF )\n const float Dr = 1.25;\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n const float Dr = 2.25;\n #endif\n\n float os = Dr * 2.0 * texelSizeY;\n\n const vec3 Gsd = vec3( - 1, 0, 1 );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zyz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xyz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zyx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xyx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n\n shadow *= realShadowDarkness * ( 1.0 / 21.0 );\n\n #else \n vec3 bd3D = normalize( lightToPosition );\n float dp = length( lightToPosition );\n\n adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D, texelSizeY ) ), shadowBias[ i ], shadow );\n\n shadow *= realShadowDarkness;\n\n #endif\n\n } else {\n\n#endif \n float texelSizeX = 1.0 / shadowMapSize[ i ].x;\n\n vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n\n bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n bool inFrustum = all( inFrustumVec );\n\n bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n bool frustumTest = all( frustumTestVec );\n\n if ( frustumTest ) {\n\n #if defined( SHADOWMAP_TYPE_PCF )\n\n\n /*\n for ( float y = -1.25; y <= 1.25; y += 1.25 )\n for ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n if ( fDepth < shadowCoord.z )\n shadow += 1.0;\n }\n shadow /= 9.0;\n */\n\n shadowCoord.z += shadowBias[ i ];\n\n const float ShadowDelta = 1.0 / 9.0;\n\n float xPixelOffset = texelSizeX;\n float yPixelOffset = texelSizeY;\n\n float dx0 = - 1.25 * xPixelOffset;\n float dy0 = - 1.25 * yPixelOffset;\n float dx1 = 1.25 * xPixelOffset;\n float dy1 = 1.25 * yPixelOffset;\n\n float fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n shadow *= shadowDarkness[ i ];\n\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n\n shadowCoord.z += shadowBias[ i ];\n\n float xPixelOffset = texelSizeX;\n float yPixelOffset = texelSizeY;\n\n float dx0 = - 1.0 * xPixelOffset;\n float dy0 = - 1.0 * yPixelOffset;\n float dx1 = 1.0 * xPixelOffset;\n float dy1 = 1.0 * yPixelOffset;\n\n mat3 shadowKernel;\n mat3 depthKernel;\n\n depthKernel[ 0 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n depthKernel[ 0 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n depthKernel[ 0 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n depthKernel[ 1 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n depthKernel[ 1 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n depthKernel[ 1 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n depthKernel[ 2 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n depthKernel[ 2 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n depthKernel[ 2 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n vec3 shadowZ = vec3( shadowCoord.z );\n shadowKernel[ 0 ] = vec3( lessThan( depthKernel[ 0 ], shadowZ ) );\n shadowKernel[ 0 ] *= vec3( 0.25 );\n\n shadowKernel[ 1 ] = vec3( lessThan( depthKernel[ 1 ], shadowZ ) );\n shadowKernel[ 1 ] *= vec3( 0.25 );\n\n shadowKernel[ 2 ] = vec3( lessThan( depthKernel[ 2 ], shadowZ ) );\n shadowKernel[ 2 ] *= vec3( 0.25 );\n\n vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[ i ].xy );\n\n shadowKernel[ 0 ] = mix( shadowKernel[ 1 ], shadowKernel[ 0 ], fractionalCoord.x );\n shadowKernel[ 1 ] = mix( shadowKernel[ 2 ], shadowKernel[ 1 ], fractionalCoord.x );\n\n vec4 shadowValues;\n shadowValues.x = mix( shadowKernel[ 0 ][ 1 ], shadowKernel[ 0 ][ 0 ], fractionalCoord.y );\n shadowValues.y = mix( shadowKernel[ 0 ][ 2 ], shadowKernel[ 0 ][ 1 ], fractionalCoord.y );\n shadowValues.z = mix( shadowKernel[ 1 ][ 1 ], shadowKernel[ 1 ][ 0 ], fractionalCoord.y );\n shadowValues.w = mix( shadowKernel[ 1 ][ 2 ], shadowKernel[ 1 ][ 1 ], fractionalCoord.y );\n\n shadow = dot( shadowValues, vec4( 1.0 ) ) * shadowDarkness[ i ];\n\n #else \n shadowCoord.z += shadowBias[ i ];\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n shadow = shadowDarkness[ i ];\n\n #endif\n\n }\n\n#ifdef SHADOWMAP_DEBUG\n\n if ( inFrustum ) {\n\n if ( i == 0 ) {\n\n outgoingLight *= vec3( 1.0, 0.5, 0.0 );\n\n } else if ( i == 1 ) {\n\n outgoingLight *= vec3( 0.0, 1.0, 0.8 );\n\n } else {\n\n outgoingLight *= vec3( 0.0, 0.5, 1.0 );\n\n }\n\n }\n\n#endif\n\n#if defined( POINT_LIGHT_SHADOWS )\n\n }\n\n#endif\n\n shadowMask = shadowMask * vec3( 1.0 - shadow );\n\n }\n\n#endif\n",THREE.ShaderChunk.shadowmap_pars_fragment="#ifdef USE_SHADOWMAP\n\n uniform sampler2D shadowMap[ MAX_SHADOWS ];\n uniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform float shadowBias[ MAX_SHADOWS ];\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n float unpackDepth( const in vec4 rgba_depth ) {\n\n const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n float depth = dot( rgba_depth, bit_shift );\n return depth;\n\n }\n\n #if defined(POINT_LIGHT_SHADOWS)\n\n\n void adjustShadowValue1K( const float testDepth, const vec4 textureData, const float bias, inout float shadowValue ) {\n\n const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n if ( testDepth >= dot( textureData, bitSh ) * 1000.0 + bias )\n shadowValue += 1.0;\n\n }\n\n\n vec2 cubeToUV( vec3 v, float texelSizeY ) {\n\n\n vec3 absV = abs( v );\n\n\n float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n absV *= scaleToCube;\n\n\n v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\n\n\n vec2 planar = v.xy;\n\n float almostATexel = 1.5 * texelSizeY;\n float almostOne = 1.0 - almostATexel;\n\n if ( absV.z >= almostOne ) {\n\n if ( v.z > 0.0 )\n planar.x = 4.0 - v.x;\n\n } else if ( absV.x >= almostOne ) {\n\n float signX = sign( v.x );\n planar.x = v.z * signX + 2.0 * signX;\n\n } else if ( absV.y >= almostOne ) {\n\n float signY = sign( v.y );\n planar.x = v.x + 2.0 * signY + 2.0;\n planar.y = v.z * signY - 2.0;\n\n }\n\n\n return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\n }\n\n #endif\n\n#endif\n",THREE.ShaderChunk.shadowmap_pars_vertex="#ifdef USE_SHADOWMAP\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform mat4 shadowMatrix[ MAX_SHADOWS ];\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n#endif",THREE.ShaderChunk.shadowmap_vertex="#ifdef USE_SHADOWMAP\n\n for ( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n }\n\n#endif",THREE.ShaderChunk.skinbase_vertex="#ifdef USE_SKINNING\n\n mat4 boneMatX = getBoneMatrix( skinIndex.x );\n mat4 boneMatY = getBoneMatrix( skinIndex.y );\n mat4 boneMatZ = getBoneMatrix( skinIndex.z );\n mat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif",THREE.ShaderChunk.skinning_pars_vertex="#ifdef USE_SKINNING\n\n uniform mat4 bindMatrix;\n uniform mat4 bindMatrixInverse;\n\n #ifdef BONE_TEXTURE\n\n uniform sampler2D boneTexture;\n uniform int boneTextureWidth;\n uniform int boneTextureHeight;\n\n mat4 getBoneMatrix( const in float i ) {\n\n float j = i * 4.0;\n float x = mod( j, float( boneTextureWidth ) );\n float y = floor( j / float( boneTextureWidth ) );\n\n float dx = 1.0 / float( boneTextureWidth );\n float dy = 1.0 / float( boneTextureHeight );\n\n y = dy * ( y + 0.5 );\n\n vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n mat4 bone = mat4( v1, v2, v3, v4 );\n\n return bone;\n\n }\n\n #else\n\n uniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n mat4 getBoneMatrix( const in float i ) {\n\n mat4 bone = boneGlobalMatrices[ int(i) ];\n return bone;\n\n }\n\n #endif\n\n#endif\n",THREE.ShaderChunk.skinning_vertex="#ifdef USE_SKINNING\n\n vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\n vec4 skinned = vec4( 0.0 );\n skinned += boneMatX * skinVertex * skinWeight.x;\n skinned += boneMatY * skinVertex * skinWeight.y;\n skinned += boneMatZ * skinVertex * skinWeight.z;\n skinned += boneMatW * skinVertex * skinWeight.w;\n skinned = bindMatrixInverse * skinned;\n\n#endif\n",THREE.ShaderChunk.skinnormal_vertex="#ifdef USE_SKINNING\n\n mat4 skinMatrix = mat4( 0.0 );\n skinMatrix += skinWeight.x * boneMatX;\n skinMatrix += skinWeight.y * boneMatY;\n skinMatrix += skinWeight.z * boneMatZ;\n skinMatrix += skinWeight.w * boneMatW;\n skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\n objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\n#endif\n",THREE.ShaderChunk.specularmap_fragment="float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n vec4 texelSpecular = texture2D( specularMap, vUv );\n specularStrength = texelSpecular.r;\n\n#else\n\n specularStrength = 1.0;\n\n#endif",THREE.ShaderChunk.specularmap_pars_fragment="#ifdef USE_SPECULARMAP\n\n uniform sampler2D specularMap;\n\n#endif",THREE.ShaderChunk.uv2_pars_fragment="#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n varying vec2 vUv2;\n\n#endif",THREE.ShaderChunk.uv2_pars_vertex="#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n attribute vec2 uv2;\n varying vec2 vUv2;\n\n#endif",THREE.ShaderChunk.uv2_vertex="#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n vUv2 = uv2;\n\n#endif",THREE.ShaderChunk.uv_pars_fragment="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n\n#endif",THREE.ShaderChunk.uv_pars_vertex="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n uniform vec4 offsetRepeat;\n\n#endif\n",THREE.ShaderChunk.uv_vertex="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif",THREE.ShaderChunk.worldpos_vertex="#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n #ifdef USE_SKINNING\n\n vec4 worldPosition = modelMatrix * skinned;\n\n #else\n\n vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n\n #endif\n\n#endif\n",THREE.UniformsUtils={merge:function(uniforms){for(var merged={},u=0;u dashSize ) {"," discard;"," }"," vec3 outgoingLight = vec3( 0.0 );"," vec4 diffuseColor = vec4( diffuse, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.color_fragment," outgoingLight = diffuseColor.rgb;",THREE.ShaderChunk.fog_fragment," gl_FragColor = vec4( outgoingLight, diffuseColor.a );","}"].join("\n")},depth:{uniforms:{mNear:{type:"f",value:1},mFar:{type:"f",value:2e3},opacity:{type:"f",value:1}},vertexShader:[THREE.ShaderChunk.common,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.begin_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.project_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform float mNear;","uniform float mFar;","uniform float opacity;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {",THREE.ShaderChunk.logdepthbuf_fragment," #ifdef USE_LOGDEPTHBUF_EXT"," float depth = gl_FragDepthEXT / gl_FragCoord.w;"," #else"," float depth = gl_FragCoord.z / gl_FragCoord.w;"," #endif"," float color = 1.0 - smoothstep( mNear, mFar, depth );"," gl_FragColor = vec4( vec3( color ), opacity );","}"].join("\n")},normal:{uniforms:{opacity:{type:"f",value:1}},vertexShader:["varying vec3 vNormal;",THREE.ShaderChunk.common,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {"," vNormal = normalize( normalMatrix * normal );",THREE.ShaderChunk.begin_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.project_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform float opacity;","varying vec3 vNormal;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {"," gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},cube:{uniforms:{tCube:{type:"t",value:null},tFlip:{type:"f",value:-1}},vertexShader:["varying vec3 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {"," vWorldPosition = transformDirection( position, modelMatrix );"," gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform samplerCube tCube;","uniform float tFlip;","varying vec3 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {"," gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},equirect:{uniforms:{tEquirect:{type:"t",value:null},tFlip:{type:"f",value:-1}},vertexShader:["varying vec3 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {"," vWorldPosition = transformDirection( position, modelMatrix );"," gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform sampler2D tEquirect;","uniform float tFlip;","varying vec3 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {","vec3 direction = normalize( vWorldPosition );","vec2 sampleUV;","sampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );","sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;","gl_FragColor = texture2D( tEquirect, sampleUV );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},depthRGBA:{uniforms:{},vertexShader:[THREE.ShaderChunk.common,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.begin_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.project_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:[THREE.ShaderChunk.common,THREE.ShaderChunk.logdepthbuf_pars_fragment,"vec4 pack_depth( const in float depth ) {"," const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );"," const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );"," vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );"," res -= res.xxyz * bit_mask;"," return res;","}","void main() {",THREE.ShaderChunk.logdepthbuf_fragment," #ifdef USE_LOGDEPTHBUF_EXT"," gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );"," #else"," gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );"," #endif","}"].join("\n")},distanceRGBA:{uniforms:{lightPos:{type:"v3",value:new THREE.Vector3(0,0,0)}},vertexShader:["varying vec4 vWorldPosition;",THREE.ShaderChunk.common,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.begin_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.project_vertex,THREE.ShaderChunk.worldpos_vertex,"vWorldPosition = worldPosition;","}"].join("\n"),fragmentShader:["uniform vec3 lightPos;","varying vec4 vWorldPosition;",THREE.ShaderChunk.common,"vec4 pack1K ( float depth ) {"," depth /= 1000.0;"," const vec4 bitSh = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );"," const vec4 bitMsk = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );"," vec4 res = fract( depth * bitSh );"," res -= res.xxyz * bitMsk;"," return res; ","}","float unpack1K ( vec4 color ) {"," const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );"," return dot( color, bitSh ) * 1000.0;","}","void main () {"," gl_FragColor = pack1K( length( vWorldPosition.xyz - lightPos.xyz ) );","}"].join("\n")}},THREE.WebGLRenderer=function(parameters){function glClearColor(r,g,b,a){_premultipliedAlpha===!0&&(r*=a,g*=a,b*=a),_gl.clearColor(r,g,b,a)}function setDefaultGLState(){state.init(),_gl.viewport(_viewportX,_viewportY,_viewportWidth,_viewportHeight),glClearColor(_clearColor.r,_clearColor.g,_clearColor.b,_clearAlpha)}function resetGLState(){_currentProgram=null,_currentCamera=null,_currentGeometryProgram="",_currentMaterialId=-1,_lightsNeedUpdate=!0,state.reset()}function onContextLost(event){event.preventDefault(),resetGLState(),setDefaultGLState(),properties.clear()}function onTextureDispose(event){var texture=event.target;texture.removeEventListener("dispose",onTextureDispose),deallocateTexture(texture),_infoMemory.textures--}function onRenderTargetDispose(event){var renderTarget=event.target;renderTarget.removeEventListener("dispose",onRenderTargetDispose),deallocateRenderTarget(renderTarget),_infoMemory.textures--}function onMaterialDispose(event){var material=event.target;material.removeEventListener("dispose",onMaterialDispose),deallocateMaterial(material)}function deallocateTexture(texture){var textureProperties=properties.get(texture);if(texture.image&&textureProperties.__image__webglTextureCube)_gl.deleteTexture(textureProperties.__image__webglTextureCube);else{if(void 0===textureProperties.__webglInit)return;_gl.deleteTexture(textureProperties.__webglTexture)}properties["delete"](texture)}function deallocateRenderTarget(renderTarget){var renderTargetProperties=properties.get(renderTarget),textureProperties=properties.get(renderTarget.texture);if(renderTarget&&void 0!==textureProperties.__webglTexture){if(_gl.deleteTexture(textureProperties.__webglTexture),renderTarget instanceof THREE.WebGLRenderTargetCube)for(var i=0;6>i;i++)_gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i]),_gl.deleteRenderbuffer(renderTargetProperties.__webglRenderbuffer[i]);else _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer),_gl.deleteRenderbuffer(renderTargetProperties.__webglRenderbuffer);properties["delete"](renderTarget.texture),properties["delete"](renderTarget)}}function deallocateMaterial(material){releaseMaterialProgramReference(material),properties["delete"](material)}function releaseMaterialProgramReference(material){var programInfo=properties.get(material).program;material.program=void 0,void 0!==programInfo&&programCache.releaseProgram(programInfo)}function setupVertexAttributes(material,program,geometry,startIndex){var extension;if(geometry instanceof THREE.InstancedBufferGeometry&&(extension=extensions.get("ANGLE_instanced_arrays"),null===extension))return void console.error("THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.");void 0===startIndex&&(startIndex=0),state.initAttributes();var geometryAttributes=geometry.attributes,programAttributes=program.getAttributes(),materialDefaultAttributeValues=material.defaultAttributeValues;for(var name in programAttributes){var programAttribute=programAttributes[name];if(programAttribute>=0){var geometryAttribute=geometryAttributes[name];if(void 0!==geometryAttribute){var size=geometryAttribute.itemSize,buffer=objects.getAttributeBuffer(geometryAttribute);if(geometryAttribute instanceof THREE.InterleavedBufferAttribute){var data=geometryAttribute.data,stride=data.stride,offset=geometryAttribute.offset;data instanceof THREE.InstancedInterleavedBuffer?(state.enableAttributeAndDivisor(programAttribute,data.meshPerAttribute,extension),void 0===geometry.maxInstancedCount&&(geometry.maxInstancedCount=data.meshPerAttribute*data.count)):state.enableAttribute(programAttribute),_gl.bindBuffer(_gl.ARRAY_BUFFER,buffer),_gl.vertexAttribPointer(programAttribute,size,_gl.FLOAT,!1,stride*data.array.BYTES_PER_ELEMENT,(startIndex*stride+offset)*data.array.BYTES_PER_ELEMENT)}else geometryAttribute instanceof THREE.InstancedBufferAttribute?(state.enableAttributeAndDivisor(programAttribute,geometryAttribute.meshPerAttribute,extension),void 0===geometry.maxInstancedCount&&(geometry.maxInstancedCount=geometryAttribute.meshPerAttribute*geometryAttribute.count)):state.enableAttribute(programAttribute),_gl.bindBuffer(_gl.ARRAY_BUFFER,buffer),_gl.vertexAttribPointer(programAttribute,size,_gl.FLOAT,!1,0,startIndex*size*4)}else if(void 0!==materialDefaultAttributeValues){var value=materialDefaultAttributeValues[name];if(void 0!==value)switch(value.length){case 2:_gl.vertexAttrib2fv(programAttribute,value);break;case 3:_gl.vertexAttrib3fv(programAttribute,value);break;case 4:_gl.vertexAttrib4fv(programAttribute,value);break;default:_gl.vertexAttrib1fv(programAttribute,value)}}}}state.disableUnusedAttributes()}function numericalSort(a,b){return b[0]-a[0]}function painterSortStable(a,b){return a.object.renderOrder!==b.object.renderOrder?a.object.renderOrder-b.object.renderOrder:a.material.id!==b.material.id?a.material.id-b.material.id:a.z!==b.z?a.z-b.z:a.id-b.id}function reversePainterSortStable(a,b){return a.object.renderOrder!==b.object.renderOrder?a.object.renderOrder-b.object.renderOrder:a.z!==b.z?b.z-a.z:a.id-b.id}function pushRenderItem(object,geometry,material,z,group){var array,index;material.transparent?(array=transparentObjects,index=++transparentObjectsLastIndex):(array=opaqueObjects,index=++opaqueObjectsLastIndex);var renderItem=array[index];void 0!==renderItem?(renderItem.id=object.id,renderItem.object=object,renderItem.geometry=geometry,renderItem.material=material,renderItem.z=_vector3.z,renderItem.group=group):(renderItem={id:object.id,object:object,geometry:geometry,material:material,z:_vector3.z,group:group},array.push(renderItem))}function projectObject(object,camera){if(object.visible!==!1){if(0!==(object.channels.mask&camera.channels.mask))if(object instanceof THREE.Light)lights.push(object);else if(object instanceof THREE.Sprite)sprites.push(object);else if(object instanceof THREE.LensFlare)lensFlares.push(object);else if(object instanceof THREE.ImmediateRenderObject)_this.sortObjects===!0&&(_vector3.setFromMatrixPosition(object.matrixWorld),_vector3.applyProjection(_projScreenMatrix)),pushRenderItem(object,null,object.material,_vector3.z,null);else if((object instanceof THREE.Mesh||object instanceof THREE.Line||object instanceof THREE.Points)&&(object instanceof THREE.SkinnedMesh&&object.skeleton.update(),object.frustumCulled===!1||_frustum.intersectsObject(object)===!0)){var material=object.material;if(material.visible===!0){_this.sortObjects===!0&&(_vector3.setFromMatrixPosition(object.matrixWorld),_vector3.applyProjection(_projScreenMatrix));var geometry=objects.update(object);if(material instanceof THREE.MeshFaceMaterial)for(var groups=geometry.groups,materials=material.materials,i=0,l=groups.length;l>i;i++){var group=groups[i],groupMaterial=materials[group.materialIndex];groupMaterial.visible===!0&&pushRenderItem(object,geometry,groupMaterial,_vector3.z,group)}else pushRenderItem(object,geometry,material,_vector3.z,null)}}for(var children=object.children,i=0,l=children.length;l>i;i++)projectObject(children[i],camera)}}function renderObjects(renderList,camera,lights,fog,overrideMaterial){for(var i=0,l=renderList.length;l>i;i++){var renderItem=renderList[i],object=renderItem.object,geometry=renderItem.geometry,material=void 0===overrideMaterial?renderItem.material:overrideMaterial,group=renderItem.group;if(object.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse,object.matrixWorld),object.normalMatrix.getNormalMatrix(object.modelViewMatrix),object instanceof THREE.ImmediateRenderObject){setMaterial(material);var program=setProgram(camera,lights,fog,material,object);_currentGeometryProgram="",object.render(function(object){_this.renderBufferImmediate(object,program,material)})}else _this.renderBufferDirect(camera,lights,fog,geometry,material,object,group)}}function initMaterial(material,lights,fog,object){var materialProperties=properties.get(material),parameters=programCache.getParameters(material,lights,fog,object),code=programCache.getProgramCode(material,parameters),program=materialProperties.program,programChange=!0;if(void 0===program)material.addEventListener("dispose",onMaterialDispose);else if(program.code!==code)releaseMaterialProgramReference(material);else{if(void 0!==parameters.shaderID)return;programChange=!1}if(programChange){if(parameters.shaderID){var shader=THREE.ShaderLib[parameters.shaderID];materialProperties.__webglShader={name:material.type,uniforms:THREE.UniformsUtils.clone(shader.uniforms),vertexShader:shader.vertexShader,fragmentShader:shader.fragmentShader}}else materialProperties.__webglShader={name:material.type,uniforms:material.uniforms,vertexShader:material.vertexShader,fragmentShader:material.fragmentShader};material.__webglShader=materialProperties.__webglShader,program=programCache.acquireProgram(material,parameters,code),materialProperties.program=program,material.program=program}var attributes=program.getAttributes();if(material.morphTargets){material.numSupportedMorphTargets=0;for(var i=0;i<_this.maxMorphTargets;i++)attributes["morphTarget"+i]>=0&&material.numSupportedMorphTargets++}if(material.morphNormals)for(material.numSupportedMorphNormals=0,i=0;i<_this.maxMorphNormals;i++)attributes["morphNormal"+i]>=0&&material.numSupportedMorphNormals++;materialProperties.uniformsList=[];var uniformLocations=materialProperties.program.getUniforms();for(var u in materialProperties.__webglShader.uniforms){var location=uniformLocations[u];location&&materialProperties.uniformsList.push([materialProperties.__webglShader.uniforms[u],location])}}function setMaterial(material){setMaterialFaces(material),material.transparent===!0?state.setBlending(material.blending,material.blendEquation,material.blendSrc,material.blendDst,material.blendEquationAlpha,material.blendSrcAlpha,material.blendDstAlpha):state.setBlending(THREE.NoBlending),state.setDepthFunc(material.depthFunc),state.setDepthTest(material.depthTest),state.setDepthWrite(material.depthWrite),state.setColorWrite(material.colorWrite),state.setPolygonOffset(material.polygonOffset,material.polygonOffsetFactor,material.polygonOffsetUnits)}function setMaterialFaces(material){material.side!==THREE.DoubleSide?state.enable(_gl.CULL_FACE):state.disable(_gl.CULL_FACE),state.setFlipSided(material.side===THREE.BackSide)}function setProgram(camera,lights,fog,material,object){_usedTextureUnits=0;var materialProperties=properties.get(material);!material.needsUpdate&&materialProperties.program||(initMaterial(material,lights,fog,object),material.needsUpdate=!1);var refreshProgram=!1,refreshMaterial=!1,refreshLights=!1,program=materialProperties.program,p_uniforms=program.getUniforms(),m_uniforms=materialProperties.__webglShader.uniforms;if(program.id!==_currentProgram&&(_gl.useProgram(program.program),_currentProgram=program.id,refreshProgram=!0,refreshMaterial=!0,refreshLights=!0),material.id!==_currentMaterialId&&(-1===_currentMaterialId&&(refreshLights=!0),_currentMaterialId=material.id,refreshMaterial=!0),(refreshProgram||camera!==_currentCamera)&&(_gl.uniformMatrix4fv(p_uniforms.projectionMatrix,!1,camera.projectionMatrix.elements),capabilities.logarithmicDepthBuffer&&_gl.uniform1f(p_uniforms.logDepthBufFC,2/(Math.log(camera.far+1)/Math.LN2)),camera!==_currentCamera&&(_currentCamera=camera),(material instanceof THREE.ShaderMaterial||material instanceof THREE.MeshPhongMaterial||material.envMap)&&void 0!==p_uniforms.cameraPosition&&(_vector3.setFromMatrixPosition(camera.matrixWorld),_gl.uniform3f(p_uniforms.cameraPosition,_vector3.x,_vector3.y,_vector3.z)),(material instanceof THREE.MeshPhongMaterial||material instanceof THREE.MeshLambertMaterial||material instanceof THREE.MeshBasicMaterial||material instanceof THREE.ShaderMaterial||material.skinning)&&void 0!==p_uniforms.viewMatrix&&_gl.uniformMatrix4fv(p_uniforms.viewMatrix,!1,camera.matrixWorldInverse.elements)),material.skinning)if(object.bindMatrix&&void 0!==p_uniforms.bindMatrix&&_gl.uniformMatrix4fv(p_uniforms.bindMatrix,!1,object.bindMatrix.elements),object.bindMatrixInverse&&void 0!==p_uniforms.bindMatrixInverse&&_gl.uniformMatrix4fv(p_uniforms.bindMatrixInverse,!1,object.bindMatrixInverse.elements),capabilities.floatVertexTextures&&object.skeleton&&object.skeleton.useVertexTexture){if(void 0!==p_uniforms.boneTexture){var textureUnit=getTextureUnit();_gl.uniform1i(p_uniforms.boneTexture,textureUnit),_this.setTexture(object.skeleton.boneTexture,textureUnit)}void 0!==p_uniforms.boneTextureWidth&&_gl.uniform1i(p_uniforms.boneTextureWidth,object.skeleton.boneTextureWidth),void 0!==p_uniforms.boneTextureHeight&&_gl.uniform1i(p_uniforms.boneTextureHeight,object.skeleton.boneTextureHeight)}else object.skeleton&&object.skeleton.boneMatrices&&void 0!==p_uniforms.boneGlobalMatrices&&_gl.uniformMatrix4fv(p_uniforms.boneGlobalMatrices,!1,object.skeleton.boneMatrices);return refreshMaterial&&(fog&&material.fog&&refreshUniformsFog(m_uniforms,fog),(material instanceof THREE.MeshPhongMaterial||material instanceof THREE.MeshLambertMaterial||material.lights)&&(_lightsNeedUpdate&&(refreshLights=!0,setupLights(lights,camera),_lightsNeedUpdate=!1),refreshLights?(refreshUniformsLights(m_uniforms,_lights),markUniformsLightsNeedsUpdate(m_uniforms,!0)):markUniformsLightsNeedsUpdate(m_uniforms,!1)),(material instanceof THREE.MeshBasicMaterial||material instanceof THREE.MeshLambertMaterial||material instanceof THREE.MeshPhongMaterial)&&refreshUniformsCommon(m_uniforms,material),material instanceof THREE.LineBasicMaterial?refreshUniformsLine(m_uniforms,material):material instanceof THREE.LineDashedMaterial?(refreshUniformsLine(m_uniforms,material),refreshUniformsDash(m_uniforms,material)):material instanceof THREE.PointsMaterial?refreshUniformsParticle(m_uniforms,material):material instanceof THREE.MeshPhongMaterial?refreshUniformsPhong(m_uniforms,material):material instanceof THREE.MeshDepthMaterial?(m_uniforms.mNear.value=camera.near,m_uniforms.mFar.value=camera.far,m_uniforms.opacity.value=material.opacity):material instanceof THREE.MeshNormalMaterial&&(m_uniforms.opacity.value=material.opacity),object.receiveShadow&&!material._shadowPass&&refreshUniformsShadow(m_uniforms,lights,camera),loadUniformsGeneric(materialProperties.uniformsList)),loadUniformsMatrices(p_uniforms,object),void 0!==p_uniforms.modelMatrix&&_gl.uniformMatrix4fv(p_uniforms.modelMatrix,!1,object.matrixWorld.elements),program}function refreshUniformsCommon(uniforms,material){uniforms.opacity.value=material.opacity,uniforms.diffuse.value=material.color,material.emissive&&(uniforms.emissive.value=material.emissive),uniforms.map.value=material.map,uniforms.specularMap.value=material.specularMap,uniforms.alphaMap.value=material.alphaMap,material.aoMap&&(uniforms.aoMap.value=material.aoMap,uniforms.aoMapIntensity.value=material.aoMapIntensity);var uvScaleMap;if(material.map?uvScaleMap=material.map:material.specularMap?uvScaleMap=material.specularMap:material.displacementMap?uvScaleMap=material.displacementMap:material.normalMap?uvScaleMap=material.normalMap:material.bumpMap?uvScaleMap=material.bumpMap:material.alphaMap?uvScaleMap=material.alphaMap:material.emissiveMap&&(uvScaleMap=material.emissiveMap),void 0!==uvScaleMap){uvScaleMap instanceof THREE.WebGLRenderTarget&&(uvScaleMap=uvScaleMap.texture);var offset=uvScaleMap.offset,repeat=uvScaleMap.repeat;uniforms.offsetRepeat.value.set(offset.x,offset.y,repeat.x,repeat.y)}uniforms.envMap.value=material.envMap,uniforms.flipEnvMap.value=material.envMap instanceof THREE.WebGLRenderTargetCube?1:-1,uniforms.reflectivity.value=material.reflectivity,uniforms.refractionRatio.value=material.refractionRatio}function refreshUniformsLine(uniforms,material){uniforms.diffuse.value=material.color,uniforms.opacity.value=material.opacity; +}function refreshUniformsDash(uniforms,material){uniforms.dashSize.value=material.dashSize,uniforms.totalSize.value=material.dashSize+material.gapSize,uniforms.scale.value=material.scale}function refreshUniformsParticle(uniforms,material){if(uniforms.psColor.value=material.color,uniforms.opacity.value=material.opacity,uniforms.size.value=material.size,uniforms.scale.value=_canvas.height/2,uniforms.map.value=material.map,null!==material.map){var offset=material.map.offset,repeat=material.map.repeat;uniforms.offsetRepeat.value.set(offset.x,offset.y,repeat.x,repeat.y)}}function refreshUniformsFog(uniforms,fog){uniforms.fogColor.value=fog.color,fog instanceof THREE.Fog?(uniforms.fogNear.value=fog.near,uniforms.fogFar.value=fog.far):fog instanceof THREE.FogExp2&&(uniforms.fogDensity.value=fog.density)}function refreshUniformsPhong(uniforms,material){uniforms.specular.value=material.specular,uniforms.shininess.value=Math.max(material.shininess,1e-4),material.lightMap&&(uniforms.lightMap.value=material.lightMap,uniforms.lightMapIntensity.value=material.lightMapIntensity),material.emissiveMap&&(uniforms.emissiveMap.value=material.emissiveMap),material.bumpMap&&(uniforms.bumpMap.value=material.bumpMap,uniforms.bumpScale.value=material.bumpScale),material.normalMap&&(uniforms.normalMap.value=material.normalMap,uniforms.normalScale.value.copy(material.normalScale)),material.displacementMap&&(uniforms.displacementMap.value=material.displacementMap,uniforms.displacementScale.value=material.displacementScale,uniforms.displacementBias.value=material.displacementBias)}function refreshUniformsLights(uniforms,lights){uniforms.ambientLightColor.value=lights.ambient,uniforms.directionalLightColor.value=lights.directional.colors,uniforms.directionalLightDirection.value=lights.directional.positions,uniforms.pointLightColor.value=lights.point.colors,uniforms.pointLightPosition.value=lights.point.positions,uniforms.pointLightDistance.value=lights.point.distances,uniforms.pointLightDecay.value=lights.point.decays,uniforms.spotLightColor.value=lights.spot.colors,uniforms.spotLightPosition.value=lights.spot.positions,uniforms.spotLightDistance.value=lights.spot.distances,uniforms.spotLightDirection.value=lights.spot.directions,uniforms.spotLightAngleCos.value=lights.spot.anglesCos,uniforms.spotLightExponent.value=lights.spot.exponents,uniforms.spotLightDecay.value=lights.spot.decays,uniforms.hemisphereLightSkyColor.value=lights.hemi.skyColors,uniforms.hemisphereLightGroundColor.value=lights.hemi.groundColors,uniforms.hemisphereLightDirection.value=lights.hemi.positions}function markUniformsLightsNeedsUpdate(uniforms,value){uniforms.ambientLightColor.needsUpdate=value,uniforms.directionalLightColor.needsUpdate=value,uniforms.directionalLightDirection.needsUpdate=value,uniforms.pointLightColor.needsUpdate=value,uniforms.pointLightPosition.needsUpdate=value,uniforms.pointLightDistance.needsUpdate=value,uniforms.pointLightDecay.needsUpdate=value,uniforms.spotLightColor.needsUpdate=value,uniforms.spotLightPosition.needsUpdate=value,uniforms.spotLightDistance.needsUpdate=value,uniforms.spotLightDirection.needsUpdate=value,uniforms.spotLightAngleCos.needsUpdate=value,uniforms.spotLightExponent.needsUpdate=value,uniforms.spotLightDecay.needsUpdate=value,uniforms.hemisphereLightSkyColor.needsUpdate=value,uniforms.hemisphereLightGroundColor.needsUpdate=value,uniforms.hemisphereLightDirection.needsUpdate=value}function refreshUniformsShadow(uniforms,lights,camera){if(uniforms.shadowMatrix)for(var j=0,i=0,il=lights.length;il>i;i++){var light=lights[i];if(light.castShadow===!0&&(light instanceof THREE.PointLight||light instanceof THREE.SpotLight||light instanceof THREE.DirectionalLight)){var shadow=light.shadow;light instanceof THREE.PointLight?(_vector3.setFromMatrixPosition(light.matrixWorld).negate(),shadow.matrix.identity().setPosition(_vector3),uniforms.shadowDarkness.value[j]=-shadow.darkness):uniforms.shadowDarkness.value[j]=shadow.darkness,uniforms.shadowMatrix.value[j]=shadow.matrix,uniforms.shadowMap.value[j]=shadow.map,uniforms.shadowMapSize.value[j]=shadow.mapSize,uniforms.shadowBias.value[j]=shadow.bias,j++}}}function loadUniformsMatrices(uniforms,object){_gl.uniformMatrix4fv(uniforms.modelViewMatrix,!1,object.modelViewMatrix.elements),uniforms.normalMatrix&&_gl.uniformMatrix3fv(uniforms.normalMatrix,!1,object.normalMatrix.elements)}function getTextureUnit(){var textureUnit=_usedTextureUnits;return textureUnit>=capabilities.maxTextures&&console.warn("WebGLRenderer: trying to use "+textureUnit+" texture units while this GPU supports only "+capabilities.maxTextures),_usedTextureUnits+=1,textureUnit}function loadUniformsGeneric(uniforms){for(var texture,textureUnit,j=0,jl=uniforms.length;jl>j;j++){var uniform=uniforms[j][0];if(uniform.needsUpdate!==!1){var type=uniform.type,value=uniform.value,location=uniforms[j][1];switch(type){case"1i":_gl.uniform1i(location,value);break;case"1f":_gl.uniform1f(location,value);break;case"2f":_gl.uniform2f(location,value[0],value[1]);break;case"3f":_gl.uniform3f(location,value[0],value[1],value[2]);break;case"4f":_gl.uniform4f(location,value[0],value[1],value[2],value[3]);break;case"1iv":_gl.uniform1iv(location,value);break;case"3iv":_gl.uniform3iv(location,value);break;case"1fv":_gl.uniform1fv(location,value);break;case"2fv":_gl.uniform2fv(location,value);break;case"3fv":_gl.uniform3fv(location,value);break;case"4fv":_gl.uniform4fv(location,value);break;case"Matrix3fv":_gl.uniformMatrix3fv(location,!1,value);break;case"Matrix4fv":_gl.uniformMatrix4fv(location,!1,value);break;case"i":_gl.uniform1i(location,value);break;case"f":_gl.uniform1f(location,value);break;case"v2":_gl.uniform2f(location,value.x,value.y);break;case"v3":_gl.uniform3f(location,value.x,value.y,value.z);break;case"v4":_gl.uniform4f(location,value.x,value.y,value.z,value.w);break;case"c":_gl.uniform3f(location,value.r,value.g,value.b);break;case"iv1":_gl.uniform1iv(location,value);break;case"iv":_gl.uniform3iv(location,value);break;case"fv1":_gl.uniform1fv(location,value);break;case"fv":_gl.uniform3fv(location,value);break;case"v2v":void 0===uniform._array&&(uniform._array=new Float32Array(2*value.length));for(var i=0,i2=0,il=value.length;il>i;i++,i2+=2)uniform._array[i2+0]=value[i].x,uniform._array[i2+1]=value[i].y;_gl.uniform2fv(location,uniform._array);break;case"v3v":void 0===uniform._array&&(uniform._array=new Float32Array(3*value.length));for(var i=0,i3=0,il=value.length;il>i;i++,i3+=3)uniform._array[i3+0]=value[i].x,uniform._array[i3+1]=value[i].y,uniform._array[i3+2]=value[i].z;_gl.uniform3fv(location,uniform._array);break;case"v4v":void 0===uniform._array&&(uniform._array=new Float32Array(4*value.length));for(var i=0,i4=0,il=value.length;il>i;i++,i4+=4)uniform._array[i4+0]=value[i].x,uniform._array[i4+1]=value[i].y,uniform._array[i4+2]=value[i].z,uniform._array[i4+3]=value[i].w;_gl.uniform4fv(location,uniform._array);break;case"m3":_gl.uniformMatrix3fv(location,!1,value.elements);break;case"m3v":void 0===uniform._array&&(uniform._array=new Float32Array(9*value.length));for(var i=0,il=value.length;il>i;i++)value[i].flattenToArrayOffset(uniform._array,9*i);_gl.uniformMatrix3fv(location,!1,uniform._array);break;case"m4":_gl.uniformMatrix4fv(location,!1,value.elements);break;case"m4v":void 0===uniform._array&&(uniform._array=new Float32Array(16*value.length));for(var i=0,il=value.length;il>i;i++)value[i].flattenToArrayOffset(uniform._array,16*i);_gl.uniformMatrix4fv(location,!1,uniform._array);break;case"t":if(texture=value,textureUnit=getTextureUnit(),_gl.uniform1i(location,textureUnit),!texture)continue;texture instanceof THREE.CubeTexture||Array.isArray(texture.image)&&6===texture.image.length?setCubeTexture(texture,textureUnit):texture instanceof THREE.WebGLRenderTargetCube?setCubeTextureDynamic(texture.texture,textureUnit):texture instanceof THREE.WebGLRenderTarget?_this.setTexture(texture.texture,textureUnit):_this.setTexture(texture,textureUnit);break;case"tv":void 0===uniform._array&&(uniform._array=[]);for(var i=0,il=uniform.value.length;il>i;i++)uniform._array[i]=getTextureUnit();_gl.uniform1iv(location,uniform._array);for(var i=0,il=uniform.value.length;il>i;i++)texture=uniform.value[i],textureUnit=uniform._array[i],texture&&(texture instanceof THREE.CubeTexture||texture.image instanceof Array&&6===texture.image.length?setCubeTexture(texture,textureUnit):texture instanceof THREE.WebGLRenderTarget?_this.setTexture(texture.texture,textureUnit):texture instanceof THREE.WebGLRenderTargetCube?setCubeTextureDynamic(texture.texture,textureUnit):_this.setTexture(texture,textureUnit));break;default:console.warn("THREE.WebGLRenderer: Unknown uniform type: "+type)}}}}function setColorLinear(array,offset,color,intensity){array[offset+0]=color.r*intensity,array[offset+1]=color.g*intensity,array[offset+2]=color.b*intensity}function setupLights(lights,camera){var l,ll,light,color,skyColor,groundColor,intensity,distance,r=0,g=0,b=0,zlights=_lights,viewMatrix=camera.matrixWorldInverse,dirColors=zlights.directional.colors,dirPositions=zlights.directional.positions,pointColors=zlights.point.colors,pointPositions=zlights.point.positions,pointDistances=zlights.point.distances,pointDecays=zlights.point.decays,spotColors=zlights.spot.colors,spotPositions=zlights.spot.positions,spotDistances=zlights.spot.distances,spotDirections=zlights.spot.directions,spotAnglesCos=zlights.spot.anglesCos,spotExponents=zlights.spot.exponents,spotDecays=zlights.spot.decays,hemiSkyColors=zlights.hemi.skyColors,hemiGroundColors=zlights.hemi.groundColors,hemiPositions=zlights.hemi.positions,dirLength=0,pointLength=0,spotLength=0,hemiLength=0,dirCount=0,pointCount=0,spotCount=0,hemiCount=0,dirOffset=0,pointOffset=0,spotOffset=0,hemiOffset=0;for(l=0,ll=lights.length;ll>l;l++)if(light=lights[l],color=light.color,intensity=light.intensity,distance=light.distance,light instanceof THREE.AmbientLight){if(!light.visible)continue;r+=color.r,g+=color.g,b+=color.b}else if(light instanceof THREE.DirectionalLight){if(dirCount+=1,!light.visible)continue;_direction.setFromMatrixPosition(light.matrixWorld),_vector3.setFromMatrixPosition(light.target.matrixWorld),_direction.sub(_vector3),_direction.transformDirection(viewMatrix),dirOffset=3*dirLength,dirPositions[dirOffset+0]=_direction.x,dirPositions[dirOffset+1]=_direction.y,dirPositions[dirOffset+2]=_direction.z,setColorLinear(dirColors,dirOffset,color,intensity),dirLength+=1}else if(light instanceof THREE.PointLight){if(pointCount+=1,!light.visible)continue;pointOffset=3*pointLength,setColorLinear(pointColors,pointOffset,color,intensity),_vector3.setFromMatrixPosition(light.matrixWorld),_vector3.applyMatrix4(viewMatrix),pointPositions[pointOffset+0]=_vector3.x,pointPositions[pointOffset+1]=_vector3.y,pointPositions[pointOffset+2]=_vector3.z,pointDistances[pointLength]=distance,pointDecays[pointLength]=0===light.distance?0:light.decay,pointLength+=1}else if(light instanceof THREE.SpotLight){if(spotCount+=1,!light.visible)continue;spotOffset=3*spotLength,setColorLinear(spotColors,spotOffset,color,intensity),_direction.setFromMatrixPosition(light.matrixWorld),_vector3.copy(_direction).applyMatrix4(viewMatrix),spotPositions[spotOffset+0]=_vector3.x,spotPositions[spotOffset+1]=_vector3.y,spotPositions[spotOffset+2]=_vector3.z,spotDistances[spotLength]=distance,_vector3.setFromMatrixPosition(light.target.matrixWorld),_direction.sub(_vector3),_direction.transformDirection(viewMatrix),spotDirections[spotOffset+0]=_direction.x,spotDirections[spotOffset+1]=_direction.y,spotDirections[spotOffset+2]=_direction.z,spotAnglesCos[spotLength]=Math.cos(light.angle),spotExponents[spotLength]=light.exponent,spotDecays[spotLength]=0===light.distance?0:light.decay,spotLength+=1}else if(light instanceof THREE.HemisphereLight){if(hemiCount+=1,!light.visible)continue;_direction.setFromMatrixPosition(light.matrixWorld),_direction.transformDirection(viewMatrix),hemiOffset=3*hemiLength,hemiPositions[hemiOffset+0]=_direction.x,hemiPositions[hemiOffset+1]=_direction.y,hemiPositions[hemiOffset+2]=_direction.z,skyColor=light.color,groundColor=light.groundColor,setColorLinear(hemiSkyColors,hemiOffset,skyColor,intensity),setColorLinear(hemiGroundColors,hemiOffset,groundColor,intensity),hemiLength+=1}for(l=3*dirLength,ll=Math.max(dirColors.length,3*dirCount);ll>l;l++)dirColors[l]=0;for(l=3*pointLength,ll=Math.max(pointColors.length,3*pointCount);ll>l;l++)pointColors[l]=0;for(l=3*spotLength,ll=Math.max(spotColors.length,3*spotCount);ll>l;l++)spotColors[l]=0;for(l=3*hemiLength,ll=Math.max(hemiSkyColors.length,3*hemiCount);ll>l;l++)hemiSkyColors[l]=0;for(l=3*hemiLength,ll=Math.max(hemiGroundColors.length,3*hemiCount);ll>l;l++)hemiGroundColors[l]=0;zlights.directional.length=dirLength,zlights.point.length=pointLength,zlights.spot.length=spotLength,zlights.hemi.length=hemiLength,zlights.ambient[0]=r,zlights.ambient[1]=g,zlights.ambient[2]=b}function setTextureParameters(textureType,texture,isImagePowerOfTwo){var extension;if(isImagePowerOfTwo?(_gl.texParameteri(textureType,_gl.TEXTURE_WRAP_S,paramThreeToGL(texture.wrapS)),_gl.texParameteri(textureType,_gl.TEXTURE_WRAP_T,paramThreeToGL(texture.wrapT)),_gl.texParameteri(textureType,_gl.TEXTURE_MAG_FILTER,paramThreeToGL(texture.magFilter)),_gl.texParameteri(textureType,_gl.TEXTURE_MIN_FILTER,paramThreeToGL(texture.minFilter))):(_gl.texParameteri(textureType,_gl.TEXTURE_WRAP_S,_gl.CLAMP_TO_EDGE),_gl.texParameteri(textureType,_gl.TEXTURE_WRAP_T,_gl.CLAMP_TO_EDGE),texture.wrapS===THREE.ClampToEdgeWrapping&&texture.wrapT===THREE.ClampToEdgeWrapping||console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.",texture),_gl.texParameteri(textureType,_gl.TEXTURE_MAG_FILTER,filterFallback(texture.magFilter)),_gl.texParameteri(textureType,_gl.TEXTURE_MIN_FILTER,filterFallback(texture.minFilter)),texture.minFilter!==THREE.NearestFilter&&texture.minFilter!==THREE.LinearFilter&&console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.",texture)),extension=extensions.get("EXT_texture_filter_anisotropic")){if(texture.type===THREE.FloatType&&null===extensions.get("OES_texture_float_linear"))return;if(texture.type===THREE.HalfFloatType&&null===extensions.get("OES_texture_half_float_linear"))return;(texture.anisotropy>1||properties.get(texture).__currentAnisotropy)&&(_gl.texParameterf(textureType,extension.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(texture.anisotropy,_this.getMaxAnisotropy())),properties.get(texture).__currentAnisotropy=texture.anisotropy)}}function uploadTexture(textureProperties,texture,slot){void 0===textureProperties.__webglInit&&(textureProperties.__webglInit=!0,texture.addEventListener("dispose",onTextureDispose),textureProperties.__webglTexture=_gl.createTexture(),_infoMemory.textures++),state.activeTexture(_gl.TEXTURE0+slot),state.bindTexture(_gl.TEXTURE_2D,textureProperties.__webglTexture),_gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL,texture.flipY),_gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL,texture.premultiplyAlpha),_gl.pixelStorei(_gl.UNPACK_ALIGNMENT,texture.unpackAlignment),texture.image=clampToMaxSize(texture.image,capabilities.maxTextureSize),textureNeedsPowerOfTwo(texture)&&isPowerOfTwo(texture.image)===!1&&(texture.image=makePowerOfTwo(texture.image));var image=texture.image,isImagePowerOfTwo=isPowerOfTwo(image),glFormat=paramThreeToGL(texture.format),glType=paramThreeToGL(texture.type);setTextureParameters(_gl.TEXTURE_2D,texture,isImagePowerOfTwo);var mipmap,mipmaps=texture.mipmaps;if(texture instanceof THREE.DataTexture)if(mipmaps.length>0&&isImagePowerOfTwo){for(var i=0,il=mipmaps.length;il>i;i++)mipmap=mipmaps[i],state.texImage2D(_gl.TEXTURE_2D,i,glFormat,mipmap.width,mipmap.height,0,glFormat,glType,mipmap.data);texture.generateMipmaps=!1}else state.texImage2D(_gl.TEXTURE_2D,0,glFormat,image.width,image.height,0,glFormat,glType,image.data);else if(texture instanceof THREE.CompressedTexture)for(var i=0,il=mipmaps.length;il>i;i++)mipmap=mipmaps[i],texture.format!==THREE.RGBAFormat&&texture.format!==THREE.RGBFormat?state.getCompressedTextureFormats().indexOf(glFormat)>-1?state.compressedTexImage2D(_gl.TEXTURE_2D,i,glFormat,mipmap.width,mipmap.height,0,mipmap.data):console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()"):state.texImage2D(_gl.TEXTURE_2D,i,glFormat,mipmap.width,mipmap.height,0,glFormat,glType,mipmap.data);else if(mipmaps.length>0&&isImagePowerOfTwo){for(var i=0,il=mipmaps.length;il>i;i++)mipmap=mipmaps[i],state.texImage2D(_gl.TEXTURE_2D,i,glFormat,glFormat,glType,mipmap);texture.generateMipmaps=!1}else state.texImage2D(_gl.TEXTURE_2D,0,glFormat,glFormat,glType,texture.image);texture.generateMipmaps&&isImagePowerOfTwo&&_gl.generateMipmap(_gl.TEXTURE_2D),textureProperties.__version=texture.version,texture.onUpdate&&texture.onUpdate(texture)}function clampToMaxSize(image,maxSize){if(image.width>maxSize||image.height>maxSize){var scale=maxSize/Math.max(image.width,image.height),canvas=document.createElement("canvas");canvas.width=Math.floor(image.width*scale),canvas.height=Math.floor(image.height*scale);var context=canvas.getContext("2d");return context.drawImage(image,0,0,image.width,image.height,0,0,canvas.width,canvas.height),console.warn("THREE.WebGLRenderer: image is too big ("+image.width+"x"+image.height+"). Resized to "+canvas.width+"x"+canvas.height,image),canvas}return image}function isPowerOfTwo(image){return THREE.Math.isPowerOfTwo(image.width)&&THREE.Math.isPowerOfTwo(image.height)}function textureNeedsPowerOfTwo(texture){return texture.wrapS!==THREE.ClampToEdgeWrapping||texture.wrapT!==THREE.ClampToEdgeWrapping?!0:texture.minFilter!==THREE.NearestFilter&&texture.minFilter!==THREE.LinearFilter}function makePowerOfTwo(image){if(image instanceof HTMLImageElement||image instanceof HTMLCanvasElement){var canvas=document.createElement("canvas");canvas.width=THREE.Math.nearestPowerOfTwo(image.width),canvas.height=THREE.Math.nearestPowerOfTwo(image.height);var context=canvas.getContext("2d");return context.drawImage(image,0,0,canvas.width,canvas.height),console.warn("THREE.WebGLRenderer: image is not power of two ("+image.width+"x"+image.height+"). Resized to "+canvas.width+"x"+canvas.height,image),canvas}return image}function setCubeTexture(texture,slot){var textureProperties=properties.get(texture);if(6===texture.image.length)if(texture.version>0&&textureProperties.__version!==texture.version){textureProperties.__image__webglTextureCube||(texture.addEventListener("dispose",onTextureDispose),textureProperties.__image__webglTextureCube=_gl.createTexture(),_infoMemory.textures++),state.activeTexture(_gl.TEXTURE0+slot),state.bindTexture(_gl.TEXTURE_CUBE_MAP,textureProperties.__image__webglTextureCube),_gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL,texture.flipY);for(var isCompressed=texture instanceof THREE.CompressedTexture,isDataTexture=texture.image[0]instanceof THREE.DataTexture,cubeImage=[],i=0;6>i;i++)!_this.autoScaleCubemaps||isCompressed||isDataTexture?cubeImage[i]=isDataTexture?texture.image[i].image:texture.image[i]:cubeImage[i]=clampToMaxSize(texture.image[i],capabilities.maxCubemapSize);var image=cubeImage[0],isImagePowerOfTwo=isPowerOfTwo(image),glFormat=paramThreeToGL(texture.format),glType=paramThreeToGL(texture.type);setTextureParameters(_gl.TEXTURE_CUBE_MAP,texture,isImagePowerOfTwo);for(var i=0;6>i;i++)if(isCompressed)for(var mipmap,mipmaps=cubeImage[i].mipmaps,j=0,jl=mipmaps.length;jl>j;j++)mipmap=mipmaps[j],texture.format!==THREE.RGBAFormat&&texture.format!==THREE.RGBFormat?state.getCompressedTextureFormats().indexOf(glFormat)>-1?state.compressedTexImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,j,glFormat,mipmap.width,mipmap.height,0,mipmap.data):console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setCubeTexture()"):state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,j,glFormat,mipmap.width,mipmap.height,0,glFormat,glType,mipmap.data);else isDataTexture?state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,0,glFormat,cubeImage[i].width,cubeImage[i].height,0,glFormat,glType,cubeImage[i].data):state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,0,glFormat,glFormat,glType,cubeImage[i]);texture.generateMipmaps&&isImagePowerOfTwo&&_gl.generateMipmap(_gl.TEXTURE_CUBE_MAP),textureProperties.__version=texture.version,texture.onUpdate&&texture.onUpdate(texture)}else state.activeTexture(_gl.TEXTURE0+slot),state.bindTexture(_gl.TEXTURE_CUBE_MAP,textureProperties.__image__webglTextureCube)}function setCubeTextureDynamic(texture,slot){state.activeTexture(_gl.TEXTURE0+slot),state.bindTexture(_gl.TEXTURE_CUBE_MAP,properties.get(texture).__webglTexture)}function setupFrameBuffer(framebuffer,renderTarget,textureTarget){_gl.bindFramebuffer(_gl.FRAMEBUFFER,framebuffer),_gl.framebufferTexture2D(_gl.FRAMEBUFFER,_gl.COLOR_ATTACHMENT0,textureTarget,properties.get(renderTarget.texture).__webglTexture,0)}function setupRenderBuffer(renderbuffer,renderTarget){_gl.bindRenderbuffer(_gl.RENDERBUFFER,renderbuffer),renderTarget.depthBuffer&&!renderTarget.stencilBuffer?(_gl.renderbufferStorage(_gl.RENDERBUFFER,_gl.DEPTH_COMPONENT16,renderTarget.width,renderTarget.height),_gl.framebufferRenderbuffer(_gl.FRAMEBUFFER,_gl.DEPTH_ATTACHMENT,_gl.RENDERBUFFER,renderbuffer)):renderTarget.depthBuffer&&renderTarget.stencilBuffer?(_gl.renderbufferStorage(_gl.RENDERBUFFER,_gl.DEPTH_STENCIL,renderTarget.width,renderTarget.height),_gl.framebufferRenderbuffer(_gl.FRAMEBUFFER,_gl.DEPTH_STENCIL_ATTACHMENT,_gl.RENDERBUFFER,renderbuffer)):_gl.renderbufferStorage(_gl.RENDERBUFFER,_gl.RGBA4,renderTarget.width,renderTarget.height)}function updateRenderTargetMipmap(renderTarget){var target=renderTarget instanceof THREE.WebGLRenderTargetCube?_gl.TEXTURE_CUBE_MAP:_gl.TEXTURE_2D,texture=properties.get(renderTarget.texture).__webglTexture;state.bindTexture(target,texture),_gl.generateMipmap(target),state.bindTexture(target,null)}function filterFallback(f){return f===THREE.NearestFilter||f===THREE.NearestMipMapNearestFilter||f===THREE.NearestMipMapLinearFilter?_gl.NEAREST:_gl.LINEAR}function paramThreeToGL(p){var extension;if(p===THREE.RepeatWrapping)return _gl.REPEAT;if(p===THREE.ClampToEdgeWrapping)return _gl.CLAMP_TO_EDGE;if(p===THREE.MirroredRepeatWrapping)return _gl.MIRRORED_REPEAT;if(p===THREE.NearestFilter)return _gl.NEAREST;if(p===THREE.NearestMipMapNearestFilter)return _gl.NEAREST_MIPMAP_NEAREST;if(p===THREE.NearestMipMapLinearFilter)return _gl.NEAREST_MIPMAP_LINEAR;if(p===THREE.LinearFilter)return _gl.LINEAR;if(p===THREE.LinearMipMapNearestFilter)return _gl.LINEAR_MIPMAP_NEAREST;if(p===THREE.LinearMipMapLinearFilter)return _gl.LINEAR_MIPMAP_LINEAR;if(p===THREE.UnsignedByteType)return _gl.UNSIGNED_BYTE;if(p===THREE.UnsignedShort4444Type)return _gl.UNSIGNED_SHORT_4_4_4_4;if(p===THREE.UnsignedShort5551Type)return _gl.UNSIGNED_SHORT_5_5_5_1;if(p===THREE.UnsignedShort565Type)return _gl.UNSIGNED_SHORT_5_6_5;if(p===THREE.ByteType)return _gl.BYTE;if(p===THREE.ShortType)return _gl.SHORT;if(p===THREE.UnsignedShortType)return _gl.UNSIGNED_SHORT;if(p===THREE.IntType)return _gl.INT;if(p===THREE.UnsignedIntType)return _gl.UNSIGNED_INT;if(p===THREE.FloatType)return _gl.FLOAT;if(extension=extensions.get("OES_texture_half_float"),null!==extension&&p===THREE.HalfFloatType)return extension.HALF_FLOAT_OES;if(p===THREE.AlphaFormat)return _gl.ALPHA;if(p===THREE.RGBFormat)return _gl.RGB;if(p===THREE.RGBAFormat)return _gl.RGBA;if(p===THREE.LuminanceFormat)return _gl.LUMINANCE;if(p===THREE.LuminanceAlphaFormat)return _gl.LUMINANCE_ALPHA;if(p===THREE.AddEquation)return _gl.FUNC_ADD;if(p===THREE.SubtractEquation)return _gl.FUNC_SUBTRACT;if(p===THREE.ReverseSubtractEquation)return _gl.FUNC_REVERSE_SUBTRACT;if(p===THREE.ZeroFactor)return _gl.ZERO;if(p===THREE.OneFactor)return _gl.ONE;if(p===THREE.SrcColorFactor)return _gl.SRC_COLOR;if(p===THREE.OneMinusSrcColorFactor)return _gl.ONE_MINUS_SRC_COLOR;if(p===THREE.SrcAlphaFactor)return _gl.SRC_ALPHA;if(p===THREE.OneMinusSrcAlphaFactor)return _gl.ONE_MINUS_SRC_ALPHA;if(p===THREE.DstAlphaFactor)return _gl.DST_ALPHA;if(p===THREE.OneMinusDstAlphaFactor)return _gl.ONE_MINUS_DST_ALPHA;if(p===THREE.DstColorFactor)return _gl.DST_COLOR;if(p===THREE.OneMinusDstColorFactor)return _gl.ONE_MINUS_DST_COLOR;if(p===THREE.SrcAlphaSaturateFactor)return _gl.SRC_ALPHA_SATURATE;if(extension=extensions.get("WEBGL_compressed_texture_s3tc"),null!==extension){if(p===THREE.RGB_S3TC_DXT1_Format)return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;if(p===THREE.RGBA_S3TC_DXT1_Format)return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(p===THREE.RGBA_S3TC_DXT3_Format)return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(p===THREE.RGBA_S3TC_DXT5_Format)return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT}if(extension=extensions.get("WEBGL_compressed_texture_pvrtc"),null!==extension){if(p===THREE.RGB_PVRTC_4BPPV1_Format)return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(p===THREE.RGB_PVRTC_2BPPV1_Format)return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(p===THREE.RGBA_PVRTC_4BPPV1_Format)return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;if(p===THREE.RGBA_PVRTC_2BPPV1_Format)return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}if(extension=extensions.get("EXT_blend_minmax"),null!==extension){if(p===THREE.MinEquation)return extension.MIN_EXT;if(p===THREE.MaxEquation)return extension.MAX_EXT}return 0}console.log("THREE.WebGLRenderer",THREE.REVISION),parameters=parameters||{};var _canvas=void 0!==parameters.canvas?parameters.canvas:document.createElement("canvas"),_context=void 0!==parameters.context?parameters.context:null,_width=_canvas.width,_height=_canvas.height,pixelRatio=1,_alpha=void 0!==parameters.alpha?parameters.alpha:!1,_depth=void 0!==parameters.depth?parameters.depth:!0,_stencil=void 0!==parameters.stencil?parameters.stencil:!0,_antialias=void 0!==parameters.antialias?parameters.antialias:!1,_premultipliedAlpha=void 0!==parameters.premultipliedAlpha?parameters.premultipliedAlpha:!0,_preserveDrawingBuffer=void 0!==parameters.preserveDrawingBuffer?parameters.preserveDrawingBuffer:!1,_clearColor=new THREE.Color(0),_clearAlpha=0,lights=[],opaqueObjects=[],opaqueObjectsLastIndex=-1,transparentObjects=[],transparentObjectsLastIndex=-1,morphInfluences=new Float32Array(8),sprites=[],lensFlares=[];this.domElement=_canvas,this.context=null,this.autoClear=!0,this.autoClearColor=!0,this.autoClearDepth=!0,this.autoClearStencil=!0,this.sortObjects=!0,this.gammaFactor=2,this.gammaInput=!1,this.gammaOutput=!1,this.maxMorphTargets=8,this.maxMorphNormals=4,this.autoScaleCubemaps=!0;var _this=this,_currentProgram=null,_currentFramebuffer=null,_currentMaterialId=-1,_currentGeometryProgram="",_currentCamera=null,_usedTextureUnits=0,_viewportX=0,_viewportY=0,_viewportWidth=_canvas.width,_viewportHeight=_canvas.height,_currentWidth=0,_currentHeight=0,_frustum=new THREE.Frustum,_projScreenMatrix=new THREE.Matrix4,_vector3=new THREE.Vector3,_direction=new THREE.Vector3,_lightsNeedUpdate=!0,_lights={ambient:[0,0,0],directional:{length:0,colors:[],positions:[]},point:{length:0,colors:[],positions:[],distances:[],decays:[]},spot:{length:0,colors:[],positions:[],distances:[],directions:[],anglesCos:[],exponents:[],decays:[]},hemi:{length:0,skyColors:[],groundColors:[],positions:[]}},_infoMemory={geometries:0,textures:0},_infoRender={calls:0,vertices:0,faces:0,points:0};this.info={render:_infoRender,memory:_infoMemory,programs:null};var _gl;try{var attributes={alpha:_alpha,depth:_depth,stencil:_stencil,antialias:_antialias,premultipliedAlpha:_premultipliedAlpha,preserveDrawingBuffer:_preserveDrawingBuffer};if(_gl=_context||_canvas.getContext("webgl",attributes)||_canvas.getContext("experimental-webgl",attributes),null===_gl)throw null!==_canvas.getContext("webgl")?"Error creating WebGL context with your selected attributes.":"Error creating WebGL context.";_canvas.addEventListener("webglcontextlost",onContextLost,!1)}catch(error){console.error("THREE.WebGLRenderer: "+error)}var extensions=new THREE.WebGLExtensions(_gl);extensions.get("OES_texture_float"),extensions.get("OES_texture_float_linear"),extensions.get("OES_texture_half_float"),extensions.get("OES_texture_half_float_linear"),extensions.get("OES_standard_derivatives"),extensions.get("ANGLE_instanced_arrays"),extensions.get("OES_element_index_uint")&&(THREE.BufferGeometry.MaxIndex=4294967296);var capabilities=new THREE.WebGLCapabilities(_gl,extensions,parameters),state=new THREE.WebGLState(_gl,extensions,paramThreeToGL),properties=new THREE.WebGLProperties,objects=new THREE.WebGLObjects(_gl,properties,this.info),programCache=new THREE.WebGLPrograms(this,capabilities);this.info.programs=programCache.programs;var bufferRenderer=new THREE.WebGLBufferRenderer(_gl,extensions,_infoRender),indexedBufferRenderer=new THREE.WebGLIndexedBufferRenderer(_gl,extensions,_infoRender);setDefaultGLState(),this.context=_gl,this.capabilities=capabilities,this.extensions=extensions,this.state=state;var shadowMap=new THREE.WebGLShadowMap(this,lights,objects);this.shadowMap=shadowMap;var spritePlugin=new THREE.SpritePlugin(this,sprites),lensFlarePlugin=new THREE.LensFlarePlugin(this,lensFlares);this.getContext=function(){return _gl},this.getContextAttributes=function(){return _gl.getContextAttributes()},this.forceContextLoss=function(){extensions.get("WEBGL_lose_context").loseContext()},this.getMaxAnisotropy=function(){var value;return function(){if(void 0!==value)return value;var extension=extensions.get("EXT_texture_filter_anisotropic");return value=null!==extension?_gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT):0}}(),this.getPrecision=function(){return capabilities.precision},this.getPixelRatio=function(){return pixelRatio},this.setPixelRatio=function(value){void 0!==value&&(pixelRatio=value)},this.getSize=function(){return{width:_width,height:_height}},this.setSize=function(width,height,updateStyle){_width=width,_height=height,_canvas.width=width*pixelRatio,_canvas.height=height*pixelRatio,updateStyle!==!1&&(_canvas.style.width=width+"px",_canvas.style.height=height+"px"),this.setViewport(0,0,width,height)},this.setViewport=function(x,y,width,height){_viewportX=x*pixelRatio,_viewportY=y*pixelRatio,_viewportWidth=width*pixelRatio,_viewportHeight=height*pixelRatio,_gl.viewport(_viewportX,_viewportY,_viewportWidth,_viewportHeight)},this.getViewport=function(dimensions){dimensions.x=_viewportX/pixelRatio,dimensions.y=_viewportY/pixelRatio,dimensions.z=_viewportWidth/pixelRatio,dimensions.w=_viewportHeight/pixelRatio},this.setScissor=function(x,y,width,height){_gl.scissor(x*pixelRatio,y*pixelRatio,width*pixelRatio,height*pixelRatio)},this.enableScissorTest=function(boolean){state.setScissorTest(boolean)},this.getClearColor=function(){return _clearColor},this.setClearColor=function(color,alpha){_clearColor.set(color),_clearAlpha=void 0!==alpha?alpha:1,glClearColor(_clearColor.r,_clearColor.g,_clearColor.b,_clearAlpha)},this.getClearAlpha=function(){return _clearAlpha},this.setClearAlpha=function(alpha){_clearAlpha=alpha,glClearColor(_clearColor.r,_clearColor.g,_clearColor.b,_clearAlpha)},this.clear=function(color,depth,stencil){var bits=0;(void 0===color||color)&&(bits|=_gl.COLOR_BUFFER_BIT),(void 0===depth||depth)&&(bits|=_gl.DEPTH_BUFFER_BIT),(void 0===stencil||stencil)&&(bits|=_gl.STENCIL_BUFFER_BIT),_gl.clear(bits)},this.clearColor=function(){_gl.clear(_gl.COLOR_BUFFER_BIT)},this.clearDepth=function(){_gl.clear(_gl.DEPTH_BUFFER_BIT)},this.clearStencil=function(){_gl.clear(_gl.STENCIL_BUFFER_BIT)},this.clearTarget=function(renderTarget,color,depth,stencil){this.setRenderTarget(renderTarget),this.clear(color,depth,stencil)},this.resetGLState=resetGLState,this.dispose=function(){_canvas.removeEventListener("webglcontextlost",onContextLost,!1)},this.renderBufferImmediate=function(object,program,material){state.initAttributes();var buffers=properties.get(object);object.hasPositions&&!buffers.position&&(buffers.position=_gl.createBuffer()), +object.hasNormals&&!buffers.normal&&(buffers.normal=_gl.createBuffer()),object.hasUvs&&!buffers.uv&&(buffers.uv=_gl.createBuffer()),object.hasColors&&!buffers.color&&(buffers.color=_gl.createBuffer());var attributes=program.getAttributes();if(object.hasPositions&&(_gl.bindBuffer(_gl.ARRAY_BUFFER,buffers.position),_gl.bufferData(_gl.ARRAY_BUFFER,object.positionArray,_gl.DYNAMIC_DRAW),state.enableAttribute(attributes.position),_gl.vertexAttribPointer(attributes.position,3,_gl.FLOAT,!1,0,0)),object.hasNormals){if(_gl.bindBuffer(_gl.ARRAY_BUFFER,buffers.normal),"MeshPhongMaterial"!==material.type&&material.shading===THREE.FlatShading)for(var i=0,l=3*object.count;l>i;i+=9){var array=object.normalArray,nx=(array[i+0]+array[i+3]+array[i+6])/3,ny=(array[i+1]+array[i+4]+array[i+7])/3,nz=(array[i+2]+array[i+5]+array[i+8])/3;array[i+0]=nx,array[i+1]=ny,array[i+2]=nz,array[i+3]=nx,array[i+4]=ny,array[i+5]=nz,array[i+6]=nx,array[i+7]=ny,array[i+8]=nz}_gl.bufferData(_gl.ARRAY_BUFFER,object.normalArray,_gl.DYNAMIC_DRAW),state.enableAttribute(attributes.normal),_gl.vertexAttribPointer(attributes.normal,3,_gl.FLOAT,!1,0,0)}object.hasUvs&&material.map&&(_gl.bindBuffer(_gl.ARRAY_BUFFER,buffers.uv),_gl.bufferData(_gl.ARRAY_BUFFER,object.uvArray,_gl.DYNAMIC_DRAW),state.enableAttribute(attributes.uv),_gl.vertexAttribPointer(attributes.uv,2,_gl.FLOAT,!1,0,0)),object.hasColors&&material.vertexColors!==THREE.NoColors&&(_gl.bindBuffer(_gl.ARRAY_BUFFER,buffers.color),_gl.bufferData(_gl.ARRAY_BUFFER,object.colorArray,_gl.DYNAMIC_DRAW),state.enableAttribute(attributes.color),_gl.vertexAttribPointer(attributes.color,3,_gl.FLOAT,!1,0,0)),state.disableUnusedAttributes(),_gl.drawArrays(_gl.TRIANGLES,0,object.count),object.count=0},this.renderBufferDirect=function(camera,lights,fog,geometry,material,object,group){setMaterial(material);var program=setProgram(camera,lights,fog,material,object),updateBuffers=!1,geometryProgram=geometry.id+"_"+program.id+"_"+material.wireframe;geometryProgram!==_currentGeometryProgram&&(_currentGeometryProgram=geometryProgram,updateBuffers=!0);var morphTargetInfluences=object.morphTargetInfluences;if(void 0!==morphTargetInfluences){for(var activeInfluences=[],i=0,l=morphTargetInfluences.length;l>i;i++){var influence=morphTargetInfluences[i];activeInfluences.push([influence,i])}activeInfluences.sort(numericalSort),activeInfluences.length>8&&(activeInfluences.length=8);for(var morphAttributes=geometry.morphAttributes,i=0,l=activeInfluences.length;l>i;i++){var influence=activeInfluences[i];if(morphInfluences[i]=influence[0],0!==influence[0]){var index=influence[1];material.morphTargets===!0&&morphAttributes.position&&geometry.addAttribute("morphTarget"+i,morphAttributes.position[index]),material.morphNormals===!0&&morphAttributes.normal&&geometry.addAttribute("morphNormal"+i,morphAttributes.normal[index])}else material.morphTargets===!0&&geometry.removeAttribute("morphTarget"+i),material.morphNormals===!0&&geometry.removeAttribute("morphNormal"+i)}var uniforms=program.getUniforms();null!==uniforms.morphTargetInfluences&&_gl.uniform1fv(uniforms.morphTargetInfluences,morphInfluences),updateBuffers=!0}var index=geometry.index,position=geometry.attributes.position;material.wireframe===!0&&(index=objects.getWireframeAttribute(geometry));var renderer;null!==index?(renderer=indexedBufferRenderer,renderer.setIndex(index)):renderer=bufferRenderer,updateBuffers&&(setupVertexAttributes(material,program,geometry),null!==index&&_gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER,objects.getAttributeBuffer(index)));var dataStart=0,dataCount=1/0;null!==index?dataCount=index.count:void 0!==position&&(dataCount=position.count);var rangeStart=geometry.drawRange.start,rangeCount=geometry.drawRange.count,groupStart=null!==group?group.start:0,groupCount=null!==group?group.count:1/0,drawStart=Math.max(dataStart,rangeStart,groupStart),drawEnd=Math.min(dataStart+dataCount,rangeStart+rangeCount,groupStart+groupCount)-1,drawCount=Math.max(0,drawEnd-drawStart+1);if(object instanceof THREE.Mesh)material.wireframe===!0?(state.setLineWidth(material.wireframeLinewidth*pixelRatio),renderer.setMode(_gl.LINES)):renderer.setMode(_gl.TRIANGLES),geometry instanceof THREE.InstancedBufferGeometry&&geometry.maxInstancedCount>0?renderer.renderInstances(geometry):renderer.render(drawStart,drawCount);else if(object instanceof THREE.Line){var lineWidth=material.linewidth;void 0===lineWidth&&(lineWidth=1),state.setLineWidth(lineWidth*pixelRatio),object instanceof THREE.LineSegments?renderer.setMode(_gl.LINES):renderer.setMode(_gl.LINE_STRIP),renderer.render(drawStart,drawCount)}else object instanceof THREE.Points&&(renderer.setMode(_gl.POINTS),renderer.render(drawStart,drawCount))},this.render=function(scene,camera,renderTarget,forceClear){if(camera instanceof THREE.Camera==!1)return void console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.");var fog=scene.fog;if(_currentGeometryProgram="",_currentMaterialId=-1,_currentCamera=null,_lightsNeedUpdate=!0,scene.autoUpdate===!0&&scene.updateMatrixWorld(),null===camera.parent&&camera.updateMatrixWorld(),camera.matrixWorldInverse.getInverse(camera.matrixWorld),_projScreenMatrix.multiplyMatrices(camera.projectionMatrix,camera.matrixWorldInverse),_frustum.setFromMatrix(_projScreenMatrix),lights.length=0,opaqueObjectsLastIndex=-1,transparentObjectsLastIndex=-1,sprites.length=0,lensFlares.length=0,projectObject(scene,camera),opaqueObjects.length=opaqueObjectsLastIndex+1,transparentObjects.length=transparentObjectsLastIndex+1,_this.sortObjects===!0&&(opaqueObjects.sort(painterSortStable),transparentObjects.sort(reversePainterSortStable)),shadowMap.render(scene),_infoRender.calls=0,_infoRender.vertices=0,_infoRender.faces=0,_infoRender.points=0,this.setRenderTarget(renderTarget),(this.autoClear||forceClear)&&this.clear(this.autoClearColor,this.autoClearDepth,this.autoClearStencil),scene.overrideMaterial){var overrideMaterial=scene.overrideMaterial;renderObjects(opaqueObjects,camera,lights,fog,overrideMaterial),renderObjects(transparentObjects,camera,lights,fog,overrideMaterial)}else state.setBlending(THREE.NoBlending),renderObjects(opaqueObjects,camera,lights,fog),renderObjects(transparentObjects,camera,lights,fog);if(spritePlugin.render(scene,camera),lensFlarePlugin.render(scene,camera,_currentWidth,_currentHeight),renderTarget){var texture=renderTarget.texture,isTargetPowerOfTwo=isPowerOfTwo(renderTarget);texture.generateMipmaps&&isTargetPowerOfTwo&&texture.minFilter!==THREE.NearestFilter&&texture.minFilter!==THREE.LinearFilter&&updateRenderTargetMipmap(renderTarget)}state.setDepthTest(!0),state.setDepthWrite(!0),state.setColorWrite(!0)},this.setFaceCulling=function(cullFace,frontFaceDirection){cullFace===THREE.CullFaceNone?state.disable(_gl.CULL_FACE):(frontFaceDirection===THREE.FrontFaceDirectionCW?_gl.frontFace(_gl.CW):_gl.frontFace(_gl.CCW),cullFace===THREE.CullFaceBack?_gl.cullFace(_gl.BACK):cullFace===THREE.CullFaceFront?_gl.cullFace(_gl.FRONT):_gl.cullFace(_gl.FRONT_AND_BACK),state.enable(_gl.CULL_FACE))},this.setTexture=function(texture,slot){var textureProperties=properties.get(texture);if(texture.version>0&&textureProperties.__version!==texture.version){var image=texture.image;return void 0===image?void console.warn("THREE.WebGLRenderer: Texture marked for update but image is undefined",texture):image.complete===!1?void console.warn("THREE.WebGLRenderer: Texture marked for update but image is incomplete",texture):void uploadTexture(textureProperties,texture,slot)}state.activeTexture(_gl.TEXTURE0+slot),state.bindTexture(_gl.TEXTURE_2D,textureProperties.__webglTexture)},this.setRenderTarget=function(renderTarget){var isCube=renderTarget instanceof THREE.WebGLRenderTargetCube;if(renderTarget&&void 0===properties.get(renderTarget).__webglFramebuffer){var renderTargetProperties=properties.get(renderTarget),textureProperties=properties.get(renderTarget.texture);void 0===renderTarget.depthBuffer&&(renderTarget.depthBuffer=!0),void 0===renderTarget.stencilBuffer&&(renderTarget.stencilBuffer=!0),renderTarget.addEventListener("dispose",onRenderTargetDispose),textureProperties.__webglTexture=_gl.createTexture(),_infoMemory.textures++;var isTargetPowerOfTwo=isPowerOfTwo(renderTarget),glFormat=paramThreeToGL(renderTarget.texture.format),glType=paramThreeToGL(renderTarget.texture.type);if(isCube){renderTargetProperties.__webglFramebuffer=[],renderTargetProperties.__webglRenderbuffer=[],state.bindTexture(_gl.TEXTURE_CUBE_MAP,textureProperties.__webglTexture),setTextureParameters(_gl.TEXTURE_CUBE_MAP,renderTarget.texture,isTargetPowerOfTwo);for(var i=0;6>i;i++)renderTargetProperties.__webglFramebuffer[i]=_gl.createFramebuffer(),renderTargetProperties.__webglRenderbuffer[i]=_gl.createRenderbuffer(),state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i,0,glFormat,renderTarget.width,renderTarget.height,0,glFormat,glType,null),setupFrameBuffer(renderTargetProperties.__webglFramebuffer[i],renderTarget,_gl.TEXTURE_CUBE_MAP_POSITIVE_X+i),setupRenderBuffer(renderTargetProperties.__webglRenderbuffer[i],renderTarget);renderTarget.texture.generateMipmaps&&isTargetPowerOfTwo&&_gl.generateMipmap(_gl.TEXTURE_CUBE_MAP)}else renderTargetProperties.__webglFramebuffer=_gl.createFramebuffer(),renderTarget.shareDepthFrom?renderTargetProperties.__webglRenderbuffer=renderTarget.shareDepthFrom.__webglRenderbuffer:renderTargetProperties.__webglRenderbuffer=_gl.createRenderbuffer(),state.bindTexture(_gl.TEXTURE_2D,textureProperties.__webglTexture),setTextureParameters(_gl.TEXTURE_2D,renderTarget.texture,isTargetPowerOfTwo),state.texImage2D(_gl.TEXTURE_2D,0,glFormat,renderTarget.width,renderTarget.height,0,glFormat,glType,null),setupFrameBuffer(renderTargetProperties.__webglFramebuffer,renderTarget,_gl.TEXTURE_2D),renderTarget.shareDepthFrom?renderTarget.depthBuffer&&!renderTarget.stencilBuffer?_gl.framebufferRenderbuffer(_gl.FRAMEBUFFER,_gl.DEPTH_ATTACHMENT,_gl.RENDERBUFFER,renderTargetProperties.__webglRenderbuffer):renderTarget.depthBuffer&&renderTarget.stencilBuffer&&_gl.framebufferRenderbuffer(_gl.FRAMEBUFFER,_gl.DEPTH_STENCIL_ATTACHMENT,_gl.RENDERBUFFER,renderTargetProperties.__webglRenderbuffer):setupRenderBuffer(renderTargetProperties.__webglRenderbuffer,renderTarget),renderTarget.texture.generateMipmaps&&isTargetPowerOfTwo&&_gl.generateMipmap(_gl.TEXTURE_2D);isCube?state.bindTexture(_gl.TEXTURE_CUBE_MAP,null):state.bindTexture(_gl.TEXTURE_2D,null),_gl.bindRenderbuffer(_gl.RENDERBUFFER,null),_gl.bindFramebuffer(_gl.FRAMEBUFFER,null)}var framebuffer,width,height,vx,vy;if(renderTarget){var renderTargetProperties=properties.get(renderTarget);framebuffer=isCube?renderTargetProperties.__webglFramebuffer[renderTarget.activeCubeFace]:renderTargetProperties.__webglFramebuffer,width=renderTarget.width,height=renderTarget.height,vx=0,vy=0}else framebuffer=null,width=_viewportWidth,height=_viewportHeight,vx=_viewportX,vy=_viewportY;if(framebuffer!==_currentFramebuffer&&(_gl.bindFramebuffer(_gl.FRAMEBUFFER,framebuffer),_gl.viewport(vx,vy,width,height),_currentFramebuffer=framebuffer),isCube){var textureProperties=properties.get(renderTarget.texture);_gl.framebufferTexture2D(_gl.FRAMEBUFFER,_gl.COLOR_ATTACHMENT0,_gl.TEXTURE_CUBE_MAP_POSITIVE_X+renderTarget.activeCubeFace,textureProperties.__webglTexture,0)}_currentWidth=width,_currentHeight=height},this.readRenderTargetPixels=function(renderTarget,x,y,width,height,buffer){if(renderTarget instanceof THREE.WebGLRenderTarget==!1)return void console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");var framebuffer=properties.get(renderTarget).__webglFramebuffer;if(framebuffer){var restore=!1;framebuffer!==_currentFramebuffer&&(_gl.bindFramebuffer(_gl.FRAMEBUFFER,framebuffer),restore=!0);try{var texture=renderTarget.texture;if(texture.format!==THREE.RGBAFormat&¶mThreeToGL(texture.format)!==_gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_FORMAT))return void console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.");if(!(texture.type===THREE.UnsignedByteType||paramThreeToGL(texture.type)===_gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_TYPE)||texture.type===THREE.FloatType&&extensions.get("WEBGL_color_buffer_float")||texture.type===THREE.HalfFloatType&&extensions.get("EXT_color_buffer_half_float")))return void console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.");_gl.checkFramebufferStatus(_gl.FRAMEBUFFER)===_gl.FRAMEBUFFER_COMPLETE?_gl.readPixels(x,y,width,height,paramThreeToGL(texture.format),paramThreeToGL(texture.type),buffer):console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.")}finally{restore&&_gl.bindFramebuffer(_gl.FRAMEBUFFER,_currentFramebuffer)}}},this.supportsFloatTextures=function(){return console.warn("THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( 'OES_texture_float' )."),extensions.get("OES_texture_float")},this.supportsHalfFloatTextures=function(){return console.warn("THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( 'OES_texture_half_float' )."),extensions.get("OES_texture_half_float")},this.supportsStandardDerivatives=function(){return console.warn("THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( 'OES_standard_derivatives' )."),extensions.get("OES_standard_derivatives")},this.supportsCompressedTextureS3TC=function(){return console.warn("THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( 'WEBGL_compressed_texture_s3tc' )."),extensions.get("WEBGL_compressed_texture_s3tc")},this.supportsCompressedTexturePVRTC=function(){return console.warn("THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( 'WEBGL_compressed_texture_pvrtc' )."),extensions.get("WEBGL_compressed_texture_pvrtc")},this.supportsBlendMinMax=function(){return console.warn("THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( 'EXT_blend_minmax' )."),extensions.get("EXT_blend_minmax")},this.supportsVertexTextures=function(){return capabilities.vertexTextures},this.supportsInstancedArrays=function(){return console.warn("THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( 'ANGLE_instanced_arrays' )."),extensions.get("ANGLE_instanced_arrays")},this.initMaterial=function(){console.warn("THREE.WebGLRenderer: .initMaterial() has been removed.")},this.addPrePlugin=function(){console.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed.")},this.addPostPlugin=function(){console.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed.")},this.updateShadowMap=function(){console.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed.")},Object.defineProperties(this,{shadowMapEnabled:{get:function(){return shadowMap.enabled},set:function(value){console.warn("THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled."),shadowMap.enabled=value}},shadowMapType:{get:function(){return shadowMap.type},set:function(value){console.warn("THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type."),shadowMap.type=value}},shadowMapCullFace:{get:function(){return shadowMap.cullFace},set:function(value){console.warn("THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace."),shadowMap.cullFace=value}},shadowMapDebug:{get:function(){return shadowMap.debug},set:function(value){console.warn("THREE.WebGLRenderer: .shadowMapDebug is now .shadowMap.debug."),shadowMap.debug=value}}})},THREE.WebGLRenderTarget=function(width,height,options){this.uuid=THREE.Math.generateUUID(),this.width=width,this.height=height,options=options||{},void 0===options.minFilter&&(options.minFilter=THREE.LinearFilter),this.texture=new THREE.Texture(void 0,void 0,options.wrapS,options.wrapT,options.magFilter,options.minFilter,options.format,options.type,options.anisotropy),this.depthBuffer=void 0!==options.depthBuffer?options.depthBuffer:!0,this.stencilBuffer=void 0!==options.stencilBuffer?options.stencilBuffer:!0,this.shareDepthFrom=void 0!==options.shareDepthFrom?options.shareDepthFrom:null},THREE.WebGLRenderTarget.prototype={constructor:THREE.WebGLRenderTarget,get wrapS(){return console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."),this.texture.wrapS},set wrapS(value){console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."),this.texture.wrapS=value},get wrapT(){return console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."),this.texture.wrapT},set wrapT(value){console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."),this.texture.wrapT=value},get magFilter(){return console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."),this.texture.magFilter},set magFilter(value){console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."),this.texture.magFilter=value},get minFilter(){return console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."),this.texture.minFilter},set minFilter(value){console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."),this.texture.minFilter=value},get anisotropy(){return console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."),this.texture.anisotropy},set anisotropy(value){console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."),this.texture.anisotropy=value},get offset(){return console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."),this.texture.offset},set offset(value){console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."),this.texture.offset=value},get repeat(){return console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."),this.texture.repeat},set repeat(value){console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."),this.texture.repeat=value},get format(){return console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."),this.texture.format},set format(value){console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."),this.texture.format=value},get type(){return console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."),this.texture.type},set type(value){console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."),this.texture.type=value},get generateMipmaps(){return console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."),this.texture.generateMipmaps},set generateMipmaps(value){console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."),this.texture.generateMipmaps=value},setSize:function(width,height){this.width===width&&this.height===height||(this.width=width,this.height=height,this.dispose())},clone:function(){return(new this.constructor).copy(this)},copy:function(source){return this.width=source.width,this.height=source.height,this.texture=source.texture.clone(),this.depthBuffer=source.depthBuffer,this.stencilBuffer=source.stencilBuffer,this.shareDepthFrom=source.shareDepthFrom,this},dispose:function(){this.dispatchEvent({type:"dispose"})}},THREE.EventDispatcher.prototype.apply(THREE.WebGLRenderTarget.prototype),THREE.WebGLRenderTargetCube=function(width,height,options){THREE.WebGLRenderTarget.call(this,width,height,options),this.activeCubeFace=0},THREE.WebGLRenderTargetCube.prototype=Object.create(THREE.WebGLRenderTarget.prototype),THREE.WebGLRenderTargetCube.prototype.constructor=THREE.WebGLRenderTargetCube,THREE.WebGLBufferRenderer=function(_gl,extensions,_infoRender){function setMode(value){mode=value}function render(start,count){_gl.drawArrays(mode,start,count),_infoRender.calls++,_infoRender.vertices+=count,mode===_gl.TRIANGLES&&(_infoRender.faces+=count/3)}function renderInstances(geometry){var extension=extensions.get("ANGLE_instanced_arrays");if(null===extension)return void console.error("THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.");var position=geometry.attributes.position;position instanceof THREE.InterleavedBufferAttribute?extension.drawArraysInstancedANGLE(mode,0,position.data.count,geometry.maxInstancedCount):extension.drawArraysInstancedANGLE(mode,0,position.count,geometry.maxInstancedCount)}var mode;this.setMode=setMode,this.render=render,this.renderInstances=renderInstances},THREE.WebGLIndexedBufferRenderer=function(_gl,extensions,_infoRender){function setMode(value){mode=value}function setIndex(index){index.array instanceof Uint32Array&&extensions.get("OES_element_index_uint")?(type=_gl.UNSIGNED_INT,size=4):(type=_gl.UNSIGNED_SHORT,size=2)}function render(start,count){_gl.drawElements(mode,count,type,start*size),_infoRender.calls++,_infoRender.vertices+=count,mode===_gl.TRIANGLES&&(_infoRender.faces+=count/3)}function renderInstances(geometry){var extension=extensions.get("ANGLE_instanced_arrays");if(null===extension)return void console.error("THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.");var index=geometry.index;extension.drawElementsInstancedANGLE(mode,index.array.length,type,0,geometry.maxInstancedCount)}var mode,type,size;this.setMode=setMode,this.setIndex=setIndex,this.render=render,this.renderInstances=renderInstances},THREE.WebGLExtensions=function(gl){var extensions={};this.get=function(name){if(void 0!==extensions[name])return extensions[name];var extension;switch(name){case"EXT_texture_filter_anisotropic":extension=gl.getExtension("EXT_texture_filter_anisotropic")||gl.getExtension("MOZ_EXT_texture_filter_anisotropic")||gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic");break;case"WEBGL_compressed_texture_s3tc":extension=gl.getExtension("WEBGL_compressed_texture_s3tc")||gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc")||gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc");break;case"WEBGL_compressed_texture_pvrtc":extension=gl.getExtension("WEBGL_compressed_texture_pvrtc")||gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc");break;default:extension=gl.getExtension(name)}return null===extension&&console.warn("THREE.WebGLRenderer: "+name+" extension not supported."),extensions[name]=extension,extension}},THREE.WebGLCapabilities=function(gl,extensions,parameters){function getMaxPrecision(precision){if("highp"===precision){if(gl.getShaderPrecisionFormat(gl.VERTEX_SHADER,gl.HIGH_FLOAT).precision>0&&gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER,gl.HIGH_FLOAT).precision>0)return"highp";precision="mediump"}return"mediump"===precision&&gl.getShaderPrecisionFormat(gl.VERTEX_SHADER,gl.MEDIUM_FLOAT).precision>0&&gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER,gl.MEDIUM_FLOAT).precision>0?"mediump":"lowp"}this.getMaxPrecision=getMaxPrecision,this.precision=void 0!==parameters.precision?parameters.precision:"highp",this.logarithmicDepthBuffer=void 0!==parameters.logarithmicDepthBuffer?parameters.logarithmicDepthBuffer:!1,this.maxTextures=gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS),this.maxVertexTextures=gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS),this.maxTextureSize=gl.getParameter(gl.MAX_TEXTURE_SIZE),this.maxCubemapSize=gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE),this.maxAttributes=gl.getParameter(gl.MAX_VERTEX_ATTRIBS),this.maxVertexUniforms=gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS),this.maxVaryings=gl.getParameter(gl.MAX_VARYING_VECTORS),this.maxFragmentUniforms=gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS),this.vertexTextures=this.maxVertexTextures>0,this.floatFragmentTextures=!!extensions.get("OES_texture_float"),this.floatVertexTextures=this.vertexTextures&&this.floatFragmentTextures;var _maxPrecision=getMaxPrecision(this.precision);_maxPrecision!==this.precision&&(console.warn("THREE.WebGLRenderer:",this.precision,"not supported, using",_maxPrecision,"instead."),this.precision=_maxPrecision),this.logarithmicDepthBuffer&&(this.logarithmicDepthBuffer=!!extensions.get("EXT_frag_depth"))},THREE.WebGLGeometries=function(gl,properties,info){function get(object){var geometry=object.geometry;if(void 0!==geometries[geometry.id])return geometries[geometry.id];geometry.addEventListener("dispose",onGeometryDispose);var buffergeometry;return geometry instanceof THREE.BufferGeometry?buffergeometry=geometry:geometry instanceof THREE.Geometry&&(void 0===geometry._bufferGeometry&&(geometry._bufferGeometry=(new THREE.BufferGeometry).setFromObject(object)),buffergeometry=geometry._bufferGeometry),geometries[geometry.id]=buffergeometry,info.memory.geometries++,buffergeometry}function onGeometryDispose(event){var geometry=event.target,buffergeometry=geometries[geometry.id];deleteAttributes(buffergeometry.attributes),geometry.removeEventListener("dispose",onGeometryDispose),delete geometries[geometry.id];var property=properties.get(geometry);property.wireframe&&deleteAttribute(property.wireframe),info.memory.geometries--}function getAttributeBuffer(attribute){return attribute instanceof THREE.InterleavedBufferAttribute?properties.get(attribute.data).__webglBuffer:properties.get(attribute).__webglBuffer}function deleteAttribute(attribute){var buffer=getAttributeBuffer(attribute);void 0!==buffer&&(gl.deleteBuffer(buffer),removeAttributeBuffer(attribute))}function deleteAttributes(attributes){for(var name in attributes)deleteAttribute(attributes[name])}function removeAttributeBuffer(attribute){attribute instanceof THREE.InterleavedBufferAttribute?properties["delete"](attribute.data):properties["delete"](attribute)}var geometries={};this.get=get},THREE.WebGLObjects=function(gl,properties,info){function update(object){var geometry=geometries.get(object);object.geometry instanceof THREE.Geometry&&geometry.updateFromObject(object);var index=geometry.index,attributes=geometry.attributes;null!==index&&updateAttribute(index,gl.ELEMENT_ARRAY_BUFFER);for(var name in attributes)updateAttribute(attributes[name],gl.ARRAY_BUFFER);var morphAttributes=geometry.morphAttributes;for(var name in morphAttributes)for(var array=morphAttributes[name],i=0,l=array.length;l>i;i++)updateAttribute(array[i],gl.ARRAY_BUFFER);return geometry}function updateAttribute(attribute,bufferType){var data=attribute instanceof THREE.InterleavedBufferAttribute?attribute.data:attribute,attributeProperties=properties.get(data);void 0===attributeProperties.__webglBuffer?createBuffer(attributeProperties,data,bufferType):attributeProperties.version!==data.version&&updateBuffer(attributeProperties,data,bufferType)}function createBuffer(attributeProperties,data,bufferType){attributeProperties.__webglBuffer=gl.createBuffer(),gl.bindBuffer(bufferType,attributeProperties.__webglBuffer);var usage=data.dynamic?gl.DYNAMIC_DRAW:gl.STATIC_DRAW;gl.bufferData(bufferType,data.array,usage),attributeProperties.version=data.version}function updateBuffer(attributeProperties,data,bufferType){gl.bindBuffer(bufferType,attributeProperties.__webglBuffer),data.dynamic===!1||-1===data.updateRange.count?gl.bufferSubData(bufferType,0,data.array):0===data.updateRange.count?console.error("THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually."):(gl.bufferSubData(bufferType,data.updateRange.offset*data.array.BYTES_PER_ELEMENT,data.array.subarray(data.updateRange.offset,data.updateRange.offset+data.updateRange.count)),data.updateRange.count=0),attributeProperties.version=data.version}function getAttributeBuffer(attribute){return attribute instanceof THREE.InterleavedBufferAttribute?properties.get(attribute.data).__webglBuffer:properties.get(attribute).__webglBuffer}function getWireframeAttribute(geometry){var property=properties.get(geometry);if(void 0!==property.wireframe)return property.wireframe;var indices=[],index=geometry.index,attributes=geometry.attributes,position=attributes.position;if(null!==index)for(var edges={},array=index.array,i=0,l=array.length;l>i;i+=3){var a=array[i+0],b=array[i+1],c=array[i+2];checkEdge(edges,a,b)&&indices.push(a,b),checkEdge(edges,b,c)&&indices.push(b,c),checkEdge(edges,c,a)&&indices.push(c,a)}else for(var array=attributes.position.array,i=0,l=array.length/3-1;l>i;i+=3){var a=i+0,b=i+1,c=i+2;indices.push(a,b,b,c,c,a)}var TypeArray=position.count>65535?Uint32Array:Uint16Array,attribute=new THREE.BufferAttribute(new TypeArray(indices),1);return updateAttribute(attribute,gl.ELEMENT_ARRAY_BUFFER),property.wireframe=attribute,attribute}function checkEdge(edges,a,b){if(a>b){var tmp=a;a=b,b=tmp}var list=edges[a];return void 0===list?(edges[a]=[b],!0):-1===list.indexOf(b)?(list.push(b),!0):!1}var geometries=new THREE.WebGLGeometries(gl,properties,info);this.getAttributeBuffer=getAttributeBuffer,this.getWireframeAttribute=getWireframeAttribute,this.update=update},THREE.WebGLProgram=function(){function generateDefines(defines){var chunks=[];for(var name in defines){var value=defines[name];value!==!1&&chunks.push("#define "+name+" "+value)}return chunks.join("\n")}function fetchUniformLocations(gl,program,identifiers){for(var uniforms={},n=gl.getProgramParameter(program,gl.ACTIVE_UNIFORMS),i=0;n>i;i++){var info=gl.getActiveUniform(program,i),name=info.name,location=gl.getUniformLocation(program,name),suffixPos=name.lastIndexOf("[0]");-1!==suffixPos&&suffixPos===name.length-3&&(uniforms[name.substr(0,suffixPos)]=location),uniforms[name]=location}return uniforms}function fetchAttributeLocations(gl,program,identifiers){for(var attributes={},n=gl.getProgramParameter(program,gl.ACTIVE_ATTRIBUTES),i=0;n>i;i++){var info=gl.getActiveAttrib(program,i),name=info.name;attributes[name]=gl.getAttribLocation(program,name)}return attributes}function filterEmptyLine(string){return""!==string}var programIdCount=0;return function(renderer,code,material,parameters){var gl=renderer.context,defines=material.defines,vertexShader=material.__webglShader.vertexShader,fragmentShader=material.__webglShader.fragmentShader,shadowMapTypeDefine="SHADOWMAP_TYPE_BASIC";parameters.shadowMapType===THREE.PCFShadowMap?shadowMapTypeDefine="SHADOWMAP_TYPE_PCF":parameters.shadowMapType===THREE.PCFSoftShadowMap&&(shadowMapTypeDefine="SHADOWMAP_TYPE_PCF_SOFT");var envMapTypeDefine="ENVMAP_TYPE_CUBE",envMapModeDefine="ENVMAP_MODE_REFLECTION",envMapBlendingDefine="ENVMAP_BLENDING_MULTIPLY";if(parameters.envMap){switch(material.envMap.mapping){case THREE.CubeReflectionMapping:case THREE.CubeRefractionMapping:envMapTypeDefine="ENVMAP_TYPE_CUBE";break;case THREE.EquirectangularReflectionMapping:case THREE.EquirectangularRefractionMapping:envMapTypeDefine="ENVMAP_TYPE_EQUIREC";break;case THREE.SphericalReflectionMapping:envMapTypeDefine="ENVMAP_TYPE_SPHERE"}switch(material.envMap.mapping){case THREE.CubeRefractionMapping:case THREE.EquirectangularRefractionMapping:envMapModeDefine="ENVMAP_MODE_REFRACTION"}switch(material.combine){case THREE.MultiplyOperation:envMapBlendingDefine="ENVMAP_BLENDING_MULTIPLY";break;case THREE.MixOperation:envMapBlendingDefine="ENVMAP_BLENDING_MIX";break;case THREE.AddOperation:envMapBlendingDefine="ENVMAP_BLENDING_ADD"}}var prefixVertex,prefixFragment,gammaFactorDefine=renderer.gammaFactor>0?renderer.gammaFactor:1,customDefines=generateDefines(defines),program=gl.createProgram();material instanceof THREE.RawShaderMaterial?(prefixVertex="",prefixFragment=""):(prefixVertex=["precision "+parameters.precision+" float;","precision "+parameters.precision+" int;","#define SHADER_NAME "+material.__webglShader.name,customDefines,parameters.supportsVertexTextures?"#define VERTEX_TEXTURES":"",renderer.gammaInput?"#define GAMMA_INPUT":"",renderer.gammaOutput?"#define GAMMA_OUTPUT":"","#define GAMMA_FACTOR "+gammaFactorDefine,"#define MAX_DIR_LIGHTS "+parameters.maxDirLights,"#define MAX_POINT_LIGHTS "+parameters.maxPointLights,"#define MAX_SPOT_LIGHTS "+parameters.maxSpotLights,"#define MAX_HEMI_LIGHTS "+parameters.maxHemiLights,"#define MAX_SHADOWS "+parameters.maxShadows,"#define MAX_BONES "+parameters.maxBones,parameters.map?"#define USE_MAP":"",parameters.envMap?"#define USE_ENVMAP":"",parameters.envMap?"#define "+envMapModeDefine:"",parameters.lightMap?"#define USE_LIGHTMAP":"",parameters.aoMap?"#define USE_AOMAP":"",parameters.emissiveMap?"#define USE_EMISSIVEMAP":"",parameters.bumpMap?"#define USE_BUMPMAP":"",parameters.normalMap?"#define USE_NORMALMAP":"",parameters.displacementMap&¶meters.supportsVertexTextures?"#define USE_DISPLACEMENTMAP":"",parameters.specularMap?"#define USE_SPECULARMAP":"",parameters.alphaMap?"#define USE_ALPHAMAP":"",parameters.vertexColors?"#define USE_COLOR":"",parameters.flatShading?"#define FLAT_SHADED":"",parameters.skinning?"#define USE_SKINNING":"",parameters.useVertexTexture?"#define BONE_TEXTURE":"",parameters.morphTargets?"#define USE_MORPHTARGETS":"",parameters.morphNormals&¶meters.flatShading===!1?"#define USE_MORPHNORMALS":"",parameters.doubleSided?"#define DOUBLE_SIDED":"",parameters.flipSided?"#define FLIP_SIDED":"",parameters.shadowMapEnabled?"#define USE_SHADOWMAP":"",parameters.shadowMapEnabled?"#define "+shadowMapTypeDefine:"",parameters.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",parameters.pointLightShadows>0?"#define POINT_LIGHT_SHADOWS":"",parameters.sizeAttenuation?"#define USE_SIZEATTENUATION":"",parameters.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",parameters.logarithmicDepthBuffer&&renderer.extensions.get("EXT_frag_depth")?"#define USE_LOGDEPTHBUF_EXT":"","uniform mat4 modelMatrix;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform mat4 viewMatrix;","uniform mat3 normalMatrix;","uniform vec3 cameraPosition;","attribute vec3 position;","attribute vec3 normal;","attribute vec2 uv;","#ifdef USE_COLOR"," attribute vec3 color;","#endif","#ifdef USE_MORPHTARGETS"," attribute vec3 morphTarget0;"," attribute vec3 morphTarget1;"," attribute vec3 morphTarget2;"," attribute vec3 morphTarget3;"," #ifdef USE_MORPHNORMALS"," attribute vec3 morphNormal0;"," attribute vec3 morphNormal1;"," attribute vec3 morphNormal2;"," attribute vec3 morphNormal3;"," #else"," attribute vec3 morphTarget4;"," attribute vec3 morphTarget5;"," attribute vec3 morphTarget6;"," attribute vec3 morphTarget7;"," #endif","#endif","#ifdef USE_SKINNING"," attribute vec4 skinIndex;"," attribute vec4 skinWeight;","#endif","\n"].filter(filterEmptyLine).join("\n"), +prefixFragment=[parameters.bumpMap||parameters.normalMap||parameters.flatShading||material.derivatives?"#extension GL_OES_standard_derivatives : enable":"",parameters.logarithmicDepthBuffer&&renderer.extensions.get("EXT_frag_depth")?"#extension GL_EXT_frag_depth : enable":"","precision "+parameters.precision+" float;","precision "+parameters.precision+" int;","#define SHADER_NAME "+material.__webglShader.name,customDefines,"#define MAX_DIR_LIGHTS "+parameters.maxDirLights,"#define MAX_POINT_LIGHTS "+parameters.maxPointLights,"#define MAX_SPOT_LIGHTS "+parameters.maxSpotLights,"#define MAX_HEMI_LIGHTS "+parameters.maxHemiLights,"#define MAX_SHADOWS "+parameters.maxShadows,parameters.alphaTest?"#define ALPHATEST "+parameters.alphaTest:"",renderer.gammaInput?"#define GAMMA_INPUT":"",renderer.gammaOutput?"#define GAMMA_OUTPUT":"","#define GAMMA_FACTOR "+gammaFactorDefine,parameters.useFog&¶meters.fog?"#define USE_FOG":"",parameters.useFog&¶meters.fogExp?"#define FOG_EXP2":"",parameters.map?"#define USE_MAP":"",parameters.envMap?"#define USE_ENVMAP":"",parameters.envMap?"#define "+envMapTypeDefine:"",parameters.envMap?"#define "+envMapModeDefine:"",parameters.envMap?"#define "+envMapBlendingDefine:"",parameters.lightMap?"#define USE_LIGHTMAP":"",parameters.aoMap?"#define USE_AOMAP":"",parameters.emissiveMap?"#define USE_EMISSIVEMAP":"",parameters.bumpMap?"#define USE_BUMPMAP":"",parameters.normalMap?"#define USE_NORMALMAP":"",parameters.specularMap?"#define USE_SPECULARMAP":"",parameters.alphaMap?"#define USE_ALPHAMAP":"",parameters.vertexColors?"#define USE_COLOR":"",parameters.flatShading?"#define FLAT_SHADED":"",parameters.metal?"#define METAL":"",parameters.doubleSided?"#define DOUBLE_SIDED":"",parameters.flipSided?"#define FLIP_SIDED":"",parameters.shadowMapEnabled?"#define USE_SHADOWMAP":"",parameters.shadowMapEnabled?"#define "+shadowMapTypeDefine:"",parameters.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",parameters.pointLightShadows>0?"#define POINT_LIGHT_SHADOWS":"",parameters.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",parameters.logarithmicDepthBuffer&&renderer.extensions.get("EXT_frag_depth")?"#define USE_LOGDEPTHBUF_EXT":"","uniform mat4 viewMatrix;","uniform vec3 cameraPosition;","\n"].filter(filterEmptyLine).join("\n"));var vertexGlsl=prefixVertex+vertexShader,fragmentGlsl=prefixFragment+fragmentShader,glVertexShader=THREE.WebGLShader(gl,gl.VERTEX_SHADER,vertexGlsl),glFragmentShader=THREE.WebGLShader(gl,gl.FRAGMENT_SHADER,fragmentGlsl);gl.attachShader(program,glVertexShader),gl.attachShader(program,glFragmentShader),void 0!==material.index0AttributeName?gl.bindAttribLocation(program,0,material.index0AttributeName):parameters.morphTargets===!0&&gl.bindAttribLocation(program,0,"position"),gl.linkProgram(program);var programLog=gl.getProgramInfoLog(program),vertexLog=gl.getShaderInfoLog(glVertexShader),fragmentLog=gl.getShaderInfoLog(glFragmentShader),runnable=!0,haveDiagnostics=!0;gl.getProgramParameter(program,gl.LINK_STATUS)===!1?(runnable=!1,console.error("THREE.WebGLProgram: shader error: ",gl.getError(),"gl.VALIDATE_STATUS",gl.getProgramParameter(program,gl.VALIDATE_STATUS),"gl.getProgramInfoLog",programLog,vertexLog,fragmentLog)):""!==programLog?console.warn("THREE.WebGLProgram: gl.getProgramInfoLog()",programLog):""!==vertexLog&&""!==fragmentLog||(haveDiagnostics=!1),haveDiagnostics&&(this.diagnostics={runnable:runnable,material:material,programLog:programLog,vertexShader:{log:vertexLog,prefix:prefixVertex},fragmentShader:{log:fragmentLog,prefix:prefixFragment}}),gl.deleteShader(glVertexShader),gl.deleteShader(glFragmentShader);var cachedUniforms;this.getUniforms=function(){return void 0===cachedUniforms&&(cachedUniforms=fetchUniformLocations(gl,program)),cachedUniforms};var cachedAttributes;return this.getAttributes=function(){return void 0===cachedAttributes&&(cachedAttributes=fetchAttributeLocations(gl,program)),cachedAttributes},this.destroy=function(){gl.deleteProgram(program),this.program=void 0},Object.defineProperties(this,{uniforms:{get:function(){return console.warn("THREE.WebGLProgram: .uniforms is now .getUniforms()."),this.getUniforms()}},attributes:{get:function(){return console.warn("THREE.WebGLProgram: .attributes is now .getAttributes()."),this.getAttributes()}}}),this.id=programIdCount++,this.code=code,this.usedTimes=1,this.program=program,this.vertexShader=glVertexShader,this.fragmentShader=glFragmentShader,this}}(),THREE.WebGLPrograms=function(renderer,capabilities){function allocateBones(object){if(capabilities.floatVertexTextures&&object&&object.skeleton&&object.skeleton.useVertexTexture)return 1024;var nVertexUniforms=capabilities.maxVertexUniforms,nVertexMatrices=Math.floor((nVertexUniforms-20)/4),maxBones=nVertexMatrices;return void 0!==object&&object instanceof THREE.SkinnedMesh&&(maxBones=Math.min(object.skeleton.bones.length,maxBones),maxBonesl;l++){var light=lights[l];light.visible!==!1&&(light instanceof THREE.DirectionalLight&&dirLights++,light instanceof THREE.PointLight&&pointLights++,light instanceof THREE.SpotLight&&spotLights++,light instanceof THREE.HemisphereLight&&hemiLights++)}return{directional:dirLights,point:pointLights,spot:spotLights,hemi:hemiLights}}function allocateShadows(lights){for(var maxShadows=0,pointLightShadows=0,l=0,ll=lights.length;ll>l;l++){var light=lights[l];light.castShadow&&((light instanceof THREE.SpotLight||light instanceof THREE.DirectionalLight)&&maxShadows++,light instanceof THREE.PointLight&&(maxShadows++,pointLightShadows++))}return{maxShadows:maxShadows,pointLightShadows:pointLightShadows}}var programs=[],shaderIDs={MeshDepthMaterial:"depth",MeshNormalMaterial:"normal",MeshBasicMaterial:"basic",MeshLambertMaterial:"lambert",MeshPhongMaterial:"phong",LineBasicMaterial:"basic",LineDashedMaterial:"dashed",PointsMaterial:"points"},parameterNames=["precision","supportsVertexTextures","map","envMap","envMapMode","lightMap","aoMap","emissiveMap","bumpMap","normalMap","displacementMap","specularMap","alphaMap","combine","vertexColors","fog","useFog","fogExp","flatShading","sizeAttenuation","logarithmicDepthBuffer","skinning","maxBones","useVertexTexture","morphTargets","morphNormals","maxMorphTargets","maxMorphNormals","maxDirLights","maxPointLights","maxSpotLights","maxHemiLights","maxShadows","shadowMapEnabled","pointLightShadows","shadowMapType","shadowMapDebug","alphaTest","metal","doubleSided","flipSided"];this.getParameters=function(material,lights,fog,object){var shaderID=shaderIDs[material.type],maxLightCount=allocateLights(lights),allocatedShadows=allocateShadows(lights),maxBones=allocateBones(object),precision=renderer.getPrecision();null!==material.precision&&(precision=capabilities.getMaxPrecision(material.precision),precision!==material.precision&&console.warn("THREE.WebGLRenderer.initMaterial:",material.precision,"not supported, using",precision,"instead."));var parameters={shaderID:shaderID,precision:precision,supportsVertexTextures:capabilities.vertexTextures,map:!!material.map,envMap:!!material.envMap,envMapMode:material.envMap&&material.envMap.mapping,lightMap:!!material.lightMap,aoMap:!!material.aoMap,emissiveMap:!!material.emissiveMap,bumpMap:!!material.bumpMap,normalMap:!!material.normalMap,displacementMap:!!material.displacementMap,specularMap:!!material.specularMap,alphaMap:!!material.alphaMap,combine:material.combine,vertexColors:material.vertexColors,fog:fog,useFog:material.fog,fogExp:fog instanceof THREE.FogExp2,flatShading:material.shading===THREE.FlatShading,sizeAttenuation:material.sizeAttenuation,logarithmicDepthBuffer:capabilities.logarithmicDepthBuffer,skinning:material.skinning,maxBones:maxBones,useVertexTexture:capabilities.floatVertexTextures&&object&&object.skeleton&&object.skeleton.useVertexTexture,morphTargets:material.morphTargets,morphNormals:material.morphNormals,maxMorphTargets:renderer.maxMorphTargets,maxMorphNormals:renderer.maxMorphNormals,maxDirLights:maxLightCount.directional,maxPointLights:maxLightCount.point,maxSpotLights:maxLightCount.spot,maxHemiLights:maxLightCount.hemi,maxShadows:allocatedShadows.maxShadows,pointLightShadows:allocatedShadows.pointLightShadows,shadowMapEnabled:renderer.shadowMap.enabled&&object.receiveShadow&&allocatedShadows.maxShadows>0,shadowMapType:renderer.shadowMap.type,shadowMapDebug:renderer.shadowMap.debug,alphaTest:material.alphaTest,metal:material.metal,doubleSided:material.side===THREE.DoubleSide,flipSided:material.side===THREE.BackSide};return parameters},this.getProgramCode=function(material,parameters){var chunks=[];if(parameters.shaderID?chunks.push(parameters.shaderID):(chunks.push(material.fragmentShader),chunks.push(material.vertexShader)),void 0!==material.defines)for(var name in material.defines)chunks.push(name),chunks.push(material.defines[name]);for(var i=0;ip;p++){var programInfo=programs[p];if(programInfo.code===code){program=programInfo,++program.usedTimes;break}}return void 0===program&&(program=new THREE.WebGLProgram(renderer,code,material,parameters),programs.push(program)),program},this.releaseProgram=function(program){if(0===--program.usedTimes){var i=programs.indexOf(program);programs[i]=programs[programs.length-1],programs.pop(),program.destroy()}},this.programs=programs},THREE.WebGLProperties=function(){var properties={};this.get=function(object){var uuid=object.uuid,map=properties[uuid];return void 0===map&&(map={},properties[uuid]=map),map},this["delete"]=function(object){delete properties[object.uuid]},this.clear=function(){properties={}}},THREE.WebGLShader=function(){function addLineNumbers(string){for(var lines=string.split("\n"),i=0;i0&&material.morphTargets,useSkinning=object instanceof THREE.SkinnedMesh&&material.skinning,variantIndex=0;useMorphing&&(variantIndex|=_MorphingFlag),useSkinning&&(variantIndex|=_SkinningFlag),newMaterial=materialVariants[variantIndex]}return newMaterial.visible=material.visible,newMaterial.wireframe=material.wireframe,newMaterial.wireframeLinewidth=material.wireframeLinewidth,isPointLight&&void 0!==newMaterial.uniforms.lightPos&&newMaterial.uniforms.lightPos.value.copy(lightPositionWorld),newMaterial}function projectObject(object,camera){if(object.visible!==!1){if((object instanceof THREE.Mesh||object instanceof THREE.Line||object instanceof THREE.Points)&&object.castShadow&&(object.frustumCulled===!1||_frustum.intersectsObject(object)===!0)){var material=object.material;material.visible===!0&&(object.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse,object.matrixWorld),_renderList.push(object))}for(var children=object.children,i=0,l=children.length;l>i;i++)projectObject(children[i],camera)}}for(var _gl=_renderer.context,_state=_renderer.state,_frustum=new THREE.Frustum,_projScreenMatrix=new THREE.Matrix4,_lookTarget=(new THREE.Vector3,new THREE.Vector3,new THREE.Vector3),_lightPositionWorld=new THREE.Vector3,_renderList=[],_MorphingFlag=1,_SkinningFlag=2,_NumberOfMaterialVariants=(_MorphingFlag|_SkinningFlag)+1,_depthMaterials=new Array(_NumberOfMaterialVariants),_distanceMaterials=new Array(_NumberOfMaterialVariants),cubeDirections=[new THREE.Vector3(1,0,0),new THREE.Vector3(-1,0,0),new THREE.Vector3(0,0,1),new THREE.Vector3(0,0,-1),new THREE.Vector3(0,1,0),new THREE.Vector3(0,-1,0)],cubeUps=[new THREE.Vector3(0,1,0),new THREE.Vector3(0,1,0),new THREE.Vector3(0,1,0),new THREE.Vector3(0,1,0),new THREE.Vector3(0,0,1),new THREE.Vector3(0,0,-1)],cube2DViewPorts=[new THREE.Vector4,new THREE.Vector4,new THREE.Vector4,new THREE.Vector4,new THREE.Vector4,new THREE.Vector4],_vector4=new THREE.Vector4,depthShader=THREE.ShaderLib.depthRGBA,depthUniforms=THREE.UniformsUtils.clone(depthShader.uniforms),distanceShader=THREE.ShaderLib.distanceRGBA,distanceUniforms=THREE.UniformsUtils.clone(distanceShader.uniforms),i=0;i!==_NumberOfMaterialVariants;++i){var useMorphing=0!==(i&_MorphingFlag),useSkinning=0!==(i&_SkinningFlag),depthMaterial=new THREE.ShaderMaterial({uniforms:depthUniforms,vertexShader:depthShader.vertexShader,fragmentShader:depthShader.fragmentShader,morphTargets:useMorphing,skinning:useSkinning});depthMaterial._shadowPass=!0,_depthMaterials[i]=depthMaterial;var distanceMaterial=new THREE.ShaderMaterial({uniforms:distanceUniforms,vertexShader:distanceShader.vertexShader,fragmentShader:distanceShader.fragmentShader,morphTargets:useMorphing,skinning:useSkinning});distanceMaterial._shadowPass=!0,_distanceMaterials[i]=distanceMaterial}var scope=this;this.enabled=!1,this.autoUpdate=!0,this.needsUpdate=!1,this.type=THREE.PCFShadowMap,this.cullFace=THREE.CullFaceFront,this.render=function(scene){var faceCount,isPointLight;if(scope.enabled!==!1&&(scope.autoUpdate!==!1||scope.needsUpdate!==!1)){_gl.clearColor(1,1,1,1),_state.disable(_gl.BLEND),_state.enable(_gl.CULL_FACE),_gl.frontFace(_gl.CCW),_gl.cullFace(scope.cullFace===THREE.CullFaceFront?_gl.FRONT:_gl.BACK),_state.setDepthTest(!0),_renderer.getViewport(_vector4);for(var i=0,il=_lights.length;il>i;i++){var light=_lights[i];if(light.castShadow===!0){var shadow=light.shadow,shadowCamera=shadow.camera,shadowMapSize=shadow.mapSize;if(light instanceof THREE.PointLight){faceCount=6,isPointLight=!0;var vpWidth=shadowMapSize.x/4,vpHeight=shadowMapSize.y/2;cube2DViewPorts[0].set(2*vpWidth,vpHeight,vpWidth,vpHeight),cube2DViewPorts[1].set(0,vpHeight,vpWidth,vpHeight),cube2DViewPorts[2].set(3*vpWidth,vpHeight,vpWidth,vpHeight),cube2DViewPorts[3].set(vpWidth,vpHeight,vpWidth,vpHeight),cube2DViewPorts[4].set(3*vpWidth,0,vpWidth,vpHeight),cube2DViewPorts[5].set(vpWidth,0,vpWidth,vpHeight)}else faceCount=1,isPointLight=!1;if(null===shadow.map){var shadowFilter=THREE.LinearFilter;scope.type===THREE.PCFSoftShadowMap&&(shadowFilter=THREE.NearestFilter);var pars={minFilter:shadowFilter,magFilter:shadowFilter,format:THREE.RGBAFormat};shadow.map=new THREE.WebGLRenderTarget(shadowMapSize.x,shadowMapSize.y,pars),shadow.matrix=new THREE.Matrix4,light instanceof THREE.SpotLight&&(shadowCamera.aspect=shadowMapSize.x/shadowMapSize.y),shadowCamera.updateProjectionMatrix()}var shadowMap=shadow.map,shadowMatrix=shadow.matrix;_lightPositionWorld.setFromMatrixPosition(light.matrixWorld),shadowCamera.position.copy(_lightPositionWorld),_renderer.setRenderTarget(shadowMap),_renderer.clear();for(var face=0;faceCount>face;face++){if(isPointLight){_lookTarget.copy(shadowCamera.position),_lookTarget.add(cubeDirections[face]),shadowCamera.up.copy(cubeUps[face]),shadowCamera.lookAt(_lookTarget);var vpDimensions=cube2DViewPorts[face];_renderer.setViewport(vpDimensions.x,vpDimensions.y,vpDimensions.z,vpDimensions.w)}else _lookTarget.setFromMatrixPosition(light.target.matrixWorld),shadowCamera.lookAt(_lookTarget);shadowCamera.updateMatrixWorld(),shadowCamera.matrixWorldInverse.getInverse(shadowCamera.matrixWorld),shadowMatrix.set(.5,0,0,.5,0,.5,0,.5,0,0,.5,.5,0,0,0,1),shadowMatrix.multiply(shadowCamera.projectionMatrix),shadowMatrix.multiply(shadowCamera.matrixWorldInverse),_projScreenMatrix.multiplyMatrices(shadowCamera.projectionMatrix,shadowCamera.matrixWorldInverse),_frustum.setFromMatrix(_projScreenMatrix),_renderList.length=0,projectObject(scene,shadowCamera);for(var j=0,jl=_renderList.length;jl>j;j++){var object=_renderList[j],geometry=_objects.update(object),material=object.material;if(material instanceof THREE.MeshFaceMaterial)for(var groups=geometry.groups,materials=material.materials,k=0,kl=groups.length;kl>k;k++){var group=groups[k],groupMaterial=materials[group.materialIndex];if(groupMaterial.visible===!0){var depthMaterial=getDepthMaterial(object,groupMaterial,isPointLight,_lightPositionWorld);_renderer.renderBufferDirect(shadowCamera,_lights,null,geometry,depthMaterial,object,group)}}else{var depthMaterial=getDepthMaterial(object,material,isPointLight,_lightPositionWorld);_renderer.renderBufferDirect(shadowCamera,_lights,null,geometry,depthMaterial,object,null)}}}_renderer.resetGLState()}}_renderer.setViewport(_vector4.x,_vector4.y,_vector4.z,_vector4.w);var clearColor=_renderer.getClearColor(),clearAlpha=_renderer.getClearAlpha();_renderer.setClearColor(clearColor,clearAlpha),_state.enable(_gl.BLEND),scope.cullFace===THREE.CullFaceFront&&_gl.cullFace(_gl.BACK),_renderer.resetGLState(),scope.needsUpdate=!1}}},THREE.WebGLState=function(gl,extensions,paramThreeToGL){var _this=this,newAttributes=new Uint8Array(16),enabledAttributes=new Uint8Array(16),attributeDivisors=new Uint8Array(16),capabilities={},compressedTextureFormats=null,currentBlending=null,currentBlendEquation=null,currentBlendSrc=null,currentBlendDst=null,currentBlendEquationAlpha=null,currentBlendSrcAlpha=null,currentBlendDstAlpha=null,currentDepthFunc=null,currentDepthWrite=null,currentColorWrite=null,currentFlipSided=null,currentLineWidth=null,currentPolygonOffsetFactor=null,currentPolygonOffsetUnits=null,maxTextures=gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS),currentTextureSlot=void 0,currentBoundTextures={};this.init=function(){gl.clearColor(0,0,0,1),gl.clearDepth(1),gl.clearStencil(0),this.enable(gl.DEPTH_TEST),gl.depthFunc(gl.LEQUAL),gl.frontFace(gl.CCW),gl.cullFace(gl.BACK),this.enable(gl.CULL_FACE),this.enable(gl.BLEND),gl.blendEquation(gl.FUNC_ADD),gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA)},this.initAttributes=function(){for(var i=0,l=newAttributes.length;l>i;i++)newAttributes[i]=0},this.enableAttribute=function(attribute){if(newAttributes[attribute]=1,0===enabledAttributes[attribute]&&(gl.enableVertexAttribArray(attribute),enabledAttributes[attribute]=1),0!==attributeDivisors[attribute]){var extension=extensions.get("ANGLE_instanced_arrays");extension.vertexAttribDivisorANGLE(attribute,0),attributeDivisors[attribute]=0}},this.enableAttributeAndDivisor=function(attribute,meshPerAttribute,extension){newAttributes[attribute]=1,0===enabledAttributes[attribute]&&(gl.enableVertexAttribArray(attribute),enabledAttributes[attribute]=1),attributeDivisors[attribute]!==meshPerAttribute&&(extension.vertexAttribDivisorANGLE(attribute,meshPerAttribute),attributeDivisors[attribute]=meshPerAttribute)},this.disableUnusedAttributes=function(){for(var i=0,l=enabledAttributes.length;l>i;i++)enabledAttributes[i]!==newAttributes[i]&&(gl.disableVertexAttribArray(i),enabledAttributes[i]=0)},this.enable=function(id){capabilities[id]!==!0&&(gl.enable(id),capabilities[id]=!0)},this.disable=function(id){capabilities[id]!==!1&&(gl.disable(id),capabilities[id]=!1)},this.getCompressedTextureFormats=function(){if(null===compressedTextureFormats&&(compressedTextureFormats=[],extensions.get("WEBGL_compressed_texture_pvrtc")||extensions.get("WEBGL_compressed_texture_s3tc")))for(var formats=gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS),i=0;i0;var shader;shader=hasVertexTexture?{vertexShader:["uniform lowp int renderType;","uniform vec3 screenPosition;","uniform vec2 scale;","uniform float rotation;","uniform sampler2D occlusionMap;","attribute vec2 position;","attribute vec2 uv;","varying vec2 vUV;","varying float vVisibility;","void main() {","vUV = uv;","vec2 pos = position;","if ( renderType == 2 ) {","vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );","visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );","visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );","visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );","visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );","visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );","visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );","visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );","visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );","vVisibility = visibility.r / 9.0;","vVisibility *= 1.0 - visibility.g / 9.0;","vVisibility *= visibility.b / 9.0;","vVisibility *= 1.0 - visibility.a / 9.0;","pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;","pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;","}","gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );","}"].join("\n"),fragmentShader:["uniform lowp int renderType;","uniform sampler2D map;","uniform float opacity;","uniform vec3 color;","varying vec2 vUV;","varying float vVisibility;","void main() {","if ( renderType == 0 ) {","gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );","} else if ( renderType == 1 ) {","gl_FragColor = texture2D( map, vUV );","} else {","vec4 texture = texture2D( map, vUV );","texture.a *= opacity * vVisibility;","gl_FragColor = texture;","gl_FragColor.rgb *= color;","}","}"].join("\n")}:{vertexShader:["uniform lowp int renderType;","uniform vec3 screenPosition;","uniform vec2 scale;","uniform float rotation;","attribute vec2 position;","attribute vec2 uv;","varying vec2 vUV;","void main() {","vUV = uv;","vec2 pos = position;","if ( renderType == 2 ) {","pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;","pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;","}","gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );","}"].join("\n"),fragmentShader:["precision mediump float;","uniform lowp int renderType;","uniform sampler2D map;","uniform sampler2D occlusionMap;","uniform float opacity;","uniform vec3 color;","varying vec2 vUV;","void main() {","if ( renderType == 0 ) {","gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );","} else if ( renderType == 1 ) {","gl_FragColor = texture2D( map, vUV );","} else {","float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;","visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;","visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;","visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;","visibility = ( 1.0 - visibility / 4.0 );","vec4 texture = texture2D( map, vUV );","texture.a *= opacity * visibility;","gl_FragColor = texture;","gl_FragColor.rgb *= color;","}","}"].join("\n")},program=createProgram(shader),attributes={vertex:gl.getAttribLocation(program,"position"),uv:gl.getAttribLocation(program,"uv")},uniforms={renderType:gl.getUniformLocation(program,"renderType"),map:gl.getUniformLocation(program,"map"),occlusionMap:gl.getUniformLocation(program,"occlusionMap"),opacity:gl.getUniformLocation(program,"opacity"),color:gl.getUniformLocation(program,"color"),scale:gl.getUniformLocation(program,"scale"),rotation:gl.getUniformLocation(program,"rotation"),screenPosition:gl.getUniformLocation(program,"screenPosition")}}function createProgram(shader){var program=gl.createProgram(),fragmentShader=gl.createShader(gl.FRAGMENT_SHADER),vertexShader=gl.createShader(gl.VERTEX_SHADER),prefix="precision "+renderer.getPrecision()+" float;\n";return gl.shaderSource(fragmentShader,prefix+shader.fragmentShader),gl.shaderSource(vertexShader,prefix+shader.vertexShader),gl.compileShader(fragmentShader),gl.compileShader(vertexShader),gl.attachShader(program,fragmentShader),gl.attachShader(program,vertexShader),gl.linkProgram(program),program}var vertexBuffer,elementBuffer,program,attributes,uniforms,hasVertexTexture,tempTexture,occlusionTexture,gl=renderer.context,state=renderer.state;this.render=function(scene,camera,viewportWidth,viewportHeight){if(0!==flares.length){var tempPosition=new THREE.Vector3,invAspect=viewportHeight/viewportWidth,halfViewportWidth=.5*viewportWidth,halfViewportHeight=.5*viewportHeight,size=16/viewportHeight,scale=new THREE.Vector2(size*invAspect,size),screenPosition=new THREE.Vector3(1,1,0),screenPositionPixels=new THREE.Vector2(1,1);void 0===program&&init(),gl.useProgram(program),state.initAttributes(),state.enableAttribute(attributes.vertex),state.enableAttribute(attributes.uv),state.disableUnusedAttributes(),gl.uniform1i(uniforms.occlusionMap,0),gl.uniform1i(uniforms.map,1),gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer),gl.vertexAttribPointer(attributes.vertex,2,gl.FLOAT,!1,16,0),gl.vertexAttribPointer(attributes.uv,2,gl.FLOAT,!1,16,8),gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,elementBuffer),state.disable(gl.CULL_FACE),gl.depthMask(!1);for(var i=0,l=flares.length;l>i;i++){size=16/viewportHeight,scale.set(size*invAspect,size);var flare=flares[i];if(tempPosition.set(flare.matrixWorld.elements[12],flare.matrixWorld.elements[13],flare.matrixWorld.elements[14]),tempPosition.applyMatrix4(camera.matrixWorldInverse),tempPosition.applyProjection(camera.projectionMatrix),screenPosition.copy(tempPosition),screenPositionPixels.x=screenPosition.x*halfViewportWidth+halfViewportWidth,screenPositionPixels.y=screenPosition.y*halfViewportHeight+halfViewportHeight, +hasVertexTexture||screenPositionPixels.x>0&&screenPositionPixels.x0&&screenPositionPixels.yj;j++){var sprite=flare.lensFlares[j];sprite.opacity>.001&&sprite.scale>.001&&(screenPosition.x=sprite.x,screenPosition.y=sprite.y,screenPosition.z=sprite.z,size=sprite.size*sprite.scale/viewportHeight,scale.x=size*invAspect,scale.y=size,gl.uniform3f(uniforms.screenPosition,screenPosition.x,screenPosition.y,screenPosition.z),gl.uniform2f(uniforms.scale,scale.x,scale.y),gl.uniform1f(uniforms.rotation,sprite.rotation),gl.uniform1f(uniforms.opacity,sprite.opacity),gl.uniform3f(uniforms.color,sprite.color.r,sprite.color.g,sprite.color.b),state.setBlending(sprite.blending,sprite.blendEquation,sprite.blendSrc,sprite.blendDst),renderer.setTexture(sprite.texture,1),gl.drawElements(gl.TRIANGLES,6,gl.UNSIGNED_SHORT,0))}}}state.enable(gl.CULL_FACE),state.enable(gl.DEPTH_TEST),gl.depthMask(!0),renderer.resetGLState()}}},THREE.SpritePlugin=function(renderer,sprites){function init(){var vertices=new Float32Array([-.5,-.5,0,0,.5,-.5,1,0,.5,.5,1,1,-.5,.5,0,1]),faces=new Uint16Array([0,1,2,0,2,3]);vertexBuffer=gl.createBuffer(),elementBuffer=gl.createBuffer(),gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer),gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW),gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,elementBuffer),gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,faces,gl.STATIC_DRAW),program=createProgram(),attributes={position:gl.getAttribLocation(program,"position"),uv:gl.getAttribLocation(program,"uv")},uniforms={uvOffset:gl.getUniformLocation(program,"uvOffset"),uvScale:gl.getUniformLocation(program,"uvScale"),rotation:gl.getUniformLocation(program,"rotation"),scale:gl.getUniformLocation(program,"scale"),color:gl.getUniformLocation(program,"color"),map:gl.getUniformLocation(program,"map"),opacity:gl.getUniformLocation(program,"opacity"),modelViewMatrix:gl.getUniformLocation(program,"modelViewMatrix"),projectionMatrix:gl.getUniformLocation(program,"projectionMatrix"),fogType:gl.getUniformLocation(program,"fogType"),fogDensity:gl.getUniformLocation(program,"fogDensity"),fogNear:gl.getUniformLocation(program,"fogNear"),fogFar:gl.getUniformLocation(program,"fogFar"),fogColor:gl.getUniformLocation(program,"fogColor"),alphaTest:gl.getUniformLocation(program,"alphaTest")};var canvas=document.createElement("canvas");canvas.width=8,canvas.height=8;var context=canvas.getContext("2d");context.fillStyle="white",context.fillRect(0,0,8,8),texture=new THREE.Texture(canvas),texture.needsUpdate=!0}function createProgram(){var program=gl.createProgram(),vertexShader=gl.createShader(gl.VERTEX_SHADER),fragmentShader=gl.createShader(gl.FRAGMENT_SHADER);return gl.shaderSource(vertexShader,["precision "+renderer.getPrecision()+" float;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform float rotation;","uniform vec2 scale;","uniform vec2 uvOffset;","uniform vec2 uvScale;","attribute vec2 position;","attribute vec2 uv;","varying vec2 vUV;","void main() {","vUV = uvOffset + uv * uvScale;","vec2 alignedPosition = position * scale;","vec2 rotatedPosition;","rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;","rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;","vec4 finalPosition;","finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );","finalPosition.xy += rotatedPosition;","finalPosition = projectionMatrix * finalPosition;","gl_Position = finalPosition;","}"].join("\n")),gl.shaderSource(fragmentShader,["precision "+renderer.getPrecision()+" float;","uniform vec3 color;","uniform sampler2D map;","uniform float opacity;","uniform int fogType;","uniform vec3 fogColor;","uniform float fogDensity;","uniform float fogNear;","uniform float fogFar;","uniform float alphaTest;","varying vec2 vUV;","void main() {","vec4 texture = texture2D( map, vUV );","if ( texture.a < alphaTest ) discard;","gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );","if ( fogType > 0 ) {","float depth = gl_FragCoord.z / gl_FragCoord.w;","float fogFactor = 0.0;","if ( fogType == 1 ) {","fogFactor = smoothstep( fogNear, fogFar, depth );","} else {","const float LOG2 = 1.442695;","fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );","fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );","}","gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );","}","}"].join("\n")),gl.compileShader(vertexShader),gl.compileShader(fragmentShader),gl.attachShader(program,vertexShader),gl.attachShader(program,fragmentShader),gl.linkProgram(program),program}function painterSortStable(a,b){return a.z!==b.z?b.z-a.z:b.id-a.id}var vertexBuffer,elementBuffer,program,attributes,uniforms,texture,gl=renderer.context,state=renderer.state,spritePosition=new THREE.Vector3,spriteRotation=new THREE.Quaternion,spriteScale=new THREE.Vector3;this.render=function(scene,camera){if(0!==sprites.length){void 0===program&&init(),gl.useProgram(program),state.initAttributes(),state.enableAttribute(attributes.position),state.enableAttribute(attributes.uv),state.disableUnusedAttributes(),state.disable(gl.CULL_FACE),state.enable(gl.BLEND),gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer),gl.vertexAttribPointer(attributes.position,2,gl.FLOAT,!1,16,0),gl.vertexAttribPointer(attributes.uv,2,gl.FLOAT,!1,16,8),gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,elementBuffer),gl.uniformMatrix4fv(uniforms.projectionMatrix,!1,camera.projectionMatrix.elements),state.activeTexture(gl.TEXTURE0),gl.uniform1i(uniforms.map,0);var oldFogType=0,sceneFogType=0,fog=scene.fog;fog?(gl.uniform3f(uniforms.fogColor,fog.color.r,fog.color.g,fog.color.b),fog instanceof THREE.Fog?(gl.uniform1f(uniforms.fogNear,fog.near),gl.uniform1f(uniforms.fogFar,fog.far),gl.uniform1i(uniforms.fogType,1),oldFogType=1,sceneFogType=1):fog instanceof THREE.FogExp2&&(gl.uniform1f(uniforms.fogDensity,fog.density),gl.uniform1i(uniforms.fogType,2),oldFogType=2,sceneFogType=2)):(gl.uniform1i(uniforms.fogType,0),oldFogType=0,sceneFogType=0);for(var i=0,l=sprites.length;l>i;i++){var sprite=sprites[i];sprite.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse,sprite.matrixWorld),sprite.z=-sprite.modelViewMatrix.elements[14]}sprites.sort(painterSortStable);for(var scale=[],i=0,l=sprites.length;l>i;i++){var sprite=sprites[i],material=sprite.material;gl.uniform1f(uniforms.alphaTest,material.alphaTest),gl.uniformMatrix4fv(uniforms.modelViewMatrix,!1,sprite.modelViewMatrix.elements),sprite.matrixWorld.decompose(spritePosition,spriteRotation,spriteScale),scale[0]=spriteScale.x,scale[1]=spriteScale.y;var fogType=0;scene.fog&&material.fog&&(fogType=sceneFogType),oldFogType!==fogType&&(gl.uniform1i(uniforms.fogType,fogType),oldFogType=fogType),null!==material.map?(gl.uniform2f(uniforms.uvOffset,material.map.offset.x,material.map.offset.y),gl.uniform2f(uniforms.uvScale,material.map.repeat.x,material.map.repeat.y)):(gl.uniform2f(uniforms.uvOffset,0,0),gl.uniform2f(uniforms.uvScale,1,1)),gl.uniform1f(uniforms.opacity,material.opacity),gl.uniform3f(uniforms.color,material.color.r,material.color.g,material.color.b),gl.uniform1f(uniforms.rotation,material.rotation),gl.uniform2fv(uniforms.scale,scale),state.setBlending(material.blending,material.blendEquation,material.blendSrc,material.blendDst),state.setDepthTest(material.depthTest),state.setDepthWrite(material.depthWrite),material.map&&material.map.image&&material.map.image.width?renderer.setTexture(material.map,0):renderer.setTexture(texture,0),gl.drawElements(gl.TRIANGLES,6,gl.UNSIGNED_SHORT,0)}state.enable(gl.CULL_FACE),renderer.resetGLState()}}},THREE.CurveUtils={tangentQuadraticBezier:function(t,p0,p1,p2){return 2*(1-t)*(p1-p0)+2*t*(p2-p1)},tangentCubicBezier:function(t,p0,p1,p2,p3){return-3*p0*(1-t)*(1-t)+3*p1*(1-t)*(1-t)-6*t*p1*(1-t)+6*t*p2*(1-t)-3*t*t*p2+3*t*t*p3},tangentSpline:function(t,p0,p1,p2,p3){var h00=6*t*t-6*t,h10=3*t*t-4*t+1,h01=-6*t*t+6*t,h11=3*t*t-2*t;return h00+h10+h01+h11},interpolate:function(p0,p1,p2,p3,t){var v0=.5*(p2-p0),v1=.5*(p3-p1),t2=t*t,t3=t*t2;return(2*p1-2*p2+v0+v1)*t3+(-3*p1+3*p2-2*v0-v1)*t2+v0*t+p1}},THREE.GeometryUtils={merge:function(geometry1,geometry2,materialIndexOffset){console.warn("THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.");var matrix;geometry2 instanceof THREE.Mesh&&(geometry2.matrixAutoUpdate&&geometry2.updateMatrix(),matrix=geometry2.matrix,geometry2=geometry2.geometry),geometry1.merge(geometry2,matrix,materialIndexOffset)},center:function(geometry){return console.warn("THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead."),geometry.center()}},THREE.ImageUtils={crossOrigin:void 0,loadTexture:function(url,mapping,onLoad,onError){console.warn("THREE.ImageUtils.loadTexture is being deprecated. Use THREE.TextureLoader() instead.");var loader=new THREE.TextureLoader;loader.setCrossOrigin(this.crossOrigin);var texture=loader.load(url,onLoad,void 0,onError);return mapping&&(texture.mapping=mapping),texture},loadTextureCube:function(urls,mapping,onLoad,onError){console.warn("THREE.ImageUtils.loadTextureCube is being deprecated. Use THREE.CubeTextureLoader() instead.");var loader=new THREE.CubeTextureLoader;loader.setCrossOrigin(this.crossOrigin);var texture=loader.load(urls,onLoad,void 0,onError);return mapping&&(texture.mapping=mapping),texture},loadCompressedTexture:function(){console.error("THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.")},loadCompressedTextureCube:function(){console.error("THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.")}},THREE.SceneUtils={createMultiMaterialObject:function(geometry,materials){for(var group=new THREE.Group,i=0,l=materials.length;l>i;i++)group.add(new THREE.Mesh(geometry,materials[i]));return group},detach:function(child,parent,scene){child.applyMatrix(parent.matrixWorld),parent.remove(child),scene.add(child)},attach:function(child,scene,parent){var matrixWorldInverse=new THREE.Matrix4;matrixWorldInverse.getInverse(parent.matrixWorld),child.applyMatrix(matrixWorldInverse),scene.remove(child),parent.add(child)}},THREE.ShapeUtils={area:function(contour){for(var n=contour.length,a=0,p=n-1,q=0;n>q;p=q++)a+=contour[p].x*contour[q].y-contour[q].x*contour[p].y;return.5*a},triangulate:function(){function snip(contour,u,v,w,n,verts){var p,ax,ay,bx,by,cx,cy,px,py;if(ax=contour[verts[u]].x,ay=contour[verts[u]].y,bx=contour[verts[v]].x,by=contour[verts[v]].y,cx=contour[verts[w]].x,cy=contour[verts[w]].y,Number.EPSILON>(bx-ax)*(cy-ay)-(by-ay)*(cx-ax))return!1;var aX,aY,bX,bY,cX,cY,apx,apy,bpx,bpy,cpx,cpy,cCROSSap,bCROSScp,aCROSSbp;for(aX=cx-bx,aY=cy-by,bX=ax-cx,bY=ay-cy,cX=bx-ax,cY=by-ay,p=0;n>p;p++)if(px=contour[verts[p]].x,py=contour[verts[p]].y,!(px===ax&&py===ay||px===bx&&py===by||px===cx&&py===cy)&&(apx=px-ax,apy=py-ay,bpx=px-bx,bpy=py-by,cpx=px-cx,cpy=py-cy,aCROSSbp=aX*bpy-aY*bpx,cCROSSap=cX*apy-cY*apx,bCROSScp=bX*cpy-bY*cpx,aCROSSbp>=-Number.EPSILON&&bCROSScp>=-Number.EPSILON&&cCROSSap>=-Number.EPSILON))return!1;return!0}return function(contour,indices){var n=contour.length;if(3>n)return null;var u,v,w,result=[],verts=[],vertIndices=[];if(THREE.ShapeUtils.area(contour)>0)for(v=0;n>v;v++)verts[v]=v;else for(v=0;n>v;v++)verts[v]=n-1-v;var nv=n,count=2*nv;for(v=nv-1;nv>2;){if(count--<=0)return console.warn("THREE.ShapeUtils: Unable to triangulate polygon! in triangulate()"),indices?vertIndices:result;if(u=v,u>=nv&&(u=0),v=u+1,v>=nv&&(v=0),w=v+1,w>=nv&&(w=0),snip(contour,u,v,w,nv,verts)){var a,b,c,s,t;for(a=verts[u],b=verts[v],c=verts[w],result.push([contour[a],contour[b],contour[c]]),vertIndices.push([verts[u],verts[v],verts[w]]),s=v,t=v+1;nv>t;s++,t++)verts[s]=verts[t];nv--,count=2*nv}}return indices?vertIndices:result}}(),triangulateShape:function(contour,holes){function point_in_segment_2D_colin(inSegPt1,inSegPt2,inOtherPt){return inSegPt1.x!==inSegPt2.x?inSegPt1.xNumber.EPSILON){var perpSeg2;if(limit>0){if(0>perpSeg1||perpSeg1>limit)return[];if(perpSeg2=seg2dy*seg1seg2dx-seg2dx*seg1seg2dy,0>perpSeg2||perpSeg2>limit)return[]}else{if(perpSeg1>0||limit>perpSeg1)return[];if(perpSeg2=seg2dy*seg1seg2dx-seg2dx*seg1seg2dy,perpSeg2>0||limit>perpSeg2)return[]}if(0===perpSeg2)return!inExcludeAdjacentSegs||0!==perpSeg1&&perpSeg1!==limit?[inSeg1Pt1]:[];if(perpSeg2===limit)return!inExcludeAdjacentSegs||0!==perpSeg1&&perpSeg1!==limit?[inSeg1Pt2]:[];if(0===perpSeg1)return[inSeg2Pt1];if(perpSeg1===limit)return[inSeg2Pt2];var factorSeg1=perpSeg2/limit;return[{x:inSeg1Pt1.x+factorSeg1*seg1dx,y:inSeg1Pt1.y+factorSeg1*seg1dy}]}if(0!==perpSeg1||seg2dy*seg1seg2dx!==seg2dx*seg1seg2dy)return[];var seg1Pt=0===seg1dx&&0===seg1dy,seg2Pt=0===seg2dx&&0===seg2dy;if(seg1Pt&&seg2Pt)return inSeg1Pt1.x!==inSeg2Pt1.x||inSeg1Pt1.y!==inSeg2Pt1.y?[]:[inSeg1Pt1];if(seg1Pt)return point_in_segment_2D_colin(inSeg2Pt1,inSeg2Pt2,inSeg1Pt1)?[inSeg1Pt1]:[];if(seg2Pt)return point_in_segment_2D_colin(inSeg1Pt1,inSeg1Pt2,inSeg2Pt1)?[inSeg2Pt1]:[];var seg1min,seg1max,seg1minVal,seg1maxVal,seg2min,seg2max,seg2minVal,seg2maxVal;return 0!==seg1dx?(inSeg1Pt1.x=seg1minVal?seg2minVal>seg1maxVal?[]:seg1maxVal===seg2minVal?inExcludeAdjacentSegs?[]:[seg2min]:seg2maxVal>=seg1maxVal?[seg2min,seg1max]:[seg2min,seg2max]:seg1minVal>seg2maxVal?[]:seg1minVal===seg2maxVal?inExcludeAdjacentSegs?[]:[seg1min]:seg2maxVal>=seg1maxVal?[seg1min,seg1max]:[seg1min,seg2max]}function isPointInsideAngle(inVertex,inLegFromPt,inLegToPt,inOtherPt){var legFromPtX=inLegFromPt.x-inVertex.x,legFromPtY=inLegFromPt.y-inVertex.y,legToPtX=inLegToPt.x-inVertex.x,legToPtY=inLegToPt.y-inVertex.y,otherPtX=inOtherPt.x-inVertex.x,otherPtY=inOtherPt.y-inVertex.y,from2toAngle=legFromPtX*legToPtY-legFromPtY*legToPtX,from2otherAngle=legFromPtX*otherPtY-legFromPtY*otherPtX;if(Math.abs(from2toAngle)>Number.EPSILON){var other2toAngle=otherPtX*legToPtY-otherPtY*legToPtX;return from2toAngle>0?from2otherAngle>=0&&other2toAngle>=0:from2otherAngle>=0||other2toAngle>=0}return from2otherAngle>0}function removeHoles(contour,holes){function isCutLineInsideAngles(inShapeIdx,inHoleIdx){var lastShapeIdx=shape.length-1,prevShapeIdx=inShapeIdx-1;0>prevShapeIdx&&(prevShapeIdx=lastShapeIdx);var nextShapeIdx=inShapeIdx+1;nextShapeIdx>lastShapeIdx&&(nextShapeIdx=0);var insideAngle=isPointInsideAngle(shape[inShapeIdx],shape[prevShapeIdx],shape[nextShapeIdx],hole[inHoleIdx]);if(!insideAngle)return!1;var lastHoleIdx=hole.length-1,prevHoleIdx=inHoleIdx-1;0>prevHoleIdx&&(prevHoleIdx=lastHoleIdx);var nextHoleIdx=inHoleIdx+1;return nextHoleIdx>lastHoleIdx&&(nextHoleIdx=0),insideAngle=isPointInsideAngle(hole[inHoleIdx],hole[prevHoleIdx],hole[nextHoleIdx],shape[inShapeIdx]),!!insideAngle}function intersectsShapeEdge(inShapePt,inHolePt){var sIdx,nextIdx,intersection;for(sIdx=0;sIdx0)return!0;return!1}function intersectsHoleEdge(inShapePt,inHolePt){var ihIdx,chkHole,hIdx,nextIdx,intersection;for(ihIdx=0;ihIdx0)return!0;return!1}for(var hole,holeIndex,shapeIndex,shapePt,holePt,holeIdx,cutKey,tmpShape1,tmpShape2,tmpHole1,tmpHole2,shape=contour.concat(),indepHoles=[],failedCuts=[],h=0,hl=holes.length;hl>h;h++)indepHoles.push(h);for(var minShapeIndex=0,counter=2*indepHoles.length;indepHoles.length>0;){if(counter--,0>counter){console.log("Infinite Loop! Holes left:"+indepHoles.length+", Probably Hole outside Shape!");break}for(shapeIndex=minShapeIndex;shapeIndex=0)break;failedCuts[cutKey]=!0}if(holeIndex>=0)break}}return shape}for(var i,il,f,face,key,index,allPointsMap={},allpoints=contour.concat(),h=0,hl=holes.length;hl>h;h++)Array.prototype.push.apply(allpoints,holes[h]);for(i=0,il=allpoints.length;il>i;i++)key=allpoints[i].x+":"+allpoints[i].y,void 0!==allPointsMap[key]&&console.warn("THREE.Shape: Duplicate point",key),allPointsMap[key]=i;var shapeWithoutHoles=removeHoles(contour,holes),triangles=THREE.ShapeUtils.triangulate(shapeWithoutHoles,!1);for(i=0,il=triangles.length;il>i;i++)for(face=triangles[i],f=0;3>f;f++)key=face[f].x+":"+face[f].y,index=allPointsMap[key],void 0!==index&&(face[f]=index);return triangles.concat()},isClockWise:function(pts){return THREE.ShapeUtils.area(pts)<0},b2:function(){function b2p0(t,p){var k=1-t;return k*k*p}function b2p1(t,p){return 2*(1-t)*t*p}function b2p2(t,p){return t*t*p}return function(t,p0,p1,p2){return b2p0(t,p0)+b2p1(t,p1)+b2p2(t,p2)}}(),b3:function(){function b3p0(t,p){var k=1-t;return k*k*k*p}function b3p1(t,p){var k=1-t;return 3*k*k*t*p}function b3p2(t,p){var k=1-t;return 3*k*t*t*p}function b3p3(t,p){return t*t*t*p}return function(t,p0,p1,p2,p3){return b3p0(t,p0)+b3p1(t,p1)+b3p2(t,p2)+b3p3(t,p3)}}()},THREE.Audio=function(listener){THREE.Object3D.call(this),this.type="Audio",this.context=listener.context,this.source=this.context.createBufferSource(),this.source.onended=this.onEnded.bind(this),this.gain=this.context.createGain(),this.gain.connect(this.context.destination),this.panner=this.context.createPanner(),this.panner.connect(this.gain),this.autoplay=!1,this.startTime=0,this.playbackRate=1,this.isPlaying=!1},THREE.Audio.prototype=Object.create(THREE.Object3D.prototype),THREE.Audio.prototype.constructor=THREE.Audio,THREE.Audio.prototype.load=function(file){var scope=this,request=new XMLHttpRequest;return request.open("GET",file,!0),request.responseType="arraybuffer",request.onload=function(e){scope.context.decodeAudioData(this.response,function(buffer){scope.source.buffer=buffer,scope.autoplay&&scope.play()})},request.send(),this},THREE.Audio.prototype.play=function(){if(this.isPlaying===!0)return void console.warn("THREE.Audio: Audio is already playing.");var source=this.context.createBufferSource();source.buffer=this.source.buffer,source.loop=this.source.loop,source.onended=this.source.onended,source.start(0,this.startTime),source.playbackRate.value=this.playbackRate,this.isPlaying=!0,this.source=source,this.connect()},THREE.Audio.prototype.pause=function(){this.source.stop(),this.startTime=this.context.currentTime},THREE.Audio.prototype.stop=function(){this.source.stop(),this.startTime=0},THREE.Audio.prototype.connect=function(){void 0!==this.filter?(this.source.connect(this.filter),this.filter.connect(this.panner)):this.source.connect(this.panner)},THREE.Audio.prototype.disconnect=function(){void 0!==this.filter?(this.source.disconnect(this.filter),this.filter.disconnect(this.panner)):this.source.disconnect(this.panner)},THREE.Audio.prototype.setFilter=function(value){this.isPlaying===!0?(this.disconnect(),this.filter=value,this.connect()):this.filter=value},THREE.Audio.prototype.getFilter=function(){return this.filter},THREE.Audio.prototype.setPlaybackRate=function(value){this.playbackRate=value,this.isPlaying===!0&&(this.source.playbackRate.value=this.playbackRate)},THREE.Audio.prototype.getPlaybackRate=function(){return this.playbackRate},THREE.Audio.prototype.onEnded=function(){this.isPlaying=!1},THREE.Audio.prototype.setLoop=function(value){this.source.loop=value},THREE.Audio.prototype.getLoop=function(){return this.source.loop},THREE.Audio.prototype.setRefDistance=function(value){this.panner.refDistance=value},THREE.Audio.prototype.getRefDistance=function(){return this.panner.refDistance},THREE.Audio.prototype.setRolloffFactor=function(value){this.panner.rolloffFactor=value},THREE.Audio.prototype.getRolloffFactor=function(){return this.panner.rolloffFactor},THREE.Audio.prototype.setVolume=function(value){this.gain.gain.value=value},THREE.Audio.prototype.getVolume=function(){return this.gain.gain.value},THREE.Audio.prototype.updateMatrixWorld=function(){var position=new THREE.Vector3;return function(force){THREE.Object3D.prototype.updateMatrixWorld.call(this,force),position.setFromMatrixPosition(this.matrixWorld),this.panner.setPosition(position.x,position.y,position.z)}}(),THREE.AudioListener=function(){THREE.Object3D.call(this),this.type="AudioListener",this.context=new(window.AudioContext||window.webkitAudioContext)},THREE.AudioListener.prototype=Object.create(THREE.Object3D.prototype),THREE.AudioListener.prototype.constructor=THREE.AudioListener,THREE.AudioListener.prototype.updateMatrixWorld=function(){var position=new THREE.Vector3,quaternion=new THREE.Quaternion,scale=new THREE.Vector3,orientation=new THREE.Vector3;return function(force){THREE.Object3D.prototype.updateMatrixWorld.call(this,force);var listener=this.context.listener,up=this.up;this.matrixWorld.decompose(position,quaternion,scale),orientation.set(0,0,-1).applyQuaternion(quaternion),listener.setPosition(position.x,position.y,position.z),listener.setOrientation(orientation.x,orientation.y,orientation.z,up.x,up.y,up.z)}}(),THREE.Curve=function(){},THREE.Curve.prototype={constructor:THREE.Curve,getPoint:function(t){return console.warn("THREE.Curve: Warning, getPoint() not implemented!"),null},getPointAt:function(u){var t=this.getUtoTmapping(u);return this.getPoint(t)},getPoints:function(divisions){divisions||(divisions=5);var d,pts=[];for(d=0;divisions>=d;d++)pts.push(this.getPoint(d/divisions));return pts},getSpacedPoints:function(divisions){divisions||(divisions=5);var d,pts=[];for(d=0;divisions>=d;d++)pts.push(this.getPointAt(d/divisions));return pts},getLength:function(){var lengths=this.getLengths();return lengths[lengths.length-1]},getLengths:function(divisions){if(divisions||(divisions=this.__arcLengthDivisions?this.__arcLengthDivisions:200),this.cacheArcLengths&&this.cacheArcLengths.length===divisions+1&&!this.needsUpdate)return this.cacheArcLengths;this.needsUpdate=!1;var current,p,cache=[],last=this.getPoint(0),sum=0;for(cache.push(0),p=1;divisions>=p;p++)current=this.getPoint(p/divisions),sum+=current.distanceTo(last),cache.push(sum),last=current;return this.cacheArcLengths=cache,cache},updateArcLengths:function(){this.needsUpdate=!0,this.getLengths()},getUtoTmapping:function(u,distance){var targetArcLength,arcLengths=this.getLengths(),i=0,il=arcLengths.length;targetArcLength=distance?distance:u*arcLengths[il-1];for(var comparison,low=0,high=il-1;high>=low;)if(i=Math.floor(low+(high-low)/2),comparison=arcLengths[i]-targetArcLength,0>comparison)low=i+1;else{if(!(comparison>0)){high=i;break}high=i-1}if(i=high,arcLengths[i]===targetArcLength){var t=i/(il-1);return t}var lengthBefore=arcLengths[i],lengthAfter=arcLengths[i+1],segmentLength=lengthAfter-lengthBefore,segmentFraction=(targetArcLength-lengthBefore)/segmentLength,t=(i+segmentFraction)/(il-1);return t},getTangent:function(t){var delta=1e-4,t1=t-delta,t2=t+delta;0>t1&&(t1=0),t2>1&&(t2=1);var pt1=this.getPoint(t1),pt2=this.getPoint(t2),vec=pt2.clone().sub(pt1);return vec.normalize()},getTangentAt:function(u){var t=this.getUtoTmapping(u);return this.getTangent(t)}},THREE.Curve.Utils=THREE.CurveUtils,THREE.Curve.create=function(constructor,getPointFunc){return constructor.prototype=Object.create(THREE.Curve.prototype),constructor.prototype.constructor=constructor,constructor.prototype.getPoint=getPointFunc,constructor},THREE.CurvePath=function(){this.curves=[],this.autoClose=!1},THREE.CurvePath.prototype=Object.create(THREE.Curve.prototype),THREE.CurvePath.prototype.constructor=THREE.CurvePath,THREE.CurvePath.prototype.add=function(curve){this.curves.push(curve)},THREE.CurvePath.prototype.closePath=function(){var startPoint=this.curves[0].getPoint(0),endPoint=this.curves[this.curves.length-1].getPoint(1);startPoint.equals(endPoint)||this.curves.push(new THREE.LineCurve(endPoint,startPoint))},THREE.CurvePath.prototype.getPoint=function(t){for(var d=t*this.getLength(),curveLengths=this.getCurveLengths(),i=0;i=d){var diff=curveLengths[i]-d,curve=this.curves[i],u=1-diff/curve.getLength();return curve.getPointAt(u)}i++}return null},THREE.CurvePath.prototype.getLength=function(){var lens=this.getCurveLengths();return lens[lens.length-1]},THREE.CurvePath.prototype.getCurveLengths=function(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length)return this.cacheLengths;for(var lengths=[],sums=0,i=0,l=this.curves.length;l>i;i++)sums+=this.curves[i].getLength(),lengths.push(sums);return this.cacheLengths=lengths,lengths},THREE.CurvePath.prototype.createPointsGeometry=function(divisions){var pts=this.getPoints(divisions,!0);return this.createGeometry(pts)},THREE.CurvePath.prototype.createSpacedPointsGeometry=function(divisions){var pts=this.getSpacedPoints(divisions,!0);return this.createGeometry(pts)},THREE.CurvePath.prototype.createGeometry=function(points){for(var geometry=new THREE.Geometry,i=0,l=points.length;l>i;i++){var point=points[i];geometry.vertices.push(new THREE.Vector3(point.x,point.y,point.z||0))}return geometry},THREE.Path=function(points){THREE.CurvePath.call(this),this.actions=[],points&&this.fromPoints(points)},THREE.Path.prototype=Object.create(THREE.CurvePath.prototype),THREE.Path.prototype.constructor=THREE.Path,THREE.Path.prototype.fromPoints=function(vectors){this.moveTo(vectors[0].x,vectors[0].y);for(var i=1,l=vectors.length;l>i;i++)this.lineTo(vectors[i].x,vectors[i].y)},THREE.Path.prototype.moveTo=function(x,y){this.actions.push({action:"moveTo",args:[x,y]})},THREE.Path.prototype.lineTo=function(x,y){var lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1],curve=new THREE.LineCurve(new THREE.Vector2(x0,y0),new THREE.Vector2(x,y));this.curves.push(curve),this.actions.push({action:"lineTo",args:[x,y]})},THREE.Path.prototype.quadraticCurveTo=function(aCPx,aCPy,aX,aY){var lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1],curve=new THREE.QuadraticBezierCurve(new THREE.Vector2(x0,y0),new THREE.Vector2(aCPx,aCPy),new THREE.Vector2(aX,aY));this.curves.push(curve),this.actions.push({action:"quadraticCurveTo",args:[aCPx,aCPy,aX,aY]})},THREE.Path.prototype.bezierCurveTo=function(aCP1x,aCP1y,aCP2x,aCP2y,aX,aY){var lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1],curve=new THREE.CubicBezierCurve(new THREE.Vector2(x0,y0),new THREE.Vector2(aCP1x,aCP1y),new THREE.Vector2(aCP2x,aCP2y),new THREE.Vector2(aX,aY));this.curves.push(curve),this.actions.push({action:"bezierCurveTo",args:[aCP1x,aCP1y,aCP2x,aCP2y,aX,aY]})},THREE.Path.prototype.splineThru=function(pts){var args=Array.prototype.slice.call(arguments),lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1],npts=[new THREE.Vector2(x0,y0)];Array.prototype.push.apply(npts,pts);var curve=new THREE.SplineCurve(npts);this.curves.push(curve),this.actions.push({action:"splineThru",args:args})},THREE.Path.prototype.arc=function(aX,aY,aRadius,aStartAngle,aEndAngle,aClockwise){var lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1];this.absarc(aX+x0,aY+y0,aRadius,aStartAngle,aEndAngle,aClockwise)},THREE.Path.prototype.absarc=function(aX,aY,aRadius,aStartAngle,aEndAngle,aClockwise){this.absellipse(aX,aY,aRadius,aRadius,aStartAngle,aEndAngle,aClockwise)},THREE.Path.prototype.ellipse=function(aX,aY,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation){var lastargs=this.actions[this.actions.length-1].args,x0=lastargs[lastargs.length-2],y0=lastargs[lastargs.length-1];this.absellipse(aX+x0,aY+y0,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation)},THREE.Path.prototype.absellipse=function(aX,aY,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation){var args=[aX,aY,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation||0],curve=new THREE.EllipseCurve(aX,aY,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation);this.curves.push(curve);var lastPoint=curve.getPoint(1);args.push(lastPoint.x),args.push(lastPoint.y),this.actions.push({action:"ellipse",args:args})},THREE.Path.prototype.getSpacedPoints=function(divisions,closedPath){divisions||(divisions=40);for(var points=[],i=0;divisions>i;i++)points.push(this.getPoint(i/divisions));return points},THREE.Path.prototype.getPoints=function(divisions,closedPath){divisions=divisions||12;for(var cpx,cpy,cpx2,cpy2,cpx1,cpy1,cpx0,cpy0,laste,tx,ty,b2=THREE.ShapeUtils.b2,b3=THREE.ShapeUtils.b3,points=[],i=0,l=this.actions.length;l>i;i++){var item=this.actions[i],action=item.action,args=item.args;switch(action){case"moveTo":points.push(new THREE.Vector2(args[0],args[1]));break;case"lineTo":points.push(new THREE.Vector2(args[0],args[1])); +break;case"quadraticCurveTo":cpx=args[2],cpy=args[3],cpx1=args[0],cpy1=args[1],points.length>0?(laste=points[points.length-1],cpx0=laste.x,cpy0=laste.y):(laste=this.actions[i-1].args,cpx0=laste[laste.length-2],cpy0=laste[laste.length-1]);for(var j=1;divisions>=j;j++){var t=j/divisions;tx=b2(t,cpx0,cpx1,cpx),ty=b2(t,cpy0,cpy1,cpy),points.push(new THREE.Vector2(tx,ty))}break;case"bezierCurveTo":cpx=args[4],cpy=args[5],cpx1=args[0],cpy1=args[1],cpx2=args[2],cpy2=args[3],points.length>0?(laste=points[points.length-1],cpx0=laste.x,cpy0=laste.y):(laste=this.actions[i-1].args,cpx0=laste[laste.length-2],cpy0=laste[laste.length-1]);for(var j=1;divisions>=j;j++){var t=j/divisions;tx=b3(t,cpx0,cpx1,cpx2,cpx),ty=b3(t,cpy0,cpy1,cpy2,cpy),points.push(new THREE.Vector2(tx,ty))}break;case"splineThru":laste=this.actions[i-1].args;var last=new THREE.Vector2(laste[laste.length-2],laste[laste.length-1]),spts=[last],n=divisions*args[0].length;spts=spts.concat(args[0]);for(var spline=new THREE.SplineCurve(spts),j=1;n>=j;j++)points.push(spline.getPointAt(j/n));break;case"arc":for(var angle,aX=args[0],aY=args[1],aRadius=args[2],aStartAngle=args[3],aEndAngle=args[4],aClockwise=!!args[5],deltaAngle=aEndAngle-aStartAngle,tdivisions=2*divisions,j=1;tdivisions>=j;j++){var t=j/tdivisions;aClockwise||(t=1-t),angle=aStartAngle+t*deltaAngle,tx=aX+aRadius*Math.cos(angle),ty=aY+aRadius*Math.sin(angle),points.push(new THREE.Vector2(tx,ty))}break;case"ellipse":var angle,cos,sin,aX=args[0],aY=args[1],xRadius=args[2],yRadius=args[3],aStartAngle=args[4],aEndAngle=args[5],aClockwise=!!args[6],aRotation=args[7],deltaAngle=aEndAngle-aStartAngle,tdivisions=2*divisions;0!==aRotation&&(cos=Math.cos(aRotation),sin=Math.sin(aRotation));for(var j=1;tdivisions>=j;j++){var t=j/tdivisions;if(aClockwise||(t=1-t),angle=aStartAngle+t*deltaAngle,tx=aX+xRadius*Math.cos(angle),ty=aY+yRadius*Math.sin(angle),0!==aRotation){var x=tx,y=ty;tx=(x-aX)*cos-(y-aY)*sin+aX,ty=(x-aX)*sin+(y-aY)*cos+aY}points.push(new THREE.Vector2(tx,ty))}}}var lastPoint=points[points.length-1];return Math.abs(lastPoint.x-points[0].x)i;i++){var item=inActions[i],args=item.args,action=item.action;"moveTo"===action&&0!==lastPath.actions.length&&(subPaths.push(lastPath),lastPath=new THREE.Path),lastPath[action].apply(lastPath,args)}return 0!==lastPath.actions.length&&subPaths.push(lastPath),subPaths}function toShapesNoHoles(inSubpaths){for(var shapes=[],i=0,l=inSubpaths.length;l>i;i++){var tmpPath=inSubpaths[i],tmpShape=new THREE.Shape;tmpShape.actions=tmpPath.actions,tmpShape.curves=tmpPath.curves,shapes.push(tmpShape)}return shapes}function isPointInsidePolygon(inPt,inPolygon){for(var polyLen=inPolygon.length,inside=!1,p=polyLen-1,q=0;polyLen>q;p=q++){var edgeLowPt=inPolygon[p],edgeHighPt=inPolygon[q],edgeDx=edgeHighPt.x-edgeLowPt.x,edgeDy=edgeHighPt.y-edgeLowPt.y;if(Math.abs(edgeDy)>Number.EPSILON){if(0>edgeDy&&(edgeLowPt=inPolygon[q],edgeDx=-edgeDx,edgeHighPt=inPolygon[p],edgeDy=-edgeDy),inPt.yedgeHighPt.y)continue;if(inPt.y===edgeLowPt.y){if(inPt.x===edgeLowPt.x)return!0}else{var perpEdge=edgeDy*(inPt.x-edgeLowPt.x)-edgeDx*(inPt.y-edgeLowPt.y);if(0===perpEdge)return!0;if(0>perpEdge)continue;inside=!inside}}else{if(inPt.y!==edgeLowPt.y)continue;if(edgeHighPt.x<=inPt.x&&inPt.x<=edgeLowPt.x||edgeLowPt.x<=inPt.x&&inPt.x<=edgeHighPt.x)return!0}}return inside}var isClockWise=THREE.ShapeUtils.isClockWise,subPaths=extractSubpaths(this.actions);if(0===subPaths.length)return[];if(noHoles===!0)return toShapesNoHoles(subPaths);var solid,tmpPath,tmpShape,shapes=[];if(1===subPaths.length)return tmpPath=subPaths[0],tmpShape=new THREE.Shape,tmpShape.actions=tmpPath.actions,tmpShape.curves=tmpPath.curves,shapes.push(tmpShape),shapes;var holesFirst=!isClockWise(subPaths[0].getPoints());holesFirst=isCCW?!holesFirst:holesFirst;var tmpPoints,betterShapeHoles=[],newShapes=[],newShapeHoles=[],mainIdx=0;newShapes[mainIdx]=void 0,newShapeHoles[mainIdx]=[];for(var i=0,l=subPaths.length;l>i;i++)tmpPath=subPaths[i],tmpPoints=tmpPath.getPoints(),solid=isClockWise(tmpPoints),solid=isCCW?!solid:solid,solid?(!holesFirst&&newShapes[mainIdx]&&mainIdx++,newShapes[mainIdx]={s:new THREE.Shape,p:tmpPoints},newShapes[mainIdx].s.actions=tmpPath.actions,newShapes[mainIdx].s.curves=tmpPath.curves,holesFirst&&mainIdx++,newShapeHoles[mainIdx]=[]):newShapeHoles[mainIdx].push({h:tmpPath,p:tmpPoints[0]});if(!newShapes[0])return toShapesNoHoles(subPaths);if(newShapes.length>1){for(var ambiguous=!1,toChange=[],sIdx=0,sLen=newShapes.length;sLen>sIdx;sIdx++)betterShapeHoles[sIdx]=[];for(var sIdx=0,sLen=newShapes.length;sLen>sIdx;sIdx++)for(var sho=newShapeHoles[sIdx],hIdx=0;hIdx0&&(ambiguous||(newShapeHoles=betterShapeHoles))}for(var tmpHoles,i=0,il=newShapes.length;il>i;i++){tmpShape=newShapes[i].s,shapes.push(tmpShape),tmpHoles=newShapeHoles[i];for(var j=0,jl=tmpHoles.length;jl>j;j++)tmpShape.holes.push(tmpHoles[j].h)}return shapes},THREE.Shape=function(){THREE.Path.apply(this,arguments),this.holes=[]},THREE.Shape.prototype=Object.create(THREE.Path.prototype),THREE.Shape.prototype.constructor=THREE.Shape,THREE.Shape.prototype.extrude=function(options){return new THREE.ExtrudeGeometry(this,options)},THREE.Shape.prototype.makeGeometry=function(options){return new THREE.ShapeGeometry(this,options)},THREE.Shape.prototype.getPointsHoles=function(divisions){for(var holesPts=[],i=0,l=this.holes.length;l>i;i++)holesPts[i]=this.holes[i].getPoints(divisions);return holesPts},THREE.Shape.prototype.extractAllPoints=function(divisions){return{shape:this.getPoints(divisions),holes:this.getPointsHoles(divisions)}},THREE.Shape.prototype.extractPoints=function(divisions){return this.extractAllPoints(divisions)},THREE.Shape.Utils=THREE.ShapeUtils,THREE.LineCurve=function(v1,v2){this.v1=v1,this.v2=v2},THREE.LineCurve.prototype=Object.create(THREE.Curve.prototype),THREE.LineCurve.prototype.constructor=THREE.LineCurve,THREE.LineCurve.prototype.getPoint=function(t){var point=this.v2.clone().sub(this.v1);return point.multiplyScalar(t).add(this.v1),point},THREE.LineCurve.prototype.getPointAt=function(u){return this.getPoint(u)},THREE.LineCurve.prototype.getTangent=function(t){var tangent=this.v2.clone().sub(this.v1);return tangent.normalize()},THREE.QuadraticBezierCurve=function(v0,v1,v2){this.v0=v0,this.v1=v1,this.v2=v2},THREE.QuadraticBezierCurve.prototype=Object.create(THREE.Curve.prototype),THREE.QuadraticBezierCurve.prototype.constructor=THREE.QuadraticBezierCurve,THREE.QuadraticBezierCurve.prototype.getPoint=function(t){var b2=THREE.ShapeUtils.b2;return new THREE.Vector2(b2(t,this.v0.x,this.v1.x,this.v2.x),b2(t,this.v0.y,this.v1.y,this.v2.y))},THREE.QuadraticBezierCurve.prototype.getTangent=function(t){var tangentQuadraticBezier=THREE.CurveUtils.tangentQuadraticBezier;return new THREE.Vector2(tangentQuadraticBezier(t,this.v0.x,this.v1.x,this.v2.x),tangentQuadraticBezier(t,this.v0.y,this.v1.y,this.v2.y)).normalize()},THREE.CubicBezierCurve=function(v0,v1,v2,v3){this.v0=v0,this.v1=v1,this.v2=v2,this.v3=v3},THREE.CubicBezierCurve.prototype=Object.create(THREE.Curve.prototype),THREE.CubicBezierCurve.prototype.constructor=THREE.CubicBezierCurve,THREE.CubicBezierCurve.prototype.getPoint=function(t){var b3=THREE.ShapeUtils.b3;return new THREE.Vector2(b3(t,this.v0.x,this.v1.x,this.v2.x,this.v3.x),b3(t,this.v0.y,this.v1.y,this.v2.y,this.v3.y))},THREE.CubicBezierCurve.prototype.getTangent=function(t){var tangentCubicBezier=THREE.CurveUtils.tangentCubicBezier;return new THREE.Vector2(tangentCubicBezier(t,this.v0.x,this.v1.x,this.v2.x,this.v3.x),tangentCubicBezier(t,this.v0.y,this.v1.y,this.v2.y,this.v3.y)).normalize()},THREE.SplineCurve=function(points){this.points=void 0==points?[]:points},THREE.SplineCurve.prototype=Object.create(THREE.Curve.prototype),THREE.SplineCurve.prototype.constructor=THREE.SplineCurve,THREE.SplineCurve.prototype.getPoint=function(t){var points=this.points,point=(points.length-1)*t,intPoint=Math.floor(point),weight=point-intPoint,point0=points[0===intPoint?intPoint:intPoint-1],point1=points[intPoint],point2=points[intPoint>points.length-2?points.length-1:intPoint+1],point3=points[intPoint>points.length-3?points.length-1:intPoint+2],interpolate=THREE.CurveUtils.interpolate;return new THREE.Vector2(interpolate(point0.x,point1.x,point2.x,point3.x,weight),interpolate(point0.y,point1.y,point2.y,point3.y,weight))},THREE.EllipseCurve=function(aX,aY,xRadius,yRadius,aStartAngle,aEndAngle,aClockwise,aRotation){this.aX=aX,this.aY=aY,this.xRadius=xRadius,this.yRadius=yRadius,this.aStartAngle=aStartAngle,this.aEndAngle=aEndAngle,this.aClockwise=aClockwise,this.aRotation=aRotation||0},THREE.EllipseCurve.prototype=Object.create(THREE.Curve.prototype),THREE.EllipseCurve.prototype.constructor=THREE.EllipseCurve,THREE.EllipseCurve.prototype.getPoint=function(t){var deltaAngle=this.aEndAngle-this.aStartAngle;0>deltaAngle&&(deltaAngle+=2*Math.PI),deltaAngle>2*Math.PI&&(deltaAngle-=2*Math.PI);var angle;angle=this.aClockwise===!0?this.aEndAngle+(1-t)*(2*Math.PI-deltaAngle):this.aStartAngle+t*deltaAngle;var x=this.aX+this.xRadius*Math.cos(angle),y=this.aY+this.yRadius*Math.sin(angle);if(0!==this.aRotation){var cos=Math.cos(this.aRotation),sin=Math.sin(this.aRotation),tx=x,ty=y;x=(tx-this.aX)*cos-(ty-this.aY)*sin+this.aX,y=(tx-this.aX)*sin+(ty-this.aY)*cos+this.aY}return new THREE.Vector2(x,y)},THREE.ArcCurve=function(aX,aY,aRadius,aStartAngle,aEndAngle,aClockwise){THREE.EllipseCurve.call(this,aX,aY,aRadius,aRadius,aStartAngle,aEndAngle,aClockwise)},THREE.ArcCurve.prototype=Object.create(THREE.EllipseCurve.prototype),THREE.ArcCurve.prototype.constructor=THREE.ArcCurve,THREE.LineCurve3=THREE.Curve.create(function(v1,v2){this.v1=v1,this.v2=v2},function(t){var vector=new THREE.Vector3;return vector.subVectors(this.v2,this.v1),vector.multiplyScalar(t),vector.add(this.v1),vector}),THREE.QuadraticBezierCurve3=THREE.Curve.create(function(v0,v1,v2){this.v0=v0,this.v1=v1,this.v2=v2},function(t){var b2=THREE.ShapeUtils.b2;return new THREE.Vector3(b2(t,this.v0.x,this.v1.x,this.v2.x),b2(t,this.v0.y,this.v1.y,this.v2.y),b2(t,this.v0.z,this.v1.z,this.v2.z))}),THREE.CubicBezierCurve3=THREE.Curve.create(function(v0,v1,v2,v3){this.v0=v0,this.v1=v1,this.v2=v2,this.v3=v3},function(t){var b3=THREE.ShapeUtils.b3;return new THREE.Vector3(b3(t,this.v0.x,this.v1.x,this.v2.x,this.v3.x),b3(t,this.v0.y,this.v1.y,this.v2.y,this.v3.y),b3(t,this.v0.z,this.v1.z,this.v2.z,this.v3.z))}),THREE.SplineCurve3=THREE.Curve.create(function(points){console.warn("THREE.SplineCurve3 will be deprecated. Please use THREE.CatmullRomCurve3"),this.points=void 0==points?[]:points},function(t){var points=this.points,point=(points.length-1)*t,intPoint=Math.floor(point),weight=point-intPoint,point0=points[0==intPoint?intPoint:intPoint-1],point1=points[intPoint],point2=points[intPoint>points.length-2?points.length-1:intPoint+1],point3=points[intPoint>points.length-3?points.length-1:intPoint+2],interpolate=THREE.CurveUtils.interpolate;return new THREE.Vector3(interpolate(point0.x,point1.x,point2.x,point3.x,weight),interpolate(point0.y,point1.y,point2.y,point3.y,weight),interpolate(point0.z,point1.z,point2.z,point3.z,weight))}),THREE.CatmullRomCurve3=function(){function CubicPoly(){}var tmp=new THREE.Vector3,px=new CubicPoly,py=new CubicPoly,pz=new CubicPoly;return CubicPoly.prototype.init=function(x0,x1,t0,t1){this.c0=x0,this.c1=t0,this.c2=-3*x0+3*x1-2*t0-t1,this.c3=2*x0-2*x1+t0+t1},CubicPoly.prototype.initNonuniformCatmullRom=function(x0,x1,x2,x3,dt0,dt1,dt2){var t1=(x1-x0)/dt0-(x2-x0)/(dt0+dt1)+(x2-x1)/dt1,t2=(x2-x1)/dt1-(x3-x1)/(dt1+dt2)+(x3-x2)/dt2;t1*=dt1,t2*=dt1,this.init(x1,x2,t1,t2)},CubicPoly.prototype.initCatmullRom=function(x0,x1,x2,x3,tension){this.init(x1,x2,tension*(x2-x0),tension*(x3-x1))},CubicPoly.prototype.calc=function(t){var t2=t*t,t3=t2*t;return this.c0+this.c1*t+this.c2*t2+this.c3*t3},THREE.Curve.create(function(p){this.points=p||[]},function(t){var point,intPoint,weight,l,points=this.points;l=points.length,2>l&&console.log("duh, you need at least 2 points"),point=(l-1)*t,intPoint=Math.floor(point),weight=point-intPoint,0===weight&&intPoint===l-1&&(intPoint=l-2,weight=1);var p0,p1,p2,p3;if(0===intPoint?(tmp.subVectors(points[0],points[1]).add(points[0]),p0=tmp):p0=points[intPoint-1],p1=points[intPoint],p2=points[intPoint+1],l>intPoint+2?p3=points[intPoint+2]:(tmp.subVectors(points[l-1],points[l-2]).add(points[l-2]),p3=tmp),void 0===this.type||"centripetal"===this.type||"chordal"===this.type){var pow="chordal"===this.type?.5:.25,dt0=Math.pow(p0.distanceToSquared(p1),pow),dt1=Math.pow(p1.distanceToSquared(p2),pow),dt2=Math.pow(p2.distanceToSquared(p3),pow);1e-4>dt1&&(dt1=1),1e-4>dt0&&(dt0=dt1),1e-4>dt2&&(dt2=dt1),px.initNonuniformCatmullRom(p0.x,p1.x,p2.x,p3.x,dt0,dt1,dt2),py.initNonuniformCatmullRom(p0.y,p1.y,p2.y,p3.y,dt0,dt1,dt2),pz.initNonuniformCatmullRom(p0.z,p1.z,p2.z,p3.z,dt0,dt1,dt2)}else if("catmullrom"===this.type){var tension=void 0!==this.tension?this.tension:.5;px.initCatmullRom(p0.x,p1.x,p2.x,p3.x,tension),py.initCatmullRom(p0.y,p1.y,p2.y,p3.y,tension),pz.initCatmullRom(p0.z,p1.z,p2.z,p3.z,tension)}var v=new THREE.Vector3(px.calc(weight),py.calc(weight),pz.calc(weight));return v})}(),THREE.ClosedSplineCurve3=THREE.Curve.create(function(points){this.points=void 0==points?[]:points},function(t){var points=this.points,point=(points.length-0)*t,intPoint=Math.floor(point),weight=point-intPoint;intPoint+=intPoint>0?0:(Math.floor(Math.abs(intPoint)/points.length)+1)*points.length;var point0=points[(intPoint-1)%points.length],point1=points[intPoint%points.length],point2=points[(intPoint+1)%points.length],point3=points[(intPoint+2)%points.length],interpolate=THREE.CurveUtils.interpolate;return new THREE.Vector3(interpolate(point0.x,point1.x,point2.x,point3.x,weight),interpolate(point0.y,point1.y,point2.y,point3.y,weight),interpolate(point0.z,point1.z,point2.z,point3.z,weight))}),THREE.BoxGeometry=function(width,height,depth,widthSegments,heightSegments,depthSegments){function buildPlane(u,v,udir,vdir,width,height,depth,materialIndex){var w,ix,iy,gridX=scope.widthSegments,gridY=scope.heightSegments,width_half=width/2,height_half=height/2,offset=scope.vertices.length;"x"===u&&"y"===v||"y"===u&&"x"===v?w="z":"x"===u&&"z"===v||"z"===u&&"x"===v?(w="y",gridY=scope.depthSegments):("z"===u&&"y"===v||"y"===u&&"z"===v)&&(w="x",gridX=scope.depthSegments);var gridX1=gridX+1,gridY1=gridY+1,segment_width=width/gridX,segment_height=height/gridY,normal=new THREE.Vector3;for(normal[w]=depth>0?1:-1,iy=0;gridY1>iy;iy++)for(ix=0;gridX1>ix;ix++){var vector=new THREE.Vector3;vector[u]=(ix*segment_width-width_half)*udir,vector[v]=(iy*segment_height-height_half)*vdir,vector[w]=depth,scope.vertices.push(vector)}for(iy=0;gridY>iy;iy++)for(ix=0;gridX>ix;ix++){var a=ix+gridX1*iy,b=ix+gridX1*(iy+1),c=ix+1+gridX1*(iy+1),d=ix+1+gridX1*iy,uva=new THREE.Vector2(ix/gridX,1-iy/gridY),uvb=new THREE.Vector2(ix/gridX,1-(iy+1)/gridY),uvc=new THREE.Vector2((ix+1)/gridX,1-(iy+1)/gridY),uvd=new THREE.Vector2((ix+1)/gridX,1-iy/gridY),face=new THREE.Face3(a+offset,b+offset,d+offset);face.normal.copy(normal),face.vertexNormals.push(normal.clone(),normal.clone(),normal.clone()),face.materialIndex=materialIndex,scope.faces.push(face),scope.faceVertexUvs[0].push([uva,uvb,uvd]),face=new THREE.Face3(b+offset,c+offset,d+offset),face.normal.copy(normal),face.vertexNormals.push(normal.clone(),normal.clone(),normal.clone()),face.materialIndex=materialIndex,scope.faces.push(face),scope.faceVertexUvs[0].push([uvb.clone(),uvc,uvd.clone()])}}THREE.Geometry.call(this),this.type="BoxGeometry",this.parameters={width:width,height:height,depth:depth,widthSegments:widthSegments,heightSegments:heightSegments,depthSegments:depthSegments},this.widthSegments=widthSegments||1,this.heightSegments=heightSegments||1,this.depthSegments=depthSegments||1;var scope=this,width_half=width/2,height_half=height/2,depth_half=depth/2;buildPlane("z","y",-1,-1,depth,height,width_half,0),buildPlane("z","y",1,-1,depth,height,-width_half,1),buildPlane("x","z",1,1,width,depth,height_half,2),buildPlane("x","z",1,-1,width,depth,-height_half,3),buildPlane("x","y",1,-1,width,height,depth_half,4),buildPlane("x","y",-1,-1,width,height,-depth_half,5),this.mergeVertices()},THREE.BoxGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.BoxGeometry.prototype.constructor=THREE.BoxGeometry,THREE.BoxGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.BoxGeometry(parameters.width,parameters.height,parameters.depth,parameters.widthSegments,parameters.heightSegments,parameters.depthSegments)},THREE.CubeGeometry=THREE.BoxGeometry,THREE.CircleGeometry=function(radius,segments,thetaStart,thetaLength){THREE.Geometry.call(this),this.type="CircleGeometry",this.parameters={radius:radius,segments:segments,thetaStart:thetaStart,thetaLength:thetaLength},this.fromBufferGeometry(new THREE.CircleBufferGeometry(radius,segments,thetaStart,thetaLength))},THREE.CircleGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.CircleGeometry.prototype.constructor=THREE.CircleGeometry,THREE.CircleGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.CircleGeometry(parameters.radius,parameters.segments,parameters.thetaStart,parameters.thetaLength)},THREE.CircleBufferGeometry=function(radius,segments,thetaStart,thetaLength){THREE.BufferGeometry.call(this),this.type="CircleBufferGeometry",this.parameters={radius:radius,segments:segments,thetaStart:thetaStart,thetaLength:thetaLength},radius=radius||50,segments=void 0!==segments?Math.max(3,segments):8,thetaStart=void 0!==thetaStart?thetaStart:0,thetaLength=void 0!==thetaLength?thetaLength:2*Math.PI;var vertices=segments+2,positions=new Float32Array(3*vertices),normals=new Float32Array(3*vertices),uvs=new Float32Array(2*vertices);normals[2]=1,uvs[0]=.5,uvs[1]=.5;for(var s=0,i=3,ii=2;segments>=s;s++,i+=3,ii+=2){var segment=thetaStart+s/segments*thetaLength;positions[i]=radius*Math.cos(segment),positions[i+1]=radius*Math.sin(segment),normals[i+2]=1,uvs[ii]=(positions[i]/radius+1)/2,uvs[ii+1]=(positions[i+1]/radius+1)/2}for(var indices=[],i=1;segments>=i;i++)indices.push(i,i+1,0);this.setIndex(new THREE.BufferAttribute(new Uint16Array(indices),1)),this.addAttribute("position",new THREE.BufferAttribute(positions,3)),this.addAttribute("normal",new THREE.BufferAttribute(normals,3)),this.addAttribute("uv",new THREE.BufferAttribute(uvs,2)),this.boundingSphere=new THREE.Sphere(new THREE.Vector3,radius)},THREE.CircleBufferGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.CircleBufferGeometry.prototype.constructor=THREE.CircleBufferGeometry,THREE.CircleBufferGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.CircleBufferGeometry(parameters.radius,parameters.segments,parameters.thetaStart,parameters.thetaLength)},THREE.CylinderGeometry=function(radiusTop,radiusBottom,height,radialSegments,heightSegments,openEnded,thetaStart,thetaLength){THREE.Geometry.call(this),this.type="CylinderGeometry",this.parameters={radiusTop:radiusTop,radiusBottom:radiusBottom,height:height,radialSegments:radialSegments,heightSegments:heightSegments,openEnded:openEnded,thetaStart:thetaStart,thetaLength:thetaLength},radiusTop=void 0!==radiusTop?radiusTop:20,radiusBottom=void 0!==radiusBottom?radiusBottom:20,height=void 0!==height?height:100,radialSegments=radialSegments||8,heightSegments=heightSegments||1,openEnded=void 0!==openEnded?openEnded:!1,thetaStart=void 0!==thetaStart?thetaStart:0,thetaLength=void 0!==thetaLength?thetaLength:2*Math.PI;var x,y,heightHalf=height/2,vertices=[],uvs=[];for(y=0;heightSegments>=y;y++){var verticesRow=[],uvsRow=[],v=y/heightSegments,radius=v*(radiusBottom-radiusTop)+radiusTop;for(x=0;radialSegments>=x;x++){var u=x/radialSegments,vertex=new THREE.Vector3;vertex.x=radius*Math.sin(u*thetaLength+thetaStart),vertex.y=-v*height+heightHalf,vertex.z=radius*Math.cos(u*thetaLength+thetaStart),this.vertices.push(vertex),verticesRow.push(this.vertices.length-1),uvsRow.push(new THREE.Vector2(u,1-v))}vertices.push(verticesRow),uvs.push(uvsRow)}var na,nb,tanTheta=(radiusBottom-radiusTop)/height;for(x=0;radialSegments>x;x++)for(0!==radiusTop?(na=this.vertices[vertices[0][x]].clone(),nb=this.vertices[vertices[0][x+1]].clone()):(na=this.vertices[vertices[1][x]].clone(),nb=this.vertices[vertices[1][x+1]].clone()),na.setY(Math.sqrt(na.x*na.x+na.z*na.z)*tanTheta).normalize(),nb.setY(Math.sqrt(nb.x*nb.x+nb.z*nb.z)*tanTheta).normalize(),y=0;heightSegments>y;y++){var v1=vertices[y][x],v2=vertices[y+1][x],v3=vertices[y+1][x+1],v4=vertices[y][x+1],n1=na.clone(),n2=na.clone(),n3=nb.clone(),n4=nb.clone(),uv1=uvs[y][x].clone(),uv2=uvs[y+1][x].clone(),uv3=uvs[y+1][x+1].clone(),uv4=uvs[y][x+1].clone();this.faces.push(new THREE.Face3(v1,v2,v4,[n1,n2,n4])),this.faceVertexUvs[0].push([uv1,uv2,uv4]),this.faces.push(new THREE.Face3(v2,v3,v4,[n2.clone(),n3,n4.clone()])),this.faceVertexUvs[0].push([uv2.clone(),uv3,uv4.clone()])}if(openEnded===!1&&radiusTop>0)for(this.vertices.push(new THREE.Vector3(0,heightHalf,0)),x=0;radialSegments>x;x++){var v1=vertices[0][x],v2=vertices[0][x+1],v3=this.vertices.length-1,n1=new THREE.Vector3(0,1,0),n2=new THREE.Vector3(0,1,0),n3=new THREE.Vector3(0,1,0),uv1=uvs[0][x].clone(),uv2=uvs[0][x+1].clone(),uv3=new THREE.Vector2(uv2.x,0);this.faces.push(new THREE.Face3(v1,v2,v3,[n1,n2,n3],void 0,1)),this.faceVertexUvs[0].push([uv1,uv2,uv3])}if(openEnded===!1&&radiusBottom>0)for(this.vertices.push(new THREE.Vector3(0,-heightHalf,0)),x=0;radialSegments>x;x++){var v1=vertices[heightSegments][x+1],v2=vertices[heightSegments][x],v3=this.vertices.length-1,n1=new THREE.Vector3(0,-1,0),n2=new THREE.Vector3(0,-1,0),n3=new THREE.Vector3(0,-1,0),uv1=uvs[heightSegments][x+1].clone(),uv2=uvs[heightSegments][x].clone(),uv3=new THREE.Vector2(uv2.x,1);this.faces.push(new THREE.Face3(v1,v2,v3,[n1,n2,n3],void 0,2)),this.faceVertexUvs[0].push([uv1,uv2,uv3])}this.computeFaceNormals()},THREE.CylinderGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.CylinderGeometry.prototype.constructor=THREE.CylinderGeometry,THREE.CylinderGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.CylinderGeometry(parameters.radiusTop,parameters.radiusBottom,parameters.height,parameters.radialSegments,parameters.heightSegments,parameters.openEnded,parameters.thetaStart,parameters.thetaLength)},THREE.EdgesGeometry=function(geometry,thresholdAngle){function sortFunction(a,b){return a-b}THREE.BufferGeometry.call(this),thresholdAngle=void 0!==thresholdAngle?thresholdAngle:1;var geometry2,thresholdDot=Math.cos(THREE.Math.degToRad(thresholdAngle)),edge=[0,0],hash={},keys=["a","b","c"];geometry instanceof THREE.BufferGeometry?(geometry2=new THREE.Geometry,geometry2.fromBufferGeometry(geometry)):geometry2=geometry.clone(),geometry2.mergeVertices(),geometry2.computeFaceNormals();for(var vertices=geometry2.vertices,faces=geometry2.faces,i=0,l=faces.length;l>i;i++)for(var face=faces[i],j=0;3>j;j++){edge[0]=face[keys[j]],edge[1]=face[keys[(j+1)%3]],edge.sort(sortFunction);var key=edge.toString();void 0===hash[key]?hash[key]={vert1:edge[0],vert2:edge[1],face1:i,face2:void 0}:hash[key].face2=i}var coords=[];for(var key in hash){var h=hash[key];if(void 0===h.face2||faces[h.face1].normal.dot(faces[h.face2].normal)<=thresholdDot){var vertex=vertices[h.vert1];coords.push(vertex.x),coords.push(vertex.y),coords.push(vertex.z),vertex=vertices[h.vert2],coords.push(vertex.x),coords.push(vertex.y),coords.push(vertex.z)}}this.addAttribute("position",new THREE.BufferAttribute(new Float32Array(coords),3))},THREE.EdgesGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.EdgesGeometry.prototype.constructor=THREE.EdgesGeometry,THREE.ExtrudeGeometry=function(shapes,options){return"undefined"==typeof shapes?void(shapes=[]):(THREE.Geometry.call(this),this.type="ExtrudeGeometry",shapes=Array.isArray(shapes)?shapes:[shapes],this.addShapeList(shapes,options),void this.computeFaceNormals())},THREE.ExtrudeGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.ExtrudeGeometry.prototype.constructor=THREE.ExtrudeGeometry,THREE.ExtrudeGeometry.prototype.addShapeList=function(shapes,options){for(var sl=shapes.length,s=0;sl>s;s++){var shape=shapes[s];this.addShape(shape,options)}},THREE.ExtrudeGeometry.prototype.addShape=function(shape,options){function scalePt2(pt,vec,size){return vec||console.error("THREE.ExtrudeGeometry: vec does not exist"),vec.clone().multiplyScalar(size).add(pt)}function getBevelVec(inPt,inPrev,inNext){var v_trans_x,v_trans_y,shrink_by=1,v_prev_x=inPt.x-inPrev.x,v_prev_y=inPt.y-inPrev.y,v_next_x=inNext.x-inPt.x,v_next_y=inNext.y-inPt.y,v_prev_lensq=v_prev_x*v_prev_x+v_prev_y*v_prev_y,collinear0=v_prev_x*v_next_y-v_prev_y*v_next_x;if(Math.abs(collinear0)>Number.EPSILON){var v_prev_len=Math.sqrt(v_prev_lensq),v_next_len=Math.sqrt(v_next_x*v_next_x+v_next_y*v_next_y),ptPrevShift_x=inPrev.x-v_prev_y/v_prev_len,ptPrevShift_y=inPrev.y+v_prev_x/v_prev_len,ptNextShift_x=inNext.x-v_next_y/v_next_len,ptNextShift_y=inNext.y+v_next_x/v_next_len,sf=((ptNextShift_x-ptPrevShift_x)*v_next_y-(ptNextShift_y-ptPrevShift_y)*v_next_x)/(v_prev_x*v_next_y-v_prev_y*v_next_x);v_trans_x=ptPrevShift_x+v_prev_x*sf-inPt.x,v_trans_y=ptPrevShift_y+v_prev_y*sf-inPt.y;var v_trans_lensq=v_trans_x*v_trans_x+v_trans_y*v_trans_y;if(2>=v_trans_lensq)return new THREE.Vector2(v_trans_x,v_trans_y);shrink_by=Math.sqrt(v_trans_lensq/2)}else{var direction_eq=!1;v_prev_x>Number.EPSILON?v_next_x>Number.EPSILON&&(direction_eq=!0):v_prev_x<-Number.EPSILON?v_next_x<-Number.EPSILON&&(direction_eq=!0):Math.sign(v_prev_y)===Math.sign(v_next_y)&&(direction_eq=!0),direction_eq?(v_trans_x=-v_prev_y,v_trans_y=v_prev_x,shrink_by=Math.sqrt(v_prev_lensq)):(v_trans_x=v_prev_x,v_trans_y=v_prev_y,shrink_by=Math.sqrt(v_prev_lensq/2))}return new THREE.Vector2(v_trans_x/shrink_by,v_trans_y/shrink_by)}function buildLidFaces(){if(bevelEnabled){var layer=0,offset=vlen*layer;for(i=0;flen>i;i++)face=faces[i],f3(face[2]+offset,face[1]+offset,face[0]+offset);for(layer=steps+2*bevelSegments,offset=vlen*layer,i=0;flen>i;i++)face=faces[i],f3(face[0]+offset,face[1]+offset,face[2]+offset)}else{for(i=0;flen>i;i++)face=faces[i],f3(face[2],face[1],face[0]);for(i=0;flen>i;i++)face=faces[i],f3(face[0]+vlen*steps,face[1]+vlen*steps,face[2]+vlen*steps)}}function buildSideFaces(){var layeroffset=0;for(sidewalls(contour,layeroffset),layeroffset+=contour.length,h=0,hl=holes.length;hl>h;h++)ahole=holes[h],sidewalls(ahole,layeroffset),layeroffset+=ahole.length}function sidewalls(contour,layeroffset){var j,k;for(i=contour.length;--i>=0;){j=i,k=i-1,0>k&&(k=contour.length-1);var s=0,sl=steps+2*bevelSegments;for(s=0;sl>s;s++){var slen1=vlen*s,slen2=vlen*(s+1),a=layeroffset+j+slen1,b=layeroffset+k+slen1,c=layeroffset+k+slen2,d=layeroffset+j+slen2;f4(a,b,c,d,contour,s,sl,j,k)}}}function v(x,y,z){scope.vertices.push(new THREE.Vector3(x,y,z))}function f3(a,b,c){a+=shapesOffset,b+=shapesOffset,c+=shapesOffset,scope.faces.push(new THREE.Face3(a,b,c,null,null,0));var uvs=uvgen.generateTopUV(scope,a,b,c);scope.faceVertexUvs[0].push(uvs)}function f4(a,b,c,d,wallContour,stepIndex,stepsLength,contourIndex1,contourIndex2){a+=shapesOffset,b+=shapesOffset,c+=shapesOffset,d+=shapesOffset,scope.faces.push(new THREE.Face3(a,b,d,null,null,1)),scope.faces.push(new THREE.Face3(b,c,d,null,null,1));var uvs=uvgen.generateSideWallUV(scope,a,b,c,d);scope.faceVertexUvs[0].push([uvs[0],uvs[1],uvs[3]]),scope.faceVertexUvs[0].push([uvs[1],uvs[2],uvs[3]])}var extrudePts,splineTube,binormal,normal,position2,amount=void 0!==options.amount?options.amount:100,bevelThickness=void 0!==options.bevelThickness?options.bevelThickness:6,bevelSize=void 0!==options.bevelSize?options.bevelSize:bevelThickness-2,bevelSegments=void 0!==options.bevelSegments?options.bevelSegments:3,bevelEnabled=void 0!==options.bevelEnabled?options.bevelEnabled:!0,curveSegments=void 0!==options.curveSegments?options.curveSegments:12,steps=void 0!==options.steps?options.steps:1,extrudePath=options.extrudePath,extrudeByPath=!1,uvgen=void 0!==options.UVGenerator?options.UVGenerator:THREE.ExtrudeGeometry.WorldUVGenerator;extrudePath&&(extrudePts=extrudePath.getSpacedPoints(steps),extrudeByPath=!0,bevelEnabled=!1,splineTube=void 0!==options.frames?options.frames:new THREE.TubeGeometry.FrenetFrames(extrudePath,steps,!1),binormal=new THREE.Vector3,normal=new THREE.Vector3,position2=new THREE.Vector3),bevelEnabled||(bevelSegments=0,bevelThickness=0,bevelSize=0);var ahole,h,hl,scope=this,shapesOffset=this.vertices.length,shapePoints=shape.extractPoints(curveSegments),vertices=shapePoints.shape,holes=shapePoints.holes,reverse=!THREE.ShapeUtils.isClockWise(vertices);if(reverse){for(vertices=vertices.reverse(),h=0,hl=holes.length;hl>h;h++)ahole=holes[h],THREE.ShapeUtils.isClockWise(ahole)&&(holes[h]=ahole.reverse());reverse=!1}var faces=THREE.ShapeUtils.triangulateShape(vertices,holes),contour=vertices;for(h=0,hl=holes.length;hl>h;h++)ahole=holes[h],vertices=vertices.concat(ahole);for(var b,bs,t,z,vert,face,vlen=vertices.length,flen=faces.length,contourMovements=[],i=0,il=contour.length,j=il-1,k=i+1;il>i;i++,j++,k++)j===il&&(j=0),k===il&&(k=0),contourMovements[i]=getBevelVec(contour[i],contour[j],contour[k]);var oneHoleMovements,holesMovements=[],verticesMovements=contourMovements.concat();for(h=0,hl=holes.length;hl>h;h++){for(ahole=holes[h],oneHoleMovements=[],i=0,il=ahole.length,j=il-1,k=i+1;il>i;i++,j++,k++)j===il&&(j=0),k===il&&(k=0),oneHoleMovements[i]=getBevelVec(ahole[i],ahole[j],ahole[k]);holesMovements.push(oneHoleMovements),verticesMovements=verticesMovements.concat(oneHoleMovements)}for(b=0;bevelSegments>b;b++){for(t=b/bevelSegments,z=bevelThickness*(1-t),bs=bevelSize*Math.sin(t*Math.PI/2),i=0,il=contour.length;il>i;i++)vert=scalePt2(contour[i],contourMovements[i],bs),v(vert.x,vert.y,-z);for(h=0,hl=holes.length;hl>h;h++)for(ahole=holes[h],oneHoleMovements=holesMovements[h],i=0,il=ahole.length;il>i;i++)vert=scalePt2(ahole[i],oneHoleMovements[i],bs),v(vert.x,vert.y,-z)}for(bs=bevelSize,i=0;vlen>i;i++)vert=bevelEnabled?scalePt2(vertices[i],verticesMovements[i],bs):vertices[i],extrudeByPath?(normal.copy(splineTube.normals[0]).multiplyScalar(vert.x),binormal.copy(splineTube.binormals[0]).multiplyScalar(vert.y),position2.copy(extrudePts[0]).add(normal).add(binormal),v(position2.x,position2.y,position2.z)):v(vert.x,vert.y,0);var s;for(s=1;steps>=s;s++)for(i=0;vlen>i;i++)vert=bevelEnabled?scalePt2(vertices[i],verticesMovements[i],bs):vertices[i],extrudeByPath?(normal.copy(splineTube.normals[s]).multiplyScalar(vert.x),binormal.copy(splineTube.binormals[s]).multiplyScalar(vert.y),position2.copy(extrudePts[s]).add(normal).add(binormal),v(position2.x,position2.y,position2.z)):v(vert.x,vert.y,amount/steps*s);for(b=bevelSegments-1;b>=0;b--){for(t=b/bevelSegments,z=bevelThickness*(1-t),bs=bevelSize*Math.sin(t*Math.PI/2),i=0,il=contour.length;il>i;i++)vert=scalePt2(contour[i],contourMovements[i],bs),v(vert.x,vert.y,amount+z);for(h=0,hl=holes.length;hl>h;h++)for(ahole=holes[h],oneHoleMovements=holesMovements[h],i=0,il=ahole.length;il>i;i++)vert=scalePt2(ahole[i],oneHoleMovements[i],bs),extrudeByPath?v(vert.x,vert.y+extrudePts[steps-1].y,extrudePts[steps-1].x+z):v(vert.x,vert.y,amount+z)}buildLidFaces(),buildSideFaces()}, +THREE.ExtrudeGeometry.WorldUVGenerator={generateTopUV:function(geometry,indexA,indexB,indexC){var vertices=geometry.vertices,a=vertices[indexA],b=vertices[indexB],c=vertices[indexC];return[new THREE.Vector2(a.x,a.y),new THREE.Vector2(b.x,b.y),new THREE.Vector2(c.x,c.y)]},generateSideWallUV:function(geometry,indexA,indexB,indexC,indexD){var vertices=geometry.vertices,a=vertices[indexA],b=vertices[indexB],c=vertices[indexC],d=vertices[indexD];return Math.abs(a.y-b.y)<.01?[new THREE.Vector2(a.x,1-a.z),new THREE.Vector2(b.x,1-b.z),new THREE.Vector2(c.x,1-c.z),new THREE.Vector2(d.x,1-d.z)]:[new THREE.Vector2(a.y,1-a.z),new THREE.Vector2(b.y,1-b.z),new THREE.Vector2(c.y,1-c.z),new THREE.Vector2(d.y,1-d.z)]}},THREE.ShapeGeometry=function(shapes,options){THREE.Geometry.call(this),this.type="ShapeGeometry",Array.isArray(shapes)===!1&&(shapes=[shapes]),this.addShapeList(shapes,options),this.computeFaceNormals()},THREE.ShapeGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.ShapeGeometry.prototype.constructor=THREE.ShapeGeometry,THREE.ShapeGeometry.prototype.addShapeList=function(shapes,options){for(var i=0,l=shapes.length;l>i;i++)this.addShape(shapes[i],options);return this},THREE.ShapeGeometry.prototype.addShape=function(shape,options){void 0===options&&(options={});var i,l,hole,curveSegments=void 0!==options.curveSegments?options.curveSegments:12,material=options.material,uvgen=void 0===options.UVGenerator?THREE.ExtrudeGeometry.WorldUVGenerator:options.UVGenerator,shapesOffset=this.vertices.length,shapePoints=shape.extractPoints(curveSegments),vertices=shapePoints.shape,holes=shapePoints.holes,reverse=!THREE.ShapeUtils.isClockWise(vertices);if(reverse){for(vertices=vertices.reverse(),i=0,l=holes.length;l>i;i++)hole=holes[i],THREE.ShapeUtils.isClockWise(hole)&&(holes[i]=hole.reverse());reverse=!1}var faces=THREE.ShapeUtils.triangulateShape(vertices,holes);for(i=0,l=holes.length;l>i;i++)hole=holes[i],vertices=vertices.concat(hole);var vert,face,vlen=vertices.length,flen=faces.length;for(i=0;vlen>i;i++)vert=vertices[i],this.vertices.push(new THREE.Vector3(vert.x,vert.y,0));for(i=0;flen>i;i++){face=faces[i];var a=face[0]+shapesOffset,b=face[1]+shapesOffset,c=face[2]+shapesOffset;this.faces.push(new THREE.Face3(a,b,c,null,null,material)),this.faceVertexUvs[0].push(uvgen.generateTopUV(this,a,b,c))}},THREE.LatheGeometry=function(points,segments,phiStart,phiLength){THREE.Geometry.call(this),this.type="LatheGeometry",this.parameters={points:points,segments:segments,phiStart:phiStart,phiLength:phiLength},segments=segments||12,phiStart=phiStart||0,phiLength=phiLength||2*Math.PI;for(var inversePointLength=1/(points.length-1),inverseSegments=1/segments,i=0,il=segments;il>=i;i++)for(var phi=phiStart+i*inverseSegments*phiLength,c=Math.cos(phi),s=Math.sin(phi),j=0,jl=points.length;jl>j;j++){var pt=points[j],vertex=new THREE.Vector3;vertex.x=c*pt.x-s*pt.y,vertex.y=s*pt.x+c*pt.y,vertex.z=pt.z,this.vertices.push(vertex)}for(var np=points.length,i=0,il=segments;il>i;i++)for(var j=0,jl=points.length-1;jl>j;j++){var base=j+np*i,a=base,b=base+np,c=base+1+np,d=base+1,u0=i*inverseSegments,v0=j*inversePointLength,u1=u0+inverseSegments,v1=v0+inversePointLength;this.faces.push(new THREE.Face3(a,b,d)),this.faceVertexUvs[0].push([new THREE.Vector2(u0,v0),new THREE.Vector2(u1,v0),new THREE.Vector2(u0,v1)]),this.faces.push(new THREE.Face3(b,c,d)),this.faceVertexUvs[0].push([new THREE.Vector2(u1,v0),new THREE.Vector2(u1,v1),new THREE.Vector2(u0,v1)])}this.mergeVertices(),this.computeFaceNormals(),this.computeVertexNormals()},THREE.LatheGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.LatheGeometry.prototype.constructor=THREE.LatheGeometry,THREE.PlaneGeometry=function(width,height,widthSegments,heightSegments){THREE.Geometry.call(this),this.type="PlaneGeometry",this.parameters={width:width,height:height,widthSegments:widthSegments,heightSegments:heightSegments},this.fromBufferGeometry(new THREE.PlaneBufferGeometry(width,height,widthSegments,heightSegments))},THREE.PlaneGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.PlaneGeometry.prototype.constructor=THREE.PlaneGeometry,THREE.PlaneGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.PlaneGeometry(parameters.width,parameters.height,parameters.widthSegments,parameters.heightSegments)},THREE.PlaneBufferGeometry=function(width,height,widthSegments,heightSegments){THREE.BufferGeometry.call(this),this.type="PlaneBufferGeometry",this.parameters={width:width,height:height,widthSegments:widthSegments,heightSegments:heightSegments};for(var width_half=width/2,height_half=height/2,gridX=Math.floor(widthSegments)||1,gridY=Math.floor(heightSegments)||1,gridX1=gridX+1,gridY1=gridY+1,segment_width=width/gridX,segment_height=height/gridY,vertices=new Float32Array(gridX1*gridY1*3),normals=new Float32Array(gridX1*gridY1*3),uvs=new Float32Array(gridX1*gridY1*2),offset=0,offset2=0,iy=0;gridY1>iy;iy++)for(var y=iy*segment_height-height_half,ix=0;gridX1>ix;ix++){var x=ix*segment_width-width_half;vertices[offset]=x,vertices[offset+1]=-y,normals[offset+2]=1,uvs[offset2]=ix/gridX,uvs[offset2+1]=1-iy/gridY,offset+=3,offset2+=2}offset=0;for(var indices=new(vertices.length/3>65535?Uint32Array:Uint16Array)(gridX*gridY*6),iy=0;gridY>iy;iy++)for(var ix=0;gridX>ix;ix++){var a=ix+gridX1*iy,b=ix+gridX1*(iy+1),c=ix+1+gridX1*(iy+1),d=ix+1+gridX1*iy;indices[offset]=a,indices[offset+1]=b,indices[offset+2]=d,indices[offset+3]=b,indices[offset+4]=c,indices[offset+5]=d,offset+=6}this.setIndex(new THREE.BufferAttribute(indices,1)),this.addAttribute("position",new THREE.BufferAttribute(vertices,3)),this.addAttribute("normal",new THREE.BufferAttribute(normals,3)),this.addAttribute("uv",new THREE.BufferAttribute(uvs,2))},THREE.PlaneBufferGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.PlaneBufferGeometry.prototype.constructor=THREE.PlaneBufferGeometry,THREE.PlaneBufferGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.PlaneBufferGeometry(parameters.width,parameters.height,parameters.widthSegments,parameters.heightSegments)},THREE.RingGeometry=function(innerRadius,outerRadius,thetaSegments,phiSegments,thetaStart,thetaLength){THREE.Geometry.call(this),this.type="RingGeometry",this.parameters={innerRadius:innerRadius,outerRadius:outerRadius,thetaSegments:thetaSegments,phiSegments:phiSegments,thetaStart:thetaStart,thetaLength:thetaLength},innerRadius=innerRadius||0,outerRadius=outerRadius||50,thetaStart=void 0!==thetaStart?thetaStart:0,thetaLength=void 0!==thetaLength?thetaLength:2*Math.PI,thetaSegments=void 0!==thetaSegments?Math.max(3,thetaSegments):8,phiSegments=void 0!==phiSegments?Math.max(1,phiSegments):8;var i,o,uvs=[],radius=innerRadius,radiusStep=(outerRadius-innerRadius)/phiSegments;for(i=0;phiSegments+1>i;i++){for(o=0;thetaSegments+1>o;o++){var vertex=new THREE.Vector3,segment=thetaStart+o/thetaSegments*thetaLength;vertex.x=radius*Math.cos(segment),vertex.y=radius*Math.sin(segment),this.vertices.push(vertex),uvs.push(new THREE.Vector2((vertex.x/outerRadius+1)/2,(vertex.y/outerRadius+1)/2))}radius+=radiusStep}var n=new THREE.Vector3(0,0,1);for(i=0;phiSegments>i;i++){var thetaSegment=i*(thetaSegments+1);for(o=0;thetaSegments>o;o++){var segment=o+thetaSegment,v1=segment,v2=segment+thetaSegments+1,v3=segment+thetaSegments+2;this.faces.push(new THREE.Face3(v1,v2,v3,[n.clone(),n.clone(),n.clone()])),this.faceVertexUvs[0].push([uvs[v1].clone(),uvs[v2].clone(),uvs[v3].clone()]),v1=segment,v2=segment+thetaSegments+2,v3=segment+1,this.faces.push(new THREE.Face3(v1,v2,v3,[n.clone(),n.clone(),n.clone()])),this.faceVertexUvs[0].push([uvs[v1].clone(),uvs[v2].clone(),uvs[v3].clone()])}}this.computeFaceNormals(),this.boundingSphere=new THREE.Sphere(new THREE.Vector3,radius)},THREE.RingGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.RingGeometry.prototype.constructor=THREE.RingGeometry,THREE.RingGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.RingGeometry(parameters.innerRadius,parameters.outerRadius,parameters.thetaSegments,parameters.phiSegments,parameters.thetaStart,parameters.thetaLength)},THREE.SphereGeometry=function(radius,widthSegments,heightSegments,phiStart,phiLength,thetaStart,thetaLength){THREE.Geometry.call(this),this.type="SphereGeometry",this.parameters={radius:radius,widthSegments:widthSegments,heightSegments:heightSegments,phiStart:phiStart,phiLength:phiLength,thetaStart:thetaStart,thetaLength:thetaLength},this.fromBufferGeometry(new THREE.SphereBufferGeometry(radius,widthSegments,heightSegments,phiStart,phiLength,thetaStart,thetaLength))},THREE.SphereGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.SphereGeometry.prototype.constructor=THREE.SphereGeometry,THREE.SphereGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.SphereGeometry(parameters.radius,parameters.widthSegments,parameters.heightSegments,parameters.phiStart,parameters.phiLength,parameters.thetaStart,parameters.thetaLength)},THREE.SphereBufferGeometry=function(radius,widthSegments,heightSegments,phiStart,phiLength,thetaStart,thetaLength){THREE.BufferGeometry.call(this),this.type="SphereBufferGeometry",this.parameters={radius:radius,widthSegments:widthSegments,heightSegments:heightSegments,phiStart:phiStart,phiLength:phiLength,thetaStart:thetaStart,thetaLength:thetaLength},radius=radius||50,widthSegments=Math.max(3,Math.floor(widthSegments)||8),heightSegments=Math.max(2,Math.floor(heightSegments)||6),phiStart=void 0!==phiStart?phiStart:0,phiLength=void 0!==phiLength?phiLength:2*Math.PI,thetaStart=void 0!==thetaStart?thetaStart:0,thetaLength=void 0!==thetaLength?thetaLength:Math.PI;for(var thetaEnd=thetaStart+thetaLength,vertexCount=(widthSegments+1)*(heightSegments+1),positions=new THREE.BufferAttribute(new Float32Array(3*vertexCount),3),normals=new THREE.BufferAttribute(new Float32Array(3*vertexCount),3),uvs=new THREE.BufferAttribute(new Float32Array(2*vertexCount),2),index=0,vertices=[],normal=new THREE.Vector3,y=0;heightSegments>=y;y++){for(var verticesRow=[],v=y/heightSegments,x=0;widthSegments>=x;x++){var u=x/widthSegments,px=-radius*Math.cos(phiStart+u*phiLength)*Math.sin(thetaStart+v*thetaLength),py=radius*Math.cos(thetaStart+v*thetaLength),pz=radius*Math.sin(phiStart+u*phiLength)*Math.sin(thetaStart+v*thetaLength);normal.set(px,py,pz).normalize(),positions.setXYZ(index,px,py,pz),normals.setXYZ(index,normal.x,normal.y,normal.z),uvs.setXY(index,u,1-v),verticesRow.push(index),index++}vertices.push(verticesRow)}for(var indices=[],y=0;heightSegments>y;y++)for(var x=0;widthSegments>x;x++){var v1=vertices[y][x+1],v2=vertices[y][x],v3=vertices[y+1][x],v4=vertices[y+1][x+1];(0!==y||thetaStart>0)&&indices.push(v1,v2,v4),(y!==heightSegments-1||thetaEnd65535?THREE.Uint32Attribute:THREE.Uint16Attribute)(indices,1)),this.addAttribute("position",positions),this.addAttribute("normal",normals),this.addAttribute("uv",uvs),this.boundingSphere=new THREE.Sphere(new THREE.Vector3,radius)},THREE.SphereBufferGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.SphereBufferGeometry.prototype.constructor=THREE.SphereBufferGeometry,THREE.SphereBufferGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.SphereBufferGeometry(parameters.radius,parameters.widthSegments,parameters.heightSegments,parameters.phiStart,parameters.phiLength,parameters.thetaStart,parameters.thetaLength)},THREE.TorusGeometry=function(radius,tube,radialSegments,tubularSegments,arc){THREE.Geometry.call(this),this.type="TorusGeometry",this.parameters={radius:radius,tube:tube,radialSegments:radialSegments,tubularSegments:tubularSegments,arc:arc},radius=radius||100,tube=tube||40,radialSegments=radialSegments||8,tubularSegments=tubularSegments||6,arc=arc||2*Math.PI;for(var center=new THREE.Vector3,uvs=[],normals=[],j=0;radialSegments>=j;j++)for(var i=0;tubularSegments>=i;i++){var u=i/tubularSegments*arc,v=j/radialSegments*Math.PI*2;center.x=radius*Math.cos(u),center.y=radius*Math.sin(u);var vertex=new THREE.Vector3;vertex.x=(radius+tube*Math.cos(v))*Math.cos(u),vertex.y=(radius+tube*Math.cos(v))*Math.sin(u),vertex.z=tube*Math.sin(v),this.vertices.push(vertex),uvs.push(new THREE.Vector2(i/tubularSegments,j/radialSegments)),normals.push(vertex.clone().sub(center).normalize())}for(var j=1;radialSegments>=j;j++)for(var i=1;tubularSegments>=i;i++){var a=(tubularSegments+1)*j+i-1,b=(tubularSegments+1)*(j-1)+i-1,c=(tubularSegments+1)*(j-1)+i,d=(tubularSegments+1)*j+i,face=new THREE.Face3(a,b,d,[normals[a].clone(),normals[b].clone(),normals[d].clone()]);this.faces.push(face),this.faceVertexUvs[0].push([uvs[a].clone(),uvs[b].clone(),uvs[d].clone()]),face=new THREE.Face3(b,c,d,[normals[b].clone(),normals[c].clone(),normals[d].clone()]),this.faces.push(face),this.faceVertexUvs[0].push([uvs[b].clone(),uvs[c].clone(),uvs[d].clone()])}this.computeFaceNormals()},THREE.TorusGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.TorusGeometry.prototype.constructor=THREE.TorusGeometry,THREE.TorusGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.TorusGeometry(parameters.radius,parameters.tube,parameters.radialSegments,parameters.tubularSegments,parameters.arc)},THREE.TorusKnotGeometry=function(radius,tube,radialSegments,tubularSegments,p,q,heightScale){function getPos(u,in_q,in_p,radius,heightScale){var cu=Math.cos(u),su=Math.sin(u),quOverP=in_q/in_p*u,cs=Math.cos(quOverP),tx=radius*(2+cs)*.5*cu,ty=radius*(2+cs)*su*.5,tz=heightScale*radius*Math.sin(quOverP)*.5;return new THREE.Vector3(tx,ty,tz)}THREE.Geometry.call(this),this.type="TorusKnotGeometry",this.parameters={radius:radius,tube:tube,radialSegments:radialSegments,tubularSegments:tubularSegments,p:p,q:q,heightScale:heightScale},radius=radius||100,tube=tube||40,radialSegments=radialSegments||64,tubularSegments=tubularSegments||8,p=p||2,q=q||3,heightScale=heightScale||1;for(var grid=new Array(radialSegments),tang=new THREE.Vector3,n=new THREE.Vector3,bitan=new THREE.Vector3,i=0;radialSegments>i;++i){grid[i]=new Array(tubularSegments);var u=i/radialSegments*2*p*Math.PI,p1=getPos(u,q,p,radius,heightScale),p2=getPos(u+.01,q,p,radius,heightScale);tang.subVectors(p2,p1),n.addVectors(p2,p1),bitan.crossVectors(tang,n),n.crossVectors(bitan,tang),bitan.normalize(),n.normalize();for(var j=0;tubularSegments>j;++j){var v=j/tubularSegments*2*Math.PI,cx=-tube*Math.cos(v),cy=tube*Math.sin(v),pos=new THREE.Vector3;pos.x=p1.x+cx*n.x+cy*bitan.x,pos.y=p1.y+cx*n.y+cy*bitan.y,pos.z=p1.z+cx*n.z+cy*bitan.z,grid[i][j]=this.vertices.push(pos)-1}}for(var i=0;radialSegments>i;++i)for(var j=0;tubularSegments>j;++j){var ip=(i+1)%radialSegments,jp=(j+1)%tubularSegments,a=grid[i][j],b=grid[ip][j],c=grid[ip][jp],d=grid[i][jp],uva=new THREE.Vector2(i/radialSegments,j/tubularSegments),uvb=new THREE.Vector2((i+1)/radialSegments,j/tubularSegments),uvc=new THREE.Vector2((i+1)/radialSegments,(j+1)/tubularSegments),uvd=new THREE.Vector2(i/radialSegments,(j+1)/tubularSegments);this.faces.push(new THREE.Face3(a,b,d)),this.faceVertexUvs[0].push([uva,uvb,uvd]),this.faces.push(new THREE.Face3(b,c,d)),this.faceVertexUvs[0].push([uvb.clone(),uvc,uvd.clone()])}this.computeFaceNormals(),this.computeVertexNormals()},THREE.TorusKnotGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.TorusKnotGeometry.prototype.constructor=THREE.TorusKnotGeometry,THREE.TorusKnotGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.TorusKnotGeometry(parameters.radius,parameters.tube,parameters.radialSegments,parameters.tubularSegments,parameters.p,parameters.q,parameters.heightScale)},THREE.TubeGeometry=function(path,segments,radius,radialSegments,closed,taper){function vert(x,y,z){return scope.vertices.push(new THREE.Vector3(x,y,z))-1}THREE.Geometry.call(this),this.type="TubeGeometry",this.parameters={path:path,segments:segments,radius:radius,radialSegments:radialSegments,closed:closed,taper:taper},segments=segments||64,radius=radius||1,radialSegments=radialSegments||8,closed=closed||!1,taper=taper||THREE.TubeGeometry.NoTaper;var tangent,normal,binormal,u,v,r,cx,cy,pos,i,j,ip,jp,a,b,c,d,uva,uvb,uvc,uvd,grid=[],scope=this,numpoints=segments+1,pos2=new THREE.Vector3,frames=new THREE.TubeGeometry.FrenetFrames(path,segments,closed),tangents=frames.tangents,normals=frames.normals,binormals=frames.binormals;for(this.tangents=tangents,this.normals=normals,this.binormals=binormals,i=0;numpoints>i;i++)for(grid[i]=[],u=i/(numpoints-1),pos=path.getPointAt(u),tangent=tangents[i],normal=normals[i],binormal=binormals[i],r=radius*taper(u),j=0;radialSegments>j;j++)v=j/radialSegments*2*Math.PI,cx=-r*Math.cos(v),cy=r*Math.sin(v),pos2.copy(pos),pos2.x+=cx*normal.x+cy*binormal.x,pos2.y+=cx*normal.y+cy*binormal.y,pos2.z+=cx*normal.z+cy*binormal.z,grid[i][j]=vert(pos2.x,pos2.y,pos2.z);for(i=0;segments>i;i++)for(j=0;radialSegments>j;j++)ip=closed?(i+1)%segments:i+1,jp=(j+1)%radialSegments,a=grid[i][j],b=grid[ip][j],c=grid[ip][jp],d=grid[i][jp],uva=new THREE.Vector2(i/segments,j/radialSegments),uvb=new THREE.Vector2((i+1)/segments,j/radialSegments),uvc=new THREE.Vector2((i+1)/segments,(j+1)/radialSegments),uvd=new THREE.Vector2(i/segments,(j+1)/radialSegments),this.faces.push(new THREE.Face3(a,b,d)),this.faceVertexUvs[0].push([uva,uvb,uvd]),this.faces.push(new THREE.Face3(b,c,d)),this.faceVertexUvs[0].push([uvb.clone(),uvc,uvd.clone()]);this.computeFaceNormals(),this.computeVertexNormals()},THREE.TubeGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.TubeGeometry.prototype.constructor=THREE.TubeGeometry,THREE.TubeGeometry.prototype.clone=function(){return new this.constructor(this.parameters.path,this.parameters.segments,this.parameters.radius,this.parameters.radialSegments,this.parameters.closed,this.parameters.taper)},THREE.TubeGeometry.NoTaper=function(u){return 1},THREE.TubeGeometry.SinusoidalTaper=function(u){return Math.sin(Math.PI*u)},THREE.TubeGeometry.FrenetFrames=function(path,segments,closed){function initialNormal3(){normals[0]=new THREE.Vector3,binormals[0]=new THREE.Vector3,smallest=Number.MAX_VALUE,tx=Math.abs(tangents[0].x),ty=Math.abs(tangents[0].y),tz=Math.abs(tangents[0].z),smallest>=tx&&(smallest=tx,normal.set(1,0,0)),smallest>=ty&&(smallest=ty,normal.set(0,1,0)),smallest>=tz&&normal.set(0,0,1),vec.crossVectors(tangents[0],normal).normalize(),normals[0].crossVectors(tangents[0],vec),binormals[0].crossVectors(tangents[0],normals[0])}var theta,smallest,tx,ty,tz,i,u,normal=new THREE.Vector3,tangents=[],normals=[],binormals=[],vec=new THREE.Vector3,mat=new THREE.Matrix4,numpoints=segments+1;for(this.tangents=tangents,this.normals=normals,this.binormals=binormals,i=0;numpoints>i;i++)u=i/(numpoints-1),tangents[i]=path.getTangentAt(u),tangents[i].normalize();for(initialNormal3(),i=1;numpoints>i;i++)normals[i]=normals[i-1].clone(),binormals[i]=binormals[i-1].clone(),vec.crossVectors(tangents[i-1],tangents[i]),vec.length()>Number.EPSILON&&(vec.normalize(),theta=Math.acos(THREE.Math.clamp(tangents[i-1].dot(tangents[i]),-1,1)),normals[i].applyMatrix4(mat.makeRotationAxis(vec,theta))),binormals[i].crossVectors(tangents[i],normals[i]);if(closed)for(theta=Math.acos(THREE.Math.clamp(normals[0].dot(normals[numpoints-1]),-1,1)),theta/=numpoints-1,tangents[0].dot(vec.crossVectors(normals[0],normals[numpoints-1]))>0&&(theta=-theta),i=1;numpoints>i;i++)normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i],theta*i)),binormals[i].crossVectors(tangents[i],normals[i])},THREE.PolyhedronGeometry=function(vertices,indices,radius,detail){function prepare(vector){var vertex=vector.normalize().clone();vertex.index=that.vertices.push(vertex)-1;var u=azimuth(vector)/2/Math.PI+.5,v=inclination(vector)/Math.PI+.5;return vertex.uv=new THREE.Vector2(u,1-v),vertex}function make(v1,v2,v3,materialIndex){var face=new THREE.Face3(v1.index,v2.index,v3.index,[v1.clone(),v2.clone(),v3.clone()],void 0,materialIndex);that.faces.push(face),centroid.copy(v1).add(v2).add(v3).divideScalar(3);var azi=azimuth(centroid);that.faceVertexUvs[0].push([correctUV(v1.uv,v1,azi),correctUV(v2.uv,v2,azi),correctUV(v3.uv,v3,azi)])}function subdivide(face,detail){for(var cols=Math.pow(2,detail),a=prepare(that.vertices[face.a]),b=prepare(that.vertices[face.b]),c=prepare(that.vertices[face.c]),v=[],materialIndex=face.materialIndex,i=0;cols>=i;i++){v[i]=[];for(var aj=prepare(a.clone().lerp(c,i/cols)),bj=prepare(b.clone().lerp(c,i/cols)),rows=cols-i,j=0;rows>=j;j++)0===j&&i===cols?v[i][j]=aj:v[i][j]=prepare(aj.clone().lerp(bj,j/rows))}for(var i=0;cols>i;i++)for(var j=0;2*(cols-i)-1>j;j++){var k=Math.floor(j/2);j%2===0?make(v[i][k+1],v[i+1][k],v[i][k],materialIndex):make(v[i][k+1],v[i+1][k+1],v[i+1][k],materialIndex)}}function azimuth(vector){return Math.atan2(vector.z,-vector.x)}function inclination(vector){return Math.atan2(-vector.y,Math.sqrt(vector.x*vector.x+vector.z*vector.z))}function correctUV(uv,vector,azimuth){return 0>azimuth&&1===uv.x&&(uv=new THREE.Vector2(uv.x-1,uv.y)),0===vector.x&&0===vector.z&&(uv=new THREE.Vector2(azimuth/2/Math.PI+.5,uv.y)),uv.clone()}THREE.Geometry.call(this),this.type="PolyhedronGeometry",this.parameters={vertices:vertices,indices:indices,radius:radius,detail:detail},radius=radius||1,detail=detail||0;for(var that=this,i=0,l=vertices.length;l>i;i+=3)prepare(new THREE.Vector3(vertices[i],vertices[i+1],vertices[i+2]));for(var p=this.vertices,faces=[],i=0,j=0,l=indices.length;l>i;i+=3,j++){var v1=p[indices[i]],v2=p[indices[i+1]],v3=p[indices[i+2]];faces[j]=new THREE.Face3(v1.index,v2.index,v3.index,[v1.clone(),v2.clone(),v3.clone()],void 0,j)}for(var centroid=new THREE.Vector3,i=0,l=faces.length;l>i;i++)subdivide(faces[i],detail);for(var i=0,l=this.faceVertexUvs[0].length;l>i;i++){var uvs=this.faceVertexUvs[0][i],x0=uvs[0].x,x1=uvs[1].x,x2=uvs[2].x,max=Math.max(x0,x1,x2),min=Math.min(x0,x1,x2);max>.9&&.1>min&&(.2>x0&&(uvs[0].x+=1),.2>x1&&(uvs[1].x+=1),.2>x2&&(uvs[2].x+=1))}for(var i=0,l=this.vertices.length;l>i;i++)this.vertices[i].multiplyScalar(radius);this.mergeVertices(),this.computeFaceNormals(),this.boundingSphere=new THREE.Sphere(new THREE.Vector3,radius)},THREE.PolyhedronGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.PolyhedronGeometry.prototype.constructor=THREE.PolyhedronGeometry,THREE.PolyhedronGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.PolyhedronGeometry(parameters.vertices,parameters.indices,parameters.radius,parameters.detail)},THREE.DodecahedronGeometry=function(radius,detail){var t=(1+Math.sqrt(5))/2,r=1/t,vertices=[-1,-1,-1,-1,-1,1,-1,1,-1,-1,1,1,1,-1,-1,1,-1,1,1,1,-1,1,1,1,0,-r,-t,0,-r,t,0,r,-t,0,r,t,-r,-t,0,-r,t,0,r,-t,0,r,t,0,-t,0,-r,t,0,-r,-t,0,r,t,0,r],indices=[3,11,7,3,7,15,3,15,13,7,19,17,7,17,6,7,6,15,17,4,8,17,8,10,17,10,6,8,0,16,8,16,2,8,2,10,0,12,1,0,1,18,0,18,16,6,10,2,6,2,13,6,13,15,2,16,18,2,18,3,2,3,13,18,1,9,18,9,11,18,11,3,4,14,12,4,12,0,4,0,8,11,9,5,11,5,19,11,19,7,19,5,14,19,14,4,19,4,17,1,12,14,1,14,5,1,5,9];THREE.PolyhedronGeometry.call(this,vertices,indices,radius,detail),this.type="DodecahedronGeometry",this.parameters={radius:radius,detail:detail}},THREE.DodecahedronGeometry.prototype=Object.create(THREE.PolyhedronGeometry.prototype),THREE.DodecahedronGeometry.prototype.constructor=THREE.DodecahedronGeometry,THREE.DodecahedronGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.DodecahedronGeometry(parameters.radius,parameters.detail)},THREE.IcosahedronGeometry=function(radius,detail){var t=(1+Math.sqrt(5))/2,vertices=[-1,t,0,1,t,0,-1,-t,0,1,-t,0,0,-1,t,0,1,t,0,-1,-t,0,1,-t,t,0,-1,t,0,1,-t,0,-1,-t,0,1],indices=[0,11,5,0,5,1,0,1,7,0,7,10,0,10,11,1,5,9,5,11,4,11,10,2,10,7,6,7,1,8,3,9,4,3,4,2,3,2,6,3,6,8,3,8,9,4,9,5,2,4,11,6,2,10,8,6,7,9,8,1];THREE.PolyhedronGeometry.call(this,vertices,indices,radius,detail),this.type="IcosahedronGeometry",this.parameters={radius:radius,detail:detail}},THREE.IcosahedronGeometry.prototype=Object.create(THREE.PolyhedronGeometry.prototype),THREE.IcosahedronGeometry.prototype.constructor=THREE.IcosahedronGeometry,THREE.IcosahedronGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.IcosahedronGeometry(parameters.radius,parameters.detail)},THREE.OctahedronGeometry=function(radius,detail){var vertices=[1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1],indices=[0,2,4,0,4,3,0,3,5,0,5,2,1,2,5,1,5,3,1,3,4,1,4,2];THREE.PolyhedronGeometry.call(this,vertices,indices,radius,detail),this.type="OctahedronGeometry",this.parameters={radius:radius,detail:detail}},THREE.OctahedronGeometry.prototype=Object.create(THREE.PolyhedronGeometry.prototype),THREE.OctahedronGeometry.prototype.constructor=THREE.OctahedronGeometry,THREE.OctahedronGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.OctahedronGeometry(parameters.radius,parameters.detail)},THREE.TetrahedronGeometry=function(radius,detail){var vertices=[1,1,1,-1,-1,1,-1,1,-1,1,-1,-1],indices=[2,1,0,0,3,2,1,3,0,2,3,1];THREE.PolyhedronGeometry.call(this,vertices,indices,radius,detail),this.type="TetrahedronGeometry",this.parameters={radius:radius,detail:detail}},THREE.TetrahedronGeometry.prototype=Object.create(THREE.PolyhedronGeometry.prototype),THREE.TetrahedronGeometry.prototype.constructor=THREE.TetrahedronGeometry,THREE.TetrahedronGeometry.prototype.clone=function(){var parameters=this.parameters;return new THREE.TetrahedronGeometry(parameters.radius,parameters.detail)},THREE.ParametricGeometry=function(func,slices,stacks){THREE.Geometry.call(this),this.type="ParametricGeometry",this.parameters={func:func,slices:slices,stacks:stacks};var i,j,p,u,v,verts=this.vertices,faces=this.faces,uvs=this.faceVertexUvs[0],sliceCount=slices+1;for(i=0;stacks>=i;i++)for(v=i/stacks,j=0;slices>=j;j++)u=j/slices,p=func(u,v),verts.push(p);var a,b,c,d,uva,uvb,uvc,uvd;for(i=0;stacks>i;i++)for(j=0;slices>j;j++)a=i*sliceCount+j,b=i*sliceCount+j+1,c=(i+1)*sliceCount+j+1,d=(i+1)*sliceCount+j,uva=new THREE.Vector2(j/slices,i/stacks),uvb=new THREE.Vector2((j+1)/slices,i/stacks),uvc=new THREE.Vector2((j+1)/slices,(i+1)/stacks),uvd=new THREE.Vector2(j/slices,(i+1)/stacks),faces.push(new THREE.Face3(a,b,d)),uvs.push([uva,uvb,uvd]),faces.push(new THREE.Face3(b,c,d)),uvs.push([uvb.clone(),uvc,uvd.clone()]);this.computeFaceNormals(),this.computeVertexNormals()},THREE.ParametricGeometry.prototype=Object.create(THREE.Geometry.prototype),THREE.ParametricGeometry.prototype.constructor=THREE.ParametricGeometry,THREE.WireframeGeometry=function(geometry){function sortFunction(a,b){return a-b}THREE.BufferGeometry.call(this);var edge=[0,0],hash={},keys=["a","b","c"];if(geometry instanceof THREE.Geometry){for(var vertices=geometry.vertices,faces=geometry.faces,numEdges=0,edges=new Uint32Array(6*faces.length),i=0,l=faces.length;l>i;i++)for(var face=faces[i],j=0;3>j;j++){edge[0]=face[keys[j]],edge[1]=face[keys[(j+1)%3]],edge.sort(sortFunction);var key=edge.toString();void 0===hash[key]&&(edges[2*numEdges]=edge[0],edges[2*numEdges+1]=edge[1],hash[key]=!0,numEdges++)}for(var coords=new Float32Array(2*numEdges*3),i=0,l=numEdges;l>i;i++)for(var j=0;2>j;j++){var vertex=vertices[edges[2*i+j]],index=6*i+3*j;coords[index+0]=vertex.x,coords[index+1]=vertex.y,coords[index+2]=vertex.z}this.addAttribute("position",new THREE.BufferAttribute(coords,3))}else if(geometry instanceof THREE.BufferGeometry)if(null!==geometry.index){var indices=geometry.index.array,vertices=geometry.attributes.position,drawcalls=geometry.drawcalls,numEdges=0;0===drawcalls.length&&geometry.addGroup(0,indices.length);for(var edges=new Uint32Array(2*indices.length),o=0,ol=drawcalls.length;ol>o;++o)for(var drawcall=drawcalls[o],start=drawcall.start,count=drawcall.count,i=start,il=start+count;il>i;i+=3)for(var j=0;3>j;j++){edge[0]=indices[i+j],edge[1]=indices[i+(j+1)%3],edge.sort(sortFunction);var key=edge.toString();void 0===hash[key]&&(edges[2*numEdges]=edge[0],edges[2*numEdges+1]=edge[1],hash[key]=!0,numEdges++)}for(var coords=new Float32Array(2*numEdges*3),i=0,l=numEdges;l>i;i++)for(var j=0;2>j;j++){var index=6*i+3*j,index2=edges[2*i+j];coords[index+0]=vertices.getX(index2),coords[index+1]=vertices.getY(index2),coords[index+2]=vertices.getZ(index2)}this.addAttribute("position",new THREE.BufferAttribute(coords,3))}else{for(var vertices=geometry.attributes.position.array,numEdges=vertices.length/3,numTris=numEdges/3,coords=new Float32Array(2*numEdges*3),i=0,l=numTris;l>i;i++)for(var j=0;3>j;j++){var index=18*i+6*j,index1=9*i+3*j;coords[index+0]=vertices[index1],coords[index+1]=vertices[index1+1],coords[index+2]=vertices[index1+2];var index2=9*i+3*((j+1)%3);coords[index+3]=vertices[index2],coords[index+4]=vertices[index2+1],coords[index+5]=vertices[index2+2]}this.addAttribute("position",new THREE.BufferAttribute(coords,3))}},THREE.WireframeGeometry.prototype=Object.create(THREE.BufferGeometry.prototype),THREE.WireframeGeometry.prototype.constructor=THREE.WireframeGeometry,THREE.AxisHelper=function(size){size=size||1;var vertices=new Float32Array([0,0,0,size,0,0,0,0,0,0,size,0,0,0,0,0,0,size]),colors=new Float32Array([1,0,0,1,.6,0,0,1,0,.6,1,0,0,0,1,0,.6,1]),geometry=new THREE.BufferGeometry;geometry.addAttribute("position",new THREE.BufferAttribute(vertices,3)),geometry.addAttribute("color",new THREE.BufferAttribute(colors,3));var material=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});THREE.LineSegments.call(this,geometry,material)},THREE.AxisHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.AxisHelper.prototype.constructor=THREE.AxisHelper,THREE.ArrowHelper=function(){var lineGeometry=new THREE.Geometry;lineGeometry.vertices.push(new THREE.Vector3(0,0,0),new THREE.Vector3(0,1,0));var coneGeometry=new THREE.CylinderGeometry(0,.5,1,5,1);return coneGeometry.translate(0,-.5,0),function(dir,origin,length,color,headLength,headWidth){THREE.Object3D.call(this),void 0===color&&(color=16776960),void 0===length&&(length=1),void 0===headLength&&(headLength=.2*length),void 0===headWidth&&(headWidth=.2*headLength),this.position.copy(origin),length>headLength&&(this.line=new THREE.Line(lineGeometry,new THREE.LineBasicMaterial({color:color})),this.line.matrixAutoUpdate=!1,this.add(this.line)),this.cone=new THREE.Mesh(coneGeometry,new THREE.MeshBasicMaterial({color:color})),this.cone.matrixAutoUpdate=!1,this.add(this.cone),this.setDirection(dir),this.setLength(length,headLength,headWidth)}}(),THREE.ArrowHelper.prototype=Object.create(THREE.Object3D.prototype),THREE.ArrowHelper.prototype.constructor=THREE.ArrowHelper,THREE.ArrowHelper.prototype.setDirection=function(){var radians,axis=new THREE.Vector3;return function(dir){dir.y>.99999?this.quaternion.set(0,0,0,1):dir.y<-.99999?this.quaternion.set(1,0,0,0):(axis.set(dir.z,0,-dir.x).normalize(),radians=Math.acos(dir.y),this.quaternion.setFromAxisAngle(axis,radians))}}(),THREE.ArrowHelper.prototype.setLength=function(length,headLength,headWidth){void 0===headLength&&(headLength=.2*length),void 0===headWidth&&(headWidth=.2*headLength),length>headLength&&(this.line.scale.set(1,length-headLength,1),this.line.updateMatrix()),this.cone.scale.set(headWidth,headLength,headWidth),this.cone.position.y=length,this.cone.updateMatrix()},THREE.ArrowHelper.prototype.setColor=function(color){void 0!==this.line&&this.line.material.color.set(color),this.cone.material.color.set(color)},THREE.BoxHelper=function(object){var indices=new Uint16Array([0,1,1,2,2,3,3,0,4,5,5,6,6,7,7,4,0,4,1,5,2,6,3,7]),positions=new Float32Array(24),geometry=new THREE.BufferGeometry;geometry.setIndex(new THREE.BufferAttribute(indices,1)),geometry.addAttribute("position",new THREE.BufferAttribute(positions,3)),THREE.LineSegments.call(this,geometry,new THREE.LineBasicMaterial({color:16776960})),void 0!==object&&this.update(object)},THREE.BoxHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.BoxHelper.prototype.constructor=THREE.BoxHelper, +THREE.BoxHelper.prototype.update=function(){var box=new THREE.Box3;return function(object){if(box.setFromObject(object),!box.empty()){var min=box.min,max=box.max,position=this.geometry.attributes.position,array=position.array;array[0]=max.x,array[1]=max.y,array[2]=max.z,array[3]=min.x,array[4]=max.y,array[5]=max.z,array[6]=min.x,array[7]=min.y,array[8]=max.z,array[9]=max.x,array[10]=min.y,array[11]=max.z,array[12]=max.x,array[13]=max.y,array[14]=min.z,array[15]=min.x,array[16]=max.y,array[17]=min.z,array[18]=min.x,array[19]=min.y,array[20]=min.z,array[21]=max.x,array[22]=min.y,array[23]=min.z,position.needsUpdate=!0,this.geometry.computeBoundingSphere()}}}(),THREE.BoundingBoxHelper=function(object,hex){var color=void 0!==hex?hex:8947848;this.object=object,this.box=new THREE.Box3,THREE.Mesh.call(this,new THREE.BoxGeometry(1,1,1),new THREE.MeshBasicMaterial({color:color,wireframe:!0}))},THREE.BoundingBoxHelper.prototype=Object.create(THREE.Mesh.prototype),THREE.BoundingBoxHelper.prototype.constructor=THREE.BoundingBoxHelper,THREE.BoundingBoxHelper.prototype.update=function(){this.box.setFromObject(this.object),this.box.size(this.scale),this.box.center(this.position)},THREE.CameraHelper=function(camera){function addLine(a,b,hex){addPoint(a,hex),addPoint(b,hex)}function addPoint(id,hex){geometry.vertices.push(new THREE.Vector3),geometry.colors.push(new THREE.Color(hex)),void 0===pointMap[id]&&(pointMap[id]=[]),pointMap[id].push(geometry.vertices.length-1)}var geometry=new THREE.Geometry,material=new THREE.LineBasicMaterial({color:16777215,vertexColors:THREE.FaceColors}),pointMap={},hexFrustum=16755200,hexCone=16711680,hexUp=43775,hexTarget=16777215,hexCross=3355443;addLine("n1","n2",hexFrustum),addLine("n2","n4",hexFrustum),addLine("n4","n3",hexFrustum),addLine("n3","n1",hexFrustum),addLine("f1","f2",hexFrustum),addLine("f2","f4",hexFrustum),addLine("f4","f3",hexFrustum),addLine("f3","f1",hexFrustum),addLine("n1","f1",hexFrustum),addLine("n2","f2",hexFrustum),addLine("n3","f3",hexFrustum),addLine("n4","f4",hexFrustum),addLine("p","n1",hexCone),addLine("p","n2",hexCone),addLine("p","n3",hexCone),addLine("p","n4",hexCone),addLine("u1","u2",hexUp),addLine("u2","u3",hexUp),addLine("u3","u1",hexUp),addLine("c","t",hexTarget),addLine("p","c",hexCross),addLine("cn1","cn2",hexCross),addLine("cn3","cn4",hexCross),addLine("cf1","cf2",hexCross),addLine("cf3","cf4",hexCross),THREE.LineSegments.call(this,geometry,material),this.camera=camera,this.camera.updateProjectionMatrix(),this.matrix=camera.matrixWorld,this.matrixAutoUpdate=!1,this.pointMap=pointMap,this.update()},THREE.CameraHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.CameraHelper.prototype.constructor=THREE.CameraHelper,THREE.CameraHelper.prototype.update=function(){function setPoint(point,x,y,z){vector.set(x,y,z).unproject(camera);var points=pointMap[point];if(void 0!==points)for(var i=0,il=points.length;il>i;i++)geometry.vertices[points[i]].copy(vector)}var geometry,pointMap,vector=new THREE.Vector3,camera=new THREE.Camera;return function(){geometry=this.geometry,pointMap=this.pointMap;var w=1,h=1;camera.projectionMatrix.copy(this.camera.projectionMatrix),setPoint("c",0,0,-1),setPoint("t",0,0,1),setPoint("n1",-w,-h,-1),setPoint("n2",w,-h,-1),setPoint("n3",-w,h,-1),setPoint("n4",w,h,-1),setPoint("f1",-w,-h,1),setPoint("f2",w,-h,1),setPoint("f3",-w,h,1),setPoint("f4",w,h,1),setPoint("u1",.7*w,1.1*h,-1),setPoint("u2",.7*-w,1.1*h,-1),setPoint("u3",0,2*h,-1),setPoint("cf1",-w,0,1),setPoint("cf2",w,0,1),setPoint("cf3",0,-h,1),setPoint("cf4",0,h,1),setPoint("cn1",-w,0,-1),setPoint("cn2",w,0,-1),setPoint("cn3",0,-h,-1),setPoint("cn4",0,h,-1),geometry.verticesNeedUpdate=!0}}(),THREE.DirectionalLightHelper=function(light,size){THREE.Object3D.call(this),this.light=light,this.light.updateMatrixWorld(),this.matrix=light.matrixWorld,this.matrixAutoUpdate=!1,size=size||1;var geometry=new THREE.Geometry;geometry.vertices.push(new THREE.Vector3(-size,size,0),new THREE.Vector3(size,size,0),new THREE.Vector3(size,-size,0),new THREE.Vector3(-size,-size,0),new THREE.Vector3(-size,size,0));var material=new THREE.LineBasicMaterial({fog:!1});material.color.copy(this.light.color).multiplyScalar(this.light.intensity),this.lightPlane=new THREE.Line(geometry,material),this.add(this.lightPlane),geometry=new THREE.Geometry,geometry.vertices.push(new THREE.Vector3,new THREE.Vector3),material=new THREE.LineBasicMaterial({fog:!1}),material.color.copy(this.light.color).multiplyScalar(this.light.intensity),this.targetLine=new THREE.Line(geometry,material),this.add(this.targetLine),this.update()},THREE.DirectionalLightHelper.prototype=Object.create(THREE.Object3D.prototype),THREE.DirectionalLightHelper.prototype.constructor=THREE.DirectionalLightHelper,THREE.DirectionalLightHelper.prototype.dispose=function(){this.lightPlane.geometry.dispose(),this.lightPlane.material.dispose(),this.targetLine.geometry.dispose(),this.targetLine.material.dispose()},THREE.DirectionalLightHelper.prototype.update=function(){var v1=new THREE.Vector3,v2=new THREE.Vector3,v3=new THREE.Vector3;return function(){v1.setFromMatrixPosition(this.light.matrixWorld),v2.setFromMatrixPosition(this.light.target.matrixWorld),v3.subVectors(v2,v1),this.lightPlane.lookAt(v3),this.lightPlane.material.color.copy(this.light.color).multiplyScalar(this.light.intensity),this.targetLine.geometry.vertices[1].copy(v3),this.targetLine.geometry.verticesNeedUpdate=!0,this.targetLine.material.color.copy(this.lightPlane.material.color)}}(),THREE.EdgesHelper=function(object,hex,thresholdAngle){var color=void 0!==hex?hex:16777215;THREE.LineSegments.call(this,new THREE.EdgesGeometry(object.geometry,thresholdAngle),new THREE.LineBasicMaterial({color:color})),this.matrix=object.matrixWorld,this.matrixAutoUpdate=!1},THREE.EdgesHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.EdgesHelper.prototype.constructor=THREE.EdgesHelper,THREE.FaceNormalsHelper=function(object,size,hex,linewidth){this.object=object,this.size=void 0!==size?size:1;var color=void 0!==hex?hex:16776960,width=void 0!==linewidth?linewidth:1,nNormals=0,objGeometry=this.object.geometry;objGeometry instanceof THREE.Geometry?nNormals=objGeometry.faces.length:console.warn("THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead.");var geometry=new THREE.BufferGeometry,positions=new THREE.Float32Attribute(2*nNormals*3,3);geometry.addAttribute("position",positions),THREE.LineSegments.call(this,geometry,new THREE.LineBasicMaterial({color:color,linewidth:width})),this.matrixAutoUpdate=!1,this.update()},THREE.FaceNormalsHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.FaceNormalsHelper.prototype.constructor=THREE.FaceNormalsHelper,THREE.FaceNormalsHelper.prototype.update=function(){var v1=new THREE.Vector3,v2=new THREE.Vector3,normalMatrix=new THREE.Matrix3;return function(){this.object.updateMatrixWorld(!0),normalMatrix.getNormalMatrix(this.object.matrixWorld);for(var matrixWorld=this.object.matrixWorld,position=this.geometry.attributes.position,objGeometry=this.object.geometry,vertices=objGeometry.vertices,faces=objGeometry.faces,idx=0,i=0,l=faces.length;l>i;i++){var face=faces[i],normal=face.normal;v1.copy(vertices[face.a]).add(vertices[face.b]).add(vertices[face.c]).divideScalar(3).applyMatrix4(matrixWorld),v2.copy(normal).applyMatrix3(normalMatrix).normalize().multiplyScalar(this.size).add(v1),position.setXYZ(idx,v1.x,v1.y,v1.z),idx+=1,position.setXYZ(idx,v2.x,v2.y,v2.z),idx+=1}return position.needsUpdate=!0,this}}(),THREE.GridHelper=function(size,step){var geometry=new THREE.Geometry,material=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});this.color1=new THREE.Color(4473924),this.color2=new THREE.Color(8947848);for(var i=-size;size>=i;i+=step){geometry.vertices.push(new THREE.Vector3(-size,0,i),new THREE.Vector3(size,0,i),new THREE.Vector3(i,0,-size),new THREE.Vector3(i,0,size));var color=0===i?this.color1:this.color2;geometry.colors.push(color,color,color,color)}THREE.LineSegments.call(this,geometry,material)},THREE.GridHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.GridHelper.prototype.constructor=THREE.GridHelper,THREE.GridHelper.prototype.setColors=function(colorCenterLine,colorGrid){this.color1.set(colorCenterLine),this.color2.set(colorGrid),this.geometry.colorsNeedUpdate=!0},THREE.HemisphereLightHelper=function(light,sphereSize){THREE.Object3D.call(this),this.light=light,this.light.updateMatrixWorld(),this.matrix=light.matrixWorld,this.matrixAutoUpdate=!1,this.colors=[new THREE.Color,new THREE.Color];var geometry=new THREE.SphereGeometry(sphereSize,4,2);geometry.rotateX(-Math.PI/2);for(var i=0,il=8;il>i;i++)geometry.faces[i].color=this.colors[4>i?0:1];var material=new THREE.MeshBasicMaterial({vertexColors:THREE.FaceColors,wireframe:!0});this.lightSphere=new THREE.Mesh(geometry,material),this.add(this.lightSphere),this.update()},THREE.HemisphereLightHelper.prototype=Object.create(THREE.Object3D.prototype),THREE.HemisphereLightHelper.prototype.constructor=THREE.HemisphereLightHelper,THREE.HemisphereLightHelper.prototype.dispose=function(){this.lightSphere.geometry.dispose(),this.lightSphere.material.dispose()},THREE.HemisphereLightHelper.prototype.update=function(){var vector=new THREE.Vector3;return function(){this.colors[0].copy(this.light.color).multiplyScalar(this.light.intensity),this.colors[1].copy(this.light.groundColor).multiplyScalar(this.light.intensity),this.lightSphere.lookAt(vector.setFromMatrixPosition(this.light.matrixWorld).negate()),this.lightSphere.geometry.colorsNeedUpdate=!0}}(),THREE.PointLightHelper=function(light,sphereSize){this.light=light,this.light.updateMatrixWorld();var geometry=new THREE.SphereGeometry(sphereSize,4,2),material=new THREE.MeshBasicMaterial({wireframe:!0,fog:!1});material.color.copy(this.light.color).multiplyScalar(this.light.intensity),THREE.Mesh.call(this,geometry,material),this.matrix=this.light.matrixWorld,this.matrixAutoUpdate=!1},THREE.PointLightHelper.prototype=Object.create(THREE.Mesh.prototype),THREE.PointLightHelper.prototype.constructor=THREE.PointLightHelper,THREE.PointLightHelper.prototype.dispose=function(){this.geometry.dispose(),this.material.dispose()},THREE.PointLightHelper.prototype.update=function(){this.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)},THREE.SkeletonHelper=function(object){this.bones=this.getBoneList(object);for(var geometry=new THREE.Geometry,i=0;ii;i++)for(var face=faces[i],j=0,jl=face.vertexNormals.length;jl>j;j++){var vertex=vertices[face[keys[j]]],normal=face.vertexNormals[j];v1.copy(vertex).applyMatrix4(matrixWorld),v2.copy(normal).applyMatrix3(normalMatrix).normalize().multiplyScalar(this.size).add(v1),position.setXYZ(idx,v1.x,v1.y,v1.z),idx+=1,position.setXYZ(idx,v2.x,v2.y,v2.z),idx+=1}else if(objGeometry instanceof THREE.BufferGeometry)for(var objPos=objGeometry.attributes.position,objNorm=objGeometry.attributes.normal,idx=0,j=0,jl=objPos.count;jl>j;j++)v1.set(objPos.getX(j),objPos.getY(j),objPos.getZ(j)).applyMatrix4(matrixWorld),v2.set(objNorm.getX(j),objNorm.getY(j),objNorm.getZ(j)),v2.applyMatrix3(normalMatrix).normalize().multiplyScalar(this.size).add(v1),position.setXYZ(idx,v1.x,v1.y,v1.z),idx+=1,position.setXYZ(idx,v2.x,v2.y,v2.z),idx+=1;return position.needsUpdate=!0,this}}(),THREE.WireframeHelper=function(object,hex){var color=void 0!==hex?hex:16777215;THREE.LineSegments.call(this,new THREE.WireframeGeometry(object.geometry),new THREE.LineBasicMaterial({color:color})),this.matrix=object.matrixWorld,this.matrixAutoUpdate=!1},THREE.WireframeHelper.prototype=Object.create(THREE.LineSegments.prototype),THREE.WireframeHelper.prototype.constructor=THREE.WireframeHelper,THREE.ImmediateRenderObject=function(material){THREE.Object3D.call(this),this.material=material,this.render=function(renderCallback){}},THREE.ImmediateRenderObject.prototype=Object.create(THREE.Object3D.prototype),THREE.ImmediateRenderObject.prototype.constructor=THREE.ImmediateRenderObject,THREE.MorphBlendMesh=function(geometry,material){THREE.Mesh.call(this,geometry,material),this.animationsMap={},this.animationsList=[];var numFrames=this.geometry.morphTargets.length,name="__default",startFrame=0,endFrame=numFrames-1,fps=numFrames/1;this.createAnimation(name,startFrame,endFrame,fps),this.setAnimationWeight(name,1)},THREE.MorphBlendMesh.prototype=Object.create(THREE.Mesh.prototype),THREE.MorphBlendMesh.prototype.constructor=THREE.MorphBlendMesh,THREE.MorphBlendMesh.prototype.createAnimation=function(name,start,end,fps){var animation={start:start,end:end,length:end-start+1,fps:fps,duration:(end-start)/fps,lastFrame:0,currentFrame:0,active:!1,time:0,direction:1,weight:1,directionBackwards:!1,mirroredLoop:!1};this.animationsMap[name]=animation,this.animationsList.push(animation)},THREE.MorphBlendMesh.prototype.autoCreateAnimations=function(fps){for(var firstAnimation,pattern=/([a-z]+)_?(\d+)/,frameRanges={},geometry=this.geometry,i=0,il=geometry.morphTargets.length;il>i;i++){var morph=geometry.morphTargets[i],chunks=morph.name.match(pattern);if(chunks&&chunks.length>1){var name=chunks[1];frameRanges[name]||(frameRanges[name]={start:1/0,end:-(1/0)});var range=frameRanges[name];irange.end&&(range.end=i),firstAnimation||(firstAnimation=name)}}for(var name in frameRanges){var range=frameRanges[name];this.createAnimation(name,range.start,range.end,fps)}this.firstAnimation=firstAnimation},THREE.MorphBlendMesh.prototype.setAnimationDirectionForward=function(name){var animation=this.animationsMap[name];animation&&(animation.direction=1,animation.directionBackwards=!1)},THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward=function(name){var animation=this.animationsMap[name];animation&&(animation.direction=-1,animation.directionBackwards=!0)},THREE.MorphBlendMesh.prototype.setAnimationFPS=function(name,fps){var animation=this.animationsMap[name];animation&&(animation.fps=fps,animation.duration=(animation.end-animation.start)/animation.fps)},THREE.MorphBlendMesh.prototype.setAnimationDuration=function(name,duration){var animation=this.animationsMap[name];animation&&(animation.duration=duration,animation.fps=(animation.end-animation.start)/animation.duration)},THREE.MorphBlendMesh.prototype.setAnimationWeight=function(name,weight){var animation=this.animationsMap[name];animation&&(animation.weight=weight)},THREE.MorphBlendMesh.prototype.setAnimationTime=function(name,time){var animation=this.animationsMap[name];animation&&(animation.time=time)},THREE.MorphBlendMesh.prototype.getAnimationTime=function(name){var time=0,animation=this.animationsMap[name];return animation&&(time=animation.time),time},THREE.MorphBlendMesh.prototype.getAnimationDuration=function(name){var duration=-1,animation=this.animationsMap[name];return animation&&(duration=animation.duration),duration},THREE.MorphBlendMesh.prototype.playAnimation=function(name){var animation=this.animationsMap[name];animation?(animation.time=0,animation.active=!0):console.warn("THREE.MorphBlendMesh: animation["+name+"] undefined in .playAnimation()")},THREE.MorphBlendMesh.prototype.stopAnimation=function(name){var animation=this.animationsMap[name];animation&&(animation.active=!1)},THREE.MorphBlendMesh.prototype.update=function(delta){for(var i=0,il=this.animationsList.length;il>i;i++){var animation=this.animationsList[i];if(animation.active){var frameTime=animation.duration/animation.length;animation.time+=animation.direction*delta,animation.mirroredLoop?(animation.time>animation.duration||animation.time<0)&&(animation.direction*=-1,animation.time>animation.duration&&(animation.time=animation.duration,animation.directionBackwards=!0),animation.time<0&&(animation.time=0,animation.directionBackwards=!1)):(animation.time=animation.time%animation.duration,animation.time<0&&(animation.time+=animation.duration));var keyframe=animation.start+THREE.Math.clamp(Math.floor(animation.time/frameTime),0,animation.length-1),weight=animation.weight;keyframe!==animation.currentFrame&&(this.morphTargetInfluences[animation.lastFrame]=0,this.morphTargetInfluences[animation.currentFrame]=1*weight,this.morphTargetInfluences[keyframe]=0,animation.lastFrame=animation.currentFrame,animation.currentFrame=keyframe);var mix=animation.time%frameTime/frameTime;animation.directionBackwards&&(mix=1-mix),animation.currentFrame!==animation.lastFrame?(this.morphTargetInfluences[animation.currentFrame]=mix*weight,this.morphTargetInfluences[animation.lastFrame]=(1-mix)*weight):this.morphTargetInfluences[animation.currentFrame]=weight}}},"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=THREE),exports.THREE=THREE):this.THREE=THREE},{}],6:[function(_dereq_,module,exports){!function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof _dereq_&&_dereq_;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof _dereq_&&_dereq_,o=0;o 0.0 && abs(vUV.x - 0.5) < .001) {","} else if (a.x < 0.0 || a.x > 1.0 || a.y < 0.0 || a.y > 1.0) {","gl_FragColor = backgroundColor;","} else {","gl_FragColor = texture2D(texture, vec2(a.x * 0.5 + (vUV.x < 0.5 ? 0.0 : 0.5), a.y));","}","}"].join("\n")};module.exports=BarrelDistortionFragment},{}],6:[function(_dereq_,module,exports){function Distortion(coefficients){this.coefficients=coefficients}Distortion.prototype.distortInverse=function(radius){for(var r0=radius/.9,r1=.9*radius,dr0=radius-this.distort(r0);Math.abs(r1-r0)>1e-4;){var dr1=radius-this.distort(r1),r2=r1-dr1*((r1-r0)/(dr1-dr0));r0=r1,r1=r2,dr0=dr1}return r1},Distortion.prototype.distort=function(radius){return radius*this.distortionFactor_(radius)},Distortion.prototype.distortionFactor_=function(radius){for(var result=1,rFactor=1,rSquared=radius*radius,i=0;i=200&&xhr.status<=299?(console.log("Successfully loaded online DPDB."),obj.dpdb=JSON.parse(xhr.response),obj.recalculateDeviceParams_()):console.error("Error loading online DPDB!")}),xhr.send()}}function DeviceParams(params){this.xdpi=params.xdpi,this.ydpi=params.ydpi,this.bevelMm=params.bevelMm}var DPDB_CACHE=_dereq_("./dpdb-cache.js"),Util=_dereq_("./util.js"),ONLINE_DPDB_URL="https://storage.googleapis.com/cardboard-dpdb/dpdb.json";Dpdb.prototype.getDeviceParams=function(){return this.deviceParams},Dpdb.prototype.recalculateDeviceParams_=function(){console.log("Recalculating device params.");var newDeviceParams=this.calcDeviceParams_();console.log("New device parameters:"),console.log(newDeviceParams),newDeviceParams?(this.deviceParams=newDeviceParams,this.onDeviceParamsUpdated&&this.onDeviceParamsUpdated(this.deviceParams)):console.warn("Failed to recalculate device parameters.")},Dpdb.prototype.calcDeviceParams_=function(){var db=this.dpdb;if(!db)return console.error("DPDB not available."),null;if(1!=db.format)return console.error("DPDB has unexpected format version."),null;if(!db.devices||!db.devices.length)return console.error("DPDB does not have a devices section."),null;var userAgent=navigator.userAgent||navigator.vendor||window.opera,width=Util.getScreenWidth(),height=Util.getScreenHeight();if(console.log("User agent: "+userAgent),console.log("Pixel width: "+width),console.log("Pixel height: "+height),!db.devices)return console.error("DPDB has no devices section."),null;for(var i=0;i %s",this.mode,mode),this.mode=mode,this.button.setMode(mode,this.isVRCompatible),this.mode==Modes.VR&&Util.isLandscapeMode()&&Util.isMobile()?this.rotateInstructions.showTemporarily(3e3):this.updateRotateInstructions_(),this.viewerSelector.hide(),this.emit("modechange",mode,oldMode),this.isTouchPannerEnabled&&(this.mode==Modes.VR?WebVRConfig.TOUCH_PANNER_DISABLED=!0:WebVRConfig.TOUCH_PANNER_DISABLED=!1),void(this.mode==Modes.VR&&this.setHMDVRDeviceParams_(this.getViewer())))},WebVRManager.prototype.onFSClick_=function(){switch(this.mode){case Modes.NORMAL:if(Util.isIOS()&&Util.isIFrame()){var url=window.location.href;return url=Util.appendQueryParameter(url,"no_fullscreen","true"),url=Util.appendQueryParameter(url,"start_mode",Modes.MAGIC_WINDOW),void(top.location.href=url)}this.normalToMagicWindow_(),this.setMode_(Modes.MAGIC_WINDOW);break;case Modes.MAGIC_WINDOW:this.isFullscreenDisabled?window.history.back():(this.anyModeToNormal_(),this.setMode_(Modes.NORMAL))}},WebVRManager.prototype.onVRClick_=function(){if(this.mode==Modes.NORMAL&&Util.isIOS()&&Util.isIFrame()){var url=window.location.href;return url=Util.appendQueryParameter(url,"no_fullscreen","true"),url=Util.appendQueryParameter(url,"start_mode",Modes.VR),void(top.location.href=url)}this.anyModeToVR_(),this.setMode_(Modes.VR)},WebVRManager.prototype.onBackClick_=function(){this.isFullscreenDisabled?window.history.back():(this.anyModeToNormal_(),this.setMode_(Modes.NORMAL))},WebVRManager.prototype.onSettingsClick_=function(){this.viewerSelector.show()},WebVRManager.prototype.normalToMagicWindow_=function(){this.requestFullscreen_(),this.wakelock.request()},WebVRManager.prototype.anyModeToVR_=function(){this.requestFullscreen_(),this.wakelock.request(),this.distorter.patch()},WebVRManager.prototype.vrToMagicWindow_=function(){this.distorter.unpatch(),this.resize_()},WebVRManager.prototype.anyModeToNormal_=function(){this.exitFullscreen_(),this.releasePointerLock_(),this.wakelock.release(),this.distorter.unpatch(),this.resize_()},WebVRManager.prototype.resizeIfNeeded_=function(camera){var size=this.renderer.getSize();size.width==window.innerWidth&&size.height==window.innerHeight||this.resize_()},WebVRManager.prototype.resize_=function(){this.effect.setSize(window.innerWidth,window.innerHeight),this.camera&&(this.camera.aspect=window.innerWidth/window.innerHeight,this.camera.updateProjectionMatrix())},WebVRManager.prototype.onOrientationChange_=function(e){this.updateRotateInstructions_(),this.viewerSelector.hide()},WebVRManager.prototype.updateRotateInstructions_=function(){this.rotateInstructions.disableShowTemporarily(),this.mode==Modes.VR&&!Util.isLandscapeMode()&&Util.isMobile()?this.rotateInstructions.show():this.rotateInstructions.hide()},WebVRManager.prototype.onFullscreenChange_=function(e){null!==document.webkitFullscreenElement&&null!==document.mozFullScreenElement||(this.anyModeToNormal_(),this.setMode_(Modes.NORMAL))},WebVRManager.prototype.requestPointerLock_=function(){var canvas=this.renderer.domElement;canvas.requestPointerLock=canvas.requestPointerLock||canvas.mozRequestPointerLock||canvas.webkitRequestPointerLock,canvas.requestPointerLock&&canvas.requestPointerLock()},WebVRManager.prototype.releasePointerLock_=function(){document.exitPointerLock=document.exitPointerLock||document.mozExitPointerLock||document.webkitExitPointerLock,document.exitPointerLock&&document.exitPointerLock()},WebVRManager.prototype.requestOrientationLock_=function(){screen.orientation&&Util.isMobile()&&screen.orientation.lock("landscape")},WebVRManager.prototype.releaseOrientationLock_=function(){screen.orientation&&screen.orientation.unlock()},WebVRManager.prototype.requestFullscreen_=function(){var canvas=document.body;canvas.requestFullscreen?canvas.requestFullscreen():canvas.mozRequestFullScreen?canvas.mozRequestFullScreen({vrDisplay:this.hmd}):canvas.webkitRequestFullscreen?canvas.webkitRequestFullscreen({vrDisplay:this.hmd}):canvas.msRequestFullscreen&&canvas.msRequestFullscreen({vrDisplay:this.hmd})},WebVRManager.prototype.exitFullscreen_=function(){document.exitFullscreen?document.exitFullscreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen?document.webkitExitFullscreen():document.msExitFullscreen&&document.msExitFullscreen()},WebVRManager.prototype.onViewerChanged_=function(viewer){this.deviceInfo.setViewer(viewer),this.distorter.updateDeviceInfo(this.deviceInfo),this.setHMDVRDeviceParams_(viewer),this.emit("viewerchange",viewer)},WebVRManager.prototype.setHMDVRDeviceParams_=function(viewer){this.getDeviceByType_(HMDVRDevice).then(function(hmd){hmd&&(hmd.setFieldOfView&&hmd.setFieldOfView(this.deviceInfo.getFieldOfViewLeftEye(this.isUndistorted),this.deviceInfo.getFieldOfViewRightEye(this.isUndistorted)),hmd.setInterpupillaryDistance&&hmd.setInterpupillaryDistance(viewer.interLensDistance))}.bind(this))},WebVRManager.prototype.onDeviceParamsUpdated_=function(newParams){console.log("DPDB reported that device params were updated."),this.deviceInfo.updateDeviceParams(newParams),this.distorter.updateDeviceInfo(this.deviceInfo)},module.exports=WebVRManager},{"./button-manager.js":2,"./cardboard-distorter.js":3,"./device-info.js":4,"./dpdb.js":8,"./emitter.js":9,"./modes.js":11,"./rotate-instructions.js":12,"./util.js":13,"./viewer-selector.js":14,"./wakelock.js":15}]},{},[10])},{}],7:[function(_dereq_,module,exports){!function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof _dereq_&&_dereq_;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof _dereq_&&_dereq_,o=0;oUtil.MAX_TIMESTEP?(console.warn("Invalid timestamps detected. Time step between successive gyroscope sensor samples is very small or not monotonic"),void(this.previousTimestampS=timestampS)):(this.accelerometer.set(-accGravity.x,-accGravity.y,-accGravity.z), +this.gyroscope.set(rotRate.alpha,rotRate.beta,rotRate.gamma),(this.isIOS||this.isFirefoxAndroid)&&this.gyroscope.multiplyScalar(Math.PI/180),this.filter.addAccelMeasurement(this.accelerometer,timestampS),this.filter.addGyroMeasurement(this.gyroscope,timestampS),void(this.previousTimestampS=timestampS))},FusionPositionSensorVRDevice.prototype.onScreenOrientationChange_=function(screenOrientation){this.setScreenTransform_()},FusionPositionSensorVRDevice.prototype.setScreenTransform_=function(){switch(this.worldToScreenQ.set(0,0,0,1),window.orientation){case 0:break;case 90:this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0,0,1),-Math.PI/2);break;case-90:this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0,0,1),Math.PI/2);break;case 180:}},module.exports=FusionPositionSensorVRDevice},{"./base.js":1,"./complementary-filter.js":3,"./pose-predictor.js":7,"./three-math.js":9,"./touch-panner.js":10,"./util.js":11}],5:[function(_dereq_,module,exports){var WebVRPolyfill=_dereq_("./webvr-polyfill.js");window.WebVRConfig=window.WebVRConfig||{},new WebVRPolyfill},{"./webvr-polyfill.js":12}],6:[function(_dereq_,module,exports){function MouseKeyboardPositionSensorVRDevice(){this.deviceId="webvr-polyfill:mouse-keyboard",this.deviceName="VR Position Device (webvr-polyfill:mouse-keyboard)",window.addEventListener("keydown",this.onKeyDown_.bind(this)),window.addEventListener("mousemove",this.onMouseMove_.bind(this)),window.addEventListener("mousedown",this.onMouseDown_.bind(this)),window.addEventListener("mouseup",this.onMouseUp_.bind(this)),this.phi=0,this.theta=0,this.targetAngle=null,this.euler=new THREE.Euler,this.orientation=new THREE.Quaternion,this.rotateStart=new THREE.Vector2,this.rotateEnd=new THREE.Vector2,this.rotateDelta=new THREE.Vector2}var PositionSensorVRDevice=_dereq_("./base.js").PositionSensorVRDevice,THREE=_dereq_("./three-math.js"),Util=_dereq_("./util.js"),KEY_SPEED=.15,KEY_ANIMATION_DURATION=80,MOUSE_SPEED_X=.5,MOUSE_SPEED_Y=.3;MouseKeyboardPositionSensorVRDevice.prototype=new PositionSensorVRDevice,MouseKeyboardPositionSensorVRDevice.prototype.getState=function(){return this.euler.set(this.phi,this.theta,0,"YXZ"),this.orientation.setFromEuler(this.euler),{hasOrientation:!0,orientation:this.orientation,hasPosition:!1,position:null}},MouseKeyboardPositionSensorVRDevice.prototype.onKeyDown_=function(e){38==e.keyCode?this.animatePhi_(this.phi+KEY_SPEED):39==e.keyCode?this.animateTheta_(this.theta-KEY_SPEED):40==e.keyCode?this.animatePhi_(this.phi-KEY_SPEED):37==e.keyCode&&this.animateTheta_(this.theta+KEY_SPEED)},MouseKeyboardPositionSensorVRDevice.prototype.animateTheta_=function(targetAngle){this.animateKeyTransitions_("theta",targetAngle)},MouseKeyboardPositionSensorVRDevice.prototype.animatePhi_=function(targetAngle){targetAngle=Util.clamp(targetAngle,-Math.PI/2,Math.PI/2),this.animateKeyTransitions_("phi",targetAngle)},MouseKeyboardPositionSensorVRDevice.prototype.animateKeyTransitions_=function(angleName,targetAngle){this.angleAnimation&&clearInterval(this.angleAnimation);var startAngle=this[angleName],startTime=new Date;this.angleAnimation=setInterval(function(){var elapsed=new Date-startTime;if(elapsed>=KEY_ANIMATION_DURATION)return this[angleName]=targetAngle,void clearInterval(this.angleAnimation);var percent=elapsed/KEY_ANIMATION_DURATION;this[angleName]=startAngle+(targetAngle-startAngle)*percent}.bind(this),1e3/60)},MouseKeyboardPositionSensorVRDevice.prototype.onMouseDown_=function(e){this.rotateStart.set(e.clientX,e.clientY),this.isDragging=!0},MouseKeyboardPositionSensorVRDevice.prototype.onMouseMove_=function(e){if(this.isDragging||this.isPointerLocked_()){if(this.isPointerLocked_()){var movementX=e.movementX||e.mozMovementX||0,movementY=e.movementY||e.mozMovementY||0;this.rotateEnd.set(this.rotateStart.x-movementX,this.rotateStart.y-movementY)}else this.rotateEnd.set(e.clientX,e.clientY);this.rotateDelta.subVectors(this.rotateEnd,this.rotateStart),this.rotateStart.copy(this.rotateEnd);var element=document.body;this.phi+=2*Math.PI*this.rotateDelta.y/element.clientHeight*MOUSE_SPEED_Y,this.theta+=2*Math.PI*this.rotateDelta.x/element.clientWidth*MOUSE_SPEED_X,this.phi=Util.clamp(this.phi,-Math.PI/2,Math.PI/2)}},MouseKeyboardPositionSensorVRDevice.prototype.onMouseUp_=function(e){this.isDragging=!1},MouseKeyboardPositionSensorVRDevice.prototype.isPointerLocked_=function(){var el=document.pointerLockElement||document.mozPointerLockElement||document.webkitPointerLockElement;return void 0!==el},MouseKeyboardPositionSensorVRDevice.prototype.resetSensor=function(){console.error("Not implemented yet.")},module.exports=MouseKeyboardPositionSensorVRDevice},{"./base.js":1,"./three-math.js":9,"./util.js":11}],7:[function(_dereq_,module,exports){function PosePredictor(predictionTimeS){this.predictionTimeS=predictionTimeS,this.previousQ=new THREE.Quaternion,this.previousTimestampS=null,this.deltaQ=new THREE.Quaternion,this.outQ=new THREE.Quaternion}var THREE=_dereq_("./three-math.js"),DEBUG=!1;PosePredictor.prototype.getPrediction=function(currentQ,gyro,timestampS){if(!this.previousTimestampS)return this.previousQ.copy(currentQ),this.previousTimestampS=timestampS,currentQ;var axis=new THREE.Vector3;axis.copy(gyro),axis.normalize();var angularSpeed=gyro.length();if(angularSpeed0?(s=.5/Math.sqrt(trace+1),this._w=.25/s,this._x=(m32-m23)*s,this._y=(m13-m31)*s,this._z=(m21-m12)*s):m11>m22&&m11>m33?(s=2*Math.sqrt(1+m11-m22-m33),this._w=(m32-m23)/s,this._x=.25*s,this._y=(m12+m21)/s,this._z=(m13+m31)/s):m22>m33?(s=2*Math.sqrt(1+m22-m11-m33),this._w=(m13-m31)/s,this._x=(m12+m21)/s,this._y=.25*s,this._z=(m23+m32)/s):(s=2*Math.sqrt(1+m33-m11-m22),this._w=(m21-m12)/s,this._x=(m13+m31)/s,this._y=(m23+m32)/s,this._z=.25*s),this.onChangeCallback(),this},setFromUnitVectors:function(){var v1,r,EPS=1e-6;return function(vFrom,vTo){return void 0===v1&&(v1=new THREE.Vector3),r=vFrom.dot(vTo)+1,EPS>r?(r=0,Math.abs(vFrom.x)>Math.abs(vFrom.z)?v1.set(-vFrom.y,vFrom.x,0):v1.set(0,-vFrom.z,vFrom.y)):v1.crossVectors(vFrom,vTo),this._x=v1.x,this._y=v1.y,this._z=v1.z,this._w=r,this.normalize(),this}}(),inverse:function(){return this.conjugate().normalize(),this},conjugate:function(){return this._x*=-1,this._y*=-1,this._z*=-1,this.onChangeCallback(),this},dot:function(v){return this._x*v._x+this._y*v._y+this._z*v._z+this._w*v._w},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var l=this.length();return 0===l?(this._x=0,this._y=0,this._z=0,this._w=1):(l=1/l,this._x=this._x*l,this._y=this._y*l,this._z=this._z*l,this._w=this._w*l),this.onChangeCallback(),this},multiply:function(q,p){return void 0!==p?(console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(q,p)):this.multiplyQuaternions(this,q)},multiplyQuaternions:function(a,b){var qax=a._x,qay=a._y,qaz=a._z,qaw=a._w,qbx=b._x,qby=b._y,qbz=b._z,qbw=b._w;return this._x=qax*qbw+qaw*qbx+qay*qbz-qaz*qby,this._y=qay*qbw+qaw*qby+qaz*qbx-qax*qbz,this._z=qaz*qbw+qaw*qbz+qax*qby-qay*qbx,this._w=qaw*qbw-qax*qbx-qay*qby-qaz*qbz,this.onChangeCallback(),this},multiplyVector3:function(vector){return console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead."),vector.applyQuaternion(this)},slerp:function(qb,t){if(0===t)return this;if(1===t)return this.copy(qb);var x=this._x,y=this._y,z=this._z,w=this._w,cosHalfTheta=w*qb._w+x*qb._x+y*qb._y+z*qb._z;if(0>cosHalfTheta?(this._w=-qb._w,this._x=-qb._x,this._y=-qb._y,this._z=-qb._z,cosHalfTheta=-cosHalfTheta):this.copy(qb),cosHalfTheta>=1)return this._w=w,this._x=x,this._y=y,this._z=z,this;var halfTheta=Math.acos(cosHalfTheta),sinHalfTheta=Math.sqrt(1-cosHalfTheta*cosHalfTheta);if(Math.abs(sinHalfTheta)<.001)return this._w=.5*(w+this._w),this._x=.5*(x+this._x),this._y=.5*(y+this._y),this._z=.5*(z+this._z),this;var ratioA=Math.sin((1-t)*halfTheta)/sinHalfTheta,ratioB=Math.sin(t*halfTheta)/sinHalfTheta;return this._w=w*ratioA+this._w*ratioB,this._x=x*ratioA+this._x*ratioB,this._y=y*ratioA+this._y*ratioB,this._z=z*ratioA+this._z*ratioB,this.onChangeCallback(),this},equals:function(quaternion){return quaternion._x===this._x&&quaternion._y===this._y&&quaternion._z===this._z&&quaternion._w===this._w},fromArray:function(array,offset){return void 0===offset&&(offset=0),this._x=array[offset],this._y=array[offset+1],this._z=array[offset+2],this._w=array[offset+3],this.onChangeCallback(),this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this._x,array[offset+1]=this._y,array[offset+2]=this._z,array[offset+3]=this._w,array},onChange:function(callback){return this.onChangeCallback=callback,this},onChangeCallback:function(){},clone:function(){return new THREE.Quaternion(this._x,this._y,this._z,this._w)}},THREE.Quaternion.slerp=function(qa,qb,qm,t){return qm.copy(qa).slerp(qb,t)},THREE.Vector2=function(x,y){this.x=x||0,this.y=y||0},THREE.Vector2.prototype={constructor:THREE.Vector2,set:function(x,y){return this.x=x,this.y=y,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+index)}},copy:function(v){return this.x=v.x,this.y=v.y,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this)},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this},addScalar:function(s){return this.x+=s,this.y+=s,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this)},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this},multiply:function(v){return this.x*=v.x,this.y*=v.y,this},multiplyScalar:function(s){return this.x*=s,this.y*=s,this},divide:function(v){return this.x/=v.x,this.y/=v.y,this},divideScalar:function(scalar){if(0!==scalar){var invScalar=1/scalar;this.x*=invScalar,this.y*=invScalar}else this.x=0,this.y=0;return this},min:function(v){return this.x>v.x&&(this.x=v.x),this.y>v.y&&(this.y=v.y),this},max:function(v){return this.xmax.x&&(this.x=max.x),this.ymax.y&&(this.y=max.y),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector2,max=new THREE.Vector2),min.set(minVal,minVal),max.set(maxVal,maxVal),this.clamp(min,max)}}(),floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this},negate:function(){return this.x=-this.x,this.y=-this.y,this},dot:function(v){return this.x*v.x+this.y*v.y},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},normalize:function(){return this.divideScalar(this.length())},distanceTo:function(v){return Math.sqrt(this.distanceToSquared(v))},distanceToSquared:function(v){var dx=this.x-v.x,dy=this.y-v.y;return dx*dx+dy*dy},setLength:function(l){var oldLength=this.length();return 0!==oldLength&&l!==oldLength&&this.multiplyScalar(l/oldLength),this},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this},equals:function(v){return v.x===this.x&&v.y===this.y},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this},clone:function(){return new THREE.Vector2(this.x,this.y)}},THREE.Vector3=function(x,y,z){this.x=x||0,this.y=y||0,this.z=z||0},THREE.Vector3.prototype={constructor:THREE.Vector3,set:function(x,y,z){return this.x=x,this.y=y,this.z=z,this},setX:function(x){return this.x=x,this},setY:function(y){return this.y=y,this},setZ:function(z){return this.z=z,this},setComponent:function(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;case 2:this.z=value;break;default:throw new Error("index is out of range: "+index)}},getComponent:function(index){switch(index){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("index is out of range: "+index)}},copy:function(v){return this.x=v.x,this.y=v.y,this.z=v.z,this},add:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(v,w)):(this.x+=v.x,this.y+=v.y,this.z+=v.z,this)},addScalar:function(s){return this.x+=s,this.y+=s,this.z+=s,this},addVectors:function(a,b){return this.x=a.x+b.x,this.y=a.y+b.y,this.z=a.z+b.z,this},sub:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(v,w)):(this.x-=v.x,this.y-=v.y,this.z-=v.z,this)},subVectors:function(a,b){return this.x=a.x-b.x,this.y=a.y-b.y,this.z=a.z-b.z,this},multiply:function(v,w){return void 0!==w?(console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(v,w)):(this.x*=v.x,this.y*=v.y,this.z*=v.z,this)},multiplyScalar:function(scalar){return this.x*=scalar,this.y*=scalar,this.z*=scalar,this},multiplyVectors:function(a,b){return this.x=a.x*b.x,this.y=a.y*b.y,this.z=a.z*b.z,this},applyEuler:function(){var quaternion;return function(euler){return euler instanceof THREE.Euler==!1&&console.error("THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order."),void 0===quaternion&&(quaternion=new THREE.Quaternion),this.applyQuaternion(quaternion.setFromEuler(euler)),this}}(),applyAxisAngle:function(){var quaternion;return function(axis,angle){return void 0===quaternion&&(quaternion=new THREE.Quaternion),this.applyQuaternion(quaternion.setFromAxisAngle(axis,angle)),this}}(),applyMatrix3:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[3]*y+e[6]*z,this.y=e[1]*x+e[4]*y+e[7]*z,this.z=e[2]*x+e[5]*y+e[8]*z,this},applyMatrix4:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z+e[12],this.y=e[1]*x+e[5]*y+e[9]*z+e[13],this.z=e[2]*x+e[6]*y+e[10]*z+e[14],this},applyProjection:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements,d=1/(e[3]*x+e[7]*y+e[11]*z+e[15]);return this.x=(e[0]*x+e[4]*y+e[8]*z+e[12])*d,this.y=(e[1]*x+e[5]*y+e[9]*z+e[13])*d,this.z=(e[2]*x+e[6]*y+e[10]*z+e[14])*d,this},applyQuaternion:function(q){var x=this.x,y=this.y,z=this.z,qx=q.x,qy=q.y,qz=q.z,qw=q.w,ix=qw*x+qy*z-qz*y,iy=qw*y+qz*x-qx*z,iz=qw*z+qx*y-qy*x,iw=-qx*x-qy*y-qz*z;return this.x=ix*qw+iw*-qx+iy*-qz-iz*-qy,this.y=iy*qw+iw*-qy+iz*-qx-ix*-qz,this.z=iz*qw+iw*-qz+ix*-qy-iy*-qx,this},project:function(){var matrix;return function(camera){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.multiplyMatrices(camera.projectionMatrix,matrix.getInverse(camera.matrixWorld)),this.applyProjection(matrix)}}(),unproject:function(){var matrix;return function(camera){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.multiplyMatrices(camera.matrixWorld,matrix.getInverse(camera.projectionMatrix)),this.applyProjection(matrix)}}(),transformDirection:function(m){var x=this.x,y=this.y,z=this.z,e=m.elements;return this.x=e[0]*x+e[4]*y+e[8]*z,this.y=e[1]*x+e[5]*y+e[9]*z,this.z=e[2]*x+e[6]*y+e[10]*z,this.normalize(),this},divide:function(v){return this.x/=v.x,this.y/=v.y,this.z/=v.z,this},divideScalar:function(scalar){if(0!==scalar){var invScalar=1/scalar;this.x*=invScalar,this.y*=invScalar,this.z*=invScalar}else this.x=0,this.y=0,this.z=0;return this},min:function(v){return this.x>v.x&&(this.x=v.x),this.y>v.y&&(this.y=v.y),this.z>v.z&&(this.z=v.z),this},max:function(v){return this.xmax.x&&(this.x=max.x),this.ymax.y&&(this.y=max.y),this.zmax.z&&(this.z=max.z),this},clampScalar:function(){var min,max;return function(minVal,maxVal){return void 0===min&&(min=new THREE.Vector3,max=new THREE.Vector3),min.set(minVal,minVal,minVal),max.set(maxVal,maxVal,maxVal),this.clamp(min,max)}}(),floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this},negate:function(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this},dot:function(v){return this.x*v.x+this.y*v.y+this.z*v.z},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length())},setLength:function(l){var oldLength=this.length();return 0!==oldLength&&l!==oldLength&&this.multiplyScalar(l/oldLength),this},lerp:function(v,alpha){return this.x+=(v.x-this.x)*alpha,this.y+=(v.y-this.y)*alpha,this.z+=(v.z-this.z)*alpha,this},cross:function(v,w){if(void 0!==w)return console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(v,w);var x=this.x,y=this.y,z=this.z;return this.x=y*v.z-z*v.y,this.y=z*v.x-x*v.z,this.z=x*v.y-y*v.x,this},crossVectors:function(a,b){var ax=a.x,ay=a.y,az=a.z,bx=b.x,by=b.y,bz=b.z;return this.x=ay*bz-az*by,this.y=az*bx-ax*bz,this.z=ax*by-ay*bx,this},projectOnVector:function(){var v1,dot;return function(vector){return void 0===v1&&(v1=new THREE.Vector3),v1.copy(vector).normalize(),dot=this.dot(v1),this.copy(v1).multiplyScalar(dot)}}(),projectOnPlane:function(){var v1;return function(planeNormal){return void 0===v1&&(v1=new THREE.Vector3),v1.copy(this).projectOnVector(planeNormal),this.sub(v1)}}(),reflect:function(){var v1;return function(normal){return void 0===v1&&(v1=new THREE.Vector3),this.sub(v1.copy(normal).multiplyScalar(2*this.dot(normal)))}}(),angleTo:function(v){var theta=this.dot(v)/(this.length()*v.length());return Math.acos(THREE.Math.clamp(theta,-1,1))},distanceTo:function(v){return Math.sqrt(this.distanceToSquared(v))},distanceToSquared:function(v){var dx=this.x-v.x,dy=this.y-v.y,dz=this.z-v.z;return dx*dx+dy*dy+dz*dz},setEulerFromRotationMatrix:function(m,order){console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.")},setEulerFromQuaternion:function(q,order){console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.")},getPositionFromMatrix:function(m){return console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()."),this.setFromMatrixPosition(m)},getScaleFromMatrix:function(m){return console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()."),this.setFromMatrixScale(m)},getColumnFromMatrix:function(index,matrix){return console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()."),this.setFromMatrixColumn(index,matrix)},setFromMatrixPosition:function(m){return this.x=m.elements[12],this.y=m.elements[13],this.z=m.elements[14],this},setFromMatrixScale:function(m){var sx=this.set(m.elements[0],m.elements[1],m.elements[2]).length(),sy=this.set(m.elements[4],m.elements[5],m.elements[6]).length(),sz=this.set(m.elements[8],m.elements[9],m.elements[10]).length();return this.x=sx,this.y=sy,this.z=sz,this},setFromMatrixColumn:function(index,matrix){var offset=4*index,me=matrix.elements;return this.x=me[offset],this.y=me[offset+1],this.z=me[offset+2],this},equals:function(v){return v.x===this.x&&v.y===this.y&&v.z===this.z},fromArray:function(array,offset){return void 0===offset&&(offset=0),this.x=array[offset],this.y=array[offset+1],this.z=array[offset+2],this},toArray:function(array,offset){return void 0===array&&(array=[]),void 0===offset&&(offset=0),array[offset]=this.x,array[offset+1]=this.y,array[offset+2]=this.z,array},fromAttribute:function(attribute,index,offset){return void 0===offset&&(offset=0),index=index*attribute.itemSize+offset,this.x=attribute.array[index],this.y=attribute.array[index+1],this.z=attribute.array[index+2],this},clone:function(){return new THREE.Vector3(this.x,this.y,this.z)}},THREE.Euler=function(x,y,z,order){this._x=x||0,this._y=y||0,this._z=z||0,this._order=order||THREE.Euler.DefaultOrder},THREE.Euler.RotationOrders=["XYZ","YZX","ZXY","XZY","YXZ","ZYX"],THREE.Euler.DefaultOrder="XYZ",THREE.Euler.prototype={constructor:THREE.Euler,_x:0,_y:0,_z:0,_order:THREE.Euler.DefaultOrder,get x(){return this._x},set x(value){this._x=value,this.onChangeCallback()},get y(){return this._y},set y(value){this._y=value,this.onChangeCallback()},get z(){return this._z},set z(value){this._z=value,this.onChangeCallback()},get order(){return this._order},set order(value){this._order=value,this.onChangeCallback()},set:function(x,y,z,order){return this._x=x,this._y=y,this._z=z,this._order=order||this._order,this.onChangeCallback(),this},copy:function(euler){return this._x=euler._x,this._y=euler._y,this._z=euler._z,this._order=euler._order,this.onChangeCallback(),this},setFromRotationMatrix:function(m,order,update){var clamp=THREE.Math.clamp,te=m.elements,m11=te[0],m12=te[4],m13=te[8],m21=te[1],m22=te[5],m23=te[9],m31=te[2],m32=te[6],m33=te[10];return order=order||this._order,"XYZ"===order?(this._y=Math.asin(clamp(m13,-1,1)),Math.abs(m13)<.99999?(this._x=Math.atan2(-m23,m33),this._z=Math.atan2(-m12,m11)):(this._x=Math.atan2(m32,m22),this._z=0)):"YXZ"===order?(this._x=Math.asin(-clamp(m23,-1,1)),Math.abs(m23)<.99999?(this._y=Math.atan2(m13,m33),this._z=Math.atan2(m21,m22)):(this._y=Math.atan2(-m31,m11),this._z=0)):"ZXY"===order?(this._x=Math.asin(clamp(m32,-1,1)),Math.abs(m32)<.99999?(this._y=Math.atan2(-m31,m33),this._z=Math.atan2(-m12,m22)):(this._y=0,this._z=Math.atan2(m21,m11))):"ZYX"===order?(this._y=Math.asin(-clamp(m31,-1,1)),Math.abs(m31)<.99999?(this._x=Math.atan2(m32,m33),this._z=Math.atan2(m21,m11)):(this._x=0,this._z=Math.atan2(-m12,m22))):"YZX"===order?(this._z=Math.asin(clamp(m21,-1,1)),Math.abs(m21)<.99999?(this._x=Math.atan2(-m23,m22),this._y=Math.atan2(-m31,m11)):(this._x=0,this._y=Math.atan2(m13,m33))):"XZY"===order?(this._z=Math.asin(-clamp(m12,-1,1)),Math.abs(m12)<.99999?(this._x=Math.atan2(m32,m22),this._y=Math.atan2(m13,m11)):(this._x=Math.atan2(-m23,m33),this._y=0)):console.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+order),this._order=order,update!==!1&&this.onChangeCallback(),this},setFromQuaternion:function(){var matrix;return function(q,order,update){return void 0===matrix&&(matrix=new THREE.Matrix4),matrix.makeRotationFromQuaternion(q),this.setFromRotationMatrix(matrix,order,update),this}}(),setFromVector3:function(v,order){return this.set(v.x,v.y,v.z,order||this._order)},reorder:function(){var q=new THREE.Quaternion;return function(newOrder){q.setFromEuler(this),this.setFromQuaternion(q,newOrder)}}(),equals:function(euler){return euler._x===this._x&&euler._y===this._y&&euler._z===this._z&&euler._order===this._order},fromArray:function(array){return this._x=array[0],this._y=array[1],this._z=array[2],void 0!==array[3]&&(this._order=array[3]),this.onChangeCallback(),this},toArray:function(){return[this._x,this._y,this._z,this._order]},toVector3:function(optionalResult){return optionalResult?optionalResult.set(this._x,this._y,this._z):new THREE.Vector3(this._x,this._y,this._z)},onChange:function(callback){return this.onChangeCallback=callback,this},onChangeCallback:function(){},clone:function(){return new THREE.Euler(this._x,this._y,this._z,this._order)}},THREE.Math={generateUUID:function(){var r,chars="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),uuid=new Array(36),rnd=0;return function(){for(var i=0;36>i;i++)8==i||13==i||18==i||23==i?uuid[i]="-":14==i?uuid[i]="4":(2>=rnd&&(rnd=33554432+16777216*Math.random()|0),r=15&rnd,rnd>>=4,uuid[i]=chars[19==i?3&r|8:r]);return uuid.join("")}}(),clamp:function(x,a,b){return a>x?a:x>b?b:x},clampBottom:function(x,a){return a>x?a:x},mapLinear:function(x,a1,a2,b1,b2){return b1+(x-a1)*(b2-b1)/(a2-a1)},smoothstep:function(x,min,max){return min>=x?0:x>=max?1:(x=(x-min)/(max-min),x*x*(3-2*x))},smootherstep:function(x,min,max){return min>=x?0:x>=max?1:(x=(x-min)/(max-min),x*x*x*(x*(6*x-15)+10))},random16:function(){return(65280*Math.random()+255*Math.random())/65535},randInt:function(low,high){return Math.floor(this.randFloat(low,high))},randFloat:function(low,high){return low+Math.random()*(high-low)},randFloatSpread:function(range){return range*(.5-Math.random())},degToRad:function(){var degreeToRadiansFactor=Math.PI/180;return function(degrees){return degrees*degreeToRadiansFactor}}(),radToDeg:function(){var radianToDegreesFactor=180/Math.PI;return function(radians){return radians*radianToDegreesFactor}}(),isPowerOfTwo:function(value){return 0===(value&value-1)&&0!==value},nextPowerOfTwo:function(value){return value--,value|=value>>1,value|=value>>2,value|=value>>4,value|=value>>8,value|=value>>16,value++,value}}),module.exports=THREE},{}],10:[function(_dereq_,module,exports){function TouchPanner(){window.addEventListener("touchstart",this.onTouchStart_.bind(this)),window.addEventListener("touchmove",this.onTouchMove_.bind(this)),window.addEventListener("touchend",this.onTouchEnd_.bind(this)),this.isTouching=!1,this.rotateStart=new THREE.Vector2,this.rotateEnd=new THREE.Vector2,this.rotateDelta=new THREE.Vector2,this.theta=0,this.orientation=new THREE.Quaternion}var THREE=_dereq_("./three-math.js"),Util=_dereq_("./util.js"),ROTATE_SPEED=.5;TouchPanner.prototype.getOrientation=function(){return this.orientation.setFromEuler(new THREE.Euler(0,0,this.theta)),this.orientation},TouchPanner.prototype.resetSensor=function(){this.theta=0},TouchPanner.prototype.onTouchStart_=function(e){1==e.touches.length&&(this.rotateStart.set(e.touches[0].pageX,e.touches[0].pageY),this.isTouching=!0)},TouchPanner.prototype.onTouchMove_=function(e){if(this.isTouching){this.rotateEnd.set(e.touches[0].pageX,e.touches[0].pageY),this.rotateDelta.subVectors(this.rotateEnd,this.rotateStart),this.rotateStart.copy(this.rotateEnd),Util.isIOS()&&(this.rotateDelta.x*=-1);var element=document.body;this.theta+=2*Math.PI*this.rotateDelta.x/element.clientWidth*ROTATE_SPEED}},TouchPanner.prototype.onTouchEnd_=function(e){this.isTouching=!1},module.exports=TouchPanner},{"./three-math.js":9,"./util.js":11}],11:[function(_dereq_,module,exports){var Util=window.Util||{};Util.MIN_TIMESTEP=.001,Util.MAX_TIMESTEP=1,Util.clamp=function(value,min,max){return Math.min(Math.max(min,value),max)},Util.isIOS=function(){return/iPad|iPhone|iPod/.test(navigator.platform)},Util.isFirefoxAndroid=function(){return-1!==navigator.userAgent.indexOf("Firefox")&&-1!==navigator.userAgent.indexOf("Android")},Util.isTimestampDeltaValid=function(timestampDeltaS){return isNaN(timestampDeltaS)?!1:timestampDeltaS<=Util.MIN_TIMESTEP?!1:!(timestampDeltaS>Util.MAX_TIMESTEP)},module.exports=Util},{}],12:[function(_dereq_,module,exports){function WebVRPolyfill(){this.devices=[],this.isWebVRAvailable()||this.enablePolyfill()}var CardboardHMDVRDevice=_dereq_("./cardboard-hmd-vr-device.js"),FusionPositionSensorVRDevice=_dereq_("./fusion-position-sensor-vr-device.js"),MouseKeyboardPositionSensorVRDevice=_dereq_("./mouse-keyboard-position-sensor-vr-device.js"),HMDVRDevice=_dereq_("./base.js").HMDVRDevice,PositionSensorVRDevice=_dereq_("./base.js").PositionSensorVRDevice; +WebVRPolyfill.prototype.isWebVRAvailable=function(){return"getVRDevices"in navigator||"mozGetVRDevices"in navigator},WebVRPolyfill.prototype.enablePolyfill=function(){this.isCardboardCompatible()&&this.devices.push(new CardboardHMDVRDevice),this.isMobile()?this.devices.push(new FusionPositionSensorVRDevice):WebVRConfig.MOUSE_KEYBOARD_CONTROLS_DISABLED||this.devices.push(new MouseKeyboardPositionSensorVRDevice),navigator.getVRDevices=this.getVRDevices.bind(this),window.HMDVRDevice=HMDVRDevice,window.PositionSensorVRDevice=PositionSensorVRDevice},WebVRPolyfill.prototype.getVRDevices=function(){var devices=this.devices;return new Promise(function(resolve,reject){try{resolve(devices)}catch(e){reject(e)}})},WebVRPolyfill.prototype.isMobile=function(){return/Android/i.test(navigator.userAgent)||/iPhone|iPad|iPod/i.test(navigator.userAgent)},WebVRPolyfill.prototype.isCardboardCompatible=function(){return this.isMobile()||WebVRConfig.FORCE_ENABLE_VR},module.exports=WebVRPolyfill},{"./base.js":1,"./cardboard-hmd-vr-device.js":2,"./fusion-position-sensor-vr-device.js":4,"./mouse-keyboard-position-sensor-vr-device.js":6}]},{},[5])},{}],8:[function(_dereq_,module,exports){function DeviceMotionReceiver(){window.addEventListener("message",this.onMessage_.bind(this),!1)}DeviceMotionReceiver.prototype.onMessage_=function(event){var message=event.data;return"DeviceMotion"!==message.type?void console.warn("Got unknown message of type %s from %s",message.type,message.origin):(console.log("onMessage_",event),void this.synthesizeDeviceMotionEvent_(message.deviceMotionEvent))},DeviceMotionReceiver.prototype.synthesizeDeviceMotionEvent_=function(eventData){var type="devicemotion-iframe",canBubble=!1,cancelable=!1,dme=document.createEvent("DeviceMotionEvent");dme.initDeviceMotionEvent(type,canBubble,cancelable,eventData.acceleration,eventData.accelerationIncludingGravity,eventData.rotationRate,eventData.interval),window.dispatchEvent(dme)},module.exports=DeviceMotionReceiver},{}],9:[function(_dereq_,module,exports){function Emitter(){this.initEmitter()}Emitter.prototype.initEmitter=function(){this.callbacks={}},Emitter.prototype.emit=function(eventName){var callbacks=this.callbacks[eventName];if(!callbacks)return void console.log("No valid callback specified.");var args=[].slice.call(arguments);args.shift();for(var i=0;ij;j++)uvs[i][j].x*=p.scaleX,uvs[i][j].x+=p.offsetX,uvs[i][j].y*=p.scaleY,uvs[i][j].y+=p.offsetY;var material=new THREE.MeshBasicMaterial({map:texture});this.distorter.setMap(texture);var out=new THREE.Mesh(geometry,material);return out.renderOrder=-1,out},PhotosphereRenderer.prototype.createScene_=function(opt_params){var scene=new THREE.Scene;scene.add(new THREE.PointLight(16777215));var photoGroup=new THREE.Object3D;return photoGroup.name="photo",scene.add(photoGroup),scene},PhotosphereRenderer.prototype.updateMaterial_=function(material_FOO){for(var i=0;i1)for(var i=1;i + + + VR View - pano example + + + + + + + + + +

Machu Pichu
World Heritage Site

+

The world-famous citadel of the Andes

+

Machu Picchu is an Incan citadel set high in the Andes Mountains in + Peru, above the Urubamba River valley.

+ + + +

A 360 panoramic view of Machu Pichu.

+ +

It is situated on a mountain ridge above the Sacred Valley which is 80 + kilometres (50 mi) northwest of Cuzco and through which the Urubamba River + flows. Most archaeologists believe that Machu Picchu was built as an estate + for the Inca emperor Pachacuti (1438–1472). Often mistakenly referred to as + the Lost City of the Incas, it is the most familiar icon of Inca + civilization.

+ +

The Incas built the estate around 1450, but abandoned it a century later + at the time of the Spanish Conquest. Although known locally, it was not + known to the Spanish during the colonial period and remained unknown to the + outside world before being brought to international attention in 1911 by the + American historian Hiram Bingham. Most of the outlying buildings have been + reconstructed in order to give tourists a better idea of what the structures + originally looked like. By 1976, 30% of Machu Picchu had been restored; + restoration continues today.

+ +

Machu Picchu was declared a Peruvian Historical Sanctuary in 1981 and a + UNESCO World Heritage Site in 1983. In 2007, Machu Picchu was voted one of + the New Seven Wonders of the World in a worldwide Internet poll.

+ +

Source Wikipedia

+ + + + diff --git a/examples/style.css b/examples/style.css new file mode 100644 index 00000000..a4af4d49 --- /dev/null +++ b/examples/style.css @@ -0,0 +1,48 @@ +body { + margin: 0; +} +h1, h2 { + font-family: 'Lora', serif; +} +h1 { font-size: 2rem; } +h2 { font-size: 1.5rem; } + +p { + font-family: 'Open Sans', sans-serif; + font-size: 0.9rem; +} + +h1, h2, p { + margin: 1rem; +} + +p.caption { + font-size: 0.7rem + margin-bottom: 0; + margin-top: 0; + font-family: 'Lora', serif; +} + +iframe { border: 0; } + + +/* MOBILE */ +@media screen and (max-width: 600px) { + iframe { + height: 240px; + } +} +/* DESKTOP */ +@media screen and (min-width: 600px) { + iframe { + height: 400px; + width: 700px; + margin: 1rem auto; + display: block; + } + h1, h2, p { + width: 700px; + margin: 1rem auto; + } +} + diff --git a/examples/video/congo_2048.jpg b/examples/video/congo_2048.jpg new file mode 100755 index 00000000..4292cc96 Binary files /dev/null and b/examples/video/congo_2048.jpg differ diff --git a/examples/video/congo_2048.mp4 b/examples/video/congo_2048.mp4 new file mode 100644 index 00000000..56d64250 Binary files /dev/null and b/examples/video/congo_2048.mp4 differ diff --git a/examples/video/index.html b/examples/video/index.html new file mode 100644 index 00000000..df74a0fc --- /dev/null +++ b/examples/video/index.html @@ -0,0 +1,48 @@ + + + + VR View - video example + + + + + + + + + +

Gorillas

+

The great apes from Central Africa

+

Gorillas are ground-dwelling, predominantly herbivorous apes that inhabit + the forests of central Africa.

+ +

The 360 video below shows gorillas in the Congo rainforest.

+ + + + +

The eponymous genus Gorilla is divided into two species: the eastern + gorillas and the western gorillas, and either four or five subspecies. They + are the largest living primates by physical size.

+ +

The DNA of gorillas is highly similar to that of humans, from 95–99% + depending on what is counted, and they are the next closest living relatives + to humans after the chimpanzees and bonobos.

+ +

Gorillas' natural habitats cover tropical or subtropical forests in + Africa. Although their range covers a small percentage of Africa, gorillas + cover a wide range of elevations. The mountain gorilla inhabits the + Albertine Rift montane cloud forests of the Virunga Volcanoes, ranging in + altitude from 2,200–4,300 metres (7,200–14,100 ft). Lowland gorillas live in + dense forests and lowland swamps and marshes as low as sea level, with + western lowland gorillas living in Central West African countries and + eastern lowland gorillas living in the Democratic Republic of the Congo near + its border with Rwanda.

+ +

Source Wikipedia

+ + + + diff --git a/images/ic_info_outline_black_24dp.svg b/images/ic_info_outline_black_24dp.svg new file mode 100644 index 00000000..1130cbaf --- /dev/null +++ b/images/ic_info_outline_black_24dp.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/images/loading.gif b/images/loading.gif new file mode 100644 index 00000000..b4ae3adc Binary files /dev/null and b/images/loading.gif differ diff --git a/index.html b/index.html new file mode 100644 index 00000000..8f644df4 --- /dev/null +++ b/index.html @@ -0,0 +1,26 @@ + + + + VR view + + + + + + + + +
+
+

Error

+

An unknown error occurred.

+
+
+ + + + + + + + diff --git a/node_modules/es6-promise/CHANGELOG.md b/node_modules/es6-promise/CHANGELOG.md new file mode 100644 index 00000000..9c6de4af --- /dev/null +++ b/node_modules/es6-promise/CHANGELOG.md @@ -0,0 +1,50 @@ +# Master + +* improve performance of Promise.all when it encounters a non-promise input object input +* then/resolve tamper protection +* reduce AST size of promise constructor, to facilitate more inlining +* Update README.md with details about PhantomJS requirement for running tests +* Mangle and compress the minified version + +# 3.0.1 + +* no longer include dist/test in npm releases + +# 3.0.0 + +* use nextTick() instead of setImmediate() to schedule microtasks with node 0.10. Later versions of + nodes are not affected as they were already using nextTick(). Note that using nextTick() might + trigger a depreciation warning on 0.10 as described at https://github.com/cujojs/when/issues/410. + The reason why nextTick() is preferred is that is setImmediate() would schedule a macrotask + instead of a microtask and might result in a different scheduling. + If needed you can revert to the former behavior as follow: + + var Promise = require('es6-promise').Promise; + Promise._setScheduler(setImmediate); + +# 2.3.0 + +* #121: Ability to override the internal asap implementation +* #120: Use an ascii character for an apostrophe, for source maps + +# 2.2.0 + +* #116: Expose asap() and a way to override the scheduling mechanism on Promise +* Lock to v0.2.3 of ember-cli + +# 2.1.1 + +* Fix #100 via #105: tell browserify to ignore vertx require +* Fix #101 via #102: "follow thenable state, not own state" + +# 2.1.0 + +* ? (see the commit log) + +# 2.0.0 + +* re-sync with RSVP. Many large performance improvements and bugfixes. + +# 1.0.0 + +* first subset of RSVP diff --git a/node_modules/es6-promise/LICENSE b/node_modules/es6-promise/LICENSE new file mode 100644 index 00000000..954ec599 --- /dev/null +++ b/node_modules/es6-promise/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/es6-promise/README.md b/node_modules/es6-promise/README.md new file mode 100644 index 00000000..a7611c27 --- /dev/null +++ b/node_modules/es6-promise/README.md @@ -0,0 +1,74 @@ +# ES6-Promise (subset of [rsvp.js](https://github.com/tildeio/rsvp.js)) + +This is a polyfill of the [ES6 Promise](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-promise-constructor). The implementation is a subset of [rsvp.js](https://github.com/tildeio/rsvp.js), if you're wanting extra features and more debugging options, check out the [full library](https://github.com/tildeio/rsvp.js). + +For API details and how to use promises, see the JavaScript Promises HTML5Rocks article. + +## Downloads + +* [es6-promise](https://raw.githubusercontent.com/jakearchibald/es6-promise/master/dist/es6-promise.js) +* [es6-promise-min](https://raw.githubusercontent.com/jakearchibald/es6-promise/master/dist/es6-promise.min.js) + +## Node.js + +To install: + +```sh +npm install es6-promise +``` + +To use: + +```js +var Promise = require('es6-promise').Promise; +``` + +## Bower + +To install: + +```sh +bower install es6-promise --save +``` + + +## Usage in IE<9 + +`catch` is a reserved word in IE<9, meaning `promise.catch(func)` throws a syntax error. To work around this, you can use a string to access the property as shown in the following example. + +However, please remember that such technique is already provided by most common minifiers, making the resulting code safe for old browsers and production: + +```js +promise['catch'](function(err) { + // ... +}); +``` + +Or use `.then` instead: + +```js +promise.then(undefined, function(err) { + // ... +}); +``` + +## Auto-polyfill + +To polyfill the global environment (either in Node or in the browser via CommonJS) use the following code snippet: + +```js +require('es6-promise').polyfill(); +``` + +Notice that we don't assign the result of `polyfill()` to any variable. The `polyfill()` method will patch the global environment (in this case to the `Promise` name) when called. + +## Building & Testing + +You will need to have PhantomJS installed globally in order to run the tests. + +`npm install -g phantomjs` + +* `npm run build` to build +* `npm test` to run tests +* `npm start` to run a build watcher, and webserver to test +* `npm run test:server` for a testem test runner and watching builder diff --git a/node_modules/es6-promise/dist/es6-promise.js b/node_modules/es6-promise/dist/es6-promise.js new file mode 100644 index 00000000..ab815773 --- /dev/null +++ b/node_modules/es6-promise/dist/es6-promise.js @@ -0,0 +1,954 @@ +/*! + * @overview es6-promise - a tiny implementation of Promises/A+. + * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) + * @license Licensed under MIT license + * See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE + * @version 3.1.2 + */ + +(function() { + "use strict"; + function lib$es6$promise$utils$$objectOrFunction(x) { + return typeof x === 'function' || (typeof x === 'object' && x !== null); + } + + function lib$es6$promise$utils$$isFunction(x) { + return typeof x === 'function'; + } + + function lib$es6$promise$utils$$isMaybeThenable(x) { + return typeof x === 'object' && x !== null; + } + + var lib$es6$promise$utils$$_isArray; + if (!Array.isArray) { + lib$es6$promise$utils$$_isArray = function (x) { + return Object.prototype.toString.call(x) === '[object Array]'; + }; + } else { + lib$es6$promise$utils$$_isArray = Array.isArray; + } + + var lib$es6$promise$utils$$isArray = lib$es6$promise$utils$$_isArray; + var lib$es6$promise$asap$$len = 0; + var lib$es6$promise$asap$$vertxNext; + var lib$es6$promise$asap$$customSchedulerFn; + + var lib$es6$promise$asap$$asap = function asap(callback, arg) { + lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len] = callback; + lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len + 1] = arg; + lib$es6$promise$asap$$len += 2; + if (lib$es6$promise$asap$$len === 2) { + // If len is 2, that means that we need to schedule an async flush. + // If additional callbacks are queued before the queue is flushed, they + // will be processed by this flush that we are scheduling. + if (lib$es6$promise$asap$$customSchedulerFn) { + lib$es6$promise$asap$$customSchedulerFn(lib$es6$promise$asap$$flush); + } else { + lib$es6$promise$asap$$scheduleFlush(); + } + } + } + + function lib$es6$promise$asap$$setScheduler(scheduleFn) { + lib$es6$promise$asap$$customSchedulerFn = scheduleFn; + } + + function lib$es6$promise$asap$$setAsap(asapFn) { + lib$es6$promise$asap$$asap = asapFn; + } + + var lib$es6$promise$asap$$browserWindow = (typeof window !== 'undefined') ? window : undefined; + var lib$es6$promise$asap$$browserGlobal = lib$es6$promise$asap$$browserWindow || {}; + var lib$es6$promise$asap$$BrowserMutationObserver = lib$es6$promise$asap$$browserGlobal.MutationObserver || lib$es6$promise$asap$$browserGlobal.WebKitMutationObserver; + var lib$es6$promise$asap$$isNode = typeof process !== 'undefined' && {}.toString.call(process) === '[object process]'; + + // test for web worker but not in IE10 + var lib$es6$promise$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' && + typeof importScripts !== 'undefined' && + typeof MessageChannel !== 'undefined'; + + // node + function lib$es6$promise$asap$$useNextTick() { + // node version 0.10.x displays a deprecation warning when nextTick is used recursively + // see https://github.com/cujojs/when/issues/410 for details + return function() { + process.nextTick(lib$es6$promise$asap$$flush); + }; + } + + // vertx + function lib$es6$promise$asap$$useVertxTimer() { + return function() { + lib$es6$promise$asap$$vertxNext(lib$es6$promise$asap$$flush); + }; + } + + function lib$es6$promise$asap$$useMutationObserver() { + var iterations = 0; + var observer = new lib$es6$promise$asap$$BrowserMutationObserver(lib$es6$promise$asap$$flush); + var node = document.createTextNode(''); + observer.observe(node, { characterData: true }); + + return function() { + node.data = (iterations = ++iterations % 2); + }; + } + + // web worker + function lib$es6$promise$asap$$useMessageChannel() { + var channel = new MessageChannel(); + channel.port1.onmessage = lib$es6$promise$asap$$flush; + return function () { + channel.port2.postMessage(0); + }; + } + + function lib$es6$promise$asap$$useSetTimeout() { + return function() { + setTimeout(lib$es6$promise$asap$$flush, 1); + }; + } + + var lib$es6$promise$asap$$queue = new Array(1000); + function lib$es6$promise$asap$$flush() { + for (var i = 0; i < lib$es6$promise$asap$$len; i+=2) { + var callback = lib$es6$promise$asap$$queue[i]; + var arg = lib$es6$promise$asap$$queue[i+1]; + + callback(arg); + + lib$es6$promise$asap$$queue[i] = undefined; + lib$es6$promise$asap$$queue[i+1] = undefined; + } + + lib$es6$promise$asap$$len = 0; + } + + function lib$es6$promise$asap$$attemptVertx() { + try { + var r = require; + var vertx = r('vertx'); + lib$es6$promise$asap$$vertxNext = vertx.runOnLoop || vertx.runOnContext; + return lib$es6$promise$asap$$useVertxTimer(); + } catch(e) { + return lib$es6$promise$asap$$useSetTimeout(); + } + } + + var lib$es6$promise$asap$$scheduleFlush; + // Decide what async method to use to triggering processing of queued callbacks: + if (lib$es6$promise$asap$$isNode) { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useNextTick(); + } else if (lib$es6$promise$asap$$BrowserMutationObserver) { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMutationObserver(); + } else if (lib$es6$promise$asap$$isWorker) { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMessageChannel(); + } else if (lib$es6$promise$asap$$browserWindow === undefined && typeof require === 'function') { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$attemptVertx(); + } else { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useSetTimeout(); + } + function lib$es6$promise$then$$then(onFulfillment, onRejection) { + var parent = this; + var state = parent._state; + + if (state === lib$es6$promise$$internal$$FULFILLED && !onFulfillment || state === lib$es6$promise$$internal$$REJECTED && !onRejection) { + return this; + } + + var child = new this.constructor(lib$es6$promise$$internal$$noop); + var result = parent._result; + + if (state) { + var callback = arguments[state - 1]; + lib$es6$promise$asap$$asap(function(){ + lib$es6$promise$$internal$$invokeCallback(state, child, callback, result); + }); + } else { + lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection); + } + + return child; + } + var lib$es6$promise$then$$default = lib$es6$promise$then$$then; + function lib$es6$promise$promise$resolve$$resolve(object) { + /*jshint validthis:true */ + var Constructor = this; + + if (object && typeof object === 'object' && object.constructor === Constructor) { + return object; + } + + var promise = new Constructor(lib$es6$promise$$internal$$noop); + lib$es6$promise$$internal$$resolve(promise, object); + return promise; + } + var lib$es6$promise$promise$resolve$$default = lib$es6$promise$promise$resolve$$resolve; + + function lib$es6$promise$$internal$$noop() {} + + var lib$es6$promise$$internal$$PENDING = void 0; + var lib$es6$promise$$internal$$FULFILLED = 1; + var lib$es6$promise$$internal$$REJECTED = 2; + + var lib$es6$promise$$internal$$GET_THEN_ERROR = new lib$es6$promise$$internal$$ErrorObject(); + + function lib$es6$promise$$internal$$selfFulfillment() { + return new TypeError("You cannot resolve a promise with itself"); + } + + function lib$es6$promise$$internal$$cannotReturnOwn() { + return new TypeError('A promises callback cannot return that same promise.'); + } + + function lib$es6$promise$$internal$$getThen(promise) { + try { + return promise.then; + } catch(error) { + lib$es6$promise$$internal$$GET_THEN_ERROR.error = error; + return lib$es6$promise$$internal$$GET_THEN_ERROR; + } + } + + function lib$es6$promise$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) { + try { + then.call(value, fulfillmentHandler, rejectionHandler); + } catch(e) { + return e; + } + } + + function lib$es6$promise$$internal$$handleForeignThenable(promise, thenable, then) { + lib$es6$promise$asap$$asap(function(promise) { + var sealed = false; + var error = lib$es6$promise$$internal$$tryThen(then, thenable, function(value) { + if (sealed) { return; } + sealed = true; + if (thenable !== value) { + lib$es6$promise$$internal$$resolve(promise, value); + } else { + lib$es6$promise$$internal$$fulfill(promise, value); + } + }, function(reason) { + if (sealed) { return; } + sealed = true; + + lib$es6$promise$$internal$$reject(promise, reason); + }, 'Settle: ' + (promise._label || ' unknown promise')); + + if (!sealed && error) { + sealed = true; + lib$es6$promise$$internal$$reject(promise, error); + } + }, promise); + } + + function lib$es6$promise$$internal$$handleOwnThenable(promise, thenable) { + if (thenable._state === lib$es6$promise$$internal$$FULFILLED) { + lib$es6$promise$$internal$$fulfill(promise, thenable._result); + } else if (thenable._state === lib$es6$promise$$internal$$REJECTED) { + lib$es6$promise$$internal$$reject(promise, thenable._result); + } else { + lib$es6$promise$$internal$$subscribe(thenable, undefined, function(value) { + lib$es6$promise$$internal$$resolve(promise, value); + }, function(reason) { + lib$es6$promise$$internal$$reject(promise, reason); + }); + } + } + + function lib$es6$promise$$internal$$handleMaybeThenable(promise, maybeThenable, then) { + if (maybeThenable.constructor === promise.constructor && + then === lib$es6$promise$then$$default && + constructor.resolve === lib$es6$promise$promise$resolve$$default) { + lib$es6$promise$$internal$$handleOwnThenable(promise, maybeThenable); + } else { + if (then === lib$es6$promise$$internal$$GET_THEN_ERROR) { + lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$GET_THEN_ERROR.error); + } else if (then === undefined) { + lib$es6$promise$$internal$$fulfill(promise, maybeThenable); + } else if (lib$es6$promise$utils$$isFunction(then)) { + lib$es6$promise$$internal$$handleForeignThenable(promise, maybeThenable, then); + } else { + lib$es6$promise$$internal$$fulfill(promise, maybeThenable); + } + } + } + + function lib$es6$promise$$internal$$resolve(promise, value) { + if (promise === value) { + lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$selfFulfillment()); + } else if (lib$es6$promise$utils$$objectOrFunction(value)) { + lib$es6$promise$$internal$$handleMaybeThenable(promise, value, lib$es6$promise$$internal$$getThen(value)); + } else { + lib$es6$promise$$internal$$fulfill(promise, value); + } + } + + function lib$es6$promise$$internal$$publishRejection(promise) { + if (promise._onerror) { + promise._onerror(promise._result); + } + + lib$es6$promise$$internal$$publish(promise); + } + + function lib$es6$promise$$internal$$fulfill(promise, value) { + if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; } + + promise._result = value; + promise._state = lib$es6$promise$$internal$$FULFILLED; + + if (promise._subscribers.length !== 0) { + lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, promise); + } + } + + function lib$es6$promise$$internal$$reject(promise, reason) { + if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; } + promise._state = lib$es6$promise$$internal$$REJECTED; + promise._result = reason; + + lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publishRejection, promise); + } + + function lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection) { + var subscribers = parent._subscribers; + var length = subscribers.length; + + parent._onerror = null; + + subscribers[length] = child; + subscribers[length + lib$es6$promise$$internal$$FULFILLED] = onFulfillment; + subscribers[length + lib$es6$promise$$internal$$REJECTED] = onRejection; + + if (length === 0 && parent._state) { + lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, parent); + } + } + + function lib$es6$promise$$internal$$publish(promise) { + var subscribers = promise._subscribers; + var settled = promise._state; + + if (subscribers.length === 0) { return; } + + var child, callback, detail = promise._result; + + for (var i = 0; i < subscribers.length; i += 3) { + child = subscribers[i]; + callback = subscribers[i + settled]; + + if (child) { + lib$es6$promise$$internal$$invokeCallback(settled, child, callback, detail); + } else { + callback(detail); + } + } + + promise._subscribers.length = 0; + } + + function lib$es6$promise$$internal$$ErrorObject() { + this.error = null; + } + + var lib$es6$promise$$internal$$TRY_CATCH_ERROR = new lib$es6$promise$$internal$$ErrorObject(); + + function lib$es6$promise$$internal$$tryCatch(callback, detail) { + try { + return callback(detail); + } catch(e) { + lib$es6$promise$$internal$$TRY_CATCH_ERROR.error = e; + return lib$es6$promise$$internal$$TRY_CATCH_ERROR; + } + } + + function lib$es6$promise$$internal$$invokeCallback(settled, promise, callback, detail) { + var hasCallback = lib$es6$promise$utils$$isFunction(callback), + value, error, succeeded, failed; + + if (hasCallback) { + value = lib$es6$promise$$internal$$tryCatch(callback, detail); + + if (value === lib$es6$promise$$internal$$TRY_CATCH_ERROR) { + failed = true; + error = value.error; + value = null; + } else { + succeeded = true; + } + + if (promise === value) { + lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$cannotReturnOwn()); + return; + } + + } else { + value = detail; + succeeded = true; + } + + if (promise._state !== lib$es6$promise$$internal$$PENDING) { + // noop + } else if (hasCallback && succeeded) { + lib$es6$promise$$internal$$resolve(promise, value); + } else if (failed) { + lib$es6$promise$$internal$$reject(promise, error); + } else if (settled === lib$es6$promise$$internal$$FULFILLED) { + lib$es6$promise$$internal$$fulfill(promise, value); + } else if (settled === lib$es6$promise$$internal$$REJECTED) { + lib$es6$promise$$internal$$reject(promise, value); + } + } + + function lib$es6$promise$$internal$$initializePromise(promise, resolver) { + try { + resolver(function resolvePromise(value){ + lib$es6$promise$$internal$$resolve(promise, value); + }, function rejectPromise(reason) { + lib$es6$promise$$internal$$reject(promise, reason); + }); + } catch(e) { + lib$es6$promise$$internal$$reject(promise, e); + } + } + + function lib$es6$promise$promise$all$$all(entries) { + return new lib$es6$promise$enumerator$$default(this, entries).promise; + } + var lib$es6$promise$promise$all$$default = lib$es6$promise$promise$all$$all; + function lib$es6$promise$promise$race$$race(entries) { + /*jshint validthis:true */ + var Constructor = this; + + var promise = new Constructor(lib$es6$promise$$internal$$noop); + + if (!lib$es6$promise$utils$$isArray(entries)) { + lib$es6$promise$$internal$$reject(promise, new TypeError('You must pass an array to race.')); + return promise; + } + + var length = entries.length; + + function onFulfillment(value) { + lib$es6$promise$$internal$$resolve(promise, value); + } + + function onRejection(reason) { + lib$es6$promise$$internal$$reject(promise, reason); + } + + for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) { + lib$es6$promise$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection); + } + + return promise; + } + var lib$es6$promise$promise$race$$default = lib$es6$promise$promise$race$$race; + function lib$es6$promise$promise$reject$$reject(reason) { + /*jshint validthis:true */ + var Constructor = this; + var promise = new Constructor(lib$es6$promise$$internal$$noop); + lib$es6$promise$$internal$$reject(promise, reason); + return promise; + } + var lib$es6$promise$promise$reject$$default = lib$es6$promise$promise$reject$$reject; + + var lib$es6$promise$promise$$counter = 0; + + function lib$es6$promise$promise$$needsResolver() { + throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); + } + + function lib$es6$promise$promise$$needsNew() { + throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); + } + + var lib$es6$promise$promise$$default = lib$es6$promise$promise$$Promise; + /** + Promise objects represent the eventual result of an asynchronous operation. The + primary way of interacting with a promise is through its `then` method, which + registers callbacks to receive either a promise's eventual value or the reason + why the promise cannot be fulfilled. + + Terminology + ----------- + + - `promise` is an object or function with a `then` method whose behavior conforms to this specification. + - `thenable` is an object or function that defines a `then` method. + - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). + - `exception` is a value that is thrown using the throw statement. + - `reason` is a value that indicates why a promise was rejected. + - `settled` the final resting state of a promise, fulfilled or rejected. + + A promise can be in one of three states: pending, fulfilled, or rejected. + + Promises that are fulfilled have a fulfillment value and are in the fulfilled + state. Promises that are rejected have a rejection reason and are in the + rejected state. A fulfillment value is never a thenable. + + Promises can also be said to *resolve* a value. If this value is also a + promise, then the original promise's settled state will match the value's + settled state. So a promise that *resolves* a promise that rejects will + itself reject, and a promise that *resolves* a promise that fulfills will + itself fulfill. + + + Basic Usage: + ------------ + + ```js + var promise = new Promise(function(resolve, reject) { + // on success + resolve(value); + + // on failure + reject(reason); + }); + + promise.then(function(value) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Advanced Usage: + --------------- + + Promises shine when abstracting away asynchronous interactions such as + `XMLHttpRequest`s. + + ```js + function getJSON(url) { + return new Promise(function(resolve, reject){ + var xhr = new XMLHttpRequest(); + + xhr.open('GET', url); + xhr.onreadystatechange = handler; + xhr.responseType = 'json'; + xhr.setRequestHeader('Accept', 'application/json'); + xhr.send(); + + function handler() { + if (this.readyState === this.DONE) { + if (this.status === 200) { + resolve(this.response); + } else { + reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); + } + } + }; + }); + } + + getJSON('/posts.json').then(function(json) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Unlike callbacks, promises are great composable primitives. + + ```js + Promise.all([ + getJSON('/posts'), + getJSON('/comments') + ]).then(function(values){ + values[0] // => postsJSON + values[1] // => commentsJSON + + return values; + }); + ``` + + @class Promise + @param {function} resolver + Useful for tooling. + @constructor + */ + function lib$es6$promise$promise$$Promise(resolver) { + this._id = lib$es6$promise$promise$$counter++; + this._state = undefined; + this._result = undefined; + this._subscribers = []; + + if (lib$es6$promise$$internal$$noop !== resolver) { + typeof resolver !== 'function' && lib$es6$promise$promise$$needsResolver(); + this instanceof lib$es6$promise$promise$$Promise ? lib$es6$promise$$internal$$initializePromise(this, resolver) : lib$es6$promise$promise$$needsNew(); + } + } + + lib$es6$promise$promise$$Promise.all = lib$es6$promise$promise$all$$default; + lib$es6$promise$promise$$Promise.race = lib$es6$promise$promise$race$$default; + lib$es6$promise$promise$$Promise.resolve = lib$es6$promise$promise$resolve$$default; + lib$es6$promise$promise$$Promise.reject = lib$es6$promise$promise$reject$$default; + lib$es6$promise$promise$$Promise._setScheduler = lib$es6$promise$asap$$setScheduler; + lib$es6$promise$promise$$Promise._setAsap = lib$es6$promise$asap$$setAsap; + lib$es6$promise$promise$$Promise._asap = lib$es6$promise$asap$$asap; + + lib$es6$promise$promise$$Promise.prototype = { + constructor: lib$es6$promise$promise$$Promise, + + /** + The primary way of interacting with a promise is through its `then` method, + which registers callbacks to receive either a promise's eventual value or the + reason why the promise cannot be fulfilled. + + ```js + findUser().then(function(user){ + // user is available + }, function(reason){ + // user is unavailable, and you are given the reason why + }); + ``` + + Chaining + -------- + + The return value of `then` is itself a promise. This second, 'downstream' + promise is resolved with the return value of the first promise's fulfillment + or rejection handler, or rejected if the handler throws an exception. + + ```js + findUser().then(function (user) { + return user.name; + }, function (reason) { + return 'default name'; + }).then(function (userName) { + // If `findUser` fulfilled, `userName` will be the user's name, otherwise it + // will be `'default name'` + }); + + findUser().then(function (user) { + throw new Error('Found user, but still unhappy'); + }, function (reason) { + throw new Error('`findUser` rejected and we're unhappy'); + }).then(function (value) { + // never reached + }, function (reason) { + // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. + // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. + }); + ``` + If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. + + ```js + findUser().then(function (user) { + throw new PedagogicalException('Upstream error'); + }).then(function (value) { + // never reached + }).then(function (value) { + // never reached + }, function (reason) { + // The `PedgagocialException` is propagated all the way down to here + }); + ``` + + Assimilation + ------------ + + Sometimes the value you want to propagate to a downstream promise can only be + retrieved asynchronously. This can be achieved by returning a promise in the + fulfillment or rejection handler. The downstream promise will then be pending + until the returned promise is settled. This is called *assimilation*. + + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // The user's comments are now available + }); + ``` + + If the assimliated promise rejects, then the downstream promise will also reject. + + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // If `findCommentsByAuthor` fulfills, we'll have the value here + }, function (reason) { + // If `findCommentsByAuthor` rejects, we'll have the reason here + }); + ``` + + Simple Example + -------------- + + Synchronous Example + + ```javascript + var result; + + try { + result = findResult(); + // success + } catch(reason) { + // failure + } + ``` + + Errback Example + + ```js + findResult(function(result, err){ + if (err) { + // failure + } else { + // success + } + }); + ``` + + Promise Example; + + ```javascript + findResult().then(function(result){ + // success + }, function(reason){ + // failure + }); + ``` + + Advanced Example + -------------- + + Synchronous Example + + ```javascript + var author, books; + + try { + author = findAuthor(); + books = findBooksByAuthor(author); + // success + } catch(reason) { + // failure + } + ``` + + Errback Example + + ```js + + function foundBooks(books) { + + } + + function failure(reason) { + + } + + findAuthor(function(author, err){ + if (err) { + failure(err); + // failure + } else { + try { + findBoooksByAuthor(author, function(books, err) { + if (err) { + failure(err); + } else { + try { + foundBooks(books); + } catch(reason) { + failure(reason); + } + } + }); + } catch(error) { + failure(err); + } + // success + } + }); + ``` + + Promise Example; + + ```javascript + findAuthor(). + then(findBooksByAuthor). + then(function(books){ + // found books + }).catch(function(reason){ + // something went wrong + }); + ``` + + @method then + @param {Function} onFulfilled + @param {Function} onRejected + Useful for tooling. + @return {Promise} + */ + then: lib$es6$promise$then$$default, + + /** + `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same + as the catch block of a try/catch statement. + + ```js + function findAuthor(){ + throw new Error('couldn't find that author'); + } + + // synchronous + try { + findAuthor(); + } catch(reason) { + // something went wrong + } + + // async with promises + findAuthor().catch(function(reason){ + // something went wrong + }); + ``` + + @method catch + @param {Function} onRejection + Useful for tooling. + @return {Promise} + */ + 'catch': function(onRejection) { + return this.then(null, onRejection); + } + }; + var lib$es6$promise$enumerator$$default = lib$es6$promise$enumerator$$Enumerator; + function lib$es6$promise$enumerator$$Enumerator(Constructor, input) { + this._instanceConstructor = Constructor; + this.promise = new Constructor(lib$es6$promise$$internal$$noop); + + if (Array.isArray(input)) { + this._input = input; + this.length = input.length; + this._remaining = input.length; + + this._result = new Array(this.length); + + if (this.length === 0) { + lib$es6$promise$$internal$$fulfill(this.promise, this._result); + } else { + this.length = this.length || 0; + this._enumerate(); + if (this._remaining === 0) { + lib$es6$promise$$internal$$fulfill(this.promise, this._result); + } + } + } else { + lib$es6$promise$$internal$$reject(this.promise, this._validationError()); + } + } + + lib$es6$promise$enumerator$$Enumerator.prototype._validationError = function() { + return new Error('Array Methods must be provided an Array'); + }; + + lib$es6$promise$enumerator$$Enumerator.prototype._enumerate = function() { + var length = this.length; + var input = this._input; + + for (var i = 0; this._state === lib$es6$promise$$internal$$PENDING && i < length; i++) { + this._eachEntry(input[i], i); + } + }; + + lib$es6$promise$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) { + var c = this._instanceConstructor; + var resolve = c.resolve; + + if (resolve === lib$es6$promise$promise$resolve$$default) { + var then = lib$es6$promise$$internal$$getThen(entry); + + if (then === lib$es6$promise$then$$default && + entry._state !== lib$es6$promise$$internal$$PENDING) { + this._settledAt(entry._state, i, entry._result); + } else if (typeof then !== 'function') { + this._remaining--; + this._result[i] = entry; + } else if (c === lib$es6$promise$promise$$default) { + var promise = new c(lib$es6$promise$$internal$$noop); + lib$es6$promise$$internal$$handleMaybeThenable(promise, entry, then); + this._willSettleAt(promise, i); + } else { + this._willSettleAt(new c(function(resolve) { resolve(entry); }), i); + } + } else { + this._willSettleAt(resolve(entry), i); + } + }; + + lib$es6$promise$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) { + var promise = this.promise; + + if (promise._state === lib$es6$promise$$internal$$PENDING) { + this._remaining--; + + if (state === lib$es6$promise$$internal$$REJECTED) { + lib$es6$promise$$internal$$reject(promise, value); + } else { + this._result[i] = value; + } + } + + if (this._remaining === 0) { + lib$es6$promise$$internal$$fulfill(promise, this._result); + } + }; + + lib$es6$promise$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) { + var enumerator = this; + + lib$es6$promise$$internal$$subscribe(promise, undefined, function(value) { + enumerator._settledAt(lib$es6$promise$$internal$$FULFILLED, i, value); + }, function(reason) { + enumerator._settledAt(lib$es6$promise$$internal$$REJECTED, i, reason); + }); + }; + function lib$es6$promise$polyfill$$polyfill() { + var local; + + if (typeof global !== 'undefined') { + local = global; + } else if (typeof self !== 'undefined') { + local = self; + } else { + try { + local = Function('return this')(); + } catch (e) { + throw new Error('polyfill failed because global object is unavailable in this environment'); + } + } + + var P = local.Promise; + + if (P && Object.prototype.toString.call(P.resolve()) === '[object Promise]' && !P.cast) { + return; + } + + local.Promise = lib$es6$promise$promise$$default; + } + var lib$es6$promise$polyfill$$default = lib$es6$promise$polyfill$$polyfill; + + var lib$es6$promise$umd$$ES6Promise = { + 'Promise': lib$es6$promise$promise$$default, + 'polyfill': lib$es6$promise$polyfill$$default + }; + + /* global define:true module:true window: true */ + if (typeof define === 'function' && define['amd']) { + define(function() { return lib$es6$promise$umd$$ES6Promise; }); + } else if (typeof module !== 'undefined' && module['exports']) { + module['exports'] = lib$es6$promise$umd$$ES6Promise; + } else if (typeof this !== 'undefined') { + this['ES6Promise'] = lib$es6$promise$umd$$ES6Promise; + } + + lib$es6$promise$polyfill$$default(); +}).call(this); + diff --git a/node_modules/es6-promise/dist/es6-promise.min.js b/node_modules/es6-promise/dist/es6-promise.min.js new file mode 100644 index 00000000..9b0a6c45 --- /dev/null +++ b/node_modules/es6-promise/dist/es6-promise.min.js @@ -0,0 +1,9 @@ +/*! + * @overview es6-promise - a tiny implementation of Promises/A+. + * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) + * @license Licensed under MIT license + * See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE + * @version 3.1.2 + */ + +(function(){"use strict";function t(t){return"function"==typeof t||"object"==typeof t&&null!==t}function e(t){return"function"==typeof t}function n(t){W=t}function r(t){H=t}function o(){return function(){process.nextTick(a)}}function i(){return function(){U(a)}}function s(){var t=0,e=new Q(a),n=document.createTextNode("");return e.observe(n,{characterData:!0}),function(){n.data=t=++t%2}}function u(){var t=new MessageChannel;return t.port1.onmessage=a,function(){t.port2.postMessage(0)}}function c(){return function(){setTimeout(a,1)}}function a(){for(var t=0;G>t;t+=2){var e=X[t],n=X[t+1];e(n),X[t]=void 0,X[t+1]=void 0}G=0}function f(){try{var t=require,e=t("vertx");return U=e.runOnLoop||e.runOnContext,i()}catch(n){return c()}}function l(t,e){var n=this,r=n._state;if(r===et&&!t||r===nt&&!e)return this;var o=new this.constructor(p),i=n._result;if(r){var s=arguments[r-1];H(function(){C(r,o,s,i)})}else j(n,o,t,e);return o}function h(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var n=new e(p);return g(n,t),n}function p(){}function _(){return new TypeError("You cannot resolve a promise with itself")}function v(){return new TypeError("A promises callback cannot return that same promise.")}function d(t){try{return t.then}catch(e){return rt.error=e,rt}}function y(t,e,n,r){try{t.call(e,n,r)}catch(o){return o}}function m(t,e,n){H(function(t){var r=!1,o=y(n,e,function(n){r||(r=!0,e!==n?g(t,n):E(t,n))},function(e){r||(r=!0,S(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&o&&(r=!0,S(t,o))},t)}function w(t,e){e._state===et?E(t,e._result):e._state===nt?S(t,e._result):j(e,void 0,function(e){g(t,e)},function(e){S(t,e)})}function b(t,n,r){n.constructor===t.constructor&&r===Z&&constructor.resolve===$?w(t,n):r===rt?S(t,rt.error):void 0===r?E(t,n):e(r)?m(t,n,r):E(t,n)}function g(e,n){e===n?S(e,_()):t(n)?b(e,n,d(n)):E(e,n)}function A(t){t._onerror&&t._onerror(t._result),T(t)}function E(t,e){t._state===tt&&(t._result=e,t._state=et,0!==t._subscribers.length&&H(T,t))}function S(t,e){t._state===tt&&(t._state=nt,t._result=e,H(A,t))}function j(t,e,n,r){var o=t._subscribers,i=o.length;t._onerror=null,o[i]=e,o[i+et]=n,o[i+nt]=r,0===i&&t._state&&H(T,t)}function T(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r,o,i=t._result,s=0;ss;s++)j(r.resolve(t[s]),void 0,e,n);return o}function Y(t){var e=this,n=new e(p);return S(n,t),n}function q(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function F(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function D(t){this._id=ct++,this._state=void 0,this._result=void 0,this._subscribers=[],p!==t&&("function"!=typeof t&&q(),this instanceof D?M(this,t):F())}function K(t,e){this._instanceConstructor=t,this.promise=new t(p),Array.isArray(e)?(this._input=e,this.length=e.length,this._remaining=e.length,this._result=new Array(this.length),0===this.length?E(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&E(this.promise,this._result))):S(this.promise,this._validationError())}function L(){var t;if("undefined"!=typeof global)t=global;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;(!n||"[object Promise]"!==Object.prototype.toString.call(n.resolve())||n.cast)&&(t.Promise=at)}var N;N=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var U,W,z,B=N,G=0,H=function(t,e){X[G]=t,X[G+1]=e,G+=2,2===G&&(W?W(a):z())},I="undefined"!=typeof window?window:void 0,J=I||{},Q=J.MutationObserver||J.WebKitMutationObserver,R="undefined"!=typeof process&&"[object process]"==={}.toString.call(process),V="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,X=new Array(1e3);z=R?o():Q?s():V?u():void 0===I&&"function"==typeof require?f():c();var Z=l,$=h,tt=void 0,et=1,nt=2,rt=new P,ot=new P,it=O,st=k,ut=Y,ct=0,at=D;D.all=it,D.race=st,D.resolve=$,D.reject=ut,D._setScheduler=n,D._setAsap=r,D._asap=H,D.prototype={constructor:D,then:Z,"catch":function(t){return this.then(null,t)}};var ft=K;K.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},K.prototype._enumerate=function(){for(var t=this.length,e=this._input,n=0;this._state===tt&&t>n;n++)this._eachEntry(e[n],n)},K.prototype._eachEntry=function(t,e){var n=this._instanceConstructor,r=n.resolve;if(r===$){var o=d(t);if(o===Z&&t._state!==tt)this._settledAt(t._state,e,t._result);else if("function"!=typeof o)this._remaining--,this._result[e]=t;else if(n===at){var i=new n(p);b(i,t,o),this._willSettleAt(i,e)}else this._willSettleAt(new n(function(e){e(t)}),e)}else this._willSettleAt(r(t),e)},K.prototype._settledAt=function(t,e,n){var r=this.promise;r._state===tt&&(this._remaining--,t===nt?S(r,n):this._result[e]=n),0===this._remaining&&E(r,this._result)},K.prototype._willSettleAt=function(t,e){var n=this;j(t,void 0,function(t){n._settledAt(et,e,t)},function(t){n._settledAt(nt,e,t)})};var lt=L,ht={Promise:at,polyfill:lt};"function"==typeof define&&define.amd?define(function(){return ht}):"undefined"!=typeof module&&module.exports?module.exports=ht:"undefined"!=typeof this&&(this.ES6Promise=ht),lt()}).call(this); \ No newline at end of file diff --git a/node_modules/es6-promise/lib/es6-promise.umd.js b/node_modules/es6-promise/lib/es6-promise.umd.js new file mode 100644 index 00000000..5984f705 --- /dev/null +++ b/node_modules/es6-promise/lib/es6-promise.umd.js @@ -0,0 +1,18 @@ +import Promise from './es6-promise/promise'; +import polyfill from './es6-promise/polyfill'; + +var ES6Promise = { + 'Promise': Promise, + 'polyfill': polyfill +}; + +/* global define:true module:true window: true */ +if (typeof define === 'function' && define['amd']) { + define(function() { return ES6Promise; }); +} else if (typeof module !== 'undefined' && module['exports']) { + module['exports'] = ES6Promise; +} else if (typeof this !== 'undefined') { + this['ES6Promise'] = ES6Promise; +} + +polyfill(); diff --git a/node_modules/es6-promise/lib/es6-promise/-internal.js b/node_modules/es6-promise/lib/es6-promise/-internal.js new file mode 100644 index 00000000..543473ad --- /dev/null +++ b/node_modules/es6-promise/lib/es6-promise/-internal.js @@ -0,0 +1,257 @@ +import { + objectOrFunction, + isFunction +} from './utils'; + +import { + asap +} from './asap'; + +import originalThen from './then'; +import originalResolve from './promise/resolve'; + +function noop() {} + +var PENDING = void 0; +var FULFILLED = 1; +var REJECTED = 2; + +var GET_THEN_ERROR = new ErrorObject(); + +function selfFulfillment() { + return new TypeError("You cannot resolve a promise with itself"); +} + +function cannotReturnOwn() { + return new TypeError('A promises callback cannot return that same promise.'); +} + +function getThen(promise) { + try { + return promise.then; + } catch(error) { + GET_THEN_ERROR.error = error; + return GET_THEN_ERROR; + } +} + +function tryThen(then, value, fulfillmentHandler, rejectionHandler) { + try { + then.call(value, fulfillmentHandler, rejectionHandler); + } catch(e) { + return e; + } +} + +function handleForeignThenable(promise, thenable, then) { + asap(function(promise) { + var sealed = false; + var error = tryThen(then, thenable, function(value) { + if (sealed) { return; } + sealed = true; + if (thenable !== value) { + resolve(promise, value); + } else { + fulfill(promise, value); + } + }, function(reason) { + if (sealed) { return; } + sealed = true; + + reject(promise, reason); + }, 'Settle: ' + (promise._label || ' unknown promise')); + + if (!sealed && error) { + sealed = true; + reject(promise, error); + } + }, promise); +} + +function handleOwnThenable(promise, thenable) { + if (thenable._state === FULFILLED) { + fulfill(promise, thenable._result); + } else if (thenable._state === REJECTED) { + reject(promise, thenable._result); + } else { + subscribe(thenable, undefined, function(value) { + resolve(promise, value); + }, function(reason) { + reject(promise, reason); + }); + } +} + +function handleMaybeThenable(promise, maybeThenable, then) { + if (maybeThenable.constructor === promise.constructor && + then === originalThen && + constructor.resolve === originalResolve) { + handleOwnThenable(promise, maybeThenable); + } else { + if (then === GET_THEN_ERROR) { + reject(promise, GET_THEN_ERROR.error); + } else if (then === undefined) { + fulfill(promise, maybeThenable); + } else if (isFunction(then)) { + handleForeignThenable(promise, maybeThenable, then); + } else { + fulfill(promise, maybeThenable); + } + } +} + +function resolve(promise, value) { + if (promise === value) { + reject(promise, selfFulfillment()); + } else if (objectOrFunction(value)) { + handleMaybeThenable(promise, value, getThen(value)); + } else { + fulfill(promise, value); + } +} + +function publishRejection(promise) { + if (promise._onerror) { + promise._onerror(promise._result); + } + + publish(promise); +} + +function fulfill(promise, value) { + if (promise._state !== PENDING) { return; } + + promise._result = value; + promise._state = FULFILLED; + + if (promise._subscribers.length !== 0) { + asap(publish, promise); + } +} + +function reject(promise, reason) { + if (promise._state !== PENDING) { return; } + promise._state = REJECTED; + promise._result = reason; + + asap(publishRejection, promise); +} + +function subscribe(parent, child, onFulfillment, onRejection) { + var subscribers = parent._subscribers; + var length = subscribers.length; + + parent._onerror = null; + + subscribers[length] = child; + subscribers[length + FULFILLED] = onFulfillment; + subscribers[length + REJECTED] = onRejection; + + if (length === 0 && parent._state) { + asap(publish, parent); + } +} + +function publish(promise) { + var subscribers = promise._subscribers; + var settled = promise._state; + + if (subscribers.length === 0) { return; } + + var child, callback, detail = promise._result; + + for (var i = 0; i < subscribers.length; i += 3) { + child = subscribers[i]; + callback = subscribers[i + settled]; + + if (child) { + invokeCallback(settled, child, callback, detail); + } else { + callback(detail); + } + } + + promise._subscribers.length = 0; +} + +function ErrorObject() { + this.error = null; +} + +var TRY_CATCH_ERROR = new ErrorObject(); + +function tryCatch(callback, detail) { + try { + return callback(detail); + } catch(e) { + TRY_CATCH_ERROR.error = e; + return TRY_CATCH_ERROR; + } +} + +function invokeCallback(settled, promise, callback, detail) { + var hasCallback = isFunction(callback), + value, error, succeeded, failed; + + if (hasCallback) { + value = tryCatch(callback, detail); + + if (value === TRY_CATCH_ERROR) { + failed = true; + error = value.error; + value = null; + } else { + succeeded = true; + } + + if (promise === value) { + reject(promise, cannotReturnOwn()); + return; + } + + } else { + value = detail; + succeeded = true; + } + + if (promise._state !== PENDING) { + // noop + } else if (hasCallback && succeeded) { + resolve(promise, value); + } else if (failed) { + reject(promise, error); + } else if (settled === FULFILLED) { + fulfill(promise, value); + } else if (settled === REJECTED) { + reject(promise, value); + } +} + +function initializePromise(promise, resolver) { + try { + resolver(function resolvePromise(value){ + resolve(promise, value); + }, function rejectPromise(reason) { + reject(promise, reason); + }); + } catch(e) { + reject(promise, e); + } +} + +export { + getThen, + noop, + resolve, + reject, + fulfill, + subscribe, + publish, + publishRejection, + initializePromise, + invokeCallback, + FULFILLED, + REJECTED, + PENDING, + handleMaybeThenable +}; diff --git a/node_modules/es6-promise/lib/es6-promise/asap.js b/node_modules/es6-promise/lib/es6-promise/asap.js new file mode 100644 index 00000000..45f25c75 --- /dev/null +++ b/node_modules/es6-promise/lib/es6-promise/asap.js @@ -0,0 +1,119 @@ +var len = 0; +var vertxNext; +var customSchedulerFn; + +export var asap = function asap(callback, arg) { + queue[len] = callback; + queue[len + 1] = arg; + len += 2; + if (len === 2) { + // If len is 2, that means that we need to schedule an async flush. + // If additional callbacks are queued before the queue is flushed, they + // will be processed by this flush that we are scheduling. + if (customSchedulerFn) { + customSchedulerFn(flush); + } else { + scheduleFlush(); + } + } +} + +export function setScheduler(scheduleFn) { + customSchedulerFn = scheduleFn; +} + +export function setAsap(asapFn) { + asap = asapFn; +} + +var browserWindow = (typeof window !== 'undefined') ? window : undefined; +var browserGlobal = browserWindow || {}; +var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; +var isNode = typeof process !== 'undefined' && {}.toString.call(process) === '[object process]'; + +// test for web worker but not in IE10 +var isWorker = typeof Uint8ClampedArray !== 'undefined' && + typeof importScripts !== 'undefined' && + typeof MessageChannel !== 'undefined'; + +// node +function useNextTick() { + // node version 0.10.x displays a deprecation warning when nextTick is used recursively + // see https://github.com/cujojs/when/issues/410 for details + return function() { + process.nextTick(flush); + }; +} + +// vertx +function useVertxTimer() { + return function() { + vertxNext(flush); + }; +} + +function useMutationObserver() { + var iterations = 0; + var observer = new BrowserMutationObserver(flush); + var node = document.createTextNode(''); + observer.observe(node, { characterData: true }); + + return function() { + node.data = (iterations = ++iterations % 2); + }; +} + +// web worker +function useMessageChannel() { + var channel = new MessageChannel(); + channel.port1.onmessage = flush; + return function () { + channel.port2.postMessage(0); + }; +} + +function useSetTimeout() { + return function() { + setTimeout(flush, 1); + }; +} + +var queue = new Array(1000); +function flush() { + for (var i = 0; i < len; i+=2) { + var callback = queue[i]; + var arg = queue[i+1]; + + callback(arg); + + queue[i] = undefined; + queue[i+1] = undefined; + } + + len = 0; +} + +function attemptVertx() { + try { + var r = require; + var vertx = r('vertx'); + vertxNext = vertx.runOnLoop || vertx.runOnContext; + return useVertxTimer(); + } catch(e) { + return useSetTimeout(); + } +} + +var scheduleFlush; +// Decide what async method to use to triggering processing of queued callbacks: +if (isNode) { + scheduleFlush = useNextTick(); +} else if (BrowserMutationObserver) { + scheduleFlush = useMutationObserver(); +} else if (isWorker) { + scheduleFlush = useMessageChannel(); +} else if (browserWindow === undefined && typeof require === 'function') { + scheduleFlush = attemptVertx(); +} else { + scheduleFlush = useSetTimeout(); +} diff --git a/node_modules/es6-promise/lib/es6-promise/enumerator.js b/node_modules/es6-promise/lib/es6-promise/enumerator.js new file mode 100644 index 00000000..3c56aad5 --- /dev/null +++ b/node_modules/es6-promise/lib/es6-promise/enumerator.js @@ -0,0 +1,113 @@ +import { + isArray, + isMaybeThenable +} from './utils'; + +import { + noop, + reject, + fulfill, + subscribe, + FULFILLED, + REJECTED, + PENDING, + getThen, + handleMaybeThenable +} from './-internal'; + +import then from './then'; +import Promise from './promise'; +import originalResolve from './promise/resolve'; +import originalThen from './then'; + +export default Enumerator; +function Enumerator(Constructor, input) { + this._instanceConstructor = Constructor; + this.promise = new Constructor(noop); + + if (Array.isArray(input)) { + this._input = input; + this.length = input.length; + this._remaining = input.length; + + this._result = new Array(this.length); + + if (this.length === 0) { + fulfill(this.promise, this._result); + } else { + this.length = this.length || 0; + this._enumerate(); + if (this._remaining === 0) { + fulfill(this.promise, this._result); + } + } + } else { + reject(this.promise, this._validationError()); + } +} + +Enumerator.prototype._validationError = function() { + return new Error('Array Methods must be provided an Array'); +}; + +Enumerator.prototype._enumerate = function() { + var length = this.length; + var input = this._input; + + for (var i = 0; this._state === PENDING && i < length; i++) { + this._eachEntry(input[i], i); + } +}; + +Enumerator.prototype._eachEntry = function(entry, i) { + var c = this._instanceConstructor; + var resolve = c.resolve; + + if (resolve === originalResolve) { + var then = getThen(entry); + + if (then === originalThen && + entry._state !== PENDING) { + this._settledAt(entry._state, i, entry._result); + } else if (typeof then !== 'function') { + this._remaining--; + this._result[i] = entry; + } else if (c === Promise) { + var promise = new c(noop); + handleMaybeThenable(promise, entry, then); + this._willSettleAt(promise, i); + } else { + this._willSettleAt(new c(function(resolve) { resolve(entry); }), i); + } + } else { + this._willSettleAt(resolve(entry), i); + } +}; + +Enumerator.prototype._settledAt = function(state, i, value) { + var promise = this.promise; + + if (promise._state === PENDING) { + this._remaining--; + + if (state === REJECTED) { + reject(promise, value); + } else { + this._result[i] = value; + } + } + + if (this._remaining === 0) { + fulfill(promise, this._result); + } +}; + +Enumerator.prototype._willSettleAt = function(promise, i) { + var enumerator = this; + + subscribe(promise, undefined, function(value) { + enumerator._settledAt(FULFILLED, i, value); + }, function(reason) { + enumerator._settledAt(REJECTED, i, reason); + }); +}; diff --git a/node_modules/es6-promise/lib/es6-promise/polyfill.js b/node_modules/es6-promise/lib/es6-promise/polyfill.js new file mode 100644 index 00000000..6696dfc6 --- /dev/null +++ b/node_modules/es6-promise/lib/es6-promise/polyfill.js @@ -0,0 +1,26 @@ +/*global self*/ +import Promise from './promise'; + +export default function polyfill() { + var local; + + if (typeof global !== 'undefined') { + local = global; + } else if (typeof self !== 'undefined') { + local = self; + } else { + try { + local = Function('return this')(); + } catch (e) { + throw new Error('polyfill failed because global object is unavailable in this environment'); + } + } + + var P = local.Promise; + + if (P && Object.prototype.toString.call(P.resolve()) === '[object Promise]' && !P.cast) { + return; + } + + local.Promise = Promise; +} diff --git a/node_modules/es6-promise/lib/es6-promise/promise.js b/node_modules/es6-promise/lib/es6-promise/promise.js new file mode 100644 index 00000000..0dfe02db --- /dev/null +++ b/node_modules/es6-promise/lib/es6-promise/promise.js @@ -0,0 +1,384 @@ +import { + isFunction +} from './utils'; + +import { + noop, + initializePromise +} from './-internal'; + +import { + asap, + setAsap, + setScheduler +} from './asap'; + +import all from './promise/all'; +import race from './promise/race'; +import Resolve from './promise/resolve'; +import Reject from './promise/reject'; +import then from './then'; + +var counter = 0; + +function needsResolver() { + throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); +} + +function needsNew() { + throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); +} + +export default Promise; +/** + Promise objects represent the eventual result of an asynchronous operation. The + primary way of interacting with a promise is through its `then` method, which + registers callbacks to receive either a promise's eventual value or the reason + why the promise cannot be fulfilled. + + Terminology + ----------- + + - `promise` is an object or function with a `then` method whose behavior conforms to this specification. + - `thenable` is an object or function that defines a `then` method. + - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). + - `exception` is a value that is thrown using the throw statement. + - `reason` is a value that indicates why a promise was rejected. + - `settled` the final resting state of a promise, fulfilled or rejected. + + A promise can be in one of three states: pending, fulfilled, or rejected. + + Promises that are fulfilled have a fulfillment value and are in the fulfilled + state. Promises that are rejected have a rejection reason and are in the + rejected state. A fulfillment value is never a thenable. + + Promises can also be said to *resolve* a value. If this value is also a + promise, then the original promise's settled state will match the value's + settled state. So a promise that *resolves* a promise that rejects will + itself reject, and a promise that *resolves* a promise that fulfills will + itself fulfill. + + + Basic Usage: + ------------ + + ```js + var promise = new Promise(function(resolve, reject) { + // on success + resolve(value); + + // on failure + reject(reason); + }); + + promise.then(function(value) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Advanced Usage: + --------------- + + Promises shine when abstracting away asynchronous interactions such as + `XMLHttpRequest`s. + + ```js + function getJSON(url) { + return new Promise(function(resolve, reject){ + var xhr = new XMLHttpRequest(); + + xhr.open('GET', url); + xhr.onreadystatechange = handler; + xhr.responseType = 'json'; + xhr.setRequestHeader('Accept', 'application/json'); + xhr.send(); + + function handler() { + if (this.readyState === this.DONE) { + if (this.status === 200) { + resolve(this.response); + } else { + reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); + } + } + }; + }); + } + + getJSON('/posts.json').then(function(json) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Unlike callbacks, promises are great composable primitives. + + ```js + Promise.all([ + getJSON('/posts'), + getJSON('/comments') + ]).then(function(values){ + values[0] // => postsJSON + values[1] // => commentsJSON + + return values; + }); + ``` + + @class Promise + @param {function} resolver + Useful for tooling. + @constructor +*/ +function Promise(resolver) { + this._id = counter++; + this._state = undefined; + this._result = undefined; + this._subscribers = []; + + if (noop !== resolver) { + typeof resolver !== 'function' && needsResolver(); + this instanceof Promise ? initializePromise(this, resolver) : needsNew(); + } +} + +Promise.all = all; +Promise.race = race; +Promise.resolve = Resolve; +Promise.reject = Reject; +Promise._setScheduler = setScheduler; +Promise._setAsap = setAsap; +Promise._asap = asap; + +Promise.prototype = { + constructor: Promise, + +/** + The primary way of interacting with a promise is through its `then` method, + which registers callbacks to receive either a promise's eventual value or the + reason why the promise cannot be fulfilled. + + ```js + findUser().then(function(user){ + // user is available + }, function(reason){ + // user is unavailable, and you are given the reason why + }); + ``` + + Chaining + -------- + + The return value of `then` is itself a promise. This second, 'downstream' + promise is resolved with the return value of the first promise's fulfillment + or rejection handler, or rejected if the handler throws an exception. + + ```js + findUser().then(function (user) { + return user.name; + }, function (reason) { + return 'default name'; + }).then(function (userName) { + // If `findUser` fulfilled, `userName` will be the user's name, otherwise it + // will be `'default name'` + }); + + findUser().then(function (user) { + throw new Error('Found user, but still unhappy'); + }, function (reason) { + throw new Error('`findUser` rejected and we're unhappy'); + }).then(function (value) { + // never reached + }, function (reason) { + // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. + // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. + }); + ``` + If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. + + ```js + findUser().then(function (user) { + throw new PedagogicalException('Upstream error'); + }).then(function (value) { + // never reached + }).then(function (value) { + // never reached + }, function (reason) { + // The `PedgagocialException` is propagated all the way down to here + }); + ``` + + Assimilation + ------------ + + Sometimes the value you want to propagate to a downstream promise can only be + retrieved asynchronously. This can be achieved by returning a promise in the + fulfillment or rejection handler. The downstream promise will then be pending + until the returned promise is settled. This is called *assimilation*. + + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // The user's comments are now available + }); + ``` + + If the assimliated promise rejects, then the downstream promise will also reject. + + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // If `findCommentsByAuthor` fulfills, we'll have the value here + }, function (reason) { + // If `findCommentsByAuthor` rejects, we'll have the reason here + }); + ``` + + Simple Example + -------------- + + Synchronous Example + + ```javascript + var result; + + try { + result = findResult(); + // success + } catch(reason) { + // failure + } + ``` + + Errback Example + + ```js + findResult(function(result, err){ + if (err) { + // failure + } else { + // success + } + }); + ``` + + Promise Example; + + ```javascript + findResult().then(function(result){ + // success + }, function(reason){ + // failure + }); + ``` + + Advanced Example + -------------- + + Synchronous Example + + ```javascript + var author, books; + + try { + author = findAuthor(); + books = findBooksByAuthor(author); + // success + } catch(reason) { + // failure + } + ``` + + Errback Example + + ```js + + function foundBooks(books) { + + } + + function failure(reason) { + + } + + findAuthor(function(author, err){ + if (err) { + failure(err); + // failure + } else { + try { + findBoooksByAuthor(author, function(books, err) { + if (err) { + failure(err); + } else { + try { + foundBooks(books); + } catch(reason) { + failure(reason); + } + } + }); + } catch(error) { + failure(err); + } + // success + } + }); + ``` + + Promise Example; + + ```javascript + findAuthor(). + then(findBooksByAuthor). + then(function(books){ + // found books + }).catch(function(reason){ + // something went wrong + }); + ``` + + @method then + @param {Function} onFulfilled + @param {Function} onRejected + Useful for tooling. + @return {Promise} +*/ + then: then, + +/** + `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same + as the catch block of a try/catch statement. + + ```js + function findAuthor(){ + throw new Error('couldn't find that author'); + } + + // synchronous + try { + findAuthor(); + } catch(reason) { + // something went wrong + } + + // async with promises + findAuthor().catch(function(reason){ + // something went wrong + }); + ``` + + @method catch + @param {Function} onRejection + Useful for tooling. + @return {Promise} +*/ + 'catch': function(onRejection) { + return this.then(null, onRejection); + } +}; diff --git a/node_modules/es6-promise/lib/es6-promise/promise/all.js b/node_modules/es6-promise/lib/es6-promise/promise/all.js new file mode 100644 index 00000000..03033f0d --- /dev/null +++ b/node_modules/es6-promise/lib/es6-promise/promise/all.js @@ -0,0 +1,52 @@ +import Enumerator from '../enumerator'; + +/** + `Promise.all` accepts an array of promises, and returns a new promise which + is fulfilled with an array of fulfillment values for the passed promises, or + rejected with the reason of the first passed promise to be rejected. It casts all + elements of the passed iterable to promises as it runs this algorithm. + + Example: + + ```javascript + var promise1 = resolve(1); + var promise2 = resolve(2); + var promise3 = resolve(3); + var promises = [ promise1, promise2, promise3 ]; + + Promise.all(promises).then(function(array){ + // The array here would be [ 1, 2, 3 ]; + }); + ``` + + If any of the `promises` given to `all` are rejected, the first promise + that is rejected will be given as an argument to the returned promises's + rejection handler. For example: + + Example: + + ```javascript + var promise1 = resolve(1); + var promise2 = reject(new Error("2")); + var promise3 = reject(new Error("3")); + var promises = [ promise1, promise2, promise3 ]; + + Promise.all(promises).then(function(array){ + // Code here never runs because there are rejected promises! + }, function(error) { + // error.message === "2" + }); + ``` + + @method all + @static + @param {Array} entries array of promises + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} promise that is fulfilled when all `promises` have been + fulfilled, or rejected if any of them become rejected. + @static +*/ +export default function all(entries) { + return new Enumerator(this, entries).promise; +} diff --git a/node_modules/es6-promise/lib/es6-promise/promise/race.js b/node_modules/es6-promise/lib/es6-promise/promise/race.js new file mode 100644 index 00000000..0d7ff133 --- /dev/null +++ b/node_modules/es6-promise/lib/es6-promise/promise/race.js @@ -0,0 +1,104 @@ +import { + isArray +} from "../utils"; + +import { + noop, + resolve, + reject, + subscribe, + PENDING +} from '../-internal'; + +/** + `Promise.race` returns a new promise which is settled in the same way as the + first passed promise to settle. + + Example: + + ```javascript + var promise1 = new Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 1'); + }, 200); + }); + + var promise2 = new Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 2'); + }, 100); + }); + + Promise.race([promise1, promise2]).then(function(result){ + // result === 'promise 2' because it was resolved before promise1 + // was resolved. + }); + ``` + + `Promise.race` is deterministic in that only the state of the first + settled promise matters. For example, even if other promises given to the + `promises` array argument are resolved, but the first settled promise has + become rejected before the other promises became fulfilled, the returned + promise will become rejected: + + ```javascript + var promise1 = new Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 1'); + }, 200); + }); + + var promise2 = new Promise(function(resolve, reject){ + setTimeout(function(){ + reject(new Error('promise 2')); + }, 100); + }); + + Promise.race([promise1, promise2]).then(function(result){ + // Code here never runs + }, function(reason){ + // reason.message === 'promise 2' because promise 2 became rejected before + // promise 1 became fulfilled + }); + ``` + + An example real-world use case is implementing timeouts: + + ```javascript + Promise.race([ajax('foo.json'), timeout(5000)]) + ``` + + @method race + @static + @param {Array} promises array of promises to observe + Useful for tooling. + @return {Promise} a promise which settles in the same way as the first passed + promise to settle. +*/ +export default function race(entries) { + /*jshint validthis:true */ + var Constructor = this; + + var promise = new Constructor(noop); + + if (!isArray(entries)) { + reject(promise, new TypeError('You must pass an array to race.')); + return promise; + } + + var length = entries.length; + + function onFulfillment(value) { + resolve(promise, value); + } + + function onRejection(reason) { + reject(promise, reason); + } + + for (var i = 0; promise._state === PENDING && i < length; i++) { + subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection); + } + + return promise; +} diff --git a/node_modules/es6-promise/lib/es6-promise/promise/reject.js b/node_modules/es6-promise/lib/es6-promise/promise/reject.js new file mode 100644 index 00000000..63b86cba --- /dev/null +++ b/node_modules/es6-promise/lib/es6-promise/promise/reject.js @@ -0,0 +1,46 @@ +import { + noop, + reject as _reject +} from '../-internal'; + +/** + `Promise.reject` returns a promise rejected with the passed `reason`. + It is shorthand for the following: + + ```javascript + var promise = new Promise(function(resolve, reject){ + reject(new Error('WHOOPS')); + }); + + promise.then(function(value){ + // Code here doesn't run because the promise is rejected! + }, function(reason){ + // reason.message === 'WHOOPS' + }); + ``` + + Instead of writing the above, your code now simply becomes the following: + + ```javascript + var promise = Promise.reject(new Error('WHOOPS')); + + promise.then(function(value){ + // Code here doesn't run because the promise is rejected! + }, function(reason){ + // reason.message === 'WHOOPS' + }); + ``` + + @method reject + @static + @param {Any} reason value that the returned promise will be rejected with. + Useful for tooling. + @return {Promise} a promise rejected with the given `reason`. +*/ +export default function reject(reason) { + /*jshint validthis:true */ + var Constructor = this; + var promise = new Constructor(noop); + _reject(promise, reason); + return promise; +} diff --git a/node_modules/es6-promise/lib/es6-promise/promise/resolve.js b/node_modules/es6-promise/lib/es6-promise/promise/resolve.js new file mode 100644 index 00000000..201a545d --- /dev/null +++ b/node_modules/es6-promise/lib/es6-promise/promise/resolve.js @@ -0,0 +1,48 @@ +import { + noop, + resolve as _resolve +} from '../-internal'; + +/** + `Promise.resolve` returns a promise that will become resolved with the + passed `value`. It is shorthand for the following: + + ```javascript + var promise = new Promise(function(resolve, reject){ + resolve(1); + }); + + promise.then(function(value){ + // value === 1 + }); + ``` + + Instead of writing the above, your code now simply becomes the following: + + ```javascript + var promise = Promise.resolve(1); + + promise.then(function(value){ + // value === 1 + }); + ``` + + @method resolve + @static + @param {Any} value value that the returned promise will be resolved with + Useful for tooling. + @return {Promise} a promise that will become fulfilled with the given + `value` +*/ +export default function resolve(object) { + /*jshint validthis:true */ + var Constructor = this; + + if (object && typeof object === 'object' && object.constructor === Constructor) { + return object; + } + + var promise = new Constructor(noop); + _resolve(promise, object); + return promise; +} diff --git a/node_modules/es6-promise/lib/es6-promise/then.js b/node_modules/es6-promise/lib/es6-promise/then.js new file mode 100644 index 00000000..8704a638 --- /dev/null +++ b/node_modules/es6-promise/lib/es6-promise/then.js @@ -0,0 +1,32 @@ +import { + invokeCallback, + subscribe, + FULFILLED, + REJECTED, + noop +} from './-internal'; + +import { asap } from './asap'; + +export default function then(onFulfillment, onRejection) { + var parent = this; + var state = parent._state; + + if (state === FULFILLED && !onFulfillment || state === REJECTED && !onRejection) { + return this; + } + + var child = new this.constructor(noop); + var result = parent._result; + + if (state) { + var callback = arguments[state - 1]; + asap(function(){ + invokeCallback(state, child, callback, result); + }); + } else { + subscribe(parent, child, onFulfillment, onRejection); + } + + return child; +} diff --git a/node_modules/es6-promise/lib/es6-promise/utils.js b/node_modules/es6-promise/lib/es6-promise/utils.js new file mode 100644 index 00000000..31ec6f9c --- /dev/null +++ b/node_modules/es6-promise/lib/es6-promise/utils.js @@ -0,0 +1,22 @@ +export function objectOrFunction(x) { + return typeof x === 'function' || (typeof x === 'object' && x !== null); +} + +export function isFunction(x) { + return typeof x === 'function'; +} + +export function isMaybeThenable(x) { + return typeof x === 'object' && x !== null; +} + +var _isArray; +if (!Array.isArray) { + _isArray = function (x) { + return Object.prototype.toString.call(x) === '[object Array]'; + }; +} else { + _isArray = Array.isArray; +} + +export var isArray = _isArray; diff --git a/node_modules/es6-promise/package.json b/node_modules/es6-promise/package.json new file mode 100644 index 00000000..c87fd0df --- /dev/null +++ b/node_modules/es6-promise/package.json @@ -0,0 +1,122 @@ +{ + "_args": [ + [ + "es6-promise@^3.0.2", + "/Users/smus/Projects/embedvr/node_modules/webvr-boilerplate" + ] + ], + "_from": "es6-promise@>=3.0.2 <4.0.0", + "_id": "es6-promise@3.1.2", + "_inCache": true, + "_installable": true, + "_location": "/es6-promise", + "_nodeVersion": "5.4.1", + "_npmOperationalInternal": { + "host": "packages-5-east.internal.npmjs.com", + "tmp": "tmp/es6-promise-3.1.2.tgz_1455383156542_0.32691872608847916" + }, + "_npmUser": { + "email": "stefan.penner@gmail.com", + "name": "stefanpenner" + }, + "_npmVersion": "3.5.3", + "_phantomChildren": {}, + "_requested": { + "name": "es6-promise", + "raw": "es6-promise@^3.0.2", + "rawSpec": "^3.0.2", + "scope": null, + "spec": ">=3.0.2 <4.0.0", + "type": "range" + }, + "_requiredBy": [ + "/webvr-boilerplate" + ], + "_resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.1.2.tgz", + "_shasum": "795e25ceb47f7babb263d151afbedd92d18e6a07", + "_shrinkwrap": null, + "_spec": "es6-promise@^3.0.2", + "_where": "/Users/smus/Projects/embedvr/node_modules/webvr-boilerplate", + "author": { + "name": "Yehuda Katz, Tom Dale, Stefan Penner and contributors", + "url": "Conversion to ES6 API by Jake Archibald" + }, + "browser": { + "vertx": false + }, + "bugs": { + "url": "https://github.com/jakearchibald/ES6-Promises/issues" + }, + "dependencies": {}, + "description": "A lightweight library that provides tools for organizing asynchronous code", + "devDependencies": { + "brfs": "0.0.8", + "broccoli-es6-module-transpiler": "^0.5.0", + "broccoli-jshint": "^1.1.1", + "broccoli-merge-trees": "^1.1.1", + "broccoli-replace": "^0.2.0", + "broccoli-stew": "^1.2.0", + "broccoli-uglify-js": "^0.1.3", + "broccoli-watchify": "^0.2.0", + "ember-cli": "2.3.0-beta.1", + "ember-publisher": "0.0.7", + "git-repo-version": "0.0.3", + "json3": "^3.3.2", + "mocha": "^1.20.1", + "promises-aplus-tests-phantom": "^2.1.0-revise", + "release-it": "0.0.10" + }, + "directories": { + "lib": "lib" + }, + "dist": { + "shasum": "795e25ceb47f7babb263d151afbedd92d18e6a07", + "tarball": "http://registry.npmjs.org/es6-promise/-/es6-promise-3.1.2.tgz" + }, + "files": [ + "!dist/test", + "dist", + "lib" + ], + "gitHead": "4a0062ee0725bb2c992de2c9636fe97c666794d1", + "homepage": "https://github.com/jakearchibald/ES6-Promises#readme", + "keywords": [ + "futures", + "promises" + ], + "license": "MIT", + "main": "dist/es6-promise.js", + "maintainers": [ + { + "name": "jaffathecake", + "email": "jaffathecake@gmail.com" + }, + { + "name": "stefanpenner", + "email": "stefan.penner@gmail.com" + } + ], + "name": "es6-promise", + "namespace": "es6-promise", + "optionalDependencies": {}, + "readme": "ERROR: No README data found!", + "repository": { + "type": "git", + "url": "git://github.com/jakearchibald/ES6-Promises.git" + }, + "scripts": { + "build": "ember build", + "build:production": "ember build --environment production", + "dry-run-release": "ember build --environment production && release-it --dry-run --non-interactive", + "lint": "jshint lib", + "prepublish": "ember build --environment production", + "start": "ember s", + "test": "ember test", + "test:node": "ember build && mocha ./dist/test/browserify", + "test:server": "ember test --server" + }, + "spm": { + "main": "dist/es6-promise.js" + }, + "version": "3.1.2" +} diff --git a/node_modules/stats-js/LICENSE b/node_modules/stats-js/LICENSE new file mode 100644 index 00000000..97eb2329 --- /dev/null +++ b/node_modules/stats-js/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2009-2012 Mr.doob + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/stats-js/README.md b/node_modules/stats-js/README.md new file mode 100644 index 00000000..0ca224cb --- /dev/null +++ b/node_modules/stats-js/README.md @@ -0,0 +1,123 @@ +stats.js +======== + +#### JavaScript Performance Monitor #### + +This class provides a simple info box that will help you monitor your code performance. + +* **FPS** Frames rendered in the last second. The higher the number the better. +* **MS** Milliseconds needed to render a frame. The lower the number the better. + + +### Screenshots ### + +![stats_js_fps.png](http://mrdoob.github.com/stats.js/assets/stats_js_fps.png) +![stats_js_ms.png](http://mrdoob.github.com/stats.js/assets/stats_js_ms.png) + + +### Usage ### + +```javascript +var stats = new Stats(); +stats.setMode(1); // 0: fps, 1: ms + +// Align top-left +stats.domElement.style.position = 'absolute'; +stats.domElement.style.left = '0px'; +stats.domElement.style.top = '0px'; + +document.body.appendChild( stats.domElement ); + +setInterval( function () { + + stats.begin(); + + // your code goes here + + stats.end(); + +}, 1000 / 60 ); +``` + + +### Bookmarklet ### + +Albeit theorically not as accurate the widget can also be easily inserted to **any site** using a bookmarklet. +[Follow the instructions](http://ricardocabello.com/blog/post/707). + + +### Change Log ### + +2014 08 11 - **r12** (2,310 KB, gzip: 1,087 B) + +* Browserify support. (@Miha-ha) + + +2012 09 01 - **r11** (2,243 KB, gzip: 903 B) + +* Renamed `build/Stats.js` to `build/stats.min.js`. + + +2012 05 10 - **r10** (2,243 KB, gzip: 903 B) + +* Changed `.getDomElement()` to `.domElement` back. +* Removed `.getFps()`, `.getFpsMin()`, `.getFpsMax()`, `.getMs()`, `.getMsMin()`, `.getMsMax()`. +* Added `.begin()` and `.end()`. +* Added `.setMode()`. +* Themeable with CSS. + + +2012 01 18 - **r9** (2,872 KB, gzip: 1,038 KB) + +* Changed `.domElement` to `.getDomElement()` +* Added `.getFps()`, `.getFpsMin()`, `.getFpsMax()`, `.getMs()`, `.getMsMin()`, `.getMsMax()`. + + +2011 10 16 - **r8** (2.670 KB, gzip: 987 B) + +* Performance and size optimizations. +* Removed memory mode. + + +2011 10 13 - **r7** (4.083 KB, gzip: 1.377 KB) + +* Replaced `new Date().getTime()` with `Date.now()`. + + +2011 05 28 - **r6** (4.103 KB, gzip: 1.384 KB) + +* Updated check for memory accesible browsers. +* Renamed MEM to MB for consistency reasons. + + +2010 09 21 - **r5** (3.800 KB) + +* Different color per mode. +* Added MEM mode. (Webkit-based browsers only) +* Force text left aligned. + + +2010 06 11 - **r4** (2.235 KB) + +* Added MS mode. + + +2010 05 12 - **r3** (1.241 KB) + +* Switched to module pattern code style. +* Removed `position = 'absolute'`. + + +2010 03 01 - **r2** (2.177 KB) + +* Simplified. + + +2010 02 21 - **r1** + +* Accurate FPS calculation. (thx @spite!) + + +2009 08 09 - **r0** + +* Base code. diff --git a/node_modules/stats-js/build/stats.min.js b/node_modules/stats-js/build/stats.min.js new file mode 100644 index 00000000..52539f4e --- /dev/null +++ b/node_modules/stats-js/build/stats.min.js @@ -0,0 +1,6 @@ +// stats.js - http://github.com/mrdoob/stats.js +var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px"; +i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div"); +k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display= +"block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:12,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height= +a+"px",m=b,r=0);return b},update:function(){l=this.end()}}};"object"===typeof module&&(module.exports=Stats); diff --git a/node_modules/stats-js/examples/basic.html b/node_modules/stats-js/examples/basic.html new file mode 100644 index 00000000..ed9aed26 --- /dev/null +++ b/node_modules/stats-js/examples/basic.html @@ -0,0 +1,53 @@ + + + + stats.js - basic example + + + + + + + + diff --git a/node_modules/stats-js/examples/theming.html b/node_modules/stats-js/examples/theming.html new file mode 100644 index 00000000..9d253b38 --- /dev/null +++ b/node_modules/stats-js/examples/theming.html @@ -0,0 +1,125 @@ + + + + stats.js - theming example + + + + + + + + diff --git a/node_modules/stats-js/package.json b/node_modules/stats-js/package.json new file mode 100644 index 00000000..31209612 --- /dev/null +++ b/node_modules/stats-js/package.json @@ -0,0 +1,76 @@ +{ + "_args": [ + [ + "stats-js", + "/Users/smus/Projects/embedvr" + ] + ], + "_from": "stats-js@*", + "_id": "stats-js@1.0.0-alpha1", + "_inCache": true, + "_installable": true, + "_location": "/stats-js", + "_npmUser": { + "email": "kevin.isom@gmail.com", + "name": "kev_nz" + }, + "_npmVersion": "1.4.9", + "_phantomChildren": {}, + "_requested": { + "name": "stats-js", + "raw": "stats-js", + "rawSpec": "", + "scope": null, + "spec": "*", + "type": "range" + }, + "_requiredBy": [ + "#USER" + ], + "_resolved": "https://registry.npmjs.org/stats-js/-/stats-js-1.0.0-alpha1.tgz", + "_shasum": "44ae82e6421123bab99750d95c3f5526b8f257db", + "_shrinkwrap": null, + "_spec": "stats-js", + "_where": "/Users/smus/Projects/embedvr", + "author": { + "email": "kevin.isom@gmail.com", + "name": "Kevin Isom" + }, + "bugs": { + "url": "https://github.com/Kevnz/stats.js/issues" + }, + "dependencies": {}, + "description": "JavaScript Performance Monitor", + "devDependencies": {}, + "directories": { + "example": "examples" + }, + "dist": { + "shasum": "44ae82e6421123bab99750d95c3f5526b8f257db", + "tarball": "http://registry.npmjs.org/stats-js/-/stats-js-1.0.0-alpha1.tgz" + }, + "homepage": "https://github.com/Kevnz/stats.js", + "keywords": [ + "stats", + "webgl" + ], + "license": "MIT", + "main": "build/stats.min.js", + "maintainers": [ + { + "name": "kev_nz", + "email": "kevin.isom@gmail.com" + } + ], + "name": "stats-js", + "optionalDependencies": {}, + "readme": "ERROR: No README data found!", + "repository": { + "type": "git", + "url": "git://github.com/Kevnz/stats.js.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "1.0.0-alpha1" +} diff --git a/node_modules/stats-js/src/Stats.js b/node_modules/stats-js/src/Stats.js new file mode 100644 index 00000000..90b2a274 --- /dev/null +++ b/node_modules/stats-js/src/Stats.js @@ -0,0 +1,149 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +var Stats = function () { + + var startTime = Date.now(), prevTime = startTime; + var ms = 0, msMin = Infinity, msMax = 0; + var fps = 0, fpsMin = Infinity, fpsMax = 0; + var frames = 0, mode = 0; + + var container = document.createElement( 'div' ); + container.id = 'stats'; + container.addEventListener( 'mousedown', function ( event ) { event.preventDefault(); setMode( ++ mode % 2 ) }, false ); + container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer'; + + var fpsDiv = document.createElement( 'div' ); + fpsDiv.id = 'fps'; + fpsDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#002'; + container.appendChild( fpsDiv ); + + var fpsText = document.createElement( 'div' ); + fpsText.id = 'fpsText'; + fpsText.style.cssText = 'color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; + fpsText.innerHTML = 'FPS'; + fpsDiv.appendChild( fpsText ); + + var fpsGraph = document.createElement( 'div' ); + fpsGraph.id = 'fpsGraph'; + fpsGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0ff'; + fpsDiv.appendChild( fpsGraph ); + + while ( fpsGraph.children.length < 74 ) { + + var bar = document.createElement( 'span' ); + bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#113'; + fpsGraph.appendChild( bar ); + + } + + var msDiv = document.createElement( 'div' ); + msDiv.id = 'ms'; + msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;display:none'; + container.appendChild( msDiv ); + + var msText = document.createElement( 'div' ); + msText.id = 'msText'; + msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; + msText.innerHTML = 'MS'; + msDiv.appendChild( msText ); + + var msGraph = document.createElement( 'div' ); + msGraph.id = 'msGraph'; + msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0'; + msDiv.appendChild( msGraph ); + + while ( msGraph.children.length < 74 ) { + + var bar = document.createElement( 'span' ); + bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131'; + msGraph.appendChild( bar ); + + } + + var setMode = function ( value ) { + + mode = value; + + switch ( mode ) { + + case 0: + fpsDiv.style.display = 'block'; + msDiv.style.display = 'none'; + break; + case 1: + fpsDiv.style.display = 'none'; + msDiv.style.display = 'block'; + break; + } + + }; + + var updateGraph = function ( dom, value ) { + + var child = dom.appendChild( dom.firstChild ); + child.style.height = value + 'px'; + + }; + + return { + + REVISION: 12, + + domElement: container, + + setMode: setMode, + + begin: function () { + + startTime = Date.now(); + + }, + + end: function () { + + var time = Date.now(); + + ms = time - startTime; + msMin = Math.min( msMin, ms ); + msMax = Math.max( msMax, ms ); + + msText.textContent = ms + ' MS (' + msMin + '-' + msMax + ')'; + updateGraph( msGraph, Math.min( 30, 30 - ( ms / 200 ) * 30 ) ); + + frames ++; + + if ( time > prevTime + 1000 ) { + + fps = Math.round( ( frames * 1000 ) / ( time - prevTime ) ); + fpsMin = Math.min( fpsMin, fps ); + fpsMax = Math.max( fpsMax, fps ); + + fpsText.textContent = fps + ' FPS (' + fpsMin + '-' + fpsMax + ')'; + updateGraph( fpsGraph, Math.min( 30, 30 - ( fps / 100 ) * 30 ) ); + + prevTime = time; + frames = 0; + + } + + return time; + + }, + + update: function () { + + startTime = this.end(); + + } + + } + +}; + +if ( typeof module === 'object' ) { + + module.exports = Stats; + +} \ No newline at end of file diff --git a/node_modules/stats-js/utils/builder.py b/node_modules/stats-js/utils/builder.py new file mode 100644 index 00000000..fd230a4f --- /dev/null +++ b/node_modules/stats-js/utils/builder.py @@ -0,0 +1,9 @@ +import os + +source = '../src/Stats.js' +output = '../build/stats.min.js' + +os.system('java -jar compiler/compiler.jar --language_in=ECMASCRIPT5 --js ' + source + ' --js_output_file ' + output) + +with open(output,'r') as f: text = f.read() +with open(output,'w') as f: f.write("// stats.js - http://github.com/mrdoob/stats.js\n" + text) diff --git a/node_modules/stats-js/utils/compiler/COPYING b/node_modules/stats-js/utils/compiler/COPYING new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/node_modules/stats-js/utils/compiler/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/node_modules/stats-js/utils/compiler/README b/node_modules/stats-js/utils/compiler/README new file mode 100644 index 00000000..77ab89db --- /dev/null +++ b/node_modules/stats-js/utils/compiler/README @@ -0,0 +1,292 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Contents +// + +The Closure Compiler performs checking, instrumentation, and +optimizations on JavaScript code. The purpose of this README is to +explain how to build and run the Closure Compiler. + +The Closure Compiler requires Java 6 or higher. +http://www.java.com/ + + +// +// Building The Closure Compiler +// + +There are three ways to get a Closure Compiler executable. + +1) Use one we built for you. + +Pre-built Closure binaries can be found at +http://code.google.com/p/closure-compiler/downloads/list + + +2) Check out the source and build it with Apache Ant. + +First, check out the full source tree of the Closure Compiler. There +are instructions on how to do this at the project site. +http://code.google.com/p/closure-compiler/source/checkout + +Apache Ant is a cross-platform build tool. +http://ant.apache.org/ + +At the root of the source tree, there is an Ant file named +build.xml. To use it, navigate to the same directory and type the +command + +ant jar + +This will produce a jar file called "build/compiler.jar". + + +3) Check out the source and build it with Eclipse. + +Eclipse is a cross-platform IDE. +http://www.eclipse.org/ + +Under Eclipse's File menu, click "New > Project ..." and create a +"Java Project." You will see an options screen. Give the project a +name, select "Create project from existing source," and choose the +root of the checked-out source tree as the existing directory. Verify +that you are using JRE version 6 or higher. + +Eclipse can use the build.xml file to discover rules. When you +navigate to the build.xml file, you will see all the build rules in +the "Outline" pane. Run the "jar" rule to build the compiler in +build/compiler.jar. + + +// +// Running The Closure Compiler +// + +Once you have the jar binary, running the Closure Compiler is straightforward. + +On the command line, type + +java -jar compiler.jar + +This starts the compiler in interactive mode. Type + +var x = 17 + 25; + +then hit "Enter", then hit "Ctrl-Z" (on Windows) or "Ctrl-D" (on Mac or Linux) +and "Enter" again. The Compiler will respond: + +var x=42; + +The Closure Compiler has many options for reading input from a file, +writing output to a file, checking your code, and running +optimizations. To learn more, type + +java -jar compiler.jar --help + +You can read more detailed documentation about the many flags at +http://code.google.com/closure/compiler/docs/gettingstarted_app.html + + +// +// Compiling Multiple Scripts +// + +If you have multiple scripts, you should compile them all together with +one compile command. + +java -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js + +The Closure Compiler will concatenate the files in the order they're +passed at the command line. + +If you need to compile many, many scripts together, you may start to +run into problems with managing dependencies between scripts. You +should check out the Closure Library. It contains functions for +enforcing dependencies between scripts, and a tool called calcdeps.py +that knows how to give scripts to the Closure Compiler in the right +order. + +http://code.google.com/p/closure-library/ + +// +// Licensing +// + +Unless otherwise stated, all source files are licensed under +the Apache License, Version 2.0. + + +----- +Code under: +src/com/google/javascript/rhino +test/com/google/javascript/rhino + +URL: http://www.mozilla.org/rhino +Version: 1.5R3, with heavy modifications +License: Netscape Public License and MPL / GPL dual license + +Description: A partial copy of Mozilla Rhino. Mozilla Rhino is an +implementation of JavaScript for the JVM. The JavaScript parser and +the parse tree data structures were extracted and modified +significantly for use by Google's JavaScript compiler. + +Local Modifications: The packages have been renamespaced. All code not +relevant to parsing has been removed. A JsDoc parser and static typing +system have been added. + + +----- +Code in: +lib/rhino + +Rhino +URL: http://www.mozilla.org/rhino +Version: Trunk +License: Netscape Public License and MPL / GPL dual license + +Description: Mozilla Rhino is an implementation of JavaScript for the JVM. + +Local Modifications: Minor changes to parsing JSDoc that usually get pushed +up-stream to Rhino trunk. + + +----- +Code in: +lib/args4j.jar + +Args4j +URL: https://args4j.dev.java.net/ +Version: 2.0.12 +License: MIT + +Description: +args4j is a small Java class library that makes it easy to parse command line +options/arguments in your CUI application. + +Local Modifications: None. + + +----- +Code in: +lib/guava.jar + +Guava Libraries +URL: http://code.google.com/p/guava-libraries/ +Version: r08 +License: Apache License 2.0 + +Description: Google's core Java libraries. + +Local Modifications: None. + + +----- +Code in: +lib/jsr305.jar + +Annotations for software defect detection +URL: http://code.google.com/p/jsr-305/ +Version: svn revision 47 +License: BSD License + +Description: Annotations for software defect detection. + +Local Modifications: None. + + +----- +Code in: +lib/jarjar.jar + +Jar Jar Links +URL: http://jarjar.googlecode.com/ +Version: 1.1 +License: Apache License 2.0 + +Description: +A utility for repackaging Java libraries. + +Local Modifications: None. + + +---- +Code in: +lib/junit.jar + +JUnit +URL: http://sourceforge.net/projects/junit/ +Version: 4.8.2 +License: Common Public License 1.0 + +Description: A framework for writing and running automated tests in Java. + +Local Modifications: None. + + +--- +Code in: +lib/protobuf-java.jar + +Protocol Buffers +URL: http://code.google.com/p/protobuf/ +Version: 2.3.0 +License: New BSD License + +Description: Supporting libraries for protocol buffers, +an encoding of structured data. + +Local Modifications: None + + +--- +Code in: +lib/ant.jar +lib/ant-launcher.jar + +URL: http://ant.apache.org/bindownload.cgi +Version: 1.8.1 +License: Apache License 2.0 +Description: + Ant is a Java based build tool. In theory it is kind of like "make" + without make's wrinkles and with the full portability of pure java code. + +Local Modifications: None + + +--- +Code in: +lib/json.jar +URL: http://json.org/java/index.html +Version: JSON version 20090211 +License: MIT license +Description: +JSON is a set of java files for use in transmitting data in JSON format. + +Local Modifications: None + +--- +Code in: +tools/maven-ant-tasks-2.1.1.jar +URL: http://maven.apache.org +Version 2.1.1 +License: Apache License 2.0 +Description: + Maven Ant tasks are used to manage dependencies and to install/deploy to + maven repositories. + +Local Modifications: None diff --git a/node_modules/stats-js/utils/compiler/compiler.jar b/node_modules/stats-js/utils/compiler/compiler.jar new file mode 100644 index 00000000..52a920f2 Binary files /dev/null and b/node_modules/stats-js/utils/compiler/compiler.jar differ diff --git a/node_modules/three/README.md b/node_modules/three/README.md new file mode 100644 index 00000000..f372e1de --- /dev/null +++ b/node_modules/three/README.md @@ -0,0 +1,68 @@ +three.js +======== + +#### JavaScript 3D library #### + +The aim of the project is to create a lightweight 3D library with a very low level of complexity — in other words, for dummies. The library provides <canvas>, <svg>, CSS3D and WebGL renderers. + +[Examples](http://threejs.org/examples/) — [Documentation](http://threejs.org/docs/) — [Migrating](https://github.com/mrdoob/three.js/wiki/Migration) — [Help](http://stackoverflow.com/questions/tagged/three.js) + + +### Usage ### + +Download the [minified library](http://threejs.org/build/three.min.js) and include it in your html. +Alternatively see [how to build the library yourself](https://github.com/mrdoob/three.js/wiki/build.py,-or-how-to-generate-a-compressed-Three.js-file). + +```html + +``` + +This code creates a scene, a camera, and a geometric cube, and it adds the cube to the scene. It then creates a `WebGL` renderer for the scene and camera, and it adds that viewport to the document.body element. Finally it animates the cube within the scene for the camera. + +```html + +``` +If everything went well you should see [this](http://jsfiddle.net/f17Lz5ux/). + +### Change log ### + +[releases](https://github.com/mrdoob/three.js/releases) diff --git a/node_modules/three/examples/js/AnimationClipCreator.js b/node_modules/three/examples/js/AnimationClipCreator.js new file mode 100644 index 00000000..2603f0cd --- /dev/null +++ b/node_modules/three/examples/js/AnimationClipCreator.js @@ -0,0 +1,139 @@ +/** + * + * Creator of typical test AnimationClips / KeyframeTracks + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.AnimationClipCreator = function() { +}; + +THREE.AnimationClipCreator.CreateRotationAnimation = function( period, axis ) { + + var keys = []; + keys.push( { time: 0, value: 0 } ); + keys.push( { time: period, value: 360 } ); + + axis = axis || 'x'; + var trackName = '.rotation[' + axis + ']'; + + var track = new THREE.NumberKeyframeTrack( trackName, keys ); + + var clip = new THREE.AnimationClip( 'rotate.x', 10, [ track ] ); + //console.log( 'rotateClip', clip ); + + return clip; +}; + +THREE.AnimationClipCreator.CreateScaleAxisAnimation = function( period, axis ) { + + var keys = []; + keys.push( { time: 0, value: 0 } ); + keys.push( { time: period, value: 360 } ); + + axis = axis || 'x'; + var trackName = '.scale[' + axis + ']'; + + var track = new THREE.NumberKeyframeTrack( trackName, keys ); + + var clip = new THREE.AnimationClip( 'scale.x', 10, [ track ] ); + //console.log( 'scaleClip', clip ); + + return clip; +}; + +THREE.AnimationClipCreator.CreateShakeAnimation = function( duration, shakeScale ) { + + var keys = []; + + for( var i = 0; i < duration * 10; i ++ ) { + + keys.push( { + time: ( i / 10.0 ), + value: new THREE.Vector3( Math.random() * 2.0 - 1.0, Math.random() * 2.0 - 1.0, Math.random() * 2.0 - 1.0 ).multiply( shakeScale ) + } ); + + } + + var trackName = '.position'; + + var track = new THREE.VectorKeyframeTrack( trackName, keys ); + + var clip = new THREE.AnimationClip( 'shake' + duration, duration, [ track ] ); + //console.log( 'shakeClip', clip ); + + return clip; +}; + + +THREE.AnimationClipCreator.CreatePulsationAnimation = function( duration, pulseScale ) { + + var keys = []; + + for( var i = 0; i < duration * 10; i ++ ) { + + var scaleFactor = Math.random() * pulseScale; + keys.push( { + time: ( i / 10.0 ), + value: new THREE.Vector3( scaleFactor, scaleFactor, scaleFactor ) + } ); + + } + + var trackName = '.scale'; + + var track = new THREE.VectorKeyframeTrack( trackName, keys ); + + var clip = new THREE.AnimationClip( 'scale' + duration, duration, [ track ] ); + //console.log( 'scaleClip', clip ); + + return clip; +}; + + +THREE.AnimationClipCreator.CreateVisibilityAnimation = function( duration ) { + + var keys = []; + keys.push( { + time: 0, + value: true + } ); + keys.push( { + time: duration - 1, + value: false + } ); + keys.push( { + time: duration, + value: true + } ); + + var trackName = '.visible'; + + var track = new THREE.BooleanKeyframeTrack( trackName, keys ); + + var clip = new THREE.AnimationClip( 'visible' + duration, duration, [ track ] ); + //console.log( 'scaleClip', clip ); + + return clip; +}; + + +THREE.AnimationClipCreator.CreateMaterialColorAnimation = function( duration, colors, loop ) { + + var timeStep = duration / colors.length; + var keys = []; + for( var i = 0; i <= colors.length; i ++ ) { + keys.push( { time: i * timeStep, value: colors[ i % colors.length ] } ); + } + + var trackName = '.material[0].color'; + + var track = new THREE.ColorKeyframeTrack( trackName, keys ); + + var clip = new THREE.AnimationClip( 'colorDiffuse', 10, [ track ] ); + //console.log( 'diffuseClip', clip ); + + return clip; +}; + diff --git a/node_modules/three/examples/js/BlendCharacter.js b/node_modules/three/examples/js/BlendCharacter.js new file mode 100644 index 00000000..5ab4ac61 --- /dev/null +++ b/node_modules/three/examples/js/BlendCharacter.js @@ -0,0 +1,140 @@ +/** + * @author Michael Guerrero / http://realitymeltdown.com + */ + +THREE.BlendCharacter = function () { + + this.animations = {}; + this.weightSchedule = []; + this.warpSchedule = []; + + this.load = function ( url, onLoad ) { + + var scope = this; + + var loader = new THREE.JSONLoader(); + loader.load( url, function( geometry, materials ) { + + var originalMaterial = materials[ 0 ]; + originalMaterial.skinning = true; + + THREE.SkinnedMesh.call( scope, geometry, originalMaterial ); + + scope.mixer = new THREE.AnimationMixer( scope ); + + // Create the animations + for ( var i = 0; i < geometry.animations.length; ++ i ) { + + var animName = geometry.animations[ i ].name; + scope.animations[ animName ] = geometry.animations[ i ]; + + } + + // Loading is complete, fire the callback + if ( onLoad !== undefined ) onLoad(); + + } ); + + }; + + this.update = function( dt ) { + + this.mixer.update( dt ); + + }; + + this.play = function( animName, weight ) { + + this.mixer.removeAllActions(); + + this.mixer.play( new THREE.AnimationAction( this.animations[ animName ] ) ); + + }; + + this.crossfade = function( fromAnimName, toAnimName, duration ) { + + this.mixer.removeAllActions(); + + var fromAction = new THREE.AnimationAction( this.animations[ fromAnimName ] ); + var toAction = new THREE.AnimationAction( this.animations[ toAnimName ] ); + + this.mixer.play( fromAction ); + this.mixer.play( toAction ); + + this.mixer.crossFade( fromAction, toAction, duration, false ); + + }; + + this.warp = function( fromAnimName, toAnimName, duration ) { + + this.mixer.removeAllActions(); + + var fromAction = new THREE.AnimationAction( this.animations[ fromAnimName ] ); + var toAction = new THREE.AnimationAction( this.animations[ toAnimName ] ); + + this.mixer.play( fromAction ); + this.mixer.play( toAction ); + + this.mixer.crossFade( fromAction, toAction, duration, true ); + + }; + + this.applyWeight = function( animName, weight ) { + + var action = this.mixer.findActionByName( animName ); + if( action ) { + action.weight = weight; + } + + }; + + this.pauseAll = function() { + + this.mixer.timeScale = 0; + + }; + + this.unPauseAll = function() { + + this.mixer.timeScale = 1; + + }; + + + this.stopAll = function() { + + this.mixer.removeAllActions(); + + }; + + this.showModel = function( boolean ) { + + this.visible = boolean; + + } + +}; + + +THREE.BlendCharacter.prototype = Object.create( THREE.SkinnedMesh.prototype ); +THREE.BlendCharacter.prototype.constructor = THREE.BlendCharacter; + +THREE.BlendCharacter.prototype.getForward = function() { + + var forward = new THREE.Vector3(); + + return function() { + + // pull the character's forward basis vector out of the matrix + forward.set( + - this.matrix.elements[ 8 ], + - this.matrix.elements[ 9 ], + - this.matrix.elements[ 10 ] + ); + + return forward; + + } + +}; + diff --git a/node_modules/three/examples/js/BlendCharacterGui.js b/node_modules/three/examples/js/BlendCharacterGui.js new file mode 100644 index 00000000..a2552a29 --- /dev/null +++ b/node_modules/three/examples/js/BlendCharacterGui.js @@ -0,0 +1,216 @@ +/** + * @author Michael Guerrero / http://realitymeltdown.com + */ + +function BlendCharacterGui( blendMesh ) { + + var controls = { + + gui: null, + "Show Model": true, + "Show Skeleton": false, + "Time Scale": 1.0, + "Step Size": 0.016, + "Crossfade Time": 3.5, + "idle": 0.33, + "walk": 0.33, + "run": 0.33 + + }; + + var blendMesh = blendMesh; + + this.showModel = function() { + + return controls[ 'Show Model' ]; + + }; + + this.showSkeleton = function() { + + return controls[ 'Show Skeleton' ]; + + }; + + this.getTimeScale = function() { + + return controls[ 'Time Scale' ]; + + }; + + this.update = function( time ) { + + var getWeight = function( actionName ) { + for( var i = 0; i < blendMesh.mixer.actions.length; i ++ ) { + var action = blendMesh.mixer.actions[i]; + if( action.clip.name === actionName ) { + return action.getWeightAt( time ); + } + } + return 0; + } + controls[ 'idle' ] = getWeight( 'idle' ); + controls[ 'walk' ] = getWeight( 'walk' ); + controls[ 'run' ] = getWeight( 'run' ); + + }; + + var init = function() { + + controls.gui = new dat.GUI(); + + var settings = controls.gui.addFolder( 'Settings' ); + var playback = controls.gui.addFolder( 'Playback' ); + var blending = controls.gui.addFolder( 'Blend Tuning' ); + + settings.add( controls, "Show Model" ).onChange( controls.showModelChanged ); + settings.add( controls, "Show Skeleton" ).onChange( controls.showSkeletonChanged ); + settings.add( controls, "Time Scale", 0, 1, 0.01 ); + settings.add( controls, "Step Size", 0.01, 0.1, 0.01 ); + settings.add( controls, "Crossfade Time", 0.1, 6.0, 0.05 ); + + // These controls execute functions + playback.add( controls, "start" ); + playback.add( controls, "pause" ); + playback.add( controls, "step" ); + playback.add( controls, "idle to walk" ); + playback.add( controls, "walk to run" ); + playback.add( controls, "warp walk to run" ); + + blending.add( controls, "idle", 0, 1, 0.01 ).listen().onChange( controls.weight ); + blending.add( controls, "walk", 0, 1, 0.01 ).listen().onChange( controls.weight ); + blending.add( controls, "run", 0, 1, 0.01 ).listen().onChange( controls.weight ); + + settings.open(); + playback.open(); + blending.open(); + + }; + + var getAnimationData = function() { + + return { + + detail: { + + anims: [ "idle", "walk", "run" ], + + weights: [ controls[ 'idle' ], + controls[ 'walk' ], + controls[ 'run' ] ] + } + + }; + + }; + + controls.start = function() { + + var startEvent = new CustomEvent( 'start-animation', getAnimationData() ); + window.dispatchEvent( startEvent ); + + }; + + controls.stop = function() { + + var stopEvent = new CustomEvent( 'stop-animation' ); + window.dispatchEvent( stopEvent ); + + }; + + controls.pause = function() { + + var pauseEvent = new CustomEvent( 'pause-animation' ); + window.dispatchEvent( pauseEvent ); + + }; + + controls.step = function() { + + var stepData = { detail: { stepSize: controls[ 'Step Size' ] } }; + window.dispatchEvent( new CustomEvent( 'step-animation', stepData ) ); + + }; + + controls.weight = function() { + + // renormalize + var sum = controls[ 'idle' ] + controls[ 'walk' ] + controls[ 'run' ]; + controls[ 'idle' ] /= sum; + controls[ 'walk' ] /= sum; + controls[ 'run' ] /= sum; + + var weightEvent = new CustomEvent( 'weight-animation', getAnimationData() ); + window.dispatchEvent( weightEvent ); + + }; + + controls.crossfade = function( from, to ) { + + var fadeData = getAnimationData(); + fadeData.detail.from = from; + fadeData.detail.to = to; + fadeData.detail.time = controls[ "Crossfade Time" ]; + + window.dispatchEvent( new CustomEvent( 'crossfade', fadeData ) ); + + }; + + controls.warp = function( from, to ) { + + var warpData = getAnimationData(); + warpData.detail.from = 'walk'; + warpData.detail.to = 'run'; + warpData.detail.time = controls[ "Crossfade Time" ]; + + window.dispatchEvent( new CustomEvent( 'warp', warpData ) ); + + }; + + controls[ 'idle to walk' ] = function() { + + controls.crossfade( 'idle', 'walk' ); + + }; + + controls[ 'walk to run' ] = function() { + + controls.crossfade( 'walk', 'run' ); + + }; + + controls[ 'warp walk to run' ] = function() { + + controls.warp( 'walk', 'run' ); + + }; + + controls.showSkeletonChanged = function() { + + var data = { + detail: { + shouldShow: controls[ 'Show Skeleton' ] + } + }; + + window.dispatchEvent( new CustomEvent( 'toggle-show-skeleton', data ) ); + + }; + + + controls.showModelChanged = function() { + + var data = { + detail: { + shouldShow: controls[ 'Show Model' ] + } + }; + + window.dispatchEvent( new CustomEvent( 'toggle-show-model', data ) ); + + }; + + + init.call( this ); + +} diff --git a/node_modules/three/examples/js/BufferGeometryUtils.js b/node_modules/three/examples/js/BufferGeometryUtils.js new file mode 100644 index 00000000..4482c5bc --- /dev/null +++ b/node_modules/three/examples/js/BufferGeometryUtils.js @@ -0,0 +1,187 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BufferGeometryUtils = { + + computeTangents: function ( geometry ) { + + var index = geometry.index; + var attributes = geometry.attributes; + + // based on http://www.terathon.com/code/tangent.html + // (per vertex tangents) + + if ( index === null || + attributes.position === undefined || + attributes.normal === undefined || + attributes.uv === undefined ) { + + console.warn( 'THREE.BufferGeometry: Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()' ); + return; + + } + + var indices = index.array; + var positions = attributes.position.array; + var normals = attributes.normal.array; + var uvs = attributes.uv.array; + + var nVertices = positions.length / 3; + + if ( attributes.tangent === undefined ) { + + geometry.addAttribute( 'tangent', new THREE.BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) ); + + } + + var tangents = attributes.tangent.array; + + var tan1 = [], tan2 = []; + + for ( var k = 0; k < nVertices; k ++ ) { + + tan1[ k ] = new THREE.Vector3(); + tan2[ k ] = new THREE.Vector3(); + + } + + var vA = new THREE.Vector3(), + vB = new THREE.Vector3(), + vC = new THREE.Vector3(), + + uvA = new THREE.Vector2(), + uvB = new THREE.Vector2(), + uvC = new THREE.Vector2(), + + sdir = new THREE.Vector3(), + tdir = new THREE.Vector3(); + + function handleTriangle( a, b, c ) { + + vA.fromArray( positions, a * 3 ); + vB.fromArray( positions, b * 3 ); + vC.fromArray( positions, c * 3 ); + + uvA.fromArray( uvs, a * 2 ); + uvB.fromArray( uvs, b * 2 ); + uvC.fromArray( uvs, c * 2 ); + + var x1 = vB.x - vA.x; + var x2 = vC.x - vA.x; + + var y1 = vB.y - vA.y; + var y2 = vC.y - vA.y; + + var z1 = vB.z - vA.z; + var z2 = vC.z - vA.z; + + var s1 = uvB.x - uvA.x; + var s2 = uvC.x - uvA.x; + + var t1 = uvB.y - uvA.y; + var t2 = uvC.y - uvA.y; + + var r = 1.0 / ( s1 * t2 - s2 * t1 ); + + sdir.set( + ( t2 * x1 - t1 * x2 ) * r, + ( t2 * y1 - t1 * y2 ) * r, + ( t2 * z1 - t1 * z2 ) * r + ); + + tdir.set( + ( s1 * x2 - s2 * x1 ) * r, + ( s1 * y2 - s2 * y1 ) * r, + ( s1 * z2 - s2 * z1 ) * r + ); + + tan1[ a ].add( sdir ); + tan1[ b ].add( sdir ); + tan1[ c ].add( sdir ); + + tan2[ a ].add( tdir ); + tan2[ b ].add( tdir ); + tan2[ c ].add( tdir ); + + } + + var groups = geometry.groups; + + if ( groups.length === 0 ) { + + groups = [ { + start: 0, + count: indices.length + } ]; + + } + + for ( var j = 0, jl = groups.length; j < jl; ++ j ) { + + var group = groups[ j ]; + + var start = group.start; + var count = group.count; + + for ( var i = start, il = start + count; i < il; i += 3 ) { + + handleTriangle( + indices[ i + 0 ], + indices[ i + 1 ], + indices[ i + 2 ] + ); + + } + + } + + var tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(); + var n = new THREE.Vector3(), n2 = new THREE.Vector3(); + var w, t, test; + + function handleVertex( v ) { + + n.fromArray( normals, v * 3 ); + n2.copy( n ); + + t = tan1[ v ]; + + // Gram-Schmidt orthogonalize + + tmp.copy( t ); + tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); + + // Calculate handedness + + tmp2.crossVectors( n2, t ); + test = tmp2.dot( tan2[ v ] ); + w = ( test < 0.0 ) ? - 1.0 : 1.0; + + tangents[ v * 4 ] = tmp.x; + tangents[ v * 4 + 1 ] = tmp.y; + tangents[ v * 4 + 2 ] = tmp.z; + tangents[ v * 4 + 3 ] = w; + + } + + for ( var j = 0, jl = groups.length; j < jl; ++ j ) { + + var group = groups[ j ]; + + var start = group.start; + var count = group.count; + + for ( var i = start, il = start + count; i < il; i += 3 ) { + + handleVertex( indices[ i + 0 ] ); + handleVertex( indices[ i + 1 ] ); + handleVertex( indices[ i + 2 ] ); + + } + + } + + } + +}; diff --git a/node_modules/three/examples/js/Car.js b/node_modules/three/examples/js/Car.js new file mode 100644 index 00000000..2893bb94 --- /dev/null +++ b/node_modules/three/examples/js/Car.js @@ -0,0 +1,407 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Car = function () { + + var scope = this; + + // car geometry manual parameters + + this.modelScale = 1; + + this.backWheelOffset = 2; + + this.autoWheelGeometry = true; + + // car geometry parameters automatically set from wheel mesh + // - assumes wheel mesh is front left wheel in proper global + // position with respect to body mesh + // - other wheels are mirrored against car root + // - if necessary back wheels can be offset manually + + this.wheelOffset = new THREE.Vector3(); + + this.wheelDiameter = 1; + + // car "feel" parameters + + this.MAX_SPEED = 2200; + this.MAX_REVERSE_SPEED = - 1500; + + this.MAX_WHEEL_ROTATION = 0.6; + + this.FRONT_ACCELERATION = 1250; + this.BACK_ACCELERATION = 1500; + + this.WHEEL_ANGULAR_ACCELERATION = 1.5; + + this.FRONT_DECCELERATION = 750; + this.WHEEL_ANGULAR_DECCELERATION = 1.0; + + this.STEERING_RADIUS_RATIO = 0.0023; + + this.MAX_TILT_SIDES = 0.05; + this.MAX_TILT_FRONTBACK = 0.015; + + // internal control variables + + this.speed = 0; + this.acceleration = 0; + + this.wheelOrientation = 0; + this.carOrientation = 0; + + // car rigging + + this.root = new THREE.Object3D(); + + this.frontLeftWheelRoot = new THREE.Object3D(); + this.frontRightWheelRoot = new THREE.Object3D(); + + this.bodyMesh = null; + + this.frontLeftWheelMesh = null; + this.frontRightWheelMesh = null; + + this.backLeftWheelMesh = null; + this.backRightWheelMesh = null; + + this.bodyGeometry = null; + this.wheelGeometry = null; + + this.bodyMaterials = null; + this.wheelMaterials = null; + + // internal helper variables + + this.loaded = false; + + this.meshes = []; + + // API + + this.enableShadows = function ( enable ) { + + for ( var i = 0; i < this.meshes.length; i ++ ) { + + this.meshes[ i ].castShadow = enable; + this.meshes[ i ].receiveShadow = enable; + + } + + }; + + this.setVisible = function ( enable ) { + + for ( var i = 0; i < this.meshes.length; i ++ ) { + + this.meshes[ i ].visible = enable; + this.meshes[ i ].visible = enable; + + } + + }; + + this.loadPartsJSON = function ( bodyURL, wheelURL ) { + + var loader = new THREE.JSONLoader(); + + loader.load( bodyURL, function( geometry, materials ) { + + createBody( geometry, materials ) + + } ); + loader.load( wheelURL, function( geometry, materials ) { + + createWheels( geometry, materials ) + + } ); + + }; + + this.loadPartsBinary = function ( bodyURL, wheelURL ) { + + var loader = new THREE.BinaryLoader(); + + loader.load( bodyURL, function( geometry, materials ) { + + createBody( geometry, materials ) + + } ); + loader.load( wheelURL, function( geometry, materials ) { + + createWheels( geometry, materials ) + + } ); + + }; + + this.updateCarModel = function ( delta, controls ) { + + // speed and wheels based on controls + + if ( controls.moveForward ) { + + this.speed = THREE.Math.clamp( this.speed + delta * this.FRONT_ACCELERATION, this.MAX_REVERSE_SPEED, this.MAX_SPEED ); + this.acceleration = THREE.Math.clamp( this.acceleration + delta, - 1, 1 ); + + } + + if ( controls.moveBackward ) { + + + this.speed = THREE.Math.clamp( this.speed - delta * this.BACK_ACCELERATION, this.MAX_REVERSE_SPEED, this.MAX_SPEED ); + this.acceleration = THREE.Math.clamp( this.acceleration - delta, - 1, 1 ); + + } + + if ( controls.moveLeft ) { + + this.wheelOrientation = THREE.Math.clamp( this.wheelOrientation + delta * this.WHEEL_ANGULAR_ACCELERATION, - this.MAX_WHEEL_ROTATION, this.MAX_WHEEL_ROTATION ); + + } + + if ( controls.moveRight ) { + + this.wheelOrientation = THREE.Math.clamp( this.wheelOrientation - delta * this.WHEEL_ANGULAR_ACCELERATION, - this.MAX_WHEEL_ROTATION, this.MAX_WHEEL_ROTATION ); + + } + + // speed decay + + if ( ! ( controls.moveForward || controls.moveBackward ) ) { + + if ( this.speed > 0 ) { + + var k = exponentialEaseOut( this.speed / this.MAX_SPEED ); + + this.speed = THREE.Math.clamp( this.speed - k * delta * this.FRONT_DECCELERATION, 0, this.MAX_SPEED ); + this.acceleration = THREE.Math.clamp( this.acceleration - k * delta, 0, 1 ); + + } else { + + var k = exponentialEaseOut( this.speed / this.MAX_REVERSE_SPEED ); + + this.speed = THREE.Math.clamp( this.speed + k * delta * this.BACK_ACCELERATION, this.MAX_REVERSE_SPEED, 0 ); + this.acceleration = THREE.Math.clamp( this.acceleration + k * delta, - 1, 0 ); + + } + + + } + + // steering decay + + if ( ! ( controls.moveLeft || controls.moveRight ) ) { + + if ( this.wheelOrientation > 0 ) { + + this.wheelOrientation = THREE.Math.clamp( this.wheelOrientation - delta * this.WHEEL_ANGULAR_DECCELERATION, 0, this.MAX_WHEEL_ROTATION ); + + } else { + + this.wheelOrientation = THREE.Math.clamp( this.wheelOrientation + delta * this.WHEEL_ANGULAR_DECCELERATION, - this.MAX_WHEEL_ROTATION, 0 ); + + } + + } + + // car update + + var forwardDelta = this.speed * delta; + + this.carOrientation += ( forwardDelta * this.STEERING_RADIUS_RATIO ) * this.wheelOrientation; + + // displacement + + this.root.position.x += Math.sin( this.carOrientation ) * forwardDelta; + this.root.position.z += Math.cos( this.carOrientation ) * forwardDelta; + + // steering + + this.root.rotation.y = this.carOrientation; + + // tilt + + if ( this.loaded ) { + + this.bodyMesh.rotation.z = this.MAX_TILT_SIDES * this.wheelOrientation * ( this.speed / this.MAX_SPEED ); + this.bodyMesh.rotation.x = - this.MAX_TILT_FRONTBACK * this.acceleration; + + } + + // wheels rolling + + var angularSpeedRatio = 1 / ( this.modelScale * ( this.wheelDiameter / 2 ) ); + + var wheelDelta = forwardDelta * angularSpeedRatio; + + if ( this.loaded ) { + + this.frontLeftWheelMesh.rotation.x += wheelDelta; + this.frontRightWheelMesh.rotation.x += wheelDelta; + this.backLeftWheelMesh.rotation.x += wheelDelta; + this.backRightWheelMesh.rotation.x += wheelDelta; + + } + + // front wheels steering + + this.frontLeftWheelRoot.rotation.y = this.wheelOrientation; + this.frontRightWheelRoot.rotation.y = this.wheelOrientation; + + }; + + // internal helper methods + + function createBody ( geometry, materials ) { + + scope.bodyGeometry = geometry; + scope.bodyMaterials = materials; + + createCar(); + + } + + function createWheels ( geometry, materials ) { + + scope.wheelGeometry = geometry; + scope.wheelMaterials = materials; + + createCar(); + + } + + function createCar () { + + if ( scope.bodyGeometry && scope.wheelGeometry ) { + + // compute wheel geometry parameters + + if ( scope.autoWheelGeometry ) { + + scope.wheelGeometry.computeBoundingBox(); + + var bb = scope.wheelGeometry.boundingBox; + + scope.wheelOffset.addVectors( bb.min, bb.max ); + scope.wheelOffset.multiplyScalar( 0.5 ); + + scope.wheelDiameter = bb.max.y - bb.min.y; + + scope.wheelGeometry.center(); + + } + + // rig the car + + var s = scope.modelScale, + delta = new THREE.Vector3(); + + var bodyFaceMaterial = new THREE.MeshFaceMaterial( scope.bodyMaterials ); + var wheelFaceMaterial = new THREE.MeshFaceMaterial( scope.wheelMaterials ); + + // body + + scope.bodyMesh = new THREE.Mesh( scope.bodyGeometry, bodyFaceMaterial ); + scope.bodyMesh.scale.set( s, s, s ); + + scope.root.add( scope.bodyMesh ); + + // front left wheel + + delta.multiplyVectors( scope.wheelOffset, new THREE.Vector3( s, s, s ) ); + + scope.frontLeftWheelRoot.position.add( delta ); + + scope.frontLeftWheelMesh = new THREE.Mesh( scope.wheelGeometry, wheelFaceMaterial ); + scope.frontLeftWheelMesh.scale.set( s, s, s ); + + scope.frontLeftWheelRoot.add( scope.frontLeftWheelMesh ); + scope.root.add( scope.frontLeftWheelRoot ); + + // front right wheel + + delta.multiplyVectors( scope.wheelOffset, new THREE.Vector3( - s, s, s ) ); + + scope.frontRightWheelRoot.position.add( delta ); + + scope.frontRightWheelMesh = new THREE.Mesh( scope.wheelGeometry, wheelFaceMaterial ); + + scope.frontRightWheelMesh.scale.set( s, s, s ); + scope.frontRightWheelMesh.rotation.z = Math.PI; + + scope.frontRightWheelRoot.add( scope.frontRightWheelMesh ); + scope.root.add( scope.frontRightWheelRoot ); + + // back left wheel + + delta.multiplyVectors( scope.wheelOffset, new THREE.Vector3( s, s, - s ) ); + delta.z -= scope.backWheelOffset; + + scope.backLeftWheelMesh = new THREE.Mesh( scope.wheelGeometry, wheelFaceMaterial ); + + scope.backLeftWheelMesh.position.add( delta ); + scope.backLeftWheelMesh.scale.set( s, s, s ); + + scope.root.add( scope.backLeftWheelMesh ); + + // back right wheel + + delta.multiplyVectors( scope.wheelOffset, new THREE.Vector3( - s, s, - s ) ); + delta.z -= scope.backWheelOffset; + + scope.backRightWheelMesh = new THREE.Mesh( scope.wheelGeometry, wheelFaceMaterial ); + + scope.backRightWheelMesh.position.add( delta ); + scope.backRightWheelMesh.scale.set( s, s, s ); + scope.backRightWheelMesh.rotation.z = Math.PI; + + scope.root.add( scope.backRightWheelMesh ); + + // cache meshes + + scope.meshes = [ scope.bodyMesh, scope.frontLeftWheelMesh, scope.frontRightWheelMesh, scope.backLeftWheelMesh, scope.backRightWheelMesh ]; + + // callback + + scope.loaded = true; + + if ( scope.callback ) { + + scope.callback( scope ); + + } + + } + + } + + function quadraticEaseOut( k ) { + + return - k * ( k - 2 ); + + } + function cubicEaseOut( k ) { + + return -- k * k * k + 1; + + } + function circularEaseOut( k ) { + + return Math.sqrt( 1 - -- k * k ); + + } + function sinusoidalEaseOut( k ) { + + return Math.sin( k * Math.PI / 2 ); + + } + function exponentialEaseOut( k ) { + + return k === 1 ? 1 : - Math.pow( 2, - 10 * k ) + 1; + + } + +}; diff --git a/node_modules/three/examples/js/Cloth.js b/node_modules/three/examples/js/Cloth.js new file mode 100644 index 00000000..b03668d0 --- /dev/null +++ b/node_modules/three/examples/js/Cloth.js @@ -0,0 +1,325 @@ +/* + * Cloth Simulation using a relaxed constrains solver + */ + +// Suggested Readings + +// Advanced Character Physics by Thomas Jakobsen Character +// http://freespace.virgin.net/hugo.elias/models/m_cloth.htm +// http://en.wikipedia.org/wiki/Cloth_modeling +// http://cg.alexandra.dk/tag/spring-mass-system/ +// Real-time Cloth Animation http://www.darwin3d.com/gamedev/articles/col0599.pdf + +var DAMPING = 0.03; +var DRAG = 1 - DAMPING; +var MASS = .1; +var restDistance = 25; + + +var xSegs = 10; // +var ySegs = 10; // + +var clothFunction = plane( restDistance * xSegs, restDistance * ySegs ); + +var cloth = new Cloth( xSegs, ySegs ); + +var GRAVITY = 981 * 1.4; // +var gravity = new THREE.Vector3( 0, - GRAVITY, 0 ).multiplyScalar( MASS ); + + +var TIMESTEP = 18 / 1000; +var TIMESTEP_SQ = TIMESTEP * TIMESTEP; + +var pins = []; + + +var wind = true; +var windStrength = 2; +var windForce = new THREE.Vector3( 0, 0, 0 ); + +var ballPosition = new THREE.Vector3( 0, - 45, 0 ); +var ballSize = 60; //40 + +var tmpForce = new THREE.Vector3(); + +var lastTime; + + +function plane( width, height ) { + + return function( u, v ) { + + var x = ( u - 0.5 ) * width; + var y = ( v + 0.5 ) * height; + var z = 0; + + return new THREE.Vector3( x, y, z ); + + }; + +} + +function Particle( x, y, z, mass ) { + + this.position = clothFunction( x, y ); // position + this.previous = clothFunction( x, y ); // previous + this.original = clothFunction( x, y ); + this.a = new THREE.Vector3( 0, 0, 0 ); // acceleration + this.mass = mass; + this.invMass = 1 / mass; + this.tmp = new THREE.Vector3(); + this.tmp2 = new THREE.Vector3(); + +} + +// Force -> Acceleration +Particle.prototype.addForce = function( force ) { + + this.a.add( + this.tmp2.copy( force ).multiplyScalar( this.invMass ) + ); + +}; + + +// Performs verlet integration +Particle.prototype.integrate = function( timesq ) { + + var newPos = this.tmp.subVectors( this.position, this.previous ); + newPos.multiplyScalar( DRAG ).add( this.position ); + newPos.add( this.a.multiplyScalar( timesq ) ); + + this.tmp = this.previous; + this.previous = this.position; + this.position = newPos; + + this.a.set( 0, 0, 0 ); + +}; + + +var diff = new THREE.Vector3(); + +function satisifyConstrains( p1, p2, distance ) { + + diff.subVectors( p2.position, p1.position ); + var currentDist = diff.length(); + if ( currentDist == 0 ) return; // prevents division by 0 + var correction = diff.multiplyScalar( 1 - distance / currentDist ); + var correctionHalf = correction.multiplyScalar( 0.5 ); + p1.position.add( correctionHalf ); + p2.position.sub( correctionHalf ); + +} + + +function Cloth( w, h ) { + + w = w || 10; + h = h || 10; + this.w = w; + this.h = h; + + var particles = []; + var constrains = []; + + var u, v; + + // Create particles + for ( v = 0; v <= h; v ++ ) { + + for ( u = 0; u <= w; u ++ ) { + + particles.push( + new Particle( u / w, v / h, 0, MASS ) + ); + + } + + } + + // Structural + + for ( v = 0; v < h; v ++ ) { + + for ( u = 0; u < w; u ++ ) { + + constrains.push( [ + particles[ index( u, v ) ], + particles[ index( u, v + 1 ) ], + restDistance + ] ); + + constrains.push( [ + particles[ index( u, v ) ], + particles[ index( u + 1, v ) ], + restDistance + ] ); + + } + + } + + for ( u = w, v = 0; v < h; v ++ ) { + + constrains.push( [ + particles[ index( u, v ) ], + particles[ index( u, v + 1 ) ], + restDistance + + ] ); + + } + + for ( v = h, u = 0; u < w; u ++ ) { + + constrains.push( [ + particles[ index( u, v ) ], + particles[ index( u + 1, v ) ], + restDistance + ] ); + + } + + + // While many system uses shear and bend springs, + // the relax constrains model seem to be just fine + // using structural springs. + // Shear + // var diagonalDist = Math.sqrt(restDistance * restDistance * 2); + + + // for (v=0;vWebGL.
', + 'Find out how to get it here.' + ].join( '\n' ) : [ + 'Your browser does not seem to support WebGL.
', + 'Find out how to get it here.' + ].join( '\n' ); + + } + + return element; + + }, + + addGetWebGLMessage: function ( parameters ) { + + var parent, id, element; + + parameters = parameters || {}; + + parent = parameters.parent !== undefined ? parameters.parent : document.body; + id = parameters.id !== undefined ? parameters.id : 'oldie'; + + element = Detector.getWebGLErrorMessage(); + element.id = id; + + parent.appendChild( element ); + + } + +}; + +// browserify support +if ( typeof module === 'object' ) { + + module.exports = Detector; + +} diff --git a/node_modules/three/examples/js/GPUParticleSystem.js b/node_modules/three/examples/js/GPUParticleSystem.js new file mode 100644 index 00000000..0982edc8 --- /dev/null +++ b/node_modules/three/examples/js/GPUParticleSystem.js @@ -0,0 +1,505 @@ +/* + * GPU Particle System + * @author flimshaw - Charlie Hoey - http://charliehoey.com + * + * A simple to use, general purpose GPU system. Particles are spawn-and-forget with + * several options available, and do not require monitoring or cleanup after spawning. + * Because the paths of all particles are completely deterministic once spawned, the scale + * and direction of time is also variable. + * + * Currently uses a static wrapping perlin noise texture for turbulence, and a small png texture for + * particles, but adding support for a particle texture atlas or changing to a different type of turbulence + * would be a fairly light day's work. + * + * Shader and javascript packing code derrived from several Stack Overflow examples. + * + */ + +THREE.GPUParticleSystem = function(options) { + + var self = this; + var options = options || {}; + + // parse options and use defaults + self.PARTICLE_COUNT = options.maxParticles || 1000000; + self.PARTICLE_CONTAINERS = options.containerCount || 1; + self.PARTICLES_PER_CONTAINER = Math.ceil(self.PARTICLE_COUNT / self.PARTICLE_CONTAINERS); + self.PARTICLE_CURSOR = 0; + self.time = 0; + + + // Custom vertex and fragement shader + var GPUParticleShader = { + + vertexShader: [ + + 'precision highp float;', + 'const vec4 bitSh = vec4(256. * 256. * 256., 256. * 256., 256., 1.);', + 'const vec4 bitMsk = vec4(0.,vec3(1./256.0));', + 'const vec4 bitShifts = vec4(1.) / bitSh;', + + '#define FLOAT_MAX 1.70141184e38', + '#define FLOAT_MIN 1.17549435e-38', + + 'lowp vec4 encode_float(highp float v) {', + 'highp float av = abs(v);', + + '//Handle special cases', + 'if(av < FLOAT_MIN) {', + 'return vec4(0.0, 0.0, 0.0, 0.0);', + '} else if(v > FLOAT_MAX) {', + 'return vec4(127.0, 128.0, 0.0, 0.0) / 255.0;', + '} else if(v < -FLOAT_MAX) {', + 'return vec4(255.0, 128.0, 0.0, 0.0) / 255.0;', + '}', + + 'highp vec4 c = vec4(0,0,0,0);', + + '//Compute exponent and mantissa', + 'highp float e = floor(log2(av));', + 'highp float m = av * pow(2.0, -e) - 1.0;', + + //Unpack mantissa + 'c[1] = floor(128.0 * m);', + 'm -= c[1] / 128.0;', + 'c[2] = floor(32768.0 * m);', + 'm -= c[2] / 32768.0;', + 'c[3] = floor(8388608.0 * m);', + + '//Unpack exponent', + 'highp float ebias = e + 127.0;', + 'c[0] = floor(ebias / 2.0);', + 'ebias -= c[0] * 2.0;', + 'c[1] += floor(ebias) * 128.0;', + + '//Unpack sign bit', + 'c[0] += 128.0 * step(0.0, -v);', + + '//Scale back to range', + 'return c / 255.0;', + '}', + + 'vec4 pack(const in float depth)', + '{', + 'const vec4 bit_shift = vec4(256.0*256.0*256.0, 256.0*256.0, 256.0, 1.0);', + 'const vec4 bit_mask = vec4(0.0, 1.0/256.0, 1.0/256.0, 1.0/256.0);', + 'vec4 res = fract(depth * bit_shift);', + 'res -= res.xxyz * bit_mask;', + 'return res;', + '}', + + 'float unpack(const in vec4 rgba_depth)', + '{', + 'const vec4 bit_shift = vec4(1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1.0);', + 'float depth = dot(rgba_depth, bit_shift);', + 'return depth;', + '}', + + 'uniform float uTime;', + 'uniform float uScale;', + 'uniform sampler2D tNoise;', + + 'attribute vec4 particlePositionsStartTime;', + 'attribute vec4 particleVelColSizeLife;', + + 'varying vec4 vColor;', + 'varying float lifeLeft;', + + 'void main() {', + + '// unpack things from our attributes', + 'vColor = encode_float( particleVelColSizeLife.y );', + + '// convert our velocity back into a value we can use', + 'vec4 velTurb = encode_float( particleVelColSizeLife.x );', + 'vec3 velocity = vec3( velTurb.xyz );', + 'float turbulence = velTurb.w;', + + 'vec3 newPosition;', + + 'float timeElapsed = uTime - particlePositionsStartTime.a;', + + 'lifeLeft = 1. - (timeElapsed / particleVelColSizeLife.w);', + + 'gl_PointSize = ( uScale * particleVelColSizeLife.z ) * lifeLeft;', + + 'velocity.x = ( velocity.x - .5 ) * 3.;', + 'velocity.y = ( velocity.y - .5 ) * 3.;', + 'velocity.z = ( velocity.z - .5 ) * 3.;', + + 'newPosition = particlePositionsStartTime.xyz + ( velocity * 10. ) * ( uTime - particlePositionsStartTime.a );', + + 'vec3 noise = texture2D( tNoise, vec2( newPosition.x * .015 + (uTime * .05), newPosition.y * .02 + (uTime * .015) )).rgb;', + 'vec3 noiseVel = ( noise.rgb - .5 ) * 30.;', + + 'newPosition = mix(newPosition, newPosition + vec3(noiseVel * ( turbulence * 5. ) ), (timeElapsed / particleVelColSizeLife.a) );', + + 'if( velocity.y > 0. && velocity.y < .05 ) {', + 'lifeLeft = 0.;', + '}', + + 'if( velocity.x < -1.45 ) {', + 'lifeLeft = 0.;', + '}', + + 'if( timeElapsed > 0. ) {', + 'gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );', + '} else {', + 'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', + 'lifeLeft = 0.;', + 'gl_PointSize = 0.;', + '}', + '}' + + ].join("\n"), + + fragmentShader: [ + + 'float scaleLinear(float value, vec2 valueDomain) {', + 'return (value - valueDomain.x) / (valueDomain.y - valueDomain.x);', + '}', + + 'float scaleLinear(float value, vec2 valueDomain, vec2 valueRange) {', + 'return mix(valueRange.x, valueRange.y, scaleLinear(value, valueDomain));', + '}', + + 'varying vec4 vColor;', + 'varying float lifeLeft;', + + 'uniform sampler2D tSprite;', + + 'void main() {', + + 'float alpha = 0.;', + + 'if( lifeLeft > .995 ) {', + 'alpha = scaleLinear( lifeLeft, vec2(1., .995), vec2(0., 1.));//mix( 0., 1., ( lifeLeft - .95 ) * 100. ) * .75;', + '} else {', + 'alpha = lifeLeft * .75;', + '}', + + 'vec4 tex = texture2D( tSprite, gl_PointCoord );', + + 'gl_FragColor = vec4( vColor.rgb * tex.a, alpha * tex.a );', + '}' + + ].join("\n") + + }; + + // preload a million random numbers + self.rand = []; + + for (var i = 1e5; i > 0; i--) { + self.rand.push(Math.random() - .5); + } + + self.random = function() { + return ++i >= self.rand.length ? self.rand[i = 1] : self.rand[i]; + } + + self.particleNoiseTex = THREE.ImageUtils.loadTexture("textures/perlin-512.png"); + self.particleNoiseTex.wrapS = self.particleNoiseTex.wrapT = THREE.RepeatWrapping; + + self.particleSpriteTex = THREE.ImageUtils.loadTexture("textures/particle2.png"); + self.particleSpriteTex.wrapS = self.particleSpriteTex.wrapT = THREE.RepeatWrapping; + + self.particleShaderMat = new THREE.ShaderMaterial({ + transparent: true, + depthWrite: false, + uniforms: { + "uTime": { + type: "f", + value: 0.0 + }, + "uScale": { + type: "f", + value: 1.0 + }, + "tNoise": { + type: "t", + value: self.particleNoiseTex + }, + "tSprite": { + type: "t", + value: self.particleSpriteTex + } + }, + blending: THREE.AdditiveBlending, + vertexShader: GPUParticleShader.vertexShader, + fragmentShader: GPUParticleShader.fragmentShader + }); + + // define defaults for all values + self.particleShaderMat.defaultAttributeValues.particlePositionsStartTime = [0, 0, 0, 0]; + self.particleShaderMat.defaultAttributeValues.particleVelColSizeLife = [0, 0, 0, 0]; + + self.particleContainers = []; + + + // extend Object3D + THREE.Object3D.apply(this, arguments); + + this.init = function() { + + for (var i = 0; i < self.PARTICLE_CONTAINERS; i++) { + + var c = new THREE.GPUParticleContainer(self.PARTICLES_PER_CONTAINER, self); + self.particleContainers.push(c); + self.add(c); + + } + + } + + this.spawnParticle = function(options) { + + self.PARTICLE_CURSOR++; + if (self.PARTICLE_CURSOR >= self.PARTICLE_COUNT) { + self.PARTICLE_CURSOR = 1; + } + + var currentContainer = self.particleContainers[Math.floor(self.PARTICLE_CURSOR / self.PARTICLES_PER_CONTAINER)]; + + currentContainer.spawnParticle(options); + + } + + this.update = function(time) { + for (var i = 0; i < self.PARTICLE_CONTAINERS; i++) { + + self.particleContainers[i].update(time); + + } + }; + + this.init(); + +} + +THREE.GPUParticleSystem.prototype = Object.create(THREE.Object3D.prototype); +THREE.GPUParticleSystem.prototype.constructor = THREE.GPUParticleSystem; + + +// Subclass for particle containers, allows for very large arrays to be spread out +THREE.GPUParticleContainer = function(maxParticles, particleSystem) { + + var self = this; + self.PARTICLE_COUNT = maxParticles || 100000; + self.PARTICLE_CURSOR = 0; + self.time = 0; + self.DPR = window.devicePixelRatio; + self.GPUParticleSystem = particleSystem; + + var particlesPerArray = Math.floor(self.PARTICLE_COUNT / self.MAX_ATTRIBUTES); + + // extend Object3D + THREE.Object3D.apply(this, arguments); + + // construct a couple small arrays used for packing variables into floats etc + var UINT8_VIEW = new Uint8Array(4) + var FLOAT_VIEW = new Float32Array(UINT8_VIEW.buffer) + + function decodeFloat(x, y, z, w) { + UINT8_VIEW[0] = Math.floor(w) + UINT8_VIEW[1] = Math.floor(z) + UINT8_VIEW[2] = Math.floor(y) + UINT8_VIEW[3] = Math.floor(x) + return FLOAT_VIEW[0] + } + + function componentToHex(c) { + var hex = c.toString(16); + return hex.length == 1 ? "0" + hex : hex; + } + + function rgbToHex(r, g, b) { + return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b); + } + + function hexToRgb(hex) { + var r = hex >> 16; + var g = (hex & 0x00FF00) >> 8; + var b = hex & 0x0000FF; + + if (r > 0) r--; + if (g > 0) g--; + if (b > 0) b--; + + return [r, g, b]; + }; + + self.particles = []; + self.deadParticles = []; + self.particlesAvailableSlot = []; + + // create a container for particles + self.particleUpdate = false; + + // Shader Based Particle System + self.particleShaderGeo = new THREE.BufferGeometry(); + + // new hyper compressed attributes + self.particleVertices = new Float32Array(self.PARTICLE_COUNT * 3); // position + self.particlePositionsStartTime = new Float32Array(self.PARTICLE_COUNT * 4); // position + self.particleVelColSizeLife = new Float32Array(self.PARTICLE_COUNT * 4); + + for (var i = 0; i < self.PARTICLE_COUNT; i++) { + self.particlePositionsStartTime[i * 4 + 0] = 100; //x + self.particlePositionsStartTime[i * 4 + 1] = 0; //y + self.particlePositionsStartTime[i * 4 + 2] = 0.0; //z + self.particlePositionsStartTime[i * 4 + 3] = 0.0; //startTime + + self.particleVertices[i * 3 + 0] = 0; //x + self.particleVertices[i * 3 + 1] = 0; //y + self.particleVertices[i * 3 + 2] = 0.0; //z + + self.particleVelColSizeLife[i * 4 + 0] = decodeFloat(128, 128, 0, 0); //vel + self.particleVelColSizeLife[i * 4 + 1] = decodeFloat(0, 254, 0, 254); //color + self.particleVelColSizeLife[i * 4 + 2] = 1.0; //size + self.particleVelColSizeLife[i * 4 + 3] = 0.0; //lifespan + } + + self.particleShaderGeo.addAttribute('position', new THREE.BufferAttribute(self.particleVertices, 3)); + self.particleShaderGeo.addAttribute('particlePositionsStartTime', new THREE.BufferAttribute(self.particlePositionsStartTime, 4).setDynamic(true)); + self.particleShaderGeo.addAttribute('particleVelColSizeLife', new THREE.BufferAttribute(self.particleVelColSizeLife, 4).setDynamic(true)); + + self.posStart = self.particleShaderGeo.getAttribute('particlePositionsStartTime') + self.velCol = self.particleShaderGeo.getAttribute('particleVelColSizeLife'); + + self.particleShaderMat = self.GPUParticleSystem.particleShaderMat; + + this.init = function() { + self.particleSystem = new THREE.Points(self.particleShaderGeo, self.particleShaderMat); + self.particleSystem.frustumCulled = false; + this.add(self.particleSystem); + }; + + var options = {}, + position = new THREE.Vector3(), + velocity = new THREE.Vector3(), + positionRandomness = 0., + velocityRandomness = 0., + color = 0xffffff, + colorRandomness = 0., + turbulence = 0., + lifetime = 0., + size = 0., + sizeRandomness = 0., + i; + + var maxVel = 2; + var maxSource = 250; + this.offset = 0; + this.count = 0; + + this.spawnParticle = function(options) { + + options = options || {}; + + // setup reasonable default values for all arguments + position = options.position !== undefined ? position.copy(options.position) : position.set(0., 0., 0.); + velocity = options.velocity !== undefined ? velocity.copy(options.velocity) : velocity.set(0., 0., 0.); + positionRandomness = options.positionRandomness !== undefined ? options.positionRandomness : 0.0; + velocityRandomness = options.velocityRandomness !== undefined ? options.velocityRandomness : 0.0; + color = options.color !== undefined ? options.color : 0xffffff; + colorRandomness = options.colorRandomness !== undefined ? options.colorRandomness : 1.0; + turbulence = options.turbulence !== undefined ? options.turbulence : 1.0; + lifetime = options.lifetime !== undefined ? options.lifetime : 5.0; + size = options.size !== undefined ? options.size : 10; + sizeRandomness = options.sizeRandomness !== undefined ? options.sizeRandomness : 0.0, + smoothPosition = options.smoothPosition !== undefined ? options.smoothPosition : false; + + if (self.DPR !== undefined) size *= self.DPR; + + i = self.PARTICLE_CURSOR; + + self.posStart.array[i * 4 + 0] = position.x + ((particleSystem.random()) * positionRandomness); // - ( velocity.x * particleSystem.random() ); //x + self.posStart.array[i * 4 + 1] = position.y + ((particleSystem.random()) * positionRandomness); // - ( velocity.y * particleSystem.random() ); //y + self.posStart.array[i * 4 + 2] = position.z + ((particleSystem.random()) * positionRandomness); // - ( velocity.z * particleSystem.random() ); //z + self.posStart.array[i * 4 + 3] = self.time + (particleSystem.random() * 2e-2); //startTime + + if (smoothPosition === true) { + self.posStart.array[i * 4 + 0] += -(velocity.x * particleSystem.random()); //x + self.posStart.array[i * 4 + 1] += -(velocity.y * particleSystem.random()); //y + self.posStart.array[i * 4 + 2] += -(velocity.z * particleSystem.random()); //z + } + + var velX = velocity.x + (particleSystem.random()) * velocityRandomness; + var velY = velocity.y + (particleSystem.random()) * velocityRandomness; + var velZ = velocity.z + (particleSystem.random()) * velocityRandomness; + + // convert turbulence rating to something we can pack into a vec4 + var turbulence = Math.floor(turbulence * 254); + + // clamp our value to between 0. and 1. + velX = Math.floor(maxSource * ((velX - -maxVel) / (maxVel - -maxVel))); + velY = Math.floor(maxSource * ((velY - -maxVel) / (maxVel - -maxVel))); + velZ = Math.floor(maxSource * ((velZ - -maxVel) / (maxVel - -maxVel))); + + self.velCol.array[i * 4 + 0] = decodeFloat(velX, velY, velZ, turbulence); //vel + + var rgb = hexToRgb(color); + + for (var c = 0; c < rgb.length; c++) { + rgb[c] = Math.floor(rgb[c] + ((particleSystem.random()) * colorRandomness) * 254); + if (rgb[c] > 254) rgb[c] = 254; + if (rgb[c] < 0) rgb[c] = 0; + } + + self.velCol.array[i * 4 + 1] = decodeFloat(rgb[0], rgb[1], rgb[2], 254); //color + self.velCol.array[i * 4 + 2] = size + (particleSystem.random()) * sizeRandomness; //size + self.velCol.array[i * 4 + 3] = lifetime; //lifespan + + if (this.offset == 0) { + this.offset = self.PARTICLE_CURSOR; + } + + self.count++; + + self.PARTICLE_CURSOR++; + + if (self.PARTICLE_CURSOR >= self.PARTICLE_COUNT) { + self.PARTICLE_CURSOR = 0; + } + + self.particleUpdate = true; + + } + + this.update = function(time) { + + self.time = time; + self.particleShaderMat.uniforms['uTime'].value = time; + + this.geometryUpdate(); + + }; + + this.geometryUpdate = function() { + if (self.particleUpdate == true) { + self.particleUpdate = false; + + // if we can get away with a partial buffer update, do so + if (self.offset + self.count < self.PARTICLE_COUNT) { + self.posStart.updateRange.offset = self.velCol.updateRange.offset = self.offset * 4; + self.posStart.updateRange.count = self.velCol.updateRange.count = self.count * 4; + } else { + self.posStart.updateRange.offset = 0; + self.posStart.updateRange.count = self.velCol.updateRange.count = (self.PARTICLE_COUNT * 4); + } + + self.posStart.needsUpdate = true; + self.velCol.needsUpdate = true; + + self.offset = 0; + self.count = 0; + } + } + + this.init(); + +} + +THREE.GPUParticleContainer.prototype = Object.create(THREE.Object3D.prototype); +THREE.GPUParticleContainer.prototype.constructor = THREE.GPUParticleContainer; diff --git a/node_modules/three/examples/js/Gyroscope.js b/node_modules/three/examples/js/Gyroscope.js new file mode 100644 index 00000000..241bcb1a --- /dev/null +++ b/node_modules/three/examples/js/Gyroscope.js @@ -0,0 +1,65 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Gyroscope = function () { + + THREE.Object3D.call( this ); + +}; + +THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Gyroscope.prototype.constructor = THREE.Gyroscope; + +THREE.Gyroscope.prototype.updateMatrixWorld = ( function () { + + var translationObject = new THREE.Vector3(); + var quaternionObject = new THREE.Quaternion(); + var scaleObject = new THREE.Vector3(); + + var translationWorld = new THREE.Vector3(); + var quaternionWorld = new THREE.Quaternion(); + var scaleWorld = new THREE.Vector3(); + + return function updateMatrixWorld( force ) { + + this.matrixAutoUpdate && this.updateMatrix(); + + // update matrixWorld + + if ( this.matrixWorldNeedsUpdate || force ) { + + if ( this.parent !== null ) { + + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + + this.matrixWorld.decompose( translationWorld, quaternionWorld, scaleWorld ); + this.matrix.decompose( translationObject, quaternionObject, scaleObject ); + + this.matrixWorld.compose( translationWorld, quaternionObject, scaleWorld ); + + + } else { + + this.matrixWorld.copy( this.matrix ); + + } + + + this.matrixWorldNeedsUpdate = false; + + force = true; + + } + + // update children + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].updateMatrixWorld( force ); + + } + + }; + +}() ); diff --git a/node_modules/three/examples/js/ImprovedNoise.js b/node_modules/three/examples/js/ImprovedNoise.js new file mode 100644 index 00000000..889bf2c9 --- /dev/null +++ b/node_modules/three/examples/js/ImprovedNoise.js @@ -0,0 +1,71 @@ +// http://mrl.nyu.edu/~perlin/noise/ + +var ImprovedNoise = function () { + + var p = [ 151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10, + 23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87, + 174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211, + 133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208, + 89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,5, + 202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,183,170,213,119, + 248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,224,232, + 178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249, + 14,239,107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205, + 93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 ]; + + for (var i = 0; i < 256 ; i ++) { + + p[256 + i] = p[i]; + + } + + function fade(t) { + + return t * t * t * (t * (t * 6 - 15) + 10); + + } + + function lerp(t, a, b) { + + return a + t * (b - a); + + } + + function grad(hash, x, y, z) { + + var h = hash & 15; + var u = h < 8 ? x : y, v = h < 4 ? y : h == 12 || h == 14 ? x : z; + return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v); + + } + + return { + + noise: function (x, y, z) { + + var floorX = ~~x, floorY = ~~y, floorZ = ~~z; + + var X = floorX & 255, Y = floorY & 255, Z = floorZ & 255; + + x -= floorX; + y -= floorY; + z -= floorZ; + + var xMinus1 = x - 1, yMinus1 = y - 1, zMinus1 = z - 1; + + var u = fade(x), v = fade(y), w = fade(z); + + var A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z, B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z; + + return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z), + grad(p[BA], xMinus1, y, z)), + lerp(u, grad(p[AB], x, yMinus1, z), + grad(p[BB], xMinus1, yMinus1, z))), + lerp(v, lerp(u, grad(p[AA + 1], x, y, zMinus1), + grad(p[BA + 1], xMinus1, y, z - 1)), + lerp(u, grad(p[AB + 1], x, yMinus1, zMinus1), + grad(p[BB + 1], xMinus1, yMinus1, zMinus1)))); + + } + } +}; diff --git a/node_modules/three/examples/js/MD2Character.js b/node_modules/three/examples/js/MD2Character.js new file mode 100644 index 00000000..c194bcc8 --- /dev/null +++ b/node_modules/three/examples/js/MD2Character.js @@ -0,0 +1,256 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.MD2Character = function () { + + var scope = this; + + this.scale = 1; + this.animationFPS = 6; + + this.root = new THREE.Object3D(); + + this.meshBody = null; + this.meshWeapon = null; + + this.skinsBody = []; + this.skinsWeapon = []; + + this.weapons = []; + + this.activeAnimation = null; + + this.mixer = null; + + this.onLoadComplete = function () {}; + + this.loadCounter = 0; + + this.loadParts = function ( config ) { + + this.loadCounter = config.weapons.length * 2 + config.skins.length + 1; + + var weaponsTextures = []; + for ( var i = 0; i < config.weapons.length; i ++ ) weaponsTextures[ i ] = config.weapons[ i ][ 1 ]; + + // SKINS + + this.skinsBody = loadTextures( config.baseUrl + "skins/", config.skins ); + this.skinsWeapon = loadTextures( config.baseUrl + "skins/", weaponsTextures ); + + // BODY + + var loader = new THREE.MD2Loader(); + + loader.load( config.baseUrl + config.body, function( geo ) { + + geo.computeBoundingBox(); + scope.root.position.y = - scope.scale * geo.boundingBox.min.y; + + var mesh = createPart( geo, scope.skinsBody[ 0 ] ); + mesh.scale.set( scope.scale, scope.scale, scope.scale ); + + scope.root.add( mesh ); + + scope.meshBody = mesh; + + scope.meshBody.clipOffset = 0; + scope.activeAnimationClipName = mesh.geometry.animations[0].name; + + scope.mixer = new THREE.AnimationMixer( mesh ); + + checkLoadingComplete(); + + } ); + + // WEAPONS + + var generateCallback = function ( index, name ) { + + return function( geo ) { + + var mesh = createPart( geo, scope.skinsWeapon[ index ] ); + mesh.scale.set( scope.scale, scope.scale, scope.scale ); + mesh.visible = false; + + mesh.name = name; + + scope.root.add( mesh ); + + scope.weapons[ index ] = mesh; + scope.meshWeapon = mesh; + + checkLoadingComplete(); + + } + + }; + + for ( var i = 0; i < config.weapons.length; i ++ ) { + + loader.load( config.baseUrl + config.weapons[ i ][ 0 ], generateCallback( i, config.weapons[ i ][ 0 ] ) ); + + } + + }; + + this.setPlaybackRate = function ( rate ) { + + if( rate !== 0 ) { + this.mixer.timeScale = 1 / rate; + } + else { + this.mixer.timeScale = 0; + } + + }; + + this.setWireframe = function ( wireframeEnabled ) { + + if ( wireframeEnabled ) { + + if ( this.meshBody ) this.meshBody.material = this.meshBody.materialWireframe; + if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialWireframe; + + } else { + + if ( this.meshBody ) this.meshBody.material = this.meshBody.materialTexture; + if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialTexture; + + } + + }; + + this.setSkin = function( index ) { + + if ( this.meshBody && this.meshBody.material.wireframe === false ) { + + this.meshBody.material.map = this.skinsBody[ index ]; + + } + + }; + + this.setWeapon = function ( index ) { + + for ( var i = 0; i < this.weapons.length; i ++ ) this.weapons[ i ].visible = false; + + var activeWeapon = this.weapons[ index ]; + + if ( activeWeapon ) { + + activeWeapon.visible = true; + this.meshWeapon = activeWeapon; + + scope.syncWeaponAnimation(); + + } + + }; + + this.setAnimation = function ( clipName ) { + + if ( this.meshBody ) { + + if( this.meshBody.activeAction ) { + scope.mixer.removeAction( this.meshBody.activeAction ); + this.meshBody.activeAction = null; + } + + var clip = THREE.AnimationClip.findByName( this.meshBody.geometry.animations, clipName ); + if( clip ) { + + var action = new THREE.AnimationAction( clip, this.mixer.time ).setLocalRoot( this.meshBody ); + scope.mixer.addAction( action ); + + this.meshBody.activeAction = action; + + } + + } + + scope.activeClipName = clipName; + + scope.syncWeaponAnimation(); + + }; + + this.syncWeaponAnimation = function() { + + var clipName = scope.activeClipName; + + if ( scope.meshWeapon ) { + + if( this.meshWeapon.activeAction ) { + scope.mixer.removeAction( this.meshWeapon.activeAction ); + this.meshWeapon.activeAction = null; + } + + var clip = THREE.AnimationClip.findByName( this.meshWeapon.geometry.animations, clipName ); + if( clip ) { + + var action = new THREE.AnimationAction( clip ).syncWith( this.meshBody.activeAction ).setLocalRoot( this.meshWeapon ); + scope.mixer.addAction( action ); + + this.meshWeapon.activeAction = action; + + } + + } + + } + + this.update = function ( delta ) { + + if( this.mixer ) this.mixer.update( delta ); + + }; + + function loadTextures( baseUrl, textureUrls ) { + + var mapping = THREE.UVMapping; + var textures = []; + + for ( var i = 0; i < textureUrls.length; i ++ ) { + + textures[ i ] = THREE.ImageUtils.loadTexture( baseUrl + textureUrls[ i ], mapping, checkLoadingComplete ); + textures[ i ].name = textureUrls[ i ]; + + } + + return textures; + + } + + function createPart( geometry, skinMap ) { + + var materialWireframe = new THREE.MeshLambertMaterial( { color: 0xffaa00, wireframe: true, morphTargets: true, morphNormals: true } ); + var materialTexture = new THREE.MeshLambertMaterial( { color: 0xffffff, wireframe: false, map: skinMap, morphTargets: true, morphNormals: true } ); + + // + + var mesh = new THREE.Mesh( geometry, materialTexture ); + mesh.rotation.y = - Math.PI / 2; + + mesh.castShadow = true; + mesh.receiveShadow = true; + + // + + mesh.materialTexture = materialTexture; + mesh.materialWireframe = materialWireframe; + + return mesh; + + } + + function checkLoadingComplete() { + + scope.loadCounter -= 1; + + if ( scope.loadCounter === 0 ) scope.onLoadComplete(); + + } + +}; diff --git a/node_modules/three/examples/js/MD2CharacterComplex.js b/node_modules/three/examples/js/MD2CharacterComplex.js new file mode 100644 index 00000000..6c74007c --- /dev/null +++ b/node_modules/three/examples/js/MD2CharacterComplex.js @@ -0,0 +1,559 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.MD2CharacterComplex = function () { + + var scope = this; + + this.scale = 1; + + // animation parameters + + this.animationFPS = 6; + this.transitionFrames = 15; + + // movement model parameters + + this.maxSpeed = 275; + this.maxReverseSpeed = - 275; + + this.frontAcceleration = 600; + this.backAcceleration = 600; + + this.frontDecceleration = 600; + + this.angularSpeed = 2.5; + + // rig + + this.root = new THREE.Object3D(); + + this.meshBody = null; + this.meshWeapon = null; + + this.controls = null; + + // skins + + this.skinsBody = []; + this.skinsWeapon = []; + + this.weapons = []; + + this.currentSkin = undefined; + + // + + this.onLoadComplete = function () {}; + + // internals + + this.meshes = []; + this.animations = {}; + + this.loadCounter = 0; + + // internal movement control variables + + this.speed = 0; + this.bodyOrientation = 0; + + this.walkSpeed = this.maxSpeed; + this.crouchSpeed = this.maxSpeed * 0.5; + + // internal animation parameters + + this.activeAnimation = null; + this.oldAnimation = null; + + // API + + this.enableShadows = function ( enable ) { + + for ( var i = 0; i < this.meshes.length; i ++ ) { + + this.meshes[ i ].castShadow = enable; + this.meshes[ i ].receiveShadow = enable; + + } + + }; + + this.setVisible = function ( enable ) { + + for ( var i = 0; i < this.meshes.length; i ++ ) { + + this.meshes[ i ].visible = enable; + this.meshes[ i ].visible = enable; + + } + + }; + + + this.shareParts = function ( original ) { + + this.animations = original.animations; + this.walkSpeed = original.walkSpeed; + this.crouchSpeed = original.crouchSpeed; + + this.skinsBody = original.skinsBody; + this.skinsWeapon = original.skinsWeapon; + + // BODY + + var mesh = createPart( original.meshBody.geometry, this.skinsBody[ 0 ] ); + mesh.scale.set( this.scale, this.scale, this.scale ); + + this.root.position.y = original.root.position.y; + this.root.add( mesh ); + + this.meshBody = mesh; + + this.meshes.push( mesh ); + + // WEAPONS + + for ( var i = 0; i < original.weapons.length; i ++ ) { + + var meshWeapon = createPart( original.weapons[ i ].geometry, this.skinsWeapon[ i ] ); + meshWeapon.scale.set( this.scale, this.scale, this.scale ); + meshWeapon.visible = false; + + meshWeapon.name = original.weapons[ i ].name; + + this.root.add( meshWeapon ); + + this.weapons[ i ] = meshWeapon; + this.meshWeapon = meshWeapon; + + this.meshes.push( meshWeapon ); + + } + + }; + + this.loadParts = function ( config ) { + + this.animations = config.animations; + this.walkSpeed = config.walkSpeed; + this.crouchSpeed = config.crouchSpeed; + + this.loadCounter = config.weapons.length * 2 + config.skins.length + 1; + + var weaponsTextures = []; + for ( var i = 0; i < config.weapons.length; i ++ ) weaponsTextures[ i ] = config.weapons[ i ][ 1 ]; + + // SKINS + + this.skinsBody = loadTextures( config.baseUrl + "skins/", config.skins ); + this.skinsWeapon = loadTextures( config.baseUrl + "skins/", weaponsTextures ); + + // BODY + + var loader = new THREE.MD2Loader(); + + loader.load( config.baseUrl + config.body, function( geo ) { + + geo.computeBoundingBox(); + scope.root.position.y = - scope.scale * geo.boundingBox.min.y; + + var mesh = createPart( geo, scope.skinsBody[ 0 ] ); + mesh.scale.set( scope.scale, scope.scale, scope.scale ); + + scope.root.add( mesh ); + + scope.meshBody = mesh; + scope.meshes.push( mesh ); + + checkLoadingComplete(); + + } ); + + // WEAPONS + + var generateCallback = function ( index, name ) { + + return function( geo ) { + + var mesh = createPart( geo, scope.skinsWeapon[ index ] ); + mesh.scale.set( scope.scale, scope.scale, scope.scale ); + mesh.visible = false; + + mesh.name = name; + + scope.root.add( mesh ); + + scope.weapons[ index ] = mesh; + scope.meshWeapon = mesh; + scope.meshes.push( mesh ); + + checkLoadingComplete(); + + } + + }; + + for ( var i = 0; i < config.weapons.length; i ++ ) { + + loader.load( config.baseUrl + config.weapons[ i ][ 0 ], generateCallback( i, config.weapons[ i ][ 0 ] ) ); + + } + + }; + + this.setPlaybackRate = function ( rate ) { + + if ( this.meshBody ) this.meshBody.duration = this.meshBody.baseDuration / rate; + if ( this.meshWeapon ) this.meshWeapon.duration = this.meshWeapon.baseDuration / rate; + + }; + + this.setWireframe = function ( wireframeEnabled ) { + + if ( wireframeEnabled ) { + + if ( this.meshBody ) this.meshBody.material = this.meshBody.materialWireframe; + if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialWireframe; + + } else { + + if ( this.meshBody ) this.meshBody.material = this.meshBody.materialTexture; + if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialTexture; + + } + + }; + + this.setSkin = function( index ) { + + if ( this.meshBody && this.meshBody.material.wireframe === false ) { + + this.meshBody.material.map = this.skinsBody[ index ]; + this.currentSkin = index; + + } + + }; + + this.setWeapon = function ( index ) { + + for ( var i = 0; i < this.weapons.length; i ++ ) this.weapons[ i ].visible = false; + + var activeWeapon = this.weapons[ index ]; + + if ( activeWeapon ) { + + activeWeapon.visible = true; + this.meshWeapon = activeWeapon; + + if ( this.activeAnimation ) { + + activeWeapon.playAnimation( this.activeAnimation ); + this.meshWeapon.setAnimationTime( this.activeAnimation, this.meshBody.getAnimationTime( this.activeAnimation ) ); + + } + + } + + }; + + this.setAnimation = function ( animationName ) { + + if ( animationName === this.activeAnimation || ! animationName ) return; + + if ( this.meshBody ) { + + this.meshBody.setAnimationWeight( animationName, 0 ); + this.meshBody.playAnimation( animationName ); + + this.oldAnimation = this.activeAnimation; + this.activeAnimation = animationName; + + this.blendCounter = this.transitionFrames; + + } + + if ( this.meshWeapon ) { + + this.meshWeapon.setAnimationWeight( animationName, 0 ); + this.meshWeapon.playAnimation( animationName ); + + } + + + }; + + this.update = function ( delta ) { + + if ( this.controls ) this.updateMovementModel( delta ); + + if ( this.animations ) { + + this.updateBehaviors( delta ); + this.updateAnimations( delta ); + + } + + }; + + this.updateAnimations = function ( delta ) { + + var mix = 1; + + if ( this.blendCounter > 0 ) { + + mix = ( this.transitionFrames - this.blendCounter ) / this.transitionFrames; + this.blendCounter -= 1; + + } + + if ( this.meshBody ) { + + this.meshBody.update( delta ); + + this.meshBody.setAnimationWeight( this.activeAnimation, mix ); + this.meshBody.setAnimationWeight( this.oldAnimation, 1 - mix ); + + } + + if ( this.meshWeapon ) { + + this.meshWeapon.update( delta ); + + this.meshWeapon.setAnimationWeight( this.activeAnimation, mix ); + this.meshWeapon.setAnimationWeight( this.oldAnimation, 1 - mix ); + + } + + }; + + this.updateBehaviors = function ( delta ) { + + var controls = this.controls; + var animations = this.animations; + + var moveAnimation, idleAnimation; + + // crouch vs stand + + if ( controls.crouch ) { + + moveAnimation = animations[ "crouchMove" ]; + idleAnimation = animations[ "crouchIdle" ]; + + } else { + + moveAnimation = animations[ "move" ]; + idleAnimation = animations[ "idle" ]; + + } + + // actions + + if ( controls.jump ) { + + moveAnimation = animations[ "jump" ]; + idleAnimation = animations[ "jump" ]; + + } + + if ( controls.attack ) { + + if ( controls.crouch ) { + + moveAnimation = animations[ "crouchAttack" ]; + idleAnimation = animations[ "crouchAttack" ]; + + } else { + + moveAnimation = animations[ "attack" ]; + idleAnimation = animations[ "attack" ]; + + } + + } + + // set animations + + if ( controls.moveForward || controls.moveBackward || controls.moveLeft || controls.moveRight ) { + + if ( this.activeAnimation !== moveAnimation ) { + + this.setAnimation( moveAnimation ); + + } + + } + + + if ( Math.abs( this.speed ) < 0.2 * this.maxSpeed && ! ( controls.moveLeft || controls.moveRight || controls.moveForward || controls.moveBackward ) ) { + + if ( this.activeAnimation !== idleAnimation ) { + + this.setAnimation( idleAnimation ); + + } + + } + + // set animation direction + + if ( controls.moveForward ) { + + if ( this.meshBody ) { + + this.meshBody.setAnimationDirectionForward( this.activeAnimation ); + this.meshBody.setAnimationDirectionForward( this.oldAnimation ); + + } + + if ( this.meshWeapon ) { + + this.meshWeapon.setAnimationDirectionForward( this.activeAnimation ); + this.meshWeapon.setAnimationDirectionForward( this.oldAnimation ); + + } + + } + + if ( controls.moveBackward ) { + + if ( this.meshBody ) { + + this.meshBody.setAnimationDirectionBackward( this.activeAnimation ); + this.meshBody.setAnimationDirectionBackward( this.oldAnimation ); + + } + + if ( this.meshWeapon ) { + + this.meshWeapon.setAnimationDirectionBackward( this.activeAnimation ); + this.meshWeapon.setAnimationDirectionBackward( this.oldAnimation ); + + } + + } + + }; + + this.updateMovementModel = function ( delta ) { + + var controls = this.controls; + + // speed based on controls + + if ( controls.crouch ) this.maxSpeed = this.crouchSpeed; + else this.maxSpeed = this.walkSpeed; + + this.maxReverseSpeed = - this.maxSpeed; + + if ( controls.moveForward ) this.speed = THREE.Math.clamp( this.speed + delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed ); + if ( controls.moveBackward ) this.speed = THREE.Math.clamp( this.speed - delta * this.backAcceleration, this.maxReverseSpeed, this.maxSpeed ); + + // orientation based on controls + // (don't just stand while turning) + + var dir = 1; + + if ( controls.moveLeft ) { + + this.bodyOrientation += delta * this.angularSpeed; + this.speed = THREE.Math.clamp( this.speed + dir * delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed ); + + } + + if ( controls.moveRight ) { + + this.bodyOrientation -= delta * this.angularSpeed; + this.speed = THREE.Math.clamp( this.speed + dir * delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed ); + + } + + // speed decay + + if ( ! ( controls.moveForward || controls.moveBackward ) ) { + + if ( this.speed > 0 ) { + + var k = exponentialEaseOut( this.speed / this.maxSpeed ); + this.speed = THREE.Math.clamp( this.speed - k * delta * this.frontDecceleration, 0, this.maxSpeed ); + + } else { + + var k = exponentialEaseOut( this.speed / this.maxReverseSpeed ); + this.speed = THREE.Math.clamp( this.speed + k * delta * this.backAcceleration, this.maxReverseSpeed, 0 ); + + } + + } + + // displacement + + var forwardDelta = this.speed * delta; + + this.root.position.x += Math.sin( this.bodyOrientation ) * forwardDelta; + this.root.position.z += Math.cos( this.bodyOrientation ) * forwardDelta; + + // steering + + this.root.rotation.y = this.bodyOrientation; + + }; + + // internal helpers + + function loadTextures( baseUrl, textureUrls ) { + + var mapping = THREE.UVMapping; + var textures = []; + + for ( var i = 0; i < textureUrls.length; i ++ ) { + + textures[ i ] = THREE.ImageUtils.loadTexture( baseUrl + textureUrls[ i ], mapping, checkLoadingComplete ); + textures[ i ].name = textureUrls[ i ]; + + } + + return textures; + + } + + function createPart( geometry, skinMap ) { + + var materialWireframe = new THREE.MeshLambertMaterial( { color: 0xffaa00, wireframe: true, morphTargets: true, morphNormals: true } ); + var materialTexture = new THREE.MeshLambertMaterial( { color: 0xffffff, wireframe: false, map: skinMap, morphTargets: true, morphNormals: true } ); + + // + + var mesh = new THREE.MorphBlendMesh( geometry, materialTexture ); + mesh.rotation.y = - Math.PI / 2; + + // + + mesh.materialTexture = materialTexture; + mesh.materialWireframe = materialWireframe; + + // + + mesh.autoCreateAnimations( scope.animationFPS ); + + return mesh; + + } + + function checkLoadingComplete() { + + scope.loadCounter -= 1; + if ( scope.loadCounter === 0 ) scope.onLoadComplete(); + + } + + function exponentialEaseOut( k ) { + + return k === 1 ? 1 : - Math.pow( 2, - 10 * k ) + 1; + + } + +}; diff --git a/node_modules/three/examples/js/MarchingCubes.js b/node_modules/three/examples/js/MarchingCubes.js new file mode 100644 index 00000000..9ca644de --- /dev/null +++ b/node_modules/three/examples/js/MarchingCubes.js @@ -0,0 +1,1057 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Port of greggman's ThreeD version of marching cubes to Three.js + * http://webglsamples.googlecode.com/hg/blob/blob.html + */ + +THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors ) { + + THREE.ImmediateRenderObject.call( this, material ); + + this.enableUvs = enableUvs !== undefined ? enableUvs : false; + this.enableColors = enableColors !== undefined ? enableColors : false; + + // functions have to be object properties + // prototype functions kill performance + // (tested and it was 4x slower !!!) + + this.init = function ( resolution ) { + + this.resolution = resolution; + + // parameters + + this.isolation = 80.0; + + // size of field, 32 is pushing it in Javascript :) + + this.size = resolution; + this.size2 = this.size * this.size; + this.size3 = this.size2 * this.size; + this.halfsize = this.size / 2.0; + + // deltas + + this.delta = 2.0 / this.size; + this.yd = this.size; + this.zd = this.size2; + + this.field = new Float32Array( this.size3 ); + this.normal_cache = new Float32Array( this.size3 * 3 ); + + // temp buffers used in polygonize + + this.vlist = new Float32Array( 12 * 3 ); + this.nlist = new Float32Array( 12 * 3 ); + + // immediate render mode simulator + + this.maxCount = 4096; // TODO: find the fastest size for this buffer + this.count = 0; + + this.hasPositions = false; + this.hasNormals = false; + this.hasColors = false; + this.hasUvs = false; + + this.positionArray = new Float32Array( this.maxCount * 3 ); + this.normalArray = new Float32Array( this.maxCount * 3 ); + + if ( this.enableUvs ) { + + this.uvArray = new Float32Array( this.maxCount * 2 ); + + } + + if ( this.enableColors ) { + + this.colorArray = new Float32Array( this.maxCount * 3 ); + + } + + }; + + /////////////////////// + // Polygonization + /////////////////////// + + this.lerp = function( a, b, t ) { + + return a + ( b - a ) * t; + + }; + + this.VIntX = function( q, pout, nout, offset, isol, x, y, z, valp1, valp2 ) { + + var mu = ( isol - valp1 ) / ( valp2 - valp1 ), + nc = this.normal_cache; + + pout[ offset ] = x + mu * this.delta; + pout[ offset + 1 ] = y; + pout[ offset + 2 ] = z; + + nout[ offset ] = this.lerp( nc[ q ], nc[ q + 3 ], mu ); + nout[ offset + 1 ] = this.lerp( nc[ q + 1 ], nc[ q + 4 ], mu ); + nout[ offset + 2 ] = this.lerp( nc[ q + 2 ], nc[ q + 5 ], mu ); + + }; + + this.VIntY = function( q, pout, nout, offset, isol, x, y, z, valp1, valp2 ) { + + var mu = ( isol - valp1 ) / ( valp2 - valp1 ), + nc = this.normal_cache; + + pout[ offset ] = x; + pout[ offset + 1 ] = y + mu * this.delta; + pout[ offset + 2 ] = z; + + var q2 = q + this.yd * 3; + + nout[ offset ] = this.lerp( nc[ q ], nc[ q2 ], mu ); + nout[ offset + 1 ] = this.lerp( nc[ q + 1 ], nc[ q2 + 1 ], mu ); + nout[ offset + 2 ] = this.lerp( nc[ q + 2 ], nc[ q2 + 2 ], mu ); + + }; + + this.VIntZ = function( q, pout, nout, offset, isol, x, y, z, valp1, valp2 ) { + + var mu = ( isol - valp1 ) / ( valp2 - valp1 ), + nc = this.normal_cache; + + pout[ offset ] = x; + pout[ offset + 1 ] = y; + pout[ offset + 2 ] = z + mu * this.delta; + + var q2 = q + this.zd * 3; + + nout[ offset ] = this.lerp( nc[ q ], nc[ q2 ], mu ); + nout[ offset + 1 ] = this.lerp( nc[ q + 1 ], nc[ q2 + 1 ], mu ); + nout[ offset + 2 ] = this.lerp( nc[ q + 2 ], nc[ q2 + 2 ], mu ); + + }; + + this.compNorm = function( q ) { + + var q3 = q * 3; + + if ( this.normal_cache[ q3 ] === 0.0 ) { + + this.normal_cache[ q3 ] = this.field[ q - 1 ] - this.field[ q + 1 ]; + this.normal_cache[ q3 + 1 ] = this.field[ q - this.yd ] - this.field[ q + this.yd ]; + this.normal_cache[ q3 + 2 ] = this.field[ q - this.zd ] - this.field[ q + this.zd ]; + + } + + }; + + // Returns total number of triangles. Fills triangles. + // (this is where most of time is spent - it's inner work of O(n3) loop ) + + this.polygonize = function( fx, fy, fz, q, isol, renderCallback ) { + + // cache indices + var q1 = q + 1, + qy = q + this.yd, + qz = q + this.zd, + q1y = q1 + this.yd, + q1z = q1 + this.zd, + qyz = q + this.yd + this.zd, + q1yz = q1 + this.yd + this.zd; + + var cubeindex = 0, + field0 = this.field[ q ], + field1 = this.field[ q1 ], + field2 = this.field[ qy ], + field3 = this.field[ q1y ], + field4 = this.field[ qz ], + field5 = this.field[ q1z ], + field6 = this.field[ qyz ], + field7 = this.field[ q1yz ]; + + if ( field0 < isol ) cubeindex |= 1; + if ( field1 < isol ) cubeindex |= 2; + if ( field2 < isol ) cubeindex |= 8; + if ( field3 < isol ) cubeindex |= 4; + if ( field4 < isol ) cubeindex |= 16; + if ( field5 < isol ) cubeindex |= 32; + if ( field6 < isol ) cubeindex |= 128; + if ( field7 < isol ) cubeindex |= 64; + + // if cube is entirely in/out of the surface - bail, nothing to draw + + var bits = THREE.edgeTable[ cubeindex ]; + if ( bits === 0 ) return 0; + + var d = this.delta, + fx2 = fx + d, + fy2 = fy + d, + fz2 = fz + d; + + // top of the cube + + if ( bits & 1 ) { + + this.compNorm( q ); + this.compNorm( q1 ); + this.VIntX( q * 3, this.vlist, this.nlist, 0, isol, fx, fy, fz, field0, field1 ); + + } + + if ( bits & 2 ) { + + this.compNorm( q1 ); + this.compNorm( q1y ); + this.VIntY( q1 * 3, this.vlist, this.nlist, 3, isol, fx2, fy, fz, field1, field3 ); + + } + + if ( bits & 4 ) { + + this.compNorm( qy ); + this.compNorm( q1y ); + this.VIntX( qy * 3, this.vlist, this.nlist, 6, isol, fx, fy2, fz, field2, field3 ); + + } + + if ( bits & 8 ) { + + this.compNorm( q ); + this.compNorm( qy ); + this.VIntY( q * 3, this.vlist, this.nlist, 9, isol, fx, fy, fz, field0, field2 ); + + } + + // bottom of the cube + + if ( bits & 16 ) { + + this.compNorm( qz ); + this.compNorm( q1z ); + this.VIntX( qz * 3, this.vlist, this.nlist, 12, isol, fx, fy, fz2, field4, field5 ); + + } + + if ( bits & 32 ) { + + this.compNorm( q1z ); + this.compNorm( q1yz ); + this.VIntY( q1z * 3, this.vlist, this.nlist, 15, isol, fx2, fy, fz2, field5, field7 ); + + } + + if ( bits & 64 ) { + + this.compNorm( qyz ); + this.compNorm( q1yz ); + this.VIntX( qyz * 3, this.vlist, this.nlist, 18, isol, fx, fy2, fz2, field6, field7 ); + + } + + if ( bits & 128 ) { + + this.compNorm( qz ); + this.compNorm( qyz ); + this.VIntY( qz * 3, this.vlist, this.nlist, 21, isol, fx, fy, fz2, field4, field6 ); + + } + + // vertical lines of the cube + + if ( bits & 256 ) { + + this.compNorm( q ); + this.compNorm( qz ); + this.VIntZ( q * 3, this.vlist, this.nlist, 24, isol, fx, fy, fz, field0, field4 ); + + } + + if ( bits & 512 ) { + + this.compNorm( q1 ); + this.compNorm( q1z ); + this.VIntZ( q1 * 3, this.vlist, this.nlist, 27, isol, fx2, fy, fz, field1, field5 ); + + } + + if ( bits & 1024 ) { + + this.compNorm( q1y ); + this.compNorm( q1yz ); + this.VIntZ( q1y * 3, this.vlist, this.nlist, 30, isol, fx2, fy2, fz, field3, field7 ); + + } + + if ( bits & 2048 ) { + + this.compNorm( qy ); + this.compNorm( qyz ); + this.VIntZ( qy * 3, this.vlist, this.nlist, 33, isol, fx, fy2, fz, field2, field6 ); + + } + + cubeindex <<= 4; // re-purpose cubeindex into an offset into triTable + + var o1, o2, o3, numtris = 0, i = 0; + + // here is where triangles are created + + while ( THREE.triTable[ cubeindex + i ] != - 1 ) { + + o1 = cubeindex + i; + o2 = o1 + 1; + o3 = o1 + 2; + + this.posnormtriv( this.vlist, this.nlist, + 3 * THREE.triTable[ o1 ], + 3 * THREE.triTable[ o2 ], + 3 * THREE.triTable[ o3 ], + renderCallback ); + + i += 3; + numtris ++; + + } + + return numtris; + + }; + + ///////////////////////////////////// + // Immediate render mode simulator + ///////////////////////////////////// + + this.posnormtriv = function( pos, norm, o1, o2, o3, renderCallback ) { + + var c = this.count * 3; + + // positions + + this.positionArray[ c ] = pos[ o1 ]; + this.positionArray[ c + 1 ] = pos[ o1 + 1 ]; + this.positionArray[ c + 2 ] = pos[ o1 + 2 ]; + + this.positionArray[ c + 3 ] = pos[ o2 ]; + this.positionArray[ c + 4 ] = pos[ o2 + 1 ]; + this.positionArray[ c + 5 ] = pos[ o2 + 2 ]; + + this.positionArray[ c + 6 ] = pos[ o3 ]; + this.positionArray[ c + 7 ] = pos[ o3 + 1 ]; + this.positionArray[ c + 8 ] = pos[ o3 + 2 ]; + + // normals + + this.normalArray[ c ] = norm[ o1 ]; + this.normalArray[ c + 1 ] = norm[ o1 + 1 ]; + this.normalArray[ c + 2 ] = norm[ o1 + 2 ]; + + this.normalArray[ c + 3 ] = norm[ o2 ]; + this.normalArray[ c + 4 ] = norm[ o2 + 1 ]; + this.normalArray[ c + 5 ] = norm[ o2 + 2 ]; + + this.normalArray[ c + 6 ] = norm[ o3 ]; + this.normalArray[ c + 7 ] = norm[ o3 + 1 ]; + this.normalArray[ c + 8 ] = norm[ o3 + 2 ]; + + // uvs + + if ( this.enableUvs ) { + + var d = this.count * 2; + + this.uvArray[ d ] = pos[ o1 ]; + this.uvArray[ d + 1 ] = pos[ o1 + 2 ]; + + this.uvArray[ d + 2 ] = pos[ o2 ]; + this.uvArray[ d + 3 ] = pos[ o2 + 2 ]; + + this.uvArray[ d + 4 ] = pos[ o3 ]; + this.uvArray[ d + 5 ] = pos[ o3 + 2 ]; + + } + + // colors + + if ( this.enableColors ) { + + this.colorArray[ c ] = pos[ o1 ]; + this.colorArray[ c + 1 ] = pos[ o1 + 1 ]; + this.colorArray[ c + 2 ] = pos[ o1 + 2 ]; + + this.colorArray[ c + 3 ] = pos[ o2 ]; + this.colorArray[ c + 4 ] = pos[ o2 + 1 ]; + this.colorArray[ c + 5 ] = pos[ o2 + 2 ]; + + this.colorArray[ c + 6 ] = pos[ o3 ]; + this.colorArray[ c + 7 ] = pos[ o3 + 1 ]; + this.colorArray[ c + 8 ] = pos[ o3 + 2 ]; + + } + + this.count += 3; + + if ( this.count >= this.maxCount - 3 ) { + + this.hasPositions = true; + this.hasNormals = true; + + if ( this.enableUvs ) { + + this.hasUvs = true; + + } + + if ( this.enableColors ) { + + this.hasColors = true; + + } + + renderCallback( this ); + + } + + }; + + this.begin = function( ) { + + this.count = 0; + + this.hasPositions = false; + this.hasNormals = false; + this.hasUvs = false; + this.hasColors = false; + + }; + + this.end = function( renderCallback ) { + + if ( this.count === 0 ) return; + + for ( var i = this.count * 3; i < this.positionArray.length; i ++ ) { + + this.positionArray[ i ] = 0.0; + + } + + this.hasPositions = true; + this.hasNormals = true; + + if ( this.enableUvs ) { + + this.hasUvs = true; + + } + + if ( this.enableColors ) { + + this.hasColors = true; + + } + + renderCallback( this ); + + }; + + ///////////////////////////////////// + // Metaballs + ///////////////////////////////////// + + // Adds a reciprocal ball (nice and blobby) that, to be fast, fades to zero after + // a fixed distance, determined by strength and subtract. + + this.addBall = function( ballx, bally, ballz, strength, subtract ) { + + // Let's solve the equation to find the radius: + // 1.0 / (0.000001 + radius^2) * strength - subtract = 0 + // strength / (radius^2) = subtract + // strength = subtract * radius^2 + // radius^2 = strength / subtract + // radius = sqrt(strength / subtract) + + var radius = this.size * Math.sqrt( strength / subtract ), + zs = ballz * this.size, + ys = bally * this.size, + xs = ballx * this.size; + + var min_z = Math.floor( zs - radius ); if ( min_z < 1 ) min_z = 1; + var max_z = Math.floor( zs + radius ); if ( max_z > this.size - 1 ) max_z = this.size - 1; + var min_y = Math.floor( ys - radius ); if ( min_y < 1 ) min_y = 1; + var max_y = Math.floor( ys + radius ); if ( max_y > this.size - 1 ) max_y = this.size - 1; + var min_x = Math.floor( xs - radius ); if ( min_x < 1 ) min_x = 1; + var max_x = Math.floor( xs + radius ); if ( max_x > this.size - 1 ) max_x = this.size - 1; + + + // Don't polygonize in the outer layer because normals aren't + // well-defined there. + + var x, y, z, y_offset, z_offset, fx, fy, fz, fz2, fy2, val; + + for ( z = min_z; z < max_z; z ++ ) { + + z_offset = this.size2 * z, + fz = z / this.size - ballz, + fz2 = fz * fz; + + for ( y = min_y; y < max_y; y ++ ) { + + y_offset = z_offset + this.size * y; + fy = y / this.size - bally; + fy2 = fy * fy; + + for ( x = min_x; x < max_x; x ++ ) { + + fx = x / this.size - ballx; + val = strength / ( 0.000001 + fx * fx + fy2 + fz2 ) - subtract; + if ( val > 0.0 ) this.field[ y_offset + x ] += val; + + } + + } + + } + + }; + + this.addPlaneX = function( strength, subtract ) { + + var x, y, z, xx, val, xdiv, cxy, + + // cache attribute lookups + size = this.size, + yd = this.yd, + zd = this.zd, + field = this.field, + + dist = size * Math.sqrt( strength / subtract ); + + if ( dist > size ) dist = size; + + for ( x = 0; x < dist; x ++ ) { + + xdiv = x / size; + xx = xdiv * xdiv; + val = strength / ( 0.0001 + xx ) - subtract; + + if ( val > 0.0 ) { + + for ( y = 0; y < size; y ++ ) { + + cxy = x + y * yd; + + for ( z = 0; z < size; z ++ ) { + + field[ zd * z + cxy ] += val; + + } + + } + + } + + } + + }; + + this.addPlaneY = function( strength, subtract ) { + + var x, y, z, yy, val, ydiv, cy, cxy, + + // cache attribute lookups + size = this.size, + yd = this.yd, + zd = this.zd, + field = this.field, + + dist = size * Math.sqrt( strength / subtract ); + + if ( dist > size ) dist = size; + + for ( y = 0; y < dist; y ++ ) { + + ydiv = y / size; + yy = ydiv * ydiv; + val = strength / ( 0.0001 + yy ) - subtract; + + if ( val > 0.0 ) { + + cy = y * yd; + + for ( x = 0; x < size; x ++ ) { + + cxy = cy + x; + + for ( z = 0; z < size; z ++ ) + field[ zd * z + cxy ] += val; + + } + + } + + } + + }; + + this.addPlaneZ = function( strength, subtract ) { + + var x, y, z, zz, val, zdiv, cz, cyz, + + // cache attribute lookups + size = this.size, + yd = this.yd, + zd = this.zd, + field = this.field, + + dist = size * Math.sqrt( strength / subtract ); + + if ( dist > size ) dist = size; + + for ( z = 0; z < dist; z ++ ) { + + zdiv = z / size; + zz = zdiv * zdiv; + val = strength / ( 0.0001 + zz ) - subtract; + if ( val > 0.0 ) { + + cz = zd * z; + + for ( y = 0; y < size; y ++ ) { + + cyz = cz + y * yd; + + for ( x = 0; x < size; x ++ ) + field[ cyz + x ] += val; + + } + + } + + } + + }; + + ///////////////////////////////////// + // Updates + ///////////////////////////////////// + + this.reset = function () { + + var i; + + // wipe the normal cache + + for ( i = 0; i < this.size3; i ++ ) { + + this.normal_cache[ i * 3 ] = 0.0; + this.field[ i ] = 0.0; + + } + + }; + + this.render = function ( renderCallback ) { + + this.begin(); + + // Triangulate. Yeah, this is slow. + + var smin2 = this.size - 2; + + for ( var z = 1; z < smin2; z ++ ) { + + var z_offset = this.size2 * z; + var fz = ( z - this.halfsize ) / this.halfsize; //+ 1 + + for ( var y = 1; y < smin2; y ++ ) { + + var y_offset = z_offset + this.size * y; + var fy = ( y - this.halfsize ) / this.halfsize; //+ 1 + + for ( var x = 1; x < smin2; x ++ ) { + + var fx = ( x - this.halfsize ) / this.halfsize; //+ 1 + var q = y_offset + x; + + this.polygonize( fx, fy, fz, q, this.isolation, renderCallback ); + + } + + } + + } + + this.end( renderCallback ); + + }; + + this.generateGeometry = function() { + + var start = 0, geo = new THREE.Geometry(); + var normals = []; + + var geo_callback = function( object ) { + + var i, x, y, z, vertex, normal, + face, a, b, c, na, nb, nc, nfaces; + + + for ( i = 0; i < object.count; i ++ ) { + + a = i * 3; + b = a + 1; + c = a + 2; + + x = object.positionArray[ a ]; + y = object.positionArray[ b ]; + z = object.positionArray[ c ]; + vertex = new THREE.Vector3( x, y, z ); + + x = object.normalArray[ a ]; + y = object.normalArray[ b ]; + z = object.normalArray[ c ]; + normal = new THREE.Vector3( x, y, z ); + normal.normalize(); + + geo.vertices.push( vertex ); + normals.push( normal ); + + } + + nfaces = object.count / 3; + + for ( i = 0; i < nfaces; i ++ ) { + + a = ( start + i ) * 3; + b = a + 1; + c = a + 2; + + na = normals[ a ]; + nb = normals[ b ]; + nc = normals[ c ]; + + face = new THREE.Face3( a, b, c, [ na, nb, nc ] ); + + geo.faces.push( face ); + + } + + start += nfaces; + object.count = 0; + + }; + + this.render( geo_callback ); + + // console.log( "generated " + geo.faces.length + " triangles" ); + + return geo; + + }; + + this.init( resolution ); + +}; + +THREE.MarchingCubes.prototype = Object.create( THREE.ImmediateRenderObject.prototype ); +THREE.MarchingCubes.prototype.constructor = THREE.MarchingCubes; + + +///////////////////////////////////// +// Marching cubes lookup tables +///////////////////////////////////// + +// These tables are straight from Paul Bourke's page: +// http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/ +// who in turn got them from Cory Gene Bloyd. + +THREE.edgeTable = new Int32Array( [ +0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, +0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, +0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, +0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, +0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c, +0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, +0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac, +0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, +0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c, +0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, +0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc, +0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, +0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c, +0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, +0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc, +0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, +0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, +0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, +0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, +0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, +0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, +0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, +0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, +0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460, +0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, +0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0, +0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, +0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230, +0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, +0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190, +0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, +0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 ] ); + +THREE.triTable = new Int32Array( [ +- 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 1, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 8, 3, 9, 8, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 8, 3, 1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 2, 10, 0, 2, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +2, 8, 3, 2, 10, 8, 10, 9, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 11, 2, 8, 11, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 9, 0, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 11, 2, 1, 9, 11, 9, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +3, 10, 1, 11, 10, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 10, 1, 0, 8, 10, 8, 11, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +3, 9, 0, 3, 11, 9, 11, 10, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 8, 10, 10, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +4, 3, 0, 7, 3, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 1, 9, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +4, 1, 9, 4, 7, 1, 7, 3, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 2, 10, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +3, 4, 7, 3, 0, 4, 1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 2, 10, 9, 0, 2, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, - 1, - 1, - 1, - 1, +8, 4, 7, 3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +11, 4, 7, 11, 2, 4, 2, 0, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 0, 1, 8, 4, 7, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, - 1, - 1, - 1, - 1, +3, 10, 1, 3, 11, 10, 7, 8, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, - 1, - 1, - 1, - 1, +4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, - 1, - 1, - 1, - 1, +4, 7, 11, 4, 11, 9, 9, 11, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 5, 4, 0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 5, 4, 1, 5, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +8, 5, 4, 8, 3, 5, 3, 1, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 2, 10, 9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +3, 0, 8, 1, 2, 10, 4, 9, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +5, 2, 10, 5, 4, 2, 4, 0, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, - 1, - 1, - 1, - 1, +9, 5, 4, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 11, 2, 0, 8, 11, 4, 9, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 5, 4, 0, 1, 5, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, - 1, - 1, - 1, - 1, +10, 3, 11, 10, 1, 3, 9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, - 1, - 1, - 1, - 1, +5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, - 1, - 1, - 1, - 1, +5, 4, 8, 5, 8, 10, 10, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 7, 8, 5, 7, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 3, 0, 9, 5, 3, 5, 7, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 7, 8, 0, 1, 7, 1, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 5, 3, 3, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 7, 8, 9, 5, 7, 10, 1, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, - 1, - 1, - 1, - 1, +8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, - 1, - 1, - 1, - 1, +2, 10, 5, 2, 5, 3, 3, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +7, 9, 5, 7, 8, 9, 3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, - 1, - 1, - 1, - 1, +2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, - 1, - 1, - 1, - 1, +11, 2, 1, 11, 1, 7, 7, 1, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, - 1, - 1, - 1, - 1, +5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, - 1, +11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, - 1, +11, 10, 5, 7, 11, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 8, 3, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 0, 1, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 8, 3, 1, 9, 8, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 6, 5, 2, 6, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 6, 5, 1, 2, 6, 3, 0, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 6, 5, 9, 0, 6, 0, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, - 1, - 1, - 1, - 1, +2, 3, 11, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +11, 0, 8, 11, 2, 0, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 1, 9, 2, 3, 11, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, - 1, - 1, - 1, - 1, +6, 3, 11, 6, 5, 3, 5, 1, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, - 1, - 1, - 1, - 1, +3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, - 1, - 1, - 1, - 1, +6, 5, 9, 6, 9, 11, 11, 9, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +5, 10, 6, 4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +4, 3, 0, 4, 7, 3, 6, 5, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 9, 0, 5, 10, 6, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, - 1, - 1, - 1, - 1, +6, 1, 2, 6, 5, 1, 4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, - 1, - 1, - 1, - 1, +8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, - 1, - 1, - 1, - 1, +7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, - 1, +3, 11, 2, 7, 8, 4, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, - 1, - 1, - 1, - 1, +0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, - 1, - 1, - 1, - 1, +9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, - 1, +8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, - 1, - 1, - 1, - 1, +5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, - 1, +0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, - 1, +6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, - 1, - 1, - 1, - 1, +10, 4, 9, 6, 4, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +4, 10, 6, 4, 9, 10, 0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +10, 0, 1, 10, 6, 0, 6, 4, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, - 1, - 1, - 1, - 1, +1, 4, 9, 1, 2, 4, 2, 6, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, - 1, - 1, - 1, - 1, +0, 2, 4, 4, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +8, 3, 2, 8, 2, 4, 4, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +10, 4, 9, 10, 6, 4, 11, 2, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, - 1, - 1, - 1, - 1, +3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, - 1, - 1, - 1, - 1, +6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, - 1, +9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, - 1, - 1, - 1, - 1, +8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, - 1, +3, 11, 6, 3, 6, 0, 0, 6, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +6, 4, 8, 11, 6, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +7, 10, 6, 7, 8, 10, 8, 9, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, - 1, - 1, - 1, - 1, +10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, - 1, - 1, - 1, - 1, +10, 6, 7, 10, 7, 1, 1, 7, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, - 1, - 1, - 1, - 1, +2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, - 1, +7, 8, 0, 7, 0, 6, 6, 0, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +7, 3, 2, 6, 7, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, - 1, - 1, - 1, - 1, +2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, - 1, +1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, - 1, +11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, - 1, - 1, - 1, - 1, +8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, - 1, +0, 9, 1, 11, 6, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, - 1, - 1, - 1, - 1, +7, 11, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +3, 0, 8, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 1, 9, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +8, 1, 9, 8, 3, 1, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +10, 1, 2, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 2, 10, 3, 0, 8, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +2, 9, 0, 2, 10, 9, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, - 1, - 1, - 1, - 1, +7, 2, 3, 6, 2, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +7, 0, 8, 7, 6, 0, 6, 2, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +2, 7, 6, 2, 3, 7, 0, 1, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, - 1, - 1, - 1, - 1, +10, 7, 6, 10, 1, 7, 1, 3, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, - 1, - 1, - 1, - 1, +0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, - 1, - 1, - 1, - 1, +7, 6, 10, 7, 10, 8, 8, 10, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +6, 8, 4, 11, 8, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +3, 6, 11, 3, 0, 6, 0, 4, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +8, 6, 11, 8, 4, 6, 9, 0, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, - 1, - 1, - 1, - 1, +6, 8, 4, 6, 11, 8, 2, 10, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, - 1, - 1, - 1, - 1, +4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, - 1, - 1, - 1, - 1, +10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, - 1, +8, 2, 3, 8, 4, 2, 4, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 4, 2, 4, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, - 1, - 1, - 1, - 1, +1, 9, 4, 1, 4, 2, 2, 4, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, - 1, - 1, - 1, - 1, +10, 1, 0, 10, 0, 6, 6, 0, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, - 1, +10, 9, 4, 6, 10, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +4, 9, 5, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 8, 3, 4, 9, 5, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +5, 0, 1, 5, 4, 0, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, - 1, - 1, - 1, - 1, +9, 5, 4, 10, 1, 2, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, - 1, - 1, - 1, - 1, +7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, - 1, - 1, - 1, - 1, +3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, - 1, +7, 2, 3, 7, 6, 2, 5, 4, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, - 1, - 1, - 1, - 1, +3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, - 1, - 1, - 1, - 1, +6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, - 1, +9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, - 1, - 1, - 1, - 1, +1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, - 1, +4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, - 1, +7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, - 1, - 1, - 1, - 1, +6, 9, 5, 6, 11, 9, 11, 8, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, - 1, - 1, - 1, - 1, +0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, - 1, - 1, - 1, - 1, +6, 11, 3, 6, 3, 5, 5, 3, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, - 1, - 1, - 1, - 1, +0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, - 1, +11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, - 1, +6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, - 1, - 1, - 1, - 1, +5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, - 1, - 1, - 1, - 1, +9, 5, 6, 9, 6, 0, 0, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, - 1, +1, 5, 6, 2, 1, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, - 1, +10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, - 1, - 1, - 1, - 1, +0, 3, 8, 5, 6, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +10, 5, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +11, 5, 10, 7, 5, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +11, 5, 10, 11, 7, 5, 8, 3, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +5, 11, 7, 5, 10, 11, 1, 9, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, - 1, - 1, - 1, - 1, +11, 1, 2, 11, 7, 1, 7, 5, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, - 1, - 1, - 1, - 1, +9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, - 1, - 1, - 1, - 1, +7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, - 1, +2, 5, 10, 2, 3, 5, 3, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, - 1, - 1, - 1, - 1, +9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, - 1, - 1, - 1, - 1, +9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, - 1, +1, 3, 5, 3, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 8, 7, 0, 7, 1, 1, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 0, 3, 9, 3, 5, 5, 3, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 8, 7, 5, 9, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +5, 8, 4, 5, 10, 8, 10, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, - 1, - 1, - 1, - 1, +0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, - 1, - 1, - 1, - 1, +10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, - 1, +2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, - 1, - 1, - 1, - 1, +0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, - 1, +0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, - 1, +9, 4, 5, 2, 11, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, - 1, - 1, - 1, - 1, +5, 10, 2, 5, 2, 4, 4, 2, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, - 1, +5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, - 1, - 1, - 1, - 1, +8, 4, 5, 8, 5, 3, 3, 5, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 4, 5, 1, 0, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, - 1, - 1, - 1, - 1, +9, 4, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +4, 11, 7, 4, 9, 11, 9, 10, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, - 1, - 1, - 1, - 1, +1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, - 1, - 1, - 1, - 1, +3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, - 1, +4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, - 1, - 1, - 1, - 1, +9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, - 1, +11, 7, 4, 11, 4, 2, 2, 4, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, - 1, - 1, - 1, - 1, +2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, - 1, - 1, - 1, - 1, +9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, - 1, +3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, - 1, +1, 10, 2, 8, 7, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +4, 9, 1, 4, 1, 7, 7, 1, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, - 1, - 1, - 1, - 1, +4, 0, 3, 7, 4, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +4, 8, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 10, 8, 10, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +3, 0, 9, 3, 9, 11, 11, 9, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 1, 10, 0, 10, 8, 8, 10, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +3, 1, 10, 11, 3, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 2, 11, 1, 11, 9, 9, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, - 1, - 1, - 1, - 1, +0, 2, 11, 8, 0, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +3, 2, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +2, 3, 8, 2, 8, 10, 10, 8, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +9, 10, 2, 0, 9, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, - 1, - 1, - 1, - 1, +1, 10, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +1, 3, 8, 9, 1, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 9, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +0, 3, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, +- 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1 ] ); diff --git a/node_modules/three/examples/js/Mirror.js b/node_modules/three/examples/js/Mirror.js new file mode 100644 index 00000000..8d4bf13d --- /dev/null +++ b/node_modules/three/examples/js/Mirror.js @@ -0,0 +1,301 @@ +/** + * @author Slayvin / http://slayvin.net + */ + +THREE.ShaderLib[ 'mirror' ] = { + + uniforms: { "mirrorColor": { type: "c", value: new THREE.Color( 0x7F7F7F ) }, + "mirrorSampler": { type: "t", value: null }, + "textureMatrix" : { type: "m4", value: new THREE.Matrix4() } + }, + + vertexShader: [ + + "uniform mat4 textureMatrix;", + + "varying vec4 mirrorCoord;", + + "void main() {", + + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + "vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", + "mirrorCoord = textureMatrix * worldPosition;", + + "gl_Position = projectionMatrix * mvPosition;", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 mirrorColor;", + "uniform sampler2D mirrorSampler;", + + "varying vec4 mirrorCoord;", + + "float blendOverlay(float base, float blend) {", + "return( base < 0.5 ? ( 2.0 * base * blend ) : (1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) );", + "}", + + "void main() {", + + "vec4 color = texture2DProj(mirrorSampler, mirrorCoord);", + "color = vec4(blendOverlay(mirrorColor.r, color.r), blendOverlay(mirrorColor.g, color.g), blendOverlay(mirrorColor.b, color.b), 1.0);", + + "gl_FragColor = color;", + + "}" + + ].join( "\n" ) + +}; + +THREE.Mirror = function ( renderer, camera, options ) { + + THREE.Object3D.call( this ); + + this.name = 'mirror_' + this.id; + + options = options || {}; + + this.matrixNeedsUpdate = true; + + var width = options.textureWidth !== undefined ? options.textureWidth : 512; + var height = options.textureHeight !== undefined ? options.textureHeight : 512; + + this.clipBias = options.clipBias !== undefined ? options.clipBias : 0.0; + + var mirrorColor = options.color !== undefined ? new THREE.Color( options.color ) : new THREE.Color( 0x7F7F7F ); + + this.renderer = renderer; + this.mirrorPlane = new THREE.Plane(); + this.normal = new THREE.Vector3( 0, 0, 1 ); + this.mirrorWorldPosition = new THREE.Vector3(); + this.cameraWorldPosition = new THREE.Vector3(); + this.rotationMatrix = new THREE.Matrix4(); + this.lookAtPosition = new THREE.Vector3( 0, 0, - 1 ); + this.clipPlane = new THREE.Vector4(); + + // For debug only, show the normal and plane of the mirror + var debugMode = options.debugMode !== undefined ? options.debugMode : false; + + if ( debugMode ) { + + var arrow = new THREE.ArrowHelper( new THREE.Vector3( 0, 0, 1 ), new THREE.Vector3( 0, 0, 0 ), 10, 0xffff80 ); + var planeGeometry = new THREE.Geometry(); + planeGeometry.vertices.push( new THREE.Vector3( - 10, - 10, 0 ) ); + planeGeometry.vertices.push( new THREE.Vector3( 10, - 10, 0 ) ); + planeGeometry.vertices.push( new THREE.Vector3( 10, 10, 0 ) ); + planeGeometry.vertices.push( new THREE.Vector3( - 10, 10, 0 ) ); + planeGeometry.vertices.push( planeGeometry.vertices[ 0 ] ); + var plane = new THREE.Line( planeGeometry, new THREE.LineBasicMaterial( { color: 0xffff80 } ) ); + + this.add( arrow ); + this.add( plane ); + + } + + if ( camera instanceof THREE.PerspectiveCamera ) { + + this.camera = camera; + + } else { + + this.camera = new THREE.PerspectiveCamera(); + console.log( this.name + ': camera is not a Perspective Camera!' ); + + } + + this.textureMatrix = new THREE.Matrix4(); + + this.mirrorCamera = this.camera.clone(); + this.mirrorCamera.matrixAutoUpdate = true; + + var parameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false }; + + this.texture = new THREE.WebGLRenderTarget( width, height, parameters ); + this.tempTexture = new THREE.WebGLRenderTarget( width, height, parameters ); + + var mirrorShader = THREE.ShaderLib[ "mirror" ]; + var mirrorUniforms = THREE.UniformsUtils.clone( mirrorShader.uniforms ); + + this.material = new THREE.ShaderMaterial( { + + fragmentShader: mirrorShader.fragmentShader, + vertexShader: mirrorShader.vertexShader, + uniforms: mirrorUniforms + + } ); + + this.material.uniforms.mirrorSampler.value = this.texture; + this.material.uniforms.mirrorColor.value = mirrorColor; + this.material.uniforms.textureMatrix.value = this.textureMatrix; + + if ( ! THREE.Math.isPowerOfTwo( width ) || ! THREE.Math.isPowerOfTwo( height ) ) { + + this.texture.generateMipmaps = false; + this.tempTexture.generateMipmaps = false; + + } + + this.updateTextureMatrix(); + this.render(); + +}; + +THREE.Mirror.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Mirror.prototype.constructor = THREE.Mirror; + +THREE.Mirror.prototype.renderWithMirror = function ( otherMirror ) { + + // update the mirror matrix to mirror the current view + this.updateTextureMatrix(); + this.matrixNeedsUpdate = false; + + // set the camera of the other mirror so the mirrored view is the reference view + var tempCamera = otherMirror.camera; + otherMirror.camera = this.mirrorCamera; + + // render the other mirror in temp texture + otherMirror.renderTemp(); + otherMirror.material.uniforms.mirrorSampler.value = otherMirror.tempTexture; + + // render the current mirror + this.render(); + this.matrixNeedsUpdate = true; + + // restore material and camera of other mirror + otherMirror.material.uniforms.mirrorSampler.value = otherMirror.texture; + otherMirror.camera = tempCamera; + + // restore texture matrix of other mirror + otherMirror.updateTextureMatrix(); + +}; + +THREE.Mirror.prototype.updateTextureMatrix = function () { + + this.updateMatrixWorld(); + this.camera.updateMatrixWorld(); + + this.mirrorWorldPosition.setFromMatrixPosition( this.matrixWorld ); + this.cameraWorldPosition.setFromMatrixPosition( this.camera.matrixWorld ); + + this.rotationMatrix.extractRotation( this.matrixWorld ); + + this.normal.set( 0, 0, 1 ); + this.normal.applyMatrix4( this.rotationMatrix ); + + var view = this.mirrorWorldPosition.clone().sub( this.cameraWorldPosition ); + view.reflect( this.normal ).negate(); + view.add( this.mirrorWorldPosition ); + + this.rotationMatrix.extractRotation( this.camera.matrixWorld ); + + this.lookAtPosition.set( 0, 0, - 1 ); + this.lookAtPosition.applyMatrix4( this.rotationMatrix ); + this.lookAtPosition.add( this.cameraWorldPosition ); + + var target = this.mirrorWorldPosition.clone().sub( this.lookAtPosition ); + target.reflect( this.normal ).negate(); + target.add( this.mirrorWorldPosition ); + + this.up.set( 0, - 1, 0 ); + this.up.applyMatrix4( this.rotationMatrix ); + this.up.reflect( this.normal ).negate(); + + this.mirrorCamera.position.copy( view ); + this.mirrorCamera.up = this.up; + this.mirrorCamera.lookAt( target ); + + this.mirrorCamera.updateProjectionMatrix(); + this.mirrorCamera.updateMatrixWorld(); + this.mirrorCamera.matrixWorldInverse.getInverse( this.mirrorCamera.matrixWorld ); + + // Update the texture matrix + this.textureMatrix.set( 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0 ); + this.textureMatrix.multiply( this.mirrorCamera.projectionMatrix ); + this.textureMatrix.multiply( this.mirrorCamera.matrixWorldInverse ); + + // Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html + // Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf + this.mirrorPlane.setFromNormalAndCoplanarPoint( this.normal, this.mirrorWorldPosition ); + this.mirrorPlane.applyMatrix4( this.mirrorCamera.matrixWorldInverse ); + + this.clipPlane.set( this.mirrorPlane.normal.x, this.mirrorPlane.normal.y, this.mirrorPlane.normal.z, this.mirrorPlane.constant ); + + var q = new THREE.Vector4(); + var projectionMatrix = this.mirrorCamera.projectionMatrix; + + q.x = ( Math.sign( this.clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ]; + q.y = ( Math.sign( this.clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ]; + q.z = - 1.0; + q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ]; + + // Calculate the scaled plane vector + var c = new THREE.Vector4(); + c = this.clipPlane.multiplyScalar( 2.0 / this.clipPlane.dot( q ) ); + + // Replacing the third row of the projection matrix + projectionMatrix.elements[ 2 ] = c.x; + projectionMatrix.elements[ 6 ] = c.y; + projectionMatrix.elements[ 10 ] = c.z + 1.0 - this.clipBias; + projectionMatrix.elements[ 14 ] = c.w; + +}; + +THREE.Mirror.prototype.render = function () { + + if ( this.matrixNeedsUpdate ) this.updateTextureMatrix(); + + this.matrixNeedsUpdate = true; + + // Render the mirrored view of the current scene into the target texture + var scene = this; + + while ( scene.parent !== null ) { + + scene = scene.parent; + + } + + if ( scene !== undefined && scene instanceof THREE.Scene ) { + + // We can't render ourself to ourself + var visible = this.material.visible; + this.material.visible = false; + + this.renderer.render( scene, this.mirrorCamera, this.texture, true ); + + this.material.visible = visible; + + } + +}; + +THREE.Mirror.prototype.renderTemp = function () { + + if ( this.matrixNeedsUpdate ) this.updateTextureMatrix(); + + this.matrixNeedsUpdate = true; + + // Render the mirrored view of the current scene into the target texture + var scene = this; + + while ( scene.parent !== null ) { + + scene = scene.parent; + + } + + if ( scene !== undefined && scene instanceof THREE.Scene ) { + + this.renderer.render( scene, this.mirrorCamera, this.tempTexture, true ); + + } + +}; diff --git a/node_modules/three/examples/js/MorphAnimMesh.js b/node_modules/three/examples/js/MorphAnimMesh.js new file mode 100644 index 00000000..ba49e683 --- /dev/null +++ b/node_modules/three/examples/js/MorphAnimMesh.js @@ -0,0 +1,70 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.MorphAnimMesh = function ( geometry, material ) { + + THREE.Mesh.call( this, geometry, material ); + + this.type = 'MorphAnimMesh'; + + this.mixer = new THREE.AnimationMixer( this ); + this.activeAction = null; +}; + +THREE.MorphAnimMesh.prototype = Object.create( THREE.Mesh.prototype ); +THREE.MorphAnimMesh.prototype.constructor = THREE.MorphAnimMesh; + +THREE.MorphAnimMesh.prototype.setDirectionForward = function () { + + this.mixer.timeScale = 1.0; + +}; + +THREE.MorphAnimMesh.prototype.setDirectionBackward = function () { + + this.mixer.timeScale = -1.0; + +}; + +THREE.MorphAnimMesh.prototype.playAnimation = function ( label, fps ) { + + if( this.activeAction ) { + + this.mixer.removeAction( this.activeAction ); + this.activeAction = null; + + } + + var clip = THREE.AnimationClip.findByName( this.geometry.animations, label ); + + if ( clip ) { + + var action = new THREE.AnimationAction( clip ); + action.timeScale = ( clip.tracks.length * fps ) / clip.duration; + this.mixer.addAction( action ); + this.activeAction = action; + + } else { + + throw new Error( 'THREE.MorphAnimMesh: animations[' + label + '] undefined in .playAnimation()' ); + + } + +}; + +THREE.MorphAnimMesh.prototype.updateAnimation = function ( delta ) { + + this.mixer.update( delta ); + +}; + +THREE.MorphAnimMesh.prototype.copy = function ( source ) { + + THREE.Mesh.prototype.copy.call( this, source ); + + this.mixer = new THREE.AnimationMixer( this ); + + return this; + +}; diff --git a/node_modules/three/examples/js/MorphAnimation.js b/node_modules/three/examples/js/MorphAnimation.js new file mode 100644 index 00000000..64d2d5be --- /dev/null +++ b/node_modules/three/examples/js/MorphAnimation.js @@ -0,0 +1,73 @@ +/** + * @author mrdoob / http://mrdoob.com + * @author willy-vvu / http://willy-vvu.github.io + */ + +THREE.MorphAnimation = function ( mesh ) { + + this.mesh = mesh; + this.frames = mesh.morphTargetInfluences.length; + this.currentTime = 0; + this.duration = 1000; + this.loop = true; + this.lastFrame = 0; + this.currentFrame = 0; + + this.isPlaying = false; + +}; + +THREE.MorphAnimation.prototype = { + + constructor: THREE.MorphAnimation, + + play: function () { + + this.isPlaying = true; + + }, + + pause: function () { + + this.isPlaying = false; + + }, + + update: function ( delta ) { + + if ( this.isPlaying === false ) return; + + this.currentTime += delta; + + if ( this.loop === true && this.currentTime > this.duration ) { + + this.currentTime %= this.duration; + + } + + this.currentTime = Math.min( this.currentTime, this.duration ); + + var frameTime = this.duration / this.frames; + var frame = Math.floor( this.currentTime / frameTime ); + + var influences = this.mesh.morphTargetInfluences; + + if ( frame !== this.currentFrame ) { + + influences[ this.lastFrame ] = 0; + influences[ this.currentFrame ] = 1; + influences[ frame ] = 0; + + this.lastFrame = this.currentFrame; + this.currentFrame = frame; + + } + + var mix = ( this.currentTime % frameTime ) / frameTime; + + influences[ frame ] = mix; + influences[ this.lastFrame ] = 1 - mix; + + } + +}; diff --git a/node_modules/three/examples/js/Ocean.js b/node_modules/three/examples/js/Ocean.js new file mode 100644 index 00000000..091516f2 --- /dev/null +++ b/node_modules/three/examples/js/Ocean.js @@ -0,0 +1,363 @@ +THREE.Ocean = function ( renderer, camera, scene, options ) { + + // flag used to trigger parameter changes + this.changed = true; + this.initial = true; + + // Assign required parameters as object properties + this.oceanCamera = new THREE.OrthographicCamera(); //camera.clone(); + this.oceanCamera.position.z = 1; + this.renderer = renderer; + this.renderer.clearColor( 0xffffff ); + + this.scene = new THREE.Scene(); + + // Enable necessary extensions + this.renderer.context.getExtension( 'OES_texture_float' ); + this.renderer.context.getExtension( 'OES_texture_float_linear' ); + + // Assign optional parameters as variables and object properties + function optionalParameter( value, defaultValue ) { + + return value !== undefined ? value : defaultValue; + + } + options = options || {}; + this.clearColor = optionalParameter( options.CLEAR_COLOR, [ 1.0, 1.0, 1.0, 0.0 ] ); + this.geometryOrigin = optionalParameter( options.GEOMETRY_ORIGIN, [ - 1000.0, - 1000.0 ] ); + this.sunDirectionX = optionalParameter( options.SUN_DIRECTION[ 0 ], - 1.0 ); + this.sunDirectionY = optionalParameter( options.SUN_DIRECTION[ 1 ], 1.0 ); + this.sunDirectionZ = optionalParameter( options.SUN_DIRECTION[ 2 ], 1.0 ); + this.oceanColor = optionalParameter( options.OCEAN_COLOR, new THREE.Vector3( 0.004, 0.016, 0.047 ) ); + this.skyColor = optionalParameter( options.SKY_COLOR, new THREE.Vector3( 3.2, 9.6, 12.8 ) ); + this.exposure = optionalParameter( options.EXPOSURE, 0.35 ); + this.geometryResolution = optionalParameter( options.GEOMETRY_RESOLUTION, 32 ); + this.geometrySize = optionalParameter( options.GEOMETRY_SIZE, 2000 ); + this.resolution = optionalParameter( options.RESOLUTION, 64 ); + this.floatSize = optionalParameter( options.SIZE_OF_FLOAT, 4 ); + this.windX = optionalParameter( options.INITIAL_WIND[ 0 ], 10.0 ), + this.windY = optionalParameter( options.INITIAL_WIND[ 1 ], 10.0 ), + this.size = optionalParameter( options.INITIAL_SIZE, 250.0 ), + this.choppiness = optionalParameter( options.INITIAL_CHOPPINESS, 1.5 ); + + // + this.matrixNeedsUpdate = false; + + // Setup framebuffer pipeline + var renderTargetType = optionalParameter( options.USE_HALF_FLOAT, false ) ? THREE.HalfFloatType : THREE.FloatType; + var LinearClampParams = { + minFilter: THREE.LinearFilter, + magFilter: THREE.LinearFilter, + wrapS: THREE.ClampToEdgeWrapping, + wrapT: THREE.ClampToEdgeWrapping, + format: THREE.RGBAFormat, + stencilBuffer: false, + depthBuffer: false, + premultiplyAlpha: false, + type: renderTargetType + }; + var NearestClampParams = { + minFilter: THREE.NearestFilter, + magFilter: THREE.NearestFilter, + wrapS: THREE.ClampToEdgeWrapping, + wrapT: THREE.ClampToEdgeWrapping, + format: THREE.RGBAFormat, + stencilBuffer: false, + depthBuffer: false, + premultiplyAlpha: false, + type: renderTargetType + }; + var NearestRepeatParams = { + minFilter: THREE.NearestFilter, + magFilter: THREE.NearestFilter, + wrapS: THREE.RepeatWrapping, + wrapT: THREE.RepeatWrapping, + format: THREE.RGBAFormat, + stencilBuffer: false, + depthBuffer: false, + premultiplyAlpha: false, + type: renderTargetType + }; + this.initialSpectrumFramebuffer = new THREE.WebGLRenderTarget( this.resolution, this.resolution, NearestRepeatParams ); + this.spectrumFramebuffer = new THREE.WebGLRenderTarget( this.resolution, this.resolution, NearestClampParams ); + this.pingPhaseFramebuffer = new THREE.WebGLRenderTarget( this.resolution, this.resolution, NearestClampParams ); + this.pongPhaseFramebuffer = new THREE.WebGLRenderTarget( this.resolution, this.resolution, NearestClampParams ); + this.pingTransformFramebuffer = new THREE.WebGLRenderTarget( this.resolution, this.resolution, NearestClampParams ); + this.pongTransformFramebuffer = new THREE.WebGLRenderTarget( this.resolution, this.resolution, NearestClampParams ); + this.displacementMapFramebuffer = new THREE.WebGLRenderTarget( this.resolution, this.resolution, LinearClampParams ); + this.normalMapFramebuffer = new THREE.WebGLRenderTarget( this.resolution, this.resolution, LinearClampParams ); + + // Define shaders and constant uniforms + //////////////////////////////////////// + + // 0 - The vertex shader used in all of the simulation steps + var fullscreeenVertexShader = THREE.ShaderLib[ "ocean_sim_vertex" ]; + + // 1 - Horizontal wave vertices used for FFT + var oceanHorizontalShader = THREE.ShaderLib[ "ocean_subtransform" ]; + var oceanHorizontalUniforms = THREE.UniformsUtils.clone( oceanHorizontalShader.uniforms ); + this.materialOceanHorizontal = new THREE.ShaderMaterial( { + uniforms: oceanHorizontalUniforms, + vertexShader: fullscreeenVertexShader.vertexShader, + fragmentShader: "#define HORIZONTAL \n" + oceanHorizontalShader.fragmentShader + } ); + this.materialOceanHorizontal.uniforms.u_transformSize = { type: "f", value: this.resolution }; + this.materialOceanHorizontal.uniforms.u_subtransformSize = { type: "f", value: null }; + this.materialOceanHorizontal.uniforms.u_input = { type: "t", value: null }; + this.materialOceanHorizontal.depthTest = false; + + // 2 - Vertical wave vertices used for FFT + var oceanVerticalShader = THREE.ShaderLib[ "ocean_subtransform" ]; + var oceanVerticalUniforms = THREE.UniformsUtils.clone( oceanVerticalShader.uniforms ); + this.materialOceanVertical = new THREE.ShaderMaterial( { + uniforms: oceanVerticalUniforms, + vertexShader: fullscreeenVertexShader.vertexShader, + fragmentShader: oceanVerticalShader.fragmentShader + } ); + this.materialOceanVertical.uniforms.u_transformSize = { type: "f", value: this.resolution }; + this.materialOceanVertical.uniforms.u_subtransformSize = { type: "f", value: null }; + this.materialOceanVertical.uniforms.u_input = { type: "t", value: null }; + this.materialOceanVertical.depthTest = false; + + // 3 - Initial spectrum used to generate height map + var initialSpectrumShader = THREE.ShaderLib[ "ocean_initial_spectrum" ]; + var initialSpectrumUniforms = THREE.UniformsUtils.clone( initialSpectrumShader.uniforms ); + this.materialInitialSpectrum = new THREE.ShaderMaterial( { + uniforms: initialSpectrumUniforms, + vertexShader: fullscreeenVertexShader.vertexShader, + fragmentShader: initialSpectrumShader.fragmentShader + } ); + this.materialInitialSpectrum.uniforms.u_wind = { type: "v2", value: new THREE.Vector2() }; + this.materialInitialSpectrum.uniforms.u_resolution = { type: "f", value: this.resolution }; + this.materialInitialSpectrum.depthTest = false; + + // 4 - Phases used to animate heightmap + var phaseShader = THREE.ShaderLib[ "ocean_phase" ]; + var phaseUniforms = THREE.UniformsUtils.clone( phaseShader.uniforms ); + this.materialPhase = new THREE.ShaderMaterial( { + uniforms: phaseUniforms, + vertexShader: fullscreeenVertexShader.vertexShader, + fragmentShader: phaseShader.fragmentShader + } ); + this.materialPhase.uniforms.u_resolution = { type: "f", value: this.resolution }; + this.materialPhase.depthTest = false; + + // 5 - Shader used to update spectrum + var spectrumShader = THREE.ShaderLib[ "ocean_spectrum" ]; + var spectrumUniforms = THREE.UniformsUtils.clone( spectrumShader.uniforms ); + this.materialSpectrum = new THREE.ShaderMaterial( { + uniforms: spectrumUniforms, + vertexShader: fullscreeenVertexShader.vertexShader, + fragmentShader: spectrumShader.fragmentShader + } ); + this.materialSpectrum.uniforms.u_initialSpectrum = { type: "t", value: null }; + this.materialSpectrum.uniforms.u_resolution = { type: "f", value: this.resolution }; + this.materialSpectrum.depthTest = false; + + // 6 - Shader used to update spectrum normals + var normalShader = THREE.ShaderLib[ "ocean_normals" ]; + var normalUniforms = THREE.UniformsUtils.clone( normalShader.uniforms ); + this.materialNormal = new THREE.ShaderMaterial( { + uniforms: normalUniforms, + vertexShader: fullscreeenVertexShader.vertexShader, + fragmentShader: normalShader.fragmentShader + } ); + this.materialNormal.uniforms.u_displacementMap = { type: "t", value: null }; + this.materialNormal.uniforms.u_resolution = { type: "f", value: this.resolution }; + this.materialNormal.depthTest = false; + + // 7 - Shader used to update normals + var oceanShader = THREE.ShaderLib[ "ocean_main" ]; + var oceanUniforms = THREE.UniformsUtils.clone( oceanShader.uniforms ); + this.materialOcean = new THREE.ShaderMaterial( { + uniforms: oceanUniforms, + vertexShader: oceanShader.vertexShader, + fragmentShader: oceanShader.fragmentShader + } ); + // this.materialOcean.wireframe = true; + this.materialOcean.uniforms.u_geometrySize = { type: "f", value: this.resolution }; + this.materialOcean.uniforms.u_displacementMap = { type: "t", value: this.displacementMapFramebuffer }; + this.materialOcean.uniforms.u_normalMap = { type: "t", value: this.normalMapFramebuffer }; + this.materialOcean.uniforms.u_oceanColor = { type: "v3", value: this.oceanColor }; + this.materialOcean.uniforms.u_skyColor = { type: "v3", value: this.skyColor }; + this.materialOcean.uniforms.u_sunDirection = { type: "v3", value: new THREE.Vector3( this.sunDirectionX, this.sunDirectionY, this.sunDirectionZ ) }; + this.materialOcean.uniforms.u_exposure = { type: "f", value: this.exposure }; + + // Disable blending to prevent default premultiplied alpha values + this.materialOceanHorizontal.blending = 0; + this.materialOceanVertical.blending = 0; + this.materialInitialSpectrum.blending = 0; + this.materialPhase.blending = 0; + this.materialSpectrum.blending = 0; + this.materialNormal.blending = 0; + this.materialOcean.blending = 0; + + // Create the simulation plane + this.screenQuad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ) ); + this.scene.add( this.screenQuad ); + + // Initialise spectrum data + this.generateSeedPhaseTexture(); + + // Generate the ocean mesh + this.generateMesh(); + +}; + +THREE.Ocean.prototype.generateMesh = function () { + + var geometry = new THREE.PlaneBufferGeometry( this.geometrySize, this.geometrySize, this.geometryResolution, this.geometryResolution ); + + geometry.rotateX( - Math.PI / 2 ); + + this.oceanMesh = new THREE.Mesh( geometry, this.materialOcean ); + +}; + +THREE.Ocean.prototype.render = function () { + + this.scene.overrideMaterial = null; + + if ( this.changed ) + this.renderInitialSpectrum(); + + this.renderWavePhase(); + this.renderSpectrum(); + this.renderSpectrumFFT(); + this.renderNormalMap(); + this.scene.overrideMaterial = null; + +}; + +THREE.Ocean.prototype.generateSeedPhaseTexture = function() { + + // Setup the seed texture + this.pingPhase = true; + var phaseArray = new window.Float32Array( this.resolution * this.resolution * 4 ); + for ( var i = 0; i < this.resolution; i ++ ) { + + for ( var j = 0; j < this.resolution; j ++ ) { + + phaseArray[ i * this.resolution * 4 + j * 4 ] = Math.random() * 2.0 * Math.PI; + phaseArray[ i * this.resolution * 4 + j * 4 + 1 ] = 0.0; + phaseArray[ i * this.resolution * 4 + j * 4 + 2 ] = 0.0; + phaseArray[ i * this.resolution * 4 + j * 4 + 3 ] = 0.0; + + } + + } + + this.pingPhaseTexture = new THREE.DataTexture( phaseArray, this.resolution, this.resolution, THREE.RGBAFormat ); + this.pingPhaseTexture.wrapS = THREE.ClampToEdgeWrapping; + this.pingPhaseTexture.wrapT = THREE.ClampToEdgeWrapping; + this.pingPhaseTexture.type = THREE.FloatType; + this.pingPhaseTexture.needsUpdate = true; + +}; + +THREE.Ocean.prototype.renderInitialSpectrum = function () { + + this.scene.overrideMaterial = this.materialInitialSpectrum; + this.materialInitialSpectrum.uniforms.u_wind.value.set( this.windX, this.windY ); + this.materialInitialSpectrum.uniforms.u_size.value = this.size; + this.renderer.render( this.scene, this.oceanCamera, this.initialSpectrumFramebuffer, true ); + +}; + +THREE.Ocean.prototype.renderWavePhase = function () { + + this.scene.overrideMaterial = this.materialPhase; + this.screenQuad.material = this.materialPhase; + if ( this.initial ) { + + this.materialPhase.uniforms.u_phases.value = this.pingPhaseTexture; + this.initial = false; + + }else { + + this.materialPhase.uniforms.u_phases.value = this.pingPhase ? this.pingPhaseFramebuffer : this.pongPhaseFramebuffer; + + } + this.materialPhase.uniforms.u_deltaTime.value = this.deltaTime; + this.materialPhase.uniforms.u_size.value = this.size; + this.renderer.render( this.scene, this.oceanCamera, this.pingPhase ? this.pongPhaseFramebuffer : this.pingPhaseFramebuffer ); + this.pingPhase = ! this.pingPhase; + +}; + +THREE.Ocean.prototype.renderSpectrum = function () { + + this.scene.overrideMaterial = this.materialSpectrum; + this.materialSpectrum.uniforms.u_initialSpectrum.value = this.initialSpectrumFramebuffer; + this.materialSpectrum.uniforms.u_phases.value = this.pingPhase ? this.pingPhaseFramebuffer : this.pongPhaseFramebuffer; + this.materialSpectrum.uniforms.u_choppiness.value = this.choppiness ; + this.materialSpectrum.uniforms.u_size.value = this.size ; + this.renderer.render( this.scene, this.oceanCamera, this.spectrumFramebuffer ); + +}; + +THREE.Ocean.prototype.renderSpectrumFFT = function() { + + // GPU FFT using Stockham formulation + var iterations = Math.log( this.resolution ) / Math.log( 2 ); // log2 + + this.scene.overrideMaterial = this.materialOceanHorizontal; + + for ( var i = 0; i < iterations; i ++ ) { + + if ( i === 0 ) { + + this.materialOceanHorizontal.uniforms.u_input.value = this.spectrumFramebuffer; + this.materialOceanHorizontal.uniforms.u_subtransformSize.value = Math.pow( 2, ( i % ( iterations ) ) + 1 ); + this.renderer.render( this.scene, this.oceanCamera, this.pingTransformFramebuffer ); + + } else if ( i % 2 === 1 ) { + + this.materialOceanHorizontal.uniforms.u_input.value = this.pingTransformFramebuffer; + this.materialOceanHorizontal.uniforms.u_subtransformSize.value = Math.pow( 2, ( i % ( iterations ) ) + 1 ); + this.renderer.render( this.scene, this.oceanCamera, this.pongTransformFramebuffer ); + + } else { + + this.materialOceanHorizontal.uniforms.u_input.value = this.pongTransformFramebuffer; + this.materialOceanHorizontal.uniforms.u_subtransformSize.value = Math.pow( 2, ( i % ( iterations ) ) + 1 ); + this.renderer.render( this.scene, this.oceanCamera, this.pingTransformFramebuffer ); + + } + + } + this.scene.overrideMaterial = this.materialOceanVertical; + for ( var i = iterations; i < iterations * 2; i ++ ) { + + if ( i === iterations * 2 - 1 ) { + + this.materialOceanVertical.uniforms.u_input.value = ( iterations % 2 === 0 ) ? this.pingTransformFramebuffer : this.pongTransformFramebuffer; + this.materialOceanVertical.uniforms.u_subtransformSize.value = Math.pow( 2, ( i % ( iterations ) ) + 1 ); + this.renderer.render( this.scene, this.oceanCamera, this.displacementMapFramebuffer ); + + } else if ( i % 2 === 1 ) { + + this.materialOceanVertical.uniforms.u_input.value = this.pingTransformFramebuffer; + this.materialOceanVertical.uniforms.u_subtransformSize.value = Math.pow( 2, ( i % ( iterations ) ) + 1 ); + this.renderer.render( this.scene, this.oceanCamera, this.pongTransformFramebuffer ); + + } else { + + this.materialOceanVertical.uniforms.u_input.value = this.pongTransformFramebuffer; + this.materialOceanVertical.uniforms.u_subtransformSize.value = Math.pow( 2, ( i % ( iterations ) ) + 1 ); + this.renderer.render( this.scene, this.oceanCamera, this.pingTransformFramebuffer ); + + } + + } + +}; + +THREE.Ocean.prototype.renderNormalMap = function () { + + this.scene.overrideMaterial = this.materialNormal; + if ( this.changed ) this.materialNormal.uniforms.u_size.value = this.size; + this.materialNormal.uniforms.u_displacementMap.value = this.displacementMapFramebuffer; + this.renderer.render( this.scene, this.oceanCamera, this.normalMapFramebuffer, true ); + +}; diff --git a/node_modules/three/examples/js/Octree.js b/node_modules/three/examples/js/Octree.js new file mode 100644 index 00000000..d4ef511f --- /dev/null +++ b/node_modules/three/examples/js/Octree.js @@ -0,0 +1,2137 @@ +/*! + * + * threeoctree.js (r60) / https://github.com/collinhover/threeoctree + * (sparse) dynamic 3D spatial representation structure for fast searches. + * + * @author Collin Hover / http://collinhover.com/ + * based on Dynamic Octree by Piko3D @ http://www.piko3d.com/ and Octree by Marek Pawlowski @ pawlowski.it + * + */ + ( function ( THREE ) { + + "use strict"; + + /*=================================================== + + utility + + =====================================================*/ + + function isNumber ( n ) { + + return ! isNaN( n ) && isFinite( n ); + + } + + function isArray ( target ) { + + return Object.prototype.toString.call( target ) === '[object Array]'; + + } + + function toArray ( target ) { + + return target ? ( isArray ( target ) !== true ? [ target ] : target ) : []; + + } + + function indexOfValue( array, value ) { + + for ( var i = 0, il = array.length; i < il; i ++ ) { + + if ( array[ i ] === value ) { + + return i; + + } + + } + + return - 1; + + } + + function indexOfPropertyWithValue( array, property, value ) { + + for ( var i = 0, il = array.length; i < il; i ++ ) { + + if ( array[ i ][ property ] === value ) { + + return i; + + } + + } + + return - 1; + + } + + /*=================================================== + + octree + + =====================================================*/ + + THREE.Octree = function ( parameters ) { + + // handle parameters + + parameters = parameters || {}; + + parameters.tree = this; + + // static properties ( modification is not recommended ) + + this.nodeCount = 0; + + this.INDEX_INSIDE_CROSS = - 1; + this.INDEX_OUTSIDE_OFFSET = 2; + + this.INDEX_OUTSIDE_POS_X = isNumber( parameters.INDEX_OUTSIDE_POS_X ) ? parameters.INDEX_OUTSIDE_POS_X : 0; + this.INDEX_OUTSIDE_NEG_X = isNumber( parameters.INDEX_OUTSIDE_NEG_X ) ? parameters.INDEX_OUTSIDE_NEG_X : 1; + this.INDEX_OUTSIDE_POS_Y = isNumber( parameters.INDEX_OUTSIDE_POS_Y ) ? parameters.INDEX_OUTSIDE_POS_Y : 2; + this.INDEX_OUTSIDE_NEG_Y = isNumber( parameters.INDEX_OUTSIDE_NEG_Y ) ? parameters.INDEX_OUTSIDE_NEG_Y : 3; + this.INDEX_OUTSIDE_POS_Z = isNumber( parameters.INDEX_OUTSIDE_POS_Z ) ? parameters.INDEX_OUTSIDE_POS_Z : 4; + this.INDEX_OUTSIDE_NEG_Z = isNumber( parameters.INDEX_OUTSIDE_NEG_Z ) ? parameters.INDEX_OUTSIDE_NEG_Z : 5; + + this.INDEX_OUTSIDE_MAP = []; + this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_POS_X ] = { index: this.INDEX_OUTSIDE_POS_X, count: 0, x: 1, y: 0, z: 0 }; + this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_NEG_X ] = { index: this.INDEX_OUTSIDE_NEG_X, count: 0, x: - 1, y: 0, z: 0 }; + this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_POS_Y ] = { index: this.INDEX_OUTSIDE_POS_Y, count: 0, x: 0, y: 1, z: 0 }; + this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_NEG_Y ] = { index: this.INDEX_OUTSIDE_NEG_Y, count: 0, x: 0, y: - 1, z: 0 }; + this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_POS_Z ] = { index: this.INDEX_OUTSIDE_POS_Z, count: 0, x: 0, y: 0, z: 1 }; + this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_NEG_Z ] = { index: this.INDEX_OUTSIDE_NEG_Z, count: 0, x: 0, y: 0, z: - 1 }; + + this.FLAG_POS_X = 1 << ( this.INDEX_OUTSIDE_POS_X + 1 ); + this.FLAG_NEG_X = 1 << ( this.INDEX_OUTSIDE_NEG_X + 1 ); + this.FLAG_POS_Y = 1 << ( this.INDEX_OUTSIDE_POS_Y + 1 ); + this.FLAG_NEG_Y = 1 << ( this.INDEX_OUTSIDE_NEG_Y + 1 ); + this.FLAG_POS_Z = 1 << ( this.INDEX_OUTSIDE_POS_Z + 1 ); + this.FLAG_NEG_Z = 1 << ( this.INDEX_OUTSIDE_NEG_Z + 1 ); + + this.utilVec31Search = new THREE.Vector3(); + this.utilVec32Search = new THREE.Vector3(); + + // pass scene to see octree structure + + this.scene = parameters.scene; + + if ( this.scene ) { + + this.visualGeometry = new THREE.BoxGeometry( 1, 1, 1 ); + this.visualMaterial = new THREE.MeshBasicMaterial( { color: 0xFF0066, wireframe: true, wireframeLinewidth: 1 } ); + + } + + // properties + + this.objects = []; + this.objectsMap = {}; + this.objectsData = []; + this.objectsDeferred = []; + + this.depthMax = isNumber( parameters.depthMax ) ? parameters.depthMax : Infinity; + this.objectsThreshold = isNumber( parameters.objectsThreshold ) ? parameters.objectsThreshold : 8; + this.overlapPct = isNumber( parameters.overlapPct ) ? parameters.overlapPct : 0.15; + this.undeferred = parameters.undeferred || false; + + this.root = parameters.root instanceof THREE.OctreeNode ? parameters.root : new THREE.OctreeNode( parameters ); + + }; + + THREE.Octree.prototype = { + + update: function () { + + // add any deferred objects that were waiting for render cycle + + if ( this.objectsDeferred.length > 0 ) { + + for ( var i = 0, il = this.objectsDeferred.length; i < il; i ++ ) { + + var deferred = this.objectsDeferred[ i ]; + + this.addDeferred( deferred.object, deferred.options ); + + } + + this.objectsDeferred.length = 0; + + } + + }, + + add: function ( object, options ) { + + // add immediately + + if ( this.undeferred ) { + + this.updateObject( object ); + + this.addDeferred( object, options ); + + } else { + + // defer add until update called + + this.objectsDeferred.push( { object: object, options: options } ); + + } + + }, + + addDeferred: function ( object, options ) { + + var i, l, + geometry, + faces, + useFaces, + vertices, + useVertices, + objectData; + + // ensure object is not object data + + if ( object instanceof THREE.OctreeObjectData ) { + + object = object.object; + + } + + // check uuid to avoid duplicates + + if ( ! object.uuid ) { + + object.uuid = THREE.Math.generateUUID(); + + } + + if ( ! this.objectsMap[ object.uuid ] ) { + + // store + + this.objects.push( object ); + this.objectsMap[ object.uuid ] = object; + + // check options + + if ( options ) { + + useFaces = options.useFaces; + useVertices = options.useVertices; + + } + + if ( useVertices === true ) { + + geometry = object.geometry; + vertices = geometry.vertices; + + for ( i = 0, l = vertices.length; i < l; i ++ ) { + + this.addObjectData( object, vertices[ i ] ); + + } + + } else if ( useFaces === true ) { + + geometry = object.geometry; + faces = geometry.faces; + + for ( i = 0, l = faces.length; i < l; i ++ ) { + + this.addObjectData( object, faces[ i ] ); + + } + + } else { + + this.addObjectData( object ); + + } + + } + + }, + + addObjectData: function ( object, part ) { + + var objectData = new THREE.OctreeObjectData( object, part ); + + // add to tree objects data list + + this.objectsData.push( objectData ); + + // add to nodes + + this.root.addObject( objectData ); + + }, + + remove: function ( object ) { + + var i, l, + objectData = object, + index, + objectsDataRemoved; + + // ensure object is not object data for index search + + if ( object instanceof THREE.OctreeObjectData ) { + + object = object.object; + + } + + // check uuid + + if ( this.objectsMap[ object.uuid ] ) { + + this.objectsMap[ object.uuid ] = undefined; + + // check and remove from objects, nodes, and data lists + + index = indexOfValue( this.objects, object ); + + if ( index !== - 1 ) { + + this.objects.splice( index, 1 ); + + // remove from nodes + + objectsDataRemoved = this.root.removeObject( objectData ); + + // remove from objects data list + + for ( i = 0, l = objectsDataRemoved.length; i < l; i ++ ) { + + objectData = objectsDataRemoved[ i ]; + + index = indexOfValue( this.objectsData, objectData ); + + if ( index !== - 1 ) { + + this.objectsData.splice( index, 1 ); + + } + + } + + } + + } else if ( this.objectsDeferred.length > 0 ) { + + // check and remove from deferred + + index = indexOfPropertyWithValue( this.objectsDeferred, 'object', object ); + + if ( index !== - 1 ) { + + this.objectsDeferred.splice( index, 1 ); + + } + + } + + }, + + extend: function ( octree ) { + + var i, l, + objectsData, + objectData; + + if ( octree instanceof THREE.Octree ) { + + // for each object data + + objectsData = octree.objectsData; + + for ( i = 0, l = objectsData.length; i < l; i ++ ) { + + objectData = objectsData[ i ]; + + this.add( objectData, { useFaces: objectData.faces, useVertices: objectData.vertices } ); + + } + + } + + }, + + rebuild: function () { + + var i, l, + node, + object, + objectData, + indexOctant, + indexOctantLast, + objectsUpdate = []; + + // check all object data for changes in position + // assumes all object matrices are up to date + + for ( i = 0, l = this.objectsData.length; i < l; i ++ ) { + + objectData = this.objectsData[ i ]; + + node = objectData.node; + + // update object + + objectData.update(); + + // if position has changed since last organization of object in tree + + if ( node instanceof THREE.OctreeNode && ! objectData.positionLast.equals( objectData.position ) ) { + + // get octant index of object within current node + + indexOctantLast = objectData.indexOctant; + + indexOctant = node.getOctantIndex( objectData ); + + // if object octant index has changed + + if ( indexOctant !== indexOctantLast ) { + + // add to update list + + objectsUpdate.push( objectData ); + + } + + } + + } + + // update changed objects + + for ( i = 0, l = objectsUpdate.length; i < l; i ++ ) { + + objectData = objectsUpdate[ i ]; + + // remove object from current node + + objectData.node.removeObject( objectData ); + + // add object to tree root + + this.root.addObject( objectData ); + + } + + }, + + updateObject: function ( object ) { + + var i, l, + parentCascade = [ object ], + parent, + parentUpdate; + + // search all parents between object and root for world matrix update + + parent = object.parent; + + while ( parent ) { + + parentCascade.push( parent ); + parent = parent.parent; + + } + + for ( i = 0, l = parentCascade.length; i < l; i ++ ) { + + parent = parentCascade[ i ]; + + if ( parent.matrixWorldNeedsUpdate === true ) { + + parentUpdate = parent; + + } + + } + + // update world matrix starting at uppermost parent that needs update + + if ( typeof parentUpdate !== 'undefined' ) { + + parentUpdate.updateMatrixWorld(); + + } + + }, + + search: function ( position, radius, organizeByObject, direction ) { + + var i, l, + node, + objects, + objectData, + object, + results, + resultData, + resultsObjectsIndices, + resultObjectIndex, + directionPct; + + // add root objects + + objects = [].concat( this.root.objects ); + + // ensure radius (i.e. distance of ray) is a number + + if ( ! ( radius > 0 ) ) { + + radius = Number.MAX_VALUE; + + } + + // if direction passed, normalize and find pct + + if ( direction instanceof THREE.Vector3 ) { + + direction = this.utilVec31Search.copy( direction ).normalize(); + directionPct = this.utilVec32Search.set( 1, 1, 1 ).divide( direction ); + + } + + // search each node of root + + for ( i = 0, l = this.root.nodesIndices.length; i < l; i ++ ) { + + node = this.root.nodesByIndex[ this.root.nodesIndices[ i ] ]; + + objects = node.search( position, radius, objects, direction, directionPct ); + + } + + // if should organize results by object + + if ( organizeByObject === true ) { + + results = []; + resultsObjectsIndices = []; + + // for each object data found + + for ( i = 0, l = objects.length; i < l; i ++ ) { + + objectData = objects[ i ]; + object = objectData.object; + + resultObjectIndex = indexOfValue( resultsObjectsIndices, object ); + + // if needed, create new result data + + if ( resultObjectIndex === - 1 ) { + + resultData = { + object: object, + faces: [], + vertices: [] + }; + + results.push( resultData ); + + resultsObjectsIndices.push( object ); + + } else { + + resultData = results[ resultObjectIndex ]; + + } + + // object data has faces or vertices, add to list + + if ( objectData.faces ) { + + resultData.faces.push( objectData.faces ); + + } else if ( objectData.vertices ) { + + resultData.vertices.push( objectData.vertices ); + + } + + } + + } else { + + results = objects; + + } + + return results; + + }, + + setRoot: function ( root ) { + + if ( root instanceof THREE.OctreeNode ) { + + // store new root + + this.root = root; + + // update properties + + this.root.updateProperties(); + + } + + }, + + getDepthEnd: function () { + + return this.root.getDepthEnd(); + + }, + + getNodeCountEnd: function () { + + return this.root.getNodeCountEnd(); + + }, + + getObjectCountEnd: function () { + + return this.root.getObjectCountEnd(); + + }, + + toConsole: function () { + + this.root.toConsole(); + + } + + }; + + /*=================================================== + + object data + + =====================================================*/ + + THREE.OctreeObjectData = function ( object, part ) { + + // properties + + this.object = object; + + // handle part by type + + if ( part instanceof THREE.Face3 ) { + + this.faces = part; + this.face3 = true; + this.utilVec31FaceBounds = new THREE.Vector3(); + + } else if ( part instanceof THREE.Vector3 ) { + + this.vertices = part; + + } + + this.radius = 0; + this.position = new THREE.Vector3(); + + // initial update + + if ( this.object instanceof THREE.Object3D ) { + + this.update(); + + } + + this.positionLast = this.position.clone(); + + }; + + THREE.OctreeObjectData.prototype = { + + update: function () { + + if ( this.face3 ) { + + this.radius = this.getFace3BoundingRadius( this.object, this.faces ); + this.position.copy( this.faces.centroid ).applyMatrix4( this.object.matrixWorld ); + + } else if ( this.vertices ) { + + this.radius = this.object.material.size || 1; + this.position.copy( this.vertices ).applyMatrix4( this.object.matrixWorld ); + + } else { + + if ( this.object.geometry ) { + + if ( this.object.geometry.boundingSphere === null ) { + + this.object.geometry.computeBoundingSphere(); + + } + + this.radius = this.object.geometry.boundingSphere.radius; + this.position.copy( this.object.geometry.boundingSphere.center ).applyMatrix4( this.object.matrixWorld ); + + } else { + + this.radius = this.object.boundRadius; + this.position.setFromMatrixPosition( this.object.matrixWorld ); + + } + + } + + this.radius = this.radius * Math.max( this.object.scale.x, this.object.scale.y, this.object.scale.z ); + + }, + + getFace3BoundingRadius: function ( object, face ) { + + if ( face.centroid === undefined ) face.centroid = new THREE.Vector3(); + + var geometry = object.geometry || object, + vertices = geometry.vertices, + centroid = face.centroid, + va = vertices[ face.a ], vb = vertices[ face.b ], vc = vertices[ face.c ], + centroidToVert = this.utilVec31FaceBounds, + radius; + + centroid.addVectors( va, vb ).add( vc ).divideScalar( 3 ); + radius = Math.max( centroidToVert.subVectors( centroid, va ).length(), centroidToVert.subVectors( centroid, vb ).length(), centroidToVert.subVectors( centroid, vc ).length() ); + + return radius; + + } + + }; + + /*=================================================== + + node + + =====================================================*/ + + THREE.OctreeNode = function ( parameters ) { + + // utility + + this.utilVec31Branch = new THREE.Vector3(); + this.utilVec31Expand = new THREE.Vector3(); + this.utilVec31Ray = new THREE.Vector3(); + + // handle parameters + + parameters = parameters || {}; + + // store or create tree + + if ( parameters.tree instanceof THREE.Octree ) { + + this.tree = parameters.tree; + + } else if ( parameters.parent instanceof THREE.OctreeNode !== true ) { + + parameters.root = this; + + this.tree = new THREE.Octree( parameters ); + + } + + // basic properties + + this.id = this.tree.nodeCount ++; + this.position = parameters.position instanceof THREE.Vector3 ? parameters.position : new THREE.Vector3(); + this.radius = parameters.radius > 0 ? parameters.radius : 1; + this.indexOctant = parameters.indexOctant; + this.depth = 0; + + // reset and assign parent + + this.reset(); + this.setParent( parameters.parent ); + + // additional properties + + this.overlap = this.radius * this.tree.overlapPct; + this.radiusOverlap = this.radius + this.overlap; + this.left = this.position.x - this.radiusOverlap; + this.right = this.position.x + this.radiusOverlap; + this.bottom = this.position.y - this.radiusOverlap; + this.top = this.position.y + this.radiusOverlap; + this.back = this.position.z - this.radiusOverlap; + this.front = this.position.z + this.radiusOverlap; + + // visual + + if ( this.tree.scene ) { + + this.visual = new THREE.Mesh( this.tree.visualGeometry, this.tree.visualMaterial ); + this.visual.scale.set( this.radiusOverlap * 2, this.radiusOverlap * 2, this.radiusOverlap * 2 ); + this.visual.position.copy( this.position ); + this.tree.scene.add( this.visual ); + + } + + }; + + THREE.OctreeNode.prototype = { + + setParent: function ( parent ) { + + // store new parent + + if ( parent !== this && this.parent !== parent ) { + + this.parent = parent; + + // update properties + + this.updateProperties(); + + } + + }, + + updateProperties: function () { + + var i, l; + + // properties + + if ( this.parent instanceof THREE.OctreeNode ) { + + this.tree = this.parent.tree; + this.depth = this.parent.depth + 1; + + } else { + + this.depth = 0; + + } + + // cascade + + for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) { + + this.nodesByIndex[ this.nodesIndices[ i ] ].updateProperties(); + + } + + }, + + reset: function ( cascade, removeVisual ) { + + var i, l, + node, + nodesIndices = this.nodesIndices || [], + nodesByIndex = this.nodesByIndex; + + this.objects = []; + this.nodesIndices = []; + this.nodesByIndex = {}; + + // unset parent in nodes + + for ( i = 0, l = nodesIndices.length; i < l; i ++ ) { + + node = nodesByIndex[ nodesIndices[ i ] ]; + + node.setParent( undefined ); + + if ( cascade === true ) { + + node.reset( cascade, removeVisual ); + + } + + } + + // visual + + if ( removeVisual === true && this.visual && this.visual.parent ) { + + this.visual.parent.remove( this.visual ); + + } + + }, + + addNode: function ( node, indexOctant ) { + + node.indexOctant = indexOctant; + + if ( indexOfValue( this.nodesIndices, indexOctant ) === - 1 ) { + + this.nodesIndices.push( indexOctant ); + + } + + this.nodesByIndex[ indexOctant ] = node; + + if ( node.parent !== this ) { + + node.setParent( this ); + + } + + }, + + removeNode: function ( indexOctant ) { + + var index, + node; + + index = indexOfValue( this.nodesIndices, indexOctant ); + + this.nodesIndices.splice( index, 1 ); + + node = node || this.nodesByIndex[ indexOctant ]; + + delete this.nodesByIndex[ indexOctant ]; + + if ( node.parent === this ) { + + node.setParent( undefined ); + + } + + }, + + addObject: function ( object ) { + + var index, + indexOctant, + node; + + // get object octant index + + indexOctant = this.getOctantIndex( object ); + + // if object fully contained by an octant, add to subtree + if ( indexOctant > - 1 && this.nodesIndices.length > 0 ) { + + node = this.branch( indexOctant ); + + node.addObject( object ); + + } else if ( indexOctant < - 1 && this.parent instanceof THREE.OctreeNode ) { + + // if object lies outside bounds, add to parent node + + this.parent.addObject( object ); + + } else { + + // add to this objects list + + index = indexOfValue( this.objects, object ); + + if ( index === - 1 ) { + + this.objects.push( object ); + + } + + // node reference + + object.node = this; + + // check if need to expand, split, or both + + this.checkGrow(); + + } + + }, + + addObjectWithoutCheck: function ( objects ) { + + var i, l, + object; + + for ( i = 0, l = objects.length; i < l; i ++ ) { + + object = objects[ i ]; + + this.objects.push( object ); + + object.node = this; + + } + + }, + + removeObject: function ( object ) { + + var i, l, + nodesRemovedFrom, + removeData; + + // cascade through tree to find and remove object + + removeData = this.removeObjectRecursive( object, { searchComplete: false, nodesRemovedFrom: [], objectsDataRemoved: [] } ); + + // if object removed, try to shrink the nodes it was removed from + + nodesRemovedFrom = removeData.nodesRemovedFrom; + + if ( nodesRemovedFrom.length > 0 ) { + + for ( i = 0, l = nodesRemovedFrom.length; i < l; i ++ ) { + + nodesRemovedFrom[ i ].shrink(); + + } + + } + + return removeData.objectsDataRemoved; + + }, + + removeObjectRecursive: function ( object, removeData ) { + + var i, l, + index = - 1, + objectData, + node, + objectRemoved; + + // find index of object in objects list + + // search and remove object data (fast) + if ( object instanceof THREE.OctreeObjectData ) { + + // remove from this objects list + + index = indexOfValue( this.objects, object ); + + if ( index !== - 1 ) { + + this.objects.splice( index, 1 ); + object.node = undefined; + + removeData.objectsDataRemoved.push( object ); + + removeData.searchComplete = objectRemoved = true; + + } + + } else { + + // search each object data for object and remove (slow) + + for ( i = this.objects.length - 1; i >= 0; i -- ) { + + objectData = this.objects[ i ]; + + if ( objectData.object === object ) { + + this.objects.splice( i, 1 ); + objectData.node = undefined; + + removeData.objectsDataRemoved.push( objectData ); + + objectRemoved = true; + + if ( ! objectData.faces && ! objectData.vertices ) { + + removeData.searchComplete = true; + break; + + } + + } + + } + + } + + // if object data removed and this is not on nodes removed from + + if ( objectRemoved === true ) { + + removeData.nodesRemovedFrom.push( this ); + + } + + // if search not complete, search nodes + + if ( removeData.searchComplete !== true ) { + + for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) { + + node = this.nodesByIndex[ this.nodesIndices[ i ] ]; + + // try removing object from node + + removeData = node.removeObjectRecursive( object, removeData ); + + if ( removeData.searchComplete === true ) { + + break; + + } + + } + + } + + return removeData; + + }, + + checkGrow: function () { + + // if object count above max + + if ( this.objects.length > this.tree.objectsThreshold && this.tree.objectsThreshold > 0 ) { + + this.grow(); + + } + + }, + + grow: function () { + + var indexOctant, + object, + objectsExpand = [], + objectsExpandOctants = [], + objectsSplit = [], + objectsSplitOctants = [], + objectsRemaining = [], + i, l; + + // for each object + + for ( i = 0, l = this.objects.length; i < l; i ++ ) { + + object = this.objects[ i ]; + + // get object octant index + + indexOctant = this.getOctantIndex( object ); + + // if lies within octant + if ( indexOctant > - 1 ) { + + objectsSplit.push( object ); + objectsSplitOctants.push( indexOctant ); + + } else if ( indexOctant < - 1 ) { + + // lies outside radius + + objectsExpand.push( object ); + objectsExpandOctants.push( indexOctant ); + + } else { + + // lies across bounds between octants + + objectsRemaining.push( object ); + + } + + } + + // if has objects to split + + if ( objectsSplit.length > 0 ) { + + objectsRemaining = objectsRemaining.concat( this.split( objectsSplit, objectsSplitOctants ) ); + + } + + // if has objects to expand + + if ( objectsExpand.length > 0 ) { + + objectsRemaining = objectsRemaining.concat( this.expand( objectsExpand, objectsExpandOctants ) ); + + } + + // store remaining + + this.objects = objectsRemaining; + + // merge check + + this.checkMerge(); + + }, + + split: function ( objects, octants ) { + + var i, l, + indexOctant, + object, + node, + objectsRemaining; + + // if not at max depth + + if ( this.depth < this.tree.depthMax ) { + + objects = objects || this.objects; + + octants = octants || []; + + objectsRemaining = []; + + // for each object + + for ( i = 0, l = objects.length; i < l; i ++ ) { + + object = objects[ i ]; + + // get object octant index + + indexOctant = octants[ i ]; + + // if object contained by octant, branch this tree + + if ( indexOctant > - 1 ) { + + node = this.branch( indexOctant ); + + node.addObject( object ); + + } else { + + objectsRemaining.push( object ); + + } + + } + + // if all objects, set remaining as new objects + + if ( objects === this.objects ) { + + this.objects = objectsRemaining; + + } + + } else { + + objectsRemaining = this.objects; + + } + + return objectsRemaining; + + }, + + branch: function ( indexOctant ) { + + var node, + overlap, + radius, + radiusOffset, + offset, + position; + + // node exists + + if ( this.nodesByIndex[ indexOctant ] instanceof THREE.OctreeNode ) { + + node = this.nodesByIndex[ indexOctant ]; + + } else { + + // properties + + radius = ( this.radiusOverlap ) * 0.5; + overlap = radius * this.tree.overlapPct; + radiusOffset = radius - overlap; + offset = this.utilVec31Branch.set( indexOctant & 1 ? radiusOffset : - radiusOffset, indexOctant & 2 ? radiusOffset : - radiusOffset, indexOctant & 4 ? radiusOffset : - radiusOffset ); + position = new THREE.Vector3().addVectors( this.position, offset ); + + // node + + node = new THREE.OctreeNode( { + tree: this.tree, + parent: this, + position: position, + radius: radius, + indexOctant: indexOctant + } ); + + // store + + this.addNode( node, indexOctant ); + + } + + return node; + + }, + + expand: function ( objects, octants ) { + + var i, l, + object, + objectsRemaining, + objectsExpand, + indexOctant, + flagsOutside, + indexOutside, + indexOctantInverse, + iom = this.tree.INDEX_OUTSIDE_MAP, + indexOutsideCounts, + infoIndexOutside1, + infoIndexOutside2, + infoIndexOutside3, + indexOutsideBitwise1, + indexOutsideBitwise2, + infoPotential1, + infoPotential2, + infoPotential3, + indexPotentialBitwise1, + indexPotentialBitwise2, + octantX, octantY, octantZ, + overlap, + radius, + radiusOffset, + radiusParent, + overlapParent, + offset = this.utilVec31Expand, + position, + parent; + + // handle max depth down tree + + if ( this.tree.root.getDepthEnd() < this.tree.depthMax ) { + + objects = objects || this.objects; + octants = octants || []; + + objectsRemaining = []; + objectsExpand = []; + + // reset counts + + for ( i = 0, l = iom.length; i < l; i ++ ) { + + iom[ i ].count = 0; + + } + + // for all outside objects, find outside octants containing most objects + + for ( i = 0, l = objects.length; i < l; i ++ ) { + + object = objects[ i ]; + + // get object octant index + + indexOctant = octants[ i ] ; + + // if object outside this, include in calculations + + if ( indexOctant < - 1 ) { + + // convert octant index to outside flags + + flagsOutside = - indexOctant - this.tree.INDEX_OUTSIDE_OFFSET; + + // check against bitwise flags + + // x + + if ( flagsOutside & this.tree.FLAG_POS_X ) { + + iom[ this.tree.INDEX_OUTSIDE_POS_X ].count ++; + + } else if ( flagsOutside & this.tree.FLAG_NEG_X ) { + + iom[ this.tree.INDEX_OUTSIDE_NEG_X ].count ++; + + } + + // y + + if ( flagsOutside & this.tree.FLAG_POS_Y ) { + + iom[ this.tree.INDEX_OUTSIDE_POS_Y ].count ++; + + } else if ( flagsOutside & this.tree.FLAG_NEG_Y ) { + + iom[ this.tree.INDEX_OUTSIDE_NEG_Y ].count ++; + + } + + // z + + if ( flagsOutside & this.tree.FLAG_POS_Z ) { + + iom[ this.tree.INDEX_OUTSIDE_POS_Z ].count ++; + + } else if ( flagsOutside & this.tree.FLAG_NEG_Z ) { + + iom[ this.tree.INDEX_OUTSIDE_NEG_Z ].count ++; + + } + + // store in expand list + + objectsExpand.push( object ); + + } else { + + objectsRemaining.push( object ); + + } + + } + + // if objects to expand + + if ( objectsExpand.length > 0 ) { + + // shallow copy index outside map + + indexOutsideCounts = iom.slice( 0 ); + + // sort outside index count so highest is first + + indexOutsideCounts.sort( function ( a, b ) { + + return b.count - a.count; + + } ); + + // get highest outside indices + + // first is first + infoIndexOutside1 = indexOutsideCounts[ 0 ]; + indexOutsideBitwise1 = infoIndexOutside1.index | 1; + + // second is ( one of next two bitwise OR 1 ) that is not opposite of ( first bitwise OR 1 ) + + infoPotential1 = indexOutsideCounts[ 1 ]; + infoPotential2 = indexOutsideCounts[ 2 ]; + + infoIndexOutside2 = ( infoPotential1.index | 1 ) !== indexOutsideBitwise1 ? infoPotential1 : infoPotential2; + indexOutsideBitwise2 = infoIndexOutside2.index | 1; + + // third is ( one of next three bitwise OR 1 ) that is not opposite of ( first or second bitwise OR 1 ) + + infoPotential1 = indexOutsideCounts[ 2 ]; + infoPotential2 = indexOutsideCounts[ 3 ]; + infoPotential3 = indexOutsideCounts[ 4 ]; + + indexPotentialBitwise1 = infoPotential1.index | 1; + indexPotentialBitwise2 = infoPotential2.index | 1; + + infoIndexOutside3 = indexPotentialBitwise1 !== indexOutsideBitwise1 && indexPotentialBitwise1 !== indexOutsideBitwise2 ? infoPotential1 : indexPotentialBitwise2 !== indexOutsideBitwise1 && indexPotentialBitwise2 !== indexOutsideBitwise2 ? infoPotential2 : infoPotential3; + + // get this octant normal based on outside octant indices + + octantX = infoIndexOutside1.x + infoIndexOutside2.x + infoIndexOutside3.x; + octantY = infoIndexOutside1.y + infoIndexOutside2.y + infoIndexOutside3.y; + octantZ = infoIndexOutside1.z + infoIndexOutside2.z + infoIndexOutside3.z; + + // get this octant indices based on octant normal + + indexOctant = this.getOctantIndexFromPosition( octantX, octantY, octantZ ); + indexOctantInverse = this.getOctantIndexFromPosition( - octantX, - octantY, - octantZ ); + + // properties + + overlap = this.overlap; + radius = this.radius; + + // radius of parent comes from reversing overlap of this, unless overlap percent is 0 + + radiusParent = this.tree.overlapPct > 0 ? overlap / ( ( 0.5 * this.tree.overlapPct ) * ( 1 + this.tree.overlapPct ) ) : radius * 2; + overlapParent = radiusParent * this.tree.overlapPct; + + // parent offset is difference between radius + overlap of parent and child + + radiusOffset = ( radiusParent + overlapParent ) - ( radius + overlap ); + offset.set( indexOctant & 1 ? radiusOffset : - radiusOffset, indexOctant & 2 ? radiusOffset : - radiusOffset, indexOctant & 4 ? radiusOffset : - radiusOffset ); + position = new THREE.Vector3().addVectors( this.position, offset ); + + // parent + + parent = new THREE.OctreeNode( { + tree: this.tree, + position: position, + radius: radiusParent + } ); + + // set self as node of parent + + parent.addNode( this, indexOctantInverse ); + + // set parent as root + + this.tree.setRoot( parent ); + + // add all expand objects to parent + + for ( i = 0, l = objectsExpand.length; i < l; i ++ ) { + + this.tree.root.addObject( objectsExpand[ i ] ); + + } + + } + + // if all objects, set remaining as new objects + + if ( objects === this.objects ) { + + this.objects = objectsRemaining; + + } + + } else { + + objectsRemaining = objects; + + } + + return objectsRemaining; + + }, + + shrink: function () { + + // merge check + + this.checkMerge(); + + // contract check + + this.tree.root.checkContract(); + + }, + + checkMerge: function () { + + var nodeParent = this, + nodeMerge; + + // traverse up tree as long as node + entire subtree's object count is under minimum + + while ( nodeParent.parent instanceof THREE.OctreeNode && nodeParent.getObjectCountEnd() < this.tree.objectsThreshold ) { + + nodeMerge = nodeParent; + nodeParent = nodeParent.parent; + + } + + // if parent node is not this, merge entire subtree into merge node + + if ( nodeParent !== this ) { + + nodeParent.merge( nodeMerge ); + + } + + }, + + merge: function ( nodes ) { + + var i, l, + j, k, + node; + + // handle nodes + + nodes = toArray( nodes ); + + for ( i = 0, l = nodes.length; i < l; i ++ ) { + + node = nodes[ i ]; + + // gather node + all subtree objects + + this.addObjectWithoutCheck( node.getObjectsEnd() ); + + // reset node + entire subtree + + node.reset( true, true ); + + // remove node + + this.removeNode( node.indexOctant, node ); + + } + + // merge check + + this.checkMerge(); + + }, + + checkContract: function () { + + var i, l, + node, + nodeObjectsCount, + nodeHeaviest, + nodeHeaviestObjectsCount, + outsideHeaviestObjectsCount; + + // find node with highest object count + + if ( this.nodesIndices.length > 0 ) { + + nodeHeaviestObjectsCount = 0; + outsideHeaviestObjectsCount = this.objects.length; + + for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) { + + node = this.nodesByIndex[ this.nodesIndices[ i ] ]; + + nodeObjectsCount = node.getObjectCountEnd(); + outsideHeaviestObjectsCount += nodeObjectsCount; + + if ( nodeHeaviest instanceof THREE.OctreeNode === false || nodeObjectsCount > nodeHeaviestObjectsCount ) { + + nodeHeaviest = node; + nodeHeaviestObjectsCount = nodeObjectsCount; + + } + + } + + // subtract heaviest count from outside count + + outsideHeaviestObjectsCount -= nodeHeaviestObjectsCount; + + // if should contract + + if ( outsideHeaviestObjectsCount < this.tree.objectsThreshold && nodeHeaviest instanceof THREE.OctreeNode ) { + + this.contract( nodeHeaviest ); + + } + + } + + }, + + contract: function ( nodeRoot ) { + + var i, l, + node; + + // handle all nodes + + for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) { + + node = this.nodesByIndex[ this.nodesIndices[ i ] ]; + + // if node is not new root + + if ( node !== nodeRoot ) { + + // add node + all subtree objects to root + + nodeRoot.addObjectWithoutCheck( node.getObjectsEnd() ); + + // reset node + entire subtree + + node.reset( true, true ); + + } + + } + + // add own objects to root + + nodeRoot.addObjectWithoutCheck( this.objects ); + + // reset self + + this.reset( false, true ); + + // set new root + + this.tree.setRoot( nodeRoot ); + + // contract check on new root + + nodeRoot.checkContract(); + + }, + + getOctantIndex: function ( objectData ) { + + var i, l, + positionObj, + radiusObj, + position = this.position, + radiusOverlap = this.radiusOverlap, + overlap = this.overlap, + deltaX, deltaY, deltaZ, + distX, distY, distZ, + distance, + indexOctant = 0; + + // handle type + + if ( objectData instanceof THREE.OctreeObjectData ) { + + radiusObj = objectData.radius; + + positionObj = objectData.position; + + // update object data position last + + objectData.positionLast.copy( positionObj ); + + } else if ( objectData instanceof THREE.OctreeNode ) { + + positionObj = objectData.position; + + radiusObj = 0; + + } + + // find delta and distance + + deltaX = positionObj.x - position.x; + deltaY = positionObj.y - position.y; + deltaZ = positionObj.z - position.z; + + distX = Math.abs( deltaX ); + distY = Math.abs( deltaY ); + distZ = Math.abs( deltaZ ); + distance = Math.max( distX, distY, distZ ); + + // if outside, use bitwise flags to indicate on which sides object is outside of + + if ( distance + radiusObj > radiusOverlap ) { + + // x + + if ( distX + radiusObj > radiusOverlap ) { + + indexOctant = indexOctant ^ ( deltaX > 0 ? this.tree.FLAG_POS_X : this.tree.FLAG_NEG_X ); + + } + + // y + + if ( distY + radiusObj > radiusOverlap ) { + + indexOctant = indexOctant ^ ( deltaY > 0 ? this.tree.FLAG_POS_Y : this.tree.FLAG_NEG_Y ); + + } + + // z + + if ( distZ + radiusObj > radiusOverlap ) { + + indexOctant = indexOctant ^ ( deltaZ > 0 ? this.tree.FLAG_POS_Z : this.tree.FLAG_NEG_Z ); + + } + + objectData.indexOctant = - indexOctant - this.tree.INDEX_OUTSIDE_OFFSET; + + return objectData.indexOctant; + + } + + // return octant index from delta xyz + + if ( deltaX - radiusObj > - overlap ) { + + // x right + + indexOctant = indexOctant | 1; + + } else if ( ! ( deltaX + radiusObj < overlap ) ) { + + // x left + + objectData.indexOctant = this.tree.INDEX_INSIDE_CROSS; + return objectData.indexOctant; + + } + + if ( deltaY - radiusObj > - overlap ) { + + // y right + + indexOctant = indexOctant | 2; + + } else if ( ! ( deltaY + radiusObj < overlap ) ) { + + // y left + + objectData.indexOctant = this.tree.INDEX_INSIDE_CROSS; + return objectData.indexOctant; + + } + + + if ( deltaZ - radiusObj > - overlap ) { + + // z right + + indexOctant = indexOctant | 4; + + } else if ( ! ( deltaZ + radiusObj < overlap ) ) { + + // z left + + objectData.indexOctant = this.tree.INDEX_INSIDE_CROSS; + return objectData.indexOctant; + + } + + objectData.indexOctant = indexOctant; + return objectData.indexOctant; + + }, + + getOctantIndexFromPosition: function ( x, y, z ) { + + var indexOctant = 0; + + if ( x > 0 ) { + + indexOctant = indexOctant | 1; + + } + + if ( y > 0 ) { + + indexOctant = indexOctant | 2; + + } + + if ( z > 0 ) { + + indexOctant = indexOctant | 4; + + } + + return indexOctant; + + }, + + search: function ( position, radius, objects, direction, directionPct ) { + + var i, l, + node, + intersects; + + // test intersects by parameters + + if ( direction ) { + + intersects = this.intersectRay( position, direction, radius, directionPct ); + + } else { + + intersects = this.intersectSphere( position, radius ); + + } + + // if intersects + + if ( intersects === true ) { + + // gather objects + + objects = objects.concat( this.objects ); + + // search subtree + + for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) { + + node = this.nodesByIndex[ this.nodesIndices[ i ] ]; + + objects = node.search( position, radius, objects, direction ); + + } + + } + + return objects; + + }, + + intersectSphere: function ( position, radius ) { + + var distance = radius * radius, + px = position.x, + py = position.y, + pz = position.z; + + if ( px < this.left ) { + + distance -= Math.pow( px - this.left, 2 ); + + } else if ( px > this.right ) { + + distance -= Math.pow( px - this.right, 2 ); + + } + + if ( py < this.bottom ) { + + distance -= Math.pow( py - this.bottom, 2 ); + + } else if ( py > this.top ) { + + distance -= Math.pow( py - this.top, 2 ); + + } + + if ( pz < this.back ) { + + distance -= Math.pow( pz - this.back, 2 ); + + } else if ( pz > this.front ) { + + distance -= Math.pow( pz - this.front, 2 ); + + } + + return distance >= 0; + + }, + + intersectRay: function ( origin, direction, distance, directionPct ) { + + if ( typeof directionPct === 'undefined' ) { + + directionPct = this.utilVec31Ray.set( 1, 1, 1 ).divide( direction ); + + } + + var t1 = ( this.left - origin.x ) * directionPct.x, + t2 = ( this.right - origin.x ) * directionPct.x, + t3 = ( this.bottom - origin.y ) * directionPct.y, + t4 = ( this.top - origin.y ) * directionPct.y, + t5 = ( this.back - origin.z ) * directionPct.z, + t6 = ( this.front - origin.z ) * directionPct.z, + tmax = Math.min( Math.min( Math.max( t1, t2 ), Math.max( t3, t4 ) ), Math.max( t5, t6 ) ), + tmin; + + // ray would intersect in reverse direction, i.e. this is behind ray + if ( tmax < 0 ) { + + return false; + + } + + tmin = Math.max( Math.max( Math.min( t1, t2 ), Math.min( t3, t4 ) ), Math.min( t5, t6 ) ); + + // if tmin > tmax or tmin > ray distance, ray doesn't intersect AABB + if ( tmin > tmax || tmin > distance ) { + + return false; + + } + + return true; + + }, + + getDepthEnd: function ( depth ) { + + var i, l, + node; + + if ( this.nodesIndices.length > 0 ) { + + for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) { + + node = this.nodesByIndex[ this.nodesIndices[ i ] ]; + + depth = node.getDepthEnd( depth ); + + } + + } else { + + depth = ! depth || this.depth > depth ? this.depth : depth; + + } + + return depth; + + }, + + getNodeCountEnd: function () { + + return this.tree.root.getNodeCountRecursive() + 1; + + }, + + getNodeCountRecursive: function () { + + var i, l, + count = this.nodesIndices.length; + + for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) { + + count += this.nodesByIndex[ this.nodesIndices[ i ] ].getNodeCountRecursive(); + + } + + return count; + + }, + + getObjectsEnd: function ( objects ) { + + var i, l, + node; + + objects = ( objects || [] ).concat( this.objects ); + + for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) { + + node = this.nodesByIndex[ this.nodesIndices[ i ] ]; + + objects = node.getObjectsEnd( objects ); + + } + + return objects; + + }, + + getObjectCountEnd: function () { + + var i, l, + count = this.objects.length; + + for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) { + + count += this.nodesByIndex[ this.nodesIndices[ i ] ].getObjectCountEnd(); + + } + + return count; + + }, + + getObjectCountStart: function () { + + var count = this.objects.length, + parent = this.parent; + + while ( parent instanceof THREE.OctreeNode ) { + + count += parent.objects.length; + parent = parent.parent; + + } + + return count; + + }, + + toConsole: function ( space ) { + + var i, l, + node, + spaceAddition = ' '; + + space = typeof space === 'string' ? space : spaceAddition; + + console.log( ( this.parent ? space + ' octree NODE > ' : ' octree ROOT > ' ), this, ' // id: ', this.id, ' // indexOctant: ', this.indexOctant, ' // position: ', this.position.x, this.position.y, this.position.z, ' // radius: ', this.radius, ' // depth: ', this.depth ); + console.log( ( this.parent ? space + ' ' : ' ' ), '+ objects ( ', this.objects.length, ' ) ', this.objects ); + console.log( ( this.parent ? space + ' ' : ' ' ), '+ children ( ', this.nodesIndices.length, ' )', this.nodesIndices, this.nodesByIndex ); + + for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) { + + node = this.nodesByIndex[ this.nodesIndices[ i ] ]; + + node.toConsole( space + spaceAddition ); + + } + + } + + }; + + /*=================================================== + + raycaster additional functionality + + =====================================================*/ + + THREE.Raycaster.prototype.intersectOctreeObject = function ( object, recursive ) { + + var intersects, + octreeObject, + facesAll, + facesSearch; + + if ( object.object instanceof THREE.Object3D ) { + + octreeObject = object; + object = octreeObject.object; + + // temporarily replace object geometry's faces with octree object faces + + facesSearch = octreeObject.faces; + facesAll = object.geometry.faces; + + if ( facesSearch.length > 0 ) { + + object.geometry.faces = facesSearch; + + } + + // intersect + + intersects = this.intersectObject( object, recursive ); + + // revert object geometry's faces + + if ( facesSearch.length > 0 ) { + + object.geometry.faces = facesAll; + + } + + } else { + + intersects = this.intersectObject( object, recursive ); + + } + + return intersects; + + }; + + THREE.Raycaster.prototype.intersectOctreeObjects = function ( objects, recursive ) { + + var i, il, + intersects = []; + + for ( i = 0, il = objects.length; i < il; i ++ ) { + + intersects = intersects.concat( this.intersectOctreeObject( objects[ i ], recursive ) ); + + } + + return intersects; + + }; + +}( THREE ) ); diff --git a/node_modules/three/examples/js/PRNG.js b/node_modules/three/examples/js/PRNG.js new file mode 100644 index 00000000..695047ec --- /dev/null +++ b/node_modules/three/examples/js/PRNG.js @@ -0,0 +1,23 @@ +// Park-Miller-Carta Pseudo-Random Number Generator +// https://github.com/pnitsch/BitmapData.js/blob/master/js/BitmapData.js + +var PRNG = function () { + + this.seed = 1; + this.next = function() { + + return ( this.gen() / 2147483647 ); + + }; + this.nextRange = function( min, max ) { + + return min + ( ( max - min ) * this.next() ) + + }; + this.gen = function() { + + return this.seed = ( this.seed * 16807 ) % 2147483647; + + }; + +}; diff --git a/node_modules/three/examples/js/ParametricGeometries.js b/node_modules/three/examples/js/ParametricGeometries.js new file mode 100644 index 00000000..d9b5ec3e --- /dev/null +++ b/node_modules/three/examples/js/ParametricGeometries.js @@ -0,0 +1,271 @@ +/* + * @author zz85 + * + * Experimenting of primitive geometry creation using Surface Parametric equations + * + */ + + +THREE.ParametricGeometries = { + + klein: function ( v, u ) { + + u *= Math.PI; + v *= 2 * Math.PI; + + u = u * 2; + var x, y, z; + if ( u < Math.PI ) { + + x = 3 * Math.cos( u ) * ( 1 + Math.sin( u ) ) + ( 2 * ( 1 - Math.cos( u ) / 2 ) ) * Math.cos( u ) * Math.cos( v ); + z = - 8 * Math.sin( u ) - 2 * ( 1 - Math.cos( u ) / 2 ) * Math.sin( u ) * Math.cos( v ); + + } else { + + x = 3 * Math.cos( u ) * ( 1 + Math.sin( u ) ) + ( 2 * ( 1 - Math.cos( u ) / 2 ) ) * Math.cos( v + Math.PI ); + z = - 8 * Math.sin( u ); + + } + + y = - 2 * ( 1 - Math.cos( u ) / 2 ) * Math.sin( v ); + + return new THREE.Vector3( x, y, z ); + + }, + + plane: function ( width, height ) { + + return function( u, v ) { + + var x = u * width; + var y = 0; + var z = v * height; + + return new THREE.Vector3( x, y, z ); + + }; + + }, + + mobius: function( u, t ) { + + // flat mobius strip + // http://www.wolframalpha.com/input/?i=M%C3%B6bius+strip+parametric+equations&lk=1&a=ClashPrefs_*Surface.MoebiusStrip.SurfaceProperty.ParametricEquations- + u = u - 0.5; + var v = 2 * Math.PI * t; + + var x, y, z; + + var a = 2; + x = Math.cos( v ) * ( a + u * Math.cos( v / 2 ) ); + y = Math.sin( v ) * ( a + u * Math.cos( v / 2 ) ); + z = u * Math.sin( v / 2 ); + return new THREE.Vector3( x, y, z ); + + }, + + mobius3d: function( u, t ) { + + // volumetric mobius strip + u *= Math.PI; + t *= 2 * Math.PI; + + u = u * 2; + var phi = u / 2; + var major = 2.25, a = 0.125, b = 0.65; + var x, y, z; + x = a * Math.cos( t ) * Math.cos( phi ) - b * Math.sin( t ) * Math.sin( phi ); + z = a * Math.cos( t ) * Math.sin( phi ) + b * Math.sin( t ) * Math.cos( phi ); + y = ( major + x ) * Math.sin( u ); + x = ( major + x ) * Math.cos( u ); + return new THREE.Vector3( x, y, z ); + + } + +}; + + +/********************************************* + * + * Parametric Replacement for TubeGeometry + * + *********************************************/ + +THREE.ParametricGeometries.TubeGeometry = function( path, segments, radius, segmentsRadius, closed, debug ) { + + this.path = path; + this.segments = segments || 64; + this.radius = radius || 1; + this.segmentsRadius = segmentsRadius || 8; + this.closed = closed || false; + if ( debug ) this.debug = new THREE.Object3D(); + + + var scope = this, + + tangent, normal, binormal, + + numpoints = this.segments + 1, + + x, y, z, tx, ty, tz, u, v, + + cx, cy, pos, pos2 = new THREE.Vector3(), + i, j, ip, jp, a, b, c, d, uva, uvb, uvc, uvd; + + var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ), + tangents = frames.tangents, + normals = frames.normals, + binormals = frames.binormals; + + // proxy internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; + + + + var ParametricTube = function( u, v ) { + + v *= 2 * Math.PI; + + i = u * ( numpoints - 1 ); + i = Math.floor( i ); + + pos = path.getPointAt( u ); + + tangent = tangents[ i ]; + normal = normals[ i ]; + binormal = binormals[ i ]; + + if ( scope.debug ) { + + scope.debug.add( new THREE.ArrowHelper( tangent, pos, radius, 0x0000ff ) ); + scope.debug.add( new THREE.ArrowHelper( normal, pos, radius, 0xff0000 ) ); + scope.debug.add( new THREE.ArrowHelper( binormal, pos, radius, 0x00ff00 ) ); + + } + + cx = - scope.radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + cy = scope.radius * Math.sin( v ); + + pos2.copy( pos ); + pos2.x += cx * normal.x + cy * binormal.x; + pos2.y += cx * normal.y + cy * binormal.y; + pos2.z += cx * normal.z + cy * binormal.z; + + return pos2.clone(); + + }; + + THREE.ParametricGeometry.call( this, ParametricTube, segments, segmentsRadius ); + +}; + +THREE.ParametricGeometries.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ParametricGeometries.TubeGeometry.prototype.constructor = THREE.ParametricGeometries.TubeGeometry; + + + /********************************************* + * + * Parametric Replacement for TorusKnotGeometry + * + *********************************************/ +THREE.ParametricGeometries.TorusKnotGeometry = function ( radius, tube, segmentsR, segmentsT, p, q, heightScale ) { + + var scope = this; + + this.radius = radius || 200; + this.tube = tube || 40; + this.segmentsR = segmentsR || 64; + this.segmentsT = segmentsT || 8; + this.p = p || 2; + this.q = q || 3; + this.heightScale = heightScale || 1; + + + var TorusKnotCurve = THREE.Curve.create( + + function() { + }, + + function( t ) { + + t *= Math.PI * 2; + + var r = 0.5; + + var tx = ( 1 + r * Math.cos( q * t ) ) * Math.cos( p * t ), + ty = ( 1 + r * Math.cos( q * t ) ) * Math.sin( p * t ), + tz = r * Math.sin( q * t ); + + return new THREE.Vector3( tx, ty * heightScale, tz ).multiplyScalar( radius ); + + } + + ); + var segments = segmentsR; + var radiusSegments = segmentsT; + var extrudePath = new TorusKnotCurve(); + + THREE.ParametricGeometries.TubeGeometry.call( this, extrudePath, segments, tube, radiusSegments, true, false ); + + +}; + +THREE.ParametricGeometries.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ParametricGeometries.TorusKnotGeometry.prototype.constructor = THREE.ParametricGeometries.TorusKnotGeometry; + + + /********************************************* + * + * Parametric Replacement for SphereGeometry + * + *********************************************/ +THREE.ParametricGeometries.SphereGeometry = function( size, u, v ) { + + function sphere( u, v ) { + + u *= Math.PI; + v *= 2 * Math.PI; + + var x = size * Math.sin( u ) * Math.cos( v ); + var y = size * Math.sin( u ) * Math.sin( v ); + var z = size * Math.cos( u ); + + + return new THREE.Vector3( x, y, z ); + + } + + THREE.ParametricGeometry.call( this, sphere, u, v, ! true ); + +}; + +THREE.ParametricGeometries.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ParametricGeometries.SphereGeometry.prototype.constructor = THREE.ParametricGeometries.SphereGeometry; + + + /********************************************* + * + * Parametric Replacement for PlaneGeometry + * + *********************************************/ + +THREE.ParametricGeometries.PlaneGeometry = function( width, depth, segmentsWidth, segmentsDepth ) { + + function plane( u, v ) { + + var x = u * width; + var y = 0; + var z = v * depth; + + return new THREE.Vector3( x, y, z ); + + } + + THREE.ParametricGeometry.call( this, plane, segmentsWidth, segmentsDepth ); + +}; + +THREE.ParametricGeometries.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ParametricGeometries.PlaneGeometry.prototype.constructor = THREE.ParametricGeometries.PlaneGeometry; diff --git a/node_modules/three/examples/js/ShaderGodRays.js b/node_modules/three/examples/js/ShaderGodRays.js new file mode 100644 index 00000000..4fc665f2 --- /dev/null +++ b/node_modules/three/examples/js/ShaderGodRays.js @@ -0,0 +1,308 @@ +/** + * @author huwb / http://huwbowles.com/ + * + * God-rays (crepuscular rays) + * + * Similar implementation to the one used by Crytek for CryEngine 2 [Sousa2008]. + * Blurs a mask generated from the depth map along radial lines emanating from the light + * source. The blur repeatedly applies a blur filter of increasing support but constant + * sample count to produce a blur filter with large support. + * + * My implementation performs 3 passes, similar to the implementation from Sousa. I found + * just 6 samples per pass produced acceptible results. The blur is applied three times, + * with decreasing filter support. The result is equivalent to a single pass with + * 6*6*6 = 216 samples. + * + * References: + * + * Sousa2008 - Crysis Next Gen Effects, GDC2008, http://www.crytek.com/sites/default/files/GDC08_SousaT_CrysisEffects.ppt + */ + +THREE.ShaderGodRays = { + + /** + * The god-ray generation shader. + * + * First pass: + * + * The input is the depth map. I found that the output from the + * THREE.MeshDepthMaterial material was directly suitable without + * requiring any treatment whatsoever. + * + * The depth map is blurred along radial lines towards the "sun". The + * output is written to a temporary render target (I used a 1/4 sized + * target). + * + * Pass two & three: + * + * The results of the previous pass are re-blurred, each time with a + * decreased distance between samples. + */ + + 'godrays_generate': { + + uniforms: { + + tInput: { + type: "t", + value: null + }, + + fStepSize: { + type: "f", + value: 1.0 + }, + + vSunPositionScreenSpace: { + type: "v2", + value: new THREE.Vector2( 0.5, 0.5 ) + } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "#define TAPS_PER_PASS 6.0", + + "varying vec2 vUv;", + + "uniform sampler2D tInput;", + + "uniform vec2 vSunPositionScreenSpace;", + "uniform float fStepSize;", // filter step size + + "void main() {", + + // delta from current pixel to "sun" position + + "vec2 delta = vSunPositionScreenSpace - vUv;", + "float dist = length( delta );", + + // Step vector (uv space) + + "vec2 stepv = fStepSize * delta / dist;", + + // Number of iterations between pixel and sun + + "float iters = dist/fStepSize;", + + "vec2 uv = vUv.xy;", + "float col = 0.0;", + + // This breaks ANGLE in Chrome 22 + // - see http://code.google.com/p/chromium/issues/detail?id=153105 + + /* + // Unrolling didnt do much on my hardware (ATI Mobility Radeon 3450), + // so i've just left the loop + + "for ( float i = 0.0; i < TAPS_PER_PASS; i += 1.0 ) {", + + // Accumulate samples, making sure we dont walk past the light source. + + // The check for uv.y < 1 would not be necessary with "border" UV wrap + // mode, with a black border colour. I don't think this is currently + // exposed by three.js. As a result there might be artifacts when the + // sun is to the left, right or bottom of screen as these cases are + // not specifically handled. + + "col += ( i <= iters && uv.y < 1.0 ? texture2D( tInput, uv ).r : 0.0 );", + "uv += stepv;", + + "}", + */ + + // Unrolling loop manually makes it work in ANGLE + + "if ( 0.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;", + "uv += stepv;", + + "if ( 1.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;", + "uv += stepv;", + + "if ( 2.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;", + "uv += stepv;", + + "if ( 3.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;", + "uv += stepv;", + + "if ( 4.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;", + "uv += stepv;", + + "if ( 5.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;", + "uv += stepv;", + + // Should technically be dividing by 'iters', but 'TAPS_PER_PASS' smooths out + // objectionable artifacts, in particular near the sun position. The side + // effect is that the result is darker than it should be around the sun, as + // TAPS_PER_PASS is greater than the number of samples actually accumulated. + // When the result is inverted (in the shader 'godrays_combine', this produces + // a slight bright spot at the position of the sun, even when it is occluded. + + "gl_FragColor = vec4( col/TAPS_PER_PASS );", + "gl_FragColor.a = 1.0;", + + "}" + + ].join( "\n" ) + + }, + + /** + * Additively applies god rays from texture tGodRays to a background (tColors). + * fGodRayIntensity attenuates the god rays. + */ + + 'godrays_combine': { + + uniforms: { + + tColors: { + type: "t", + value: null + }, + + tGodRays: { + type: "t", + value: null + }, + + fGodRayIntensity: { + type: "f", + value: 0.69 + }, + + vSunPositionScreenSpace: { + type: "v2", + value: new THREE.Vector2( 0.5, 0.5 ) + } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "varying vec2 vUv;", + + "uniform sampler2D tColors;", + "uniform sampler2D tGodRays;", + + "uniform vec2 vSunPositionScreenSpace;", + "uniform float fGodRayIntensity;", + + "void main() {", + + // Since THREE.MeshDepthMaterial renders foreground objects white and background + // objects black, the god-rays will be white streaks. Therefore value is inverted + // before being combined with tColors + + "gl_FragColor = texture2D( tColors, vUv ) + fGodRayIntensity * vec4( 1.0 - texture2D( tGodRays, vUv ).r );", + "gl_FragColor.a = 1.0;", + + "}" + + ].join( "\n" ) + + }, + + + /** + * A dodgy sun/sky shader. Makes a bright spot at the sun location. Would be + * cheaper/faster/simpler to implement this as a simple sun sprite. + */ + + 'godrays_fake_sun': { + + uniforms: { + + vSunPositionScreenSpace: { + type: "v2", + value: new THREE.Vector2( 0.5, 0.5 ) + }, + + fAspect: { + type: "f", + value: 1.0 + }, + + sunColor: { + type: "c", + value: new THREE.Color( 0xffee00 ) + }, + + bgColor: { + type: "c", + value: new THREE.Color( 0x000000 ) + } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "varying vec2 vUv;", + + "uniform vec2 vSunPositionScreenSpace;", + "uniform float fAspect;", + + "uniform vec3 sunColor;", + "uniform vec3 bgColor;", + + "void main() {", + + "vec2 diff = vUv - vSunPositionScreenSpace;", + + // Correct for aspect ratio + + "diff.x *= fAspect;", + + "float prop = clamp( length( diff ) / 0.5, 0.0, 1.0 );", + "prop = 0.35 * pow( 1.0 - prop, 3.0 );", + + "gl_FragColor.xyz = mix( sunColor, bgColor, 1.0 - prop );", + "gl_FragColor.w = 1.0;", + + "}" + + ].join( "\n" ) + + } + +}; diff --git a/node_modules/three/examples/js/ShaderSkin.js b/node_modules/three/examples/js/ShaderSkin.js new file mode 100644 index 00000000..1291e5d0 --- /dev/null +++ b/node_modules/three/examples/js/ShaderSkin.js @@ -0,0 +1,789 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + */ + + +THREE.ShaderSkin = { + + /* ------------------------------------------------------------------------------------------ + // Simple skin shader + // - per-pixel Blinn-Phong diffuse term mixed with half-Lambert wrap-around term (per color component) + // - physically based specular term (Kelemen/Szirmay-Kalos specular reflectance) + // + // - diffuse map + // - bump map + // - specular map + // - point, directional and hemisphere lights (use with "lights: true" material option) + // - fog (use with "fog: true" material option) + // - shadow maps + // + // ------------------------------------------------------------------------------------------ */ + + 'skinSimple' : { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], + + { + + "enableBump" : { type: "i", value: 0 }, + "enableSpecular": { type: "i", value: 0 }, + + "tDiffuse" : { type: "t", value: null }, + "tBeckmann" : { type: "t", value: null }, + + "diffuse": { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "specular": { type: "c", value: new THREE.Color( 0x111111 ) }, + "opacity": { type: "f", value: 1 }, + + "uRoughness": { type: "f", value: 0.15 }, + "uSpecularBrightness": { type: "f", value: 0.75 }, + + "bumpMap" : { type: "t", value: null }, + "bumpScale" : { type: "f", value: 1 }, + + "specularMap" : { type: "t", value: null }, + + "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, + + "uWrapRGB": { type: "v3", value: new THREE.Vector3( 0.75, 0.375, 0.1875 ) } + + } + + ] ), + + fragmentShader: [ + + "#define USE_BUMPMAP", + + "uniform bool enableBump;", + "uniform bool enableSpecular;", + + "uniform vec3 diffuse;", + "uniform vec3 specular;", + "uniform float opacity;", + + "uniform float uRoughness;", + "uniform float uSpecularBrightness;", + + "uniform vec3 uWrapRGB;", + + "uniform sampler2D tDiffuse;", + "uniform sampler2D tBeckmann;", + + "uniform sampler2D specularMap;", + + "varying vec3 vNormal;", + "varying vec2 vUv;", + + "uniform vec3 ambientLightColor;", + + "#if MAX_DIR_LIGHTS > 0", + + "uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];", + "uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];", + + "#endif", + + "#if MAX_HEMI_LIGHTS > 0", + + "uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];", + "uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];", + "uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];", + + "#endif", + + "#if MAX_POINT_LIGHTS > 0", + + "uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];", + "uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", + "uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", + "uniform float pointLightDecay[ MAX_POINT_LIGHTS ];", + + "#endif", + + "varying vec3 vViewPosition;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "bumpmap_pars_fragment" ], + + // Fresnel term + + "float fresnelReflectance( vec3 H, vec3 V, float F0 ) {", + + "float base = 1.0 - dot( V, H );", + "float exponential = pow( base, 5.0 );", + + "return exponential + F0 * ( 1.0 - exponential );", + + "}", + + // Kelemen/Szirmay-Kalos specular BRDF + + "float KS_Skin_Specular( vec3 N,", // Bumped surface normal + "vec3 L,", // Points to light + "vec3 V,", // Points to eye + "float m,", // Roughness + "float rho_s", // Specular brightness + ") {", + + "float result = 0.0;", + "float ndotl = dot( N, L );", + + "if( ndotl > 0.0 ) {", + + "vec3 h = L + V;", // Unnormalized half-way vector + "vec3 H = normalize( h );", + + "float ndoth = dot( N, H );", + + "float PH = pow( 2.0 * texture2D( tBeckmann, vec2( ndoth, m ) ).x, 10.0 );", + + "float F = fresnelReflectance( H, V, 0.028 );", + "float frSpec = max( PH * F / dot( h, h ), 0.0 );", + + "result = ndotl * rho_s * frSpec;", // BRDF * dot(N,L) * rho_s + + "}", + + "return result;", + + "}", + + "void main() {", + + "vec3 outgoingLight = vec3( 0.0 );", // outgoing light does not have an alpha, the surface does + "vec4 diffuseColor = vec4( diffuse, opacity );", + "vec3 shadowMask = vec3( 1.0 );", + + "vec4 colDiffuse = texture2D( tDiffuse, vUv );", + "colDiffuse.rgb *= colDiffuse.rgb;", + + "diffuseColor = diffuseColor * colDiffuse;", + + "vec3 normal = normalize( vNormal );", + "vec3 viewerDirection = normalize( vViewPosition );", + + "float specularStrength;", + + "if ( enableSpecular ) {", + + "vec4 texelSpecular = texture2D( specularMap, vUv );", + "specularStrength = texelSpecular.r;", + + "} else {", + + "specularStrength = 1.0;", + + "}", + + "#ifdef USE_BUMPMAP", + + "if ( enableBump ) normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );", + + "#endif", + + // point lights + + "vec3 totalSpecularLight = vec3( 0.0 );", + "vec3 totalDiffuseLight = vec3( 0.0 );", + + "#if MAX_POINT_LIGHTS > 0", + + "for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {", + + "vec3 lVector = pointLightPosition[ i ] + vViewPosition.xyz;", + + "float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[i] );", + + "lVector = normalize( lVector );", + + "float pointDiffuseWeightFull = max( dot( normal, lVector ), 0.0 );", + "float pointDiffuseWeightHalf = max( 0.5 * dot( normal, lVector ) + 0.5, 0.0 );", + "vec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), uWrapRGB );", + + "float pointSpecularWeight = KS_Skin_Specular( normal, lVector, viewerDirection, uRoughness, uSpecularBrightness );", + + "totalDiffuseLight += pointLightColor[ i ] * ( pointDiffuseWeight * attenuation );", + "totalSpecularLight += pointLightColor[ i ] * specular * ( pointSpecularWeight * specularStrength * attenuation );", + + "}", + + "#endif", + + // directional lights + + "#if MAX_DIR_LIGHTS > 0", + + "for( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {", + + "vec3 dirVector = directionalLightDirection[ i ];", + + "float dirDiffuseWeightFull = max( dot( normal, dirVector ), 0.0 );", + "float dirDiffuseWeightHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );", + "vec3 dirDiffuseWeight = mix( vec3 ( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), uWrapRGB );", + + "float dirSpecularWeight = KS_Skin_Specular( normal, dirVector, viewerDirection, uRoughness, uSpecularBrightness );", + + "totalDiffuseLight += directionalLightColor[ i ] * dirDiffuseWeight;", + "totalSpecularLight += directionalLightColor[ i ] * ( dirSpecularWeight * specularStrength );", + + "}", + + "#endif", + + // hemisphere lights + + "#if MAX_HEMI_LIGHTS > 0", + + "for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {", + + "vec3 lVector = hemisphereLightDirection[ i ];", + + "float dotProduct = dot( normal, lVector );", + "float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;", + + "totalDiffuseLight += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );", + + // specular (sky light) + + "float hemiSpecularWeight = 0.0;", + "hemiSpecularWeight += KS_Skin_Specular( normal, lVector, viewerDirection, uRoughness, uSpecularBrightness );", + + // specular (ground light) + + "vec3 lVectorGround = -lVector;", + "hemiSpecularWeight += KS_Skin_Specular( normal, lVectorGround, viewerDirection, uRoughness, uSpecularBrightness );", + + "vec3 hemiSpecularColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );", + + "totalSpecularLight += hemiSpecularColor * specular * ( hemiSpecularWeight * specularStrength );", + + "}", + + "#endif", + + THREE.ShaderChunk[ "shadowmap_fragment" ], + + "totalDiffuseLight *= shadowMask;", + "totalSpecularLight *= shadowMask;", + + "outgoingLight += diffuseColor.xyz * ( totalDiffuseLight + ambientLightColor * diffuse ) + totalSpecularLight;", + + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], + + "gl_FragColor = vec4( outgoingLight, diffuseColor.a );", // TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects + + + "}" + + ].join( "\n" ), + + vertexShader: [ + + "uniform vec4 offsetRepeat;", + + "varying vec3 vNormal;", + "varying vec2 vUv;", + + "varying vec3 vViewPosition;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + + "void main() {", + + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + "vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", + + "vViewPosition = -mvPosition.xyz;", + + "vNormal = normalize( normalMatrix * normal );", + + "vUv = uv * offsetRepeat.zw + offsetRepeat.xy;", + + "gl_Position = projectionMatrix * mvPosition;", + + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join( "\n" ) + + }, + + /* ------------------------------------------------------------------------------------------ + // Skin shader + // - Blinn-Phong diffuse term (using normal + diffuse maps) + // - subsurface scattering approximation by four blur layers + // - physically based specular term (Kelemen/Szirmay-Kalos specular reflectance) + // + // - point and directional lights (use with "lights: true" material option) + // + // - based on Nvidia Advanced Skin Rendering GDC 2007 presentation + // and GPU Gems 3 Chapter 14. Advanced Techniques for Realistic Real-Time Skin Rendering + // + // http://developer.download.nvidia.com/presentations/2007/gdc/Advanced_Skin.pdf + // http://http.developer.nvidia.com/GPUGems3/gpugems3_ch14.html + // ------------------------------------------------------------------------------------------ */ + + 'skin' : { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + + { + + "passID": { type: "i", value: 0 }, + + "tDiffuse" : { type: "t", value: null }, + "tNormal" : { type: "t", value: null }, + + "tBlur1" : { type: "t", value: null }, + "tBlur2" : { type: "t", value: null }, + "tBlur3" : { type: "t", value: null }, + "tBlur4" : { type: "t", value: null }, + + "tBeckmann" : { type: "t", value: null }, + + "uNormalScale": { type: "f", value: 1.0 }, + + "diffuse": { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "specular": { type: "c", value: new THREE.Color( 0x111111 ) }, + "opacity": { type: "f", value: 1 }, + + "uRoughness": { type: "f", value: 0.15 }, + "uSpecularBrightness": { type: "f", value: 0.75 } + + } + + ] ), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform vec3 specular;", + "uniform float opacity;", + + "uniform float uRoughness;", + "uniform float uSpecularBrightness;", + + "uniform int passID;", + + "uniform sampler2D tDiffuse;", + "uniform sampler2D tNormal;", + + "uniform sampler2D tBlur1;", + "uniform sampler2D tBlur2;", + "uniform sampler2D tBlur3;", + "uniform sampler2D tBlur4;", + + "uniform sampler2D tBeckmann;", + + "uniform float uNormalScale;", + + "varying vec3 vNormal;", + "varying vec2 vUv;", + + "uniform vec3 ambientLightColor;", + + "#if MAX_DIR_LIGHTS > 0", + "uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];", + "uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];", + "#endif", + + "#if MAX_POINT_LIGHTS > 0", + "uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];", + "varying vec4 vPointLight[ MAX_POINT_LIGHTS ];", + "#endif", + + "varying vec3 vViewPosition;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + + "float fresnelReflectance( vec3 H, vec3 V, float F0 ) {", + + "float base = 1.0 - dot( V, H );", + "float exponential = pow( base, 5.0 );", + + "return exponential + F0 * ( 1.0 - exponential );", + + "}", + + // Kelemen/Szirmay-Kalos specular BRDF + + "float KS_Skin_Specular( vec3 N,", // Bumped surface normal + "vec3 L,", // Points to light + "vec3 V,", // Points to eye + "float m,", // Roughness + "float rho_s", // Specular brightness + ") {", + + "float result = 0.0;", + "float ndotl = dot( N, L );", + + "if( ndotl > 0.0 ) {", + + "vec3 h = L + V;", // Unnormalized half-way vector + "vec3 H = normalize( h );", + + "float ndoth = dot( N, H );", + + "float PH = pow( 2.0 * texture2D( tBeckmann, vec2( ndoth, m ) ).x, 10.0 );", + "float F = fresnelReflectance( H, V, 0.028 );", + "float frSpec = max( PH * F / dot( h, h ), 0.0 );", + + "result = ndotl * rho_s * frSpec;", // BRDF * dot(N,L) * rho_s + + "}", + + "return result;", + + "}", + + "void main() {", + + "vec3 outgoingLight = vec3( 0.0 );", // outgoing light does not have an alpha, the surface does + "vec4 diffuseColor = vec4( diffuse, opacity );", + + "vec4 mSpecular = vec4( specular, opacity );", + + "vec4 colDiffuse = texture2D( tDiffuse, vUv );", + "colDiffuse *= colDiffuse;", + + "diffuseColor *= colDiffuse;", + + // normal mapping + + "vec4 posAndU = vec4( -vViewPosition, vUv.x );", + "vec4 posAndU_dx = dFdx( posAndU ), posAndU_dy = dFdy( posAndU );", + "vec3 tangent = posAndU_dx.w * posAndU_dx.xyz + posAndU_dy.w * posAndU_dy.xyz;", + "vec3 normal = normalize( vNormal );", + "vec3 binormal = normalize( cross( tangent, normal ) );", + "tangent = cross( normal, binormal );", // no normalization required + "mat3 tsb = mat3( tangent, binormal, normal );", + + "vec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;", + "normalTex.xy *= uNormalScale;", + "normalTex = normalize( normalTex );", + + "vec3 finalNormal = tsb * normalTex;", + "normal = normalize( finalNormal );", + + "vec3 viewerDirection = normalize( vViewPosition );", + + // point lights + + "vec3 totalDiffuseLight = vec3( 0.0 );", + "vec3 totalSpecularLight = vec3( 0.0 );", + + "#if MAX_POINT_LIGHTS > 0", + + "for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {", + + "vec3 pointVector = normalize( vPointLight[ i ].xyz );", + "float attenuation = vPointLight[ i ].w;", + + "float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );", + + "totalDiffuseLight += pointLightColor[ i ] * ( pointDiffuseWeight * attenuation );", + + "if ( passID == 1 ) {", + + "float pointSpecularWeight = KS_Skin_Specular( normal, pointVector, viewerDirection, uRoughness, uSpecularBrightness );", + + "totalSpecularLight += pointLightColor[ i ] * mSpecular.xyz * ( pointSpecularWeight * attenuation );", + + "}", + + "}", + + "#endif", + + // directional lights + + "#if MAX_DIR_LIGHTS > 0", + + "for( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {", + + "vec3 dirVector = directionalLightDirection[ i ];", + + "float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );", + + "totalDiffuseLight += directionalLightColor[ i ] * dirDiffuseWeight;", + + "if ( passID == 1 ) {", + + "float dirSpecularWeight = KS_Skin_Specular( normal, dirVector, viewerDirection, uRoughness, uSpecularBrightness );", + + "totalSpecularLight += directionalLightColor[ i ] * mSpecular.xyz * dirSpecularWeight;", + + "}", + + "}", + + "#endif", + + + "outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalSpecularLight );", + + "if ( passID == 0 ) {", + + "outgoingLight = sqrt( outgoingLight );", + + "} else if ( passID == 1 ) {", + + //"#define VERSION1", + + "#ifdef VERSION1", + + "vec3 nonblurColor = sqrt(outgoingLight );", + + "#else", + + "vec3 nonblurColor = outgoingLight;", + + "#endif", + + "vec3 blur1Color = texture2D( tBlur1, vUv ).xyz;", + "vec3 blur2Color = texture2D( tBlur2, vUv ).xyz;", + "vec3 blur3Color = texture2D( tBlur3, vUv ).xyz;", + "vec3 blur4Color = texture2D( tBlur4, vUv ).xyz;", + + + //"gl_FragColor = vec4( blur1Color, gl_FragColor.w );", + + //"gl_FragColor = vec4( vec3( 0.22, 0.5, 0.7 ) * nonblurColor + vec3( 0.2, 0.5, 0.3 ) * blur1Color + vec3( 0.58, 0.0, 0.0 ) * blur2Color, gl_FragColor.w );", + + //"gl_FragColor = vec4( vec3( 0.25, 0.6, 0.8 ) * nonblurColor + vec3( 0.15, 0.25, 0.2 ) * blur1Color + vec3( 0.15, 0.15, 0.0 ) * blur2Color + vec3( 0.45, 0.0, 0.0 ) * blur3Color, gl_FragColor.w );", + + + "outgoingLight = vec3( vec3( 0.22, 0.437, 0.635 ) * nonblurColor + ", + "vec3( 0.101, 0.355, 0.365 ) * blur1Color + ", + "vec3( 0.119, 0.208, 0.0 ) * blur2Color + ", + "vec3( 0.114, 0.0, 0.0 ) * blur3Color + ", + "vec3( 0.444, 0.0, 0.0 ) * blur4Color );", + + "outgoingLight *= sqrt( colDiffuse.xyz );", + + "outgoingLight += ambientLightColor * diffuse * colDiffuse.xyz + totalSpecularLight;", + + "#ifndef VERSION1", + + "outgoingLight = sqrt( outgoingLight );", + + "#endif", + + "}", + + THREE.ShaderChunk[ "fog_fragment" ], + + "gl_FragColor = vec4( outgoingLight, diffuseColor.a );", // TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects + + "}" + + ].join( "\n" ), + + vertexShader: [ + + "#ifdef VERTEX_TEXTURES", + + "uniform sampler2D tDisplacement;", + "uniform float uDisplacementScale;", + "uniform float uDisplacementBias;", + + "#endif", + + "varying vec3 vNormal;", + "varying vec2 vUv;", + + "#if MAX_POINT_LIGHTS > 0", + + "uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", + "uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", + "uniform float pointLightDecay[ MAX_POINT_LIGHTS ];", + + "varying vec4 vPointLight[ MAX_POINT_LIGHTS ];", + + "#endif", + + "varying vec3 vViewPosition;", + + THREE.ShaderChunk[ "common" ], + + "void main() {", + + "vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", + + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + + "vViewPosition = -mvPosition.xyz;", + + "vNormal = normalize( normalMatrix * normal );", + + "vUv = uv;", + + // point lights + + "#if MAX_POINT_LIGHTS > 0", + + "for( int i = 0; i < MAX_POINT_LIGHTS; i++ ) {", + + "vec3 lVector = pointLightPosition[ i ] - vViewPosition;", + + "float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[i] );", + + "lVector = normalize( lVector );", + + "vPointLight[ i ] = vec4( lVector, attenuation );", + + "}", + + "#endif", + + // displacement mapping + + "#ifdef VERTEX_TEXTURES", + + "vec3 dv = texture2D( tDisplacement, uv ).xyz;", + "float df = uDisplacementScale * dv.x + uDisplacementBias;", + "vec4 displacedPosition = vec4( vNormal.xyz * df, 0.0 ) + mvPosition;", + "gl_Position = projectionMatrix * displacedPosition;", + + "#else", + + "gl_Position = projectionMatrix * mvPosition;", + + "#endif", + + "}" + + ].join( "\n" ), + + vertexShaderUV: [ + + "varying vec3 vNormal;", + "varying vec2 vUv;", + + "#if MAX_POINT_LIGHTS > 0", + + "uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", + "uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", + "uniform float pointLightDecay[ MAX_POINT_LIGHTS ];", + + "varying vec4 vPointLight[ MAX_POINT_LIGHTS ];", + + "#endif", + + "varying vec3 vViewPosition;", + + THREE.ShaderChunk[ "common" ], + + "void main() {", + + "vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", + + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + + "vViewPosition = -mvPosition.xyz;", + + "vNormal = normalize( normalMatrix * normal );", + + "vUv = uv;", + + // point lights + + "#if MAX_POINT_LIGHTS > 0", + + "for( int i = 0; i < MAX_POINT_LIGHTS; i++ ) {", + + "vec3 lVector = pointLightPosition[ i ] - vViewPosition;", + + "float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[i] );", + + "lVector = normalize( lVector );", + + "vPointLight[ i ] = vec4( lVector, attenuation );", + + "}", + + "#endif", + + "gl_Position = vec4( uv.x * 2.0 - 1.0, uv.y * 2.0 - 1.0, 0.0, 1.0 );", + + "}" + + ].join( "\n" ) + + }, + + /* ------------------------------------------------------------------------------------------ + // Beckmann distribution function + // - to be used in specular term of skin shader + // - render a screen-aligned quad to precompute a 512 x 512 texture + // + // - from http://developer.nvidia.com/node/171 + ------------------------------------------------------------------------------------------ */ + + "beckmann" : { + + uniforms: {}, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "varying vec2 vUv;", + + "float PHBeckmann( float ndoth, float m ) {", + + "float alpha = acos( ndoth );", + "float ta = tan( alpha );", + + "float val = 1.0 / ( m * m * pow( ndoth, 4.0 ) ) * exp( -( ta * ta ) / ( m * m ) );", + "return val;", + + "}", + + "float KSTextureCompute( vec2 tex ) {", + + // Scale the value to fit within [0,1] invert upon lookup. + + "return 0.5 * pow( PHBeckmann( tex.x, tex.y ), 0.1 );", + + "}", + + "void main() {", + + "float x = KSTextureCompute( vUv );", + + "gl_FragColor = vec4( x, x, x, 1.0 );", + + "}" + + ].join( "\n" ) + + } + +}; diff --git a/node_modules/three/examples/js/ShaderTerrain.js b/node_modules/three/examples/js/ShaderTerrain.js new file mode 100644 index 00000000..53d6ae87 --- /dev/null +++ b/node_modules/three/examples/js/ShaderTerrain.js @@ -0,0 +1,342 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + */ + +THREE.ShaderTerrain = { + + /* ------------------------------------------------------------------------- + // Dynamic terrain shader + // - Blinn-Phong + // - height + normal + diffuse1 + diffuse2 + specular + detail maps + // - point, directional and hemisphere lights (use with "lights: true" material option) + // - shadow maps receiving + ------------------------------------------------------------------------- */ + + 'terrain' : { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], + + { + + "enableDiffuse1" : { type: "i", value: 0 }, + "enableDiffuse2" : { type: "i", value: 0 }, + "enableSpecular" : { type: "i", value: 0 }, + "enableReflection": { type: "i", value: 0 }, + + "tDiffuse1" : { type: "t", value: null }, + "tDiffuse2" : { type: "t", value: null }, + "tDetail" : { type: "t", value: null }, + "tNormal" : { type: "t", value: null }, + "tSpecular" : { type: "t", value: null }, + "tDisplacement": { type: "t", value: null }, + + "uNormalScale": { type: "f", value: 1.0 }, + + "uDisplacementBias": { type: "f", value: 0.0 }, + "uDisplacementScale": { type: "f", value: 1.0 }, + + "diffuse": { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "specular": { type: "c", value: new THREE.Color( 0x111111 ) }, + "shininess": { type: "f", value: 30 }, + "opacity": { type: "f", value: 1 }, + + "uRepeatBase" : { type: "v2", value: new THREE.Vector2( 1, 1 ) }, + "uRepeatOverlay" : { type: "v2", value: new THREE.Vector2( 1, 1 ) }, + + "uOffset" : { type: "v2", value: new THREE.Vector2( 0, 0 ) } + + } + + ] ), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform vec3 specular;", + "uniform float shininess;", + "uniform float opacity;", + + "uniform bool enableDiffuse1;", + "uniform bool enableDiffuse2;", + "uniform bool enableSpecular;", + + "uniform sampler2D tDiffuse1;", + "uniform sampler2D tDiffuse2;", + "uniform sampler2D tDetail;", + "uniform sampler2D tNormal;", + "uniform sampler2D tSpecular;", + "uniform sampler2D tDisplacement;", + + "uniform float uNormalScale;", + + "uniform vec2 uRepeatOverlay;", + "uniform vec2 uRepeatBase;", + + "uniform vec2 uOffset;", + + "varying vec3 vTangent;", + "varying vec3 vBinormal;", + "varying vec3 vNormal;", + "varying vec2 vUv;", + + "uniform vec3 ambientLightColor;", + + "#if MAX_DIR_LIGHTS > 0", + + "uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];", + "uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];", + + "#endif", + + "#if MAX_HEMI_LIGHTS > 0", + + "uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];", + "uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];", + "uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];", + + "#endif", + + "#if MAX_POINT_LIGHTS > 0", + + "uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];", + "uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", + "uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", + "uniform float pointLightDecay[ MAX_POINT_LIGHTS ];", + + "#endif", + + "varying vec3 vViewPosition;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + + "void main() {", + + "vec3 outgoingLight = vec3( 0.0 );", // outgoing light does not have an alpha, the surface does + "vec4 diffuseColor = vec4( diffuse, opacity );", + + "vec3 specularTex = vec3( 1.0 );", + + "vec2 uvOverlay = uRepeatOverlay * vUv + uOffset;", + "vec2 uvBase = uRepeatBase * vUv;", + + "vec3 normalTex = texture2D( tDetail, uvOverlay ).xyz * 2.0 - 1.0;", + "normalTex.xy *= uNormalScale;", + "normalTex = normalize( normalTex );", + + "if( enableDiffuse1 && enableDiffuse2 ) {", + + "vec4 colDiffuse1 = texture2D( tDiffuse1, uvOverlay );", + "vec4 colDiffuse2 = texture2D( tDiffuse2, uvOverlay );", + + "colDiffuse1.xyz = inputToLinear( colDiffuse1.xyz );", + "colDiffuse2.xyz = inputToLinear( colDiffuse2.xyz );", + + "diffuseColor *= mix ( colDiffuse1, colDiffuse2, 1.0 - texture2D( tDisplacement, uvBase ) );", + + " } else if( enableDiffuse1 ) {", + + "diffuseColor *= texture2D( tDiffuse1, uvOverlay );", + + "} else if( enableDiffuse2 ) {", + + "diffuseColor *= texture2D( tDiffuse2, uvOverlay );", + + "}", + + "if( enableSpecular )", + "specularTex = texture2D( tSpecular, uvOverlay ).xyz;", + + "mat3 tsb = mat3( vTangent, vBinormal, vNormal );", + "vec3 finalNormal = tsb * normalTex;", + + "vec3 normal = normalize( finalNormal );", + "vec3 viewPosition = normalize( vViewPosition );", + + "vec3 totalDiffuseLight = vec3( 0.0 );", + "vec3 totalSpecularLight = vec3( 0.0 );", + + // point lights + + "#if MAX_POINT_LIGHTS > 0", + + "for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {", + + "vec3 lVector = pointLightPosition[ i ] + vViewPosition.xyz;", + + "float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[i] );", + + "lVector = normalize( lVector );", + + "vec3 pointHalfVector = normalize( lVector + viewPosition );", + + "float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );", + "float pointDiffuseWeight = max( dot( normal, lVector ), 0.0 );", + + "float pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, shininess ), 0.0 );", + + "totalDiffuseLight += attenuation * pointLightColor[ i ] * pointDiffuseWeight;", + "totalSpecularLight += attenuation * pointLightColor[ i ] * specular * pointSpecularWeight * pointDiffuseWeight;", + + "}", + + "#endif", + + // directional lights + + "#if MAX_DIR_LIGHTS > 0", + + "vec3 dirDiffuse = vec3( 0.0 );", + "vec3 dirSpecular = vec3( 0.0 );", + + "for( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {", + + "vec3 dirVector = directionalLightDirection[ i ];", + "vec3 dirHalfVector = normalize( dirVector + viewPosition );", + + "float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );", + "float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );", + + "float dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, shininess ), 0.0 );", + + "totalDiffuseLight += directionalLightColor[ i ] * dirDiffuseWeight;", + "totalSpecularLight += directionalLightColor[ i ] * specular * dirSpecularWeight * dirDiffuseWeight;", + + "}", + + "#endif", + + // hemisphere lights + + "#if MAX_HEMI_LIGHTS > 0", + + "vec3 hemiDiffuse = vec3( 0.0 );", + "vec3 hemiSpecular = vec3( 0.0 );", + + "for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {", + + "vec3 lVector = hemisphereLightDirection[ i ];", + + // diffuse + + "float dotProduct = dot( normal, lVector );", + "float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;", + + "totalDiffuseLight += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );", + + // specular (sky light) + + "float hemiSpecularWeight = 0.0;", + + "vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );", + "float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;", + "hemiSpecularWeight += specularTex.r * max( pow( hemiDotNormalHalfSky, shininess ), 0.0 );", + + // specular (ground light) + + "vec3 lVectorGround = -lVector;", + + "vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );", + "float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;", + "hemiSpecularWeight += specularTex.r * max( pow( hemiDotNormalHalfGround, shininess ), 0.0 );", + + "totalSpecularLight += specular * mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight ) * hemiSpecularWeight * hemiDiffuseWeight;", + + "}", + + "#endif", + + "outgoingLight += diffuseColor.xyz * ( totalDiffuseLight + ambientLightColor + totalSpecularLight );", + + THREE.ShaderChunk[ "shadowmap_fragment" ], + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], + + "gl_FragColor = vec4( outgoingLight, diffuseColor.a );", // TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects + + "}" + + ].join( "\n" ), + + vertexShader: [ + + "attribute vec4 tangent;", + + "uniform vec2 uRepeatBase;", + + "uniform sampler2D tNormal;", + + "#ifdef VERTEX_TEXTURES", + + "uniform sampler2D tDisplacement;", + "uniform float uDisplacementScale;", + "uniform float uDisplacementBias;", + + "#endif", + + "varying vec3 vTangent;", + "varying vec3 vBinormal;", + "varying vec3 vNormal;", + "varying vec2 vUv;", + + "varying vec3 vViewPosition;", + + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + + "void main() {", + + "vNormal = normalize( normalMatrix * normal );", + + // tangent and binormal vectors + + "vTangent = normalize( normalMatrix * tangent.xyz );", + + "vBinormal = cross( vNormal, vTangent ) * tangent.w;", + "vBinormal = normalize( vBinormal );", + + // texture coordinates + + "vUv = uv;", + + "vec2 uvBase = uv * uRepeatBase;", + + // displacement mapping + + "#ifdef VERTEX_TEXTURES", + + "vec3 dv = texture2D( tDisplacement, uvBase ).xyz;", + "float df = uDisplacementScale * dv.x + uDisplacementBias;", + "vec3 displacedPosition = normal * df + position;", + + "vec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );", + "vec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );", + + "#else", + + "vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + + "#endif", + + "gl_Position = projectionMatrix * mvPosition;", + + "vViewPosition = -mvPosition.xyz;", + + "vec3 normalTex = texture2D( tNormal, uvBase ).xyz * 2.0 - 1.0;", + "vNormal = normalMatrix * normalTex;", + + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join( "\n" ) + + } + +}; diff --git a/node_modules/three/examples/js/ShaderToon.js b/node_modules/three/examples/js/ShaderToon.js new file mode 100644 index 00000000..3f349f93 --- /dev/null +++ b/node_modules/three/examples/js/ShaderToon.js @@ -0,0 +1,331 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * ShaderToon currently contains: + * + * toon1 + * toon2 + * hatching + * dotted + */ + +THREE.ShaderToon = { + + 'toon1' : { + + uniforms: { + + "uDirLightPos": { type: "v3", value: new THREE.Vector3() }, + "uDirLightColor": { type: "c", value: new THREE.Color( 0xeeeeee ) }, + + "uAmbientLightColor": { type: "c", value: new THREE.Color( 0x050505 ) }, + + "uBaseColor": { type: "c", value: new THREE.Color( 0xffffff ) } + + }, + + vertexShader: [ + + "varying vec3 vNormal;", + "varying vec3 vRefract;", + + "void main() {", + + "vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + "vec3 worldNormal = normalize ( mat3( modelMatrix[0].xyz, modelMatrix[1].xyz, modelMatrix[2].xyz ) * normal );", + + "vNormal = normalize( normalMatrix * normal );", + + "vec3 I = worldPosition.xyz - cameraPosition;", + "vRefract = refract( normalize( I ), worldNormal, 1.02 );", + + "gl_Position = projectionMatrix * mvPosition;", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 uBaseColor;", + + "uniform vec3 uDirLightPos;", + "uniform vec3 uDirLightColor;", + + "uniform vec3 uAmbientLightColor;", + + "varying vec3 vNormal;", + + "varying vec3 vRefract;", + + "void main() {", + + "float directionalLightWeighting = max( dot( normalize( vNormal ), uDirLightPos ), 0.0);", + "vec3 lightWeighting = uAmbientLightColor + uDirLightColor * directionalLightWeighting;", + + "float intensity = smoothstep( - 0.5, 1.0, pow( length(lightWeighting), 20.0 ) );", + "intensity += length(lightWeighting) * 0.2;", + + "float cameraWeighting = dot( normalize( vNormal ), vRefract );", + "intensity += pow( 1.0 - length( cameraWeighting ), 6.0 );", + "intensity = intensity * 0.2 + 0.3;", + + "if ( intensity < 0.50 ) {", + + "gl_FragColor = vec4( 2.0 * intensity * uBaseColor, 1.0 );", + + "} else {", + + "gl_FragColor = vec4( 1.0 - 2.0 * ( 1.0 - intensity ) * ( 1.0 - uBaseColor ), 1.0 );", + + "}", + + "}" + + ].join( "\n" ) + + }, + + 'toon2' : { + + uniforms: { + + "uDirLightPos": { type: "v3", value: new THREE.Vector3() }, + "uDirLightColor": { type: "c", value: new THREE.Color( 0xeeeeee ) }, + + "uAmbientLightColor": { type: "c", value: new THREE.Color( 0x050505 ) }, + + "uBaseColor": { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "uLineColor1": { type: "c", value: new THREE.Color( 0x808080 ) }, + "uLineColor2": { type: "c", value: new THREE.Color( 0x000000 ) }, + "uLineColor3": { type: "c", value: new THREE.Color( 0x000000 ) }, + "uLineColor4": { type: "c", value: new THREE.Color( 0x000000 ) } + + }, + + vertexShader: [ + + "varying vec3 vNormal;", + + "void main() {", + + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + "vNormal = normalize( normalMatrix * normal );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 uBaseColor;", + "uniform vec3 uLineColor1;", + "uniform vec3 uLineColor2;", + "uniform vec3 uLineColor3;", + "uniform vec3 uLineColor4;", + + "uniform vec3 uDirLightPos;", + "uniform vec3 uDirLightColor;", + + "uniform vec3 uAmbientLightColor;", + + "varying vec3 vNormal;", + + "void main() {", + + "float camera = max( dot( normalize( vNormal ), vec3( 0.0, 0.0, 1.0 ) ), 0.4);", + "float light = max( dot( normalize( vNormal ), uDirLightPos ), 0.0);", + + "gl_FragColor = vec4( uBaseColor, 1.0 );", + + "if ( length(uAmbientLightColor + uDirLightColor * light) < 1.00 ) {", + + "gl_FragColor *= vec4( uLineColor1, 1.0 );", + + "}", + + "if ( length(uAmbientLightColor + uDirLightColor * camera) < 0.50 ) {", + + "gl_FragColor *= vec4( uLineColor2, 1.0 );", + + "}", + + "}" + + ].join( "\n" ) + + }, + + 'hatching' : { + + uniforms: { + + "uDirLightPos": { type: "v3", value: new THREE.Vector3() }, + "uDirLightColor": { type: "c", value: new THREE.Color( 0xeeeeee ) }, + + "uAmbientLightColor": { type: "c", value: new THREE.Color( 0x050505 ) }, + + "uBaseColor": { type: "c", value: new THREE.Color( 0xffffff ) }, + "uLineColor1": { type: "c", value: new THREE.Color( 0x000000 ) }, + "uLineColor2": { type: "c", value: new THREE.Color( 0x000000 ) }, + "uLineColor3": { type: "c", value: new THREE.Color( 0x000000 ) }, + "uLineColor4": { type: "c", value: new THREE.Color( 0x000000 ) } + + }, + + vertexShader: [ + + "varying vec3 vNormal;", + + "void main() {", + + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + "vNormal = normalize( normalMatrix * normal );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 uBaseColor;", + "uniform vec3 uLineColor1;", + "uniform vec3 uLineColor2;", + "uniform vec3 uLineColor3;", + "uniform vec3 uLineColor4;", + + "uniform vec3 uDirLightPos;", + "uniform vec3 uDirLightColor;", + + "uniform vec3 uAmbientLightColor;", + + "varying vec3 vNormal;", + + "void main() {", + + "float directionalLightWeighting = max( dot( normalize(vNormal), uDirLightPos ), 0.0);", + "vec3 lightWeighting = uAmbientLightColor + uDirLightColor * directionalLightWeighting;", + + "gl_FragColor = vec4( uBaseColor, 1.0 );", + + "if ( length(lightWeighting) < 1.00 ) {", + + "if ( mod(gl_FragCoord.x + gl_FragCoord.y, 10.0) == 0.0) {", + + "gl_FragColor = vec4( uLineColor1, 1.0 );", + + "}", + + "}", + + "if ( length(lightWeighting) < 0.75 ) {", + + "if (mod(gl_FragCoord.x - gl_FragCoord.y, 10.0) == 0.0) {", + + "gl_FragColor = vec4( uLineColor2, 1.0 );", + + "}", + "}", + + "if ( length(lightWeighting) < 0.50 ) {", + + "if (mod(gl_FragCoord.x + gl_FragCoord.y - 5.0, 10.0) == 0.0) {", + + "gl_FragColor = vec4( uLineColor3, 1.0 );", + + "}", + "}", + + "if ( length(lightWeighting) < 0.3465 ) {", + + "if (mod(gl_FragCoord.x - gl_FragCoord.y - 5.0, 10.0) == 0.0) {", + + "gl_FragColor = vec4( uLineColor4, 1.0 );", + + "}", + "}", + + "}" + + ].join( "\n" ) + + }, + + 'dotted' : { + + uniforms: { + + "uDirLightPos": { type: "v3", value: new THREE.Vector3() }, + "uDirLightColor": { type: "c", value: new THREE.Color( 0xeeeeee ) }, + + "uAmbientLightColor": { type: "c", value: new THREE.Color( 0x050505 ) }, + + "uBaseColor": { type: "c", value: new THREE.Color( 0xffffff ) }, + "uLineColor1": { type: "c", value: new THREE.Color( 0x000000 ) } + + }, + + vertexShader: [ + + "varying vec3 vNormal;", + + "void main() {", + + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + "vNormal = normalize( normalMatrix * normal );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 uBaseColor;", + "uniform vec3 uLineColor1;", + "uniform vec3 uLineColor2;", + "uniform vec3 uLineColor3;", + "uniform vec3 uLineColor4;", + + "uniform vec3 uDirLightPos;", + "uniform vec3 uDirLightColor;", + + "uniform vec3 uAmbientLightColor;", + + "varying vec3 vNormal;", + + "void main() {", + + "float directionalLightWeighting = max( dot( normalize(vNormal), uDirLightPos ), 0.0);", + "vec3 lightWeighting = uAmbientLightColor + uDirLightColor * directionalLightWeighting;", + + "gl_FragColor = vec4( uBaseColor, 1.0 );", + + "if ( length(lightWeighting) < 1.00 ) {", + + "if ( ( mod(gl_FragCoord.x, 4.001) + mod(gl_FragCoord.y, 4.0) ) > 6.00 ) {", + + "gl_FragColor = vec4( uLineColor1, 1.0 );", + + "}", + + "}", + + "if ( length(lightWeighting) < 0.50 ) {", + + "if ( ( mod(gl_FragCoord.x + 2.0, 4.001) + mod(gl_FragCoord.y + 2.0, 4.0) ) > 6.00 ) {", + + "gl_FragColor = vec4( uLineColor1, 1.0 );", + + "}", + + "}", + + "}" + + ].join( "\n" ) + + } + +}; diff --git a/node_modules/three/examples/js/SimplexNoise.js b/node_modules/three/examples/js/SimplexNoise.js new file mode 100644 index 00000000..d3c8c53e --- /dev/null +++ b/node_modules/three/examples/js/SimplexNoise.js @@ -0,0 +1,324 @@ +// Ported from Stefan Gustavson's java implementation +// http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf +// Read Stefan's excellent paper for details on how this code works. +// +// Sean McCullough banksean@gmail.com +// +// Added 4D noise +// Joshua Koo zz85nus@gmail.com + +/** + * You can pass in a random number generator object if you like. + * It is assumed to have a random() method. + */ +var SimplexNoise = function(r) { + if (r == undefined) r = Math; + this.grad3 = [[ 1,1,0 ],[ -1,1,0 ],[ 1,-1,0 ],[ -1,-1,0 ], + [ 1,0,1 ],[ -1,0,1 ],[ 1,0,-1 ],[ -1,0,-1 ], + [ 0,1,1 ],[ 0,-1,1 ],[ 0,1,-1 ],[ 0,-1,-1 ]]; + + this.grad4 = [[ 0,1,1,1 ], [ 0,1,1,-1 ], [ 0,1,-1,1 ], [ 0,1,-1,-1 ], + [ 0,-1,1,1 ], [ 0,-1,1,-1 ], [ 0,-1,-1,1 ], [ 0,-1,-1,-1 ], + [ 1,0,1,1 ], [ 1,0,1,-1 ], [ 1,0,-1,1 ], [ 1,0,-1,-1 ], + [ -1,0,1,1 ], [ -1,0,1,-1 ], [ -1,0,-1,1 ], [ -1,0,-1,-1 ], + [ 1,1,0,1 ], [ 1,1,0,-1 ], [ 1,-1,0,1 ], [ 1,-1,0,-1 ], + [ -1,1,0,1 ], [ -1,1,0,-1 ], [ -1,-1,0,1 ], [ -1,-1,0,-1 ], + [ 1,1,1,0 ], [ 1,1,-1,0 ], [ 1,-1,1,0 ], [ 1,-1,-1,0 ], + [ -1,1,1,0 ], [ -1,1,-1,0 ], [ -1,-1,1,0 ], [ -1,-1,-1,0 ]]; + + this.p = []; + for (var i = 0; i < 256; i ++) { + this.p[i] = Math.floor(r.random() * 256); + } + // To remove the need for index wrapping, double the permutation table length + this.perm = []; + for (var i = 0; i < 512; i ++) { + this.perm[i] = this.p[i & 255]; + } + + // A lookup table to traverse the simplex around a given point in 4D. + // Details can be found where this table is used, in the 4D noise method. + this.simplex = [ + [ 0,1,2,3 ],[ 0,1,3,2 ],[ 0,0,0,0 ],[ 0,2,3,1 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 1,2,3,0 ], + [ 0,2,1,3 ],[ 0,0,0,0 ],[ 0,3,1,2 ],[ 0,3,2,1 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 1,3,2,0 ], + [ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ], + [ 1,2,0,3 ],[ 0,0,0,0 ],[ 1,3,0,2 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 2,3,0,1 ],[ 2,3,1,0 ], + [ 1,0,2,3 ],[ 1,0,3,2 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 2,0,3,1 ],[ 0,0,0,0 ],[ 2,1,3,0 ], + [ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ], + [ 2,0,1,3 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 3,0,1,2 ],[ 3,0,2,1 ],[ 0,0,0,0 ],[ 3,1,2,0 ], + [ 2,1,0,3 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 3,1,0,2 ],[ 0,0,0,0 ],[ 3,2,0,1 ],[ 3,2,1,0 ]]; +}; + +SimplexNoise.prototype.dot = function(g, x, y) { + return g[0] * x + g[1] * y; +}; + +SimplexNoise.prototype.dot3 = function(g, x, y, z) { + return g[0] * x + g[1] * y + g[2] * z; +}; + +SimplexNoise.prototype.dot4 = function(g, x, y, z, w) { + return g[0] * x + g[1] * y + g[2] * z + g[3] * w; +}; + +SimplexNoise.prototype.noise = function(xin, yin) { + var n0, n1, n2; // Noise contributions from the three corners + // Skew the input space to determine which simplex cell we're in + var F2 = 0.5 * (Math.sqrt(3.0) - 1.0); + var s = (xin + yin) * F2; // Hairy factor for 2D + var i = Math.floor(xin + s); + var j = Math.floor(yin + s); + var G2 = (3.0 - Math.sqrt(3.0)) / 6.0; + var t = (i + j) * G2; + var X0 = i - t; // Unskew the cell origin back to (x,y) space + var Y0 = j - t; + var x0 = xin - X0; // The x,y distances from the cell origin + var y0 = yin - Y0; + // For the 2D case, the simplex shape is an equilateral triangle. + // Determine which simplex we are in. + var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords + if (x0 > y0) {i1 = 1; j1 = 0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1) + else {i1 = 0; j1 = 1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1) + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords + var y1 = y0 - j1 + G2; + var x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords + var y2 = y0 - 1.0 + 2.0 * G2; + // Work out the hashed gradient indices of the three simplex corners + var ii = i & 255; + var jj = j & 255; + var gi0 = this.perm[ii + this.perm[jj]] % 12; + var gi1 = this.perm[ii + i1 + this.perm[jj + j1]] % 12; + var gi2 = this.perm[ii + 1 + this.perm[jj + 1]] % 12; + // Calculate the contribution from the three corners + var t0 = 0.5 - x0 * x0 - y0 * y0; + if (t0 < 0) n0 = 0.0; + else { + t0 *= t0; + n0 = t0 * t0 * this.dot(this.grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient + } + var t1 = 0.5 - x1 * x1 - y1 * y1; + if (t1 < 0) n1 = 0.0; + else { + t1 *= t1; + n1 = t1 * t1 * this.dot(this.grad3[gi1], x1, y1); + } + var t2 = 0.5 - x2 * x2 - y2 * y2; + if (t2 < 0) n2 = 0.0; + else { + t2 *= t2; + n2 = t2 * t2 * this.dot(this.grad3[gi2], x2, y2); + } + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 70.0 * (n0 + n1 + n2); +}; + +// 3D simplex noise +SimplexNoise.prototype.noise3d = function(xin, yin, zin) { + var n0, n1, n2, n3; // Noise contributions from the four corners + // Skew the input space to determine which simplex cell we're in + var F3 = 1.0 / 3.0; + var s = (xin + yin + zin) * F3; // Very nice and simple skew factor for 3D + var i = Math.floor(xin + s); + var j = Math.floor(yin + s); + var k = Math.floor(zin + s); + var G3 = 1.0 / 6.0; // Very nice and simple unskew factor, too + var t = (i + j + k) * G3; + var X0 = i - t; // Unskew the cell origin back to (x,y,z) space + var Y0 = j - t; + var Z0 = k - t; + var x0 = xin - X0; // The x,y,z distances from the cell origin + var y0 = yin - Y0; + var z0 = zin - Z0; + // For the 3D case, the simplex shape is a slightly irregular tetrahedron. + // Determine which simplex we are in. + var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords + var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords + if (x0 >= y0) { + if (y0 >= z0) + { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; } // X Y Z order + else if (x0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; } // X Z Y order + else { i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; } // Z X Y order + } + else { // x0 y0) ? 32 : 0; + var c2 = (x0 > z0) ? 16 : 0; + var c3 = (y0 > z0) ? 8 : 0; + var c4 = (x0 > w0) ? 4 : 0; + var c5 = (y0 > w0) ? 2 : 0; + var c6 = (z0 > w0) ? 1 : 0; + var c = c1 + c2 + c3 + c4 + c5 + c6; + var i1, j1, k1, l1; // The integer offsets for the second simplex corner + var i2, j2, k2, l2; // The integer offsets for the third simplex corner + var i3, j3, k3, l3; // The integer offsets for the fourth simplex corner + // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. + // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0; + j1 = simplex[c][1] >= 3 ? 1 : 0; + k1 = simplex[c][2] >= 3 ? 1 : 0; + l1 = simplex[c][3] >= 3 ? 1 : 0; + // The number 2 in the "simplex" array is at the second largest coordinate. + i2 = simplex[c][0] >= 2 ? 1 : 0; + j2 = simplex[c][1] >= 2 ? 1 : 0; k2 = simplex[c][2] >= 2 ? 1 : 0; + l2 = simplex[c][3] >= 2 ? 1 : 0; + // The number 1 in the "simplex" array is at the second smallest coordinate. + i3 = simplex[c][0] >= 1 ? 1 : 0; + j3 = simplex[c][1] >= 1 ? 1 : 0; + k3 = simplex[c][2] >= 1 ? 1 : 0; + l3 = simplex[c][3] >= 1 ? 1 : 0; + // The fifth corner has all coordinate offsets = 1, so no need to look that up. + var x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords + var y1 = y0 - j1 + G4; + var z1 = z0 - k1 + G4; + var w1 = w0 - l1 + G4; + var x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords + var y2 = y0 - j2 + 2.0 * G4; + var z2 = z0 - k2 + 2.0 * G4; + var w2 = w0 - l2 + 2.0 * G4; + var x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords + var y3 = y0 - j3 + 3.0 * G4; + var z3 = z0 - k3 + 3.0 * G4; + var w3 = w0 - l3 + 3.0 * G4; + var x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords + var y4 = y0 - 1.0 + 4.0 * G4; + var z4 = z0 - 1.0 + 4.0 * G4; + var w4 = w0 - 1.0 + 4.0 * G4; + // Work out the hashed gradient indices of the five simplex corners + var ii = i & 255; + var jj = j & 255; + var kk = k & 255; + var ll = l & 255; + var gi0 = perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32; + var gi1 = perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32; + var gi2 = perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32; + var gi3 = perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32; + var gi4 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32; + // Calculate the contribution from the five corners + var t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0; + if (t0 < 0) n0 = 0.0; + else { + t0 *= t0; + n0 = t0 * t0 * this.dot4(grad4[gi0], x0, y0, z0, w0); + } + var t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1; + if (t1 < 0) n1 = 0.0; + else { + t1 *= t1; + n1 = t1 * t1 * this.dot4(grad4[gi1], x1, y1, z1, w1); + } + var t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2; + if (t2 < 0) n2 = 0.0; + else { + t2 *= t2; + n2 = t2 * t2 * this.dot4(grad4[gi2], x2, y2, z2, w2); + } var t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3; + if (t3 < 0) n3 = 0.0; + else { + t3 *= t3; + n3 = t3 * t3 * this.dot4(grad4[gi3], x3, y3, z3, w3); + } + var t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4; + if (t4 < 0) n4 = 0.0; + else { + t4 *= t4; + n4 = t4 * t4 * this.dot4(grad4[gi4], x4, y4, z4, w4); + } + // Sum up and scale the result to cover the range [-1,1] + return 27.0 * (n0 + n1 + n2 + n3 + n4); +}; diff --git a/node_modules/three/examples/js/SimulationRenderer.js b/node_modules/three/examples/js/SimulationRenderer.js new file mode 100644 index 00000000..ef2ff978 --- /dev/null +++ b/node_modules/three/examples/js/SimulationRenderer.js @@ -0,0 +1,235 @@ +/** + * @author zz85 https://github.com/zz85 / http://www.lab4games.net/zz85/blog + * + * Bird Simulation Render + * + * A simple scene rendering a quad of the following shaders + * 1. Pass-thru Shader, + * 2. Bird Position Update Shader, + * 3. Bird Velocity Update Shader + * + */ + +function SimulationRenderer( WIDTH, renderer ) { + + WIDTH = WIDTH || 4; + var camera = new THREE.Camera(); + camera.position.z = 1; + + // Init RTT stuff + gl = renderer.getContext(); + + if ( ! gl.getExtension( "OES_texture_float" ) ) { + + alert( "No OES_texture_float support for float textures!" ); + return; + + } + + if ( gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) == 0 ) { + + alert( "No support for vertex shader textures!" ); + return; + + } + + var scene = new THREE.Scene(); + + var uniforms = { + time: { type: "f", value: 1.0 }, + resolution: { type: "v2", value: new THREE.Vector2( WIDTH, WIDTH ) }, + texture: { type: "t", value: null } + }; + + var passThruShader = new THREE.ShaderMaterial( { + uniforms: uniforms, + vertexShader: document.getElementById( 'vertexShader' ).textContent, + fragmentShader: document.getElementById( 'fragmentShader' ).textContent + } ); + + var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), passThruShader ); + + var positionShader = new THREE.ShaderMaterial( { + + uniforms: { + time: { type: "f", value: 1.0 }, + delta: { type: "f", value: 0.0 }, + resolution: { type: "v2", value: new THREE.Vector2( WIDTH, WIDTH ) }, + texturePosition: { type: "t", value: null }, + textureVelocity: { type: "t", value: null }, + }, + vertexShader: document.getElementById( 'vertexShader' ).textContent, + fragmentShader: document.getElementById( 'fragmentShaderPosition' ).textContent + + } ); + + this.positionShader = positionShader; + + var velocityShader = new THREE.ShaderMaterial( { + + uniforms: { + time: { type: "f", value: 1.0 }, + delta: { type: "f", value: 0.0 }, + resolution: { type: "v2", value: new THREE.Vector2( WIDTH, WIDTH ) }, + texturePosition: { type: "t", value: null }, + textureVelocity: { type: "t", value: null }, + testing: { type: "f", value: 1.0 }, + seperationDistance: { type: "f", value: 1.0 }, + alignmentDistance: { type: "f", value: 1.0 }, + cohesionDistance: { type: "f", value: 1.0 }, + freedomFactor: { type: "f", value: 1.0 }, + predator: { type: "v3", value: new THREE.Vector3() } + }, + defines: { + WIDTH: WIDTH.toFixed( 2 ) + }, + vertexShader: document.getElementById( 'vertexShader' ).textContent, + fragmentShader: document.getElementById( 'fragmentShaderVelocity' ).textContent + + } ); + + this.velocityUniforms = velocityShader.uniforms; + + scene.add( mesh ); + + var flipflop = true; + var rtPosition1, rtPosition2, rtVelocity1, rtVelocity2; + + function init() { + + var dtPosition = generatePositionTexture(); + var dtVelocity = generateVelocityTexture(); + + rtPosition1 = getRenderTarget( THREE.RGBAFormat ); + rtPosition2 = rtPosition1.clone(); + rtVelocity1 = getRenderTarget( THREE.RGBFormat ); + rtVelocity2 = rtVelocity1.clone(); + + simulator.renderTexture( dtPosition, rtPosition1 ); + simulator.renderTexture( rtPosition1, rtPosition2 ); + + simulator.renderTexture( dtVelocity, rtVelocity1 ); + simulator.renderTexture( rtVelocity1, rtVelocity2 ); + + simulator.velocityUniforms.testing.value = 10; + + } + + this.init = init; + + function getRenderTarget( type ) { + + var renderTarget = new THREE.WebGLRenderTarget( WIDTH, WIDTH, { + wrapS: THREE.RepeatWrapping, + wrapT: THREE.RepeatWrapping, + minFilter: THREE.NearestFilter, + magFilter: THREE.NearestFilter, + format: type, + type: THREE.FloatType, + stencilBuffer: false + } ); + + return renderTarget; + + } + + // Takes a texture, and render out as another texture + this.renderTexture = function ( input, output ) { + + mesh.material = passThruShader; + uniforms.texture.value = input; + renderer.render( scene, camera, output ); + + }; + + + this.renderPosition = function( position, velocity, output, delta ) { + + mesh.material = positionShader; + positionShader.uniforms.texturePosition.value = position; + positionShader.uniforms.textureVelocity.value = velocity; + positionShader.uniforms.time.value = performance.now(); + positionShader.uniforms.delta.value = delta; + renderer.render( scene, camera, output ); + this.currentPosition = output; + + }; + + this.renderVelocity = function( position, velocity, output, delta ) { + + mesh.material = velocityShader; + velocityShader.uniforms.texturePosition.value = position; + velocityShader.uniforms.textureVelocity.value = velocity; + velocityShader.uniforms.time.value = performance.now(); + velocityShader.uniforms.delta.value = delta; + renderer.render( scene, camera, output ); + this.currentVelocity = output; + + }; + + this.simulate = function( delta ) { + + if ( flipflop ) { + + simulator.renderVelocity( rtPosition1, rtVelocity1, rtVelocity2, delta ); + simulator.renderPosition( rtPosition1, rtVelocity2, rtPosition2, delta ); + + } else { + + simulator.renderVelocity( rtPosition2, rtVelocity2, rtVelocity1, delta ); + simulator.renderPosition( rtPosition2, rtVelocity1, rtPosition1, delta ); + + } + + flipflop = ! flipflop; + + }; + + function generatePositionTexture() { + + var a = new Float32Array( PARTICLES * 4 ); + + for ( var k = 0, kl = a.length; k < kl; k += 4 ) { + + var x = Math.random() * BOUNDS - BOUNDS_HALF; + var y = Math.random() * BOUNDS - BOUNDS_HALF; + var z = Math.random() * BOUNDS - BOUNDS_HALF; + + a[ k + 0 ] = x; + a[ k + 1 ] = y; + a[ k + 2 ] = z; + a[ k + 3 ] = 1; + + } + + var texture = new THREE.DataTexture( a, WIDTH, WIDTH, THREE.RGBAFormat, THREE.FloatType ); + texture.needsUpdate = true; + + return texture; + + } + + function generateVelocityTexture() { + + var a = new Float32Array( PARTICLES * 3 ); + + for ( var k = 0, kl = a.length; k < kl; k += 3 ) { + + var x = Math.random() - 0.5; + var y = Math.random() - 0.5; + var z = Math.random() - 0.5; + + a[ k + 0 ] = x * 10; + a[ k + 1 ] = y * 10; + a[ k + 2 ] = z * 10; + + } + + var texture = new THREE.DataTexture( a, WIDTH, WIDTH, THREE.RGBFormat, THREE.FloatType ); + texture.needsUpdate = true; + + return texture; + + } + +} diff --git a/node_modules/three/examples/js/SkyShader.js b/node_modules/three/examples/js/SkyShader.js new file mode 100644 index 00000000..b3295b62 --- /dev/null +++ b/node_modules/three/examples/js/SkyShader.js @@ -0,0 +1,261 @@ +/** + * @author zz85 / https://github.com/zz85 + * + * Based on "A Practical Analytic Model for Daylight" + * aka The Preetham Model, the de facto standard analytic skydome model + * http://www.cs.utah.edu/~shirley/papers/sunsky/sunsky.pdf + * + * First implemented by Simon Wallner + * http://www.simonwallner.at/projects/atmospheric-scattering + * + * Improved by Martin Upitis + * http://blenderartists.org/forum/showthread.php?245954-preethams-sky-impementation-HDR + * + * Three.js integration by zz85 http://twitter.com/blurspline +*/ + +THREE.ShaderLib[ 'sky' ] = { + + uniforms: { + + luminance: { type: "f", value: 1 }, + turbidity: { type: "f", value: 2 }, + reileigh: { type: "f", value: 1 }, + mieCoefficient: { type: "f", value: 0.005 }, + mieDirectionalG: { type: "f", value: 0.8 }, + sunPosition: { type: "v3", value: new THREE.Vector3() } + + }, + + vertexShader: [ + + "varying vec3 vWorldPosition;", + + "void main() {", + + "vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", + "vWorldPosition = worldPosition.xyz;", + + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}", + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D skySampler;", + "uniform vec3 sunPosition;", + "varying vec3 vWorldPosition;", + + "vec3 cameraPos = vec3(0., 0., 0.);", + "// uniform sampler2D sDiffuse;", + "// const float turbidity = 10.0; //", + "// const float reileigh = 2.; //", + "// const float luminance = 1.0; //", + "// const float mieCoefficient = 0.005;", + "// const float mieDirectionalG = 0.8;", + + "uniform float luminance;", + "uniform float turbidity;", + "uniform float reileigh;", + "uniform float mieCoefficient;", + "uniform float mieDirectionalG;", + + "// constants for atmospheric scattering", + "const float e = 2.71828182845904523536028747135266249775724709369995957;", + "const float pi = 3.141592653589793238462643383279502884197169;", + + "const float n = 1.0003; // refractive index of air", + "const float N = 2.545E25; // number of molecules per unit volume for air at", + "// 288.15K and 1013mb (sea level -45 celsius)", + "const float pn = 0.035; // depolatization factor for standard air", + + "// wavelength of used primaries, according to preetham", + "const vec3 lambda = vec3(680E-9, 550E-9, 450E-9);", + + "// mie stuff", + "// K coefficient for the primaries", + "const vec3 K = vec3(0.686, 0.678, 0.666);", + "const float v = 4.0;", + + "// optical length at zenith for molecules", + "const float rayleighZenithLength = 8.4E3;", + "const float mieZenithLength = 1.25E3;", + "const vec3 up = vec3(0.0, 1.0, 0.0);", + + "const float EE = 1000.0;", + "const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324;", + "// 66 arc seconds -> degrees, and the cosine of that", + + "// earth shadow hack", + "const float cutoffAngle = pi/1.95;", + "const float steepness = 1.5;", + + + "vec3 totalRayleigh(vec3 lambda)", + "{", + "return (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn));", + "}", + + // see http://blenderartists.org/forum/showthread.php?321110-Shaders-and-Skybox-madness + "// A simplied version of the total Reayleigh scattering to works on browsers that use ANGLE", + "vec3 simplifiedRayleigh()", + "{", + "return 0.0005 / vec3(94, 40, 18);", + // return 0.00054532832366 / (3.0 * 2.545E25 * pow(vec3(680E-9, 550E-9, 450E-9), vec3(4.0)) * 6.245); + "}", + + "float rayleighPhase(float cosTheta)", + "{ ", + "return (3.0 / (16.0*pi)) * (1.0 + pow(cosTheta, 2.0));", + "// return (1.0 / (3.0*pi)) * (1.0 + pow(cosTheta, 2.0));", + "// return (3.0 / 4.0) * (1.0 + pow(cosTheta, 2.0));", + "}", + + "vec3 totalMie(vec3 lambda, vec3 K, float T)", + "{", + "float c = (0.2 * T ) * 10E-18;", + "return 0.434 * c * pi * pow((2.0 * pi) / lambda, vec3(v - 2.0)) * K;", + "}", + + "float hgPhase(float cosTheta, float g)", + "{", + "return (1.0 / (4.0*pi)) * ((1.0 - pow(g, 2.0)) / pow(1.0 - 2.0*g*cosTheta + pow(g, 2.0), 1.5));", + "}", + + "float sunIntensity(float zenithAngleCos)", + "{", + "return EE * max(0.0, 1.0 - exp(-((cutoffAngle - acos(zenithAngleCos))/steepness)));", + "}", + + "// float logLuminance(vec3 c)", + "// {", + "// return log(c.r * 0.2126 + c.g * 0.7152 + c.b * 0.0722);", + "// }", + + "// Filmic ToneMapping http://filmicgames.com/archives/75", + "float A = 0.15;", + "float B = 0.50;", + "float C = 0.10;", + "float D = 0.20;", + "float E = 0.02;", + "float F = 0.30;", + "float W = 1000.0;", + + "vec3 Uncharted2Tonemap(vec3 x)", + "{", + "return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;", + "}", + + + "void main() ", + "{", + "float sunfade = 1.0-clamp(1.0-exp((sunPosition.y/450000.0)),0.0,1.0);", + + "// luminance = 1.0 ;// vWorldPosition.y / 450000. + 0.5; //sunPosition.y / 450000. * 1. + 0.5;", + + "// gl_FragColor = vec4(sunfade, sunfade, sunfade, 1.0);", + + "float reileighCoefficient = reileigh - (1.0* (1.0-sunfade));", + + "vec3 sunDirection = normalize(sunPosition);", + + "float sunE = sunIntensity(dot(sunDirection, up));", + + "// extinction (absorbtion + out scattering) ", + "// rayleigh coefficients", + + // "vec3 betaR = totalRayleigh(lambda) * reileighCoefficient;", + "vec3 betaR = simplifiedRayleigh() * reileighCoefficient;", + + "// mie coefficients", + "vec3 betaM = totalMie(lambda, K, turbidity) * mieCoefficient;", + + "// optical length", + "// cutoff angle at 90 to avoid singularity in next formula.", + "float zenithAngle = acos(max(0.0, dot(up, normalize(vWorldPosition - cameraPos))));", + "float sR = rayleighZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253));", + "float sM = mieZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253));", + + + + "// combined extinction factor ", + "vec3 Fex = exp(-(betaR * sR + betaM * sM));", + + "// in scattering", + "float cosTheta = dot(normalize(vWorldPosition - cameraPos), sunDirection);", + + "float rPhase = rayleighPhase(cosTheta*0.5+0.5);", + "vec3 betaRTheta = betaR * rPhase;", + + "float mPhase = hgPhase(cosTheta, mieDirectionalG);", + "vec3 betaMTheta = betaM * mPhase;", + + + "vec3 Lin = pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * (1.0 - Fex),vec3(1.5));", + "Lin *= mix(vec3(1.0),pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * Fex,vec3(1.0/2.0)),clamp(pow(1.0-dot(up, sunDirection),5.0),0.0,1.0));", + + "//nightsky", + "vec3 direction = normalize(vWorldPosition - cameraPos);", + "float theta = acos(direction.y); // elevation --> y-axis, [-pi/2, pi/2]", + "float phi = atan(direction.z, direction.x); // azimuth --> x-axis [-pi/2, pi/2]", + "vec2 uv = vec2(phi, theta) / vec2(2.0*pi, pi) + vec2(0.5, 0.0);", + "// vec3 L0 = texture2D(skySampler, uv).rgb+0.1 * Fex;", + "vec3 L0 = vec3(0.1) * Fex;", + + "// composition + solar disc", + "//if (cosTheta > sunAngularDiameterCos)", + "float sundisk = smoothstep(sunAngularDiameterCos,sunAngularDiameterCos+0.00002,cosTheta);", + "// if (normalize(vWorldPosition - cameraPos).y>0.0)", + "L0 += (sunE * 19000.0 * Fex)*sundisk;", + + + "vec3 whiteScale = 1.0/Uncharted2Tonemap(vec3(W));", + + "vec3 texColor = (Lin+L0); ", + "texColor *= 0.04 ;", + "texColor += vec3(0.0,0.001,0.0025)*0.3;", + + "float g_fMaxLuminance = 1.0;", + "float fLumScaled = 0.1 / luminance; ", + "float fLumCompressed = (fLumScaled * (1.0 + (fLumScaled / (g_fMaxLuminance * g_fMaxLuminance)))) / (1.0 + fLumScaled); ", + + "float ExposureBias = fLumCompressed;", + + "vec3 curr = Uncharted2Tonemap((log2(2.0/pow(luminance,4.0)))*texColor);", + "vec3 color = curr*whiteScale;", + + "vec3 retColor = pow(color,vec3(1.0/(1.2+(1.2*sunfade))));", + + + "gl_FragColor.rgb = retColor;", + + "gl_FragColor.a = 1.0;", + "}", + + ].join( "\n" ) + +}; + +THREE.Sky = function () { + + var skyShader = THREE.ShaderLib[ "sky" ]; + var skyUniforms = THREE.UniformsUtils.clone( skyShader.uniforms ); + + var skyMat = new THREE.ShaderMaterial( { + fragmentShader: skyShader.fragmentShader, + vertexShader: skyShader.vertexShader, + uniforms: skyUniforms, + side: THREE.BackSide + } ); + + var skyGeo = new THREE.SphereBufferGeometry( 450000, 32, 15 ); + var skyMesh = new THREE.Mesh( skyGeo, skyMat ); + + + // Expose variables + this.mesh = skyMesh; + this.uniforms = skyUniforms; + +}; diff --git a/node_modules/three/examples/js/TypedArrayUtils.js b/node_modules/three/examples/js/TypedArrayUtils.js new file mode 100644 index 00000000..9c4c0bf4 --- /dev/null +++ b/node_modules/three/examples/js/TypedArrayUtils.js @@ -0,0 +1,602 @@ + +THREE.TypedArrayUtils = {}; + +/** + * In-place quicksort for typed arrays (e.g. for Float32Array) + * provides fast sorting + * useful e.g. for a custom shader and/or BufferGeometry + * + * @author Roman Bolzern , 2013 + * @author I4DS http://www.fhnw.ch/i4ds, 2013 + * @license MIT License + * + * Complexity: http://bigocheatsheet.com/ see Quicksort + * + * Example: + * points: [x, y, z, x, y, z, x, y, z, ...] + * eleSize: 3 //because of (x, y, z) + * orderElement: 0 //order according to x + */ + +THREE.TypedArrayUtils.quicksortIP = function ( arr, eleSize, orderElement ) { + + var stack = []; + var sp = - 1; + var left = 0; + var right = arr.length / eleSize - 1; + var tmp = 0.0, x = 0, y = 0; + + var swapF = function ( a, b ) { + + a *= eleSize; b *= eleSize; + + for ( y = 0; y < eleSize; y ++ ) { + + tmp = arr[ a + y ]; + arr[ a + y ] = arr[ b + y ]; + arr[ b + y ] = tmp; + + } + + }; + + var i, j, swap = new Float32Array( eleSize ), temp = new Float32Array( eleSize ); + + while ( true ) { + + if ( right - left <= 25 ) { + + for ( j = left + 1; j <= right; j ++ ) { + + for ( x = 0; x < eleSize; x ++ ) { + + swap[ x ] = arr[ j * eleSize + x ]; + + } + + i = j - 1; + + while ( i >= left && arr[ i * eleSize + orderElement ] > swap[ orderElement ] ) { + + for ( x = 0; x < eleSize; x ++ ) { + + arr[ ( i + 1 ) * eleSize + x ] = arr[ i * eleSize + x ]; + + } + + i --; + + } + + for ( x = 0; x < eleSize; x ++ ) { + + arr[ ( i + 1 ) * eleSize + x ] = swap[ x ]; + + } + + } + + if ( sp == - 1 ) break; + + right = stack[ sp -- ]; //? + left = stack[ sp -- ]; + + } else { + + var median = ( left + right ) >> 1; + + i = left + 1; + j = right; + + swapF( median, i ); + + if ( arr[ left * eleSize + orderElement ] > arr[ right * eleSize + orderElement ] ) { + + swapF( left, right ); + + } + + if ( arr[ i * eleSize + orderElement ] > arr[ right * eleSize + orderElement ] ) { + + swapF( i, right ); + + } + + if ( arr[ left * eleSize + orderElement ] > arr[ i * eleSize + orderElement ] ) { + + swapF( left, i ); + + } + + for ( x = 0; x < eleSize; x ++ ) { + + temp[ x ] = arr[ i * eleSize + x ]; + + } + + while ( true ) { + + do i ++; while ( arr[ i * eleSize + orderElement ] < temp[ orderElement ] ); + do j --; while ( arr[ j * eleSize + orderElement ] > temp[ orderElement ] ); + + if ( j < i ) break; + + swapF( i, j ); + + } + + for ( x = 0; x < eleSize; x ++ ) { + + arr[ ( left + 1 ) * eleSize + x ] = arr[ j * eleSize + x ]; + arr[ j * eleSize + x ] = temp[ x ]; + + } + + if ( right - i + 1 >= j - left ) { + + stack[ ++ sp ] = i; + stack[ ++ sp ] = right; + right = j - 1; + + } else { + + stack[ ++ sp ] = left; + stack[ ++ sp ] = j - 1; + left = i; + + } + + } + + } + + return arr; + +}; + + + +/** + * k-d Tree for typed arrays (e.g. for Float32Array), in-place + * provides fast nearest neighbour search + * useful e.g. for a custom shader and/or BufferGeometry, saves tons of memory + * has no insert and remove, only buildup and neares neighbour search + * + * Based on https://github.com/ubilabs/kd-tree-javascript by Ubilabs + * + * @author Roman Bolzern , 2013 + * @author I4DS http://www.fhnw.ch/i4ds, 2013 + * @license MIT License + * + * Requires typed array quicksort + * + * Example: + * points: [x, y, z, x, y, z, x, y, z, ...] + * metric: function(a, b){ return Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2) + Math.pow(a[2] - b[2], 2); } //Manhatten distance + * eleSize: 3 //because of (x, y, z) + * + * Further information (including mathematical properties) + * http://en.wikipedia.org/wiki/Binary_tree + * http://en.wikipedia.org/wiki/K-d_tree + * + * If you want to further minimize memory usage, remove Node.depth and replace in search algorithm with a traversal to root node (see comments at THREE.TypedArrayUtils.Kdtree.prototype.Node) + */ + + THREE.TypedArrayUtils.Kdtree = function ( points, metric, eleSize ) { + + var self = this; + + var maxDepth = 0; + + var getPointSet = function ( points, pos ) { + + return points.subarray( pos * eleSize, pos * eleSize + eleSize ); + + }; + + function buildTree( points, depth, parent, pos ) { + + var dim = depth % eleSize, + median, + node, + plength = points.length / eleSize; + + if ( depth > maxDepth ) maxDepth = depth; + + if ( plength === 0 ) return null; + if ( plength === 1 ) { + + return new self.Node( getPointSet( points, 0 ), depth, parent, pos ); + + } + + THREE.TypedArrayUtils.quicksortIP( points, eleSize, dim ); + + median = Math.floor( plength / 2 ); + + node = new self.Node( getPointSet( points, median ), depth, parent, median + pos ); + node.left = buildTree( points.subarray( 0, median * eleSize ), depth + 1, node, pos ); + node.right = buildTree( points.subarray( ( median + 1 ) * eleSize, points.length ), depth + 1, node, pos + median + 1 ); + + return node; + + } + + this.root = buildTree( points, 0, null, 0 ); + + this.getMaxDepth = function () { + + return maxDepth; + + }; + + this.nearest = function ( point, maxNodes, maxDistance ) { + + /* point: array of size eleSize + maxNodes: max amount of nodes to return + maxDistance: maximum distance to point result nodes should have + condition (not implemented): function to test node before it's added to the result list, e.g. test for view frustum + */ + + var i, + result, + bestNodes; + + bestNodes = new THREE.TypedArrayUtils.Kdtree.BinaryHeap( + + function ( e ) { + + return - e[ 1 ]; + + } + + ); + + function nearestSearch( node ) { + + var bestChild, + dimension = node.depth % eleSize, + ownDistance = metric( point, node.obj ), + linearDistance = 0, + otherChild, + i, + linearPoint = []; + + function saveNode( node, distance ) { + + bestNodes.push( [ node, distance ] ); + + if ( bestNodes.size() > maxNodes ) { + + bestNodes.pop(); + + } + + } + + for ( i = 0; i < eleSize; i += 1 ) { + + if ( i === node.depth % eleSize ) { + + linearPoint[ i ] = point[ i ]; + + } else { + + linearPoint[ i ] = node.obj[ i ]; + + } + + } + + linearDistance = metric( linearPoint, node.obj ); + + // if it's a leaf + + if ( node.right === null && node.left === null ) { + + if ( bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[ 1 ] ) { + + saveNode( node, ownDistance ); + + } + + return; + + } + + if ( node.right === null ) { + + bestChild = node.left; + + } else if ( node.left === null ) { + + bestChild = node.right; + + } else { + + if ( point[ dimension ] < node.obj[ dimension ] ) { + + bestChild = node.left; + + } else { + + bestChild = node.right; + + } + + } + + // recursive search + + nearestSearch( bestChild ); + + if ( bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[ 1 ] ) { + + saveNode( node, ownDistance ); + + } + + // if there's still room or the current distance is nearer than the best distance + + if ( bestNodes.size() < maxNodes || Math.abs( linearDistance ) < bestNodes.peek()[ 1 ] ) { + + if ( bestChild === node.left ) { + + otherChild = node.right; + + } else { + + otherChild = node.left; + + } + + if ( otherChild !== null ) { + + nearestSearch( otherChild ); + + } + + } + + } + + if ( maxDistance ) { + + for ( i = 0; i < maxNodes; i += 1 ) { + + bestNodes.push( [ null, maxDistance ] ); + + } + + } + + nearestSearch( self.root ); + + result = []; + + for ( i = 0; i < maxNodes; i += 1 ) { + + if ( bestNodes.content[ i ][ 0 ] ) { + + result.push( [ bestNodes.content[ i ][ 0 ], bestNodes.content[ i ][ 1 ] ] ); + + } + + } + + return result; + + }; + +}; + +/** + * If you need to free up additional memory and agree with an additional O( log n ) traversal time you can get rid of "depth" and "pos" in Node: + * Depth can be easily done by adding 1 for every parent (care: root node has depth 0, not 1) + * Pos is a bit tricky: Assuming the tree is balanced (which is the case when after we built it up), perform the following steps: + * By traversing to the root store the path e.g. in a bit pattern (01001011, 0 is left, 1 is right) + * From buildTree we know that "median = Math.floor( plength / 2 );", therefore for each bit... + * 0: amountOfNodesRelevantForUs = Math.floor( (pamountOfNodesRelevantForUs - 1) / 2 ); + * 1: amountOfNodesRelevantForUs = Math.ceil( (pamountOfNodesRelevantForUs - 1) / 2 ); + * pos += Math.floor( (pamountOfNodesRelevantForUs - 1) / 2 ); + * when recursion done, we still need to add all left children of target node: + * pos += Math.floor( (pamountOfNodesRelevantForUs - 1) / 2 ); + * and I think you need to +1 for the current position, not sure.. depends, try it out ^^ + * + * I experienced that for 200'000 nodes you can get rid of 4 MB memory each, leading to 8 MB memory saved. + */ +THREE.TypedArrayUtils.Kdtree.prototype.Node = function ( obj, depth, parent, pos ) { + + this.obj = obj; + this.left = null; + this.right = null; + this.parent = parent; + this.depth = depth; + this.pos = pos; + +}; + +/** + * Binary heap implementation + * @author http://eloquentjavascript.net/appendix2.htm + */ + +THREE.TypedArrayUtils.Kdtree.BinaryHeap = function ( scoreFunction ) { + + this.content = []; + this.scoreFunction = scoreFunction; + +}; + +THREE.TypedArrayUtils.Kdtree.BinaryHeap.prototype = { + + push: function ( element ) { + + // Add the new element to the end of the array. + this.content.push( element ); + + // Allow it to bubble up. + this.bubbleUp( this.content.length - 1 ); + + }, + + pop: function () { + + // Store the first element so we can return it later. + var result = this.content[ 0 ]; + + // Get the element at the end of the array. + var end = this.content.pop(); + + // If there are any elements left, put the end element at the + // start, and let it sink down. + if ( this.content.length > 0 ) { + + this.content[ 0 ] = end; + this.sinkDown( 0 ); + + } + + return result; + + }, + + peek: function () { + + return this.content[ 0 ]; + + }, + + remove: function ( node ) { + + var len = this.content.length; + + // To remove a value, we must search through the array to find it. + for ( var i = 0; i < len; i ++ ) { + + if ( this.content[ i ] == node ) { + + // When it is found, the process seen in 'pop' is repeated + // to fill up the hole. + var end = this.content.pop(); + + if ( i != len - 1 ) { + + this.content[ i ] = end; + + if ( this.scoreFunction( end ) < this.scoreFunction( node ) ) { + + this.bubbleUp( i ); + + } else { + + this.sinkDown( i ); + + } + + } + + return; + + } + + } + + throw new Error( "Node not found." ); + + }, + + size: function () { + + return this.content.length; + + }, + + bubbleUp: function ( n ) { + + // Fetch the element that has to be moved. + var element = this.content[ n ]; + + // When at 0, an element can not go up any further. + while ( n > 0 ) { + + // Compute the parent element's index, and fetch it. + var parentN = Math.floor( ( n + 1 ) / 2 ) - 1, + parent = this.content[ parentN ]; + + // Swap the elements if the parent is greater. + if ( this.scoreFunction( element ) < this.scoreFunction( parent ) ) { + + this.content[ parentN ] = element; + this.content[ n ] = parent; + + // Update 'n' to continue at the new position. + n = parentN; + + } else { + + // Found a parent that is less, no need to move it further. + break; + + } + + } + + }, + + sinkDown: function ( n ) { + + // Look up the target element and its score. + var length = this.content.length, + element = this.content[ n ], + elemScore = this.scoreFunction( element ); + + while ( true ) { + + // Compute the indices of the child elements. + var child2N = ( n + 1 ) * 2, child1N = child2N - 1; + + // This is used to store the new position of the element, if any. + var swap = null; + + // If the first child exists (is inside the array)... + if ( child1N < length ) { + + // Look it up and compute its score. + var child1 = this.content[ child1N ], + child1Score = this.scoreFunction( child1 ); + + // If the score is less than our element's, we need to swap. + if ( child1Score < elemScore ) swap = child1N; + + } + + // Do the same checks for the other child. + if ( child2N < length ) { + + var child2 = this.content[ child2N ], + child2Score = this.scoreFunction( child2 ); + + if ( child2Score < ( swap === null ? elemScore : child1Score ) ) swap = child2N; + + } + + // If the element needs to be moved, swap it, and continue. + if ( swap !== null ) { + + this.content[ n ] = this.content[ swap ]; + this.content[ swap ] = element; + n = swap; + + } else { + + // Otherwise, we are done. + break; + + } + + } + + } + +}; diff --git a/node_modules/three/examples/js/UCSCharacter.js b/node_modules/three/examples/js/UCSCharacter.js new file mode 100644 index 00000000..1fc15689 --- /dev/null +++ b/node_modules/three/examples/js/UCSCharacter.js @@ -0,0 +1,140 @@ +THREE.UCSCharacter = function() { + + var scope = this; + + var mesh; + + this.scale = 1; + + this.root = new THREE.Object3D(); + + this.numSkins; + this.numMorphs; + + this.skins = []; + this.materials = []; + this.morphs = []; + + this.mixer = new THREE.AnimationMixer( this.root ); + + this.onLoadComplete = function () {}; + + this.loadCounter = 0; + + this.loadParts = function ( config ) { + + this.numSkins = config.skins.length; + this.numMorphs = config.morphs.length; + + // Character geometry + number of skins + this.loadCounter = 1 + config.skins.length; + + // SKINS + this.skins = loadTextures( config.baseUrl + "skins/", config.skins ); + this.materials = createMaterials( this.skins ); + + // MORPHS + this.morphs = config.morphs; + + // CHARACTER + var loader = new THREE.JSONLoader(); + console.log( config.baseUrl + config.character ); + loader.load( config.baseUrl + config.character, function( geometry ) { + + geometry.computeBoundingBox(); + geometry.computeVertexNormals(); + + mesh = new THREE.SkinnedMesh( geometry, new THREE.MeshFaceMaterial() ); + mesh.name = config.character; + scope.root.add( mesh ); + + var bb = geometry.boundingBox; + scope.root.scale.set( config.s, config.s, config.s ); + scope.root.position.set( config.x, config.y - bb.min.y * config.s, config.z ); + + mesh.castShadow = true; + mesh.receiveShadow = true; + + scope.mixer.addAction( new THREE.AnimationAction( geometry.animations[0] ).setLocalRoot( mesh ) ); + + scope.setSkin( 0 ); + + scope.checkLoadComplete(); + + } ); + + }; + + this.setSkin = function( index ) { + + if ( mesh && scope.materials ) { + + mesh.material = scope.materials[ index ]; + + } + + }; + + this.updateMorphs = function( influences ) { + + if ( mesh ) { + + for ( var i = 0; i < scope.numMorphs; i ++ ) { + + mesh.morphTargetInfluences[ i ] = influences[ scope.morphs[ i ] ] / 100; + + } + + } + + }; + + function loadTextures( baseUrl, textureUrls ) { + + var mapping = THREE.UVMapping; + var textures = []; + + for ( var i = 0; i < textureUrls.length; i ++ ) { + + textures[ i ] = THREE.ImageUtils.loadTexture( baseUrl + textureUrls[ i ], mapping, scope.checkLoadComplete ); + textures[ i ].name = textureUrls[ i ]; + + } + + return textures; + + } + + function createMaterials( skins ) { + + var materials = []; + + for ( var i = 0; i < skins.length; i ++ ) { + + materials[ i ] = new THREE.MeshLambertMaterial( { + color: 0xeeeeee, + specular: 10.0, + map: skins[ i ], + skinning: true, + morphTargets: true + } ); + + } + + return materials; + + } + + this.checkLoadComplete = function () { + + scope.loadCounter -= 1; + + if ( scope.loadCounter === 0 ) { + + scope.onLoadComplete(); + + } + + } + +}; diff --git a/node_modules/three/examples/js/WaterShader.js b/node_modules/three/examples/js/WaterShader.js new file mode 100644 index 00000000..d515258b --- /dev/null +++ b/node_modules/three/examples/js/WaterShader.js @@ -0,0 +1,295 @@ +/** + * @author jbouny / https://github.com/jbouny + * + * Work based on : + * @author Slayvin / http://slayvin.net : Flat mirror for three.js + * @author Stemkoski / http://www.adelphi.edu/~stemkoski : An implementation of water shader based on the flat mirror + * @author Jonas Wagner / http://29a.ch/ && http://29a.ch/slides/2012/webglwater/ : Water shader explanations in WebGL + */ + +THREE.ShaderLib[ 'water' ] = { + + uniforms: THREE.UniformsUtils.merge( [ + THREE.UniformsLib[ "fog" ], { + "normalSampler": { type: "t", value: null }, + "mirrorSampler": { type: "t", value: null }, + "alpha": { type: "f", value: 1.0 }, + "time": { type: "f", value: 0.0 }, + "distortionScale": { type: "f", value: 20.0 }, + "noiseScale": { type: "f", value: 1.0 }, + "textureMatrix" : { type: "m4", value: new THREE.Matrix4() }, + "sunColor": { type: "c", value: new THREE.Color(0x7F7F7F) }, + "sunDirection": { type: "v3", value: new THREE.Vector3(0.70707, 0.70707, 0) }, + "eye": { type: "v3", value: new THREE.Vector3(0, 0, 0) }, + "waterColor": { type: "c", value: new THREE.Color(0x555555) } + } + ] ), + + vertexShader: [ + 'uniform mat4 textureMatrix;', + 'uniform float time;', + + 'varying vec4 mirrorCoord;', + 'varying vec3 worldPosition;', + + 'void main()', + '{', + ' mirrorCoord = modelMatrix * vec4( position, 1.0 );', + ' worldPosition = mirrorCoord.xyz;', + ' mirrorCoord = textureMatrix * mirrorCoord;', + ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', + '}' + ].join( '\n' ), + + fragmentShader: [ + 'precision highp float;', + + 'uniform sampler2D mirrorSampler;', + 'uniform float alpha;', + 'uniform float time;', + 'uniform float distortionScale;', + 'uniform sampler2D normalSampler;', + 'uniform vec3 sunColor;', + 'uniform vec3 sunDirection;', + 'uniform vec3 eye;', + 'uniform vec3 waterColor;', + + 'varying vec4 mirrorCoord;', + 'varying vec3 worldPosition;', + + 'vec4 getNoise( vec2 uv )', + '{', + ' vec2 uv0 = ( uv / 103.0 ) + vec2(time / 17.0, time / 29.0);', + ' vec2 uv1 = uv / 107.0-vec2( time / -19.0, time / 31.0 );', + ' vec2 uv2 = uv / vec2( 8907.0, 9803.0 ) + vec2( time / 101.0, time / 97.0 );', + ' vec2 uv3 = uv / vec2( 1091.0, 1027.0 ) - vec2( time / 109.0, time / -113.0 );', + ' vec4 noise = ( texture2D( normalSampler, uv0 ) ) +', + ' ( texture2D( normalSampler, uv1 ) ) +', + ' ( texture2D( normalSampler, uv2 ) ) +', + ' ( texture2D( normalSampler, uv3 ) );', + ' return noise * 0.5 - 1.0;', + '}', + + 'void sunLight( const vec3 surfaceNormal, const vec3 eyeDirection, float shiny, float spec, float diffuse, inout vec3 diffuseColor, inout vec3 specularColor )', + '{', + ' vec3 reflection = normalize( reflect( -sunDirection, surfaceNormal ) );', + ' float direction = max( 0.0, dot( eyeDirection, reflection ) );', + ' specularColor += pow( direction, shiny ) * sunColor * spec;', + ' diffuseColor += max( dot( sunDirection, surfaceNormal ), 0.0 ) * sunColor * diffuse;', + '}', + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + + 'void main()', + '{', + ' vec4 noise = getNoise( worldPosition.xz );', + ' vec3 surfaceNormal = normalize( noise.xzy * vec3( 1.5, 1.0, 1.5 ) );', + + ' vec3 diffuseLight = vec3(0.0);', + ' vec3 specularLight = vec3(0.0);', + + ' vec3 worldToEye = eye-worldPosition;', + ' vec3 eyeDirection = normalize( worldToEye );', + ' sunLight( surfaceNormal, eyeDirection, 100.0, 2.0, 0.5, diffuseLight, specularLight );', + + ' float distance = length(worldToEye);', + + ' vec2 distortion = surfaceNormal.xz * ( 0.001 + 1.0 / distance ) * distortionScale;', + ' vec3 reflectionSample = vec3( texture2D( mirrorSampler, mirrorCoord.xy / mirrorCoord.z + distortion ) );', + + ' float theta = max( dot( eyeDirection, surfaceNormal ), 0.0 );', + ' float rf0 = 0.3;', + ' float reflectance = rf0 + ( 1.0 - rf0 ) * pow( ( 1.0 - theta ), 5.0 );', + ' vec3 scatter = max( 0.0, dot( surfaceNormal, eyeDirection ) ) * waterColor;', + ' vec3 albedo = mix( sunColor * diffuseLight * 0.3 + scatter, ( vec3( 0.1 ) + reflectionSample * 0.9 + reflectionSample * specularLight ), reflectance );', + ' vec3 outgoingLight = albedo;', + THREE.ShaderChunk[ "fog_fragment" ], + ' gl_FragColor = vec4( outgoingLight, alpha );', + '}' + ].join( '\n' ) + +}; + +THREE.Water = function ( renderer, camera, scene, options ) { + + THREE.Object3D.call( this ); + this.name = 'water_' + this.id; + + function optionalParameter ( value, defaultValue ) { + + return value !== undefined ? value : defaultValue; + + } + + options = options || {}; + + this.matrixNeedsUpdate = true; + + var width = optionalParameter( options.textureWidth, 512 ); + var height = optionalParameter( options.textureHeight, 512 ); + this.clipBias = optionalParameter( options.clipBias, 0.0 ); + this.alpha = optionalParameter( options.alpha, 1.0 ); + this.time = optionalParameter( options.time, 0.0 ); + this.normalSampler = optionalParameter( options.waterNormals, null ); + this.sunDirection = optionalParameter( options.sunDirection, new THREE.Vector3( 0.70707, 0.70707, 0.0 ) ); + this.sunColor = new THREE.Color( optionalParameter( options.sunColor, 0xffffff ) ); + this.waterColor = new THREE.Color( optionalParameter( options.waterColor, 0x7F7F7F ) ); + this.eye = optionalParameter( options.eye, new THREE.Vector3( 0, 0, 0 ) ); + this.distortionScale = optionalParameter( options.distortionScale, 20.0 ); + this.side = optionalParameter(options.side, THREE.FrontSide); + this.fog = optionalParameter(options.fog, false); + + this.renderer = renderer; + this.scene = scene; + this.mirrorPlane = new THREE.Plane(); + this.normal = new THREE.Vector3( 0, 0, 1 ); + this.mirrorWorldPosition = new THREE.Vector3(); + this.cameraWorldPosition = new THREE.Vector3(); + this.rotationMatrix = new THREE.Matrix4(); + this.lookAtPosition = new THREE.Vector3( 0, 0, - 1 ); + this.clipPlane = new THREE.Vector4(); + + if ( camera instanceof THREE.PerspectiveCamera ) + this.camera = camera; + else { + + this.camera = new THREE.PerspectiveCamera(); + console.log( this.name + ': camera is not a Perspective Camera!' ) + + } + + this.textureMatrix = new THREE.Matrix4(); + + this.mirrorCamera = this.camera.clone(); + + this.texture = new THREE.WebGLRenderTarget( width, height ); + this.tempTexture = new THREE.WebGLRenderTarget( width, height ); + + var mirrorShader = THREE.ShaderLib[ "water" ]; + var mirrorUniforms = THREE.UniformsUtils.clone( mirrorShader.uniforms ); + + this.material = new THREE.ShaderMaterial( { + fragmentShader: mirrorShader.fragmentShader, + vertexShader: mirrorShader.vertexShader, + uniforms: mirrorUniforms, + transparent: true, + side: this.side, + fog: this.fog + } ); + + this.material.uniforms.mirrorSampler.value = this.texture; + this.material.uniforms.textureMatrix.value = this.textureMatrix; + this.material.uniforms.alpha.value = this.alpha; + this.material.uniforms.time.value = this.time; + this.material.uniforms.normalSampler.value = this.normalSampler; + this.material.uniforms.sunColor.value = this.sunColor; + this.material.uniforms.waterColor.value = this.waterColor; + this.material.uniforms.sunDirection.value = this.sunDirection; + this.material.uniforms.distortionScale.value = this.distortionScale; + + this.material.uniforms.eye.value = this.eye; + + if ( ! THREE.Math.isPowerOfTwo( width ) || ! THREE.Math.isPowerOfTwo( height ) ) { + + this.texture.generateMipmaps = false; + this.texture.minFilter = THREE.LinearFilter; + this.tempTexture.generateMipmaps = false; + this.tempTexture.minFilter = THREE.LinearFilter; + + } + + this.updateTextureMatrix(); + this.render(); + +}; + +THREE.Water.prototype = Object.create( THREE.Mirror.prototype ); +THREE.Water.prototype.constructor = THREE.Water; + + +THREE.Water.prototype.updateTextureMatrix = function () { + + function sign( x ) { + + return x ? x < 0 ? - 1 : 1 : 0; + + } + + this.updateMatrixWorld(); + this.camera.updateMatrixWorld(); + + this.mirrorWorldPosition.setFromMatrixPosition( this.matrixWorld ); + this.cameraWorldPosition.setFromMatrixPosition( this.camera.matrixWorld ); + + this.rotationMatrix.extractRotation( this.matrixWorld ); + + this.normal.set( 0, 0, 1 ); + this.normal.applyMatrix4( this.rotationMatrix ); + + var view = this.mirrorWorldPosition.clone().sub( this.cameraWorldPosition ); + view.reflect( this.normal ).negate(); + view.add( this.mirrorWorldPosition ); + + this.rotationMatrix.extractRotation( this.camera.matrixWorld ); + + this.lookAtPosition.set( 0, 0, - 1 ); + this.lookAtPosition.applyMatrix4( this.rotationMatrix ); + this.lookAtPosition.add( this.cameraWorldPosition ); + + var target = this.mirrorWorldPosition.clone().sub( this.lookAtPosition ); + target.reflect( this.normal ).negate(); + target.add( this.mirrorWorldPosition ); + + this.up.set( 0, - 1, 0 ); + this.up.applyMatrix4( this.rotationMatrix ); + this.up.reflect( this.normal ).negate(); + + this.mirrorCamera.position.copy( view ); + this.mirrorCamera.up = this.up; + this.mirrorCamera.lookAt( target ); + this.mirrorCamera.aspect = this.camera.aspect; + + this.mirrorCamera.updateProjectionMatrix(); + this.mirrorCamera.updateMatrixWorld(); + this.mirrorCamera.matrixWorldInverse.getInverse( this.mirrorCamera.matrixWorld ); + + // Update the texture matrix + this.textureMatrix.set( 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0 ); + this.textureMatrix.multiply( this.mirrorCamera.projectionMatrix ); + this.textureMatrix.multiply( this.mirrorCamera.matrixWorldInverse ); + + // Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html + // Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf + this.mirrorPlane.setFromNormalAndCoplanarPoint( this.normal, this.mirrorWorldPosition ); + this.mirrorPlane.applyMatrix4( this.mirrorCamera.matrixWorldInverse ); + + this.clipPlane.set( this.mirrorPlane.normal.x, this.mirrorPlane.normal.y, this.mirrorPlane.normal.z, this.mirrorPlane.constant ); + + var q = new THREE.Vector4(); + var projectionMatrix = this.mirrorCamera.projectionMatrix; + + q.x = ( sign( this.clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ]; + q.y = ( sign( this.clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ]; + q.z = - 1.0; + q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ]; + + // Calculate the scaled plane vector + var c = new THREE.Vector4(); + c = this.clipPlane.multiplyScalar( 2.0 / this.clipPlane.dot( q ) ); + + // Replacing the third row of the projection matrix + projectionMatrix.elements[ 2 ] = c.x; + projectionMatrix.elements[ 6 ] = c.y; + projectionMatrix.elements[ 10 ] = c.z + 1.0 - this.clipBias; + projectionMatrix.elements[ 14 ] = c.w; + + var worldCoordinates = new THREE.Vector3(); + worldCoordinates.setFromMatrixPosition( this.camera.matrixWorld ); + this.eye = worldCoordinates; + this.material.uniforms.eye.value = this.eye; + +}; diff --git a/node_modules/three/examples/js/cameras/CombinedCamera.js b/node_modules/three/examples/js/cameras/CombinedCamera.js new file mode 100644 index 00000000..a21ab599 --- /dev/null +++ b/node_modules/three/examples/js/cameras/CombinedCamera.js @@ -0,0 +1,236 @@ +/** + * @author zz85 / http://twitter.com/blurspline / http://www.lab4games.net/zz85/blog + * + * A general perpose camera, for setting FOV, Lens Focal Length, + * and switching between perspective and orthographic views easily. + * Use this only if you do not wish to manage + * both a Orthographic and Perspective Camera + * + */ + + +THREE.CombinedCamera = function ( width, height, fov, near, far, orthoNear, orthoFar ) { + + THREE.Camera.call( this ); + + this.fov = fov; + + this.left = - width / 2; + this.right = width / 2; + this.top = height / 2; + this.bottom = - height / 2; + + // We could also handle the projectionMatrix internally, but just wanted to test nested camera objects + + this.cameraO = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, orthoNear, orthoFar ); + this.cameraP = new THREE.PerspectiveCamera( fov, width / height, near, far ); + + this.zoom = 1; + + this.toPerspective(); + +}; + +THREE.CombinedCamera.prototype = Object.create( THREE.Camera.prototype ); +THREE.CombinedCamera.prototype.constructor = THREE.CombinedCamera; + +THREE.CombinedCamera.prototype.toPerspective = function () { + + // Switches to the Perspective Camera + + this.near = this.cameraP.near; + this.far = this.cameraP.far; + + this.cameraP.fov = this.fov / this.zoom ; + + this.cameraP.updateProjectionMatrix(); + + this.projectionMatrix = this.cameraP.projectionMatrix; + + this.inPerspectiveMode = true; + this.inOrthographicMode = false; + +}; + +THREE.CombinedCamera.prototype.toOrthographic = function () { + + // Switches to the Orthographic camera estimating viewport from Perspective + + var fov = this.fov; + var aspect = this.cameraP.aspect; + var near = this.cameraP.near; + var far = this.cameraP.far; + + // The size that we set is the mid plane of the viewing frustum + + var hyperfocus = ( near + far ) / 2; + + var halfHeight = Math.tan( fov * Math.PI / 180 / 2 ) * hyperfocus; + var planeHeight = 2 * halfHeight; + var planeWidth = planeHeight * aspect; + var halfWidth = planeWidth / 2; + + halfHeight /= this.zoom; + halfWidth /= this.zoom; + + this.cameraO.left = - halfWidth; + this.cameraO.right = halfWidth; + this.cameraO.top = halfHeight; + this.cameraO.bottom = - halfHeight; + + // this.cameraO.left = -farHalfWidth; + // this.cameraO.right = farHalfWidth; + // this.cameraO.top = farHalfHeight; + // this.cameraO.bottom = -farHalfHeight; + + // this.cameraO.left = this.left / this.zoom; + // this.cameraO.right = this.right / this.zoom; + // this.cameraO.top = this.top / this.zoom; + // this.cameraO.bottom = this.bottom / this.zoom; + + this.cameraO.updateProjectionMatrix(); + + this.near = this.cameraO.near; + this.far = this.cameraO.far; + this.projectionMatrix = this.cameraO.projectionMatrix; + + this.inPerspectiveMode = false; + this.inOrthographicMode = true; + +}; + + +THREE.CombinedCamera.prototype.setSize = function( width, height ) { + + this.cameraP.aspect = width / height; + this.left = - width / 2; + this.right = width / 2; + this.top = height / 2; + this.bottom = - height / 2; + +}; + + +THREE.CombinedCamera.prototype.setFov = function( fov ) { + + this.fov = fov; + + if ( this.inPerspectiveMode ) { + + this.toPerspective(); + + } else { + + this.toOrthographic(); + + } + +}; + +// For maintaining similar API with PerspectiveCamera + +THREE.CombinedCamera.prototype.updateProjectionMatrix = function() { + + if ( this.inPerspectiveMode ) { + + this.toPerspective(); + + } else { + + this.toPerspective(); + this.toOrthographic(); + + } + +}; + +/* +* Uses Focal Length (in mm) to estimate and set FOV +* 35mm (fullframe) camera is used if frame size is not specified; +* Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html +*/ +THREE.CombinedCamera.prototype.setLens = function ( focalLength, frameHeight ) { + + if ( frameHeight === undefined ) frameHeight = 24; + + var fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) ); + + this.setFov( fov ); + + return fov; + +}; + + +THREE.CombinedCamera.prototype.setZoom = function( zoom ) { + + this.zoom = zoom; + + if ( this.inPerspectiveMode ) { + + this.toPerspective(); + + } else { + + this.toOrthographic(); + + } + +}; + +THREE.CombinedCamera.prototype.toFrontView = function() { + + this.rotation.x = 0; + this.rotation.y = 0; + this.rotation.z = 0; + + // should we be modifing the matrix instead? + + this.rotationAutoUpdate = false; + +}; + +THREE.CombinedCamera.prototype.toBackView = function() { + + this.rotation.x = 0; + this.rotation.y = Math.PI; + this.rotation.z = 0; + this.rotationAutoUpdate = false; + +}; + +THREE.CombinedCamera.prototype.toLeftView = function() { + + this.rotation.x = 0; + this.rotation.y = - Math.PI / 2; + this.rotation.z = 0; + this.rotationAutoUpdate = false; + +}; + +THREE.CombinedCamera.prototype.toRightView = function() { + + this.rotation.x = 0; + this.rotation.y = Math.PI / 2; + this.rotation.z = 0; + this.rotationAutoUpdate = false; + +}; + +THREE.CombinedCamera.prototype.toTopView = function() { + + this.rotation.x = - Math.PI / 2; + this.rotation.y = 0; + this.rotation.z = 0; + this.rotationAutoUpdate = false; + +}; + +THREE.CombinedCamera.prototype.toBottomView = function() { + + this.rotation.x = Math.PI / 2; + this.rotation.y = 0; + this.rotation.z = 0; + this.rotationAutoUpdate = false; + +}; diff --git a/node_modules/three/examples/js/controls/DeviceOrientationControls.js b/node_modules/three/examples/js/controls/DeviceOrientationControls.js new file mode 100644 index 00000000..5ea04dc0 --- /dev/null +++ b/node_modules/three/examples/js/controls/DeviceOrientationControls.js @@ -0,0 +1,99 @@ +/** + * @author richt / http://richt.me + * @author WestLangley / http://github.com/WestLangley + * + * W3C Device Orientation control (http://w3c.github.io/deviceorientation/spec-source-orientation.html) + */ + +THREE.DeviceOrientationControls = function ( object ) { + + var scope = this; + + this.object = object; + this.object.rotation.reorder( "YXZ" ); + + this.enabled = true; + + this.deviceOrientation = {}; + this.screenOrientation = 0; + + var onDeviceOrientationChangeEvent = function ( event ) { + + scope.deviceOrientation = event; + + }; + + var onScreenOrientationChangeEvent = function () { + + scope.screenOrientation = window.orientation || 0; + + }; + + // The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y'' + + var setObjectQuaternion = function () { + + var zee = new THREE.Vector3( 0, 0, 1 ); + + var euler = new THREE.Euler(); + + var q0 = new THREE.Quaternion(); + + var q1 = new THREE.Quaternion( - Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis + + return function ( quaternion, alpha, beta, gamma, orient ) { + + euler.set( beta, alpha, - gamma, 'YXZ' ); // 'ZXY' for the device, but 'YXZ' for us + + quaternion.setFromEuler( euler ); // orient the device + + quaternion.multiply( q1 ); // camera looks out the back of the device, not the top + + quaternion.multiply( q0.setFromAxisAngle( zee, - orient ) ); // adjust for screen orientation + + } + + }(); + + this.connect = function() { + + onScreenOrientationChangeEvent(); // run once on load + + window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent, false ); + window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent, false ); + + scope.enabled = true; + + }; + + this.disconnect = function() { + + window.removeEventListener( 'orientationchange', onScreenOrientationChangeEvent, false ); + window.removeEventListener( 'deviceorientation', onDeviceOrientationChangeEvent, false ); + + scope.enabled = false; + + }; + + this.update = function () { + + if ( scope.enabled === false ) return; + + var alpha = scope.deviceOrientation.alpha ? THREE.Math.degToRad( scope.deviceOrientation.alpha ) : 0; // Z + var beta = scope.deviceOrientation.beta ? THREE.Math.degToRad( scope.deviceOrientation.beta ) : 0; // X' + var gamma = scope.deviceOrientation.gamma ? THREE.Math.degToRad( scope.deviceOrientation.gamma ) : 0; // Y'' + var orient = scope.screenOrientation ? THREE.Math.degToRad( scope.screenOrientation ) : 0; // O + + setObjectQuaternion( scope.object.quaternion, alpha, beta, gamma, orient ); + + }; + + this.dispose = function () { + + this.disconnect(); + + }; + + this.connect(); + +}; diff --git a/node_modules/three/examples/js/controls/DragControls.js b/node_modules/three/examples/js/controls/DragControls.js new file mode 100644 index 00000000..ed62963d --- /dev/null +++ b/node_modules/three/examples/js/controls/DragControls.js @@ -0,0 +1,236 @@ +/* + * @author zz85 / https://github.com/zz85 + * Running this will allow you to drag three.js objects around the screen. + */ +THREE.DragControls = function( _camera, _objects, _domElement ) { + + var _projector = new THREE.Projector(); + var _raycaster = new THREE.Raycaster(); + + var _mouse = new THREE.Vector3(), + _offset = new THREE.Vector3(); + var _selected, _hovered; + + var p3subp1 = new THREE.Vector3(); + var targetposition = new THREE.Vector3(); + var zerovector = new THREE.Vector3(); + + this.enabled = false; + + /* Custom Event Handling */ + var _listeners = { + + }; + + var me = this; + this.on = function( event, handler ) { + + if ( ! _listeners[ event ] ) _listeners[ event ] = []; + + _listeners[ event ].push( handler ); + return me; + + }; + + this.off = function( event, handler ) { + + var l = _listeners[ event ]; + if ( ! l ) return me; + + if ( l.indexOf( handler ) > - 1 ) { + + l.splice( handler, 1 ); + + } + + return me; + + }; + + var notify = function( event, data, member ) { + + var l = _listeners[ event ]; + if ( ! l ) return; + + if ( ! member ) { + + for ( var i = 0; i < l.length; i ++ ) { + + l[ i ]( data ); + + } + + } + + }; + + this.setObjects = function( objects ) { + + if ( objects instanceof THREE.Scene ) { + + _objects = objects.children; + + } else { + + _objects = objects; + + } + + }; + + this.setObjects( _objects ); + + this.activate = function() { + + _domElement.addEventListener( 'mousemove', onDocumentMouseMove, false ); + _domElement.addEventListener( 'mousedown', onDocumentMouseDown, false ); + _domElement.addEventListener( 'mouseup', onDocumentMouseUp, false ); + + }; + + this.deactivate = function() { + + _domElement.removeEventListener( 'mousemove', onDocumentMouseMove, false ); + _domElement.removeEventListener( 'mousedown', onDocumentMouseDown, false ); + _domElement.removeEventListener( 'mouseup', onDocumentMouseUp, false ); + + }; + + this.dispose = function() { + + me.deactivate(); + + }; + + this.activate(); + + function onDocumentMouseMove( event ) { + + event.preventDefault(); + + _mouse.x = ( event.clientX / _domElement.width ) * 2 - 1; + _mouse.y = - ( event.clientY / _domElement.height ) * 2 + 1; + + _raycaster.setFromCamera( _mouse, _camera ); + var ray = _raycaster.ray; + + + if ( _selected && me.enabled ) { + + var normal = _selected.normal; + + // I found this article useful about plane-line intersections + // http://paulbourke.net/geometry/planeline/ + + var denom = normal.dot( ray.direction ); + if ( denom == 0 ) { + + // bail + console.log( 'no or infinite solutions' ); + return; + + } + + var num = normal.dot( p3subp1.copy( _selected.point ).sub( ray.origin ) ); + var u = num / denom; + + targetposition.copy( ray.direction ).multiplyScalar( u ).add( ray.origin ).sub( _offset ); + // _selected.object.position.copy(targetposition); + + var xLock, yLock, zLock = false; + + var moveX, moveY, moveZ; + + + if ( xLock ) { + + moveX = true; + moveY = false; + moveZ = false; + + } else if ( yLock ) { + + moveX = false; + moveY = true; + moveZ = false; + + } else { + + moveX = moveY = moveZ = true; + + } + + // Reverse Matrix? + if ( moveX ) _selected.object.position.x = targetposition.x; + if ( moveY ) _selected.object.position.y = targetposition.y; + if ( moveZ ) _selected.object.position.z = targetposition.z; + + notify( 'drag', _selected ); + + return; + + } + + _raycaster.setFromCamera( _mouse, _camera ); + var intersects = _raycaster.intersectObjects( _objects ); + + if ( intersects.length > 0 ) { + + _domElement.style.cursor = 'pointer'; + _hovered = intersects[ 0 ]; + notify( 'hoveron', _hovered ); + + } else { + + notify( 'hoveroff', _hovered ); + _hovered = null; + _domElement.style.cursor = 'auto'; + + } + + } + + function onDocumentMouseDown( event ) { + + event.preventDefault(); + + _mouse.x = ( event.clientX / _domElement.width ) * 2 - 1; + _mouse.y = - ( event.clientY / _domElement.height ) * 2 + 1; + + _raycaster.setFromCamera( _mouse, _camera ); + var intersects = _raycaster.intersectObjects( _objects ); + var ray = _raycaster.ray; + + var normal = ray.direction; // normal ray to the camera position + if ( intersects.length > 0 ) { + + _selected = intersects[ 0 ]; + _selected.ray = ray; + _selected.normal = normal ; + _offset.copy( _selected.point ).sub( _selected.object.position ); + + _domElement.style.cursor = 'move'; + + notify( 'dragstart', _selected ); + + } + + + } + + function onDocumentMouseUp( event ) { + + event.preventDefault(); + + if ( _selected ) { + + notify( 'dragend', _selected ); + _selected = null; + + } + + _domElement.style.cursor = 'auto'; + + } + +} diff --git a/node_modules/three/examples/js/controls/EditorControls.js b/node_modules/three/examples/js/controls/EditorControls.js new file mode 100644 index 00000000..9249fef8 --- /dev/null +++ b/node_modules/three/examples/js/controls/EditorControls.js @@ -0,0 +1,327 @@ +/** + * @author qiao / https://github.com/qiao + * @author mrdoob / http://mrdoob.com + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.EditorControls = function ( object, domElement ) { + + domElement = ( domElement !== undefined ) ? domElement : document; + + // API + + this.enabled = true; + this.center = new THREE.Vector3(); + + // internals + + var scope = this; + var vector = new THREE.Vector3(); + + var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2 }; + var state = STATE.NONE; + + var center = this.center; + var normalMatrix = new THREE.Matrix3(); + var pointer = new THREE.Vector2(); + var pointerOld = new THREE.Vector2(); + + // events + + var changeEvent = { type: 'change' }; + + this.focus = function ( target, frame ) { + + var scale = new THREE.Vector3(); + target.matrixWorld.decompose( center, new THREE.Quaternion(), scale ); + + if ( frame && target.geometry ) { + + scale = ( scale.x + scale.y + scale.z ) / 3; + center.add( target.geometry.boundingSphere.center.clone().multiplyScalar( scale ) ); + var radius = target.geometry.boundingSphere.radius * ( scale ); + var pos = object.position.clone().sub( center ).normalize().multiplyScalar( radius * 2 ); + object.position.copy( center ).add( pos ); + + } + + object.lookAt( center ); + + scope.dispatchEvent( changeEvent ); + + }; + + this.pan = function ( delta ) { + + var distance = object.position.distanceTo( center ); + + delta.multiplyScalar( distance * 0.001 ); + delta.applyMatrix3( normalMatrix.getNormalMatrix( object.matrix ) ); + + object.position.add( delta ); + center.add( delta ); + + scope.dispatchEvent( changeEvent ); + + }; + + this.zoom = function ( delta ) { + + var distance = object.position.distanceTo( center ); + + delta.multiplyScalar( distance * 0.001 ); + + if ( delta.length() > distance ) return; + + delta.applyMatrix3( normalMatrix.getNormalMatrix( object.matrix ) ); + + object.position.add( delta ); + + scope.dispatchEvent( changeEvent ); + + }; + + this.rotate = function ( delta ) { + + vector.copy( object.position ).sub( center ); + + var theta = Math.atan2( vector.x, vector.z ); + var phi = Math.atan2( Math.sqrt( vector.x * vector.x + vector.z * vector.z ), vector.y ); + + theta += delta.x; + phi += delta.y; + + var EPS = 0.000001; + + phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); + + var radius = vector.length(); + + vector.x = radius * Math.sin( phi ) * Math.sin( theta ); + vector.y = radius * Math.cos( phi ); + vector.z = radius * Math.sin( phi ) * Math.cos( theta ); + + object.position.copy( center ).add( vector ); + + object.lookAt( center ); + + scope.dispatchEvent( changeEvent ); + + }; + + // mouse + + function onMouseDown( event ) { + + if ( scope.enabled === false ) return; + + if ( event.button === 0 ) { + + state = STATE.ROTATE; + + } else if ( event.button === 1 ) { + + state = STATE.ZOOM; + + } else if ( event.button === 2 ) { + + state = STATE.PAN; + + } + + pointerOld.set( event.clientX, event.clientY ); + + domElement.addEventListener( 'mousemove', onMouseMove, false ); + domElement.addEventListener( 'mouseup', onMouseUp, false ); + domElement.addEventListener( 'mouseout', onMouseUp, false ); + domElement.addEventListener( 'dblclick', onMouseUp, false ); + + } + + function onMouseMove( event ) { + + if ( scope.enabled === false ) return; + + pointer.set( event.clientX, event.clientY ); + + var movementX = pointer.x - pointerOld.x; + var movementY = pointer.y - pointerOld.y; + + if ( state === STATE.ROTATE ) { + + scope.rotate( new THREE.Vector3( - movementX * 0.005, - movementY * 0.005, 0 ) ); + + } else if ( state === STATE.ZOOM ) { + + scope.zoom( new THREE.Vector3( 0, 0, movementY ) ); + + } else if ( state === STATE.PAN ) { + + scope.pan( new THREE.Vector3( - movementX, movementY, 0 ) ); + + } + + pointerOld.set( event.clientX, event.clientY ); + + } + + function onMouseUp( event ) { + + domElement.removeEventListener( 'mousemove', onMouseMove, false ); + domElement.removeEventListener( 'mouseup', onMouseUp, false ); + domElement.removeEventListener( 'mouseout', onMouseUp, false ); + domElement.removeEventListener( 'dblclick', onMouseUp, false ); + + state = STATE.NONE; + + } + + function onMouseWheel( event ) { + + event.preventDefault(); + + // if ( scope.enabled === false ) return; + + var delta = 0; + + if ( event.wheelDelta ) { + + // WebKit / Opera / Explorer 9 + + delta = - event.wheelDelta; + + } else if ( event.detail ) { + + // Firefox + + delta = event.detail * 10; + + } + + scope.zoom( new THREE.Vector3( 0, 0, delta ) ); + + } + + function contextmenu( event ) { + + event.preventDefault(); + + } + + this.dispose = function() { + + domElement.removeEventListener( 'contextmenu', contextmenu, false ); + domElement.removeEventListener( 'mousedown', onMouseDown, false ); + domElement.removeEventListener( 'mousewheel', onMouseWheel, false ); + domElement.removeEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox + + domElement.removeEventListener( 'mousemove', onMouseMove, false ); + domElement.removeEventListener( 'mouseup', onMouseUp, false ); + domElement.removeEventListener( 'mouseout', onMouseUp, false ); + domElement.removeEventListener( 'dblclick', onMouseUp, false ); + + domElement.removeEventListener( 'touchstart', touchStart, false ); + domElement.removeEventListener( 'touchmove', touchMove, false ); + + } + + domElement.addEventListener( 'contextmenu', contextmenu, false ); + domElement.addEventListener( 'mousedown', onMouseDown, false ); + domElement.addEventListener( 'mousewheel', onMouseWheel, false ); + domElement.addEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox + + // touch + + var touch = new THREE.Vector3(); + + var touches = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; + var prevTouches = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; + + var prevDistance = null; + + function touchStart( event ) { + + if ( scope.enabled === false ) return; + + switch ( event.touches.length ) { + + case 1: + touches[ 0 ].set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, 0 ); + touches[ 1 ].set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, 0 ); + break; + + case 2: + touches[ 0 ].set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, 0 ); + touches[ 1 ].set( event.touches[ 1 ].pageX, event.touches[ 1 ].pageY, 0 ); + prevDistance = touches[ 0 ].distanceTo( touches[ 1 ] ); + break; + + } + + prevTouches[ 0 ].copy( touches[ 0 ] ); + prevTouches[ 1 ].copy( touches[ 1 ] ); + + } + + + function touchMove( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + var getClosest = function( touch, touches ) { + + var closest = touches[ 0 ]; + + for ( var i in touches ) { + + if ( closest.distanceTo( touch ) > touches[ i ].distanceTo( touch ) ) closest = touches[ i ]; + + } + + return closest; + + }; + + switch ( event.touches.length ) { + + case 1: + touches[ 0 ].set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, 0 ); + touches[ 1 ].set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, 0 ); + scope.rotate( touches[ 0 ].sub( getClosest( touches[ 0 ], prevTouches ) ).multiplyScalar( - 0.005 ) ); + break; + + case 2: + touches[ 0 ].set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, 0 ); + touches[ 1 ].set( event.touches[ 1 ].pageX, event.touches[ 1 ].pageY, 0 ); + distance = touches[ 0 ].distanceTo( touches[ 1 ] ); + scope.zoom( new THREE.Vector3( 0, 0, prevDistance - distance ) ); + prevDistance = distance; + + + var offset0 = touches[ 0 ].clone().sub( getClosest( touches[ 0 ], prevTouches ) ); + var offset1 = touches[ 1 ].clone().sub( getClosest( touches[ 1 ], prevTouches ) ); + offset0.x = - offset0.x; + offset1.x = - offset1.x; + + scope.pan( offset0.add( offset1 ).multiplyScalar( 0.5 ) ); + + break; + + } + + prevTouches[ 0 ].copy( touches[ 0 ] ); + prevTouches[ 1 ].copy( touches[ 1 ] ); + + } + + domElement.addEventListener( 'touchstart', touchStart, false ); + domElement.addEventListener( 'touchmove', touchMove, false ); + +}; + +THREE.EditorControls.prototype = Object.create( THREE.EventDispatcher.prototype ); +THREE.EditorControls.prototype.constructor = THREE.EditorControls; diff --git a/node_modules/three/examples/js/controls/FirstPersonControls.js b/node_modules/three/examples/js/controls/FirstPersonControls.js new file mode 100644 index 00000000..542c3bd3 --- /dev/null +++ b/node_modules/three/examples/js/controls/FirstPersonControls.js @@ -0,0 +1,300 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author paulirish / http://paulirish.com/ + */ + +THREE.FirstPersonControls = function ( object, domElement ) { + + this.object = object; + this.target = new THREE.Vector3( 0, 0, 0 ); + + this.domElement = ( domElement !== undefined ) ? domElement : document; + + this.enabled = true; + + this.movementSpeed = 1.0; + this.lookSpeed = 0.005; + + this.lookVertical = true; + this.autoForward = false; + + this.activeLook = true; + + this.heightSpeed = false; + this.heightCoef = 1.0; + this.heightMin = 0.0; + this.heightMax = 1.0; + + this.constrainVertical = false; + this.verticalMin = 0; + this.verticalMax = Math.PI; + + this.autoSpeedFactor = 0.0; + + this.mouseX = 0; + this.mouseY = 0; + + this.lat = 0; + this.lon = 0; + this.phi = 0; + this.theta = 0; + + this.moveForward = false; + this.moveBackward = false; + this.moveLeft = false; + this.moveRight = false; + + this.mouseDragOn = false; + + this.viewHalfX = 0; + this.viewHalfY = 0; + + if ( this.domElement !== document ) { + + this.domElement.setAttribute( 'tabindex', - 1 ); + + } + + // + + this.handleResize = function () { + + if ( this.domElement === document ) { + + this.viewHalfX = window.innerWidth / 2; + this.viewHalfY = window.innerHeight / 2; + + } else { + + this.viewHalfX = this.domElement.offsetWidth / 2; + this.viewHalfY = this.domElement.offsetHeight / 2; + + } + + }; + + this.onMouseDown = function ( event ) { + + if ( this.domElement !== document ) { + + this.domElement.focus(); + + } + + event.preventDefault(); + event.stopPropagation(); + + if ( this.activeLook ) { + + switch ( event.button ) { + + case 0: this.moveForward = true; break; + case 2: this.moveBackward = true; break; + + } + + } + + this.mouseDragOn = true; + + }; + + this.onMouseUp = function ( event ) { + + event.preventDefault(); + event.stopPropagation(); + + if ( this.activeLook ) { + + switch ( event.button ) { + + case 0: this.moveForward = false; break; + case 2: this.moveBackward = false; break; + + } + + } + + this.mouseDragOn = false; + + }; + + this.onMouseMove = function ( event ) { + + if ( this.domElement === document ) { + + this.mouseX = event.pageX - this.viewHalfX; + this.mouseY = event.pageY - this.viewHalfY; + + } else { + + this.mouseX = event.pageX - this.domElement.offsetLeft - this.viewHalfX; + this.mouseY = event.pageY - this.domElement.offsetTop - this.viewHalfY; + + } + + }; + + this.onKeyDown = function ( event ) { + + //event.preventDefault(); + + switch ( event.keyCode ) { + + case 38: /*up*/ + case 87: /*W*/ this.moveForward = true; break; + + case 37: /*left*/ + case 65: /*A*/ this.moveLeft = true; break; + + case 40: /*down*/ + case 83: /*S*/ this.moveBackward = true; break; + + case 39: /*right*/ + case 68: /*D*/ this.moveRight = true; break; + + case 82: /*R*/ this.moveUp = true; break; + case 70: /*F*/ this.moveDown = true; break; + + } + + }; + + this.onKeyUp = function ( event ) { + + switch ( event.keyCode ) { + + case 38: /*up*/ + case 87: /*W*/ this.moveForward = false; break; + + case 37: /*left*/ + case 65: /*A*/ this.moveLeft = false; break; + + case 40: /*down*/ + case 83: /*S*/ this.moveBackward = false; break; + + case 39: /*right*/ + case 68: /*D*/ this.moveRight = false; break; + + case 82: /*R*/ this.moveUp = false; break; + case 70: /*F*/ this.moveDown = false; break; + + } + + }; + + this.update = function( delta ) { + + if ( this.enabled === false ) return; + + if ( this.heightSpeed ) { + + var y = THREE.Math.clamp( this.object.position.y, this.heightMin, this.heightMax ); + var heightDelta = y - this.heightMin; + + this.autoSpeedFactor = delta * ( heightDelta * this.heightCoef ); + + } else { + + this.autoSpeedFactor = 0.0; + + } + + var actualMoveSpeed = delta * this.movementSpeed; + + if ( this.moveForward || ( this.autoForward && ! this.moveBackward ) ) this.object.translateZ( - ( actualMoveSpeed + this.autoSpeedFactor ) ); + if ( this.moveBackward ) this.object.translateZ( actualMoveSpeed ); + + if ( this.moveLeft ) this.object.translateX( - actualMoveSpeed ); + if ( this.moveRight ) this.object.translateX( actualMoveSpeed ); + + if ( this.moveUp ) this.object.translateY( actualMoveSpeed ); + if ( this.moveDown ) this.object.translateY( - actualMoveSpeed ); + + var actualLookSpeed = delta * this.lookSpeed; + + if ( ! this.activeLook ) { + + actualLookSpeed = 0; + + } + + var verticalLookRatio = 1; + + if ( this.constrainVertical ) { + + verticalLookRatio = Math.PI / ( this.verticalMax - this.verticalMin ); + + } + + this.lon += this.mouseX * actualLookSpeed; + if ( this.lookVertical ) this.lat -= this.mouseY * actualLookSpeed * verticalLookRatio; + + this.lat = Math.max( - 85, Math.min( 85, this.lat ) ); + this.phi = THREE.Math.degToRad( 90 - this.lat ); + + this.theta = THREE.Math.degToRad( this.lon ); + + if ( this.constrainVertical ) { + + this.phi = THREE.Math.mapLinear( this.phi, 0, Math.PI, this.verticalMin, this.verticalMax ); + + } + + var targetPosition = this.target, + position = this.object.position; + + targetPosition.x = position.x + 100 * Math.sin( this.phi ) * Math.cos( this.theta ); + targetPosition.y = position.y + 100 * Math.cos( this.phi ); + targetPosition.z = position.z + 100 * Math.sin( this.phi ) * Math.sin( this.theta ); + + this.object.lookAt( targetPosition ); + + }; + + function contextmenu( event ) { + + event.preventDefault(); + + } + + this.dispose = function() { + + this.domElement.removeEventListener( 'contextmenu', contextmenu, false ); + this.domElement.removeEventListener( 'mousedown', _onMouseDown, false ); + this.domElement.removeEventListener( 'mousemove', _onMouseMove, false ); + this.domElement.removeEventListener( 'mouseup', _onMouseUp, false ); + + window.removeEventListener( 'keydown', _onKeyDown, false ); + window.removeEventListener( 'keyup', _onKeyUp, false ); + + } + + var _onMouseMove = bind( this, this.onMouseMove ); + var _onMouseDown = bind( this, this.onMouseDown ); + var _onMouseUp = bind( this, this.onMouseUp ); + var _onKeyDown = bind( this, this.onKeyDown ); + var _onKeyUp = bind( this, this.onKeyUp ); + + this.domElement.addEventListener( 'contextmenu', contextmenu, false ); + this.domElement.addEventListener( 'mousemove', _onMouseMove, false ); + this.domElement.addEventListener( 'mousedown', _onMouseDown, false ); + this.domElement.addEventListener( 'mouseup', _onMouseUp, false ); + + window.addEventListener( 'keydown', _onKeyDown, false ); + window.addEventListener( 'keyup', _onKeyUp, false ); + + function bind( scope, fn ) { + + return function () { + + fn.apply( scope, arguments ); + + }; + + } + + this.handleResize(); + +}; diff --git a/node_modules/three/examples/js/controls/FlyControls.js b/node_modules/three/examples/js/controls/FlyControls.js new file mode 100644 index 00000000..0b5a65d2 --- /dev/null +++ b/node_modules/three/examples/js/controls/FlyControls.js @@ -0,0 +1,293 @@ +/** + * @author James Baicoianu / http://www.baicoianu.com/ + */ + +THREE.FlyControls = function ( object, domElement ) { + + this.object = object; + + this.domElement = ( domElement !== undefined ) ? domElement : document; + if ( domElement ) this.domElement.setAttribute( 'tabindex', - 1 ); + + // API + + this.movementSpeed = 1.0; + this.rollSpeed = 0.005; + + this.dragToLook = false; + this.autoForward = false; + + // disable default target object behavior + + // internals + + this.tmpQuaternion = new THREE.Quaternion(); + + this.mouseStatus = 0; + + this.moveState = { up: 0, down: 0, left: 0, right: 0, forward: 0, back: 0, pitchUp: 0, pitchDown: 0, yawLeft: 0, yawRight: 0, rollLeft: 0, rollRight: 0 }; + this.moveVector = new THREE.Vector3( 0, 0, 0 ); + this.rotationVector = new THREE.Vector3( 0, 0, 0 ); + + this.handleEvent = function ( event ) { + + if ( typeof this[ event.type ] == 'function' ) { + + this[ event.type ]( event ); + + } + + }; + + this.keydown = function( event ) { + + if ( event.altKey ) { + + return; + + } + + //event.preventDefault(); + + switch ( event.keyCode ) { + + case 16: /* shift */ this.movementSpeedMultiplier = .1; break; + + case 87: /*W*/ this.moveState.forward = 1; break; + case 83: /*S*/ this.moveState.back = 1; break; + + case 65: /*A*/ this.moveState.left = 1; break; + case 68: /*D*/ this.moveState.right = 1; break; + + case 82: /*R*/ this.moveState.up = 1; break; + case 70: /*F*/ this.moveState.down = 1; break; + + case 38: /*up*/ this.moveState.pitchUp = 1; break; + case 40: /*down*/ this.moveState.pitchDown = 1; break; + + case 37: /*left*/ this.moveState.yawLeft = 1; break; + case 39: /*right*/ this.moveState.yawRight = 1; break; + + case 81: /*Q*/ this.moveState.rollLeft = 1; break; + case 69: /*E*/ this.moveState.rollRight = 1; break; + + } + + this.updateMovementVector(); + this.updateRotationVector(); + + }; + + this.keyup = function( event ) { + + switch ( event.keyCode ) { + + case 16: /* shift */ this.movementSpeedMultiplier = 1; break; + + case 87: /*W*/ this.moveState.forward = 0; break; + case 83: /*S*/ this.moveState.back = 0; break; + + case 65: /*A*/ this.moveState.left = 0; break; + case 68: /*D*/ this.moveState.right = 0; break; + + case 82: /*R*/ this.moveState.up = 0; break; + case 70: /*F*/ this.moveState.down = 0; break; + + case 38: /*up*/ this.moveState.pitchUp = 0; break; + case 40: /*down*/ this.moveState.pitchDown = 0; break; + + case 37: /*left*/ this.moveState.yawLeft = 0; break; + case 39: /*right*/ this.moveState.yawRight = 0; break; + + case 81: /*Q*/ this.moveState.rollLeft = 0; break; + case 69: /*E*/ this.moveState.rollRight = 0; break; + + } + + this.updateMovementVector(); + this.updateRotationVector(); + + }; + + this.mousedown = function( event ) { + + if ( this.domElement !== document ) { + + this.domElement.focus(); + + } + + event.preventDefault(); + event.stopPropagation(); + + if ( this.dragToLook ) { + + this.mouseStatus ++; + + } else { + + switch ( event.button ) { + + case 0: this.moveState.forward = 1; break; + case 2: this.moveState.back = 1; break; + + } + + this.updateMovementVector(); + + } + + }; + + this.mousemove = function( event ) { + + if ( ! this.dragToLook || this.mouseStatus > 0 ) { + + var container = this.getContainerDimensions(); + var halfWidth = container.size[ 0 ] / 2; + var halfHeight = container.size[ 1 ] / 2; + + this.moveState.yawLeft = - ( ( event.pageX - container.offset[ 0 ] ) - halfWidth ) / halfWidth; + this.moveState.pitchDown = ( ( event.pageY - container.offset[ 1 ] ) - halfHeight ) / halfHeight; + + this.updateRotationVector(); + + } + + }; + + this.mouseup = function( event ) { + + event.preventDefault(); + event.stopPropagation(); + + if ( this.dragToLook ) { + + this.mouseStatus --; + + this.moveState.yawLeft = this.moveState.pitchDown = 0; + + } else { + + switch ( event.button ) { + + case 0: this.moveState.forward = 0; break; + case 2: this.moveState.back = 0; break; + + } + + this.updateMovementVector(); + + } + + this.updateRotationVector(); + + }; + + this.update = function( delta ) { + + var moveMult = delta * this.movementSpeed; + var rotMult = delta * this.rollSpeed; + + this.object.translateX( this.moveVector.x * moveMult ); + this.object.translateY( this.moveVector.y * moveMult ); + this.object.translateZ( this.moveVector.z * moveMult ); + + this.tmpQuaternion.set( this.rotationVector.x * rotMult, this.rotationVector.y * rotMult, this.rotationVector.z * rotMult, 1 ).normalize(); + this.object.quaternion.multiply( this.tmpQuaternion ); + + // expose the rotation vector for convenience + this.object.rotation.setFromQuaternion( this.object.quaternion, this.object.rotation.order ); + + + }; + + this.updateMovementVector = function() { + + var forward = ( this.moveState.forward || ( this.autoForward && ! this.moveState.back ) ) ? 1 : 0; + + this.moveVector.x = ( - this.moveState.left + this.moveState.right ); + this.moveVector.y = ( - this.moveState.down + this.moveState.up ); + this.moveVector.z = ( - forward + this.moveState.back ); + + //console.log( 'move:', [ this.moveVector.x, this.moveVector.y, this.moveVector.z ] ); + + }; + + this.updateRotationVector = function() { + + this.rotationVector.x = ( - this.moveState.pitchDown + this.moveState.pitchUp ); + this.rotationVector.y = ( - this.moveState.yawRight + this.moveState.yawLeft ); + this.rotationVector.z = ( - this.moveState.rollRight + this.moveState.rollLeft ); + + //console.log( 'rotate:', [ this.rotationVector.x, this.rotationVector.y, this.rotationVector.z ] ); + + }; + + this.getContainerDimensions = function() { + + if ( this.domElement != document ) { + + return { + size : [ this.domElement.offsetWidth, this.domElement.offsetHeight ], + offset : [ this.domElement.offsetLeft, this.domElement.offsetTop ] + }; + + } else { + + return { + size : [ window.innerWidth, window.innerHeight ], + offset : [ 0, 0 ] + }; + + } + + }; + + function bind( scope, fn ) { + + return function () { + + fn.apply( scope, arguments ); + + }; + + } + + function contextmenu( event ) { + + event.preventDefault(); + + } + + this.dispose = function() { + + this.domElement.removeEventListener( 'contextmenu', contextmenu, false ); + this.domElement.removeEventListener( 'mousedown', _mousedown, false ); + this.domElement.removeEventListener( 'mousemove', _mousemove, false ); + this.domElement.removeEventListener( 'mouseup', _mouseup, false ); + + window.removeEventListener( 'keydown', _keydown, false ); + window.removeEventListener( 'keyup', _keyup, false ); + + } + + var _mousemove = bind( this, this.mousemove ); + var _mousedown = bind( this, this.mousedown ); + var _mouseup = bind( this, this.mouseup ); + var _keydown = bind( this, this.keydown ); + var _keyup = bind( this, this.keyup ); + + this.domElement.addEventListener( 'contextmenu', contextmenu, false ); + + this.domElement.addEventListener( 'mousemove', _mousemove, false ); + this.domElement.addEventListener( 'mousedown', _mousedown, false ); + this.domElement.addEventListener( 'mouseup', _mouseup, false ); + + window.addEventListener( 'keydown', _keydown, false ); + window.addEventListener( 'keyup', _keyup, false ); + + this.updateMovementVector(); + this.updateRotationVector(); + +}; diff --git a/node_modules/three/examples/js/controls/MouseControls.js b/node_modules/three/examples/js/controls/MouseControls.js new file mode 100644 index 00000000..be9d0e96 --- /dev/null +++ b/node_modules/three/examples/js/controls/MouseControls.js @@ -0,0 +1,61 @@ +/** + * @author dmarcos / http://github.com/dmarcos + * + * This controls allow to change the orientation of the camera using the mouse + */ + +THREE.MouseControls = function ( object ) { + + var scope = this; + var PI_2 = Math.PI / 2; + var mouseQuat = { + x: new THREE.Quaternion(), + y: new THREE.Quaternion() + }; + var object = object; + var xVector = new THREE.Vector3( 1, 0, 0 ); + var yVector = new THREE.Vector3( 0, 1, 0 ); + + var onMouseMove = function ( event ) { + + if ( scope.enabled === false ) return; + + var orientation = scope.orientation; + + var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0; + var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0; + + orientation.y += movementX * 0.0025; + orientation.x += movementY * 0.0025; + + orientation.x = Math.max( - PI_2, Math.min( PI_2, orientation.x ) ); + + }; + + this.enabled = true; + + this.orientation = { + x: 0, + y: 0, + }; + + this.update = function() { + + if ( this.enabled === false ) return; + + mouseQuat.x.setFromAxisAngle( xVector, this.orientation.x ); + mouseQuat.y.setFromAxisAngle( yVector, this.orientation.y ); + object.quaternion.copy( mouseQuat.y ).multiply( mouseQuat.x ); + return; + + }; + + this.dispose = function() { + + document.removeEventListener( 'mousemove', onMouseMove, false ); + + } + + document.addEventListener( 'mousemove', onMouseMove, false ); + +}; diff --git a/node_modules/three/examples/js/controls/OrbitControls.js b/node_modules/three/examples/js/controls/OrbitControls.js new file mode 100644 index 00000000..adcc74d0 --- /dev/null +++ b/node_modules/three/examples/js/controls/OrbitControls.js @@ -0,0 +1,1115 @@ +/** + * @author qiao / https://github.com/qiao + * @author mrdoob / http://mrdoob.com + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author erich666 / http://erichaines.com + */ +/*global THREE, console */ + +( function () { + + function OrbitConstraint ( object ) { + + this.object = object; + + // "target" sets the location of focus, where the object orbits around + // and where it pans with respect to. + this.target = new THREE.Vector3(); + + // Limits to how far you can dolly in and out ( PerspectiveCamera only ) + this.minDistance = 0; + this.maxDistance = Infinity; + + // Limits to how far you can zoom in and out ( OrthographicCamera only ) + this.minZoom = 0; + this.maxZoom = Infinity; + + // How far you can orbit vertically, upper and lower limits. + // Range is 0 to Math.PI radians. + this.minPolarAngle = 0; // radians + this.maxPolarAngle = Math.PI; // radians + + // How far you can orbit horizontally, upper and lower limits. + // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. + this.minAzimuthAngle = - Infinity; // radians + this.maxAzimuthAngle = Infinity; // radians + + // Set to true to enable damping (inertia) + // If damping is enabled, you must call controls.update() in your animation loop + this.enableDamping = false; + this.dampingFactor = 0.25; + + //////////// + // internals + + var scope = this; + + var EPS = 0.000001; + + // Current position in spherical coordinate system. + var theta; + var phi; + + // Pending changes + var phiDelta = 0; + var thetaDelta = 0; + var scale = 1; + var panOffset = new THREE.Vector3(); + var zoomChanged = false; + + // API + + this.getPolarAngle = function () { + + return phi; + + }; + + this.getAzimuthalAngle = function () { + + return theta; + + }; + + this.rotateLeft = function ( angle ) { + + thetaDelta -= angle; + + }; + + this.rotateUp = function ( angle ) { + + phiDelta -= angle; + + }; + + // pass in distance in world space to move left + this.panLeft = function() { + + var v = new THREE.Vector3(); + + return function panLeft ( distance ) { + + var te = this.object.matrix.elements; + + // get X column of matrix + v.set( te[ 0 ], te[ 1 ], te[ 2 ] ); + v.multiplyScalar( - distance ); + + panOffset.add( v ); + + }; + + }(); + + // pass in distance in world space to move up + this.panUp = function() { + + var v = new THREE.Vector3(); + + return function panUp ( distance ) { + + var te = this.object.matrix.elements; + + // get Y column of matrix + v.set( te[ 4 ], te[ 5 ], te[ 6 ] ); + v.multiplyScalar( distance ); + + panOffset.add( v ); + + }; + + }(); + + // pass in x,y of change desired in pixel space, + // right and down are positive + this.pan = function ( deltaX, deltaY, screenWidth, screenHeight ) { + + if ( scope.object instanceof THREE.PerspectiveCamera ) { + + // perspective + var position = scope.object.position; + var offset = position.clone().sub( scope.target ); + var targetDistance = offset.length(); + + // half of the fov is center to top of screen + targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); + + // we actually don't use screenWidth, since perspective camera is fixed to screen height + scope.panLeft( 2 * deltaX * targetDistance / screenHeight ); + scope.panUp( 2 * deltaY * targetDistance / screenHeight ); + + } else if ( scope.object instanceof THREE.OrthographicCamera ) { + + // orthographic + scope.panLeft( deltaX * ( scope.object.right - scope.object.left ) / screenWidth ); + scope.panUp( deltaY * ( scope.object.top - scope.object.bottom ) / screenHeight ); + + } else { + + // camera neither orthographic or perspective + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); + + } + + }; + + this.dollyIn = function ( dollyScale ) { + + if ( scope.object instanceof THREE.PerspectiveCamera ) { + + scale /= dollyScale; + + } else if ( scope.object instanceof THREE.OrthographicCamera ) { + + scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom * dollyScale ) ); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); + + } + + }; + + this.dollyOut = function ( dollyScale ) { + + if ( scope.object instanceof THREE.PerspectiveCamera ) { + + scale *= dollyScale; + + } else if ( scope.object instanceof THREE.OrthographicCamera ) { + + scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom / dollyScale ) ); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); + + } + + }; + + this.update = function() { + + var offset = new THREE.Vector3(); + + // so camera.up is the orbit axis + var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); + var quatInverse = quat.clone().inverse(); + + var lastPosition = new THREE.Vector3(); + var lastQuaternion = new THREE.Quaternion(); + + return function () { + + var position = this.object.position; + + offset.copy( position ).sub( this.target ); + + // rotate offset to "y-axis-is-up" space + offset.applyQuaternion( quat ); + + // angle from z-axis around y-axis + + theta = Math.atan2( offset.x, offset.z ); + + // angle from y-axis + + phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); + + theta += thetaDelta; + phi += phiDelta; + + // restrict theta to be between desired limits + theta = Math.max( this.minAzimuthAngle, Math.min( this.maxAzimuthAngle, theta ) ); + + // restrict phi to be between desired limits + phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); + + // restrict phi to be betwee EPS and PI-EPS + phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); + + var radius = offset.length() * scale; + + // restrict radius to be between desired limits + radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); + + // move target to panned location + this.target.add( panOffset ); + + offset.x = radius * Math.sin( phi ) * Math.sin( theta ); + offset.y = radius * Math.cos( phi ); + offset.z = radius * Math.sin( phi ) * Math.cos( theta ); + + // rotate offset back to "camera-up-vector-is-up" space + offset.applyQuaternion( quatInverse ); + + position.copy( this.target ).add( offset ); + + this.object.lookAt( this.target ); + + if ( this.enableDamping === true ) { + + thetaDelta *= ( 1 - this.dampingFactor ); + phiDelta *= ( 1 - this.dampingFactor ); + + } else { + + thetaDelta = 0; + phiDelta = 0; + + } + + scale = 1; + panOffset.set( 0, 0, 0 ); + + // update condition is: + // min(camera displacement, camera rotation in radians)^2 > EPS + // using small-angle approximation cos(x/2) = 1 - x^2 / 8 + + if ( zoomChanged || + lastPosition.distanceToSquared( this.object.position ) > EPS || + 8 * ( 1 - lastQuaternion.dot( this.object.quaternion ) ) > EPS ) { + + lastPosition.copy( this.object.position ); + lastQuaternion.copy( this.object.quaternion ); + zoomChanged = false; + + return true; + + } + + return false; + + }; + + }(); + + }; + + + // This set of controls performs orbiting, dollying (zooming), and panning. It maintains + // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is + // supported. + // + // Orbit - left mouse / touch: one finger move + // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish + // Pan - right mouse, or arrow keys / touch: three finter swipe + + THREE.OrbitControls = function ( object, domElement ) { + + var constraint = new OrbitConstraint( object ); + + this.domElement = ( domElement !== undefined ) ? domElement : document; + + // API + + Object.defineProperty( this, 'constraint', { + + get: function() { + + return constraint; + + } + + } ); + + this.getPolarAngle = function () { + + return constraint.getPolarAngle(); + + }; + + this.getAzimuthalAngle = function () { + + return constraint.getAzimuthalAngle(); + + }; + + // Set to false to disable this control + this.enabled = true; + + // center is old, deprecated; use "target" instead + this.center = this.target; + + // This option actually enables dollying in and out; left as "zoom" for + // backwards compatibility. + // Set to false to disable zooming + this.enableZoom = true; + this.zoomSpeed = 1.0; + + // Set to false to disable rotating + this.enableRotate = true; + this.rotateSpeed = 1.0; + + // Set to false to disable panning + this.enablePan = true; + this.keyPanSpeed = 7.0; // pixels moved per arrow key push + + // Set to true to automatically rotate around the target + // If auto-rotate is enabled, you must call controls.update() in your animation loop + this.autoRotate = false; + this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 + + // Set to false to disable use of the keys + this.enableKeys = true; + + // The four arrow keys + this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; + + // Mouse buttons + this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT }; + + //////////// + // internals + + var scope = this; + + var rotateStart = new THREE.Vector2(); + var rotateEnd = new THREE.Vector2(); + var rotateDelta = new THREE.Vector2(); + + var panStart = new THREE.Vector2(); + var panEnd = new THREE.Vector2(); + var panDelta = new THREE.Vector2(); + + var dollyStart = new THREE.Vector2(); + var dollyEnd = new THREE.Vector2(); + var dollyDelta = new THREE.Vector2(); + + var STATE = { NONE : - 1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; + + var state = STATE.NONE; + + // for reset + + this.target0 = this.target.clone(); + this.position0 = this.object.position.clone(); + this.zoom0 = this.object.zoom; + + // events + + var changeEvent = { type: 'change' }; + var startEvent = { type: 'start' }; + var endEvent = { type: 'end' }; + + // pass in x,y of change desired in pixel space, + // right and down are positive + function pan( deltaX, deltaY ) { + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + constraint.pan( deltaX, deltaY, element.clientWidth, element.clientHeight ); + + } + + this.update = function () { + + if ( this.autoRotate && state === STATE.NONE ) { + + constraint.rotateLeft( getAutoRotationAngle() ); + + } + + if ( constraint.update() === true ) { + + this.dispatchEvent( changeEvent ); + + } + + }; + + this.reset = function () { + + state = STATE.NONE; + + this.target.copy( this.target0 ); + this.object.position.copy( this.position0 ); + this.object.zoom = this.zoom0; + + this.object.updateProjectionMatrix(); + this.dispatchEvent( changeEvent ); + + this.update(); + + }; + + function getAutoRotationAngle() { + + return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; + + } + + function getZoomScale() { + + return Math.pow( 0.95, scope.zoomSpeed ); + + } + + function onMouseDown( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + if ( event.button === scope.mouseButtons.ORBIT ) { + + if ( scope.enableRotate === false ) return; + + state = STATE.ROTATE; + + rotateStart.set( event.clientX, event.clientY ); + + } else if ( event.button === scope.mouseButtons.ZOOM ) { + + if ( scope.enableZoom === false ) return; + + state = STATE.DOLLY; + + dollyStart.set( event.clientX, event.clientY ); + + } else if ( event.button === scope.mouseButtons.PAN ) { + + if ( scope.enablePan === false ) return; + + state = STATE.PAN; + + panStart.set( event.clientX, event.clientY ); + + } + + if ( state !== STATE.NONE ) { + + document.addEventListener( 'mousemove', onMouseMove, false ); + document.addEventListener( 'mouseup', onMouseUp, false ); + scope.dispatchEvent( startEvent ); + + } + + } + + function onMouseMove( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + if ( state === STATE.ROTATE ) { + + if ( scope.enableRotate === false ) return; + + rotateEnd.set( event.clientX, event.clientY ); + rotateDelta.subVectors( rotateEnd, rotateStart ); + + // rotating across whole screen goes 360 degrees around + constraint.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); + + // rotating up and down along whole screen attempts to go 360, but limited to 180 + constraint.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); + + rotateStart.copy( rotateEnd ); + + } else if ( state === STATE.DOLLY ) { + + if ( scope.enableZoom === false ) return; + + dollyEnd.set( event.clientX, event.clientY ); + dollyDelta.subVectors( dollyEnd, dollyStart ); + + if ( dollyDelta.y > 0 ) { + + constraint.dollyIn( getZoomScale() ); + + } else if ( dollyDelta.y < 0 ) { + + constraint.dollyOut( getZoomScale() ); + + } + + dollyStart.copy( dollyEnd ); + + } else if ( state === STATE.PAN ) { + + if ( scope.enablePan === false ) return; + + panEnd.set( event.clientX, event.clientY ); + panDelta.subVectors( panEnd, panStart ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + } + + if ( state !== STATE.NONE ) scope.update(); + + } + + function onMouseUp( /* event */ ) { + + if ( scope.enabled === false ) return; + + document.removeEventListener( 'mousemove', onMouseMove, false ); + document.removeEventListener( 'mouseup', onMouseUp, false ); + scope.dispatchEvent( endEvent ); + state = STATE.NONE; + + } + + function onMouseWheel( event ) { + + if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return; + + event.preventDefault(); + event.stopPropagation(); + + var delta = 0; + + if ( event.wheelDelta !== undefined ) { + + // WebKit / Opera / Explorer 9 + + delta = event.wheelDelta; + + } else if ( event.detail !== undefined ) { + + // Firefox + + delta = - event.detail; + + } + + if ( delta > 0 ) { + + constraint.dollyOut( getZoomScale() ); + + } else if ( delta < 0 ) { + + constraint.dollyIn( getZoomScale() ); + + } + + scope.update(); + scope.dispatchEvent( startEvent ); + scope.dispatchEvent( endEvent ); + + } + + function onKeyDown( event ) { + + if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return; + + switch ( event.keyCode ) { + + case scope.keys.UP: + pan( 0, scope.keyPanSpeed ); + scope.update(); + break; + + case scope.keys.BOTTOM: + pan( 0, - scope.keyPanSpeed ); + scope.update(); + break; + + case scope.keys.LEFT: + pan( scope.keyPanSpeed, 0 ); + scope.update(); + break; + + case scope.keys.RIGHT: + pan( - scope.keyPanSpeed, 0 ); + scope.update(); + break; + + } + + } + + function touchstart( event ) { + + if ( scope.enabled === false ) return; + + switch ( event.touches.length ) { + + case 1: // one-fingered touch: rotate + + if ( scope.enableRotate === false ) return; + + state = STATE.TOUCH_ROTATE; + + rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + break; + + case 2: // two-fingered touch: dolly + + if ( scope.enableZoom === false ) return; + + state = STATE.TOUCH_DOLLY; + + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + var distance = Math.sqrt( dx * dx + dy * dy ); + dollyStart.set( 0, distance ); + break; + + case 3: // three-fingered touch: pan + + if ( scope.enablePan === false ) return; + + state = STATE.TOUCH_PAN; + + panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + break; + + default: + + state = STATE.NONE; + + } + + if ( state !== STATE.NONE ) scope.dispatchEvent( startEvent ); + + } + + function touchmove( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + switch ( event.touches.length ) { + + case 1: // one-fingered touch: rotate + + if ( scope.enableRotate === false ) return; + if ( state !== STATE.TOUCH_ROTATE ) return; + + rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + rotateDelta.subVectors( rotateEnd, rotateStart ); + + // rotating across whole screen goes 360 degrees around + constraint.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); + // rotating up and down along whole screen attempts to go 360, but limited to 180 + constraint.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); + + rotateStart.copy( rotateEnd ); + + scope.update(); + break; + + case 2: // two-fingered touch: dolly + + if ( scope.enableZoom === false ) return; + if ( state !== STATE.TOUCH_DOLLY ) return; + + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + var distance = Math.sqrt( dx * dx + dy * dy ); + + dollyEnd.set( 0, distance ); + dollyDelta.subVectors( dollyEnd, dollyStart ); + + if ( dollyDelta.y > 0 ) { + + constraint.dollyOut( getZoomScale() ); + + } else if ( dollyDelta.y < 0 ) { + + constraint.dollyIn( getZoomScale() ); + + } + + dollyStart.copy( dollyEnd ); + + scope.update(); + break; + + case 3: // three-fingered touch: pan + + if ( scope.enablePan === false ) return; + if ( state !== STATE.TOUCH_PAN ) return; + + panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + panDelta.subVectors( panEnd, panStart ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + scope.update(); + break; + + default: + + state = STATE.NONE; + + } + + } + + function touchend( /* event */ ) { + + if ( scope.enabled === false ) return; + + scope.dispatchEvent( endEvent ); + state = STATE.NONE; + + } + + function contextmenu( event ) { + + event.preventDefault(); + + } + + this.dispose = function() { + + this.domElement.removeEventListener( 'contextmenu', contextmenu, false ); + this.domElement.removeEventListener( 'mousedown', onMouseDown, false ); + this.domElement.removeEventListener( 'mousewheel', onMouseWheel, false ); + this.domElement.removeEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox + + this.domElement.removeEventListener( 'touchstart', touchstart, false ); + this.domElement.removeEventListener( 'touchend', touchend, false ); + this.domElement.removeEventListener( 'touchmove', touchmove, false ); + + document.removeEventListener( 'mousemove', onMouseMove, false ); + document.removeEventListener( 'mouseup', onMouseUp, false ); + + window.removeEventListener( 'keydown', onKeyDown, false ); + + } + + this.domElement.addEventListener( 'contextmenu', contextmenu, false ); + + this.domElement.addEventListener( 'mousedown', onMouseDown, false ); + this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); + this.domElement.addEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox + + this.domElement.addEventListener( 'touchstart', touchstart, false ); + this.domElement.addEventListener( 'touchend', touchend, false ); + this.domElement.addEventListener( 'touchmove', touchmove, false ); + + window.addEventListener( 'keydown', onKeyDown, false ); + + // force an update at start + this.update(); + + }; + + THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); + THREE.OrbitControls.prototype.constructor = THREE.OrbitControls; + + Object.defineProperties( THREE.OrbitControls.prototype, { + + object: { + + get: function () { + + return this.constraint.object; + + } + + }, + + target: { + + get: function () { + + return this.constraint.target; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: target is now immutable. Use target.set() instead.' ); + this.constraint.target.copy( value ); + + } + + }, + + minDistance : { + + get: function () { + + return this.constraint.minDistance; + + }, + + set: function ( value ) { + + this.constraint.minDistance = value; + + } + + }, + + maxDistance : { + + get: function () { + + return this.constraint.maxDistance; + + }, + + set: function ( value ) { + + this.constraint.maxDistance = value; + + } + + }, + + minZoom : { + + get: function () { + + return this.constraint.minZoom; + + }, + + set: function ( value ) { + + this.constraint.minZoom = value; + + } + + }, + + maxZoom : { + + get: function () { + + return this.constraint.maxZoom; + + }, + + set: function ( value ) { + + this.constraint.maxZoom = value; + + } + + }, + + minPolarAngle : { + + get: function () { + + return this.constraint.minPolarAngle; + + }, + + set: function ( value ) { + + this.constraint.minPolarAngle = value; + + } + + }, + + maxPolarAngle : { + + get: function () { + + return this.constraint.maxPolarAngle; + + }, + + set: function ( value ) { + + this.constraint.maxPolarAngle = value; + + } + + }, + + minAzimuthAngle : { + + get: function () { + + return this.constraint.minAzimuthAngle; + + }, + + set: function ( value ) { + + this.constraint.minAzimuthAngle = value; + + } + + }, + + maxAzimuthAngle : { + + get: function () { + + return this.constraint.maxAzimuthAngle; + + }, + + set: function ( value ) { + + this.constraint.maxAzimuthAngle = value; + + } + + }, + + enableDamping : { + + get: function () { + + return this.constraint.enableDamping; + + }, + + set: function ( value ) { + + this.constraint.enableDamping = value; + + } + + }, + + dampingFactor : { + + get: function () { + + return this.constraint.dampingFactor; + + }, + + set: function ( value ) { + + this.constraint.dampingFactor = value; + + } + + }, + + // backward compatibility + + noZoom: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); + return ! this.enableZoom; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); + this.enableZoom = ! value; + + } + + }, + + noRotate: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); + return ! this.enableRotate; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); + this.enableRotate = ! value; + + } + + }, + + noPan: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); + return ! this.enablePan; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); + this.enablePan = ! value; + + } + + }, + + noKeys: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); + return ! this.enableKeys; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); + this.enableKeys = ! value; + + } + + }, + + staticMoving : { + + get: function () { + + console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); + return ! this.constraint.enableDamping; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); + this.constraint.enableDamping = ! value; + + } + + }, + + dynamicDampingFactor : { + + get: function () { + + console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); + return this.constraint.dampingFactor; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); + this.constraint.dampingFactor = value; + + } + + } + + } ); + +}() ); diff --git a/node_modules/three/examples/js/controls/OrthographicTrackballControls.js b/node_modules/three/examples/js/controls/OrthographicTrackballControls.js new file mode 100644 index 00000000..d5d86b91 --- /dev/null +++ b/node_modules/three/examples/js/controls/OrthographicTrackballControls.js @@ -0,0 +1,655 @@ +/** + * @author Eberhard Graether / http://egraether.com/ + * @author Mark Lundin / http://mark-lundin.com + * @author Patrick Fuller / http://patrick-fuller.com + * @author Max Smolens / https://github.com/msmolens + */ + +THREE.OrthographicTrackballControls = function ( object, domElement ) { + + var _this = this; + var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 }; + + this.object = object; + this.domElement = ( domElement !== undefined ) ? domElement : document; + + // API + + this.enabled = true; + + this.screen = { left: 0, top: 0, width: 0, height: 0 }; + + this.radius = 0; + + this.rotateSpeed = 1.0; + this.zoomSpeed = 1.2; + + this.noRotate = false; + this.noZoom = false; + this.noPan = false; + this.noRoll = false; + + this.staticMoving = false; + this.dynamicDampingFactor = 0.2; + + this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ]; + + // internals + + this.target = new THREE.Vector3(); + + var EPS = 0.000001; + + var _changed = true; + + var _state = STATE.NONE, + _prevState = STATE.NONE, + + _eye = new THREE.Vector3(), + + _rotateStart = new THREE.Vector3(), + _rotateEnd = new THREE.Vector3(), + + _zoomStart = new THREE.Vector2(), + _zoomEnd = new THREE.Vector2(), + + _touchZoomDistanceStart = 0, + _touchZoomDistanceEnd = 0, + + _panStart = new THREE.Vector2(), + _panEnd = new THREE.Vector2(); + + // for reset + + this.target0 = this.target.clone(); + this.position0 = this.object.position.clone(); + this.up0 = this.object.up.clone(); + + this.left0 = this.object.left; + this.right0 = this.object.right; + this.top0 = this.object.top; + this.bottom0 = this.object.bottom; + + // events + + var changeEvent = { type: 'change' }; + var startEvent = { type: 'start' }; + var endEvent = { type: 'end' }; + + + // methods + + this.handleResize = function () { + + if ( this.domElement === document ) { + + this.screen.left = 0; + this.screen.top = 0; + this.screen.width = window.innerWidth; + this.screen.height = window.innerHeight; + + } else { + + var box = this.domElement.getBoundingClientRect(); + // adjustments come from similar code in the jquery offset() function + var d = this.domElement.ownerDocument.documentElement; + this.screen.left = box.left + window.pageXOffset - d.clientLeft; + this.screen.top = box.top + window.pageYOffset - d.clientTop; + this.screen.width = box.width; + this.screen.height = box.height; + + } + + this.radius = 0.5 * Math.min( this.screen.width, this.screen.height ); + + this.left0 = this.object.left; + this.right0 = this.object.right; + this.top0 = this.object.top; + this.bottom0 = this.object.bottom; + + }; + + this.handleEvent = function ( event ) { + + if ( typeof this[ event.type ] == 'function' ) { + + this[ event.type ]( event ); + + } + + }; + + var getMouseOnScreen = ( function () { + + var vector = new THREE.Vector2(); + + return function getMouseOnScreen( pageX, pageY ) { + + vector.set( + ( pageX - _this.screen.left ) / _this.screen.width, + ( pageY - _this.screen.top ) / _this.screen.height + ); + + return vector; + + }; + + }() ); + + var getMouseProjectionOnBall = ( function () { + + var vector = new THREE.Vector3(); + var objectUp = new THREE.Vector3(); + var mouseOnBall = new THREE.Vector3(); + + return function getMouseProjectionOnBall( pageX, pageY ) { + + mouseOnBall.set( + ( pageX - _this.screen.width * 0.5 - _this.screen.left ) / _this.radius, + ( _this.screen.height * 0.5 + _this.screen.top - pageY ) / _this.radius, + 0.0 + ); + + var length = mouseOnBall.length(); + + if ( _this.noRoll ) { + + if ( length < Math.SQRT1_2 ) { + + mouseOnBall.z = Math.sqrt( 1.0 - length * length ); + + } else { + + mouseOnBall.z = .5 / length; + + } + + } else if ( length > 1.0 ) { + + mouseOnBall.normalize(); + + } else { + + mouseOnBall.z = Math.sqrt( 1.0 - length * length ); + + } + + _eye.copy( _this.object.position ).sub( _this.target ); + + vector.copy( _this.object.up ).setLength( mouseOnBall.y ); + vector.add( objectUp.copy( _this.object.up ).cross( _eye ).setLength( mouseOnBall.x ) ); + vector.add( _eye.setLength( mouseOnBall.z ) ); + + return vector; + + }; + + }() ); + + this.rotateCamera = ( function() { + + var axis = new THREE.Vector3(), + quaternion = new THREE.Quaternion(); + + + return function rotateCamera() { + + var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() ); + + if ( angle ) { + + axis.crossVectors( _rotateStart, _rotateEnd ).normalize(); + + angle *= _this.rotateSpeed; + + quaternion.setFromAxisAngle( axis, - angle ); + + _eye.applyQuaternion( quaternion ); + _this.object.up.applyQuaternion( quaternion ); + + _rotateEnd.applyQuaternion( quaternion ); + + if ( _this.staticMoving ) { + + _rotateStart.copy( _rotateEnd ); + + } else { + + quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) ); + _rotateStart.applyQuaternion( quaternion ); + + } + + _changed = true; + + } + + } + + }() ); + + this.zoomCamera = function () { + + if ( _state === STATE.TOUCH_ZOOM_PAN ) { + + var factor = _touchZoomDistanceEnd / _touchZoomDistanceStart; + _touchZoomDistanceStart = _touchZoomDistanceEnd; + + _this.object.zoom *= factor; + + _changed = true; + + } else { + + var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed; + + if ( Math.abs( factor - 1.0 ) > EPS && factor > 0.0 ) { + + _this.object.zoom /= factor; + + if ( _this.staticMoving ) { + + _zoomStart.copy( _zoomEnd ); + + } else { + + _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor; + + } + + _changed = true; + + } + + } + + }; + + this.panCamera = ( function() { + + var mouseChange = new THREE.Vector2(), + objectUp = new THREE.Vector3(), + pan = new THREE.Vector3(); + + return function panCamera() { + + mouseChange.copy( _panEnd ).sub( _panStart ); + + if ( mouseChange.lengthSq() ) { + + // Scale movement to keep clicked/dragged position under cursor + var scale_x = ( _this.object.right - _this.object.left ) / _this.object.zoom; + var scale_y = ( _this.object.top - _this.object.bottom ) / _this.object.zoom; + mouseChange.x *= scale_x; + mouseChange.y *= scale_y; + + pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x ); + pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) ); + + _this.object.position.add( pan ); + _this.target.add( pan ); + + if ( _this.staticMoving ) { + + _panStart.copy( _panEnd ); + + } else { + + _panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) ); + + } + + _changed = true; + + } + + } + + }() ); + + this.update = function () { + + _eye.subVectors( _this.object.position, _this.target ); + + if ( ! _this.noRotate ) { + + _this.rotateCamera(); + + } + + if ( ! _this.noZoom ) { + + _this.zoomCamera(); + + if ( _changed ) { + + _this.object.updateProjectionMatrix(); + + } + + } + + if ( ! _this.noPan ) { + + _this.panCamera(); + + } + + _this.object.position.addVectors( _this.target, _eye ); + + _this.object.lookAt( _this.target ); + + if ( _changed ) { + + _this.dispatchEvent( changeEvent ); + + _changed = false; + + } + + }; + + this.reset = function () { + + _state = STATE.NONE; + _prevState = STATE.NONE; + + _this.target.copy( _this.target0 ); + _this.object.position.copy( _this.position0 ); + _this.object.up.copy( _this.up0 ); + + _eye.subVectors( _this.object.position, _this.target ); + + _this.object.left = _this.left0; + _this.object.right = _this.right0; + _this.object.top = _this.top0; + _this.object.bottom = _this.bottom0; + + _this.object.lookAt( _this.target ); + + _this.dispatchEvent( changeEvent ); + + _changed = false; + + }; + + // listeners + + function keydown( event ) { + + if ( _this.enabled === false ) return; + + window.removeEventListener( 'keydown', keydown ); + + _prevState = _state; + + if ( _state !== STATE.NONE ) { + + return; + + } else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && ! _this.noRotate ) { + + _state = STATE.ROTATE; + + } else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && ! _this.noZoom ) { + + _state = STATE.ZOOM; + + } else if ( event.keyCode === _this.keys[ STATE.PAN ] && ! _this.noPan ) { + + _state = STATE.PAN; + + } + + } + + function keyup( event ) { + + if ( _this.enabled === false ) return; + + _state = _prevState; + + window.addEventListener( 'keydown', keydown, false ); + + } + + function mousedown( event ) { + + if ( _this.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + if ( _state === STATE.NONE ) { + + _state = event.button; + + } + + if ( _state === STATE.ROTATE && ! _this.noRotate ) { + + _rotateStart.copy( getMouseProjectionOnBall( event.pageX, event.pageY ) ); + _rotateEnd.copy( _rotateStart ); + + } else if ( _state === STATE.ZOOM && ! _this.noZoom ) { + + _zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) ); + _zoomEnd.copy( _zoomStart ); + + } else if ( _state === STATE.PAN && ! _this.noPan ) { + + _panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) ); + _panEnd.copy( _panStart ) + + } + + document.addEventListener( 'mousemove', mousemove, false ); + document.addEventListener( 'mouseup', mouseup, false ); + + _this.dispatchEvent( startEvent ); + + } + + function mousemove( event ) { + + if ( _this.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + if ( _state === STATE.ROTATE && ! _this.noRotate ) { + + _rotateEnd.copy( getMouseProjectionOnBall( event.pageX, event.pageY ) ); + + } else if ( _state === STATE.ZOOM && ! _this.noZoom ) { + + _zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) ); + + } else if ( _state === STATE.PAN && ! _this.noPan ) { + + _panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) ); + + } + + } + + function mouseup( event ) { + + if ( _this.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + _state = STATE.NONE; + + document.removeEventListener( 'mousemove', mousemove ); + document.removeEventListener( 'mouseup', mouseup ); + _this.dispatchEvent( endEvent ); + + } + + function mousewheel( event ) { + + if ( _this.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + var delta = 0; + + if ( event.wheelDelta ) { + + // WebKit / Opera / Explorer 9 + + delta = event.wheelDelta / 40; + + } else if ( event.detail ) { + + // Firefox + + delta = - event.detail / 3; + + } + + _zoomStart.y += delta * 0.01; + _this.dispatchEvent( startEvent ); + _this.dispatchEvent( endEvent ); + + } + + function touchstart( event ) { + + if ( _this.enabled === false ) return; + + switch ( event.touches.length ) { + + case 1: + _state = STATE.TOUCH_ROTATE; + _rotateStart.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); + _rotateEnd.copy( _rotateStart ); + break; + + case 2: + _state = STATE.TOUCH_ZOOM_PAN; + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy ); + + var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; + var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; + _panStart.copy( getMouseOnScreen( x, y ) ); + _panEnd.copy( _panStart ); + break; + + default: + _state = STATE.NONE; + + } + _this.dispatchEvent( startEvent ); + + } + + function touchmove( event ) { + + if ( _this.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + switch ( event.touches.length ) { + + case 1: + _rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); + break; + + case 2: + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy ); + + var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; + var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; + _panEnd.copy( getMouseOnScreen( x, y ) ); + break; + + default: + _state = STATE.NONE; + + } + + } + + function touchend( event ) { + + if ( _this.enabled === false ) return; + + switch ( event.touches.length ) { + + case 1: + _rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); + _rotateStart.copy( _rotateEnd ); + break; + + case 2: + _touchZoomDistanceStart = _touchZoomDistanceEnd = 0; + + var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; + var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; + _panEnd.copy( getMouseOnScreen( x, y ) ); + _panStart.copy( _panEnd ); + break; + + } + + _state = STATE.NONE; + _this.dispatchEvent( endEvent ); + + } + + function contextmenu( event ) { + + event.preventDefault(); + + } + + this.dispose = function() { + + this.domElement.removeEventListener( 'contextmenu', contextmenu, false ); + this.domElement.removeEventListener( 'mousedown', mousedown, false ); + this.domElement.removeEventListener( 'mousewheel', mousewheel, false ); + this.domElement.removeEventListener( 'MozMousePixelScroll', mousewheel, false ); // firefox + + this.domElement.removeEventListener( 'touchstart', touchstart, false ); + this.domElement.removeEventListener( 'touchend', touchend, false ); + this.domElement.removeEventListener( 'touchmove', touchmove, false ); + + document.removeEventListener( 'mousemove', mousemove, false ); + document.removeEventListener( 'mouseup', mouseup, false ); + + window.removeEventListener( 'keydown', keydown, false ); + window.removeEventListener( 'keyup', keyup, false ); + + } + + + this.domElement.addEventListener( 'contextmenu', contextmenu, false ); + this.domElement.addEventListener( 'mousedown', mousedown, false ); + this.domElement.addEventListener( 'mousewheel', mousewheel, false ); + this.domElement.addEventListener( 'MozMousePixelScroll', mousewheel, false ); // firefox + + this.domElement.addEventListener( 'touchstart', touchstart, false ); + this.domElement.addEventListener( 'touchend', touchend, false ); + this.domElement.addEventListener( 'touchmove', touchmove, false ); + + window.addEventListener( 'keydown', keydown, false ); + window.addEventListener( 'keyup', keyup, false ); + + this.handleResize(); + + // force an update at start + this.update(); + +}; + +THREE.OrthographicTrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype ); +THREE.OrthographicTrackballControls.prototype.constructor = THREE.OrthographicTrackballControls; diff --git a/node_modules/three/examples/js/controls/PointerLockControls.js b/node_modules/three/examples/js/controls/PointerLockControls.js new file mode 100644 index 00000000..a857e7d4 --- /dev/null +++ b/node_modules/three/examples/js/controls/PointerLockControls.js @@ -0,0 +1,69 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.PointerLockControls = function ( camera ) { + + var scope = this; + + camera.rotation.set( 0, 0, 0 ); + + var pitchObject = new THREE.Object3D(); + pitchObject.add( camera ); + + var yawObject = new THREE.Object3D(); + yawObject.position.y = 10; + yawObject.add( pitchObject ); + + var PI_2 = Math.PI / 2; + + var onMouseMove = function ( event ) { + + if ( scope.enabled === false ) return; + + var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0; + var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0; + + yawObject.rotation.y -= movementX * 0.002; + pitchObject.rotation.x -= movementY * 0.002; + + pitchObject.rotation.x = Math.max( - PI_2, Math.min( PI_2, pitchObject.rotation.x ) ); + + }; + + this.dispose = function() { + + document.removeEventListener( 'mousemove', onMouseMove, false ); + + } + + document.addEventListener( 'mousemove', onMouseMove, false ); + + this.enabled = false; + + this.getObject = function () { + + return yawObject; + + }; + + this.getDirection = function() { + + // assumes the camera itself is not rotated + + var direction = new THREE.Vector3( 0, 0, - 1 ); + var rotation = new THREE.Euler( 0, 0, 0, "YXZ" ); + + return function( v ) { + + rotation.set( pitchObject.rotation.x, yawObject.rotation.y, 0 ); + + v.copy( direction ).applyEuler( rotation ); + + return v; + + } + + }(); + +}; diff --git a/node_modules/three/examples/js/controls/TrackballControls.js b/node_modules/three/examples/js/controls/TrackballControls.js new file mode 100644 index 00000000..8ef58f89 --- /dev/null +++ b/node_modules/three/examples/js/controls/TrackballControls.js @@ -0,0 +1,634 @@ +/** + * @author Eberhard Graether / http://egraether.com/ + * @author Mark Lundin / http://mark-lundin.com + * @author Simone Manini / http://daron1337.github.io + * @author Luca Antiga / http://lantiga.github.io + */ + +THREE.TrackballControls = function ( object, domElement ) { + + var _this = this; + var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 }; + + this.object = object; + this.domElement = ( domElement !== undefined ) ? domElement : document; + + // API + + this.enabled = true; + + this.screen = { left: 0, top: 0, width: 0, height: 0 }; + + this.rotateSpeed = 1.0; + this.zoomSpeed = 1.2; + this.panSpeed = 0.3; + + this.noRotate = false; + this.noZoom = false; + this.noPan = false; + + this.staticMoving = false; + this.dynamicDampingFactor = 0.2; + + this.minDistance = 0; + this.maxDistance = Infinity; + + this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ]; + + // internals + + this.target = new THREE.Vector3(); + + var EPS = 0.000001; + + var lastPosition = new THREE.Vector3(); + + var _state = STATE.NONE, + _prevState = STATE.NONE, + + _eye = new THREE.Vector3(), + + _movePrev = new THREE.Vector2(), + _moveCurr = new THREE.Vector2(), + + _lastAxis = new THREE.Vector3(), + _lastAngle = 0, + + _zoomStart = new THREE.Vector2(), + _zoomEnd = new THREE.Vector2(), + + _touchZoomDistanceStart = 0, + _touchZoomDistanceEnd = 0, + + _panStart = new THREE.Vector2(), + _panEnd = new THREE.Vector2(); + + // for reset + + this.target0 = this.target.clone(); + this.position0 = this.object.position.clone(); + this.up0 = this.object.up.clone(); + + // events + + var changeEvent = { type: 'change' }; + var startEvent = { type: 'start' }; + var endEvent = { type: 'end' }; + + + // methods + + this.handleResize = function () { + + if ( this.domElement === document ) { + + this.screen.left = 0; + this.screen.top = 0; + this.screen.width = window.innerWidth; + this.screen.height = window.innerHeight; + + } else { + + var box = this.domElement.getBoundingClientRect(); + // adjustments come from similar code in the jquery offset() function + var d = this.domElement.ownerDocument.documentElement; + this.screen.left = box.left + window.pageXOffset - d.clientLeft; + this.screen.top = box.top + window.pageYOffset - d.clientTop; + this.screen.width = box.width; + this.screen.height = box.height; + + } + + }; + + this.handleEvent = function ( event ) { + + if ( typeof this[ event.type ] == 'function' ) { + + this[ event.type ]( event ); + + } + + }; + + var getMouseOnScreen = ( function () { + + var vector = new THREE.Vector2(); + + return function getMouseOnScreen( pageX, pageY ) { + + vector.set( + ( pageX - _this.screen.left ) / _this.screen.width, + ( pageY - _this.screen.top ) / _this.screen.height + ); + + return vector; + + }; + + }() ); + + var getMouseOnCircle = ( function () { + + var vector = new THREE.Vector2(); + + return function getMouseOnCircle( pageX, pageY ) { + + vector.set( + ( ( pageX - _this.screen.width * 0.5 - _this.screen.left ) / ( _this.screen.width * 0.5 ) ), + ( ( _this.screen.height + 2 * ( _this.screen.top - pageY ) ) / _this.screen.width ) // screen.width intentional + ); + + return vector; + + }; + + }() ); + + this.rotateCamera = ( function() { + + var axis = new THREE.Vector3(), + quaternion = new THREE.Quaternion(), + eyeDirection = new THREE.Vector3(), + objectUpDirection = new THREE.Vector3(), + objectSidewaysDirection = new THREE.Vector3(), + moveDirection = new THREE.Vector3(), + angle; + + return function rotateCamera() { + + moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 ); + angle = moveDirection.length(); + + if ( angle ) { + + _eye.copy( _this.object.position ).sub( _this.target ); + + eyeDirection.copy( _eye ).normalize(); + objectUpDirection.copy( _this.object.up ).normalize(); + objectSidewaysDirection.crossVectors( objectUpDirection, eyeDirection ).normalize(); + + objectUpDirection.setLength( _moveCurr.y - _movePrev.y ); + objectSidewaysDirection.setLength( _moveCurr.x - _movePrev.x ); + + moveDirection.copy( objectUpDirection.add( objectSidewaysDirection ) ); + + axis.crossVectors( moveDirection, _eye ).normalize(); + + angle *= _this.rotateSpeed; + quaternion.setFromAxisAngle( axis, angle ); + + _eye.applyQuaternion( quaternion ); + _this.object.up.applyQuaternion( quaternion ); + + _lastAxis.copy( axis ); + _lastAngle = angle; + + } else if ( ! _this.staticMoving && _lastAngle ) { + + _lastAngle *= Math.sqrt( 1.0 - _this.dynamicDampingFactor ); + _eye.copy( _this.object.position ).sub( _this.target ); + quaternion.setFromAxisAngle( _lastAxis, _lastAngle ); + _eye.applyQuaternion( quaternion ); + _this.object.up.applyQuaternion( quaternion ); + + } + + _movePrev.copy( _moveCurr ); + + }; + + }() ); + + + this.zoomCamera = function () { + + var factor; + + if ( _state === STATE.TOUCH_ZOOM_PAN ) { + + factor = _touchZoomDistanceStart / _touchZoomDistanceEnd; + _touchZoomDistanceStart = _touchZoomDistanceEnd; + _eye.multiplyScalar( factor ); + + } else { + + factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed; + + if ( factor !== 1.0 && factor > 0.0 ) { + + _eye.multiplyScalar( factor ); + + if ( _this.staticMoving ) { + + _zoomStart.copy( _zoomEnd ); + + } else { + + _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor; + + } + + } + + } + + }; + + this.panCamera = ( function() { + + var mouseChange = new THREE.Vector2(), + objectUp = new THREE.Vector3(), + pan = new THREE.Vector3(); + + return function panCamera() { + + mouseChange.copy( _panEnd ).sub( _panStart ); + + if ( mouseChange.lengthSq() ) { + + mouseChange.multiplyScalar( _eye.length() * _this.panSpeed ); + + pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x ); + pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) ); + + _this.object.position.add( pan ); + _this.target.add( pan ); + + if ( _this.staticMoving ) { + + _panStart.copy( _panEnd ); + + } else { + + _panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) ); + + } + + } + + }; + + }() ); + + this.checkDistances = function () { + + if ( ! _this.noZoom || ! _this.noPan ) { + + if ( _eye.lengthSq() > _this.maxDistance * _this.maxDistance ) { + + _this.object.position.addVectors( _this.target, _eye.setLength( _this.maxDistance ) ); + _zoomStart.copy( _zoomEnd ); + + } + + if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) { + + _this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) ); + _zoomStart.copy( _zoomEnd ); + + } + + } + + }; + + this.update = function () { + + _eye.subVectors( _this.object.position, _this.target ); + + if ( ! _this.noRotate ) { + + _this.rotateCamera(); + + } + + if ( ! _this.noZoom ) { + + _this.zoomCamera(); + + } + + if ( ! _this.noPan ) { + + _this.panCamera(); + + } + + _this.object.position.addVectors( _this.target, _eye ); + + _this.checkDistances(); + + _this.object.lookAt( _this.target ); + + if ( lastPosition.distanceToSquared( _this.object.position ) > EPS ) { + + _this.dispatchEvent( changeEvent ); + + lastPosition.copy( _this.object.position ); + + } + + }; + + this.reset = function () { + + _state = STATE.NONE; + _prevState = STATE.NONE; + + _this.target.copy( _this.target0 ); + _this.object.position.copy( _this.position0 ); + _this.object.up.copy( _this.up0 ); + + _eye.subVectors( _this.object.position, _this.target ); + + _this.object.lookAt( _this.target ); + + _this.dispatchEvent( changeEvent ); + + lastPosition.copy( _this.object.position ); + + }; + + // listeners + + function keydown( event ) { + + if ( _this.enabled === false ) return; + + window.removeEventListener( 'keydown', keydown ); + + _prevState = _state; + + if ( _state !== STATE.NONE ) { + + return; + + } else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && ! _this.noRotate ) { + + _state = STATE.ROTATE; + + } else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && ! _this.noZoom ) { + + _state = STATE.ZOOM; + + } else if ( event.keyCode === _this.keys[ STATE.PAN ] && ! _this.noPan ) { + + _state = STATE.PAN; + + } + + } + + function keyup( event ) { + + if ( _this.enabled === false ) return; + + _state = _prevState; + + window.addEventListener( 'keydown', keydown, false ); + + } + + function mousedown( event ) { + + if ( _this.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + if ( _state === STATE.NONE ) { + + _state = event.button; + + } + + if ( _state === STATE.ROTATE && ! _this.noRotate ) { + + _moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) ); + _movePrev.copy( _moveCurr ); + + } else if ( _state === STATE.ZOOM && ! _this.noZoom ) { + + _zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) ); + _zoomEnd.copy( _zoomStart ); + + } else if ( _state === STATE.PAN && ! _this.noPan ) { + + _panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) ); + _panEnd.copy( _panStart ); + + } + + document.addEventListener( 'mousemove', mousemove, false ); + document.addEventListener( 'mouseup', mouseup, false ); + + _this.dispatchEvent( startEvent ); + + } + + function mousemove( event ) { + + if ( _this.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + if ( _state === STATE.ROTATE && ! _this.noRotate ) { + + _movePrev.copy( _moveCurr ); + _moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) ); + + } else if ( _state === STATE.ZOOM && ! _this.noZoom ) { + + _zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) ); + + } else if ( _state === STATE.PAN && ! _this.noPan ) { + + _panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) ); + + } + + } + + function mouseup( event ) { + + if ( _this.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + _state = STATE.NONE; + + document.removeEventListener( 'mousemove', mousemove ); + document.removeEventListener( 'mouseup', mouseup ); + _this.dispatchEvent( endEvent ); + + } + + function mousewheel( event ) { + + if ( _this.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + var delta = 0; + + if ( event.wheelDelta ) { + + // WebKit / Opera / Explorer 9 + + delta = event.wheelDelta / 40; + + } else if ( event.detail ) { + + // Firefox + + delta = - event.detail / 3; + + } + + _zoomStart.y += delta * 0.01; + _this.dispatchEvent( startEvent ); + _this.dispatchEvent( endEvent ); + + } + + function touchstart( event ) { + + if ( _this.enabled === false ) return; + + switch ( event.touches.length ) { + + case 1: + _state = STATE.TOUCH_ROTATE; + _moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); + _movePrev.copy( _moveCurr ); + break; + + case 2: + _state = STATE.TOUCH_ZOOM_PAN; + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy ); + + var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; + var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; + _panStart.copy( getMouseOnScreen( x, y ) ); + _panEnd.copy( _panStart ); + break; + + default: + _state = STATE.NONE; + + } + _this.dispatchEvent( startEvent ); + + + } + + function touchmove( event ) { + + if ( _this.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + switch ( event.touches.length ) { + + case 1: + _movePrev.copy( _moveCurr ); + _moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); + break; + + case 2: + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy ); + + var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; + var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; + _panEnd.copy( getMouseOnScreen( x, y ) ); + break; + + default: + _state = STATE.NONE; + + } + + } + + function touchend( event ) { + + if ( _this.enabled === false ) return; + + switch ( event.touches.length ) { + + case 1: + _movePrev.copy( _moveCurr ); + _moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); + break; + + case 2: + _touchZoomDistanceStart = _touchZoomDistanceEnd = 0; + + var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; + var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; + _panEnd.copy( getMouseOnScreen( x, y ) ); + _panStart.copy( _panEnd ); + break; + + } + + _state = STATE.NONE; + _this.dispatchEvent( endEvent ); + + } + + function contextmenu( event ) { + + event.preventDefault(); + + } + + this.dispose = function() { + + this.domElement.removeEventListener( 'contextmenu', contextmenu, false ); + this.domElement.removeEventListener( 'mousedown', mousedown, false ); + this.domElement.removeEventListener( 'mousewheel', mousewheel, false ); + this.domElement.removeEventListener( 'MozMousePixelScroll', mousewheel, false ); // firefox + + this.domElement.removeEventListener( 'touchstart', touchstart, false ); + this.domElement.removeEventListener( 'touchend', touchend, false ); + this.domElement.removeEventListener( 'touchmove', touchmove, false ); + + document.removeEventListener( 'mousemove', mousemove, false ); + document.removeEventListener( 'mouseup', mouseup, false ); + + window.removeEventListener( 'keydown', keydown, false ); + window.removeEventListener( 'keyup', keyup, false ); + + } + + this.domElement.addEventListener( 'contextmenu', contextmenu, false ); + this.domElement.addEventListener( 'mousedown', mousedown, false ); + this.domElement.addEventListener( 'mousewheel', mousewheel, false ); + this.domElement.addEventListener( 'MozMousePixelScroll', mousewheel, false ); // firefox + + this.domElement.addEventListener( 'touchstart', touchstart, false ); + this.domElement.addEventListener( 'touchend', touchend, false ); + this.domElement.addEventListener( 'touchmove', touchmove, false ); + + window.addEventListener( 'keydown', keydown, false ); + window.addEventListener( 'keyup', keyup, false ); + + this.handleResize(); + + // force an update at start + this.update(); + +}; + +THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype ); +THREE.TrackballControls.prototype.constructor = THREE.TrackballControls; diff --git a/node_modules/three/examples/js/controls/TransformControls.js b/node_modules/three/examples/js/controls/TransformControls.js new file mode 100644 index 00000000..9d343f42 --- /dev/null +++ b/node_modules/three/examples/js/controls/TransformControls.js @@ -0,0 +1,1132 @@ +/** + * @author arodic / https://github.com/arodic + */ + /*jshint sub:true*/ + +( function () { + + 'use strict'; + + + var GizmoMaterial = function ( parameters ) { + + THREE.MeshBasicMaterial.call( this ); + + this.depthTest = false; + this.depthWrite = false; + this.side = THREE.FrontSide; + this.transparent = true; + + this.setValues( parameters ); + + this.oldColor = this.color.clone(); + this.oldOpacity = this.opacity; + + this.highlight = function( highlighted ) { + + if ( highlighted ) { + + this.color.setRGB( 1, 1, 0 ); + this.opacity = 1; + + } else { + + this.color.copy( this.oldColor ); + this.opacity = this.oldOpacity; + + } + + }; + + }; + + GizmoMaterial.prototype = Object.create( THREE.MeshBasicMaterial.prototype ); + GizmoMaterial.prototype.constructor = GizmoMaterial; + + + var GizmoLineMaterial = function ( parameters ) { + + THREE.LineBasicMaterial.call( this ); + + this.depthTest = false; + this.depthWrite = false; + this.transparent = true; + this.linewidth = 1; + + this.setValues( parameters ); + + this.oldColor = this.color.clone(); + this.oldOpacity = this.opacity; + + this.highlight = function( highlighted ) { + + if ( highlighted ) { + + this.color.setRGB( 1, 1, 0 ); + this.opacity = 1; + + } else { + + this.color.copy( this.oldColor ); + this.opacity = this.oldOpacity; + + } + + }; + + }; + + GizmoLineMaterial.prototype = Object.create( THREE.LineBasicMaterial.prototype ); + GizmoLineMaterial.prototype.constructor = GizmoLineMaterial; + + + var pickerMaterial = new GizmoMaterial( { visible: false, transparent: false } ); + + + THREE.TransformGizmo = function () { + + var scope = this; + + this.init = function () { + + THREE.Object3D.call( this ); + + this.handles = new THREE.Object3D(); + this.pickers = new THREE.Object3D(); + this.planes = new THREE.Object3D(); + + this.add( this.handles ); + this.add( this.pickers ); + this.add( this.planes ); + + //// PLANES + + var planeGeometry = new THREE.PlaneBufferGeometry( 50, 50, 2, 2 ); + var planeMaterial = new THREE.MeshBasicMaterial( { visible: false, side: THREE.DoubleSide } ); + + var planes = { + "XY": new THREE.Mesh( planeGeometry, planeMaterial ), + "YZ": new THREE.Mesh( planeGeometry, planeMaterial ), + "XZ": new THREE.Mesh( planeGeometry, planeMaterial ), + "XYZE": new THREE.Mesh( planeGeometry, planeMaterial ) + }; + + this.activePlane = planes[ "XYZE" ]; + + planes[ "YZ" ].rotation.set( 0, Math.PI / 2, 0 ); + planes[ "XZ" ].rotation.set( - Math.PI / 2, 0, 0 ); + + for ( var i in planes ) { + + planes[ i ].name = i; + this.planes.add( planes[ i ] ); + this.planes[ i ] = planes[ i ]; + + } + + //// HANDLES AND PICKERS + + var setupGizmos = function( gizmoMap, parent ) { + + for ( var name in gizmoMap ) { + + for ( i = gizmoMap[ name ].length; i --; ) { + + var object = gizmoMap[ name ][ i ][ 0 ]; + var position = gizmoMap[ name ][ i ][ 1 ]; + var rotation = gizmoMap[ name ][ i ][ 2 ]; + + object.name = name; + + if ( position ) object.position.set( position[ 0 ], position[ 1 ], position[ 2 ] ); + if ( rotation ) object.rotation.set( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ] ); + + parent.add( object ); + + } + + } + + }; + + setupGizmos( this.handleGizmos, this.handles ); + setupGizmos( this.pickerGizmos, this.pickers ); + + // reset Transformations + + this.traverse( function ( child ) { + + if ( child instanceof THREE.Mesh ) { + + child.updateMatrix(); + + var tempGeometry = child.geometry.clone(); + tempGeometry.applyMatrix( child.matrix ); + child.geometry = tempGeometry; + + child.position.set( 0, 0, 0 ); + child.rotation.set( 0, 0, 0 ); + child.scale.set( 1, 1, 1 ); + + } + + } ); + + }; + + this.highlight = function ( axis ) { + + this.traverse( function( child ) { + + if ( child.material && child.material.highlight ) { + + if ( child.name === axis ) { + + child.material.highlight( true ); + + } else { + + child.material.highlight( false ); + + } + + } + + } ); + + }; + + }; + + THREE.TransformGizmo.prototype = Object.create( THREE.Object3D.prototype ); + THREE.TransformGizmo.prototype.constructor = THREE.TransformGizmo; + + THREE.TransformGizmo.prototype.update = function ( rotation, eye ) { + + var vec1 = new THREE.Vector3( 0, 0, 0 ); + var vec2 = new THREE.Vector3( 0, 1, 0 ); + var lookAtMatrix = new THREE.Matrix4(); + + this.traverse( function( child ) { + + if ( child.name.search( "E" ) !== - 1 ) { + + child.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( eye, vec1, vec2 ) ); + + } else if ( child.name.search( "X" ) !== - 1 || child.name.search( "Y" ) !== - 1 || child.name.search( "Z" ) !== - 1 ) { + + child.quaternion.setFromEuler( rotation ); + + } + + } ); + + }; + + THREE.TransformGizmoTranslate = function () { + + THREE.TransformGizmo.call( this ); + + var arrowGeometry = new THREE.Geometry(); + var mesh = new THREE.Mesh( new THREE.CylinderGeometry( 0, 0.05, 0.2, 12, 1, false ) ); + mesh.position.y = 0.5; + mesh.updateMatrix(); + + arrowGeometry.merge( mesh.geometry, mesh.matrix ); + + var lineXGeometry = new THREE.BufferGeometry(); + lineXGeometry.addAttribute( 'position', new THREE.Float32Attribute( [ 0, 0, 0, 1, 0, 0 ], 3 ) ); + + var lineYGeometry = new THREE.BufferGeometry(); + lineYGeometry.addAttribute( 'position', new THREE.Float32Attribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) ); + + var lineZGeometry = new THREE.BufferGeometry(); + lineZGeometry.addAttribute( 'position', new THREE.Float32Attribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) ); + + this.handleGizmos = { + + X: [ + [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0xff0000 } ) ), [ 0.5, 0, 0 ], [ 0, 0, - Math.PI / 2 ] ], + [ new THREE.Line( lineXGeometry, new GizmoLineMaterial( { color: 0xff0000 } ) ) ] + ], + + Y: [ + [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x00ff00 } ) ), [ 0, 0.5, 0 ] ], + [ new THREE.Line( lineYGeometry, new GizmoLineMaterial( { color: 0x00ff00 } ) ) ] + ], + + Z: [ + [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x0000ff } ) ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ] ], + [ new THREE.Line( lineZGeometry, new GizmoLineMaterial( { color: 0x0000ff } ) ) ] + ], + + XYZ: [ + [ new THREE.Mesh( new THREE.OctahedronGeometry( 0.1, 0 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ 0, 0, 0 ] ] + ], + + XY: [ + [ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0xffff00, opacity: 0.25 } ) ), [ 0.15, 0.15, 0 ] ] + ], + + YZ: [ + [ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0x00ffff, opacity: 0.25 } ) ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ] ] + ], + + XZ: [ + [ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0xff00ff, opacity: 0.25 } ) ), [ 0.15, 0, 0.15 ], [ - Math.PI / 2, 0, 0 ] ] + ] + + }; + + this.pickerGizmos = { + + X: [ + [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0.6, 0, 0 ], [ 0, 0, - Math.PI / 2 ] ] + ], + + Y: [ + [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0, 0.6, 0 ] ] + ], + + Z: [ + [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0, 0, 0.6 ], [ Math.PI / 2, 0, 0 ] ] + ], + + XYZ: [ + [ new THREE.Mesh( new THREE.OctahedronGeometry( 0.2, 0 ), pickerMaterial ) ] + ], + + XY: [ + [ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), pickerMaterial ), [ 0.2, 0.2, 0 ] ] + ], + + YZ: [ + [ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), pickerMaterial ), [ 0, 0.2, 0.2 ], [ 0, Math.PI / 2, 0 ] ] + ], + + XZ: [ + [ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), pickerMaterial ), [ 0.2, 0, 0.2 ], [ - Math.PI / 2, 0, 0 ] ] + ] + + }; + + this.setActivePlane = function ( axis, eye ) { + + var tempMatrix = new THREE.Matrix4(); + eye.applyMatrix4( tempMatrix.getInverse( tempMatrix.extractRotation( this.planes[ "XY" ].matrixWorld ) ) ); + + if ( axis === "X" ) { + + this.activePlane = this.planes[ "XY" ]; + + if ( Math.abs( eye.y ) > Math.abs( eye.z ) ) this.activePlane = this.planes[ "XZ" ]; + + } + + if ( axis === "Y" ) { + + this.activePlane = this.planes[ "XY" ]; + + if ( Math.abs( eye.x ) > Math.abs( eye.z ) ) this.activePlane = this.planes[ "YZ" ]; + + } + + if ( axis === "Z" ) { + + this.activePlane = this.planes[ "XZ" ]; + + if ( Math.abs( eye.x ) > Math.abs( eye.y ) ) this.activePlane = this.planes[ "YZ" ]; + + } + + if ( axis === "XYZ" ) this.activePlane = this.planes[ "XYZE" ]; + + if ( axis === "XY" ) this.activePlane = this.planes[ "XY" ]; + + if ( axis === "YZ" ) this.activePlane = this.planes[ "YZ" ]; + + if ( axis === "XZ" ) this.activePlane = this.planes[ "XZ" ]; + + }; + + this.init(); + + }; + + THREE.TransformGizmoTranslate.prototype = Object.create( THREE.TransformGizmo.prototype ); + THREE.TransformGizmoTranslate.prototype.constructor = THREE.TransformGizmoTranslate; + + THREE.TransformGizmoRotate = function () { + + THREE.TransformGizmo.call( this ); + + var CircleGeometry = function ( radius, facing, arc ) { + + var geometry = new THREE.BufferGeometry(); + var vertices = []; + arc = arc ? arc : 1; + + for ( var i = 0; i <= 64 * arc; ++ i ) { + + if ( facing === 'x' ) vertices.push( 0, Math.cos( i / 32 * Math.PI ) * radius, Math.sin( i / 32 * Math.PI ) * radius ); + if ( facing === 'y' ) vertices.push( Math.cos( i / 32 * Math.PI ) * radius, 0, Math.sin( i / 32 * Math.PI ) * radius ); + if ( facing === 'z' ) vertices.push( Math.sin( i / 32 * Math.PI ) * radius, Math.cos( i / 32 * Math.PI ) * radius, 0 ); + + } + + geometry.addAttribute( 'position', new THREE.Float32Attribute( vertices, 3 ) ); + return geometry; + + }; + + this.handleGizmos = { + + X: [ + [ new THREE.Line( new CircleGeometry( 1, 'x', 0.5 ), new GizmoLineMaterial( { color: 0xff0000 } ) ) ] + ], + + Y: [ + [ new THREE.Line( new CircleGeometry( 1, 'y', 0.5 ), new GizmoLineMaterial( { color: 0x00ff00 } ) ) ] + ], + + Z: [ + [ new THREE.Line( new CircleGeometry( 1, 'z', 0.5 ), new GizmoLineMaterial( { color: 0x0000ff } ) ) ] + ], + + E: [ + [ new THREE.Line( new CircleGeometry( 1.25, 'z', 1 ), new GizmoLineMaterial( { color: 0xcccc00 } ) ) ] + ], + + XYZE: [ + [ new THREE.Line( new CircleGeometry( 1, 'z', 1 ), new GizmoLineMaterial( { color: 0x787878 } ) ) ] + ] + + }; + + this.pickerGizmos = { + + X: [ + [ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), pickerMaterial ), [ 0, 0, 0 ], [ 0, - Math.PI / 2, - Math.PI / 2 ] ] + ], + + Y: [ + [ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), pickerMaterial ), [ 0, 0, 0 ], [ Math.PI / 2, 0, 0 ] ] + ], + + Z: [ + [ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), pickerMaterial ), [ 0, 0, 0 ], [ 0, 0, - Math.PI / 2 ] ] + ], + + E: [ + [ new THREE.Mesh( new THREE.TorusGeometry( 1.25, 0.12, 2, 24 ), pickerMaterial ) ] + ], + + XYZE: [ + [ new THREE.Mesh( new THREE.Geometry() ) ]// TODO + ] + + }; + + this.setActivePlane = function ( axis ) { + + if ( axis === "E" ) this.activePlane = this.planes[ "XYZE" ]; + + if ( axis === "X" ) this.activePlane = this.planes[ "YZ" ]; + + if ( axis === "Y" ) this.activePlane = this.planes[ "XZ" ]; + + if ( axis === "Z" ) this.activePlane = this.planes[ "XY" ]; + + }; + + this.update = function ( rotation, eye2 ) { + + THREE.TransformGizmo.prototype.update.apply( this, arguments ); + + var group = { + + handles: this[ "handles" ], + pickers: this[ "pickers" ], + + }; + + var tempMatrix = new THREE.Matrix4(); + var worldRotation = new THREE.Euler( 0, 0, 1 ); + var tempQuaternion = new THREE.Quaternion(); + var unitX = new THREE.Vector3( 1, 0, 0 ); + var unitY = new THREE.Vector3( 0, 1, 0 ); + var unitZ = new THREE.Vector3( 0, 0, 1 ); + var quaternionX = new THREE.Quaternion(); + var quaternionY = new THREE.Quaternion(); + var quaternionZ = new THREE.Quaternion(); + var eye = eye2.clone(); + + worldRotation.copy( this.planes[ "XY" ].rotation ); + tempQuaternion.setFromEuler( worldRotation ); + + tempMatrix.makeRotationFromQuaternion( tempQuaternion ).getInverse( tempMatrix ); + eye.applyMatrix4( tempMatrix ); + + this.traverse( function( child ) { + + tempQuaternion.setFromEuler( worldRotation ); + + if ( child.name === "X" ) { + + quaternionX.setFromAxisAngle( unitX, Math.atan2( - eye.y, eye.z ) ); + tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX ); + child.quaternion.copy( tempQuaternion ); + + } + + if ( child.name === "Y" ) { + + quaternionY.setFromAxisAngle( unitY, Math.atan2( eye.x, eye.z ) ); + tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY ); + child.quaternion.copy( tempQuaternion ); + + } + + if ( child.name === "Z" ) { + + quaternionZ.setFromAxisAngle( unitZ, Math.atan2( eye.y, eye.x ) ); + tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ ); + child.quaternion.copy( tempQuaternion ); + + } + + } ); + + }; + + this.init(); + + }; + + THREE.TransformGizmoRotate.prototype = Object.create( THREE.TransformGizmo.prototype ); + THREE.TransformGizmoRotate.prototype.constructor = THREE.TransformGizmoRotate; + + THREE.TransformGizmoScale = function () { + + THREE.TransformGizmo.call( this ); + + var arrowGeometry = new THREE.Geometry(); + var mesh = new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ) ); + mesh.position.y = 0.5; + mesh.updateMatrix(); + + arrowGeometry.merge( mesh.geometry, mesh.matrix ); + + var lineXGeometry = new THREE.BufferGeometry(); + lineXGeometry.addAttribute( 'position', new THREE.Float32Attribute( [ 0, 0, 0, 1, 0, 0 ], 3 ) ); + + var lineYGeometry = new THREE.BufferGeometry(); + lineYGeometry.addAttribute( 'position', new THREE.Float32Attribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) ); + + var lineZGeometry = new THREE.BufferGeometry(); + lineZGeometry.addAttribute( 'position', new THREE.Float32Attribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) ); + + this.handleGizmos = { + + X: [ + [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0xff0000 } ) ), [ 0.5, 0, 0 ], [ 0, 0, - Math.PI / 2 ] ], + [ new THREE.Line( lineXGeometry, new GizmoLineMaterial( { color: 0xff0000 } ) ) ] + ], + + Y: [ + [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x00ff00 } ) ), [ 0, 0.5, 0 ] ], + [ new THREE.Line( lineYGeometry, new GizmoLineMaterial( { color: 0x00ff00 } ) ) ] + ], + + Z: [ + [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x0000ff } ) ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ] ], + [ new THREE.Line( lineZGeometry, new GizmoLineMaterial( { color: 0x0000ff } ) ) ] + ], + + XYZ: [ + [ new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ) ] + ] + + }; + + this.pickerGizmos = { + + X: [ + [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0.6, 0, 0 ], [ 0, 0, - Math.PI / 2 ] ] + ], + + Y: [ + [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0, 0.6, 0 ] ] + ], + + Z: [ + [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0, 0, 0.6 ], [ Math.PI / 2, 0, 0 ] ] + ], + + XYZ: [ + [ new THREE.Mesh( new THREE.BoxGeometry( 0.4, 0.4, 0.4 ), pickerMaterial ) ] + ] + + }; + + this.setActivePlane = function ( axis, eye ) { + + var tempMatrix = new THREE.Matrix4(); + eye.applyMatrix4( tempMatrix.getInverse( tempMatrix.extractRotation( this.planes[ "XY" ].matrixWorld ) ) ); + + if ( axis === "X" ) { + + this.activePlane = this.planes[ "XY" ]; + if ( Math.abs( eye.y ) > Math.abs( eye.z ) ) this.activePlane = this.planes[ "XZ" ]; + + } + + if ( axis === "Y" ) { + + this.activePlane = this.planes[ "XY" ]; + if ( Math.abs( eye.x ) > Math.abs( eye.z ) ) this.activePlane = this.planes[ "YZ" ]; + + } + + if ( axis === "Z" ) { + + this.activePlane = this.planes[ "XZ" ]; + if ( Math.abs( eye.x ) > Math.abs( eye.y ) ) this.activePlane = this.planes[ "YZ" ]; + + } + + if ( axis === "XYZ" ) this.activePlane = this.planes[ "XYZE" ]; + + }; + + this.init(); + + }; + + THREE.TransformGizmoScale.prototype = Object.create( THREE.TransformGizmo.prototype ); + THREE.TransformGizmoScale.prototype.constructor = THREE.TransformGizmoScale; + + THREE.TransformControls = function ( camera, domElement ) { + + // TODO: Make non-uniform scale and rotate play nice in hierarchies + // TODO: ADD RXYZ contol + + THREE.Object3D.call( this ); + + domElement = ( domElement !== undefined ) ? domElement : document; + + this.object = undefined; + this.visible = false; + this.translationSnap = null; + this.rotationSnap = null; + this.space = "world"; + this.size = 1; + this.axis = null; + + var scope = this; + + var _mode = "translate"; + var _dragging = false; + var _plane = "XY"; + var _gizmo = { + + "translate": new THREE.TransformGizmoTranslate(), + "rotate": new THREE.TransformGizmoRotate(), + "scale": new THREE.TransformGizmoScale() + }; + + for ( var type in _gizmo ) { + + var gizmoObj = _gizmo[ type ]; + + gizmoObj.visible = ( type === _mode ); + this.add( gizmoObj ); + + } + + var changeEvent = { type: "change" }; + var mouseDownEvent = { type: "mouseDown" }; + var mouseUpEvent = { type: "mouseUp", mode: _mode }; + var objectChangeEvent = { type: "objectChange" }; + + var ray = new THREE.Raycaster(); + var pointerVector = new THREE.Vector2(); + + var point = new THREE.Vector3(); + var offset = new THREE.Vector3(); + + var rotation = new THREE.Vector3(); + var offsetRotation = new THREE.Vector3(); + var scale = 1; + + var lookAtMatrix = new THREE.Matrix4(); + var eye = new THREE.Vector3(); + + var tempMatrix = new THREE.Matrix4(); + var tempVector = new THREE.Vector3(); + var tempQuaternion = new THREE.Quaternion(); + var unitX = new THREE.Vector3( 1, 0, 0 ); + var unitY = new THREE.Vector3( 0, 1, 0 ); + var unitZ = new THREE.Vector3( 0, 0, 1 ); + + var quaternionXYZ = new THREE.Quaternion(); + var quaternionX = new THREE.Quaternion(); + var quaternionY = new THREE.Quaternion(); + var quaternionZ = new THREE.Quaternion(); + var quaternionE = new THREE.Quaternion(); + + var oldPosition = new THREE.Vector3(); + var oldScale = new THREE.Vector3(); + var oldRotationMatrix = new THREE.Matrix4(); + + var parentRotationMatrix = new THREE.Matrix4(); + var parentScale = new THREE.Vector3(); + + var worldPosition = new THREE.Vector3(); + var worldRotation = new THREE.Euler(); + var worldRotationMatrix = new THREE.Matrix4(); + var camPosition = new THREE.Vector3(); + var camRotation = new THREE.Euler(); + + domElement.addEventListener( "mousedown", onPointerDown, false ); + domElement.addEventListener( "touchstart", onPointerDown, false ); + + domElement.addEventListener( "mousemove", onPointerHover, false ); + domElement.addEventListener( "touchmove", onPointerHover, false ); + + domElement.addEventListener( "mousemove", onPointerMove, false ); + domElement.addEventListener( "touchmove", onPointerMove, false ); + + domElement.addEventListener( "mouseup", onPointerUp, false ); + domElement.addEventListener( "mouseout", onPointerUp, false ); + domElement.addEventListener( "touchend", onPointerUp, false ); + domElement.addEventListener( "touchcancel", onPointerUp, false ); + domElement.addEventListener( "touchleave", onPointerUp, false ); + + this.dispose = function () { + + domElement.removeEventListener( "mousedown", onPointerDown ); + domElement.removeEventListener( "touchstart", onPointerDown ); + + domElement.removeEventListener( "mousemove", onPointerHover ); + domElement.removeEventListener( "touchmove", onPointerHover ); + + domElement.removeEventListener( "mousemove", onPointerMove ); + domElement.removeEventListener( "touchmove", onPointerMove ); + + domElement.removeEventListener( "mouseup", onPointerUp ); + domElement.removeEventListener( "mouseout", onPointerUp ); + domElement.removeEventListener( "touchend", onPointerUp ); + domElement.removeEventListener( "touchcancel", onPointerUp ); + domElement.removeEventListener( "touchleave", onPointerUp ); + + }; + + this.attach = function ( object ) { + + this.object = object; + this.visible = true; + this.update(); + + }; + + this.detach = function () { + + this.object = undefined; + this.visible = false; + this.axis = null; + + }; + + this.setMode = function ( mode ) { + + _mode = mode ? mode : _mode; + + if ( _mode === "scale" ) scope.space = "local"; + + for ( var type in _gizmo ) _gizmo[ type ].visible = ( type === _mode ); + + this.update(); + scope.dispatchEvent( changeEvent ); + + }; + + this.setTranslationSnap = function ( translationSnap ) { + + scope.translationSnap = translationSnap; + + }; + + this.setRotationSnap = function ( rotationSnap ) { + + scope.rotationSnap = rotationSnap; + + }; + + this.setSize = function ( size ) { + + scope.size = size; + this.update(); + scope.dispatchEvent( changeEvent ); + + }; + + this.setSpace = function ( space ) { + + scope.space = space; + this.update(); + scope.dispatchEvent( changeEvent ); + + }; + + this.update = function () { + + if ( scope.object === undefined ) return; + + scope.object.updateMatrixWorld(); + worldPosition.setFromMatrixPosition( scope.object.matrixWorld ); + worldRotation.setFromRotationMatrix( tempMatrix.extractRotation( scope.object.matrixWorld ) ); + + camera.updateMatrixWorld(); + camPosition.setFromMatrixPosition( camera.matrixWorld ); + camRotation.setFromRotationMatrix( tempMatrix.extractRotation( camera.matrixWorld ) ); + + scale = worldPosition.distanceTo( camPosition ) / 6 * scope.size; + this.position.copy( worldPosition ); + this.scale.set( scale, scale, scale ); + + eye.copy( camPosition ).sub( worldPosition ).normalize(); + + if ( scope.space === "local" ) { + + _gizmo[ _mode ].update( worldRotation, eye ); + + } else if ( scope.space === "world" ) { + + _gizmo[ _mode ].update( new THREE.Euler(), eye ); + + } + + _gizmo[ _mode ].highlight( scope.axis ); + + }; + + function onPointerHover( event ) { + + if ( scope.object === undefined || _dragging === true || ( event.button !== undefined && event.button !== 0 ) ) return; + + var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event; + + var intersect = intersectObjects( pointer, _gizmo[ _mode ].pickers.children ); + + var axis = null; + + if ( intersect ) { + + axis = intersect.object.name; + + event.preventDefault(); + + } + + if ( scope.axis !== axis ) { + + scope.axis = axis; + scope.update(); + scope.dispatchEvent( changeEvent ); + + } + + } + + function onPointerDown( event ) { + + if ( scope.object === undefined || _dragging === true || ( event.button !== undefined && event.button !== 0 ) ) return; + + var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event; + + if ( pointer.button === 0 || pointer.button === undefined ) { + + var intersect = intersectObjects( pointer, _gizmo[ _mode ].pickers.children ); + + if ( intersect ) { + + event.preventDefault(); + event.stopPropagation(); + + scope.dispatchEvent( mouseDownEvent ); + + scope.axis = intersect.object.name; + + scope.update(); + + eye.copy( camPosition ).sub( worldPosition ).normalize(); + + _gizmo[ _mode ].setActivePlane( scope.axis, eye ); + + var planeIntersect = intersectObjects( pointer, [ _gizmo[ _mode ].activePlane ] ); + + if ( planeIntersect ) { + + oldPosition.copy( scope.object.position ); + oldScale.copy( scope.object.scale ); + + oldRotationMatrix.extractRotation( scope.object.matrix ); + worldRotationMatrix.extractRotation( scope.object.matrixWorld ); + + parentRotationMatrix.extractRotation( scope.object.parent.matrixWorld ); + parentScale.setFromMatrixScale( tempMatrix.getInverse( scope.object.parent.matrixWorld ) ); + + offset.copy( planeIntersect.point ); + + } + + } + + } + + _dragging = true; + + } + + function onPointerMove( event ) { + + if ( scope.object === undefined || scope.axis === null || _dragging === false || ( event.button !== undefined && event.button !== 0 ) ) return; + + var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event; + + var planeIntersect = intersectObjects( pointer, [ _gizmo[ _mode ].activePlane ] ); + + if ( planeIntersect === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + point.copy( planeIntersect.point ); + + if ( _mode === "translate" ) { + + point.sub( offset ); + point.multiply( parentScale ); + + if ( scope.space === "local" ) { + + point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) ); + + if ( scope.axis.search( "X" ) === - 1 ) point.x = 0; + if ( scope.axis.search( "Y" ) === - 1 ) point.y = 0; + if ( scope.axis.search( "Z" ) === - 1 ) point.z = 0; + + point.applyMatrix4( oldRotationMatrix ); + + scope.object.position.copy( oldPosition ); + scope.object.position.add( point ); + + } + + if ( scope.space === "world" || scope.axis.search( "XYZ" ) !== - 1 ) { + + if ( scope.axis.search( "X" ) === - 1 ) point.x = 0; + if ( scope.axis.search( "Y" ) === - 1 ) point.y = 0; + if ( scope.axis.search( "Z" ) === - 1 ) point.z = 0; + + point.applyMatrix4( tempMatrix.getInverse( parentRotationMatrix ) ); + + scope.object.position.copy( oldPosition ); + scope.object.position.add( point ); + + } + + if ( scope.translationSnap !== null ) { + + if ( scope.space === "local" ) { + + scope.object.position.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) ); + + } + + if ( scope.axis.search( "X" ) !== - 1 ) scope.object.position.x = Math.round( scope.object.position.x / scope.translationSnap ) * scope.translationSnap; + if ( scope.axis.search( "Y" ) !== - 1 ) scope.object.position.y = Math.round( scope.object.position.y / scope.translationSnap ) * scope.translationSnap; + if ( scope.axis.search( "Z" ) !== - 1 ) scope.object.position.z = Math.round( scope.object.position.z / scope.translationSnap ) * scope.translationSnap; + + if ( scope.space === "local" ) { + + scope.object.position.applyMatrix4( worldRotationMatrix ); + + } + + } + + } else if ( _mode === "scale" ) { + + point.sub( offset ); + point.multiply( parentScale ); + + if ( scope.space === "local" ) { + + if ( scope.axis === "XYZ" ) { + + scale = 1 + ( ( point.y ) / 50 ); + + scope.object.scale.x = oldScale.x * scale; + scope.object.scale.y = oldScale.y * scale; + scope.object.scale.z = oldScale.z * scale; + + } else { + + point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) ); + + if ( scope.axis === "X" ) scope.object.scale.x = oldScale.x * ( 1 + point.x / 50 ); + if ( scope.axis === "Y" ) scope.object.scale.y = oldScale.y * ( 1 + point.y / 50 ); + if ( scope.axis === "Z" ) scope.object.scale.z = oldScale.z * ( 1 + point.z / 50 ); + + } + + } + + } else if ( _mode === "rotate" ) { + + point.sub( worldPosition ); + point.multiply( parentScale ); + tempVector.copy( offset ).sub( worldPosition ); + tempVector.multiply( parentScale ); + + if ( scope.axis === "E" ) { + + point.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) ); + tempVector.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) ); + + rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) ); + offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) ); + + tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) ); + + quaternionE.setFromAxisAngle( eye, rotation.z - offsetRotation.z ); + quaternionXYZ.setFromRotationMatrix( worldRotationMatrix ); + + tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionE ); + tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ ); + + scope.object.quaternion.copy( tempQuaternion ); + + } else if ( scope.axis === "XYZE" ) { + + quaternionE.setFromEuler( point.clone().cross( tempVector ).normalize() ); // rotation axis + + tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) ); + quaternionX.setFromAxisAngle( quaternionE, - point.clone().angleTo( tempVector ) ); + quaternionXYZ.setFromRotationMatrix( worldRotationMatrix ); + + tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX ); + tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ ); + + scope.object.quaternion.copy( tempQuaternion ); + + } else if ( scope.space === "local" ) { + + point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) ); + + tempVector.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) ); + + rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) ); + offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) ); + + quaternionXYZ.setFromRotationMatrix( oldRotationMatrix ); + + if ( scope.rotationSnap !== null ) { + + quaternionX.setFromAxisAngle( unitX, Math.round( ( rotation.x - offsetRotation.x ) / scope.rotationSnap ) * scope.rotationSnap ); + quaternionY.setFromAxisAngle( unitY, Math.round( ( rotation.y - offsetRotation.y ) / scope.rotationSnap ) * scope.rotationSnap ); + quaternionZ.setFromAxisAngle( unitZ, Math.round( ( rotation.z - offsetRotation.z ) / scope.rotationSnap ) * scope.rotationSnap ); + + } else { + + quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x ); + quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y ); + quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z ); + + } + + if ( scope.axis === "X" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionX ); + if ( scope.axis === "Y" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionY ); + if ( scope.axis === "Z" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionZ ); + + scope.object.quaternion.copy( quaternionXYZ ); + + } else if ( scope.space === "world" ) { + + rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) ); + offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) ); + + tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) ); + + if ( scope.rotationSnap !== null ) { + + quaternionX.setFromAxisAngle( unitX, Math.round( ( rotation.x - offsetRotation.x ) / scope.rotationSnap ) * scope.rotationSnap ); + quaternionY.setFromAxisAngle( unitY, Math.round( ( rotation.y - offsetRotation.y ) / scope.rotationSnap ) * scope.rotationSnap ); + quaternionZ.setFromAxisAngle( unitZ, Math.round( ( rotation.z - offsetRotation.z ) / scope.rotationSnap ) * scope.rotationSnap ); + + } else { + + quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x ); + quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y ); + quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z ); + + } + + quaternionXYZ.setFromRotationMatrix( worldRotationMatrix ); + + if ( scope.axis === "X" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX ); + if ( scope.axis === "Y" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY ); + if ( scope.axis === "Z" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ ); + + tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ ); + + scope.object.quaternion.copy( tempQuaternion ); + + } + + } + + scope.update(); + scope.dispatchEvent( changeEvent ); + scope.dispatchEvent( objectChangeEvent ); + + } + + function onPointerUp( event ) { + + if ( event.button !== undefined && event.button !== 0 ) return; + + if ( _dragging && ( scope.axis !== null ) ) { + + mouseUpEvent.mode = _mode; + scope.dispatchEvent( mouseUpEvent ) + + } + + _dragging = false; + onPointerHover( event ); + + } + + function intersectObjects( pointer, objects ) { + + var rect = domElement.getBoundingClientRect(); + var x = ( pointer.clientX - rect.left ) / rect.width; + var y = ( pointer.clientY - rect.top ) / rect.height; + + pointerVector.set( ( x * 2 ) - 1, - ( y * 2 ) + 1 ); + ray.setFromCamera( pointerVector, camera ); + + var intersections = ray.intersectObjects( objects, true ); + return intersections[ 0 ] ? intersections[ 0 ] : false; + + } + + }; + + THREE.TransformControls.prototype = Object.create( THREE.Object3D.prototype ); + THREE.TransformControls.prototype.constructor = THREE.TransformControls; + +}() ); diff --git a/node_modules/three/examples/js/controls/VRControls.js b/node_modules/three/examples/js/controls/VRControls.js new file mode 100644 index 00000000..3a0f20d6 --- /dev/null +++ b/node_modules/three/examples/js/controls/VRControls.js @@ -0,0 +1,131 @@ +/** + * @author dmarcos / https://github.com/dmarcos + * @author mrdoob / http://mrdoob.com + */ + +var VRControls = function ( object, onError ) { + + var scope = this; + + var vrInputs = []; + + function filterInvalidDevices( devices ) { + + // Exclude Cardboard position sensor if Oculus exists. + + var oculusDevices = devices.filter( function ( device ) { + + return device.deviceName.toLowerCase().indexOf( 'oculus' ) !== - 1; + + } ); + + if ( oculusDevices.length >= 1 ) { + + return devices.filter( function ( device ) { + + return device.deviceName.toLowerCase().indexOf( 'cardboard' ) === - 1; + + } ); + + } else { + + return devices; + + } + + } + + function gotVRDevices( devices ) { + + devices = filterInvalidDevices( devices ); + + for ( var i = 0; i < devices.length; i ++ ) { + + if ( devices[ i ] instanceof PositionSensorVRDevice ) { + + vrInputs.push( devices[ i ] ); + + } + + } + + if ( onError ) onError( 'HMD not available' ); + + } + + if ( navigator.getVRDevices ) { + + navigator.getVRDevices().then( gotVRDevices ); + + } + + // the Rift SDK returns the position in meters + // this scale factor allows the user to define how meters + // are converted to scene units. + + this.scale = 1; + + this.update = function () { + + for ( var i = 0; i < vrInputs.length; i ++ ) { + + var vrInput = vrInputs[ i ]; + + var state = vrInput.getState(); + + if ( state.orientation !== null ) { + + object.quaternion.copy( state.orientation ); + + } + + if ( state.position !== null ) { + + object.position.copy( state.position ).multiplyScalar( scope.scale ); + + } + + } + + }; + + this.resetSensor = function () { + + for ( var i = 0; i < vrInputs.length; i ++ ) { + + var vrInput = vrInputs[ i ]; + + if ( vrInput.resetSensor !== undefined ) { + + vrInput.resetSensor(); + + } else if ( vrInput.zeroSensor !== undefined ) { + + vrInput.zeroSensor(); + + } + + } + + }; + + this.zeroSensor = function () { + + console.warn( 'THREE.VRControls: .zeroSensor() is now .resetSensor().' ); + this.resetSensor(); + + }; + + this.dispose = function () { + + vrInputs = []; + + }; + +}; + +try { + module.exports = VRControls; +} catch (e) { + THREE.VRControls = VRControls; +} diff --git a/node_modules/three/examples/js/crossfade/gui.js b/node_modules/three/examples/js/crossfade/gui.js new file mode 100644 index 00000000..9afd4216 --- /dev/null +++ b/node_modules/three/examples/js/crossfade/gui.js @@ -0,0 +1,39 @@ +var transitionParams = { + "useTexture": true, + "transition": 0.5, + "transitionSpeed": 2.0, + "texture": 5, + "loopTexture": true, + "animateTransition": true, + "textureThreshold": 0.3 +}; + +function initGUI() { + + var gui = new dat.GUI(); + + gui.add( transitionParams, "useTexture" ).onChange( function( value ) { + + transition.useTexture( value ); + + } ); + + gui.add( transitionParams, 'loopTexture' ); + + gui.add( transitionParams, 'texture', { Perlin: 0, Squares: 1, Cells: 2, Distort: 3, Gradient: 4, Radial: 5 } ).onChange( function( value ) { + + transition.setTexture( value ); + + } ).listen(); + + gui.add( transitionParams, "textureThreshold", 0, 1, 0.01 ).onChange( function( value ) { + + transition.setTextureThreshold( value ); + + } ); + + gui.add( transitionParams, "animateTransition" ); + gui.add( transitionParams, "transition", 0, 1, 0.01 ).listen(); + gui.add( transitionParams, "transitionSpeed", 0.5, 5, 0.01 ); + +} diff --git a/node_modules/three/examples/js/crossfade/scenes.js b/node_modules/three/examples/js/crossfade/scenes.js new file mode 100644 index 00000000..e112f843 --- /dev/null +++ b/node_modules/three/examples/js/crossfade/scenes.js @@ -0,0 +1,111 @@ +function generateGeometry( objectType, numObjects ) { + + var geometry = new THREE.Geometry(); + + function applyVertexColors( g, c ) { + + g.faces.forEach( function( f ) { + + var n = ( f instanceof THREE.Face3 ) ? 3 : 4; + + for ( var j = 0; j < n; j ++ ) { + + f.vertexColors[ j ] = c; + + } + + } ); + + } + + for ( var i = 0; i < numObjects; i ++ ) { + + var position = new THREE.Vector3(); + + position.x = Math.random() * 10000 - 5000; + position.y = Math.random() * 6000 - 3000; + position.z = Math.random() * 8000 - 4000; + + var rotation = new THREE.Euler(); + + rotation.x = Math.random() * 2 * Math.PI; + rotation.y = Math.random() * 2 * Math.PI; + rotation.z = Math.random() * 2 * Math.PI; + + var scale = new THREE.Vector3(); + + var geom, color = new THREE.Color(); + + scale.x = Math.random() * 200 + 100; + + if ( objectType == "cube" ) { + + geom = new THREE.BoxGeometry( 1, 1, 1 ); + scale.y = Math.random() * 200 + 100; + scale.z = Math.random() * 200 + 100; + color.setRGB( 0, 0, Math.random() + 0.1 ); + + } else if ( objectType == "sphere" ) { + + geom = new THREE.IcosahedronGeometry( 1, 1 ); + scale.y = scale.z = scale.x; + color.setRGB( Math.random() + 0.1, 0, 0 ); + + } + + // give the geom's vertices a random color, to be displayed + applyVertexColors( geom, color ); + + var mesh = new THREE.Mesh( geom ); + mesh.position.copy( position ); + mesh.rotation.copy( rotation ); + mesh.scale.copy( scale ); + mesh.updateMatrix(); + + geometry.merge( mesh.geometry, mesh.matrix ); + + } + + return geometry; + +} + +function Scene ( type, numObjects, cameraZ, fov, rotationSpeed, clearColor ) { + + this.clearColor = clearColor; + + this.camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 1, 10000 ); + this.camera.position.z = cameraZ; + + // Setup scene + this.scene = new THREE.Scene(); + this.scene.add( new THREE.AmbientLight( 0x555555 ) ); + + var light = new THREE.SpotLight( 0xffffff, 1.5 ); + light.position.set( 0, 500, 2000 ); + this.scene.add( light ); + + this.rotationSpeed = rotationSpeed; + defaultMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff, shading: THREE.FlatShading, vertexColors: THREE.VertexColors } ); + this.mesh = new THREE.Mesh( generateGeometry( type, numObjects ), defaultMaterial ); + this.scene.add( this.mesh ); + + renderTargetParameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false }; + this.fbo = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, renderTargetParameters ); + + this.render = function( delta, rtt ) { + + this.mesh.rotation.x += delta * this.rotationSpeed.x; + this.mesh.rotation.y += delta * this.rotationSpeed.y; + this.mesh.rotation.z += delta * this.rotationSpeed.z; + + renderer.setClearColor( this.clearColor ); + + if ( rtt ) + renderer.render( this.scene, this.camera, this.fbo, true ); + else + renderer.render( this.scene, this.camera ); + + }; + +} diff --git a/node_modules/three/examples/js/crossfade/transition.js b/node_modules/three/examples/js/crossfade/transition.js new file mode 100644 index 00000000..c6e755d1 --- /dev/null +++ b/node_modules/three/examples/js/crossfade/transition.js @@ -0,0 +1,167 @@ +function Transition ( sceneA, sceneB ) { + + this.scene = new THREE.Scene(); + + this.cameraOrtho = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, - 10, 10 ); + + this.textures = []; + for ( var i = 0; i < 6; i ++ ) + this.textures[ i ] = new THREE.ImageUtils.loadTexture( 'textures/transition/transition' + ( i + 1 ) + '.png' ); + + this.quadmaterial = new THREE.ShaderMaterial( { + + uniforms: { + + tDiffuse1: { + type: "t", + value: null + }, + tDiffuse2: { + type: "t", + value: null + }, + mixRatio: { + type: "f", + value: 0.0 + }, + threshold: { + type: "f", + value: 0.1 + }, + useTexture: { + type: "i", + value: 1, + }, + tMixTexture: { + type: "t", + value: this.textures[ 0 ] + } + }, + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = vec2( uv.x, uv.y );", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + fragmentShader: [ + + "uniform float mixRatio;", + + "uniform sampler2D tDiffuse1;", + "uniform sampler2D tDiffuse2;", + "uniform sampler2D tMixTexture;", + + "uniform int useTexture;", + "uniform float threshold;", + + "varying vec2 vUv;", + + "void main() {", + + "vec4 texel1 = texture2D( tDiffuse1, vUv );", + "vec4 texel2 = texture2D( tDiffuse2, vUv );", + + "if (useTexture==1) {", + + "vec4 transitionTexel = texture2D( tMixTexture, vUv );", + "float r = mixRatio * (1.0 + threshold * 2.0) - threshold;", + "float mixf=clamp((transitionTexel.r - r)*(1.0/threshold), 0.0, 1.0);", + + "gl_FragColor = mix( texel1, texel2, mixf );", + "} else {", + + "gl_FragColor = mix( texel2, texel1, mixRatio );", + + "}", + "}" + + ].join( "\n" ) + + } ); + + quadgeometry = new THREE.PlaneBufferGeometry( window.innerWidth, window.innerHeight ); + + this.quad = new THREE.Mesh( quadgeometry, this.quadmaterial ); + this.scene.add( this.quad ); + + // Link both scenes and their FBOs + this.sceneA = sceneA; + this.sceneB = sceneB; + + this.quadmaterial.uniforms.tDiffuse1.value = sceneA.fbo; + this.quadmaterial.uniforms.tDiffuse2.value = sceneB.fbo; + + this.needChange = false; + + this.setTextureThreshold = function ( value ) { + + this.quadmaterial.uniforms.threshold.value = value; + + }; + + this.useTexture = function ( value ) { + + this.quadmaterial.uniforms.useTexture.value = value ? 1 : 0; + + }; + + this.setTexture = function ( i ) { + + this.quadmaterial.uniforms.tMixTexture.value = this.textures[ i ]; + + }; + + this.render = function( delta ) { + + // Transition animation + if ( transitionParams.animateTransition ) { + + var t = ( 1 + Math.sin( transitionParams.transitionSpeed * clock.getElapsedTime() / Math.PI ) ) / 2; + transitionParams.transition = THREE.Math.smoothstep( t, 0.3, 0.7 ); + + // Change the current alpha texture after each transition + if ( transitionParams.loopTexture && ( transitionParams.transition == 0 || transitionParams.transition == 1 ) ) { + + if ( this.needChange ) { + + transitionParams.texture = ( transitionParams.texture + 1 ) % this.textures.length; + this.quadmaterial.uniforms.tMixTexture.value = this.textures[ transitionParams.texture ]; + this.needChange = false; + + } + + } else + this.needChange = true; + + } + + this.quadmaterial.uniforms.mixRatio.value = transitionParams.transition; + + // Prevent render both scenes when it's not necessary + if ( transitionParams.transition == 0 ) { + + this.sceneB.render( delta, false ); + + } else if ( transitionParams.transition == 1 ) { + + this.sceneA.render( delta, false ); + + } else { + + // When 0u + + // following results in (wx, wy, wz, w) homogeneous point + var hpoint = THREE.NURBSUtils.calcBSplinePoint( this.degree, this.knots, this.controlPoints, u ); + + if ( hpoint.w != 1.0 ) { + + // project to 3D space: (wx, wy, wz, w) -> (x, y, z, 1) + hpoint.divideScalar( hpoint.w ); + + } + + return new THREE.Vector3( hpoint.x, hpoint.y, hpoint.z ); + +}; + + +THREE.NURBSCurve.prototype.getTangent = function ( t ) { + + var u = this.knots[ 0 ] + t * ( this.knots[ this.knots.length - 1 ] - this.knots[ 0 ] ); + var ders = THREE.NURBSUtils.calcNURBSDerivatives( this.degree, this.knots, this.controlPoints, u, 1 ); + var tangent = ders[ 1 ].clone(); + tangent.normalize(); + + return tangent; + +}; + diff --git a/node_modules/three/examples/js/curves/NURBSSurface.js b/node_modules/three/examples/js/curves/NURBSSurface.js new file mode 100644 index 00000000..c69d85cc --- /dev/null +++ b/node_modules/three/examples/js/curves/NURBSSurface.js @@ -0,0 +1,55 @@ +/** + * @author renej + * NURBS surface object + * + * Implementation is based on (x, y [, z=0 [, w=1]]) control points with w=weight. + * + **/ + + +/************************************************************** + * NURBS surface + **************************************************************/ + +THREE.NURBSSurface = function ( degree1, degree2, knots1, knots2 /* arrays of reals */, controlPoints /* array^2 of Vector(2|3|4) */ ) { + + this.degree1 = degree1; + this.degree2 = degree2; + this.knots1 = knots1; + this.knots2 = knots2; + this.controlPoints = []; + + var len1 = knots1.length - degree1 - 1; + var len2 = knots2.length - degree2 - 1; + + // ensure Vector4 for control points + for ( var i = 0; i < len1; ++ i ) { + + this.controlPoints[ i ] = []; + for ( var j = 0; j < len2; ++ j ) { + + var point = controlPoints[ i ][ j ]; + this.controlPoints[ i ][ j ] = new THREE.Vector4( point.x, point.y, point.z, point.w ); + + } + + } + +}; + + +THREE.NURBSSurface.prototype = { + + constructor: THREE.NURBSSurface, + + getPoint: function ( t1, t2 ) { + + var u = this.knots1[ 0 ] + t1 * ( this.knots1[ this.knots1.length - 1 ] - this.knots1[ 0 ] ); // linear mapping t1->u + var v = this.knots2[ 0 ] + t2 * ( this.knots2[ this.knots2.length - 1 ] - this.knots2[ 0 ] ); // linear mapping t2->u + + return THREE.NURBSUtils.calcSurfacePoint( this.degree1, this.degree2, this.knots1, this.knots2, this.controlPoints, u, v ); + + } +}; + + diff --git a/node_modules/three/examples/js/curves/NURBSUtils.js b/node_modules/three/examples/js/curves/NURBSUtils.js new file mode 100644 index 00000000..070503ef --- /dev/null +++ b/node_modules/three/examples/js/curves/NURBSUtils.js @@ -0,0 +1,472 @@ +/** + * @author renej + * NURBS utils + * + * See NURBSCurve and NURBSSurface. + * + **/ + + +/************************************************************** + * NURBS Utils + **************************************************************/ + +THREE.NURBSUtils = { + + /* + Finds knot vector span. + + p : degree + u : parametric value + U : knot vector + + returns the span + */ + findSpan: function( p, u, U ) { + + var n = U.length - p - 1; + + if ( u >= U[ n ] ) { + + return n - 1; + + } + + if ( u <= U[ p ] ) { + + return p; + + } + + var low = p; + var high = n; + var mid = Math.floor( ( low + high ) / 2 ); + + while ( u < U[ mid ] || u >= U[ mid + 1 ] ) { + + if ( u < U[ mid ] ) { + + high = mid; + + } else { + + low = mid; + + } + + mid = Math.floor( ( low + high ) / 2 ); + + } + + return mid; + + }, + + + /* + Calculate basis functions. See The NURBS Book, page 70, algorithm A2.2 + + span : span in which u lies + u : parametric point + p : degree + U : knot vector + + returns array[p+1] with basis functions values. + */ + calcBasisFunctions: function( span, u, p, U ) { + + var N = []; + var left = []; + var right = []; + N[ 0 ] = 1.0; + + for ( var j = 1; j <= p; ++ j ) { + + left[ j ] = u - U[ span + 1 - j ]; + right[ j ] = U[ span + j ] - u; + + var saved = 0.0; + + for ( var r = 0; r < j; ++ r ) { + + var rv = right[ r + 1 ]; + var lv = left[ j - r ]; + var temp = N[ r ] / ( rv + lv ); + N[ r ] = saved + rv * temp; + saved = lv * temp; + + } + + N[ j ] = saved; + + } + + return N; + + }, + + + /* + Calculate B-Spline curve points. See The NURBS Book, page 82, algorithm A3.1. + + p : degree of B-Spline + U : knot vector + P : control points (x, y, z, w) + u : parametric point + + returns point for given u + */ + calcBSplinePoint: function( p, U, P, u ) { + + var span = this.findSpan( p, u, U ); + var N = this.calcBasisFunctions( span, u, p, U ); + var C = new THREE.Vector4( 0, 0, 0, 0 ); + + for ( var j = 0; j <= p; ++ j ) { + + var point = P[ span - p + j ]; + var Nj = N[ j ]; + var wNj = point.w * Nj; + C.x += point.x * wNj; + C.y += point.y * wNj; + C.z += point.z * wNj; + C.w += point.w * Nj; + + } + + return C; + + }, + + + /* + Calculate basis functions derivatives. See The NURBS Book, page 72, algorithm A2.3. + + span : span in which u lies + u : parametric point + p : degree + n : number of derivatives to calculate + U : knot vector + + returns array[n+1][p+1] with basis functions derivatives + */ + calcBasisFunctionDerivatives: function( span, u, p, n, U ) { + + var zeroArr = []; + for ( var i = 0; i <= p; ++ i ) + zeroArr[ i ] = 0.0; + + var ders = []; + for ( var i = 0; i <= n; ++ i ) + ders[ i ] = zeroArr.slice( 0 ); + + var ndu = []; + for ( var i = 0; i <= p; ++ i ) + ndu[ i ] = zeroArr.slice( 0 ); + + ndu[ 0 ][ 0 ] = 1.0; + + var left = zeroArr.slice( 0 ); + var right = zeroArr.slice( 0 ); + + for ( var j = 1; j <= p; ++ j ) { + + left[ j ] = u - U[ span + 1 - j ]; + right[ j ] = U[ span + j ] - u; + + var saved = 0.0; + + for ( var r = 0; r < j; ++ r ) { + + var rv = right[ r + 1 ]; + var lv = left[ j - r ]; + ndu[ j ][ r ] = rv + lv; + + var temp = ndu[ r ][ j - 1 ] / ndu[ j ][ r ]; + ndu[ r ][ j ] = saved + rv * temp; + saved = lv * temp; + + } + + ndu[ j ][ j ] = saved; + + } + + for ( var j = 0; j <= p; ++ j ) { + + ders[ 0 ][ j ] = ndu[ j ][ p ]; + + } + + for ( var r = 0; r <= p; ++ r ) { + + var s1 = 0; + var s2 = 1; + + var a = []; + for ( var i = 0; i <= p; ++ i ) { + + a[ i ] = zeroArr.slice( 0 ); + + } + a[ 0 ][ 0 ] = 1.0; + + for ( var k = 1; k <= n; ++ k ) { + + var d = 0.0; + var rk = r - k; + var pk = p - k; + + if ( r >= k ) { + + a[ s2 ][ 0 ] = a[ s1 ][ 0 ] / ndu[ pk + 1 ][ rk ]; + d = a[ s2 ][ 0 ] * ndu[ rk ][ pk ]; + + } + + var j1 = ( rk >= - 1 ) ? 1 : - rk; + var j2 = ( r - 1 <= pk ) ? k - 1 : p - r; + + for ( var j = j1; j <= j2; ++ j ) { + + a[ s2 ][ j ] = ( a[ s1 ][ j ] - a[ s1 ][ j - 1 ] ) / ndu[ pk + 1 ][ rk + j ]; + d += a[ s2 ][ j ] * ndu[ rk + j ][ pk ]; + + } + + if ( r <= pk ) { + + a[ s2 ][ k ] = - a[ s1 ][ k - 1 ] / ndu[ pk + 1 ][ r ]; + d += a[ s2 ][ k ] * ndu[ r ][ pk ]; + + } + + ders[ k ][ r ] = d; + + var j = s1; + s1 = s2; + s2 = j; + + } + + } + + var r = p; + + for ( var k = 1; k <= n; ++ k ) { + + for ( var j = 0; j <= p; ++ j ) { + + ders[ k ][ j ] *= r; + + } + r *= p - k; + + } + + return ders; + + }, + + + /* + Calculate derivatives of a B-Spline. See The NURBS Book, page 93, algorithm A3.2. + + p : degree + U : knot vector + P : control points + u : Parametric points + nd : number of derivatives + + returns array[d+1] with derivatives + */ + calcBSplineDerivatives: function( p, U, P, u, nd ) { + + var du = nd < p ? nd : p; + var CK = []; + var span = this.findSpan( p, u, U ); + var nders = this.calcBasisFunctionDerivatives( span, u, p, du, U ); + var Pw = []; + + for ( var i = 0; i < P.length; ++ i ) { + + var point = P[ i ].clone(); + var w = point.w; + + point.x *= w; + point.y *= w; + point.z *= w; + + Pw[ i ] = point; + + } + for ( var k = 0; k <= du; ++ k ) { + + var point = Pw[ span - p ].clone().multiplyScalar( nders[ k ][ 0 ] ); + + for ( var j = 1; j <= p; ++ j ) { + + point.add( Pw[ span - p + j ].clone().multiplyScalar( nders[ k ][ j ] ) ); + + } + + CK[ k ] = point; + + } + + for ( var k = du + 1; k <= nd + 1; ++ k ) { + + CK[ k ] = new THREE.Vector4( 0, 0, 0 ); + + } + + return CK; + + }, + + + /* + Calculate "K over I" + + returns k!/(i!(k-i)!) + */ + calcKoverI: function( k, i ) { + + var nom = 1; + + for ( var j = 2; j <= k; ++ j ) { + + nom *= j; + + } + + var denom = 1; + + for ( var j = 2; j <= i; ++ j ) { + + denom *= j; + + } + + for ( var j = 2; j <= k - i; ++ j ) { + + denom *= j; + + } + + return nom / denom; + + }, + + + /* + Calculate derivatives (0-nd) of rational curve. See The NURBS Book, page 127, algorithm A4.2. + + Pders : result of function calcBSplineDerivatives + + returns array with derivatives for rational curve. + */ + calcRationalCurveDerivatives: function ( Pders ) { + + var nd = Pders.length; + var Aders = []; + var wders = []; + + for ( var i = 0; i < nd; ++ i ) { + + var point = Pders[ i ]; + Aders[ i ] = new THREE.Vector3( point.x, point.y, point.z ); + wders[ i ] = point.w; + + } + + var CK = []; + + for ( var k = 0; k < nd; ++ k ) { + + var v = Aders[ k ].clone(); + + for ( var i = 1; i <= k; ++ i ) { + + v.sub( CK[ k - i ].clone().multiplyScalar( this.calcKoverI( k, i ) * wders[ i ] ) ); + + } + + CK[ k ] = v.divideScalar( wders[ 0 ] ); + + } + + return CK; + + }, + + + /* + Calculate NURBS curve derivatives. See The NURBS Book, page 127, algorithm A4.2. + + p : degree + U : knot vector + P : control points in homogeneous space + u : parametric points + nd : number of derivatives + + returns array with derivatives. + */ + calcNURBSDerivatives: function( p, U, P, u, nd ) { + + var Pders = this.calcBSplineDerivatives( p, U, P, u, nd ); + return this.calcRationalCurveDerivatives( Pders ); + + }, + + + /* + Calculate rational B-Spline surface point. See The NURBS Book, page 134, algorithm A4.3. + + p1, p2 : degrees of B-Spline surface + U1, U2 : knot vectors + P : control points (x, y, z, w) + u, v : parametric values + + returns point for given (u, v) + */ + calcSurfacePoint: function( p, q, U, V, P, u, v ) { + + var uspan = this.findSpan( p, u, U ); + var vspan = this.findSpan( q, v, V ); + var Nu = this.calcBasisFunctions( uspan, u, p, U ); + var Nv = this.calcBasisFunctions( vspan, v, q, V ); + var temp = []; + + for ( var l = 0; l <= q; ++ l ) { + + temp[ l ] = new THREE.Vector4( 0, 0, 0, 0 ); + for ( var k = 0; k <= p; ++ k ) { + + var point = P[ uspan - p + k ][ vspan - q + l ].clone(); + var w = point.w; + point.x *= w; + point.y *= w; + point.z *= w; + temp[ l ].add( point.multiplyScalar( Nu[ k ] ) ); + + } + + } + + var Sw = new THREE.Vector4( 0, 0, 0, 0 ); + for ( var l = 0; l <= q; ++ l ) { + + Sw.add( temp[ l ].multiplyScalar( Nv[ l ] ) ); + + } + + Sw.divideScalar( Sw.w ); + return new THREE.Vector3( Sw.x, Sw.y, Sw.z ); + + } + +}; + + + diff --git a/node_modules/three/examples/js/effects/AnaglyphEffect.js b/node_modules/three/examples/js/effects/AnaglyphEffect.js new file mode 100644 index 00000000..51cb5d9c --- /dev/null +++ b/node_modules/three/examples/js/effects/AnaglyphEffect.js @@ -0,0 +1,177 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author marklundin / http://mark-lundin.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.AnaglyphEffect = function ( renderer, width, height ) { + + var eyeRight = new THREE.Matrix4(); + var eyeLeft = new THREE.Matrix4(); + var focalLength = 125; + var _aspect, _near, _far, _fov; + + var _cameraL = new THREE.PerspectiveCamera(); + _cameraL.matrixAutoUpdate = false; + + var _cameraR = new THREE.PerspectiveCamera(); + _cameraR.matrixAutoUpdate = false; + + var _camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); + + var _scene = new THREE.Scene(); + + var _params = { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat }; + + if ( width === undefined ) width = 512; + if ( height === undefined ) height = 512; + + var _renderTargetL = new THREE.WebGLRenderTarget( width, height, _params ); + var _renderTargetR = new THREE.WebGLRenderTarget( width, height, _params ); + + var _material = new THREE.ShaderMaterial( { + + uniforms: { + + "mapLeft": { type: "t", value: _renderTargetL }, + "mapRight": { type: "t", value: _renderTargetR } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + " vUv = vec2( uv.x, uv.y );", + " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D mapLeft;", + "uniform sampler2D mapRight;", + "varying vec2 vUv;", + + "void main() {", + + " vec4 colorL, colorR;", + " vec2 uv = vUv;", + + " colorL = texture2D( mapLeft, uv );", + " colorR = texture2D( mapRight, uv );", + + // http://3dtv.at/Knowhow/AnaglyphComparison_en.aspx + + " gl_FragColor = vec4( colorL.g * 0.7 + colorL.b * 0.3, colorR.g, colorR.b, colorL.a + colorR.a ) * 1.1;", + + "}" + + ].join( "\n" ) + + } ); + + var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), _material ); + _scene.add( mesh ); + + this.setSize = function ( width, height ) { + + if ( _renderTargetL ) _renderTargetL.dispose(); + if ( _renderTargetR ) _renderTargetR.dispose(); + _renderTargetL = new THREE.WebGLRenderTarget( width, height, _params ); + _renderTargetR = new THREE.WebGLRenderTarget( width, height, _params ); + + _material.uniforms[ "mapLeft" ].value = _renderTargetL; + _material.uniforms[ "mapRight" ].value = _renderTargetR; + + renderer.setSize( width, height ); + + }; + + /* + * Renderer now uses an asymmetric perspective projection + * (http://paulbourke.net/miscellaneous/stereographics/stereorender/). + * + * Each camera is offset by the eye seperation and its projection matrix is + * also skewed asymetrically back to converge on the same projection plane. + * Added a focal length parameter to, this is where the parallax is equal to 0. + */ + + this.render = function ( scene, camera ) { + + scene.updateMatrixWorld(); + + if ( camera.parent === null ) camera.updateMatrixWorld(); + + var hasCameraChanged = ( _aspect !== camera.aspect ) || ( _near !== camera.near ) || ( _far !== camera.far ) || ( _fov !== camera.fov ); + + if ( hasCameraChanged ) { + + _aspect = camera.aspect; + _near = camera.near; + _far = camera.far; + _fov = camera.fov; + + var projectionMatrix = camera.projectionMatrix.clone(); + var eyeSep = focalLength / 30 * 0.5; + var eyeSepOnProjection = eyeSep * _near / focalLength; + var ymax = _near * Math.tan( THREE.Math.degToRad( _fov * 0.5 ) ); + var xmin, xmax; + + // translate xOffset + + eyeRight.elements[ 12 ] = eyeSep; + eyeLeft.elements[ 12 ] = - eyeSep; + + // for left eye + + xmin = - ymax * _aspect + eyeSepOnProjection; + xmax = ymax * _aspect + eyeSepOnProjection; + + projectionMatrix.elements[ 0 ] = 2 * _near / ( xmax - xmin ); + projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); + + _cameraL.projectionMatrix.copy( projectionMatrix ); + + // for right eye + + xmin = - ymax * _aspect - eyeSepOnProjection; + xmax = ymax * _aspect - eyeSepOnProjection; + + projectionMatrix.elements[ 0 ] = 2 * _near / ( xmax - xmin ); + projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); + + _cameraR.projectionMatrix.copy( projectionMatrix ); + + } + + _cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft ); + _cameraL.position.copy( camera.position ); + _cameraL.near = camera.near; + _cameraL.far = camera.far; + + renderer.render( scene, _cameraL, _renderTargetL, true ); + + _cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight ); + _cameraR.position.copy( camera.position ); + _cameraR.near = camera.near; + _cameraR.far = camera.far; + + renderer.render( scene, _cameraR, _renderTargetR, true ); + + renderer.render( _scene, _camera ); + + }; + + this.dispose = function() { + + if ( _renderTargetL ) _renderTargetL.dispose(); + if ( _renderTargetR ) _renderTargetR.dispose(); + + } + +}; diff --git a/node_modules/three/examples/js/effects/AsciiEffect.js b/node_modules/three/examples/js/effects/AsciiEffect.js new file mode 100644 index 00000000..a411e2cd --- /dev/null +++ b/node_modules/three/examples/js/effects/AsciiEffect.js @@ -0,0 +1,283 @@ +/* + * @author zz85 / https://github.com/zz85 + * + * Ascii generation is based on http://www.nihilogic.dk/labs/jsascii/ + * Maybe more about this later with a blog post at http://lab4games.net/zz85/blog + * + * 16 April 2012 - @blurspline + */ + +THREE.AsciiEffect = function ( renderer, charSet, options ) { + + // its fun to create one your own! + + charSet = ( charSet === undefined ) ? ' .:-=+*#%@' : charSet; + + // ' .,:;=|iI+hHOE#`$'; + // darker bolder character set from https://github.com/saw/Canvas-ASCII-Art/ + // ' .\'`^",:;Il!i~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$'.split(''); + + if ( ! options ) options = {}; + + // Some ASCII settings + + var bResolution = ! options[ 'resolution' ] ? 0.15 : options[ 'resolution' ]; // Higher for more details + var iScale = ! options[ 'scale' ] ? 1 : options[ 'scale' ]; + var bColor = ! options[ 'color' ] ? false : options[ 'color' ]; // nice but slows down rendering! + var bAlpha = ! options[ 'alpha' ] ? false : options[ 'alpha' ]; // Transparency + var bBlock = ! options[ 'block' ] ? false : options[ 'block' ]; // blocked characters. like good O dos + var bInvert = ! options[ 'invert' ] ? false : options[ 'invert' ]; // black is white, white is black + + var strResolution = 'low'; + + var width, height; + + var domElement = document.createElement( 'div' ); + domElement.style.cursor = 'default'; + + var oAscii = document.createElement( "table" ); + domElement.appendChild( oAscii ); + + var iWidth, iHeight; + var oImg; + + this.setSize = function ( w, h ) { + + width = w; + height = h; + + renderer.setSize( w, h ); + + initAsciiSize(); + + }; + + + this.render = function ( scene, camera ) { + + renderer.render( scene, camera ); + asciifyImage( renderer, oAscii ); + + }; + + this.domElement = domElement; + + + // Throw in ascii library from http://www.nihilogic.dk/labs/jsascii/jsascii.js + + /* + * jsAscii 0.1 + * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk, http://blog.nihilogic.dk/ + * MIT License [http://www.nihilogic.dk/licenses/mit-license.txt] + */ + + function initAsciiSize() { + + iWidth = Math.round( width * fResolution ); + iHeight = Math.round( height * fResolution ); + + oCanvas.width = iWidth; + oCanvas.height = iHeight; + // oCanvas.style.display = "none"; + // oCanvas.style.width = iWidth; + // oCanvas.style.height = iHeight; + + oImg = renderer.domElement; + + if ( oImg.style.backgroundColor ) { + + oAscii.rows[ 0 ].cells[ 0 ].style.backgroundColor = oImg.style.backgroundColor; + oAscii.rows[ 0 ].cells[ 0 ].style.color = oImg.style.color; + + } + + oAscii.cellSpacing = 0; + oAscii.cellPadding = 0; + + var oStyle = oAscii.style; + oStyle.display = "inline"; + oStyle.width = Math.round( iWidth / fResolution * iScale ) + "px"; + oStyle.height = Math.round( iHeight / fResolution * iScale ) + "px"; + oStyle.whiteSpace = "pre"; + oStyle.margin = "0px"; + oStyle.padding = "0px"; + oStyle.letterSpacing = fLetterSpacing + "px"; + oStyle.fontFamily = strFont; + oStyle.fontSize = fFontSize + "px"; + oStyle.lineHeight = fLineHeight + "px"; + oStyle.textAlign = "left"; + oStyle.textDecoration = "none"; + + } + + + var aDefaultCharList = ( " .,:;i1tfLCG08@" ).split( "" ); + var aDefaultColorCharList = ( " CGO08@" ).split( "" ); + var strFont = "courier new, monospace"; + + var oCanvasImg = renderer.domElement; + + var oCanvas = document.createElement( "canvas" ); + if ( ! oCanvas.getContext ) { + + return; + + } + + var oCtx = oCanvas.getContext( "2d" ); + if ( ! oCtx.getImageData ) { + + return; + + } + + var aCharList = ( bColor ? aDefaultColorCharList : aDefaultCharList ); + + if ( charSet ) aCharList = charSet; + + var fResolution = 0.5; + + switch ( strResolution ) { + + case "low" : fResolution = 0.25; break; + case "medium" : fResolution = 0.5; break; + case "high" : fResolution = 1; break; + + } + + if ( bResolution ) fResolution = bResolution; + + // Setup dom + + var fFontSize = ( 2 / fResolution ) * iScale; + var fLineHeight = ( 2 / fResolution ) * iScale; + + // adjust letter-spacing for all combinations of scale and resolution to get it to fit the image width. + + var fLetterSpacing = 0; + + if ( strResolution == "low" ) { + + switch ( iScale ) { + case 1 : fLetterSpacing = - 1; break; + case 2 : + case 3 : fLetterSpacing = - 2.1; break; + case 4 : fLetterSpacing = - 3.1; break; + case 5 : fLetterSpacing = - 4.15; break; + } + + } + + if ( strResolution == "medium" ) { + + switch ( iScale ) { + case 1 : fLetterSpacing = 0; break; + case 2 : fLetterSpacing = - 1; break; + case 3 : fLetterSpacing = - 1.04; break; + case 4 : + case 5 : fLetterSpacing = - 2.1; break; + } + + } + + if ( strResolution == "high" ) { + + switch ( iScale ) { + case 1 : + case 2 : fLetterSpacing = 0; break; + case 3 : + case 4 : + case 5 : fLetterSpacing = - 1; break; + } + + } + + + // can't get a span or div to flow like an img element, but a table works? + + + // convert img element to ascii + + function asciifyImage( canvasRenderer, oAscii ) { + + oCtx.clearRect( 0, 0, iWidth, iHeight ); + oCtx.drawImage( oCanvasImg, 0, 0, iWidth, iHeight ); + var oImgData = oCtx.getImageData( 0, 0, iWidth, iHeight ).data; + + // Coloring loop starts now + var strChars = ""; + + // console.time('rendering'); + + for ( var y = 0; y < iHeight; y += 2 ) { + + for ( var x = 0; x < iWidth; x ++ ) { + + var iOffset = ( y * iWidth + x ) * 4; + + var iRed = oImgData[ iOffset ]; + var iGreen = oImgData[ iOffset + 1 ]; + var iBlue = oImgData[ iOffset + 2 ]; + var iAlpha = oImgData[ iOffset + 3 ]; + var iCharIdx; + + var fBrightness; + + fBrightness = ( 0.3 * iRed + 0.59 * iGreen + 0.11 * iBlue ) / 255; + // fBrightness = (0.3*iRed + 0.5*iGreen + 0.3*iBlue) / 255; + + if ( iAlpha == 0 ) { + + // should calculate alpha instead, but quick hack :) + //fBrightness *= (iAlpha / 255); + fBrightness = 1; + + } + + iCharIdx = Math.floor( ( 1 - fBrightness ) * ( aCharList.length - 1 ) ); + + if ( bInvert ) { + + iCharIdx = aCharList.length - iCharIdx - 1; + + } + + // good for debugging + //fBrightness = Math.floor(fBrightness * 10); + //strThisChar = fBrightness; + + var strThisChar = aCharList[ iCharIdx ]; + + if ( strThisChar === undefined || strThisChar == " " ) + strThisChar = " "; + + if ( bColor ) { + + strChars += "" + strThisChar + ""; + + } else { + + strChars += strThisChar; + + } + + } + strChars += "
"; + + } + + oAscii.innerHTML = "" + strChars + ""; + + // console.timeEnd('rendering'); + + // return oAscii; + + } + + // end modified asciifyImage block + +}; diff --git a/node_modules/three/examples/js/effects/ParallaxBarrierEffect.js b/node_modules/three/examples/js/effects/ParallaxBarrierEffect.js new file mode 100644 index 00000000..c523e82d --- /dev/null +++ b/node_modules/three/examples/js/effects/ParallaxBarrierEffect.js @@ -0,0 +1,171 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author marklundin / http://mark-lundin.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ParallaxBarrierEffect = function ( renderer ) { + + var eyeRight = new THREE.Matrix4(); + var eyeLeft = new THREE.Matrix4(); + var focalLength = 125; + var _aspect, _near, _far, _fov; + + var _cameraL = new THREE.PerspectiveCamera(); + _cameraL.matrixAutoUpdate = false; + + var _cameraR = new THREE.PerspectiveCamera(); + _cameraR.matrixAutoUpdate = false; + + var _scene = new THREE.Scene(); + + var _camera = new THREE.PerspectiveCamera( 53, 1, 1, 10000 ); + _camera.position.z = 2; + _scene.add( _camera ); + + var _params = { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat }; + + var _renderTargetL = new THREE.WebGLRenderTarget( 512, 512, _params ); + var _renderTargetR = new THREE.WebGLRenderTarget( 512, 512, _params ); + + var _material = new THREE.ShaderMaterial( { + + uniforms: { + + "mapLeft": { type: "t", value: _renderTargetL }, + "mapRight": { type: "t", value: _renderTargetR } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + " vUv = vec2( uv.x, uv.y );", + " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D mapLeft;", + "uniform sampler2D mapRight;", + "varying vec2 vUv;", + + "void main() {", + + " vec2 uv = vUv;", + + " if ( ( mod( gl_FragCoord.y, 2.0 ) ) > 1.00 ) {", + + " gl_FragColor = texture2D( mapLeft, uv );", + + " } else {", + + " gl_FragColor = texture2D( mapRight, uv );", + + " }", + + "}" + + ].join( "\n" ) + + } ); + + var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), _material ); + _scene.add( mesh ); + + this.setSize = function ( width, height ) { + + _renderTargetL = new THREE.WebGLRenderTarget( width, height, _params ); + _renderTargetR = new THREE.WebGLRenderTarget( width, height, _params ); + + _material.uniforms[ "mapLeft" ].value = _renderTargetL; + _material.uniforms[ "mapRight" ].value = _renderTargetR; + + renderer.setSize( width, height ); + + }; + + /* + * Renderer now uses an asymmetric perspective projection + * (http://paulbourke.net/miscellaneous/stereographics/stereorender/). + * + * Each camera is offset by the eye seperation and its projection matrix is + * also skewed asymetrically back to converge on the same projection plane. + * Added a focal length parameter to, this is where the parallax is equal to 0. + */ + + this.render = function ( scene, camera ) { + + scene.updateMatrixWorld(); + + if ( camera.parent === null ) camera.updateMatrixWorld(); + + var hasCameraChanged = ( _aspect !== camera.aspect ) || ( _near !== camera.near ) || ( _far !== camera.far ) || ( _fov !== camera.fov ); + + if ( hasCameraChanged ) { + + _aspect = camera.aspect; + _near = camera.near; + _far = camera.far; + _fov = camera.fov; + + var projectionMatrix = camera.projectionMatrix.clone(); + var eyeSep = focalLength / 30 * 0.5; + var eyeSepOnProjection = eyeSep * _near / focalLength; + var ymax = _near * Math.tan( THREE.Math.degToRad( _fov * 0.5 ) ); + var xmin, xmax; + + // translate xOffset + + eyeRight.elements[ 12 ] = eyeSep; + eyeLeft.elements[ 12 ] = - eyeSep; + + // for left eye + + xmin = - ymax * _aspect + eyeSepOnProjection; + xmax = ymax * _aspect + eyeSepOnProjection; + + projectionMatrix.elements[ 0 ] = 2 * _near / ( xmax - xmin ); + projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); + + _cameraL.projectionMatrix.copy( projectionMatrix ); + + // for right eye + + xmin = - ymax * _aspect - eyeSepOnProjection; + xmax = ymax * _aspect - eyeSepOnProjection; + + projectionMatrix.elements[ 0 ] = 2 * _near / ( xmax - xmin ); + projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); + + _cameraR.projectionMatrix.copy( projectionMatrix ); + + } + + _cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft ); + _cameraL.position.copy( camera.position ); + _cameraL.near = camera.near; + _cameraL.far = camera.far; + + renderer.render( scene, _cameraL, _renderTargetL, true ); + + _cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight ); + _cameraR.position.copy( camera.position ); + _cameraR.near = camera.near; + _cameraR.far = camera.far; + + renderer.render( scene, _cameraR, _renderTargetR, true ); + + _scene.updateMatrixWorld(); + + renderer.render( _scene, _camera ); + + }; + +}; diff --git a/node_modules/three/examples/js/effects/StereoEffect.js b/node_modules/three/examples/js/effects/StereoEffect.js new file mode 100644 index 00000000..37a74fac --- /dev/null +++ b/node_modules/three/examples/js/effects/StereoEffect.js @@ -0,0 +1,148 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * @authod mrdoob / http://mrdoob.com/ + * @authod arodic / http://aleksandarrodic.com/ + * @authod fonserbc / http://fonserbc.github.io/ + * + * Off-axis stereoscopic effect based on http://paulbourke.net/stereographics/stereorender/ + */ + +THREE.StereoEffect = function ( renderer ) { + + // API + + var scope = this; + + this.eyeSeparation = 3; + this.focalLength = 15; // Distance to the non-parallax or projection plane + + Object.defineProperties( this, { + separation: { + get: function () { + + return scope.eyeSeparation; + + }, + set: function ( value ) { + + console.warn( 'THREE.StereoEffect: .separation is now .eyeSeparation.' ); + scope.eyeSeparation = value; + + } + }, + targetDistance: { + get: function () { + + return scope.focalLength; + + }, + set: function ( value ) { + + console.warn( 'THREE.StereoEffect: .targetDistance is now .focalLength.' ); + scope.focalLength = value; + + } + } + } ); + + // internals + + var _width, _height; + + var _position = new THREE.Vector3(); + var _quaternion = new THREE.Quaternion(); + var _scale = new THREE.Vector3(); + + var _cameraL = new THREE.PerspectiveCamera(); + var _cameraR = new THREE.PerspectiveCamera(); + + var _fov; + var _outer, _inner, _top, _bottom; + var _ndfl, _halfFocalWidth, _halfFocalHeight; + var _innerFactor, _outerFactor; + + // initialization + + renderer.autoClear = false; + + this.setSize = function ( width, height ) { + + _width = width / 2; + _height = height; + + renderer.setSize( width, height ); + + }; + + this.render = function ( scene, camera ) { + + scene.updateMatrixWorld(); + + if ( camera.parent === null ) camera.updateMatrixWorld(); + + camera.matrixWorld.decompose( _position, _quaternion, _scale ); + + // Effective fov of the camera + + _fov = THREE.Math.radToDeg( 2 * Math.atan( Math.tan( THREE.Math.degToRad( camera.fov ) * 0.5 ) / camera.zoom ) ); + + _ndfl = camera.near / this.focalLength; + _halfFocalHeight = Math.tan( THREE.Math.degToRad( _fov ) * 0.5 ) * this.focalLength; + _halfFocalWidth = _halfFocalHeight * 0.5 * camera.aspect; + + _top = _halfFocalHeight * _ndfl; + _bottom = - _top; + _innerFactor = ( _halfFocalWidth + this.eyeSeparation / 2.0 ) / ( _halfFocalWidth * 2.0 ); + _outerFactor = 1.0 - _innerFactor; + + _outer = _halfFocalWidth * 2.0 * _ndfl * _outerFactor; + _inner = _halfFocalWidth * 2.0 * _ndfl * _innerFactor; + + // left + + _cameraL.projectionMatrix.makeFrustum( + - _outer, + _inner, + _bottom, + _top, + camera.near, + camera.far + ); + + _cameraL.position.copy( _position ); + _cameraL.quaternion.copy( _quaternion ); + _cameraL.translateX( - this.eyeSeparation / 2.0 ); + + // right + + _cameraR.projectionMatrix.makeFrustum( + - _inner, + _outer, + _bottom, + _top, + camera.near, + camera.far + ); + + _cameraR.position.copy( _position ); + _cameraR.quaternion.copy( _quaternion ); + _cameraR.translateX( this.eyeSeparation / 2.0 ); + + // + + renderer.clear(); + renderer.enableScissorTest( true ); + + renderer.setScissor( 0, 0, _width, _height ); + renderer.setViewport( 0, 0, _width, _height ); + renderer.render( scene, _cameraL ); + + renderer.setScissor( _width, 0, _width, _height ); + renderer.setViewport( _width, 0, _width, _height ); + renderer.render( scene, _cameraR ); + + renderer.enableScissorTest( false ); + + }; + +}; diff --git a/node_modules/three/examples/js/effects/VREffect.js b/node_modules/three/examples/js/effects/VREffect.js new file mode 100644 index 00000000..e4574979 --- /dev/null +++ b/node_modules/three/examples/js/effects/VREffect.js @@ -0,0 +1,258 @@ +/** + * @author dmarcos / https://github.com/dmarcos + * @author mrdoob / http://mrdoob.com + * + * WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html + * + * Firefox: http://mozvr.com/downloads/ + * Chromium: https://drive.google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ&usp=sharing#list + * + */ + +var VREffect = function ( renderer, onError ) { + + var vrHMD; + var eyeTranslationL, eyeFOVL, rectL; + var eyeTranslationR, eyeFOVR, rectR; + + function gotVRDevices( devices ) { + + for ( var i = 0; i < devices.length; i ++ ) { + + if ( devices[ i ] instanceof HMDVRDevice ) { + + vrHMD = devices[ i ]; + + break; // We keep the first we encounter + + } + + } + + if ( vrHMD === undefined ) { + + if ( onError ) onError( 'HMD not available' ); + + } + + } + + if ( navigator.getVRDevices ) { + + navigator.getVRDevices().then( gotVRDevices ); + + } + + // + + this.scale = 1; + + this.setSize = function( width, height ) { + + renderer.setSize( width, height ); + + }; + + // fullscreen + + var isFullscreen = false; + + var canvas = renderer.domElement; + var fullscreenchange = canvas.mozRequestFullScreen ? 'mozfullscreenchange' : 'webkitfullscreenchange'; + + document.addEventListener( fullscreenchange, function ( event ) { + + isFullscreen = document.mozFullScreenElement || document.webkitFullscreenElement; + + }, false ); + + this.setFullScreen = function ( boolean ) { + + if ( vrHMD === undefined ) return; + if ( isFullscreen === boolean ) return; + + if ( canvas.mozRequestFullScreen ) { + + canvas.mozRequestFullScreen( { vrDisplay: vrHMD } ); + + } else if ( canvas.webkitRequestFullscreen ) { + + canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } ); + + } + + }; + + // render + + var cameraL = new THREE.PerspectiveCamera(); + var cameraR = new THREE.PerspectiveCamera(); + + this.render = function ( scene, camera ) { + + if ( vrHMD ) { + + var eyeParamsL = vrHMD.getEyeParameters( 'left' ); + var eyeParamsR = vrHMD.getEyeParameters( 'right' ); + + eyeTranslationL = eyeParamsL.eyeTranslation; + eyeTranslationR = eyeParamsR.eyeTranslation; + eyeFOVL = eyeParamsL.recommendedFieldOfView; + eyeFOVR = eyeParamsR.recommendedFieldOfView; + rectL = eyeParamsL.renderRect; + rectR = eyeParamsR.renderRect; + + var sceneL, sceneR; + + if ( Array.isArray( scene ) ) { + + sceneL = scene[ 0 ]; + sceneR = scene[ 1 ]; + + } else { + + sceneL = scene; + sceneR = scene; + + } + + var size = renderer.getSize(); + size.width /= 2; + + renderer.enableScissorTest( true ); + renderer.clear(); + + if ( camera.parent === null ) camera.updateMatrixWorld(); + + cameraL.projectionMatrix = fovToProjection( eyeFOVL, true, camera.near, camera.far ); + cameraR.projectionMatrix = fovToProjection( eyeFOVR, true, camera.near, camera.far ); + + camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale ); + camera.matrixWorld.decompose( cameraR.position, cameraR.quaternion, cameraR.scale ); + + cameraL.translateX( eyeTranslationL.x * this.scale ); + cameraR.translateX( eyeTranslationR.x * this.scale ); + + // render left eye + if ( rectL ) { + + renderer.setViewport( rectL.x, rectL.y, rectL.width, rectL.height ); + renderer.setScissor( rectL.x, rectL.y, rectL.width, rectL.height ); + + } else { + + renderer.setViewport( 0, 0, size.width, size.height ); + renderer.setScissor( 0, 0, size.width, size.height ); + + } + renderer.render( sceneL, cameraL ); + + // render right eye + if ( rectR ) { + + renderer.setViewport( rectR.x, rectR.y, rectR.width, rectR.height ); + renderer.setScissor( rectR.x, rectR.y, rectR.width, rectR.height ); + + } else { + + renderer.setViewport( size.width, 0, size.width, size.height ); + renderer.setScissor( size.width, 0, size.width, size.height ); + + } + renderer.render( sceneR, cameraR ); + + renderer.enableScissorTest( false ); + + return; + + } + + // Regular render mode if not HMD + + if ( Array.isArray( scene ) ) scene = scene[ 0 ]; + + renderer.render( scene, camera ); + + }; + + // + + function fovToNDCScaleOffset( fov ) { + + var pxscale = 2.0 / ( fov.leftTan + fov.rightTan ); + var pxoffset = ( fov.leftTan - fov.rightTan ) * pxscale * 0.5; + var pyscale = 2.0 / ( fov.upTan + fov.downTan ); + var pyoffset = ( fov.upTan - fov.downTan ) * pyscale * 0.5; + return { scale: [ pxscale, pyscale ], offset: [ pxoffset, pyoffset ] }; + + } + + function fovPortToProjection( fov, rightHanded, zNear, zFar ) { + + rightHanded = rightHanded === undefined ? true : rightHanded; + zNear = zNear === undefined ? 0.01 : zNear; + zFar = zFar === undefined ? 10000.0 : zFar; + + var handednessScale = rightHanded ? - 1.0 : 1.0; + + // start with an identity matrix + var mobj = new THREE.Matrix4(); + var m = mobj.elements; + + // and with scale/offset info for normalized device coords + var scaleAndOffset = fovToNDCScaleOffset( fov ); + + // X result, map clip edges to [-w,+w] + m[ 0 * 4 + 0 ] = scaleAndOffset.scale[ 0 ]; + m[ 0 * 4 + 1 ] = 0.0; + m[ 0 * 4 + 2 ] = scaleAndOffset.offset[ 0 ] * handednessScale; + m[ 0 * 4 + 3 ] = 0.0; + + // Y result, map clip edges to [-w,+w] + // Y offset is negated because this proj matrix transforms from world coords with Y=up, + // but the NDC scaling has Y=down (thanks D3D?) + m[ 1 * 4 + 0 ] = 0.0; + m[ 1 * 4 + 1 ] = scaleAndOffset.scale[ 1 ]; + m[ 1 * 4 + 2 ] = - scaleAndOffset.offset[ 1 ] * handednessScale; + m[ 1 * 4 + 3 ] = 0.0; + + // Z result (up to the app) + m[ 2 * 4 + 0 ] = 0.0; + m[ 2 * 4 + 1 ] = 0.0; + m[ 2 * 4 + 2 ] = zFar / ( zNear - zFar ) * - handednessScale; + m[ 2 * 4 + 3 ] = ( zFar * zNear ) / ( zNear - zFar ); + + // W result (= Z in) + m[ 3 * 4 + 0 ] = 0.0; + m[ 3 * 4 + 1 ] = 0.0; + m[ 3 * 4 + 2 ] = handednessScale; + m[ 3 * 4 + 3 ] = 0.0; + + mobj.transpose(); + + return mobj; + + } + + function fovToProjection( fov, rightHanded, zNear, zFar ) { + + var DEG2RAD = Math.PI / 180.0; + + var fovPort = { + upTan: Math.tan( fov.upDegrees * DEG2RAD ), + downTan: Math.tan( fov.downDegrees * DEG2RAD ), + leftTan: Math.tan( fov.leftDegrees * DEG2RAD ), + rightTan: Math.tan( fov.rightDegrees * DEG2RAD ) + }; + + return fovPortToProjection( fovPort, rightHanded, zNear, zFar ); + + } + +}; + +try { + module.exports = VREffect; +} catch (e) { + THREE.VREffect = VREffect; +} diff --git a/node_modules/three/examples/js/exporters/OBJExporter.js b/node_modules/three/examples/js/exporters/OBJExporter.js new file mode 100644 index 00000000..4fb73847 --- /dev/null +++ b/node_modules/three/examples/js/exporters/OBJExporter.js @@ -0,0 +1,148 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.OBJExporter = function () {}; + +THREE.OBJExporter.prototype = { + + constructor: THREE.OBJExporter, + + parse: function ( object ) { + + var output = ''; + + var indexVertex = 0; + var indexVertexUvs = 0; + var indexNormals = 0; + + var parseMesh = function ( mesh ) { + + var nbVertex = 0; + var nbVertexUvs = 0; + var nbNormals = 0; + + var geometry = mesh.geometry; + + if ( geometry instanceof THREE.Geometry ) { + + output += 'o ' + mesh.name + '\n'; + + var vertices = geometry.vertices; + + for ( var i = 0, l = vertices.length; i < l; i ++ ) { + + var vertex = vertices[ i ].clone(); + vertex.applyMatrix4( mesh.matrixWorld ); + + output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n'; + + nbVertex ++; + + } + + // uvs + + var faces = geometry.faces; + var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; + var hasVertexUvs = faces.length === faceVertexUvs.length; + + if ( hasVertexUvs ) { + + for ( var i = 0, l = faceVertexUvs.length; i < l; i ++ ) { + + var vertexUvs = faceVertexUvs[ i ]; + + for ( var j = 0, jl = vertexUvs.length; j < jl; j ++ ) { + + var uv = vertexUvs[ j ]; + + output += 'vt ' + uv.x + ' ' + uv.y + '\n'; + + nbVertexUvs ++; + + } + + } + + } + + // normals + + var normalMatrixWorld = new THREE.Matrix3(); + normalMatrixWorld.getNormalMatrix( mesh.matrixWorld ); + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + var vertexNormals = face.vertexNormals; + + if ( vertexNormals.length === 3 ) { + + for ( var j = 0, jl = vertexNormals.length; j < jl; j ++ ) { + + var normal = vertexNormals[ j ].clone(); + normal.applyMatrix3( normalMatrixWorld ); + + output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n'; + + nbNormals ++; + + } + + } else { + + var normal = face.normal.clone(); + normal.applyMatrix3( normalMatrixWorld ); + + for ( var j = 0; j < 3; j ++ ) { + + output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n'; + + nbNormals ++; + + } + + } + + } + + // faces + + + for ( var i = 0, j = 1, l = faces.length; i < l; i ++, j += 3 ) { + + var face = faces[ i ]; + + output += 'f '; + output += ( indexVertex + face.a + 1 ) + '/' + ( hasVertexUvs ? ( indexVertexUvs + j ) : '' ) + '/' + ( indexNormals + j ) + ' '; + output += ( indexVertex + face.b + 1 ) + '/' + ( hasVertexUvs ? ( indexVertexUvs + j + 1 ) : '' ) + '/' + ( indexNormals + j + 1 ) + ' '; + output += ( indexVertex + face.c + 1 ) + '/' + ( hasVertexUvs ? ( indexVertexUvs + j + 2 ) : '' ) + '/' + ( indexNormals + j + 2 ) + '\n'; + + } + + } else { + + console.warn( 'THREE.OBJExporter.parseMesh(): geometry type unsupported', mesh ); + // TODO: Support only BufferGeometry and use use setFromObject() + + } + + // update index + indexVertex += nbVertex; + indexVertexUvs += nbVertexUvs; + indexNormals += nbNormals; + + }; + + object.traverse( function ( child ) { + + if ( child instanceof THREE.Mesh ) parseMesh( child ); + + } ); + + return output; + + } + +}; diff --git a/node_modules/three/examples/js/exporters/STLBinaryExporter.js b/node_modules/three/examples/js/exporters/STLBinaryExporter.js new file mode 100644 index 00000000..c55761f5 --- /dev/null +++ b/node_modules/three/examples/js/exporters/STLBinaryExporter.js @@ -0,0 +1,81 @@ +/** + * @author kovacsv / http://kovacsv.hu/ + * @author mrdoob / http://mrdoob.com/ + * @author mudcube / http://mudcu.be/ + */ + +THREE.STLBinaryExporter = function () {}; + +THREE.STLBinaryExporter.prototype = { + + constructor: THREE.STLBinaryExporter, + + parse: ( function () { + + var vector = new THREE.Vector3(); + var normalMatrixWorld = new THREE.Matrix3(); + + return function parse( scene ) { + + var triangles = 0; + scene.traverse( function ( object ) { + + if ( ! ( object instanceof THREE.Mesh ) ) return; + triangles += object.geometry.faces.length; + + } ); + + var offset = 80; // skip header + var bufferLength = triangles * 2 + triangles * 3 * 4 * 4 + 80 + 4; + var arrayBuffer = new ArrayBuffer( bufferLength ); + var output = new DataView( arrayBuffer ); + output.setUint32( offset, triangles, true ); offset += 4; + + scene.traverse( function ( object ) { + + if ( ! ( object instanceof THREE.Mesh ) ) return; + if ( ! ( object.geometry instanceof THREE.Geometry ) ) return; + + var geometry = object.geometry; + var matrixWorld = object.matrixWorld; + + var vertices = geometry.vertices; + var faces = geometry.faces; + + normalMatrixWorld.getNormalMatrix( matrixWorld ); + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + vector.copy( face.normal ).applyMatrix3( normalMatrixWorld ).normalize(); + + output.setFloat32( offset, vector.x, true ); offset += 4; // normal + output.setFloat32( offset, vector.y, true ); offset += 4; + output.setFloat32( offset, vector.z, true ); offset += 4; + + var indices = [ face.a, face.b, face.c ]; + + for ( var j = 0; j < 3; j ++ ) { + + vector.copy( vertices[ indices[ j ] ] ).applyMatrix4( matrixWorld ); + + output.setFloat32( offset, vector.x, true ); offset += 4; // vertices + output.setFloat32( offset, vector.y, true ); offset += 4; + output.setFloat32( offset, vector.z, true ); offset += 4; + + } + + output.setUint16( offset, 0, true ); offset += 2; // attribute byte count + + } + + } ); + + return output; + + }; + + }() ) + +}; diff --git a/node_modules/three/examples/js/exporters/STLExporter.js b/node_modules/three/examples/js/exporters/STLExporter.js new file mode 100644 index 00000000..89c31800 --- /dev/null +++ b/node_modules/three/examples/js/exporters/STLExporter.js @@ -0,0 +1,75 @@ +/** + * @author kovacsv / http://kovacsv.hu/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.STLExporter = function () {}; + +THREE.STLExporter.prototype = { + + constructor: THREE.STLExporter, + + parse: ( function () { + + var vector = new THREE.Vector3(); + var normalMatrixWorld = new THREE.Matrix3(); + + return function parse( scene ) { + + var output = ''; + + output += 'solid exported\n'; + + scene.traverse( function ( object ) { + + if ( object instanceof THREE.Mesh ) { + + var geometry = object.geometry; + var matrixWorld = object.matrixWorld; + + if ( geometry instanceof THREE.Geometry ) { + + var vertices = geometry.vertices; + var faces = geometry.faces; + + normalMatrixWorld.getNormalMatrix( matrixWorld ); + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + vector.copy( face.normal ).applyMatrix3( normalMatrixWorld ).normalize(); + + output += '\tfacet normal ' + vector.x + ' ' + vector.y + ' ' + vector.z + '\n'; + output += '\t\touter loop\n'; + + var indices = [ face.a, face.b, face.c ]; + + for ( var j = 0; j < 3; j ++ ) { + + vector.copy( vertices[ indices[ j ] ] ).applyMatrix4( matrixWorld ); + + output += '\t\t\tvertex ' + vector.x + ' ' + vector.y + ' ' + vector.z + '\n'; + + } + + output += '\t\tendloop\n'; + output += '\tendfacet\n'; + + } + + } + + } + + } ); + + output += 'endsolid exported\n'; + + return output; + + }; + + }() ) + +}; diff --git a/node_modules/three/examples/js/exporters/TypedGeometryExporter.js b/node_modules/three/examples/js/exporters/TypedGeometryExporter.js new file mode 100644 index 00000000..2bbc08fb --- /dev/null +++ b/node_modules/three/examples/js/exporters/TypedGeometryExporter.js @@ -0,0 +1,55 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.TypedGeometryExporter = function () {}; + +THREE.TypedGeometryExporter.prototype = { + + constructor: THREE.TypedGeometryExporter, + + parse: function ( geometry ) { + + var output = { + metadata: { + version: 4.0, + type: 'TypedGeometry', + generator: 'TypedGeometryExporter' + } + }; + + var attributes = [ 'vertices', 'normals', 'uvs' ]; + + for ( var key in attributes ) { + + var attribute = attributes[ key ]; + + var typedArray = geometry[ attribute ]; + var array = []; + + for ( var i = 0, l = typedArray.length; i < l; i ++ ) { + + array[ i ] = typedArray[ i ]; + + } + + output[ attribute ] = array; + + } + + var boundingSphere = geometry.boundingSphere; + + if ( boundingSphere !== null ) { + + output.boundingSphere = { + center: boundingSphere.center.toArray(), + radius: boundingSphere.radius + } + + } + + return output; + + } + +}; diff --git a/node_modules/three/examples/js/geometries/ConvexGeometry.js b/node_modules/three/examples/js/geometries/ConvexGeometry.js new file mode 100644 index 00000000..b5c17836 --- /dev/null +++ b/node_modules/three/examples/js/geometries/ConvexGeometry.js @@ -0,0 +1,226 @@ +/** + * @author qiao / https://github.com/qiao + * @fileoverview This is a convex hull generator using the incremental method. + * The complexity is O(n^2) where n is the number of vertices. + * O(nlogn) algorithms do exist, but they are much more complicated. + * + * Benchmark: + * + * Platform: CPU: P7350 @2.00GHz Engine: V8 + * + * Num Vertices Time(ms) + * + * 10 1 + * 20 3 + * 30 19 + * 40 48 + * 50 107 + */ + +THREE.ConvexGeometry = function( vertices ) { + + THREE.Geometry.call( this ); + + var faces = [ [ 0, 1, 2 ], [ 0, 2, 1 ] ]; + + for ( var i = 3; i < vertices.length; i ++ ) { + + addPoint( i ); + + } + + + function addPoint( vertexId ) { + + var vertex = vertices[ vertexId ].clone(); + + var mag = vertex.length(); + vertex.x += mag * randomOffset(); + vertex.y += mag * randomOffset(); + vertex.z += mag * randomOffset(); + + var hole = []; + + for ( var f = 0; f < faces.length; ) { + + var face = faces[ f ]; + + // for each face, if the vertex can see it, + // then we try to add the face's edges into the hole. + if ( visible( face, vertex ) ) { + + for ( var e = 0; e < 3; e ++ ) { + + var edge = [ face[ e ], face[ ( e + 1 ) % 3 ] ]; + var boundary = true; + + // remove duplicated edges. + for ( var h = 0; h < hole.length; h ++ ) { + + if ( equalEdge( hole[ h ], edge ) ) { + + hole[ h ] = hole[ hole.length - 1 ]; + hole.pop(); + boundary = false; + break; + + } + + } + + if ( boundary ) { + + hole.push( edge ); + + } + + } + + // remove faces[ f ] + faces[ f ] = faces[ faces.length - 1 ]; + faces.pop(); + + } else { + + // not visible + + f ++; + + } + + } + + // construct the new faces formed by the edges of the hole and the vertex + for ( var h = 0; h < hole.length; h ++ ) { + + faces.push( [ + hole[ h ][ 0 ], + hole[ h ][ 1 ], + vertexId + ] ); + + } + + } + + /** + * Whether the face is visible from the vertex + */ + function visible( face, vertex ) { + + var va = vertices[ face[ 0 ] ]; + var vb = vertices[ face[ 1 ] ]; + var vc = vertices[ face[ 2 ] ]; + + var n = normal( va, vb, vc ); + + // distance from face to origin + var dist = n.dot( va ); + + return n.dot( vertex ) >= dist; + + } + + /** + * Face normal + */ + function normal( va, vb, vc ) { + + var cb = new THREE.Vector3(); + var ab = new THREE.Vector3(); + + cb.subVectors( vc, vb ); + ab.subVectors( va, vb ); + cb.cross( ab ); + + cb.normalize(); + + return cb; + + } + + /** + * Detect whether two edges are equal. + * Note that when constructing the convex hull, two same edges can only + * be of the negative direction. + */ + function equalEdge( ea, eb ) { + + return ea[ 0 ] === eb[ 1 ] && ea[ 1 ] === eb[ 0 ]; + + } + + /** + * Create a random offset between -1e-6 and 1e-6. + */ + function randomOffset() { + + return ( Math.random() - 0.5 ) * 2 * 1e-6; + + } + + + /** + * XXX: Not sure if this is the correct approach. Need someone to review. + */ + function vertexUv( vertex ) { + + var mag = vertex.length(); + return new THREE.Vector2( vertex.x / mag, vertex.y / mag ); + + } + + // Push vertices into `this.vertices`, skipping those inside the hull + var id = 0; + var newId = new Array( vertices.length ); // map from old vertex id to new id + + for ( var i = 0; i < faces.length; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0; j < 3; j ++ ) { + + if ( newId[ face[ j ] ] === undefined ) { + + newId[ face[ j ] ] = id ++; + this.vertices.push( vertices[ face[ j ] ] ); + + } + + face[ j ] = newId[ face[ j ] ]; + + } + + } + + // Convert faces into instances of THREE.Face3 + for ( var i = 0; i < faces.length; i ++ ) { + + this.faces.push( new THREE.Face3( + faces[ i ][ 0 ], + faces[ i ][ 1 ], + faces[ i ][ 2 ] + ) ); + + } + + // Compute UVs + for ( var i = 0; i < this.faces.length; i ++ ) { + + var face = this.faces[ i ]; + + this.faceVertexUvs[ 0 ].push( [ + vertexUv( this.vertices[ face.a ] ), + vertexUv( this.vertices[ face.b ] ), + vertexUv( this.vertices[ face.c ] ) + ] ); + + } + + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.ConvexGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ConvexGeometry.prototype.constructor = THREE.ConvexGeometry; diff --git a/node_modules/three/examples/js/geometries/DecalGeometry.js b/node_modules/three/examples/js/geometries/DecalGeometry.js new file mode 100644 index 00000000..2de2541d --- /dev/null +++ b/node_modules/three/examples/js/geometries/DecalGeometry.js @@ -0,0 +1,289 @@ +THREE.DecalVertex = function( v, n ) { + + this.vertex = v; + this.normal = n; + +}; + +THREE.DecalVertex.prototype.clone = function() { + + return new THREE.DecalVertex( this.vertex.clone(), this.normal.clone() ); + +}; + +THREE.DecalGeometry = function( mesh, position, rotation, dimensions, check ) { + + THREE.Geometry.call( this ); + + if ( check === undefined ) check = null; + check = check || new THREE.Vector3( 1, 1, 1 ); + + this.uvs = []; + + this.cube = new THREE.Mesh( new THREE.BoxGeometry( dimensions.x, dimensions.y, dimensions.z ), new THREE.MeshBasicMaterial() ); + this.cube.rotation.set( rotation.x, rotation.y, rotation.z ); + this.cube.position.copy( position ); + this.cube.scale.set( 1, 1, 1 ); + this.cube.updateMatrix(); + + this.iCubeMatrix = ( new THREE.Matrix4() ).getInverse( this.cube.matrix ); + + this.faceIndices = [ 'a', 'b', 'c', 'd' ]; + + this.clipFace = function( inVertices, plane ) { + + var size = .5 * Math.abs( ( dimensions.clone() ).dot( plane ) ); + + function clip( v0, v1, p ) { + + var d0 = v0.vertex.dot( p ) - size, + d1 = v1.vertex.dot( p ) - size; + + var s = d0 / ( d0 - d1 ); + var v = new THREE.DecalVertex( + new THREE.Vector3( + v0.vertex.x + s * ( v1.vertex.x - v0.vertex.x ), + v0.vertex.y + s * ( v1.vertex.y - v0.vertex.y ), + v0.vertex.z + s * ( v1.vertex.z - v0.vertex.z ) + ), + new THREE.Vector3( + v0.normal.x + s * ( v1.normal.x - v0.normal.x ), + v0.normal.y + s * ( v1.normal.y - v0.normal.y ), + v0.normal.z + s * ( v1.normal.z - v0.normal.z ) + ) + ); + + // need to clip more values (texture coordinates)? do it this way: + //intersectpoint.value = a.value + s*(b.value-a.value); + + return v; + + } + + if ( inVertices.length === 0 ) return []; + var outVertices = []; + + for ( var j = 0; j < inVertices.length; j += 3 ) { + + var v1Out, v2Out, v3Out, total = 0; + + var d1 = inVertices[ j + 0 ].vertex.dot( plane ) - size, + d2 = inVertices[ j + 1 ].vertex.dot( plane ) - size, + d3 = inVertices[ j + 2 ].vertex.dot( plane ) - size; + + v1Out = d1 > 0; + v2Out = d2 > 0; + v3Out = d3 > 0; + + total = ( v1Out ? 1 : 0 ) + ( v2Out ? 1 : 0 ) + ( v3Out ? 1 : 0 ); + + switch ( total ) { + case 0: { + + outVertices.push( inVertices[ j ] ); + outVertices.push( inVertices[ j + 1 ] ); + outVertices.push( inVertices[ j + 2 ] ); + break; + + } + case 1: { + + var nV1, nV2, nV3; + if ( v1Out ) { + + nV1 = inVertices[ j + 1 ]; + nV2 = inVertices[ j + 2 ]; + nV3 = clip( inVertices[ j ], nV1, plane ); + nV4 = clip( inVertices[ j ], nV2, plane ); + + } + if ( v2Out ) { + + nV1 = inVertices[ j ]; + nV2 = inVertices[ j + 2 ]; + nV3 = clip( inVertices[ j + 1 ], nV1, plane ); + nV4 = clip( inVertices[ j + 1 ], nV2, plane ); + + outVertices.push( nV3 ); + outVertices.push( nV2.clone() ); + outVertices.push( nV1.clone() ); + + outVertices.push( nV2.clone() ); + outVertices.push( nV3.clone() ); + outVertices.push( nV4 ); + break; + + } + if ( v3Out ) { + + nV1 = inVertices[ j ]; + nV2 = inVertices[ j + 1 ]; + nV3 = clip( inVertices[ j + 2 ], nV1, plane ); + nV4 = clip( inVertices[ j + 2 ], nV2, plane ); + + } + + outVertices.push( nV1.clone() ); + outVertices.push( nV2.clone() ); + outVertices.push( nV3 ); + + outVertices.push( nV4 ); + outVertices.push( nV3.clone() ); + outVertices.push( nV2.clone() ); + + break; + + } + case 2: { + + var nV1, nV2, nV3; + if ( ! v1Out ) { + + nV1 = inVertices[ j ].clone(); + nV2 = clip( nV1, inVertices[ j + 1 ], plane ); + nV3 = clip( nV1, inVertices[ j + 2 ], plane ); + outVertices.push( nV1 ); + outVertices.push( nV2 ); + outVertices.push( nV3 ); + + } + if ( ! v2Out ) { + + nV1 = inVertices[ j + 1 ].clone(); + nV2 = clip( nV1, inVertices[ j + 2 ], plane ); + nV3 = clip( nV1, inVertices[ j ], plane ); + outVertices.push( nV1 ); + outVertices.push( nV2 ); + outVertices.push( nV3 ); + + } + if ( ! v3Out ) { + + nV1 = inVertices[ j + 2 ].clone(); + nV2 = clip( nV1, inVertices[ j ], plane ); + nV3 = clip( nV1, inVertices[ j + 1 ], plane ); + outVertices.push( nV1 ); + outVertices.push( nV2 ); + outVertices.push( nV3 ); + + } + + break; + + } + case 3: { + + break; + + } + } + + } + + return outVertices; + + }; + + this.pushVertex = function( vertices, id, n ) { + + var v = mesh.geometry.vertices[ id ].clone(); + v.applyMatrix4( mesh.matrix ); + v.applyMatrix4( this.iCubeMatrix ); + vertices.push( new THREE.DecalVertex( v, n.clone() ) ); + + }; + + this.computeDecal = function() { + + var finalVertices = []; + + for ( var i = 0; i < mesh.geometry.faces.length; i ++ ) { + + var f = mesh.geometry.faces[ i ]; + var vertices = []; + + this.pushVertex( vertices, f[ this.faceIndices[ 0 ] ], f.vertexNormals[ 0 ] ); + this.pushVertex( vertices, f[ this.faceIndices[ 1 ] ], f.vertexNormals[ 1 ] ); + this.pushVertex( vertices, f[ this.faceIndices[ 2 ] ], f.vertexNormals[ 2 ] ); + + if ( check.x ) { + + vertices = this.clipFace( vertices, new THREE.Vector3( 1, 0, 0 ) ); + vertices = this.clipFace( vertices, new THREE.Vector3( - 1, 0, 0 ) ); + + } + if ( check.y ) { + + vertices = this.clipFace( vertices, new THREE.Vector3( 0, 1, 0 ) ); + vertices = this.clipFace( vertices, new THREE.Vector3( 0, - 1, 0 ) ); + + } + if ( check.z ) { + + vertices = this.clipFace( vertices, new THREE.Vector3( 0, 0, 1 ) ); + vertices = this.clipFace( vertices, new THREE.Vector3( 0, 0, - 1 ) ); + + } + + for ( var j = 0; j < vertices.length; j ++ ) { + + var v = vertices[ j ]; + + this.uvs.push( new THREE.Vector2( + .5 + ( v.vertex.x / dimensions.x ), + .5 + ( v.vertex.y / dimensions.y ) + ) ); + + vertices[ j ].vertex.applyMatrix4( this.cube.matrix ); + + } + + if ( vertices.length === 0 ) continue; + + finalVertices = finalVertices.concat( vertices ); + + } + + for ( var k = 0; k < finalVertices.length; k += 3 ) { + + this.vertices.push( + finalVertices[ k ].vertex, + finalVertices[ k + 1 ].vertex, + finalVertices[ k + 2 ].vertex + ); + + var f = new THREE.Face3( + k, + k + 1, + k + 2 + ); + f.vertexNormals.push( finalVertices[ k + 0 ].normal ); + f.vertexNormals.push( finalVertices[ k + 1 ].normal ); + f.vertexNormals.push( finalVertices[ k + 2 ].normal ); + + this.faces.push( f ); + + this.faceVertexUvs[ 0 ].push( [ + this.uvs[ k ], + this.uvs[ k + 1 ], + this.uvs[ k + 2 ] + ] ); + + } + + this.verticesNeedUpdate = true; + this.elementsNeedUpdate = true; + this.morphTargetsNeedUpdate = true; + this.uvsNeedUpdate = true; + this.normalsNeedUpdate = true; + this.colorsNeedUpdate = true; + this.computeFaceNormals(); + + }; + + this.computeDecal(); + +}; + +THREE.DecalGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.DecalGeometry.prototype.constructor = THREE.DecalGeometry; diff --git a/node_modules/three/examples/js/geometries/TeapotBufferGeometry.js b/node_modules/three/examples/js/geometries/TeapotBufferGeometry.js new file mode 100644 index 00000000..66eec85c --- /dev/null +++ b/node_modules/three/examples/js/geometries/TeapotBufferGeometry.js @@ -0,0 +1,751 @@ +/** + * @author Eric Haines / http://erichaines.com/ + * + * Tessellates the famous Utah teapot database by Martin Newell into triangles. + * + * THREE.TeapotBufferGeometry = function ( size, segments, bottom, lid, body, fitLid, blinn ) + * + * defaults: size = 50, segments = 10, bottom = true, lid = true, body = true, + * fitLid = false, blinn = true + * + * size is a relative scale: I've scaled the teapot to fit vertically between -1 and 1. + * Think of it as a "radius". + * segments - number of line segments to subdivide each patch edge; + * 1 is possible but gives degenerates, so two is the real minimum. + * bottom - boolean, if true (default) then the bottom patches are added. Some consider + * adding the bottom heresy, so set this to "false" to adhere to the One True Way. + * lid - to remove the lid and look inside, set to true. + * body - to remove the body and leave the lid, set this and "bottom" to false. + * fitLid - the lid is a tad small in the original. This stretches it a bit so you can't + * see the teapot's insides through the gap. + * blinn - Jim Blinn scaled the original data vertically by dividing by about 1.3 to look + * nicer. If you want to see the original teapot, similar to the real-world model, set + * this to false. True by default. + * See http://en.wikipedia.org/wiki/File:Original_Utah_Teapot.jpg for the original + * real-world teapot (from http://en.wikipedia.org/wiki/Utah_teapot). + * + * Note that the bottom (the last four patches) is not flat - blame Frank Crow, not me. + * + * The teapot should normally be rendered as a double sided object, since for some + * patches both sides can be seen, e.g., the gap around the lid and inside the spout. + * + * Segments 'n' determines the number of triangles output. + * Total triangles = 32*2*n*n - 8*n [degenerates at the top and bottom cusps are deleted] + * + * size_factor # triangles + * 1 56 + * 2 240 + * 3 552 + * 4 992 + * + * 10 6320 + * 20 25440 + * 30 57360 + * + * Code converted from my ancient SPD software, http://tog.acm.org/resources/SPD/ + * Created for the Udacity course "Interactive Rendering", http://bit.ly/ericity + * Lesson: https://www.udacity.com/course/viewer#!/c-cs291/l-68866048/m-106482448 + * YouTube video on teapot history: https://www.youtube.com/watch?v=DxMfblPzFNc + * + * See https://en.wikipedia.org/wiki/Utah_teapot for the history of the teapot + * + */ +/*global THREE */ + +THREE.TeapotBufferGeometry = function ( size, segments, bottom, lid, body, fitLid, blinn ) { + + "use strict"; + + // 32 * 4 * 4 Bezier spline patches + var teapotPatches = [ +/*rim*/ +0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, +3,16,17,18,7,19,20,21,11,22,23,24,15,25,26,27, +18,28,29,30,21,31,32,33,24,34,35,36,27,37,38,39, +30,40,41,0,33,42,43,4,36,44,45,8,39,46,47,12, +/*body*/ +12,13,14,15,48,49,50,51,52,53,54,55,56,57,58,59, +15,25,26,27,51,60,61,62,55,63,64,65,59,66,67,68, +27,37,38,39,62,69,70,71,65,72,73,74,68,75,76,77, +39,46,47,12,71,78,79,48,74,80,81,52,77,82,83,56, +56,57,58,59,84,85,86,87,88,89,90,91,92,93,94,95, +59,66,67,68,87,96,97,98,91,99,100,101,95,102,103,104, +68,75,76,77,98,105,106,107,101,108,109,110,104,111,112,113, +77,82,83,56,107,114,115,84,110,116,117,88,113,118,119,92, +/*handle*/ +120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135, +123,136,137,120,127,138,139,124,131,140,141,128,135,142,143,132, +132,133,134,135,144,145,146,147,148,149,150,151,68,152,153,154, +135,142,143,132,147,155,156,144,151,157,158,148,154,159,160,68, +/*spout*/ +161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176, +164,177,178,161,168,179,180,165,172,181,182,169,176,183,184,173, +173,174,175,176,185,186,187,188,189,190,191,192,193,194,195,196, +176,183,184,173,188,197,198,185,192,199,200,189,196,201,202,193, +/*lid*/ +203,203,203,203,204,205,206,207,208,208,208,208,209,210,211,212, +203,203,203,203,207,213,214,215,208,208,208,208,212,216,217,218, +203,203,203,203,215,219,220,221,208,208,208,208,218,222,223,224, +203,203,203,203,221,225,226,204,208,208,208,208,224,227,228,209, +209,210,211,212,229,230,231,232,233,234,235,236,237,238,239,240, +212,216,217,218,232,241,242,243,236,244,245,246,240,247,248,249, +218,222,223,224,243,250,251,252,246,253,254,255,249,256,257,258, +224,227,228,209,252,259,260,229,255,261,262,233,258,263,264,237, +/*bottom*/ +265,265,265,265,266,267,268,269,270,271,272,273,92,119,118,113, +265,265,265,265,269,274,275,276,273,277,278,279,113,112,111,104, +265,265,265,265,276,280,281,282,279,283,284,285,104,103,102,95, +265,265,265,265,282,286,287,266,285,288,289,270,95,94,93,92 + ] ; + + var teapotVertices = [ +1.4,0,2.4, +1.4,-0.784,2.4, +0.784,-1.4,2.4, +0,-1.4,2.4, +1.3375,0,2.53125, +1.3375,-0.749,2.53125, +0.749,-1.3375,2.53125, +0,-1.3375,2.53125, +1.4375,0,2.53125, +1.4375,-0.805,2.53125, +0.805,-1.4375,2.53125, +0,-1.4375,2.53125, +1.5,0,2.4, +1.5,-0.84,2.4, +0.84,-1.5,2.4, +0,-1.5,2.4, +-0.784,-1.4,2.4, +-1.4,-0.784,2.4, +-1.4,0,2.4, +-0.749,-1.3375,2.53125, +-1.3375,-0.749,2.53125, +-1.3375,0,2.53125, +-0.805,-1.4375,2.53125, +-1.4375,-0.805,2.53125, +-1.4375,0,2.53125, +-0.84,-1.5,2.4, +-1.5,-0.84,2.4, +-1.5,0,2.4, +-1.4,0.784,2.4, +-0.784,1.4,2.4, +0,1.4,2.4, +-1.3375,0.749,2.53125, +-0.749,1.3375,2.53125, +0,1.3375,2.53125, +-1.4375,0.805,2.53125, +-0.805,1.4375,2.53125, +0,1.4375,2.53125, +-1.5,0.84,2.4, +-0.84,1.5,2.4, +0,1.5,2.4, +0.784,1.4,2.4, +1.4,0.784,2.4, +0.749,1.3375,2.53125, +1.3375,0.749,2.53125, +0.805,1.4375,2.53125, +1.4375,0.805,2.53125, +0.84,1.5,2.4, +1.5,0.84,2.4, +1.75,0,1.875, +1.75,-0.98,1.875, +0.98,-1.75,1.875, +0,-1.75,1.875, +2,0,1.35, +2,-1.12,1.35, +1.12,-2,1.35, +0,-2,1.35, +2,0,0.9, +2,-1.12,0.9, +1.12,-2,0.9, +0,-2,0.9, +-0.98,-1.75,1.875, +-1.75,-0.98,1.875, +-1.75,0,1.875, +-1.12,-2,1.35, +-2,-1.12,1.35, +-2,0,1.35, +-1.12,-2,0.9, +-2,-1.12,0.9, +-2,0,0.9, +-1.75,0.98,1.875, +-0.98,1.75,1.875, +0,1.75,1.875, +-2,1.12,1.35, +-1.12,2,1.35, +0,2,1.35, +-2,1.12,0.9, +-1.12,2,0.9, +0,2,0.9, +0.98,1.75,1.875, +1.75,0.98,1.875, +1.12,2,1.35, +2,1.12,1.35, +1.12,2,0.9, +2,1.12,0.9, +2,0,0.45, +2,-1.12,0.45, +1.12,-2,0.45, +0,-2,0.45, +1.5,0,0.225, +1.5,-0.84,0.225, +0.84,-1.5,0.225, +0,-1.5,0.225, +1.5,0,0.15, +1.5,-0.84,0.15, +0.84,-1.5,0.15, +0,-1.5,0.15, +-1.12,-2,0.45, +-2,-1.12,0.45, +-2,0,0.45, +-0.84,-1.5,0.225, +-1.5,-0.84,0.225, +-1.5,0,0.225, +-0.84,-1.5,0.15, +-1.5,-0.84,0.15, +-1.5,0,0.15, +-2,1.12,0.45, +-1.12,2,0.45, +0,2,0.45, +-1.5,0.84,0.225, +-0.84,1.5,0.225, +0,1.5,0.225, +-1.5,0.84,0.15, +-0.84,1.5,0.15, +0,1.5,0.15, +1.12,2,0.45, +2,1.12,0.45, +0.84,1.5,0.225, +1.5,0.84,0.225, +0.84,1.5,0.15, +1.5,0.84,0.15, +-1.6,0,2.025, +-1.6,-0.3,2.025, +-1.5,-0.3,2.25, +-1.5,0,2.25, +-2.3,0,2.025, +-2.3,-0.3,2.025, +-2.5,-0.3,2.25, +-2.5,0,2.25, +-2.7,0,2.025, +-2.7,-0.3,2.025, +-3,-0.3,2.25, +-3,0,2.25, +-2.7,0,1.8, +-2.7,-0.3,1.8, +-3,-0.3,1.8, +-3,0,1.8, +-1.5,0.3,2.25, +-1.6,0.3,2.025, +-2.5,0.3,2.25, +-2.3,0.3,2.025, +-3,0.3,2.25, +-2.7,0.3,2.025, +-3,0.3,1.8, +-2.7,0.3,1.8, +-2.7,0,1.575, +-2.7,-0.3,1.575, +-3,-0.3,1.35, +-3,0,1.35, +-2.5,0,1.125, +-2.5,-0.3,1.125, +-2.65,-0.3,0.9375, +-2.65,0,0.9375, +-2,-0.3,0.9, +-1.9,-0.3,0.6, +-1.9,0,0.6, +-3,0.3,1.35, +-2.7,0.3,1.575, +-2.65,0.3,0.9375, +-2.5,0.3,1.125, +-1.9,0.3,0.6, +-2,0.3,0.9, +1.7,0,1.425, +1.7,-0.66,1.425, +1.7,-0.66,0.6, +1.7,0,0.6, +2.6,0,1.425, +2.6,-0.66,1.425, +3.1,-0.66,0.825, +3.1,0,0.825, +2.3,0,2.1, +2.3,-0.25,2.1, +2.4,-0.25,2.025, +2.4,0,2.025, +2.7,0,2.4, +2.7,-0.25,2.4, +3.3,-0.25,2.4, +3.3,0,2.4, +1.7,0.66,0.6, +1.7,0.66,1.425, +3.1,0.66,0.825, +2.6,0.66,1.425, +2.4,0.25,2.025, +2.3,0.25,2.1, +3.3,0.25,2.4, +2.7,0.25,2.4, +2.8,0,2.475, +2.8,-0.25,2.475, +3.525,-0.25,2.49375, +3.525,0,2.49375, +2.9,0,2.475, +2.9,-0.15,2.475, +3.45,-0.15,2.5125, +3.45,0,2.5125, +2.8,0,2.4, +2.8,-0.15,2.4, +3.2,-0.15,2.4, +3.2,0,2.4, +3.525,0.25,2.49375, +2.8,0.25,2.475, +3.45,0.15,2.5125, +2.9,0.15,2.475, +3.2,0.15,2.4, +2.8,0.15,2.4, +0,0,3.15, +0.8,0,3.15, +0.8,-0.45,3.15, +0.45,-0.8,3.15, +0,-0.8,3.15, +0,0,2.85, +0.2,0,2.7, +0.2,-0.112,2.7, +0.112,-0.2,2.7, +0,-0.2,2.7, +-0.45,-0.8,3.15, +-0.8,-0.45,3.15, +-0.8,0,3.15, +-0.112,-0.2,2.7, +-0.2,-0.112,2.7, +-0.2,0,2.7, +-0.8,0.45,3.15, +-0.45,0.8,3.15, +0,0.8,3.15, +-0.2,0.112,2.7, +-0.112,0.2,2.7, +0,0.2,2.7, +0.45,0.8,3.15, +0.8,0.45,3.15, +0.112,0.2,2.7, +0.2,0.112,2.7, +0.4,0,2.55, +0.4,-0.224,2.55, +0.224,-0.4,2.55, +0,-0.4,2.55, +1.3,0,2.55, +1.3,-0.728,2.55, +0.728,-1.3,2.55, +0,-1.3,2.55, +1.3,0,2.4, +1.3,-0.728,2.4, +0.728,-1.3,2.4, +0,-1.3,2.4, +-0.224,-0.4,2.55, +-0.4,-0.224,2.55, +-0.4,0,2.55, +-0.728,-1.3,2.55, +-1.3,-0.728,2.55, +-1.3,0,2.55, +-0.728,-1.3,2.4, +-1.3,-0.728,2.4, +-1.3,0,2.4, +-0.4,0.224,2.55, +-0.224,0.4,2.55, +0,0.4,2.55, +-1.3,0.728,2.55, +-0.728,1.3,2.55, +0,1.3,2.55, +-1.3,0.728,2.4, +-0.728,1.3,2.4, +0,1.3,2.4, +0.224,0.4,2.55, +0.4,0.224,2.55, +0.728,1.3,2.55, +1.3,0.728,2.55, +0.728,1.3,2.4, +1.3,0.728,2.4, +0,0,0, +1.425,0,0, +1.425,0.798,0, +0.798,1.425,0, +0,1.425,0, +1.5,0,0.075, +1.5,0.84,0.075, +0.84,1.5,0.075, +0,1.5,0.075, +-0.798,1.425,0, +-1.425,0.798,0, +-1.425,0,0, +-0.84,1.5,0.075, +-1.5,0.84,0.075, +-1.5,0,0.075, +-1.425,-0.798,0, +-0.798,-1.425,0, +0,-1.425,0, +-1.5,-0.84,0.075, +-0.84,-1.5,0.075, +0,-1.5,0.075, +0.798,-1.425,0, +1.425,-0.798,0, +0.84,-1.5,0.075, +1.5,-0.84,0.075 + ] ; + + THREE.BufferGeometry.call( this ); + + this.type = 'TeapotBufferGeometry'; + + this.parameters = { + size: size, + segments: segments, + bottom: bottom, + lid: lid, + body: body, + fitLid: fitLid, + blinn: blinn + }; + + size = size || 50; + + // number of segments per patch + segments = segments !== undefined ? Math.max( 2, Math.floor( segments ) || 10 ) : 10; + + // which parts should be visible + bottom = bottom === undefined ? true : bottom; + lid = lid === undefined ? true : lid; + body = body === undefined ? true : body; + + // Should the lid be snug? It's not traditional, so off by default + fitLid = fitLid === undefined ? false : fitLid; + + // Jim Blinn scaled the teapot down in size by about 1.3 for + // some rendering tests. He liked the new proportions that he kept + // the data in this form. The model was distributed with these new + // proportions and became the norm. Trivia: comparing images of the + // real teapot and the computer model, the ratio for the bowl of the + // real teapot is more like 1.25, but since 1.3 is the traditional + // value given, we use it here. + var blinnScale = 1.3; + blinn = blinn === undefined ? true : blinn; + + // scale the size to be the real scaling factor + var maxHeight = 3.15 * ( blinn ? 1 : blinnScale ); + + var maxHeight2 = maxHeight / 2; + var trueSize = size / maxHeight2; + + // Number of elements depends on what is needed. Subtract degenerate + // triangles at tip of bottom and lid out in advance. + var numTriangles = bottom ? ( 8 * segments - 4 ) * segments : 0; + numTriangles += lid ? ( 16 * segments - 4 ) * segments : 0; + numTriangles += body ? 40 * segments * segments : 0; + + var indices = new Uint32Array( numTriangles * 3 ); + + var numVertices = bottom ? 4 : 0; + numVertices += lid ? 8 : 0; + numVertices += body ? 20 : 0; + numVertices *= ( segments + 1 ) * ( segments + 1 ); + + var vertices = new Float32Array( numVertices * 3 ); + var normals = new Float32Array( numVertices * 3 ); + var uvs = new Float32Array( numVertices * 2 ); + + // Bezier form + var ms = new THREE.Matrix4(); + ms.set( -1.0, 3.0, -3.0, 1.0, + 3.0, -6.0, 3.0, 0.0, + -3.0, 3.0, 0.0, 0.0, + 1.0, 0.0, 0.0, 0.0 ) ; + + var g = []; + var i, r, c; + + var sp = []; + var tp = []; + var dsp = []; + var dtp = []; + + // M * G * M matrix, sort of see + // http://www.cs.helsinki.fi/group/goa/mallinnus/curves/surfaces.html + var mgm = []; + + var vert = []; + var sdir = []; + var tdir = []; + + var norm = new THREE.Vector3(); + + var tcoord; + + var sstep, tstep; + var vertPerRow, eps; + + var s, t, sval, tval, p, dsval, dtval; + + var normOut = new THREE.Vector3(); + var v1, v2, v3, v4; + + var gmx = new THREE.Matrix4(); + var tmtx = new THREE.Matrix4(); + + var vsp = new THREE.Vector4(); + var vtp = new THREE.Vector4(); + var vdsp = new THREE.Vector4(); + var vdtp = new THREE.Vector4(); + + var vsdir = new THREE.Vector3(); + var vtdir = new THREE.Vector3(); + + var mst = ms.clone(); + mst.transpose(); + + // internal function: test if triangle has any matching vertices; + // if so, don't save triangle, since it won't display anything. + var notDegenerate = function ( vtx1, vtx2, vtx3 ) { + + // if any vertex matches, return false + return ! ( ( ( vertices[ vtx1 * 3 ] === vertices[ vtx2 * 3 ] ) && + ( vertices[ vtx1 * 3 + 1 ] === vertices[ vtx2 * 3 + 1 ] ) && + ( vertices[ vtx1 * 3 + 2 ] === vertices[ vtx2 * 3 + 2 ] ) ) || + ( ( vertices[ vtx1 * 3 ] === vertices[ vtx3 * 3 ] ) && + ( vertices[ vtx1 * 3 + 1 ] === vertices[ vtx3 * 3 + 1 ] ) && + ( vertices[ vtx1 * 3 + 2 ] === vertices[ vtx3 * 3 + 2 ] ) ) || + ( ( vertices[ vtx2 * 3 ] === vertices[ vtx3 * 3 ] ) && + ( vertices[ vtx2 * 3 + 1 ] === vertices[ vtx3 * 3 + 1 ] ) && + ( vertices[ vtx2 * 3 + 2 ] === vertices[ vtx3 * 3 + 2 ] ) ) ); + + }; + + + for ( i = 0; i < 3; i ++ ) + { + + mgm[ i ] = new THREE.Matrix4(); + + } + + var minPatches = body ? 0 : 20; + var maxPatches = bottom ? 32 : 28; + + vertPerRow = segments + 1; + + eps = 0.0000001; + + var surfCount = 0; + + var vertCount = 0; + var normCount = 0; + var uvCount = 0; + + var indexCount = 0; + + for ( var surf = minPatches ; surf < maxPatches ; surf ++ ) { + + // lid is in the middle of the data, patches 20-27, + // so ignore it for this part of the loop if the lid is not desired + if ( lid || ( surf < 20 || surf >= 28 ) ) { + + // get M * G * M matrix for x,y,z + for ( i = 0 ; i < 3 ; i ++ ) { + + // get control patches + for ( r = 0 ; r < 4 ; r ++ ) { + + for ( c = 0 ; c < 4 ; c ++ ) { + + // transposed + g[ c * 4 + r ] = teapotVertices[ teapotPatches[ surf * 16 + r * 4 + c ] * 3 + i ] ; + + // is the lid to be made larger, and is this a point on the lid + // that is X or Y? + if ( fitLid && ( surf >= 20 && surf < 28 ) && ( i !== 2 ) ) { + + // increase XY size by 7.7%, found empirically. I don't + // increase Z so that the teapot will continue to fit in the + // space -1 to 1 for Y (Y is up for the final model). + g[ c * 4 + r ] *= 1.077; + + } + + // Blinn "fixed" the teapot by dividing Z by blinnScale, and that's the + // data we now use. The original teapot is taller. Fix it: + if ( ! blinn && ( i === 2 ) ) { + + g[ c * 4 + r ] *= blinnScale; + + } + + } + + } + + gmx.set( g[ 0 ], g[ 1 ], g[ 2 ], g[ 3 ], g[ 4 ], g[ 5 ], g[ 6 ], g[ 7 ], g[ 8 ], g[ 9 ], g[ 10 ], g[ 11 ], g[ 12 ], g[ 13 ], g[ 14 ], g[ 15 ] ); + + tmtx.multiplyMatrices( gmx, ms ); + mgm[ i ].multiplyMatrices( mst, tmtx ); + + } + + // step along, get points, and output + for ( sstep = 0 ; sstep <= segments ; sstep ++ ) { + + s = sstep / segments; + + for ( tstep = 0 ; tstep <= segments ; tstep ++ ) { + + t = tstep / segments; + + // point from basis + // get power vectors and their derivatives + for ( p = 4, sval = tval = 1.0 ; p -- ; ) { + + sp[ p ] = sval ; + tp[ p ] = tval ; + sval *= s ; + tval *= t ; + + if ( p === 3 ) { + + dsp[ p ] = dtp[ p ] = 0.0 ; + dsval = dtval = 1.0 ; + + } else { + + dsp[ p ] = dsval * ( 3 - p ) ; + dtp[ p ] = dtval * ( 3 - p ) ; + dsval *= s ; + dtval *= t ; + + } + + } + + vsp.fromArray( sp ); + vtp.fromArray( tp ); + vdsp.fromArray( dsp ); + vdtp.fromArray( dtp ); + + // do for x,y,z + for ( i = 0 ; i < 3 ; i ++ ) { + + // multiply power vectors times matrix to get value + tcoord = vsp.clone(); + tcoord.applyMatrix4( mgm[ i ] ); + vert[ i ] = tcoord.dot( vtp ); + + // get s and t tangent vectors + tcoord = vdsp.clone(); + tcoord.applyMatrix4( mgm[ i ] ); + sdir[ i ] = tcoord.dot( vtp ) ; + + tcoord = vsp.clone(); + tcoord.applyMatrix4( mgm[ i ] ); + tdir[ i ] = tcoord.dot( vdtp ) ; + + } + + // find normal + vsdir.fromArray( sdir ); + vtdir.fromArray( tdir ); + norm.crossVectors( vtdir, vsdir ); + norm.normalize(); + + // if X and Z length is 0, at the cusp, so point the normal up or down, depending on patch number + if ( vert[ 0 ] === 0 && vert[ 1 ] === 0 ) + { + + // if above the middle of the teapot, normal points up, else down + normOut.set( 0, vert[ 2 ] > maxHeight2 ? 1 : - 1, 0 ); + + } + else + { + + // standard output: rotate on X axis + normOut.set( norm.x, norm.z, - norm.y ); + + } + + // store it all + vertices[ vertCount ++ ] = trueSize * vert[ 0 ]; + vertices[ vertCount ++ ] = trueSize * ( vert[ 2 ] - maxHeight2 ); + vertices[ vertCount ++ ] = - trueSize * vert[ 1 ]; + + normals[ normCount ++ ] = normOut.x; + normals[ normCount ++ ] = normOut.y; + normals[ normCount ++ ] = normOut.z; + + uvs[ uvCount ++ ] = 1 - t; + uvs[ uvCount ++ ] = 1 - s; + + } + + } + + // save the faces + for ( sstep = 0 ; sstep < segments ; sstep ++ ) { + + for ( tstep = 0 ; tstep < segments ; tstep ++ ) { + + v1 = surfCount * vertPerRow * vertPerRow + sstep * vertPerRow + tstep; + v2 = v1 + 1; + v3 = v2 + vertPerRow; + v4 = v1 + vertPerRow; + + // Normals and UVs cannot be shared. Without clone(), you can see the consequences + // of sharing if you call geometry.applyMatrix( matrix ). + if ( notDegenerate ( v1, v2, v3 ) ) { + + indices[ indexCount ++ ] = v1; + indices[ indexCount ++ ] = v2; + indices[ indexCount ++ ] = v3; + + } + if ( notDegenerate ( v1, v3, v4 ) ) { + + indices[ indexCount ++ ] = v1; + indices[ indexCount ++ ] = v3; + indices[ indexCount ++ ] = v4; + + } + + } + + } + + // increment only if a surface was used + surfCount ++; + + } + + } + + this.setIndex( new THREE.BufferAttribute( indices, 1 ) ); + this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + + this.computeBoundingSphere(); + +}; + + +THREE.TeapotBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.TeapotBufferGeometry.prototype.constructor = THREE.TeapotBufferGeometry; + +THREE.TeapotBufferGeometry.prototype.clone = function () { + + var bufferGeometry = new THREE.TeapotBufferGeometry( + this.parameters.size, + this.parameters.segments, + this.parameters.bottom, + this.parameters.lid, + this.parameters.body, + this.parameters.fitLid, + this.parameters.blinn + ); + + return bufferGeometry; + +}; diff --git a/node_modules/three/examples/js/geometries/TextGeometry.js b/node_modules/three/examples/js/geometries/TextGeometry.js new file mode 100644 index 00000000..bf331297 --- /dev/null +++ b/node_modules/three/examples/js/geometries/TextGeometry.js @@ -0,0 +1,62 @@ +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author alteredq / http://alteredqualia.com/ + * + * For creating 3D text geometry in three.js + * + * Text = 3D Text + * + * parameters = { + * size: , // size of the text + * height: , // thickness to extrude text + * curveSegments: , // number of points on the curves + * + * font: , // font name + * weight: , // font weight (normal, bold) + * style: , // font style (normal, italics) + * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into text bevel goes + * bevelSize: , // how far from text outline is bevel + * } + * + */ + +/* Usage Examples + + // TextGeometry wrapper + + var text3d = new TextGeometry( text, options ); + + // Complete manner + + var textShapes = THREE.FontUtils.generateShapes( text, options ); + var text3d = new ExtrudeGeometry( textShapes, options ); + +*/ + + +THREE.TextGeometry = function ( text, parameters ) { + + parameters = parameters || {}; + + var textShapes = THREE.FontUtils.generateShapes( text, parameters ); + + // translate parameters to ExtrudeGeometry API + + parameters.amount = parameters.height !== undefined ? parameters.height : 50; + + // defaults + + if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; + if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; + if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; + + THREE.ExtrudeGeometry.call( this, textShapes, parameters ); + + this.type = 'TextGeometry'; + +}; + +THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype ); +THREE.TextGeometry.prototype.constructor = THREE.TextGeometry; diff --git a/node_modules/three/examples/js/geometries/hilbert2D.js b/node_modules/three/examples/js/geometries/hilbert2D.js new file mode 100644 index 00000000..c94cee17 --- /dev/null +++ b/node_modules/three/examples/js/geometries/hilbert2D.js @@ -0,0 +1,63 @@ +/** + * Hilbert Curve: Generates 2D-Coordinates in a very fast way. + * + * @author Dylan Grafmyre + * + * Based on work by: + * @author Thomas Diewald + * @link http://www.openprocessing.org/sketch/15493 + * + * @param center Center of Hilbert curve. + * @param size Total width of Hilbert curve. + * @param iterations Number of subdivisions. + * @param v0 Corner index -X, -Z. + * @param v1 Corner index -X, +Z. + * @param v2 Corner index +X, +Z. + * @param v3 Corner index +X, -Z. + */ +function hilbert2D ( center, size, iterations, v0, v1, v2, v3 ) { + + // Default Vars + var center = undefined !== center ? center : new THREE.Vector3( 0, 0, 0 ), + size = undefined !== size ? size : 10, + half = size / 2, + iterations = undefined !== iterations ? iterations : 1, + v0 = undefined !== v0 ? v0 : 0, + v1 = undefined !== v1 ? v1 : 1, + v2 = undefined !== v2 ? v2 : 2, + v3 = undefined !== v3 ? v3 : 3 + ; + + var vec_s = [ + new THREE.Vector3( center.x - half, center.y, center.z - half ), + new THREE.Vector3( center.x - half, center.y, center.z + half ), + new THREE.Vector3( center.x + half, center.y, center.z + half ), + new THREE.Vector3( center.x + half, center.y, center.z - half ) + ]; + + var vec = [ + vec_s[ v0 ], + vec_s[ v1 ], + vec_s[ v2 ], + vec_s[ v3 ] + ]; + + // Recurse iterations + if ( 0 <= -- iterations ) { + + var tmp = []; + + Array.prototype.push.apply( tmp, hilbert2D ( vec[ 0 ], half, iterations, v0, v3, v2, v1 ) ); + Array.prototype.push.apply( tmp, hilbert2D ( vec[ 1 ], half, iterations, v0, v1, v2, v3 ) ); + Array.prototype.push.apply( tmp, hilbert2D ( vec[ 2 ], half, iterations, v0, v1, v2, v3 ) ); + Array.prototype.push.apply( tmp, hilbert2D ( vec[ 3 ], half, iterations, v2, v1, v0, v3 ) ); + + // Return recursive call + return tmp; + + } + + // Return complete Hilbert Curve. + return vec; + +} diff --git a/node_modules/three/examples/js/geometries/hilbert3D.js b/node_modules/three/examples/js/geometries/hilbert3D.js new file mode 100644 index 00000000..03d2af88 --- /dev/null +++ b/node_modules/three/examples/js/geometries/hilbert3D.js @@ -0,0 +1,88 @@ +/** + * Hilbert Curve: Generates 2D-Coordinates in a very fast way. + * + * @author Dylan Grafmyre + * + * Based on work by: + * @author Thomas Diewald + * @link http://www.openprocessing.org/visuals/?visualID=15599 + * + * Based on `examples/canvas_lines_colors.html`: + * @author OpenShift guest + * @link https://github.com/mrdoob/three.js/blob/8413a860aa95ed29c79cbb7f857c97d7880d260f/examples/canvas_lines_colors.html + * @see Line 149 - 186 + * + * @param center Center of Hilbert curve. + * @param size Total width of Hilbert curve. + * @param iterations Number of subdivisions. + * @param v0 Corner index -X, +Y, -Z. + * @param v1 Corner index -X, +Y, +Z. + * @param v2 Corner index -X, -Y, +Z. + * @param v3 Corner index -X, -Y, -Z. + * @param v4 Corner index +X, -Y, -Z. + * @param v5 Corner index +X, -Y, +Z. + * @param v6 Corner index +X, +Y, +Z. + * @param v7 Corner index +X, +Y, -Z. + */ +function hilbert3D( center, size, iterations, v0, v1, v2, v3, v4, v5, v6, v7 ) { + + // Default Vars + var center = undefined !== center ? center : new THREE.Vector3( 0, 0, 0 ), + size = undefined !== size ? size : 10, + half = size / 2, + iterations = undefined !== iterations ? iterations : 1, + v0 = undefined !== v0 ? v0 : 0, + v1 = undefined !== v1 ? v1 : 1, + v2 = undefined !== v2 ? v2 : 2, + v3 = undefined !== v3 ? v3 : 3, + v4 = undefined !== v4 ? v4 : 4, + v5 = undefined !== v5 ? v5 : 5, + v6 = undefined !== v6 ? v6 : 6, + v7 = undefined !== v7 ? v7 : 7 + ; + + var vec_s = [ + new THREE.Vector3( center.x - half, center.y + half, center.z - half ), + new THREE.Vector3( center.x - half, center.y + half, center.z + half ), + new THREE.Vector3( center.x - half, center.y - half, center.z + half ), + new THREE.Vector3( center.x - half, center.y - half, center.z - half ), + new THREE.Vector3( center.x + half, center.y - half, center.z - half ), + new THREE.Vector3( center.x + half, center.y - half, center.z + half ), + new THREE.Vector3( center.x + half, center.y + half, center.z + half ), + new THREE.Vector3( center.x + half, center.y + half, center.z - half ) + ]; + + var vec = [ + vec_s[ v0 ], + vec_s[ v1 ], + vec_s[ v2 ], + vec_s[ v3 ], + vec_s[ v4 ], + vec_s[ v5 ], + vec_s[ v6 ], + vec_s[ v7 ] + ]; + + // Recurse iterations + if ( -- iterations >= 0 ) { + + var tmp = []; + + Array.prototype.push.apply( tmp, hilbert3D ( vec[ 0 ], half, iterations, v0, v3, v4, v7, v6, v5, v2, v1 ) ); + Array.prototype.push.apply( tmp, hilbert3D ( vec[ 1 ], half, iterations, v0, v7, v6, v1, v2, v5, v4, v3 ) ); + Array.prototype.push.apply( tmp, hilbert3D ( vec[ 2 ], half, iterations, v0, v7, v6, v1, v2, v5, v4, v3 ) ); + Array.prototype.push.apply( tmp, hilbert3D ( vec[ 3 ], half, iterations, v2, v3, v0, v1, v6, v7, v4, v5 ) ); + Array.prototype.push.apply( tmp, hilbert3D ( vec[ 4 ], half, iterations, v2, v3, v0, v1, v6, v7, v4, v5 ) ); + Array.prototype.push.apply( tmp, hilbert3D ( vec[ 5 ], half, iterations, v4, v3, v2, v5, v6, v1, v0, v7 ) ); + Array.prototype.push.apply( tmp, hilbert3D ( vec[ 6 ], half, iterations, v4, v3, v2, v5, v6, v1, v0, v7 ) ); + Array.prototype.push.apply( tmp, hilbert3D ( vec[ 7 ], half, iterations, v6, v5, v2, v1, v0, v3, v4, v7 ) ); + + // Return recursive call + return tmp; + + } + + // Return complete Hilbert Curve. + return vec; + +} diff --git a/node_modules/three/examples/js/libs/dat.gui.min.js b/node_modules/three/examples/js/libs/dat.gui.min.js new file mode 100644 index 00000000..3fb091ed --- /dev/null +++ b/node_modules/three/examples/js/libs/dat.gui.min.js @@ -0,0 +1,94 @@ +/** + * dat-gui JavaScript Controller Library + * http://code.google.com/p/dat-gui + * + * Copyright 2011 Data Arts Team, Google Creative Lab + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +var dat=dat||{};dat.gui=dat.gui||{};dat.utils=dat.utils||{};dat.controllers=dat.controllers||{};dat.dom=dat.dom||{};dat.color=dat.color||{};dat.utils.css=function(){return{load:function(e,a){var a=a||document,c=a.createElement("link");c.type="text/css";c.rel="stylesheet";c.href=e;a.getElementsByTagName("head")[0].appendChild(c)},inject:function(e,a){var a=a||document,c=document.createElement("style");c.type="text/css";c.innerHTML=e;a.getElementsByTagName("head")[0].appendChild(c)}}}(); +dat.utils.common=function(){var e=Array.prototype.forEach,a=Array.prototype.slice;return{BREAK:{},extend:function(c){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(a[f])||(c[f]=a[f])},this);return c},defaults:function(c){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(c[f])&&(c[f]=a[f])},this);return c},compose:function(){var c=a.call(arguments);return function(){for(var d=a.call(arguments),f=c.length-1;f>=0;f--)d=[c[f].apply(this,d)];return d[0]}}, +each:function(a,d,f){if(e&&a.forEach===e)a.forEach(d,f);else if(a.length===a.length+0)for(var b=0,n=a.length;b-1?d.length-d.indexOf(".")-1:0};c.superclass=e;a.extend(c.prototype,e.prototype,{setValue:function(a){if(this.__min!==void 0&&athis.__max)a=this.__max;this.__step!==void 0&&a%this.__step!=0&&(a=Math.round(a/this.__step)*this.__step);return c.superclass.prototype.setValue.call(this,a)},min:function(a){this.__min=a;return this},max:function(a){this.__max=a;return this},step:function(a){this.__step=a;return this}});return c}(dat.controllers.Controller,dat.utils.common); +dat.controllers.NumberControllerBox=function(e,a,c){var d=function(f,b,e){function h(){var a=parseFloat(l.__input.value);c.isNaN(a)||l.setValue(a)}function j(a){var b=o-a.clientY;l.setValue(l.getValue()+b*l.__impliedStep);o=a.clientY}function m(){a.unbind(window,"mousemove",j);a.unbind(window,"mouseup",m)}this.__truncationSuspended=false;d.superclass.call(this,f,b,e);var l=this,o;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"change",h); +a.bind(this.__input,"blur",function(){h();l.__onFinishChange&&l.__onFinishChange.call(l,l.getValue())});a.bind(this.__input,"mousedown",function(b){a.bind(window,"mousemove",j);a.bind(window,"mouseup",m);o=b.clientY});a.bind(this.__input,"keydown",function(a){if(a.keyCode===13)l.__truncationSuspended=true,this.blur(),l.__truncationSuspended=false});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;c.extend(d.prototype,e.prototype,{updateDisplay:function(){var a=this.__input, +b;if(this.__truncationSuspended)b=this.getValue();else{b=this.getValue();var c=Math.pow(10,this.__precision);b=Math.round(b*c)/c}a.value=b;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.NumberController,dat.dom.dom,dat.utils.common); +dat.controllers.NumberControllerSlider=function(e,a,c,d,f){var b=function(d,c,f,e,l){function o(b){b.preventDefault();var d=a.getOffset(g.__background),c=a.getWidth(g.__background);g.setValue(g.__min+(g.__max-g.__min)*((b.clientX-d.left)/(d.left+c-d.left)));return false}function y(){a.unbind(window,"mousemove",o);a.unbind(window,"mouseup",y);g.__onFinishChange&&g.__onFinishChange.call(g,g.getValue())}b.superclass.call(this,d,c,{min:f,max:e,step:l});var g=this;this.__background=document.createElement("div"); +this.__foreground=document.createElement("div");a.bind(this.__background,"mousedown",function(b){a.bind(window,"mousemove",o);a.bind(window,"mouseup",y);o(b)});a.addClass(this.__background,"slider");a.addClass(this.__foreground,"slider-fg");this.updateDisplay();this.__background.appendChild(this.__foreground);this.domElement.appendChild(this.__background)};b.superclass=e;b.useDefaultStyles=function(){c.inject(f)};d.extend(b.prototype,e.prototype,{updateDisplay:function(){this.__foreground.style.width= +(this.getValue()-this.__min)/(this.__max-this.__min)*100+"%";return b.superclass.prototype.updateDisplay.call(this)}});return b}(dat.controllers.NumberController,dat.dom.dom,dat.utils.css,dat.utils.common,".slider {\n box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);\n height: 1em;\n border-radius: 1em;\n background-color: #eee;\n padding: 0 0.5em;\n overflow: hidden;\n}\n\n.slider-fg {\n padding: 1px 0 2px 0;\n background-color: #aaa;\n height: 1em;\n margin-left: -0.5em;\n padding-right: 0.5em;\n border-radius: 1em 0 0 1em;\n}\n\n.slider-fg:after {\n display: inline-block;\n border-radius: 1em;\n background-color: #fff;\n border: 1px solid #aaa;\n content: '';\n float: right;\n margin-right: -1em;\n margin-top: -1px;\n height: 0.9em;\n width: 0.9em;\n}"); +dat.controllers.FunctionController=function(e,a,c){var d=function(c,b,e){d.superclass.call(this,c,b);var h=this;this.__button=document.createElement("div");this.__button.innerHTML=e===void 0?"Fire":e;a.bind(this.__button,"click",function(a){a.preventDefault();h.fire();return false});a.addClass(this.__button,"button");this.domElement.appendChild(this.__button)};d.superclass=e;c.extend(d.prototype,e.prototype,{fire:function(){this.__onChange&&this.__onChange.call(this);this.__onFinishChange&&this.__onFinishChange.call(this, +this.getValue());this.getValue().call(this.object)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); +dat.controllers.BooleanController=function(e,a,c){var d=function(c,b){d.superclass.call(this,c,b);var e=this;this.__prev=this.getValue();this.__checkbox=document.createElement("input");this.__checkbox.setAttribute("type","checkbox");a.bind(this.__checkbox,"change",function(){e.setValue(!e.__prev)},false);this.domElement.appendChild(this.__checkbox);this.updateDisplay()};d.superclass=e;c.extend(d.prototype,e.prototype,{setValue:function(a){a=d.superclass.prototype.setValue.call(this,a);this.__onFinishChange&& +this.__onFinishChange.call(this,this.getValue());this.__prev=this.getValue();return a},updateDisplay:function(){this.getValue()===true?(this.__checkbox.setAttribute("checked","checked"),this.__checkbox.checked=true):this.__checkbox.checked=false;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); +dat.color.toString=function(e){return function(a){if(a.a==1||e.isUndefined(a.a)){for(a=a.hex.toString(16);a.length<6;)a="0"+a;return"#"+a}else return"rgba("+Math.round(a.r)+","+Math.round(a.g)+","+Math.round(a.b)+","+a.a+")"}}(dat.utils.common); +dat.color.interpret=function(e,a){var c,d,f=[{litmus:a.isString,conversions:{THREE_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);return a===null?false:{space:"HEX",hex:parseInt("0x"+a[1].toString()+a[1].toString()+a[2].toString()+a[2].toString()+a[3].toString()+a[3].toString())}},write:e},SIX_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9]{6})$/i);return a===null?false:{space:"HEX",hex:parseInt("0x"+a[1].toString())}},write:e},CSS_RGB:{read:function(a){a=a.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/); +return a===null?false:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3])}},write:e},CSS_RGBA:{read:function(a){a=a.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);return a===null?false:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3]),a:parseFloat(a[4])}},write:e}}},{litmus:a.isNumber,conversions:{HEX:{read:function(a){return{space:"HEX",hex:a,conversionName:"HEX"}},write:function(a){return a.hex}}}},{litmus:a.isArray,conversions:{RGB_ARRAY:{read:function(a){return a.length!= +3?false:{space:"RGB",r:a[0],g:a[1],b:a[2]}},write:function(a){return[a.r,a.g,a.b]}},RGBA_ARRAY:{read:function(a){return a.length!=4?false:{space:"RGB",r:a[0],g:a[1],b:a[2],a:a[3]}},write:function(a){return[a.r,a.g,a.b,a.a]}}}},{litmus:a.isObject,conversions:{RGBA_OBJ:{read:function(b){return a.isNumber(b.r)&&a.isNumber(b.g)&&a.isNumber(b.b)&&a.isNumber(b.a)?{space:"RGB",r:b.r,g:b.g,b:b.b,a:b.a}:false},write:function(a){return{r:a.r,g:a.g,b:a.b,a:a.a}}},RGB_OBJ:{read:function(b){return a.isNumber(b.r)&& +a.isNumber(b.g)&&a.isNumber(b.b)?{space:"RGB",r:b.r,g:b.g,b:b.b}:false},write:function(a){return{r:a.r,g:a.g,b:a.b}}},HSVA_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)&&a.isNumber(b.a)?{space:"HSV",h:b.h,s:b.s,v:b.v,a:b.a}:false},write:function(a){return{h:a.h,s:a.s,v:a.v,a:a.a}}},HSV_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)?{space:"HSV",h:b.h,s:b.s,v:b.v}:false},write:function(a){return{h:a.h,s:a.s,v:a.v}}}}}];return function(){d= +false;var b=arguments.length>1?a.toArray(arguments):arguments[0];a.each(f,function(e){if(e.litmus(b))return a.each(e.conversions,function(e,f){c=e.read(b);if(d===false&&c!==false)return d=c,c.conversionName=f,c.conversion=e,a.BREAK}),a.BREAK});return d}}(dat.color.toString,dat.utils.common); +dat.GUI=dat.gui.GUI=function(e,a,c,d,f,b,n,h,j,m,l,o,y,g,i){function q(a,b,r,c){if(b[r]===void 0)throw Error("Object "+b+' has no property "'+r+'"');c.color?b=new l(b,r):(b=[b,r].concat(c.factoryArgs),b=d.apply(a,b));if(c.before instanceof f)c.before=c.before.__li;t(a,b);g.addClass(b.domElement,"c");r=document.createElement("span");g.addClass(r,"property-name");r.innerHTML=b.property;var e=document.createElement("div");e.appendChild(r);e.appendChild(b.domElement);c=s(a,e,c.before);g.addClass(c,k.CLASS_CONTROLLER_ROW); +g.addClass(c,typeof b.getValue());p(a,c,b);a.__controllers.push(b);return b}function s(a,b,d){var c=document.createElement("li");b&&c.appendChild(b);d?a.__ul.insertBefore(c,params.before):a.__ul.appendChild(c);a.onResize();return c}function p(a,d,c){c.__li=d;c.__gui=a;i.extend(c,{options:function(b){if(arguments.length>1)return c.remove(),q(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[i.toArray(arguments)]});if(i.isArray(b)||i.isObject(b))return c.remove(),q(a,c.object,c.property, +{before:c.__li.nextElementSibling,factoryArgs:[b]})},name:function(a){c.__li.firstElementChild.firstElementChild.innerHTML=a;return c},listen:function(){c.__gui.listen(c);return c},remove:function(){c.__gui.remove(c);return c}});if(c instanceof j){var e=new h(c.object,c.property,{min:c.__min,max:c.__max,step:c.__step});i.each(["updateDisplay","onChange","onFinishChange"],function(a){var b=c[a],H=e[a];c[a]=e[a]=function(){var a=Array.prototype.slice.call(arguments);b.apply(c,a);return H.apply(e,a)}}); +g.addClass(d,"has-slider");c.domElement.insertBefore(e.domElement,c.domElement.firstElementChild)}else if(c instanceof h){var f=function(b){return i.isNumber(c.__min)&&i.isNumber(c.__max)?(c.remove(),q(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[c.__min,c.__max,c.__step]})):b};c.min=i.compose(f,c.min);c.max=i.compose(f,c.max)}else if(c instanceof b)g.bind(d,"click",function(){g.fakeEvent(c.__checkbox,"click")}),g.bind(c.__checkbox,"click",function(a){a.stopPropagation()}); +else if(c instanceof n)g.bind(d,"click",function(){g.fakeEvent(c.__button,"click")}),g.bind(d,"mouseover",function(){g.addClass(c.__button,"hover")}),g.bind(d,"mouseout",function(){g.removeClass(c.__button,"hover")});else if(c instanceof l)g.addClass(d,"color"),c.updateDisplay=i.compose(function(a){d.style.borderLeftColor=c.__color.toString();return a},c.updateDisplay),c.updateDisplay();c.setValue=i.compose(function(b){a.getRoot().__preset_select&&c.isModified()&&B(a.getRoot(),true);return b},c.setValue)} +function t(a,b){var c=a.getRoot(),d=c.__rememberedObjects.indexOf(b.object);if(d!=-1){var e=c.__rememberedObjectIndecesToControllers[d];e===void 0&&(e={},c.__rememberedObjectIndecesToControllers[d]=e);e[b.property]=b;if(c.load&&c.load.remembered){c=c.load.remembered;if(c[a.preset])c=c[a.preset];else if(c[w])c=c[w];else return;if(c[d]&&c[d][b.property]!==void 0)d=c[d][b.property],b.initialValue=d,b.setValue(d)}}}function I(a){var b=a.__save_row=document.createElement("li");g.addClass(a.domElement, +"has-save");a.__ul.insertBefore(b,a.__ul.firstChild);g.addClass(b,"save-row");var c=document.createElement("span");c.innerHTML=" ";g.addClass(c,"button gears");var d=document.createElement("span");d.innerHTML="Save";g.addClass(d,"button");g.addClass(d,"save");var e=document.createElement("span");e.innerHTML="New";g.addClass(e,"button");g.addClass(e,"save-as");var f=document.createElement("span");f.innerHTML="Revert";g.addClass(f,"button");g.addClass(f,"revert");var m=a.__preset_select=document.createElement("select"); +a.load&&a.load.remembered?i.each(a.load.remembered,function(b,c){C(a,c,c==a.preset)}):C(a,w,false);g.bind(m,"change",function(){for(var b=0;b0){a.preset=this.preset;if(!a.remembered)a.remembered={};a.remembered[this.preset]=z(this)}a.folders={};i.each(this.__folders,function(b, +c){a.folders[c]=b.getSaveObject()});return a},save:function(){if(!this.load.remembered)this.load.remembered={};this.load.remembered[this.preset]=z(this);B(this,false)},saveAs:function(a){if(!this.load.remembered)this.load.remembered={},this.load.remembered[w]=z(this,true);this.load.remembered[a]=z(this);this.preset=a;C(this,a,true)},revert:function(a){i.each(this.__controllers,function(b){this.getRoot().load.remembered?t(a||this.getRoot(),b):b.setValue(b.initialValue)},this);i.each(this.__folders, +function(a){a.revert(a)});a||B(this.getRoot(),false)},listen:function(a){var b=this.__listening.length==0;this.__listening.push(a);b&&E(this.__listening)}});return k}(dat.utils.css,'
\n\n Here\'s the new load parameter for your GUI\'s constructor:\n\n \n\n
\n\n Automatically save\n values to localStorage on exit.\n\n
The values saved to localStorage will\n override those passed to dat.GUI\'s constructor. This makes it\n easier to work incrementally, but localStorage is fragile,\n and your friends may not see the same values you do.\n \n
\n \n
\n\n
', +".dg ul{list-style:none;margin:0;padding:0;width:100%;clear:both}.dg.ac{position:fixed;top:0;left:0;right:0;height:0;z-index:0}.dg:not(.ac) .main{overflow:hidden}.dg.main{-webkit-transition:opacity 0.1s linear;-o-transition:opacity 0.1s linear;-moz-transition:opacity 0.1s linear;transition:opacity 0.1s linear}.dg.main.taller-than-window{overflow-y:auto}.dg.main.taller-than-window .close-button{opacity:1;margin-top:-1px;border-top:1px solid #2c2c2c}.dg.main ul.closed .close-button{opacity:1 !important}.dg.main:hover .close-button,.dg.main .close-button.drag{opacity:1}.dg.main .close-button{-webkit-transition:opacity 0.1s linear;-o-transition:opacity 0.1s linear;-moz-transition:opacity 0.1s linear;transition:opacity 0.1s linear;border:0;position:absolute;line-height:19px;height:20px;cursor:pointer;text-align:center;background-color:#000}.dg.main .close-button:hover{background-color:#111}.dg.a{float:right;margin-right:15px;overflow-x:hidden}.dg.a.has-save ul{margin-top:27px}.dg.a.has-save ul.closed{margin-top:0}.dg.a .save-row{position:fixed;top:0;z-index:1002}.dg li{-webkit-transition:height 0.1s ease-out;-o-transition:height 0.1s ease-out;-moz-transition:height 0.1s ease-out;transition:height 0.1s ease-out}.dg li:not(.folder){cursor:auto;height:27px;line-height:27px;overflow:hidden;padding:0 4px 0 5px}.dg li.folder{padding:0;border-left:4px solid rgba(0,0,0,0)}.dg li.title{cursor:pointer;margin-left:-4px}.dg .closed li:not(.title),.dg .closed ul li,.dg .closed ul li > *{height:0;overflow:hidden;border:0}.dg .cr{clear:both;padding-left:3px;height:27px}.dg .property-name{cursor:default;float:left;clear:left;width:40%;overflow:hidden;text-overflow:ellipsis}.dg .c{float:left;width:60%}.dg .c input[type=text]{border:0;margin-top:4px;padding:3px;width:100%;float:right}.dg .has-slider input[type=text]{width:30%;margin-left:0}.dg .slider{float:left;width:66%;margin-left:-5px;margin-right:0;height:19px;margin-top:4px}.dg .slider-fg{height:100%}.dg .c input[type=checkbox]{margin-top:9px}.dg .c select{margin-top:5px}.dg .cr.function,.dg .cr.function .property-name,.dg .cr.function *,.dg .cr.boolean,.dg .cr.boolean *{cursor:pointer}.dg .selector{display:none;position:absolute;margin-left:-9px;margin-top:23px;z-index:10}.dg .c:hover .selector,.dg .selector.drag{display:block}.dg li.save-row{padding:0}.dg li.save-row .button{display:inline-block;padding:0px 6px}.dg.dialogue{background-color:#222;width:460px;padding:15px;font-size:13px;line-height:15px}#dg-new-constructor{padding:10px;color:#222;font-family:Monaco, monospace;font-size:10px;border:0;resize:none;box-shadow:inset 1px 1px 1px #888;word-wrap:break-word;margin:12px 0;display:block;width:440px;overflow-y:scroll;height:100px;position:relative}#dg-local-explain{display:none;font-size:11px;line-height:17px;border-radius:3px;background-color:#333;padding:8px;margin-top:10px}#dg-local-explain code{font-size:10px}#dat-gui-save-locally{display:none}.dg{color:#eee;font:11px 'Lucida Grande', sans-serif;text-shadow:0 -1px 0 #111}.dg.main::-webkit-scrollbar{width:5px;background:#1a1a1a}.dg.main::-webkit-scrollbar-corner{height:0;display:none}.dg.main::-webkit-scrollbar-thumb{border-radius:5px;background:#676767}.dg li:not(.folder){background:#1a1a1a;border-bottom:1px solid #2c2c2c}.dg li.save-row{line-height:25px;background:#dad5cb;border:0}.dg li.save-row select{margin-left:5px;width:108px}.dg li.save-row .button{margin-left:5px;margin-top:1px;border-radius:2px;font-size:9px;line-height:7px;padding:4px 4px 5px 4px;background:#c5bdad;color:#fff;text-shadow:0 1px 0 #b0a58f;box-shadow:0 -1px 0 #b0a58f;cursor:pointer}.dg li.save-row .button.gears{background:#c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;height:7px;width:8px}.dg li.save-row .button:hover{background-color:#bab19e;box-shadow:0 -1px 0 #b0a58f}.dg li.folder{border-bottom:0}.dg li.title{padding-left:16px;background:#000 url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;cursor:pointer;border-bottom:1px solid rgba(255,255,255,0.2)}.dg .closed li.title{background-image:url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==)}.dg .cr.boolean{border-left:3px solid #806787}.dg .cr.function{border-left:3px solid #e61d5f}.dg .cr.number{border-left:3px solid #2fa1d6}.dg .cr.number input[type=text]{color:#2fa1d6}.dg .cr.string{border-left:3px solid #1ed36f}.dg .cr.string input[type=text]{color:#1ed36f}.dg .cr.function:hover,.dg .cr.boolean:hover{background:#111}.dg .c input[type=text]{background:#303030;outline:none}.dg .c input[type=text]:hover{background:#3c3c3c}.dg .c input[type=text]:focus{background:#494949;color:#fff}.dg .c .slider{background:#303030;cursor:ew-resize}.dg .c .slider-fg{background:#2fa1d6}.dg .c .slider:hover{background:#3c3c3c}.dg .c .slider:hover .slider-fg{background:#44abda}\n", +dat.controllers.factory=function(e,a,c,d,f,b,n){return function(h,j,m,l){var o=h[j];if(n.isArray(m)||n.isObject(m))return new e(h,j,m);if(n.isNumber(o))return n.isNumber(m)&&n.isNumber(l)?new c(h,j,m,l):new a(h,j,{min:m,max:l});if(n.isString(o))return new d(h,j);if(n.isFunction(o))return new f(h,j,"");if(n.isBoolean(o))return new b(h,j)}}(dat.controllers.OptionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.StringController=function(e,a,c){var d= +function(c,b){function e(){h.setValue(h.__input.value)}d.superclass.call(this,c,b);var h=this;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"keyup",e);a.bind(this.__input,"change",e);a.bind(this.__input,"blur",function(){h.__onFinishChange&&h.__onFinishChange.call(h,h.getValue())});a.bind(this.__input,"keydown",function(a){a.keyCode===13&&this.blur()});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;c.extend(d.prototype, +e.prototype,{updateDisplay:function(){if(!a.isActive(this.__input))this.__input.value=this.getValue();return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.controllers.FunctionController,dat.controllers.BooleanController,dat.utils.common),dat.controllers.Controller,dat.controllers.BooleanController,dat.controllers.FunctionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.OptionController, +dat.controllers.ColorController=function(e,a,c,d,f){function b(a,b,c,d){a.style.background="";f.each(j,function(e){a.style.cssText+="background: "+e+"linear-gradient("+b+", "+c+" 0%, "+d+" 100%); "})}function n(a){a.style.background="";a.style.cssText+="background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);";a.style.cssText+="background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"; +a.style.cssText+="background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}var h=function(e,l){function o(b){q(b);a.bind(window,"mousemove",q);a.bind(window, +"mouseup",j)}function j(){a.unbind(window,"mousemove",q);a.unbind(window,"mouseup",j)}function g(){var a=d(this.value);a!==false?(p.__color.__state=a,p.setValue(p.__color.toOriginal())):this.value=p.__color.toString()}function i(){a.unbind(window,"mousemove",s);a.unbind(window,"mouseup",i)}function q(b){b.preventDefault();var c=a.getWidth(p.__saturation_field),d=a.getOffset(p.__saturation_field),e=(b.clientX-d.left+document.body.scrollLeft)/c,b=1-(b.clientY-d.top+document.body.scrollTop)/c;b>1?b= +1:b<0&&(b=0);e>1?e=1:e<0&&(e=0);p.__color.v=b;p.__color.s=e;p.setValue(p.__color.toOriginal());return false}function s(b){b.preventDefault();var c=a.getHeight(p.__hue_field),d=a.getOffset(p.__hue_field),b=1-(b.clientY-d.top+document.body.scrollTop)/c;b>1?b=1:b<0&&(b=0);p.__color.h=b*360;p.setValue(p.__color.toOriginal());return false}h.superclass.call(this,e,l);this.__color=new c(this.getValue());this.__temp=new c(0);var p=this;this.domElement=document.createElement("div");a.makeSelectable(this.domElement, +false);this.__selector=document.createElement("div");this.__selector.className="selector";this.__saturation_field=document.createElement("div");this.__saturation_field.className="saturation-field";this.__field_knob=document.createElement("div");this.__field_knob.className="field-knob";this.__field_knob_border="2px solid ";this.__hue_knob=document.createElement("div");this.__hue_knob.className="hue-knob";this.__hue_field=document.createElement("div");this.__hue_field.className="hue-field";this.__input= +document.createElement("input");this.__input.type="text";this.__input_textShadow="0 1px 1px ";a.bind(this.__input,"keydown",function(a){a.keyCode===13&&g.call(this)});a.bind(this.__input,"blur",g);a.bind(this.__selector,"mousedown",function(){a.addClass(this,"drag").bind(window,"mouseup",function(){a.removeClass(p.__selector,"drag")})});var t=document.createElement("div");f.extend(this.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"}); +f.extend(this.__field_knob.style,{position:"absolute",width:"12px",height:"12px",border:this.__field_knob_border+(this.__color.v<0.5?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1});f.extend(this.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1});f.extend(this.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"});f.extend(t.style, +{width:"100%",height:"100%",background:"none"});b(t,"top","rgba(0,0,0,0)","#000");f.extend(this.__hue_field.style,{width:"15px",height:"100px",display:"inline-block",border:"1px solid #555",cursor:"ns-resize"});n(this.__hue_field);f.extend(this.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:this.__input_textShadow+"rgba(0,0,0,0.7)"});a.bind(this.__saturation_field,"mousedown",o);a.bind(this.__field_knob,"mousedown",o);a.bind(this.__hue_field,"mousedown", +function(b){s(b);a.bind(window,"mousemove",s);a.bind(window,"mouseup",i)});this.__saturation_field.appendChild(t);this.__selector.appendChild(this.__field_knob);this.__selector.appendChild(this.__saturation_field);this.__selector.appendChild(this.__hue_field);this.__hue_field.appendChild(this.__hue_knob);this.domElement.appendChild(this.__input);this.domElement.appendChild(this.__selector);this.updateDisplay()};h.superclass=e;f.extend(h.prototype,e.prototype,{updateDisplay:function(){var a=d(this.getValue()); +if(a!==false){var e=false;f.each(c.COMPONENTS,function(b){if(!f.isUndefined(a[b])&&!f.isUndefined(this.__color.__state[b])&&a[b]!==this.__color.__state[b])return e=true,{}},this);e&&f.extend(this.__color.__state,a)}f.extend(this.__temp.__state,this.__color.__state);this.__temp.a=1;var h=this.__color.v<0.5||this.__color.s>0.5?255:0,j=255-h;f.extend(this.__field_knob.style,{marginLeft:100*this.__color.s-7+"px",marginTop:100*(1-this.__color.v)-7+"px",backgroundColor:this.__temp.toString(),border:this.__field_knob_border+ +"rgb("+h+","+h+","+h+")"});this.__hue_knob.style.marginTop=(1-this.__color.h/360)*100+"px";this.__temp.s=1;this.__temp.v=1;b(this.__saturation_field,"left","#fff",this.__temp.toString());f.extend(this.__input.style,{backgroundColor:this.__input.value=this.__color.toString(),color:"rgb("+h+","+h+","+h+")",textShadow:this.__input_textShadow+"rgba("+j+","+j+","+j+",.7)"})}});var j=["-moz-","-o-","-webkit-","-ms-",""];return h}(dat.controllers.Controller,dat.dom.dom,dat.color.Color=function(e,a,c,d){function f(a, +b,c){Object.defineProperty(a,b,{get:function(){if(this.__state.space==="RGB")return this.__state[b];n(this,b,c);return this.__state[b]},set:function(a){if(this.__state.space!=="RGB")n(this,b,c),this.__state.space="RGB";this.__state[b]=a}})}function b(a,b){Object.defineProperty(a,b,{get:function(){if(this.__state.space==="HSV")return this.__state[b];h(this);return this.__state[b]},set:function(a){if(this.__state.space!=="HSV")h(this),this.__state.space="HSV";this.__state[b]=a}})}function n(b,c,e){if(b.__state.space=== +"HEX")b.__state[c]=a.component_from_hex(b.__state.hex,e);else if(b.__state.space==="HSV")d.extend(b.__state,a.hsv_to_rgb(b.__state.h,b.__state.s,b.__state.v));else throw"Corrupted color state";}function h(b){var c=a.rgb_to_hsv(b.r,b.g,b.b);d.extend(b.__state,{s:c.s,v:c.v});if(d.isNaN(c.h)){if(d.isUndefined(b.__state.h))b.__state.h=0}else b.__state.h=c.h}var j=function(){this.__state=e.apply(this,arguments);if(this.__state===false)throw"Failed to interpret color arguments";this.__state.a=this.__state.a|| +1};j.COMPONENTS="r,g,b,h,s,v,hex,a".split(",");d.extend(j.prototype,{toString:function(){return c(this)},toOriginal:function(){return this.__state.conversion.write(this)}});f(j.prototype,"r",2);f(j.prototype,"g",1);f(j.prototype,"b",0);b(j.prototype,"h");b(j.prototype,"s");b(j.prototype,"v");Object.defineProperty(j.prototype,"a",{get:function(){return this.__state.a},set:function(a){this.__state.a=a}});Object.defineProperty(j.prototype,"hex",{get:function(){if(!this.__state.space!=="HEX")this.__state.hex= +a.rgb_to_hex(this.r,this.g,this.b);return this.__state.hex},set:function(a){this.__state.space="HEX";this.__state.hex=a}});return j}(dat.color.interpret,dat.color.math=function(){var e;return{hsv_to_rgb:function(a,c,d){var e=a/60-Math.floor(a/60),b=d*(1-c),n=d*(1-e*c),c=d*(1-(1-e)*c),a=[[d,c,b],[n,d,b],[b,d,c],[b,n,d],[c,b,d],[d,b,n]][Math.floor(a/60)%6];return{r:a[0]*255,g:a[1]*255,b:a[2]*255}},rgb_to_hsv:function(a,c,d){var e=Math.min(a,c,d),b=Math.max(a,c,d),e=b-e;if(b==0)return{h:NaN,s:0,v:0}; +a=a==b?(c-d)/e:c==b?2+(d-a)/e:4+(a-c)/e;a/=6;a<0&&(a+=1);return{h:a*360,s:e/b,v:b/255}},rgb_to_hex:function(a,c,d){a=this.hex_with_component(0,2,a);a=this.hex_with_component(a,1,c);return a=this.hex_with_component(a,0,d)},component_from_hex:function(a,c){return a>>c*8&255},hex_with_component:function(a,c,d){return d<<(e=c*8)|a&~(255< + +(c) 2009-2014 Stuart Knightley +Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/master/LICENSE.markdown. + +JSZip uses the library pako released under the MIT license : +https://github.com/nodeca/pako/blob/master/LICENSE +*/ +!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;"undefined"!=typeof window?b=window:"undefined"!=typeof global?b=global:"undefined"!=typeof self&&(b=self),b.JSZip=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g>2,g=(3&b)<<4|c>>4,h=(15&c)<<2|e>>6,i=63&e,isNaN(c)?h=i=64:isNaN(e)&&(i=64),j=j+d.charAt(f)+d.charAt(g)+d.charAt(h)+d.charAt(i);return j},c.decode=function(a){var b,c,e,f,g,h,i,j="",k=0;for(a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");k>4,c=(15&g)<<4|h>>2,e=(3&h)<<6|i,j+=String.fromCharCode(b),64!=h&&(j+=String.fromCharCode(c)),64!=i&&(j+=String.fromCharCode(e));return j}},{}],2:[function(a,b){"use strict";function c(){this.compressedSize=0,this.uncompressedSize=0,this.crc32=0,this.compressionMethod=null,this.compressedContent=null}c.prototype={getContent:function(){return null},getCompressedContent:function(){return null}},b.exports=c},{}],3:[function(a,b,c){"use strict";c.STORE={magic:"\x00\x00",compress:function(a){return a},uncompress:function(a){return a},compressInputType:null,uncompressInputType:null},c.DEFLATE=a("./flate")},{"./flate":8}],4:[function(a,b){"use strict";var c=a("./utils"),d=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918e3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117];b.exports=function(a,b){if("undefined"==typeof a||!a.length)return 0;var e="string"!==c.getTypeOf(a);"undefined"==typeof b&&(b=0);var f=0,g=0,h=0;b=-1^b;for(var i=0,j=a.length;j>i;i++)h=e?a[i]:a.charCodeAt(i),g=255&(b^h),f=d[g],b=b>>>8^f;return-1^b}},{"./utils":21}],5:[function(a,b){"use strict";function c(){this.data=null,this.length=0,this.index=0}var d=a("./utils");c.prototype={checkOffset:function(a){this.checkIndex(this.index+a)},checkIndex:function(a){if(this.lengtha)throw new Error("End of data reached (data length = "+this.length+", asked index = "+a+"). Corrupted zip ?")},setIndex:function(a){this.checkIndex(a),this.index=a},skip:function(a){this.setIndex(this.index+a)},byteAt:function(){},readInt:function(a){var b,c=0;for(this.checkOffset(a),b=this.index+a-1;b>=this.index;b--)c=(c<<8)+this.byteAt(b);return this.index+=a,c},readString:function(a){return d.transformTo("string",this.readData(a))},readData:function(){},lastIndexOfSignature:function(){},readDate:function(){var a=this.readInt(4);return new Date((a>>25&127)+1980,(a>>21&15)-1,a>>16&31,a>>11&31,a>>5&63,(31&a)<<1)}},b.exports=c},{"./utils":21}],6:[function(a,b,c){"use strict";c.base64=!1,c.binary=!1,c.dir=!1,c.createFolders=!1,c.date=null,c.compression=null,c.comment=null},{}],7:[function(a,b,c){"use strict";var d=a("./utils");c.string2binary=function(a){return d.string2binary(a)},c.string2Uint8Array=function(a){return d.transformTo("uint8array",a)},c.uint8Array2String=function(a){return d.transformTo("string",a)},c.string2Blob=function(a){var b=d.transformTo("arraybuffer",a);return d.arrayBuffer2Blob(b)},c.arrayBuffer2Blob=function(a){return d.arrayBuffer2Blob(a)},c.transformTo=function(a,b){return d.transformTo(a,b)},c.getTypeOf=function(a){return d.getTypeOf(a)},c.checkSupport=function(a){return d.checkSupport(a)},c.MAX_VALUE_16BITS=d.MAX_VALUE_16BITS,c.MAX_VALUE_32BITS=d.MAX_VALUE_32BITS,c.pretty=function(a){return d.pretty(a)},c.findCompression=function(a){return d.findCompression(a)},c.isRegExp=function(a){return d.isRegExp(a)}},{"./utils":21}],8:[function(a,b,c){"use strict";var d="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,e=a("pako");c.uncompressInputType=d?"uint8array":"array",c.compressInputType=d?"uint8array":"array",c.magic="\b\x00",c.compress=function(a){return e.deflateRaw(a)},c.uncompress=function(a){return e.inflateRaw(a)}},{pako:24}],9:[function(a,b){"use strict";function c(a,b){return this instanceof c?(this.files={},this.comment=null,this.root="",a&&this.load(a,b),void(this.clone=function(){var a=new c;for(var b in this)"function"!=typeof this[b]&&(a[b]=this[b]);return a})):new c(a,b)}var d=a("./base64");c.prototype=a("./object"),c.prototype.load=a("./load"),c.support=a("./support"),c.defaults=a("./defaults"),c.utils=a("./deprecatedPublicUtils"),c.base64={encode:function(a){return d.encode(a)},decode:function(a){return d.decode(a)}},c.compressions=a("./compressions"),b.exports=c},{"./base64":1,"./compressions":3,"./defaults":6,"./deprecatedPublicUtils":7,"./load":10,"./object":13,"./support":17}],10:[function(a,b){"use strict";var c=a("./base64"),d=a("./zipEntries");b.exports=function(a,b){var e,f,g,h;for(b=b||{},b.base64&&(a=c.decode(a)),f=new d(a,b),e=f.files,g=0;gc;c++)d+=String.fromCharCode(255&a),a>>>=8;return d},t=function(){var a,b,c={};for(a=0;a0?a.substring(0,b):""},x=function(a,b){return"/"!=a.slice(-1)&&(a+="/"),b="undefined"!=typeof b?b:!1,this.files[a]||v.call(this,a,null,{dir:!0,createFolders:b}),this.files[a]},y=function(a,b){var c,f=new j;return a._data instanceof j?(f.uncompressedSize=a._data.uncompressedSize,f.crc32=a._data.crc32,0===f.uncompressedSize||a.dir?(b=i.STORE,f.compressedContent="",f.crc32=0):a._data.compressionMethod===b.magic?f.compressedContent=a._data.getCompressedContent():(c=a._data.getContent(),f.compressedContent=b.compress(d.transformTo(b.compressInputType,c)))):(c=p(a),(!c||0===c.length||a.dir)&&(b=i.STORE,c=""),f.uncompressedSize=c.length,f.crc32=e(c),f.compressedContent=b.compress(d.transformTo(b.compressInputType,c))),f.compressedSize=f.compressedContent.length,f.compressionMethod=b.magic,f},z=function(a,b,c,g){var h,i,j,k,m=(c.compressedContent,d.transformTo("string",l.utf8encode(b.name))),n=b.comment||"",o=d.transformTo("string",l.utf8encode(n)),p=m.length!==b.name.length,q=o.length!==n.length,r=b.options,t="",u="",v="";j=b._initialMetadata.dir!==b.dir?b.dir:r.dir,k=b._initialMetadata.date!==b.date?b.date:r.date,h=k.getHours(),h<<=6,h|=k.getMinutes(),h<<=5,h|=k.getSeconds()/2,i=k.getFullYear()-1980,i<<=4,i|=k.getMonth()+1,i<<=5,i|=k.getDate(),p&&(u=s(1,1)+s(e(m),4)+m,t+="up"+s(u.length,2)+u),q&&(v=s(1,1)+s(this.crc32(o),4)+o,t+="uc"+s(v.length,2)+v);var w="";w+="\n\x00",w+=p||q?"\x00\b":"\x00\x00",w+=c.compressionMethod,w+=s(h,2),w+=s(i,2),w+=s(c.crc32,4),w+=s(c.compressedSize,4),w+=s(c.uncompressedSize,4),w+=s(m.length,2),w+=s(t.length,2);var x=f.LOCAL_FILE_HEADER+w+m+t,y=f.CENTRAL_FILE_HEADER+"\x00"+w+s(o.length,2)+"\x00\x00\x00\x00"+(j===!0?"\x00\x00\x00":"\x00\x00\x00\x00")+s(g,4)+m+t+o;return{fileRecord:x,dirRecord:y,compressedObject:c}},A={load:function(){throw new Error("Load method is not defined. Is the file jszip-load.js included ?")},filter:function(a){var b,c,d,e,f=[];for(b in this.files)this.files.hasOwnProperty(b)&&(d=this.files[b],e=new r(d.name,d._data,t(d.options)),c=b.slice(this.root.length,b.length),b.slice(0,this.root.length)===this.root&&a(c,e)&&f.push(e));return f},file:function(a,b,c){if(1===arguments.length){if(d.isRegExp(a)){var e=a;return this.filter(function(a,b){return!b.dir&&e.test(a)})}return this.filter(function(b,c){return!c.dir&&b===a})[0]||null}return a=this.root+a,v.call(this,a,b,c),this},folder:function(a){if(!a)return this;if(d.isRegExp(a))return this.filter(function(b,c){return c.dir&&a.test(b)});var b=this.root+a,c=x.call(this,b),e=this.clone();return e.root=c.name,e},remove:function(a){a=this.root+a;var b=this.files[a];if(b||("/"!=a.slice(-1)&&(a+="/"),b=this.files[a]),b&&!b.dir)delete this.files[a];else for(var c=this.filter(function(b,c){return c.name.slice(0,a.length)===a}),d=0;d=0;--f)if(this.data[f]===b&&this.data[f+1]===c&&this.data[f+2]===d&&this.data[f+3]===e)return f;return-1},c.prototype.readData=function(a){if(this.checkOffset(a),0===a)return new Uint8Array(0);var b=this.data.subarray(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./dataReader":5}],19:[function(a,b){"use strict";var c=a("./utils"),d=function(a){this.data=new Uint8Array(a),this.index=0};d.prototype={append:function(a){0!==a.length&&(a=c.transformTo("uint8array",a),this.data.set(a,this.index),this.index+=a.length)},finalize:function(){return this.data}},b.exports=d},{"./utils":21}],20:[function(a,b,c){"use strict";for(var d=a("./utils"),e=a("./support"),f=a("./nodeBuffer"),g=new Array(256),h=0;256>h;h++)g[h]=h>=252?6:h>=248?5:h>=240?4:h>=224?3:h>=192?2:1;g[254]=g[254]=1;var i=function(a){var b,c,d,f,g,h=a.length,i=0;for(f=0;h>f;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),i+=128>c?1:2048>c?2:65536>c?3:4;for(b=e.uint8array?new Uint8Array(i):new Array(i),g=0,f=0;i>g;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),128>c?b[g++]=c:2048>c?(b[g++]=192|c>>>6,b[g++]=128|63&c):65536>c?(b[g++]=224|c>>>12,b[g++]=128|c>>>6&63,b[g++]=128|63&c):(b[g++]=240|c>>>18,b[g++]=128|c>>>12&63,b[g++]=128|c>>>6&63,b[g++]=128|63&c);return b},j=function(a,b){var c;for(b=b||a.length,b>a.length&&(b=a.length),c=b-1;c>=0&&128===(192&a[c]);)c--;return 0>c?b:0===c?b:c+g[a[c]]>b?c:b},k=function(a){var b,c,e,f,h=a.length,i=new Array(2*h);for(c=0,b=0;h>b;)if(e=a[b++],128>e)i[c++]=e;else if(f=g[e],f>4)i[c++]=65533,b+=f-1;else{for(e&=2===f?31:3===f?15:7;f>1&&h>b;)e=e<<6|63&a[b++],f--;f>1?i[c++]=65533:65536>e?i[c++]=e:(e-=65536,i[c++]=55296|e>>10&1023,i[c++]=56320|1023&e)}return i.length!==c&&(i.subarray?i=i.subarray(0,c):i.length=c),d.applyFromCharCode(i)};c.utf8encode=function(a){return e.nodebuffer?f(a,"utf-8"):i(a)},c.utf8decode=function(a){if(e.nodebuffer)return d.transformTo("nodebuffer",a).toString("utf-8");a=d.transformTo(e.uint8array?"uint8array":"array",a);for(var b=[],c=0,f=a.length,g=65536;f>c;){var h=j(a,Math.min(c+g,f));b.push(e.uint8array?k(a.subarray(c,h)):k(a.slice(c,h))),c=h}return b.join("")}},{"./nodeBuffer":11,"./support":17,"./utils":21}],21:[function(a,b,c){"use strict";function d(a){return a}function e(a,b){for(var c=0;cg&&b>1;)try{d.push("array"===f||"nodebuffer"===f?String.fromCharCode.apply(null,a.slice(g,Math.min(g+b,e))):String.fromCharCode.apply(null,a.subarray(g,Math.min(g+b,e)))),g+=b}catch(i){b=Math.floor(b/2)}return d.join("")}function g(a,b){for(var c=0;cb?"0":"")+b.toString(16).toUpperCase();return d},c.findCompression=function(a){for(var b in i)if(i.hasOwnProperty(b)&&i[b].magic===a)return i[b];return null},c.isRegExp=function(a){return"[object RegExp]"===Object.prototype.toString.call(a)}},{"./compressions":3,"./nodeBuffer":11,"./support":17}],22:[function(a,b){"use strict";function c(a,b){this.files=[],this.loadOptions=b,a&&this.load(a)}var d=a("./stringReader"),e=a("./nodeBufferReader"),f=a("./uint8ArrayReader"),g=a("./utils"),h=a("./signature"),i=a("./zipEntry"),j=a("./support"),k=a("./object");c.prototype={checkSignature:function(a){var b=this.reader.readString(4);if(b!==a)throw new Error("Corrupted zip or bug : unexpected signature ("+g.pretty(b)+", expected "+g.pretty(a)+")")},readBlockEndOfCentral:function(){this.diskNumber=this.reader.readInt(2),this.diskWithCentralDirStart=this.reader.readInt(2),this.centralDirRecordsOnThisDisk=this.reader.readInt(2),this.centralDirRecords=this.reader.readInt(2),this.centralDirSize=this.reader.readInt(4),this.centralDirOffset=this.reader.readInt(4),this.zipCommentLength=this.reader.readInt(2),this.zipComment=this.reader.readString(this.zipCommentLength),this.zipComment=k.utf8decode(this.zipComment)},readBlockZip64EndOfCentral:function(){this.zip64EndOfCentralSize=this.reader.readInt(8),this.versionMadeBy=this.reader.readString(2),this.versionNeeded=this.reader.readInt(2),this.diskNumber=this.reader.readInt(4),this.diskWithCentralDirStart=this.reader.readInt(4),this.centralDirRecordsOnThisDisk=this.reader.readInt(8),this.centralDirRecords=this.reader.readInt(8),this.centralDirSize=this.reader.readInt(8),this.centralDirOffset=this.reader.readInt(8),this.zip64ExtensibleData={};for(var a,b,c,d=this.zip64EndOfCentralSize-44,e=0;d>e;)a=this.reader.readInt(2),b=this.reader.readInt(4),c=this.reader.readString(b),this.zip64ExtensibleData[a]={id:a,length:b,value:c}},readBlockZip64EndOfCentralLocator:function(){if(this.diskWithZip64CentralDirStart=this.reader.readInt(4),this.relativeOffsetEndOfZip64CentralDir=this.reader.readInt(8),this.disksCount=this.reader.readInt(4),this.disksCount>1)throw new Error("Multi-volumes zip are not supported")},readLocalFiles:function(){var a,b;for(a=0;a0?b.windowBits=-b.windowBits:b.gzip&&b.windowBits>0&&b.windowBits<16&&(b.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new k,this.strm.avail_out=0;var c=g.deflateInit2(this.strm,b.level,b.method,b.windowBits,b.memLevel,b.strategy);if(c!==n)throw new Error(j[c]);b.header&&g.deflateSetHeader(this.strm,b.header) +};s.prototype.push=function(a,b){var c,d,e=this.strm,f=this.options.chunkSize;if(this.ended)return!1;d=b===~~b?b:b===!0?m:l,e.input="string"==typeof a?i.string2buf(a):a,e.next_in=0,e.avail_in=e.input.length;do{if(0===e.avail_out&&(e.output=new h.Buf8(f),e.next_out=0,e.avail_out=f),c=g.deflate(e,d),c!==o&&c!==n)return this.onEnd(c),this.ended=!0,!1;(0===e.avail_out||0===e.avail_in&&d===m)&&this.onData("string"===this.options.to?i.buf2binstring(h.shrinkBuf(e.output,e.next_out)):h.shrinkBuf(e.output,e.next_out))}while((e.avail_in>0||0===e.avail_out)&&c!==o);return d===m?(c=g.deflateEnd(this.strm),this.onEnd(c),this.ended=!0,c===n):!0},s.prototype.onData=function(a){this.chunks.push(a)},s.prototype.onEnd=function(a){a===n&&(this.result="string"===this.options.to?this.chunks.join(""):h.flattenChunks(this.chunks)),this.chunks=[],this.err=a,this.msg=this.strm.msg},c.Deflate=s,c.deflate=d,c.deflateRaw=e,c.gzip=f},{"./utils/common":27,"./utils/strings":28,"./zlib/deflate.js":32,"./zlib/messages":37,"./zlib/zstream":39}],26:[function(a,b,c){"use strict";function d(a,b){var c=new m(b);if(c.push(a,!0),c.err)throw c.msg;return c.result}function e(a,b){return b=b||{},b.raw=!0,d(a,b)}var f=a("./zlib/inflate.js"),g=a("./utils/common"),h=a("./utils/strings"),i=a("./zlib/constants"),j=a("./zlib/messages"),k=a("./zlib/zstream"),l=a("./zlib/gzheader"),m=function(a){this.options=g.assign({chunkSize:16384,windowBits:0,to:""},a||{});var b=this.options;b.raw&&b.windowBits>=0&&b.windowBits<16&&(b.windowBits=-b.windowBits,0===b.windowBits&&(b.windowBits=-15)),!(b.windowBits>=0&&b.windowBits<16)||a&&a.windowBits||(b.windowBits+=32),b.windowBits>15&&b.windowBits<48&&0===(15&b.windowBits)&&(b.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new k,this.strm.avail_out=0;var c=f.inflateInit2(this.strm,b.windowBits);if(c!==i.Z_OK)throw new Error(j[c]);this.header=new l,f.inflateGetHeader(this.strm,this.header)};m.prototype.push=function(a,b){var c,d,e,j,k,l=this.strm,m=this.options.chunkSize;if(this.ended)return!1;d=b===~~b?b:b===!0?i.Z_FINISH:i.Z_NO_FLUSH,l.input="string"==typeof a?h.binstring2buf(a):a,l.next_in=0,l.avail_in=l.input.length;do{if(0===l.avail_out&&(l.output=new g.Buf8(m),l.next_out=0,l.avail_out=m),c=f.inflate(l,i.Z_NO_FLUSH),c!==i.Z_STREAM_END&&c!==i.Z_OK)return this.onEnd(c),this.ended=!0,!1;l.next_out&&(0===l.avail_out||c===i.Z_STREAM_END||0===l.avail_in&&d===i.Z_FINISH)&&("string"===this.options.to?(e=h.utf8border(l.output,l.next_out),j=l.next_out-e,k=h.buf2string(l.output,e),l.next_out=j,l.avail_out=m-j,j&&g.arraySet(l.output,l.output,e,j,0),this.onData(k)):this.onData(g.shrinkBuf(l.output,l.next_out)))}while(l.avail_in>0&&c!==i.Z_STREAM_END);return c===i.Z_STREAM_END&&(d=i.Z_FINISH),d===i.Z_FINISH?(c=f.inflateEnd(this.strm),this.onEnd(c),this.ended=!0,c===i.Z_OK):!0},m.prototype.onData=function(a){this.chunks.push(a)},m.prototype.onEnd=function(a){a===i.Z_OK&&(this.result="string"===this.options.to?this.chunks.join(""):g.flattenChunks(this.chunks)),this.chunks=[],this.err=a,this.msg=this.strm.msg},c.Inflate=m,c.inflate=d,c.inflateRaw=e,c.ungzip=d},{"./utils/common":27,"./utils/strings":28,"./zlib/constants":30,"./zlib/gzheader":33,"./zlib/inflate.js":35,"./zlib/messages":37,"./zlib/zstream":39}],27:[function(a,b,c){"use strict";var d="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;c.assign=function(a){for(var b=Array.prototype.slice.call(arguments,1);b.length;){var c=b.shift();if(c){if("object"!=typeof c)throw new TypeError(c+"must be non-object");for(var d in c)c.hasOwnProperty(d)&&(a[d]=c[d])}}return a},c.shrinkBuf=function(a,b){return a.length===b?a:a.subarray?a.subarray(0,b):(a.length=b,a)};var e={arraySet:function(a,b,c,d,e){if(b.subarray&&a.subarray)return void a.set(b.subarray(c,c+d),e);for(var f=0;d>f;f++)a[e+f]=b[c+f]},flattenChunks:function(a){var b,c,d,e,f,g;for(d=0,b=0,c=a.length;c>b;b++)d+=a[b].length;for(g=new Uint8Array(d),e=0,b=0,c=a.length;c>b;b++)f=a[b],g.set(f,e),e+=f.length;return g}},f={arraySet:function(a,b,c,d,e){for(var f=0;d>f;f++)a[e+f]=b[c+f]},flattenChunks:function(a){return[].concat.apply([],a)}};c.setTyped=function(a){a?(c.Buf8=Uint8Array,c.Buf16=Uint16Array,c.Buf32=Int32Array,c.assign(c,e)):(c.Buf8=Array,c.Buf16=Array,c.Buf32=Array,c.assign(c,f))},c.setTyped(d)},{}],28:[function(a,b,c){"use strict";function d(a,b){if(65537>b&&(a.subarray&&g||!a.subarray&&f))return String.fromCharCode.apply(null,e.shrinkBuf(a,b));for(var c="",d=0;b>d;d++)c+=String.fromCharCode(a[d]);return c}var e=a("./common"),f=!0,g=!0;try{String.fromCharCode.apply(null,[0])}catch(h){f=!1}try{String.fromCharCode.apply(null,new Uint8Array(1))}catch(h){g=!1}for(var i=new e.Buf8(256),j=0;256>j;j++)i[j]=j>=252?6:j>=248?5:j>=240?4:j>=224?3:j>=192?2:1;i[254]=i[254]=1,c.string2buf=function(a){var b,c,d,f,g,h=a.length,i=0;for(f=0;h>f;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),i+=128>c?1:2048>c?2:65536>c?3:4;for(b=new e.Buf8(i),g=0,f=0;i>g;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),128>c?b[g++]=c:2048>c?(b[g++]=192|c>>>6,b[g++]=128|63&c):65536>c?(b[g++]=224|c>>>12,b[g++]=128|c>>>6&63,b[g++]=128|63&c):(b[g++]=240|c>>>18,b[g++]=128|c>>>12&63,b[g++]=128|c>>>6&63,b[g++]=128|63&c);return b},c.buf2binstring=function(a){return d(a,a.length)},c.binstring2buf=function(a){for(var b=new e.Buf8(a.length),c=0,d=b.length;d>c;c++)b[c]=a.charCodeAt(c);return b},c.buf2string=function(a,b){var c,e,f,g,h=b||a.length,j=new Array(2*h);for(e=0,c=0;h>c;)if(f=a[c++],128>f)j[e++]=f;else if(g=i[f],g>4)j[e++]=65533,c+=g-1;else{for(f&=2===g?31:3===g?15:7;g>1&&h>c;)f=f<<6|63&a[c++],g--;g>1?j[e++]=65533:65536>f?j[e++]=f:(f-=65536,j[e++]=55296|f>>10&1023,j[e++]=56320|1023&f)}return d(j,e)},c.utf8border=function(a,b){var c;for(b=b||a.length,b>a.length&&(b=a.length),c=b-1;c>=0&&128===(192&a[c]);)c--;return 0>c?b:0===c?b:c+i[a[c]]>b?c:b}},{"./common":27}],29:[function(a,b){"use strict";function c(a,b,c,d){for(var e=65535&a|0,f=a>>>16&65535|0,g=0;0!==c;){g=c>2e3?2e3:c,c-=g;do e=e+b[d++]|0,f=f+e|0;while(--g);e%=65521,f%=65521}return e|f<<16|0}b.exports=c},{}],30:[function(a,b){b.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],31:[function(a,b){"use strict";function c(){for(var a,b=[],c=0;256>c;c++){a=c;for(var d=0;8>d;d++)a=1&a?3988292384^a>>>1:a>>>1;b[c]=a}return b}function d(a,b,c,d){var f=e,g=d+c;a=-1^a;for(var h=d;g>h;h++)a=a>>>8^f[255&(a^b[h])];return-1^a}var e=c();b.exports=d},{}],32:[function(a,b,c){"use strict";function d(a,b){return a.msg=G[b],b}function e(a){return(a<<1)-(a>4?9:0)}function f(a){for(var b=a.length;--b>=0;)a[b]=0}function g(a){var b=a.state,c=b.pending;c>a.avail_out&&(c=a.avail_out),0!==c&&(C.arraySet(a.output,b.pending_buf,b.pending_out,c,a.next_out),a.next_out+=c,b.pending_out+=c,a.total_out+=c,a.avail_out-=c,b.pending-=c,0===b.pending&&(b.pending_out=0))}function h(a,b){D._tr_flush_block(a,a.block_start>=0?a.block_start:-1,a.strstart-a.block_start,b),a.block_start=a.strstart,g(a.strm)}function i(a,b){a.pending_buf[a.pending++]=b}function j(a,b){a.pending_buf[a.pending++]=b>>>8&255,a.pending_buf[a.pending++]=255&b}function k(a,b,c,d){var e=a.avail_in;return e>d&&(e=d),0===e?0:(a.avail_in-=e,C.arraySet(b,a.input,a.next_in,e,c),1===a.state.wrap?a.adler=E(a.adler,b,e,c):2===a.state.wrap&&(a.adler=F(a.adler,b,e,c)),a.next_in+=e,a.total_in+=e,e)}function l(a,b){var c,d,e=a.max_chain_length,f=a.strstart,g=a.prev_length,h=a.nice_match,i=a.strstart>a.w_size-jb?a.strstart-(a.w_size-jb):0,j=a.window,k=a.w_mask,l=a.prev,m=a.strstart+ib,n=j[f+g-1],o=j[f+g];a.prev_length>=a.good_match&&(e>>=2),h>a.lookahead&&(h=a.lookahead);do if(c=b,j[c+g]===o&&j[c+g-1]===n&&j[c]===j[f]&&j[++c]===j[f+1]){f+=2,c++;do;while(j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&m>f);if(d=ib-(m-f),f=m-ib,d>g){if(a.match_start=b,g=d,d>=h)break;n=j[f+g-1],o=j[f+g]}}while((b=l[b&k])>i&&0!==--e);return g<=a.lookahead?g:a.lookahead}function m(a){var b,c,d,e,f,g=a.w_size;do{if(e=a.window_size-a.lookahead-a.strstart,a.strstart>=g+(g-jb)){C.arraySet(a.window,a.window,g,g,0),a.match_start-=g,a.strstart-=g,a.block_start-=g,c=a.hash_size,b=c;do d=a.head[--b],a.head[b]=d>=g?d-g:0;while(--c);c=g,b=c;do d=a.prev[--b],a.prev[b]=d>=g?d-g:0;while(--c);e+=g}if(0===a.strm.avail_in)break;if(c=k(a.strm,a.window,a.strstart+a.lookahead,e),a.lookahead+=c,a.lookahead+a.insert>=hb)for(f=a.strstart-a.insert,a.ins_h=a.window[f],a.ins_h=(a.ins_h<a.pending_buf_size-5&&(c=a.pending_buf_size-5);;){if(a.lookahead<=1){if(m(a),0===a.lookahead&&b===H)return sb;if(0===a.lookahead)break}a.strstart+=a.lookahead,a.lookahead=0;var d=a.block_start+c;if((0===a.strstart||a.strstart>=d)&&(a.lookahead=a.strstart-d,a.strstart=d,h(a,!1),0===a.strm.avail_out))return sb;if(a.strstart-a.block_start>=a.w_size-jb&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.strstart>a.block_start&&(h(a,!1),0===a.strm.avail_out)?sb:sb}function o(a,b){for(var c,d;;){if(a.lookahead=hb&&(a.ins_h=(a.ins_h<=hb)if(d=D._tr_tally(a,a.strstart-a.match_start,a.match_length-hb),a.lookahead-=a.match_length,a.match_length<=a.max_lazy_match&&a.lookahead>=hb){a.match_length--;do a.strstart++,a.ins_h=(a.ins_h<=hb&&(a.ins_h=(a.ins_h<4096)&&(a.match_length=hb-1)),a.prev_length>=hb&&a.match_length<=a.prev_length){e=a.strstart+a.lookahead-hb,d=D._tr_tally(a,a.strstart-1-a.prev_match,a.prev_length-hb),a.lookahead-=a.prev_length-1,a.prev_length-=2;do++a.strstart<=e&&(a.ins_h=(a.ins_h<=hb&&a.strstart>0&&(e=a.strstart-1,d=g[e],d===g[++e]&&d===g[++e]&&d===g[++e])){f=a.strstart+ib;do;while(d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&f>e);a.match_length=ib-(f-e),a.match_length>a.lookahead&&(a.match_length=a.lookahead)}if(a.match_length>=hb?(c=D._tr_tally(a,1,a.match_length-hb),a.lookahead-=a.match_length,a.strstart+=a.match_length,a.match_length=0):(c=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++),c&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function r(a,b){for(var c;;){if(0===a.lookahead&&(m(a),0===a.lookahead)){if(b===H)return sb;break}if(a.match_length=0,c=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++,c&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function s(a){a.window_size=2*a.w_size,f(a.head),a.max_lazy_match=B[a.level].max_lazy,a.good_match=B[a.level].good_length,a.nice_match=B[a.level].nice_length,a.max_chain_length=B[a.level].max_chain,a.strstart=0,a.block_start=0,a.lookahead=0,a.insert=0,a.match_length=a.prev_length=hb-1,a.match_available=0,a.ins_h=0}function t(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=Y,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new C.Buf16(2*fb),this.dyn_dtree=new C.Buf16(2*(2*db+1)),this.bl_tree=new C.Buf16(2*(2*eb+1)),f(this.dyn_ltree),f(this.dyn_dtree),f(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new C.Buf16(gb+1),this.heap=new C.Buf16(2*cb+1),f(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new C.Buf16(2*cb+1),f(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function u(a){var b;return a&&a.state?(a.total_in=a.total_out=0,a.data_type=X,b=a.state,b.pending=0,b.pending_out=0,b.wrap<0&&(b.wrap=-b.wrap),b.status=b.wrap?lb:qb,a.adler=2===b.wrap?0:1,b.last_flush=H,D._tr_init(b),M):d(a,O)}function v(a){var b=u(a);return b===M&&s(a.state),b}function w(a,b){return a&&a.state?2!==a.state.wrap?O:(a.state.gzhead=b,M):O}function x(a,b,c,e,f,g){if(!a)return O;var h=1;if(b===R&&(b=6),0>e?(h=0,e=-e):e>15&&(h=2,e-=16),1>f||f>Z||c!==Y||8>e||e>15||0>b||b>9||0>g||g>V)return d(a,O);8===e&&(e=9);var i=new t;return a.state=i,i.strm=a,i.wrap=h,i.gzhead=null,i.w_bits=e,i.w_size=1<>1,i.l_buf=3*i.lit_bufsize,i.level=b,i.strategy=g,i.method=c,v(a)}function y(a,b){return x(a,b,Y,$,_,W)}function z(a,b){var c,h,k,l;if(!a||!a.state||b>L||0>b)return a?d(a,O):O;if(h=a.state,!a.output||!a.input&&0!==a.avail_in||h.status===rb&&b!==K)return d(a,0===a.avail_out?Q:O);if(h.strm=a,c=h.last_flush,h.last_flush=b,h.status===lb)if(2===h.wrap)a.adler=0,i(h,31),i(h,139),i(h,8),h.gzhead?(i(h,(h.gzhead.text?1:0)+(h.gzhead.hcrc?2:0)+(h.gzhead.extra?4:0)+(h.gzhead.name?8:0)+(h.gzhead.comment?16:0)),i(h,255&h.gzhead.time),i(h,h.gzhead.time>>8&255),i(h,h.gzhead.time>>16&255),i(h,h.gzhead.time>>24&255),i(h,9===h.level?2:h.strategy>=T||h.level<2?4:0),i(h,255&h.gzhead.os),h.gzhead.extra&&h.gzhead.extra.length&&(i(h,255&h.gzhead.extra.length),i(h,h.gzhead.extra.length>>8&255)),h.gzhead.hcrc&&(a.adler=F(a.adler,h.pending_buf,h.pending,0)),h.gzindex=0,h.status=mb):(i(h,0),i(h,0),i(h,0),i(h,0),i(h,0),i(h,9===h.level?2:h.strategy>=T||h.level<2?4:0),i(h,wb),h.status=qb);else{var m=Y+(h.w_bits-8<<4)<<8,n=-1;n=h.strategy>=T||h.level<2?0:h.level<6?1:6===h.level?2:3,m|=n<<6,0!==h.strstart&&(m|=kb),m+=31-m%31,h.status=qb,j(h,m),0!==h.strstart&&(j(h,a.adler>>>16),j(h,65535&a.adler)),a.adler=1}if(h.status===mb)if(h.gzhead.extra){for(k=h.pending;h.gzindex<(65535&h.gzhead.extra.length)&&(h.pending!==h.pending_buf_size||(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending!==h.pending_buf_size));)i(h,255&h.gzhead.extra[h.gzindex]),h.gzindex++;h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),h.gzindex===h.gzhead.extra.length&&(h.gzindex=0,h.status=nb)}else h.status=nb;if(h.status===nb)if(h.gzhead.name){k=h.pending;do{if(h.pending===h.pending_buf_size&&(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending===h.pending_buf_size)){l=1;break}l=h.gzindexk&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),0===l&&(h.gzindex=0,h.status=ob)}else h.status=ob;if(h.status===ob)if(h.gzhead.comment){k=h.pending;do{if(h.pending===h.pending_buf_size&&(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending===h.pending_buf_size)){l=1;break}l=h.gzindexk&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),0===l&&(h.status=pb)}else h.status=pb;if(h.status===pb&&(h.gzhead.hcrc?(h.pending+2>h.pending_buf_size&&g(a),h.pending+2<=h.pending_buf_size&&(i(h,255&a.adler),i(h,a.adler>>8&255),a.adler=0,h.status=qb)):h.status=qb),0!==h.pending){if(g(a),0===a.avail_out)return h.last_flush=-1,M}else if(0===a.avail_in&&e(b)<=e(c)&&b!==K)return d(a,Q);if(h.status===rb&&0!==a.avail_in)return d(a,Q);if(0!==a.avail_in||0!==h.lookahead||b!==H&&h.status!==rb){var o=h.strategy===T?r(h,b):h.strategy===U?q(h,b):B[h.level].func(h,b);if((o===ub||o===vb)&&(h.status=rb),o===sb||o===ub)return 0===a.avail_out&&(h.last_flush=-1),M;if(o===tb&&(b===I?D._tr_align(h):b!==L&&(D._tr_stored_block(h,0,0,!1),b===J&&(f(h.head),0===h.lookahead&&(h.strstart=0,h.block_start=0,h.insert=0))),g(a),0===a.avail_out))return h.last_flush=-1,M}return b!==K?M:h.wrap<=0?N:(2===h.wrap?(i(h,255&a.adler),i(h,a.adler>>8&255),i(h,a.adler>>16&255),i(h,a.adler>>24&255),i(h,255&a.total_in),i(h,a.total_in>>8&255),i(h,a.total_in>>16&255),i(h,a.total_in>>24&255)):(j(h,a.adler>>>16),j(h,65535&a.adler)),g(a),h.wrap>0&&(h.wrap=-h.wrap),0!==h.pending?M:N)}function A(a){var b;return a&&a.state?(b=a.state.status,b!==lb&&b!==mb&&b!==nb&&b!==ob&&b!==pb&&b!==qb&&b!==rb?d(a,O):(a.state=null,b===qb?d(a,P):M)):O}var B,C=a("../utils/common"),D=a("./trees"),E=a("./adler32"),F=a("./crc32"),G=a("./messages"),H=0,I=1,J=3,K=4,L=5,M=0,N=1,O=-2,P=-3,Q=-5,R=-1,S=1,T=2,U=3,V=4,W=0,X=2,Y=8,Z=9,$=15,_=8,ab=29,bb=256,cb=bb+1+ab,db=30,eb=19,fb=2*cb+1,gb=15,hb=3,ib=258,jb=ib+hb+1,kb=32,lb=42,mb=69,nb=73,ob=91,pb=103,qb=113,rb=666,sb=1,tb=2,ub=3,vb=4,wb=3,xb=function(a,b,c,d,e){this.good_length=a,this.max_lazy=b,this.nice_length=c,this.max_chain=d,this.func=e};B=[new xb(0,0,0,0,n),new xb(4,4,8,4,o),new xb(4,5,16,8,o),new xb(4,6,32,32,o),new xb(4,4,16,16,p),new xb(8,16,32,32,p),new xb(8,16,128,128,p),new xb(8,32,128,256,p),new xb(32,128,258,1024,p),new xb(32,258,258,4096,p)],c.deflateInit=y,c.deflateInit2=x,c.deflateReset=v,c.deflateResetKeep=u,c.deflateSetHeader=w,c.deflate=z,c.deflateEnd=A,c.deflateInfo="pako deflate (from Nodeca project)"},{"../utils/common":27,"./adler32":29,"./crc32":31,"./messages":37,"./trees":38}],33:[function(a,b){"use strict";function c(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1}b.exports=c},{}],34:[function(a,b){"use strict";var c=30,d=12;b.exports=function(a,b){var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C;e=a.state,f=a.next_in,B=a.input,g=f+(a.avail_in-5),h=a.next_out,C=a.output,i=h-(b-a.avail_out),j=h+(a.avail_out-257),k=e.dmax,l=e.wsize,m=e.whave,n=e.wnext,o=e.window,p=e.hold,q=e.bits,r=e.lencode,s=e.distcode,t=(1<q&&(p+=B[f++]<>>24,p>>>=w,q-=w,w=v>>>16&255,0===w)C[h++]=65535&v;else{if(!(16&w)){if(0===(64&w)){v=r[(65535&v)+(p&(1<q&&(p+=B[f++]<>>=w,q-=w),15>q&&(p+=B[f++]<>>24,p>>>=w,q-=w,w=v>>>16&255,!(16&w)){if(0===(64&w)){v=s[(65535&v)+(p&(1<q&&(p+=B[f++]<q&&(p+=B[f++]<k){a.msg="invalid distance too far back",e.mode=c;break a}if(p>>>=w,q-=w,w=h-i,y>w){if(w=y-w,w>m&&e.sane){a.msg="invalid distance too far back",e.mode=c;break a}if(z=0,A=o,0===n){if(z+=l-w,x>w){x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}}else if(w>n){if(z+=l+n-w,w-=n,x>w){x-=w;do C[h++]=o[z++];while(--w);if(z=0,x>n){w=n,x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}}}else if(z+=n-w,x>w){x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}for(;x>2;)C[h++]=A[z++],C[h++]=A[z++],C[h++]=A[z++],x-=3;x&&(C[h++]=A[z++],x>1&&(C[h++]=A[z++]))}else{z=h-y;do C[h++]=C[z++],C[h++]=C[z++],C[h++]=C[z++],x-=3;while(x>2);x&&(C[h++]=C[z++],x>1&&(C[h++]=C[z++]))}break}}break}}while(g>f&&j>h);x=q>>3,f-=x,q-=x<<3,p&=(1<f?5+(g-f):5-(f-g),a.avail_out=j>h?257+(j-h):257-(h-j),e.hold=p,e.bits=q}},{}],35:[function(a,b,c){"use strict";function d(a){return(a>>>24&255)+(a>>>8&65280)+((65280&a)<<8)+((255&a)<<24)}function e(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new r.Buf16(320),this.work=new r.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function f(a){var b;return a&&a.state?(b=a.state,a.total_in=a.total_out=b.total=0,a.msg="",b.wrap&&(a.adler=1&b.wrap),b.mode=K,b.last=0,b.havedict=0,b.dmax=32768,b.head=null,b.hold=0,b.bits=0,b.lencode=b.lendyn=new r.Buf32(ob),b.distcode=b.distdyn=new r.Buf32(pb),b.sane=1,b.back=-1,C):F}function g(a){var b;return a&&a.state?(b=a.state,b.wsize=0,b.whave=0,b.wnext=0,f(a)):F}function h(a,b){var c,d;return a&&a.state?(d=a.state,0>b?(c=0,b=-b):(c=(b>>4)+1,48>b&&(b&=15)),b&&(8>b||b>15)?F:(null!==d.window&&d.wbits!==b&&(d.window=null),d.wrap=c,d.wbits=b,g(a))):F}function i(a,b){var c,d;return a?(d=new e,a.state=d,d.window=null,c=h(a,b),c!==C&&(a.state=null),c):F}function j(a){return i(a,rb)}function k(a){if(sb){var b;for(p=new r.Buf32(512),q=new r.Buf32(32),b=0;144>b;)a.lens[b++]=8;for(;256>b;)a.lens[b++]=9;for(;280>b;)a.lens[b++]=7;for(;288>b;)a.lens[b++]=8;for(v(x,a.lens,0,288,p,0,a.work,{bits:9}),b=0;32>b;)a.lens[b++]=5;v(y,a.lens,0,32,q,0,a.work,{bits:5}),sb=!1}a.lencode=p,a.lenbits=9,a.distcode=q,a.distbits=5}function l(a,b,c,d){var e,f=a.state;return null===f.window&&(f.wsize=1<=f.wsize?(r.arraySet(f.window,b,c-f.wsize,f.wsize,0),f.wnext=0,f.whave=f.wsize):(e=f.wsize-f.wnext,e>d&&(e=d),r.arraySet(f.window,b,c-d,e,f.wnext),d-=e,d?(r.arraySet(f.window,b,c-d,d,0),f.wnext=d,f.whave=f.wsize):(f.wnext+=e,f.wnext===f.wsize&&(f.wnext=0),f.whaven;){if(0===i)break a;i--,m+=e[g++]<>>8&255,c.check=t(c.check,Bb,2,0),m=0,n=0,c.mode=L;break}if(c.flags=0,c.head&&(c.head.done=!1),!(1&c.wrap)||(((255&m)<<8)+(m>>8))%31){a.msg="incorrect header check",c.mode=lb;break}if((15&m)!==J){a.msg="unknown compression method",c.mode=lb;break}if(m>>>=4,n-=4,wb=(15&m)+8,0===c.wbits)c.wbits=wb;else if(wb>c.wbits){a.msg="invalid window size",c.mode=lb;break}c.dmax=1<n;){if(0===i)break a;i--,m+=e[g++]<>8&1),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0,c.mode=M;case M:for(;32>n;){if(0===i)break a;i--,m+=e[g++]<>>8&255,Bb[2]=m>>>16&255,Bb[3]=m>>>24&255,c.check=t(c.check,Bb,4,0)),m=0,n=0,c.mode=N;case N:for(;16>n;){if(0===i)break a;i--,m+=e[g++]<>8),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0,c.mode=O;case O:if(1024&c.flags){for(;16>n;){if(0===i)break a;i--,m+=e[g++]<>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0}else c.head&&(c.head.extra=null);c.mode=P;case P:if(1024&c.flags&&(q=c.length,q>i&&(q=i),q&&(c.head&&(wb=c.head.extra_len-c.length,c.head.extra||(c.head.extra=new Array(c.head.extra_len)),r.arraySet(c.head.extra,e,g,q,wb)),512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,c.length-=q),c.length))break a;c.length=0,c.mode=Q;case Q:if(2048&c.flags){if(0===i)break a;q=0;do wb=e[g+q++],c.head&&wb&&c.length<65536&&(c.head.name+=String.fromCharCode(wb));while(wb&&i>q);if(512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,wb)break a}else c.head&&(c.head.name=null);c.length=0,c.mode=R;case R:if(4096&c.flags){if(0===i)break a;q=0;do wb=e[g+q++],c.head&&wb&&c.length<65536&&(c.head.comment+=String.fromCharCode(wb));while(wb&&i>q);if(512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,wb)break a}else c.head&&(c.head.comment=null);c.mode=S;case S:if(512&c.flags){for(;16>n;){if(0===i)break a;i--,m+=e[g++]<>9&1,c.head.done=!0),a.adler=c.check=0,c.mode=V;break;case T:for(;32>n;){if(0===i)break a;i--,m+=e[g++]<>>=7&n,n-=7&n,c.mode=ib;break}for(;3>n;){if(0===i)break a;i--,m+=e[g++]<>>=1,n-=1,3&m){case 0:c.mode=X;break;case 1:if(k(c),c.mode=bb,b===B){m>>>=2,n-=2;break a}break;case 2:c.mode=$;break;case 3:a.msg="invalid block type",c.mode=lb}m>>>=2,n-=2;break;case X:for(m>>>=7&n,n-=7&n;32>n;){if(0===i)break a;i--,m+=e[g++]<>>16^65535)){a.msg="invalid stored block lengths",c.mode=lb;break}if(c.length=65535&m,m=0,n=0,c.mode=Y,b===B)break a;case Y:c.mode=Z;case Z:if(q=c.length){if(q>i&&(q=i),q>j&&(q=j),0===q)break a;r.arraySet(f,e,g,q,h),i-=q,g+=q,j-=q,h+=q,c.length-=q;break}c.mode=V;break;case $:for(;14>n;){if(0===i)break a;i--,m+=e[g++]<>>=5,n-=5,c.ndist=(31&m)+1,m>>>=5,n-=5,c.ncode=(15&m)+4,m>>>=4,n-=4,c.nlen>286||c.ndist>30){a.msg="too many length or distance symbols",c.mode=lb;break}c.have=0,c.mode=_;case _:for(;c.haven;){if(0===i)break a;i--,m+=e[g++]<>>=3,n-=3}for(;c.have<19;)c.lens[Cb[c.have++]]=0;if(c.lencode=c.lendyn,c.lenbits=7,yb={bits:c.lenbits},xb=v(w,c.lens,0,19,c.lencode,0,c.work,yb),c.lenbits=yb.bits,xb){a.msg="invalid code lengths set",c.mode=lb;break}c.have=0,c.mode=ab;case ab:for(;c.have>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<sb)m>>>=qb,n-=qb,c.lens[c.have++]=sb;else{if(16===sb){for(zb=qb+2;zb>n;){if(0===i)break a;i--,m+=e[g++]<>>=qb,n-=qb,0===c.have){a.msg="invalid bit length repeat",c.mode=lb;break}wb=c.lens[c.have-1],q=3+(3&m),m>>>=2,n-=2}else if(17===sb){for(zb=qb+3;zb>n;){if(0===i)break a;i--,m+=e[g++]<>>=qb,n-=qb,wb=0,q=3+(7&m),m>>>=3,n-=3}else{for(zb=qb+7;zb>n;){if(0===i)break a;i--,m+=e[g++]<>>=qb,n-=qb,wb=0,q=11+(127&m),m>>>=7,n-=7}if(c.have+q>c.nlen+c.ndist){a.msg="invalid bit length repeat",c.mode=lb;break}for(;q--;)c.lens[c.have++]=wb}}if(c.mode===lb)break;if(0===c.lens[256]){a.msg="invalid code -- missing end-of-block",c.mode=lb;break}if(c.lenbits=9,yb={bits:c.lenbits},xb=v(x,c.lens,0,c.nlen,c.lencode,0,c.work,yb),c.lenbits=yb.bits,xb){a.msg="invalid literal/lengths set",c.mode=lb;break}if(c.distbits=6,c.distcode=c.distdyn,yb={bits:c.distbits},xb=v(y,c.lens,c.nlen,c.ndist,c.distcode,0,c.work,yb),c.distbits=yb.bits,xb){a.msg="invalid distances set",c.mode=lb;break}if(c.mode=bb,b===B)break a;case bb:c.mode=cb;case cb:if(i>=6&&j>=258){a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,u(a,p),h=a.next_out,f=a.output,j=a.avail_out,g=a.next_in,e=a.input,i=a.avail_in,m=c.hold,n=c.bits,c.mode===V&&(c.back=-1);break}for(c.back=0;Ab=c.lencode[m&(1<>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<>tb)],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=tb+qb);){if(0===i)break a;i--,m+=e[g++]<>>=tb,n-=tb,c.back+=tb}if(m>>>=qb,n-=qb,c.back+=qb,c.length=sb,0===rb){c.mode=hb;break}if(32&rb){c.back=-1,c.mode=V;break}if(64&rb){a.msg="invalid literal/length code",c.mode=lb;break}c.extra=15&rb,c.mode=db;case db:if(c.extra){for(zb=c.extra;zb>n;){if(0===i)break a;i--,m+=e[g++]<>>=c.extra,n-=c.extra,c.back+=c.extra}c.was=c.length,c.mode=eb;case eb:for(;Ab=c.distcode[m&(1<>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<>tb)],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=tb+qb);){if(0===i)break a;i--,m+=e[g++]<>>=tb,n-=tb,c.back+=tb}if(m>>>=qb,n-=qb,c.back+=qb,64&rb){a.msg="invalid distance code",c.mode=lb;break}c.offset=sb,c.extra=15&rb,c.mode=fb;case fb:if(c.extra){for(zb=c.extra;zb>n;){if(0===i)break a;i--,m+=e[g++]<>>=c.extra,n-=c.extra,c.back+=c.extra}if(c.offset>c.dmax){a.msg="invalid distance too far back",c.mode=lb;break}c.mode=gb;case gb:if(0===j)break a; +if(q=p-j,c.offset>q){if(q=c.offset-q,q>c.whave&&c.sane){a.msg="invalid distance too far back",c.mode=lb;break}q>c.wnext?(q-=c.wnext,ob=c.wsize-q):ob=c.wnext-q,q>c.length&&(q=c.length),pb=c.window}else pb=f,ob=h-c.offset,q=c.length;q>j&&(q=j),j-=q,c.length-=q;do f[h++]=pb[ob++];while(--q);0===c.length&&(c.mode=cb);break;case hb:if(0===j)break a;f[h++]=c.length,j--,c.mode=cb;break;case ib:if(c.wrap){for(;32>n;){if(0===i)break a;i--,m|=e[g++]<n;){if(0===i)break a;i--,m+=e[g++]<=D;D++)P[D]=0;for(E=0;o>E;E++)P[b[n+E]]++;for(H=C,G=d;G>=1&&0===P[G];G--);if(H>G&&(H=G),0===G)return p[q++]=20971520,p[q++]=20971520,s.bits=1,0;for(F=1;G>F&&0===P[F];F++);for(F>H&&(H=F),K=1,D=1;d>=D;D++)if(K<<=1,K-=P[D],0>K)return-1;if(K>0&&(a===g||1!==G))return-1;for(Q[1]=0,D=1;d>D;D++)Q[D+1]=Q[D]+P[D];for(E=0;o>E;E++)0!==b[n+E]&&(r[Q[b[n+E]]++]=E);if(a===g?(N=R=r,y=19):a===h?(N=j,O-=257,R=k,S-=257,y=256):(N=l,R=m,y=-1),M=0,E=0,D=F,x=q,I=H,J=0,v=-1,L=1<e||a===i&&L>f)return 1;for(var T=0;;){T++,z=D-J,r[E]y?(A=R[S+r[E]],B=N[O+r[E]]):(A=96,B=0),t=1<>J)+u]=z<<24|A<<16|B|0;while(0!==u);for(t=1<>=1;if(0!==t?(M&=t-1,M+=t):M=0,E++,0===--P[D]){if(D===G)break;D=b[n+r[E]]}if(D>H&&(M&w)!==v){for(0===J&&(J=H),x+=F,I=D-J,K=1<I+J&&(K-=P[I+J],!(0>=K));)I++,K<<=1;if(L+=1<e||a===i&&L>f)return 1;v=M&w,p[v]=H<<24|I<<16|x-q|0}}return 0!==M&&(p[x+M]=D-J<<24|64<<16|0),s.bits=H,0}},{"../utils/common":27}],37:[function(a,b){"use strict";b.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],38:[function(a,b,c){"use strict";function d(a){for(var b=a.length;--b>=0;)a[b]=0}function e(a){return 256>a?gb[a]:gb[256+(a>>>7)]}function f(a,b){a.pending_buf[a.pending++]=255&b,a.pending_buf[a.pending++]=b>>>8&255}function g(a,b,c){a.bi_valid>V-c?(a.bi_buf|=b<>V-a.bi_valid,a.bi_valid+=c-V):(a.bi_buf|=b<>>=1,c<<=1;while(--b>0);return c>>>1}function j(a){16===a.bi_valid?(f(a,a.bi_buf),a.bi_buf=0,a.bi_valid=0):a.bi_valid>=8&&(a.pending_buf[a.pending++]=255&a.bi_buf,a.bi_buf>>=8,a.bi_valid-=8)}function k(a,b){var c,d,e,f,g,h,i=b.dyn_tree,j=b.max_code,k=b.stat_desc.static_tree,l=b.stat_desc.has_stree,m=b.stat_desc.extra_bits,n=b.stat_desc.extra_base,o=b.stat_desc.max_length,p=0;for(f=0;U>=f;f++)a.bl_count[f]=0;for(i[2*a.heap[a.heap_max]+1]=0,c=a.heap_max+1;T>c;c++)d=a.heap[c],f=i[2*i[2*d+1]+1]+1,f>o&&(f=o,p++),i[2*d+1]=f,d>j||(a.bl_count[f]++,g=0,d>=n&&(g=m[d-n]),h=i[2*d],a.opt_len+=h*(f+g),l&&(a.static_len+=h*(k[2*d+1]+g)));if(0!==p){do{for(f=o-1;0===a.bl_count[f];)f--;a.bl_count[f]--,a.bl_count[f+1]+=2,a.bl_count[o]--,p-=2}while(p>0);for(f=o;0!==f;f--)for(d=a.bl_count[f];0!==d;)e=a.heap[--c],e>j||(i[2*e+1]!==f&&(a.opt_len+=(f-i[2*e+1])*i[2*e],i[2*e+1]=f),d--)}}function l(a,b,c){var d,e,f=new Array(U+1),g=0;for(d=1;U>=d;d++)f[d]=g=g+c[d-1]<<1;for(e=0;b>=e;e++){var h=a[2*e+1];0!==h&&(a[2*e]=i(f[h]++,h))}}function m(){var a,b,c,d,e,f=new Array(U+1);for(c=0,d=0;O-1>d;d++)for(ib[d]=c,a=0;a<1<<_[d];a++)hb[c++]=d;for(hb[c-1]=d,e=0,d=0;16>d;d++)for(jb[d]=e,a=0;a<1<>=7;R>d;d++)for(jb[d]=e<<7,a=0;a<1<=b;b++)f[b]=0;for(a=0;143>=a;)eb[2*a+1]=8,a++,f[8]++;for(;255>=a;)eb[2*a+1]=9,a++,f[9]++;for(;279>=a;)eb[2*a+1]=7,a++,f[7]++;for(;287>=a;)eb[2*a+1]=8,a++,f[8]++;for(l(eb,Q+1,f),a=0;R>a;a++)fb[2*a+1]=5,fb[2*a]=i(a,5);kb=new nb(eb,_,P+1,Q,U),lb=new nb(fb,ab,0,R,U),mb=new nb(new Array(0),bb,0,S,W)}function n(a){var b;for(b=0;Q>b;b++)a.dyn_ltree[2*b]=0;for(b=0;R>b;b++)a.dyn_dtree[2*b]=0;for(b=0;S>b;b++)a.bl_tree[2*b]=0;a.dyn_ltree[2*X]=1,a.opt_len=a.static_len=0,a.last_lit=a.matches=0}function o(a){a.bi_valid>8?f(a,a.bi_buf):a.bi_valid>0&&(a.pending_buf[a.pending++]=a.bi_buf),a.bi_buf=0,a.bi_valid=0}function p(a,b,c,d){o(a),d&&(f(a,c),f(a,~c)),E.arraySet(a.pending_buf,a.window,b,c,a.pending),a.pending+=c}function q(a,b,c,d){var e=2*b,f=2*c;return a[e]c;c++)0!==f[2*c]?(a.heap[++a.heap_len]=j=c,a.depth[c]=0):f[2*c+1]=0;for(;a.heap_len<2;)e=a.heap[++a.heap_len]=2>j?++j:0,f[2*e]=1,a.depth[e]=0,a.opt_len--,h&&(a.static_len-=g[2*e+1]);for(b.max_code=j,c=a.heap_len>>1;c>=1;c--)r(a,f,c);e=i;do c=a.heap[1],a.heap[1]=a.heap[a.heap_len--],r(a,f,1),d=a.heap[1],a.heap[--a.heap_max]=c,a.heap[--a.heap_max]=d,f[2*e]=f[2*c]+f[2*d],a.depth[e]=(a.depth[c]>=a.depth[d]?a.depth[c]:a.depth[d])+1,f[2*c+1]=f[2*d+1]=e,a.heap[1]=e++,r(a,f,1);while(a.heap_len>=2);a.heap[--a.heap_max]=a.heap[1],k(a,b),l(f,j,a.bl_count)}function u(a,b,c){var d,e,f=-1,g=b[1],h=0,i=7,j=4;for(0===g&&(i=138,j=3),b[2*(c+1)+1]=65535,d=0;c>=d;d++)e=g,g=b[2*(d+1)+1],++hh?a.bl_tree[2*e]+=h:0!==e?(e!==f&&a.bl_tree[2*e]++,a.bl_tree[2*Y]++):10>=h?a.bl_tree[2*Z]++:a.bl_tree[2*$]++,h=0,f=e,0===g?(i=138,j=3):e===g?(i=6,j=3):(i=7,j=4))}function v(a,b,c){var d,e,f=-1,i=b[1],j=0,k=7,l=4;for(0===i&&(k=138,l=3),d=0;c>=d;d++)if(e=i,i=b[2*(d+1)+1],!(++jj){do h(a,e,a.bl_tree);while(0!==--j)}else 0!==e?(e!==f&&(h(a,e,a.bl_tree),j--),h(a,Y,a.bl_tree),g(a,j-3,2)):10>=j?(h(a,Z,a.bl_tree),g(a,j-3,3)):(h(a,$,a.bl_tree),g(a,j-11,7));j=0,f=e,0===i?(k=138,l=3):e===i?(k=6,l=3):(k=7,l=4)}}function w(a){var b;for(u(a,a.dyn_ltree,a.l_desc.max_code),u(a,a.dyn_dtree,a.d_desc.max_code),t(a,a.bl_desc),b=S-1;b>=3&&0===a.bl_tree[2*cb[b]+1];b--);return a.opt_len+=3*(b+1)+5+5+4,b}function x(a,b,c,d){var e;for(g(a,b-257,5),g(a,c-1,5),g(a,d-4,4),e=0;d>e;e++)g(a,a.bl_tree[2*cb[e]+1],3);v(a,a.dyn_ltree,b-1),v(a,a.dyn_dtree,c-1)}function y(a){var b,c=4093624447;for(b=0;31>=b;b++,c>>>=1)if(1&c&&0!==a.dyn_ltree[2*b])return G;if(0!==a.dyn_ltree[18]||0!==a.dyn_ltree[20]||0!==a.dyn_ltree[26])return H;for(b=32;P>b;b++)if(0!==a.dyn_ltree[2*b])return H;return G}function z(a){pb||(m(),pb=!0),a.l_desc=new ob(a.dyn_ltree,kb),a.d_desc=new ob(a.dyn_dtree,lb),a.bl_desc=new ob(a.bl_tree,mb),a.bi_buf=0,a.bi_valid=0,n(a)}function A(a,b,c,d){g(a,(J<<1)+(d?1:0),3),p(a,b,c,!0)}function B(a){g(a,K<<1,3),h(a,X,eb),j(a)}function C(a,b,c,d){var e,f,h=0;a.level>0?(a.strm.data_type===I&&(a.strm.data_type=y(a)),t(a,a.l_desc),t(a,a.d_desc),h=w(a),e=a.opt_len+3+7>>>3,f=a.static_len+3+7>>>3,e>=f&&(e=f)):e=f=c+5,e>=c+4&&-1!==b?A(a,b,c,d):a.strategy===F||f===e?(g(a,(K<<1)+(d?1:0),3),s(a,eb,fb)):(g(a,(L<<1)+(d?1:0),3),x(a,a.l_desc.max_code+1,a.d_desc.max_code+1,h+1),s(a,a.dyn_ltree,a.dyn_dtree)),n(a),d&&o(a)}function D(a,b,c){return a.pending_buf[a.d_buf+2*a.last_lit]=b>>>8&255,a.pending_buf[a.d_buf+2*a.last_lit+1]=255&b,a.pending_buf[a.l_buf+a.last_lit]=255&c,a.last_lit++,0===b?a.dyn_ltree[2*c]++:(a.matches++,b--,a.dyn_ltree[2*(hb[c]+P+1)]++,a.dyn_dtree[2*e(b)]++),a.last_lit===a.lit_bufsize-1}var E=a("../utils/common"),F=4,G=0,H=1,I=2,J=0,K=1,L=2,M=3,N=258,O=29,P=256,Q=P+1+O,R=30,S=19,T=2*Q+1,U=15,V=16,W=7,X=256,Y=16,Z=17,$=18,_=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],ab=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],bb=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],cb=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],db=512,eb=new Array(2*(Q+2));d(eb);var fb=new Array(2*R);d(fb);var gb=new Array(db);d(gb);var hb=new Array(N-M+1);d(hb);var ib=new Array(O);d(ib);var jb=new Array(R);d(jb);var kb,lb,mb,nb=function(a,b,c,d,e){this.static_tree=a,this.extra_bits=b,this.extra_base=c,this.elems=d,this.max_length=e,this.has_stree=a&&a.length},ob=function(a,b){this.dyn_tree=a,this.max_code=0,this.stat_desc=b},pb=!1;c._tr_init=z,c._tr_stored_block=A,c._tr_flush_block=C,c._tr_tally=D,c._tr_align=B},{"../utils/common":27}],39:[function(a,b){"use strict";function c(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}b.exports=c},{}]},{},[9])(9)}); \ No newline at end of file diff --git a/node_modules/three/examples/js/libs/msgpack-js.js b/node_modules/three/examples/js/libs/msgpack-js.js new file mode 100644 index 00000000..243d8e4a --- /dev/null +++ b/node_modules/three/examples/js/libs/msgpack-js.js @@ -0,0 +1,600 @@ +( // Module boilerplate to support browser globals and browserify and AMD. + typeof define === "function" ? function (m) { define("msgpack-js", m); } : + typeof exports === "object" ? function (m) { module.exports = m(); } : + function(m){ this.msgpack = m(); } +)(function () { +"use strict"; + +var exports = {}; + +exports.inspect = inspect; +function inspect(buffer) { + if (buffer === undefined) return "undefined"; + var view; + var type; + if (buffer instanceof ArrayBuffer) { + type = "ArrayBuffer"; + view = new DataView(buffer); + } + else if (buffer instanceof DataView) { + type = "DataView"; + view = buffer; + } + if (!view) return JSON.stringify(buffer); + var bytes = []; + for (var i = 0; i < buffer.byteLength; i++) { + if (i > 20) { + bytes.push("..."); + break; + } + var byte = view.getUint8(i).toString(16); + if (byte.length === 1) byte = "0" + byte; + bytes.push(byte); + } + return "<" + type + " " + bytes.join(" ") + ">"; +} + +// Encode string as utf8 into dataview at offset +exports.utf8Write = utf8Write; +function utf8Write(view, offset, string) { + var byteLength = view.byteLength; + for(var i = 0, l = string.length; i < l; i++) { + var codePoint = string.charCodeAt(i); + + // One byte of UTF-8 + if (codePoint < 0x80) { + view.setUint8(offset++, codePoint >>> 0 & 0x7f | 0x00); + continue; + } + + // Two bytes of UTF-8 + if (codePoint < 0x800) { + view.setUint8(offset++, codePoint >>> 6 & 0x1f | 0xc0); + view.setUint8(offset++, codePoint >>> 0 & 0x3f | 0x80); + continue; + } + + // Three bytes of UTF-8. + if (codePoint < 0x10000) { + view.setUint8(offset++, codePoint >>> 12 & 0x0f | 0xe0); + view.setUint8(offset++, codePoint >>> 6 & 0x3f | 0x80); + view.setUint8(offset++, codePoint >>> 0 & 0x3f | 0x80); + continue; + } + + // Four bytes of UTF-8 + if (codePoint < 0x110000) { + view.setUint8(offset++, codePoint >>> 18 & 0x07 | 0xf0); + view.setUint8(offset++, codePoint >>> 12 & 0x3f | 0x80); + view.setUint8(offset++, codePoint >>> 6 & 0x3f | 0x80); + view.setUint8(offset++, codePoint >>> 0 & 0x3f | 0x80); + continue; + } + throw new Error("bad codepoint " + codePoint); + } +} + +exports.utf8Read = utf8Read; +function utf8Read(view, offset, length) { + var string = ""; + for (var i = offset, end = offset + length; i < end; i++) { + var byte = view.getUint8(i); + // One byte character + if ((byte & 0x80) === 0x00) { + string += String.fromCharCode(byte); + continue; + } + // Two byte character + if ((byte & 0xe0) === 0xc0) { + string += String.fromCharCode( + ((byte & 0x0f) << 6) | + (view.getUint8(++i) & 0x3f) + ); + continue; + } + // Three byte character + if ((byte & 0xf0) === 0xe0) { + string += String.fromCharCode( + ((byte & 0x0f) << 12) | + ((view.getUint8(++i) & 0x3f) << 6) | + ((view.getUint8(++i) & 0x3f) << 0) + ); + continue; + } + // Four byte character + if ((byte & 0xf8) === 0xf0) { + string += String.fromCharCode( + ((byte & 0x07) << 18) | + ((view.getUint8(++i) & 0x3f) << 12) | + ((view.getUint8(++i) & 0x3f) << 6) | + ((view.getUint8(++i) & 0x3f) << 0) + ); + continue; + } + throw new Error("Invalid byte " + byte.toString(16)); + } + return string; +} + +exports.utf8ByteCount = utf8ByteCount; +function utf8ByteCount(string) { + var count = 0; + for(var i = 0, l = string.length; i < l; i++) { + var codePoint = string.charCodeAt(i); + if (codePoint < 0x80) { + count += 1; + continue; + } + if (codePoint < 0x800) { + count += 2; + continue; + } + if (codePoint < 0x10000) { + count += 3; + continue; + } + if (codePoint < 0x110000) { + count += 4; + continue; + } + throw new Error("bad codepoint " + codePoint); + } + return count; +} + +exports.encode = function (value) { + var buffer = new ArrayBuffer(sizeof(value)); + var view = new DataView(buffer); + encode(value, view, 0); + return buffer; +}; + +exports.decode = decode; + +// http://wiki.msgpack.org/display/MSGPACK/Format+specification +// I've extended the protocol to have two new types that were previously reserved. +// buffer 16 11011000 0xd8 +// buffer 32 11011001 0xd9 +// These work just like raw16 and raw32 except they are node buffers instead of strings. +// +// Also I've added a type for `undefined` +// undefined 11000100 0xc4 + +function Decoder(view, offset) { + this.offset = offset || 0; + this.view = view; +} +Decoder.prototype.map = function (length) { + var value = {}; + for (var i = 0; i < length; i++) { + var key = this.parse(); + value[key] = this.parse(); + } + return value; +}; +Decoder.prototype.buf = function (length) { + var value = new ArrayBuffer(length); + (new Uint8Array(value)).set(new Uint8Array(this.view.buffer, this.offset, length), 0); + this.offset += length; + return value; +}; +Decoder.prototype.raw = function (length) { + var value = utf8Read(this.view, this.offset, length); + this.offset += length; + return value; +}; +Decoder.prototype.array = function (length) { + var value = new Array(length); + for (var i = 0; i < length; i++) { + value[i] = this.parse(); + } + return value; +}; +Decoder.prototype.parse = function () { + var type = this.view.getUint8(this.offset); + var value, length; + // FixRaw + if ((type & 0xe0) === 0xa0) { + length = type & 0x1f; + this.offset++; + return this.raw(length); + } + // FixMap + if ((type & 0xf0) === 0x80) { + length = type & 0x0f; + this.offset++; + return this.map(length); + } + // FixArray + if ((type & 0xf0) === 0x90) { + length = type & 0x0f; + this.offset++; + return this.array(length); + } + // Positive FixNum + if ((type & 0x80) === 0x00) { + this.offset++; + return type; + } + // Negative Fixnum + if ((type & 0xe0) === 0xe0) { + value = this.view.getInt8(this.offset); + this.offset++; + return value; + } + switch (type) { + // raw 16 + case 0xda: + length = this.view.getUint16(this.offset + 1); + this.offset += 3; + return this.raw(length); + // raw 32 + case 0xdb: + length = this.view.getUint32(this.offset + 1); + this.offset += 5; + return this.raw(length); + // nil + case 0xc0: + this.offset++; + return null; + // false + case 0xc2: + this.offset++; + return false; + // true + case 0xc3: + this.offset++; + return true; + // undefined + case 0xc4: + this.offset++; + return undefined; + // uint8 + case 0xcc: + value = this.view.getUint8(this.offset + 1); + this.offset += 2; + return value; + // uint 16 + case 0xcd: + value = this.view.getUint16(this.offset + 1); + this.offset += 3; + return value; + // uint 32 + case 0xce: + value = this.view.getUint32(this.offset + 1); + this.offset += 5; + return value; + // int 8 + case 0xd0: + value = this.view.getInt8(this.offset + 1); + this.offset += 2; + return value; + // int 16 + case 0xd1: + value = this.view.getInt16(this.offset + 1); + this.offset += 3; + return value; + // int 32 + case 0xd2: + value = this.view.getInt32(this.offset + 1); + this.offset += 5; + return value; + // map 16 + case 0xde: + length = this.view.getUint16(this.offset + 1); + this.offset += 3; + return this.map(length); + // map 32 + case 0xdf: + length = this.view.getUint32(this.offset + 1); + this.offset += 5; + return this.map(length); + // array 16 + case 0xdc: + length = this.view.getUint16(this.offset + 1); + this.offset += 3; + return this.array(length); + // array 32 + case 0xdd: + length = this.view.getUint32(this.offset + 1); + this.offset += 5; + return this.array(length); + // buffer 16 + case 0xd8: + length = this.view.getUint16(this.offset + 1); + this.offset += 3; + return this.buf(length); + // buffer 32 + case 0xd9: + length = this.view.getUint32(this.offset + 1); + this.offset += 5; + return this.buf(length); + // float + case 0xca: + value = this.view.getFloat32(this.offset + 1); + this.offset += 5; + return value; + // double + case 0xcb: + value = this.view.getFloat64(this.offset + 1); + this.offset += 9; + return value; + } + throw new Error("Unknown type 0x" + type.toString(16)); +}; +function decode(buffer) { + var view = new DataView(buffer); + var decoder = new Decoder(view); + var value = decoder.parse(); + if (decoder.offset !== buffer.byteLength) throw new Error((buffer.byteLength - decoder.offset) + " trailing bytes"); + return value; +} + +function encode(value, view, offset) { + var type = typeof value; + + // Strings Bytes + if (type === "string") { + var length = utf8ByteCount(value); + // fix raw + if (length < 0x20) { + view.setUint8(offset, length | 0xa0); + utf8Write(view, offset + 1, value); + return 1 + length; + } + // raw 16 + if (length < 0x10000) { + view.setUint8(offset, 0xda); + view.setUint16(offset + 1, length); + utf8Write(view, offset + 3, value); + return 3 + length; + } + // raw 32 + if (length < 0x100000000) { + view.setUint8(offset, 0xdb); + view.setUint32(offset + 1, length); + utf8Write(view, offset + 5, value); + return 5 + length; + } + } + + if (value instanceof ArrayBuffer) { + var length = value.byteLength; + // buffer 16 + if (length < 0x10000) { + view.setUint8(offset, 0xd8); + view.setUint16(offset + 1, length); + (new Uint8Array(view.buffer)).set(new Uint8Array(value), offset + 3); + return 3 + length; + } + // buffer 32 + if (length < 0x100000000) { + view.setUint8(offset, 0xd9); + view.setUint32(offset + 1, length); + (new Uint8Array(view.buffer)).set(new Uint8Array(value), offset + 5); + return 5 + length; + } + } + + if (type === "number") { + // Floating Point + if ((value << 0) !== value) { + view.setUint8(offset, 0xcb); + view.setFloat64(offset + 1, value); + return 9; + } + + // Integers + if (value >=0) { + // positive fixnum + if (value < 0x80) { + view.setUint8(offset, value); + return 1; + } + // uint 8 + if (value < 0x100) { + view.setUint8(offset, 0xcc); + view.setUint8(offset + 1, value); + return 2; + } + // uint 16 + if (value < 0x10000) { + view.setUint8(offset, 0xcd); + view.setUint16(offset + 1, value); + return 3; + } + // uint 32 + if (value < 0x100000000) { + view.setUint8(offset, 0xce); + view.setUint32(offset + 1, value); + return 5; + } + throw new Error("Number too big 0x" + value.toString(16)); + } + // negative fixnum + if (value >= -0x20) { + view.setInt8(offset, value); + return 1; + } + // int 8 + if (value >= -0x80) { + view.setUint8(offset, 0xd0); + view.setInt8(offset + 1, value); + return 2; + } + // int 16 + if (value >= -0x8000) { + view.setUint8(offset, 0xd1); + view.setInt16(offset + 1, value); + return 3; + } + // int 32 + if (value >= -0x80000000) { + view.setUint8(offset, 0xd2); + view.setInt32(offset + 1, value); + return 5; + } + throw new Error("Number too small -0x" + (-value).toString(16).substr(1)); + } + + // undefined + if (type === "undefined") { + view.setUint8(offset, 0xc4); + return 1; + } + + // null + if (value === null) { + view.setUint8(offset, 0xc0); + return 1; + } + + // Boolean + if (type === "boolean") { + view.setUint8(offset, value ? 0xc3 : 0xc2); + return 1; + } + + // Container Types + if (type === "object") { + var length, size = 0; + var isArray = Array.isArray(value); + + if (isArray) { + length = value.length; + } + else { + var keys = Object.keys(value); + length = keys.length; + } + + var size; + if (length < 0x10) { + view.setUint8(offset, length | (isArray ? 0x90 : 0x80)); + size = 1; + } + else if (length < 0x10000) { + view.setUint8(offset, isArray ? 0xdc : 0xde); + view.setUint16(offset + 1, length); + size = 3; + } + else if (length < 0x100000000) { + view.setUint8(offset, isArray ? 0xdd : 0xdf); + view.setUint32(offset + 1, length); + size = 5; + } + + if (isArray) { + for (var i = 0; i < length; i++) { + size += encode(value[i], view, offset + size); + } + } + else { + for (var i = 0; i < length; i++) { + var key = keys[i]; + size += encode(key, view, offset + size); + size += encode(value[key], view, offset + size); + } + } + + return size; + } + throw new Error("Unknown type " + type); +} + +function sizeof(value) { + var type = typeof value; + + // Raw Bytes + if (type === "string") { + var length = utf8ByteCount(value); + if (length < 0x20) { + return 1 + length; + } + if (length < 0x10000) { + return 3 + length; + } + if (length < 0x100000000) { + return 5 + length; + } + } + + if (value instanceof ArrayBuffer) { + var length = value.byteLength; + if (length < 0x10000) { + return 3 + length; + } + if (length < 0x100000000) { + return 5 + length; + } + } + + if (type === "number") { + // Floating Point + // double + if (value << 0 !== value) return 9; + + // Integers + if (value >=0) { + // positive fixnum + if (value < 0x80) return 1; + // uint 8 + if (value < 0x100) return 2; + // uint 16 + if (value < 0x10000) return 3; + // uint 32 + if (value < 0x100000000) return 5; + // uint 64 + if (value < 0x10000000000000000) return 9; + throw new Error("Number too big 0x" + value.toString(16)); + } + // negative fixnum + if (value >= -0x20) return 1; + // int 8 + if (value >= -0x80) return 2; + // int 16 + if (value >= -0x8000) return 3; + // int 32 + if (value >= -0x80000000) return 5; + // int 64 + if (value >= -0x8000000000000000) return 9; + throw new Error("Number too small -0x" + value.toString(16).substr(1)); + } + + // Boolean, null, undefined + if (type === "boolean" || type === "undefined" || value === null) return 1; + + // Container Types + if (type === "object") { + var length, size = 0; + if (Array.isArray(value)) { + length = value.length; + for (var i = 0; i < length; i++) { + size += sizeof(value[i]); + } + } + else { + var keys = Object.keys(value); + length = keys.length; + for (var i = 0; i < length; i++) { + var key = keys[i]; + size += sizeof(key) + sizeof(value[key]); + } + } + if (length < 0x10) { + return 1 + size; + } + if (length < 0x10000) { + return 3 + size; + } + if (length < 0x100000000) { + return 5 + size; + } + throw new Error("Array or object too long 0x" + length.toString(16)); + } + throw new Error("Unknown type " + type); +} + +return exports; + +}); diff --git a/node_modules/three/examples/js/libs/pnltri.min.js b/node_modules/three/examples/js/libs/pnltri.min.js new file mode 100644 index 00000000..948b990c --- /dev/null +++ b/node_modules/three/examples/js/libs/pnltri.min.js @@ -0,0 +1,19 @@ +// pnltri.js / raw.github.com/jahting/pnltri.js/master/LICENSE +'use strict';var r={ca:"2.0"};r.c={random:Math.random,da:function(b){for(var c=b.length-1;0r.c.n)return 1;l=b.x-c.x;return lr.c.n?1:0},N:function(b,c,l){return(c.x-b.x)*(l.y-b.y)-(c.y-b.y)*(l.x-b.x)}};r.c.n=Math.pow(2,-43);r.c.D=-r.c.n;function u(b){this.U=[];this.r=[];this.X=[];this.L=0;this.F=[];this.R=[];this.T=[];if(b)for(var c=0,l=b.length;ck.length)console.log("Polygon has < 3 vertices!",k);else{for(var a=void 0,e=void 0,d=void 0,f=0;fr.c.n)return!1;var d;Math.abs(a.y-b.y)b)a=!1;else{a=m.b.s;var d=a.p,b=d?a.a:a.i, +b=M(c,b);0b?a=!1:(a=d?a.m:a.o,b=d?a.i:a.a,b=M(c,b),a=0--e){console.log("ERR add_segment: infinite loop", +m,c,b);return}if(!m.b&&!m.d){console.log("ERR add_segment: missing successors",m,c,b);return}d=m.u;d.h=c;d.A=null;v&&v.s==m.s?(q=m,n=v,n.l=m.l,d.left=new H(q),d.right=v.u):(y&&y.t==m.t?(n=m,q=y,q.l=m.l,d.left=y.u):(q=m,n=P(b,m),d.left=new H(q)),d.right=new H(n));m.e&&m.f?m.C?(m.ba?(n.e=m.f,n.f=m.C,n.e.b=n,n.f.d=n):(q.f=m.e,q.e=m.C,q.e.b=q,q.f.d=q),q.C=n.C=null):m.q==g.q?(n.f.d=n,q.f=n.e=null):n==m?(n.e=n.f,n.f=null,n.e.b=n):(q.f=q.e,q.e=null):l();m.b&&m.d?d=a():(d=m.b?m.b:m.d,k(d));q.s&&(q.s.J=n); +n.t&&(n.t.K=q);q.s=n.t=c;c.J=q;c.K=n;m.l!=t.l?(y=q,v=n,m=d):m=null}c.v=!0}else console.log("ERR add_segment: missing trFirst.uX: ",g)} +function Q(b,c){if(c)var l=b.a,k=b.i,a=b.H;else l=b.i,k=b.a,a=b.I;for(var e;a;)if(a.V)a=-1==r.c.G(l==a.V?k:l,a.V)?a.left:a.right;else if(a.h){if(l==a.h.a||l==a.h.i)if(Math.abs(l.y-k.y)=a.h.i.x:k.x=a.h.a.x)?b.m.p:a.h.o.p)?a.left:a.right:k.x=a.h.i.y:k.y=a.h.a.y)?M(a.h,b.m.i):-M(a.h,a.h.o.a));else e=M(a.h,l),0==e&&(e=M(a.h,k),0==e&&(e=M(a.h,c?b.o.a:b.m.i)));if(0e)a=a.right;else break}else{a.A||console.log("ptNode: unknown type",a);c?b.H=a:b.I=a;break}}function N(b){Q(b,!0);Q(b,!1)}function M(b,c){var l;l=b.a.x-c.x;var k=b.i.x-c.x,a=Math.abs(b.a.y-c.y)h){do d.j=d.m,d=d.g=d.o;while(d!=e);a.F[0]=!1}else{do d.j=d.o,d=d.g=d.m;while(d!=e)}for(e=a=e;a.g!=a.j;){b:{var d=a.j.a.x,f=a.j.a.y,h=a.a.x,g=a.a.y,p=a.g.a.x,t=a.g.a.y,m=p-h,q=t-g,n=d-p,y=f-t,v=h-d,K=g-f;if(r.c.n>v*q-m*K)d=!1;else{for(var Y= +a.j.j,C=a.g;C!=Y;){var C=C.g,z=C.a.x,A=C.a.y,R=z-d,S=A-f;if(0!=R||0!=S){var T=z-h,U=A-g;if(0!=T||0!=U)if(z-=p,A-=t,(0!=z||0!=A)&&m*U-q*T>=r.c.D&&v*S-K*R>=r.c.D&&n*A-y*z>=r.c.D){d=!1;break b}}}d=!0}}if(d)B(k.k,a.j.a,a.a,a.g.a),a.j.g=a.g,a.g.j=a.j,e=a=a.g;else if(a=a.g,a==e){k=!1;break a}}k=!0}}if(!k){k=new W(l);k.S=new V(k.k);e=k.S;a=e.k.r.concat();r.c.da(a);d=0;f=e.k.L;if(1!=f)for(h=Array(f),g=a.concat(),p=0;pthis.depCount&&!this.defined){if(G(c)){if(this.events.error&&this.map.isDefine||h.onError!==da)try{f=i.execCb(b,c,e,f)}catch(d){a=d}else f=i.execCb(b,c,e,f);this.map.isDefine&&void 0===f&&((e=this.module)?f=e.exports:this.usingExports&& +(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=c;this.exports=f;if(this.map.isDefine&&!this.ignore&&(p[b]=f,h.onResourceLoad))h.onResourceLoad(i,this.map,this.depMaps);y(b);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a= +this.map,b=a.id,d=m(a.prefix);this.depMaps.push(d);r(d,"defined",t(this,function(f){var d,g;g=j(ba,this.map.id);var J=this.map.name,u=this.map.parentMap?this.map.parentMap.name:null,p=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(J=f.normalize(J,function(a){return c(a,u,!0)})||""),f=m(a.prefix+"!"+J,this.map.parentMap),r(f,"defined",t(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),g=j(k,f.id)){this.depMaps.push(f); +if(this.events.error)g.on("error",t(this,function(a){this.emit("error",a)}));g.enable()}}else g?(this.map.url=i.nameToUrl(g),this.load()):(d=t(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),d.error=t(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(k,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),d.fromText=t(this,function(f,c){var g=a.name,J=m(g),k=O;c&&(f=c);k&&(O=!1);q(J);s(l.config,b)&&(l.config[g]=l.config[b]);try{h.exec(f)}catch(j){return w(C("fromtexteval", +"fromText eval for "+b+" failed: "+j,j,[b]))}k&&(O=!0);this.depMaps.push(J);i.completeLoad(g);p([g],d)}),f.load(a.name,p,d,l))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){W[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,t(this,function(a,b){var c,f;if("string"===typeof a){a=m(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=j(N,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;r(a,"defined",t(this,function(a){this.defineDep(b, +a);this.check()}));this.errback&&r(a,"error",t(this,this.errback))}c=a.id;f=k[c];!s(N,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,t(this,function(a){var b=j(k,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:l,contextName:b,registry:k,defined:p,urlFetched:T,defQueue:A,Module:$,makeModuleMap:m, +nextTick:h.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=l.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(l[b]||(l[b]={}),V(l[b],a,!0,!0)):l[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(ba[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),l.shim=b);a.packages&&v(a.packages,function(a){var b, +a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(l.paths[b]=a.location);l.pkgs[b]=a.name+"/"+(a.main||"main").replace(ja,"").replace(R,"")});B(k,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=m(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ca,arguments));return b||a.exports&&ea(a.exports)}},makeRequire:function(a,e){function g(f,c,d){var j,l;e.enableBuildCallback&&(c&&G(c))&&(c.__requireJsBuild= +!0);if("string"===typeof f){if(G(c))return w(C("requireargs","Invalid require call"),d);if(a&&s(N,f))return N[f](k[a.id]);if(h.get)return h.get(i,f,a,g);j=m(f,a,!1,!0);j=j.id;return!s(p,j)?w(C("notloaded",'Module name "'+j+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):p[j]}L();i.nextTick(function(){L();l=q(m(null,a));l.skipMap=e.skipMap;l.init(f,c,d,{enabled:!0});D()});return g}e=e||{};V(g,{isBrowser:z,toUrl:function(b){var e,d=b.lastIndexOf("."),g=b.split("/")[0];if(-1!== +d&&(!("."===g||".."===g)||1g.attachEvent.toString().indexOf("[native code"))&&!Z?(O=!0,g.attachEvent("onreadystatechange",b.onScriptLoad)): +(g.addEventListener("load",b.onScriptLoad,!1),g.addEventListener("error",b.onScriptError,!1)),g.src=d,L=g,D?y.insertBefore(g,D):y.appendChild(g),L=null,g;if(fa)try{importScripts(d),b.completeLoad(c)}catch(j){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,j,[c]))}};z&&!r.skipDataMain&&U(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(K=b.getAttribute("data-main"))return q=K,r.baseUrl||(E=q.split("/"),q=E.pop(),Q=E.length?E.join("/")+"/":"./",r.baseUrl= +Q),q=q.replace(R,""),h.jsExtRegExp.test(q)&&(q=K),r.deps=r.deps?r.deps.concat(q):[q],!0});define=function(b,c,d){var g,h;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(la,"").replace(ma,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(O){if(!(g=L))P&&"interactive"===P.readyState||U(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return P=b}),g=P;g&&(b|| +(b=g.getAttribute("data-requiremodule")),h=F[g.getAttribute("data-requirecontext")])}(h?h.defQueue:S).push([b,c,d])};define.amd={jQuery:!0};h.exec=function(b){return eval(b)};h(r)}})(this); diff --git a/node_modules/three/examples/js/libs/stats.min.js b/node_modules/three/examples/js/libs/stats.min.js new file mode 100644 index 00000000..8c2e8b6c --- /dev/null +++ b/node_modules/three/examples/js/libs/stats.min.js @@ -0,0 +1,5 @@ +// stats.js - http://github.com/mrdoob/stats.js +var Stats=function(){function f(a,e,b){a=document.createElement(a);a.id=e;a.style.cssText=b;return a}function l(a,e,b){var c=f("div",a,"padding:0 0 3px 3px;text-align:left;background:"+b),d=f("div",a+"Text","font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px;color:"+e);d.innerHTML=a.toUpperCase();c.appendChild(d);a=f("div",a+"Graph","width:74px;height:30px;background:"+e);c.appendChild(a);for(e=0;74>e;e++)a.appendChild(f("span","","width:1px;height:30px;float:left;opacity:0.9;background:"+ +b));return c}function m(a){for(var b=c.children,d=0;dr+1E3&&(d=Math.round(1E3* +t/(a-r)),u=Math.min(u,d),v=Math.max(v,d),A.textContent=d+" FPS ("+u+"-"+v+")",p(B,d/100),r=a,t=0,void 0!==h)){var b=performance.memory.usedJSHeapSize,c=performance.memory.jsHeapSizeLimit;h=Math.round(9.54E-7*b);y=Math.min(y,h);z=Math.max(z,h);E.textContent=h+" MB ("+y+"-"+z+")";p(F,b/c)}return a},update:function(){k=this.end()}}};"object"===typeof module&&(module.exports=Stats); diff --git a/node_modules/three/examples/js/libs/system.min.js b/node_modules/three/examples/js/libs/system.min.js new file mode 100644 index 00000000..2a1f22cc --- /dev/null +++ b/node_modules/three/examples/js/libs/system.min.js @@ -0,0 +1,4 @@ +// system.js - http://github.com/mrdoob/system.js +'use strict';var System={browser:function(){var a=navigator.userAgent;return/Arora/i.test(a)?"Arora":/Chrome/i.test(a)?"Chrome":/Epiphany/i.test(a)?"Epiphany":/Firefox/i.test(a)?"Firefox":/Mobile(\/.*)? Safari/i.test(a)?"Mobile Safari":/MSIE/i.test(a)?"Internet Explorer":/Midori/i.test(a)?"Midori":/Opera/.test(a)?"Opera":/Safari/i.test(a)?"Safari":!1}(),os:function(){var a=navigator.userAgent;return/Android/i.test(a)?"Android":/CrOS/i.test(a)?"Chrome OS":/iP[ao]d|iPhone/i.test(a)?"iOS":/Linux/i.test(a)? +"Linux":/Mac OS/i.test(a)?"Mac OS":/windows/i.test(a)?"Windows":!1}(),support:{canvas:!!window.CanvasRenderingContext2D,localStorage:function(){try{return!!window.localStorage.getItem}catch(a){return!1}}(),file:!!window.File&&!!window.FileReader&&!!window.FileList&&!!window.Blob,fileSystem:!!window.requestFileSystem||!!window.webkitRequestFileSystem,getUserMedia:!!window.navigator.getUserMedia||!!window.navigator.webkitGetUserMedia||!!window.navigator.mozGetUserMedia||!!window.navigator.msGetUserMedia, +requestAnimationFrame:!!window.mozRequestAnimationFrame||!!window.webkitRequestAnimationFrame||!!window.oRequestAnimationFrame||!!window.msRequestAnimationFrame,sessionStorage:function(){try{return!!window.sessionStorage.getItem}catch(a){return!1}}(),webgl:function(){try{return!!window.WebGLRenderingContext&&!!document.createElement("canvas").getContext("experimental-webgl")}catch(a){return!1}}(),worker:!!window.Worker}}; diff --git a/node_modules/three/examples/js/libs/tween.min.js b/node_modules/three/examples/js/libs/tween.min.js new file mode 100644 index 00000000..81882c98 --- /dev/null +++ b/node_modules/three/examples/js/libs/tween.min.js @@ -0,0 +1,13 @@ +// tween.js - http://github.com/sole/tween.js +'use strict';var TWEEN=TWEEN||function(){var a=[];return{REVISION:"7",getAll:function(){return a},removeAll:function(){a=[]},add:function(c){a.push(c)},remove:function(c){c=a.indexOf(c);-1!==c&&a.splice(c,1)},update:function(c){if(0===a.length)return!1;for(var b=0,d=a.length,c=void 0!==c?c:Date.now();b(a*=2)?0.5*a*a:-0.5*(--a*(a-2)-1)}},Cubic:{In:function(a){return a*a*a},Out:function(a){return--a*a*a+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*a:0.5*((a-=2)*a*a+2)}},Quartic:{In:function(a){return a*a*a*a},Out:function(a){return 1- --a*a*a*a},InOut:function(a){return 1>(a*=2)?0.5*a*a*a*a:-0.5*((a-=2)*a*a*a-2)}},Quintic:{In:function(a){return a*a*a* +a*a},Out:function(a){return--a*a*a*a*a+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*a*a*a:0.5*((a-=2)*a*a*a*a+2)}},Sinusoidal:{In:function(a){return 1-Math.cos(a*Math.PI/2)},Out:function(a){return Math.sin(a*Math.PI/2)},InOut:function(a){return 0.5*(1-Math.cos(Math.PI*a))}},Exponential:{In:function(a){return 0===a?0:Math.pow(1024,a-1)},Out:function(a){return 1===a?1:1-Math.pow(2,-10*a)},InOut:function(a){return 0===a?0:1===a?1:1>(a*=2)?0.5*Math.pow(1024,a-1):0.5*(-Math.pow(2,-10*(a-1))+2)}},Circular:{In:function(a){return 1- +Math.sqrt(1-a*a)},Out:function(a){return Math.sqrt(1- --a*a)},InOut:function(a){return 1>(a*=2)?-0.5*(Math.sqrt(1-a*a)-1):0.5*(Math.sqrt(1-(a-=2)*a)+1)}},Elastic:{In:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return-(b*Math.pow(2,10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4))},Out:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return b*Math.pow(2,-10*a)*Math.sin((a-c)* +2*Math.PI/0.4)+1},InOut:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return 1>(a*=2)?-0.5*b*Math.pow(2,10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4):0.5*b*Math.pow(2,-10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4)+1}},Back:{In:function(a){return a*a*(2.70158*a-1.70158)},Out:function(a){return--a*a*(2.70158*a+1.70158)+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*(3.5949095*a-2.5949095):0.5*((a-=2)*a*(3.5949095*a+2.5949095)+2)}},Bounce:{In:function(a){return 1- +TWEEN.Easing.Bounce.Out(1-a)},Out:function(a){return a<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+0.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+0.9375:7.5625*(a-=2.625/2.75)*a+0.984375},InOut:function(a){return 0.5>a?0.5*TWEEN.Easing.Bounce.In(2*a):0.5*TWEEN.Easing.Bounce.Out(2*a-1)+0.5}}}; +TWEEN.Interpolation={Linear:function(a,c){var b=a.length-1,d=b*c,e=Math.floor(d),f=TWEEN.Interpolation.Utils.Linear;return 0>c?f(a[0],a[1],d):1b?b:e+1],d-e)},Bezier:function(a,c){var b=0,d=a.length-1,e=Math.pow,f=TWEEN.Interpolation.Utils.Bernstein,h;for(h=0;h<=d;h++)b+=e(1-c,d-h)*e(c,h)*a[h]*f(d,h);return b},CatmullRom:function(a,c){var b=a.length-1,d=b*c,e=Math.floor(d),f=TWEEN.Interpolation.Utils.CatmullRom;return a[0]===a[b]?(0>c&&(e=Math.floor(d=b*(1+c))),f(a[(e- +1+b)%b],a[e],a[(e+1)%b],a[(e+2)%b],d-e)):0>c?a[0]-(f(a[0],a[0],a[1],a[1],-d)-a[0]):1 1 ) { + + mesh = new THREE.Object3D(); + for ( i = 0; i < meshLen; i ++ ) { + + var sm = new THREE.Mesh( geometries[ i ] ); + meshes.push( sm ); + mesh.add( sm ); + + } + + } else { + + mesh = new THREE.Mesh( geometries[ 0 ] ); + meshes.push( mesh ); + + } + + mesh.applyMatrix( mtx ); + mesh.name = name; + + + parent = this.getBlock( par_id ) || this.trunk; + parent.add( mesh ); + + + var matLen = materials.length; + var maxLen = Math.max( meshLen, matLen ); + for ( i = 0; i < maxLen; i ++ ) + meshes[ i % meshLen ].material = materials[ i % matLen ]; + + + // Ignore for now + this.parseProperties( null ); + mesh.extra = this.parseUserAttributes(); + + return mesh; + + }, + + parseMaterial: function ( len ) { + + var name, + type, + props, + mat, + attributes, + finalize, + num_methods, + methods_parsed; + + name = this.readUTF(); + type = this.readU8(); + num_methods = this.readU8(); + + //log( "AWDLoader parseMaterial ",name ) + + // Read material numerical properties + // (1=color, 2=bitmap url, 11=alpha_blending, 12=alpha_threshold, 13=repeat) + props = this.parseProperties( { + 1: AWD_FIELD_INT32, + 2: AWD_FIELD_BADDR, + 11: AWD_FIELD_BOOL, + 12: AWD_FIELD_FLOAT32, + 13: AWD_FIELD_BOOL + } ); + + methods_parsed = 0; + + while ( methods_parsed < num_methods ) { + + var method_type = this.readU16(); + this.parseProperties( null ); + this.parseUserAttributes(); + + } + + attributes = this.parseUserAttributes(); + + if ( this.materialFactory !== undefined ) { + + mat = this.materialFactory( name ); + if ( mat ) return mat; + + } + + mat = new THREE.MeshPhongMaterial(); + + if ( type === 1 ) { + + // Color material + mat.color.setHex( props.get( 1, 0xcccccc ) ); + + } else if ( type === 2 ) { + + // Bitmap material + var tex_addr = props.get( 2, 0 ); + mat.map = this.getBlock( tex_addr ); + + } + + mat.extra = attributes; + mat.alphaThreshold = props.get( 12, 0.0 ); + mat.repeat = props.get( 13, false ); + + + return mat; + + }, + + parseTexture: function( len ) { + + var name = this.readUTF(), + type = this.readU8(), + asset, + data_len; + + // External + if ( type === 0 ) { + + data_len = this.readU32(); + var url = this.readUTFBytes( data_len ); + console.log( url ); + + asset = this.loadTexture( url ); + + } else { + // embed texture not supported + } + // Ignore for now + this.parseProperties( null ); + + this.parseUserAttributes(); + return asset; + + }, + + loadTexture: function( url ) { + + var tex = new THREE.Texture(); + + var loader = new THREE.ImageLoader( this.manager ); + + loader.load( this._baseDir + url, function( image ) { + + tex.image = image; + tex.needsUpdate = true; + + } ); + + return tex; + + }, + + parseSkeleton: function( len ) { + + // Array + var name = this.readUTF(), + num_joints = this.readU16(), + skeleton = [], + joints_parsed = 0; + + this.parseProperties( null ); + + while ( joints_parsed < num_joints ) { + + var joint, ibp; + + // Ignore joint id + this.readU16(); + + joint = new THREE.Bone(); + joint.parent = this.readU16() - 1; // 0=null in AWD + joint.name = this.readUTF(); + + ibp = this.parseMatrix4(); + joint.skinMatrix = ibp; + + // Ignore joint props/attributes for now + this.parseProperties( null ); + this.parseUserAttributes(); + + skeleton.push( joint ); + joints_parsed ++; + + } + + // Discard attributes for now + this.parseUserAttributes(); + + + return skeleton; + + }, + + parseSkeletonPose: function( blockID ) { + + var name = this.readUTF(); + + var num_joints = this.readU16(); + this.parseProperties( null ); + + // debug( 'parse Skeleton Pose. joints : ' + num_joints); + + var pose = []; + + var joints_parsed = 0; + + while ( joints_parsed < num_joints ) { + + var joint_pose; + + var has_transform; //:uint; + var mtx_data; + + has_transform = this.readU8(); + + if ( has_transform === 1 ) { + + mtx_data = this.parseMatrix4(); + + } else { + + mtx_data = new THREE.Matrix4(); + + } + pose[ joints_parsed ] = mtx_data; + joints_parsed ++; + + } + + // Skip attributes for now + this.parseUserAttributes(); + + return pose; + + }, + + parseSkeletonAnimation: function( blockID ) { + + var frame_dur; + var pose_addr; + var pose; + + var name = this.readUTF(); + + var clip = []; + + var num_frames = this.readU16(); + this.parseProperties( null ); + + var frames_parsed = 0; + var returnedArray; + + // debug( 'parse Skeleton Animation. frames : ' + num_frames); + + while ( frames_parsed < num_frames ) { + + pose_addr = this.readU32(); + frame_dur = this.readU16(); + + pose = this._blocks[ pose_addr ].data; + // debug( 'pose address ',pose[2].elements[12],pose[2].elements[13],pose[2].elements[14] ); + clip.push( { + pose : pose, + duration : frame_dur + } ); + + frames_parsed ++; + + } + + if ( clip.length === 0 ) { + + // debug("Could not this SkeletonClipNode, because no Frames where set."); + return; + + } + // Ignore attributes for now + this.parseUserAttributes(); + return clip; + + }, + + parseVertexAnimationSet: function( len ) { + + var poseBlockAdress, + name = this.readUTF(), + num_frames = this.readU16(), + props = this.parseProperties( { 1: UINT16 } ), + frames_parsed = 0, + skeletonFrames = []; + + while ( frames_parsed < num_frames ) { + + poseBlockAdress = this.readU32(); + skeletonFrames.push( this._blocks[ poseBlockAdress ].data ); + frames_parsed ++; + + } + + this.parseUserAttributes(); + + + return skeletonFrames; + + }, + + parseAnimatorSet: function( len ) { + + var targetMesh; + + var animSetBlockAdress; //:int + + var targetAnimationSet; //:AnimationSetBase; + var outputString = ""; //:String = ""; + var name = this.readUTF(); + var type = this.readU16(); + + var props = this.parseProperties( { 1: BADDR } ); + + animSetBlockAdress = this.readU32(); + var targetMeshLength = this.readU16(); + + var meshAdresses = []; //:Vector. = new Vector.; + + for ( var i = 0; i < targetMeshLength; i ++ ) + meshAdresses.push( this.readU32() ); + + var activeState = this.readU16(); + var autoplay = Boolean( this.readU8() ); + this.parseUserAttributes(); + this.parseUserAttributes(); + + var returnedArray; + var targetMeshes = []; //:Vector. = new Vector.; + + for ( i = 0; i < meshAdresses.length; i ++ ) { + + // returnedArray = getAssetByID(meshAdresses[i], [AssetType.MESH]); + // if (returnedArray[0]) + targetMeshes.push( this._blocks[ meshAdresses[ i ]].data ); + + } + + targetAnimationSet = this._blocks[ animSetBlockAdress ].data; + var thisAnimator; + + if ( type == 1 ) { + + + thisAnimator = { + animationSet : targetAnimationSet, + skeleton : this._blocks[ props.get( 1, 0 ) ].data + }; + + } else if ( type == 2 ) { + // debug( "vertex Anim???"); + } + + + for ( i = 0; i < targetMeshes.length; i ++ ) { + + targetMeshes[ i ].animator = thisAnimator; + + } + // debug("Parsed a Animator: Name = " + name); + + return thisAnimator; + + }, + + parseMeshData: function ( len ) { + + var name = this.readUTF(), + num_subs = this.readU16(), + geom, + subs_parsed = 0, + props, + buffer, + skinW, skinI, + geometries = []; + + props = this.parseProperties( { + 1: this._geoNrType, + 2: this._geoNrType + } ); + + // Loop through sub meshes + while ( subs_parsed < num_subs ) { + + var sm_len, sm_end, attrib; + + geom = new THREE.BufferGeometry(); + geom.name = name; + geometries.push( geom ); + + + sm_len = this.readU32(); + sm_end = this._ptr + sm_len; + + + // Ignore for now + this.parseProperties( { 1: this._geoNrType, 2: this._geoNrType } ); + + // Loop through data streams + while ( this._ptr < sm_end ) { + + var idx = 0, + str_type = this.readU8(), + str_ftype = this.readU8(), + str_len = this.readU32(), + str_end = str_len + this._ptr; + + // VERTICES + // ------------------ + if ( str_type === 1 ) { + + buffer = new Float32Array( ( str_len / 12 ) * 3 ); + attrib = new THREE.BufferAttribute( buffer, 3 ); + + geom.addAttribute( 'position', attrib ); + idx = 0; + + while ( this._ptr < str_end ) { + + buffer[ idx ] = - this.readF32(); + buffer[ idx + 1 ] = this.readF32(); + buffer[ idx + 2 ] = this.readF32(); + idx += 3; + + } + + } + + // INDICES + // ----------------- + else if ( str_type === 2 ) { + + buffer = new Uint16Array( str_len / 2 ); + attrib = new THREE.BufferAttribute( buffer, 1 ); + geom.setIndex( attrib ); + + idx = 0; + + while ( this._ptr < str_end ) { + + buffer[ idx + 1 ] = this.readU16(); + buffer[ idx ] = this.readU16(); + buffer[ idx + 2 ] = this.readU16(); + idx += 3; + + } + + } + + // UVS + // ------------------- + else if ( str_type === 3 ) { + + buffer = new Float32Array( ( str_len / 8 ) * 2 ); + attrib = new THREE.BufferAttribute( buffer, 2 ); + + geom.addAttribute( 'uv', attrib ); + idx = 0; + + while ( this._ptr < str_end ) { + + buffer[ idx ] = this.readF32(); + buffer[ idx + 1 ] = 1.0 - this.readF32(); + idx += 2; + + } + + } + + // NORMALS + else if ( str_type === 4 ) { + + buffer = new Float32Array( ( str_len / 12 ) * 3 ); + attrib = new THREE.BufferAttribute( buffer, 3 ); + geom.addAttribute( 'normal', attrib ); + idx = 0; + + while ( this._ptr < str_end ) { + + buffer[ idx ] = - this.readF32(); + buffer[ idx + 1 ] = this.readF32(); + buffer[ idx + 2 ] = this.readF32(); + idx += 3; + + } + + } + + // else if (str_type == 6) { + // skinI = new Float32Array( str_len>>1 ); + // idx = 0 + + // while (this._ptr < str_end) { + // skinI[idx] = this.readU16(); + // idx++; + // } + + // } + // else if (str_type == 7) { + // skinW = new Float32Array( str_len>>2 ); + // idx = 0; + + // while (this._ptr < str_end) { + // skinW[idx] = this.readF32(); + // idx++; + // } + // } + else { + + this._ptr = str_end; + + } + + } + + this.parseUserAttributes(); + + geom.computeBoundingSphere(); + subs_parsed ++; + + } + + //geom.computeFaceNormals(); + + this.parseUserAttributes(); + //finalizeAsset(geom, name); + + return geometries; + + }, + + parseMeshPoseAnimation: function( len, poseOnly ) { + + var num_frames = 1, + num_submeshes, + frames_parsed, + subMeshParsed, + frame_dur, + x, y, z, + + str_len, + str_end, + geom, + subGeom, + idx = 0, + clip = {}, + indices, + verts, + num_Streams, + streamsParsed, + streamtypes = [], + + props, + thisGeo, + name = this.readUTF(), + geoAdress = this.readU32(); + + var mesh = this.getBlock( geoAdress ); + + if ( mesh === null ) { + + console.log( "parseMeshPoseAnimation target mesh not found at:", geoAdress ); + return; + + } + + geom = mesh.geometry; + geom.morphTargets = []; + + if ( ! poseOnly ) + num_frames = this.readU16(); + + num_submeshes = this.readU16(); + num_Streams = this.readU16(); + + // debug("VA num_frames : ", num_frames ); + // debug("VA num_submeshes : ", num_submeshes ); + // debug("VA numstreams : ", num_Streams ); + + streamsParsed = 0; + while ( streamsParsed < num_Streams ) { + + streamtypes.push( this.readU16() ); + streamsParsed ++; + + } + props = this.parseProperties( { 1: BOOL, 2: BOOL } ); + + clip.looping = props.get( 1, true ); + clip.stitchFinalFrame = props.get( 2, false ); + + frames_parsed = 0; + + while ( frames_parsed < num_frames ) { + + frame_dur = this.readU16(); + subMeshParsed = 0; + + while ( subMeshParsed < num_submeshes ) { + + streamsParsed = 0; + str_len = this.readU32(); + str_end = this._ptr + str_len; + + while ( streamsParsed < num_Streams ) { + + if ( streamtypes[ streamsParsed ] === 1 ) { + + //geom.addAttribute( 'morphTarget'+frames_parsed, Float32Array, str_len/12, 3 ); + var buffer = new Float32Array( str_len / 4 ); + geom.morphTargets.push( { + array : buffer + } ); + + //buffer = geom.attributes['morphTarget'+frames_parsed].array + idx = 0; + + while ( this._ptr < str_end ) { + + buffer[ idx ] = this.readF32(); + buffer[ idx + 1 ] = this.readF32(); + buffer[ idx + 2 ] = this.readF32(); + idx += 3; + + } + + + subMeshParsed ++; + + } else + this._ptr = str_end; + streamsParsed ++; + + } + + } + + + frames_parsed ++; + + } + + this.parseUserAttributes(); + + return null; + + }, + + getBlock: function ( id ) { + + return this._blocks[ id ].data; + + }, + + parseMatrix4: function () { + + var mtx = new THREE.Matrix4(); + var e = mtx.elements; + + e[ 0 ] = this.readF32(); + e[ 1 ] = this.readF32(); + e[ 2 ] = this.readF32(); + e[ 3 ] = 0.0; + //e[3] = 0.0; + + e[ 4 ] = this.readF32(); + e[ 5 ] = this.readF32(); + e[ 6 ] = this.readF32(); + //e[7] = this.readF32(); + e[ 7 ] = 0.0; + + e[ 8 ] = this.readF32(); + e[ 9 ] = this.readF32(); + e[ 10 ] = this.readF32(); + //e[11] = this.readF32(); + e[ 11 ] = 0.0; + + e[ 12 ] = - this.readF32(); + e[ 13 ] = this.readF32(); + e[ 14 ] = this.readF32(); + //e[15] = this.readF32(); + e[ 15 ] = 1.0; + return mtx; + + }, + + parseProperties: function ( expected ) { + + var list_len = this.readU32(); + var list_end = this._ptr + list_len; + + var props = new AWDProperties(); + + if ( expected ) { + + while ( this._ptr < list_end ) { + + var key = this.readU16(); + var len = this.readU32(); + var type; + + if ( expected.hasOwnProperty( key ) ) { + + type = expected[ key ]; + props.set( key, this.parseAttrValue( type, len ) ); + + } else { + + this._ptr += len; + + } + + } + + } + + return props; + + }, + + parseUserAttributes: function () { + + // skip for now + this._ptr = this.readU32() + this._ptr; + return null; + + }, + + parseAttrValue: function ( type, len ) { + + var elem_len; + var read_func; + + switch ( type ) { + case AWD_FIELD_INT8: + elem_len = 1; + read_func = this.readI8; + break; + case AWD_FIELD_INT16: + elem_len = 2; + read_func = this.readI16; + break; + case AWD_FIELD_INT32: + elem_len = 4; + read_func = this.readI32; + break; + case AWD_FIELD_BOOL: + case AWD_FIELD_UINT8: + elem_len = 1; + read_func = this.readU8; + break; + case AWD_FIELD_UINT16: + elem_len = 2; + read_func = this.readU16; + break; + case AWD_FIELD_UINT32: + case AWD_FIELD_BADDR: + elem_len = 4; + read_func = this.readU32; + break; + case AWD_FIELD_FLOAT32: + elem_len = 4; + read_func = this.readF32; + break; + case AWD_FIELD_FLOAT64: + elem_len = 8; + read_func = this.readF64; + break; + case AWD_FIELD_VECTOR2x1: + case AWD_FIELD_VECTOR3x1: + case AWD_FIELD_VECTOR4x1: + case AWD_FIELD_MTX3x2: + case AWD_FIELD_MTX3x3: + case AWD_FIELD_MTX4x3: + case AWD_FIELD_MTX4x4: + elem_len = 8; + read_func = this.readF64; + break; + } + + if ( elem_len < len ) { + + var list; + var num_read; + var num_elems; + + list = []; + num_read = 0; + num_elems = len / elem_len; + + while ( num_read < num_elems ) { + + list.push( read_func.call( this ) ); + num_read ++; + + } + + return list; + + } else { + + return read_func.call( this ); + + } + + }, + + readU8: function () { + + return this._data.getUint8( this._ptr ++ ); + + }, + readI8: function () { + + return this._data.getInt8( this._ptr ++ ); + + }, + readU16: function () { + + var a = this._data.getUint16( this._ptr, littleEndian ); + this._ptr += 2; + return a; + + }, + readI16: function () { + + var a = this._data.getInt16( this._ptr, littleEndian ); + this._ptr += 2; + return a; + + }, + readU32: function () { + + var a = this._data.getUint32( this._ptr, littleEndian ); + this._ptr += 4; + return a; + + }, + readI32: function () { + + var a = this._data.getInt32( this._ptr, littleEndian ); + this._ptr += 4; + return a; + + }, + readF32: function () { + + var a = this._data.getFloat32( this._ptr, littleEndian ); + this._ptr += 4; + return a; + + }, + readF64: function () { + + var a = this._data.getFloat64( this._ptr, littleEndian ); + this._ptr += 8; + return a; + + }, + + /** + * Converts a UTF-8 byte array to JavaScript's 16-bit Unicode. + * @param {Array.} bytes UTF-8 byte array. + * @return {string} 16-bit Unicode string. + */ + readUTF: function () { + + var len = this.readU16(); + return this.readUTFBytes( len ); + + }, + + /** + * Converts a UTF-8 byte array to JavaScript's 16-bit Unicode. + * @param {Array.} bytes UTF-8 byte array. + * @return {string} 16-bit Unicode string. + */ + readUTFBytes: function ( len ) { + + // TODO(user): Use native implementations if/when available + var out = [], c = 0; + + while ( out.length < len ) { + + var c1 = this._data.getUint8( this._ptr ++, littleEndian ); + if ( c1 < 128 ) { + + out[ c ++ ] = String.fromCharCode( c1 ); + + } else if ( c1 > 191 && c1 < 224 ) { + + var c2 = this._data.getUint8( this._ptr ++, littleEndian ); + out[ c ++ ] = String.fromCharCode( ( c1 & 31 ) << 6 | c2 & 63 ); + + } else { + + var c2 = this._data.getUint8( this._ptr ++, littleEndian ); + var c3 = this._data.getUint8( this._ptr ++, littleEndian ); + out[ c ++ ] = String.fromCharCode( + ( c1 & 15 ) << 12 | ( c2 & 63 ) << 6 | c3 & 63 + ); + + } + + } + return out.join( '' ); + + } + + }; + +} )(); diff --git a/node_modules/three/examples/js/loaders/AssimpJSONLoader.js b/node_modules/three/examples/js/loaders/AssimpJSONLoader.js new file mode 100644 index 00000000..0f6ea31b --- /dev/null +++ b/node_modules/three/examples/js/loaders/AssimpJSONLoader.js @@ -0,0 +1,386 @@ +/** + * @author Alexander Gessler / http://www.greentoken.de/ + * https://github.com/acgessler + * + * Loader for models imported with Open Asset Import Library (http://assimp.sf.net) + * through assimp2json (https://github.com/acgessler/assimp2json). + * + * Supports any input format that assimp supports, including 3ds, obj, dae, blend, + * fbx, x, ms3d, lwo (and many more). + * + * See webgl_loader_assimp2json example. + */ + +THREE.AssimpJSONLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.AssimpJSONLoader.prototype = { + + constructor: THREE.AssimpJSONLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + this.texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : this.extractUrlBase( url ); + + var loader = new THREE.XHRLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + var json = JSON.parse( text ), scene, metadata; + + // Check __metadata__ meta header if present + // This header is used to disambiguate between + // different JSON-based file formats. + metadata = json.__metadata__; + if ( typeof metadata !== 'undefined' ) { + + // Check if assimp2json at all + if ( metadata.format !== 'assimp2json' ) { + + onError( 'Not an assimp2json scene' ); + return; + + } + // Check major format version + else if ( metadata.version < 100 && metadata.version >= 200 ) { + + onError( 'Unsupported assimp2json file format version' ); + return; + + } + + } + + scene = scope.parse( json ); + onLoad( scene ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setTexturePath: function ( value ) { + + this.texturePath = value; + + }, + + extractUrlBase: function ( url ) { + + // from three/src/loaders/Loader.js + var parts = url.split( '/' ); + parts.pop(); + return ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/'; + + }, + + parse: function ( json ) { + + var meshes = this.parseList ( json.meshes, this.parseMesh ); + var materials = this.parseList ( json.materials, this.parseMaterial ); + return this.parseObject( json, json.rootnode, meshes, materials ); + + }, + + parseList : function( json, handler ) { + + var meshes = new Array( json.length ); + for ( var i = 0; i < json.length; ++ i ) { + + meshes[ i ] = handler.call( this, json[ i ] ); + + } + return meshes; + + }, + + parseMesh : function( json ) { + + var vertex, geometry, i, e, in_data, src; + + + geometry = new THREE.Geometry(); + + // read vertex positions + for ( in_data = json.vertices, i = 0, e = in_data.length; i < e; ) { + + geometry.vertices.push( new THREE.Vector3( in_data[ i ++ ], in_data[ i ++ ], in_data[ i ++ ] ) ); + + } + + // read faces + var cnt = 0; + for ( in_data = json.faces, i = 0, e = in_data.length; i < e; ++ i ) { + + src = in_data[ i ]; + face = new THREE.Face3( src[ 0 ], src[ 1 ], src[ 2 ] ); + geometry.faces.push( face ); + + } + + // read texture coordinates - three.js attaches them to its faces + json.texturecoords = json.texturecoords || []; + for ( i = 0, e = json.texturecoords.length; i < e; ++ i ) { + + function convertTextureCoords( in_uv, out_faces, out_vertex_uvs ) { + + var i, e, face, a, b, c; + + for ( i = 0, e = out_faces.length; i < e; ++ i ) { + + face = out_faces[ i ]; + a = face.a * 2; + b = face.b * 2; + c = face.c * 2; + out_vertex_uvs.push( [ + new THREE.Vector2( in_uv[ a ], in_uv[ a + 1 ] ), + new THREE.Vector2( in_uv[ b ], in_uv[ b + 1 ] ), + new THREE.Vector2( in_uv[ c ], in_uv[ c + 1 ] ) + ] ); + + } + + } + + convertTextureCoords( json.texturecoords[ i ], geometry.faces, geometry.faceVertexUvs[ i ] ); + + } + + // read normals - three.js also attaches them to its faces + if ( json.normals ) { + + function convertNormals( in_nor, out_faces ) { + + var i, e, face, a, b, c; + + for ( i = 0, e = out_faces.length; i < e; ++ i ) { + + face = out_faces[ i ]; + a = face.a * 3; + b = face.b * 3; + c = face.c * 3; + face.vertexNormals = [ + new THREE.Vector3( in_nor[ a ], in_nor[ a + 1 ], in_nor[ a + 2 ] ), + new THREE.Vector3( in_nor[ b ], in_nor[ b + 1 ], in_nor[ b + 2 ] ), + new THREE.Vector3( in_nor[ c ], in_nor[ c + 1 ], in_nor[ c + 2 ] ) + ]; + + } + + } + + convertNormals( json.normals, geometry.faces ); + + } + + // read vertex colors - three.js also attaches them to its faces + if ( json.colors && json.colors[ 0 ] ) { + + function convertColors( in_color, out_faces ) { + + for ( var i = 0, e = out_faces.length; i < e; ++ i ) { + + var face = out_faces[ i ]; + var a = face.a * 4; + var b = face.b * 4; + var c = face.c * 4; + + face.vertexColors = [ + new THREE.Color().fromArray( a ), + new THREE.Color().fromArray( b ), + new THREE.Color().fromArray( c ) + ]; + + } + + } + + convertColors( json.colors[ 0 ], geometry.faces ); + + } + + + //geometry.computeFaceNormals(); + //geometry.computeVertexNormals(); + geometry.computeBoundingSphere(); + + return geometry; + + }, + + parseMaterial : function( json ) { + + var mat = null, + scope = this, i, prop, has_textures = [], + + init_props = { + shading : THREE.SmoothShading + }; + + function toColor( value_arr ) { + + var col = new THREE.Color(); + col.setRGB( value_arr[ 0 ], value_arr[ 1 ], value_arr[ 2 ] ); + return col; + + } + + function defaultTexture() { + + var im = new Image(); + im.width = 1; + im.height = 1; + return new THREE.Texture( im ); + + } + + for ( var i in json.properties ) { + + prop = json.properties[ i ]; + + if ( prop.key === '$tex.file' ) { + + // prop.semantic gives the type of the texture + // 1: diffuse + // 2: specular mao + // 5: height map (bumps) + // 6: normal map + // more values (i.e. emissive, environment) are known by assimp and may be relevant + if ( prop.semantic === 1 || prop.semantic === 5 || prop.semantic === 6 || prop.semantic === 2 ) { + + ( function( semantic ) { + + var loader = new THREE.TextureLoader( scope.manager ), + keyname; + + if ( semantic === 1 ) { + + keyname = 'map'; + + } else if ( semantic === 5 ) { + + keyname = 'bumpMap'; + + } else if ( semantic === 6 ) { + + keyname = 'normalMap'; + + } else if ( semantic === 2 ) { + + keyname = 'specularMap'; + + } + + has_textures.push( keyname ); + + loader.setCrossOrigin( this.crossOrigin ); + var material_url = scope.texturePath + '/' + prop.value; + material_url = material_url.replace( /\\/g, '/' ); + loader.load( material_url, function( tex ) { + + if ( tex ) { + + // TODO: read texture settings from assimp. + // Wrapping is the default, though. + tex.wrapS = tex.wrapT = THREE.RepeatWrapping; + + mat[ keyname ] = tex; + mat.needsUpdate = true; + + } + + } ); + + } )( prop.semantic ); + + } + + } else if ( prop.key === '?mat.name' ) { + + init_props.name = prop.value; + + } else if ( prop.key === '$clr.diffuse' ) { + + init_props.color = toColor( prop.value ); + + } else if ( prop.key === '$clr.specular' ) { + + init_props.specular = toColor( prop.value ); + + } else if ( prop.key === '$clr.emissive' ) { + + init_props.emissive = toColor( prop.value ); + + } else if ( prop.key === '$mat.shadingm' ) { + + // aiShadingMode_Flat + if ( prop.value === 1 ) { + + init_props.shading = THREE.FlatShading; + + } + + } else if ( prop.key === '$mat.shininess' ) { + + init_props.shininess = prop.value; + + } + + } + + // note: three.js does not like it when a texture is added after the geometry + // has been rendered once, see http://stackoverflow.com/questions/16531759/. + // for this reason we fill all slots upfront with default textures + if ( has_textures.length ) { + + for ( i = has_textures.length - 1; i >= 0; -- i ) { + + init_props[ has_textures[ i ]] = defaultTexture(); + + } + + } + + mat = new THREE.MeshPhongMaterial( init_props ); + return mat; + + }, + + parseObject : function( json, node, meshes, materials ) { + + var obj = new THREE.Object3D() + , i + , idx + ; + + obj.name = node.name || ""; + obj.matrix = new THREE.Matrix4().fromArray( node.transformation ).transpose(); + obj.matrix.decompose( obj.position, obj.quaternion, obj.scale ); + + for ( i = 0; node.meshes && i < node.meshes.length; ++ i ) { + + idx = node.meshes[ i ]; + obj.add( new THREE.Mesh( meshes[ idx ], materials[ json.meshes[ idx ].materialindex ] ) ); + + } + + for ( i = 0; node.children && i < node.children.length; ++ i ) { + + obj.add( this.parseObject( json, node.children[ i ], meshes, materials ) ); + + } + + return obj; + + }, +}; diff --git a/node_modules/three/examples/js/loaders/BabylonLoader.js b/node_modules/three/examples/js/loaders/BabylonLoader.js new file mode 100644 index 00000000..ece45be8 --- /dev/null +++ b/node_modules/three/examples/js/loaders/BabylonLoader.js @@ -0,0 +1,255 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BabylonLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.BabylonLoader.prototype = { + + constructor: THREE.ObjectLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( json ) { + + var materials = this.parseMaterials( json ); + var scene = this.parseObjects( json, materials ); + + return scene; + + }, + + parseMaterials: function ( json ) { + + var materials = {}; + + for ( var i = 0, l = json.materials.length; i < l; i ++ ) { + + var data = json.materials[ i ]; + + var material = new THREE.MeshPhongMaterial(); + material.name = data.name; + material.color.fromArray( data.diffuse ); + material.emissive.fromArray( data.emissive ); + material.specular.fromArray( data.specular ); + material.shininess = data.specularPower; + material.opacity = data.alpha; + + materials[ data.id ] = material; + + } + + if ( json.multiMaterials ) { + + for ( var i = 0, l = json.multiMaterials.length; i < l; i ++ ) { + + var data = json.multiMaterials[ i ]; + + console.warn( 'THREE.BabylonLoader: Multi materials not yet supported.' ); + + materials[ data.id ] = new THREE.MeshPhongMaterial(); + + } + + } + + return materials; + + }, + + parseGeometry: function ( json ) { + + var geometry = new THREE.BufferGeometry(); + + // indices + + var indices = new Uint16Array( json.indices ); + + geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) ); + + // positions + + var positions = new Float32Array( json.positions ); + + for ( var j = 2, jl = positions.length; j < jl; j += 3 ) { + + positions[ j ] = - positions[ j ]; + + } + + geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + + // normals + + if ( json.normals ) { + + var normals = new Float32Array( json.normals ); + + for ( var j = 2, jl = normals.length; j < jl; j += 3 ) { + + normals[ j ] = - normals[ j ]; + + } + + geometry.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + + } + + // uvs + + if ( json.uvs ) { + + var uvs = new Float32Array( json.uvs ); + + geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + + } + + // offsets + + var subMeshes = json.subMeshes; + + if ( subMeshes ) { + + for ( var j = 0, jl = subMeshes.length; j < jl; j ++ ) { + + var subMesh = subMeshes[ j ]; + + geometry.addGroup( subMesh.indexStart, subMesh.indexCount ); + + } + + } + + return geometry; + + }, + + parseObjects: function ( json, materials ) { + + var objects = {}; + var scene = new THREE.Scene(); + + var cameras = json.cameras; + + for ( var i = 0, l = cameras.length; i < l; i ++ ) { + + var data = cameras[ i ]; + + var camera = new THREE.PerspectiveCamera( ( data.fov / Math.PI ) * 180, 1.33, data.minZ, data.maxZ ); + + camera.name = data.name; + camera.position.fromArray( data.position ); + if ( data.rotation ) camera.rotation.fromArray( data.rotation ); + + objects[ data.id ] = camera; + + } + + var lights = json.lights; + + for ( var i = 0, l = lights.length; i < l; i ++ ) { + + var data = lights[ i ]; + + var light; + + switch ( data.type ) { + + case 0: + light = new THREE.PointLight(); + break; + + case 1: + light = new THREE.DirectionalLight(); + break; + + case 2: + light = new THREE.SpotLight(); + break; + + case 3: + light = new THREE.HemisphereLight(); + break; + } + + light.name = data.name; + if ( data.position ) light.position.set( data.position[ 0 ], data.position[ 1 ], - data.position[ 2 ] ); + light.color.fromArray( data.diffuse ); + if ( data.groundColor ) light.groundColor.fromArray( data.groundColor ); + if ( data.intensity ) light.intensity = data.intensity; + + objects[ data.id ] = light; + + scene.add( light ); + + } + + var meshes = json.meshes; + + for ( var i = 0, l = meshes.length; i < l; i ++ ) { + + var data = meshes[ i ]; + + var object; + + if ( data.indices ) { + + var geometry = this.parseGeometry( data ); + + object = new THREE.Mesh( geometry, materials[ data.materialId ] ); + + } else { + + object = new THREE.Group(); + + } + + object.name = data.name; + object.position.set( data.position[ 0 ], data.position[ 1 ], - data.position[ 2 ] ); + object.rotation.fromArray( data.rotation ); + if ( data.rotationQuaternion ) object.quaternion.fromArray( data.rotationQuaternion ); + object.scale.fromArray( data.scaling ); + // object.visible = data.isVisible; + + if ( data.parentId ) { + + objects[ data.parentId ].add( object ); + + } else { + + scene.add( object ); + + } + + objects[ data.id ] = object; + + } + + return scene; + + } + +}; diff --git a/node_modules/three/examples/js/loaders/BinaryLoader.js b/node_modules/three/examples/js/loaders/BinaryLoader.js new file mode 100644 index 00000000..f399e7cf --- /dev/null +++ b/node_modules/three/examples/js/loaders/BinaryLoader.js @@ -0,0 +1,715 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.BinaryLoader = function ( manager ) { + + if ( typeof manager === 'boolean' ) { + + console.warn( 'THREE.BinaryLoader: showStatus parameter has been removed from constructor.' ); + manager = undefined; + + } + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.BinaryLoader.prototype = { + + constructor: THREE.BinaryLoader, + + // Deprecated + + get statusDomElement () { + + if ( this._statusDomElement === undefined ) { + + this._statusDomElement = document.createElement( 'div' ); + + } + + console.warn( 'THREE.BinaryLoader: .statusDomElement has been removed.' ); + return this._statusDomElement; + + }, + + // Load models generated by slim OBJ converter with BINARY option (converter_obj_three_slim.py -t binary) + // - binary models consist of two files: JS and BIN + // - parameters + // - url (required) + // - callback (required) + // - texturePath (optional: if not specified, textures will be assumed to be in the same folder as JS model file) + // - binaryPath (optional: if not specified, binary file will be assumed to be in the same folder as JS model file) + load: function ( url, onLoad, onProgress, onError ) { + + // todo: unify load API to for easier SceneLoader use + + var texturePath = this.texturePath || THREE.Loader.prototype.extractUrlBase( url ); + var binaryPath = this.binaryPath || THREE.Loader.prototype.extractUrlBase( url ); + + // #1 load JS part via web worker + + var scope = this; + + var jsonloader = new THREE.XHRLoader( this.manager ); + jsonloader.setCrossOrigin( this.crossOrigin ); + jsonloader.load( url, function ( data ) { + + var json = JSON.parse( data ); + + var bufferUrl = binaryPath + json.buffers; + + var bufferLoader = new THREE.XHRLoader( scope.manager ); + bufferLoader.setCrossOrigin( scope.crossOrigin ); + bufferLoader.setResponseType( 'arraybuffer' ); + bufferLoader.load( bufferUrl, function ( bufData ) { + + // IEWEBGL needs this ??? + //buffer = ( new Uint8Array( xhr.responseBody ) ).buffer; + + //// iOS and other XMLHttpRequest level 1 ??? + + scope.parse( bufData, onLoad, texturePath, json.materials ); + + }, onProgress, onError ); + + }, onProgress, onError ); + + }, + + setBinaryPath: function ( value ) { + + this.binaryPath = value; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setTexturePath: function ( value ) { + + this.texturePath = value; + + }, + + parse: function ( data, callback, texturePath, jsonMaterials ) { + + var Model = function ( texturePath ) { + + var scope = this, + currentOffset = 0, + md, + normals = [], + uvs = [], + start_tri_flat, start_tri_smooth, start_tri_flat_uv, start_tri_smooth_uv, + start_quad_flat, start_quad_smooth, start_quad_flat_uv, start_quad_smooth_uv, + tri_size, quad_size, + len_tri_flat, len_tri_smooth, len_tri_flat_uv, len_tri_smooth_uv, + len_quad_flat, len_quad_smooth, len_quad_flat_uv, len_quad_smooth_uv; + + + THREE.Geometry.call( this ); + + md = parseMetaData( data, currentOffset ); + + currentOffset += md.header_bytes; + /* + md.vertex_index_bytes = Uint32Array.BYTES_PER_ELEMENT; + md.material_index_bytes = Uint16Array.BYTES_PER_ELEMENT; + md.normal_index_bytes = Uint32Array.BYTES_PER_ELEMENT; + md.uv_index_bytes = Uint32Array.BYTES_PER_ELEMENT; + */ + // buffers sizes + + tri_size = md.vertex_index_bytes * 3 + md.material_index_bytes; + quad_size = md.vertex_index_bytes * 4 + md.material_index_bytes; + + len_tri_flat = md.ntri_flat * ( tri_size ); + len_tri_smooth = md.ntri_smooth * ( tri_size + md.normal_index_bytes * 3 ); + len_tri_flat_uv = md.ntri_flat_uv * ( tri_size + md.uv_index_bytes * 3 ); + len_tri_smooth_uv = md.ntri_smooth_uv * ( tri_size + md.normal_index_bytes * 3 + md.uv_index_bytes * 3 ); + + len_quad_flat = md.nquad_flat * ( quad_size ); + len_quad_smooth = md.nquad_smooth * ( quad_size + md.normal_index_bytes * 4 ); + len_quad_flat_uv = md.nquad_flat_uv * ( quad_size + md.uv_index_bytes * 4 ); + len_quad_smooth_uv = md.nquad_smooth_uv * ( quad_size + md.normal_index_bytes * 4 + md.uv_index_bytes * 4 ); + + // read buffers + + currentOffset += init_vertices( currentOffset ); + + currentOffset += init_normals( currentOffset ); + currentOffset += handlePadding( md.nnormals * 3 ); + + currentOffset += init_uvs( currentOffset ); + + start_tri_flat = currentOffset; + start_tri_smooth = start_tri_flat + len_tri_flat + handlePadding( md.ntri_flat * 2 ); + start_tri_flat_uv = start_tri_smooth + len_tri_smooth + handlePadding( md.ntri_smooth * 2 ); + start_tri_smooth_uv = start_tri_flat_uv + len_tri_flat_uv + handlePadding( md.ntri_flat_uv * 2 ); + + start_quad_flat = start_tri_smooth_uv + len_tri_smooth_uv + handlePadding( md.ntri_smooth_uv * 2 ); + start_quad_smooth = start_quad_flat + len_quad_flat + handlePadding( md.nquad_flat * 2 ); + start_quad_flat_uv = start_quad_smooth + len_quad_smooth + handlePadding( md.nquad_smooth * 2 ); + start_quad_smooth_uv = start_quad_flat_uv + len_quad_flat_uv + handlePadding( md.nquad_flat_uv * 2 ); + + // have to first process faces with uvs + // so that face and uv indices match + + init_triangles_flat_uv( start_tri_flat_uv ); + init_triangles_smooth_uv( start_tri_smooth_uv ); + + init_quads_flat_uv( start_quad_flat_uv ); + init_quads_smooth_uv( start_quad_smooth_uv ); + + // now we can process untextured faces + + init_triangles_flat( start_tri_flat ); + init_triangles_smooth( start_tri_smooth ); + + init_quads_flat( start_quad_flat ); + init_quads_smooth( start_quad_smooth ); + + this.computeFaceNormals(); + + function handlePadding( n ) { + + return ( n % 4 ) ? ( 4 - n % 4 ) : 0; + + } + + function parseMetaData( data, offset ) { + + var metaData = { + + 'signature' : parseString( data, offset, 12 ), + 'header_bytes' : parseUChar8( data, offset + 12 ), + + 'vertex_coordinate_bytes' : parseUChar8( data, offset + 13 ), + 'normal_coordinate_bytes' : parseUChar8( data, offset + 14 ), + 'uv_coordinate_bytes' : parseUChar8( data, offset + 15 ), + + 'vertex_index_bytes' : parseUChar8( data, offset + 16 ), + 'normal_index_bytes' : parseUChar8( data, offset + 17 ), + 'uv_index_bytes' : parseUChar8( data, offset + 18 ), + 'material_index_bytes' : parseUChar8( data, offset + 19 ), + + 'nvertices' : parseUInt32( data, offset + 20 ), + 'nnormals' : parseUInt32( data, offset + 20 + 4 * 1 ), + 'nuvs' : parseUInt32( data, offset + 20 + 4 * 2 ), + + 'ntri_flat' : parseUInt32( data, offset + 20 + 4 * 3 ), + 'ntri_smooth' : parseUInt32( data, offset + 20 + 4 * 4 ), + 'ntri_flat_uv' : parseUInt32( data, offset + 20 + 4 * 5 ), + 'ntri_smooth_uv' : parseUInt32( data, offset + 20 + 4 * 6 ), + + 'nquad_flat' : parseUInt32( data, offset + 20 + 4 * 7 ), + 'nquad_smooth' : parseUInt32( data, offset + 20 + 4 * 8 ), + 'nquad_flat_uv' : parseUInt32( data, offset + 20 + 4 * 9 ), + 'nquad_smooth_uv' : parseUInt32( data, offset + 20 + 4 * 10 ) + + }; + /* + console.log( "signature: " + metaData.signature ); + + console.log( "header_bytes: " + metaData.header_bytes ); + console.log( "vertex_coordinate_bytes: " + metaData.vertex_coordinate_bytes ); + console.log( "normal_coordinate_bytes: " + metaData.normal_coordinate_bytes ); + console.log( "uv_coordinate_bytes: " + metaData.uv_coordinate_bytes ); + + console.log( "vertex_index_bytes: " + metaData.vertex_index_bytes ); + console.log( "normal_index_bytes: " + metaData.normal_index_bytes ); + console.log( "uv_index_bytes: " + metaData.uv_index_bytes ); + console.log( "material_index_bytes: " + metaData.material_index_bytes ); + + console.log( "nvertices: " + metaData.nvertices ); + console.log( "nnormals: " + metaData.nnormals ); + console.log( "nuvs: " + metaData.nuvs ); + + console.log( "ntri_flat: " + metaData.ntri_flat ); + console.log( "ntri_smooth: " + metaData.ntri_smooth ); + console.log( "ntri_flat_uv: " + metaData.ntri_flat_uv ); + console.log( "ntri_smooth_uv: " + metaData.ntri_smooth_uv ); + + console.log( "nquad_flat: " + metaData.nquad_flat ); + console.log( "nquad_smooth: " + metaData.nquad_smooth ); + console.log( "nquad_flat_uv: " + metaData.nquad_flat_uv ); + console.log( "nquad_smooth_uv: " + metaData.nquad_smooth_uv ); + + var total = metaData.header_bytes + + metaData.nvertices * metaData.vertex_coordinate_bytes * 3 + + metaData.nnormals * metaData.normal_coordinate_bytes * 3 + + metaData.nuvs * metaData.uv_coordinate_bytes * 2 + + metaData.ntri_flat * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes ) + + metaData.ntri_smooth * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes + metaData.normal_index_bytes*3 ) + + metaData.ntri_flat_uv * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes + metaData.uv_index_bytes*3 ) + + metaData.ntri_smooth_uv * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes + metaData.normal_index_bytes*3 + metaData.uv_index_bytes*3 ) + + metaData.nquad_flat * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes ) + + metaData.nquad_smooth * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes + metaData.normal_index_bytes*4 ) + + metaData.nquad_flat_uv * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes + metaData.uv_index_bytes*4 ) + + metaData.nquad_smooth_uv * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes + metaData.normal_index_bytes*4 + metaData.uv_index_bytes*4 ); + console.log( "total bytes: " + total ); + */ + + return metaData; + + } + + function parseString( data, offset, length ) { + + var charArray = new Uint8Array( data, offset, length ); + + var text = ""; + + for ( var i = 0; i < length; i ++ ) { + + text += String.fromCharCode( charArray[ offset + i ] ); + + } + + return text; + + } + + function parseUChar8( data, offset ) { + + var charArray = new Uint8Array( data, offset, 1 ); + + return charArray[ 0 ]; + + } + + function parseUInt32( data, offset ) { + + var intArray = new Uint32Array( data, offset, 1 ); + + return intArray[ 0 ]; + + } + + function init_vertices( start ) { + + var nElements = md.nvertices; + + var coordArray = new Float32Array( data, start, nElements * 3 ); + + var i, x, y, z; + + for ( i = 0; i < nElements; i ++ ) { + + x = coordArray[ i * 3 ]; + y = coordArray[ i * 3 + 1 ]; + z = coordArray[ i * 3 + 2 ]; + + scope.vertices.push( new THREE.Vector3( x, y, z ) ); + + } + + return nElements * 3 * Float32Array.BYTES_PER_ELEMENT; + + } + + function init_normals( start ) { + + var nElements = md.nnormals; + + if ( nElements ) { + + var normalArray = new Int8Array( data, start, nElements * 3 ); + + var i, x, y, z; + + for ( i = 0; i < nElements; i ++ ) { + + x = normalArray[ i * 3 ]; + y = normalArray[ i * 3 + 1 ]; + z = normalArray[ i * 3 + 2 ]; + + normals.push( x / 127, y / 127, z / 127 ); + + } + + } + + return nElements * 3 * Int8Array.BYTES_PER_ELEMENT; + + } + + function init_uvs( start ) { + + var nElements = md.nuvs; + + if ( nElements ) { + + var uvArray = new Float32Array( data, start, nElements * 2 ); + + var i, u, v; + + for ( i = 0; i < nElements; i ++ ) { + + u = uvArray[ i * 2 ]; + v = uvArray[ i * 2 + 1 ]; + + uvs.push( u, v ); + + } + + } + + return nElements * 2 * Float32Array.BYTES_PER_ELEMENT; + + } + + function init_uvs3( nElements, offset ) { + + var i, uva, uvb, uvc, u1, u2, u3, v1, v2, v3; + + var uvIndexBuffer = new Uint32Array( data, offset, 3 * nElements ); + + for ( i = 0; i < nElements; i ++ ) { + + uva = uvIndexBuffer[ i * 3 ]; + uvb = uvIndexBuffer[ i * 3 + 1 ]; + uvc = uvIndexBuffer[ i * 3 + 2 ]; + + u1 = uvs[ uva * 2 ]; + v1 = uvs[ uva * 2 + 1 ]; + + u2 = uvs[ uvb * 2 ]; + v2 = uvs[ uvb * 2 + 1 ]; + + u3 = uvs[ uvc * 2 ]; + v3 = uvs[ uvc * 2 + 1 ]; + + scope.faceVertexUvs[ 0 ].push( [ + new THREE.Vector2( u1, v1 ), + new THREE.Vector2( u2, v2 ), + new THREE.Vector2( u3, v3 ) + ] ); + + } + + } + + function init_uvs4( nElements, offset ) { + + var i, uva, uvb, uvc, uvd, u1, u2, u3, u4, v1, v2, v3, v4; + + var uvIndexBuffer = new Uint32Array( data, offset, 4 * nElements ); + + for ( i = 0; i < nElements; i ++ ) { + + uva = uvIndexBuffer[ i * 4 ]; + uvb = uvIndexBuffer[ i * 4 + 1 ]; + uvc = uvIndexBuffer[ i * 4 + 2 ]; + uvd = uvIndexBuffer[ i * 4 + 3 ]; + + u1 = uvs[ uva * 2 ]; + v1 = uvs[ uva * 2 + 1 ]; + + u2 = uvs[ uvb * 2 ]; + v2 = uvs[ uvb * 2 + 1 ]; + + u3 = uvs[ uvc * 2 ]; + v3 = uvs[ uvc * 2 + 1 ]; + + u4 = uvs[ uvd * 2 ]; + v4 = uvs[ uvd * 2 + 1 ]; + + scope.faceVertexUvs[ 0 ].push( [ + new THREE.Vector2( u1, v1 ), + new THREE.Vector2( u2, v2 ), + new THREE.Vector2( u4, v4 ) + ] ); + + scope.faceVertexUvs[ 0 ].push( [ + new THREE.Vector2( u2, v2 ), + new THREE.Vector2( u3, v3 ), + new THREE.Vector2( u4, v4 ) + ] ); + + } + + } + + function init_faces3_flat( nElements, offsetVertices, offsetMaterials ) { + + var i, a, b, c, m; + + var vertexIndexBuffer = new Uint32Array( data, offsetVertices, 3 * nElements ); + var materialIndexBuffer = new Uint16Array( data, offsetMaterials, nElements ); + + for ( i = 0; i < nElements; i ++ ) { + + a = vertexIndexBuffer[ i * 3 ]; + b = vertexIndexBuffer[ i * 3 + 1 ]; + c = vertexIndexBuffer[ i * 3 + 2 ]; + + m = materialIndexBuffer[ i ]; + + scope.faces.push( new THREE.Face3( a, b, c, null, null, m ) ); + + } + + } + + function init_faces4_flat( nElements, offsetVertices, offsetMaterials ) { + + var i, a, b, c, d, m; + + var vertexIndexBuffer = new Uint32Array( data, offsetVertices, 4 * nElements ); + var materialIndexBuffer = new Uint16Array( data, offsetMaterials, nElements ); + + for ( i = 0; i < nElements; i ++ ) { + + a = vertexIndexBuffer[ i * 4 ]; + b = vertexIndexBuffer[ i * 4 + 1 ]; + c = vertexIndexBuffer[ i * 4 + 2 ]; + d = vertexIndexBuffer[ i * 4 + 3 ]; + + m = materialIndexBuffer[ i ]; + + scope.faces.push( new THREE.Face3( a, b, d, null, null, m ) ); + scope.faces.push( new THREE.Face3( b, c, d, null, null, m ) ); + + } + + } + + function init_faces3_smooth( nElements, offsetVertices, offsetNormals, offsetMaterials ) { + + var i, a, b, c, m; + var na, nb, nc; + + var vertexIndexBuffer = new Uint32Array( data, offsetVertices, 3 * nElements ); + var normalIndexBuffer = new Uint32Array( data, offsetNormals, 3 * nElements ); + var materialIndexBuffer = new Uint16Array( data, offsetMaterials, nElements ); + + for ( i = 0; i < nElements; i ++ ) { + + a = vertexIndexBuffer[ i * 3 ]; + b = vertexIndexBuffer[ i * 3 + 1 ]; + c = vertexIndexBuffer[ i * 3 + 2 ]; + + na = normalIndexBuffer[ i * 3 ]; + nb = normalIndexBuffer[ i * 3 + 1 ]; + nc = normalIndexBuffer[ i * 3 + 2 ]; + + m = materialIndexBuffer[ i ]; + + var nax = normals[ na * 3 ], + nay = normals[ na * 3 + 1 ], + naz = normals[ na * 3 + 2 ], + + nbx = normals[ nb * 3 ], + nby = normals[ nb * 3 + 1 ], + nbz = normals[ nb * 3 + 2 ], + + ncx = normals[ nc * 3 ], + ncy = normals[ nc * 3 + 1 ], + ncz = normals[ nc * 3 + 2 ]; + + scope.faces.push( new THREE.Face3( a, b, c, [ + new THREE.Vector3( nax, nay, naz ), + new THREE.Vector3( nbx, nby, nbz ), + new THREE.Vector3( ncx, ncy, ncz ) + ], null, m ) ); + + } + + } + + function init_faces4_smooth( nElements, offsetVertices, offsetNormals, offsetMaterials ) { + + var i, a, b, c, d, m; + var na, nb, nc, nd; + + var vertexIndexBuffer = new Uint32Array( data, offsetVertices, 4 * nElements ); + var normalIndexBuffer = new Uint32Array( data, offsetNormals, 4 * nElements ); + var materialIndexBuffer = new Uint16Array( data, offsetMaterials, nElements ); + + for ( i = 0; i < nElements; i ++ ) { + + a = vertexIndexBuffer[ i * 4 ]; + b = vertexIndexBuffer[ i * 4 + 1 ]; + c = vertexIndexBuffer[ i * 4 + 2 ]; + d = vertexIndexBuffer[ i * 4 + 3 ]; + + na = normalIndexBuffer[ i * 4 ]; + nb = normalIndexBuffer[ i * 4 + 1 ]; + nc = normalIndexBuffer[ i * 4 + 2 ]; + nd = normalIndexBuffer[ i * 4 + 3 ]; + + m = materialIndexBuffer[ i ]; + + var nax = normals[ na * 3 ], + nay = normals[ na * 3 + 1 ], + naz = normals[ na * 3 + 2 ], + + nbx = normals[ nb * 3 ], + nby = normals[ nb * 3 + 1 ], + nbz = normals[ nb * 3 + 2 ], + + ncx = normals[ nc * 3 ], + ncy = normals[ nc * 3 + 1 ], + ncz = normals[ nc * 3 + 2 ], + + ndx = normals[ nd * 3 ], + ndy = normals[ nd * 3 + 1 ], + ndz = normals[ nd * 3 + 2 ]; + + scope.faces.push( new THREE.Face3( a, b, d, [ + new THREE.Vector3( nax, nay, naz ), + new THREE.Vector3( nbx, nby, nbz ), + new THREE.Vector3( ndx, ndy, ndz ) + ], null, m ) ); + + scope.faces.push( new THREE.Face3( b, c, d, [ + new THREE.Vector3( nbx, nby, nbz ), + new THREE.Vector3( ncx, ncy, ncz ), + new THREE.Vector3( ndx, ndy, ndz ) + ], null, m ) ); + + } + + } + + function init_triangles_flat( start ) { + + var nElements = md.ntri_flat; + + if ( nElements ) { + + var offsetMaterials = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; + init_faces3_flat( nElements, start, offsetMaterials ); + + } + + } + + function init_triangles_flat_uv( start ) { + + var nElements = md.ntri_flat_uv; + + if ( nElements ) { + + var offsetUvs = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; + var offsetMaterials = offsetUvs + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; + + init_faces3_flat( nElements, start, offsetMaterials ); + init_uvs3( nElements, offsetUvs ); + + } + + } + + function init_triangles_smooth( start ) { + + var nElements = md.ntri_smooth; + + if ( nElements ) { + + var offsetNormals = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; + var offsetMaterials = offsetNormals + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; + + init_faces3_smooth( nElements, start, offsetNormals, offsetMaterials ); + + } + + } + + function init_triangles_smooth_uv( start ) { + + var nElements = md.ntri_smooth_uv; + + if ( nElements ) { + + var offsetNormals = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; + var offsetUvs = offsetNormals + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; + var offsetMaterials = offsetUvs + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; + + init_faces3_smooth( nElements, start, offsetNormals, offsetMaterials ); + init_uvs3( nElements, offsetUvs ); + + } + + } + + function init_quads_flat( start ) { + + var nElements = md.nquad_flat; + + if ( nElements ) { + + var offsetMaterials = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; + init_faces4_flat( nElements, start, offsetMaterials ); + + } + + } + + function init_quads_flat_uv( start ) { + + var nElements = md.nquad_flat_uv; + + if ( nElements ) { + + var offsetUvs = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; + var offsetMaterials = offsetUvs + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; + + init_faces4_flat( nElements, start, offsetMaterials ); + init_uvs4( nElements, offsetUvs ); + + } + + } + + function init_quads_smooth( start ) { + + var nElements = md.nquad_smooth; + + if ( nElements ) { + + var offsetNormals = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; + var offsetMaterials = offsetNormals + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; + + init_faces4_smooth( nElements, start, offsetNormals, offsetMaterials ); + + } + + } + + function init_quads_smooth_uv( start ) { + + var nElements = md.nquad_smooth_uv; + + if ( nElements ) { + + var offsetNormals = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; + var offsetUvs = offsetNormals + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; + var offsetMaterials = offsetUvs + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; + + init_faces4_smooth( nElements, start, offsetNormals, offsetMaterials ); + init_uvs4( nElements, offsetUvs ); + + } + + } + + }; + + Model.prototype = Object.create( THREE.Geometry.prototype ); + Model.prototype.constructor = Model; + + var geometry = new Model( texturePath ); + var materials = THREE.Loader.prototype.initMaterials( jsonMaterials, texturePath, this.crossOrigin ); + + callback( geometry, materials ); + + } + +}; diff --git a/node_modules/three/examples/js/loaders/ColladaLoader.js b/node_modules/three/examples/js/loaders/ColladaLoader.js new file mode 100644 index 00000000..1a68a0e8 --- /dev/null +++ b/node_modules/three/examples/js/loaders/ColladaLoader.js @@ -0,0 +1,5579 @@ +/** +* @author Tim Knip / http://www.floorplanner.com/ / tim at floorplanner.com +* @author Tony Parisi / http://www.tonyparisi.com/ +*/ + + +( function() { + + var COLLADA = null; + var scene = null; + var visualScene; + var kinematicsModel; + + var readyCallbackFunc = null; + + var sources = {}; + var images = {}; + var animations = {}; + var controllers = {}; + var geometries = {}; + var materials = {}; + var effects = {}; + var cameras = {}; + var lights = {}; + + var animData; + var kinematics; + var visualScenes; + var kinematicsModels; + var baseUrl; + var morphs; + var skins; + + var flip_uv = true; + var preferredShading = THREE.SmoothShading; + + var colladaUnit = 1.0; + var colladaUp = 'Y'; + var upConversion = null; + + var options = { + // Force Geometry to always be centered at the local origin of the + // containing Mesh. + centerGeometry: false, + + // Axis conversion is done for geometries, animations, and controllers. + // If we ever pull cameras or lights out of the COLLADA file, they'll + // need extra work. + convertUpAxis: false, + + subdivideFaces: true, + + upAxis: 'Y', + + // For reflective or refractive materials we'll use this cubemap + defaultEnvMap: null + + }; + + THREE.ColladaLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + /* + + return { + + load: load, + parse: parse, + setPreferredShading: setPreferredShading, + applySkin: applySkin, + geometries : geometries, + options: options + + }; + + */ + + }; + + THREE.ColladaLoader.prototype = { + + constructor: THREE.ColladaLoader, + + options: options, //hack + + load: function ( url, onLoad, onProgress, onError ) { + + var length = 0; + + if ( document.implementation && document.implementation.createDocument ) { + + var scope = this; + + var loader = new THREE.XHRLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + var parts = url.split( '/' ); + parts.pop(); + baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/'; + + onLoad( scope.parse( text, url ) ); + + }, onProgress, onError ); + + } else { + + alert( "Don't know how to parse XML!" ); + + } + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function( text ) { + + COLLADA = new DOMParser().parseFromString( text, 'application/xml' ); + + this.parseAsset(); + this.setUpConversion(); + images = this.parseLib( "library_images image", _Image, "image" ); + materials = this.parseLib( "library_materials material", Material, "material" ); + effects = this.parseLib( "library_effects effect", Effect, "effect" ); + geometries = this.parseLib( "library_geometries geometry", Geometry, "geometry" ); + cameras = this.parseLib( "library_cameras camera", Camera, "camera" ); + lights = this.parseLib( "library_lights light", Light, "light" ); + controllers = this.parseLib( "library_controllers controller", Controller, "controller" ); + animations = this.parseLib( "library_animations animation", Animation, "animation" ); + visualScenes = this.parseLib( "library_visual_scenes visual_scene", VisualScene, "visual_scene" ); + kinematicsModels = this.parseLib( "library_kinematics_models kinematics_model", KinematicsModel, "kinematics_model" ); + + morphs = []; + skins = []; + + visualScene = this.parseScene(); + scene = new THREE.Group(); + + for ( var i = 0; i < visualScene.nodes.length; i ++ ) { + + scene.add( createSceneGraph( visualScene.nodes[ i ] ) ); + + } + + // unit conversion + scene.scale.multiplyScalar( colladaUnit ); + + this.createAnimations(); + + kinematicsModel = this.parseKinematicsModel(); + this.createKinematics(); + + var result = { + scene: scene, + morphs: morphs, + skins: skins, + animations: animData, + kinematics: kinematics, + dae: { + images: images, + materials: materials, + cameras: cameras, + lights: lights, + effects: effects, + geometries: geometries, + controllers: controllers, + animations: animations, + visualScenes: visualScenes, + visualScene: visualScene, + scene: visualScene, + kinematicsModels: kinematicsModels, + kinematicsModel: kinematicsModel + } + }; + + return result; + + }, + + setPreferredShading: function ( shading ) { + + preferredShading = shading; + + }, + + parseAsset: function () { + + var elements = COLLADA.querySelectorAll( 'asset' ); + + var element = elements[ 0 ]; + + if ( element && element.childNodes ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'unit': + + var meter = child.getAttribute( 'meter' ); + + if ( meter ) { + + colladaUnit = parseFloat( meter ); + + } + + break; + + case 'up_axis': + + colladaUp = child.textContent.charAt( 0 ); + break; + + } + + } + + } + + }, + + parseLib: function ( q, classSpec, prefix ) { + + var elements = COLLADA.querySelectorAll( q ); + + var lib = {}; + + var i = 0; + + var elementsLength = elements.length; + + for ( var j = 0; j < elementsLength; j ++ ) { + + var element = elements[ j ]; + var daeElement = ( new classSpec() ).parse( element ); + + if ( ! daeElement.id || daeElement.id.length === 0 ) daeElement.id = prefix + ( i ++ ); + lib[ daeElement.id ] = daeElement; + + } + + return lib; + + }, + + parseScene: function () { + + var sceneElement = COLLADA.querySelectorAll( 'scene instance_visual_scene' )[ 0 ]; + + if ( sceneElement ) { + + var url = sceneElement.getAttribute( 'url' ).replace( /^#/, '' ); + return visualScenes[ url.length > 0 ? url : 'visual_scene0' ]; + + } else { + + return null; + + } + + }, + + parseKinematicsModel: function () { + + var kinematicsModelElement = COLLADA.querySelectorAll( 'instance_kinematics_model' )[ 0 ]; + + if ( kinematicsModelElement ) { + + var url = kinematicsModelElement.getAttribute( 'url' ).replace( /^#/, '' ); + return kinematicsModels[ url.length > 0 ? url : 'kinematics_model0' ]; + + } else { + + return null; + + } + + }, + + createAnimations: function () { + + animData = []; + + // fill in the keys + recurseHierarchy( scene ); + + }, + + recurseHierarchy: function ( node ) { + + var n = visualScene.getChildById( node.colladaId, true ), + newData = null; + + if ( n && n.keys ) { + + newData = { + fps: 60, + hierarchy: [ { + node: n, + keys: n.keys, + sids: n.sids + } ], + node: node, + name: 'animation_' + node.name, + length: 0 + }; + + animData.push( newData ); + + for ( var i = 0, il = n.keys.length; i < il; i ++ ) { + + newData.length = Math.max( newData.length, n.keys[ i ].time ); + + } + + } else { + + newData = { + hierarchy: [ { + keys: [], + sids: [] + } ] + } + + } + + for ( var i = 0, il = node.children.length; i < il; i ++ ) { + + var d = recurseHierarchy( node.children[ i ] ); + + for ( var j = 0, jl = d.hierarchy.length; j < jl; j ++ ) { + + newData.hierarchy.push( { + keys: [], + sids: [] + } ); + + } + + } + + return newData; + + }, + + createMorph: function ( geometry, ctrl ) { + + var morphCtrl = ctrl instanceof InstanceController ? controllers[ ctrl.url ] : ctrl; + + if ( ! morphCtrl || ! morphCtrl.morph ) { + + console.log( "could not find morph controller!" ); + return; + + } + + var morph = morphCtrl.morph; + + for ( var i = 0; i < morph.targets.length; i ++ ) { + + var target_id = morph.targets[ i ]; + var daeGeometry = geometries[ target_id ]; + + if ( ! daeGeometry.mesh || + ! daeGeometry.mesh.primitives || + ! daeGeometry.mesh.primitives.length ) { + + continue; + + } + + var target = daeGeometry.mesh.primitives[ 0 ].geometry; + + if ( target.vertices.length === geometry.vertices.length ) { + + geometry.morphTargets.push( { name: "target_1", vertices: target.vertices } ); + + } + + } + + geometry.morphTargets.push( { name: "target_Z", vertices: geometry.vertices } ); + + }, + + createSkin: function ( geometry, ctrl, applyBindShape ) { + + var skinCtrl = controllers[ ctrl.url ]; + + if ( ! skinCtrl || ! skinCtrl.skin ) { + + console.log( "could not find skin controller!" ); + return; + + } + + if ( ! ctrl.skeleton || ! ctrl.skeleton.length ) { + + console.log( "could not find the skeleton for the skin!" ); + return; + + } + + var skin = skinCtrl.skin; + var skeleton = visualScene.getChildById( ctrl.skeleton[ 0 ] ); + var hierarchy = []; + + applyBindShape = applyBindShape !== undefined ? applyBindShape : true; + + var bones = []; + geometry.skinWeights = []; + geometry.skinIndices = []; + + //createBones( geometry.bones, skin, hierarchy, skeleton, null, -1 ); + //createWeights( skin, geometry.bones, geometry.skinIndices, geometry.skinWeights ); + + /* + geometry.animation = { + name: 'take_001', + fps: 30, + length: 2, + JIT: true, + hierarchy: hierarchy + }; + */ + + if ( applyBindShape ) { + + for ( var i = 0; i < geometry.vertices.length; i ++ ) { + + geometry.vertices[ i ].applyMatrix4( skin.bindShapeMatrix ); + + } + + } + + }, + + createKinematics: function () { + + if ( kinematicsModel && kinematicsModel.joints.length === 0 ) { + + kinematics = undefined; + return; + + } + + var jointMap = {}; + + var _addToMap = function( jointIndex, parentVisualElement ) { + + var parentVisualElementId = parentVisualElement.getAttribute( 'id' ); + var colladaNode = visualScene.getChildById( parentVisualElementId, true ); + var joint = kinematicsModel.joints[ jointIndex ]; + + scene.traverse( function( node ) { + + if ( node.colladaId == parentVisualElementId ) { + + jointMap[ jointIndex ] = { + node: node, + transforms: colladaNode.transforms, + joint: joint, + position: joint.zeroPosition + }; + + } + + } ); + + }; + + kinematics = { + + joints: kinematicsModel && kinematicsModel.joints, + + getJointValue: function( jointIndex ) { + + var jointData = jointMap[ jointIndex ]; + + if ( jointData ) { + + return jointData.position; + + } else { + + console.log( 'getJointValue: joint ' + jointIndex + ' doesn\'t exist' ); + + } + + }, + + setJointValue: function( jointIndex, value ) { + + var jointData = jointMap[ jointIndex ]; + + if ( jointData ) { + + var joint = jointData.joint; + + if ( value > joint.limits.max || value < joint.limits.min ) { + + console.log( 'setJointValue: joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ')' ); + + } else if ( joint.static ) { + + console.log( 'setJointValue: joint ' + jointIndex + ' is static' ); + + } else { + + var threejsNode = jointData.node; + var axis = joint.axis; + var transforms = jointData.transforms; + + var matrix = new THREE.Matrix4(); + + for ( i = 0; i < transforms.length; i ++ ) { + + var transform = transforms[ i ]; + + // kinda ghetto joint detection + if ( transform.sid && transform.sid.indexOf( 'joint' + jointIndex ) !== - 1 ) { + + // apply actual joint value here + switch ( joint.type ) { + + case 'revolute': + + matrix.multiply( m1.makeRotationAxis( axis, THREE.Math.degToRad( value ) ) ); + break; + + case 'prismatic': + + matrix.multiply( m1.makeTranslation( axis.x * value, axis.y * value, axis.z * value ) ); + break; + + default: + + console.warn( 'setJointValue: unknown joint type: ' + joint.type ); + break; + + } + + } else { + + var m1 = new THREE.Matrix4(); + + switch ( transform.type ) { + + case 'matrix': + + matrix.multiply( transform.obj ); + + break; + + case 'translate': + + matrix.multiply( m1.makeTranslation( transform.obj.x, transform.obj.y, transform.obj.z ) ); + + break; + + case 'rotate': + + matrix.multiply( m1.makeRotationAxis( transform.obj, transform.angle ) ); + + break; + + } + + } + + } + + // apply the matrix to the threejs node + var elementsFloat32Arr = matrix.elements; + var elements = Array.prototype.slice.call( elementsFloat32Arr ); + + var elementsRowMajor = [ + elements[ 0 ], + elements[ 4 ], + elements[ 8 ], + elements[ 12 ], + elements[ 1 ], + elements[ 5 ], + elements[ 9 ], + elements[ 13 ], + elements[ 2 ], + elements[ 6 ], + elements[ 10 ], + elements[ 14 ], + elements[ 3 ], + elements[ 7 ], + elements[ 11 ], + elements[ 15 ] + ]; + + threejsNode.matrix.set.apply( threejsNode.matrix, elementsRowMajor ); + threejsNode.matrix.decompose( threejsNode.position, threejsNode.quaternion, threejsNode.scale ); + + } + + } else { + + console.log( 'setJointValue: joint ' + jointIndex + ' doesn\'t exist' ); + + } + + } + + }; + + var element = COLLADA.querySelector( 'scene instance_kinematics_scene' ); + + if ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'bind_joint_axis': + + var visualTarget = child.getAttribute( 'target' ).split( '/' ).pop(); + var axis = child.querySelector( 'axis param' ).textContent; + var jointIndex = parseInt( axis.split( 'joint' ).pop().split( '.' )[ 0 ] ); + var visualTargetElement = COLLADA.querySelector( '[sid="' + visualTarget + '"]' ); + + if ( visualTargetElement ) { + + var parentVisualElement = visualTargetElement.parentElement; + _addToMap( jointIndex, parentVisualElement ); + + } + + break; + + default: + + break; + + } + + } + + } + + }, + + getJointId: function ( skin, id ) { + + for ( var i = 0; i < skin.joints.length; i ++ ) { + + if ( skin.joints[ i ] === id ) { + + return i; + + } + + } + + }, + + calcFrameDuration: function ( node ) { + + var minT = 10000000; + + for ( var i = 0; i < node.channels.length; i ++ ) { + + var sampler = node.channels[ i ].sampler; + + for ( var j = 0; j < sampler.input.length - 1; j ++ ) { + + var t0 = sampler.input[ j ]; + var t1 = sampler.input[ j + 1 ]; + minT = Math.min( minT, t1 - t0 ); + + } + + } + + return minT; + + }, + + calcMatrixAt: function ( node, t ) { + + var animated = {}; + + var i, j; + + for ( i = 0; i < node.channels.length; i ++ ) { + + var channel = node.channels[ i ]; + animated[ channel.sid ] = channel; + + } + + var matrix = new THREE.Matrix4(); + + for ( i = 0; i < node.transforms.length; i ++ ) { + + var transform = node.transforms[ i ]; + var channel = animated[ transform.sid ]; + + if ( channel !== undefined ) { + + var sampler = channel.sampler; + var value; + + for ( j = 0; j < sampler.input.length - 1; j ++ ) { + + if ( sampler.input[ j + 1 ] > t ) { + + value = sampler.output[ j ]; + //console.log(value.flatten) + break; + + } + + } + + if ( value !== undefined ) { + + if ( value instanceof THREE.Matrix4 ) { + + matrix.multiplyMatrices( matrix, value ); + + } else { + + // FIXME: handle other types + + matrix.multiplyMatrices( matrix, transform.matrix ); + + } + + } else { + + matrix.multiplyMatrices( matrix, transform.matrix ); + + } + + } else { + + matrix.multiplyMatrices( matrix, transform.matrix ); + + } + + } + + return matrix; + + }, + + // Up axis conversion + setUpConversion: function () { + + if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { + + upConversion = null; + + } else { + + switch ( colladaUp ) { + + case 'X': + + upConversion = options.upAxis === 'Y' ? 'XtoY' : 'XtoZ'; + break; + + case 'Y': + + upConversion = options.upAxis === 'X' ? 'YtoX' : 'YtoZ'; + break; + + case 'Z': + + upConversion = options.upAxis === 'X' ? 'ZtoX' : 'ZtoY'; + break; + + } + + } + + } + + + }; + + function _Image() { + + this.id = ""; + this.init_from = ""; + + }; + + _Image.prototype.parse = function( element ) { + + this.id = element.getAttribute( 'id' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeName === 'init_from' ) { + + this.init_from = child.textContent; + + } + + } + + return this; + + }; + + function Controller() { + + this.id = ""; + this.name = ""; + this.type = ""; + this.skin = null; + this.morph = null; + + }; + + Controller.prototype.parse = function( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + this.type = "none"; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'skin': + + this.skin = ( new Skin() ).parse( child ); + this.type = child.nodeName; + break; + + case 'morph': + + this.morph = ( new Morph() ).parse( child ); + this.type = child.nodeName; + break; + + default: + break; + + } + + } + + return this; + + }; + + function Morph() { + + this.method = null; + this.source = null; + this.targets = null; + this.weights = null; + + }; + + Morph.prototype.parse = function( element ) { + + var sources = {}; + var inputs = []; + var i; + + this.method = element.getAttribute( 'method' ); + this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); + + for ( i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'source': + + var source = ( new Source() ).parse( child ); + sources[ source.id ] = source; + break; + + case 'targets': + + inputs = this.parseInputs( child ); + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + for ( i = 0; i < inputs.length; i ++ ) { + + var input = inputs[ i ]; + var source = sources[ input.source ]; + + switch ( input.semantic ) { + + case 'MORPH_TARGET': + + this.targets = source.read(); + break; + + case 'MORPH_WEIGHT': + + this.weights = source.read(); + break; + + default: + break; + + } + + } + + return this; + + }; + + Morph.prototype.parseInputs = function( element ) { + + var inputs = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + + inputs.push( ( new Input() ).parse( child ) ); + break; + + default: + break; + } + + } + + return inputs; + + }; + + function Skin() { + + this.source = ""; + this.bindShapeMatrix = null; + this.invBindMatrices = []; + this.joints = []; + this.weights = []; + + }; + + Skin.prototype.parse = function( element ) { + + var sources = {}; + var joints, weights; + + this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); + this.invBindMatrices = []; + this.joints = []; + this.weights = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'bind_shape_matrix': + + var f = _floats( child.textContent ); + this.bindShapeMatrix = getConvertedMat4( f ); + break; + + case 'source': + + var src = new Source().parse( child ); + sources[ src.id ] = src; + break; + + case 'joints': + + joints = child; + break; + + case 'vertex_weights': + + weights = child; + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + this.parseJoints( joints, sources ); + this.parseWeights( weights, sources ); + + return this; + + }; + + Skin.prototype.parseJoints = function ( element, sources ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + + var input = ( new Input() ).parse( child ); + var source = sources[ input.source ]; + + if ( input.semantic === 'JOINT' ) { + + this.joints = source.read(); + + } else if ( input.semantic === 'INV_BIND_MATRIX' ) { + + this.invBindMatrices = source.read(); + + } + + break; + + default: + break; + } + + } + + }; + + Skin.prototype.parseWeights = function ( element, sources ) { + + var v, vcount, inputs = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + + inputs.push( ( new Input() ).parse( child ) ); + break; + + case 'v': + + v = _ints( child.textContent ); + break; + + case 'vcount': + + vcount = _ints( child.textContent ); + break; + + default: + break; + + } + + } + + var index = 0; + + for ( var i = 0; i < vcount.length; i ++ ) { + + var numBones = vcount[ i ]; + var vertex_weights = []; + + for ( var j = 0; j < numBones; j ++ ) { + + var influence = {}; + + for ( var k = 0; k < inputs.length; k ++ ) { + + var input = inputs[ k ]; + var value = v[ index + input.offset ]; + + switch ( input.semantic ) { + + case 'JOINT': + + influence.joint = value;//this.joints[value]; + break; + + case 'WEIGHT': + + influence.weight = sources[ input.source ].data[ value ]; + break; + + default: + break; + + } + + } + + vertex_weights.push( influence ); + index += inputs.length; + + } + + for ( var j = 0; j < vertex_weights.length; j ++ ) { + + vertex_weights[ j ].index = i; + + } + + this.weights.push( vertex_weights ); + + } + + }; + + function VisualScene () { + + this.id = ""; + this.name = ""; + this.nodes = []; + this.scene = new THREE.Group(); + + }; + + VisualScene.prototype.getChildById = function( id, recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var node = this.nodes[ i ].getChildById( id, recursive ); + + if ( node ) { + + return node; + + } + + } + + return null; + + }; + + VisualScene.prototype.getChildBySid = function( sid, recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var node = this.nodes[ i ].getChildBySid( sid, recursive ); + + if ( node ) { + + return node; + + } + + } + + return null; + + }; + + VisualScene.prototype.parse = function( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + this.nodes = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'node': + + this.nodes.push( ( new Node() ).parse( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Node() { + + this.id = ""; + this.name = ""; + this.sid = ""; + this.nodes = []; + this.controllers = []; + this.transforms = []; + this.geometries = []; + this.channels = []; + this.matrix = new THREE.Matrix4(); + + }; + + Node.prototype.getChannelForTransform = function( transformSid ) { + + for ( var i = 0; i < this.channels.length; i ++ ) { + + var channel = this.channels[ i ]; + var parts = channel.target.split( '/' ); + var id = parts.shift(); + var sid = parts.shift(); + var dotSyntax = ( sid.indexOf( "." ) >= 0 ); + var arrSyntax = ( sid.indexOf( "(" ) >= 0 ); + var arrIndices; + var member; + + if ( dotSyntax ) { + + parts = sid.split( "." ); + sid = parts.shift(); + member = parts.shift(); + + } else if ( arrSyntax ) { + + arrIndices = sid.split( "(" ); + sid = arrIndices.shift(); + + for ( var j = 0; j < arrIndices.length; j ++ ) { + + arrIndices[ j ] = parseInt( arrIndices[ j ].replace( /\)/, '' ) ); + + } + + } + + if ( sid === transformSid ) { + + channel.info = { sid: sid, dotSyntax: dotSyntax, arrSyntax: arrSyntax, arrIndices: arrIndices }; + return channel; + + } + + } + + return null; + + }; + + Node.prototype.getChildById = function ( id, recursive ) { + + if ( this.id === id ) { + + return this; + + } + + if ( recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var n = this.nodes[ i ].getChildById( id, recursive ); + + if ( n ) { + + return n; + + } + + } + + } + + return null; + + }; + + Node.prototype.getChildBySid = function ( sid, recursive ) { + + if ( this.sid === sid ) { + + return this; + + } + + if ( recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var n = this.nodes[ i ].getChildBySid( sid, recursive ); + + if ( n ) { + + return n; + + } + + } + + } + + return null; + + }; + + Node.prototype.getTransformBySid = function ( sid ) { + + for ( var i = 0; i < this.transforms.length; i ++ ) { + + if ( this.transforms[ i ].sid === sid ) return this.transforms[ i ]; + + } + + return null; + + }; + + Node.prototype.parse = function( element ) { + + var url; + + this.id = element.getAttribute( 'id' ); + this.sid = element.getAttribute( 'sid' ); + this.name = element.getAttribute( 'name' ); + this.type = element.getAttribute( 'type' ); + this.layer = element.getAttribute( 'layer' ); + + this.type = this.type === 'JOINT' ? this.type : 'NODE'; + + this.nodes = []; + this.transforms = []; + this.geometries = []; + this.cameras = []; + this.lights = []; + this.controllers = []; + this.matrix = new THREE.Matrix4(); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'node': + + this.nodes.push( ( new Node() ).parse( child ) ); + break; + + case 'instance_camera': + + this.cameras.push( ( new InstanceCamera() ).parse( child ) ); + break; + + case 'instance_controller': + + this.controllers.push( ( new InstanceController() ).parse( child ) ); + break; + + case 'instance_geometry': + + this.geometries.push( ( new InstanceGeometry() ).parse( child ) ); + break; + + case 'instance_light': + + this.lights.push( ( new InstanceLight() ).parse( child ) ); + break; + + case 'instance_node': + + url = child.getAttribute( 'url' ).replace( /^#/, '' ); + var iNode = getLibraryNode( url ); + + if ( iNode ) { + + this.nodes.push( ( new Node() ).parse( iNode ) ) ; + + } + + break; + + case 'rotate': + case 'translate': + case 'scale': + case 'matrix': + case 'lookat': + case 'skew': + + this.transforms.push( ( new Transform() ).parse( child ) ); + break; + + case 'extra': + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + this.channels = getChannelsForNode( this ); + bakeAnimations( this ); + + this.updateMatrix(); + + return this; + + }; + + Node.prototype.updateMatrix = function () { + + this.matrix.identity(); + + for ( var i = 0; i < this.transforms.length; i ++ ) { + + this.transforms[ i ].apply( this.matrix ); + + } + + }; + + function Transform () { + + this.sid = ""; + this.type = ""; + this.data = []; + this.obj = null; + + }; + + Transform.prototype.parse = function ( element ) { + + this.sid = element.getAttribute( 'sid' ); + this.type = element.nodeName; + this.data = _floats( element.textContent ); + this.convert(); + + return this; + + }; + + Transform.prototype.convert = function () { + + switch ( this.type ) { + + case 'matrix': + + this.obj = getConvertedMat4( this.data ); + break; + + case 'rotate': + + this.angle = THREE.Math.degToRad( this.data[ 3 ] ); + + case 'translate': + + fixCoords( this.data, - 1 ); + this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); + break; + + case 'scale': + + fixCoords( this.data, 1 ); + this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); + break; + + default: + console.log( 'Can not convert Transform of type ' + this.type ); + break; + + } + + }; + + Transform.prototype.apply = function () { + + var m1 = new THREE.Matrix4(); + + return function ( matrix ) { + + switch ( this.type ) { + + case 'matrix': + + matrix.multiply( this.obj ); + + break; + + case 'translate': + + matrix.multiply( m1.makeTranslation( this.obj.x, this.obj.y, this.obj.z ) ); + + break; + + case 'rotate': + + matrix.multiply( m1.makeRotationAxis( this.obj, this.angle ) ); + + break; + + case 'scale': + + matrix.scale( this.obj ); + + break; + + } + + }; + + }(); + + Transform.prototype.update = function ( data, member ) { + + var members = [ 'X', 'Y', 'Z', 'ANGLE' ]; + + switch ( this.type ) { + + case 'matrix': + + if ( ! member ) { + + this.obj.copy( data ); + + } else if ( member.length === 1 ) { + + switch ( member[ 0 ] ) { + + case 0: + + this.obj.n11 = data[ 0 ]; + this.obj.n21 = data[ 1 ]; + this.obj.n31 = data[ 2 ]; + this.obj.n41 = data[ 3 ]; + + break; + + case 1: + + this.obj.n12 = data[ 0 ]; + this.obj.n22 = data[ 1 ]; + this.obj.n32 = data[ 2 ]; + this.obj.n42 = data[ 3 ]; + + break; + + case 2: + + this.obj.n13 = data[ 0 ]; + this.obj.n23 = data[ 1 ]; + this.obj.n33 = data[ 2 ]; + this.obj.n43 = data[ 3 ]; + + break; + + case 3: + + this.obj.n14 = data[ 0 ]; + this.obj.n24 = data[ 1 ]; + this.obj.n34 = data[ 2 ]; + this.obj.n44 = data[ 3 ]; + + break; + + } + + } else if ( member.length === 2 ) { + + var propName = 'n' + ( member[ 0 ] + 1 ) + ( member[ 1 ] + 1 ); + this.obj[ propName ] = data; + + } else { + + console.log( 'Incorrect addressing of matrix in transform.' ); + + } + + break; + + case 'translate': + case 'scale': + + if ( Object.prototype.toString.call( member ) === '[object Array]' ) { + + member = members[ member[ 0 ] ]; + + } + + switch ( member ) { + + case 'X': + + this.obj.x = data; + break; + + case 'Y': + + this.obj.y = data; + break; + + case 'Z': + + this.obj.z = data; + break; + + default: + + this.obj.x = data[ 0 ]; + this.obj.y = data[ 1 ]; + this.obj.z = data[ 2 ]; + break; + + } + + break; + + case 'rotate': + + if ( Object.prototype.toString.call( member ) === '[object Array]' ) { + + member = members[ member[ 0 ] ]; + + } + + switch ( member ) { + + case 'X': + + this.obj.x = data; + break; + + case 'Y': + + this.obj.y = data; + break; + + case 'Z': + + this.obj.z = data; + break; + + case 'ANGLE': + + this.angle = THREE.Math.degToRad( data ); + break; + + default: + + this.obj.x = data[ 0 ]; + this.obj.y = data[ 1 ]; + this.obj.z = data[ 2 ]; + this.angle = THREE.Math.degToRad( data[ 3 ] ); + break; + + } + break; + + } + + }; + + function InstanceController() { + + this.url = ""; + this.skeleton = []; + this.instance_material = []; + + }; + + InstanceController.prototype.parse = function ( element ) { + + this.url = element.getAttribute( 'url' ).replace( /^#/, '' ); + this.skeleton = []; + this.instance_material = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'skeleton': + + this.skeleton.push( child.textContent.replace( /^#/, '' ) ); + break; + + case 'bind_material': + + var instances = child.querySelectorAll( 'instance_material' ); + + for ( var j = 0; j < instances.length; j ++ ) { + + var instance = instances[ j ]; + this.instance_material.push( ( new InstanceMaterial() ).parse( instance ) ); + + } + + + break; + + case 'extra': + break; + + default: + break; + + } + + } + + return this; + + }; + + function InstanceMaterial () { + + this.symbol = ""; + this.target = ""; + + }; + + InstanceMaterial.prototype.parse = function ( element ) { + + this.symbol = element.getAttribute( 'symbol' ); + this.target = element.getAttribute( 'target' ).replace( /^#/, '' ); + return this; + + }; + + function InstanceGeometry() { + + this.url = ""; + this.instance_material = []; + + }; + + InstanceGeometry.prototype.parse = function ( element ) { + + this.url = element.getAttribute( 'url' ).replace( /^#/, '' ); + this.instance_material = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + if ( child.nodeName === 'bind_material' ) { + + var instances = child.querySelectorAll( 'instance_material' ); + + for ( var j = 0; j < instances.length; j ++ ) { + + var instance = instances[ j ]; + this.instance_material.push( ( new InstanceMaterial() ).parse( instance ) ); + + } + + break; + + } + + } + + return this; + + }; + + function Geometry() { + + this.id = ""; + this.mesh = null; + + }; + + Geometry.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + + extractDoubleSided( this, element ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'mesh': + + this.mesh = ( new Mesh( this ) ).parse( child ); + break; + + case 'extra': + + // console.log( child ); + break; + + default: + break; + } + + } + + return this; + + }; + + function Mesh( geometry ) { + + this.geometry = geometry.id; + this.primitives = []; + this.vertices = null; + this.geometry3js = null; + + }; + + Mesh.prototype.parse = function ( element ) { + + this.primitives = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'source': + + _source( child ); + break; + + case 'vertices': + + this.vertices = ( new Vertices() ).parse( child ); + break; + + case 'linestrips': + + this.primitives.push( ( new LineStrips().parse( child ) ) ); + break; + + case 'triangles': + + this.primitives.push( ( new Triangles().parse( child ) ) ); + break; + + case 'polygons': + + this.primitives.push( ( new Polygons().parse( child ) ) ); + break; + + case 'polylist': + + this.primitives.push( ( new Polylist().parse( child ) ) ); + break; + + default: + break; + + } + + } + + this.geometry3js = new THREE.Geometry(); + + if ( this.vertices === null ) { + + // TODO (mrdoob): Study case when this is null (carrier.dae) + + return this; + + } + + var vertexData = sources[ this.vertices.input[ 'POSITION' ].source ].data; + + for ( var i = 0; i < vertexData.length; i += 3 ) { + + this.geometry3js.vertices.push( getConvertedVec3( vertexData, i ).clone() ); + + } + + for ( var i = 0; i < this.primitives.length; i ++ ) { + + var primitive = this.primitives[ i ]; + primitive.setVertices( this.vertices ); + this.handlePrimitive( primitive, this.geometry3js ); + + } + + if ( this.geometry3js.calcNormals ) { + + this.geometry3js.computeVertexNormals(); + delete this.geometry3js.calcNormals; + + } + + return this; + + }; + + Mesh.prototype.handlePrimitive = function ( primitive, geom ) { + + if ( primitive instanceof LineStrips ) { + + // TODO: Handle indices. Maybe easier with BufferGeometry? + + geom.isLineStrip = true; + return; + + } + + var j, k, pList = primitive.p, inputs = primitive.inputs; + var input, index, idx32; + var source, numParams; + var vcIndex = 0, vcount = 3, maxOffset = 0; + var texture_sets = []; + + for ( j = 0; j < inputs.length; j ++ ) { + + input = inputs[ j ]; + + var offset = input.offset + 1; + maxOffset = ( maxOffset < offset ) ? offset : maxOffset; + + switch ( input.semantic ) { + + case 'TEXCOORD': + texture_sets.push( input.set ); + break; + + } + + } + + for ( var pCount = 0; pCount < pList.length; ++ pCount ) { + + var p = pList[ pCount ], i = 0; + + while ( i < p.length ) { + + var vs = []; + var ns = []; + var ts = null; + var cs = []; + + if ( primitive.vcount ) { + + vcount = primitive.vcount.length ? primitive.vcount[ vcIndex ++ ] : primitive.vcount; + + } else { + + vcount = p.length / maxOffset; + + } + + + for ( j = 0; j < vcount; j ++ ) { + + for ( k = 0; k < inputs.length; k ++ ) { + + input = inputs[ k ]; + source = sources[ input.source ]; + + index = p[ i + ( j * maxOffset ) + input.offset ]; + numParams = source.accessor.params.length; + idx32 = index * numParams; + + switch ( input.semantic ) { + + case 'VERTEX': + + vs.push( index ); + + break; + + case 'NORMAL': + + ns.push( getConvertedVec3( source.data, idx32 ) ); + + break; + + case 'TEXCOORD': + + ts = ts || { }; + if ( ts[ input.set ] === undefined ) ts[ input.set ] = []; + // invert the V + ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], source.data[ idx32 + 1 ] ) ); + + break; + + case 'COLOR': + + cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); + + break; + + default: + + break; + + } + + } + + } + + if ( ns.length === 0 ) { + + // check the vertices inputs + input = this.vertices.input.NORMAL; + + if ( input ) { + + source = sources[ input.source ]; + numParams = source.accessor.params.length; + + for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { + + ns.push( getConvertedVec3( source.data, vs[ ndx ] * numParams ) ); + + } + + } else { + + geom.calcNormals = true; + + } + + } + + if ( ! ts ) { + + ts = { }; + // check the vertices inputs + input = this.vertices.input.TEXCOORD; + + if ( input ) { + + texture_sets.push( input.set ); + source = sources[ input.source ]; + numParams = source.accessor.params.length; + + for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { + + idx32 = vs[ ndx ] * numParams; + if ( ts[ input.set ] === undefined ) ts[ input.set ] = [ ]; + // invert the V + ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], 1.0 - source.data[ idx32 + 1 ] ) ); + + } + + } + + } + + if ( cs.length === 0 ) { + + // check the vertices inputs + input = this.vertices.input.COLOR; + + if ( input ) { + + source = sources[ input.source ]; + numParams = source.accessor.params.length; + + for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { + + idx32 = vs[ ndx ] * numParams; + cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); + + } + + } + + } + + var face = null, faces = [], uv, uvArr; + + if ( vcount === 3 ) { + + faces.push( new THREE.Face3( vs[ 0 ], vs[ 1 ], vs[ 2 ], ns, cs.length ? cs : new THREE.Color() ) ); + + } else if ( vcount === 4 ) { + + faces.push( new THREE.Face3( vs[ 0 ], vs[ 1 ], vs[ 3 ], [ ns[ 0 ].clone(), ns[ 1 ].clone(), ns[ 3 ].clone() ], cs.length ? [ cs[ 0 ], cs[ 1 ], cs[ 3 ]] : new THREE.Color() ) ); + + faces.push( new THREE.Face3( vs[ 1 ], vs[ 2 ], vs[ 3 ], [ ns[ 1 ].clone(), ns[ 2 ].clone(), ns[ 3 ].clone() ], cs.length ? [ cs[ 1 ], cs[ 2 ], cs[ 3 ]] : new THREE.Color() ) ); + + } else if ( vcount > 4 && options.subdivideFaces ) { + + var clr = cs.length ? cs : new THREE.Color(), + vec1, vec2, vec3, v1, v2, norm; + + // subdivide into multiple Face3s + + for ( k = 1; k < vcount - 1; ) { + + // FIXME: normals don't seem to be quite right + + faces.push( new THREE.Face3( vs[ 0 ], vs[ k ], vs[ k + 1 ], [ ns[ 0 ].clone(), ns[ k ++ ].clone(), ns[ k ].clone() ], clr ) ); + + } + + } + + if ( faces.length ) { + + for ( var ndx = 0, len = faces.length; ndx < len; ndx ++ ) { + + face = faces[ ndx ]; + face.daeMaterial = primitive.material; + geom.faces.push( face ); + + for ( k = 0; k < texture_sets.length; k ++ ) { + + uv = ts[ texture_sets[ k ] ]; + + if ( vcount > 4 ) { + + // Grab the right UVs for the vertices in this face + uvArr = [ uv[ 0 ], uv[ ndx + 1 ], uv[ ndx + 2 ] ]; + + } else if ( vcount === 4 ) { + + if ( ndx === 0 ) { + + uvArr = [ uv[ 0 ], uv[ 1 ], uv[ 3 ] ]; + + } else { + + uvArr = [ uv[ 1 ].clone(), uv[ 2 ], uv[ 3 ].clone() ]; + + } + + } else { + + uvArr = [ uv[ 0 ], uv[ 1 ], uv[ 2 ] ]; + + } + + if ( geom.faceVertexUvs[ k ] === undefined ) { + + geom.faceVertexUvs[ k ] = []; + + } + + geom.faceVertexUvs[ k ].push( uvArr ); + + } + + } + + } else { + + console.log( 'dropped face with vcount ' + vcount + ' for geometry with id: ' + geom.id ); + + } + + i += maxOffset * vcount; + + } + + } + + }; + + function Polygons () { + + this.material = ""; + this.count = 0; + this.inputs = []; + this.vcount = null; + this.p = []; + this.geometry = new THREE.Geometry(); + + }; + + Polygons.prototype.setVertices = function ( vertices ) { + + for ( var i = 0; i < this.inputs.length; i ++ ) { + + if ( this.inputs[ i ].source === vertices.id ) { + + this.inputs[ i ].source = vertices.input[ 'POSITION' ].source; + + } + + } + + }; + + Polygons.prototype.parse = function ( element ) { + + this.material = element.getAttribute( 'material' ); + this.count = _attr_as_int( element, 'count', 0 ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'input': + + this.inputs.push( ( new Input() ).parse( element.childNodes[ i ] ) ); + break; + + case 'vcount': + + this.vcount = _ints( child.textContent ); + break; + + case 'p': + + this.p.push( _ints( child.textContent ) ); + break; + + case 'ph': + + console.warn( 'polygon holes not yet supported!' ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Polylist () { + + Polygons.call( this ); + + this.vcount = []; + + }; + + Polylist.prototype = Object.create( Polygons.prototype ); + Polylist.prototype.constructor = Polylist; + + function LineStrips() { + + Polygons.call( this ); + + this.vcount = 1; + + }; + + LineStrips.prototype = Object.create( Polygons.prototype ); + LineStrips.prototype.constructor = LineStrips; + + function Triangles () { + + Polygons.call( this ); + + this.vcount = 3; + + }; + + Triangles.prototype = Object.create( Polygons.prototype ); + Triangles.prototype.constructor = Triangles; + + function Accessor() { + + this.source = ""; + this.count = 0; + this.stride = 0; + this.params = []; + + }; + + Accessor.prototype.parse = function ( element ) { + + this.params = []; + this.source = element.getAttribute( 'source' ); + this.count = _attr_as_int( element, 'count', 0 ); + this.stride = _attr_as_int( element, 'stride', 0 ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeName === 'param' ) { + + var param = {}; + param[ 'name' ] = child.getAttribute( 'name' ); + param[ 'type' ] = child.getAttribute( 'type' ); + this.params.push( param ); + + } + + } + + return this; + + }; + + function Vertices() { + + this.input = {}; + + }; + + Vertices.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + if ( element.childNodes[ i ].nodeName === 'input' ) { + + var input = ( new Input() ).parse( element.childNodes[ i ] ); + this.input[ input.semantic ] = input; + + } + + } + + return this; + + }; + + function Input () { + + this.semantic = ""; + this.offset = 0; + this.source = ""; + this.set = 0; + + }; + + Input.prototype.parse = function ( element ) { + + this.semantic = element.getAttribute( 'semantic' ); + this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); + this.set = _attr_as_int( element, 'set', - 1 ); + this.offset = _attr_as_int( element, 'offset', 0 ); + + if ( this.semantic === 'TEXCOORD' && this.set < 0 ) { + + this.set = 0; + + } + + return this; + + }; + + function Source ( id ) { + + this.id = id; + this.type = null; + + }; + + Source.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'bool_array': + + this.data = _bools( child.textContent ); + this.type = child.nodeName; + break; + + case 'float_array': + + this.data = _floats( child.textContent ); + this.type = child.nodeName; + break; + + case 'int_array': + + this.data = _ints( child.textContent ); + this.type = child.nodeName; + break; + + case 'IDREF_array': + case 'Name_array': + + this.data = _strings( child.textContent ); + this.type = child.nodeName; + break; + + case 'technique_common': + + for ( var j = 0; j < child.childNodes.length; j ++ ) { + + if ( child.childNodes[ j ].nodeName === 'accessor' ) { + + this.accessor = ( new Accessor() ).parse( child.childNodes[ j ] ); + break; + + } + + } + break; + + default: + // console.log(child.nodeName); + break; + + } + + } + + return this; + + }; + + Source.prototype.read = function () { + + var result = []; + + //for (var i = 0; i < this.accessor.params.length; i++) { + + var param = this.accessor.params[ 0 ]; + + //console.log(param.name + " " + param.type); + + switch ( param.type ) { + + case 'IDREF': + case 'Name': case 'name': + case 'float': + + return this.data; + + case 'float4x4': + + for ( var j = 0; j < this.data.length; j += 16 ) { + + var s = this.data.slice( j, j + 16 ); + var m = getConvertedMat4( s ); + result.push( m ); + + } + + break; + + default: + + console.log( 'ColladaLoader: Source: Read dont know how to read ' + param.type + '.' ); + break; + + } + + //} + + return result; + + }; + + function Material () { + + this.id = ""; + this.name = ""; + this.instance_effect = null; + + }; + + Material.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + if ( element.childNodes[ i ].nodeName === 'instance_effect' ) { + + this.instance_effect = ( new InstanceEffect() ).parse( element.childNodes[ i ] ); + break; + + } + + } + + return this; + + }; + + function ColorOrTexture () { + + this.color = new THREE.Color(); + this.color.setRGB( Math.random(), Math.random(), Math.random() ); + this.color.a = 1.0; + + this.texture = null; + this.texcoord = null; + this.texOpts = null; + + }; + + ColorOrTexture.prototype.isColor = function () { + + return ( this.texture === null ); + + }; + + ColorOrTexture.prototype.isTexture = function () { + + return ( this.texture != null ); + + }; + + ColorOrTexture.prototype.parse = function ( element ) { + + if ( element.nodeName === 'transparent' ) { + + this.opaque = element.getAttribute( 'opaque' ); + + } + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'color': + + var rgba = _floats( child.textContent ); + this.color = new THREE.Color(); + this.color.setRGB( rgba[ 0 ], rgba[ 1 ], rgba[ 2 ] ); + this.color.a = rgba[ 3 ]; + break; + + case 'texture': + + this.texture = child.getAttribute( 'texture' ); + this.texcoord = child.getAttribute( 'texcoord' ); + // Defaults from: + // https://collada.org/mediawiki/index.php/Maya_texture_placement_MAYA_extension + this.texOpts = { + offsetU: 0, + offsetV: 0, + repeatU: 1, + repeatV: 1, + wrapU: 1, + wrapV: 1 + }; + this.parseTexture( child ); + break; + + default: + break; + + } + + } + + return this; + + }; + + ColorOrTexture.prototype.parseTexture = function ( element ) { + + if ( ! element.childNodes ) return this; + + // This should be supported by Maya, 3dsMax, and MotionBuilder + + if ( element.childNodes[ 1 ] && element.childNodes[ 1 ].nodeName === 'extra' ) { + + element = element.childNodes[ 1 ]; + + if ( element.childNodes[ 1 ] && element.childNodes[ 1 ].nodeName === 'technique' ) { + + element = element.childNodes[ 1 ]; + + } + + } + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'offsetU': + case 'offsetV': + case 'repeatU': + case 'repeatV': + + this.texOpts[ child.nodeName ] = parseFloat( child.textContent ); + + break; + + case 'wrapU': + case 'wrapV': + + // some dae have a value of true which becomes NaN via parseInt + + if ( child.textContent.toUpperCase() === 'TRUE' ) { + + this.texOpts[ child.nodeName ] = 1; + + } else { + + this.texOpts[ child.nodeName ] = parseInt( child.textContent ); + + } + break; + + default: + + this.texOpts[ child.nodeName ] = child.textContent; + + break; + + } + + } + + return this; + + }; + + function Shader ( type, effect ) { + + this.type = type; + this.effect = effect; + this.material = null; + + }; + + Shader.prototype.parse = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'emission': + case 'diffuse': + case 'specular': + case 'transparent': + + this[ child.nodeName ] = ( new ColorOrTexture() ).parse( child ); + break; + + case 'bump': + + // If 'bumptype' is 'heightfield', create a 'bump' property + // Else if 'bumptype' is 'normalmap', create a 'normal' property + // (Default to 'bump') + var bumpType = child.getAttribute( 'bumptype' ); + if ( bumpType ) { + + if ( bumpType.toLowerCase() === "heightfield" ) { + + this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); + + } else if ( bumpType.toLowerCase() === "normalmap" ) { + + this[ 'normal' ] = ( new ColorOrTexture() ).parse( child ); + + } else { + + console.error( "Shader.prototype.parse: Invalid value for attribute 'bumptype' (" + bumpType + + ") - valid bumptypes are 'HEIGHTFIELD' and 'NORMALMAP' - defaulting to 'HEIGHTFIELD'" ); + this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); + + } + + } else { + + console.warn( "Shader.prototype.parse: Attribute 'bumptype' missing from bump node - defaulting to 'HEIGHTFIELD'" ); + this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); + + } + + break; + + case 'shininess': + case 'reflectivity': + case 'index_of_refraction': + case 'transparency': + + var f = child.querySelectorAll( 'float' ); + + if ( f.length > 0 ) + this[ child.nodeName ] = parseFloat( f[ 0 ].textContent ); + + break; + + default: + break; + + } + + } + + this.create(); + return this; + + }; + + Shader.prototype.create = function() { + + var props = {}; + + var transparent = false; + + if ( this[ 'transparency' ] !== undefined && this[ 'transparent' ] !== undefined ) { + + // convert transparent color RBG to average value + var transparentColor = this[ 'transparent' ]; + var transparencyLevel = ( this.transparent.color.r + this.transparent.color.g + this.transparent.color.b ) / 3 * this.transparency; + + if ( transparencyLevel > 0 ) { + + transparent = true; + props[ 'transparent' ] = true; + props[ 'opacity' ] = 1 - transparencyLevel; + + } + + } + + var keys = { + 'diffuse': 'map', + 'ambient': 'lightMap', + 'specular': 'specularMap', + 'emission': 'emissionMap', + 'bump': 'bumpMap', + 'normal': 'normalMap' + }; + + for ( var prop in this ) { + + switch ( prop ) { + + case 'ambient': + case 'emission': + case 'diffuse': + case 'specular': + case 'bump': + case 'normal': + + var cot = this[ prop ]; + + if ( cot instanceof ColorOrTexture ) { + + if ( cot.isTexture() ) { + + var samplerId = cot.texture; + var surfaceId = this.effect.sampler[ samplerId ]; + + if ( surfaceId !== undefined && surfaceId.source !== undefined ) { + + var surface = this.effect.surface[ surfaceId.source ]; + + if ( surface !== undefined ) { + + var image = images[ surface.init_from ]; + + if ( image && baseUrl ) { + + var url = baseUrl + image.init_from; + + var texture; + var loader = THREE.Loader.Handlers.get( url ); + + if ( loader !== null ) { + + texture = loader.load( url ); + + } else { + + texture = new THREE.Texture(); + + loadTextureImage( texture, url ); + + } + + texture.wrapS = cot.texOpts.wrapU ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + texture.wrapT = cot.texOpts.wrapV ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + texture.offset.x = cot.texOpts.offsetU; + texture.offset.y = cot.texOpts.offsetV; + texture.repeat.x = cot.texOpts.repeatU; + texture.repeat.y = cot.texOpts.repeatV; + props[ keys[ prop ]] = texture; + + // Texture with baked lighting? + if ( prop === 'emission' ) props[ 'emissive' ] = 0xffffff; + + } + + } + + } + + } else if ( prop === 'diffuse' || ! transparent ) { + + if ( prop === 'emission' ) { + + props[ 'emissive' ] = cot.color.getHex(); + + } else { + + props[ prop ] = cot.color.getHex(); + + } + + } + + } + + break; + + case 'shininess': + + props[ prop ] = this[ prop ]; + break; + + case 'reflectivity': + + props[ prop ] = this[ prop ]; + if ( props[ prop ] > 0.0 ) props[ 'envMap' ] = options.defaultEnvMap; + props[ 'combine' ] = THREE.MixOperation; //mix regular shading with reflective component + break; + + case 'index_of_refraction': + + props[ 'refractionRatio' ] = this[ prop ]; //TODO: "index_of_refraction" becomes "refractionRatio" in shader, but I'm not sure if the two are actually comparable + if ( this[ prop ] !== 1.0 ) props[ 'envMap' ] = options.defaultEnvMap; + break; + + case 'transparency': + // gets figured out up top + break; + + default: + break; + + } + + } + + props[ 'shading' ] = preferredShading; + props[ 'side' ] = this.effect.doubleSided ? THREE.DoubleSide : THREE.FrontSide; + + switch ( this.type ) { + + case 'constant': + + if ( props.emissive != undefined ) props.color = props.emissive; + this.material = new THREE.MeshBasicMaterial( props ); + break; + + case 'phong': + case 'blinn': + + if ( props.diffuse != undefined ) props.color = props.diffuse; + this.material = new THREE.MeshPhongMaterial( props ); + break; + + case 'lambert': + default: + + if ( props.diffuse != undefined ) props.color = props.diffuse; + this.material = new THREE.MeshLambertMaterial( props ); + break; + + } + + return this.material; + + }; + + function Surface ( effect ) { + + this.effect = effect; + this.init_from = null; + this.format = null; + + }; + + Surface.prototype.parse = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'init_from': + + this.init_from = child.textContent; + break; + + case 'format': + + this.format = child.textContent; + break; + + default: + + console.log( "unhandled Surface prop: " + child.nodeName ); + break; + + } + + } + + return this; + + }; + + function Sampler2D ( effect ) { + + this.effect = effect; + this.source = null; + this.wrap_s = null; + this.wrap_t = null; + this.minfilter = null; + this.magfilter = null; + this.mipfilter = null; + + }; + + Sampler2D.prototype.parse = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'source': + + this.source = child.textContent; + break; + + case 'minfilter': + + this.minfilter = child.textContent; + break; + + case 'magfilter': + + this.magfilter = child.textContent; + break; + + case 'mipfilter': + + this.mipfilter = child.textContent; + break; + + case 'wrap_s': + + this.wrap_s = child.textContent; + break; + + case 'wrap_t': + + this.wrap_t = child.textContent; + break; + + default: + + console.log( "unhandled Sampler2D prop: " + child.nodeName ); + break; + + } + + } + + return this; + + }; + + function Effect () { + + this.id = ""; + this.name = ""; + this.shader = null; + this.surface = {}; + this.sampler = {}; + + }; + + Effect.prototype.create = function () { + + if ( this.shader === null ) { + + return null; + + } + + }; + + Effect.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + extractDoubleSided( this, element ); + + this.shader = null; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'profile_COMMON': + + this.parseTechnique( this.parseProfileCOMMON( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Effect.prototype.parseNewparam = function ( element ) { + + var sid = element.getAttribute( 'sid' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'surface': + + this.surface[ sid ] = ( new Surface( this ) ).parse( child ); + break; + + case 'sampler2D': + + this.sampler[ sid ] = ( new Sampler2D( this ) ).parse( child ); + break; + + case 'extra': + + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + }; + + Effect.prototype.parseProfileCOMMON = function ( element ) { + + var technique; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'profile_COMMON': + + this.parseProfileCOMMON( child ); + break; + + case 'technique': + + technique = child; + break; + + case 'newparam': + + this.parseNewparam( child ); + break; + + case 'image': + + var _image = ( new _Image() ).parse( child ); + images[ _image.id ] = _image; + break; + + case 'extra': + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + return technique; + + }; + + Effect.prototype.parseTechnique = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'constant': + case 'lambert': + case 'blinn': + case 'phong': + + this.shader = ( new Shader( child.nodeName, this ) ).parse( child ); + break; + case 'extra': + this.parseExtra( child ); + break; + default: + break; + + } + + } + + }; + + Effect.prototype.parseExtra = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique': + this.parseExtraTechnique( child ); + break; + default: + break; + + } + + } + + }; + + Effect.prototype.parseExtraTechnique = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'bump': + this.shader.parse( element ); + break; + default: + break; + + } + + } + + }; + + function InstanceEffect () { + + this.url = ""; + + }; + + InstanceEffect.prototype.parse = function ( element ) { + + this.url = element.getAttribute( 'url' ).replace( /^#/, '' ); + return this; + + }; + + function Animation() { + + this.id = ""; + this.name = ""; + this.source = {}; + this.sampler = []; + this.channel = []; + + }; + + Animation.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + this.source = {}; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'animation': + + var anim = ( new Animation() ).parse( child ); + + for ( var src in anim.source ) { + + this.source[ src ] = anim.source[ src ]; + + } + + for ( var j = 0; j < anim.channel.length; j ++ ) { + + this.channel.push( anim.channel[ j ] ); + this.sampler.push( anim.sampler[ j ] ); + + } + + break; + + case 'source': + + var src = ( new Source() ).parse( child ); + this.source[ src.id ] = src; + break; + + case 'sampler': + + this.sampler.push( ( new Sampler( this ) ).parse( child ) ); + break; + + case 'channel': + + this.channel.push( ( new Channel( this ) ).parse( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Channel( animation ) { + + this.animation = animation; + this.source = ""; + this.target = ""; + this.fullSid = null; + this.sid = null; + this.dotSyntax = null; + this.arrSyntax = null; + this.arrIndices = null; + this.member = null; + + }; + + Channel.prototype.parse = function ( element ) { + + this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); + this.target = element.getAttribute( 'target' ); + + var parts = this.target.split( '/' ); + + var id = parts.shift(); + var sid = parts.shift(); + + var dotSyntax = ( sid.indexOf( "." ) >= 0 ); + var arrSyntax = ( sid.indexOf( "(" ) >= 0 ); + + if ( dotSyntax ) { + + parts = sid.split( "." ); + this.sid = parts.shift(); + this.member = parts.shift(); + + } else if ( arrSyntax ) { + + var arrIndices = sid.split( "(" ); + this.sid = arrIndices.shift(); + + for ( var j = 0; j < arrIndices.length; j ++ ) { + + arrIndices[ j ] = parseInt( arrIndices[ j ].replace( /\)/, '' ) ); + + } + + this.arrIndices = arrIndices; + + } else { + + this.sid = sid; + + } + + this.fullSid = sid; + this.dotSyntax = dotSyntax; + this.arrSyntax = arrSyntax; + + return this; + + }; + + function Sampler ( animation ) { + + this.id = ""; + this.animation = animation; + this.inputs = []; + this.input = null; + this.output = null; + this.strideOut = null; + this.interpolation = null; + this.startTime = null; + this.endTime = null; + this.duration = 0; + + }; + + Sampler.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.inputs = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + + this.inputs.push( ( new Input() ).parse( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Sampler.prototype.create = function () { + + for ( var i = 0; i < this.inputs.length; i ++ ) { + + var input = this.inputs[ i ]; + var source = this.animation.source[ input.source ]; + + switch ( input.semantic ) { + + case 'INPUT': + + this.input = source.read(); + break; + + case 'OUTPUT': + + this.output = source.read(); + this.strideOut = source.accessor.stride; + break; + + case 'INTERPOLATION': + + this.interpolation = source.read(); + break; + + case 'IN_TANGENT': + + break; + + case 'OUT_TANGENT': + + break; + + default: + + console.log( input.semantic ); + break; + + } + + } + + this.startTime = 0; + this.endTime = 0; + this.duration = 0; + + if ( this.input.length ) { + + this.startTime = 100000000; + this.endTime = - 100000000; + + for ( var i = 0; i < this.input.length; i ++ ) { + + this.startTime = Math.min( this.startTime, this.input[ i ] ); + this.endTime = Math.max( this.endTime, this.input[ i ] ); + + } + + this.duration = this.endTime - this.startTime; + + } + + }; + + Sampler.prototype.getData = function ( type, ndx, member ) { + + var data; + + if ( type === 'matrix' && this.strideOut === 16 ) { + + data = this.output[ ndx ]; + + } else if ( this.strideOut > 1 ) { + + data = []; + ndx *= this.strideOut; + + for ( var i = 0; i < this.strideOut; ++ i ) { + + data[ i ] = this.output[ ndx + i ]; + + } + + if ( this.strideOut === 3 ) { + + switch ( type ) { + + case 'rotate': + case 'translate': + + fixCoords( data, - 1 ); + break; + + case 'scale': + + fixCoords( data, 1 ); + break; + + } + + } else if ( this.strideOut === 4 && type === 'matrix' ) { + + fixCoords( data, - 1 ); + + } + + } else { + + data = this.output[ ndx ]; + + if ( member && type === 'translate' ) { + + data = getConvertedTranslation( member, data ); + + } + + } + + return data; + + }; + + function Key ( time ) { + + this.targets = []; + this.time = time; + + }; + + Key.prototype.addTarget = function ( fullSid, transform, member, data ) { + + this.targets.push( { + sid: fullSid, + member: member, + transform: transform, + data: data + } ); + + }; + + Key.prototype.apply = function ( opt_sid ) { + + for ( var i = 0; i < this.targets.length; ++ i ) { + + var target = this.targets[ i ]; + + if ( ! opt_sid || target.sid === opt_sid ) { + + target.transform.update( target.data, target.member ); + + } + + } + + }; + + Key.prototype.getTarget = function ( fullSid ) { + + for ( var i = 0; i < this.targets.length; ++ i ) { + + if ( this.targets[ i ].sid === fullSid ) { + + return this.targets[ i ]; + + } + + } + + return null; + + }; + + Key.prototype.hasTarget = function ( fullSid ) { + + for ( var i = 0; i < this.targets.length; ++ i ) { + + if ( this.targets[ i ].sid === fullSid ) { + + return true; + + } + + } + + return false; + + }; + + // TODO: Currently only doing linear interpolation. Should support full COLLADA spec. + Key.prototype.interpolate = function ( nextKey, time ) { + + for ( var i = 0, l = this.targets.length; i < l; i ++ ) { + + var target = this.targets[ i ], + nextTarget = nextKey.getTarget( target.sid ), + data; + + if ( target.transform.type !== 'matrix' && nextTarget ) { + + var scale = ( time - this.time ) / ( nextKey.time - this.time ), + nextData = nextTarget.data, + prevData = target.data; + + if ( scale < 0 ) scale = 0; + if ( scale > 1 ) scale = 1; + + if ( prevData.length ) { + + data = []; + + for ( var j = 0; j < prevData.length; ++ j ) { + + data[ j ] = prevData[ j ] + ( nextData[ j ] - prevData[ j ] ) * scale; + + } + + } else { + + data = prevData + ( nextData - prevData ) * scale; + + } + + } else { + + data = target.data; + + } + + target.transform.update( data, target.member ); + + } + + }; + + // Camera + function Camera() { + + this.id = ""; + this.name = ""; + this.technique = ""; + + }; + + Camera.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'optics': + + this.parseOptics( child ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Camera.prototype.parseOptics = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + if ( element.childNodes[ i ].nodeName === 'technique_common' ) { + + var technique = element.childNodes[ i ]; + + for ( var j = 0; j < technique.childNodes.length; j ++ ) { + + this.technique = technique.childNodes[ j ].nodeName; + + if ( this.technique === 'perspective' ) { + + var perspective = technique.childNodes[ j ]; + + for ( var k = 0; k < perspective.childNodes.length; k ++ ) { + + var param = perspective.childNodes[ k ]; + + switch ( param.nodeName ) { + + case 'yfov': + this.yfov = param.textContent; + break; + case 'xfov': + this.xfov = param.textContent; + break; + case 'znear': + this.znear = param.textContent; + break; + case 'zfar': + this.zfar = param.textContent; + break; + case 'aspect_ratio': + this.aspect_ratio = param.textContent; + break; + + } + + } + + } else if ( this.technique === 'orthographic' ) { + + var orthographic = technique.childNodes[ j ]; + + for ( var k = 0; k < orthographic.childNodes.length; k ++ ) { + + var param = orthographic.childNodes[ k ]; + + switch ( param.nodeName ) { + + case 'xmag': + this.xmag = param.textContent; + break; + case 'ymag': + this.ymag = param.textContent; + break; + case 'znear': + this.znear = param.textContent; + break; + case 'zfar': + this.zfar = param.textContent; + break; + case 'aspect_ratio': + this.aspect_ratio = param.textContent; + break; + + } + + } + + } + + } + + } + + } + + return this; + + }; + + function InstanceCamera() { + + this.url = ""; + + }; + + InstanceCamera.prototype.parse = function ( element ) { + + this.url = element.getAttribute( 'url' ).replace( /^#/, '' ); + + return this; + + }; + + // Light + + function Light() { + + this.id = ""; + this.name = ""; + this.technique = ""; + + }; + + Light.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique_common': + + this.parseCommon( child ); + break; + + case 'technique': + + this.parseTechnique( child ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Light.prototype.parseCommon = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + switch ( element.childNodes[ i ].nodeName ) { + + case 'directional': + case 'point': + case 'spot': + case 'ambient': + + this.technique = element.childNodes[ i ].nodeName; + + var light = element.childNodes[ i ]; + + for ( var j = 0; j < light.childNodes.length; j ++ ) { + + var child = light.childNodes[ j ]; + + switch ( child.nodeName ) { + + case 'color': + + var rgba = _floats( child.textContent ); + this.color = new THREE.Color( 0 ); + this.color.setRGB( rgba[ 0 ], rgba[ 1 ], rgba[ 2 ] ); + this.color.a = rgba[ 3 ]; + break; + + case 'falloff_angle': + + this.falloff_angle = parseFloat( child.textContent ); + break; + + case 'quadratic_attenuation': + var f = parseFloat( child.textContent ); + this.distance = f ? Math.sqrt( 1 / f ) : 0; + } + + } + + } + + } + + return this; + + }; + + Light.prototype.parseTechnique = function ( element ) { + + this.profile = element.getAttribute( 'profile' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'intensity': + + this.intensity = parseFloat( child.textContent ); + break; + + } + + } + + return this; + + }; + + function InstanceLight() { + + this.url = ""; + + }; + + InstanceLight.prototype.parse = function ( element ) { + + this.url = element.getAttribute( 'url' ).replace( /^#/, '' ); + + return this; + + }; + + function KinematicsModel( ) { + + this.id = ''; + this.name = ''; + this.joints = []; + this.links = []; + + } + + KinematicsModel.prototype.parse = function( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + this.joints = []; + this.links = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique_common': + + this.parseCommon( child ); + break; + + default: + break; + + } + + } + + return this; + + }; + + KinematicsModel.prototype.parseCommon = function( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( element.childNodes[ i ].nodeName ) { + + case 'joint': + this.joints.push( ( new Joint() ).parse( child ) ); + break; + + case 'link': + this.links.push( ( new Link() ).parse( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Joint( ) { + + this.sid = ''; + this.name = ''; + this.axis = new THREE.Vector3(); + this.limits = { + min: 0, + max: 0 + }; + this.type = ''; + this.static = false; + this.zeroPosition = 0.0; + this.middlePosition = 0.0; + + } + + Joint.prototype.parse = function( element ) { + + this.sid = element.getAttribute( 'sid' ); + this.name = element.getAttribute( 'name' ); + this.axis = new THREE.Vector3(); + this.limits = { + min: 0, + max: 0 + }; + this.type = ''; + this.static = false; + this.zeroPosition = 0.0; + this.middlePosition = 0.0; + + var axisElement = element.querySelector( 'axis' ); + var _axis = _floats( axisElement.textContent ); + this.axis = getConvertedVec3( _axis, 0 ); + + var min = element.querySelector( 'limits min' ) ? parseFloat( element.querySelector( 'limits min' ).textContent ) : - 360; + var max = element.querySelector( 'limits max' ) ? parseFloat( element.querySelector( 'limits max' ).textContent ) : 360; + + this.limits = { + min: min, + max: max + }; + + var jointTypes = [ 'prismatic', 'revolute' ]; + for ( var i = 0; i < jointTypes.length; i ++ ) { + + var type = jointTypes[ i ]; + + var jointElement = element.querySelector( type ); + + if ( jointElement ) { + + this.type = type; + + } + + } + + // if the min is equal to or somehow greater than the max, consider the joint static + if ( this.limits.min >= this.limits.max ) { + + this.static = true; + + } + + this.middlePosition = ( this.limits.min + this.limits.max ) / 2.0; + return this; + + }; + + function Link( ) { + + this.sid = ''; + this.name = ''; + this.transforms = []; + this.attachments = []; + + } + + Link.prototype.parse = function( element ) { + + this.sid = element.getAttribute( 'sid' ); + this.name = element.getAttribute( 'name' ); + this.transforms = []; + this.attachments = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'attachment_full': + this.attachments.push( ( new Attachment() ).parse( child ) ); + break; + + case 'rotate': + case 'translate': + case 'matrix': + + this.transforms.push( ( new Transform() ).parse( child ) ); + break; + + default: + + break; + + } + + } + + return this; + + }; + + function Attachment( ) { + + this.joint = ''; + this.transforms = []; + this.links = []; + + } + + Attachment.prototype.parse = function( element ) { + + this.joint = element.getAttribute( 'joint' ).split( '/' ).pop(); + this.links = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'link': + this.links.push( ( new Link() ).parse( child ) ); + break; + + case 'rotate': + case 'translate': + case 'matrix': + + this.transforms.push( ( new Transform() ).parse( child ) ); + break; + + default: + + break; + + } + + } + + return this; + + }; + + function _source( element ) { + + var id = element.getAttribute( 'id' ); + + if ( sources[ id ] != undefined ) { + + return sources[ id ]; + + } + + sources[ id ] = ( new Source( id ) ).parse( element ); + return sources[ id ]; + + }; + + function _nsResolver( nsPrefix ) { + + if ( nsPrefix === "dae" ) { + + return "http://www.collada.org/2005/11/COLLADASchema"; + + } + + return null; + + }; + + function _bools( str ) { + + var raw = _strings( str ); + var data = []; + + for ( var i = 0, l = raw.length; i < l; i ++ ) { + + data.push( ( raw[ i ] === 'true' || raw[ i ] === '1' ) ? true : false ); + + } + + return data; + + }; + + function _floats( str ) { + + var raw = _strings( str ); + var data = []; + + for ( var i = 0, l = raw.length; i < l; i ++ ) { + + data.push( parseFloat( raw[ i ] ) ); + + } + + return data; + + }; + + function _ints( str ) { + + var raw = _strings( str ); + var data = []; + + for ( var i = 0, l = raw.length; i < l; i ++ ) { + + data.push( parseInt( raw[ i ], 10 ) ); + + } + + return data; + + }; + + function _strings( str ) { + + return ( str.length > 0 ) ? _trimString( str ).split( /\s+/ ) : []; + + }; + + function _trimString( str ) { + + return str.replace( /^\s+/, "" ).replace( /\s+$/, "" ); + + }; + + function _attr_as_float( element, name, defaultValue ) { + + if ( element.hasAttribute( name ) ) { + + return parseFloat( element.getAttribute( name ) ); + + } else { + + return defaultValue; + + } + + }; + + function _attr_as_int( element, name, defaultValue ) { + + if ( element.hasAttribute( name ) ) { + + return parseInt( element.getAttribute( name ), 10 ) ; + + } else { + + return defaultValue; + + } + + }; + + function _attr_as_string( element, name, defaultValue ) { + + if ( element.hasAttribute( name ) ) { + + return element.getAttribute( name ); + + } else { + + return defaultValue; + + } + + }; + + function _format_float( f, num ) { + + if ( f === undefined ) { + + var s = '0.'; + + while ( s.length < num + 2 ) { + + s += '0'; + + } + + return s; + + } + + num = num || 2; + + var parts = f.toString().split( '.' ); + parts[ 1 ] = parts.length > 1 ? parts[ 1 ].substr( 0, num ) : "0"; + + while ( parts[ 1 ].length < num ) { + + parts[ 1 ] += '0'; + + } + + return parts.join( '.' ); + + }; + + function loadTextureImage ( texture, url ) { + + var loader = new THREE.ImageLoader(); + + loader.load( url, function ( image ) { + + texture.image = image; + texture.needsUpdate = true; + + } ); + + }; + + function applySkin ( geometry, instanceCtrl, frame ) { + + var skinController = controllers[ instanceCtrl.url ]; + + frame = frame !== undefined ? frame : 40; + + if ( ! skinController || ! skinController.skin ) { + + console.log( 'ColladaLoader: Could not find skin controller.' ); + return; + + } + + if ( ! instanceCtrl.skeleton || ! instanceCtrl.skeleton.length ) { + + console.log( 'ColladaLoader: Could not find the skeleton for the skin. ' ); + return; + + } + + var animationBounds = calcAnimationBounds(); + var skeleton = visualScene.getChildById( instanceCtrl.skeleton[ 0 ], true ) || + visualScene.getChildBySid( instanceCtrl.skeleton[ 0 ], true ); + + //flatten the skeleton into a list of bones + var bonelist = flattenSkeleton( skeleton ); + var joints = skinController.skin.joints; + + //sort that list so that the order reflects the order in the joint list + var sortedbones = []; + for ( var i = 0; i < joints.length; i ++ ) { + + for ( var j = 0; j < bonelist.length; j ++ ) { + + if ( bonelist[ j ].name === joints[ i ] ) { + + sortedbones[ i ] = bonelist[ j ]; + + } + + } + + } + + //hook up the parents by index instead of name + for ( var i = 0; i < sortedbones.length; i ++ ) { + + for ( var j = 0; j < sortedbones.length; j ++ ) { + + if ( sortedbones[ i ].parent === sortedbones[ j ].name ) { + + sortedbones[ i ].parent = j; + + } + + } + + } + + + var i, j, w, vidx, weight; + var v = new THREE.Vector3(), o, s; + + // move vertices to bind shape + for ( i = 0; i < geometry.vertices.length; i ++ ) { + + geometry.vertices[ i ].applyMatrix4( skinController.skin.bindShapeMatrix ); + + } + + var skinIndices = []; + var skinWeights = []; + var weights = skinController.skin.weights; + + //hook up the skin weights + // TODO - this might be a good place to choose greatest 4 weights + for ( var i = 0; i < weights.length; i ++ ) { + + var indicies = new THREE.Vector4( weights[ i ][ 0 ] ? weights[ i ][ 0 ].joint : 0, weights[ i ][ 1 ] ? weights[ i ][ 1 ].joint : 0, weights[ i ][ 2 ] ? weights[ i ][ 2 ].joint : 0, weights[ i ][ 3 ] ? weights[ i ][ 3 ].joint : 0 ); + var weight = new THREE.Vector4( weights[ i ][ 0 ] ? weights[ i ][ 0 ].weight : 0, weights[ i ][ 1 ] ? weights[ i ][ 1 ].weight : 0, weights[ i ][ 2 ] ? weights[ i ][ 2 ].weight : 0, weights[ i ][ 3 ] ? weights[ i ][ 3 ].weight : 0 ); + + skinIndices.push( indicies ); + skinWeights.push( weight ); + + } + + geometry.skinIndices = skinIndices; + geometry.skinWeights = skinWeights; + geometry.bones = sortedbones; + // process animation, or simply pose the rig if no animation + + //create an animation for the animated bones + //NOTE: this has no effect when using morphtargets + var animationdata = { "name": animationBounds.ID, "fps": 30, "length": animationBounds.frames / 30, "hierarchy": [] }; + + for ( var j = 0; j < sortedbones.length; j ++ ) { + + animationdata.hierarchy.push( { parent: sortedbones[ j ].parent, name: sortedbones[ j ].name, keys: [] } ); + + } + + console.log( 'ColladaLoader:', animationBounds.ID + ' has ' + sortedbones.length + ' bones.' ); + + + + skinToBindPose( geometry, skeleton, skinController ); + + + for ( frame = 0; frame < animationBounds.frames; frame ++ ) { + + var bones = []; + var skinned = []; + // process the frame and setup the rig with a fresh + // transform, possibly from the bone's animation channel(s) + + setupSkeleton( skeleton, bones, frame ); + setupSkinningMatrices( bones, skinController.skin ); + + for ( var i = 0; i < bones.length; i ++ ) { + + for ( var j = 0; j < animationdata.hierarchy.length; j ++ ) { + + if ( animationdata.hierarchy[ j ].name === bones[ i ].sid ) { + + var key = {}; + key.time = ( frame / 30 ); + key.matrix = bones[ i ].animatrix; + + if ( frame === 0 ) + bones[ i ].matrix = key.matrix; + + var data = [ new THREE.Vector3(), new THREE.Quaternion(), new THREE.Vector3() ]; + key.matrix.decompose( data[ 0 ], data[ 1 ], data[ 2 ] ); + + key.pos = [ data[ 0 ].x, data[ 0 ].y, data[ 0 ].z ]; + + key.scl = [ data[ 2 ].x, data[ 2 ].y, data[ 2 ].z ]; + key.rot = data[ 1 ]; + + animationdata.hierarchy[ j ].keys.push( key ); + + } + + } + + } + + geometry.animation = animationdata; + + } + + }; + + function calcAnimationBounds () { + + var start = 1000000; + var end = - start; + var frames = 0; + var ID; + for ( var id in animations ) { + + var animation = animations[ id ]; + ID = ID || animation.id; + for ( var i = 0; i < animation.sampler.length; i ++ ) { + + var sampler = animation.sampler[ i ]; + + sampler.create(); + + start = Math.min( start, sampler.startTime ); + end = Math.max( end, sampler.endTime ); + frames = Math.max( frames, sampler.input.length ); + + } + + } + + return { start: start, end: end, frames: frames, ID: ID }; + + }; + + function createSceneGraph ( node, parent ) { + + var obj = new THREE.Object3D(); + var skinned = false; + var skinController; + var morphController; + var i, j; + + // FIXME: controllers + + for ( i = 0; i < node.controllers.length; i ++ ) { + + var controller = controllers[ node.controllers[ i ].url ]; + + switch ( controller.type ) { + + case 'skin': + + if ( geometries[ controller.skin.source ] ) { + + var inst_geom = new InstanceGeometry(); + + inst_geom.url = controller.skin.source; + inst_geom.instance_material = node.controllers[ i ].instance_material; + + node.geometries.push( inst_geom ); + skinned = true; + skinController = node.controllers[ i ]; + + } else if ( controllers[ controller.skin.source ] ) { + + // urgh: controller can be chained + // handle the most basic case... + + var second = controllers[ controller.skin.source ]; + morphController = second; + // skinController = node.controllers[i]; + + if ( second.morph && geometries[ second.morph.source ] ) { + + var inst_geom = new InstanceGeometry(); + + inst_geom.url = second.morph.source; + inst_geom.instance_material = node.controllers[ i ].instance_material; + + node.geometries.push( inst_geom ); + + } + + } + + break; + + case 'morph': + + if ( geometries[ controller.morph.source ] ) { + + var inst_geom = new InstanceGeometry(); + + inst_geom.url = controller.morph.source; + inst_geom.instance_material = node.controllers[ i ].instance_material; + + node.geometries.push( inst_geom ); + morphController = node.controllers[ i ]; + + } + + console.log( 'ColladaLoader: Morph-controller partially supported.' ); + + default: + break; + + } + + } + + // geometries + + var double_sided_materials = {}; + + for ( i = 0; i < node.geometries.length; i ++ ) { + + var instance_geometry = node.geometries[ i ]; + var instance_materials = instance_geometry.instance_material; + var geometry = geometries[ instance_geometry.url ]; + var used_materials = {}; + var used_materials_array = []; + var num_materials = 0; + var first_material; + + if ( geometry ) { + + if ( ! geometry.mesh || ! geometry.mesh.primitives ) + continue; + + if ( obj.name.length === 0 ) { + + obj.name = geometry.id; + + } + + // collect used fx for this geometry-instance + + if ( instance_materials ) { + + for ( j = 0; j < instance_materials.length; j ++ ) { + + var instance_material = instance_materials[ j ]; + var mat = materials[ instance_material.target ]; + var effect_id = mat.instance_effect.url; + var shader = effects[ effect_id ].shader; + var material3js = shader.material; + + if ( geometry.doubleSided ) { + + if ( ! ( instance_material.symbol in double_sided_materials ) ) { + + var _copied_material = material3js.clone(); + _copied_material.side = THREE.DoubleSide; + double_sided_materials[ instance_material.symbol ] = _copied_material; + + } + + material3js = double_sided_materials[ instance_material.symbol ]; + + } + + material3js.opacity = ! material3js.opacity ? 1 : material3js.opacity; + used_materials[ instance_material.symbol ] = num_materials; + used_materials_array.push( material3js ); + first_material = material3js; + first_material.name = mat.name === null || mat.name === '' ? mat.id : mat.name; + num_materials ++; + + } + + } + + var mesh; + var material = first_material || new THREE.MeshLambertMaterial( { color: 0xdddddd, side: geometry.doubleSided ? THREE.DoubleSide : THREE.FrontSide } ); + var geom = geometry.mesh.geometry3js; + + if ( num_materials > 1 ) { + + material = new THREE.MeshFaceMaterial( used_materials_array ); + + for ( j = 0; j < geom.faces.length; j ++ ) { + + var face = geom.faces[ j ]; + face.materialIndex = used_materials[ face.daeMaterial ] + + } + + } + + if ( skinController !== undefined ) { + + + applySkin( geom, skinController ); + + if ( geom.morphTargets.length > 0 ) { + + material.morphTargets = true; + material.skinning = false; + + } else { + + material.morphTargets = false; + material.skinning = true; + + } + + + mesh = new THREE.SkinnedMesh( geom, material, false ); + + + //mesh.skeleton = skinController.skeleton; + //mesh.skinController = controllers[ skinController.url ]; + //mesh.skinInstanceController = skinController; + mesh.name = 'skin_' + skins.length; + + + + //mesh.animationHandle.setKey(0); + skins.push( mesh ); + + } else if ( morphController !== undefined ) { + + createMorph( geom, morphController ); + + material.morphTargets = true; + + mesh = new THREE.Mesh( geom, material ); + mesh.name = 'morph_' + morphs.length; + + morphs.push( mesh ); + + } else { + + if ( geom.isLineStrip === true ) { + + mesh = new THREE.Line( geom ); + + } else { + + mesh = new THREE.Mesh( geom, material ); + + } + + } + + obj.add( mesh ); + + } + + } + + for ( i = 0; i < node.cameras.length; i ++ ) { + + var instance_camera = node.cameras[ i ]; + var cparams = cameras[ instance_camera.url ]; + + var cam = new THREE.PerspectiveCamera( cparams.yfov, parseFloat( cparams.aspect_ratio ), + parseFloat( cparams.znear ), parseFloat( cparams.zfar ) ); + + obj.add( cam ); + + } + + for ( i = 0; i < node.lights.length; i ++ ) { + + var light = null; + var instance_light = node.lights[ i ]; + var lparams = lights[ instance_light.url ]; + + if ( lparams && lparams.technique ) { + + var color = lparams.color.getHex(); + var intensity = lparams.intensity; + var distance = lparams.distance; + var angle = lparams.falloff_angle; + var exponent; // Intentionally undefined, don't know what this is yet + + switch ( lparams.technique ) { + + case 'directional': + + light = new THREE.DirectionalLight( color, intensity, distance ); + light.position.set( 0, 0, 1 ); + break; + + case 'point': + + light = new THREE.PointLight( color, intensity, distance ); + break; + + case 'spot': + + light = new THREE.SpotLight( color, intensity, distance, angle, exponent ); + light.position.set( 0, 0, 1 ); + break; + + case 'ambient': + + light = new THREE.AmbientLight( color ); + break; + + } + + } + + if ( light ) { + + obj.add( light ); + + } + + } + + obj.name = node.name || node.id || ""; + obj.colladaId = node.id || ""; + obj.layer = node.layer || ""; + obj.matrix = node.matrix; + obj.matrix.decompose( obj.position, obj.quaternion, obj.scale ); + + if ( options.centerGeometry && obj.geometry ) { + + var delta = obj.geometry.center(); + delta.multiply( obj.scale ); + delta.applyQuaternion( obj.quaternion ); + + obj.position.sub( delta ); + + } + + for ( i = 0; i < node.nodes.length; i ++ ) { + + obj.add( createSceneGraph( node.nodes[ i ], node ) ); + + } + + return obj; + + }; + + function bakeAnimations ( node ) { + + if ( node.channels && node.channels.length ) { + + var keys = [], + sids = []; + + for ( var i = 0, il = node.channels.length; i < il; i ++ ) { + + var channel = node.channels[ i ], + fullSid = channel.fullSid, + sampler = channel.sampler, + input = sampler.input, + transform = node.getTransformBySid( channel.sid ), + member; + + if ( channel.arrIndices ) { + + member = []; + + for ( var j = 0, jl = channel.arrIndices.length; j < jl; j ++ ) { + + member[ j ] = getConvertedIndex( channel.arrIndices[ j ] ); + + } + + } else { + + member = getConvertedMember( channel.member ); + + } + + if ( transform ) { + + if ( sids.indexOf( fullSid ) === - 1 ) { + + sids.push( fullSid ); + + } + + for ( var j = 0, jl = input.length; j < jl; j ++ ) { + + var time = input[ j ], + data = sampler.getData( transform.type, j, member ), + key = findKey( keys, time ); + + if ( ! key ) { + + key = new Key( time ); + var timeNdx = findTimeNdx( keys, time ); + keys.splice( timeNdx === - 1 ? keys.length : timeNdx, 0, key ); + + } + + key.addTarget( fullSid, transform, member, data ); + + } + + } else { + + console.log( 'Could not find transform "' + channel.sid + '" in node ' + node.id ); + + } + + } + + // post process + for ( var i = 0; i < sids.length; i ++ ) { + + var sid = sids[ i ]; + + for ( var j = 0; j < keys.length; j ++ ) { + + var key = keys[ j ]; + + if ( ! key.hasTarget( sid ) ) { + + interpolateKeys( keys, key, j, sid ); + + } + + } + + } + + node.keys = keys; + node.sids = sids; + + } + + }; + + function extractDoubleSided( obj, element ) { + + obj.doubleSided = false; + + var node = element.querySelectorAll( 'extra double_sided' )[ 0 ]; + + if ( node ) { + + if ( node && parseInt( node.textContent, 10 ) === 1 ) { + + obj.doubleSided = true; + + } + + } + + }; + + function findKey ( keys, time ) { + + var retVal = null; + + for ( var i = 0, il = keys.length; i < il && retVal === null; i ++ ) { + + var key = keys[ i ]; + + if ( key.time === time ) { + + retVal = key; + + } else if ( key.time > time ) { + + break; + + } + + } + + return retVal; + + }; + + function findTimeNdx ( keys, time ) { + + var ndx = - 1; + + for ( var i = 0, il = keys.length; i < il && ndx === - 1; i ++ ) { + + var key = keys[ i ]; + + if ( key.time >= time ) { + + ndx = i; + + } + + } + + return ndx; + + }; + + function fixCoords( data, sign ) { + + if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { + + return; + + } + + switch ( upConversion ) { + + case 'XtoY': + + var tmp = data[ 0 ]; + data[ 0 ] = sign * data[ 1 ]; + data[ 1 ] = tmp; + break; + + case 'XtoZ': + + var tmp = data[ 2 ]; + data[ 2 ] = data[ 1 ]; + data[ 1 ] = data[ 0 ]; + data[ 0 ] = tmp; + break; + + case 'YtoX': + + var tmp = data[ 0 ]; + data[ 0 ] = data[ 1 ]; + data[ 1 ] = sign * tmp; + break; + + case 'YtoZ': + + var tmp = data[ 1 ]; + data[ 1 ] = sign * data[ 2 ]; + data[ 2 ] = tmp; + break; + + case 'ZtoX': + + var tmp = data[ 0 ]; + data[ 0 ] = data[ 1 ]; + data[ 1 ] = data[ 2 ]; + data[ 2 ] = tmp; + break; + + case 'ZtoY': + + var tmp = data[ 1 ]; + data[ 1 ] = data[ 2 ]; + data[ 2 ] = sign * tmp; + break; + + } + + }; + + //Walk the Collada tree and flatten the bones into a list, extract the position, quat and scale from the matrix + function flattenSkeleton ( skeleton ) { + + var list = []; + var walk = function( parentid, node, list ) { + + var bone = {}; + bone.name = node.sid; + bone.parent = parentid; + bone.matrix = node.matrix; + var data = [ new THREE.Vector3(), new THREE.Quaternion(), new THREE.Vector3() ]; + bone.matrix.decompose( data[ 0 ], data[ 1 ], data[ 2 ] ); + + bone.pos = [ data[ 0 ].x, data[ 0 ].y, data[ 0 ].z ]; + + bone.scl = [ data[ 2 ].x, data[ 2 ].y, data[ 2 ].z ]; + bone.rotq = [ data[ 1 ].x, data[ 1 ].y, data[ 1 ].z, data[ 1 ].w ]; + list.push( bone ); + + for ( var i in node.nodes ) { + + walk( node.sid, node.nodes[ i ], list ); + + } + + }; + + walk( - 1, skeleton, list ); + return list; + + }; + + function getConvertedTranslation( axis, data ) { + + if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { + + return data; + + } + + switch ( axis ) { + case 'X': + data = upConversion === 'XtoY' ? data * - 1 : data; + break; + case 'Y': + data = upConversion === 'YtoZ' || upConversion === 'YtoX' ? data * - 1 : data; + break; + case 'Z': + data = upConversion === 'ZtoY' ? data * - 1 : data ; + break; + default: + break; + } + + return data; + + }; + + function getConvertedVec3( data, offset ) { + + var arr = [ data[ offset ], data[ offset + 1 ], data[ offset + 2 ] ]; + fixCoords( arr, - 1 ); + return new THREE.Vector3( arr[ 0 ], arr[ 1 ], arr[ 2 ] ); + + }; + + function getConvertedMat4( data ) { + + if ( options.convertUpAxis ) { + + // First fix rotation and scale + + // Columns first + var arr = [ data[ 0 ], data[ 4 ], data[ 8 ] ]; + fixCoords( arr, - 1 ); + data[ 0 ] = arr[ 0 ]; + data[ 4 ] = arr[ 1 ]; + data[ 8 ] = arr[ 2 ]; + arr = [ data[ 1 ], data[ 5 ], data[ 9 ] ]; + fixCoords( arr, - 1 ); + data[ 1 ] = arr[ 0 ]; + data[ 5 ] = arr[ 1 ]; + data[ 9 ] = arr[ 2 ]; + arr = [ data[ 2 ], data[ 6 ], data[ 10 ] ]; + fixCoords( arr, - 1 ); + data[ 2 ] = arr[ 0 ]; + data[ 6 ] = arr[ 1 ]; + data[ 10 ] = arr[ 2 ]; + // Rows second + arr = [ data[ 0 ], data[ 1 ], data[ 2 ] ]; + fixCoords( arr, - 1 ); + data[ 0 ] = arr[ 0 ]; + data[ 1 ] = arr[ 1 ]; + data[ 2 ] = arr[ 2 ]; + arr = [ data[ 4 ], data[ 5 ], data[ 6 ] ]; + fixCoords( arr, - 1 ); + data[ 4 ] = arr[ 0 ]; + data[ 5 ] = arr[ 1 ]; + data[ 6 ] = arr[ 2 ]; + arr = [ data[ 8 ], data[ 9 ], data[ 10 ] ]; + fixCoords( arr, - 1 ); + data[ 8 ] = arr[ 0 ]; + data[ 9 ] = arr[ 1 ]; + data[ 10 ] = arr[ 2 ]; + + // Now fix translation + arr = [ data[ 3 ], data[ 7 ], data[ 11 ] ]; + fixCoords( arr, - 1 ); + data[ 3 ] = arr[ 0 ]; + data[ 7 ] = arr[ 1 ]; + data[ 11 ] = arr[ 2 ]; + + } + + return new THREE.Matrix4().set( + data[ 0 ], data[ 1 ], data[ 2 ], data[ 3 ], + data[ 4 ], data[ 5 ], data[ 6 ], data[ 7 ], + data[ 8 ], data[ 9 ], data[ 10 ], data[ 11 ], + data[ 12 ], data[ 13 ], data[ 14 ], data[ 15 ] + ); + + }; + + function getConvertedIndex( index ) { + + if ( index > - 1 && index < 3 ) { + + var members = [ 'X', 'Y', 'Z' ], + indices = { X: 0, Y: 1, Z: 2 }; + + index = getConvertedMember( members[ index ] ); + index = indices[ index ]; + + } + + return index; + + }; + + function getChannelsForNode ( node ) { + + var channels = []; + var startTime = 1000000; + var endTime = - 1000000; + + for ( var id in animations ) { + + var animation = animations[ id ]; + + for ( var i = 0; i < animation.channel.length; i ++ ) { + + var channel = animation.channel[ i ]; + var sampler = animation.sampler[ i ]; + var id = channel.target.split( '/' )[ 0 ]; + + if ( id == node.id ) { + + sampler.create(); + channel.sampler = sampler; + startTime = Math.min( startTime, sampler.startTime ); + endTime = Math.max( endTime, sampler.endTime ); + channels.push( channel ); + + } + + } + + } + + if ( channels.length ) { + + node.startTime = startTime; + node.endTime = endTime; + + } + + return channels; + + }; + + function getConvertedMember( member ) { + + if ( options.convertUpAxis ) { + + switch ( member ) { + + case 'X': + + switch ( upConversion ) { + + case 'XtoY': + case 'XtoZ': + case 'YtoX': + + member = 'Y'; + break; + + case 'ZtoX': + + member = 'Z'; + break; + + } + + break; + + case 'Y': + + switch ( upConversion ) { + + case 'XtoY': + case 'YtoX': + case 'ZtoX': + + member = 'X'; + break; + + case 'XtoZ': + case 'YtoZ': + case 'ZtoY': + + member = 'Z'; + break; + + } + + break; + + case 'Z': + + switch ( upConversion ) { + + case 'XtoZ': + + member = 'X'; + break; + + case 'YtoZ': + case 'ZtoX': + case 'ZtoY': + + member = 'Y'; + break; + + } + + break; + + } + + } + + return member; + + }; + + function getLibraryNode ( id ) { + + var nodes = COLLADA.querySelectorAll( 'library_nodes node' ); + + for ( var i = 0; i < nodes.length; i ++ ) { + + var attObj = nodes[ i ].attributes.getNamedItem( 'id' ); + if ( attObj && attObj.value === id ) { + + return nodes[ i ]; + + } + + } + + return undefined; + + }; + + // Get next key with given sid + function getNextKeyWith ( keys, fullSid, ndx ) { + + for ( ; ndx < keys.length; ndx ++ ) { + + var key = keys[ ndx ]; + + if ( key.hasTarget( fullSid ) ) { + + return key; + + } + + } + + return null; + + }; + + // Get previous key with given sid + function getPrevKeyWith ( keys, fullSid, ndx ) { + + ndx = ndx >= 0 ? ndx : ndx + keys.length; + + for ( ; ndx >= 0; ndx -- ) { + + var key = keys[ ndx ]; + + if ( key.hasTarget( fullSid ) ) { + + return key; + + } + + } + + return null; + + }; + + function interpolateKeys ( keys, key, ndx, fullSid ) { + + var prevKey = getPrevKeyWith( keys, fullSid, ndx ? ndx - 1 : 0 ), + nextKey = getNextKeyWith( keys, fullSid, ndx + 1 ); + + if ( prevKey && nextKey ) { + + var scale = ( key.time - prevKey.time ) / ( nextKey.time - prevKey.time ), + prevTarget = prevKey.getTarget( fullSid ), + nextData = nextKey.getTarget( fullSid ).data, + prevData = prevTarget.data, + data; + + if ( prevTarget.type === 'matrix' ) { + + data = prevData; + + } else if ( prevData.length ) { + + data = []; + + for ( var i = 0; i < prevData.length; ++ i ) { + + data[ i ] = prevData[ i ] + ( nextData[ i ] - prevData[ i ] ) * scale; + + } + + } else { + + data = prevData + ( nextData - prevData ) * scale; + + } + + key.addTarget( fullSid, prevTarget.transform, prevTarget.member, data ); + + } + + }; + + function recurseHierarchy( node ) { + + var n = visualScene.getChildById( node.colladaId, true ), + newData = null; + + if ( n && n.keys ) { + + newData = { + fps: 60, + hierarchy: [ { + node: n, + keys: n.keys, + sids: n.sids + } ], + node: node, + name: 'animation_' + node.name, + length: 0 + }; + + animData.push( newData ); + + for ( var i = 0, il = n.keys.length; i < il; i ++ ) { + + newData.length = Math.max( newData.length, n.keys[ i ].time ); + + } + + } else { + + newData = { + hierarchy: [ { + keys: [], + sids: [] + } ] + } + + } + + for ( var i = 0, il = node.children.length; i < il; i ++ ) { + + var d = recurseHierarchy( node.children[ i ] ); + + for ( var j = 0, jl = d.hierarchy.length; j < jl; j ++ ) { + + newData.hierarchy.push( { + keys: [], + sids: [] + } ); + + } + + } + + return newData; + + }; + + function setupSkeleton ( node, bones, frame, parent ) { + + node.world = node.world || new THREE.Matrix4(); + node.localworld = node.localworld || new THREE.Matrix4(); + node.world.copy( node.matrix ); + node.localworld.copy( node.matrix ); + + if ( node.channels && node.channels.length ) { + + var channel = node.channels[ 0 ]; + var m = channel.sampler.output[ frame ]; + + if ( m instanceof THREE.Matrix4 ) { + + node.world.copy( m ); + node.localworld.copy( m ); + if ( frame === 0 ) + node.matrix.copy( m ); + + } + + } + + if ( parent ) { + + node.world.multiplyMatrices( parent, node.world ); + + } + + bones.push( node ); + + for ( var i = 0; i < node.nodes.length; i ++ ) { + + setupSkeleton( node.nodes[ i ], bones, frame, node.world ); + + } + + }; + + function setupSkinningMatrices ( bones, skin ) { + + // FIXME: this is dumb... + + for ( var i = 0; i < bones.length; i ++ ) { + + var bone = bones[ i ]; + var found = - 1; + + if ( bone.type != 'JOINT' ) continue; + + for ( var j = 0; j < skin.joints.length; j ++ ) { + + if ( bone.sid === skin.joints[ j ] ) { + + found = j; + break; + + } + + } + + if ( found >= 0 ) { + + var inv = skin.invBindMatrices[ found ]; + + bone.invBindMatrix = inv; + bone.skinningMatrix = new THREE.Matrix4(); + bone.skinningMatrix.multiplyMatrices( bone.world, inv ); // (IBMi * JMi) + bone.animatrix = new THREE.Matrix4(); + + bone.animatrix.copy( bone.localworld ); + bone.weights = []; + + for ( var j = 0; j < skin.weights.length; j ++ ) { + + for ( var k = 0; k < skin.weights[ j ].length; k ++ ) { + + var w = skin.weights[ j ][ k ]; + + if ( w.joint === found ) { + + bone.weights.push( w ); + + } + + } + + } + + } else { + + console.warn( "ColladaLoader: Could not find joint '" + bone.sid + "'." ); + + bone.skinningMatrix = new THREE.Matrix4(); + bone.weights = []; + + } + + } + + }; + + //Move the vertices into the pose that is proper for the start of the animation + function skinToBindPose ( geometry, skeleton, skinController ) { + + var bones = []; + setupSkeleton( skeleton, bones, - 1 ); + setupSkinningMatrices( bones, skinController.skin ); + var v = new THREE.Vector3(); + var skinned = []; + + for ( var i = 0; i < geometry.vertices.length; i ++ ) { + + skinned.push( new THREE.Vector3() ); + + } + + for ( var i = 0; i < bones.length; i ++ ) { + + if ( bones[ i ].type != 'JOINT' ) continue; + + for ( var j = 0; j < bones[ i ].weights.length; j ++ ) { + + var w = bones[ i ].weights[ j ]; + var vidx = w.index; + var weight = w.weight; + + var o = geometry.vertices[ vidx ]; + var s = skinned[ vidx ]; + + v.x = o.x; + v.y = o.y; + v.z = o.z; + + v.applyMatrix4( bones[ i ].skinningMatrix ); + + s.x += ( v.x * weight ); + s.y += ( v.y * weight ); + s.z += ( v.z * weight ); + + } + + } + + for ( var i = 0; i < geometry.vertices.length; i ++ ) { + + geometry.vertices[ i ] = skinned[ i ]; + + } + + }; + + +} )(); diff --git a/node_modules/three/examples/js/loaders/ColladaLoader2.js b/node_modules/three/examples/js/loaders/ColladaLoader2.js new file mode 100644 index 00000000..d2746c02 --- /dev/null +++ b/node_modules/three/examples/js/loaders/ColladaLoader2.js @@ -0,0 +1,865 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.ColladaLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.ColladaLoader.prototype = { + + constructor: THREE.ColladaLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + function getBaseUrl( url ) { + + var parts = url.split( '/' ); + parts.pop(); + return ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/'; + + } + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( scope.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text, getBaseUrl( url ) ) ); + + }, onProgress, onError ); + + }, + + options: { + + set convertUpAxis ( value ) { + console.log( 'ColladaLoder.options.convertUpAxis: TODO' ); + } + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( text, baseUrl ) { + + function parseFloats( text ) { + + var parts = text.trim().split( /\s+/ ); + var array = new Array( parts.length ); + + for ( var i = 0, l = parts.length; i < l; i ++ ) { + array[ i ] = parseFloat( parts[ i ] ); + } + + return array; + + } + + function parseInts( text ) { + + var parts = text.trim().split( /\s+/ ); + var array = new Array( parts.length ); + + for ( var i = 0, l = parts.length; i < l; i ++ ) { + array[ i ] = parseInt( parts[ i ] ); + } + + return array; + + } + + function parseId( text ) { + + return text.substring( 1 ); + + } + + // asset + + function parseAsset( xml ) { + + return { + upAxis: parseAssetUpAxis( xml.getElementsByTagName( 'up_axis' )[ 0 ] ) + }; + + } + + function parseAssetUpAxis( xml ) { + + return xml !== undefined ? xml.textContent : 'Y_UP'; + + } + + // library + + function parseLibrary( data, libraryName, nodeName, parser ) { + + var library = xml.getElementsByTagName( libraryName )[ 0 ]; + + if ( library !== undefined ) { + + var elements = library.getElementsByTagName( nodeName ); + + for ( var i = 0; i < elements.length; i ++ ) { + + parser( elements[ i ] ); + + } + + } + + } + + function buildLibrary( data, builder ) { + + for ( var name in data ) { + + var object = data[ name ]; + object.build = builder( data[ name ] ); + + } + + } + + // get + + function getBuild( data, builder ) { + + if ( data.build !== undefined ) return data.build; + + data.build = builder( data ); + + return data.build; + + } + + // image + + var imageLoader = new THREE.ImageLoader(); + + function parseImage( xml ) { + + var data = { + url: xml.getElementsByTagName( 'init_from' )[ 0 ].textContent + }; + + library.images[ xml.getAttribute( 'id' ) ] = data; + + } + + function buildImage( data ) { + + if ( data.build !== undefined ) return data.build; + + var url = data.url; + + if ( baseUrl !== undefined ) url = baseUrl + url; + + return imageLoader.load( url ); + + } + + function getImage( id ) { + + return getBuild( library.images[ id ], buildImage ); + + } + + // effect + + function parseEffect( xml ) { + + } + + function buildEffect( data ) { + + } + + function getEffect( id ) { + + return getBuild( library.effects[ id ], buildEffect ); + + } + + // camera + + function parseCamera( xml ) { + + var data = { + name: xml.getAttribute( 'name' ) + }; + + library.cameras[ xml.getAttribute( 'id' ) ] = {}; + + } + + function buildCamera( data ) { + + var camera = new THREE.PerspectiveCamera(); + camera.name = data.name; + + return camera; + + } + + function getCamera( id ) { + + return getBuild( library.cameras[ id ], buildCamera ); + + } + + // light + + function parseLight( xml ) { + + var data = {}; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique_common': + data = parseLightTechnique( child ); + break; + + } + + } + + library.lights[ xml.getAttribute( 'id' ) ] = data; + + } + + function parseLightTechnique( xml ) { + + var data = {}; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'directional': + case 'point': + case 'spot': + case 'ambient': + + data.technique = child.nodeName; + data.parameters = parseLightParameters( child ); + + } + + } + + return data; + + } + + function parseLightParameters( xml ) { + + var data = {}; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'color': + var array = parseFloats( child.textContent ); + data.color = new THREE.Color().fromArray( array ); + break; + + case 'falloff_angle': + data.falloffAngle = parseFloat( child.textContent ); + break; + + case 'quadratic_attenuation': + var f = parseFloat( child.textContent ); + data.distance = f ? Math.sqrt( 1 / f ) : 0; + break; + + } + + } + + return data; + + } + + function buildLight( data ) { + + var light; + + switch ( data.technique ) { + + case 'directional': + light = new THREE.DirectionalLight(); + break; + + case 'point': + light = new THREE.PointLight(); + break; + + case 'spot': + light = new THREE.SpotLight(); + break; + + case 'ambient': + light = new THREE.AmbientLight(); + break; + + } + + if ( data.parameters.color ) light.color.copy( data.parameters.color ); + if ( data.parameters.distance ) light.distance = data.parameters.distance; + + return light; + + } + + function getLight( id ) { + + return getBuild( library.lights[ id ], buildLight ); + + } + + // geometry + + function parseGeometry( xml ) { + + var data = { + name: xml.getAttribute( 'name' ), + sources: {}, + primitives: [] + }; + + var mesh = xml.getElementsByTagName( 'mesh' )[ 0 ]; + + for ( var i = 0; i < mesh.childNodes.length; i ++ ) { + + var child = mesh.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'source': + data.sources[ child.getAttribute( 'id' ) ] = parseFloats( child.getElementsByTagName( 'float_array' )[ 0 ].textContent ); + break; + + case 'vertices': + data.sources[ child.getAttribute( 'id' ) ] = data.sources[ parseId( child.getElementsByTagName( 'input' )[ 0 ].getAttribute( 'source' ) ) ]; + break; + + case 'polygons': + console.log( 'ColladaLoader: Unsupported primitive type: ', child.nodeName ); + break; + + case 'lines': + case 'linestrips': + case 'polylist': + case 'triangles': + data.primitives.push( parseGeometryPrimitive( child ) ); + break; + + default: + console.log( child ); + + } + + } + + library.geometries[ xml.getAttribute( 'id' ) ] = data; + + } + + function parseGeometryPrimitive( xml ) { + + var primitive = { + type: xml.nodeName, + inputs: {}, + stride: 0 + }; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + var id = parseId( child.getAttribute( 'source' ) ); + var semantic = child.getAttribute( 'semantic' ); + var offset = parseInt( child.getAttribute( 'offset' ) ); + primitive.inputs[ semantic ] = { id: id, offset: offset }; + primitive.stride = Math.max( primitive.stride, offset + 1 ); + break; + + case 'vcount': + primitive.vcount = parseInts( child.textContent ); + break; + + case 'p': + primitive.p = parseInts( child.textContent ); + break; + + } + + } + + return primitive; + + } + + function buildGeometry( data ) { + + var group = new THREE.Group(); + + var sources = data.sources; + var primitives = data.primitives; + + if ( primitives.length === 0 ) return group; + + for ( var p = 0; p < primitives.length; p ++ ) { + + var primitive = primitives[ p ]; + + var inputs = primitive.inputs; + var stride = primitive.stride; + var vcount = primitive.vcount; + + var indices = primitive.p; + var vcount = primitive.vcount; + + var maxcount = 0; + + var geometry = new THREE.BufferGeometry(); + if ( data.name ) geometry.name = data.name; + + for ( var name in inputs ) { + + var input = inputs[ name ]; + + var source = sources[ input.id ]; + var offset = input.offset; + + var array = []; + + function pushVector( i ) { + + var index = indices[ i + offset ] * 3; + + if ( asset.upAxis === 'Z_UP' ) { + array.push( source[ index + 0 ], source[ index + 2 ], - source[ index + 1 ] ); + } else { + array.push( source[ index + 0 ], source[ index + 1 ], source[ index + 2 ] ); + } + + } + + if ( primitive.vcount !== undefined ) { + + var index = 0; + + for ( var i = 0, l = vcount.length; i < l; i ++ ) { + + var count = vcount[ i ]; + + if ( count === 4 ) { + + var a = index + stride * 0; + var b = index + stride * 1; + var c = index + stride * 2; + var d = index + stride * 3; + + pushVector( a ); pushVector( b ); pushVector( d ); + pushVector( b ); pushVector( c ); pushVector( d ); + + } else if ( count === 3 ) { + + var a = index + stride * 0; + var b = index + stride * 1; + var c = index + stride * 2; + + pushVector( a ); pushVector( b ); pushVector( c ); + + } else { + + maxcount = Math.max( maxcount, count ); + + } + + index += stride * count; + + } + + } else { + + for ( var i = 0, l = indices.length; i < l; i += stride ) { + + pushVector( i ); + + } + + } + + switch ( name ) { + + case 'VERTEX': + geometry.addAttribute( 'position', new THREE.Float32Attribute( array, 3 ) ); + break; + + case 'NORMAL': + geometry.addAttribute( 'normal', new THREE.Float32Attribute( array, 3 ) ); + break; + + } + + } + + if ( maxcount > 0 ) { + + console.log( 'ColladaLoader: Geometry', data.id, 'has faces with more than 4 vertices.' ); + + } + + switch ( primitive.type ) { + + case 'lines': + group.add( new THREE.LineSegments( geometry ) ); + break; + + case 'linestrips': + group.add( new THREE.Line( geometry ) ); + break; + + case 'triangles': + case 'polylist': + group.add( new THREE.Mesh( geometry ) ); + break; + + } + + } + + // flatten + + if ( group.children.length === 1 ) { + + return group.children[ 0 ]; + + } + + return group; + + } + + function getGeometry( id ) { + + return getBuild( library.geometries[ id ], buildGeometry ); + + } + + // nodes + + var matrix = new THREE.Matrix4(); + var vector = new THREE.Vector3(); + + function parseNode( xml ) { + + var data = { + name: xml.getAttribute( 'name' ), + matrix: new THREE.Matrix4(), + nodes: [], + instanceCameras: [], + instanceLights: [], + instanceGeometries: [], + instanceNodes: [] + }; + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'node': + parseNode( child ); + data.nodes.push( child.getAttribute( 'id' ) ); + break; + + case 'instance_camera': + data.instanceCameras.push( parseId( child.getAttribute( 'url' ) ) ); + break; + + case 'instance_light': + data.instanceLights.push( parseId( child.getAttribute( 'url' ) ) ); + break; + + case 'instance_geometry': + data.instanceGeometries.push( parseId( child.getAttribute( 'url' ) ) ); + break; + + case 'instance_node': + data.instanceNodes.push( parseId( child.getAttribute( 'url' ) ) ); + break; + + case 'matrix': + var array = parseFloats( child.textContent ); + data.matrix.multiply( matrix.fromArray( array ).transpose() ); // .transpose() when Z_UP? + break; + + case 'translate': + var array = parseFloats( child.textContent ); + vector.fromArray( array ); + data.matrix.multiply( matrix.makeTranslation( vector.x, vector.y, vector.z ) ); + break; + + case 'rotate': + var array = parseFloats( child.textContent ); + var angle = THREE.Math.degToRad( array[ 3 ] ); + data.matrix.multiply( matrix.makeRotationAxis( vector.fromArray( array ), angle ) ); + break; + + case 'scale': + var array = parseFloats( child.textContent ); + data.matrix.scale( vector.fromArray( array ) ); + break; + + case 'extra': + break; + + default: + console.log( child ); + break; + + } + + } + + if ( xml.getAttribute( 'id' ) !== null ) { + + library.nodes[ xml.getAttribute( 'id' ) ] = data; + + } + + return data; + + } + + function buildNode( data ) { + + var objects = []; + + var matrix = data.matrix; + var nodes = data.nodes; + var instanceCameras = data.instanceCameras; + var instanceLights = data.instanceLights; + var instanceGeometries = data.instanceGeometries; + var instanceNodes = data.instanceNodes; + + for ( var i = 0, l = nodes.length; i < l; i ++ ) { + + objects.push( getNode( nodes[ i ] ).clone() ); + + } + + for ( var i = 0, l = instanceCameras.length; i < l; i ++ ) { + + objects.push( getCamera( instanceCameras[ i ] ).clone() ); + + } + + for ( var i = 0, l = instanceLights.length; i < l; i ++ ) { + + objects.push( getLight( instanceLights[ i ] ).clone() ); + + } + + for ( var i = 0, l = instanceGeometries.length; i < l; i ++ ) { + + objects.push( getGeometry( instanceGeometries[ i ] ).clone() ); + + } + + for ( var i = 0, l = instanceNodes.length; i < l; i ++ ) { + + objects.push( getNode( instanceNodes[ i ] ).clone() ); + + } + + var object; + + if ( objects.length === 1 ) { + + object = objects[ 0 ]; + + } else { + + object = new THREE.Group(); + + for ( var i = 0; i < objects.length; i ++ ) { + + object.add( objects[ i ] ); + + } + + } + + object.name = data.name; + matrix.decompose( object.position, object.quaternion, object.scale ); + + return object; + + } + + function getNode( id ) { + + return getBuild( library.nodes[ id ], buildNode ); + + } + + // visual scenes + + function parseVisualScene( xml ) { + + var data = { + name: xml.getAttribute( 'name' ), + children: [] + }; + + var elements = xml.getElementsByTagName( 'node' ); + + for ( var i = 0; i < elements.length; i ++ ) { + + data.children.push( parseNode( elements[ i ] ) ); + + } + + library.visualScenes[ xml.getAttribute( 'id' ) ] = data; + + } + + function buildVisualScene( data ) { + + var group = new THREE.Group(); + group.name = data.name; + + var children = data.children; + + for ( var i = 0; i < children.length; i ++ ) { + + group.add( buildNode( children[ i ] ) ); + + } + + return group; + + } + + function getVisualScene( id ) { + + return getBuild( library.visualScenes[ id ], buildVisualScene ); + + } + + // scenes + + function parseScene( xml ) { + + var scene = xml.getElementsByTagName( 'scene' )[ 0 ]; + var instance = scene.getElementsByTagName( 'instance_visual_scene' )[ 0 ]; + return getVisualScene( parseId( instance.getAttribute( 'url' ) ) ); + + } + + console.time( 'ColladaLoader' ); + + console.time( 'ColladaLoader: DOMParser' ); + + var xml = new DOMParser().parseFromString( text, 'application/xml' ); + + console.timeEnd( 'ColladaLoader: DOMParser' ); + + // metadata + + var version = xml.getElementsByTagName( 'COLLADA' )[ 0 ].getAttribute( 'version' ); + console.log( 'ColladaLoader: File version', version ); + + var asset = parseAsset( xml.getElementsByTagName( 'asset' )[ 0 ] ); + + // + + var library = { + images: {}, + // effects: {}, + cameras: {}, + lights: {}, + geometries: {}, + nodes: {}, + visualScenes: {} + }; + + console.time( 'ColladaLoader: Parse' ); + + parseLibrary( library.images, 'library_images', 'image', parseImage ); + // parseLibrary( library.effects, 'library_effects', 'effect', parseEffect ); + parseLibrary( library.cameras, 'library_cameras', 'camera', parseCamera ); + parseLibrary( library.lights, 'library_lights', 'light', parseLight ); + parseLibrary( library.geometries, 'library_geometries', 'geometry', parseGeometry ); + parseLibrary( library.nodes, 'library_nodes', 'node', parseNode ); + parseLibrary( library.visualScenes, 'library_visual_scenes', 'visual_scene', parseVisualScene ); + + console.timeEnd( 'ColladaLoader: Parse' ); + + console.time( 'ColladaLoader: Build' ); + + // buildLibrary( library.images, buildImage ); + // buildLibrary( library.effects, buildEffect ); + buildLibrary( library.cameras, buildCamera ); + buildLibrary( library.lights, buildLight ); + buildLibrary( library.geometries, buildGeometry ); + buildLibrary( library.nodes, buildNode ); + buildLibrary( library.visualScenes, buildVisualScene ); + + console.timeEnd( 'ColladaLoader: Build' ); + + // console.log( library ); + + var scene = parseScene( xml ); + + console.timeEnd( 'ColladaLoader' ); + + // console.log( scene ); + + return { + animations: [], + kinematics: { joints: [] }, + scene: scene + }; + + } + +}; diff --git a/node_modules/three/examples/js/loaders/DDSLoader.js b/node_modules/three/examples/js/loaders/DDSLoader.js new file mode 100644 index 00000000..963ee2a7 --- /dev/null +++ b/node_modules/three/examples/js/loaders/DDSLoader.js @@ -0,0 +1,262 @@ +/* + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.DDSLoader = function () { + + this._parser = THREE.DDSLoader.parse; + +}; + +THREE.DDSLoader.prototype = Object.create( THREE.CompressedTextureLoader.prototype ); +THREE.DDSLoader.prototype.constructor = THREE.DDSLoader; + +THREE.DDSLoader.parse = function ( buffer, loadMipmaps ) { + + var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 }; + + // Adapted from @toji's DDS utils + // https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js + + // All values and structures referenced from: + // http://msdn.microsoft.com/en-us/library/bb943991.aspx/ + + var DDS_MAGIC = 0x20534444; + + var DDSD_CAPS = 0x1, + DDSD_HEIGHT = 0x2, + DDSD_WIDTH = 0x4, + DDSD_PITCH = 0x8, + DDSD_PIXELFORMAT = 0x1000, + DDSD_MIPMAPCOUNT = 0x20000, + DDSD_LINEARSIZE = 0x80000, + DDSD_DEPTH = 0x800000; + + var DDSCAPS_COMPLEX = 0x8, + DDSCAPS_MIPMAP = 0x400000, + DDSCAPS_TEXTURE = 0x1000; + + var DDSCAPS2_CUBEMAP = 0x200, + DDSCAPS2_CUBEMAP_POSITIVEX = 0x400, + DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800, + DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000, + DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000, + DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000, + DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000, + DDSCAPS2_VOLUME = 0x200000; + + var DDPF_ALPHAPIXELS = 0x1, + DDPF_ALPHA = 0x2, + DDPF_FOURCC = 0x4, + DDPF_RGB = 0x40, + DDPF_YUV = 0x200, + DDPF_LUMINANCE = 0x20000; + + function fourCCToInt32( value ) { + + return value.charCodeAt( 0 ) + + ( value.charCodeAt( 1 ) << 8 ) + + ( value.charCodeAt( 2 ) << 16 ) + + ( value.charCodeAt( 3 ) << 24 ); + + } + + function int32ToFourCC( value ) { + + return String.fromCharCode( + value & 0xff, + ( value >> 8 ) & 0xff, + ( value >> 16 ) & 0xff, + ( value >> 24 ) & 0xff + ); + + } + + function loadARGBMip( buffer, dataOffset, width, height ) { + + var dataLength = width * height * 4; + var srcBuffer = new Uint8Array( buffer, dataOffset, dataLength ); + var byteArray = new Uint8Array( dataLength ); + var dst = 0; + var src = 0; + for ( var y = 0; y < height; y ++ ) { + + for ( var x = 0; x < width; x ++ ) { + + var b = srcBuffer[ src ]; src ++; + var g = srcBuffer[ src ]; src ++; + var r = srcBuffer[ src ]; src ++; + var a = srcBuffer[ src ]; src ++; + byteArray[ dst ] = r; dst ++; //r + byteArray[ dst ] = g; dst ++; //g + byteArray[ dst ] = b; dst ++; //b + byteArray[ dst ] = a; dst ++; //a + + } + + } + return byteArray; + + } + + var FOURCC_DXT1 = fourCCToInt32( "DXT1" ); + var FOURCC_DXT3 = fourCCToInt32( "DXT3" ); + var FOURCC_DXT5 = fourCCToInt32( "DXT5" ); + + var headerLengthInt = 31; // The header length in 32 bit ints + + // Offsets into the header array + + var off_magic = 0; + + var off_size = 1; + var off_flags = 2; + var off_height = 3; + var off_width = 4; + + var off_mipmapCount = 7; + + var off_pfFlags = 20; + var off_pfFourCC = 21; + var off_RGBBitCount = 22; + var off_RBitMask = 23; + var off_GBitMask = 24; + var off_BBitMask = 25; + var off_ABitMask = 26; + + var off_caps = 27; + var off_caps2 = 28; + var off_caps3 = 29; + var off_caps4 = 30; + + // Parse header + + var header = new Int32Array( buffer, 0, headerLengthInt ); + + if ( header[ off_magic ] !== DDS_MAGIC ) { + + console.error( 'THREE.DDSLoader.parse: Invalid magic number in DDS header.' ); + return dds; + + } + + if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) { + + console.error( 'THREE.DDSLoader.parse: Unsupported format, must contain a FourCC code.' ); + return dds; + + } + + var blockBytes; + + var fourCC = header[ off_pfFourCC ]; + + var isRGBAUncompressed = false; + + switch ( fourCC ) { + + case FOURCC_DXT1: + + blockBytes = 8; + dds.format = THREE.RGB_S3TC_DXT1_Format; + break; + + case FOURCC_DXT3: + + blockBytes = 16; + dds.format = THREE.RGBA_S3TC_DXT3_Format; + break; + + case FOURCC_DXT5: + + blockBytes = 16; + dds.format = THREE.RGBA_S3TC_DXT5_Format; + break; + + default: + + if ( header[ off_RGBBitCount ] === 32 + && header[ off_RBitMask ] & 0xff0000 + && header[ off_GBitMask ] & 0xff00 + && header[ off_BBitMask ] & 0xff + && header[ off_ABitMask ] & 0xff000000 ) { + + isRGBAUncompressed = true; + blockBytes = 64; + dds.format = THREE.RGBAFormat; + + } else { + + console.error( 'THREE.DDSLoader.parse: Unsupported FourCC code ', int32ToFourCC( fourCC ) ); + return dds; + + } + } + + dds.mipmapCount = 1; + + if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) { + + dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] ); + + } + + var caps2 = header[ off_caps2 ]; + dds.isCubemap = caps2 & DDSCAPS2_CUBEMAP ? true : false; + if ( dds.isCubemap && ( + ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEX ) || + ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEX ) || + ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEY ) || + ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEY ) || + ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEZ ) || + ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ ) + ) ) { + + console.error( 'THREE.DDSLoader.parse: Incomplete cubemap faces' ); + return dds; + + } + + dds.width = header[ off_width ]; + dds.height = header[ off_height ]; + + var dataOffset = header[ off_size ] + 4; + + // Extract mipmaps buffers + + var faces = dds.isCubemap ? 6 : 1; + + for ( var face = 0; face < faces; face ++ ) { + + var width = dds.width; + var height = dds.height; + + for ( var i = 0; i < dds.mipmapCount; i ++ ) { + + if ( isRGBAUncompressed ) { + + var byteArray = loadARGBMip( buffer, dataOffset, width, height ); + var dataLength = byteArray.length; + + } else { + + var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes; + var byteArray = new Uint8Array( buffer, dataOffset, dataLength ); + + } + + var mipmap = { "data": byteArray, "width": width, "height": height }; + dds.mipmaps.push( mipmap ); + + dataOffset += dataLength; + + width = Math.max( width >> 1, 1 ); + height = Math.max( height >> 1, 1 ); + + } + + } + + return dds; + +}; diff --git a/node_modules/three/examples/js/loaders/KMZLoader.js b/node_modules/three/examples/js/loaders/KMZLoader.js new file mode 100644 index 00000000..c1e9fdd9 --- /dev/null +++ b/node_modules/three/examples/js/loaders/KMZLoader.js @@ -0,0 +1,54 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.KMZLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.KMZLoader.prototype = { + + constructor: THREE.KMZLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text ) ); + + }, onProgress, onError ); + + }, + + parse: function ( data ) { + + var zip = new JSZip( data ); + + // console.log( zip ); + + for ( var name in zip.files ) { + + if ( name.toLowerCase().substr( - 4 ) === '.dae' ) { + + return new THREE.ColladaLoader().parse( zip.file( name ).asText() ); + + } + + } + + console.error( 'KZMLoader: Couldn\'t find .dae file.' ); + + return { + scene: new THREE.Group() + } + + } + +}; diff --git a/node_modules/three/examples/js/loaders/MD2Loader.js b/node_modules/three/examples/js/loaders/MD2Loader.js new file mode 100644 index 00000000..c9c1153f --- /dev/null +++ b/node_modules/three/examples/js/loaders/MD2Loader.js @@ -0,0 +1,318 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.MD2Loader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.MD2Loader.prototype = { + + constructor: THREE.MD2Loader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( buffer ) { + + onLoad( scope.parse( buffer ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: ( function () { + + var normals = [ + [ -0.525731, 0.000000, 0.850651 ], [ -0.442863, 0.238856, 0.864188 ], + [ -0.295242, 0.000000, 0.955423 ], [ -0.309017, 0.500000, 0.809017 ], + [ -0.162460, 0.262866, 0.951056 ], [ 0.000000, 0.000000, 1.000000 ], + [ 0.000000, 0.850651, 0.525731 ], [ -0.147621, 0.716567, 0.681718 ], + [ 0.147621, 0.716567, 0.681718 ], [ 0.000000, 0.525731, 0.850651 ], + [ 0.309017, 0.500000, 0.809017 ], [ 0.525731, 0.000000, 0.850651 ], + [ 0.295242, 0.000000, 0.955423 ], [ 0.442863, 0.238856, 0.864188 ], + [ 0.162460, 0.262866, 0.951056 ], [ -0.681718, 0.147621, 0.716567 ], + [ -0.809017, 0.309017, 0.500000 ], [ -0.587785, 0.425325, 0.688191 ], + [ -0.850651, 0.525731, 0.000000 ], [ -0.864188, 0.442863, 0.238856 ], + [ -0.716567, 0.681718, 0.147621 ], [ -0.688191, 0.587785, 0.425325 ], + [ -0.500000, 0.809017, 0.309017 ], [ -0.238856, 0.864188, 0.442863 ], + [ -0.425325, 0.688191, 0.587785 ], [ -0.716567, 0.681718, -0.147621 ], + [ -0.500000, 0.809017, -0.309017 ], [ -0.525731, 0.850651, 0.000000 ], + [ 0.000000, 0.850651, -0.525731 ], [ -0.238856, 0.864188, -0.442863 ], + [ 0.000000, 0.955423, -0.295242 ], [ -0.262866, 0.951056, -0.162460 ], + [ 0.000000, 1.000000, 0.000000 ], [ 0.000000, 0.955423, 0.295242 ], + [ -0.262866, 0.951056, 0.162460 ], [ 0.238856, 0.864188, 0.442863 ], + [ 0.262866, 0.951056, 0.162460 ], [ 0.500000, 0.809017, 0.309017 ], + [ 0.238856, 0.864188, -0.442863 ], [ 0.262866, 0.951056, -0.162460 ], + [ 0.500000, 0.809017, -0.309017 ], [ 0.850651, 0.525731, 0.000000 ], + [ 0.716567, 0.681718, 0.147621 ], [ 0.716567, 0.681718, -0.147621 ], + [ 0.525731, 0.850651, 0.000000 ], [ 0.425325, 0.688191, 0.587785 ], + [ 0.864188, 0.442863, 0.238856 ], [ 0.688191, 0.587785, 0.425325 ], + [ 0.809017, 0.309017, 0.500000 ], [ 0.681718, 0.147621, 0.716567 ], + [ 0.587785, 0.425325, 0.688191 ], [ 0.955423, 0.295242, 0.000000 ], + [ 1.000000, 0.000000, 0.000000 ], [ 0.951056, 0.162460, 0.262866 ], + [ 0.850651, -0.525731, 0.000000 ], [ 0.955423, -0.295242, 0.000000 ], + [ 0.864188, -0.442863, 0.238856 ], [ 0.951056, -0.162460, 0.262866 ], + [ 0.809017, -0.309017, 0.500000 ], [ 0.681718, -0.147621, 0.716567 ], + [ 0.850651, 0.000000, 0.525731 ], [ 0.864188, 0.442863, -0.238856 ], + [ 0.809017, 0.309017, -0.500000 ], [ 0.951056, 0.162460, -0.262866 ], + [ 0.525731, 0.000000, -0.850651 ], [ 0.681718, 0.147621, -0.716567 ], + [ 0.681718, -0.147621, -0.716567 ], [ 0.850651, 0.000000, -0.525731 ], + [ 0.809017, -0.309017, -0.500000 ], [ 0.864188, -0.442863, -0.238856 ], + [ 0.951056, -0.162460, -0.262866 ], [ 0.147621, 0.716567, -0.681718 ], + [ 0.309017, 0.500000, -0.809017 ], [ 0.425325, 0.688191, -0.587785 ], + [ 0.442863, 0.238856, -0.864188 ], [ 0.587785, 0.425325, -0.688191 ], + [ 0.688191, 0.587785, -0.425325 ], [ -0.147621, 0.716567, -0.681718 ], + [ -0.309017, 0.500000, -0.809017 ], [ 0.000000, 0.525731, -0.850651 ], + [ -0.525731, 0.000000, -0.850651 ], [ -0.442863, 0.238856, -0.864188 ], + [ -0.295242, 0.000000, -0.955423 ], [ -0.162460, 0.262866, -0.951056 ], + [ 0.000000, 0.000000, -1.000000 ], [ 0.295242, 0.000000, -0.955423 ], + [ 0.162460, 0.262866, -0.951056 ], [ -0.442863, -0.238856, -0.864188 ], + [ -0.309017, -0.500000, -0.809017 ], [ -0.162460, -0.262866, -0.951056 ], + [ 0.000000, -0.850651, -0.525731 ], [ -0.147621, -0.716567, -0.681718 ], + [ 0.147621, -0.716567, -0.681718 ], [ 0.000000, -0.525731, -0.850651 ], + [ 0.309017, -0.500000, -0.809017 ], [ 0.442863, -0.238856, -0.864188 ], + [ 0.162460, -0.262866, -0.951056 ], [ 0.238856, -0.864188, -0.442863 ], + [ 0.500000, -0.809017, -0.309017 ], [ 0.425325, -0.688191, -0.587785 ], + [ 0.716567, -0.681718, -0.147621 ], [ 0.688191, -0.587785, -0.425325 ], + [ 0.587785, -0.425325, -0.688191 ], [ 0.000000, -0.955423, -0.295242 ], + [ 0.000000, -1.000000, 0.000000 ], [ 0.262866, -0.951056, -0.162460 ], + [ 0.000000, -0.850651, 0.525731 ], [ 0.000000, -0.955423, 0.295242 ], + [ 0.238856, -0.864188, 0.442863 ], [ 0.262866, -0.951056, 0.162460 ], + [ 0.500000, -0.809017, 0.309017 ], [ 0.716567, -0.681718, 0.147621 ], + [ 0.525731, -0.850651, 0.000000 ], [ -0.238856, -0.864188, -0.442863 ], + [ -0.500000, -0.809017, -0.309017 ], [ -0.262866, -0.951056, -0.162460 ], + [ -0.850651, -0.525731, 0.000000 ], [ -0.716567, -0.681718, -0.147621 ], + [ -0.716567, -0.681718, 0.147621 ], [ -0.525731, -0.850651, 0.000000 ], + [ -0.500000, -0.809017, 0.309017 ], [ -0.238856, -0.864188, 0.442863 ], + [ -0.262866, -0.951056, 0.162460 ], [ -0.864188, -0.442863, 0.238856 ], + [ -0.809017, -0.309017, 0.500000 ], [ -0.688191, -0.587785, 0.425325 ], + [ -0.681718, -0.147621, 0.716567 ], [ -0.442863, -0.238856, 0.864188 ], + [ -0.587785, -0.425325, 0.688191 ], [ -0.309017, -0.500000, 0.809017 ], + [ -0.147621, -0.716567, 0.681718 ], [ -0.425325, -0.688191, 0.587785 ], + [ -0.162460, -0.262866, 0.951056 ], [ 0.442863, -0.238856, 0.864188 ], + [ 0.162460, -0.262866, 0.951056 ], [ 0.309017, -0.500000, 0.809017 ], + [ 0.147621, -0.716567, 0.681718 ], [ 0.000000, -0.525731, 0.850651 ], + [ 0.425325, -0.688191, 0.587785 ], [ 0.587785, -0.425325, 0.688191 ], + [ 0.688191, -0.587785, 0.425325 ], [ -0.955423, 0.295242, 0.000000 ], + [ -0.951056, 0.162460, 0.262866 ], [ -1.000000, 0.000000, 0.000000 ], + [ -0.850651, 0.000000, 0.525731 ], [ -0.955423, -0.295242, 0.000000 ], + [ -0.951056, -0.162460, 0.262866 ], [ -0.864188, 0.442863, -0.238856 ], + [ -0.951056, 0.162460, -0.262866 ], [ -0.809017, 0.309017, -0.500000 ], + [ -0.864188, -0.442863, -0.238856 ], [ -0.951056, -0.162460, -0.262866 ], + [ -0.809017, -0.309017, -0.500000 ], [ -0.681718, 0.147621, -0.716567 ], + [ -0.681718, -0.147621, -0.716567 ], [ -0.850651, 0.000000, -0.525731 ], + [ -0.688191, 0.587785, -0.425325 ], [ -0.587785, 0.425325, -0.688191 ], + [ -0.425325, 0.688191, -0.587785 ], [ -0.425325, -0.688191, -0.587785 ], + [ -0.587785, -0.425325, -0.688191 ], [ -0.688191, -0.587785, -0.425325 ] + ]; + + return function ( buffer ) { + + console.time( 'MD2Loader' ); + + var data = new DataView( buffer ); + + // http://tfc.duke.free.fr/coding/md2-specs-en.html + + var header = {}; + var headerNames = [ + 'ident', 'version', + 'skinwidth', 'skinheight', + 'framesize', + 'num_skins', 'num_vertices', 'num_st', 'num_tris', 'num_glcmds', 'num_frames', + 'offset_skins', 'offset_st', 'offset_tris', 'offset_frames', 'offset_glcmds', 'offset_end' + ]; + + for ( var i = 0; i < headerNames.length; i ++ ) { + + header[ headerNames[ i ] ] = data.getInt32( i * 4, true ); + + } + + if ( header.ident !== 844121161 || header.version !== 8 ) { + + console.error( 'Not a valid MD2 file' ); + return; + + } + + if ( header.offset_end !== data.byteLength ) { + + console.error( 'Corrupted MD2 file' ); + return; + + } + + // + + var geometry = new THREE.Geometry(); + + // uvs + + var uvs = []; + var offset = header.offset_st; + + for ( var i = 0, l = header.num_st; i < l; i ++ ) { + + var u = data.getInt16( offset + 0, true ); + var v = data.getInt16( offset + 2, true ); + + uvs.push( new THREE.Vector2( u / header.skinwidth, 1 - ( v / header.skinheight ) ) ); + + offset += 4; + + } + + // triangles + + var offset = header.offset_tris; + + for ( var i = 0, l = header.num_tris; i < l; i ++ ) { + + var a = data.getUint16( offset + 0, true ); + var b = data.getUint16( offset + 2, true ); + var c = data.getUint16( offset + 4, true ); + + geometry.faces.push( new THREE.Face3( a, b, c ) ); + + geometry.faceVertexUvs[ 0 ].push( [ + uvs[ data.getUint16( offset + 6, true ) ], + uvs[ data.getUint16( offset + 8, true ) ], + uvs[ data.getUint16( offset + 10, true ) ] + ] ); + + offset += 12; + + } + + // frames + + var translation = new THREE.Vector3(); + var scale = new THREE.Vector3(); + var string = []; + + var offset = header.offset_frames; + + for ( var i = 0, l = header.num_frames; i < l; i ++ ) { + + scale.set( + data.getFloat32( offset + 0, true ), + data.getFloat32( offset + 4, true ), + data.getFloat32( offset + 8, true ) + ); + + translation.set( + data.getFloat32( offset + 12, true ), + data.getFloat32( offset + 16, true ), + data.getFloat32( offset + 20, true ) + ); + + offset += 24; + + for ( var j = 0; j < 16; j ++ ) { + + var character = data.getUint8( offset + j, true ); + if( character === 0 ) break; + + string[ j ] = character; + + } + + var frame = { + name: String.fromCharCode.apply( null, string ), + vertices: [], + normals: [] + }; + + offset += 16; + + for ( var j = 0; j < header.num_vertices; j ++ ) { + + var x = data.getUint8( offset ++, true ); + var y = data.getUint8( offset ++, true ); + var z = data.getUint8( offset ++, true ); + var n = normals[ data.getUint8( offset ++, true ) ]; + + var vertex = new THREE.Vector3( + x * scale.x + translation.x, + z * scale.z + translation.z, + y * scale.y + translation.y + ); + + var normal = new THREE.Vector3( n[ 0 ], n[ 2 ], n[ 1 ] ); + + frame.vertices.push( vertex ); + frame.normals.push( normal ); + + } + + geometry.morphTargets.push( frame ); + + } + + // Static + + geometry.vertices = geometry.morphTargets[ 0 ].vertices; + + var morphTarget = geometry.morphTargets[ 0 ]; + + for ( var j = 0, jl = geometry.faces.length; j < jl; j ++ ) { + + var face = geometry.faces[ j ]; + + face.vertexNormals = [ + morphTarget.normals[ face.a ], + morphTarget.normals[ face.b ], + morphTarget.normals[ face.c ] + ]; + + } + + + // Convert to geometry.morphNormals + + for ( var i = 0, l = geometry.morphTargets.length; i < l; i ++ ) { + + var morphTarget = geometry.morphTargets[ i ]; + var vertexNormals = []; + + for ( var j = 0, jl = geometry.faces.length; j < jl; j ++ ) { + + var face = geometry.faces[ j ]; + + vertexNormals.push( { + a: morphTarget.normals[ face.a ], + b: morphTarget.normals[ face.b ], + c: morphTarget.normals[ face.c ] + } ); + + } + + geometry.morphNormals.push( { vertexNormals: vertexNormals } ); + + } + + geometry.animations = THREE.AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ) + + console.timeEnd( 'MD2Loader' ); + + return geometry; + + } + + } )() + +} diff --git a/node_modules/three/examples/js/loaders/MTLLoader.js b/node_modules/three/examples/js/loaders/MTLLoader.js new file mode 100644 index 00000000..7c89f173 --- /dev/null +++ b/node_modules/three/examples/js/loaders/MTLLoader.js @@ -0,0 +1,466 @@ +/** + * Loads a Wavefront .mtl file specifying materials + * + * @author angelxuanchang + */ + +THREE.MTLLoader = function( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.MTLLoader.prototype = { + + constructor: THREE.MTLLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text ) ); + + }, onProgress, onError ); + + }, + + setBaseUrl: function( value ) { + + this.baseUrl = value; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setMaterialOptions: function ( value ) { + + this.materialOptions = value; + + }, + + /** + * Parses loaded MTL file + * @param text - Content of MTL file + * @return {THREE.MTLLoader.MaterialCreator} + */ + parse: function ( text ) { + + var lines = text.split( "\n" ); + var info = {}; + var delimiter_pattern = /\s+/; + var materialsInfo = {}; + + for ( var i = 0; i < lines.length; i ++ ) { + + var line = lines[ i ]; + line = line.trim(); + + if ( line.length === 0 || line.charAt( 0 ) === '#' ) { + + // Blank line or comment ignore + continue; + + } + + var pos = line.indexOf( ' ' ); + + var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line; + key = key.toLowerCase(); + + var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : ""; + value = value.trim(); + + if ( key === "newmtl" ) { + + // New material + + info = { name: value }; + materialsInfo[ value ] = info; + + } else if ( info ) { + + if ( key === "ka" || key === "kd" || key === "ks" ) { + + var ss = value.split( delimiter_pattern, 3 ); + info[ key ] = [ parseFloat( ss[ 0 ] ), parseFloat( ss[ 1 ] ), parseFloat( ss[ 2 ] ) ]; + + } else { + + info[ key ] = value; + + } + + } + + } + + var materialCreator = new THREE.MTLLoader.MaterialCreator( this.baseUrl, this.materialOptions ); + materialCreator.setCrossOrigin( this.crossOrigin ); + materialCreator.setManager( this.manager ); + materialCreator.setMaterials( materialsInfo ); + return materialCreator; + + } + +}; + +/** + * Create a new THREE-MTLLoader.MaterialCreator + * @param baseUrl - Url relative to which textures are loaded + * @param options - Set of options on how to construct the materials + * side: Which side to apply the material + * THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide + * wrap: What type of wrapping to apply for textures + * THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping + * normalizeRGB: RGBs need to be normalized to 0-1 from 0-255 + * Default: false, assumed to be already normalized + * ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's + * Default: false + * invertTransparency: If transparency need to be inverted (inversion is needed if d = 0 is fully opaque) + * Default: false (d = 1 is fully opaque) + * @constructor + */ + +THREE.MTLLoader.MaterialCreator = function( baseUrl, options ) { + + this.baseUrl = baseUrl; + this.options = options; + this.materialsInfo = {}; + this.materials = {}; + this.materialsArray = []; + this.nameLookup = {}; + + this.side = ( this.options && this.options.side ) ? this.options.side : THREE.FrontSide; + this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREE.RepeatWrapping; + +}; + +THREE.MTLLoader.MaterialCreator.prototype = { + + constructor: THREE.MTLLoader.MaterialCreator, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setManager: function ( value ) { + + this.manager = value; + + }, + + setMaterials: function( materialsInfo ) { + + this.materialsInfo = this.convert( materialsInfo ); + this.materials = {}; + this.materialsArray = []; + this.nameLookup = {}; + + }, + + convert: function( materialsInfo ) { + + if ( ! this.options ) return materialsInfo; + + var converted = {}; + + for ( var mn in materialsInfo ) { + + // Convert materials info into normalized form based on options + + var mat = materialsInfo[ mn ]; + + var covmat = {}; + + converted[ mn ] = covmat; + + for ( var prop in mat ) { + + var save = true; + var value = mat[ prop ]; + var lprop = prop.toLowerCase(); + + switch ( lprop ) { + + case 'kd': + case 'ka': + case 'ks': + + // Diffuse color (color under white light) using RGB values + + if ( this.options && this.options.normalizeRGB ) { + + value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ]; + + } + + if ( this.options && this.options.ignoreZeroRGBs ) { + + if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 1 ] === 0 ) { + + // ignore + + save = false; + + } + + } + + break; + + case 'd': + + // According to MTL format (http://paulbourke.net/dataformats/mtl/): + // d is dissolve for current material + // factor of 1.0 is fully opaque, a factor of 0 is fully dissolved (completely transparent) + + if ( this.options && this.options.invertTransparency ) { + + value = 1 - value; + + } + + break; + + default: + + break; + } + + if ( save ) { + + covmat[ lprop ] = value; + + } + + } + + } + + return converted; + + }, + + preload: function () { + + for ( var mn in this.materialsInfo ) { + + this.create( mn ); + + } + + }, + + getIndex: function( materialName ) { + + return this.nameLookup[ materialName ]; + + }, + + getAsArray: function() { + + var index = 0; + + for ( var mn in this.materialsInfo ) { + + this.materialsArray[ index ] = this.create( mn ); + this.nameLookup[ mn ] = index; + index ++; + + } + + return this.materialsArray; + + }, + + create: function ( materialName ) { + + if ( this.materials[ materialName ] === undefined ) { + + this.createMaterial_( materialName ); + + } + + return this.materials[ materialName ]; + + }, + + createMaterial_: function ( materialName ) { + + // Create material + + var mat = this.materialsInfo[ materialName ]; + var params = { + + name: materialName, + side: this.side + + }; + + for ( var prop in mat ) { + + var value = mat[ prop ]; + + switch ( prop.toLowerCase() ) { + + // Ns is material specular exponent + + case 'kd': + + // Diffuse color (color under white light) using RGB values + + params[ 'color' ] = new THREE.Color().fromArray( value ); + + break; + + case 'ka': + + // Ambient color (color under shadow) using RGB values + + break; + + case 'ks': + + // Specular color (color when light is reflected from shiny surface) using RGB values + params[ 'specular' ] = new THREE.Color().fromArray( value ); + + break; + + case 'map_kd': + + // Diffuse texture map + + params[ 'map' ] = this.loadTexture( this.baseUrl + value ); + params[ 'map' ].wrapS = this.wrap; + params[ 'map' ].wrapT = this.wrap; + + break; + + case 'ns': + + // The specular exponent (defines the focus of the specular highlight) + // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000. + + params[ 'shininess' ] = parseFloat( value ); + + break; + + case 'd': + + // According to MTL format (http://paulbourke.net/dataformats/mtl/): + // d is dissolve for current material + // factor of 1.0 is fully opaque, a factor of 0 is fully dissolved (completely transparent) + + if ( value < 1 ) { + + params[ 'transparent' ] = true; + params[ 'opacity' ] = value; + + } + + break; + + case 'map_bump': + case 'bump': + + // Bump texture map + + if ( params[ 'bumpMap' ] ) break; // Avoid loading twice. + + params[ 'bumpMap' ] = this.loadTexture( this.baseUrl + value ); + params[ 'bumpMap' ].wrapS = this.wrap; + params[ 'bumpMap' ].wrapT = this.wrap; + + break; + + default: + break; + + } + + } + + this.materials[ materialName ] = new THREE.MeshPhongMaterial( params ); + return this.materials[ materialName ]; + + }, + + + loadTexture: function ( url, mapping, onLoad, onProgress, onError ) { + + var texture; + var loader = THREE.Loader.Handlers.get( url ); + var manager = ( this.manager !== undefined ) ? this.manager : THREE.DefaultLoadingManager; + + if ( loader !== null ) { + + texture = loader.load( url, onLoad ); + + } else { + + texture = new THREE.Texture(); + + loader = new THREE.ImageLoader( manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( image ) { + + texture.image = THREE.MTLLoader.ensurePowerOfTwo_( image ); + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + }, onProgress, onError ); + + } + + if ( mapping !== undefined ) texture.mapping = mapping; + + return texture; + + } + +}; + +THREE.MTLLoader.ensurePowerOfTwo_ = function ( image ) { + + if ( ! THREE.Math.isPowerOfTwo( image.width ) || ! THREE.Math.isPowerOfTwo( image.height ) ) { + + var canvas = document.createElement( "canvas" ); + canvas.width = THREE.MTLLoader.nextHighestPowerOfTwo_( image.width ); + canvas.height = THREE.MTLLoader.nextHighestPowerOfTwo_( image.height ); + + var ctx = canvas.getContext( "2d" ); + ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); + return canvas; + + } + + return image; + +}; + +THREE.MTLLoader.nextHighestPowerOfTwo_ = function( x ) { + + -- x; + + for ( var i = 1; i < 32; i <<= 1 ) { + + x = x | x >> i; + + } + + return x + 1; + +}; + +THREE.EventDispatcher.prototype.apply( THREE.MTLLoader.prototype ); diff --git a/node_modules/three/examples/js/loaders/OBJLoader.js b/node_modules/three/examples/js/loaders/OBJLoader.js new file mode 100644 index 00000000..a26b57da --- /dev/null +++ b/node_modules/three/examples/js/loaders/OBJLoader.js @@ -0,0 +1,382 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.OBJLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.OBJLoader.prototype = { + + constructor: THREE.OBJLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( text ) { + + console.time( 'OBJLoader' ); + + var object, objects = []; + var geometry, material; + + function parseVertexIndex( value ) { + + var index = parseInt( value ); + + return ( index >= 0 ? index - 1 : index + vertices.length / 3 ) * 3; + + } + + function parseNormalIndex( value ) { + + var index = parseInt( value ); + + return ( index >= 0 ? index - 1 : index + normals.length / 3 ) * 3; + + } + + function parseUVIndex( value ) { + + var index = parseInt( value ); + + return ( index >= 0 ? index - 1 : index + uvs.length / 2 ) * 2; + + } + + function addVertex( a, b, c ) { + + geometry.vertices.push( + vertices[ a ], vertices[ a + 1 ], vertices[ a + 2 ], + vertices[ b ], vertices[ b + 1 ], vertices[ b + 2 ], + vertices[ c ], vertices[ c + 1 ], vertices[ c + 2 ] + ); + + } + + function addNormal( a, b, c ) { + + geometry.normals.push( + normals[ a ], normals[ a + 1 ], normals[ a + 2 ], + normals[ b ], normals[ b + 1 ], normals[ b + 2 ], + normals[ c ], normals[ c + 1 ], normals[ c + 2 ] + ); + + } + + function addUV( a, b, c ) { + + geometry.uvs.push( + uvs[ a ], uvs[ a + 1 ], + uvs[ b ], uvs[ b + 1 ], + uvs[ c ], uvs[ c + 1 ] + ); + + } + + function addFace( a, b, c, d, ua, ub, uc, ud, na, nb, nc, nd ) { + + var ia = parseVertexIndex( a ); + var ib = parseVertexIndex( b ); + var ic = parseVertexIndex( c ); + var id; + + if ( d === undefined ) { + + addVertex( ia, ib, ic ); + + } else { + + id = parseVertexIndex( d ); + + addVertex( ia, ib, id ); + addVertex( ib, ic, id ); + + } + + if ( ua !== undefined ) { + + ia = parseUVIndex( ua ); + ib = parseUVIndex( ub ); + ic = parseUVIndex( uc ); + + if ( d === undefined ) { + + addUV( ia, ib, ic ); + + } else { + + id = parseUVIndex( ud ); + + addUV( ia, ib, id ); + addUV( ib, ic, id ); + + } + + } + + if ( na !== undefined ) { + + ia = parseNormalIndex( na ); + ib = parseNormalIndex( nb ); + ic = parseNormalIndex( nc ); + + if ( d === undefined ) { + + addNormal( ia, ib, ic ); + + } else { + + id = parseNormalIndex( nd ); + + addNormal( ia, ib, id ); + addNormal( ib, ic, id ); + + } + + } + + } + + // create mesh if no objects in text + + if ( /^o /gm.test( text ) === false ) { + + geometry = { + vertices: [], + normals: [], + uvs: [] + }; + + material = { + name: '' + }; + + object = { + name: '', + geometry: geometry, + material: material + }; + + objects.push( object ); + + } + + var vertices = []; + var normals = []; + var uvs = []; + + // v float float float + + var vertex_pattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; + + // vn float float float + + var normal_pattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; + + // vt float float + + var uv_pattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; + + // f vertex vertex vertex ... + + var face_pattern1 = /f( +-?\d+)( +-?\d+)( +-?\d+)( +-?\d+)?/; + + // f vertex/uv vertex/uv vertex/uv ... + + var face_pattern2 = /f( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))?/; + + // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... + + var face_pattern3 = /f( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))?/; + + // f vertex//normal vertex//normal vertex//normal ... + + var face_pattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))?/; + + // + + var lines = text.split( '\n' ); + + for ( var i = 0; i < lines.length; i ++ ) { + + var line = lines[ i ]; + line = line.trim(); + + var result; + + if ( line.length === 0 || line.charAt( 0 ) === '#' ) { + + continue; + + } else if ( ( result = vertex_pattern.exec( line ) ) !== null ) { + + // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"] + + vertices.push( + parseFloat( result[ 1 ] ), + parseFloat( result[ 2 ] ), + parseFloat( result[ 3 ] ) + ); + + } else if ( ( result = normal_pattern.exec( line ) ) !== null ) { + + // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"] + + normals.push( + parseFloat( result[ 1 ] ), + parseFloat( result[ 2 ] ), + parseFloat( result[ 3 ] ) + ); + + } else if ( ( result = uv_pattern.exec( line ) ) !== null ) { + + // ["vt 0.1 0.2", "0.1", "0.2"] + + uvs.push( + parseFloat( result[ 1 ] ), + parseFloat( result[ 2 ] ) + ); + + } else if ( ( result = face_pattern1.exec( line ) ) !== null ) { + + // ["f 1 2 3", "1", "2", "3", undefined] + + addFace( + result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ] + ); + + } else if ( ( result = face_pattern2.exec( line ) ) !== null ) { + + // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined] + + addFace( + result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ], + result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] + ); + + } else if ( ( result = face_pattern3.exec( line ) ) !== null ) { + + // ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined] + + addFace( + result[ 2 ], result[ 6 ], result[ 10 ], result[ 14 ], + result[ 3 ], result[ 7 ], result[ 11 ], result[ 15 ], + result[ 4 ], result[ 8 ], result[ 12 ], result[ 16 ] + ); + + } else if ( ( result = face_pattern4.exec( line ) ) !== null ) { + + // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined] + + addFace( + result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ], + undefined, undefined, undefined, undefined, + result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] + ); + + } else if ( /^o /.test( line ) ) { + + geometry = { + vertices: [], + normals: [], + uvs: [] + }; + + material = { + name: '' + }; + + object = { + name: line.substring( 2 ).trim(), + geometry: geometry, + material: material + }; + + objects.push( object ) + + } else if ( /^g /.test( line ) ) { + + // group + + } else if ( /^usemtl /.test( line ) ) { + + // material + + material.name = line.substring( 7 ).trim(); + + } else if ( /^mtllib /.test( line ) ) { + + // mtl file + + } else if ( /^s /.test( line ) ) { + + // smooth shading + + } else { + + // console.log( "THREE.OBJLoader: Unhandled line " + line ); + + } + + } + + var container = new THREE.Object3D(); + + for ( var i = 0, l = objects.length; i < l; i ++ ) { + + object = objects[ i ]; + geometry = object.geometry; + + var buffergeometry = new THREE.BufferGeometry(); + + buffergeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( geometry.vertices ), 3 ) ); + + if ( geometry.normals.length > 0 ) { + + buffergeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( geometry.normals ), 3 ) ); + + } + + if ( geometry.uvs.length > 0 ) { + + buffergeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( geometry.uvs ), 2 ) ); + + } + + material = new THREE.MeshLambertMaterial(); + material.name = object.material.name; + + var mesh = new THREE.Mesh( buffergeometry, material ); + mesh.name = object.name; + + container.add( mesh ); + + } + + console.timeEnd( 'OBJLoader' ); + + return container; + + } + +}; diff --git a/node_modules/three/examples/js/loaders/OBJMTLLoader.js b/node_modules/three/examples/js/loaders/OBJMTLLoader.js new file mode 100644 index 00000000..bcef6f72 --- /dev/null +++ b/node_modules/three/examples/js/loaders/OBJMTLLoader.js @@ -0,0 +1,374 @@ +/** + * Loads a Wavefront .obj file with materials + * + * @author mrdoob / http://mrdoob.com/ + * @author angelxuanchang + */ + +THREE.OBJMTLLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.OBJMTLLoader.prototype = { + + constructor: THREE.OBJMTLLoader, + + load: function ( url, mtlurl, onLoad, onProgress, onError ) { + + var scope = this; + + var mtlLoader = new THREE.MTLLoader( this.manager ); + mtlLoader.setBaseUrl( url.substr( 0, url.lastIndexOf( "/" ) + 1 ) ); + mtlLoader.setCrossOrigin( this.crossOrigin ); + mtlLoader.load( mtlurl, function ( materials ) { + + var materialsCreator = materials; + materialsCreator.preload(); + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( scope.crossOrigin ); + loader.load( url, function ( text ) { + + var object = scope.parse( text ); + + object.traverse( function ( object ) { + + if ( object instanceof THREE.Mesh ) { + + if ( object.material.name ) { + + var material = materialsCreator.create( object.material.name ); + + if ( material ) object.material = material; + + } + + } + + } ); + + onLoad( object ); + + }, onProgress, onError ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + /** + * Parses loaded .obj file + * @param data - content of .obj file + * @param mtllibCallback - callback to handle mtllib declaration (optional) + * @return {THREE.Object3D} - Object3D (with default material) + */ + + parse: function ( data, mtllibCallback ) { + + function vector( x, y, z ) { + + return new THREE.Vector3( x, y, z ); + + } + + function uv( u, v ) { + + return new THREE.Vector2( u, v ); + + } + + function face3( a, b, c, normals ) { + + return new THREE.Face3( a, b, c, normals ); + + } + + var face_offset = 0; + + function meshN( meshName, materialName ) { + + if ( vertices.length > 0 ) { + + geometry.vertices = vertices; + + geometry.mergeVertices(); + geometry.computeFaceNormals(); + geometry.computeBoundingSphere(); + + object.add( mesh ); + + geometry = new THREE.Geometry(); + mesh = new THREE.Mesh( geometry, material ); + + } + + if ( meshName !== undefined ) mesh.name = meshName; + + if ( materialName !== undefined ) { + + material = new THREE.MeshLambertMaterial(); + material.name = materialName; + + mesh.material = material; + + } + + } + + var group = new THREE.Group(); + var object = group; + + var geometry = new THREE.Geometry(); + var material = new THREE.MeshLambertMaterial(); + var mesh = new THREE.Mesh( geometry, material ); + + var vertices = []; + var normals = []; + var uvs = []; + + function add_face( a, b, c, normals_inds ) { + + if ( normals_inds === undefined ) { + + geometry.faces.push( face3( + parseInt( a ) - ( face_offset + 1 ), + parseInt( b ) - ( face_offset + 1 ), + parseInt( c ) - ( face_offset + 1 ) + ) ); + + } else { + + geometry.faces.push( face3( + parseInt( a ) - ( face_offset + 1 ), + parseInt( b ) - ( face_offset + 1 ), + parseInt( c ) - ( face_offset + 1 ), + [ + normals[ parseInt( normals_inds[ 0 ] ) - 1 ].clone(), + normals[ parseInt( normals_inds[ 1 ] ) - 1 ].clone(), + normals[ parseInt( normals_inds[ 2 ] ) - 1 ].clone() + ] + ) ); + + } + + } + + function add_uvs( a, b, c ) { + + geometry.faceVertexUvs[ 0 ].push( [ + uvs[ parseInt( a ) - 1 ].clone(), + uvs[ parseInt( b ) - 1 ].clone(), + uvs[ parseInt( c ) - 1 ].clone() + ] ); + + } + + function handle_face_line( faces, uvs, normals_inds ) { + + if ( faces[ 3 ] === undefined ) { + + add_face( faces[ 0 ], faces[ 1 ], faces[ 2 ], normals_inds ); + + if ( ! ( uvs === undefined ) && uvs.length > 0 ) { + + add_uvs( uvs[ 0 ], uvs[ 1 ], uvs[ 2 ] ); + + } + + } else { + + if ( ! ( normals_inds === undefined ) && normals_inds.length > 0 ) { + + add_face( faces[ 0 ], faces[ 1 ], faces[ 3 ], [ normals_inds[ 0 ], normals_inds[ 1 ], normals_inds[ 3 ] ] ); + add_face( faces[ 1 ], faces[ 2 ], faces[ 3 ], [ normals_inds[ 1 ], normals_inds[ 2 ], normals_inds[ 3 ] ] ); + + } else { + + add_face( faces[ 0 ], faces[ 1 ], faces[ 3 ] ); + add_face( faces[ 1 ], faces[ 2 ], faces[ 3 ] ); + + } + + if ( ! ( uvs === undefined ) && uvs.length > 0 ) { + + add_uvs( uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ); + add_uvs( uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ); + + } + + } + + } + + + // v float float float + + var vertex_pattern = /v( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; + + // vn float float float + + var normal_pattern = /vn( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; + + // vt float float + + var uv_pattern = /vt( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; + + // f vertex vertex vertex ... + + var face_pattern1 = /f( +\d+)( +\d+)( +\d+)( +\d+)?/; + + // f vertex/uv vertex/uv vertex/uv ... + + var face_pattern2 = /f( +(\d+)\/(\d+))( +(\d+)\/(\d+))( +(\d+)\/(\d+))( +(\d+)\/(\d+))?/; + + // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... + + var face_pattern3 = /f( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))?/; + + // f vertex//normal vertex//normal vertex//normal ... + + var face_pattern4 = /f( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))?/; + + // + + var lines = data.split( "\n" ); + + for ( var i = 0; i < lines.length; i ++ ) { + + var line = lines[ i ]; + line = line.trim(); + + var result; + + if ( line.length === 0 || line.charAt( 0 ) === '#' ) { + + continue; + + } else if ( ( result = vertex_pattern.exec( line ) ) !== null ) { + + // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"] + + vertices.push( vector( + parseFloat( result[ 1 ] ), + parseFloat( result[ 2 ] ), + parseFloat( result[ 3 ] ) + ) ); + + } else if ( ( result = normal_pattern.exec( line ) ) !== null ) { + + // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"] + + normals.push( vector( + parseFloat( result[ 1 ] ), + parseFloat( result[ 2 ] ), + parseFloat( result[ 3 ] ) + ) ); + + } else if ( ( result = uv_pattern.exec( line ) ) !== null ) { + + // ["vt 0.1 0.2", "0.1", "0.2"] + + uvs.push( uv( + parseFloat( result[ 1 ] ), + parseFloat( result[ 2 ] ) + ) ); + + } else if ( ( result = face_pattern1.exec( line ) ) !== null ) { + + // ["f 1 2 3", "1", "2", "3", undefined] + + handle_face_line( [ result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ] ] ); + + } else if ( ( result = face_pattern2.exec( line ) ) !== null ) { + + // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined] + + handle_face_line( + [ result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ] ], //faces + [ result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ] //uv + ); + + } else if ( ( result = face_pattern3.exec( line ) ) !== null ) { + + // ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined] + + handle_face_line( + [ result[ 2 ], result[ 6 ], result[ 10 ], result[ 14 ] ], //faces + [ result[ 3 ], result[ 7 ], result[ 11 ], result[ 15 ] ], //uv + [ result[ 4 ], result[ 8 ], result[ 12 ], result[ 16 ] ] //normal + ); + + } else if ( ( result = face_pattern4.exec( line ) ) !== null ) { + + // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined] + + handle_face_line( + [ result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ] ], //faces + [ ], //uv + [ result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ] //normal + ); + + } else if ( /^o /.test( line ) ) { + + // object + + meshN(); + face_offset = face_offset + vertices.length; + vertices = []; + object = new THREE.Object3D(); + object.name = line.substring( 2 ).trim(); + group.add( object ); + + } else if ( /^g /.test( line ) ) { + + // group + + meshN( line.substring( 2 ).trim(), undefined ); + + } else if ( /^usemtl /.test( line ) ) { + + // material + + meshN( undefined, line.substring( 7 ).trim() ); + + } else if ( /^mtllib /.test( line ) ) { + + // mtl file + + if ( mtllibCallback ) { + + var mtlfile = line.substring( 7 ); + mtlfile = mtlfile.trim(); + mtllibCallback( mtlfile ); + + } + + } else if ( /^s /.test( line ) ) { + + // Smooth shading + + } else { + + console.log( "THREE.OBJMTLLoader: Unhandled line " + line ); + + } + + } + + //Add last object + meshN( undefined, undefined ); + + return group; + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.OBJMTLLoader.prototype ); diff --git a/node_modules/three/examples/js/loaders/PDBLoader.js b/node_modules/three/examples/js/loaders/PDBLoader.js new file mode 100644 index 00000000..7e00277e --- /dev/null +++ b/node_modules/three/examples/js/loaders/PDBLoader.js @@ -0,0 +1,186 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.PDBLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.PDBLoader.prototype = { + + constructor: THREE.PDBLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + var json = scope.parsePDB( text ); + scope.createModel( json, onLoad ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + // Based on CanvasMol PDB parser + + parsePDB: function ( text ) { + + function trim( text ) { + + return text.replace( /^\s\s*/, '' ).replace( /\s\s*$/, '' ); + + } + + function capitalize( text ) { + + return text.charAt( 0 ).toUpperCase() + text.substr( 1 ).toLowerCase(); + + } + + function hash( s, e ) { + + return "s" + Math.min( s, e ) + "e" + Math.max( s, e ); + + } + + function parseBond( start, length ) { + + var eatom = parseInt( lines[ i ].substr( start, length ) ); + + if ( eatom ) { + + var h = hash( satom, eatom ); + + if ( bhash[ h ] == undefined ) { + + bonds.push( [ satom - 1, eatom - 1, 1 ] ); + bhash[ h ] = bonds.length - 1; + + } else { + + // doesn't really work as almost all PDBs + // have just normal bonds appearing multiple + // times instead of being double/triple bonds + // bonds[bhash[h]][2] += 1; + + } + + } + + } + + var CPK = { "h": [ 255, 255, 255 ], "he": [ 217, 255, 255 ], "li": [ 204, 128, 255 ], "be": [ 194, 255, 0 ], "b": [ 255, 181, 181 ], "c": [ 144, 144, 144 ], "n": [ 48, 80, 248 ], "o": [ 255, 13, 13 ], "f": [ 144, 224, 80 ], "ne": [ 179, 227, 245 ], "na": [ 171, 92, 242 ], "mg": [ 138, 255, 0 ], "al": [ 191, 166, 166 ], "si": [ 240, 200, 160 ], "p": [ 255, 128, 0 ], "s": [ 255, 255, 48 ], "cl": [ 31, 240, 31 ], "ar": [ 128, 209, 227 ], "k": [ 143, 64, 212 ], "ca": [ 61, 255, 0 ], "sc": [ 230, 230, 230 ], "ti": [ 191, 194, 199 ], "v": [ 166, 166, 171 ], "cr": [ 138, 153, 199 ], "mn": [ 156, 122, 199 ], "fe": [ 224, 102, 51 ], "co": [ 240, 144, 160 ], "ni": [ 80, 208, 80 ], "cu": [ 200, 128, 51 ], "zn": [ 125, 128, 176 ], "ga": [ 194, 143, 143 ], "ge": [ 102, 143, 143 ], "as": [ 189, 128, 227 ], "se": [ 255, 161, 0 ], "br": [ 166, 41, 41 ], "kr": [ 92, 184, 209 ], "rb": [ 112, 46, 176 ], "sr": [ 0, 255, 0 ], "y": [ 148, 255, 255 ], "zr": [ 148, 224, 224 ], "nb": [ 115, 194, 201 ], "mo": [ 84, 181, 181 ], "tc": [ 59, 158, 158 ], "ru": [ 36, 143, 143 ], "rh": [ 10, 125, 140 ], "pd": [ 0, 105, 133 ], "ag": [ 192, 192, 192 ], "cd": [ 255, 217, 143 ], "in": [ 166, 117, 115 ], "sn": [ 102, 128, 128 ], "sb": [ 158, 99, 181 ], "te": [ 212, 122, 0 ], "i": [ 148, 0, 148 ], "xe": [ 66, 158, 176 ], "cs": [ 87, 23, 143 ], "ba": [ 0, 201, 0 ], "la": [ 112, 212, 255 ], "ce": [ 255, 255, 199 ], "pr": [ 217, 255, 199 ], "nd": [ 199, 255, 199 ], "pm": [ 163, 255, 199 ], "sm": [ 143, 255, 199 ], "eu": [ 97, 255, 199 ], "gd": [ 69, 255, 199 ], "tb": [ 48, 255, 199 ], "dy": [ 31, 255, 199 ], "ho": [ 0, 255, 156 ], "er": [ 0, 230, 117 ], "tm": [ 0, 212, 82 ], "yb": [ 0, 191, 56 ], "lu": [ 0, 171, 36 ], "hf": [ 77, 194, 255 ], "ta": [ 77, 166, 255 ], "w": [ 33, 148, 214 ], "re": [ 38, 125, 171 ], "os": [ 38, 102, 150 ], "ir": [ 23, 84, 135 ], "pt": [ 208, 208, 224 ], "au": [ 255, 209, 35 ], "hg": [ 184, 184, 208 ], "tl": [ 166, 84, 77 ], "pb": [ 87, 89, 97 ], "bi": [ 158, 79, 181 ], "po": [ 171, 92, 0 ], "at": [ 117, 79, 69 ], "rn": [ 66, 130, 150 ], "fr": [ 66, 0, 102 ], "ra": [ 0, 125, 0 ], "ac": [ 112, 171, 250 ], "th": [ 0, 186, 255 ], "pa": [ 0, 161, 255 ], "u": [ 0, 143, 255 ], "np": [ 0, 128, 255 ], "pu": [ 0, 107, 255 ], "am": [ 84, 92, 242 ], "cm": [ 120, 92, 227 ], "bk": [ 138, 79, 227 ], "cf": [ 161, 54, 212 ], "es": [ 179, 31, 212 ], "fm": [ 179, 31, 186 ], "md": [ 179, 13, 166 ], "no": [ 189, 13, 135 ], "lr": [ 199, 0, 102 ], "rf": [ 204, 0, 89 ], "db": [ 209, 0, 79 ], "sg": [ 217, 0, 69 ], "bh": [ 224, 0, 56 ], "hs": [ 230, 0, 46 ], "mt": [ 235, 0, 38 ], + "ds": [ 235, 0, 38 ], "rg": [ 235, 0, 38 ], "cn": [ 235, 0, 38 ], "uut": [ 235, 0, 38 ], "uuq": [ 235, 0, 38 ], "uup": [ 235, 0, 38 ], "uuh": [ 235, 0, 38 ], "uus": [ 235, 0, 38 ], "uuo": [ 235, 0, 38 ] }; + + + var atoms = []; + var bonds = []; + var histogram = {}; + + var bhash = {}; + + var lines = text.split( "\n" ); + + var x, y, z, e; + + for ( var i = 0, il = lines.length; i < il; ++ i ) { + + if ( lines[ i ].substr( 0, 4 ) == "ATOM" || lines[ i ].substr( 0, 6 ) == "HETATM" ) { + + x = parseFloat( lines[ i ].substr( 30, 7 ) ); + y = parseFloat( lines[ i ].substr( 38, 7 ) ); + z = parseFloat( lines[ i ].substr( 46, 7 ) ); + + e = trim( lines[ i ].substr( 76, 2 ) ).toLowerCase(); + + if ( e == "" ) e = trim( lines[ i ].substr( 12, 2 ) ).toLowerCase(); + atoms.push( [ x, y, z, CPK[ e ], capitalize( e ) ] ); + + if ( histogram[ e ] == undefined ) histogram[ e ] = 1; + else histogram[ e ] += 1; + + } else if ( lines[ i ].substr( 0, 6 ) == "CONECT" ) { + + var satom = parseInt( lines[ i ].substr( 6, 5 ) ); + + parseBond( 11, 5 ); + parseBond( 16, 5 ); + parseBond( 21, 5 ); + parseBond( 26, 5 ); + + } + + } + + return { "ok": true, "atoms": atoms, "bonds": bonds, "histogram": histogram }; + + }, + + createModel: function ( json, callback ) { + + var scope = this, + geometryAtoms = new THREE.Geometry(), + geometryBonds = new THREE.Geometry(); + + geometryAtoms.elements = []; + + var atoms = json.atoms; + var bonds = json.bonds; + + for ( var i = 0; i < atoms.length; i ++ ) { + + var atom = atoms[ i ]; + + var x = atom[ 0 ]; + var y = atom[ 1 ]; + var z = atom[ 2 ]; + + var position = new THREE.Vector3( x, y, z ); + geometryAtoms.vertices.push( position ); + + var r = atom[ 3 ][ 0 ] / 255; + var g = atom[ 3 ][ 1 ] / 255; + var b = atom[ 3 ][ 2 ] / 255; + + var color = new THREE.Color(); + color.setRGB( r, g, b ); + + geometryAtoms.colors.push( color ); + + geometryAtoms.elements.push( atom[ 4 ] ); + + } + + for ( var i = 0; i < bonds.length; i ++ ) { + + var bond = bonds[ i ]; + + var start = bond[ 0 ]; + var end = bond[ 1 ]; + + var vertex1 = geometryAtoms.vertices[ start ]; + var vertex2 = geometryAtoms.vertices[ end ]; + + geometryBonds.vertices.push( vertex1.clone() ); + geometryBonds.vertices.push( vertex2.clone() ); + + } + + callback( geometryAtoms, geometryBonds, json ); + + } + +}; + diff --git a/node_modules/three/examples/js/loaders/PLYLoader.js b/node_modules/three/examples/js/loaders/PLYLoader.js new file mode 100644 index 00000000..42e2b9d3 --- /dev/null +++ b/node_modules/three/examples/js/loaders/PLYLoader.js @@ -0,0 +1,484 @@ +/** + * @author Wei Meng / http://about.me/menway + * + * Description: A THREE loader for PLY ASCII files (known as the Polygon File Format or the Stanford Triangle Format). + * + * + * Limitations: ASCII decoding assumes file is UTF-8. + * + * Usage: + * var loader = new THREE.PLYLoader(); + * loader.load('./models/ply/ascii/dolphins.ply', function (geometry) { + * + * scene.add( new THREE.Mesh( geometry ) ); + * + * } ); + * + * If the PLY file uses non standard property names, they can be mapped while + * loading. For example, the following maps the properties + * “diffuse_(red|green|blue)” in the file to standard color names. + * + * loader.setPropertyNameMapping( { + * diffuse_red: 'red', + * diffuse_green: 'green', + * diffuse_blue: 'blue' + * } ); + * + */ + + +THREE.PLYLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + this.propertyNameMapping = {}; + +}; + +THREE.PLYLoader.prototype = { + + constructor: THREE.PLYLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setPropertyNameMapping: function ( mapping ) { + + this.propertyNameMapping = mapping; + + }, + + bin2str: function ( buf ) { + + var array_buffer = new Uint8Array( buf ); + var str = ''; + for ( var i = 0; i < buf.byteLength; i ++ ) { + + str += String.fromCharCode( array_buffer[ i ] ); // implicitly assumes little-endian + + } + + return str; + + }, + + isASCII: function( data ) { + + var header = this.parseHeader( this.bin2str( data ) ); + + return header.format === "ascii"; + + }, + + parse: function ( data ) { + + if ( data instanceof ArrayBuffer ) { + + return this.isASCII( data ) + ? this.parseASCII( this.bin2str( data ) ) + : this.parseBinary( data ); + + } else { + + return this.parseASCII( data ); + + } + + }, + + parseHeader: function ( data ) { + + var patternHeader = /ply([\s\S]*)end_header\s/; + var headerText = ""; + var headerLength = 0; + var result = patternHeader.exec( data ); + if ( result !== null ) { + + headerText = result [ 1 ]; + headerLength = result[ 0 ].length; + + } + + var header = { + comments: [], + elements: [], + headerLength: headerLength + }; + + var lines = headerText.split( '\n' ); + var currentElement = undefined; + var lineType, lineValues; + + function make_ply_element_property( propertValues, propertyNameMapping ) { + + var property = { + type: propertValues[ 0 ] + }; + + if ( property.type === 'list' ) { + + property.name = propertValues[ 3 ]; + property.countType = propertValues[ 1 ]; + property.itemType = propertValues[ 2 ]; + + } else { + + property.name = propertValues[ 1 ]; + + } + + if ( property.name in propertyNameMapping ) { + + property.name = propertyNameMapping[ property.name ]; + + } + + return property; + + } + + for ( var i = 0; i < lines.length; i ++ ) { + + var line = lines[ i ]; + line = line.trim(); + if ( line === "" ) { + + continue; + + } + lineValues = line.split( /\s+/ ); + lineType = lineValues.shift(); + line = lineValues.join( " " ); + + switch ( lineType ) { + + case "format": + + header.format = lineValues[ 0 ]; + header.version = lineValues[ 1 ]; + + break; + + case "comment": + + header.comments.push( line ); + + break; + + case "element": + + if ( ! ( currentElement === undefined ) ) { + + header.elements.push( currentElement ); + + } + + currentElement = Object(); + currentElement.name = lineValues[ 0 ]; + currentElement.count = parseInt( lineValues[ 1 ] ); + currentElement.properties = []; + + break; + + case "property": + + currentElement.properties.push( make_ply_element_property( lineValues, this.propertyNameMapping ) ); + + break; + + + default: + + console.log( "unhandled", lineType, lineValues ); + + } + + } + + if ( ! ( currentElement === undefined ) ) { + + header.elements.push( currentElement ); + + } + + return header; + + }, + + parseASCIINumber: function ( n, type ) { + + switch ( type ) { + + case 'char': case 'uchar': case 'short': case 'ushort': case 'int': case 'uint': + case 'int8': case 'uint8': case 'int16': case 'uint16': case 'int32': case 'uint32': + + return parseInt( n ); + + case 'float': case 'double': case 'float32': case 'float64': + + return parseFloat( n ); + + } + + }, + + parseASCIIElement: function ( properties, line ) { + + var values = line.split( /\s+/ ); + + var element = Object(); + + for ( var i = 0; i < properties.length; i ++ ) { + + if ( properties[ i ].type === "list" ) { + + var list = []; + var n = this.parseASCIINumber( values.shift(), properties[ i ].countType ); + + for ( var j = 0; j < n; j ++ ) { + + list.push( this.parseASCIINumber( values.shift(), properties[ i ].itemType ) ); + + } + + element[ properties[ i ].name ] = list; + + } else { + + element[ properties[ i ].name ] = this.parseASCIINumber( values.shift(), properties[ i ].type ); + + } + + } + + return element; + + }, + + parseASCII: function ( data ) { + + // PLY ascii format specification, as per http://en.wikipedia.org/wiki/PLY_(file_format) + + var geometry = new THREE.Geometry(); + + var result; + + var header = this.parseHeader( data ); + + var patternBody = /end_header\s([\s\S]*)$/; + var body = ""; + if ( ( result = patternBody.exec( data ) ) !== null ) { + + body = result [ 1 ]; + + } + + var lines = body.split( '\n' ); + var currentElement = 0; + var currentElementCount = 0; + geometry.useColor = false; + + for ( var i = 0; i < lines.length; i ++ ) { + + var line = lines[ i ]; + line = line.trim(); + if ( line === "" ) { + + continue; + + } + + if ( currentElementCount >= header.elements[ currentElement ].count ) { + + currentElement ++; + currentElementCount = 0; + + } + + var element = this.parseASCIIElement( header.elements[ currentElement ].properties, line ); + + this.handleElement( geometry, header.elements[ currentElement ].name, element ); + + currentElementCount ++; + + } + + return this.postProcess( geometry ); + + }, + + postProcess: function ( geometry ) { + + if ( geometry.useColor ) { + + for ( var i = 0; i < geometry.faces.length; i ++ ) { + + geometry.faces[ i ].vertexColors = [ + geometry.colors[ geometry.faces[ i ].a ], + geometry.colors[ geometry.faces[ i ].b ], + geometry.colors[ geometry.faces[ i ].c ] + ]; + + } + + geometry.elementsNeedUpdate = true; + + } + + geometry.computeBoundingSphere(); + + return geometry; + + }, + + handleElement: function ( geometry, elementName, element ) { + + if ( elementName === "vertex" ) { + + geometry.vertices.push( + new THREE.Vector3( element.x, element.y, element.z ) + ); + + if ( 'red' in element && 'green' in element && 'blue' in element ) { + + geometry.useColor = true; + + var color = new THREE.Color(); + color.setRGB( element.red / 255.0, element.green / 255.0, element.blue / 255.0 ); + geometry.colors.push( color ); + + } + + } else if ( elementName === "face" ) { + + var vertex_indices = element.vertex_indices; + + if ( vertex_indices.length === 3 ) { + + geometry.faces.push( + new THREE.Face3( vertex_indices[ 0 ], vertex_indices[ 1 ], vertex_indices[ 2 ] ) + ); + + } else if ( vertex_indices.length === 4 ) { + + geometry.faces.push( + new THREE.Face3( vertex_indices[ 0 ], vertex_indices[ 1 ], vertex_indices[ 3 ] ), + new THREE.Face3( vertex_indices[ 1 ], vertex_indices[ 2 ], vertex_indices[ 3 ] ) + ); + + } + + } + + }, + + binaryRead: function ( dataview, at, type, little_endian ) { + + switch ( type ) { + + // corespondences for non-specific length types here match rply: + case 'int8': case 'char': return [ dataview.getInt8( at ), 1 ]; + + case 'uint8': case 'uchar': return [ dataview.getUint8( at ), 1 ]; + + case 'int16': case 'short': return [ dataview.getInt16( at, little_endian ), 2 ]; + + case 'uint16': case 'ushort': return [ dataview.getUint16( at, little_endian ), 2 ]; + + case 'int32': case 'int': return [ dataview.getInt32( at, little_endian ), 4 ]; + + case 'uint32': case 'uint': return [ dataview.getUint32( at, little_endian ), 4 ]; + + case 'float32': case 'float': return [ dataview.getFloat32( at, little_endian ), 4 ]; + + case 'float64': case 'double': return [ dataview.getFloat64( at, little_endian ), 8 ]; + + } + + }, + + binaryReadElement: function ( dataview, at, properties, little_endian ) { + + var element = Object(); + var result, read = 0; + + for ( var i = 0; i < properties.length; i ++ ) { + + if ( properties[ i ].type === "list" ) { + + var list = []; + + result = this.binaryRead( dataview, at + read, properties[ i ].countType, little_endian ); + var n = result[ 0 ]; + read += result[ 1 ]; + + for ( var j = 0; j < n; j ++ ) { + + result = this.binaryRead( dataview, at + read, properties[ i ].itemType, little_endian ); + list.push( result[ 0 ] ); + read += result[ 1 ]; + + } + + element[ properties[ i ].name ] = list; + + } else { + + result = this.binaryRead( dataview, at + read, properties[ i ].type, little_endian ); + element[ properties[ i ].name ] = result[ 0 ]; + read += result[ 1 ]; + + } + + } + + return [ element, read ]; + + }, + + parseBinary: function ( data ) { + + var geometry = new THREE.Geometry(); + + var header = this.parseHeader( this.bin2str( data ) ); + var little_endian = ( header.format === "binary_little_endian" ); + var body = new DataView( data, header.headerLength ); + var result, loc = 0; + + for ( var currentElement = 0; currentElement < header.elements.length; currentElement ++ ) { + + for ( var currentElementCount = 0; currentElementCount < header.elements[ currentElement ].count; currentElementCount ++ ) { + + result = this.binaryReadElement( body, loc, header.elements[ currentElement ].properties, little_endian ); + loc += result[ 1 ]; + var element = result[ 0 ]; + + this.handleElement( geometry, header.elements[ currentElement ].name, element ); + + } + + } + + return this.postProcess( geometry ); + + } + +}; diff --git a/node_modules/three/examples/js/loaders/PVRLoader.js b/node_modules/three/examples/js/loaders/PVRLoader.js new file mode 100644 index 00000000..00acdd6b --- /dev/null +++ b/node_modules/three/examples/js/loaders/PVRLoader.js @@ -0,0 +1,269 @@ +/* + * PVRLoader + * Author: pierre lepers + * Date: 17/09/2014 11:09 + * + * PVR v2 (legacy) parser + * TODO : Add Support for PVR v3 format + * TODO : implement loadMipmaps option + */ + +THREE.PVRLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + this._parser = THREE.PVRLoader.parse; + +}; + +THREE.PVRLoader.prototype = Object.create( THREE.CompressedTextureLoader.prototype ); +THREE.PVRLoader.prototype.constructor = THREE.PVRLoader; + + +THREE.PVRLoader.parse = function ( buffer, loadMipmaps ) { + + var headerLengthInt = 13; + var header = new Uint32Array( buffer, 0, headerLengthInt ); + + var pvrDatas = { + buffer: buffer, + header : header, + loadMipmaps : loadMipmaps + }; + + // PVR v3 + if ( header[ 0 ] === 0x03525650 ) { + + return THREE.PVRLoader._parseV3( pvrDatas ); + + } + // PVR v2 + else if ( header[ 11 ] === 0x21525650 ) { + + return THREE.PVRLoader._parseV2( pvrDatas ); + + } else { + + throw new Error( "[THREE.PVRLoader] Unknown PVR format" ); + + } + +}; + +THREE.PVRLoader._parseV3 = function ( pvrDatas ) { + + var header = pvrDatas.header; + var bpp, format; + + + var metaLen = header[ 12 ], + pixelFormat = header[ 2 ], + height = header[ 6 ], + width = header[ 7 ], + numSurfs = header[ 9 ], + numFaces = header[ 10 ], + numMipmaps = header[ 11 ]; + + switch ( pixelFormat ) { + case 0 : // PVRTC 2bpp RGB + bpp = 2; + format = THREE.RGB_PVRTC_2BPPV1_Format; + break; + case 1 : // PVRTC 2bpp RGBA + bpp = 2; + format = THREE.RGBA_PVRTC_2BPPV1_Format; + break; + case 2 : // PVRTC 4bpp RGB + bpp = 4; + format = THREE.RGB_PVRTC_4BPPV1_Format; + break; + case 3 : // PVRTC 4bpp RGBA + bpp = 4; + format = THREE.RGBA_PVRTC_4BPPV1_Format; + break; + default : + throw new Error( "pvrtc - unsupported PVR format " + pixelFormat ); + } + + pvrDatas.dataPtr = 52 + metaLen; + pvrDatas.bpp = bpp; + pvrDatas.format = format; + pvrDatas.width = width; + pvrDatas.height = height; + pvrDatas.numSurfaces = numFaces; + pvrDatas.numMipmaps = numMipmaps; + + pvrDatas.isCubemap = ( numFaces === 6 ); + + return THREE.PVRLoader._extract( pvrDatas ); + +}; + +THREE.PVRLoader._parseV2 = function ( pvrDatas ) { + + var header = pvrDatas.header; + + var headerLength = header[ 0 ], + height = header[ 1 ], + width = header[ 2 ], + numMipmaps = header[ 3 ], + flags = header[ 4 ], + dataLength = header[ 5 ], + bpp = header[ 6 ], + bitmaskRed = header[ 7 ], + bitmaskGreen = header[ 8 ], + bitmaskBlue = header[ 9 ], + bitmaskAlpha = header[ 10 ], + pvrTag = header[ 11 ], + numSurfs = header[ 12 ]; + + + var TYPE_MASK = 0xff; + var PVRTC_2 = 24, + PVRTC_4 = 25; + + var formatFlags = flags & TYPE_MASK; + + + + var bpp, format; + var _hasAlpha = bitmaskAlpha > 0; + + if ( formatFlags === PVRTC_4 ) { + + format = _hasAlpha ? THREE.RGBA_PVRTC_4BPPV1_Format : THREE.RGB_PVRTC_4BPPV1_Format; + bpp = 4; + + } else if ( formatFlags === PVRTC_2 ) { + + format = _hasAlpha ? THREE.RGBA_PVRTC_2BPPV1_Format : THREE.RGB_PVRTC_2BPPV1_Format; + bpp = 2; + + } else + throw new Error( "pvrtc - unknown format " + formatFlags ); + + + + pvrDatas.dataPtr = headerLength; + pvrDatas.bpp = bpp; + pvrDatas.format = format; + pvrDatas.width = width; + pvrDatas.height = height; + pvrDatas.numSurfaces = numSurfs; + pvrDatas.numMipmaps = numMipmaps + 1; + + // guess cubemap type seems tricky in v2 + // it juste a pvr containing 6 surface (no explicit cubemap type) + pvrDatas.isCubemap = ( numSurfs === 6 ); + + return THREE.PVRLoader._extract( pvrDatas ); + +}; + + +THREE.PVRLoader._extract = function ( pvrDatas ) { + + var pvr = { + mipmaps: [], + width: pvrDatas.width, + height: pvrDatas.height, + format: pvrDatas.format, + mipmapCount: pvrDatas.numMipmaps, + isCubemap : pvrDatas.isCubemap + }; + + var buffer = pvrDatas.buffer; + + + + // console.log( "--------------------------" ); + + // console.log( "headerLength ", headerLength); + // console.log( "height ", height ); + // console.log( "width ", width ); + // console.log( "numMipmaps ", numMipmaps ); + // console.log( "flags ", flags ); + // console.log( "dataLength ", dataLength ); + // console.log( "bpp ", bpp ); + // console.log( "bitmaskRed ", bitmaskRed ); + // console.log( "bitmaskGreen ", bitmaskGreen); + // console.log( "bitmaskBlue ", bitmaskBlue ); + // console.log( "bitmaskAlpha ", bitmaskAlpha); + // console.log( "pvrTag ", pvrTag ); + // console.log( "numSurfs ", numSurfs ); + + + + + var dataOffset = pvrDatas.dataPtr, + bpp = pvrDatas.bpp, + numSurfs = pvrDatas.numSurfaces, + dataSize = 0, + blockSize = 0, + blockWidth = 0, + blockHeight = 0, + widthBlocks = 0, + heightBlocks = 0; + + + + if ( bpp === 2 ) { + + blockWidth = 8; + blockHeight = 4; + + } else { + + blockWidth = 4; + blockHeight = 4; + + } + + blockSize = ( blockWidth * blockHeight ) * bpp / 8; + + pvr.mipmaps.length = pvrDatas.numMipmaps * numSurfs; + + var mipLevel = 0; + + while ( mipLevel < pvrDatas.numMipmaps ) { + + var sWidth = pvrDatas.width >> mipLevel, + sHeight = pvrDatas.height >> mipLevel; + + widthBlocks = sWidth / blockWidth; + heightBlocks = sHeight / blockHeight; + + // Clamp to minimum number of blocks + if ( widthBlocks < 2 ) + widthBlocks = 2; + if ( heightBlocks < 2 ) + heightBlocks = 2; + + dataSize = widthBlocks * heightBlocks * blockSize; + + + for ( var surfIndex = 0; surfIndex < numSurfs; surfIndex ++ ) { + + var byteArray = new Uint8Array( buffer, dataOffset, dataSize ); + + var mipmap = { + data: byteArray, + width: sWidth, + height: sHeight + }; + + pvr.mipmaps[ surfIndex * pvrDatas.numMipmaps + mipLevel ] = mipmap; + + dataOffset += dataSize; + + + } + + mipLevel ++; + + } + + + return pvr; + +}; diff --git a/node_modules/three/examples/js/loaders/RGBELoader.js b/node_modules/three/examples/js/loaders/RGBELoader.js new file mode 100644 index 00000000..f0ee1d9e --- /dev/null +++ b/node_modules/three/examples/js/loaders/RGBELoader.js @@ -0,0 +1,349 @@ +/** + * @author Nikos M. / https://github.com/foo123/ + */ + +// https://github.com/mrdoob/three.js/issues/5552 +// http://en.wikipedia.org/wiki/RGBE_image_format + +THREE.HDRLoader = THREE.RGBELoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +// extend THREE.BinaryTextureLoader +THREE.RGBELoader.prototype = Object.create( THREE.BinaryTextureLoader.prototype ); + +// adapted from http://www.graphics.cornell.edu/~bjw/rgbe.html +THREE.RGBELoader.prototype._parser = function( buffer ) { + + var + /* return codes for rgbe routines */ + RGBE_RETURN_SUCCESS = 0, + RGBE_RETURN_FAILURE = - 1, + + /* default error routine. change this to change error handling */ + rgbe_read_error = 1, + rgbe_write_error = 2, + rgbe_format_error = 3, + rgbe_memory_error = 4, + rgbe_error = function( rgbe_error_code, msg ) { + + switch ( rgbe_error_code ) { + case rgbe_read_error: console.error( "THREE.RGBELoader Read Error: " + ( msg || '' ) ); + break; + case rgbe_write_error: console.error( "THREE.RGBELoader Write Error: " + ( msg || '' ) ); + break; + case rgbe_format_error: console.error( "THREE.RGBELoader Bad File Format: " + ( msg || '' ) ); + break; + default: + case rgbe_memory_error: console.error( "THREE.RGBELoader: Error: " + ( msg || '' ) ); + } + return RGBE_RETURN_FAILURE; + + }, + + /* offsets to red, green, and blue components in a data (float) pixel */ + RGBE_DATA_RED = 0, + RGBE_DATA_GREEN = 1, + RGBE_DATA_BLUE = 2, + + /* number of floats per pixel, use 4 since stored in rgba image format */ + RGBE_DATA_SIZE = 4, + + /* flags indicating which fields in an rgbe_header_info are valid */ + RGBE_VALID_PROGRAMTYPE = 1, + RGBE_VALID_FORMAT = 2, + RGBE_VALID_DIMENSIONS = 4, + + NEWLINE = "\n", + + fgets = function( buffer, lineLimit, consume ) { + + lineLimit = ! lineLimit ? 1024 : lineLimit; + var p = buffer.pos, + i = - 1, len = 0, s = '', chunkSize = 128, + chunk = String.fromCharCode.apply( null, new Uint16Array( buffer.subarray( p, p + chunkSize ) ) ) + ; + while ( ( 0 > ( i = chunk.indexOf( NEWLINE ) ) ) && ( len < lineLimit ) && ( p < buffer.byteLength ) ) { + + s += chunk; len += chunk.length; + p += chunkSize; + chunk += String.fromCharCode.apply( null, new Uint16Array( buffer.subarray( p, p + chunkSize ) ) ); + + } + + if ( - 1 < i ) { + + /*for (i=l-1; i>=0; i--) { + byteCode = m.charCodeAt(i); + if (byteCode > 0x7f && byteCode <= 0x7ff) byteLen++; + else if (byteCode > 0x7ff && byteCode <= 0xffff) byteLen += 2; + if (byteCode >= 0xDC00 && byteCode <= 0xDFFF) i--; //trail surrogate + }*/ + if ( false !== consume ) buffer.pos += len + i + 1; + return s + chunk.slice( 0, i ); + + } + return false; + + }, + + /* minimal header reading. modify if you want to parse more information */ + RGBE_ReadHeader = function( buffer ) { + + var line, match, + + // regexes to parse header info fields + magic_token_re = /^#\?(\S+)$/, + gamma_re = /^\s*GAMMA\s*=\s*(\d+(\.\d+)?)\s*$/, + exposure_re = /^\s*EXPOSURE\s*=\s*(\d+(\.\d+)?)\s*$/, + format_re = /^\s*FORMAT=(\S+)\s*$/, + dimensions_re = /^\s*\-Y\s+(\d+)\s+\+X\s+(\d+)\s*$/, + + // RGBE format header struct + header = { + + valid: 0, /* indicate which fields are valid */ + + string: '', /* the actual header string */ + + comments: '', /* comments found in header */ + + programtype: 'RGBE', /* listed at beginning of file to identify it + * after "#?". defaults to "RGBE" */ + + format: '', /* RGBE format, default 32-bit_rle_rgbe */ + + gamma: 1.0, /* image has already been gamma corrected with + * given gamma. defaults to 1.0 (no correction) */ + + exposure: 1.0, /* a value of 1.0 in an image corresponds to + * watts/steradian/m^2. + * defaults to 1.0 */ + + width: 0, height: 0 /* image dimensions, width/height */ + + } + ; + + if ( buffer.pos >= buffer.byteLength || ! ( line = fgets( buffer ) ) ) { + + return rgbe_error( rgbe_read_error, "no header found" ); + + } + /* if you want to require the magic token then uncomment the next line */ + if ( ! ( match = line.match( magic_token_re ) ) ) { + + return rgbe_error( rgbe_format_error, "bad initial token" ); + + } + header.valid |= RGBE_VALID_PROGRAMTYPE; + header.programtype = match[ 1 ]; + header.string += line + "\n"; + + while ( true ) { + + line = fgets( buffer ); + if ( false === line ) break; + header.string += line + "\n"; + + if ( '#' === line.charAt( 0 ) ) { + + header.comments += line + "\n"; + continue; // comment line + + } + + if ( match = line.match( gamma_re ) ) { + + header.gamma = parseFloat( match[ 1 ], 10 ); + + } + if ( match = line.match( exposure_re ) ) { + + header.exposure = parseFloat( match[ 1 ], 10 ); + + } + if ( match = line.match( format_re ) ) { + + header.valid |= RGBE_VALID_FORMAT; + header.format = match[ 1 ];//'32-bit_rle_rgbe'; + + } + if ( match = line.match( dimensions_re ) ) { + + header.valid |= RGBE_VALID_DIMENSIONS; + header.height = parseInt( match[ 1 ], 10 ); + header.width = parseInt( match[ 2 ], 10 ); + + } + + if ( ( header.valid & RGBE_VALID_FORMAT ) && ( header.valid & RGBE_VALID_DIMENSIONS ) ) break; + + } + + if ( ! ( header.valid & RGBE_VALID_FORMAT ) ) { + + return rgbe_error( rgbe_format_error, "missing format specifier" ); + + } + if ( ! ( header.valid & RGBE_VALID_DIMENSIONS ) ) { + + return rgbe_error( rgbe_format_error, "missing image size specifier" ); + + } + + return header; + + }, + + RGBE_ReadPixels_RLE = function( buffer, w, h ) { + + var data_rgba, offset, pos, count, byteValue, + scanline_buffer, ptr, ptr_end, i, l, off, isEncodedRun, + scanline_width = w, num_scanlines = h, rgbeStart + ; + + if ( + // run length encoding is not allowed so read flat + ( ( scanline_width < 8 ) || ( scanline_width > 0x7fff ) ) || + // this file is not run length encoded + ( ( 2 !== buffer[ 0 ] ) || ( 2 !== buffer[ 1 ] ) || ( buffer[ 2 ] & 0x80 ) ) + ) { + + // return the flat buffer + return new Uint8Array( buffer ); + + } + + if ( scanline_width !== ( ( buffer[ 2 ] << 8 ) | buffer[ 3 ] ) ) { + + return rgbe_error( rgbe_format_error, "wrong scanline width" ); + + } + + data_rgba = new Uint8Array( 4 * w * h ); + + if ( ! data_rgba || ! data_rgba.length ) { + + return rgbe_error( rgbe_memory_error, "unable to allocate buffer space" ); + + } + + offset = 0; pos = 0; ptr_end = 4 * scanline_width; + rgbeStart = new Uint8Array( 4 ); + scanline_buffer = new Uint8Array( ptr_end ); + + // read in each successive scanline + while ( ( num_scanlines > 0 ) && ( pos < buffer.byteLength ) ) { + + if ( pos + 4 > buffer.byteLength ) { + + return rgbe_error( rgbe_read_error ); + + } + + rgbeStart[ 0 ] = buffer[ pos ++ ]; + rgbeStart[ 1 ] = buffer[ pos ++ ]; + rgbeStart[ 2 ] = buffer[ pos ++ ]; + rgbeStart[ 3 ] = buffer[ pos ++ ]; + + if ( ( 2 != rgbeStart[ 0 ] ) || ( 2 != rgbeStart[ 1 ] ) || ( ( ( rgbeStart[ 2 ] << 8 ) | rgbeStart[ 3 ] ) != scanline_width ) ) { + + return rgbe_error( rgbe_format_error, "bad rgbe scanline format" ); + + } + + // read each of the four channels for the scanline into the buffer + // first red, then green, then blue, then exponent + ptr = 0; + while ( ( ptr < ptr_end ) && ( pos < buffer.byteLength ) ) { + + count = buffer[ pos ++ ]; + isEncodedRun = count > 128; + if ( isEncodedRun ) count -= 128; + + if ( ( 0 === count ) || ( ptr + count > ptr_end ) ) { + + return rgbe_error( rgbe_format_error, "bad scanline data" ); + + } + + if ( isEncodedRun ) { + + // a (encoded) run of the same value + byteValue = buffer[ pos ++ ]; + for ( i = 0; i < count; i ++ ) { + + scanline_buffer[ ptr ++ ] = byteValue; + + } + //ptr += count; + + } else { + + // a literal-run + scanline_buffer.set( buffer.subarray( pos, pos + count ), ptr ); + ptr += count; pos += count; + + } + + } + + + // now convert data from buffer into rgba + // first red, then green, then blue, then exponent (alpha) + l = scanline_width; //scanline_buffer.byteLength; + for ( i = 0; i < l; i ++ ) { + + off = 0; + data_rgba[ offset ] = scanline_buffer[ i + off ]; + off += scanline_width; //1; + data_rgba[ offset + 1 ] = scanline_buffer[ i + off ]; + off += scanline_width; //1; + data_rgba[ offset + 2 ] = scanline_buffer[ i + off ]; + off += scanline_width; //1; + data_rgba[ offset + 3 ] = scanline_buffer[ i + off ]; + offset += 4; + + } + + num_scanlines --; + + } + + return data_rgba; + + } + ; + + var byteArray = new Uint8Array( buffer ), + byteLength = byteArray.byteLength; + byteArray.pos = 0; + var rgbe_header_info = RGBE_ReadHeader( byteArray ); + + if ( RGBE_RETURN_FAILURE !== rgbe_header_info ) { + + var w = rgbe_header_info.width, + h = rgbe_header_info.height + , image_rgba_data = RGBE_ReadPixels_RLE( byteArray.subarray( byteArray.pos ), w, h ) + ; + if ( RGBE_RETURN_FAILURE !== image_rgba_data ) { + + return { + width: w, height: h, + data: image_rgba_data, + header: rgbe_header_info.string, + gamma: rgbe_header_info.gamma, + exposure: rgbe_header_info.exposure, + format: THREE.RGBEFormat, // handled as THREE.RGBAFormat in shaders + type: THREE.UnsignedByteType + }; + + } + + } + return null; + +}; diff --git a/node_modules/three/examples/js/loaders/STLLoader.js b/node_modules/three/examples/js/loaders/STLLoader.js new file mode 100644 index 00000000..2c3c3b11 --- /dev/null +++ b/node_modules/three/examples/js/loaders/STLLoader.js @@ -0,0 +1,498 @@ +/** + * @author aleeper / http://adamleeper.com/ + * @author mrdoob / http://mrdoob.com/ + * @author gero3 / https://github.com/gero3 + * + * Description: A THREE loader for STL ASCII files, as created by Solidworks and other CAD programs. + * + * Supports both binary and ASCII encoded files, with automatic detection of type. + * + * Limitations: + * Binary decoding supports "Magics" color format (http://en.wikipedia.org/wiki/STL_(file_format)#Color_in_binary_STL). + * There is perhaps some question as to how valid it is to always assume little-endian-ness. + * ASCII decoding assumes file is UTF-8. Seems to work for the examples... + * + * Usage: + * var loader = new THREE.STLLoader(); + * loader.load( './models/stl/slotted_disk.stl', function ( geometry ) { + * scene.add( new THREE.Mesh( geometry ) ); + * }); + * + * For binary STLs geometry might contain colors for vertices. To use it: + * // use the same code to load STL as above + * if (geometry.hasColors) { + * material = new THREE.MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: THREE.VertexColors }); + * } else { .... } + * var mesh = new THREE.Mesh( geometry, material ); + */ + + +THREE.STLLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.STLLoader.prototype = { + + constructor: THREE.STLLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( data ) { + + var isBinary = function () { + + var expect, face_size, n_faces, reader; + reader = new DataView( binData ); + face_size = ( 32 / 8 * 3 ) + ( ( 32 / 8 * 3 ) * 3 ) + ( 16 / 8 ); + n_faces = reader.getUint32( 80, true ); + expect = 80 + ( 32 / 8 ) + ( n_faces * face_size ); + + if ( expect === reader.byteLength ) { + + return true; + + } + + // some binary files will have different size from expected, + // checking characters higher than ASCII to confirm is binary + var fileLength = reader.byteLength; + for ( var index = 0; index < fileLength; index ++ ) { + + if ( reader.getUint8( index, false ) > 127 ) { + + return true; + + } + + } + + return false; + + }; + + var binData = this.ensureBinary( data ); + + return isBinary() + ? this.parseBinary( binData ) + : this.parseASCII( this.ensureString( data ) ); + + }, + + parseBinary: function ( data ) { + + var reader = new DataView( data ); + var faces = reader.getUint32( 80, true ); + + var r, g, b, hasColors = false, colors; + var defaultR, defaultG, defaultB, alpha; + + // process STL header + // check for default color in header ("COLOR=rgba" sequence). + + for ( var index = 0; index < 80 - 10; index ++ ) { + + if ( ( reader.getUint32( index, false ) == 0x434F4C4F /*COLO*/ ) && + ( reader.getUint8( index + 4 ) == 0x52 /*'R'*/ ) && + ( reader.getUint8( index + 5 ) == 0x3D /*'='*/ ) ) { + + hasColors = true; + colors = new Float32Array( faces * 3 * 3 ); + + defaultR = reader.getUint8( index + 6 ) / 255; + defaultG = reader.getUint8( index + 7 ) / 255; + defaultB = reader.getUint8( index + 8 ) / 255; + alpha = reader.getUint8( index + 9 ) / 255; + + } + + } + + var dataOffset = 84; + var faceLength = 12 * 4 + 2; + + var offset = 0; + + var geometry = new THREE.BufferGeometry(); + + var vertices = new Float32Array( faces * 3 * 3 ); + var normals = new Float32Array( faces * 3 * 3 ); + + for ( var face = 0; face < faces; face ++ ) { + + var start = dataOffset + face * faceLength; + var normalX = reader.getFloat32( start, true ); + var normalY = reader.getFloat32( start + 4, true ); + var normalZ = reader.getFloat32( start + 8, true ); + + if ( hasColors ) { + + var packedColor = reader.getUint16( start + 48, true ); + + if ( ( packedColor & 0x8000 ) === 0 ) { + + // facet has its own unique color + + r = ( packedColor & 0x1F ) / 31; + g = ( ( packedColor >> 5 ) & 0x1F ) / 31; + b = ( ( packedColor >> 10 ) & 0x1F ) / 31; + + } else { + + r = defaultR; + g = defaultG; + b = defaultB; + + } + + } + + for ( var i = 1; i <= 3; i ++ ) { + + var vertexstart = start + i * 12; + + vertices[ offset ] = reader.getFloat32( vertexstart, true ); + vertices[ offset + 1 ] = reader.getFloat32( vertexstart + 4, true ); + vertices[ offset + 2 ] = reader.getFloat32( vertexstart + 8, true ); + + normals[ offset ] = normalX; + normals[ offset + 1 ] = normalY; + normals[ offset + 2 ] = normalZ; + + if ( hasColors ) { + + colors[ offset ] = r; + colors[ offset + 1 ] = g; + colors[ offset + 2 ] = b; + + } + + offset += 3; + + } + + } + + geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + geometry.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + + if ( hasColors ) { + + geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); + geometry.hasColors = true; + geometry.alpha = alpha; + + } + + return geometry; + + }, + + parseASCII: function ( data ) { + + var geometry, length, normal, patternFace, patternNormal, patternVertex, result, text; + geometry = new THREE.Geometry(); + patternFace = /facet([\s\S]*?)endfacet/g; + + while ( ( result = patternFace.exec( data ) ) !== null ) { + + text = result[ 0 ]; + patternNormal = /normal[\s]+([\-+]?[0-9]+\.?[0-9]*([eE][\-+]?[0-9]+)?)+[\s]+([\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?)+[\s]+([\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?)+/g; + + while ( ( result = patternNormal.exec( text ) ) !== null ) { + + normal = new THREE.Vector3( parseFloat( result[ 1 ] ), parseFloat( result[ 3 ] ), parseFloat( result[ 5 ] ) ); + + } + + patternVertex = /vertex[\s]+([\-+]?[0-9]+\.?[0-9]*([eE][\-+]?[0-9]+)?)+[\s]+([\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?)+[\s]+([\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?)+/g; + + while ( ( result = patternVertex.exec( text ) ) !== null ) { + + geometry.vertices.push( new THREE.Vector3( parseFloat( result[ 1 ] ), parseFloat( result[ 3 ] ), parseFloat( result[ 5 ] ) ) ); + + } + + length = geometry.vertices.length; + + geometry.faces.push( new THREE.Face3( length - 3, length - 2, length - 1, normal ) ); + + } + + geometry.computeBoundingBox(); + geometry.computeBoundingSphere(); + + return geometry; + + }, + + ensureString: function ( buf ) { + + if ( typeof buf !== "string" ) { + + var array_buffer = new Uint8Array( buf ); + var str = ''; + for ( var i = 0; i < buf.byteLength; i ++ ) { + + str += String.fromCharCode( array_buffer[ i ] ); // implicitly assumes little-endian + + } + return str; + + } else { + + return buf; + + } + + }, + + ensureBinary: function ( buf ) { + + if ( typeof buf === "string" ) { + + var array_buffer = new Uint8Array( buf.length ); + for ( var i = 0; i < buf.length; i ++ ) { + + array_buffer[ i ] = buf.charCodeAt( i ) & 0xff; // implicitly assumes little-endian + + } + return array_buffer.buffer || array_buffer; + + } else { + + return buf; + + } + + } + +}; + +if ( typeof DataView === 'undefined' ) { + + DataView = function( buffer, byteOffset, byteLength ) { + + this.buffer = buffer; + this.byteOffset = byteOffset || 0; + this.byteLength = byteLength || buffer.byteLength || buffer.length; + this._isString = typeof buffer === "string"; + + }; + + DataView.prototype = { + + _getCharCodes: function( buffer, start, length ) { + + start = start || 0; + length = length || buffer.length; + var end = start + length; + var codes = []; + for ( var i = start; i < end; i ++ ) { + + codes.push( buffer.charCodeAt( i ) & 0xff ); + + } + return codes; + + }, + + _getBytes: function ( length, byteOffset, littleEndian ) { + + var result; + + // Handle the lack of endianness + if ( littleEndian === undefined ) { + + littleEndian = this._littleEndian; + + } + + // Handle the lack of byteOffset + if ( byteOffset === undefined ) { + + byteOffset = this.byteOffset; + + } else { + + byteOffset = this.byteOffset + byteOffset; + + } + + if ( length === undefined ) { + + length = this.byteLength - byteOffset; + + } + + // Error Checking + if ( typeof byteOffset !== 'number' ) { + + throw new TypeError( 'DataView byteOffset is not a number' ); + + } + + if ( length < 0 || byteOffset + length > this.byteLength ) { + + throw new Error( 'DataView length or (byteOffset+length) value is out of bounds' ); + + } + + if ( this.isString ) { + + result = this._getCharCodes( this.buffer, byteOffset, byteOffset + length ); + + } else { + + result = this.buffer.slice( byteOffset, byteOffset + length ); + + } + + if ( ! littleEndian && length > 1 ) { + + if ( Array.isArray( result ) === false ) { + + result = Array.prototype.slice.call( result ); + + } + + result.reverse(); + + } + + return result; + + }, + + // Compatibility functions on a String Buffer + + getFloat64: function ( byteOffset, littleEndian ) { + + var b = this._getBytes( 8, byteOffset, littleEndian ), + + sign = 1 - ( 2 * ( b[ 7 ] >> 7 ) ), + exponent = ( ( ( ( b[ 7 ] << 1 ) & 0xff ) << 3 ) | ( b[ 6 ] >> 4 ) ) - ( ( 1 << 10 ) - 1 ), + + // Binary operators such as | and << operate on 32 bit values, using + and Math.pow(2) instead + mantissa = ( ( b[ 6 ] & 0x0f ) * Math.pow( 2, 48 ) ) + ( b[ 5 ] * Math.pow( 2, 40 ) ) + ( b[ 4 ] * Math.pow( 2, 32 ) ) + + ( b[ 3 ] * Math.pow( 2, 24 ) ) + ( b[ 2 ] * Math.pow( 2, 16 ) ) + ( b[ 1 ] * Math.pow( 2, 8 ) ) + b[ 0 ]; + + if ( exponent === 1024 ) { + + if ( mantissa !== 0 ) { + + return NaN; + + } else { + + return sign * Infinity; + + } + + } + + if ( exponent === - 1023 ) { + + // Denormalized + return sign * mantissa * Math.pow( 2, - 1022 - 52 ); + + } + + return sign * ( 1 + mantissa * Math.pow( 2, - 52 ) ) * Math.pow( 2, exponent ); + + }, + + getFloat32: function ( byteOffset, littleEndian ) { + + var b = this._getBytes( 4, byteOffset, littleEndian ), + + sign = 1 - ( 2 * ( b[ 3 ] >> 7 ) ), + exponent = ( ( ( b[ 3 ] << 1 ) & 0xff ) | ( b[ 2 ] >> 7 ) ) - 127, + mantissa = ( ( b[ 2 ] & 0x7f ) << 16 ) | ( b[ 1 ] << 8 ) | b[ 0 ]; + + if ( exponent === 128 ) { + + if ( mantissa !== 0 ) { + + return NaN; + + } else { + + return sign * Infinity; + + } + + } + + if ( exponent === - 127 ) { + + // Denormalized + return sign * mantissa * Math.pow( 2, - 126 - 23 ); + + } + + return sign * ( 1 + mantissa * Math.pow( 2, - 23 ) ) * Math.pow( 2, exponent ); + + }, + + getInt32: function ( byteOffset, littleEndian ) { + + var b = this._getBytes( 4, byteOffset, littleEndian ); + return ( b[ 3 ] << 24 ) | ( b[ 2 ] << 16 ) | ( b[ 1 ] << 8 ) | b[ 0 ]; + + }, + + getUint32: function ( byteOffset, littleEndian ) { + + return this.getInt32( byteOffset, littleEndian ) >>> 0; + + }, + + getInt16: function ( byteOffset, littleEndian ) { + + return ( this.getUint16( byteOffset, littleEndian ) << 16 ) >> 16; + + }, + + getUint16: function ( byteOffset, littleEndian ) { + + var b = this._getBytes( 2, byteOffset, littleEndian ); + return ( b[ 1 ] << 8 ) | b[ 0 ]; + + }, + + getInt8: function ( byteOffset ) { + + return ( this.getUint8( byteOffset ) << 24 ) >> 24; + + }, + + getUint8: function ( byteOffset ) { + + return this._getBytes( 1, byteOffset )[ 0 ]; + + } + + }; + +} diff --git a/node_modules/three/examples/js/loaders/SVGLoader.js b/node_modules/three/examples/js/loaders/SVGLoader.js new file mode 100644 index 00000000..1a6532cc --- /dev/null +++ b/node_modules/three/examples/js/loaders/SVGLoader.js @@ -0,0 +1,40 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author zz85 / http://joshuakoo.com/ + */ + +THREE.SVGLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.SVGLoader.prototype = { + + constructor: THREE.SVGLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var parser = new DOMParser(); + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( svgString ) { + + var doc = parser.parseFromString( svgString, 'image/svg+xml' ); // application/xml + + onLoad( doc.documentElement ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; diff --git a/node_modules/three/examples/js/loaders/TGALoader.js b/node_modules/three/examples/js/loaders/TGALoader.js new file mode 100644 index 00000000..d6c15a8b --- /dev/null +++ b/node_modules/three/examples/js/loaders/TGALoader.js @@ -0,0 +1,483 @@ +/* + * @author Daosheng Mu / https://github.com/DaoshengMu/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.TGALoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +// extend THREE.BinaryTextureLoader +THREE.TGALoader.prototype = Object.create( THREE.BinaryTextureLoader.prototype ); + +// reference from vthibault, https://github.com/vthibault/roBrowser/blob/master/src/Loaders/Targa.js +THREE.TGALoader.prototype._parser = function ( buffer ) { + + // TGA Constants + var TGA_TYPE_NO_DATA = 0, + TGA_TYPE_INDEXED = 1, + TGA_TYPE_RGB = 2, + TGA_TYPE_GREY = 3, + TGA_TYPE_RLE_INDEXED = 9, + TGA_TYPE_RLE_RGB = 10, + TGA_TYPE_RLE_GREY = 11, + + TGA_ORIGIN_MASK = 0x30, + TGA_ORIGIN_SHIFT = 0x04, + TGA_ORIGIN_BL = 0x00, + TGA_ORIGIN_BR = 0x01, + TGA_ORIGIN_UL = 0x02, + TGA_ORIGIN_UR = 0x03; + + + if ( buffer.length < 19 ) + console.error( 'THREE.TGALoader.parse: Not enough data to contain header.' ); + + var content = new Uint8Array( buffer ), + offset = 0, + header = { + id_length: content[ offset ++ ], + colormap_type: content[ offset ++ ], + image_type: content[ offset ++ ], + colormap_index: content[ offset ++ ] | content[ offset ++ ] << 8, + colormap_length: content[ offset ++ ] | content[ offset ++ ] << 8, + colormap_size: content[ offset ++ ], + + origin: [ + content[ offset ++ ] | content[ offset ++ ] << 8, + content[ offset ++ ] | content[ offset ++ ] << 8 + ], + width: content[ offset ++ ] | content[ offset ++ ] << 8, + height: content[ offset ++ ] | content[ offset ++ ] << 8, + pixel_size: content[ offset ++ ], + flags: content[ offset ++ ] + }; + + function tgaCheckHeader( header ) { + + switch ( header.image_type ) { + + // Check indexed type + case TGA_TYPE_INDEXED: + case TGA_TYPE_RLE_INDEXED: + if ( header.colormap_length > 256 || header.colormap_size !== 24 || header.colormap_type !== 1 ) { + + console.error( 'THREE.TGALoader.parse.tgaCheckHeader: Invalid type colormap data for indexed type' ); + + } + break; + + // Check colormap type + case TGA_TYPE_RGB: + case TGA_TYPE_GREY: + case TGA_TYPE_RLE_RGB: + case TGA_TYPE_RLE_GREY: + if ( header.colormap_type ) { + + console.error( 'THREE.TGALoader.parse.tgaCheckHeader: Invalid type colormap data for colormap type' ); + + } + break; + + // What the need of a file without data ? + case TGA_TYPE_NO_DATA: + console.error( 'THREE.TGALoader.parse.tgaCheckHeader: No data' ); + + // Invalid type ? + default: + console.error( 'THREE.TGALoader.parse.tgaCheckHeader: Invalid type " ' + header.image_type + '"' ); + + } + + // Check image width and height + if ( header.width <= 0 || header.height <= 0 ) { + + console.error( 'THREE.TGALoader.parse.tgaCheckHeader: Invalid image size' ); + + } + + // Check image pixel size + if ( header.pixel_size !== 8 && + header.pixel_size !== 16 && + header.pixel_size !== 24 && + header.pixel_size !== 32 ) { + + console.error( 'THREE.TGALoader.parse.tgaCheckHeader: Invalid pixel size "' + header.pixel_size + '"' ); + + } + + } + + // Check tga if it is valid format + tgaCheckHeader( header ); + + if ( header.id_length + offset > buffer.length ) { + + console.error( 'THREE.TGALoader.parse: No data' ); + + } + + // Skip the needn't data + offset += header.id_length; + + // Get targa information about RLE compression and palette + var use_rle = false, + use_pal = false, + use_grey = false; + + switch ( header.image_type ) { + + case TGA_TYPE_RLE_INDEXED: + use_rle = true; + use_pal = true; + break; + + case TGA_TYPE_INDEXED: + use_pal = true; + break; + + case TGA_TYPE_RLE_RGB: + use_rle = true; + break; + + case TGA_TYPE_RGB: + break; + + case TGA_TYPE_RLE_GREY: + use_rle = true; + use_grey = true; + break; + + case TGA_TYPE_GREY: + use_grey = true; + break; + + } + + // Parse tga image buffer + function tgaParse( use_rle, use_pal, header, offset, data ) { + + var pixel_data, + pixel_size, + pixel_total, + palettes; + + pixel_size = header.pixel_size >> 3; + pixel_total = header.width * header.height * pixel_size; + + // Read palettes + if ( use_pal ) { + + palettes = data.subarray( offset, offset += header.colormap_length * ( header.colormap_size >> 3 ) ); + + } + + // Read RLE + if ( use_rle ) { + + pixel_data = new Uint8Array( pixel_total ); + + var c, count, i; + var shift = 0; + var pixels = new Uint8Array( pixel_size ); + + while ( shift < pixel_total ) { + + c = data[ offset ++ ]; + count = ( c & 0x7f ) + 1; + + // RLE pixels. + if ( c & 0x80 ) { + + // Bind pixel tmp array + for ( i = 0; i < pixel_size; ++ i ) { + + pixels[ i ] = data[ offset ++ ]; + + } + + // Copy pixel array + for ( i = 0; i < count; ++ i ) { + + pixel_data.set( pixels, shift + i * pixel_size ); + + } + + shift += pixel_size * count; + + } else { + + // Raw pixels. + count *= pixel_size; + for ( i = 0; i < count; ++ i ) { + + pixel_data[ shift + i ] = data[ offset ++ ]; + + } + shift += count; + + } + + } + + } else { + + // RAW Pixels + pixel_data = data.subarray( + offset, offset += ( use_pal ? header.width * header.height : pixel_total ) + ); + + } + + return { + pixel_data: pixel_data, + palettes: palettes + }; + + } + + function tgaGetImageData8bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image, palettes ) { + + var colormap = palettes; + var color, i = 0, x, y; + var width = header.width; + + for ( y = y_start; y !== y_end; y += y_step ) { + + for ( x = x_start; x !== x_end; x += x_step, i ++ ) { + + color = image[ i ]; + imageData[ ( x + width * y ) * 4 + 3 ] = 255; + imageData[ ( x + width * y ) * 4 + 2 ] = colormap[ ( color * 3 ) + 0 ]; + imageData[ ( x + width * y ) * 4 + 1 ] = colormap[ ( color * 3 ) + 1 ]; + imageData[ ( x + width * y ) * 4 + 0 ] = colormap[ ( color * 3 ) + 2 ]; + + } + + } + + return imageData; + + } + + function tgaGetImageData16bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) { + + var color, i = 0, x, y; + var width = header.width; + + for ( y = y_start; y !== y_end; y += y_step ) { + + for ( x = x_start; x !== x_end; x += x_step, i += 2 ) { + + color = image[ i + 0 ] + ( image[ i + 1 ] << 8 ); // Inversed ? + imageData[ ( x + width * y ) * 4 + 0 ] = ( color & 0x7C00 ) >> 7; + imageData[ ( x + width * y ) * 4 + 1 ] = ( color & 0x03E0 ) >> 2; + imageData[ ( x + width * y ) * 4 + 2 ] = ( color & 0x001F ) >> 3; + imageData[ ( x + width * y ) * 4 + 3 ] = ( color & 0x8000 ) ? 0 : 255; + + } + + } + + return imageData; + + } + + function tgaGetImageData24bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) { + + var i = 0, x, y; + var width = header.width; + + for ( y = y_start; y !== y_end; y += y_step ) { + + for ( x = x_start; x !== x_end; x += x_step, i += 3 ) { + + imageData[ ( x + width * y ) * 4 + 3 ] = 255; + imageData[ ( x + width * y ) * 4 + 2 ] = image[ i + 0 ]; + imageData[ ( x + width * y ) * 4 + 1 ] = image[ i + 1 ]; + imageData[ ( x + width * y ) * 4 + 0 ] = image[ i + 2 ]; + + } + + } + + return imageData; + + } + + function tgaGetImageData32bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) { + + var i = 0, x, y; + var width = header.width; + + for ( y = y_start; y !== y_end; y += y_step ) { + + for ( x = x_start; x !== x_end; x += x_step, i += 4 ) { + + imageData[ ( x + width * y ) * 4 + 2 ] = image[ i + 0 ]; + imageData[ ( x + width * y ) * 4 + 1 ] = image[ i + 1 ]; + imageData[ ( x + width * y ) * 4 + 0 ] = image[ i + 2 ]; + imageData[ ( x + width * y ) * 4 + 3 ] = image[ i + 3 ]; + + } + + } + + return imageData; + + } + + function tgaGetImageDataGrey8bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) { + + var color, i = 0, x, y; + var width = header.width; + + for ( y = y_start; y !== y_end; y += y_step ) { + + for ( x = x_start; x !== x_end; x += x_step, i ++ ) { + + color = image[ i ]; + imageData[ ( x + width * y ) * 4 + 0 ] = color; + imageData[ ( x + width * y ) * 4 + 1 ] = color; + imageData[ ( x + width * y ) * 4 + 2 ] = color; + imageData[ ( x + width * y ) * 4 + 3 ] = 255; + + } + + } + + return imageData; + + } + + function tgaGetImageDataGrey16bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) { + + var i = 0, x, y; + var width = header.width; + + for ( y = y_start; y !== y_end; y += y_step ) { + + for ( x = x_start; x !== x_end; x += x_step, i += 2 ) { + + imageData[ ( x + width * y ) * 4 + 0 ] = image[ i + 0 ]; + imageData[ ( x + width * y ) * 4 + 1 ] = image[ i + 0 ]; + imageData[ ( x + width * y ) * 4 + 2 ] = image[ i + 0 ]; + imageData[ ( x + width * y ) * 4 + 3 ] = image[ i + 1 ]; + + } + + } + + return imageData; + + } + + function getTgaRGBA( width, height, image, palette ) { + + var x_start, + y_start, + x_step, + y_step, + x_end, + y_end, + data = new Uint8Array( width * height * 4 ); + + switch ( ( header.flags & TGA_ORIGIN_MASK ) >> TGA_ORIGIN_SHIFT ) { + default: + case TGA_ORIGIN_UL: + x_start = 0; + x_step = 1; + x_end = width; + y_start = 0; + y_step = 1; + y_end = height; + break; + + case TGA_ORIGIN_BL: + x_start = 0; + x_step = 1; + x_end = width; + y_start = height - 1; + y_step = - 1; + y_end = - 1; + break; + + case TGA_ORIGIN_UR: + x_start = width - 1; + x_step = - 1; + x_end = - 1; + y_start = 0; + y_step = 1; + y_end = height; + break; + + case TGA_ORIGIN_BR: + x_start = width - 1; + x_step = - 1; + x_end = - 1; + y_start = height - 1; + y_step = - 1; + y_end = - 1; + break; + + } + + if ( use_grey ) { + + switch ( header.pixel_size ) { + case 8: + tgaGetImageDataGrey8bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image ); + break; + case 16: + tgaGetImageDataGrey16bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image ); + break; + default: + console.error( 'THREE.TGALoader.parse.getTgaRGBA: not support this format' ); + break; + } + + } else { + + switch ( header.pixel_size ) { + case 8: + tgaGetImageData8bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image, palette ); + break; + + case 16: + tgaGetImageData16bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image ); + break; + + case 24: + tgaGetImageData24bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image ); + break; + + case 32: + tgaGetImageData32bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image ); + break; + + default: + console.error( 'THREE.TGALoader.parse.getTgaRGBA: not support this format' ); + break; + } + + } + + // Load image data according to specific method + // var func = 'tgaGetImageData' + (use_grey ? 'Grey' : '') + (header.pixel_size) + 'bits'; + // func(data, y_start, y_step, y_end, x_start, x_step, x_end, width, image, palette ); + return data; + + } + + var result = tgaParse( use_rle, use_pal, header, offset, content ); + var rgbaData = getTgaRGBA( header.width, header.height, result.pixel_data, result.palettes ); + + return { + width: header.width, + height: header.height, + data: rgbaData + }; + +}; diff --git a/node_modules/three/examples/js/loaders/UTF8Loader.js b/node_modules/three/examples/js/loaders/UTF8Loader.js new file mode 100644 index 00000000..8d1a0bb4 --- /dev/null +++ b/node_modules/three/examples/js/loaders/UTF8Loader.js @@ -0,0 +1,779 @@ +/** + * Loader for UTF8 version2 (after r51) encoded models generated by: + * http://code.google.com/p/webgl-loader/ + * + * Code to load/decompress mesh is taken from r100 of this webgl-loader + */ + +THREE.UTF8Loader = function () {}; + +/** + * Load UTF8 encoded model + * @param jsonUrl - URL from which to load json containing information about model + * @param callback - Callback(THREE.Object3D) on successful loading of model + * @param options - options on how to load model (see THREE.MTLLoader.MaterialCreator for basic options) + * Additional options include + * geometryBase: Base url from which to load referenced geometries + * materialBase: Base url from which to load referenced textures + */ + +THREE.UTF8Loader.prototype.load = function ( jsonUrl, callback, options ) { + + this.downloadModelJson( jsonUrl, callback, options ); + +}; + +// BufferGeometryCreator + +THREE.UTF8Loader.BufferGeometryCreator = function () { +}; + +THREE.UTF8Loader.BufferGeometryCreator.prototype.create = function ( attribArray, indices ) { + + var ntris = indices.length / 3; + + var geometry = new THREE.BufferGeometry(); + + var positions = new Float32Array( ntris * 3 * 3 ); + var normals = new Float32Array( ntris * 3 * 3 ); + var uvs = new Float32Array( ntris * 3 * 2 ); + + var i, j, offset; + var x, y, z; + var u, v; + + var end = attribArray.length; + var stride = 8; + + // extract positions + + j = 0; + offset = 0; + + for ( i = offset; i < end; i += stride ) { + + x = attribArray[ i ]; + y = attribArray[ i + 1 ]; + z = attribArray[ i + 2 ]; + + positions[ j ++ ] = x; + positions[ j ++ ] = y; + positions[ j ++ ] = z; + + } + + // extract uvs + + j = 0; + offset = 3; + + for ( i = offset; i < end; i += stride ) { + + u = attribArray[ i ]; + v = attribArray[ i + 1 ]; + + uvs[ j ++ ] = u; + uvs[ j ++ ] = v; + + } + + // extract normals + + j = 0; + offset = 5; + + for ( i = offset; i < end; i += stride ) { + + x = attribArray[ i ]; + y = attribArray[ i + 1 ]; + z = attribArray[ i + 2 ]; + + normals[ j ++ ] = x; + normals[ j ++ ] = y; + normals[ j ++ ] = z; + + } + + geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) ); + geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + geometry.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + + geometry.computeBoundingSphere(); + + return geometry; + +}; + + +// UTF-8 decoder from webgl-loader (r100) +// http://code.google.com/p/webgl-loader/ + +// Model manifest description. Contains objects like: +// name: { +// materials: { 'material_name': { ... } ... }, +// decodeParams: { +// decodeOffsets: [ ... ], +// decodeScales: [ ... ], +// }, +// urls: { +// 'url': [ +// { material: 'material_name', +// attribRange: [#, #], +// indexRange: [#, #], +// names: [ 'object names' ... ], +// lengths: [#, #, # ... ] +// } +// ], +// ... +// } +// } + +var DEFAULT_DECODE_PARAMS = { + + decodeOffsets: [ -4095, -4095, -4095, 0, 0, -511, -511, -511 ], + decodeScales: [ 1 / 8191, 1 / 8191, 1 / 8191, 1 / 1023, 1 / 1023, 1 / 1023, 1 / 1023, 1 / 1023 ] + + // TODO: normal decoding? (see walt.js) + // needs to know: input, output (from vertex format!) + // + // Should split attrib/index. + // 1) Decode position and non-normal attributes. + // 2) Decode indices, computing normals + // 3) Maybe normalize normals? Only necessary for refinement, or fixed? + // 4) Maybe refine normals? Should this be part of regular refinement? + // 5) Morphing + +}; + +// Triangle strips! + +// TODO: will it be an optimization to specialize this method at +// runtime for different combinations of stride, decodeOffset and +// decodeScale? + +THREE.UTF8Loader.prototype.decompressAttribsInner_ = function ( str, inputStart, inputEnd, + output, outputStart, stride, + decodeOffset, decodeScale ) { + + var prev = 0; + + for ( var j = inputStart; j < inputEnd; j ++ ) { + + var code = str.charCodeAt( j ); + prev += ( code >> 1 ) ^ ( -( code & 1 ) ); + + output[ outputStart ] = decodeScale * ( prev + decodeOffset ); + outputStart += stride; + + } + +}; + +THREE.UTF8Loader.prototype.decompressIndices_ = function( str, inputStart, numIndices, + output, outputStart ) { + + var highest = 0; + + for ( var i = 0; i < numIndices; i ++ ) { + + var code = str.charCodeAt( inputStart ++ ); + + output[ outputStart ++ ] = highest - code; + + if ( code === 0 ) { + + highest ++; + + } + + } + +}; + +THREE.UTF8Loader.prototype.decompressAABBs_ = function ( str, inputStart, numBBoxen, + decodeOffsets, decodeScales ) { + var numFloats = 6 * numBBoxen; + + var inputEnd = inputStart + numFloats; + var outputStart = 0; + + var bboxen = new Float32Array( numFloats ); + + for ( var i = inputStart; i < inputEnd; i += 6 ) { + + var minX = str.charCodeAt(i + 0) + decodeOffsets[0]; + var minY = str.charCodeAt(i + 1) + decodeOffsets[1]; + var minZ = str.charCodeAt(i + 2) + decodeOffsets[2]; + + var radiusX = (str.charCodeAt(i + 3) + 1) >> 1; + var radiusY = (str.charCodeAt(i + 4) + 1) >> 1; + var radiusZ = (str.charCodeAt(i + 5) + 1) >> 1; + + bboxen[ outputStart ++ ] = decodeScales[0] * (minX + radiusX); + bboxen[ outputStart ++ ] = decodeScales[1] * (minY + radiusY); + bboxen[ outputStart ++ ] = decodeScales[2] * (minZ + radiusZ); + + bboxen[ outputStart ++ ] = decodeScales[0] * radiusX; + bboxen[ outputStart ++ ] = decodeScales[1] * radiusY; + bboxen[ outputStart ++ ] = decodeScales[2] * radiusZ; + + } + + return bboxen; + +}; + +THREE.UTF8Loader.prototype.decompressMesh = function ( str, meshParams, decodeParams, name, idx, callback ) { + + // Extract conversion parameters from attribArrays. + + var stride = decodeParams.decodeScales.length; + + var decodeOffsets = decodeParams.decodeOffsets; + var decodeScales = decodeParams.decodeScales; + + var attribStart = meshParams.attribRange[0]; + var numVerts = meshParams.attribRange[1]; + + // Decode attributes. + + var inputOffset = attribStart; + var attribsOut = new Float32Array( stride * numVerts ); + + for (var j = 0; j < stride; j ++ ) { + + var end = inputOffset + numVerts; + + var decodeScale = decodeScales[j]; + + if ( decodeScale ) { + + // Assume if decodeScale is never set, simply ignore the + // attribute. + + this.decompressAttribsInner_( str, inputOffset, end, + attribsOut, j, stride, + decodeOffsets[j], decodeScale ); + } + + inputOffset = end; + + } + + var indexStart = meshParams.indexRange[ 0 ]; + var numIndices = 3 * meshParams.indexRange[ 1 ]; + + var indicesOut = new Uint16Array( numIndices ); + + this.decompressIndices_( str, inputOffset, numIndices, indicesOut, 0 ); + + // Decode bboxen. + + var bboxen = undefined; + var bboxOffset = meshParams.bboxes; + + if ( bboxOffset ) { + + bboxen = this.decompressAABBs_( str, bboxOffset, meshParams.names.length, decodeOffsets, decodeScales ); + } + + callback( name, idx, attribsOut, indicesOut, bboxen, meshParams ); + +}; + +THREE.UTF8Loader.prototype.copyAttrib = function ( stride, attribsOutFixed, lastAttrib, index ) { + + for ( var j = 0; j < stride; j ++ ) { + + lastAttrib[ j ] = attribsOutFixed[ stride * index + j ]; + + } + +}; + +THREE.UTF8Loader.prototype.decodeAttrib2 = function ( str, stride, decodeOffsets, decodeScales, deltaStart, + numVerts, attribsOut, attribsOutFixed, lastAttrib, + index ) { + + for ( var j = 0; j < 5; j ++ ) { + + var code = str.charCodeAt( deltaStart + numVerts * j + index ); + var delta = ( code >> 1) ^ (-(code & 1)); + + lastAttrib[ j ] += delta; + attribsOutFixed[ stride * index + j ] = lastAttrib[ j ]; + attribsOut[ stride * index + j ] = decodeScales[ j ] * ( lastAttrib[ j ] + decodeOffsets[ j ] ); + } + +}; + +THREE.UTF8Loader.prototype.accumulateNormal = function ( i0, i1, i2, attribsOutFixed, crosses ) { + + var p0x = attribsOutFixed[ 8 * i0 ]; + var p0y = attribsOutFixed[ 8 * i0 + 1 ]; + var p0z = attribsOutFixed[ 8 * i0 + 2 ]; + + var p1x = attribsOutFixed[ 8 * i1 ]; + var p1y = attribsOutFixed[ 8 * i1 + 1 ]; + var p1z = attribsOutFixed[ 8 * i1 + 2 ]; + + var p2x = attribsOutFixed[ 8 * i2 ]; + var p2y = attribsOutFixed[ 8 * i2 + 1 ]; + var p2z = attribsOutFixed[ 8 * i2 + 2 ]; + + p1x -= p0x; + p1y -= p0y; + p1z -= p0z; + + p2x -= p0x; + p2y -= p0y; + p2z -= p0z; + + p0x = p1y * p2z - p1z * p2y; + p0y = p1z * p2x - p1x * p2z; + p0z = p1x * p2y - p1y * p2x; + + crosses[ 3 * i0 ] += p0x; + crosses[ 3 * i0 + 1 ] += p0y; + crosses[ 3 * i0 + 2 ] += p0z; + + crosses[ 3 * i1 ] += p0x; + crosses[ 3 * i1 + 1 ] += p0y; + crosses[ 3 * i1 + 2 ] += p0z; + + crosses[ 3 * i2 ] += p0x; + crosses[ 3 * i2 + 1 ] += p0y; + crosses[ 3 * i2 + 2 ] += p0z; + +}; + +THREE.UTF8Loader.prototype.decompressMesh2 = function( str, meshParams, decodeParams, name, idx, callback ) { + + var MAX_BACKREF = 96; + + // Extract conversion parameters from attribArrays. + + var stride = decodeParams.decodeScales.length; + + var decodeOffsets = decodeParams.decodeOffsets; + var decodeScales = decodeParams.decodeScales; + + var deltaStart = meshParams.attribRange[ 0 ]; + var numVerts = meshParams.attribRange[ 1 ]; + + var codeStart = meshParams.codeRange[ 0 ]; + var codeLength = meshParams.codeRange[ 1 ]; + + var numIndices = 3 * meshParams.codeRange[ 2 ]; + + var indicesOut = new Uint16Array( numIndices ); + + var crosses = new Int32Array( 3 * numVerts ); + + var lastAttrib = new Uint16Array( stride ); + + var attribsOutFixed = new Uint16Array( stride * numVerts ); + var attribsOut = new Float32Array( stride * numVerts ); + + var highest = 0; + var outputStart = 0; + + for ( var i = 0; i < numIndices; i += 3 ) { + + var code = str.charCodeAt( codeStart ++ ); + + var max_backref = Math.min( i, MAX_BACKREF ); + + if ( code < max_backref ) { + + // Parallelogram + + var winding = code % 3; + var backref = i - ( code - winding ); + var i0, i1, i2; + + switch ( winding ) { + + case 0: + + i0 = indicesOut[ backref + 2 ]; + i1 = indicesOut[ backref + 1 ]; + i2 = indicesOut[ backref + 0 ]; + break; + + case 1: + + i0 = indicesOut[ backref + 0 ]; + i1 = indicesOut[ backref + 2 ]; + i2 = indicesOut[ backref + 1 ]; + break; + + case 2: + + i0 = indicesOut[ backref + 1 ]; + i1 = indicesOut[ backref + 0 ]; + i2 = indicesOut[ backref + 2 ]; + break; + + } + + indicesOut[ outputStart ++ ] = i0; + indicesOut[ outputStart ++ ] = i1; + + code = str.charCodeAt( codeStart ++ ); + + var index = highest - code; + indicesOut[ outputStart ++ ] = index; + + if ( code === 0 ) { + + for (var j = 0; j < 5; j ++ ) { + + var deltaCode = str.charCodeAt( deltaStart + numVerts * j + highest ); + + var prediction = ((deltaCode >> 1) ^ (-(deltaCode & 1))) + + attribsOutFixed[stride * i0 + j] + + attribsOutFixed[stride * i1 + j] - + attribsOutFixed[stride * i2 + j]; + + lastAttrib[j] = prediction; + + attribsOutFixed[ stride * highest + j ] = prediction; + attribsOut[ stride * highest + j ] = decodeScales[ j ] * ( prediction + decodeOffsets[ j ] ); + + } + + highest ++; + + } else { + + this.copyAttrib( stride, attribsOutFixed, lastAttrib, index ); + + } + + this.accumulateNormal( i0, i1, index, attribsOutFixed, crosses ); + + } else { + + // Simple + + var index0 = highest - ( code - max_backref ); + + indicesOut[ outputStart ++ ] = index0; + + if ( code === max_backref ) { + + this.decodeAttrib2( str, stride, decodeOffsets, decodeScales, deltaStart, + numVerts, attribsOut, attribsOutFixed, lastAttrib, + highest ++ ); + + } else { + + this.copyAttrib(stride, attribsOutFixed, lastAttrib, index0); + + } + + code = str.charCodeAt( codeStart ++ ); + + var index1 = highest - code; + indicesOut[ outputStart ++ ] = index1; + + if ( code === 0 ) { + + this.decodeAttrib2( str, stride, decodeOffsets, decodeScales, deltaStart, + numVerts, attribsOut, attribsOutFixed, lastAttrib, + highest ++ ); + + } else { + + this.copyAttrib( stride, attribsOutFixed, lastAttrib, index1 ); + + } + + code = str.charCodeAt( codeStart ++ ); + + var index2 = highest - code; + indicesOut[ outputStart ++ ] = index2; + + if ( code === 0 ) { + + for ( var j = 0; j < 5; j ++ ) { + + lastAttrib[ j ] = ( attribsOutFixed[ stride * index0 + j ] + attribsOutFixed[ stride * index1 + j ] ) / 2; + + } + + this.decodeAttrib2( str, stride, decodeOffsets, decodeScales, deltaStart, + numVerts, attribsOut, attribsOutFixed, lastAttrib, + highest ++ ); + + } else { + + this.copyAttrib( stride, attribsOutFixed, lastAttrib, index2 ); + + } + + this.accumulateNormal( index0, index1, index2, attribsOutFixed, crosses ); + + } + + } + + for ( var i = 0; i < numVerts; i ++ ) { + + var nx = crosses[ 3 * i ]; + var ny = crosses[ 3 * i + 1 ]; + var nz = crosses[ 3 * i + 2 ]; + + var norm = 511.0 / Math.sqrt( nx * nx + ny * ny + nz * nz ); + + var cx = str.charCodeAt( deltaStart + 5 * numVerts + i ); + var cy = str.charCodeAt( deltaStart + 6 * numVerts + i ); + var cz = str.charCodeAt( deltaStart + 7 * numVerts + i ); + + attribsOut[ stride * i + 5 ] = norm * nx + ((cx >> 1) ^ (-(cx & 1))); + attribsOut[ stride * i + 6 ] = norm * ny + ((cy >> 1) ^ (-(cy & 1))); + attribsOut[ stride * i + 7 ] = norm * nz + ((cz >> 1) ^ (-(cz & 1))); + } + + callback( name, idx, attribsOut, indicesOut, undefined, meshParams ); + +}; + +THREE.UTF8Loader.prototype.downloadMesh = function ( path, name, meshEntry, decodeParams, callback ) { + + var loader = this; + var idx = 0; + + function onprogress( req, e ) { + + while ( idx < meshEntry.length ) { + + var meshParams = meshEntry[ idx ]; + var indexRange = meshParams.indexRange; + + if ( indexRange ) { + + var meshEnd = indexRange[ 0 ] + 3 * indexRange[ 1 ]; + + if ( req.responseText.length < meshEnd ) break; + + loader.decompressMesh( req.responseText, meshParams, decodeParams, name, idx, callback ); + + } else { + + var codeRange = meshParams.codeRange; + var meshEnd = codeRange[ 0 ] + codeRange[ 1 ]; + + if ( req.responseText.length < meshEnd ) break; + + loader.decompressMesh2( req.responseText, meshParams, decodeParams, name, idx, callback ); + } + + ++ idx; + + } + + } + + getHttpRequest( path, function( req, e ) { + + if ( req.status === 200 || req.status === 0 ) { + + onprogress( req, e ); + + } + + // TODO: handle errors. + + }, onprogress ); + +}; + +THREE.UTF8Loader.prototype.downloadMeshes = function ( path, meshUrlMap, decodeParams, callback ) { + + for ( var url in meshUrlMap ) { + + var meshEntry = meshUrlMap[url]; + this.downloadMesh( path + url, url, meshEntry, decodeParams, callback ); + + } + +}; + +THREE.UTF8Loader.prototype.createMeshCallback = function( materialBaseUrl, loadModelInfo, allDoneCallback ) { + + var nCompletedUrls = 0; + var nExpectedUrls = 0; + + var expectedMeshesPerUrl = {}; + var decodedMeshesPerUrl = {}; + + var modelParts = {}; + + var meshUrlMap = loadModelInfo.urls; + + for ( var url in meshUrlMap ) { + + expectedMeshesPerUrl[ url ] = meshUrlMap[ url ].length; + decodedMeshesPerUrl[ url ] = 0; + + nExpectedUrls ++; + + modelParts[ url ] = new THREE.Object3D(); + + } + + var model = new THREE.Object3D(); + + // Prepare materials first... + + var materialCreator = new THREE.MTLLoader.MaterialCreator( materialBaseUrl, loadModelInfo.options ); + materialCreator.setMaterials( loadModelInfo.materials ); + + materialCreator.preload(); + + // Create callback for creating mesh parts + + var bufferGeometryCreator = new THREE.UTF8Loader.BufferGeometryCreator(); + + var meshCallback = function( name, idx, attribArray, indexArray, bboxen, meshParams ) { + + // Got ourselves a new mesh + + // name identifies this part of the model (url) + // idx is the mesh index of this mesh of the part + // attribArray defines the vertices + // indexArray defines the faces + // bboxen defines the bounding box + // meshParams contains the material info + + var geometry = bufferGeometryCreator.create( attribArray, indexArray ); + var material = materialCreator.create( meshParams.material ); + + var mesh = new THREE.Mesh( geometry, material ); + modelParts[ name ].add( mesh ); + + //model.add(new THREE.Mesh(geometry, material)); + + decodedMeshesPerUrl[ name ] ++; + + if ( decodedMeshesPerUrl[ name ] === expectedMeshesPerUrl[ name ] ) { + + nCompletedUrls ++; + + model.add( modelParts[ name ] ); + + if ( nCompletedUrls === nExpectedUrls ) { + + // ALL DONE!!! + + allDoneCallback( model ); + + } + + } + + }; + + return meshCallback; + +}; + +THREE.UTF8Loader.prototype.downloadModel = function ( geometryBase, materialBase, model, callback ) { + + var meshCallback = this.createMeshCallback( materialBase, model, callback ); + this.downloadMeshes( geometryBase, model.urls, model.decodeParams, meshCallback ); + +}; + +THREE.UTF8Loader.prototype.downloadModelJson = function ( jsonUrl, callback, options ) { + + getJsonRequest( jsonUrl, function( loaded ) { + + if ( ! loaded.decodeParams ) { + + if ( options && options.decodeParams ) { + + loaded.decodeParams = options.decodeParams; + + } else { + + loaded.decodeParams = DEFAULT_DECODE_PARAMS; + + } + + } + + loaded.options = options; + + var geometryBase = jsonUrl.substr( 0, jsonUrl.lastIndexOf( "/" ) + 1 ); + var materialBase = geometryBase; + + if ( options && options.geometryBase ) { + + geometryBase = options.geometryBase; + + if ( geometryBase.charAt( geometryBase.length - 1 ) !== "/" ) { + + geometryBase = geometryBase + "/"; + + } + + } + + if ( options && options.materialBase ) { + + materialBase = options.materialBase; + + if ( materialBase.charAt( materialBase.length - 1 ) !== "/" ) { + + materialBase = materialBase + "/"; + + } + + } + + this.downloadModel( geometryBase, materialBase, loaded, callback ); + + }.bind( this ) ); + +}; + +// XMLHttpRequest stuff + +function getHttpRequest( url, onload, opt_onprogress ) { + + var LISTENERS = { + + load: function( e ) { onload( req, e ); }, + progress: function( e ) { opt_onprogress( req, e ); } + + }; + + var req = new XMLHttpRequest(); + addListeners( req, LISTENERS ); + + req.open( 'GET', url, true ); + req.send( null ); +} + +function getJsonRequest( url, onjson ) { + + getHttpRequest( url, + function( e ) { onjson( JSON.parse( e.responseText ) ); }, + function() {} ); + +} + +function addListeners( dom, listeners ) { + + // TODO: handle event capture, object binding. + + for ( var key in listeners ) { + + dom.addEventListener( key, listeners[ key ] ); + + } +} diff --git a/node_modules/three/examples/js/loaders/VRMLLoader.js b/node_modules/three/examples/js/loaders/VRMLLoader.js new file mode 100644 index 00000000..faac8ee4 --- /dev/null +++ b/node_modules/three/examples/js/loaders/VRMLLoader.js @@ -0,0 +1,1012 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.VRMLLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.VRMLLoader.prototype = { + + constructor: THREE.VRMLLoader, + + // for IndexedFaceSet support + isRecordingPoints: false, + isRecordingFaces: false, + points: [], + indexes : [], + + // for Background support + isRecordingAngles: false, + isRecordingColors: false, + angles: [], + colors: [], + + recordingFieldname: null, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + THREE.ImageUtils.crossOrigin = value; + + }, + + parse: function ( data ) { + + var texturePath = this.texturePath || ''; + + var parseV1 = function ( lines, scene ) { + + console.warn( 'VRML V1.0 not supported yet' ); + + }; + + var parseV2 = function ( lines, scene ) { + + var defines = {}; + var float_pattern = /(\b|\-|\+)([\d\.e]+)/; + var float2_pattern = /([\d\.\+\-e]+)\s+([\d\.\+\-e]+)/g; + var float3_pattern = /([\d\.\+\-e]+)\s+([\d\.\+\-e]+)\s+([\d\.\+\-e]+)/g; + + /** + * Interpolates colors a and b following their relative distance + * expressed by t. + * + * @param float a + * @param float b + * @param float t + * @returns {Color} + */ + var interpolateColors = function( a, b, t ) { + + var deltaR = a.r - b.r; + var deltaG = a.g - b.g; + var deltaB = a.b - b.b; + + var c = new THREE.Color(); + + c.r = a.r - t * deltaR; + c.g = a.g - t * deltaG; + c.b = a.b - t * deltaB; + + return c; + + }; + + /** + * Vertically paints the faces interpolating between the + * specified colors at the specified angels. This is used for the Background + * node, but could be applied to other nodes with multiple faces as well. + * + * When used with the Background node, default is directionIsDown is true if + * interpolating the skyColor down from the Zenith. When interpolationg up from + * the Nadir i.e. interpolating the groundColor, the directionIsDown is false. + * + * The first angle is never specified, it is the Zenith (0 rad). Angles are specified + * in radians. The geometry is thought a sphere, but could be anything. The color interpolation + * is linear along the Y axis in any case. + * + * You must specify one more color than you have angles at the beginning of the colors array. + * This is the color of the Zenith (the top of the shape). + * + * @param geometry + * @param radius + * @param angles + * @param colors + * @param boolean directionIsDown Whether to work bottom up or top down. + */ + var paintFaces = function ( geometry, radius, angles, colors, directionIsDown ) { + + var f, n, p, vertexIndex, color; + + var direction = directionIsDown ? 1 : - 1; + + var faceIndices = [ 'a', 'b', 'c', 'd' ]; + + var coord = [ ], aColor, bColor, t = 1, A = {}, B = {}, applyColor = false, colorIndex; + + for ( var k = 0; k < angles.length; k ++ ) { + + var vec = { }; + + // push the vector at which the color changes + vec.y = direction * ( Math.cos( angles[ k ] ) * radius ); + + vec.x = direction * ( Math.sin( angles[ k ] ) * radius ); + + coord.push( vec ); + + } + + // painting the colors on the faces + for ( var i = 0; i < geometry.faces.length ; i ++ ) { + + f = geometry.faces[ i ]; + + n = ( f instanceof THREE.Face3 ) ? 3 : 4; + + for ( var j = 0; j < n; j ++ ) { + + vertexIndex = f[ faceIndices[ j ] ]; + + p = geometry.vertices[ vertexIndex ]; + + for ( var index = 0; index < colors.length; index ++ ) { + + // linear interpolation between aColor and bColor, calculate proportion + // A is previous point (angle) + if ( index === 0 ) { + + A.x = 0; + A.y = directionIsDown ? radius : - 1 * radius; + + } else { + + A.x = coord[ index - 1 ].x; + A.y = coord[ index - 1 ].y; + + } + + // B is current point (angle) + B = coord[ index ]; + + if ( undefined !== B ) { + + // p has to be between the points A and B which we interpolate + applyColor = directionIsDown ? p.y <= A.y && p.y > B.y : p.y >= A.y && p.y < B.y; + + if ( applyColor ) { + + bColor = colors[ index + 1 ]; + + aColor = colors[ index ]; + + // below is simple linear interpolation + t = Math.abs( p.y - A.y ) / ( A.y - B.y ); + + // to make it faster, you can only calculate this if the y coord changes, the color is the same for points with the same y + color = interpolateColors( aColor, bColor, t ); + + f.vertexColors[ j ] = color; + + } + + } else if ( undefined === f.vertexColors[ j ] ) { + + colorIndex = directionIsDown ? colors.length - 1 : 0; + f.vertexColors[ j ] = colors[ colorIndex ]; + + } + + } + + } + + } + + }; + + var parseProperty = function ( node, line ) { + + var parts = [], part, property = {}, fieldName; + + /** + * Expression for matching relevant information, such as a name or value, but not the separators + * @type {RegExp} + */ + var regex = /[^\s,\[\]]+/g; + + var point, index, angles, colors; + + while ( null != ( part = regex.exec( line ) ) ) { + + parts.push( part[ 0 ] ); + + } + + fieldName = parts[ 0 ]; + + + // trigger several recorders + switch ( fieldName ) { + case 'skyAngle': + case 'groundAngle': + this.recordingFieldname = fieldName; + this.isRecordingAngles = true; + this.angles = []; + break; + case 'skyColor': + case 'groundColor': + this.recordingFieldname = fieldName; + this.isRecordingColors = true; + this.colors = []; + break; + case 'point': + this.recordingFieldname = fieldName; + this.isRecordingPoints = true; + this.points = []; + break; + case 'coordIndex': + case 'texCoordIndex': + this.recordingFieldname = fieldName; + this.isRecordingFaces = true; + this.indexes = []; + } + + if ( this.isRecordingFaces ) { + + // the parts hold the indexes as strings + if ( parts.length > 0 ) { + + index = []; + + for ( var ind = 0; ind < parts.length; ind ++ ) { + + // the part should either be positive integer or -1 + if ( ! /(-?\d+)/.test( parts[ ind ] ) ) { + + continue; + + } + + // end of current face + if ( parts[ ind ] === "-1" ) { + + if ( index.length > 0 ) { + + this.indexes.push( index ); + + } + + // start new one + index = []; + + } else { + + index.push( parseInt( parts[ ind ] ) ); + + } + + } + + } + + // end + if ( /]/.exec( line ) ) { + + this.isRecordingFaces = false; + node[this.recordingFieldname] = this.indexes; + + } + + } else if ( this.isRecordingPoints ) { + + if ( node.nodeType == 'Coordinate' ) + + while ( null !== ( parts = float3_pattern.exec( line ) ) ) { + + point = { + x: parseFloat( parts[ 1 ] ), + y: parseFloat( parts[ 2 ] ), + z: parseFloat( parts[ 3 ] ) + }; + + this.points.push( point ); + + } + + if ( node.nodeType == 'TextureCoordinate' ) + + while ( null !== ( parts = float2_pattern.exec( line ) ) ) { + + point = { + x: parseFloat( parts[ 1 ] ), + y: parseFloat( parts[ 2 ] ) + }; + + this.points.push( point ); + + } + + // end + if ( /]/.exec( line ) ) { + + this.isRecordingPoints = false; + node.points = this.points; + + } + + } else if ( this.isRecordingAngles ) { + + // the parts hold the angles as strings + if ( parts.length > 0 ) { + + for ( var ind = 0; ind < parts.length; ind ++ ) { + + // the part should be a float + if ( ! float_pattern.test( parts[ ind ] ) ) { + + continue; + + } + + this.angles.push( parseFloat( parts[ ind ] ) ); + + } + + } + + // end + if ( /]/.exec( line ) ) { + + this.isRecordingAngles = false; + node[ this.recordingFieldname ] = this.angles; + + } + + } else if ( this.isRecordingColors ) { + + while ( null !== ( parts = float3_pattern.exec( line ) ) ) { + + color = { + r: parseFloat( parts[ 1 ] ), + g: parseFloat( parts[ 2 ] ), + b: parseFloat( parts[ 3 ] ) + }; + + this.colors.push( color ); + + } + + // end + if ( /]/.exec( line ) ) { + + this.isRecordingColors = false; + node[ this.recordingFieldname ] = this.colors; + + } + + } else if ( parts[ parts.length - 1 ] !== 'NULL' && fieldName !== 'children' ) { + + switch ( fieldName ) { + + case 'diffuseColor': + case 'emissiveColor': + case 'specularColor': + case 'color': + + if ( parts.length != 4 ) { + + console.warn( 'Invalid color format detected for ' + fieldName ); + break; + + } + + property = { + r: parseFloat( parts[ 1 ] ), + g: parseFloat( parts[ 2 ] ), + b: parseFloat( parts[ 3 ] ) + }; + + break; + + case 'translation': + case 'scale': + case 'size': + if ( parts.length != 4 ) { + + console.warn( 'Invalid vector format detected for ' + fieldName ); + break; + + } + + property = { + x: parseFloat( parts[ 1 ] ), + y: parseFloat( parts[ 2 ] ), + z: parseFloat( parts[ 3 ] ) + }; + + break; + + case 'radius': + case 'topRadius': + case 'bottomRadius': + case 'height': + case 'transparency': + case 'shininess': + case 'ambientIntensity': + if ( parts.length != 2 ) { + + console.warn( 'Invalid single float value specification detected for ' + fieldName ); + break; + + } + + property = parseFloat( parts[ 1 ] ); + + break; + + case 'rotation': + if ( parts.length != 5 ) { + + console.warn( 'Invalid quaternion format detected for ' + fieldName ); + break; + + } + + property = { + x: parseFloat( parts[ 1 ] ), + y: parseFloat( parts[ 2 ] ), + z: parseFloat( parts[ 3 ] ), + w: parseFloat( parts[ 4 ] ) + }; + + break; + + case 'ccw': + case 'solid': + case 'colorPerVertex': + case 'convex': + if ( parts.length != 2 ) { + + console.warn( 'Invalid format detected for ' + fieldName ); + break; + + } + + property = parts[ 1 ] === 'TRUE' ? true : false; + + break; + } + + node[ fieldName ] = property; + + } + + return property; + + }; + + var getTree = function ( lines ) { + + var tree = { 'string': 'Scene', children: [] }; + var current = tree; + var matches; + var specification; + + for ( var i = 0; i < lines.length; i ++ ) { + + var comment = ''; + + var line = lines[ i ]; + + // omit whitespace only lines + if ( null !== ( result = /^\s+?$/g.exec( line ) ) ) { + + continue; + + } + + line = line.trim(); + + // skip empty lines + if ( line === '' ) { + + continue; + + } + + if ( /#/.exec( line ) ) { + + var parts = line.split( '#' ); + + // discard everything after the #, it is a comment + line = parts[ 0 ]; + + // well, let's also keep the comment + comment = parts[ 1 ]; + + } + + if ( matches = /([^\s]*){1}(?:\s+)?{/.exec( line ) ) { + + // first subpattern should match the Node name + + var block = { 'nodeType' : matches[ 1 ], 'string': line, 'parent': current, 'children': [], 'comment' : comment }; + current.children.push( block ); + current = block; + + if ( /}/.exec( line ) ) { + + // example: geometry Box { size 1 1 1 } # all on the same line + specification = /{(.*)}/.exec( line )[ 1 ]; + + // todo: remove once new parsing is complete? + block.children.push( specification ); + + parseProperty( current, specification ); + + current = current.parent; + + } + + } else if ( /}/.exec( line ) ) { + + current = current.parent; + + } else if ( line !== '' ) { + + parseProperty( current, line ); + // todo: remove once new parsing is complete? we still do not parse geometry and appearance the new way + current.children.push( line ); + + } + + } + + return tree; + + }; + + var parseNode = function ( data, parent ) { + + // console.log( data ); + + if ( typeof data === 'string' ) { + + if ( /USE/.exec( data ) ) { + + var defineKey = /USE\s+?(\w+)/.exec( data )[ 1 ]; + + if ( undefined == defines[ defineKey ] ) { + + console.warn( defineKey + ' is not defined.' ); + + } else { + + if ( /appearance/.exec( data ) && defineKey ) { + + parent.material = defines[ defineKey ].clone(); + + } else if ( /geometry/.exec( data ) && defineKey ) { + + parent.geometry = defines[ defineKey ].clone(); + + // the solid property is not cloned with clone(), is only needed for VRML loading, so we need to transfer it + if ( undefined !== defines[ defineKey ].solid && defines[ defineKey ].solid === false ) { + + parent.geometry.solid = false; + parent.material.side = THREE.DoubleSide; + + } + + } else if ( defineKey ) { + + var object = defines[ defineKey ].clone(); + parent.add( object ); + + } + + } + + } + + return; + + } + + var object = parent; + + if ( 'Transform' === data.nodeType || 'Group' === data.nodeType ) { + + object = new THREE.Object3D(); + + if ( /DEF/.exec( data.string ) ) { + + object.name = /DEF\s+(\w+)/.exec( data.string )[ 1 ]; + defines[ object.name ] = object; + + } + + if ( undefined !== data[ 'translation' ] ) { + + var t = data.translation; + + object.position.set( t.x, t.y, t.z ); + + } + + if ( undefined !== data.rotation ) { + + var r = data.rotation; + + object.quaternion.setFromAxisAngle( new THREE.Vector3( r.x, r.y, r.z ), r.w ); + + } + + if ( undefined !== data.scale ) { + + var s = data.scale; + + object.scale.set( s.x, s.y, s.z ); + + } + + parent.add( object ); + + } else if ( 'Shape' === data.nodeType ) { + + object = new THREE.Mesh(); + + if ( /DEF/.exec( data.string ) ) { + + object.name = /DEF\s+(\w+)/.exec( data.string )[ 1 ]; + + defines[ object.name ] = object; + + } + + parent.add( object ); + + } else if ( 'Background' === data.nodeType ) { + + var segments = 20; + + // sky (full sphere): + + var radius = 2e4; + + var skyGeometry = new THREE.SphereGeometry( radius, segments, segments ); + var skyMaterial = new THREE.MeshBasicMaterial( { fog: false, side: THREE.BackSide } ); + + if ( data.skyColor.length > 1 ) { + + paintFaces( skyGeometry, radius, data.skyAngle, data.skyColor, true ); + + skyMaterial.vertexColors = THREE.VertexColors + + } else { + + var color = data.skyColor[ 0 ]; + skyMaterial.color.setRGB( color.r, color.b, color.g ); + + } + + scene.add( new THREE.Mesh( skyGeometry, skyMaterial ) ); + + // ground (half sphere): + + if ( data.groundColor !== undefined ) { + + radius = 1.2e4; + + var groundGeometry = new THREE.SphereGeometry( radius, segments, segments, 0, 2 * Math.PI, 0.5 * Math.PI, 1.5 * Math.PI ); + var groundMaterial = new THREE.MeshBasicMaterial( { fog: false, side: THREE.BackSide, vertexColors: THREE.VertexColors } ); + + paintFaces( groundGeometry, radius, data.groundAngle, data.groundColor, false ); + + scene.add( new THREE.Mesh( groundGeometry, groundMaterial ) ); + + } + + } else if ( /geometry/.exec( data.string ) ) { + + if ( 'Box' === data.nodeType ) { + + var s = data.size; + + parent.geometry = new THREE.BoxGeometry( s.x, s.y, s.z ); + + } else if ( 'Cylinder' === data.nodeType ) { + + parent.geometry = new THREE.CylinderGeometry( data.radius, data.radius, data.height ); + + } else if ( 'Cone' === data.nodeType ) { + + parent.geometry = new THREE.CylinderGeometry( data.topRadius, data.bottomRadius, data.height ); + + } else if ( 'Sphere' === data.nodeType ) { + + parent.geometry = new THREE.SphereGeometry( data.radius ); + + } else if ( 'IndexedFaceSet' === data.nodeType ) { + + var geometry = new THREE.Geometry(); + + var indexes, uvIndexes, uvs; + + for ( var i = 0, j = data.children.length; i < j; i ++ ) { + + var child = data.children[ i ]; + + var vec; + + if ( 'TextureCoordinate' === child.nodeType ) { + + uvs = child.points; + + } + + + if ( 'Coordinate' === child.nodeType ) { + + if ( child.points ) { + + for ( var k = 0, l = child.points.length; k < l; k ++ ) { + + var point = child.points[ k ]; + + vec = new THREE.Vector3( point.x, point.y, point.z ); + + geometry.vertices.push( vec ); + + } + + } + + if ( child.string.indexOf ( 'DEF' ) > -1 ) { + + var name = /DEF\s+(\w+)/.exec( child.string )[ 1 ]; + + defines[ name ] = geometry.vertices; + + } + + if ( child.string.indexOf ( 'USE' ) > -1 ) { + + var defineKey = /USE\s+(\w+)/.exec( child.string )[ 1 ]; + + geometry.vertices = defines[ defineKey ]; + } + + } + + } + + var skip = 0; + + // some shapes only have vertices for use in other shapes + if ( data.coordIndex ) { + + // read this: http://math.hws.edu/eck/cs424/notes2013/16_Threejs_Advanced.html + for ( var i = 0, j = data.coordIndex.length; i < j; i ++ ) { + + indexes = data.coordIndex[ i ]; if ( data.texCoordIndex ) uvIndexes = data.texCoordIndex[ i ]; + + // vrml support multipoint indexed face sets (more then 3 vertices). You must calculate the composing triangles here + skip = 0; + + // Face3 only works with triangles, but IndexedFaceSet allows shapes with more then three vertices, build them of triangles + while ( indexes.length >= 3 && skip < ( indexes.length - 2 ) ) { + + var face = new THREE.Face3( + indexes[ 0 ], + indexes[ skip + (data.ccw ? 1 : 2) ], + indexes[ skip + (data.ccw ? 2 : 1) ], + null // normal, will be added later + // todo: pass in the color, if a color index is present + ); + + if ( uvs && uvIndexes ) { + geometry.faceVertexUvs [0].push( [ + new THREE.Vector2 ( + uvs[ uvIndexes[ 0 ] ].x , + uvs[ uvIndexes[ 0 ] ].y + ) , + new THREE.Vector2 ( + uvs[ uvIndexes[ skip + (data.ccw ? 1 : 2) ] ].x , + uvs[ uvIndexes[ skip + (data.ccw ? 1 : 2) ] ].y + ) , + new THREE.Vector2 ( + uvs[ uvIndexes[ skip + (data.ccw ? 2 : 1) ] ].x , + uvs[ uvIndexes[ skip + (data.ccw ? 2 : 1) ] ].y + ) + ] ); + } + + skip ++; + + geometry.faces.push( face ); + + } + + + } + + } else { + + // do not add dummy mesh to the scene + parent.parent.remove( parent ); + + } + + if ( false === data.solid ) { + + parent.material.side = THREE.DoubleSide; + + } + + // we need to store it on the geometry for use with defines + geometry.solid = data.solid; + + geometry.computeFaceNormals(); + //geometry.computeVertexNormals(); // does not show + geometry.computeBoundingSphere(); + + // see if it's a define + if ( /DEF/.exec( data.string ) ) { + + geometry.name = /DEF (\w+)/.exec( data.string )[ 1 ]; + defines[ geometry.name ] = geometry; + + } + + parent.geometry = geometry; + + } + + return; + + } else if ( /appearance/.exec( data.string ) ) { + + for ( var i = 0; i < data.children.length; i ++ ) { + + var child = data.children[ i ]; + + if ( 'Material' === child.nodeType ) { + + var material = new THREE.MeshPhongMaterial(); + + if ( undefined !== child.diffuseColor ) { + + var d = child.diffuseColor; + + material.color.setRGB( d.r, d.g, d.b ); + + } + + if ( undefined !== child.emissiveColor ) { + + var e = child.emissiveColor; + + material.emissive.setRGB( e.r, e.g, e.b ); + + } + + if ( undefined !== child.specularColor ) { + + var s = child.specularColor; + + material.specular.setRGB( s.r, s.g, s.b ); + + } + + if ( undefined !== child.transparency ) { + + var t = child.transparency; + + // transparency is opposite of opacity + material.opacity = Math.abs( 1 - t ); + + material.transparent = true; + + } + + if ( /DEF/.exec( data.string ) ) { + + material.name = /DEF (\w+)/.exec( data.string )[ 1 ]; + + defines[ material.name ] = material; + + } + + parent.material = material; + + } + + if ( 'ImageTexture' === child.nodeType ) { + + var textureName = /"([^"]+)"/.exec(child.children[ 0 ]); + + if (textureName) { + + parent.material.name = textureName[ 1 ]; + + parent.material.map = THREE.ImageUtils.loadTexture (texturePath + textureName[ 1 ]); + + } + + } + + } + + return; + + } + + for ( var i = 0, l = data.children.length; i < l; i ++ ) { + + var child = data.children[ i ]; + + parseNode( data.children[ i ], object ); + + } + + }; + + parseNode( getTree( lines ), scene ); + + }; + + var scene = new THREE.Scene(); + + var lines = data.split( '\n' ); + + // some lines do not have breaks + for (var i = lines.length -1; i > -1; i--) { + + // split lines with {..{ or {..[ - some have both + if (/{.*[{\[]/.test (lines[i])) { + var parts = lines[i].split ('{').join ('{\n').split ('\n'); + parts.unshift(1); + parts.unshift(i); + lines.splice.apply(lines, parts); + } else + + // split lines with ]..} + if (/\].*}/.test (lines[i])) { + var parts = lines[i].split (']').join (']\n').split ('\n'); + parts.unshift(1); + parts.unshift(i); + lines.splice.apply(lines, parts); + } + + // split lines with }..} + if (/}.*}/.test (lines[i])) { + var parts = lines[i].split ('}').join ('}\n').split ('\n'); + parts.unshift(1); + parts.unshift(i); + lines.splice.apply(lines, parts); + } + + // force the parser to create Coordinate node for empty coords + // coord USE something -> coord USE something Coordinate {} + if((lines[i].indexOf ('coord') > -1) && (lines[i].indexOf ('[') < 0) && (lines[i].indexOf ('{') < 0)) { + lines[i] += ' Coordinate {}'; + } + } + + var header = lines.shift(); + + if ( /V1.0/.exec( header ) ) { + + parseV1( lines, scene ); + + } else if ( /V2.0/.exec( header ) ) { + + parseV2( lines, scene ); + + } + + return scene; + + } + +}; diff --git a/node_modules/three/examples/js/loaders/VTKLoader.js b/node_modules/three/examples/js/loaders/VTKLoader.js new file mode 100644 index 00000000..6070dc3c --- /dev/null +++ b/node_modules/three/examples/js/loaders/VTKLoader.js @@ -0,0 +1,113 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.VTKLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.VTKLoader.prototype = { + + constructor: THREE.VTKLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( data ) { + + var indices = []; + var positions = []; + + var result; + + // float float float + + var pat3Floats = /([\-]?[\d]+[\.]?[\d|\-|e]*)[ ]+([\-]?[\d]+[\.]?[\d|\-|e]*)[ ]+([\-]?[\d]+[\.]?[\d|\-|e]*)/g; + var patTriangle = /^3[ ]+([\d]+)[ ]+([\d]+)[ ]+([\d]+)/; + var patQuad = /^4[ ]+([\d]+)[ ]+([\d]+)[ ]+([\d]+)[ ]+([\d]+)/; + var patPOINTS = /^POINTS /; + var patPOLYGONS = /^POLYGONS /; + var inPointsSection = false; + var inPolygonsSection = false; + + var lines = data.split('\n'); + for ( var i = 0; i < lines.length; ++i ) { + + line = lines[i]; + + if ( inPointsSection ) { + + // get the vertices + + while ( ( result = pat3Floats.exec( line ) ) !== null ) { + positions.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) ); + } + } + else if ( inPolygonsSection ) { + + result = patTriangle.exec(line); + + if ( result !== null ) { + + // 3 int int int + // triangle + + indices.push( parseInt( result[ 1 ] ), parseInt( result[ 2 ] ), parseInt( result[ 3 ] ) ); + } + else { + + result = patQuad.exec(line); + + if ( result !== null ) { + + // 4 int int int int + // break quad into two triangles + + indices.push( parseInt( result[ 1 ] ), parseInt( result[ 2 ] ), parseInt( result[ 4 ] ) ); + indices.push( parseInt( result[ 2 ] ), parseInt( result[ 3 ] ), parseInt( result[ 4 ] ) ); + } + + } + + } + + if ( patPOLYGONS.exec(line) !== null ) { + inPointsSection = false; + inPolygonsSection = true; + } + if ( patPOINTS.exec(line) !== null ) { + inPolygonsSection = false; + inPointsSection = true; + } + } + + var geometry = new THREE.BufferGeometry(); + geometry.setIndex( new THREE.BufferAttribute( new ( indices.length > 65535 ? Uint32Array : Uint16Array )( indices ), 1 ) ); + geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( positions ), 3 ) ); + + return geometry; + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.VTKLoader.prototype ); diff --git a/node_modules/three/examples/js/loaders/collada/Animation.js b/node_modules/three/examples/js/loaders/collada/Animation.js new file mode 100644 index 00000000..b3abfaf2 --- /dev/null +++ b/node_modules/three/examples/js/loaders/collada/Animation.js @@ -0,0 +1,407 @@ +/** + * @author mikael emtinger / http://gomo.se/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Animation = function ( root, data ) { + + this.root = root; + this.data = THREE.AnimationHandler.init( data ); + this.hierarchy = THREE.AnimationHandler.parse( root ); + + this.currentTime = 0; + this.timeScale = 1; + + this.isPlaying = false; + this.loop = true; + this.weight = 0; + + this.interpolationType = THREE.AnimationHandler.LINEAR; + +}; + +THREE.Animation.prototype = { + + constructor: THREE.Animation, + + keyTypes: [ "pos", "rot", "scl" ], + + play: function ( startTime, weight ) { + + this.currentTime = startTime !== undefined ? startTime : 0; + this.weight = weight !== undefined ? weight : 1; + + this.isPlaying = true; + + this.reset(); + + THREE.AnimationHandler.play( this ); + + }, + + stop: function() { + + this.isPlaying = false; + + THREE.AnimationHandler.stop( this ); + + }, + + reset: function () { + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + var object = this.hierarchy[ h ]; + + if ( object.animationCache === undefined ) { + + object.animationCache = { + animations: {}, + blending: { + positionWeight: 0.0, + quaternionWeight: 0.0, + scaleWeight: 0.0 + } + }; + + } + + var name = this.data.name; + var animations = object.animationCache.animations; + var animationCache = animations[ name ]; + + if ( animationCache === undefined ) { + + animationCache = { + prevKey: { pos: 0, rot: 0, scl: 0 }, + nextKey: { pos: 0, rot: 0, scl: 0 }, + originalMatrix: object.matrix + }; + + animations[ name ] = animationCache; + + } + + // Get keys to match our current time + + for ( var t = 0; t < 3; t ++ ) { + + var type = this.keyTypes[ t ]; + + var prevKey = this.data.hierarchy[ h ].keys[ 0 ]; + var nextKey = this.getNextKeyWith( type, h, 1 ); + + while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { + + prevKey = nextKey; + nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); + + } + + animationCache.prevKey[ type ] = prevKey; + animationCache.nextKey[ type ] = nextKey; + + } + + } + + }, + + resetBlendWeights: function () { + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + var object = this.hierarchy[ h ]; + var animationCache = object.animationCache; + + if ( animationCache !== undefined ) { + + var blending = animationCache.blending; + + blending.positionWeight = 0.0; + blending.quaternionWeight = 0.0; + blending.scaleWeight = 0.0; + + } + + } + + }, + + update: ( function() { + + var points = []; + var target = new THREE.Vector3(); + var newVector = new THREE.Vector3(); + var newQuat = new THREE.Quaternion(); + + // Catmull-Rom spline + + var interpolateCatmullRom = function ( points, scale ) { + + var c = [], v3 = [], + point, intPoint, weight, w2, w3, + pa, pb, pc, pd; + + point = ( points.length - 1 ) * scale; + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1; + c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2; + + pa = points[ c[ 0 ] ]; + pb = points[ c[ 1 ] ]; + pc = points[ c[ 2 ] ]; + pd = points[ c[ 3 ] ]; + + w2 = weight * weight; + w3 = weight * w2; + + v3[ 0 ] = interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 ); + v3[ 1 ] = interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 ); + v3[ 2 ] = interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 ); + + return v3; + + }; + + var interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) { + + var v0 = ( p2 - p0 ) * 0.5, + v1 = ( p3 - p1 ) * 0.5; + + return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + }; + + return function ( delta ) { + + if ( this.isPlaying === false ) return; + + this.currentTime += delta * this.timeScale; + + if ( this.weight === 0 ) + return; + + // + + var duration = this.data.length; + + if ( this.currentTime > duration || this.currentTime < 0 ) { + + if ( this.loop ) { + + this.currentTime %= duration; + + if ( this.currentTime < 0 ) + this.currentTime += duration; + + this.reset(); + + } else { + + this.stop(); + + } + + } + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + var object = this.hierarchy[ h ]; + var animationCache = object.animationCache.animations[ this.data.name ]; + var blending = object.animationCache.blending; + + // loop through pos/rot/scl + + for ( var t = 0; t < 3; t ++ ) { + + // get keys + + var type = this.keyTypes[ t ]; + var prevKey = animationCache.prevKey[ type ]; + var nextKey = animationCache.nextKey[ type ]; + + if ( ( this.timeScale > 0 && nextKey.time <= this.currentTime ) || + ( this.timeScale < 0 && prevKey.time >= this.currentTime ) ) { + + prevKey = this.data.hierarchy[ h ].keys[ 0 ]; + nextKey = this.getNextKeyWith( type, h, 1 ); + + while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { + + prevKey = nextKey; + nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); + + } + + animationCache.prevKey[ type ] = prevKey; + animationCache.nextKey[ type ] = nextKey; + + } + + var scale = ( this.currentTime - prevKey.time ) / ( nextKey.time - prevKey.time ); + + var prevXYZ = prevKey[ type ]; + var nextXYZ = nextKey[ type ]; + + if ( scale < 0 ) scale = 0; + if ( scale > 1 ) scale = 1; + + // interpolate + + if ( type === "pos" ) { + + if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) { + + newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; + newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; + newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; + + // blend + var proportionalWeight = this.weight / ( this.weight + blending.positionWeight ); + object.position.lerp( newVector, proportionalWeight ); + blending.positionWeight += this.weight; + + } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ]; + points[ 1 ] = prevXYZ; + points[ 2 ] = nextXYZ; + points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ]; + + scale = scale * 0.33 + 0.33; + + var currentPoint = interpolateCatmullRom( points, scale ); + var proportionalWeight = this.weight / ( this.weight + blending.positionWeight ); + blending.positionWeight += this.weight; + + // blend + + var vector = object.position; + + vector.x = vector.x + ( currentPoint[ 0 ] - vector.x ) * proportionalWeight; + vector.y = vector.y + ( currentPoint[ 1 ] - vector.y ) * proportionalWeight; + vector.z = vector.z + ( currentPoint[ 2 ] - vector.z ) * proportionalWeight; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + var forwardPoint = interpolateCatmullRom( points, scale * 1.01 ); + + target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] ); + target.sub( vector ); + target.y = 0; + target.normalize(); + + var angle = Math.atan2( target.x, target.z ); + object.rotation.set( 0, angle, 0 ); + + } + + } + + } else if ( type === "rot" ) { + + THREE.Quaternion.slerp( prevXYZ, nextXYZ, newQuat, scale ); + + // Avoid paying the cost of an additional slerp if we don't have to + if ( blending.quaternionWeight === 0 ) { + + object.quaternion.copy( newQuat ); + blending.quaternionWeight = this.weight; + + } else { + + var proportionalWeight = this.weight / ( this.weight + blending.quaternionWeight ); + THREE.Quaternion.slerp( object.quaternion, newQuat, object.quaternion, proportionalWeight ); + blending.quaternionWeight += this.weight; + + } + + } else if ( type === "scl" ) { + + newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; + newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; + newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; + + var proportionalWeight = this.weight / ( this.weight + blending.scaleWeight ); + object.scale.lerp( newVector, proportionalWeight ); + blending.scaleWeight += this.weight; + + } + + } + + } + + return true; + + }; + + } )(), + + getNextKeyWith: function ( type, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + key = key < keys.length - 1 ? key : keys.length - 1; + + } else { + + key = key % keys.length; + + } + + for ( ; key < keys.length; key ++ ) { + + if ( keys[ key ][ type ] !== undefined ) { + + return keys[ key ]; + + } + + } + + return this.data.hierarchy[ h ].keys[ 0 ]; + + }, + + getPrevKeyWith: function ( type, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + key = key > 0 ? key : 0; + + } else { + + key = key >= 0 ? key : key + keys.length; + + } + + + for ( ; key >= 0; key -- ) { + + if ( keys[ key ][ type ] !== undefined ) { + + return keys[ key ]; + + } + + } + + return this.data.hierarchy[ h ].keys[ keys.length - 1 ]; + + } + +}; diff --git a/node_modules/three/examples/js/loaders/collada/AnimationHandler.js b/node_modules/three/examples/js/loaders/collada/AnimationHandler.js new file mode 100644 index 00000000..d35ae7cb --- /dev/null +++ b/node_modules/three/examples/js/loaders/collada/AnimationHandler.js @@ -0,0 +1,220 @@ +/** + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.AnimationHandler = { + + LINEAR: 0, + CATMULLROM: 1, + CATMULLROM_FORWARD: 2, + + // + + add: function () { + + console.warn( 'THREE.AnimationHandler.add() has been deprecated.' ); + + }, + get: function () { + + console.warn( 'THREE.AnimationHandler.get() has been deprecated.' ); + + }, + remove: function () { + + console.warn( 'THREE.AnimationHandler.remove() has been deprecated.' ); + + }, + + // + + animations: [], + + init: function ( data ) { + + if ( data.initialized === true ) return data; + + // loop through all keys + + for ( var h = 0; h < data.hierarchy.length; h ++ ) { + + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + + // remove minus times + + if ( data.hierarchy[ h ].keys[ k ].time < 0 ) { + + data.hierarchy[ h ].keys[ k ].time = 0; + + } + + // create quaternions + + if ( data.hierarchy[ h ].keys[ k ].rot !== undefined && + ! ( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) { + + var quat = data.hierarchy[ h ].keys[ k ].rot; + data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion().fromArray( quat ); + + } + + } + + // prepare morph target keys + + if ( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) { + + // get all used + + var usedMorphTargets = {}; + + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + + for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { + + var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ]; + usedMorphTargets[ morphTargetName ] = - 1; + + } + + } + + data.hierarchy[ h ].usedMorphTargets = usedMorphTargets; + + + // set all used on all frames + + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + + var influences = {}; + + for ( var morphTargetName in usedMorphTargets ) { + + for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { + + if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) { + + influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ]; + break; + + } + + } + + if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) { + + influences[ morphTargetName ] = 0; + + } + + } + + data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences; + + } + + } + + + // remove all keys that are on the same time + + for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) { + + if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) { + + data.hierarchy[ h ].keys.splice( k, 1 ); + k --; + + } + + } + + + // set index + + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + + data.hierarchy[ h ].keys[ k ].index = k; + + } + + } + + data.initialized = true; + + return data; + + }, + + parse: function ( root ) { + + var parseRecurseHierarchy = function ( root, hierarchy ) { + + hierarchy.push( root ); + + for ( var c = 0; c < root.children.length; c ++ ) + parseRecurseHierarchy( root.children[ c ], hierarchy ); + + }; + + // setup hierarchy + + var hierarchy = []; + + if ( root instanceof THREE.SkinnedMesh ) { + + for ( var b = 0; b < root.skeleton.bones.length; b ++ ) { + + hierarchy.push( root.skeleton.bones[ b ] ); + + } + + } else { + + parseRecurseHierarchy( root, hierarchy ); + + } + + return hierarchy; + + }, + + play: function ( animation ) { + + if ( this.animations.indexOf( animation ) === - 1 ) { + + this.animations.push( animation ); + + } + + }, + + stop: function ( animation ) { + + var index = this.animations.indexOf( animation ); + + if ( index !== - 1 ) { + + this.animations.splice( index, 1 ); + + } + + }, + + update: function ( deltaTimeMS ) { + + for ( var i = 0; i < this.animations.length; i ++ ) { + + this.animations[ i ].resetBlendWeights(); + + } + + for ( var i = 0; i < this.animations.length; i ++ ) { + + this.animations[ i ].update( deltaTimeMS ); + + } + + } + +}; diff --git a/node_modules/three/examples/js/loaders/collada/KeyFrameAnimation.js b/node_modules/three/examples/js/loaders/collada/KeyFrameAnimation.js new file mode 100644 index 00000000..de00aa1d --- /dev/null +++ b/node_modules/three/examples/js/loaders/collada/KeyFrameAnimation.js @@ -0,0 +1,241 @@ +/** + * @author mikael emtinger / http://gomo.se/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author khang duong + * @author erik kitson + */ + +THREE.KeyFrameAnimation = function ( data ) { + + this.root = data.node; + this.data = THREE.AnimationHandler.init( data ); + this.hierarchy = THREE.AnimationHandler.parse( this.root ); + this.currentTime = 0; + this.timeScale = 0.001; + this.isPlaying = false; + this.isPaused = true; + this.loop = true; + + // initialize to first keyframes + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + var keys = this.data.hierarchy[ h ].keys, + sids = this.data.hierarchy[ h ].sids, + obj = this.hierarchy[ h ]; + + if ( keys.length && sids ) { + + for ( var s = 0; s < sids.length; s ++ ) { + + var sid = sids[ s ], + next = this.getNextKeyWith( sid, h, 0 ); + + if ( next ) { + + next.apply( sid ); + + } + + } + + obj.matrixAutoUpdate = false; + this.data.hierarchy[ h ].node.updateMatrix(); + obj.matrixWorldNeedsUpdate = true; + + } + + } + +}; + +THREE.KeyFrameAnimation.prototype = { + + constructor: THREE.KeyFrameAnimation, + + play: function ( startTime ) { + + this.currentTime = startTime !== undefined ? startTime : 0; + + if ( this.isPlaying === false ) { + + this.isPlaying = true; + + // reset key cache + + var h, hl = this.hierarchy.length, + object, + node; + + for ( h = 0; h < hl; h ++ ) { + + object = this.hierarchy[ h ]; + node = this.data.hierarchy[ h ]; + + if ( node.animationCache === undefined ) { + + node.animationCache = {}; + node.animationCache.prevKey = null; + node.animationCache.nextKey = null; + node.animationCache.originalMatrix = object.matrix; + + } + + var keys = this.data.hierarchy[ h ].keys; + + if ( keys.length ) { + + node.animationCache.prevKey = keys[ 0 ]; + node.animationCache.nextKey = keys[ 1 ]; + + this.startTime = Math.min( keys[ 0 ].time, this.startTime ); + this.endTime = Math.max( keys[ keys.length - 1 ].time, this.endTime ); + + } + + } + + this.update( 0 ); + + } + + this.isPaused = false; + + THREE.AnimationHandler.play( this ); + + }, + + stop: function () { + + this.isPlaying = false; + this.isPaused = false; + + THREE.AnimationHandler.stop( this ); + + // reset JIT matrix and remove cache + + for ( var h = 0; h < this.data.hierarchy.length; h ++ ) { + + var obj = this.hierarchy[ h ]; + var node = this.data.hierarchy[ h ]; + + if ( node.animationCache !== undefined ) { + + var original = node.animationCache.originalMatrix; + + original.copy( obj.matrix ); + obj.matrix = original; + + delete node.animationCache; + + } + + } + + }, + + update: function ( delta ) { + + if ( this.isPlaying === false ) return; + + this.currentTime += delta * this.timeScale; + + // + + var duration = this.data.length; + + if ( this.loop === true && this.currentTime > duration ) { + + this.currentTime %= duration; + + } + + this.currentTime = Math.min( this.currentTime, duration ); + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + var object = this.hierarchy[ h ]; + var node = this.data.hierarchy[ h ]; + + var keys = node.keys, + animationCache = node.animationCache; + + + if ( keys.length ) { + + var prevKey = animationCache.prevKey; + var nextKey = animationCache.nextKey; + + if ( nextKey.time <= this.currentTime ) { + + while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { + + prevKey = nextKey; + nextKey = keys[ prevKey.index + 1 ]; + + } + + animationCache.prevKey = prevKey; + animationCache.nextKey = nextKey; + + } + + if ( nextKey.time >= this.currentTime ) { + + prevKey.interpolate( nextKey, this.currentTime ); + + } else { + + prevKey.interpolate( nextKey, nextKey.time ); + + } + + this.data.hierarchy[ h ].node.updateMatrix(); + object.matrixWorldNeedsUpdate = true; + + } + + } + + }, + + getNextKeyWith: function ( sid, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + key = key % keys.length; + + for ( ; key < keys.length; key ++ ) { + + if ( keys[ key ].hasTarget( sid ) ) { + + return keys[ key ]; + + } + + } + + return keys[ 0 ]; + + }, + + getPrevKeyWith: function ( sid, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + key = key >= 0 ? key : key + keys.length; + + for ( ; key >= 0; key -- ) { + + if ( keys[ key ].hasTarget( sid ) ) { + + return keys[ key ]; + + } + + } + + return keys[ keys.length - 1 ]; + + } + +}; diff --git a/node_modules/three/examples/js/loaders/ctm/CTMLoader.js b/node_modules/three/examples/js/loaders/ctm/CTMLoader.js new file mode 100644 index 00000000..587d1d5f --- /dev/null +++ b/node_modules/three/examples/js/loaders/ctm/CTMLoader.js @@ -0,0 +1,276 @@ +/** + * Loader for CTM encoded models generated by OpenCTM tools: + * http://openctm.sourceforge.net/ + * + * Uses js-openctm library by Juan Mellado + * http://code.google.com/p/js-openctm/ + * + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.CTMLoader = function () { + + THREE.Loader.call( this ); + + // Deprecated + + Object.defineProperties( this, { + statusDomElement: { + get: function () { + + if ( this._statusDomElement === undefined ) { + + this._statusDomElement = document.createElement( 'div' ); + + } + + console.warn( 'THREE.BinaryLoader: .statusDomElement has been removed.' ); + return this._statusDomElement; + + } + }, + } ); + +}; + +THREE.CTMLoader.prototype = Object.create( THREE.Loader.prototype ); +THREE.CTMLoader.prototype.constructor = THREE.CTMLoader; + +// Load multiple CTM parts defined in JSON + +THREE.CTMLoader.prototype.loadParts = function( url, callback, parameters ) { + + parameters = parameters || {}; + + var scope = this; + + var xhr = new XMLHttpRequest(); + + var basePath = parameters.basePath ? parameters.basePath : this.extractUrlBase( url ); + + xhr.onreadystatechange = function() { + + if ( xhr.readyState === 4 ) { + + if ( xhr.status === 200 || xhr.status === 0 ) { + + var jsonObject = JSON.parse( xhr.responseText ); + + var materials = [], geometries = [], counter = 0; + + function callbackFinal( geometry ) { + + counter += 1; + + geometries.push( geometry ); + + if ( counter === jsonObject.offsets.length ) { + + callback( geometries, materials ); + + } + + } + + + // init materials + + for ( var i = 0; i < jsonObject.materials.length; i ++ ) { + + materials[ i ] = scope.createMaterial( jsonObject.materials[ i ], basePath ); + + } + + // load joined CTM file + + var partUrl = basePath + jsonObject.data; + var parametersPart = { useWorker: parameters.useWorker, offsets: jsonObject.offsets }; + scope.load( partUrl, callbackFinal, parametersPart ); + + } + + } + + }; + + xhr.open( "GET", url, true ); + xhr.setRequestHeader( "Content-Type", "text/plain" ); + xhr.send( null ); + +}; + +// Load CTMLoader compressed models +// - parameters +// - url (required) +// - callback (required) + +THREE.CTMLoader.prototype.load = function( url, callback, parameters ) { + + parameters = parameters || {}; + + var scope = this; + + var offsets = parameters.offsets !== undefined ? parameters.offsets : [ 0 ]; + + var xhr = new XMLHttpRequest(), + callbackProgress = null; + + var length = 0; + + xhr.onreadystatechange = function() { + + if ( xhr.readyState === 4 ) { + + if ( xhr.status === 200 || xhr.status === 0 ) { + + var binaryData = new Uint8Array(xhr.response); + + var s = Date.now(); + + if ( parameters.useWorker ) { + + var worker = parameters.worker || new Worker( "js/loaders/ctm/CTMWorker.js" ); + + worker.onmessage = function( event ) { + + var files = event.data; + + for ( var i = 0; i < files.length; i ++ ) { + + var ctmFile = files[ i ]; + + var e1 = Date.now(); + // console.log( "CTM data parse time [worker]: " + (e1-s) + " ms" ); + + scope.createModel( ctmFile, callback ); + + var e = Date.now(); + console.log( "model load time [worker]: " + (e - e1) + " ms, total: " + (e - s)); + + } + + + }; + + worker.postMessage( { "data": binaryData, "offsets": offsets } ); + + } else { + + for ( var i = 0; i < offsets.length; i ++ ) { + + var stream = new CTM.Stream( binaryData ); + stream.offset = offsets[ i ]; + + var ctmFile = new CTM.File( stream ); + + scope.createModel( ctmFile, callback ); + + } + + //var e = Date.now(); + //console.log( "CTM data parse time [inline]: " + (e-s) + " ms" ); + + } + + } else { + + console.error( "Couldn't load [" + url + "] [" + xhr.status + "]" ); + + } + + } else if ( xhr.readyState === 3 ) { + + if ( callbackProgress ) { + + if ( length === 0 ) { + + length = xhr.getResponseHeader( "Content-Length" ); + + } + + callbackProgress( { total: length, loaded: xhr.responseText.length } ); + + } + + } else if ( xhr.readyState === 2 ) { + + length = xhr.getResponseHeader( "Content-Length" ); + + } + + }; + + xhr.open( "GET", url, true ); + xhr.responseType = "arraybuffer"; + + xhr.send( null ); + +}; + + +THREE.CTMLoader.prototype.createModel = function ( file, callback ) { + + var Model = function () { + + THREE.BufferGeometry.call( this ); + + this.materials = []; + + var indices = file.body.indices, + positions = file.body.vertices, + normals = file.body.normals; + + var uvs, colors; + + var uvMaps = file.body.uvMaps; + + if ( uvMaps !== undefined && uvMaps.length > 0 ) { + + uvs = uvMaps[ 0 ].uv; + + } + + var attrMaps = file.body.attrMaps; + + if ( attrMaps !== undefined && attrMaps.length > 0 && attrMaps[ 0 ].name === 'Color' ) { + + colors = attrMaps[ 0 ].attr; + + } + + this.setIndex( new THREE.BufferAttribute( indices, 1 ) ); + this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + + if ( normals !== undefined ) { + + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + + } + + if ( uvs !== undefined ) { + + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + + } + + if ( colors !== undefined ) { + + this.addAttribute( 'color', new THREE.BufferAttribute( colors, 4 ) ); + + } + + }; + + Model.prototype = Object.create( THREE.BufferGeometry.prototype ); + Model.prototype.constructor = Model; + + var geometry = new Model(); + + // compute vertex normals if not present in the CTM model + if ( geometry.attributes.normal === undefined ) { + geometry.computeVertexNormals(); + } + + callback( geometry ); + +}; diff --git a/node_modules/three/examples/js/loaders/ctm/CTMWorker.js b/node_modules/three/examples/js/loaders/ctm/CTMWorker.js new file mode 100644 index 00000000..f1cdb149 --- /dev/null +++ b/node_modules/three/examples/js/loaders/ctm/CTMWorker.js @@ -0,0 +1,19 @@ +importScripts( "lzma.js", "ctm.js" ); + +self.onmessage = function( event ) { + + var files = []; + + for ( var i = 0; i < event.data.offsets.length; i ++ ) { + + var stream = new CTM.Stream( event.data.data ); + stream.offset = event.data.offsets[ i ]; + + files[ i ] = new CTM.File( stream ); + + } + + self.postMessage( files ); + self.close(); + +}; diff --git a/node_modules/three/examples/js/loaders/ctm/ctm.js b/node_modules/three/examples/js/loaders/ctm/ctm.js new file mode 100644 index 00000000..b4a805de --- /dev/null +++ b/node_modules/three/examples/js/loaders/ctm/ctm.js @@ -0,0 +1,661 @@ +/* +Copyright (c) 2011 Juan Mellado + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +/* +References: +- "OpenCTM: The Open Compressed Triangle Mesh file format" by Marcus Geelnard + http://openctm.sourceforge.net/ +*/ + +var CTM = CTM || {}; + +// browserify support +if ( typeof module === 'object' ) { + + module.exports = CTM; + +} + +CTM.CompressionMethod = { + RAW: 0x00574152, + MG1: 0x0031474d, + MG2: 0x0032474d +}; + +CTM.Flags = { + NORMALS: 0x00000001 +}; + +CTM.File = function(stream) { + this.load(stream); +}; + +CTM.File.prototype.load = function(stream) { + this.header = new CTM.FileHeader(stream); + + this.body = new CTM.FileBody(this.header); + + this.getReader().read(stream, this.body); +}; + +CTM.File.prototype.getReader = function() { + var reader; + + switch (this.header.compressionMethod){ + case CTM.CompressionMethod.RAW: + reader = new CTM.ReaderRAW(); + break; + case CTM.CompressionMethod.MG1: + reader = new CTM.ReaderMG1(); + break; + case CTM.CompressionMethod.MG2: + reader = new CTM.ReaderMG2(); + break; + } + + return reader; +}; + +CTM.FileHeader = function(stream) { + stream.readInt32(); //magic "OCTM" + this.fileFormat = stream.readInt32(); + this.compressionMethod = stream.readInt32(); + this.vertexCount = stream.readInt32(); + this.triangleCount = stream.readInt32(); + this.uvMapCount = stream.readInt32(); + this.attrMapCount = stream.readInt32(); + this.flags = stream.readInt32(); + this.comment = stream.readString(); +}; + +CTM.FileHeader.prototype.hasNormals = function() { + return this.flags & CTM.Flags.NORMALS; +}; + +CTM.FileBody = function(header) { + var i = header.triangleCount * 3, + v = header.vertexCount * 3, + n = header.hasNormals() ? header.vertexCount * 3 : 0, + u = header.vertexCount * 2, + a = header.vertexCount * 4, + j = 0; + + var data = new ArrayBuffer( + (i + v + n + (u * header.uvMapCount) + (a * header.attrMapCount) ) * 4); + + this.indices = new Uint32Array(data, 0, i); + + this.vertices = new Float32Array(data, i * 4, v); + + if ( header.hasNormals() ) { + this.normals = new Float32Array(data, (i + v) * 4, n); + } + + if (header.uvMapCount) { + this.uvMaps = []; + for (j = 0; j < header.uvMapCount; ++ j) { + this.uvMaps[j] = { uv: new Float32Array(data, + (i + v + n + (j * u) ) * 4, u) }; + } + } + + if (header.attrMapCount) { + this.attrMaps = []; + for (j = 0; j < header.attrMapCount; ++ j) { + this.attrMaps[j] = { attr: new Float32Array(data, + (i + v + n + (u * header.uvMapCount) + (j * a) ) * 4, a) }; + } + } +}; + +CTM.FileMG2Header = function(stream) { + stream.readInt32(); //magic "MG2H" + this.vertexPrecision = stream.readFloat32(); + this.normalPrecision = stream.readFloat32(); + this.lowerBoundx = stream.readFloat32(); + this.lowerBoundy = stream.readFloat32(); + this.lowerBoundz = stream.readFloat32(); + this.higherBoundx = stream.readFloat32(); + this.higherBoundy = stream.readFloat32(); + this.higherBoundz = stream.readFloat32(); + this.divx = stream.readInt32(); + this.divy = stream.readInt32(); + this.divz = stream.readInt32(); + + this.sizex = (this.higherBoundx - this.lowerBoundx) / this.divx; + this.sizey = (this.higherBoundy - this.lowerBoundy) / this.divy; + this.sizez = (this.higherBoundz - this.lowerBoundz) / this.divz; +}; + +CTM.ReaderRAW = function() { +}; + +CTM.ReaderRAW.prototype.read = function(stream, body) { + this.readIndices(stream, body.indices); + this.readVertices(stream, body.vertices); + + if (body.normals) { + this.readNormals(stream, body.normals); + } + if (body.uvMaps) { + this.readUVMaps(stream, body.uvMaps); + } + if (body.attrMaps) { + this.readAttrMaps(stream, body.attrMaps); + } +}; + +CTM.ReaderRAW.prototype.readIndices = function(stream, indices) { + stream.readInt32(); //magic "INDX" + stream.readArrayInt32(indices); +}; + +CTM.ReaderRAW.prototype.readVertices = function(stream, vertices) { + stream.readInt32(); //magic "VERT" + stream.readArrayFloat32(vertices); +}; + +CTM.ReaderRAW.prototype.readNormals = function(stream, normals) { + stream.readInt32(); //magic "NORM" + stream.readArrayFloat32(normals); +}; + +CTM.ReaderRAW.prototype.readUVMaps = function(stream, uvMaps) { + var i = 0; + for (; i < uvMaps.length; ++ i) { + stream.readInt32(); //magic "TEXC" + + uvMaps[i].name = stream.readString(); + uvMaps[i].filename = stream.readString(); + stream.readArrayFloat32(uvMaps[i].uv); + } +}; + +CTM.ReaderRAW.prototype.readAttrMaps = function(stream, attrMaps) { + var i = 0; + for (; i < attrMaps.length; ++ i) { + stream.readInt32(); //magic "ATTR" + + attrMaps[i].name = stream.readString(); + stream.readArrayFloat32(attrMaps[i].attr); + } +}; + +CTM.ReaderMG1 = function() { +}; + +CTM.ReaderMG1.prototype.read = function(stream, body) { + this.readIndices(stream, body.indices); + this.readVertices(stream, body.vertices); + + if (body.normals) { + this.readNormals(stream, body.normals); + } + if (body.uvMaps) { + this.readUVMaps(stream, body.uvMaps); + } + if (body.attrMaps) { + this.readAttrMaps(stream, body.attrMaps); + } +}; + +CTM.ReaderMG1.prototype.readIndices = function(stream, indices) { + stream.readInt32(); //magic "INDX" + stream.readInt32(); //packed size + + var interleaved = new CTM.InterleavedStream(indices, 3); + LZMA.decompress(stream, stream, interleaved, interleaved.data.length); + + CTM.restoreIndices(indices, indices.length); +}; + +CTM.ReaderMG1.prototype.readVertices = function(stream, vertices) { + stream.readInt32(); //magic "VERT" + stream.readInt32(); //packed size + + var interleaved = new CTM.InterleavedStream(vertices, 1); + LZMA.decompress(stream, stream, interleaved, interleaved.data.length); +}; + +CTM.ReaderMG1.prototype.readNormals = function(stream, normals) { + stream.readInt32(); //magic "NORM" + stream.readInt32(); //packed size + + var interleaved = new CTM.InterleavedStream(normals, 3); + LZMA.decompress(stream, stream, interleaved, interleaved.data.length); +}; + +CTM.ReaderMG1.prototype.readUVMaps = function(stream, uvMaps) { + var i = 0; + for (; i < uvMaps.length; ++ i) { + stream.readInt32(); //magic "TEXC" + + uvMaps[i].name = stream.readString(); + uvMaps[i].filename = stream.readString(); + + stream.readInt32(); //packed size + + var interleaved = new CTM.InterleavedStream(uvMaps[i].uv, 2); + LZMA.decompress(stream, stream, interleaved, interleaved.data.length); + } +}; + +CTM.ReaderMG1.prototype.readAttrMaps = function(stream, attrMaps) { + var i = 0; + for (; i < attrMaps.length; ++ i) { + stream.readInt32(); //magic "ATTR" + + attrMaps[i].name = stream.readString(); + + stream.readInt32(); //packed size + + var interleaved = new CTM.InterleavedStream(attrMaps[i].attr, 4); + LZMA.decompress(stream, stream, interleaved, interleaved.data.length); + } +}; + +CTM.ReaderMG2 = function() { +}; + +CTM.ReaderMG2.prototype.read = function(stream, body) { + this.MG2Header = new CTM.FileMG2Header(stream); + + this.readVertices(stream, body.vertices); + this.readIndices(stream, body.indices); + + if (body.normals) { + this.readNormals(stream, body); + } + if (body.uvMaps) { + this.readUVMaps(stream, body.uvMaps); + } + if (body.attrMaps) { + this.readAttrMaps(stream, body.attrMaps); + } +}; + +CTM.ReaderMG2.prototype.readVertices = function(stream, vertices) { + stream.readInt32(); //magic "VERT" + stream.readInt32(); //packed size + + var interleaved = new CTM.InterleavedStream(vertices, 3); + LZMA.decompress(stream, stream, interleaved, interleaved.data.length); + + var gridIndices = this.readGridIndices(stream, vertices); + + CTM.restoreVertices(vertices, this.MG2Header, gridIndices, this.MG2Header.vertexPrecision); +}; + +CTM.ReaderMG2.prototype.readGridIndices = function(stream, vertices) { + stream.readInt32(); //magic "GIDX" + stream.readInt32(); //packed size + + var gridIndices = new Uint32Array(vertices.length / 3); + + var interleaved = new CTM.InterleavedStream(gridIndices, 1); + LZMA.decompress(stream, stream, interleaved, interleaved.data.length); + + CTM.restoreGridIndices(gridIndices, gridIndices.length); + + return gridIndices; +}; + +CTM.ReaderMG2.prototype.readIndices = function(stream, indices) { + stream.readInt32(); //magic "INDX" + stream.readInt32(); //packed size + + var interleaved = new CTM.InterleavedStream(indices, 3); + LZMA.decompress(stream, stream, interleaved, interleaved.data.length); + + CTM.restoreIndices(indices, indices.length); +}; + +CTM.ReaderMG2.prototype.readNormals = function(stream, body) { + stream.readInt32(); //magic "NORM" + stream.readInt32(); //packed size + + var interleaved = new CTM.InterleavedStream(body.normals, 3); + LZMA.decompress(stream, stream, interleaved, interleaved.data.length); + + var smooth = CTM.calcSmoothNormals(body.indices, body.vertices); + + CTM.restoreNormals(body.normals, smooth, this.MG2Header.normalPrecision); +}; + +CTM.ReaderMG2.prototype.readUVMaps = function(stream, uvMaps) { + var i = 0; + for (; i < uvMaps.length; ++ i) { + stream.readInt32(); //magic "TEXC" + + uvMaps[i].name = stream.readString(); + uvMaps[i].filename = stream.readString(); + + var precision = stream.readFloat32(); + + stream.readInt32(); //packed size + + var interleaved = new CTM.InterleavedStream(uvMaps[i].uv, 2); + LZMA.decompress(stream, stream, interleaved, interleaved.data.length); + + CTM.restoreMap(uvMaps[i].uv, 2, precision); + } +}; + +CTM.ReaderMG2.prototype.readAttrMaps = function(stream, attrMaps) { + var i = 0; + for (; i < attrMaps.length; ++ i) { + stream.readInt32(); //magic "ATTR" + + attrMaps[i].name = stream.readString(); + + var precision = stream.readFloat32(); + + stream.readInt32(); //packed size + + var interleaved = new CTM.InterleavedStream(attrMaps[i].attr, 4); + LZMA.decompress(stream, stream, interleaved, interleaved.data.length); + + CTM.restoreMap(attrMaps[i].attr, 4, precision); + } +}; + +CTM.restoreIndices = function(indices, len) { + var i = 3; + if (len > 0) { + indices[2] += indices[0]; + indices[1] += indices[0]; + } + for (; i < len; i += 3) { + indices[i] += indices[i - 3]; + + if (indices[i] === indices[i - 3]) { + indices[i + 1] += indices[i - 2]; + }else { + indices[i + 1] += indices[i]; + } + + indices[i + 2] += indices[i]; + } +}; + +CTM.restoreGridIndices = function(gridIndices, len) { + var i = 1; + for (; i < len; ++ i) { + gridIndices[i] += gridIndices[i - 1]; + } +}; + +CTM.restoreVertices = function(vertices, grid, gridIndices, precision) { + var gridIdx, delta, x, y, z, + intVertices = new Uint32Array(vertices.buffer, vertices.byteOffset, vertices.length), + ydiv = grid.divx, zdiv = ydiv * grid.divy, + prevGridIdx = 0x7fffffff, prevDelta = 0, + i = 0, j = 0, len = gridIndices.length; + + for (; i < len; j += 3) { + x = gridIdx = gridIndices[i ++]; + + z = ~~(x / zdiv); + x -= ~~(z * zdiv); + y = ~~(x / ydiv); + x -= ~~(y * ydiv); + + delta = intVertices[j]; + if (gridIdx === prevGridIdx) { + delta += prevDelta; + } + + vertices[j] = grid.lowerBoundx + + x * grid.sizex + precision * delta; + vertices[j + 1] = grid.lowerBoundy + + y * grid.sizey + precision * intVertices[j + 1]; + vertices[j + 2] = grid.lowerBoundz + + z * grid.sizez + precision * intVertices[j + 2]; + + prevGridIdx = gridIdx; + prevDelta = delta; + } +}; + +CTM.restoreNormals = function(normals, smooth, precision) { + var ro, phi, theta, sinPhi, + nx, ny, nz, by, bz, len, + intNormals = new Uint32Array(normals.buffer, normals.byteOffset, normals.length), + i = 0, k = normals.length, + PI_DIV_2 = 3.141592653589793238462643 * 0.5; + + for (; i < k; i += 3) { + ro = intNormals[i] * precision; + phi = intNormals[i + 1]; + + if (phi === 0) { + normals[i] = smooth[i] * ro; + normals[i + 1] = smooth[i + 1] * ro; + normals[i + 2] = smooth[i + 2] * ro; + }else { + + if (phi <= 4) { + theta = (intNormals[i + 2] - 2) * PI_DIV_2; + }else { + theta = ( (intNormals[i + 2] * 4 / phi) - 2) * PI_DIV_2; + } + + phi *= precision * PI_DIV_2; + sinPhi = ro * Math.sin(phi); + + nx = sinPhi * Math.cos(theta); + ny = sinPhi * Math.sin(theta); + nz = ro * Math.cos(phi); + + bz = smooth[i + 1]; + by = smooth[i] - smooth[i + 2]; + + len = Math.sqrt(2 * bz * bz + by * by); + if (len > 1e-20) { + by /= len; + bz /= len; + } + + normals[i] = smooth[i] * nz + + (smooth[i + 1] * bz - smooth[i + 2] * by) * ny - bz * nx; + normals[i + 1] = smooth[i + 1] * nz - + (smooth[i + 2] + smooth[i] ) * bz * ny + by * nx; + normals[i + 2] = smooth[i + 2] * nz + + (smooth[i] * by + smooth[i + 1] * bz) * ny + bz * nx; + } + } +}; + +CTM.restoreMap = function(map, count, precision) { + var delta, value, + intMap = new Uint32Array(map.buffer, map.byteOffset, map.length), + i = 0, j, len = map.length; + + for (; i < count; ++ i) { + delta = 0; + + for (j = i; j < len; j += count) { + value = intMap[j]; + + delta += value & 1 ? -( (value + 1) >> 1) : value >> 1; + + map[j] = delta * precision; + } + } +}; + +CTM.calcSmoothNormals = function(indices, vertices) { + var smooth = new Float32Array(vertices.length), + indx, indy, indz, nx, ny, nz, + v1x, v1y, v1z, v2x, v2y, v2z, len, + i, k; + + for (i = 0, k = indices.length; i < k;) { + indx = indices[i ++] * 3; + indy = indices[i ++] * 3; + indz = indices[i ++] * 3; + + v1x = vertices[indy] - vertices[indx]; + v2x = vertices[indz] - vertices[indx]; + v1y = vertices[indy + 1] - vertices[indx + 1]; + v2y = vertices[indz + 1] - vertices[indx + 1]; + v1z = vertices[indy + 2] - vertices[indx + 2]; + v2z = vertices[indz + 2] - vertices[indx + 2]; + + nx = v1y * v2z - v1z * v2y; + ny = v1z * v2x - v1x * v2z; + nz = v1x * v2y - v1y * v2x; + + len = Math.sqrt(nx * nx + ny * ny + nz * nz); + if (len > 1e-10) { + nx /= len; + ny /= len; + nz /= len; + } + + smooth[indx] += nx; + smooth[indx + 1] += ny; + smooth[indx + 2] += nz; + smooth[indy] += nx; + smooth[indy + 1] += ny; + smooth[indy + 2] += nz; + smooth[indz] += nx; + smooth[indz + 1] += ny; + smooth[indz + 2] += nz; + } + + for (i = 0, k = smooth.length; i < k; i += 3) { + len = Math.sqrt(smooth[i] * smooth[i] + + smooth[i + 1] * smooth[i + 1] + + smooth[i + 2] * smooth[i + 2]); + + if (len > 1e-10) { + smooth[i] /= len; + smooth[i + 1] /= len; + smooth[i + 2] /= len; + } + } + + return smooth; +}; + +CTM.isLittleEndian = (function() { + var buffer = new ArrayBuffer(2), + bytes = new Uint8Array(buffer), + ints = new Uint16Array(buffer); + + bytes[0] = 1; + + return ints[0] === 1; +}()); + +CTM.InterleavedStream = function(data, count) { + this.data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength); + this.offset = CTM.isLittleEndian ? 3 : 0; + this.count = count * 4; + this.len = this.data.length; +}; + +CTM.InterleavedStream.prototype.writeByte = function(value) { + this.data[this.offset] = value; + + this.offset += this.count; + if (this.offset >= this.len) { + + this.offset -= this.len - 4; + if (this.offset >= this.count) { + + this.offset -= this.count + (CTM.isLittleEndian ? 1 : -1); + } + } +}; + +CTM.Stream = function(data) { + this.data = data; + this.offset = 0; +}; + +CTM.Stream.prototype.TWO_POW_MINUS23 = Math.pow(2, -23); + +CTM.Stream.prototype.TWO_POW_MINUS126 = Math.pow(2, -126); + +CTM.Stream.prototype.readByte = function() { + return this.data[this.offset ++] & 0xff; +}; + +CTM.Stream.prototype.readInt32 = function() { + var i = this.readByte(); + i |= this.readByte() << 8; + i |= this.readByte() << 16; + return i | (this.readByte() << 24); +}; + +CTM.Stream.prototype.readFloat32 = function() { + var m = this.readByte(); + m += this.readByte() << 8; + + var b1 = this.readByte(); + var b2 = this.readByte(); + + m += (b1 & 0x7f) << 16; + var e = ( (b2 & 0x7f) << 1) | ( (b1 & 0x80) >>> 7); + var s = b2 & 0x80 ? -1 : 1; + + if (e === 255) { + return m !== 0 ? NaN : s * Infinity; + } + if (e > 0) { + return s * (1 + (m * this.TWO_POW_MINUS23) ) * Math.pow(2, e - 127); + } + if (m !== 0) { + return s * m * this.TWO_POW_MINUS126; + } + return s * 0; +}; + +CTM.Stream.prototype.readString = function() { + var len = this.readInt32(); + + this.offset += len; + + return String.fromCharCode.apply(null, this.data.subarray(this.offset - len, this.offset)); +}; + +CTM.Stream.prototype.readArrayInt32 = function(array) { + var i = 0, len = array.length; + + while (i < len) { + array[i ++] = this.readInt32(); + } + + return array; +}; + +CTM.Stream.prototype.readArrayFloat32 = function(array) { + var i = 0, len = array.length; + + while (i < len) { + array[i ++] = this.readFloat32(); + } + + return array; +}; diff --git a/node_modules/three/examples/js/loaders/ctm/license/OpenCTM.txt b/node_modules/three/examples/js/loaders/ctm/license/OpenCTM.txt new file mode 100644 index 00000000..7a2d90b3 --- /dev/null +++ b/node_modules/three/examples/js/loaders/ctm/license/OpenCTM.txt @@ -0,0 +1,20 @@ +Copyright (c) 2009-2010 Marcus Geelnard + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. diff --git a/node_modules/three/examples/js/loaders/ctm/license/js-lzma.txt b/node_modules/three/examples/js/loaders/ctm/license/js-lzma.txt new file mode 100644 index 00000000..6b878a7c --- /dev/null +++ b/node_modules/three/examples/js/loaders/ctm/license/js-lzma.txt @@ -0,0 +1,19 @@ +Copyright (c) 2011 Juan Mellado + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/three/examples/js/loaders/ctm/license/js-openctm.txt b/node_modules/three/examples/js/loaders/ctm/license/js-openctm.txt new file mode 100644 index 00000000..6b878a7c --- /dev/null +++ b/node_modules/three/examples/js/loaders/ctm/license/js-openctm.txt @@ -0,0 +1,19 @@ +Copyright (c) 2011 Juan Mellado + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/three/examples/js/loaders/ctm/lzma.js b/node_modules/three/examples/js/loaders/ctm/lzma.js new file mode 100644 index 00000000..3d60b355 --- /dev/null +++ b/node_modules/three/examples/js/loaders/ctm/lzma.js @@ -0,0 +1,517 @@ + +var LZMA = LZMA || {}; + +// browserify support +if ( typeof module === 'object' ) { + + module.exports = LZMA; + +} + +LZMA.OutWindow = function() { + this._windowSize = 0; +}; + +LZMA.OutWindow.prototype.create = function(windowSize) { + if ( (!this._buffer) || (this._windowSize !== windowSize) ) { + this._buffer = []; + } + this._windowSize = windowSize; + this._pos = 0; + this._streamPos = 0; +}; + +LZMA.OutWindow.prototype.flush = function() { + var size = this._pos - this._streamPos; + if (size !== 0) { + while (size --) { + this._stream.writeByte(this._buffer[this._streamPos ++]); + } + if (this._pos >= this._windowSize) { + this._pos = 0; + } + this._streamPos = this._pos; + } +}; + +LZMA.OutWindow.prototype.releaseStream = function() { + this.flush(); + this._stream = null; +}; + +LZMA.OutWindow.prototype.setStream = function(stream) { + this.releaseStream(); + this._stream = stream; +}; + +LZMA.OutWindow.prototype.init = function(solid) { + if (!solid) { + this._streamPos = 0; + this._pos = 0; + } +}; + +LZMA.OutWindow.prototype.copyBlock = function(distance, len) { + var pos = this._pos - distance - 1; + if (pos < 0) { + pos += this._windowSize; + } + while (len --) { + if (pos >= this._windowSize) { + pos = 0; + } + this._buffer[this._pos ++] = this._buffer[pos ++]; + if (this._pos >= this._windowSize) { + this.flush(); + } + } +}; + +LZMA.OutWindow.prototype.putByte = function(b) { + this._buffer[this._pos ++] = b; + if (this._pos >= this._windowSize) { + this.flush(); + } +}; + +LZMA.OutWindow.prototype.getByte = function(distance) { + var pos = this._pos - distance - 1; + if (pos < 0) { + pos += this._windowSize; + } + return this._buffer[pos]; +}; + +LZMA.RangeDecoder = function() { +}; + +LZMA.RangeDecoder.prototype.setStream = function(stream) { + this._stream = stream; +}; + +LZMA.RangeDecoder.prototype.releaseStream = function() { + this._stream = null; +}; + +LZMA.RangeDecoder.prototype.init = function() { + var i = 5; + + this._code = 0; + this._range = -1; + + while (i --) { + this._code = (this._code << 8) | this._stream.readByte(); + } +}; + +LZMA.RangeDecoder.prototype.decodeDirectBits = function(numTotalBits) { + var result = 0, i = numTotalBits, t; + + while (i --) { + this._range >>>= 1; + t = (this._code - this._range) >>> 31; + this._code -= this._range & (t - 1); + result = (result << 1) | (1 - t); + + if ( (this._range & 0xff000000) === 0) { + this._code = (this._code << 8) | this._stream.readByte(); + this._range <<= 8; + } + } + + return result; +}; + +LZMA.RangeDecoder.prototype.decodeBit = function(probs, index) { + var prob = probs[index], + newBound = (this._range >>> 11) * prob; + + if ( (this._code ^ 0x80000000) < (newBound ^ 0x80000000) ) { + this._range = newBound; + probs[index] += (2048 - prob) >>> 5; + if ( (this._range & 0xff000000) === 0) { + this._code = (this._code << 8) | this._stream.readByte(); + this._range <<= 8; + } + return 0; + } + + this._range -= newBound; + this._code -= newBound; + probs[index] -= prob >>> 5; + if ( (this._range & 0xff000000) === 0) { + this._code = (this._code << 8) | this._stream.readByte(); + this._range <<= 8; + } + return 1; +}; + +LZMA.initBitModels = function(probs, len) { + while (len --) { + probs[len] = 1024; + } +}; + +LZMA.BitTreeDecoder = function(numBitLevels) { + this._models = []; + this._numBitLevels = numBitLevels; +}; + +LZMA.BitTreeDecoder.prototype.init = function() { + LZMA.initBitModels(this._models, 1 << this._numBitLevels); +}; + +LZMA.BitTreeDecoder.prototype.decode = function(rangeDecoder) { + var m = 1, i = this._numBitLevels; + + while (i --) { + m = (m << 1) | rangeDecoder.decodeBit(this._models, m); + } + return m - (1 << this._numBitLevels); +}; + +LZMA.BitTreeDecoder.prototype.reverseDecode = function(rangeDecoder) { + var m = 1, symbol = 0, i = 0, bit; + + for (; i < this._numBitLevels; ++ i) { + bit = rangeDecoder.decodeBit(this._models, m); + m = (m << 1) | bit; + symbol |= bit << i; + } + return symbol; +}; + +LZMA.reverseDecode2 = function(models, startIndex, rangeDecoder, numBitLevels) { + var m = 1, symbol = 0, i = 0, bit; + + for (; i < numBitLevels; ++ i) { + bit = rangeDecoder.decodeBit(models, startIndex + m); + m = (m << 1) | bit; + symbol |= bit << i; + } + return symbol; +}; + +LZMA.LenDecoder = function() { + this._choice = []; + this._lowCoder = []; + this._midCoder = []; + this._highCoder = new LZMA.BitTreeDecoder(8); + this._numPosStates = 0; +}; + +LZMA.LenDecoder.prototype.create = function(numPosStates) { + for (; this._numPosStates < numPosStates; ++ this._numPosStates) { + this._lowCoder[this._numPosStates] = new LZMA.BitTreeDecoder(3); + this._midCoder[this._numPosStates] = new LZMA.BitTreeDecoder(3); + } +}; + +LZMA.LenDecoder.prototype.init = function() { + var i = this._numPosStates; + LZMA.initBitModels(this._choice, 2); + while (i --) { + this._lowCoder[i].init(); + this._midCoder[i].init(); + } + this._highCoder.init(); +}; + +LZMA.LenDecoder.prototype.decode = function(rangeDecoder, posState) { + if (rangeDecoder.decodeBit(this._choice, 0) === 0) { + return this._lowCoder[posState].decode(rangeDecoder); + } + if (rangeDecoder.decodeBit(this._choice, 1) === 0) { + return 8 + this._midCoder[posState].decode(rangeDecoder); + } + return 16 + this._highCoder.decode(rangeDecoder); +}; + +LZMA.Decoder2 = function() { + this._decoders = []; +}; + +LZMA.Decoder2.prototype.init = function() { + LZMA.initBitModels(this._decoders, 0x300); +}; + +LZMA.Decoder2.prototype.decodeNormal = function(rangeDecoder) { + var symbol = 1; + + do { + symbol = (symbol << 1) | rangeDecoder.decodeBit(this._decoders, symbol); + }while (symbol < 0x100); + + return symbol & 0xff; +}; + +LZMA.Decoder2.prototype.decodeWithMatchByte = function(rangeDecoder, matchByte) { + var symbol = 1, matchBit, bit; + + do { + matchBit = (matchByte >> 7) & 1; + matchByte <<= 1; + bit = rangeDecoder.decodeBit(this._decoders, ( (1 + matchBit) << 8) + symbol); + symbol = (symbol << 1) | bit; + if (matchBit !== bit) { + while (symbol < 0x100) { + symbol = (symbol << 1) | rangeDecoder.decodeBit(this._decoders, symbol); + } + break; + } + }while (symbol < 0x100); + + return symbol & 0xff; +}; + +LZMA.LiteralDecoder = function() { +}; + +LZMA.LiteralDecoder.prototype.create = function(numPosBits, numPrevBits) { + var i; + + if (this._coders + && (this._numPrevBits === numPrevBits) + && (this._numPosBits === numPosBits) ) { + return; + } + this._numPosBits = numPosBits; + this._posMask = (1 << numPosBits) - 1; + this._numPrevBits = numPrevBits; + + this._coders = []; + + i = 1 << (this._numPrevBits + this._numPosBits); + while (i --) { + this._coders[i] = new LZMA.Decoder2(); + } +}; + +LZMA.LiteralDecoder.prototype.init = function() { + var i = 1 << (this._numPrevBits + this._numPosBits); + while (i --) { + this._coders[i].init(); + } +}; + +LZMA.LiteralDecoder.prototype.getDecoder = function(pos, prevByte) { + return this._coders[( (pos & this._posMask) << this._numPrevBits) + + ( (prevByte & 0xff) >>> (8 - this._numPrevBits) )]; +}; + +LZMA.Decoder = function() { + this._outWindow = new LZMA.OutWindow(); + this._rangeDecoder = new LZMA.RangeDecoder(); + this._isMatchDecoders = []; + this._isRepDecoders = []; + this._isRepG0Decoders = []; + this._isRepG1Decoders = []; + this._isRepG2Decoders = []; + this._isRep0LongDecoders = []; + this._posSlotDecoder = []; + this._posDecoders = []; + this._posAlignDecoder = new LZMA.BitTreeDecoder(4); + this._lenDecoder = new LZMA.LenDecoder(); + this._repLenDecoder = new LZMA.LenDecoder(); + this._literalDecoder = new LZMA.LiteralDecoder(); + this._dictionarySize = -1; + this._dictionarySizeCheck = -1; + + this._posSlotDecoder[0] = new LZMA.BitTreeDecoder(6); + this._posSlotDecoder[1] = new LZMA.BitTreeDecoder(6); + this._posSlotDecoder[2] = new LZMA.BitTreeDecoder(6); + this._posSlotDecoder[3] = new LZMA.BitTreeDecoder(6); +}; + +LZMA.Decoder.prototype.setDictionarySize = function(dictionarySize) { + if (dictionarySize < 0) { + return false; + } + if (this._dictionarySize !== dictionarySize) { + this._dictionarySize = dictionarySize; + this._dictionarySizeCheck = Math.max(this._dictionarySize, 1); + this._outWindow.create( Math.max(this._dictionarySizeCheck, 4096) ); + } + return true; +}; + +LZMA.Decoder.prototype.setLcLpPb = function(lc, lp, pb) { + var numPosStates = 1 << pb; + + if (lc > 8 || lp > 4 || pb > 4) { + return false; + } + + this._literalDecoder.create(lp, lc); + + this._lenDecoder.create(numPosStates); + this._repLenDecoder.create(numPosStates); + this._posStateMask = numPosStates - 1; + + return true; +}; + +LZMA.Decoder.prototype.init = function() { + var i = 4; + + this._outWindow.init(false); + + LZMA.initBitModels(this._isMatchDecoders, 192); + LZMA.initBitModels(this._isRep0LongDecoders, 192); + LZMA.initBitModels(this._isRepDecoders, 12); + LZMA.initBitModels(this._isRepG0Decoders, 12); + LZMA.initBitModels(this._isRepG1Decoders, 12); + LZMA.initBitModels(this._isRepG2Decoders, 12); + LZMA.initBitModels(this._posDecoders, 114); + + this._literalDecoder.init(); + + while (i --) { + this._posSlotDecoder[i].init(); + } + + this._lenDecoder.init(); + this._repLenDecoder.init(); + this._posAlignDecoder.init(); + this._rangeDecoder.init(); +}; + +LZMA.Decoder.prototype.decode = function(inStream, outStream, outSize) { + var state = 0, rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0, nowPos64 = 0, prevByte = 0, + posState, decoder2, len, distance, posSlot, numDirectBits; + + this._rangeDecoder.setStream(inStream); + this._outWindow.setStream(outStream); + + this.init(); + + while (outSize < 0 || nowPos64 < outSize) { + posState = nowPos64 & this._posStateMask; + + if (this._rangeDecoder.decodeBit(this._isMatchDecoders, (state << 4) + posState) === 0) { + decoder2 = this._literalDecoder.getDecoder(nowPos64 ++, prevByte); + + if (state >= 7) { + prevByte = decoder2.decodeWithMatchByte(this._rangeDecoder, this._outWindow.getByte(rep0) ); + }else { + prevByte = decoder2.decodeNormal(this._rangeDecoder); + } + this._outWindow.putByte(prevByte); + + state = state < 4 ? 0 : state - (state < 10 ? 3 : 6); + + }else { + + if (this._rangeDecoder.decodeBit(this._isRepDecoders, state) === 1) { + len = 0; + if (this._rangeDecoder.decodeBit(this._isRepG0Decoders, state) === 0) { + if (this._rangeDecoder.decodeBit(this._isRep0LongDecoders, (state << 4) + posState) === 0) { + state = state < 7 ? 9 : 11; + len = 1; + } + }else { + if (this._rangeDecoder.decodeBit(this._isRepG1Decoders, state) === 0) { + distance = rep1; + }else { + if (this._rangeDecoder.decodeBit(this._isRepG2Decoders, state) === 0) { + distance = rep2; + }else { + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + if (len === 0) { + len = 2 + this._repLenDecoder.decode(this._rangeDecoder, posState); + state = state < 7 ? 8 : 11; + } + }else { + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + + len = 2 + this._lenDecoder.decode(this._rangeDecoder, posState); + state = state < 7 ? 7 : 10; + + posSlot = this._posSlotDecoder[len <= 5 ? len - 2 : 3].decode(this._rangeDecoder); + if (posSlot >= 4) { + + numDirectBits = (posSlot >> 1) - 1; + rep0 = (2 | (posSlot & 1) ) << numDirectBits; + + if (posSlot < 14) { + rep0 += LZMA.reverseDecode2(this._posDecoders, + rep0 - posSlot - 1, this._rangeDecoder, numDirectBits); + }else { + rep0 += this._rangeDecoder.decodeDirectBits(numDirectBits - 4) << 4; + rep0 += this._posAlignDecoder.reverseDecode(this._rangeDecoder); + if (rep0 < 0) { + if (rep0 === -1) { + break; + } + return false; + } + } + }else { + rep0 = posSlot; + } + } + + if (rep0 >= nowPos64 || rep0 >= this._dictionarySizeCheck) { + return false; + } + + this._outWindow.copyBlock(rep0, len); + nowPos64 += len; + prevByte = this._outWindow.getByte(0); + } + } + + this._outWindow.flush(); + this._outWindow.releaseStream(); + this._rangeDecoder.releaseStream(); + + return true; +}; + +LZMA.Decoder.prototype.setDecoderProperties = function(properties) { + var value, lc, lp, pb, dictionarySize; + + if (properties.size < 5) { + return false; + } + + value = properties.readByte(); + lc = value % 9; + value = ~~(value / 9); + lp = value % 5; + pb = ~~(value / 5); + + if ( !this.setLcLpPb(lc, lp, pb) ) { + return false; + } + + dictionarySize = properties.readByte(); + dictionarySize |= properties.readByte() << 8; + dictionarySize |= properties.readByte() << 16; + dictionarySize += properties.readByte() * 16777216; + + return this.setDictionarySize(dictionarySize); +}; + +LZMA.decompress = function(properties, inStream, outStream, outSize) { + var decoder = new LZMA.Decoder(); + + if ( !decoder.setDecoderProperties(properties) ) { + throw "Incorrect stream properties"; + } + + if ( !decoder.decode(inStream, outStream, outSize) ) { + throw "Error in data stream"; + } + + return true; +}; diff --git a/node_modules/three/examples/js/loaders/deprecated/SceneLoader.js b/node_modules/three/examples/js/loaders/deprecated/SceneLoader.js new file mode 100644 index 00000000..f166de5a --- /dev/null +++ b/node_modules/three/examples/js/loaders/deprecated/SceneLoader.js @@ -0,0 +1,1138 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SceneLoader = function ( manager ) { + + this.onLoadStart = function () {}; + this.onLoadProgress = function() {}; + this.onLoadComplete = function () {}; + + this.callbackSync = function () {}; + this.callbackProgress = function () {}; + + this.geometryHandlers = {}; + this.hierarchyHandlers = {}; + + this.addGeometryHandler( "ascii", THREE.JSONLoader ); + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.SceneLoader.prototype = { + + constructor: THREE.SceneLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + scope.parse( JSON.parse( text ), onLoad, url ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + addGeometryHandler: function ( typeID, loaderClass ) { + + this.geometryHandlers[ typeID ] = { "loaderClass": loaderClass }; + + }, + + addHierarchyHandler: function ( typeID, loaderClass ) { + + this.hierarchyHandlers[ typeID ] = { "loaderClass": loaderClass }; + + }, + + parse: function ( json, callbackFinished, url ) { + + var scope = this; + + var urlBase = THREE.Loader.prototype.extractUrlBase( url ); + + var geometry, material, camera, fog, + texture, images, color, + light, hex, intensity, + counter_models, counter_textures, + total_models, total_textures, + result; + + var target_array = []; + + var data = json; + + // async geometry loaders + + for ( var typeID in this.geometryHandlers ) { + + var loaderClass = this.geometryHandlers[ typeID ][ "loaderClass" ]; + this.geometryHandlers[ typeID ][ "loaderObject" ] = new loaderClass(); + + } + + // async hierachy loaders + + for ( var typeID in this.hierarchyHandlers ) { + + var loaderClass = this.hierarchyHandlers[ typeID ][ "loaderClass" ]; + this.hierarchyHandlers[ typeID ][ "loaderObject" ] = new loaderClass(); + + } + + counter_models = 0; + counter_textures = 0; + + result = { + + scene: new THREE.Scene(), + geometries: {}, + face_materials: {}, + materials: {}, + textures: {}, + objects: {}, + cameras: {}, + lights: {}, + fogs: {}, + empties: {}, + groups: {} + + }; + + if ( data.transform ) { + + var position = data.transform.position, + rotation = data.transform.rotation, + scale = data.transform.scale; + + if ( position ) { + + result.scene.position.fromArray( position ); + + } + + if ( rotation ) { + + result.scene.rotation.fromArray( rotation ); + + } + + if ( scale ) { + + result.scene.scale.fromArray( scale ); + + } + + if ( position || rotation || scale ) { + + result.scene.updateMatrix(); + result.scene.updateMatrixWorld(); + + } + + } + + function get_url( source_url, url_type ) { + + if ( url_type == "relativeToHTML" ) { + + return source_url; + + } else { + + return urlBase + source_url; + + } + + } + + // toplevel loader function, delegates to handle_children + + function handle_objects() { + + handle_children( result.scene, data.objects ); + + } + + // handle all the children from the loaded json and attach them to given parent + + function handle_children( parent, children ) { + + var mat, dst, pos, rot, scl, quat; + + for ( var objID in children ) { + + // check by id if child has already been handled, + // if not, create new object + + var object = result.objects[ objID ]; + var objJSON = children[ objID ]; + + if ( object === undefined ) { + + // meshes + + if ( objJSON.type && ( objJSON.type in scope.hierarchyHandlers ) ) { + + if ( objJSON.loading === undefined ) { + + material = result.materials[ objJSON.material ]; + + objJSON.loading = true; + + var loader = scope.hierarchyHandlers[ objJSON.type ][ "loaderObject" ]; + + // ColladaLoader + + if ( loader.options ) { + + loader.load( get_url( objJSON.url, data.urlBaseType ), create_callback_hierachy( objID, parent, material, objJSON ) ); + + // UTF8Loader + // OBJLoader + + } else { + + loader.load( get_url( objJSON.url, data.urlBaseType ), create_callback_hierachy( objID, parent, material, objJSON ) ); + + } + + } + + } else if ( objJSON.geometry !== undefined ) { + + geometry = result.geometries[ objJSON.geometry ]; + + // geometry already loaded + + if ( geometry ) { + + material = result.materials[ objJSON.material ]; + + pos = objJSON.position; + rot = objJSON.rotation; + scl = objJSON.scale; + mat = objJSON.matrix; + quat = objJSON.quaternion; + + // use materials from the model file + // if there is no material specified in the object + + if ( ! objJSON.material ) { + + material = new THREE.MeshFaceMaterial( result.face_materials[ objJSON.geometry ] ); + + } + + // use materials from the model file + // if there is just empty face material + // (must create new material as each model has its own face material) + + if ( ( material instanceof THREE.MeshFaceMaterial ) && material.materials.length === 0 ) { + + material = new THREE.MeshFaceMaterial( result.face_materials[ objJSON.geometry ] ); + + } + + if ( objJSON.skin ) { + + object = new THREE.SkinnedMesh( geometry, material ); + + } else if ( objJSON.morph ) { + + object = new THREE.MorphAnimMesh( geometry, material ); + + if ( objJSON.duration !== undefined ) { + + object.duration = objJSON.duration; + + } + + if ( objJSON.time !== undefined ) { + + object.time = objJSON.time; + + } + + if ( objJSON.mirroredLoop !== undefined ) { + + object.mirroredLoop = objJSON.mirroredLoop; + + } + + if ( material.morphNormals ) { + + geometry.computeMorphNormals(); + + } + + } else { + + object = new THREE.Mesh( geometry, material ); + + } + + object.name = objID; + + if ( mat ) { + + object.matrixAutoUpdate = false; + object.matrix.set( + mat[ 0 ], mat[ 1 ], mat[ 2 ], mat[ 3 ], + mat[ 4 ], mat[ 5 ], mat[ 6 ], mat[ 7 ], + mat[ 8 ], mat[ 9 ], mat[ 10 ], mat[ 11 ], + mat[ 12 ], mat[ 13 ], mat[ 14 ], mat[ 15 ] + ); + + } else { + + object.position.fromArray( pos ); + + if ( quat ) { + + object.quaternion.fromArray( quat ); + + } else { + + object.rotation.fromArray( rot ); + + } + + object.scale.fromArray( scl ); + + } + + object.visible = objJSON.visible; + object.castShadow = objJSON.castShadow; + object.receiveShadow = objJSON.receiveShadow; + + parent.add( object ); + + result.objects[ objID ] = object; + + } + + // lights + + } else if ( objJSON.type === "AmbientLight" || objJSON.type === "PointLight" || + objJSON.type === "DirectionalLight" || objJSON.type === "SpotLight" || + objJSON.type === "HemisphereLight" ) { + + var color = objJSON.color; + var intensity = objJSON.intensity; + var distance = objJSON.distance; + var position = objJSON.position; + var rotation = objJSON.rotation; + + switch ( objJSON.type ) { + + case 'AmbientLight': + light = new THREE.AmbientLight( color ); + break; + + case 'PointLight': + light = new THREE.PointLight( color, intensity, distance ); + light.position.fromArray( position ); + break; + + case 'DirectionalLight': + light = new THREE.DirectionalLight( color, intensity ); + light.position.fromArray( objJSON.direction ); + break; + + case 'SpotLight': + light = new THREE.SpotLight( color, intensity, distance, 1 ); + light.angle = objJSON.angle; + light.position.fromArray( position ); + light.target.set( position[ 0 ], position[ 1 ] - distance, position[ 2 ] ); + light.target.applyEuler( new THREE.Euler( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ], 'XYZ' ) ); + break; + + case 'HemisphereLight': + light = new THREE.DirectionalLight( color, intensity, distance ); + light.target.set( position[ 0 ], position[ 1 ] - distance, position[ 2 ] ); + light.target.applyEuler( new THREE.Euler( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ], 'XYZ' ) ); + break; + + } + + parent.add( light ); + + light.name = objID; + result.lights[ objID ] = light; + result.objects[ objID ] = light; + + // cameras + + } else if ( objJSON.type === "PerspectiveCamera" || objJSON.type === "OrthographicCamera" ) { + + pos = objJSON.position; + rot = objJSON.rotation; + quat = objJSON.quaternion; + + if ( objJSON.type === "PerspectiveCamera" ) { + + camera = new THREE.PerspectiveCamera( objJSON.fov, objJSON.aspect, objJSON.near, objJSON.far ); + + } else if ( objJSON.type === "OrthographicCamera" ) { + + camera = new THREE.OrthographicCamera( objJSON.left, objJSON.right, objJSON.top, objJSON.bottom, objJSON.near, objJSON.far ); + + } + + camera.name = objID; + camera.position.fromArray( pos ); + + if ( quat !== undefined ) { + + camera.quaternion.fromArray( quat ); + + } else if ( rot !== undefined ) { + + camera.rotation.fromArray( rot ); + + } else if ( objJSON.target ) { + + camera.lookAt( new THREE.Vector3().fromArray( objJSON.target ) ); + + } + + parent.add( camera ); + + result.cameras[ objID ] = camera; + result.objects[ objID ] = camera; + + // pure Object3D + + } else { + + pos = objJSON.position; + rot = objJSON.rotation; + scl = objJSON.scale; + quat = objJSON.quaternion; + + object = new THREE.Object3D(); + object.name = objID; + object.position.fromArray( pos ); + + if ( quat ) { + + object.quaternion.fromArray( quat ); + + } else { + + object.rotation.fromArray( rot ); + + } + + object.scale.fromArray( scl ); + object.visible = ( objJSON.visible !== undefined ) ? objJSON.visible : false; + + parent.add( object ); + + result.objects[ objID ] = object; + result.empties[ objID ] = object; + + } + + if ( object ) { + + if ( objJSON.userData !== undefined ) { + + for ( var key in objJSON.userData ) { + + var value = objJSON.userData[ key ]; + object.userData[ key ] = value; + + } + + } + + if ( objJSON.groups !== undefined ) { + + for ( var i = 0; i < objJSON.groups.length; i ++ ) { + + var groupID = objJSON.groups[ i ]; + + if ( result.groups[ groupID ] === undefined ) { + + result.groups[ groupID ] = []; + + } + + result.groups[ groupID ].push( objID ); + + } + + } + + } + + } + + if ( object !== undefined && objJSON.children !== undefined ) { + + handle_children( object, objJSON.children ); + + } + + } + + } + + function handle_mesh( geo, mat, id ) { + + result.geometries[ id ] = geo; + result.face_materials[ id ] = mat; + handle_objects(); + + } + + function handle_hierarchy( node, id, parent, material, obj ) { + + var p = obj.position; + var r = obj.rotation; + var q = obj.quaternion; + var s = obj.scale; + + node.position.fromArray( p ); + + if ( q ) { + + node.quaternion.fromArray( q ); + + } else { + + node.rotation.fromArray( r ); + + } + + node.scale.fromArray( s ); + + // override children materials + // if object material was specified in JSON explicitly + + if ( material ) { + + node.traverse( function ( child ) { + + child.material = material; + + } ); + + } + + // override children visibility + // with root node visibility as specified in JSON + + var visible = ( obj.visible !== undefined ) ? obj.visible : true; + + node.traverse( function ( child ) { + + child.visible = visible; + + } ); + + parent.add( node ); + + node.name = id; + + result.objects[ id ] = node; + handle_objects(); + + } + + function create_callback_geometry( id ) { + + return function ( geo, mat ) { + + geo.name = id; + + handle_mesh( geo, mat, id ); + + counter_models -= 1; + + scope.onLoadComplete(); + + async_callback_gate(); + + } + + } + + function create_callback_hierachy( id, parent, material, obj ) { + + return function ( event ) { + + var result; + + // loaders which use EventDispatcher + + if ( event.content ) { + + result = event.content; + + // ColladaLoader + + } else if ( event.dae ) { + + result = event.scene; + + + // UTF8Loader + + } else { + + result = event; + + } + + handle_hierarchy( result, id, parent, material, obj ); + + counter_models -= 1; + + scope.onLoadComplete(); + + async_callback_gate(); + + } + + } + + function create_callback_embed( id ) { + + return function ( geo, mat ) { + + geo.name = id; + + result.geometries[ id ] = geo; + result.face_materials[ id ] = mat; + + } + + } + + function async_callback_gate() { + + var progress = { + + totalModels : total_models, + totalTextures : total_textures, + loadedModels : total_models - counter_models, + loadedTextures : total_textures - counter_textures + + }; + + scope.callbackProgress( progress, result ); + + scope.onLoadProgress(); + + if ( counter_models === 0 && counter_textures === 0 ) { + + finalize(); + callbackFinished( result ); + + } + + } + + function finalize() { + + // take care of targets which could be asynchronously loaded objects + + for ( var i = 0; i < target_array.length; i ++ ) { + + var ta = target_array[ i ]; + + var target = result.objects[ ta.targetName ]; + + if ( target ) { + + ta.object.target = target; + + } else { + + // if there was error and target of specified name doesn't exist in the scene file + // create instead dummy target + // (target must be added to scene explicitly as parent is already added) + + ta.object.target = new THREE.Object3D(); + result.scene.add( ta.object.target ); + + } + + ta.object.target.userData.targetInverse = ta.object; + + } + + } + + var callbackTexture = function ( count ) { + + counter_textures -= count; + async_callback_gate(); + + scope.onLoadComplete(); + + }; + + // must use this instead of just directly calling callbackTexture + // because of closure in the calling context loop + + var generateTextureCallback = function ( count ) { + + return function () { + + callbackTexture( count ); + + }; + + }; + + function traverse_json_hierarchy( objJSON, callback ) { + + callback( objJSON ); + + if ( objJSON.children !== undefined ) { + + for ( var objChildID in objJSON.children ) { + + traverse_json_hierarchy( objJSON.children[ objChildID ], callback ); + + } + + } + + } + + // first go synchronous elements + + // fogs + + var fogID, fogJSON; + + for ( fogID in data.fogs ) { + + fogJSON = data.fogs[ fogID ]; + + if ( fogJSON.type === "linear" ) { + + fog = new THREE.Fog( 0x000000, fogJSON.near, fogJSON.far ); + + } else if ( fogJSON.type === "exp2" ) { + + fog = new THREE.FogExp2( 0x000000, fogJSON.density ); + + } + + color = fogJSON.color; + fog.color.setRGB( color[ 0 ], color[ 1 ], color[ 2 ] ); + + result.fogs[ fogID ] = fog; + + } + + // now come potentially asynchronous elements + + // geometries + + // count how many geometries will be loaded asynchronously + + var geoID, geoJSON; + + for ( geoID in data.geometries ) { + + geoJSON = data.geometries[ geoID ]; + + if ( geoJSON.type in this.geometryHandlers ) { + + counter_models += 1; + + scope.onLoadStart(); + + } + + } + + // count how many hierarchies will be loaded asynchronously + + for ( var objID in data.objects ) { + + traverse_json_hierarchy( data.objects[ objID ], function ( objJSON ) { + + if ( objJSON.type && ( objJSON.type in scope.hierarchyHandlers ) ) { + + counter_models += 1; + + scope.onLoadStart(); + + } + + } ); + + } + + total_models = counter_models; + + for ( geoID in data.geometries ) { + + geoJSON = data.geometries[ geoID ]; + + if ( geoJSON.type === "cube" ) { + + geometry = new THREE.BoxGeometry( geoJSON.width, geoJSON.height, geoJSON.depth, geoJSON.widthSegments, geoJSON.heightSegments, geoJSON.depthSegments ); + geometry.name = geoID; + result.geometries[ geoID ] = geometry; + + } else if ( geoJSON.type === "plane" ) { + + geometry = new THREE.PlaneGeometry( geoJSON.width, geoJSON.height, geoJSON.widthSegments, geoJSON.heightSegments ); + geometry.name = geoID; + result.geometries[ geoID ] = geometry; + + } else if ( geoJSON.type === "sphere" ) { + + geometry = new THREE.SphereGeometry( geoJSON.radius, geoJSON.widthSegments, geoJSON.heightSegments ); + geometry.name = geoID; + result.geometries[ geoID ] = geometry; + + } else if ( geoJSON.type === "cylinder" ) { + + geometry = new THREE.CylinderGeometry( geoJSON.topRad, geoJSON.botRad, geoJSON.height, geoJSON.radSegs, geoJSON.heightSegs ); + geometry.name = geoID; + result.geometries[ geoID ] = geometry; + + } else if ( geoJSON.type === "torus" ) { + + geometry = new THREE.TorusGeometry( geoJSON.radius, geoJSON.tube, geoJSON.segmentsR, geoJSON.segmentsT ); + geometry.name = geoID; + result.geometries[ geoID ] = geometry; + + } else if ( geoJSON.type === "icosahedron" ) { + + geometry = new THREE.IcosahedronGeometry( geoJSON.radius, geoJSON.subdivisions ); + geometry.name = geoID; + result.geometries[ geoID ] = geometry; + + } else if ( geoJSON.type in this.geometryHandlers ) { + + var loader = this.geometryHandlers[ geoJSON.type ][ "loaderObject" ]; + loader.load( get_url( geoJSON.url, data.urlBaseType ), create_callback_geometry( geoID ) ); + + } else if ( geoJSON.type === "embedded" ) { + + var modelJson = data.embeds[ geoJSON.id ], + texture_path = ""; + + // pass metadata along to jsonLoader so it knows the format version + + modelJson.metadata = data.metadata; + + if ( modelJson ) { + + var jsonLoader = this.geometryHandlers[ "ascii" ][ "loaderObject" ]; + var model = jsonLoader.parse( modelJson, texture_path ); + create_callback_embed( geoID )( model.geometry, model.materials ); + + } + + } + + } + + // textures + + // count how many textures will be loaded asynchronously + + var textureID, textureJSON; + + for ( textureID in data.textures ) { + + textureJSON = data.textures[ textureID ]; + + if ( Array.isArray( textureJSON.url ) ) { + + counter_textures += textureJSON.url.length; + + for ( var n = 0; n < textureJSON.url.length; n ++ ) { + + scope.onLoadStart(); + + } + + } else { + + counter_textures += 1; + + scope.onLoadStart(); + + } + + } + + total_textures = counter_textures; + + for ( textureID in data.textures ) { + + textureJSON = data.textures[ textureID ]; + + if ( textureJSON.mapping !== undefined && THREE[ textureJSON.mapping ] !== undefined ) { + + textureJSON.mapping = THREE[ textureJSON.mapping ]; + + } + + var texture; + + if ( Array.isArray( textureJSON.url ) ) { + + var count = textureJSON.url.length; + var url_array = []; + + for ( var i = 0; i < count; i ++ ) { + + url_array[ i ] = get_url( textureJSON.url[ i ], data.urlBaseType ); + + } + + var loader = THREE.Loader.Handlers.get( url_array[ 0 ] ); + + if ( loader !== null ) { + + texture = loader.load( url_array, generateTextureCallback( count ) ); + + if ( textureJSON.mapping !== undefined ) + texture.mapping = textureJSON.mapping; + + } else { + + texture = THREE.ImageUtils.loadTextureCube( url_array, textureJSON.mapping, generateTextureCallback( count ) ); + + } + + } else { + + var fullUrl = get_url( textureJSON.url, data.urlBaseType ); + var textureCallback = generateTextureCallback( 1 ); + + var loader = THREE.Loader.Handlers.get( fullUrl ); + + if ( loader !== null ) { + + texture = loader.load( fullUrl, textureCallback ); + + } else { + + texture = new THREE.Texture(); + loader = new THREE.ImageLoader(); + + ( function ( texture ) { + + loader.load( fullUrl, function ( image ) { + + texture.image = image; + texture.needsUpdate = true; + + textureCallback(); + + } ); + + } )( texture ) + + + } + + if ( textureJSON.mapping !== undefined ) + texture.mapping = textureJSON.mapping; + + if ( THREE[ textureJSON.minFilter ] !== undefined ) + texture.minFilter = THREE[ textureJSON.minFilter ]; + + if ( THREE[ textureJSON.magFilter ] !== undefined ) + texture.magFilter = THREE[ textureJSON.magFilter ]; + + if ( textureJSON.anisotropy ) texture.anisotropy = textureJSON.anisotropy; + + if ( textureJSON.repeat ) { + + texture.repeat.set( textureJSON.repeat[ 0 ], textureJSON.repeat[ 1 ] ); + + if ( textureJSON.repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping; + if ( textureJSON.repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping; + + } + + if ( textureJSON.offset ) { + + texture.offset.set( textureJSON.offset[ 0 ], textureJSON.offset[ 1 ] ); + + } + + // handle wrap after repeat so that default repeat can be overriden + + if ( textureJSON.wrap ) { + + var wrapMap = { + "repeat": THREE.RepeatWrapping, + "mirror": THREE.MirroredRepeatWrapping + }; + + if ( wrapMap[ textureJSON.wrap[ 0 ] ] !== undefined ) texture.wrapS = wrapMap[ textureJSON.wrap[ 0 ] ]; + if ( wrapMap[ textureJSON.wrap[ 1 ] ] !== undefined ) texture.wrapT = wrapMap[ textureJSON.wrap[ 1 ] ]; + + } + + } + + result.textures[ textureID ] = texture; + + } + + // materials + + var matID, matJSON; + var parID; + + for ( matID in data.materials ) { + + matJSON = data.materials[ matID ]; + + for ( parID in matJSON.parameters ) { + + if ( parID === "envMap" || parID === "map" || parID === "lightMap" || parID === "bumpMap" || parID === "normalMap" || parID === "alphaMap" ) { + + matJSON.parameters[ parID ] = result.textures[ matJSON.parameters[ parID ] ]; + + } else if ( parID === "shading" ) { + + matJSON.parameters[ parID ] = ( matJSON.parameters[ parID ] === "flat" ) ? THREE.FlatShading : THREE.SmoothShading; + + } else if ( parID === "side" ) { + + if ( matJSON.parameters[ parID ] == "double" ) { + + matJSON.parameters[ parID ] = THREE.DoubleSide; + + } else if ( matJSON.parameters[ parID ] == "back" ) { + + matJSON.parameters[ parID ] = THREE.BackSide; + + } else { + + matJSON.parameters[ parID ] = THREE.FrontSide; + + } + + } else if ( parID === "blending" ) { + + matJSON.parameters[ parID ] = matJSON.parameters[ parID ] in THREE ? THREE[ matJSON.parameters[ parID ] ] : THREE.NormalBlending; + + } else if ( parID === "combine" ) { + + matJSON.parameters[ parID ] = matJSON.parameters[ parID ] in THREE ? THREE[ matJSON.parameters[ parID ] ] : THREE.MultiplyOperation; + + } else if ( parID === "vertexColors" ) { + + if ( matJSON.parameters[ parID ] == "face" ) { + + matJSON.parameters[ parID ] = THREE.FaceColors; + + // default to vertex colors if "vertexColors" is anything else face colors or 0 / null / false + + } else if ( matJSON.parameters[ parID ] ) { + + matJSON.parameters[ parID ] = THREE.VertexColors; + + } + + } else if ( parID === "wrapRGB" ) { + + var v3 = matJSON.parameters[ parID ]; + matJSON.parameters[ parID ] = new THREE.Vector3( v3[ 0 ], v3[ 1 ], v3[ 2 ] ); + + } else if ( parID === "normalScale" ) { + + var v2 = matJSON.parameters[ parID ]; + matJSON.parameters[ parID ] = new THREE.Vector2( v2[ 0 ], v2[ 1 ] ); + + } + + } + + if ( matJSON.parameters.opacity !== undefined && matJSON.parameters.opacity < 1.0 ) { + + matJSON.parameters.transparent = true; + + } + + material = new THREE[ matJSON.type ]( matJSON.parameters ); + material.name = matID; + + result.materials[ matID ] = material; + + } + + // second pass through all materials to initialize MeshFaceMaterials + // that could be referring to other materials out of order + + for ( matID in data.materials ) { + + matJSON = data.materials[ matID ]; + + if ( matJSON.parameters.materials ) { + + var materialArray = []; + + for ( var i = 0; i < matJSON.parameters.materials.length; i ++ ) { + + var label = matJSON.parameters.materials[ i ]; + materialArray.push( result.materials[ label ] ); + + } + + result.materials[ matID ].materials = materialArray; + + } + + } + + // objects ( synchronous init of procedural primitives ) + + handle_objects(); + + // defaults + + if ( result.cameras && data.defaults.camera ) { + + result.currentCamera = result.cameras[ data.defaults.camera ]; + + } + + if ( result.fogs && data.defaults.fog ) { + + result.scene.fog = result.fogs[ data.defaults.fog ]; + + } + + // synchronous callback + + scope.callbackSync( result ); + + // just in case there are no async elements + + async_callback_gate(); + + } + +}; diff --git a/node_modules/three/examples/js/loaders/gltf/glTF-parser.js b/node_modules/three/examples/js/loaders/gltf/glTF-parser.js new file mode 100644 index 00000000..10e40831 --- /dev/null +++ b/node_modules/three/examples/js/loaders/gltf/glTF-parser.js @@ -0,0 +1,388 @@ +// Copyright (c) 2013 Fabrice Robinet +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* + The Abstract Loader has two modes: + #1: [static] load all the JSON at once [as of now] + #2: [stream] stream and parse JSON progressively [not yet supported] + + Whatever is the mechanism used to parse the JSON (#1 or #2), + The loader starts by resolving the paths to binaries and referenced json files (by replace the value of the path property with an absolute path if it was relative). + + In case #1: it is guaranteed to call the concrete loader implementation methods in a order that solves the dependencies between the entries. + only the nodes requires an extra pass to set up the hirerarchy. + In case #2: the concrete implementation will have to solve the dependencies. no order is guaranteed. + + When case #1 is used the followed dependency order is: + + scenes -> nodes -> meshes -> materials -> techniques -> shaders + -> buffers + -> cameras + -> lights + + The readers starts with the leafs, i.e: + shaders, techniques, materials, meshes, buffers, cameras, lights, nodes, scenes + + For each called handle method called the client should return true if the next handle can be call right after returning, + or false if a callback on client side will notify the loader that the next handle method can be called. + +*/ +var global = window; +(function (root, factory) { + if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like enviroments that support module.exports, + // like Node. + factory(module.exports); + } else if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define([], function () { + return factory(root); + }); + } else { + // Browser globals + factory(root); + } +}(this, function (root) { + "use strict"; + + var categoriesDepsOrder = [ "buffers", "bufferViews", "images", "videos", "samplers", "textures", "shaders", "programs", "techniques", "materials", "accessors", "meshes", "cameras", "lights", "skins", "nodes", "scenes", "animations" ]; + + var glTFParser = Object.create(Object.prototype, { + + _rootDescription: { value: null, writable: true }, + + rootDescription: { + set: function(value) { + this._rootDescription = value; + }, + get: function() { + return this._rootDescription; + } + }, + + baseURL: { value: null, writable: true }, + + //detect absolute path following the same protocol than window.location + _isAbsolutePath: { + value: function(path) { + var isAbsolutePathRegExp = new RegExp("^" + window.location.protocol, "i"); + + return path.match(isAbsolutePathRegExp) ? true : false; + } + }, + + resolvePathIfNeeded: { + value: function(path) { + if (this._isAbsolutePath(path)) { + return path; + } + + return this.baseURL + path; + } + }, + + _resolvePathsForCategories: { + value: function(categories) { + categories.forEach( function(category) { + var descriptions = this.json[category]; + if (descriptions) { + var descriptionKeys = Object.keys(descriptions); + descriptionKeys.forEach( function(descriptionKey) { + var description = descriptions[descriptionKey]; + description.path = this.resolvePathIfNeeded(description.path); + }, this); + } + }, this); + } + }, + + _json: { + value: null, + writable: true + }, + + json: { + enumerable: true, + get: function() { + return this._json; + }, + set: function(value) { + if (this._json !== value) { + this._json = value; + this._resolvePathsForCategories([ "buffers", "shaders", "images", "videos" ]); + } + } + }, + + _path: { + value: null, + writable: true + }, + + getEntryDescription: { + value: function (entryID, entryType) { + var entries = null; + + var category = entryType; + entries = this.rootDescription[category]; + if (!entries) { + console.log("ERROR:CANNOT find expected category named:" + category); + return null; + } + + return entries ? entries[entryID] : null; + } + }, + + _stepToNextCategory: { + value: function() { + this._state.categoryIndex = this.getNextCategoryIndex(this._state.categoryIndex + 1); + if (this._state.categoryIndex !== -1) { + this._state.categoryState.index = 0; + return true; + } + + return false; + } + }, + + _stepToNextDescription: { + enumerable: false, + value: function() { + var categoryState = this._state.categoryState; + var keys = categoryState.keys; + if (!keys) { + console.log("INCONSISTENCY ERROR"); + return false; + } + + categoryState.index ++; + categoryState.keys = null; + if (categoryState.index >= keys.length) { + return this._stepToNextCategory(); + } + return false; + } + }, + + hasCategory: { + value: function(category) { + return this.rootDescription[category] ? true : false; + } + }, + + _handleState: { + value: function() { + + var methodForType = { + "buffers" : this.handleBuffer, + "bufferViews" : this.handleBufferView, + "shaders" : this.handleShader, + "programs" : this.handleProgram, + "techniques" : this.handleTechnique, + "materials" : this.handleMaterial, + "meshes" : this.handleMesh, + "cameras" : this.handleCamera, + "lights" : this.handleLight, + "nodes" : this.handleNode, + "scenes" : this.handleScene, + "images" : this.handleImage, + "animations" : this.handleAnimation, + "accessors" : this.handleAccessor, + "skins" : this.handleSkin, + "samplers" : this.handleSampler, + "textures" : this.handleTexture, + "videos" : this.handleVideo + + }; + + var success = true; + while (this._state.categoryIndex !== -1) { + var category = categoriesDepsOrder[this._state.categoryIndex]; + var categoryState = this._state.categoryState; + var keys = categoryState.keys; + if (!keys) { + categoryState.keys = keys = Object.keys(this.rootDescription[category]); + if (keys) { + if (keys.length == 0) { + this._stepToNextDescription(); + continue; + } + } + } + + var type = category; + var entryID = keys[categoryState.index]; + var description = this.getEntryDescription(entryID, type); + if (!description) { + if (this.handleError) { + this.handleError("INCONSISTENCY ERROR: no description found for entry " + entryID); + success = false; + break; + } + } else { + + if (methodForType[type]) { + if (methodForType[type].call(this, entryID, description, this._state.userInfo) === false) { + success = false; + break; + } + } + + this._stepToNextDescription(); + } + } + + if (this.handleLoadCompleted) { + this.handleLoadCompleted(success); + } + + } + }, + + _loadJSONIfNeeded: { + enumerable: true, + value: function(callback) { + var self = this; + //FIXME: handle error + if (!this._json) { + var jsonPath = this._path; + var i = jsonPath.lastIndexOf("/"); + this.baseURL = (i !== 0) ? jsonPath.substring(0, i + 1) : ''; + var jsonfile = new XMLHttpRequest(); + jsonfile.open("GET", jsonPath, true); + jsonfile.addEventListener( 'load', function ( event ) { + self.json = JSON.parse(jsonfile.responseText); + if (callback) { + callback(self.json); + } + }, false ); + jsonfile.send(null); + } else { + if (callback) { + callback(this.json); + } + } + } + }, + + /* load JSON and assign it as description to the reader */ + _buildLoader: { + value: function(callback) { + var self = this; + function JSONReady(json) { + self.rootDescription = json; + if (callback) + callback(this); + } + + this._loadJSONIfNeeded(JSONReady); + } + }, + + _state: { value: null, writable: true }, + + _getEntryType: { + value: function(entryID) { + var rootKeys = categoriesDepsOrder; + for (var i = 0 ; i < rootKeys.length ; i ++) { + var rootValues = this.rootDescription[rootKeys[i]]; + if (rootValues) { + return rootKeys[i]; + } + } + return null; + } + }, + + getNextCategoryIndex: { + value: function(currentIndex) { + for (var i = currentIndex ; i < categoriesDepsOrder.length ; i ++) { + if (this.hasCategory(categoriesDepsOrder[i])) { + return i; + } + } + + return -1; + } + }, + + load: { + enumerable: true, + value: function(userInfo, options) { + var self = this; + this._buildLoader(function loaderReady(reader) { + var startCategory = self.getNextCategoryIndex.call(self, 0); + if (startCategory !== -1) { + self._state = { "userInfo" : userInfo, + "options" : options, + "categoryIndex" : startCategory, + "categoryState" : { "index" : "0" } }; + self._handleState(); + } + }); + } + }, + + initWithPath: { + value: function(path) { + this._path = path; + this._json = null; + return this; + } + }, + + //this is meant to be global and common for all instances + _knownURLs: { writable: true, value: {} }, + + //to be invoked by subclass, so that ids can be ensured to not overlap + loaderContext: { + value: function() { + if (typeof this._knownURLs[this._path] === "undefined") { + this._knownURLs[this._path] = Object.keys(this._knownURLs).length; + } + return "__" + this._knownURLs[this._path]; + } + }, + + initWithJSON: { + value: function(json, baseURL) { + this.json = json; + this.baseURL = baseURL; + if (!baseURL) { + console.log("WARNING: no base URL passed to Reader:initWithJSON"); + } + return this; + } + } + + }); + + if (root) { + root.glTFParser = glTFParser; + } + + return glTFParser; + +})); diff --git a/node_modules/three/examples/js/loaders/gltf/glTFAnimation.js b/node_modules/three/examples/js/loaders/gltf/glTFAnimation.js new file mode 100644 index 00000000..293e1f5d --- /dev/null +++ b/node_modules/three/examples/js/loaders/gltf/glTFAnimation.js @@ -0,0 +1,251 @@ +/** + * @author Tony Parisi / http://www.tonyparisi.com/ + */ + +THREE.glTFAnimator = ( function () { + + var animators = []; + + return { + add : function(animator) + { + animators.push(animator); + }, + + remove: function(animator) + { + + var i = animators.indexOf(animator); + + if ( i !== -1 ) { + animators.splice( i, 1 ); + } + }, + + update : function() + { + for (i = 0; i < animators.length; i ++) + { + animators[i].update(); + } + }, + }; +})(); + +// Construction/initialization +THREE.glTFAnimation = function(interps) +{ + this.running = false; + this.loop = false; + this.duration = 0; + this.startTime = 0; + this.interps = []; + + if (interps) + { + this.createInterpolators(interps); + } +}; + +THREE.glTFAnimation.prototype.createInterpolators = function(interps) +{ + var i, len = interps.length; + for (i = 0; i < len; i ++) + { + var interp = new THREE.glTFInterpolator(interps[i]); + this.interps.push(interp); + this.duration = Math.max(this.duration, interp.duration); + } +}; + +// Start/stop +THREE.glTFAnimation.prototype.play = function() +{ + if (this.running) + return; + + this.startTime = Date.now(); + this.running = true; + THREE.glTFAnimator.add(this); +}; + +THREE.glTFAnimation.prototype.stop = function() +{ + this.running = false; + THREE.glTFAnimator.remove(this); +}; + +// Update - drive key frame evaluation +THREE.glTFAnimation.prototype.update = function() +{ + if (!this.running) + return; + + var now = Date.now(); + var deltat = (now - this.startTime) / 1000; + var t = deltat % this.duration; + var nCycles = Math.floor(deltat / this.duration); + + if (nCycles >= 1 && !this.loop) + { + this.running = false; + var i, len = this.interps.length; + for (i = 0; i < len; i ++) + { + this.interps[i].interp(this.duration); + } + this.stop(); + return; + } + else + { + var i, len = this.interps.length; + for (i = 0; i < len; i ++) + { + this.interps[i].interp(t); + } + } +}; + +//Interpolator class +//Construction/initialization +THREE.glTFInterpolator = function(param) +{ + this.keys = param.keys; + this.values = param.values; + this.count = param.count; + this.type = param.type; + this.path = param.path; + this.isRot = false; + + var node = param.target; + node.updateMatrix(); + node.matrixAutoUpdate = true; + this.targetNode = node; + + switch (param.path) { + case "translation" : + this.target = node.position; + this.originalValue = node.position.clone(); + break; + case "rotation" : + this.target = node.quaternion; + this.originalValue = node.quaternion.clone(); + this.isRot = true; + break; + case "scale" : + this.target = node.scale; + this.originalValue = node.scale.clone(); + break; + } + + this.duration = this.keys[this.count - 1]; + + this.vec1 = new THREE.Vector3; + this.vec2 = new THREE.Vector3; + this.vec3 = new THREE.Vector3; + this.quat1 = new THREE.Quaternion; + this.quat2 = new THREE.Quaternion; + this.quat3 = new THREE.Quaternion; +}; + +//Interpolation and tweening methods +THREE.glTFInterpolator.prototype.interp = function(t) +{ + var i, j; + if (t == this.keys[0]) + { + if (this.isRot) { + this.quat3.set(this.values[0], this.values[1], this.values[2], this.values[3]); + } + else { + this.vec3.set(this.values[0], this.values[1], this.values[2]); + } + } + else if (t < this.keys[0]) + { + if (this.isRot) { + this.quat1.set(this.originalValue.x, + this.originalValue.y, + this.originalValue.z, + this.originalValue.w); + this.quat2.set(this.values[0], + this.values[1], + this.values[2], + this.values[3]); + THREE.Quaternion.slerp(this.quat1, this.quat2, this.quat3, t / this.keys[0]); + } + else { + this.vec3.set(this.originalValue.x, + this.originalValue.y, + this.originalValue.z); + this.vec2.set(this.values[0], + this.values[1], + this.values[2]); + + this.vec3.lerp(this.vec2, t / this.keys[0]); + } + } + else if (t >= this.keys[this.count - 1]) + { + if (this.isRot) { + this.quat3.set(this.values[(this.count - 1) * 4], + this.values[(this.count - 1) * 4 + 1], + this.values[(this.count - 1) * 4 + 2], + this.values[(this.count - 1) * 4 + 3]); + } + else { + this.vec3.set(this.values[(this.count - 1) * 3], + this.values[(this.count - 1) * 3 + 1], + this.values[(this.count - 1) * 3 + 2]); + } + } + else + { + for (i = 0; i < this.count - 1; i ++) + { + var key1 = this.keys[i]; + var key2 = this.keys[i + 1]; + + if (t >= key1 && t <= key2) + { + if (this.isRot) { + this.quat1.set(this.values[i * 4], + this.values[i * 4 + 1], + this.values[i * 4 + 2], + this.values[i * 4 + 3]); + this.quat2.set(this.values[(i + 1) * 4], + this.values[(i + 1) * 4 + 1], + this.values[(i + 1) * 4 + 2], + this.values[(i + 1) * 4 + 3]); + THREE.Quaternion.slerp(this.quat1, this.quat2, this.quat3, (t - key1) / (key2 - key1)); + } + else { + this.vec3.set(this.values[i * 3], + this.values[i * 3 + 1], + this.values[i * 3 + 2]); + this.vec2.set(this.values[(i + 1) * 3], + this.values[(i + 1) * 3 + 1], + this.values[(i + 1) * 3 + 2]); + + this.vec3.lerp(this.vec2, (t - key1) / (key2 - key1)); + } + } + } + } + + if (this.target) + { + this.copyValue(this.target); + } +}; + +THREE.glTFInterpolator.prototype.copyValue = function(target) { + + if (this.isRot) { + target.copy(this.quat3); + } + else { + target.copy(this.vec3); + } +}; diff --git a/node_modules/three/examples/js/loaders/gltf/glTFLoader.js b/node_modules/three/examples/js/loaders/gltf/glTFLoader.js new file mode 100644 index 00000000..ea9691cd --- /dev/null +++ b/node_modules/three/examples/js/loaders/gltf/glTFLoader.js @@ -0,0 +1,1554 @@ +/** + * @author Tony Parisi / http://www.tonyparisi.com/ + */ + + +THREE.glTFLoader = function () { + this.meshesRequested = 0; + this.meshesLoaded = 0; + this.pendingMeshes = []; + this.animationsRequested = 0; + this.animationsLoaded = 0; + this.animations = []; + this.shadersRequested = 0; + this.shadersLoaded = 0; + this.shaders = {}; + THREE.Loader.call( this ); +}; + +THREE.glTFLoader.prototype = Object.create( THREE.Loader.prototype ); +THREE.glTFLoader.prototype.constructor = THREE.glTFLoader; + +THREE.glTFLoader.prototype.load = function( url, callback ) { + + var theLoader = this; + // Utilities + + function RgbArraytoHex(colorArray) { + if (!colorArray) return 0xFFFFFFFF; + var r = Math.floor(colorArray[0] * 255), + g = Math.floor(colorArray[1] * 255), + b = Math.floor(colorArray[2] * 255), + a = 255; + + var color = (a << 24) + (r << 16) + (g << 8) + b; + + return color; + } + + function convertAxisAngleToQuaternion(rotations, count) + { + var q = new THREE.Quaternion; + var axis = new THREE.Vector3; + var euler = new THREE.Vector3; + + var i; + for (i = 0; i < count; i ++) { + axis.set(rotations[i * 4], rotations[i * 4 + 1], + rotations[i * 4 + 2]).normalize(); + var angle = rotations[i * 4 + 3]; + q.setFromAxisAngle(axis, angle); + rotations[i * 4] = q.x; + rotations[i * 4 + 1] = q.y; + rotations[i * 4 + 2] = q.z; + rotations[i * 4 + 3] = q.w; + } + } + + function componentsPerElementForGLType(glType) { + switch (glType) { + case WebGLRenderingContext.FLOAT : + case WebGLRenderingContext.UNSIGNED_BYTE : + case WebGLRenderingContext.UNSIGNED_SHORT : + return 1; + case WebGLRenderingContext.FLOAT_VEC2 : + return 2; + case WebGLRenderingContext.FLOAT_VEC3 : + return 3; + case WebGLRenderingContext.FLOAT_VEC4 : + return 4; + case WebGLRenderingContext.FLOAT_MAT4 : + return 16; + default: + return null; + } + } + + + function LoadTexture(src) { + if (!src) { return null; } + return THREE.ImageUtils.loadTexture(src); + } + + // Geometry processing + + var ClassicGeometry = function() { + + this.geometry = new THREE.BufferGeometry(); + + this.totalAttributes = 0; + this.loadedAttributes = 0; + this.indicesLoaded = false; + this.finished = false; + + this.onload = null; + + this.uvs = null; + this.indexArray = null; + }; + + ClassicGeometry.prototype.constructor = ClassicGeometry; + + ClassicGeometry.prototype.buildBufferGeometry = function() { + // Build indexed mesh + var geometry = this.geometry; + + geometry.addAttribute( 'index', new THREE.BufferAttribute( this.indexArray, 1 ) ); + geometry.addDrawCall( 0, this.indexArray.length ); + + geometry.computeBoundingSphere(); + }; + + ClassicGeometry.prototype.checkFinished = function() { + if (this.indexArray && this.loadedAttributes === this.totalAttributes) { + + this.buildBufferGeometry(); + + this.finished = true; + + if (this.onload) { + this.onload(); + } + } + }; + + // Delegate for processing index buffers + var IndicesDelegate = function() {}; + + IndicesDelegate.prototype.handleError = function(errorCode, info) { + // FIXME: report error + console.log("ERROR(IndicesDelegate):" + errorCode + ":" + info); + }; + + IndicesDelegate.prototype.convert = function(resource, ctx) { + return new Uint16Array(resource, 0, ctx.indices.count); + }; + + IndicesDelegate.prototype.resourceAvailable = function(glResource, ctx) { + var geometry = ctx.geometry; + geometry.indexArray = glResource; + geometry.checkFinished(); + return true; + }; + + var indicesDelegate = new IndicesDelegate(); + + var IndicesContext = function(indices, geometry) { + this.indices = indices; + this.geometry = geometry; + }; + + // Delegate for processing vertex attribute buffers + var VertexAttributeDelegate = function() {}; + + VertexAttributeDelegate.prototype.handleError = function(errorCode, info) { + // FIXME: report error + console.log("ERROR(VertexAttributeDelegate):" + errorCode + ":" + info); + }; + + VertexAttributeDelegate.prototype.convert = function(resource, ctx) { + return resource; + }; + + + + VertexAttributeDelegate.prototype.arrayResourceAvailable = function(glResource, ctx) { + var geom = ctx.geometry; + var attribute = ctx.attribute; + var semantic = ctx.semantic; + var floatArray; + var i, l; + //FIXME: Float32 is assumed here, but should be checked. + + if (semantic == "POSITION") { + // TODO: Should be easy to take strides into account here + floatArray = new Float32Array(glResource, 0, attribute.count * componentsPerElementForGLType(attribute.type)); + for (i = 0, l = floatArray.length; i < l; i += 3) { + geom.geometry.vertices.push( new THREE.Vector3( floatArray[i], floatArray[i + 1], floatArray[i + 2] ) ); + } + } else if (semantic == "NORMAL") { + geom.geometry.normals = []; + floatArray = new Float32Array(glResource, 0, attribute.count * componentsPerElementForGLType(attribute.type)); + for (i = 0, l = floatArray.length; i < l; i += 3) { + geom.geometry.normals.push( new THREE.Vector3( floatArray[i], floatArray[i + 1], floatArray[i + 2] ) ); + } + } else if ((semantic == "TEXCOORD_0") || (semantic == "TEXCOORD" )) { + geom.uvs = []; + floatArray = new Float32Array(glResource, 0, attribute.count * componentsPerElementForGLType(attribute.type)); + for (i = 0, l = floatArray.length; i < l; i += 2) { + geom.uvs.push( new THREE.Vector2( floatArray[i], 1.0 - floatArray[i + 1] ) ); + } + } + else if (semantic == "WEIGHT") { + nComponents = componentsPerElementForGLType(attribute.type); + floatArray = new Float32Array(glResource, 0, attribute.count * nComponents); + for (i = 0, l = floatArray.length; i < l; i += 4) { + geom.geometry.skinWeights.push( new THREE.Vector4( floatArray[i], floatArray[i + 1], floatArray[i + 2], floatArray[i + 3] ) ); + } + } + else if (semantic == "JOINT") { + nComponents = componentsPerElementForGLType(attribute.type); + floatArray = new Float32Array(glResource, 0, attribute.count * nComponents); + for (i = 0, l = floatArray.length; i < l; i += 4) { + geom.geometry.skinIndices.push( new THREE.Vector4( floatArray[i], floatArray[i + 1], floatArray[i + 2], floatArray[i + 3] ) ); + } + } + }; + + VertexAttributeDelegate.prototype.bufferResourceAvailable = function(glResource, ctx) { + var geom = ctx.geometry; + var attribute = ctx.attribute; + var semantic = ctx.semantic; + var floatArray; + var i, l; + var nComponents; + //FIXME: Float32 is assumed here, but should be checked. + + if (semantic == "POSITION") { + // TODO: Should be easy to take strides into account here + floatArray = new Float32Array(glResource, 0, attribute.count * componentsPerElementForGLType(attribute.type)); + geom.geometry.addAttribute( 'position', new THREE.BufferAttribute( floatArray, 3 ) ); + } else if (semantic == "NORMAL") { + floatArray = new Float32Array(glResource, 0, attribute.count * componentsPerElementForGLType(attribute.type)); + geom.geometry.addAttribute( 'normal', new THREE.BufferAttribute( floatArray, 3 ) ); + } else if ((semantic == "TEXCOORD_0") || (semantic == "TEXCOORD" )) { + + nComponents = componentsPerElementForGLType(attribute.type); + floatArray = new Float32Array(glResource, 0, attribute.count * nComponents); + // N.B.: flip Y value... should we just set texture.flipY everywhere? + for (i = 0; i < floatArray.length / 2; i ++) { + floatArray[i * 2 + 1] = 1.0 - floatArray[i * 2 + 1]; + } + geom.geometry.addAttribute( 'uv', new THREE.BufferAttribute( floatArray, nComponents ) ); + } + else if (semantic == "WEIGHT") { + nComponents = componentsPerElementForGLType(attribute.type); + floatArray = new Float32Array(glResource, 0, attribute.count * nComponents); + geom.geometry.addAttribute( 'skinWeight', new THREE.BufferAttribute( floatArray, nComponents ) ); + } + else if (semantic == "JOINT") { + nComponents = componentsPerElementForGLType(attribute.type); + floatArray = new Float32Array(glResource, 0, attribute.count * nComponents); + geom.geometry.addAttribute( 'skinIndex', new THREE.BufferAttribute( floatArray, nComponents ) ); + } + }; + + VertexAttributeDelegate.prototype.resourceAvailable = function(glResource, ctx) { + + this.bufferResourceAvailable(glResource, ctx); + + var geom = ctx.geometry; + geom.loadedAttributes ++; + geom.checkFinished(); + return true; + }; + + var vertexAttributeDelegate = new VertexAttributeDelegate(); + + var VertexAttributeContext = function(attribute, semantic, geometry) { + this.attribute = attribute; + this.semantic = semantic; + this.geometry = geometry; + }; + + var Mesh = function() { + this.primitives = []; + this.materialsPending = []; + this.loadedGeometry = 0; + this.onCompleteCallbacks = []; + }; + + Mesh.prototype.addPrimitive = function(geometry, material) { + + var self = this; + geometry.onload = function() { + self.loadedGeometry ++; + self.checkComplete(); + }; + + this.primitives.push({ + geometry: geometry, + material: material, + mesh: null + }); + }; + + Mesh.prototype.onComplete = function(callback) { + this.onCompleteCallbacks.push(callback); + this.checkComplete(); + }; + + Mesh.prototype.checkComplete = function() { + var self = this; + if (this.onCompleteCallbacks.length && this.primitives.length == this.loadedGeometry) { + this.onCompleteCallbacks.forEach(function(callback) { + callback(self); + }); + this.onCompleteCallbacks = []; + } + }; + + Mesh.prototype.attachToNode = function(threeNode) { + // Assumes that the geometry is complete + this.primitives.forEach(function(primitive) { + /*if(!primitive.mesh) { + primitive.mesh = new THREE.Mesh(primitive.geometry, primitive.material); + }*/ + var material = primitive.material; + if (!(material instanceof THREE.Material)) { + material = theLoader.createShaderMaterial(material); + } + + var threeMesh = new THREE.Mesh(primitive.geometry.geometry, material); + threeMesh.castShadow = true; + threeNode.add(threeMesh); + }); + }; + + // Delayed-loaded material + var Material = function(params) { + this.params = params; + }; + + // Delegate for processing animation parameter buffers + var AnimationParameterDelegate = function() {}; + + AnimationParameterDelegate.prototype.handleError = function(errorCode, info) { + // FIXME: report error + console.log("ERROR(AnimationParameterDelegate):" + errorCode + ":" + info); + }; + + AnimationParameterDelegate.prototype.convert = function(resource, ctx) { + var parameter = ctx.parameter; + + var glResource = null; + switch (parameter.type) { + case WebGLRenderingContext.FLOAT : + case WebGLRenderingContext.FLOAT_VEC2 : + case WebGLRenderingContext.FLOAT_VEC3 : + case WebGLRenderingContext.FLOAT_VEC4 : + glResource = new Float32Array(resource, 0, parameter.count * componentsPerElementForGLType(parameter.type)); + break; + default: + break; + } + + return glResource; + }; + + AnimationParameterDelegate.prototype.resourceAvailable = function(glResource, ctx) { + var animation = ctx.animation; + var parameter = ctx.parameter; + parameter.data = glResource; + animation.handleParameterLoaded(parameter); + return true; + }; + + var animationParameterDelegate = new AnimationParameterDelegate(); + + var AnimationParameterContext = function(parameter, animation) { + this.parameter = parameter; + this.animation = animation; + }; + + // Animations + var Animation = function() { + + // create Three.js keyframe here + this.totalParameters = 0; + this.loadedParameters = 0; + this.parameters = {}; + this.finishedLoading = false; + this.onload = null; + + }; + + Animation.prototype.constructor = Animation; + + Animation.prototype.handleParameterLoaded = function(parameter) { + this.parameters[parameter.name] = parameter; + this.loadedParameters ++; + this.checkFinished(); + }; + + Animation.prototype.checkFinished = function() { + if (this.loadedParameters === this.totalParameters) { + // Build animation + this.finishedLoading = true; + + if (this.onload) { + this.onload(); + } + } + }; + + // Delegate for processing inverse bind matrices buffer + var InverseBindMatricesDelegate = function() {}; + + InverseBindMatricesDelegate.prototype.handleError = function(errorCode, info) { + // FIXME: report error + console.log("ERROR(InverseBindMatricesDelegate):" + errorCode + ":" + info); + }; + + InverseBindMatricesDelegate.prototype.convert = function(resource, ctx) { + var parameter = ctx.parameter; + + var glResource = null; + switch (parameter.type) { + case WebGLRenderingContext.FLOAT_MAT4 : + glResource = new Float32Array(resource, 0, parameter.count * componentsPerElementForGLType(parameter.type)); + break; + default: + break; + } + + return glResource; + }; + + InverseBindMatricesDelegate.prototype.resourceAvailable = function(glResource, ctx) { + var skin = ctx.skin; + skin.inverseBindMatrices = glResource; + return true; + }; + + var inverseBindMatricesDelegate = new InverseBindMatricesDelegate(); + + var InverseBindMatricesContext = function(param, skin) { + this.parameter = param; + this.skin = skin; + }; + + // Delegate for processing shaders from external files + var ShaderDelegate = function() {}; + + ShaderDelegate.prototype.handleError = function(errorCode, info) { + // FIXME: report error + console.log("ERROR(ShaderDelegate):" + errorCode + ":" + info); + }; + + ShaderDelegate.prototype.convert = function(resource, ctx) { + return resource; + }; + + ShaderDelegate.prototype.resourceAvailable = function(data, ctx) { + theLoader.shadersLoaded ++; + theLoader.shaders[ctx.id] = data; + theLoader.checkComplete(); + return true; + }; + + var shaderDelegate = new ShaderDelegate(); + + var ShaderContext = function(id, path) { + this.id = id; + this.path = path; + }; + + // Resource management + + var ResourceEntry = function(entryID, object, description) { + this.entryID = entryID; + this.object = object; + this.description = description; + }; + + var Resources = function() { + this._entries = {}; + }; + + Resources.prototype.setEntry = function(entryID, object, description) { + if (!entryID) { + console.error("No EntryID provided, cannot store", description); + return; + } + + if (this._entries[entryID]) { + console.warn("entry[" + entryID + "] is being overwritten"); + } + + this._entries[entryID] = new ResourceEntry(entryID, object, description ); + }; + + Resources.prototype.getEntry = function(entryID) { + return this._entries[entryID]; + }; + + Resources.prototype.clearEntries = function() { + this._entries = {}; + }; + + LoadDelegate = function() { + }; + + LoadDelegate.prototype.loadCompleted = function(callback, obj) { + callback.call(Window, obj); + }; + + // Loader + + var ThreeGLTFLoader = Object.create(glTFParser, { + + load: { + enumerable: true, + value: function(userInfo, options) { + this.resources = new Resources(); + this.cameras = []; + this.lights = []; + this.animations = []; + this.joints = {}; + this.skeltons = {}; + THREE.GLTFLoaderUtils.init(); + glTFParser.load.call(this, userInfo, options); + } + }, + + cameras: { + enumerable: true, + writable: true, + value : [] + }, + + lights: { + enumerable: true, + writable: true, + value : [] + }, + + animations: { + enumerable: true, + writable: true, + value : [] + }, + + // Implement WebGLTFLoader handlers + + handleBuffer: { + value: function(entryID, description, userInfo) { + this.resources.setEntry(entryID, null, description); + description.type = "ArrayBuffer"; + return true; + } + }, + + handleBufferView: { + value: function(entryID, description, userInfo) { + this.resources.setEntry(entryID, null, description); + + var buffer = this.resources.getEntry(description.buffer); + description.type = "ArrayBufferView"; + + var bufferViewEntry = this.resources.getEntry(entryID); + bufferViewEntry.buffer = buffer; + return true; + } + }, + + handleShader: { + value: function(entryID, description, userInfo) { + this.resources.setEntry(entryID, null, description); + var shaderRequest = { + id : entryID, + path : description.path, + }; + + var shaderContext = new ShaderContext(entryID, description.path); + + theLoader.shadersRequested ++; + THREE.GLTFLoaderUtils.getFile(shaderRequest, shaderDelegate, shaderContext); + + return true; + } + }, + + handleProgram: { + value: function(entryID, description, userInfo) { + this.resources.setEntry(entryID, null, description); + return true; + } + }, + + handleTechnique: { + value: function(entryID, description, userInfo) { + this.resources.setEntry(entryID, null, description); + return true; + } + }, + + createShaderMaterial : { + value: function(material) { + + var fragmentShader = theLoader.shaders[material.params.fragmentShader]; + if (!fragmentShader) { + console.log("ERROR: Missing fragment shader definition:", material.params.fragmentShader); + return new THREE.MeshPhongMaterial; + } + + var vertexShader = theLoader.shaders[material.params.vertexShader]; + if (!fragmentShader) { + console.log("ERROR: Missing vertex shader definition:", material.params.vertexShader); + return new THREE.MeshPhongMaterial; + } + + var uniforms = {}; + var shaderMaterial = new THREE.ShaderMaterial( { + + fragmentShader: fragmentShader, + vertexShader: vertexShader, + uniforms: uniforms, + + } ); + + return new THREE.MeshPhongMaterial(material.params); + } + }, + + createShaderParams : { + value: function(materialId, values, params, instanceProgram) { + var program = this.resources.getEntry(instanceProgram.program); + + if (program) { + params.fragmentShader = program.description.fragmentShader; + params.vertexShader = program.description.vertexShader; + params.attributes = instanceProgram.attributes; + params.uniforms = instanceProgram.uniforms; + } + } + }, + + threeJSMaterialType : { + value: function(materialId, technique, values, params) { + + var materialType = THREE.MeshPhongMaterial; + var defaultPass = null; + if (technique && technique.description && technique.description.passes) + defaultPass = technique.description.passes.defaultPass; + + if (defaultPass) { + if (defaultPass.details && defaultPass.details.commonProfile) { + var profile = technique.description.passes.defaultPass.details.commonProfile; + if (profile) + { + switch (profile.lightingModel) + { + case 'Blinn' : + case 'Phong' : + materialType = THREE.MeshPhongMaterial; + break; + + case 'Lambert' : + materialType = THREE.MeshLambertMaterial; + break; + + default : + materialType = THREE.MeshBasicMaterial; + break; + } + + if (profile.extras && profile.extras.doubleSided) + { + params.side = THREE.DoubleSide; + } + } + } + else if (defaultPass.instanceProgram) { + + var instanceProgram = defaultPass.instanceProgram; + + this.createShaderParams(materialId, values, params, instanceProgram); + + var loadshaders = true; + + if (loadshaders) { + materialType = Material; + } + } + } + + var texturePath = null; + var textureParams = null; + var diffuse = values.diffuse; + if (diffuse) + { + var texture = diffuse; + if (texture) { + var textureEntry = this.resources.getEntry(texture); + if (textureEntry) { + { + var imageEntry = this.resources.getEntry(textureEntry.description.source); + if (imageEntry) { + texturePath = imageEntry.description.path; + } + + var samplerEntry = this.resources.getEntry(textureEntry.description.sampler); + if (samplerEntry) { + textureParams = samplerEntry.description; + } + } + } + } + } + + var texture = LoadTexture(texturePath); + if (texture && textureParams) { + + if (textureParams.wrapS == WebGLRenderingContext.REPEAT) + texture.wrapS = THREE.RepeatWrapping; + + if (textureParams.wrapT == WebGLRenderingContext.REPEAT) + texture.wrapT = THREE.RepeatWrapping; + + if (textureParams.magFilter == WebGLRenderingContext.LINEAR) + texture.magFilter = THREE.LinearFilter; + +// if (textureParams.minFilter == "LINEAR") +// texture.minFilter = THREE.LinearFilter; + + params.map = texture; + } + + var envMapPath = null; + var envMapParams = null; + var reflective = values.reflective; + if (reflective) + { + var texture = reflective; + if (texture) { + var textureEntry = this.resources.getEntry(texture); + if (textureEntry) { + { + var imageEntry = this.resources.getEntry(textureEntry.description.source); + if (imageEntry) { + envMapPath = imageEntry.description.path; + } + + var samplerEntry = this.resources.getEntry(textureEntry.description.sampler); + if (samplerEntry) { + envMapParams = samplerEntry.description; + } + } + } + } + } + + var texture = LoadTexture(envMapPath); + if (texture && envMapParams) { + + if (envMapParams.wrapS == WebGLRenderingContext.REPEAT) + texture.wrapS = THREE.RepeatWrapping; + + if (envMapParams.wrapT == WebGLRenderingContext.REPEAT) + texture.wrapT = THREE.RepeatWrapping; + + if (envMapParams.magFilter == WebGLRenderingContext.LINEAR) + texture.magFilter = THREE.LinearFilter; + +// if (envMapParams.minFilter == WebGLRenderingContext.LINEAR) +// texture.minFilter = THREE.LinearFilter; + + params.envMap = texture; + } + + var shininess = values.shininesss || values.shininess; // N.B.: typo in converter! + if (shininess) + { + shininess = shininess; + } + + var diffuseColor = !texturePath ? diffuse : null; + var opacity = 1.0; + if (values.hasOwnProperty("transparency")) + { + var USE_A_ONE = true; // for now, hack because file format isn't telling us + opacity = USE_A_ONE ? values.transparency : (1.0 - values.transparency); + } + + // if (diffuseColor) diffuseColor = [0, 1, 0]; + + params.color = RgbArraytoHex(diffuseColor); + params.opacity = opacity; + params.transparent = opacity < 1.0; + // hack hack hack + if (texturePath && texturePath.toLowerCase().indexOf(".png") != -1) + params.transparent = true; + + if (!(shininess === undefined)) + { + params.shininess = shininess; + } + + if (!(values.emission === undefined)) + { + params.emissive = RgbArraytoHex(values.emission); + } + + if (!(values.specular === undefined)) + { + params.specular = RgbArraytoHex(values.specular); + } + + return materialType; + + } + }, + + handleMaterial: { + value: function(entryID, description, userInfo) { + //this should be rewritten using the meta datas that actually create the shader. + //here we will infer what needs to be pass to Three.js by looking inside the technique parameters. + var technique = this.resources.getEntry(description.instanceTechnique.technique); + var materialParams = {}; + var values = description.instanceTechnique.values; + + var materialType = this.threeJSMaterialType(entryID, technique, values, materialParams); + + var material = new materialType(materialParams); + + this.resources.setEntry(entryID, material, description); + + return true; + } + }, + + handleMesh: { + value: function(entryID, description, userInfo) { + var mesh = new Mesh(); + this.resources.setEntry(entryID, mesh, description); + var primitivesDescription = description.primitives; + if (!primitivesDescription) { + //FIXME: not implemented in delegate + console.log("MISSING_PRIMITIVES for mesh:" + entryID); + return false; + } + + for (var i = 0 ; i < primitivesDescription.length ; i ++) { + var primitiveDescription = primitivesDescription[i]; + + if (primitiveDescription.primitive === WebGLRenderingContext.TRIANGLES) { + + var geometry = new ClassicGeometry(); + var materialEntry = this.resources.getEntry(primitiveDescription.material); + + mesh.addPrimitive(geometry, materialEntry.object); + + var indices = this.resources.getEntry(primitiveDescription.indices); + var bufferEntry = this.resources.getEntry(indices.description.bufferView); + var indicesObject = { + bufferView : bufferEntry, + byteOffset : indices.description.byteOffset, + count : indices.description.count, + id : indices.entryID, + type : indices.description.type + }; + + var indicesContext = new IndicesContext(indicesObject, geometry); + var alreadyProcessedIndices = THREE.GLTFLoaderUtils.getBuffer(indicesObject, indicesDelegate, indicesContext); + /*if(alreadyProcessedIndices) { + indicesDelegate.resourceAvailable(alreadyProcessedIndices, indicesContext); + }*/ + + // Load Vertex Attributes + var allAttributes = Object.keys(primitiveDescription.attributes); + allAttributes.forEach( function(semantic) { + geometry.totalAttributes ++; + + var attribute; + var attributeID = primitiveDescription.attributes[semantic]; + var attributeEntry = this.resources.getEntry(attributeID); + if (!attributeEntry) { + //let's just use an anonymous object for the attribute + attribute = description.attributes[attributeID]; + attribute.id = attributeID; + this.resources.setEntry(attributeID, attribute, attribute); + + var bufferEntry = this.resources.getEntry(attribute.bufferView); + attributeEntry = this.resources.getEntry(attributeID); + + } else { + attribute = attributeEntry.object; + attribute.id = attributeID; + var bufferEntry = this.resources.getEntry(attribute.bufferView); + } + + var attributeObject = { + bufferView : bufferEntry, + byteOffset : attribute.byteOffset, + byteStride : attribute.byteStride, + count : attribute.count, + max : attribute.max, + min : attribute.min, + type : attribute.type, + id : attributeID + }; + + var attribContext = new VertexAttributeContext(attributeObject, semantic, geometry); + + var alreadyProcessedAttribute = THREE.GLTFLoaderUtils.getBuffer(attributeObject, vertexAttributeDelegate, attribContext); + /*if(alreadyProcessedAttribute) { + vertexAttributeDelegate.resourceAvailable(alreadyProcessedAttribute, attribContext); + }*/ + }, this); + } + } + return true; + } + }, + + handleCamera: { + value: function(entryID, description, userInfo) { + var camera; + if (description.type == "perspective") + { + var znear = description.perspective.znear; + var zfar = description.perspective.zfar; + var yfov = description.perspective.yfov; + var xfov = description.perspective.xfov; + var aspect_ratio = description.perspective.aspect_ratio; + + if (!aspect_ratio) + aspect_ratio = 1; + + if (yfov === undefined) + { + if (xfov) + { + // According to COLLADA spec... + // aspect_ratio = xfov / yfov + yfov = xfov / aspect_ratio; + } + + } + + if (yfov) + { + camera = new THREE.PerspectiveCamera(yfov, aspect_ratio, znear, zfar); + } + } + else + { + camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, znear, zfar ); + } + + if (camera) + { + this.resources.setEntry(entryID, camera, description); + } + + return true; + } + }, + + handleLight: { + value: function(entryID, description, userInfo) { + + var light = null; + var type = description.type; + if (type && description[type]) + { + var lparams = description[type]; + var color = RgbArraytoHex(lparams.color); + + switch (type) { + case "directional" : + light = new THREE.DirectionalLight(color); + light.position.set(0, 0, 1); + break; + + case "point" : + light = new THREE.PointLight(color); + break; + + case "spot " : + light = new THREE.SpotLight(color); + light.position.set(0, 0, 1); + break; + + case "ambient" : + light = new THREE.AmbientLight(color); + break; + } + } + + if (light) + { + this.resources.setEntry(entryID, light, description); + } + + return true; + } + }, + + addPendingMesh: { + value: function(mesh, threeNode) { + theLoader.pendingMeshes.push({ + mesh: mesh, + node: threeNode + }); + } + }, + + handleNode: { + value: function(entryID, description, userInfo) { + + var threeNode = null; + if (description.jointId) { + threeNode = new THREE.Bone(); + threeNode.jointId = description.jointId; + this.joints[description.jointId] = entryID; + } + else { + threeNode = new THREE.Object3D(); + } + + threeNode.name = description.name; + + this.resources.setEntry(entryID, threeNode, description); + + var m = description.matrix; + if (m) { + threeNode.applyMatrix(new THREE.Matrix4().fromArray( m )); + threeNode.matrixAutoUpdate = false; + threeNode.matrixWorldNeedsUpdate = true; + } + else { + var t = description.translation; + var r = description.rotation; + var s = description.scale; + + var position = t ? new THREE.Vector3(t[0], t[1], t[2]) : + new THREE.Vector3; + if (r) { + convertAxisAngleToQuaternion(r, 1); + } + var rotation = r ? new THREE.Quaternion(r[0], r[1], r[2], r[3]) : + new THREE.Quaternion; + var scale = s ? new THREE.Vector3(s[0], s[1], s[2]) : + new THREE.Vector3; + + var matrix = new THREE.Matrix4; + matrix.compose(position, rotation, scale); + threeNode.matrixAutoUpdate = false; + threeNode.matrixWorldNeedsUpdate = true; + threeNode.applyMatrix(matrix); + } + + var self = this; + + // Iterate through all node meshes and attach the appropriate objects + //FIXME: decision needs to be made between these 2 ways, probably meshes will be discarded. + var meshEntry; + if (description.mesh) { + meshEntry = this.resources.getEntry(description.mesh); + theLoader.meshesRequested ++; + meshEntry.object.onComplete(function(mesh) { + self.addPendingMesh(mesh, threeNode); + theLoader.meshesLoaded ++; + theLoader.checkComplete(); + }); + } + + if (description.meshes) { + description.meshes.forEach( function(meshID) { + meshEntry = this.resources.getEntry(meshID); + theLoader.meshesRequested ++; + meshEntry.object.onComplete(function(mesh) { + self.addPendingMesh(mesh, threeNode); + theLoader.meshesLoaded ++; + theLoader.checkComplete(); + }); + }, this); + } + + if (description.instanceSkin) { + + var skinEntry = this.resources.getEntry(description.instanceSkin.skin); + + if (skinEntry) { + + var skin = skinEntry.object; + description.instanceSkin.skin = skin; + threeNode.instanceSkin = description.instanceSkin; + + var sources = description.instanceSkin.sources; + skin.meshes = []; + sources.forEach( function(meshID) { + meshEntry = this.resources.getEntry(meshID); + theLoader.meshesRequested ++; + meshEntry.object.onComplete(function(mesh) { + + skin.meshes.push(mesh); + theLoader.meshesLoaded ++; + theLoader.checkComplete(); + }); + }, this); + + } + } + + if (description.camera) { + var cameraEntry = this.resources.getEntry(description.camera); + if (cameraEntry) { + threeNode.add(cameraEntry.object); + this.cameras.push(cameraEntry.object); + } + } + + if (description.light) { + var lightEntry = this.resources.getEntry(description.light); + if (lightEntry) { + threeNode.add(lightEntry.object); + this.lights.push(lightEntry.object); + } + } + + return true; + } + }, + + buildNodeHirerachy: { + value: function(nodeEntryId, parentThreeNode) { + var nodeEntry = this.resources.getEntry(nodeEntryId); + var threeNode = nodeEntry.object; + parentThreeNode.add(threeNode); + + var children = nodeEntry.description.children; + if (children) { + children.forEach( function(childID) { + this.buildNodeHirerachy(childID, threeNode); + }, this); + } + + return threeNode; + } + }, + + buildSkin: { + value: function(node) { + + var skin = node.instanceSkin.skin; + if (skin) { + node.instanceSkin.skeletons.forEach(function(skeleton) { + var nodeEntry = this.resources.getEntry(skeleton); + if (nodeEntry) { + + var rootSkeleton = nodeEntry.object; + + var dobones = true; + + var i, len = skin.meshes.length; + for (i = 0; i < len; i ++) { + var mesh = skin.meshes[i]; + var threeMesh = null; + mesh.primitives.forEach(function(primitive) { + + var material = primitive.material; + if (!(material instanceof THREE.Material)) { + material = this.createShaderMaterial(material); + } + + threeMesh = new THREE.SkinnedMesh(primitive.geometry.geometry, material, false); + threeMesh.add(rootSkeleton); + + var geometry = primitive.geometry.geometry; + var j; + if (geometry.vertices) { + for ( j = 0; j < geometry.vertices.length; j ++ ) { + geometry.vertices[j].applyMatrix4( skin.bindShapeMatrix ); + } + } + else if (geometry.attributes.position) { + var a = geometry.attributes.position.array; + var v = new THREE.Vector3; + for ( j = 0; j < a.length / 3; j ++ ) { + v.set(a[j * 3], a[j * 3 + 1], a[j * 3 + 2]); + v.applyMatrix4( skin.bindShapeMatrix ); + a[j * 3] = v.x; + a[j * 3 + 1] = v.y; + a[j * 3 + 2] = v.z; + } + } + + if (threeMesh && dobones) { + + material.skinning = true; + + threeMesh.boneInverses = []; + var jointsIds = skin.jointsIds; + var bones = []; + var boneInverses = []; + var i, len = jointsIds.length; + for (i = 0; i < len; i ++) { + var jointId = jointsIds[i]; + var nodeForJoint = this.joints[jointId]; + var joint = this.resources.getEntry(nodeForJoint).object; + if (joint) { + + joint.skin = threeMesh; + bones.push(joint); + + var m = skin.inverseBindMatrices; + var mat = new THREE.Matrix4().set( + m[i * 16 + 0], m[i * 16 + 4], m[i * 16 + 8], m[i * 16 + 12], + m[i * 16 + 1], m[i * 16 + 5], m[i * 16 + 9], m[i * 16 + 13], + m[i * 16 + 2], m[i * 16 + 6], m[i * 16 + 10], m[i * 16 + 14], + m[i * 16 + 3], m[i * 16 + 7], m[i * 16 + 11], m[i * 16 + 15] + ); + boneInverses.push(mat); + + } else { + console.log("WARNING: jointId:" + jointId + " cannot be found in skeleton:" + skeleton); + } + } + + threeMesh.bind( new THREE.Skeleton( bones, boneInverses, false ), threeMesh.matrixWorld ); + } + + if (threeMesh) { + threeMesh.castShadow = true; + node.add(threeMesh); + } + + }, this); + } + + } + + + }, this); + + } + } + }, + + buildSkins: { + value: function(node) { + + if (node.instanceSkin) + this.buildSkin(node); + + var children = node.children; + if (children) { + children.forEach( function(child) { + this.buildSkins(child); + }, this); + } + } + }, + + createMeshAnimations : { + value : function(root) { + this.buildSkins(root); + } + }, + + handleScene: { + value: function(entryID, description, userInfo) { + + if (!description.nodes) { + console.log("ERROR: invalid file required nodes property is missing from scene"); + return false; + } + + description.nodes.forEach( function(nodeUID) { + this.buildNodeHirerachy(nodeUID, userInfo.rootObj); + }, this); + + if (this.delegate) { + this.delegate.loadCompleted(userInfo.callback, userInfo.rootObj); + } + + return true; + } + }, + + handleImage: { + value: function(entryID, description, userInfo) { + this.resources.setEntry(entryID, null, description); + return true; + } + }, + + addNodeAnimationChannel : { + value : function(name, channel, interp) { + if (!this.nodeAnimationChannels) + this.nodeAnimationChannels = {}; + + if (!this.nodeAnimationChannels[name]) { + this.nodeAnimationChannels[name] = []; + } + + this.nodeAnimationChannels[name].push(interp); + }, + }, + + createAnimations : { + value : function() { + for (var name in this.nodeAnimationChannels) { + var nodeAnimationChannels = this.nodeAnimationChannels[name]; + var i, len = nodeAnimationChannels.length; + //console.log(" animation channels for node " + name); + //for (i = 0; i < len; i++) { + // console.log(nodeAnimationChannels[i]); + //} + var anim = new THREE.glTFAnimation(nodeAnimationChannels); + anim.name = "animation_" + name; + this.animations.push(anim); + } + } + }, + + buildAnimation: { + value : function(animation) { + + var interps = []; + var i, len = animation.channels.length; + for (i = 0; i < len; i ++) { + + var channel = animation.channels[i]; + var sampler = animation.samplers[channel.sampler]; + if (sampler) { + + var input = animation.parameters[sampler.input]; + if (input && input.data) { + + var output = animation.parameters[sampler.output]; + if (output && output.data) { + + var target = channel.target; + var node = this.resources.getEntry(target.id); + if (node) { + + var path = target.path; + + if (path == "rotation") + { + convertAxisAngleToQuaternion(output.data, output.count); + } + + var interp = { + keys : input.data, + values : output.data, + count : input.count, + target : node.object, + path : path, + type : sampler.interpolation + }; + + this.addNodeAnimationChannel(target.id, channel, interp); + interps.push(interp); + } + } + } + } + } + } + }, + + handleAnimation: { + value: function(entryID, description, userInfo) { + + var self = this; + theLoader.animationsRequested ++; + var animation = new Animation(); + animation.name = entryID; + animation.onload = function() { + // self.buildAnimation(animation); + theLoader.animationsLoaded ++; + theLoader.animations.push(animation); + theLoader.checkComplete(); + }; + + animation.channels = description.channels; + animation.samplers = description.samplers; + this.resources.setEntry(entryID, animation, description); + var parameters = description.parameters; + if (!parameters) { + //FIXME: not implemented in delegate + console.log("MISSING_PARAMETERS for animation:" + entryID); + return false; + } + + // Load parameter buffers + var params = Object.keys(parameters); + params.forEach( function(param) { + + animation.totalParameters ++; + var parameter = parameters[param]; + var accessor = this.resources.getEntry(parameter); + if (!accessor) + debugger; + accessor = accessor.object; + var bufferView = this.resources.getEntry(accessor.bufferView); + var paramObject = { + bufferView : bufferView, + byteOffset : accessor.byteOffset, + count : accessor.count, + type : accessor.type, + id : accessor.bufferView, + name : param + }; + + var paramContext = new AnimationParameterContext(paramObject, animation); + + var alreadyProcessedAttribute = THREE.GLTFLoaderUtils.getBuffer(paramObject, animationParameterDelegate, paramContext); + /*if(alreadyProcessedAttribute) { + vertexAttributeDelegate.resourceAvailable(alreadyProcessedAttribute, attribContext); + }*/ + }, this); + + return true; + } + }, + + handleAccessor: { + value: function(entryID, description, userInfo) { + // Save attribute entry + this.resources.setEntry(entryID, description, description); + return true; + } + }, + + handleSkin: { + value: function(entryID, description, userInfo) { + // Save skin entry + + var skin = { + }; + + var m = description.bindShapeMatrix; + skin.bindShapeMatrix = new THREE.Matrix4().fromArray( m ); + + skin.jointsIds = description.joints; + var inverseBindMatricesDescription = description.inverseBindMatrices; + skin.inverseBindMatricesDescription = inverseBindMatricesDescription; + skin.inverseBindMatricesDescription.id = entryID + "_inverseBindMatrices"; + + var bufferEntry = this.resources.getEntry(inverseBindMatricesDescription.bufferView); + + var paramObject = { + bufferView : bufferEntry, + byteOffset : inverseBindMatricesDescription.byteOffset, + count : inverseBindMatricesDescription.count, + type : inverseBindMatricesDescription.type, + id : inverseBindMatricesDescription.bufferView, + name : skin.inverseBindMatricesDescription.id + }; + + var context = new InverseBindMatricesContext(paramObject, skin); + + var alreadyProcessedAttribute = THREE.GLTFLoaderUtils.getBuffer(paramObject, inverseBindMatricesDelegate, context); + + var bufferView = this.resources.getEntry(skin.inverseBindMatricesDescription.bufferView); + skin.inverseBindMatricesDescription.bufferView = + bufferView.object; + this.resources.setEntry(entryID, skin, description); + return true; + } + }, + + handleSampler: { + value: function(entryID, description, userInfo) { + // Save attribute entry + this.resources.setEntry(entryID, description, description); + return true; + } + }, + + handleTexture: { + value: function(entryID, description, userInfo) { + // Save attribute entry + this.resources.setEntry(entryID, null, description); + return true; + } + }, + + handleError: { + value: function(msg) { + + throw new Error(msg); + return true; + } + }, + + _delegate: { + value: new LoadDelegate, + writable: true + }, + + delegate: { + enumerable: true, + get: function() { + return this._delegate; + }, + set: function(value) { + this._delegate = value; + } + } + }); + + + // Loader + + var Context = function(rootObj, callback) { + this.rootObj = rootObj; + this.callback = callback; + }; + + var rootObj = new THREE.Object3D(); + + var self = this; + + var loader = Object.create(ThreeGLTFLoader); + loader.initWithPath(url); + loader.load(new Context(rootObj, + function(obj) { + }), + null); + + this.loader = loader; + this.callback = callback; + this.rootObj = rootObj; + return rootObj; +}; + +THREE.glTFLoader.prototype.callLoadedCallback = function() { + var result = { + scene : this.rootObj, + cameras : this.loader.cameras, + animations : this.loader.animations, + }; + + this.callback(result); +}; + +THREE.glTFLoader.prototype.checkComplete = function() { + if (this.meshesLoaded == this.meshesRequested + && this.shadersLoaded == this.shadersRequested + && this.animationsLoaded == this.animationsRequested) + { + + for (var i = 0; i < this.pendingMeshes.length; i ++) { + var pending = this.pendingMeshes[i]; + pending.mesh.attachToNode(pending.node); + } + + for (var i = 0; i < this.animationsLoaded; i ++) { + var animation = this.animations[i]; + this.loader.buildAnimation(animation); + } + + this.loader.createAnimations(); + this.loader.createMeshAnimations(this.rootObj); + + this.callLoadedCallback(); + } +}; diff --git a/node_modules/three/examples/js/loaders/gltf/glTFLoaderUtils.js b/node_modules/three/examples/js/loaders/gltf/glTFLoaderUtils.js new file mode 100644 index 00000000..6493165e --- /dev/null +++ b/node_modules/three/examples/js/loaders/gltf/glTFLoaderUtils.js @@ -0,0 +1,220 @@ +/** + * @author Tony Parisi / http://www.tonyparisi.com/ + */ + +THREE.GLTFLoaderUtils = Object.create(Object, { + + // errors + MISSING_DESCRIPTION: { value: "MISSING_DESCRIPTION" }, + INVALID_PATH: { value: "INVALID_PATH" }, + INVALID_TYPE: { value: "INVALID_TYPE" }, + XMLHTTPREQUEST_STATUS_ERROR: { value: "XMLHTTPREQUEST_STATUS_ERROR" }, + NOT_FOUND: { value: "NOT_FOUND" }, + // misc constants + ARRAY_BUFFER: { value: "ArrayBuffer" }, + + _streams : { value:{}, writable: true }, + + _streamsStatus: { value: {}, writable: true }, + + _resources: { value: {}, writable: true }, + + _resourcesStatus: { value: {}, writable: true }, + + // initialization + init: { + value: function() { + this._streams = {}; + this._streamsStatus = {}; + this._resources = {}; + this._resourcesStatus = {}; + } + }, + + //manage entries + _containsResource: { + enumerable: false, + value: function(resourceID) { + return this._resources[resourceID] ? true : false; + } + }, + + _storeResource: { + enumerable: false, + value: function(resourceID, resource) { + if (!resourceID) { + console.log("ERROR: entry does not contain id, cannot store"); + return; + } + + if (this._containsResource[resourceID]) { + console.log("WARNING: resource:" + resourceID + " is already stored, overriding"); + } + + this._resources[resourceID] = resource; + } + }, + + _getResource: { + enumerable: false, + value: function(resourceID) { + return this._resources[resourceID]; + } + }, + + _loadStream: { + value: function(path, type, delegate) { + var self = this; + + if (!type) { + delegate.handleError(THREE.GLTFLoaderUtils.INVALID_TYPE, null); + return; + } + + if (!path) { + delegate.handleError(THREE.GLTFLoaderUtils.INVALID_PATH); + return; + } + + var xhr = new XMLHttpRequest(); + xhr.open('GET', path, true); + xhr.responseType = (type === this.ARRAY_BUFFER) ? "arraybuffer" : "text"; + + //if this is not specified, 1 "big blob" scenes fails to load. + xhr.setRequestHeader("If-Modified-Since", "Sat, 01 Jan 1970 00:00:00 GMT"); + xhr.addEventListener( 'load', function ( event ) { + delegate.streamAvailable(path, xhr.response); + }, false ); + xhr.addEventListener( 'error', function ( event ) { + delegate.handleError(THREE.GLTFLoaderUtils.XMLHTTPREQUEST_STATUS_ERROR, xhr.status); + }, false ); + xhr.send(null); + } + }, + + send: { value: 0, writable: true }, + requested: { value: 0, writable: true }, + + _handleRequest: { + value: function(request) { + var resourceStatus = this._resourcesStatus[request.id]; + if (resourceStatus) + { + this._resourcesStatus[request.id] ++; + } + else + { + this._resourcesStatus[request.id] = 1; + } + + var streamStatus = this._streamsStatus[request.path]; + if (streamStatus && streamStatus.status === "loading" ) + { + streamStatus.requests.push(request); + return; + } + + this._streamsStatus[request.path] = { status : "loading", requests : [ request ] }; + + var self = this; + var processResourceDelegate = {}; + + processResourceDelegate.streamAvailable = function(path, res_) { + var streamStatus = self._streamsStatus[path]; + var requests = streamStatus.requests; + requests.forEach( function(req_) { + var subArray = res_.slice(req_.range[0], req_.range[1]); + var convertedResource = req_.delegate.convert(subArray, req_.ctx); + self._storeResource(req_.id, convertedResource); + req_.delegate.resourceAvailable(convertedResource, req_.ctx); + -- self._resourcesStatus[req_.id]; + + }, this); + + delete self._streamsStatus[path]; + + }; + + processResourceDelegate.handleError = function(errorCode, info) { + request.delegate.handleError(errorCode, info); + }; + + this._loadStream(request.path, request.type, processResourceDelegate); + } + }, + + + _elementSizeForGLType: { + value: function(glType) { + switch (glType) { + case WebGLRenderingContext.FLOAT : + return Float32Array.BYTES_PER_ELEMENT; + case WebGLRenderingContext.UNSIGNED_BYTE : + return Uint8Array.BYTES_PER_ELEMENT; + case WebGLRenderingContext.UNSIGNED_SHORT : + return Uint16Array.BYTES_PER_ELEMENT; + case WebGLRenderingContext.FLOAT_VEC2 : + return Float32Array.BYTES_PER_ELEMENT * 2; + case WebGLRenderingContext.FLOAT_VEC3 : + return Float32Array.BYTES_PER_ELEMENT * 3; + case WebGLRenderingContext.FLOAT_VEC4 : + return Float32Array.BYTES_PER_ELEMENT * 4; + case WebGLRenderingContext.FLOAT_MAT3 : + return Float32Array.BYTES_PER_ELEMENT * 9; + case WebGLRenderingContext.FLOAT_MAT4 : + return Float32Array.BYTES_PER_ELEMENT * 16; + default: + return null; + } + } + }, + + _handleWrappedBufferViewResourceLoading: { + value: function(wrappedBufferView, delegate, ctx) { + var bufferView = wrappedBufferView.bufferView; + var buffer = bufferView.buffer; + var byteOffset = wrappedBufferView.byteOffset + bufferView.description.byteOffset; + var range = [ byteOffset, (this._elementSizeForGLType(wrappedBufferView.type) * wrappedBufferView.count) + byteOffset ]; + + this._handleRequest({ "id" : wrappedBufferView.id, + "range" : range, + "type" : buffer.description.type, + "path" : buffer.description.path, + "delegate" : delegate, + "ctx" : ctx }, null); + } + }, + + getBuffer: { + + value: function(wrappedBufferView, delegate, ctx) { + + var savedBuffer = this._getResource(wrappedBufferView.id); + if (savedBuffer) { + return savedBuffer; + } else { + this._handleWrappedBufferViewResourceLoading(wrappedBufferView, delegate, ctx); + } + + return null; + } + }, + + getFile: { + + value: function(request, delegate, ctx) { + + request.delegate = delegate; + request.ctx = ctx; + + this._handleRequest({ "id" : request.id, + "path" : request.path, + "range" : [ 0 ], + "type" : "text", + "delegate" : delegate, + "ctx" : ctx }, null); + + return null; +} + }, +}); diff --git a/node_modules/three/examples/js/loaders/sea3d/SEA3D.js b/node_modules/three/examples/js/loaders/sea3d/SEA3D.js new file mode 100644 index 00000000..e0558feb --- /dev/null +++ b/node_modules/three/examples/js/loaders/sea3d/SEA3D.js @@ -0,0 +1,4807 @@ +/** + * SEA3D SDK + * @author Sunag / http://www.sunag.com.br/ + */ + +'use strict'; + +var SEA3D = { VERSION : 17000 } + +SEA3D.getVersion = function() { + + // Max = 16777215 - VVSSBB | V = Version | S = Subversion | B = Buildversion + var v = SEA3D.VERSION.toString(), l = v.length; + return v.substring( 0, l - 4 ) + "." + v.substring( l - 4, l - 3 ) + "." + v.substring( l - 3, l - 2 ) + "." + parseFloat( v.substring( l - 2, l ) ).toString(); + +}; + +console.log( 'SEA3D ' + SEA3D.getVersion() ); + +// +// STREAM : STANDARD DATA-IO ( LITTLE-ENDIAN ) +// + +SEA3D.Stream = function( buffer ) { + + this.position = 0; + this.buffer = buffer || new ArrayBuffer(); + +}; + +SEA3D.Stream.NONE = 0; + +// 1D = 0 at 31 +SEA3D.Stream.BOOLEAN = 1; + +SEA3D.Stream.BYTE = 2; +SEA3D.Stream.UBYTE = 3; + +SEA3D.Stream.SHORT = 4; +SEA3D.Stream.USHORT = 5; + +SEA3D.Stream.INT24 = 6; +SEA3D.Stream.UINT24 = 7; + +SEA3D.Stream.INT = 8; +SEA3D.Stream.UINT = 9; + +SEA3D.Stream.FLOAT = 10; +SEA3D.Stream.DOUBLE = 11; +SEA3D.Stream.DECIMAL = 12; + +// 2D = 32 at 63 + +// 3D = 64 at 95 +SEA3D.Stream.VECTOR3D = 74; + +// 4D = 96 at 127 +SEA3D.Stream.VECTOR4D = 106; + +// Undefined Size = 128 at 255 +SEA3D.Stream.STRING_TINY = 128; +SEA3D.Stream.STRING_SHORT = 129; +SEA3D.Stream.STRING_LONG = 130; + +SEA3D.Stream.ASSET = 200; +SEA3D.Stream.GROUP = 255; + +SEA3D.Stream.BLEND_MODE = [ + "normal", "add", "subtract", "multiply", "dividing", "alpha", "screen", "darken", + "overlay", "colorburn", "linearburn", "lighten", "colordodge", "lineardodge", + "softlight", "hardlight", "pinlight", "spotlight", "spotlightblend", "hardmix", + "average", "difference", "exclusion", "hue", "saturation", "color", "value" +]; + +SEA3D.Stream.INTERPOLATION_TABLE = [ + "normal", "linear", + "sine.in", "sine.out", "sine.inout", + "cubic.in", "cubic.out", "cubic.inout", + "quint.in", "quint.out", "quint.inout", + "circ.in", "circ.out", "circ.inout", + "back.in", "back.out", "back.inout", + "quad.in", "quad.out", "quad.inout", + "quart.in", "quart.out", "quart.inout", + "expo.in", "expo.out", "expo.inout", + "elastic.in", "elastic.out", "elastic.inout", + "bounce.in", "bounce.out", "bounce.inout" +]; + +SEA3D.Stream.sizeOf = function( kind ) { + + if ( kind == 0 ) return 0; + else if ( kind >= 1 && kind <= 31 ) return 1; + else if ( kind >= 32 && kind <= 63 ) return 2; + else if ( kind >= 64 && kind <= 95 ) return 3; + else if ( kind >= 96 && kind <= 125 ) return 4; + return - 1; + +}; + +SEA3D.Stream.prototype = { + constructor: SEA3D.Stream, + + set buffer ( val ) { + + this.buf = val; + this.length = val.byteLength; + this.data = new DataView( val ); + + }, + + get buffer () { + + return this.buf; + + }, + + get bytesAvailable () { + + return this.length - this.position; + + } +}; + +SEA3D.Stream.prototype.readBytes = function( len ) { + + var buf = this.buf.slice( this.position, this.position + len ); + this.position += len; + return buf; + +}; + +SEA3D.Stream.prototype.readByte = function() { + + return this.data.getInt8( this.position ++ ); + +}; + +SEA3D.Stream.prototype.readUByte = function() { + + return this.data.getUint8( this.position ++ ); + +}; + +SEA3D.Stream.prototype.readBool = function() { + + return this.data.getInt8( this.position ++ ) != 0; + +}; + +SEA3D.Stream.prototype.readShort = function() { + + var v = this.data.getInt16( this.position, true ); + this.position += 2; + return v; + +}; + +SEA3D.Stream.prototype.readUShort = function() { + + var v = this.data.getUint16( this.position, true ); + this.position += 2; + return v; + +}; + +SEA3D.Stream.prototype.readUInt24 = function() { + + return this.readUShort() | ( this.readUByte() << 16 ); + +}; + +SEA3D.Stream.prototype.readInt = function() { + + var v = this.data.getInt32( this.position, true ); + this.position += 4; + return v; + +}; + +SEA3D.Stream.prototype.readUInt = function() { + + var v = this.data.getUint32( this.position, true ); + this.position += 4; + return v; + +}; + +SEA3D.Stream.prototype.readFloat = function() { + + var v = this.data.getFloat32( this.position, true ); + this.position += 4; + return v; + +}; + +SEA3D.Stream.prototype.readUInteger = function() { + + var v = this.readUByte(), + r = v & 0x7F; + + if ( ( v & 0x80 ) != 0 ) { + + v = this.readUByte(); + r |= ( v & 0x7F ) << 7; + + if ( ( v & 0x80 ) != 0 ) { + + v = this.readUByte(); + r |= ( v & 0x7F ) << 13; + + } + + } + + return r; + +}; + +SEA3D.Stream.prototype.readVector2 = function() { + + return { x: this.readFloat(), y: this.readFloat() } + +}; + +SEA3D.Stream.prototype.readVector3 = function() { + + return { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() } + +}; + +SEA3D.Stream.prototype.readVector4 = function() { + + return { x: this.readFloat(), y: this.readFloat(), z: this.readFloat(), w: this.readFloat() } + +}; + +SEA3D.Stream.prototype.readMatrix = function() { + + var mtx = new Float32Array( 16 ); + + mtx[ 0 ] = this.readFloat(); + mtx[ 1 ] = this.readFloat(); + mtx[ 2 ] = this.readFloat(); + mtx[ 3 ] = 0.0; + mtx[ 4 ] = this.readFloat(); + mtx[ 5 ] = this.readFloat(); + mtx[ 6 ] = this.readFloat(); + mtx[ 7 ] = 0.0; + mtx[ 8 ] = this.readFloat(); + mtx[ 9 ] = this.readFloat(); + mtx[ 10 ] = this.readFloat(); + mtx[ 11 ] = 0.0; + mtx[ 12 ] = this.readFloat(); + mtx[ 13 ] = this.readFloat(); + mtx[ 14 ] = this.readFloat(); + mtx[ 15 ] = 1.0; + + return mtx; + +}; + +SEA3D.Stream.prototype.readUTF = function( len ) { + + return String.fromCharCode.apply( undefined, new Uint16Array( new Uint8Array( this.readBytes( len ) ) ) ); + +}; + +SEA3D.Stream.prototype.readExt = function() { + + return this.readUTF( 4 ).replace( /\0/g, "" ); + +}; + +SEA3D.Stream.prototype.readUTF8 = function() { + + return this.readUTF( this.readUByte() ); + +}; + +SEA3D.Stream.prototype.readUTF8Short = function() { + + return this.readUTF( this.readUShort() ); + +}; + +SEA3D.Stream.prototype.readUTF8Long = function() { + + return this.readUTF( this.readUInt() ); + +}; + +SEA3D.Stream.prototype.readUByteArray = function( length ) { + + var v = new Uint8Array( length ); + + SEA3D.Stream.memcpy( + v.buffer, + 0, + this.buffer, + this.position, + length + ); + + this.position += length; + + return v; + +}; + +SEA3D.Stream.prototype.readUShortArray = function( length ) { + + var v = new Uint16Array( length ), + len = length * 2; + + SEA3D.Stream.memcpy( + v.buffer, + 0, + this.buffer, + this.position, + len + ); + + this.position += len; + + return v; + +}; + +SEA3D.Stream.prototype.readUIntArray = function( length ) { + + var v = new Uint32Array( length ), + len = length * 2; + + SEA3D.Stream.memcpy( + v.buffer, + 0, + this.buffer, + this.position, + len + ); + + this.position += len; + + return v; + +}; + +SEA3D.Stream.prototype.readFloatArray = function( length ) { + + var v = new Float32Array( length ), + len = length * 4; + + SEA3D.Stream.memcpy( + v.buffer, + 0, + this.buffer, + this.position, + len + ); + + this.position += len; + + return v; + +}; + + +SEA3D.Stream.prototype.readBlendMode = function() { + + return SEA3D.Stream.BLEND_MODE[ this.readUByte() ]; + +}; + +SEA3D.Stream.prototype.readInterpolation = function() { + + return SEA3D.Stream.INTERPOLATION_TABLE[ this.readUByte() ]; + +}; + +SEA3D.Stream.prototype.readTags = function( callback ) { + + var numTag = this.readUByte(); + + for ( var i = 0; i < numTag; ++ i ) { + + var kind = this.readUShort(); + var size = this.readUInt(); + var pos = this.position; + + callback( kind, data, size ); + + this.position = pos += size; + + } + +}; + +SEA3D.Stream.prototype.readProperties = function( sea ) { + + var count = this.readUByte(), + props = {}, types = {}; + + props.__type = types; + + for ( var i = 0; i < count; i ++ ) { + + var name = this.readUTF8(), + type = this.readUByte(); + + props[ name ] = this.readToken( type, sea ); + types[ name ] = type; + + } + + return props; + +}; + +SEA3D.Stream.prototype.readAnimationList = function( sea ) { + + var list = [], + count = this.readUByte(); + + var i = 0; + while ( i < count ) { + + var attrib = this.readUByte(), + anm = {}; + + anm.relative = ( attrib & 1 ) != 0; + + if ( attrib & 2 ) anm.timeScale = this.readFloat(); + + anm.tag = sea.getObject( this.readUInt() ); + + list[ i ++ ] = anm; + + } + + return list; + +}; + +SEA3D.Stream.prototype.readScriptList = function( sea ) { + + var list = [], + count = this.readUByte(); + + var i = 0; + while ( i < count ) { + + var attrib = this.readUByte(), + script = {}; + + script.priority = ( attrib & 1 ) | ( attrib & 2 ); + + if ( attrib & 4 ) { + + var numParams = data.readUByte(); + + script.params = {}; + + for ( var j = 0; j < numParams; j ++ ) { + + var name = this.readUTF8(); + + script.params[ name ] = this.readObject( sea ); + + } + + } + + if ( attrib & 8 ) { + + script.method = this.readUTF8(); + + } + + script.tag = sea.getObject( this.readUInt() ); + + list[ i ++ ] = script; + + } + + return list; + +}; + +SEA3D.Stream.prototype.readObject = function( sea ) { + + return this.readToken( this.readUByte(), sea ); + +}; + +SEA3D.Stream.prototype.readToken = function( type, sea ) { + + switch ( type ) + { + // 1D + case SEA3D.Stream.BOOLEAN: + return this.readBool(); + break; + + case SEA3D.Stream.UBYTE: + return this.readUByte(); + break; + + case SEA3D.Stream.USHORT: + return this.readUShort(); + break; + + case SEA3D.Stream.UINT24: + return this.readUInt24(); + break; + + case SEA3D.Stream.INT: + return this.readInt(); + break; + + case SEA3D.Stream.UINT: + return this.readUInt(); + break; + + case SEA3D.Stream.FLOAT: + return this.readFloat(); + break; + + // 3D + case SEA3D.Stream.VECTOR3D: + return this.readVector3(); + break; + + // 4D + case SEA3D.Stream.VECTOR4D: + return this.readVector4(); + break; + + // Undefined Values + case SEA3D.Stream.STRING_TINY: + return data.readUTF8(); + break; + + case SEA3D.Stream.STRING_SHORT: + return this.readUTF8Short(); + break; + + case SEA3D.Stream.STRING_LONG: + return v.readUTF8Long(); + break + + case SEA3D.Stream.ASSET: + var asset = this.readUInt(); + return asset > 0 ? sea.getObject( asset - 1 ).tag : null; + break; + + default: + console.error( "DataType not found!" ); + break; + } + + return null; + +}; + +SEA3D.Stream.prototype.readVector = function( type, length, offset ) { + + var size = SEA3D.Stream.sizeOf( type ), + i = offset * size, + count = i + ( length * size ); + + switch ( type ) + { + // 1D + case SEA3D.Stream.BOOLEAN: + + return this.readUByteArray( count ); + + + case SEA3D.Stream.UBYTE: + + return this.readUByteArray( count ); + + + case SEA3D.Stream.USHORT: + + return this.readUShortArray( count ); + + + case SEA3D.Stream.UINT24: + + return this.readUInt24Array( count ); + + + case SEA3D.Stream.UINT: + + return this.readUIntArray( count ); + + + case SEA3D.Stream.FLOAT: + + return this.readFloatArray( count ); + + + // 3D + case SEA3D.Stream.VECTOR3D: + + return this.readFloatArray( count ); + + + // 4D + case SEA3D.Stream.VECTOR4D: + + return this.readFloatArray( count ); + + } + +}; + +SEA3D.Stream.prototype.append = function( data ) { + + var tmp = new ArrayBuffer( this.data.byteLength + data.byteLength ); + tmp.set( new ArrayBuffer( this.data ), 0 ); + tmp.set( new ArrayBuffer( data ), this.data.byteLength ); + this.data = tmp; + +}; + +SEA3D.Stream.prototype.concat = function( position, length ) { + + return new SEA3D.Stream( this.buffer.slice( position, position + length ) ); + +}; + +/** + * @author DataStream.js + */ + +SEA3D.Stream.memcpy = function( dst, dstOffset, src, srcOffset, byteLength ) { + + var dstU8 = new Uint8Array( dst, dstOffset, byteLength ); + var srcU8 = new Uint8Array( src, srcOffset, byteLength ); + + dstU8.set( srcU8 ); + +}; + +// +// UByteArray +// + +SEA3D.UByteArray = function() { + + this.ubytes = []; + this.length = 0; + +}; + +SEA3D.UByteArray.prototype = { + constructor: SEA3D.UByteArray, + + add : function ( ubytes ) { + + this.ubytes.push( ubytes ); + this.length += ubytes.byteLength; + + }, + + toBuffer : function () { + + var memcpy = new Uint8Array( this.length ); + + for ( var i = 0, offset = 0; i < this.ubytes.length; i ++ ) { + + memcpy.set( this.ubytes[ i ], offset ); + offset += this.ubytes[ i ].byteLength; + + } + + return memcpy.buffer; + + } +}; + +// +// Math +// + +SEA3D.Math = { + DEGREES : 180 / Math.PI, + RADIANS : Math.PI / 180 +}; + +SEA3D.Math.angle = function( val ) { + + var ang = 180, + inv = val < 0; + + val = ( inv ? - val : val ) % 360; + + if ( val > ang ) { + + val = - ang + ( val - ang ); + + } + + return ( inv ? - val : val ); + +}; + +SEA3D.Math.lerpAngle = function( val, tar, t ) { + + if ( Math.abs( val - tar ) > 180 ) { + + if ( val > tar ) { + + tar += 360; + + } + else { + + tar -= 360; + + } + + } + + val += ( tar - val ) * t; + + return SEA3D.Math.angle( val ); + +}; + +SEA3D.Math.lerpColor = function( val, tar, t ) { + + var a0 = val >> 24 & 0xff, + r0 = val >> 16 & 0xff, + g0 = val >> 8 & 0xff, + b0 = val & 0xff; + + var a1 = tar >> 24 & 0xff, + r1 = tar >> 16 & 0xff, + g1 = tar >> 8 & 0xff, + b1 = tar & 0xff; + + a0 += ( a1 - a0 ) * t; + r0 += ( r1 - r0 ) * t; + g0 += ( g1 - g0 ) * t; + b0 += ( b1 - b0 ) * t; + + return a0 << 24 | r0 << 16 | g0 << 8 | b0; + +}; + +SEA3D.Math.lerp = function( val, tar, t ) { + + return val + ( ( tar - val ) * t ); + +}; + +SEA3D.Math.lerp1x = function( val, tar, t ) { + + val[ 0 ] += ( tar[ 0 ] - val[ 0 ] ) * t; + +}; + +SEA3D.Math.lerp3x = function( val, tar, t ) { + + val[ 0 ] += ( tar[ 0 ] - val[ 0 ] ) * t; + val[ 1 ] += ( tar[ 1 ] - val[ 1 ] ) * t; + val[ 2 ] += ( tar[ 2 ] - val[ 2 ] ) * t; + +}; + +SEA3D.Math.lerpAng1x = function( val, tar, t ) { + + val[ 0 ] = SEA3D.Math.lerpAngle( val[ 0 ], tar[ 0 ], t ); + +}; + +SEA3D.Math.lerpColor1x = function( val, tar, t ) { + + val[ 0 ] = SEA3D.Math.lerpColor( val[ 0 ], tar[ 0 ], t ); + +}; + +SEA3D.Math.lerpQuat4x = function( val, tar, t ) { + + var x1 = val[ 0 ], + y1 = val[ 1 ], + z1 = val[ 2 ], + w1 = val[ 3 ]; + + var x2 = tar[ 0 ], + y2 = tar[ 1 ], + z2 = tar[ 2 ], + w2 = tar[ 3 ]; + + var x, y, z, w, l; + + // shortest direction + if ( x1 * x2 + y1 * y2 + z1 * z2 + w1 * w2 < 0 ) { + + x2 = - x2; + y2 = - y2; + z2 = - z2; + w2 = - w2; + + } + + x = x1 + t * ( x2 - x1 ); + y = y1 + t * ( y2 - y1 ); + z = z1 + t * ( z2 - z1 ); + w = w1 + t * ( w2 - w1 ); + + l = 1.0 / Math.sqrt( w * w + x * x + y * y + z * z ); + val[ 0 ] = x * l; + val[ 1 ] = y * l; + val[ 2 ] = z * l; + val[ 3 ] = w * l; + +}; + +// +// Timer +// + +SEA3D.Timer = function() { + + this.time = this.start = Date.now(); + +}; + +SEA3D.Timer.prototype = { + constructor: SEA3D.Timer, + + get now () { + + return Date.now(); + + }, + + get deltaTime () { + + return Date.now() - this.time; + + }, + + get elapsedTime () { + + return Date.now() - this.start; + + }, + + update: function () { + + this.time = Date.now(); + + } +}; + +// +// Blend Method +// + +SEA3D.AnimationBlendMethod = { + LINEAR : 'linear', + EASING : 'easing' +}; + +// +// Domain +// + +SEA3D.Domain = function( id ) { + + this.id = id; + this.scripts = []; + this.global = {}; + this.events = new SEA3D.EventDispatcher(); + +}; + +SEA3D.Domain.prototype = { + constructor: SEA3D.Domain, + + add : function( src ) { + + this.scripts.push( src ); + + }, + + remove : function( src ) { + + this.scripts.splice( this.scripts.indexOf( src ), 1 ); + + }, + + contains : function( src ) { + + return this.scripts.indexOf( src ) != - 1; + + }, + + addEvent : function( type, listener ) { + + this.events.addEventListener( type, listener ); + + }, + + hasEvent : function( type, listener ) { + + return this.events.hasEventListener( type, listener ); + + }, + + removeEvent : function( type, listener ) { + + this.events.removeEventListener( type, listener ); + + }, + + print : function() { + + console.log.apply( console, arguments ); + + }, + + watch : function() { + + console.log.apply( console, 'watch:', arguments ); + + }, + + getReference : function( ns ) { + + return eval( ns ); + + }, + + dispatchEvent : function( event ) { + + event.domain = this; + + var scripts = this.scripts.concat(), + i = scripts.length; + + while ( i -- ) { + + scripts[ i ].dispatchEvent( event ); + + } + + this.events.dispatchEvent( event ); + + }, + + dispose : function() { + + var scripts = this.scripts.concat(), + i = scripts.length; + + while ( i -- ) { + + scripts[ i ].dispose(); + + } + + this.dispatchEvent( { type : "dispose" } ); + + } +}; + +// +// Domain Manager +// + +SEA3D.DomainManager = function( autoDisposeRootDomain ) { + + this.domains = []; + this.autoDisposeRootDomain = autoDisposeRootDomain == undefined ? true : false; + +}; + +SEA3D.DomainManager.prototype = { + constructor: SEA3D.DomainManager, + + onDisposeDomain : function( e ) { + + this.remove( e.domain ); + + if ( this.autoDisposeRootDomain && this.domains.length == 1 ) { + + this.dispose(); + + } + + }, + + add : function( domain ) { + + this._onDisposeDomain = this._onDisposeDomain || this.onDisposeDomain.bind( this ); + + domain.addEvent( "dispose", this._onDisposeDomain ); + + this.domains.push( domain ); + + }, + + remove : function( domain ) { + + domain.removeEvent( "dispose", this._onDisposeDomain ); + + this.domains.splice( this.domains.indexOf( domain ), 1 ); + + }, + + contains : function( domain ) { + + return this.domains.indexOf( domain ) != - 1; + + }, + + dispose : function() { + + var domains = this.domains.concat(), + i = domains.length; + + while ( i -- ) { + + domains[ i ].dispose(); + + } + + } +}; + + +// +// Script +// + +SEA3D.Script = function( domain, root ) { + + domain = domain || new SEA3D.Domain(); + domain.add( this ); + + var events = new SEA3D.EventDispatcher(); + + this.getId = function() { + + return domain.id; + + } + + this.isRoot = function() { + + return root; + + } + + this.addEvent = function( type, listener ) { + + events.addEventListener( type, listener ); + + } + + this.hasEvent = function( type, listener ) { + + return events.hasEventListener( type, listener ); + + } + + this.removeEvent = function( type, listener ) { + + events.removeEventListener( type, listener ); + + } + + this.dispatchEvent = function( event ) { + + event.script = this; + + events.dispatchEvent( event ); + + } + + this.dispose = function() { + + domain.remove( this ); + + if ( root ) domain.dispose(); + + this.dispatchEvent( { type : "dispose" } ); + + } + +}; + +// +// Script Manager +// + +SEA3D.ScriptManager = function() { + + this.scripts = []; + + var onDisposeScript = ( function( e ) { + + this.remove( e.script ); + + } ).bind( this ); + + this.add = function( src ) { + + src.addEvent( "dispose", onDisposeScript ); + + this.scripts.push( src ); + + } + + this.remove = function( src ) { + + src.removeEvent( "dispose", onDisposeScript ); + + this.scripts.splice( this.scripts.indexOf( src ), 1 ); + + } + + this.contains = function( src ) { + + return this.scripts.indexOf( src ) > - 1; + + } + + this.dispatchEvent = function( event ) { + + var scripts = this.scripts.concat(), + i = scripts.length; + + while ( i -- ) { + + scripts[ i ].dispatchEvent( event ); + + } + + } + +}; + +// +// AnimationFrame +// + +SEA3D.AnimationFrame = function() { + + this.data = [ 0, 0, 0, 0 ]; + +}; + +SEA3D.AnimationFrame.prototype.toVector = function() { + + return { x: this.data[ 0 ], y: this.data[ 1 ], z: this.data[ 2 ], w: this.data[ 3 ] }; + +}; + +SEA3D.AnimationFrame.prototype.toAngles = function( d ) { + + var x = this.data[ 0 ], + y = this.data[ 1 ], + z = this.data[ 2 ], + w = this.data[ 3 ]; + + var a = 2 * ( w * y - z * x ); + + if ( a < - 1 ) a = - 1; + else if ( a > 1 ) a = 1; + + return { + x : Math.atan2( 2 * ( w * x + y * z ), 1 - 2 * ( x * x + y * y ) ) * d, + y : Math.asin( a ) * d, + z : Math.atan2( 2 * ( w * z + x * y ), 1 - 2 * ( y * y + z * z ) ) * d + } + +}; + +SEA3D.AnimationFrame.prototype.toEuler = function() { + + return this.toAngles( SEA3D.Math.DEGREES ); + +}; + +SEA3D.AnimationFrame.prototype.toRadians = function() { + + return this.toAngles( 1 ); + +}; + +SEA3D.AnimationFrame.prototype.setX = function( val ) { + + this.data[ 0 ] = val; + +}; + +SEA3D.AnimationFrame.prototype.getX = function() { + + return this.data[ 0 ]; + +}; + +SEA3D.AnimationFrame.prototype.setY = function( val ) { + + this.data[ 1 ] = val; + +}; + +SEA3D.AnimationFrame.prototype.getY = function() { + + return this.data[ 1 ]; + +}; + +SEA3D.AnimationFrame.prototype.setZ = function( val ) { + + this.data[ 2 ] = val; + +}; + +SEA3D.AnimationFrame.prototype.getZ = function() { + + return this.data[ 2 ]; + +}; + +SEA3D.AnimationFrame.prototype.setW = function( val ) { + + this.data[ 3 ] = val; + +}; + +SEA3D.AnimationFrame.prototype.getW = function() { + + return this.data[ 3 ]; + +}; + +// +// AnimationData +// + +SEA3D.AnimationData = function( kind, dataType, data, offset ) { + + this.kind = kind; + this.type = dataType; + this.blockLength = SEA3D.Stream.sizeOf( dataType ); + this.data = data; + this.offset = offset == undefined ? 0 : offset; + + switch ( this.blockLength ) + { + case 1: this.getData = this.getData1x; break; + case 2: this.getData = this.getData2x; break; + case 3: this.getData = this.getData3x; break; + case 4: this.getData = this.getData4x; break; + } + +}; + +SEA3D.AnimationData.prototype.getData1x = function( frame, data ) { + + frame = this.offset + frame * this.blockLength; + + data[ 0 ] = this.data[ frame ]; + +}; + +SEA3D.AnimationData.prototype.getData2x = function( frame, data ) { + + frame = this.offset + frame * this.blockLength; + + data[ 0 ] = this.data[ frame ]; + data[ 1 ] = this.data[ frame + 1 ]; + +}; + +SEA3D.AnimationData.prototype.getData3x = function( frame, data ) { + + frame = this.offset + frame * this.blockLength; + + data[ 0 ] = this.data[ frame ]; + data[ 1 ] = this.data[ frame + 1 ]; + data[ 2 ] = this.data[ frame + 2 ]; + +}; + +SEA3D.AnimationData.prototype.getData4x = function( frame, data ) { + + frame = this.offset + frame * this.blockLength; + + data[ 0 ] = this.data[ frame ]; + data[ 1 ] = this.data[ frame + 1 ]; + data[ 2 ] = this.data[ frame + 2 ]; + data[ 3 ] = this.data[ frame + 3 ]; + +}; + +// +// AnimationNode +// + +SEA3D.AnimationNode = function( name, frameRate, numFrames, repeat, intrpl ) { + + this.name = name; + this.frameRate = frameRate; + this.frameMill = 1000 / frameRate; + this.numFrames = numFrames; + this.length = numFrames - 1; + this.time = 0; + this.duration = this.length * this.frameMill; + this.repeat = repeat; + this.intrpl = intrpl; + this.invalidState = true; + this.dataList = []; + this.dataListId = {}; + this.buffer = new SEA3D.AnimationFrame(); + this.percent = 0; + this.prevFrame = 0; + this.nextFrame = 0; + this.frame = 0; + +}; + +SEA3D.AnimationNode.prototype.setTime = function( value ) { + + this.frame = this.validFrame( value / this.frameMill ); + this.time = this.frame * this.frameRate; + this.invalidState = true; + +}; + +SEA3D.AnimationNode.prototype.getTime = function() { + + return this.time; + +}; + +SEA3D.AnimationNode.prototype.setFrame = function( value ) { + + this.setTime( value * this.frameMill ); + +}; + +SEA3D.AnimationNode.prototype.getRealFrame = function() { + + return Math.floor( this.frame ); + +}; + +SEA3D.AnimationNode.prototype.getFrame = function() { + + return this.frame; + +}; + +SEA3D.AnimationNode.prototype.setPosition = function( value ) { + + this.setFrame( value * ( this.numFrames - 1 ) ); + +}; + +SEA3D.AnimationNode.prototype.getPosition = function() { + + return this.frame / ( this.numFrames - 1 ); + +}; + +SEA3D.AnimationNode.prototype.validFrame = function( value ) { + + var inverse = value < 0; + + if ( inverse ) value = - value; + + if ( value > this.length ) { + + value = this.repeat ? value % this.length : this.length; + + } + + if ( inverse ) value = this.length - value; + + return value; + +}; + +SEA3D.AnimationNode.prototype.addData = function( animationData ) { + + this.dataListId[ animationData.kind ] = animationData; + this.dataList[ this.dataList.length ] = animationData; + +}; + +SEA3D.AnimationNode.prototype.removeData = function( animationData ) { + + delete this.dataListId[ animationData.kind ]; + this.dataList.splice( this.dataList.indexOf( animationData ), 1 ); + +}; + +SEA3D.AnimationNode.prototype.getDataByKind = function( kind ) { + + return this.dataListId[ kind ]; + +}; + +SEA3D.AnimationNode.prototype.getFrameAt = function( frame, id ) { + + this.dataListId[ id ].getFrameData( frame, this.buffer.data ); + return this.buffer; + +}; + +SEA3D.AnimationNode.prototype.getFrame = function( id ) { + + this.dataListId[ id ].getFrameData( this.getRealFrame(), this.buffer.data ); + return this.buffer; + +}; + +SEA3D.AnimationNode.prototype.getInterpolationFrame = function( animationData, iFunc ) { + + if ( this.numFrames == 0 ) return this.buffer; + + if ( this.invalidState ) { + + this.prevFrame = this.getRealFrame(); + this.nextFrame = this.validFrame( this.prevFrame + 1 ); + this.percent = this.frame - this.prevFrame; + this.invalidState = false; + + } + + animationData.getData( this.prevFrame, this.buffer.data ); + + if ( this.percent > 0 ) { + + animationData.getData( this.nextFrame, SEA3D.AnimationNode.FRAME_BUFFER ); + + // interpolation function + iFunc( this.buffer.data, SEA3D.AnimationNode.FRAME_BUFFER, this.percent ); + + } + + return this.buffer; + +}; + +SEA3D.AnimationNode.FRAME_BUFFER = [ 0, 0, 0, 0 ]; + +// +// AnimationSet +// + +SEA3D.AnimationSet = function() { + + this.animations = []; + this.dataCount = - 1; + +}; + +SEA3D.AnimationSet.prototype.addAnimation = function( node ) { + + if ( this.dataCount == - 1 ) this.dataCount = node.dataList.length; + + this.animations[ node.name ] = node; + this.animations.push( node ); + +}; + +SEA3D.AnimationSet.prototype.getAnimationByName = function( name ) { + + return this.animations[ name ]; + +}; + +// +// AnimationState +// + +SEA3D.AnimationState = function( node ) { + + this.node = node; + this.offset = 0; + this.weight = 0; + this.time = 0; + +}; + +SEA3D.AnimationState.prototype.setTime = function( val ) { + + this.node.time = this.time = val; + +}; + +SEA3D.AnimationState.prototype.getTime = function() { + + return this.time; + +}; + +SEA3D.AnimationState.prototype.setFrame = function( val ) { + + this.node.setFrame( val ); + + this.time = this.node.time; + +}; + +SEA3D.AnimationState.prototype.getFrame = function() { + + this.update(); + + return this.node.getFrame(); + +}; + +SEA3D.AnimationState.prototype.setPosition = function( val ) { + + this.node.setPosition( val ); + + this.time = this.node.time; + +}; + +SEA3D.AnimationState.prototype.getPosition = function() { + + this.update(); + + return this.node.getPosition(); + +}; + +SEA3D.AnimationState.prototype.update = function() { + + if ( this.node.time != this.time ) + this.node.setTime( this.time ); + +}; + +// +// Animation Handler +// + +SEA3D.AnimationHandler = function( animationSet ) { + + this.animationSet = animationSet; + this.states = SEA3D.AnimationHandler.stateFromAnimations( animationSet.animations ); + this.timeScale = 1; + this.time = 0; + this.numAnimation = animationSet.animations.length; + this.relative = false; + this.playing = false; + this.delta = 0; + this.easeSpeed = 2; + this.crossfade = 0; + this.updateAllStates = false; + this.blendMethod = SEA3D.AnimationBlendMethod.LINEAR; + +}; + +SEA3D.AnimationHandler.prototype.update = function( delta ) { + + this.delta = delta; + this.time += delta * this.timeScale; + + this.updateState(); + this.updateAnimation(); + +}; + +SEA3D.AnimationHandler.prototype.updateState = function() { + + var i, state; + + this.currentState.node.setTime( this.time - this.currentState.offset ); + + if ( this.currentState.weight < 1 && this.crossfade > 0 ) { + + var delta = Math.abs( this.delta ) / ( 1000.0 * this.crossfade ); + var weight = 1; + + if ( this.blendMethod === SEA3D.AnimationBlendMethod.EASING ) { + + delta *= this.easeSpeed; + + } + + for ( i = 0; i < this.states.length; ++ i ) { + + state = this.states[ i ]; + + if ( state !== this.currentState ) { + + if ( this.blendMethod === SEA3D.AnimationBlendMethod.LINEAR ) { + + state.weight -= delta; + + } + else if ( this.blendMethod === SEA3D.AnimationBlendMethod.EASING ) { + + state.weight -= state.weight * delta; + + } + + if ( state.weight < 0 ) state.weight = 0; + + weight -= state.weight; + + if ( this.updateAllStates ) { + + state.node.setTime( this.time - state.offset ); + + } + + } + + } + + if ( weight < 0 ) weight = 0; + + this.currentState.weight = weight; + + } else { + + for ( i = 0; i < this.states.length; ++ i ) { + + state = this.states[ i ]; + + if ( state === this.currentState ) state.weight = 1; + else { + + state.weight = 0; + + if ( this.updateAllStates ) { + + state.node.setTime( this.time ); + + } + + } + + } + + } + +}; + +SEA3D.AnimationHandler.prototype.updateAnimation = function() { + + var dataCount = this.animationSet.dataCount; + var nodes = this.animationSet.animations; + var currentNode = this.currentState.node; + + for ( var i = 0; i < dataCount; i ++ ) { + + for ( var n = 0; n < nodes.length; n ++ ) { + + var node = nodes[ n ], + state = this.states[ n ], + data = node.dataList[ i ], + iFunc = SEA3D.Animation.DefaultLerpFuncs[ data.kind ], + frame; + + if ( n == 0 ) { + + frame = currentNode.getInterpolationFrame( currentNode.dataList[ i ], iFunc ); + + if ( ! currentNode.repeat && currentNode.frame == currentNode.numFrames - 1 ) { + + if ( this.onComplete ) + this.onComplete( this ); + + } + + } + + if ( node != currentNode ) { + + if ( state.weight > 0 ) { + + iFunc( + frame.data, + node.getInterpolationFrame( data, iFunc ).data, + state.weight + ); + + } + + } + + if ( this.updateAnimationFrame ) { + + this.updateAnimationFrame( frame, data.kind ); + + } + + } + + } + +}; + +SEA3D.AnimationHandler.prototype.getStateByName = function( name ) { + + return this.states[ name ]; + +}; + +SEA3D.AnimationHandler.prototype.getStateNameByIndex = function( index ) { + + return this.animationSet.animations[ index ].name; + +}; + +SEA3D.AnimationHandler.prototype.play = function( name, crossfade, offset ) { + + this.currentState = this.getStateByName( name ); + + if ( ! this.currentState ) + throw new Error( 'Animation "' + name + '" not found.' ); + + this.crossfade = crossfade; + this.currentState.offset = this.time; + + if ( offset !== undefined ) { + + this.currentState.time = offset; + + } + + if ( ! this.playing ) { + + // Add in animation collector + + SEA3D.AnimationHandler.add( this ); + + this.playing = true; + + } + +}; + +SEA3D.AnimationHandler.prototype.resume = function() { + + if ( ! this.playing ) { + + SEA3D.AnimationHandler.add( this ); + this.playing = true; + + } + +}; + +SEA3D.AnimationHandler.prototype.pause = function() { + + if ( this.playing ) { + + SEA3D.AnimationHandler.remove( this ); + this.playing = false; + + } + +}; + +SEA3D.AnimationHandler.prototype.stop = function() { + + this.time = 0; + + this.pause(); + +}; + +SEA3D.AnimationHandler.prototype.setRelative = function( val ) { + + this.relative = val; + +}; + +SEA3D.AnimationHandler.prototype.getRelative = function() { + + return this.relative; + +}; + +// +// Manager +// + +SEA3D.AnimationHandler.add = function( animation ) { + + SEA3D.AnimationHandler.animations.push( animation ); + +}; + +SEA3D.AnimationHandler.remove = function( animation ) { + + SEA3D.AnimationHandler.animations.splice( SEA3D.AnimationHandler.animations.indexOf( animation ), 1 ); + +}; + +SEA3D.AnimationHandler.stateFromAnimations = function( anms ) { + + var states = []; + for ( var i = 0; i < anms.length; i ++ ) { + + states[ anms[ i ].name ] = states[ i ] = new SEA3D.AnimationState( anms[ i ] ); + + } + return states; + +}; + +SEA3D.AnimationHandler.update = function( delta ) { + + for ( var i = 0, len = SEA3D.AnimationHandler.animations.length; i < len; i ++ ) { + + SEA3D.AnimationHandler.animations[ i ].update( delta * 1000 ); + + } + +}; + +SEA3D.AnimationHandler.setTime = function( time ) { + + for ( var i = 0, len = SEA3D.AnimationHandler.animations.length; i < len; i ++ ) { + + SEA3D.AnimationHandler.animations[ i ].time = time; + + } + +}; + +SEA3D.AnimationHandler.stop = function() { + + while ( SEA3D.AnimationHandler.animations.length ) { + + SEA3D.AnimationHandler.animations[ 0 ].stop(); + + } + +}; + +SEA3D.AnimationHandler.animations = []; + +// +// Object +// + +SEA3D.Object = function( name, data, type, sea ) { + + this.name = name; + this.data = data; + this.type = type; + this.sea = sea; + +}; + +// +// Geometry Base +// + +SEA3D.GeometryBase = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.attrib = data.readUShort(); + + this.isBig = ( this.attrib & 1 ) != 0; + + // variable uint + data.readVInt = this.isBig ? data.readUInt : data.readUShort; + + this.numVertex = data.readVInt(); + + this.length = this.numVertex * 3; + +}; + +// +// Geometry +// + +SEA3D.Geometry = function( name, data, sea ) { + + SEA3D.GeometryBase.call( this, name, data, sea ); + + var i, j, vec, len; + + // NORMAL + if ( this.attrib & 4 ) { + + this.normal = data.readFloatArray( this.length ); + + } + + // TANGENT + if ( this.attrib & 8 ) { + + this.tangent = data.readFloatArray( this.length ); + + } + + // UV + if ( this.attrib & 32 ) { + + this.uv = []; + this.uv.length = data.readUByte(); + + len = this.numVertex * 2; + + i = 0; + while ( i < this.uv.length ) { + + // UV VERTEX DATA + this.uv[ i ++ ] = data.readFloatArray( len ); + + } + + } + + // JOINT-INDEXES / WEIGHTS + if ( this.attrib & 64 ) { + + this.jointPerVertex = data.readUByte(); + + var jntLen = this.numVertex * this.jointPerVertex; + + this.joint = data.readUShortArray( jntLen ); + this.weight = data.readFloatArray( jntLen ); + + } + + // VERTEX_COLOR + if ( this.attrib & 128 ) { + + var colorAttrib = data.readUByte(); + + this.numColor = ( ( ( colorAttrib & 64 ) >> 6 ) | ( ( colorAttrib & 128 ) >> 6 ) ) + 1; + + this.color = []; + + for ( i = 0, len = colorAttrib & 15; i < len; i ++ ) { + + this.color.push( data.readFloatArray( this.numVertex * this.numColor ) ); + + } + + } + + // VERTEX + this.vertex = data.readFloatArray( this.length ); + + // SUB-MESHES + var count = data.readUByte(); + + this.groups = []; + + if ( this.attrib & 2 ) { + + // INDEXES + for ( i = 0, len = 0; i < count; i ++ ) { + + this.groups.push( { + start : len, + count : len += ( data.readVInt() * 3 ), + } ); + + } + + this.indexes = this.isBig ? data.readUIntArray( len ) : data.readUShortArray( len ); + + } else { + + // INDEXES + var stride = this.isBig ? 4 : 2, + bytearray = new SEA3D.UByteArray(); + + for ( i = 0, j = 0; i < count; i ++ ) { + + len = data.readVInt() * 3; + + this.groups.push( { + start : j, + count : len, + } ); + + len += j; + + bytearray.add( data.readUByteArray( len * stride ) ); + + } + + this.indexes = this.isBig ? new Uint32Array( bytearray.toBuffer() ) : new Uint16Array( bytearray.toBuffer() ); + + } + +}; + +SEA3D.Geometry.prototype = Object.create( SEA3D.GeometryBase.prototype ); +SEA3D.Geometry.prototype.constructor = SEA3D.Geometry; + +SEA3D.Geometry.prototype.type = "geo"; + +// +// Geometry 16 Bit +// + +SEA3D.Geometry16 = function( name, data, sea ) { + + SEA3D.GeometryBase.call( this, name, data, sea ); + + var i, len; + + // NORMAL + if ( this.attrib & 4 ) { + + this.normal = data.readFloatArray( this.length ); + + } + + // TANGENT + if ( this.attrib & 8 ) { + + this.tangent = data.readFloatArray( this.length ); + + } + + // UV + if ( this.attrib & 32 ) { + + this.uv = []; + this.uv.length = data.readUByte(); + + len = this.numVertex * 2; + + i = 0; + while ( i < this.uv.length ) { + + // UV VERTEX DATA + this.uv[ i ++ ] = data.readFloatArray( len ); + + } + + } + + // JOINT-INDEXES / WEIGHTS + if ( this.attrib & 64 ) { + + this.jointPerVertex = data.readUByte(); + + var jntLen = this.numVertex * this.jointPerVertex; + + this.joint = data.readUShortArray( jntLen ); + this.weight = data.readFloatArray( jntLen ); + + } + + // VERTEX_COLOR + if ( this.attrib & 128 ) { + + var colorAttrib = data.readUByte(); + + this.numColor = ( ( ( colorAttrib & 64 ) >> 6 ) | ( ( colorAttrib & 128 ) >> 6 ) ) + 1; + + this.color = []; + + for ( i = 0, len = colorAttrib & 15; i < len; i ++ ) { + + this.color.push( data.readFloatArray( this.numVertex * this.numColor ) ); + + } + + } + + // VERTEX + this.vertex = data.readFloatArray( this.length ); + + // SUB-MESHES + var count = data.readUByte(); + + this.indexes = new Uint16Array(); + this.groups = []; + + // INDEXES + for ( i = 0, j = 0; i < count; i ++ ) { + + len = data.readVInt() * 3; + + this.groups.push( { + start : j, + count : len, + } ); + + len += j; + + this.indexes = this.isBig ? data.readUIntArray( len ) : data.readUShortArray( len ); + + } + +}; + + +SEA3D.Geometry16.prototype = Object.create( SEA3D.GeometryBase.prototype ); +SEA3D.Geometry16.prototype.constructor = SEA3D.Geometry16; + +SEA3D.Geometry16.prototype.type = "ge16"; + +// +// Geometry Delta Base +// + +SEA3D.GeometryDeltaBase = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.attrib = data.readUShort(); + + this.numVertex = data.readUInteger(); + + this.length = this.numVertex * 3; + + if ( this.attrib & 1 ) { + + data.readNumber = data.readByte; + this.numDiv = 0xFF / 2; + + } + else { + + data.readNumber = data.readShort; + numDiv = 0xFFFF / 2; + + } + +}; + +// +// Geometry Delta +// + +SEA3D.GeometryDelta = function( name, data, sea ) { + + SEA3D.GeometryDeltaBase.call( this, name, data, sea ); + + var i, j, start, delta, len, vec; + + // NORMAL + if ( this.attrib & 4 ) { + + delta = data.readFloat(); + + this.normal = new Float32Array( this.length ); + + i = 0; + while ( i < this.length ) { + + this.normal[ i ++ ] = ( data.readNumber() / this.numDiv ) * delta; + + } + + } + + // TANGENT + if ( this.attrib & 8 ) { + + delta = data.readFloat(); + + this.tangent = new Float32Array( this.length ); + + i = 0; + while ( i < this.length ) { + + this.tangent[ i ++ ] = ( data.readNumber() / this.numDiv ) * delta; + + } + + } + + // UV + if ( this.attrib & 32 ) { + + this.uv = []; + this.uv.length = data.readUByte(); + + var uvLen = this.numVertex * 2; + + i = 0; + while ( i < this.uv.length ) { + + // UV VERTEX DATA + delta = data.readFloat(); + this.uv[ i ++ ] = vec = new Float32Array( uvLen ); + + j = 0; + while ( j < uvLen ) { + + vec[ j ++ ] = ( data.readNumber() / this.numDiv ) * delta; + + } + + } + + } + + // JOINT-INDEXES / WEIGHTS + if ( this.attrib & 64 ) { + + this.jointPerVertex = data.readUByte(); + + var jntLen = this.numVertex * this.jointPerVertex; + + this.joint = new Uint16Array( jntLen ); + this.weight = new Float32Array( jntLen ); + + i = 0; + while ( i < jntLen ) { + + this.joint[ i ++ ] = data.readUInteger(); + + } + + i = 0; + while ( i < jntLen ) { + + this.weight[ i ++ ] = ( data.readNumber() / this.numDiv ) * 1; + + } + + } + + // VERTEX_COLOR + if ( this.attrib & 128 ) { + + var colorAttrib = data.readUByte(), + numColorData = ( ( ( colorAttrib & 64 ) >> 6 ) | ( ( colorAttrib & 128 ) >> 6 ) ) + 1, + colorCount = this.numVertex * 4; + + this.color = []; + this.color.length = colorAttrib & 15; + + this.numColor = 4; + + for ( i = 0; i < this.color.length; i ++ ) { + + var vColor = new Float32Array( colorCount ); + + switch ( numColorData ) + { + case 1: + j = 0; + while ( j < colorCount ) { + + vColor[ j ++ ] = data.readUByte() / 0xFF; + vColor[ j ++ ] = 0; + vColor[ j ++ ] = 0; + vColor[ j ++ ] = 1; + + } + break; + + case 2: + j = 0; + while ( j < colorCount ) { + + vColor[ j ++ ] = data.readUByte() / 0xFF; + vColor[ j ++ ] = data.readUByte() / 0xFF; + vColor[ j ++ ] = 0; + vColor[ j ++ ] = 1; + + } + break; + + case 3: + j = 0; + while ( j < colorCount ) { + + vColor[ j ++ ] = data.readUByte() / 0xFF; + vColor[ j ++ ] = data.readUByte() / 0xFF; + vColor[ j ++ ] = data.readUByte() / 0xFF; + vColor[ j ++ ] = 1; + + } + break; + + case 4: + j = 0; + while ( j < colorCount ) { + + vColor[ j ++ ] = data.readUByte() / 0xFF; + vColor[ j ++ ] = data.readUByte() / 0xFF; + vColor[ j ++ ] = data.readUByte() / 0xFF; + vColor[ j ++ ] = data.readUByte() / 0xFF; + + } + break; + } + + this.color[ i ] = vColor; + + } + + } + + // VERTEX + delta = data.readFloat(); + + this.vertex = new Float32Array( this.length ); + + i = 0; + while ( i < this.length ) { + + this.vertex[ i ++ ] = ( data.readNumber() / this.numDiv ) * delta; + + } + + // SUB-MESHES + var count = data.readUByte(); + + this.indexes = vec = []; + this.groups = []; + + // INDEXES + j = 0; + for ( i = 0; i < count; i ++ ) { + + len = data.readVInt() * 3; + + this.groups.push( { + start : j, + count : len, + } ); + + len += j; + while ( j < len ) { + + vec[ j ++ ] = data.readVInt(); + + } + + } + + // SUB-MESHES + var count = data.readUByte(); + + this.indexes = vec = []; + this.groups = []; + + // INDEXES + if ( this.attrib & 2 ) { + + // POLYGON + for ( i = 0; i < count; i ++ ) { + + len = data.readUInteger(); + + start = vec.length; + + for ( j = 0; j < len; j ++ ) { + + var a = data.readUInteger(), + b = data.readUInteger(), + c = data.readUInteger(), + d = data.readUInteger(); + + + vec.push( a ); + vec.push( b ); + vec.push( c ); + + if ( d > 0 ) + { + + vec.push( c ); + vec.push( d + 1 ); + vec.push( a ); + + } + else continue; + + } + + this.groups.push( { + start : start, + count : vec.length - start, + } ); + + } + + } else { + + // TRIANGLE + j = 0; + for ( i = 0; i < count; i ++ ) { + + len = data.readUInteger() * 3; + + this.groups.push( { + start : j, + count : len, + } ); + + len += j; + while ( j < len ) { + + vec[ j ++ ] = data.readUInteger(); + + } + + } + + } + +}; + +SEA3D.GeometryDeltaBase.prototype = Object.create( SEA3D.GeometryDeltaBase.prototype ); +SEA3D.GeometryDeltaBase.prototype.constructor = SEA3D.GeometryDelta; + +SEA3D.GeometryDelta.prototype.type = "geDL"; + +// +// Object3D +// + +SEA3D.Object3D = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.isStatic = false; + + this.attrib = data.readUShort(); + + if ( this.attrib & 1 ) this.parent = sea.getObject( data.readUInt() ); + + if ( this.attrib & 2 ) this.animations = data.readAnimationList( sea ); + + if ( this.attrib & 4 ) this.scripts = data.readScriptList( sea ); + + if ( this.attrib & 16 ) this.properties = sea.getObject( data.readUInt() ); + + if ( this.attrib & 32 ) { + + var objectType = data.readUByte(); + this.isStatic = objectType & 1; + + } + +}; + +SEA3D.Object3D.prototype.readTag = function( kind, data, size ) { + +}; + +// +// Entity3D +// + +SEA3D.Entity3D = function( name, data, sea ) { + + SEA3D.Object3D.call( this, name, data, sea ); + + this.castShadows = true; + + if ( this.attrib & 64 ) { + + var lightType = data.readUByte(); + + this.castShadows = ( lightType & 1 ) == 0; + + } + +}; + +SEA3D.Entity3D.prototype = Object.create( SEA3D.Object3D.prototype ); +SEA3D.Entity3D.prototype.constructor = SEA3D.Entity3D; + +// +// Sound3D +// + +SEA3D.Sound3D = function( name, data, sea ) { + + SEA3D.Object3D.call( this, name, data, sea ); + + this.autoPlay = ( this.attrib & 64 ) != 0; + + if ( this.attrib & 128 ) this.mixer = sea.getObject( data.readUInt() ); + + this.sound = sea.getObject( data.readUInt() ); + this.volume = data.readFloat(); + +}; + +SEA3D.Sound3D.prototype = Object.create( SEA3D.Object3D.prototype ); +SEA3D.Sound3D.prototype.constructor = SEA3D.Sound3D; + +// +// Sound Point +// + +SEA3D.SoundPoint = function( name, data, sea ) { + + SEA3D.Sound3D.call( this, name, data, sea ); + + this.position = data.readVector3(); + this.distance = data.readFloat(); + + data.readTags( this.readTag.bind( this ) ); + +}; + +SEA3D.SoundPoint.prototype = Object.create( SEA3D.Sound3D.prototype ); +SEA3D.SoundPoint.prototype.constructor = SEA3D.SoundPoint; + +SEA3D.SoundPoint.prototype.type = "sp"; + +// +// Container3D +// + +SEA3D.Container3D = function( name, data, sea ) { + + SEA3D.Object3D.call( this, name, data, sea ); + + this.transform = data.readMatrix(); + + data.readTags( this.readTag.bind( this ) ); + +}; + +SEA3D.Container3D.prototype = Object.create( SEA3D.Object3D.prototype ); +SEA3D.Container3D.prototype.constructor = SEA3D.Container3D; + +SEA3D.Container3D.prototype.type = "c3d"; + +// +// Texture URL +// + +SEA3D.TextureURL = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.url = data.readUTF( data.length ); + +}; + +SEA3D.TextureURL.prototype.type = "urlT"; + +// +// Actions +// + +SEA3D.Actions = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.count = data.readUInt(); + this.actions = []; + + for ( var i = 0; i < this.count; i ++ ) { + + var flag = data.readUByte(); + var kind = data.readUShort(); + + var size = data.readUShort(); + + var position = data.position; + var act = this.actions[ i ] = { kind: kind }; + + // range of animation + if ( flag & 1 ) { + + // start and count in frames + act.range = [ data.readUInt(), data.readUInt() ]; + + } + + // time + if ( flag & 2 ) { + + act.time = data.readUInt(); + + } + + // easing + if ( flag & 4 ) { + + act.intrpl = data.readInterpolation(); + + if ( act.intrpl.indexOf( 'back.' ) == 0 ) { + + act.intrplParam0 = data.readFloat(); + + } + else if ( act.intrpl.indexOf( 'elastic.' ) == 0 ) { + + act.intrplParam0 = data.readFloat(); + act.intrplParam1 = data.readFloat(); + + } + + } + + switch ( kind ) { + case SEA3D.Actions.RTT_TARGET: + act.source = sea.getObject( data.readUInt() ); + act.target = sea.getObject( data.readUInt() ); + break; + + case SEA3D.Actions.LOOK_AT: + act.source = sea.getObject( data.readUInt() ); + act.target = sea.getObject( data.readUInt() ); + break; + + case SEA3D.Actions.PLAY_SOUND: + act.sound = sea.getObject( data.readUInt() ); + act.offset = data.readUInt(); + break; + + case SEA3D.Actions.PLAY_ANIMATION: + act.object = sea.getObject( data.readUInt() ); + act.name = data.readUTF8(); + break; + + case SEA3D.Actions.FOG: + act.color = data.readUInt24(); + act.min = data.readFloat(); + act.max = data.readFloat(); + break; + + case SEA3D.Actions.ENVIRONMENT: + act.texture = sea.getObject( data.readUInt() ); + break; + + case SEA3D.Actions.ENVIRONMENT_COLOR: + act.color = data.readUInt24(); + break; + + case SEA3D.Actions.CAMERA: + act.camera = sea.getObject( data.readUInt() ); + break; + + case SEA3D.Actions.SCRIPTS: + act.scripts = data.readScriptList( sea ); + break; + + case SEA3D.Actions.CLASS_OF: + act.classof = sea.getObject( data.readUInt() ); + break; + + default: + console.log( "Action \"" + kind + "\" not found." ); + break; + } + + data.position = position + size; + + } + +}; + +SEA3D.Actions.SCENE = 0; +SEA3D.Actions.ENVIRONMENT_COLOR = 1; +SEA3D.Actions.ENVIRONMENT = 2; +SEA3D.Actions.FOG = 3; +SEA3D.Actions.PLAY_ANIMATION = 4; +SEA3D.Actions.PLAY_SOUND = 5; +SEA3D.Actions.ANIMATION_AUDIO_SYNC = 6; +SEA3D.Actions.LOOK_AT = 7; +SEA3D.Actions.RTT_TARGET = 8; +SEA3D.Actions.CAMERA = 9; +SEA3D.Actions.SCRIPTS = 10; +SEA3D.Actions.CLASS_OF = 11; + +SEA3D.Actions.prototype.type = "act"; + +// +// Properties +// + +SEA3D.Properties = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.tag = data.readProperties( sea ); + this.tag.__name = name; + +}; + +SEA3D.Properties.prototype.type = "prop"; + +// +// File Info +// + +SEA3D.FileInfo = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.tag = data.readProperties( sea ); + this.tag.__name = name; + + sea.info = this.tag; + +}; + +SEA3D.FileInfo.prototype.type = "info"; + +// +// Java Script +// + +SEA3D.JavaScript = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.src = data.readUTF( data.length ); + +}; + +SEA3D.JavaScript.prototype.type = "js"; + +// +// Java Script Method +// + +SEA3D.JavaScriptMethod = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + var count = data.readUShort(); + + this.methods = {}; + + for ( var i = 0; i < count; i ++ ) { + + var flag = data.readUByte(); + var method = data.readUTF8(); + + this.methods[ method ] = { + src : data.readUTF8Long() + } + + } + +}; + +SEA3D.JavaScriptMethod.prototype.type = "jsm"; + +// +// GLSL +// + +SEA3D.GLSL = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.src = data.readUTF( data.length ); + +}; + +SEA3D.GLSL.prototype.type = "glsl"; + +// +// Dummy +// + +SEA3D.Dummy = function( name, data, sea ) { + + SEA3D.Object3D.call( this, name, data, sea ); + + this.transform = data.readMatrix(); + + this.width = data.readFloat(); + this.height = data.readFloat(); + this.depth = data.readFloat(); + + data.readTags( this.readTag.bind( this ) ); + +}; + +SEA3D.Dummy.prototype = Object.create( SEA3D.Object3D.prototype ); +SEA3D.Dummy.prototype.constructor = SEA3D.Dummy; + +SEA3D.Dummy.prototype.type = "dmy"; + +// +// Line +// + +SEA3D.Line = function( name, data, sea ) { + + SEA3D.Object3D.call( this, name, data, sea ); + + this.count = ( this.attrib & 64 ? data.readUInt() : data.readUShort() ) * 3; + this.closed = ( this.attrib & 128 ) != 0; + this.transform = data.readMatrix(); + + this.vertex = []; + + var i = 0; + while ( i < this.count ) { + + this.vertex[ i ++ ] = data.readFloat(); + + } + + data.readTags( this.readTag.bind( this ) ); + +}; + +SEA3D.Line.prototype = Object.create( SEA3D.Object3D.prototype ); +SEA3D.Line.prototype.constructor = SEA3D.Line; + +SEA3D.Line.prototype.type = "line"; + +// +// Mesh2D +// + +SEA3D.Mesh2D = function( name, data, sea ) { + + SEA3D.Object3D.call( this, name, data, sea ); + + if ( this.attrib & 256 ) { + + this.material = sea.getObject( data.readUInt() ); + + } + + this.position = data.readVector3(); + + this.width = data.readFloat(); + this.height = data.readFloat(); + + data.readTags( this.readTag.bind( this ) ); + +}; + +SEA3D.Mesh2D.prototype = Object.create( SEA3D.Object3D.prototype ); +SEA3D.Mesh2D.prototype.constructor = SEA3D.Mesh2D; + +SEA3D.Mesh2D.prototype.type = "m2d"; + +// +// Mesh +// + +SEA3D.Mesh = function( name, data, sea ) { + + SEA3D.Entity3D.call( this, name, data, sea ); + + // MATERIAL + if ( this.attrib & 256 ) { + + this.material = []; + + var len = data.readUByte(); + + if ( len == 1 ) this.material[ 0 ] = sea.getObject( data.readUInt() ); + else { + + var i = 0; + while ( i < len ) { + + var matIndex = data.readUInt(); + + if ( matIndex > 0 ) this.material[ i ++ ] = sea.getObject( matIndex - 1 ); + else this.material[ i ++ ] = undefined; + + } + + } + + } + + if ( this.attrib & 512 ) { + + this.modifiers = []; + + var len = data.readUByte(); + + for ( var i = 0; i < len; i ++ ) { + + this.modifiers[ i ] = sea.getObject( data.readUInt() ); + + } + + } + + this.transform = data.readMatrix(); + + this.geometry = sea.getObject( data.readUInt() ); + + data.readTags( this.readTag.bind( this ) ); + +}; + +SEA3D.Mesh.prototype = Object.create( SEA3D.Entity3D.prototype ); +SEA3D.Mesh.prototype.constructor = SEA3D.Mesh; + +SEA3D.Mesh.prototype.type = "m3d"; + +// +// Skeleton +// + +SEA3D.Skeleton = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + var length = data.readUShort(); + + this.joint = []; + + for ( var i = 0; i < length; i ++ ) { + + this.joint[ i ] = { + name: data.readUTF8(), + parentIndex: data.readUShort() - 1, + inverseBindMatrix: data.readMatrix() + }; + + } + +}; + +SEA3D.Skeleton.prototype.type = "skl"; + +// +// Skeleton Local +// + +SEA3D.SkeletonLocal = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + var length = data.readUShort(); + + this.joint = []; + + for ( var i = 0; i < length; i ++ ) { + + this.joint[ i ] = { + name: data.readUTF8(), + parentIndex: data.readUShort() - 1, + // POSITION XYZ + x: data.readFloat(), + y: data.readFloat(), + z: data.readFloat(), + // QUATERNION XYZW + qx: data.readFloat(), + qy: data.readFloat(), + qz: data.readFloat(), + qw: data.readFloat() + }; + + } + +}; + +SEA3D.SkeletonLocal.prototype.type = "sklq"; + +// +// Animation Base +// + +SEA3D.AnimationBase = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + var flag = data.readUByte(); + + this.sequence = []; + + if ( flag & 1 ) { + + var count = data.readUShort(); + + for ( var i = 0; i < count; i ++ ) { + + flag = data.readUByte(); + + this.sequence[ i ] = { + name: data.readUTF8(), + start: data.readUInt(), + count: data.readUInt(), + repeat: ( flag & 1 ) != 0, + intrpl: ( flag & 2 ) != 0 + } + + } + + } + + this.frameRate = data.readUByte(); + this.numFrames = data.readUInt(); + + // no contains sequence + if ( this.sequence.length == 0 ) { + + this.sequence[ 0 ] = { name: "root", start: 0, count: this.numFrames, repeat: true, intrpl: true }; + + } + +}; + +// +// Animation +// + +SEA3D.Animation = function( name, data, sea ) { + + SEA3D.AnimationBase.call( this, name, data, sea ); + + this.dataList = []; + this.dataList.length = data.readUByte(); + + for ( var i = 0; i < this.dataList.length; i ++ ) { + + var kind = data.readUShort(), + type = data.readUByte(); + + var anmRaw = data.readVector( type, this.numFrames, 0 ); + + this.dataList[ i ] = { + kind: kind, + type: type, + blockSize: SEA3D.Stream.sizeOf( type ), + data: anmRaw + } + + } + +}; + +SEA3D.Animation.POSITION = 0; +SEA3D.Animation.ROTATION = 1; +SEA3D.Animation.SCALE = 2; +SEA3D.Animation.COLOR = 3; +SEA3D.Animation.MULTIPLIER = 4; +SEA3D.Animation.ATTENUATION_START = 5; +SEA3D.Animation.ATTENUATION_END = 6; +SEA3D.Animation.FOV = 7; +SEA3D.Animation.OFFSET_U = 8; +SEA3D.Animation.OFFSET_V = 9; +SEA3D.Animation.SCALE_U = 10; +SEA3D.Animation.SCALE_V = 11; +SEA3D.Animation.ANGLE = 12; +SEA3D.Animation.ALPHA = 13; +SEA3D.Animation.VOLUME = 14; + +SEA3D.Animation.DefaultLerpFuncs = [ + SEA3D.Math.lerp3x, // POSITION + SEA3D.Math.lerpQuat4x, // ROTATION + SEA3D.Math.lerp3x, // SCALE + SEA3D.Math.lerpColor1x, // COLOR + SEA3D.Math.lerp1x, // MULTIPLIER + SEA3D.Math.lerp1x, // ATTENUATION_START + SEA3D.Math.lerp1x, // ATTENUATION_END + SEA3D.Math.lerp1x, // FOV + SEA3D.Math.lerp1x, // OFFSET_U + SEA3D.Math.lerp1x, // OFFSET_V + SEA3D.Math.lerp1x, // SCALE_U + SEA3D.Math.lerp1x, // SCALE_V + SEA3D.Math.lerpAng1x, // ANGLE + SEA3D.Math.lerp1x, // ALPHA + SEA3D.Math.lerp1x // VOLUME +]; + +SEA3D.Animation.prototype = Object.create( SEA3D.AnimationBase.prototype ); +SEA3D.Animation.prototype.constructor = SEA3D.Animation; + +SEA3D.Animation.prototype.type = "anm"; + +// +// Skeleton Animation +// + +SEA3D.SkeletonAnimation = function( name, data, sea ) { + + SEA3D.AnimationBase.call( this, name, data, sea ); + + this.name = name; + this.data = data; + this.sea = sea; + + this.numJoints = data.readUShort() + + this.raw = data.readFloatArray( this.numFrames * this.numJoints * 7 ); + +}; + +SEA3D.SkeletonAnimation.prototype.type = "skla"; + +// +// Morph +// + +SEA3D.Morph = function( name, data, sea ) { + + SEA3D.GeometryBase.call( this, name, data, sea ); + + var useVertex = ( this.attrib & 2 ) != 0; + var useNormal = ( this.attrib & 4 ) != 0; + + var nodeCount = data.readUShort(); + + this.node = []; + + for ( var i = 0; i < nodeCount; i ++ ) { + + var nodeName = data.readUTF8(), + verts, norms; + + if ( useVertex ) verts = data.readFloatArray( this.length ); + if ( useNormal ) norms = data.readFloatArray( this.length ); + + this.node[ i ] = { vertex: verts, normal: norms, name: nodeName } + + } + +}; + +SEA3D.Morph.prototype = Object.create( SEA3D.GeometryBase.prototype ); +SEA3D.Morph.prototype.constructor = SEA3D.Morph; + +SEA3D.Morph.prototype.type = "mph"; + +// +// Vertex Animation +// + +SEA3D.VertexAnimation = function( name, data, sea ) { + + SEA3D.AnimationBase.call( this, name, data, sea ); + + var flags = data.readUByte(); + + this.isBig = ( flags & 1 ) != 0; + + data.readVInt = this.isBig ? data.readUInt : data.readUShort; + + this.numVertex = data.readVInt(); + + this.length = this.numVertex * 3; + + var useVertex = ( flags & 2 ) != 0; + var useNormal = ( flags & 4 ) != 0; + + this.frame = []; + + var i, verts, norms; + + for ( i = 0; i < this.numFrames; i ++ ) { + + if ( useVertex ) verts = data.readFloatArray( this.length ); + if ( useNormal ) norms = data.readFloatArray( this.length ); + + this.frame[ i ] = { vertex: verts, normal: norms } + + } + +}; + +SEA3D.VertexAnimation.prototype = Object.create( SEA3D.AnimationBase.prototype ); +SEA3D.VertexAnimation.prototype.constructor = SEA3D.VertexAnimation; + +SEA3D.VertexAnimation.prototype.type = "vtxa"; + +// +// Camera +// + +SEA3D.Camera = function( name, data, sea ) { + + SEA3D.Object3D.call( this, name, data, sea ); + + if ( this.attrib & 64 ) { + + this.dof = { + distance: data.readFloat(), + range: data.readFloat() + }; + + } + + this.transform = data.readMatrix(); + + this.fov = data.readFloat(); + + data.readTags( this.readTag.bind( this ) ); + +}; + +SEA3D.Camera.prototype = Object.create( SEA3D.Object3D.prototype ); +SEA3D.Camera.prototype.constructor = SEA3D.Camera; + +SEA3D.Camera.prototype.type = "cam"; + +// +// Joint Object +// + +SEA3D.JointObject = function( name, data, sea ) { + + SEA3D.Object3D.call( this, name, data, sea ); + + this.target = sea.getObject( data.readUInt() ); + this.joint = data.readUShort(); + + data.readTags( this.readTag.bind( this ) ); + +}; + +SEA3D.JointObject.prototype = Object.create( SEA3D.Object3D.prototype ); +SEA3D.JointObject.prototype.constructor = SEA3D.JointObject; + +SEA3D.JointObject.prototype.type = "jnt"; + +// +// Light +// + +SEA3D.Light = function( name, data, sea ) { + + SEA3D.Object3D.call( this, name, data, sea ); + + this.attenStart = Number.MAX_VALUE; + this.attenEnd = Number.MAX_VALUE; + + if ( this.attrib & 64 ) { + + var shadowHeader = data.readUByte(); + + this.shadow = {} + + this.shadow.opacity = shadowHeader & 1 ? data.readFloat() : 1; + this.shadow.color = shadowHeader & 2 ? data.readUInt24() : 0x000000; + + } + + if ( this.attrib & 512 ) { + + this.attenStart = data.readFloat(); + this.attenEnd = data.readFloat(); + + } + + this.color = data.readUInt24(); + this.multiplier = data.readFloat(); + +}; + +SEA3D.Light.prototype = Object.create( SEA3D.Object3D.prototype ); +SEA3D.Light.prototype.constructor = SEA3D.Light; + +// +// Point Light +// + +SEA3D.PointLight = function( name, data, sea ) { + + SEA3D.Light.call( this, name, data, sea ); + + if ( this.attrib & 128 ) { + + this.attenuation = { + start: data.readFloat(), + end: data.readFloat() + } + + } + + this.position = data.readVector3(); + + data.readTags( this.readTag.bind( this ) ); + +}; + +SEA3D.PointLight.prototype = Object.create( SEA3D.Light.prototype ); +SEA3D.PointLight.prototype.constructor = SEA3D.PointLight; + +SEA3D.PointLight.prototype.type = "plht"; + +// +// Hemisphere Light +// + +SEA3D.HemisphereLight = function( name, data, sea ) { + + SEA3D.Light.call( this, name, data, sea ); + + if ( this.attrib & 128 ) { + + this.attenuation = { + start: data.readFloat(), + end: data.readFloat() + } + + } + + this.secondColor = data.readUInt24(); + + data.readTags( this.readTag.bind( this ) ); + +}; + +SEA3D.HemisphereLight.prototype = Object.create( SEA3D.Light.prototype ); +SEA3D.HemisphereLight.prototype.constructor = SEA3D.HemisphereLight; + +SEA3D.HemisphereLight.prototype.type = "hlht"; + +// +// Directional Light +// + +SEA3D.DirectionalLight = function( name, data, sea ) { + + SEA3D.Light.call( this, name, data, sea ); + + this.transform = data.readMatrix(); + + data.readTags( this.readTag.bind( this ) ); + +}; + +SEA3D.DirectionalLight.prototype = Object.create( SEA3D.Light.prototype ); +SEA3D.DirectionalLight.prototype.constructor = SEA3D.DirectionalLight; + +SEA3D.DirectionalLight.prototype.type = "dlht"; + +// +// Material +// + +SEA3D.Material = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.technique = []; + + this.attrib = data.readUShort(); + + this.alpha = 1; + this.blendMode = "normal"; + this.alphaThreshold = .5; + + this.bothSides = ( this.attrib & 1 ) != 0; + + this.receiveLights = ( this.attrib & 2 ) == 0; + this.receiveShadows = ( this.attrib & 4 ) == 0; + this.receiveFog = ( this.attrib & 8 ) == 0; + + this.smooth = ( this.attrib & 16 ) == 0; + + if ( this.attrib & 32 ) + this.alpha = data.readFloat(); + + if ( this.attrib & 64 ) + this.blendMode = data.readBlendMode(); + + if ( this.attrib & 128 ) + this.animations = data.readAnimationList( sea ); + + this.depthMask = ( this.attrib & 256 ) == 0; + + var count = data.readUByte(); + + for ( var i = 0; i < count; ++ i ) { + + var kind = data.readUShort(); + var size = data.readUShort(); + var pos = data.position; + var tech, methodAttrib; + + switch ( kind ) { + case SEA3D.Material.DEFAULT: + tech = { + ambientColor: data.readUInt24(), + diffuseColor: data.readUInt24(), + specularColor: data.readUInt24(), + + specular: data.readFloat(), + gloss: data.readFloat() + }; + break; + case SEA3D.Material.COMPOSITE_TEXTURE: + tech = { + composite: sea.getObject( data.readUInt() ) + }; + break; + case SEA3D.Material.DIFFUSE_MAP: + tech = { + texture: sea.getObject( data.readUInt() ) + }; + break; + case SEA3D.Material.SPECULAR_MAP: + tech = { + texture: sea.getObject( data.readUInt() ) + }; + break; + case SEA3D.Material.NORMAL_MAP: + tech = { + texture: sea.getObject( data.readUInt() ) + }; + break; + case SEA3D.Material.REFLECTION: + case SEA3D.Material.FRESNEL_REFLECTION: + tech = { + texture: sea.getObject( data.readUInt() ), + alpha: data.readFloat() + }; + + if ( kind == SEA3D.Material.FRESNEL_REFLECTION ) { + + tech.power = data.readFloat(); + tech.normal = data.readFloat(); + + } + break; + case SEA3D.Material.REFRACTION: + tech = { + texture: sea.getObject( data.readUInt() ), + alpha: data.readFloat(), + ior: data.readFloat() + }; + break; + case SEA3D.Material.RIM: + tech = { + color: data.readUInt24(), + strength: data.readFloat(), + power: data.readFloat(), + blendMode: data.readBlendMode() + }; + break; + case SEA3D.Material.LIGHT_MAP: + tech = { + texture: sea.getObject( data.readUInt() ), + channel: data.readUByte(), + blendMode: data.readBlendMode() + }; + break; + case SEA3D.Material.DETAIL_MAP: + tech = { + texture: sea.getObject( data.readUInt() ), + scale: data.readFloat(), + blendMode: data.readBlendMode() + }; + break; + case SEA3D.Material.CEL: + tech = { + color: data.readUInt24(), + levels: data.readUByte(), + size: data.readFloat(), + specularCutOff: data.readFloat(), + smoothness: data.readFloat() + }; + break; + case SEA3D.Material.TRANSLUCENT: + tech = { + color: data.readUInt24(), + translucency: data.readFloat(), + scattering: data.readFloat() + }; + break; + case SEA3D.Material.BLEND_NORMAL_MAP: + methodAttrib = data.readUByte(); + + tech = { + texture: sea.getObject( data.readUInt() ), + secondaryTexture: sea.getObject( data.readUInt() ) + }; + + if ( methodAttrib & 1 ) { + + tech.offsetX0 = data.readFloat(); + tech.offsetY0 = data.readFloat(); + + tech.offsetX1 = data.readFloat(); + tech.offsetY1 = data.readFloat(); + + } + else { + + tech.offsetX0 = tech.offsetY0 = + tech.offsetX1 = tech.offsetY1 = 0 + + } + + tech.animate = methodAttrib & 2; + break; + case SEA3D.Material.MIRROR_REFLECTION: + tech = { + texture: sea.getObject( data.readUInt() ), + alpha: data.readFloat() + }; + break; + + case SEA3D.Material.AMBIENT_MAP: + tech = { + texture: sea.getObject( data.readUInt() ) + } + break; + + case SEA3D.Material.ALPHA_MAP: + tech = { + texture: sea.getObject( data.readUInt() ) + }; + break; + + case SEA3D.Material.EMISSIVE_MAP: + tech = { + texture: sea.getObject( data.readUInt() ) + }; + break; + + case SEA3D.Material.VERTEX_COLOR: + tech = { + blendMode: data.readBlendMode() + }; + break; + + case SEA3D.Material.WRAP_LIGHTING: + tech = { + color: data.readUInt24(), + strength: data.readFloat() + }; + break; + + case SEA3D.Material.COLOR_REPLACE: + methodAttrib = data.readUByte(); + + tech = { + red: data.readUInt24(), + green: data.readUInt24(), + blue: data.readUInt24() + }; + + if ( methodAttrib & 1 ) tech.mask = sea.getObject( data.readUInt() ); + + if ( methodAttrib & 2 ) tech.alpha = data.readFloat(); + + break; + + case SEA3D.Material.REFLECTION_SPHERICAL: + tech = { + texture: sea.getObject( data.readUInt() ), + alpha: data.readFloat() + }; + break; + + default: + console.warn( "SEA3D: MaterialTechnique not found:", kind.toString( 16 ) ); + + data.position = pos += size; + continue; + } + + tech.kind = kind; + + this.technique.push( tech ); + + data.position = pos += size; + + } + +}; + +SEA3D.Material.DEFAULT = 0; +SEA3D.Material.COMPOSITE_TEXTURE = 1; +SEA3D.Material.DIFFUSE_MAP = 2; +SEA3D.Material.SPECULAR_MAP = 3; +SEA3D.Material.REFLECTION = 4; +SEA3D.Material.REFRACTION = 5; +SEA3D.Material.NORMAL_MAP = 6; +SEA3D.Material.FRESNEL_REFLECTION = 7; +SEA3D.Material.RIM = 8; +SEA3D.Material.LIGHT_MAP = 9; +SEA3D.Material.DETAIL_MAP = 10; +SEA3D.Material.CEL = 11; +SEA3D.Material.TRANSLUCENT = 12; +SEA3D.Material.BLEND_NORMAL_MAP = 13; +SEA3D.Material.MIRROR_REFLECTION = 14; +SEA3D.Material.AMBIENT_MAP = 15; +SEA3D.Material.ALPHA_MAP = 16; +SEA3D.Material.EMISSIVE_MAP = 17; +SEA3D.Material.VERTEX_COLOR = 18; +SEA3D.Material.WRAP_LIGHTING = 19; +SEA3D.Material.COLOR_REPLACE = 20; +SEA3D.Material.REFLECTION_SPHERICAL = 21; + +SEA3D.Material.prototype.type = "mat"; + +// +// Composite +// + +SEA3D.Composite = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + var layerCount = data.readUByte(); + + this.layer = []; + + for ( var i = 0; i < layerCount; i ++ ) { + + this.layer[ i ] = new SEA3D.Composite.prototype.Layer( data, sea ); + + } + +}; + +SEA3D.Composite.prototype.getLayerByName = function( name ) { + + for ( var i = 0; i < this.layer.length; i ++ ) { + + if ( this.layer[ i ].name == name ) { + + return this.layer[ i ]; + + } + + } + +}; + +SEA3D.Composite.prototype.Layer = function( data, sea ) { + + var attrib = data.readUShort(); + + if ( attrib & 1 ) this.texture = new SEA3D.Composite.LayerBitmap( data, sea ); + else this.color = data.readUInt24(); + + if ( attrib & 2 ) { + + this.mask = new SEA3D.Composite.LayerBitmap( data, sea ); + + } + + if ( attrib & 4 ) { + + this.name = data.readUTF8(); + + } + + this.blendMode = attrib & 8 ? data.readBlendMode() : "normal"; + + this.opacity = attrib & 16 ? data.readFloat() : 1; + +}; + +SEA3D.Composite.LayerBitmap = function( data, sea ) { + + this.map = sea.getObject( data.readUInt() ); + + var attrib = data.readUShort(); + + this.channel = attrib & 1 ? data.readUByte() : 0; + this.repeat = attrib & 2 == 0; + this.offsetU = attrib & 4 ? data.readFloat() : 0; + this.offsetV = attrib & 8 ? data.readFloat() : 0; + this.scaleU = attrib & 16 ? data.readFloat() : 1; + this.scaleV = attrib & 32 ? data.readFloat() : 1; + this.rotation = attrib & 64 ? data.readFloat() : 0; + + if ( attrib & 128 ) this.animation = data.readAnimationList( sea ); + +}; + +SEA3D.Composite.prototype.type = "ctex"; + +// +// Sphere +// + +SEA3D.Sphere = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.radius = data.readFloat(); + +}; + +SEA3D.Sphere.prototype.type = "sph"; + +// +// Box +// + +SEA3D.Box = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.width = data.readFloat(); + this.height = data.readFloat(); + this.depth = data.readFloat(); + +}; + +SEA3D.Box.prototype.type = "box"; + +// +// Cone +// + +SEA3D.Cone = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.radius = data.readFloat(); + this.height = data.readFloat(); + +}; + +SEA3D.Cone.prototype.type = "cone"; + +// +// Capsule +// + +SEA3D.Capsule = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.radius = data.readFloat(); + this.height = data.readFloat(); + +}; + +SEA3D.Capsule.prototype.type = "cap"; + +// +// Cylinder +// + +SEA3D.Cylinder = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.radius = data.readFloat(); + this.height = data.readFloat(); + +}; + +SEA3D.Cylinder.prototype.type = "cyl"; + +// +// Geometry Shape +// + +SEA3D.GeometryShape = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.geometry = sea.getObject( data.readUInt() ); + this.subGeometryIndex = data.readUByte(); + +}; + +SEA3D.GeometryShape.prototype.type = "gs"; + +// +// Static Geometry Shape +// + +SEA3D.StaticGeometryShape = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.geometry = sea.getObject( data.readUInt() ); + this.subGeometryIndex = data.readUByte(); + +}; + +SEA3D.StaticGeometryShape.prototype.type = "sgs"; + +// +// Physics +// + +SEA3D.Physics = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.attrib = data.readUShort(); + + this.shape = sea.getObject( data.readUInt() ); + + if ( this.attrib & 1 ) this.target = sea.getObject( data.readUInt() ); + else this.transform = data.readMatrix(); + +}; + +SEA3D.Physics.prototype.readTag = function( kind, data, size ) { + +}; + +// +// Rigidy Body Base +// + +SEA3D.RigidBodyBase = function( name, data, sea ) { + + SEA3D.Physics.call( this, name, data, sea ); + + if ( this.attrib & 32 ) { + + this.linearDamping = data.readFloat(); + this.angularDamping = data.readFloat(); + + } else { + + this.linearDamping = 0; + this.angularDamping = 0; + + } + + this.mass = data.readFloat(); + this.friction = data.readFloat(); + this.restitution = data.readFloat(); + +}; + +SEA3D.RigidBodyBase.prototype = Object.create( SEA3D.Physics.prototype ); +SEA3D.RigidBodyBase.prototype.constructor = SEA3D.RigidBodyBase; + +// +// Rigidy Body +// + +SEA3D.RigidBody = function( name, data, sea ) { + + SEA3D.RigidBodyBase.call( this, name, data, sea ); + + data.readTags( this.readTag.bind( this ) ); + +}; + +SEA3D.RigidBody.prototype = Object.create( SEA3D.RigidBodyBase.prototype ); +SEA3D.RigidBody.prototype.constructor = SEA3D.RigidBody; + +SEA3D.RigidBody.prototype.type = "rb"; + +// +// Car Controller +// + +SEA3D.CarController = function( name, data, sea ) { + + SEA3D.RigidBodyBase.call( this, name, data, sea ); + + this.suspensionStiffness = data.readFloat(); + this.suspensionCompression = data.readFloat(); + this.suspensionDamping = data.readFloat(); + this.maxSuspensionTravelCm = data.readFloat(); + this.frictionSlip = data.readFloat(); + this.maxSuspensionForce = data.readFloat(); + + this.dampingCompression = data.readFloat(); + this.dampingRelaxation = data.readFloat(); + + var count = data.readUByte(); + + this.wheel = []; + + for ( var i = 0; i < count; i ++ ) { + + this.wheel[ i ] = new SEA3D.CarController.Wheel( data, sea ); + + } + + data.readTags( this.readTag.bind( this ) ); + +}; + +SEA3D.CarController.Wheel = function( data, sea ) { + + this.data = data; + this.sea = sea; + + var attrib = data.readUShort(); + + this.isFront = ( attrib & 1 ) != 0, + + this.target = sea.getObject( data.readUInt() ); + + this.pos = data.readVector3(); + this.dir = data.readVector3(); + this.axle = data.readVector3(); + + this.radius = data.readFloat(); + this.suspensionRestLength = data.readFloat(); + +}; + +SEA3D.CarController.prototype = Object.create( SEA3D.RigidBodyBase.prototype ); +SEA3D.CarController.prototype.constructor = SEA3D.CarController; + +SEA3D.CarController.prototype.type = "carc"; + +// +// Constraints +// + +SEA3D.Constraints = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.attrib = data.readUShort(); + + this.disableCollisionsBetweenBodies = this.attrib & 1 != 0; + + this.targetA = sea.getObject( data.readUInt() ); + this.pointA = data.readVector3(); + + if ( this.attrib & 2 ) { + + this.targetB = sea.getObject( data.readUInt() ); + this.pointB = data.readVector3(); + + } + +}; + +// +// P2P Constraint +// + +SEA3D.P2PConstraint = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + SEA3D.Constraints.call( this, name, data, sea ); + +}; + +SEA3D.P2PConstraint.prototype = Object.create( SEA3D.Constraints.prototype ); +SEA3D.P2PConstraint.prototype.constructor = SEA3D.P2PConstraint; + +SEA3D.P2PConstraint.prototype.type = "p2pc"; + +// +// Hinge Constraint +// + +SEA3D.HingeConstraint = function( name, data, sea ) { + + SEA3D.Constraints.call( this, name, data, sea ); + + this.axisA = data.readVector3(); + + if ( this.attrib & 1 ) { + + this.axisB = data.readVector3(); + + } + + if ( this.attrib & 4 ) { + + this.limit = { + low : data.readFloat(), + high : data.readFloat(), + softness : data.readFloat(), + biasFactor : data.readFloat(), + relaxationFactor : data.readFloat() + } + + } + + if ( this.attrib & 8 ) { + + this.angularMotor = { + velocity : data.readFloat(), + impulse : data.readFloat() + } + + } + +}; + +SEA3D.HingeConstraint.prototype = Object.create( SEA3D.Constraints.prototype ); +SEA3D.HingeConstraint.prototype.constructor = SEA3D.HingeConstraint; + +SEA3D.HingeConstraint.prototype.type = "hnec"; + +// +// Cone Twist Constraint +// + +SEA3D.ConeTwistConstraint = function( name, data, sea ) { + + SEA3D.Constraints.call( this, name, data, sea ); + + this.axisA = data.readVector3(); + + if ( this.attrib & 1 ) { + + this.axisB = data.readVector3(); + + } + + if ( this.attrib & 4 ) { + + this.limit = { + swingSpan1 : data.readFloat(), + swingSpan2 : data.readFloat(), + twistSpan : data.readFloat(), + softness : data.readFloat(), + biasFactor : data.readFloat(), + relaxationFactor : data.readFloat() + }; + + } + +}; + +SEA3D.ConeTwistConstraint.prototype = Object.create( SEA3D.Constraints.prototype ); +SEA3D.ConeTwistConstraint.prototype.constructor = SEA3D.ConeTwistConstraint; + +SEA3D.ConeTwistConstraint.prototype.type = "ctwc"; + +// +// Planar Render +// + +SEA3D.PlanarRender = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.attrib = data.readUByte(); + + this.quality = ( this.attrib & 1 ) | ( this.attrib & 2 ); + this.transform = data.readMatrix(); + +}; + +SEA3D.PlanarRender.prototype.type = "rttp"; + +// +// Cube Render +// + +SEA3D.CubeRender = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.attrib = data.readUByte(); + + this.quality = ( this.attrib & 1 ) | ( this.attrib & 2 ); + this.position = data.readVector3(); + +}; + +SEA3D.CubeRender.prototype.type = "rttc"; + +// +// Cube Maps +// + +SEA3D.CubeMap = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.transparent = false; + + var ext = data.readExt(); + + this.faces = []; + + for ( var i = 0; i < 6; i ++ ) { + + var size = data.readUInt(); + + this.faces[ i ] = data.concat( data.position, size ); + + data.position += size; + + } + +}; + +SEA3D.CubeMap.prototype.type = "cmap"; + +// +// JPEG +// + +SEA3D.JPEG = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.transparent = false; + +}; + +SEA3D.JPEG.prototype.type = "jpg"; + +// +// JPEG_XR +// + +SEA3D.JPEG_XR = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.transparent = true; + +}; + +SEA3D.JPEG_XR.prototype.type = "wdp"; + +// +// PNG +// + +SEA3D.PNG = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.transparent = data.buffer[ 25 ] == 0x06; + +}; + +SEA3D.PNG.prototype.type = "png"; + +// +// GIF +// + +SEA3D.GIF = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + + this.transparent = data.buffer[ 11 ] > 0; + +}; + +SEA3D.GIF.prototype.type = "gif"; + +// +// OGG +// + +SEA3D.OGG = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + +}; + +SEA3D.OGG.prototype.type = "ogg"; + +// +// MP3 +// + +SEA3D.MP3 = function( name, data, sea ) { + + this.name = name; + this.data = data; + this.sea = sea; + +}; + +SEA3D.MP3.prototype.type = "mp3"; + +// +// FILE FORMAT +// + +SEA3D.File = function( data ) { + + this.version = SEA3D.VERSION; + this.objects = []; + this.typeClass = {}; + this.typeRead = {}; + this.typeUnique = {}; + this.position = + this.dataPosition = 0; + this.scope = this; + this.streaming = true; + this.timeLimit = 60; + + // SEA3D + this.addClass( SEA3D.FileInfo, true ); + this.addClass( SEA3D.Geometry, true ); + this.addClass( SEA3D.GeometryDelta, true ); + this.addClass( SEA3D.Mesh ); + this.addClass( SEA3D.Mesh2D ); + this.addClass( SEA3D.Material ); + this.addClass( SEA3D.Composite ); + this.addClass( SEA3D.PointLight ); + this.addClass( SEA3D.DirectionalLight ); + this.addClass( SEA3D.HemisphereLight ); + this.addClass( SEA3D.Skeleton, true ); + this.addClass( SEA3D.SkeletonLocal, true ); + this.addClass( SEA3D.SkeletonAnimation, true ); + this.addClass( SEA3D.JointObject ); + this.addClass( SEA3D.Camera ); + this.addClass( SEA3D.Morph, true ); + this.addClass( SEA3D.VertexAnimation, true ); + this.addClass( SEA3D.CubeMap, true ); + this.addClass( SEA3D.Animation ); + this.addClass( SEA3D.Dummy ); + this.addClass( SEA3D.Line ); + this.addClass( SEA3D.SoundPoint ); + this.addClass( SEA3D.PlanarRender ); + this.addClass( SEA3D.CubeRender ); + this.addClass( SEA3D.Actions ); + this.addClass( SEA3D.Container3D ); + this.addClass( SEA3D.Properties ); + + // URL + this.addClass( SEA3D.TextureURL, true ); + + // PHYSICS + this.addClass( SEA3D.Sphere ); + this.addClass( SEA3D.Box ); + this.addClass( SEA3D.Cone ); + this.addClass( SEA3D.Capsule ); + this.addClass( SEA3D.Cylinder ); + this.addClass( SEA3D.GeometryShape ); + this.addClass( SEA3D.StaticGeometryShape ); + this.addClass( SEA3D.RigidBody ); + this.addClass( SEA3D.P2PConstraint ); + this.addClass( SEA3D.HingeConstraint ); + this.addClass( SEA3D.ConeTwistConstraint ); + this.addClass( SEA3D.CarController ); + + // UNIVERSAL + this.addClass( SEA3D.JPEG, true ); + this.addClass( SEA3D.JPEG_XR, true ); + this.addClass( SEA3D.PNG, true ); + this.addClass( SEA3D.GIF, true ); + this.addClass( SEA3D.OGG, true ); + this.addClass( SEA3D.MP3, true ); + this.addClass( SEA3D.JavaScript, true ); + this.addClass( SEA3D.JavaScriptMethod, true ); + this.addClass( SEA3D.GLSL, true ); + +}; + +SEA3D.File.CompressionLibs = {}; +SEA3D.File.DecompressionMethod = {} + +SEA3D.File.setDecompressionEngine = function( id, name, method ) { + + SEA3D.File.CompressionLibs[ id ] = name; + SEA3D.File.DecompressionMethod[ id ] = method; + +}; + +SEA3D.File.prototype.addClass = function( clazz, unique ) { + + this.typeClass[ clazz.prototype.type ] = clazz; + this.typeUnique[ clazz.prototype.type ] = unique === true; + +}; + +SEA3D.File.prototype.readHead = function() { + + if ( this.stream.bytesAvailable < 16 ) + return false; + + if ( this.stream.readUTF( 3 ) != "SEA" ) + console.error( "Invalid SEA3D format." ); + + this.sign = this.stream.readUTF( 3 ); + + this.version = this.stream.readUInt24(); + + if ( this.stream.readUByte() != 0 ) { + + throw new Error( "Protection algorithm not compatible." ); + + } + + this.compressionID = this.stream.readUByte(); + + this.compressionAlgorithm = SEA3D.File.CompressionLibs[ this.compressionID ]; + this.decompressionMethod = SEA3D.File.DecompressionMethod[ this.compressionID ]; + + if ( this.compressionID > 0 && ! this.decompressionMethod ) { + + throw new Error( "Compression algorithm not compatible." ); + + } + + this.length = this.stream.readUInt(); + + this.dataPosition = this.stream.position; + + this.objects.length = 0; + + this.state = this.readBody; + + if ( this.onHead ) + this.onHead( { + file: this, + sign: this.sign + } ); + + return true; + +}; + +SEA3D.File.prototype.getObject = function( index ) { + + return this.objects[ index ]; + +}; + +SEA3D.File.prototype.readSEAObject = function() { + + if ( this.stream.bytesAvailable < 4 ) + return null; + + var size = this.stream.readUInt(); + var position = this.stream.position; + + if ( this.stream.bytesAvailable < size ) + return null; + + var flag = this.stream.readUByte(); + var type = this.stream.readExt(); + var meta = null; + + var name = flag & 1 ? this.stream.readUTF8() : "", + compressed = ( flag & 2 ) != 0, + streaming = ( flag & 4 ) != 0; + + if ( flag & 8 ) { + + var metalen = this.stream.readUShort(); + var metabytes = this.stream.concat( this.stream.position, metalen ); + + this.stream.position += metalen; + + if ( compressed && this.decompressionMethod ) { + + metabytes.set( this.decompressionMethod( metabytes.buffer ) ); + + } + + meta = metabytes.readProperties( this ); + + } + + size -= this.stream.position - position; + position = this.stream.position; + + var data = this.stream.concat( position, size ), + obj; + + if ( this.typeClass[ type ] ) { + + if ( compressed && this.decompressionMethod ) { + + data.buffer = this.decompressionMethod( data.buffer ); + + } + + obj = new this.typeClass[ type ]( name, data, this ); + + if ( this.streaming && streaming && this.typeRead[ type ] ) { + + this.typeRead[ type ].call( this.scope, obj ); + + } + + } + else { + + obj = new SEA3D.Object( name, data, type, this ); + + console.warn( "SEA3D: Unknown format \"" + type + "\" of file \"" + name + "\". Add a module referring for this format." ); + + } + + obj.streaming = streaming; + obj.metadata = meta; + + this.objects.push( this.objects[ obj.type + "/" + obj.name ] = obj ); + + this.dataPosition = position + size; + + ++ this.position; + + return obj; + +}; + +SEA3D.File.prototype.readBody = function() { + + this.timer.update(); + + while ( this.position < this.length ) { + + if ( this.timer.deltaTime < this.timeLimit ) { + + this.stream.position = this.dataPosition; + + var sea = this.readSEAObject(); + + if ( sea ) this.dispatchCompleteObject( sea ); + else return false; + + } + else return false; + + } + + this.state = this.readComplete; + + return true; + +}; + +SEA3D.File.prototype.parse = function() { + + this.timer = new SEA3D.Timer(); + this.position = 0; + + setTimeout( this.parseObject.bind( this ), 10 ); + +}; + +SEA3D.File.prototype.parseObject = function() { + + this.timer.update(); + + while ( this.position < this.length && this.timer.deltaTime < this.timeLimit ) { + + var obj = this.objects[ this.position ++ ], + type = obj.type; + + if ( ! this.typeUnique[ type ] ) delete obj.tag; + + if ( obj.streaming && this.typeRead[ type ] ) { + + if ( obj.tag == undefined ) { + + this.typeRead[ type ].call( this.scope, obj ); + + } + + } + + } + + if ( this.position == this.length ) { + + var elapsedTime = this.timer.elapsedTime; + var message = elapsedTime + "ms, " + this.objects.length + " objects"; + + if ( this.onParseComplete ) { + + this.onParseComplete( { + file: this, + timeTotal: elapsedTime, + message: message + } ); + + } else console.log( "SEA3D Parse Complete:", message ); + + } else { + + if ( this.onParseProgress ) { + + this.onParseProgress( { + file: this, + loaded: this.position, + total: this.length, + progress: this.position / this.length + } ); + + } + + setTimeout( this.parseObject.bind( this ), 10 ); + + } + +}; + +SEA3D.File.prototype.readComplete = function() { + + this.stream.position = this.dataPosition; + + if ( this.stream.readUInt24() != 0x5EA3D1 ) + console.warn( "SEA3D file is corrupted." ); + + delete this.state; + + this.dispatchComplete(); + +}; + +SEA3D.File.prototype.readState = function() { + + while ( this.state && this.state() ); + + if ( this.state ) { + + setTimeout( this.readState.bind( this ), 10 ); + this.dispatchProgress(); + + } + +}; + +SEA3D.File.prototype.read = function( data ) { + + this.stream = new SEA3D.Stream( data ); + this.timer = new SEA3D.Timer(); + this.state = this.readHead; + + this.readState(); + +}; + +SEA3D.File.prototype.dispatchCompleteObject = function( obj ) { + + if ( ! this.onCompleteObject ) return; + + this.onCompleteObject( { + file: this, + object: obj + } ); + +}; + +SEA3D.File.prototype.dispatchProgress = function() { + + if ( ! this.onProgress ) return; + + this.onProgress( { + file: this, + loaded: this.position, + total: this.length, + progress: this.position / this.length + } ); + +}; + +SEA3D.File.prototype.dispatchDownloadProgress = function( position, length ) { + + if ( ! this.onDownloadProgress ) return; + + this.onDownloadProgress( { + file: this, + loaded: position, + total: length, + progress: position / length + } ); + +}; + +SEA3D.File.prototype.dispatchComplete = function() { + + var elapsedTime = this.timer.elapsedTime; + var message = elapsedTime + "ms, " + this.objects.length + " objects"; + + if ( this.onComplete ) this.onComplete( { + file: this, + timeTotal: elapsedTime, + message: message + } ); + else console.log( "SEA3D:", message ); + +}; + +SEA3D.File.prototype.dispatchError = function( id, message ) { + + if ( this.onError ) this.onError( { file: this, id: id, message: message } ); + else console.error( "SEA3D: #" + id, message ); + +}; + +SEA3D.File.prototype.load = function( url ) { + + var file = this, + xhr = new XMLHttpRequest(); + + xhr.open( "GET", url, true ); + xhr.responseType = 'arraybuffer'; + + xhr.onprogress = function( e ) { + + if ( e.lengthComputable ) { + + file.dispatchDownloadProgress( e.loaded, e.total ); + + } + + } + + xhr.onreadystatechange = function() { + + if ( xhr.readyState === 2 ) { + //xhr.getResponseHeader("Content-Length"); + } else if ( xhr.readyState === 3 ) { + // progress + } else if ( xhr.readyState === 4 ) { + + if ( xhr.status === 200 || xhr.status === 0 ) { + + // complete + file.read( this.response ); + + } else { + + this.dispatchError( 1001, "Couldn't load [" + url + "] [" + xhr.status + "]" ); + + } + + } + + } + + xhr.send(); + +}; + +/** + * EventDispatcher.js + * @author mrdoob / http://mrdoob.com/ + * @sunag sunag / http://www.sunag.com.br/ + */ + +SEA3D.EventDispatcher = function () {} + +SEA3D.EventDispatcher.prototype = { + + constructor: SEA3D.EventDispatcher, + + addEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) this._listeners = {}; + + var listeners = this._listeners; + + if ( listeners[ type ] === undefined ) { + + listeners[ type ] = []; + + } + + if ( listeners[ type ].indexOf( listener ) === - 1 ) { + + listeners[ type ].push( listener ); + + } + + }, + + hasEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) return false; + + var listeners = this._listeners; + + if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) { + + return true; + + } + + return false; + + }, + + removeEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) return; + + var listeners = this._listeners; + var listenerArray = listeners[ type ]; + + if ( listenerArray !== undefined ) { + + var index = listenerArray.indexOf( listener ); + + if ( index !== - 1 ) { + + listenerArray.splice( index, 1 ); + + } + + } + + }, + + dispatchEvent: function ( event ) { + + if ( this._listeners === undefined ) return; + + var listeners = this._listeners; + var listenerArray = listeners[ event.type ]; + + if ( listenerArray !== undefined ) { + + event.target = this; + + var array = []; + var length = listenerArray.length; + + for ( var i = 0; i < length; i ++ ) { + + array[ i ] = listenerArray[ i ]; + + } + + for ( var i = 0; i < length; i ++ ) { + + array[ i ].call( this, event ); + + } + + } + + } + +}; + +SEA3D.EventDispatcher.apply = function ( object ) { + + object.addEventListener = SEA3D.EventDispatcher.prototype.addEvenListener; + object.hasEventListener = SEA3D.EventDispatcher.prototype.hasEventListener; + object.removeEventListener = SEA3D.EventDispatcher.prototype.removeEventListener; + object.dispatchEvent = SEA3D.EventDispatcher.prototype.dispatchEvent; + +}; diff --git a/node_modules/three/examples/js/loaders/sea3d/SEA3DDeflate.js b/node_modules/three/examples/js/loaders/sea3d/SEA3DDeflate.js new file mode 100644 index 00000000..91e3d34c --- /dev/null +++ b/node_modules/three/examples/js/loaders/sea3d/SEA3DDeflate.js @@ -0,0 +1,766 @@ +/* + * $Id: rawinflate.js,v 0.3 2013/04/09 14:25:38 dankogai Exp dankogai $ + * + * GNU General Public License, version 2 (GPL-2.0) + * http://opensource.org/licenses/GPL-2.0 + * original: + * http://www.onicos.com/staff/iz/amuse/javascript/expert/inflate.txt + */ + +(function(ctx){ + +/* Copyright (C) 1999 Masanao Izumo + * Version: 1.0.0.1 + * LastModified: Dec 25 1999 + */ + +/* Interface: + * data = zip_inflate(src); + */ + +/* constant parameters */ +var zip_WSIZE = 32768; // Sliding Window size +var zip_STORED_BLOCK = 0; +var zip_STATIC_TREES = 1; +var zip_DYN_TREES = 2; + +/* for inflate */ +var zip_lbits = 9; // bits in base literal/length lookup table +var zip_dbits = 6; // bits in base distance lookup table +var zip_INBUFSIZ = 32768; // Input buffer size +var zip_INBUF_EXTRA = 64; // Extra buffer + +/* variables (inflate) */ +var zip_slide; +var zip_wp; // current position in slide +var zip_fixed_tl = null; // inflate static +var zip_fixed_td; // inflate static +var zip_fixed_bl, fixed_bd; // inflate static +var zip_bit_buf; // bit buffer +var zip_bit_len; // bits in bit buffer +var zip_method; +var zip_eof; +var zip_copy_leng; +var zip_copy_dist; +var zip_tl, zip_td; // literal/length and distance decoder tables +var zip_bl, zip_bd; // number of bits decoded by tl and td + +var zip_inflate_data; +var zip_inflate_pos; + + +/* constant tables (inflate) */ +var zip_MASK_BITS = new Array( + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff); +// Tables for deflate from PKZIP's appnote.txt. +var zip_cplens = new Array( // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0); +/* note: see note #13 above about the 258 in this list. */ +var zip_cplext = new Array( // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99); // 99==invalid +var zip_cpdist = new Array( // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577); +var zip_cpdext = new Array( // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13); +var zip_border = new Array( // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15); +/* objects (inflate) */ + +var zip_HuftList = function() { + this.next = null; + this.list = null; +} + +var zip_HuftNode = function() { + this.e = 0; // number of extra bits or operation + this.b = 0; // number of bits in this code or subcode + + // union + this.n = 0; // literal, length base, or distance base + this.t = null; // (zip_HuftNode) pointer to next level of table +} + +var zip_HuftBuild = function(b, // code lengths in bits (all assumed <= BMAX) + n, // number of codes (assumed <= N_MAX) + s, // number of simple-valued codes (0..s-1) + d, // list of base values for non-simple codes + e, // list of extra bits for non-simple codes + mm // maximum lookup bits + ) { + this.BMAX = 16; // maximum bit length of any code + this.N_MAX = 288; // maximum number of codes in any set + this.status = 0; // 0: success, 1: incomplete table, 2: bad input + this.root = null; // (zip_HuftList) starting table + this.m = 0; // maximum lookup bits, returns actual + +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return zero on success, one if + the given code set is incomplete (the tables are still built in this + case), two if the input is invalid (all zero length codes or an + oversubscribed set of lengths), and three if not enough memory. + The code with value 256 is special, and the tables are constructed + so that no bits beyond that code are fetched when that code is + decoded. */ + { + var a; // counter for codes of length k + var c = new Array(this.BMAX+1); // bit length count table + var el; // length of EOB code (value 256) + var f; // i repeats in table every f entries + var g; // maximum code length + var h; // table level + var i; // counter, current code + var j; // counter + var k; // number of bits in current code + var lx = new Array(this.BMAX+1); // stack of bits per table + var p; // pointer into c[], b[], or v[] + var pidx; // index of p + var q; // (zip_HuftNode) points to current table + var r = new zip_HuftNode(); // table entry for structure assignment + var u = new Array(this.BMAX); // zip_HuftNode[BMAX][] table stack + var v = new Array(this.N_MAX); // values in order of bit length + var w; + var x = new Array(this.BMAX+1);// bit offsets, then code stack + var xp; // pointer into x or c + var y; // number of dummy codes added + var z; // number of entries in current table + var o; + var tail; // (zip_HuftList) + + tail = this.root = null; + for(i = 0; i < c.length; i++) + c[i] = 0; + for(i = 0; i < lx.length; i++) + lx[i] = 0; + for(i = 0; i < u.length; i++) + u[i] = null; + for(i = 0; i < v.length; i++) + v[i] = 0; + for(i = 0; i < x.length; i++) + x[i] = 0; + + // Generate counts for each bit length + el = n > 256 ? b[256] : this.BMAX; // set length of EOB code, if any + p = b; pidx = 0; + i = n; + do { + c[p[pidx]]++; // assume all entries <= BMAX + pidx++; + } while(--i > 0); + if(c[0] == n) { // null input--all zero length codes + this.root = null; + this.m = 0; + this.status = 0; + return; + } + + // Find minimum and maximum length, bound *m by those + for(j = 1; j <= this.BMAX; j++) + if(c[j] != 0) + break; + k = j; // minimum code length + if(mm < j) + mm = j; + for(i = this.BMAX; i != 0; i--) + if(c[i] != 0) + break; + g = i; // maximum code length + if(mm > i) + mm = i; + + // Adjust last length count to fill out codes, if needed + for(y = 1 << j; j < i; j++, y <<= 1) + if((y -= c[j]) < 0) { + this.status = 2; // bad input: more codes than bits + this.m = mm; + return; + } + if((y -= c[i]) < 0) { + this.status = 2; + this.m = mm; + return; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = c; + pidx = 1; + xp = 2; + while(--i > 0) // note that i == g from above + x[xp++] = (j += p[pidx++]); + + // Make a table of values in order of bit lengths + p = b; pidx = 0; + i = 0; + do { + if((j = p[pidx++]) != 0) + v[x[j]++] = i; + } while(++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = v; pidx = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = lx[0] = 0; // no bits decoded yet + q = null; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for(; k <= g; k++) { + a = c[k]; + while(a-- > 0) { + // here i is the Huffman code of length k bits for value p[pidx] + // make tables up to required level + while(k > w + lx[1 + h]) { + w += lx[1 + h]; // add bits already decoded + h++; + + // compute minimum size table less than or equal to *m bits + z = (z = g - w) > mm ? mm : z; // upper limit + if((f = 1 << (j = k - w)) > a + 1) { // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + while(++j < z) { // try smaller tables up to z bits + if((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + if(w + j > el && w < el) + j = el - w; // make EOB code end at table + z = 1 << j; // table entries for j-bit table + lx[1 + h] = j; // set table size in stack + + // allocate and link in new table + q = new Array(z); + for(o = 0; o < z; o++) { + q[o] = new zip_HuftNode(); + } + + if(tail == null) + tail = this.root = new zip_HuftList(); + else + tail = tail.next = new zip_HuftList(); + tail.next = null; + tail.list = q; + u[h] = q; // table starts after link + + /* connect to last table, if there is one */ + if(h > 0) { + x[h] = i; // save pattern for backing up + r.b = lx[h]; // bits to dump before this table + r.e = 16 + j; // bits in this table + r.t = q; // pointer to this table + j = (i & ((1 << w) - 1)) >> (w - lx[h]); + u[h-1][j].e = r.e; + u[h-1][j].b = r.b; + u[h-1][j].n = r.n; + u[h-1][j].t = r.t; + } + } + + // set up table entry in r + r.b = k - w; + if(pidx >= n) + r.e = 99; // out of values--invalid code + else if(p[pidx] < s) { + r.e = (p[pidx] < 256 ? 16 : 15); // 256 is end-of-block code + r.n = p[pidx++]; // simple code is just the value + } else { + r.e = e[p[pidx] - s]; // non-simple--look up in lists + r.n = d[p[pidx++] - s]; + } + + // fill code-like entries with r // + f = 1 << (k - w); + for(j = i >> w; j < z; j += f) { + q[j].e = r.e; + q[j].b = r.b; + q[j].n = r.n; + q[j].t = r.t; + } + + // backwards increment the k-bit code i + for(j = 1 << (k - 1); (i & j) != 0; j >>= 1) + i ^= j; + i ^= j; + + // backup over finished tables + while((i & ((1 << w) - 1)) != x[h]) { + w -= lx[h]; // don't need to update q + h--; + } + } + } + + /* return actual size of base table */ + this.m = lx[1]; + + /* Return true (1) if we were given an incomplete table */ + this.status = ((y != 0 && g != 1) ? 1 : 0); + } /* end of constructor */ +} + + +/* routines (inflate) */ + +var zip_GET_BYTE = function() { + if(zip_inflate_data.length == zip_inflate_pos) + return -1; + return zip_inflate_data[zip_inflate_pos++]; +} + +var zip_NEEDBITS = function(n) { + while(zip_bit_len < n) { + zip_bit_buf |= zip_GET_BYTE() << zip_bit_len; + zip_bit_len += 8; + } +} + +var zip_GETBITS = function(n) { + return zip_bit_buf & zip_MASK_BITS[n]; +} + +var zip_DUMPBITS = function(n) { + zip_bit_buf >>= n; + zip_bit_len -= n; +} + +var zip_inflate_codes = function(buff, off, size) { + /* inflate (decompress) the codes in a deflated (compressed) block. + Return an error code or zero if it all goes ok. */ + var e; // table entry flag/number of extra bits + var t; // (zip_HuftNode) pointer to table entry + var n; + + if(size == 0) + return 0; + + // inflate the coded data + n = 0; + for(;;) { // do until end of block + zip_NEEDBITS(zip_bl); + t = zip_tl.list[zip_GETBITS(zip_bl)]; + e = t.e; + while(e > 16) { + if(e == 99) + return -1; + zip_DUMPBITS(t.b); + e -= 16; + zip_NEEDBITS(e); + t = t.t[zip_GETBITS(e)]; + e = t.e; + } + zip_DUMPBITS(t.b); + + if(e == 16) { // then it's a literal + zip_wp &= zip_WSIZE - 1; + buff[off + n++] = zip_slide[zip_wp++] = t.n; + if(n == size) + return size; + continue; + } + + // exit if end of block + if(e == 15) + break; + + // it's an EOB or a length + + // get length of block to copy + zip_NEEDBITS(e); + zip_copy_leng = t.n + zip_GETBITS(e); + zip_DUMPBITS(e); + + // decode distance of block to copy + zip_NEEDBITS(zip_bd); + t = zip_td.list[zip_GETBITS(zip_bd)]; + e = t.e; + + while(e > 16) { + if(e == 99) + return -1; + zip_DUMPBITS(t.b); + e -= 16; + zip_NEEDBITS(e); + t = t.t[zip_GETBITS(e)]; + e = t.e; + } + zip_DUMPBITS(t.b); + zip_NEEDBITS(e); + zip_copy_dist = zip_wp - t.n - zip_GETBITS(e); + zip_DUMPBITS(e); + + // do the copy + while(zip_copy_leng > 0 && n < size) { + zip_copy_leng--; + zip_copy_dist &= zip_WSIZE - 1; + zip_wp &= zip_WSIZE - 1; + buff[off + n++] = zip_slide[zip_wp++] + = zip_slide[zip_copy_dist++]; + } + + if(n == size) + return size; + } + + zip_method = -1; // done + return n; +} + +var zip_inflate_stored = function(buff, off, size) { + /* "decompress" an inflated type 0 (stored) block. */ + var n; + + // go to byte boundary + n = zip_bit_len & 7; + zip_DUMPBITS(n); + + // get the length and its complement + zip_NEEDBITS(16); + n = zip_GETBITS(16); + zip_DUMPBITS(16); + zip_NEEDBITS(16); + if(n != ((~zip_bit_buf) & 0xffff)) + return -1; // error in compressed data + zip_DUMPBITS(16); + + // read and output the compressed data + zip_copy_leng = n; + + n = 0; + while(zip_copy_leng > 0 && n < size) { + zip_copy_leng--; + zip_wp &= zip_WSIZE - 1; + zip_NEEDBITS(8); + buff[off + n++] = zip_slide[zip_wp++] = + zip_GETBITS(8); + zip_DUMPBITS(8); + } + + if(zip_copy_leng == 0) + zip_method = -1; // done + return n; +} + +var zip_inflate_fixed = function(buff, off, size) { + /* decompress an inflated type 1 (fixed Huffman codes) block. We should + either replace this with a custom decoder, or at least precompute the + Huffman tables. */ + + // if first time, set up tables for fixed blocks + if(zip_fixed_tl == null) { + var i; // temporary variable + var l = new Array(288); // length list for huft_build + var h; // zip_HuftBuild + + // literal table + for(i = 0; i < 144; i++) + l[i] = 8; + for(; i < 256; i++) + l[i] = 9; + for(; i < 280; i++) + l[i] = 7; + for(; i < 288; i++) // make a complete, but wrong code set + l[i] = 8; + zip_fixed_bl = 7; + + h = new zip_HuftBuild(l, 288, 257, zip_cplens, zip_cplext, + zip_fixed_bl); + if(h.status != 0) { + alert("HufBuild error: "+h.status); + return -1; + } + zip_fixed_tl = h.root; + zip_fixed_bl = h.m; + + // distance table + for(i = 0; i < 30; i++) // make an incomplete code set + l[i] = 5; + zip_fixed_bd = 5; + + h = new zip_HuftBuild(l, 30, 0, zip_cpdist, zip_cpdext, zip_fixed_bd); + if(h.status > 1) { + zip_fixed_tl = null; + alert("HufBuild error: "+h.status); + return -1; + } + zip_fixed_td = h.root; + zip_fixed_bd = h.m; + } + + zip_tl = zip_fixed_tl; + zip_td = zip_fixed_td; + zip_bl = zip_fixed_bl; + zip_bd = zip_fixed_bd; + return zip_inflate_codes(buff, off, size); +} + +var zip_inflate_dynamic = function(buff, off, size) { + // decompress an inflated type 2 (dynamic Huffman codes) block. + var i; // temporary variables + var j; + var l; // last length + var n; // number of lengths to get + var t; // (zip_HuftNode) literal/length code table + var nb; // number of bit length codes + var nl; // number of literal/length codes + var nd; // number of distance codes + var ll = new Array(286+30); // literal/length and distance code lengths + var h; // (zip_HuftBuild) + + for(i = 0; i < ll.length; i++) + ll[i] = 0; + + // read in table lengths + zip_NEEDBITS(5); + nl = 257 + zip_GETBITS(5); // number of literal/length codes + zip_DUMPBITS(5); + zip_NEEDBITS(5); + nd = 1 + zip_GETBITS(5); // number of distance codes + zip_DUMPBITS(5); + zip_NEEDBITS(4); + nb = 4 + zip_GETBITS(4); // number of bit length codes + zip_DUMPBITS(4); + if(nl > 286 || nd > 30) + return -1; // bad lengths + + // read in bit-length-code lengths + for(j = 0; j < nb; j++) + { + zip_NEEDBITS(3); + ll[zip_border[j]] = zip_GETBITS(3); + zip_DUMPBITS(3); + } + for(; j < 19; j++) + ll[zip_border[j]] = 0; + + // build decoding table for trees--single level, 7 bit lookup + zip_bl = 7; + h = new zip_HuftBuild(ll, 19, 19, null, null, zip_bl); + if(h.status != 0) + return -1; // incomplete code set + + zip_tl = h.root; + zip_bl = h.m; + + // read in literal and distance code lengths + n = nl + nd; + i = l = 0; + while(i < n) { + zip_NEEDBITS(zip_bl); + t = zip_tl.list[zip_GETBITS(zip_bl)]; + j = t.b; + zip_DUMPBITS(j); + j = t.n; + if(j < 16) // length of code in bits (0..15) + ll[i++] = l = j; // save last length in l + else if(j == 16) { // repeat last length 3 to 6 times + zip_NEEDBITS(2); + j = 3 + zip_GETBITS(2); + zip_DUMPBITS(2); + if(i + j > n) + return -1; + while(j-- > 0) + ll[i++] = l; + } else if(j == 17) { // 3 to 10 zero length codes + zip_NEEDBITS(3); + j = 3 + zip_GETBITS(3); + zip_DUMPBITS(3); + if(i + j > n) + return -1; + while(j-- > 0) + ll[i++] = 0; + l = 0; + } else { // j == 18: 11 to 138 zero length codes + zip_NEEDBITS(7); + j = 11 + zip_GETBITS(7); + zip_DUMPBITS(7); + if(i + j > n) + return -1; + while(j-- > 0) + ll[i++] = 0; + l = 0; + } + } + + // build the decoding tables for literal/length and distance codes + zip_bl = zip_lbits; + h = new zip_HuftBuild(ll, nl, 257, zip_cplens, zip_cplext, zip_bl); + if(zip_bl == 0) // no literals or lengths + h.status = 1; + if(h.status != 0) { + if(h.status == 1) + ;// **incomplete literal tree** + return -1; // incomplete code set + } + zip_tl = h.root; + zip_bl = h.m; + + for(i = 0; i < nd; i++) + ll[i] = ll[i + nl]; + zip_bd = zip_dbits; + h = new zip_HuftBuild(ll, nd, 0, zip_cpdist, zip_cpdext, zip_bd); + zip_td = h.root; + zip_bd = h.m; + + if(zip_bd == 0 && nl > 257) { // lengths but no distances + // **incomplete distance tree** + return -1; + } + + if(h.status == 1) { + ;// **incomplete distance tree** + } + if(h.status != 0) + return -1; + + // decompress until an end-of-block code + return zip_inflate_codes(buff, off, size); +} + +var zip_inflate_start = function() { + var i; + + if(zip_slide == null) + zip_slide = new Array(2 * zip_WSIZE); + zip_wp = 0; + zip_bit_buf = 0; + zip_bit_len = 0; + zip_method = -1; + zip_eof = false; + zip_copy_leng = zip_copy_dist = 0; + zip_tl = null; +} + +var zip_inflate_internal = function(buff, off, size) { + // decompress an inflated entry + var n, i; + + n = 0; + while(n < size) { + if(zip_eof && zip_method == -1) + return n; + + if(zip_copy_leng > 0) { + if(zip_method != zip_STORED_BLOCK) { + // STATIC_TREES or DYN_TREES + while(zip_copy_leng > 0 && n < size) { + zip_copy_leng--; + zip_copy_dist &= zip_WSIZE - 1; + zip_wp &= zip_WSIZE - 1; + buff[off + n++] = zip_slide[zip_wp++] = + zip_slide[zip_copy_dist++]; + } + } else { + while(zip_copy_leng > 0 && n < size) { + zip_copy_leng--; + zip_wp &= zip_WSIZE - 1; + zip_NEEDBITS(8); + buff[off + n++] = zip_slide[zip_wp++] = zip_GETBITS(8); + zip_DUMPBITS(8); + } + if(zip_copy_leng == 0) + zip_method = -1; // done + } + if(n == size) + return n; + } + + if(zip_method == -1) { + if(zip_eof) + break; + + // read in last block bit + zip_NEEDBITS(1); + if(zip_GETBITS(1) != 0) + zip_eof = true; + zip_DUMPBITS(1); + + // read in block type + zip_NEEDBITS(2); + zip_method = zip_GETBITS(2); + zip_DUMPBITS(2); + zip_tl = null; + zip_copy_leng = 0; + } + + switch(zip_method) { + case 0: // zip_STORED_BLOCK + i = zip_inflate_stored(buff, off + n, size - n); + break; + + case 1: // zip_STATIC_TREES + if(zip_tl != null) + i = zip_inflate_codes(buff, off + n, size - n); + else + i = zip_inflate_fixed(buff, off + n, size - n); + break; + + case 2: // zip_DYN_TREES + if(zip_tl != null) + i = zip_inflate_codes(buff, off + n, size - n); + else + i = zip_inflate_dynamic(buff, off + n, size - n); + break; + + default: // error + i = -1; + break; + } + + if(i == -1) { + if(zip_eof) + return 0; + return -1; + } + n += i; + } + return n; +} + +var zip_inflate = function(data) { + var i, j, pos = 0; + + zip_inflate_start(); + zip_inflate_data = new Uint8Array(data); + zip_inflate_pos = 0; + + var buff = new Uint8Array(1024); + + var out = []; + while((i = zip_inflate_internal(buff, 0, buff.length)) > 0) + for(j = 0; j < i; j++) + out[pos++] = buff[j]; + + zip_inflate_data = null; // G.C. + return new Uint8Array(out).buffer; +} + +if (! ctx.RawDeflate) ctx.RawDeflate = {}; +ctx.RawDeflate.inflate = zip_inflate; + +})(this); + +/** + * SEA3D DEFLATE + * @author Sunag / http://www.sunag.com.br/ + */ + +SEA3D.File.DeflateUncompress = function( data ) { + + return RawDeflate.inflate( data ); + +} + +SEA3D.File.setDecompressionEngine( 1, "deflate", SEA3D.File.DeflateUncompress ); \ No newline at end of file diff --git a/node_modules/three/examples/js/loaders/sea3d/SEA3DLZMA.js b/node_modules/three/examples/js/loaders/sea3d/SEA3DLZMA.js new file mode 100644 index 00000000..727ba5e7 --- /dev/null +++ b/node_modules/three/examples/js/loaders/sea3d/SEA3DLZMA.js @@ -0,0 +1,598 @@ +/* +Copyright (c) 2011 Juan Mellado + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +/* +References: +- "LZMA SDK" by Igor Pavlov + http://www.7-zip.org/sdk.html +*/ + +var LZMA = LZMA || {}; + +LZMA.OutWindow = function(){ + this._windowSize = 0; +}; + +LZMA.OutWindow.prototype.create = function(windowSize){ + if ( (!this._buffer) || (this._windowSize !== windowSize) ){ + this._buffer = []; + } + this._windowSize = windowSize; + this._pos = 0; + this._streamPos = 0; +}; + +LZMA.OutWindow.prototype.flush = function(){ + var size = this._pos - this._streamPos; + if (size !== 0){ + while(size --){ + this._stream.writeByte(this._buffer[this._streamPos ++]); + } + if (this._pos >= this._windowSize){ + this._pos = 0; + } + this._streamPos = this._pos; + } +}; + +LZMA.OutWindow.prototype.releaseStream = function(){ + this.flush(); + this._stream = null; +}; + +LZMA.OutWindow.prototype.setStream = function(stream){ + this.releaseStream(); + this._stream = stream; +}; + +LZMA.OutWindow.prototype.init = function(solid){ + if (!solid){ + this._streamPos = 0; + this._pos = 0; + } +}; + +LZMA.OutWindow.prototype.copyBlock = function(distance, len){ + var pos = this._pos - distance - 1; + if (pos < 0){ + pos += this._windowSize; + } + while(len --){ + if (pos >= this._windowSize){ + pos = 0; + } + this._buffer[this._pos ++] = this._buffer[pos ++]; + if (this._pos >= this._windowSize){ + this.flush(); + } + } +}; + +LZMA.OutWindow.prototype.putByte = function(b){ + this._buffer[this._pos ++] = b; + if (this._pos >= this._windowSize){ + this.flush(); + } +}; + +LZMA.OutWindow.prototype.getByte = function(distance){ + var pos = this._pos - distance - 1; + if (pos < 0){ + pos += this._windowSize; + } + return this._buffer[pos]; +}; + +LZMA.RangeDecoder = function(){ +}; + +LZMA.RangeDecoder.prototype.setStream = function(stream){ + this._stream = stream; +}; + +LZMA.RangeDecoder.prototype.releaseStream = function(){ + this._stream = null; +}; + +LZMA.RangeDecoder.prototype.init = function(){ + var i = 5; + + this._code = 0; + this._range = -1; + + while(i --){ + this._code = (this._code << 8) | this._stream.readByte(); + } +}; + +LZMA.RangeDecoder.prototype.decodeDirectBits = function(numTotalBits){ + var result = 0, i = numTotalBits, t; + + while(i --){ + this._range >>>= 1; + t = (this._code - this._range) >>> 31; + this._code -= this._range & (t - 1); + result = (result << 1) | (1 - t); + + if ( (this._range & 0xff000000) === 0){ + this._code = (this._code << 8) | this._stream.readByte(); + this._range <<= 8; + } + } + + return result; +}; + +LZMA.RangeDecoder.prototype.decodeBit = function(probs, index){ + var prob = probs[index], + newBound = (this._range >>> 11) * prob; + + if ( (this._code ^ 0x80000000) < (newBound ^ 0x80000000) ){ + this._range = newBound; + probs[index] += (2048 - prob) >>> 5; + if ( (this._range & 0xff000000) === 0){ + this._code = (this._code << 8) | this._stream.readByte(); + this._range <<= 8; + } + return 0; + } + + this._range -= newBound; + this._code -= newBound; + probs[index] -= prob >>> 5; + if ( (this._range & 0xff000000) === 0){ + this._code = (this._code << 8) | this._stream.readByte(); + this._range <<= 8; + } + return 1; +}; + +LZMA.initBitModels = function(probs, len){ + while(len --){ + probs[len] = 1024; + } +}; + +LZMA.BitTreeDecoder = function(numBitLevels){ + this._models = []; + this._numBitLevels = numBitLevels; +}; + +LZMA.BitTreeDecoder.prototype.init = function(){ + LZMA.initBitModels(this._models, 1 << this._numBitLevels); +}; + +LZMA.BitTreeDecoder.prototype.decode = function(rangeDecoder){ + var m = 1, i = this._numBitLevels; + + while(i --){ + m = (m << 1) | rangeDecoder.decodeBit(this._models, m); + } + return m - (1 << this._numBitLevels); +}; + +LZMA.BitTreeDecoder.prototype.reverseDecode = function(rangeDecoder){ + var m = 1, symbol = 0, i = 0, bit; + + for (; i < this._numBitLevels; ++ i){ + bit = rangeDecoder.decodeBit(this._models, m); + m = (m << 1) | bit; + symbol |= bit << i; + } + return symbol; +}; + +LZMA.reverseDecode2 = function(models, startIndex, rangeDecoder, numBitLevels){ + var m = 1, symbol = 0, i = 0, bit; + + for (; i < numBitLevels; ++ i){ + bit = rangeDecoder.decodeBit(models, startIndex + m); + m = (m << 1) | bit; + symbol |= bit << i; + } + return symbol; +}; + +LZMA.LenDecoder = function(){ + this._choice = []; + this._lowCoder = []; + this._midCoder = []; + this._highCoder = new LZMA.BitTreeDecoder(8); + this._numPosStates = 0; +}; + +LZMA.LenDecoder.prototype.create = function(numPosStates){ + for (; this._numPosStates < numPosStates; ++ this._numPosStates){ + this._lowCoder[this._numPosStates] = new LZMA.BitTreeDecoder(3); + this._midCoder[this._numPosStates] = new LZMA.BitTreeDecoder(3); + } +}; + +LZMA.LenDecoder.prototype.init = function(){ + var i = this._numPosStates; + LZMA.initBitModels(this._choice, 2); + while(i --){ + this._lowCoder[i].init(); + this._midCoder[i].init(); + } + this._highCoder.init(); +}; + +LZMA.LenDecoder.prototype.decode = function(rangeDecoder, posState){ + if (rangeDecoder.decodeBit(this._choice, 0) === 0){ + return this._lowCoder[posState].decode(rangeDecoder); + } + if (rangeDecoder.decodeBit(this._choice, 1) === 0){ + return 8 + this._midCoder[posState].decode(rangeDecoder); + } + return 16 + this._highCoder.decode(rangeDecoder); +}; + +LZMA.Decoder2 = function(){ + this._decoders = []; +}; + +LZMA.Decoder2.prototype.init = function(){ + LZMA.initBitModels(this._decoders, 0x300); +}; + +LZMA.Decoder2.prototype.decodeNormal = function(rangeDecoder){ + var symbol = 1; + + do{ + symbol = (symbol << 1) | rangeDecoder.decodeBit(this._decoders, symbol); + }while(symbol < 0x100); + + return symbol & 0xff; +}; + +LZMA.Decoder2.prototype.decodeWithMatchByte = function(rangeDecoder, matchByte){ + var symbol = 1, matchBit, bit; + + do{ + matchBit = (matchByte >> 7) & 1; + matchByte <<= 1; + bit = rangeDecoder.decodeBit(this._decoders, ( (1 + matchBit) << 8) + symbol); + symbol = (symbol << 1) | bit; + if (matchBit !== bit){ + while(symbol < 0x100){ + symbol = (symbol << 1) | rangeDecoder.decodeBit(this._decoders, symbol); + } + break; + } + }while(symbol < 0x100); + + return symbol & 0xff; +}; + +LZMA.LiteralDecoder = function(){ +}; + +LZMA.LiteralDecoder.prototype.create = function(numPosBits, numPrevBits){ + var i; + + if (this._coders + && (this._numPrevBits === numPrevBits) + && (this._numPosBits === numPosBits) ){ + return; + } + this._numPosBits = numPosBits; + this._posMask = (1 << numPosBits) - 1; + this._numPrevBits = numPrevBits; + + this._coders = []; + + i = 1 << (this._numPrevBits + this._numPosBits); + while(i --){ + this._coders[i] = new LZMA.Decoder2(); + } +}; + +LZMA.LiteralDecoder.prototype.init = function(){ + var i = 1 << (this._numPrevBits + this._numPosBits); + while(i --){ + this._coders[i].init(); + } +}; + +LZMA.LiteralDecoder.prototype.getDecoder = function(pos, prevByte){ + return this._coders[( (pos & this._posMask) << this._numPrevBits) + + ( (prevByte & 0xff) >>> (8 - this._numPrevBits) )]; +}; + +LZMA.Decoder = function(){ + this._outWindow = new LZMA.OutWindow(); + this._rangeDecoder = new LZMA.RangeDecoder(); + this._isMatchDecoders = []; + this._isRepDecoders = []; + this._isRepG0Decoders = []; + this._isRepG1Decoders = []; + this._isRepG2Decoders = []; + this._isRep0LongDecoders = []; + this._posSlotDecoder = []; + this._posDecoders = []; + this._posAlignDecoder = new LZMA.BitTreeDecoder(4); + this._lenDecoder = new LZMA.LenDecoder(); + this._repLenDecoder = new LZMA.LenDecoder(); + this._literalDecoder = new LZMA.LiteralDecoder(); + this._dictionarySize = -1; + this._dictionarySizeCheck = -1; + + this._posSlotDecoder[0] = new LZMA.BitTreeDecoder(6); + this._posSlotDecoder[1] = new LZMA.BitTreeDecoder(6); + this._posSlotDecoder[2] = new LZMA.BitTreeDecoder(6); + this._posSlotDecoder[3] = new LZMA.BitTreeDecoder(6); +}; + +LZMA.Decoder.prototype.setDictionarySize = function(dictionarySize){ + if (dictionarySize < 0){ + return false; + } + if (this._dictionarySize !== dictionarySize){ + this._dictionarySize = dictionarySize; + this._dictionarySizeCheck = Math.max(this._dictionarySize, 1); + this._outWindow.create( Math.max(this._dictionarySizeCheck, 4096) ); + } + return true; +}; + +LZMA.Decoder.prototype.setLcLpPb = function(lc, lp, pb){ + var numPosStates = 1 << pb; + + if (lc > 8 || lp > 4 || pb > 4){ + return false; + } + + this._literalDecoder.create(lp, lc); + + this._lenDecoder.create(numPosStates); + this._repLenDecoder.create(numPosStates); + this._posStateMask = numPosStates - 1; + + return true; +}; + +LZMA.Decoder.prototype.init = function(){ + var i = 4; + + this._outWindow.init(false); + + LZMA.initBitModels(this._isMatchDecoders, 192); + LZMA.initBitModels(this._isRep0LongDecoders, 192); + LZMA.initBitModels(this._isRepDecoders, 12); + LZMA.initBitModels(this._isRepG0Decoders, 12); + LZMA.initBitModels(this._isRepG1Decoders, 12); + LZMA.initBitModels(this._isRepG2Decoders, 12); + LZMA.initBitModels(this._posDecoders, 114); + + this._literalDecoder.init(); + + while(i --){ + this._posSlotDecoder[i].init(); + } + + this._lenDecoder.init(); + this._repLenDecoder.init(); + this._posAlignDecoder.init(); + this._rangeDecoder.init(); +}; + +LZMA.Decoder.prototype.decode = function(inStream, outStream, outSize){ + var state = 0, rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0, nowPos64 = 0, prevByte = 0, + posState, decoder2, len, distance, posSlot, numDirectBits; + + this._rangeDecoder.setStream(inStream); + this._outWindow.setStream(outStream); + + this.init(); + + while(outSize < 0 || nowPos64 < outSize){ + posState = nowPos64 & this._posStateMask; + + if (this._rangeDecoder.decodeBit(this._isMatchDecoders, (state << 4) + posState) === 0){ + decoder2 = this._literalDecoder.getDecoder(nowPos64 ++, prevByte); + + if (state >= 7){ + prevByte = decoder2.decodeWithMatchByte(this._rangeDecoder, this._outWindow.getByte(rep0) ); + }else{ + prevByte = decoder2.decodeNormal(this._rangeDecoder); + } + this._outWindow.putByte(prevByte); + + state = state < 4? 0: state - (state < 10? 3: 6); + + }else{ + + if (this._rangeDecoder.decodeBit(this._isRepDecoders, state) === 1){ + len = 0; + if (this._rangeDecoder.decodeBit(this._isRepG0Decoders, state) === 0){ + if (this._rangeDecoder.decodeBit(this._isRep0LongDecoders, (state << 4) + posState) === 0){ + state = state < 7? 9: 11; + len = 1; + } + }else{ + if (this._rangeDecoder.decodeBit(this._isRepG1Decoders, state) === 0){ + distance = rep1; + }else{ + if (this._rangeDecoder.decodeBit(this._isRepG2Decoders, state) === 0){ + distance = rep2; + }else{ + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + if (len === 0){ + len = 2 + this._repLenDecoder.decode(this._rangeDecoder, posState); + state = state < 7? 8: 11; + } + }else{ + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + + len = 2 + this._lenDecoder.decode(this._rangeDecoder, posState); + state = state < 7? 7: 10; + + posSlot = this._posSlotDecoder[len <= 5? len - 2: 3].decode(this._rangeDecoder); + if (posSlot >= 4){ + + numDirectBits = (posSlot >> 1) - 1; + rep0 = (2 | (posSlot & 1) ) << numDirectBits; + + if (posSlot < 14){ + rep0 += LZMA.reverseDecode2(this._posDecoders, + rep0 - posSlot - 1, this._rangeDecoder, numDirectBits); + }else{ + rep0 += this._rangeDecoder.decodeDirectBits(numDirectBits - 4) << 4; + rep0 += this._posAlignDecoder.reverseDecode(this._rangeDecoder); + if (rep0 < 0){ + if (rep0 === -1){ + break; + } + return false; + } + } + }else{ + rep0 = posSlot; + } + } + + if (rep0 >= nowPos64 || rep0 >= this._dictionarySizeCheck){ + return false; + } + + this._outWindow.copyBlock(rep0, len); + nowPos64 += len; + prevByte = this._outWindow.getByte(0); + } + } + + this._outWindow.flush(); + this._outWindow.releaseStream(); + this._rangeDecoder.releaseStream(); + + return true; +}; + +LZMA.Decoder.prototype.setDecoderProperties = function(properties){ + var value, lc, lp, pb, dictionarySize; + + if (properties.size < 5){ + return false; + } + + value = properties.readByte(); + lc = value % 9; + value = ~~(value / 9); + lp = value % 5; + pb = ~~(value / 5); + + if ( !this.setLcLpPb(lc, lp, pb) ){ + return false; + } + + dictionarySize = properties.readByte(); + dictionarySize |= properties.readByte() << 8; + dictionarySize |= properties.readByte() << 16; + dictionarySize += properties.readByte() * 16777216; + + return this.setDictionarySize(dictionarySize); +}; + +LZMA.decompress = function(properties, inStream, outStream, outSize){ + var decoder = new LZMA.Decoder(); + + if ( !decoder.setDecoderProperties(properties) ){ + throw "Incorrect stream properties"; + } + + if ( !decoder.decode(inStream, outStream, outSize) ){ + throw "Error in data stream"; + } + + return true; +}; + +LZMA.decompressFile = function(inStream, outStream){ + var decoder = new LZMA.Decoder(), outSize; + + if ( !decoder.setDecoderProperties(inStream) ){ + throw "Incorrect stream properties"; + } + + outSize = inStream.readByte(); + outSize |= inStream.readByte() << 8; + outSize |= inStream.readByte() << 16; + outSize += inStream.readByte() * 16777216; + + inStream.readByte(); + inStream.readByte(); + inStream.readByte(); + inStream.readByte(); + + if ( !decoder.decode(inStream, outStream, outSize) ){ + throw "Error in data stream"; + } + + return true; +}; + +/** + * SEA3D LZMA + * @author Sunag / http://www.sunag.com.br/ + */ + +SEA3D.File.LZMAUncompress = function( data ) { + + data = new Uint8Array( data ); + + var inStream = { + data: data, + position: 0, + readByte: function() { + + return this.data[ this.position ++ ]; + + } + } + + var outStream = { + data: [], + position: 0, + writeByte: function( value ) { + + this.data[ this.position ++ ] = value; + + } + } + + LZMA.decompressFile( inStream, outStream ); + + return new Uint8Array( outStream.data ).buffer; + +} + +SEA3D.File.setDecompressionEngine( 2, "lzma", SEA3D.File.LZMAUncompress ); diff --git a/node_modules/three/examples/js/loaders/sea3d/SEA3DLoader.js b/node_modules/three/examples/js/loaders/sea3d/SEA3DLoader.js new file mode 100644 index 00000000..f799fe81 --- /dev/null +++ b/node_modules/three/examples/js/loaders/sea3d/SEA3DLoader.js @@ -0,0 +1,2469 @@ +/** + * SEA3D for Three.JS + * @author Sunag / http://www.sunag.com.br/ + */ + +'use strict'; + +// +// SEA3D +// + +THREE.SEA3D = function( config ) { + + this.config = config || {}; + + if ( this.config.script == undefined ) this.config.script = true; + if ( this.config.autoPlay == undefined ) this.config.autoPlay = false; + if ( this.config.multiplier == undefined ) this.config.multiplier = 1; + if ( this.config.bounding == undefined ) this.config.bounding = true; + if ( this.config.standardMaterial == undefined ) this.config.standardMaterial = true; + if ( this.config.audioRolloffFactor == undefined ) this.config.audioRolloffFactor = 10; + if ( this.config.timeLimit == undefined ) this.config.timeLimit = 10; + if ( this.config.streaming == undefined ) this.config.streaming = true; + if ( this.config.lights == undefined ) this.config.lights = true; + +}; + +THREE.SEA3D.prototype = { + constructor: THREE.SEA3D, + + addEventListener: THREE.EventDispatcher.prototype.addEventListener, + hasEventListener: THREE.EventDispatcher.prototype.hasEventListener, + removeEventListener: THREE.EventDispatcher.prototype.removeEventListener, + dispatchEvent: THREE.EventDispatcher.prototype.dispatchEvent, + + set container ( val ) { + + this.config.container = val; + + }, + + get container () { + + return this.config.container; + + } +}; + +// +// Defaults +// + +THREE.SEA3D.BACKGROUND_COLOR = 0x333333; +THREE.SEA3D.HELPER_COLOR = 0x9AB9E5; +THREE.SEA3D.RTT_SIZE = 512; + +// +// Shader +// + +THREE.SEA3D.ShaderLib = {}; + +THREE.SEA3D.ShaderLib.replaceCode = function( src, target, replace ) { + + for ( var i = 0; i < target.length; i ++ ) { + + var tar = target[ i ], + rep = replace[ i ], + index = src.indexOf( tar ); + + if ( index > - 1 ) { + + src = src.substring( 0, index ) + rep + src.substring( index + tar.length ); + + } + + } + + return src; + +}; + +// TODO: Emissive to Ambient Color Extension + +THREE.SEA3D.ShaderLib.fragStdMtl = THREE.SEA3D.ShaderLib.replaceCode( THREE.ShaderLib.phong.fragmentShader, [ + // Target + 'outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) * specular + totalSpecularLight + totalEmissiveLight;', // METAL + 'outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) + totalSpecularLight + totalEmissiveLight;' +], [ + // Replace To + 'outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight + totalEmissiveLight ) * specular + totalSpecularLight;', // METAL + 'outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight + totalEmissiveLight ) + totalSpecularLight;' +] ); + +// +// Standard Material +// + +THREE.SEA3D.StandardMaterial = function () { + + THREE.MeshPhongMaterial.call( this ); + +}; + +THREE.SEA3D.StandardMaterial.prototype = Object.create( THREE.MeshPhongMaterial.prototype ); +THREE.SEA3D.StandardMaterial.prototype.constructor = THREE.SEA3D.StandardMaterial; + +THREE.SEA3D.StandardMaterial.prototype.copy = function ( source ) { + + THREE.MeshPhongMaterial.prototype.copy.call( this, source ); + + return this; + +}; + +THREE.SEA3D.StandardMaterial.prototype.clone = function() { + + return new THREE.SEA3D.StandardMaterial().copy( this ); + +}; + +THREE.SEA3D.StandardMaterial.prototype.__defineSetter__( "__webglShader", function( val ) { + + val.fragmentShader = THREE.SEA3D.ShaderLib.fragStdMtl; + this.__webglShader__ = val; + +} ) + +THREE.SEA3D.StandardMaterial.prototype.__defineGetter__( "__webglShader", function() { + + return this.__webglShader__; + +} ) + +// +// Container +// + +THREE.SEA3D.Object3D = function ( ) { + + THREE.Object3D.call( this ); + +}; + +THREE.SEA3D.Object3D.prototype = Object.create( THREE.Object3D.prototype ); +THREE.SEA3D.Object3D.prototype.constructor = THREE.SEA3D.Object3D; + +// Relative Animation Extension +// TODO: It can be done with shader + +THREE.SEA3D.Object3D.prototype.updateAnimateMatrix = function( force ) { + + if ( this.matrixAutoUpdate === true ) this.updateMatrix(); + + if ( this.matrixWorldNeedsUpdate === true || force === true ) { + + if ( this.parent === null ) { + + this.matrixWorld.copy( this.matrix ); + + } else { + + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + + } + + this.animateMatrix.compose( this.animatePosition, this.animateQuaternion, this.animateScale ); + + this.matrixWorld.multiplyMatrices( this.matrixWorld, this.animateMatrix ); + + this.matrixWorldNeedsUpdate = false; + + force = true; + + } + + // update children + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].updateMatrixWorld( force ); + + } + +}; + +THREE.SEA3D.Object3D.prototype.setAnimateMatrix = function( val ) { + + if ( this.getAnimateMatrix() == val ) + return; + + if ( val ) { + + this.animateMatrix = new THREE.Matrix4(); + + this.animatePosition = new THREE.Vector3(); + this.animateQuaternion = new THREE.Quaternion(); + this.animateScale = new THREE.Vector3( 1, 1, 1 ); + + this.updateMatrixWorld = THREE.SEA3D.Object3D.prototype.updateAnimateMatrix; + + } else { + + delete this.animateMatrix; + + delete this.animatePosition; + delete this.animateQuaternion; + delete this.animateScale; + + this.updateMatrixWorld = THREE.Object3D.prototype.updateMatrixWorld; + + } + + this.matrixWorldNeedsUpdate = true; + +}; + +THREE.SEA3D.Object3D.prototype.getAnimateMatrix = function() { + + return this.animateMatrix != undefined; + +}; + +// +// Dummy +// + +THREE.SEA3D.Dummy = function ( width, height, depth ) { + + this.width = width != undefined ? width : 100; + this.height = height != undefined ? height : 100; + this.depth = depth != undefined ? depth : 100; + + var geo = new THREE.BoxGeometry( this.width, this.height, this.depth, 1, 1, 1 ); + + THREE.Mesh.call( this, geo, THREE.SEA3D.Dummy.MATERIAL ); + +}; + +THREE.SEA3D.Dummy.prototype = Object.create( THREE.Mesh.prototype ); +THREE.SEA3D.Dummy.prototype.constructor = THREE.Dummy; + +THREE.SEA3D.Dummy.prototype.setAnimateMatrix = THREE.SEA3D.Object3D.prototype.setAnimateMatrix; +THREE.SEA3D.Dummy.prototype.getAnimateMatrix = THREE.SEA3D.Object3D.prototype.getAnimateMatrix; + +THREE.SEA3D.Dummy.MATERIAL = new THREE.MeshBasicMaterial( { wireframe: true, color: THREE.SEA3D.HELPER_COLOR } ); + +THREE.SEA3D.Dummy.prototype.clone = function ( object ) { + + return new THREE.SEA3D.Dummy( this.width, this.height, this.depth ).copy( this ); + +}; + +THREE.SEA3D.Dummy.prototype.dispose = function () { + + this.geometry.dispose(); + +}; + +// +// Mesh +// + +THREE.SEA3D.Mesh = function ( geometry, material ) { + + THREE.Mesh.call( this, geometry, material ); + +}; + +THREE.SEA3D.Mesh.prototype = Object.create( THREE.Mesh.prototype ); +THREE.SEA3D.Mesh.prototype.constructor = THREE.Mesh; + +THREE.SEA3D.Mesh.prototype.setAnimateMatrix = THREE.SEA3D.Object3D.prototype.setAnimateMatrix; +THREE.SEA3D.Mesh.prototype.getAnimateMatrix = THREE.SEA3D.Object3D.prototype.getAnimateMatrix; + +THREE.SEA3D.Mesh.prototype.setWeight = function( name, val ) { + + this.morphTargetInfluences[ this.morphTargetDictionary[ name ] ] = val; + +}; + +THREE.SEA3D.Mesh.prototype.getWeight = function( name ) { + + return this.morphTargetInfluences[ this.morphTargetDictionary[ name ] ]; + +}; + +THREE.SEA3D.Mesh.prototype.copy = function ( source ) { + + THREE.Mesh.prototype.copy.call( this, source ); + + if ( this.animation ) + this.animation = source.animation.clone( this ); + + return this; + +}; + +THREE.SEA3D.Mesh.prototype.clone = function ( object ) { + + return new THREE.SEA3D.Mesh( this.geometry, this.material ).copy( this ); + +}; + +// +// Skinning +// + +THREE.SEA3D.SkinnedMesh = function ( geometry, material, useVertexTexture ) { + + THREE.SkinnedMesh.call( this, geometry, material, useVertexTexture ); + +}; + +THREE.SEA3D.SkinnedMesh.prototype = Object.create( THREE.SkinnedMesh.prototype ); +THREE.SEA3D.SkinnedMesh.prototype.constructor = THREE.SEA3D.SkinnedMesh; + +THREE.SEA3D.SkinnedMesh.prototype.setAnimateMatrix = THREE.SEA3D.Object3D.prototype.setAnimateMatrix; +THREE.SEA3D.SkinnedMesh.prototype.getAnimateMatrix = THREE.SEA3D.Object3D.prototype.getAnimateMatrix; + +THREE.SEA3D.SkinnedMesh.prototype.setWeight = THREE.SEA3D.Mesh.prototype.setWeight; +THREE.SEA3D.SkinnedMesh.prototype.getWeight = THREE.SEA3D.Mesh.prototype.getWeight; + +THREE.SEA3D.SkinnedMesh.prototype.isPlaying = false; + +THREE.SEA3D.SkinnedMesh.prototype.stop = function() { + + if ( this.currentAnimation ) { + + this.currentAnimation.stop(); + + delete this.currentAnimation; + + this.isPlaying = false; + + } + +}; + +THREE.SEA3D.SkinnedMesh.prototype.pause = function() { + + if ( this.isPlaying ) { + + this.currentAnimation.pause(); + this.isPlaying = false; + + } + +}; + +THREE.SEA3D.SkinnedMesh.prototype.resume = function() { + + if ( ! this.isPlaying && this.currentAnimation ) { + + this.currentAnimation.pause(); + this.isPlaying = true; + + } + +}; + +THREE.SEA3D.SkinnedMesh.prototype.play = function( name, crossfade, offset ) { + + this.previousAnimation = this.currentAnimation; + this.currentAnimation = this.animations[ name ]; + + if ( ! this.currentAnimation ) + throw new Error( 'Animation "' + name + '" not found.' ); + + if ( this.previousAnimation && this.previousAnimation !== this.currentAnimation && crossfade > 0 ) { + + this.previousAnimation.play( this.previousAnimation.currentTime, this.previousAnimation.weight ); + this.currentAnimation.play( offset !== undefined ? offset : this.currentAnimation.currentTime, this.currentAnimation.weight ); + + THREE.SEA3D.AnimationHandler.addCrossfade( this, crossfade ); + + } else { + + this.currentAnimation.play( offset !== undefined ? offset : this.currentAnimation.currentTime, 1 ); + + } + + this.isPlaying = true; + +}; + +THREE.SEA3D.SkinnedMesh.prototype.setAnimations = function( animations ) { + + this.animations = []; + this.weightSchedule = []; + this.warpSchedule = []; + + var nsIndex = animations[ 0 ].name.indexOf( "/" ) + 1; + this.animationNamespace = animations[ 0 ].name.substring( 0, nsIndex ); + + for ( var i = 0; i < animations.length; i ++ ) { + + var ns = animations[ i ].name; + var name = ns.substring( nsIndex ); + + this.animations[ i ] = new THREE.SEA3D.Animation( this, animations[ i ] ); + this.animations[ i ].loop = animations[ i ].repeat; + this.animations[ i ].name = name; + + this.animations[ name ] = this.animations[ i ]; + + } + +}; + +THREE.SEA3D.SkinnedMesh.prototype.boneByName = function( name ) { + + var bones = this.skeleton.bones; + + for ( var i = 0, bl = bones.length; i < bl; i ++ ) { + + if ( name == bones[ i ].name ) + return bones[ i ]; + + } + +}; + +THREE.SEA3D.SkinnedMesh.prototype.copy = function ( source ) { + + THREE.SkinnedMesh.prototype.copy.call( this, source ); + + if ( this.animation ) + this.animation = source.animation.clone( this ); + + this.animations = []; + + if ( source.geometry.animations ) { + + var refAnimations = source.geometry.animations; + var nsIndex = refAnimations[ 0 ].name.indexOf( "/" ) + 1; + + for ( var i = 0; i < refAnimations.length; i ++ ) { + + var name = refAnimations[ i ].name.substring( nsIndex ); + var data = refAnimations[ i ]; + + data.initialized = false; + + this.animations[ i ] = new THREE.SEA3D.Animation( this, data ); + this.animations[ i ].loop = refAnimations[ i ].repeat; + this.animations[ i ].name = name; + + } + + } + + return this; + +}; + +THREE.SEA3D.SkinnedMesh.prototype.clone = function ( object ) { + + return new THREE.SEA3D.SkinnedMesh( this.geometry, this.material, this.useVertexTexture ).copy( this ); + +}; + +// +// Vertex Animation +// + +THREE.SEA3D.VertexAnimationMesh = function ( geometry, material, fps ) { + + THREE.MorphAnimMesh.call( this, geometry, material ); + + this.fps = fps !== undefined ? fps : 30; + this.animations = geometry.animations; + + this.isPlaying = false; + + this.totalTime = 0; + + this.playingCallback = this.updateAnimation.bind( this ); + +}; + +THREE.SEA3D.VertexAnimationMesh.prototype = Object.create( THREE.MorphAnimMesh.prototype ); +THREE.SEA3D.VertexAnimationMesh.prototype.constructor = THREE.SEA3D.VertexAnimationMesh; + +THREE.SEA3D.VertexAnimationMesh.prototype.setAnimateMatrix = THREE.SEA3D.Object3D.prototype.setAnimateMatrix; +THREE.SEA3D.VertexAnimationMesh.prototype.getAnimateMatrix = THREE.SEA3D.Object3D.prototype.getAnimateMatrix; + +THREE.SEA3D.VertexAnimationMesh.prototype.play = function( name, offset ) { + + var animation = this.animations[ name ]; + + this.setFrameRange( animation.start ? animation.start : 1, animation.end - 1 ); + + this.duration = ( animation.end - animation.start ) / this.fps; + this.time = offset !== undefined ? offset : this.time; + + this.resume(); + +}; + +THREE.SEA3D.VertexAnimationMesh.prototype.pause = function() { + + if ( this.isPlaying ) { + + this.isPlaying = false; + + THREE.SEA3D.AnimationHandler.removeUpdate( this.playingCallback ); + + } + +}; + +THREE.SEA3D.VertexAnimationMesh.prototype.resume = function() { + + if ( ! this.isPlaying ) { + + this.isPlaying = true; + + THREE.SEA3D.AnimationHandler.addUpdate( this.playingCallback ); + + } + +}; + +THREE.SEA3D.VertexAnimationMesh.prototype.stop = function() { + + this.pause(); + + this.time = 0; + +}; + +THREE.SEA3D.VertexAnimationMesh.prototype.clone = function ( object ) { + + return new THREE.SEA3D.VertexAnimationMesh( this.geometry, this.material, this.fps ).copy( this ); + +}; + +// +// Camera +// + +THREE.SEA3D.Camera = function ( fov, aspect, near, far ) { + + THREE.PerspectiveCamera.call( this, fov, aspect, near, far ); + +}; + +THREE.SEA3D.Camera.prototype = Object.create( THREE.PerspectiveCamera.prototype ); +THREE.SEA3D.Camera.prototype.constructor = THREE.SEA3D.Camera; + +THREE.SEA3D.Camera.prototype.setAnimateMatrix = THREE.SEA3D.Object3D.prototype.setAnimateMatrix; +THREE.SEA3D.Camera.prototype.getAnimateMatrix = THREE.SEA3D.Object3D.prototype.getAnimateMatrix; + +THREE.SEA3D.Camera.prototype.copy = function ( source ) { + + THREE.PerspectiveCamera.prototype.copy.call( this, source ); + + return this; + +}; + +// +// Animation Update +// + +THREE.SEA3D.AnimationHandler = { + + crossfade : [], + updates : [], + + update : function( dt ) { + + var i, cf = THREE.SEA3D.AnimationHandler.crossfade, ups = THREE.SEA3D.AnimationHandler.updates; + + // crossfade + i = 0; + while ( i < cf.length ) { + + var mesh = cf[ i ]; + + mesh.currentAnimation.weight += dt / mesh.crossfade; + + if ( mesh.currentAnimation.weight > 1 ) { + + mesh.previousAnimation.weight = 0; + mesh.currentAnimation.weight = 1; + + if ( mesh.onCrossfadeComplete ) mesh.onCrossfadeComplete( mesh ); + + cf.splice( i, 1 ); + + delete mesh.crossfade; + + } + else ++ i; + + mesh.previousAnimation.weight = 1 - mesh.currentAnimation.weight; + + } + + // updates + i = 0; + while ( i < ups.length ) { + + ups[ i ++ ]( dt ); + + } + + SEA3D.AnimationHandler.update( dt ); + + }, + + addCrossfade : function( mesh, crossfade ) { + + if ( mesh.crossfade !== undefined ) { + + THREE.SEA3D.AnimationHandler.crossfade.splice( THREE.SEA3D.AnimationHandler.crossfade.indexOf( mesh ), 1 ); + + } + + mesh.crossfade = crossfade; + + THREE.SEA3D.AnimationHandler.crossfade.push( mesh ); + + }, + + addUpdate : function( func ) { + + THREE.SEA3D.AnimationHandler.updates.push( func ); + + }, + + removeUpdate : function( func ) { + + var index = THREE.SEA3D.AnimationHandler.updates.indexOf( func ); + + if ( index !== - 1 ) { + + THREE.SEA3D.AnimationHandler.crossfade.splice( THREE.SEA3D.AnimationHandler.updates.indexOf( func ), 1 ); + + } + + } + +}; + +// +// Animation Event +// + +THREE.SEA3D.Animation = function ( root, data ) { + + THREE.Animation.call( this, root, data ); + +}; + +THREE.SEA3D.Animation.prototype = Object.create( THREE.Animation.prototype ); +THREE.SEA3D.Animation.prototype.constructor = THREE.SEA3D.Animation; + +THREE.SEA3D.Animation.prototype.stop = function() { + + if ( this.onComplete ) this.onComplete( this ); + + THREE.Animation.prototype.stop.call( this ); + +}; + +THREE.SEA3D.Animation.prototype.reset = function() { + + if ( this.onReset ) this.onReset( this ); + + THREE.Animation.prototype.reset.call( this ); + +}; + +// +// Config +// + +THREE.SEA3D.MTXBUF = new THREE.Matrix4(); +THREE.SEA3D.VECBUF = new THREE.Vector3(); + +THREE.SEA3D.prototype.setShadowMap = function( light, opacity ) { + + light.shadowMapWidth = + light.shadowMapHeight = 2048; + + light.castShadow = true; + light.shadowDarkness = opacity !== undefined ? opacity : 1; + +}; + +// +// Output +// + +THREE.SEA3D.prototype.getMesh = function( name ) { + + return this.objects[ "m3d/" + name ]; + +}; + +THREE.SEA3D.prototype.getDummy = function( name ) { + + return this.objects[ "dmy/" + name ]; + +}; + +THREE.SEA3D.prototype.getLine = function( name ) { + + return this.objects[ "line/" + name ]; + +}; + +THREE.SEA3D.prototype.getSound3D = function( name ) { + + return this.objects[ "sn3d/" + name ]; + +}; + +THREE.SEA3D.prototype.getMaterial = function( name ) { + + return this.objects[ "mat/" + name ]; + +}; + +THREE.SEA3D.prototype.getLight = function( name ) { + + return this.objects[ "lht/" + name ]; + +}; + +THREE.SEA3D.prototype.getGLSL = function( name ) { + + return this.objects[ "glsl/" + name ]; + +}; + +THREE.SEA3D.prototype.getCamera = function( name ) { + + return this.objects[ "cam/" + name ]; + +}; + +THREE.SEA3D.prototype.getTexture = function( name ) { + + return this.objects[ "tex/" + name ]; + +}; + +THREE.SEA3D.prototype.getCubeMap = function( name ) { + + return this.objects[ "cmap/" + name ]; + +}; + +THREE.SEA3D.prototype.getJointObject = function( name ) { + + return this.objects[ "jnt/" + name ]; + +}; + +THREE.SEA3D.prototype.getContainer3D = function( name ) { + + return this.objects[ "c3d/" + name ]; + +}; + +THREE.SEA3D.prototype.getSprite = function( name ) { + + return this.objects[ "m2d/" + name ]; + +}; + +THREE.SEA3D.prototype.getProperty = function( name ) { + + return this.objects[ "prop/" + name ]; + +}; + +// +// Utils +// + +THREE.SEA3D.prototype.isPowerOfTwo = function( num ) { + + return num ? ( ( num & - num ) == num ) : false; + +}; + +THREE.SEA3D.prototype.nearestPowerOfTwo = function( num ) { + + return Math.pow( 2, Math.round( Math.log( num ) / Math.LN2 ) ); + +}; + +THREE.SEA3D.prototype.updateTransform = function( obj3d, sea ) { + + var mtx = THREE.SEA3D.MTXBUF, vec = THREE.SEA3D.VECBUF; + + if ( sea.transform ) mtx.elements.set( sea.transform ); + else mtx.makeTranslation( sea.position.x, sea.position.y, sea.position.z ); + + // matrix + + obj3d.position.setFromMatrixPosition( mtx ); + obj3d.scale.setFromMatrixScale( mtx ); + + // ignore rotation scale + + mtx.scale( vec.set( 1 / obj3d.scale.x, 1 / obj3d.scale.y, 1 / obj3d.scale.z ) ); + obj3d.rotation.setFromRotationMatrix( mtx ); + + // optimize if is static + + if ( sea.isStatic ) { + + obj3d.updateMatrixWorld(); + obj3d.matrixAutoUpdate = false; + + } + +}; + +THREE.SEA3D.prototype.toVector3 = function( data ) { + + return new THREE.Vector3( data.x, data.y, data.z ); + +}; + +THREE.SEA3D.prototype.scaleColor = function( color, scale ) { + + var r = ( color >> 16 ) * scale; + var g = ( color >> 8 & 0xFF ) * scale; + var b = ( color & 0xFF ) * scale; + + return ( r << 16 | g << 8 | b ); + +}; + +THREE.SEA3D.prototype.updateScene = function() { + + if ( this.materials != undefined ) { + + for ( var i = 0, l = this.materials.length; i < l; ++ i ) { + + this.materials[ i ].needsUpdate = true; + + } + + } + +}; + +THREE.SEA3D.prototype.addSceneObject = function( sea ) { + + var obj3d = sea.tag; + + obj3d.userData = sea.properties; + + if ( this.config.script && sea.scripts ) { + + this.runJSMList( obj3d, sea.scripts ); + + } + + if ( sea.parent ) + sea.parent.tag.add( obj3d ); + else if ( this.config.container ) + this.config.container.add( obj3d ); + +}; + +THREE.SEA3D.prototype.createObjectURL = function( raw, mime ) { + + return ( window.URL || window.webkitURL ).createObjectURL( new Blob( [ raw ], { type: mime } ) ); + +}; + +THREE.SEA3D.prototype.bufferToTexture = function( raw ) { + + return this.createObjectURL( raw, "image" ); + +}; + +THREE.SEA3D.prototype.bufferToSound = function( raw ) { + + return this.createObjectURL( raw, "audio" ); + +}; + +THREE.SEA3D.prototype.applyDefaultAnimation = function( sea, animatorClass ) { + + var obj = sea.tag; + + for ( var i = 0, count = sea.animations ? sea.animations.length : 0; i < count; i ++ ) { + + var anm = sea.animations[ i ]; + + switch ( anm.tag.type ) { + case SEA3D.Animation.prototype.type: + obj.animation = new animatorClass( obj, anm.tag.tag ); + obj.animation.setRelative( anm.relative ); + + if ( this.config.autoPlay ) { + + obj.animation.play( obj.animation.getStateNameByIndex( 0 ) ); + + } + + return obj.animation; + break; + } + + } + +}; + +// +// Animation +// + +THREE.SEA3D.prototype.readAnimation = function( sea ) { + + var anmSet = new SEA3D.AnimationSet(); + + for ( var i = 0; i < sea.sequence.length; i ++ ) { + + var seq = sea.sequence[ i ], + node = new SEA3D.AnimationNode( seq.name, sea.frameRate, seq.count, seq.repeat, seq.intrpl ); + + for ( var j = 0; j < sea.dataList.length; j ++ ) { + + var anmData = sea.dataList[ j ]; + + node.addData( new SEA3D.AnimationData( anmData.kind, anmData.type, anmData.data, seq.start * anmData.blockSize ) ); + + } + + anmSet.addAnimation( node ); + + } + + this.domain.animationSets = this.animationSets = this.animationSets || []; + this.animationSets.push( this.objects[ sea.name + '.#anm' ] = sea.tag = anmSet ); + +}; + +// +// Object3D Animator +// + +THREE.SEA3D.Object3DAnimator = function( object3d, animationSet ) { + + SEA3D.AnimationHandler.call( this, animationSet ); + + this.object3d = object3d; + +}; + +THREE.SEA3D.Object3DAnimator.prototype = Object.create( SEA3D.AnimationHandler.prototype ); +THREE.SEA3D.Object3DAnimator.prototype.constructor = THREE.SEA3D.Object3DAnimator; + +THREE.SEA3D.Object3DAnimator.prototype.stop = function() { + + if ( this.relative ) { + + this.object3d.animatePosition = new THREE.Vector3(); + this.object3d.animateQuaternion = new THREE.Quaternion(); + this.object3d.animateScale = new THREE.Vector3( 1, 1, 1 ); + + } + + SEA3D.AnimationHandler.prototype.stop.call( this ); + +}; + +THREE.SEA3D.Object3DAnimator.prototype.setRelative = function( val ) { + + this.object3d.setAnimateMatrix( this.relative = val ); + +}; + +THREE.SEA3D.Object3DAnimator.prototype.updateAnimationFrame = function( frame, kind ) { + + if ( this.relative ) { + + switch ( kind ) { + case SEA3D.Animation.POSITION: + var v = frame.toVector(); + + this.object3d.animatePosition.set( v.x, v.y, v.z ); + break; + + case SEA3D.Animation.ROTATION: + var v = frame.toVector(); + + this.object3d.animateQuaternion.set( v.x, v.y, v.z, v.w ); + break; + + case SEA3D.Animation.SCALE: + var v = frame.toVector(); + + this.object3d.animateScale.set( v.x, v.y, v.z ); + break; + } + + this.object3d.matrixWorldNeedsUpdate = true; + + } else { + + switch ( kind ) { + case SEA3D.Animation.POSITION: + var v = frame.toVector(); + + this.object3d.position.set( v.x, v.y, v.z ); + break; + + case SEA3D.Animation.ROTATION: + var v = frame.toVector(); + + this.object3d.quaternion.set( v.x, v.y, v.z, v.w ); + break; + + case SEA3D.Animation.SCALE: + var v = frame.toVector(); + + this.object3d.scale.set( v.x, v.y, v.z ); + break; + } + + } + +}; + +// +// Camera Animator +// + +THREE.SEA3D.CameraAnimator = function( object3d, animationSet ) { + + THREE.SEA3D.Object3DAnimator.call( this, object3d, animationSet ); + +}; + +THREE.SEA3D.CameraAnimator.prototype = Object.create( THREE.SEA3D.Object3DAnimator.prototype ); +THREE.SEA3D.CameraAnimator.prototype.constructor = THREE.SEA3D.Object3DAnimator; + +THREE.SEA3D.CameraAnimator.prototype.updateAnimationFrame = function( frame, kind ) { + + switch ( kind ) { + case SEA3D.Animation.FOV: + this.object3d.fov = frame.getX(); + break; + + default: + THREE.SEA3D.Object3DAnimator.prototype.updateAnimationFrame.call( this, frame, kind ); + break; + } + +}; + +// +// Light Animator +// + +THREE.SEA3D.LightAnimator = function( object3d, animationSet ) { + + THREE.SEA3D.Object3DAnimator.call( this, object3d, animationSet ); + +}; + +THREE.SEA3D.LightAnimator.prototype = Object.create( THREE.SEA3D.Object3DAnimator.prototype ); +THREE.SEA3D.LightAnimator.prototype.constructor = THREE.SEA3D.Object3DAnimator; + +THREE.SEA3D.LightAnimator.prototype.updateAnimationFrame = function( frame, kind ) { + + switch ( kind ) { + case SEA3D.Animation.COLOR: + this.object3d.color.setHex( frame.getX() ); + break; + + case SEA3D.Animation.MULTIPLIER: + this.object3d.intensity = frame.getX(); + break; + + default: + THREE.SEA3D.Object3DAnimator.prototype.updateAnimationFrame.call( this, frame, kind ); + break; + } + +}; + +// +// Geometry +// + +THREE.SEA3D.prototype.readGeometryBuffer = function( sea ) { + + var geo = new THREE.BufferGeometry(); + + for ( var i = 0; i < sea.groups.length; i ++ ) { + + var g = sea.groups[ i ]; + geo.addGroup( g.start, g.count, i ); + + } + + geo.setIndex( new THREE.BufferAttribute( sea.indexes, 1 ) ); + geo.addAttribute( 'position', new THREE.BufferAttribute( sea.vertex, 3 ) ); + + if ( sea.uv ) { + + geo.addAttribute( 'uv', new THREE.BufferAttribute( sea.uv[ 0 ], 2 ) ); + if ( sea.uv.length > 1 ) geo.addAttribute( 'uv2', new THREE.BufferAttribute( sea.uv[ 1 ], 2 ) ); + + } + + if ( sea.normal ) geo.addAttribute( 'normal', new THREE.BufferAttribute( sea.normal, 3 ) ); + else geo.computeVertexNormals(); + + if ( sea.tangent4 ) geo.addAttribute( 'tangent', new THREE.BufferAttribute( sea.tangent4, 4 ) ); + + if ( sea.color ) geo.addAttribute( 'color', new THREE.BufferAttribute( sea.color[ 0 ], sea.numColor ) ); + + if ( sea.joint ) { + + geo.addAttribute( 'skinIndex', new THREE.Float32Attribute( sea.joint, 4 ) ); + geo.addAttribute( 'skinWeight', new THREE.Float32Attribute( sea.weight, 4 ) ); + + } + + if ( this.config.bounding ) { + + geo.computeBoundingBox(); + geo.computeBoundingSphere(); + + } + + geo.name = sea.name; + + this.domain.geometries = this.geometries = this.geometries || []; + this.geometries.push( this.objects[ "geo/" + sea.name ] = sea.tag = geo ); + +}; + +// +// Dummy +// + +THREE.SEA3D.prototype.readDummy = function( sea ) { + + var dummy = new THREE.SEA3D.Dummy( sea.width, sea.height, sea.depth ); + dummy.name = sea.name; + + this.domain.dummys = this.dummys = this.dummys || []; + this.dummys.push( this.objects[ "dmy/" + sea.name ] = sea.tag = dummy ); + + this.addSceneObject( sea ); + this.updateTransform( dummy, sea ); + + this.applyDefaultAnimation( sea, THREE.SEA3D.Object3DAnimator ); + +}; + +// +// Line +// + +THREE.SEA3D.prototype.readLine = function( sea ) { + + var geo = new THREE.BufferGeometry(); + + if ( sea.closed ) + sea.vertex.push( sea.vertex[ 0 ], sea.vertex[ 1 ], sea.vertex[ 2 ] ); + + geo.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( sea.vertex ), 3 ) ); + + var line = new THREE.Line( geo, new THREE.LineBasicMaterial( { color: THREE.SEA3D.HELPER_COLOR, linewidth: 3 } ) ); + line.name = sea.name; + + this.lines = this.lines || []; + this.lines.push( this.objects[ "line/" + sea.name ] = sea.tag = line ); + + this.addSceneObject( sea ); + this.updateTransform( line, sea ); + + this.applyDefaultAnimation( sea, THREE.SEA3D.Object3DAnimator ); + +}; + +// +// Container3D +// + +THREE.SEA3D.prototype.readContainer3D = function( sea ) { + + var container = new THREE.SEA3D.Object3D(); + + this.domain.containers = this.containers = this.containers || []; + this.containers.push( this.objects[ "c3d/" + sea.name ] = sea.tag = container ); + + this.addSceneObject( sea ); + this.updateTransform( container, sea ); + + this.applyDefaultAnimation( sea, THREE.SEA3D.Object3DAnimator ); + +}; + +// +// Mesh2D | Sprite +// + +THREE.SEA3D.prototype.readMesh2D = function( sea ) { + + var material; + + if ( sea.material ) { + + if ( ! sea.material.tag.sprite ) { + + material = sea.material.tag.sprite = new THREE.SpriteMaterial(); + + material.map = sea.material.tag.map; + material.map.flipY = true; + + material.color = sea.material.tag.emissive; + material.opacity = sea.material.tag.opacity; + material.blending = sea.material.tag.blending; + + } + else material = sea.material.tag.sprite; + + } + + var sprite = new THREE.Sprite( material ); + sprite.name = sea.name; + + this.domain.sprites = this.sprites = this.sprites || []; + this.sprites.push( this.objects[ "m2d/" + sea.name ] = sea.tag = sprite ); + + this.addSceneObject( sea ); + this.updateTransform( sprite, sea ); + + sprite.scale.set( sea.width, sea.height, 1 ); + +}; + +// +// Mesh +// + +THREE.SEA3D.prototype.readMesh = function( sea ) { + + var i, count, geo = sea.geometry.tag, + mesh, mat, skeleton, skeletonAnimation, vertexAnimation, morpher; + + for ( i = 0, count = sea.modifiers ? sea.modifiers.length : 0; i < count; i ++ ) { + + var mod = sea.modifiers[ i ]; + + switch ( mod.type ) { + case SEA3D.SkeletonLocal.prototype.type: + skeleton = mod; + + geo.bones = skeleton.tag; + break; + + case SEA3D.Morph.prototype.type: + morpher = mod; + + geo.morphAttributes = morpher.tag.attribs; + geo.morphTargets = morpher.tag.targets; + break; + } + + } + + for ( i = 0, count = sea.animations ? sea.animations.length : 0; i < count; i ++ ) { + + var anm = sea.animations[ i ]; + + switch ( anm.tag.type ) { + case SEA3D.SkeletonAnimation.prototype.type: + skeletonAnimation = anm.tag; + + geo.animations = this.getSkeletonAnimation( skeletonAnimation, skeleton ); + break; + + case SEA3D.VertexAnimation.prototype.type: + vertexAnimation = anm.tag; + + geo.morphAttributes = vertexAnimation.tag.attribs; + geo.morphTargets = vertexAnimation.tag.targets; + geo.animations = vertexAnimation.tag.animations; + break; + } + + } + + var uMorph = morpher != undefined || vertexAnimation != undefined, + uMorphNormal = + ( morpher && morpher.tag.attribs.normal != undefined ) || + ( vertexAnimation && vertexAnimation.tag.attribs.normal != undefined ); + + if ( sea.material ) { + + if ( sea.material.length > 1 ) { + + var mats = []; + + for ( i = 0; i < sea.material.length; i ++ ) { + + mats[ i ] = sea.material[ i ].tag; + + mats[ i ].skinning = skeleton != undefined; + mats[ i ].morphTargets = uMorph; + mats[ i ].morphNormals = uMorphNormal; + mats[ i ].vertexColors = sea.geometry.color ? THREE.VertexColors : THREE.NoColors; + + } + + mat = new THREE.MultiMaterial( mats ); + + } else { + + mat = sea.material[ 0 ].tag; + + mat.skinning = skeleton != undefined; + mat.morphTargets = uMorph; + mat.morphNormals = uMorphNormal; + mat.vertexColors = sea.geometry.color ? THREE.VertexColors : THREE.NoColors; + + } + + } + + if ( skeleton ) { + + mesh = new THREE.SEA3D.SkinnedMesh( geo, mat, false ); + + if ( skeletonAnimation ) { + + mesh.setAnimations( geo.animations ); + + if ( this.config.autoPlay ) { + + mesh.play( mesh.animations[ 0 ].name ); + + } + + } + + } else if ( vertexAnimation ) { + + mesh = new THREE.SEA3D.VertexAnimationMesh( geo, mat, vertexAnimation.frameRate ); + + if ( this.config.autoPlay ) { + + mesh.play( mesh.animations[ 0 ].name ); + + } + + } else { + + mesh = new THREE.SEA3D.Mesh( geo, mat ); + + } + + mesh.name = sea.name; + + mesh.castShadow = sea.castShadows; + mesh.receiveShadow = sea.material ? sea.material[ 0 ].receiveShadows : true; + + this.domain.meshes = this.meshes = this.meshes || []; + this.meshes.push( this.objects[ "m3d/" + sea.name ] = sea.tag = mesh ); + + this.addSceneObject( sea ); + this.updateTransform( mesh, sea ); + + this.applyDefaultAnimation( sea, THREE.SEA3D.Object3DAnimator ); + +}; + +// +// Sound Point +// + +THREE.SEA3D.prototype.readSoundPoint = function( sea ) { + + if ( ! this.audioListener ) { + + this.audioListener = new THREE.AudioListener(); + + if ( this.config.container ) { + + this.config.container.add( this.audioListener ); + + } + + } + + var sound3d = new THREE.Audio( this.audioListener ); + + sound3d.load( sea.sound.tag ); + sound3d.autoplay = sea.autoPlay; + sound3d.setLoop( sea.autoPlay ); + sound3d.setVolume( sea.volume ); + sound3d.setRefDistance( sea.distance ); + sound3d.setRolloffFactor( this.config.audioRolloffFactor ); + + sound3d.name = sea.name; + + this.domain.sounds3d = this.sounds3d = this.sounds3d || []; + this.sounds3d.push( this.objects[ "sn3d/" + sea.name ] = sea.tag = sound3d ); + + this.addSceneObject( sea ); + this.updateTransform( sound3d, sea ); + + this.applyDefaultAnimation( sea, THREE.SEA3D.Object3DAnimator ); + +}; + +// +// Cube Render +// + +THREE.SEA3D.prototype.readCubeRender = function( sea ) { + + var cube = new THREE.CubeCamera( 0.1, 5000, THREE.SEA3D.RTT_SIZE ); + cube.renderTarget.cubeCamera = cube; + + this.domain.cubeRenderers = this.cubeRenderers = this.cubeRenderers || []; + this.cubeRenderers.push( this.objects[ "rttc/" + sea.name ] = sea.tag = cube.renderTarget ); + + this.addSceneObject( sea ); + this.updateTransform( cube, sea ); + + this.applyDefaultAnimation( sea, THREE.SEA3D.Object3DAnimator ); + +}; + +// +// Images (WDP, JPEG, PNG and GIF) +// + +THREE.SEA3D.prototype.readImage = function( sea ) { + + var image = new Image(), texture = new THREE.Texture(), scope = this; + + texture.name = sea.name; + texture.wrapS = texture.wrapT = THREE.RepeatWrapping; + texture.flipY = false; + + image.onload = function () { + + if ( ! scope.isPowerOfTwo( image.width ) || + ! scope.isPowerOfTwo( image.height ) ) { + + var width = scope.nearestPowerOfTwo( image.width ), + height = scope.nearestPowerOfTwo( image.height ); + + var canvas = document.createElement( "canvas" ); + + canvas.width = width; + canvas.height = height; + + var ctx = canvas.getContext( "2d" ); + + ctx.drawImage( image, 0, 0, width, height ); + + image = canvas; + + } + + texture.image = image; + texture.needsUpdate = true; + + } + + image.src = this.bufferToTexture( sea.data.buffer ); + + this.domain.textures = this.textures = this.textures || []; + this.textures.push( this.objects[ "tex/" + sea.name ] = sea.tag = texture ); + +}; + +// +// Cube Map +// + +THREE.SEA3D.prototype.readCubeMap = function( sea ) { + + var images = [], + texture = new THREE.Texture(); + + // xyz(- / +) to xyz(+ / -) sequence + var faces = []; + + faces[ 0 ] = sea.faces[ 1 ]; + faces[ 1 ] = sea.faces[ 0 ]; + faces[ 2 ] = sea.faces[ 3 ]; + faces[ 3 ] = sea.faces[ 2 ]; + faces[ 4 ] = sea.faces[ 5 ]; + faces[ 5 ] = sea.faces[ 4 ]; + + images.loadedCount = 0; + + texture.name = sea.name; + texture.image = images; + texture.flipY = false; + + for ( var i = 0, il = faces.length; i < il; ++ i ) { + + var cubeImage = new Image(); + + images[ i ] = cubeImage; + + cubeImage.onload = function () { + + if ( ++ images.loadedCount == 6 ) { + + texture.needsUpdate = true; + + } + + } + + cubeImage.src = this.bufferToTexture( faces[ i ].buffer ); + + } + + this.domain.cubemaps = this.cubemaps = this.cubemaps || []; + this.cubemaps.push( this.objects[ "cmap/" + sea.name ] = sea.tag = texture ); + +}; + +// +// Sound (MP3, OGG) +// + +THREE.SEA3D.prototype.readSound = function( sea ) { + + var sound = this.bufferToSound( sea.data.buffer ); + + this.domain.sounds = this.sounds = this.sounds || []; + this.sounds.push( this.objects[ "snd/" + sea.name ] = sea.tag = sound ); + +}; + +// +// Texture URL +// + +THREE.SEA3D.prototype.readTextureURL = function( sea ) { + + var texture = THREE.ImageUtils.loadTexture( sea.url ); + + texture.name = sea.name; + texture.wrapS = texture.wrapT = THREE.RepeatWrapping; + texture.flipY = false; + + this.domain.textures = this.textures = this.textures || []; + this.textures.push( this.objects[ "tex/" + sea.name ] = sea.tag = texture ); + +}; + +// +// Java Script +// + +THREE.SEA3D.SCRIPT = new SEA3D.ScriptManager(); + +THREE.SEA3D.Domain = function( id, objects, container, extensions ) { + + SEA3D.Domain.call( this, id ); + + this.objects = objects; + this.container = container; + this.extensions = extensions || []; + +}; + +THREE.SEA3D.Domain.prototype = Object.create( SEA3D.Domain.prototype ); +THREE.SEA3D.Domain.prototype.constructor = THREE.SEA3D.Domain; + +THREE.SEA3D.Domain.prototype.disposeExtensions = function() { + + extensions = extensions.concat(); + + var i = list.length; + + while ( i -- ) list[ i ].dispose(); + +}; + +THREE.SEA3D.Domain.prototype.disposeList = function( list ) { + + if ( ! list || ! list.length ) return; + + list = list.concat(); + + var i = list.length; + while ( i -- ) list[ i ].dispose(); + +}; + +THREE.SEA3D.Domain.prototype.dispose = function() { + + SEA3D.Domain.prototype.dispose.call( this ); + + while ( this.container.children.length ) { + + this.container.remove( this.container.children[ 0 ] ); + + } + + var i = this.extensions.length; + while ( i -- ) this.extensions[ i ].dispose.call( this ); + + this.disposeList( this.materials ); + this.disposeList( this.dummys ); + +}; + +SEA3D.Domain.prototype.getMesh = THREE.SEA3D.prototype.getMesh; +SEA3D.Domain.prototype.getDummy = THREE.SEA3D.prototype.getDummy; +SEA3D.Domain.prototype.getLine = THREE.SEA3D.prototype.getLine; +SEA3D.Domain.prototype.getSound3D = THREE.SEA3D.prototype.getSound3D; +SEA3D.Domain.prototype.getMaterial = THREE.SEA3D.prototype.getMaterial; +SEA3D.Domain.prototype.getLight = THREE.SEA3D.prototype.getLight; +SEA3D.Domain.prototype.getGLSL = THREE.SEA3D.prototype.getGLSL; +SEA3D.Domain.prototype.getCamera = THREE.SEA3D.prototype.getCamera; +SEA3D.Domain.prototype.getTexture = THREE.SEA3D.prototype.getTexture; +SEA3D.Domain.prototype.getCubeMap = THREE.SEA3D.prototype.getCubeMap; +SEA3D.Domain.prototype.getJointObject = THREE.SEA3D.prototype.getJointObject; +SEA3D.Domain.prototype.getContainer3D = THREE.SEA3D.prototype.getContainer3D; +SEA3D.Domain.prototype.getSprite = THREE.SEA3D.prototype.getSprite; +SEA3D.Domain.prototype.getProperty = THREE.SEA3D.prototype.getProperty; + +THREE.SEA3D.DomainManager = function( autoDisposeRootDomain ) { + + SEA3D.DomainManager.call( this, autoDisposeRootDomain ); + +}; + +THREE.SEA3D.DomainManager.prototype = Object.create( SEA3D.DomainManager.prototype ); +THREE.SEA3D.DomainManager.prototype.constructor = THREE.SEA3D.DomainManager; + +THREE.SEA3D.DomainManager.prototype.add = function( domain ) { + + SEA3D.DomainManager.prototype.add.call( this, domain ); + + this.textures = this.textures || domain.textures; + this.cubemaps = this.cubemaps || domain.cubemaps; + this.geometries = this.geometries || domain.geometries; + +}; + +THREE.SEA3D.DomainManager.prototype.disposeList = THREE.SEA3D.Domain.prototype.disposeList; + +THREE.SEA3D.DomainManager.prototype.dispose = function() { + + SEA3D.DomainManager.prototype.dispose.call( this ); + + this.disposeList( this.textures ); + this.disposeList( this.cubemaps ); + this.disposeList( this.geometries ); + +}; + +// +// Runtime +// + +THREE.SEA3D.prototype.runJSMList = function( target, scripts, root ) { + + for ( var i = 0; i < scripts.length; i ++ ) { + + var script = scripts[ i ]; + + if ( script.tag.type == SEA3D.JavaScriptMethod.prototype.type ) { + + this.runJSM( target, script, root ); + + } + + } + +}; + +THREE.SEA3D.prototype.runJSM = function( target, script, root ) { + + if ( target.local == undefined ) target.local = {}; + + var include = { + print : this.domain.print, + watch : this.domain.watch, + sea3d : this.domain, + scene : this.config.container, + source : new SEA3D.Script( this.domain, root == true ) + }; + + Object.freeze( include.source ); + + THREE.SEA3D.SCRIPT.add( include.source ); + + try { + + this.script[ script.method ] ( + include, + this.domain.getReference, + this.domain.global, + target.local, + target, + script.params + ); + + } + catch ( e ) { + + console.error( 'SEA3D JavaScript: Error running method "' + script.method + '".' ); + console.error( e ); + + } + +}; + +THREE.SEA3D.prototype.readJavaScriptMethod = function( sea ) { + + try { + + var src = + '(function() {\n' + + 'var $METHOD = {}\n'; + + var declare = + 'function($INC, $REF, global, local, $his, $PARAM) {\n' + + 'var watch = $INC["watch"],\n' + + 'scene = $INC["scene"],\n' + + 'sea3d = $INC["sea3d"],\n' + + 'print = $INC["print"];\n'; + + declare += + 'var $SRC = $INC["source"],\n' + + 'addEvent = $SRC.addEvent.bind( $SRC ),\n' + + 'hasEvent = $SRC.hasEvent.bind( $SRC ),\n' + + 'dispatchEvent = $SRC.dispatchEvent.bind( $SRC ),\n' + + 'removeEvent = $SRC.removeEvent.bind( $SRC ),\n' + + 'dispose = $SRC.dispose.bind( $SRC );\n' + + for ( var name in sea.methods ) { + + src += '$METHOD["' + name + '"] = ' + declare + sea.methods[ name ].src + '}\n'; + + } + + src += 'return $METHOD; })' + + this.script = eval( src )(); + + } + catch ( e ) { + + console.error( 'SEA3D JavaScriptMethod: Error running "' + sea.name + '".' ); + console.error( e ); + + } + +}; + +// +// GLSL +// + +THREE.SEA3D.prototype.readGLSL = function( sea ) { + + this.domain.glsl = this.glsl = this.glsl || []; + this.glsl.push( this.objects[ "glsl/" + sea.name ] = sea.tag = sea.src ); + +}; + +// +// Material +// + +THREE.SEA3D.prototype.blendMode = { + normal: THREE.NormalBlending, + add: THREE.AdditiveBlending, + subtract: THREE.SubtractiveBlending, + multiply: THREE.MultiplyBlending, + screen: THREE.AdditiveBlending +}; + +THREE.SEA3D.prototype.materialTechnique = +( function() { + + var techniques = {} + + // DEFAULT + techniques[ SEA3D.Material.DEFAULT ] = + function( tech, mat ) { + + mat.emissive.setHex( tech.ambientColor ); + mat.color.setHex( tech.diffuseColor ); + mat.specular.setHex( this.scaleColor( tech.specularColor, tech.specular ) ); + mat.shininess = tech.gloss; + + } + + // DIFFUSE_MAP + techniques[ SEA3D.Material.DIFFUSE_MAP ] = + function( tech, mat ) { + + mat.map = tech.texture.tag; + mat.transparent = tech.texture.transparent; + mat.color.setHex( 0xFFFFFF ); + + } + + // SPECULAR_MAP + techniques[ SEA3D.Material.SPECULAR_MAP ] = + function( tech, mat ) { + + mat.specularMap = tech.texture.tag; + + } + + // NORMAL_MAP + techniques[ SEA3D.Material.NORMAL_MAP ] = + function( tech, mat ) { + + mat.normalMap = tech.texture.tag; + + } + + // REFLECTION + techniques[ SEA3D.Material.REFLECTION ] = + techniques[ SEA3D.Material.FRESNEL_REFLECTION ] = + function( tech, mat ) { + + mat.envMap = tech.texture.tag; + mat.envMap.mapping = THREE.CubeReflectionMapping; + mat.combine = THREE.MixOperation; + + mat.reflectivity = tech.alpha; + + //if (tech.kind == SEA3D.Material.FRESNEL_REFLECTION) { + // not implemented + //} + + } + + // REFLECTION_SPHERICAL + techniques[ SEA3D.Material.REFLECTION_SPHERICAL ] = + function( tech, mat ) { + + mat.envMap = tech.texture.tag; + mat.envMap.mapping = THREE.SphericalReflectionMapping; + mat.combine = THREE.MixOperation; + + mat.reflectivity = tech.alpha; + + } + + // REFRACTION + techniques[ SEA3D.Material.REFRACTION_MAP ] = + function( tech, mat ) { + + mat.envMap = tech.texture.tag; + mat.envMap.mapping = THREE.CubeRefractionMapping(); + + mat.refractionRatio = tech.ior; + mat.reflectivity = tech.alpha; + + } + + // LIGHT_MAP + techniques[ SEA3D.Material.LIGHT_MAP ] = + function( tech, mat ) { + + if ( tech.blendMode == "multiply" ) mat.aoMap = tech.texture.tag; + else mat.lightMap = tech.texture.tag; + + } + + return techniques; + +} )(); + +THREE.SEA3D.prototype.readMaterial = function( sea ) { + + var mat = this.config.standardMaterial ? new THREE.SEA3D.StandardMaterial() : new THREE.MeshPhongMaterial(); + mat.emissiveToAmbientColor = this.config.ambientColor; + mat.name = sea.name; + + mat.side = sea.bothSides ? THREE.DoubleSide : THREE.FrontSide; + mat.shading = sea.smooth ? THREE.SmoothShading : THREE.FlatShading; + + if ( sea.blendMode != "normal" && this.blendMode[ sea.blendMode ] ) { + + mat.blending = this.blendMode[ sea.blendMode ]; + + } + + if ( sea.alpha < 1 || mat.blending > THREE.NormalBlending ) { + + mat.opacity = sea.alpha; + mat.transparent = true; + + } + + for ( var i = 0; i < sea.technique.length; i ++ ) { + + var tech = sea.technique[ i ]; + + if ( this.materialTechnique[ tech.kind ] ) { + + this.materialTechnique[ tech.kind ].call( this, tech, mat ); + + } + + } + + if ( mat.transparent ) { + + mat.alphaTest = sea.alphaThreshold; + + } + + this.domain.materials = this.materials = this.materials || []; + this.materials.push( this.objects[ "mat/" + sea.name ] = sea.tag = mat ); + +}; + +// +// Point Light +// + +THREE.SEA3D.prototype.readPointLight = function( sea ) { + + var light = new THREE.PointLight( sea.color, sea.multiplier * this.config.multiplier ); + light.name = sea.name; + + if ( sea.attenuation ) { + + light.distance = sea.attenuation.end; + + } + + if ( sea.shadow ) { + + this.setShadowMap( light, sea.shadow.opacity ); + + } + + this.domain.lights = this.lights = this.lights || []; + this.lights.push( this.objects[ "lht/" + sea.name ] = sea.tag = light ); + + if ( this.config.lights ) this.addSceneObject( sea ); + + this.updateTransform( light, sea ); + + this.applyDefaultAnimation( sea, THREE.SEA3D.LightAnimator ); + + this.updateScene(); + +}; + +// +// Hemisphere Light +// + +THREE.SEA3D.prototype.readHemisphereLight = function( sea ) { + + var light = new THREE.HemisphereLight( sea.color, sea.secondColor, sea.multiplier * this.config.multiplier ); + light.name = sea.name; + + this.domain.lights = this.lights = this.lights || []; + this.lights.push( this.objects[ "lht/" + sea.name ] = sea.tag = light ); + + if ( this.config.lights ) this.addSceneObject( sea ); + + this.applyDefaultAnimation( sea, THREE.SEA3D.LightAnimator ); + + this.updateScene(); + +}; + +// +// Directional Light +// + +THREE.SEA3D.prototype.readDirectionalLight = function( sea ) { + + var light = new THREE.DirectionalLight( sea.color, sea.multiplier * this.config.multiplier ); + light.name = sea.name; + + if ( sea.shadow ) { + + this.setShadowMap( light, sea.shadow.opacity ); + + } + + this.domain.lights = this.lights = this.lights || []; + this.lights.push( this.objects[ "lht/" + sea.name ] = sea.tag = light ); + + if ( this.config.lights ) this.addSceneObject( sea ); + + this.updateTransform( light, sea ); + + this.applyDefaultAnimation( sea, THREE.SEA3D.LightAnimator ); + + this.updateScene(); + +}; + +// +// Camera +// + +THREE.SEA3D.prototype.readCamera = function( sea ) { + + var camera = new THREE.SEA3D.Camera( sea.fov ); + camera.name = sea.name; + + this.domain.cameras = this.cameras = this.camera || []; + this.cameras.push( this.objects[ "cam/" + sea.name ] = sea.tag = camera ); + + this.addSceneObject( sea ); + this.updateTransform( camera, sea ); + + this.applyDefaultAnimation( sea, THREE.SEA3D.CameraAnimator ); + +}; + +// +// Skeleton +// + +THREE.SEA3D.prototype.readSkeletonLocal = function( sea ) { + + var bones = []; + + for ( var i = 0; i < sea.joint.length; i ++ ) { + + var bone = sea.joint[ i ]; + + bones[ i ] = { + name: bone.name, + pos: [ bone.x, bone.y, bone.z ], + rotq: [ bone.qx, bone.qy, bone.qz, bone.qw ], + parent: bone.parentIndex + }; + + } + + sea.tag = bones; + +}; + +// +// Joint Object +// + +THREE.SEA3D.prototype.readJointObject = function( sea ) { + + var mesh = sea.target.tag, + bone = mesh.skeleton.bones[ sea.joint ]; + + this.domain.joints = this.joints = this.joints || []; + this.joints.push( this.objects[ "jnt/" + sea.name ] = sea.tag = bone ); + +}; + +// +// Skeleton Animation +// + +THREE.SEA3D.prototype.getSkeletonAnimation = function( sea, skl ) { + + if ( sea.tag ) return sea.tag; + + var animations = [], + delta = sea.frameRate / 1000, + scale = [ 1, 1, 1 ]; + + for ( var i = 0; i < sea.sequence.length; i ++ ) { + + var seq = sea.sequence[ i ]; + + var start = seq.start; + var end = start + seq.count; + var ns = sea.name + "/" + seq.name; + + var animation = { + name: ns, + repeat: seq.repeat, + fps: sea.frameRate, + JIT: 0, + length: delta * ( seq.count - 1 ), + hierarchy: [] + }; + + var numJoints = sea.numJoints, + raw = sea.raw; + + for ( var j = 0; j < numJoints; j ++ ) { + + var bone = skl.joint[ j ], + node = { parent: bone.parentIndex, keys: [] }, + keys = node.keys, + time = 0; + + for ( var frame = start; frame < end; frame ++ ) { + + var idx = ( frame * numJoints * 7 ) + ( j * 7 ); + + keys.push( { + time: time, + pos: [ raw[ idx ], raw[ idx + 1 ], raw[ idx + 2 ] ], + rot: [ raw[ idx + 3 ], raw[ idx + 4 ], raw[ idx + 5 ], raw[ idx + 6 ] ], + scl: scale + } ); + + time += delta; + + } + + animation.hierarchy[ j ] = node; + + } + + animations.push( animation ); + + } + + return sea.tag = animations; + +}; + +// +// Morpher +// + +THREE.SEA3D.prototype.readMorpher = function( sea ) { + + var attribs = { + position : [] + }, + targets = []; + + for ( var i = 0; i < sea.node.length; i ++ ) { + + var node = sea.node[ i ]; + + attribs.position[ i ] = new THREE.Float32Attribute( new Float32Array( node.vertex ), 3 ); + + if ( node.normal ) { + + attribs.normal = attribs.normal || []; + attribs.normal[ i ] = new THREE.Float32Attribute( new Float32Array( node.normal ), 3 ); + + } + + targets[ i ] = { name: node.name }; + + } + + sea.tag = { + attribs : attribs, + targets : targets + } + +}; + +// +// Vertex Animation +// + +THREE.SEA3D.prototype.readVertexAnimation = function( sea ) { + + var attribs = { + position : [] + }, + targets = [], + animations = []; + + for ( var i = 0, l = sea.frame.length; i < l; i ++ ) { + + var frame = sea.frame[ i ]; + + attribs.position[ i ] = new THREE.Float32Attribute( new Float32Array( frame.vertex ), 3 ); + + if ( frame.normal ) { + + attribs.normal = attribs.normal || []; + attribs.normal[ i ] = new THREE.Float32Attribute( new Float32Array( frame.normal ), 3 ); + + } + + targets[ i ] = { name: i }; + + } + + for ( var i = 0; i < sea.sequence.length; i ++ ) { + + var seq = sea.sequence[ i ]; + + animations[ i ] = animations[ seq.name ] = { + name : seq.name, + start : seq.start, + end : seq.start + seq.count, + repeat : seq.repeat + } + + } + + sea.tag = { + attribs : attribs, + targets : targets, + animations : animations, + frameRate : sea.frameRate + }; + +}; + +// +// Actions +// + +THREE.SEA3D.prototype.readActions = function( sea ) { + + for ( var i = 0; i < sea.actions.length; i ++ ) { + + var act = sea.actions[ i ]; + + switch ( act.kind ) { + + case SEA3D.Actions.SCRIPTS: + + this.runJSMList( this.domain, act.scripts, true ); + + break; + + } + + } + +}; + +// +// Events +// + +THREE.SEA3D.Event = { + LOAD_PROGRESS: "sea3d_progress", + DOWNLOAD_PROGRESS: "sea3d_download", + COMPLETE: "sea3d_complete", + OBJECT_COMPLETE: "sea3d_object", + PARSE_PROGRESS: "parse_progress", + PARSE_COMPLETE: "parse_complete", + ERROR: "sea3d_error" +}; + +THREE.SEA3D.prototype.onProgress = undefined; + +THREE.SEA3D.prototype.onComplete = function( args ) { + + args.file = this.scope; args.type = THREE.SEA3D.Event.COMPLETE; + args.file.dispatchEvent( args ); + +}; + +THREE.SEA3D.prototype.onLoadProgress = function( args ) { + + args.file = this.scope; args.type = THREE.SEA3D.Event.LOAD_PROGRESS; + args.file.dispatchEvent( args ); + if ( args.file.onProgress ) args.file.onProgress( args ); + +}; + +THREE.SEA3D.prototype.onDownloadProgress = function( args ) { + + args.file = this.scope; args.type = THREE.SEA3D.Event.DOWNLOAD_PROGRESS; + args.file.dispatchEvent( args ); + if ( args.file.onProgress ) args.file.onProgress( args ); + +}; + +THREE.SEA3D.prototype.onCompleteObject = function( args ) { + + args.file = this.scope; args.type = THREE.SEA3D.Event.OBJECT_COMPLETE; + args.file.dispatchEvent( args ); + +}; + +THREE.SEA3D.prototype.onParseProgress = function( args ) { + + args.file = this.scope; args.type = THREE.SEA3D.Event.PARSE_PROGRESS; + args.file.dispatchEvent( args ); + +}; + +THREE.SEA3D.prototype.onParseComplete = function( args ) { + + args.file = this.scope; args.type = THREE.SEA3D.Event.PARSE_COMPLETE; + args.file.dispatchEvent( args ); + +}; + +THREE.SEA3D.prototype.onError = function( args ) { + + args.file = this.scope; args.type = THREE.SEA3D.Event.ERROR; + args.file.dispatchEvent( args ); + +}; + +// +// Loader +// + +THREE.SEA3D.prototype.newDomain = function() { + + this.domain = new THREE.SEA3D.Domain( + this.config.id, + this.objects = {}, + this.config.container, + THREE.SEA3D.EXTENSIONS_DOMAIN + ); + +} + +THREE.SEA3D.prototype.parse = function( onParseComplete, onParseProgress ) { + + delete this.cameras; + delete this.containers; + delete this.lights; + delete this.joints; + delete this.meshes; + delete this.materials; + delete this.animationSets; + delete this.sprites; + delete this.sounds3d; + delete this.cubeRenderers; + delete this.sounds; + delete this.glsl; + delete this.dummy; + + delete this.domain; + + this.newDomain(); + + this.file.onParseComplete = ( function( e ) { + + if ( this.config.manager ) this.config.manager.add( this.domain ); + + ( onParseComplete || this.onParseComplete ).call( this.file, e ); + + } ).bind( this ); + + this.file.onParseProgress = onParseProgress || this.onParseProgress; + + // EXTENSIONS + + for ( var i = 0; i < THREE.SEA3D.EXTENSIONS_PARSE.length; i ++ ) { + + THREE.SEA3D.EXTENSIONS_PARSE[ i ].call( this ); + + } + + this.file.parse(); + + return this; + +}; + +THREE.SEA3D.prototype.load = function( url ) { + + this.loadBytes(); + this.file.load( url ); + +}; + +THREE.SEA3D.prototype.onHead = function( args ) { + + if ( args.sign != 'TJS' ) { + + throw "Sign '" + args.sign + "' not supported! Use SEA3D Studio to export."; + + } + +}; + +THREE.SEA3D.EXTENSIONS = []; +THREE.SEA3D.EXTENSIONS_PARSE = []; +THREE.SEA3D.EXTENSIONS_DOMAIN = []; + +THREE.SEA3D.prototype.loadBytes = function( data ) { + + this.file = new SEA3D.File(); + this.file.scope = this; + this.file.streaming = this.config.streaming; + this.file.timeLimit = this.config.timeLimit; + this.file.onProgress = this.onLoadProgress; + this.file.onCompleteObject = this.onCompleteObject; + this.file.onDownloadProgress = this.onDownloadProgress; + this.file.onParseProgress = this.onParseProgress; + this.file.onParseComplete = this.onParseComplete; + this.file.onError = this.onError; + this.file.onHead = this.onHead; + + this.file.onComplete = ( function( e ) { + + if ( this.config.manager ) this.config.manager.add( this.domain ); + + this.onComplete.call( this.file, e ); + + } ).bind( this ); + + // SEA3D + + this.newDomain(); + + this.file.typeRead[ SEA3D.Geometry.prototype.type ] = + this.file.typeRead[ SEA3D.GeometryDelta.prototype.type ] = this.readGeometryBuffer; + this.file.typeRead[ SEA3D.Mesh.prototype.type ] = this.readMesh; + this.file.typeRead[ SEA3D.Mesh2D.prototype.type ] = this.readMesh2D; + this.file.typeRead[ SEA3D.Container3D.prototype.type ] = this.readContainer3D; + this.file.typeRead[ SEA3D.Dummy.prototype.type ] = this.readDummy; + this.file.typeRead[ SEA3D.Line.prototype.type ] = this.readLine; + this.file.typeRead[ SEA3D.Material.prototype.type ] = this.readMaterial; + this.file.typeRead[ SEA3D.Camera.prototype.type ] = this.readCamera; + this.file.typeRead[ SEA3D.SkeletonLocal.prototype.type ] = this.readSkeletonLocal; + this.file.typeRead[ SEA3D.JointObject.prototype.type ] = this.readJointObject; + this.file.typeRead[ SEA3D.CubeMap.prototype.type ] = this.readCubeMap; + this.file.typeRead[ SEA3D.CubeRender.prototype.type ] = this.readCubeRender; + this.file.typeRead[ SEA3D.Animation.prototype.type ] = this.readAnimation; + this.file.typeRead[ SEA3D.SoundPoint.prototype.type ] = this.readSoundPoint; + this.file.typeRead[ SEA3D.TextureURL.prototype.type ] = this.readTextureURL; + this.file.typeRead[ SEA3D.Morph.prototype.type ] = this.readMorpher; + this.file.typeRead[ SEA3D.VertexAnimation.prototype.type ] = this.readVertexAnimation; + this.file.typeRead[ SEA3D.PointLight.prototype.type ] = this.readPointLight; + this.file.typeRead[ SEA3D.DirectionalLight.prototype.type ] = this.readDirectionalLight; + this.file.typeRead[ SEA3D.HemisphereLight.prototype.type ] = this.readHemisphereLight; + this.file.typeRead[ SEA3D.Actions.prototype.type ] = this.readActions; + + // UNIVERSAL + + this.file.typeRead[ SEA3D.JPEG.prototype.type ] = + this.file.typeRead[ SEA3D.JPEG_XR.prototype.type ] = + this.file.typeRead[ SEA3D.PNG.prototype.type ] = + this.file.typeRead[ SEA3D.GIF.prototype.type ] = this.readImage; + this.file.typeRead[ SEA3D.MP3.prototype.type ] = this.readSound; + this.file.typeRead[ SEA3D.GLSL.prototype.type ] = this.readGLSL; + this.file.typeRead[ SEA3D.JavaScriptMethod.prototype.type ] = this.readJavaScriptMethod; + + // EXTENSIONS + + for ( var i = 0; i < THREE.SEA3D.EXTENSIONS.length; i ++ ) { + + THREE.SEA3D.EXTENSIONS[ i ].call( this ); + + } + + this.file.read( data ); + +}; diff --git a/node_modules/three/examples/js/math/ColorConverter.js b/node_modules/three/examples/js/math/ColorConverter.js new file mode 100644 index 00000000..3e3f63c3 --- /dev/null +++ b/node_modules/three/examples/js/math/ColorConverter.js @@ -0,0 +1,64 @@ +/** + * @author bhouston / http://exocortex.com/ + * @author zz85 / http://github.com/zz85 + */ + +THREE.ColorConverter = { + + setHSV: function ( color, h, s, v ) { + + // https://gist.github.com/xpansive/1337890#file-index-js + + h = THREE.Math.euclideanModulo( h, 1 ); + s = THREE.Math.clamp( s, 0, 1 ); + v = THREE.Math.clamp( v, 0, 1 ); + + return color.setHSL( h, ( s * v ) / ( ( h = ( 2 - s ) * v ) < 1 ? h : ( 2 - h ) ), h * 0.5 ); + + }, + + getHSV: function( color ) { + + var hsl = color.getHSL(); + + // based on https://gist.github.com/xpansive/1337890#file-index-js + hsl.s *= ( hsl.l < 0.5 ) ? hsl.l : ( 1 - hsl.l ); + + return { + h: hsl.h, + s: 2 * hsl.s / ( hsl.l + hsl.s ), + v: hsl.l + hsl.s + }; + + }, + + // where c, m, y, k is between 0 and 1 + + setCMYK: function ( color, c, m, y, k ) { + + var r = ( 1 - c ) * ( 1 - k ); + var g = ( 1 - m ) * ( 1 - k ); + var b = ( 1 - y ) * ( 1 - k ); + + return color.setRGB( r, g, b ); + + }, + + getCMYK: function ( color ) { + + var r = color.r; + var g = color.g; + var b = color.b; + var k = 1 - Math.max( r, g, b ); + var c = ( 1 - r - k ) / ( 1 - k ); + var m = ( 1 - g - k ) / ( 1 - k ); + var y = ( 1 - b - k ) / ( 1 - k ); + + return { + c: c, m: m, y: y, k: k + }; + + } + + +}; diff --git a/node_modules/three/examples/js/math/Lut.js b/node_modules/three/examples/js/math/Lut.js new file mode 100644 index 00000000..f9a60ea0 --- /dev/null +++ b/node_modules/three/examples/js/math/Lut.js @@ -0,0 +1,501 @@ +/** + * @author daron1337 / http://daron1337.github.io/ + */ + +THREE.Lut = function ( colormap, numberofcolors ) { + + this.lut = []; + this.map = THREE.ColorMapKeywords[ colormap ]; + this.n = numberofcolors; + this.mapname = colormap; + + var step = 1.0 / this.n; + + for ( var i = 0; i <= 1; i += step ) { + + for ( var j = 0; j < this.map.length - 1; j ++ ) { + + if ( i >= this.map[ j ][ 0 ] && i < this.map[ j + 1 ][ 0 ] ) { + + var min = this.map[ j ][ 0 ]; + var max = this.map[ j + 1 ][ 0 ]; + + var color = new THREE.Color( 0xffffff ); + var minColor = new THREE.Color( 0xffffff ).setHex( this.map[ j ][ 1 ] ); + var maxColor = new THREE.Color( 0xffffff ).setHex( this.map[ j + 1 ][ 1 ] ); + + color = minColor.lerp( maxColor, ( i - min ) / ( max - min ) ); + + this.lut.push( color ); + + } + + } + + } + + return this.set( this ); + +}; + +THREE.Lut.prototype = { + + constructor: THREE.Lut, + + lut: [], map: [], mapname: 'rainbow', n: 256, minV: 0, maxV: 1, legend: null, + + set: function ( value ) { + + if ( value instanceof THREE.Lut ) { + + this.copy( value ); + + } + + return this; + + }, + + setMin: function ( min ) { + + this.minV = min; + + return this; + + }, + + setMax: function ( max ) { + + this.maxV = max; + + return this; + + }, + + changeNumberOfColors: function ( numberofcolors ) { + + this.n = numberofcolors; + + return new THREE.Lut( this.mapname, this.n ); + + }, + + changeColorMap: function ( colormap ) { + + this.mapname = colormap; + + return new THREE.Lut( this.mapname, this.n ); + + }, + + copy: function ( lut ) { + + this.lut = lut.lut; + this.mapname = lut.mapname; + this.map = lut.map; + this.n = lut.n; + this.minV = lut.minV; + this.maxV = lut.maxV; + + return this; + + }, + + getColor: function ( alpha ) { + + if ( alpha <= this.minV ) { + + alpha = this.minV; + + } else if ( alpha >= this.maxV ) { + + alpha = this.maxV; + + } + + alpha = ( alpha - this.minV ) / ( this.maxV - this.minV ); + + var colorPosition = Math.round ( alpha * this.n ); + colorPosition == this.n ? colorPosition -= 1 : colorPosition; + + return this.lut[ colorPosition ]; + + }, + + addColorMap: function ( colormapName, arrayOfColors ) { + + THREE.ColorMapKeywords[ colormapName ] = arrayOfColors; + + }, + + setLegendOn: function ( parameters ) { + + if ( parameters === undefined ) { + + parameters = {}; + + } + + this.legend = {}; + + this.legend.layout = parameters.hasOwnProperty( 'layout' ) ? parameters[ 'layout' ] : 'vertical'; + + this.legend.position = parameters.hasOwnProperty( 'position' ) ? parameters[ 'position' ] : { 'x': 21.5, 'y': 8, 'z': 5 }; + + this.legend.dimensions = parameters.hasOwnProperty( 'dimensions' ) ? parameters[ 'dimensions' ] : { 'width': 0.5, 'height': 3 }; + + this.legend.canvas = document.createElement( 'canvas' ); + + this.legend.canvas.setAttribute( 'id', 'legend' ); + this.legend.canvas.setAttribute( 'hidden', true ); + + document.body.appendChild( this.legend.canvas ); + + this.legend.ctx = this.legend.canvas.getContext( '2d' ); + + this.legend.canvas.setAttribute( 'width', 1 ); + this.legend.canvas.setAttribute( 'height', this.n ); + + this.legend.texture = new THREE.Texture( this.legend.canvas ); + + imageData = this.legend.ctx.getImageData( 0, 0, 1, this.n ); + + data = imageData.data; + len = data.length; + + this.map = THREE.ColorMapKeywords[ this.mapname ]; + + var k = 0; + + var step = 1.0 / this.n; + + for ( var i = 1; i >= 0; i -= step ) { + + for ( var j = this.map.length - 1; j >= 0; j -- ) { + + if ( i < this.map[ j ][ 0 ] && i >= this.map[ j - 1 ][ 0 ] ) { + + var min = this.map[ j - 1 ][ 0 ]; + var max = this.map[ j ][ 0 ]; + var color = new THREE.Color( 0xffffff ); + var minColor = new THREE.Color( 0xffffff ).setHex( this.map[ j - 1 ][ 1 ] ); + var maxColor = new THREE.Color( 0xffffff ).setHex( this.map[ j ][ 1 ] ); + color = minColor.lerp( maxColor, ( i - min ) / ( max - min ) ); + + data[ k * 4 ] = Math.round( color.r * 255 ); + data[ k * 4 + 1 ] = Math.round( color.g * 255 ); + data[ k * 4 + 2 ] = Math.round( color.b * 255 ); + data[ k * 4 + 3 ] = 255; + + k += 1; + + } + + } + + } + + this.legend.ctx.putImageData( imageData, 0, 0 ); + this.legend.texture.needsUpdate = true; + + this.legend.legendGeometry = new THREE.PlaneBufferGeometry( this.legend.dimensions.width, this.legend.dimensions.height ); + this.legend.legendMaterial = new THREE.MeshBasicMaterial( { map : this.legend.texture, side : THREE.DoubleSide } ); + + this.legend.mesh = new THREE.Mesh( this.legend.legendGeometry, this.legend.legendMaterial ); + + if ( this.legend.layout == 'horizontal' ) { + + this.legend.mesh.rotation.z = - 90 * ( Math.PI / 180 ); + + } + + this.legend.mesh.position.copy( this.legend.position ); + + return this.legend.mesh; + + }, + + setLegendOff: function () { + + this.legend = null; + + return this.legend; + + }, + + setLegendLayout: function ( layout ) { + + if ( ! this.legend ) { + + return false; + + } + + if ( this.legend.layout == layout ) { + + return false; + + } + + if ( layout != 'horizontal' && layout != 'vertical' ) { + + return false; + + } + + this.layout = layout; + + if ( layout == 'horizontal' ) { + + this.legend.mesh.rotation.z = 90 * ( Math.PI / 180 ); + + } + + if ( layout == 'vertical' ) { + + this.legend.mesh.rotation.z = - 90 * ( Math.PI / 180 ); + + } + + return this.legend.mesh; + + }, + + setLegendPosition: function ( position ) { + + this.legend.position = new THREE.Vector3( position.x, position.y, position.z ); + + return this.legend; + + }, + + setLegendLabels: function ( parameters, callback ) { + + if ( ! this.legend ) { + + return false; + + } + + if ( typeof parameters === 'function' ) { + + callback = parameters; + + } + + if ( parameters === undefined ) { + + parameters = {}; + + } + + this.legend.labels = {}; + + this.legend.labels.fontsize = parameters.hasOwnProperty( 'fontsize' ) ? parameters[ 'fontsize' ] : 24; + + this.legend.labels.fontface = parameters.hasOwnProperty( 'fontface' ) ? parameters[ 'fontface' ] : 'Arial'; + + this.legend.labels.title = parameters.hasOwnProperty( 'title' ) ? parameters[ 'title' ] : ''; + + this.legend.labels.um = parameters.hasOwnProperty( 'um' ) ? ' [ ' + parameters[ 'um' ] + ' ]' : ''; + + this.legend.labels.ticks = parameters.hasOwnProperty( 'ticks' ) ? parameters[ 'ticks' ] : 0; + + this.legend.labels.decimal = parameters.hasOwnProperty( 'decimal' ) ? parameters[ 'decimal' ] : 2; + + this.legend.labels.notation = parameters.hasOwnProperty( 'notation' ) ? parameters[ 'notation' ] : 'standard'; + + var backgroundColor = { r: 255, g: 100, b: 100, a: 0.8 }; + var borderColor = { r: 255, g: 0, b: 0, a: 1.0 }; + var borderThickness = 4; + + var canvasTitle = document.createElement( 'canvas' ); + var contextTitle = canvasTitle.getContext( '2d' ); + + contextTitle.font = 'Normal ' + this.legend.labels.fontsize * 1.2 + 'px ' + this.legend.labels.fontface; + + var metrics = contextTitle.measureText( this.legend.labels.title.toString() + this.legend.labels.um.toString() ); + var textWidth = metrics.width; + + contextTitle.fillStyle = 'rgba(' + backgroundColor.r + ',' + backgroundColor.g + ',' + backgroundColor.b + ',' + backgroundColor.a + ')'; + + contextTitle.strokeStyle = 'rgba(' + borderColor.r + ',' + borderColor.g + ',' + borderColor.b + ',' + borderColor.a + ')'; + + contextTitle.lineWidth = borderThickness; + + contextTitle.fillStyle = 'rgba( 0, 0, 0, 1.0 )'; + + contextTitle.fillText( this.legend.labels.title.toString() + this.legend.labels.um.toString(), borderThickness, this.legend.labels.fontsize + borderThickness ); + + var txtTitle = new THREE.CanvasTexture( canvasTitle ); + txtTitle.minFilter = THREE.LinearFilter; + + var spriteMaterialTitle = new THREE.SpriteMaterial( { map: txtTitle } ); + + var spriteTitle = new THREE.Sprite( spriteMaterialTitle ); + + spriteTitle.scale.set( 2, 1, 1.0 ); + + if ( this.legend.layout == 'vertical' ) { + + spriteTitle.position.set( this.legend.position.x + this.legend.dimensions.width, this.legend.position.y + ( this.legend.dimensions.height * 0.45 ), this.legend.position.z ); + + } + + if ( this.legend.layout == 'horizontal' ) { + + spriteTitle.position.set( this.legend.position.x * 1.015, this.legend.position.y + ( this.legend.dimensions.height * 0.03 ), this.legend.position.z ); + + } + + if ( this.legend.labels.ticks > 0 ) { + + var ticks = {}; + var lines = {}; + + if ( this.legend.layout == 'vertical' ) { + + var topPositionY = this.legend.position.y + ( this.legend.dimensions.height * 0.36 ); + var bottomPositionY = this.legend.position.y - ( this.legend.dimensions.height * 0.61 ); + + } + + if ( this.legend.layout == 'horizontal' ) { + + var topPositionX = this.legend.position.x + ( this.legend.dimensions.height * 0.75 ); + var bottomPositionX = this.legend.position.x - ( this.legend.dimensions.width * 1.2 ) ; + + } + + for ( var i = 0; i < this.legend.labels.ticks; i ++ ) { + + var value = ( this.maxV - this.minV ) / ( this.legend.labels.ticks - 1 ) * i ; + + if ( callback ) { + + value = callback ( value ); + + } else { + + if ( this.legend.labels.notation == 'scientific' ) { + + value = value.toExponential( this.legend.labels.decimal ); + + } else { + + value = value.toFixed( this.legend.labels.decimal ); + + } + + } + + var canvasTick = document.createElement( 'canvas' ); + var contextTick = canvasTick.getContext( '2d' ); + + contextTick.font = 'Normal ' + this.legend.labels.fontsize + 'px ' + this.legend.labels.fontface; + + var metrics = contextTick.measureText( value.toString() ); + var textWidth = metrics.width; + + contextTick.fillStyle = 'rgba(' + backgroundColor.r + ',' + backgroundColor.g + ',' + backgroundColor.b + ',' + backgroundColor.a + ')'; + + contextTick.strokeStyle = 'rgba(' + borderColor.r + ',' + borderColor.g + ',' + borderColor.b + ',' + borderColor.a + ')'; + + contextTick.lineWidth = borderThickness; + + contextTick.fillStyle = 'rgba( 0, 0, 0, 1.0 )'; + + contextTick.fillText( value.toString(), borderThickness, this.legend.labels.fontsize + borderThickness ); + + var txtTick = new THREE.CanvasTexture( canvasTick ); + txtTick.minFilter = THREE.LinearFilter; + + var spriteMaterialTick = new THREE.SpriteMaterial( { map: txtTick } ); + + var spriteTick = new THREE.Sprite( spriteMaterialTick ); + + spriteTick.scale.set( 2, 1, 1.0 ); + + if ( this.legend.layout == 'vertical' ) { + + var position = bottomPositionY + ( topPositionY - bottomPositionY ) * ( value / ( this.maxV - this.minV ) ); + + spriteTick.position.set( this.legend.position.x + ( this.legend.dimensions.width * 2.7 ), position, this.legend.position.z ); + + } + + if ( this.legend.layout == 'horizontal' ) { + + var position = bottomPositionX + ( topPositionX - bottomPositionX ) * ( value / ( this.maxV - this.minV ) ); + + if ( this.legend.labels.ticks > 5 ) { + + if ( i % 2 === 0 ) { + + var offset = 1.7; + + } else { + + var offset = 2.1; + + } + + } else { + + var offset = 1.7; + + } + + spriteTick.position.set( position, this.legend.position.y - this.legend.dimensions.width * offset, this.legend.position.z ); + + } + + var material = new THREE.LineBasicMaterial( { color: 0x000000, linewidth: 2 } ); + + var geometry = new THREE.Geometry(); + + + if ( this.legend.layout == 'vertical' ) { + + var linePosition = ( this.legend.position.y - ( this.legend.dimensions.height * 0.5 ) + 0.01 ) + ( this.legend.dimensions.height ) * ( value / ( this.maxV - this.minV ) * 0.99 ); + + geometry.vertices.push( new THREE.Vector3( this.legend.position.x + this.legend.dimensions.width * 0.55, linePosition, this.legend.position.z ) ); + + geometry.vertices.push( new THREE.Vector3( this.legend.position.x + this.legend.dimensions.width * 0.7, linePosition, this.legend.position.z ) ); + + } + + if ( this.legend.layout == 'horizontal' ) { + + var linePosition = ( this.legend.position.x - ( this.legend.dimensions.height * 0.5 ) + 0.01 ) + ( this.legend.dimensions.height ) * ( value / ( this.maxV - this.minV ) * 0.99 ); + + geometry.vertices.push( new THREE.Vector3( linePosition, this.legend.position.y - this.legend.dimensions.width * 0.55, this.legend.position.z ) ); + + geometry.vertices.push( new THREE.Vector3( linePosition, this.legend.position.y - this.legend.dimensions.width * 0.7, this.legend.position.z ) ); + + } + + var line = new THREE.Line( geometry, material ); + + lines[ i ] = line; + ticks[ i ] = spriteTick; + + } + + } + + return { 'title': spriteTitle, 'ticks': ticks, 'lines': lines }; + + } + +}; + + +THREE.ColorMapKeywords = { + + "rainbow": [ [ 0.0, '0x0000FF' ], [ 0.2, '0x00FFFF' ], [ 0.5, '0x00FF00' ], [ 0.8, '0xFFFF00' ], [ 1.0, '0xFF0000' ] ], + "cooltowarm": [ [ 0.0, '0x3C4EC2' ], [ 0.2, '0x9BBCFF' ], [ 0.5, '0xDCDCDC' ], [ 0.8, '0xF6A385' ], [ 1.0, '0xB40426' ] ], + "blackbody" : [ [ 0.0, '0x000000' ], [ 0.2, '0x780000' ], [ 0.5, '0xE63200' ], [ 0.8, '0xFFFF00' ], [ 1.0, '0xFFFFFF' ] ], + "grayscale" : [ [ 0.0, '0x000000' ], [ 0.2, '0x404040' ], [ 0.5, '0x7F7F80' ], [ 0.8, '0xBFBFBF' ], [ 1.0, '0xFFFFFF' ] ] + +}; diff --git a/node_modules/three/examples/js/modifiers/ExplodeModifier.js b/node_modules/three/examples/js/modifiers/ExplodeModifier.js new file mode 100644 index 00000000..b48d4e04 --- /dev/null +++ b/node_modules/three/examples/js/modifiers/ExplodeModifier.js @@ -0,0 +1,43 @@ +/** + * Make all faces use unique vertices + * so that each face can be separated from others + * + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ExplodeModifier = function () { + +}; + +THREE.ExplodeModifier.prototype.modify = function ( geometry ) { + + var vertices = []; + + for ( var i = 0, il = geometry.faces.length; i < il; i ++ ) { + + var n = vertices.length; + + var face = geometry.faces[ i ]; + + var a = face.a; + var b = face.b; + var c = face.c; + + var va = geometry.vertices[ a ]; + var vb = geometry.vertices[ b ]; + var vc = geometry.vertices[ c ]; + + vertices.push( va.clone() ); + vertices.push( vb.clone() ); + vertices.push( vc.clone() ); + + face.a = n; + face.b = n + 1; + face.c = n + 2; + + } + + geometry.vertices = vertices; + delete geometry.__tmpVertices; + +}; diff --git a/node_modules/three/examples/js/modifiers/SubdivisionModifier.js b/node_modules/three/examples/js/modifiers/SubdivisionModifier.js new file mode 100644 index 00000000..a86a79e4 --- /dev/null +++ b/node_modules/three/examples/js/modifiers/SubdivisionModifier.js @@ -0,0 +1,350 @@ +/* + * @author zz85 / http://twitter.com/blurspline / http://www.lab4games.net/zz85/blog + * + * Subdivision Geometry Modifier + * using Loop Subdivision Scheme + * + * References: + * http://graphics.stanford.edu/~mdfisher/subdivision.html + * http://www.holmes3d.net/graphics/subdivision/ + * http://www.cs.rutgers.edu/~decarlo/readings/subdiv-sg00c.pdf + * + * Known Issues: + * - currently doesn't handle UVs + * - currently doesn't handle "Sharp Edges" + * + */ + +THREE.SubdivisionModifier = function ( subdivisions ) { + + this.subdivisions = ( subdivisions === undefined ) ? 1 : subdivisions; + +}; + +// Applies the "modify" pattern +THREE.SubdivisionModifier.prototype.modify = function ( geometry ) { + + var repeats = this.subdivisions; + + while ( repeats -- > 0 ) { + + this.smooth( geometry ); + + } + + delete geometry.__tmpVertices; + + geometry.computeFaceNormals(); + geometry.computeVertexNormals(); + +}; + +( function() { + + // Some constants + var WARNINGS = ! true; // Set to true for development + var ABC = [ 'a', 'b', 'c' ]; + + + function getEdge( a, b, map ) { + + var vertexIndexA = Math.min( a, b ); + var vertexIndexB = Math.max( a, b ); + + var key = vertexIndexA + "_" + vertexIndexB; + + return map[ key ]; + + } + + + function processEdge( a, b, vertices, map, face, metaVertices ) { + + var vertexIndexA = Math.min( a, b ); + var vertexIndexB = Math.max( a, b ); + + var key = vertexIndexA + "_" + vertexIndexB; + + var edge; + + if ( key in map ) { + + edge = map[ key ]; + + } else { + + var vertexA = vertices[ vertexIndexA ]; + var vertexB = vertices[ vertexIndexB ]; + + edge = { + + a: vertexA, // pointer reference + b: vertexB, + newEdge: null, + // aIndex: a, // numbered reference + // bIndex: b, + faces: [] // pointers to face + + }; + + map[ key ] = edge; + + } + + edge.faces.push( face ); + + metaVertices[ a ].edges.push( edge ); + metaVertices[ b ].edges.push( edge ); + + + } + + function generateLookups( vertices, faces, metaVertices, edges ) { + + var i, il, face, edge; + + for ( i = 0, il = vertices.length; i < il; i ++ ) { + + metaVertices[ i ] = { edges: [] }; + + } + + for ( i = 0, il = faces.length; i < il; i ++ ) { + + face = faces[ i ]; + + processEdge( face.a, face.b, vertices, edges, face, metaVertices ); + processEdge( face.b, face.c, vertices, edges, face, metaVertices ); + processEdge( face.c, face.a, vertices, edges, face, metaVertices ); + + } + + } + + function newFace( newFaces, a, b, c ) { + + newFaces.push( new THREE.Face3( a, b, c ) ); + + } + + + ///////////////////////////// + + // Performs one iteration of Subdivision + THREE.SubdivisionModifier.prototype.smooth = function ( geometry ) { + + var tmp = new THREE.Vector3(); + + var oldVertices, oldFaces; + var newVertices, newFaces; // newUVs = []; + + var n, l, i, il, j, k; + var metaVertices, sourceEdges; + + // new stuff. + var sourceEdges, newEdgeVertices, newSourceVertices; + + oldVertices = geometry.vertices; // { x, y, z} + oldFaces = geometry.faces; // { a: oldVertex1, b: oldVertex2, c: oldVertex3 } + + /****************************************************** + * + * Step 0: Preprocess Geometry to Generate edges Lookup + * + *******************************************************/ + + metaVertices = new Array( oldVertices.length ); + sourceEdges = {}; // Edge => { oldVertex1, oldVertex2, faces[] } + + generateLookups( oldVertices, oldFaces, metaVertices, sourceEdges ); + + + /****************************************************** + * + * Step 1. + * For each edge, create a new Edge Vertex, + * then position it. + * + *******************************************************/ + + newEdgeVertices = []; + var other, currentEdge, newEdge, face; + var edgeVertexWeight, adjacentVertexWeight, connectedFaces; + + for ( i in sourceEdges ) { + + currentEdge = sourceEdges[ i ]; + newEdge = new THREE.Vector3(); + + edgeVertexWeight = 3 / 8; + adjacentVertexWeight = 1 / 8; + + connectedFaces = currentEdge.faces.length; + + // check how many linked faces. 2 should be correct. + if ( connectedFaces != 2 ) { + + // if length is not 2, handle condition + edgeVertexWeight = 0.5; + adjacentVertexWeight = 0; + + if ( connectedFaces != 1 ) { + + if ( WARNINGS ) console.warn( 'Subdivision Modifier: Number of connected faces != 2, is: ', connectedFaces, currentEdge ); + + } + + } + + newEdge.addVectors( currentEdge.a, currentEdge.b ).multiplyScalar( edgeVertexWeight ); + + tmp.set( 0, 0, 0 ); + + for ( j = 0; j < connectedFaces; j ++ ) { + + face = currentEdge.faces[ j ]; + + for ( k = 0; k < 3; k ++ ) { + + other = oldVertices[ face[ ABC[ k ] ] ]; + if ( other !== currentEdge.a && other !== currentEdge.b ) break; + + } + + tmp.add( other ); + + } + + tmp.multiplyScalar( adjacentVertexWeight ); + newEdge.add( tmp ); + + currentEdge.newEdge = newEdgeVertices.length; + newEdgeVertices.push( newEdge ); + + // console.log(currentEdge, newEdge); + + } + + /****************************************************** + * + * Step 2. + * Reposition each source vertices. + * + *******************************************************/ + + var beta, sourceVertexWeight, connectingVertexWeight; + var connectingEdge, connectingEdges, oldVertex, newSourceVertex; + newSourceVertices = []; + + for ( i = 0, il = oldVertices.length; i < il; i ++ ) { + + oldVertex = oldVertices[ i ]; + + // find all connecting edges (using lookupTable) + connectingEdges = metaVertices[ i ].edges; + n = connectingEdges.length; + beta; + + if ( n == 3 ) { + + beta = 3 / 16; + + } else if ( n > 3 ) { + + beta = 3 / ( 8 * n ); // Warren's modified formula + + } + + // Loop's original beta formula + // beta = 1 / n * ( 5/8 - Math.pow( 3/8 + 1/4 * Math.cos( 2 * Math. PI / n ), 2) ); + + sourceVertexWeight = 1 - n * beta; + connectingVertexWeight = beta; + + if ( n <= 2 ) { + + // crease and boundary rules + // console.warn('crease and boundary rules'); + + if ( n == 2 ) { + + if ( WARNINGS ) console.warn( '2 connecting edges', connectingEdges ); + sourceVertexWeight = 3 / 4; + connectingVertexWeight = 1 / 8; + + // sourceVertexWeight = 1; + // connectingVertexWeight = 0; + + } else if ( n == 1 ) { + + if ( WARNINGS ) console.warn( 'only 1 connecting edge' ); + + } else if ( n == 0 ) { + + if ( WARNINGS ) console.warn( '0 connecting edges' ); + + } + + } + + newSourceVertex = oldVertex.clone().multiplyScalar( sourceVertexWeight ); + + tmp.set( 0, 0, 0 ); + + for ( j = 0; j < n; j ++ ) { + + connectingEdge = connectingEdges[ j ]; + other = connectingEdge.a !== oldVertex ? connectingEdge.a : connectingEdge.b; + tmp.add( other ); + + } + + tmp.multiplyScalar( connectingVertexWeight ); + newSourceVertex.add( tmp ); + + newSourceVertices.push( newSourceVertex ); + + } + + + /****************************************************** + * + * Step 3. + * Generate Faces between source vertecies + * and edge vertices. + * + *******************************************************/ + + newVertices = newSourceVertices.concat( newEdgeVertices ); + var sl = newSourceVertices.length, edge1, edge2, edge3; + newFaces = []; + + for ( i = 0, il = oldFaces.length; i < il; i ++ ) { + + face = oldFaces[ i ]; + + // find the 3 new edges vertex of each old face + + edge1 = getEdge( face.a, face.b, sourceEdges ).newEdge + sl; + edge2 = getEdge( face.b, face.c, sourceEdges ).newEdge + sl; + edge3 = getEdge( face.c, face.a, sourceEdges ).newEdge + sl; + + // create 4 faces. + + newFace( newFaces, edge1, edge2, edge3 ); + newFace( newFaces, face.a, edge1, edge3 ); + newFace( newFaces, face.b, edge2, edge1 ); + newFace( newFaces, face.c, edge3, edge2 ); + + } + + // Overwrite old arrays + geometry.vertices = newVertices; + geometry.faces = newFaces; + + // console.log('done'); + + }; + + +} )(); diff --git a/node_modules/three/examples/js/modifiers/TessellateModifier.js b/node_modules/three/examples/js/modifiers/TessellateModifier.js new file mode 100644 index 00000000..3737c829 --- /dev/null +++ b/node_modules/three/examples/js/modifiers/TessellateModifier.js @@ -0,0 +1,236 @@ +/** + * Break faces with edges longer than maxEdgeLength + * - not recursive + * + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.TessellateModifier = function ( maxEdgeLength ) { + + this.maxEdgeLength = maxEdgeLength; + +}; + +THREE.TessellateModifier.prototype.modify = function ( geometry ) { + + var edge; + + var faces = []; + var faceVertexUvs = []; + var maxEdgeLengthSquared = this.maxEdgeLength * this.maxEdgeLength; + + for ( var i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) { + + faceVertexUvs[ i ] = []; + + } + + for ( var i = 0, il = geometry.faces.length; i < il; i ++ ) { + + var face = geometry.faces[ i ]; + + if ( face instanceof THREE.Face3 ) { + + var a = face.a; + var b = face.b; + var c = face.c; + + var va = geometry.vertices[ a ]; + var vb = geometry.vertices[ b ]; + var vc = geometry.vertices[ c ]; + + var dab = va.distanceToSquared( vb ); + var dbc = vb.distanceToSquared( vc ); + var dac = va.distanceToSquared( vc ); + + if ( dab > maxEdgeLengthSquared || dbc > maxEdgeLengthSquared || dac > maxEdgeLengthSquared ) { + + var m = geometry.vertices.length; + + var triA = face.clone(); + var triB = face.clone(); + + if ( dab >= dbc && dab >= dac ) { + + var vm = va.clone(); + vm.lerp( vb, 0.5 ); + + triA.a = a; + triA.b = m; + triA.c = c; + + triB.a = m; + triB.b = b; + triB.c = c; + + if ( face.vertexNormals.length === 3 ) { + + var vnm = face.vertexNormals[ 0 ].clone(); + vnm.lerp( face.vertexNormals[ 1 ], 0.5 ); + + triA.vertexNormals[ 1 ].copy( vnm ); + triB.vertexNormals[ 0 ].copy( vnm ); + + } + + if ( face.vertexColors.length === 3 ) { + + var vcm = face.vertexColors[ 0 ].clone(); + vcm.lerp( face.vertexColors[ 1 ], 0.5 ); + + triA.vertexColors[ 1 ].copy( vcm ); + triB.vertexColors[ 0 ].copy( vcm ); + + } + + edge = 0; + + } else if ( dbc >= dab && dbc >= dac ) { + + var vm = vb.clone(); + vm.lerp( vc, 0.5 ); + + triA.a = a; + triA.b = b; + triA.c = m; + + triB.a = m; + triB.b = c; + triB.c = a; + + if ( face.vertexNormals.length === 3 ) { + + var vnm = face.vertexNormals[ 1 ].clone(); + vnm.lerp( face.vertexNormals[ 2 ], 0.5 ); + + triA.vertexNormals[ 2 ].copy( vnm ); + + triB.vertexNormals[ 0 ].copy( vnm ); + triB.vertexNormals[ 1 ].copy( face.vertexNormals[ 2 ] ); + triB.vertexNormals[ 2 ].copy( face.vertexNormals[ 0 ] ); + + } + + if ( face.vertexColors.length === 3 ) { + + var vcm = face.vertexColors[ 1 ].clone(); + vcm.lerp( face.vertexColors[ 2 ], 0.5 ); + + triA.vertexColors[ 2 ].copy( vcm ); + + triB.vertexColors[ 0 ].copy( vcm ); + triB.vertexColors[ 1 ].copy( face.vertexColors[ 2 ] ); + triB.vertexColors[ 2 ].copy( face.vertexColors[ 0 ] ); + + } + + edge = 1; + + } else { + + var vm = va.clone(); + vm.lerp( vc, 0.5 ); + + triA.a = a; + triA.b = b; + triA.c = m; + + triB.a = m; + triB.b = b; + triB.c = c; + + if ( face.vertexNormals.length === 3 ) { + + var vnm = face.vertexNormals[ 0 ].clone(); + vnm.lerp( face.vertexNormals[ 2 ], 0.5 ); + + triA.vertexNormals[ 2 ].copy( vnm ); + triB.vertexNormals[ 0 ].copy( vnm ); + + } + + if ( face.vertexColors.length === 3 ) { + + var vcm = face.vertexColors[ 0 ].clone(); + vcm.lerp( face.vertexColors[ 2 ], 0.5 ); + + triA.vertexColors[ 2 ].copy( vcm ); + triB.vertexColors[ 0 ].copy( vcm ); + + } + + edge = 2; + + } + + faces.push( triA, triB ); + geometry.vertices.push( vm ); + + for ( var j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { + + if ( geometry.faceVertexUvs[ j ].length ) { + + var uvs = geometry.faceVertexUvs[ j ][ i ]; + + var uvA = uvs[ 0 ]; + var uvB = uvs[ 1 ]; + var uvC = uvs[ 2 ]; + + // AB + + if ( edge === 0 ) { + + var uvM = uvA.clone(); + uvM.lerp( uvB, 0.5 ); + + var uvsTriA = [ uvA.clone(), uvM.clone(), uvC.clone() ]; + var uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ]; + + // BC + + } else if ( edge === 1 ) { + + var uvM = uvB.clone(); + uvM.lerp( uvC, 0.5 ); + + var uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ]; + var uvsTriB = [ uvM.clone(), uvC.clone(), uvA.clone() ]; + + // AC + + } else { + + var uvM = uvA.clone(); + uvM.lerp( uvC, 0.5 ); + + var uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ]; + var uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ]; + + } + + faceVertexUvs[ j ].push( uvsTriA, uvsTriB ); + + } + + } + + } else { + + faces.push( face ); + + for ( var j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { + + faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] ); + + } + + } + + } + + } + + geometry.faces = faces; + geometry.faceVertexUvs = faceVertexUvs; + +}; diff --git a/node_modules/three/examples/js/objects/ShadowMesh.js b/node_modules/three/examples/js/objects/ShadowMesh.js new file mode 100644 index 00000000..3c85e94e --- /dev/null +++ b/node_modules/three/examples/js/objects/ShadowMesh.js @@ -0,0 +1,69 @@ +/** + * @author erichlof / http://github.com/erichlof + * + * A shadow Mesh that follows a shadow-casting Mesh in the scene, but is confined to a single plane. + */ + +THREE.ShadowMesh = function ( mesh ) { + + var shadowMaterial = new THREE.MeshBasicMaterial( { + + color: 0x000000, + transparent: true, + opacity: 0.6, + depthWrite: false + + } ); + + THREE.Mesh.call( this, mesh.geometry, shadowMaterial ); + + this.meshMatrix = mesh.matrixWorld; + + this.frustumCulled = false; + this.matrixAutoUpdate = false; + +}; + +THREE.ShadowMesh.prototype = Object.create( THREE.Mesh.prototype ); +THREE.ShadowMesh.prototype.constructor = THREE.ShadowMesh; + +THREE.ShadowMesh.prototype.update = function () { + + var shadowMatrix = new THREE.Matrix4(); + + return function ( plane, lightPosition4D ) { + + // based on https://www.opengl.org/archives/resources/features/StencilTalk/tsld021.htm + + var dot = plane.normal.x * lightPosition4D.x + + plane.normal.y * lightPosition4D.y + + plane.normal.z * lightPosition4D.z + + - plane.constant * lightPosition4D.w; + + var sme = shadowMatrix.elements; + + sme[ 0 ] = dot - lightPosition4D.x * plane.normal.x; + sme[ 4 ] = - lightPosition4D.x * plane.normal.y; + sme[ 8 ] = - lightPosition4D.x * plane.normal.z; + sme[ 12 ] = - lightPosition4D.x * - plane.constant; + + sme[ 1 ] = - lightPosition4D.y * plane.normal.x; + sme[ 5 ] = dot - lightPosition4D.y * plane.normal.y; + sme[ 9 ] = - lightPosition4D.y * plane.normal.z; + sme[ 13 ] = - lightPosition4D.y * - plane.constant; + + sme[ 2 ] = - lightPosition4D.z * plane.normal.x; + sme[ 6 ] = - lightPosition4D.z * plane.normal.y; + sme[ 10 ] = dot - lightPosition4D.z * plane.normal.z; + sme[ 14 ] = - lightPosition4D.z * - plane.constant; + + sme[ 3 ] = - lightPosition4D.w * plane.normal.x; + sme[ 7 ] = - lightPosition4D.w * plane.normal.y; + sme[ 11 ] = - lightPosition4D.w * plane.normal.z; + sme[ 15 ] = dot - lightPosition4D.w * - plane.constant; + + this.matrix.multiplyMatrices( shadowMatrix, this.meshMatrix ); + + }; + +}(); diff --git a/node_modules/three/examples/js/postprocessing/AdaptiveToneMappingPass.js b/node_modules/three/examples/js/postprocessing/AdaptiveToneMappingPass.js new file mode 100644 index 00000000..eae2b431 --- /dev/null +++ b/node_modules/three/examples/js/postprocessing/AdaptiveToneMappingPass.js @@ -0,0 +1,315 @@ +/** + * @author miibond + * Generate a texture that represents the luminosity of the current scene, adapted over time + * to simulate the optic nerve responding to the amount of light it is receiving. + * Based on a GDC2007 presentation by Wolfgang Engel titled "Post-Processing Pipeline" + * + * Full-screen tone-mapping shader based on http://www.graphics.cornell.edu/~jaf/publications/sig02_paper.pdf + */ + +THREE.AdaptiveToneMappingPass = function ( adaptive, resolution ) { + + this.resolution = ( resolution !== undefined ) ? resolution : 256; + this.needsInit = true; + this.adaptive = adaptive !== undefined ? !! adaptive : true; + + this.luminanceRT = null; + this.previousLuminanceRT = null; + this.currentLuminanceRT = null; + + if ( THREE.CopyShader === undefined ) + console.error( "THREE.AdaptiveToneMappingPass relies on THREE.CopyShader" ); + + var copyShader = THREE.CopyShader; + + this.copyUniforms = THREE.UniformsUtils.clone( copyShader.uniforms ); + + this.materialCopy = new THREE.ShaderMaterial( { + + uniforms: this.copyUniforms, + vertexShader: copyShader.vertexShader, + fragmentShader: copyShader.fragmentShader, + blending: THREE.NoBlending, + depthTest: false + + } ); + + if ( THREE.LuminosityShader === undefined ) + console.error( "THREE.AdaptiveToneMappingPass relies on THREE.LuminosityShader" ); + + this.materialLuminance = new THREE.ShaderMaterial( { + + uniforms: THREE.UniformsUtils.clone( THREE.LuminosityShader.uniforms ), + vertexShader: THREE.LuminosityShader.vertexShader, + fragmentShader: THREE.LuminosityShader.fragmentShader, + blending: THREE.NoBlending, + } ); + + this.adaptLuminanceShader = { + defines: { + "MIP_LEVEL_1X1" : ( Math.log( this.resolution ) / Math.log( 2.0 ) ).toFixed( 1 ), + }, + uniforms: { + "lastLum": { type: "t", value: null }, + "currentLum": { type: "t", value: null }, + "delta": { type: 'f', value: 0.016 }, + "tau": { type: 'f', value: 1.0 } + }, + vertexShader: [ + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + ].join( '\n' ), + fragmentShader: [ + "varying vec2 vUv;", + + "uniform sampler2D lastLum;", + "uniform sampler2D currentLum;", + "uniform float delta;", + "uniform float tau;", + + "void main() {", + + "vec4 lastLum = texture2D( lastLum, vUv, MIP_LEVEL_1X1 );", + "vec4 currentLum = texture2D( currentLum, vUv, MIP_LEVEL_1X1 );", + + "float fLastLum = lastLum.r;", + "float fCurrentLum = currentLum.r;", + + //The adaption seems to work better in extreme lighting differences + //if the input luminance is squared. + "fCurrentLum *= fCurrentLum;", + + // Adapt the luminance using Pattanaik's technique + "float fAdaptedLum = fLastLum + (fCurrentLum - fLastLum) * (1.0 - exp(-delta * tau));", + // "fAdaptedLum = sqrt(fAdaptedLum);", + "gl_FragColor = vec4( vec3( fAdaptedLum ), 1.0 );", + "}", + ].join( '\n' ) + }; + + this.materialAdaptiveLum = new THREE.ShaderMaterial( { + + uniforms: THREE.UniformsUtils.clone( this.adaptLuminanceShader.uniforms ), + vertexShader: this.adaptLuminanceShader.vertexShader, + fragmentShader: this.adaptLuminanceShader.fragmentShader, + defines: this.adaptLuminanceShader.defines, + blending: THREE.NoBlending + } ); + + if ( THREE.ToneMapShader === undefined ) + console.error( "THREE.AdaptiveToneMappingPass relies on THREE.ToneMapShader" ); + + this.materialToneMap = new THREE.ShaderMaterial( { + + uniforms: THREE.UniformsUtils.clone( THREE.ToneMapShader.uniforms ), + vertexShader: THREE.ToneMapShader.vertexShader, + fragmentShader: THREE.ToneMapShader.fragmentShader, + blending: THREE.NoBlending + } ); + + this.enabled = true; + this.needsSwap = true; + this.clear = false; + + this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); + this.scene = new THREE.Scene(); + + this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null ); + this.scene.add( this.quad ); + +}; + +THREE.AdaptiveToneMappingPass.prototype = { + + render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) { + + if ( this.needsInit ) { + + this.reset( renderer ); + this.luminanceRT.type = readBuffer.type; + this.previousLuminanceRT.type = readBuffer.type; + this.currentLuminanceRT.type = readBuffer.type; + this.needsInit = false; + + } + + if ( this.adaptive ) { + + //Render the luminance of the current scene into a render target with mipmapping enabled + this.quad.material = this.materialLuminance; + this.materialLuminance.uniforms.tDiffuse.value = readBuffer; + renderer.render( this.scene, this.camera, this.currentLuminanceRT ); + + //Use the new luminance values, the previous luminance and the frame delta to + //adapt the luminance over time. + this.quad.material = this.materialAdaptiveLum; + this.materialAdaptiveLum.uniforms.delta.value = delta; + this.materialAdaptiveLum.uniforms.lastLum.value = this.previousLuminanceRT; + this.materialAdaptiveLum.uniforms.currentLum.value = this.currentLuminanceRT; + renderer.render( this.scene, this.camera, this.luminanceRT ); + + //Copy the new adapted luminance value so that it can be used by the next frame. + this.quad.material = this.materialCopy; + this.copyUniforms.tDiffuse.value = this.luminanceRT; + renderer.render( this.scene, this.camera, this.previousLuminanceRT ); + + } + + this.quad.material = this.materialToneMap; + this.materialToneMap.uniforms.tDiffuse.value = readBuffer; + renderer.render( this.scene, this.camera, writeBuffer, this.clear ); + + }, + + reset: function( renderer ) { + + // render targets + if ( this.luminanceRT ) { + + this.luminanceRT.dispose(); + + } + if ( this.currentLuminanceRT ) { + + this.currentLuminanceRT.dispose(); + + } + if ( this.previousLuminanceRT ) { + + this.previousLuminanceRT.dispose(); + + } + var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat }; + + this.luminanceRT = new THREE.WebGLRenderTarget( this.resolution, this.resolution, pars ); + this.luminanceRT.generateMipmaps = false; + this.previousLuminanceRT = new THREE.WebGLRenderTarget( this.resolution, this.resolution, pars ); + this.previousLuminanceRT.generateMipmaps = false; + + //We only need mipmapping for the current luminosity because we want a down-sampled version to sample in our adaptive shader + pars.minFilter = THREE.LinearMipMapLinearFilter; + this.currentLuminanceRT = new THREE.WebGLRenderTarget( this.resolution, this.resolution, pars ); + + if ( this.adaptive ) { + + this.materialToneMap.defines[ "ADAPTED_LUMINANCE" ] = ""; + this.materialToneMap.uniforms.luminanceMap.value = this.luminanceRT; + + } + //Put something in the adaptive luminance texture so that the scene can render initially + this.quad.material = new THREE.MeshBasicMaterial( { color: 0x777777 } ); + this.materialLuminance.needsUpdate = true; + this.materialAdaptiveLum.needsUpdate = true; + this.materialToneMap.needsUpdate = true; + // renderer.render( this.scene, this.camera, this.luminanceRT ); + // renderer.render( this.scene, this.camera, this.previousLuminanceRT ); + // renderer.render( this.scene, this.camera, this.currentLuminanceRT ); + + }, + + setAdaptive: function( adaptive ) { + + if ( adaptive ) { + + this.adaptive = true; + this.materialToneMap.defines[ "ADAPTED_LUMINANCE" ] = ""; + this.materialToneMap.uniforms.luminanceMap.value = this.luminanceRT; + + } else { + + this.adaptive = false; + delete this.materialToneMap.defines[ "ADAPTED_LUMINANCE" ]; + this.materialToneMap.uniforms.luminanceMap.value = undefined; + + } + this.materialToneMap.needsUpdate = true; + + }, + + setAdaptionRate: function( rate ) { + + if ( rate ) { + + this.materialAdaptiveLum.uniforms.tau.value = Math.abs( rate ); + + } + + }, + + setMaxLuminance: function( maxLum ) { + + if ( maxLum ) { + + this.materialToneMap.uniforms.maxLuminance.value = maxLum; + + } + + }, + + setAverageLuminance: function( avgLum ) { + + if ( avgLum ) { + + this.materialToneMap.uniforms.averageLuminance.value = avgLum; + + } + + }, + + setMiddleGrey: function( middleGrey ) { + + if ( middleGrey ) { + + this.materialToneMap.uniforms.middleGrey.value = middleGrey; + + } + + }, + + dispose: function() { + + if ( this.luminanceRT ) { + + this.luminanceRT.dispose(); + + } + if ( this.previousLuminanceRT ) { + + this.previousLuminanceRT.dispose(); + + } + if ( this.currentLuminanceRT ) { + + this.currentLuminanceRT.dispose(); + + } + if ( this.materialLuminance ) { + + this.materialLuminance.dispose(); + + } + if ( this.materialAdaptiveLum ) { + + this.materialAdaptiveLum.dispose(); + + } + if ( this.materialCopy ) { + + this.materialCopy.dispose(); + + } + if ( this.materialToneMap ) { + + this.materialToneMap.dispose(); + + } + + } + +}; diff --git a/node_modules/three/examples/js/postprocessing/BloomPass.js b/node_modules/three/examples/js/postprocessing/BloomPass.js new file mode 100644 index 00000000..6557c193 --- /dev/null +++ b/node_modules/three/examples/js/postprocessing/BloomPass.js @@ -0,0 +1,115 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.BloomPass = function ( strength, kernelSize, sigma, resolution ) { + + strength = ( strength !== undefined ) ? strength : 1; + kernelSize = ( kernelSize !== undefined ) ? kernelSize : 25; + sigma = ( sigma !== undefined ) ? sigma : 4.0; + resolution = ( resolution !== undefined ) ? resolution : 256; + + // render targets + + var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat }; + + this.renderTargetX = new THREE.WebGLRenderTarget( resolution, resolution, pars ); + this.renderTargetY = new THREE.WebGLRenderTarget( resolution, resolution, pars ); + + // copy material + + if ( THREE.CopyShader === undefined ) + console.error( "THREE.BloomPass relies on THREE.CopyShader" ); + + var copyShader = THREE.CopyShader; + + this.copyUniforms = THREE.UniformsUtils.clone( copyShader.uniforms ); + + this.copyUniforms[ "opacity" ].value = strength; + + this.materialCopy = new THREE.ShaderMaterial( { + + uniforms: this.copyUniforms, + vertexShader: copyShader.vertexShader, + fragmentShader: copyShader.fragmentShader, + blending: THREE.AdditiveBlending, + transparent: true + + } ); + + // convolution material + + if ( THREE.ConvolutionShader === undefined ) + console.error( "THREE.BloomPass relies on THREE.ConvolutionShader" ); + + var convolutionShader = THREE.ConvolutionShader; + + this.convolutionUniforms = THREE.UniformsUtils.clone( convolutionShader.uniforms ); + + this.convolutionUniforms[ "uImageIncrement" ].value = THREE.BloomPass.blurX; + this.convolutionUniforms[ "cKernel" ].value = THREE.ConvolutionShader.buildKernel( sigma ); + + this.materialConvolution = new THREE.ShaderMaterial( { + + uniforms: this.convolutionUniforms, + vertexShader: convolutionShader.vertexShader, + fragmentShader: convolutionShader.fragmentShader, + defines: { + "KERNEL_SIZE_FLOAT": kernelSize.toFixed( 1 ), + "KERNEL_SIZE_INT": kernelSize.toFixed( 0 ) + } + + } ); + + this.enabled = true; + this.needsSwap = false; + this.clear = false; + + + this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); + this.scene = new THREE.Scene(); + + this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null ); + this.scene.add( this.quad ); + +}; + +THREE.BloomPass.prototype = { + + render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) { + + if ( maskActive ) renderer.context.disable( renderer.context.STENCIL_TEST ); + + // Render quad with blured scene into texture (convolution pass 1) + + this.quad.material = this.materialConvolution; + + this.convolutionUniforms[ "tDiffuse" ].value = readBuffer; + this.convolutionUniforms[ "uImageIncrement" ].value = THREE.BloomPass.blurX; + + renderer.render( this.scene, this.camera, this.renderTargetX, true ); + + + // Render quad with blured scene into texture (convolution pass 2) + + this.convolutionUniforms[ "tDiffuse" ].value = this.renderTargetX; + this.convolutionUniforms[ "uImageIncrement" ].value = THREE.BloomPass.blurY; + + renderer.render( this.scene, this.camera, this.renderTargetY, true ); + + // Render original scene with superimposed blur to texture + + this.quad.material = this.materialCopy; + + this.copyUniforms[ "tDiffuse" ].value = this.renderTargetY; + + if ( maskActive ) renderer.context.enable( renderer.context.STENCIL_TEST ); + + renderer.render( this.scene, this.camera, readBuffer, this.clear ); + + } + +}; + +THREE.BloomPass.blurX = new THREE.Vector2( 0.001953125, 0.0 ); +THREE.BloomPass.blurY = new THREE.Vector2( 0.0, 0.001953125 ); diff --git a/node_modules/three/examples/js/postprocessing/BokehPass.js b/node_modules/three/examples/js/postprocessing/BokehPass.js new file mode 100644 index 00000000..d8f95dfe --- /dev/null +++ b/node_modules/three/examples/js/postprocessing/BokehPass.js @@ -0,0 +1,102 @@ +/** + * Depth-of-field post-process with bokeh shader + */ + + +THREE.BokehPass = function ( scene, camera, params ) { + + this.scene = scene; + this.camera = camera; + + var focus = ( params.focus !== undefined ) ? params.focus : 1.0; + var aspect = ( params.aspect !== undefined ) ? params.aspect : camera.aspect; + var aperture = ( params.aperture !== undefined ) ? params.aperture : 0.025; + var maxblur = ( params.maxblur !== undefined ) ? params.maxblur : 1.0; + + // render targets + + var width = params.width || window.innerWidth || 1; + var height = params.height || window.innerHeight || 1; + + this.renderTargetColor = new THREE.WebGLRenderTarget( width, height, { + minFilter: THREE.LinearFilter, + magFilter: THREE.LinearFilter, + format: THREE.RGBFormat + } ); + + this.renderTargetDepth = this.renderTargetColor.clone(); + + // depth material + + this.materialDepth = new THREE.MeshDepthMaterial(); + + // bokeh material + + if ( THREE.BokehShader === undefined ) { + + console.error( "THREE.BokehPass relies on THREE.BokehShader" ); + + } + + var bokehShader = THREE.BokehShader; + var bokehUniforms = THREE.UniformsUtils.clone( bokehShader.uniforms ); + + bokehUniforms[ "tDepth" ].value = this.renderTargetDepth; + + bokehUniforms[ "focus" ].value = focus; + bokehUniforms[ "aspect" ].value = aspect; + bokehUniforms[ "aperture" ].value = aperture; + bokehUniforms[ "maxblur" ].value = maxblur; + + this.materialBokeh = new THREE.ShaderMaterial( { + uniforms: bokehUniforms, + vertexShader: bokehShader.vertexShader, + fragmentShader: bokehShader.fragmentShader + } ); + + this.uniforms = bokehUniforms; + this.enabled = true; + this.needsSwap = false; + this.renderToScreen = false; + this.clear = false; + + this.camera2 = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); + this.scene2 = new THREE.Scene(); + + this.quad2 = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null ); + this.scene2.add( this.quad2 ); + +}; + +THREE.BokehPass.prototype = { + + render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) { + + this.quad2.material = this.materialBokeh; + + // Render depth into texture + + this.scene.overrideMaterial = this.materialDepth; + + renderer.render( this.scene, this.camera, this.renderTargetDepth, true ); + + // Render bokeh composite + + this.uniforms[ "tColor" ].value = readBuffer; + + if ( this.renderToScreen ) { + + renderer.render( this.scene2, this.camera2 ); + + } else { + + renderer.render( this.scene2, this.camera2, writeBuffer, this.clear ); + + } + + this.scene.overrideMaterial = null; + + } + +}; + diff --git a/node_modules/three/examples/js/postprocessing/DotScreenPass.js b/node_modules/three/examples/js/postprocessing/DotScreenPass.js new file mode 100644 index 00000000..9743ad70 --- /dev/null +++ b/node_modules/three/examples/js/postprocessing/DotScreenPass.js @@ -0,0 +1,60 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.DotScreenPass = function ( center, angle, scale ) { + + if ( THREE.DotScreenShader === undefined ) + console.error( "THREE.DotScreenPass relies on THREE.DotScreenShader" ); + + var shader = THREE.DotScreenShader; + + this.uniforms = THREE.UniformsUtils.clone( shader.uniforms ); + + if ( center !== undefined ) this.uniforms[ "center" ].value.copy( center ); + if ( angle !== undefined ) this.uniforms[ "angle" ].value = angle; + if ( scale !== undefined ) this.uniforms[ "scale" ].value = scale; + + this.material = new THREE.ShaderMaterial( { + + uniforms: this.uniforms, + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader + + } ); + + this.enabled = true; + this.renderToScreen = false; + this.needsSwap = true; + + + this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); + this.scene = new THREE.Scene(); + + this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null ); + this.scene.add( this.quad ); + +}; + +THREE.DotScreenPass.prototype = { + + render: function ( renderer, writeBuffer, readBuffer, delta ) { + + this.uniforms[ "tDiffuse" ].value = readBuffer; + this.uniforms[ "tSize" ].value.set( readBuffer.width, readBuffer.height ); + + this.quad.material = this.material; + + if ( this.renderToScreen ) { + + renderer.render( this.scene, this.camera ); + + } else { + + renderer.render( this.scene, this.camera, writeBuffer, false ); + + } + + } + +}; diff --git a/node_modules/three/examples/js/postprocessing/EffectComposer.js b/node_modules/three/examples/js/postprocessing/EffectComposer.js new file mode 100644 index 00000000..3d214500 --- /dev/null +++ b/node_modules/three/examples/js/postprocessing/EffectComposer.js @@ -0,0 +1,137 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.EffectComposer = function ( renderer, renderTarget ) { + + this.renderer = renderer; + + if ( renderTarget === undefined ) { + + var pixelRatio = renderer.getPixelRatio(); + + var width = Math.floor( renderer.context.canvas.width / pixelRatio ) || 1; + var height = Math.floor( renderer.context.canvas.height / pixelRatio ) || 1; + var parameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false }; + + renderTarget = new THREE.WebGLRenderTarget( width, height, parameters ); + + } + + this.renderTarget1 = renderTarget; + this.renderTarget2 = renderTarget.clone(); + + this.writeBuffer = this.renderTarget1; + this.readBuffer = this.renderTarget2; + + this.passes = []; + + if ( THREE.CopyShader === undefined ) + console.error( "THREE.EffectComposer relies on THREE.CopyShader" ); + + this.copyPass = new THREE.ShaderPass( THREE.CopyShader ); + +}; + +THREE.EffectComposer.prototype = { + + swapBuffers: function() { + + var tmp = this.readBuffer; + this.readBuffer = this.writeBuffer; + this.writeBuffer = tmp; + + }, + + addPass: function ( pass ) { + + this.passes.push( pass ); + + }, + + insertPass: function ( pass, index ) { + + this.passes.splice( index, 0, pass ); + + }, + + render: function ( delta ) { + + this.writeBuffer = this.renderTarget1; + this.readBuffer = this.renderTarget2; + + var maskActive = false; + + var pass, i, il = this.passes.length; + + for ( i = 0; i < il; i ++ ) { + + pass = this.passes[ i ]; + + if ( ! pass.enabled ) continue; + + pass.render( this.renderer, this.writeBuffer, this.readBuffer, delta, maskActive ); + + if ( pass.needsSwap ) { + + if ( maskActive ) { + + var context = this.renderer.context; + + context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff ); + + this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, delta ); + + context.stencilFunc( context.EQUAL, 1, 0xffffffff ); + + } + + this.swapBuffers(); + + } + + if ( pass instanceof THREE.MaskPass ) { + + maskActive = true; + + } else if ( pass instanceof THREE.ClearMaskPass ) { + + maskActive = false; + + } + + } + + }, + + reset: function ( renderTarget ) { + + if ( renderTarget === undefined ) { + + renderTarget = this.renderTarget1.clone(); + + var pixelRatio = this.renderer.getPixelRatio(); + + renderTarget.width = Math.floor( this.renderer.context.canvas.width / pixelRatio ); + renderTarget.height = Math.floor( this.renderer.context.canvas.height / pixelRatio ); + + } + + this.renderTarget1.dispose(); + this.renderTarget1 = renderTarget; + this.renderTarget2.dispose(); + this.renderTarget2 = renderTarget.clone(); + + this.writeBuffer = this.renderTarget1; + this.readBuffer = this.renderTarget2; + + }, + + setSize: function ( width, height ) { + + this.renderTarget1.setSize( width, height ); + this.renderTarget2.setSize( width, height ); + + } + +}; diff --git a/node_modules/three/examples/js/postprocessing/FilmPass.js b/node_modules/three/examples/js/postprocessing/FilmPass.js new file mode 100644 index 00000000..bb7a8397 --- /dev/null +++ b/node_modules/three/examples/js/postprocessing/FilmPass.js @@ -0,0 +1,61 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.FilmPass = function ( noiseIntensity, scanlinesIntensity, scanlinesCount, grayscale ) { + + if ( THREE.FilmShader === undefined ) + console.error( "THREE.FilmPass relies on THREE.FilmShader" ); + + var shader = THREE.FilmShader; + + this.uniforms = THREE.UniformsUtils.clone( shader.uniforms ); + + this.material = new THREE.ShaderMaterial( { + + uniforms: this.uniforms, + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader + + } ); + + if ( grayscale !== undefined ) this.uniforms.grayscale.value = grayscale; + if ( noiseIntensity !== undefined ) this.uniforms.nIntensity.value = noiseIntensity; + if ( scanlinesIntensity !== undefined ) this.uniforms.sIntensity.value = scanlinesIntensity; + if ( scanlinesCount !== undefined ) this.uniforms.sCount.value = scanlinesCount; + + this.enabled = true; + this.renderToScreen = false; + this.needsSwap = true; + + + this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); + this.scene = new THREE.Scene(); + + this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null ); + this.scene.add( this.quad ); + +}; + +THREE.FilmPass.prototype = { + + render: function ( renderer, writeBuffer, readBuffer, delta ) { + + this.uniforms[ "tDiffuse" ].value = readBuffer; + this.uniforms[ "time" ].value += delta; + + this.quad.material = this.material; + + if ( this.renderToScreen ) { + + renderer.render( this.scene, this.camera ); + + } else { + + renderer.render( this.scene, this.camera, writeBuffer, false ); + + } + + } + +}; diff --git a/node_modules/three/examples/js/postprocessing/GlitchPass.js b/node_modules/three/examples/js/postprocessing/GlitchPass.js new file mode 100644 index 00000000..ad558947 --- /dev/null +++ b/node_modules/three/examples/js/postprocessing/GlitchPass.js @@ -0,0 +1,112 @@ +/** + + */ + +THREE.GlitchPass = function ( dt_size ) { + + if ( THREE.DigitalGlitch === undefined ) console.error( "THREE.GlitchPass relies on THREE.DigitalGlitch" ); + + var shader = THREE.DigitalGlitch; + this.uniforms = THREE.UniformsUtils.clone( shader.uniforms ); + + if ( dt_size == undefined ) dt_size = 64; + + + this.uniforms[ "tDisp" ].value = this.generateHeightmap( dt_size ); + + + this.material = new THREE.ShaderMaterial( { + uniforms: this.uniforms, + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader + } ); + + this.enabled = true; + this.renderToScreen = false; + this.needsSwap = true; + + + this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); + this.scene = new THREE.Scene(); + + this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null ); + this.scene.add( this.quad ); + + this.goWild = false; + this.curF = 0; + this.generateTrigger(); + +}; + +THREE.GlitchPass.prototype = { + + render: function ( renderer, writeBuffer, readBuffer, delta ) { + + this.uniforms[ "tDiffuse" ].value = readBuffer; + this.uniforms[ 'seed' ].value = Math.random();//default seeding + this.uniforms[ 'byp' ].value = 0; + + if ( this.curF % this.randX == 0 || this.goWild == true ) { + + this.uniforms[ 'amount' ].value = Math.random() / 30; + this.uniforms[ 'angle' ].value = THREE.Math.randFloat( - Math.PI, Math.PI ); + this.uniforms[ 'seed_x' ].value = THREE.Math.randFloat( - 1, 1 ); + this.uniforms[ 'seed_y' ].value = THREE.Math.randFloat( - 1, 1 ); + this.uniforms[ 'distortion_x' ].value = THREE.Math.randFloat( 0, 1 ); + this.uniforms[ 'distortion_y' ].value = THREE.Math.randFloat( 0, 1 ); + this.curF = 0; + this.generateTrigger(); + + } else if ( this.curF % this.randX < this.randX / 5 ) { + + this.uniforms[ 'amount' ].value = Math.random() / 90; + this.uniforms[ 'angle' ].value = THREE.Math.randFloat( - Math.PI, Math.PI ); + this.uniforms[ 'distortion_x' ].value = THREE.Math.randFloat( 0, 1 ); + this.uniforms[ 'distortion_y' ].value = THREE.Math.randFloat( 0, 1 ); + this.uniforms[ 'seed_x' ].value = THREE.Math.randFloat( - 0.3, 0.3 ); + this.uniforms[ 'seed_y' ].value = THREE.Math.randFloat( - 0.3, 0.3 ); + + } else if ( this.goWild == false ) { + + this.uniforms[ 'byp' ].value = 1; + + } + this.curF ++; + + this.quad.material = this.material; + if ( this.renderToScreen ) { + + renderer.render( this.scene, this.camera ); + + } else { + + renderer.render( this.scene, this.camera, writeBuffer, false ); + + } + + }, + generateTrigger: function() { + + this.randX = THREE.Math.randInt( 120, 240 ); + + }, + generateHeightmap: function( dt_size ) { + + var data_arr = new Float32Array( dt_size * dt_size * 3 ); + var length = dt_size * dt_size; + + for ( var i = 0; i < length; i ++ ) { + + var val = THREE.Math.randFloat( 0, 1 ); + data_arr[ i * 3 + 0 ] = val; + data_arr[ i * 3 + 1 ] = val; + data_arr[ i * 3 + 2 ] = val; + + } + + var texture = new THREE.DataTexture( data_arr, dt_size, dt_size, THREE.RGBFormat, THREE.FloatType ); + texture.needsUpdate = true; + return texture; + + } +}; diff --git a/node_modules/three/examples/js/postprocessing/MaskPass.js b/node_modules/three/examples/js/postprocessing/MaskPass.js new file mode 100644 index 00000000..54166bdc --- /dev/null +++ b/node_modules/three/examples/js/postprocessing/MaskPass.js @@ -0,0 +1,86 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.MaskPass = function ( scene, camera ) { + + this.scene = scene; + this.camera = camera; + + this.enabled = true; + this.clear = true; + this.needsSwap = false; + + this.inverse = false; + +}; + +THREE.MaskPass.prototype = { + + render: function ( renderer, writeBuffer, readBuffer, delta ) { + + var context = renderer.context; + + // don't update color or depth + + context.colorMask( false, false, false, false ); + context.depthMask( false ); + + // set up stencil + + var writeValue, clearValue; + + if ( this.inverse ) { + + writeValue = 0; + clearValue = 1; + + } else { + + writeValue = 1; + clearValue = 0; + + } + + context.enable( context.STENCIL_TEST ); + context.stencilOp( context.REPLACE, context.REPLACE, context.REPLACE ); + context.stencilFunc( context.ALWAYS, writeValue, 0xffffffff ); + context.clearStencil( clearValue ); + + // draw into the stencil buffer + + renderer.render( this.scene, this.camera, readBuffer, this.clear ); + renderer.render( this.scene, this.camera, writeBuffer, this.clear ); + + // re-enable update of color and depth + + context.colorMask( true, true, true, true ); + context.depthMask( true ); + + // only render where stencil is set to 1 + + context.stencilFunc( context.EQUAL, 1, 0xffffffff ); // draw if == 1 + context.stencilOp( context.KEEP, context.KEEP, context.KEEP ); + + } + +}; + + +THREE.ClearMaskPass = function () { + + this.enabled = true; + +}; + +THREE.ClearMaskPass.prototype = { + + render: function ( renderer, writeBuffer, readBuffer, delta ) { + + var context = renderer.context; + + context.disable( context.STENCIL_TEST ); + + } + +}; diff --git a/node_modules/three/examples/js/postprocessing/RenderPass.js b/node_modules/three/examples/js/postprocessing/RenderPass.js new file mode 100644 index 00000000..ca0c88f6 --- /dev/null +++ b/node_modules/three/examples/js/postprocessing/RenderPass.js @@ -0,0 +1,51 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.RenderPass = function ( scene, camera, overrideMaterial, clearColor, clearAlpha ) { + + this.scene = scene; + this.camera = camera; + + this.overrideMaterial = overrideMaterial; + + this.clearColor = clearColor; + this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 1; + + this.oldClearColor = new THREE.Color(); + this.oldClearAlpha = 1; + + this.enabled = true; + this.clear = true; + this.needsSwap = false; + +}; + +THREE.RenderPass.prototype = { + + render: function ( renderer, writeBuffer, readBuffer, delta ) { + + this.scene.overrideMaterial = this.overrideMaterial; + + if ( this.clearColor ) { + + this.oldClearColor.copy( renderer.getClearColor() ); + this.oldClearAlpha = renderer.getClearAlpha(); + + renderer.setClearColor( this.clearColor, this.clearAlpha ); + + } + + renderer.render( this.scene, this.camera, readBuffer, this.clear ); + + if ( this.clearColor ) { + + renderer.setClearColor( this.oldClearColor, this.oldClearAlpha ); + + } + + this.scene.overrideMaterial = null; + + } + +}; diff --git a/node_modules/three/examples/js/postprocessing/SavePass.js b/node_modules/three/examples/js/postprocessing/SavePass.js new file mode 100644 index 00000000..d9b5501f --- /dev/null +++ b/node_modules/three/examples/js/postprocessing/SavePass.js @@ -0,0 +1,62 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SavePass = function ( renderTarget ) { + + if ( THREE.CopyShader === undefined ) + console.error( "THREE.SavePass relies on THREE.CopyShader" ); + + var shader = THREE.CopyShader; + + this.textureID = "tDiffuse"; + + this.uniforms = THREE.UniformsUtils.clone( shader.uniforms ); + + this.material = new THREE.ShaderMaterial( { + + uniforms: this.uniforms, + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader + + } ); + + this.renderTarget = renderTarget; + + if ( this.renderTarget === undefined ) { + + this.renderTargetParameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false }; + this.renderTarget = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, this.renderTargetParameters ); + + } + + this.enabled = true; + this.needsSwap = false; + this.clear = false; + + + this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); + this.scene = new THREE.Scene(); + + this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null ); + this.scene.add( this.quad ); + +}; + +THREE.SavePass.prototype = { + + render: function ( renderer, writeBuffer, readBuffer, delta ) { + + if ( this.uniforms[ this.textureID ] ) { + + this.uniforms[ this.textureID ].value = readBuffer; + + } + + this.quad.material = this.material; + + renderer.render( this.scene, this.camera, this.renderTarget, this.clear ); + + } + +}; diff --git a/node_modules/three/examples/js/postprocessing/ShaderPass.js b/node_modules/three/examples/js/postprocessing/ShaderPass.js new file mode 100644 index 00000000..e090e3ad --- /dev/null +++ b/node_modules/three/examples/js/postprocessing/ShaderPass.js @@ -0,0 +1,59 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ShaderPass = function ( shader, textureID ) { + + this.textureID = ( textureID !== undefined ) ? textureID : "tDiffuse"; + + this.uniforms = THREE.UniformsUtils.clone( shader.uniforms ); + + this.material = new THREE.ShaderMaterial( { + + defines: shader.defines || {}, + uniforms: this.uniforms, + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader + + } ); + + this.renderToScreen = false; + + this.enabled = true; + this.needsSwap = true; + this.clear = false; + + + this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); + this.scene = new THREE.Scene(); + + this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null ); + this.scene.add( this.quad ); + +}; + +THREE.ShaderPass.prototype = { + + render: function ( renderer, writeBuffer, readBuffer, delta ) { + + if ( this.uniforms[ this.textureID ] ) { + + this.uniforms[ this.textureID ].value = readBuffer; + + } + + this.quad.material = this.material; + + if ( this.renderToScreen ) { + + renderer.render( this.scene, this.camera ); + + } else { + + renderer.render( this.scene, this.camera, writeBuffer, this.clear ); + + } + + } + +}; diff --git a/node_modules/three/examples/js/postprocessing/TexturePass.js b/node_modules/three/examples/js/postprocessing/TexturePass.js new file mode 100644 index 00000000..ab8dad85 --- /dev/null +++ b/node_modules/three/examples/js/postprocessing/TexturePass.js @@ -0,0 +1,47 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.TexturePass = function ( texture, opacity ) { + + if ( THREE.CopyShader === undefined ) + console.error( "THREE.TexturePass relies on THREE.CopyShader" ); + + var shader = THREE.CopyShader; + + this.uniforms = THREE.UniformsUtils.clone( shader.uniforms ); + + this.uniforms[ "opacity" ].value = ( opacity !== undefined ) ? opacity : 1.0; + this.uniforms[ "tDiffuse" ].value = texture; + + this.material = new THREE.ShaderMaterial( { + + uniforms: this.uniforms, + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader + + } ); + + this.enabled = true; + this.needsSwap = false; + + + this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); + this.scene = new THREE.Scene(); + + this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null ); + this.scene.add( this.quad ); + +}; + +THREE.TexturePass.prototype = { + + render: function ( renderer, writeBuffer, readBuffer, delta ) { + + this.quad.material = this.material; + + renderer.render( this.scene, this.camera, readBuffer ); + + } + +}; diff --git a/node_modules/three/examples/js/renderers/CSS2DRenderer.js b/node_modules/three/examples/js/renderers/CSS2DRenderer.js new file mode 100644 index 00000000..90e0c9c2 --- /dev/null +++ b/node_modules/three/examples/js/renderers/CSS2DRenderer.js @@ -0,0 +1,104 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CSS2DObject = function ( element ) { + + THREE.Object3D.call( this ); + + this.element = element; + this.element.style.position = 'absolute'; + + this.addEventListener( 'removed', function ( event ) { + + if ( this.element.parentNode !== null ) { + + this.element.parentNode.removeChild( this.element ); + + } + + } ); + +}; + +THREE.CSS2DObject.prototype = Object.create( THREE.Object3D.prototype ); +THREE.CSS2DObject.prototype.constructor = THREE.CSS2DObject; + +// + +THREE.CSS2DRenderer = function () { + + console.log( 'THREE.CSS2DRenderer', THREE.REVISION ); + + var _width, _height; + var _widthHalf, _heightHalf; + + var vector = new THREE.Vector3(); + var viewMatrix = new THREE.Matrix4(); + var viewProjectionMatrix = new THREE.Matrix4(); + + var domElement = document.createElement( 'div' ); + domElement.style.overflow = 'hidden'; + + this.domElement = domElement; + + this.setSize = function ( width, height ) { + + _width = width; + _height = height; + + _widthHalf = _width / 2; + _heightHalf = _height / 2; + + domElement.style.width = width + 'px'; + domElement.style.height = height + 'px'; + + }; + + var renderObject = function ( object, camera ) { + + if ( object instanceof THREE.CSS2DObject ) { + + vector.setFromMatrixPosition( object.matrixWorld ); + vector.applyProjection( viewProjectionMatrix ); + + var element = object.element; + var style = 'translate(-50%,-50%) translate(' + ( vector.x * _widthHalf + _widthHalf ) + 'px,' + ( - vector.y * _heightHalf + _heightHalf ) + 'px)'; + + element.style.WebkitTransform = style; + element.style.MozTransform = style; + element.style.oTransform = style; + element.style.transform = style; + + if ( element.parentNode !== domElement ) { + + domElement.appendChild( element ); + + } + + } + + for ( var i = 0, l = object.children.length; i < l; i ++ ) { + + renderObject( object.children[ i ], camera ); + + } + + }; + + this.render = function ( scene, camera ) { + + scene.updateMatrixWorld(); + + if ( camera.parent === null ) camera.updateMatrixWorld(); + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + + viewMatrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) ); + viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, viewMatrix ); + + renderObject( scene, camera ); + + }; + +}; diff --git a/node_modules/three/examples/js/renderers/CSS3DRenderer.js b/node_modules/three/examples/js/renderers/CSS3DRenderer.js new file mode 100644 index 00000000..abbe742b --- /dev/null +++ b/node_modules/three/examples/js/renderers/CSS3DRenderer.js @@ -0,0 +1,252 @@ +/** + * Based on http://www.emagix.net/academic/mscs-project/item/camera-sync-with-css3-and-webgl-threejs + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CSS3DObject = function ( element ) { + + THREE.Object3D.call( this ); + + this.element = element; + this.element.style.position = 'absolute'; + + this.addEventListener( 'removed', function ( event ) { + + if ( this.element.parentNode !== null ) { + + this.element.parentNode.removeChild( this.element ); + + } + + } ); + +}; + +THREE.CSS3DObject.prototype = Object.create( THREE.Object3D.prototype ); +THREE.CSS3DObject.prototype.constructor = THREE.CSS3DObject; + +THREE.CSS3DSprite = function ( element ) { + + THREE.CSS3DObject.call( this, element ); + +}; + +THREE.CSS3DSprite.prototype = Object.create( THREE.CSS3DObject.prototype ); +THREE.CSS3DSprite.prototype.constructor = THREE.CSS3DSprite; + +// + +THREE.CSS3DRenderer = function () { + + console.log( 'THREE.CSS3DRenderer', THREE.REVISION ); + + var _width, _height; + var _widthHalf, _heightHalf; + + var matrix = new THREE.Matrix4(); + + var cache = { + camera: { fov: 0, style: '' }, + objects: {} + }; + + var domElement = document.createElement( 'div' ); + domElement.style.overflow = 'hidden'; + + domElement.style.WebkitTransformStyle = 'preserve-3d'; + domElement.style.MozTransformStyle = 'preserve-3d'; + domElement.style.oTransformStyle = 'preserve-3d'; + domElement.style.transformStyle = 'preserve-3d'; + + this.domElement = domElement; + + var cameraElement = document.createElement( 'div' ); + + cameraElement.style.WebkitTransformStyle = 'preserve-3d'; + cameraElement.style.MozTransformStyle = 'preserve-3d'; + cameraElement.style.oTransformStyle = 'preserve-3d'; + cameraElement.style.transformStyle = 'preserve-3d'; + + domElement.appendChild( cameraElement ); + + this.setClearColor = function () {}; + + this.getSize = function() { + + return { + width: _width, + height: _height + }; + + }; + + this.setSize = function ( width, height ) { + + _width = width; + _height = height; + + _widthHalf = _width / 2; + _heightHalf = _height / 2; + + domElement.style.width = width + 'px'; + domElement.style.height = height + 'px'; + + cameraElement.style.width = width + 'px'; + cameraElement.style.height = height + 'px'; + + }; + + var epsilon = function ( value ) { + + return Math.abs( value ) < Number.EPSILON ? 0 : value; + + }; + + var getCameraCSSMatrix = function ( matrix ) { + + var elements = matrix.elements; + + return 'matrix3d(' + + epsilon( elements[ 0 ] ) + ',' + + epsilon( - elements[ 1 ] ) + ',' + + epsilon( elements[ 2 ] ) + ',' + + epsilon( elements[ 3 ] ) + ',' + + epsilon( elements[ 4 ] ) + ',' + + epsilon( - elements[ 5 ] ) + ',' + + epsilon( elements[ 6 ] ) + ',' + + epsilon( elements[ 7 ] ) + ',' + + epsilon( elements[ 8 ] ) + ',' + + epsilon( - elements[ 9 ] ) + ',' + + epsilon( elements[ 10 ] ) + ',' + + epsilon( elements[ 11 ] ) + ',' + + epsilon( elements[ 12 ] ) + ',' + + epsilon( - elements[ 13 ] ) + ',' + + epsilon( elements[ 14 ] ) + ',' + + epsilon( elements[ 15 ] ) + + ')'; + + }; + + var getObjectCSSMatrix = function ( matrix ) { + + var elements = matrix.elements; + + return 'translate3d(-50%,-50%,0) matrix3d(' + + epsilon( elements[ 0 ] ) + ',' + + epsilon( elements[ 1 ] ) + ',' + + epsilon( elements[ 2 ] ) + ',' + + epsilon( elements[ 3 ] ) + ',' + + epsilon( - elements[ 4 ] ) + ',' + + epsilon( - elements[ 5 ] ) + ',' + + epsilon( - elements[ 6 ] ) + ',' + + epsilon( - elements[ 7 ] ) + ',' + + epsilon( elements[ 8 ] ) + ',' + + epsilon( elements[ 9 ] ) + ',' + + epsilon( elements[ 10 ] ) + ',' + + epsilon( elements[ 11 ] ) + ',' + + epsilon( elements[ 12 ] ) + ',' + + epsilon( elements[ 13 ] ) + ',' + + epsilon( elements[ 14 ] ) + ',' + + epsilon( elements[ 15 ] ) + + ')'; + + }; + + var renderObject = function ( object, camera ) { + + if ( object instanceof THREE.CSS3DObject ) { + + var style; + + if ( object instanceof THREE.CSS3DSprite ) { + + // http://swiftcoder.wordpress.com/2008/11/25/constructing-a-billboard-matrix/ + + matrix.copy( camera.matrixWorldInverse ); + matrix.transpose(); + matrix.copyPosition( object.matrixWorld ); + matrix.scale( object.scale ); + + matrix.elements[ 3 ] = 0; + matrix.elements[ 7 ] = 0; + matrix.elements[ 11 ] = 0; + matrix.elements[ 15 ] = 1; + + style = getObjectCSSMatrix( matrix ); + + } else { + + style = getObjectCSSMatrix( object.matrixWorld ); + + } + + var element = object.element; + var cachedStyle = cache.objects[ object.id ]; + + if ( cachedStyle === undefined || cachedStyle !== style ) { + + element.style.WebkitTransform = style; + element.style.MozTransform = style; + element.style.oTransform = style; + element.style.transform = style; + + cache.objects[ object.id ] = style; + + } + + if ( element.parentNode !== cameraElement ) { + + cameraElement.appendChild( element ); + + } + + } + + for ( var i = 0, l = object.children.length; i < l; i ++ ) { + + renderObject( object.children[ i ], camera ); + + } + + }; + + this.render = function ( scene, camera ) { + + var fov = 0.5 / Math.tan( THREE.Math.degToRad( camera.fov * 0.5 ) ) * _height; + + if ( cache.camera.fov !== fov ) { + + domElement.style.WebkitPerspective = fov + "px"; + domElement.style.MozPerspective = fov + "px"; + domElement.style.oPerspective = fov + "px"; + domElement.style.perspective = fov + "px"; + + cache.camera.fov = fov; + + } + + scene.updateMatrixWorld(); + + if ( camera.parent === null ) camera.updateMatrixWorld(); + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + + var style = "translate3d(0,0," + fov + "px)" + getCameraCSSMatrix( camera.matrixWorldInverse ) + + " translate3d(" + _widthHalf + "px," + _heightHalf + "px, 0)"; + + if ( cache.camera.style !== style ) { + + cameraElement.style.WebkitTransform = style; + cameraElement.style.MozTransform = style; + cameraElement.style.oTransform = style; + cameraElement.style.transform = style; + + cache.camera.style = style; + + } + + renderObject( scene, camera ); + + }; + +}; diff --git a/node_modules/three/examples/js/renderers/CSS3DStereoRenderer.js b/node_modules/three/examples/js/renderers/CSS3DStereoRenderer.js new file mode 100644 index 00000000..565efe8f --- /dev/null +++ b/node_modules/three/examples/js/renderers/CSS3DStereoRenderer.js @@ -0,0 +1,329 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CSS3DObject = function ( element ) { + + THREE.Object3D.call( this ); + + this.elementL = element.cloneNode( true ); + this.elementL.style.position = 'absolute'; + + this.elementR = element.cloneNode( true ); + this.elementR.style.position = 'absolute'; + + this.addEventListener( 'removed', function ( event ) { + + if ( this.elementL.parentNode !== null ) { + + this.elementL.parentNode.removeChild( this.elementL ); + + } + + if ( this.elementR.parentNode !== null ) { + + this.elementR.parentNode.removeChild( this.elementR ); + + } + + } ); + +}; + +THREE.CSS3DObject.prototype = Object.create( THREE.Object3D.prototype ); +THREE.CSS3DObject.prototype.constructor = THREE.CSS3DObject; + +THREE.CSS3DSprite = function ( element ) { + + THREE.CSS3DObject.call( this, element ); + +}; + +THREE.CSS3DSprite.prototype = Object.create( THREE.CSS3DObject.prototype ); +THREE.CSS3DSprite.prototype.constructor = THREE.CSS3DSprite; + +// + +THREE.CSS3DStereoRenderer = function () { + + console.log( 'THREE.CSS3DRenderer', THREE.REVISION ); + + var _width, _height; + var _widthHalf, _heightHalf; + + var matrix = new THREE.Matrix4(); + + var _position = new THREE.Vector3(); + var _quaternion = new THREE.Quaternion(); + var _scale = new THREE.Vector3(); + + var _cameraL = new THREE.PerspectiveCamera(); + var _cameraR = new THREE.PerspectiveCamera(); + var _target = new THREE.Vector3(); + + this.separation = 6; + this.targetDistance = 500; + + // + + var domElement = document.createElement( 'div' ); + + this.domElement = domElement; + + // + + var domElementL = document.createElement( 'div' ); + domElementL.style.display = 'inline-block'; + domElementL.style.overflow = 'hidden'; + + domElementL.style.WebkitTransformStyle = 'preserve-3d'; + domElementL.style.MozTransformStyle = 'preserve-3d'; + domElementL.style.oTransformStyle = 'preserve-3d'; + domElementL.style.transformStyle = 'preserve-3d'; + + domElement.appendChild( domElementL ); + + var cameraElementL = document.createElement( 'div' ); + + cameraElementL.style.WebkitTransformStyle = 'preserve-3d'; + cameraElementL.style.MozTransformStyle = 'preserve-3d'; + cameraElementL.style.oTransformStyle = 'preserve-3d'; + cameraElementL.style.transformStyle = 'preserve-3d'; + + domElementL.appendChild( cameraElementL ); + + // + + var domElementR = document.createElement( 'div' ); + domElementR.style.display = 'inline-block'; + domElementR.style.overflow = 'hidden'; + + domElementR.style.WebkitTransformStyle = 'preserve-3d'; + domElementR.style.MozTransformStyle = 'preserve-3d'; + domElementR.style.oTransformStyle = 'preserve-3d'; + domElementR.style.transformStyle = 'preserve-3d'; + + domElement.appendChild( domElementR ); + + var cameraElementR = document.createElement( 'div' ); + + cameraElementR.style.WebkitTransformStyle = 'preserve-3d'; + cameraElementR.style.MozTransformStyle = 'preserve-3d'; + cameraElementR.style.oTransformStyle = 'preserve-3d'; + cameraElementR.style.transformStyle = 'preserve-3d'; + + domElementR.appendChild( cameraElementR ); + + this.setClearColor = function () { + + }; + + this.setSize = function ( width, height ) { + + domElement.style.width = width + 'px'; + domElement.style.height = height + 'px'; + + _width = width / 2; + _height = height; + + _widthHalf = _width / 2; + _heightHalf = _height / 2; + + domElementL.style.width = _width + 'px'; + domElementL.style.height = _height + 'px'; + + cameraElementL.style.width = _width + 'px'; + cameraElementL.style.height = _height + 'px'; + + domElementR.style.width = _width + 'px'; + domElementR.style.height = _height + 'px'; + + cameraElementR.style.width = _width + 'px'; + cameraElementR.style.height = _height + 'px'; + + }; + + var epsilon = function ( value ) { + + return Math.abs( value ) < Number.EPSILON ? 0 : value; + + }; + + var getCameraCSSMatrix = function ( matrix ) { + + var elements = matrix.elements; + + return 'matrix3d(' + + epsilon( elements[ 0 ] ) + ',' + + epsilon( - elements[ 1 ] ) + ',' + + epsilon( elements[ 2 ] ) + ',' + + epsilon( elements[ 3 ] ) + ',' + + epsilon( elements[ 4 ] ) + ',' + + epsilon( - elements[ 5 ] ) + ',' + + epsilon( elements[ 6 ] ) + ',' + + epsilon( elements[ 7 ] ) + ',' + + epsilon( elements[ 8 ] ) + ',' + + epsilon( - elements[ 9 ] ) + ',' + + epsilon( elements[ 10 ] ) + ',' + + epsilon( elements[ 11 ] ) + ',' + + epsilon( elements[ 12 ] ) + ',' + + epsilon( - elements[ 13 ] ) + ',' + + epsilon( elements[ 14 ] ) + ',' + + epsilon( elements[ 15 ] ) + + ')'; + + }; + + var getObjectCSSMatrix = function ( matrix ) { + + var elements = matrix.elements; + + return 'translate3d(-50%,-50%,0) matrix3d(' + + epsilon( elements[ 0 ] ) + ',' + + epsilon( elements[ 1 ] ) + ',' + + epsilon( elements[ 2 ] ) + ',' + + epsilon( elements[ 3 ] ) + ',' + + epsilon( - elements[ 4 ] ) + ',' + + epsilon( - elements[ 5 ] ) + ',' + + epsilon( - elements[ 6 ] ) + ',' + + epsilon( - elements[ 7 ] ) + ',' + + epsilon( elements[ 8 ] ) + ',' + + epsilon( elements[ 9 ] ) + ',' + + epsilon( elements[ 10 ] ) + ',' + + epsilon( elements[ 11 ] ) + ',' + + epsilon( elements[ 12 ] ) + ',' + + epsilon( elements[ 13 ] ) + ',' + + epsilon( elements[ 14 ] ) + ',' + + epsilon( elements[ 15 ] ) + + ')'; + + }; + + var renderObject = function ( object, camera, cameraElement, side ) { + + if ( object instanceof THREE.CSS3DObject ) { + + var style; + + if ( object instanceof THREE.CSS3DSprite ) { + + // http://swiftcoder.wordpress.com/2008/11/25/constructing-a-billboard-matrix/ + + matrix.copy( camera.matrixWorldInverse ); + matrix.transpose(); + matrix.copyPosition( object.matrixWorld ); + matrix.scale( object.scale ); + + matrix.elements[ 3 ] = 0; + matrix.elements[ 7 ] = 0; + matrix.elements[ 11 ] = 0; + matrix.elements[ 15 ] = 1; + + style = getObjectCSSMatrix( matrix ); + + } else { + + style = getObjectCSSMatrix( object.matrixWorld ); + + } + + var element = object[ 'element' + side ]; + + element.style.WebkitTransform = style; + element.style.MozTransform = style; + element.style.oTransform = style; + element.style.transform = style; + + if ( element.parentNode !== cameraElement ) { + + cameraElement.appendChild( element ); + + } + + } + + for ( var i = 0, l = object.children.length; i < l; i ++ ) { + + renderObject( object.children[ i ], camera, cameraElement, side ); + + } + + }; + + this.render = function ( scene, camera ) { + + scene.updateMatrixWorld(); + + if ( camera.parent === null ) camera.updateMatrixWorld(); + + camera.matrixWorld.decompose( _position, _quaternion, _scale ); + + _target.set( 0, 0, - this.targetDistance ); + _target.applyQuaternion( _quaternion ); + _target.add( _position ); + + var fov = 0.5 / Math.tan( THREE.Math.degToRad( camera.fov * 0.5 ) ) * _height; + + // Left + + _cameraL.fov = camera.fov; + _cameraL.aspect = 0.5 * camera.aspect; + _cameraL.updateProjectionMatrix(); + + _cameraL.near = camera.near; + _cameraL.far = camera.far; + + _cameraL.position.copy( _position ); + _cameraL.translateX( - this.separation ); + _cameraL.lookAt( _target ); + _cameraL.updateMatrixWorld(); + + domElementL.style.WebkitPerspective = fov + "px"; + domElementL.style.MozPerspective = fov + "px"; + domElementL.style.oPerspective = fov + "px"; + domElementL.style.perspective = fov + "px"; + + _cameraL.matrixWorldInverse.getInverse( _cameraL.matrixWorld ); + + var style = "translate3d(0,0," + fov + "px)" + getCameraCSSMatrix( _cameraL.matrixWorldInverse ) + + " translate3d(" + _widthHalf + "px," + _heightHalf + "px, 0)"; + + cameraElementL.style.WebkitTransform = style; + cameraElementL.style.MozTransform = style; + cameraElementL.style.oTransform = style; + cameraElementL.style.transform = style; + + renderObject( scene, _cameraL, cameraElementL, 'L' ); + + // Right + + _cameraR.projectionMatrix = _cameraL.projectionMatrix; + + _cameraR.near = camera.near; + _cameraR.far = camera.far; + + _cameraR.position.copy( _position ); + _cameraR.translateX( this.separation ); + _cameraR.lookAt( _target ); + _cameraR.updateMatrixWorld(); + + domElementR.style.WebkitPerspective = fov + "px"; + domElementR.style.MozPerspective = fov + "px"; + domElementR.style.oPerspective = fov + "px"; + domElementR.style.perspective = fov + "px"; + + _cameraR.matrixWorldInverse.getInverse( _cameraR.matrixWorld ); + + var style = "translate3d(0,0," + fov + "px)" + getCameraCSSMatrix( _cameraR.matrixWorldInverse ) + + " translate3d(" + _widthHalf + "px," + _heightHalf + "px, 0)"; + + cameraElementR.style.WebkitTransform = style; + cameraElementR.style.MozTransform = style; + cameraElementR.style.oTransform = style; + cameraElementR.style.transform = style; + + renderObject( scene, _cameraR, cameraElementR, 'R' ); + + }; + +}; diff --git a/node_modules/three/examples/js/renderers/CanvasRenderer.js b/node_modules/three/examples/js/renderers/CanvasRenderer.js new file mode 100644 index 00000000..5a1bea24 --- /dev/null +++ b/node_modules/three/examples/js/renderers/CanvasRenderer.js @@ -0,0 +1,1094 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.SpriteCanvasMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'SpriteCanvasMaterial'; + + this.color = new THREE.Color( 0xffffff ); + this.program = function ( context, color ) {}; + + this.setValues( parameters ); + +}; + +THREE.SpriteCanvasMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.SpriteCanvasMaterial.prototype.constructor = THREE.SpriteCanvasMaterial; + +THREE.SpriteCanvasMaterial.prototype.clone = function () { + + var material = new THREE.SpriteCanvasMaterial(); + + material.copy( this ); + material.color.copy( this.color ); + material.program = this.program; + + return material; + +}; + +// + +THREE.CanvasRenderer = function ( parameters ) { + + console.log( 'THREE.CanvasRenderer', THREE.REVISION ); + + parameters = parameters || {}; + + var _this = this, + _renderData, _elements, _lights, + _projector = new THREE.Projector(), + + _canvas = parameters.canvas !== undefined + ? parameters.canvas + : document.createElement( 'canvas' ), + + _canvasWidth = _canvas.width, + _canvasHeight = _canvas.height, + _canvasWidthHalf = Math.floor( _canvasWidth / 2 ), + _canvasHeightHalf = Math.floor( _canvasHeight / 2 ), + + _viewportX = 0, + _viewportY = 0, + _viewportWidth = _canvasWidth, + _viewportHeight = _canvasHeight, + + pixelRatio = 1, + + _context = _canvas.getContext( '2d', { + alpha: parameters.alpha === true + } ), + + _clearColor = new THREE.Color( 0x000000 ), + _clearAlpha = parameters.alpha === true ? 0 : 1, + + _contextGlobalAlpha = 1, + _contextGlobalCompositeOperation = 0, + _contextStrokeStyle = null, + _contextFillStyle = null, + _contextLineWidth = null, + _contextLineCap = null, + _contextLineJoin = null, + _contextLineDash = [], + + _camera, + + _v1, _v2, _v3, _v4, + _v5 = new THREE.RenderableVertex(), + _v6 = new THREE.RenderableVertex(), + + _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, + _v4x, _v4y, _v5x, _v5y, _v6x, _v6y, + + _color = new THREE.Color(), + _color1 = new THREE.Color(), + _color2 = new THREE.Color(), + _color3 = new THREE.Color(), + _color4 = new THREE.Color(), + + _diffuseColor = new THREE.Color(), + _emissiveColor = new THREE.Color(), + + _lightColor = new THREE.Color(), + + _patterns = {}, + + _image, _uvs, + _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, + + _clipBox = new THREE.Box2(), + _clearBox = new THREE.Box2(), + _elemBox = new THREE.Box2(), + + _ambientLight = new THREE.Color(), + _directionalLights = new THREE.Color(), + _pointLights = new THREE.Color(), + + _vector3 = new THREE.Vector3(), // Needed for PointLight + _centroid = new THREE.Vector3(), + _normal = new THREE.Vector3(), + _normalViewMatrix = new THREE.Matrix3(); + + // dash+gap fallbacks for Firefox and everything else + + if ( _context.setLineDash === undefined ) { + + _context.setLineDash = function () {} + + } + + this.domElement = _canvas; + + this.autoClear = true; + this.sortObjects = true; + this.sortElements = true; + + this.info = { + + render: { + + vertices: 0, + faces: 0 + + } + + }; + + // WebGLRenderer compatibility + + this.supportsVertexTextures = function () {}; + this.setFaceCulling = function () {}; + + // API + + this.getContext = function () { + + return _context; + + }; + + this.getContextAttributes = function () { + + return _context.getContextAttributes(); + + }; + + this.getPixelRatio = function () { + + return pixelRatio; + + }; + + this.setPixelRatio = function ( value ) { + + if ( value !== undefined ) pixelRatio = value; + + }; + + this.setSize = function ( width, height, updateStyle ) { + + _canvasWidth = width * pixelRatio; + _canvasHeight = height * pixelRatio; + + _canvas.width = _canvasWidth; + _canvas.height = _canvasHeight; + + _canvasWidthHalf = Math.floor( _canvasWidth / 2 ); + _canvasHeightHalf = Math.floor( _canvasHeight / 2 ); + + if ( updateStyle !== false ) { + + _canvas.style.width = width + 'px'; + _canvas.style.height = height + 'px'; + + } + + _clipBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); + _clipBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); + + _clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); + _clearBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); + + _contextGlobalAlpha = 1; + _contextGlobalCompositeOperation = 0; + _contextStrokeStyle = null; + _contextFillStyle = null; + _contextLineWidth = null; + _contextLineCap = null; + _contextLineJoin = null; + + this.setViewport( 0, 0, width, height ); + + }; + + this.setViewport = function ( x, y, width, height ) { + + _viewportX = x * pixelRatio; + _viewportY = y * pixelRatio; + + _viewportWidth = width * pixelRatio; + _viewportHeight = height * pixelRatio; + + }; + + this.setScissor = function () {}; + this.enableScissorTest = function () {}; + + this.setClearColor = function ( color, alpha ) { + + _clearColor.set( color ); + _clearAlpha = alpha !== undefined ? alpha : 1; + + _clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); + _clearBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); + + }; + + this.setClearColorHex = function ( hex, alpha ) { + + console.warn( 'THREE.CanvasRenderer: .setClearColorHex() is being removed. Use .setClearColor() instead.' ); + this.setClearColor( hex, alpha ); + + }; + + this.getClearColor = function () { + + return _clearColor; + + }; + + this.getClearAlpha = function () { + + return _clearAlpha; + + }; + + this.getMaxAnisotropy = function () { + + return 0; + + }; + + this.clear = function () { + + if ( _clearBox.empty() === false ) { + + _clearBox.intersect( _clipBox ); + _clearBox.expandByScalar( 2 ); + + _clearBox.min.x = _clearBox.min.x + _canvasWidthHalf; + _clearBox.min.y = - _clearBox.min.y + _canvasHeightHalf; // higher y value ! + _clearBox.max.x = _clearBox.max.x + _canvasWidthHalf; + _clearBox.max.y = - _clearBox.max.y + _canvasHeightHalf; // lower y value ! + + if ( _clearAlpha < 1 ) { + + _context.clearRect( + _clearBox.min.x | 0, + _clearBox.max.y | 0, + ( _clearBox.max.x - _clearBox.min.x ) | 0, + ( _clearBox.min.y - _clearBox.max.y ) | 0 + ); + + } + + if ( _clearAlpha > 0 ) { + + setBlending( THREE.NormalBlending ); + setOpacity( 1 ); + + setFillStyle( 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearAlpha + ')' ); + + _context.fillRect( + _clearBox.min.x | 0, + _clearBox.max.y | 0, + ( _clearBox.max.x - _clearBox.min.x ) | 0, + ( _clearBox.min.y - _clearBox.max.y ) | 0 + ); + + } + + _clearBox.makeEmpty(); + + } + + }; + + // compatibility + + this.clearColor = function () {}; + this.clearDepth = function () {}; + this.clearStencil = function () {}; + + this.render = function ( scene, camera ) { + + if ( camera instanceof THREE.Camera === false ) { + + console.error( 'THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.' ); + return; + + } + + if ( this.autoClear === true ) this.clear(); + + _this.info.render.vertices = 0; + _this.info.render.faces = 0; + + _context.setTransform( _viewportWidth / _canvasWidth, 0, 0, - _viewportHeight / _canvasHeight, _viewportX, _canvasHeight - _viewportY ); + _context.translate( _canvasWidthHalf, _canvasHeightHalf ); + + _renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); + _elements = _renderData.elements; + _lights = _renderData.lights; + _camera = camera; + + _normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse ); + + /* DEBUG + setFillStyle( 'rgba( 0, 255, 255, 0.5 )' ); + _context.fillRect( _clipBox.min.x, _clipBox.min.y, _clipBox.max.x - _clipBox.min.x, _clipBox.max.y - _clipBox.min.y ); + */ + + calculateLights(); + + for ( var e = 0, el = _elements.length; e < el; e ++ ) { + + var element = _elements[ e ]; + + var material = element.material; + + if ( material === undefined || material.opacity === 0 ) continue; + + _elemBox.makeEmpty(); + + if ( element instanceof THREE.RenderableSprite ) { + + _v1 = element; + _v1.x *= _canvasWidthHalf; _v1.y *= _canvasHeightHalf; + + renderSprite( _v1, element, material ); + + } else if ( element instanceof THREE.RenderableLine ) { + + _v1 = element.v1; _v2 = element.v2; + + _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; + _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; + + _elemBox.setFromPoints( [ + _v1.positionScreen, + _v2.positionScreen + ] ); + + if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { + + renderLine( _v1, _v2, element, material ); + + } + + } else if ( element instanceof THREE.RenderableFace ) { + + _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; + + if ( _v1.positionScreen.z < - 1 || _v1.positionScreen.z > 1 ) continue; + if ( _v2.positionScreen.z < - 1 || _v2.positionScreen.z > 1 ) continue; + if ( _v3.positionScreen.z < - 1 || _v3.positionScreen.z > 1 ) continue; + + _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; + _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; + _v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf; + + if ( material.overdraw > 0 ) { + + expand( _v1.positionScreen, _v2.positionScreen, material.overdraw ); + expand( _v2.positionScreen, _v3.positionScreen, material.overdraw ); + expand( _v3.positionScreen, _v1.positionScreen, material.overdraw ); + + } + + _elemBox.setFromPoints( [ + _v1.positionScreen, + _v2.positionScreen, + _v3.positionScreen + ] ); + + if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { + + renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material ); + + } + + } + + /* DEBUG + setLineWidth( 1 ); + setStrokeStyle( 'rgba( 0, 255, 0, 0.5 )' ); + _context.strokeRect( _elemBox.min.x, _elemBox.min.y, _elemBox.max.x - _elemBox.min.x, _elemBox.max.y - _elemBox.min.y ); + */ + + _clearBox.union( _elemBox ); + + } + + /* DEBUG + setLineWidth( 1 ); + setStrokeStyle( 'rgba( 255, 0, 0, 0.5 )' ); + _context.strokeRect( _clearBox.min.x, _clearBox.min.y, _clearBox.max.x - _clearBox.min.x, _clearBox.max.y - _clearBox.min.y ); + */ + + _context.setTransform( 1, 0, 0, 1, 0, 0 ); + + }; + + // + + function calculateLights() { + + _ambientLight.setRGB( 0, 0, 0 ); + _directionalLights.setRGB( 0, 0, 0 ); + _pointLights.setRGB( 0, 0, 0 ); + + for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { + + var light = _lights[ l ]; + var lightColor = light.color; + + if ( light instanceof THREE.AmbientLight ) { + + _ambientLight.add( lightColor ); + + } else if ( light instanceof THREE.DirectionalLight ) { + + // for sprites + + _directionalLights.add( lightColor ); + + } else if ( light instanceof THREE.PointLight ) { + + // for sprites + + _pointLights.add( lightColor ); + + } + + } + + } + + function calculateLight( position, normal, color ) { + + for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { + + var light = _lights[ l ]; + + _lightColor.copy( light.color ); + + if ( light instanceof THREE.DirectionalLight ) { + + var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ).normalize(); + + var amount = normal.dot( lightPosition ); + + if ( amount <= 0 ) continue; + + amount *= light.intensity; + + color.add( _lightColor.multiplyScalar( amount ) ); + + } else if ( light instanceof THREE.PointLight ) { + + var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ); + + var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() ); + + if ( amount <= 0 ) continue; + + amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); + + if ( amount == 0 ) continue; + + amount *= light.intensity; + + color.add( _lightColor.multiplyScalar( amount ) ); + + } + + } + + } + + function renderSprite( v1, element, material ) { + + setOpacity( material.opacity ); + setBlending( material.blending ); + + var scaleX = element.scale.x * _canvasWidthHalf; + var scaleY = element.scale.y * _canvasHeightHalf; + + var dist = 0.5 * Math.sqrt( scaleX * scaleX + scaleY * scaleY ); // allow for rotated sprite + _elemBox.min.set( v1.x - dist, v1.y - dist ); + _elemBox.max.set( v1.x + dist, v1.y + dist ); + + if ( material instanceof THREE.SpriteMaterial ) { + + var texture = material.map; + + if ( texture !== null ) { + + var pattern = _patterns[ texture.id ]; + + if ( pattern === undefined || pattern.version !== texture.version ) { + + pattern = textureToPattern( texture ); + _patterns[ texture.id ] = pattern; + + } + + if ( pattern.canvas !== undefined ) { + + setFillStyle( pattern.canvas ); + + var bitmap = texture.image; + + var ox = bitmap.width * texture.offset.x; + var oy = bitmap.height * texture.offset.y; + + var sx = bitmap.width * texture.repeat.x; + var sy = bitmap.height * texture.repeat.y; + + var cx = scaleX / sx; + var cy = scaleY / sy; + + _context.save(); + _context.translate( v1.x, v1.y ); + if ( material.rotation !== 0 ) _context.rotate( material.rotation ); + _context.translate( - scaleX / 2, - scaleY / 2 ); + _context.scale( cx, cy ); + _context.translate( - ox, - oy ); + _context.fillRect( ox, oy, sx, sy ); + _context.restore(); + + } + + } else { + + // no texture + + setFillStyle( material.color.getStyle() ); + + _context.save(); + _context.translate( v1.x, v1.y ); + if ( material.rotation !== 0 ) _context.rotate( material.rotation ); + _context.scale( scaleX, - scaleY ); + _context.fillRect( - 0.5, - 0.5, 1, 1 ); + _context.restore(); + + } + + } else if ( material instanceof THREE.SpriteCanvasMaterial ) { + + setStrokeStyle( material.color.getStyle() ); + setFillStyle( material.color.getStyle() ); + + _context.save(); + _context.translate( v1.x, v1.y ); + if ( material.rotation !== 0 ) _context.rotate( material.rotation ); + _context.scale( scaleX, scaleY ); + + material.program( _context ); + + _context.restore(); + + } + + /* DEBUG + setStrokeStyle( 'rgb(255,255,0)' ); + _context.beginPath(); + _context.moveTo( v1.x - 10, v1.y ); + _context.lineTo( v1.x + 10, v1.y ); + _context.moveTo( v1.x, v1.y - 10 ); + _context.lineTo( v1.x, v1.y + 10 ); + _context.stroke(); + */ + + } + + function renderLine( v1, v2, element, material ) { + + setOpacity( material.opacity ); + setBlending( material.blending ); + + _context.beginPath(); + _context.moveTo( v1.positionScreen.x, v1.positionScreen.y ); + _context.lineTo( v2.positionScreen.x, v2.positionScreen.y ); + + if ( material instanceof THREE.LineBasicMaterial ) { + + setLineWidth( material.linewidth ); + setLineCap( material.linecap ); + setLineJoin( material.linejoin ); + + if ( material.vertexColors !== THREE.VertexColors ) { + + setStrokeStyle( material.color.getStyle() ); + + } else { + + var colorStyle1 = element.vertexColors[ 0 ].getStyle(); + var colorStyle2 = element.vertexColors[ 1 ].getStyle(); + + if ( colorStyle1 === colorStyle2 ) { + + setStrokeStyle( colorStyle1 ); + + } else { + + try { + + var grad = _context.createLinearGradient( + v1.positionScreen.x, + v1.positionScreen.y, + v2.positionScreen.x, + v2.positionScreen.y + ); + grad.addColorStop( 0, colorStyle1 ); + grad.addColorStop( 1, colorStyle2 ); + + } catch ( exception ) { + + grad = colorStyle1; + + } + + setStrokeStyle( grad ); + + } + + } + + _context.stroke(); + _elemBox.expandByScalar( material.linewidth * 2 ); + + } else if ( material instanceof THREE.LineDashedMaterial ) { + + setLineWidth( material.linewidth ); + setLineCap( material.linecap ); + setLineJoin( material.linejoin ); + setStrokeStyle( material.color.getStyle() ); + setLineDash( [ material.dashSize, material.gapSize ] ); + + _context.stroke(); + + _elemBox.expandByScalar( material.linewidth * 2 ); + + setLineDash( [] ); + + } + + } + + function renderFace3( v1, v2, v3, uv1, uv2, uv3, element, material ) { + + _this.info.render.vertices += 3; + _this.info.render.faces ++; + + setOpacity( material.opacity ); + setBlending( material.blending ); + + _v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y; + _v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y; + _v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y; + + drawTriangle( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y ); + + if ( ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) && material.map === null ) { + + _diffuseColor.copy( material.color ); + _emissiveColor.copy( material.emissive ); + + if ( material.vertexColors === THREE.FaceColors ) { + + _diffuseColor.multiply( element.color ); + + } + + _color.copy( _ambientLight ); + + _centroid.copy( v1.positionWorld ).add( v2.positionWorld ).add( v3.positionWorld ).divideScalar( 3 ); + + calculateLight( _centroid, element.normalModel, _color ); + + _color.multiply( _diffuseColor ).add( _emissiveColor ); + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } else if ( material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshPhongMaterial ) { + + if ( material.map !== null ) { + + var mapping = material.map.mapping; + + if ( mapping === THREE.UVMapping ) { + + _uvs = element.uvs; + patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uvs[ uv1 ].x, _uvs[ uv1 ].y, _uvs[ uv2 ].x, _uvs[ uv2 ].y, _uvs[ uv3 ].x, _uvs[ uv3 ].y, material.map ); + + } + + } else if ( material.envMap !== null ) { + + if ( material.envMap.mapping === THREE.SphericalReflectionMapping ) { + + _normal.copy( element.vertexNormalsModel[ uv1 ] ).applyMatrix3( _normalViewMatrix ); + _uv1x = 0.5 * _normal.x + 0.5; + _uv1y = 0.5 * _normal.y + 0.5; + + _normal.copy( element.vertexNormalsModel[ uv2 ] ).applyMatrix3( _normalViewMatrix ); + _uv2x = 0.5 * _normal.x + 0.5; + _uv2y = 0.5 * _normal.y + 0.5; + + _normal.copy( element.vertexNormalsModel[ uv3 ] ).applyMatrix3( _normalViewMatrix ); + _uv3x = 0.5 * _normal.x + 0.5; + _uv3y = 0.5 * _normal.y + 0.5; + + patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap ); + + } + + } else { + + _color.copy( material.color ); + + if ( material.vertexColors === THREE.FaceColors ) { + + _color.multiply( element.color ); + + } + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + _normal.copy( element.normalModel ).applyMatrix3( _normalViewMatrix ); + + _color.setRGB( _normal.x, _normal.y, _normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } else { + + _color.setRGB( 1, 1, 1 ); + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } + + } + + // + + function drawTriangle( x0, y0, x1, y1, x2, y2 ) { + + _context.beginPath(); + _context.moveTo( x0, y0 ); + _context.lineTo( x1, y1 ); + _context.lineTo( x2, y2 ); + _context.closePath(); + + } + + function strokePath( color, linewidth, linecap, linejoin ) { + + setLineWidth( linewidth ); + setLineCap( linecap ); + setLineJoin( linejoin ); + setStrokeStyle( color.getStyle() ); + + _context.stroke(); + + _elemBox.expandByScalar( linewidth * 2 ); + + } + + function fillPath( color ) { + + setFillStyle( color.getStyle() ); + _context.fill(); + + } + + function textureToPattern( texture ) { + + if ( texture.version === 0 || + texture instanceof THREE.CompressedTexture || + texture instanceof THREE.DataTexture ) { + + return { + canvas: undefined, + version: texture.version + } + + } + + var image = texture.image; + + var canvas = document.createElement( 'canvas' ); + canvas.width = image.width; + canvas.height = image.height; + + var context = canvas.getContext( '2d' ); + context.setTransform( 1, 0, 0, - 1, 0, image.height ); + context.drawImage( image, 0, 0 ); + + var repeatX = texture.wrapS === THREE.RepeatWrapping; + var repeatY = texture.wrapT === THREE.RepeatWrapping; + + var repeat = 'no-repeat'; + + if ( repeatX === true && repeatY === true ) { + + repeat = 'repeat'; + + } else if ( repeatX === true ) { + + repeat = 'repeat-x'; + + } else if ( repeatY === true ) { + + repeat = 'repeat-y'; + + } + + return { + canvas: _context.createPattern( canvas, repeat ), + version: texture.version + } + + } + + function patternPath( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, texture ) { + + var pattern = _patterns[ texture.id ]; + + if ( pattern === undefined || pattern.version !== texture.version ) { + + pattern = textureToPattern( texture ); + _patterns[ texture.id ] = pattern; + + } + + if ( pattern.canvas !== undefined ) { + + setFillStyle( pattern.canvas ); + + } else { + + setFillStyle( 'rgba( 0, 0, 0, 1)' ); + _context.fill(); + return; + + } + + // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 + + var a, b, c, d, e, f, det, idet, + offsetX = texture.offset.x / texture.repeat.x, + offsetY = texture.offset.y / texture.repeat.y, + width = texture.image.width * texture.repeat.x, + height = texture.image.height * texture.repeat.y; + + u0 = ( u0 + offsetX ) * width; + v0 = ( v0 + offsetY ) * height; + + u1 = ( u1 + offsetX ) * width; + v1 = ( v1 + offsetY ) * height; + + u2 = ( u2 + offsetX ) * width; + v2 = ( v2 + offsetY ) * height; + + x1 -= x0; y1 -= y0; + x2 -= x0; y2 -= y0; + + u1 -= u0; v1 -= v0; + u2 -= u0; v2 -= v0; + + det = u1 * v2 - u2 * v1; + + if ( det === 0 ) return; + + idet = 1 / det; + + a = ( v2 * x1 - v1 * x2 ) * idet; + b = ( v2 * y1 - v1 * y2 ) * idet; + c = ( u1 * x2 - u2 * x1 ) * idet; + d = ( u1 * y2 - u2 * y1 ) * idet; + + e = x0 - a * u0 - c * v0; + f = y0 - b * u0 - d * v0; + + _context.save(); + _context.transform( a, b, c, d, e, f ); + _context.fill(); + _context.restore(); + + } + + function clipImage( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, image ) { + + // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 + + var a, b, c, d, e, f, det, idet, + width = image.width - 1, + height = image.height - 1; + + u0 *= width; v0 *= height; + u1 *= width; v1 *= height; + u2 *= width; v2 *= height; + + x1 -= x0; y1 -= y0; + x2 -= x0; y2 -= y0; + + u1 -= u0; v1 -= v0; + u2 -= u0; v2 -= v0; + + det = u1 * v2 - u2 * v1; + + idet = 1 / det; + + a = ( v2 * x1 - v1 * x2 ) * idet; + b = ( v2 * y1 - v1 * y2 ) * idet; + c = ( u1 * x2 - u2 * x1 ) * idet; + d = ( u1 * y2 - u2 * y1 ) * idet; + + e = x0 - a * u0 - c * v0; + f = y0 - b * u0 - d * v0; + + _context.save(); + _context.transform( a, b, c, d, e, f ); + _context.clip(); + _context.drawImage( image, 0, 0 ); + _context.restore(); + + } + + // Hide anti-alias gaps + + function expand( v1, v2, pixels ) { + + var x = v2.x - v1.x, y = v2.y - v1.y, + det = x * x + y * y, idet; + + if ( det === 0 ) return; + + idet = pixels / Math.sqrt( det ); + + x *= idet; y *= idet; + + v2.x += x; v2.y += y; + v1.x -= x; v1.y -= y; + + } + + // Context cached methods. + + function setOpacity( value ) { + + if ( _contextGlobalAlpha !== value ) { + + _context.globalAlpha = value; + _contextGlobalAlpha = value; + + } + + } + + function setBlending( value ) { + + if ( _contextGlobalCompositeOperation !== value ) { + + if ( value === THREE.NormalBlending ) { + + _context.globalCompositeOperation = 'source-over'; + + } else if ( value === THREE.AdditiveBlending ) { + + _context.globalCompositeOperation = 'lighter'; + + } else if ( value === THREE.SubtractiveBlending ) { + + _context.globalCompositeOperation = 'darker'; + + } + + _contextGlobalCompositeOperation = value; + + } + + } + + function setLineWidth( value ) { + + if ( _contextLineWidth !== value ) { + + _context.lineWidth = value; + _contextLineWidth = value; + + } + + } + + function setLineCap( value ) { + + // "butt", "round", "square" + + if ( _contextLineCap !== value ) { + + _context.lineCap = value; + _contextLineCap = value; + + } + + } + + function setLineJoin( value ) { + + // "round", "bevel", "miter" + + if ( _contextLineJoin !== value ) { + + _context.lineJoin = value; + _contextLineJoin = value; + + } + + } + + function setStrokeStyle( value ) { + + if ( _contextStrokeStyle !== value ) { + + _context.strokeStyle = value; + _contextStrokeStyle = value; + + } + + } + + function setFillStyle( value ) { + + if ( _contextFillStyle !== value ) { + + _context.fillStyle = value; + _contextFillStyle = value; + + } + + } + + function setLineDash( value ) { + + if ( _contextLineDash.length !== value.length ) { + + _context.setLineDash( value ); + _contextLineDash = value; + + } + + } + +}; diff --git a/node_modules/three/examples/js/renderers/Projector.js b/node_modules/three/examples/js/renderers/Projector.js new file mode 100644 index 00000000..aeaaa768 --- /dev/null +++ b/node_modules/three/examples/js/renderers/Projector.js @@ -0,0 +1,921 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author julianwa / https://github.com/julianwa + */ + +THREE.RenderableObject = function () { + + this.id = 0; + + this.object = null; + this.z = 0; + this.renderOrder = 0; + +}; + +// + +THREE.RenderableFace = function () { + + this.id = 0; + + this.v1 = new THREE.RenderableVertex(); + this.v2 = new THREE.RenderableVertex(); + this.v3 = new THREE.RenderableVertex(); + + this.normalModel = new THREE.Vector3(); + + this.vertexNormalsModel = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; + this.vertexNormalsLength = 0; + + this.color = new THREE.Color(); + this.material = null; + this.uvs = [ new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ]; + + this.z = 0; + this.renderOrder = 0; + +}; + +// + +THREE.RenderableVertex = function () { + + this.position = new THREE.Vector3(); + this.positionWorld = new THREE.Vector3(); + this.positionScreen = new THREE.Vector4(); + + this.visible = true; + +}; + +THREE.RenderableVertex.prototype.copy = function ( vertex ) { + + this.positionWorld.copy( vertex.positionWorld ); + this.positionScreen.copy( vertex.positionScreen ); + +}; + +// + +THREE.RenderableLine = function () { + + this.id = 0; + + this.v1 = new THREE.RenderableVertex(); + this.v2 = new THREE.RenderableVertex(); + + this.vertexColors = [ new THREE.Color(), new THREE.Color() ]; + this.material = null; + + this.z = 0; + this.renderOrder = 0; + +}; + +// + +THREE.RenderableSprite = function () { + + this.id = 0; + + this.object = null; + + this.x = 0; + this.y = 0; + this.z = 0; + + this.rotation = 0; + this.scale = new THREE.Vector2(); + + this.material = null; + this.renderOrder = 0; + +}; + +// + +THREE.Projector = function () { + + var _object, _objectCount, _objectPool = [], _objectPoolLength = 0, + _vertex, _vertexCount, _vertexPool = [], _vertexPoolLength = 0, + _face, _faceCount, _facePool = [], _facePoolLength = 0, + _line, _lineCount, _linePool = [], _linePoolLength = 0, + _sprite, _spriteCount, _spritePool = [], _spritePoolLength = 0, + + _renderData = { objects: [], lights: [], elements: [] }, + + _vector3 = new THREE.Vector3(), + _vector4 = new THREE.Vector4(), + + _clipBox = new THREE.Box3( new THREE.Vector3( - 1, - 1, - 1 ), new THREE.Vector3( 1, 1, 1 ) ), + _boundingBox = new THREE.Box3(), + _points3 = new Array( 3 ), + _points4 = new Array( 4 ), + + _viewMatrix = new THREE.Matrix4(), + _viewProjectionMatrix = new THREE.Matrix4(), + + _modelMatrix, + _modelViewProjectionMatrix = new THREE.Matrix4(), + + _normalMatrix = new THREE.Matrix3(), + + _frustum = new THREE.Frustum(), + + _clippedVertex1PositionScreen = new THREE.Vector4(), + _clippedVertex2PositionScreen = new THREE.Vector4(); + + // + + this.projectVector = function ( vector, camera ) { + + console.warn( 'THREE.Projector: .projectVector() is now vector.project().' ); + vector.project( camera ); + + }; + + this.unprojectVector = function ( vector, camera ) { + + console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' ); + vector.unproject( camera ); + + }; + + this.pickingRay = function ( vector, camera ) { + + console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' ); + + }; + + // + + var RenderList = function () { + + var normals = []; + var uvs = []; + + var object = null; + var material = null; + + var normalMatrix = new THREE.Matrix3(); + + var setObject = function ( value ) { + + object = value; + material = object.material; + + normalMatrix.getNormalMatrix( object.matrixWorld ); + + normals.length = 0; + uvs.length = 0; + + }; + + var projectVertex = function ( vertex ) { + + var position = vertex.position; + var positionWorld = vertex.positionWorld; + var positionScreen = vertex.positionScreen; + + positionWorld.copy( position ).applyMatrix4( _modelMatrix ); + positionScreen.copy( positionWorld ).applyMatrix4( _viewProjectionMatrix ); + + var invW = 1 / positionScreen.w; + + positionScreen.x *= invW; + positionScreen.y *= invW; + positionScreen.z *= invW; + + vertex.visible = positionScreen.x >= - 1 && positionScreen.x <= 1 && + positionScreen.y >= - 1 && positionScreen.y <= 1 && + positionScreen.z >= - 1 && positionScreen.z <= 1; + + }; + + var pushVertex = function ( x, y, z ) { + + _vertex = getNextVertexInPool(); + _vertex.position.set( x, y, z ); + + projectVertex( _vertex ); + + }; + + var pushNormal = function ( x, y, z ) { + + normals.push( x, y, z ); + + }; + + var pushUv = function ( x, y ) { + + uvs.push( x, y ); + + }; + + var checkTriangleVisibility = function ( v1, v2, v3 ) { + + if ( v1.visible === true || v2.visible === true || v3.visible === true ) return true; + + _points3[ 0 ] = v1.positionScreen; + _points3[ 1 ] = v2.positionScreen; + _points3[ 2 ] = v3.positionScreen; + + return _clipBox.isIntersectionBox( _boundingBox.setFromPoints( _points3 ) ); + + }; + + var checkBackfaceCulling = function ( v1, v2, v3 ) { + + return ( ( v3.positionScreen.x - v1.positionScreen.x ) * + ( v2.positionScreen.y - v1.positionScreen.y ) - + ( v3.positionScreen.y - v1.positionScreen.y ) * + ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0; + + }; + + var pushLine = function ( a, b ) { + + var v1 = _vertexPool[ a ]; + var v2 = _vertexPool[ b ]; + + _line = getNextLineInPool(); + + _line.id = object.id; + _line.v1.copy( v1 ); + _line.v2.copy( v2 ); + _line.z = ( v1.positionScreen.z + v2.positionScreen.z ) / 2; + _line.renderOrder = object.renderOrder; + + _line.material = object.material; + + _renderData.elements.push( _line ); + + }; + + var pushTriangle = function ( a, b, c ) { + + var v1 = _vertexPool[ a ]; + var v2 = _vertexPool[ b ]; + var v3 = _vertexPool[ c ]; + + if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return; + + if ( material.side === THREE.DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) { + + _face = getNextFaceInPool(); + + _face.id = object.id; + _face.v1.copy( v1 ); + _face.v2.copy( v2 ); + _face.v3.copy( v3 ); + _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; + _face.renderOrder = object.renderOrder; + + // use first vertex normal as face normal + + _face.normalModel.fromArray( normals, a * 3 ); + _face.normalModel.applyMatrix3( normalMatrix ).normalize(); + + for ( var i = 0; i < 3; i ++ ) { + + var normal = _face.vertexNormalsModel[ i ]; + normal.fromArray( normals, arguments[ i ] * 3 ); + normal.applyMatrix3( normalMatrix ).normalize(); + + var uv = _face.uvs[ i ]; + uv.fromArray( uvs, arguments[ i ] * 2 ); + + } + + _face.vertexNormalsLength = 3; + + _face.material = object.material; + + _renderData.elements.push( _face ); + + } + + }; + + return { + setObject: setObject, + projectVertex: projectVertex, + checkTriangleVisibility: checkTriangleVisibility, + checkBackfaceCulling: checkBackfaceCulling, + pushVertex: pushVertex, + pushNormal: pushNormal, + pushUv: pushUv, + pushLine: pushLine, + pushTriangle: pushTriangle + } + + }; + + var renderList = new RenderList(); + + this.projectScene = function ( scene, camera, sortObjects, sortElements ) { + + _faceCount = 0; + _lineCount = 0; + _spriteCount = 0; + + _renderData.elements.length = 0; + + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + if ( camera.parent === null ) camera.updateMatrixWorld(); + + _viewMatrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) ); + _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); + + _frustum.setFromMatrix( _viewProjectionMatrix ); + + // + + _objectCount = 0; + + _renderData.objects.length = 0; + _renderData.lights.length = 0; + + scene.traverseVisible( function ( object ) { + + if ( object instanceof THREE.Light ) { + + _renderData.lights.push( object ); + + } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Sprite ) { + + var material = object.material; + + if ( material.visible === false ) return; + + if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) { + + _object = getNextObjectInPool(); + _object.id = object.id; + _object.object = object; + + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyProjection( _viewProjectionMatrix ); + _object.z = _vector3.z; + _object.renderOrder = object.renderOrder; + + _renderData.objects.push( _object ); + + } + + } + + } ); + + if ( sortObjects === true ) { + + _renderData.objects.sort( painterSort ); + + } + + // + + for ( var o = 0, ol = _renderData.objects.length; o < ol; o ++ ) { + + var object = _renderData.objects[ o ].object; + var geometry = object.geometry; + + renderList.setObject( object ); + + _modelMatrix = object.matrixWorld; + + _vertexCount = 0; + + if ( object instanceof THREE.Mesh ) { + + if ( geometry instanceof THREE.BufferGeometry ) { + + var attributes = geometry.attributes; + var groups = geometry.groups; + + if ( attributes.position === undefined ) continue; + + var positions = attributes.position.array; + + for ( var i = 0, l = positions.length; i < l; i += 3 ) { + + renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + + } + + if ( attributes.normal !== undefined ) { + + var normals = attributes.normal.array; + + for ( var i = 0, l = normals.length; i < l; i += 3 ) { + + renderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ); + + } + + } + + if ( attributes.uv !== undefined ) { + + var uvs = attributes.uv.array; + + for ( var i = 0, l = uvs.length; i < l; i += 2 ) { + + renderList.pushUv( uvs[ i ], uvs[ i + 1 ] ); + + } + + } + + if ( geometry.index !== null ) { + + var indices = geometry.index.array; + + if ( groups.length > 0 ) { + + for ( var o = 0; o < groups.length; o ++ ) { + + var group = groups[ o ]; + + for ( var i = group.start, l = group.start + group.count; i < l; i += 3 ) { + + renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); + + } + + } + + } else { + + for ( var i = 0, l = indices.length; i < l; i += 3 ) { + + renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); + + } + + } + + } else { + + for ( var i = 0, l = positions.length / 3; i < l; i += 3 ) { + + renderList.pushTriangle( i, i + 1, i + 2 ); + + } + + } + + } else if ( geometry instanceof THREE.Geometry ) { + + var vertices = geometry.vertices; + var faces = geometry.faces; + var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; + + _normalMatrix.getNormalMatrix( _modelMatrix ); + + var material = object.material; + + var isFaceMaterial = material instanceof THREE.MeshFaceMaterial; + var objectMaterials = isFaceMaterial === true ? object.material : null; + + for ( var v = 0, vl = vertices.length; v < vl; v ++ ) { + + var vertex = vertices[ v ]; + + _vector3.copy( vertex ); + + if ( material.morphTargets === true ) { + + var morphTargets = geometry.morphTargets; + var morphInfluences = object.morphTargetInfluences; + + for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { + + var influence = morphInfluences[ t ]; + + if ( influence === 0 ) continue; + + var target = morphTargets[ t ]; + var targetVertex = target.vertices[ v ]; + + _vector3.x += ( targetVertex.x - vertex.x ) * influence; + _vector3.y += ( targetVertex.y - vertex.y ) * influence; + _vector3.z += ( targetVertex.z - vertex.z ) * influence; + + } + + } + + renderList.pushVertex( _vector3.x, _vector3.y, _vector3.z ); + + } + + for ( var f = 0, fl = faces.length; f < fl; f ++ ) { + + var face = faces[ f ]; + + material = isFaceMaterial === true + ? objectMaterials.materials[ face.materialIndex ] + : object.material; + + if ( material === undefined ) continue; + + var side = material.side; + + var v1 = _vertexPool[ face.a ]; + var v2 = _vertexPool[ face.b ]; + var v3 = _vertexPool[ face.c ]; + + if ( renderList.checkTriangleVisibility( v1, v2, v3 ) === false ) continue; + + var visible = renderList.checkBackfaceCulling( v1, v2, v3 ); + + if ( side !== THREE.DoubleSide ) { + + if ( side === THREE.FrontSide && visible === false ) continue; + if ( side === THREE.BackSide && visible === true ) continue; + + } + + _face = getNextFaceInPool(); + + _face.id = object.id; + _face.v1.copy( v1 ); + _face.v2.copy( v2 ); + _face.v3.copy( v3 ); + + _face.normalModel.copy( face.normal ); + + if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) { + + _face.normalModel.negate(); + + } + + _face.normalModel.applyMatrix3( _normalMatrix ).normalize(); + + var faceVertexNormals = face.vertexNormals; + + for ( var n = 0, nl = Math.min( faceVertexNormals.length, 3 ); n < nl; n ++ ) { + + var normalModel = _face.vertexNormalsModel[ n ]; + normalModel.copy( faceVertexNormals[ n ] ); + + if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) { + + normalModel.negate(); + + } + + normalModel.applyMatrix3( _normalMatrix ).normalize(); + + } + + _face.vertexNormalsLength = faceVertexNormals.length; + + var vertexUvs = faceVertexUvs[ f ]; + + if ( vertexUvs !== undefined ) { + + for ( var u = 0; u < 3; u ++ ) { + + _face.uvs[ u ].copy( vertexUvs[ u ] ); + + } + + } + + _face.color = face.color; + _face.material = material; + + _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; + _face.renderOrder = object.renderOrder; + + _renderData.elements.push( _face ); + + } + + } + + } else if ( object instanceof THREE.Line ) { + + if ( geometry instanceof THREE.BufferGeometry ) { + + var attributes = geometry.attributes; + + if ( attributes.position !== undefined ) { + + var positions = attributes.position.array; + + for ( var i = 0, l = positions.length; i < l; i += 3 ) { + + renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + + } + + if ( geometry.index !== null ) { + + var indices = geometry.index.array; + + for ( var i = 0, l = indices.length; i < l; i += 2 ) { + + renderList.pushLine( indices[ i ], indices[ i + 1 ] ); + + } + + } else { + + var step = object instanceof THREE.LineSegments ? 2 : 1; + + for ( var i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) { + + renderList.pushLine( i, i + 1 ); + + } + + } + + } + + } else if ( geometry instanceof THREE.Geometry ) { + + _modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix ); + + var vertices = object.geometry.vertices; + + if ( vertices.length === 0 ) continue; + + v1 = getNextVertexInPool(); + v1.positionScreen.copy( vertices[ 0 ] ).applyMatrix4( _modelViewProjectionMatrix ); + + var step = object instanceof THREE.LineSegments ? 2 : 1; + + for ( var v = 1, vl = vertices.length; v < vl; v ++ ) { + + v1 = getNextVertexInPool(); + v1.positionScreen.copy( vertices[ v ] ).applyMatrix4( _modelViewProjectionMatrix ); + + if ( ( v + 1 ) % step > 0 ) continue; + + v2 = _vertexPool[ _vertexCount - 2 ]; + + _clippedVertex1PositionScreen.copy( v1.positionScreen ); + _clippedVertex2PositionScreen.copy( v2.positionScreen ); + + if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) === true ) { + + // Perform the perspective divide + _clippedVertex1PositionScreen.multiplyScalar( 1 / _clippedVertex1PositionScreen.w ); + _clippedVertex2PositionScreen.multiplyScalar( 1 / _clippedVertex2PositionScreen.w ); + + _line = getNextLineInPool(); + + _line.id = object.id; + _line.v1.positionScreen.copy( _clippedVertex1PositionScreen ); + _line.v2.positionScreen.copy( _clippedVertex2PositionScreen ); + + _line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z ); + _line.renderOrder = object.renderOrder; + + _line.material = object.material; + + if ( object.material.vertexColors === THREE.VertexColors ) { + + _line.vertexColors[ 0 ].copy( object.geometry.colors[ v ] ); + _line.vertexColors[ 1 ].copy( object.geometry.colors[ v - 1 ] ); + + } + + _renderData.elements.push( _line ); + + } + + } + + } + + } else if ( object instanceof THREE.Sprite ) { + + _vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 ); + _vector4.applyMatrix4( _viewProjectionMatrix ); + + var invW = 1 / _vector4.w; + + _vector4.z *= invW; + + if ( _vector4.z >= - 1 && _vector4.z <= 1 ) { + + _sprite = getNextSpriteInPool(); + _sprite.id = object.id; + _sprite.x = _vector4.x * invW; + _sprite.y = _vector4.y * invW; + _sprite.z = _vector4.z; + _sprite.renderOrder = object.renderOrder; + _sprite.object = object; + + _sprite.rotation = object.rotation; + + _sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[ 0 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 12 ] ) ); + _sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[ 5 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 13 ] ) ); + + _sprite.material = object.material; + + _renderData.elements.push( _sprite ); + + } + + } + + } + + if ( sortElements === true ) { + + _renderData.elements.sort( painterSort ); + + } + + return _renderData; + + }; + + // Pools + + function getNextObjectInPool() { + + if ( _objectCount === _objectPoolLength ) { + + var object = new THREE.RenderableObject(); + _objectPool.push( object ); + _objectPoolLength ++; + _objectCount ++; + return object; + + } + + return _objectPool[ _objectCount ++ ]; + + } + + function getNextVertexInPool() { + + if ( _vertexCount === _vertexPoolLength ) { + + var vertex = new THREE.RenderableVertex(); + _vertexPool.push( vertex ); + _vertexPoolLength ++; + _vertexCount ++; + return vertex; + + } + + return _vertexPool[ _vertexCount ++ ]; + + } + + function getNextFaceInPool() { + + if ( _faceCount === _facePoolLength ) { + + var face = new THREE.RenderableFace(); + _facePool.push( face ); + _facePoolLength ++; + _faceCount ++; + return face; + + } + + return _facePool[ _faceCount ++ ]; + + + } + + function getNextLineInPool() { + + if ( _lineCount === _linePoolLength ) { + + var line = new THREE.RenderableLine(); + _linePool.push( line ); + _linePoolLength ++; + _lineCount ++; + return line; + + } + + return _linePool[ _lineCount ++ ]; + + } + + function getNextSpriteInPool() { + + if ( _spriteCount === _spritePoolLength ) { + + var sprite = new THREE.RenderableSprite(); + _spritePool.push( sprite ); + _spritePoolLength ++; + _spriteCount ++; + return sprite; + + } + + return _spritePool[ _spriteCount ++ ]; + + } + + // + + function painterSort( a, b ) { + + if ( a.renderOrder !== b.renderOrder ) { + + return a.renderOrder - b.renderOrder; + + } else if ( a.z !== b.z ) { + + return b.z - a.z; + + } else if ( a.id !== b.id ) { + + return a.id - b.id; + + } else { + + return 0; + + } + + } + + function clipLine( s1, s2 ) { + + var alpha1 = 0, alpha2 = 1, + + // Calculate the boundary coordinate of each vertex for the near and far clip planes, + // Z = -1 and Z = +1, respectively. + bc1near = s1.z + s1.w, + bc2near = s2.z + s2.w, + bc1far = - s1.z + s1.w, + bc2far = - s2.z + s2.w; + + if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) { + + // Both vertices lie entirely within all clip planes. + return true; + + } else if ( ( bc1near < 0 && bc2near < 0 ) || ( bc1far < 0 && bc2far < 0 ) ) { + + // Both vertices lie entirely outside one of the clip planes. + return false; + + } else { + + // The line segment spans at least one clip plane. + + if ( bc1near < 0 ) { + + // v1 lies outside the near plane, v2 inside + alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) ); + + } else if ( bc2near < 0 ) { + + // v2 lies outside the near plane, v1 inside + alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) ); + + } + + if ( bc1far < 0 ) { + + // v1 lies outside the far plane, v2 inside + alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) ); + + } else if ( bc2far < 0 ) { + + // v2 lies outside the far plane, v2 inside + alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) ); + + } + + if ( alpha2 < alpha1 ) { + + // The line segment spans two boundaries, but is outside both of them. + // (This can't happen when we're only clipping against just near/far but good + // to leave the check here for future usage if other clip planes are added.) + return false; + + } else { + + // Update the s1 and s2 vertices to match the clipped line segment. + s1.lerp( s2, alpha1 ); + s2.lerp( s1, 1 - alpha2 ); + + return true; + + } + + } + + } + +}; diff --git a/node_modules/three/examples/js/renderers/RaytracingRenderer.js b/node_modules/three/examples/js/renderers/RaytracingRenderer.js new file mode 100644 index 00000000..065c5287 --- /dev/null +++ b/node_modules/three/examples/js/renderers/RaytracingRenderer.js @@ -0,0 +1,546 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.RaytracingRenderer = function ( parameters ) { + + console.log( 'THREE.RaytracingRenderer', THREE.REVISION ); + + parameters = parameters || {}; + + var scope = this; + + var canvas = document.createElement( 'canvas' ); + var context = canvas.getContext( '2d', { + alpha: parameters.alpha === true + } ); + + var maxRecursionDepth = 3; + + var canvasWidth, canvasHeight; + var canvasWidthHalf, canvasHeightHalf; + + var clearColor = new THREE.Color( 0x000000 ); + + var origin = new THREE.Vector3(); + var direction = new THREE.Vector3(); + + var cameraPosition = new THREE.Vector3(); + + var raycaster = new THREE.Raycaster( origin, direction ); + var raycasterLight = new THREE.Raycaster(); + + var perspective; + var modelViewMatrix = new THREE.Matrix4(); + var cameraNormalMatrix = new THREE.Matrix3(); + + var objects; + var lights = []; + var cache = {}; + + var animationFrameId = null; + + this.domElement = canvas; + + this.autoClear = true; + + this.setClearColor = function ( color, alpha ) { + + clearColor.set( color ); + + }; + + this.setPixelRatio = function () {}; + + this.setSize = function ( width, height ) { + + canvas.width = width; + canvas.height = height; + + canvasWidth = canvas.width; + canvasHeight = canvas.height; + + canvasWidthHalf = Math.floor( canvasWidth / 2 ); + canvasHeightHalf = Math.floor( canvasHeight / 2 ); + + context.fillStyle = 'white'; + + }; + + this.setSize( canvas.width, canvas.height ); + + this.clear = function () { + + }; + + // + + var spawnRay = ( function () { + + var diffuseColor = new THREE.Color(); + var specularColor = new THREE.Color(); + var lightColor = new THREE.Color(); + var schlick = new THREE.Color(); + + var lightContribution = new THREE.Color(); + + var eyeVector = new THREE.Vector3(); + var lightVector = new THREE.Vector3(); + var normalVector = new THREE.Vector3(); + var halfVector = new THREE.Vector3(); + + var localPoint = new THREE.Vector3(); + var reflectionVector = new THREE.Vector3(); + + var tmpVec = new THREE.Vector3(); + + var tmpColor = []; + + for ( var i = 0; i < maxRecursionDepth; i ++ ) { + + tmpColor[ i ] = new THREE.Color(); + + } + + return function spawnRay( rayOrigin, rayDirection, outputColor, recursionDepth ) { + + var ray = raycaster.ray; + + ray.origin = rayOrigin; + ray.direction = rayDirection; + + // + + var rayLight = raycasterLight.ray; + + // + + outputColor.setRGB( 0, 0, 0 ); + + // + + var intersections = raycaster.intersectObjects( objects, true ); + + // ray didn't find anything + // (here should come setting of background color?) + + if ( intersections.length === 0 ) { + + return; + + } + + // ray hit + + var intersection = intersections[ 0 ]; + + var point = intersection.point; + var object = intersection.object; + var material = object.material; + var face = intersection.face; + + var vertices = object.geometry.vertices; + + // + + var _object = cache[ object.id ]; + + localPoint.copy( point ).applyMatrix4( _object.inverseMatrix ); + eyeVector.subVectors( raycaster.ray.origin, point ).normalize(); + + // resolve pixel diffuse color + + if ( material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshBasicMaterial ) { + + diffuseColor.copyGammaToLinear( material.color ); + + } else { + + diffuseColor.setRGB( 1, 1, 1 ); + + } + + if ( material.vertexColors === THREE.FaceColors ) { + + diffuseColor.multiply( face.color ); + + } + + // compute light shading + + rayLight.origin.copy( point ); + + if ( material instanceof THREE.MeshBasicMaterial ) { + + for ( var i = 0, l = lights.length; i < l; i ++ ) { + + var light = lights[ i ]; + + lightVector.setFromMatrixPosition( light.matrixWorld ); + lightVector.sub( point ); + + rayLight.direction.copy( lightVector ).normalize(); + + var intersections = raycasterLight.intersectObjects( objects, true ); + + // point in shadow + + if ( intersections.length > 0 ) continue; + + // point visible + + outputColor.add( diffuseColor ); + + } + + } else if ( material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshPhongMaterial ) { + + var normalComputed = false; + + for ( var i = 0, l = lights.length; i < l; i ++ ) { + + var light = lights[ i ]; + + lightColor.copyGammaToLinear( light.color ); + + lightVector.setFromMatrixPosition( light.matrixWorld ); + lightVector.sub( point ); + + rayLight.direction.copy( lightVector ).normalize(); + + var intersections = raycasterLight.intersectObjects( objects, true ); + + // point in shadow + + if ( intersections.length > 0 ) continue; + + // point lit + + if ( normalComputed === false ) { + + // the same normal can be reused for all lights + // (should be possible to cache even more) + + computePixelNormal( normalVector, localPoint, material.shading, face, vertices ); + normalVector.applyMatrix3( _object.normalMatrix ).normalize(); + + normalComputed = true; + + } + + // compute attenuation + + var attenuation = 1.0; + + if ( light.physicalAttenuation === true ) { + + attenuation = lightVector.length(); + attenuation = 1.0 / ( attenuation * attenuation ); + + } + + lightVector.normalize(); + + // compute diffuse + + var dot = Math.max( normalVector.dot( lightVector ), 0 ); + var diffuseIntensity = dot * light.intensity; + + lightContribution.copy( diffuseColor ); + lightContribution.multiply( lightColor ); + lightContribution.multiplyScalar( diffuseIntensity * attenuation ); + + outputColor.add( lightContribution ); + + // compute specular + + if ( material instanceof THREE.MeshPhongMaterial ) { + + halfVector.addVectors( lightVector, eyeVector ).normalize(); + + var dotNormalHalf = Math.max( normalVector.dot( halfVector ), 0.0 ); + var specularIntensity = Math.max( Math.pow( dotNormalHalf, material.shininess ), 0.0 ) * diffuseIntensity; + + var specularNormalization = ( material.shininess + 2.0 ) / 8.0; + + specularColor.copyGammaToLinear( material.specular ); + + var alpha = Math.pow( Math.max( 1.0 - lightVector.dot( halfVector ), 0.0 ), 5.0 ); + + schlick.r = specularColor.r + ( 1.0 - specularColor.r ) * alpha; + schlick.g = specularColor.g + ( 1.0 - specularColor.g ) * alpha; + schlick.b = specularColor.b + ( 1.0 - specularColor.b ) * alpha; + + lightContribution.copy( schlick ); + + lightContribution.multiply( lightColor ); + lightContribution.multiplyScalar( specularNormalization * specularIntensity * attenuation ); + outputColor.add( lightContribution ); + + } + + } + + } + + // reflection / refraction + + var reflectivity = material.reflectivity; + + if ( ( material.mirror || material.glass ) && reflectivity > 0 && recursionDepth < maxRecursionDepth ) { + + if ( material.mirror ) { + + reflectionVector.copy( rayDirection ); + reflectionVector.reflect( normalVector ); + + } else if ( material.glass ) { + + var eta = material.refractionRatio; + + var dotNI = rayDirection.dot( normalVector ); + var k = 1.0 - eta * eta * ( 1.0 - dotNI * dotNI ); + + if ( k < 0.0 ) { + + reflectionVector.set( 0, 0, 0 ); + + } else { + + reflectionVector.copy( rayDirection ); + reflectionVector.multiplyScalar( eta ); + + var alpha = eta * dotNI + Math.sqrt( k ); + tmpVec.copy( normalVector ); + tmpVec.multiplyScalar( alpha ); + reflectionVector.sub( tmpVec ); + + } + + } + + var theta = Math.max( eyeVector.dot( normalVector ), 0.0 ); + var rf0 = reflectivity; + var fresnel = rf0 + ( 1.0 - rf0 ) * Math.pow( ( 1.0 - theta ), 5.0 ); + + var weight = fresnel; + + var zColor = tmpColor[ recursionDepth ]; + + spawnRay( point, reflectionVector, zColor, recursionDepth + 1 ); + + if ( material.specular !== undefined ) { + + zColor.multiply( material.specular ); + + } + + zColor.multiplyScalar( weight ); + outputColor.multiplyScalar( 1 - weight ); + outputColor.add( zColor ); + + } + + }; + + }() ); + + var computePixelNormal = ( function () { + + var tmpVec1 = new THREE.Vector3(); + var tmpVec2 = new THREE.Vector3(); + var tmpVec3 = new THREE.Vector3(); + + return function computePixelNormal( outputVector, point, shading, face, vertices ) { + + var faceNormal = face.normal; + var vertexNormals = face.vertexNormals; + + if ( shading === THREE.FlatShading ) { + + outputVector.copy( faceNormal ); + + } else if ( shading === THREE.SmoothShading ) { + + // compute barycentric coordinates + + var vA = vertices[ face.a ]; + var vB = vertices[ face.b ]; + var vC = vertices[ face.c ]; + + tmpVec3.crossVectors( tmpVec1.subVectors( vB, vA ), tmpVec2.subVectors( vC, vA ) ); + var areaABC = faceNormal.dot( tmpVec3 ); + + tmpVec3.crossVectors( tmpVec1.subVectors( vB, point ), tmpVec2.subVectors( vC, point ) ); + var areaPBC = faceNormal.dot( tmpVec3 ); + var a = areaPBC / areaABC; + + tmpVec3.crossVectors( tmpVec1.subVectors( vC, point ), tmpVec2.subVectors( vA, point ) ); + var areaPCA = faceNormal.dot( tmpVec3 ); + var b = areaPCA / areaABC; + + var c = 1.0 - a - b; + + // compute interpolated vertex normal + + tmpVec1.copy( vertexNormals[ 0 ] ); + tmpVec1.multiplyScalar( a ); + + tmpVec2.copy( vertexNormals[ 1 ] ); + tmpVec2.multiplyScalar( b ); + + tmpVec3.copy( vertexNormals[ 2 ] ); + tmpVec3.multiplyScalar( c ); + + outputVector.addVectors( tmpVec1, tmpVec2 ); + outputVector.add( tmpVec3 ); + + } + + }; + + }() ); + + var renderBlock = ( function () { + + var blockSize = 64; + + var canvasBlock = document.createElement( 'canvas' ); + canvasBlock.width = blockSize; + canvasBlock.height = blockSize; + + var contextBlock = canvasBlock.getContext( '2d', { + + alpha: parameters.alpha === true + + } ); + + var imagedata = contextBlock.getImageData( 0, 0, blockSize, blockSize ); + var data = imagedata.data; + + var pixelColor = new THREE.Color(); + + return function renderBlock( blockX, blockY ) { + + var index = 0; + + for ( var y = 0; y < blockSize; y ++ ) { + + for ( var x = 0; x < blockSize; x ++, index += 4 ) { + + // spawn primary ray at pixel position + + origin.copy( cameraPosition ); + + direction.set( x + blockX - canvasWidthHalf, - ( y + blockY - canvasHeightHalf ), - perspective ); + direction.applyMatrix3( cameraNormalMatrix ).normalize(); + + spawnRay( origin, direction, pixelColor, 0 ); + + // convert from linear to gamma + + data[ index ] = Math.sqrt( pixelColor.r ) * 255; + data[ index + 1 ] = Math.sqrt( pixelColor.g ) * 255; + data[ index + 2 ] = Math.sqrt( pixelColor.b ) * 255; + + } + + } + + context.putImageData( imagedata, blockX, blockY ); + + blockX += blockSize; + + if ( blockX >= canvasWidth ) { + + blockX = 0; + blockY += blockSize; + + if ( blockY >= canvasHeight ) { + + scope.dispatchEvent( { type: "complete" } ); + return; + + } + + } + + context.fillRect( blockX, blockY, blockSize, blockSize ); + + animationFrameId = requestAnimationFrame( function () { + + renderBlock( blockX, blockY ); + + } ); + + }; + + }() ); + + this.render = function ( scene, camera ) { + + if ( this.autoClear === true ) this.clear(); + + cancelAnimationFrame( animationFrameId ); + + // update scene graph + + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + + // update camera matrices + + if ( camera.parent === null ) camera.updateMatrixWorld(); + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + cameraPosition.setFromMatrixPosition( camera.matrixWorld ); + + // + + cameraNormalMatrix.getNormalMatrix( camera.matrixWorld ); + origin.copy( cameraPosition ); + + perspective = 0.5 / Math.tan( THREE.Math.degToRad( camera.fov * 0.5 ) ) * canvasHeight; + + objects = scene.children; + + // collect lights and set up object matrices + + lights.length = 0; + + scene.traverse( function ( object ) { + + if ( object instanceof THREE.Light ) { + + lights.push( object ); + + } + + if ( cache[ object.id ] === undefined ) { + + cache[ object.id ] = { + normalMatrix: new THREE.Matrix3(), + inverseMatrix: new THREE.Matrix4() + }; + + } + + modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + + var _object = cache[ object.id ]; + + _object.normalMatrix.getNormalMatrix( modelViewMatrix ); + _object.inverseMatrix.getInverse( object.matrixWorld ); + + } ); + + renderBlock( 0, 0 ); + + }; + +}; + +THREE.EventDispatcher.prototype.apply( THREE.RaytracingRenderer.prototype ); diff --git a/node_modules/three/examples/js/renderers/SVGRenderer.js b/node_modules/three/examples/js/renderers/SVGRenderer.js new file mode 100644 index 00000000..4925cae4 --- /dev/null +++ b/node_modules/three/examples/js/renderers/SVGRenderer.js @@ -0,0 +1,467 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.SVGObject = function ( node ) { + + THREE.Object3D.call( this ); + + this.node = node; + +}; + +THREE.SVGObject.prototype = Object.create( THREE.Object3D.prototype ); +THREE.SVGObject.prototype.constructor = THREE.SVGObject; + +THREE.SVGRenderer = function () { + + console.log( 'THREE.SVGRenderer', THREE.REVISION ); + + var _this = this, + _renderData, _elements, _lights, + _projector = new THREE.Projector(), + _svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ), + _svgWidth, _svgHeight, _svgWidthHalf, _svgHeightHalf, + + _v1, _v2, _v3, _v4, + + _clipBox = new THREE.Box2(), + _elemBox = new THREE.Box2(), + + _color = new THREE.Color(), + _diffuseColor = new THREE.Color(), + _ambientLight = new THREE.Color(), + _directionalLights = new THREE.Color(), + _pointLights = new THREE.Color(), + _clearColor = new THREE.Color(), + _clearAlpha = 1, + + _vector3 = new THREE.Vector3(), // Needed for PointLight + _centroid = new THREE.Vector3(), + _normal = new THREE.Vector3(), + _normalViewMatrix = new THREE.Matrix3(), + + _viewMatrix = new THREE.Matrix4(), + _viewProjectionMatrix = new THREE.Matrix4(), + + _svgPathPool = [], _svgLinePool = [], _svgRectPool = [], + _svgNode, _pathCount = 0, _lineCount = 0, _rectCount = 0, + _quality = 1; + + this.domElement = _svg; + + this.autoClear = true; + this.sortObjects = true; + this.sortElements = true; + + this.info = { + + render: { + + vertices: 0, + faces: 0 + + } + + }; + + this.setQuality = function( quality ) { + + switch ( quality ) { + + case "high": _quality = 1; break; + case "low": _quality = 0; break; + + } + + }; + + // WebGLRenderer compatibility + + this.supportsVertexTextures = function () {}; + this.setFaceCulling = function () {}; + + this.setClearColor = function ( color, alpha ) { + + _clearColor.set( color ); + _clearAlpha = alpha !== undefined ? alpha : 1; + + }; + + this.setPixelRatio = function () {}; + + this.setSize = function( width, height ) { + + _svgWidth = width; _svgHeight = height; + _svgWidthHalf = _svgWidth / 2; _svgHeightHalf = _svgHeight / 2; + + _svg.setAttribute( 'viewBox', ( - _svgWidthHalf ) + ' ' + ( - _svgHeightHalf ) + ' ' + _svgWidth + ' ' + _svgHeight ); + _svg.setAttribute( 'width', _svgWidth ); + _svg.setAttribute( 'height', _svgHeight ); + + _clipBox.min.set( - _svgWidthHalf, - _svgHeightHalf ); + _clipBox.max.set( _svgWidthHalf, _svgHeightHalf ); + + }; + + this.clear = function () { + + _pathCount = 0; + _lineCount = 0; + _rectCount = 0; + + while ( _svg.childNodes.length > 0 ) { + + _svg.removeChild( _svg.childNodes[ 0 ] ); + + } + + _svg.style.backgroundColor = 'rgba(' + ( ( _clearColor.r * 255 ) | 0 ) + ',' + ( ( _clearColor.g * 255 ) | 0 ) + ',' + ( ( _clearColor.b * 255 ) | 0 ) + ',' + _clearAlpha + ')'; + + }; + + this.render = function ( scene, camera ) { + + if ( camera instanceof THREE.Camera === false ) { + + console.error( 'THREE.SVGRenderer.render: camera is not an instance of THREE.Camera.' ); + return; + + } + + if ( this.autoClear === true ) this.clear(); + + _this.info.render.vertices = 0; + _this.info.render.faces = 0; + + _viewMatrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) ); + _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); + + _renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); + _elements = _renderData.elements; + _lights = _renderData.lights; + + _normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse ); + + calculateLights( _lights ); + + for ( var e = 0, el = _elements.length; e < el; e ++ ) { + + var element = _elements[ e ]; + var material = element.material; + + if ( material === undefined || material.opacity === 0 ) continue; + + _elemBox.makeEmpty(); + + if ( element instanceof THREE.RenderableSprite ) { + + _v1 = element; + _v1.x *= _svgWidthHalf; _v1.y *= - _svgHeightHalf; + + renderSprite( _v1, element, material ); + + } else if ( element instanceof THREE.RenderableLine ) { + + _v1 = element.v1; _v2 = element.v2; + + _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; + _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; + + _elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen ] ); + + if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { + + renderLine( _v1, _v2, element, material ); + + } + + } else if ( element instanceof THREE.RenderableFace ) { + + _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; + + if ( _v1.positionScreen.z < - 1 || _v1.positionScreen.z > 1 ) continue; + if ( _v2.positionScreen.z < - 1 || _v2.positionScreen.z > 1 ) continue; + if ( _v3.positionScreen.z < - 1 || _v3.positionScreen.z > 1 ) continue; + + _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; + _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; + _v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= - _svgHeightHalf; + + _elemBox.setFromPoints( [ + _v1.positionScreen, + _v2.positionScreen, + _v3.positionScreen + ] ); + + if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { + + renderFace3( _v1, _v2, _v3, element, material ); + + } + + } + + } + + scene.traverseVisible( function ( object ) { + + if ( object instanceof THREE.SVGObject ) { + + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyProjection( _viewProjectionMatrix ); + + var x = _vector3.x * _svgWidthHalf; + var y = - _vector3.y * _svgHeightHalf; + + var node = object.node; + node.setAttribute( 'transform', 'translate(' + x + ',' + y + ')' ); + + _svg.appendChild( node ); + + } + + } ); + + }; + + function calculateLights( lights ) { + + _ambientLight.setRGB( 0, 0, 0 ); + _directionalLights.setRGB( 0, 0, 0 ); + _pointLights.setRGB( 0, 0, 0 ); + + for ( var l = 0, ll = lights.length; l < ll; l ++ ) { + + var light = lights[ l ]; + var lightColor = light.color; + + if ( light instanceof THREE.AmbientLight ) { + + _ambientLight.r += lightColor.r; + _ambientLight.g += lightColor.g; + _ambientLight.b += lightColor.b; + + } else if ( light instanceof THREE.DirectionalLight ) { + + _directionalLights.r += lightColor.r; + _directionalLights.g += lightColor.g; + _directionalLights.b += lightColor.b; + + } else if ( light instanceof THREE.PointLight ) { + + _pointLights.r += lightColor.r; + _pointLights.g += lightColor.g; + _pointLights.b += lightColor.b; + + } + + } + + } + + function calculateLight( lights, position, normal, color ) { + + for ( var l = 0, ll = lights.length; l < ll; l ++ ) { + + var light = lights[ l ]; + var lightColor = light.color; + + if ( light instanceof THREE.DirectionalLight ) { + + var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ).normalize(); + + var amount = normal.dot( lightPosition ); + + if ( amount <= 0 ) continue; + + amount *= light.intensity; + + color.r += lightColor.r * amount; + color.g += lightColor.g * amount; + color.b += lightColor.b * amount; + + } else if ( light instanceof THREE.PointLight ) { + + var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ); + + var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() ); + + if ( amount <= 0 ) continue; + + amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); + + if ( amount == 0 ) continue; + + amount *= light.intensity; + + color.r += lightColor.r * amount; + color.g += lightColor.g * amount; + color.b += lightColor.b * amount; + + } + + } + + } + + function renderSprite( v1, element, material ) { + + var scaleX = element.scale.x * _svgWidthHalf; + var scaleY = element.scale.y * _svgHeightHalf; + + _svgNode = getRectNode( _rectCount ++ ); + + _svgNode.setAttribute( 'x', v1.x - ( scaleX * 0.5 ) ); + _svgNode.setAttribute( 'y', v1.y - ( scaleY * 0.5 ) ); + _svgNode.setAttribute( 'width', scaleX ); + _svgNode.setAttribute( 'height', scaleY ); + + if ( material instanceof THREE.SpriteMaterial ) { + + _svgNode.setAttribute( 'style', 'fill: ' + material.color.getStyle() ); + + } + + _svg.appendChild( _svgNode ); + + } + + function renderLine( v1, v2, element, material ) { + + _svgNode = getLineNode( _lineCount ++ ); + + _svgNode.setAttribute( 'x1', v1.positionScreen.x ); + _svgNode.setAttribute( 'y1', v1.positionScreen.y ); + _svgNode.setAttribute( 'x2', v2.positionScreen.x ); + _svgNode.setAttribute( 'y2', v2.positionScreen.y ); + + if ( material instanceof THREE.LineBasicMaterial ) { + + _svgNode.setAttribute( 'style', 'fill: none; stroke: ' + material.color.getStyle() + '; stroke-width: ' + material.linewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.linecap + '; stroke-linejoin: ' + material.linejoin ); + + _svg.appendChild( _svgNode ); + + } + + } + + function renderFace3( v1, v2, v3, element, material ) { + + _this.info.render.vertices += 3; + _this.info.render.faces ++; + + _svgNode = getPathNode( _pathCount ++ ); + _svgNode.setAttribute( 'd', 'M ' + v1.positionScreen.x + ' ' + v1.positionScreen.y + ' L ' + v2.positionScreen.x + ' ' + v2.positionScreen.y + ' L ' + v3.positionScreen.x + ',' + v3.positionScreen.y + 'z' ); + + if ( material instanceof THREE.MeshBasicMaterial ) { + + _color.copy( material.color ); + + if ( material.vertexColors === THREE.FaceColors ) { + + _color.multiply( element.color ); + + } + + } else if ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) { + + _diffuseColor.copy( material.color ); + + if ( material.vertexColors === THREE.FaceColors ) { + + _diffuseColor.multiply( element.color ); + + } + + _color.copy( _ambientLight ); + + _centroid.copy( v1.positionWorld ).add( v2.positionWorld ).add( v3.positionWorld ).divideScalar( 3 ); + + calculateLight( _lights, _centroid, element.normalModel, _color ); + + _color.multiply( _diffuseColor ).add( material.emissive ); + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + _normal.copy( element.normalModel ).applyMatrix3( _normalViewMatrix ); + + _color.setRGB( _normal.x, _normal.y, _normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); + + } + + if ( material.wireframe ) { + + _svgNode.setAttribute( 'style', 'fill: none; stroke: ' + _color.getStyle() + '; stroke-width: ' + material.wireframeLinewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.wireframeLinecap + '; stroke-linejoin: ' + material.wireframeLinejoin ); + + } else { + + _svgNode.setAttribute( 'style', 'fill: ' + _color.getStyle() + '; fill-opacity: ' + material.opacity ); + + } + + _svg.appendChild( _svgNode ); + + } + + function getLineNode( id ) { + + if ( _svgLinePool[ id ] == null ) { + + _svgLinePool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'line' ); + + if ( _quality == 0 ) { + + _svgLinePool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed + + } + + return _svgLinePool[ id ]; + + } + + return _svgLinePool[ id ]; + + } + + function getPathNode( id ) { + + if ( _svgPathPool[ id ] == null ) { + + _svgPathPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' ); + + if ( _quality == 0 ) { + + _svgPathPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed + + } + + return _svgPathPool[ id ]; + + } + + return _svgPathPool[ id ]; + + } + + function getRectNode( id ) { + + if ( _svgRectPool[ id ] == null ) { + + _svgRectPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'rect' ); + + if ( _quality == 0 ) { + + _svgRectPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed + + } + + return _svgRectPool[ id ]; + + } + + return _svgRectPool[ id ]; + + } + +}; diff --git a/node_modules/three/examples/js/renderers/SoftwareRenderer.js b/node_modules/three/examples/js/renderers/SoftwareRenderer.js new file mode 100644 index 00000000..6f0ef7d0 --- /dev/null +++ b/node_modules/three/examples/js/renderers/SoftwareRenderer.js @@ -0,0 +1,1398 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author ryg / http://farbrausch.de/~fg + * @author mraleph / http://mrale.ph/ + * @author daoshengmu / http://dsmu.me/ + */ + +THREE.SoftwareRenderer = function ( parameters ) { + + console.log( 'THREE.SoftwareRenderer', THREE.REVISION ); + + parameters = parameters || {}; + + var canvas = parameters.canvas !== undefined + ? parameters.canvas + : document.createElement( 'canvas' ); + + var context = canvas.getContext( '2d', { + alpha: parameters.alpha === true + } ); + + var shaders = {}; + var textures = {}; + + var canvasWidth, canvasHeight; + var canvasWBlocks, canvasHBlocks; + var viewportXScale, viewportYScale, viewportZScale; + var viewportXOffs, viewportYOffs, viewportZOffs; + + var clearColor = new THREE.Color( 0x000000 ); + + var imagedata, data, zbuffer; + var numBlocks, blockMaxZ, blockFlags; + + var BLOCK_ISCLEAR = ( 1 << 0 ); + var BLOCK_NEEDCLEAR = ( 1 << 1 ); + + var subpixelBits = 4; + var subpixelBias = ( 1 << subpixelBits ) - 1; + var blockShift = 3; + var blockSize = 1 << blockShift; + var maxZVal = ( 1 << 24 ); // Note: You want to size this so you don't get overflows. + var lineMode = false; + var lookVector = new THREE.Vector3( 0, 0, 1 ); + var crossVector = new THREE.Vector3(); + + var rectx1 = Infinity, recty1 = Infinity; + var rectx2 = 0, recty2 = 0; + + var prevrectx1 = Infinity, prevrecty1 = Infinity; + var prevrectx2 = 0, prevrecty2 = 0; + + var projector = new THREE.Projector(); + + var vector1 = new THREE.Vector3(); + var vector2 = new THREE.Vector3(); + var vector3 = new THREE.Vector3(); + + var texCoord1 = new THREE.Vector2(); + var texCoord2 = new THREE.Vector2(); + var texCoord3 = new THREE.Vector2(); + + this.domElement = canvas; + + this.autoClear = true; + + // WebGLRenderer compatibility + + this.supportsVertexTextures = function () {}; + this.setFaceCulling = function () {}; + + this.setClearColor = function ( color, alpha ) { + + clearColor.set( color ); + cleanColorBuffer(); + + }; + + this.setPixelRatio = function () {}; + + this.setSize = function ( width, height ) { + + canvasWBlocks = Math.floor( width / blockSize ); + canvasHBlocks = Math.floor( height / blockSize ); + canvasWidth = canvasWBlocks * blockSize; + canvasHeight = canvasHBlocks * blockSize; + + var fixScale = 1 << subpixelBits; + + viewportXScale = fixScale * canvasWidth / 2; + viewportYScale = - fixScale * canvasHeight / 2; + viewportZScale = maxZVal / 2; + + viewportXOffs = fixScale * canvasWidth / 2 + 0.5; + viewportYOffs = fixScale * canvasHeight / 2 + 0.5; + viewportZOffs = maxZVal / 2 + 0.5; + + canvas.width = canvasWidth; + canvas.height = canvasHeight; + + context.fillStyle = clearColor.getStyle(); + context.fillRect( 0, 0, canvasWidth, canvasHeight ); + + imagedata = context.getImageData( 0, 0, canvasWidth, canvasHeight ); + data = imagedata.data; + + zbuffer = new Int32Array( data.length / 4 ); + + numBlocks = canvasWBlocks * canvasHBlocks; + blockMaxZ = new Int32Array( numBlocks ); + blockFlags = new Uint8Array( numBlocks ); + + for ( var i = 0, l = zbuffer.length; i < l; i ++ ) { + + zbuffer[ i ] = maxZVal; + + } + + for ( var i = 0; i < numBlocks; i ++ ) { + + blockFlags[ i ] = BLOCK_ISCLEAR; + + } + + cleanColorBuffer(); + + }; + + this.setSize( canvas.width, canvas.height ); + + this.clear = function () { + + rectx1 = Infinity; + recty1 = Infinity; + rectx2 = 0; + recty2 = 0; + + for ( var i = 0; i < numBlocks; i ++ ) { + + blockMaxZ[ i ] = maxZVal; + blockFlags[ i ] = ( blockFlags[ i ] & BLOCK_ISCLEAR ) ? BLOCK_ISCLEAR : BLOCK_NEEDCLEAR; + + } + + }; + + // TODO: Check why autoClear can't be false. + this.render = function ( scene, camera ) { + + if ( this.autoClear === true ) this.clear(); + + var renderData = projector.projectScene( scene, camera, false, false ); + var elements = renderData.elements; + + for ( var e = 0, el = elements.length; e < el; e ++ ) { + + var element = elements[ e ]; + var material = element.material; + var shader = getMaterialShader( material ); + + if ( element instanceof THREE.RenderableFace ) { + + if ( ! element.uvs ) { + + drawTriangle( + element.v1.positionScreen, + element.v2.positionScreen, + element.v3.positionScreen, + null, null, null, + shader, element, material + ); + + } else { + + drawTriangle( + element.v1.positionScreen, + element.v2.positionScreen, + element.v3.positionScreen, + element.uvs[ 0 ], element.uvs[ 1 ], element.uvs[ 2 ], + shader, element, material + ); + + } + + + } else if ( element instanceof THREE.RenderableSprite ) { + + var scaleX = element.scale.x * 0.5; + var scaleY = element.scale.y * 0.5; + + vector1.copy( element ); + vector1.x -= scaleX; + vector1.y += scaleY; + + vector2.copy( element ); + vector2.x -= scaleX; + vector2.y -= scaleY; + + vector3.copy( element ); + vector3.x += scaleX; + vector3.y += scaleY; + + if ( material.map ) { + + texCoord1.set( 0, 1 ); + texCoord2.set( 0, 0 ); + texCoord3.set( 1, 1 ); + + drawTriangle( + vector1, vector2, vector3, + texCoord1, texCoord2, texCoord3, + shader, element, material + ); + + } else { + + drawTriangle( + vector1, vector2, vector3, + null, null, null, + shader, element, material + ); + + } + + vector1.copy( element ); + vector1.x += scaleX; + vector1.y += scaleY; + + vector2.copy( element ); + vector2.x -= scaleX; + vector2.y -= scaleY; + + vector3.copy( element ); + vector3.x += scaleX; + vector3.y -= scaleY; + + if ( material.map ) { + + texCoord1.set( 1, 1 ); + texCoord2.set( 0, 0 ); + texCoord3.set( 1, 0 ); + + drawTriangle( + vector1, vector2, vector3, + texCoord1, texCoord2, texCoord3, + shader, element, material + ); + + } else { + + drawTriangle( + vector1, vector2, vector3, + null, null, null, + shader, element, material + ); + + } + + } else if ( element instanceof THREE.RenderableLine ) { + + var shader = getMaterialShader( material ); + + drawLine( + element.v1.positionScreen, + element.v2.positionScreen, + element.vertexColors[0], + element.vertexColors[1], + shader, + material + ); + } + + } + + finishClear(); + + var x = Math.min( rectx1, prevrectx1 ); + var y = Math.min( recty1, prevrecty1 ); + var width = Math.max( rectx2, prevrectx2 ) - x; + var height = Math.max( recty2, prevrecty2 ) - y; + + /* + // debug; draw zbuffer + + for ( var i = 0, l = zbuffer.length; i < l; i++ ) { + + var o = i * 4; + var v = (65535 - zbuffer[ i ]) >> 3; + data[ o + 0 ] = v; + data[ o + 1 ] = v; + data[ o + 2 ] = v; + data[ o + 3 ] = 255; + } + */ + + if ( x !== Infinity ) { + + context.putImageData( imagedata, 0, 0, x, y, width, height ); + + } + + prevrectx1 = rectx1; prevrecty1 = recty1; + prevrectx2 = rectx2; prevrecty2 = recty2; + + }; + + function setSize( width, height ) { + + canvasWBlocks = Math.floor( width / blockSize ); + canvasHBlocks = Math.floor( height / blockSize ); + canvasWidth = canvasWBlocks * blockSize; + canvasHeight = canvasHBlocks * blockSize; + + var fixScale = 1 << subpixelBits; + + viewportXScale = fixScale * canvasWidth / 2; + viewportYScale = -fixScale * canvasHeight / 2; + viewportZScale = maxZVal / 2; + + viewportXOffs = fixScale * canvasWidth / 2 + 0.5; + viewportYOffs = fixScale * canvasHeight / 2 + 0.5; + viewportZOffs = maxZVal / 2 + 0.5; + + canvas.width = canvasWidth; + canvas.height = canvasHeight; + + context.fillStyle = clearColor.getStyle(); + context.fillRect( 0, 0, canvasWidth, canvasHeight ); + + imagedata = context.getImageData( 0, 0, canvasWidth, canvasHeight ); + data = imagedata.data; + + zbuffer = new Int32Array( data.length / 4 ); + + numBlocks = canvasWBlocks * canvasHBlocks; + blockMaxZ = new Int32Array( numBlocks ); + blockFlags = new Uint8Array( numBlocks ); + + for ( var i = 0, l = zbuffer.length; i < l; i ++ ) { + + zbuffer[ i ] = maxZVal; + + } + + for ( var i = 0; i < numBlocks; i ++ ) { + + blockFlags[ i ] = BLOCK_ISCLEAR; + + } + + cleanColorBuffer(); + } + + function cleanColorBuffer() { + + var size = canvasWidth * canvasHeight * 4; + + for ( var i = 0; i < size; i += 4 ) { + + data[ i ] = clearColor.r * 255 | 0; + data[ i + 1 ] = clearColor.g * 255 | 0; + data[ i + 2 ] = clearColor.b * 255 | 0; + data[ i + 3 ] = 255; + + } + + context.fillStyle = clearColor.getStyle(); + context.fillRect( 0, 0, canvasWidth, canvasHeight ); + + } + + function getPalette( material, bSimulateSpecular ) { + + var i = 0, j = 0; + var diffuseR = material.color.r * 255; + var diffuseG = material.color.g * 255; + var diffuseB = material.color.b * 255; + var palette = new Uint8Array( 256 * 3 ); + + if ( bSimulateSpecular ) { + + while ( i < 204 ) { + + palette[ j ++ ] = Math.min( i * diffuseR / 204, 255 ); + palette[ j ++ ] = Math.min( i * diffuseG / 204, 255 ); + palette[ j ++ ] = Math.min( i * diffuseB / 204, 255 ); + ++ i; + + } + + while ( i < 256 ) { + + // plus specular highlight + palette[ j ++ ] = Math.min( diffuseR + ( i - 204 ) * ( 255 - diffuseR ) / 82, 255 ); + palette[ j ++ ] = Math.min( diffuseG + ( i - 204 ) * ( 255 - diffuseG ) / 82, 255 ); + palette[ j ++ ] = Math.min( diffuseB + ( i - 204 ) * ( 255 - diffuseB ) / 82, 255 ); + ++ i; + + } + + } else { + + while ( i < 256 ) { + + palette[ j ++ ] = Math.min( i * diffuseR / 255, 255 ); + palette[ j ++ ] = Math.min( i * diffuseG / 255, 255 ); + palette[ j ++ ] = Math.min( i * diffuseB / 255, 255 ); + ++ i; + + } + + } + + return palette; + + } + + function basicMaterialShader( buffer, depthBuf, offset, depth, u, v, n, face, material ) { + + var colorOffset = offset * 4; + + var texture = textures[ material.map.id ]; + + if ( ! texture.data ) + return; + + var tdim = texture.width; + var isTransparent = material.transparent; + var tbound = tdim - 1; + var tdata = texture.data; + var tIndex = ( ( ( v * tdim ) & tbound ) * tdim + ( ( u * tdim ) & tbound ) ) * 4; + + if ( ! isTransparent ) { + + buffer[ colorOffset ] = tdata[ tIndex ]; + buffer[ colorOffset + 1 ] = tdata[ tIndex + 1 ]; + buffer[ colorOffset + 2 ] = tdata[ tIndex + 2 ]; + buffer[ colorOffset + 3 ] = material.opacity * 255; + depthBuf[ offset ] = depth; + + } else { + + var opaci = tdata[ tIndex + 3 ] * material.opacity; + var texel = ( tdata[ tIndex ] << 16 ) + ( tdata[ tIndex + 1 ] << 8 ) + tdata[ tIndex + 2 ]; + if ( opaci < 250 ) { + + var backColor = ( buffer[ colorOffset ] << 16 ) + ( buffer[ colorOffset + 1 ] << 8 ) + buffer[ colorOffset + 2 ]; + texel = texel * opaci + backColor * ( 1 - opaci ); + + } + + buffer[ colorOffset ] = ( texel & 0xff0000 ) >> 16; + buffer[ colorOffset + 1 ] = ( texel & 0xff00 ) >> 8; + buffer[ colorOffset + 2 ] = texel & 0xff; + buffer[ colorOffset + 3 ] = material.opacity * 255; + + } + + } + + function lightingMaterialShader( buffer, depthBuf, offset, depth, u, v, n, face, material ) { + + var colorOffset = offset * 4; + + var texture = textures[ material.map.id ]; + + if ( ! texture.data ) + return; + + var tdim = texture.width; + var isTransparent = material.transparent; + var cIndex = ( n > 0 ? ( ~~ n ) : 0 ) * 3; + var tbound = tdim - 1; + var tdata = texture.data; + var tIndex = ( ( ( v * tdim ) & tbound ) * tdim + ( ( u * tdim ) & tbound ) ) * 4; + + if ( ! isTransparent ) { + + buffer[ colorOffset ] = ( material.palette[ cIndex ] * tdata[ tIndex ] ) >> 8; + buffer[ colorOffset + 1 ] = ( material.palette[ cIndex + 1 ] * tdata[ tIndex + 1 ] ) >> 8; + buffer[ colorOffset + 2 ] = ( material.palette[ cIndex + 2 ] * tdata[ tIndex + 2 ] ) >> 8; + buffer[ colorOffset + 3 ] = material.opacity * 255; + depthBuf[ offset ] = depth; + + } else { + + var opaci = tdata[ tIndex + 3 ] * material.opacity; + var foreColor = ( ( material.palette[ cIndex ] * tdata[ tIndex ] ) << 16 ) + + ( ( material.palette[ cIndex + 1 ] * tdata[ tIndex + 1 ] ) << 8 ) + + ( material.palette[ cIndex + 2 ] * tdata[ tIndex + 2 ] ); + + if ( opaci < 250 ) { + + var backColor = buffer[ colorOffset ] << 24 + buffer[ colorOffset + 1 ] << 16 + buffer[ colorOffset + 2 ] << 8; + foreColor = foreColor * opaci + backColor * ( 1 - opaci ); + + } + + buffer[ colorOffset ] = ( foreColor & 0xff0000 ) >> 16; + buffer[ colorOffset + 1 ] = ( foreColor & 0xff00 ) >> 8; + buffer[ colorOffset + 2 ] = ( foreColor & 0xff ); + buffer[ colorOffset + 3 ] = material.opacity * 255; + + } + + } + + function onMaterialUpdate ( event ) { + + var material = event.target; + + material.removeEventListener( 'update', onMaterialUpdate ); + + delete shaders[ material.id ]; + + } + + function getMaterialShader( material ) { + + var id = material.id; + var shader = shaders[ id ]; + + if ( shaders[ id ] === undefined ) { + + material.addEventListener( 'update', onMaterialUpdate ); + + if ( material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.SpriteMaterial ) { + + if ( material instanceof THREE.MeshLambertMaterial ) { + + // Generate color palette + if ( ! material.palette ) { + + material.palette = getPalette( material, false ); + + } + + } else if ( material instanceof THREE.MeshPhongMaterial ) { + + // Generate color palette + if ( ! material.palette ) { + + material.palette = getPalette( material, true ); + + } + + } + + var string; + + if ( material.map ) { + + var texture = new THREE.SoftwareRenderer.Texture(); + texture.fromImage( material.map.image ); + + textures[ material.map.id ] = texture; + + if ( material instanceof THREE.MeshBasicMaterial + || material instanceof THREE.SpriteMaterial ) { + + shader = basicMaterialShader; + + } else { + + shader = lightingMaterialShader; + + } + + + } else { + + if ( material.vertexColors === THREE.FaceColors ) { + + string = [ + 'var colorOffset = offset * 4;', + 'buffer[ colorOffset ] = face.color.r * 255;', + 'buffer[ colorOffset + 1 ] = face.color.g * 255;', + 'buffer[ colorOffset + 2 ] = face.color.b * 255;', + 'buffer[ colorOffset + 3 ] = material.opacity * 255;', + 'depthBuf[ offset ] = depth;' + ].join( '\n' ); + + } else { + + string = [ + 'var colorOffset = offset * 4;', + 'buffer[ colorOffset ] = material.color.r * 255;', + 'buffer[ colorOffset + 1 ] = material.color.g * 255;', + 'buffer[ colorOffset + 2 ] = material.color.b * 255;', + 'buffer[ colorOffset + 3 ] = material.opacity * 255;', + 'depthBuf[ offset ] = depth;' + ].join( '\n' ); + + } + + shader = new Function( 'buffer, depthBuf, offset, depth, u, v, n, face, material', string ); + + } + + } else if ( material instanceof THREE.LineBasicMaterial ) { + + var string = [ + 'var colorOffset = offset * 4;', + 'buffer[ colorOffset ] = material.color.r * (color1.r+color2.r) * 0.5 * 255;', + 'buffer[ colorOffset + 1 ] = material.color.g * (color1.g+color2.g) * 0.5 * 255;', + 'buffer[ colorOffset + 2 ] = material.color.b * (color1.b+color2.b) * 0.5 * 255;', + 'buffer[ colorOffset + 3 ] = 255;', + 'depthBuf[ offset ] = depth;' + ].join('\n'); + + shader = new Function( 'buffer, depthBuf, offset, depth, color1, color2, material', string ); + + } else { + + var string = [ + 'var colorOffset = offset * 4;', + 'buffer[ colorOffset ] = u * 255;', + 'buffer[ colorOffset + 1 ] = v * 255;', + 'buffer[ colorOffset + 2 ] = 0;', + 'buffer[ colorOffset + 3 ] = 255;', + 'depthBuf[ offset ] = depth;' + ].join( '\n' ); + + shader = new Function( 'buffer, depthBuf, offset, depth, u, v, n, face, material', string ); + + } + + shaders[ id ] = shader; + + } + + return shader; + + } + + function clearRectangle( x1, y1, x2, y2 ) { + + var xmin = Math.max( Math.min( x1, x2 ), 0 ); + var xmax = Math.min( Math.max( x1, x2 ), canvasWidth ); + var ymin = Math.max( Math.min( y1, y2 ), 0 ); + var ymax = Math.min( Math.max( y1, y2 ), canvasHeight ); + + var offset = ( xmin + ymin * canvasWidth ) * 4 + 3; + var linestep = ( canvasWidth - ( xmax - xmin ) ) * 4; + + for ( var y = ymin; y < ymax; y ++ ) { + + for ( var x = xmin; x < xmax; x ++ ) { + + data[ offset += 4 ] = 0; + + } + + offset += linestep; + + } + + } + + function drawTriangle( v1, v2, v3, uv1, uv2, uv3, shader, face, material ) { + + // TODO: Implement per-pixel z-clipping + + if ( v1.z < - 1 || v1.z > 1 || v2.z < - 1 || v2.z > 1 || v3.z < - 1 || v3.z > 1 ) return; + + // https://gist.github.com/2486101 + // explanation: http://pouet.net/topic.php?which=8760&page=1 + + // 28.4 fixed-point coordinates + + var x1 = ( v1.x * viewportXScale + viewportXOffs ) | 0; + var x2 = ( v2.x * viewportXScale + viewportXOffs ) | 0; + var x3 = ( v3.x * viewportXScale + viewportXOffs ) | 0; + + var y1 = ( v1.y * viewportYScale + viewportYOffs ) | 0; + var y2 = ( v2.y * viewportYScale + viewportYOffs ) | 0; + var y3 = ( v3.y * viewportYScale + viewportYOffs ) | 0; + + // Z values (.28 fixed-point) + + var z1 = ( v1.z * viewportZScale + viewportZOffs ) | 0; + var z2 = ( v2.z * viewportZScale + viewportZOffs ) | 0; + var z3 = ( v3.z * viewportZScale + viewportZOffs ) | 0; + + // UV values + var bHasUV = false; + var tu1, tv1, tu2, tv2, tu3, tv3; + + if ( uv1 && uv2 && uv3 ) { + + bHasUV = true; + + tu1 = uv1.x; + tv1 = 1 - uv1.y; + tu2 = uv2.x; + tv2 = 1 - uv2.y; + tu3 = uv3.x; + tv3 = 1 - uv3.y; + + } + + // Normal values + var bHasNormal = false; + var n1, n2, n3, nz1, nz2, nz3; + + if ( face.vertexNormalsModel ) { + + bHasNormal = true; + + n1 = face.vertexNormalsModel[ 0 ]; + n2 = face.vertexNormalsModel[ 1 ]; + n3 = face.vertexNormalsModel[ 2 ]; + nz1 = n1.z * 255; + nz2 = n2.z * 255; + nz3 = n3.z * 255; + + } + + // Deltas + + var dx12 = x1 - x2, dy12 = y2 - y1; + var dx23 = x2 - x3, dy23 = y3 - y2; + var dx31 = x3 - x1, dy31 = y1 - y3; + + // Bounding rectangle + + var minx = Math.max( ( Math.min( x1, x2, x3 ) + subpixelBias ) >> subpixelBits, 0 ); + var maxx = Math.min( ( Math.max( x1, x2, x3 ) + subpixelBias ) >> subpixelBits, canvasWidth ); + var miny = Math.max( ( Math.min( y1, y2, y3 ) + subpixelBias ) >> subpixelBits, 0 ); + var maxy = Math.min( ( Math.max( y1, y2, y3 ) + subpixelBias ) >> subpixelBits, canvasHeight ); + + rectx1 = Math.min( minx, rectx1 ); + rectx2 = Math.max( maxx, rectx2 ); + recty1 = Math.min( miny, recty1 ); + recty2 = Math.max( maxy, recty2 ); + + // Block size, standard 8x8 (must be power of two) + + var q = blockSize; + + // Start in corner of 8x8 block + + minx &= ~ ( q - 1 ); + miny &= ~ ( q - 1 ); + + // Constant part of half-edge functions + + var minXfixscale = ( minx << subpixelBits ); + var minYfixscale = ( miny << subpixelBits ); + + var c1 = dy12 * ( ( minXfixscale ) - x1 ) + dx12 * ( ( minYfixscale ) - y1 ); + var c2 = dy23 * ( ( minXfixscale ) - x2 ) + dx23 * ( ( minYfixscale ) - y2 ); + var c3 = dy31 * ( ( minXfixscale ) - x3 ) + dx31 * ( ( minYfixscale ) - y3 ); + + // Correct for fill convention + + if ( dy12 > 0 || ( dy12 == 0 && dx12 > 0 ) ) c1 ++; + if ( dy23 > 0 || ( dy23 == 0 && dx23 > 0 ) ) c2 ++; + if ( dy31 > 0 || ( dy31 == 0 && dx31 > 0 ) ) c3 ++; + + // Note this doesn't kill subpixel precision, but only because we test for >=0 (not >0). + // It's a bit subtle. :) + c1 = ( c1 - 1 ) >> subpixelBits; + c2 = ( c2 - 1 ) >> subpixelBits; + c3 = ( c3 - 1 ) >> subpixelBits; + + // Z interpolation setup + + var dz12 = z1 - z2, dz31 = z3 - z1; + var invDet = 1.0 / ( dx12 * dy31 - dx31 * dy12 ); + var dzdx = ( invDet * ( dz12 * dy31 - dz31 * dy12 ) ); // dz per one subpixel step in x + var dzdy = ( invDet * ( dz12 * dx31 - dx12 * dz31 ) ); // dz per one subpixel step in y + + // Z at top/left corner of rast area + + var cz = ( z1 + ( ( minXfixscale ) - x1 ) * dzdx + ( ( minYfixscale ) - y1 ) * dzdy ) | 0; + + // Z pixel steps + + var fixscale = ( 1 << subpixelBits ); + dzdx = ( dzdx * fixscale ) | 0; + dzdy = ( dzdy * fixscale ) | 0; + + var dtvdx, dtvdy, cbtu, cbtv; + if ( bHasUV ) { + + // UV interpolation setup + var dtu12 = tu1 - tu2, dtu31 = tu3 - tu1; + var dtudx = ( invDet * ( dtu12 * dy31 - dtu31 * dy12 ) ); // dtu per one subpixel step in x + var dtudy = ( invDet * ( dtu12 * dx31 - dx12 * dtu31 ) ); // dtu per one subpixel step in y + var dtv12 = tv1 - tv2, dtv31 = tv3 - tv1; + dtvdx = ( invDet * ( dtv12 * dy31 - dtv31 * dy12 ) ); // dtv per one subpixel step in x + dtvdy = ( invDet * ( dtv12 * dx31 - dx12 * dtv31 ) ); // dtv per one subpixel step in y + + // UV at top/left corner of rast area + cbtu = ( tu1 + ( minXfixscale - x1 ) * dtudx + ( minYfixscale - y1 ) * dtudy ); + cbtv = ( tv1 + ( minXfixscale - x1 ) * dtvdx + ( minYfixscale - y1 ) * dtvdy ); + + // UV pixel steps + dtudx = dtudx * fixscale; + dtudy = dtudy * fixscale; + dtvdx = dtvdx * fixscale; + dtvdy = dtvdy * fixscale; + + } + + var dnxdx, dnzdy, cbnz; + if ( bHasNormal ) { + + // Normal interpolation setup + var dnz12 = nz1 - nz2, dnz31 = nz3 - nz1; + var dnzdx = ( invDet * ( dnz12 * dy31 - dnz31 * dy12 ) ); // dnz per one subpixel step in x + var dnzdy = ( invDet * ( dnz12 * dx31 - dx12 * dnz31 ) ); // dnz per one subpixel step in y + + // Normal at top/left corner of rast area + cbnz = ( nz1 + ( minXfixscale - x1 ) * dnzdx + ( minYfixscale - y1 ) * dnzdy ); + + // Normal pixel steps + dnzdx = ( dnzdx * fixscale ); + dnzdy = ( dnzdy * fixscale ); + + } + + // Set up min/max corners + var qm1 = q - 1; // for convenience + var nmin1 = 0, nmax1 = 0; + var nmin2 = 0, nmax2 = 0; + var nmin3 = 0, nmax3 = 0; + var nminz = 0, nmaxz = 0; + if ( dx12 >= 0 ) nmax1 -= qm1 * dx12; else nmin1 -= qm1 * dx12; + if ( dy12 >= 0 ) nmax1 -= qm1 * dy12; else nmin1 -= qm1 * dy12; + if ( dx23 >= 0 ) nmax2 -= qm1 * dx23; else nmin2 -= qm1 * dx23; + if ( dy23 >= 0 ) nmax2 -= qm1 * dy23; else nmin2 -= qm1 * dy23; + if ( dx31 >= 0 ) nmax3 -= qm1 * dx31; else nmin3 -= qm1 * dx31; + if ( dy31 >= 0 ) nmax3 -= qm1 * dy31; else nmin3 -= qm1 * dy31; + if ( dzdx >= 0 ) nmaxz += qm1 * dzdx; else nminz += qm1 * dzdx; + if ( dzdy >= 0 ) nmaxz += qm1 * dzdy; else nminz += qm1 * dzdy; + + // Loop through blocks + var linestep = canvasWidth - q; + + var cb1 = c1; + var cb2 = c2; + var cb3 = c3; + var cbz = cz; + var qstep = - q; + var e1x = qstep * dy12; + var e2x = qstep * dy23; + var e3x = qstep * dy31; + var ezx = qstep * dzdx; + + var etux, etvx; + if ( bHasUV ) { + + etux = qstep * dtudx; + etvx = qstep * dtvdx; + + } + + var enzx; + if ( bHasNormal ) { + + enzx = qstep * dnzdx; + + } + + var x0 = minx; + + for ( var y0 = miny; y0 < maxy; y0 += q ) { + + // New block line - keep hunting for tri outer edge in old block line dir + while ( x0 >= minx && x0 < maxx && cb1 >= nmax1 && cb2 >= nmax2 && cb3 >= nmax3 ) { + + x0 += qstep; + cb1 += e1x; + cb2 += e2x; + cb3 += e3x; + cbz += ezx; + + if ( bHasUV ) { + + cbtu += etux; + cbtv += etvx; + + } + + if ( bHasNormal ) { + + cbnz += enzx; + + } + + } + + // Okay, we're now in a block we know is outside. Reverse direction and go into main loop. + qstep = - qstep; + e1x = - e1x; + e2x = - e2x; + e3x = - e3x; + ezx = - ezx; + + if ( bHasUV ) { + + etux = - etux; + etvx = - etvx; + + } + + if ( bHasNormal ) { + + enzx = - enzx; + + } + + while ( 1 ) { + + // Step everything + x0 += qstep; + cb1 += e1x; + cb2 += e2x; + cb3 += e3x; + cbz += ezx; + + if ( bHasUV ) { + + cbtu += etux; + cbtv += etvx; + + } + + if ( bHasNormal ) { + + cbnz += enzx; + + } + + // We're done with this block line when at least one edge completely out + // If an edge function is too small and decreasing in the current traversal + // dir, we're done with this line. + if ( x0 < minx || x0 >= maxx ) break; + if ( cb1 < nmax1 ) if ( e1x < 0 ) break; else continue; + if ( cb2 < nmax2 ) if ( e2x < 0 ) break; else continue; + if ( cb3 < nmax3 ) if ( e3x < 0 ) break; else continue; + + // We can skip this block if it's already fully covered + var blockX = x0 >> blockShift; + var blockY = y0 >> blockShift; + var blockId = blockX + blockY * canvasWBlocks; + var minz = cbz + nminz; + + // farthest point in block closer than closest point in our tri? + if ( blockMaxZ[ blockId ] < minz ) continue; + + // Need to do a deferred clear? + var bflags = blockFlags[ blockId ]; + if ( bflags & BLOCK_NEEDCLEAR ) clearBlock( blockX, blockY ); + blockFlags[ blockId ] = bflags & ~ ( BLOCK_ISCLEAR | BLOCK_NEEDCLEAR ); + + // Offset at top-left corner + var offset = x0 + y0 * canvasWidth; + + // Accept whole block when fully covered + if ( cb1 >= nmin1 && cb2 >= nmin2 && cb3 >= nmin3 ) { + + var maxz = cbz + nmaxz; + blockMaxZ[ blockId ] = Math.min( blockMaxZ[ blockId ], maxz ); + + var cy1 = cb1; + var cy2 = cb2; + var cyz = cbz; + + var cytu, cytv; + if ( bHasUV ) { + + cytu = cbtu; + cytv = cbtv; + + } + + var cynz; + if ( bHasNormal ) { + + cynz = cbnz; + + } + + + for ( var iy = 0; iy < q; iy ++ ) { + + var cx1 = cy1; + var cx2 = cy2; + var cxz = cyz; + + var cxtu; + var cxtv; + if ( bHasUV ) { + + cxtu = cytu; + cxtv = cytv; + + } + + var cxnz; + if ( bHasNormal ) { + + cxnz = cynz; + + } + + for ( var ix = 0; ix < q; ix ++ ) { + + var z = cxz; + + if ( z < zbuffer[ offset ] ) { + + shader( data, zbuffer, offset, z, cxtu, cxtv, cxnz, face, material ); + + } + + cx1 += dy12; + cx2 += dy23; + cxz += dzdx; + + if ( bHasUV ) { + + cxtu += dtudx; + cxtv += dtvdx; + + } + + if ( bHasNormal ) { + + cxnz += dnzdx; + + } + + offset ++; + + } + + cy1 += dx12; + cy2 += dx23; + cyz += dzdy; + + if ( bHasUV ) { + + cytu += dtudy; + cytv += dtvdy; + + } + + if ( bHasNormal ) { + + cynz += dnzdy; + + } + + offset += linestep; + + } + + } else { + + // Partially covered block + + var cy1 = cb1; + var cy2 = cb2; + var cy3 = cb3; + var cyz = cbz; + + var cytu, cytv; + if ( bHasUV ) { + + cytu = cbtu; + cytv = cbtv; + + } + + var cynz; + if ( bHasNormal ) { + + cynz = cbnz; + + } + + for ( var iy = 0; iy < q; iy ++ ) { + + var cx1 = cy1; + var cx2 = cy2; + var cx3 = cy3; + var cxz = cyz; + + var cxtu; + var cxtv; + if ( bHasUV ) { + + cxtu = cytu; + cxtv = cytv; + + } + + var cxnz; + if ( bHasNormal ) { + + cxnz = cynz; + + } + + for ( var ix = 0; ix < q; ix ++ ) { + + if ( ( cx1 | cx2 | cx3 ) >= 0 ) { + + var z = cxz; + + if ( z < zbuffer[ offset ] ) { + + shader( data, zbuffer, offset, z, cxtu, cxtv, cxnz, face, material ); + + } + + } + + cx1 += dy12; + cx2 += dy23; + cx3 += dy31; + cxz += dzdx; + + if ( bHasUV ) { + + cxtu += dtudx; + cxtv += dtvdx; + + } + + if ( bHasNormal ) { + + cxnz += dnzdx; + + } + + offset ++; + + } + + cy1 += dx12; + cy2 += dx23; + cy3 += dx31; + cyz += dzdy; + + if ( bHasUV ) { + + cytu += dtudy; + cytv += dtvdy; + + } + + if ( bHasNormal ) { + + cynz += dnzdy; + + } + + offset += linestep; + + } + + } + + } + + // Advance to next row of blocks + cb1 += q * dx12; + cb2 += q * dx23; + cb3 += q * dx31; + cbz += q * dzdy; + + if ( bHasUV ) { + + cbtu += q * dtudy; + cbtv += q * dtvdy; + + } + + if ( bHasNormal ) { + + cbnz += q * dnzdy; + + } + + } + + } + + // When drawing line, the blockShiftShift has to be zero. In order to clean pixel + // Using color1 and color2 to interpolation pixel color + // LineWidth is according to material.linewidth + function drawLine( v1, v2, color1, color2, shader, material ) { + + // While the line mode is enable, blockSize has to be changed to 0. + if ( !lineMode ) { + lineMode = true; + blockShift = 0; + blockSize = 1 << blockShift; + + setSize( canvas.width, canvas.height ); + } + + // TODO: Implement per-pixel z-clipping + if ( v1.z < -1 || v1.z > 1 || v2.z < -1 || v2.z > 1 ) return; + + var halfLineWidth = Math.floor( (material.linewidth-1) * 0.5 ); + + // https://gist.github.com/2486101 + // explanation: http://pouet.net/topic.php?which=8760&page=1 + + // 28.4 fixed-point coordinates + var x1 = (v1.x * viewportXScale + viewportXOffs) | 0; + var x2 = (v2.x * viewportXScale + viewportXOffs) | 0; + + var y1 = (v1.y * viewportYScale + viewportYOffs) | 0; + var y2 = (v2.y * viewportYScale + viewportYOffs) | 0; + + var z1 = (v1.z * viewportZScale + viewportZOffs) | 0; + var z2 = (v2.z * viewportZScale + viewportZOffs) | 0; + + // Deltas + var dx12 = x1 - x2, dy12 = y1 - y2, dz12 = z1 - z2; + + // Bounding rectangle + var minx = Math.max( ( Math.min( x1, x2 ) + subpixelBias ) >> subpixelBits, 0 ); + var maxx = Math.min( ( Math.max( x1, x2 ) + subpixelBias ) >> subpixelBits, canvasWidth ); + var miny = Math.max( ( Math.min( y1, y2 ) + subpixelBias ) >> subpixelBits, 0 ); + var maxy = Math.min( ( Math.max( y1, y2 ) + subpixelBias ) >> subpixelBits, canvasHeight ); + var minz = Math.max( ( Math.min( z1, z2 ) + subpixelBias ) >> subpixelBits, 0 ); + var maxz = Math.min( ( Math.max( z1, z2 ) + subpixelBias ) >> subpixelBits, 0 ); + + rectx1 = Math.min( minx, rectx1 ); + rectx2 = Math.max( maxx, rectx2 ); + recty1 = Math.min( miny, recty1 ); + recty2 = Math.max( maxy, recty2 ); + + // Get the line's unit vector and cross vector + var length = Math.sqrt((dy12 * dy12) + (dx12 * dx12)); + var unitX = (dx12 / length); + var unitY = (dy12 / length); + var unitZ = (dz12 / length); + var pixelX, pixelY, pixelZ; + var pX, pY, pZ; + crossVector.set( unitX, unitY, unitZ ); + crossVector.cross( lookVector ); + crossVector.normalize(); + + while (length > 0) { + + // Get this pixel. + pixelX = (x2 + length * unitX); + pixelY = (y2 + length * unitY); + pixelZ = (z2 + length * unitZ); + + pixelX = (pixelX + subpixelBias) >> subpixelBits; + pixelY = (pixelY + subpixelBias) >> subpixelBits; + pZ = (pixelZ + subpixelBias) >> subpixelBits; + + // Draw line with line width + for ( var i = -halfLineWidth; i <= halfLineWidth; ++i ) { + + // Compute the line pixels. + // Get the pixels on the vector that crosses to the line vector + pX = Math.floor((pixelX + crossVector.x * i)); + pY = Math.floor((pixelY + crossVector.y * i)); + + // if pixel is over the rect. Continue + if ( rectx1 >= pX || rectx2 <= pX || recty1 >= pY + || recty2 <= pY ) + continue; + + // Find this pixel at which block + var blockX = pX >> blockShift; + var blockY = pY >> blockShift; + var blockId = blockX + blockY * canvasWBlocks; + + // Compare the pixel depth width z block. + if ( blockMaxZ[ blockId ] < minz ) continue; + + blockMaxZ[ blockId ] = Math.min( blockMaxZ[ blockId ], maxz ); + + var bflags = blockFlags[ blockId ]; + if ( bflags & BLOCK_NEEDCLEAR ) clearBlock( blockX, blockY ); + blockFlags[ blockId ] = bflags & ~( BLOCK_ISCLEAR | BLOCK_NEEDCLEAR ); + + // draw pixel + var offset = pX + pY * canvasWidth; + + if ( pZ < zbuffer[ offset ] ) { + shader( data, zbuffer, offset, pZ, color1, color2, material ); + } + } + + --length; + } + + } + + function clearBlock( blockX, blockY ) { + + var zoffset = blockX * blockSize + blockY * blockSize * canvasWidth; + var poffset = zoffset * 4; + + var zlinestep = canvasWidth - blockSize; + var plinestep = zlinestep * 4; + + for ( var y = 0; y < blockSize; y ++ ) { + + for ( var x = 0; x < blockSize; x ++ ) { + + zbuffer[ zoffset ++ ] = maxZVal; + + data[ poffset ++ ] = clearColor.r * 255 | 0; + data[ poffset ++ ] = clearColor.g * 255 | 0; + data[ poffset ++ ] = clearColor.b * 255 | 0; + data[ poffset ++ ] = 255; + + } + + zoffset += zlinestep; + poffset += plinestep; + + } + + } + + function finishClear( ) { + + var block = 0; + + for ( var y = 0; y < canvasHBlocks; y ++ ) { + + for ( var x = 0; x < canvasWBlocks; x ++ ) { + + if ( blockFlags[ block ] & BLOCK_NEEDCLEAR ) { + + clearBlock( x, y ); + blockFlags[ block ] = BLOCK_ISCLEAR; + + } + + block ++; + + } + + } + + } + +}; + +THREE.SoftwareRenderer.Texture = function() { + + var canvas; + + this.fromImage = function( image ) { + + if ( ! image || image.width <= 0 || image.height <= 0 ) + return; + + if ( canvas === undefined ) { + + canvas = document.createElement( 'canvas' ); + + } + + var size = image.width > image.height ? image.width : image.height; + size = THREE.Math.nextPowerOfTwo( size ); + + if ( canvas.width != size || canvas.height != size ) { + + canvas.width = size; + canvas.height = size; + + } + + var ctx = canvas.getContext( '2d' ); + ctx.clearRect( 0, 0, size, size ); + ctx.drawImage( image, 0, 0, size, size ); + + var imgData = ctx.getImageData( 0, 0, size, size ); + + this.data = imgData.data; + this.width = size; + this.height = size; + this.srcUrl = image.src; + + }; + +}; diff --git a/node_modules/three/examples/js/shaders/BasicShader.js b/node_modules/three/examples/js/shaders/BasicShader.js new file mode 100644 index 00000000..aea89616 --- /dev/null +++ b/node_modules/three/examples/js/shaders/BasicShader.js @@ -0,0 +1,31 @@ +/** + * @author mrdoob / http://www.mrdoob.com + * + * Simple test shader + */ + +THREE.BasicShader = { + + uniforms: {}, + + vertexShader: [ + + "void main() {", + + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "void main() {", + + "gl_FragColor = vec4( 1.0, 0.0, 0.0, 0.5 );", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/BleachBypassShader.js b/node_modules/three/examples/js/shaders/BleachBypassShader.js new file mode 100644 index 00000000..f24bb95d --- /dev/null +++ b/node_modules/three/examples/js/shaders/BleachBypassShader.js @@ -0,0 +1,64 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Bleach bypass shader [http://en.wikipedia.org/wiki/Bleach_bypass] + * - based on Nvidia example + * http://developer.download.nvidia.com/shaderlibrary/webpages/shader_library.html#post_bleach_bypass + */ + +THREE.BleachBypassShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "opacity": { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform float opacity;", + + "uniform sampler2D tDiffuse;", + + "varying vec2 vUv;", + + "void main() {", + + "vec4 base = texture2D( tDiffuse, vUv );", + + "vec3 lumCoeff = vec3( 0.25, 0.65, 0.1 );", + "float lum = dot( lumCoeff, base.rgb );", + "vec3 blend = vec3( lum );", + + "float L = min( 1.0, max( 0.0, 10.0 * ( lum - 0.45 ) ) );", + + "vec3 result1 = 2.0 * base.rgb * blend;", + "vec3 result2 = 1.0 - 2.0 * ( 1.0 - blend ) * ( 1.0 - base.rgb );", + + "vec3 newColor = mix( result1, result2, L );", + + "float A2 = opacity * base.a;", + "vec3 mixRGB = A2 * newColor.rgb;", + "mixRGB += ( ( 1.0 - A2 ) * base.rgb );", + + "gl_FragColor = vec4( mixRGB, base.a );", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/BlendShader.js b/node_modules/three/examples/js/shaders/BlendShader.js new file mode 100644 index 00000000..ccdf0cc1 --- /dev/null +++ b/node_modules/three/examples/js/shaders/BlendShader.js @@ -0,0 +1,51 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Blend two textures + */ + +THREE.BlendShader = { + + uniforms: { + + "tDiffuse1": { type: "t", value: null }, + "tDiffuse2": { type: "t", value: null }, + "mixRatio": { type: "f", value: 0.5 }, + "opacity": { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform float opacity;", + "uniform float mixRatio;", + + "uniform sampler2D tDiffuse1;", + "uniform sampler2D tDiffuse2;", + + "varying vec2 vUv;", + + "void main() {", + + "vec4 texel1 = texture2D( tDiffuse1, vUv );", + "vec4 texel2 = texture2D( tDiffuse2, vUv );", + "gl_FragColor = opacity * mix( texel1, texel2, mixRatio );", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/BokehShader.js b/node_modules/three/examples/js/shaders/BokehShader.js new file mode 100644 index 00000000..bb6a9a11 --- /dev/null +++ b/node_modules/three/examples/js/shaders/BokehShader.js @@ -0,0 +1,116 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Depth-of-field shader with bokeh + * ported from GLSL shader by Martins Upitis + * http://artmartinsh.blogspot.com/2010/02/glsl-lens-blur-filter-with-bokeh.html + */ + +THREE.BokehShader = { + + uniforms: { + + "tColor": { type: "t", value: null }, + "tDepth": { type: "t", value: null }, + "focus": { type: "f", value: 1.0 }, + "aspect": { type: "f", value: 1.0 }, + "aperture": { type: "f", value: 0.025 }, + "maxblur": { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "varying vec2 vUv;", + + "uniform sampler2D tColor;", + "uniform sampler2D tDepth;", + + "uniform float maxblur;", // max blur amount + "uniform float aperture;", // aperture - bigger values for shallower depth of field + + "uniform float focus;", + "uniform float aspect;", + + "void main() {", + + "vec2 aspectcorrect = vec2( 1.0, aspect );", + + "vec4 depth1 = texture2D( tDepth, vUv );", + + "float factor = depth1.x - focus;", + + "vec2 dofblur = vec2 ( clamp( factor * aperture, -maxblur, maxblur ) );", + + "vec2 dofblur9 = dofblur * 0.9;", + "vec2 dofblur7 = dofblur * 0.7;", + "vec2 dofblur4 = dofblur * 0.4;", + + "vec4 col = vec4( 0.0 );", + + "col += texture2D( tColor, vUv.xy );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.0, 0.4 ) * aspectcorrect ) * dofblur );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.15, 0.37 ) * aspectcorrect ) * dofblur );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.29, 0.29 ) * aspectcorrect ) * dofblur );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.37, 0.15 ) * aspectcorrect ) * dofblur );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.40, 0.0 ) * aspectcorrect ) * dofblur );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.37, -0.15 ) * aspectcorrect ) * dofblur );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.29, -0.29 ) * aspectcorrect ) * dofblur );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.15, -0.37 ) * aspectcorrect ) * dofblur );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.0, -0.4 ) * aspectcorrect ) * dofblur );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.15, 0.37 ) * aspectcorrect ) * dofblur );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.29, 0.29 ) * aspectcorrect ) * dofblur );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.37, 0.15 ) * aspectcorrect ) * dofblur );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.4, 0.0 ) * aspectcorrect ) * dofblur );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.37, -0.15 ) * aspectcorrect ) * dofblur );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.29, -0.29 ) * aspectcorrect ) * dofblur );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.15, -0.37 ) * aspectcorrect ) * dofblur );", + + "col += texture2D( tColor, vUv.xy + ( vec2( 0.15, 0.37 ) * aspectcorrect ) * dofblur9 );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.37, 0.15 ) * aspectcorrect ) * dofblur9 );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.37, -0.15 ) * aspectcorrect ) * dofblur9 );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.15, -0.37 ) * aspectcorrect ) * dofblur9 );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.15, 0.37 ) * aspectcorrect ) * dofblur9 );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.37, 0.15 ) * aspectcorrect ) * dofblur9 );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.37, -0.15 ) * aspectcorrect ) * dofblur9 );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.15, -0.37 ) * aspectcorrect ) * dofblur9 );", + + "col += texture2D( tColor, vUv.xy + ( vec2( 0.29, 0.29 ) * aspectcorrect ) * dofblur7 );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.40, 0.0 ) * aspectcorrect ) * dofblur7 );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.29, -0.29 ) * aspectcorrect ) * dofblur7 );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.0, -0.4 ) * aspectcorrect ) * dofblur7 );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.29, 0.29 ) * aspectcorrect ) * dofblur7 );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.4, 0.0 ) * aspectcorrect ) * dofblur7 );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.29, -0.29 ) * aspectcorrect ) * dofblur7 );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.0, 0.4 ) * aspectcorrect ) * dofblur7 );", + + "col += texture2D( tColor, vUv.xy + ( vec2( 0.29, 0.29 ) * aspectcorrect ) * dofblur4 );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.4, 0.0 ) * aspectcorrect ) * dofblur4 );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.29, -0.29 ) * aspectcorrect ) * dofblur4 );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.0, -0.4 ) * aspectcorrect ) * dofblur4 );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.29, 0.29 ) * aspectcorrect ) * dofblur4 );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.4, 0.0 ) * aspectcorrect ) * dofblur4 );", + "col += texture2D( tColor, vUv.xy + ( vec2( -0.29, -0.29 ) * aspectcorrect ) * dofblur4 );", + "col += texture2D( tColor, vUv.xy + ( vec2( 0.0, 0.4 ) * aspectcorrect ) * dofblur4 );", + + "gl_FragColor = col / 41.0;", + "gl_FragColor.a = 1.0;", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/BokehShader2.js b/node_modules/three/examples/js/shaders/BokehShader2.js new file mode 100644 index 00000000..d51c720f --- /dev/null +++ b/node_modules/three/examples/js/shaders/BokehShader2.js @@ -0,0 +1,370 @@ +/** + * @author zz85 / https://github.com/zz85 | twitter.com/blurspline + * + * Depth-of-field shader with bokeh + * ported from GLSL shader by Martins Upitis + * http://blenderartists.org/forum/showthread.php?237488-GLSL-depth-of-field-with-bokeh-v2-4-(update) + * + * Requires #define RINGS and SAMPLES integers + */ + + + +THREE.BokehShader = { + + uniforms: { + + "textureWidth": { type: "f", value: 1.0 }, + "textureHeight": { type: "f", value: 1.0 }, + + "focalDepth": { type: "f", value: 1.0 }, + "focalLength": { type: "f", value: 24.0 }, + "fstop": { type: "f", value: 0.9 }, + + "tColor": { type: "t", value: null }, + "tDepth": { type: "t", value: null }, + + "maxblur": { type: "f", value: 1.0 }, + + "showFocus": { type: "i", value: 0 }, + "manualdof": { type: "i", value: 0 }, + "vignetting": { type: "i", value: 0 }, + "depthblur": { type: "i", value: 0 }, + + "threshold": { type: "f", value: 0.5 }, + "gain": { type: "f", value: 2.0 }, + "bias": { type: "f", value: 0.5 }, + "fringe": { type: "f", value: 0.7 }, + + "znear": { type: "f", value: 0.1 }, + "zfar": { type: "f", value: 100 }, + + "noise": { type: "i", value: 1 }, + "dithering": { type: "f", value: 0.0001 }, + "pentagon": { type: "i", value: 0 }, + + "shaderFocus": { type: "i", value: 1 }, + "focusCoords": { type: "v2", value: new THREE.Vector2() }, + + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "varying vec2 vUv;", + + "uniform sampler2D tColor;", + "uniform sampler2D tDepth;", + "uniform float textureWidth;", + "uniform float textureHeight;", + + "const float PI = 3.14159265;", + + "uniform float focalDepth; //focal distance value in meters, but you may use autofocus option below", + "uniform float focalLength; //focal length in mm", + "uniform float fstop; //f-stop value", + "uniform bool showFocus; //show debug focus point and focal range (red = focal point, green = focal range)", + + "/*", + "make sure that these two values are the same for your camera, otherwise distances will be wrong.", + "*/", + + "uniform float znear; // camera clipping start", + "uniform float zfar; // camera clipping end", + + "//------------------------------------------", + "//user variables", + + "const int samples = SAMPLES; //samples on the first ring", + "const int rings = RINGS; //ring count", + + "const int maxringsamples = rings * samples;", + + "uniform bool manualdof; // manual dof calculation", + "float ndofstart = 1.0; // near dof blur start", + "float ndofdist = 2.0; // near dof blur falloff distance", + "float fdofstart = 1.0; // far dof blur start", + "float fdofdist = 3.0; // far dof blur falloff distance", + + "float CoC = 0.03; //circle of confusion size in mm (35mm film = 0.03mm)", + + "uniform bool vignetting; // use optical lens vignetting", + + "float vignout = 1.3; // vignetting outer border", + "float vignin = 0.0; // vignetting inner border", + "float vignfade = 22.0; // f-stops till vignete fades", + + "uniform bool shaderFocus;", + "// disable if you use external focalDepth value", + + "uniform vec2 focusCoords;", + "// autofocus point on screen (0.0,0.0 - left lower corner, 1.0,1.0 - upper right)", + "// if center of screen use vec2(0.5, 0.5);", + + "uniform float maxblur;", + "//clamp value of max blur (0.0 = no blur, 1.0 default)", + + "uniform float threshold; // highlight threshold;", + "uniform float gain; // highlight gain;", + + "uniform float bias; // bokeh edge bias", + "uniform float fringe; // bokeh chromatic aberration / fringing", + + "uniform bool noise; //use noise instead of pattern for sample dithering", + + "uniform float dithering;", + + "uniform bool depthblur; // blur the depth buffer", + "float dbsize = 1.25; // depth blur size", + + "/*", + "next part is experimental", + "not looking good with small sample and ring count", + "looks okay starting from samples = 4, rings = 4", + "*/", + + "uniform bool pentagon; //use pentagon as bokeh shape?", + "float feather = 0.4; //pentagon shape feather", + + "//------------------------------------------", + + "float penta(vec2 coords) {", + "//pentagonal shape", + "float scale = float(rings) - 1.3;", + "vec4 HS0 = vec4( 1.0, 0.0, 0.0, 1.0);", + "vec4 HS1 = vec4( 0.309016994, 0.951056516, 0.0, 1.0);", + "vec4 HS2 = vec4(-0.809016994, 0.587785252, 0.0, 1.0);", + "vec4 HS3 = vec4(-0.809016994,-0.587785252, 0.0, 1.0);", + "vec4 HS4 = vec4( 0.309016994,-0.951056516, 0.0, 1.0);", + "vec4 HS5 = vec4( 0.0 ,0.0 , 1.0, 1.0);", + + "vec4 one = vec4( 1.0 );", + + "vec4 P = vec4((coords),vec2(scale, scale));", + + "vec4 dist = vec4(0.0);", + "float inorout = -4.0;", + + "dist.x = dot( P, HS0 );", + "dist.y = dot( P, HS1 );", + "dist.z = dot( P, HS2 );", + "dist.w = dot( P, HS3 );", + + "dist = smoothstep( -feather, feather, dist );", + + "inorout += dot( dist, one );", + + "dist.x = dot( P, HS4 );", + "dist.y = HS5.w - abs( P.z );", + + "dist = smoothstep( -feather, feather, dist );", + "inorout += dist.x;", + + "return clamp( inorout, 0.0, 1.0 );", + "}", + + "float bdepth(vec2 coords) {", + "// Depth buffer blur", + "float d = 0.0;", + "float kernel[9];", + "vec2 offset[9];", + + "vec2 wh = vec2(1.0/textureWidth,1.0/textureHeight) * dbsize;", + + "offset[0] = vec2(-wh.x,-wh.y);", + "offset[1] = vec2( 0.0, -wh.y);", + "offset[2] = vec2( wh.x -wh.y);", + + "offset[3] = vec2(-wh.x, 0.0);", + "offset[4] = vec2( 0.0, 0.0);", + "offset[5] = vec2( wh.x, 0.0);", + + "offset[6] = vec2(-wh.x, wh.y);", + "offset[7] = vec2( 0.0, wh.y);", + "offset[8] = vec2( wh.x, wh.y);", + + "kernel[0] = 1.0/16.0; kernel[1] = 2.0/16.0; kernel[2] = 1.0/16.0;", + "kernel[3] = 2.0/16.0; kernel[4] = 4.0/16.0; kernel[5] = 2.0/16.0;", + "kernel[6] = 1.0/16.0; kernel[7] = 2.0/16.0; kernel[8] = 1.0/16.0;", + + + "for( int i=0; i<9; i++ ) {", + "float tmp = texture2D(tDepth, coords + offset[i]).r;", + "d += tmp * kernel[i];", + "}", + + "return d;", + "}", + + + "vec3 color(vec2 coords,float blur) {", + "//processing the sample", + + "vec3 col = vec3(0.0);", + "vec2 texel = vec2(1.0/textureWidth,1.0/textureHeight);", + + "col.r = texture2D(tColor,coords + vec2(0.0,1.0)*texel*fringe*blur).r;", + "col.g = texture2D(tColor,coords + vec2(-0.866,-0.5)*texel*fringe*blur).g;", + "col.b = texture2D(tColor,coords + vec2(0.866,-0.5)*texel*fringe*blur).b;", + + "vec3 lumcoeff = vec3(0.299,0.587,0.114);", + "float lum = dot(col.rgb, lumcoeff);", + "float thresh = max((lum-threshold)*gain, 0.0);", + "return col+mix(vec3(0.0),col,thresh*blur);", + "}", + + "vec2 rand(vec2 coord) {", + "// generating noise / pattern texture for dithering", + + "float noiseX = ((fract(1.0-coord.s*(textureWidth/2.0))*0.25)+(fract(coord.t*(textureHeight/2.0))*0.75))*2.0-1.0;", + "float noiseY = ((fract(1.0-coord.s*(textureWidth/2.0))*0.75)+(fract(coord.t*(textureHeight/2.0))*0.25))*2.0-1.0;", + + "if (noise) {", + "noiseX = clamp(fract(sin(dot(coord ,vec2(12.9898,78.233))) * 43758.5453),0.0,1.0)*2.0-1.0;", + "noiseY = clamp(fract(sin(dot(coord ,vec2(12.9898,78.233)*2.0)) * 43758.5453),0.0,1.0)*2.0-1.0;", + "}", + + "return vec2(noiseX,noiseY);", + "}", + + "vec3 debugFocus(vec3 col, float blur, float depth) {", + "float edge = 0.002*depth; //distance based edge smoothing", + "float m = clamp(smoothstep(0.0,edge,blur),0.0,1.0);", + "float e = clamp(smoothstep(1.0-edge,1.0,blur),0.0,1.0);", + + "col = mix(col,vec3(1.0,0.5,0.0),(1.0-m)*0.6);", + "col = mix(col,vec3(0.0,0.5,1.0),((1.0-e)-(1.0-m))*0.2);", + + "return col;", + "}", + + "float linearize(float depth) {", + "return -zfar * znear / (depth * (zfar - znear) - zfar);", + "}", + + + "float vignette() {", + "float dist = distance(vUv.xy, vec2(0.5,0.5));", + "dist = smoothstep(vignout+(fstop/vignfade), vignin+(fstop/vignfade), dist);", + "return clamp(dist,0.0,1.0);", + "}", + + "float gather(float i, float j, int ringsamples, inout vec3 col, float w, float h, float blur) {", + "float rings2 = float(rings);", + "float step = PI*2.0 / float(ringsamples);", + "float pw = cos(j*step)*i;", + "float ph = sin(j*step)*i;", + "float p = 1.0;", + "if (pentagon) {", + "p = penta(vec2(pw,ph));", + "}", + "col += color(vUv.xy + vec2(pw*w,ph*h), blur) * mix(1.0, i/rings2, bias) * p;", + "return 1.0 * mix(1.0, i /rings2, bias) * p;", + "}", + + "void main() {", + "//scene depth calculation", + + "float depth = linearize(texture2D(tDepth,vUv.xy).x);", + + "// Blur depth?", + "if (depthblur) {", + "depth = linearize(bdepth(vUv.xy));", + "}", + + "//focal plane calculation", + + "float fDepth = focalDepth;", + + "if (shaderFocus) {", + + "fDepth = linearize(texture2D(tDepth,focusCoords).x);", + + "}", + + "// dof blur factor calculation", + + "float blur = 0.0;", + + "if (manualdof) {", + "float a = depth-fDepth; // Focal plane", + "float b = (a-fdofstart)/fdofdist; // Far DoF", + "float c = (-a-ndofstart)/ndofdist; // Near Dof", + "blur = (a>0.0) ? b : c;", + "} else {", + "float f = focalLength; // focal length in mm", + "float d = fDepth*1000.0; // focal plane in mm", + "float o = depth*1000.0; // depth in mm", + + "float a = (o*f)/(o-f);", + "float b = (d*f)/(d-f);", + "float c = (d-f)/(d*fstop*CoC);", + + "blur = abs(a-b)*c;", + "}", + + "blur = clamp(blur,0.0,1.0);", + + "// calculation of pattern for dithering", + + "vec2 noise = rand(vUv.xy)*dithering*blur;", + + "// getting blur x and y step factor", + + "float w = (1.0/textureWidth)*blur*maxblur+noise.x;", + "float h = (1.0/textureHeight)*blur*maxblur+noise.y;", + + "// calculation of final color", + + "vec3 col = vec3(0.0);", + + "if(blur < 0.05) {", + "//some optimization thingy", + "col = texture2D(tColor, vUv.xy).rgb;", + "} else {", + "col = texture2D(tColor, vUv.xy).rgb;", + "float s = 1.0;", + "int ringsamples;", + + "for (int i = 1; i <= rings; i++) {", + "/*unboxstart*/", + "ringsamples = i * samples;", + + "for (int j = 0 ; j < maxringsamples ; j++) {", + "if (j >= ringsamples) break;", + "s += gather(float(i), float(j), ringsamples, col, w, h, blur);", + "}", + "/*unboxend*/", + "}", + + "col /= s; //divide by sample count", + "}", + + "if (showFocus) {", + "col = debugFocus(col, blur, depth);", + "}", + + "if (vignetting) {", + "col *= vignette();", + "}", + + "gl_FragColor.rgb = col;", + "gl_FragColor.a = 1.0;", + "} " + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/BrightnessContrastShader.js b/node_modules/three/examples/js/shaders/BrightnessContrastShader.js new file mode 100644 index 00000000..5d8b3fc2 --- /dev/null +++ b/node_modules/three/examples/js/shaders/BrightnessContrastShader.js @@ -0,0 +1,58 @@ +/** + * @author tapio / http://tapio.github.com/ + * + * Brightness and contrast adjustment + * https://github.com/evanw/glfx.js + * brightness: -1 to 1 (-1 is solid black, 0 is no change, and 1 is solid white) + * contrast: -1 to 1 (-1 is solid gray, 0 is no change, and 1 is maximum contrast) + */ + +THREE.BrightnessContrastShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "brightness": { type: "f", value: 0 }, + "contrast": { type: "f", value: 0 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tDiffuse;", + "uniform float brightness;", + "uniform float contrast;", + + "varying vec2 vUv;", + + "void main() {", + + "gl_FragColor = texture2D( tDiffuse, vUv );", + + "gl_FragColor.rgb += brightness;", + + "if (contrast > 0.0) {", + "gl_FragColor.rgb = (gl_FragColor.rgb - 0.5) / (1.0 - contrast) + 0.5;", + "} else {", + "gl_FragColor.rgb = (gl_FragColor.rgb - 0.5) * (1.0 + contrast) + 0.5;", + "}", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/ColorCorrectionShader.js b/node_modules/three/examples/js/shaders/ColorCorrectionShader.js new file mode 100644 index 00000000..c5e6fc6a --- /dev/null +++ b/node_modules/three/examples/js/shaders/ColorCorrectionShader.js @@ -0,0 +1,50 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Color correction + */ + +THREE.ColorCorrectionShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "powRGB": { type: "v3", value: new THREE.Vector3( 2, 2, 2 ) }, + "mulRGB": { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) }, + "addRGB": { type: "v3", value: new THREE.Vector3( 0, 0, 0 ) } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tDiffuse;", + "uniform vec3 powRGB;", + "uniform vec3 mulRGB;", + "uniform vec3 addRGB;", + + "varying vec2 vUv;", + + "void main() {", + + "gl_FragColor = texture2D( tDiffuse, vUv );", + "gl_FragColor.rgb = mulRGB * pow( ( gl_FragColor.rgb + addRGB ), powRGB );", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/ColorifyShader.js b/node_modules/three/examples/js/shaders/ColorifyShader.js new file mode 100644 index 00000000..56c33c81 --- /dev/null +++ b/node_modules/three/examples/js/shaders/ColorifyShader.js @@ -0,0 +1,49 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Colorify shader + */ + +THREE.ColorifyShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "color": { type: "c", value: new THREE.Color( 0xffffff ) } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 color;", + "uniform sampler2D tDiffuse;", + + "varying vec2 vUv;", + + "void main() {", + + "vec4 texel = texture2D( tDiffuse, vUv );", + + "vec3 luma = vec3( 0.299, 0.587, 0.114 );", + "float v = dot( texel.xyz, luma );", + + "gl_FragColor = vec4( v * color, texel.w );", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/ConvolutionShader.js b/node_modules/three/examples/js/shaders/ConvolutionShader.js new file mode 100644 index 00000000..513a27ff --- /dev/null +++ b/node_modules/three/examples/js/shaders/ConvolutionShader.js @@ -0,0 +1,101 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Convolution shader + * ported from o3d sample to WebGL / GLSL + * http://o3d.googlecode.com/svn/trunk/samples/convolution.html + */ + +THREE.ConvolutionShader = { + + defines: { + + "KERNEL_SIZE_FLOAT": "25.0", + "KERNEL_SIZE_INT": "25", + + }, + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "uImageIncrement": { type: "v2", value: new THREE.Vector2( 0.001953125, 0.0 ) }, + "cKernel": { type: "fv1", value: [] } + + }, + + vertexShader: [ + + "uniform vec2 uImageIncrement;", + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv - ( ( KERNEL_SIZE_FLOAT - 1.0 ) / 2.0 ) * uImageIncrement;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform float cKernel[ KERNEL_SIZE_INT ];", + + "uniform sampler2D tDiffuse;", + "uniform vec2 uImageIncrement;", + + "varying vec2 vUv;", + + "void main() {", + + "vec2 imageCoord = vUv;", + "vec4 sum = vec4( 0.0, 0.0, 0.0, 0.0 );", + + "for( int i = 0; i < KERNEL_SIZE_INT; i ++ ) {", + + "sum += texture2D( tDiffuse, imageCoord ) * cKernel[ i ];", + "imageCoord += uImageIncrement;", + + "}", + + "gl_FragColor = sum;", + + "}" + + + ].join( "\n" ), + + buildKernel: function ( sigma ) { + + // We lop off the sqrt(2 * pi) * sigma term, since we're going to normalize anyway. + + function gauss( x, sigma ) { + + return Math.exp( - ( x * x ) / ( 2.0 * sigma * sigma ) ); + + } + + var i, values, sum, halfWidth, kMaxKernelSize = 25, kernelSize = 2 * Math.ceil( sigma * 3.0 ) + 1; + + if ( kernelSize > kMaxKernelSize ) kernelSize = kMaxKernelSize; + halfWidth = ( kernelSize - 1 ) * 0.5; + + values = new Array( kernelSize ); + sum = 0.0; + for ( i = 0; i < kernelSize; ++ i ) { + + values[ i ] = gauss( i - halfWidth, sigma ); + sum += values[ i ]; + + } + + // normalize the kernel + + for ( i = 0; i < kernelSize; ++ i ) values[ i ] /= sum; + + return values; + + } + +}; diff --git a/node_modules/three/examples/js/shaders/CopyShader.js b/node_modules/three/examples/js/shaders/CopyShader.js new file mode 100644 index 00000000..ee268237 --- /dev/null +++ b/node_modules/three/examples/js/shaders/CopyShader.js @@ -0,0 +1,46 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Full-screen textured quad shader + */ + +THREE.CopyShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "opacity": { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform float opacity;", + + "uniform sampler2D tDiffuse;", + + "varying vec2 vUv;", + + "void main() {", + + "vec4 texel = texture2D( tDiffuse, vUv );", + "gl_FragColor = opacity * texel;", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/DOFMipMapShader.js b/node_modules/three/examples/js/shaders/DOFMipMapShader.js new file mode 100644 index 00000000..1eebbfd2 --- /dev/null +++ b/node_modules/three/examples/js/shaders/DOFMipMapShader.js @@ -0,0 +1,58 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Depth-of-field shader using mipmaps + * - from Matt Handley @applmak + * - requires power-of-2 sized render target with enabled mipmaps + */ + +THREE.DOFMipMapShader = { + + uniforms: { + + "tColor": { type: "t", value: null }, + "tDepth": { type: "t", value: null }, + "focus": { type: "f", value: 1.0 }, + "maxblur": { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform float focus;", + "uniform float maxblur;", + + "uniform sampler2D tColor;", + "uniform sampler2D tDepth;", + + "varying vec2 vUv;", + + "void main() {", + + "vec4 depth = texture2D( tDepth, vUv );", + + "float factor = depth.x - focus;", + + "vec4 col = texture2D( tColor, vUv, 2.0 * maxblur * abs( focus - depth.x ) );", + + "gl_FragColor = col;", + "gl_FragColor.a = 1.0;", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/DigitalGlitch.js b/node_modules/three/examples/js/shaders/DigitalGlitch.js new file mode 100644 index 00000000..fe8b0bdc --- /dev/null +++ b/node_modules/three/examples/js/shaders/DigitalGlitch.js @@ -0,0 +1,103 @@ +/** + * @author felixturner / http://airtight.cc/ + * + * RGB Shift Shader + * Shifts red and blue channels from center in opposite directions + * Ported from http://kriss.cx/tom/2009/05/rgb-shift/ + * by Tom Butterworth / http://kriss.cx/tom/ + * + * amount: shift distance (1 is width of input) + * angle: shift angle in radians + */ + +THREE.DigitalGlitch = { + + uniforms: { + + "tDiffuse": { type: "t", value: null },//diffuse texture + "tDisp": { type: "t", value: null },//displacement texture for digital glitch squares + "byp": { type: "i", value: 0 },//apply the glitch ? + "amount": { type: "f", value: 0.08 }, + "angle": { type: "f", value: 0.02 }, + "seed": { type: "f", value: 0.02 }, + "seed_x": { type: "f", value: 0.02 },//-1,1 + "seed_y": { type: "f", value: 0.02 },//-1,1 + "distortion_x": { type: "f", value: 0.5 }, + "distortion_y": { type: "f", value: 0.6 }, + "col_s": { type: "f", value: 0.05 } + }, + + vertexShader: [ + + "varying vec2 vUv;", + "void main() {", + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + "}" + ].join( "\n" ), + + fragmentShader: [ + "uniform int byp;",//should we apply the glitch ? + + "uniform sampler2D tDiffuse;", + "uniform sampler2D tDisp;", + + "uniform float amount;", + "uniform float angle;", + "uniform float seed;", + "uniform float seed_x;", + "uniform float seed_y;", + "uniform float distortion_x;", + "uniform float distortion_y;", + "uniform float col_s;", + + "varying vec2 vUv;", + + + "float rand(vec2 co){", + "return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);", + "}", + + "void main() {", + "if(byp<1) {", + "vec2 p = vUv;", + "float xs = floor(gl_FragCoord.x / 0.5);", + "float ys = floor(gl_FragCoord.y / 0.5);", + //based on staffantans glitch shader for unity https://github.com/staffantan/unityglitch + "vec4 normal = texture2D (tDisp, p*seed*seed);", + "if(p.ydistortion_x-col_s*seed) {", + "if(seed_x>0.){", + "p.y = 1. - (p.y + distortion_y);", + "}", + "else {", + "p.y = distortion_y;", + "}", + "}", + "if(p.xdistortion_y-col_s*seed) {", + "if(seed_y>0.){", + "p.x=distortion_x;", + "}", + "else {", + "p.x = 1. - (p.x + distortion_x);", + "}", + "}", + "p.x+=normal.x*seed_x*(seed/5.);", + "p.y+=normal.y*seed_y*(seed/5.);", + //base from RGB shift shader + "vec2 offset = amount * vec2( cos(angle), sin(angle));", + "vec4 cr = texture2D(tDiffuse, p + offset);", + "vec4 cga = texture2D(tDiffuse, p);", + "vec4 cb = texture2D(tDiffuse, p - offset);", + "gl_FragColor = vec4(cr.r, cga.g, cb.b, cga.a);", + //add noise + "vec4 snow = 200.*amount*vec4(rand(vec2(xs * seed,ys * seed*50.))*0.2);", + "gl_FragColor = gl_FragColor+ snow;", + "}", + "else {", + "gl_FragColor=texture2D (tDiffuse, vUv);", + "}", + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/DotScreenShader.js b/node_modules/three/examples/js/shaders/DotScreenShader.js new file mode 100644 index 00000000..5f0fb9f4 --- /dev/null +++ b/node_modules/three/examples/js/shaders/DotScreenShader.js @@ -0,0 +1,68 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Dot screen shader + * based on glfx.js sepia shader + * https://github.com/evanw/glfx.js + */ + +THREE.DotScreenShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "tSize": { type: "v2", value: new THREE.Vector2( 256, 256 ) }, + "center": { type: "v2", value: new THREE.Vector2( 0.5, 0.5 ) }, + "angle": { type: "f", value: 1.57 }, + "scale": { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec2 center;", + "uniform float angle;", + "uniform float scale;", + "uniform vec2 tSize;", + + "uniform sampler2D tDiffuse;", + + "varying vec2 vUv;", + + "float pattern() {", + + "float s = sin( angle ), c = cos( angle );", + + "vec2 tex = vUv * tSize - center;", + "vec2 point = vec2( c * tex.x - s * tex.y, s * tex.x + c * tex.y ) * scale;", + + "return ( sin( point.x ) * sin( point.y ) ) * 4.0;", + + "}", + + "void main() {", + + "vec4 color = texture2D( tDiffuse, vUv );", + + "float average = ( color.r + color.g + color.b ) / 3.0;", + + "gl_FragColor = vec4( vec3( average * 10.0 - 5.0 + pattern() ), color.a );", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/EdgeShader.js b/node_modules/three/examples/js/shaders/EdgeShader.js new file mode 100644 index 00000000..1d9a09dd --- /dev/null +++ b/node_modules/three/examples/js/shaders/EdgeShader.js @@ -0,0 +1,93 @@ +/** + * @author zz85 / https://github.com/zz85 | https://www.lab4games.net/zz85/blog + * + * Edge Detection Shader using Frei-Chen filter + * Based on http://rastergrid.com/blog/2011/01/frei-chen-edge-detector + * + * aspect: vec2 of (1/width, 1/height) + */ + +THREE.EdgeShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "aspect": { type: "v2", value: new THREE.Vector2( 512, 512 ) }, + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tDiffuse;", + "varying vec2 vUv;", + + "uniform vec2 aspect;", + + "vec2 texel = vec2(1.0 / aspect.x, 1.0 / aspect.y);", + + + "mat3 G[9];", + + // hard coded matrix values!!!! as suggested in https://github.com/neilmendoza/ofxPostProcessing/blob/master/src/EdgePass.cpp#L45 + + "const mat3 g0 = mat3( 0.3535533845424652, 0, -0.3535533845424652, 0.5, 0, -0.5, 0.3535533845424652, 0, -0.3535533845424652 );", + "const mat3 g1 = mat3( 0.3535533845424652, 0.5, 0.3535533845424652, 0, 0, 0, -0.3535533845424652, -0.5, -0.3535533845424652 );", + "const mat3 g2 = mat3( 0, 0.3535533845424652, -0.5, -0.3535533845424652, 0, 0.3535533845424652, 0.5, -0.3535533845424652, 0 );", + "const mat3 g3 = mat3( 0.5, -0.3535533845424652, 0, -0.3535533845424652, 0, 0.3535533845424652, 0, 0.3535533845424652, -0.5 );", + "const mat3 g4 = mat3( 0, -0.5, 0, 0.5, 0, 0.5, 0, -0.5, 0 );", + "const mat3 g5 = mat3( -0.5, 0, 0.5, 0, 0, 0, 0.5, 0, -0.5 );", + "const mat3 g6 = mat3( 0.1666666716337204, -0.3333333432674408, 0.1666666716337204, -0.3333333432674408, 0.6666666865348816, -0.3333333432674408, 0.1666666716337204, -0.3333333432674408, 0.1666666716337204 );", + "const mat3 g7 = mat3( -0.3333333432674408, 0.1666666716337204, -0.3333333432674408, 0.1666666716337204, 0.6666666865348816, 0.1666666716337204, -0.3333333432674408, 0.1666666716337204, -0.3333333432674408 );", + "const mat3 g8 = mat3( 0.3333333432674408, 0.3333333432674408, 0.3333333432674408, 0.3333333432674408, 0.3333333432674408, 0.3333333432674408, 0.3333333432674408, 0.3333333432674408, 0.3333333432674408 );", + + "void main(void)", + "{", + + "G[0] = g0,", + "G[1] = g1,", + "G[2] = g2,", + "G[3] = g3,", + "G[4] = g4,", + "G[5] = g5,", + "G[6] = g6,", + "G[7] = g7,", + "G[8] = g8;", + + "mat3 I;", + "float cnv[9];", + "vec3 sample;", + + /* fetch the 3x3 neighbourhood and use the RGB vector's length as intensity value */ + "for (float i=0.0; i<3.0; i++) {", + "for (float j=0.0; j<3.0; j++) {", + "sample = texture2D(tDiffuse, vUv + texel * vec2(i-1.0,j-1.0) ).rgb;", + "I[int(i)][int(j)] = length(sample);", + "}", + "}", + + /* calculate the convolution values for all the masks */ + "for (int i=0; i<9; i++) {", + "float dp3 = dot(G[i][0], I[0]) + dot(G[i][1], I[1]) + dot(G[i][2], I[2]);", + "cnv[i] = dp3 * dp3;", + "}", + + "float M = (cnv[0] + cnv[1]) + (cnv[2] + cnv[3]);", + "float S = (cnv[4] + cnv[5]) + (cnv[6] + cnv[7]) + (cnv[8] + M);", + + "gl_FragColor = vec4(vec3(sqrt(M/S)), 1.0);", + "}", + + ].join( "\n" ) +}; diff --git a/node_modules/three/examples/js/shaders/EdgeShader2.js b/node_modules/three/examples/js/shaders/EdgeShader2.js new file mode 100644 index 00000000..e071b117 --- /dev/null +++ b/node_modules/three/examples/js/shaders/EdgeShader2.js @@ -0,0 +1,73 @@ +/** + * @author zz85 / https://github.com/zz85 | https://www.lab4games.net/zz85/blog + * + * Edge Detection Shader using Sobel filter + * Based on http://rastergrid.com/blog/2011/01/frei-chen-edge-detector + * + * aspect: vec2 of (1/width, 1/height) + */ + +THREE.EdgeShader2 = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "aspect": { type: "v2", value: new THREE.Vector2( 512, 512 ) }, + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tDiffuse;", + "varying vec2 vUv;", + "uniform vec2 aspect;", + + + "vec2 texel = vec2(1.0 / aspect.x, 1.0 / aspect.y);", + + "mat3 G[2];", + + "const mat3 g0 = mat3( 1.0, 2.0, 1.0, 0.0, 0.0, 0.0, -1.0, -2.0, -1.0 );", + "const mat3 g1 = mat3( 1.0, 0.0, -1.0, 2.0, 0.0, -2.0, 1.0, 0.0, -1.0 );", + + + "void main(void)", + "{", + "mat3 I;", + "float cnv[2];", + "vec3 sample;", + + "G[0] = g0;", + "G[1] = g1;", + + /* fetch the 3x3 neighbourhood and use the RGB vector's length as intensity value */ + "for (float i=0.0; i<3.0; i++)", + "for (float j=0.0; j<3.0; j++) {", + "sample = texture2D( tDiffuse, vUv + texel * vec2(i-1.0,j-1.0) ).rgb;", + "I[int(i)][int(j)] = length(sample);", + "}", + + /* calculate the convolution values for all the masks */ + "for (int i=0; i<2; i++) {", + "float dp3 = dot(G[i][0], I[0]) + dot(G[i][1], I[1]) + dot(G[i][2], I[2]);", + "cnv[i] = dp3 * dp3; ", + "}", + + "gl_FragColor = vec4(0.5 * sqrt(cnv[0]*cnv[0]+cnv[1]*cnv[1]));", + "} ", + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/FXAAShader.js b/node_modules/three/examples/js/shaders/FXAAShader.js new file mode 100644 index 00000000..7cd806e9 --- /dev/null +++ b/node_modules/three/examples/js/shaders/FXAAShader.js @@ -0,0 +1,88 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * @author davidedc / http://www.sketchpatch.net/ + * + * NVIDIA FXAA by Timothy Lottes + * http://timothylottes.blogspot.com/2011/06/fxaa3-source-released.html + * - WebGL port by @supereggbert + * http://www.glge.org/demos/fxaa/ + */ + +THREE.FXAAShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "resolution": { type: "v2", value: new THREE.Vector2( 1 / 1024, 1 / 512 ) } + + }, + + vertexShader: [ + + "void main() {", + + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tDiffuse;", + "uniform vec2 resolution;", + + "#define FXAA_REDUCE_MIN (1.0/128.0)", + "#define FXAA_REDUCE_MUL (1.0/8.0)", + "#define FXAA_SPAN_MAX 8.0", + + "void main() {", + + "vec3 rgbNW = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( -1.0, -1.0 ) ) * resolution ).xyz;", + "vec3 rgbNE = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( 1.0, -1.0 ) ) * resolution ).xyz;", + "vec3 rgbSW = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( -1.0, 1.0 ) ) * resolution ).xyz;", + "vec3 rgbSE = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( 1.0, 1.0 ) ) * resolution ).xyz;", + "vec4 rgbaM = texture2D( tDiffuse, gl_FragCoord.xy * resolution );", + "vec3 rgbM = rgbaM.xyz;", + "vec3 luma = vec3( 0.299, 0.587, 0.114 );", + + "float lumaNW = dot( rgbNW, luma );", + "float lumaNE = dot( rgbNE, luma );", + "float lumaSW = dot( rgbSW, luma );", + "float lumaSE = dot( rgbSE, luma );", + "float lumaM = dot( rgbM, luma );", + "float lumaMin = min( lumaM, min( min( lumaNW, lumaNE ), min( lumaSW, lumaSE ) ) );", + "float lumaMax = max( lumaM, max( max( lumaNW, lumaNE) , max( lumaSW, lumaSE ) ) );", + + "vec2 dir;", + "dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));", + "dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));", + + "float dirReduce = max( ( lumaNW + lumaNE + lumaSW + lumaSE ) * ( 0.25 * FXAA_REDUCE_MUL ), FXAA_REDUCE_MIN );", + + "float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce );", + "dir = min( vec2( FXAA_SPAN_MAX, FXAA_SPAN_MAX),", + "max( vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),", + "dir * rcpDirMin)) * resolution;", + "vec4 rgbA = (1.0/2.0) * (", + "texture2D(tDiffuse, gl_FragCoord.xy * resolution + dir * (1.0/3.0 - 0.5)) +", + "texture2D(tDiffuse, gl_FragCoord.xy * resolution + dir * (2.0/3.0 - 0.5)));", + "vec4 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * (", + "texture2D(tDiffuse, gl_FragCoord.xy * resolution + dir * (0.0/3.0 - 0.5)) +", + "texture2D(tDiffuse, gl_FragCoord.xy * resolution + dir * (3.0/3.0 - 0.5)));", + "float lumaB = dot(rgbB, vec4(luma, 0.0));", + + "if ( ( lumaB < lumaMin ) || ( lumaB > lumaMax ) ) {", + + "gl_FragColor = rgbA;", + + "} else {", + "gl_FragColor = rgbB;", + + "}", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/FilmShader.js b/node_modules/three/examples/js/shaders/FilmShader.js new file mode 100644 index 00000000..6a6c545c --- /dev/null +++ b/node_modules/three/examples/js/shaders/FilmShader.js @@ -0,0 +1,104 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Film grain & scanlines shader + * + * - ported from HLSL to WebGL / GLSL + * http://www.truevision3d.com/forums/showcase/staticnoise_colorblackwhite_scanline_shaders-t18698.0.html + * + * Screen Space Static Postprocessor + * + * Produces an analogue noise overlay similar to a film grain / TV static + * + * Original implementation and noise algorithm + * Pat 'Hawthorne' Shearon + * + * Optimized scanlines + noise version with intensity scaling + * Georg 'Leviathan' Steinrohder + * + * This version is provided under a Creative Commons Attribution 3.0 License + * http://creativecommons.org/licenses/by/3.0/ + */ + +THREE.FilmShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "time": { type: "f", value: 0.0 }, + "nIntensity": { type: "f", value: 0.5 }, + "sIntensity": { type: "f", value: 0.05 }, + "sCount": { type: "f", value: 4096 }, + "grayscale": { type: "i", value: 1 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + // control parameter + "uniform float time;", + + "uniform bool grayscale;", + + // noise effect intensity value (0 = no effect, 1 = full effect) + "uniform float nIntensity;", + + // scanlines effect intensity value (0 = no effect, 1 = full effect) + "uniform float sIntensity;", + + // scanlines effect count value (0 = no effect, 4096 = full effect) + "uniform float sCount;", + + "uniform sampler2D tDiffuse;", + + "varying vec2 vUv;", + + "void main() {", + + // sample the source + "vec4 cTextureScreen = texture2D( tDiffuse, vUv );", + + // make some noise + "float x = vUv.x * vUv.y * time * 1000.0;", + "x = mod( x, 13.0 ) * mod( x, 123.0 );", + "float dx = mod( x, 0.01 );", + + // add noise + "vec3 cResult = cTextureScreen.rgb + cTextureScreen.rgb * clamp( 0.1 + dx * 100.0, 0.0, 1.0 );", + + // get us a sine and cosine + "vec2 sc = vec2( sin( vUv.y * sCount ), cos( vUv.y * sCount ) );", + + // add scanlines + "cResult += cTextureScreen.rgb * vec3( sc.x, sc.y, sc.x ) * sIntensity;", + + // interpolate between source and result by intensity + "cResult = cTextureScreen.rgb + clamp( nIntensity, 0.0,1.0 ) * ( cResult - cTextureScreen.rgb );", + + // convert to grayscale if desired + "if( grayscale ) {", + + "cResult = vec3( cResult.r * 0.3 + cResult.g * 0.59 + cResult.b * 0.11 );", + + "}", + + "gl_FragColor = vec4( cResult, cTextureScreen.a );", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/FocusShader.js b/node_modules/three/examples/js/shaders/FocusShader.js new file mode 100644 index 00000000..619a5638 --- /dev/null +++ b/node_modules/three/examples/js/shaders/FocusShader.js @@ -0,0 +1,91 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Focus shader + * based on PaintEffect postprocess from ro.me + * http://code.google.com/p/3-dreams-of-black/source/browse/deploy/js/effects/PaintEffect.js + */ + +THREE.FocusShader = { + + uniforms : { + + "tDiffuse": { type: "t", value: null }, + "screenWidth": { type: "f", value: 1024 }, + "screenHeight": { type: "f", value: 1024 }, + "sampleDistance": { type: "f", value: 0.94 }, + "waveFactor": { type: "f", value: 0.00125 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform float screenWidth;", + "uniform float screenHeight;", + "uniform float sampleDistance;", + "uniform float waveFactor;", + + "uniform sampler2D tDiffuse;", + + "varying vec2 vUv;", + + "void main() {", + + "vec4 color, org, tmp, add;", + "float sample_dist, f;", + "vec2 vin;", + "vec2 uv = vUv;", + + "add = color = org = texture2D( tDiffuse, uv );", + + "vin = ( uv - vec2( 0.5 ) ) * vec2( 1.4 );", + "sample_dist = dot( vin, vin ) * 2.0;", + + "f = ( waveFactor * 100.0 + sample_dist ) * sampleDistance * 4.0;", + + "vec2 sampleSize = vec2( 1.0 / screenWidth, 1.0 / screenHeight ) * vec2( f );", + + "add += tmp = texture2D( tDiffuse, uv + vec2( 0.111964, 0.993712 ) * sampleSize );", + "if( tmp.b < color.b ) color = tmp;", + + "add += tmp = texture2D( tDiffuse, uv + vec2( 0.846724, 0.532032 ) * sampleSize );", + "if( tmp.b < color.b ) color = tmp;", + + "add += tmp = texture2D( tDiffuse, uv + vec2( 0.943883, -0.330279 ) * sampleSize );", + "if( tmp.b < color.b ) color = tmp;", + + "add += tmp = texture2D( tDiffuse, uv + vec2( 0.330279, -0.943883 ) * sampleSize );", + "if( tmp.b < color.b ) color = tmp;", + + "add += tmp = texture2D( tDiffuse, uv + vec2( -0.532032, -0.846724 ) * sampleSize );", + "if( tmp.b < color.b ) color = tmp;", + + "add += tmp = texture2D( tDiffuse, uv + vec2( -0.993712, -0.111964 ) * sampleSize );", + "if( tmp.b < color.b ) color = tmp;", + + "add += tmp = texture2D( tDiffuse, uv + vec2( -0.707107, 0.707107 ) * sampleSize );", + "if( tmp.b < color.b ) color = tmp;", + + "color = color * vec4( 2.0 ) - ( add / vec4( 8.0 ) );", + "color = color + ( add / vec4( 8.0 ) - color ) * ( vec4( 1.0 ) - vec4( sample_dist * 0.5 ) );", + + "gl_FragColor = vec4( color.rgb * color.rgb * vec3( 0.95 ) + color.rgb, 1.0 );", + + "}" + + + ].join( "\n" ) +}; diff --git a/node_modules/three/examples/js/shaders/FresnelShader.js b/node_modules/three/examples/js/shaders/FresnelShader.js new file mode 100644 index 00000000..f4ad7229 --- /dev/null +++ b/node_modules/three/examples/js/shaders/FresnelShader.js @@ -0,0 +1,74 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Based on Nvidia Cg tutorial + */ + +THREE.FresnelShader = { + + uniforms: { + + "mRefractionRatio": { type: "f", value: 1.02 }, + "mFresnelBias": { type: "f", value: 0.1 }, + "mFresnelPower": { type: "f", value: 2.0 }, + "mFresnelScale": { type: "f", value: 1.0 }, + "tCube": { type: "t", value: null } + + }, + + vertexShader: [ + + "uniform float mRefractionRatio;", + "uniform float mFresnelBias;", + "uniform float mFresnelScale;", + "uniform float mFresnelPower;", + + "varying vec3 vReflect;", + "varying vec3 vRefract[3];", + "varying float vReflectionFactor;", + + "void main() {", + + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + "vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", + + "vec3 worldNormal = normalize( mat3( modelMatrix[0].xyz, modelMatrix[1].xyz, modelMatrix[2].xyz ) * normal );", + + "vec3 I = worldPosition.xyz - cameraPosition;", + + "vReflect = reflect( I, worldNormal );", + "vRefract[0] = refract( normalize( I ), worldNormal, mRefractionRatio );", + "vRefract[1] = refract( normalize( I ), worldNormal, mRefractionRatio * 0.99 );", + "vRefract[2] = refract( normalize( I ), worldNormal, mRefractionRatio * 0.98 );", + "vReflectionFactor = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( I ), worldNormal ), mFresnelPower );", + + "gl_Position = projectionMatrix * mvPosition;", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform samplerCube tCube;", + + "varying vec3 vReflect;", + "varying vec3 vRefract[3];", + "varying float vReflectionFactor;", + + "void main() {", + + "vec4 reflectedColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );", + "vec4 refractedColor = vec4( 1.0 );", + + "refractedColor.r = textureCube( tCube, vec3( -vRefract[0].x, vRefract[0].yz ) ).r;", + "refractedColor.g = textureCube( tCube, vec3( -vRefract[1].x, vRefract[1].yz ) ).g;", + "refractedColor.b = textureCube( tCube, vec3( -vRefract[2].x, vRefract[2].yz ) ).b;", + + "gl_FragColor = mix( refractedColor, reflectedColor, clamp( vReflectionFactor, 0.0, 1.0 ) );", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/GammaCorrectionShader.js b/node_modules/three/examples/js/shaders/GammaCorrectionShader.js new file mode 100644 index 00000000..269ec882 --- /dev/null +++ b/node_modules/three/examples/js/shaders/GammaCorrectionShader.js @@ -0,0 +1,50 @@ +/** + * @author WestLangley / http://github.com/WestLangley + * + * Gamma Correction Shader + * http://en.wikipedia.org/wiki/gamma_correction + */ + +THREE.GammaCorrectionShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "#define GAMMA_OUTPUT", + "#define GAMMA_FACTOR 2", + + "uniform sampler2D tDiffuse;", + + "varying vec2 vUv;", + + THREE.ShaderChunk[ "common" ], + + "void main() {", + + "vec4 tex = texture2D( tDiffuse, vec2( vUv.x, vUv.y ) );", + + "gl_FragColor = vec4( linearToOutput( tex.rgb ), tex.a );", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/HorizontalBlurShader.js b/node_modules/three/examples/js/shaders/HorizontalBlurShader.js new file mode 100644 index 00000000..cf114544 --- /dev/null +++ b/node_modules/three/examples/js/shaders/HorizontalBlurShader.js @@ -0,0 +1,62 @@ +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + * Two pass Gaussian blur filter (horizontal and vertical blur shaders) + * - described in http://www.gamerendering.com/2008/10/11/gaussian-blur-filter-shader/ + * and used in http://www.cake23.de/traveling-wavefronts-lit-up.html + * + * - 9 samples per pass + * - standard deviation 2.7 + * - "h" and "v" parameters should be set to "1 / width" and "1 / height" + */ + +THREE.HorizontalBlurShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "h": { type: "f", value: 1.0 / 512.0 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tDiffuse;", + "uniform float h;", + + "varying vec2 vUv;", + + "void main() {", + + "vec4 sum = vec4( 0.0 );", + + "sum += texture2D( tDiffuse, vec2( vUv.x - 4.0 * h, vUv.y ) ) * 0.051;", + "sum += texture2D( tDiffuse, vec2( vUv.x - 3.0 * h, vUv.y ) ) * 0.0918;", + "sum += texture2D( tDiffuse, vec2( vUv.x - 2.0 * h, vUv.y ) ) * 0.12245;", + "sum += texture2D( tDiffuse, vec2( vUv.x - 1.0 * h, vUv.y ) ) * 0.1531;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y ) ) * 0.1633;", + "sum += texture2D( tDiffuse, vec2( vUv.x + 1.0 * h, vUv.y ) ) * 0.1531;", + "sum += texture2D( tDiffuse, vec2( vUv.x + 2.0 * h, vUv.y ) ) * 0.12245;", + "sum += texture2D( tDiffuse, vec2( vUv.x + 3.0 * h, vUv.y ) ) * 0.0918;", + "sum += texture2D( tDiffuse, vec2( vUv.x + 4.0 * h, vUv.y ) ) * 0.051;", + + "gl_FragColor = sum;", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/HorizontalTiltShiftShader.js b/node_modules/three/examples/js/shaders/HorizontalTiltShiftShader.js new file mode 100644 index 00000000..290ada42 --- /dev/null +++ b/node_modules/three/examples/js/shaders/HorizontalTiltShiftShader.js @@ -0,0 +1,65 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Simple fake tilt-shift effect, modulating two pass Gaussian blur (see above) by vertical position + * + * - 9 samples per pass + * - standard deviation 2.7 + * - "h" and "v" parameters should be set to "1 / width" and "1 / height" + * - "r" parameter control where "focused" horizontal line lies + */ + +THREE.HorizontalTiltShiftShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "h": { type: "f", value: 1.0 / 512.0 }, + "r": { type: "f", value: 0.35 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tDiffuse;", + "uniform float h;", + "uniform float r;", + + "varying vec2 vUv;", + + "void main() {", + + "vec4 sum = vec4( 0.0 );", + + "float hh = h * abs( r - vUv.y );", + + "sum += texture2D( tDiffuse, vec2( vUv.x - 4.0 * hh, vUv.y ) ) * 0.051;", + "sum += texture2D( tDiffuse, vec2( vUv.x - 3.0 * hh, vUv.y ) ) * 0.0918;", + "sum += texture2D( tDiffuse, vec2( vUv.x - 2.0 * hh, vUv.y ) ) * 0.12245;", + "sum += texture2D( tDiffuse, vec2( vUv.x - 1.0 * hh, vUv.y ) ) * 0.1531;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y ) ) * 0.1633;", + "sum += texture2D( tDiffuse, vec2( vUv.x + 1.0 * hh, vUv.y ) ) * 0.1531;", + "sum += texture2D( tDiffuse, vec2( vUv.x + 2.0 * hh, vUv.y ) ) * 0.12245;", + "sum += texture2D( tDiffuse, vec2( vUv.x + 3.0 * hh, vUv.y ) ) * 0.0918;", + "sum += texture2D( tDiffuse, vec2( vUv.x + 4.0 * hh, vUv.y ) ) * 0.051;", + + "gl_FragColor = sum;", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/HueSaturationShader.js b/node_modules/three/examples/js/shaders/HueSaturationShader.js new file mode 100644 index 00000000..82c226aa --- /dev/null +++ b/node_modules/three/examples/js/shaders/HueSaturationShader.js @@ -0,0 +1,69 @@ +/** + * @author tapio / http://tapio.github.com/ + * + * Hue and saturation adjustment + * https://github.com/evanw/glfx.js + * hue: -1 to 1 (-1 is 180 degrees in the negative direction, 0 is no change, etc. + * saturation: -1 to 1 (-1 is solid gray, 0 is no change, and 1 is maximum contrast) + */ + +THREE.HueSaturationShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "hue": { type: "f", value: 0 }, + "saturation": { type: "f", value: 0 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tDiffuse;", + "uniform float hue;", + "uniform float saturation;", + + "varying vec2 vUv;", + + "void main() {", + + "gl_FragColor = texture2D( tDiffuse, vUv );", + + // hue + "float angle = hue * 3.14159265;", + "float s = sin(angle), c = cos(angle);", + "vec3 weights = (vec3(2.0 * c, -sqrt(3.0) * s - c, sqrt(3.0) * s - c) + 1.0) / 3.0;", + "float len = length(gl_FragColor.rgb);", + "gl_FragColor.rgb = vec3(", + "dot(gl_FragColor.rgb, weights.xyz),", + "dot(gl_FragColor.rgb, weights.zxy),", + "dot(gl_FragColor.rgb, weights.yzx)", + ");", + + // saturation + "float average = (gl_FragColor.r + gl_FragColor.g + gl_FragColor.b) / 3.0;", + "if (saturation > 0.0) {", + "gl_FragColor.rgb += (average - gl_FragColor.rgb) * (1.0 - 1.0 / (1.001 - saturation));", + "} else {", + "gl_FragColor.rgb += (average - gl_FragColor.rgb) * (-saturation);", + "}", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/KaleidoShader.js b/node_modules/three/examples/js/shaders/KaleidoShader.js new file mode 100644 index 00000000..004de2cf --- /dev/null +++ b/node_modules/three/examples/js/shaders/KaleidoShader.js @@ -0,0 +1,60 @@ +/** + * @author felixturner / http://airtight.cc/ + * + * Kaleidoscope Shader + * Radial reflection around center point + * Ported from: http://pixelshaders.com/editor/ + * by Toby Schachman / http://tobyschachman.com/ + * + * sides: number of reflections + * angle: initial angle in radians + */ + +THREE.KaleidoShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "sides": { type: "f", value: 6.0 }, + "angle": { type: "f", value: 0.0 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tDiffuse;", + "uniform float sides;", + "uniform float angle;", + + "varying vec2 vUv;", + + "void main() {", + + "vec2 p = vUv - 0.5;", + "float r = length(p);", + "float a = atan(p.y, p.x) + angle;", + "float tau = 2. * 3.1416 ;", + "a = mod(a, tau/sides);", + "a = abs(a - tau/sides/2.) ;", + "p = r * vec2(cos(a), sin(a));", + "vec4 color = texture2D(tDiffuse, p + 0.5);", + "gl_FragColor = color;", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/LuminosityShader.js b/node_modules/three/examples/js/shaders/LuminosityShader.js new file mode 100644 index 00000000..f5ff85e4 --- /dev/null +++ b/node_modules/three/examples/js/shaders/LuminosityShader.js @@ -0,0 +1,50 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Luminosity + * http://en.wikipedia.org/wiki/Luminosity + */ + +THREE.LuminosityShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tDiffuse;", + + "varying vec2 vUv;", + + "void main() {", + + "vec4 texel = texture2D( tDiffuse, vUv );", + + "vec3 luma = vec3( 0.299, 0.587, 0.114 );", + + "float v = dot( texel.xyz, luma );", + + "gl_FragColor = vec4( v, v, v, texel.w );", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/MirrorShader.js b/node_modules/three/examples/js/shaders/MirrorShader.js new file mode 100644 index 00000000..b87f02f7 --- /dev/null +++ b/node_modules/three/examples/js/shaders/MirrorShader.js @@ -0,0 +1,58 @@ +/** + * @author felixturner / http://airtight.cc/ + * + * Mirror Shader + * Copies half the input to the other half + * + * side: side of input to mirror (0 = left, 1 = right, 2 = top, 3 = bottom) + */ + +THREE.MirrorShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "side": { type: "i", value: 1 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tDiffuse;", + "uniform int side;", + + "varying vec2 vUv;", + + "void main() {", + + "vec2 p = vUv;", + "if (side == 0){", + "if (p.x > 0.5) p.x = 1.0 - p.x;", + "}else if (side == 1){", + "if (p.x < 0.5) p.x = 1.0 - p.x;", + "}else if (side == 2){", + "if (p.y < 0.5) p.y = 1.0 - p.y;", + "}else if (side == 3){", + "if (p.y > 0.5) p.y = 1.0 - p.y;", + "} ", + "vec4 color = texture2D(tDiffuse, p);", + "gl_FragColor = color;", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/NormalMapShader.js b/node_modules/three/examples/js/shaders/NormalMapShader.js new file mode 100644 index 00000000..d2b419df --- /dev/null +++ b/node_modules/three/examples/js/shaders/NormalMapShader.js @@ -0,0 +1,53 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Normal map shader + * - compute normals from heightmap + */ + +THREE.NormalMapShader = { + + uniforms: { + + "heightMap": { type: "t", value: null }, + "resolution": { type: "v2", value: new THREE.Vector2( 512, 512 ) }, + "scale": { type: "v2", value: new THREE.Vector2( 1, 1 ) }, + "height": { type: "f", value: 0.05 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform float height;", + "uniform vec2 resolution;", + "uniform sampler2D heightMap;", + + "varying vec2 vUv;", + + "void main() {", + + "float val = texture2D( heightMap, vUv ).x;", + + "float valU = texture2D( heightMap, vUv + vec2( 1.0 / resolution.x, 0.0 ) ).x;", + "float valV = texture2D( heightMap, vUv + vec2( 0.0, 1.0 / resolution.y ) ).x;", + + "gl_FragColor = vec4( ( 0.5 * normalize( vec3( val - valU, val - valV, height ) ) + 0.5 ), 1.0 );", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/OceanShaders.js b/node_modules/three/examples/js/shaders/OceanShaders.js new file mode 100644 index 00000000..e955dd4d --- /dev/null +++ b/node_modules/three/examples/js/shaders/OceanShaders.js @@ -0,0 +1,388 @@ +// Author: Aleksandr Albert +// Website: www.routter.co.tt + +// Description: A deep water ocean shader set +// based on an implementation of a Tessendorf Waves +// originally presented by David Li ( www.david.li/waves ) + +// The general method is to apply shaders to simulation Framebuffers +// and then sample these framebuffers when rendering the ocean mesh + +// The set uses 7 shaders: + +// -- Simulation shaders +// [1] ocean_sim_vertex -> Vertex shader used to set up a 2x2 simulation plane centered at (0,0) +// [2] ocean_subtransform -> Fragment shader used to subtransform the mesh (generates the displacement map) +// [3] ocean_initial_spectrum -> Fragment shader used to set intitial wave frequency at a texel coordinate +// [4] ocean_phase -> Fragment shader used to set wave phase at a texel coordinate +// [5] ocean_spectrum -> Fragment shader used to set current wave frequency at a texel coordinate +// [6] ocean_normal -> Fragment shader used to set face normals at a texel coordinate + +// -- Rendering Shader +// [7] ocean_main -> Vertex and Fragment shader used to create the final render + + +THREE.ShaderLib[ 'ocean_sim_vertex' ] = { + varying: { + "vUV": { type: "v2" } + }, + vertexShader: [ + 'varying vec2 vUV;', + + 'void main (void) {', + 'vUV = position.xy * 0.5 + 0.5;', + 'gl_Position = vec4(position, 1.0 );', + '}' + ].join( '\n' ) +}; +THREE.ShaderLib[ 'ocean_subtransform' ] = { + uniforms: { + "u_input": { type: "t", value: null }, + "u_transformSize": { type: "f", value: 512.0 }, + "u_subtransformSize": { type: "f", value: 250.0 } + }, + varying: { + "vUV": { type: "v2" } + }, + fragmentShader: [ + //GPU FFT using a Stockham formulation + 'precision highp float;', + + 'const float PI = 3.14159265359;', + + 'uniform sampler2D u_input;', + 'uniform float u_transformSize;', + 'uniform float u_subtransformSize;', + + 'varying vec2 vUV;', + + 'vec2 multiplyComplex (vec2 a, vec2 b) {', + 'return vec2(a[0] * b[0] - a[1] * b[1], a[1] * b[0] + a[0] * b[1]);', + '}', + + 'void main (void) {', + '#ifdef HORIZONTAL', + 'float index = vUV.x * u_transformSize - 0.5;', + '#else', + 'float index = vUV.y * u_transformSize - 0.5;', + '#endif', + + 'float evenIndex = floor(index / u_subtransformSize) * (u_subtransformSize * 0.5) + mod(index, u_subtransformSize * 0.5);', + + //transform two complex sequences simultaneously + '#ifdef HORIZONTAL', + 'vec4 even = texture2D(u_input, vec2(evenIndex + 0.5, gl_FragCoord.y) / u_transformSize).rgba;', + 'vec4 odd = texture2D(u_input, vec2(evenIndex + u_transformSize * 0.5 + 0.5, gl_FragCoord.y) / u_transformSize).rgba;', + '#else', + 'vec4 even = texture2D(u_input, vec2(gl_FragCoord.x, evenIndex + 0.5) / u_transformSize).rgba;', + 'vec4 odd = texture2D(u_input, vec2(gl_FragCoord.x, evenIndex + u_transformSize * 0.5 + 0.5) / u_transformSize).rgba;', + '#endif', + + 'float twiddleArgument = -2.0 * PI * (index / u_subtransformSize);', + 'vec2 twiddle = vec2(cos(twiddleArgument), sin(twiddleArgument));', + + 'vec2 outputA = even.xy + multiplyComplex(twiddle, odd.xy);', + 'vec2 outputB = even.zw + multiplyComplex(twiddle, odd.zw);', + + 'gl_FragColor = vec4(outputA, outputB);', + '}' + ].join( '\n' ) +}; +THREE.ShaderLib[ 'ocean_initial_spectrum' ] = { + uniforms: { + "u_wind": { type: "v2", value: new THREE.Vector2( 10.0, 10.0 ) }, + "u_resolution": { type: "f", value: 512.0 }, + "u_size": { type: "f", value: 250.0 }, + }, + fragmentShader: [ + 'precision highp float;', + + 'const float PI = 3.14159265359;', + 'const float G = 9.81;', + 'const float KM = 370.0;', + 'const float CM = 0.23;', + + 'uniform vec2 u_wind;', + 'uniform float u_resolution;', + 'uniform float u_size;', + + 'float square (float x) {', + 'return x * x;', + '}', + + 'float omega (float k) {', + 'return sqrt(G * k * (1.0 + square(k / KM)));', + '}', + + 'float tanh (float x) {', + 'return (1.0 - exp(-2.0 * x)) / (1.0 + exp(-2.0 * x));', + '}', + + 'void main (void) {', + 'vec2 coordinates = gl_FragCoord.xy - 0.5;', + + 'float n = (coordinates.x < u_resolution * 0.5) ? coordinates.x : coordinates.x - u_resolution;', + 'float m = (coordinates.y < u_resolution * 0.5) ? coordinates.y : coordinates.y - u_resolution;', + + 'vec2 K = (2.0 * PI * vec2(n, m)) / u_size;', + 'float k = length(K);', + + 'float l_wind = length(u_wind);', + + 'float Omega = 0.84;', + 'float kp = G * square(Omega / l_wind);', + + 'float c = omega(k) / k;', + 'float cp = omega(kp) / kp;', + + 'float Lpm = exp(-1.25 * square(kp / k));', + 'float gamma = 1.7;', + 'float sigma = 0.08 * (1.0 + 4.0 * pow(Omega, -3.0));', + 'float Gamma = exp(-square(sqrt(k / kp) - 1.0) / 2.0 * square(sigma));', + 'float Jp = pow(gamma, Gamma);', + 'float Fp = Lpm * Jp * exp(-Omega / sqrt(10.0) * (sqrt(k / kp) - 1.0));', + 'float alphap = 0.006 * sqrt(Omega);', + 'float Bl = 0.5 * alphap * cp / c * Fp;', + + 'float z0 = 0.000037 * square(l_wind) / G * pow(l_wind / cp, 0.9);', + 'float uStar = 0.41 * l_wind / log(10.0 / z0);', + 'float alpham = 0.01 * ((uStar < CM) ? (1.0 + log(uStar / CM)) : (1.0 + 3.0 * log(uStar / CM)));', + 'float Fm = exp(-0.25 * square(k / KM - 1.0));', + 'float Bh = 0.5 * alpham * CM / c * Fm * Lpm;', + + 'float a0 = log(2.0) / 4.0;', + 'float am = 0.13 * uStar / CM;', + 'float Delta = tanh(a0 + 4.0 * pow(c / cp, 2.5) + am * pow(CM / c, 2.5));', + + 'float cosPhi = dot(normalize(u_wind), normalize(K));', + + 'float S = (1.0 / (2.0 * PI)) * pow(k, -4.0) * (Bl + Bh) * (1.0 + Delta * (2.0 * cosPhi * cosPhi - 1.0));', + + 'float dk = 2.0 * PI / u_size;', + 'float h = sqrt(S / 2.0) * dk;', + + 'if (K.x == 0.0 && K.y == 0.0) {', + 'h = 0.0;', //no DC term + '}', + 'gl_FragColor = vec4(h, 0.0, 0.0, 0.0);', + '}' + ].join( '\n' ) +}; +THREE.ShaderLib[ 'ocean_phase' ] = { + uniforms: { + "u_phases": { type: "t", value: null }, + "u_deltaTime": { type: "f", value: null }, + "u_resolution": { type: "f", value: null }, + "u_size": { type: "f", value: null }, + }, + varying: { + "vUV": { type: "v2" } + }, + fragmentShader: [ + 'precision highp float;', + + 'const float PI = 3.14159265359;', + 'const float G = 9.81;', + 'const float KM = 370.0;', + + 'varying vec2 vUV;', + + 'uniform sampler2D u_phases;', + 'uniform float u_deltaTime;', + 'uniform float u_resolution;', + 'uniform float u_size;', + + 'float omega (float k) {', + 'return sqrt(G * k * (1.0 + k * k / KM * KM));', + '}', + + 'void main (void) {', + 'float deltaTime = 1.0 / 60.0;', + 'vec2 coordinates = gl_FragCoord.xy - 0.5;', + 'float n = (coordinates.x < u_resolution * 0.5) ? coordinates.x : coordinates.x - u_resolution;', + 'float m = (coordinates.y < u_resolution * 0.5) ? coordinates.y : coordinates.y - u_resolution;', + 'vec2 waveVector = (2.0 * PI * vec2(n, m)) / u_size;', + + 'float phase = texture2D(u_phases, vUV).r;', + 'float deltaPhase = omega(length(waveVector)) * u_deltaTime;', + 'phase = mod(phase + deltaPhase, 2.0 * PI);', + + 'gl_FragColor = vec4(phase, 0.0, 0.0, 0.0);', + '}' + ].join( '\n' ) +}; +THREE.ShaderLib[ 'ocean_spectrum' ] = { + uniforms: { + "u_size": { type: "f", value: null }, + "u_resolution": { type: "f", value: null }, + "u_choppiness": { type: "f", value: null }, + "u_phases": { type: "t", value: null }, + "u_initialSpectrum": { type: "t", value: null }, + }, + varying: { + "vUV": { type: "v2" } + }, + fragmentShader: [ + 'precision highp float;', + + 'const float PI = 3.14159265359;', + 'const float G = 9.81;', + 'const float KM = 370.0;', + + 'varying vec2 vUV;', + + 'uniform float u_size;', + 'uniform float u_resolution;', + 'uniform float u_choppiness;', + 'uniform sampler2D u_phases;', + 'uniform sampler2D u_initialSpectrum;', + + 'vec2 multiplyComplex (vec2 a, vec2 b) {', + 'return vec2(a[0] * b[0] - a[1] * b[1], a[1] * b[0] + a[0] * b[1]);', + '}', + + 'vec2 multiplyByI (vec2 z) {', + 'return vec2(-z[1], z[0]);', + '}', + + 'float omega (float k) {', + 'return sqrt(G * k * (1.0 + k * k / KM * KM));', + '}', + + 'void main (void) {', + 'vec2 coordinates = gl_FragCoord.xy - 0.5;', + 'float n = (coordinates.x < u_resolution * 0.5) ? coordinates.x : coordinates.x - u_resolution;', + 'float m = (coordinates.y < u_resolution * 0.5) ? coordinates.y : coordinates.y - u_resolution;', + 'vec2 waveVector = (2.0 * PI * vec2(n, m)) / u_size;', + + 'float phase = texture2D(u_phases, vUV).r;', + 'vec2 phaseVector = vec2(cos(phase), sin(phase));', + + 'vec2 h0 = texture2D(u_initialSpectrum, vUV).rg;', + 'vec2 h0Star = texture2D(u_initialSpectrum, vec2(1.0 - vUV + 1.0 / u_resolution)).rg;', + 'h0Star.y *= -1.0;', + + 'vec2 h = multiplyComplex(h0, phaseVector) + multiplyComplex(h0Star, vec2(phaseVector.x, -phaseVector.y));', + + 'vec2 hX = -multiplyByI(h * (waveVector.x / length(waveVector))) * u_choppiness;', + 'vec2 hZ = -multiplyByI(h * (waveVector.y / length(waveVector))) * u_choppiness;', + + //no DC term + 'if (waveVector.x == 0.0 && waveVector.y == 0.0) {', + 'h = vec2(0.0);', + 'hX = vec2(0.0);', + 'hZ = vec2(0.0);', + '}', + + 'gl_FragColor = vec4(hX + multiplyByI(h), hZ);', + '}' + ].join( '\n' ) +}; +THREE.ShaderLib[ 'ocean_normals' ] = { + uniforms: { + "u_displacementMap": { type: "t", value: null }, + "u_resolution": { type: "f", value: null }, + "u_size": { type: "f", value: null }, + }, + varying: { + "vUV": { type: "v2" } + }, + fragmentShader: [ + 'precision highp float;', + + 'varying vec2 vUV;', + + 'uniform sampler2D u_displacementMap;', + 'uniform float u_resolution;', + 'uniform float u_size;', + + 'void main (void) {', + 'float texel = 1.0 / u_resolution;', + 'float texelSize = u_size / u_resolution;', + + 'vec3 center = texture2D(u_displacementMap, vUV).rgb;', + 'vec3 right = vec3(texelSize, 0.0, 0.0) + texture2D(u_displacementMap, vUV + vec2(texel, 0.0)).rgb - center;', + 'vec3 left = vec3(-texelSize, 0.0, 0.0) + texture2D(u_displacementMap, vUV + vec2(-texel, 0.0)).rgb - center;', + 'vec3 top = vec3(0.0, 0.0, -texelSize) + texture2D(u_displacementMap, vUV + vec2(0.0, -texel)).rgb - center;', + 'vec3 bottom = vec3(0.0, 0.0, texelSize) + texture2D(u_displacementMap, vUV + vec2(0.0, texel)).rgb - center;', + + 'vec3 topRight = cross(right, top);', + 'vec3 topLeft = cross(top, left);', + 'vec3 bottomLeft = cross(left, bottom);', + 'vec3 bottomRight = cross(bottom, right);', + + 'gl_FragColor = vec4(normalize(topRight + topLeft + bottomLeft + bottomRight), 1.0);', + '}' + ].join( '\n' ) +}; +THREE.ShaderLib[ 'ocean_main' ] = { + uniforms: { + "u_displacementMap": { type: "t", value: null }, + "u_normalMap": { type: "t", value: null }, + "u_geometrySize": { type: "f", value: null }, + "u_size": { type: "f", value: null }, + "u_projectionMatrix": { type: "m4", value: null }, + "u_viewMatrix": { type: "m4", value: null }, + "u_cameraPosition": { type: "v3", value: null }, + "u_skyColor": { type: "v3", value: null }, + "u_oceanColor": { type: "v3", value: null }, + "u_sunDirection": { type: "v3", value: null }, + "u_exposure": { type: "f", value: null }, + }, + varying: { + "vPos": { type: "v3" }, + "vUV": { type: "v2" } + }, + vertexShader: [ + 'precision highp float;', + + 'varying vec3 vPos;', + 'varying vec2 vUV;', + + 'uniform mat4 u_projectionMatrix;', + 'uniform mat4 u_viewMatrix;', + 'uniform float u_size;', + 'uniform float u_geometrySize;', + 'uniform sampler2D u_displacementMap;', + + 'void main (void) {', + 'vec3 newPos = position + texture2D(u_displacementMap, uv).rgb * (u_geometrySize / u_size);', + 'vPos = newPos;', + 'vUV = uv;', + 'gl_Position = u_projectionMatrix * u_viewMatrix * vec4(newPos, 1.0);', + '}' + ].join( '\n' ), + fragmentShader: [ + 'precision highp float;', + + 'varying vec3 vPos;', + 'varying vec2 vUV;', + + 'uniform sampler2D u_displacementMap;', + 'uniform sampler2D u_normalMap;', + 'uniform vec3 u_cameraPosition;', + 'uniform vec3 u_oceanColor;', + 'uniform vec3 u_skyColor;', + 'uniform vec3 u_sunDirection;', + 'uniform float u_exposure;', + + 'vec3 hdr (vec3 color, float exposure) {', + 'return 1.0 - exp(-color * exposure);', + '}', + + 'void main (void) {', + 'vec3 normal = texture2D(u_normalMap, vUV).rgb;', + + 'vec3 view = normalize(u_cameraPosition - vPos);', + 'float fresnel = 0.02 + 0.98 * pow(1.0 - dot(normal, view), 5.0);', + 'vec3 sky = fresnel * u_skyColor;', + + 'float diffuse = clamp(dot(normal, normalize(u_sunDirection)), 0.0, 1.0);', + 'vec3 water = (1.0 - fresnel) * u_oceanColor * u_skyColor * diffuse;', + + 'vec3 color = sky + water;', + + 'gl_FragColor = vec4(hdr(color, u_exposure), 1.0);', + '}' + ].join( '\n' ) +}; diff --git a/node_modules/three/examples/js/shaders/ParallaxShader.js b/node_modules/three/examples/js/shaders/ParallaxShader.js new file mode 100644 index 00000000..1a5e67dd --- /dev/null +++ b/node_modules/three/examples/js/shaders/ParallaxShader.js @@ -0,0 +1,184 @@ +// Parallax Occlusion shaders from +// http://sunandblackcat.com/tipFullView.php?topicid=28 +// No tangent-space transforms logic based on +// http://mmikkelsen3d.blogspot.sk/2012/02/parallaxpoc-mapping-and-no-tangent.html + +THREE.ParallaxShader = { + // Ordered from fastest to best quality. + modes: { + none: 'NO_PARALLAX', + basic: 'USE_BASIC_PARALLAX', + steep: 'USE_STEEP_PARALLAX', + occlusion: 'USE_OCLUSION_PARALLAX', // a.k.a. POM + relief: 'USE_RELIEF_PARALLAX', + }, + + uniforms: { + "bumpMap": { type: "t", value: null }, + "map": { type: "t", value: null }, + "parallaxScale": { type: "f", value: null }, + "parallaxMinLayers": { type: "f", value: null }, + "parallaxMaxLayers": { type: "f", value: null } + }, + + vertexShader: [ + "varying vec2 vUv;", + "varying vec3 vViewPosition;", + "varying vec3 vNormal;", + + "void main() {", + + "vUv = uv;", + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + "vViewPosition = -mvPosition.xyz;", + "vNormal = normalize( normalMatrix * normal );", + "gl_Position = projectionMatrix * mvPosition;", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + "uniform sampler2D bumpMap;", + "uniform sampler2D map;", + + "uniform float parallaxScale;", + "uniform float parallaxMinLayers;", + "uniform float parallaxMaxLayers;", + + "varying vec2 vUv;", + "varying vec3 vViewPosition;", + "varying vec3 vNormal;", + + "#ifdef USE_BASIC_PARALLAX", + + "vec2 parallaxMap( in vec3 V ) {", + + "float initialHeight = texture2D( bumpMap, vUv ).r;", + + // No Offset Limitting: messy, floating output at grazing angles. + //"vec2 texCoordOffset = parallaxScale * V.xy / V.z * initialHeight;", + + // Offset Limiting + "vec2 texCoordOffset = parallaxScale * V.xy * initialHeight;", + "return vUv - texCoordOffset;", + + "}", + + "#else", + + "vec2 parallaxMap( in vec3 V ) {", + + // Determine number of layers from angle between V and N + "float numLayers = mix( parallaxMaxLayers, parallaxMinLayers, abs( dot( vec3( 0.0, 0.0, 1.0 ), V ) ) );", + + "float layerHeight = 1.0 / numLayers;", + "float currentLayerHeight = 0.0;", + // Shift of texture coordinates for each iteration + "vec2 dtex = parallaxScale * V.xy / V.z / numLayers;", + + "vec2 currentTextureCoords = vUv;", + + "float heightFromTexture = texture2D( bumpMap, currentTextureCoords ).r;", + + // while ( heightFromTexture > currentLayerHeight ) + // Infinite loops are not well supported. Do a "large" finite + // loop, but not too large, as it slows down some compilers. + "for ( int i = 0; i < 30; i += 1 ) {", + "if ( heightFromTexture <= currentLayerHeight ) {", + "break;", + "}", + "currentLayerHeight += layerHeight;", + // Shift texture coordinates along vector V + "currentTextureCoords -= dtex;", + "heightFromTexture = texture2D( bumpMap, currentTextureCoords ).r;", + "}", + + "#ifdef USE_STEEP_PARALLAX", + + "return currentTextureCoords;", + + "#elif defined( USE_RELIEF_PARALLAX )", + + "vec2 deltaTexCoord = dtex / 2.0;", + "float deltaHeight = layerHeight / 2.0;", + + // Return to the mid point of previous layer + "currentTextureCoords += deltaTexCoord;", + "currentLayerHeight -= deltaHeight;", + + // Binary search to increase precision of Steep Parallax Mapping + "const int numSearches = 5;", + "for ( int i = 0; i < numSearches; i += 1 ) {", + + "deltaTexCoord /= 2.0;", + "deltaHeight /= 2.0;", + "heightFromTexture = texture2D( bumpMap, currentTextureCoords ).r;", + // Shift along or against vector V + "if( heightFromTexture > currentLayerHeight ) {", // Below the surface + + "currentTextureCoords -= deltaTexCoord;", + "currentLayerHeight += deltaHeight;", + + "} else {", // above the surface + + "currentTextureCoords += deltaTexCoord;", + "currentLayerHeight -= deltaHeight;", + + "}", + + "}", + "return currentTextureCoords;", + + "#elif defined( USE_OCLUSION_PARALLAX )", + + "vec2 prevTCoords = currentTextureCoords + dtex;", + + // Heights for linear interpolation + "float nextH = heightFromTexture - currentLayerHeight;", + "float prevH = texture2D( bumpMap, prevTCoords ).r - currentLayerHeight + layerHeight;", + + // Proportions for linear interpolation + "float weight = nextH / ( nextH - prevH );", + + // Interpolation of texture coordinates + "return prevTCoords * weight + currentTextureCoords * ( 1.0 - weight );", + + "#else", // NO_PARALLAX + + "return vUv;", + + "#endif", + + "}", + "#endif", + + "vec2 perturbUv( vec3 surfPosition, vec3 surfNormal, vec3 viewPosition ) {", + + "vec2 texDx = dFdx( vUv );", + "vec2 texDy = dFdy( vUv );", + + "vec3 vSigmaX = dFdx( surfPosition );", + "vec3 vSigmaY = dFdy( surfPosition );", + "vec3 vR1 = cross( vSigmaY, surfNormal );", + "vec3 vR2 = cross( surfNormal, vSigmaX );", + "float fDet = dot( vSigmaX, vR1 );", + + "vec2 vProjVscr = ( 1.0 / fDet ) * vec2( dot( vR1, viewPosition ), dot( vR2, viewPosition ) );", + "vec3 vProjVtex;", + "vProjVtex.xy = texDx * vProjVscr.x + texDy * vProjVscr.y;", + "vProjVtex.z = dot( surfNormal, viewPosition );", + + "return parallaxMap( vProjVtex );", + "}", + + "void main() {", + + "vec2 mapUv = perturbUv( -vViewPosition, normalize( vNormal ), normalize( vViewPosition ) );", + "gl_FragColor = texture2D( map, mapUv );", + + "}", + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/RGBShiftShader.js b/node_modules/three/examples/js/shaders/RGBShiftShader.js new file mode 100644 index 00000000..1c9d8485 --- /dev/null +++ b/node_modules/three/examples/js/shaders/RGBShiftShader.js @@ -0,0 +1,56 @@ +/** + * @author felixturner / http://airtight.cc/ + * + * RGB Shift Shader + * Shifts red and blue channels from center in opposite directions + * Ported from http://kriss.cx/tom/2009/05/rgb-shift/ + * by Tom Butterworth / http://kriss.cx/tom/ + * + * amount: shift distance (1 is width of input) + * angle: shift angle in radians + */ + +THREE.RGBShiftShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "amount": { type: "f", value: 0.005 }, + "angle": { type: "f", value: 0.0 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tDiffuse;", + "uniform float amount;", + "uniform float angle;", + + "varying vec2 vUv;", + + "void main() {", + + "vec2 offset = amount * vec2( cos(angle), sin(angle));", + "vec4 cr = texture2D(tDiffuse, vUv + offset);", + "vec4 cga = texture2D(tDiffuse, vUv);", + "vec4 cb = texture2D(tDiffuse, vUv - offset);", + "gl_FragColor = vec4(cr.r, cga.g, cb.b, cga.a);", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/SSAOShader.js b/node_modules/three/examples/js/shaders/SSAOShader.js new file mode 100644 index 00000000..cc0f736d --- /dev/null +++ b/node_modules/three/examples/js/shaders/SSAOShader.js @@ -0,0 +1,225 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Screen-space ambient occlusion shader + * - ported from + * SSAO GLSL shader v1.2 + * assembled by Martins Upitis (martinsh) (http://devlog-martinsh.blogspot.com) + * original technique is made by ArKano22 (http://www.gamedev.net/topic/550699-ssao-no-halo-artifacts/) + * - modifications + * - modified to use RGBA packed depth texture (use clear color 1,1,1,1 for depth pass) + * - refactoring and optimizations + */ + +THREE.SSAOShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "tDepth": { type: "t", value: null }, + "size": { type: "v2", value: new THREE.Vector2( 512, 512 ) }, + "cameraNear": { type: "f", value: 1 }, + "cameraFar": { type: "f", value: 100 }, + "onlyAO": { type: "i", value: 0 }, + "aoClamp": { type: "f", value: 0.5 }, + "lumInfluence": { type: "f", value: 0.5 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform float cameraNear;", + "uniform float cameraFar;", + + "uniform bool onlyAO;", // use only ambient occlusion pass? + + "uniform vec2 size;", // texture width, height + "uniform float aoClamp;", // depth clamp - reduces haloing at screen edges + + "uniform float lumInfluence;", // how much luminance affects occlusion + + "uniform sampler2D tDiffuse;", + "uniform sampler2D tDepth;", + + "varying vec2 vUv;", + + // "#define PI 3.14159265", + "#define DL 2.399963229728653", // PI * ( 3.0 - sqrt( 5.0 ) ) + "#define EULER 2.718281828459045", + + // user variables + + "const int samples = 8;", // ao sample count + "const float radius = 5.0;", // ao radius + + "const bool useNoise = false;", // use noise instead of pattern for sample dithering + "const float noiseAmount = 0.0003;", // dithering amount + + "const float diffArea = 0.4;", // self-shadowing reduction + "const float gDisplace = 0.4;", // gauss bell center + + + // RGBA depth + + "float unpackDepth( const in vec4 rgba_depth ) {", + + "const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );", + "float depth = dot( rgba_depth, bit_shift );", + "return depth;", + + "}", + + // generating noise / pattern texture for dithering + + "vec2 rand( const vec2 coord ) {", + + "vec2 noise;", + + "if ( useNoise ) {", + + "float nx = dot ( coord, vec2( 12.9898, 78.233 ) );", + "float ny = dot ( coord, vec2( 12.9898, 78.233 ) * 2.0 );", + + "noise = clamp( fract ( 43758.5453 * sin( vec2( nx, ny ) ) ), 0.0, 1.0 );", + + "} else {", + + "float ff = fract( 1.0 - coord.s * ( size.x / 2.0 ) );", + "float gg = fract( coord.t * ( size.y / 2.0 ) );", + + "noise = vec2( 0.25, 0.75 ) * vec2( ff ) + vec2( 0.75, 0.25 ) * gg;", + + "}", + + "return ( noise * 2.0 - 1.0 ) * noiseAmount;", + + "}", + + "float readDepth( const in vec2 coord ) {", + + "float cameraFarPlusNear = cameraFar + cameraNear;", + "float cameraFarMinusNear = cameraFar - cameraNear;", + "float cameraCoef = 2.0 * cameraNear;", + + // "return ( 2.0 * cameraNear ) / ( cameraFar + cameraNear - unpackDepth( texture2D( tDepth, coord ) ) * ( cameraFar - cameraNear ) );", + "return cameraCoef / ( cameraFarPlusNear - unpackDepth( texture2D( tDepth, coord ) ) * cameraFarMinusNear );", + + + "}", + + "float compareDepths( const in float depth1, const in float depth2, inout int far ) {", + + "float garea = 2.0;", // gauss bell width + "float diff = ( depth1 - depth2 ) * 100.0;", // depth difference (0-100) + + // reduce left bell width to avoid self-shadowing + + "if ( diff < gDisplace ) {", + + "garea = diffArea;", + + "} else {", + + "far = 1;", + + "}", + + "float dd = diff - gDisplace;", + "float gauss = pow( EULER, -2.0 * dd * dd / ( garea * garea ) );", + "return gauss;", + + "}", + + "float calcAO( float depth, float dw, float dh ) {", + + "float dd = radius - depth * radius;", + "vec2 vv = vec2( dw, dh );", + + "vec2 coord1 = vUv + dd * vv;", + "vec2 coord2 = vUv - dd * vv;", + + "float temp1 = 0.0;", + "float temp2 = 0.0;", + + "int far = 0;", + "temp1 = compareDepths( depth, readDepth( coord1 ), far );", + + // DEPTH EXTRAPOLATION + + "if ( far > 0 ) {", + + "temp2 = compareDepths( readDepth( coord2 ), depth, far );", + "temp1 += ( 1.0 - temp1 ) * temp2;", + + "}", + + "return temp1;", + + "}", + + "void main() {", + + "vec2 noise = rand( vUv );", + "float depth = readDepth( vUv );", + + "float tt = clamp( depth, aoClamp, 1.0 );", + + "float w = ( 1.0 / size.x ) / tt + ( noise.x * ( 1.0 - noise.x ) );", + "float h = ( 1.0 / size.y ) / tt + ( noise.y * ( 1.0 - noise.y ) );", + + "float ao = 0.0;", + + "float dz = 1.0 / float( samples );", + "float z = 1.0 - dz / 2.0;", + "float l = 0.0;", + + "for ( int i = 0; i <= samples; i ++ ) {", + + "float r = sqrt( 1.0 - z );", + + "float pw = cos( l ) * r;", + "float ph = sin( l ) * r;", + "ao += calcAO( depth, pw * w, ph * h );", + "z = z - dz;", + "l = l + DL;", + + "}", + + "ao /= float( samples );", + "ao = 1.0 - ao;", + + "vec3 color = texture2D( tDiffuse, vUv ).rgb;", + + "vec3 lumcoeff = vec3( 0.299, 0.587, 0.114 );", + "float lum = dot( color.rgb, lumcoeff );", + "vec3 luminance = vec3( lum );", + + "vec3 final = vec3( color * mix( vec3( ao ), vec3( 1.0 ), luminance * lumInfluence ) );", // mix( color * ao, white, luminance ) + + "if ( onlyAO ) {", + + "final = vec3( mix( vec3( ao ), vec3( 1.0 ), luminance * lumInfluence ) );", // ambient occlusion only + + "}", + + "gl_FragColor = vec4( final, 1.0 );", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/SepiaShader.js b/node_modules/three/examples/js/shaders/SepiaShader.js new file mode 100644 index 00000000..a8f3fd0e --- /dev/null +++ b/node_modules/three/examples/js/shaders/SepiaShader.js @@ -0,0 +1,54 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Sepia tone shader + * based on glfx.js sepia shader + * https://github.com/evanw/glfx.js + */ + +THREE.SepiaShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "amount": { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform float amount;", + + "uniform sampler2D tDiffuse;", + + "varying vec2 vUv;", + + "void main() {", + + "vec4 color = texture2D( tDiffuse, vUv );", + "vec3 c = color.rgb;", + + "color.r = dot( c, vec3( 1.0 - 0.607 * amount, 0.769 * amount, 0.189 * amount ) );", + "color.g = dot( c, vec3( 0.349 * amount, 1.0 - 0.314 * amount, 0.168 * amount ) );", + "color.b = dot( c, vec3( 0.272 * amount, 0.534 * amount, 1.0 - 0.869 * amount ) );", + + "gl_FragColor = vec4( min( vec3( 1.0 ), color.rgb ), color.a );", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/TechnicolorShader.js b/node_modules/three/examples/js/shaders/TechnicolorShader.js new file mode 100644 index 00000000..743382c8 --- /dev/null +++ b/node_modules/three/examples/js/shaders/TechnicolorShader.js @@ -0,0 +1,47 @@ +/** + * @author flimshaw / http://charliehoey.com + * + * Technicolor Shader + * Simulates the look of the two-strip technicolor process popular in early 20th century films. + * More historical info here: http://www.widescreenmuseum.com/oldcolor/technicolor1.htm + * Demo here: http://charliehoey.com/technicolor_shader/shader_test.html + */ + +THREE.TechnicolorShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tDiffuse;", + "varying vec2 vUv;", + + "void main() {", + + "vec4 tex = texture2D( tDiffuse, vec2( vUv.x, vUv.y ) );", + "vec4 newTex = vec4(tex.r, (tex.g + tex.b) * .5, (tex.g + tex.b) * .5, 1.0);", + + "gl_FragColor = newTex;", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/ToneMapShader.js b/node_modules/three/examples/js/shaders/ToneMapShader.js new file mode 100644 index 00000000..feba5983 --- /dev/null +++ b/node_modules/three/examples/js/shaders/ToneMapShader.js @@ -0,0 +1,75 @@ +/** + * @author miibond + * + * Full-screen tone-mapping shader based on http://www.graphics.cornell.edu/~jaf/publications/sig02_paper.pdf + */ + +THREE.ToneMapShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "averageLuminance": { type: "f", value: 1.0 }, + "luminanceMap": { type: "t", value: null }, + "maxLuminance": { type: "f", value: 16.0 }, + "middleGrey": { type: "f", value: 0.6 } + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tDiffuse;", + + "varying vec2 vUv;", + + "uniform float middleGrey;", + "uniform float maxLuminance;", + "#ifdef ADAPTED_LUMINANCE", + "uniform sampler2D luminanceMap;", + "#else", + "uniform float averageLuminance;", + "#endif", + + "const vec3 LUM_CONVERT = vec3(0.299, 0.587, 0.114);", + + "vec3 ToneMap( vec3 vColor ) {", + "#ifdef ADAPTED_LUMINANCE", + // Get the calculated average luminance + "float fLumAvg = texture2D(luminanceMap, vec2(0.5, 0.5)).r;", + "#else", + "float fLumAvg = averageLuminance;", + "#endif", + + // Calculate the luminance of the current pixel + "float fLumPixel = dot(vColor, LUM_CONVERT);", + + // Apply the modified operator (Eq. 4) + "float fLumScaled = (fLumPixel * middleGrey) / fLumAvg;", + + "float fLumCompressed = (fLumScaled * (1.0 + (fLumScaled / (maxLuminance * maxLuminance)))) / (1.0 + fLumScaled);", + "return fLumCompressed * vColor;", + "}", + + "void main() {", + + "vec4 texel = texture2D( tDiffuse, vUv );", + + "gl_FragColor = vec4( ToneMap( texel.xyz ), texel.w );", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/TriangleBlurShader.js b/node_modules/three/examples/js/shaders/TriangleBlurShader.js new file mode 100644 index 00000000..72531666 --- /dev/null +++ b/node_modules/three/examples/js/shaders/TriangleBlurShader.js @@ -0,0 +1,78 @@ +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + * Triangle blur shader + * based on glfx.js triangle blur shader + * https://github.com/evanw/glfx.js + * + * A basic blur filter, which convolves the image with a + * pyramid filter. The pyramid filter is separable and is applied as two + * perpendicular triangle filters. + */ + +THREE.TriangleBlurShader = { + + uniforms : { + + "texture": { type: "t", value: null }, + "delta": { type: "v2", value: new THREE.Vector2( 1, 1 ) } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "#define ITERATIONS 10.0", + + "uniform sampler2D texture;", + "uniform vec2 delta;", + + "varying vec2 vUv;", + + "float random( vec3 scale, float seed ) {", + + // use the fragment position for a different seed per-pixel + + "return fract( sin( dot( gl_FragCoord.xyz + seed, scale ) ) * 43758.5453 + seed );", + + "}", + + "void main() {", + + "vec4 color = vec4( 0.0 );", + + "float total = 0.0;", + + // randomize the lookup values to hide the fixed number of samples + + "float offset = random( vec3( 12.9898, 78.233, 151.7182 ), 0.0 );", + + "for ( float t = -ITERATIONS; t <= ITERATIONS; t ++ ) {", + + "float percent = ( t + offset - 0.5 ) / ITERATIONS;", + "float weight = 1.0 - abs( percent );", + + "color += texture2D( texture, vUv + delta * percent ) * weight;", + "total += weight;", + + "}", + + "gl_FragColor = color / total;", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/UnpackDepthRGBAShader.js b/node_modules/three/examples/js/shaders/UnpackDepthRGBAShader.js new file mode 100644 index 00000000..e4dbfd81 --- /dev/null +++ b/node_modules/three/examples/js/shaders/UnpackDepthRGBAShader.js @@ -0,0 +1,57 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Unpack RGBA depth shader + * - show RGBA encoded depth as monochrome color + */ + +THREE.UnpackDepthRGBAShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "opacity": { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform float opacity;", + + "uniform sampler2D tDiffuse;", + + "varying vec2 vUv;", + + // RGBA depth + + "float unpackDepth( const in vec4 rgba_depth ) {", + + "const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );", + "float depth = dot( rgba_depth, bit_shift );", + "return depth;", + + "}", + + "void main() {", + + "float depth = 1.0 - unpackDepth( texture2D( tDiffuse, vUv ) );", + "gl_FragColor = opacity * vec4( vec3( depth ), 1.0 );", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/VerticalBlurShader.js b/node_modules/three/examples/js/shaders/VerticalBlurShader.js new file mode 100644 index 00000000..334d4d3e --- /dev/null +++ b/node_modules/three/examples/js/shaders/VerticalBlurShader.js @@ -0,0 +1,62 @@ +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + * Two pass Gaussian blur filter (horizontal and vertical blur shaders) + * - described in http://www.gamerendering.com/2008/10/11/gaussian-blur-filter-shader/ + * and used in http://www.cake23.de/traveling-wavefronts-lit-up.html + * + * - 9 samples per pass + * - standard deviation 2.7 + * - "h" and "v" parameters should be set to "1 / width" and "1 / height" + */ + +THREE.VerticalBlurShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "v": { type: "f", value: 1.0 / 512.0 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tDiffuse;", + "uniform float v;", + + "varying vec2 vUv;", + + "void main() {", + + "vec4 sum = vec4( 0.0 );", + + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 4.0 * v ) ) * 0.051;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 3.0 * v ) ) * 0.0918;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 2.0 * v ) ) * 0.12245;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 1.0 * v ) ) * 0.1531;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y ) ) * 0.1633;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 1.0 * v ) ) * 0.1531;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 2.0 * v ) ) * 0.12245;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 3.0 * v ) ) * 0.0918;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 4.0 * v ) ) * 0.051;", + + "gl_FragColor = sum;", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/VerticalTiltShiftShader.js b/node_modules/three/examples/js/shaders/VerticalTiltShiftShader.js new file mode 100644 index 00000000..16314eba --- /dev/null +++ b/node_modules/three/examples/js/shaders/VerticalTiltShiftShader.js @@ -0,0 +1,65 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Simple fake tilt-shift effect, modulating two pass Gaussian blur (see above) by vertical position + * + * - 9 samples per pass + * - standard deviation 2.7 + * - "h" and "v" parameters should be set to "1 / width" and "1 / height" + * - "r" parameter control where "focused" horizontal line lies + */ + +THREE.VerticalTiltShiftShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "v": { type: "f", value: 1.0 / 512.0 }, + "r": { type: "f", value: 0.35 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform sampler2D tDiffuse;", + "uniform float v;", + "uniform float r;", + + "varying vec2 vUv;", + + "void main() {", + + "vec4 sum = vec4( 0.0 );", + + "float vv = v * abs( r - vUv.y );", + + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 4.0 * vv ) ) * 0.051;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 3.0 * vv ) ) * 0.0918;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 2.0 * vv ) ) * 0.12245;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 1.0 * vv ) ) * 0.1531;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y ) ) * 0.1633;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 1.0 * vv ) ) * 0.1531;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 2.0 * vv ) ) * 0.12245;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 3.0 * vv ) ) * 0.0918;", + "sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 4.0 * vv ) ) * 0.051;", + + "gl_FragColor = sum;", + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/shaders/VignetteShader.js b/node_modules/three/examples/js/shaders/VignetteShader.js new file mode 100644 index 00000000..c34c07b3 --- /dev/null +++ b/node_modules/three/examples/js/shaders/VignetteShader.js @@ -0,0 +1,63 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * + * Vignette shader + * based on PaintEffect postprocess from ro.me + * http://code.google.com/p/3-dreams-of-black/source/browse/deploy/js/effects/PaintEffect.js + */ + +THREE.VignetteShader = { + + uniforms: { + + "tDiffuse": { type: "t", value: null }, + "offset": { type: "f", value: 1.0 }, + "darkness": { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = uv;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform float offset;", + "uniform float darkness;", + + "uniform sampler2D tDiffuse;", + + "varying vec2 vUv;", + + "void main() {", + + // Eskil's vignette + + "vec4 texel = texture2D( tDiffuse, vUv );", + "vec2 uv = ( vUv - vec2( 0.5 ) ) * vec2( offset );", + "gl_FragColor = vec4( mix( texel.rgb, vec3( 1.0 - darkness ), dot( uv, uv ) ), texel.a );", + + /* + // alternative version from glfx.js + // this one makes more "dusty" look (as opposed to "burned") + + "vec4 color = texture2D( tDiffuse, vUv );", + "float dist = distance( vUv, vec2( 0.5 ) );", + "color.rgb *= smoothstep( 0.8, offset * 0.799, dist *( darkness + offset ) );", + "gl_FragColor = color;", + */ + + "}" + + ].join( "\n" ) + +}; diff --git a/node_modules/three/examples/js/utils/FontUtils.js b/node_modules/three/examples/js/utils/FontUtils.js new file mode 100644 index 00000000..c8d36bdb --- /dev/null +++ b/node_modules/three/examples/js/utils/FontUtils.js @@ -0,0 +1,276 @@ +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author alteredq / http://alteredqualia.com/ + * + * For Text operations in three.js (See TextGeometry) + * + * It uses techniques used in: + * + * Triangulation ported from AS3 + * Simple Polygon Triangulation + * http://actionsnippet.com/?p=1462 + * + * A Method to triangulate shapes with holes + * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/ + * + */ + +THREE.FontUtils = { + + faces: {}, + + // Just for now. face[weight][style] + + face: 'helvetiker', + weight: 'normal', + style: 'normal', + size: 150, + divisions: 10, + + getFace: function () { + + try { + + return this.faces[ this.face.toLowerCase() ][ this.weight ][ this.style ]; + + } catch ( e ) { + + throw "The font " + this.face + " with " + this.weight + " weight and " + this.style + " style is missing." + + } + + }, + + loadFace: function ( data ) { + + var family = data.familyName.toLowerCase(); + + var ThreeFont = this; + + ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {}; + + ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {}; + ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + + ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + + return data; + + }, + + drawText: function ( text ) { + + // RenderText + + var i, + face = this.getFace(), + scale = this.size / face.resolution, + offset = 0, + chars = String( text ).split( '' ), + length = chars.length; + + var fontPaths = []; + + for ( i = 0; i < length; i ++ ) { + + var path = new THREE.Path(); + + var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path ); + offset += ret.offset; + + fontPaths.push( ret.path ); + + } + + // get the width + + var width = offset / 2; + // + // for ( p = 0; p < allPts.length; p++ ) { + // + // allPts[ p ].x -= width; + // + // } + + //var extract = this.extractPoints( allPts, characterPts ); + //extract.contour = allPts; + + //extract.paths = fontPaths; + //extract.offset = width; + + return { paths: fontPaths, offset: width }; + + }, + + + + + extractGlyphPoints: function ( c, face, scale, offset, path ) { + + var pts = []; + + var b2 = THREE.ShapeUtils.b2; + var b3 = THREE.ShapeUtils.b3; + + var i, i2, divisions, + outline, action, length, + scaleX, scaleY, + x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, + laste, + glyph = face.glyphs[ c ] || face.glyphs[ '?' ]; + + if ( ! glyph ) return; + + if ( glyph.o ) { + + outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); + length = outline.length; + + scaleX = scale; + scaleY = scale; + + for ( i = 0; i < length; ) { + + action = outline[ i ++ ]; + + //console.log( action ); + + switch ( action ) { + + case 'm': + + // Move To + + x = outline[ i ++ ] * scaleX + offset; + y = outline[ i ++ ] * scaleY; + + path.moveTo( x, y ); + break; + + case 'l': + + // Line To + + x = outline[ i ++ ] * scaleX + offset; + y = outline[ i ++ ] * scaleY; + path.lineTo( x, y ); + break; + + case 'q': + + // QuadraticCurveTo + + cpx = outline[ i ++ ] * scaleX + offset; + cpy = outline[ i ++ ] * scaleY; + cpx1 = outline[ i ++ ] * scaleX + offset; + cpy1 = outline[ i ++ ] * scaleY; + + path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); + + laste = pts[ pts.length - 1 ]; + + if ( laste ) { + + cpx0 = laste.x; + cpy0 = laste.y; + + for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { + + var t = i2 / divisions; + b2( t, cpx0, cpx1, cpx ); + b2( t, cpy0, cpy1, cpy ); + + } + + } + + break; + + case 'b': + + // Cubic Bezier Curve + + cpx = outline[ i ++ ] * scaleX + offset; + cpy = outline[ i ++ ] * scaleY; + cpx1 = outline[ i ++ ] * scaleX + offset; + cpy1 = outline[ i ++ ] * scaleY; + cpx2 = outline[ i ++ ] * scaleX + offset; + cpy2 = outline[ i ++ ] * scaleY; + + path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); + + laste = pts[ pts.length - 1 ]; + + if ( laste ) { + + cpx0 = laste.x; + cpy0 = laste.y; + + for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { + + var t = i2 / divisions; + b3( t, cpx0, cpx1, cpx2, cpx ); + b3( t, cpy0, cpy1, cpy2, cpy ); + + } + + } + + break; + + } + + } + + } + + + + return { offset: glyph.ha * scale, path: path }; + + } + +}; + + +THREE.FontUtils.generateShapes = function ( text, parameters ) { + + // Parameters + + parameters = parameters || {}; + + var size = parameters.size !== undefined ? parameters.size : 100; + var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments : 4; + + var font = parameters.font !== undefined ? parameters.font : 'helvetiker'; + var weight = parameters.weight !== undefined ? parameters.weight : 'normal'; + var style = parameters.style !== undefined ? parameters.style : 'normal'; + + THREE.FontUtils.size = size; + THREE.FontUtils.divisions = curveSegments; + + THREE.FontUtils.face = font; + THREE.FontUtils.weight = weight; + THREE.FontUtils.style = style; + + // Get a Font data json object + + var data = THREE.FontUtils.drawText( text ); + + var paths = data.paths; + var shapes = []; + + for ( var p = 0, pl = paths.length; p < pl; p ++ ) { + + Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); + + } + + return shapes; + +}; + +// To use the typeface.js face files, hook up the API + +THREE.typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace }; +if ( typeof self !== 'undefined' ) self._typeface_js = THREE.typeface_js; diff --git a/node_modules/three/examples/js/utils/GeometryUtils.js b/node_modules/three/examples/js/utils/GeometryUtils.js new file mode 100644 index 00000000..d63b1b9a --- /dev/null +++ b/node_modules/three/examples/js/utils/GeometryUtils.js @@ -0,0 +1,301 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.GeometryUtils = { + + // Merge two geometries or geometry and geometry from object (using object's transform) + + merge: function ( geometry1, geometry2, materialIndexOffset ) { + + console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' ); + + var matrix; + + if ( geometry2 instanceof THREE.Mesh ) { + + geometry2.matrixAutoUpdate && geometry2.updateMatrix(); + + matrix = geometry2.matrix; + geometry2 = geometry2.geometry; + + } + + geometry1.merge( geometry2, matrix, materialIndexOffset ); + + }, + + // Get random point in triangle (via barycentric coordinates) + // (uniform distribution) + // http://www.cgafaq.info/wiki/Random_Point_In_Triangle + + randomPointInTriangle: function () { + + var vector = new THREE.Vector3(); + + return function ( vectorA, vectorB, vectorC ) { + + var point = new THREE.Vector3(); + + var a = THREE.Math.random16(); + var b = THREE.Math.random16(); + + if ( ( a + b ) > 1 ) { + + a = 1 - a; + b = 1 - b; + + } + + var c = 1 - a - b; + + point.copy( vectorA ); + point.multiplyScalar( a ); + + vector.copy( vectorB ); + vector.multiplyScalar( b ); + + point.add( vector ); + + vector.copy( vectorC ); + vector.multiplyScalar( c ); + + point.add( vector ); + + return point; + + }; + + }(), + + // Get random point in face (triangle) + // (uniform distribution) + + randomPointInFace: function ( face, geometry ) { + + var vA, vB, vC; + + vA = geometry.vertices[ face.a ]; + vB = geometry.vertices[ face.b ]; + vC = geometry.vertices[ face.c ]; + + return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vC ); + + }, + + // Get uniformly distributed random points in mesh + // - create array with cumulative sums of face areas + // - pick random number from 0 to total area + // - find corresponding place in area array by binary search + // - get random point in face + + randomPointsInGeometry: function ( geometry, n ) { + + var face, i, + faces = geometry.faces, + vertices = geometry.vertices, + il = faces.length, + totalArea = 0, + cumulativeAreas = [], + vA, vB, vC; + + // precompute face areas + + for ( i = 0; i < il; i ++ ) { + + face = faces[ i ]; + + vA = vertices[ face.a ]; + vB = vertices[ face.b ]; + vC = vertices[ face.c ]; + + face._area = THREE.GeometryUtils.triangleArea( vA, vB, vC ); + + totalArea += face._area; + + cumulativeAreas[ i ] = totalArea; + + } + + // binary search cumulative areas array + + function binarySearchIndices( value ) { + + function binarySearch( start, end ) { + + // return closest larger index + // if exact number is not found + + if ( end < start ) + return start; + + var mid = start + Math.floor( ( end - start ) / 2 ); + + if ( cumulativeAreas[ mid ] > value ) { + + return binarySearch( start, mid - 1 ); + + } else if ( cumulativeAreas[ mid ] < value ) { + + return binarySearch( mid + 1, end ); + + } else { + + return mid; + + } + + } + + var result = binarySearch( 0, cumulativeAreas.length - 1 ); + return result; + + } + + // pick random face weighted by face area + + var r, index, + result = []; + + var stats = {}; + + for ( i = 0; i < n; i ++ ) { + + r = THREE.Math.random16() * totalArea; + + index = binarySearchIndices( r ); + + result[ i ] = THREE.GeometryUtils.randomPointInFace( faces[ index ], geometry ); + + if ( ! stats[ index ] ) { + + stats[ index ] = 1; + + } else { + + stats[ index ] += 1; + + } + + } + + return result; + + }, + + randomPointsInBufferGeometry: function ( geometry, n ) { + + var i, + vertices = geometry.attributes.position.array, + totalArea = 0, + cumulativeAreas = [], + vA, vB, vC; + + // precompute face areas + vA = new THREE.Vector3(); + vB = new THREE.Vector3(); + vC = new THREE.Vector3(); + + // geometry._areas = []; + var il = vertices.length / 9; + + for ( i = 0; i < il; i ++ ) { + + vA.set( vertices[ i * 9 + 0 ], vertices[ i * 9 + 1 ], vertices[ i * 9 + 2 ] ); + vB.set( vertices[ i * 9 + 3 ], vertices[ i * 9 + 4 ], vertices[ i * 9 + 5 ] ); + vC.set( vertices[ i * 9 + 6 ], vertices[ i * 9 + 7 ], vertices[ i * 9 + 8 ] ); + + area = THREE.GeometryUtils.triangleArea( vA, vB, vC ); + totalArea += area; + + cumulativeAreas.push( totalArea ); + + } + + // binary search cumulative areas array + + function binarySearchIndices( value ) { + + function binarySearch( start, end ) { + + // return closest larger index + // if exact number is not found + + if ( end < start ) + return start; + + var mid = start + Math.floor( ( end - start ) / 2 ); + + if ( cumulativeAreas[ mid ] > value ) { + + return binarySearch( start, mid - 1 ); + + } else if ( cumulativeAreas[ mid ] < value ) { + + return binarySearch( mid + 1, end ); + + } else { + + return mid; + + } + + } + + var result = binarySearch( 0, cumulativeAreas.length - 1 ); + return result; + + } + + // pick random face weighted by face area + + var r, index, + result = []; + + for ( i = 0; i < n; i ++ ) { + + r = THREE.Math.random16() * totalArea; + + index = binarySearchIndices( r ); + + // result[ i ] = THREE.GeometryUtils.randomPointInFace( faces[ index ], geometry, true ); + vA.set( vertices[ index * 9 + 0 ], vertices[ index * 9 + 1 ], vertices[ index * 9 + 2 ] ); + vB.set( vertices[ index * 9 + 3 ], vertices[ index * 9 + 4 ], vertices[ index * 9 + 5 ] ); + vC.set( vertices[ index * 9 + 6 ], vertices[ index * 9 + 7 ], vertices[ index * 9 + 8 ] ); + result[ i ] = THREE.GeometryUtils.randomPointInTriangle( vA, vB, vC ); + + } + + return result; + + }, + + // Get triangle area (half of parallelogram) + // http://mathworld.wolfram.com/TriangleArea.html + + triangleArea: function () { + + var vector1 = new THREE.Vector3(); + var vector2 = new THREE.Vector3(); + + return function ( vectorA, vectorB, vectorC ) { + + vector1.subVectors( vectorB, vectorA ); + vector2.subVectors( vectorC, vectorA ); + vector1.cross( vector2 ); + + return 0.5 * vector1.length(); + + }; + + }(), + + center: function ( geometry ) { + + console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' ); + return geometry.center(); + + } + +}; diff --git a/node_modules/three/examples/js/utils/ImageUtils.js b/node_modules/three/examples/js/utils/ImageUtils.js new file mode 100644 index 00000000..5594c186 --- /dev/null +++ b/node_modules/three/examples/js/utils/ImageUtils.js @@ -0,0 +1,136 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author Daosheng Mu / https://github.com/DaoshengMu/ + */ + +THREE.ImageUtils = { + + getNormalMap: function ( image, depth ) { + + // Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/ + + function cross( a, b ) { + + return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ]; + + } + + function subtract( a, b ) { + + return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ]; + + } + + function normalize( a ) { + + var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] ); + return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ]; + + } + + depth = depth | 1; + + var width = image.width; + var height = image.height; + + var canvas = document.createElement( 'canvas' ); + canvas.width = width; + canvas.height = height; + + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0 ); + + var data = context.getImageData( 0, 0, width, height ).data; + var imageData = context.createImageData( width, height ); + var output = imageData.data; + + for ( var x = 0; x < width; x ++ ) { + + for ( var y = 0; y < height; y ++ ) { + + var ly = y - 1 < 0 ? 0 : y - 1; + var uy = y + 1 > height - 1 ? height - 1 : y + 1; + var lx = x - 1 < 0 ? 0 : x - 1; + var ux = x + 1 > width - 1 ? width - 1 : x + 1; + + var points = []; + var origin = [ 0, 0, data[ ( y * width + x ) * 4 ] / 255 * depth ]; + points.push( [ - 1, 0, data[ ( y * width + lx ) * 4 ] / 255 * depth ] ); + points.push( [ - 1, - 1, data[ ( ly * width + lx ) * 4 ] / 255 * depth ] ); + points.push( [ 0, - 1, data[ ( ly * width + x ) * 4 ] / 255 * depth ] ); + points.push( [ 1, - 1, data[ ( ly * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 1, 0, data[ ( y * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 1, 1, data[ ( uy * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 0, 1, data[ ( uy * width + x ) * 4 ] / 255 * depth ] ); + points.push( [ - 1, 1, data[ ( uy * width + lx ) * 4 ] / 255 * depth ] ); + + var normals = []; + var num_points = points.length; + + for ( var i = 0; i < num_points; i ++ ) { + + var v1 = points[ i ]; + var v2 = points[ ( i + 1 ) % num_points ]; + v1 = subtract( v1, origin ); + v2 = subtract( v2, origin ); + normals.push( normalize( cross( v1, v2 ) ) ); + + } + + var normal = [ 0, 0, 0 ]; + + for ( var i = 0; i < normals.length; i ++ ) { + + normal[ 0 ] += normals[ i ][ 0 ]; + normal[ 1 ] += normals[ i ][ 1 ]; + normal[ 2 ] += normals[ i ][ 2 ]; + + } + + normal[ 0 ] /= normals.length; + normal[ 1 ] /= normals.length; + normal[ 2 ] /= normals.length; + + var idx = ( y * width + x ) * 4; + + output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0; + output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0; + output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0; + output[ idx + 3 ] = 255; + + } + + } + + context.putImageData( imageData, 0, 0 ); + + return canvas; + + }, + + generateDataTexture: function ( width, height, color ) { + + var size = width * height; + var data = new Uint8Array( 3 * size ); + + var r = Math.floor( color.r * 255 ); + var g = Math.floor( color.g * 255 ); + var b = Math.floor( color.b * 255 ); + + for ( var i = 0; i < size; i ++ ) { + + data[ i * 3 ] = r; + data[ i * 3 + 1 ] = g; + data[ i * 3 + 2 ] = b; + + } + + var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat ); + texture.needsUpdate = true; + + return texture; + + } + +}; diff --git a/node_modules/three/examples/js/utils/ShadowMapViewer.js b/node_modules/three/examples/js/utils/ShadowMapViewer.js new file mode 100644 index 00000000..8363f380 --- /dev/null +++ b/node_modules/three/examples/js/utils/ShadowMapViewer.js @@ -0,0 +1,190 @@ +/** + * @author arya-s / https://github.com/arya-s + * + * This is a helper for visualising a given light's shadow map. + * It works for shadow casting lights: THREE.DirectionalLight and THREE.SpotLight. + * It renders out the shadow map and displays it on a HUD. + * + * Example usage: + * 1) Include + + + + + + + + + + + + + + + + + + + + + + + diff --git a/node_modules/webvr-boilerplate/package.json b/node_modules/webvr-boilerplate/package.json new file mode 100644 index 00000000..2f7b01c0 --- /dev/null +++ b/node_modules/webvr-boilerplate/package.json @@ -0,0 +1,89 @@ +{ + "_args": [ + [ + "webvr-boilerplate@0.3.8", + "/Users/smus/Projects/embedvr" + ] + ], + "_from": "webvr-boilerplate@0.3.8", + "_id": "webvr-boilerplate@0.3.8", + "_inCache": true, + "_installable": true, + "_location": "/webvr-boilerplate", + "_nodeVersion": "5.4.0", + "_npmOperationalInternal": { + "host": "packages-13-west.internal.npmjs.com", + "tmp": "tmp/webvr-boilerplate-0.3.8.tgz_1457032022129_0.7890693168155849" + }, + "_npmUser": { + "email": "boris@smus.com", + "name": "smus" + }, + "_npmVersion": "3.3.12", + "_phantomChildren": {}, + "_requested": { + "name": "webvr-boilerplate", + "raw": "webvr-boilerplate@0.3.8", + "rawSpec": "0.3.8", + "scope": null, + "spec": "0.3.8", + "type": "version" + }, + "_requiredBy": [ + "/" + ], + "_shasum": "62a8cbeec743278e521be4f9bb05a885a3bb450e", + "_shrinkwrap": null, + "_spec": "webvr-boilerplate@0.3.8", + "_where": "/Users/smus/Projects/embedvr", + "author": { + "name": "Boris Smus" + }, + "bugs": { + "url": "https://github.com/borismus/webvr-boilerplate/issues" + }, + "dependencies": { + "es6-promise": "^3.0.2", + "three": "^0.73", + "webvr-polyfill": "^0.2.4" + }, + "description": "A starting point for web-based VR experiences that work in both Cardboard and Oculus.", + "devDependencies": { + "browserify": "latest", + "derequire": "latest", + "watchify": "latest" + }, + "directories": { + "test": "test" + }, + "dist": { + "shasum": "62a8cbeec743278e521be4f9bb05a885a3bb450e", + "tarball": "http://registry.npmjs.org/webvr-boilerplate/-/webvr-boilerplate-0.3.8.tgz" + }, + "gitHead": "d91cc2866bd54e65d59022800f62c7e160dc9fee", + "homepage": "https://github.com/borismus/webvr-boilerplate#readme", + "keywords": [ + "vr" + ], + "license": "Apache-2.0", + "main": "src/main.js", + "maintainers": [ + { + "name": "smus", + "email": "boris@smus.com" + } + ], + "name": "webvr-boilerplate", + "optionalDependencies": {}, + "readme": "ERROR: No README data found!", + "repository": { + "type": "git", + "url": "git+https://github.com/borismus/webvr-boilerplate.git" + }, + "scripts": { + "build": "browserify src/main.js | derequire > build/webvr-manager.js", + "test": "browserify test/device-info-test.js > build/device-info-test.js", + "watch": "watchify src/main.js -v -d -o build/webvr-manager.js" + }, + "version": "0.3.8" +} diff --git a/node_modules/webvr-polyfill/.jscsrc b/node_modules/webvr-polyfill/.jscsrc new file mode 100644 index 00000000..84437f9c --- /dev/null +++ b/node_modules/webvr-polyfill/.jscsrc @@ -0,0 +1,4 @@ +{ + "preset": "google", + "maximumLineLength": 120 +} \ No newline at end of file diff --git a/node_modules/webvr-polyfill/CONTRIBUTING b/node_modules/webvr-polyfill/CONTRIBUTING new file mode 100644 index 00000000..2f4cd1ce --- /dev/null +++ b/node_modules/webvr-polyfill/CONTRIBUTING @@ -0,0 +1,21 @@ +# Building + +This project uses browserify to manage dependencies and build. Watchify is +especially convenient to preserve the write-and-reload model of development. +This package lives in the npm index. There is also a bower version of it, but it +is deprecated. + +Relevant commands: + + npm build - builds the module. + npm watch - auto-builds the module whenever any source changes. + + +# Updating the npm entry + +Once changes are made, a new version can be published to the index using the +following commands: + + npm version + npm publish + git push diff --git a/node_modules/webvr-polyfill/COPYING b/node_modules/webvr-polyfill/COPYING new file mode 100644 index 00000000..dfc59b7f --- /dev/null +++ b/node_modules/webvr-polyfill/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2015 Google Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/node_modules/webvr-polyfill/README.md b/node_modules/webvr-polyfill/README.md new file mode 100644 index 00000000..5096a99c --- /dev/null +++ b/node_modules/webvr-polyfill/README.md @@ -0,0 +1,58 @@ +# WebVR Polyfill + +This project provides a JavaScript implementation of the [WebVR +spec][spec]. + +The goal of this project is two fold: + +1. Use WebVR today, without requiring a special browser build. +2. View (mono) content without a virtual reality headset. + +[spec]: http://mozvr.github.io/webvr-spec/webvr.html + +## Implementation + +The polyfill decides which VRDevices to provide, depending on the configuration +of your browser. Mobile devices provide both the FusedPositionSensorVRDevice and +the CardboardHMDVRDevice. Desktop devices use the +MouseKeyboardPositionSensorVRDevice. + +`CardboardHMDVRDevice` provides default parameters for Cardboard's +interpupillary distance and headset. + +`MouseKeyboardPositionSensorVRDevice` uses mouse events to allow you to do the +equivalent of mouselook. It also uses keyboard arrows and WASD keys to look +around the scene with the keyboard. + +`FusedPositionSensorVRDevice` uses DeviceMotionEvents and implements a +complementary filter which does sensor fusion. This device also implements pose +prediction, which greatly improves head tracking performance. + +**Deprecated**: `OrientationPositionSensorVRDevice` uses DeviceOrientationEvents +to polyfill head-tracking on mobile devices. + +**Experimental**: `WebcamPositionSensorVRDevice` uses your laptop's webcam in +order to introduce translational degrees of freedom. + +[ss]: https://play.google.com/store/apps/details?id=com.motorola.avatar + +## Configuration + +The polyfill can be configured and debugged with various options. The following +are supported: + + WebVRConfig = { + // Forces availability of VR mode. + //FORCE_ENABLE_VR: true, // Default: false. + // Complementary filter coefficient. 0 for accelerometer, 1 for gyro. + //K_FILTER: 0.98, // Default: 0.98. + // How far into the future to predict during fast motion. + //PREDICTION_TIME_S: 0.050, // Default: 0.050s. + // Flag to disable touch panner. In case you have your own touch controls + //TOUCH_PANNER_DISABLED: true, // Default: false. + // Enable yaw panning only, disabling roll and pitch. This can be useful + // for panoramas with nothing interesting above or below. + //YAW_ONLY: true, // Default: false. + // To disable keyboard and mouse controls. If you implement your own. + //MOUSE_KEYBOARD_CONTROLS_DISABLED: true // Default: false + } diff --git a/node_modules/webvr-polyfill/build/webvr-polyfill.js b/node_modules/webvr-polyfill/build/webvr-polyfill.js new file mode 100644 index 00000000..852ced8b --- /dev/null +++ b/node_modules/webvr-polyfill/build/webvr-polyfill.js @@ -0,0 +1,3333 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o Util.MAX_TIMESTEP) { + console.warn('Invalid timestamps detected. Time step between successive ' + + 'gyroscope sensor samples is very small or not monotonic'); + this.previousTimestampS = timestampS; + return; + } + this.accelerometer.set(-accGravity.x, -accGravity.y, -accGravity.z); + this.gyroscope.set(rotRate.alpha, rotRate.beta, rotRate.gamma); + + // With iOS and Firefox Android, rotationRate is reported in degrees, + // so we first convert to radians. + if (this.isIOS || this.isFirefoxAndroid) { + this.gyroscope.multiplyScalar(Math.PI / 180); + } + + this.filter.addAccelMeasurement(this.accelerometer, timestampS); + this.filter.addGyroMeasurement(this.gyroscope, timestampS); + + this.previousTimestampS = timestampS; +}; + +FusionPositionSensorVRDevice.prototype.onScreenOrientationChange_ = + function(screenOrientation) { + this.setScreenTransform_(); +}; + +FusionPositionSensorVRDevice.prototype.setScreenTransform_ = function() { + this.worldToScreenQ.set(0, 0, 0, 1); + switch (window.orientation) { + case 0: + break; + case 90: + this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0, 0, 1), -Math.PI/2); + break; + case -90: + this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI/2); + break; + case 180: + // TODO. + break; + } +}; + + +module.exports = FusionPositionSensorVRDevice; + +},{"./base.js":1,"./complementary-filter.js":3,"./pose-predictor.js":7,"./three-math.js":9,"./touch-panner.js":10,"./util.js":11}],5:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var WebVRPolyfill = _dereq_('./webvr-polyfill.js'); + +// Initialize a WebVRConfig just in case. +window.WebVRConfig = window.WebVRConfig || {}; +new WebVRPolyfill(); + +},{"./webvr-polyfill.js":12}],6:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var PositionSensorVRDevice = _dereq_('./base.js').PositionSensorVRDevice; +var THREE = _dereq_('./three-math.js'); +var Util = _dereq_('./util.js'); + +// How much to rotate per key stroke. +var KEY_SPEED = 0.15; +var KEY_ANIMATION_DURATION = 80; + +// How much to rotate for mouse events. +var MOUSE_SPEED_X = 0.5; +var MOUSE_SPEED_Y = 0.3; + +/** + * A virtual position sensor, implemented using keyboard and + * mouse APIs. This is designed as for desktops/laptops where no Device* + * events work. + */ +function MouseKeyboardPositionSensorVRDevice() { + this.deviceId = 'webvr-polyfill:mouse-keyboard'; + this.deviceName = 'VR Position Device (webvr-polyfill:mouse-keyboard)'; + + // Attach to mouse and keyboard events. + window.addEventListener('keydown', this.onKeyDown_.bind(this)); + window.addEventListener('mousemove', this.onMouseMove_.bind(this)); + window.addEventListener('mousedown', this.onMouseDown_.bind(this)); + window.addEventListener('mouseup', this.onMouseUp_.bind(this)); + + this.phi = 0; + this.theta = 0; + + // Variables for keyboard-based rotation animation. + this.targetAngle = null; + + // State variables for calculations. + this.euler = new THREE.Euler(); + this.orientation = new THREE.Quaternion(); + + // Variables for mouse-based rotation. + this.rotateStart = new THREE.Vector2(); + this.rotateEnd = new THREE.Vector2(); + this.rotateDelta = new THREE.Vector2(); +} +MouseKeyboardPositionSensorVRDevice.prototype = new PositionSensorVRDevice(); + +/** + * Returns {orientation: {x,y,z,w}, position: null}. + * Position is not supported for parity with other PositionSensors. + */ +MouseKeyboardPositionSensorVRDevice.prototype.getState = function() { + this.euler.set(this.phi, this.theta, 0, 'YXZ'); + this.orientation.setFromEuler(this.euler); + + return { + hasOrientation: true, + orientation: this.orientation, + hasPosition: false, + position: null + } +}; + +MouseKeyboardPositionSensorVRDevice.prototype.onKeyDown_ = function(e) { + // Track WASD and arrow keys. + if (e.keyCode == 38) { // Up key. + this.animatePhi_(this.phi + KEY_SPEED); + } else if (e.keyCode == 39) { // Right key. + this.animateTheta_(this.theta - KEY_SPEED); + } else if (e.keyCode == 40) { // Down key. + this.animatePhi_(this.phi - KEY_SPEED); + } else if (e.keyCode == 37) { // Left key. + this.animateTheta_(this.theta + KEY_SPEED); + } +}; + +MouseKeyboardPositionSensorVRDevice.prototype.animateTheta_ = function(targetAngle) { + this.animateKeyTransitions_('theta', targetAngle); +}; + +MouseKeyboardPositionSensorVRDevice.prototype.animatePhi_ = function(targetAngle) { + // Prevent looking too far up or down. + targetAngle = Util.clamp(targetAngle, -Math.PI/2, Math.PI/2); + this.animateKeyTransitions_('phi', targetAngle); +}; + +/** + * Start an animation to transition an angle from one value to another. + */ +MouseKeyboardPositionSensorVRDevice.prototype.animateKeyTransitions_ = function(angleName, targetAngle) { + // If an animation is currently running, cancel it. + if (this.angleAnimation) { + clearInterval(this.angleAnimation); + } + var startAngle = this[angleName]; + var startTime = new Date(); + // Set up an interval timer to perform the animation. + this.angleAnimation = setInterval(function() { + // Once we're finished the animation, we're done. + var elapsed = new Date() - startTime; + if (elapsed >= KEY_ANIMATION_DURATION) { + this[angleName] = targetAngle; + clearInterval(this.angleAnimation); + return; + } + // Linearly interpolate the angle some amount. + var percent = elapsed / KEY_ANIMATION_DURATION; + this[angleName] = startAngle + (targetAngle - startAngle) * percent; + }.bind(this), 1000/60); +}; + +MouseKeyboardPositionSensorVRDevice.prototype.onMouseDown_ = function(e) { + this.rotateStart.set(e.clientX, e.clientY); + this.isDragging = true; +}; + +// Very similar to https://gist.github.com/mrflix/8351020 +MouseKeyboardPositionSensorVRDevice.prototype.onMouseMove_ = function(e) { + if (!this.isDragging && !this.isPointerLocked_()) { + return; + } + // Support pointer lock API. + if (this.isPointerLocked_()) { + var movementX = e.movementX || e.mozMovementX || 0; + var movementY = e.movementY || e.mozMovementY || 0; + this.rotateEnd.set(this.rotateStart.x - movementX, this.rotateStart.y - movementY); + } else { + this.rotateEnd.set(e.clientX, e.clientY); + } + // Calculate how much we moved in mouse space. + this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart); + this.rotateStart.copy(this.rotateEnd); + + // Keep track of the cumulative euler angles. + var element = document.body; + this.phi += 2 * Math.PI * this.rotateDelta.y / element.clientHeight * MOUSE_SPEED_Y; + this.theta += 2 * Math.PI * this.rotateDelta.x / element.clientWidth * MOUSE_SPEED_X; + + // Prevent looking too far up or down. + this.phi = Util.clamp(this.phi, -Math.PI/2, Math.PI/2); +}; + +MouseKeyboardPositionSensorVRDevice.prototype.onMouseUp_ = function(e) { + this.isDragging = false; +}; + +MouseKeyboardPositionSensorVRDevice.prototype.isPointerLocked_ = function() { + var el = document.pointerLockElement || document.mozPointerLockElement || + document.webkitPointerLockElement; + return el !== undefined; +}; + +MouseKeyboardPositionSensorVRDevice.prototype.resetSensor = function() { + console.error('Not implemented yet.'); +}; + +module.exports = MouseKeyboardPositionSensorVRDevice; + +},{"./base.js":1,"./three-math.js":9,"./util.js":11}],7:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var THREE = _dereq_('./three-math.js'); + +var DEBUG = false; + +/** + * Given an orientation and the gyroscope data, predicts the future orientation + * of the head. This makes rendering appear faster. + * + * Also see: http://msl.cs.uiuc.edu/~lavalle/papers/LavYerKatAnt14.pdf + * + * @param {Number} predictionTimeS time from head movement to the appearance of + * the corresponding image. + */ +function PosePredictor(predictionTimeS) { + this.predictionTimeS = predictionTimeS; + + // The quaternion corresponding to the previous state. + this.previousQ = new THREE.Quaternion(); + // Previous time a prediction occurred. + this.previousTimestampS = null; + + // The delta quaternion that adjusts the current pose. + this.deltaQ = new THREE.Quaternion(); + // The output quaternion. + this.outQ = new THREE.Quaternion(); +} + +PosePredictor.prototype.getPrediction = function(currentQ, gyro, timestampS) { + if (!this.previousTimestampS) { + this.previousQ.copy(currentQ); + this.previousTimestampS = timestampS; + return currentQ; + } + + // Calculate axis and angle based on gyroscope rotation rate data. + var axis = new THREE.Vector3(); + axis.copy(gyro); + axis.normalize(); + + var angularSpeed = gyro.length(); + + // If we're rotating slowly, don't do prediction. + if (angularSpeed < THREE.Math.degToRad(20)) { + if (DEBUG) { + console.log('Moving slowly, at %s deg/s: no prediction', + THREE.Math.radToDeg(angularSpeed).toFixed(1)); + } + this.outQ.copy(currentQ); + this.previousQ.copy(currentQ); + return this.outQ; + } + + // Get the predicted angle based on the time delta and latency. + var deltaT = timestampS - this.previousTimestampS; + var predictAngle = angularSpeed * this.predictionTimeS; + + this.deltaQ.setFromAxisAngle(axis, predictAngle); + this.outQ.copy(this.previousQ); + this.outQ.multiply(this.deltaQ); + + this.previousQ.copy(currentQ); + + return this.outQ; +}; + + +module.exports = PosePredictor; + +},{"./three-math.js":9}],8:[function(_dereq_,module,exports){ +function SensorSample(sample, timestampS) { + this.set(sample, timestampS); +}; + +SensorSample.prototype.set = function(sample, timestampS) { + this.sample = sample; + this.timestampS = timestampS; +}; + +SensorSample.prototype.copy = function(sensorSample) { + this.set(sensorSample.sample, sensorSample.timestampS); +}; + +module.exports = SensorSample; + +},{}],9:[function(_dereq_,module,exports){ +/* + * A subset of THREE.js, providing mostly quaternion and euler-related + * operations, manually lifted from + * https://github.com/mrdoob/three.js/tree/master/src/math, as of 9c30286b38df039fca389989ff06ea1c15d6bad1 + */ + +// Only use if the real THREE is not provided. +var THREE = window.THREE || {}; + +// If some piece of THREE is missing, fill it in here. +if (!THREE.Quaternion || !THREE.Vector3 || !THREE.Vector2 || !THREE.Euler || !THREE.Math) { +console.log('No THREE.js found.'); + + +/*** START Quaternion ***/ + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://exocortex.com + */ + +THREE.Quaternion = function ( x, y, z, w ) { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._w = ( w !== undefined ) ? w : 1; + +}; + +THREE.Quaternion.prototype = { + + constructor: THREE.Quaternion, + + _x: 0,_y: 0, _z: 0, _w: 0, + + get x () { + + return this._x; + + }, + + set x ( value ) { + + this._x = value; + this.onChangeCallback(); + + }, + + get y () { + + return this._y; + + }, + + set y ( value ) { + + this._y = value; + this.onChangeCallback(); + + }, + + get z () { + + return this._z; + + }, + + set z ( value ) { + + this._z = value; + this.onChangeCallback(); + + }, + + get w () { + + return this._w; + + }, + + set w ( value ) { + + this._w = value; + this.onChangeCallback(); + + }, + + set: function ( x, y, z, w ) { + + this._x = x; + this._y = y; + this._z = z; + this._w = w; + + this.onChangeCallback(); + + return this; + + }, + + copy: function ( quaternion ) { + + this._x = quaternion.x; + this._y = quaternion.y; + this._z = quaternion.z; + this._w = quaternion.w; + + this.onChangeCallback(); + + return this; + + }, + + setFromEuler: function ( euler, update ) { + + if ( euler instanceof THREE.Euler === false ) { + + throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + } + + // http://www.mathworks.com/matlabcentral/fileexchange/ + // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ + // content/SpinCalc.m + + var c1 = Math.cos( euler._x / 2 ); + var c2 = Math.cos( euler._y / 2 ); + var c3 = Math.cos( euler._z / 2 ); + var s1 = Math.sin( euler._x / 2 ); + var s2 = Math.sin( euler._y / 2 ); + var s3 = Math.sin( euler._z / 2 ); + + if ( euler.order === 'XYZ' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( euler.order === 'YXZ' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } else if ( euler.order === 'ZXY' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( euler.order === 'ZYX' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } else if ( euler.order === 'YZX' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( euler.order === 'XZY' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } + + if ( update !== false ) this.onChangeCallback(); + + return this; + + }, + + setFromAxisAngle: function ( axis, angle ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm + + // assumes axis is normalized + + var halfAngle = angle / 2, s = Math.sin( halfAngle ); + + this._x = axis.x * s; + this._y = axis.y * s; + this._z = axis.z * s; + this._w = Math.cos( halfAngle ); + + this.onChangeCallback(); + + return this; + + }, + + setFromRotationMatrix: function ( m ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements, + + m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], + m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], + m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], + + trace = m11 + m22 + m33, + s; + + if ( trace > 0 ) { + + s = 0.5 / Math.sqrt( trace + 1.0 ); + + this._w = 0.25 / s; + this._x = ( m32 - m23 ) * s; + this._y = ( m13 - m31 ) * s; + this._z = ( m21 - m12 ) * s; + + } else if ( m11 > m22 && m11 > m33 ) { + + s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); + + this._w = ( m32 - m23 ) / s; + this._x = 0.25 * s; + this._y = ( m12 + m21 ) / s; + this._z = ( m13 + m31 ) / s; + + } else if ( m22 > m33 ) { + + s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); + + this._w = ( m13 - m31 ) / s; + this._x = ( m12 + m21 ) / s; + this._y = 0.25 * s; + this._z = ( m23 + m32 ) / s; + + } else { + + s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); + + this._w = ( m21 - m12 ) / s; + this._x = ( m13 + m31 ) / s; + this._y = ( m23 + m32 ) / s; + this._z = 0.25 * s; + + } + + this.onChangeCallback(); + + return this; + + }, + + setFromUnitVectors: function () { + + // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final + + // assumes direction vectors vFrom and vTo are normalized + + var v1, r; + + var EPS = 0.000001; + + return function ( vFrom, vTo ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + r = vFrom.dot( vTo ) + 1; + + if ( r < EPS ) { + + r = 0; + + if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { + + v1.set( - vFrom.y, vFrom.x, 0 ); + + } else { + + v1.set( 0, - vFrom.z, vFrom.y ); + + } + + } else { + + v1.crossVectors( vFrom, vTo ); + + } + + this._x = v1.x; + this._y = v1.y; + this._z = v1.z; + this._w = r; + + this.normalize(); + + return this; + + } + + }(), + + inverse: function () { + + this.conjugate().normalize(); + + return this; + + }, + + conjugate: function () { + + this._x *= - 1; + this._y *= - 1; + this._z *= - 1; + + this.onChangeCallback(); + + return this; + + }, + + dot: function ( v ) { + + return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; + + }, + + lengthSq: function () { + + return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; + + }, + + length: function () { + + return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); + + }, + + normalize: function () { + + var l = this.length(); + + if ( l === 0 ) { + + this._x = 0; + this._y = 0; + this._z = 0; + this._w = 1; + + } else { + + l = 1 / l; + + this._x = this._x * l; + this._y = this._y * l; + this._z = this._z * l; + this._w = this._w * l; + + } + + this.onChangeCallback(); + + return this; + + }, + + multiply: function ( q, p ) { + + if ( p !== undefined ) { + + console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); + return this.multiplyQuaternions( q, p ); + + } + + return this.multiplyQuaternions( this, q ); + + }, + + multiplyQuaternions: function ( a, b ) { + + // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm + + var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; + var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; + + this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; + this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; + this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; + this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; + + this.onChangeCallback(); + + return this; + + }, + + multiplyVector3: function ( vector ) { + + console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); + return vector.applyQuaternion( this ); + + }, + + slerp: function ( qb, t ) { + + if ( t === 0 ) return this; + if ( t === 1 ) return this.copy( qb ); + + var x = this._x, y = this._y, z = this._z, w = this._w; + + // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + + var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; + + if ( cosHalfTheta < 0 ) { + + this._w = - qb._w; + this._x = - qb._x; + this._y = - qb._y; + this._z = - qb._z; + + cosHalfTheta = - cosHalfTheta; + + } else { + + this.copy( qb ); + + } + + if ( cosHalfTheta >= 1.0 ) { + + this._w = w; + this._x = x; + this._y = y; + this._z = z; + + return this; + + } + + var halfTheta = Math.acos( cosHalfTheta ); + var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); + + if ( Math.abs( sinHalfTheta ) < 0.001 ) { + + this._w = 0.5 * ( w + this._w ); + this._x = 0.5 * ( x + this._x ); + this._y = 0.5 * ( y + this._y ); + this._z = 0.5 * ( z + this._z ); + + return this; + + } + + var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, + ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; + + this._w = ( w * ratioA + this._w * ratioB ); + this._x = ( x * ratioA + this._x * ratioB ); + this._y = ( y * ratioA + this._y * ratioB ); + this._z = ( z * ratioA + this._z * ratioB ); + + this.onChangeCallback(); + + return this; + + }, + + equals: function ( quaternion ) { + + return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this._x = array[ offset ]; + this._y = array[ offset + 1 ]; + this._z = array[ offset + 2 ]; + this._w = array[ offset + 3 ]; + + this.onChangeCallback(); + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this._x; + array[ offset + 1 ] = this._y; + array[ offset + 2 ] = this._z; + array[ offset + 3 ] = this._w; + + return array; + + }, + + onChange: function ( callback ) { + + this.onChangeCallback = callback; + + return this; + + }, + + onChangeCallback: function () {}, + + clone: function () { + + return new THREE.Quaternion( this._x, this._y, this._z, this._w ); + + } + +}; + +THREE.Quaternion.slerp = function ( qa, qb, qm, t ) { + + return qm.copy( qa ).slerp( qb, t ); + +} + +/*** END Quaternion ***/ +/*** START Vector2 ***/ +/** + * @author mrdoob / http://mrdoob.com/ + * @author philogb / http://blog.thejit.org/ + * @author egraether / http://egraether.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.Vector2 = function ( x, y ) { + + this.x = x || 0; + this.y = y || 0; + +}; + +THREE.Vector2.prototype = { + + constructor: THREE.Vector2, + + set: function ( x, y ) { + + this.x = x; + this.y = y; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + + return this; + + }, + + multiply: function ( v ) { + + this.x *= v.x; + this.y *= v.y; + + return this; + + }, + + multiplyScalar: function ( s ) { + + this.x *= s; + this.y *= s; + + return this; + + }, + + divide: function ( v ) { + + this.x /= v.x; + this.y /= v.y; + + return this; + + }, + + divideScalar: function ( scalar ) { + + if ( scalar !== 0 ) { + + var invScalar = 1 / scalar; + + this.x *= invScalar; + this.y *= invScalar; + + } else { + + this.x = 0; + this.y = 0; + + } + + return this; + + }, + + min: function ( v ) { + + if ( this.x > v.x ) { + + this.x = v.x; + + } + + if ( this.y > v.y ) { + + this.y = v.y; + + } + + return this; + + }, + + max: function ( v ) { + + if ( this.x < v.x ) { + + this.x = v.x; + + } + + if ( this.y < v.y ) { + + this.y = v.y; + + } + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + if ( this.x < min.x ) { + + this.x = min.x; + + } else if ( this.x > max.x ) { + + this.x = max.x; + + } + + if ( this.y < min.y ) { + + this.y = min.y; + + } else if ( this.y > max.y ) { + + this.y = max.y; + + } + + return this; + }, + + clampScalar: ( function () { + + var min, max; + + return function ( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector2(); + max = new THREE.Vector2(); + + } + + min.set( minVal, minVal ); + max.set( maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + } )(), + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + distanceTo: function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared: function ( v ) { + + var dx = this.x - v.x, dy = this.y - v.y; + return dx * dx + dy * dy; + + }, + + setLength: function ( l ) { + + var oldLength = this.length(); + + if ( oldLength !== 0 && l !== oldLength ) { + + this.multiplyScalar( l / oldLength ); + } + + return this; + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + + return array; + + }, + + fromAttribute: function ( attribute, index, offset ) { + + if ( offset === undefined ) offset = 0; + + index = index * attribute.itemSize + offset; + + this.x = attribute.array[ index ]; + this.y = attribute.array[ index + 1 ]; + + return this; + + }, + + clone: function () { + + return new THREE.Vector2( this.x, this.y ); + + } + +}; +/*** END Vector2 ***/ +/*** START Vector3 ***/ + +/** + * @author mrdoob / http://mrdoob.com/ + * @author *kile / http://kile.stravaganza.org/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Vector3 = function ( x, y, z ) { + + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + +}; + +THREE.Vector3.prototype = { + + constructor: THREE.Vector3, + + set: function ( x, y, z ) { + + this.x = x; + this.y = y; + this.z = z; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setZ: function ( z ) { + + this.z = z; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + case 2: this.z = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + case 2: return this.z; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + this.z = v.z; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + this.z += v.z; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + this.z += s; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + + return this; + + }, + + multiply: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); + return this.multiplyVectors( v, w ); + + } + + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; + + return this; + + }, + + multiplyScalar: function ( scalar ) { + + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + + return this; + + }, + + multiplyVectors: function ( a, b ) { + + this.x = a.x * b.x; + this.y = a.y * b.y; + this.z = a.z * b.z; + + return this; + + }, + + applyEuler: function () { + + var quaternion; + + return function ( euler ) { + + if ( euler instanceof THREE.Euler === false ) { + + console.error( 'THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + + } + + if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); + + this.applyQuaternion( quaternion.setFromEuler( euler ) ); + + return this; + + }; + + }(), + + applyAxisAngle: function () { + + var quaternion; + + return function ( axis, angle ) { + + if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); + + this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); + + return this; + + }; + + }(), + + applyMatrix3: function ( m ) { + + var x = this.x; + var y = this.y; + var z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; + this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; + this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; + + return this; + + }, + + applyMatrix4: function ( m ) { + + // input: THREE.Matrix4 affine matrix + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ]; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ]; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ]; + + return this; + + }, + + applyProjection: function ( m ) { + + // input: THREE.Matrix4 projection matrix + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide + + this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d; + this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d; + this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d; + + return this; + + }, + + applyQuaternion: function ( q ) { + + var x = this.x; + var y = this.y; + var z = this.z; + + var qx = q.x; + var qy = q.y; + var qz = q.z; + var qw = q.w; + + // calculate quat * vector + + var ix = qw * x + qy * z - qz * y; + var iy = qw * y + qz * x - qx * z; + var iz = qw * z + qx * y - qy * x; + var iw = - qx * x - qy * y - qz * z; + + // calculate result * inverse quat + + this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; + this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; + this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; + + return this; + + }, + + project: function () { + + var matrix; + + return function ( camera ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + + matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) ); + return this.applyProjection( matrix ); + + }; + + }(), + + unproject: function () { + + var matrix; + + return function ( camera ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + + matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) ); + return this.applyProjection( matrix ); + + }; + + }(), + + transformDirection: function ( m ) { + + // input: THREE.Matrix4 affine matrix + // vector interpreted as a direction + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; + + this.normalize(); + + return this; + + }, + + divide: function ( v ) { + + this.x /= v.x; + this.y /= v.y; + this.z /= v.z; + + return this; + + }, + + divideScalar: function ( scalar ) { + + if ( scalar !== 0 ) { + + var invScalar = 1 / scalar; + + this.x *= invScalar; + this.y *= invScalar; + this.z *= invScalar; + + } else { + + this.x = 0; + this.y = 0; + this.z = 0; + + } + + return this; + + }, + + min: function ( v ) { + + if ( this.x > v.x ) { + + this.x = v.x; + + } + + if ( this.y > v.y ) { + + this.y = v.y; + + } + + if ( this.z > v.z ) { + + this.z = v.z; + + } + + return this; + + }, + + max: function ( v ) { + + if ( this.x < v.x ) { + + this.x = v.x; + + } + + if ( this.y < v.y ) { + + this.y = v.y; + + } + + if ( this.z < v.z ) { + + this.z = v.z; + + } + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + if ( this.x < min.x ) { + + this.x = min.x; + + } else if ( this.x > max.x ) { + + this.x = max.x; + + } + + if ( this.y < min.y ) { + + this.y = min.y; + + } else if ( this.y > max.y ) { + + this.y = max.y; + + } + + if ( this.z < min.z ) { + + this.z = min.z; + + } else if ( this.z > max.z ) { + + this.z = max.z; + + } + + return this; + + }, + + clampScalar: ( function () { + + var min, max; + + return function ( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector3(); + max = new THREE.Vector3(); + + } + + min.set( minVal, minVal, minVal ); + max.set( maxVal, maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + } )(), + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y + this.z * v.z; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y + this.z * this.z; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); + + }, + + lengthManhattan: function () { + + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + setLength: function ( l ) { + + var oldLength = this.length(); + + if ( oldLength !== 0 && l !== oldLength ) { + + this.multiplyScalar( l / oldLength ); + } + + return this; + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; + + return this; + + }, + + cross: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); + return this.crossVectors( v, w ); + + } + + var x = this.x, y = this.y, z = this.z; + + this.x = y * v.z - z * v.y; + this.y = z * v.x - x * v.z; + this.z = x * v.y - y * v.x; + + return this; + + }, + + crossVectors: function ( a, b ) { + + var ax = a.x, ay = a.y, az = a.z; + var bx = b.x, by = b.y, bz = b.z; + + this.x = ay * bz - az * by; + this.y = az * bx - ax * bz; + this.z = ax * by - ay * bx; + + return this; + + }, + + projectOnVector: function () { + + var v1, dot; + + return function ( vector ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + v1.copy( vector ).normalize(); + + dot = this.dot( v1 ); + + return this.copy( v1 ).multiplyScalar( dot ); + + }; + + }(), + + projectOnPlane: function () { + + var v1; + + return function ( planeNormal ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + v1.copy( this ).projectOnVector( planeNormal ); + + return this.sub( v1 ); + + } + + }(), + + reflect: function () { + + // reflect incident vector off plane orthogonal to normal + // normal is assumed to have unit length + + var v1; + + return function ( normal ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); + + } + + }(), + + angleTo: function ( v ) { + + var theta = this.dot( v ) / ( this.length() * v.length() ); + + // clamp, to handle numerical problems + + return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) ); + + }, + + distanceTo: function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared: function ( v ) { + + var dx = this.x - v.x; + var dy = this.y - v.y; + var dz = this.z - v.z; + + return dx * dx + dy * dy + dz * dz; + + }, + + setEulerFromRotationMatrix: function ( m, order ) { + + console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); + + }, + + setEulerFromQuaternion: function ( q, order ) { + + console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); + + }, + + getPositionFromMatrix: function ( m ) { + + console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); + + return this.setFromMatrixPosition( m ); + + }, + + getScaleFromMatrix: function ( m ) { + + console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); + + return this.setFromMatrixScale( m ); + }, + + getColumnFromMatrix: function ( index, matrix ) { + + console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); + + return this.setFromMatrixColumn( index, matrix ); + + }, + + setFromMatrixPosition: function ( m ) { + + this.x = m.elements[ 12 ]; + this.y = m.elements[ 13 ]; + this.z = m.elements[ 14 ]; + + return this; + + }, + + setFromMatrixScale: function ( m ) { + + var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length(); + var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length(); + var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length(); + + this.x = sx; + this.y = sy; + this.z = sz; + + return this; + }, + + setFromMatrixColumn: function ( index, matrix ) { + + var offset = index * 4; + + var me = matrix.elements; + + this.x = me[ offset ]; + this.y = me[ offset + 1 ]; + this.z = me[ offset + 2 ]; + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; + + return array; + + }, + + fromAttribute: function ( attribute, index, offset ) { + + if ( offset === undefined ) offset = 0; + + index = index * attribute.itemSize + offset; + + this.x = attribute.array[ index ]; + this.y = attribute.array[ index + 1 ]; + this.z = attribute.array[ index + 2 ]; + + return this; + + }, + + clone: function () { + + return new THREE.Vector3( this.x, this.y, this.z ); + + } + +}; +/*** END Vector3 ***/ +/*** START Euler ***/ +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://exocortex.com + */ + +THREE.Euler = function ( x, y, z, order ) { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._order = order || THREE.Euler.DefaultOrder; + +}; + +THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; + +THREE.Euler.DefaultOrder = 'XYZ'; + +THREE.Euler.prototype = { + + constructor: THREE.Euler, + + _x: 0, _y: 0, _z: 0, _order: THREE.Euler.DefaultOrder, + + get x () { + + return this._x; + + }, + + set x ( value ) { + + this._x = value; + this.onChangeCallback(); + + }, + + get y () { + + return this._y; + + }, + + set y ( value ) { + + this._y = value; + this.onChangeCallback(); + + }, + + get z () { + + return this._z; + + }, + + set z ( value ) { + + this._z = value; + this.onChangeCallback(); + + }, + + get order () { + + return this._order; + + }, + + set order ( value ) { + + this._order = value; + this.onChangeCallback(); + + }, + + set: function ( x, y, z, order ) { + + this._x = x; + this._y = y; + this._z = z; + this._order = order || this._order; + + this.onChangeCallback(); + + return this; + + }, + + copy: function ( euler ) { + + this._x = euler._x; + this._y = euler._y; + this._z = euler._z; + this._order = euler._order; + + this.onChangeCallback(); + + return this; + + }, + + setFromRotationMatrix: function ( m, order, update ) { + + var clamp = THREE.Math.clamp; + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements; + var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; + var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; + var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; + + order = order || this._order; + + if ( order === 'XYZ' ) { + + this._y = Math.asin( clamp( m13, - 1, 1 ) ); + + if ( Math.abs( m13 ) < 0.99999 ) { + + this._x = Math.atan2( - m23, m33 ); + this._z = Math.atan2( - m12, m11 ); + + } else { + + this._x = Math.atan2( m32, m22 ); + this._z = 0; + + } + + } else if ( order === 'YXZ' ) { + + this._x = Math.asin( - clamp( m23, - 1, 1 ) ); + + if ( Math.abs( m23 ) < 0.99999 ) { + + this._y = Math.atan2( m13, m33 ); + this._z = Math.atan2( m21, m22 ); + + } else { + + this._y = Math.atan2( - m31, m11 ); + this._z = 0; + + } + + } else if ( order === 'ZXY' ) { + + this._x = Math.asin( clamp( m32, - 1, 1 ) ); + + if ( Math.abs( m32 ) < 0.99999 ) { + + this._y = Math.atan2( - m31, m33 ); + this._z = Math.atan2( - m12, m22 ); + + } else { + + this._y = 0; + this._z = Math.atan2( m21, m11 ); + + } + + } else if ( order === 'ZYX' ) { + + this._y = Math.asin( - clamp( m31, - 1, 1 ) ); + + if ( Math.abs( m31 ) < 0.99999 ) { + + this._x = Math.atan2( m32, m33 ); + this._z = Math.atan2( m21, m11 ); + + } else { + + this._x = 0; + this._z = Math.atan2( - m12, m22 ); + + } + + } else if ( order === 'YZX' ) { + + this._z = Math.asin( clamp( m21, - 1, 1 ) ); + + if ( Math.abs( m21 ) < 0.99999 ) { + + this._x = Math.atan2( - m23, m22 ); + this._y = Math.atan2( - m31, m11 ); + + } else { + + this._x = 0; + this._y = Math.atan2( m13, m33 ); + + } + + } else if ( order === 'XZY' ) { + + this._z = Math.asin( - clamp( m12, - 1, 1 ) ); + + if ( Math.abs( m12 ) < 0.99999 ) { + + this._x = Math.atan2( m32, m22 ); + this._y = Math.atan2( m13, m11 ); + + } else { + + this._x = Math.atan2( - m23, m33 ); + this._y = 0; + + } + + } else { + + console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order ) + + } + + this._order = order; + + if ( update !== false ) this.onChangeCallback(); + + return this; + + }, + + setFromQuaternion: function () { + + var matrix; + + return function ( q, order, update ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + matrix.makeRotationFromQuaternion( q ); + this.setFromRotationMatrix( matrix, order, update ); + + return this; + + }; + + }(), + + setFromVector3: function ( v, order ) { + + return this.set( v.x, v.y, v.z, order || this._order ); + + }, + + reorder: function () { + + // WARNING: this discards revolution information -bhouston + + var q = new THREE.Quaternion(); + + return function ( newOrder ) { + + q.setFromEuler( this ); + this.setFromQuaternion( q, newOrder ); + + }; + + }(), + + equals: function ( euler ) { + + return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); + + }, + + fromArray: function ( array ) { + + this._x = array[ 0 ]; + this._y = array[ 1 ]; + this._z = array[ 2 ]; + if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; + + this.onChangeCallback(); + + return this; + + }, + + toArray: function () { + + return [ this._x, this._y, this._z, this._order ]; + + }, + + toVector3: function ( optionalResult ) { + + if ( optionalResult ) { + + return optionalResult.set( this._x, this._y, this._z ); + + } else { + + return new THREE.Vector3( this._x, this._y, this._z ); + + } + + }, + + onChange: function ( callback ) { + + this.onChangeCallback = callback; + + return this; + + }, + + onChangeCallback: function () {}, + + clone: function () { + + return new THREE.Euler( this._x, this._y, this._z, this._order ); + + } + +}; +/*** END Euler ***/ +/*** START Math ***/ +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Math = { + + generateUUID: function () { + + // http://www.broofa.com/Tools/Math.uuid.htm + + var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' ); + var uuid = new Array( 36 ); + var rnd = 0, r; + + return function () { + + for ( var i = 0; i < 36; i ++ ) { + + if ( i == 8 || i == 13 || i == 18 || i == 23 ) { + + uuid[ i ] = '-'; + + } else if ( i == 14 ) { + + uuid[ i ] = '4'; + + } else { + + if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0; + r = rnd & 0xf; + rnd = rnd >> 4; + uuid[ i ] = chars[ ( i == 19 ) ? ( r & 0x3 ) | 0x8 : r ]; + + } + } + + return uuid.join( '' ); + + }; + + }(), + + // Clamp value to range + + clamp: function ( x, a, b ) { + + return ( x < a ) ? a : ( ( x > b ) ? b : x ); + + }, + + // Clamp value to range to range + + mapLinear: function ( x, a1, a2, b1, b2 ) { + + return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); + + }, + + // http://en.wikipedia.org/wiki/Smoothstep + + smoothstep: function ( x, min, max ) { + + if ( x <= min ) return 0; + if ( x >= max ) return 1; + + x = ( x - min ) / ( max - min ); + + return x * x * ( 3 - 2 * x ); + + }, + + smootherstep: function ( x, min, max ) { + + if ( x <= min ) return 0; + if ( x >= max ) return 1; + + x = ( x - min ) / ( max - min ); + + return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); + + }, + + // Random float from <0, 1> with 16 bits of randomness + // (standard Math.random() creates repetitive patterns when applied over larger space) + + random16: function () { + + return ( 65280 * Math.random() + 255 * Math.random() ) / 65535; + + }, + + // Random integer from interval + + randInt: function ( low, high ) { + + return Math.floor( this.randFloat( low, high ) ); + + }, + + // Random float from interval + + randFloat: function ( low, high ) { + + return low + Math.random() * ( high - low ); + + }, + + // Random float from <-range/2, range/2> interval + + randFloatSpread: function ( range ) { + + return range * ( 0.5 - Math.random() ); + + }, + + degToRad: function () { + + var degreeToRadiansFactor = Math.PI / 180; + + return function ( degrees ) { + + return degrees * degreeToRadiansFactor; + + }; + + }(), + + radToDeg: function () { + + var radianToDegreesFactor = 180 / Math.PI; + + return function ( radians ) { + + return radians * radianToDegreesFactor; + + }; + + }(), + + isPowerOfTwo: function ( value ) { + + return ( value & ( value - 1 ) ) === 0 && value !== 0; + + }, + + nextPowerOfTwo: function ( value ) { + + value --; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value ++; + + return value; + } + +}; + +/*** END Math ***/ + +} + +module.exports = THREE; + +},{}],10:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var THREE = _dereq_('./three-math.js'); +var Util = _dereq_('./util.js'); + +var ROTATE_SPEED = 0.5; +/** + * Provides a quaternion responsible for pre-panning the scene before further + * transformations due to device sensors. + */ +function TouchPanner() { + window.addEventListener('touchstart', this.onTouchStart_.bind(this)); + window.addEventListener('touchmove', this.onTouchMove_.bind(this)); + window.addEventListener('touchend', this.onTouchEnd_.bind(this)); + + this.isTouching = false; + this.rotateStart = new THREE.Vector2(); + this.rotateEnd = new THREE.Vector2(); + this.rotateDelta = new THREE.Vector2(); + + this.theta = 0; + this.orientation = new THREE.Quaternion(); +} + +TouchPanner.prototype.getOrientation = function() { + this.orientation.setFromEuler(new THREE.Euler(0, 0, this.theta)); + return this.orientation; +}; + +TouchPanner.prototype.resetSensor = function() { + this.theta = 0; +}; + +TouchPanner.prototype.onTouchStart_ = function(e) { + // Only respond if there is exactly one touch. + if (e.touches.length != 1) { + return; + } + this.rotateStart.set(e.touches[0].pageX, e.touches[0].pageY); + this.isTouching = true; +}; + +TouchPanner.prototype.onTouchMove_ = function(e) { + if (!this.isTouching) { + return; + } + this.rotateEnd.set(e.touches[0].pageX, e.touches[0].pageY); + this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart); + this.rotateStart.copy(this.rotateEnd); + + // On iOS, direction is inverted. + if (Util.isIOS()) { + this.rotateDelta.x *= -1; + } + + var element = document.body; + this.theta += 2 * Math.PI * this.rotateDelta.x / element.clientWidth * ROTATE_SPEED; +}; + +TouchPanner.prototype.onTouchEnd_ = function(e) { + this.isTouching = false; +}; + +module.exports = TouchPanner; + +},{"./three-math.js":9,"./util.js":11}],11:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var Util = window.Util || {}; + +Util.MIN_TIMESTEP = 0.001; +Util.MAX_TIMESTEP = 1; + +Util.clamp = function(value, min, max) { + return Math.min(Math.max(min, value), max); +}; + +Util.isIOS = function() { + return /iPad|iPhone|iPod/.test(navigator.platform); +}; + +Util.isFirefoxAndroid = function() { + return navigator.userAgent.indexOf('Firefox') !== -1 && navigator.userAgent.indexOf('Android') !== -1; +} + +// Helper method to validate the time steps of sensor timestamps. +Util.isTimestampDeltaValid = function(timestampDeltaS) { + if (isNaN(timestampDeltaS)) { + return false; + } + if (timestampDeltaS <= Util.MIN_TIMESTEP) { + return false; + } + if (timestampDeltaS > Util.MAX_TIMESTEP) { + return false; + } + return true; +} + +module.exports = Util; + +},{}],12:[function(_dereq_,module,exports){ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var CardboardHMDVRDevice = _dereq_('./cardboard-hmd-vr-device.js'); +//var OrientationPositionSensorVRDevice = require('./orientation-position-sensor-vr-device.js'); +var FusionPositionSensorVRDevice = _dereq_('./fusion-position-sensor-vr-device.js'); +var MouseKeyboardPositionSensorVRDevice = _dereq_('./mouse-keyboard-position-sensor-vr-device.js'); +// Uncomment to add positional tracking via webcam. +//var WebcamPositionSensorVRDevice = require('./webcam-position-sensor-vr-device.js'); +var HMDVRDevice = _dereq_('./base.js').HMDVRDevice; +var PositionSensorVRDevice = _dereq_('./base.js').PositionSensorVRDevice; + +function WebVRPolyfill() { + this.devices = []; + + if (!this.isWebVRAvailable()) { + this.enablePolyfill(); + } +} + +WebVRPolyfill.prototype.isWebVRAvailable = function() { + return ('getVRDevices' in navigator) || ('mozGetVRDevices' in navigator); +}; + + +WebVRPolyfill.prototype.enablePolyfill = function() { + // Initialize our virtual VR devices. + if (this.isCardboardCompatible()) { + this.devices.push(new CardboardHMDVRDevice()); + } + + // Polyfill using the right position sensor. + if (this.isMobile()) { + //this.devices.push(new OrientationPositionSensorVRDevice()); + this.devices.push(new FusionPositionSensorVRDevice()); + } else { + if (!WebVRConfig.MOUSE_KEYBOARD_CONTROLS_DISABLED) { + this.devices.push(new MouseKeyboardPositionSensorVRDevice()); + } + // Uncomment to add positional tracking via webcam. + //this.devices.push(new WebcamPositionSensorVRDevice()); + } + + // Provide navigator.getVRDevices. + navigator.getVRDevices = this.getVRDevices.bind(this); + + // Provide the CardboardHMDVRDevice and PositionSensorVRDevice objects. + window.HMDVRDevice = HMDVRDevice; + window.PositionSensorVRDevice = PositionSensorVRDevice; +}; + +WebVRPolyfill.prototype.getVRDevices = function() { + var devices = this.devices; + return new Promise(function(resolve, reject) { + try { + resolve(devices); + } catch (e) { + reject(e); + } + }); +}; + +/** + * Determine if a device is mobile. + */ +WebVRPolyfill.prototype.isMobile = function() { + return /Android/i.test(navigator.userAgent) || + /iPhone|iPad|iPod/i.test(navigator.userAgent); +}; + +WebVRPolyfill.prototype.isCardboardCompatible = function() { + // For now, support all iOS and Android devices. + // Also enable the WebVRConfig.FORCE_VR flag for debugging. + return this.isMobile() || WebVRConfig.FORCE_ENABLE_VR; +}; + +module.exports = WebVRPolyfill; + +},{"./base.js":1,"./cardboard-hmd-vr-device.js":2,"./fusion-position-sensor-vr-device.js":4,"./mouse-keyboard-position-sensor-vr-device.js":6}]},{},[5]); diff --git a/node_modules/webvr-polyfill/package.json b/node_modules/webvr-polyfill/package.json new file mode 100644 index 00000000..88b9e6f7 --- /dev/null +++ b/node_modules/webvr-polyfill/package.json @@ -0,0 +1,83 @@ +{ + "_args": [ + [ + "webvr-polyfill@^0.2.4", + "/Users/smus/Projects/embedvr/node_modules/webvr-boilerplate" + ] + ], + "_from": "webvr-polyfill@>=0.2.4 <0.3.0", + "_id": "webvr-polyfill@0.2.7", + "_inCache": true, + "_installable": true, + "_location": "/webvr-polyfill", + "_nodeVersion": "5.4.0", + "_npmOperationalInternal": { + "host": "packages-6-west.internal.npmjs.com", + "tmp": "tmp/webvr-polyfill-0.2.7.tgz_1455045929559_0.8940962227061391" + }, + "_npmUser": { + "email": "boris@smus.com", + "name": "smus" + }, + "_npmVersion": "3.3.12", + "_phantomChildren": {}, + "_requested": { + "name": "webvr-polyfill", + "raw": "webvr-polyfill@^0.2.4", + "rawSpec": "^0.2.4", + "scope": null, + "spec": ">=0.2.4 <0.3.0", + "type": "range" + }, + "_requiredBy": [ + "/webvr-boilerplate" + ], + "_shasum": "72fd062ecc1c9154e5dc917c55f2be0647e7d207", + "_shrinkwrap": null, + "_spec": "webvr-polyfill@^0.2.4", + "_where": "/Users/smus/Projects/embedvr/node_modules/webvr-boilerplate", + "author": { + "name": "Boris Smus" + }, + "authors": [ + "Boris Smus " + ], + "bugs": { + "url": "https://github.com/borismus/webvr-polyfill/issues" + }, + "dependencies": {}, + "description": "Use WebVR today, on mobile or desktop, without requiring a special browser build.", + "devDependencies": {}, + "directories": {}, + "dist": { + "shasum": "72fd062ecc1c9154e5dc917c55f2be0647e7d207", + "tarball": "http://registry.npmjs.org/webvr-polyfill/-/webvr-polyfill-0.2.7.tgz" + }, + "gitHead": "adf9d0ca046c69a942a15a5e58a1f91f57467044", + "homepage": "https://github.com/borismus/webvr-polyfill", + "keywords": [ + "vr", + "webvr" + ], + "license": "Apache-2.0", + "main": "build/webvr-polyfill.js", + "maintainers": [ + { + "name": "smus", + "email": "boris@smus.com" + } + ], + "name": "webvr-polyfill", + "optionalDependencies": {}, + "readme": "ERROR: No README data found!", + "repository": { + "type": "git", + "url": "git+https://github.com/borismus/webvr-polyfill.git" + }, + "scripts": { + "build": "browserify src/main.js | derequire > build/webvr-polyfill.js", + "copy": "cp build/webvr-polyfill.js ../webvr-boilerplate/node_modules/webvr-polyfill/build/webvr-polyfill.js", + "watch": "watchify src/main.js -v -d -o build/webvr-polyfill.js" + }, + "version": "0.2.7" +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..be5e829f --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "vrview", + "version": "0.2.0", + "description": "Embed VR content into your webpage.", + "main": "index.js", + "//": "TODO: Re-add dependencies once vanilla versions can be used again.", + "dependencies": { + "es6-promise": "^3.0.2" + }, + "devDependencies": {}, + "scripts": { + "copy": "cp ../webvr-boilerplate/build/webvr-manager.js node_modules/webvr-boilerplate/build/", + "build": "browserify src/main.js | derequire > build/vrview.js", + "build-min": "browserify src/main.js | derequire | uglifyjs -c > build/vrview.min.js", + "build-analytics": "browserify src/with-analytics.js | derequire > build/vrview-analytics.js", + "build-analytics-min": "browserify src/with-analytics.js | derequire | uglifyjs -c > build/vrview-analytics.min.js", + "build-dms": "uglifyjs scripts/js/device-motion-sender.js > build/device-motion-sender.min.js", + "watch": "watchify src/main.js -v -d -o build/vrview.js", + "deploy": "./scripts/deploy.sh" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/google/vrview.git" + }, + "author": "", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/google/vrview/issues" + }, + "homepage": "https://github.com/google/vrview#readme" +} diff --git a/scripts/acl.txt b/scripts/acl.txt new file mode 100644 index 00000000..a760a5b5 --- /dev/null +++ b/scripts/acl.txt @@ -0,0 +1,21 @@ +[ + { + "entity": "allUsers", + "role": "READER" + }, + { + "email": "smus@google.com", + "entity": "user-smus@google.com", + "role": "OWNER" + }, + { + "email": "bwuest@google.com", + "entity": "user-bwuest@google.com", + "role": "OWNER" + }, + { + "email": "nathanmartz@google.com", + "entity": "user-nathanmartz@google.com", + "role": "OWNER" + } +] diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100755 index 00000000..c1cd8012 --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env sh +SCRIPT_DIR=`dirname $BASH_SOURCE` + +gsutil cp -r build/ images/ examples/ gs://vrview/ +gsutil -m acl set -r $SCRIPT_DIR/acl.txt gs://vrview/ diff --git a/scripts/js/device-motion-sender.js b/scripts/js/device-motion-sender.js new file mode 100644 index 00000000..59fa734e --- /dev/null +++ b/scripts/js/device-motion-sender.js @@ -0,0 +1,88 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * Sends DeviceMotion events to all embedded VR views via postMessage. Note: + * each iframe must have a class 'vrview'. + * + * This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=150072. + */ +function DeviceMotionSender() { + // This is an iOS-specific workaround. + if (!this.isIOS_()) { + return; + } + + window.addEventListener('devicemotion', this.onDeviceMotion_.bind(this), false); + + // Find the right iFrame to send data to. + this.iframes = document.querySelectorAll('iframe.vrview'); +} + +DeviceMotionSender.prototype.onDeviceMotion_ = function(e) { + var message = { + type: 'DeviceMotion', + deviceMotionEvent: this.cloneDeviceMotionEvent_(e) + }; + for (var i = 0; i < this.iframes.length; i++) { + // Only send data if we're on iOS and we are dealing with a cross-domain + // iframe. + var iframe = this.iframes[i]; + var iframeWindow = iframe.contentWindow; + if (this.isCrossDomainIframe_(iframe)) { + iframeWindow.postMessage(message, '*'); + } + } +}; + +DeviceMotionSender.prototype.cloneDeviceMotionEvent_ = function(e) { + return { + acceleration: { + x: e.acceleration.x, + y: e.acceleration.y, + z: e.acceleration.z, + }, + accelerationIncludingGravity: { + x: e.accelerationIncludingGravity.x, + y: e.accelerationIncludingGravity.y, + z: e.accelerationIncludingGravity.z, + }, + rotationRate: { + alpha: e.rotationRate.alpha, + beta: e.rotationRate.beta, + gamma: e.rotationRate.gamma, + }, + interval: e.interval + }; +}; + +DeviceMotionSender.prototype.isIOS_ = function() { + return /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; +}; + +// From http://stackoverflow.com/questions/12381334/foolproof-way-to-detect-if-iframe-is-cross-domain. +DeviceMotionSender.prototype.isCrossDomainIframe_ = function(iframe) { + var html = null; + try { + var doc = iframe.contentDocument || iframe.contentWindow.document; + html = doc.body.innerHTML; + } catch (err) { + } + + return (html === null); +}; + +var dms = new DeviceMotionSender(); diff --git a/src/analytics.js b/src/analytics.js new file mode 100644 index 00000000..5ee873a1 --- /dev/null +++ b/src/analytics.js @@ -0,0 +1,55 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var MODE_LABELS = { + 0: 'UNKNOWN', + 1: 'NORMAL', + 2: 'MAGIC_WINDOW', + 3: 'VR' +}; + +function Analytics() { + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-35315454-8', 'auto'); + ga('send', 'pageview'); + + this.lastModeChangeTime = window.performance.now(); + this.lastModeLabel = WebVRManager.Modes.UNKNOWN; +} + +Analytics.prototype.logModeChanged = function(mode) { + var modeLabel = MODE_LABELS[mode]; + var lastModeLabel = MODE_LABELS[this.lastMode]; + + console.log('Analytics: going from mode %s to %s', lastModeLabel, modeLabel); + + ga('send', 'screenview', { + appName: 'EmbedVR', + screenName: modeLabel + }); + + var now = window.performance.now(); + var msSinceLastModeChange = Math.round(now - this.lastModeChangeTime); + ga('send', 'timing', 'Time spent in mode', lastModeLabel, msSinceLastModeChange); + + this.lastModeChangeTime = now; + this.lastMode = mode; +} + +window.analytics = new Analytics(); diff --git a/src/device-motion-receiver.js b/src/device-motion-receiver.js new file mode 100644 index 00000000..a0380ac7 --- /dev/null +++ b/src/device-motion-receiver.js @@ -0,0 +1,55 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * Sits in an embedded iframe, receiving DeviceMotion messages from a containing + * iFrame. These messages are converted into synthetic DeviceMotion events. + * + * This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=150072. + */ +function DeviceMotionReceiver() { + window.addEventListener('message', this.onMessage_.bind(this), false); +} + +DeviceMotionReceiver.prototype.onMessage_ = function(event) { + var message = event.data; + if (message.type !== 'DeviceMotion') { + console.warn('Got unknown message of type %s from %s', message.type, message.origin); + return; + } + + console.log('onMessage_', event); + + // Synthesize a DeviceMotion event. + this.synthesizeDeviceMotionEvent_(message.deviceMotionEvent); +}; + +DeviceMotionReceiver.prototype.synthesizeDeviceMotionEvent_ = function(eventData) { + var type = 'devicemotion-iframe'; + var canBubble = false; + var cancelable = false; + + var dme = document.createEvent('DeviceMotionEvent'); + dme.initDeviceMotionEvent(type, canBubble, cancelable, + eventData.acceleration, + eventData.accelerationIncludingGravity, + eventData.rotationRate, + eventData.interval); + + window.dispatchEvent(dme); +}; + +module.exports = DeviceMotionReceiver; diff --git a/src/emitter.js b/src/emitter.js new file mode 100644 index 00000000..f786b83d --- /dev/null +++ b/src/emitter.js @@ -0,0 +1,63 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +function Emitter() { + this.initEmitter(); +} + +Emitter.prototype.initEmitter = function() { + this.callbacks = {}; +}; + +Emitter.prototype.emit = function(eventName) { + var callbacks = this.callbacks[eventName]; + if (!callbacks) { + console.log('No valid callback specified.'); + return; + } + var args = [].slice.call(arguments) + // Eliminate the first param (the callback). + args.shift(); + for (var i = 0; i < callbacks.length; i++) { + callbacks[i].apply(this, args); + } +}; + +Emitter.prototype.on = function(eventName, callback) { + if (eventName in this.callbacks) { + var cbs = this.callbacks[eventName] + if (cbs.indexOf(callback) == -1) { + cbs.push(callback); + } + } else { + this.callbacks[eventName] = [callback]; + } +}; + +Emitter.prototype.removeListener = function(eventName, callback) { + if (!(eventName in this.callbacks)) { + return; + } + var cbs = this.callbacks[eventName]; + var ind = cbs.indexOf(callback); + if (ind == -1) { + console.warn('No matching callback found'); + return; + } + cbs.splice(ind, 1); +}; + +module.exports = Emitter; diff --git a/src/eyes.js b/src/eyes.js new file mode 100644 index 00000000..09279a3e --- /dev/null +++ b/src/eyes.js @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var Eyes = { + LEFT: 0, + RIGHT: 1 +}; + +module.exports = Eyes; diff --git a/src/loading-indicator.js b/src/loading-indicator.js new file mode 100644 index 00000000..4dd64d6b --- /dev/null +++ b/src/loading-indicator.js @@ -0,0 +1,54 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Shows a 2D loading indicator while various pieces of EmbedVR load. + */ +function LoadingIndicator() { + this.el = this.build_(); + document.body.appendChild(this.el); + this.show(); +} + +LoadingIndicator.prototype.build_ = function() { + var overlay = document.createElement('div'); + var s = overlay.style; + s.position = 'fixed'; + s.top = 0; + s.left = 0; + s.width = '100%'; + s.height = '100%'; + s.background = '#eee'; + var img = document.createElement('img'); + img.src = 'images/loading.gif'; + var s = img.style; + s.position = 'absolute'; + s.top = '50%'; + s.left = '50%'; + s.transform = 'translate(-50%, -50%)'; + + overlay.appendChild(img); + return overlay; +}; + +LoadingIndicator.prototype.hide = function() { + this.el.style.display = 'none'; +}; + +LoadingIndicator.prototype.show = function() { + this.el.style.display = 'block'; +}; + +module.exports = LoadingIndicator; diff --git a/src/main.js b/src/main.js new file mode 100644 index 00000000..7d4b76dc --- /dev/null +++ b/src/main.js @@ -0,0 +1,209 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Disable distortion provided by the boilerplate because we are doing +// vertex-based distortion. +WebVRConfig = window.WebVRConfig || {} +WebVRConfig.PREVENT_DISTORTION = true; + +// Initialize the loading indicator as quickly as possible to give the user +// immediate feedback. +var LoadingIndicator = require('./loading-indicator'); +var loadIndicator = new LoadingIndicator(); + +// Include relevant polyfills. +require('../node_modules/webvr-polyfill/build/webvr-polyfill'); +var ES6Promise = require('../node_modules/es6-promise/dist/es6-promise.min'); +// Polyfill ES6 promises for IE. +ES6Promise.polyfill(); + +var PhotosphereRenderer = require('./photosphere-renderer'); +var SceneLoader = require('./scene-loader'); +var Stats = require('../node_modules/stats-js/build/stats.min'); +var Util = require('./util'); + +// Include the DeviceMotionReceiver for the iOS cross domain iframe workaround. +// This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=150072. +var DeviceMotionReceiver = require('./device-motion-receiver'); +var dmr = new DeviceMotionReceiver(); + + +window.addEventListener('load', init); + +var stats = new Stats(); + +var loader = new SceneLoader(); +loader.on('error', onSceneError); +loader.on('load', onSceneLoad); + +var renderer = new PhotosphereRenderer(); +renderer.on('error', onRenderError); + +var videoElement = null; +// TODO: Make this not global. +// Currently global in order to allow callbacks. +var loadedScene = null; + +function init() { + if (!Util.isWebGLEnabled()) { + showError('WebGL not supported.'); + return; + } + // Load the scene. + loader.loadScene(); + + if (Util.getQueryParameter('debug')) { + showStats(); + } +} + +function loadImage(src, params) { + renderer.on('load', onRenderLoad); + renderer.setPhotosphere(src, params); +} + +function onSceneLoad(scene) { + if (!scene || !scene.isComplete()) { + showError('Scene failed to load'); + return; + } + + loadedScene = scene; + + var params = { + isStereo: scene.isStereo, + } + renderer.setDefaultLookDirection(scene.yaw || 0); + + if (scene.preview) { + var onPreviewLoad = function() { + loadIndicator.hide(); + renderer.removeListener('load', onPreviewLoad); + renderer.setPhotosphere(scene.image, params); + } + renderer.removeListener('load', onRenderLoad); + renderer.on('load', onPreviewLoad); + renderer.setPhotosphere(scene.preview, params); + } else if (scene.video) { + if (Util.isIOS() || Util.isIE11()) { + // On iOS and IE 11, if an 'image' param is provided, load it instead of + // showing an error. + // + // TODO(smus): Once video textures are supported, remove this fallback. + if (scene.image) { + loadImage(scene.image, params); + } else { + showError('Video is not supported on this platform (iOS or IE11).'); + } + } else { + // Load the video element. + videoElement = document.createElement('video'); + videoElement.src = scene.video; + videoElement.loop = true; + videoElement.setAttribute('crossorigin', 'anonymous'); + videoElement.addEventListener('canplaythrough', onVideoLoad); + videoElement.addEventListener('error', onVideoError); + } + } else if (scene.image) { + // Otherwise, just render the photosphere. + loadImage(scene.image, params); + } + + console.log('Loaded scene', scene); +} + +function onVideoLoad() { + // Render the stereo video. + var params = { + isStereo: loadedScene.isStereo, + } + renderer.set360Video(videoElement, params); + + // On mobile, tell the user they need to tap to start. Otherwise, autoplay. + if (!Util.isMobile()) { + // Hide loading indicator. + loadIndicator.hide(); + // Autoplay the video on desktop. + videoElement.play(); + } else { + // Tell user to tap to start. + showError('Tap to start video', 'Play'); + document.body.addEventListener('touchend', onVideoTap); + } + + // Prevent onVideoLoad from firing multiple times. + videoElement.removeEventListener('canplaythrough', onVideoLoad); +} + +function onVideoTap() { + hideError(); + videoElement.play(); + + // Prevent multiple play() calls on the video element. + document.body.removeEventListener('touchend', onVideoTap); +} + +function onRenderLoad() { + // Hide loading indicator. + loadIndicator.hide(); +} + +function onSceneError(message) { + showError('Loader: ' + message); +} + +function onRenderError(message) { + showError('Render: ' + message); +} + +function onVideoError(e) { + showError('Video load error'); + console.log(e); +} + +function showError(message, opt_title) { + // Hide loading indicator. + loadIndicator.hide(); + + var error = document.querySelector('#error'); + error.classList.add('visible'); + error.querySelector('.message').innerHTML = message; + + var title = (opt_title !== undefined ? opt_title : 'Error'); + error.querySelector('.title').innerHTML = title; +} + +function hideError() { + var error = document.querySelector('#error'); + error.classList.remove('visible'); +} + +function showStats() { + stats.setMode(0); // 0: fps, 1: ms + + // Align bottom-left. + stats.domElement.style.position = 'absolute'; + stats.domElement.style.left = '0px'; + stats.domElement.style.bottom = '0px'; + document.body.appendChild(stats.domElement); +} + +function loop(time) { + stats.begin(); + renderer.render(time); + stats.end(); + requestAnimationFrame(loop); +} +requestAnimationFrame(loop); diff --git a/src/photosphere-renderer.js b/src/photosphere-renderer.js new file mode 100644 index 00000000..e02417ec --- /dev/null +++ b/src/photosphere-renderer.js @@ -0,0 +1,258 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var Emitter = require('./emitter'); +var Eyes = require('./eyes'); +var THREE = require('../node_modules/three/three'); +THREE.VRControls = require('../node_modules/three/examples/js/controls/VRControls'); +THREE.VREffect = require('../node_modules/three/examples/js/effects/VREffect'); +var Util = require('./util'); +var VertexDistorter = require('./vertex-distorter'); +require('../node_modules/webvr-boilerplate/build/webvr-manager'); + +function PhotosphereRenderer() { + this.init(); +} +PhotosphereRenderer.prototype = new Emitter(); + +PhotosphereRenderer.prototype.init = function() { + var container = document.querySelector('body'); + var camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.1, 100); + var cameraDummy = new THREE.Object3D(); + cameraDummy.add(camera); + + // Antialiasing temporarily disabled to improve performance. + var renderer = new THREE.WebGLRenderer({antialias: false}); + renderer.setClearColor(0x000000, 0); + renderer.setSize(window.innerWidth, window.innerHeight); + + // Round down fractional DPR values for better performance. + renderer.setPixelRatio(Math.floor(window.devicePixelRatio)); + container.appendChild(renderer.domElement); + + var controls = new THREE.VRControls(camera); + var effect = new THREE.VREffect(renderer); + effect.setSize(window.innerWidth, window.innerHeight); + + this.camera = camera; + this.renderer = renderer; + this.effect = effect; + this.controls = controls; + this.manager = new WebVRManager(renderer, effect, {isUndistorted: true}); + + this.initScenes_(); + + // The vertex distorter. + this.distorter = new VertexDistorter(this.manager.getDeviceInfo()); + + this.manager.on('modechange', this.onModeChange_.bind(this)); + this.manager.on('viewerchange', this.onViewerChange_.bind(this)); + + // Watch the resize event. + window.addEventListener('resize', this.onResize_.bind(this)); + + var that = this; + navigator.getVRDevices().then(function(devices) { + devices.forEach(function(device) { + if (device instanceof HMDVRDevice) { + that.hmd = device; + } + }); + }); +}; + +PhotosphereRenderer.prototype.render = function(timestamp) { + this.controls.update(); + this.manager.render(this.scenes, this.camera, timestamp); +}; + +PhotosphereRenderer.prototype.setDefaultLookDirection = function(phi) { + // Rotate the camera parent to take into account the scene's rotation. + this.camera.parent.rotation.y = phi; +}; + +/** + * Sets the photosphere based on the image in the source. Supports stereo and + * mono photospheres. + * + * Emits 'load' and 'error' events. + */ +PhotosphereRenderer.prototype.setPhotosphere = function(src, opt_params) { + var params = opt_params || {}; + + this.isStereo = !!params.isStereo; + this.src = src; + + // Load texture. + var loader = new THREE.TextureLoader(); + loader.crossOrigin = 'anonymous'; + loader.load(src, this.onTextureLoaded_.bind(this), null, + this.onTextureError_.bind(this)); +}; + +PhotosphereRenderer.prototype.set360Video = function(videoElement, opt_params) { + var params = opt_params || {}; + + this.isStereo = !!params.isStereo; + + // Load the video texture. + var videoTexture = new THREE.VideoTexture(videoElement); + videoTexture.minFilter = THREE.LinearFilter; + videoTexture.magFilter = THREE.LinearFilter; + videoTexture.format = THREE.RGBFormat; + videoTexture.generateMipmaps = false; + videoTexture.needsUpdate = true; + + this.onTextureLoaded_(videoTexture); +}; + +PhotosphereRenderer.prototype.initScenes_ = function() { + this.sceneLeft = this.createScene_(); + this.sceneRight = this.createScene_(); + this.sceneLeft.add(this.camera.parent); + + this.scenes = [this.sceneLeft, this.sceneRight]; + this.eyes = [Eyes.LEFT, Eyes.RIGHT]; +}; + +PhotosphereRenderer.prototype.onTextureLoaded_ = function(texture) { + var sphereLeft; + var sphereRight; + if (this.isStereo) { + sphereLeft = this.createPhotosphere_(texture, {offsetY: 0.5, scaleY: 0.5}); + sphereRight = this.createPhotosphere_(texture, {offsetY: 0, scaleY: 0.5}); + } else { + sphereLeft = this.createPhotosphere_(texture); + sphereRight = this.createPhotosphere_(texture); + } + + this.sceneLeft.getObjectByName('photo').children = [sphereLeft]; + this.sceneRight.getObjectByName('photo').children = [sphereRight]; + + this.emit('load'); +}; + +PhotosphereRenderer.prototype.onTextureError_ = function(error) { + this.emit('error', 'Unable to load texture from ' + this.src); +}; + + +PhotosphereRenderer.prototype.createPhotosphere_ = function(texture, opt_params) { + var p = opt_params || {}; + p.scaleX = p.scaleX || 1; + p.scaleY = p.scaleY || 1; + p.offsetX = p.offsetX || 0; + p.offsetY = p.offsetY || 0; + p.phiStart = p.phiStart || 0; + p.phiLength = p.phiLength || Math.PI * 2; + p.thetaStart = p.thetaStart || 0; + p.thetaLength = p.thetaLength || Math.PI; + + var geometry = new THREE.SphereGeometry(1, 48, 48, + p.phiStart, p.phiLength, p.thetaStart, p.thetaLength); + geometry.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1)); + var uvs = geometry.faceVertexUvs[0]; + for (var i = 0; i < uvs.length; i ++) { + for (var j = 0; j < 3; j ++) { + uvs[i][j].x *= p.scaleX; + uvs[i][j].x += p.offsetX; + uvs[i][j].y *= p.scaleY; + uvs[i][j].y += p.offsetY; + } + } + + var material = new THREE.MeshBasicMaterial({ map: texture }); + this.distorter.setMap(texture); + var out = new THREE.Mesh(geometry, material); + out.renderOrder = -1; + return out; +}; + +PhotosphereRenderer.prototype.createScene_ = function(opt_params) { + var scene = new THREE.Scene(); + // Add a light. + scene.add(new THREE.PointLight(0xFFFFFF)); + + // Add a group for the photosphere. + var photoGroup = new THREE.Object3D(); + photoGroup.name = 'photo'; + scene.add(photoGroup); + + return scene; +}; + +PhotosphereRenderer.prototype.updateMaterial_ = function(material_FOO) { + for (var i = 0; i < this.scenes.length; i++) { + var eye = this.eyes[i]; + var material = this.distorter.getShaderMaterial(eye); + var scene = this.scenes[i]; + var children = scene.getObjectByName('photo').children; + for (var j = 0; j < children.length; j++) { + var child = children[j]; + child.material = material; + child.material.needsUpdate = true; + } + } +}; + +PhotosphereRenderer.prototype.updateRenderRect_ = function() { + if (this.hmd && this.hmd.setRenderRect) { + var deviceInfo = this.manager.getDeviceInfo(); + var leftRect = deviceInfo.getUndistortedViewportLeftEye(); + var dpr = window.devicePixelRatio; + leftRect.x /= dpr; + leftRect.y /= dpr; + leftRect.width /= dpr; + leftRect.height /= dpr; + + var rightRect = Util.clone(leftRect); + rightRect.x = (window.innerWidth - leftRect.x) - leftRect.width; + + this.hmd.setRenderRect(leftRect, rightRect); + } +}; + +PhotosphereRenderer.prototype.onModeChange_ = function(newMode, oldMode) { + console.log('onModeChange_', newMode); + + var coefficients; + if (newMode == WebVRManager.Modes.VR) { + // Entering VR mode. + this.distorter.setEnabled(true); + this.updateMaterial_(); + } else if (oldMode == WebVRManager.Modes.VR) { + // Leaving VR mode. + this.distorter.setEnabled(false); + this.updateMaterial_(); + } + + if (window.analytics) { + analytics.logModeChanged(newMode); + } +}; + +PhotosphereRenderer.prototype.onViewerChange_ = function(newViewer) { + console.log('onViewerChange_', newViewer); + + // Reset the photosphere with new coefficients. + this.updateMaterial_(); + this.updateRenderRect_(); +}; + +PhotosphereRenderer.prototype.onResize_ = function() { + this.updateRenderRect_(); +}; + +module.exports = PhotosphereRenderer; diff --git a/src/scene-info.js b/src/scene-info.js new file mode 100644 index 00000000..ef580946 --- /dev/null +++ b/src/scene-info.js @@ -0,0 +1,55 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Contains all information about a given scene, including the photosphere asset, + * background music. + */ +function SceneInfo(opt_params) { + var params = opt_params || {}; + this.id = params.id; + this.title = params.title; + this.image = params.image; + this.preview = params.preview; + this.isStereo = !!params.isStereo; + this.audio = params.audio; + this.video = params.video; + this.yaw = params.yaw || 0; + this.isYawOnly = params.isYawOnly; +} + +SceneInfo.prototype.isComplete = function() { + return !!this.image || !!this.video; +}; + +SceneInfo.prototype.toObject = function() { + return { + id: this.id || null, + title: this.title || null, + image: this.image, + preview: this.preview, + isStereo: this.isStereo, + audio: this.audio, + yaw: this.yaw || null, + video: this.video || null, + }; +}; + +SceneInfo.prototype.isImageDataURI = function() { + return this.image.indexOf('data:') == 0; +}; + + +module.exports = SceneInfo; diff --git a/src/scene-loader.js b/src/scene-loader.js new file mode 100644 index 00000000..8c0fa052 --- /dev/null +++ b/src/scene-loader.js @@ -0,0 +1,116 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var Emitter = require('./emitter'); +var SceneInfo = require('./scene-info'); + +var Query = { + JSON_URL: 'url', + VIDEO_URL: 'video', + OBJECT_URL: 'object', + IMAGE_URL: 'image', + PREVIEW_URL: 'preview', + IS_STEREO: 'is_stereo', + AUDIO_URL: 'audio', + START_YAW: 'start_yaw', + IS_YAW_ONLY: 'is_yaw_only', +}; + +function SceneLoader() { +} +SceneLoader.prototype = new Emitter(); + +/** + * Loads a scene from a JSON file. + */ +SceneLoader.prototype.loadFromJson_ = function(url, callback) { + // XHR to fetch the JSON. + var xhr = new XMLHttpRequest(); + xhr.open('GET', url); + var that = this; + xhr.onload = function(e) { + try { + var jsonObj = JSON.parse(this.response); + } catch (e) { + that.emit('error', 'Invalid JSON at ' + url + '.'); + return; + } + var labelObjects = {}; + var labels = jsonObj.labels || []; + // Go through the labels in the data and objectify them. + for (var i = 0; i < labels.length; i++) { + var label = new Label(labels[i]); + labelObjects[label.id] = label; + } + jsonObj.labels = labelObjects; + var scene = new SceneInfo(jsonObj); + that.emit('load', scene); + }; + xhr.send(); +}; + +/** + * Parse out GET parameters from the URL string and load the scene. + */ +SceneLoader.prototype.loadFromGetParams_ = function() { + var params = { + image: Util.getQueryParameter(Query.IMAGE_URL), + video: Util.getQueryParameter(Query.VIDEO_URL), + object: Util.getQueryParameter(Query.OBJECT_URL), + preview: Util.getQueryParameter(Query.PREVIEW_URL), + isStereo: this.parseBoolean_(Util.getQueryParameter(Query.IS_STEREO)), + audio: Util.getQueryParameter(Query.AUDIO_URL), + isYawOnly: this.parseBoolean_(Util.getQueryParameter(Query.IS_YAW_ONLY)), + yaw: THREE.Math.degToRad(Util.getQueryParameter(Query.START_YAW)), + }; + + var count = 0; + count += (params[Query.IMAGE_URL] ? 1 : 0); + count += (params[Query.VIDEO_URL] ? 1 : 0); + count += (params[Query.OBJECT_URL] ? 1 : 0); + // Validate this. + if (count == 0) { + this.emit('error', 'Either "image", "video", or "object" GET parameter is required.'); + return; + } + + var scene = new SceneInfo(params); + this.emit('load', scene); +}; + +SceneLoader.prototype.parseBoolean_ = function(value) { + if (value == 'false') { + return false; + } + return !!value; +}; + +SceneLoader.prototype.loadScene = function(callback) { + // If there's a url param specified, try loading from JSON. + var url = Util.getQueryParameter('url'); + var image = Util.getQueryParameter('image'); + var video = Util.getQueryParameter('video'); + var object = Util.getQueryParameter('object'); + if (url) { + this.loadFromJson_(url); + } else if (image || video || object) { + // Otherwise, try loading from URL parameters. + this.loadFromGetParams_(); + } else { + // If it fails, throw an exception. + this.emit('error', 'Unable to load scene. Required parameter missing.'); + } +}; + +module.exports = SceneLoader; diff --git a/src/util.js b/src/util.js new file mode 100644 index 00000000..aebf87ad --- /dev/null +++ b/src/util.js @@ -0,0 +1,128 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +Util = window.Util || {}; + +Util.isDataURI = function(src) { + return src && src.indexOf('data:') == 0; +}; + +Util.generateUUID = function() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); +}; + +Util.isMobile = function() { + var check = false; + (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); + return check; +}; + +Util.isIOS = function() { + return /(iPad|iPhone|iPod)/g.test(navigator.userAgent); +}; + +Util.cloneObject = function(obj) { + var out = {}; + for (key in obj) { + out[key] = obj[key]; + } + return out; +}; + +Util.hashCode = function(s) { + return s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0); +}; + +Util.loadTrackSrc = function(context, src, callback, opt_progressCallback) { + var request = new XMLHttpRequest(); + request.open('GET', src, true); + request.responseType = 'arraybuffer'; + + // Decode asynchronously. + request.onload = function() { + context.decodeAudioData(request.response, function(buffer) { + callback(buffer); + }, function(e) { + console.error(e); + }); + }; + if (opt_progressCallback) { + request.onprogress = function(e) { + var percent = e.loaded / e.total; + opt_progressCallback(percent); + }; + } + request.send(); +}; + +Util.isPow2 = function(n) { + return (n & (n - 1)) == 0; +}; + +Util.capitalize = function(s) { + return s.charAt(0).toUpperCase() + s.slice(1); +}; + +Util.isIFrame = function() { + try { + return window.self !== window.top; + } catch (e) { + return true; + } +}; + +// From http://goo.gl/4WX3tg +Util.getQueryParameter = function(name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), + results = regex.exec(location.search); + return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); +}; + + +// From http://stackoverflow.com/questions/11871077/proper-way-to-detect-webgl-support. +Util.isWebGLEnabled = function() { + var canvas = document.createElement('canvas'); + try { gl = canvas.getContext("webgl"); } + catch (x) { gl = null; } + + if (gl == null) { + try { gl = canvas.getContext("experimental-webgl"); experimental = true; } + catch (x) { gl = null; } + } + return !!gl; +}; + +Util.clone = function(obj) { + return JSON.parse(JSON.stringify(obj)); +}; + +// From http://stackoverflow.com/questions/10140604/fastest-hypotenuse-in-javascript +Util.hypot = Math.hypot || function(x, y) { + return Math.sqrt(x*x + y*y); +}; + +// From http://stackoverflow.com/a/17447718/693934 +Util.isIE11 = function() { + return navigator.userAgent.match(/Trident/); +}; + +module.exports = Util; diff --git a/src/vertex-distorter.js b/src/vertex-distorter.js new file mode 100644 index 00000000..ad2e3622 --- /dev/null +++ b/src/vertex-distorter.js @@ -0,0 +1,241 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +THREE = require('../node_modules/three/three'); +var Eyes = require('./eyes'); +var Util = require('./util'); + +var DEFAULT_FOV = 40; +var NO_DISTORTION = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; +/** + * Responsible for distortion correction by pre-distorting the vertex geometry + * so that a second shader pass is not needed. This is intended to greatly + * optimize the rendering process. + */ +function VertexDistorter(deviceInfo) { + this.texture = null; + this.isEnabled = false; + + this.deviceInfo = deviceInfo; +} + +VertexDistorter.prototype.setEnabled = function(isEnabled) { + this.isEnabled = isEnabled; +} + +/** + * Sets the texture that is used to render this photosphere. + */ +VertexDistorter.prototype.setMap = function(texture) { + this.texture = texture; +}; + +VertexDistorter.prototype.getVertexShader_ = function() { + return [ + '#ifdef GL_ES', + 'precision highp float;', + '#endif', + + 'varying vec2 vUV;', + + this.getDistortionInclude_(), + + 'void main() {', + // Pass through texture coordinates to the fragment shader. + 'vUV = uv;', + + // Here, we want to ensure that we are using an undistorted projection + // matrix. By setting isUndistorted: true in the WebVRManager, we + // guarantee this. + 'vec4 pos = projectionMatrix * modelViewMatrix * vec4(position, 1.0);', + + 'gl_Position = Distort(pos);', + '}' + ].join('\n'); +}; + +VertexDistorter.prototype.getNoopVertexShader_ = function() { + return [ + '#ifdef GL_ES', + 'precision highp float;', + '#endif', + + 'varying vec2 vUV;', + + 'void main() {', + 'gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);', + 'vUV = uv;', + '}' + ].join('\n'); +}; + +VertexDistorter.prototype.getFragmentShader_ = function() { + return [ + '#ifdef GL_ES', + 'precision highp float;', + '#endif', + + 'varying vec2 vUV;', + 'uniform sampler2D texture;', + + 'void main() {', + 'gl_FragColor = texture2D(texture, vUV);', + '}' + ].join('\n'); +}; + +VertexDistorter.prototype.getNoopDistortionInclude_ = function() { + return [ + 'vec4 Distort(vec4 point) {', + 'return point;', + '}' + ].join('\n'); +}; + +VertexDistorter.prototype.getDistortionInclude_ = function() { + return [ + 'uniform float uDistortionCoefficients[12];', + 'uniform float uDistortionMaxFovSquared;', + 'uniform vec2 uDistortionFovOffset;', + 'uniform vec2 uDistortionFovScale;', + + // Returns a scalar to distort a point; computed in reverse via the polynomial approximation: + // r' = 1 + Σ_i (uDistortionCoefficients[i] rSquared^(i+1)) i=[0..11] + // where rSquared is the squared radius of an undistorted point in tan-angle space. + // See {@link Distortion} for more information. + 'float DistortionFactor(float rSquared) {', + 'float ret = 0.0;', + 'rSquared = min(uDistortionMaxFovSquared, rSquared);', + 'ret = rSquared * (ret + uDistortionCoefficients[11]);', + 'ret = rSquared * (ret + uDistortionCoefficients[10]);', + 'ret = rSquared * (ret + uDistortionCoefficients[9]);', + 'ret = rSquared * (ret + uDistortionCoefficients[8]);', + 'ret = rSquared * (ret + uDistortionCoefficients[7]);', + 'ret = rSquared * (ret + uDistortionCoefficients[6]);', + 'ret = rSquared * (ret + uDistortionCoefficients[5]);', + 'ret = rSquared * (ret + uDistortionCoefficients[4]);', + 'ret = rSquared * (ret + uDistortionCoefficients[3]);', + 'ret = rSquared * (ret + uDistortionCoefficients[2]);', + 'ret = rSquared * (ret + uDistortionCoefficients[1]);', + 'ret = rSquared * (ret + uDistortionCoefficients[0]);', + 'return ret + 1.0;', + '}', + + // Given a point in clip space, distort the point according to the coefficients stored in + // uDistortionCoefficients and the field of view (FOV) specified in uDistortionFovOffset and + // uDistortionFovScale. + // Returns the distorted point in clip space, with its Z untouched. + 'vec4 Distort(vec4 point) {', + // Put point into normalized device coordinates (NDC), [(-1, -1, -1) to (1, 1, 1)]. + 'vec3 pointNdc = point.xyz / point.w;', + // Throw away the Z coordinate and map the point to the unit square, [(0, 0) to (1, 1)]. + 'vec2 pointUnitSquare = (pointNdc.xy + vec2(1.0)) / 2.0;', + // Map the point into FOV tan-angle space. + 'vec2 pointTanAngle = pointUnitSquare * uDistortionFovScale - uDistortionFovOffset;', + 'float radiusSquared = dot(pointTanAngle, pointTanAngle);', + 'float distortionFactor = DistortionFactor(radiusSquared);', + //'float distortionFactor = 2.0;', + 'vec2 distortedPointTanAngle = pointTanAngle * distortionFactor;', + // Reverse the mappings above to bring the distorted point back into NDC space. + 'vec2 distortedPointUnitSquare = (distortedPointTanAngle + uDistortionFovOffset)', + '/ uDistortionFovScale;', + 'vec3 distortedPointNdc = vec3(distortedPointUnitSquare * 2.0 - vec2(1.0), pointNdc.z);', + // Convert the point into clip space before returning in case any operations are done after. + 'return vec4(distortedPointNdc, 1.0) * point.w;', + '}', + ].join('\n'); +}; + +VertexDistorter.prototype.getDistortionMaxFovSquared_ = function() { + var fov = this.getFov_(); + var maxFov = Util.hypot( + Math.tan(THREE.Math.degToRad(Math.max(fov.leftDegrees, fov.rightDegrees))), + Math.tan(THREE.Math.degToRad(Math.max(fov.downDegrees, fov.upDegrees)))); + return maxFov * maxFov; +}; + +VertexDistorter.prototype.getDistortionCoefficients_ = function() { + var viewer = this.deviceInfo.viewer; + return this.isEnabled ? viewer.inverseCoefficients : NO_DISTORTION; +}; + +VertexDistorter.prototype.getDistortionFovOffset_ = function(eye) { + var fov = this.getFov_(eye); + /* + var sideDegrees = Math.min(fov.leftDegrees, fov.rightDegrees); + var left = Math.tan(THREE.Math.degToRad(sideDegrees)); + */ + var left = Math.tan(THREE.Math.degToRad(fov.leftDegrees)); + var down = Math.tan(THREE.Math.degToRad(fov.downDegrees)); + return new THREE.Vector2(left, down); +}; + +VertexDistorter.prototype.getDistortionFovScale_ = function() { + var fov = this.getFov_(); + var left = Math.tan(THREE.Math.degToRad(fov.leftDegrees)); + var right = Math.tan(THREE.Math.degToRad(fov.rightDegrees)); + var up = Math.tan(THREE.Math.degToRad(fov.upDegrees)); + var down = Math.tan(THREE.Math.degToRad(fov.downDegrees)); + return new THREE.Vector2(left + right, up + down); +}; + + +VertexDistorter.prototype.getUniforms_ = function(eye) { + return { + texture: { + type: 't', + value: this.texture + }, + uDistortionCoefficients: { + type: 'fv1', + value: this.getDistortionCoefficients_() + }, + uDistortionMaxFovSquared: { + type: 'f', + value: this.getDistortionMaxFovSquared_() + }, + uDistortionFovOffset: { + type: 'v2', + value: this.getDistortionFovOffset_(eye) + }, + uDistortionFovScale: { + type: 'v2', + value: this.getDistortionFovScale_() + } + }; +}; + +VertexDistorter.prototype.getShaderMaterial = function(eye) { + var uniforms = this.getUniforms_(eye); + return new THREE.ShaderMaterial({ + uniforms: uniforms, + vertexShader: this.getVertexShader_(), + fragmentShader: this.getFragmentShader_() + }); +}; + +VertexDistorter.prototype.getFov_ = function(opt_eye) { + var eye = opt_eye || Eyes.LEFT; + + if (eye == Eyes.LEFT) { + return this.deviceInfo.getFieldOfViewLeftEye(true); + } + if (eye == Eyes.RIGHT) { + return this.deviceInfo.getFieldOfViewRightEye(true); + } + return null; +}; + +module.exports = VertexDistorter; diff --git a/src/with-analytics.js b/src/with-analytics.js new file mode 100644 index 00000000..9f777d64 --- /dev/null +++ b/src/with-analytics.js @@ -0,0 +1,5 @@ +// Load EmbedVR. +require('./main'); + +// Load Analytics for EmbedVR. +require('./analytics'); diff --git a/style.css b/style.css new file mode 100644 index 00000000..c7e78f14 --- /dev/null +++ b/style.css @@ -0,0 +1,86 @@ +html, body { + background-color: #000; + color: #eee; + margin: 0px; + padding: 0px; + position: fixed; + overflow: hidden; + top: 0; + bottom: 0; + left: 0; + right: 0; +} +.dialog { + display: none; + align-items: center; + justify-content: center; + font-family: sans-serif; + font-size: 170%; + position: absolute; + width: 100%; + height: 100%; + background: rgba(255, 255, 255, 0.2); + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} +.dialog.visible { + display: flex; +} +.dialog .wrap { + padding: 30px 60px; + margin: 20px auto; + width: 60%; + background: rgba(0, 0, 0, 0.8); + border-radius: 5px; +} +.dialog h1 { + margin: 0; +} + +a, a:visited { + color: skyblue; +} + +#title { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + position: absolute; + top: 10%; + width: 100%; + font-size: 3em; + font-family: 'Dosis'; + opacity: 1; + -webkit-transition: all 0.3s ease-in-out; + -moz-transition: all 0.3s ease-in-out; + transition: all 0.3s ease-in-out; + margin: 0 0.5rem; +} + +#title.hidden { + opacity: 0; +} + +#watermark img { + position: fixed; + overflow: hidden; + left: 0; + bottom: 0; + opacity: 0.3; + width: 24px; + height: 24px; + padding: 12px; +} +#watermark img:hover { + opacity: 1; + -webkit-filter: drop-shadow(white 0 0 5px); +} + + +canvas { + cursor: -webkit-grab; +} +canvas:active { + cursor: -webkit-grabbing; +}