├── .eslintignore ├── .gitignore ├── LICENSE ├── README.md ├── babel.config.js ├── dist ├── favicon.ico ├── index.html └── static │ ├── css │ └── app.90f1cf61.css │ ├── img │ ├── back.1c4c0640.jpg │ ├── bottom.f3c15dcc.jpg │ ├── front.71cb831c.jpg │ ├── left.ce4064e2.jpg │ ├── right.8ff40888.jpg │ └── top.4b15e8e9.jpg │ ├── js │ ├── app.48b07d0a.js │ └── chunk-vendors.35a2dcb6.js │ └── media │ └── audio.2040dda6.mp3 ├── example.png ├── example1.png ├── package-lock.json ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── assets │ ├── bg2.jpg │ └── skybox │ │ ├── back.jpg │ │ ├── bottom.jpg │ │ ├── front.jpg │ │ ├── left.jpg │ │ ├── right.jpg │ │ └── top.jpg ├── components │ ├── Triangle.js │ ├── node.js │ ├── randomRange.js │ └── range.js ├── lib │ ├── CopyShader.js │ ├── EffectComposer.js │ ├── LuminosityHighPassShader.js │ ├── MaskPass.js │ ├── OBJLoader.js │ ├── OrbitControls.js │ ├── Pass.js │ ├── RenderPass.js │ ├── ShaderPass.js │ ├── UnrealBloomPass.js │ ├── dat.gui.module.js │ ├── stats.module.js │ └── three.module.js ├── main.js └── router │ └── index.js ├── static └── audio.mp3 └── vue.config.js /.eslintignore: -------------------------------------------------------------------------------- 1 | /src/lib 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Hocoa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AudioPlayer 2 | 3 | ### 初始加载github上的音乐文件可能较慢,推荐选择本地的音乐文件 4 | 5 | [Demo](https://hocoa.github.io/AudioPlayer/dist/index.html) 6 | 7 | 8 | ![Image text](example.png) 9 | ![Image text](example1.png) 10 | ## Project setup 11 | ``` 12 | npm install 13 | ``` 14 | 15 | ### Compiles and hot-reloads for development 16 | ``` 17 | npm run serve 18 | ``` 19 | 20 | ### Compiles and minifies for production 21 | ``` 22 | npm run build 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/cli-plugin-babel/preset"] 3 | }; 4 | -------------------------------------------------------------------------------- /dist/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hocoa/AudioPlayer/97baf14994f6423ada381249c3b2682ba7827c71/dist/favicon.ico -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | audioplayer
-------------------------------------------------------------------------------- /dist/static/css/app.90f1cf61.css: -------------------------------------------------------------------------------- 1 | body{margin:0;padding:0;overflow:hidden;height:100%}.file-select-btn{position:fixed;bottom:0;left:0;width:70px;height:26px;box-shadow:inset 0 0 10px #c500e5;line-height:26px;color:#c500e5;font-size:12px;text-align:center;cursor:pointer}.file-select-btn .file-input{display:none} -------------------------------------------------------------------------------- /dist/static/img/back.1c4c0640.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hocoa/AudioPlayer/97baf14994f6423ada381249c3b2682ba7827c71/dist/static/img/back.1c4c0640.jpg -------------------------------------------------------------------------------- /dist/static/img/bottom.f3c15dcc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hocoa/AudioPlayer/97baf14994f6423ada381249c3b2682ba7827c71/dist/static/img/bottom.f3c15dcc.jpg -------------------------------------------------------------------------------- /dist/static/img/front.71cb831c.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hocoa/AudioPlayer/97baf14994f6423ada381249c3b2682ba7827c71/dist/static/img/front.71cb831c.jpg -------------------------------------------------------------------------------- /dist/static/img/left.ce4064e2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hocoa/AudioPlayer/97baf14994f6423ada381249c3b2682ba7827c71/dist/static/img/left.ce4064e2.jpg -------------------------------------------------------------------------------- /dist/static/img/right.8ff40888.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hocoa/AudioPlayer/97baf14994f6423ada381249c3b2682ba7827c71/dist/static/img/right.8ff40888.jpg -------------------------------------------------------------------------------- /dist/static/img/top.4b15e8e9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hocoa/AudioPlayer/97baf14994f6423ada381249c3b2682ba7827c71/dist/static/img/top.4b15e8e9.jpg -------------------------------------------------------------------------------- /dist/static/js/app.48b07d0a.js: -------------------------------------------------------------------------------- 1 | (function(e){function t(t){for(var i,a,s=t[0],l=t[1],c=t[2],u=0,h=[];uc||8*(1-_.dot(n.object.quaternion))>c)&&(n.dispatchEvent(i),m.copy(n.object.position),_.copy(n.object.quaternion),p=!1,!0)}}(),this.dispose=function(){n.domElement.removeEventListener("contextmenu",se,!1),n.domElement.removeEventListener("mousedown",$,!1),n.domElement.removeEventListener("wheel",ne,!1),n.domElement.removeEventListener("touchstart",oe,!1),n.domElement.removeEventListener("touchend",ae,!1),n.domElement.removeEventListener("touchmove",re,!1),document.removeEventListener("mousemove",ee,!1),document.removeEventListener("mouseup",te,!1),n.domElement.removeEventListener("keydown",ie,!1)};var n=this,i={type:"change"},o={type:"start"},r={type:"end"},s={NONE:-1,ROTATE:0,DOLLY:1,PAN:2,TOUCH_ROTATE:3,TOUCH_PAN:4,TOUCH_DOLLY_PAN:5,TOUCH_DOLLY_ROTATE:6},l=s.NONE,c=1e-6,d=new a["F"],u=new a["F"],h=1,f=new a["J"],p=!1,m=new a["I"],_=new a["I"],g=new a["I"],b=new a["I"],v=new a["I"],y=new a["I"],w=new a["I"],x=new a["I"],E=new a["I"];function C(){return 2*Math.PI/60/60*n.autoRotateSpeed}function S(){return Math.pow(.95,n.zoomSpeed)}function T(e){u.theta-=e}function A(e){u.phi-=e}var O=function(){var e=new a["J"];return function(t,n){e.setFromMatrixColumn(n,0),e.multiplyScalar(-t),f.add(e)}}(),k=function(){var e=new a["J"];return function(t,i){!0===n.screenSpacePanning?e.setFromMatrixColumn(i,1):(e.setFromMatrixColumn(i,0),e.crossVectors(n.object.up,e)),e.multiplyScalar(t),f.add(e)}}(),R=function(){var e=new a["J"];return function(t,i){var o=n.domElement;if(n.object.isPerspectiveCamera){var r=n.object.position;e.copy(r).sub(n.target);var a=e.length();a*=Math.tan(n.object.fov/2*Math.PI/180),O(2*t*a/o.clientHeight,n.object.matrix),k(2*i*a/o.clientHeight,n.object.matrix)}else n.object.isOrthographicCamera?(O(t*(n.object.right-n.object.left)/n.object.zoom/o.clientWidth,n.object.matrix),k(i*(n.object.top-n.object.bottom)/n.object.zoom/o.clientHeight,n.object.matrix)):(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."),n.enablePan=!1)}}();function P(e){n.object.isPerspectiveCamera?h/=e:n.object.isOrthographicCamera?(n.object.zoom=Math.max(n.minZoom,Math.min(n.maxZoom,n.object.zoom*e)),n.object.updateProjectionMatrix(),p=!0):(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."),n.enableZoom=!1)}function M(e){n.object.isPerspectiveCamera?h*=e:n.object.isOrthographicCamera?(n.object.zoom=Math.max(n.minZoom,Math.min(n.maxZoom,n.object.zoom/e)),n.object.updateProjectionMatrix(),p=!0):(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."),n.enableZoom=!1)}function L(e){m.set(e.clientX,e.clientY)}function j(e){w.set(e.clientX,e.clientY)}function B(e){b.set(e.clientX,e.clientY)}function D(e){_.set(e.clientX,e.clientY),g.subVectors(_,m).multiplyScalar(n.rotateSpeed);var t=n.domElement;T(2*Math.PI*g.x/t.clientHeight),A(2*Math.PI*g.y/t.clientHeight),m.copy(_),n.update()}function N(e){x.set(e.clientX,e.clientY),E.subVectors(x,w),E.y>0?P(S()):E.y<0&&M(S()),w.copy(x),n.update()}function H(e){v.set(e.clientX,e.clientY),y.subVectors(v,b).multiplyScalar(n.panSpeed),R(y.x,y.y),b.copy(v),n.update()}function F(){}function I(e){e.deltaY<0?M(S()):e.deltaY>0&&P(S()),n.update()}function z(e){var t=!1;switch(e.keyCode){case n.keys.UP:R(0,n.keyPanSpeed),t=!0;break;case n.keys.BOTTOM:R(0,-n.keyPanSpeed),t=!0;break;case n.keys.LEFT:R(n.keyPanSpeed,0),t=!0;break;case n.keys.RIGHT:R(-n.keyPanSpeed,0),t=!0;break}t&&(e.preventDefault(),n.update())}function U(e){if(1==e.touches.length)m.set(e.touches[0].pageX,e.touches[0].pageY);else{var t=.5*(e.touches[0].pageX+e.touches[1].pageX),n=.5*(e.touches[0].pageY+e.touches[1].pageY);m.set(t,n)}}function V(e){if(1==e.touches.length)b.set(e.touches[0].pageX,e.touches[0].pageY);else{var t=.5*(e.touches[0].pageX+e.touches[1].pageX),n=.5*(e.touches[0].pageY+e.touches[1].pageY);b.set(t,n)}}function G(e){var t=e.touches[0].pageX-e.touches[1].pageX,n=e.touches[0].pageY-e.touches[1].pageY,i=Math.sqrt(t*t+n*n);w.set(0,i)}function Y(e){n.enableZoom&&G(e),n.enablePan&&V(e)}function X(e){n.enableZoom&&G(e),n.enableRotate&&U(e)}function Q(e){if(1==e.touches.length)_.set(e.touches[0].pageX,e.touches[0].pageY);else{var t=.5*(e.touches[0].pageX+e.touches[1].pageX),i=.5*(e.touches[0].pageY+e.touches[1].pageY);_.set(t,i)}g.subVectors(_,m).multiplyScalar(n.rotateSpeed);var o=n.domElement;T(2*Math.PI*g.x/o.clientHeight),A(2*Math.PI*g.y/o.clientHeight),m.copy(_)}function J(e){if(1==e.touches.length)v.set(e.touches[0].pageX,e.touches[0].pageY);else{var t=.5*(e.touches[0].pageX+e.touches[1].pageX),i=.5*(e.touches[0].pageY+e.touches[1].pageY);v.set(t,i)}y.subVectors(v,b).multiplyScalar(n.panSpeed),R(y.x,y.y),b.copy(v)}function W(e){var t=e.touches[0].pageX-e.touches[1].pageX,i=e.touches[0].pageY-e.touches[1].pageY,o=Math.sqrt(t*t+i*i);x.set(0,o),E.set(0,Math.pow(x.y/w.y,n.zoomSpeed)),P(E.y),w.copy(x)}function K(e){n.enableZoom&&W(e),n.enablePan&&J(e)}function Z(e){n.enableZoom&&W(e),n.enableRotate&&Q(e)}function q(){}function $(e){if(!1!==n.enabled){var t;switch(e.preventDefault(),n.domElement.focus?n.domElement.focus():window.focus(),e.button){case 0:t=n.mouseButtons.LEFT;break;case 1:t=n.mouseButtons.MIDDLE;break;case 2:t=n.mouseButtons.RIGHT;break;default:t=-1}switch(t){case a["t"].DOLLY:if(!1===n.enableZoom)return;j(e),l=s.DOLLY;break;case a["t"].ROTATE:if(e.ctrlKey||e.metaKey||e.shiftKey){if(!1===n.enablePan)return;B(e),l=s.PAN}else{if(!1===n.enableRotate)return;L(e),l=s.ROTATE}break;case a["t"].PAN:if(e.ctrlKey||e.metaKey||e.shiftKey){if(!1===n.enableRotate)return;L(e),l=s.ROTATE}else{if(!1===n.enablePan)return;B(e),l=s.PAN}break;default:l=s.NONE}l!==s.NONE&&(document.addEventListener("mousemove",ee,!1),document.addEventListener("mouseup",te,!1),n.dispatchEvent(o))}}function ee(e){if(!1!==n.enabled)switch(e.preventDefault(),l){case s.ROTATE:if(!1===n.enableRotate)return;D(e);break;case s.DOLLY:if(!1===n.enableZoom)return;N(e);break;case s.PAN:if(!1===n.enablePan)return;H(e);break}}function te(e){!1!==n.enabled&&(F(e),document.removeEventListener("mousemove",ee,!1),document.removeEventListener("mouseup",te,!1),n.dispatchEvent(r),l=s.NONE)}function ne(e){!1===n.enabled||!1===n.enableZoom||l!==s.NONE&&l!==s.ROTATE||(e.preventDefault(),e.stopPropagation(),n.dispatchEvent(o),I(e),n.dispatchEvent(r))}function ie(e){!1!==n.enabled&&!1!==n.enableKeys&&!1!==n.enablePan&&z(e)}function oe(e){if(!1!==n.enabled){switch(e.preventDefault(),e.touches.length){case 1:switch(n.touches.ONE){case a["G"].ROTATE:if(!1===n.enableRotate)return;U(e),l=s.TOUCH_ROTATE;break;case a["G"].PAN:if(!1===n.enablePan)return;V(e),l=s.TOUCH_PAN;break;default:l=s.NONE}break;case 2:switch(n.touches.TWO){case a["G"].DOLLY_PAN:if(!1===n.enableZoom&&!1===n.enablePan)return;Y(e),l=s.TOUCH_DOLLY_PAN;break;case a["G"].DOLLY_ROTATE:if(!1===n.enableZoom&&!1===n.enableRotate)return;X(e),l=s.TOUCH_DOLLY_ROTATE;break;default:l=s.NONE}break;default:l=s.NONE}l!==s.NONE&&n.dispatchEvent(o)}}function re(e){if(!1!==n.enabled)switch(e.preventDefault(),e.stopPropagation(),l){case s.TOUCH_ROTATE:if(!1===n.enableRotate)return;Q(e),n.update();break;case s.TOUCH_PAN:if(!1===n.enablePan)return;J(e),n.update();break;case s.TOUCH_DOLLY_PAN:if(!1===n.enableZoom&&!1===n.enablePan)return;K(e),n.update();break;case s.TOUCH_DOLLY_ROTATE:if(!1===n.enableZoom&&!1===n.enableRotate)return;Z(e),n.update();break;default:l=s.NONE}}function ae(e){!1!==n.enabled&&(q(e),n.dispatchEvent(r),l=s.NONE)}function se(e){!1!==n.enabled&&e.preventDefault()}n.domElement.addEventListener("contextmenu",se,!1),n.domElement.addEventListener("mousedown",$,!1),n.domElement.addEventListener("wheel",ne,!1),n.domElement.addEventListener("touchstart",oe,!1),n.domElement.addEventListener("touchend",ae,!1),n.domElement.addEventListener("touchmove",re,!1),n.domElement.addEventListener("keydown",ie,!1),-1===n.domElement.tabIndex&&(n.domElement.tabIndex=0),this.update()});s.prototype=Object.create(a["m"].prototype),s.prototype.constructor=s;var l=function(e,t){s.call(this,e,t),this.mouseButtons.LEFT=a["t"].PAN,this.mouseButtons.RIGHT=a["t"].ROTATE,this.touches.ONE=a["G"].PAN,this.touches.TWO=a["G"].DOLLY_ROTATE};l.prototype=Object.create(a["m"].prototype),l.prototype.constructor=l;n("a4d3"),n("e01a"),n("d28b"),n("c975"),n("a15b"),n("fb6a"),n("a434"),n("b0c0"),n("b680"),n("e439"),n("3410"),n("b64b"),n("4d63"),n("ac1f"),n("25f0"),n("3ca3"),n("466d"),n("5319"),n("1276");var c=n("53ca");function d(e){if(e&&"undefined"!==typeof window){var t=document.createElement("style");return t.setAttribute("type","text/css"),t.innerHTML=e,document.head.appendChild(t),e}}function u(e,t){var n=e.__state.conversionName.toString(),i=Math.round(e.r),o=Math.round(e.g),r=Math.round(e.b),a=e.a,s=Math.round(e.h),l=e.s.toFixed(1),c=e.v.toFixed(1);if(t||"THREE_CHAR_HEX"===n||"SIX_CHAR_HEX"===n){var d=e.hex.toString(16);while(d.length<6)d="0"+d;return"#"+d}return"CSS_RGB"===n?"rgb("+i+","+o+","+r+")":"CSS_RGBA"===n?"rgba("+i+","+o+","+r+","+a+")":"HEX"===n?"0x"+e.hex.toString(16):"RGB_ARRAY"===n?"["+i+","+o+","+r+"]":"RGBA_ARRAY"===n?"["+i+","+o+","+r+","+a+"]":"RGB_OBJ"===n?"{r:"+i+",g:"+o+",b:"+r+"}":"RGBA_OBJ"===n?"{r:"+i+",g:"+o+",b:"+r+",a:"+a+"}":"HSV_OBJ"===n?"{h:"+s+",s:"+l+",v:"+c+"}":"HSVA_OBJ"===n?"{h:"+s+",s:"+l+",v:"+c+",a:"+a+"}":"unknown format"}var h=Array.prototype.forEach,f=Array.prototype.slice,p={BREAK:{},extend:function(e){return this.each(f.call(arguments,1),(function(t){var n=this.isObject(t)?Object.keys(t):[];n.forEach(function(n){this.isUndefined(t[n])||(e[n]=t[n])}.bind(this))}),this),e},defaults:function(e){return this.each(f.call(arguments,1),(function(t){var n=this.isObject(t)?Object.keys(t):[];n.forEach(function(n){this.isUndefined(e[n])&&(e[n]=t[n])}.bind(this))}),this),e},compose:function(){var e=f.call(arguments);return function(){for(var t=f.call(arguments),n=e.length-1;n>=0;n--)t=[e[n].apply(this,t)];return t[0]}},each:function(e,t,n){if(e)if(h&&e.forEach&&e.forEach===h)e.forEach(t,n);else if(e.length===e.length+0){var i=void 0,o=void 0;for(i=0,o=e.length;i1?p.toArray(arguments):arguments[0];return p.each(m,(function(t){if(t.litmus(e))return p.each(t.conversions,(function(t,n){if(_=t.read(e),!1===g&&!1!==_)return g=_,_.conversionName=n,_.conversion=t,p.BREAK})),p.BREAK})),g},v=void 0,y={hsv_to_rgb:function(e,t,n){var i=Math.floor(e/60)%6,o=e/60-Math.floor(e/60),r=n*(1-t),a=n*(1-o*t),s=n*(1-(1-o)*t),l=[[n,s,r],[a,n,r],[r,n,s],[r,a,n],[s,r,n],[n,r,a]][i];return{r:255*l[0],g:255*l[1],b:255*l[2]}},rgb_to_hsv:function(e,t,n){var i=Math.min(e,t,n),o=Math.max(e,t,n),r=o-i,a=void 0,s=void 0;return 0===o?{h:NaN,s:0,v:0}:(s=r/o,a=e===o?(t-n)/r:t===o?2+(n-e)/r:4+(e-t)/r,a/=6,a<0&&(a+=1),{h:360*a,s:s,v:o/255})},rgb_to_hex:function(e,t,n){var i=this.hex_with_component(0,2,e);return i=this.hex_with_component(i,1,t),i=this.hex_with_component(i,0,n),i},component_from_hex:function(e,t){return e>>8*t&255},hex_with_component:function(e,t,n){return n<<(v=8*t)|e&~(255<-1?t.length-t.indexOf(".")-1:0}var I=function(e){function t(e,n,i){x(this,t);var o=T(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n)),r=i||{};return o.__min=r.min,o.__max=r.max,o.__step=r.step,p.isUndefined(o.__step)?0===o.initialValue?o.__impliedStep=1:o.__impliedStep=Math.pow(10,Math.floor(Math.log(Math.abs(o.initialValue))/Math.LN10))/10:o.__impliedStep=o.__step,o.__precision=F(o.__impliedStep),o}return S(t,e),E(t,[{key:"setValue",value:function(e){var n=e;return void 0!==this.__min&&nthis.__max&&(n=this.__max),void 0!==this.__step&&n%this.__step!==0&&(n=Math.round(n/this.__step)*this.__step),C(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"setValue",this).call(this,n)}},{key:"min",value:function(e){return this.__min=e,this}},{key:"max",value:function(e){return this.__max=e,this}},{key:"step",value:function(e){return this.__step=e,this.__impliedStep=e,this.__precision=F(e),this}}]),t}(R);function z(e,t){var n=Math.pow(10,t);return Math.round(e*n)/n}var U=function(e){function t(e,n,i){x(this,t);var o=T(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n,i));o.__truncationSuspended=!1;var r=o,a=void 0;function s(){var e=parseFloat(r.__input.value);p.isNaN(e)||r.setValue(e)}function l(){r.__onFinishChange&&r.__onFinishChange.call(r,r.getValue())}function c(){l()}function d(e){var t=a-e.clientY;r.setValue(r.getValue()+t*r.__impliedStep),a=e.clientY}function u(){B.unbind(window,"mousemove",d),B.unbind(window,"mouseup",u),l()}function h(e){B.bind(window,"mousemove",d),B.bind(window,"mouseup",u),a=e.clientY}return o.__input=document.createElement("input"),o.__input.setAttribute("type","text"),B.bind(o.__input,"change",s),B.bind(o.__input,"blur",c),B.bind(o.__input,"mousedown",h),B.bind(o.__input,"keydown",(function(e){13===e.keyCode&&(r.__truncationSuspended=!0,this.blur(),r.__truncationSuspended=!1,l())})),o.updateDisplay(),o.domElement.appendChild(o.__input),o}return S(t,e),E(t,[{key:"updateDisplay",value:function(){return this.__input.value=this.__truncationSuspended?this.getValue():z(this.getValue(),this.__precision),C(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"updateDisplay",this).call(this)}}]),t}(I);function V(e,t,n,i,o){return i+(e-t)/(n-t)*(o-i)}var G=function(e){function t(e,n,i,o,r){x(this,t);var a=T(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n,{min:i,max:o,step:r})),s=a;function l(e){document.activeElement.blur(),B.bind(window,"mousemove",c),B.bind(window,"mouseup",d),c(e)}function c(e){e.preventDefault();var t=s.__background.getBoundingClientRect();return s.setValue(V(e.clientX,t.left,t.right,s.__min,s.__max)),!1}function d(){B.unbind(window,"mousemove",c),B.unbind(window,"mouseup",d),s.__onFinishChange&&s.__onFinishChange.call(s,s.getValue())}function u(e){1===e.touches.length&&(B.bind(window,"touchmove",h),B.bind(window,"touchend",f),h(e))}function h(e){var t=e.touches[0].clientX,n=s.__background.getBoundingClientRect();s.setValue(V(t,n.left,n.right,s.__min,s.__max))}function f(){B.unbind(window,"touchmove",h),B.unbind(window,"touchend",f),s.__onFinishChange&&s.__onFinishChange.call(s,s.getValue())}return a.__background=document.createElement("div"),a.__foreground=document.createElement("div"),B.bind(a.__background,"mousedown",l),B.bind(a.__background,"touchstart",u),B.addClass(a.__background,"slider"),B.addClass(a.__foreground,"slider-fg"),a.updateDisplay(),a.__background.appendChild(a.__foreground),a.domElement.appendChild(a.__background),a}return S(t,e),E(t,[{key:"updateDisplay",value:function(){var e=(this.getValue()-this.__min)/(this.__max-this.__min);return this.__foreground.style.width=100*e+"%",C(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"updateDisplay",this).call(this)}}]),t}(I),Y=function(e){function t(e,n,i){x(this,t);var o=T(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n)),r=o;return o.__button=document.createElement("div"),o.__button.innerHTML=void 0===i?"Fire":i,B.bind(o.__button,"click",(function(e){return e.preventDefault(),r.fire(),!1})),B.addClass(o.__button,"button"),o.domElement.appendChild(o.__button),o}return S(t,e),E(t,[{key:"fire",value:function(){this.__onChange&&this.__onChange.call(this),this.getValue().call(this.object),this.__onFinishChange&&this.__onFinishChange.call(this,this.getValue())}}]),t}(R),X=function(e){function t(e,n){x(this,t);var i=T(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n));i.__color=new A(i.getValue()),i.__temp=new A(0);var o=i;i.domElement=document.createElement("div"),B.makeSelectable(i.domElement,!1),i.__selector=document.createElement("div"),i.__selector.className="selector",i.__saturation_field=document.createElement("div"),i.__saturation_field.className="saturation-field",i.__field_knob=document.createElement("div"),i.__field_knob.className="field-knob",i.__field_knob_border="2px solid ",i.__hue_knob=document.createElement("div"),i.__hue_knob.className="hue-knob",i.__hue_field=document.createElement("div"),i.__hue_field.className="hue-field",i.__input=document.createElement("input"),i.__input.type="text",i.__input_textShadow="0 1px 1px ",B.bind(i.__input,"keydown",(function(e){13===e.keyCode&&d.call(this)})),B.bind(i.__input,"blur",d),B.bind(i.__selector,"mousedown",(function(){B.addClass(this,"drag").bind(window,"mouseup",(function(){B.removeClass(o.__selector,"drag")}))})),B.bind(i.__selector,"touchstart",(function(){B.addClass(this,"drag").bind(window,"touchend",(function(){B.removeClass(o.__selector,"drag")}))}));var r=document.createElement("div");function a(e){h(e),B.bind(window,"mousemove",h),B.bind(window,"touchmove",h),B.bind(window,"mouseup",l),B.bind(window,"touchend",l)}function s(e){f(e),B.bind(window,"mousemove",f),B.bind(window,"touchmove",f),B.bind(window,"mouseup",c),B.bind(window,"touchend",c)}function l(){B.unbind(window,"mousemove",h),B.unbind(window,"touchmove",h),B.unbind(window,"mouseup",l),B.unbind(window,"touchend",l),u()}function c(){B.unbind(window,"mousemove",f),B.unbind(window,"touchmove",f),B.unbind(window,"mouseup",c),B.unbind(window,"touchend",c),u()}function d(){var e=b(this.value);!1!==e?(o.__color.__state=e,o.setValue(o.__color.toOriginal())):this.value=o.__color.toString()}function u(){o.__onFinishChange&&o.__onFinishChange.call(o,o.__color.toOriginal())}function h(e){-1===e.type.indexOf("touch")&&e.preventDefault();var t=o.__saturation_field.getBoundingClientRect(),n=e.touches&&e.touches[0]||e,i=n.clientX,r=n.clientY,a=(i-t.left)/(t.right-t.left),s=1-(r-t.top)/(t.bottom-t.top);return s>1?s=1:s<0&&(s=0),a>1?a=1:a<0&&(a=0),o.__color.v=s,o.__color.s=a,o.setValue(o.__color.toOriginal()),!1}function f(e){-1===e.type.indexOf("touch")&&e.preventDefault();var t=o.__hue_field.getBoundingClientRect(),n=e.touches&&e.touches[0]||e,i=n.clientY,r=1-(i-t.top)/(t.bottom-t.top);return r>1?r=1:r<0&&(r=0),o.__color.h=360*r,o.setValue(o.__color.toOriginal()),!1}return p.extend(i.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"}),p.extend(i.__field_knob.style,{position:"absolute",width:"12px",height:"12px",border:i.__field_knob_border+(i.__color.v<.5?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1}),p.extend(i.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1}),p.extend(i.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"}),p.extend(r.style,{width:"100%",height:"100%",background:"none"}),J(r,"top","rgba(0,0,0,0)","#000"),p.extend(i.__hue_field.style,{width:"15px",height:"100px",border:"1px solid #555",cursor:"ns-resize",position:"absolute",top:"3px",right:"3px"}),W(i.__hue_field),p.extend(i.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:i.__input_textShadow+"rgba(0,0,0,0.7)"}),B.bind(i.__saturation_field,"mousedown",a),B.bind(i.__saturation_field,"touchstart",a),B.bind(i.__field_knob,"mousedown",a),B.bind(i.__field_knob,"touchstart",a),B.bind(i.__hue_field,"mousedown",s),B.bind(i.__hue_field,"touchstart",s),i.__saturation_field.appendChild(r),i.__selector.appendChild(i.__field_knob),i.__selector.appendChild(i.__saturation_field),i.__selector.appendChild(i.__hue_field),i.__hue_field.appendChild(i.__hue_knob),i.domElement.appendChild(i.__input),i.domElement.appendChild(i.__selector),i.updateDisplay(),i}return S(t,e),E(t,[{key:"updateDisplay",value:function(){var e=b(this.getValue());if(!1!==e){var t=!1;p.each(A.COMPONENTS,(function(n){if(!p.isUndefined(e[n])&&!p.isUndefined(this.__color.__state[n])&&e[n]!==this.__color.__state[n])return t=!0,{}}),this),t&&p.extend(this.__color.__state,e)}p.extend(this.__temp.__state,this.__color.__state),this.__temp.a=1;var n=this.__color.v<.5||this.__color.s>.5?255:0,i=255-n;p.extend(this.__field_knob.style,{marginLeft:100*this.__color.s-7+"px",marginTop:100*(1-this.__color.v)-7+"px",backgroundColor:this.__temp.toHexString(),border:this.__field_knob_border+"rgb("+n+","+n+","+n+")"}),this.__hue_knob.style.marginTop=100*(1-this.__color.h/360)+"px",this.__temp.s=1,this.__temp.v=1,J(this.__saturation_field,"left","#fff",this.__temp.toHexString()),this.__input.value=this.__color.toString(),p.extend(this.__input.style,{backgroundColor:this.__color.toHexString(),color:"rgb("+n+","+n+","+n+")",textShadow:this.__input_textShadow+"rgba("+i+","+i+","+i+",.7)"})}}]),t}(R),Q=["-moz-","-o-","-webkit-","-ms-",""];function J(e,t,n,i){e.style.background="",p.each(Q,(function(o){e.style.cssText+="background: "+o+"linear-gradient("+t+", "+n+" 0%, "+i+" 100%); "}))}function W(e){e.style.background="",e.style.cssText+="background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);",e.style.cssText+="background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);",e.style.cssText+="background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);",e.style.cssText+="background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);",e.style.cssText+="background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}var K={load:function(e,t){var n=t||document,i=n.createElement("link");i.type="text/css",i.rel="stylesheet",i.href=e,n.getElementsByTagName("head")[0].appendChild(i)},inject:function(e,t){var n=t||document,i=document.createElement("style");i.type="text/css",i.innerHTML=e;var o=n.getElementsByTagName("head")[0];try{o.appendChild(i)}catch(r){}}},Z='
\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
',q=function(e,t){var n=e[t];return p.isArray(arguments[2])||p.isObject(arguments[2])?new N(e,t,arguments[2]):p.isNumber(n)?p.isNumber(arguments[2])&&p.isNumber(arguments[3])?p.isNumber(arguments[4])?new G(e,t,arguments[2],arguments[3],arguments[4]):new G(e,t,arguments[2],arguments[3]):p.isNumber(arguments[4])?new U(e,t,{min:arguments[2],max:arguments[3],step:arguments[4]}):new U(e,t,{min:arguments[2],max:arguments[3]}):p.isString(n)?new H(e,t):p.isFunction(n)?new Y(e,t,""):p.isBoolean(n)?new D(e,t):null};function $(e){setTimeout(e,1e3/60)}var ee=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||$,te=function(){function e(){x(this,e),this.backgroundElement=document.createElement("div"),p.extend(this.backgroundElement.style,{backgroundColor:"rgba(0,0,0,0.8)",top:0,left:0,display:"none",zIndex:"1000",opacity:0,WebkitTransition:"opacity 0.2s linear",transition:"opacity 0.2s linear"}),B.makeFullscreen(this.backgroundElement),this.backgroundElement.style.position="fixed",this.domElement=document.createElement("div"),p.extend(this.domElement.style,{position:"fixed",display:"none",zIndex:"1001",opacity:0,WebkitTransition:"-webkit-transform 0.2s ease-out, opacity 0.2s linear",transition:"transform 0.2s ease-out, opacity 0.2s linear"}),document.body.appendChild(this.backgroundElement),document.body.appendChild(this.domElement);var t=this;B.bind(this.backgroundElement,"click",(function(){t.hide()}))}return E(e,[{key:"show",value:function(){var e=this;this.backgroundElement.style.display="block",this.domElement.style.display="block",this.domElement.style.opacity=0,this.domElement.style.webkitTransform="scale(1.1)",this.layout(),p.defer((function(){e.backgroundElement.style.opacity=1,e.domElement.style.opacity=1,e.domElement.style.webkitTransform="scale(1)"}))}},{key:"hide",value:function(){var e=this,t=function t(){e.domElement.style.display="none",e.backgroundElement.style.display="none",B.unbind(e.domElement,"webkitTransitionEnd",t),B.unbind(e.domElement,"transitionend",t),B.unbind(e.domElement,"oTransitionEnd",t)};B.bind(this.domElement,"webkitTransitionEnd",t),B.bind(this.domElement,"transitionend",t),B.bind(this.domElement,"oTransitionEnd",t),this.backgroundElement.style.opacity=0,this.domElement.style.opacity=0,this.domElement.style.webkitTransform="scale(1.1)"}},{key:"layout",value:function(){this.domElement.style.left=window.innerWidth/2-B.getWidth(this.domElement)/2+"px",this.domElement.style.top=window.innerHeight/2-B.getHeight(this.domElement)/2+"px"}}]),e}(),ne=d(".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 .1s linear;-o-transition:opacity .1s linear;-moz-transition:opacity .1s linear;transition:opacity .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 .1s linear;-o-transition:opacity .1s linear;-moz-transition:opacity .1s linear;transition:opacity .1s linear;border:0;line-height:19px;height:20px;cursor:pointer;text-align:center;background-color:#000}.dg.main .close-button.close-top{position:relative}.dg.main .close-button.close-bottom{position:absolute}.dg.main .close-button:hover{background-color:#111}.dg.a{float:right;margin-right:15px;overflow-y:visible}.dg.a.has-save>ul.close-top{margin-top:0}.dg.a.has-save>ul.close-bottom{margin-top:27px}.dg.a.has-save>ul.closed{margin-top:0}.dg.a .save-row{top:0;z-index:1002}.dg.a .save-row.close-top{position:relative}.dg.a .save-row.close-bottom{position:fixed}.dg li{-webkit-transition:height .1s ease-out;-o-transition:height .1s ease-out;-moz-transition:height .1s ease-out;transition:height .1s ease-out;-webkit-transition:overflow .1s linear;-o-transition:overflow .1s linear;-moz-transition:overflow .1s linear;transition:overflow .1s linear}.dg li:not(.folder){cursor:auto;height:27px;line-height:27px;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;overflow:hidden}.dg .property-name{cursor:default;float:left;clear:left;width:40%;overflow:hidden;text-overflow:ellipsis}.dg .c{float:left;width:60%;position:relative}.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:7px}.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 .cr.color{overflow:visible}.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.color{border-left:3px solid}.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;max-width:100%}.dg .c .slider:hover{background:#3c3c3c}.dg .c .slider:hover .slider-fg{background:#44abda}\n");K.inject(ne);var ie="dg",oe=72,re=20,ae="Default",se=function(){try{return!!window.localStorage}catch(e){return!1}}(),le=void 0,ce=!0,de=void 0,ue=!1,he=[],fe=function e(t){var n=this,i=t||{};this.domElement=document.createElement("div"),this.__ul=document.createElement("ul"),this.domElement.appendChild(this.__ul),B.addClass(this.domElement,ie),this.__folders={},this.__controllers=[],this.__rememberedObjects=[],this.__rememberedObjectIndecesToControllers=[],this.__listening=[],i=p.defaults(i,{closeOnTop:!1,autoPlace:!0,width:e.DEFAULT_WIDTH}),i=p.defaults(i,{resizable:i.autoPlace,hideable:i.autoPlace}),p.isUndefined(i.load)?i.load={preset:ae}:i.preset&&(i.load.preset=i.preset),p.isUndefined(i.parent)&&i.hideable&&he.push(this),i.resizable=p.isUndefined(i.parent)&&i.resizable,i.autoPlace&&p.isUndefined(i.scrollable)&&(i.scrollable=!0);var o=se&&"true"===localStorage.getItem(ye(this,"isLocal")),r=void 0,a=void 0;if(Object.defineProperties(this,{parent:{get:function(){return i.parent}},scrollable:{get:function(){return i.scrollable}},autoPlace:{get:function(){return i.autoPlace}},closeOnTop:{get:function(){return i.closeOnTop}},preset:{get:function(){return n.parent?n.getRoot().preset:i.load.preset},set:function(e){n.parent?n.getRoot().preset=e:i.load.preset=e,Ae(this),n.revert()}},width:{get:function(){return i.width},set:function(e){i.width=e,Se(n,e)}},name:{get:function(){return i.name},set:function(e){i.name=e,a&&(a.innerHTML=i.name)}},closed:{get:function(){return i.closed},set:function(t){i.closed=t,i.closed?B.addClass(n.__ul,e.CLASS_CLOSED):B.removeClass(n.__ul,e.CLASS_CLOSED),this.onResize(),n.__closeButton&&(n.__closeButton.innerHTML=t?e.TEXT_OPEN:e.TEXT_CLOSED)}},load:{get:function(){return i.load}},useLocalStorage:{get:function(){return o},set:function(e){se&&(o=e,e?B.bind(window,"unload",r):B.unbind(window,"unload",r),localStorage.setItem(ye(n,"isLocal"),e))}}}),p.isUndefined(i.parent)){if(this.closed=i.closed||!1,B.addClass(this.domElement,e.CLASS_MAIN),B.makeSelectable(this.domElement,!1),se&&o){n.useLocalStorage=!0;var s=localStorage.getItem(ye(this,"gui"));s&&(i.load=JSON.parse(s))}this.__closeButton=document.createElement("div"),this.__closeButton.innerHTML=e.TEXT_CLOSED,B.addClass(this.__closeButton,e.CLASS_CLOSE_BUTTON),i.closeOnTop?(B.addClass(this.__closeButton,e.CLASS_CLOSE_TOP),this.domElement.insertBefore(this.__closeButton,this.domElement.childNodes[0])):(B.addClass(this.__closeButton,e.CLASS_CLOSE_BOTTOM),this.domElement.appendChild(this.__closeButton)),B.bind(this.__closeButton,"click",(function(){n.closed=!n.closed}))}else{void 0===i.closed&&(i.closed=!0);var l=document.createTextNode(i.name);B.addClass(l,"controller-name"),a=pe(n,l);var c=function(e){return e.preventDefault(),n.closed=!n.closed,!1};B.addClass(this.__ul,e.CLASS_CLOSED),B.addClass(a,"title"),B.bind(a,"click",c),i.closed||(this.closed=!1)}function d(){var e=n.getRoot();e.width+=1,p.defer((function(){e.width-=1}))}i.autoPlace&&(p.isUndefined(i.parent)&&(ce&&(de=document.createElement("div"),B.addClass(de,ie),B.addClass(de,e.CLASS_AUTO_PLACE_CONTAINER),document.body.appendChild(de),ce=!1),de.appendChild(this.domElement),B.addClass(this.domElement,e.CLASS_AUTO_PLACE)),this.parent||Se(n,i.width)),this.__resizeHandler=function(){n.onResizeDebounced()},B.bind(window,"resize",this.__resizeHandler),B.bind(this.__ul,"webkitTransitionEnd",this.__resizeHandler),B.bind(this.__ul,"transitionend",this.__resizeHandler),B.bind(this.__ul,"oTransitionEnd",this.__resizeHandler),this.onResize(),i.resizable&&Ce(this),r=function(){se&&"true"===localStorage.getItem(ye(n,"isLocal"))&&localStorage.setItem(ye(n,"gui"),JSON.stringify(n.getSaveObject()))},this.saveToLocalStorageIfPossible=r,i.parent||d()};function pe(e,t,n){var i=document.createElement("li");return t&&i.appendChild(t),n?e.__ul.insertBefore(i,n):e.__ul.appendChild(i),e.onResize(),i}function me(e){B.unbind(window,"resize",e.__resizeHandler),e.saveToLocalStorageIfPossible&&B.unbind(window,"unload",e.saveToLocalStorageIfPossible)}function _e(e,t){var n=e.__preset_select[e.__preset_select.selectedIndex];n.innerHTML=t?n.value+"*":n.value}function ge(e,t,n){if(n.__li=t,n.__gui=e,p.extend(n,{options:function(t){if(arguments.length>1){var i=n.__li.nextElementSibling;return n.remove(),ve(e,n.object,n.property,{before:i,factoryArgs:[p.toArray(arguments)]})}if(p.isArray(t)||p.isObject(t)){var o=n.__li.nextElementSibling;return n.remove(),ve(e,n.object,n.property,{before:o,factoryArgs:[t]})}},name:function(e){return n.__li.firstElementChild.firstElementChild.innerHTML=e,n},listen:function(){return n.__gui.listen(n),n},remove:function(){return n.__gui.remove(n),n}}),n instanceof G){var i=new U(n.object,n.property,{min:n.__min,max:n.__max,step:n.__step});p.each(["updateDisplay","onChange","onFinishChange","step","min","max"],(function(e){var t=n[e],o=i[e];n[e]=i[e]=function(){var e=Array.prototype.slice.call(arguments);return o.apply(i,e),t.apply(n,e)}})),B.addClass(t,"has-slider"),n.domElement.insertBefore(i.domElement,n.domElement.firstElementChild)}else if(n instanceof U){var o=function(t){if(p.isNumber(n.__min)&&p.isNumber(n.__max)){var i=n.__li.firstElementChild.firstElementChild.innerHTML,o=n.__gui.__listening.indexOf(n)>-1;n.remove();var r=ve(e,n.object,n.property,{before:n.__li.nextElementSibling,factoryArgs:[n.__min,n.__max,n.__step]});return r.name(i),o&&r.listen(),r}return t};n.min=p.compose(o,n.min),n.max=p.compose(o,n.max)}else n instanceof D?(B.bind(t,"click",(function(){B.fakeEvent(n.__checkbox,"click")})),B.bind(n.__checkbox,"click",(function(e){e.stopPropagation()}))):n instanceof Y?(B.bind(t,"click",(function(){B.fakeEvent(n.__button,"click")})),B.bind(t,"mouseover",(function(){B.addClass(n.__button,"hover")})),B.bind(t,"mouseout",(function(){B.removeClass(n.__button,"hover")}))):n instanceof X&&(B.addClass(t,"color"),n.updateDisplay=p.compose((function(e){return t.style.borderLeftColor=n.__color.toString(),e}),n.updateDisplay),n.updateDisplay());n.setValue=p.compose((function(t){return e.getRoot().__preset_select&&n.isModified()&&_e(e.getRoot(),!0),t}),n.setValue)}function be(e,t){var n=e.getRoot(),i=n.__rememberedObjects.indexOf(t.object);if(-1!==i){var o=n.__rememberedObjectIndecesToControllers[i];if(void 0===o&&(o={},n.__rememberedObjectIndecesToControllers[i]=o),o[t.property]=t,n.load&&n.load.remembered){var r=n.load.remembered,a=void 0;if(r[e.preset])a=r[e.preset];else{if(!r[ae])return;a=r[ae]}if(a[i]&&void 0!==a[i][t.property]){var s=a[i][t.property];t.initialValue=s,t.setValue(s)}}}}function ve(e,t,n,i){if(void 0===t[n])throw new Error('Object "'+t+'" has no property "'+n+'"');var o=void 0;if(i.color)o=new X(t,n);else{var r=[t,n].concat(i.factoryArgs);o=q.apply(e,r)}i.before instanceof R&&(i.before=i.before.__li),be(e,o),B.addClass(o.domElement,"c");var a=document.createElement("span");B.addClass(a,"property-name"),a.innerHTML=o.property;var s=document.createElement("div");s.appendChild(a),s.appendChild(o.domElement);var l=pe(e,s,i.before);return B.addClass(l,fe.CLASS_CONTROLLER_ROW),o instanceof X?B.addClass(l,"color"):B.addClass(l,w(o.getValue())),ge(e,l,o),e.__controllers.push(o),o}function ye(e,t){return document.location.href+"."+t}function we(e,t,n){var i=document.createElement("option");i.innerHTML=t,i.value=t,e.__preset_select.appendChild(i),n&&(e.__preset_select.selectedIndex=e.__preset_select.length-1)}function xe(e,t){t.style.display=e.useLocalStorage?"block":"none"}function Ee(e){var t=e.__save_row=document.createElement("li");B.addClass(e.domElement,"has-save"),e.__ul.insertBefore(t,e.__ul.firstChild),B.addClass(t,"save-row");var n=document.createElement("span");n.innerHTML=" ",B.addClass(n,"button gears");var i=document.createElement("span");i.innerHTML="Save",B.addClass(i,"button"),B.addClass(i,"save");var o=document.createElement("span");o.innerHTML="New",B.addClass(o,"button"),B.addClass(o,"save-as");var r=document.createElement("span");r.innerHTML="Revert",B.addClass(r,"button"),B.addClass(r,"revert");var a=e.__preset_select=document.createElement("select");if(e.load&&e.load.remembered?p.each(e.load.remembered,(function(t,n){we(e,n,n===e.preset)})):we(e,ae,!1),B.bind(a,"change",(function(){for(var t=0;t0&&(e.preset=this.preset,e.remembered||(e.remembered={}),e.remembered[this.preset]=Te(this)),e.folders={},p.each(this.__folders,(function(t,n){e.folders[n]=t.getSaveObject()})),e},save:function(){this.load.remembered||(this.load.remembered={}),this.load.remembered[this.preset]=Te(this),_e(this,!1),this.saveToLocalStorageIfPossible()},saveAs:function(e){this.load.remembered||(this.load.remembered={},this.load.remembered[ae]=Te(this,!0)),this.load.remembered[e]=Te(this),this.preset=e,we(this,e,!0),this.saveToLocalStorageIfPossible()},revert:function(e){p.each(this.__controllers,(function(t){this.getRoot().load.remembered?be(e||this.getRoot(),t):t.setValue(t.initialValue),t.__onFinishChange&&t.__onFinishChange.call(t,t.getValue())}),this),p.each(this.__folders,(function(e){e.revert(e)})),e||_e(this.getRoot(),!1)},listen:function(e){var t=0===this.__listening.length;this.__listening.push(e),t&&Oe(this.__listening)},updateDisplay:function(){p.each(this.__controllers,(function(e){e.updateDisplay()})),p.each(this.__folders,(function(e){e.updateDisplay()}))}});var ke=fe,Re={uniforms:{tDiffuse:{value:null},opacity:{value:1}},vertexShader:["varying vec2 vUv;","void main() {","\tvUv = uv;","\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );","}"].join("\n"),fragmentShader:["uniform float opacity;","uniform sampler2D tDiffuse;","varying vec2 vUv;","void main() {","\tvec4 texel = texture2D( tDiffuse, vUv );","\tgl_FragColor = opacity * texel;","}"].join("\n")};function Pe(){this.enabled=!0,this.needsSwap=!0,this.clear=!1,this.renderToScreen=!1}Object.assign(Pe.prototype,{setSize:function(){},render:function(){console.error("THREE.Pass: .render() must be implemented in derived pass.")}}),Pe.FullScreenQuad=function(){var e=new a["x"](-1,1,1,-1,0,1),t=new a["z"](2,2),n=function(e){this._mesh=new a["u"](t,e)};return Object.defineProperty(n.prototype,"material",{get:function(){return this._mesh.material},set:function(e){this._mesh.material=e}}),Object.assign(n.prototype,{render:function(t){t.render(this._mesh,e)}}),n}();var Me=function(e,t){Pe.call(this),this.textureID=void 0!==t?t:"tDiffuse",e instanceof a["E"]?(this.uniforms=e.uniforms,this.material=e):e&&(this.uniforms=a["H"].clone(e.uniforms),this.material=new a["E"]({defines:Object.assign({},e.defines),uniforms:this.uniforms,vertexShader:e.vertexShader,fragmentShader:e.fragmentShader})),this.fsQuad=new Pe.FullScreenQuad(this.material)};Me.prototype=Object.assign(Object.create(Pe.prototype),{constructor:Me,render:function(e,t,n){this.uniforms[this.textureID]&&(this.uniforms[this.textureID].value=n.texture),this.fsQuad.material=this.material,this.renderToScreen?(e.setRenderTarget(null),this.fsQuad.render(e)):(e.setRenderTarget(t),this.clear&&e.clear(e.autoClearColor,e.autoClearDepth,e.autoClearStencil),this.fsQuad.render(e))}});var Le=function(e,t){Pe.call(this),this.scene=e,this.camera=t,this.clear=!0,this.needsSwap=!1,this.inverse=!1};Le.prototype=Object.assign(Object.create(Pe.prototype),{constructor:Le,render:function(e,t,n){var i,o,r=e.getContext(),a=e.state;a.buffers.color.setMask(!1),a.buffers.depth.setMask(!1),a.buffers.color.setLocked(!0),a.buffers.depth.setLocked(!0),this.inverse?(i=0,o=1):(i=1,o=0),a.buffers.stencil.setTest(!0),a.buffers.stencil.setOp(r.REPLACE,r.REPLACE,r.REPLACE),a.buffers.stencil.setFunc(r.ALWAYS,i,4294967295),a.buffers.stencil.setClear(o),a.buffers.stencil.setLocked(!0),e.setRenderTarget(n),this.clear&&e.clear(),e.render(this.scene,this.camera),e.setRenderTarget(t),this.clear&&e.clear(),e.render(this.scene,this.camera),a.buffers.color.setLocked(!1),a.buffers.depth.setLocked(!1),a.buffers.stencil.setLocked(!1),a.buffers.stencil.setFunc(r.EQUAL,1,4294967295),a.buffers.stencil.setOp(r.KEEP,r.KEEP,r.KEEP),a.buffers.stencil.setLocked(!0)}});var je=function(){Pe.call(this),this.needsSwap=!1};je.prototype=Object.create(Pe.prototype),Object.assign(je.prototype,{render:function(e){e.state.buffers.stencil.setLocked(!1),e.state.buffers.stencil.setTest(!1)}});var Be=function(e,t){if(this.renderer=e,void 0===t){var n={minFilter:a["s"],magFilter:a["s"],format:a["C"],stencilBuffer:!1},i=e.getSize(new a["I"]);this._pixelRatio=e.getPixelRatio(),this._width=i.width,this._height=i.height,t=new a["K"](this._width*this._pixelRatio,this._height*this._pixelRatio,n),t.texture.name="EffectComposer.rt1"}else this._pixelRatio=1,this._width=t.width,this._height=t.height;this.renderTarget1=t,this.renderTarget2=t.clone(),this.renderTarget2.texture.name="EffectComposer.rt2",this.writeBuffer=this.renderTarget1,this.readBuffer=this.renderTarget2,this.renderToScreen=!0,this.passes=[],void 0===Re&&console.error("THREE.EffectComposer relies on CopyShader"),void 0===Me&&console.error("THREE.EffectComposer relies on ShaderPass"),this.copyPass=new Me(Re),this.clock=new a["j"]};Object.assign(Be.prototype,{swapBuffers:function(){var e=this.readBuffer;this.readBuffer=this.writeBuffer,this.writeBuffer=e},addPass:function(e){this.passes.push(e),e.setSize(this._width*this._pixelRatio,this._height*this._pixelRatio)},insertPass:function(e,t){this.passes.splice(t,0,e)},isLastEnabledPass:function(e){for(var t=e+1;t\t\t\t\tvarying vec2 vUv;\n\t\t\t\tuniform sampler2D colorTexture;\n\t\t\t\tuniform vec2 texSize;\t\t\t\tuniform vec2 direction;\t\t\t\t\t\t\t\tfloat gaussianPdf(in float x, in float sigma) {\t\t\t\t\treturn 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;\t\t\t\t}\t\t\t\tvoid main() {\n\t\t\t\t\tvec2 invSize = 1.0 / texSize;\t\t\t\t\tfloat fSigma = float(SIGMA);\t\t\t\t\tfloat weightSum = gaussianPdf(0.0, fSigma);\t\t\t\t\tvec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum;\t\t\t\t\tfor( int i = 1; i < KERNEL_RADIUS; i ++ ) {\t\t\t\t\t\tfloat x = float(i);\t\t\t\t\t\tfloat w = gaussianPdf(x, fSigma);\t\t\t\t\t\tvec2 uvOffset = direction * invSize * x;\t\t\t\t\t\tvec3 sample1 = texture2D( colorTexture, vUv + uvOffset).rgb;\t\t\t\t\t\tvec3 sample2 = texture2D( colorTexture, vUv - uvOffset).rgb;\t\t\t\t\t\tdiffuseSum += (sample1 + sample2) * w;\t\t\t\t\t\tweightSum += 2.0 * w;\t\t\t\t\t}\t\t\t\t\tgl_FragColor = vec4(diffuseSum/weightSum, 1.0);\n\t\t\t\t}"})},getCompositeMaterial:function(e){return new a["E"]({defines:{NUM_MIPS:e},uniforms:{blurTexture1:{value:null},blurTexture2:{value:null},blurTexture3:{value:null},blurTexture4:{value:null},blurTexture5:{value:null},dirtTexture:{value:null},bloomStrength:{value:1},bloomFactors:{value:null},bloomTintColors:{value:null},bloomRadius:{value:0}},vertexShader:"varying vec2 vUv;\n\t\t\t\tvoid main() {\n\t\t\t\t\tvUv = uv;\n\t\t\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\t\t\t\t}",fragmentShader:"varying vec2 vUv;\t\t\t\tuniform sampler2D blurTexture1;\t\t\t\tuniform sampler2D blurTexture2;\t\t\t\tuniform sampler2D blurTexture3;\t\t\t\tuniform sampler2D blurTexture4;\t\t\t\tuniform sampler2D blurTexture5;\t\t\t\tuniform sampler2D dirtTexture;\t\t\t\tuniform float bloomStrength;\t\t\t\tuniform float bloomRadius;\t\t\t\tuniform float bloomFactors[NUM_MIPS];\t\t\t\tuniform vec3 bloomTintColors[NUM_MIPS];\t\t\t\t\t\t\t\tfloat lerpBloomFactor(const in float factor) { \t\t\t\t\tfloat mirrorFactor = 1.2 - factor;\t\t\t\t\treturn mix(factor, mirrorFactor, bloomRadius);\t\t\t\t}\t\t\t\t\t\t\t\tvoid main() {\t\t\t\t\tgl_FragColor = bloomStrength * ( lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) + \t\t\t\t\t\t\t\t\t\t\t\t\t lerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) + \t\t\t\t\t\t\t\t\t\t\t\t\t lerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) + \t\t\t\t\t\t\t\t\t\t\t\t\t lerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) + \t\t\t\t\t\t\t\t\t\t\t\t\t lerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) );\t\t\t\t}"})}}),Fe.BlurDirectionX=new a["I"](1,0),Fe.BlurDirectionY=new a["I"](0,1);var Ie=function e(){var t=0,n=document.createElement("div");function i(e){return n.appendChild(e.dom),e}function o(e){for(var i=0;i=a+1e3&&(l.update(1e3*s/(e-a),100),a=e,s=0,d)){var t=performance.memory;d.update(t.usedJSHeapSize/1048576,t.jsHeapSizeLimit/1048576)}return e},update:function(){r=this.end()},domElement:n,setMode:o}};Ie.Panel=function(e,t,n){var i=1/0,o=0,r=Math.round,a=r(window.devicePixelRatio||1),s=80*a,l=48*a,c=3*a,d=2*a,u=3*a,h=15*a,f=74*a,p=30*a,m=document.createElement("canvas");m.width=s,m.height=l,m.style.cssText="width:80px;height:48px";var _=m.getContext("2d");return _.font="bold "+9*a+"px Helvetica,Arial,sans-serif",_.textBaseline="top",_.fillStyle=n,_.fillRect(0,0,s,l),_.fillStyle=t,_.fillText(e,c,d),_.fillRect(u,h,f,p),_.fillStyle=n,_.globalAlpha=.9,_.fillRect(u,h,f,p),{dom:m,update:function(l,g){i=Math.min(i,l),o=Math.max(o,l),_.fillStyle=n,_.globalAlpha=1,_.fillRect(0,0,s,h),_.fillStyle=t,_.fillText(r(l)+" "+e+" ("+r(i)+"-"+r(o)+")",c,d),_.drawImage(m,u+a,h,f-a,p,u,h,f-a,p),_.fillRect(u+f-a,h,a,p),_.fillStyle=n,_.globalAlpha=.9,_.fillRect(u+f-a,h,a,r((1-l/g)*p))}}};var ze=Ie;function Ue(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1,i=arguments.length>3&&void 0!==arguments[3]&&arguments[3],o=-1,r=Math.max(Math.ceil((t-e)/n),0),a=Array(r);while(r--)a[i?r:++o]=e,e+=n;return a}var Ve=n("d4ec"),Ge=n("bee2"),Ye=n("ade3"),Xe=function(){function e(t,n,i){Object(Ve["a"])(this,e),Object(Ye["a"])(this,"lastStrength",0),Object(Ye["a"])(this,"theStrength",0),Object(Ye["a"])(this,"targetRange",0),Object(Ye["a"])(this,"_range",0),this.baseRange=t,this.angle=n,this.center=i}return Object(Ge["a"])(e,[{key:"positionA",value:function(){var e=this._range+this.baseRange,t=Math.cos(this.angle*Math.PI/180)*e,n=Math.sin(this.angle*Math.PI/180)*e;return new a["I"](this.center.x+t,this.center.y+n)}},{key:"positionB",value:function(){var e=-1*this._range+this.baseRange,t=Math.cos(this.angle*Math.PI/180)*e,n=Math.sin(this.angle*Math.PI/180)*e;return new a["I"](this.center.x+t,this.center.y+n)}},{key:"strength",value:function(e){this.lastStrength=this.theStrength,this.theStrength=e,this.targetRange=Math.max(this.theStrength-this.lastStrength,0),this.targetRange>this._range&&(this._range=this.targetRange)}},{key:"transition",value:function(e){this._range=Math.max(this._range-e*this._range*5,0)}}]),e}();function Qe(e,t){return e+(Math.random()-.5)*t}var Je,We,Ke,Ze,qe,$e,et,tt,nt,it,ot,rt,at,st,lt=function(){function e(t,n,i,o,r,s,l,c,d){var u,h;Object(Ve["a"])(this,e),Object(Ye["a"])(this,"rotate",360*Math.random()),Object(Ye["a"])(this,"id",Math.random()),Object(Ye["a"])(this,"distance",void 0),Object(Ye["a"])(this,"rotateSpeed",void 0),Object(Ye["a"])(this,"angle",void 0),Object(Ye["a"])(this,"speed",void 0),Object(Ye["a"])(this,"center",void 0),Object(Ye["a"])(this,"mesh",void 0),Object(Ye["a"])(this,"showDistance",void 0),Object(Ye["a"])(this,"panelMaterial",void 0),Object(Ye["a"])(this,"lineMaterial",void 0),Object(Ye["a"])(this,"panelOpacity",.1),Object(Ye["a"])(this,"line",void 0),Object(Ye["a"])(this,"group",void 0),Object(Ye["a"])(this,"onDelete",void 0),this.rotateSpeed=r,this.angle=i,this.speed=o,this.center=n,this.onDelete=d,this.distance=c.startShow,this.showDistance=c,this.panelMaterial=(new a["v"]).copy(s),this.panelMaterial.transparent=!0,this.lineMaterial=(new a["r"]).copy(l),this.lineMaterial.transparent=!0;var f=new a["o"],p=new a["o"],m=[new a["J"](Qe(t,t/2),Qe(t,t/2),Qe(t,t/2)),new a["J"](-1*Qe(t,t/2),Qe(t,t/2),Qe(t,t/2)-1),new a["J"](-1*Qe(t,t/2),-1*Qe(t,t/2),Qe(t,t/2)-1)];(u=f.vertices).push.apply(u,m),(h=p.vertices).push.apply(h,[].concat(m,[m[0]])),f.faces.push(new a["n"](0,1,2)),f.computeFaceNormals(),f.computeVertexNormals(),this.line=new a["q"](p,this.lineMaterial),this.mesh=new a["u"](f,this.panelMaterial),this.group=new a["p"],this.translateOnAxis(this.translate(this.distance),1),this.group.add(this.line),this.group.add(this.mesh),this.rotateZ(this.rotate),this.updatePosition(0)}return Object(Ge["a"])(e,[{key:"rotateZ",value:function(e){this.mesh.geometry.rotateZ(e),this.line.geometry.rotateZ(e)}},{key:"translate",value:function(e){var t=Math.cos(this.angle*Math.PI/180)*e,n=Math.sin(this.angle*Math.PI/180)*e,i=Math.cos(this.angle*Math.PI/360)*e*this.angle/180;return new a["J"](t,n,i)}},{key:"translateOnAxis",value:function(e,t){this.group.translateOnAxis(e,t)}},{key:"updatePosition",value:function(e){this.translateOnAxis(this.translate(e*this.speed),1),this.distance+=e*this.speed,this.rotateZ(this.rotateSpeed*e),this.panelMaterial.opacity=this.opacity(this.distance,this.showDistance)*this.panelOpacity,this.lineMaterial.opacity=this.opacity(this.distance,this.showDistance),this.distance>this.showDistance.endHide&&this.delete()}},{key:"delete",value:function(){this.onDelete(this),this.mesh.geometry.dispose(),this.line.geometry.dispose()}},{key:"opacity",value:function(e,t){return this.distancet.startHide?(t.endHide-this.distance)/(t.endHide-t.startHide):1}},{key:"transition",value:function(e){this.updatePosition(e)}}]),e}(),ct={R:20,G:90,B:225,TrianglesBgColor:240116,TrianglesLineColor:240116,lineColor:65535,rotate:!1},dt=[],ut=[],ht={name:"App",data:function(){return{positionZ:80,N:256,clock:new a["j"],scale:1}},methods:{init:function(){Je=new a["L"]({antialias:!0,alpha:!0}),Je.setClearAlpha(0),Je.setSize(window.innerWidth,window.innerHeight),document.body.appendChild(Je.domElement),We=new a["D"],We.background=(new a["l"]).load([n("9ac2"),n("1dc2"),n("6f4f"),n("f254"),n("a7f3"),n("dbe6")]),Ke=new a["y"](75,window.innerWidth/window.innerHeight,1,1e4),Ke.position.z=this.positionZ,window.addEventListener("resize",this.onWindowResize,!1),this.audioLines(20,this.N),this.audioBars(25,this.N/2),st=new a["p"],setInterval(this.addTriangle.bind(this),500),We.add(st);var e=new a["e"];et=new a["c"](e);var t=n("5e76");this.audioLoad(t),this.initLight(),this.initControls(),this.initGui(),this.initBloomPass(),this.initStats(),this.animate()},renderGeometries:function(e){var t=[];return e=e.concat(e[0]),e.forEach((function(e){t.push(e.x,e.y,0)})),new a["h"](new Float32Array(t),3)},updateCircle:function(){if(rt){nt.scale.set(this.scale,this.scale,this.scale);var e=it.geometry,t=e.getAttribute("position"),n=ot.geometry,i=n.getAttribute("position"),o=rt.map((function(e){return[e.positionA(),e.positionB()]}));o.forEach((function(e,n){t.set([e[0].x,e[0].y],3*n),i.set([e[1].x,e[1].y],3*n);var o=dt[n].geometry,r=o.getAttribute("position");r.set([e[0].x,e[0].y,0,e[1].x,e[1].y,0],0),r.needsUpdate=!0})),t.set([t.array[0],t.array[1]],3*o.length),i.set([i.array[0],i.array[1]],3*o.length),t.needsUpdate=!0,i.needsUpdate=!0}},audioLines:function(e,t){var n=this;rt=Ue(0,t).map((function(n){return new Xe(e,(n/t*360+45)%360,new a["I"](0,0))}));var i=new a["r"]({color:ct.lineColor});dt=Ue(0,t).map((function(e){return new a["q"]((new a["i"]).setAttribute("position",n.renderGeometries([rt[e].positionA(),rt[e].positionB()])),i)})),it=new a["q"]((new a["i"]).setAttribute("position",this.renderGeometries(rt.map((function(e){return e.positionA()})))),i),ot=new a["q"]((new a["i"]).setAttribute("position",this.renderGeometries(rt.map((function(e){return e.positionB()})))),i),nt=new a["p"],nt.add(it),nt.add(ot),dt.forEach((function(e){return nt.add(e)})),We.add(nt)},addTriangle:function(){var e=new a["v"]({color:ct.TrianglesBgColor}),t=new a["r"]({color:ct.TrianglesLineColor}),n=this.makeTriangle(e,t,(function(e){ut=ut.filter((function(t){return t!==e})),st.remove(e.group)}));st.add(n.group),ut.push(n)},makeTriangle:function(e,t,n){var i=new lt(2,new a["J"](0,0,0),360*Math.random(),Qe(5,1),Qe(.1,.05),e,t,{startShow:15,endShow:30,startHide:60,endHide:70},n);return i},audioBars:function(e,t){at=new a["p"];for(var n=e,i=t,o=0;o 1%", 47 | "last 2 versions", 48 | "not dead" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hocoa/AudioPlayer/97baf14994f6423ada381249c3b2682ba7827c71/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 457 | 458 | 485 | -------------------------------------------------------------------------------- /src/assets/bg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hocoa/AudioPlayer/97baf14994f6423ada381249c3b2682ba7827c71/src/assets/bg2.jpg -------------------------------------------------------------------------------- /src/assets/skybox/back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hocoa/AudioPlayer/97baf14994f6423ada381249c3b2682ba7827c71/src/assets/skybox/back.jpg -------------------------------------------------------------------------------- /src/assets/skybox/bottom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hocoa/AudioPlayer/97baf14994f6423ada381249c3b2682ba7827c71/src/assets/skybox/bottom.jpg -------------------------------------------------------------------------------- /src/assets/skybox/front.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hocoa/AudioPlayer/97baf14994f6423ada381249c3b2682ba7827c71/src/assets/skybox/front.jpg -------------------------------------------------------------------------------- /src/assets/skybox/left.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hocoa/AudioPlayer/97baf14994f6423ada381249c3b2682ba7827c71/src/assets/skybox/left.jpg -------------------------------------------------------------------------------- /src/assets/skybox/right.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hocoa/AudioPlayer/97baf14994f6423ada381249c3b2682ba7827c71/src/assets/skybox/right.jpg -------------------------------------------------------------------------------- /src/assets/skybox/top.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hocoa/AudioPlayer/97baf14994f6423ada381249c3b2682ba7827c71/src/assets/skybox/top.jpg -------------------------------------------------------------------------------- /src/components/Triangle.js: -------------------------------------------------------------------------------- 1 | // import * as THREE from '../lib/three.module.js'; 2 | import * as THREE from "three"; 3 | import { randomRange } from "./randomRange.js"; 4 | 5 | export class ILineDistanceOption { 6 | startShow; 7 | endShow; 8 | startHide; 9 | endHide; 10 | } 11 | 12 | export class Triangle { 13 | rotate = Math.random() * 360; 14 | id = Math.random(); 15 | distance; 16 | rotateSpeed; 17 | angle; 18 | // public pointA: THREE.Vector3; 19 | // public pointB: THREE.Vector3; 20 | // public pointC: THREE.Vector3; 21 | speed; 22 | center; 23 | mesh; 24 | showDistance; 25 | panelMaterial; 26 | lineMaterial; 27 | panelOpacity = 0.1; 28 | line; 29 | group; 30 | onDelete; 31 | constructor( 32 | size, 33 | center, 34 | angle, 35 | speed, 36 | rotateSpeed, 37 | material, 38 | lineMaterial, 39 | lineDistance, 40 | cb 41 | ) { 42 | this.rotateSpeed = rotateSpeed; 43 | this.angle = angle; 44 | this.speed = speed; 45 | this.center = center; 46 | this.onDelete = cb; 47 | this.distance = lineDistance.startShow; 48 | this.showDistance = lineDistance; 49 | this.panelMaterial = new THREE.MeshBasicMaterial().copy(material); 50 | this.panelMaterial.transparent = true; 51 | this.lineMaterial = new THREE.LineBasicMaterial().copy(lineMaterial); 52 | this.lineMaterial.transparent = true; 53 | const panelGeometry = new THREE.Geometry(); 54 | const lineGeometry = new THREE.Geometry(); 55 | 56 | const vertices = [ 57 | new THREE.Vector3( 58 | randomRange(size, size / 2), 59 | randomRange(size, size / 2), 60 | randomRange(size, size / 2) 61 | ), 62 | new THREE.Vector3( 63 | randomRange(size, size / 2) * -1, 64 | randomRange(size, size / 2), 65 | randomRange(size, size / 2) - 1 66 | ), 67 | new THREE.Vector3( 68 | randomRange(size, size / 2) * -1, 69 | randomRange(size, size / 2) * -1, 70 | randomRange(size, size / 2) - 1 71 | ) 72 | ]; 73 | 74 | panelGeometry.vertices.push(...vertices); 75 | 76 | lineGeometry.vertices.push(...[...vertices, vertices[0]]); 77 | 78 | panelGeometry.faces.push(new THREE.Face3(0, 1, 2)); 79 | panelGeometry.computeFaceNormals(); 80 | panelGeometry.computeVertexNormals(); 81 | 82 | this.line = new THREE.Line(lineGeometry, this.lineMaterial); 83 | this.mesh = new THREE.Mesh(panelGeometry, this.panelMaterial); 84 | this.group = new THREE.Group(); 85 | this.translateOnAxis(this.translate(this.distance), 1); 86 | this.group.add(this.line); 87 | this.group.add(this.mesh); 88 | this.rotateZ(this.rotate); 89 | this.updatePosition(0); 90 | } 91 | 92 | rotateZ(angle) { 93 | this.mesh.geometry.rotateZ(angle); 94 | this.line.geometry.rotateZ(angle); 95 | } 96 | 97 | translate(distance) { 98 | const x = Math.cos((this.angle * Math.PI) / 180) * distance; 99 | const y = Math.sin((this.angle * Math.PI) / 180) * distance; 100 | const z = 101 | (Math.cos((this.angle * Math.PI) / 360) * distance * this.angle) / 180; 102 | return new THREE.Vector3(x, y, z); 103 | } 104 | 105 | translateOnAxis(axis, distance) { 106 | this.group.translateOnAxis(axis, distance); 107 | } 108 | 109 | updatePosition(delay) { 110 | this.translateOnAxis(this.translate(delay * this.speed), 1); 111 | this.distance += delay * this.speed; 112 | this.rotateZ(this.rotateSpeed * delay); 113 | this.panelMaterial.opacity = 114 | this.opacity(this.distance, this.showDistance) * this.panelOpacity; 115 | this.lineMaterial.opacity = this.opacity(this.distance, this.showDistance); 116 | if (this.distance > this.showDistance.endHide) { 117 | this.delete(); 118 | } 119 | } 120 | 121 | delete() { 122 | this.onDelete(this); 123 | this.mesh.geometry.dispose(); 124 | this.line.geometry.dispose(); 125 | } 126 | 127 | opacity(distance, showDistance) { 128 | if (this.distance < showDistance.endShow) { 129 | return ( 130 | (this.distance - showDistance.startShow) / 131 | (showDistance.endShow - showDistance.startShow) 132 | ); 133 | } else if (this.distance > showDistance.startHide) { 134 | return ( 135 | (showDistance.endHide - this.distance) / 136 | (showDistance.endHide - showDistance.startHide) 137 | ); 138 | } else { 139 | return 1; 140 | } 141 | } 142 | 143 | transition(delay) { 144 | this.updatePosition(delay); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/components/node.js: -------------------------------------------------------------------------------- 1 | // import {Vector2} from '../lib/three.module.js'; 2 | import {Vector2} from 'three'; 3 | 4 | export class node { 5 | lastStrength = 0; 6 | theStrength = 0; 7 | targetRange = 0; 8 | _range = 0; 9 | constructor(baseRange, angle, center) { 10 | this.baseRange = baseRange; 11 | this.angle = angle; 12 | this.center = center; 13 | } 14 | positionA() { 15 | 16 | const range = this._range + this.baseRange; 17 | const x = Math.cos(this.angle * Math.PI / 180) * range; 18 | const y = Math.sin(this.angle * Math.PI / 180) * range; 19 | return new Vector2(this.center.x + x, this.center.y + y); 20 | } 21 | positionB() { 22 | const range = this._range * -1 + this.baseRange; 23 | const x = Math.cos(this.angle * Math.PI / 180) * range; 24 | const y = Math.sin(this.angle * Math.PI / 180) * range; 25 | return new Vector2(this.center.x + x, this.center.y + y); 26 | } 27 | strength(newStrength) { 28 | this.lastStrength = this.theStrength; 29 | this.theStrength = newStrength; 30 | this.targetRange = Math.max(this.theStrength - this.lastStrength, 0); 31 | if (this.targetRange > this._range) this._range = this.targetRange; 32 | } 33 | transition(delay) { 34 | this._range = Math.max(this._range - delay * this._range * 5, 0); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/components/randomRange.js: -------------------------------------------------------------------------------- 1 | export function randomRange(a, range) { 2 | return a + (Math.random() - 0.5) * range; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/range.js: -------------------------------------------------------------------------------- 1 | export function range(start, end, step = 1, fromRight = false) { 2 | let index = -1, 3 | length = Math.max(Math.ceil((end - start) / step), 0), 4 | result = Array(length); 5 | 6 | while (length--) { 7 | result[fromRight ? length : ++index] = start; 8 | start += step; 9 | } 10 | return result; 11 | } 12 | -------------------------------------------------------------------------------- /src/lib/CopyShader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Full-screen textured quad shader 3 | */ 4 | 5 | var CopyShader = { 6 | 7 | uniforms: { 8 | 9 | 'tDiffuse': { value: null }, 10 | 'opacity': { value: 1.0 } 11 | 12 | }, 13 | 14 | vertexShader: [ 15 | 16 | 'varying vec2 vUv;', 17 | 18 | 'void main() {', 19 | 20 | ' vUv = uv;', 21 | ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', 22 | 23 | '}' 24 | 25 | ].join( '\n' ), 26 | 27 | fragmentShader: [ 28 | 29 | 'uniform float opacity;', 30 | 31 | 'uniform sampler2D tDiffuse;', 32 | 33 | 'varying vec2 vUv;', 34 | 35 | 'void main() {', 36 | 37 | ' vec4 texel = texture2D( tDiffuse, vUv );', 38 | ' gl_FragColor = opacity * texel;', 39 | 40 | '}' 41 | 42 | ].join( '\n' ) 43 | 44 | }; 45 | 46 | export { CopyShader }; 47 | -------------------------------------------------------------------------------- /src/lib/EffectComposer.js: -------------------------------------------------------------------------------- 1 | import { 2 | BufferGeometry, 3 | Clock, 4 | Float32BufferAttribute, 5 | LinearFilter, 6 | Mesh, 7 | OrthographicCamera, 8 | RGBAFormat, 9 | Vector2, 10 | WebGLRenderTarget 11 | } from "./three.module.js"; 12 | import { CopyShader } from './CopyShader.js'; 13 | import { ShaderPass } from './ShaderPass.js'; 14 | import { MaskPass } from './MaskPass.js'; 15 | import { ClearMaskPass } from './MaskPass.js'; 16 | 17 | var EffectComposer = function ( renderer, renderTarget ) { 18 | 19 | this.renderer = renderer; 20 | 21 | if ( renderTarget === undefined ) { 22 | 23 | var parameters = { 24 | minFilter: LinearFilter, 25 | magFilter: LinearFilter, 26 | format: RGBAFormat 27 | }; 28 | 29 | var size = renderer.getSize( new Vector2() ); 30 | this._pixelRatio = renderer.getPixelRatio(); 31 | this._width = size.width; 32 | this._height = size.height; 33 | 34 | renderTarget = new WebGLRenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio, parameters ); 35 | renderTarget.texture.name = 'EffectComposer.rt1'; 36 | 37 | } else { 38 | 39 | this._pixelRatio = 1; 40 | this._width = renderTarget.width; 41 | this._height = renderTarget.height; 42 | 43 | } 44 | 45 | this.renderTarget1 = renderTarget; 46 | this.renderTarget2 = renderTarget.clone(); 47 | this.renderTarget2.texture.name = 'EffectComposer.rt2'; 48 | 49 | this.writeBuffer = this.renderTarget1; 50 | this.readBuffer = this.renderTarget2; 51 | 52 | this.renderToScreen = true; 53 | 54 | this.passes = []; 55 | 56 | // dependencies 57 | 58 | if ( CopyShader === undefined ) { 59 | 60 | console.error( 'THREE.EffectComposer relies on CopyShader' ); 61 | 62 | } 63 | 64 | if ( ShaderPass === undefined ) { 65 | 66 | console.error( 'THREE.EffectComposer relies on ShaderPass' ); 67 | 68 | } 69 | 70 | this.copyPass = new ShaderPass( CopyShader ); 71 | 72 | this.clock = new Clock(); 73 | 74 | }; 75 | 76 | Object.assign( EffectComposer.prototype, { 77 | 78 | swapBuffers: function () { 79 | 80 | var tmp = this.readBuffer; 81 | this.readBuffer = this.writeBuffer; 82 | this.writeBuffer = tmp; 83 | 84 | }, 85 | 86 | addPass: function ( pass ) { 87 | 88 | this.passes.push( pass ); 89 | pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); 90 | 91 | }, 92 | 93 | insertPass: function ( pass, index ) { 94 | 95 | this.passes.splice( index, 0, pass ); 96 | pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); 97 | 98 | }, 99 | 100 | removePass: function ( pass ) { 101 | 102 | const index = this.passes.indexOf( pass ); 103 | 104 | if ( index !== - 1 ) { 105 | 106 | this.passes.splice( index, 1 ); 107 | 108 | } 109 | 110 | }, 111 | 112 | isLastEnabledPass: function ( passIndex ) { 113 | 114 | for ( var i = passIndex + 1; i < this.passes.length; i ++ ) { 115 | 116 | if ( this.passes[ i ].enabled ) { 117 | 118 | return false; 119 | 120 | } 121 | 122 | } 123 | 124 | return true; 125 | 126 | }, 127 | 128 | render: function ( deltaTime ) { 129 | 130 | // deltaTime value is in seconds 131 | 132 | if ( deltaTime === undefined ) { 133 | 134 | deltaTime = this.clock.getDelta(); 135 | 136 | } 137 | 138 | var currentRenderTarget = this.renderer.getRenderTarget(); 139 | 140 | var maskActive = false; 141 | 142 | var pass, i, il = this.passes.length; 143 | 144 | for ( i = 0; i < il; i ++ ) { 145 | 146 | pass = this.passes[ i ]; 147 | 148 | if ( pass.enabled === false ) continue; 149 | 150 | pass.renderToScreen = ( this.renderToScreen && this.isLastEnabledPass( i ) ); 151 | pass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime, maskActive ); 152 | 153 | if ( pass.needsSwap ) { 154 | 155 | if ( maskActive ) { 156 | 157 | var context = this.renderer.getContext(); 158 | var stencil = this.renderer.state.buffers.stencil; 159 | 160 | //context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff ); 161 | stencil.setFunc( context.NOTEQUAL, 1, 0xffffffff ); 162 | 163 | this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime ); 164 | 165 | //context.stencilFunc( context.EQUAL, 1, 0xffffffff ); 166 | stencil.setFunc( context.EQUAL, 1, 0xffffffff ); 167 | 168 | } 169 | 170 | this.swapBuffers(); 171 | 172 | } 173 | 174 | if ( MaskPass !== undefined ) { 175 | 176 | if ( pass instanceof MaskPass ) { 177 | 178 | maskActive = true; 179 | 180 | } else if ( pass instanceof ClearMaskPass ) { 181 | 182 | maskActive = false; 183 | 184 | } 185 | 186 | } 187 | 188 | } 189 | 190 | this.renderer.setRenderTarget( currentRenderTarget ); 191 | 192 | }, 193 | 194 | reset: function ( renderTarget ) { 195 | 196 | if ( renderTarget === undefined ) { 197 | 198 | var size = this.renderer.getSize( new Vector2() ); 199 | this._pixelRatio = this.renderer.getPixelRatio(); 200 | this._width = size.width; 201 | this._height = size.height; 202 | 203 | renderTarget = this.renderTarget1.clone(); 204 | renderTarget.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); 205 | 206 | } 207 | 208 | this.renderTarget1.dispose(); 209 | this.renderTarget2.dispose(); 210 | this.renderTarget1 = renderTarget; 211 | this.renderTarget2 = renderTarget.clone(); 212 | 213 | this.writeBuffer = this.renderTarget1; 214 | this.readBuffer = this.renderTarget2; 215 | 216 | }, 217 | 218 | setSize: function ( width, height ) { 219 | 220 | this._width = width; 221 | this._height = height; 222 | 223 | var effectiveWidth = this._width * this._pixelRatio; 224 | var effectiveHeight = this._height * this._pixelRatio; 225 | 226 | this.renderTarget1.setSize( effectiveWidth, effectiveHeight ); 227 | this.renderTarget2.setSize( effectiveWidth, effectiveHeight ); 228 | 229 | for ( var i = 0; i < this.passes.length; i ++ ) { 230 | 231 | this.passes[ i ].setSize( effectiveWidth, effectiveHeight ); 232 | 233 | } 234 | 235 | }, 236 | 237 | setPixelRatio: function ( pixelRatio ) { 238 | 239 | this._pixelRatio = pixelRatio; 240 | 241 | this.setSize( this._width, this._height ); 242 | 243 | } 244 | 245 | } ); 246 | 247 | 248 | var Pass = function () { 249 | 250 | // if set to true, the pass is processed by the composer 251 | this.enabled = true; 252 | 253 | // if set to true, the pass indicates to swap read and write buffer after rendering 254 | this.needsSwap = true; 255 | 256 | // if set to true, the pass clears its buffer before rendering 257 | this.clear = false; 258 | 259 | // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer. 260 | this.renderToScreen = false; 261 | 262 | }; 263 | 264 | Object.assign( Pass.prototype, { 265 | 266 | setSize: function ( /* width, height */ ) {}, 267 | 268 | render: function ( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */ ) { 269 | 270 | console.error( 'THREE.Pass: .render() must be implemented in derived pass.' ); 271 | 272 | } 273 | 274 | } ); 275 | 276 | // Helper for passes that need to fill the viewport with a single quad. 277 | Pass.FullScreenQuad = ( function () { 278 | 279 | var camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); 280 | 281 | // https://github.com/mrdoob/three.js/pull/21358 282 | 283 | var geometry = new BufferGeometry(); 284 | geometry.setAttribute( 'position', new Float32BufferAttribute( [ - 1, 3, 0, - 1, - 1, 0, 3, - 1, 0 ], 3 ) ); 285 | geometry.setAttribute( 'uv', new Float32BufferAttribute( [ 0, 2, 0, 0, 2, 0 ], 2 ) ); 286 | 287 | var FullScreenQuad = function ( material ) { 288 | 289 | this._mesh = new Mesh( geometry, material ); 290 | 291 | }; 292 | 293 | Object.defineProperty( FullScreenQuad.prototype, 'material', { 294 | 295 | get: function () { 296 | 297 | return this._mesh.material; 298 | 299 | }, 300 | 301 | set: function ( value ) { 302 | 303 | this._mesh.material = value; 304 | 305 | } 306 | 307 | } ); 308 | 309 | Object.assign( FullScreenQuad.prototype, { 310 | 311 | dispose: function () { 312 | 313 | this._mesh.geometry.dispose(); 314 | 315 | }, 316 | 317 | render: function ( renderer ) { 318 | 319 | renderer.render( this._mesh, camera ); 320 | 321 | } 322 | 323 | } ); 324 | 325 | return FullScreenQuad; 326 | 327 | } )(); 328 | 329 | export { EffectComposer, Pass }; 330 | -------------------------------------------------------------------------------- /src/lib/LuminosityHighPassShader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author bhouston / http://clara.io/ 3 | * 4 | * Luminosity 5 | * http://en.wikipedia.org/wiki/Luminosity 6 | */ 7 | 8 | // import { Color } from "./three.module.js"; 9 | import { Color } from "three"; 10 | 11 | var LuminosityHighPassShader = { 12 | shaderID: "luminosityHighPass", 13 | 14 | uniforms: { 15 | tDiffuse: { value: null }, 16 | luminosityThreshold: { value: 1.0 }, 17 | smoothWidth: { value: 1.0 }, 18 | defaultColor: { value: new Color(0x000000) }, 19 | defaultOpacity: { value: 0.0 }, 20 | }, 21 | 22 | vertexShader: [ 23 | "varying vec2 vUv;", 24 | 25 | "void main() {", 26 | 27 | " vUv = uv;", 28 | 29 | " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", 30 | 31 | "}", 32 | ].join("\n"), 33 | 34 | fragmentShader: [ 35 | "uniform sampler2D tDiffuse;", 36 | "uniform vec3 defaultColor;", 37 | "uniform float defaultOpacity;", 38 | "uniform float luminosityThreshold;", 39 | "uniform float smoothWidth;", 40 | 41 | "varying vec2 vUv;", 42 | 43 | "void main() {", 44 | 45 | " vec4 texel = texture2D( tDiffuse, vUv );", 46 | 47 | " vec3 luma = vec3( 0.299, 0.587, 0.114 );", 48 | 49 | " float v = dot( texel.xyz, luma );", 50 | 51 | " vec4 outputColor = vec4( defaultColor.rgb, defaultOpacity );", 52 | 53 | " float alpha = smoothstep( luminosityThreshold, luminosityThreshold + smoothWidth, v );", 54 | 55 | " gl_FragColor = mix( outputColor, texel, alpha );", 56 | 57 | "}", 58 | ].join("\n"), 59 | }; 60 | 61 | export { LuminosityHighPassShader }; 62 | -------------------------------------------------------------------------------- /src/lib/MaskPass.js: -------------------------------------------------------------------------------- 1 | import { Pass } from './Pass'; 2 | 3 | var MaskPass = function ( scene, camera ) { 4 | 5 | Pass.call( this ); 6 | 7 | this.scene = scene; 8 | this.camera = camera; 9 | 10 | this.clear = true; 11 | this.needsSwap = false; 12 | 13 | this.inverse = false; 14 | 15 | }; 16 | 17 | MaskPass.prototype = Object.assign( Object.create( Pass.prototype ), { 18 | 19 | constructor: MaskPass, 20 | 21 | render: function ( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { 22 | 23 | var context = renderer.getContext(); 24 | var state = renderer.state; 25 | 26 | // don't update color or depth 27 | 28 | state.buffers.color.setMask( false ); 29 | state.buffers.depth.setMask( false ); 30 | 31 | // lock buffers 32 | 33 | state.buffers.color.setLocked( true ); 34 | state.buffers.depth.setLocked( true ); 35 | 36 | // set up stencil 37 | 38 | var writeValue, clearValue; 39 | 40 | if ( this.inverse ) { 41 | 42 | writeValue = 0; 43 | clearValue = 1; 44 | 45 | } else { 46 | 47 | writeValue = 1; 48 | clearValue = 0; 49 | 50 | } 51 | 52 | state.buffers.stencil.setTest( true ); 53 | state.buffers.stencil.setOp( context.REPLACE, context.REPLACE, context.REPLACE ); 54 | state.buffers.stencil.setFunc( context.ALWAYS, writeValue, 0xffffffff ); 55 | state.buffers.stencil.setClear( clearValue ); 56 | state.buffers.stencil.setLocked( true ); 57 | 58 | // draw into the stencil buffer 59 | 60 | renderer.setRenderTarget( readBuffer ); 61 | if ( this.clear ) renderer.clear(); 62 | renderer.render( this.scene, this.camera ); 63 | 64 | renderer.setRenderTarget( writeBuffer ); 65 | if ( this.clear ) renderer.clear(); 66 | renderer.render( this.scene, this.camera ); 67 | 68 | // unlock color and depth buffer for subsequent rendering 69 | 70 | state.buffers.color.setLocked( false ); 71 | state.buffers.depth.setLocked( false ); 72 | 73 | // only render where stencil is set to 1 74 | 75 | state.buffers.stencil.setLocked( false ); 76 | state.buffers.stencil.setFunc( context.EQUAL, 1, 0xffffffff ); // draw if == 1 77 | state.buffers.stencil.setOp( context.KEEP, context.KEEP, context.KEEP ); 78 | state.buffers.stencil.setLocked( true ); 79 | 80 | } 81 | 82 | } ); 83 | 84 | 85 | var ClearMaskPass = function () { 86 | 87 | Pass.call( this ); 88 | 89 | this.needsSwap = false; 90 | 91 | }; 92 | 93 | ClearMaskPass.prototype = Object.create( Pass.prototype ); 94 | 95 | Object.assign( ClearMaskPass.prototype, { 96 | 97 | render: function ( renderer /*, writeBuffer, readBuffer, deltaTime, maskActive */ ) { 98 | 99 | renderer.state.buffers.stencil.setLocked( false ); 100 | renderer.state.buffers.stencil.setTest( false ); 101 | 102 | } 103 | 104 | } ); 105 | 106 | export { MaskPass, ClearMaskPass }; 107 | -------------------------------------------------------------------------------- /src/lib/OBJLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | // import { 6 | // BufferGeometry, 7 | // FileLoader, 8 | // Float32BufferAttribute, 9 | // Group, 10 | // LineBasicMaterial, 11 | // LineSegments, 12 | // Loader, 13 | // Material, 14 | // Mesh, 15 | // MeshPhongMaterial, 16 | // NoColors, 17 | // Points, 18 | // PointsMaterial, 19 | // VertexColors, 20 | // } from "./three.module.js"; 21 | import { 22 | BufferGeometry, 23 | FileLoader, 24 | Float32BufferAttribute, 25 | Group, 26 | LineBasicMaterial, 27 | LineSegments, 28 | Loader, 29 | Material, 30 | Mesh, 31 | MeshPhongMaterial, 32 | NoColors, 33 | Points, 34 | PointsMaterial, 35 | VertexColors, 36 | } from "three"; 37 | 38 | var OBJLoader = (function () { 39 | // o object_name | g group_name 40 | var object_pattern = /^[og]\s*(.+)?/; 41 | // mtllib file_reference 42 | var material_library_pattern = /^mtllib /; 43 | // usemtl material_name 44 | var material_use_pattern = /^usemtl /; 45 | // usemap map_name 46 | var map_use_pattern = /^usemap /; 47 | 48 | function ParserState() { 49 | var state = { 50 | objects: [], 51 | object: {}, 52 | 53 | vertices: [], 54 | normals: [], 55 | colors: [], 56 | uvs: [], 57 | 58 | materialLibraries: [], 59 | 60 | startObject: function (name, fromDeclaration) { 61 | // If the current object (initial from reset) is not from a g/o declaration in the parsed 62 | // file. We need to use it for the first parsed g/o to keep things in sync. 63 | if (this.object && this.object.fromDeclaration === false) { 64 | this.object.name = name; 65 | this.object.fromDeclaration = fromDeclaration !== false; 66 | return; 67 | } 68 | 69 | var previousMaterial = 70 | this.object && typeof this.object.currentMaterial === "function" 71 | ? this.object.currentMaterial() 72 | : undefined; 73 | 74 | if (this.object && typeof this.object._finalize === "function") { 75 | this.object._finalize(true); 76 | } 77 | 78 | this.object = { 79 | name: name || "", 80 | fromDeclaration: fromDeclaration !== false, 81 | 82 | geometry: { 83 | vertices: [], 84 | normals: [], 85 | colors: [], 86 | uvs: [], 87 | }, 88 | materials: [], 89 | smooth: true, 90 | 91 | startMaterial: function (name, libraries) { 92 | var previous = this._finalize(false); 93 | 94 | // New usemtl declaration overwrites an inherited material, except if faces were declared 95 | // after the material, then it must be preserved for proper MultiMaterial continuation. 96 | if (previous && (previous.inherited || previous.groupCount <= 0)) { 97 | this.materials.splice(previous.index, 1); 98 | } 99 | 100 | var material = { 101 | index: this.materials.length, 102 | name: name || "", 103 | mtllib: 104 | Array.isArray(libraries) && libraries.length > 0 105 | ? libraries[libraries.length - 1] 106 | : "", 107 | smooth: previous !== undefined ? previous.smooth : this.smooth, 108 | groupStart: previous !== undefined ? previous.groupEnd : 0, 109 | groupEnd: -1, 110 | groupCount: -1, 111 | inherited: false, 112 | 113 | clone: function (index) { 114 | var cloned = { 115 | index: typeof index === "number" ? index : this.index, 116 | name: this.name, 117 | mtllib: this.mtllib, 118 | smooth: this.smooth, 119 | groupStart: 0, 120 | groupEnd: -1, 121 | groupCount: -1, 122 | inherited: false, 123 | }; 124 | cloned.clone = this.clone.bind(cloned); 125 | return cloned; 126 | }, 127 | }; 128 | 129 | this.materials.push(material); 130 | 131 | return material; 132 | }, 133 | 134 | currentMaterial: function () { 135 | if (this.materials.length > 0) { 136 | return this.materials[this.materials.length - 1]; 137 | } 138 | 139 | return undefined; 140 | }, 141 | 142 | _finalize: function (end) { 143 | var lastMultiMaterial = this.currentMaterial(); 144 | if (lastMultiMaterial && lastMultiMaterial.groupEnd === -1) { 145 | lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3; 146 | lastMultiMaterial.groupCount = 147 | lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart; 148 | lastMultiMaterial.inherited = false; 149 | } 150 | 151 | // Ignore objects tail materials if no face declarations followed them before a new o/g started. 152 | if (end && this.materials.length > 1) { 153 | for (var mi = this.materials.length - 1; mi >= 0; mi--) { 154 | if (this.materials[mi].groupCount <= 0) { 155 | this.materials.splice(mi, 1); 156 | } 157 | } 158 | } 159 | 160 | // Guarantee at least one empty material, this makes the creation later more straight forward. 161 | if (end && this.materials.length === 0) { 162 | this.materials.push({ 163 | name: "", 164 | smooth: this.smooth, 165 | }); 166 | } 167 | 168 | return lastMultiMaterial; 169 | }, 170 | }; 171 | 172 | // Inherit previous objects material. 173 | // Spec tells us that a declared material must be set to all objects until a new material is declared. 174 | // If a usemtl declaration is encountered while this new object is being parsed, it will 175 | // overwrite the inherited material. Exception being that there was already face declarations 176 | // to the inherited material, then it will be preserved for proper MultiMaterial continuation. 177 | 178 | if ( 179 | previousMaterial && 180 | previousMaterial.name && 181 | typeof previousMaterial.clone === "function" 182 | ) { 183 | var declared = previousMaterial.clone(0); 184 | declared.inherited = true; 185 | this.object.materials.push(declared); 186 | } 187 | 188 | this.objects.push(this.object); 189 | }, 190 | 191 | finalize: function () { 192 | if (this.object && typeof this.object._finalize === "function") { 193 | this.object._finalize(true); 194 | } 195 | }, 196 | 197 | parseVertexIndex: function (value, len) { 198 | var index = parseInt(value, 10); 199 | return (index >= 0 ? index - 1 : index + len / 3) * 3; 200 | }, 201 | 202 | parseNormalIndex: function (value, len) { 203 | var index = parseInt(value, 10); 204 | return (index >= 0 ? index - 1 : index + len / 3) * 3; 205 | }, 206 | 207 | parseUVIndex: function (value, len) { 208 | var index = parseInt(value, 10); 209 | return (index >= 0 ? index - 1 : index + len / 2) * 2; 210 | }, 211 | 212 | addVertex: function (a, b, c) { 213 | var src = this.vertices; 214 | var dst = this.object.geometry.vertices; 215 | 216 | dst.push(src[a + 0], src[a + 1], src[a + 2]); 217 | dst.push(src[b + 0], src[b + 1], src[b + 2]); 218 | dst.push(src[c + 0], src[c + 1], src[c + 2]); 219 | }, 220 | 221 | addVertexPoint: function (a) { 222 | var src = this.vertices; 223 | var dst = this.object.geometry.vertices; 224 | 225 | dst.push(src[a + 0], src[a + 1], src[a + 2]); 226 | }, 227 | 228 | addVertexLine: function (a) { 229 | var src = this.vertices; 230 | var dst = this.object.geometry.vertices; 231 | 232 | dst.push(src[a + 0], src[a + 1], src[a + 2]); 233 | }, 234 | 235 | addNormal: function (a, b, c) { 236 | var src = this.normals; 237 | var dst = this.object.geometry.normals; 238 | 239 | dst.push(src[a + 0], src[a + 1], src[a + 2]); 240 | dst.push(src[b + 0], src[b + 1], src[b + 2]); 241 | dst.push(src[c + 0], src[c + 1], src[c + 2]); 242 | }, 243 | 244 | addColor: function (a, b, c) { 245 | var src = this.colors; 246 | var dst = this.object.geometry.colors; 247 | 248 | dst.push(src[a + 0], src[a + 1], src[a + 2]); 249 | dst.push(src[b + 0], src[b + 1], src[b + 2]); 250 | dst.push(src[c + 0], src[c + 1], src[c + 2]); 251 | }, 252 | 253 | addUV: function (a, b, c) { 254 | var src = this.uvs; 255 | var dst = this.object.geometry.uvs; 256 | 257 | dst.push(src[a + 0], src[a + 1]); 258 | dst.push(src[b + 0], src[b + 1]); 259 | dst.push(src[c + 0], src[c + 1]); 260 | }, 261 | 262 | addUVLine: function (a) { 263 | var src = this.uvs; 264 | var dst = this.object.geometry.uvs; 265 | 266 | dst.push(src[a + 0], src[a + 1]); 267 | }, 268 | 269 | addFace: function (a, b, c, ua, ub, uc, na, nb, nc) { 270 | var vLen = this.vertices.length; 271 | 272 | var ia = this.parseVertexIndex(a, vLen); 273 | var ib = this.parseVertexIndex(b, vLen); 274 | var ic = this.parseVertexIndex(c, vLen); 275 | 276 | this.addVertex(ia, ib, ic); 277 | 278 | if (this.colors.length > 0) { 279 | this.addColor(ia, ib, ic); 280 | } 281 | 282 | if (ua !== undefined && ua !== "") { 283 | var uvLen = this.uvs.length; 284 | ia = this.parseUVIndex(ua, uvLen); 285 | ib = this.parseUVIndex(ub, uvLen); 286 | ic = this.parseUVIndex(uc, uvLen); 287 | this.addUV(ia, ib, ic); 288 | } 289 | 290 | if (na !== undefined && na !== "") { 291 | // Normals are many times the same. If so, skip function call and parseInt. 292 | var nLen = this.normals.length; 293 | ia = this.parseNormalIndex(na, nLen); 294 | 295 | ib = na === nb ? ia : this.parseNormalIndex(nb, nLen); 296 | ic = na === nc ? ia : this.parseNormalIndex(nc, nLen); 297 | 298 | this.addNormal(ia, ib, ic); 299 | } 300 | }, 301 | 302 | addPointGeometry: function (vertices) { 303 | this.object.geometry.type = "Points"; 304 | 305 | var vLen = this.vertices.length; 306 | 307 | for (var vi = 0, l = vertices.length; vi < l; vi++) { 308 | this.addVertexPoint(this.parseVertexIndex(vertices[vi], vLen)); 309 | } 310 | }, 311 | 312 | addLineGeometry: function (vertices, uvs) { 313 | this.object.geometry.type = "Line"; 314 | 315 | var vLen = this.vertices.length; 316 | var uvLen = this.uvs.length; 317 | 318 | for (var vi = 0, l = vertices.length; vi < l; vi++) { 319 | this.addVertexLine(this.parseVertexIndex(vertices[vi], vLen)); 320 | } 321 | 322 | for (var uvi = 0, l = uvs.length; uvi < l; uvi++) { 323 | this.addUVLine(this.parseUVIndex(uvs[uvi], uvLen)); 324 | } 325 | }, 326 | }; 327 | 328 | state.startObject("", false); 329 | 330 | return state; 331 | } 332 | 333 | // 334 | 335 | function OBJLoader(manager) { 336 | Loader.call(this, manager); 337 | 338 | this.materials = null; 339 | } 340 | 341 | OBJLoader.prototype = Object.assign(Object.create(Loader.prototype), { 342 | constructor: OBJLoader, 343 | 344 | load: function (url, onLoad, onProgress, onError) { 345 | var scope = this; 346 | 347 | var loader = new FileLoader(scope.manager); 348 | loader.setPath(this.path); 349 | loader.load( 350 | url, 351 | function (text) { 352 | onLoad(scope.parse(text)); 353 | }, 354 | onProgress, 355 | onError 356 | ); 357 | }, 358 | 359 | setMaterials: function (materials) { 360 | this.materials = materials; 361 | 362 | return this; 363 | }, 364 | 365 | parse: function (text) { 366 | console.time("OBJLoader"); 367 | 368 | var state = new ParserState(); 369 | 370 | if (text.indexOf("\r\n") !== -1) { 371 | // This is faster than String.split with regex that splits on both 372 | text = text.replace(/\r\n/g, "\n"); 373 | } 374 | 375 | if (text.indexOf("\\\n") !== -1) { 376 | // join lines separated by a line continuation character (\) 377 | text = text.replace(/\\\n/g, ""); 378 | } 379 | 380 | var lines = text.split("\n"); 381 | var line = "", 382 | lineFirstChar = ""; 383 | var lineLength = 0; 384 | var result = []; 385 | 386 | // Faster to just trim left side of the line. Use if available. 387 | var trimLeft = typeof "".trimLeft === "function"; 388 | 389 | for (var i = 0, l = lines.length; i < l; i++) { 390 | line = lines[i]; 391 | 392 | line = trimLeft ? line.trimLeft() : line.trim(); 393 | 394 | lineLength = line.length; 395 | 396 | if (lineLength === 0) continue; 397 | 398 | lineFirstChar = line.charAt(0); 399 | 400 | // @todo invoke passed in handler if any 401 | if (lineFirstChar === "#") continue; 402 | 403 | if (lineFirstChar === "v") { 404 | var data = line.split(/\s+/); 405 | 406 | switch (data[0]) { 407 | case "v": 408 | state.vertices.push( 409 | parseFloat(data[1]), 410 | parseFloat(data[2]), 411 | parseFloat(data[3]) 412 | ); 413 | if (data.length >= 7) { 414 | state.colors.push( 415 | parseFloat(data[4]), 416 | parseFloat(data[5]), 417 | parseFloat(data[6]) 418 | ); 419 | } 420 | break; 421 | case "vn": 422 | state.normals.push( 423 | parseFloat(data[1]), 424 | parseFloat(data[2]), 425 | parseFloat(data[3]) 426 | ); 427 | break; 428 | case "vt": 429 | state.uvs.push(parseFloat(data[1]), parseFloat(data[2])); 430 | break; 431 | } 432 | } else if (lineFirstChar === "f") { 433 | var lineData = line.substr(1).trim(); 434 | var vertexData = lineData.split(/\s+/); 435 | var faceVertices = []; 436 | 437 | // Parse the face vertex data into an easy to work with format 438 | 439 | for (var j = 0, jl = vertexData.length; j < jl; j++) { 440 | var vertex = vertexData[j]; 441 | 442 | if (vertex.length > 0) { 443 | var vertexParts = vertex.split("/"); 444 | faceVertices.push(vertexParts); 445 | } 446 | } 447 | 448 | // Draw an edge between the first vertex and all subsequent vertices to form an n-gon 449 | 450 | var v1 = faceVertices[0]; 451 | 452 | for (var j = 1, jl = faceVertices.length - 1; j < jl; j++) { 453 | var v2 = faceVertices[j]; 454 | var v3 = faceVertices[j + 1]; 455 | 456 | state.addFace( 457 | v1[0], 458 | v2[0], 459 | v3[0], 460 | v1[1], 461 | v2[1], 462 | v3[1], 463 | v1[2], 464 | v2[2], 465 | v3[2] 466 | ); 467 | } 468 | } else if (lineFirstChar === "l") { 469 | var lineParts = line.substring(1).trim().split(" "); 470 | var lineVertices = [], 471 | lineUVs = []; 472 | 473 | if (line.indexOf("/") === -1) { 474 | lineVertices = lineParts; 475 | } else { 476 | for (var li = 0, llen = lineParts.length; li < llen; li++) { 477 | var parts = lineParts[li].split("/"); 478 | 479 | if (parts[0] !== "") lineVertices.push(parts[0]); 480 | if (parts[1] !== "") lineUVs.push(parts[1]); 481 | } 482 | } 483 | state.addLineGeometry(lineVertices, lineUVs); 484 | } else if (lineFirstChar === "p") { 485 | var lineData = line.substr(1).trim(); 486 | var pointData = lineData.split(" "); 487 | 488 | state.addPointGeometry(pointData); 489 | } else if ((result = object_pattern.exec(line)) !== null) { 490 | // o object_name 491 | // or 492 | // g group_name 493 | 494 | // WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869 495 | // var name = result[ 0 ].substr( 1 ).trim(); 496 | var name = (" " + result[0].substr(1).trim()).substr(1); 497 | 498 | state.startObject(name); 499 | } else if (material_use_pattern.test(line)) { 500 | // material 501 | 502 | state.object.startMaterial( 503 | line.substring(7).trim(), 504 | state.materialLibraries 505 | ); 506 | } else if (material_library_pattern.test(line)) { 507 | // mtl file 508 | 509 | state.materialLibraries.push(line.substring(7).trim()); 510 | } else if (map_use_pattern.test(line)) { 511 | // the line is parsed but ignored since the loader assumes textures are defined MTL files 512 | // (according to https://www.okino.com/conv/imp_wave.htm, 'usemap' is the old-style Wavefront texture reference method) 513 | 514 | console.warn( 515 | 'THREE.OBJLoader: Rendering identifier "usemap" not supported. Textures must be defined in MTL files.' 516 | ); 517 | } else if (lineFirstChar === "s") { 518 | result = line.split(" "); 519 | 520 | // smooth shading 521 | 522 | // @todo Handle files that have varying smooth values for a set of faces inside one geometry, 523 | // but does not define a usemtl for each face set. 524 | // This should be detected and a dummy material created (later MultiMaterial and geometry groups). 525 | // This requires some care to not create extra material on each smooth value for "normal" obj files. 526 | // where explicit usemtl defines geometry groups. 527 | // Example asset: examples/models/obj/cerberus/Cerberus.obj 528 | 529 | /* 530 | * http://paulbourke.net/dataformats/obj/ 531 | * or 532 | * http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf 533 | * 534 | * From chapter "Grouping" Syntax explanation "s group_number": 535 | * "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off. 536 | * Polygonal elements use group numbers to put elements in different smoothing groups. For free-form 537 | * surfaces, smoothing groups are either turned on or off; there is no difference between values greater 538 | * than 0." 539 | */ 540 | if (result.length > 1) { 541 | var value = result[1].trim().toLowerCase(); 542 | state.object.smooth = value !== "0" && value !== "off"; 543 | } else { 544 | // ZBrush can produce "s" lines #11707 545 | state.object.smooth = true; 546 | } 547 | var material = state.object.currentMaterial(); 548 | if (material) material.smooth = state.object.smooth; 549 | } else { 550 | // Handle null terminated files without exception 551 | if (line === "\0") continue; 552 | 553 | throw new Error('THREE.OBJLoader: Unexpected line: "' + line + '"'); 554 | } 555 | } 556 | 557 | state.finalize(); 558 | 559 | var container = new Group(); 560 | container.materialLibraries = [].concat(state.materialLibraries); 561 | 562 | for (var i = 0, l = state.objects.length; i < l; i++) { 563 | var object = state.objects[i]; 564 | var geometry = object.geometry; 565 | var materials = object.materials; 566 | var isLine = geometry.type === "Line"; 567 | var isPoints = geometry.type === "Points"; 568 | var hasVertexColors = false; 569 | 570 | // Skip o/g line declarations that did not follow with any faces 571 | if (geometry.vertices.length === 0) continue; 572 | 573 | var buffergeometry = new BufferGeometry(); 574 | 575 | buffergeometry.setAttribute( 576 | "position", 577 | new Float32BufferAttribute(geometry.vertices, 3) 578 | ); 579 | 580 | if (geometry.normals.length > 0) { 581 | buffergeometry.setAttribute( 582 | "normal", 583 | new Float32BufferAttribute(geometry.normals, 3) 584 | ); 585 | } else { 586 | buffergeometry.computeVertexNormals(); 587 | } 588 | 589 | if (geometry.colors.length > 0) { 590 | hasVertexColors = true; 591 | buffergeometry.setAttribute( 592 | "color", 593 | new Float32BufferAttribute(geometry.colors, 3) 594 | ); 595 | } 596 | 597 | if (geometry.uvs.length > 0) { 598 | buffergeometry.setAttribute( 599 | "uv", 600 | new Float32BufferAttribute(geometry.uvs, 2) 601 | ); 602 | } 603 | 604 | // Create materials 605 | 606 | var createdMaterials = []; 607 | 608 | for (var mi = 0, miLen = materials.length; mi < miLen; mi++) { 609 | var sourceMaterial = materials[mi]; 610 | var material = undefined; 611 | 612 | if (this.materials !== null) { 613 | material = this.materials.create(sourceMaterial.name); 614 | 615 | // mtl etc. loaders probably can't create line materials correctly, copy properties to a line material. 616 | if ( 617 | isLine && 618 | material && 619 | !(material instanceof LineBasicMaterial) 620 | ) { 621 | var materialLine = new LineBasicMaterial(); 622 | Material.prototype.copy.call(materialLine, material); 623 | materialLine.color.copy(material.color); 624 | material = materialLine; 625 | } else if ( 626 | isPoints && 627 | material && 628 | !(material instanceof PointsMaterial) 629 | ) { 630 | var materialPoints = new PointsMaterial({ 631 | size: 10, 632 | sizeAttenuation: false, 633 | }); 634 | Material.prototype.copy.call(materialPoints, material); 635 | materialPoints.color.copy(material.color); 636 | materialPoints.map = material.map; 637 | material = materialPoints; 638 | } 639 | } 640 | 641 | if (!material) { 642 | if (isLine) { 643 | material = new LineBasicMaterial(); 644 | } else if (isPoints) { 645 | material = new PointsMaterial({ 646 | size: 1, 647 | sizeAttenuation: false, 648 | }); 649 | } else { 650 | material = new MeshPhongMaterial(); 651 | } 652 | 653 | material.name = sourceMaterial.name; 654 | } 655 | 656 | material.flatShading = sourceMaterial.smooth ? false : true; 657 | material.vertexColors = hasVertexColors ? VertexColors : NoColors; 658 | 659 | createdMaterials.push(material); 660 | } 661 | 662 | // Create mesh 663 | 664 | var mesh; 665 | 666 | if (createdMaterials.length > 1) { 667 | for (var mi = 0, miLen = materials.length; mi < miLen; mi++) { 668 | var sourceMaterial = materials[mi]; 669 | buffergeometry.addGroup( 670 | sourceMaterial.groupStart, 671 | sourceMaterial.groupCount, 672 | mi 673 | ); 674 | } 675 | 676 | if (isLine) { 677 | mesh = new LineSegments(buffergeometry, createdMaterials); 678 | } else if (isPoints) { 679 | mesh = new Points(buffergeometry, createdMaterials); 680 | } else { 681 | mesh = new Mesh(buffergeometry, createdMaterials); 682 | } 683 | } else { 684 | if (isLine) { 685 | mesh = new LineSegments(buffergeometry, createdMaterials[0]); 686 | } else if (isPoints) { 687 | mesh = new Points(buffergeometry, createdMaterials[0]); 688 | } else { 689 | mesh = new Mesh(buffergeometry, createdMaterials[0]); 690 | } 691 | } 692 | 693 | mesh.name = object.name; 694 | 695 | container.add(mesh); 696 | } 697 | 698 | console.timeEnd("OBJLoader"); 699 | 700 | return container; 701 | }, 702 | }); 703 | 704 | return OBJLoader; 705 | })(); 706 | 707 | export { OBJLoader }; 708 | -------------------------------------------------------------------------------- /src/lib/OrbitControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author qiao / https://github.com/qiao 3 | * @author mrdoob / http://mrdoob.com 4 | * @author alteredq / http://alteredqualia.com/ 5 | * @author WestLangley / http://github.com/WestLangley 6 | * @author erich666 / http://erichaines.com 7 | * @author ScieCode / http://github.com/sciecode 8 | */ 9 | 10 | // import { 11 | // EventDispatcher, 12 | // MOUSE, 13 | // Quaternion, 14 | // Spherical, 15 | // TOUCH, 16 | // Vector2, 17 | // Vector3, 18 | // } from "./three.module.js"; 19 | import { 20 | EventDispatcher, 21 | MOUSE, 22 | Quaternion, 23 | Spherical, 24 | TOUCH, 25 | Vector2, 26 | Vector3, 27 | } from "three"; 28 | 29 | // This set of controls performs orbiting, dollying (zooming), and panning. 30 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). 31 | // 32 | // Orbit - left mouse / touch: one-finger move 33 | // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish 34 | // Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move 35 | 36 | var OrbitControls = function (object, domElement) { 37 | if (domElement === undefined) 38 | console.warn( 39 | 'THREE.OrbitControls: The second parameter "domElement" is now mandatory.' 40 | ); 41 | if (domElement === document) 42 | console.error( 43 | 'THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' 44 | ); 45 | 46 | this.object = object; 47 | this.domElement = domElement; 48 | 49 | // Set to false to disable this control 50 | this.enabled = true; 51 | 52 | // "target" sets the location of focus, where the object orbits around 53 | this.target = new Vector3(); 54 | 55 | // How far you can dolly in and out ( PerspectiveCamera only ) 56 | this.minDistance = 0; 57 | this.maxDistance = Infinity; 58 | 59 | // How far you can zoom in and out ( OrthographicCamera only ) 60 | this.minZoom = 0; 61 | this.maxZoom = Infinity; 62 | 63 | // How far you can orbit vertically, upper and lower limits. 64 | // Range is 0 to Math.PI radians. 65 | this.minPolarAngle = 0; // radians 66 | this.maxPolarAngle = Math.PI; // radians 67 | 68 | // How far you can orbit horizontally, upper and lower limits. 69 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. 70 | this.minAzimuthAngle = -Infinity; // radians 71 | this.maxAzimuthAngle = Infinity; // radians 72 | 73 | // Set to true to enable damping (inertia) 74 | // If damping is enabled, you must call controls.update() in your animation loop 75 | this.enableDamping = false; 76 | this.dampingFactor = 0.05; 77 | 78 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. 79 | // Set to false to disable zooming 80 | this.enableZoom = true; 81 | this.zoomSpeed = 1.0; 82 | 83 | // Set to false to disable rotating 84 | this.enableRotate = true; 85 | this.rotateSpeed = 1.0; 86 | 87 | // Set to false to disable panning 88 | this.enablePan = true; 89 | this.panSpeed = 1.0; 90 | this.screenSpacePanning = false; // if true, pan in screen-space 91 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 92 | 93 | // Set to true to automatically rotate around the target 94 | // If auto-rotate is enabled, you must call controls.update() in your animation loop 95 | this.autoRotate = false; 96 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 97 | 98 | // Set to false to disable use of the keys 99 | this.enableKeys = true; 100 | 101 | // The four arrow keys 102 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 103 | 104 | // Mouse buttons 105 | this.mouseButtons = { 106 | LEFT: MOUSE.ROTATE, 107 | MIDDLE: MOUSE.DOLLY, 108 | RIGHT: MOUSE.PAN, 109 | }; 110 | 111 | // Touch fingers 112 | this.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN }; 113 | 114 | // for reset 115 | this.target0 = this.target.clone(); 116 | this.position0 = this.object.position.clone(); 117 | this.zoom0 = this.object.zoom; 118 | 119 | // 120 | // public methods 121 | // 122 | 123 | this.getPolarAngle = function () { 124 | return spherical.phi; 125 | }; 126 | 127 | this.getAzimuthalAngle = function () { 128 | return spherical.theta; 129 | }; 130 | 131 | this.saveState = function () { 132 | scope.target0.copy(scope.target); 133 | scope.position0.copy(scope.object.position); 134 | scope.zoom0 = scope.object.zoom; 135 | }; 136 | 137 | this.reset = function () { 138 | scope.target.copy(scope.target0); 139 | scope.object.position.copy(scope.position0); 140 | scope.object.zoom = scope.zoom0; 141 | 142 | scope.object.updateProjectionMatrix(); 143 | scope.dispatchEvent(changeEvent); 144 | 145 | scope.update(); 146 | 147 | state = STATE.NONE; 148 | }; 149 | 150 | // this method is exposed, but perhaps it would be better if we can make it private... 151 | this.update = (function () { 152 | var offset = new Vector3(); 153 | 154 | // so camera.up is the orbit axis 155 | var quat = new Quaternion().setFromUnitVectors( 156 | object.up, 157 | new Vector3(0, 1, 0) 158 | ); 159 | var quatInverse = quat.clone().inverse(); 160 | 161 | var lastPosition = new Vector3(); 162 | var lastQuaternion = new Quaternion(); 163 | 164 | return function update() { 165 | var position = scope.object.position; 166 | 167 | offset.copy(position).sub(scope.target); 168 | 169 | // rotate offset to "y-axis-is-up" space 170 | offset.applyQuaternion(quat); 171 | 172 | // angle from z-axis around y-axis 173 | spherical.setFromVector3(offset); 174 | 175 | if (scope.autoRotate && state === STATE.NONE) { 176 | rotateLeft(getAutoRotationAngle()); 177 | } 178 | 179 | if (scope.enableDamping) { 180 | spherical.theta += sphericalDelta.theta * scope.dampingFactor; 181 | spherical.phi += sphericalDelta.phi * scope.dampingFactor; 182 | } else { 183 | spherical.theta += sphericalDelta.theta; 184 | spherical.phi += sphericalDelta.phi; 185 | } 186 | 187 | // restrict theta to be between desired limits 188 | spherical.theta = Math.max( 189 | scope.minAzimuthAngle, 190 | Math.min(scope.maxAzimuthAngle, spherical.theta) 191 | ); 192 | 193 | // restrict phi to be between desired limits 194 | spherical.phi = Math.max( 195 | scope.minPolarAngle, 196 | Math.min(scope.maxPolarAngle, spherical.phi) 197 | ); 198 | 199 | spherical.makeSafe(); 200 | 201 | spherical.radius *= scale; 202 | 203 | // restrict radius to be between desired limits 204 | spherical.radius = Math.max( 205 | scope.minDistance, 206 | Math.min(scope.maxDistance, spherical.radius) 207 | ); 208 | 209 | // move target to panned location 210 | 211 | if (scope.enableDamping === true) { 212 | scope.target.addScaledVector(panOffset, scope.dampingFactor); 213 | } else { 214 | scope.target.add(panOffset); 215 | } 216 | 217 | offset.setFromSpherical(spherical); 218 | 219 | // rotate offset back to "camera-up-vector-is-up" space 220 | offset.applyQuaternion(quatInverse); 221 | 222 | position.copy(scope.target).add(offset); 223 | 224 | scope.object.lookAt(scope.target); 225 | 226 | if (scope.enableDamping === true) { 227 | sphericalDelta.theta *= 1 - scope.dampingFactor; 228 | sphericalDelta.phi *= 1 - scope.dampingFactor; 229 | 230 | panOffset.multiplyScalar(1 - scope.dampingFactor); 231 | } else { 232 | sphericalDelta.set(0, 0, 0); 233 | 234 | panOffset.set(0, 0, 0); 235 | } 236 | 237 | scale = 1; 238 | 239 | // update condition is: 240 | // min(camera displacement, camera rotation in radians)^2 > EPS 241 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8 242 | 243 | if ( 244 | zoomChanged || 245 | lastPosition.distanceToSquared(scope.object.position) > EPS || 246 | 8 * (1 - lastQuaternion.dot(scope.object.quaternion)) > EPS 247 | ) { 248 | scope.dispatchEvent(changeEvent); 249 | 250 | lastPosition.copy(scope.object.position); 251 | lastQuaternion.copy(scope.object.quaternion); 252 | zoomChanged = false; 253 | 254 | return true; 255 | } 256 | 257 | return false; 258 | }; 259 | })(); 260 | 261 | this.dispose = function () { 262 | scope.domElement.removeEventListener("contextmenu", onContextMenu, false); 263 | scope.domElement.removeEventListener("mousedown", onMouseDown, false); 264 | scope.domElement.removeEventListener("wheel", onMouseWheel, false); 265 | 266 | scope.domElement.removeEventListener("touchstart", onTouchStart, false); 267 | scope.domElement.removeEventListener("touchend", onTouchEnd, false); 268 | scope.domElement.removeEventListener("touchmove", onTouchMove, false); 269 | 270 | document.removeEventListener("mousemove", onMouseMove, false); 271 | document.removeEventListener("mouseup", onMouseUp, false); 272 | 273 | scope.domElement.removeEventListener("keydown", onKeyDown, false); 274 | 275 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? 276 | }; 277 | 278 | // 279 | // internals 280 | // 281 | 282 | var scope = this; 283 | 284 | var changeEvent = { type: "change" }; 285 | var startEvent = { type: "start" }; 286 | var endEvent = { type: "end" }; 287 | 288 | var STATE = { 289 | NONE: -1, 290 | ROTATE: 0, 291 | DOLLY: 1, 292 | PAN: 2, 293 | TOUCH_ROTATE: 3, 294 | TOUCH_PAN: 4, 295 | TOUCH_DOLLY_PAN: 5, 296 | TOUCH_DOLLY_ROTATE: 6, 297 | }; 298 | 299 | var state = STATE.NONE; 300 | 301 | var EPS = 0.000001; 302 | 303 | // current position in spherical coordinates 304 | var spherical = new Spherical(); 305 | var sphericalDelta = new Spherical(); 306 | 307 | var scale = 1; 308 | var panOffset = new Vector3(); 309 | var zoomChanged = false; 310 | 311 | var rotateStart = new Vector2(); 312 | var rotateEnd = new Vector2(); 313 | var rotateDelta = new Vector2(); 314 | 315 | var panStart = new Vector2(); 316 | var panEnd = new Vector2(); 317 | var panDelta = new Vector2(); 318 | 319 | var dollyStart = new Vector2(); 320 | var dollyEnd = new Vector2(); 321 | var dollyDelta = new Vector2(); 322 | 323 | function getAutoRotationAngle() { 324 | return ((2 * Math.PI) / 60 / 60) * scope.autoRotateSpeed; 325 | } 326 | 327 | function getZoomScale() { 328 | return Math.pow(0.95, scope.zoomSpeed); 329 | } 330 | 331 | function rotateLeft(angle) { 332 | sphericalDelta.theta -= angle; 333 | } 334 | 335 | function rotateUp(angle) { 336 | sphericalDelta.phi -= angle; 337 | } 338 | 339 | var panLeft = (function () { 340 | var v = new Vector3(); 341 | 342 | return function panLeft(distance, objectMatrix) { 343 | v.setFromMatrixColumn(objectMatrix, 0); // get X column of objectMatrix 344 | v.multiplyScalar(-distance); 345 | 346 | panOffset.add(v); 347 | }; 348 | })(); 349 | 350 | var panUp = (function () { 351 | var v = new Vector3(); 352 | 353 | return function panUp(distance, objectMatrix) { 354 | if (scope.screenSpacePanning === true) { 355 | v.setFromMatrixColumn(objectMatrix, 1); 356 | } else { 357 | v.setFromMatrixColumn(objectMatrix, 0); 358 | v.crossVectors(scope.object.up, v); 359 | } 360 | 361 | v.multiplyScalar(distance); 362 | 363 | panOffset.add(v); 364 | }; 365 | })(); 366 | 367 | // deltaX and deltaY are in pixels; right and down are positive 368 | var pan = (function () { 369 | var offset = new Vector3(); 370 | 371 | return function pan(deltaX, deltaY) { 372 | var element = scope.domElement; 373 | 374 | if (scope.object.isPerspectiveCamera) { 375 | // perspective 376 | var position = scope.object.position; 377 | offset.copy(position).sub(scope.target); 378 | var targetDistance = offset.length(); 379 | 380 | // half of the fov is center to top of screen 381 | targetDistance *= Math.tan(((scope.object.fov / 2) * Math.PI) / 180.0); 382 | 383 | // we use only clientHeight here so aspect ratio does not distort speed 384 | panLeft( 385 | (2 * deltaX * targetDistance) / element.clientHeight, 386 | scope.object.matrix 387 | ); 388 | panUp( 389 | (2 * deltaY * targetDistance) / element.clientHeight, 390 | scope.object.matrix 391 | ); 392 | } else if (scope.object.isOrthographicCamera) { 393 | // orthographic 394 | panLeft( 395 | (deltaX * (scope.object.right - scope.object.left)) / 396 | scope.object.zoom / 397 | element.clientWidth, 398 | scope.object.matrix 399 | ); 400 | panUp( 401 | (deltaY * (scope.object.top - scope.object.bottom)) / 402 | scope.object.zoom / 403 | element.clientHeight, 404 | scope.object.matrix 405 | ); 406 | } else { 407 | // camera neither orthographic nor perspective 408 | console.warn( 409 | "WARNING: OrbitControls.js encountered an unknown camera type - pan disabled." 410 | ); 411 | scope.enablePan = false; 412 | } 413 | }; 414 | })(); 415 | 416 | function dollyOut(dollyScale) { 417 | if (scope.object.isPerspectiveCamera) { 418 | scale /= dollyScale; 419 | } else if (scope.object.isOrthographicCamera) { 420 | scope.object.zoom = Math.max( 421 | scope.minZoom, 422 | Math.min(scope.maxZoom, scope.object.zoom * dollyScale) 423 | ); 424 | scope.object.updateProjectionMatrix(); 425 | zoomChanged = true; 426 | } else { 427 | console.warn( 428 | "WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled." 429 | ); 430 | scope.enableZoom = false; 431 | } 432 | } 433 | 434 | function dollyIn(dollyScale) { 435 | if (scope.object.isPerspectiveCamera) { 436 | scale *= dollyScale; 437 | } else if (scope.object.isOrthographicCamera) { 438 | scope.object.zoom = Math.max( 439 | scope.minZoom, 440 | Math.min(scope.maxZoom, scope.object.zoom / dollyScale) 441 | ); 442 | scope.object.updateProjectionMatrix(); 443 | zoomChanged = true; 444 | } else { 445 | console.warn( 446 | "WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled." 447 | ); 448 | scope.enableZoom = false; 449 | } 450 | } 451 | 452 | // 453 | // event callbacks - update the object state 454 | // 455 | 456 | function handleMouseDownRotate(event) { 457 | rotateStart.set(event.clientX, event.clientY); 458 | } 459 | 460 | function handleMouseDownDolly(event) { 461 | dollyStart.set(event.clientX, event.clientY); 462 | } 463 | 464 | function handleMouseDownPan(event) { 465 | panStart.set(event.clientX, event.clientY); 466 | } 467 | 468 | function handleMouseMoveRotate(event) { 469 | rotateEnd.set(event.clientX, event.clientY); 470 | 471 | rotateDelta 472 | .subVectors(rotateEnd, rotateStart) 473 | .multiplyScalar(scope.rotateSpeed); 474 | 475 | var element = scope.domElement; 476 | 477 | rotateLeft((2 * Math.PI * rotateDelta.x) / element.clientHeight); // yes, height 478 | 479 | rotateUp((2 * Math.PI * rotateDelta.y) / element.clientHeight); 480 | 481 | rotateStart.copy(rotateEnd); 482 | 483 | scope.update(); 484 | } 485 | 486 | function handleMouseMoveDolly(event) { 487 | dollyEnd.set(event.clientX, event.clientY); 488 | 489 | dollyDelta.subVectors(dollyEnd, dollyStart); 490 | 491 | if (dollyDelta.y > 0) { 492 | dollyOut(getZoomScale()); 493 | } else if (dollyDelta.y < 0) { 494 | dollyIn(getZoomScale()); 495 | } 496 | 497 | dollyStart.copy(dollyEnd); 498 | 499 | scope.update(); 500 | } 501 | 502 | function handleMouseMovePan(event) { 503 | panEnd.set(event.clientX, event.clientY); 504 | 505 | panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed); 506 | 507 | pan(panDelta.x, panDelta.y); 508 | 509 | panStart.copy(panEnd); 510 | 511 | scope.update(); 512 | } 513 | 514 | function handleMouseUp(/*event*/) { 515 | // no-op 516 | } 517 | 518 | function handleMouseWheel(event) { 519 | if (event.deltaY < 0) { 520 | dollyIn(getZoomScale()); 521 | } else if (event.deltaY > 0) { 522 | dollyOut(getZoomScale()); 523 | } 524 | 525 | scope.update(); 526 | } 527 | 528 | function handleKeyDown(event) { 529 | var needsUpdate = false; 530 | 531 | switch (event.keyCode) { 532 | case scope.keys.UP: 533 | pan(0, scope.keyPanSpeed); 534 | needsUpdate = true; 535 | break; 536 | 537 | case scope.keys.BOTTOM: 538 | pan(0, -scope.keyPanSpeed); 539 | needsUpdate = true; 540 | break; 541 | 542 | case scope.keys.LEFT: 543 | pan(scope.keyPanSpeed, 0); 544 | needsUpdate = true; 545 | break; 546 | 547 | case scope.keys.RIGHT: 548 | pan(-scope.keyPanSpeed, 0); 549 | needsUpdate = true; 550 | break; 551 | } 552 | 553 | if (needsUpdate) { 554 | // prevent the browser from scrolling on cursor keys 555 | event.preventDefault(); 556 | 557 | scope.update(); 558 | } 559 | } 560 | 561 | function handleTouchStartRotate(event) { 562 | if (event.touches.length == 1) { 563 | rotateStart.set(event.touches[0].pageX, event.touches[0].pageY); 564 | } else { 565 | var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX); 566 | var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY); 567 | 568 | rotateStart.set(x, y); 569 | } 570 | } 571 | 572 | function handleTouchStartPan(event) { 573 | if (event.touches.length == 1) { 574 | panStart.set(event.touches[0].pageX, event.touches[0].pageY); 575 | } else { 576 | var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX); 577 | var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY); 578 | 579 | panStart.set(x, y); 580 | } 581 | } 582 | 583 | function handleTouchStartDolly(event) { 584 | var dx = event.touches[0].pageX - event.touches[1].pageX; 585 | var dy = event.touches[0].pageY - event.touches[1].pageY; 586 | 587 | var distance = Math.sqrt(dx * dx + dy * dy); 588 | 589 | dollyStart.set(0, distance); 590 | } 591 | 592 | function handleTouchStartDollyPan(event) { 593 | if (scope.enableZoom) handleTouchStartDolly(event); 594 | 595 | if (scope.enablePan) handleTouchStartPan(event); 596 | } 597 | 598 | function handleTouchStartDollyRotate(event) { 599 | if (scope.enableZoom) handleTouchStartDolly(event); 600 | 601 | if (scope.enableRotate) handleTouchStartRotate(event); 602 | } 603 | 604 | function handleTouchMoveRotate(event) { 605 | if (event.touches.length == 1) { 606 | rotateEnd.set(event.touches[0].pageX, event.touches[0].pageY); 607 | } else { 608 | var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX); 609 | var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY); 610 | 611 | rotateEnd.set(x, y); 612 | } 613 | 614 | rotateDelta 615 | .subVectors(rotateEnd, rotateStart) 616 | .multiplyScalar(scope.rotateSpeed); 617 | 618 | var element = scope.domElement; 619 | 620 | rotateLeft((2 * Math.PI * rotateDelta.x) / element.clientHeight); // yes, height 621 | 622 | rotateUp((2 * Math.PI * rotateDelta.y) / element.clientHeight); 623 | 624 | rotateStart.copy(rotateEnd); 625 | } 626 | 627 | function handleTouchMovePan(event) { 628 | if (event.touches.length == 1) { 629 | panEnd.set(event.touches[0].pageX, event.touches[0].pageY); 630 | } else { 631 | var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX); 632 | var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY); 633 | 634 | panEnd.set(x, y); 635 | } 636 | 637 | panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed); 638 | 639 | pan(panDelta.x, panDelta.y); 640 | 641 | panStart.copy(panEnd); 642 | } 643 | 644 | function handleTouchMoveDolly(event) { 645 | var dx = event.touches[0].pageX - event.touches[1].pageX; 646 | var dy = event.touches[0].pageY - event.touches[1].pageY; 647 | 648 | var distance = Math.sqrt(dx * dx + dy * dy); 649 | 650 | dollyEnd.set(0, distance); 651 | 652 | dollyDelta.set(0, Math.pow(dollyEnd.y / dollyStart.y, scope.zoomSpeed)); 653 | 654 | dollyOut(dollyDelta.y); 655 | 656 | dollyStart.copy(dollyEnd); 657 | } 658 | 659 | function handleTouchMoveDollyPan(event) { 660 | if (scope.enableZoom) handleTouchMoveDolly(event); 661 | 662 | if (scope.enablePan) handleTouchMovePan(event); 663 | } 664 | 665 | function handleTouchMoveDollyRotate(event) { 666 | if (scope.enableZoom) handleTouchMoveDolly(event); 667 | 668 | if (scope.enableRotate) handleTouchMoveRotate(event); 669 | } 670 | 671 | function handleTouchEnd(/*event*/) { 672 | // no-op 673 | } 674 | 675 | // 676 | // event handlers - FSM: listen for events and reset state 677 | // 678 | 679 | function onMouseDown(event) { 680 | if (scope.enabled === false) return; 681 | 682 | // Prevent the browser from scrolling. 683 | event.preventDefault(); 684 | 685 | // Manually set the focus since calling preventDefault above 686 | // prevents the browser from setting it automatically. 687 | 688 | scope.domElement.focus ? scope.domElement.focus() : window.focus(); 689 | 690 | var mouseAction; 691 | 692 | switch (event.button) { 693 | case 0: 694 | mouseAction = scope.mouseButtons.LEFT; 695 | break; 696 | 697 | case 1: 698 | mouseAction = scope.mouseButtons.MIDDLE; 699 | break; 700 | 701 | case 2: 702 | mouseAction = scope.mouseButtons.RIGHT; 703 | break; 704 | 705 | default: 706 | mouseAction = -1; 707 | } 708 | 709 | switch (mouseAction) { 710 | case MOUSE.DOLLY: 711 | if (scope.enableZoom === false) return; 712 | 713 | handleMouseDownDolly(event); 714 | 715 | state = STATE.DOLLY; 716 | 717 | break; 718 | 719 | case MOUSE.ROTATE: 720 | if (event.ctrlKey || event.metaKey || event.shiftKey) { 721 | if (scope.enablePan === false) return; 722 | 723 | handleMouseDownPan(event); 724 | 725 | state = STATE.PAN; 726 | } else { 727 | if (scope.enableRotate === false) return; 728 | 729 | handleMouseDownRotate(event); 730 | 731 | state = STATE.ROTATE; 732 | } 733 | 734 | break; 735 | 736 | case MOUSE.PAN: 737 | if (event.ctrlKey || event.metaKey || event.shiftKey) { 738 | if (scope.enableRotate === false) return; 739 | 740 | handleMouseDownRotate(event); 741 | 742 | state = STATE.ROTATE; 743 | } else { 744 | if (scope.enablePan === false) return; 745 | 746 | handleMouseDownPan(event); 747 | 748 | state = STATE.PAN; 749 | } 750 | 751 | break; 752 | 753 | default: 754 | state = STATE.NONE; 755 | } 756 | 757 | if (state !== STATE.NONE) { 758 | document.addEventListener("mousemove", onMouseMove, false); 759 | document.addEventListener("mouseup", onMouseUp, false); 760 | 761 | scope.dispatchEvent(startEvent); 762 | } 763 | } 764 | 765 | function onMouseMove(event) { 766 | if (scope.enabled === false) return; 767 | 768 | event.preventDefault(); 769 | 770 | switch (state) { 771 | case STATE.ROTATE: 772 | if (scope.enableRotate === false) return; 773 | 774 | handleMouseMoveRotate(event); 775 | 776 | break; 777 | 778 | case STATE.DOLLY: 779 | if (scope.enableZoom === false) return; 780 | 781 | handleMouseMoveDolly(event); 782 | 783 | break; 784 | 785 | case STATE.PAN: 786 | if (scope.enablePan === false) return; 787 | 788 | handleMouseMovePan(event); 789 | 790 | break; 791 | } 792 | } 793 | 794 | function onMouseUp(event) { 795 | if (scope.enabled === false) return; 796 | 797 | handleMouseUp(event); 798 | 799 | document.removeEventListener("mousemove", onMouseMove, false); 800 | document.removeEventListener("mouseup", onMouseUp, false); 801 | 802 | scope.dispatchEvent(endEvent); 803 | 804 | state = STATE.NONE; 805 | } 806 | 807 | function onMouseWheel(event) { 808 | if ( 809 | scope.enabled === false || 810 | scope.enableZoom === false || 811 | (state !== STATE.NONE && state !== STATE.ROTATE) 812 | ) 813 | return; 814 | 815 | event.preventDefault(); 816 | event.stopPropagation(); 817 | 818 | scope.dispatchEvent(startEvent); 819 | 820 | handleMouseWheel(event); 821 | 822 | scope.dispatchEvent(endEvent); 823 | } 824 | 825 | function onKeyDown(event) { 826 | if ( 827 | scope.enabled === false || 828 | scope.enableKeys === false || 829 | scope.enablePan === false 830 | ) 831 | return; 832 | 833 | handleKeyDown(event); 834 | } 835 | 836 | function onTouchStart(event) { 837 | if (scope.enabled === false) return; 838 | 839 | event.preventDefault(); // prevent scrolling 840 | 841 | switch (event.touches.length) { 842 | case 1: 843 | switch (scope.touches.ONE) { 844 | case TOUCH.ROTATE: 845 | if (scope.enableRotate === false) return; 846 | 847 | handleTouchStartRotate(event); 848 | 849 | state = STATE.TOUCH_ROTATE; 850 | 851 | break; 852 | 853 | case TOUCH.PAN: 854 | if (scope.enablePan === false) return; 855 | 856 | handleTouchStartPan(event); 857 | 858 | state = STATE.TOUCH_PAN; 859 | 860 | break; 861 | 862 | default: 863 | state = STATE.NONE; 864 | } 865 | 866 | break; 867 | 868 | case 2: 869 | switch (scope.touches.TWO) { 870 | case TOUCH.DOLLY_PAN: 871 | if (scope.enableZoom === false && scope.enablePan === false) return; 872 | 873 | handleTouchStartDollyPan(event); 874 | 875 | state = STATE.TOUCH_DOLLY_PAN; 876 | 877 | break; 878 | 879 | case TOUCH.DOLLY_ROTATE: 880 | if (scope.enableZoom === false && scope.enableRotate === false) 881 | return; 882 | 883 | handleTouchStartDollyRotate(event); 884 | 885 | state = STATE.TOUCH_DOLLY_ROTATE; 886 | 887 | break; 888 | 889 | default: 890 | state = STATE.NONE; 891 | } 892 | 893 | break; 894 | 895 | default: 896 | state = STATE.NONE; 897 | } 898 | 899 | if (state !== STATE.NONE) { 900 | scope.dispatchEvent(startEvent); 901 | } 902 | } 903 | 904 | function onTouchMove(event) { 905 | if (scope.enabled === false) return; 906 | 907 | event.preventDefault(); // prevent scrolling 908 | event.stopPropagation(); 909 | 910 | switch (state) { 911 | case STATE.TOUCH_ROTATE: 912 | if (scope.enableRotate === false) return; 913 | 914 | handleTouchMoveRotate(event); 915 | 916 | scope.update(); 917 | 918 | break; 919 | 920 | case STATE.TOUCH_PAN: 921 | if (scope.enablePan === false) return; 922 | 923 | handleTouchMovePan(event); 924 | 925 | scope.update(); 926 | 927 | break; 928 | 929 | case STATE.TOUCH_DOLLY_PAN: 930 | if (scope.enableZoom === false && scope.enablePan === false) return; 931 | 932 | handleTouchMoveDollyPan(event); 933 | 934 | scope.update(); 935 | 936 | break; 937 | 938 | case STATE.TOUCH_DOLLY_ROTATE: 939 | if (scope.enableZoom === false && scope.enableRotate === false) return; 940 | 941 | handleTouchMoveDollyRotate(event); 942 | 943 | scope.update(); 944 | 945 | break; 946 | 947 | default: 948 | state = STATE.NONE; 949 | } 950 | } 951 | 952 | function onTouchEnd(event) { 953 | if (scope.enabled === false) return; 954 | 955 | handleTouchEnd(event); 956 | 957 | scope.dispatchEvent(endEvent); 958 | 959 | state = STATE.NONE; 960 | } 961 | 962 | function onContextMenu(event) { 963 | if (scope.enabled === false) return; 964 | 965 | event.preventDefault(); 966 | } 967 | 968 | // 969 | 970 | scope.domElement.addEventListener("contextmenu", onContextMenu, false); 971 | 972 | scope.domElement.addEventListener("mousedown", onMouseDown, false); 973 | scope.domElement.addEventListener("wheel", onMouseWheel, false); 974 | 975 | scope.domElement.addEventListener("touchstart", onTouchStart, false); 976 | scope.domElement.addEventListener("touchend", onTouchEnd, false); 977 | scope.domElement.addEventListener("touchmove", onTouchMove, false); 978 | 979 | scope.domElement.addEventListener("keydown", onKeyDown, false); 980 | 981 | // make sure element can receive keys. 982 | 983 | if (scope.domElement.tabIndex === -1) { 984 | scope.domElement.tabIndex = 0; 985 | } 986 | 987 | // force an update at start 988 | 989 | this.update(); 990 | }; 991 | 992 | OrbitControls.prototype = Object.create(EventDispatcher.prototype); 993 | OrbitControls.prototype.constructor = OrbitControls; 994 | 995 | // This set of controls performs orbiting, dollying (zooming), and panning. 996 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). 997 | // This is very similar to OrbitControls, another set of touch behavior 998 | // 999 | // Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate 1000 | // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish 1001 | // Pan - left mouse, or arrow keys / touch: one-finger move 1002 | 1003 | var MapControls = function (object, domElement) { 1004 | OrbitControls.call(this, object, domElement); 1005 | 1006 | this.mouseButtons.LEFT = MOUSE.PAN; 1007 | this.mouseButtons.RIGHT = MOUSE.ROTATE; 1008 | 1009 | this.touches.ONE = TOUCH.PAN; 1010 | this.touches.TWO = TOUCH.DOLLY_ROTATE; 1011 | }; 1012 | 1013 | MapControls.prototype = Object.create(EventDispatcher.prototype); 1014 | MapControls.prototype.constructor = MapControls; 1015 | 1016 | export { OrbitControls, MapControls }; 1017 | -------------------------------------------------------------------------------- /src/lib/Pass.js: -------------------------------------------------------------------------------- 1 | import { 2 | OrthographicCamera, 3 | PlaneGeometry, 4 | Mesh 5 | } from "./three.module.js"; 6 | 7 | function Pass() { 8 | 9 | // if set to true, the pass is processed by the composer 10 | this.enabled = true; 11 | 12 | // if set to true, the pass indicates to swap read and write buffer after rendering 13 | this.needsSwap = true; 14 | 15 | // if set to true, the pass clears its buffer before rendering 16 | this.clear = false; 17 | 18 | // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer. 19 | this.renderToScreen = false; 20 | 21 | } 22 | 23 | Object.assign( Pass.prototype, { 24 | 25 | setSize: function ( /* width, height */ ) {}, 26 | 27 | render: function ( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */ ) { 28 | 29 | console.error( 'THREE.Pass: .render() must be implemented in derived pass.' ); 30 | 31 | } 32 | 33 | } ); 34 | 35 | // Helper for passes that need to fill the viewport with a single quad. 36 | 37 | // Important: It's actually a hack to put FullScreenQuad into the Pass namespace. This is only 38 | // done to make examples/js code work. Normally, FullScreenQuad should be exported 39 | // from this module like Pass. 40 | 41 | Pass.FullScreenQuad = ( function () { 42 | 43 | var camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); 44 | var geometry = new PlaneGeometry( 2, 2 ); 45 | 46 | var FullScreenQuad = function ( material ) { 47 | 48 | this._mesh = new Mesh( geometry, material ); 49 | 50 | }; 51 | 52 | Object.defineProperty( FullScreenQuad.prototype, 'material', { 53 | 54 | get: function () { 55 | 56 | return this._mesh.material; 57 | 58 | }, 59 | 60 | set: function ( value ) { 61 | 62 | this._mesh.material = value; 63 | 64 | } 65 | 66 | } ); 67 | 68 | Object.assign( FullScreenQuad.prototype, { 69 | 70 | dispose: function () { 71 | 72 | this._mesh.geometry.dispose(); 73 | 74 | }, 75 | 76 | render: function ( renderer ) { 77 | 78 | renderer.render( this._mesh, camera ); 79 | 80 | } 81 | 82 | } ); 83 | 84 | return FullScreenQuad; 85 | 86 | } )(); 87 | 88 | export { Pass }; 89 | -------------------------------------------------------------------------------- /src/lib/RenderPass.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author alteredq / http://alteredqualia.com/ 3 | */ 4 | 5 | import { Pass } from "./Pass.js"; 6 | 7 | var RenderPass = function ( 8 | scene, 9 | camera, 10 | overrideMaterial, 11 | clearColor, 12 | clearAlpha 13 | ) { 14 | Pass.call(this); 15 | 16 | this.scene = scene; 17 | this.camera = camera; 18 | 19 | this.overrideMaterial = overrideMaterial; 20 | 21 | this.clearColor = clearColor; 22 | this.clearAlpha = clearAlpha !== undefined ? clearAlpha : 0; 23 | 24 | this.clear = true; 25 | this.clearDepth = false; 26 | this.needsSwap = false; 27 | }; 28 | 29 | RenderPass.prototype = Object.assign(Object.create(Pass.prototype), { 30 | constructor: RenderPass, 31 | 32 | render: function ( 33 | renderer, 34 | writeBuffer, 35 | readBuffer /*, deltaTime, maskActive */ 36 | ) { 37 | var oldAutoClear = renderer.autoClear; 38 | renderer.autoClear = false; 39 | 40 | this.scene.overrideMaterial = this.overrideMaterial; 41 | 42 | var oldClearColor, oldClearAlpha; 43 | 44 | if (this.clearColor) { 45 | oldClearColor = renderer.getClearColor().getHex(); 46 | oldClearAlpha = renderer.getClearAlpha(); 47 | renderer.setClearColor(this.clearColor, this.clearAlpha); 48 | } 49 | 50 | if (this.clearDepth) { 51 | renderer.clearDepth(); 52 | } 53 | 54 | 55 | renderer.setRenderTarget(this.renderToScreen ? null : readBuffer); 56 | 57 | // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 58 | if (this.clear) 59 | renderer.clear( 60 | renderer.autoClearColor, 61 | renderer.autoClearDepth, 62 | renderer.autoClearStencil 63 | ); 64 | renderer.render(this.scene, this.camera); 65 | 66 | if (this.clearColor) { 67 | renderer.setClearColor(oldClearColor, oldClearAlpha); 68 | } 69 | 70 | this.scene.overrideMaterial = null; 71 | renderer.autoClear = oldAutoClear; 72 | }, 73 | }); 74 | 75 | export { RenderPass }; 76 | -------------------------------------------------------------------------------- /src/lib/ShaderPass.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author alteredq / http://alteredqualia.com/ 3 | */ 4 | 5 | // import { ShaderMaterial, UniformsUtils } from "./three.module.js"; 6 | import { ShaderMaterial, UniformsUtils } from "three"; 7 | import { Pass } from "./Pass.js"; 8 | 9 | var ShaderPass = function (shader, textureID) { 10 | Pass.call(this); 11 | 12 | this.textureID = textureID !== undefined ? textureID : "tDiffuse"; 13 | 14 | if (shader instanceof ShaderMaterial) { 15 | this.uniforms = shader.uniforms; 16 | 17 | this.material = shader; 18 | } else if (shader) { 19 | this.uniforms = UniformsUtils.clone(shader.uniforms); 20 | 21 | this.material = new ShaderMaterial({ 22 | defines: Object.assign({}, shader.defines), 23 | uniforms: this.uniforms, 24 | vertexShader: shader.vertexShader, 25 | fragmentShader: shader.fragmentShader, 26 | }); 27 | } 28 | 29 | this.fsQuad = new Pass.FullScreenQuad(this.material); 30 | }; 31 | 32 | ShaderPass.prototype = Object.assign(Object.create(Pass.prototype), { 33 | constructor: ShaderPass, 34 | 35 | render: function ( 36 | renderer, 37 | writeBuffer, 38 | readBuffer /*, deltaTime, maskActive */ 39 | ) { 40 | if (this.uniforms[this.textureID]) { 41 | this.uniforms[this.textureID].value = readBuffer.texture; 42 | } 43 | 44 | this.fsQuad.material = this.material; 45 | 46 | if (this.renderToScreen) { 47 | renderer.setRenderTarget(null); 48 | this.fsQuad.render(renderer); 49 | } else { 50 | renderer.setRenderTarget(writeBuffer); 51 | // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 52 | if (this.clear) 53 | renderer.clear( 54 | renderer.autoClearColor, 55 | renderer.autoClearDepth, 56 | renderer.autoClearStencil 57 | ); 58 | this.fsQuad.render(renderer); 59 | } 60 | }, 61 | }); 62 | 63 | export { ShaderPass }; 64 | -------------------------------------------------------------------------------- /src/lib/UnrealBloomPass.js: -------------------------------------------------------------------------------- 1 | import { 2 | AdditiveBlending, 3 | Color, 4 | LinearFilter, 5 | MeshBasicMaterial, 6 | RGBAFormat, 7 | ShaderMaterial, 8 | UniformsUtils, 9 | Vector2, 10 | Vector3, 11 | WebGLRenderTarget 12 | } from "./three.module.js"; 13 | import { Pass } from './Pass.js'; 14 | import { CopyShader } from './CopyShader.js'; 15 | import { LuminosityHighPassShader } from './LuminosityHighPassShader.js'; 16 | 17 | /** 18 | * UnrealBloomPass is inspired by the bloom pass of Unreal Engine. It creates a 19 | * mip map chain of bloom textures and blurs them with different radii. Because 20 | * of the weighted combination of mips, and because larger blurs are done on 21 | * higher mips, this effect provides good quality and performance. 22 | * 23 | * Reference: 24 | * - https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/ 25 | */ 26 | var UnrealBloomPass = function (resolution, strength, radius, threshold) { 27 | 28 | Pass.call(this); 29 | 30 | this.strength = (strength !== undefined) ? strength : 1; 31 | this.radius = radius; 32 | this.threshold = threshold; 33 | this.resolution = (resolution !== undefined) ? new Vector2(resolution.x, resolution.y) : new Vector2(256, 256); 34 | 35 | // create color only once here, reuse it later inside the render function 36 | this.clearColor = new Color(0, 0, 0); 37 | 38 | // render targets 39 | var pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; 40 | this.renderTargetsHorizontal = []; 41 | this.renderTargetsVertical = []; 42 | this.nMips = 5; 43 | var resx = Math.round(this.resolution.x / 2); 44 | var resy = Math.round(this.resolution.y / 2); 45 | 46 | this.renderTargetBright = new WebGLRenderTarget(resx, resy, pars); 47 | this.renderTargetBright.texture.name = "UnrealBloomPass.bright"; 48 | this.renderTargetBright.texture.generateMipmaps = false; 49 | 50 | for (var i = 0; i < this.nMips; i++) { 51 | 52 | var renderTargetHorizonal = new WebGLRenderTarget(resx, resy, pars); 53 | 54 | renderTargetHorizonal.texture.name = "UnrealBloomPass.h" + i; 55 | renderTargetHorizonal.texture.generateMipmaps = false; 56 | 57 | this.renderTargetsHorizontal.push(renderTargetHorizonal); 58 | 59 | var renderTargetVertical = new WebGLRenderTarget(resx, resy, pars); 60 | 61 | renderTargetVertical.texture.name = "UnrealBloomPass.v" + i; 62 | renderTargetVertical.texture.generateMipmaps = false; 63 | 64 | this.renderTargetsVertical.push(renderTargetVertical); 65 | 66 | resx = Math.round(resx / 2); 67 | 68 | resy = Math.round(resy / 2); 69 | 70 | } 71 | 72 | // luminosity high pass material 73 | 74 | if (LuminosityHighPassShader === undefined) 75 | console.error("UnrealBloomPass relies on LuminosityHighPassShader"); 76 | 77 | var highPassShader = LuminosityHighPassShader; 78 | this.highPassUniforms = UniformsUtils.clone(highPassShader.uniforms); 79 | 80 | this.highPassUniforms["luminosityThreshold"].value = threshold; 81 | this.highPassUniforms["smoothWidth"].value = 0.01; 82 | 83 | this.materialHighPassFilter = new ShaderMaterial({ 84 | uniforms: this.highPassUniforms, 85 | vertexShader: highPassShader.vertexShader, 86 | fragmentShader: highPassShader.fragmentShader, 87 | defines: {} 88 | }); 89 | 90 | // Gaussian Blur Materials 91 | this.separableBlurMaterials = []; 92 | var kernelSizeArray = [3, 5, 7, 9, 11]; 93 | var resx = Math.round(this.resolution.x / 2); 94 | var resy = Math.round(this.resolution.y / 2); 95 | 96 | for (var i = 0; i < this.nMips; i++) { 97 | 98 | this.separableBlurMaterials.push(this.getSeperableBlurMaterial(kernelSizeArray[i])); 99 | 100 | this.separableBlurMaterials[i].uniforms["texSize"].value = new Vector2(resx, resy); 101 | 102 | resx = Math.round(resx / 2); 103 | 104 | resy = Math.round(resy / 2); 105 | 106 | } 107 | 108 | // Composite material 109 | this.compositeMaterial = this.getCompositeMaterial(this.nMips); 110 | this.compositeMaterial.uniforms["blurTexture1"].value = this.renderTargetsVertical[0].texture; 111 | this.compositeMaterial.uniforms["blurTexture2"].value = this.renderTargetsVertical[1].texture; 112 | this.compositeMaterial.uniforms["blurTexture3"].value = this.renderTargetsVertical[2].texture; 113 | this.compositeMaterial.uniforms["blurTexture4"].value = this.renderTargetsVertical[3].texture; 114 | this.compositeMaterial.uniforms["blurTexture5"].value = this.renderTargetsVertical[4].texture; 115 | this.compositeMaterial.uniforms["bloomStrength"].value = strength; 116 | this.compositeMaterial.uniforms["bloomRadius"].value = 0.1; 117 | this.compositeMaterial.needsUpdate = true; 118 | 119 | var bloomFactors = [1.0, 0.8, 0.6, 0.4, 0.2]; 120 | this.compositeMaterial.uniforms["bloomFactors"].value = bloomFactors; 121 | this.bloomTintColors = [new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, 1), 122 | new Vector3(1, 1, 1), new Vector3(1, 1, 1)]; 123 | this.compositeMaterial.uniforms["bloomTintColors"].value = this.bloomTintColors; 124 | 125 | // copy material 126 | if (CopyShader === undefined) { 127 | 128 | console.error("UnrealBloomPass relies on CopyShader"); 129 | 130 | } 131 | 132 | var copyShader = CopyShader; 133 | 134 | this.copyUniforms = UniformsUtils.clone(copyShader.uniforms); 135 | this.copyUniforms["opacity"].value = 1.0; 136 | 137 | this.materialCopy = new ShaderMaterial({ 138 | uniforms: this.copyUniforms, 139 | vertexShader: copyShader.vertexShader, 140 | fragmentShader: copyShader.fragmentShader, 141 | blending: AdditiveBlending, 142 | depthTest: false, 143 | depthWrite: false, 144 | transparent: true 145 | }); 146 | 147 | this.enabled = true; 148 | this.needsSwap = false; 149 | 150 | this.oldClearColor = new Color(); 151 | this.oldClearAlpha = 1; 152 | 153 | this.basic = new MeshBasicMaterial(); 154 | 155 | this.fsQuad = new Pass.FullScreenQuad(null); 156 | 157 | }; 158 | 159 | UnrealBloomPass.prototype = Object.assign(Object.create(Pass.prototype), { 160 | 161 | constructor: UnrealBloomPass, 162 | 163 | dispose: function () { 164 | 165 | for (var i = 0; i < this.renderTargetsHorizontal.length; i++) { 166 | 167 | this.renderTargetsHorizontal[i].dispose(); 168 | 169 | } 170 | 171 | for (var i = 0; i < this.renderTargetsVertical.length; i++) { 172 | 173 | this.renderTargetsVertical[i].dispose(); 174 | 175 | } 176 | 177 | this.renderTargetBright.dispose(); 178 | 179 | }, 180 | 181 | setSize: function (width, height) { 182 | 183 | var resx = Math.round(width / 2); 184 | var resy = Math.round(height / 2); 185 | 186 | this.renderTargetBright.setSize(resx, resy); 187 | 188 | for (var i = 0; i < this.nMips; i++) { 189 | 190 | this.renderTargetsHorizontal[i].setSize(resx, resy); 191 | this.renderTargetsVertical[i].setSize(resx, resy); 192 | 193 | this.separableBlurMaterials[i].uniforms["texSize"].value = new Vector2(resx, resy); 194 | 195 | resx = Math.round(resx / 2); 196 | resy = Math.round(resy / 2); 197 | 198 | } 199 | 200 | }, 201 | 202 | render: function (renderer, writeBuffer, readBuffer, deltaTime, maskActive) { 203 | 204 | this.oldClearColor.copy(renderer.getClearColor()); 205 | this.oldClearAlpha = renderer.getClearAlpha(); 206 | var oldAutoClear = renderer.autoClear; 207 | renderer.autoClear = false; 208 | 209 | renderer.setClearColor(this.clearColor, 0); 210 | 211 | if (maskActive) renderer.state.buffers.stencil.setTest(false); 212 | 213 | // Render input to screen 214 | 215 | if (this.renderToScreen) { 216 | 217 | this.fsQuad.material = this.basic; 218 | this.basic.map = readBuffer.texture; 219 | 220 | renderer.setRenderTarget(null); 221 | renderer.clear(); 222 | this.fsQuad.render(renderer); 223 | 224 | } 225 | 226 | // 1. Extract Bright Areas 227 | 228 | this.highPassUniforms["tDiffuse"].value = readBuffer.texture; 229 | this.highPassUniforms["luminosityThreshold"].value = this.threshold; 230 | this.fsQuad.material = this.materialHighPassFilter; 231 | 232 | renderer.setRenderTarget(this.renderTargetBright); 233 | renderer.clear(); 234 | this.fsQuad.render(renderer); 235 | 236 | // 2. Blur All the mips progressively 237 | 238 | var inputRenderTarget = this.renderTargetBright; 239 | 240 | for (var i = 0; i < this.nMips; i++) { 241 | 242 | this.fsQuad.material = this.separableBlurMaterials[i]; 243 | 244 | this.separableBlurMaterials[i].uniforms["colorTexture"].value = inputRenderTarget.texture; 245 | this.separableBlurMaterials[i].uniforms["direction"].value = UnrealBloomPass.BlurDirectionX; 246 | renderer.setRenderTarget(this.renderTargetsHorizontal[i]); 247 | renderer.clear(); 248 | this.fsQuad.render(renderer); 249 | 250 | this.separableBlurMaterials[i].uniforms["colorTexture"].value = this.renderTargetsHorizontal[i].texture; 251 | this.separableBlurMaterials[i].uniforms["direction"].value = UnrealBloomPass.BlurDirectionY; 252 | renderer.setRenderTarget(this.renderTargetsVertical[i]); 253 | renderer.clear(); 254 | this.fsQuad.render(renderer); 255 | 256 | inputRenderTarget = this.renderTargetsVertical[i]; 257 | 258 | } 259 | 260 | // Composite All the mips 261 | 262 | this.fsQuad.material = this.compositeMaterial; 263 | this.compositeMaterial.uniforms["bloomStrength"].value = this.strength; 264 | this.compositeMaterial.uniforms["bloomRadius"].value = this.radius; 265 | this.compositeMaterial.uniforms["bloomTintColors"].value = this.bloomTintColors; 266 | 267 | renderer.setRenderTarget(this.renderTargetsHorizontal[0]); 268 | renderer.clear(); 269 | this.fsQuad.render(renderer); 270 | 271 | // Blend it additively over the input texture 272 | 273 | this.fsQuad.material = this.materialCopy; 274 | this.copyUniforms["tDiffuse"].value = this.renderTargetsHorizontal[0].texture; 275 | 276 | if (maskActive) renderer.state.buffers.stencil.setTest(true); 277 | 278 | if (this.renderToScreen) { 279 | 280 | renderer.setRenderTarget(null); 281 | this.fsQuad.render(renderer); 282 | 283 | } else { 284 | 285 | renderer.setRenderTarget(readBuffer); 286 | this.fsQuad.render(renderer); 287 | 288 | } 289 | 290 | // Restore renderer settings 291 | 292 | renderer.setClearColor(this.oldClearColor, this.oldClearAlpha); 293 | renderer.autoClear = oldAutoClear; 294 | 295 | }, 296 | 297 | getSeperableBlurMaterial: function (kernelRadius) { 298 | 299 | return new ShaderMaterial({ 300 | 301 | defines: { 302 | "KERNEL_RADIUS": kernelRadius, 303 | "SIGMA": kernelRadius 304 | }, 305 | 306 | uniforms: { 307 | "colorTexture": { value: null }, 308 | "texSize": { value: new Vector2(0.5, 0.5) }, 309 | "direction": { value: new Vector2(0.5, 0.5) } 310 | }, 311 | 312 | vertexShader: 313 | "varying vec2 vUv;\n\ 314 | void main() {\n\ 315 | vUv = uv;\n\ 316 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\ 317 | }", 318 | 319 | fragmentShader: 320 | "#include \ 321 | varying vec2 vUv;\n\ 322 | uniform sampler2D colorTexture;\n\ 323 | uniform vec2 texSize;\ 324 | uniform vec2 direction;\ 325 | \ 326 | float gaussianPdf(in float x, in float sigma) {\ 327 | return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;\ 328 | }\ 329 | void main() {\n\ 330 | vec2 invSize = 1.0 / texSize;\ 331 | float fSigma = float(SIGMA);\ 332 | float weightSum = gaussianPdf(0.0, fSigma);\ 333 | float alphaSum = 0.0;\ 334 | vec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum;\ 335 | for( int i = 1; i < KERNEL_RADIUS; i ++ ) {\ 336 | float x = float(i);\ 337 | float w = gaussianPdf(x, fSigma);\ 338 | vec2 uvOffset = direction * invSize * x;\ 339 | vec4 sample1 = texture2D( colorTexture, vUv + uvOffset);\ 340 | vec4 sample2 = texture2D( colorTexture, vUv - uvOffset);\ 341 | diffuseSum += (sample1.rgb + sample2.rgb) * w;\ 342 | alphaSum += (sample1.a + sample2.a) * w;\ 343 | weightSum += 2.0 * w;\ 344 | }\ 345 | gl_FragColor = vec4(diffuseSum/weightSum, alphaSum/weightSum);\n\ 346 | }" 347 | }); 348 | 349 | }, 350 | 351 | getCompositeMaterial: function (nMips) { 352 | 353 | return new ShaderMaterial({ 354 | 355 | defines: { 356 | "NUM_MIPS": nMips 357 | }, 358 | 359 | uniforms: { 360 | "blurTexture1": { value: null }, 361 | "blurTexture2": { value: null }, 362 | "blurTexture3": { value: null }, 363 | "blurTexture4": { value: null }, 364 | "blurTexture5": { value: null }, 365 | "dirtTexture": { value: null }, 366 | "bloomStrength": { value: 1.0 }, 367 | "bloomFactors": { value: null }, 368 | "bloomTintColors": { value: null }, 369 | "bloomRadius": { value: 0.0 } 370 | }, 371 | 372 | vertexShader: 373 | "varying vec2 vUv;\n\ 374 | void main() {\n\ 375 | vUv = uv;\n\ 376 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\ 377 | }", 378 | 379 | fragmentShader: 380 | "varying vec2 vUv;\ 381 | uniform sampler2D blurTexture1;\ 382 | uniform sampler2D blurTexture2;\ 383 | uniform sampler2D blurTexture3;\ 384 | uniform sampler2D blurTexture4;\ 385 | uniform sampler2D blurTexture5;\ 386 | uniform sampler2D dirtTexture;\ 387 | uniform float bloomStrength;\ 388 | uniform float bloomRadius;\ 389 | uniform float bloomFactors[NUM_MIPS];\ 390 | uniform vec3 bloomTintColors[NUM_MIPS];\ 391 | \ 392 | float lerpBloomFactor(const in float factor) { \ 393 | float mirrorFactor = 1.2 - factor;\ 394 | return mix(factor, mirrorFactor, bloomRadius);\ 395 | }\ 396 | \ 397 | void main() {\ 398 | gl_FragColor = bloomStrength * ( lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) + \ 399 | lerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) + \ 400 | lerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) + \ 401 | lerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) + \ 402 | lerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) );\ 403 | }" 404 | }); 405 | 406 | } 407 | 408 | }); 409 | 410 | UnrealBloomPass.BlurDirectionX = new Vector2(1.0, 0.0); 411 | UnrealBloomPass.BlurDirectionY = new Vector2(0.0, 1.0); 412 | 413 | export { UnrealBloomPass }; 414 | -------------------------------------------------------------------------------- /src/lib/stats.module.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | var Stats = function () { 6 | 7 | var mode = 0; 8 | 9 | var container = document.createElement( 'div' ); 10 | container.style.cssText = 'position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000'; 11 | container.addEventListener( 'click', function ( event ) { 12 | 13 | event.preventDefault(); 14 | showPanel( ++ mode % container.children.length ); 15 | 16 | }, false ); 17 | 18 | // 19 | 20 | function addPanel( panel ) { 21 | 22 | container.appendChild( panel.dom ); 23 | return panel; 24 | 25 | } 26 | 27 | function showPanel( id ) { 28 | 29 | for ( var i = 0; i < container.children.length; i ++ ) { 30 | 31 | container.children[ i ].style.display = i === id ? 'block' : 'none'; 32 | 33 | } 34 | 35 | mode = id; 36 | 37 | } 38 | 39 | // 40 | 41 | var beginTime = ( performance || Date ).now(), prevTime = beginTime, frames = 0; 42 | 43 | var fpsPanel = addPanel( new Stats.Panel( 'FPS', '#0ff', '#002' ) ); 44 | var msPanel = addPanel( new Stats.Panel( 'MS', '#0f0', '#020' ) ); 45 | 46 | if ( self.performance && self.performance.memory ) { 47 | 48 | var memPanel = addPanel( new Stats.Panel( 'MB', '#f08', '#201' ) ); 49 | 50 | } 51 | 52 | showPanel( 0 ); 53 | 54 | return { 55 | 56 | REVISION: 16, 57 | 58 | dom: container, 59 | 60 | addPanel: addPanel, 61 | showPanel: showPanel, 62 | 63 | begin: function () { 64 | 65 | beginTime = ( performance || Date ).now(); 66 | 67 | }, 68 | 69 | end: function () { 70 | 71 | frames ++; 72 | 73 | var time = ( performance || Date ).now(); 74 | 75 | msPanel.update( time - beginTime, 200 ); 76 | 77 | if ( time >= prevTime + 1000 ) { 78 | 79 | fpsPanel.update( ( frames * 1000 ) / ( time - prevTime ), 100 ); 80 | 81 | prevTime = time; 82 | frames = 0; 83 | 84 | if ( memPanel ) { 85 | 86 | var memory = performance.memory; 87 | memPanel.update( memory.usedJSHeapSize / 1048576, memory.jsHeapSizeLimit / 1048576 ); 88 | 89 | } 90 | 91 | } 92 | 93 | return time; 94 | 95 | }, 96 | 97 | update: function () { 98 | 99 | beginTime = this.end(); 100 | 101 | }, 102 | 103 | // Backwards Compatibility 104 | 105 | domElement: container, 106 | setMode: showPanel 107 | 108 | }; 109 | 110 | }; 111 | 112 | Stats.Panel = function ( name, fg, bg ) { 113 | 114 | var min = Infinity, max = 0, round = Math.round; 115 | var PR = round( window.devicePixelRatio || 1 ); 116 | 117 | var WIDTH = 80 * PR, HEIGHT = 48 * PR, 118 | TEXT_X = 3 * PR, TEXT_Y = 2 * PR, 119 | GRAPH_X = 3 * PR, GRAPH_Y = 15 * PR, 120 | GRAPH_WIDTH = 74 * PR, GRAPH_HEIGHT = 30 * PR; 121 | 122 | var canvas = document.createElement( 'canvas' ); 123 | canvas.width = WIDTH; 124 | canvas.height = HEIGHT; 125 | canvas.style.cssText = 'width:80px;height:48px'; 126 | 127 | var context = canvas.getContext( '2d' ); 128 | context.font = 'bold ' + ( 9 * PR ) + 'px Helvetica,Arial,sans-serif'; 129 | context.textBaseline = 'top'; 130 | 131 | context.fillStyle = bg; 132 | context.fillRect( 0, 0, WIDTH, HEIGHT ); 133 | 134 | context.fillStyle = fg; 135 | context.fillText( name, TEXT_X, TEXT_Y ); 136 | context.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT ); 137 | 138 | context.fillStyle = bg; 139 | context.globalAlpha = 0.9; 140 | context.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT ); 141 | 142 | return { 143 | 144 | dom: canvas, 145 | 146 | update: function ( value, maxValue ) { 147 | 148 | min = Math.min( min, value ); 149 | max = Math.max( max, value ); 150 | 151 | context.fillStyle = bg; 152 | context.globalAlpha = 1; 153 | context.fillRect( 0, 0, WIDTH, GRAPH_Y ); 154 | context.fillStyle = fg; 155 | context.fillText( round( value ) + ' ' + name + ' (' + round( min ) + '-' + round( max ) + ')', TEXT_X, TEXT_Y ); 156 | 157 | context.drawImage( canvas, GRAPH_X + PR, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT, GRAPH_X, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT ); 158 | 159 | context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, GRAPH_HEIGHT ); 160 | 161 | context.fillStyle = bg; 162 | context.globalAlpha = 0.9; 163 | context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, round( ( 1 - ( value / maxValue ) ) * GRAPH_HEIGHT ) ); 164 | 165 | } 166 | 167 | }; 168 | 169 | }; 170 | 171 | export default Stats; 172 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | 5 | Vue.config.productionTip = false; 6 | 7 | new Vue({ 8 | router, 9 | render: h => h(App) 10 | }).$mount("#app"); 11 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | // import Home from "../views/Home.vue"; 4 | 5 | Vue.use(VueRouter); 6 | 7 | const routes = [ 8 | // { 9 | // path: "/", 10 | // name: "Home", 11 | // component: Home 12 | // }, 13 | // { 14 | // path: "/about", 15 | // name: "About", 16 | // // route level code-splitting 17 | // // this generates a separate chunk (about.[hash].js) for this route 18 | // // which is lazy-loaded when the route is visited. 19 | // component: () => 20 | // import(/* webpackChunkName: "about" */ "../views/About.vue") 21 | // } 22 | ]; 23 | 24 | const router = new VueRouter({ 25 | mode: "history", 26 | base: process.env.BASE_URL, 27 | routes 28 | }); 29 | 30 | export default router; 31 | -------------------------------------------------------------------------------- /static/audio.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hocoa/AudioPlayer/97baf14994f6423ada381249c3b2682ba7827c71/static/audio.mp3 -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | // 部署生产环境和开发环境下的URL。 5 | // 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上 6 | // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。 7 | publicPath: "./", 8 | // 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist) 9 | outputDir: "dist", 10 | // 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下) 11 | assetsDir: "static", 12 | // 是否开启eslint保存检测,有效值:ture | false | 'error' 13 | lintOnSave: process.env.NODE_ENV === "development", 14 | // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。 15 | productionSourceMap: false 16 | }; 17 | --------------------------------------------------------------------------------